everything-dev 2.0.0-rc.1 → 2.0.0-rc.5

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.
Files changed (151) hide show
  1. package/dist/api-contract.cjs +3 -6
  2. package/dist/api-contract.cjs.map +1 -1
  3. package/dist/api-contract.mjs +3 -6
  4. package/dist/api-contract.mjs.map +1 -1
  5. package/dist/apps/upload/src/client.cjs +188 -0
  6. package/dist/apps/upload/src/client.cjs.map +1 -0
  7. package/dist/apps/upload/src/client.mjs +180 -0
  8. package/dist/apps/upload/src/client.mjs.map +1 -0
  9. package/dist/auth-login.cjs +1 -1
  10. package/dist/auth-login.mjs +1 -1
  11. package/dist/build.cjs +2 -2
  12. package/dist/build.mjs +2 -2
  13. package/dist/bundle-fs-resolve.cjs +9 -17
  14. package/dist/bundle-fs-resolve.cjs.map +1 -1
  15. package/dist/bundle-fs-resolve.d.cts +6 -0
  16. package/dist/bundle-fs-resolve.d.mts +6 -0
  17. package/dist/bundle-fs-resolve.mjs +9 -17
  18. package/dist/bundle-fs-resolve.mjs.map +1 -1
  19. package/dist/bundle-path.cjs +48 -0
  20. package/dist/bundle-path.cjs.map +1 -0
  21. package/dist/bundle-path.mjs +45 -0
  22. package/dist/bundle-path.mjs.map +1 -0
  23. package/dist/cdn-deploy.cjs +29 -11
  24. package/dist/cdn-deploy.cjs.map +1 -1
  25. package/dist/cdn-deploy.mjs +29 -11
  26. package/dist/cdn-deploy.mjs.map +1 -1
  27. package/dist/cli/init.cjs +4 -6
  28. package/dist/cli/init.cjs.map +1 -1
  29. package/dist/cli/init.d.cts +1 -1
  30. package/dist/cli/init.d.mts +1 -1
  31. package/dist/cli/init.mjs +4 -6
  32. package/dist/cli/init.mjs.map +1 -1
  33. package/dist/cli/status.cjs +69 -3
  34. package/dist/cli/status.cjs.map +1 -1
  35. package/dist/cli/status.mjs +67 -4
  36. package/dist/cli/status.mjs.map +1 -1
  37. package/dist/cli/sync.cjs +1 -2
  38. package/dist/cli/sync.cjs.map +1 -1
  39. package/dist/cli/sync.mjs +1 -2
  40. package/dist/cli/sync.mjs.map +1 -1
  41. package/dist/cli/upgrade.cjs +2 -2
  42. package/dist/cli/upgrade.cjs.map +1 -1
  43. package/dist/cli/upgrade.mjs +2 -2
  44. package/dist/cli/upgrade.mjs.map +1 -1
  45. package/dist/cli.cjs +5 -7
  46. package/dist/cli.cjs.map +1 -1
  47. package/dist/cli.mjs +5 -7
  48. package/dist/cli.mjs.map +1 -1
  49. package/dist/commands/deploy.cjs +26 -18
  50. package/dist/commands/deploy.cjs.map +1 -1
  51. package/dist/commands/deploy.mjs +26 -18
  52. package/dist/commands/deploy.mjs.map +1 -1
  53. package/dist/commands/init.cjs +1 -1
  54. package/dist/commands/init.cjs.map +1 -1
  55. package/dist/commands/init.mjs +1 -1
  56. package/dist/commands/init.mjs.map +1 -1
  57. package/dist/commands/plugins.cjs +2 -2
  58. package/dist/commands/plugins.cjs.map +1 -1
  59. package/dist/commands/plugins.mjs +2 -2
  60. package/dist/commands/plugins.mjs.map +1 -1
  61. package/dist/commands/upgrade.cjs +31 -8
  62. package/dist/commands/upgrade.cjs.map +1 -1
  63. package/dist/commands/upgrade.mjs +31 -8
  64. package/dist/commands/upgrade.mjs.map +1 -1
  65. package/dist/components/dev-render.cjs +1 -1
  66. package/dist/components/dev-render.mjs +1 -1
  67. package/dist/config.cjs +3 -3
  68. package/dist/config.cjs.map +1 -1
  69. package/dist/config.mjs +3 -3
  70. package/dist/config.mjs.map +1 -1
  71. package/dist/contract.cjs +2 -0
  72. package/dist/contract.cjs.map +1 -1
  73. package/dist/contract.d.cts +3 -0
  74. package/dist/contract.d.mts +3 -0
  75. package/dist/contract.meta.cjs +2 -1
  76. package/dist/contract.meta.cjs.map +1 -1
  77. package/dist/contract.meta.d.cts +4 -1
  78. package/dist/contract.meta.d.mts +4 -1
  79. package/dist/contract.meta.mjs +2 -1
  80. package/dist/contract.meta.mjs.map +1 -1
  81. package/dist/contract.mjs +2 -0
  82. package/dist/contract.mjs.map +1 -1
  83. package/dist/db/driver.cjs +22 -2
  84. package/dist/db/driver.cjs.map +1 -1
  85. package/dist/db/driver.d.cts +13 -2
  86. package/dist/db/driver.d.mts +13 -2
  87. package/dist/db/driver.mjs +22 -3
  88. package/dist/db/driver.mjs.map +1 -1
  89. package/dist/db/index.cjs +1 -0
  90. package/dist/db/index.d.cts +2 -2
  91. package/dist/db/index.d.mts +2 -2
  92. package/dist/db/index.mjs +2 -2
  93. package/dist/deploy-diff.cjs +1 -1
  94. package/dist/deploy-diff.cjs.map +1 -1
  95. package/dist/deploy-diff.mjs +1 -1
  96. package/dist/deploy-diff.mjs.map +1 -1
  97. package/dist/dev-program.cjs +11 -2
  98. package/dist/dev-program.cjs.map +1 -1
  99. package/dist/dev-program.mjs +11 -3
  100. package/dist/dev-program.mjs.map +1 -1
  101. package/dist/image-deploy.cjs +103 -31
  102. package/dist/image-deploy.cjs.map +1 -1
  103. package/dist/image-deploy.mjs +103 -33
  104. package/dist/image-deploy.mjs.map +1 -1
  105. package/dist/internal/manifest-normalizer.cjs.map +1 -1
  106. package/dist/internal/manifest-normalizer.mjs.map +1 -1
  107. package/dist/plugin.d.cts +902 -1
  108. package/dist/plugin.d.mts +902 -1
  109. package/dist/publish.cjs +51 -2
  110. package/dist/publish.cjs.map +1 -1
  111. package/dist/publish.mjs +51 -4
  112. package/dist/publish.mjs.map +1 -1
  113. package/dist/resolution/session.cjs.map +1 -1
  114. package/dist/resolution/session.d.cts +5 -5
  115. package/dist/resolution/session.d.mts +5 -5
  116. package/dist/resolution/session.mjs.map +1 -1
  117. package/dist/storage-upload.cjs +7 -68
  118. package/dist/storage-upload.cjs.map +1 -1
  119. package/dist/storage-upload.mjs +7 -68
  120. package/dist/storage-upload.mjs.map +1 -1
  121. package/dist/types.d.cts +31 -1
  122. package/dist/types.d.mts +31 -1
  123. package/dist/ui/api.cjs +11 -69
  124. package/dist/ui/api.cjs.map +1 -1
  125. package/dist/ui/api.d.cts +2 -4
  126. package/dist/ui/api.d.mts +2 -4
  127. package/dist/ui/api.mjs +11 -69
  128. package/dist/ui/api.mjs.map +1 -1
  129. package/dist/ui/auth-guards.d.cts +78 -2
  130. package/dist/ui/auth-guards.d.mts +78 -2
  131. package/dist/ui/auth.cjs.map +1 -1
  132. package/dist/ui/auth.d.cts +4133 -7
  133. package/dist/ui/auth.d.mts +4133 -7
  134. package/dist/ui/auth.mjs.map +1 -1
  135. package/dist/ui/hydrate.cjs +2 -2
  136. package/dist/ui/hydrate.cjs.map +1 -1
  137. package/dist/ui/hydrate.mjs +2 -2
  138. package/dist/ui/hydrate.mjs.map +1 -1
  139. package/dist/utils/theme.cjs +2 -2
  140. package/dist/utils/theme.cjs.map +1 -1
  141. package/dist/utils/theme.mjs +2 -2
  142. package/dist/utils/theme.mjs.map +1 -1
  143. package/dist/version-manifest-resolve.cjs +55 -10
  144. package/dist/version-manifest-resolve.cjs.map +1 -1
  145. package/dist/version-manifest-resolve.mjs +53 -10
  146. package/dist/version-manifest-resolve.mjs.map +1 -1
  147. package/dist/workspace.cjs +26 -13
  148. package/dist/workspace.cjs.map +1 -1
  149. package/dist/workspace.mjs +26 -14
  150. package/dist/workspace.mjs.map +1 -1
  151. package/package.json +12 -10
@@ -1 +1 @@
1
- {"version":3,"file":"version-manifest-resolve.mjs","names":[],"sources":["../src/version-manifest-resolve.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n type WorkspaceVersionManifest,\n WorkspaceVersionManifestSchema,\n} from \"every-plugin/version-manifest\";\n\n/**\n * Version-manifest slot resolution (atomic-deploys 04): a config slot's\n * `pin` ({ manifest, integrity }) resolves to the derived entry-level fields\n * every consumer needs. The cache is keyed by the full pin — the pinned\n * bytes are immutable, so a verified entry is cached for the process\n * lifetime and serves as last-known-good if the origin later fails; a fetch\n * or SRI failure with nothing cached propagates (a boot-time resolution\n * failure fails the deploy, which is the platform's rollback).\n */\n\nexport interface ResolvedSlotVersion {\n /** Absolute URL of the content-hashed container entry. */\n entryUrl: string;\n entryIntegrity: string;\n /** Absolute URL of the hashed browser manifest (mf-manifest), when present. */\n browserManifestUrl?: string;\n /** Absolute URL of the content-hashed SSR entry, when present. */\n ssrEntryUrl?: string;\n ssrIntegrity?: string;\n /** Full per-file digest map of the dist (object path → SRI), when the\n * pinned manifest carries one — the diff base for skip-unchanged uploads. */\n files?: Record<string, string>;\n}\n\nexport interface SlotPin {\n /** versioned manifest filename, relative to the slot's production base */\n manifest: string;\n /** the manifest document's own SRI */\n integrity: string;\n}\n\nconst cache = new Map<string, ResolvedSlotVersion>();\n\nexport function clearSlotVersionCache(): void {\n cache.clear();\n}\n\nfunction fetchText(url: string, fetchImpl: typeof fetch): Promise<string> {\n return fetchImpl(url).then((response) => {\n if (!response.ok) throw new Error(`version manifest fetch failed: ${response.status}`);\n return response.text();\n });\n}\n\nexport async function resolveSlotVersion(input: {\n base: string;\n pin: SlotPin;\n fetchImpl?: typeof fetch;\n}): Promise<ResolvedSlotVersion> {\n const key = `${input.base}::${input.pin.manifest}::${input.pin.integrity}`;\n // content-addressed: a verified pin is cached for the process lifetime and\n // never refetched — the cache IS the last-known-good\n const cached = cache.get(key);\n if (cached) return cached;\n\n const manifestUrl = `${input.base.replace(/\\/$/, \"\")}/${input.pin.manifest.replace(/^\\//, \"\")}`;\n const body = await fetchText(manifestUrl, input.fetchImpl ?? fetch);\n const computed = `sha384-${createHash(\"sha384\").update(body).digest(\"base64\")}`;\n if (computed !== input.pin.integrity) {\n throw new Error(\n `[SRI] version manifest integrity mismatch for ${manifestUrl}\\n Expected: ${input.pin.integrity}\\n Computed: ${computed}`,\n );\n }\n const manifest: WorkspaceVersionManifest = WorkspaceVersionManifestSchema.parse(JSON.parse(body));\n\n const resolved: ResolvedSlotVersion = {\n entryUrl: `${input.base.replace(/\\/$/, \"\")}/${manifest.entry}`,\n entryIntegrity: manifest.entryIntegrity,\n ...(manifest.browserManifest\n ? {\n browserManifestUrl: `${input.base.replace(/\\/$/, \"\")}/${manifest.browserManifest.file}`,\n }\n : {}),\n ...(manifest.ssr\n ? {\n ssrEntryUrl: `${input.base.replace(/\\/$/, \"\")}/${manifest.ssr.entry}`,\n ssrIntegrity: manifest.ssr.integrity,\n }\n : {}),\n ...(manifest.files ? { files: manifest.files } : {}),\n };\n cache.set(key, resolved);\n return resolved;\n}\n"],"mappings":";;;;AAqCA,MAAM,wBAAQ,IAAI,IAAiC;AAEnD,SAAgB,wBAA8B;CAC5C,MAAM,MAAM;AACd;AAEA,SAAS,UAAU,KAAa,WAA0C;CACxE,OAAO,UAAU,GAAG,CAAC,CAAC,MAAM,aAAa;EACvC,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,kCAAkC,SAAS,QAAQ;EACrF,OAAO,SAAS,KAAK;CACvB,CAAC;AACH;AAEA,eAAsB,mBAAmB,OAIR;CAC/B,MAAM,MAAM,GAAG,MAAM,KAAK,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,IAAI;CAG/D,MAAM,SAAS,MAAM,IAAI,GAAG;CAC5B,IAAI,QAAQ,OAAO;CAEnB,MAAM,cAAc,GAAG,MAAM,KAAK,QAAQ,OAAO,EAAE,EAAE,GAAG,MAAM,IAAI,SAAS,QAAQ,OAAO,EAAE;CAC5F,MAAM,OAAO,MAAM,UAAU,aAAa,MAAM,aAAa,KAAK;CAClE,MAAM,WAAW,UAAU,WAAW,QAAQ,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,QAAQ;CAC5E,IAAI,aAAa,MAAM,IAAI,WACzB,MAAM,IAAI,MACR,iDAAiD,YAAY,gBAAgB,MAAM,IAAI,UAAU,gBAAgB,UACnH;CAEF,MAAM,WAAqC,+BAA+B,MAAM,KAAK,MAAM,IAAI,CAAC;CAEhG,MAAM,WAAgC;EACpC,UAAU,GAAG,MAAM,KAAK,QAAQ,OAAO,EAAE,EAAE,GAAG,SAAS;EACvD,gBAAgB,SAAS;EACzB,GAAI,SAAS,kBACT,EACE,oBAAoB,GAAG,MAAM,KAAK,QAAQ,OAAO,EAAE,EAAE,GAAG,SAAS,gBAAgB,OACnF,IACA,CAAC;EACL,GAAI,SAAS,MACT;GACE,aAAa,GAAG,MAAM,KAAK,QAAQ,OAAO,EAAE,EAAE,GAAG,SAAS,IAAI;GAC9D,cAAc,SAAS,IAAI;EAC7B,IACA,CAAC;EACL,GAAI,SAAS,QAAQ,EAAE,OAAO,SAAS,MAAM,IAAI,CAAC;CACpD;CACA,MAAM,IAAI,KAAK,QAAQ;CACvB,OAAO;AACT"}
1
+ {"version":3,"file":"version-manifest-resolve.mjs","names":[],"sources":["../src/version-manifest-resolve.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\nimport {\n type WorkspaceVersionManifest,\n WorkspaceVersionManifestSchema,\n} from \"every-plugin/version-manifest\";\nimport { bundleUrlToStagedPath, resolveContained } from \"./bundle-path\";\n\n/**\n * Version-manifest slot resolution (atomic-deploys 04): a config slot's\n * `pin` ({ manifest, integrity }) resolves to the derived entry-level fields\n * every consumer needs. The cache is keyed by the full pin — the pinned\n * bytes are immutable, so a verified entry is cached for the process\n * lifetime and serves as last-known-good if the origin later fails; a fetch\n * or SRI failure with nothing cached propagates (a boot-time resolution\n * failure fails the deploy, which is the platform's rollback).\n */\n\nexport interface ResolvedSlotVersion {\n /** Absolute URL of the content-hashed container entry. */\n entryUrl: string;\n entryIntegrity: string;\n /** Absolute URL of the hashed browser manifest (mf-manifest), when present. */\n browserManifestUrl?: string;\n /** Absolute URL of the content-hashed SSR entry, when present. */\n ssrEntryUrl?: string;\n ssrIntegrity?: string;\n /** Full per-file digest map of the dist (object path → SRI), when the\n * pinned manifest carries one — the diff base for skip-unchanged uploads. */\n files?: Record<string, string>;\n}\n\nexport interface SlotPin {\n /** versioned manifest filename, relative to the slot's production base */\n manifest: string;\n /** the manifest document's own SRI */\n integrity: string;\n}\n\nconst cache = new Map<string, ResolvedSlotVersion>();\n\nexport function clearSlotVersionCache(): void {\n cache.clear();\n}\n\nfunction fetchText(url: string, fetchImpl: typeof fetch): Promise<string> {\n return fetchImpl(url).then((response) => {\n if (!response.ok) throw new Error(`version manifest fetch failed: ${response.status}`);\n return response.text();\n });\n}\n\nfunction slotVersionFromManifest(\n base: string,\n manifest: WorkspaceVersionManifest,\n): ResolvedSlotVersion {\n const trimmedBase = base.replace(/\\/$/, \"\");\n return {\n entryUrl: `${trimmedBase}/${manifest.entry}`,\n entryIntegrity: manifest.entryIntegrity,\n ...(manifest.browserManifest\n ? { browserManifestUrl: `${trimmedBase}/${manifest.browserManifest.file}` }\n : {}),\n ...(manifest.ssr\n ? {\n ssrEntryUrl: `${trimmedBase}/${manifest.ssr.entry}`,\n ssrIntegrity: manifest.ssr.integrity,\n }\n : {}),\n ...(manifest.files ? { files: manifest.files } : {}),\n };\n}\n\n/**\n * Staged-slot preference (ADR 0020/0021 self-contained tier): when a runtime\n * stages its own namespace under `BOS_BUNDLE_DIR`, a slot resolves from the\n * staged dist's own version manifest instead of the config's pin — pinned\n * manifest bytes that predate the image (deploy lag after a release bump)\n * cannot exist in the staged namespace, so their hashed files would 404 the\n * disk and fall through to the network, where the stale immutable bytes\n * still answer 200 and boot a foreign build. The staged manifest is trusted\n * (staged by the image build itself) and skipped entirely when the slot is\n * absent from the staged namespace or the pinned manifest is present (the\n * normal healthy case, which keeps the pin's SRI check).\n */\nfunction readStagedSlotVersion(base: string, pin: SlotPin): ResolvedSlotVersion | null {\n const bundleDir = process.env.BOS_BUNDLE_DIR;\n if (!bundleDir) return null;\n\n const slotDir = bundleUrlToStagedPath(base, bundleDir);\n if (!slotDir || !existsSync(slotDir)) return null;\n\n const pinnedManifestPath = resolveContained(slotDir, pin.manifest);\n if (!pinnedManifestPath || existsSync(pinnedManifestPath)) return null;\n\n const versionsDir = path.join(slotDir, \"versions\");\n if (!existsSync(versionsDir)) return null;\n const manifests = readdirSync(versionsDir)\n .filter((name) => name.endsWith(\".json\"))\n .map((name) => path.join(versionsDir, name))\n .filter((file) => statSync(file).isFile())\n .sort((a, b) => statSync(b).mtimeMs - statSync(a).mtimeMs || (a < b ? -1 : 1));\n if (manifests.length === 0) return null;\n const manifestPath = manifests[0];\n if (!manifestPath) return null;\n\n try {\n const manifest = WorkspaceVersionManifestSchema.parse(\n JSON.parse(readFileSync(manifestPath, \"utf8\")),\n );\n return slotVersionFromManifest(base, manifest);\n } catch {\n return null;\n }\n}\n\nexport async function resolveSlotVersion(input: {\n base: string;\n pin: SlotPin;\n fetchImpl?: typeof fetch;\n}): Promise<ResolvedSlotVersion> {\n const key = `${input.base}::${input.pin.manifest}::${input.pin.integrity}`;\n // content-addressed: a verified pin is cached for the process lifetime and\n // never refetched — the cache IS the last-known-good\n const cached = cache.get(key);\n if (cached) return cached;\n\n const staged = readStagedSlotVersion(input.base, input.pin);\n if (staged) {\n cache.set(key, staged);\n return staged;\n }\n\n const manifestUrl = `${input.base.replace(/\\/$/, \"\")}/${input.pin.manifest.replace(/^\\//, \"\")}`;\n const body = await fetchText(manifestUrl, input.fetchImpl ?? fetch);\n const computed = `sha384-${createHash(\"sha384\").update(body).digest(\"base64\")}`;\n if (computed !== input.pin.integrity) {\n throw new Error(\n `[SRI] version manifest integrity mismatch for ${manifestUrl}\\n Expected: ${input.pin.integrity}\\n Computed: ${computed}`,\n );\n }\n const manifest: WorkspaceVersionManifest = WorkspaceVersionManifestSchema.parse(JSON.parse(body));\n\n const resolved = slotVersionFromManifest(input.base, manifest);\n cache.set(key, resolved);\n return resolved;\n}\n"],"mappings":"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"}
@@ -1,17 +1,14 @@
1
1
  const require_run = require('./utils/run.cjs');
2
2
  let node_fs = require("node:fs");
3
3
  let node_path = require("node:path");
4
+ let effect = require("effect");
4
5
  let yaml = require("yaml");
5
6
  let node_fs_promises = require("node:fs/promises");
6
7
 
7
8
  //#region src/workspace.ts
8
- var DepsBuildFailure = class extends Error {
9
- failures;
10
- constructor(failures) {
11
- const detail = failures.map((failure) => `${failure.member.name} (exit ${failure.exitCode})\n${outputTail(failure.output)}`).join("\n\n");
12
- super(`workspace dependency builds failed:\n${detail}`);
13
- this.name = "DepsBuildFailure";
14
- this.failures = [...failures];
9
+ var DepsBuildFailure = class extends effect.Data.TaggedError("DepsBuildFailure") {
10
+ get message() {
11
+ return `workspace dependency builds failed:\n${this.failures.map((failure) => `${failure.member.name} (exit ${failure.exitCode})\n${outputTail(failure.output)}`).join("\n\n")}`;
15
12
  }
16
13
  };
17
14
  const workspaceRootCache = /* @__PURE__ */ new Map();
@@ -154,28 +151,43 @@ async function ensureFreshDeps(startDir, targetDirs, opts) {
154
151
  const judgements = await Promise.all(closure.map(async (member) => force || await isStaleMember(member)));
155
152
  const rebuilt = closure.filter((_, index) => judgements[index]);
156
153
  const fresh = closure.filter((_, index) => !judgements[index]);
157
- if (rebuilt.length > 0) {
158
- const failures = (await Promise.allSettled(rebuilt.map((member) => require_run.run("pnpm", ["run", "build"], {
154
+ const failures = [];
155
+ for (const layer of buildLayers(rebuilt)) {
156
+ const results = await Promise.allSettled(layer.map((member) => require_run.run("pnpm", ["run", "build"], {
159
157
  cwd: member.dir,
160
158
  capture: true,
161
159
  env: opts?.env
162
- })))).flatMap((result, index) => {
160
+ })));
161
+ failures.push(...results.flatMap((result, index) => {
163
162
  const value = result.status === "fulfilled" ? result.value : void 0;
164
163
  if (value && value.exitCode === 0) return [];
165
164
  const output = value ? `${value.stdout}\n${value.stderr}` : result.status === "rejected" ? String(result.reason) : "";
166
165
  return [{
167
- member: rebuilt[index],
166
+ member: layer[index],
168
167
  exitCode: value ? value.exitCode ?? 1 : 1,
169
168
  output
170
169
  }];
171
- });
172
- if (failures.length > 0) throw new DepsBuildFailure(failures);
170
+ }));
171
+ if (failures.length > 0) break;
173
172
  }
173
+ if (failures.length > 0) throw new DepsBuildFailure({ failures });
174
174
  return {
175
175
  rebuilt,
176
176
  fresh
177
177
  };
178
178
  }
179
+ function buildLayers(members) {
180
+ const byName = new Map(members.map((member) => [member.name, member]));
181
+ const remaining = new Set(members.map((member) => member.name));
182
+ const layers = [];
183
+ while (remaining.size > 0) {
184
+ const layer = members.filter((member) => remaining.has(member.name) && member.localDeps.every((dep) => !byName.has(dep) || !remaining.has(dep)));
185
+ if (layer.length === 0) throw new Error(`dependency cycle in workspace closure: ${[...remaining].join(", ")}`);
186
+ for (const member of layer) remaining.delete(member.name);
187
+ layers.push(layer);
188
+ }
189
+ return layers;
190
+ }
179
191
  async function isStaleMember(member) {
180
192
  const oracle = distOracle(member.dir);
181
193
  if (!oracle) return false;
@@ -230,6 +242,7 @@ function outputTail(output) {
230
242
 
231
243
  //#endregion
232
244
  exports.DepsBuildFailure = DepsBuildFailure;
245
+ exports.buildLayers = buildLayers;
233
246
  exports.ensureFreshDeps = ensureFreshDeps;
234
247
  exports.findWorkspaceRoot = findWorkspaceRoot;
235
248
  exports.isWorkspaceDistStale = isWorkspaceDistStale;
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.cjs","names":["resolve","dirname","join","existsSync","parse","readFileSync","readdirSync","run","readdir","stat"],"sources":["../src/workspace.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { parse } from \"yaml\";\nimport { run } from \"./utils/run\";\n\nexport interface WorkspaceMember {\n readonly name: string;\n readonly dir: string;\n readonly localDeps: readonly string[];\n}\n\nexport interface WorkspaceRoot {\n readonly dir: string;\n readonly members: readonly WorkspaceMember[];\n}\n\nexport interface DepsReport {\n readonly rebuilt: readonly WorkspaceMember[];\n readonly fresh: readonly WorkspaceMember[];\n}\n\nexport interface DepsBuildFailureEntry {\n readonly member: WorkspaceMember;\n readonly exitCode: number;\n readonly output: string;\n}\n\nexport class DepsBuildFailure extends Error {\n readonly failures: readonly DepsBuildFailureEntry[];\n\n constructor(failures: readonly DepsBuildFailureEntry[]) {\n const detail = failures\n .map(\n (failure) =>\n `${failure.member.name} (exit ${failure.exitCode})\\n${outputTail(failure.output)}`,\n )\n .join(\"\\n\\n\");\n super(`workspace dependency builds failed:\\n${detail}`);\n this.name = \"DepsBuildFailure\";\n this.failures = [...failures];\n }\n}\n\nconst workspaceRootCache = new Map<string, WorkspaceRoot | null>();\n\nexport function clearWorkspaceRootCache(): void {\n workspaceRootCache.clear();\n}\n\nexport function findWorkspaceRoot(startDir: string): WorkspaceRoot | null {\n const key = resolve(startDir);\n const cached = workspaceRootCache.get(key);\n if (cached !== undefined) return cached;\n const found = discoverWorkspaceRoot(key);\n workspaceRootCache.set(key, found);\n return found;\n}\n\nfunction discoverWorkspaceRoot(dir: string): WorkspaceRoot | null {\n let current = dir;\n while (true) {\n const root = readWorkspaceRootAt(current);\n if (root) return root;\n const parent = dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\nfunction readWorkspaceRootAt(dir: string): WorkspaceRoot | null {\n // v2 (ADR 0026): workspace globs live in pnpm-workspace.yaml. Pre-v2\n // runtimes keep the package.json workspaces fallback.\n const yamlPath = join(dir, \"pnpm-workspace.yaml\");\n if (existsSync(yamlPath)) {\n const doc = parse(readFileSync(yamlPath, \"utf8\")) as { packages?: unknown } | null;\n const globs = workspaceGlobsFromYaml(doc?.packages);\n if (globs) return readWorkspaceMembers(dir, globs);\n }\n\n const manifestPath = join(dir, \"package.json\");\n if (!existsSync(manifestPath)) return null;\n const manifest = readManifest(manifestPath);\n if (!manifest) return null;\n const globs = workspaceGlobs(manifest.workspaces);\n if (!globs) return null;\n\n return readWorkspaceMembers(dir, globs);\n}\n\nfunction workspaceGlobsFromYaml(packages: unknown): string[] | null {\n if (!Array.isArray(packages)) return null;\n const globs = packages.filter((glob): glob is string => typeof glob === \"string\");\n return globs.length > 0 ? globs : null;\n}\n\nfunction readWorkspaceMembers(dir: string, globs: readonly string[]): WorkspaceRoot {\n const memberDirs = matchMemberDirs(dir, globs);\n const drafts: { name: string; dir: string; manifest: Record<string, unknown> }[] = [];\n for (const memberDir of memberDirs) {\n const memberManifest = readManifest(join(memberDir, \"package.json\"));\n if (!memberManifest || typeof memberManifest.name !== \"string\") continue;\n drafts.push({ name: memberManifest.name, dir: memberDir, manifest: memberManifest });\n }\n\n const names = new Set(drafts.map((draft) => draft.name));\n const members = drafts\n .map((draft) => ({\n name: draft.name,\n dir: draft.dir,\n localDeps: declaredDepNames(draft.manifest).filter((dep) => names.has(dep)),\n }))\n .sort((a, b) => a.name.localeCompare(b.name));\n\n return { dir, members };\n}\n\nfunction workspaceGlobs(workspaces: unknown): string[] | null {\n if (Array.isArray(workspaces)) {\n return workspaces.filter((glob): glob is string => typeof glob === \"string\");\n }\n if (workspaces && typeof workspaces === \"object\") {\n const packages = (workspaces as { packages?: unknown }).packages;\n if (Array.isArray(packages)) {\n return packages.filter((glob): glob is string => typeof glob === \"string\");\n }\n }\n return null;\n}\n\nfunction matchMemberDirs(rootDir: string, globs: readonly string[]): string[] {\n const found = new Set<string>();\n for (const glob of globs) {\n collectMemberDirs(rootDir, glob.split(\"/\"), found);\n }\n return [...found].sort();\n}\n\nfunction collectMemberDirs(dir: string, segments: readonly string[], out: Set<string>): void {\n const [head, ...rest] = segments;\n if (!head) return;\n let entries: readonly { name: string; isDirectory(): boolean }[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (head !== \"*\" && head !== entry.name) continue;\n const child = join(dir, entry.name);\n if (rest.length === 0) out.add(child);\n else collectMemberDirs(child, rest, out);\n }\n}\n\nfunction readManifest(path: string): Record<string, unknown> | null {\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nfunction declaredDepNames(manifest: Record<string, unknown>): string[] {\n const names: string[] = [];\n for (const field of [\"dependencies\", \"devDependencies\"] as const) {\n const deps = manifest[field];\n if (deps && typeof deps === \"object\") {\n names.push(...Object.keys(deps as Record<string, unknown>));\n }\n }\n return names;\n}\n\nexport function localDepsOf(\n root: WorkspaceRoot,\n targetDirs: readonly string[],\n): readonly WorkspaceMember[] {\n const byName = new Map(root.members.map((member) => [member.name, member]));\n const byDir = new Map(root.members.map((member) => [member.dir, member]));\n const ordered: WorkspaceMember[] = [];\n const seen = new Set<string>();\n\n const visit = (member: WorkspaceMember): void => {\n if (seen.has(member.name)) return;\n seen.add(member.name);\n for (const dep of member.localDeps) {\n const depMember = byName.get(dep);\n if (depMember) visit(depMember);\n }\n ordered.push(member);\n };\n\n for (const targetDir of targetDirs) {\n const target = byDir.get(resolve(targetDir));\n if (!target) continue;\n for (const dep of target.localDeps) {\n const depMember = byName.get(dep);\n if (depMember) visit(depMember);\n }\n }\n\n return ordered;\n}\n\nexport async function ensureFreshDeps(\n startDir: string,\n targetDirs: readonly string[],\n opts?: {\n readonly force?: boolean;\n /** Build-mode contract for prerequisite children (ADR 0018): deploy\n * builds run dist-first, so every build child — prerequisite or target —\n * must receive the same env; a bundler-config workspace in the closure\n * resolves differently without it. Callers that pass nothing keep the\n * ambient env (dev-time staleness rebuilds). */\n readonly env?: Record<string, string>;\n },\n): Promise<DepsReport> {\n const root = findWorkspaceRoot(startDir);\n if (!root) return { rebuilt: [], fresh: [] };\n\n const closure = localDepsOf(root, targetDirs);\n const force = opts?.force ?? false;\n const judgements = await Promise.all(\n closure.map(async (member) => force || (await isStaleMember(member))),\n );\n const rebuilt = closure.filter((_, index) => judgements[index]);\n const fresh = closure.filter((_, index) => !judgements[index]);\n\n if (rebuilt.length > 0) {\n const results = await Promise.allSettled(\n rebuilt.map((member) =>\n run(\"pnpm\", [\"run\", \"build\"], {\n cwd: member.dir,\n capture: true,\n env: opts?.env,\n }),\n ),\n );\n const failures = results.flatMap((result, index) => {\n const value = result.status === \"fulfilled\" ? result.value : undefined;\n if (value && value.exitCode === 0) return [];\n const output = value\n ? `${value.stdout}\\n${value.stderr}`\n : result.status === \"rejected\"\n ? String(result.reason)\n : \"\";\n return [\n {\n member: rebuilt[index],\n exitCode: value ? (value.exitCode ?? 1) : 1,\n output,\n },\n ];\n });\n if (failures.length > 0) throw new DepsBuildFailure(failures);\n }\n\n return { rebuilt, fresh };\n}\n\nasync function isStaleMember(member: WorkspaceMember): Promise<boolean> {\n const oracle = distOracle(member.dir);\n if (!oracle) return false;\n return isWorkspaceDistStale(member.dir, oracle);\n}\n\nfunction distOracle(memberDir: string): string | null {\n const manifest = readManifest(join(memberDir, \"package.json\"));\n if (!manifest) return null;\n\n const entry =\n manifest.exports && typeof manifest.exports === \"object\"\n ? (manifest.exports as Record<string, unknown>)[\".\"]\n : undefined;\n if (entry && typeof entry === \"object\") {\n for (const key of [\"import\", \"default\", \"require\", \"types\"] as const) {\n const value = (entry as Record<string, unknown>)[key];\n if (typeof value === \"string\" && !value.startsWith(\"./src\")) return value;\n }\n }\n if (typeof manifest.module === \"string\" && !manifest.module.startsWith(\"./src\")) {\n return manifest.module;\n }\n if (typeof manifest.main === \"string\" && !manifest.main.startsWith(\"./src\")) {\n return manifest.main;\n }\n\n const reportPath = join(memberDir, \"dist\", \"build-report.json\");\n if (existsSync(reportPath)) {\n const report = readManifest(reportPath);\n if (report && typeof report.entry === \"string\" && report.entry) {\n return join(\"dist\", report.entry);\n }\n }\n return null;\n}\n\nasync function newestSourceMtimeMs(dir: string): Promise<number> {\n let newest = 0;\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.name === \"node_modules\" || entry.name === \"dist\") continue;\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n newest = Math.max(newest, await newestSourceMtimeMs(fullPath));\n } else {\n newest = Math.max(newest, (await stat(fullPath)).mtimeMs);\n }\n }\n return newest;\n}\n\nexport async function isWorkspaceDistStale(\n packageDir: string,\n distEntry: string,\n): Promise<boolean> {\n const distPath = join(packageDir, distEntry);\n if (!existsSync(distPath)) return true;\n const [distMtime, srcMtime, pkgMtime] = await Promise.all([\n stat(distPath).then((s) => s.mtimeMs),\n newestSourceMtimeMs(join(packageDir, \"src\")),\n stat(join(packageDir, \"package.json\")).then((s) => s.mtimeMs),\n ]);\n return Math.max(srcMtime, pkgMtime) > distMtime;\n}\n\nfunction outputTail(output: string): string {\n const lines = output.trim().split(\"\\n\");\n return lines.slice(-10).join(\"\\n\");\n}\n"],"mappings":";;;;;;;AA4BA,IAAa,mBAAb,cAAsC,MAAM;CAC1C,AAAS;CAET,YAAY,UAA4C;EACtD,MAAM,SAAS,SACZ,KACE,YACC,GAAG,QAAQ,OAAO,KAAK,SAAS,QAAQ,SAAS,KAAK,WAAW,QAAQ,MAAM,GACnF,CAAC,CACA,KAAK,MAAM;EACd,MAAM,wCAAwC,QAAQ;EACtD,KAAK,OAAO;EACZ,KAAK,WAAW,CAAC,GAAG,QAAQ;CAC9B;AACF;AAEA,MAAM,qCAAqB,IAAI,IAAkC;AAMjE,SAAgB,kBAAkB,UAAwC;CACxE,MAAM,UAAMA,mBAAQ,QAAQ;CAC5B,MAAM,SAAS,mBAAmB,IAAI,GAAG;CACzC,IAAI,WAAW,QAAW,OAAO;CACjC,MAAM,QAAQ,sBAAsB,GAAG;CACvC,mBAAmB,IAAI,KAAK,KAAK;CACjC,OAAO;AACT;AAEA,SAAS,sBAAsB,KAAmC;CAChE,IAAI,UAAU;CACd,OAAO,MAAM;EACX,MAAM,OAAO,oBAAoB,OAAO;EACxC,IAAI,MAAM,OAAO;EACjB,MAAM,aAASC,mBAAQ,OAAO;EAC9B,IAAI,WAAW,SAAS,OAAO;EAC/B,UAAU;CACZ;AACF;AAEA,SAAS,oBAAoB,KAAmC;CAG9D,MAAM,eAAWC,gBAAK,KAAK,qBAAqB;CAChD,QAAIC,oBAAW,QAAQ,GAAG;EAExB,MAAM,QAAQ,2BADFC,gBAAMC,sBAAa,UAAU,MAAM,CACR,CAAC,EAAE,QAAQ;EAClD,IAAI,OAAO,OAAO,qBAAqB,KAAK,KAAK;CACnD;CAEA,MAAM,mBAAeH,gBAAK,KAAK,cAAc;CAC7C,IAAI,KAACC,oBAAW,YAAY,GAAG,OAAO;CACtC,MAAM,WAAW,aAAa,YAAY;CAC1C,IAAI,CAAC,UAAU,OAAO;CACtB,MAAM,QAAQ,eAAe,SAAS,UAAU;CAChD,IAAI,CAAC,OAAO,OAAO;CAEnB,OAAO,qBAAqB,KAAK,KAAK;AACxC;AAEA,SAAS,uBAAuB,UAAoC;CAClE,IAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG,OAAO;CACrC,MAAM,QAAQ,SAAS,QAAQ,SAAyB,OAAO,SAAS,QAAQ;CAChF,OAAO,MAAM,SAAS,IAAI,QAAQ;AACpC;AAEA,SAAS,qBAAqB,KAAa,OAAyC;CAClF,MAAM,aAAa,gBAAgB,KAAK,KAAK;CAC7C,MAAM,SAA6E,CAAC;CACpF,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,iBAAiB,iBAAaD,gBAAK,WAAW,cAAc,CAAC;EACnE,IAAI,CAAC,kBAAkB,OAAO,eAAe,SAAS,UAAU;EAChE,OAAO,KAAK;GAAE,MAAM,eAAe;GAAM,KAAK;GAAW,UAAU;EAAe,CAAC;CACrF;CAEA,MAAM,QAAQ,IAAI,IAAI,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC;CASvD,OAAO;EAAE;EAAK,SARE,OACb,KAAK,WAAW;GACf,MAAM,MAAM;GACZ,KAAK,MAAM;GACX,WAAW,iBAAiB,MAAM,QAAQ,CAAC,CAAC,QAAQ,QAAQ,MAAM,IAAI,GAAG,CAAC;EAC5E,EAAE,CAAC,CACF,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAEzB;CAAE;AACxB;AAEA,SAAS,eAAe,YAAsC;CAC5D,IAAI,MAAM,QAAQ,UAAU,GAC1B,OAAO,WAAW,QAAQ,SAAyB,OAAO,SAAS,QAAQ;CAE7E,IAAI,cAAc,OAAO,eAAe,UAAU;EAChD,MAAM,WAAY,WAAsC;EACxD,IAAI,MAAM,QAAQ,QAAQ,GACxB,OAAO,SAAS,QAAQ,SAAyB,OAAO,SAAS,QAAQ;CAE7E;CACA,OAAO;AACT;AAEA,SAAS,gBAAgB,SAAiB,OAAoC;CAC5E,MAAM,wBAAQ,IAAI,IAAY;CAC9B,KAAK,MAAM,QAAQ,OACjB,kBAAkB,SAAS,KAAK,MAAM,GAAG,GAAG,KAAK;CAEnD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK;AACzB;AAEA,SAAS,kBAAkB,KAAa,UAA6B,KAAwB;CAC3F,MAAM,CAAC,MAAM,GAAG,QAAQ;CACxB,IAAI,CAAC,MAAM;CACX,IAAI;CACJ,IAAI;EACF,cAAUI,qBAAY,KAAK,EAAE,eAAe,KAAK,CAAC;CACpD,QAAQ;EACN;CACF;CACA,KAAK,MAAM,SAAS,SAAS;EAC3B,IAAI,CAAC,MAAM,YAAY,GAAG;EAC1B,IAAI,SAAS,OAAO,SAAS,MAAM,MAAM;EACzC,MAAM,YAAQJ,gBAAK,KAAK,MAAM,IAAI;EAClC,IAAI,KAAK,WAAW,GAAG,IAAI,IAAI,KAAK;OAC/B,kBAAkB,OAAO,MAAM,GAAG;CACzC;AACF;AAEA,SAAS,aAAa,MAA8C;CAClE,IAAI;EACF,OAAO,KAAK,UAAMG,sBAAa,MAAM,MAAM,CAAC;CAC9C,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,iBAAiB,UAA6C;CACrE,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,SAAS,CAAC,gBAAgB,iBAAiB,GAAY;EAChE,MAAM,OAAO,SAAS;EACtB,IAAI,QAAQ,OAAO,SAAS,UAC1B,MAAM,KAAK,GAAG,OAAO,KAAK,IAA+B,CAAC;CAE9D;CACA,OAAO;AACT;AAEA,SAAgB,YACd,MACA,YAC4B;CAC5B,MAAM,SAAS,IAAI,IAAI,KAAK,QAAQ,KAAK,WAAW,CAAC,OAAO,MAAM,MAAM,CAAC,CAAC;CAC1E,MAAM,QAAQ,IAAI,IAAI,KAAK,QAAQ,KAAK,WAAW,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC;CACxE,MAAM,UAA6B,CAAC;CACpC,MAAM,uBAAO,IAAI,IAAY;CAE7B,MAAM,SAAS,WAAkC;EAC/C,IAAI,KAAK,IAAI,OAAO,IAAI,GAAG;EAC3B,KAAK,IAAI,OAAO,IAAI;EACpB,KAAK,MAAM,OAAO,OAAO,WAAW;GAClC,MAAM,YAAY,OAAO,IAAI,GAAG;GAChC,IAAI,WAAW,MAAM,SAAS;EAChC;EACA,QAAQ,KAAK,MAAM;CACrB;CAEA,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,SAAS,MAAM,QAAIL,mBAAQ,SAAS,CAAC;EAC3C,IAAI,CAAC,QAAQ;EACb,KAAK,MAAM,OAAO,OAAO,WAAW;GAClC,MAAM,YAAY,OAAO,IAAI,GAAG;GAChC,IAAI,WAAW,MAAM,SAAS;EAChC;CACF;CAEA,OAAO;AACT;AAEA,eAAsB,gBACpB,UACA,YACA,MASqB;CACrB,MAAM,OAAO,kBAAkB,QAAQ;CACvC,IAAI,CAAC,MAAM,OAAO;EAAE,SAAS,CAAC;EAAG,OAAO,CAAC;CAAE;CAE3C,MAAM,UAAU,YAAY,MAAM,UAAU;CAC5C,MAAM,QAAQ,MAAM,SAAS;CAC7B,MAAM,aAAa,MAAM,QAAQ,IAC/B,QAAQ,IAAI,OAAO,WAAW,SAAU,MAAM,cAAc,MAAM,CAAE,CACtE;CACA,MAAM,UAAU,QAAQ,QAAQ,GAAG,UAAU,WAAW,MAAM;CAC9D,MAAM,QAAQ,QAAQ,QAAQ,GAAG,UAAU,CAAC,WAAW,MAAM;CAE7D,IAAI,QAAQ,SAAS,GAAG;EAUtB,MAAM,YAAW,MATK,QAAQ,WAC5B,QAAQ,KAAK,WACXO,gBAAI,QAAQ,CAAC,OAAO,OAAO,GAAG;GAC5B,KAAK,OAAO;GACZ,SAAS;GACT,KAAK,MAAM;EACb,CAAC,CACH,CACF,EACwB,CAAC,SAAS,QAAQ,UAAU;GAClD,MAAM,QAAQ,OAAO,WAAW,cAAc,OAAO,QAAQ;GAC7D,IAAI,SAAS,MAAM,aAAa,GAAG,OAAO,CAAC;GAC3C,MAAM,SAAS,QACX,GAAG,MAAM,OAAO,IAAI,MAAM,WAC1B,OAAO,WAAW,aAChB,OAAO,OAAO,MAAM,IACpB;GACN,OAAO,CACL;IACE,QAAQ,QAAQ;IAChB,UAAU,QAAS,MAAM,YAAY,IAAK;IAC1C;GACF,CACF;EACF,CAAC;EACD,IAAI,SAAS,SAAS,GAAG,MAAM,IAAI,iBAAiB,QAAQ;CAC9D;CAEA,OAAO;EAAE;EAAS;CAAM;AAC1B;AAEA,eAAe,cAAc,QAA2C;CACtE,MAAM,SAAS,WAAW,OAAO,GAAG;CACpC,IAAI,CAAC,QAAQ,OAAO;CACpB,OAAO,qBAAqB,OAAO,KAAK,MAAM;AAChD;AAEA,SAAS,WAAW,WAAkC;CACpD,MAAM,WAAW,iBAAaL,gBAAK,WAAW,cAAc,CAAC;CAC7D,IAAI,CAAC,UAAU,OAAO;CAEtB,MAAM,QACJ,SAAS,WAAW,OAAO,SAAS,YAAY,WAC3C,SAAS,QAAoC,OAC9C;CACN,IAAI,SAAS,OAAO,UAAU,UAC5B,KAAK,MAAM,OAAO;EAAC;EAAU;EAAW;EAAW;CAAO,GAAY;EACpE,MAAM,QAAS,MAAkC;EACjD,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,WAAW,OAAO,GAAG,OAAO;CACtE;CAEF,IAAI,OAAO,SAAS,WAAW,YAAY,CAAC,SAAS,OAAO,WAAW,OAAO,GAC5E,OAAO,SAAS;CAElB,IAAI,OAAO,SAAS,SAAS,YAAY,CAAC,SAAS,KAAK,WAAW,OAAO,GACxE,OAAO,SAAS;CAGlB,MAAM,iBAAaA,gBAAK,WAAW,QAAQ,mBAAmB;CAC9D,QAAIC,oBAAW,UAAU,GAAG;EAC1B,MAAM,SAAS,aAAa,UAAU;EACtC,IAAI,UAAU,OAAO,OAAO,UAAU,YAAY,OAAO,OACvD,WAAOD,gBAAK,QAAQ,OAAO,KAAK;CAEpC;CACA,OAAO;AACT;AAEA,eAAe,oBAAoB,KAA8B;CAC/D,IAAI,SAAS;CACb,MAAM,UAAU,UAAMM,0BAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;CAC1D,KAAK,MAAM,SAAS,SAAS;EAC3B,IAAI,MAAM,SAAS,kBAAkB,MAAM,SAAS,QAAQ;EAC5D,MAAM,eAAWN,gBAAK,KAAK,MAAM,IAAI;EACrC,IAAI,MAAM,YAAY,GACpB,SAAS,KAAK,IAAI,QAAQ,MAAM,oBAAoB,QAAQ,CAAC;OAE7D,SAAS,KAAK,IAAI,SAAS,UAAMO,uBAAK,QAAQ,EAAC,CAAE,OAAO;CAE5D;CACA,OAAO;AACT;AAEA,eAAsB,qBACpB,YACA,WACkB;CAClB,MAAM,eAAWP,gBAAK,YAAY,SAAS;CAC3C,IAAI,KAACC,oBAAW,QAAQ,GAAG,OAAO;CAClC,MAAM,CAAC,WAAW,UAAU,YAAY,MAAM,QAAQ,IAAI;MACxDM,uBAAK,QAAQ,CAAC,CAAC,MAAM,MAAM,EAAE,OAAO;EACpC,wBAAoBP,gBAAK,YAAY,KAAK,CAAC;MAC3CO,2BAAKP,gBAAK,YAAY,cAAc,CAAC,CAAC,CAAC,MAAM,MAAM,EAAE,OAAO;CAC9D,CAAC;CACD,OAAO,KAAK,IAAI,UAAU,QAAQ,IAAI;AACxC;AAEA,SAAS,WAAW,QAAwB;CAE1C,OADc,OAAO,KAAK,CAAC,CAAC,MAAM,IACvB,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI;AACnC"}
1
+ {"version":3,"file":"workspace.cjs","names":["Data","resolve","dirname","join","existsSync","parse","readFileSync","readdirSync","run","readdir","stat"],"sources":["../src/workspace.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { Data } from \"effect\";\nimport { parse } from \"yaml\";\nimport { run } from \"./utils/run\";\n\nexport interface WorkspaceMember {\n readonly name: string;\n readonly dir: string;\n readonly localDeps: readonly string[];\n}\n\nexport interface WorkspaceRoot {\n readonly dir: string;\n readonly members: readonly WorkspaceMember[];\n}\n\nexport interface DepsReport {\n readonly rebuilt: readonly WorkspaceMember[];\n readonly fresh: readonly WorkspaceMember[];\n}\n\nexport interface DepsBuildFailureEntry {\n readonly member: WorkspaceMember;\n readonly exitCode: number;\n readonly output: string;\n}\n\nexport class DepsBuildFailure extends Data.TaggedError(\"DepsBuildFailure\")<{\n readonly failures: readonly DepsBuildFailureEntry[];\n}> {\n override get message() {\n const detail = this.failures\n .map(\n (failure) =>\n `${failure.member.name} (exit ${failure.exitCode})\\n${outputTail(failure.output)}`,\n )\n .join(\"\\n\\n\");\n return `workspace dependency builds failed:\\n${detail}`;\n }\n}\n\nconst workspaceRootCache = new Map<string, WorkspaceRoot | null>();\n\nexport function clearWorkspaceRootCache(): void {\n workspaceRootCache.clear();\n}\n\nexport function findWorkspaceRoot(startDir: string): WorkspaceRoot | null {\n const key = resolve(startDir);\n const cached = workspaceRootCache.get(key);\n if (cached !== undefined) return cached;\n const found = discoverWorkspaceRoot(key);\n workspaceRootCache.set(key, found);\n return found;\n}\n\nfunction discoverWorkspaceRoot(dir: string): WorkspaceRoot | null {\n let current = dir;\n while (true) {\n const root = readWorkspaceRootAt(current);\n if (root) return root;\n const parent = dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\nfunction readWorkspaceRootAt(dir: string): WorkspaceRoot | null {\n // v2 (ADR 0026): workspace globs live in pnpm-workspace.yaml. Pre-v2\n // runtimes keep the package.json workspaces fallback.\n const yamlPath = join(dir, \"pnpm-workspace.yaml\");\n if (existsSync(yamlPath)) {\n const doc = parse(readFileSync(yamlPath, \"utf8\")) as { packages?: unknown } | null;\n const globs = workspaceGlobsFromYaml(doc?.packages);\n if (globs) return readWorkspaceMembers(dir, globs);\n }\n\n const manifestPath = join(dir, \"package.json\");\n if (!existsSync(manifestPath)) return null;\n const manifest = readManifest(manifestPath);\n if (!manifest) return null;\n const globs = workspaceGlobs(manifest.workspaces);\n if (!globs) return null;\n\n return readWorkspaceMembers(dir, globs);\n}\n\nfunction workspaceGlobsFromYaml(packages: unknown): string[] | null {\n if (!Array.isArray(packages)) return null;\n const globs = packages.filter((glob): glob is string => typeof glob === \"string\");\n return globs.length > 0 ? globs : null;\n}\n\nfunction readWorkspaceMembers(dir: string, globs: readonly string[]): WorkspaceRoot {\n const memberDirs = matchMemberDirs(dir, globs);\n const drafts: { name: string; dir: string; manifest: Record<string, unknown> }[] = [];\n for (const memberDir of memberDirs) {\n const memberManifest = readManifest(join(memberDir, \"package.json\"));\n if (!memberManifest || typeof memberManifest.name !== \"string\") continue;\n drafts.push({ name: memberManifest.name, dir: memberDir, manifest: memberManifest });\n }\n\n const names = new Set(drafts.map((draft) => draft.name));\n const members = drafts\n .map((draft) => ({\n name: draft.name,\n dir: draft.dir,\n localDeps: declaredDepNames(draft.manifest).filter((dep) => names.has(dep)),\n }))\n .sort((a, b) => a.name.localeCompare(b.name));\n\n return { dir, members };\n}\n\nfunction workspaceGlobs(workspaces: unknown): string[] | null {\n if (Array.isArray(workspaces)) {\n return workspaces.filter((glob): glob is string => typeof glob === \"string\");\n }\n if (workspaces && typeof workspaces === \"object\") {\n const packages = (workspaces as { packages?: unknown }).packages;\n if (Array.isArray(packages)) {\n return packages.filter((glob): glob is string => typeof glob === \"string\");\n }\n }\n return null;\n}\n\nfunction matchMemberDirs(rootDir: string, globs: readonly string[]): string[] {\n const found = new Set<string>();\n for (const glob of globs) {\n collectMemberDirs(rootDir, glob.split(\"/\"), found);\n }\n return [...found].sort();\n}\n\nfunction collectMemberDirs(dir: string, segments: readonly string[], out: Set<string>): void {\n const [head, ...rest] = segments;\n if (!head) return;\n let entries: readonly { name: string; isDirectory(): boolean }[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (head !== \"*\" && head !== entry.name) continue;\n const child = join(dir, entry.name);\n if (rest.length === 0) out.add(child);\n else collectMemberDirs(child, rest, out);\n }\n}\n\nfunction readManifest(path: string): Record<string, unknown> | null {\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nfunction declaredDepNames(manifest: Record<string, unknown>): string[] {\n const names: string[] = [];\n for (const field of [\"dependencies\", \"devDependencies\"] as const) {\n const deps = manifest[field];\n if (deps && typeof deps === \"object\") {\n names.push(...Object.keys(deps as Record<string, unknown>));\n }\n }\n return names;\n}\n\nexport function localDepsOf(\n root: WorkspaceRoot,\n targetDirs: readonly string[],\n): readonly WorkspaceMember[] {\n const byName = new Map(root.members.map((member) => [member.name, member]));\n const byDir = new Map(root.members.map((member) => [member.dir, member]));\n const ordered: WorkspaceMember[] = [];\n const seen = new Set<string>();\n\n const visit = (member: WorkspaceMember): void => {\n if (seen.has(member.name)) return;\n seen.add(member.name);\n for (const dep of member.localDeps) {\n const depMember = byName.get(dep);\n if (depMember) visit(depMember);\n }\n ordered.push(member);\n };\n\n for (const targetDir of targetDirs) {\n const target = byDir.get(resolve(targetDir));\n if (!target) continue;\n for (const dep of target.localDeps) {\n const depMember = byName.get(dep);\n if (depMember) visit(depMember);\n }\n }\n\n return ordered;\n}\n\nexport async function ensureFreshDeps(\n startDir: string,\n targetDirs: readonly string[],\n opts?: {\n readonly force?: boolean;\n /** Build-mode contract for prerequisite children (ADR 0018): deploy\n * builds run dist-first, so every build child — prerequisite or target —\n * must receive the same env; a bundler-config workspace in the closure\n * resolves differently without it. Callers that pass nothing keep the\n * ambient env (dev-time staleness rebuilds). */\n readonly env?: Record<string, string>;\n },\n): Promise<DepsReport> {\n const root = findWorkspaceRoot(startDir);\n if (!root) return { rebuilt: [], fresh: [] };\n\n const closure = localDepsOf(root, targetDirs);\n const force = opts?.force ?? false;\n const judgements = await Promise.all(\n closure.map(async (member) => force || (await isStaleMember(member))),\n );\n const rebuilt = closure.filter((_, index) => judgements[index]);\n const fresh = closure.filter((_, index) => !judgements[index]);\n\n const failures: DepsBuildFailureEntry[] = [];\n // Layered order: a member's dist must exist before its dependents build —\n // dts emit resolves types through dependency dists, so building the whole\n // stale closure in parallel races (everything-dev dts collapsing to `any`\n // when its deps were still writing dists was shipped this way in rc.1).\n for (const layer of buildLayers(rebuilt)) {\n const results = await Promise.allSettled(\n layer.map((member) =>\n run(\"pnpm\", [\"run\", \"build\"], {\n cwd: member.dir,\n capture: true,\n env: opts?.env,\n }),\n ),\n );\n failures.push(\n ...results.flatMap((result, index) => {\n const value = result.status === \"fulfilled\" ? result.value : undefined;\n if (value && value.exitCode === 0) return [];\n const output = value\n ? `${value.stdout}\\n${value.stderr}`\n : result.status === \"rejected\"\n ? String(result.reason)\n : \"\";\n return [\n {\n member: layer[index],\n exitCode: value ? (value.exitCode ?? 1) : 1,\n output,\n },\n ];\n }),\n );\n if (failures.length > 0) break;\n }\n if (failures.length > 0) throw new DepsBuildFailure({ failures });\n\n return { rebuilt, fresh };\n}\n\nexport function buildLayers(members: readonly WorkspaceMember[]): readonly WorkspaceMember[][] {\n const byName = new Map(members.map((member) => [member.name, member]));\n const remaining = new Set(members.map((member) => member.name));\n const layers: WorkspaceMember[][] = [];\n while (remaining.size > 0) {\n const layer = members.filter(\n (member) =>\n remaining.has(member.name) &&\n member.localDeps.every((dep) => !byName.has(dep) || !remaining.has(dep)),\n );\n if (layer.length === 0) {\n throw new Error(`dependency cycle in workspace closure: ${[...remaining].join(\", \")}`);\n }\n for (const member of layer) remaining.delete(member.name);\n layers.push(layer);\n }\n return layers;\n}\n\nasync function isStaleMember(member: WorkspaceMember): Promise<boolean> {\n const oracle = distOracle(member.dir);\n if (!oracle) return false;\n return isWorkspaceDistStale(member.dir, oracle);\n}\n\nfunction distOracle(memberDir: string): string | null {\n const manifest = readManifest(join(memberDir, \"package.json\"));\n if (!manifest) return null;\n\n const entry =\n manifest.exports && typeof manifest.exports === \"object\"\n ? (manifest.exports as Record<string, unknown>)[\".\"]\n : undefined;\n if (entry && typeof entry === \"object\") {\n for (const key of [\"import\", \"default\", \"require\", \"types\"] as const) {\n const value = (entry as Record<string, unknown>)[key];\n if (typeof value === \"string\" && !value.startsWith(\"./src\")) return value;\n }\n }\n if (typeof manifest.module === \"string\" && !manifest.module.startsWith(\"./src\")) {\n return manifest.module;\n }\n if (typeof manifest.main === \"string\" && !manifest.main.startsWith(\"./src\")) {\n return manifest.main;\n }\n\n const reportPath = join(memberDir, \"dist\", \"build-report.json\");\n if (existsSync(reportPath)) {\n const report = readManifest(reportPath);\n if (report && typeof report.entry === \"string\" && report.entry) {\n return join(\"dist\", report.entry);\n }\n }\n return null;\n}\n\nasync function newestSourceMtimeMs(dir: string): Promise<number> {\n let newest = 0;\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.name === \"node_modules\" || entry.name === \"dist\") continue;\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n newest = Math.max(newest, await newestSourceMtimeMs(fullPath));\n } else {\n newest = Math.max(newest, (await stat(fullPath)).mtimeMs);\n }\n }\n return newest;\n}\n\nexport async function isWorkspaceDistStale(\n packageDir: string,\n distEntry: string,\n): Promise<boolean> {\n const distPath = join(packageDir, distEntry);\n if (!existsSync(distPath)) return true;\n const [distMtime, srcMtime, pkgMtime] = await Promise.all([\n stat(distPath).then((s) => s.mtimeMs),\n newestSourceMtimeMs(join(packageDir, \"src\")),\n stat(join(packageDir, \"package.json\")).then((s) => s.mtimeMs),\n ]);\n return Math.max(srcMtime, pkgMtime) > distMtime;\n}\n\nfunction outputTail(output: string): string {\n const lines = output.trim().split(\"\\n\");\n return lines.slice(-10).join(\"\\n\");\n}\n"],"mappings":"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"}
@@ -1,17 +1,14 @@
1
1
  import { run } from "./utils/run.mjs";
2
2
  import { existsSync, readFileSync, readdirSync } from "node:fs";
3
3
  import { dirname, join, resolve } from "node:path";
4
+ import { Data } from "effect";
4
5
  import { parse } from "yaml";
5
6
  import { readdir, stat } from "node:fs/promises";
6
7
 
7
8
  //#region src/workspace.ts
8
- var DepsBuildFailure = class extends Error {
9
- failures;
10
- constructor(failures) {
11
- const detail = failures.map((failure) => `${failure.member.name} (exit ${failure.exitCode})\n${outputTail(failure.output)}`).join("\n\n");
12
- super(`workspace dependency builds failed:\n${detail}`);
13
- this.name = "DepsBuildFailure";
14
- this.failures = [...failures];
9
+ var DepsBuildFailure = class extends Data.TaggedError("DepsBuildFailure") {
10
+ get message() {
11
+ return `workspace dependency builds failed:\n${this.failures.map((failure) => `${failure.member.name} (exit ${failure.exitCode})\n${outputTail(failure.output)}`).join("\n\n")}`;
15
12
  }
16
13
  };
17
14
  const workspaceRootCache = /* @__PURE__ */ new Map();
@@ -154,28 +151,43 @@ async function ensureFreshDeps(startDir, targetDirs, opts) {
154
151
  const judgements = await Promise.all(closure.map(async (member) => force || await isStaleMember(member)));
155
152
  const rebuilt = closure.filter((_, index) => judgements[index]);
156
153
  const fresh = closure.filter((_, index) => !judgements[index]);
157
- if (rebuilt.length > 0) {
158
- const failures = (await Promise.allSettled(rebuilt.map((member) => run("pnpm", ["run", "build"], {
154
+ const failures = [];
155
+ for (const layer of buildLayers(rebuilt)) {
156
+ const results = await Promise.allSettled(layer.map((member) => run("pnpm", ["run", "build"], {
159
157
  cwd: member.dir,
160
158
  capture: true,
161
159
  env: opts?.env
162
- })))).flatMap((result, index) => {
160
+ })));
161
+ failures.push(...results.flatMap((result, index) => {
163
162
  const value = result.status === "fulfilled" ? result.value : void 0;
164
163
  if (value && value.exitCode === 0) return [];
165
164
  const output = value ? `${value.stdout}\n${value.stderr}` : result.status === "rejected" ? String(result.reason) : "";
166
165
  return [{
167
- member: rebuilt[index],
166
+ member: layer[index],
168
167
  exitCode: value ? value.exitCode ?? 1 : 1,
169
168
  output
170
169
  }];
171
- });
172
- if (failures.length > 0) throw new DepsBuildFailure(failures);
170
+ }));
171
+ if (failures.length > 0) break;
173
172
  }
173
+ if (failures.length > 0) throw new DepsBuildFailure({ failures });
174
174
  return {
175
175
  rebuilt,
176
176
  fresh
177
177
  };
178
178
  }
179
+ function buildLayers(members) {
180
+ const byName = new Map(members.map((member) => [member.name, member]));
181
+ const remaining = new Set(members.map((member) => member.name));
182
+ const layers = [];
183
+ while (remaining.size > 0) {
184
+ const layer = members.filter((member) => remaining.has(member.name) && member.localDeps.every((dep) => !byName.has(dep) || !remaining.has(dep)));
185
+ if (layer.length === 0) throw new Error(`dependency cycle in workspace closure: ${[...remaining].join(", ")}`);
186
+ for (const member of layer) remaining.delete(member.name);
187
+ layers.push(layer);
188
+ }
189
+ return layers;
190
+ }
179
191
  async function isStaleMember(member) {
180
192
  const oracle = distOracle(member.dir);
181
193
  if (!oracle) return false;
@@ -229,5 +241,5 @@ function outputTail(output) {
229
241
  }
230
242
 
231
243
  //#endregion
232
- export { DepsBuildFailure, ensureFreshDeps, findWorkspaceRoot, isWorkspaceDistStale, localDepsOf };
244
+ export { DepsBuildFailure, buildLayers, ensureFreshDeps, findWorkspaceRoot, isWorkspaceDistStale, localDepsOf };
233
245
  //# sourceMappingURL=workspace.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.mjs","names":[],"sources":["../src/workspace.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { parse } from \"yaml\";\nimport { run } from \"./utils/run\";\n\nexport interface WorkspaceMember {\n readonly name: string;\n readonly dir: string;\n readonly localDeps: readonly string[];\n}\n\nexport interface WorkspaceRoot {\n readonly dir: string;\n readonly members: readonly WorkspaceMember[];\n}\n\nexport interface DepsReport {\n readonly rebuilt: readonly WorkspaceMember[];\n readonly fresh: readonly WorkspaceMember[];\n}\n\nexport interface DepsBuildFailureEntry {\n readonly member: WorkspaceMember;\n readonly exitCode: number;\n readonly output: string;\n}\n\nexport class DepsBuildFailure extends Error {\n readonly failures: readonly DepsBuildFailureEntry[];\n\n constructor(failures: readonly DepsBuildFailureEntry[]) {\n const detail = failures\n .map(\n (failure) =>\n `${failure.member.name} (exit ${failure.exitCode})\\n${outputTail(failure.output)}`,\n )\n .join(\"\\n\\n\");\n super(`workspace dependency builds failed:\\n${detail}`);\n this.name = \"DepsBuildFailure\";\n this.failures = [...failures];\n }\n}\n\nconst workspaceRootCache = new Map<string, WorkspaceRoot | null>();\n\nexport function clearWorkspaceRootCache(): void {\n workspaceRootCache.clear();\n}\n\nexport function findWorkspaceRoot(startDir: string): WorkspaceRoot | null {\n const key = resolve(startDir);\n const cached = workspaceRootCache.get(key);\n if (cached !== undefined) return cached;\n const found = discoverWorkspaceRoot(key);\n workspaceRootCache.set(key, found);\n return found;\n}\n\nfunction discoverWorkspaceRoot(dir: string): WorkspaceRoot | null {\n let current = dir;\n while (true) {\n const root = readWorkspaceRootAt(current);\n if (root) return root;\n const parent = dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\nfunction readWorkspaceRootAt(dir: string): WorkspaceRoot | null {\n // v2 (ADR 0026): workspace globs live in pnpm-workspace.yaml. Pre-v2\n // runtimes keep the package.json workspaces fallback.\n const yamlPath = join(dir, \"pnpm-workspace.yaml\");\n if (existsSync(yamlPath)) {\n const doc = parse(readFileSync(yamlPath, \"utf8\")) as { packages?: unknown } | null;\n const globs = workspaceGlobsFromYaml(doc?.packages);\n if (globs) return readWorkspaceMembers(dir, globs);\n }\n\n const manifestPath = join(dir, \"package.json\");\n if (!existsSync(manifestPath)) return null;\n const manifest = readManifest(manifestPath);\n if (!manifest) return null;\n const globs = workspaceGlobs(manifest.workspaces);\n if (!globs) return null;\n\n return readWorkspaceMembers(dir, globs);\n}\n\nfunction workspaceGlobsFromYaml(packages: unknown): string[] | null {\n if (!Array.isArray(packages)) return null;\n const globs = packages.filter((glob): glob is string => typeof glob === \"string\");\n return globs.length > 0 ? globs : null;\n}\n\nfunction readWorkspaceMembers(dir: string, globs: readonly string[]): WorkspaceRoot {\n const memberDirs = matchMemberDirs(dir, globs);\n const drafts: { name: string; dir: string; manifest: Record<string, unknown> }[] = [];\n for (const memberDir of memberDirs) {\n const memberManifest = readManifest(join(memberDir, \"package.json\"));\n if (!memberManifest || typeof memberManifest.name !== \"string\") continue;\n drafts.push({ name: memberManifest.name, dir: memberDir, manifest: memberManifest });\n }\n\n const names = new Set(drafts.map((draft) => draft.name));\n const members = drafts\n .map((draft) => ({\n name: draft.name,\n dir: draft.dir,\n localDeps: declaredDepNames(draft.manifest).filter((dep) => names.has(dep)),\n }))\n .sort((a, b) => a.name.localeCompare(b.name));\n\n return { dir, members };\n}\n\nfunction workspaceGlobs(workspaces: unknown): string[] | null {\n if (Array.isArray(workspaces)) {\n return workspaces.filter((glob): glob is string => typeof glob === \"string\");\n }\n if (workspaces && typeof workspaces === \"object\") {\n const packages = (workspaces as { packages?: unknown }).packages;\n if (Array.isArray(packages)) {\n return packages.filter((glob): glob is string => typeof glob === \"string\");\n }\n }\n return null;\n}\n\nfunction matchMemberDirs(rootDir: string, globs: readonly string[]): string[] {\n const found = new Set<string>();\n for (const glob of globs) {\n collectMemberDirs(rootDir, glob.split(\"/\"), found);\n }\n return [...found].sort();\n}\n\nfunction collectMemberDirs(dir: string, segments: readonly string[], out: Set<string>): void {\n const [head, ...rest] = segments;\n if (!head) return;\n let entries: readonly { name: string; isDirectory(): boolean }[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (head !== \"*\" && head !== entry.name) continue;\n const child = join(dir, entry.name);\n if (rest.length === 0) out.add(child);\n else collectMemberDirs(child, rest, out);\n }\n}\n\nfunction readManifest(path: string): Record<string, unknown> | null {\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nfunction declaredDepNames(manifest: Record<string, unknown>): string[] {\n const names: string[] = [];\n for (const field of [\"dependencies\", \"devDependencies\"] as const) {\n const deps = manifest[field];\n if (deps && typeof deps === \"object\") {\n names.push(...Object.keys(deps as Record<string, unknown>));\n }\n }\n return names;\n}\n\nexport function localDepsOf(\n root: WorkspaceRoot,\n targetDirs: readonly string[],\n): readonly WorkspaceMember[] {\n const byName = new Map(root.members.map((member) => [member.name, member]));\n const byDir = new Map(root.members.map((member) => [member.dir, member]));\n const ordered: WorkspaceMember[] = [];\n const seen = new Set<string>();\n\n const visit = (member: WorkspaceMember): void => {\n if (seen.has(member.name)) return;\n seen.add(member.name);\n for (const dep of member.localDeps) {\n const depMember = byName.get(dep);\n if (depMember) visit(depMember);\n }\n ordered.push(member);\n };\n\n for (const targetDir of targetDirs) {\n const target = byDir.get(resolve(targetDir));\n if (!target) continue;\n for (const dep of target.localDeps) {\n const depMember = byName.get(dep);\n if (depMember) visit(depMember);\n }\n }\n\n return ordered;\n}\n\nexport async function ensureFreshDeps(\n startDir: string,\n targetDirs: readonly string[],\n opts?: {\n readonly force?: boolean;\n /** Build-mode contract for prerequisite children (ADR 0018): deploy\n * builds run dist-first, so every build child — prerequisite or target —\n * must receive the same env; a bundler-config workspace in the closure\n * resolves differently without it. Callers that pass nothing keep the\n * ambient env (dev-time staleness rebuilds). */\n readonly env?: Record<string, string>;\n },\n): Promise<DepsReport> {\n const root = findWorkspaceRoot(startDir);\n if (!root) return { rebuilt: [], fresh: [] };\n\n const closure = localDepsOf(root, targetDirs);\n const force = opts?.force ?? false;\n const judgements = await Promise.all(\n closure.map(async (member) => force || (await isStaleMember(member))),\n );\n const rebuilt = closure.filter((_, index) => judgements[index]);\n const fresh = closure.filter((_, index) => !judgements[index]);\n\n if (rebuilt.length > 0) {\n const results = await Promise.allSettled(\n rebuilt.map((member) =>\n run(\"pnpm\", [\"run\", \"build\"], {\n cwd: member.dir,\n capture: true,\n env: opts?.env,\n }),\n ),\n );\n const failures = results.flatMap((result, index) => {\n const value = result.status === \"fulfilled\" ? result.value : undefined;\n if (value && value.exitCode === 0) return [];\n const output = value\n ? `${value.stdout}\\n${value.stderr}`\n : result.status === \"rejected\"\n ? String(result.reason)\n : \"\";\n return [\n {\n member: rebuilt[index],\n exitCode: value ? (value.exitCode ?? 1) : 1,\n output,\n },\n ];\n });\n if (failures.length > 0) throw new DepsBuildFailure(failures);\n }\n\n return { rebuilt, fresh };\n}\n\nasync function isStaleMember(member: WorkspaceMember): Promise<boolean> {\n const oracle = distOracle(member.dir);\n if (!oracle) return false;\n return isWorkspaceDistStale(member.dir, oracle);\n}\n\nfunction distOracle(memberDir: string): string | null {\n const manifest = readManifest(join(memberDir, \"package.json\"));\n if (!manifest) return null;\n\n const entry =\n manifest.exports && typeof manifest.exports === \"object\"\n ? (manifest.exports as Record<string, unknown>)[\".\"]\n : undefined;\n if (entry && typeof entry === \"object\") {\n for (const key of [\"import\", \"default\", \"require\", \"types\"] as const) {\n const value = (entry as Record<string, unknown>)[key];\n if (typeof value === \"string\" && !value.startsWith(\"./src\")) return value;\n }\n }\n if (typeof manifest.module === \"string\" && !manifest.module.startsWith(\"./src\")) {\n return manifest.module;\n }\n if (typeof manifest.main === \"string\" && !manifest.main.startsWith(\"./src\")) {\n return manifest.main;\n }\n\n const reportPath = join(memberDir, \"dist\", \"build-report.json\");\n if (existsSync(reportPath)) {\n const report = readManifest(reportPath);\n if (report && typeof report.entry === \"string\" && report.entry) {\n return join(\"dist\", report.entry);\n }\n }\n return null;\n}\n\nasync function newestSourceMtimeMs(dir: string): Promise<number> {\n let newest = 0;\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.name === \"node_modules\" || entry.name === \"dist\") continue;\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n newest = Math.max(newest, await newestSourceMtimeMs(fullPath));\n } else {\n newest = Math.max(newest, (await stat(fullPath)).mtimeMs);\n }\n }\n return newest;\n}\n\nexport async function isWorkspaceDistStale(\n packageDir: string,\n distEntry: string,\n): Promise<boolean> {\n const distPath = join(packageDir, distEntry);\n if (!existsSync(distPath)) return true;\n const [distMtime, srcMtime, pkgMtime] = await Promise.all([\n stat(distPath).then((s) => s.mtimeMs),\n newestSourceMtimeMs(join(packageDir, \"src\")),\n stat(join(packageDir, \"package.json\")).then((s) => s.mtimeMs),\n ]);\n return Math.max(srcMtime, pkgMtime) > distMtime;\n}\n\nfunction outputTail(output: string): string {\n const lines = output.trim().split(\"\\n\");\n return lines.slice(-10).join(\"\\n\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"workspace.mjs","names":[],"sources":["../src/workspace.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { Data } from \"effect\";\nimport { parse } from \"yaml\";\nimport { run } from \"./utils/run\";\n\nexport interface WorkspaceMember {\n readonly name: string;\n readonly dir: string;\n readonly localDeps: readonly string[];\n}\n\nexport interface WorkspaceRoot {\n readonly dir: string;\n readonly members: readonly WorkspaceMember[];\n}\n\nexport interface DepsReport {\n readonly rebuilt: readonly WorkspaceMember[];\n readonly fresh: readonly WorkspaceMember[];\n}\n\nexport interface DepsBuildFailureEntry {\n readonly member: WorkspaceMember;\n readonly exitCode: number;\n readonly output: string;\n}\n\nexport class DepsBuildFailure extends Data.TaggedError(\"DepsBuildFailure\")<{\n readonly failures: readonly DepsBuildFailureEntry[];\n}> {\n override get message() {\n const detail = this.failures\n .map(\n (failure) =>\n `${failure.member.name} (exit ${failure.exitCode})\\n${outputTail(failure.output)}`,\n )\n .join(\"\\n\\n\");\n return `workspace dependency builds failed:\\n${detail}`;\n }\n}\n\nconst workspaceRootCache = new Map<string, WorkspaceRoot | null>();\n\nexport function clearWorkspaceRootCache(): void {\n workspaceRootCache.clear();\n}\n\nexport function findWorkspaceRoot(startDir: string): WorkspaceRoot | null {\n const key = resolve(startDir);\n const cached = workspaceRootCache.get(key);\n if (cached !== undefined) return cached;\n const found = discoverWorkspaceRoot(key);\n workspaceRootCache.set(key, found);\n return found;\n}\n\nfunction discoverWorkspaceRoot(dir: string): WorkspaceRoot | null {\n let current = dir;\n while (true) {\n const root = readWorkspaceRootAt(current);\n if (root) return root;\n const parent = dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\nfunction readWorkspaceRootAt(dir: string): WorkspaceRoot | null {\n // v2 (ADR 0026): workspace globs live in pnpm-workspace.yaml. Pre-v2\n // runtimes keep the package.json workspaces fallback.\n const yamlPath = join(dir, \"pnpm-workspace.yaml\");\n if (existsSync(yamlPath)) {\n const doc = parse(readFileSync(yamlPath, \"utf8\")) as { packages?: unknown } | null;\n const globs = workspaceGlobsFromYaml(doc?.packages);\n if (globs) return readWorkspaceMembers(dir, globs);\n }\n\n const manifestPath = join(dir, \"package.json\");\n if (!existsSync(manifestPath)) return null;\n const manifest = readManifest(manifestPath);\n if (!manifest) return null;\n const globs = workspaceGlobs(manifest.workspaces);\n if (!globs) return null;\n\n return readWorkspaceMembers(dir, globs);\n}\n\nfunction workspaceGlobsFromYaml(packages: unknown): string[] | null {\n if (!Array.isArray(packages)) return null;\n const globs = packages.filter((glob): glob is string => typeof glob === \"string\");\n return globs.length > 0 ? globs : null;\n}\n\nfunction readWorkspaceMembers(dir: string, globs: readonly string[]): WorkspaceRoot {\n const memberDirs = matchMemberDirs(dir, globs);\n const drafts: { name: string; dir: string; manifest: Record<string, unknown> }[] = [];\n for (const memberDir of memberDirs) {\n const memberManifest = readManifest(join(memberDir, \"package.json\"));\n if (!memberManifest || typeof memberManifest.name !== \"string\") continue;\n drafts.push({ name: memberManifest.name, dir: memberDir, manifest: memberManifest });\n }\n\n const names = new Set(drafts.map((draft) => draft.name));\n const members = drafts\n .map((draft) => ({\n name: draft.name,\n dir: draft.dir,\n localDeps: declaredDepNames(draft.manifest).filter((dep) => names.has(dep)),\n }))\n .sort((a, b) => a.name.localeCompare(b.name));\n\n return { dir, members };\n}\n\nfunction workspaceGlobs(workspaces: unknown): string[] | null {\n if (Array.isArray(workspaces)) {\n return workspaces.filter((glob): glob is string => typeof glob === \"string\");\n }\n if (workspaces && typeof workspaces === \"object\") {\n const packages = (workspaces as { packages?: unknown }).packages;\n if (Array.isArray(packages)) {\n return packages.filter((glob): glob is string => typeof glob === \"string\");\n }\n }\n return null;\n}\n\nfunction matchMemberDirs(rootDir: string, globs: readonly string[]): string[] {\n const found = new Set<string>();\n for (const glob of globs) {\n collectMemberDirs(rootDir, glob.split(\"/\"), found);\n }\n return [...found].sort();\n}\n\nfunction collectMemberDirs(dir: string, segments: readonly string[], out: Set<string>): void {\n const [head, ...rest] = segments;\n if (!head) return;\n let entries: readonly { name: string; isDirectory(): boolean }[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (head !== \"*\" && head !== entry.name) continue;\n const child = join(dir, entry.name);\n if (rest.length === 0) out.add(child);\n else collectMemberDirs(child, rest, out);\n }\n}\n\nfunction readManifest(path: string): Record<string, unknown> | null {\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nfunction declaredDepNames(manifest: Record<string, unknown>): string[] {\n const names: string[] = [];\n for (const field of [\"dependencies\", \"devDependencies\"] as const) {\n const deps = manifest[field];\n if (deps && typeof deps === \"object\") {\n names.push(...Object.keys(deps as Record<string, unknown>));\n }\n }\n return names;\n}\n\nexport function localDepsOf(\n root: WorkspaceRoot,\n targetDirs: readonly string[],\n): readonly WorkspaceMember[] {\n const byName = new Map(root.members.map((member) => [member.name, member]));\n const byDir = new Map(root.members.map((member) => [member.dir, member]));\n const ordered: WorkspaceMember[] = [];\n const seen = new Set<string>();\n\n const visit = (member: WorkspaceMember): void => {\n if (seen.has(member.name)) return;\n seen.add(member.name);\n for (const dep of member.localDeps) {\n const depMember = byName.get(dep);\n if (depMember) visit(depMember);\n }\n ordered.push(member);\n };\n\n for (const targetDir of targetDirs) {\n const target = byDir.get(resolve(targetDir));\n if (!target) continue;\n for (const dep of target.localDeps) {\n const depMember = byName.get(dep);\n if (depMember) visit(depMember);\n }\n }\n\n return ordered;\n}\n\nexport async function ensureFreshDeps(\n startDir: string,\n targetDirs: readonly string[],\n opts?: {\n readonly force?: boolean;\n /** Build-mode contract for prerequisite children (ADR 0018): deploy\n * builds run dist-first, so every build child — prerequisite or target —\n * must receive the same env; a bundler-config workspace in the closure\n * resolves differently without it. Callers that pass nothing keep the\n * ambient env (dev-time staleness rebuilds). */\n readonly env?: Record<string, string>;\n },\n): Promise<DepsReport> {\n const root = findWorkspaceRoot(startDir);\n if (!root) return { rebuilt: [], fresh: [] };\n\n const closure = localDepsOf(root, targetDirs);\n const force = opts?.force ?? false;\n const judgements = await Promise.all(\n closure.map(async (member) => force || (await isStaleMember(member))),\n );\n const rebuilt = closure.filter((_, index) => judgements[index]);\n const fresh = closure.filter((_, index) => !judgements[index]);\n\n const failures: DepsBuildFailureEntry[] = [];\n // Layered order: a member's dist must exist before its dependents build —\n // dts emit resolves types through dependency dists, so building the whole\n // stale closure in parallel races (everything-dev dts collapsing to `any`\n // when its deps were still writing dists was shipped this way in rc.1).\n for (const layer of buildLayers(rebuilt)) {\n const results = await Promise.allSettled(\n layer.map((member) =>\n run(\"pnpm\", [\"run\", \"build\"], {\n cwd: member.dir,\n capture: true,\n env: opts?.env,\n }),\n ),\n );\n failures.push(\n ...results.flatMap((result, index) => {\n const value = result.status === \"fulfilled\" ? result.value : undefined;\n if (value && value.exitCode === 0) return [];\n const output = value\n ? `${value.stdout}\\n${value.stderr}`\n : result.status === \"rejected\"\n ? String(result.reason)\n : \"\";\n return [\n {\n member: layer[index],\n exitCode: value ? (value.exitCode ?? 1) : 1,\n output,\n },\n ];\n }),\n );\n if (failures.length > 0) break;\n }\n if (failures.length > 0) throw new DepsBuildFailure({ failures });\n\n return { rebuilt, fresh };\n}\n\nexport function buildLayers(members: readonly WorkspaceMember[]): readonly WorkspaceMember[][] {\n const byName = new Map(members.map((member) => [member.name, member]));\n const remaining = new Set(members.map((member) => member.name));\n const layers: WorkspaceMember[][] = [];\n while (remaining.size > 0) {\n const layer = members.filter(\n (member) =>\n remaining.has(member.name) &&\n member.localDeps.every((dep) => !byName.has(dep) || !remaining.has(dep)),\n );\n if (layer.length === 0) {\n throw new Error(`dependency cycle in workspace closure: ${[...remaining].join(\", \")}`);\n }\n for (const member of layer) remaining.delete(member.name);\n layers.push(layer);\n }\n return layers;\n}\n\nasync function isStaleMember(member: WorkspaceMember): Promise<boolean> {\n const oracle = distOracle(member.dir);\n if (!oracle) return false;\n return isWorkspaceDistStale(member.dir, oracle);\n}\n\nfunction distOracle(memberDir: string): string | null {\n const manifest = readManifest(join(memberDir, \"package.json\"));\n if (!manifest) return null;\n\n const entry =\n manifest.exports && typeof manifest.exports === \"object\"\n ? (manifest.exports as Record<string, unknown>)[\".\"]\n : undefined;\n if (entry && typeof entry === \"object\") {\n for (const key of [\"import\", \"default\", \"require\", \"types\"] as const) {\n const value = (entry as Record<string, unknown>)[key];\n if (typeof value === \"string\" && !value.startsWith(\"./src\")) return value;\n }\n }\n if (typeof manifest.module === \"string\" && !manifest.module.startsWith(\"./src\")) {\n return manifest.module;\n }\n if (typeof manifest.main === \"string\" && !manifest.main.startsWith(\"./src\")) {\n return manifest.main;\n }\n\n const reportPath = join(memberDir, \"dist\", \"build-report.json\");\n if (existsSync(reportPath)) {\n const report = readManifest(reportPath);\n if (report && typeof report.entry === \"string\" && report.entry) {\n return join(\"dist\", report.entry);\n }\n }\n return null;\n}\n\nasync function newestSourceMtimeMs(dir: string): Promise<number> {\n let newest = 0;\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.name === \"node_modules\" || entry.name === \"dist\") continue;\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n newest = Math.max(newest, await newestSourceMtimeMs(fullPath));\n } else {\n newest = Math.max(newest, (await stat(fullPath)).mtimeMs);\n }\n }\n return newest;\n}\n\nexport async function isWorkspaceDistStale(\n packageDir: string,\n distEntry: string,\n): Promise<boolean> {\n const distPath = join(packageDir, distEntry);\n if (!existsSync(distPath)) return true;\n const [distMtime, srcMtime, pkgMtime] = await Promise.all([\n stat(distPath).then((s) => s.mtimeMs),\n newestSourceMtimeMs(join(packageDir, \"src\")),\n stat(join(packageDir, \"package.json\")).then((s) => s.mtimeMs),\n ]);\n return Math.max(srcMtime, pkgMtime) > distMtime;\n}\n\nfunction outputTail(output: string): string {\n const lines = output.trim().split(\"\\n\");\n return lines.slice(-10).join(\"\\n\");\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "everything-dev",
3
- "version": "2.0.0-rc.1",
3
+ "version": "2.0.0-rc.5",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=20.11"
@@ -45,14 +45,14 @@
45
45
  "require": "./dist/bundle-cache.cjs"
46
46
  },
47
47
  "./fingerprint": {
48
- "types": "./src/fingerprint.ts",
49
- "import": "./src/fingerprint.ts",
50
- "require": "./src/fingerprint.ts"
48
+ "types": "./dist/fingerprint.d.mts",
49
+ "import": "./dist/fingerprint.mjs",
50
+ "require": "./dist/fingerprint.cjs"
51
51
  },
52
52
  "./version-manifest-resolve": {
53
- "types": "./src/version-manifest-resolve.ts",
54
- "import": "./src/version-manifest-resolve.ts",
55
- "require": "./src/version-manifest-resolve.ts"
53
+ "types": "./dist/version-manifest-resolve.d.mts",
54
+ "import": "./dist/version-manifest-resolve.mjs",
55
+ "require": "./dist/version-manifest-resolve.cjs"
56
56
  },
57
57
  "./bundle-fs-resolve": {
58
58
  "types": "./dist/bundle-fs-resolve.d.mts",
@@ -201,8 +201,9 @@
201
201
  "require": "./dist/db/index.cjs"
202
202
  },
203
203
  "./ui/version-check": {
204
- "types": "./src/ui/version-check.ts",
205
- "import": "./src/ui/version-check.ts"
204
+ "types": "./dist/ui/version-check.d.mts",
205
+ "import": "./dist/ui/version-check.mjs",
206
+ "require": "./dist/ui/version-check.cjs"
206
207
  }
207
208
  },
208
209
  "files": [
@@ -241,7 +242,8 @@
241
242
  "defu": "^6.1.7",
242
243
  "dotenv": "^17.3.1",
243
244
  "effect": "4.0.0-rc.117",
244
- "every-plugin": "^3.0.0-rc.0",
245
+ "@everything-dev/upload-app": "workspace:^",
246
+ "every-plugin": "^3.0.0-rc.4",
245
247
  "execa": "^9.5.2",
246
248
  "glob": "^13.0.6",
247
249
  "near-kit": "^0.20.2",