@agimon-ai/doompi 0.0.1-alpha.20 → 0.0.1-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/extensionCompiler.cjs +2 -2
- package/dist/adapters/extensionCompiler.cjs.map +1 -1
- package/dist/adapters/extensionCompiler.mjs +2 -2
- package/dist/adapters/extensionCompiler.mjs.map +1 -1
- package/dist/adapters/sharedBuildCache.cjs +1 -1
- package/dist/adapters/sharedBuildCache.cjs.map +1 -1
- package/dist/adapters/sharedBuildCache.mjs +1 -1
- package/dist/adapters/sharedBuildCache.mjs.map +1 -1
- package/package.json +19 -19
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`./modules/moduleResolution.cjs`);require(`../utils/index.cjs`);const n=require(`./sharedBuildCache.cjs`);let r=require(`node:url`),i=require(`node:path`);i=e.__toESM(i,1);let a=require(`node:fs`);a=e.__toESM(a,1);let o=require(`node:crypto`),s=require(`node:module`);const c=[`.ts`,`.mts`,`.cts`],l=`.mjs`,u=`sets`,d=`\0doompi-extension-set`,f=`@agimon-ai/doompi-ui/extensionName`,p=`__DOOMPI_PATH_`,m={"@mariozechner/pi-coding-agent":`@earendil-works/pi-coding-agent`,"@mariozechner/pi-tui":`@earendil-works/pi-tui`,"@mariozechner/pi-ai":`@earendil-works/pi-ai`,"@sinclair/typebox":`typebox`};function
|
|
2
|
-
`)}function
|
|
1
|
+
const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`./modules/moduleResolution.cjs`);require(`../utils/index.cjs`);const n=require(`./sharedBuildCache.cjs`);let r=require(`node:url`),i=require(`node:path`);i=e.__toESM(i,1);let a=require(`node:fs`);a=e.__toESM(a,1);let o=require(`node:crypto`),s=require(`node:module`);const c=[`.ts`,`.mts`,`.cts`],l=`.mjs`,u=`sets`,d=`\0doompi-extension-set`,f=`@agimon-ai/doompi-ui/extensionName`,p=`__DOOMPI_PATH_`,m=`repo/`,h=`external/`,g={"@mariozechner/pi-coding-agent":`@earendil-works/pi-coding-agent`,"@mariozechner/pi-tui":`@earendil-works/pi-tui`,"@mariozechner/pi-ai":`@earendil-works/pi-ai`,"@sinclair/typebox":`typebox`};function _(e){return c.some(t=>e.endsWith(t))}function v(e){for(let[t,n]of Object.entries(g)){if(e===t)return n;if(e.startsWith(`${t}/`))return`${n}${e.slice(t.length)}`}return e}const y=new Set([`@earendil-works/pi-coding-agent`,`@earendil-works/pi-tui`,`@earendil-works/pi-ai`]);function ee(e){let n=t.optionalPackageEntry(`@earendil-works/pi-coding-agent`);if(n)try{return(0,s.createRequire)(n).resolve(e)}catch{return}}const b=new Set([`@parcel/watcher`,`@napi-rs/keyring`,`better-sqlite3`,`canvas`,`fsevents`,`isolated-vm`,`keytar`,`node-pty`,`onnxruntime-node`,`playwright`,`playwright-core`,`puppeteer`,`sharp`,`typescript`]),x=new Set([`@agimon-ai/foundation-port-registry`,`@agimon-ai/foundation-process-registry`,`@agimon-ai/log-sink-mcp`,`@modelcontextprotocol/sdk`,`@narumitw/pi-tui-kit`,`@rmux/sdk`,`@tursodatabase/database`,`@tursodatabase/database-common`,`ajv`,`ajv-formats`,`gpt-tokenizer`,`highlight.js`,`hono`,`inversify`,`open`,`reflect-metadata`,`zod-to-json-schema`,`yaml`,`zod`]),S=new Set(s.builtinModules.flatMap(e=>[e,`node:${e}`]));function C(e){return e.trim().replace(/[^a-zA-Z0-9._-]+/g,`-`).replace(/^[-.]+|[-.]+$/g,``)||`doom-set`}function w(e){let t=e.startsWith(`@`)?e.indexOf(`/`,e.indexOf(`/`)+1):e.indexOf(`/`);return t===-1?e:e.slice(0,t)}function te(e){let t=i.default.dirname(e);for(;;){let e=i.default.join(t,`package.json`);if(a.default.existsSync(e))try{let t=JSON.parse(a.default.readFileSync(e,`utf8`));if(Z(t)&&typeof t.name==`string`)return t.name}catch{return}let n=i.default.dirname(t);if(n===t)return;t=n}}function T(e){let t=i.default.resolve(e);try{return a.default.realpathSync.native(t)}catch{return t}}function E(e,t){let n=i.default.relative(i.default.resolve(e),i.default.resolve(t));return n===``||!n.startsWith(`..`)&&!i.default.isAbsolute(n)}function D(e){return`${h}${(0,o.createHash)(`sha256`).update(e).digest(`hex`)}`}function O(e,t){let n=T(e);return E(t,n)?{logicalPath:`${m}${i.default.relative(t,n).split(i.default.sep).join(`/`)}`,target:n}:{logicalPath:D(n),target:n,sourcePath:n}}function k(e,t){if(e.logicalPath.startsWith(m)&&e.sourcePath===void 0){let n=i.default.resolve(t,e.logicalPath.slice(5));return E(t,n)?n:void 0}if(e.logicalPath.startsWith(h)&&e.sourcePath!==void 0&&e.logicalPath===D(e.sourcePath))return e.sourcePath}function A(e){try{let t=a.default.statSync(e);return t.isFile()?{path:e,size:t.size,mtimeMs:t.mtimeMs}:void 0}catch{return}}function j(e,t,n){return e.version===t&&e.output===n&&a.default.existsSync(n)&&e.artifacts.every(e=>a.default.existsSync(e))}function M(e,t){return!j(e,`v12`,e.output)||JSON.stringify(e.entries)!==JSON.stringify(t)?!1:e.inputs.every(e=>{let t=A(e.path);return t?.size===e.size&&t.mtimeMs===e.mtimeMs})}function N(e){try{let t=JSON.parse(a.default.readFileSync(e,`utf8`));return t&&typeof t.output==`string`&&Array.isArray(t.artifacts)?t:void 0}catch{return}}function P(e){let t=N(e);return t&&Array.isArray(t.entries)&&Array.isArray(t.inputs)?t:void 0}function F(e,t){a.default.mkdirSync(i.default.dirname(e),{recursive:!0});let n=`${e}.${process.pid}.${Date.now()}.tmp`;a.default.writeFileSync(n,t);try{a.default.renameSync(n,e)}catch(t){if(a.default.rmSync(n,{force:!0}),!a.default.existsSync(e))throw t}}function I(e){return{format:`esm`,sourcemap:!1,minify:!0,codeSplitting:!0,hashCharacters:`hex`,entryFileNames:e,chunkFileNames:`chunks/[name].[hash:16]${l}`,assetFileNames:`assets/[name].[hash:16][extname]`}}function L(e,t){let n=i.default.resolve(e,t.fileName),r=i.default.relative(e,n);if(r.startsWith(`..`)||i.default.isAbsolute(r))throw Error(`Rolldown emitted an artifact outside the output directory: ${t.fileName}`);return n}function R(e,t){try{let n=a.default.readFileSync(e),r=Buffer.from(t);return n.equals(r)}catch{return!1}}function z(e,t){let n=e.output.find(e=>e.type===`chunk`&&e.isEntry);if(!n||n.type!==`chunk`)throw Error(`Rolldown produced no extension entry`);let r=i.default.resolve(t),a=e.output.filter(e=>e!==n);for(let e of[...a,n]){let t=e.type===`chunk`?e.code:e.source,n=L(r,e);R(n,t)||F(n,t)}return{entry:L(r,n),artifacts:e.output.map(e=>L(r,e)).sort()}}function B(e,t){return(0,o.createHash)(`sha256`).update(`v3-rolldown-1.0.0-rc.17`).update(process.version).update(process.platform).update(process.arch).update(t).update(JSON.stringify(e.map(e=>e.logicalPath))).digest(`hex`)}function V(e,t){let r=[];for(let n of e)r.push(O(n,t));r.sort((e,t)=>e.logicalPath.localeCompare(t.logicalPath));let o=[{logicalPath:m,sha256:``,token:`${p}ROOT__`}],s=new Map([[t,`${p}ROOT__`]]),c=new Map;r.forEach((e,t)=>{let r=c.get(e.logicalPath),l=r??`${p}${String(t)}__`;r||(o.push({logicalPath:e.logicalPath,sha256:n.contentSha256(a.default.readFileSync(e.target)),token:l,...e.sourcePath?{sourcePath:e.sourcePath}:{}}),c.set(e.logicalPath,l)),s.set(i.default.resolve(e.target),l)});for(let n of e){let e=O(n,t),r=c.get(e.logicalPath);r&&s.set(i.default.resolve(n),r)}return{shared:o,byTarget:s}}function H(e,t){let n=new Map,i=[...t.entries()].sort(([e],[t])=>t.length-e.length);for(let t of e.output){if(t.type===`asset`){n.set(t.fileName,(t.source,t.source));continue}let e=t.code;for(let[t,n]of i)e=e.replaceAll((0,r.pathToFileURL)(t).href,`${n}:url`).replaceAll(q(t),n).replaceAll(t,n);n.set(t.fileName,e)}return n}function U(e,t,n,r,i){let a=[...i].map(A).filter(e=>e!==void 0).sort((e,t)=>e.path.localeCompare(t.path));F(e,`${JSON.stringify({version:`v12`,entries:t,output:n,artifacts:[...r].sort(),inputs:a},null,2)}\n`)}function W(e,t,r,a){let o=n.materializeSharedBuild(e,t),s=new Set(e.replacements.values());return U(r,a,o,e.manifest.artifacts.map(e=>i.default.join(t,e.path)),s),o}function G(e){return(0,o.createHash)(`sha256`).update(`v12`).update(JSON.stringify(e.map(e=>i.default.resolve(e)))).digest(`hex`).slice(0,16)}function K(e){let n=t.optionalPackageEntry(f);if(!n)throw Error(`Cannot resolve ${f}`);let r=e.map(e=>`() => import(${JSON.stringify(q(e))})`),i=e.map(e=>JSON.stringify(e));return[`import { withExtensionSource } from ${JSON.stringify(q(n))};`,`const loaders = [${r.join(`,`)}];`,`const names = [${i.join(`,`)}];`,`function report(index, phase, startedAt, error) {`,` const elapsed = (performance.now() - startedAt).toFixed(1);`,` const detail = error instanceof Error ? ": " + error.message : error === undefined ? "" : ": " + String(error);`,` process.stderr.write("[doompi:timing] " + phase + " " + elapsed + "ms " + names[index] + detail + "\\n");`,`}`,`export default async function doompiExtensionSet(pi) {`,` const timings = process.env.DOOMPI_TIMING === "1" || process.env.PI_TIMING === "1";`,` const loaded = await Promise.all(loaders.map(async (load, index) => {`,` const startedAt = performance.now();`,` try {`,` const extension = await load();`,` if (timings) report(index, "import", startedAt);`,` return extension;`,` } catch (error) {`,` report(index, "import-error", startedAt, error);`,` return undefined;`,` }`,` }));`,` for (let index = 0; index < loaded.length; index += 1) {`,` const extension = loaded[index];`,` if (!extension) continue;`,` const startedAt = performance.now();`,` try {`,` const factory = extension.default;`,` if (typeof factory !== 'function') throw new Error('does not export an extension factory');`,` await factory(withExtensionSource(pi, names[index]));`,` if (timings) report(index, "factory", startedAt);`,` } catch (error) {`,` report(index, "factory-error", startedAt, error);`,` }`,` }`,`}`].join(`
|
|
2
|
+
`)}function q(e){return i.default.resolve(e).split(i.default.sep).join(`/`)}function J(e){return{name:`doom-set-external`,async resolveId(n,r,a){if(n===d)return null;if(S.has(n)||n.startsWith(`node:`))return{id:n,external:!0};if(i.default.isAbsolute(n)){let t=te(n);return t&&(y.has(t)||b.has(t)||x.has(t))?(e.add(n),{id:n,external:!0}):null}if(n.startsWith(`.`))return null;let o=v(n),s=w(o);if(s&&y.has(s)){let n=t.optionalPackageEntry(o);if(n)return e.add(n),{id:n,external:!0}}if(s===`typebox`){let t=ee(o);if(t)return e.add(t),{id:t,external:!0}}if(s&&(b.has(s)||x.has(s))){let t=await this.resolve(o,r,a);if(t)return e.add(t.id),{id:t.id,external:!0}}return null}}}function Y(e){return{name:`doom-set-inputs`,load(t){return i.default.isAbsolute(t)&&a.default.existsSync(t)&&e.add(t),null}}}function X(e,t,n){t.code===`UNRESOLVED_IMPORT`&&n(`error`,t)}function Z(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Q(e,t){if(Array.isArray(e)){for(let n of e)Q(n,t);return}if(!Z(e))return;let n=e.object,r=e.property;if(e.type===`MemberExpression`&&e.computed===!1&&typeof e.start==`number`&&typeof e.end==`number`&&Z(n)&&n.type===`MetaProperty`&&Z(n.meta)&&n.meta.name===`import`&&Z(n.property)&&n.property.name===`meta`&&Z(r)&&r.name===`url`){t.push({start:e.start,end:e.end});return}for(let n of Object.values(e))Q(n,t)}function ne(){return{name:`doom-original-import-meta-url`,transform(e,t){if(!i.default.isAbsolute(t)||!e.includes(`import.meta.url`))return null;let n=i.default.extname(t).toLowerCase(),a=n===`.tsx`?`tsx`:n===`.jsx`?`jsx`:_(t)?`ts`:`js`,o=[];if(Q(this.parse(e,{lang:a}),o),o.length===0)return null;let s=JSON.stringify((0,r.pathToFileURL)(t).href),c=e;for(let e of o.sort((e,t)=>t.start-e.start))c=`${c.slice(0,e.start)}${s}${c.slice(e.end)}`;return c}}}function $(e,t,n={}){let r=G(e.map(e=>i.default.resolve(e))),a=i.default.resolve(n.outputDirectory??i.default.join(t,u)),s=C(n.outputName??`doom-set`),c=(0,o.createHash)(`sha256`).update(a).update(s).digest(`hex`).slice(0,16);return i.default.join(t,u,`${r}.${c}.json`)}async function re(e,t,r={}){if(e.length===0)throw Error(`Cannot compile an empty extension set`);let o=e.map(e=>i.default.resolve(e)),s=i.default.join(t,u);a.default.mkdirSync(s,{recursive:!0});let c=i.default.resolve(r.outputDirectory??s),f=C(r.outputName??`doom-set`);a.default.mkdirSync(c,{recursive:!0});let p=$(o,t,r),m=P(p);if(m&&M(m,o))return m.output;let h=async()=>{let e=new Set(o),t=K(o),{rolldown:n}=await import(`rolldown`),r=await n({input:d,platform:`node`,plugins:[{name:`doom-set-entry`,resolveId(e){return e===d?e:null},load(e){return e===d?t:null}},J(e),ne(),Y(e)],onLog:X});try{return{generated:await r.generate(I(`${f}.[hash:16]${l}`)),inputs:e}}finally{await r.close()}},g=r.repositoryRoot?T(r.repositoryRoot):void 0;if(g&&r.sharedCacheDirectory){let e=B(o.map(e=>O(e,g)),f),t=e=>k(e,g);return n.withSharedBuildLock(r.sharedCacheDirectory,e,async()=>{let i=n.findSharedBuild(r.sharedCacheDirectory,e,t);if(i)return W(i,c,p,o);let{generated:a,inputs:s}=await h(),l=V(s,g),u=H(a,l.byTarget);if([...u.values()].some(e=>typeof e==`string`&&e.includes(g)))throw Error(`Shared build graph still contains an absolute worktree path after tokenization`);let d=a.output.find(e=>e.type===`chunk`&&e.isEntry);if(!d)throw Error(`Rolldown produced no shared extension entry`);n.publishSharedBuild({cacheDirectory:r.sharedCacheDirectory,lookupKey:e,entry:d.fileName,inputs:l.shared,artifacts:u});let f=n.findSharedBuild(r.sharedCacheDirectory,e,t);if(!f)throw Error(`Published shared build could not be verified`);return W(f,c,p,o)})}let{generated:_,inputs:v}=await h(),y=z(_,c);return U(p,o,y.entry,y.artifacts,v),y.entry}exports.compileExtensionSet=re,exports.extensionSetKey=G,exports.extensionSetManifestPath=$;
|
|
3
3
|
//# sourceMappingURL=extensionCompiler.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extensionCompiler.cjs","names":["optionalPackageEntry","builtinModules","path","fs","contentSha256","materializeSharedBuild","withSharedBuildLock","findSharedBuild"],"sources":["../../src/adapters/extensionCompiler.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport { builtinModules, createRequire } from 'node:module';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { LogLevel, LogOrStringHandler, OutputAsset, OutputChunk, RolldownLog, RolldownOutput } from 'rolldown';\nimport { optionalPackageEntry } from '../utils/index.ts';\nimport {\n contentSha256,\n findSharedBuild,\n materializeSharedBuild,\n publishSharedBuild,\n type ResolvedSharedBuild,\n type SharedBuildInput,\n withSharedBuildLock,\n} from './sharedBuildCache.ts';\n\n/**\n * Compiles TypeScript extensions into plain ESM ahead of the session.\n *\n * Pi loads extensions through jiti, which transpiles TypeScript at import time.\n * That works, but it costs a transform per module on a cold cache and makes Node\n * complain about type stripping under node_modules on every launch. Resolution\n * already knows the full extension list, so each TypeScript graph is compiled\n * to native ESM here and Pi imports it without a runtime transform. Lazy imports\n * remain separate content-addressed chunks so their evaluation semantics survive\n * compilation.\n *\n * Mode artifacts bundle their JavaScript dependencies. Node built-ins, native\n * packages, and Pi's shared runtime stay external; those imports are rewritten\n * to absolute paths so a worktree-local artifact never relies on its\n * generated directory having its own `node_modules` tree. Immutable build objects\n * may be shared by linked worktrees, but materialized output stays worktree-local.\n */\n\nconst TYPESCRIPT_SUFFIXES = ['.ts', '.mts', '.cts'] as const;\nconst COMPILED_SUFFIX = '.mjs';\nconst CACHE_VERSION = 'v4';\nconst SET_CACHE_VERSION = 'v12';\nconst HASH_LENGTH = 16;\nconst SET_DIRECTORY = 'sets';\nconst MANIFEST_SUFFIX = '.json';\nconst VIRTUAL_SET_ENTRY = '\\0doompi-extension-set';\nconst EXTENSION_SOURCE_HELPER = '@agimon-ai/doompi-ui/extensionName';\nconst SHARED_BUILD_VERSION = 'v1-rolldown-1.0.0-rc.17';\nconst SHARED_PATH_TOKEN_PREFIX = '__DOOMPI_PATH_';\n\n/** Pi's former npm scope. The packages are the same, published under a new name. */\nconst PI_SCOPE_RENAMES: Readonly<Record<string, string>> = {\n '@mariozechner/pi-coding-agent': '@earendil-works/pi-coding-agent',\n '@mariozechner/pi-tui': '@earendil-works/pi-tui',\n '@mariozechner/pi-ai': '@earendil-works/pi-ai',\n '@sinclair/typebox': 'typebox',\n};\n\nexport function isTypeScriptEntry(entry: string): boolean {\n return TYPESCRIPT_SUFFIXES.some((suffix) => entry.endsWith(suffix));\n}\n\nfunction renameScope(specifier: string): string {\n for (const [before, after] of Object.entries(PI_SCOPE_RENAMES)) {\n if (specifier === before) return after;\n if (specifier.startsWith(`${before}/`)) return `${after}${specifier.slice(before.length)}`;\n }\n return specifier;\n}\n\n/**\n * Cache key for a compiled entry.\n *\n * The entry's own bytes are the only cheap signal that the extension changed; a\n * dependency edit is not visible without walking the graph, so an upgrade that\n * leaves the entry byte-identical is served from cache until the version below\n * is raised.\n */\nfunction cacheKey(entry: string): string {\n const source = fs.readFileSync(entry);\n return createHash('sha256').update(CACHE_VERSION).update(entry).update(source).digest('hex').slice(0, HASH_LENGTH);\n}\n\nfunction compiledName(entry: string, key: string): string {\n const base = path.basename(entry).replace(/\\.[cm]?ts$/, '');\n const parent = path.basename(path.dirname(entry));\n return `${parent}-${base}.${key}${COMPILED_SUFFIX}`;\n}\n\n/**\n * Packages that must resolve to the copy this harness runs, never to a second\n * one bundled into an extension. Pi owns the TUI and the session state, so a\n * duplicate renders into a different screen and mutates a different store.\n */\nconst HOSTED_PACKAGES = new Set(['@earendil-works/pi-coding-agent', '@earendil-works/pi-tui', '@earendil-works/pi-ai']);\n\n/**\n * Resolves packages Pi exposes to extensions as compatibility aliases.\n *\n * Extensions do not need to declare TypeBox themselves because Pi's loader\n * provides it as a virtual module. A dist bundle bypasses that loader, so it\n * resolves the same package against Pi's dependency tree and bundles it.\n */\nfunction piExtensionDependencyEntry(specifier: string): string | undefined {\n const piEntry = optionalPackageEntry('@earendil-works/pi-coding-agent');\n if (!piEntry) return undefined;\n try {\n return createRequire(piEntry).resolve(specifier);\n } catch {\n return undefined;\n }\n}\n\n/** Packages whose native binaries or runtime discovery cannot be rolled up. */\nconst NATIVE_PACKAGES = new Set([\n '@parcel/watcher',\n '@napi-rs/keyring',\n 'better-sqlite3',\n 'canvas',\n 'fsevents',\n 'isolated-vm',\n 'keytar',\n 'node-pty',\n 'onnxruntime-node',\n 'playwright',\n 'playwright-core',\n 'puppeteer',\n 'sharp',\n // TypeScript's Node system adapter reads CommonJS globals such as\n // __filename. Rolldown does not provide those globals when it folds the\n // adapter into the aggregate ESM extension, so keep the installed runtime\n // intact and import it by its resolved absolute path.\n 'typescript',\n]);\n\n/**\n * Dependency-heavy ESM runtimes that should retain their installed graph.\n *\n * Folding these packages into the aggregate creates dozens of generated chunks\n * that Node must parse and link before any extension factory can register. Node\n * can load the installed graph directly while still sharing its module cache.\n */\nconst STARTUP_EXTERNAL_PACKAGES = new Set([\n '@agimon-ai/foundation-port-registry',\n '@agimon-ai/foundation-process-registry',\n '@agimon-ai/log-sink-mcp',\n '@modelcontextprotocol/sdk',\n '@narumitw/pi-tui-kit',\n '@rmux/sdk',\n '@tursodatabase/database',\n '@tursodatabase/database-common',\n 'ajv',\n 'ajv-formats',\n 'gpt-tokenizer',\n 'highlight.js',\n 'hono',\n 'inversify',\n // open's CommonJS runtime resolves its bundled xdg-open helper through\n // __dirname, which is unavailable when Rolldown folds it into native ESM.\n 'open',\n 'reflect-metadata',\n 'zod-to-json-schema',\n 'yaml',\n 'zod',\n]);\nconst NODE_BUILTINS = new Set(builtinModules.flatMap((name) => [name, `node:${name}`]));\n\ninterface InputFingerprint {\n path: string;\n size: number;\n mtimeMs: number;\n}\n\ninterface CompiledExtensionManifest {\n version: string;\n output: string;\n artifacts: string[];\n}\n\ninterface ExtensionSetManifest extends CompiledExtensionManifest {\n entries: string[];\n inputs: InputFingerprint[];\n}\n\nexport interface CompileExtensionSetOptions {\n /** Persistent worktree artifact directory. Compiler manifests remain local. */\n outputDirectory?: string;\n /** Human-readable artifact prefix, normally the major-mode name. */\n outputName?: string;\n /** Canonical worktree root used to create relocatable logical input paths. */\n repositoryRoot?: string;\n /** Repository-level immutable cache shared by linked worktrees. */\n sharedCacheDirectory?: string;\n}\n\nfunction safeOutputName(value: string): string {\n const normalized = value\n .trim()\n .replace(/[^a-zA-Z0-9._-]+/g, '-')\n .replace(/^[-.]+|[-.]+$/g, '');\n return normalized || 'doom-set';\n}\n\nfunction packageRootOf(specifier: string): string | undefined {\n const slash = specifier.startsWith('@') ? specifier.indexOf('/', specifier.indexOf('/') + 1) : specifier.indexOf('/');\n return slash === -1 ? specifier : specifier.slice(0, slash);\n}\n\nfunction packageRootFromPath(target: string): string | undefined {\n const normalized = target.split(path.sep).join('/');\n const marker = '/node_modules/';\n const offset = normalized.lastIndexOf(marker);\n if (offset === -1) return undefined;\n const parts = normalized.slice(offset + marker.length).split('/');\n // pnpm's first segment is a store key; the second node_modules marker names\n // the package that owns the file.\n const nested = parts.lastIndexOf('node_modules');\n const packageParts = nested === -1 ? parts : parts.slice(nested + 1);\n return packageParts[0]?.startsWith('@') ? packageParts.slice(0, 2).join('/') : packageParts[0];\n}\n\ninterface LogicalInput {\n logicalPath: string;\n target: string;\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync.native(absolute);\n } catch {\n return absolute;\n }\n}\n\nfunction isInside(directory: string, target: string): boolean {\n const relative = path.relative(path.resolve(directory), path.resolve(target));\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\nfunction packageLogicalPath(target: string): string | undefined {\n const absolute = path.resolve(target);\n const packageName = packageRootFromPath(absolute);\n if (packageName) {\n const normalized = absolute.split(path.sep).join('/');\n const marker = `/node_modules/${packageName}/`;\n const offset = normalized.lastIndexOf(marker);\n if (offset !== -1) {\n return `package/${encodeURIComponent(packageName)}/${normalized.slice(offset + marker.length)}`;\n }\n }\n\n let directory = path.dirname(absolute);\n while (true) {\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(directory, 'package.json'), 'utf8')) as unknown;\n if (isRecord(manifest) && typeof manifest.name === 'string') {\n const relative = path.relative(directory, absolute).split(path.sep).join('/');\n return `package/${encodeURIComponent(manifest.name)}/${relative}`;\n }\n } catch {\n // Keep walking until a package boundary is found.\n }\n const parent = path.dirname(directory);\n if (parent === directory) return undefined;\n directory = parent;\n }\n}\n\nfunction logicalInput(target: string, repositoryRoot: string): LogicalInput | undefined {\n const absolute = canonicalPath(target);\n if (isInside(repositoryRoot, absolute)) {\n return {\n logicalPath: `repo/${path.relative(repositoryRoot, absolute).split(path.sep).join('/')}`,\n target: absolute,\n };\n }\n const packagePath = packageLogicalPath(absolute);\n return packagePath ? { logicalPath: packagePath, target: absolute } : undefined;\n}\n\nfunction findPackageDirectory(packageName: string, repositoryRoot: string): string | undefined {\n const candidates = [optionalPackageEntry(packageName)];\n try {\n candidates.push(createRequire(path.join(repositoryRoot, 'package.json')).resolve(packageName));\n } catch {\n // The package may only be visible through DoomPi's own dependency tree.\n }\n for (const candidate of candidates) {\n if (!candidate) continue;\n let directory = path.dirname(candidate);\n while (true) {\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(directory, 'package.json'), 'utf8')) as unknown;\n if (isRecord(manifest) && manifest.name === packageName) return directory;\n } catch {\n // Keep walking to the package root.\n }\n const parent = path.dirname(directory);\n if (parent === directory) break;\n directory = parent;\n }\n }\n return undefined;\n}\n\nfunction resolveLogicalInputPath(logicalPath: string, repositoryRoot: string): string | undefined {\n if (logicalPath.startsWith('repo/')) {\n const target = path.resolve(repositoryRoot, logicalPath.slice('repo/'.length));\n return isInside(repositoryRoot, target) ? target : undefined;\n }\n if (!logicalPath.startsWith('package/')) return undefined;\n const remainder = logicalPath.slice('package/'.length);\n const separator = remainder.indexOf('/');\n if (separator === -1) return undefined;\n const packageName = decodeURIComponent(remainder.slice(0, separator));\n const packageDirectory = findPackageDirectory(packageName, repositoryRoot);\n if (!packageDirectory) return undefined;\n const target = path.resolve(packageDirectory, remainder.slice(separator + 1));\n return isInside(packageDirectory, target) ? target : undefined;\n}\n\nfunction externalResolver() {\n return {\n name: 'doom-external-absolute',\n async resolveId(\n this: { resolve: (id: string, importer?: string, options?: unknown) => Promise<{ id: string } | null> },\n specifier: string,\n importer: string | undefined,\n options: unknown,\n ): Promise<{ id: string; external: boolean } | null> {\n if (specifier.startsWith('.') || path.isAbsolute(specifier) || specifier.startsWith('node:')) return null;\n const renamed = renameScope(specifier);\n\n // Host packages are resolved from this module, the way the rest of the\n // harness resolves them, because an extension written against Pi's former\n // scope does not have the current one in its own dependency tree.\n const root = packageRootOf(renamed);\n if (root && HOSTED_PACKAGES.has(root)) {\n const hosted = optionalPackageEntry(renamed);\n if (hosted) return { id: hosted, external: true };\n }\n\n // Rolldown's resolver rather than `createRequire`, which cannot see an\n // ESM-only package that publishes no `require` condition and would send\n // Pi's own core into the bundle.\n const resolved = await this.resolve(renamed, importer, options);\n if (!resolved) return null;\n // A dependency that is itself TypeScript cannot be handed to Node either,\n // so it is bundled rather than externalised.\n if (isTypeScriptEntry(resolved.id)) return null;\n return { id: resolved.id, external: true };\n },\n };\n}\n\nfunction fingerprint(target: string): InputFingerprint | undefined {\n try {\n const stat = fs.statSync(target);\n if (!stat.isFile()) return undefined;\n return { path: target, size: stat.size, mtimeMs: stat.mtimeMs };\n } catch {\n return undefined;\n }\n}\n\nfunction artifactsAreFresh(manifest: CompiledExtensionManifest, version: string, output: string): boolean {\n return (\n manifest.version === version &&\n manifest.output === output &&\n fs.existsSync(output) &&\n manifest.artifacts.every((artifact) => fs.existsSync(artifact))\n );\n}\n\nfunction manifestIsFresh(manifest: ExtensionSetManifest, entries: string[]): boolean {\n if (!artifactsAreFresh(manifest, SET_CACHE_VERSION, manifest.output)) return false;\n if (JSON.stringify(manifest.entries) !== JSON.stringify(entries)) {\n return false;\n }\n return manifest.inputs.every((previous) => {\n const current = fingerprint(previous.path);\n return current?.size === previous.size && current.mtimeMs === previous.mtimeMs;\n });\n}\n\nfunction readCompiledExtensionManifest(manifestPath: string): CompiledExtensionManifest | undefined {\n try {\n const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as CompiledExtensionManifest;\n return parsed && typeof parsed.output === 'string' && Array.isArray(parsed.artifacts) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction readSetManifest(manifestPath: string): ExtensionSetManifest | undefined {\n const parsed = readCompiledExtensionManifest(manifestPath) as ExtensionSetManifest | undefined;\n return parsed && Array.isArray(parsed.entries) && Array.isArray(parsed.inputs) ? parsed : undefined;\n}\n\nfunction writeAtomic(target: string, contents: string | Uint8Array): void {\n fs.mkdirSync(path.dirname(target), { recursive: true });\n const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;\n fs.writeFileSync(temporary, contents);\n try {\n fs.renameSync(temporary, target);\n } catch (error) {\n fs.rmSync(temporary, { force: true });\n if (!fs.existsSync(target)) throw error;\n }\n}\n\ninterface CompiledGraph {\n entry: string;\n artifacts: string[];\n}\n\nfunction splitOutputOptions(entryFileNames: string) {\n return {\n format: 'esm' as const,\n sourcemap: false,\n minify: true,\n codeSplitting: true,\n hashCharacters: 'hex' as const,\n entryFileNames,\n chunkFileNames: `chunks/[name].[hash:${HASH_LENGTH}]${COMPILED_SUFFIX}`,\n assetFileNames: `assets/[name].[hash:${HASH_LENGTH}][extname]`,\n };\n}\n\nfunction artifactTarget(outputDirectory: string, artifact: OutputAsset | OutputChunk): string {\n const target = path.resolve(outputDirectory, artifact.fileName);\n const relative = path.relative(outputDirectory, target);\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n throw new Error(`Rolldown emitted an artifact outside the output directory: ${artifact.fileName}`);\n }\n return target;\n}\n\nfunction artifactMatches(target: string, contents: string | Uint8Array): boolean {\n try {\n const current = fs.readFileSync(target);\n const generated = typeof contents === 'string' ? Buffer.from(contents) : Buffer.from(contents);\n return current.equals(generated);\n } catch {\n return false;\n }\n}\n\n/** Writes referenced chunks first so a visible entry always has a complete graph. */\nfunction writeCompiledGraph(generated: RolldownOutput, outputDirectory: string): CompiledGraph {\n const entry = generated.output.find((artifact) => artifact.type === 'chunk' && artifact.isEntry);\n if (!entry || entry.type !== 'chunk') throw new Error('Rolldown produced no extension entry');\n const root = path.resolve(outputDirectory);\n const remaining = generated.output.filter((artifact) => artifact !== entry);\n for (const artifact of [...remaining, entry]) {\n const contents = artifact.type === 'chunk' ? artifact.code : artifact.source;\n const target = artifactTarget(root, artifact);\n if (!artifactMatches(target, contents)) writeAtomic(target, contents);\n }\n return {\n entry: artifactTarget(root, entry),\n artifacts: generated.output.map((artifact) => artifactTarget(root, artifact)).sort(),\n };\n}\n\nfunction sharedLookupKey(entries: readonly LogicalInput[], outputName: string): string {\n return createHash('sha256')\n .update(SHARED_BUILD_VERSION)\n .update(process.version)\n .update(process.platform)\n .update(process.arch)\n .update(outputName)\n .update(JSON.stringify(entries.map((entry) => entry.logicalPath)))\n .digest('hex');\n}\n\ninterface TokenizedInputs {\n shared: SharedBuildInput[];\n byTarget: Map<string, string>;\n}\n\nfunction tokenizedInputs(inputs: ReadonlySet<string>, repositoryRoot: string): TokenizedInputs {\n const logical: LogicalInput[] = [];\n for (const target of inputs) {\n const input = logicalInput(target, repositoryRoot);\n if (!input) throw new Error(`Shared build input has no relocatable identity: ${target}`);\n logical.push(input);\n }\n logical.sort((left, right) => left.logicalPath.localeCompare(right.logicalPath));\n const shared: SharedBuildInput[] = [{ logicalPath: 'repo/', sha256: '', token: `${SHARED_PATH_TOKEN_PREFIX}ROOT__` }];\n const byTarget = new Map<string, string>([[repositoryRoot, `${SHARED_PATH_TOKEN_PREFIX}ROOT__`]]);\n const tokenByLogicalPath = new Map<string, string>();\n logical.forEach((input, index) => {\n const existing = tokenByLogicalPath.get(input.logicalPath);\n const token = existing ?? `${SHARED_PATH_TOKEN_PREFIX}${String(index)}__`;\n if (!existing) {\n shared.push({ logicalPath: input.logicalPath, sha256: contentSha256(fs.readFileSync(input.target)), token });\n tokenByLogicalPath.set(input.logicalPath, token);\n }\n byTarget.set(path.resolve(input.target), token);\n });\n for (const target of inputs) {\n const input = logicalInput(target, repositoryRoot);\n const token = input ? tokenByLogicalPath.get(input.logicalPath) : undefined;\n if (token) byTarget.set(path.resolve(target), token);\n }\n return { shared, byTarget };\n}\n\nfunction tokenizedArtifactContents(\n generated: RolldownOutput,\n tokens: ReadonlyMap<string, string>,\n): Map<string, string | Uint8Array> {\n const artifacts = new Map<string, string | Uint8Array>();\n const replacements = [...tokens.entries()].sort(([left], [right]) => right.length - left.length);\n for (const artifact of generated.output) {\n if (artifact.type === 'asset') {\n artifacts.set(artifact.fileName, typeof artifact.source === 'string' ? artifact.source : artifact.source);\n continue;\n }\n let source = artifact.code;\n for (const [target, token] of replacements) {\n source = source\n .replaceAll(pathToFileURL(target).href, `${token}:url`)\n .replaceAll(pathToImportSpecifier(target), token)\n .replaceAll(target, token);\n }\n artifacts.set(artifact.fileName, source);\n }\n return artifacts;\n}\n\nfunction writeLocalSetManifest(\n manifestPath: string,\n entries: string[],\n output: string,\n artifacts: string[],\n inputs: ReadonlySet<string>,\n): void {\n const inputFingerprints = [...inputs]\n .map(fingerprint)\n .filter((value): value is InputFingerprint => value !== undefined)\n .sort((left, right) => left.path.localeCompare(right.path));\n writeAtomic(\n manifestPath,\n `${JSON.stringify(\n { version: SET_CACHE_VERSION, entries, output, artifacts: [...artifacts].sort(), inputs: inputFingerprints },\n null,\n 2,\n )}\\n`,\n );\n}\n\nfunction materializeSharedSet(\n build: ResolvedSharedBuild,\n outputDirectory: string,\n manifestPath: string,\n entries: string[],\n): string {\n const output = materializeSharedBuild(build, outputDirectory);\n const inputs = new Set(build.replacements.values());\n const artifacts = build.manifest.artifacts.map((artifact) => path.join(outputDirectory, artifact.path));\n writeLocalSetManifest(manifestPath, entries, output, artifacts, inputs);\n return output;\n}\n\n/** Stable lookup key for one exact Pi load order. */\nexport function extensionSetKey(entries: readonly string[]): string {\n return createHash('sha256')\n .update(SET_CACHE_VERSION)\n .update(JSON.stringify(entries.map((entry) => path.resolve(entry))))\n .digest('hex')\n .slice(0, HASH_LENGTH);\n}\n\nfunction extensionSetSource(entries: readonly string[]): string {\n const helper = optionalPackageEntry(EXTENSION_SOURCE_HELPER);\n if (!helper) throw new Error(`Cannot resolve ${EXTENSION_SOURCE_HELPER}`);\n const loaders = entries.map((entry) => `() => import(${JSON.stringify(pathToImportSpecifier(entry))})`);\n const names = entries.map((entry) => JSON.stringify(entry));\n return [\n `import { withExtensionSource } from ${JSON.stringify(pathToImportSpecifier(helper))};`,\n `const loaders = [${loaders.join(',')}];`,\n `const names = [${names.join(',')}];`,\n 'function report(index, phase, startedAt, error) {',\n ' const elapsed = (performance.now() - startedAt).toFixed(1);',\n ' const detail = error instanceof Error ? \": \" + error.message : error === undefined ? \"\" : \": \" + String(error);',\n ' process.stderr.write(\"[doompi:timing] \" + phase + \" \" + elapsed + \"ms \" + names[index] + detail + \"\\\\n\");',\n '}',\n 'export default async function doompiExtensionSet(pi) {',\n ' const timings = process.env.DOOMPI_TIMING === \"1\" || process.env.PI_TIMING === \"1\";',\n ' const loaded = await Promise.all(loaders.map(async (load, index) => {',\n ' const startedAt = performance.now();',\n ' try {',\n ' const extension = await load();',\n ' if (timings) report(index, \"import\", startedAt);',\n ' return extension;',\n ' } catch (error) {',\n ' report(index, \"import-error\", startedAt, error);',\n ' return undefined;',\n ' }',\n ' }));',\n ' for (let index = 0; index < loaded.length; index += 1) {',\n ' const extension = loaded[index];',\n ' if (!extension) continue;',\n ' const startedAt = performance.now();',\n ' try {',\n ' const factory = extension.default;',\n \" if (typeof factory !== 'function') throw new Error('does not export an extension factory');\",\n ' await factory(withExtensionSource(pi, names[index]));',\n ' if (timings) report(index, \"factory\", startedAt);',\n ' } catch (error) {',\n ' report(index, \"factory-error\", startedAt, error);',\n ' }',\n ' }',\n '}',\n ].join('\\n');\n}\n\nfunction pathToImportSpecifier(target: string): string {\n // Rolldown accepts absolute POSIX paths directly. Windows needs a file URL,\n // but replacing separators is enough for its platform-independent parser.\n return path.resolve(target).split(path.sep).join('/');\n}\n\nfunction setExternalResolver(inputs: Set<string>) {\n return {\n name: 'doom-set-external',\n async resolveId(\n this: { resolve: (id: string, importer?: string, options?: unknown) => Promise<{ id: string } | null> },\n specifier: string,\n importer: string | undefined,\n options: unknown,\n ): Promise<{ id: string; external: boolean } | null> {\n if (specifier === VIRTUAL_SET_ENTRY) return null;\n if (NODE_BUILTINS.has(specifier) || specifier.startsWith('node:')) {\n return { id: specifier, external: true };\n }\n\n if (path.isAbsolute(specifier)) {\n const root = packageRootFromPath(specifier);\n if (root && (HOSTED_PACKAGES.has(root) || NATIVE_PACKAGES.has(root) || STARTUP_EXTERNAL_PACKAGES.has(root))) {\n inputs.add(specifier);\n return { id: specifier, external: true };\n }\n return null;\n }\n if (specifier.startsWith('.')) return null;\n\n const renamed = renameScope(specifier);\n const root = packageRootOf(renamed);\n if (root && HOSTED_PACKAGES.has(root)) {\n const hosted = optionalPackageEntry(renamed);\n if (hosted) {\n inputs.add(hosted);\n return { id: hosted, external: true };\n }\n }\n if (root === 'typebox') {\n const typebox = piExtensionDependencyEntry(renamed);\n if (typebox) {\n inputs.add(typebox);\n return { id: typebox, external: true };\n }\n }\n if (root && (NATIVE_PACKAGES.has(root) || STARTUP_EXTERNAL_PACKAGES.has(root))) {\n const resolved = await this.resolve(renamed, importer, options);\n if (resolved) {\n inputs.add(resolved.id);\n return { id: resolved.id, external: true };\n }\n }\n return null;\n },\n };\n}\n\nfunction inputCollector(inputs: Set<string>) {\n return {\n name: 'doom-set-inputs',\n load(id: string): null {\n if (path.isAbsolute(id) && fs.existsSync(id)) inputs.add(id);\n return null;\n },\n };\n}\n\n/** An unresolved bare import would otherwise become a broken dist external. */\nfunction failUnresolvedImport(_level: LogLevel, log: RolldownLog, handler: LogOrStringHandler): void {\n if (log.code === 'UNRESOLVED_IMPORT') handler('error', log);\n}\n\ninterface SourceRange {\n start: number;\n end: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction collectImportMetaUrlRanges(value: unknown, ranges: SourceRange[]): void {\n if (Array.isArray(value)) {\n for (const item of value) collectImportMetaUrlRanges(item, ranges);\n return;\n }\n if (!isRecord(value)) return;\n\n const object = value.object;\n const property = value.property;\n if (\n value.type === 'MemberExpression' &&\n value.computed === false &&\n typeof value.start === 'number' &&\n typeof value.end === 'number' &&\n isRecord(object) &&\n object.type === 'MetaProperty' &&\n isRecord(object.meta) &&\n object.meta.name === 'import' &&\n isRecord(object.property) &&\n object.property.name === 'meta' &&\n isRecord(property) &&\n property.name === 'url'\n ) {\n ranges.push({ start: value.start, end: value.end });\n return;\n }\n\n for (const child of Object.values(value)) collectImportMetaUrlRanges(child, ranges);\n}\n\n/** Keeps package-owned resource lookup anchored to each original source file. */\nfunction preserveImportMetaUrl() {\n return {\n name: 'doom-original-import-meta-url',\n transform(\n this: {\n parse: (source: string, options?: { lang?: 'js' | 'jsx' | 'ts' | 'tsx' }) => unknown;\n },\n source: string,\n id: string,\n ): string | null {\n if (!path.isAbsolute(id) || !source.includes('import.meta.url')) return null;\n const extension = path.extname(id).toLowerCase();\n const lang = extension === '.tsx' ? 'tsx' : extension === '.jsx' ? 'jsx' : isTypeScriptEntry(id) ? 'ts' : 'js';\n const ranges: SourceRange[] = [];\n collectImportMetaUrlRanges(this.parse(source, { lang }), ranges);\n if (ranges.length === 0) return null;\n const replacement = JSON.stringify(pathToFileURL(id).href);\n let transformed = source;\n for (const range of ranges.sort((left, right) => right.start - left.start)) {\n transformed = `${transformed.slice(0, range.start)}${replacement}${transformed.slice(range.end)}`;\n }\n return transformed;\n },\n };\n}\n\n/** Location of the lightweight input-fingerprint record for one extension set. */\nexport function extensionSetManifestPath(\n entries: readonly string[],\n cacheDirectory: string,\n options: CompileExtensionSetOptions = {},\n): string {\n const normalized = entries.map((entry) => path.resolve(entry));\n const key = extensionSetKey(normalized);\n const outputDirectory = path.resolve(options.outputDirectory ?? path.join(cacheDirectory, SET_DIRECTORY));\n const outputName = safeOutputName(options.outputName ?? 'doom-set');\n // One graph can be emitted under several named mode artifacts. Keep their\n // cache records separate without changing extensionSetKey, which is the\n // runtime lookup key for an exact activation order.\n const outputKey = createHash('sha256').update(outputDirectory).update(outputName).digest('hex').slice(0, HASH_LENGTH);\n return path.join(cacheDirectory, SET_DIRECTORY, `${key}.${outputKey}${MANIFEST_SUFFIX}`);\n}\n\n/**\n * Compiles an ordered extension set into one native ESM entry.\n *\n * The generated entry starts every module load together, then invokes factories\n * sequentially in activation order. Its manifest records every loaded module,\n * so a dependency edit invalidates the cache without running the compiler.\n */\nexport async function compileExtensionSet(\n entries: readonly string[],\n cacheDirectory: string,\n options: CompileExtensionSetOptions = {},\n): Promise<string> {\n if (entries.length === 0) throw new Error('Cannot compile an empty extension set');\n const normalized = entries.map((entry) => path.resolve(entry));\n const setDirectory = path.join(cacheDirectory, SET_DIRECTORY);\n fs.mkdirSync(setDirectory, { recursive: true });\n const outputDirectory = path.resolve(options.outputDirectory ?? setDirectory);\n const outputName = safeOutputName(options.outputName ?? 'doom-set');\n fs.mkdirSync(outputDirectory, { recursive: true });\n const manifestPath = extensionSetManifestPath(normalized, cacheDirectory, options);\n const previous = readSetManifest(manifestPath);\n if (previous && manifestIsFresh(previous, normalized)) return previous.output;\n\n const generate = async (): Promise<{ generated: RolldownOutput; inputs: Set<string> }> => {\n const inputs = new Set(normalized);\n const source = extensionSetSource(normalized);\n const { rolldown } = await import('rolldown');\n const build = await rolldown({\n input: VIRTUAL_SET_ENTRY,\n platform: 'node',\n plugins: [\n {\n name: 'doom-set-entry',\n resolveId(id: string): string | null {\n return id === VIRTUAL_SET_ENTRY ? id : null;\n },\n load(id: string): string | null {\n return id === VIRTUAL_SET_ENTRY ? source : null;\n },\n },\n setExternalResolver(inputs),\n preserveImportMetaUrl(),\n inputCollector(inputs),\n ],\n onLog: failUnresolvedImport,\n });\n try {\n return {\n generated: await build.generate(splitOutputOptions(`${outputName}.[hash:${HASH_LENGTH}]${COMPILED_SUFFIX}`)),\n inputs,\n };\n } finally {\n await build.close();\n }\n };\n\n const repositoryRoot = options.repositoryRoot ? canonicalPath(options.repositoryRoot) : undefined;\n const logicalEntries = repositoryRoot ? normalized.map((entry) => logicalInput(entry, repositoryRoot)) : [];\n const canShare =\n repositoryRoot !== undefined &&\n options.sharedCacheDirectory !== undefined &&\n logicalEntries.length === normalized.length &&\n logicalEntries.every((entry): entry is LogicalInput => entry !== undefined);\n\n if (canShare && repositoryRoot && options.sharedCacheDirectory) {\n const lookupKey = sharedLookupKey(logicalEntries, outputName);\n const resolveInput = (logicalPath: string): string | undefined =>\n resolveLogicalInputPath(logicalPath, repositoryRoot);\n return withSharedBuildLock(options.sharedCacheDirectory, lookupKey, async () => {\n const cached = findSharedBuild(options.sharedCacheDirectory as string, lookupKey, resolveInput);\n if (cached) return materializeSharedSet(cached, outputDirectory, manifestPath, normalized);\n\n const { generated, inputs } = await generate();\n const tokenized = tokenizedInputs(inputs, repositoryRoot);\n const artifacts = tokenizedArtifactContents(generated, tokenized.byTarget);\n if (\n [...artifacts.values()].some((contents) => typeof contents === 'string' && contents.includes(repositoryRoot))\n ) {\n throw new Error('Shared build graph still contains an absolute worktree path after tokenization');\n }\n const entry = generated.output.find((artifact) => artifact.type === 'chunk' && artifact.isEntry);\n if (!entry) throw new Error('Rolldown produced no shared extension entry');\n publishSharedBuild({\n cacheDirectory: options.sharedCacheDirectory as string,\n lookupKey,\n entry: entry.fileName,\n inputs: tokenized.shared,\n artifacts,\n });\n const published = findSharedBuild(options.sharedCacheDirectory as string, lookupKey, resolveInput);\n if (!published) throw new Error('Published shared build could not be verified');\n return materializeSharedSet(published, outputDirectory, manifestPath, normalized);\n });\n }\n\n const { generated, inputs } = await generate();\n const compiled = writeCompiledGraph(generated, outputDirectory);\n writeLocalSetManifest(manifestPath, normalized, compiled.entry, compiled.artifacts, inputs);\n return compiled.entry;\n}\n\n/**\n * Bundles `entry` into `cacheDirectory` and returns the compiled path.\n *\n * Non-TypeScript entries are returned untouched. A cache hit skips the build.\n */\nexport async function compileExtension(entry: string, cacheDirectory: string): Promise<string> {\n if (!isTypeScriptEntry(entry)) return entry;\n\n fs.mkdirSync(cacheDirectory, { recursive: true });\n const outputName = compiledName(entry, cacheKey(entry));\n const output = path.join(cacheDirectory, outputName);\n const manifestPath = `${output}${MANIFEST_SUFFIX}`;\n const previous = readCompiledExtensionManifest(manifestPath);\n if (previous && artifactsAreFresh(previous, CACHE_VERSION, output)) return output;\n\n // Rolldown loads a native module and a sizeable parser graph. Sync is the\n // only command that needs it, and a cache hit does not, so keep it off every\n // other CLI startup path.\n const { rolldown } = await import('rolldown');\n const build = await rolldown({\n input: entry,\n platform: 'node',\n plugins: [externalResolver(), preserveImportMetaUrl()],\n onLog: failUnresolvedImport,\n });\n let compiled: CompiledGraph;\n try {\n const generated = await build.generate(splitOutputOptions(outputName));\n compiled = writeCompiledGraph(generated, cacheDirectory);\n } finally {\n await build.close();\n }\n writeAtomic(\n manifestPath,\n `${JSON.stringify({ version: CACHE_VERSION, output, artifacts: compiled.artifacts }, null, 2)}\\n`,\n );\n return output;\n}\n\n/**\n * Compiles every TypeScript entry in a resolution map, leaving the rest as-is.\n *\n * A failure is not fatal: the original path is kept so Pi falls back to loading\n * the TypeScript through jiti, which is what it did before this existed.\n */\nexport async function compileResolvedEntries(\n resolved: Record<string, string>,\n cacheDirectory: string,\n): Promise<Record<string, string>> {\n fs.mkdirSync(cacheDirectory, { recursive: true });\n const compiled: Record<string, string> = {};\n for (const [key, target] of Object.entries(resolved)) {\n try {\n compiled[key] = await compileExtension(target, cacheDirectory);\n } catch {\n compiled[key] = target;\n }\n }\n return compiled;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extensionCompiler.cjs","names":["optionalPackageEntry","builtinModules","path","fs","contentSha256","materializeSharedBuild","withSharedBuildLock","findSharedBuild"],"sources":["../../src/adapters/extensionCompiler.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport { builtinModules, createRequire } from 'node:module';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { LogLevel, LogOrStringHandler, OutputAsset, OutputChunk, RolldownLog, RolldownOutput } from 'rolldown';\nimport { optionalPackageEntry } from '../utils/index.ts';\nimport {\n contentSha256,\n findSharedBuild,\n materializeSharedBuild,\n publishSharedBuild,\n type ResolvedSharedBuild,\n type SharedBuildInput,\n withSharedBuildLock,\n} from './sharedBuildCache.ts';\n\n/**\n * Compiles TypeScript extensions into plain ESM ahead of the session.\n *\n * Pi loads extensions through jiti, which transpiles TypeScript at import time.\n * That works, but it costs a transform per module on a cold cache and makes Node\n * complain about type stripping under node_modules on every launch. Resolution\n * already knows the full extension list, so each TypeScript graph is compiled\n * to native ESM here and Pi imports it without a runtime transform. Lazy imports\n * remain separate content-addressed chunks so their evaluation semantics survive\n * compilation.\n *\n * Mode artifacts bundle their JavaScript dependencies. Node built-ins, native\n * packages, and Pi's shared runtime stay external; those imports are rewritten\n * to absolute paths so a worktree-local artifact never relies on its\n * generated directory having its own `node_modules` tree. Immutable build objects\n * may be shared by linked worktrees, but materialized output stays worktree-local.\n */\n\nconst TYPESCRIPT_SUFFIXES = ['.ts', '.mts', '.cts'] as const;\nconst COMPILED_SUFFIX = '.mjs';\nconst CACHE_VERSION = 'v4';\nconst SET_CACHE_VERSION = 'v12';\nconst HASH_LENGTH = 16;\nconst SET_DIRECTORY = 'sets';\nconst MANIFEST_SUFFIX = '.json';\nconst VIRTUAL_SET_ENTRY = '\\0doompi-extension-set';\nconst EXTENSION_SOURCE_HELPER = '@agimon-ai/doompi-ui/extensionName';\nconst SHARED_BUILD_VERSION = 'v3-rolldown-1.0.0-rc.17';\nconst SHARED_PATH_TOKEN_PREFIX = '__DOOMPI_PATH_';\nconst REPOSITORY_INPUT_PREFIX = 'repo/';\nconst EXTERNAL_INPUT_PREFIX = 'external/';\n\n/** Pi's former npm scope. The packages are the same, published under a new name. */\nconst PI_SCOPE_RENAMES: Readonly<Record<string, string>> = {\n '@mariozechner/pi-coding-agent': '@earendil-works/pi-coding-agent',\n '@mariozechner/pi-tui': '@earendil-works/pi-tui',\n '@mariozechner/pi-ai': '@earendil-works/pi-ai',\n '@sinclair/typebox': 'typebox',\n};\n\nexport function isTypeScriptEntry(entry: string): boolean {\n return TYPESCRIPT_SUFFIXES.some((suffix) => entry.endsWith(suffix));\n}\n\nfunction renameScope(specifier: string): string {\n for (const [before, after] of Object.entries(PI_SCOPE_RENAMES)) {\n if (specifier === before) return after;\n if (specifier.startsWith(`${before}/`)) return `${after}${specifier.slice(before.length)}`;\n }\n return specifier;\n}\n\n/**\n * Cache key for a compiled entry.\n *\n * The entry's own bytes are the only cheap signal that the extension changed; a\n * dependency edit is not visible without walking the graph, so an upgrade that\n * leaves the entry byte-identical is served from cache until the version below\n * is raised.\n */\nfunction cacheKey(entry: string): string {\n const source = fs.readFileSync(entry);\n return createHash('sha256').update(CACHE_VERSION).update(entry).update(source).digest('hex').slice(0, HASH_LENGTH);\n}\n\nfunction compiledName(entry: string, key: string): string {\n const base = path.basename(entry).replace(/\\.[cm]?ts$/, '');\n const parent = path.basename(path.dirname(entry));\n return `${parent}-${base}.${key}${COMPILED_SUFFIX}`;\n}\n\n/**\n * Packages that must resolve to the copy this harness runs, never to a second\n * one bundled into an extension. Pi owns the TUI and the session state, so a\n * duplicate renders into a different screen and mutates a different store.\n */\nconst HOSTED_PACKAGES = new Set(['@earendil-works/pi-coding-agent', '@earendil-works/pi-tui', '@earendil-works/pi-ai']);\n\n/**\n * Resolves packages Pi exposes to extensions as compatibility aliases.\n *\n * Extensions do not need to declare TypeBox themselves because Pi's loader\n * provides it as a virtual module. A dist bundle bypasses that loader, so it\n * resolves the same package against Pi's dependency tree and bundles it.\n */\nfunction piExtensionDependencyEntry(specifier: string): string | undefined {\n const piEntry = optionalPackageEntry('@earendil-works/pi-coding-agent');\n if (!piEntry) return undefined;\n try {\n return createRequire(piEntry).resolve(specifier);\n } catch {\n return undefined;\n }\n}\n\n/** Packages whose native binaries or runtime discovery cannot be rolled up. */\nconst NATIVE_PACKAGES = new Set([\n '@parcel/watcher',\n '@napi-rs/keyring',\n 'better-sqlite3',\n 'canvas',\n 'fsevents',\n 'isolated-vm',\n 'keytar',\n 'node-pty',\n 'onnxruntime-node',\n 'playwright',\n 'playwright-core',\n 'puppeteer',\n 'sharp',\n // TypeScript's Node system adapter reads CommonJS globals such as\n // __filename. Rolldown does not provide those globals when it folds the\n // adapter into the aggregate ESM extension, so keep the installed runtime\n // intact and import it by its resolved absolute path.\n 'typescript',\n]);\n\n/**\n * Dependency-heavy ESM runtimes that should retain their installed graph.\n *\n * Folding these packages into the aggregate creates dozens of generated chunks\n * that Node must parse and link before any extension factory can register. Node\n * can load the installed graph directly while still sharing its module cache.\n */\nconst STARTUP_EXTERNAL_PACKAGES = new Set([\n '@agimon-ai/foundation-port-registry',\n '@agimon-ai/foundation-process-registry',\n '@agimon-ai/log-sink-mcp',\n '@modelcontextprotocol/sdk',\n '@narumitw/pi-tui-kit',\n '@rmux/sdk',\n '@tursodatabase/database',\n '@tursodatabase/database-common',\n 'ajv',\n 'ajv-formats',\n 'gpt-tokenizer',\n 'highlight.js',\n 'hono',\n 'inversify',\n // open's CommonJS runtime resolves its bundled xdg-open helper through\n // __dirname, which is unavailable when Rolldown folds it into native ESM.\n 'open',\n 'reflect-metadata',\n 'zod-to-json-schema',\n 'yaml',\n 'zod',\n]);\nconst NODE_BUILTINS = new Set(builtinModules.flatMap((name) => [name, `node:${name}`]));\n\ninterface InputFingerprint {\n path: string;\n size: number;\n mtimeMs: number;\n}\n\ninterface CompiledExtensionManifest {\n version: string;\n output: string;\n artifacts: string[];\n}\n\ninterface ExtensionSetManifest extends CompiledExtensionManifest {\n entries: string[];\n inputs: InputFingerprint[];\n}\n\nexport interface CompileExtensionSetOptions {\n /** Persistent worktree artifact directory. Compiler manifests remain local. */\n outputDirectory?: string;\n /** Human-readable artifact prefix, normally the major-mode name. */\n outputName?: string;\n /** Canonical worktree root used to create relocatable logical input paths. */\n repositoryRoot?: string;\n /** Repository-level immutable cache shared by linked worktrees. */\n sharedCacheDirectory?: string;\n}\n\nfunction safeOutputName(value: string): string {\n const normalized = value\n .trim()\n .replace(/[^a-zA-Z0-9._-]+/g, '-')\n .replace(/^[-.]+|[-.]+$/g, '');\n return normalized || 'doom-set';\n}\n\nfunction packageRootOf(specifier: string): string | undefined {\n const slash = specifier.startsWith('@') ? specifier.indexOf('/', specifier.indexOf('/') + 1) : specifier.indexOf('/');\n return slash === -1 ? specifier : specifier.slice(0, slash);\n}\n\nfunction packageRootFromPath(target: string): string | undefined {\n let directory = path.dirname(target);\n while (true) {\n const manifestPath = path.join(directory, 'package.json');\n if (fs.existsSync(manifestPath)) {\n try {\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as unknown;\n if (isRecord(manifest) && typeof manifest.name === 'string') return manifest.name;\n } catch {\n return undefined;\n }\n }\n const parent = path.dirname(directory);\n if (parent === directory) return undefined;\n directory = parent;\n }\n}\n\ninterface LogicalInput {\n logicalPath: string;\n target: string;\n sourcePath?: string;\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync.native(absolute);\n } catch {\n return absolute;\n }\n}\n\nfunction isInside(directory: string, target: string): boolean {\n const relative = path.relative(path.resolve(directory), path.resolve(target));\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\nfunction externalLogicalPath(sourcePath: string): string {\n const identity = createHash('sha256').update(sourcePath).digest('hex');\n return `${EXTERNAL_INPUT_PREFIX}${identity}`;\n}\n\nfunction logicalInput(target: string, repositoryRoot: string): LogicalInput {\n const absolute = canonicalPath(target);\n if (isInside(repositoryRoot, absolute)) {\n return {\n logicalPath: `${REPOSITORY_INPUT_PREFIX}${path.relative(repositoryRoot, absolute).split(path.sep).join('/')}`,\n target: absolute,\n };\n }\n return {\n logicalPath: externalLogicalPath(absolute),\n target: absolute,\n sourcePath: absolute,\n };\n}\n\nfunction resolveLogicalInputPath(input: SharedBuildInput, repositoryRoot: string): string | undefined {\n if (input.logicalPath.startsWith(REPOSITORY_INPUT_PREFIX) && input.sourcePath === undefined) {\n const target = path.resolve(repositoryRoot, input.logicalPath.slice(REPOSITORY_INPUT_PREFIX.length));\n return isInside(repositoryRoot, target) ? target : undefined;\n }\n if (\n input.logicalPath.startsWith(EXTERNAL_INPUT_PREFIX) &&\n input.sourcePath !== undefined &&\n input.logicalPath === externalLogicalPath(input.sourcePath)\n ) {\n return input.sourcePath;\n }\n return undefined;\n}\n\nfunction externalResolver() {\n return {\n name: 'doom-external-absolute',\n async resolveId(\n this: { resolve: (id: string, importer?: string, options?: unknown) => Promise<{ id: string } | null> },\n specifier: string,\n importer: string | undefined,\n options: unknown,\n ): Promise<{ id: string; external: boolean } | null> {\n if (specifier.startsWith('.') || path.isAbsolute(specifier) || specifier.startsWith('node:')) return null;\n const renamed = renameScope(specifier);\n\n // Host packages are resolved from this module, the way the rest of the\n // harness resolves them, because an extension written against Pi's former\n // scope does not have the current one in its own dependency tree.\n const root = packageRootOf(renamed);\n if (root && HOSTED_PACKAGES.has(root)) {\n const hosted = optionalPackageEntry(renamed);\n if (hosted) return { id: hosted, external: true };\n }\n\n // Rolldown's resolver rather than `createRequire`, which cannot see an\n // ESM-only package that publishes no `require` condition and would send\n // Pi's own core into the bundle.\n const resolved = await this.resolve(renamed, importer, options);\n if (!resolved) return null;\n // A dependency that is itself TypeScript cannot be handed to Node either,\n // so it is bundled rather than externalised.\n if (isTypeScriptEntry(resolved.id)) return null;\n return { id: resolved.id, external: true };\n },\n };\n}\n\nfunction fingerprint(target: string): InputFingerprint | undefined {\n try {\n const stat = fs.statSync(target);\n if (!stat.isFile()) return undefined;\n return { path: target, size: stat.size, mtimeMs: stat.mtimeMs };\n } catch {\n return undefined;\n }\n}\n\nfunction artifactsAreFresh(manifest: CompiledExtensionManifest, version: string, output: string): boolean {\n return (\n manifest.version === version &&\n manifest.output === output &&\n fs.existsSync(output) &&\n manifest.artifacts.every((artifact) => fs.existsSync(artifact))\n );\n}\n\nfunction manifestIsFresh(manifest: ExtensionSetManifest, entries: string[]): boolean {\n if (!artifactsAreFresh(manifest, SET_CACHE_VERSION, manifest.output)) return false;\n if (JSON.stringify(manifest.entries) !== JSON.stringify(entries)) {\n return false;\n }\n return manifest.inputs.every((previous) => {\n const current = fingerprint(previous.path);\n return current?.size === previous.size && current.mtimeMs === previous.mtimeMs;\n });\n}\n\nfunction readCompiledExtensionManifest(manifestPath: string): CompiledExtensionManifest | undefined {\n try {\n const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as CompiledExtensionManifest;\n return parsed && typeof parsed.output === 'string' && Array.isArray(parsed.artifacts) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction readSetManifest(manifestPath: string): ExtensionSetManifest | undefined {\n const parsed = readCompiledExtensionManifest(manifestPath) as ExtensionSetManifest | undefined;\n return parsed && Array.isArray(parsed.entries) && Array.isArray(parsed.inputs) ? parsed : undefined;\n}\n\nfunction writeAtomic(target: string, contents: string | Uint8Array): void {\n fs.mkdirSync(path.dirname(target), { recursive: true });\n const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;\n fs.writeFileSync(temporary, contents);\n try {\n fs.renameSync(temporary, target);\n } catch (error) {\n fs.rmSync(temporary, { force: true });\n if (!fs.existsSync(target)) throw error;\n }\n}\n\ninterface CompiledGraph {\n entry: string;\n artifacts: string[];\n}\n\nfunction splitOutputOptions(entryFileNames: string) {\n return {\n format: 'esm' as const,\n sourcemap: false,\n minify: true,\n codeSplitting: true,\n hashCharacters: 'hex' as const,\n entryFileNames,\n chunkFileNames: `chunks/[name].[hash:${HASH_LENGTH}]${COMPILED_SUFFIX}`,\n assetFileNames: `assets/[name].[hash:${HASH_LENGTH}][extname]`,\n };\n}\n\nfunction artifactTarget(outputDirectory: string, artifact: OutputAsset | OutputChunk): string {\n const target = path.resolve(outputDirectory, artifact.fileName);\n const relative = path.relative(outputDirectory, target);\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n throw new Error(`Rolldown emitted an artifact outside the output directory: ${artifact.fileName}`);\n }\n return target;\n}\n\nfunction artifactMatches(target: string, contents: string | Uint8Array): boolean {\n try {\n const current = fs.readFileSync(target);\n const generated = typeof contents === 'string' ? Buffer.from(contents) : Buffer.from(contents);\n return current.equals(generated);\n } catch {\n return false;\n }\n}\n\n/** Writes referenced chunks first so a visible entry always has a complete graph. */\nfunction writeCompiledGraph(generated: RolldownOutput, outputDirectory: string): CompiledGraph {\n const entry = generated.output.find((artifact) => artifact.type === 'chunk' && artifact.isEntry);\n if (!entry || entry.type !== 'chunk') throw new Error('Rolldown produced no extension entry');\n const root = path.resolve(outputDirectory);\n const remaining = generated.output.filter((artifact) => artifact !== entry);\n for (const artifact of [...remaining, entry]) {\n const contents = artifact.type === 'chunk' ? artifact.code : artifact.source;\n const target = artifactTarget(root, artifact);\n if (!artifactMatches(target, contents)) writeAtomic(target, contents);\n }\n return {\n entry: artifactTarget(root, entry),\n artifacts: generated.output.map((artifact) => artifactTarget(root, artifact)).sort(),\n };\n}\n\nfunction sharedLookupKey(entries: readonly LogicalInput[], outputName: string): string {\n return createHash('sha256')\n .update(SHARED_BUILD_VERSION)\n .update(process.version)\n .update(process.platform)\n .update(process.arch)\n .update(outputName)\n .update(JSON.stringify(entries.map((entry) => entry.logicalPath)))\n .digest('hex');\n}\n\ninterface TokenizedInputs {\n shared: SharedBuildInput[];\n byTarget: Map<string, string>;\n}\n\nfunction tokenizedInputs(inputs: ReadonlySet<string>, repositoryRoot: string): TokenizedInputs {\n const logical: LogicalInput[] = [];\n for (const target of inputs) logical.push(logicalInput(target, repositoryRoot));\n logical.sort((left, right) => left.logicalPath.localeCompare(right.logicalPath));\n const shared: SharedBuildInput[] = [\n { logicalPath: REPOSITORY_INPUT_PREFIX, sha256: '', token: `${SHARED_PATH_TOKEN_PREFIX}ROOT__` },\n ];\n const byTarget = new Map<string, string>([[repositoryRoot, `${SHARED_PATH_TOKEN_PREFIX}ROOT__`]]);\n const tokenByLogicalPath = new Map<string, string>();\n logical.forEach((input, index) => {\n const existing = tokenByLogicalPath.get(input.logicalPath);\n const token = existing ?? `${SHARED_PATH_TOKEN_PREFIX}${String(index)}__`;\n if (!existing) {\n shared.push({\n logicalPath: input.logicalPath,\n sha256: contentSha256(fs.readFileSync(input.target)),\n token,\n ...(input.sourcePath ? { sourcePath: input.sourcePath } : {}),\n });\n tokenByLogicalPath.set(input.logicalPath, token);\n }\n byTarget.set(path.resolve(input.target), token);\n });\n for (const target of inputs) {\n const input = logicalInput(target, repositoryRoot);\n const token = tokenByLogicalPath.get(input.logicalPath);\n if (token) byTarget.set(path.resolve(target), token);\n }\n return { shared, byTarget };\n}\n\nfunction tokenizedArtifactContents(\n generated: RolldownOutput,\n tokens: ReadonlyMap<string, string>,\n): Map<string, string | Uint8Array> {\n const artifacts = new Map<string, string | Uint8Array>();\n const replacements = [...tokens.entries()].sort(([left], [right]) => right.length - left.length);\n for (const artifact of generated.output) {\n if (artifact.type === 'asset') {\n artifacts.set(artifact.fileName, typeof artifact.source === 'string' ? artifact.source : artifact.source);\n continue;\n }\n let source = artifact.code;\n for (const [target, token] of replacements) {\n source = source\n .replaceAll(pathToFileURL(target).href, `${token}:url`)\n .replaceAll(pathToImportSpecifier(target), token)\n .replaceAll(target, token);\n }\n artifacts.set(artifact.fileName, source);\n }\n return artifacts;\n}\n\nfunction writeLocalSetManifest(\n manifestPath: string,\n entries: string[],\n output: string,\n artifacts: string[],\n inputs: ReadonlySet<string>,\n): void {\n const inputFingerprints = [...inputs]\n .map(fingerprint)\n .filter((value): value is InputFingerprint => value !== undefined)\n .sort((left, right) => left.path.localeCompare(right.path));\n writeAtomic(\n manifestPath,\n `${JSON.stringify(\n { version: SET_CACHE_VERSION, entries, output, artifacts: [...artifacts].sort(), inputs: inputFingerprints },\n null,\n 2,\n )}\\n`,\n );\n}\n\nfunction materializeSharedSet(\n build: ResolvedSharedBuild,\n outputDirectory: string,\n manifestPath: string,\n entries: string[],\n): string {\n const output = materializeSharedBuild(build, outputDirectory);\n const inputs = new Set(build.replacements.values());\n const artifacts = build.manifest.artifacts.map((artifact) => path.join(outputDirectory, artifact.path));\n writeLocalSetManifest(manifestPath, entries, output, artifacts, inputs);\n return output;\n}\n\n/** Stable lookup key for one exact Pi load order. */\nexport function extensionSetKey(entries: readonly string[]): string {\n return createHash('sha256')\n .update(SET_CACHE_VERSION)\n .update(JSON.stringify(entries.map((entry) => path.resolve(entry))))\n .digest('hex')\n .slice(0, HASH_LENGTH);\n}\n\nfunction extensionSetSource(entries: readonly string[]): string {\n const helper = optionalPackageEntry(EXTENSION_SOURCE_HELPER);\n if (!helper) throw new Error(`Cannot resolve ${EXTENSION_SOURCE_HELPER}`);\n const loaders = entries.map((entry) => `() => import(${JSON.stringify(pathToImportSpecifier(entry))})`);\n const names = entries.map((entry) => JSON.stringify(entry));\n return [\n `import { withExtensionSource } from ${JSON.stringify(pathToImportSpecifier(helper))};`,\n `const loaders = [${loaders.join(',')}];`,\n `const names = [${names.join(',')}];`,\n 'function report(index, phase, startedAt, error) {',\n ' const elapsed = (performance.now() - startedAt).toFixed(1);',\n ' const detail = error instanceof Error ? \": \" + error.message : error === undefined ? \"\" : \": \" + String(error);',\n ' process.stderr.write(\"[doompi:timing] \" + phase + \" \" + elapsed + \"ms \" + names[index] + detail + \"\\\\n\");',\n '}',\n 'export default async function doompiExtensionSet(pi) {',\n ' const timings = process.env.DOOMPI_TIMING === \"1\" || process.env.PI_TIMING === \"1\";',\n ' const loaded = await Promise.all(loaders.map(async (load, index) => {',\n ' const startedAt = performance.now();',\n ' try {',\n ' const extension = await load();',\n ' if (timings) report(index, \"import\", startedAt);',\n ' return extension;',\n ' } catch (error) {',\n ' report(index, \"import-error\", startedAt, error);',\n ' return undefined;',\n ' }',\n ' }));',\n ' for (let index = 0; index < loaded.length; index += 1) {',\n ' const extension = loaded[index];',\n ' if (!extension) continue;',\n ' const startedAt = performance.now();',\n ' try {',\n ' const factory = extension.default;',\n \" if (typeof factory !== 'function') throw new Error('does not export an extension factory');\",\n ' await factory(withExtensionSource(pi, names[index]));',\n ' if (timings) report(index, \"factory\", startedAt);',\n ' } catch (error) {',\n ' report(index, \"factory-error\", startedAt, error);',\n ' }',\n ' }',\n '}',\n ].join('\\n');\n}\n\nfunction pathToImportSpecifier(target: string): string {\n // Rolldown accepts absolute POSIX paths directly. Windows needs a file URL,\n // but replacing separators is enough for its platform-independent parser.\n return path.resolve(target).split(path.sep).join('/');\n}\n\nfunction setExternalResolver(inputs: Set<string>) {\n return {\n name: 'doom-set-external',\n async resolveId(\n this: { resolve: (id: string, importer?: string, options?: unknown) => Promise<{ id: string } | null> },\n specifier: string,\n importer: string | undefined,\n options: unknown,\n ): Promise<{ id: string; external: boolean } | null> {\n if (specifier === VIRTUAL_SET_ENTRY) return null;\n if (NODE_BUILTINS.has(specifier) || specifier.startsWith('node:')) {\n return { id: specifier, external: true };\n }\n\n if (path.isAbsolute(specifier)) {\n const root = packageRootFromPath(specifier);\n if (root && (HOSTED_PACKAGES.has(root) || NATIVE_PACKAGES.has(root) || STARTUP_EXTERNAL_PACKAGES.has(root))) {\n inputs.add(specifier);\n return { id: specifier, external: true };\n }\n return null;\n }\n if (specifier.startsWith('.')) return null;\n\n const renamed = renameScope(specifier);\n const root = packageRootOf(renamed);\n if (root && HOSTED_PACKAGES.has(root)) {\n const hosted = optionalPackageEntry(renamed);\n if (hosted) {\n inputs.add(hosted);\n return { id: hosted, external: true };\n }\n }\n if (root === 'typebox') {\n const typebox = piExtensionDependencyEntry(renamed);\n if (typebox) {\n inputs.add(typebox);\n return { id: typebox, external: true };\n }\n }\n if (root && (NATIVE_PACKAGES.has(root) || STARTUP_EXTERNAL_PACKAGES.has(root))) {\n const resolved = await this.resolve(renamed, importer, options);\n if (resolved) {\n inputs.add(resolved.id);\n return { id: resolved.id, external: true };\n }\n }\n return null;\n },\n };\n}\n\nfunction inputCollector(inputs: Set<string>) {\n return {\n name: 'doom-set-inputs',\n load(id: string): null {\n if (path.isAbsolute(id) && fs.existsSync(id)) inputs.add(id);\n return null;\n },\n };\n}\n\n/** An unresolved bare import would otherwise become a broken dist external. */\nfunction failUnresolvedImport(_level: LogLevel, log: RolldownLog, handler: LogOrStringHandler): void {\n if (log.code === 'UNRESOLVED_IMPORT') handler('error', log);\n}\n\ninterface SourceRange {\n start: number;\n end: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction collectImportMetaUrlRanges(value: unknown, ranges: SourceRange[]): void {\n if (Array.isArray(value)) {\n for (const item of value) collectImportMetaUrlRanges(item, ranges);\n return;\n }\n if (!isRecord(value)) return;\n\n const object = value.object;\n const property = value.property;\n if (\n value.type === 'MemberExpression' &&\n value.computed === false &&\n typeof value.start === 'number' &&\n typeof value.end === 'number' &&\n isRecord(object) &&\n object.type === 'MetaProperty' &&\n isRecord(object.meta) &&\n object.meta.name === 'import' &&\n isRecord(object.property) &&\n object.property.name === 'meta' &&\n isRecord(property) &&\n property.name === 'url'\n ) {\n ranges.push({ start: value.start, end: value.end });\n return;\n }\n\n for (const child of Object.values(value)) collectImportMetaUrlRanges(child, ranges);\n}\n\n/** Keeps package-owned resource lookup anchored to each original source file. */\nfunction preserveImportMetaUrl() {\n return {\n name: 'doom-original-import-meta-url',\n transform(\n this: {\n parse: (source: string, options?: { lang?: 'js' | 'jsx' | 'ts' | 'tsx' }) => unknown;\n },\n source: string,\n id: string,\n ): string | null {\n if (!path.isAbsolute(id) || !source.includes('import.meta.url')) return null;\n const extension = path.extname(id).toLowerCase();\n const lang = extension === '.tsx' ? 'tsx' : extension === '.jsx' ? 'jsx' : isTypeScriptEntry(id) ? 'ts' : 'js';\n const ranges: SourceRange[] = [];\n collectImportMetaUrlRanges(this.parse(source, { lang }), ranges);\n if (ranges.length === 0) return null;\n const replacement = JSON.stringify(pathToFileURL(id).href);\n let transformed = source;\n for (const range of ranges.sort((left, right) => right.start - left.start)) {\n transformed = `${transformed.slice(0, range.start)}${replacement}${transformed.slice(range.end)}`;\n }\n return transformed;\n },\n };\n}\n\n/** Location of the lightweight input-fingerprint record for one extension set. */\nexport function extensionSetManifestPath(\n entries: readonly string[],\n cacheDirectory: string,\n options: CompileExtensionSetOptions = {},\n): string {\n const normalized = entries.map((entry) => path.resolve(entry));\n const key = extensionSetKey(normalized);\n const outputDirectory = path.resolve(options.outputDirectory ?? path.join(cacheDirectory, SET_DIRECTORY));\n const outputName = safeOutputName(options.outputName ?? 'doom-set');\n // One graph can be emitted under several named mode artifacts. Keep their\n // cache records separate without changing extensionSetKey, which is the\n // runtime lookup key for an exact activation order.\n const outputKey = createHash('sha256').update(outputDirectory).update(outputName).digest('hex').slice(0, HASH_LENGTH);\n return path.join(cacheDirectory, SET_DIRECTORY, `${key}.${outputKey}${MANIFEST_SUFFIX}`);\n}\n\n/**\n * Compiles an ordered extension set into one native ESM entry.\n *\n * The generated entry starts every module load together, then invokes factories\n * sequentially in activation order. Its manifest records every loaded module,\n * so a dependency edit invalidates the cache without running the compiler.\n */\nexport async function compileExtensionSet(\n entries: readonly string[],\n cacheDirectory: string,\n options: CompileExtensionSetOptions = {},\n): Promise<string> {\n if (entries.length === 0) throw new Error('Cannot compile an empty extension set');\n const normalized = entries.map((entry) => path.resolve(entry));\n const setDirectory = path.join(cacheDirectory, SET_DIRECTORY);\n fs.mkdirSync(setDirectory, { recursive: true });\n const outputDirectory = path.resolve(options.outputDirectory ?? setDirectory);\n const outputName = safeOutputName(options.outputName ?? 'doom-set');\n fs.mkdirSync(outputDirectory, { recursive: true });\n const manifestPath = extensionSetManifestPath(normalized, cacheDirectory, options);\n const previous = readSetManifest(manifestPath);\n if (previous && manifestIsFresh(previous, normalized)) return previous.output;\n\n const generate = async (): Promise<{ generated: RolldownOutput; inputs: Set<string> }> => {\n const inputs = new Set(normalized);\n const source = extensionSetSource(normalized);\n const { rolldown } = await import('rolldown');\n const build = await rolldown({\n input: VIRTUAL_SET_ENTRY,\n platform: 'node',\n plugins: [\n {\n name: 'doom-set-entry',\n resolveId(id: string): string | null {\n return id === VIRTUAL_SET_ENTRY ? id : null;\n },\n load(id: string): string | null {\n return id === VIRTUAL_SET_ENTRY ? source : null;\n },\n },\n setExternalResolver(inputs),\n preserveImportMetaUrl(),\n inputCollector(inputs),\n ],\n onLog: failUnresolvedImport,\n });\n try {\n return {\n generated: await build.generate(splitOutputOptions(`${outputName}.[hash:${HASH_LENGTH}]${COMPILED_SUFFIX}`)),\n inputs,\n };\n } finally {\n await build.close();\n }\n };\n\n const repositoryRoot = options.repositoryRoot ? canonicalPath(options.repositoryRoot) : undefined;\n\n if (repositoryRoot && options.sharedCacheDirectory) {\n const logicalEntries = normalized.map((entry) => logicalInput(entry, repositoryRoot));\n const lookupKey = sharedLookupKey(logicalEntries, outputName);\n const resolveInput = (input: SharedBuildInput): string | undefined =>\n resolveLogicalInputPath(input, repositoryRoot);\n return withSharedBuildLock(options.sharedCacheDirectory, lookupKey, async () => {\n const cached = findSharedBuild(options.sharedCacheDirectory as string, lookupKey, resolveInput);\n if (cached) return materializeSharedSet(cached, outputDirectory, manifestPath, normalized);\n\n const { generated, inputs } = await generate();\n const tokenized = tokenizedInputs(inputs, repositoryRoot);\n const artifacts = tokenizedArtifactContents(generated, tokenized.byTarget);\n if (\n [...artifacts.values()].some((contents) => typeof contents === 'string' && contents.includes(repositoryRoot))\n ) {\n throw new Error('Shared build graph still contains an absolute worktree path after tokenization');\n }\n const entry = generated.output.find((artifact) => artifact.type === 'chunk' && artifact.isEntry);\n if (!entry) throw new Error('Rolldown produced no shared extension entry');\n publishSharedBuild({\n cacheDirectory: options.sharedCacheDirectory as string,\n lookupKey,\n entry: entry.fileName,\n inputs: tokenized.shared,\n artifacts,\n });\n const published = findSharedBuild(options.sharedCacheDirectory as string, lookupKey, resolveInput);\n if (!published) throw new Error('Published shared build could not be verified');\n return materializeSharedSet(published, outputDirectory, manifestPath, normalized);\n });\n }\n\n const { generated, inputs } = await generate();\n const compiled = writeCompiledGraph(generated, outputDirectory);\n writeLocalSetManifest(manifestPath, normalized, compiled.entry, compiled.artifacts, inputs);\n return compiled.entry;\n}\n\n/**\n * Bundles `entry` into `cacheDirectory` and returns the compiled path.\n *\n * Non-TypeScript entries are returned untouched. A cache hit skips the build.\n */\nexport async function compileExtension(entry: string, cacheDirectory: string): Promise<string> {\n if (!isTypeScriptEntry(entry)) return entry;\n\n fs.mkdirSync(cacheDirectory, { recursive: true });\n const outputName = compiledName(entry, cacheKey(entry));\n const output = path.join(cacheDirectory, outputName);\n const manifestPath = `${output}${MANIFEST_SUFFIX}`;\n const previous = readCompiledExtensionManifest(manifestPath);\n if (previous && artifactsAreFresh(previous, CACHE_VERSION, output)) return output;\n\n // Rolldown loads a native module and a sizeable parser graph. Sync is the\n // only command that needs it, and a cache hit does not, so keep it off every\n // other CLI startup path.\n const { rolldown } = await import('rolldown');\n const build = await rolldown({\n input: entry,\n platform: 'node',\n plugins: [externalResolver(), preserveImportMetaUrl()],\n onLog: failUnresolvedImport,\n });\n let compiled: CompiledGraph;\n try {\n const generated = await build.generate(splitOutputOptions(outputName));\n compiled = writeCompiledGraph(generated, cacheDirectory);\n } finally {\n await build.close();\n }\n writeAtomic(\n manifestPath,\n `${JSON.stringify({ version: CACHE_VERSION, output, artifacts: compiled.artifacts }, null, 2)}\\n`,\n );\n return output;\n}\n\n/**\n * Compiles every TypeScript entry in a resolution map, leaving the rest as-is.\n *\n * A failure is not fatal: the original path is kept so Pi falls back to loading\n * the TypeScript through jiti, which is what it did before this existed.\n */\nexport async function compileResolvedEntries(\n resolved: Record<string, string>,\n cacheDirectory: string,\n): Promise<Record<string, string>> {\n fs.mkdirSync(cacheDirectory, { recursive: true });\n const compiled: Record<string, string> = {};\n for (const [key, target] of Object.entries(resolved)) {\n try {\n compiled[key] = await compileExtension(target, cacheDirectory);\n } catch {\n compiled[key] = target;\n }\n }\n return compiled;\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{optionalPackageEntry as e}from"./modules/moduleResolution.mjs";import"../utils/index.mjs";import{contentSha256 as t,findSharedBuild as n,materializeSharedBuild as r,publishSharedBuild as i,withSharedBuildLock as a}from"./sharedBuildCache.mjs";import{builtinModules as o,createRequire as s}from"node:module";import{pathToFileURL as c}from"node:url";import l from"node:path";import u from"node:fs";import{createHash as d}from"node:crypto";const f=[`.ts`,`.mts`,`.cts`],p=`.mjs`,m=`sets`,h=`\0doompi-extension-set`,g=`@agimon-ai/doompi-ui/extensionName`,_=`__DOOMPI_PATH_`,v={"@mariozechner/pi-coding-agent":`@earendil-works/pi-coding-agent`,"@mariozechner/pi-tui":`@earendil-works/pi-tui`,"@mariozechner/pi-ai":`@earendil-works/pi-ai`,"@sinclair/typebox":`typebox`};function
|
|
2
|
-
`)}function X(e){return l.resolve(e).split(l.sep).join(`/`)}function
|
|
1
|
+
import{optionalPackageEntry as e}from"./modules/moduleResolution.mjs";import"../utils/index.mjs";import{contentSha256 as t,findSharedBuild as n,materializeSharedBuild as r,publishSharedBuild as i,withSharedBuildLock as a}from"./sharedBuildCache.mjs";import{builtinModules as o,createRequire as s}from"node:module";import{pathToFileURL as c}from"node:url";import l from"node:path";import u from"node:fs";import{createHash as d}from"node:crypto";const f=[`.ts`,`.mts`,`.cts`],p=`.mjs`,m=`sets`,h=`\0doompi-extension-set`,g=`@agimon-ai/doompi-ui/extensionName`,_=`__DOOMPI_PATH_`,v=`repo/`,y=`external/`,b={"@mariozechner/pi-coding-agent":`@earendil-works/pi-coding-agent`,"@mariozechner/pi-tui":`@earendil-works/pi-tui`,"@mariozechner/pi-ai":`@earendil-works/pi-ai`,"@sinclair/typebox":`typebox`};function x(e){return f.some(t=>e.endsWith(t))}function S(e){for(let[t,n]of Object.entries(b)){if(e===t)return n;if(e.startsWith(`${t}/`))return`${n}${e.slice(t.length)}`}return e}const C=new Set([`@earendil-works/pi-coding-agent`,`@earendil-works/pi-tui`,`@earendil-works/pi-ai`]);function w(t){let n=e(`@earendil-works/pi-coding-agent`);if(n)try{return s(n).resolve(t)}catch{return}}const T=new Set([`@parcel/watcher`,`@napi-rs/keyring`,`better-sqlite3`,`canvas`,`fsevents`,`isolated-vm`,`keytar`,`node-pty`,`onnxruntime-node`,`playwright`,`playwright-core`,`puppeteer`,`sharp`,`typescript`]),E=new Set([`@agimon-ai/foundation-port-registry`,`@agimon-ai/foundation-process-registry`,`@agimon-ai/log-sink-mcp`,`@modelcontextprotocol/sdk`,`@narumitw/pi-tui-kit`,`@rmux/sdk`,`@tursodatabase/database`,`@tursodatabase/database-common`,`ajv`,`ajv-formats`,`gpt-tokenizer`,`highlight.js`,`hono`,`inversify`,`open`,`reflect-metadata`,`zod-to-json-schema`,`yaml`,`zod`]),D=new Set(o.flatMap(e=>[e,`node:${e}`]));function O(e){return e.trim().replace(/[^a-zA-Z0-9._-]+/g,`-`).replace(/^[-.]+|[-.]+$/g,``)||`doom-set`}function k(e){let t=e.startsWith(`@`)?e.indexOf(`/`,e.indexOf(`/`)+1):e.indexOf(`/`);return t===-1?e:e.slice(0,t)}function A(e){let t=l.dirname(e);for(;;){let e=l.join(t,`package.json`);if(u.existsSync(e))try{let t=JSON.parse(u.readFileSync(e,`utf8`));if(Z(t)&&typeof t.name==`string`)return t.name}catch{return}let n=l.dirname(t);if(n===t)return;t=n}}function j(e){let t=l.resolve(e);try{return u.realpathSync.native(t)}catch{return t}}function M(e,t){let n=l.relative(l.resolve(e),l.resolve(t));return n===``||!n.startsWith(`..`)&&!l.isAbsolute(n)}function N(e){return`${y}${d(`sha256`).update(e).digest(`hex`)}`}function P(e,t){let n=j(e);return M(t,n)?{logicalPath:`${v}${l.relative(t,n).split(l.sep).join(`/`)}`,target:n}:{logicalPath:N(n),target:n,sourcePath:n}}function F(e,t){if(e.logicalPath.startsWith(v)&&e.sourcePath===void 0){let n=l.resolve(t,e.logicalPath.slice(5));return M(t,n)?n:void 0}if(e.logicalPath.startsWith(y)&&e.sourcePath!==void 0&&e.logicalPath===N(e.sourcePath))return e.sourcePath}function I(e){try{let t=u.statSync(e);return t.isFile()?{path:e,size:t.size,mtimeMs:t.mtimeMs}:void 0}catch{return}}function L(e,t,n){return e.version===t&&e.output===n&&u.existsSync(n)&&e.artifacts.every(e=>u.existsSync(e))}function ee(e,t){return!L(e,`v12`,e.output)||JSON.stringify(e.entries)!==JSON.stringify(t)?!1:e.inputs.every(e=>{let t=I(e.path);return t?.size===e.size&&t.mtimeMs===e.mtimeMs})}function te(e){try{let t=JSON.parse(u.readFileSync(e,`utf8`));return t&&typeof t.output==`string`&&Array.isArray(t.artifacts)?t:void 0}catch{return}}function ne(e){let t=te(e);return t&&Array.isArray(t.entries)&&Array.isArray(t.inputs)?t:void 0}function R(e,t){u.mkdirSync(l.dirname(e),{recursive:!0});let n=`${e}.${process.pid}.${Date.now()}.tmp`;u.writeFileSync(n,t);try{u.renameSync(n,e)}catch(t){if(u.rmSync(n,{force:!0}),!u.existsSync(e))throw t}}function z(e){return{format:`esm`,sourcemap:!1,minify:!0,codeSplitting:!0,hashCharacters:`hex`,entryFileNames:e,chunkFileNames:`chunks/[name].[hash:16]${p}`,assetFileNames:`assets/[name].[hash:16][extname]`}}function B(e,t){let n=l.resolve(e,t.fileName),r=l.relative(e,n);if(r.startsWith(`..`)||l.isAbsolute(r))throw Error(`Rolldown emitted an artifact outside the output directory: ${t.fileName}`);return n}function V(e,t){try{let n=u.readFileSync(e),r=Buffer.from(t);return n.equals(r)}catch{return!1}}function H(e,t){let n=e.output.find(e=>e.type===`chunk`&&e.isEntry);if(!n||n.type!==`chunk`)throw Error(`Rolldown produced no extension entry`);let r=l.resolve(t),i=e.output.filter(e=>e!==n);for(let e of[...i,n]){let t=e.type===`chunk`?e.code:e.source,n=B(r,e);V(n,t)||R(n,t)}return{entry:B(r,n),artifacts:e.output.map(e=>B(r,e)).sort()}}function U(e,t){return d(`sha256`).update(`v3-rolldown-1.0.0-rc.17`).update(process.version).update(process.platform).update(process.arch).update(t).update(JSON.stringify(e.map(e=>e.logicalPath))).digest(`hex`)}function W(e,n){let r=[];for(let t of e)r.push(P(t,n));r.sort((e,t)=>e.logicalPath.localeCompare(t.logicalPath));let i=[{logicalPath:v,sha256:``,token:`${_}ROOT__`}],a=new Map([[n,`${_}ROOT__`]]),o=new Map;r.forEach((e,n)=>{let r=o.get(e.logicalPath),s=r??`${_}${String(n)}__`;r||(i.push({logicalPath:e.logicalPath,sha256:t(u.readFileSync(e.target)),token:s,...e.sourcePath?{sourcePath:e.sourcePath}:{}}),o.set(e.logicalPath,s)),a.set(l.resolve(e.target),s)});for(let t of e){let e=P(t,n),r=o.get(e.logicalPath);r&&a.set(l.resolve(t),r)}return{shared:i,byTarget:a}}function G(e,t){let n=new Map,r=[...t.entries()].sort(([e],[t])=>t.length-e.length);for(let t of e.output){if(t.type===`asset`){n.set(t.fileName,(t.source,t.source));continue}let e=t.code;for(let[t,n]of r)e=e.replaceAll(c(t).href,`${n}:url`).replaceAll(X(t),n).replaceAll(t,n);n.set(t.fileName,e)}return n}function K(e,t,n,r,i){let a=[...i].map(I).filter(e=>e!==void 0).sort((e,t)=>e.path.localeCompare(t.path));R(e,`${JSON.stringify({version:`v12`,entries:t,output:n,artifacts:[...r].sort(),inputs:a},null,2)}\n`)}function q(e,t,n,i){let a=r(e,t),o=new Set(e.replacements.values());return K(n,i,a,e.manifest.artifacts.map(e=>l.join(t,e.path)),o),a}function J(e){return d(`sha256`).update(`v12`).update(JSON.stringify(e.map(e=>l.resolve(e)))).digest(`hex`).slice(0,16)}function Y(t){let n=e(g);if(!n)throw Error(`Cannot resolve ${g}`);let r=t.map(e=>`() => import(${JSON.stringify(X(e))})`),i=t.map(e=>JSON.stringify(e));return[`import { withExtensionSource } from ${JSON.stringify(X(n))};`,`const loaders = [${r.join(`,`)}];`,`const names = [${i.join(`,`)}];`,`function report(index, phase, startedAt, error) {`,` const elapsed = (performance.now() - startedAt).toFixed(1);`,` const detail = error instanceof Error ? ": " + error.message : error === undefined ? "" : ": " + String(error);`,` process.stderr.write("[doompi:timing] " + phase + " " + elapsed + "ms " + names[index] + detail + "\\n");`,`}`,`export default async function doompiExtensionSet(pi) {`,` const timings = process.env.DOOMPI_TIMING === "1" || process.env.PI_TIMING === "1";`,` const loaded = await Promise.all(loaders.map(async (load, index) => {`,` const startedAt = performance.now();`,` try {`,` const extension = await load();`,` if (timings) report(index, "import", startedAt);`,` return extension;`,` } catch (error) {`,` report(index, "import-error", startedAt, error);`,` return undefined;`,` }`,` }));`,` for (let index = 0; index < loaded.length; index += 1) {`,` const extension = loaded[index];`,` if (!extension) continue;`,` const startedAt = performance.now();`,` try {`,` const factory = extension.default;`,` if (typeof factory !== 'function') throw new Error('does not export an extension factory');`,` await factory(withExtensionSource(pi, names[index]));`,` if (timings) report(index, "factory", startedAt);`,` } catch (error) {`,` report(index, "factory-error", startedAt, error);`,` }`,` }`,`}`].join(`
|
|
2
|
+
`)}function X(e){return l.resolve(e).split(l.sep).join(`/`)}function re(t){return{name:`doom-set-external`,async resolveId(n,r,i){if(n===h)return null;if(D.has(n)||n.startsWith(`node:`))return{id:n,external:!0};if(l.isAbsolute(n)){let e=A(n);return e&&(C.has(e)||T.has(e)||E.has(e))?(t.add(n),{id:n,external:!0}):null}if(n.startsWith(`.`))return null;let a=S(n),o=k(a);if(o&&C.has(o)){let n=e(a);if(n)return t.add(n),{id:n,external:!0}}if(o===`typebox`){let e=w(a);if(e)return t.add(e),{id:e,external:!0}}if(o&&(T.has(o)||E.has(o))){let e=await this.resolve(a,r,i);if(e)return t.add(e.id),{id:e.id,external:!0}}return null}}}function ie(e){return{name:`doom-set-inputs`,load(t){return l.isAbsolute(t)&&u.existsSync(t)&&e.add(t),null}}}function ae(e,t,n){t.code===`UNRESOLVED_IMPORT`&&n(`error`,t)}function Z(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Q(e,t){if(Array.isArray(e)){for(let n of e)Q(n,t);return}if(!Z(e))return;let n=e.object,r=e.property;if(e.type===`MemberExpression`&&e.computed===!1&&typeof e.start==`number`&&typeof e.end==`number`&&Z(n)&&n.type===`MetaProperty`&&Z(n.meta)&&n.meta.name===`import`&&Z(n.property)&&n.property.name===`meta`&&Z(r)&&r.name===`url`){t.push({start:e.start,end:e.end});return}for(let n of Object.values(e))Q(n,t)}function oe(){return{name:`doom-original-import-meta-url`,transform(e,t){if(!l.isAbsolute(t)||!e.includes(`import.meta.url`))return null;let n=l.extname(t).toLowerCase(),r=n===`.tsx`?`tsx`:n===`.jsx`?`jsx`:x(t)?`ts`:`js`,i=[];if(Q(this.parse(e,{lang:r}),i),i.length===0)return null;let a=JSON.stringify(c(t).href),o=e;for(let e of i.sort((e,t)=>t.start-e.start))o=`${o.slice(0,e.start)}${a}${o.slice(e.end)}`;return o}}}function $(e,t,n={}){let r=J(e.map(e=>l.resolve(e))),i=l.resolve(n.outputDirectory??l.join(t,m)),a=O(n.outputName??`doom-set`),o=d(`sha256`).update(i).update(a).digest(`hex`).slice(0,16);return l.join(t,m,`${r}.${o}.json`)}async function se(e,t,r={}){if(e.length===0)throw Error(`Cannot compile an empty extension set`);let o=e.map(e=>l.resolve(e)),s=l.join(t,m);u.mkdirSync(s,{recursive:!0});let c=l.resolve(r.outputDirectory??s),d=O(r.outputName??`doom-set`);u.mkdirSync(c,{recursive:!0});let f=$(o,t,r),g=ne(f);if(g&&ee(g,o))return g.output;let _=async()=>{let e=new Set(o),t=Y(o),{rolldown:n}=await import(`rolldown`),r=await n({input:h,platform:`node`,plugins:[{name:`doom-set-entry`,resolveId(e){return e===h?e:null},load(e){return e===h?t:null}},re(e),oe(),ie(e)],onLog:ae});try{return{generated:await r.generate(z(`${d}.[hash:16]${p}`)),inputs:e}}finally{await r.close()}},v=r.repositoryRoot?j(r.repositoryRoot):void 0;if(v&&r.sharedCacheDirectory){let e=U(o.map(e=>P(e,v)),d),t=e=>F(e,v);return a(r.sharedCacheDirectory,e,async()=>{let a=n(r.sharedCacheDirectory,e,t);if(a)return q(a,c,f,o);let{generated:s,inputs:l}=await _(),u=W(l,v),d=G(s,u.byTarget);if([...d.values()].some(e=>typeof e==`string`&&e.includes(v)))throw Error(`Shared build graph still contains an absolute worktree path after tokenization`);let p=s.output.find(e=>e.type===`chunk`&&e.isEntry);if(!p)throw Error(`Rolldown produced no shared extension entry`);i({cacheDirectory:r.sharedCacheDirectory,lookupKey:e,entry:p.fileName,inputs:u.shared,artifacts:d});let m=n(r.sharedCacheDirectory,e,t);if(!m)throw Error(`Published shared build could not be verified`);return q(m,c,f,o)})}let{generated:y,inputs:b}=await _(),x=H(y,c);return K(f,o,x.entry,x.artifacts,b),x.entry}export{se as compileExtensionSet,J as extensionSetKey,$ as extensionSetManifestPath};
|
|
3
3
|
//# sourceMappingURL=extensionCompiler.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extensionCompiler.mjs","names":[],"sources":["../../src/adapters/extensionCompiler.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport { builtinModules, createRequire } from 'node:module';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { LogLevel, LogOrStringHandler, OutputAsset, OutputChunk, RolldownLog, RolldownOutput } from 'rolldown';\nimport { optionalPackageEntry } from '../utils/index.ts';\nimport {\n contentSha256,\n findSharedBuild,\n materializeSharedBuild,\n publishSharedBuild,\n type ResolvedSharedBuild,\n type SharedBuildInput,\n withSharedBuildLock,\n} from './sharedBuildCache.ts';\n\n/**\n * Compiles TypeScript extensions into plain ESM ahead of the session.\n *\n * Pi loads extensions through jiti, which transpiles TypeScript at import time.\n * That works, but it costs a transform per module on a cold cache and makes Node\n * complain about type stripping under node_modules on every launch. Resolution\n * already knows the full extension list, so each TypeScript graph is compiled\n * to native ESM here and Pi imports it without a runtime transform. Lazy imports\n * remain separate content-addressed chunks so their evaluation semantics survive\n * compilation.\n *\n * Mode artifacts bundle their JavaScript dependencies. Node built-ins, native\n * packages, and Pi's shared runtime stay external; those imports are rewritten\n * to absolute paths so a worktree-local artifact never relies on its\n * generated directory having its own `node_modules` tree. Immutable build objects\n * may be shared by linked worktrees, but materialized output stays worktree-local.\n */\n\nconst TYPESCRIPT_SUFFIXES = ['.ts', '.mts', '.cts'] as const;\nconst COMPILED_SUFFIX = '.mjs';\nconst CACHE_VERSION = 'v4';\nconst SET_CACHE_VERSION = 'v12';\nconst HASH_LENGTH = 16;\nconst SET_DIRECTORY = 'sets';\nconst MANIFEST_SUFFIX = '.json';\nconst VIRTUAL_SET_ENTRY = '\\0doompi-extension-set';\nconst EXTENSION_SOURCE_HELPER = '@agimon-ai/doompi-ui/extensionName';\nconst SHARED_BUILD_VERSION = 'v1-rolldown-1.0.0-rc.17';\nconst SHARED_PATH_TOKEN_PREFIX = '__DOOMPI_PATH_';\n\n/** Pi's former npm scope. The packages are the same, published under a new name. */\nconst PI_SCOPE_RENAMES: Readonly<Record<string, string>> = {\n '@mariozechner/pi-coding-agent': '@earendil-works/pi-coding-agent',\n '@mariozechner/pi-tui': '@earendil-works/pi-tui',\n '@mariozechner/pi-ai': '@earendil-works/pi-ai',\n '@sinclair/typebox': 'typebox',\n};\n\nexport function isTypeScriptEntry(entry: string): boolean {\n return TYPESCRIPT_SUFFIXES.some((suffix) => entry.endsWith(suffix));\n}\n\nfunction renameScope(specifier: string): string {\n for (const [before, after] of Object.entries(PI_SCOPE_RENAMES)) {\n if (specifier === before) return after;\n if (specifier.startsWith(`${before}/`)) return `${after}${specifier.slice(before.length)}`;\n }\n return specifier;\n}\n\n/**\n * Cache key for a compiled entry.\n *\n * The entry's own bytes are the only cheap signal that the extension changed; a\n * dependency edit is not visible without walking the graph, so an upgrade that\n * leaves the entry byte-identical is served from cache until the version below\n * is raised.\n */\nfunction cacheKey(entry: string): string {\n const source = fs.readFileSync(entry);\n return createHash('sha256').update(CACHE_VERSION).update(entry).update(source).digest('hex').slice(0, HASH_LENGTH);\n}\n\nfunction compiledName(entry: string, key: string): string {\n const base = path.basename(entry).replace(/\\.[cm]?ts$/, '');\n const parent = path.basename(path.dirname(entry));\n return `${parent}-${base}.${key}${COMPILED_SUFFIX}`;\n}\n\n/**\n * Packages that must resolve to the copy this harness runs, never to a second\n * one bundled into an extension. Pi owns the TUI and the session state, so a\n * duplicate renders into a different screen and mutates a different store.\n */\nconst HOSTED_PACKAGES = new Set(['@earendil-works/pi-coding-agent', '@earendil-works/pi-tui', '@earendil-works/pi-ai']);\n\n/**\n * Resolves packages Pi exposes to extensions as compatibility aliases.\n *\n * Extensions do not need to declare TypeBox themselves because Pi's loader\n * provides it as a virtual module. A dist bundle bypasses that loader, so it\n * resolves the same package against Pi's dependency tree and bundles it.\n */\nfunction piExtensionDependencyEntry(specifier: string): string | undefined {\n const piEntry = optionalPackageEntry('@earendil-works/pi-coding-agent');\n if (!piEntry) return undefined;\n try {\n return createRequire(piEntry).resolve(specifier);\n } catch {\n return undefined;\n }\n}\n\n/** Packages whose native binaries or runtime discovery cannot be rolled up. */\nconst NATIVE_PACKAGES = new Set([\n '@parcel/watcher',\n '@napi-rs/keyring',\n 'better-sqlite3',\n 'canvas',\n 'fsevents',\n 'isolated-vm',\n 'keytar',\n 'node-pty',\n 'onnxruntime-node',\n 'playwright',\n 'playwright-core',\n 'puppeteer',\n 'sharp',\n // TypeScript's Node system adapter reads CommonJS globals such as\n // __filename. Rolldown does not provide those globals when it folds the\n // adapter into the aggregate ESM extension, so keep the installed runtime\n // intact and import it by its resolved absolute path.\n 'typescript',\n]);\n\n/**\n * Dependency-heavy ESM runtimes that should retain their installed graph.\n *\n * Folding these packages into the aggregate creates dozens of generated chunks\n * that Node must parse and link before any extension factory can register. Node\n * can load the installed graph directly while still sharing its module cache.\n */\nconst STARTUP_EXTERNAL_PACKAGES = new Set([\n '@agimon-ai/foundation-port-registry',\n '@agimon-ai/foundation-process-registry',\n '@agimon-ai/log-sink-mcp',\n '@modelcontextprotocol/sdk',\n '@narumitw/pi-tui-kit',\n '@rmux/sdk',\n '@tursodatabase/database',\n '@tursodatabase/database-common',\n 'ajv',\n 'ajv-formats',\n 'gpt-tokenizer',\n 'highlight.js',\n 'hono',\n 'inversify',\n // open's CommonJS runtime resolves its bundled xdg-open helper through\n // __dirname, which is unavailable when Rolldown folds it into native ESM.\n 'open',\n 'reflect-metadata',\n 'zod-to-json-schema',\n 'yaml',\n 'zod',\n]);\nconst NODE_BUILTINS = new Set(builtinModules.flatMap((name) => [name, `node:${name}`]));\n\ninterface InputFingerprint {\n path: string;\n size: number;\n mtimeMs: number;\n}\n\ninterface CompiledExtensionManifest {\n version: string;\n output: string;\n artifacts: string[];\n}\n\ninterface ExtensionSetManifest extends CompiledExtensionManifest {\n entries: string[];\n inputs: InputFingerprint[];\n}\n\nexport interface CompileExtensionSetOptions {\n /** Persistent worktree artifact directory. Compiler manifests remain local. */\n outputDirectory?: string;\n /** Human-readable artifact prefix, normally the major-mode name. */\n outputName?: string;\n /** Canonical worktree root used to create relocatable logical input paths. */\n repositoryRoot?: string;\n /** Repository-level immutable cache shared by linked worktrees. */\n sharedCacheDirectory?: string;\n}\n\nfunction safeOutputName(value: string): string {\n const normalized = value\n .trim()\n .replace(/[^a-zA-Z0-9._-]+/g, '-')\n .replace(/^[-.]+|[-.]+$/g, '');\n return normalized || 'doom-set';\n}\n\nfunction packageRootOf(specifier: string): string | undefined {\n const slash = specifier.startsWith('@') ? specifier.indexOf('/', specifier.indexOf('/') + 1) : specifier.indexOf('/');\n return slash === -1 ? specifier : specifier.slice(0, slash);\n}\n\nfunction packageRootFromPath(target: string): string | undefined {\n const normalized = target.split(path.sep).join('/');\n const marker = '/node_modules/';\n const offset = normalized.lastIndexOf(marker);\n if (offset === -1) return undefined;\n const parts = normalized.slice(offset + marker.length).split('/');\n // pnpm's first segment is a store key; the second node_modules marker names\n // the package that owns the file.\n const nested = parts.lastIndexOf('node_modules');\n const packageParts = nested === -1 ? parts : parts.slice(nested + 1);\n return packageParts[0]?.startsWith('@') ? packageParts.slice(0, 2).join('/') : packageParts[0];\n}\n\ninterface LogicalInput {\n logicalPath: string;\n target: string;\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync.native(absolute);\n } catch {\n return absolute;\n }\n}\n\nfunction isInside(directory: string, target: string): boolean {\n const relative = path.relative(path.resolve(directory), path.resolve(target));\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\nfunction packageLogicalPath(target: string): string | undefined {\n const absolute = path.resolve(target);\n const packageName = packageRootFromPath(absolute);\n if (packageName) {\n const normalized = absolute.split(path.sep).join('/');\n const marker = `/node_modules/${packageName}/`;\n const offset = normalized.lastIndexOf(marker);\n if (offset !== -1) {\n return `package/${encodeURIComponent(packageName)}/${normalized.slice(offset + marker.length)}`;\n }\n }\n\n let directory = path.dirname(absolute);\n while (true) {\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(directory, 'package.json'), 'utf8')) as unknown;\n if (isRecord(manifest) && typeof manifest.name === 'string') {\n const relative = path.relative(directory, absolute).split(path.sep).join('/');\n return `package/${encodeURIComponent(manifest.name)}/${relative}`;\n }\n } catch {\n // Keep walking until a package boundary is found.\n }\n const parent = path.dirname(directory);\n if (parent === directory) return undefined;\n directory = parent;\n }\n}\n\nfunction logicalInput(target: string, repositoryRoot: string): LogicalInput | undefined {\n const absolute = canonicalPath(target);\n if (isInside(repositoryRoot, absolute)) {\n return {\n logicalPath: `repo/${path.relative(repositoryRoot, absolute).split(path.sep).join('/')}`,\n target: absolute,\n };\n }\n const packagePath = packageLogicalPath(absolute);\n return packagePath ? { logicalPath: packagePath, target: absolute } : undefined;\n}\n\nfunction findPackageDirectory(packageName: string, repositoryRoot: string): string | undefined {\n const candidates = [optionalPackageEntry(packageName)];\n try {\n candidates.push(createRequire(path.join(repositoryRoot, 'package.json')).resolve(packageName));\n } catch {\n // The package may only be visible through DoomPi's own dependency tree.\n }\n for (const candidate of candidates) {\n if (!candidate) continue;\n let directory = path.dirname(candidate);\n while (true) {\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(directory, 'package.json'), 'utf8')) as unknown;\n if (isRecord(manifest) && manifest.name === packageName) return directory;\n } catch {\n // Keep walking to the package root.\n }\n const parent = path.dirname(directory);\n if (parent === directory) break;\n directory = parent;\n }\n }\n return undefined;\n}\n\nfunction resolveLogicalInputPath(logicalPath: string, repositoryRoot: string): string | undefined {\n if (logicalPath.startsWith('repo/')) {\n const target = path.resolve(repositoryRoot, logicalPath.slice('repo/'.length));\n return isInside(repositoryRoot, target) ? target : undefined;\n }\n if (!logicalPath.startsWith('package/')) return undefined;\n const remainder = logicalPath.slice('package/'.length);\n const separator = remainder.indexOf('/');\n if (separator === -1) return undefined;\n const packageName = decodeURIComponent(remainder.slice(0, separator));\n const packageDirectory = findPackageDirectory(packageName, repositoryRoot);\n if (!packageDirectory) return undefined;\n const target = path.resolve(packageDirectory, remainder.slice(separator + 1));\n return isInside(packageDirectory, target) ? target : undefined;\n}\n\nfunction externalResolver() {\n return {\n name: 'doom-external-absolute',\n async resolveId(\n this: { resolve: (id: string, importer?: string, options?: unknown) => Promise<{ id: string } | null> },\n specifier: string,\n importer: string | undefined,\n options: unknown,\n ): Promise<{ id: string; external: boolean } | null> {\n if (specifier.startsWith('.') || path.isAbsolute(specifier) || specifier.startsWith('node:')) return null;\n const renamed = renameScope(specifier);\n\n // Host packages are resolved from this module, the way the rest of the\n // harness resolves them, because an extension written against Pi's former\n // scope does not have the current one in its own dependency tree.\n const root = packageRootOf(renamed);\n if (root && HOSTED_PACKAGES.has(root)) {\n const hosted = optionalPackageEntry(renamed);\n if (hosted) return { id: hosted, external: true };\n }\n\n // Rolldown's resolver rather than `createRequire`, which cannot see an\n // ESM-only package that publishes no `require` condition and would send\n // Pi's own core into the bundle.\n const resolved = await this.resolve(renamed, importer, options);\n if (!resolved) return null;\n // A dependency that is itself TypeScript cannot be handed to Node either,\n // so it is bundled rather than externalised.\n if (isTypeScriptEntry(resolved.id)) return null;\n return { id: resolved.id, external: true };\n },\n };\n}\n\nfunction fingerprint(target: string): InputFingerprint | undefined {\n try {\n const stat = fs.statSync(target);\n if (!stat.isFile()) return undefined;\n return { path: target, size: stat.size, mtimeMs: stat.mtimeMs };\n } catch {\n return undefined;\n }\n}\n\nfunction artifactsAreFresh(manifest: CompiledExtensionManifest, version: string, output: string): boolean {\n return (\n manifest.version === version &&\n manifest.output === output &&\n fs.existsSync(output) &&\n manifest.artifacts.every((artifact) => fs.existsSync(artifact))\n );\n}\n\nfunction manifestIsFresh(manifest: ExtensionSetManifest, entries: string[]): boolean {\n if (!artifactsAreFresh(manifest, SET_CACHE_VERSION, manifest.output)) return false;\n if (JSON.stringify(manifest.entries) !== JSON.stringify(entries)) {\n return false;\n }\n return manifest.inputs.every((previous) => {\n const current = fingerprint(previous.path);\n return current?.size === previous.size && current.mtimeMs === previous.mtimeMs;\n });\n}\n\nfunction readCompiledExtensionManifest(manifestPath: string): CompiledExtensionManifest | undefined {\n try {\n const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as CompiledExtensionManifest;\n return parsed && typeof parsed.output === 'string' && Array.isArray(parsed.artifacts) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction readSetManifest(manifestPath: string): ExtensionSetManifest | undefined {\n const parsed = readCompiledExtensionManifest(manifestPath) as ExtensionSetManifest | undefined;\n return parsed && Array.isArray(parsed.entries) && Array.isArray(parsed.inputs) ? parsed : undefined;\n}\n\nfunction writeAtomic(target: string, contents: string | Uint8Array): void {\n fs.mkdirSync(path.dirname(target), { recursive: true });\n const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;\n fs.writeFileSync(temporary, contents);\n try {\n fs.renameSync(temporary, target);\n } catch (error) {\n fs.rmSync(temporary, { force: true });\n if (!fs.existsSync(target)) throw error;\n }\n}\n\ninterface CompiledGraph {\n entry: string;\n artifacts: string[];\n}\n\nfunction splitOutputOptions(entryFileNames: string) {\n return {\n format: 'esm' as const,\n sourcemap: false,\n minify: true,\n codeSplitting: true,\n hashCharacters: 'hex' as const,\n entryFileNames,\n chunkFileNames: `chunks/[name].[hash:${HASH_LENGTH}]${COMPILED_SUFFIX}`,\n assetFileNames: `assets/[name].[hash:${HASH_LENGTH}][extname]`,\n };\n}\n\nfunction artifactTarget(outputDirectory: string, artifact: OutputAsset | OutputChunk): string {\n const target = path.resolve(outputDirectory, artifact.fileName);\n const relative = path.relative(outputDirectory, target);\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n throw new Error(`Rolldown emitted an artifact outside the output directory: ${artifact.fileName}`);\n }\n return target;\n}\n\nfunction artifactMatches(target: string, contents: string | Uint8Array): boolean {\n try {\n const current = fs.readFileSync(target);\n const generated = typeof contents === 'string' ? Buffer.from(contents) : Buffer.from(contents);\n return current.equals(generated);\n } catch {\n return false;\n }\n}\n\n/** Writes referenced chunks first so a visible entry always has a complete graph. */\nfunction writeCompiledGraph(generated: RolldownOutput, outputDirectory: string): CompiledGraph {\n const entry = generated.output.find((artifact) => artifact.type === 'chunk' && artifact.isEntry);\n if (!entry || entry.type !== 'chunk') throw new Error('Rolldown produced no extension entry');\n const root = path.resolve(outputDirectory);\n const remaining = generated.output.filter((artifact) => artifact !== entry);\n for (const artifact of [...remaining, entry]) {\n const contents = artifact.type === 'chunk' ? artifact.code : artifact.source;\n const target = artifactTarget(root, artifact);\n if (!artifactMatches(target, contents)) writeAtomic(target, contents);\n }\n return {\n entry: artifactTarget(root, entry),\n artifacts: generated.output.map((artifact) => artifactTarget(root, artifact)).sort(),\n };\n}\n\nfunction sharedLookupKey(entries: readonly LogicalInput[], outputName: string): string {\n return createHash('sha256')\n .update(SHARED_BUILD_VERSION)\n .update(process.version)\n .update(process.platform)\n .update(process.arch)\n .update(outputName)\n .update(JSON.stringify(entries.map((entry) => entry.logicalPath)))\n .digest('hex');\n}\n\ninterface TokenizedInputs {\n shared: SharedBuildInput[];\n byTarget: Map<string, string>;\n}\n\nfunction tokenizedInputs(inputs: ReadonlySet<string>, repositoryRoot: string): TokenizedInputs {\n const logical: LogicalInput[] = [];\n for (const target of inputs) {\n const input = logicalInput(target, repositoryRoot);\n if (!input) throw new Error(`Shared build input has no relocatable identity: ${target}`);\n logical.push(input);\n }\n logical.sort((left, right) => left.logicalPath.localeCompare(right.logicalPath));\n const shared: SharedBuildInput[] = [{ logicalPath: 'repo/', sha256: '', token: `${SHARED_PATH_TOKEN_PREFIX}ROOT__` }];\n const byTarget = new Map<string, string>([[repositoryRoot, `${SHARED_PATH_TOKEN_PREFIX}ROOT__`]]);\n const tokenByLogicalPath = new Map<string, string>();\n logical.forEach((input, index) => {\n const existing = tokenByLogicalPath.get(input.logicalPath);\n const token = existing ?? `${SHARED_PATH_TOKEN_PREFIX}${String(index)}__`;\n if (!existing) {\n shared.push({ logicalPath: input.logicalPath, sha256: contentSha256(fs.readFileSync(input.target)), token });\n tokenByLogicalPath.set(input.logicalPath, token);\n }\n byTarget.set(path.resolve(input.target), token);\n });\n for (const target of inputs) {\n const input = logicalInput(target, repositoryRoot);\n const token = input ? tokenByLogicalPath.get(input.logicalPath) : undefined;\n if (token) byTarget.set(path.resolve(target), token);\n }\n return { shared, byTarget };\n}\n\nfunction tokenizedArtifactContents(\n generated: RolldownOutput,\n tokens: ReadonlyMap<string, string>,\n): Map<string, string | Uint8Array> {\n const artifacts = new Map<string, string | Uint8Array>();\n const replacements = [...tokens.entries()].sort(([left], [right]) => right.length - left.length);\n for (const artifact of generated.output) {\n if (artifact.type === 'asset') {\n artifacts.set(artifact.fileName, typeof artifact.source === 'string' ? artifact.source : artifact.source);\n continue;\n }\n let source = artifact.code;\n for (const [target, token] of replacements) {\n source = source\n .replaceAll(pathToFileURL(target).href, `${token}:url`)\n .replaceAll(pathToImportSpecifier(target), token)\n .replaceAll(target, token);\n }\n artifacts.set(artifact.fileName, source);\n }\n return artifacts;\n}\n\nfunction writeLocalSetManifest(\n manifestPath: string,\n entries: string[],\n output: string,\n artifacts: string[],\n inputs: ReadonlySet<string>,\n): void {\n const inputFingerprints = [...inputs]\n .map(fingerprint)\n .filter((value): value is InputFingerprint => value !== undefined)\n .sort((left, right) => left.path.localeCompare(right.path));\n writeAtomic(\n manifestPath,\n `${JSON.stringify(\n { version: SET_CACHE_VERSION, entries, output, artifacts: [...artifacts].sort(), inputs: inputFingerprints },\n null,\n 2,\n )}\\n`,\n );\n}\n\nfunction materializeSharedSet(\n build: ResolvedSharedBuild,\n outputDirectory: string,\n manifestPath: string,\n entries: string[],\n): string {\n const output = materializeSharedBuild(build, outputDirectory);\n const inputs = new Set(build.replacements.values());\n const artifacts = build.manifest.artifacts.map((artifact) => path.join(outputDirectory, artifact.path));\n writeLocalSetManifest(manifestPath, entries, output, artifacts, inputs);\n return output;\n}\n\n/** Stable lookup key for one exact Pi load order. */\nexport function extensionSetKey(entries: readonly string[]): string {\n return createHash('sha256')\n .update(SET_CACHE_VERSION)\n .update(JSON.stringify(entries.map((entry) => path.resolve(entry))))\n .digest('hex')\n .slice(0, HASH_LENGTH);\n}\n\nfunction extensionSetSource(entries: readonly string[]): string {\n const helper = optionalPackageEntry(EXTENSION_SOURCE_HELPER);\n if (!helper) throw new Error(`Cannot resolve ${EXTENSION_SOURCE_HELPER}`);\n const loaders = entries.map((entry) => `() => import(${JSON.stringify(pathToImportSpecifier(entry))})`);\n const names = entries.map((entry) => JSON.stringify(entry));\n return [\n `import { withExtensionSource } from ${JSON.stringify(pathToImportSpecifier(helper))};`,\n `const loaders = [${loaders.join(',')}];`,\n `const names = [${names.join(',')}];`,\n 'function report(index, phase, startedAt, error) {',\n ' const elapsed = (performance.now() - startedAt).toFixed(1);',\n ' const detail = error instanceof Error ? \": \" + error.message : error === undefined ? \"\" : \": \" + String(error);',\n ' process.stderr.write(\"[doompi:timing] \" + phase + \" \" + elapsed + \"ms \" + names[index] + detail + \"\\\\n\");',\n '}',\n 'export default async function doompiExtensionSet(pi) {',\n ' const timings = process.env.DOOMPI_TIMING === \"1\" || process.env.PI_TIMING === \"1\";',\n ' const loaded = await Promise.all(loaders.map(async (load, index) => {',\n ' const startedAt = performance.now();',\n ' try {',\n ' const extension = await load();',\n ' if (timings) report(index, \"import\", startedAt);',\n ' return extension;',\n ' } catch (error) {',\n ' report(index, \"import-error\", startedAt, error);',\n ' return undefined;',\n ' }',\n ' }));',\n ' for (let index = 0; index < loaded.length; index += 1) {',\n ' const extension = loaded[index];',\n ' if (!extension) continue;',\n ' const startedAt = performance.now();',\n ' try {',\n ' const factory = extension.default;',\n \" if (typeof factory !== 'function') throw new Error('does not export an extension factory');\",\n ' await factory(withExtensionSource(pi, names[index]));',\n ' if (timings) report(index, \"factory\", startedAt);',\n ' } catch (error) {',\n ' report(index, \"factory-error\", startedAt, error);',\n ' }',\n ' }',\n '}',\n ].join('\\n');\n}\n\nfunction pathToImportSpecifier(target: string): string {\n // Rolldown accepts absolute POSIX paths directly. Windows needs a file URL,\n // but replacing separators is enough for its platform-independent parser.\n return path.resolve(target).split(path.sep).join('/');\n}\n\nfunction setExternalResolver(inputs: Set<string>) {\n return {\n name: 'doom-set-external',\n async resolveId(\n this: { resolve: (id: string, importer?: string, options?: unknown) => Promise<{ id: string } | null> },\n specifier: string,\n importer: string | undefined,\n options: unknown,\n ): Promise<{ id: string; external: boolean } | null> {\n if (specifier === VIRTUAL_SET_ENTRY) return null;\n if (NODE_BUILTINS.has(specifier) || specifier.startsWith('node:')) {\n return { id: specifier, external: true };\n }\n\n if (path.isAbsolute(specifier)) {\n const root = packageRootFromPath(specifier);\n if (root && (HOSTED_PACKAGES.has(root) || NATIVE_PACKAGES.has(root) || STARTUP_EXTERNAL_PACKAGES.has(root))) {\n inputs.add(specifier);\n return { id: specifier, external: true };\n }\n return null;\n }\n if (specifier.startsWith('.')) return null;\n\n const renamed = renameScope(specifier);\n const root = packageRootOf(renamed);\n if (root && HOSTED_PACKAGES.has(root)) {\n const hosted = optionalPackageEntry(renamed);\n if (hosted) {\n inputs.add(hosted);\n return { id: hosted, external: true };\n }\n }\n if (root === 'typebox') {\n const typebox = piExtensionDependencyEntry(renamed);\n if (typebox) {\n inputs.add(typebox);\n return { id: typebox, external: true };\n }\n }\n if (root && (NATIVE_PACKAGES.has(root) || STARTUP_EXTERNAL_PACKAGES.has(root))) {\n const resolved = await this.resolve(renamed, importer, options);\n if (resolved) {\n inputs.add(resolved.id);\n return { id: resolved.id, external: true };\n }\n }\n return null;\n },\n };\n}\n\nfunction inputCollector(inputs: Set<string>) {\n return {\n name: 'doom-set-inputs',\n load(id: string): null {\n if (path.isAbsolute(id) && fs.existsSync(id)) inputs.add(id);\n return null;\n },\n };\n}\n\n/** An unresolved bare import would otherwise become a broken dist external. */\nfunction failUnresolvedImport(_level: LogLevel, log: RolldownLog, handler: LogOrStringHandler): void {\n if (log.code === 'UNRESOLVED_IMPORT') handler('error', log);\n}\n\ninterface SourceRange {\n start: number;\n end: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction collectImportMetaUrlRanges(value: unknown, ranges: SourceRange[]): void {\n if (Array.isArray(value)) {\n for (const item of value) collectImportMetaUrlRanges(item, ranges);\n return;\n }\n if (!isRecord(value)) return;\n\n const object = value.object;\n const property = value.property;\n if (\n value.type === 'MemberExpression' &&\n value.computed === false &&\n typeof value.start === 'number' &&\n typeof value.end === 'number' &&\n isRecord(object) &&\n object.type === 'MetaProperty' &&\n isRecord(object.meta) &&\n object.meta.name === 'import' &&\n isRecord(object.property) &&\n object.property.name === 'meta' &&\n isRecord(property) &&\n property.name === 'url'\n ) {\n ranges.push({ start: value.start, end: value.end });\n return;\n }\n\n for (const child of Object.values(value)) collectImportMetaUrlRanges(child, ranges);\n}\n\n/** Keeps package-owned resource lookup anchored to each original source file. */\nfunction preserveImportMetaUrl() {\n return {\n name: 'doom-original-import-meta-url',\n transform(\n this: {\n parse: (source: string, options?: { lang?: 'js' | 'jsx' | 'ts' | 'tsx' }) => unknown;\n },\n source: string,\n id: string,\n ): string | null {\n if (!path.isAbsolute(id) || !source.includes('import.meta.url')) return null;\n const extension = path.extname(id).toLowerCase();\n const lang = extension === '.tsx' ? 'tsx' : extension === '.jsx' ? 'jsx' : isTypeScriptEntry(id) ? 'ts' : 'js';\n const ranges: SourceRange[] = [];\n collectImportMetaUrlRanges(this.parse(source, { lang }), ranges);\n if (ranges.length === 0) return null;\n const replacement = JSON.stringify(pathToFileURL(id).href);\n let transformed = source;\n for (const range of ranges.sort((left, right) => right.start - left.start)) {\n transformed = `${transformed.slice(0, range.start)}${replacement}${transformed.slice(range.end)}`;\n }\n return transformed;\n },\n };\n}\n\n/** Location of the lightweight input-fingerprint record for one extension set. */\nexport function extensionSetManifestPath(\n entries: readonly string[],\n cacheDirectory: string,\n options: CompileExtensionSetOptions = {},\n): string {\n const normalized = entries.map((entry) => path.resolve(entry));\n const key = extensionSetKey(normalized);\n const outputDirectory = path.resolve(options.outputDirectory ?? path.join(cacheDirectory, SET_DIRECTORY));\n const outputName = safeOutputName(options.outputName ?? 'doom-set');\n // One graph can be emitted under several named mode artifacts. Keep their\n // cache records separate without changing extensionSetKey, which is the\n // runtime lookup key for an exact activation order.\n const outputKey = createHash('sha256').update(outputDirectory).update(outputName).digest('hex').slice(0, HASH_LENGTH);\n return path.join(cacheDirectory, SET_DIRECTORY, `${key}.${outputKey}${MANIFEST_SUFFIX}`);\n}\n\n/**\n * Compiles an ordered extension set into one native ESM entry.\n *\n * The generated entry starts every module load together, then invokes factories\n * sequentially in activation order. Its manifest records every loaded module,\n * so a dependency edit invalidates the cache without running the compiler.\n */\nexport async function compileExtensionSet(\n entries: readonly string[],\n cacheDirectory: string,\n options: CompileExtensionSetOptions = {},\n): Promise<string> {\n if (entries.length === 0) throw new Error('Cannot compile an empty extension set');\n const normalized = entries.map((entry) => path.resolve(entry));\n const setDirectory = path.join(cacheDirectory, SET_DIRECTORY);\n fs.mkdirSync(setDirectory, { recursive: true });\n const outputDirectory = path.resolve(options.outputDirectory ?? setDirectory);\n const outputName = safeOutputName(options.outputName ?? 'doom-set');\n fs.mkdirSync(outputDirectory, { recursive: true });\n const manifestPath = extensionSetManifestPath(normalized, cacheDirectory, options);\n const previous = readSetManifest(manifestPath);\n if (previous && manifestIsFresh(previous, normalized)) return previous.output;\n\n const generate = async (): Promise<{ generated: RolldownOutput; inputs: Set<string> }> => {\n const inputs = new Set(normalized);\n const source = extensionSetSource(normalized);\n const { rolldown } = await import('rolldown');\n const build = await rolldown({\n input: VIRTUAL_SET_ENTRY,\n platform: 'node',\n plugins: [\n {\n name: 'doom-set-entry',\n resolveId(id: string): string | null {\n return id === VIRTUAL_SET_ENTRY ? id : null;\n },\n load(id: string): string | null {\n return id === VIRTUAL_SET_ENTRY ? source : null;\n },\n },\n setExternalResolver(inputs),\n preserveImportMetaUrl(),\n inputCollector(inputs),\n ],\n onLog: failUnresolvedImport,\n });\n try {\n return {\n generated: await build.generate(splitOutputOptions(`${outputName}.[hash:${HASH_LENGTH}]${COMPILED_SUFFIX}`)),\n inputs,\n };\n } finally {\n await build.close();\n }\n };\n\n const repositoryRoot = options.repositoryRoot ? canonicalPath(options.repositoryRoot) : undefined;\n const logicalEntries = repositoryRoot ? normalized.map((entry) => logicalInput(entry, repositoryRoot)) : [];\n const canShare =\n repositoryRoot !== undefined &&\n options.sharedCacheDirectory !== undefined &&\n logicalEntries.length === normalized.length &&\n logicalEntries.every((entry): entry is LogicalInput => entry !== undefined);\n\n if (canShare && repositoryRoot && options.sharedCacheDirectory) {\n const lookupKey = sharedLookupKey(logicalEntries, outputName);\n const resolveInput = (logicalPath: string): string | undefined =>\n resolveLogicalInputPath(logicalPath, repositoryRoot);\n return withSharedBuildLock(options.sharedCacheDirectory, lookupKey, async () => {\n const cached = findSharedBuild(options.sharedCacheDirectory as string, lookupKey, resolveInput);\n if (cached) return materializeSharedSet(cached, outputDirectory, manifestPath, normalized);\n\n const { generated, inputs } = await generate();\n const tokenized = tokenizedInputs(inputs, repositoryRoot);\n const artifacts = tokenizedArtifactContents(generated, tokenized.byTarget);\n if (\n [...artifacts.values()].some((contents) => typeof contents === 'string' && contents.includes(repositoryRoot))\n ) {\n throw new Error('Shared build graph still contains an absolute worktree path after tokenization');\n }\n const entry = generated.output.find((artifact) => artifact.type === 'chunk' && artifact.isEntry);\n if (!entry) throw new Error('Rolldown produced no shared extension entry');\n publishSharedBuild({\n cacheDirectory: options.sharedCacheDirectory as string,\n lookupKey,\n entry: entry.fileName,\n inputs: tokenized.shared,\n artifacts,\n });\n const published = findSharedBuild(options.sharedCacheDirectory as string, lookupKey, resolveInput);\n if (!published) throw new Error('Published shared build could not be verified');\n return materializeSharedSet(published, outputDirectory, manifestPath, normalized);\n });\n }\n\n const { generated, inputs } = await generate();\n const compiled = writeCompiledGraph(generated, outputDirectory);\n writeLocalSetManifest(manifestPath, normalized, compiled.entry, compiled.artifacts, inputs);\n return compiled.entry;\n}\n\n/**\n * Bundles `entry` into `cacheDirectory` and returns the compiled path.\n *\n * Non-TypeScript entries are returned untouched. A cache hit skips the build.\n */\nexport async function compileExtension(entry: string, cacheDirectory: string): Promise<string> {\n if (!isTypeScriptEntry(entry)) return entry;\n\n fs.mkdirSync(cacheDirectory, { recursive: true });\n const outputName = compiledName(entry, cacheKey(entry));\n const output = path.join(cacheDirectory, outputName);\n const manifestPath = `${output}${MANIFEST_SUFFIX}`;\n const previous = readCompiledExtensionManifest(manifestPath);\n if (previous && artifactsAreFresh(previous, CACHE_VERSION, output)) return output;\n\n // Rolldown loads a native module and a sizeable parser graph. Sync is the\n // only command that needs it, and a cache hit does not, so keep it off every\n // other CLI startup path.\n const { rolldown } = await import('rolldown');\n const build = await rolldown({\n input: entry,\n platform: 'node',\n plugins: [externalResolver(), preserveImportMetaUrl()],\n onLog: failUnresolvedImport,\n });\n let compiled: CompiledGraph;\n try {\n const generated = await build.generate(splitOutputOptions(outputName));\n compiled = writeCompiledGraph(generated, cacheDirectory);\n } finally {\n await build.close();\n }\n writeAtomic(\n manifestPath,\n `${JSON.stringify({ version: CACHE_VERSION, output, artifacts: compiled.artifacts }, null, 2)}\\n`,\n );\n return output;\n}\n\n/**\n * Compiles every TypeScript entry in a resolution map, leaving the rest as-is.\n *\n * A failure is not fatal: the original path is kept so Pi falls back to loading\n * the TypeScript through jiti, which is what it did before this existed.\n */\nexport async function compileResolvedEntries(\n resolved: Record<string, string>,\n cacheDirectory: string,\n): Promise<Record<string, string>> {\n fs.mkdirSync(cacheDirectory, { recursive: true });\n const compiled: Record<string, string> = {};\n for (const [key, target] of Object.entries(resolved)) {\n try {\n compiled[key] = await compileExtension(target, cacheDirectory);\n } catch {\n compiled[key] = target;\n }\n }\n return compiled;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extensionCompiler.mjs","names":[],"sources":["../../src/adapters/extensionCompiler.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport { builtinModules, createRequire } from 'node:module';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { LogLevel, LogOrStringHandler, OutputAsset, OutputChunk, RolldownLog, RolldownOutput } from 'rolldown';\nimport { optionalPackageEntry } from '../utils/index.ts';\nimport {\n contentSha256,\n findSharedBuild,\n materializeSharedBuild,\n publishSharedBuild,\n type ResolvedSharedBuild,\n type SharedBuildInput,\n withSharedBuildLock,\n} from './sharedBuildCache.ts';\n\n/**\n * Compiles TypeScript extensions into plain ESM ahead of the session.\n *\n * Pi loads extensions through jiti, which transpiles TypeScript at import time.\n * That works, but it costs a transform per module on a cold cache and makes Node\n * complain about type stripping under node_modules on every launch. Resolution\n * already knows the full extension list, so each TypeScript graph is compiled\n * to native ESM here and Pi imports it without a runtime transform. Lazy imports\n * remain separate content-addressed chunks so their evaluation semantics survive\n * compilation.\n *\n * Mode artifacts bundle their JavaScript dependencies. Node built-ins, native\n * packages, and Pi's shared runtime stay external; those imports are rewritten\n * to absolute paths so a worktree-local artifact never relies on its\n * generated directory having its own `node_modules` tree. Immutable build objects\n * may be shared by linked worktrees, but materialized output stays worktree-local.\n */\n\nconst TYPESCRIPT_SUFFIXES = ['.ts', '.mts', '.cts'] as const;\nconst COMPILED_SUFFIX = '.mjs';\nconst CACHE_VERSION = 'v4';\nconst SET_CACHE_VERSION = 'v12';\nconst HASH_LENGTH = 16;\nconst SET_DIRECTORY = 'sets';\nconst MANIFEST_SUFFIX = '.json';\nconst VIRTUAL_SET_ENTRY = '\\0doompi-extension-set';\nconst EXTENSION_SOURCE_HELPER = '@agimon-ai/doompi-ui/extensionName';\nconst SHARED_BUILD_VERSION = 'v3-rolldown-1.0.0-rc.17';\nconst SHARED_PATH_TOKEN_PREFIX = '__DOOMPI_PATH_';\nconst REPOSITORY_INPUT_PREFIX = 'repo/';\nconst EXTERNAL_INPUT_PREFIX = 'external/';\n\n/** Pi's former npm scope. The packages are the same, published under a new name. */\nconst PI_SCOPE_RENAMES: Readonly<Record<string, string>> = {\n '@mariozechner/pi-coding-agent': '@earendil-works/pi-coding-agent',\n '@mariozechner/pi-tui': '@earendil-works/pi-tui',\n '@mariozechner/pi-ai': '@earendil-works/pi-ai',\n '@sinclair/typebox': 'typebox',\n};\n\nexport function isTypeScriptEntry(entry: string): boolean {\n return TYPESCRIPT_SUFFIXES.some((suffix) => entry.endsWith(suffix));\n}\n\nfunction renameScope(specifier: string): string {\n for (const [before, after] of Object.entries(PI_SCOPE_RENAMES)) {\n if (specifier === before) return after;\n if (specifier.startsWith(`${before}/`)) return `${after}${specifier.slice(before.length)}`;\n }\n return specifier;\n}\n\n/**\n * Cache key for a compiled entry.\n *\n * The entry's own bytes are the only cheap signal that the extension changed; a\n * dependency edit is not visible without walking the graph, so an upgrade that\n * leaves the entry byte-identical is served from cache until the version below\n * is raised.\n */\nfunction cacheKey(entry: string): string {\n const source = fs.readFileSync(entry);\n return createHash('sha256').update(CACHE_VERSION).update(entry).update(source).digest('hex').slice(0, HASH_LENGTH);\n}\n\nfunction compiledName(entry: string, key: string): string {\n const base = path.basename(entry).replace(/\\.[cm]?ts$/, '');\n const parent = path.basename(path.dirname(entry));\n return `${parent}-${base}.${key}${COMPILED_SUFFIX}`;\n}\n\n/**\n * Packages that must resolve to the copy this harness runs, never to a second\n * one bundled into an extension. Pi owns the TUI and the session state, so a\n * duplicate renders into a different screen and mutates a different store.\n */\nconst HOSTED_PACKAGES = new Set(['@earendil-works/pi-coding-agent', '@earendil-works/pi-tui', '@earendil-works/pi-ai']);\n\n/**\n * Resolves packages Pi exposes to extensions as compatibility aliases.\n *\n * Extensions do not need to declare TypeBox themselves because Pi's loader\n * provides it as a virtual module. A dist bundle bypasses that loader, so it\n * resolves the same package against Pi's dependency tree and bundles it.\n */\nfunction piExtensionDependencyEntry(specifier: string): string | undefined {\n const piEntry = optionalPackageEntry('@earendil-works/pi-coding-agent');\n if (!piEntry) return undefined;\n try {\n return createRequire(piEntry).resolve(specifier);\n } catch {\n return undefined;\n }\n}\n\n/** Packages whose native binaries or runtime discovery cannot be rolled up. */\nconst NATIVE_PACKAGES = new Set([\n '@parcel/watcher',\n '@napi-rs/keyring',\n 'better-sqlite3',\n 'canvas',\n 'fsevents',\n 'isolated-vm',\n 'keytar',\n 'node-pty',\n 'onnxruntime-node',\n 'playwright',\n 'playwright-core',\n 'puppeteer',\n 'sharp',\n // TypeScript's Node system adapter reads CommonJS globals such as\n // __filename. Rolldown does not provide those globals when it folds the\n // adapter into the aggregate ESM extension, so keep the installed runtime\n // intact and import it by its resolved absolute path.\n 'typescript',\n]);\n\n/**\n * Dependency-heavy ESM runtimes that should retain their installed graph.\n *\n * Folding these packages into the aggregate creates dozens of generated chunks\n * that Node must parse and link before any extension factory can register. Node\n * can load the installed graph directly while still sharing its module cache.\n */\nconst STARTUP_EXTERNAL_PACKAGES = new Set([\n '@agimon-ai/foundation-port-registry',\n '@agimon-ai/foundation-process-registry',\n '@agimon-ai/log-sink-mcp',\n '@modelcontextprotocol/sdk',\n '@narumitw/pi-tui-kit',\n '@rmux/sdk',\n '@tursodatabase/database',\n '@tursodatabase/database-common',\n 'ajv',\n 'ajv-formats',\n 'gpt-tokenizer',\n 'highlight.js',\n 'hono',\n 'inversify',\n // open's CommonJS runtime resolves its bundled xdg-open helper through\n // __dirname, which is unavailable when Rolldown folds it into native ESM.\n 'open',\n 'reflect-metadata',\n 'zod-to-json-schema',\n 'yaml',\n 'zod',\n]);\nconst NODE_BUILTINS = new Set(builtinModules.flatMap((name) => [name, `node:${name}`]));\n\ninterface InputFingerprint {\n path: string;\n size: number;\n mtimeMs: number;\n}\n\ninterface CompiledExtensionManifest {\n version: string;\n output: string;\n artifacts: string[];\n}\n\ninterface ExtensionSetManifest extends CompiledExtensionManifest {\n entries: string[];\n inputs: InputFingerprint[];\n}\n\nexport interface CompileExtensionSetOptions {\n /** Persistent worktree artifact directory. Compiler manifests remain local. */\n outputDirectory?: string;\n /** Human-readable artifact prefix, normally the major-mode name. */\n outputName?: string;\n /** Canonical worktree root used to create relocatable logical input paths. */\n repositoryRoot?: string;\n /** Repository-level immutable cache shared by linked worktrees. */\n sharedCacheDirectory?: string;\n}\n\nfunction safeOutputName(value: string): string {\n const normalized = value\n .trim()\n .replace(/[^a-zA-Z0-9._-]+/g, '-')\n .replace(/^[-.]+|[-.]+$/g, '');\n return normalized || 'doom-set';\n}\n\nfunction packageRootOf(specifier: string): string | undefined {\n const slash = specifier.startsWith('@') ? specifier.indexOf('/', specifier.indexOf('/') + 1) : specifier.indexOf('/');\n return slash === -1 ? specifier : specifier.slice(0, slash);\n}\n\nfunction packageRootFromPath(target: string): string | undefined {\n let directory = path.dirname(target);\n while (true) {\n const manifestPath = path.join(directory, 'package.json');\n if (fs.existsSync(manifestPath)) {\n try {\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as unknown;\n if (isRecord(manifest) && typeof manifest.name === 'string') return manifest.name;\n } catch {\n return undefined;\n }\n }\n const parent = path.dirname(directory);\n if (parent === directory) return undefined;\n directory = parent;\n }\n}\n\ninterface LogicalInput {\n logicalPath: string;\n target: string;\n sourcePath?: string;\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync.native(absolute);\n } catch {\n return absolute;\n }\n}\n\nfunction isInside(directory: string, target: string): boolean {\n const relative = path.relative(path.resolve(directory), path.resolve(target));\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\nfunction externalLogicalPath(sourcePath: string): string {\n const identity = createHash('sha256').update(sourcePath).digest('hex');\n return `${EXTERNAL_INPUT_PREFIX}${identity}`;\n}\n\nfunction logicalInput(target: string, repositoryRoot: string): LogicalInput {\n const absolute = canonicalPath(target);\n if (isInside(repositoryRoot, absolute)) {\n return {\n logicalPath: `${REPOSITORY_INPUT_PREFIX}${path.relative(repositoryRoot, absolute).split(path.sep).join('/')}`,\n target: absolute,\n };\n }\n return {\n logicalPath: externalLogicalPath(absolute),\n target: absolute,\n sourcePath: absolute,\n };\n}\n\nfunction resolveLogicalInputPath(input: SharedBuildInput, repositoryRoot: string): string | undefined {\n if (input.logicalPath.startsWith(REPOSITORY_INPUT_PREFIX) && input.sourcePath === undefined) {\n const target = path.resolve(repositoryRoot, input.logicalPath.slice(REPOSITORY_INPUT_PREFIX.length));\n return isInside(repositoryRoot, target) ? target : undefined;\n }\n if (\n input.logicalPath.startsWith(EXTERNAL_INPUT_PREFIX) &&\n input.sourcePath !== undefined &&\n input.logicalPath === externalLogicalPath(input.sourcePath)\n ) {\n return input.sourcePath;\n }\n return undefined;\n}\n\nfunction externalResolver() {\n return {\n name: 'doom-external-absolute',\n async resolveId(\n this: { resolve: (id: string, importer?: string, options?: unknown) => Promise<{ id: string } | null> },\n specifier: string,\n importer: string | undefined,\n options: unknown,\n ): Promise<{ id: string; external: boolean } | null> {\n if (specifier.startsWith('.') || path.isAbsolute(specifier) || specifier.startsWith('node:')) return null;\n const renamed = renameScope(specifier);\n\n // Host packages are resolved from this module, the way the rest of the\n // harness resolves them, because an extension written against Pi's former\n // scope does not have the current one in its own dependency tree.\n const root = packageRootOf(renamed);\n if (root && HOSTED_PACKAGES.has(root)) {\n const hosted = optionalPackageEntry(renamed);\n if (hosted) return { id: hosted, external: true };\n }\n\n // Rolldown's resolver rather than `createRequire`, which cannot see an\n // ESM-only package that publishes no `require` condition and would send\n // Pi's own core into the bundle.\n const resolved = await this.resolve(renamed, importer, options);\n if (!resolved) return null;\n // A dependency that is itself TypeScript cannot be handed to Node either,\n // so it is bundled rather than externalised.\n if (isTypeScriptEntry(resolved.id)) return null;\n return { id: resolved.id, external: true };\n },\n };\n}\n\nfunction fingerprint(target: string): InputFingerprint | undefined {\n try {\n const stat = fs.statSync(target);\n if (!stat.isFile()) return undefined;\n return { path: target, size: stat.size, mtimeMs: stat.mtimeMs };\n } catch {\n return undefined;\n }\n}\n\nfunction artifactsAreFresh(manifest: CompiledExtensionManifest, version: string, output: string): boolean {\n return (\n manifest.version === version &&\n manifest.output === output &&\n fs.existsSync(output) &&\n manifest.artifacts.every((artifact) => fs.existsSync(artifact))\n );\n}\n\nfunction manifestIsFresh(manifest: ExtensionSetManifest, entries: string[]): boolean {\n if (!artifactsAreFresh(manifest, SET_CACHE_VERSION, manifest.output)) return false;\n if (JSON.stringify(manifest.entries) !== JSON.stringify(entries)) {\n return false;\n }\n return manifest.inputs.every((previous) => {\n const current = fingerprint(previous.path);\n return current?.size === previous.size && current.mtimeMs === previous.mtimeMs;\n });\n}\n\nfunction readCompiledExtensionManifest(manifestPath: string): CompiledExtensionManifest | undefined {\n try {\n const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as CompiledExtensionManifest;\n return parsed && typeof parsed.output === 'string' && Array.isArray(parsed.artifacts) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction readSetManifest(manifestPath: string): ExtensionSetManifest | undefined {\n const parsed = readCompiledExtensionManifest(manifestPath) as ExtensionSetManifest | undefined;\n return parsed && Array.isArray(parsed.entries) && Array.isArray(parsed.inputs) ? parsed : undefined;\n}\n\nfunction writeAtomic(target: string, contents: string | Uint8Array): void {\n fs.mkdirSync(path.dirname(target), { recursive: true });\n const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;\n fs.writeFileSync(temporary, contents);\n try {\n fs.renameSync(temporary, target);\n } catch (error) {\n fs.rmSync(temporary, { force: true });\n if (!fs.existsSync(target)) throw error;\n }\n}\n\ninterface CompiledGraph {\n entry: string;\n artifacts: string[];\n}\n\nfunction splitOutputOptions(entryFileNames: string) {\n return {\n format: 'esm' as const,\n sourcemap: false,\n minify: true,\n codeSplitting: true,\n hashCharacters: 'hex' as const,\n entryFileNames,\n chunkFileNames: `chunks/[name].[hash:${HASH_LENGTH}]${COMPILED_SUFFIX}`,\n assetFileNames: `assets/[name].[hash:${HASH_LENGTH}][extname]`,\n };\n}\n\nfunction artifactTarget(outputDirectory: string, artifact: OutputAsset | OutputChunk): string {\n const target = path.resolve(outputDirectory, artifact.fileName);\n const relative = path.relative(outputDirectory, target);\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n throw new Error(`Rolldown emitted an artifact outside the output directory: ${artifact.fileName}`);\n }\n return target;\n}\n\nfunction artifactMatches(target: string, contents: string | Uint8Array): boolean {\n try {\n const current = fs.readFileSync(target);\n const generated = typeof contents === 'string' ? Buffer.from(contents) : Buffer.from(contents);\n return current.equals(generated);\n } catch {\n return false;\n }\n}\n\n/** Writes referenced chunks first so a visible entry always has a complete graph. */\nfunction writeCompiledGraph(generated: RolldownOutput, outputDirectory: string): CompiledGraph {\n const entry = generated.output.find((artifact) => artifact.type === 'chunk' && artifact.isEntry);\n if (!entry || entry.type !== 'chunk') throw new Error('Rolldown produced no extension entry');\n const root = path.resolve(outputDirectory);\n const remaining = generated.output.filter((artifact) => artifact !== entry);\n for (const artifact of [...remaining, entry]) {\n const contents = artifact.type === 'chunk' ? artifact.code : artifact.source;\n const target = artifactTarget(root, artifact);\n if (!artifactMatches(target, contents)) writeAtomic(target, contents);\n }\n return {\n entry: artifactTarget(root, entry),\n artifacts: generated.output.map((artifact) => artifactTarget(root, artifact)).sort(),\n };\n}\n\nfunction sharedLookupKey(entries: readonly LogicalInput[], outputName: string): string {\n return createHash('sha256')\n .update(SHARED_BUILD_VERSION)\n .update(process.version)\n .update(process.platform)\n .update(process.arch)\n .update(outputName)\n .update(JSON.stringify(entries.map((entry) => entry.logicalPath)))\n .digest('hex');\n}\n\ninterface TokenizedInputs {\n shared: SharedBuildInput[];\n byTarget: Map<string, string>;\n}\n\nfunction tokenizedInputs(inputs: ReadonlySet<string>, repositoryRoot: string): TokenizedInputs {\n const logical: LogicalInput[] = [];\n for (const target of inputs) logical.push(logicalInput(target, repositoryRoot));\n logical.sort((left, right) => left.logicalPath.localeCompare(right.logicalPath));\n const shared: SharedBuildInput[] = [\n { logicalPath: REPOSITORY_INPUT_PREFIX, sha256: '', token: `${SHARED_PATH_TOKEN_PREFIX}ROOT__` },\n ];\n const byTarget = new Map<string, string>([[repositoryRoot, `${SHARED_PATH_TOKEN_PREFIX}ROOT__`]]);\n const tokenByLogicalPath = new Map<string, string>();\n logical.forEach((input, index) => {\n const existing = tokenByLogicalPath.get(input.logicalPath);\n const token = existing ?? `${SHARED_PATH_TOKEN_PREFIX}${String(index)}__`;\n if (!existing) {\n shared.push({\n logicalPath: input.logicalPath,\n sha256: contentSha256(fs.readFileSync(input.target)),\n token,\n ...(input.sourcePath ? { sourcePath: input.sourcePath } : {}),\n });\n tokenByLogicalPath.set(input.logicalPath, token);\n }\n byTarget.set(path.resolve(input.target), token);\n });\n for (const target of inputs) {\n const input = logicalInput(target, repositoryRoot);\n const token = tokenByLogicalPath.get(input.logicalPath);\n if (token) byTarget.set(path.resolve(target), token);\n }\n return { shared, byTarget };\n}\n\nfunction tokenizedArtifactContents(\n generated: RolldownOutput,\n tokens: ReadonlyMap<string, string>,\n): Map<string, string | Uint8Array> {\n const artifacts = new Map<string, string | Uint8Array>();\n const replacements = [...tokens.entries()].sort(([left], [right]) => right.length - left.length);\n for (const artifact of generated.output) {\n if (artifact.type === 'asset') {\n artifacts.set(artifact.fileName, typeof artifact.source === 'string' ? artifact.source : artifact.source);\n continue;\n }\n let source = artifact.code;\n for (const [target, token] of replacements) {\n source = source\n .replaceAll(pathToFileURL(target).href, `${token}:url`)\n .replaceAll(pathToImportSpecifier(target), token)\n .replaceAll(target, token);\n }\n artifacts.set(artifact.fileName, source);\n }\n return artifacts;\n}\n\nfunction writeLocalSetManifest(\n manifestPath: string,\n entries: string[],\n output: string,\n artifacts: string[],\n inputs: ReadonlySet<string>,\n): void {\n const inputFingerprints = [...inputs]\n .map(fingerprint)\n .filter((value): value is InputFingerprint => value !== undefined)\n .sort((left, right) => left.path.localeCompare(right.path));\n writeAtomic(\n manifestPath,\n `${JSON.stringify(\n { version: SET_CACHE_VERSION, entries, output, artifacts: [...artifacts].sort(), inputs: inputFingerprints },\n null,\n 2,\n )}\\n`,\n );\n}\n\nfunction materializeSharedSet(\n build: ResolvedSharedBuild,\n outputDirectory: string,\n manifestPath: string,\n entries: string[],\n): string {\n const output = materializeSharedBuild(build, outputDirectory);\n const inputs = new Set(build.replacements.values());\n const artifacts = build.manifest.artifacts.map((artifact) => path.join(outputDirectory, artifact.path));\n writeLocalSetManifest(manifestPath, entries, output, artifacts, inputs);\n return output;\n}\n\n/** Stable lookup key for one exact Pi load order. */\nexport function extensionSetKey(entries: readonly string[]): string {\n return createHash('sha256')\n .update(SET_CACHE_VERSION)\n .update(JSON.stringify(entries.map((entry) => path.resolve(entry))))\n .digest('hex')\n .slice(0, HASH_LENGTH);\n}\n\nfunction extensionSetSource(entries: readonly string[]): string {\n const helper = optionalPackageEntry(EXTENSION_SOURCE_HELPER);\n if (!helper) throw new Error(`Cannot resolve ${EXTENSION_SOURCE_HELPER}`);\n const loaders = entries.map((entry) => `() => import(${JSON.stringify(pathToImportSpecifier(entry))})`);\n const names = entries.map((entry) => JSON.stringify(entry));\n return [\n `import { withExtensionSource } from ${JSON.stringify(pathToImportSpecifier(helper))};`,\n `const loaders = [${loaders.join(',')}];`,\n `const names = [${names.join(',')}];`,\n 'function report(index, phase, startedAt, error) {',\n ' const elapsed = (performance.now() - startedAt).toFixed(1);',\n ' const detail = error instanceof Error ? \": \" + error.message : error === undefined ? \"\" : \": \" + String(error);',\n ' process.stderr.write(\"[doompi:timing] \" + phase + \" \" + elapsed + \"ms \" + names[index] + detail + \"\\\\n\");',\n '}',\n 'export default async function doompiExtensionSet(pi) {',\n ' const timings = process.env.DOOMPI_TIMING === \"1\" || process.env.PI_TIMING === \"1\";',\n ' const loaded = await Promise.all(loaders.map(async (load, index) => {',\n ' const startedAt = performance.now();',\n ' try {',\n ' const extension = await load();',\n ' if (timings) report(index, \"import\", startedAt);',\n ' return extension;',\n ' } catch (error) {',\n ' report(index, \"import-error\", startedAt, error);',\n ' return undefined;',\n ' }',\n ' }));',\n ' for (let index = 0; index < loaded.length; index += 1) {',\n ' const extension = loaded[index];',\n ' if (!extension) continue;',\n ' const startedAt = performance.now();',\n ' try {',\n ' const factory = extension.default;',\n \" if (typeof factory !== 'function') throw new Error('does not export an extension factory');\",\n ' await factory(withExtensionSource(pi, names[index]));',\n ' if (timings) report(index, \"factory\", startedAt);',\n ' } catch (error) {',\n ' report(index, \"factory-error\", startedAt, error);',\n ' }',\n ' }',\n '}',\n ].join('\\n');\n}\n\nfunction pathToImportSpecifier(target: string): string {\n // Rolldown accepts absolute POSIX paths directly. Windows needs a file URL,\n // but replacing separators is enough for its platform-independent parser.\n return path.resolve(target).split(path.sep).join('/');\n}\n\nfunction setExternalResolver(inputs: Set<string>) {\n return {\n name: 'doom-set-external',\n async resolveId(\n this: { resolve: (id: string, importer?: string, options?: unknown) => Promise<{ id: string } | null> },\n specifier: string,\n importer: string | undefined,\n options: unknown,\n ): Promise<{ id: string; external: boolean } | null> {\n if (specifier === VIRTUAL_SET_ENTRY) return null;\n if (NODE_BUILTINS.has(specifier) || specifier.startsWith('node:')) {\n return { id: specifier, external: true };\n }\n\n if (path.isAbsolute(specifier)) {\n const root = packageRootFromPath(specifier);\n if (root && (HOSTED_PACKAGES.has(root) || NATIVE_PACKAGES.has(root) || STARTUP_EXTERNAL_PACKAGES.has(root))) {\n inputs.add(specifier);\n return { id: specifier, external: true };\n }\n return null;\n }\n if (specifier.startsWith('.')) return null;\n\n const renamed = renameScope(specifier);\n const root = packageRootOf(renamed);\n if (root && HOSTED_PACKAGES.has(root)) {\n const hosted = optionalPackageEntry(renamed);\n if (hosted) {\n inputs.add(hosted);\n return { id: hosted, external: true };\n }\n }\n if (root === 'typebox') {\n const typebox = piExtensionDependencyEntry(renamed);\n if (typebox) {\n inputs.add(typebox);\n return { id: typebox, external: true };\n }\n }\n if (root && (NATIVE_PACKAGES.has(root) || STARTUP_EXTERNAL_PACKAGES.has(root))) {\n const resolved = await this.resolve(renamed, importer, options);\n if (resolved) {\n inputs.add(resolved.id);\n return { id: resolved.id, external: true };\n }\n }\n return null;\n },\n };\n}\n\nfunction inputCollector(inputs: Set<string>) {\n return {\n name: 'doom-set-inputs',\n load(id: string): null {\n if (path.isAbsolute(id) && fs.existsSync(id)) inputs.add(id);\n return null;\n },\n };\n}\n\n/** An unresolved bare import would otherwise become a broken dist external. */\nfunction failUnresolvedImport(_level: LogLevel, log: RolldownLog, handler: LogOrStringHandler): void {\n if (log.code === 'UNRESOLVED_IMPORT') handler('error', log);\n}\n\ninterface SourceRange {\n start: number;\n end: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction collectImportMetaUrlRanges(value: unknown, ranges: SourceRange[]): void {\n if (Array.isArray(value)) {\n for (const item of value) collectImportMetaUrlRanges(item, ranges);\n return;\n }\n if (!isRecord(value)) return;\n\n const object = value.object;\n const property = value.property;\n if (\n value.type === 'MemberExpression' &&\n value.computed === false &&\n typeof value.start === 'number' &&\n typeof value.end === 'number' &&\n isRecord(object) &&\n object.type === 'MetaProperty' &&\n isRecord(object.meta) &&\n object.meta.name === 'import' &&\n isRecord(object.property) &&\n object.property.name === 'meta' &&\n isRecord(property) &&\n property.name === 'url'\n ) {\n ranges.push({ start: value.start, end: value.end });\n return;\n }\n\n for (const child of Object.values(value)) collectImportMetaUrlRanges(child, ranges);\n}\n\n/** Keeps package-owned resource lookup anchored to each original source file. */\nfunction preserveImportMetaUrl() {\n return {\n name: 'doom-original-import-meta-url',\n transform(\n this: {\n parse: (source: string, options?: { lang?: 'js' | 'jsx' | 'ts' | 'tsx' }) => unknown;\n },\n source: string,\n id: string,\n ): string | null {\n if (!path.isAbsolute(id) || !source.includes('import.meta.url')) return null;\n const extension = path.extname(id).toLowerCase();\n const lang = extension === '.tsx' ? 'tsx' : extension === '.jsx' ? 'jsx' : isTypeScriptEntry(id) ? 'ts' : 'js';\n const ranges: SourceRange[] = [];\n collectImportMetaUrlRanges(this.parse(source, { lang }), ranges);\n if (ranges.length === 0) return null;\n const replacement = JSON.stringify(pathToFileURL(id).href);\n let transformed = source;\n for (const range of ranges.sort((left, right) => right.start - left.start)) {\n transformed = `${transformed.slice(0, range.start)}${replacement}${transformed.slice(range.end)}`;\n }\n return transformed;\n },\n };\n}\n\n/** Location of the lightweight input-fingerprint record for one extension set. */\nexport function extensionSetManifestPath(\n entries: readonly string[],\n cacheDirectory: string,\n options: CompileExtensionSetOptions = {},\n): string {\n const normalized = entries.map((entry) => path.resolve(entry));\n const key = extensionSetKey(normalized);\n const outputDirectory = path.resolve(options.outputDirectory ?? path.join(cacheDirectory, SET_DIRECTORY));\n const outputName = safeOutputName(options.outputName ?? 'doom-set');\n // One graph can be emitted under several named mode artifacts. Keep their\n // cache records separate without changing extensionSetKey, which is the\n // runtime lookup key for an exact activation order.\n const outputKey = createHash('sha256').update(outputDirectory).update(outputName).digest('hex').slice(0, HASH_LENGTH);\n return path.join(cacheDirectory, SET_DIRECTORY, `${key}.${outputKey}${MANIFEST_SUFFIX}`);\n}\n\n/**\n * Compiles an ordered extension set into one native ESM entry.\n *\n * The generated entry starts every module load together, then invokes factories\n * sequentially in activation order. Its manifest records every loaded module,\n * so a dependency edit invalidates the cache without running the compiler.\n */\nexport async function compileExtensionSet(\n entries: readonly string[],\n cacheDirectory: string,\n options: CompileExtensionSetOptions = {},\n): Promise<string> {\n if (entries.length === 0) throw new Error('Cannot compile an empty extension set');\n const normalized = entries.map((entry) => path.resolve(entry));\n const setDirectory = path.join(cacheDirectory, SET_DIRECTORY);\n fs.mkdirSync(setDirectory, { recursive: true });\n const outputDirectory = path.resolve(options.outputDirectory ?? setDirectory);\n const outputName = safeOutputName(options.outputName ?? 'doom-set');\n fs.mkdirSync(outputDirectory, { recursive: true });\n const manifestPath = extensionSetManifestPath(normalized, cacheDirectory, options);\n const previous = readSetManifest(manifestPath);\n if (previous && manifestIsFresh(previous, normalized)) return previous.output;\n\n const generate = async (): Promise<{ generated: RolldownOutput; inputs: Set<string> }> => {\n const inputs = new Set(normalized);\n const source = extensionSetSource(normalized);\n const { rolldown } = await import('rolldown');\n const build = await rolldown({\n input: VIRTUAL_SET_ENTRY,\n platform: 'node',\n plugins: [\n {\n name: 'doom-set-entry',\n resolveId(id: string): string | null {\n return id === VIRTUAL_SET_ENTRY ? id : null;\n },\n load(id: string): string | null {\n return id === VIRTUAL_SET_ENTRY ? source : null;\n },\n },\n setExternalResolver(inputs),\n preserveImportMetaUrl(),\n inputCollector(inputs),\n ],\n onLog: failUnresolvedImport,\n });\n try {\n return {\n generated: await build.generate(splitOutputOptions(`${outputName}.[hash:${HASH_LENGTH}]${COMPILED_SUFFIX}`)),\n inputs,\n };\n } finally {\n await build.close();\n }\n };\n\n const repositoryRoot = options.repositoryRoot ? canonicalPath(options.repositoryRoot) : undefined;\n\n if (repositoryRoot && options.sharedCacheDirectory) {\n const logicalEntries = normalized.map((entry) => logicalInput(entry, repositoryRoot));\n const lookupKey = sharedLookupKey(logicalEntries, outputName);\n const resolveInput = (input: SharedBuildInput): string | undefined =>\n resolveLogicalInputPath(input, repositoryRoot);\n return withSharedBuildLock(options.sharedCacheDirectory, lookupKey, async () => {\n const cached = findSharedBuild(options.sharedCacheDirectory as string, lookupKey, resolveInput);\n if (cached) return materializeSharedSet(cached, outputDirectory, manifestPath, normalized);\n\n const { generated, inputs } = await generate();\n const tokenized = tokenizedInputs(inputs, repositoryRoot);\n const artifacts = tokenizedArtifactContents(generated, tokenized.byTarget);\n if (\n [...artifacts.values()].some((contents) => typeof contents === 'string' && contents.includes(repositoryRoot))\n ) {\n throw new Error('Shared build graph still contains an absolute worktree path after tokenization');\n }\n const entry = generated.output.find((artifact) => artifact.type === 'chunk' && artifact.isEntry);\n if (!entry) throw new Error('Rolldown produced no shared extension entry');\n publishSharedBuild({\n cacheDirectory: options.sharedCacheDirectory as string,\n lookupKey,\n entry: entry.fileName,\n inputs: tokenized.shared,\n artifacts,\n });\n const published = findSharedBuild(options.sharedCacheDirectory as string, lookupKey, resolveInput);\n if (!published) throw new Error('Published shared build could not be verified');\n return materializeSharedSet(published, outputDirectory, manifestPath, normalized);\n });\n }\n\n const { generated, inputs } = await generate();\n const compiled = writeCompiledGraph(generated, outputDirectory);\n writeLocalSetManifest(manifestPath, normalized, compiled.entry, compiled.artifacts, inputs);\n return compiled.entry;\n}\n\n/**\n * Bundles `entry` into `cacheDirectory` and returns the compiled path.\n *\n * Non-TypeScript entries are returned untouched. A cache hit skips the build.\n */\nexport async function compileExtension(entry: string, cacheDirectory: string): Promise<string> {\n if (!isTypeScriptEntry(entry)) return entry;\n\n fs.mkdirSync(cacheDirectory, { recursive: true });\n const outputName = compiledName(entry, cacheKey(entry));\n const output = path.join(cacheDirectory, outputName);\n const manifestPath = `${output}${MANIFEST_SUFFIX}`;\n const previous = readCompiledExtensionManifest(manifestPath);\n if (previous && artifactsAreFresh(previous, CACHE_VERSION, output)) return output;\n\n // Rolldown loads a native module and a sizeable parser graph. Sync is the\n // only command that needs it, and a cache hit does not, so keep it off every\n // other CLI startup path.\n const { rolldown } = await import('rolldown');\n const build = await rolldown({\n input: entry,\n platform: 'node',\n plugins: [externalResolver(), preserveImportMetaUrl()],\n onLog: failUnresolvedImport,\n });\n let compiled: CompiledGraph;\n try {\n const generated = await build.generate(splitOutputOptions(outputName));\n compiled = writeCompiledGraph(generated, cacheDirectory);\n } finally {\n await build.close();\n }\n writeAtomic(\n manifestPath,\n `${JSON.stringify({ version: CACHE_VERSION, output, artifacts: compiled.artifacts }, null, 2)}\\n`,\n );\n return output;\n}\n\n/**\n * Compiles every TypeScript entry in a resolution map, leaving the rest as-is.\n *\n * A failure is not fatal: the original path is kept so Pi falls back to loading\n * the TypeScript through jiti, which is what it did before this existed.\n */\nexport async function compileResolvedEntries(\n resolved: Record<string, string>,\n cacheDirectory: string,\n): Promise<Record<string, string>> {\n fs.mkdirSync(cacheDirectory, { recursive: true });\n const compiled: Record<string, string> = {};\n for (const [key, target] of Object.entries(resolved)) {\n try {\n compiled[key] = await compileExtension(target, cacheDirectory);\n } catch {\n compiled[key] = target;\n }\n }\n return compiled;\n}\n"],"mappings":"4bAmCA,MAAM,EAAsB,CAAC,MAAO,OAAQ,OAAO,CAC7C,EAAkB,OAIlB,EAAgB,OAEhB,EAAoB,yBACpB,EAA0B,qCAE1B,EAA2B,iBAC3B,EAA0B,QAC1B,EAAwB,YAGxB,EAAqD,CACzD,gCAAiC,kCACjC,uBAAwB,yBACxB,sBAAuB,wBACvB,oBAAqB,UACtB,CAED,SAAgB,EAAkB,EAAwB,CACxD,OAAO,EAAoB,KAAM,GAAW,EAAM,SAAS,EAAO,CAAC,CAGrE,SAAS,EAAY,EAA2B,CAC9C,IAAK,GAAM,CAAC,EAAQ,KAAU,OAAO,QAAQ,EAAiB,CAAE,CAC9D,GAAI,IAAc,EAAQ,OAAO,EACjC,GAAI,EAAU,WAAW,GAAG,EAAO,GAAG,CAAE,MAAO,GAAG,IAAQ,EAAU,MAAM,EAAO,OAAO,GAE1F,OAAO,EA2BT,MAAM,EAAkB,IAAI,IAAI,CAAC,kCAAmC,yBAA0B,wBAAwB,CAAC,CASvH,SAAS,EAA2B,EAAuC,CACzE,IAAM,EAAU,EAAqB,kCAAkC,CAClE,KACL,GAAI,CACF,OAAO,EAAc,EAAQ,CAAC,QAAQ,EAAU,MAC1C,CACN,QAKJ,MAAM,EAAkB,IAAI,IAAI,CAC9B,kBACA,mBACA,iBACA,SACA,WACA,cACA,SACA,WACA,mBACA,aACA,kBACA,YACA,QAKA,aACD,CAAC,CASI,EAA4B,IAAI,IAAI,CACxC,sCACA,yCACA,0BACA,4BACA,uBACA,YACA,0BACA,iCACA,MACA,cACA,gBACA,eACA,OACA,YAGA,OACA,mBACA,qBACA,OACA,MACD,CAAC,CACI,EAAgB,IAAI,IAAI,EAAe,QAAS,GAAS,CAAC,EAAM,QAAQ,IAAO,CAAC,CAAC,CA8BvF,SAAS,EAAe,EAAuB,CAK7C,OAJmB,EAChB,MAAM,CACN,QAAQ,oBAAqB,IAAI,CACjC,QAAQ,iBAAkB,GACZ,EAAI,WAGvB,SAAS,EAAc,EAAuC,CAC5D,IAAM,EAAQ,EAAU,WAAW,IAAI,CAAG,EAAU,QAAQ,IAAK,EAAU,QAAQ,IAAI,CAAG,EAAE,CAAG,EAAU,QAAQ,IAAI,CACrH,OAAO,IAAU,GAAK,EAAY,EAAU,MAAM,EAAG,EAAM,CAG7D,SAAS,EAAoB,EAAoC,CAC/D,IAAI,EAAY,EAAK,QAAQ,EAAO,CACpC,OAAa,CACX,IAAM,EAAe,EAAK,KAAK,EAAW,eAAe,CACzD,GAAI,EAAG,WAAW,EAAa,CAC7B,GAAI,CACF,IAAM,EAAW,KAAK,MAAM,EAAG,aAAa,EAAc,OAAO,CAAC,CAClE,GAAI,EAAS,EAAS,EAAI,OAAO,EAAS,MAAS,SAAU,OAAO,EAAS,UACvE,CACN,OAGJ,IAAM,EAAS,EAAK,QAAQ,EAAU,CACtC,GAAI,IAAW,EAAW,OAC1B,EAAY,GAUhB,SAAS,EAAc,EAAwB,CAC7C,IAAM,EAAW,EAAK,QAAQ,EAAO,CACrC,GAAI,CACF,OAAO,EAAG,aAAa,OAAO,EAAS,MACjC,CACN,OAAO,GAIX,SAAS,EAAS,EAAmB,EAAyB,CAC5D,IAAM,EAAW,EAAK,SAAS,EAAK,QAAQ,EAAU,CAAE,EAAK,QAAQ,EAAO,CAAC,CAC7E,OAAO,IAAa,IAAO,CAAC,EAAS,WAAW,KAAK,EAAI,CAAC,EAAK,WAAW,EAAS,CAGrF,SAAS,EAAoB,EAA4B,CAEvD,MAAO,GAAG,IADO,EAAW,SAAS,CAAC,OAAO,EAAW,CAAC,OAAO,MACtB,GAG5C,SAAS,EAAa,EAAgB,EAAsC,CAC1E,IAAM,EAAW,EAAc,EAAO,CAOtC,OANI,EAAS,EAAgB,EAAS,CAC7B,CACL,YAAa,GAAG,IAA0B,EAAK,SAAS,EAAgB,EAAS,CAAC,MAAM,EAAK,IAAI,CAAC,KAAK,IAAI,GAC3G,OAAQ,EACT,CAEI,CACL,YAAa,EAAoB,EAAS,CAC1C,OAAQ,EACR,WAAY,EACb,CAGH,SAAS,EAAwB,EAAyB,EAA4C,CACpG,GAAI,EAAM,YAAY,WAAW,EAAwB,EAAI,EAAM,aAAe,IAAA,GAAW,CAC3F,IAAM,EAAS,EAAK,QAAQ,EAAgB,EAAM,YAAY,MAAM,EAA+B,CAAC,CACpG,OAAO,EAAS,EAAgB,EAAO,CAAG,EAAS,IAAA,GAErD,GACE,EAAM,YAAY,WAAW,EAAsB,EACnD,EAAM,aAAe,IAAA,IACrB,EAAM,cAAgB,EAAoB,EAAM,WAAW,CAE3D,OAAO,EAAM,WAuCjB,SAAS,EAAY,EAA8C,CACjE,GAAI,CACF,IAAM,EAAO,EAAG,SAAS,EAAO,CAEhC,OADK,EAAK,QAAQ,CACX,CAAE,KAAM,EAAQ,KAAM,EAAK,KAAM,QAAS,EAAK,QAAS,CAD3C,YAEd,CACN,QAIJ,SAAS,EAAkB,EAAqC,EAAiB,EAAyB,CACxG,OACE,EAAS,UAAY,GACrB,EAAS,SAAW,GACpB,EAAG,WAAW,EAAO,EACrB,EAAS,UAAU,MAAO,GAAa,EAAG,WAAW,EAAS,CAAC,CAInE,SAAS,GAAgB,EAAgC,EAA4B,CAKnF,MAJI,CAAC,EAAkB,EAAU,MAAmB,EAAS,OAAO,EAChE,KAAK,UAAU,EAAS,QAAQ,GAAK,KAAK,UAAU,EAAQ,CACvD,GAEF,EAAS,OAAO,MAAO,GAAa,CACzC,IAAM,EAAU,EAAY,EAAS,KAAK,CAC1C,OAAO,GAAS,OAAS,EAAS,MAAQ,EAAQ,UAAY,EAAS,SACvE,CAGJ,SAAS,GAA8B,EAA6D,CAClG,GAAI,CACF,IAAM,EAAS,KAAK,MAAM,EAAG,aAAa,EAAc,OAAO,CAAC,CAChE,OAAO,GAAU,OAAO,EAAO,QAAW,UAAY,MAAM,QAAQ,EAAO,UAAU,CAAG,EAAS,IAAA,QAC3F,CACN,QAIJ,SAAS,GAAgB,EAAwD,CAC/E,IAAM,EAAS,GAA8B,EAAa,CAC1D,OAAO,GAAU,MAAM,QAAQ,EAAO,QAAQ,EAAI,MAAM,QAAQ,EAAO,OAAO,CAAG,EAAS,IAAA,GAG5F,SAAS,EAAY,EAAgB,EAAqC,CACxE,EAAG,UAAU,EAAK,QAAQ,EAAO,CAAE,CAAE,UAAW,GAAM,CAAC,CACvD,IAAM,EAAY,GAAG,EAAO,GAAG,QAAQ,IAAI,GAAG,KAAK,KAAK,CAAC,MACzD,EAAG,cAAc,EAAW,EAAS,CACrC,GAAI,CACF,EAAG,WAAW,EAAW,EAAO,OACzB,EAAO,CAEd,GADA,EAAG,OAAO,EAAW,CAAE,MAAO,GAAM,CAAC,CACjC,CAAC,EAAG,WAAW,EAAO,CAAE,MAAM,GAStC,SAAS,EAAmB,EAAwB,CAClD,MAAO,CACL,OAAQ,MACR,UAAW,GACX,OAAQ,GACR,cAAe,GACf,eAAgB,MAChB,iBACA,eAAgB,0BAAsC,IACtD,eAAgB,mCACjB,CAGH,SAAS,EAAe,EAAyB,EAA6C,CAC5F,IAAM,EAAS,EAAK,QAAQ,EAAiB,EAAS,SAAS,CACzD,EAAW,EAAK,SAAS,EAAiB,EAAO,CACvD,GAAI,EAAS,WAAW,KAAK,EAAI,EAAK,WAAW,EAAS,CACxD,MAAU,MAAM,8DAA8D,EAAS,WAAW,CAEpG,OAAO,EAGT,SAAS,EAAgB,EAAgB,EAAwC,CAC/E,GAAI,CACF,IAAM,EAAU,EAAG,aAAa,EAAO,CACjC,EAA2C,OAAO,KAAK,EAAS,CACtE,OAAO,EAAQ,OAAO,EAAU,MAC1B,CACN,MAAO,IAKX,SAAS,EAAmB,EAA2B,EAAwC,CAC7F,IAAM,EAAQ,EAAU,OAAO,KAAM,GAAa,EAAS,OAAS,SAAW,EAAS,QAAQ,CAChG,GAAI,CAAC,GAAS,EAAM,OAAS,QAAS,MAAU,MAAM,uCAAuC,CAC7F,IAAM,EAAO,EAAK,QAAQ,EAAgB,CACpC,EAAY,EAAU,OAAO,OAAQ,GAAa,IAAa,EAAM,CAC3E,IAAK,IAAM,IAAY,CAAC,GAAG,EAAW,EAAM,CAAE,CAC5C,IAAM,EAAW,EAAS,OAAS,QAAU,EAAS,KAAO,EAAS,OAChE,EAAS,EAAe,EAAM,EAAS,CACxC,EAAgB,EAAQ,EAAS,EAAE,EAAY,EAAQ,EAAS,CAEvE,MAAO,CACL,MAAO,EAAe,EAAM,EAAM,CAClC,UAAW,EAAU,OAAO,IAAK,GAAa,EAAe,EAAM,EAAS,CAAC,CAAC,MAAM,CACrF,CAGH,SAAS,EAAgB,EAAkC,EAA4B,CACrF,OAAO,EAAW,SAAS,CACxB,OAAO,0BAAqB,CAC5B,OAAO,QAAQ,QAAQ,CACvB,OAAO,QAAQ,SAAS,CACxB,OAAO,QAAQ,KAAK,CACpB,OAAO,EAAW,CAClB,OAAO,KAAK,UAAU,EAAQ,IAAK,GAAU,EAAM,YAAY,CAAC,CAAC,CACjE,OAAO,MAAM,CAQlB,SAAS,EAAgB,EAA6B,EAAyC,CAC7F,IAAM,EAA0B,EAAE,CAClC,IAAK,IAAM,KAAU,EAAQ,EAAQ,KAAK,EAAa,EAAQ,EAAe,CAAC,CAC/E,EAAQ,MAAM,EAAM,IAAU,EAAK,YAAY,cAAc,EAAM,YAAY,CAAC,CAChF,IAAM,EAA6B,CACjC,CAAE,YAAa,EAAyB,OAAQ,GAAI,MAAO,GAAG,EAAyB,QAAS,CACjG,CACK,EAAW,IAAI,IAAoB,CAAC,CAAC,EAAgB,GAAG,EAAyB,QAAQ,CAAC,CAAC,CAC3F,EAAqB,IAAI,IAC/B,EAAQ,SAAS,EAAO,IAAU,CAChC,IAAM,EAAW,EAAmB,IAAI,EAAM,YAAY,CACpD,EAAQ,GAAY,GAAG,IAA2B,OAAO,EAAM,CAAC,IACjE,IACH,EAAO,KAAK,CACV,YAAa,EAAM,YACnB,OAAQ,EAAc,EAAG,aAAa,EAAM,OAAO,CAAC,CACpD,QACA,GAAI,EAAM,WAAa,CAAE,WAAY,EAAM,WAAY,CAAG,EAAE,CAC7D,CAAC,CACF,EAAmB,IAAI,EAAM,YAAa,EAAM,EAElD,EAAS,IAAI,EAAK,QAAQ,EAAM,OAAO,CAAE,EAAM,EAC/C,CACF,IAAK,IAAM,KAAU,EAAQ,CAC3B,IAAM,EAAQ,EAAa,EAAQ,EAAe,CAC5C,EAAQ,EAAmB,IAAI,EAAM,YAAY,CACnD,GAAO,EAAS,IAAI,EAAK,QAAQ,EAAO,CAAE,EAAM,CAEtD,MAAO,CAAE,SAAQ,WAAU,CAG7B,SAAS,EACP,EACA,EACkC,CAClC,IAAM,EAAY,IAAI,IAChB,EAAe,CAAC,GAAG,EAAO,SAAS,CAAC,CAAC,MAAM,CAAC,GAAO,CAAC,KAAW,EAAM,OAAS,EAAK,OAAO,CAChG,IAAK,IAAM,KAAY,EAAU,OAAQ,CACvC,GAAI,EAAS,OAAS,QAAS,CAC7B,EAAU,IAAI,EAAS,UAAiB,EAAS,OAAsB,EAAS,QAAyB,CACzG,SAEF,IAAI,EAAS,EAAS,KACtB,IAAK,GAAM,CAAC,EAAQ,KAAU,EAC5B,EAAS,EACN,WAAW,EAAc,EAAO,CAAC,KAAM,GAAG,EAAM,MAAM,CACtD,WAAW,EAAsB,EAAO,CAAE,EAAM,CAChD,WAAW,EAAQ,EAAM,CAE9B,EAAU,IAAI,EAAS,SAAU,EAAO,CAE1C,OAAO,EAGT,SAAS,EACP,EACA,EACA,EACA,EACA,EACM,CACN,IAAM,EAAoB,CAAC,GAAG,EAAO,CAClC,IAAI,EAAY,CAChB,OAAQ,GAAqC,IAAU,IAAA,GAAU,CACjE,MAAM,EAAM,IAAU,EAAK,KAAK,cAAc,EAAM,KAAK,CAAC,CAC7D,EACE,EACA,GAAG,KAAK,UACN,CAAE,QAAS,MAAmB,UAAS,SAAQ,UAAW,CAAC,GAAG,EAAU,CAAC,MAAM,CAAE,OAAQ,EAAmB,CAC5G,KACA,EACD,CAAC,IACH,CAGH,SAAS,EACP,EACA,EACA,EACA,EACQ,CACR,IAAM,EAAS,EAAuB,EAAO,EAAgB,CACvD,EAAS,IAAI,IAAI,EAAM,aAAa,QAAQ,CAAC,CAGnD,OADA,EAAsB,EAAc,EAAS,EAD3B,EAAM,SAAS,UAAU,IAAK,GAAa,EAAK,KAAK,EAAiB,EAAS,KAAK,CACxC,CAAE,EAAO,CAChE,EAIT,SAAgB,EAAgB,EAAoC,CAClE,OAAO,EAAW,SAAS,CACxB,OAAO,MAAkB,CACzB,OAAO,KAAK,UAAU,EAAQ,IAAK,GAAU,EAAK,QAAQ,EAAM,CAAC,CAAC,CAAC,CACnE,OAAO,MAAM,CACb,MAAM,EAAG,GAAY,CAG1B,SAAS,EAAmB,EAAoC,CAC9D,IAAM,EAAS,EAAqB,EAAwB,CAC5D,GAAI,CAAC,EAAQ,MAAU,MAAM,kBAAkB,IAA0B,CACzE,IAAM,EAAU,EAAQ,IAAK,GAAU,gBAAgB,KAAK,UAAU,EAAsB,EAAM,CAAC,CAAC,GAAG,CACjG,EAAQ,EAAQ,IAAK,GAAU,KAAK,UAAU,EAAM,CAAC,CAC3D,MAAO,CACL,uCAAuC,KAAK,UAAU,EAAsB,EAAO,CAAC,CAAC,GACrF,oBAAoB,EAAQ,KAAK,IAAI,CAAC,IACtC,kBAAkB,EAAM,KAAK,IAAI,CAAC,IAClC,oDACA,gEACA,oHACA,8GACA,IACA,yDACA,wFACA,0EACA,2CACA,YACA,wCACA,yDACA,0BACA,wBACA,yDACA,0BACA,QACA,SACA,6DACA,uCACA,gCACA,2CACA,YACA,2CACA,oGACA,8DACA,0DACA,wBACA,0DACA,QACA,MACA,IACD,CAAC,KAAK;EAAK,CAGd,SAAS,EAAsB,EAAwB,CAGrD,OAAO,EAAK,QAAQ,EAAO,CAAC,MAAM,EAAK,IAAI,CAAC,KAAK,IAAI,CAGvD,SAAS,GAAoB,EAAqB,CAChD,MAAO,CACL,KAAM,oBACN,MAAM,UAEJ,EACA,EACA,EACmD,CACnD,GAAI,IAAc,EAAmB,OAAO,KAC5C,GAAI,EAAc,IAAI,EAAU,EAAI,EAAU,WAAW,QAAQ,CAC/D,MAAO,CAAE,GAAI,EAAW,SAAU,GAAM,CAG1C,GAAI,EAAK,WAAW,EAAU,CAAE,CAC9B,IAAM,EAAO,EAAoB,EAAU,CAK3C,OAJI,IAAS,EAAgB,IAAI,EAAK,EAAI,EAAgB,IAAI,EAAK,EAAI,EAA0B,IAAI,EAAK,GACxG,EAAO,IAAI,EAAU,CACd,CAAE,GAAI,EAAW,SAAU,GAAM,EAEnC,KAET,GAAI,EAAU,WAAW,IAAI,CAAE,OAAO,KAEtC,IAAM,EAAU,EAAY,EAAU,CAChC,EAAO,EAAc,EAAQ,CACnC,GAAI,GAAQ,EAAgB,IAAI,EAAK,CAAE,CACrC,IAAM,EAAS,EAAqB,EAAQ,CAC5C,GAAI,EAEF,OADA,EAAO,IAAI,EAAO,CACX,CAAE,GAAI,EAAQ,SAAU,GAAM,CAGzC,GAAI,IAAS,UAAW,CACtB,IAAM,EAAU,EAA2B,EAAQ,CACnD,GAAI,EAEF,OADA,EAAO,IAAI,EAAQ,CACZ,CAAE,GAAI,EAAS,SAAU,GAAM,CAG1C,GAAI,IAAS,EAAgB,IAAI,EAAK,EAAI,EAA0B,IAAI,EAAK,EAAG,CAC9E,IAAM,EAAW,MAAM,KAAK,QAAQ,EAAS,EAAU,EAAQ,CAC/D,GAAI,EAEF,OADA,EAAO,IAAI,EAAS,GAAG,CAChB,CAAE,GAAI,EAAS,GAAI,SAAU,GAAM,CAG9C,OAAO,MAEV,CAGH,SAAS,GAAe,EAAqB,CAC3C,MAAO,CACL,KAAM,kBACN,KAAK,EAAkB,CAErB,OADI,EAAK,WAAW,EAAG,EAAI,EAAG,WAAW,EAAG,EAAE,EAAO,IAAI,EAAG,CACrD,MAEV,CAIH,SAAS,GAAqB,EAAkB,EAAkB,EAAmC,CAC/F,EAAI,OAAS,qBAAqB,EAAQ,QAAS,EAAI,CAQ7D,SAAS,EAAS,EAAkD,CAClE,OAAO,OAAO,GAAU,YAAY,GAAkB,CAAC,MAAM,QAAQ,EAAM,CAG7E,SAAS,EAA2B,EAAgB,EAA6B,CAC/E,GAAI,MAAM,QAAQ,EAAM,CAAE,CACxB,IAAK,IAAM,KAAQ,EAAO,EAA2B,EAAM,EAAO,CAClE,OAEF,GAAI,CAAC,EAAS,EAAM,CAAE,OAEtB,IAAM,EAAS,EAAM,OACf,EAAW,EAAM,SACvB,GACE,EAAM,OAAS,oBACf,EAAM,WAAa,IACnB,OAAO,EAAM,OAAU,UACvB,OAAO,EAAM,KAAQ,UACrB,EAAS,EAAO,EAChB,EAAO,OAAS,gBAChB,EAAS,EAAO,KAAK,EACrB,EAAO,KAAK,OAAS,UACrB,EAAS,EAAO,SAAS,EACzB,EAAO,SAAS,OAAS,QACzB,EAAS,EAAS,EAClB,EAAS,OAAS,MAClB,CACA,EAAO,KAAK,CAAE,MAAO,EAAM,MAAO,IAAK,EAAM,IAAK,CAAC,CACnD,OAGF,IAAK,IAAM,KAAS,OAAO,OAAO,EAAM,CAAE,EAA2B,EAAO,EAAO,CAIrF,SAAS,IAAwB,CAC/B,MAAO,CACL,KAAM,gCACN,UAIE,EACA,EACe,CACf,GAAI,CAAC,EAAK,WAAW,EAAG,EAAI,CAAC,EAAO,SAAS,kBAAkB,CAAE,OAAO,KACxE,IAAM,EAAY,EAAK,QAAQ,EAAG,CAAC,aAAa,CAC1C,EAAO,IAAc,OAAS,MAAQ,IAAc,OAAS,MAAQ,EAAkB,EAAG,CAAG,KAAO,KACpG,EAAwB,EAAE,CAEhC,GADA,EAA2B,KAAK,MAAM,EAAQ,CAAE,OAAM,CAAC,CAAE,EAAO,CAC5D,EAAO,SAAW,EAAG,OAAO,KAChC,IAAM,EAAc,KAAK,UAAU,EAAc,EAAG,CAAC,KAAK,CACtD,EAAc,EAClB,IAAK,IAAM,KAAS,EAAO,MAAM,EAAM,IAAU,EAAM,MAAQ,EAAK,MAAM,CACxE,EAAc,GAAG,EAAY,MAAM,EAAG,EAAM,MAAM,GAAG,IAAc,EAAY,MAAM,EAAM,IAAI,GAEjG,OAAO,GAEV,CAIH,SAAgB,EACd,EACA,EACA,EAAsC,EAAE,CAChC,CAER,IAAM,EAAM,EADO,EAAQ,IAAK,GAAU,EAAK,QAAQ,EAAM,CACvB,CAAC,CACjC,EAAkB,EAAK,QAAQ,EAAQ,iBAAmB,EAAK,KAAK,EAAgB,EAAc,CAAC,CACnG,EAAa,EAAe,EAAQ,YAAc,WAAW,CAI7D,EAAY,EAAW,SAAS,CAAC,OAAO,EAAgB,CAAC,OAAO,EAAW,CAAC,OAAO,MAAM,CAAC,MAAM,EAAG,GAAY,CACrH,OAAO,EAAK,KAAK,EAAgB,EAAe,GAAG,EAAI,GAAG,SAA8B,CAU1F,eAAsB,GACpB,EACA,EACA,EAAsC,EAAE,CACvB,CACjB,GAAI,EAAQ,SAAW,EAAG,MAAU,MAAM,wCAAwC,CAClF,IAAM,EAAa,EAAQ,IAAK,GAAU,EAAK,QAAQ,EAAM,CAAC,CACxD,EAAe,EAAK,KAAK,EAAgB,EAAc,CAC7D,EAAG,UAAU,EAAc,CAAE,UAAW,GAAM,CAAC,CAC/C,IAAM,EAAkB,EAAK,QAAQ,EAAQ,iBAAmB,EAAa,CACvE,EAAa,EAAe,EAAQ,YAAc,WAAW,CACnE,EAAG,UAAU,EAAiB,CAAE,UAAW,GAAM,CAAC,CAClD,IAAM,EAAe,EAAyB,EAAY,EAAgB,EAAQ,CAC5E,EAAW,GAAgB,EAAa,CAC9C,GAAI,GAAY,GAAgB,EAAU,EAAW,CAAE,OAAO,EAAS,OAEvE,IAAM,EAAW,SAAyE,CACxF,IAAM,EAAS,IAAI,IAAI,EAAW,CAC5B,EAAS,EAAmB,EAAW,CACvC,CAAE,YAAa,MAAM,OAAO,YAC5B,EAAQ,MAAM,EAAS,CAC3B,MAAO,EACP,SAAU,OACV,QAAS,CACP,CACE,KAAM,iBACN,UAAU,EAA2B,CACnC,OAAO,IAAO,EAAoB,EAAK,MAEzC,KAAK,EAA2B,CAC9B,OAAO,IAAO,EAAoB,EAAS,MAE9C,CACD,GAAoB,EAAO,CAC3B,IAAuB,CACvB,GAAe,EAAO,CACvB,CACD,MAAO,GACR,CAAC,CACF,GAAI,CACF,MAAO,CACL,UAAW,MAAM,EAAM,SAAS,EAAmB,GAAG,EAAW,YAAwB,IAAkB,CAAC,CAC5G,SACD,QACO,CACR,MAAM,EAAM,OAAO,GAIjB,EAAiB,EAAQ,eAAiB,EAAc,EAAQ,eAAe,CAAG,IAAA,GAExF,GAAI,GAAkB,EAAQ,qBAAsB,CAElD,IAAM,EAAY,EADK,EAAW,IAAK,GAAU,EAAa,EAAO,EAAe,CACpC,CAAE,EAAW,CACvD,EAAgB,GACpB,EAAwB,EAAO,EAAe,CAChD,OAAO,EAAoB,EAAQ,qBAAsB,EAAW,SAAY,CAC9E,IAAM,EAAS,EAAgB,EAAQ,qBAAgC,EAAW,EAAa,CAC/F,GAAI,EAAQ,OAAO,EAAqB,EAAQ,EAAiB,EAAc,EAAW,CAE1F,GAAM,CAAE,YAAW,UAAW,MAAM,GAAU,CACxC,EAAY,EAAgB,EAAQ,EAAe,CACnD,EAAY,EAA0B,EAAW,EAAU,SAAS,CAC1E,GACE,CAAC,GAAG,EAAU,QAAQ,CAAC,CAAC,KAAM,GAAa,OAAO,GAAa,UAAY,EAAS,SAAS,EAAe,CAAC,CAE7G,MAAU,MAAM,iFAAiF,CAEnG,IAAM,EAAQ,EAAU,OAAO,KAAM,GAAa,EAAS,OAAS,SAAW,EAAS,QAAQ,CAChG,GAAI,CAAC,EAAO,MAAU,MAAM,8CAA8C,CAC1E,EAAmB,CACjB,eAAgB,EAAQ,qBACxB,YACA,MAAO,EAAM,SACb,OAAQ,EAAU,OAClB,YACD,CAAC,CACF,IAAM,EAAY,EAAgB,EAAQ,qBAAgC,EAAW,EAAa,CAClG,GAAI,CAAC,EAAW,MAAU,MAAM,+CAA+C,CAC/E,OAAO,EAAqB,EAAW,EAAiB,EAAc,EAAW,EACjF,CAGJ,GAAM,CAAE,YAAW,UAAW,MAAM,GAAU,CACxC,EAAW,EAAmB,EAAW,EAAgB,CAE/D,OADA,EAAsB,EAAc,EAAY,EAAS,MAAO,EAAS,UAAW,EAAO,CACpF,EAAS"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`../_virtual/_rolldown/runtime.cjs`);let t=require(`node:url`),n=require(`node:path`);n=e.__toESM(n,1);let r=require(`node:fs`);r=e.__toESM(r,1);let i=require(`node:crypto`);i=e.__toESM(i,1);const a=`manifest.json`,o=/^[a-f0-9]{64}$/u;function
|
|
1
|
+
const e=require(`../_virtual/_rolldown/runtime.cjs`);let t=require(`node:url`),n=require(`node:path`);n=e.__toESM(n,1);let r=require(`node:fs`);r=e.__toESM(r,1);let i=require(`node:crypto`);i=e.__toESM(i,1);const a=`manifest.json`,o=`owner.json`,s=/^[a-f0-9]{64}$/u;function c(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function l(e){return i.default.createHash(`sha256`).update(e).digest(`hex`)}function u(e){if(e===``||n.default.isAbsolute(e))return;let t=n.default.normalize(e);return t===`..`||t.startsWith(`..${n.default.sep}`)?void 0:t}function d(e){if(!c(e)||e.schema!==1||typeof e.key!=`string`||!s.test(e.key)||typeof e.lookupKey!=`string`||!s.test(e.lookupKey)||typeof e.entry!=`string`||!Array.isArray(e.inputs)||!Array.isArray(e.artifacts))return;let t=[];for(let r of e.inputs){if(!c(r)||typeof r.logicalPath!=`string`||typeof r.sha256!=`string`||r.sha256!==``&&!s.test(r.sha256)||typeof r.token!=`string`||r.sourcePath!==void 0&&(typeof r.sourcePath!=`string`||!n.default.isAbsolute(r.sourcePath)))return;t.push({logicalPath:r.logicalPath,sha256:r.sha256,token:r.token,...typeof r.sourcePath==`string`?{sourcePath:r.sourcePath}:{}})}let r=[];for(let t of e.artifacts){if(!c(t)||typeof t.path!=`string`||typeof t.sha256!=`string`||!s.test(t.sha256)||!u(t.path))return;r.push({path:t.path,sha256:t.sha256})}if(u(e.entry))return{schema:1,key:e.key,lookupKey:e.lookupKey,entry:e.entry,inputs:t,artifacts:r}}function f(e){try{return JSON.parse(r.default.readFileSync(e,`utf8`))}catch{return}}function p(e,t){r.default.mkdirSync(n.default.dirname(e),{recursive:!0,mode:448});let a=`${e}.${process.pid}.${i.default.randomUUID()}.tmp`;try{r.default.writeFileSync(a,t,{mode:384}),r.default.renameSync(a,e),r.default.chmodSync(e,384)}catch(e){throw r.default.rmSync(a,{force:!0}),e}}function m(e,t){return n.default.join(e,`index`,`${t}.json`)}function h(e,t){let n=f(m(e,t));return!c(n)||n.schema!==1||!Array.isArray(n.keys)?{schema:1,keys:[]}:{schema:1,keys:n.keys.filter(e=>typeof e==`string`)}}function g(e,t,n){let r=[n,...h(e,t).keys.filter(e=>e!==n)];p(m(e,t),`${JSON.stringify({schema:1,keys:r},null,2)}\n`)}function _(e,t){return n.default.join(e,`objects`,t)}function v(e,t){let i=u(t.path);if(i)try{if(r.default.lstatSync(e).isSymbolicLink())return;let a=n.default.join(e,i);if(r.default.lstatSync(a).isSymbolicLink())return;let o=r.default.realpathSync(e),s=r.default.realpathSync(a),c=n.default.relative(o,s);if(c.startsWith(`..`)||n.default.isAbsolute(c))return;let u=r.default.readFileSync(s);return l(u)===t.sha256?u:void 0}catch{return}}function y(e,t,i){if(s.test(t))for(let o of h(e,t).keys){if(!s.test(o))continue;let c=_(e,o),u=d(f(n.default.join(c,a)));if(!u||u.key!==o||u.lookupKey!==t||!x(u)||u.artifacts.some(e=>v(c,e)===void 0))continue;let p=new Map,m=!0;for(let e of u.inputs){let t=i(e);if(!t){m=!1;break}if(e.sha256!==``)try{if(l(r.default.readFileSync(t))!==e.sha256){m=!1;break}}catch{m=!1;break}p.set(e.token,t)}if(m)return{directory:c,manifest:u,replacements:p}}}function b(e,t){return l(JSON.stringify({schema:1,lookupKey:e.lookupKey,entry:e.entry,inputs:e.inputs,artifacts:t}))}function x(e){return e.key===b({lookupKey:e.lookupKey,entry:e.entry,inputs:e.inputs,artifacts:new Map},e.artifacts)}function S(e,t){let r=d(f(n.default.join(e,a)));return!r||r.key!==t.key||!x(r)?!1:t.artifacts.every(t=>v(e,t)!==void 0)}function C(e){if(!s.test(e.lookupKey))throw Error(`Shared build lookup key must be a full SHA-256 digest: ${e.lookupKey}`);if(e.inputs.some(e=>e.sourcePath!==void 0&&!n.default.isAbsolute(e.sourcePath)))throw Error(`Shared build external source paths must be absolute`);let t=[...e.artifacts.entries()].map(([e,t])=>{let n=u(e);if(!n)throw Error(`Shared build artifact escapes its object: ${e}`);return{path:n,sha256:l(t)}}).sort((e,t)=>e.path.localeCompare(t.path)),o=[...e.inputs].sort((e,t)=>e.logicalPath.localeCompare(t.logicalPath)),c=b({...e,inputs:o},t),d={schema:1,key:c,lookupKey:e.lookupKey,entry:e.entry,inputs:o,artifacts:t},f=_(e.cacheDirectory,c);if(r.default.existsSync(f)&&!S(f,d)&&r.default.rmSync(f,{recursive:!0,force:!0}),!r.default.existsSync(f)){let t=`${f}.${process.pid}.${i.default.randomUUID()}.tmp`;try{r.default.mkdirSync(t,{recursive:!0,mode:448});for(let[i,a]of e.artifacts){let e=u(i);if(!e)throw Error(`Shared build artifact escapes its object: ${i}`);let o=n.default.join(t,e);r.default.mkdirSync(n.default.dirname(o),{recursive:!0,mode:448}),r.default.writeFileSync(o,a,{mode:384})}r.default.writeFileSync(n.default.join(t,a),`${JSON.stringify(d,null,2)}\n`,{mode:384}),r.default.mkdirSync(n.default.dirname(f),{recursive:!0,mode:448});try{r.default.renameSync(t,f)}catch(e){if(!r.default.existsSync(f))throw e}}finally{r.default.rmSync(t,{recursive:!0,force:!0})}}return g(e.cacheDirectory,e.lookupKey,c),d}function w(e,r){let i=[...e.replacements.entries()].sort(([e],[t])=>t.length-e.length);for(let a of e.manifest.artifacts){let o=v(e.directory,a);if(!o)throw Error(`Shared build artifact is corrupt: ${a.path}`);let s=o;if(a.path.endsWith(`.mjs`)){let e=o.toString(`utf8`);for(let[n,r]of i)e=e.replaceAll(`${n}:url`,(0,t.pathToFileURL)(r).href).replaceAll(n,r);if(e.includes(`__DOOMPI_PATH_`))throw Error(`Shared build left unresolved path tokens in ${a.path}`);s=e}p(n.default.join(r,a.path),s)}return n.default.join(r,e.manifest.entry)}function T(e){try{return process.kill(e,0),!0}catch{return!1}}function E(e){let t=f(n.default.join(e,o));return!c(t)||typeof t.pid!=`number`||typeof t.createdAt!=`number`?!0:Date.now()-t.createdAt>3e5&&!T(t.pid)}async function D(e){await new Promise(t=>setTimeout(t,e))}async function O(e,t,i){if(!s.test(t))throw Error(`Shared build lock key must be a full SHA-256 digest: ${t}`);let a=n.default.join(e,`locks`,`${t}.lock`);r.default.mkdirSync(n.default.dirname(a),{recursive:!0,mode:448});let c=Date.now();for(;;)try{r.default.mkdirSync(a,{mode:448}),r.default.writeFileSync(n.default.join(a,o),`${JSON.stringify({pid:process.pid,createdAt:Date.now()})}\n`,{mode:384});break}catch(e){if(!(e instanceof Error)||!(`code`in e)||e.code!==`EEXIST`)throw e;if(E(a)){r.default.rmSync(a,{recursive:!0,force:!0});continue}if(Date.now()-c>=6e4)throw Error(`Timed out waiting for shared build lock ${t}`);await D(25)}try{return await i()}finally{r.default.rmSync(a,{recursive:!0,force:!0})}}exports.contentSha256=l,exports.findSharedBuild=y,exports.materializeSharedBuild=w,exports.publishSharedBuild=C,exports.withSharedBuildLock=O;
|
|
2
2
|
//# sourceMappingURL=sharedBuildCache.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sharedBuildCache.cjs","names":["crypto","path","fs"],"sources":["../../src/adapters/sharedBuildCache.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nconst SHARED_BUILD_SCHEMA = 1;\nconst OBJECTS_DIRECTORY = 'objects';\nconst INDEX_DIRECTORY = 'index';\nconst LOCKS_DIRECTORY = 'locks';\nconst MANIFEST_FILE = 'manifest.json';\nconst LOCK_STALE_MS = 5 * 60_000;\nconst LOCK_TIMEOUT_MS = 60_000;\nconst LOCK_RETRY_MS = 25;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/u;\n\nexport interface SharedBuildInput {\n logicalPath: string;\n sha256: string;\n token: string;\n}\n\nexport interface SharedBuildArtifact {\n path: string;\n sha256: string;\n}\n\nexport interface SharedBuildManifest {\n schema: number;\n key: string;\n lookupKey: string;\n entry: string;\n inputs: SharedBuildInput[];\n artifacts: SharedBuildArtifact[];\n}\n\nexport interface ResolvedSharedBuild {\n directory: string;\n manifest: SharedBuildManifest;\n replacements: ReadonlyMap<string, string>;\n}\n\ninterface SharedBuildIndex {\n schema: number;\n keys: string[];\n}\n\nexport interface PublishSharedBuildOptions {\n cacheDirectory: string;\n lookupKey: string;\n entry: string;\n inputs: SharedBuildInput[];\n artifacts: ReadonlyMap<string, string | Uint8Array>;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport function contentSha256(contents: string | Uint8Array): string {\n return crypto.createHash('sha256').update(contents).digest('hex');\n}\n\nfunction safeRelativePath(value: string): string | undefined {\n if (value === '' || path.isAbsolute(value)) return undefined;\n const normalized = path.normalize(value);\n return normalized === '..' || normalized.startsWith(`..${path.sep}`) ? undefined : normalized;\n}\n\nfunction parseManifest(value: unknown): SharedBuildManifest | undefined {\n if (\n !isRecord(value) ||\n value.schema !== SHARED_BUILD_SCHEMA ||\n typeof value.key !== 'string' ||\n !SHA256_PATTERN.test(value.key) ||\n typeof value.lookupKey !== 'string' ||\n !SHA256_PATTERN.test(value.lookupKey) ||\n typeof value.entry !== 'string' ||\n !Array.isArray(value.inputs) ||\n !Array.isArray(value.artifacts)\n ) {\n return undefined;\n }\n const inputs: SharedBuildInput[] = [];\n for (const input of value.inputs) {\n if (\n !isRecord(input) ||\n typeof input.logicalPath !== 'string' ||\n typeof input.sha256 !== 'string' ||\n (input.sha256 !== '' && !SHA256_PATTERN.test(input.sha256)) ||\n typeof input.token !== 'string'\n ) {\n return undefined;\n }\n inputs.push({ logicalPath: input.logicalPath, sha256: input.sha256, token: input.token });\n }\n const artifacts: SharedBuildArtifact[] = [];\n for (const artifact of value.artifacts) {\n if (\n !isRecord(artifact) ||\n typeof artifact.path !== 'string' ||\n typeof artifact.sha256 !== 'string' ||\n !SHA256_PATTERN.test(artifact.sha256)\n ) {\n return undefined;\n }\n if (!safeRelativePath(artifact.path)) return undefined;\n artifacts.push({ path: artifact.path, sha256: artifact.sha256 });\n }\n if (!safeRelativePath(value.entry)) return undefined;\n return {\n schema: SHARED_BUILD_SCHEMA,\n key: value.key,\n lookupKey: value.lookupKey,\n entry: value.entry,\n inputs,\n artifacts,\n };\n}\n\nfunction readJson(target: string): unknown {\n try {\n return JSON.parse(fs.readFileSync(target, 'utf8')) as unknown;\n } catch {\n return undefined;\n }\n}\n\nfunction writeAtomic(target: string, contents: string | Uint8Array): void {\n fs.mkdirSync(path.dirname(target), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n const temporary = `${target}.${process.pid}.${crypto.randomUUID()}.tmp`;\n try {\n fs.writeFileSync(temporary, contents, { mode: PRIVATE_FILE_MODE });\n fs.renameSync(temporary, target);\n fs.chmodSync(target, PRIVATE_FILE_MODE);\n } catch (error) {\n fs.rmSync(temporary, { force: true });\n throw error;\n }\n}\n\nfunction indexPath(cacheDirectory: string, lookupKey: string): string {\n return path.join(cacheDirectory, INDEX_DIRECTORY, `${lookupKey}.json`);\n}\n\nfunction readIndex(cacheDirectory: string, lookupKey: string): SharedBuildIndex {\n const parsed = readJson(indexPath(cacheDirectory, lookupKey));\n if (!isRecord(parsed) || parsed.schema !== SHARED_BUILD_SCHEMA || !Array.isArray(parsed.keys)) {\n return { schema: SHARED_BUILD_SCHEMA, keys: [] };\n }\n return {\n schema: SHARED_BUILD_SCHEMA,\n keys: parsed.keys.filter((key): key is string => typeof key === 'string'),\n };\n}\n\nfunction writeIndex(cacheDirectory: string, lookupKey: string, key: string): void {\n const current = readIndex(cacheDirectory, lookupKey);\n const keys = [key, ...current.keys.filter((candidate) => candidate !== key)];\n writeAtomic(\n indexPath(cacheDirectory, lookupKey),\n `${JSON.stringify({ schema: SHARED_BUILD_SCHEMA, keys }, null, 2)}\\n`,\n );\n}\n\nfunction objectDirectory(cacheDirectory: string, key: string): string {\n return path.join(cacheDirectory, OBJECTS_DIRECTORY, key);\n}\n\nfunction artifactBytes(directory: string, artifact: SharedBuildArtifact): Buffer | undefined {\n const relative = safeRelativePath(artifact.path);\n if (!relative) return undefined;\n try {\n if (fs.lstatSync(directory).isSymbolicLink()) return undefined;\n const target = path.join(directory, relative);\n if (fs.lstatSync(target).isSymbolicLink()) return undefined;\n const canonicalDirectory = fs.realpathSync(directory);\n const canonicalTarget = fs.realpathSync(target);\n const confined = path.relative(canonicalDirectory, canonicalTarget);\n if (confined.startsWith('..') || path.isAbsolute(confined)) return undefined;\n const bytes = fs.readFileSync(canonicalTarget);\n return contentSha256(bytes) === artifact.sha256 ? bytes : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function findSharedBuild(\n cacheDirectory: string,\n lookupKey: string,\n resolveInput: (logicalPath: string) => string | undefined,\n): ResolvedSharedBuild | undefined {\n if (!SHA256_PATTERN.test(lookupKey)) return undefined;\n for (const key of readIndex(cacheDirectory, lookupKey).keys) {\n if (!SHA256_PATTERN.test(key)) continue;\n const directory = objectDirectory(cacheDirectory, key);\n const manifest = parseManifest(readJson(path.join(directory, MANIFEST_FILE)));\n if (!manifest || manifest.key !== key || manifest.lookupKey !== lookupKey || !manifestKeyMatches(manifest))\n continue;\n if (manifest.artifacts.some((artifact) => artifactBytes(directory, artifact) === undefined)) continue;\n const replacements = new Map<string, string>();\n let valid = true;\n for (const input of manifest.inputs) {\n const target = resolveInput(input.logicalPath);\n if (!target) {\n valid = false;\n break;\n }\n if (input.sha256 !== '') {\n try {\n if (contentSha256(fs.readFileSync(target)) !== input.sha256) {\n valid = false;\n break;\n }\n } catch {\n valid = false;\n break;\n }\n }\n replacements.set(input.token, target);\n }\n if (valid) return { directory, manifest, replacements };\n }\n return undefined;\n}\n\nfunction manifestKey(\n options: Omit<PublishSharedBuildOptions, 'cacheDirectory'>,\n artifacts: SharedBuildArtifact[],\n): string {\n return contentSha256(\n JSON.stringify({\n schema: SHARED_BUILD_SCHEMA,\n lookupKey: options.lookupKey,\n entry: options.entry,\n inputs: options.inputs,\n artifacts,\n }),\n );\n}\n\nfunction manifestKeyMatches(manifest: SharedBuildManifest): boolean {\n return (\n manifest.key ===\n manifestKey(\n {\n lookupKey: manifest.lookupKey,\n entry: manifest.entry,\n inputs: manifest.inputs,\n artifacts: new Map(),\n },\n manifest.artifacts,\n )\n );\n}\n\nexport function publishSharedBuild(options: PublishSharedBuildOptions): SharedBuildManifest {\n if (!SHA256_PATTERN.test(options.lookupKey)) {\n throw new Error(`Shared build lookup key must be a full SHA-256 digest: ${options.lookupKey}`);\n }\n const artifacts = [...options.artifacts.entries()]\n .map(([artifactPath, contents]) => {\n const relative = safeRelativePath(artifactPath);\n if (!relative) throw new Error(`Shared build artifact escapes its object: ${artifactPath}`);\n return { path: relative, sha256: contentSha256(contents) };\n })\n .sort((left, right) => left.path.localeCompare(right.path));\n const inputs = [...options.inputs].sort((left, right) => left.logicalPath.localeCompare(right.logicalPath));\n const key = manifestKey({ ...options, inputs }, artifacts);\n const manifest: SharedBuildManifest = {\n schema: SHARED_BUILD_SCHEMA,\n key,\n lookupKey: options.lookupKey,\n entry: options.entry,\n inputs,\n artifacts,\n };\n const finalDirectory = objectDirectory(options.cacheDirectory, key);\n if (!fs.existsSync(finalDirectory)) {\n const temporaryDirectory = `${finalDirectory}.${process.pid}.${crypto.randomUUID()}.tmp`;\n try {\n fs.mkdirSync(temporaryDirectory, { recursive: true, mode: 0o700 });\n for (const [artifactPath, contents] of options.artifacts) {\n const relative = safeRelativePath(artifactPath);\n if (!relative) throw new Error(`Shared build artifact escapes its object: ${artifactPath}`);\n const target = path.join(temporaryDirectory, relative);\n fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });\n fs.writeFileSync(target, contents, { mode: 0o600 });\n }\n fs.writeFileSync(path.join(temporaryDirectory, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\\n`, {\n mode: 0o600,\n });\n fs.mkdirSync(path.dirname(finalDirectory), { recursive: true, mode: 0o700 });\n try {\n fs.renameSync(temporaryDirectory, finalDirectory);\n } catch (error) {\n if (!fs.existsSync(finalDirectory)) throw error;\n }\n } finally {\n fs.rmSync(temporaryDirectory, { recursive: true, force: true });\n }\n }\n writeIndex(options.cacheDirectory, options.lookupKey, key);\n return manifest;\n}\n\nexport function materializeSharedBuild(build: ResolvedSharedBuild, outputDirectory: string): string {\n const replacements = [...build.replacements.entries()].sort(([left], [right]) => right.length - left.length);\n for (const artifact of build.manifest.artifacts) {\n const bytes = artifactBytes(build.directory, artifact);\n if (!bytes) throw new Error(`Shared build artifact is corrupt: ${artifact.path}`);\n let contents: string | Uint8Array = bytes;\n if (artifact.path.endsWith('.mjs')) {\n let source = bytes.toString('utf8');\n for (const [token, target] of replacements) {\n source = source.replaceAll(`${token}:url`, pathToFileURL(target).href).replaceAll(token, target);\n }\n if (source.includes('__DOOMPI_PATH_'))\n throw new Error(`Shared build left unresolved path tokens in ${artifact.path}`);\n contents = source;\n }\n writeAtomic(path.join(outputDirectory, artifact.path), contents);\n }\n return path.join(outputDirectory, build.manifest.entry);\n}\n\nfunction processAlive(processId: number): boolean {\n try {\n process.kill(processId, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction lockIsStale(lockDirectory: string): boolean {\n const owner = readJson(path.join(lockDirectory, 'owner.json'));\n if (!isRecord(owner) || typeof owner.pid !== 'number' || typeof owner.createdAt !== 'number') return true;\n return Date.now() - owner.createdAt > LOCK_STALE_MS && !processAlive(owner.pid);\n}\n\nasync function sleep(milliseconds: number): Promise<void> {\n await new Promise<void>((resolve) => setTimeout(resolve, milliseconds));\n}\n\nexport async function withSharedBuildLock<T>(\n cacheDirectory: string,\n lookupKey: string,\n operation: () => Promise<T>,\n): Promise<T> {\n if (!SHA256_PATTERN.test(lookupKey)) {\n throw new Error(`Shared build lock key must be a full SHA-256 digest: ${lookupKey}`);\n }\n const lockDirectory = path.join(cacheDirectory, LOCKS_DIRECTORY, `${lookupKey}.lock`);\n fs.mkdirSync(path.dirname(lockDirectory), { recursive: true, mode: 0o700 });\n const startedAt = Date.now();\n while (true) {\n try {\n fs.mkdirSync(lockDirectory, { mode: 0o700 });\n fs.writeFileSync(\n path.join(lockDirectory, 'owner.json'),\n `${JSON.stringify({ pid: process.pid, createdAt: Date.now() })}\\n`,\n { mode: 0o600 },\n );\n break;\n } catch (error) {\n if (!(error instanceof Error) || !('code' in error) || error.code !== 'EEXIST') throw error;\n if (lockIsStale(lockDirectory)) {\n fs.rmSync(lockDirectory, { recursive: true, force: true });\n continue;\n }\n if (Date.now() - startedAt >= LOCK_TIMEOUT_MS) {\n throw new Error(`Timed out waiting for shared build lock ${lookupKey}`);\n }\n await sleep(LOCK_RETRY_MS);\n }\n }\n try {\n return await operation();\n } finally {\n fs.rmSync(lockDirectory, { recursive: true, force: true });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sharedBuildCache.cjs","names":["crypto","path","fs"],"sources":["../../src/adapters/sharedBuildCache.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nconst SHARED_BUILD_SCHEMA = 1;\nconst OBJECTS_DIRECTORY = 'objects';\nconst INDEX_DIRECTORY = 'index';\nconst LOCKS_DIRECTORY = 'locks';\nconst MANIFEST_FILE = 'manifest.json';\nconst OWNER_FILE = 'owner.json';\nconst LOCK_STALE_MS = 5 * 60_000;\nconst LOCK_TIMEOUT_MS = 60_000;\nconst LOCK_RETRY_MS = 25;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/u;\n\nexport interface SharedBuildInput {\n logicalPath: string;\n sha256: string;\n token: string;\n /** Canonical source path for inputs outside the synchronized repository. */\n sourcePath?: string;\n}\n\nexport interface SharedBuildArtifact {\n path: string;\n sha256: string;\n}\n\nexport interface SharedBuildManifest {\n schema: number;\n key: string;\n lookupKey: string;\n entry: string;\n inputs: SharedBuildInput[];\n artifacts: SharedBuildArtifact[];\n}\n\nexport interface ResolvedSharedBuild {\n directory: string;\n manifest: SharedBuildManifest;\n replacements: ReadonlyMap<string, string>;\n}\n\ninterface SharedBuildIndex {\n schema: number;\n keys: string[];\n}\n\nexport interface PublishSharedBuildOptions {\n cacheDirectory: string;\n lookupKey: string;\n entry: string;\n inputs: SharedBuildInput[];\n artifacts: ReadonlyMap<string, string | Uint8Array>;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport function contentSha256(contents: string | Uint8Array): string {\n return crypto.createHash('sha256').update(contents).digest('hex');\n}\n\nfunction safeRelativePath(value: string): string | undefined {\n if (value === '' || path.isAbsolute(value)) return undefined;\n const normalized = path.normalize(value);\n return normalized === '..' || normalized.startsWith(`..${path.sep}`) ? undefined : normalized;\n}\n\nfunction parseManifest(value: unknown): SharedBuildManifest | undefined {\n if (\n !isRecord(value) ||\n value.schema !== SHARED_BUILD_SCHEMA ||\n typeof value.key !== 'string' ||\n !SHA256_PATTERN.test(value.key) ||\n typeof value.lookupKey !== 'string' ||\n !SHA256_PATTERN.test(value.lookupKey) ||\n typeof value.entry !== 'string' ||\n !Array.isArray(value.inputs) ||\n !Array.isArray(value.artifacts)\n ) {\n return undefined;\n }\n const inputs: SharedBuildInput[] = [];\n for (const input of value.inputs) {\n if (\n !isRecord(input) ||\n typeof input.logicalPath !== 'string' ||\n typeof input.sha256 !== 'string' ||\n (input.sha256 !== '' && !SHA256_PATTERN.test(input.sha256)) ||\n typeof input.token !== 'string' ||\n (input.sourcePath !== undefined && (typeof input.sourcePath !== 'string' || !path.isAbsolute(input.sourcePath)))\n ) {\n return undefined;\n }\n inputs.push({\n logicalPath: input.logicalPath,\n sha256: input.sha256,\n token: input.token,\n ...(typeof input.sourcePath === 'string' ? { sourcePath: input.sourcePath } : {}),\n });\n }\n const artifacts: SharedBuildArtifact[] = [];\n for (const artifact of value.artifacts) {\n if (\n !isRecord(artifact) ||\n typeof artifact.path !== 'string' ||\n typeof artifact.sha256 !== 'string' ||\n !SHA256_PATTERN.test(artifact.sha256)\n ) {\n return undefined;\n }\n if (!safeRelativePath(artifact.path)) return undefined;\n artifacts.push({ path: artifact.path, sha256: artifact.sha256 });\n }\n if (!safeRelativePath(value.entry)) return undefined;\n return {\n schema: SHARED_BUILD_SCHEMA,\n key: value.key,\n lookupKey: value.lookupKey,\n entry: value.entry,\n inputs,\n artifacts,\n };\n}\n\nfunction readJson(target: string): unknown {\n try {\n return JSON.parse(fs.readFileSync(target, 'utf8')) as unknown;\n } catch {\n return undefined;\n }\n}\n\nfunction writeAtomic(target: string, contents: string | Uint8Array): void {\n fs.mkdirSync(path.dirname(target), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n const temporary = `${target}.${process.pid}.${crypto.randomUUID()}.tmp`;\n try {\n fs.writeFileSync(temporary, contents, { mode: PRIVATE_FILE_MODE });\n fs.renameSync(temporary, target);\n fs.chmodSync(target, PRIVATE_FILE_MODE);\n } catch (error) {\n fs.rmSync(temporary, { force: true });\n throw error;\n }\n}\n\nfunction indexPath(cacheDirectory: string, lookupKey: string): string {\n return path.join(cacheDirectory, INDEX_DIRECTORY, `${lookupKey}.json`);\n}\n\nfunction readIndex(cacheDirectory: string, lookupKey: string): SharedBuildIndex {\n const parsed = readJson(indexPath(cacheDirectory, lookupKey));\n if (!isRecord(parsed) || parsed.schema !== SHARED_BUILD_SCHEMA || !Array.isArray(parsed.keys)) {\n return { schema: SHARED_BUILD_SCHEMA, keys: [] };\n }\n return {\n schema: SHARED_BUILD_SCHEMA,\n keys: parsed.keys.filter((key): key is string => typeof key === 'string'),\n };\n}\n\nfunction writeIndex(cacheDirectory: string, lookupKey: string, key: string): void {\n const current = readIndex(cacheDirectory, lookupKey);\n const keys = [key, ...current.keys.filter((candidate) => candidate !== key)];\n writeAtomic(\n indexPath(cacheDirectory, lookupKey),\n `${JSON.stringify({ schema: SHARED_BUILD_SCHEMA, keys }, null, 2)}\\n`,\n );\n}\n\nfunction objectDirectory(cacheDirectory: string, key: string): string {\n return path.join(cacheDirectory, OBJECTS_DIRECTORY, key);\n}\n\nfunction artifactBytes(directory: string, artifact: SharedBuildArtifact): Buffer | undefined {\n const relative = safeRelativePath(artifact.path);\n if (!relative) return undefined;\n try {\n if (fs.lstatSync(directory).isSymbolicLink()) return undefined;\n const target = path.join(directory, relative);\n if (fs.lstatSync(target).isSymbolicLink()) return undefined;\n const canonicalDirectory = fs.realpathSync(directory);\n const canonicalTarget = fs.realpathSync(target);\n const confined = path.relative(canonicalDirectory, canonicalTarget);\n if (confined.startsWith('..') || path.isAbsolute(confined)) return undefined;\n const bytes = fs.readFileSync(canonicalTarget);\n return contentSha256(bytes) === artifact.sha256 ? bytes : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function findSharedBuild(\n cacheDirectory: string,\n lookupKey: string,\n resolveInput: (input: SharedBuildInput) => string | undefined,\n): ResolvedSharedBuild | undefined {\n if (!SHA256_PATTERN.test(lookupKey)) return undefined;\n for (const key of readIndex(cacheDirectory, lookupKey).keys) {\n if (!SHA256_PATTERN.test(key)) continue;\n const directory = objectDirectory(cacheDirectory, key);\n const manifest = parseManifest(readJson(path.join(directory, MANIFEST_FILE)));\n if (!manifest || manifest.key !== key || manifest.lookupKey !== lookupKey || !manifestKeyMatches(manifest))\n continue;\n if (manifest.artifacts.some((artifact) => artifactBytes(directory, artifact) === undefined)) continue;\n const replacements = new Map<string, string>();\n let valid = true;\n for (const input of manifest.inputs) {\n const target = resolveInput(input);\n if (!target) {\n valid = false;\n break;\n }\n if (input.sha256 !== '') {\n try {\n if (contentSha256(fs.readFileSync(target)) !== input.sha256) {\n valid = false;\n break;\n }\n } catch {\n valid = false;\n break;\n }\n }\n replacements.set(input.token, target);\n }\n if (valid) return { directory, manifest, replacements };\n }\n return undefined;\n}\n\nfunction manifestKey(\n options: Omit<PublishSharedBuildOptions, 'cacheDirectory'>,\n artifacts: SharedBuildArtifact[],\n): string {\n return contentSha256(\n JSON.stringify({\n schema: SHARED_BUILD_SCHEMA,\n lookupKey: options.lookupKey,\n entry: options.entry,\n inputs: options.inputs,\n artifacts,\n }),\n );\n}\n\nfunction manifestKeyMatches(manifest: SharedBuildManifest): boolean {\n return (\n manifest.key ===\n manifestKey(\n {\n lookupKey: manifest.lookupKey,\n entry: manifest.entry,\n inputs: manifest.inputs,\n artifacts: new Map(),\n },\n manifest.artifacts,\n )\n );\n}\n\nfunction sharedObjectMatches(directory: string, expected: SharedBuildManifest): boolean {\n const current = parseManifest(readJson(path.join(directory, MANIFEST_FILE)));\n if (!current || current.key !== expected.key || !manifestKeyMatches(current)) return false;\n return expected.artifacts.every((artifact) => artifactBytes(directory, artifact) !== undefined);\n}\n\nexport function publishSharedBuild(options: PublishSharedBuildOptions): SharedBuildManifest {\n if (!SHA256_PATTERN.test(options.lookupKey)) {\n throw new Error(`Shared build lookup key must be a full SHA-256 digest: ${options.lookupKey}`);\n }\n if (options.inputs.some((input) => input.sourcePath !== undefined && !path.isAbsolute(input.sourcePath))) {\n throw new Error('Shared build external source paths must be absolute');\n }\n const artifacts = [...options.artifacts.entries()]\n .map(([artifactPath, contents]) => {\n const relative = safeRelativePath(artifactPath);\n if (!relative) throw new Error(`Shared build artifact escapes its object: ${artifactPath}`);\n return { path: relative, sha256: contentSha256(contents) };\n })\n .sort((left, right) => left.path.localeCompare(right.path));\n const inputs = [...options.inputs].sort((left, right) => left.logicalPath.localeCompare(right.logicalPath));\n const key = manifestKey({ ...options, inputs }, artifacts);\n const manifest: SharedBuildManifest = {\n schema: SHARED_BUILD_SCHEMA,\n key,\n lookupKey: options.lookupKey,\n entry: options.entry,\n inputs,\n artifacts,\n };\n const finalDirectory = objectDirectory(options.cacheDirectory, key);\n if (fs.existsSync(finalDirectory) && !sharedObjectMatches(finalDirectory, manifest)) {\n fs.rmSync(finalDirectory, { recursive: true, force: true });\n }\n if (!fs.existsSync(finalDirectory)) {\n const temporaryDirectory = `${finalDirectory}.${process.pid}.${crypto.randomUUID()}.tmp`;\n try {\n fs.mkdirSync(temporaryDirectory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n for (const [artifactPath, contents] of options.artifacts) {\n const relative = safeRelativePath(artifactPath);\n if (!relative) throw new Error(`Shared build artifact escapes its object: ${artifactPath}`);\n const target = path.join(temporaryDirectory, relative);\n fs.mkdirSync(path.dirname(target), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n fs.writeFileSync(target, contents, { mode: PRIVATE_FILE_MODE });\n }\n fs.writeFileSync(path.join(temporaryDirectory, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\\n`, {\n mode: PRIVATE_FILE_MODE,\n });\n fs.mkdirSync(path.dirname(finalDirectory), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n try {\n fs.renameSync(temporaryDirectory, finalDirectory);\n } catch (error) {\n if (!fs.existsSync(finalDirectory)) throw error;\n }\n } finally {\n fs.rmSync(temporaryDirectory, { recursive: true, force: true });\n }\n }\n writeIndex(options.cacheDirectory, options.lookupKey, key);\n return manifest;\n}\n\nexport function materializeSharedBuild(build: ResolvedSharedBuild, outputDirectory: string): string {\n const replacements = [...build.replacements.entries()].sort(([left], [right]) => right.length - left.length);\n for (const artifact of build.manifest.artifacts) {\n const bytes = artifactBytes(build.directory, artifact);\n if (!bytes) throw new Error(`Shared build artifact is corrupt: ${artifact.path}`);\n let contents: string | Uint8Array = bytes;\n if (artifact.path.endsWith('.mjs')) {\n let source = bytes.toString('utf8');\n for (const [token, target] of replacements) {\n source = source.replaceAll(`${token}:url`, pathToFileURL(target).href).replaceAll(token, target);\n }\n if (source.includes('__DOOMPI_PATH_'))\n throw new Error(`Shared build left unresolved path tokens in ${artifact.path}`);\n contents = source;\n }\n writeAtomic(path.join(outputDirectory, artifact.path), contents);\n }\n return path.join(outputDirectory, build.manifest.entry);\n}\n\nfunction processAlive(processId: number): boolean {\n try {\n process.kill(processId, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction lockIsStale(lockDirectory: string): boolean {\n const owner = readJson(path.join(lockDirectory, OWNER_FILE));\n if (!isRecord(owner) || typeof owner.pid !== 'number' || typeof owner.createdAt !== 'number') return true;\n return Date.now() - owner.createdAt > LOCK_STALE_MS && !processAlive(owner.pid);\n}\n\nasync function sleep(milliseconds: number): Promise<void> {\n await new Promise<void>((resolve) => setTimeout(resolve, milliseconds));\n}\n\nexport async function withSharedBuildLock<T>(\n cacheDirectory: string,\n lookupKey: string,\n operation: () => Promise<T>,\n): Promise<T> {\n if (!SHA256_PATTERN.test(lookupKey)) {\n throw new Error(`Shared build lock key must be a full SHA-256 digest: ${lookupKey}`);\n }\n const lockDirectory = path.join(cacheDirectory, LOCKS_DIRECTORY, `${lookupKey}.lock`);\n fs.mkdirSync(path.dirname(lockDirectory), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n const startedAt = Date.now();\n while (true) {\n try {\n fs.mkdirSync(lockDirectory, { mode: PRIVATE_DIRECTORY_MODE });\n fs.writeFileSync(\n path.join(lockDirectory, OWNER_FILE),\n `${JSON.stringify({ pid: process.pid, createdAt: Date.now() })}\\n`,\n { mode: PRIVATE_FILE_MODE },\n );\n break;\n } catch (error) {\n if (!(error instanceof Error) || !('code' in error) || error.code !== 'EEXIST') throw error;\n if (lockIsStale(lockDirectory)) {\n fs.rmSync(lockDirectory, { recursive: true, force: true });\n continue;\n }\n if (Date.now() - startedAt >= LOCK_TIMEOUT_MS) {\n throw new Error(`Timed out waiting for shared build lock ${lookupKey}`);\n }\n await sleep(LOCK_RETRY_MS);\n }\n }\n try {\n return await operation();\n } finally {\n fs.rmSync(lockDirectory, { recursive: true, force: true });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{pathToFileURL as e}from"node:url";import t from"node:path";import n from"node:fs";import r from"node:crypto";const i=`manifest.json`,a=/^[a-f0-9]{64}$/u;function
|
|
1
|
+
import{pathToFileURL as e}from"node:url";import t from"node:path";import n from"node:fs";import r from"node:crypto";const i=`manifest.json`,a=`owner.json`,o=/^[a-f0-9]{64}$/u;function s(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function c(e){return r.createHash(`sha256`).update(e).digest(`hex`)}function l(e){if(e===``||t.isAbsolute(e))return;let n=t.normalize(e);return n===`..`||n.startsWith(`..${t.sep}`)?void 0:n}function u(e){if(!s(e)||e.schema!==1||typeof e.key!=`string`||!o.test(e.key)||typeof e.lookupKey!=`string`||!o.test(e.lookupKey)||typeof e.entry!=`string`||!Array.isArray(e.inputs)||!Array.isArray(e.artifacts))return;let n=[];for(let r of e.inputs){if(!s(r)||typeof r.logicalPath!=`string`||typeof r.sha256!=`string`||r.sha256!==``&&!o.test(r.sha256)||typeof r.token!=`string`||r.sourcePath!==void 0&&(typeof r.sourcePath!=`string`||!t.isAbsolute(r.sourcePath)))return;n.push({logicalPath:r.logicalPath,sha256:r.sha256,token:r.token,...typeof r.sourcePath==`string`?{sourcePath:r.sourcePath}:{}})}let r=[];for(let t of e.artifacts){if(!s(t)||typeof t.path!=`string`||typeof t.sha256!=`string`||!o.test(t.sha256)||!l(t.path))return;r.push({path:t.path,sha256:t.sha256})}if(l(e.entry))return{schema:1,key:e.key,lookupKey:e.lookupKey,entry:e.entry,inputs:n,artifacts:r}}function d(e){try{return JSON.parse(n.readFileSync(e,`utf8`))}catch{return}}function f(e,i){n.mkdirSync(t.dirname(e),{recursive:!0,mode:448});let a=`${e}.${process.pid}.${r.randomUUID()}.tmp`;try{n.writeFileSync(a,i,{mode:384}),n.renameSync(a,e),n.chmodSync(e,384)}catch(e){throw n.rmSync(a,{force:!0}),e}}function p(e,n){return t.join(e,`index`,`${n}.json`)}function m(e,t){let n=d(p(e,t));return!s(n)||n.schema!==1||!Array.isArray(n.keys)?{schema:1,keys:[]}:{schema:1,keys:n.keys.filter(e=>typeof e==`string`)}}function h(e,t,n){let r=[n,...m(e,t).keys.filter(e=>e!==n)];f(p(e,t),`${JSON.stringify({schema:1,keys:r},null,2)}\n`)}function g(e,n){return t.join(e,`objects`,n)}function _(e,r){let i=l(r.path);if(i)try{if(n.lstatSync(e).isSymbolicLink())return;let a=t.join(e,i);if(n.lstatSync(a).isSymbolicLink())return;let o=n.realpathSync(e),s=n.realpathSync(a),l=t.relative(o,s);if(l.startsWith(`..`)||t.isAbsolute(l))return;let u=n.readFileSync(s);return c(u)===r.sha256?u:void 0}catch{return}}function v(e,r,a){if(o.test(r))for(let s of m(e,r).keys){if(!o.test(s))continue;let l=g(e,s),f=u(d(t.join(l,i)));if(!f||f.key!==s||f.lookupKey!==r||!b(f)||f.artifacts.some(e=>_(l,e)===void 0))continue;let p=new Map,m=!0;for(let e of f.inputs){let t=a(e);if(!t){m=!1;break}if(e.sha256!==``)try{if(c(n.readFileSync(t))!==e.sha256){m=!1;break}}catch{m=!1;break}p.set(e.token,t)}if(m)return{directory:l,manifest:f,replacements:p}}}function y(e,t){return c(JSON.stringify({schema:1,lookupKey:e.lookupKey,entry:e.entry,inputs:e.inputs,artifacts:t}))}function b(e){return e.key===y({lookupKey:e.lookupKey,entry:e.entry,inputs:e.inputs,artifacts:new Map},e.artifacts)}function x(e,n){let r=u(d(t.join(e,i)));return!r||r.key!==n.key||!b(r)?!1:n.artifacts.every(t=>_(e,t)!==void 0)}function S(e){if(!o.test(e.lookupKey))throw Error(`Shared build lookup key must be a full SHA-256 digest: ${e.lookupKey}`);if(e.inputs.some(e=>e.sourcePath!==void 0&&!t.isAbsolute(e.sourcePath)))throw Error(`Shared build external source paths must be absolute`);let a=[...e.artifacts.entries()].map(([e,t])=>{let n=l(e);if(!n)throw Error(`Shared build artifact escapes its object: ${e}`);return{path:n,sha256:c(t)}}).sort((e,t)=>e.path.localeCompare(t.path)),s=[...e.inputs].sort((e,t)=>e.logicalPath.localeCompare(t.logicalPath)),u=y({...e,inputs:s},a),d={schema:1,key:u,lookupKey:e.lookupKey,entry:e.entry,inputs:s,artifacts:a},f=g(e.cacheDirectory,u);if(n.existsSync(f)&&!x(f,d)&&n.rmSync(f,{recursive:!0,force:!0}),!n.existsSync(f)){let a=`${f}.${process.pid}.${r.randomUUID()}.tmp`;try{n.mkdirSync(a,{recursive:!0,mode:448});for(let[r,i]of e.artifacts){let e=l(r);if(!e)throw Error(`Shared build artifact escapes its object: ${r}`);let o=t.join(a,e);n.mkdirSync(t.dirname(o),{recursive:!0,mode:448}),n.writeFileSync(o,i,{mode:384})}n.writeFileSync(t.join(a,i),`${JSON.stringify(d,null,2)}\n`,{mode:384}),n.mkdirSync(t.dirname(f),{recursive:!0,mode:448});try{n.renameSync(a,f)}catch(e){if(!n.existsSync(f))throw e}}finally{n.rmSync(a,{recursive:!0,force:!0})}}return h(e.cacheDirectory,e.lookupKey,u),d}function C(n,r){let i=[...n.replacements.entries()].sort(([e],[t])=>t.length-e.length);for(let a of n.manifest.artifacts){let o=_(n.directory,a);if(!o)throw Error(`Shared build artifact is corrupt: ${a.path}`);let s=o;if(a.path.endsWith(`.mjs`)){let t=o.toString(`utf8`);for(let[n,r]of i)t=t.replaceAll(`${n}:url`,e(r).href).replaceAll(n,r);if(t.includes(`__DOOMPI_PATH_`))throw Error(`Shared build left unresolved path tokens in ${a.path}`);s=t}f(t.join(r,a.path),s)}return t.join(r,n.manifest.entry)}function w(e){try{return process.kill(e,0),!0}catch{return!1}}function T(e){let n=d(t.join(e,a));return!s(n)||typeof n.pid!=`number`||typeof n.createdAt!=`number`?!0:Date.now()-n.createdAt>3e5&&!w(n.pid)}async function E(e){await new Promise(t=>setTimeout(t,e))}async function D(e,r,i){if(!o.test(r))throw Error(`Shared build lock key must be a full SHA-256 digest: ${r}`);let s=t.join(e,`locks`,`${r}.lock`);n.mkdirSync(t.dirname(s),{recursive:!0,mode:448});let c=Date.now();for(;;)try{n.mkdirSync(s,{mode:448}),n.writeFileSync(t.join(s,a),`${JSON.stringify({pid:process.pid,createdAt:Date.now()})}\n`,{mode:384});break}catch(e){if(!(e instanceof Error)||!(`code`in e)||e.code!==`EEXIST`)throw e;if(T(s)){n.rmSync(s,{recursive:!0,force:!0});continue}if(Date.now()-c>=6e4)throw Error(`Timed out waiting for shared build lock ${r}`);await E(25)}try{return await i()}finally{n.rmSync(s,{recursive:!0,force:!0})}}export{c as contentSha256,v as findSharedBuild,C as materializeSharedBuild,S as publishSharedBuild,D as withSharedBuildLock};
|
|
2
2
|
//# sourceMappingURL=sharedBuildCache.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sharedBuildCache.mjs","names":[],"sources":["../../src/adapters/sharedBuildCache.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nconst SHARED_BUILD_SCHEMA = 1;\nconst OBJECTS_DIRECTORY = 'objects';\nconst INDEX_DIRECTORY = 'index';\nconst LOCKS_DIRECTORY = 'locks';\nconst MANIFEST_FILE = 'manifest.json';\nconst LOCK_STALE_MS = 5 * 60_000;\nconst LOCK_TIMEOUT_MS = 60_000;\nconst LOCK_RETRY_MS = 25;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/u;\n\nexport interface SharedBuildInput {\n logicalPath: string;\n sha256: string;\n token: string;\n}\n\nexport interface SharedBuildArtifact {\n path: string;\n sha256: string;\n}\n\nexport interface SharedBuildManifest {\n schema: number;\n key: string;\n lookupKey: string;\n entry: string;\n inputs: SharedBuildInput[];\n artifacts: SharedBuildArtifact[];\n}\n\nexport interface ResolvedSharedBuild {\n directory: string;\n manifest: SharedBuildManifest;\n replacements: ReadonlyMap<string, string>;\n}\n\ninterface SharedBuildIndex {\n schema: number;\n keys: string[];\n}\n\nexport interface PublishSharedBuildOptions {\n cacheDirectory: string;\n lookupKey: string;\n entry: string;\n inputs: SharedBuildInput[];\n artifacts: ReadonlyMap<string, string | Uint8Array>;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport function contentSha256(contents: string | Uint8Array): string {\n return crypto.createHash('sha256').update(contents).digest('hex');\n}\n\nfunction safeRelativePath(value: string): string | undefined {\n if (value === '' || path.isAbsolute(value)) return undefined;\n const normalized = path.normalize(value);\n return normalized === '..' || normalized.startsWith(`..${path.sep}`) ? undefined : normalized;\n}\n\nfunction parseManifest(value: unknown): SharedBuildManifest | undefined {\n if (\n !isRecord(value) ||\n value.schema !== SHARED_BUILD_SCHEMA ||\n typeof value.key !== 'string' ||\n !SHA256_PATTERN.test(value.key) ||\n typeof value.lookupKey !== 'string' ||\n !SHA256_PATTERN.test(value.lookupKey) ||\n typeof value.entry !== 'string' ||\n !Array.isArray(value.inputs) ||\n !Array.isArray(value.artifacts)\n ) {\n return undefined;\n }\n const inputs: SharedBuildInput[] = [];\n for (const input of value.inputs) {\n if (\n !isRecord(input) ||\n typeof input.logicalPath !== 'string' ||\n typeof input.sha256 !== 'string' ||\n (input.sha256 !== '' && !SHA256_PATTERN.test(input.sha256)) ||\n typeof input.token !== 'string'\n ) {\n return undefined;\n }\n inputs.push({ logicalPath: input.logicalPath, sha256: input.sha256, token: input.token });\n }\n const artifacts: SharedBuildArtifact[] = [];\n for (const artifact of value.artifacts) {\n if (\n !isRecord(artifact) ||\n typeof artifact.path !== 'string' ||\n typeof artifact.sha256 !== 'string' ||\n !SHA256_PATTERN.test(artifact.sha256)\n ) {\n return undefined;\n }\n if (!safeRelativePath(artifact.path)) return undefined;\n artifacts.push({ path: artifact.path, sha256: artifact.sha256 });\n }\n if (!safeRelativePath(value.entry)) return undefined;\n return {\n schema: SHARED_BUILD_SCHEMA,\n key: value.key,\n lookupKey: value.lookupKey,\n entry: value.entry,\n inputs,\n artifacts,\n };\n}\n\nfunction readJson(target: string): unknown {\n try {\n return JSON.parse(fs.readFileSync(target, 'utf8')) as unknown;\n } catch {\n return undefined;\n }\n}\n\nfunction writeAtomic(target: string, contents: string | Uint8Array): void {\n fs.mkdirSync(path.dirname(target), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n const temporary = `${target}.${process.pid}.${crypto.randomUUID()}.tmp`;\n try {\n fs.writeFileSync(temporary, contents, { mode: PRIVATE_FILE_MODE });\n fs.renameSync(temporary, target);\n fs.chmodSync(target, PRIVATE_FILE_MODE);\n } catch (error) {\n fs.rmSync(temporary, { force: true });\n throw error;\n }\n}\n\nfunction indexPath(cacheDirectory: string, lookupKey: string): string {\n return path.join(cacheDirectory, INDEX_DIRECTORY, `${lookupKey}.json`);\n}\n\nfunction readIndex(cacheDirectory: string, lookupKey: string): SharedBuildIndex {\n const parsed = readJson(indexPath(cacheDirectory, lookupKey));\n if (!isRecord(parsed) || parsed.schema !== SHARED_BUILD_SCHEMA || !Array.isArray(parsed.keys)) {\n return { schema: SHARED_BUILD_SCHEMA, keys: [] };\n }\n return {\n schema: SHARED_BUILD_SCHEMA,\n keys: parsed.keys.filter((key): key is string => typeof key === 'string'),\n };\n}\n\nfunction writeIndex(cacheDirectory: string, lookupKey: string, key: string): void {\n const current = readIndex(cacheDirectory, lookupKey);\n const keys = [key, ...current.keys.filter((candidate) => candidate !== key)];\n writeAtomic(\n indexPath(cacheDirectory, lookupKey),\n `${JSON.stringify({ schema: SHARED_BUILD_SCHEMA, keys }, null, 2)}\\n`,\n );\n}\n\nfunction objectDirectory(cacheDirectory: string, key: string): string {\n return path.join(cacheDirectory, OBJECTS_DIRECTORY, key);\n}\n\nfunction artifactBytes(directory: string, artifact: SharedBuildArtifact): Buffer | undefined {\n const relative = safeRelativePath(artifact.path);\n if (!relative) return undefined;\n try {\n if (fs.lstatSync(directory).isSymbolicLink()) return undefined;\n const target = path.join(directory, relative);\n if (fs.lstatSync(target).isSymbolicLink()) return undefined;\n const canonicalDirectory = fs.realpathSync(directory);\n const canonicalTarget = fs.realpathSync(target);\n const confined = path.relative(canonicalDirectory, canonicalTarget);\n if (confined.startsWith('..') || path.isAbsolute(confined)) return undefined;\n const bytes = fs.readFileSync(canonicalTarget);\n return contentSha256(bytes) === artifact.sha256 ? bytes : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function findSharedBuild(\n cacheDirectory: string,\n lookupKey: string,\n resolveInput: (logicalPath: string) => string | undefined,\n): ResolvedSharedBuild | undefined {\n if (!SHA256_PATTERN.test(lookupKey)) return undefined;\n for (const key of readIndex(cacheDirectory, lookupKey).keys) {\n if (!SHA256_PATTERN.test(key)) continue;\n const directory = objectDirectory(cacheDirectory, key);\n const manifest = parseManifest(readJson(path.join(directory, MANIFEST_FILE)));\n if (!manifest || manifest.key !== key || manifest.lookupKey !== lookupKey || !manifestKeyMatches(manifest))\n continue;\n if (manifest.artifacts.some((artifact) => artifactBytes(directory, artifact) === undefined)) continue;\n const replacements = new Map<string, string>();\n let valid = true;\n for (const input of manifest.inputs) {\n const target = resolveInput(input.logicalPath);\n if (!target) {\n valid = false;\n break;\n }\n if (input.sha256 !== '') {\n try {\n if (contentSha256(fs.readFileSync(target)) !== input.sha256) {\n valid = false;\n break;\n }\n } catch {\n valid = false;\n break;\n }\n }\n replacements.set(input.token, target);\n }\n if (valid) return { directory, manifest, replacements };\n }\n return undefined;\n}\n\nfunction manifestKey(\n options: Omit<PublishSharedBuildOptions, 'cacheDirectory'>,\n artifacts: SharedBuildArtifact[],\n): string {\n return contentSha256(\n JSON.stringify({\n schema: SHARED_BUILD_SCHEMA,\n lookupKey: options.lookupKey,\n entry: options.entry,\n inputs: options.inputs,\n artifacts,\n }),\n );\n}\n\nfunction manifestKeyMatches(manifest: SharedBuildManifest): boolean {\n return (\n manifest.key ===\n manifestKey(\n {\n lookupKey: manifest.lookupKey,\n entry: manifest.entry,\n inputs: manifest.inputs,\n artifacts: new Map(),\n },\n manifest.artifacts,\n )\n );\n}\n\nexport function publishSharedBuild(options: PublishSharedBuildOptions): SharedBuildManifest {\n if (!SHA256_PATTERN.test(options.lookupKey)) {\n throw new Error(`Shared build lookup key must be a full SHA-256 digest: ${options.lookupKey}`);\n }\n const artifacts = [...options.artifacts.entries()]\n .map(([artifactPath, contents]) => {\n const relative = safeRelativePath(artifactPath);\n if (!relative) throw new Error(`Shared build artifact escapes its object: ${artifactPath}`);\n return { path: relative, sha256: contentSha256(contents) };\n })\n .sort((left, right) => left.path.localeCompare(right.path));\n const inputs = [...options.inputs].sort((left, right) => left.logicalPath.localeCompare(right.logicalPath));\n const key = manifestKey({ ...options, inputs }, artifacts);\n const manifest: SharedBuildManifest = {\n schema: SHARED_BUILD_SCHEMA,\n key,\n lookupKey: options.lookupKey,\n entry: options.entry,\n inputs,\n artifacts,\n };\n const finalDirectory = objectDirectory(options.cacheDirectory, key);\n if (!fs.existsSync(finalDirectory)) {\n const temporaryDirectory = `${finalDirectory}.${process.pid}.${crypto.randomUUID()}.tmp`;\n try {\n fs.mkdirSync(temporaryDirectory, { recursive: true, mode: 0o700 });\n for (const [artifactPath, contents] of options.artifacts) {\n const relative = safeRelativePath(artifactPath);\n if (!relative) throw new Error(`Shared build artifact escapes its object: ${artifactPath}`);\n const target = path.join(temporaryDirectory, relative);\n fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });\n fs.writeFileSync(target, contents, { mode: 0o600 });\n }\n fs.writeFileSync(path.join(temporaryDirectory, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\\n`, {\n mode: 0o600,\n });\n fs.mkdirSync(path.dirname(finalDirectory), { recursive: true, mode: 0o700 });\n try {\n fs.renameSync(temporaryDirectory, finalDirectory);\n } catch (error) {\n if (!fs.existsSync(finalDirectory)) throw error;\n }\n } finally {\n fs.rmSync(temporaryDirectory, { recursive: true, force: true });\n }\n }\n writeIndex(options.cacheDirectory, options.lookupKey, key);\n return manifest;\n}\n\nexport function materializeSharedBuild(build: ResolvedSharedBuild, outputDirectory: string): string {\n const replacements = [...build.replacements.entries()].sort(([left], [right]) => right.length - left.length);\n for (const artifact of build.manifest.artifacts) {\n const bytes = artifactBytes(build.directory, artifact);\n if (!bytes) throw new Error(`Shared build artifact is corrupt: ${artifact.path}`);\n let contents: string | Uint8Array = bytes;\n if (artifact.path.endsWith('.mjs')) {\n let source = bytes.toString('utf8');\n for (const [token, target] of replacements) {\n source = source.replaceAll(`${token}:url`, pathToFileURL(target).href).replaceAll(token, target);\n }\n if (source.includes('__DOOMPI_PATH_'))\n throw new Error(`Shared build left unresolved path tokens in ${artifact.path}`);\n contents = source;\n }\n writeAtomic(path.join(outputDirectory, artifact.path), contents);\n }\n return path.join(outputDirectory, build.manifest.entry);\n}\n\nfunction processAlive(processId: number): boolean {\n try {\n process.kill(processId, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction lockIsStale(lockDirectory: string): boolean {\n const owner = readJson(path.join(lockDirectory, 'owner.json'));\n if (!isRecord(owner) || typeof owner.pid !== 'number' || typeof owner.createdAt !== 'number') return true;\n return Date.now() - owner.createdAt > LOCK_STALE_MS && !processAlive(owner.pid);\n}\n\nasync function sleep(milliseconds: number): Promise<void> {\n await new Promise<void>((resolve) => setTimeout(resolve, milliseconds));\n}\n\nexport async function withSharedBuildLock<T>(\n cacheDirectory: string,\n lookupKey: string,\n operation: () => Promise<T>,\n): Promise<T> {\n if (!SHA256_PATTERN.test(lookupKey)) {\n throw new Error(`Shared build lock key must be a full SHA-256 digest: ${lookupKey}`);\n }\n const lockDirectory = path.join(cacheDirectory, LOCKS_DIRECTORY, `${lookupKey}.lock`);\n fs.mkdirSync(path.dirname(lockDirectory), { recursive: true, mode: 0o700 });\n const startedAt = Date.now();\n while (true) {\n try {\n fs.mkdirSync(lockDirectory, { mode: 0o700 });\n fs.writeFileSync(\n path.join(lockDirectory, 'owner.json'),\n `${JSON.stringify({ pid: process.pid, createdAt: Date.now() })}\\n`,\n { mode: 0o600 },\n );\n break;\n } catch (error) {\n if (!(error instanceof Error) || !('code' in error) || error.code !== 'EEXIST') throw error;\n if (lockIsStale(lockDirectory)) {\n fs.rmSync(lockDirectory, { recursive: true, force: true });\n continue;\n }\n if (Date.now() - startedAt >= LOCK_TIMEOUT_MS) {\n throw new Error(`Timed out waiting for shared build lock ${lookupKey}`);\n }\n await sleep(LOCK_RETRY_MS);\n }\n }\n try {\n return await operation();\n } finally {\n fs.rmSync(lockDirectory, { recursive: true, force: true });\n }\n}\n"],"mappings":"oHAKA,MAIM,EAAgB,gBAMhB,EAAiB,kBAyCvB,SAAS,EAAS,EAAkD,CAClE,OAAO,OAAO,GAAU,YAAY,GAAkB,CAAC,MAAM,QAAQ,EAAM,CAG7E,SAAgB,EAAc,EAAuC,CACnE,OAAO,EAAO,WAAW,SAAS,CAAC,OAAO,EAAS,CAAC,OAAO,MAAM,CAGnE,SAAS,EAAiB,EAAmC,CAC3D,GAAI,IAAU,IAAM,EAAK,WAAW,EAAM,CAAE,OAC5C,IAAM,EAAa,EAAK,UAAU,EAAM,CACxC,OAAO,IAAe,MAAQ,EAAW,WAAW,KAAK,EAAK,MAAM,CAAG,IAAA,GAAY,EAGrF,SAAS,EAAc,EAAiD,CACtE,GACE,CAAC,EAAS,EAAM,EAChB,EAAM,SAAW,GACjB,OAAO,EAAM,KAAQ,UACrB,CAAC,EAAe,KAAK,EAAM,IAAI,EAC/B,OAAO,EAAM,WAAc,UAC3B,CAAC,EAAe,KAAK,EAAM,UAAU,EACrC,OAAO,EAAM,OAAU,UACvB,CAAC,MAAM,QAAQ,EAAM,OAAO,EAC5B,CAAC,MAAM,QAAQ,EAAM,UAAU,CAE/B,OAEF,IAAM,EAA6B,EAAE,CACrC,IAAK,IAAM,KAAS,EAAM,OAAQ,CAChC,GACE,CAAC,EAAS,EAAM,EAChB,OAAO,EAAM,aAAgB,UAC7B,OAAO,EAAM,QAAW,UACvB,EAAM,SAAW,IAAM,CAAC,EAAe,KAAK,EAAM,OAAO,EAC1D,OAAO,EAAM,OAAU,SAEvB,OAEF,EAAO,KAAK,CAAE,YAAa,EAAM,YAAa,OAAQ,EAAM,OAAQ,MAAO,EAAM,MAAO,CAAC,CAE3F,IAAM,EAAmC,EAAE,CAC3C,IAAK,IAAM,KAAY,EAAM,UAAW,CAStC,GAPE,CAAC,EAAS,EAAS,EACnB,OAAO,EAAS,MAAS,UACzB,OAAO,EAAS,QAAW,UAC3B,CAAC,EAAe,KAAK,EAAS,OAAO,EAInC,CAAC,EAAiB,EAAS,KAAK,CAAE,OACtC,EAAU,KAAK,CAAE,KAAM,EAAS,KAAM,OAAQ,EAAS,OAAQ,CAAC,CAE7D,KAAiB,EAAM,MAAM,CAClC,MAAO,CACL,OAAQ,EACR,IAAK,EAAM,IACX,UAAW,EAAM,UACjB,MAAO,EAAM,MACb,SACA,YACD,CAGH,SAAS,EAAS,EAAyB,CACzC,GAAI,CACF,OAAO,KAAK,MAAM,EAAG,aAAa,EAAQ,OAAO,CAAC,MAC5C,CACN,QAIJ,SAAS,EAAY,EAAgB,EAAqC,CACxE,EAAG,UAAU,EAAK,QAAQ,EAAO,CAAE,CAAE,UAAW,GAAM,KAAM,IAAwB,CAAC,CACrF,IAAM,EAAY,GAAG,EAAO,GAAG,QAAQ,IAAI,GAAG,EAAO,YAAY,CAAC,MAClE,GAAI,CACF,EAAG,cAAc,EAAW,EAAU,CAAE,KAAM,IAAmB,CAAC,CAClE,EAAG,WAAW,EAAW,EAAO,CAChC,EAAG,UAAU,EAAQ,IAAkB,OAChC,EAAO,CAEd,MADA,EAAG,OAAO,EAAW,CAAE,MAAO,GAAM,CAAC,CAC/B,GAIV,SAAS,EAAU,EAAwB,EAA2B,CACpE,OAAO,EAAK,KAAK,EAAgB,QAAiB,GAAG,EAAU,OAAO,CAGxE,SAAS,EAAU,EAAwB,EAAqC,CAC9E,IAAM,EAAS,EAAS,EAAU,EAAgB,EAAU,CAAC,CAI7D,MAHI,CAAC,EAAS,EAAO,EAAI,EAAO,SAAW,GAAuB,CAAC,MAAM,QAAQ,EAAO,KAAK,CACpF,CAAE,OAAQ,EAAqB,KAAM,EAAE,CAAE,CAE3C,CACL,OAAQ,EACR,KAAM,EAAO,KAAK,OAAQ,GAAuB,OAAO,GAAQ,SAAS,CAC1E,CAGH,SAAS,EAAW,EAAwB,EAAmB,EAAmB,CAEhF,IAAM,EAAO,CAAC,EAAK,GADH,EAAU,EAAgB,EACb,CAAC,KAAK,OAAQ,GAAc,IAAc,EAAI,CAAC,CAC5E,EACE,EAAU,EAAgB,EAAU,CACpC,GAAG,KAAK,UAAU,CAAE,OAAQ,EAAqB,OAAM,CAAE,KAAM,EAAE,CAAC,IACnE,CAGH,SAAS,EAAgB,EAAwB,EAAqB,CACpE,OAAO,EAAK,KAAK,EAAgB,UAAmB,EAAI,CAG1D,SAAS,EAAc,EAAmB,EAAmD,CAC3F,IAAM,EAAW,EAAiB,EAAS,KAAK,CAC3C,KACL,GAAI,CACF,GAAI,EAAG,UAAU,EAAU,CAAC,gBAAgB,CAAE,OAC9C,IAAM,EAAS,EAAK,KAAK,EAAW,EAAS,CAC7C,GAAI,EAAG,UAAU,EAAO,CAAC,gBAAgB,CAAE,OAC3C,IAAM,EAAqB,EAAG,aAAa,EAAU,CAC/C,EAAkB,EAAG,aAAa,EAAO,CACzC,EAAW,EAAK,SAAS,EAAoB,EAAgB,CACnE,GAAI,EAAS,WAAW,KAAK,EAAI,EAAK,WAAW,EAAS,CAAE,OAC5D,IAAM,EAAQ,EAAG,aAAa,EAAgB,CAC9C,OAAO,EAAc,EAAM,GAAK,EAAS,OAAS,EAAQ,IAAA,QACpD,CACN,QAIJ,SAAgB,EACd,EACA,EACA,EACiC,CAC5B,KAAe,KAAK,EAAU,CACnC,IAAK,IAAM,KAAO,EAAU,EAAgB,EAAU,CAAC,KAAM,CAC3D,GAAI,CAAC,EAAe,KAAK,EAAI,CAAE,SAC/B,IAAM,EAAY,EAAgB,EAAgB,EAAI,CAChD,EAAW,EAAc,EAAS,EAAK,KAAK,EAAW,EAAc,CAAC,CAAC,CAG7E,GAFI,CAAC,GAAY,EAAS,MAAQ,GAAO,EAAS,YAAc,GAAa,CAAC,EAAmB,EAAS,EAEtG,EAAS,UAAU,KAAM,GAAa,EAAc,EAAW,EAAS,GAAK,IAAA,GAAU,CAAE,SAC7F,IAAM,EAAe,IAAI,IACrB,EAAQ,GACZ,IAAK,IAAM,KAAS,EAAS,OAAQ,CACnC,IAAM,EAAS,EAAa,EAAM,YAAY,CAC9C,GAAI,CAAC,EAAQ,CACX,EAAQ,GACR,MAEF,GAAI,EAAM,SAAW,GACnB,GAAI,CACF,GAAI,EAAc,EAAG,aAAa,EAAO,CAAC,GAAK,EAAM,OAAQ,CAC3D,EAAQ,GACR,YAEI,CACN,EAAQ,GACR,MAGJ,EAAa,IAAI,EAAM,MAAO,EAAO,CAEvC,GAAI,EAAO,MAAO,CAAE,YAAW,WAAU,eAAc,EAK3D,SAAS,EACP,EACA,EACQ,CACR,OAAO,EACL,KAAK,UAAU,CACb,OAAQ,EACR,UAAW,EAAQ,UACnB,MAAO,EAAQ,MACf,OAAQ,EAAQ,OAChB,YACD,CAAC,CACH,CAGH,SAAS,EAAmB,EAAwC,CAClE,OACE,EAAS,MACT,EACE,CACE,UAAW,EAAS,UACpB,MAAO,EAAS,MAChB,OAAQ,EAAS,OACjB,UAAW,IAAI,IAChB,CACD,EAAS,UACV,CAIL,SAAgB,EAAmB,EAAyD,CAC1F,GAAI,CAAC,EAAe,KAAK,EAAQ,UAAU,CACzC,MAAU,MAAM,0DAA0D,EAAQ,YAAY,CAEhG,IAAM,EAAY,CAAC,GAAG,EAAQ,UAAU,SAAS,CAAC,CAC/C,KAAK,CAAC,EAAc,KAAc,CACjC,IAAM,EAAW,EAAiB,EAAa,CAC/C,GAAI,CAAC,EAAU,MAAU,MAAM,6CAA6C,IAAe,CAC3F,MAAO,CAAE,KAAM,EAAU,OAAQ,EAAc,EAAS,CAAE,EAC1D,CACD,MAAM,EAAM,IAAU,EAAK,KAAK,cAAc,EAAM,KAAK,CAAC,CACvD,EAAS,CAAC,GAAG,EAAQ,OAAO,CAAC,MAAM,EAAM,IAAU,EAAK,YAAY,cAAc,EAAM,YAAY,CAAC,CACrG,EAAM,EAAY,CAAE,GAAG,EAAS,SAAQ,CAAE,EAAU,CACpD,EAAgC,CACpC,OAAQ,EACR,MACA,UAAW,EAAQ,UACnB,MAAO,EAAQ,MACf,SACA,YACD,CACK,EAAiB,EAAgB,EAAQ,eAAgB,EAAI,CACnE,GAAI,CAAC,EAAG,WAAW,EAAe,CAAE,CAClC,IAAM,EAAqB,GAAG,EAAe,GAAG,QAAQ,IAAI,GAAG,EAAO,YAAY,CAAC,MACnF,GAAI,CACF,EAAG,UAAU,EAAoB,CAAE,UAAW,GAAM,KAAM,IAAO,CAAC,CAClE,IAAK,GAAM,CAAC,EAAc,KAAa,EAAQ,UAAW,CACxD,IAAM,EAAW,EAAiB,EAAa,CAC/C,GAAI,CAAC,EAAU,MAAU,MAAM,6CAA6C,IAAe,CAC3F,IAAM,EAAS,EAAK,KAAK,EAAoB,EAAS,CACtD,EAAG,UAAU,EAAK,QAAQ,EAAO,CAAE,CAAE,UAAW,GAAM,KAAM,IAAO,CAAC,CACpE,EAAG,cAAc,EAAQ,EAAU,CAAE,KAAM,IAAO,CAAC,CAErD,EAAG,cAAc,EAAK,KAAK,EAAoB,EAAc,CAAE,GAAG,KAAK,UAAU,EAAU,KAAM,EAAE,CAAC,IAAK,CACvG,KAAM,IACP,CAAC,CACF,EAAG,UAAU,EAAK,QAAQ,EAAe,CAAE,CAAE,UAAW,GAAM,KAAM,IAAO,CAAC,CAC5E,GAAI,CACF,EAAG,WAAW,EAAoB,EAAe,OAC1C,EAAO,CACd,GAAI,CAAC,EAAG,WAAW,EAAe,CAAE,MAAM,UAEpC,CACR,EAAG,OAAO,EAAoB,CAAE,UAAW,GAAM,MAAO,GAAM,CAAC,EAInE,OADA,EAAW,EAAQ,eAAgB,EAAQ,UAAW,EAAI,CACnD,EAGT,SAAgB,EAAuB,EAA4B,EAAiC,CAClG,IAAM,EAAe,CAAC,GAAG,EAAM,aAAa,SAAS,CAAC,CAAC,MAAM,CAAC,GAAO,CAAC,KAAW,EAAM,OAAS,EAAK,OAAO,CAC5G,IAAK,IAAM,KAAY,EAAM,SAAS,UAAW,CAC/C,IAAM,EAAQ,EAAc,EAAM,UAAW,EAAS,CACtD,GAAI,CAAC,EAAO,MAAU,MAAM,qCAAqC,EAAS,OAAO,CACjF,IAAI,EAAgC,EACpC,GAAI,EAAS,KAAK,SAAS,OAAO,CAAE,CAClC,IAAI,EAAS,EAAM,SAAS,OAAO,CACnC,IAAK,GAAM,CAAC,EAAO,KAAW,EAC5B,EAAS,EAAO,WAAW,GAAG,EAAM,MAAO,EAAc,EAAO,CAAC,KAAK,CAAC,WAAW,EAAO,EAAO,CAElG,GAAI,EAAO,SAAS,iBAAiB,CACnC,MAAU,MAAM,+CAA+C,EAAS,OAAO,CACjF,EAAW,EAEb,EAAY,EAAK,KAAK,EAAiB,EAAS,KAAK,CAAE,EAAS,CAElE,OAAO,EAAK,KAAK,EAAiB,EAAM,SAAS,MAAM,CAGzD,SAAS,EAAa,EAA4B,CAChD,GAAI,CAEF,OADA,QAAQ,KAAK,EAAW,EAAE,CACnB,QACD,CACN,MAAO,IAIX,SAAS,EAAY,EAAgC,CACnD,IAAM,EAAQ,EAAS,EAAK,KAAK,EAAe,aAAa,CAAC,CAE9D,MADI,CAAC,EAAS,EAAM,EAAI,OAAO,EAAM,KAAQ,UAAY,OAAO,EAAM,WAAc,SAAiB,GAC9F,KAAK,KAAK,CAAG,EAAM,UAAY,KAAiB,CAAC,EAAa,EAAM,IAAI,CAGjF,eAAe,EAAM,EAAqC,CACxD,MAAM,IAAI,QAAe,GAAY,WAAW,EAAS,EAAa,CAAC,CAGzE,eAAsB,EACpB,EACA,EACA,EACY,CACZ,GAAI,CAAC,EAAe,KAAK,EAAU,CACjC,MAAU,MAAM,wDAAwD,IAAY,CAEtF,IAAM,EAAgB,EAAK,KAAK,EAAgB,QAAiB,GAAG,EAAU,OAAO,CACrF,EAAG,UAAU,EAAK,QAAQ,EAAc,CAAE,CAAE,UAAW,GAAM,KAAM,IAAO,CAAC,CAC3E,IAAM,EAAY,KAAK,KAAK,CAC5B,OACE,GAAI,CACF,EAAG,UAAU,EAAe,CAAE,KAAM,IAAO,CAAC,CAC5C,EAAG,cACD,EAAK,KAAK,EAAe,aAAa,CACtC,GAAG,KAAK,UAAU,CAAE,IAAK,QAAQ,IAAK,UAAW,KAAK,KAAK,CAAE,CAAC,CAAC,IAC/D,CAAE,KAAM,IAAO,CAChB,CACD,YACO,EAAO,CACd,GAAI,EAAE,aAAiB,QAAU,EAAE,SAAU,IAAU,EAAM,OAAS,SAAU,MAAM,EACtF,GAAI,EAAY,EAAc,CAAE,CAC9B,EAAG,OAAO,EAAe,CAAE,UAAW,GAAM,MAAO,GAAM,CAAC,CAC1D,SAEF,GAAI,KAAK,KAAK,CAAG,GAAa,IAC5B,MAAU,MAAM,2CAA2C,IAAY,CAEzE,MAAM,EAAM,GAAc,CAG9B,GAAI,CACF,OAAO,MAAM,GAAW,QAChB,CACR,EAAG,OAAO,EAAe,CAAE,UAAW,GAAM,MAAO,GAAM,CAAC"}
|
|
1
|
+
{"version":3,"file":"sharedBuildCache.mjs","names":[],"sources":["../../src/adapters/sharedBuildCache.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nconst SHARED_BUILD_SCHEMA = 1;\nconst OBJECTS_DIRECTORY = 'objects';\nconst INDEX_DIRECTORY = 'index';\nconst LOCKS_DIRECTORY = 'locks';\nconst MANIFEST_FILE = 'manifest.json';\nconst OWNER_FILE = 'owner.json';\nconst LOCK_STALE_MS = 5 * 60_000;\nconst LOCK_TIMEOUT_MS = 60_000;\nconst LOCK_RETRY_MS = 25;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/u;\n\nexport interface SharedBuildInput {\n logicalPath: string;\n sha256: string;\n token: string;\n /** Canonical source path for inputs outside the synchronized repository. */\n sourcePath?: string;\n}\n\nexport interface SharedBuildArtifact {\n path: string;\n sha256: string;\n}\n\nexport interface SharedBuildManifest {\n schema: number;\n key: string;\n lookupKey: string;\n entry: string;\n inputs: SharedBuildInput[];\n artifacts: SharedBuildArtifact[];\n}\n\nexport interface ResolvedSharedBuild {\n directory: string;\n manifest: SharedBuildManifest;\n replacements: ReadonlyMap<string, string>;\n}\n\ninterface SharedBuildIndex {\n schema: number;\n keys: string[];\n}\n\nexport interface PublishSharedBuildOptions {\n cacheDirectory: string;\n lookupKey: string;\n entry: string;\n inputs: SharedBuildInput[];\n artifacts: ReadonlyMap<string, string | Uint8Array>;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport function contentSha256(contents: string | Uint8Array): string {\n return crypto.createHash('sha256').update(contents).digest('hex');\n}\n\nfunction safeRelativePath(value: string): string | undefined {\n if (value === '' || path.isAbsolute(value)) return undefined;\n const normalized = path.normalize(value);\n return normalized === '..' || normalized.startsWith(`..${path.sep}`) ? undefined : normalized;\n}\n\nfunction parseManifest(value: unknown): SharedBuildManifest | undefined {\n if (\n !isRecord(value) ||\n value.schema !== SHARED_BUILD_SCHEMA ||\n typeof value.key !== 'string' ||\n !SHA256_PATTERN.test(value.key) ||\n typeof value.lookupKey !== 'string' ||\n !SHA256_PATTERN.test(value.lookupKey) ||\n typeof value.entry !== 'string' ||\n !Array.isArray(value.inputs) ||\n !Array.isArray(value.artifacts)\n ) {\n return undefined;\n }\n const inputs: SharedBuildInput[] = [];\n for (const input of value.inputs) {\n if (\n !isRecord(input) ||\n typeof input.logicalPath !== 'string' ||\n typeof input.sha256 !== 'string' ||\n (input.sha256 !== '' && !SHA256_PATTERN.test(input.sha256)) ||\n typeof input.token !== 'string' ||\n (input.sourcePath !== undefined && (typeof input.sourcePath !== 'string' || !path.isAbsolute(input.sourcePath)))\n ) {\n return undefined;\n }\n inputs.push({\n logicalPath: input.logicalPath,\n sha256: input.sha256,\n token: input.token,\n ...(typeof input.sourcePath === 'string' ? { sourcePath: input.sourcePath } : {}),\n });\n }\n const artifacts: SharedBuildArtifact[] = [];\n for (const artifact of value.artifacts) {\n if (\n !isRecord(artifact) ||\n typeof artifact.path !== 'string' ||\n typeof artifact.sha256 !== 'string' ||\n !SHA256_PATTERN.test(artifact.sha256)\n ) {\n return undefined;\n }\n if (!safeRelativePath(artifact.path)) return undefined;\n artifacts.push({ path: artifact.path, sha256: artifact.sha256 });\n }\n if (!safeRelativePath(value.entry)) return undefined;\n return {\n schema: SHARED_BUILD_SCHEMA,\n key: value.key,\n lookupKey: value.lookupKey,\n entry: value.entry,\n inputs,\n artifacts,\n };\n}\n\nfunction readJson(target: string): unknown {\n try {\n return JSON.parse(fs.readFileSync(target, 'utf8')) as unknown;\n } catch {\n return undefined;\n }\n}\n\nfunction writeAtomic(target: string, contents: string | Uint8Array): void {\n fs.mkdirSync(path.dirname(target), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n const temporary = `${target}.${process.pid}.${crypto.randomUUID()}.tmp`;\n try {\n fs.writeFileSync(temporary, contents, { mode: PRIVATE_FILE_MODE });\n fs.renameSync(temporary, target);\n fs.chmodSync(target, PRIVATE_FILE_MODE);\n } catch (error) {\n fs.rmSync(temporary, { force: true });\n throw error;\n }\n}\n\nfunction indexPath(cacheDirectory: string, lookupKey: string): string {\n return path.join(cacheDirectory, INDEX_DIRECTORY, `${lookupKey}.json`);\n}\n\nfunction readIndex(cacheDirectory: string, lookupKey: string): SharedBuildIndex {\n const parsed = readJson(indexPath(cacheDirectory, lookupKey));\n if (!isRecord(parsed) || parsed.schema !== SHARED_BUILD_SCHEMA || !Array.isArray(parsed.keys)) {\n return { schema: SHARED_BUILD_SCHEMA, keys: [] };\n }\n return {\n schema: SHARED_BUILD_SCHEMA,\n keys: parsed.keys.filter((key): key is string => typeof key === 'string'),\n };\n}\n\nfunction writeIndex(cacheDirectory: string, lookupKey: string, key: string): void {\n const current = readIndex(cacheDirectory, lookupKey);\n const keys = [key, ...current.keys.filter((candidate) => candidate !== key)];\n writeAtomic(\n indexPath(cacheDirectory, lookupKey),\n `${JSON.stringify({ schema: SHARED_BUILD_SCHEMA, keys }, null, 2)}\\n`,\n );\n}\n\nfunction objectDirectory(cacheDirectory: string, key: string): string {\n return path.join(cacheDirectory, OBJECTS_DIRECTORY, key);\n}\n\nfunction artifactBytes(directory: string, artifact: SharedBuildArtifact): Buffer | undefined {\n const relative = safeRelativePath(artifact.path);\n if (!relative) return undefined;\n try {\n if (fs.lstatSync(directory).isSymbolicLink()) return undefined;\n const target = path.join(directory, relative);\n if (fs.lstatSync(target).isSymbolicLink()) return undefined;\n const canonicalDirectory = fs.realpathSync(directory);\n const canonicalTarget = fs.realpathSync(target);\n const confined = path.relative(canonicalDirectory, canonicalTarget);\n if (confined.startsWith('..') || path.isAbsolute(confined)) return undefined;\n const bytes = fs.readFileSync(canonicalTarget);\n return contentSha256(bytes) === artifact.sha256 ? bytes : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function findSharedBuild(\n cacheDirectory: string,\n lookupKey: string,\n resolveInput: (input: SharedBuildInput) => string | undefined,\n): ResolvedSharedBuild | undefined {\n if (!SHA256_PATTERN.test(lookupKey)) return undefined;\n for (const key of readIndex(cacheDirectory, lookupKey).keys) {\n if (!SHA256_PATTERN.test(key)) continue;\n const directory = objectDirectory(cacheDirectory, key);\n const manifest = parseManifest(readJson(path.join(directory, MANIFEST_FILE)));\n if (!manifest || manifest.key !== key || manifest.lookupKey !== lookupKey || !manifestKeyMatches(manifest))\n continue;\n if (manifest.artifacts.some((artifact) => artifactBytes(directory, artifact) === undefined)) continue;\n const replacements = new Map<string, string>();\n let valid = true;\n for (const input of manifest.inputs) {\n const target = resolveInput(input);\n if (!target) {\n valid = false;\n break;\n }\n if (input.sha256 !== '') {\n try {\n if (contentSha256(fs.readFileSync(target)) !== input.sha256) {\n valid = false;\n break;\n }\n } catch {\n valid = false;\n break;\n }\n }\n replacements.set(input.token, target);\n }\n if (valid) return { directory, manifest, replacements };\n }\n return undefined;\n}\n\nfunction manifestKey(\n options: Omit<PublishSharedBuildOptions, 'cacheDirectory'>,\n artifacts: SharedBuildArtifact[],\n): string {\n return contentSha256(\n JSON.stringify({\n schema: SHARED_BUILD_SCHEMA,\n lookupKey: options.lookupKey,\n entry: options.entry,\n inputs: options.inputs,\n artifacts,\n }),\n );\n}\n\nfunction manifestKeyMatches(manifest: SharedBuildManifest): boolean {\n return (\n manifest.key ===\n manifestKey(\n {\n lookupKey: manifest.lookupKey,\n entry: manifest.entry,\n inputs: manifest.inputs,\n artifacts: new Map(),\n },\n manifest.artifacts,\n )\n );\n}\n\nfunction sharedObjectMatches(directory: string, expected: SharedBuildManifest): boolean {\n const current = parseManifest(readJson(path.join(directory, MANIFEST_FILE)));\n if (!current || current.key !== expected.key || !manifestKeyMatches(current)) return false;\n return expected.artifacts.every((artifact) => artifactBytes(directory, artifact) !== undefined);\n}\n\nexport function publishSharedBuild(options: PublishSharedBuildOptions): SharedBuildManifest {\n if (!SHA256_PATTERN.test(options.lookupKey)) {\n throw new Error(`Shared build lookup key must be a full SHA-256 digest: ${options.lookupKey}`);\n }\n if (options.inputs.some((input) => input.sourcePath !== undefined && !path.isAbsolute(input.sourcePath))) {\n throw new Error('Shared build external source paths must be absolute');\n }\n const artifacts = [...options.artifacts.entries()]\n .map(([artifactPath, contents]) => {\n const relative = safeRelativePath(artifactPath);\n if (!relative) throw new Error(`Shared build artifact escapes its object: ${artifactPath}`);\n return { path: relative, sha256: contentSha256(contents) };\n })\n .sort((left, right) => left.path.localeCompare(right.path));\n const inputs = [...options.inputs].sort((left, right) => left.logicalPath.localeCompare(right.logicalPath));\n const key = manifestKey({ ...options, inputs }, artifacts);\n const manifest: SharedBuildManifest = {\n schema: SHARED_BUILD_SCHEMA,\n key,\n lookupKey: options.lookupKey,\n entry: options.entry,\n inputs,\n artifacts,\n };\n const finalDirectory = objectDirectory(options.cacheDirectory, key);\n if (fs.existsSync(finalDirectory) && !sharedObjectMatches(finalDirectory, manifest)) {\n fs.rmSync(finalDirectory, { recursive: true, force: true });\n }\n if (!fs.existsSync(finalDirectory)) {\n const temporaryDirectory = `${finalDirectory}.${process.pid}.${crypto.randomUUID()}.tmp`;\n try {\n fs.mkdirSync(temporaryDirectory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n for (const [artifactPath, contents] of options.artifacts) {\n const relative = safeRelativePath(artifactPath);\n if (!relative) throw new Error(`Shared build artifact escapes its object: ${artifactPath}`);\n const target = path.join(temporaryDirectory, relative);\n fs.mkdirSync(path.dirname(target), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n fs.writeFileSync(target, contents, { mode: PRIVATE_FILE_MODE });\n }\n fs.writeFileSync(path.join(temporaryDirectory, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\\n`, {\n mode: PRIVATE_FILE_MODE,\n });\n fs.mkdirSync(path.dirname(finalDirectory), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n try {\n fs.renameSync(temporaryDirectory, finalDirectory);\n } catch (error) {\n if (!fs.existsSync(finalDirectory)) throw error;\n }\n } finally {\n fs.rmSync(temporaryDirectory, { recursive: true, force: true });\n }\n }\n writeIndex(options.cacheDirectory, options.lookupKey, key);\n return manifest;\n}\n\nexport function materializeSharedBuild(build: ResolvedSharedBuild, outputDirectory: string): string {\n const replacements = [...build.replacements.entries()].sort(([left], [right]) => right.length - left.length);\n for (const artifact of build.manifest.artifacts) {\n const bytes = artifactBytes(build.directory, artifact);\n if (!bytes) throw new Error(`Shared build artifact is corrupt: ${artifact.path}`);\n let contents: string | Uint8Array = bytes;\n if (artifact.path.endsWith('.mjs')) {\n let source = bytes.toString('utf8');\n for (const [token, target] of replacements) {\n source = source.replaceAll(`${token}:url`, pathToFileURL(target).href).replaceAll(token, target);\n }\n if (source.includes('__DOOMPI_PATH_'))\n throw new Error(`Shared build left unresolved path tokens in ${artifact.path}`);\n contents = source;\n }\n writeAtomic(path.join(outputDirectory, artifact.path), contents);\n }\n return path.join(outputDirectory, build.manifest.entry);\n}\n\nfunction processAlive(processId: number): boolean {\n try {\n process.kill(processId, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction lockIsStale(lockDirectory: string): boolean {\n const owner = readJson(path.join(lockDirectory, OWNER_FILE));\n if (!isRecord(owner) || typeof owner.pid !== 'number' || typeof owner.createdAt !== 'number') return true;\n return Date.now() - owner.createdAt > LOCK_STALE_MS && !processAlive(owner.pid);\n}\n\nasync function sleep(milliseconds: number): Promise<void> {\n await new Promise<void>((resolve) => setTimeout(resolve, milliseconds));\n}\n\nexport async function withSharedBuildLock<T>(\n cacheDirectory: string,\n lookupKey: string,\n operation: () => Promise<T>,\n): Promise<T> {\n if (!SHA256_PATTERN.test(lookupKey)) {\n throw new Error(`Shared build lock key must be a full SHA-256 digest: ${lookupKey}`);\n }\n const lockDirectory = path.join(cacheDirectory, LOCKS_DIRECTORY, `${lookupKey}.lock`);\n fs.mkdirSync(path.dirname(lockDirectory), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n const startedAt = Date.now();\n while (true) {\n try {\n fs.mkdirSync(lockDirectory, { mode: PRIVATE_DIRECTORY_MODE });\n fs.writeFileSync(\n path.join(lockDirectory, OWNER_FILE),\n `${JSON.stringify({ pid: process.pid, createdAt: Date.now() })}\\n`,\n { mode: PRIVATE_FILE_MODE },\n );\n break;\n } catch (error) {\n if (!(error instanceof Error) || !('code' in error) || error.code !== 'EEXIST') throw error;\n if (lockIsStale(lockDirectory)) {\n fs.rmSync(lockDirectory, { recursive: true, force: true });\n continue;\n }\n if (Date.now() - startedAt >= LOCK_TIMEOUT_MS) {\n throw new Error(`Timed out waiting for shared build lock ${lookupKey}`);\n }\n await sleep(LOCK_RETRY_MS);\n }\n }\n try {\n return await operation();\n } finally {\n fs.rmSync(lockDirectory, { recursive: true, force: true });\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/doompi",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.21",
|
|
4
4
|
"description": "Pi-based agent harness for repository automations and developer workflows",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -346,24 +346,24 @@
|
|
|
346
346
|
"rolldown": "1.0.0-rc.17",
|
|
347
347
|
"typebox": "1.1.38",
|
|
348
348
|
"yaml": "2.9.0",
|
|
349
|
-
"@agimon-ai/doompi-autocompact": "0.0.1-alpha.
|
|
350
|
-
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.
|
|
351
|
-
"@agimon-ai/doompi-
|
|
352
|
-
"@agimon-ai/doompi-
|
|
353
|
-
"@agimon-ai/doompi-
|
|
354
|
-
"@agimon-ai/doompi-
|
|
355
|
-
"@agimon-ai/doompi-
|
|
356
|
-
"@agimon-ai/doompi-
|
|
357
|
-
"@agimon-ai/doompi-
|
|
358
|
-
"@agimon-ai/doompi-
|
|
359
|
-
"@agimon-ai/doompi-
|
|
360
|
-
"@agimon-ai/doompi-
|
|
361
|
-
"@agimon-ai/doompi-
|
|
362
|
-
"@agimon-ai/doompi-
|
|
363
|
-
"@agimon-ai/doompi-
|
|
364
|
-
"@agimon-ai/doompi-
|
|
365
|
-
"@agimon-ai/doompi-
|
|
366
|
-
"@agimon-ai/doompi-
|
|
349
|
+
"@agimon-ai/doompi-autocompact": "0.0.1-alpha.21",
|
|
350
|
+
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.21",
|
|
351
|
+
"@agimon-ai/doompi-config": "0.0.1-alpha.21",
|
|
352
|
+
"@agimon-ai/doompi-help": "0.0.1-alpha.4",
|
|
353
|
+
"@agimon-ai/doompi-log": "0.0.1-alpha.21",
|
|
354
|
+
"@agimon-ai/doompi-loop": "0.0.1-alpha.21",
|
|
355
|
+
"@agimon-ai/doompi-file-edit": "0.0.1-alpha.21",
|
|
356
|
+
"@agimon-ai/doompi-mcp": "0.0.1-alpha.21",
|
|
357
|
+
"@agimon-ai/doompi-plan": "0.0.1-alpha.21",
|
|
358
|
+
"@agimon-ai/doompi-goal": "0.0.1-alpha.11",
|
|
359
|
+
"@agimon-ai/doompi-runner": "0.0.1-alpha.21",
|
|
360
|
+
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.21",
|
|
361
|
+
"@agimon-ai/doompi-user-feedback": "0.0.1-alpha.10",
|
|
362
|
+
"@agimon-ai/doompi-voice": "0.0.1-alpha.21",
|
|
363
|
+
"@agimon-ai/doompi-workflow": "0.0.1-alpha.21",
|
|
364
|
+
"@agimon-ai/doompi-team": "0.0.1-alpha.21",
|
|
365
|
+
"@agimon-ai/doompi-ui": "0.0.1-alpha.21",
|
|
366
|
+
"@agimon-ai/doompi-task": "0.0.1-alpha.21"
|
|
367
367
|
},
|
|
368
368
|
"devDependencies": {
|
|
369
369
|
"@earendil-works/pi-ai": "0.84.2",
|