@xgsd/workers 0.1.0-beta.2 → 0.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-7KEXJOGB.js → chunk-FPVPJQS7.js} +4 -1
- package/dist/index.cjs +67 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +51 -5
- package/dist/index.js.map +1 -1
- package/dist/process/workers.process.cjs +2 -1
- package/dist/process/workers.process.cjs.map +1 -1
- package/dist/process/workers.process.js +3 -2
- package/dist/process/workers.process.js.map +1 -1
- package/package.json +1 -1
- /package/dist/{chunk-7KEXJOGB.js.map → chunk-FPVPJQS7.js.map} +0 -0
|
@@ -212,10 +212,13 @@ ${bannerLines(options.banner)}
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
export {
|
|
215
|
+
__require,
|
|
215
216
|
WorkerErrorCode,
|
|
216
217
|
pathExists,
|
|
217
218
|
ensureDir,
|
|
219
|
+
readJsonSync,
|
|
220
|
+
pathExistsSync,
|
|
218
221
|
createBundle,
|
|
219
222
|
resolveDependency
|
|
220
223
|
};
|
|
221
|
-
//# sourceMappingURL=chunk-
|
|
224
|
+
//# sourceMappingURL=chunk-FPVPJQS7.js.map
|
package/dist/index.cjs
CHANGED
|
@@ -39,7 +39,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
39
39
|
|
|
40
40
|
// src/worker.ts
|
|
41
41
|
var import_child_process = require("child_process");
|
|
42
|
-
var
|
|
42
|
+
var import_path2 = require("path");
|
|
43
43
|
|
|
44
44
|
// src/types.ts
|
|
45
45
|
var WorkerErrorCode = /* @__PURE__ */ ((WorkerErrorCode2) => {
|
|
@@ -52,16 +52,68 @@ var WorkerErrorCode = /* @__PURE__ */ ((WorkerErrorCode2) => {
|
|
|
52
52
|
})(WorkerErrorCode || {});
|
|
53
53
|
|
|
54
54
|
// src/worker.ts
|
|
55
|
-
var
|
|
55
|
+
var import_fs3 = require("fs");
|
|
56
56
|
|
|
57
57
|
// src/util/fs.ts
|
|
58
|
+
var import_fs = require("fs");
|
|
58
59
|
var import_promises = require("fs/promises");
|
|
59
|
-
async function ensureDir(
|
|
60
|
-
await (0, import_promises.mkdir)(
|
|
60
|
+
async function ensureDir(path3) {
|
|
61
|
+
await (0, import_promises.mkdir)(path3, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
function readJsonSync(path3) {
|
|
64
|
+
return JSON.parse((0, import_fs.readFileSync)(path3, "utf8"));
|
|
65
|
+
}
|
|
66
|
+
function pathExistsSync(path3) {
|
|
67
|
+
try {
|
|
68
|
+
require("fs").accessSync(path3, import_promises.constants.F_OK);
|
|
69
|
+
return true;
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
61
73
|
}
|
|
62
74
|
|
|
63
75
|
// src/worker.ts
|
|
64
76
|
var import_crypto = require("crypto");
|
|
77
|
+
|
|
78
|
+
// src/util/package.ts
|
|
79
|
+
var import_path = __toESM(require("path"), 1);
|
|
80
|
+
function getPackageVersion(input, root = process.cwd()) {
|
|
81
|
+
try {
|
|
82
|
+
const pkgPath = resolvePackageJson(input, root);
|
|
83
|
+
console.log(pkgPath);
|
|
84
|
+
const json = readJsonSync(pkgPath);
|
|
85
|
+
if (!json?.version || typeof json.version !== "string") {
|
|
86
|
+
return "unknown";
|
|
87
|
+
}
|
|
88
|
+
return `${json.version}`;
|
|
89
|
+
} catch (err) {
|
|
90
|
+
return "unknown";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function resolvePackageJson(input, root) {
|
|
94
|
+
try {
|
|
95
|
+
return require.resolve(`${input}/package.json`, {
|
|
96
|
+
paths: [root]
|
|
97
|
+
});
|
|
98
|
+
} catch {
|
|
99
|
+
try {
|
|
100
|
+
const entry = require.resolve(input, {
|
|
101
|
+
paths: [root]
|
|
102
|
+
});
|
|
103
|
+
let dir = import_path.default.dirname(entry);
|
|
104
|
+
while (dir !== import_path.default.dirname(dir)) {
|
|
105
|
+
const candidate = import_path.default.join(dir, "package.json");
|
|
106
|
+
if (pathExistsSync(candidate)) return candidate;
|
|
107
|
+
dir = import_path.default.dirname(dir);
|
|
108
|
+
}
|
|
109
|
+
throw new Error(`package.json not found for ${input}`);
|
|
110
|
+
} catch (err) {
|
|
111
|
+
throw new Error(`Cannot resolve package.json for "${input}"`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/worker.ts
|
|
65
117
|
function startWorkerGuard(child, opts, suspended) {
|
|
66
118
|
const { ttl = 5e3, memory = 128 } = opts;
|
|
67
119
|
let ttlTimer = null;
|
|
@@ -117,21 +169,23 @@ async function runWorker(context) {
|
|
|
117
169
|
const id = (0, import_crypto.randomUUID)();
|
|
118
170
|
const ctx = { ...context, id };
|
|
119
171
|
const start = performance.now();
|
|
120
|
-
const
|
|
121
|
-
await ensureDir(
|
|
172
|
+
const path3 = (0, import_path2.join)(ctx.cwd, ctx.dist ?? ".xgsd");
|
|
173
|
+
await ensureDir(path3);
|
|
122
174
|
let started = false;
|
|
123
175
|
let completed = false;
|
|
124
176
|
let res;
|
|
125
177
|
const contextStr = JSON.stringify(ctx);
|
|
126
|
-
const
|
|
178
|
+
const version = getPackageVersion("@xgsd/workers", ctx.cwd);
|
|
179
|
+
const child = (0, import_child_process.fork)((0, import_path2.join)(__dirname, "process", "workers.process.js"), {
|
|
127
180
|
stdio: ["inherit", "pipe", "pipe", "ipc"],
|
|
128
181
|
//execArgv: [`--max-old-space-size=${ctx.limits?.memory ?? 128}`],
|
|
129
182
|
env: {
|
|
130
183
|
...ctx.env,
|
|
184
|
+
XGSD_WORKER_VERSION: version,
|
|
131
185
|
XGSD_CTX: contextStr
|
|
132
186
|
}
|
|
133
187
|
});
|
|
134
|
-
const events = (0,
|
|
188
|
+
const events = (0, import_fs3.createWriteStream)((0, import_path2.join)(path3, "events.jsonl"), { flags: "a" });
|
|
135
189
|
const startGuard = () => {
|
|
136
190
|
startWorkerGuard(child, ctx.limits ?? {}, (reason) => {
|
|
137
191
|
if (completed) return;
|
|
@@ -198,10 +252,11 @@ async function runWorker(context) {
|
|
|
198
252
|
res = msg.result;
|
|
199
253
|
}
|
|
200
254
|
res.duration = performance.now() - start;
|
|
201
|
-
(0,
|
|
202
|
-
(0,
|
|
255
|
+
(0, import_fs3.appendFileSync)(
|
|
256
|
+
(0, import_path2.join)(path3, "activations.jsonl"),
|
|
203
257
|
JSON.stringify({
|
|
204
258
|
id,
|
|
259
|
+
version,
|
|
205
260
|
ok: res.ok,
|
|
206
261
|
code: res.code,
|
|
207
262
|
duration: res.duration,
|
|
@@ -218,11 +273,11 @@ async function runWorker(context) {
|
|
|
218
273
|
}
|
|
219
274
|
|
|
220
275
|
// src/bundler.ts
|
|
221
|
-
var
|
|
276
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
222
277
|
var import_module = require("module");
|
|
223
278
|
function resolveDependency(dependency, projectRoot) {
|
|
224
279
|
try {
|
|
225
|
-
const require2 = (0, import_module.createRequire)((0,
|
|
280
|
+
const require2 = (0, import_module.createRequire)((0, import_path3.join)(projectRoot, "package.json"));
|
|
226
281
|
return require2(dependency);
|
|
227
282
|
} catch {
|
|
228
283
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/worker.ts","../src/types.ts","../src/util/fs.ts","../src/bundler.ts"],"sourcesContent":["import {WorkerResult, WorkerConfig} from './types.js'\nimport {runWorker} from './worker.js'\n\nexport {runWorker}\nexport * from './types.js'\n\nexport {resolveDependency} from './bundler.js'\n\nexport type Activation<T = unknown> = {\n id?: string\n data?: T\n env?: Record<string, unknown>\n cwd: string\n}\n\nexport type ActivationHandler = <T = unknown>(activation: Activation<T>) => Promise<WorkerResult<T>>\n\nexport function createHandler(config: WorkerConfig): ActivationHandler {\n return async function handler(activation) {\n return runWorker({\n ...config,\n id: activation.id,\n cwd: activation.cwd,\n data: activation.data,\n env: activation.env,\n })\n }\n}\n","import {fork} from 'child_process'\nimport {join} from 'path'\nimport {WorkerContext, WorkerError, WorkerErrorCode, WorkerResult} from './types.js'\nimport {createWriteStream, appendFileSync} from 'fs'\nimport {ensureDir} from './util/fs.js'\nimport {randomUUID} from 'crypto'\n\nexport function startWorkerGuard(\n child: any,\n opts: {\n ttl?: number\n memory?: number\n },\n suspended?: (reason: string) => void,\n) {\n const {ttl = 5000, memory = 128} = opts\n\n let ttlTimer: NodeJS.Timeout | null = null\n let killed = false\n\n const kill = (reason: string) => {\n if (killed) return\n killed = true\n\n if (ttlTimer) clearTimeout(ttlTimer)\n\n child.kill('SIGKILL')\n suspended?.(reason)\n }\n\n // TTL watchdog\n ttlTimer = setTimeout(() => {\n kill(`ttl exceeded limit (limit: ${ttl.toFixed(2)}ms)`)\n }, ttl)\n\n child.on('message', (msg: any) => {\n if (msg.type !== 'ALIVE') {\n return\n }\n\n // (v0.1.0) this isn't set in stone\n // may be worth using RSS\n const heapUsed = msg.memory?.heapUsed ?? 0\n const memMB = heapUsed / 1024 / 1024\n\n if (memMB > memory) {\n kill(`memory limit exceeded: ${memMB.toFixed(2)}MB/${memory.toFixed(2)}MB`)\n }\n })\n\n child.on('exit', () => {\n if (ttlTimer) clearTimeout(ttlTimer)\n })\n}\n\ntype ChildMessage<T> =\n | {type: 'ALIVE'; error: undefined; result: undefined}\n | {type: 'DONE'; result: WorkerResult<T>; error: undefined | null}\n | {type: 'ERROR'; result: undefined | null; error: WorkerError}\n\nexport function normaliseKeys(value: any): any {\n if (Array.isArray(value)) {\n return value.map(normaliseKeys)\n }\n\n if (value && typeof value === 'object' && value.constructor === Object) {\n const sorted: Record<string, any> = {}\n\n for (const key of Object.keys(value).sort()) {\n sorted[key] = normaliseKeys(value[key])\n }\n\n return sorted\n }\n\n return value\n}\n\nexport function formatWorkerResult(opts: {result?: any; error?: any; duration: number}): WorkerResult<any> {\n return {\n version: 'v1',\n ok: !opts.error,\n result: opts.result ?? null,\n error: opts.error ?? null,\n duration: opts.duration,\n }\n}\n\nexport async function runWorker<T>(context: WorkerContext): Promise<WorkerResult<T>> {\n return new Promise(async (resolve, reject) => {\n const id = randomUUID()\n\n const ctx = {...context, id}\n const start = performance.now()\n\n const path = join(ctx.cwd!, ctx.dist ?? '.xgsd')\n await ensureDir(path)\n\n let started = false\n let completed = false\n let res: WorkerResult<unknown>\n\n const contextStr = JSON.stringify(ctx)\n\n // TODO: remove hardcoded worker path\n const child = fork(join(__dirname, 'process', 'workers.process.js'), {\n stdio: ['inherit', 'pipe', 'pipe', 'ipc'],\n //execArgv: [`--max-old-space-size=${ctx.limits?.memory ?? 128}`],\n env: {\n ...ctx.env,\n XGSD_CTX: contextStr,\n },\n })\n\n const events = createWriteStream(join(path, 'events.jsonl'), {flags: 'a'})\n\n const startGuard = () => {\n startWorkerGuard(child, ctx.limits ?? {}, (reason: string) => {\n if (completed) return\n\n // normalise system/watch dog error\n // then *reject*\n const err: WorkerError = {\n code: WorkerErrorCode.CODE_WORKER_GUARD,\n message: `${reason}`,\n type: 'system',\n }\n\n console.warn(`[guard] worker suspended (reason: ${reason})`)\n\n writeEvent({\n type: 'error',\n message: err.message,\n guard: true,\n timestamp: Date.now(),\n })\n\n cleanup()\n\n resolve(formatWorkerResult({error: err, duration: performance.now() - start}))\n })\n }\n\n const cleanup = () => {\n if (child.connected) {\n child.removeAllListeners('message')\n\n child.disconnect()\n }\n }\n\n const writeEvent = (data: any) => {\n const normalised = normaliseKeys(data)\n events.write(JSON.stringify({...normalised, id}) + '\\n')\n }\n\n child.stdout?.on('data', (chunk) => {\n const lines = chunk.toString().split('\\n').filter(Boolean)\n\n for (const line of lines) {\n // log child process messages (typically from usercode)\n console.log(`${line}`)\n\n try {\n writeEvent(JSON.parse(line))\n } catch {\n // optionally fallback for non-json logs\n writeEvent({\n type: 'log',\n message: line,\n timestamp: Date.now(),\n })\n }\n }\n })\n\n child.stderr?.on('data', (chunk) => {\n const e = {type: 'error', message: chunk.toString(), timestamp: Date.now()}\n console.error(e.message)\n writeEvent(e)\n })\n\n child.on('message', (msg: ChildMessage<unknown>) => {\n if (msg.type !== 'ALIVE' && msg.type !== 'DONE' && msg.type !== 'ERROR') return\n\n if (msg.type === 'ALIVE') {\n if (!started) {\n started = true\n startGuard()\n }\n return\n }\n\n if (msg.type === 'ERROR') {\n const {error} = msg\n\n res = formatWorkerResult({error, duration: performance.now() - start})\n }\n\n if (msg.type === 'DONE') {\n // do something with result\n res = msg.result\n }\n\n res.duration = performance.now() - start\n\n // log activation\n appendFileSync(\n join(path, 'activations.jsonl'),\n JSON.stringify({\n id,\n ok: res.ok,\n code: res.code,\n duration: res.duration,\n error: res.error?.code ?? res.error?.message,\n result: res.result !== null,\n timestamp: new Date().toISOString(),\n }) + '\\n',\n )\n\n completed = true\n\n resolve(res as any)\n cleanup()\n })\n })\n}\n","export enum WorkerErrorCode {\n // thrown when limits exceeded (ttl/memory)\n CODE_WORKER_GUARD = 'CODE_WORKER_GUARD',\n\n // thrown when entry file is invalid/cannot be parsed\n CODE_INVALID_ENTRY_FILE = 'CODE_INVALID_ENTRY_FILE',\n\n // thrown when default is not a function\n CODE_INVALID_DEFAULT_FUNCTION = 'CODE_INVALID_DEFAULT_FUNCTION',\n\n CODE_INVALID_MIDDLEWARE_FUNCTION = 'CODE_INVALID_MIDDLEWARE_FUNCTION',\n\n // thrown when bundling fails\n CODE_BUNDLE_ERROR = 'CODE_BUNDLE_ERROR',\n}\n\nexport type WorkerErrorType = 'user' | 'system' | 'unknown'\nexport type WorkerError = {\n code?: WorkerErrorCode\n message?: string\n type?: WorkerErrorType\n}\n\nexport type WorkerResult<T> =\n | {\n version: 'v1'\n ok: true\n code?: number\n result: T\n error: null\n duration: number\n }\n | {\n version: 'v1'\n ok: false\n code?: number\n result: null\n error: WorkerError\n duration: number\n }\n\nexport type WorkerConfig = {\n entry: string\n dist?: string\n bundler?: {\n enabled?: boolean\n cache?: {\n strategy: 'never'\n }\n }\n http?: {\n enabled?: boolean\n }\n limits?: {\n ttl?: number\n memory?: number\n }\n}\n\nexport type WorkerContext<T = unknown> = WorkerConfig & {\n id?: string\n data: T\n cwd: string\n result?: any\n error?: any\n code?: number\n env?: Record<string, any>\n pid?: number\n}\n","import {readFileSync, writeFileSync, mkdirSync} from 'fs'\nimport {writeFile, readFile, constants, access, mkdir} from 'fs/promises'\nimport {dirname} from 'path'\n\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\n// ensureDir\nexport async function ensureDir(path: string): Promise<void> {\n await mkdir(path, {recursive: true})\n}\n\n// ensureDirSync\nexport function ensureDirSync(path: string): void {\n mkdirSync(path, {recursive: true})\n}\n\n// ensurePath\n// Ensures parent directory exists for a file path\nexport async function ensurePath(path: string): Promise<void> {\n await mkdir(dirname(path), {recursive: true})\n}\n\n// ensurePathSync\nexport function ensurePathSync(path: string): void {\n mkdirSync(dirname(path), {recursive: true})\n}\n\nexport async function readJson<T = any>(path: string): Promise<T> {\n const content = await readFile(path, 'utf8')\n return JSON.parse(content)\n}\n\nexport function readJsonSync<T = any>(path: string): T {\n return JSON.parse(readFileSync(path, 'utf8'))\n}\n\nexport async function writeJson(path: string, data: unknown, pretty = true): Promise<void> {\n const json = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)\n\n await writeFile(path, json, 'utf8')\n}\n\nexport function writeJsonSync(path: string, data: unknown, pretty = true): void {\n const json = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)\n\n writeFileSync(path, json, 'utf8')\n}\n\nexport function pathExistsSync(path: string): boolean {\n try {\n require('fs').accessSync(path, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n","import {createHash, timingSafeEqual} from 'crypto'\nimport path, {join, relative, sep} from 'path'\nimport {readdir, readFile, stat} from 'fs/promises'\nimport {pathExistsSync, readJsonSync, writeJsonSync} from './util/fs'\nimport {createRequire} from 'module'\n\nexport async function createBundle({\n project,\n dist,\n cwd,\n entry,\n cacheStrategy,\n log,\n}: {\n project: string\n dist?: string\n cwd?: string\n entry: string\n cacheStrategy: 'always' | 'change' | 'never'\n log?: boolean\n}): Promise<string> {\n const start = performance.now()\n\n const xgsd = join(project, dist ?? '.xgsd')\n const out = join(xgsd, 'bundle.js')\n const entryFile = join(project, entry)\n const packageJsonPath = join(project, 'package.json')\n const outPathRel = join(dist ?? '.xgsd', 'bundle.js')\n\n // v0.7 note\n // dont do this as it adds 20-30MB of memory before anything even runs\n // bundling is fine but current AST parsing/traversal is unneeded\n // instead split into two concerns: dependencies (from package.json) and code changes (from hashes)\n // do this instead:\n const hash = await calculateProjectHash(project)\n const outdir = path.dirname(out)\n\n const packageJson = await readJsonSync(packageJsonPath)\n\n const outPackageJsonPath = join(outdir, 'package.json')\n const cacheFilesExist = pathExistsSync(outPackageJsonPath) && pathExistsSync(out)\n\n if (cacheFilesExist && cacheStrategy === 'always') {\n console.log(`${outPathRel} loaded from cache (set cache.strategy = \"never\" if this is unintentional)`)\n\n return out\n }\n\n if (cacheFilesExist && cacheStrategy === 'change') {\n const outPackageJson = readJsonSync(outPackageJsonPath)\n\n if (outPackageJson.hash && safeHashCompare(outPackageJson.hash, hash)) {\n // cache hit\n\n console.log(`${outPathRel} loaded from cache (set cache.strategy = \"never\" if this is unintentional)`)\n\n return out\n }\n }\n\n const dependencies = Object.entries(readJsonSync(packageJsonPath).dependencies).map((d) => d[0])\n const generated = new Date().toISOString()\n\n // for now let esbuild notify of errors\n await bundle({\n entry: entryFile,\n out,\n banner: {\n generated,\n hash,\n },\n format: 'esm',\n dependencies,\n })\n\n writeJsonSync(path.join(outdir, 'package.json'), {\n ...packageJson,\n hash,\n generated,\n type: 'module',\n })\n\n const ms = performance.now() - start\n\n console.log(`[bundler] copied package.json to ${join(dist ?? '.xgsd', 'package.json')}`)\n console.log(`[bundler] ${entry} bundled to ${outPathRel}`)\n console.log(`[bundler] completed in ${ms.toFixed(2)}ms.`)\n\n if (cacheStrategy === 'never') {\n console.warn(`[bundler] (warn) you can speed this up with bundler.cache.strategy = always|change.`)\n }\n\n return out\n}\n\nfunction bannerLines(object: Record<string, string>) {\n const lines = []\n lines.push(' * xGSD bundle.js')\n for (const key of Object.keys(object)) {\n lines.push(` * ${key}: ${object[key]}`)\n }\n lines.push(' * WARNING: this file is generated. Do not edit manually.')\n return lines.join('\\r\\n')\n}\n\nexport function resolvePath(moduleName: string, root: string): string {\n return require.resolve(moduleName, {\n paths: [root],\n })\n}\n\nexport function resolveDependency(dependency: string, projectRoot: string): any {\n try {\n const require = createRequire(join(projectRoot, 'package.json'))\n return require(dependency)\n } catch {}\n\n throw new Error(\n `Could not resolve ${dependency}.\\nInstall it with \\`yarn add ${dependency}\\`.\\nPath: ${projectRoot}.`,\n )\n}\n\nexport type WalkedFile = {\n path: string\n hash: string\n size: number\n}\n\ntype WalkOptions = {\n ignore?: string[]\n filter?: (path: string) => boolean\n}\n\nexport async function calculateProjectHash(project: string): Promise<string> {\n const hashes = await collectProjectHashes(project, {\n ignore: ['node_modules', '.xgsd', 'dist', '.git'],\n filter: (path) => path.endsWith('.js') || path.endsWith('.ts'),\n })\n\n const normalised = hashes\n .map((h) => h.hash.trim().slice(0, 9))\n .sort()\n .join('|')\n\n return createHash('sha256').update(normalised).digest('hex')\n}\n\nexport async function collectProjectHashes(projectPath: string, options: WalkOptions = {}): Promise<WalkedFile[]> {\n const {ignore = ['node_modules'], filter = () => true} = options\n\n const files: WalkedFile[] = []\n\n const ignored = new Set(ignore)\n\n const shouldIgnore = (target: string) => {\n const parts = relative(projectPath, target).split(sep)\n\n return parts.some((part) => ignored.has(part))\n }\n\n const hashFile = async (filePath: string) => {\n const buffer = await readFile(filePath)\n\n return createHash('sha256').update(buffer).digest('hex')\n }\n\n const visit = async (current: string): Promise<void> => {\n if (shouldIgnore(current)) {\n return\n }\n\n const entries = await readdir(current)\n\n for (const entry of entries) {\n const fullPath = join(current, entry)\n\n if (shouldIgnore(fullPath)) {\n continue\n }\n\n const info = await stat(fullPath)\n\n if (info.isDirectory()) {\n await visit(fullPath)\n continue\n }\n\n if (!info.isFile()) {\n continue\n }\n\n const path = relative(projectPath, fullPath)\n\n if (!filter(path)) {\n continue\n }\n\n files.push({\n path,\n hash: await hashFile(fullPath),\n size: info.size,\n })\n }\n }\n\n await visit(projectPath)\n\n return files.sort((a, b) => a.path.localeCompare(b.path))\n}\n\nexport function safeHashCompare(a: string, b: string): boolean {\n const abuf = Buffer.from(a, 'hex')\n const bbuf = Buffer.from(b, 'hex')\n\n return timingSafeEqual(abuf, bbuf)\n}\n\nexport async function bundle(options: {\n entry: string\n out: string\n format: 'esm' | 'cjs'\n banner: Record<string, string>\n dependencies: string[]\n}) {\n const {dependencies} = options\n\n const esbuild = resolveDependency('esbuild', path.dirname(options.entry))\n\n if (esbuild.version) {\n console.log(`[bundler] building with esbuild@${esbuild.version}`)\n }\n\n return esbuild.build({\n keepNames: true,\n entryPoints: [options.entry],\n bundle: true,\n platform: 'node',\n outfile: options.out,\n format: options.format,\n minify: false,\n sourcemap: false,\n external: dependencies,\n banner: {\n js: `\n/**\n${bannerLines(options.banner)}\n */\n`.trim(),\n },\n })\n}\n"],"mappings":"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","names":["WorkerErrorCode","path","path","import_path","require"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/worker.ts","../src/types.ts","../src/util/fs.ts","../src/util/package.ts","../src/bundler.ts"],"sourcesContent":["import {WorkerResult, WorkerConfig} from './types.js'\nimport {runWorker} from './worker.js'\n\nexport {runWorker}\nexport * from './types.js'\n\nexport {resolveDependency} from './bundler.js'\n\nexport type Activation<T = unknown> = {\n id?: string\n data?: T\n env?: Record<string, unknown>\n cwd: string\n}\n\nexport type ActivationHandler = <T = unknown>(activation: Activation<T>) => Promise<WorkerResult<T>>\n\nexport function createHandler(config: WorkerConfig): ActivationHandler {\n return async function handler(activation) {\n return runWorker({\n ...config,\n id: activation.id,\n cwd: activation.cwd,\n data: activation.data,\n env: activation.env,\n })\n }\n}\n","import {fork} from 'child_process'\nimport {join} from 'path'\nimport {WorkerContext, WorkerError, WorkerErrorCode, WorkerResult} from './types.js'\nimport {createWriteStream, appendFileSync} from 'fs'\nimport {ensureDir} from './util/fs.js'\nimport {randomUUID} from 'crypto'\nimport {getPackageVersion} from './util/package.js'\n\nexport function startWorkerGuard(\n child: any,\n opts: {\n ttl?: number\n memory?: number\n },\n suspended?: (reason: string) => void,\n) {\n const {ttl = 5000, memory = 128} = opts\n\n let ttlTimer: NodeJS.Timeout | null = null\n let killed = false\n\n const kill = (reason: string) => {\n if (killed) return\n killed = true\n\n if (ttlTimer) clearTimeout(ttlTimer)\n\n child.kill('SIGKILL')\n suspended?.(reason)\n }\n\n // TTL watchdog\n ttlTimer = setTimeout(() => {\n kill(`ttl exceeded limit (limit: ${ttl.toFixed(2)}ms)`)\n }, ttl)\n\n child.on('message', (msg: any) => {\n if (msg.type !== 'ALIVE') {\n return\n }\n\n // (v0.1.0) this isn't set in stone\n // may be worth using RSS\n const heapUsed = msg.memory?.heapUsed ?? 0\n const memMB = heapUsed / 1024 / 1024\n\n if (memMB > memory) {\n kill(`memory limit exceeded: ${memMB.toFixed(2)}MB/${memory.toFixed(2)}MB`)\n }\n })\n\n child.on('exit', () => {\n if (ttlTimer) clearTimeout(ttlTimer)\n })\n}\n\ntype ChildMessage<T> =\n | {type: 'ALIVE'; error: undefined; result: undefined}\n | {type: 'DONE'; result: WorkerResult<T>; error: undefined | null}\n | {type: 'ERROR'; result: undefined | null; error: WorkerError}\n\nexport function normaliseKeys(value: any): any {\n if (Array.isArray(value)) {\n return value.map(normaliseKeys)\n }\n\n if (value && typeof value === 'object' && value.constructor === Object) {\n const sorted: Record<string, any> = {}\n\n for (const key of Object.keys(value).sort()) {\n sorted[key] = normaliseKeys(value[key])\n }\n\n return sorted\n }\n\n return value\n}\n\nexport function formatWorkerResult(opts: {result?: any; error?: any; duration: number}): WorkerResult<any> {\n return {\n version: 'v1',\n ok: !opts.error,\n result: opts.result ?? null,\n error: opts.error ?? null,\n duration: opts.duration,\n }\n}\n\nexport async function runWorker<T>(context: WorkerContext): Promise<WorkerResult<T>> {\n return new Promise(async (resolve, reject) => {\n const id = randomUUID()\n\n const ctx = {...context, id}\n const start = performance.now()\n\n const path = join(ctx.cwd!, ctx.dist ?? '.xgsd')\n await ensureDir(path)\n\n let started = false\n let completed = false\n let res: WorkerResult<unknown>\n\n const contextStr = JSON.stringify(ctx)\n const version = getPackageVersion('@xgsd/workers', ctx.cwd)\n\n // TODO: remove hardcoded worker path\n const child = fork(join(__dirname, 'process', 'workers.process.js'), {\n stdio: ['inherit', 'pipe', 'pipe', 'ipc'],\n //execArgv: [`--max-old-space-size=${ctx.limits?.memory ?? 128}`],\n env: {\n ...ctx.env,\n XGSD_WORKER_VERSION: version,\n XGSD_CTX: contextStr,\n },\n })\n\n const events = createWriteStream(join(path, 'events.jsonl'), {flags: 'a'})\n\n const startGuard = () => {\n startWorkerGuard(child, ctx.limits ?? {}, (reason: string) => {\n if (completed) return\n\n // normalise system/watch dog error\n // then *reject*\n const err: WorkerError = {\n code: WorkerErrorCode.CODE_WORKER_GUARD,\n message: `${reason}`,\n type: 'system',\n }\n\n console.warn(`[guard] worker suspended (reason: ${reason})`)\n\n writeEvent({\n type: 'error',\n message: err.message,\n guard: true,\n timestamp: Date.now(),\n })\n\n cleanup()\n\n resolve(formatWorkerResult({error: err, duration: performance.now() - start}))\n })\n }\n\n const cleanup = () => {\n if (child.connected) {\n child.removeAllListeners('message')\n\n child.disconnect()\n }\n }\n\n const writeEvent = (data: any) => {\n const normalised = normaliseKeys(data)\n events.write(JSON.stringify({...normalised, id}) + '\\n')\n }\n\n child.stdout?.on('data', (chunk) => {\n const lines = chunk.toString().split('\\n').filter(Boolean)\n\n for (const line of lines) {\n // log child process messages (typically from usercode)\n console.log(`${line}`)\n\n try {\n writeEvent(JSON.parse(line))\n } catch {\n // optionally fallback for non-json logs\n writeEvent({\n type: 'log',\n message: line,\n timestamp: Date.now(),\n })\n }\n }\n })\n\n child.stderr?.on('data', (chunk) => {\n const e = {type: 'error', message: chunk.toString(), timestamp: Date.now()}\n console.error(e.message)\n writeEvent(e)\n })\n\n child.on('message', (msg: ChildMessage<unknown>) => {\n if (msg.type !== 'ALIVE' && msg.type !== 'DONE' && msg.type !== 'ERROR') return\n\n if (msg.type === 'ALIVE') {\n if (!started) {\n started = true\n startGuard()\n }\n return\n }\n\n if (msg.type === 'ERROR') {\n const {error} = msg\n\n res = formatWorkerResult({error, duration: performance.now() - start})\n }\n\n if (msg.type === 'DONE') {\n // do something with result\n res = msg.result\n }\n\n res.duration = performance.now() - start\n\n // log activation\n appendFileSync(\n join(path, 'activations.jsonl'),\n JSON.stringify({\n id,\n version,\n ok: res.ok,\n code: res.code,\n duration: res.duration,\n error: res.error?.code ?? res.error?.message,\n result: res.result !== null,\n timestamp: new Date().toISOString(),\n }) + '\\n',\n )\n\n completed = true\n\n resolve(res as any)\n cleanup()\n })\n })\n}\n","export enum WorkerErrorCode {\n // thrown when limits exceeded (ttl/memory)\n CODE_WORKER_GUARD = 'CODE_WORKER_GUARD',\n\n // thrown when entry file is invalid/cannot be parsed\n CODE_INVALID_ENTRY_FILE = 'CODE_INVALID_ENTRY_FILE',\n\n // thrown when default is not a function\n CODE_INVALID_DEFAULT_FUNCTION = 'CODE_INVALID_DEFAULT_FUNCTION',\n\n CODE_INVALID_MIDDLEWARE_FUNCTION = 'CODE_INVALID_MIDDLEWARE_FUNCTION',\n\n // thrown when bundling fails\n CODE_BUNDLE_ERROR = 'CODE_BUNDLE_ERROR',\n}\n\nexport type WorkerErrorType = 'user' | 'system' | 'unknown'\nexport type WorkerError = {\n code?: WorkerErrorCode\n message?: string\n type?: WorkerErrorType\n}\n\nexport type WorkerResult<T> =\n | {\n version: 'v1'\n ok: true\n code?: number\n result: T\n error: null\n duration: number\n }\n | {\n version: 'v1'\n ok: false\n code?: number\n result: null\n error: WorkerError\n duration: number\n }\n\nexport type WorkerConfig = {\n entry: string\n dist?: string\n bundler?: {\n enabled?: boolean\n cache?: {\n strategy: 'never'\n }\n }\n http?: {\n enabled?: boolean\n }\n limits?: {\n ttl?: number\n memory?: number\n }\n}\n\nexport type WorkerContext<T = unknown> = WorkerConfig & {\n id?: string\n data: T\n cwd: string\n result?: any\n error?: any\n code?: number\n env?: Record<string, any>\n pid?: number\n}\n","import {readFileSync, writeFileSync, mkdirSync} from 'fs'\nimport {writeFile, readFile, constants, access, mkdir} from 'fs/promises'\nimport {dirname} from 'path'\n\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\n// ensureDir\nexport async function ensureDir(path: string): Promise<void> {\n await mkdir(path, {recursive: true})\n}\n\n// ensureDirSync\nexport function ensureDirSync(path: string): void {\n mkdirSync(path, {recursive: true})\n}\n\n// ensurePath\n// Ensures parent directory exists for a file path\nexport async function ensurePath(path: string): Promise<void> {\n await mkdir(dirname(path), {recursive: true})\n}\n\n// ensurePathSync\nexport function ensurePathSync(path: string): void {\n mkdirSync(dirname(path), {recursive: true})\n}\n\nexport async function readJson<T = any>(path: string): Promise<T> {\n const content = await readFile(path, 'utf8')\n return JSON.parse(content)\n}\n\nexport function readJsonSync<T = any>(path: string): T {\n return JSON.parse(readFileSync(path, 'utf8'))\n}\n\nexport async function writeJson(path: string, data: unknown, pretty = true): Promise<void> {\n const json = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)\n\n await writeFile(path, json, 'utf8')\n}\n\nexport function writeJsonSync(path: string, data: unknown, pretty = true): void {\n const json = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)\n\n writeFileSync(path, json, 'utf8')\n}\n\nexport function pathExistsSync(path: string): boolean {\n try {\n require('fs').accessSync(path, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n","import {pathExistsSync, readJsonSync} from './fs'\nimport path from 'path'\n\nexport function getPackageVersion(input: string, root: string = process.cwd()): string {\n try {\n const pkgPath = resolvePackageJson(input, root)\n\n console.log(pkgPath)\n\n const json = readJsonSync(pkgPath)\n\n if (!json?.version || typeof json.version !== 'string') {\n return 'unknown'\n }\n\n return `${json.version}`\n } catch (err: any) {\n return 'unknown'\n }\n}\n\nexport function resolvePackageJson(input: string, root: string): string {\n try {\n return require.resolve(`${input}/package.json`, {\n paths: [root],\n })\n } catch {\n try {\n const entry = require.resolve(input, {\n paths: [root],\n })\n\n let dir = path.dirname(entry)\n\n while (dir !== path.dirname(dir)) {\n const candidate = path.join(dir, 'package.json')\n if (pathExistsSync(candidate)) return candidate\n dir = path.dirname(dir)\n }\n\n throw new Error(`package.json not found for ${input}`)\n } catch (err: any) {\n throw new Error(`Cannot resolve package.json for \"${input}\"`)\n }\n }\n}\n","import {createHash, timingSafeEqual} from 'crypto'\nimport path, {join, relative, sep} from 'path'\nimport {readdir, readFile, stat} from 'fs/promises'\nimport {pathExistsSync, readJsonSync, writeJsonSync} from './util/fs'\nimport {createRequire} from 'module'\n\nexport async function createBundle({\n project,\n dist,\n cwd,\n entry,\n cacheStrategy,\n log,\n}: {\n project: string\n dist?: string\n cwd?: string\n entry: string\n cacheStrategy: 'always' | 'change' | 'never'\n log?: boolean\n}): Promise<string> {\n const start = performance.now()\n\n const xgsd = join(project, dist ?? '.xgsd')\n const out = join(xgsd, 'bundle.js')\n const entryFile = join(project, entry)\n const packageJsonPath = join(project, 'package.json')\n const outPathRel = join(dist ?? '.xgsd', 'bundle.js')\n\n // v0.7 note\n // dont do this as it adds 20-30MB of memory before anything even runs\n // bundling is fine but current AST parsing/traversal is unneeded\n // instead split into two concerns: dependencies (from package.json) and code changes (from hashes)\n // do this instead:\n const hash = await calculateProjectHash(project)\n const outdir = path.dirname(out)\n\n const packageJson = await readJsonSync(packageJsonPath)\n\n const outPackageJsonPath = join(outdir, 'package.json')\n const cacheFilesExist = pathExistsSync(outPackageJsonPath) && pathExistsSync(out)\n\n if (cacheFilesExist && cacheStrategy === 'always') {\n console.log(`${outPathRel} loaded from cache (set cache.strategy = \"never\" if this is unintentional)`)\n\n return out\n }\n\n if (cacheFilesExist && cacheStrategy === 'change') {\n const outPackageJson = readJsonSync(outPackageJsonPath)\n\n if (outPackageJson.hash && safeHashCompare(outPackageJson.hash, hash)) {\n // cache hit\n\n console.log(`${outPathRel} loaded from cache (set cache.strategy = \"never\" if this is unintentional)`)\n\n return out\n }\n }\n\n const dependencies = Object.entries(readJsonSync(packageJsonPath).dependencies).map((d) => d[0])\n const generated = new Date().toISOString()\n\n // for now let esbuild notify of errors\n await bundle({\n entry: entryFile,\n out,\n banner: {\n generated,\n hash,\n },\n format: 'esm',\n dependencies,\n })\n\n writeJsonSync(path.join(outdir, 'package.json'), {\n ...packageJson,\n hash,\n generated,\n type: 'module',\n })\n\n const ms = performance.now() - start\n\n console.log(`[bundler] copied package.json to ${join(dist ?? '.xgsd', 'package.json')}`)\n console.log(`[bundler] ${entry} bundled to ${outPathRel}`)\n console.log(`[bundler] completed in ${ms.toFixed(2)}ms.`)\n\n if (cacheStrategy === 'never') {\n console.warn(`[bundler] (warn) you can speed this up with bundler.cache.strategy = always|change.`)\n }\n\n return out\n}\n\nfunction bannerLines(object: Record<string, string>) {\n const lines = []\n lines.push(' * xGSD bundle.js')\n for (const key of Object.keys(object)) {\n lines.push(` * ${key}: ${object[key]}`)\n }\n lines.push(' * WARNING: this file is generated. Do not edit manually.')\n return lines.join('\\r\\n')\n}\n\nexport function resolvePath(moduleName: string, root: string): string {\n return require.resolve(moduleName, {\n paths: [root],\n })\n}\n\nexport function resolveDependency(dependency: string, projectRoot: string): any {\n try {\n const require = createRequire(join(projectRoot, 'package.json'))\n return require(dependency)\n } catch {}\n\n throw new Error(\n `Could not resolve ${dependency}.\\nInstall it with \\`yarn add ${dependency}\\`.\\nPath: ${projectRoot}.`,\n )\n}\n\nexport type WalkedFile = {\n path: string\n hash: string\n size: number\n}\n\ntype WalkOptions = {\n ignore?: string[]\n filter?: (path: string) => boolean\n}\n\nexport async function calculateProjectHash(project: string): Promise<string> {\n const hashes = await collectProjectHashes(project, {\n ignore: ['node_modules', '.xgsd', 'dist', '.git'],\n filter: (path) => path.endsWith('.js') || path.endsWith('.ts'),\n })\n\n const normalised = hashes\n .map((h) => h.hash.trim().slice(0, 9))\n .sort()\n .join('|')\n\n return createHash('sha256').update(normalised).digest('hex')\n}\n\nexport async function collectProjectHashes(projectPath: string, options: WalkOptions = {}): Promise<WalkedFile[]> {\n const {ignore = ['node_modules'], filter = () => true} = options\n\n const files: WalkedFile[] = []\n\n const ignored = new Set(ignore)\n\n const shouldIgnore = (target: string) => {\n const parts = relative(projectPath, target).split(sep)\n\n return parts.some((part) => ignored.has(part))\n }\n\n const hashFile = async (filePath: string) => {\n const buffer = await readFile(filePath)\n\n return createHash('sha256').update(buffer).digest('hex')\n }\n\n const visit = async (current: string): Promise<void> => {\n if (shouldIgnore(current)) {\n return\n }\n\n const entries = await readdir(current)\n\n for (const entry of entries) {\n const fullPath = join(current, entry)\n\n if (shouldIgnore(fullPath)) {\n continue\n }\n\n const info = await stat(fullPath)\n\n if (info.isDirectory()) {\n await visit(fullPath)\n continue\n }\n\n if (!info.isFile()) {\n continue\n }\n\n const path = relative(projectPath, fullPath)\n\n if (!filter(path)) {\n continue\n }\n\n files.push({\n path,\n hash: await hashFile(fullPath),\n size: info.size,\n })\n }\n }\n\n await visit(projectPath)\n\n return files.sort((a, b) => a.path.localeCompare(b.path))\n}\n\nexport function safeHashCompare(a: string, b: string): boolean {\n const abuf = Buffer.from(a, 'hex')\n const bbuf = Buffer.from(b, 'hex')\n\n return timingSafeEqual(abuf, bbuf)\n}\n\nexport async function bundle(options: {\n entry: string\n out: string\n format: 'esm' | 'cjs'\n banner: Record<string, string>\n dependencies: string[]\n}) {\n const {dependencies} = options\n\n const esbuild = resolveDependency('esbuild', path.dirname(options.entry))\n\n if (esbuild.version) {\n console.log(`[bundler] building with esbuild@${esbuild.version}`)\n }\n\n return esbuild.build({\n keepNames: true,\n entryPoints: [options.entry],\n bundle: true,\n platform: 'node',\n outfile: options.out,\n format: options.format,\n minify: false,\n sourcemap: false,\n external: dependencies,\n banner: {\n js: `\n/**\n${bannerLines(options.banner)}\n */\n`.trim(),\n },\n })\n}\n"],"mappings":"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","names":["import_path","WorkerErrorCode","import_fs","path","path","path","path","path","import_path","require"]}
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,57 @@
|
|
|
1
1
|
import {
|
|
2
2
|
WorkerErrorCode,
|
|
3
|
+
__require,
|
|
3
4
|
ensureDir,
|
|
5
|
+
pathExistsSync,
|
|
6
|
+
readJsonSync,
|
|
4
7
|
resolveDependency
|
|
5
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-FPVPJQS7.js";
|
|
6
9
|
|
|
7
10
|
// src/worker.ts
|
|
8
11
|
import { fork } from "child_process";
|
|
9
12
|
import { join } from "path";
|
|
10
13
|
import { createWriteStream, appendFileSync } from "fs";
|
|
11
14
|
import { randomUUID } from "crypto";
|
|
15
|
+
|
|
16
|
+
// src/util/package.ts
|
|
17
|
+
import path from "path";
|
|
18
|
+
function getPackageVersion(input, root = process.cwd()) {
|
|
19
|
+
try {
|
|
20
|
+
const pkgPath = resolvePackageJson(input, root);
|
|
21
|
+
console.log(pkgPath);
|
|
22
|
+
const json = readJsonSync(pkgPath);
|
|
23
|
+
if (!json?.version || typeof json.version !== "string") {
|
|
24
|
+
return "unknown";
|
|
25
|
+
}
|
|
26
|
+
return `${json.version}`;
|
|
27
|
+
} catch (err) {
|
|
28
|
+
return "unknown";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function resolvePackageJson(input, root) {
|
|
32
|
+
try {
|
|
33
|
+
return __require.resolve(`${input}/package.json`, {
|
|
34
|
+
paths: [root]
|
|
35
|
+
});
|
|
36
|
+
} catch {
|
|
37
|
+
try {
|
|
38
|
+
const entry = __require.resolve(input, {
|
|
39
|
+
paths: [root]
|
|
40
|
+
});
|
|
41
|
+
let dir = path.dirname(entry);
|
|
42
|
+
while (dir !== path.dirname(dir)) {
|
|
43
|
+
const candidate = path.join(dir, "package.json");
|
|
44
|
+
if (pathExistsSync(candidate)) return candidate;
|
|
45
|
+
dir = path.dirname(dir);
|
|
46
|
+
}
|
|
47
|
+
throw new Error(`package.json not found for ${input}`);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
throw new Error(`Cannot resolve package.json for "${input}"`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/worker.ts
|
|
12
55
|
function startWorkerGuard(child, opts, suspended) {
|
|
13
56
|
const { ttl = 5e3, memory = 128 } = opts;
|
|
14
57
|
let ttlTimer = null;
|
|
@@ -64,21 +107,23 @@ async function runWorker(context) {
|
|
|
64
107
|
const id = randomUUID();
|
|
65
108
|
const ctx = { ...context, id };
|
|
66
109
|
const start = performance.now();
|
|
67
|
-
const
|
|
68
|
-
await ensureDir(
|
|
110
|
+
const path2 = join(ctx.cwd, ctx.dist ?? ".xgsd");
|
|
111
|
+
await ensureDir(path2);
|
|
69
112
|
let started = false;
|
|
70
113
|
let completed = false;
|
|
71
114
|
let res;
|
|
72
115
|
const contextStr = JSON.stringify(ctx);
|
|
116
|
+
const version = getPackageVersion("@xgsd/workers", ctx.cwd);
|
|
73
117
|
const child = fork(join(__dirname, "process", "workers.process.js"), {
|
|
74
118
|
stdio: ["inherit", "pipe", "pipe", "ipc"],
|
|
75
119
|
//execArgv: [`--max-old-space-size=${ctx.limits?.memory ?? 128}`],
|
|
76
120
|
env: {
|
|
77
121
|
...ctx.env,
|
|
122
|
+
XGSD_WORKER_VERSION: version,
|
|
78
123
|
XGSD_CTX: contextStr
|
|
79
124
|
}
|
|
80
125
|
});
|
|
81
|
-
const events = createWriteStream(join(
|
|
126
|
+
const events = createWriteStream(join(path2, "events.jsonl"), { flags: "a" });
|
|
82
127
|
const startGuard = () => {
|
|
83
128
|
startWorkerGuard(child, ctx.limits ?? {}, (reason) => {
|
|
84
129
|
if (completed) return;
|
|
@@ -146,9 +191,10 @@ async function runWorker(context) {
|
|
|
146
191
|
}
|
|
147
192
|
res.duration = performance.now() - start;
|
|
148
193
|
appendFileSync(
|
|
149
|
-
join(
|
|
194
|
+
join(path2, "activations.jsonl"),
|
|
150
195
|
JSON.stringify({
|
|
151
196
|
id,
|
|
197
|
+
version,
|
|
152
198
|
ok: res.ok,
|
|
153
199
|
code: res.code,
|
|
154
200
|
duration: res.duration,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker.ts","../src/index.ts"],"sourcesContent":["import {fork} from 'child_process'\nimport {join} from 'path'\nimport {WorkerContext, WorkerError, WorkerErrorCode, WorkerResult} from './types.js'\nimport {createWriteStream, appendFileSync} from 'fs'\nimport {ensureDir} from './util/fs.js'\nimport {randomUUID} from 'crypto'\n\nexport function startWorkerGuard(\n child: any,\n opts: {\n ttl?: number\n memory?: number\n },\n suspended?: (reason: string) => void,\n) {\n const {ttl = 5000, memory = 128} = opts\n\n let ttlTimer: NodeJS.Timeout | null = null\n let killed = false\n\n const kill = (reason: string) => {\n if (killed) return\n killed = true\n\n if (ttlTimer) clearTimeout(ttlTimer)\n\n child.kill('SIGKILL')\n suspended?.(reason)\n }\n\n // TTL watchdog\n ttlTimer = setTimeout(() => {\n kill(`ttl exceeded limit (limit: ${ttl.toFixed(2)}ms)`)\n }, ttl)\n\n child.on('message', (msg: any) => {\n if (msg.type !== 'ALIVE') {\n return\n }\n\n // (v0.1.0) this isn't set in stone\n // may be worth using RSS\n const heapUsed = msg.memory?.heapUsed ?? 0\n const memMB = heapUsed / 1024 / 1024\n\n if (memMB > memory) {\n kill(`memory limit exceeded: ${memMB.toFixed(2)}MB/${memory.toFixed(2)}MB`)\n }\n })\n\n child.on('exit', () => {\n if (ttlTimer) clearTimeout(ttlTimer)\n })\n}\n\ntype ChildMessage<T> =\n | {type: 'ALIVE'; error: undefined; result: undefined}\n | {type: 'DONE'; result: WorkerResult<T>; error: undefined | null}\n | {type: 'ERROR'; result: undefined | null; error: WorkerError}\n\nexport function normaliseKeys(value: any): any {\n if (Array.isArray(value)) {\n return value.map(normaliseKeys)\n }\n\n if (value && typeof value === 'object' && value.constructor === Object) {\n const sorted: Record<string, any> = {}\n\n for (const key of Object.keys(value).sort()) {\n sorted[key] = normaliseKeys(value[key])\n }\n\n return sorted\n }\n\n return value\n}\n\nexport function formatWorkerResult(opts: {result?: any; error?: any; duration: number}): WorkerResult<any> {\n return {\n version: 'v1',\n ok: !opts.error,\n result: opts.result ?? null,\n error: opts.error ?? null,\n duration: opts.duration,\n }\n}\n\nexport async function runWorker<T>(context: WorkerContext): Promise<WorkerResult<T>> {\n return new Promise(async (resolve, reject) => {\n const id = randomUUID()\n\n const ctx = {...context, id}\n const start = performance.now()\n\n const path = join(ctx.cwd!, ctx.dist ?? '.xgsd')\n await ensureDir(path)\n\n let started = false\n let completed = false\n let res: WorkerResult<unknown>\n\n const contextStr = JSON.stringify(ctx)\n\n // TODO: remove hardcoded worker path\n const child = fork(join(__dirname, 'process', 'workers.process.js'), {\n stdio: ['inherit', 'pipe', 'pipe', 'ipc'],\n //execArgv: [`--max-old-space-size=${ctx.limits?.memory ?? 128}`],\n env: {\n ...ctx.env,\n XGSD_CTX: contextStr,\n },\n })\n\n const events = createWriteStream(join(path, 'events.jsonl'), {flags: 'a'})\n\n const startGuard = () => {\n startWorkerGuard(child, ctx.limits ?? {}, (reason: string) => {\n if (completed) return\n\n // normalise system/watch dog error\n // then *reject*\n const err: WorkerError = {\n code: WorkerErrorCode.CODE_WORKER_GUARD,\n message: `${reason}`,\n type: 'system',\n }\n\n console.warn(`[guard] worker suspended (reason: ${reason})`)\n\n writeEvent({\n type: 'error',\n message: err.message,\n guard: true,\n timestamp: Date.now(),\n })\n\n cleanup()\n\n resolve(formatWorkerResult({error: err, duration: performance.now() - start}))\n })\n }\n\n const cleanup = () => {\n if (child.connected) {\n child.removeAllListeners('message')\n\n child.disconnect()\n }\n }\n\n const writeEvent = (data: any) => {\n const normalised = normaliseKeys(data)\n events.write(JSON.stringify({...normalised, id}) + '\\n')\n }\n\n child.stdout?.on('data', (chunk) => {\n const lines = chunk.toString().split('\\n').filter(Boolean)\n\n for (const line of lines) {\n // log child process messages (typically from usercode)\n console.log(`${line}`)\n\n try {\n writeEvent(JSON.parse(line))\n } catch {\n // optionally fallback for non-json logs\n writeEvent({\n type: 'log',\n message: line,\n timestamp: Date.now(),\n })\n }\n }\n })\n\n child.stderr?.on('data', (chunk) => {\n const e = {type: 'error', message: chunk.toString(), timestamp: Date.now()}\n console.error(e.message)\n writeEvent(e)\n })\n\n child.on('message', (msg: ChildMessage<unknown>) => {\n if (msg.type !== 'ALIVE' && msg.type !== 'DONE' && msg.type !== 'ERROR') return\n\n if (msg.type === 'ALIVE') {\n if (!started) {\n started = true\n startGuard()\n }\n return\n }\n\n if (msg.type === 'ERROR') {\n const {error} = msg\n\n res = formatWorkerResult({error, duration: performance.now() - start})\n }\n\n if (msg.type === 'DONE') {\n // do something with result\n res = msg.result\n }\n\n res.duration = performance.now() - start\n\n // log activation\n appendFileSync(\n join(path, 'activations.jsonl'),\n JSON.stringify({\n id,\n ok: res.ok,\n code: res.code,\n duration: res.duration,\n error: res.error?.code ?? res.error?.message,\n result: res.result !== null,\n timestamp: new Date().toISOString(),\n }) + '\\n',\n )\n\n completed = true\n\n resolve(res as any)\n cleanup()\n })\n })\n}\n","import {WorkerResult, WorkerConfig} from './types.js'\nimport {runWorker} from './worker.js'\n\nexport {runWorker}\nexport * from './types.js'\n\nexport {resolveDependency} from './bundler.js'\n\nexport type Activation<T = unknown> = {\n id?: string\n data?: T\n env?: Record<string, unknown>\n cwd: string\n}\n\nexport type ActivationHandler = <T = unknown>(activation: Activation<T>) => Promise<WorkerResult<T>>\n\nexport function createHandler(config: WorkerConfig): ActivationHandler {\n return async function handler(activation) {\n return runWorker({\n ...config,\n id: activation.id,\n cwd: activation.cwd,\n data: activation.data,\n env: activation.env,\n })\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/worker.ts","../src/util/package.ts","../src/index.ts"],"sourcesContent":["import {fork} from 'child_process'\nimport {join} from 'path'\nimport {WorkerContext, WorkerError, WorkerErrorCode, WorkerResult} from './types.js'\nimport {createWriteStream, appendFileSync} from 'fs'\nimport {ensureDir} from './util/fs.js'\nimport {randomUUID} from 'crypto'\nimport {getPackageVersion} from './util/package.js'\n\nexport function startWorkerGuard(\n child: any,\n opts: {\n ttl?: number\n memory?: number\n },\n suspended?: (reason: string) => void,\n) {\n const {ttl = 5000, memory = 128} = opts\n\n let ttlTimer: NodeJS.Timeout | null = null\n let killed = false\n\n const kill = (reason: string) => {\n if (killed) return\n killed = true\n\n if (ttlTimer) clearTimeout(ttlTimer)\n\n child.kill('SIGKILL')\n suspended?.(reason)\n }\n\n // TTL watchdog\n ttlTimer = setTimeout(() => {\n kill(`ttl exceeded limit (limit: ${ttl.toFixed(2)}ms)`)\n }, ttl)\n\n child.on('message', (msg: any) => {\n if (msg.type !== 'ALIVE') {\n return\n }\n\n // (v0.1.0) this isn't set in stone\n // may be worth using RSS\n const heapUsed = msg.memory?.heapUsed ?? 0\n const memMB = heapUsed / 1024 / 1024\n\n if (memMB > memory) {\n kill(`memory limit exceeded: ${memMB.toFixed(2)}MB/${memory.toFixed(2)}MB`)\n }\n })\n\n child.on('exit', () => {\n if (ttlTimer) clearTimeout(ttlTimer)\n })\n}\n\ntype ChildMessage<T> =\n | {type: 'ALIVE'; error: undefined; result: undefined}\n | {type: 'DONE'; result: WorkerResult<T>; error: undefined | null}\n | {type: 'ERROR'; result: undefined | null; error: WorkerError}\n\nexport function normaliseKeys(value: any): any {\n if (Array.isArray(value)) {\n return value.map(normaliseKeys)\n }\n\n if (value && typeof value === 'object' && value.constructor === Object) {\n const sorted: Record<string, any> = {}\n\n for (const key of Object.keys(value).sort()) {\n sorted[key] = normaliseKeys(value[key])\n }\n\n return sorted\n }\n\n return value\n}\n\nexport function formatWorkerResult(opts: {result?: any; error?: any; duration: number}): WorkerResult<any> {\n return {\n version: 'v1',\n ok: !opts.error,\n result: opts.result ?? null,\n error: opts.error ?? null,\n duration: opts.duration,\n }\n}\n\nexport async function runWorker<T>(context: WorkerContext): Promise<WorkerResult<T>> {\n return new Promise(async (resolve, reject) => {\n const id = randomUUID()\n\n const ctx = {...context, id}\n const start = performance.now()\n\n const path = join(ctx.cwd!, ctx.dist ?? '.xgsd')\n await ensureDir(path)\n\n let started = false\n let completed = false\n let res: WorkerResult<unknown>\n\n const contextStr = JSON.stringify(ctx)\n const version = getPackageVersion('@xgsd/workers', ctx.cwd)\n\n // TODO: remove hardcoded worker path\n const child = fork(join(__dirname, 'process', 'workers.process.js'), {\n stdio: ['inherit', 'pipe', 'pipe', 'ipc'],\n //execArgv: [`--max-old-space-size=${ctx.limits?.memory ?? 128}`],\n env: {\n ...ctx.env,\n XGSD_WORKER_VERSION: version,\n XGSD_CTX: contextStr,\n },\n })\n\n const events = createWriteStream(join(path, 'events.jsonl'), {flags: 'a'})\n\n const startGuard = () => {\n startWorkerGuard(child, ctx.limits ?? {}, (reason: string) => {\n if (completed) return\n\n // normalise system/watch dog error\n // then *reject*\n const err: WorkerError = {\n code: WorkerErrorCode.CODE_WORKER_GUARD,\n message: `${reason}`,\n type: 'system',\n }\n\n console.warn(`[guard] worker suspended (reason: ${reason})`)\n\n writeEvent({\n type: 'error',\n message: err.message,\n guard: true,\n timestamp: Date.now(),\n })\n\n cleanup()\n\n resolve(formatWorkerResult({error: err, duration: performance.now() - start}))\n })\n }\n\n const cleanup = () => {\n if (child.connected) {\n child.removeAllListeners('message')\n\n child.disconnect()\n }\n }\n\n const writeEvent = (data: any) => {\n const normalised = normaliseKeys(data)\n events.write(JSON.stringify({...normalised, id}) + '\\n')\n }\n\n child.stdout?.on('data', (chunk) => {\n const lines = chunk.toString().split('\\n').filter(Boolean)\n\n for (const line of lines) {\n // log child process messages (typically from usercode)\n console.log(`${line}`)\n\n try {\n writeEvent(JSON.parse(line))\n } catch {\n // optionally fallback for non-json logs\n writeEvent({\n type: 'log',\n message: line,\n timestamp: Date.now(),\n })\n }\n }\n })\n\n child.stderr?.on('data', (chunk) => {\n const e = {type: 'error', message: chunk.toString(), timestamp: Date.now()}\n console.error(e.message)\n writeEvent(e)\n })\n\n child.on('message', (msg: ChildMessage<unknown>) => {\n if (msg.type !== 'ALIVE' && msg.type !== 'DONE' && msg.type !== 'ERROR') return\n\n if (msg.type === 'ALIVE') {\n if (!started) {\n started = true\n startGuard()\n }\n return\n }\n\n if (msg.type === 'ERROR') {\n const {error} = msg\n\n res = formatWorkerResult({error, duration: performance.now() - start})\n }\n\n if (msg.type === 'DONE') {\n // do something with result\n res = msg.result\n }\n\n res.duration = performance.now() - start\n\n // log activation\n appendFileSync(\n join(path, 'activations.jsonl'),\n JSON.stringify({\n id,\n version,\n ok: res.ok,\n code: res.code,\n duration: res.duration,\n error: res.error?.code ?? res.error?.message,\n result: res.result !== null,\n timestamp: new Date().toISOString(),\n }) + '\\n',\n )\n\n completed = true\n\n resolve(res as any)\n cleanup()\n })\n })\n}\n","import {pathExistsSync, readJsonSync} from './fs'\nimport path from 'path'\n\nexport function getPackageVersion(input: string, root: string = process.cwd()): string {\n try {\n const pkgPath = resolvePackageJson(input, root)\n\n console.log(pkgPath)\n\n const json = readJsonSync(pkgPath)\n\n if (!json?.version || typeof json.version !== 'string') {\n return 'unknown'\n }\n\n return `${json.version}`\n } catch (err: any) {\n return 'unknown'\n }\n}\n\nexport function resolvePackageJson(input: string, root: string): string {\n try {\n return require.resolve(`${input}/package.json`, {\n paths: [root],\n })\n } catch {\n try {\n const entry = require.resolve(input, {\n paths: [root],\n })\n\n let dir = path.dirname(entry)\n\n while (dir !== path.dirname(dir)) {\n const candidate = path.join(dir, 'package.json')\n if (pathExistsSync(candidate)) return candidate\n dir = path.dirname(dir)\n }\n\n throw new Error(`package.json not found for ${input}`)\n } catch (err: any) {\n throw new Error(`Cannot resolve package.json for \"${input}\"`)\n }\n }\n}\n","import {WorkerResult, WorkerConfig} from './types.js'\nimport {runWorker} from './worker.js'\n\nexport {runWorker}\nexport * from './types.js'\n\nexport {resolveDependency} from './bundler.js'\n\nexport type Activation<T = unknown> = {\n id?: string\n data?: T\n env?: Record<string, unknown>\n cwd: string\n}\n\nexport type ActivationHandler = <T = unknown>(activation: Activation<T>) => Promise<WorkerResult<T>>\n\nexport function createHandler(config: WorkerConfig): ActivationHandler {\n return async function handler(activation) {\n return runWorker({\n ...config,\n id: activation.id,\n cwd: activation.cwd,\n data: activation.data,\n env: activation.env,\n })\n }\n}\n"],"mappings":"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","names":["path"]}
|
|
@@ -360,7 +360,8 @@ async function main(ctx2) {
|
|
|
360
360
|
console.log(`[middleware] ${middleware.length} functions registered in ${dt} ms`);
|
|
361
361
|
}
|
|
362
362
|
const runtime = compose([...middleware, wrapper(mod.default)]);
|
|
363
|
-
|
|
363
|
+
const version = process.env.XGSD_WORKER_VERSION ?? "unknown";
|
|
364
|
+
console.log(`[runtime] started running worker (version: ${version})`);
|
|
364
365
|
const start = performance.now();
|
|
365
366
|
const result = await runtime(ctx2);
|
|
366
367
|
const ms = performance.now() - start;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/process/workers.process.ts","../../src/bundler.ts","../../src/util/fs.ts","../../src/compose.ts"],"sourcesContent":["import {join} from 'path'\nimport {createBundle, resolveDependency} from '../bundler'\nimport {compose, Next} from '../compose'\nimport {WorkerContext, WorkerError, WorkerErrorCode} from '../types'\nimport {pathExists} from '../util/fs'\n\nexport type RunFn<T> = (data: T) => Promise<any>\n\nfunction dispatch(event: 'ALIVE' | 'DONE' | 'ERROR', payload: any) {\n process.send?.({\n type: event,\n ...payload,\n })\n}\n\nfunction startHeartbeat(interval = 50) {\n return setInterval(() => {\n const memory = process.memoryUsage()\n\n dispatch('ALIVE', {\n pid: process.pid,\n uptime: process.uptime(),\n memory: {\n rss: memory.rss,\n heapUsed: memory.heapUsed,\n heapTotal: memory.heapTotal,\n external: memory.external,\n },\n })\n }, interval)\n}\n\nexport function wrapper(fn: RunFn<unknown>) {\n const {execute} = resolveDependency('@xgsd/engine', ctx.cwd!)\n\n return async (ctx: WorkerContext, next: Next) => {\n const res = await execute(ctx.data as any, fn)\n\n ctx.result = res.data\n ctx.error = res.error\n\n await next()\n }\n}\n\nasync function main(ctx: WorkerContext) {\n const heartbeat = startHeartbeat()\n\n const {entry, cwd} = ctx\n let entryFile = join(cwd ?? '', entry)\n\n try {\n if (!(await pathExists(entryFile))) {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_ENTRY_FILE,\n message: 'entry file not found',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n // bundler\n if (ctx.bundler?.enabled) {\n entryFile = await createBundle({\n project: cwd!,\n dist: ctx.dist,\n entry,\n cacheStrategy: ctx.bundler?.cache?.strategy ?? 'never',\n })\n }\n\n let mod = undefined\n try {\n mod = await import(entryFile)\n } catch (error) {}\n\n if (!mod || !mod.default || typeof mod.default !== 'function') {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_DEFAULT_FUNCTION,\n message: 'default must be a function',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n // load middleware\n let middleware = []\n if (mod.middleware && typeof mod.middleware === 'function') {\n const start = performance.now()\n middleware = mod.middleware() ?? []\n\n if (!Array.isArray(middleware) || middleware.filter((m) => typeof m !== 'function').length > 0) {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_MIDDLEWARE_FUNCTION,\n message: 'middleware not configured correctly',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n const dt = (performance.now() - start).toFixed(2)\n console.log(`[middleware] ${middleware.length} functions registered in ${dt} ms`)\n }\n\n // runtime\n const runtime = compose([...middleware, wrapper(mod.default)])\n\n console.log(`[runtime] started running worker`)\n\n const start = performance.now()\n\n const result = await runtime(ctx)\n\n const ms = performance.now() - start\n console.log(`[runtime] finished running worker took ${ms.toFixed(2)} ms`)\n\n if (result.error) {\n console.warn(`[runtime] finished with errors (error: ${ctx.error?.message ?? 'unknown'})`)\n }\n\n dispatch('DONE', {result})\n } finally {\n clearInterval(heartbeat)\n }\n}\n\nconst ctx = JSON.parse(process.env.XGSD_CTX!) as WorkerContext\nmain(ctx)\n","import {createHash, timingSafeEqual} from 'crypto'\nimport path, {join, relative, sep} from 'path'\nimport {readdir, readFile, stat} from 'fs/promises'\nimport {pathExistsSync, readJsonSync, writeJsonSync} from './util/fs'\nimport {createRequire} from 'module'\n\nexport async function createBundle({\n project,\n dist,\n cwd,\n entry,\n cacheStrategy,\n log,\n}: {\n project: string\n dist?: string\n cwd?: string\n entry: string\n cacheStrategy: 'always' | 'change' | 'never'\n log?: boolean\n}): Promise<string> {\n const start = performance.now()\n\n const xgsd = join(project, dist ?? '.xgsd')\n const out = join(xgsd, 'bundle.js')\n const entryFile = join(project, entry)\n const packageJsonPath = join(project, 'package.json')\n const outPathRel = join(dist ?? '.xgsd', 'bundle.js')\n\n // v0.7 note\n // dont do this as it adds 20-30MB of memory before anything even runs\n // bundling is fine but current AST parsing/traversal is unneeded\n // instead split into two concerns: dependencies (from package.json) and code changes (from hashes)\n // do this instead:\n const hash = await calculateProjectHash(project)\n const outdir = path.dirname(out)\n\n const packageJson = await readJsonSync(packageJsonPath)\n\n const outPackageJsonPath = join(outdir, 'package.json')\n const cacheFilesExist = pathExistsSync(outPackageJsonPath) && pathExistsSync(out)\n\n if (cacheFilesExist && cacheStrategy === 'always') {\n console.log(`${outPathRel} loaded from cache (set cache.strategy = \"never\" if this is unintentional)`)\n\n return out\n }\n\n if (cacheFilesExist && cacheStrategy === 'change') {\n const outPackageJson = readJsonSync(outPackageJsonPath)\n\n if (outPackageJson.hash && safeHashCompare(outPackageJson.hash, hash)) {\n // cache hit\n\n console.log(`${outPathRel} loaded from cache (set cache.strategy = \"never\" if this is unintentional)`)\n\n return out\n }\n }\n\n const dependencies = Object.entries(readJsonSync(packageJsonPath).dependencies).map((d) => d[0])\n const generated = new Date().toISOString()\n\n // for now let esbuild notify of errors\n await bundle({\n entry: entryFile,\n out,\n banner: {\n generated,\n hash,\n },\n format: 'esm',\n dependencies,\n })\n\n writeJsonSync(path.join(outdir, 'package.json'), {\n ...packageJson,\n hash,\n generated,\n type: 'module',\n })\n\n const ms = performance.now() - start\n\n console.log(`[bundler] copied package.json to ${join(dist ?? '.xgsd', 'package.json')}`)\n console.log(`[bundler] ${entry} bundled to ${outPathRel}`)\n console.log(`[bundler] completed in ${ms.toFixed(2)}ms.`)\n\n if (cacheStrategy === 'never') {\n console.warn(`[bundler] (warn) you can speed this up with bundler.cache.strategy = always|change.`)\n }\n\n return out\n}\n\nfunction bannerLines(object: Record<string, string>) {\n const lines = []\n lines.push(' * xGSD bundle.js')\n for (const key of Object.keys(object)) {\n lines.push(` * ${key}: ${object[key]}`)\n }\n lines.push(' * WARNING: this file is generated. Do not edit manually.')\n return lines.join('\\r\\n')\n}\n\nexport function resolvePath(moduleName: string, root: string): string {\n return require.resolve(moduleName, {\n paths: [root],\n })\n}\n\nexport function resolveDependency(dependency: string, projectRoot: string): any {\n try {\n const require = createRequire(join(projectRoot, 'package.json'))\n return require(dependency)\n } catch {}\n\n throw new Error(\n `Could not resolve ${dependency}.\\nInstall it with \\`yarn add ${dependency}\\`.\\nPath: ${projectRoot}.`,\n )\n}\n\nexport type WalkedFile = {\n path: string\n hash: string\n size: number\n}\n\ntype WalkOptions = {\n ignore?: string[]\n filter?: (path: string) => boolean\n}\n\nexport async function calculateProjectHash(project: string): Promise<string> {\n const hashes = await collectProjectHashes(project, {\n ignore: ['node_modules', '.xgsd', 'dist', '.git'],\n filter: (path) => path.endsWith('.js') || path.endsWith('.ts'),\n })\n\n const normalised = hashes\n .map((h) => h.hash.trim().slice(0, 9))\n .sort()\n .join('|')\n\n return createHash('sha256').update(normalised).digest('hex')\n}\n\nexport async function collectProjectHashes(projectPath: string, options: WalkOptions = {}): Promise<WalkedFile[]> {\n const {ignore = ['node_modules'], filter = () => true} = options\n\n const files: WalkedFile[] = []\n\n const ignored = new Set(ignore)\n\n const shouldIgnore = (target: string) => {\n const parts = relative(projectPath, target).split(sep)\n\n return parts.some((part) => ignored.has(part))\n }\n\n const hashFile = async (filePath: string) => {\n const buffer = await readFile(filePath)\n\n return createHash('sha256').update(buffer).digest('hex')\n }\n\n const visit = async (current: string): Promise<void> => {\n if (shouldIgnore(current)) {\n return\n }\n\n const entries = await readdir(current)\n\n for (const entry of entries) {\n const fullPath = join(current, entry)\n\n if (shouldIgnore(fullPath)) {\n continue\n }\n\n const info = await stat(fullPath)\n\n if (info.isDirectory()) {\n await visit(fullPath)\n continue\n }\n\n if (!info.isFile()) {\n continue\n }\n\n const path = relative(projectPath, fullPath)\n\n if (!filter(path)) {\n continue\n }\n\n files.push({\n path,\n hash: await hashFile(fullPath),\n size: info.size,\n })\n }\n }\n\n await visit(projectPath)\n\n return files.sort((a, b) => a.path.localeCompare(b.path))\n}\n\nexport function safeHashCompare(a: string, b: string): boolean {\n const abuf = Buffer.from(a, 'hex')\n const bbuf = Buffer.from(b, 'hex')\n\n return timingSafeEqual(abuf, bbuf)\n}\n\nexport async function bundle(options: {\n entry: string\n out: string\n format: 'esm' | 'cjs'\n banner: Record<string, string>\n dependencies: string[]\n}) {\n const {dependencies} = options\n\n const esbuild = resolveDependency('esbuild', path.dirname(options.entry))\n\n if (esbuild.version) {\n console.log(`[bundler] building with esbuild@${esbuild.version}`)\n }\n\n return esbuild.build({\n keepNames: true,\n entryPoints: [options.entry],\n bundle: true,\n platform: 'node',\n outfile: options.out,\n format: options.format,\n minify: false,\n sourcemap: false,\n external: dependencies,\n banner: {\n js: `\n/**\n${bannerLines(options.banner)}\n */\n`.trim(),\n },\n })\n}\n","import {readFileSync, writeFileSync, mkdirSync} from 'fs'\nimport {writeFile, readFile, constants, access, mkdir} from 'fs/promises'\nimport {dirname} from 'path'\n\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\n// ensureDir\nexport async function ensureDir(path: string): Promise<void> {\n await mkdir(path, {recursive: true})\n}\n\n// ensureDirSync\nexport function ensureDirSync(path: string): void {\n mkdirSync(path, {recursive: true})\n}\n\n// ensurePath\n// Ensures parent directory exists for a file path\nexport async function ensurePath(path: string): Promise<void> {\n await mkdir(dirname(path), {recursive: true})\n}\n\n// ensurePathSync\nexport function ensurePathSync(path: string): void {\n mkdirSync(dirname(path), {recursive: true})\n}\n\nexport async function readJson<T = any>(path: string): Promise<T> {\n const content = await readFile(path, 'utf8')\n return JSON.parse(content)\n}\n\nexport function readJsonSync<T = any>(path: string): T {\n return JSON.parse(readFileSync(path, 'utf8'))\n}\n\nexport async function writeJson(path: string, data: unknown, pretty = true): Promise<void> {\n const json = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)\n\n await writeFile(path, json, 'utf8')\n}\n\nexport function writeJsonSync(path: string, data: unknown, pretty = true): void {\n const json = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)\n\n writeFileSync(path, json, 'utf8')\n}\n\nexport function pathExistsSync(path: string): boolean {\n try {\n require('fs').accessSync(path, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n","import {resolveDependency} from './bundler'\nimport {WorkerContext, WorkerResult} from './types'\n\nexport type Next = () => Promise<void>\n\nexport type Middleware = (ctx: WorkerContext, next: Next) => Promise<void>\n\nexport type ComposedMiddleware = (ctx: WorkerContext) => Promise<WorkerResult<unknown>>\n\nexport function compose(middleware: Middleware[]): ComposedMiddleware {\n return async function run(ctx: WorkerContext): Promise<WorkerResult<unknown>> {\n let index = -1\n\n let res: any = {\n version: 'v1',\n ok: undefined,\n result: undefined,\n error: undefined,\n duration: undefined,\n }\n\n const {execute} = resolveDependency('@xgsd/engine', ctx.cwd!)\n\n async function dispatch(i: number): Promise<void> {\n if (i <= index) {\n throw new Error('next() called multiple times')\n }\n\n index = i\n\n const fn = middleware[i]\n\n if (!fn) {\n return\n }\n\n const executeWrapper = async (ctx: WorkerContext) => {\n await fn(ctx, async () => {\n await dispatch(i + 1)\n })\n\n return ctx\n }\n\n const result = await execute(ctx, executeWrapper)\n\n if (result.error || result.data?.error) {\n res.ok = false\n res.code = ctx.code\n res.result = null\n res.error = result.error ?? result.data?.error\n }\n\n if (result.data && result.data.result) {\n res.ok = true\n res.code = ctx.code\n res.result = result.data.result\n res.error = result.error ?? result.data?.error\n }\n }\n\n await dispatch(0)\n\n return res\n }\n}\n"],"mappings":"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","names":["import_path","import_promises","path","path","path","path","require","path","ctx","dispatch","ctx","start"]}
|
|
1
|
+
{"version":3,"sources":["../../src/process/workers.process.ts","../../src/bundler.ts","../../src/util/fs.ts","../../src/compose.ts"],"sourcesContent":["import {join} from 'path'\nimport {createBundle, resolveDependency} from '../bundler'\nimport {compose, Next} from '../compose'\nimport {WorkerContext, WorkerError, WorkerErrorCode} from '../types'\nimport {pathExists} from '../util/fs'\nimport {getPackageVersion} from '../util/package'\n\nexport type RunFn<T> = (data: T) => Promise<any>\n\nfunction dispatch(event: 'ALIVE' | 'DONE' | 'ERROR', payload: any) {\n process.send?.({\n type: event,\n ...payload,\n })\n}\n\nfunction startHeartbeat(interval = 50) {\n return setInterval(() => {\n const memory = process.memoryUsage()\n\n dispatch('ALIVE', {\n pid: process.pid,\n uptime: process.uptime(),\n memory: {\n rss: memory.rss,\n heapUsed: memory.heapUsed,\n heapTotal: memory.heapTotal,\n external: memory.external,\n },\n })\n }, interval)\n}\n\nexport function wrapper(fn: RunFn<unknown>) {\n const {execute} = resolveDependency('@xgsd/engine', ctx.cwd!)\n\n return async (ctx: WorkerContext, next: Next) => {\n const res = await execute(ctx.data as any, fn)\n\n ctx.result = res.data\n ctx.error = res.error\n\n await next()\n }\n}\n\nasync function main(ctx: WorkerContext) {\n const heartbeat = startHeartbeat()\n\n const {entry, cwd} = ctx\n let entryFile = join(cwd ?? '', entry)\n\n try {\n if (!(await pathExists(entryFile))) {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_ENTRY_FILE,\n message: 'entry file not found',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n // bundler\n if (ctx.bundler?.enabled) {\n entryFile = await createBundle({\n project: cwd!,\n dist: ctx.dist,\n entry,\n cacheStrategy: ctx.bundler?.cache?.strategy ?? 'never',\n })\n }\n\n let mod = undefined\n try {\n mod = await import(entryFile)\n } catch (error) {}\n\n if (!mod || !mod.default || typeof mod.default !== 'function') {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_DEFAULT_FUNCTION,\n message: 'default must be a function',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n // load middleware\n let middleware = []\n if (mod.middleware && typeof mod.middleware === 'function') {\n const start = performance.now()\n middleware = mod.middleware() ?? []\n\n if (!Array.isArray(middleware) || middleware.filter((m) => typeof m !== 'function').length > 0) {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_MIDDLEWARE_FUNCTION,\n message: 'middleware not configured correctly',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n const dt = (performance.now() - start).toFixed(2)\n console.log(`[middleware] ${middleware.length} functions registered in ${dt} ms`)\n }\n\n // runtime\n const runtime = compose([...middleware, wrapper(mod.default)])\n const version = process.env.XGSD_WORKER_VERSION ?? 'unknown'\n\n console.log(`[runtime] started running worker (version: ${version})`)\n\n const start = performance.now()\n\n const result = await runtime(ctx)\n\n const ms = performance.now() - start\n console.log(`[runtime] finished running worker took ${ms.toFixed(2)} ms`)\n\n if (result.error) {\n console.warn(`[runtime] finished with errors (error: ${ctx.error?.message ?? 'unknown'})`)\n }\n\n dispatch('DONE', {result})\n } finally {\n clearInterval(heartbeat)\n }\n}\n\nconst ctx = JSON.parse(process.env.XGSD_CTX!) as WorkerContext\nmain(ctx)\n","import {createHash, timingSafeEqual} from 'crypto'\nimport path, {join, relative, sep} from 'path'\nimport {readdir, readFile, stat} from 'fs/promises'\nimport {pathExistsSync, readJsonSync, writeJsonSync} from './util/fs'\nimport {createRequire} from 'module'\n\nexport async function createBundle({\n project,\n dist,\n cwd,\n entry,\n cacheStrategy,\n log,\n}: {\n project: string\n dist?: string\n cwd?: string\n entry: string\n cacheStrategy: 'always' | 'change' | 'never'\n log?: boolean\n}): Promise<string> {\n const start = performance.now()\n\n const xgsd = join(project, dist ?? '.xgsd')\n const out = join(xgsd, 'bundle.js')\n const entryFile = join(project, entry)\n const packageJsonPath = join(project, 'package.json')\n const outPathRel = join(dist ?? '.xgsd', 'bundle.js')\n\n // v0.7 note\n // dont do this as it adds 20-30MB of memory before anything even runs\n // bundling is fine but current AST parsing/traversal is unneeded\n // instead split into two concerns: dependencies (from package.json) and code changes (from hashes)\n // do this instead:\n const hash = await calculateProjectHash(project)\n const outdir = path.dirname(out)\n\n const packageJson = await readJsonSync(packageJsonPath)\n\n const outPackageJsonPath = join(outdir, 'package.json')\n const cacheFilesExist = pathExistsSync(outPackageJsonPath) && pathExistsSync(out)\n\n if (cacheFilesExist && cacheStrategy === 'always') {\n console.log(`${outPathRel} loaded from cache (set cache.strategy = \"never\" if this is unintentional)`)\n\n return out\n }\n\n if (cacheFilesExist && cacheStrategy === 'change') {\n const outPackageJson = readJsonSync(outPackageJsonPath)\n\n if (outPackageJson.hash && safeHashCompare(outPackageJson.hash, hash)) {\n // cache hit\n\n console.log(`${outPathRel} loaded from cache (set cache.strategy = \"never\" if this is unintentional)`)\n\n return out\n }\n }\n\n const dependencies = Object.entries(readJsonSync(packageJsonPath).dependencies).map((d) => d[0])\n const generated = new Date().toISOString()\n\n // for now let esbuild notify of errors\n await bundle({\n entry: entryFile,\n out,\n banner: {\n generated,\n hash,\n },\n format: 'esm',\n dependencies,\n })\n\n writeJsonSync(path.join(outdir, 'package.json'), {\n ...packageJson,\n hash,\n generated,\n type: 'module',\n })\n\n const ms = performance.now() - start\n\n console.log(`[bundler] copied package.json to ${join(dist ?? '.xgsd', 'package.json')}`)\n console.log(`[bundler] ${entry} bundled to ${outPathRel}`)\n console.log(`[bundler] completed in ${ms.toFixed(2)}ms.`)\n\n if (cacheStrategy === 'never') {\n console.warn(`[bundler] (warn) you can speed this up with bundler.cache.strategy = always|change.`)\n }\n\n return out\n}\n\nfunction bannerLines(object: Record<string, string>) {\n const lines = []\n lines.push(' * xGSD bundle.js')\n for (const key of Object.keys(object)) {\n lines.push(` * ${key}: ${object[key]}`)\n }\n lines.push(' * WARNING: this file is generated. Do not edit manually.')\n return lines.join('\\r\\n')\n}\n\nexport function resolvePath(moduleName: string, root: string): string {\n return require.resolve(moduleName, {\n paths: [root],\n })\n}\n\nexport function resolveDependency(dependency: string, projectRoot: string): any {\n try {\n const require = createRequire(join(projectRoot, 'package.json'))\n return require(dependency)\n } catch {}\n\n throw new Error(\n `Could not resolve ${dependency}.\\nInstall it with \\`yarn add ${dependency}\\`.\\nPath: ${projectRoot}.`,\n )\n}\n\nexport type WalkedFile = {\n path: string\n hash: string\n size: number\n}\n\ntype WalkOptions = {\n ignore?: string[]\n filter?: (path: string) => boolean\n}\n\nexport async function calculateProjectHash(project: string): Promise<string> {\n const hashes = await collectProjectHashes(project, {\n ignore: ['node_modules', '.xgsd', 'dist', '.git'],\n filter: (path) => path.endsWith('.js') || path.endsWith('.ts'),\n })\n\n const normalised = hashes\n .map((h) => h.hash.trim().slice(0, 9))\n .sort()\n .join('|')\n\n return createHash('sha256').update(normalised).digest('hex')\n}\n\nexport async function collectProjectHashes(projectPath: string, options: WalkOptions = {}): Promise<WalkedFile[]> {\n const {ignore = ['node_modules'], filter = () => true} = options\n\n const files: WalkedFile[] = []\n\n const ignored = new Set(ignore)\n\n const shouldIgnore = (target: string) => {\n const parts = relative(projectPath, target).split(sep)\n\n return parts.some((part) => ignored.has(part))\n }\n\n const hashFile = async (filePath: string) => {\n const buffer = await readFile(filePath)\n\n return createHash('sha256').update(buffer).digest('hex')\n }\n\n const visit = async (current: string): Promise<void> => {\n if (shouldIgnore(current)) {\n return\n }\n\n const entries = await readdir(current)\n\n for (const entry of entries) {\n const fullPath = join(current, entry)\n\n if (shouldIgnore(fullPath)) {\n continue\n }\n\n const info = await stat(fullPath)\n\n if (info.isDirectory()) {\n await visit(fullPath)\n continue\n }\n\n if (!info.isFile()) {\n continue\n }\n\n const path = relative(projectPath, fullPath)\n\n if (!filter(path)) {\n continue\n }\n\n files.push({\n path,\n hash: await hashFile(fullPath),\n size: info.size,\n })\n }\n }\n\n await visit(projectPath)\n\n return files.sort((a, b) => a.path.localeCompare(b.path))\n}\n\nexport function safeHashCompare(a: string, b: string): boolean {\n const abuf = Buffer.from(a, 'hex')\n const bbuf = Buffer.from(b, 'hex')\n\n return timingSafeEqual(abuf, bbuf)\n}\n\nexport async function bundle(options: {\n entry: string\n out: string\n format: 'esm' | 'cjs'\n banner: Record<string, string>\n dependencies: string[]\n}) {\n const {dependencies} = options\n\n const esbuild = resolveDependency('esbuild', path.dirname(options.entry))\n\n if (esbuild.version) {\n console.log(`[bundler] building with esbuild@${esbuild.version}`)\n }\n\n return esbuild.build({\n keepNames: true,\n entryPoints: [options.entry],\n bundle: true,\n platform: 'node',\n outfile: options.out,\n format: options.format,\n minify: false,\n sourcemap: false,\n external: dependencies,\n banner: {\n js: `\n/**\n${bannerLines(options.banner)}\n */\n`.trim(),\n },\n })\n}\n","import {readFileSync, writeFileSync, mkdirSync} from 'fs'\nimport {writeFile, readFile, constants, access, mkdir} from 'fs/promises'\nimport {dirname} from 'path'\n\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\n// ensureDir\nexport async function ensureDir(path: string): Promise<void> {\n await mkdir(path, {recursive: true})\n}\n\n// ensureDirSync\nexport function ensureDirSync(path: string): void {\n mkdirSync(path, {recursive: true})\n}\n\n// ensurePath\n// Ensures parent directory exists for a file path\nexport async function ensurePath(path: string): Promise<void> {\n await mkdir(dirname(path), {recursive: true})\n}\n\n// ensurePathSync\nexport function ensurePathSync(path: string): void {\n mkdirSync(dirname(path), {recursive: true})\n}\n\nexport async function readJson<T = any>(path: string): Promise<T> {\n const content = await readFile(path, 'utf8')\n return JSON.parse(content)\n}\n\nexport function readJsonSync<T = any>(path: string): T {\n return JSON.parse(readFileSync(path, 'utf8'))\n}\n\nexport async function writeJson(path: string, data: unknown, pretty = true): Promise<void> {\n const json = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)\n\n await writeFile(path, json, 'utf8')\n}\n\nexport function writeJsonSync(path: string, data: unknown, pretty = true): void {\n const json = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)\n\n writeFileSync(path, json, 'utf8')\n}\n\nexport function pathExistsSync(path: string): boolean {\n try {\n require('fs').accessSync(path, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n","import {resolveDependency} from './bundler'\nimport {WorkerContext, WorkerResult} from './types'\n\nexport type Next = () => Promise<void>\n\nexport type Middleware = (ctx: WorkerContext, next: Next) => Promise<void>\n\nexport type ComposedMiddleware = (ctx: WorkerContext) => Promise<WorkerResult<unknown>>\n\nexport function compose(middleware: Middleware[]): ComposedMiddleware {\n return async function run(ctx: WorkerContext): Promise<WorkerResult<unknown>> {\n let index = -1\n\n let res: any = {\n version: 'v1',\n ok: undefined,\n result: undefined,\n error: undefined,\n duration: undefined,\n }\n\n const {execute} = resolveDependency('@xgsd/engine', ctx.cwd!)\n\n async function dispatch(i: number): Promise<void> {\n if (i <= index) {\n throw new Error('next() called multiple times')\n }\n\n index = i\n\n const fn = middleware[i]\n\n if (!fn) {\n return\n }\n\n const executeWrapper = async (ctx: WorkerContext) => {\n await fn(ctx, async () => {\n await dispatch(i + 1)\n })\n\n return ctx\n }\n\n const result = await execute(ctx, executeWrapper)\n\n if (result.error || result.data?.error) {\n res.ok = false\n res.code = ctx.code\n res.result = null\n res.error = result.error ?? result.data?.error\n }\n\n if (result.data && result.data.result) {\n res.ok = true\n res.code = ctx.code\n res.result = result.data.result\n res.error = result.error ?? result.data?.error\n }\n }\n\n await dispatch(0)\n\n return res\n }\n}\n"],"mappings":"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","names":["import_path","import_promises","path","path","path","path","require","path","ctx","dispatch","ctx","start"]}
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
createBundle,
|
|
3
3
|
pathExists,
|
|
4
4
|
resolveDependency
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-FPVPJQS7.js";
|
|
6
6
|
|
|
7
7
|
// src/process/workers.process.ts
|
|
8
8
|
import { join } from "path";
|
|
@@ -137,7 +137,8 @@ async function main(ctx2) {
|
|
|
137
137
|
console.log(`[middleware] ${middleware.length} functions registered in ${dt} ms`);
|
|
138
138
|
}
|
|
139
139
|
const runtime = compose([...middleware, wrapper(mod.default)]);
|
|
140
|
-
|
|
140
|
+
const version = process.env.XGSD_WORKER_VERSION ?? "unknown";
|
|
141
|
+
console.log(`[runtime] started running worker (version: ${version})`);
|
|
141
142
|
const start = performance.now();
|
|
142
143
|
const result = await runtime(ctx2);
|
|
143
144
|
const ms = performance.now() - start;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/process/workers.process.ts","../../src/compose.ts"],"sourcesContent":["import {join} from 'path'\nimport {createBundle, resolveDependency} from '../bundler'\nimport {compose, Next} from '../compose'\nimport {WorkerContext, WorkerError, WorkerErrorCode} from '../types'\nimport {pathExists} from '../util/fs'\n\nexport type RunFn<T> = (data: T) => Promise<any>\n\nfunction dispatch(event: 'ALIVE' | 'DONE' | 'ERROR', payload: any) {\n process.send?.({\n type: event,\n ...payload,\n })\n}\n\nfunction startHeartbeat(interval = 50) {\n return setInterval(() => {\n const memory = process.memoryUsage()\n\n dispatch('ALIVE', {\n pid: process.pid,\n uptime: process.uptime(),\n memory: {\n rss: memory.rss,\n heapUsed: memory.heapUsed,\n heapTotal: memory.heapTotal,\n external: memory.external,\n },\n })\n }, interval)\n}\n\nexport function wrapper(fn: RunFn<unknown>) {\n const {execute} = resolveDependency('@xgsd/engine', ctx.cwd!)\n\n return async (ctx: WorkerContext, next: Next) => {\n const res = await execute(ctx.data as any, fn)\n\n ctx.result = res.data\n ctx.error = res.error\n\n await next()\n }\n}\n\nasync function main(ctx: WorkerContext) {\n const heartbeat = startHeartbeat()\n\n const {entry, cwd} = ctx\n let entryFile = join(cwd ?? '', entry)\n\n try {\n if (!(await pathExists(entryFile))) {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_ENTRY_FILE,\n message: 'entry file not found',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n // bundler\n if (ctx.bundler?.enabled) {\n entryFile = await createBundle({\n project: cwd!,\n dist: ctx.dist,\n entry,\n cacheStrategy: ctx.bundler?.cache?.strategy ?? 'never',\n })\n }\n\n let mod = undefined\n try {\n mod = await import(entryFile)\n } catch (error) {}\n\n if (!mod || !mod.default || typeof mod.default !== 'function') {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_DEFAULT_FUNCTION,\n message: 'default must be a function',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n // load middleware\n let middleware = []\n if (mod.middleware && typeof mod.middleware === 'function') {\n const start = performance.now()\n middleware = mod.middleware() ?? []\n\n if (!Array.isArray(middleware) || middleware.filter((m) => typeof m !== 'function').length > 0) {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_MIDDLEWARE_FUNCTION,\n message: 'middleware not configured correctly',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n const dt = (performance.now() - start).toFixed(2)\n console.log(`[middleware] ${middleware.length} functions registered in ${dt} ms`)\n }\n\n // runtime\n const runtime = compose([...middleware, wrapper(mod.default)])\n\n console.log(`[runtime] started running worker`)\n\n const start = performance.now()\n\n const result = await runtime(ctx)\n\n const ms = performance.now() - start\n console.log(`[runtime] finished running worker took ${ms.toFixed(2)} ms`)\n\n if (result.error) {\n console.warn(`[runtime] finished with errors (error: ${ctx.error?.message ?? 'unknown'})`)\n }\n\n dispatch('DONE', {result})\n } finally {\n clearInterval(heartbeat)\n }\n}\n\nconst ctx = JSON.parse(process.env.XGSD_CTX!) as WorkerContext\nmain(ctx)\n","import {resolveDependency} from './bundler'\nimport {WorkerContext, WorkerResult} from './types'\n\nexport type Next = () => Promise<void>\n\nexport type Middleware = (ctx: WorkerContext, next: Next) => Promise<void>\n\nexport type ComposedMiddleware = (ctx: WorkerContext) => Promise<WorkerResult<unknown>>\n\nexport function compose(middleware: Middleware[]): ComposedMiddleware {\n return async function run(ctx: WorkerContext): Promise<WorkerResult<unknown>> {\n let index = -1\n\n let res: any = {\n version: 'v1',\n ok: undefined,\n result: undefined,\n error: undefined,\n duration: undefined,\n }\n\n const {execute} = resolveDependency('@xgsd/engine', ctx.cwd!)\n\n async function dispatch(i: number): Promise<void> {\n if (i <= index) {\n throw new Error('next() called multiple times')\n }\n\n index = i\n\n const fn = middleware[i]\n\n if (!fn) {\n return\n }\n\n const executeWrapper = async (ctx: WorkerContext) => {\n await fn(ctx, async () => {\n await dispatch(i + 1)\n })\n\n return ctx\n }\n\n const result = await execute(ctx, executeWrapper)\n\n if (result.error || result.data?.error) {\n res.ok = false\n res.code = ctx.code\n res.result = null\n res.error = result.error ?? result.data?.error\n }\n\n if (result.data && result.data.result) {\n res.ok = true\n res.code = ctx.code\n res.result = result.data.result\n res.error = result.error ?? result.data?.error\n }\n }\n\n await dispatch(0)\n\n return res\n }\n}\n"],"mappings":";;;;;;;AAAA,SAAQ,YAAW;;;ACSZ,SAAS,QAAQ,YAA8C;AACpE,SAAO,eAAe,IAAIA,MAAoD;AAC5E,QAAI,QAAQ;AAEZ,QAAI,MAAW;AAAA,MACb,SAAS;AAAA,MACT,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,UAAU;AAAA,IACZ;AAEA,UAAM,EAAC,QAAO,IAAI,kBAAkB,gBAAgBA,KAAI,GAAI;AAE5D,mBAAeC,UAAS,GAA0B;AAChD,UAAI,KAAK,OAAO;AACd,cAAM,IAAI,MAAM,8BAA8B;AAAA,MAChD;AAEA,cAAQ;AAER,YAAM,KAAK,WAAW,CAAC;AAEvB,UAAI,CAAC,IAAI;AACP;AAAA,MACF;AAEA,YAAM,iBAAiB,OAAOD,SAAuB;AACnD,cAAM,GAAGA,MAAK,YAAY;AACxB,gBAAMC,UAAS,IAAI,CAAC;AAAA,QACtB,CAAC;AAED,eAAOD;AAAA,MACT;AAEA,YAAM,SAAS,MAAM,QAAQA,MAAK,cAAc;AAEhD,UAAI,OAAO,SAAS,OAAO,MAAM,OAAO;AACtC,YAAI,KAAK;AACT,YAAI,OAAOA,KAAI;AACf,YAAI,SAAS;AACb,YAAI,QAAQ,OAAO,SAAS,OAAO,MAAM;AAAA,MAC3C;AAEA,UAAI,OAAO,QAAQ,OAAO,KAAK,QAAQ;AACrC,YAAI,KAAK;AACT,YAAI,OAAOA,KAAI;AACf,YAAI,SAAS,OAAO,KAAK;AACzB,YAAI,QAAQ,OAAO,SAAS,OAAO,MAAM;AAAA,MAC3C;AAAA,IACF;AAEA,UAAMC,UAAS,CAAC;AAEhB,WAAO;AAAA,EACT;AACF;;;
|
|
1
|
+
{"version":3,"sources":["../../src/process/workers.process.ts","../../src/compose.ts"],"sourcesContent":["import {join} from 'path'\nimport {createBundle, resolveDependency} from '../bundler'\nimport {compose, Next} from '../compose'\nimport {WorkerContext, WorkerError, WorkerErrorCode} from '../types'\nimport {pathExists} from '../util/fs'\nimport {getPackageVersion} from '../util/package'\n\nexport type RunFn<T> = (data: T) => Promise<any>\n\nfunction dispatch(event: 'ALIVE' | 'DONE' | 'ERROR', payload: any) {\n process.send?.({\n type: event,\n ...payload,\n })\n}\n\nfunction startHeartbeat(interval = 50) {\n return setInterval(() => {\n const memory = process.memoryUsage()\n\n dispatch('ALIVE', {\n pid: process.pid,\n uptime: process.uptime(),\n memory: {\n rss: memory.rss,\n heapUsed: memory.heapUsed,\n heapTotal: memory.heapTotal,\n external: memory.external,\n },\n })\n }, interval)\n}\n\nexport function wrapper(fn: RunFn<unknown>) {\n const {execute} = resolveDependency('@xgsd/engine', ctx.cwd!)\n\n return async (ctx: WorkerContext, next: Next) => {\n const res = await execute(ctx.data as any, fn)\n\n ctx.result = res.data\n ctx.error = res.error\n\n await next()\n }\n}\n\nasync function main(ctx: WorkerContext) {\n const heartbeat = startHeartbeat()\n\n const {entry, cwd} = ctx\n let entryFile = join(cwd ?? '', entry)\n\n try {\n if (!(await pathExists(entryFile))) {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_ENTRY_FILE,\n message: 'entry file not found',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n // bundler\n if (ctx.bundler?.enabled) {\n entryFile = await createBundle({\n project: cwd!,\n dist: ctx.dist,\n entry,\n cacheStrategy: ctx.bundler?.cache?.strategy ?? 'never',\n })\n }\n\n let mod = undefined\n try {\n mod = await import(entryFile)\n } catch (error) {}\n\n if (!mod || !mod.default || typeof mod.default !== 'function') {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_DEFAULT_FUNCTION,\n message: 'default must be a function',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n // load middleware\n let middleware = []\n if (mod.middleware && typeof mod.middleware === 'function') {\n const start = performance.now()\n middleware = mod.middleware() ?? []\n\n if (!Array.isArray(middleware) || middleware.filter((m) => typeof m !== 'function').length > 0) {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_MIDDLEWARE_FUNCTION,\n message: 'middleware not configured correctly',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n const dt = (performance.now() - start).toFixed(2)\n console.log(`[middleware] ${middleware.length} functions registered in ${dt} ms`)\n }\n\n // runtime\n const runtime = compose([...middleware, wrapper(mod.default)])\n const version = process.env.XGSD_WORKER_VERSION ?? 'unknown'\n\n console.log(`[runtime] started running worker (version: ${version})`)\n\n const start = performance.now()\n\n const result = await runtime(ctx)\n\n const ms = performance.now() - start\n console.log(`[runtime] finished running worker took ${ms.toFixed(2)} ms`)\n\n if (result.error) {\n console.warn(`[runtime] finished with errors (error: ${ctx.error?.message ?? 'unknown'})`)\n }\n\n dispatch('DONE', {result})\n } finally {\n clearInterval(heartbeat)\n }\n}\n\nconst ctx = JSON.parse(process.env.XGSD_CTX!) as WorkerContext\nmain(ctx)\n","import {resolveDependency} from './bundler'\nimport {WorkerContext, WorkerResult} from './types'\n\nexport type Next = () => Promise<void>\n\nexport type Middleware = (ctx: WorkerContext, next: Next) => Promise<void>\n\nexport type ComposedMiddleware = (ctx: WorkerContext) => Promise<WorkerResult<unknown>>\n\nexport function compose(middleware: Middleware[]): ComposedMiddleware {\n return async function run(ctx: WorkerContext): Promise<WorkerResult<unknown>> {\n let index = -1\n\n let res: any = {\n version: 'v1',\n ok: undefined,\n result: undefined,\n error: undefined,\n duration: undefined,\n }\n\n const {execute} = resolveDependency('@xgsd/engine', ctx.cwd!)\n\n async function dispatch(i: number): Promise<void> {\n if (i <= index) {\n throw new Error('next() called multiple times')\n }\n\n index = i\n\n const fn = middleware[i]\n\n if (!fn) {\n return\n }\n\n const executeWrapper = async (ctx: WorkerContext) => {\n await fn(ctx, async () => {\n await dispatch(i + 1)\n })\n\n return ctx\n }\n\n const result = await execute(ctx, executeWrapper)\n\n if (result.error || result.data?.error) {\n res.ok = false\n res.code = ctx.code\n res.result = null\n res.error = result.error ?? result.data?.error\n }\n\n if (result.data && result.data.result) {\n res.ok = true\n res.code = ctx.code\n res.result = result.data.result\n res.error = result.error ?? result.data?.error\n }\n }\n\n await dispatch(0)\n\n return res\n }\n}\n"],"mappings":"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","names":["ctx","dispatch","ctx","start"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xgsd/workers",
|
|
3
3
|
"description": "Super lightweight alternative to standard xGSD runtime. Optimised for queues/serverless vs project orchestration.",
|
|
4
|
-
"version": "0.1.0-beta.
|
|
4
|
+
"version": "0.1.0-beta.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
File without changes
|