Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8cce6dff51 | |||
| 6e99acf7a7 | |||
| 553a147396 |
@@ -40,3 +40,6 @@ updates:
|
||||
typescript-eslint:
|
||||
patterns:
|
||||
- "@typescript-eslint/*"
|
||||
tensorflow:
|
||||
patterns:
|
||||
- "@tensorflow/*"
|
||||
|
||||
+4
-1
@@ -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
@@ -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
@@ -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",
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
Generated
+518
-1
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,7 @@ onlyBuiltDependencies:
|
||||
- '@sentry/profiling-node'
|
||||
- '@sentry-internal/node-cpu-profiler'
|
||||
- '@swc/core'
|
||||
- '@tensorflow/tfjs-node'
|
||||
- bufferutil
|
||||
- canvas
|
||||
- core-js
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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 });
|
||||
|
||||
Reference in New Issue
Block a user