Compare commits

..

3 Commits

Author SHA1 Message Date
syuilo 8cce6dff51 wip 2026-01-06 11:38:10 +09:00
syuilo 6e99acf7a7 update clean scripts 2026-01-05 21:49:45 +09:00
github-actions[bot] 553a147396 Bump version to 2026.1.0-alpha.1 2026-01-05 12:03:42 +00:00
17 changed files with 716 additions and 28 deletions
+3
View File
@@ -40,3 +40,6 @@ updates:
typescript-eslint:
patterns:
- "@typescript-eslint/*"
tensorflow:
patterns:
- "@tensorflow/*"
+4 -1
View File
@@ -1,6 +1,6 @@
{
"name": "misskey",
"version": "2026.1.0-alpha.0",
"version": "2026.1.0-alpha.1",
"codename": "nasubi",
"repository": {
"type": "git",
@@ -83,6 +83,9 @@
"typescript": "5.9.3",
"start-server-and-test": "2.1.3"
},
"optionalDependencies": {
"@tensorflow/tfjs-core": "4.22.0"
},
"pnpm": {
"overrides": {
"@aiscript-dev/aiscript-languageserver": "-"
+81 -23
View File
@@ -1,7 +1,7 @@
import fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import { build } from 'esbuild';
import * as esbuild from 'esbuild';
import { swcPlugin } from 'esbuild-plugin-swc';
const _filename = fileURLToPath(import.meta.url);
@@ -42,8 +42,8 @@ const options = {
target: 'node22',
platform: 'node',
format: 'esm',
sourcemap: false,
packages: 'bundle',
sourcemap: 'linked',
packages: 'external',
banner: {
js: 'import { createRequire as topLevelCreateRequire } from "module";' +
'import ___url___ from "url";' +
@@ -76,26 +76,25 @@ const options = {
keepClassNames: true,
},
}),
//externalIpaddrPlugin,
],
external: [
'slacc-*',
'class-transformer',
'class-validator',
'@sentry/*',
'@nestjs/websockets/socket-module',
'@nestjs/microservices/microservices-module',
'@nestjs/microservices',
'@napi-rs/canvas-win32-x64-msvc',
'mock-aws-s3',
'aws-sdk',
'nock',
'sharp',
're2',
'@napi-rs/canvas',
'oauth2orize',
'oauth2orize-pkce',
externalIpaddrPlugin,
],
// external: [
// 'slacc-*',
// 'class-transformer',
// 'class-validator',
// '@sentry/*',
// '@nestjs/websockets/socket-module',
// '@nestjs/microservices/microservices-module',
// '@nestjs/microservices',
// '@napi-rs/canvas-win32-x64-msvc',
// 'mock-aws-s3',
// 'aws-sdk',
// 'nock',
// 'sharp',
// 'jsdom',
// 're2',
// '@napi-rs/canvas',
// ],
};
const args = process.argv.slice(2).map(arg => arg.toLowerCase());
@@ -104,12 +103,16 @@ if (!args.includes('--no-clean')) {
fs.rmSync('./built', { recursive: true, force: true });
}
//await minifyJsFiles(join(_dirname, 'node_modules'));
await minifyJsFiles(join(_dirname, '../../node_modules'));
await buildSrc();
async function buildSrc() {
console.log(`[${_package.name}] start building...`);
await build(options)
await esbuild.build(options)
.then(() => {
console.log(`[${_package.name}] build succeeded.`);
})
@@ -120,3 +123,58 @@ async function buildSrc() {
console.log(`[${_package.name}] finish building.`);
}
async function minifyJsFile(fullPath) {
if (!fullPath.includes('node_modules') || fullPath.includes('storybook') || fullPath.includes('tensorflow') || fullPath.includes('vite') || fullPath.includes('vue') || fullPath.includes('esbuild') || fullPath.includes('typescript') || fullPath.includes('css') || fullPath.includes('lint') || fullPath.includes('roll') || fullPath.includes('sass')) {
console.log(`Skipped: ${fullPath}`);
return;
}
try {
const data = fs.readFileSync(fullPath, 'utf-8');
if (data.includes('0 && (module.exports')) {
console.log(`Skipped: ${fullPath}`);
return;
}
//await esbuild.build({
// entryPoints: [fullPath],
// minifyWhitespace: true,
// outdir: dirname(fullPath),
// allowOverwrite: true,
//});
const result = await esbuild.transform(data, {
minifyWhitespace: true,
minifyIdentifiers: true,
minifySyntax: false, // nestjsが壊れる
treeShaking: false,
});
fs.writeFileSync(fullPath, result.code, 'utf-8');
console.log(`Minified: ${fullPath}`);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (err) {
console.log(`Skipped (error): ${fullPath}`);
}
}
async function minifyJsFiles(dir) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
await minifyJsFiles(fullPath);
} else if (entry.isFile() && entry.name.endsWith('.js')) {
await minifyJsFile(fullPath);
} else {
// resolve symbolic link
const stats = fs.lstatSync(fullPath);
if (stats.isSymbolicLink()) {
const realPath = fs.realpathSync(fullPath);
const realStats = fs.statSync(realPath);
if (realStats.isDirectory()) {
await minifyJsFiles(realPath);
} else if (realStats.isFile() && realPath.endsWith('.js')) {
await minifyJsFile(realPath);
}
}
}
}
}
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3
View File
@@ -52,6 +52,8 @@
"@swc/core-win32-arm64-msvc": "1.15.7",
"@swc/core-win32-ia32-msvc": "1.15.7",
"@swc/core-win32-x64-msvc": "1.15.7",
"@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.1.0",
"slacc-android-arm-eabi": "0.0.10",
"slacc-android-arm64": "0.0.10",
@@ -139,6 +141,7 @@
"node-fetch": "3.3.2",
"node-html-parser": "7.0.1",
"nodemailer": "7.0.12",
"nsfwjs": "4.2.0",
"oauth2orize": "1.12.0",
"oauth2orize-pkce": "0.1.2",
"os-utils": "0.0.14",
+73 -2
View File
@@ -3,17 +3,88 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import { Injectable } from '@nestjs/common';
import { Mutex } from 'async-mutex';
import fetch from 'node-fetch';
import { bindThis } from '@/decorators.js';
import type { NSFWJS, PredictionType } from 'nsfwjs';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const REQUIRED_CPU_FLAGS_X64 = ['avx2', 'fma'];
let isSupportedCpu: undefined | boolean = undefined;
@Injectable()
export class AiService {
private model: NSFWJS;
private modelLoadMutex: Mutex = new Mutex();
constructor(
) {
}
@bindThis
public async detectSensitive(source: string | Buffer): Promise<null> {
return null;
public async detectSensitive(source: string | Buffer): Promise<PredictionType[] | null> {
try {
if (isSupportedCpu === undefined) {
isSupportedCpu = await this.computeIsSupportedCpu();
}
if (!isSupportedCpu) {
console.error('This CPU cannot use TensorFlow.');
return null;
}
const tf = await import('@tensorflow/tfjs-node');
tf.env().global.fetch = fetch;
if (this.model == null) {
const nsfw = await import('nsfwjs');
await this.modelLoadMutex.runExclusive(async () => {
if (this.model == null) {
this.model = await nsfw.load(`file://${_dirname}/../../nsfw-model/`, { size: 299 });
}
});
}
const buffer = source instanceof Buffer ? source : await fs.promises.readFile(source);
const image = await tf.node.decodeImage(buffer, 3) as any;
try {
const predictions = await this.model.classify(image);
return predictions;
} finally {
image.dispose();
}
} catch (err) {
console.error(err);
return null;
}
}
private async computeIsSupportedCpu(): Promise<boolean> {
switch (process.arch) {
case 'x64': {
const cpuFlags = await this.getCpuFlags();
return REQUIRED_CPU_FLAGS_X64.every(required => cpuFlags.includes(required));
}
case 'arm64': {
// As far as I know, no required CPU flags for ARM64.
return true;
}
default: {
return false;
}
}
}
@bindThis
private async getCpuFlags(): Promise<string[]> {
const si = await import('systeminformation');
const str = await si.cpuFlags();
return str.split(/\s+/);
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
"version": "2026.1.0-alpha.0",
"version": "2026.1.0-alpha.1",
"description": "Misskey SDK for JavaScript",
"license": "MIT",
"main": "./built/index.js",
+518 -1
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -17,6 +17,7 @@ onlyBuiltDependencies:
- '@sentry/profiling-node'
- '@sentry-internal/node-cpu-profiler'
- '@swc/core'
- '@tensorflow/tfjs-node'
- bufferutil
- canvas
- core-js
+1
View File
@@ -8,6 +8,7 @@ const fs = require('fs');
(async () => {
fs.rmSync(__dirname + '/../packages/backend/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/backend/src-js', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/backend/node_modules', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/frontend-shared/built', { recursive: true, force: true });
+1
View File
@@ -7,6 +7,7 @@ const fs = require('fs');
(async () => {
fs.rmSync(__dirname + '/../packages/backend/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/backend/src-js', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/frontend-shared/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/frontend/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/frontend-embed/built', { recursive: true, force: true });