handoff-app 1.2.2-7 → 2.0.0-1

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 (351) hide show
  1. package/Changelog.md +37 -15
  2. package/README.md +372 -118
  3. package/dist/app-builder/build.d.ts +41 -2
  4. package/dist/app-builder/build.js +455 -18
  5. package/dist/app-builder/client-config.d.ts +15 -2
  6. package/dist/app-builder/client-config.js +43 -4
  7. package/dist/app-builder/config-diff/snapshot.d.ts +1 -6
  8. package/dist/app-builder/config-diff/snapshot.js +2 -9
  9. package/dist/app-builder/index.d.ts +1 -1
  10. package/dist/app-builder/index.js +3 -1
  11. package/dist/app-builder/nav-shell.d.ts +23 -0
  12. package/dist/app-builder/nav-shell.js +80 -0
  13. package/dist/app-builder/paths.js +25 -1
  14. package/dist/app-builder/static-export.d.ts +17 -0
  15. package/dist/app-builder/static-export.js +206 -0
  16. package/dist/app-builder/vercel-output.d.ts +39 -0
  17. package/dist/app-builder/vercel-output.js +259 -0
  18. package/dist/app-builder/watchers/utils.d.ts +2 -0
  19. package/dist/app-builder/watchers.d.ts +14 -4
  20. package/dist/app-builder/watchers.js +123 -28
  21. package/dist/app-builder/websocket.js +1 -1
  22. package/dist/artifacts/index.d.ts +2 -0
  23. package/dist/artifacts/index.js +16 -0
  24. package/dist/artifacts/types.d.ts +136 -0
  25. package/dist/artifacts/types.js +24 -0
  26. package/dist/artifacts/url.d.ts +67 -0
  27. package/dist/artifacts/url.js +116 -0
  28. package/dist/cache/build-cache.js +48 -4
  29. package/dist/cli.js +0 -0
  30. package/dist/commands/build/app.js +1 -2
  31. package/dist/commands/build/index.d.ts +17 -0
  32. package/dist/commands/build/index.js +66 -0
  33. package/dist/commands/checkout/index.d.ts +24 -0
  34. package/dist/commands/checkout/index.js +71 -0
  35. package/dist/commands/db/migrate.d.ts +11 -0
  36. package/dist/commands/db/migrate.js +33 -0
  37. package/dist/commands/index.js +8 -0
  38. package/dist/commands/publish/index.d.ts +21 -0
  39. package/dist/commands/publish/index.js +68 -0
  40. package/dist/config/defaults.js +15 -1
  41. package/dist/config/loader.js +6 -1
  42. package/dist/config/normalizers/declaration.js +4 -1
  43. package/dist/config/normalizers/page.d.ts +18 -0
  44. package/dist/config/normalizers/page.js +34 -0
  45. package/dist/config/normalizers/pattern.js +7 -1
  46. package/dist/config/runtime.d.ts +4 -0
  47. package/dist/config/runtime.js +53 -5
  48. package/dist/index.d.ts +65 -1
  49. package/dist/index.js +123 -3
  50. package/dist/nav/index.d.ts +83 -0
  51. package/dist/nav/index.js +9 -0
  52. package/dist/nav/registry.d.ts +15 -0
  53. package/dist/nav/registry.js +42 -0
  54. package/dist/nav/resolver.d.ts +8 -0
  55. package/dist/nav/resolver.js +192 -0
  56. package/dist/nav/workspace.d.ts +18 -0
  57. package/dist/nav/workspace.js +103 -0
  58. package/dist/pipeline/styles.js +7 -35
  59. package/dist/pipeline/token-transformers.d.ts +45 -0
  60. package/dist/pipeline/token-transformers.js +71 -0
  61. package/dist/registry/asset-storage/adapters/vercel-blob.d.ts +17 -0
  62. package/dist/registry/asset-storage/adapters/vercel-blob.js +87 -0
  63. package/dist/registry/asset-storage/define.d.ts +10 -0
  64. package/dist/registry/asset-storage/define.js +13 -0
  65. package/dist/registry/asset-storage/resolve.d.ts +45 -0
  66. package/dist/registry/asset-storage/resolve.js +36 -0
  67. package/dist/registry/asset-storage/types.d.ts +51 -0
  68. package/dist/registry/asset-storage/types.js +9 -0
  69. package/dist/registry/assets/layout.d.ts +22 -0
  70. package/dist/registry/assets/layout.js +30 -0
  71. package/dist/registry/assets/sets.d.ts +18 -0
  72. package/dist/registry/assets/sets.js +44 -0
  73. package/dist/registry/assets/transfer.d.ts +45 -0
  74. package/dist/registry/assets/transfer.js +11 -0
  75. package/dist/registry/checkout/assets.d.ts +16 -0
  76. package/dist/registry/checkout/assets.js +150 -0
  77. package/dist/registry/checkout/index.d.ts +33 -0
  78. package/dist/registry/checkout/index.js +610 -0
  79. package/dist/registry/checkout/tokens.d.ts +15 -0
  80. package/dist/registry/checkout/tokens.js +171 -0
  81. package/dist/registry/client.d.ts +61 -0
  82. package/dist/registry/client.js +211 -0
  83. package/dist/registry/connection.d.ts +30 -0
  84. package/dist/registry/connection.js +36 -0
  85. package/dist/registry/db/adapter.d.ts +43 -0
  86. package/dist/registry/db/adapter.js +59 -0
  87. package/dist/registry/db/client.d.ts +35 -0
  88. package/dist/registry/db/client.js +94 -0
  89. package/dist/registry/db/index.d.ts +5 -0
  90. package/dist/registry/db/index.js +24 -0
  91. package/dist/registry/db/migrate.d.ts +23 -0
  92. package/dist/registry/db/migrate.js +64 -0
  93. package/dist/registry/db/schema.d.ts +4869 -0
  94. package/dist/registry/db/schema.js +347 -0
  95. package/dist/registry/path.d.ts +4 -0
  96. package/dist/registry/path.js +31 -0
  97. package/dist/registry/publish/artifacts.d.ts +16 -0
  98. package/dist/registry/publish/artifacts.js +132 -0
  99. package/dist/registry/publish/assets.d.ts +16 -0
  100. package/dist/registry/publish/assets.js +129 -0
  101. package/dist/registry/publish/errors.d.ts +5 -0
  102. package/dist/registry/publish/errors.js +26 -0
  103. package/dist/registry/publish/index.d.ts +27 -0
  104. package/dist/registry/publish/index.js +126 -0
  105. package/dist/registry/publish/package.d.ts +27 -0
  106. package/dist/registry/publish/package.js +257 -0
  107. package/dist/registry/publish/publish-build.d.ts +12 -0
  108. package/dist/registry/publish/publish-build.js +43 -0
  109. package/dist/registry/publish/tokens.d.ts +18 -0
  110. package/dist/registry/publish/tokens.js +112 -0
  111. package/dist/registry/tokens/sets.d.ts +59 -0
  112. package/dist/registry/tokens/sets.js +109 -0
  113. package/dist/registry/tokens/transfer.d.ts +48 -0
  114. package/dist/registry/tokens/transfer.js +10 -0
  115. package/dist/registry/transfer.d.ts +75 -0
  116. package/dist/registry/transfer.js +11 -0
  117. package/dist/store/filesystem-assets.d.ts +17 -0
  118. package/dist/store/filesystem-assets.js +147 -0
  119. package/dist/store/filesystem.d.ts +76 -0
  120. package/dist/store/filesystem.js +208 -0
  121. package/dist/store/index.d.ts +20 -0
  122. package/dist/store/index.js +38 -0
  123. package/dist/store/registry.d.ts +77 -0
  124. package/dist/store/registry.js +350 -0
  125. package/dist/store/source-files.d.ts +24 -0
  126. package/dist/store/source-files.js +147 -0
  127. package/dist/store/types.d.ts +185 -0
  128. package/dist/store/types.js +12 -0
  129. package/dist/transformers/plugins/csf-render.js +17 -8
  130. package/dist/transformers/plugins/handlebars-previews.js +10 -3
  131. package/dist/transformers/plugins/ssr-render.js +116 -49
  132. package/dist/transformers/preview/component/api.js +3 -1
  133. package/dist/transformers/preview/component/artifacts.js +8 -1
  134. package/dist/transformers/preview/component/builder.js +13 -5
  135. package/dist/transformers/preview/component/css.js +9 -1
  136. package/dist/transformers/preview/component/javascript.d.ts +9 -3
  137. package/dist/transformers/preview/component/javascript.js +45 -17
  138. package/dist/transformers/preview/component/shared-artifacts.d.ts +83 -0
  139. package/dist/transformers/preview/component/shared-artifacts.js +92 -0
  140. package/dist/transformers/preview/component.js +5 -2
  141. package/dist/transformers/preview/pattern/builder.js +28 -9
  142. package/dist/transformers/preview/pattern/html.d.ts +13 -10
  143. package/dist/transformers/preview/pattern/html.js +62 -27
  144. package/dist/transformers/preview/types.d.ts +38 -0
  145. package/dist/transformers/utils/handlebars.d.ts +3 -0
  146. package/dist/transformers/utils/handlebars.js +19 -5
  147. package/dist/types/config.d.ts +124 -31
  148. package/dist/utils/menu-shell.d.ts +60 -0
  149. package/dist/utils/menu-shell.js +221 -0
  150. package/dist/utils/stable-stringify.d.ts +2 -0
  151. package/dist/utils/stable-stringify.js +8 -0
  152. package/docs/cli.md +4 -2
  153. package/drizzle/0000_init_registry_storage.sql +95 -0
  154. package/drizzle/0001_add_review_metadata.sql +2 -0
  155. package/drizzle/0002_add_pages.sql +28 -0
  156. package/drizzle/0003_add_tokens.sql +29 -0
  157. package/drizzle/0004_add_assets.sql +37 -0
  158. package/drizzle/meta/0000_snapshot.json +642 -0
  159. package/drizzle/meta/0001_snapshot.json +654 -0
  160. package/drizzle/meta/0002_snapshot.json +835 -0
  161. package/drizzle/meta/0003_snapshot.json +1023 -0
  162. package/drizzle/meta/0004_snapshot.json +1252 -0
  163. package/drizzle/meta/_journal.json +41 -0
  164. package/drizzle.config.ts +16 -0
  165. package/package.json +10 -1
  166. package/src/app/components/Component/Preview.tsx +3 -2
  167. package/src/app/components/Component/RegistryActions.tsx +150 -0
  168. package/src/app/components/Foundations/DisplayLogo.tsx +11 -3
  169. package/src/app/components/Layout/Header.tsx +21 -3
  170. package/src/app/components/Layout/Main.tsx +11 -3
  171. package/src/app/components/Navigation/MainNav.tsx +4 -2
  172. package/src/app/components/Navigation/MobileNav.tsx +4 -2
  173. package/src/app/components/Navigation/SideNav.tsx +46 -11
  174. package/src/app/components/NotFound.tsx +24 -0
  175. package/src/app/components/context/NavProvider.tsx +71 -0
  176. package/src/app/components/old/Header.tsx +3 -3
  177. package/src/app/components/util/index.ts +203 -380
  178. package/src/app/components/util/useCollectionAssets.ts +125 -0
  179. package/src/app/components/util/useFoundationTokens.ts +67 -0
  180. package/src/app/generated/default-pages.json +1 -0
  181. package/src/app/generated/nav-shell.json +1 -0
  182. package/src/app/lib/api/query.ts +6 -0
  183. package/src/app/lib/api/redact.ts +46 -0
  184. package/src/app/lib/asset-storage.ts +81 -0
  185. package/src/app/lib/docs-api/artifacts.ts +116 -0
  186. package/src/app/lib/docs-api/backend.ts +151 -0
  187. package/src/app/lib/docs-api/errors.ts +52 -0
  188. package/src/app/lib/docs-api/handler.ts +34 -0
  189. package/src/app/lib/docs-api/index.ts +7 -0
  190. package/src/app/lib/docs-api/records.ts +189 -0
  191. package/src/app/lib/docs-api/registry.ts +130 -0
  192. package/src/app/lib/docs-api/runtime-config.ts +173 -0
  193. package/src/app/lib/docs-api/serve.ts +37 -0
  194. package/src/app/lib/docs-api/token-detail.ts +19 -0
  195. package/src/app/lib/registry-api/allowlist.ts +163 -0
  196. package/src/app/lib/registry-api/asset-transfer.ts +363 -0
  197. package/src/app/lib/registry-api/auth.ts +68 -0
  198. package/src/app/lib/registry-api/entity-routes.ts +197 -0
  199. package/src/app/lib/registry-api/errors.ts +51 -0
  200. package/src/app/lib/registry-api/files.ts +135 -0
  201. package/src/app/lib/registry-api/handler.ts +96 -0
  202. package/src/app/lib/registry-api/index.ts +4 -0
  203. package/src/app/lib/registry-api/meta.ts +87 -0
  204. package/src/app/lib/registry-api/revalidate.ts +36 -0
  205. package/src/app/lib/registry-api/store.ts +348 -0
  206. package/src/app/lib/registry-api/token-transfer.ts +231 -0
  207. package/src/app/lib/registry-api/transfer.ts +513 -0
  208. package/src/app/lib/registry-api/validation.ts +74 -0
  209. package/src/app/lib/registry-connection.ts +58 -0
  210. package/src/app/lib/utils.ts +14 -0
  211. package/src/app/next.config.mjs +72 -13
  212. package/src/app/pages/[...slug]/index.tsx +74 -20
  213. package/src/app/pages/_app.tsx +5 -2
  214. package/src/app/pages/api/docs/artifacts/[...artifactPath].ts +16 -0
  215. package/src/app/pages/api/docs/assets/[collection]/[...path].ts +50 -0
  216. package/src/app/pages/api/docs/assets/[collection]/index.ts +22 -0
  217. package/src/app/pages/api/docs/components/[id]/assets/[asset].ts +33 -0
  218. package/src/app/pages/api/docs/components/[id]/inspect/[previewId].ts +20 -0
  219. package/src/app/pages/api/docs/components/[id]/previews/[previewId].ts +21 -0
  220. package/src/app/pages/api/docs/components/[id].ts +24 -0
  221. package/src/app/pages/api/docs/components.json.ts +12 -0
  222. package/src/app/pages/api/docs/nav.json.ts +48 -0
  223. package/src/app/pages/api/docs/patterns/[id]/preview.ts +19 -0
  224. package/src/app/pages/api/docs/patterns/[id].ts +24 -0
  225. package/src/app/pages/api/docs/patterns.json.ts +12 -0
  226. package/src/app/pages/api/docs/tokens/components/[id].ts +30 -0
  227. package/src/app/pages/api/docs/tokens/foundations/[type].ts +32 -0
  228. package/src/app/pages/api/registry/components/[id]/files/[...filePath].ts +19 -0
  229. package/src/app/pages/api/registry/components/[id]/files/index.ts +19 -0
  230. package/src/app/pages/api/registry/components/[id].ts +20 -0
  231. package/src/app/pages/api/registry/components/index.ts +19 -0
  232. package/src/app/pages/api/registry/health.ts +20 -0
  233. package/src/app/pages/api/registry/patterns/[id]/files/[...filePath].ts +19 -0
  234. package/src/app/pages/api/registry/patterns/[id]/files/index.ts +19 -0
  235. package/src/app/pages/api/registry/patterns/[id].ts +20 -0
  236. package/src/app/pages/api/registry/patterns/index.ts +19 -0
  237. package/src/app/pages/api/registry/transfer/[kind]/[id].ts +39 -0
  238. package/src/app/pages/api/registry/transfer/assets/[collection].ts +24 -0
  239. package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +23 -0
  240. package/src/app/pages/api/registry/transfer/assets/blobs/have.ts +11 -0
  241. package/src/app/pages/api/registry/transfer/assets/index.ts +10 -0
  242. package/src/app/pages/api/registry/transfer/tokens/[...setId].ts +26 -0
  243. package/src/app/pages/api/registry/transfer/tokens/index.ts +11 -0
  244. package/src/app/pages/assets/fonts.tsx +41 -17
  245. package/src/app/pages/assets/index.tsx +2 -2
  246. package/src/app/pages/assets/logos.tsx +6 -3
  247. package/src/app/pages/foundations/colors.tsx +18 -8
  248. package/src/app/pages/foundations/effects.tsx +18 -8
  249. package/src/app/pages/foundations/icons/[name]/index.tsx +28 -16
  250. package/src/app/pages/foundations/icons/index.tsx +18 -12
  251. package/src/app/pages/foundations/index.tsx +1 -1
  252. package/src/app/pages/foundations/logo.tsx +12 -8
  253. package/src/app/pages/foundations/typography.tsx +19 -9
  254. package/src/app/pages/index.tsx +1 -1
  255. package/src/app/pages/system/component/[component]/index.tsx +87 -17
  256. package/src/app/pages/system/index.tsx +13 -4
  257. package/src/app/pages/system/pattern/[pattern]/index.tsx +70 -14
  258. package/src/app/pages/system/tokens/components/[component]/index.tsx +42 -14
  259. package/src/app/pages/system/tokens/foundations/colors.tsx +7 -5
  260. package/src/app/pages/system/tokens/foundations/effects.tsx +7 -5
  261. package/src/app/pages/system/tokens/foundations/typography.tsx +8 -4
  262. package/src/app/tsconfig.json +2 -3
  263. package/src/app-builder/build.ts +555 -11
  264. package/src/app-builder/client-config.ts +61 -4
  265. package/src/app-builder/config-diff/snapshot.ts +1 -11
  266. package/src/app-builder/index.ts +1 -1
  267. package/src/app-builder/nav-shell.ts +74 -0
  268. package/src/app-builder/paths.ts +27 -1
  269. package/src/app-builder/static-export.ts +242 -0
  270. package/src/app-builder/vercel-output.ts +297 -0
  271. package/src/app-builder/watchers/utils.ts +2 -0
  272. package/src/app-builder/watchers.ts +149 -25
  273. package/src/app-builder/websocket.ts +2 -2
  274. package/src/artifacts/README.md +14 -0
  275. package/src/artifacts/index.ts +24 -0
  276. package/src/artifacts/types.ts +146 -0
  277. package/src/artifacts/url.ts +122 -0
  278. package/src/cache/build-cache.ts +47 -2
  279. package/src/commands/build/app.ts +1 -2
  280. package/src/commands/build/index.ts +61 -0
  281. package/src/commands/checkout/index.ts +66 -0
  282. package/src/commands/db/migrate.ts +25 -0
  283. package/src/commands/index.ts +8 -0
  284. package/src/commands/publish/index.ts +63 -0
  285. package/src/config/defaults.ts +15 -0
  286. package/src/config/loader.ts +10 -1
  287. package/src/config/normalizers/declaration.ts +3 -0
  288. package/src/config/normalizers/page.ts +45 -0
  289. package/src/config/normalizers/pattern.ts +4 -1
  290. package/src/config/runtime.ts +48 -2
  291. package/src/index.ts +126 -3
  292. package/src/nav/index.ts +94 -0
  293. package/src/nav/registry.ts +54 -0
  294. package/src/nav/resolver.ts +217 -0
  295. package/src/nav/workspace.ts +87 -0
  296. package/src/pipeline/styles.ts +4 -35
  297. package/src/pipeline/token-transformers.ts +97 -0
  298. package/src/registry/asset-storage/adapters/vercel-blob.ts +51 -0
  299. package/src/registry/asset-storage/define.ts +15 -0
  300. package/src/registry/asset-storage/resolve.ts +72 -0
  301. package/src/registry/asset-storage/types.ts +46 -0
  302. package/src/registry/assets/layout.ts +38 -0
  303. package/src/registry/assets/sets.ts +48 -0
  304. package/src/registry/assets/transfer.ts +50 -0
  305. package/src/registry/checkout/assets.ts +126 -0
  306. package/src/registry/checkout/index.ts +596 -0
  307. package/src/registry/checkout/tokens.ts +152 -0
  308. package/src/registry/client.ts +234 -0
  309. package/src/registry/connection.ts +50 -0
  310. package/src/registry/db/adapter.ts +74 -0
  311. package/src/registry/db/client.ts +88 -0
  312. package/src/registry/db/index.ts +35 -0
  313. package/src/registry/db/migrate.ts +56 -0
  314. package/src/registry/db/schema.ts +444 -0
  315. package/src/registry/path.ts +26 -0
  316. package/src/registry/publish/artifacts.ts +147 -0
  317. package/src/registry/publish/assets.ts +129 -0
  318. package/src/registry/publish/errors.ts +34 -0
  319. package/src/registry/publish/index.ts +128 -0
  320. package/src/registry/publish/package.ts +272 -0
  321. package/src/registry/publish/publish-build.ts +51 -0
  322. package/src/registry/publish/tokens.ts +119 -0
  323. package/src/registry/tokens/sets.ts +132 -0
  324. package/src/registry/tokens/transfer.ts +53 -0
  325. package/src/registry/transfer.ts +82 -0
  326. package/src/store/filesystem-assets.ts +152 -0
  327. package/src/store/filesystem.ts +237 -0
  328. package/src/store/index.ts +62 -0
  329. package/src/store/registry.ts +334 -0
  330. package/src/store/source-files.ts +155 -0
  331. package/src/store/types.ts +210 -0
  332. package/src/transformers/plugins/csf-render.ts +31 -7
  333. package/src/transformers/plugins/handlebars-previews.ts +22 -3
  334. package/src/transformers/plugins/ssr-render.ts +145 -57
  335. package/src/transformers/preview/component/api.ts +2 -1
  336. package/src/transformers/preview/component/artifacts.ts +10 -1
  337. package/src/transformers/preview/component/builder.ts +9 -2
  338. package/src/transformers/preview/component/css.ts +10 -1
  339. package/src/transformers/preview/component/javascript.ts +55 -20
  340. package/src/transformers/preview/component/shared-artifacts.ts +146 -0
  341. package/src/transformers/preview/component.ts +5 -2
  342. package/src/transformers/preview/pattern/builder.ts +33 -7
  343. package/src/transformers/preview/pattern/html.ts +71 -27
  344. package/src/transformers/preview/types.ts +42 -0
  345. package/src/transformers/utils/handlebars.ts +31 -5
  346. package/src/types/config.ts +129 -31
  347. package/src/utils/menu-shell.ts +241 -0
  348. package/src/utils/stable-stringify.ts +7 -0
  349. package/templates/default/package.json.tpl +1 -1
  350. package/handoff.config.js +0 -90
  351. package/src/app/components/ComponentSearch.tsx +0 -93
@@ -1,9 +1,22 @@
1
1
  import Handoff from '..';
2
+ import type { RuntimeMode } from '../types/config';
2
3
  /**
3
4
  * Publishes the tokens API files to the public directory.
4
5
  */
5
6
  export declare const generateTokensApi: (handoff: Handoff) => Promise<void>;
7
+ /** Options for {@link persistClientConfig}. */
8
+ export interface PersistClientConfigOptions {
9
+ /**
10
+ * Force the runtime mode persisted to both `client.config.json` and `runtime.server.json`,
11
+ * irrespective of the source project's `runtime.mode`. The registry build sets this to `registry`
12
+ * so the packaged artifact reports registry mode (badge + DB-backed reads) even when built from a
13
+ * workspace-mode project.
14
+ */
15
+ runtimeModeOverride?: RuntimeMode;
16
+ }
6
17
  /**
7
- * Persists the client config to a JSON file.
18
+ * Persists the app's resolved config to disk: the browser-facing `client.config.json` and the
19
+ * server-only `runtime.server.json` the docs read API resolves the active mode + registry
20
+ * connection inputs from. Both are always written together so they never drift.
8
21
  */
9
- export declare const persistClientConfig: (handoff: Handoff) => Promise<void>;
22
+ export declare const persistClientConfig: (handoff: Handoff, options?: PersistClientConfigOptions) => Promise<void>;
@@ -16,6 +16,8 @@ exports.persistClientConfig = exports.generateTokensApi = void 0;
16
16
  const fs_extra_1 = __importDefault(require("fs-extra"));
17
17
  const path_1 = __importDefault(require("path"));
18
18
  const config_1 = require("../config");
19
+ const resolve_1 = require("../registry/asset-storage/resolve");
20
+ const adapter_1 = require("../registry/db/adapter");
19
21
  const paths_1 = require("./paths");
20
22
  /**
21
23
  * Publishes the tokens API files to the public directory.
@@ -50,13 +52,50 @@ const generateTokensApi = (handoff) => __awaiter(void 0, void 0, void 0, functio
50
52
  });
51
53
  exports.generateTokensApi = generateTokensApi;
52
54
  /**
53
- * Persists the client config to a JSON file.
55
+ * Build the server-only runtime config consumed by the docs read API.
56
+ *
57
+ * The browser-facing `client.config.json` carries only the resolved mode; the server additionally
58
+ * needs the registry connection *inputs* (selected adapter + the *name* of the database-URL env
59
+ * var) to back the registry-mode docs read API. These are non-secret — the connection-string value
60
+ * itself is never persisted, only resolved from the env var at request time.
54
61
  */
55
- const persistClientConfig = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
62
+ const buildServerRuntimeConfig = (config, modeOverride) => {
63
+ var _a, _b;
64
+ const assetStorage = (0, resolve_1.resolveAssetStorageFromConfig)(config);
65
+ return {
66
+ mode: (_b = modeOverride !== null && modeOverride !== void 0 ? modeOverride : (_a = config === null || config === void 0 ? void 0 : config.runtime) === null || _a === void 0 ? void 0 : _a.mode) !== null && _b !== void 0 ? _b : 'workspace',
67
+ registry: {
68
+ adapter: (0, adapter_1.resolveRegistryAdapter)(config),
69
+ databaseUrlEnv: (0, adapter_1.resolveDatabaseUrlEnv)(config),
70
+ apiTokenEnv: (0, adapter_1.resolveApiTokenEnv)(config),
71
+ },
72
+ assetStorage: {
73
+ adapter: assetStorage.adapterKind,
74
+ module: assetStorage.module,
75
+ tokenEnv: assetStorage.tokenEnv,
76
+ maxInlineBytes: assetStorage.maxInlineBytes,
77
+ options: assetStorage.options,
78
+ },
79
+ };
80
+ };
81
+ /**
82
+ * Persists the app's resolved config to disk: the browser-facing `client.config.json` and the
83
+ * server-only `runtime.server.json` the docs read API resolves the active mode + registry
84
+ * connection inputs from. Both are always written together so they never drift.
85
+ */
86
+ const persistClientConfig = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1, ...args_1], void 0, function* (handoff, options = {}) {
56
87
  const appPath = (0, paths_1.getAppPath)(handoff);
57
- const destination = path_1.default.resolve(appPath, 'client.config.json');
58
88
  // Ensure directory exists
59
89
  yield fs_extra_1.default.ensureDir(appPath);
60
- yield fs_extra_1.default.writeJson(destination, { config: (0, config_1.getClientConfig)(handoff.config) }, { spaces: 2 });
90
+ const clientConfig = (0, config_1.getClientConfig)(handoff.config);
91
+ if (options.runtimeModeOverride) {
92
+ // A forced registry mode (the registry build target) is never a connected workspace, so the
93
+ // baked client config must report `connected: false` even when packaged from a connected
94
+ // workspace project.
95
+ const connected = options.runtimeModeOverride === 'registry' ? false : clientConfig.runtime.connected;
96
+ clientConfig.runtime = Object.assign(Object.assign({}, clientConfig.runtime), { mode: options.runtimeModeOverride, connected });
97
+ }
98
+ yield fs_extra_1.default.writeJson(path_1.default.resolve(appPath, 'client.config.json'), { config: clientConfig }, { spaces: 2 });
99
+ yield fs_extra_1.default.writeJson(path_1.default.resolve(appPath, 'runtime.server.json'), buildServerRuntimeConfig(handoff.config, options.runtimeModeOverride), { spaces: 2 });
61
100
  });
62
101
  exports.persistClientConfig = persistClientConfig;
@@ -1,11 +1,6 @@
1
1
  /** outerKey -> Map<innerKey, serializedValue> */
2
2
  export type MapSnapshot = Map<string, Map<string, string>>;
3
- /**
4
- * Deterministic JSON serializer that sorts object keys at every nesting level.
5
- * Prevents false-positive diffs when identical values are constructed with
6
- * different key insertion order across reloads.
7
- */
8
- export declare const stableStringify: (value: unknown) => string;
3
+ export { stableStringify } from '../../utils/stable-stringify';
9
4
  /**
10
5
  * Compares two MapSnapshots and returns the outer keys where the inner maps
11
6
  * differ (added, removed, or values changed).
@@ -1,15 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.diffMapSnapshots = exports.stableStringify = void 0;
4
- /**
5
- * Deterministic JSON serializer that sorts object keys at every nesting level.
6
- * Prevents false-positive diffs when identical values are constructed with
7
- * different key insertion order across reloads.
8
- */
9
- const stableStringify = (value) => JSON.stringify(value, (_, v) => v && typeof v === 'object' && !Array.isArray(v)
10
- ? Object.fromEntries(Object.entries(v).sort(([a], [b]) => a.localeCompare(b)))
11
- : v);
12
- exports.stableStringify = stableStringify;
4
+ var stable_stringify_1 = require("../../utils/stable-stringify");
5
+ Object.defineProperty(exports, "stableStringify", { enumerable: true, get: function () { return stable_stringify_1.stableStringify; } });
13
6
  /**
14
7
  * Compares two MapSnapshots and returns the outer keys where the inner maps
15
8
  * differ (added, removed, or values changed).
@@ -1,2 +1,2 @@
1
1
  export { buildComponents } from '../pipeline/components';
2
- export { default, devApp, watchApp } from './build';
2
+ export { default, devApp, watchApp, HandoffBuildError, DEFAULT_BUILD_TARGET, type BuildPackage, type BuildTarget } from './build';
@@ -3,10 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.watchApp = exports.devApp = exports.default = exports.buildComponents = void 0;
6
+ exports.DEFAULT_BUILD_TARGET = exports.HandoffBuildError = exports.watchApp = exports.devApp = exports.default = exports.buildComponents = void 0;
7
7
  var components_1 = require("../pipeline/components");
8
8
  Object.defineProperty(exports, "buildComponents", { enumerable: true, get: function () { return components_1.buildComponents; } });
9
9
  var build_1 = require("./build");
10
10
  Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(build_1).default; } });
11
11
  Object.defineProperty(exports, "devApp", { enumerable: true, get: function () { return build_1.devApp; } });
12
12
  Object.defineProperty(exports, "watchApp", { enumerable: true, get: function () { return build_1.watchApp; } });
13
+ Object.defineProperty(exports, "HandoffBuildError", { enumerable: true, get: function () { return build_1.HandoffBuildError; } });
14
+ Object.defineProperty(exports, "DEFAULT_BUILD_TARGET", { enumerable: true, get: function () { return build_1.DEFAULT_BUILD_TARGET; } });
@@ -0,0 +1,23 @@
1
+ import Handoff from '..';
2
+ /**
3
+ * Relative path (within the staged Next app) of the baked navigation shell. The docs read API
4
+ * (`pages/api/docs/nav.json.ts`) imports this statically so it is bundled into the route chunk and
5
+ * available at runtime without any filesystem read — see `src/utils/menu-shell.ts` for why the
6
+ * markdown-driven shell must be frozen at build time for the Vercel registry function.
7
+ */
8
+ export declare const NAV_SHELL_RELATIVE_PATH: string;
9
+ /** Baked package-default markdown used as the registry catch-all's runtime fallback. */
10
+ export declare const DEFAULT_PAGES_RELATIVE_PATH: string;
11
+ /**
12
+ * Bake package-default docs into the staged app so registry fallback renders do not depend on the
13
+ * build machine's absolute module path. The static JSON import is traced into every deployment.
14
+ */
15
+ export declare const generateDefaultPages: (handoff: Handoff, appPath: string) => Promise<void>;
16
+ /**
17
+ * Bake the markdown-driven navigation shell into the staged app as `generated/nav-shell.json`.
18
+ *
19
+ * Must run BEFORE `next build` so Next inlines the JSON import into the API route chunk. Generated
20
+ * for every build target (workspace dev/start, static export, registry) so the static import never
21
+ * fails to resolve; only registry-mode clients actually fetch and use it.
22
+ */
23
+ export declare const generateNavShell: (handoff: Handoff, appPath: string, includeWorkspacePages?: boolean) => Promise<void>;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.generateNavShell = exports.generateDefaultPages = exports.DEFAULT_PAGES_RELATIVE_PATH = exports.NAV_SHELL_RELATIVE_PATH = void 0;
16
+ const fs_extra_1 = __importDefault(require("fs-extra"));
17
+ const gray_matter_1 = __importDefault(require("gray-matter"));
18
+ const path_1 = __importDefault(require("path"));
19
+ const menu_shell_1 = require("../utils/menu-shell");
20
+ const logger_1 = require("../utils/logger");
21
+ /**
22
+ * Relative path (within the staged Next app) of the baked navigation shell. The docs read API
23
+ * (`pages/api/docs/nav.json.ts`) imports this statically so it is bundled into the route chunk and
24
+ * available at runtime without any filesystem read — see `src/utils/menu-shell.ts` for why the
25
+ * markdown-driven shell must be frozen at build time for the Vercel registry function.
26
+ */
27
+ exports.NAV_SHELL_RELATIVE_PATH = path_1.default.join('generated', 'nav-shell.json');
28
+ /** Baked package-default markdown used as the registry catch-all's runtime fallback. */
29
+ exports.DEFAULT_PAGES_RELATIVE_PATH = path_1.default.join('generated', 'default-pages.json');
30
+ /** Recursively collect package docs (except dedicated index routes) by their catch-all id. */
31
+ const collectDefaultPages = (root, relativeParts = []) => {
32
+ if (!fs_extra_1.default.existsSync(root))
33
+ return {};
34
+ const pages = {};
35
+ for (const entry of fs_extra_1.default.readdirSync(root)) {
36
+ const absolutePath = path_1.default.join(root, entry);
37
+ if (fs_extra_1.default.lstatSync(absolutePath).isDirectory()) {
38
+ Object.assign(pages, collectDefaultPages(absolutePath, [...relativeParts, entry]));
39
+ }
40
+ else if (entry.endsWith('.md') && entry !== 'index.md') {
41
+ const id = [...relativeParts, entry.replace(/\.md$/, '')].join('/');
42
+ const { data, content } = (0, gray_matter_1.default)(fs_extra_1.default.readFileSync(absolutePath, 'utf8'));
43
+ pages[id] = { metadata: data, content };
44
+ }
45
+ }
46
+ return pages;
47
+ };
48
+ /**
49
+ * Bake package-default docs into the staged app so registry fallback renders do not depend on the
50
+ * build machine's absolute module path. The static JSON import is traced into every deployment.
51
+ */
52
+ const generateDefaultPages = (handoff, appPath) => __awaiter(void 0, void 0, void 0, function* () {
53
+ const pages = collectDefaultPages(path_1.default.join(handoff.modulePath, 'config', 'docs'));
54
+ const target = path_1.default.resolve(appPath, exports.DEFAULT_PAGES_RELATIVE_PATH);
55
+ yield fs_extra_1.default.ensureDir(path_1.default.dirname(target));
56
+ yield fs_extra_1.default.writeJson(target, pages);
57
+ });
58
+ exports.generateDefaultPages = generateDefaultPages;
59
+ /**
60
+ * Bake the markdown-driven navigation shell into the staged app as `generated/nav-shell.json`.
61
+ *
62
+ * Must run BEFORE `next build` so Next inlines the JSON import into the API route chunk. Generated
63
+ * for every build target (workspace dev/start, static export, registry) so the static import never
64
+ * fails to resolve; only registry-mode clients actually fetch and use it.
65
+ */
66
+ const generateNavShell = (handoff_1, appPath_1, ...args_1) => __awaiter(void 0, [handoff_1, appPath_1, ...args_1], void 0, function* (handoff, appPath, includeWorkspacePages = true) {
67
+ var _a, _b, _c;
68
+ const docRoot = path_1.default.join(handoff.modulePath, 'config', 'docs');
69
+ // A registry serves only what is in its database, so workspace `pages/` present at build time must
70
+ // NOT be frozen into the shell (same rule as components/patterns). Published pages are merged into
71
+ // the shell at request time by `/api/docs/nav.json`; the package's own `config/docs` stays baked.
72
+ const workingPagesDir = includeWorkspacePages ? path_1.default.resolve(handoff.workingPath, 'pages') : undefined;
73
+ const basePath = (_c = (_b = (_a = handoff.config) === null || _a === void 0 ? void 0 : _a.app) === null || _b === void 0 ? void 0 : _b.base_path) !== null && _c !== void 0 ? _c : '';
74
+ const shell = (0, menu_shell_1.buildMenuShell)({ docRoot, workingPagesDir, basePath });
75
+ const target = path_1.default.resolve(appPath, exports.NAV_SHELL_RELATIVE_PATH);
76
+ yield fs_extra_1.default.ensureDir(path_1.default.dirname(target));
77
+ yield fs_extra_1.default.writeJson(target, shell, { spaces: 2 });
78
+ logger_1.Logger.info(`Baked navigation shell (${shell.length} sections) to ${exports.NAV_SHELL_RELATIVE_PATH}.`);
79
+ });
80
+ exports.generateNavShell = generateNavShell;
@@ -50,14 +50,35 @@ const mirrorDirectory = (sourcePath, destinationPath) => __awaiter(void 0, void
50
50
  yield fs_extra_1.default.ensureDir(path_1.default.dirname(destinationPath));
51
51
  yield fs_extra_1.default.copy(sourcePath, destinationPath, { overwrite: true });
52
52
  });
53
+ /**
54
+ * Stage the fetch-produced asset download bundles (`exported/<id>/{collection}.zip`) into the app
55
+ * public root, where the docs read API's workspace asset store serves them at
56
+ * `/api/docs/assets/{collection}/{collection}.zip` (the canonical download URL). `fetch` writes these
57
+ * bundles, but a plain `build`/`start` cleans and re-stages `.handoff` without carrying them, so the
58
+ * download links would 404 until a fetch happened to run. Copying the committed export bundles here
59
+ * makes the route resolve deterministically in workspace dev and static builds. Absent bundles (a
60
+ * project with no logos/icons) are skipped.
61
+ */
62
+ const stageAssetDownloadBundles = (handoff, destinationPublicPath) => __awaiter(void 0, void 0, void 0, function* () {
63
+ const bundles = [
64
+ { src: handoff.getLogosZipFilePath(), name: 'logos.zip' },
65
+ { src: handoff.getIconsZipFilePath(), name: 'icons.zip' },
66
+ ];
67
+ for (const { src, name } of bundles) {
68
+ if (fs_extra_1.default.existsSync(src) && fs_extra_1.default.statSync(src).isFile()) {
69
+ yield fs_extra_1.default.ensureDir(destinationPublicPath);
70
+ yield fs_extra_1.default.copy(src, path_1.default.resolve(destinationPublicPath, name), { overwrite: true });
71
+ }
72
+ }
73
+ });
53
74
  /**
54
75
  * Copy the public dir from the working dir to the module dir.
55
76
  */
56
77
  const syncPublicFiles = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
57
78
  const appPath = (0, exports.getAppPath)(handoff);
79
+ const destinationPublicPath = path_1.default.resolve(appPath, 'public');
58
80
  const workingPublicPath = (0, exports.getWorkingPublicPath)(handoff);
59
81
  if (workingPublicPath) {
60
- const destinationPublicPath = path_1.default.resolve(appPath, 'public');
61
82
  const sourceApiPath = path_1.default.resolve(workingPublicPath, 'api');
62
83
  const destinationApiPath = path_1.default.resolve(destinationPublicPath, 'api');
63
84
  yield fs_extra_1.default.copy(workingPublicPath, destinationPublicPath, {
@@ -69,5 +90,8 @@ const syncPublicFiles = (handoff) => __awaiter(void 0, void 0, void 0, function*
69
90
  });
70
91
  yield mirrorDirectory(sourceApiPath, destinationApiPath);
71
92
  }
93
+ // Stage the asset download bundles regardless of whether the workspace has a `public/` dir — they
94
+ // come from the fetch output, not the workspace public tree.
95
+ yield stageAssetDownloadBundles(handoff, destinationPublicPath);
72
96
  });
73
97
  exports.syncPublicFiles = syncPublicFiles;
@@ -0,0 +1,17 @@
1
+ import Handoff from '..';
2
+ /**
3
+ * Validate that every artifact referenced by generated HTML can be materialized — i.e. the
4
+ * referenced file exists under the artifact root. Required references (e.g. a React preview's
5
+ * `component/<id>.client.js`) are always emitted, so a missing one fails the static build *before*
6
+ * export rather than producing a site with broken previews.
7
+ *
8
+ * @throws When any generated HTML references an artifact that is not present on disk.
9
+ */
10
+ export declare const validateReferencedArtifacts: (handoff: Handoff) => void;
11
+ /**
12
+ * Materialize the docs read model into route-shaped static files under `outDir` (the Next export
13
+ * output). Produces the canonical artifact tree plus the list/detail metadata reads, matching the
14
+ * shapes the live docs read API returns so the exported site is byte-for-byte interchangeable with
15
+ * the dev server's responses.
16
+ */
17
+ export declare const materializeDocsReadModel: (handoff: Handoff, outDir: string) => Promise<void>;
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.materializeDocsReadModel = exports.validateReferencedArtifacts = void 0;
16
+ const fs_extra_1 = __importDefault(require("fs-extra"));
17
+ const path_1 = __importDefault(require("path"));
18
+ const url_1 = require("../artifacts/url");
19
+ const logger_1 = require("../utils/logger");
20
+ const paths_1 = require("./paths");
21
+ /**
22
+ * Absolute path of the generated artifact root for the running app — the same
23
+ * `.handoff/{projectId}/public/api` directory the docs read API resolves artifacts from.
24
+ */
25
+ const getStaticArtifactRoot = (handoff) => path_1.default.resolve((0, paths_1.getAppPath)(handoff), 'public', 'api');
26
+ const readJsonArray = (absolutePath) => {
27
+ try {
28
+ if (!fs_extra_1.default.existsSync(absolutePath) || !fs_extra_1.default.statSync(absolutePath).isFile()) {
29
+ return [];
30
+ }
31
+ const parsed = JSON.parse(fs_extra_1.default.readFileSync(absolutePath, 'utf8'));
32
+ return Array.isArray(parsed) ? parsed : [];
33
+ }
34
+ catch (_a) {
35
+ return [];
36
+ }
37
+ };
38
+ /** Whether the entity's primary detail artifact was generated (drives `current` vs `missing`). */
39
+ const detailArtifactExists = (artifactRoot, relativePath) => {
40
+ const absolutePath = path_1.default.resolve(artifactRoot, relativePath);
41
+ return fs_extra_1.default.existsSync(absolutePath) && fs_extra_1.default.statSync(absolutePath).isFile();
42
+ };
43
+ const deriveBuildStatus = (artifactRoot, detailRelativePath) => detailArtifactExists(artifactRoot, detailRelativePath) ? 'current' : 'missing';
44
+ /**
45
+ * Suffix of a component-owned React client/hydration bundle (`<id>.client.js`). Interactive React
46
+ * previews treat this artifact as a *required* reference: the
47
+ * preview cannot hydrate without it, so a missing one is a genuine build failure.
48
+ */
49
+ const CLIENT_ARTIFACT_SUFFIX = '.client.js';
50
+ /**
51
+ * Whether a referenced artifact is *required* — i.e. the referencing HTML cannot render correctly
52
+ * without it. Only the React client bundle qualifies. Component-owned styles/scripts
53
+ * (`<id>.css`/`<id>.js`) and shared/global artifacts (`main.css`/`main.js`/`shared.css`) are
54
+ * optional: the renderers emit `<id>.css`/`<id>.js` optimistically and a component that produces no
55
+ * stylesheet/script simply has none, so an absent optional artifact must not fail the build.
56
+ */
57
+ const isRequiredReference = (artifactPath) => artifactPath.endsWith(CLIENT_ARTIFACT_SUFFIX);
58
+ /**
59
+ * Extract the logical artifact paths a generated HTML document references through the canonical
60
+ * artifact route. References are read from the emitted `href`/`src` URLs (the build's structured
61
+ * references are realized as these tags); this is a build-time integrity check, not the runtime
62
+ * dependency-resolution path.
63
+ */
64
+ const extractReferencedArtifactPaths = (html) => {
65
+ // Only `href`/`src` attribute values are treated as references, so artifact-shaped strings that
66
+ // happen to appear in embedded preview content are not mistaken for real dependencies.
67
+ const pattern = new RegExp(`(?:href|src)=["'][^"']*?${url_1.ARTIFACTS_ROUTE_SEGMENT}/([^"']+)["']`, 'g');
68
+ const paths = new Set();
69
+ let match;
70
+ while ((match = pattern.exec(html)) !== null) {
71
+ const encoded = match[1].split(/[?#]/)[0];
72
+ const segments = encoded.split('/').filter(Boolean);
73
+ try {
74
+ const decoded = segments.map((segment) => decodeURIComponent(segment));
75
+ if (decoded.length > 0) {
76
+ paths.add(decoded.join('/'));
77
+ }
78
+ }
79
+ catch (_a) {
80
+ // A reference that cannot be decoded is treated as missing below by failing to resolve.
81
+ paths.add(encoded);
82
+ }
83
+ }
84
+ return Array.from(paths);
85
+ };
86
+ /** Recursively collect every file under a directory as paths relative to it. */
87
+ const collectFilesRelative = (root) => {
88
+ const results = [];
89
+ const walk = (current) => {
90
+ for (const entry of fs_extra_1.default.readdirSync(current, { withFileTypes: true })) {
91
+ const absolute = path_1.default.join(current, entry.name);
92
+ if (entry.isDirectory()) {
93
+ walk(absolute);
94
+ }
95
+ else if (entry.isFile()) {
96
+ results.push(path_1.default.relative(root, absolute));
97
+ }
98
+ }
99
+ };
100
+ if (fs_extra_1.default.existsSync(root)) {
101
+ walk(root);
102
+ }
103
+ return results;
104
+ };
105
+ /**
106
+ * Validate that every artifact referenced by generated HTML can be materialized — i.e. the
107
+ * referenced file exists under the artifact root. Required references (e.g. a React preview's
108
+ * `component/<id>.client.js`) are always emitted, so a missing one fails the static build *before*
109
+ * export rather than producing a site with broken previews.
110
+ *
111
+ * @throws When any generated HTML references an artifact that is not present on disk.
112
+ */
113
+ const validateReferencedArtifacts = (handoff) => {
114
+ const artifactRoot = getStaticArtifactRoot(handoff);
115
+ if (!fs_extra_1.default.existsSync(artifactRoot)) {
116
+ return;
117
+ }
118
+ const htmlFiles = collectFilesRelative(artifactRoot).filter((relative) => relative.toLowerCase().endsWith('.html'));
119
+ const missing = new Map();
120
+ for (const htmlRelative of htmlFiles) {
121
+ const html = fs_extra_1.default.readFileSync(path_1.default.resolve(artifactRoot, htmlRelative), 'utf8');
122
+ for (const referenced of extractReferencedArtifactPaths(html)) {
123
+ // Only required references gate the build; absent optional artifacts (component-owned
124
+ // styles/scripts, shared/global artifacts) are tolerated.
125
+ if (!isRequiredReference(referenced)) {
126
+ continue;
127
+ }
128
+ const target = path_1.default.resolve(artifactRoot, ...referenced.split('/'));
129
+ const contained = !path_1.default.relative(artifactRoot, target).startsWith('..');
130
+ if (!contained || !fs_extra_1.default.existsSync(target) || !fs_extra_1.default.statSync(target).isFile()) {
131
+ if (!missing.has(referenced)) {
132
+ missing.set(referenced, new Set());
133
+ }
134
+ missing.get(referenced).add(htmlRelative);
135
+ }
136
+ }
137
+ }
138
+ if (missing.size > 0) {
139
+ const details = Array.from(missing.entries())
140
+ .map(([artifactPath, referrers]) => ` - ${artifactPath} (referenced by ${Array.from(referrers).join(', ')})`)
141
+ .join('\n');
142
+ throw new Error(`Static build aborted: ${missing.size} required artifact(s) referenced by generated HTML could not be materialized:\n${details}`);
143
+ }
144
+ };
145
+ exports.validateReferencedArtifacts = validateReferencedArtifacts;
146
+ /**
147
+ * Materialize the docs read model into route-shaped static files under `outDir` (the Next export
148
+ * output). Produces the canonical artifact tree plus the list/detail metadata reads, matching the
149
+ * shapes the live docs read API returns so the exported site is byte-for-byte interchangeable with
150
+ * the dev server's responses.
151
+ */
152
+ const materializeDocsReadModel = (handoff, outDir) => __awaiter(void 0, void 0, void 0, function* () {
153
+ const artifactRoot = getStaticArtifactRoot(handoff);
154
+ if (!fs_extra_1.default.existsSync(artifactRoot)) {
155
+ logger_1.Logger.warn('No generated artifacts found to materialize for the static build.');
156
+ return;
157
+ }
158
+ const docsRoot = path_1.default.join(outDir, 'api', 'docs');
159
+ const artifactsOut = path_1.default.join(docsRoot, 'artifacts');
160
+ // Canonical artifact route: every generated artifact, addressable at `/api/docs/artifacts/{path}`.
161
+ yield fs_extra_1.default.ensureDir(artifactsOut);
162
+ yield fs_extra_1.default.copy(artifactRoot, artifactsOut, { overwrite: true });
163
+ const components = readJsonArray(path_1.default.resolve(artifactRoot, 'components.json'));
164
+ const patterns = readJsonArray(path_1.default.resolve(artifactRoot, 'patterns.json'));
165
+ yield fs_extra_1.default.ensureDir(docsRoot);
166
+ yield fs_extra_1.default.writeJson(path_1.default.join(docsRoot, 'components.json'), components);
167
+ yield fs_extra_1.default.writeJson(path_1.default.join(docsRoot, 'patterns.json'), patterns);
168
+ // Detail metadata reads, each carrying the artifact-derived build state.
169
+ yield fs_extra_1.default.ensureDir(path_1.default.join(docsRoot, 'components'));
170
+ for (const component of components) {
171
+ const detail = Object.assign(Object.assign({}, component), { build: { status: deriveBuildStatus(artifactRoot, path_1.default.join('component', `${component.id}.json`)) } });
172
+ yield fs_extra_1.default.writeJson(path_1.default.join(docsRoot, 'components', `${component.id}.json`), detail);
173
+ }
174
+ yield fs_extra_1.default.ensureDir(path_1.default.join(docsRoot, 'patterns'));
175
+ for (const pattern of patterns) {
176
+ const detail = Object.assign(Object.assign({}, pattern), { build: { status: deriveBuildStatus(artifactRoot, path_1.default.join('pattern', `${pattern.id}.json`)) } });
177
+ yield fs_extra_1.default.writeJson(path_1.default.join(docsRoot, 'patterns', `${pattern.id}.json`), detail);
178
+ }
179
+ yield materializeAssetDownloads(handoff, docsRoot);
180
+ logger_1.Logger.success(`Materialized docs read model (${components.length} component(s), ${patterns.length} pattern(s)) for static export.`);
181
+ });
182
+ exports.materializeDocsReadModel = materializeDocsReadModel;
183
+ /**
184
+ * Materialize the whole-collection asset download bundles at the canonical asset route
185
+ * (`api/docs/assets/{collection}/{collection}.zip`) — the same URL the docs read API serves in
186
+ * registry mode, so the "Download" links resolve identically on a static host. The bundles come from
187
+ * the fetch output (`exported/<id>/{collection}.zip`); `next build` never re-creates them, and the
188
+ * fetch-time copy into the app `public/` is wiped by the build's clean+restage, so they are copied
189
+ * here. Absent bundles (a project with no logos/icons) are skipped, not an error.
190
+ */
191
+ const materializeAssetDownloads = (handoff, docsRoot) => __awaiter(void 0, void 0, void 0, function* () {
192
+ const bundles = [
193
+ { collection: 'logos', src: handoff.getLogosZipFilePath() },
194
+ { collection: 'icons', src: handoff.getIconsZipFilePath() },
195
+ ];
196
+ let copied = 0;
197
+ for (const { collection, src } of bundles) {
198
+ if (fs_extra_1.default.existsSync(src) && fs_extra_1.default.statSync(src).isFile()) {
199
+ yield fs_extra_1.default.copy(src, path_1.default.join(docsRoot, 'assets', collection, `${collection}.zip`), { overwrite: true });
200
+ copied += 1;
201
+ }
202
+ }
203
+ if (copied > 0) {
204
+ logger_1.Logger.info(`Materialized ${copied} asset download bundle(s) for static export.`);
205
+ }
206
+ });
@@ -0,0 +1,39 @@
1
+ import Handoff from '..';
2
+ /** Absolute path of the `.vercel/output` directory at the repo root. */
3
+ export declare const getVercelOutputPath: (handoff: Handoff) => string;
4
+ /**
5
+ * Lay a materialized static export under `.vercel/output/static/` and write a minimal Build Output
6
+ * API v3 `config.json` carrying the app's `trailingSlash` and resolved `basePath`. Pure CDN files —
7
+ * no functions, no database.
8
+ *
9
+ * @param exportDir Absolute path of the materialized static export (the same content the static
10
+ * target writes to `out/<projectId>`).
11
+ */
12
+ export declare const writeStaticVercelOutput: (handoff: Handoff, exportDir: string) => Promise<void>;
13
+ /**
14
+ * Options for {@link writeRegistryVercelOutput}. The build module owns the standalone assembly (entry
15
+ * flattening + static/public/config layout) and passes it in, keeping this seam free of any
16
+ * standalone-layout knowledge and avoiding a circular import.
17
+ */
18
+ export interface RegistryVercelOutputOptions {
19
+ /** Absolute path of the staged Next app (source of `.next/static` + `public`). */
20
+ appPath: string;
21
+ /** Absolute path of the Next `standalone` build root (`<appPath>/.next/standalone`). */
22
+ standaloneRoot: string;
23
+ /**
24
+ * Lay the standalone bundle out under the given entry dir (flatten the server entry, copy static
25
+ * assets, public tree, and runtime config beside it) and return the absolute server-entry path.
26
+ * Shared with the standalone deliverable so both layouts stay identical.
27
+ */
28
+ assembleStandalone: (handoff: Handoff, appPath: string, standaloneRoot: string, entryDir: string) => Promise<string>;
29
+ }
30
+ /**
31
+ * Package the dynamic registry app as the Vercel Build Output API directory. Lays the traced
32
+ * standalone bundle into a single Node function (`functions/index.func/`)
33
+ * wrapping the Next standalone server, copies the immutable static assets + public tree under
34
+ * `static/` for the Edge CDN, and writes a route table that serves static files first and routes
35
+ * everything else (SSR pages, the docs read API, the registry API) to the function.
36
+ *
37
+ * Hard constraint: `.vercel/output` lives at the repo root with that exact name (see module header).
38
+ */
39
+ export declare const writeRegistryVercelOutput: (handoff: Handoff, options: RegistryVercelOutputOptions) => Promise<void>;