mirror of https://github.com/vladmandic/human
remove old build server
parent
c3b59baff1
commit
c9dacbd152
|
@ -1,8 +0,0 @@
|
|||
# Human Library: Dev Server & Build Scripts
|
||||
|
||||
For details see Wiki:
|
||||
|
||||
- [**Build Process**](https://github.com/vladmandic/human/wiki/Build-Process)
|
||||
- [**Development Server**](https://github.com/vladmandic/human/wiki/Development-Server)
|
||||
|
||||
Not required for normal funcioning of library
|
270
server/build.js
270
server/build.js
|
@ -1,270 +0,0 @@
|
|||
/**
|
||||
* Implements Human build process
|
||||
* Used to generate prod builds for releases or by dev server to generate on-the-fly debug builds
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const log = require('@vladmandic/pilogger');
|
||||
const esbuild = require('esbuild');
|
||||
const rimraf = require('rimraf');
|
||||
const tfjs = require('@tensorflow/tfjs/package.json');
|
||||
const changelog = require('./changelog.js');
|
||||
const lint = require('./lint.js');
|
||||
const typedoc = require('./typedoc.js');
|
||||
const typings = require('./typings.js');
|
||||
|
||||
let busy = false;
|
||||
|
||||
const config = {
|
||||
build: {
|
||||
banner: { js: `
|
||||
/*
|
||||
Human library
|
||||
homepage: <https://github.com/vladmandic/human>
|
||||
author: <https://github.com/vladmandic>'
|
||||
*/` },
|
||||
tsconfig: './tsconfig.json',
|
||||
logLevel: 'error',
|
||||
bundle: true,
|
||||
metafile: true,
|
||||
target: 'es2018',
|
||||
},
|
||||
debug: {
|
||||
minifyWhitespace: false,
|
||||
minifyIdentifiers: false,
|
||||
minifySyntax: false,
|
||||
},
|
||||
production: {
|
||||
minifyWhitespace: true,
|
||||
minifyIdentifiers: true,
|
||||
minifySyntax: true,
|
||||
},
|
||||
buildLog: 'build.log',
|
||||
changelog: '../CHANGELOG.md',
|
||||
lintLocations: ['server/', 'src/', 'tfjs/', 'test/', 'demo/'],
|
||||
cleanLocations: ['dist/*', 'types/*', 'typedoc/*'],
|
||||
};
|
||||
|
||||
const targets = {
|
||||
node: {
|
||||
tfjs: {
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
entryPoints: ['tfjs/tf-node.ts'],
|
||||
outfile: 'dist/tfjs.esm.js',
|
||||
external: ['@tensorflow'],
|
||||
sourcemap: false,
|
||||
minifyWhitespace: false,
|
||||
minifyIdentifiers: false,
|
||||
minifySyntax: false,
|
||||
},
|
||||
node: {
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
entryPoints: ['src/human.ts'],
|
||||
outfile: 'dist/human.node.js',
|
||||
external: ['@tensorflow'],
|
||||
sourcemap: false,
|
||||
minifyWhitespace: false,
|
||||
minifyIdentifiers: false,
|
||||
minifySyntax: false,
|
||||
},
|
||||
},
|
||||
nodeGPU: {
|
||||
tfjs: {
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
entryPoints: ['tfjs/tf-node-gpu.ts'],
|
||||
outfile: 'dist/tfjs.esm.js',
|
||||
external: ['@tensorflow'],
|
||||
sourcemap: false,
|
||||
minifyWhitespace: false,
|
||||
minifyIdentifiers: false,
|
||||
minifySyntax: false,
|
||||
},
|
||||
node: {
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
entryPoints: ['src/human.ts'],
|
||||
outfile: 'dist/human.node-gpu.js',
|
||||
external: ['@tensorflow'],
|
||||
sourcemap: false,
|
||||
minifyWhitespace: false,
|
||||
minifyIdentifiers: false,
|
||||
minifySyntax: false,
|
||||
},
|
||||
},
|
||||
nodeWASM: {
|
||||
tfjs: {
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
entryPoints: ['tfjs/tf-node-wasm.ts'],
|
||||
outfile: 'dist/tfjs.esm.js',
|
||||
external: ['@tensorflow'],
|
||||
sourcemap: false,
|
||||
minifyWhitespace: false,
|
||||
minifyIdentifiers: false,
|
||||
minifySyntax: false,
|
||||
},
|
||||
node: {
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
entryPoints: ['src/human.ts'],
|
||||
outfile: 'dist/human.node-wasm.js',
|
||||
external: ['@tensorflow'],
|
||||
sourcemap: false,
|
||||
minifyWhitespace: false,
|
||||
minifyIdentifiers: false,
|
||||
minifySyntax: false,
|
||||
},
|
||||
},
|
||||
|
||||
browserNoBundle: {
|
||||
tfjs: {
|
||||
platform: 'browser',
|
||||
format: 'esm',
|
||||
entryPoints: ['tfjs/tf-browser.ts'],
|
||||
outfile: 'dist/tfjs.esm.js',
|
||||
external: ['fs', 'buffer', 'util', 'os', '@tensorflow'],
|
||||
sourcemap: true,
|
||||
},
|
||||
esm: {
|
||||
platform: 'browser',
|
||||
format: 'esm',
|
||||
entryPoints: ['src/human.ts'],
|
||||
outfile: 'dist/human.esm-nobundle.js',
|
||||
external: ['fs', 'buffer', 'util', 'os', '@tensorflow'],
|
||||
sourcemap: true,
|
||||
},
|
||||
},
|
||||
browserBundle: {
|
||||
tfjs: {
|
||||
banner: { js: '/* TFJS custom ESM bundle in ES2018 */' },
|
||||
platform: 'browser',
|
||||
format: 'esm',
|
||||
entryPoints: ['tfjs/tf-browser.ts'],
|
||||
outfile: 'dist/tfjs.esm.js',
|
||||
external: ['fs', 'buffer', 'util', 'os'],
|
||||
// treeShaking: 'ignore-annotations',
|
||||
sourcemap: true,
|
||||
minifyWhitespace: false,
|
||||
minifyIdentifiers: false,
|
||||
minifySyntax: false,
|
||||
},
|
||||
iife: {
|
||||
platform: 'browser',
|
||||
format: 'iife',
|
||||
globalName: 'Human',
|
||||
entryPoints: ['src/human.ts'],
|
||||
outfile: 'dist/human.js',
|
||||
external: ['fs', 'buffer', 'util', 'os'],
|
||||
sourcemap: false,
|
||||
},
|
||||
esm: {
|
||||
platform: 'browser',
|
||||
format: 'esm',
|
||||
entryPoints: ['src/human.ts'],
|
||||
outfile: 'dist/human.esm.js',
|
||||
external: ['fs', 'buffer', 'util', 'os'],
|
||||
sourcemap: true,
|
||||
},
|
||||
/*
|
||||
demo: {
|
||||
platform: 'browser',
|
||||
format: 'esm',
|
||||
entryPoints: ['demo/browser.js'],
|
||||
outfile: 'dist/demo-browser-index.js',
|
||||
external: ['fs', 'buffer', 'util', 'os'],
|
||||
},
|
||||
*/
|
||||
},
|
||||
};
|
||||
|
||||
async function getStats(json) {
|
||||
const stats = {};
|
||||
if (json && json.metafile?.inputs && json.metafile?.outputs) {
|
||||
for (const [key, val] of Object.entries(json.metafile.inputs)) {
|
||||
if (key.startsWith('node_modules')) {
|
||||
stats.modules = (stats.modules || 0) + 1;
|
||||
stats.moduleBytes = (stats.moduleBytes || 0) + val.bytes;
|
||||
} else {
|
||||
stats.imports = (stats.imports || 0) + 1;
|
||||
stats.importBytes = (stats.importBytes || 0) + val.bytes;
|
||||
}
|
||||
}
|
||||
const files = [];
|
||||
for (const [key, val] of Object.entries(json.metafile.outputs)) {
|
||||
if (!key.endsWith('.map')) {
|
||||
files.push(key);
|
||||
stats.outputBytes = (stats.outputBytes || 0) + val.bytes;
|
||||
}
|
||||
}
|
||||
stats.outputFiles = files.join(', ');
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
// rebuild typings
|
||||
|
||||
// rebuild on file change
|
||||
async function build(f, msg, dev = false) {
|
||||
if (busy) {
|
||||
log.state('Build: busy...');
|
||||
setTimeout(() => build(f, msg, dev), 500);
|
||||
return;
|
||||
}
|
||||
busy = true;
|
||||
log.info('Build: file', msg, f, 'type:', dev ? 'debug' : 'production', 'config:', dev ? config.debug : config.production);
|
||||
// common build options
|
||||
try {
|
||||
// rebuild all target groups and types
|
||||
for (const [targetGroupName, targetGroup] of Object.entries(targets)) {
|
||||
for (const [targetName, targetOptions] of Object.entries(targetGroup)) {
|
||||
// if triggered from watch mode, rebuild only browser bundle
|
||||
// if ((require.main !== module) && ((targetGroupName === 'browserNoBundle') || (targetGroupName === 'nodeGPU'))) continue;
|
||||
const opt = dev ? config.debug : config.production;
|
||||
// @ts-ignore // eslint-typescript complains about string enums used in js code
|
||||
const meta = await esbuild.build({ ...config.build, ...opt, ...targetOptions });
|
||||
const stats = await getStats(meta);
|
||||
log.state(` target: ${targetGroupName} type: ${targetName}:`, stats);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// catch errors and print where it occured
|
||||
log.error('Build error', JSON.stringify(err.errors || err, null, 2));
|
||||
if (require.main === module) process.exit(1);
|
||||
}
|
||||
if (!dev) { // only for prod builds, skipped for dev build
|
||||
await lint.run(config.lintLocations); // run linter
|
||||
await changelog.update(config.changelog); // generate changelog
|
||||
await typings.run(targets.browserBundle.esm.entryPoints); // generate typings
|
||||
await typedoc.run(targets.browserBundle.esm.entryPoints); // generate typedoc
|
||||
}
|
||||
if (require.main === module) process.exit(0);
|
||||
busy = false;
|
||||
}
|
||||
|
||||
function clean() {
|
||||
log.info('Clean:', config.cleanLocations);
|
||||
for (const loc of config.cleanLocations) rimraf.sync(loc);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
config.buildLog = path.join(__dirname, config.buildLog);
|
||||
if (fs.existsSync(config.buildLog)) fs.unlinkSync(config.buildLog);
|
||||
log.logFile(config.buildLog);
|
||||
log.header();
|
||||
const toolchain = {
|
||||
tfjs: tfjs.version,
|
||||
esbuild: esbuild.version,
|
||||
typescript: typings.version,
|
||||
typedoc: typedoc.version,
|
||||
eslint: lint.version,
|
||||
};
|
||||
log.info('Toolchain: ', toolchain);
|
||||
clean();
|
||||
build('all', 'startup');
|
||||
} else {
|
||||
exports.build = build;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
2021-09-10 19:34:11 [36mINFO: [39m @vladmandic/human version 2.1.5
|
||||
2021-09-10 19:34:11 [36mINFO: [39m User: vlado Platform: linux Arch: x64 Node: v16.5.0
|
||||
2021-09-10 19:34:11 [36mINFO: [39m Toolchain: {"tfjs":"3.9.0","esbuild":"0.12.26","typescript":"4.4.3","typedoc":"0.22.1","eslint":"7.32.0"}
|
||||
2021-09-10 19:34:11 [36mINFO: [39m Clean: ["dist/*","types/*","typedoc/*"]
|
||||
2021-09-10 19:34:11 [36mINFO: [39m Build: file startup all type: production config: {"minifyWhitespace":true,"minifyIdentifiers":true,"minifySyntax":true}
|
||||
2021-09-10 19:34:11 [35mSTATE:[39m target: node type: tfjs: {"imports":1,"importBytes":102,"outputBytes":1444,"outputFiles":"dist/tfjs.esm.js"}
|
||||
2021-09-10 19:34:11 [35mSTATE:[39m target: node type: node: {"imports":47,"importBytes":456903,"outputBytes":396607,"outputFiles":"dist/human.node.js"}
|
||||
2021-09-10 19:34:11 [35mSTATE:[39m target: nodeGPU type: tfjs: {"imports":1,"importBytes":110,"outputBytes":1452,"outputFiles":"dist/tfjs.esm.js"}
|
||||
2021-09-10 19:34:11 [35mSTATE:[39m target: nodeGPU type: node: {"imports":47,"importBytes":456911,"outputBytes":396611,"outputFiles":"dist/human.node-gpu.js"}
|
||||
2021-09-10 19:34:11 [35mSTATE:[39m target: nodeWASM type: tfjs: {"imports":1,"importBytes":149,"outputBytes":1519,"outputFiles":"dist/tfjs.esm.js"}
|
||||
2021-09-10 19:34:11 [35mSTATE:[39m target: nodeWASM type: node: {"imports":47,"importBytes":456978,"outputBytes":396683,"outputFiles":"dist/human.node-wasm.js"}
|
||||
2021-09-10 19:34:11 [35mSTATE:[39m target: browserNoBundle type: tfjs: {"imports":1,"importBytes":2168,"outputBytes":1242,"outputFiles":"dist/tfjs.esm.js"}
|
||||
2021-09-10 19:34:11 [35mSTATE:[39m target: browserNoBundle type: esm: {"imports":47,"importBytes":456701,"outputBytes":255452,"outputFiles":"dist/human.esm-nobundle.js"}
|
|
@ -1,59 +0,0 @@
|
|||
/**
|
||||
* Creates changelog in markdown format from git log as part of the build process
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const dayjs = require('dayjs');
|
||||
const simpleGit = require('simple-git/promise');
|
||||
const logger = require('@vladmandic/pilogger');
|
||||
const app = require('../package.json');
|
||||
|
||||
const git = simpleGit();
|
||||
|
||||
let text = `# ${app.name}
|
||||
|
||||
Version: **${app.version}**
|
||||
Description: **${app.description}**
|
||||
|
||||
Author: **${app.author}**
|
||||
License: **${app.license}** </LICENSE>
|
||||
Repository: **<${app.repository.url}>**
|
||||
|
||||
## Changelog
|
||||
`;
|
||||
|
||||
async function update(f) {
|
||||
const gitLog = await git.log();
|
||||
const entries = [...gitLog.all];
|
||||
const log = entries.sort((a, b) => (new Date(b.date).getTime() - new Date(a.date).getTime()));
|
||||
|
||||
let previous = '';
|
||||
const headings = [];
|
||||
for (const l of log) {
|
||||
const msg = l.message.toLowerCase();
|
||||
if ((l.refs !== '') || msg.match(/^[0-99].[0-99].[0-99]/)) {
|
||||
const dt = dayjs(l.date).format('YYYY/MM/DD');
|
||||
let ver = msg.match(/[0-99].[0-99].[0-99]/) ? msg : l.refs;
|
||||
ver = ver.replace('tag: v', '').replace('tag: ', 'release: ').split(',')[0];
|
||||
const heading = `\n### **${ver}** ${dt} ${l.author_email}\n\n`;
|
||||
if (!headings.includes(heading) && !ver.startsWith('tag')) {
|
||||
headings.push(heading);
|
||||
text += heading;
|
||||
}
|
||||
} else if ((msg.length > 2) && !msg.startsWith('update') && (previous !== msg)) {
|
||||
previous = msg;
|
||||
text += `- ${msg}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
const name = path.join(__dirname, f);
|
||||
fs.writeFileSync(name, text);
|
||||
logger.info('Generate ChangeLog:', [name]);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
update('../CHANGELOG.md');
|
||||
} else {
|
||||
exports.update = update;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
|
@ -1,52 +0,0 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
qIRta3H9KNw2Mhpkm77TtUSV/W5aOmY=
|
||||
-----END PRIVATE KEY-----
|
|
@ -1,23 +0,0 @@
|
|||
const log = require('@vladmandic/pilogger');
|
||||
|
||||
let eslint = null;
|
||||
const { ESLint } = require('eslint');
|
||||
|
||||
const version = ESLint.version;
|
||||
|
||||
async function lint(lintLocations) {
|
||||
log.info('Running Linter:', lintLocations);
|
||||
if (!eslint) eslint = new ESLint();
|
||||
const results = await eslint.lintFiles(lintLocations);
|
||||
const errors = results.reduce((prev, curr) => prev += curr.errorCount, 0);
|
||||
const warnings = results.reduce((prev, curr) => prev += curr.warningCount, 0);
|
||||
log.info('Linter complete: files:', results.length, 'errors:', errors, 'warnings:', warnings);
|
||||
if (errors > 0 || warnings > 0) {
|
||||
const formatter = await eslint.loadFormatter('stylish');
|
||||
const text = formatter.format(results);
|
||||
log.warn(text);
|
||||
}
|
||||
}
|
||||
|
||||
exports.run = lint;
|
||||
exports.version = version;
|
194
server/serve.js
194
server/serve.js
|
@ -1,194 +0,0 @@
|
|||
/**
|
||||
Micro http/http2 server with file monitoring and automatic app rebuild
|
||||
- can process concurrent http requests
|
||||
- monitors specified filed and folders for changes
|
||||
- triggers library and application rebuild
|
||||
- any build errors are immediately displayed and can be corrected without need for restart
|
||||
- passthrough data compression
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const zlib = require('zlib');
|
||||
const http = require('http');
|
||||
const http2 = require('http2');
|
||||
const path = require('path');
|
||||
const chokidar = require('chokidar');
|
||||
const log = require('@vladmandic/pilogger');
|
||||
const build = require('./build.js');
|
||||
|
||||
// app configuration
|
||||
// you can provide your server key and certificate or use provided self-signed ones
|
||||
// self-signed certificate generated using:
|
||||
// openssl req -x509 -newkey rsa:4096 -nodes -keyout https.key -out https.crt -days 365 -subj "/C=US/ST=Florida/L=Miami/O=@vladmandic"
|
||||
// client app does not work without secure server since browsers enforce https for webcam access
|
||||
const options = {
|
||||
key: fs.readFileSync('server/https.key'),
|
||||
cert: fs.readFileSync('server/https.crt'),
|
||||
defaultFolder: 'demo',
|
||||
defaultFile: 'index.html',
|
||||
httpPort: 10030,
|
||||
httpsPort: 10031,
|
||||
insecureHTTPParser: false,
|
||||
minElapsed: 2,
|
||||
monitor: ['package.json', 'config.ts', 'demo/*.js', 'demo/*.html', 'src'],
|
||||
};
|
||||
|
||||
// just some predefined mime types
|
||||
const mime = {
|
||||
'.html': 'text/html; charset=utf-8',
|
||||
'.js': 'text/javascript; charset=utf-8',
|
||||
'.css': 'text/css; charset=utf-8',
|
||||
'.json': 'application/json; charset=utf-8',
|
||||
'.png': 'image/png',
|
||||
'.jpg': 'image/jpg',
|
||||
'.gif': 'image/gif',
|
||||
'.ico': 'image/x-icon',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.wav': 'audio/wav',
|
||||
'.mp4': 'video/mp4',
|
||||
'.woff': 'font/woff',
|
||||
'.woff2': 'font/woff2',
|
||||
'.ttf': 'font/ttf',
|
||||
'.wasm': 'application/wasm',
|
||||
};
|
||||
|
||||
let last = Date.now();
|
||||
async function buildAll(evt, msg) {
|
||||
const now = Date.now();
|
||||
if ((now - last) > options.minElapsed) build.build(evt, msg, true);
|
||||
else log.state('Build: merge event file', msg, evt);
|
||||
last = now;
|
||||
}
|
||||
|
||||
// watch filesystem for any changes and notify build when needed
|
||||
async function watch() {
|
||||
const watcher = chokidar.watch(options.monitor, {
|
||||
persistent: true,
|
||||
ignorePermissionErrors: false,
|
||||
alwaysStat: false,
|
||||
ignoreInitial: true,
|
||||
followSymlinks: true,
|
||||
usePolling: false,
|
||||
useFsEvents: false,
|
||||
atomic: true,
|
||||
});
|
||||
// single event handler for file add/change/delete
|
||||
watcher
|
||||
.on('add', (evt) => buildAll(evt, 'add'))
|
||||
.on('change', (evt) => buildAll(evt, 'modify'))
|
||||
.on('unlink', (evt) => buildAll(evt, 'remove'))
|
||||
.on('error', (err) => log.error(`Client watcher error: ${err}`))
|
||||
.on('ready', () => log.state('Monitoring:', options.monitor));
|
||||
}
|
||||
|
||||
function handle(url) {
|
||||
url = url.split(/[?#]/)[0];
|
||||
const result = { ok: false, stat: {}, file: '' };
|
||||
const checkFile = (f) => {
|
||||
result.file = f;
|
||||
if (fs.existsSync(f)) {
|
||||
result.stat = fs.statSync(f);
|
||||
if (result.stat.isFile()) {
|
||||
result.ok = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const checkFolder = (f) => {
|
||||
result.file = f;
|
||||
if (fs.existsSync(f)) {
|
||||
result.stat = fs.statSync(f);
|
||||
if (result.stat.isDirectory()) {
|
||||
result.ok = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return new Promise((resolve) => {
|
||||
if (checkFile(path.join(process.cwd(), url))) resolve(result);
|
||||
else if (checkFile(path.join(process.cwd(), url, options.defaultFile))) resolve(result);
|
||||
else if (checkFile(path.join(process.cwd(), options.defaultFolder, url))) resolve(result);
|
||||
else if (checkFile(path.join(process.cwd(), options.defaultFolder, url, options.defaultFile))) resolve(result);
|
||||
else if (checkFolder(path.join(process.cwd(), url))) resolve(result);
|
||||
else if (checkFolder(path.join(process.cwd(), options.defaultFolder, url))) resolve(result);
|
||||
else resolve(result);
|
||||
});
|
||||
}
|
||||
|
||||
// process http requests
|
||||
async function httpRequest(req, res) {
|
||||
handle(decodeURI(req.url)).then((result) => {
|
||||
// get original ip of requestor, regardless if it's behind proxy or not
|
||||
const forwarded = (req.headers['forwarded'] || '').match(/for="\[(.*)\]:/);
|
||||
const ip = (Array.isArray(forwarded) ? forwarded[1] : null) || req.headers['x-forwarded-for'] || req.ip || req.socket.remoteAddress;
|
||||
if (!result || !result.ok || !result.stat) {
|
||||
res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||
res.end('Error 404: Not Found\n', 'utf-8');
|
||||
log.warn(`${req.method}/${req.httpVersion}`, res.statusCode, decodeURI(req.url), ip);
|
||||
} else {
|
||||
const input = encodeURIComponent(result.file).replace(/\*/g, '').replace(/\?/g, '').replace(/%2F/g, '/').replace(/%40/g, '@').replace(/%20/g, ' ');
|
||||
if (result?.stat?.isFile()) {
|
||||
const ext = String(path.extname(input)).toLowerCase();
|
||||
const contentType = mime[ext] || 'application/octet-stream';
|
||||
const accept = req.headers['accept-encoding'] ? req.headers['accept-encoding'].includes('br') : false; // does target accept brotli compressed data
|
||||
res.writeHead(200, {
|
||||
// 'Content-Length': result.stat.size, // not using as it's misleading for compressed streams
|
||||
'Content-Language': 'en',
|
||||
'Content-Type': contentType,
|
||||
'Content-Encoding': accept ? 'br' : '',
|
||||
'Last-Modified': result.stat.mtime,
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Content-Type-Options': 'nosniff',
|
||||
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||
'Content-Security-Policy': "media-src 'self' http: https: data:",
|
||||
});
|
||||
const compress = zlib.createBrotliCompress({ params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 5 } }); // instance of brotli compression with level 5
|
||||
const stream = fs.createReadStream(input);
|
||||
if (!accept) stream.pipe(res); // don't compress data
|
||||
else stream.pipe(compress).pipe(res); // compress data
|
||||
|
||||
/// alternative #2 read stream and send by chunk
|
||||
// const stream = fs.createReadStream(result.file);
|
||||
// stream.on('data', (chunk) => res.write(chunk));
|
||||
// stream.on('end', () => res.end());
|
||||
|
||||
// alternative #3 read entire file and send it as blob
|
||||
// const data = fs.readFileSync(result.file);
|
||||
// res.write(data);
|
||||
log.data(`${req.method}/${req.httpVersion}`, res.statusCode, contentType, result.stat.size, req.url, ip);
|
||||
}
|
||||
if (result?.stat?.isDirectory()) {
|
||||
res.writeHead(200, { 'Content-Language': 'en', 'Content-Type': 'application/json; charset=utf-8', 'Last-Modified': result.stat.mtime, 'Cache-Control': 'no-cache', 'X-Content-Type-Options': 'nosniff' });
|
||||
let dir = fs.readdirSync(input);
|
||||
dir = dir.map((f) => path.join(decodeURI(req.url), f));
|
||||
res.end(JSON.stringify(dir), 'utf-8');
|
||||
log.data(`${req.method}/${req.httpVersion}`, res.statusCode, 'directory/json', result.stat.size, req.url, ip);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// app main entry point
|
||||
async function main() {
|
||||
log.header();
|
||||
await watch();
|
||||
process.chdir(path.join(__dirname, '..'));
|
||||
if (options.httpPort && options.httpPort > 0) {
|
||||
const server1 = http.createServer(options, httpRequest);
|
||||
server1.on('listening', () => log.state('HTTP server listening:', options.httpPort));
|
||||
server1.on('error', (err) => log.error('HTTP server:', err.message || err));
|
||||
server1.listen(options.httpPort);
|
||||
}
|
||||
if (options.httpsPort && options.httpsPort > 0) {
|
||||
const server2 = http2.createSecureServer(options, httpRequest);
|
||||
server2.on('listening', () => log.state('HTTP2 server listening:', options.httpsPort));
|
||||
server2.on('error', (err) => log.error('HTTP2 server:', err.message || err));
|
||||
server2.listen(options.httpsPort);
|
||||
}
|
||||
await build.build('all', 'startup', true);
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,99 +0,0 @@
|
|||
#!/usr/bin/env -S node --no-deprecation --trace-warnings
|
||||
|
||||
/**
|
||||
* Helper app that analyzes any TensorFlow SavedModel or GraphModel for inputs and outputs
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const log = require('@vladmandic/pilogger');
|
||||
const tf = require('@tensorflow/tfjs-node');
|
||||
|
||||
async function analyzeGraph(modelPath) {
|
||||
const model = await tf.loadGraphModel(`file://${modelPath}`);
|
||||
log.info('graph model:', path.resolve(modelPath));
|
||||
log.info('size:', tf.engine().memory());
|
||||
|
||||
const inputs = [];
|
||||
if (model.modelSignature && model.modelSignature['inputs']) {
|
||||
log.info('model inputs based on signature');
|
||||
for (const [key, val] of Object.entries(model.modelSignature['inputs'])) {
|
||||
const shape = val.tensorShape.dim.map((a) => parseInt(a.size));
|
||||
inputs.push({ name: key, dtype: val.dtype, shape });
|
||||
}
|
||||
// @ts-ignore accessing private property
|
||||
} else if (model.executor.graph['inputs']) {
|
||||
log.info('model inputs based on executor');
|
||||
// @ts-ignore accessing private property
|
||||
for (const t of model.executor.graph['inputs']) {
|
||||
inputs.push({ name: t.name, dtype: t.attrParams.dtype.value, shape: t.attrParams.shape.value });
|
||||
}
|
||||
} else {
|
||||
log.warn('model inputs: cannot determine');
|
||||
}
|
||||
|
||||
const outputs = [];
|
||||
let i = 0;
|
||||
if (model.modelSignature && model.modelSignature['outputs'] && Object.values(model.modelSignature['outputs'])[0].dtype) {
|
||||
log.info('model outputs based on signature');
|
||||
for (const [key, val] of Object.entries(model.modelSignature['outputs'])) {
|
||||
const shape = val.tensorShape?.dim.map((a) => parseInt(a.size));
|
||||
outputs.push({ id: i++, name: key, dytpe: val.dtype, shape });
|
||||
}
|
||||
// @ts-ignore accessing private property
|
||||
} else if (model.executor.graph['outputs']) {
|
||||
log.info('model outputs based on executor');
|
||||
// @ts-ignore accessing private property
|
||||
for (const t of model.executor.graph['outputs']) {
|
||||
outputs.push({ id: i++, name: t.name, dtype: t.attrParams.dtype?.value || t.rawAttrs.T.type, shape: t.attrParams.shape?.value });
|
||||
}
|
||||
} else {
|
||||
log.warn('model outputs: cannot determine');
|
||||
}
|
||||
|
||||
log.data('inputs:', inputs);
|
||||
log.data('outputs:', outputs);
|
||||
}
|
||||
|
||||
async function analyzeSaved(modelPath) {
|
||||
const meta = await tf.node.getMetaGraphsFromSavedModel(modelPath);
|
||||
log.info('saved model:', path.resolve(modelPath));
|
||||
const sign = Object.values(meta[0].signatureDefs)[0];
|
||||
log.data('tags:', meta[0].tags);
|
||||
log.data('signature:', Object.keys(meta[0].signatureDefs));
|
||||
const inputs = Object.values(sign.inputs)[0];
|
||||
// @ts-ignore a is array
|
||||
const inputShape = inputs.shape?.map((a) => a.array[0]);
|
||||
log.data('inputs:', { name: inputs.name, dtype: inputs.dtype, shape: inputShape });
|
||||
const outputs = [];
|
||||
let i = 0;
|
||||
for (const [key, val] of Object.entries(sign.outputs)) {
|
||||
// @ts-ignore a is array
|
||||
const shape = val.shape?.map((a) => a.array[0]);
|
||||
outputs.push({ id: i++, name: key, dytpe: val.dtype, shape });
|
||||
}
|
||||
log.data('outputs:', outputs);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
log.header();
|
||||
const param = process.argv[2];
|
||||
if (process.argv.length !== 3) {
|
||||
log.error('path required');
|
||||
process.exit(0);
|
||||
} else if (!fs.existsSync(param)) {
|
||||
log.error(`path does not exist: ${param}`);
|
||||
process.exit(0);
|
||||
}
|
||||
const stat = fs.statSync(param);
|
||||
log.data('created on:', stat.birthtime);
|
||||
if (stat.isFile()) {
|
||||
if (param.endsWith('.json')) analyzeGraph(param);
|
||||
}
|
||||
if (stat.isDirectory()) {
|
||||
if (fs.existsSync(path.join(param, '/saved_model.pb'))) analyzeSaved(param);
|
||||
if (fs.existsSync(path.join(param, '/model.json'))) analyzeGraph(path.join(param, '/model.json'));
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,35 +0,0 @@
|
|||
const log = require('@vladmandic/pilogger');
|
||||
const TypeDoc = require('typedoc');
|
||||
const tsconfig = require('../tsconfig.json');
|
||||
|
||||
let td = null;
|
||||
|
||||
const version = TypeDoc.Application.VERSION;
|
||||
|
||||
async function typedoc(entryPoints) {
|
||||
if (!td) {
|
||||
td = new TypeDoc.Application();
|
||||
td.options.addReader(new TypeDoc.TSConfigReader());
|
||||
td.bootstrap({ entryPoints });
|
||||
td.logger.warn = log.warn;
|
||||
td.logger.error = log.error;
|
||||
td.logger.verbose = () => { /***/ };
|
||||
// td.logger.verbose = log.data; // remove extra logging
|
||||
td.logger.log = log.info;
|
||||
// td.converter = converter;
|
||||
}
|
||||
log.info('Generate TypeDocs:', entryPoints, 'outDir:', [tsconfig.typedocOptions.out]);
|
||||
const project = td.convert();
|
||||
if (!project) log.warn('TypeDoc: convert returned empty project');
|
||||
if (td.logger.hasErrors() || td.logger.hasWarnings()) log.warn('TypeDoc:', 'errors:', td.logger.errorCount, 'warnings:', td.logger.warningCount);
|
||||
const result = project ? await td.generateDocs(project, 'typedoc') : null;
|
||||
if (result) log.warn('TypeDoc:', result);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
log.header();
|
||||
typedoc(['src/human.ts']); // generate typedoc
|
||||
} else {
|
||||
exports.run = typedoc;
|
||||
exports.version = version;
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
const ts = require('typescript');
|
||||
const log = require('@vladmandic/pilogger');
|
||||
|
||||
const version = ts.version;
|
||||
|
||||
async function typings(entryPoint) {
|
||||
const configFileName = ts.findConfigFile('./', ts.sys.fileExists, 'tsconfig.json') || '';
|
||||
const configFile = ts.readConfigFile(configFileName, ts.sys.readFile);
|
||||
const compilerOptions = ts.parseJsonConfigFileContent(configFile.config, ts.sys, './');
|
||||
// add explicitly to avoid generating compiled js files
|
||||
compilerOptions.options.emitDeclarationOnly = true;
|
||||
log.info('Generate Typings:', entryPoint, 'outDir:', [compilerOptions.options.outDir]);
|
||||
const compilerHost = ts.createCompilerHost(compilerOptions.options);
|
||||
const program = ts.createProgram(entryPoint, compilerOptions.options, compilerHost);
|
||||
const emit = program.emit();
|
||||
const diag = ts
|
||||
.getPreEmitDiagnostics(program)
|
||||
.concat(emit.diagnostics);
|
||||
for (const info of diag) {
|
||||
const msg = info.messageText['messageText'] || info.messageText;
|
||||
if (msg.includes('package.json')) continue;
|
||||
if (info.file) {
|
||||
const pos = info.file.getLineAndCharacterOfPosition(info.start || 0);
|
||||
log.error(`TSC: ${info.file.fileName} [${pos.line + 1},${pos.character + 1}]:`, msg);
|
||||
} else {
|
||||
log.error('TSC:', msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
log.header();
|
||||
typings(['src/human.ts']); // generate typedoc
|
||||
} else {
|
||||
exports.run = typings;
|
||||
exports.version = version;
|
||||
}
|
Loading…
Reference in New Issue