@xgsd/workers 0.1.0-beta.2 → 0.1.0-beta.4
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-CI26Z23S.js} +5 -2
- package/dist/chunk-CI26Z23S.js.map +1 -0
- package/dist/index.cjs +67 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +51 -7
- package/dist/index.js.map +1 -1
- package/dist/process/workers.process.cjs +3 -2
- 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 +0 -1
|
@@ -103,7 +103,7 @@ async function createBundle({
|
|
|
103
103
|
console.log(`[bundler] ${entry} bundled to ${outPathRel}`);
|
|
104
104
|
console.log(`[bundler] completed in ${ms.toFixed(2)}ms.`);
|
|
105
105
|
if (cacheStrategy === "never") {
|
|
106
|
-
console.
|
|
106
|
+
console.log(`[bundler] you can speed this up with bundler.cache.strategy = always|change.`);
|
|
107
107
|
}
|
|
108
108
|
return out;
|
|
109
109
|
}
|
|
@@ -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-CI26Z23S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/util/fs.ts","../src/bundler.ts"],"sourcesContent":["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.log(`[bundler] 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","path","readFile","require","path","readFile"]}
|
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,67 @@ 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
|
+
const json = readJsonSync(pkgPath);
|
|
84
|
+
if (!json?.version || typeof json.version !== "string") {
|
|
85
|
+
return "unknown";
|
|
86
|
+
}
|
|
87
|
+
return `${json.version}`;
|
|
88
|
+
} catch (err) {
|
|
89
|
+
return "unknown";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function resolvePackageJson(input, root) {
|
|
93
|
+
try {
|
|
94
|
+
return require.resolve(`${input}/package.json`, {
|
|
95
|
+
paths: [root]
|
|
96
|
+
});
|
|
97
|
+
} catch {
|
|
98
|
+
try {
|
|
99
|
+
const entry = require.resolve(input, {
|
|
100
|
+
paths: [root]
|
|
101
|
+
});
|
|
102
|
+
let dir = import_path.default.dirname(entry);
|
|
103
|
+
while (dir !== import_path.default.dirname(dir)) {
|
|
104
|
+
const candidate = import_path.default.join(dir, "package.json");
|
|
105
|
+
if (pathExistsSync(candidate)) return candidate;
|
|
106
|
+
dir = import_path.default.dirname(dir);
|
|
107
|
+
}
|
|
108
|
+
throw new Error(`package.json not found for ${input}`);
|
|
109
|
+
} catch (err) {
|
|
110
|
+
throw new Error(`Cannot resolve package.json for "${input}"`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/worker.ts
|
|
65
116
|
function startWorkerGuard(child, opts, suspended) {
|
|
66
117
|
const { ttl = 5e3, memory = 128 } = opts;
|
|
67
118
|
let ttlTimer = null;
|
|
@@ -117,21 +168,23 @@ async function runWorker(context) {
|
|
|
117
168
|
const id = (0, import_crypto.randomUUID)();
|
|
118
169
|
const ctx = { ...context, id };
|
|
119
170
|
const start = performance.now();
|
|
120
|
-
const
|
|
121
|
-
await ensureDir(
|
|
171
|
+
const path3 = (0, import_path2.join)(ctx.cwd, ctx.dist ?? ".xgsd");
|
|
172
|
+
await ensureDir(path3);
|
|
122
173
|
let started = false;
|
|
123
174
|
let completed = false;
|
|
124
175
|
let res;
|
|
125
176
|
const contextStr = JSON.stringify(ctx);
|
|
126
|
-
const
|
|
177
|
+
const version = getPackageVersion("@xgsd/workers", ctx.cwd);
|
|
178
|
+
const child = (0, import_child_process.fork)((0, import_path2.join)(__dirname, "process", "workers.process.js"), {
|
|
127
179
|
stdio: ["inherit", "pipe", "pipe", "ipc"],
|
|
128
180
|
//execArgv: [`--max-old-space-size=${ctx.limits?.memory ?? 128}`],
|
|
129
181
|
env: {
|
|
130
182
|
...ctx.env,
|
|
183
|
+
XGSD_WORKER_VERSION: version,
|
|
131
184
|
XGSD_CTX: contextStr
|
|
132
185
|
}
|
|
133
186
|
});
|
|
134
|
-
const events = (0,
|
|
187
|
+
const events = (0, import_fs3.createWriteStream)((0, import_path2.join)(path3, "events.jsonl"), { flags: "a" });
|
|
135
188
|
const startGuard = () => {
|
|
136
189
|
startWorkerGuard(child, ctx.limits ?? {}, (reason) => {
|
|
137
190
|
if (completed) return;
|
|
@@ -164,7 +217,7 @@ async function runWorker(context) {
|
|
|
164
217
|
child.stdout?.on("data", (chunk) => {
|
|
165
218
|
const lines = chunk.toString().split("\n").filter(Boolean);
|
|
166
219
|
for (const line of lines) {
|
|
167
|
-
console.log(
|
|
220
|
+
console.log(line.trim());
|
|
168
221
|
try {
|
|
169
222
|
writeEvent(JSON.parse(line));
|
|
170
223
|
} catch {
|
|
@@ -178,7 +231,6 @@ async function runWorker(context) {
|
|
|
178
231
|
});
|
|
179
232
|
child.stderr?.on("data", (chunk) => {
|
|
180
233
|
const e = { type: "error", message: chunk.toString(), timestamp: Date.now() };
|
|
181
|
-
console.error(e.message);
|
|
182
234
|
writeEvent(e);
|
|
183
235
|
});
|
|
184
236
|
child.on("message", (msg) => {
|
|
@@ -198,10 +250,11 @@ async function runWorker(context) {
|
|
|
198
250
|
res = msg.result;
|
|
199
251
|
}
|
|
200
252
|
res.duration = performance.now() - start;
|
|
201
|
-
(0,
|
|
202
|
-
(0,
|
|
253
|
+
(0, import_fs3.appendFileSync)(
|
|
254
|
+
(0, import_path2.join)(path3, "activations.jsonl"),
|
|
203
255
|
JSON.stringify({
|
|
204
256
|
id,
|
|
257
|
+
version,
|
|
205
258
|
ok: res.ok,
|
|
206
259
|
code: res.code,
|
|
207
260
|
duration: res.duration,
|
|
@@ -218,11 +271,11 @@ async function runWorker(context) {
|
|
|
218
271
|
}
|
|
219
272
|
|
|
220
273
|
// src/bundler.ts
|
|
221
|
-
var
|
|
274
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
222
275
|
var import_module = require("module");
|
|
223
276
|
function resolveDependency(dependency, projectRoot) {
|
|
224
277
|
try {
|
|
225
|
-
const require2 = (0, import_module.createRequire)((0,
|
|
278
|
+
const require2 = (0, import_module.createRequire)((0, import_path3.join)(projectRoot, "package.json"));
|
|
226
279
|
return require2(dependency);
|
|
227
280
|
} catch {
|
|
228
281
|
}
|
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.trim())\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 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.log(`[bundler] 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,56 @@
|
|
|
1
1
|
import {
|
|
2
2
|
WorkerErrorCode,
|
|
3
|
+
__require,
|
|
3
4
|
ensureDir,
|
|
5
|
+
pathExistsSync,
|
|
6
|
+
readJsonSync,
|
|
4
7
|
resolveDependency
|
|
5
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-CI26Z23S.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
|
+
const json = readJsonSync(pkgPath);
|
|
22
|
+
if (!json?.version || typeof json.version !== "string") {
|
|
23
|
+
return "unknown";
|
|
24
|
+
}
|
|
25
|
+
return `${json.version}`;
|
|
26
|
+
} catch (err) {
|
|
27
|
+
return "unknown";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function resolvePackageJson(input, root) {
|
|
31
|
+
try {
|
|
32
|
+
return __require.resolve(`${input}/package.json`, {
|
|
33
|
+
paths: [root]
|
|
34
|
+
});
|
|
35
|
+
} catch {
|
|
36
|
+
try {
|
|
37
|
+
const entry = __require.resolve(input, {
|
|
38
|
+
paths: [root]
|
|
39
|
+
});
|
|
40
|
+
let dir = path.dirname(entry);
|
|
41
|
+
while (dir !== path.dirname(dir)) {
|
|
42
|
+
const candidate = path.join(dir, "package.json");
|
|
43
|
+
if (pathExistsSync(candidate)) return candidate;
|
|
44
|
+
dir = path.dirname(dir);
|
|
45
|
+
}
|
|
46
|
+
throw new Error(`package.json not found for ${input}`);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
throw new Error(`Cannot resolve package.json for "${input}"`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/worker.ts
|
|
12
54
|
function startWorkerGuard(child, opts, suspended) {
|
|
13
55
|
const { ttl = 5e3, memory = 128 } = opts;
|
|
14
56
|
let ttlTimer = null;
|
|
@@ -64,21 +106,23 @@ async function runWorker(context) {
|
|
|
64
106
|
const id = randomUUID();
|
|
65
107
|
const ctx = { ...context, id };
|
|
66
108
|
const start = performance.now();
|
|
67
|
-
const
|
|
68
|
-
await ensureDir(
|
|
109
|
+
const path2 = join(ctx.cwd, ctx.dist ?? ".xgsd");
|
|
110
|
+
await ensureDir(path2);
|
|
69
111
|
let started = false;
|
|
70
112
|
let completed = false;
|
|
71
113
|
let res;
|
|
72
114
|
const contextStr = JSON.stringify(ctx);
|
|
115
|
+
const version = getPackageVersion("@xgsd/workers", ctx.cwd);
|
|
73
116
|
const child = fork(join(__dirname, "process", "workers.process.js"), {
|
|
74
117
|
stdio: ["inherit", "pipe", "pipe", "ipc"],
|
|
75
118
|
//execArgv: [`--max-old-space-size=${ctx.limits?.memory ?? 128}`],
|
|
76
119
|
env: {
|
|
77
120
|
...ctx.env,
|
|
121
|
+
XGSD_WORKER_VERSION: version,
|
|
78
122
|
XGSD_CTX: contextStr
|
|
79
123
|
}
|
|
80
124
|
});
|
|
81
|
-
const events = createWriteStream(join(
|
|
125
|
+
const events = createWriteStream(join(path2, "events.jsonl"), { flags: "a" });
|
|
82
126
|
const startGuard = () => {
|
|
83
127
|
startWorkerGuard(child, ctx.limits ?? {}, (reason) => {
|
|
84
128
|
if (completed) return;
|
|
@@ -111,7 +155,7 @@ async function runWorker(context) {
|
|
|
111
155
|
child.stdout?.on("data", (chunk) => {
|
|
112
156
|
const lines = chunk.toString().split("\n").filter(Boolean);
|
|
113
157
|
for (const line of lines) {
|
|
114
|
-
console.log(
|
|
158
|
+
console.log(line.trim());
|
|
115
159
|
try {
|
|
116
160
|
writeEvent(JSON.parse(line));
|
|
117
161
|
} catch {
|
|
@@ -125,7 +169,6 @@ async function runWorker(context) {
|
|
|
125
169
|
});
|
|
126
170
|
child.stderr?.on("data", (chunk) => {
|
|
127
171
|
const e = { type: "error", message: chunk.toString(), timestamp: Date.now() };
|
|
128
|
-
console.error(e.message);
|
|
129
172
|
writeEvent(e);
|
|
130
173
|
});
|
|
131
174
|
child.on("message", (msg) => {
|
|
@@ -146,9 +189,10 @@ async function runWorker(context) {
|
|
|
146
189
|
}
|
|
147
190
|
res.duration = performance.now() - start;
|
|
148
191
|
appendFileSync(
|
|
149
|
-
join(
|
|
192
|
+
join(path2, "activations.jsonl"),
|
|
150
193
|
JSON.stringify({
|
|
151
194
|
id,
|
|
195
|
+
version,
|
|
152
196
|
ok: res.ok,
|
|
153
197
|
code: res.code,
|
|
154
198
|
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.trim())\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 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"]}
|
|
@@ -122,7 +122,7 @@ async function createBundle({
|
|
|
122
122
|
console.log(`[bundler] ${entry} bundled to ${outPathRel}`);
|
|
123
123
|
console.log(`[bundler] completed in ${ms.toFixed(2)}ms.`);
|
|
124
124
|
if (cacheStrategy === "never") {
|
|
125
|
-
console.
|
|
125
|
+
console.log(`[bundler] you can speed this up with bundler.cache.strategy = always|change.`);
|
|
126
126
|
}
|
|
127
127
|
return out;
|
|
128
128
|
}
|
|
@@ -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.log(`[bundler] 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-CI26Z23S.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.4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/util/fs.ts","../src/bundler.ts"],"sourcesContent":["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","path","readFile","require","path","readFile"]}
|