@xgsd/workers 0.1.0-beta.3 → 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.
@@ -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.warn(`[bundler] (warn) you can speed this up with bundler.cache.strategy = always|change.`);
106
+ console.log(`[bundler] you can speed this up with bundler.cache.strategy = always|change.`);
107
107
  }
108
108
  return out;
109
109
  }
@@ -221,4 +221,4 @@ export {
221
221
  createBundle,
222
222
  resolveDependency
223
223
  };
224
- //# sourceMappingURL=chunk-FPVPJQS7.js.map
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
@@ -80,7 +80,6 @@ var import_path = __toESM(require("path"), 1);
80
80
  function getPackageVersion(input, root = process.cwd()) {
81
81
  try {
82
82
  const pkgPath = resolvePackageJson(input, root);
83
- console.log(pkgPath);
84
83
  const json = readJsonSync(pkgPath);
85
84
  if (!json?.version || typeof json.version !== "string") {
86
85
  return "unknown";
@@ -218,7 +217,7 @@ async function runWorker(context) {
218
217
  child.stdout?.on("data", (chunk) => {
219
218
  const lines = chunk.toString().split("\n").filter(Boolean);
220
219
  for (const line of lines) {
221
- console.log(`${line}`);
220
+ console.log(line.trim());
222
221
  try {
223
222
  writeEvent(JSON.parse(line));
224
223
  } catch {
@@ -232,7 +231,6 @@ async function runWorker(context) {
232
231
  });
233
232
  child.stderr?.on("data", (chunk) => {
234
233
  const e = { type: "error", message: chunk.toString(), timestamp: Date.now() };
235
- console.error(e.message);
236
234
  writeEvent(e);
237
235
  });
238
236
  child.on("message", (msg) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/worker.ts","../src/types.ts","../src/util/fs.ts","../src/util/package.ts","../src/bundler.ts"],"sourcesContent":["import {WorkerResult, WorkerConfig} from './types.js'\nimport {runWorker} from './worker.js'\n\nexport {runWorker}\nexport * from './types.js'\n\nexport {resolveDependency} from './bundler.js'\n\nexport type Activation<T = unknown> = {\n id?: string\n data?: T\n env?: Record<string, unknown>\n cwd: string\n}\n\nexport type ActivationHandler = <T = unknown>(activation: Activation<T>) => Promise<WorkerResult<T>>\n\nexport function createHandler(config: WorkerConfig): ActivationHandler {\n return async function handler(activation) {\n return runWorker({\n ...config,\n id: activation.id,\n cwd: activation.cwd,\n data: activation.data,\n env: activation.env,\n })\n }\n}\n","import {fork} from 'child_process'\nimport {join} from 'path'\nimport {WorkerContext, WorkerError, WorkerErrorCode, WorkerResult} from './types.js'\nimport {createWriteStream, appendFileSync} from 'fs'\nimport {ensureDir} from './util/fs.js'\nimport {randomUUID} from 'crypto'\nimport {getPackageVersion} from './util/package.js'\n\nexport function startWorkerGuard(\n child: any,\n opts: {\n ttl?: number\n memory?: number\n },\n suspended?: (reason: string) => void,\n) {\n const {ttl = 5000, memory = 128} = opts\n\n let ttlTimer: NodeJS.Timeout | null = null\n let killed = false\n\n const kill = (reason: string) => {\n if (killed) return\n killed = true\n\n if (ttlTimer) clearTimeout(ttlTimer)\n\n child.kill('SIGKILL')\n suspended?.(reason)\n }\n\n // TTL watchdog\n ttlTimer = setTimeout(() => {\n kill(`ttl exceeded limit (limit: ${ttl.toFixed(2)}ms)`)\n }, ttl)\n\n child.on('message', (msg: any) => {\n if (msg.type !== 'ALIVE') {\n return\n }\n\n // (v0.1.0) this isn't set in stone\n // may be worth using RSS\n const heapUsed = msg.memory?.heapUsed ?? 0\n const memMB = heapUsed / 1024 / 1024\n\n if (memMB > memory) {\n kill(`memory limit exceeded: ${memMB.toFixed(2)}MB/${memory.toFixed(2)}MB`)\n }\n })\n\n child.on('exit', () => {\n if (ttlTimer) clearTimeout(ttlTimer)\n })\n}\n\ntype ChildMessage<T> =\n | {type: 'ALIVE'; error: undefined; result: undefined}\n | {type: 'DONE'; result: WorkerResult<T>; error: undefined | null}\n | {type: 'ERROR'; result: undefined | null; error: WorkerError}\n\nexport function normaliseKeys(value: any): any {\n if (Array.isArray(value)) {\n return value.map(normaliseKeys)\n }\n\n if (value && typeof value === 'object' && value.constructor === Object) {\n const sorted: Record<string, any> = {}\n\n for (const key of Object.keys(value).sort()) {\n sorted[key] = normaliseKeys(value[key])\n }\n\n return sorted\n }\n\n return value\n}\n\nexport function formatWorkerResult(opts: {result?: any; error?: any; duration: number}): WorkerResult<any> {\n return {\n version: 'v1',\n ok: !opts.error,\n result: opts.result ?? null,\n error: opts.error ?? null,\n duration: opts.duration,\n }\n}\n\nexport async function runWorker<T>(context: WorkerContext): Promise<WorkerResult<T>> {\n return new Promise(async (resolve, reject) => {\n const id = randomUUID()\n\n const ctx = {...context, id}\n const start = performance.now()\n\n const path = join(ctx.cwd!, ctx.dist ?? '.xgsd')\n await ensureDir(path)\n\n let started = false\n let completed = false\n let res: WorkerResult<unknown>\n\n const contextStr = JSON.stringify(ctx)\n const version = getPackageVersion('@xgsd/workers', ctx.cwd)\n\n // TODO: remove hardcoded worker path\n const child = fork(join(__dirname, 'process', 'workers.process.js'), {\n stdio: ['inherit', 'pipe', 'pipe', 'ipc'],\n //execArgv: [`--max-old-space-size=${ctx.limits?.memory ?? 128}`],\n env: {\n ...ctx.env,\n XGSD_WORKER_VERSION: version,\n XGSD_CTX: contextStr,\n },\n })\n\n const events = createWriteStream(join(path, 'events.jsonl'), {flags: 'a'})\n\n const startGuard = () => {\n startWorkerGuard(child, ctx.limits ?? {}, (reason: string) => {\n if (completed) return\n\n // normalise system/watch dog error\n // then *reject*\n const err: WorkerError = {\n code: WorkerErrorCode.CODE_WORKER_GUARD,\n message: `${reason}`,\n type: 'system',\n }\n\n console.warn(`[guard] worker suspended (reason: ${reason})`)\n\n writeEvent({\n type: 'error',\n message: err.message,\n guard: true,\n timestamp: Date.now(),\n })\n\n cleanup()\n\n resolve(formatWorkerResult({error: err, duration: performance.now() - start}))\n })\n }\n\n const cleanup = () => {\n if (child.connected) {\n child.removeAllListeners('message')\n\n child.disconnect()\n }\n }\n\n const writeEvent = (data: any) => {\n const normalised = normaliseKeys(data)\n events.write(JSON.stringify({...normalised, id}) + '\\n')\n }\n\n child.stdout?.on('data', (chunk) => {\n const lines = chunk.toString().split('\\n').filter(Boolean)\n\n for (const line of lines) {\n // log child process messages (typically from usercode)\n console.log(`${line}`)\n\n try {\n writeEvent(JSON.parse(line))\n } catch {\n // optionally fallback for non-json logs\n writeEvent({\n type: 'log',\n message: line,\n timestamp: Date.now(),\n })\n }\n }\n })\n\n child.stderr?.on('data', (chunk) => {\n const e = {type: 'error', message: chunk.toString(), timestamp: Date.now()}\n console.error(e.message)\n writeEvent(e)\n })\n\n child.on('message', (msg: ChildMessage<unknown>) => {\n if (msg.type !== 'ALIVE' && msg.type !== 'DONE' && msg.type !== 'ERROR') return\n\n if (msg.type === 'ALIVE') {\n if (!started) {\n started = true\n startGuard()\n }\n return\n }\n\n if (msg.type === 'ERROR') {\n const {error} = msg\n\n res = formatWorkerResult({error, duration: performance.now() - start})\n }\n\n if (msg.type === 'DONE') {\n // do something with result\n res = msg.result\n }\n\n res.duration = performance.now() - start\n\n // log activation\n appendFileSync(\n join(path, 'activations.jsonl'),\n JSON.stringify({\n id,\n version,\n ok: res.ok,\n code: res.code,\n duration: res.duration,\n error: res.error?.code ?? res.error?.message,\n result: res.result !== null,\n timestamp: new Date().toISOString(),\n }) + '\\n',\n )\n\n completed = true\n\n resolve(res as any)\n cleanup()\n })\n })\n}\n","export enum WorkerErrorCode {\n // thrown when limits exceeded (ttl/memory)\n CODE_WORKER_GUARD = 'CODE_WORKER_GUARD',\n\n // thrown when entry file is invalid/cannot be parsed\n CODE_INVALID_ENTRY_FILE = 'CODE_INVALID_ENTRY_FILE',\n\n // thrown when default is not a function\n CODE_INVALID_DEFAULT_FUNCTION = 'CODE_INVALID_DEFAULT_FUNCTION',\n\n CODE_INVALID_MIDDLEWARE_FUNCTION = 'CODE_INVALID_MIDDLEWARE_FUNCTION',\n\n // thrown when bundling fails\n CODE_BUNDLE_ERROR = 'CODE_BUNDLE_ERROR',\n}\n\nexport type WorkerErrorType = 'user' | 'system' | 'unknown'\nexport type WorkerError = {\n code?: WorkerErrorCode\n message?: string\n type?: WorkerErrorType\n}\n\nexport type WorkerResult<T> =\n | {\n version: 'v1'\n ok: true\n code?: number\n result: T\n error: null\n duration: number\n }\n | {\n version: 'v1'\n ok: false\n code?: number\n result: null\n error: WorkerError\n duration: number\n }\n\nexport type WorkerConfig = {\n entry: string\n dist?: string\n bundler?: {\n enabled?: boolean\n cache?: {\n strategy: 'never'\n }\n }\n http?: {\n enabled?: boolean\n }\n limits?: {\n ttl?: number\n memory?: number\n }\n}\n\nexport type WorkerContext<T = unknown> = WorkerConfig & {\n id?: string\n data: T\n cwd: string\n result?: any\n error?: any\n code?: number\n env?: Record<string, any>\n pid?: number\n}\n","import {readFileSync, writeFileSync, mkdirSync} from 'fs'\nimport {writeFile, readFile, constants, access, mkdir} from 'fs/promises'\nimport {dirname} from 'path'\n\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\n// ensureDir\nexport async function ensureDir(path: string): Promise<void> {\n await mkdir(path, {recursive: true})\n}\n\n// ensureDirSync\nexport function ensureDirSync(path: string): void {\n mkdirSync(path, {recursive: true})\n}\n\n// ensurePath\n// Ensures parent directory exists for a file path\nexport async function ensurePath(path: string): Promise<void> {\n await mkdir(dirname(path), {recursive: true})\n}\n\n// ensurePathSync\nexport function ensurePathSync(path: string): void {\n mkdirSync(dirname(path), {recursive: true})\n}\n\nexport async function readJson<T = any>(path: string): Promise<T> {\n const content = await readFile(path, 'utf8')\n return JSON.parse(content)\n}\n\nexport function readJsonSync<T = any>(path: string): T {\n return JSON.parse(readFileSync(path, 'utf8'))\n}\n\nexport async function writeJson(path: string, data: unknown, pretty = true): Promise<void> {\n const json = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)\n\n await writeFile(path, json, 'utf8')\n}\n\nexport function writeJsonSync(path: string, data: unknown, pretty = true): void {\n const json = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)\n\n writeFileSync(path, json, 'utf8')\n}\n\nexport function pathExistsSync(path: string): boolean {\n try {\n require('fs').accessSync(path, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n","import {pathExistsSync, readJsonSync} from './fs'\nimport path from 'path'\n\nexport function getPackageVersion(input: string, root: string = process.cwd()): string {\n try {\n const pkgPath = resolvePackageJson(input, root)\n\n console.log(pkgPath)\n\n const json = readJsonSync(pkgPath)\n\n if (!json?.version || typeof json.version !== 'string') {\n return 'unknown'\n }\n\n return `${json.version}`\n } catch (err: any) {\n return 'unknown'\n }\n}\n\nexport function resolvePackageJson(input: string, root: string): string {\n try {\n return require.resolve(`${input}/package.json`, {\n paths: [root],\n })\n } catch {\n try {\n const entry = require.resolve(input, {\n paths: [root],\n })\n\n let dir = path.dirname(entry)\n\n while (dir !== path.dirname(dir)) {\n const candidate = path.join(dir, 'package.json')\n if (pathExistsSync(candidate)) return candidate\n dir = path.dirname(dir)\n }\n\n throw new Error(`package.json not found for ${input}`)\n } catch (err: any) {\n throw new Error(`Cannot resolve package.json for \"${input}\"`)\n }\n }\n}\n","import {createHash, timingSafeEqual} from 'crypto'\nimport path, {join, relative, sep} from 'path'\nimport {readdir, readFile, stat} from 'fs/promises'\nimport {pathExistsSync, readJsonSync, writeJsonSync} from './util/fs'\nimport {createRequire} from 'module'\n\nexport async function createBundle({\n project,\n dist,\n cwd,\n entry,\n cacheStrategy,\n log,\n}: {\n project: string\n dist?: string\n cwd?: string\n entry: string\n cacheStrategy: 'always' | 'change' | 'never'\n log?: boolean\n}): Promise<string> {\n const start = performance.now()\n\n const xgsd = join(project, dist ?? '.xgsd')\n const out = join(xgsd, 'bundle.js')\n const entryFile = join(project, entry)\n const packageJsonPath = join(project, 'package.json')\n const outPathRel = join(dist ?? '.xgsd', 'bundle.js')\n\n // v0.7 note\n // dont do this as it adds 20-30MB of memory before anything even runs\n // bundling is fine but current AST parsing/traversal is unneeded\n // instead split into two concerns: dependencies (from package.json) and code changes (from hashes)\n // do this instead:\n const hash = await calculateProjectHash(project)\n const outdir = path.dirname(out)\n\n const packageJson = await readJsonSync(packageJsonPath)\n\n const outPackageJsonPath = join(outdir, 'package.json')\n const cacheFilesExist = pathExistsSync(outPackageJsonPath) && pathExistsSync(out)\n\n if (cacheFilesExist && cacheStrategy === 'always') {\n console.log(`${outPathRel} loaded from cache (set cache.strategy = \"never\" if this is unintentional)`)\n\n return out\n }\n\n if (cacheFilesExist && cacheStrategy === 'change') {\n const outPackageJson = readJsonSync(outPackageJsonPath)\n\n if (outPackageJson.hash && safeHashCompare(outPackageJson.hash, hash)) {\n // cache hit\n\n console.log(`${outPathRel} loaded from cache (set cache.strategy = \"never\" if this is unintentional)`)\n\n return out\n }\n }\n\n const dependencies = Object.entries(readJsonSync(packageJsonPath).dependencies).map((d) => d[0])\n const generated = new Date().toISOString()\n\n // for now let esbuild notify of errors\n await bundle({\n entry: entryFile,\n out,\n banner: {\n generated,\n hash,\n },\n format: 'esm',\n dependencies,\n })\n\n writeJsonSync(path.join(outdir, 'package.json'), {\n ...packageJson,\n hash,\n generated,\n type: 'module',\n })\n\n const ms = performance.now() - start\n\n console.log(`[bundler] copied package.json to ${join(dist ?? '.xgsd', 'package.json')}`)\n console.log(`[bundler] ${entry} bundled to ${outPathRel}`)\n console.log(`[bundler] completed in ${ms.toFixed(2)}ms.`)\n\n if (cacheStrategy === 'never') {\n console.warn(`[bundler] (warn) you can speed this up with bundler.cache.strategy = always|change.`)\n }\n\n return out\n}\n\nfunction bannerLines(object: Record<string, string>) {\n const lines = []\n lines.push(' * xGSD bundle.js')\n for (const key of Object.keys(object)) {\n lines.push(` * ${key}: ${object[key]}`)\n }\n lines.push(' * WARNING: this file is generated. Do not edit manually.')\n return lines.join('\\r\\n')\n}\n\nexport function resolvePath(moduleName: string, root: string): string {\n return require.resolve(moduleName, {\n paths: [root],\n })\n}\n\nexport function resolveDependency(dependency: string, projectRoot: string): any {\n try {\n const require = createRequire(join(projectRoot, 'package.json'))\n return require(dependency)\n } catch {}\n\n throw new Error(\n `Could not resolve ${dependency}.\\nInstall it with \\`yarn add ${dependency}\\`.\\nPath: ${projectRoot}.`,\n )\n}\n\nexport type WalkedFile = {\n path: string\n hash: string\n size: number\n}\n\ntype WalkOptions = {\n ignore?: string[]\n filter?: (path: string) => boolean\n}\n\nexport async function calculateProjectHash(project: string): Promise<string> {\n const hashes = await collectProjectHashes(project, {\n ignore: ['node_modules', '.xgsd', 'dist', '.git'],\n filter: (path) => path.endsWith('.js') || path.endsWith('.ts'),\n })\n\n const normalised = hashes\n .map((h) => h.hash.trim().slice(0, 9))\n .sort()\n .join('|')\n\n return createHash('sha256').update(normalised).digest('hex')\n}\n\nexport async function collectProjectHashes(projectPath: string, options: WalkOptions = {}): Promise<WalkedFile[]> {\n const {ignore = ['node_modules'], filter = () => true} = options\n\n const files: WalkedFile[] = []\n\n const ignored = new Set(ignore)\n\n const shouldIgnore = (target: string) => {\n const parts = relative(projectPath, target).split(sep)\n\n return parts.some((part) => ignored.has(part))\n }\n\n const hashFile = async (filePath: string) => {\n const buffer = await readFile(filePath)\n\n return createHash('sha256').update(buffer).digest('hex')\n }\n\n const visit = async (current: string): Promise<void> => {\n if (shouldIgnore(current)) {\n return\n }\n\n const entries = await readdir(current)\n\n for (const entry of entries) {\n const fullPath = join(current, entry)\n\n if (shouldIgnore(fullPath)) {\n continue\n }\n\n const info = await stat(fullPath)\n\n if (info.isDirectory()) {\n await visit(fullPath)\n continue\n }\n\n if (!info.isFile()) {\n continue\n }\n\n const path = relative(projectPath, fullPath)\n\n if (!filter(path)) {\n continue\n }\n\n files.push({\n path,\n hash: await hashFile(fullPath),\n size: info.size,\n })\n }\n }\n\n await visit(projectPath)\n\n return files.sort((a, b) => a.path.localeCompare(b.path))\n}\n\nexport function safeHashCompare(a: string, b: string): boolean {\n const abuf = Buffer.from(a, 'hex')\n const bbuf = Buffer.from(b, 'hex')\n\n return timingSafeEqual(abuf, bbuf)\n}\n\nexport async function bundle(options: {\n entry: string\n out: string\n format: 'esm' | 'cjs'\n banner: Record<string, string>\n dependencies: string[]\n}) {\n const {dependencies} = options\n\n const esbuild = resolveDependency('esbuild', path.dirname(options.entry))\n\n if (esbuild.version) {\n console.log(`[bundler] building with esbuild@${esbuild.version}`)\n }\n\n return esbuild.build({\n keepNames: true,\n entryPoints: [options.entry],\n bundle: true,\n platform: 'node',\n outfile: options.out,\n format: options.format,\n minify: false,\n sourcemap: false,\n external: dependencies,\n banner: {\n js: `\n/**\n${bannerLines(options.banner)}\n */\n`.trim(),\n },\n })\n}\n"],"mappings":"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","names":["import_path","WorkerErrorCode","import_fs","path","path","path","path","path","import_path","require"]}
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
@@ -5,7 +5,7 @@ import {
5
5
  pathExistsSync,
6
6
  readJsonSync,
7
7
  resolveDependency
8
- } from "./chunk-FPVPJQS7.js";
8
+ } from "./chunk-CI26Z23S.js";
9
9
 
10
10
  // src/worker.ts
11
11
  import { fork } from "child_process";
@@ -18,7 +18,6 @@ import path from "path";
18
18
  function getPackageVersion(input, root = process.cwd()) {
19
19
  try {
20
20
  const pkgPath = resolvePackageJson(input, root);
21
- console.log(pkgPath);
22
21
  const json = readJsonSync(pkgPath);
23
22
  if (!json?.version || typeof json.version !== "string") {
24
23
  return "unknown";
@@ -156,7 +155,7 @@ async function runWorker(context) {
156
155
  child.stdout?.on("data", (chunk) => {
157
156
  const lines = chunk.toString().split("\n").filter(Boolean);
158
157
  for (const line of lines) {
159
- console.log(`${line}`);
158
+ console.log(line.trim());
160
159
  try {
161
160
  writeEvent(JSON.parse(line));
162
161
  } catch {
@@ -170,7 +169,6 @@ async function runWorker(context) {
170
169
  });
171
170
  child.stderr?.on("data", (chunk) => {
172
171
  const e = { type: "error", message: chunk.toString(), timestamp: Date.now() };
173
- console.error(e.message);
174
172
  writeEvent(e);
175
173
  });
176
174
  child.on("message", (msg) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/worker.ts","../src/util/package.ts","../src/index.ts"],"sourcesContent":["import {fork} from 'child_process'\nimport {join} from 'path'\nimport {WorkerContext, WorkerError, WorkerErrorCode, WorkerResult} from './types.js'\nimport {createWriteStream, appendFileSync} from 'fs'\nimport {ensureDir} from './util/fs.js'\nimport {randomUUID} from 'crypto'\nimport {getPackageVersion} from './util/package.js'\n\nexport function startWorkerGuard(\n child: any,\n opts: {\n ttl?: number\n memory?: number\n },\n suspended?: (reason: string) => void,\n) {\n const {ttl = 5000, memory = 128} = opts\n\n let ttlTimer: NodeJS.Timeout | null = null\n let killed = false\n\n const kill = (reason: string) => {\n if (killed) return\n killed = true\n\n if (ttlTimer) clearTimeout(ttlTimer)\n\n child.kill('SIGKILL')\n suspended?.(reason)\n }\n\n // TTL watchdog\n ttlTimer = setTimeout(() => {\n kill(`ttl exceeded limit (limit: ${ttl.toFixed(2)}ms)`)\n }, ttl)\n\n child.on('message', (msg: any) => {\n if (msg.type !== 'ALIVE') {\n return\n }\n\n // (v0.1.0) this isn't set in stone\n // may be worth using RSS\n const heapUsed = msg.memory?.heapUsed ?? 0\n const memMB = heapUsed / 1024 / 1024\n\n if (memMB > memory) {\n kill(`memory limit exceeded: ${memMB.toFixed(2)}MB/${memory.toFixed(2)}MB`)\n }\n })\n\n child.on('exit', () => {\n if (ttlTimer) clearTimeout(ttlTimer)\n })\n}\n\ntype ChildMessage<T> =\n | {type: 'ALIVE'; error: undefined; result: undefined}\n | {type: 'DONE'; result: WorkerResult<T>; error: undefined | null}\n | {type: 'ERROR'; result: undefined | null; error: WorkerError}\n\nexport function normaliseKeys(value: any): any {\n if (Array.isArray(value)) {\n return value.map(normaliseKeys)\n }\n\n if (value && typeof value === 'object' && value.constructor === Object) {\n const sorted: Record<string, any> = {}\n\n for (const key of Object.keys(value).sort()) {\n sorted[key] = normaliseKeys(value[key])\n }\n\n return sorted\n }\n\n return value\n}\n\nexport function formatWorkerResult(opts: {result?: any; error?: any; duration: number}): WorkerResult<any> {\n return {\n version: 'v1',\n ok: !opts.error,\n result: opts.result ?? null,\n error: opts.error ?? null,\n duration: opts.duration,\n }\n}\n\nexport async function runWorker<T>(context: WorkerContext): Promise<WorkerResult<T>> {\n return new Promise(async (resolve, reject) => {\n const id = randomUUID()\n\n const ctx = {...context, id}\n const start = performance.now()\n\n const path = join(ctx.cwd!, ctx.dist ?? '.xgsd')\n await ensureDir(path)\n\n let started = false\n let completed = false\n let res: WorkerResult<unknown>\n\n const contextStr = JSON.stringify(ctx)\n const version = getPackageVersion('@xgsd/workers', ctx.cwd)\n\n // TODO: remove hardcoded worker path\n const child = fork(join(__dirname, 'process', 'workers.process.js'), {\n stdio: ['inherit', 'pipe', 'pipe', 'ipc'],\n //execArgv: [`--max-old-space-size=${ctx.limits?.memory ?? 128}`],\n env: {\n ...ctx.env,\n XGSD_WORKER_VERSION: version,\n XGSD_CTX: contextStr,\n },\n })\n\n const events = createWriteStream(join(path, 'events.jsonl'), {flags: 'a'})\n\n const startGuard = () => {\n startWorkerGuard(child, ctx.limits ?? {}, (reason: string) => {\n if (completed) return\n\n // normalise system/watch dog error\n // then *reject*\n const err: WorkerError = {\n code: WorkerErrorCode.CODE_WORKER_GUARD,\n message: `${reason}`,\n type: 'system',\n }\n\n console.warn(`[guard] worker suspended (reason: ${reason})`)\n\n writeEvent({\n type: 'error',\n message: err.message,\n guard: true,\n timestamp: Date.now(),\n })\n\n cleanup()\n\n resolve(formatWorkerResult({error: err, duration: performance.now() - start}))\n })\n }\n\n const cleanup = () => {\n if (child.connected) {\n child.removeAllListeners('message')\n\n child.disconnect()\n }\n }\n\n const writeEvent = (data: any) => {\n const normalised = normaliseKeys(data)\n events.write(JSON.stringify({...normalised, id}) + '\\n')\n }\n\n child.stdout?.on('data', (chunk) => {\n const lines = chunk.toString().split('\\n').filter(Boolean)\n\n for (const line of lines) {\n // log child process messages (typically from usercode)\n console.log(`${line}`)\n\n try {\n writeEvent(JSON.parse(line))\n } catch {\n // optionally fallback for non-json logs\n writeEvent({\n type: 'log',\n message: line,\n timestamp: Date.now(),\n })\n }\n }\n })\n\n child.stderr?.on('data', (chunk) => {\n const e = {type: 'error', message: chunk.toString(), timestamp: Date.now()}\n console.error(e.message)\n writeEvent(e)\n })\n\n child.on('message', (msg: ChildMessage<unknown>) => {\n if (msg.type !== 'ALIVE' && msg.type !== 'DONE' && msg.type !== 'ERROR') return\n\n if (msg.type === 'ALIVE') {\n if (!started) {\n started = true\n startGuard()\n }\n return\n }\n\n if (msg.type === 'ERROR') {\n const {error} = msg\n\n res = formatWorkerResult({error, duration: performance.now() - start})\n }\n\n if (msg.type === 'DONE') {\n // do something with result\n res = msg.result\n }\n\n res.duration = performance.now() - start\n\n // log activation\n appendFileSync(\n join(path, 'activations.jsonl'),\n JSON.stringify({\n id,\n version,\n ok: res.ok,\n code: res.code,\n duration: res.duration,\n error: res.error?.code ?? res.error?.message,\n result: res.result !== null,\n timestamp: new Date().toISOString(),\n }) + '\\n',\n )\n\n completed = true\n\n resolve(res as any)\n cleanup()\n })\n })\n}\n","import {pathExistsSync, readJsonSync} from './fs'\nimport path from 'path'\n\nexport function getPackageVersion(input: string, root: string = process.cwd()): string {\n try {\n const pkgPath = resolvePackageJson(input, root)\n\n console.log(pkgPath)\n\n const json = readJsonSync(pkgPath)\n\n if (!json?.version || typeof json.version !== 'string') {\n return 'unknown'\n }\n\n return `${json.version}`\n } catch (err: any) {\n return 'unknown'\n }\n}\n\nexport function resolvePackageJson(input: string, root: string): string {\n try {\n return require.resolve(`${input}/package.json`, {\n paths: [root],\n })\n } catch {\n try {\n const entry = require.resolve(input, {\n paths: [root],\n })\n\n let dir = path.dirname(entry)\n\n while (dir !== path.dirname(dir)) {\n const candidate = path.join(dir, 'package.json')\n if (pathExistsSync(candidate)) return candidate\n dir = path.dirname(dir)\n }\n\n throw new Error(`package.json not found for ${input}`)\n } catch (err: any) {\n throw new Error(`Cannot resolve package.json for \"${input}\"`)\n }\n }\n}\n","import {WorkerResult, WorkerConfig} from './types.js'\nimport {runWorker} from './worker.js'\n\nexport {runWorker}\nexport * from './types.js'\n\nexport {resolveDependency} from './bundler.js'\n\nexport type Activation<T = unknown> = {\n id?: string\n data?: T\n env?: Record<string, unknown>\n cwd: string\n}\n\nexport type ActivationHandler = <T = unknown>(activation: Activation<T>) => Promise<WorkerResult<T>>\n\nexport function createHandler(config: WorkerConfig): ActivationHandler {\n return async function handler(activation) {\n return runWorker({\n ...config,\n id: activation.id,\n cwd: activation.cwd,\n data: activation.data,\n env: activation.env,\n })\n }\n}\n"],"mappings":"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","names":["path"]}
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.warn(`[bundler] (warn) you can speed this up with bundler.cache.strategy = always|change.`);
125
+ console.log(`[bundler] you can speed this up with bundler.cache.strategy = always|change.`);
126
126
  }
127
127
  return out;
128
128
  }
@@ -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'\nimport {getPackageVersion} from '../util/package'\n\nexport type RunFn<T> = (data: T) => Promise<any>\n\nfunction dispatch(event: 'ALIVE' | 'DONE' | 'ERROR', payload: any) {\n process.send?.({\n type: event,\n ...payload,\n })\n}\n\nfunction startHeartbeat(interval = 50) {\n return setInterval(() => {\n const memory = process.memoryUsage()\n\n dispatch('ALIVE', {\n pid: process.pid,\n uptime: process.uptime(),\n memory: {\n rss: memory.rss,\n heapUsed: memory.heapUsed,\n heapTotal: memory.heapTotal,\n external: memory.external,\n },\n })\n }, interval)\n}\n\nexport function wrapper(fn: RunFn<unknown>) {\n const {execute} = resolveDependency('@xgsd/engine', ctx.cwd!)\n\n return async (ctx: WorkerContext, next: Next) => {\n const res = await execute(ctx.data as any, fn)\n\n ctx.result = res.data\n ctx.error = res.error\n\n await next()\n }\n}\n\nasync function main(ctx: WorkerContext) {\n const heartbeat = startHeartbeat()\n\n const {entry, cwd} = ctx\n let entryFile = join(cwd ?? '', entry)\n\n try {\n if (!(await pathExists(entryFile))) {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_ENTRY_FILE,\n message: 'entry file not found',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n // bundler\n if (ctx.bundler?.enabled) {\n entryFile = await createBundle({\n project: cwd!,\n dist: ctx.dist,\n entry,\n cacheStrategy: ctx.bundler?.cache?.strategy ?? 'never',\n })\n }\n\n let mod = undefined\n try {\n mod = await import(entryFile)\n } catch (error) {}\n\n if (!mod || !mod.default || typeof mod.default !== 'function') {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_DEFAULT_FUNCTION,\n message: 'default must be a function',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n // load middleware\n let middleware = []\n if (mod.middleware && typeof mod.middleware === 'function') {\n const start = performance.now()\n middleware = mod.middleware() ?? []\n\n if (!Array.isArray(middleware) || middleware.filter((m) => typeof m !== 'function').length > 0) {\n const error: WorkerError = {\n code: WorkerErrorCode.CODE_INVALID_MIDDLEWARE_FUNCTION,\n message: 'middleware not configured correctly',\n type: 'user',\n }\n\n dispatch('ERROR', {error})\n return\n }\n\n const dt = (performance.now() - start).toFixed(2)\n console.log(`[middleware] ${middleware.length} functions registered in ${dt} ms`)\n }\n\n // runtime\n const runtime = compose([...middleware, wrapper(mod.default)])\n const version = process.env.XGSD_WORKER_VERSION ?? 'unknown'\n\n console.log(`[runtime] started running worker (version: ${version})`)\n\n const start = performance.now()\n\n const result = await runtime(ctx)\n\n const ms = performance.now() - start\n console.log(`[runtime] finished running worker took ${ms.toFixed(2)} ms`)\n\n if (result.error) {\n console.warn(`[runtime] finished with errors (error: ${ctx.error?.message ?? 'unknown'})`)\n }\n\n dispatch('DONE', {result})\n } finally {\n clearInterval(heartbeat)\n }\n}\n\nconst ctx = JSON.parse(process.env.XGSD_CTX!) as WorkerContext\nmain(ctx)\n","import {createHash, timingSafeEqual} from 'crypto'\nimport path, {join, relative, sep} from 'path'\nimport {readdir, readFile, stat} from 'fs/promises'\nimport {pathExistsSync, readJsonSync, writeJsonSync} from './util/fs'\nimport {createRequire} from 'module'\n\nexport async function createBundle({\n project,\n dist,\n cwd,\n entry,\n cacheStrategy,\n log,\n}: {\n project: string\n dist?: string\n cwd?: string\n entry: string\n cacheStrategy: 'always' | 'change' | 'never'\n log?: boolean\n}): Promise<string> {\n const start = performance.now()\n\n const xgsd = join(project, dist ?? '.xgsd')\n const out = join(xgsd, 'bundle.js')\n const entryFile = join(project, entry)\n const packageJsonPath = join(project, 'package.json')\n const outPathRel = join(dist ?? '.xgsd', 'bundle.js')\n\n // v0.7 note\n // dont do this as it adds 20-30MB of memory before anything even runs\n // bundling is fine but current AST parsing/traversal is unneeded\n // instead split into two concerns: dependencies (from package.json) and code changes (from hashes)\n // do this instead:\n const hash = await calculateProjectHash(project)\n const outdir = path.dirname(out)\n\n const packageJson = await readJsonSync(packageJsonPath)\n\n const outPackageJsonPath = join(outdir, 'package.json')\n const cacheFilesExist = pathExistsSync(outPackageJsonPath) && pathExistsSync(out)\n\n if (cacheFilesExist && cacheStrategy === 'always') {\n console.log(`${outPathRel} loaded from cache (set cache.strategy = \"never\" if this is unintentional)`)\n\n return out\n }\n\n if (cacheFilesExist && cacheStrategy === 'change') {\n const outPackageJson = readJsonSync(outPackageJsonPath)\n\n if (outPackageJson.hash && safeHashCompare(outPackageJson.hash, hash)) {\n // cache hit\n\n console.log(`${outPathRel} loaded from cache (set cache.strategy = \"never\" if this is unintentional)`)\n\n return out\n }\n }\n\n const dependencies = Object.entries(readJsonSync(packageJsonPath).dependencies).map((d) => d[0])\n const generated = new Date().toISOString()\n\n // for now let esbuild notify of errors\n await bundle({\n entry: entryFile,\n out,\n banner: {\n generated,\n hash,\n },\n format: 'esm',\n dependencies,\n })\n\n writeJsonSync(path.join(outdir, 'package.json'), {\n ...packageJson,\n hash,\n generated,\n type: 'module',\n })\n\n const ms = performance.now() - start\n\n console.log(`[bundler] copied package.json to ${join(dist ?? '.xgsd', 'package.json')}`)\n console.log(`[bundler] ${entry} bundled to ${outPathRel}`)\n console.log(`[bundler] completed in ${ms.toFixed(2)}ms.`)\n\n if (cacheStrategy === 'never') {\n console.warn(`[bundler] (warn) you can speed this up with bundler.cache.strategy = always|change.`)\n }\n\n return out\n}\n\nfunction bannerLines(object: Record<string, string>) {\n const lines = []\n lines.push(' * xGSD bundle.js')\n for (const key of Object.keys(object)) {\n lines.push(` * ${key}: ${object[key]}`)\n }\n lines.push(' * WARNING: this file is generated. Do not edit manually.')\n return lines.join('\\r\\n')\n}\n\nexport function resolvePath(moduleName: string, root: string): string {\n return require.resolve(moduleName, {\n paths: [root],\n })\n}\n\nexport function resolveDependency(dependency: string, projectRoot: string): any {\n try {\n const require = createRequire(join(projectRoot, 'package.json'))\n return require(dependency)\n } catch {}\n\n throw new Error(\n `Could not resolve ${dependency}.\\nInstall it with \\`yarn add ${dependency}\\`.\\nPath: ${projectRoot}.`,\n )\n}\n\nexport type WalkedFile = {\n path: string\n hash: string\n size: number\n}\n\ntype WalkOptions = {\n ignore?: string[]\n filter?: (path: string) => boolean\n}\n\nexport async function calculateProjectHash(project: string): Promise<string> {\n const hashes = await collectProjectHashes(project, {\n ignore: ['node_modules', '.xgsd', 'dist', '.git'],\n filter: (path) => path.endsWith('.js') || path.endsWith('.ts'),\n })\n\n const normalised = hashes\n .map((h) => h.hash.trim().slice(0, 9))\n .sort()\n .join('|')\n\n return createHash('sha256').update(normalised).digest('hex')\n}\n\nexport async function collectProjectHashes(projectPath: string, options: WalkOptions = {}): Promise<WalkedFile[]> {\n const {ignore = ['node_modules'], filter = () => true} = options\n\n const files: WalkedFile[] = []\n\n const ignored = new Set(ignore)\n\n const shouldIgnore = (target: string) => {\n const parts = relative(projectPath, target).split(sep)\n\n return parts.some((part) => ignored.has(part))\n }\n\n const hashFile = async (filePath: string) => {\n const buffer = await readFile(filePath)\n\n return createHash('sha256').update(buffer).digest('hex')\n }\n\n const visit = async (current: string): Promise<void> => {\n if (shouldIgnore(current)) {\n return\n }\n\n const entries = await readdir(current)\n\n for (const entry of entries) {\n const fullPath = join(current, entry)\n\n if (shouldIgnore(fullPath)) {\n continue\n }\n\n const info = await stat(fullPath)\n\n if (info.isDirectory()) {\n await visit(fullPath)\n continue\n }\n\n if (!info.isFile()) {\n continue\n }\n\n const path = relative(projectPath, fullPath)\n\n if (!filter(path)) {\n continue\n }\n\n files.push({\n path,\n hash: await hashFile(fullPath),\n size: info.size,\n })\n }\n }\n\n await visit(projectPath)\n\n return files.sort((a, b) => a.path.localeCompare(b.path))\n}\n\nexport function safeHashCompare(a: string, b: string): boolean {\n const abuf = Buffer.from(a, 'hex')\n const bbuf = Buffer.from(b, 'hex')\n\n return timingSafeEqual(abuf, bbuf)\n}\n\nexport async function bundle(options: {\n entry: string\n out: string\n format: 'esm' | 'cjs'\n banner: Record<string, string>\n dependencies: string[]\n}) {\n const {dependencies} = options\n\n const esbuild = resolveDependency('esbuild', path.dirname(options.entry))\n\n if (esbuild.version) {\n console.log(`[bundler] building with esbuild@${esbuild.version}`)\n }\n\n return esbuild.build({\n keepNames: true,\n entryPoints: [options.entry],\n bundle: true,\n platform: 'node',\n outfile: options.out,\n format: options.format,\n minify: false,\n sourcemap: false,\n external: dependencies,\n banner: {\n js: `\n/**\n${bannerLines(options.banner)}\n */\n`.trim(),\n },\n })\n}\n","import {readFileSync, writeFileSync, mkdirSync} from 'fs'\nimport {writeFile, readFile, constants, access, mkdir} from 'fs/promises'\nimport {dirname} from 'path'\n\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\n// ensureDir\nexport async function ensureDir(path: string): Promise<void> {\n await mkdir(path, {recursive: true})\n}\n\n// ensureDirSync\nexport function ensureDirSync(path: string): void {\n mkdirSync(path, {recursive: true})\n}\n\n// ensurePath\n// Ensures parent directory exists for a file path\nexport async function ensurePath(path: string): Promise<void> {\n await mkdir(dirname(path), {recursive: true})\n}\n\n// ensurePathSync\nexport function ensurePathSync(path: string): void {\n mkdirSync(dirname(path), {recursive: true})\n}\n\nexport async function readJson<T = any>(path: string): Promise<T> {\n const content = await readFile(path, 'utf8')\n return JSON.parse(content)\n}\n\nexport function readJsonSync<T = any>(path: string): T {\n return JSON.parse(readFileSync(path, 'utf8'))\n}\n\nexport async function writeJson(path: string, data: unknown, pretty = true): Promise<void> {\n const json = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)\n\n await writeFile(path, json, 'utf8')\n}\n\nexport function writeJsonSync(path: string, data: unknown, pretty = true): void {\n const json = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)\n\n writeFileSync(path, json, 'utf8')\n}\n\nexport function pathExistsSync(path: string): boolean {\n try {\n require('fs').accessSync(path, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n","import {resolveDependency} from './bundler'\nimport {WorkerContext, WorkerResult} from './types'\n\nexport type Next = () => Promise<void>\n\nexport type Middleware = (ctx: WorkerContext, next: Next) => Promise<void>\n\nexport type ComposedMiddleware = (ctx: WorkerContext) => Promise<WorkerResult<unknown>>\n\nexport function compose(middleware: Middleware[]): ComposedMiddleware {\n return async function run(ctx: WorkerContext): Promise<WorkerResult<unknown>> {\n let index = -1\n\n let res: any = {\n version: 'v1',\n ok: undefined,\n result: undefined,\n error: undefined,\n duration: undefined,\n }\n\n const {execute} = resolveDependency('@xgsd/engine', ctx.cwd!)\n\n async function dispatch(i: number): Promise<void> {\n if (i <= index) {\n throw new Error('next() called multiple times')\n }\n\n index = i\n\n const fn = middleware[i]\n\n if (!fn) {\n return\n }\n\n const executeWrapper = async (ctx: WorkerContext) => {\n await fn(ctx, async () => {\n await dispatch(i + 1)\n })\n\n return ctx\n }\n\n const result = await execute(ctx, executeWrapper)\n\n if (result.error || result.data?.error) {\n res.ok = false\n res.code = ctx.code\n res.result = null\n res.error = result.error ?? result.data?.error\n }\n\n if (result.data && result.data.result) {\n res.ok = true\n res.code = ctx.code\n res.result = result.data.result\n res.error = result.error ?? result.data?.error\n }\n }\n\n await dispatch(0)\n\n return res\n }\n}\n"],"mappings":"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","names":["import_path","import_promises","path","path","path","path","require","path","ctx","dispatch","ctx","start"]}
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-FPVPJQS7.js";
5
+ } from "../chunk-CI26Z23S.js";
6
6
 
7
7
  // src/process/workers.process.ts
8
8
  import { join } from "path";
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.3",
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"]}