@mysten-incubation/devstack 0.3.0 → 0.4.0

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 (132) hide show
  1. package/README.md +1 -1
  2. package/dashboard-ui/assets/{grpc-Clz0oOtc.js → grpc-CpkDu4SA.js} +1 -1
  3. package/dashboard-ui/assets/{index-Cmqv9kiU.js → index-jLPRmjst.js} +2 -2
  4. package/dashboard-ui/index.html +1 -1
  5. package/dist/api/inference-network.d.mts +5 -5
  6. package/dist/api/inference-network.mjs +5 -5
  7. package/dist/api/inference-network.mjs.map +1 -1
  8. package/dist/api/run-stack-internal.mjs +1 -3
  9. package/dist/api/run-stack-internal.mjs.map +1 -1
  10. package/dist/build-integrations/dapp-kit/index.d.mts +12 -0
  11. package/dist/build-integrations/dapp-kit/index.mjs +14 -1
  12. package/dist/build-integrations/dapp-kit/index.mjs.map +1 -1
  13. package/dist/build-integrations/playwright/errors.d.mts +1 -1
  14. package/dist/build-integrations/playwright/errors.mjs.map +1 -1
  15. package/dist/build-integrations/playwright/index.d.mts +2 -2
  16. package/dist/build-integrations/playwright/index.mjs +2 -2
  17. package/dist/build-integrations/playwright/wallet-context.d.mts +2 -1
  18. package/dist/build-integrations/playwright/wallet-context.mjs +141 -41
  19. package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
  20. package/dist/build-integrations/runtime/dapp-kit-slot.mjs +1 -1
  21. package/dist/build-integrations/runtime/dapp-kit-slot.mjs.map +1 -1
  22. package/dist/build-integrations/vite/index.d.mts +54 -42
  23. package/dist/build-integrations/vite/index.mjs +239 -106
  24. package/dist/build-integrations/vite/index.mjs.map +1 -1
  25. package/dist/build-integrations/vitest/global-setup.mjs +8 -8
  26. package/dist/build-integrations/vitest/global-setup.mjs.map +1 -1
  27. package/dist/cli/main.mjs +3 -2
  28. package/dist/cli/main.mjs.map +1 -1
  29. package/dist/cli/wirings/apply.mjs +0 -1
  30. package/dist/cli/wirings/apply.mjs.map +1 -1
  31. package/dist/cli/wirings/codegen.mjs +2 -3
  32. package/dist/cli/wirings/codegen.mjs.map +1 -1
  33. package/dist/cli/wirings/dump-deployment.mjs +141 -0
  34. package/dist/cli/wirings/dump-deployment.mjs.map +1 -0
  35. package/dist/cli/wirings/snapshot.mjs +0 -1
  36. package/dist/cli/wirings/snapshot.mjs.map +1 -1
  37. package/dist/contracts/codegenable.d.mts +22 -25
  38. package/dist/contracts/codegenable.mjs +35 -4
  39. package/dist/contracts/codegenable.mjs.map +1 -1
  40. package/dist/contracts/config-bindings.mjs +43 -18
  41. package/dist/contracts/config-bindings.mjs.map +1 -1
  42. package/dist/index.mjs +1 -1
  43. package/dist/orchestrators/boot.d.mts +0 -10
  44. package/dist/orchestrators/boot.mjs +19 -59
  45. package/dist/orchestrators/boot.mjs.map +1 -1
  46. package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
  47. package/dist/orchestrators/codegen/config-runtime.mjs +137 -112
  48. package/dist/orchestrators/codegen/config-runtime.mjs.map +1 -1
  49. package/dist/orchestrators/codegen/deployment-network-file.mjs +130 -0
  50. package/dist/orchestrators/codegen/deployment-network-file.mjs.map +1 -0
  51. package/dist/orchestrators/codegen/deployment-strict.mjs +77 -0
  52. package/dist/orchestrators/codegen/deployment-strict.mjs.map +1 -0
  53. package/dist/orchestrators/codegen/deployment.d.mts +63 -0
  54. package/dist/orchestrators/codegen/deployment.mjs +153 -0
  55. package/dist/orchestrators/codegen/deployment.mjs.map +1 -0
  56. package/dist/orchestrators/codegen/errors.mjs +7 -9
  57. package/dist/orchestrators/codegen/errors.mjs.map +1 -1
  58. package/dist/orchestrators/codegen/format.mjs +20 -1
  59. package/dist/orchestrators/codegen/format.mjs.map +1 -1
  60. package/dist/orchestrators/codegen/paths.mjs +1 -8
  61. package/dist/orchestrators/codegen/paths.mjs.map +1 -1
  62. package/dist/orchestrators/codegen/service.mjs +354 -141
  63. package/dist/orchestrators/codegen/service.mjs.map +1 -1
  64. package/dist/orchestrators/layers.mjs +0 -1
  65. package/dist/orchestrators/layers.mjs.map +1 -1
  66. package/dist/orchestrators/network-options.d.mts +37 -35
  67. package/dist/orchestrators/network-options.mjs +13 -10
  68. package/dist/orchestrators/network-options.mjs.map +1 -1
  69. package/dist/plugins/account/codegen.d.mts +3 -4
  70. package/dist/plugins/account/codegen.mjs +5 -7
  71. package/dist/plugins/account/codegen.mjs.map +1 -1
  72. package/dist/plugins/account/index.mjs +1 -3
  73. package/dist/plugins/account/index.mjs.map +1 -1
  74. package/dist/plugins/coin/codegen.mjs +5 -5
  75. package/dist/plugins/coin/codegen.mjs.map +1 -1
  76. package/dist/plugins/coin/index.mjs.map +1 -1
  77. package/dist/plugins/dashboard/schema/root.mjs +3 -1
  78. package/dist/plugins/dashboard/schema/root.mjs.map +1 -1
  79. package/dist/plugins/deepbook/codegen.mjs +4 -4
  80. package/dist/plugins/deepbook/codegen.mjs.map +1 -1
  81. package/dist/plugins/deepbook/index.mjs.map +1 -1
  82. package/dist/plugins/internal/codegen-helpers.mjs +0 -1
  83. package/dist/plugins/internal/codegen-helpers.mjs.map +1 -1
  84. package/dist/plugins/package/codegen.d.mts +1 -12
  85. package/dist/plugins/package/codegen.mjs +43 -25
  86. package/dist/plugins/package/codegen.mjs.map +1 -1
  87. package/dist/plugins/package/dep-resolution.mjs +1 -1
  88. package/dist/plugins/package/dep-resolution.mjs.map +1 -1
  89. package/dist/plugins/package/index.d.mts +14 -11
  90. package/dist/plugins/package/index.mjs +6 -7
  91. package/dist/plugins/package/index.mjs.map +1 -1
  92. package/dist/plugins/seal/codegen.mjs +3 -3
  93. package/dist/plugins/seal/codegen.mjs.map +1 -1
  94. package/dist/plugins/seal/index.mjs.map +1 -1
  95. package/dist/plugins/sui/codegen.mjs +5 -4
  96. package/dist/plugins/sui/codegen.mjs.map +1 -1
  97. package/dist/plugins/sui/index.mjs +1 -2
  98. package/dist/plugins/sui/index.mjs.map +1 -1
  99. package/dist/plugins/wallet/codegen.d.mts +8 -12
  100. package/dist/plugins/wallet/codegen.mjs +22 -15
  101. package/dist/plugins/wallet/codegen.mjs.map +1 -1
  102. package/dist/plugins/wallet/index.d.mts +4 -3
  103. package/dist/plugins/wallet/index.mjs +1 -1
  104. package/dist/plugins/wallet/index.mjs.map +1 -1
  105. package/dist/plugins/wallet/service.d.mts +2 -2
  106. package/dist/plugins/wallet/service.mjs +2 -3
  107. package/dist/plugins/wallet/service.mjs.map +1 -1
  108. package/dist/plugins/walrus/codegen.mjs +4 -4
  109. package/dist/plugins/walrus/codegen.mjs.map +1 -1
  110. package/dist/plugins/walrus/index.mjs +1 -1
  111. package/dist/plugins/walrus/index.mjs.map +1 -1
  112. package/dist/substrate/manifest.d.mts +17 -22
  113. package/dist/substrate/manifest.mjs +4 -6
  114. package/dist/substrate/manifest.mjs.map +1 -1
  115. package/dist/substrate/plugin-ctx.d.mts +6 -6
  116. package/dist/substrate/plugin-ctx.mjs.map +1 -1
  117. package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
  118. package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
  119. package/dist/substrate/runtime/paths.mjs +1 -1
  120. package/dist/surfaces/cli/command-tree.mjs +6 -6
  121. package/dist/surfaces/cli/command-tree.mjs.map +1 -1
  122. package/dist/surfaces/cli/index.mjs +6 -4
  123. package/dist/surfaces/cli/index.mjs.map +1 -1
  124. package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
  125. package/package.json +1 -1
  126. package/dist/cli/wirings/dump-ids.mjs +0 -86
  127. package/dist/cli/wirings/dump-ids.mjs.map +0 -1
  128. package/dist/orchestrators/codegen/id-config.d.mts +0 -1
  129. package/dist/orchestrators/codegen/id-config.mjs +0 -107
  130. package/dist/orchestrators/codegen/id-config.mjs.map +0 -1
  131. package/dist/orchestrators/codegen/output-location.mjs +0 -32
  132. package/dist/orchestrators/codegen/output-location.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment-strict.mjs","names":[],"sources":["../../../src/orchestrators/codegen/deployment-strict.ts"],"sourcesContent":["// The APP-SPECIFIC strict deployment type — emitted as\n// `src/generated/deployment.ts`.\n//\n// Unlike `config-runtime.ts` (a verbatim constant string), THIS file is\n// RENDERED FROM DATA: it narrows the generic `NetworkDeployment` (from\n// `config-runtime.ts`) to exactly the package names + MVR placeholders the\n// app declares, and enumerates the live networks the app ships from the\n// `deployments/` directory filenames.\n//\n// Four exported types/values:\n// - `AppPackages` — exhaustive over THIS app's declared package\n// names.\n// - `AppNetworkDeployment` — the per-network shape a prod author hand-\n// writes in `deployments/<net>.ts`: a\n// `NetworkDeployment` with `packages: AppPackages`\n// and `mvrOverrides` requiring the declared\n// `@local/*` placeholder keys. NO `accounts`\n// (those ride the runtime envelope, dev-only).\n// - `ProvidedNetwork` — a union of the LIVE network names (the\n// `deployments/*.ts` filenames, local excluded);\n// `never` when none.\n// - `ProvidedDeployments` — `Partial<Record<ProvidedNetwork,\n// AppNetworkDeployment>>`.\n// - `NETWORK_NAMES` — a literal `as const` tuple\n// `[<localNetworkName>, ...<deployments files>]`\n// for dapp-kit's typed `switchNetwork` (D2).\n//\n// Types-only + zero runtime: on a clean clone (no `deployments/` dir) this is\n// the local network alone — `ProvidedNetwork = never`, `ProvidedDeployments`\n// empty — so `tsc` stays green.\n\n/** Input the renderer derives the strict types from. All values are\n * CONFIG-known (no chain / no live resolution): the declared package names\n * + MVR placeholders come from the `config.ts` aggregate bucket, the local\n * network name from the sui binding, the live network names from the\n * `deployments/*.ts` filenames. */\nexport interface DeploymentStrictInput {\n\t/** The local (dev-stack) network name — the sui binding's `network`\n\t * field. Always the head of `NETWORK_NAMES`. */\n\treadonly localNetworkName: string;\n\t/** This app's declared package names (the `config.packages.<name>`\n\t * keys), in stable order. */\n\treadonly packageNames: ReadonlyArray<string>;\n\t/** This app's declared MVR placeholders (the `@local/<slug>` keys an app\n\t * feeds dapp-kit's `mvr.overrides.packages`), in stable order. */\n\treadonly mvrPlaceholders: ReadonlyArray<string>;\n\t/** This app's declared MVR `types` keys — fully-qualified\n\t * `@local/<slug>::<module>::<Name>` named struct tags (the @mysten MVR\n\t * `types` override surface), in stable order. Empty when the app declares\n\t * no Move datatypes. Drives `AppNetworkDeployment.mvrOverrides.types`. */\n\treadonly mvrTypeTags: ReadonlyArray<string>;\n\t/** The LIVE network names — `deployments/*.ts` filenames (sans `.ts`),\n\t * with the local network name excluded. Empty on a clean clone. */\n\treadonly providedNetworks: ReadonlyArray<string>;\n\t/** The structured SERVICE-VALUE channel: `values[namespace][key] = <tsType>`\n\t * — every generic (non-sugar) config-binding the app's service plugins\n\t * declare (deepbook ids/pools, walrus/seal endpoints, coin decimals +\n\t * full coin type, package object captures). Drives a REQUIRED `values`\n\t * shape on `AppNetworkDeployment` so a hand-written `deployments/<net>.ts`\n\t * is compile-checked for every service value. Empty for a service-less\n\t * (counter-style) app — then `values` stays optional/loose. */\n\treadonly serviceValues: Readonly<Record<string, Readonly<Record<string, string>>>>;\n}\n\n/** Output path (relative to the generated tree) of the emitted strict type. */\nexport const DEPLOYMENT_STRICT_OUTPUT_PATH = 'deployment.ts';\n\n/** Render the app-specific strict deployment type from data. Pure +\n * deterministic (stable key ordering), types-only output. */\nexport const renderDeploymentStrict = (input: DeploymentStrictInput): string => {\n\tconst { localNetworkName, packageNames, mvrPlaceholders, mvrTypeTags } = input;\n\n\t// Defensive: the local network is the head of `NETWORK_NAMES` and is never a\n\t// committed/provided network, so drop it (and any duplicate) from\n\t// `providedNetworks` even if a caller passes it in. The source discovery\n\t// (`discoverProvidedNetworks`) already filters it, but a hand-supplied set\n\t// (tests, future callers) must not yield a duplicate `localnet` tuple element\n\t// or pollute the `ProvidedNetwork` union.\n\tconst providedNetworks = [...new Set(input.providedNetworks)].filter(\n\t\t(n) => n !== localNetworkName,\n\t);\n\n\t// `AppPackages` — exhaustive over the declared package names. Each entry\n\t// carries `{ id; objects? }`. An app with no packages still declares the\n\t// interface (empty), so `AppNetworkDeployment.packages` stays a concrete\n\t// type.\n\tconst packagesBody =\n\t\tpackageNames.length > 0\n\t\t\t? packageNames\n\t\t\t\t\t.map(\n\t\t\t\t\t\t(name) =>\n\t\t\t\t\t\t\t`\\treadonly ${JSON.stringify(name)}: { readonly id: string; readonly objects?: Record<string, string> };`,\n\t\t\t\t\t)\n\t\t\t\t\t.join('\\n')\n\t\t\t: '\\treadonly [packageName: string]: never;';\n\n\t// `mvrOverrides.packages` requires every declared `@local/<slug>` placeholder\n\t// key (resolved package id). The @mysten MVR override shape exactly:\n\t// `{ packages: Record<mvr, id>; types: Record<namedType, resolvedType> }`.\n\tconst mvrPackagesBody =\n\t\tmvrPlaceholders.length > 0\n\t\t\t? mvrPlaceholders.map((mvr) => `\\t\\t\\t${JSON.stringify(mvr)}: string;`).join('\\n')\n\t\t\t: '\\t\\t\\t// (this app declares no MVR placeholders)';\n\t// `mvrOverrides.types` is OPT-IN. When the app DECLARES named types, the\n\t// field is REQUIRED and exhaustively narrowed over the declared tags (a\n\t// hand-written `deployments/<net>.ts` must provide exactly those, valued by\n\t// the resolved `<packageId>::<module>::<Name>`), plus the permissive trailing\n\t// index. When NONE are declared, the field is OPTIONAL + loose so an app that\n\t// exposes no Move datatypes need not write a `types` block at all.\n\tconst hasMvrTypes = mvrTypeTags.length > 0;\n\tconst mvrTypesBody = hasMvrTypes\n\t\t? mvrTypeTags.map((tag) => `\\t\\t\\t${JSON.stringify(tag)}: string;`).join('\\n')\n\t\t: '';\n\tconst mvrTypesField = hasMvrTypes\n\t\t? `\\t\\treadonly types: {\\n${mvrTypesBody}\\n\\t\\t} & { readonly [namedType: string]: string };`\n\t\t: `\\t\\treadonly types?: { readonly [namedType: string]: string };`;\n\n\t// The structured `values` channel — required per service namespace/key with\n\t// its concrete TS type, so a hand-written deployment is compile-checked for\n\t// every service value (deepbook/coin/seal/walrus/package-objects). A\n\t// service-less app contributes nothing → `values` stays optional/loose.\n\tconst valueNamespaces = Object.keys(input.serviceValues).sort();\n\tconst hasServiceValues = valueNamespaces.length > 0;\n\tconst valuesBody = hasServiceValues\n\t\t? valueNamespaces\n\t\t\t\t.map((ns) => {\n\t\t\t\t\tconst keys = Object.keys(input.serviceValues[ns]!).sort();\n\t\t\t\t\tconst fields = keys\n\t\t\t\t\t\t.map((k) => `\\t\\t\\t${JSON.stringify(k)}: ${input.serviceValues[ns]![k]!};`)\n\t\t\t\t\t\t.join('\\n');\n\t\t\t\t\treturn `\\t\\t${JSON.stringify(ns)}: {\\n${fields}\\n\\t\\t};`;\n\t\t\t\t})\n\t\t\t\t.join('\\n')\n\t\t: '';\n\n\t// The emitted `values` field. With service values it is REQUIRED and\n\t// narrowed per namespace/key (a missing service value fails `tsc`), plus a\n\t// permissive trailing index so an app can carry extra namespaces. With NO\n\t// service values (a counter-style app) it stays OPTIONAL + loose so a\n\t// service-less clean clone stays `tsc`-green.\n\tconst valuesField = hasServiceValues\n\t\t? `\\treadonly values: {\\n${valuesBody}\\n\\t} & {\\n\\t\\treadonly [namespace: string]: { readonly [key: string]: unknown };\\n\\t};`\n\t\t: `\\treadonly values?: NetworkDeployment['values'];`;\n\n\t// `ProvidedNetwork` — the live (committed) network union; `never` when\n\t// the app ships none (a clean clone with no `deployments/` dir).\n\tconst providedUnion =\n\t\tprovidedNetworks.length > 0\n\t\t\t? providedNetworks.map((n) => JSON.stringify(n)).join(' | ')\n\t\t\t: 'never';\n\n\t// `NETWORK_NAMES` — `[<local>, ...<provided>]` as a literal tuple.\n\tconst networkNamesTuple = [localNetworkName, ...providedNetworks]\n\t\t.map((n) => JSON.stringify(n))\n\t\t.join(', ');\n\n\treturn `// THIS FILE IS AUTO-GENERATED BY @mysten/devstack.\n// Do not edit by hand — your changes will be overwritten by the next\n// \\`devstack codegen\\`. Apps consume codegen output; codegen output never\n// imports from devstack.\n//\n// The APP-SPECIFIC strict deployment type. A prod author writes a\n// \\`deployments/<net>.ts\\` that \\`satisfies AppNetworkDeployment\\` — the type\n// is exhaustive over this app's declared packages + MVR placeholders, so a\n// missing/typo'd id fails \\`tsc\\`. The live network set\n// (\\`ProvidedNetwork\\` / \\`NETWORK_NAMES\\`) is derived from the\n// \\`deployments/*.ts\\` filenames. Types-only — zero runtime — except the\n// \\`NETWORK_NAMES\\` literal tuple (for dapp-kit's typed \\`switchNetwork\\`).\n\nimport type { NetworkDeployment } from './config-runtime.js';\n\n/** Exhaustive over THIS app's declared package names. */\nexport interface AppPackages {\n${packagesBody}\n}\n\n/** The per-network shape a prod author hand-writes in\n * \\`deployments/<net>.ts\\`: a \\`NetworkDeployment\\` narrowed so \\`packages\\` is\n * exhaustive over this app's packages, \\`mvrOverrides\\` is the @mysten MVR\n * override surface (\\`{ packages, types }\\`) requiring every declared\n * \\`@local/<slug>\\` placeholder + named-type tag, and \\`values\\` requires every\n * service value namespace/key (when the app declares any). NO \\`accounts\\` —\n * dev accounts ride the runtime envelope, never the per-network authoring\n * surface. */\nexport interface AppNetworkDeployment\n\textends Omit<NetworkDeployment, 'packages' | 'mvrOverrides' | 'values'> {\n\treadonly packages: AppPackages;\n\treadonly mvrOverrides: {\n\t\treadonly packages: {\n${mvrPackagesBody}\n\t\t} & { readonly [mvrPlaceholder: string]: string };\n${mvrTypesField}\n\t};\n${valuesField}\n}\n\n/** The LIVE network names this app ships — the \\`deployments/*.ts\\` filenames\n * (local excluded). \\`never\\` when the app ships no committed deployments. */\nexport type ProvidedNetwork = ${providedUnion};\n\n/** The committed per-network deployments map a prod build / dev serve loads.\n * Partial — an app need not ship every live network at once. */\nexport type ProvidedDeployments = Partial<Record<ProvidedNetwork, AppNetworkDeployment>>;\n\n/** The full network-name set, local-first: \\`[<local>, ...<provided>]\\`.\n * dapp-kit's \\`networks\\` / \\`switchNetwork\\` type-check against this tuple\n * (D2). */\nexport const NETWORK_NAMES = [${networkNamesTuple}] as const;\n`;\n};\n"],"mappings":";;AAiEA,MAAa,gCAAgC;;;AAI7C,MAAa,0BAA0B,UAAyC;CAC/E,MAAM,EAAE,kBAAkB,cAAc,iBAAiB,gBAAgB;CAQzE,MAAM,mBAAmB,CAAC,GAAG,IAAI,IAAI,MAAM,gBAAgB,CAAC,CAAC,CAAC,QAC5D,MAAM,MAAM,gBACd;CAMA,MAAM,eACL,aAAa,SAAS,IACnB,aACC,KACC,SACA,cAAc,KAAK,UAAU,IAAI,EAAE,sEACrC,CAAC,CACA,KAAK,IAAI,IACV;CAKJ,MAAM,kBACL,gBAAgB,SAAS,IACtB,gBAAgB,KAAK,QAAQ,SAAS,KAAK,UAAU,GAAG,EAAE,UAAU,CAAC,CAAC,KAAK,IAAI,IAC/E;CAOJ,MAAM,cAAc,YAAY,SAAS;CACzC,MAAM,eAAe,cAClB,YAAY,KAAK,QAAQ,SAAS,KAAK,UAAU,GAAG,EAAE,UAAU,CAAC,CAAC,KAAK,IAAI,IAC3E;CACH,MAAM,gBAAgB,cACnB,0BAA0B,aAAa,uDACvC;CAMH,MAAM,kBAAkB,OAAO,KAAK,MAAM,aAAa,CAAC,CAAC,KAAK;CAC9D,MAAM,mBAAmB,gBAAgB,SAAS;CAClD,MAAM,aAAa,mBAChB,gBACC,KAAK,OAAO;EAEZ,MAAM,SADO,OAAO,KAAK,MAAM,cAAc,GAAI,CAAC,CAAC,KACjC,CAAC,CACjB,KAAK,MAAM,SAAS,KAAK,UAAU,CAAC,EAAE,IAAI,MAAM,cAAc,GAAG,CAAE,GAAI,EAAE,CAAC,CAC1E,KAAK,IAAI;EACX,OAAO,OAAO,KAAK,UAAU,EAAE,EAAE,OAAO,OAAO;CAChD,CAAC,CAAC,CACD,KAAK,IAAI,IACV;CAuBH,OAAO;;;;;;;;;;;;;;;;;EAiBN,aAAa;;;;;;;;;;;;;;;;EAgBb,gBAAgB;;EAEhB,cAAc;;EAnDK,mBACjB,yBAAyB,WAAW,2FACpC,mDAmDU;;;;;gCA9CZ,iBAAiB,SAAS,IACvB,iBAAiB,KAAK,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,IACzD,QAiDyC;;;;;;;;;gCA9CnB,CAAC,kBAAkB,GAAG,gBAAgB,CAAC,CAC/D,KAAK,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,CAC7B,KAAK,IAqDwC,EAAE;;AAElD"}
@@ -0,0 +1,63 @@
1
+ import { Effect, FileSystem, Schema } from "effect";
2
+
3
+ //#region src/orchestrators/codegen/deployment.d.ts
4
+ /** Any JSON-serialisable value. The deployment round-trips through
5
+ * `JSON.stringify` into the injected `__DEVSTACK_DEPLOYMENT__` global, so a
6
+ * plugin live value carried in the generic `values` channel must be JSON. */
7
+ type JsonValue = null | string | number | boolean | ReadonlyArray<JsonValue> | {
8
+ readonly [key: string]: JsonValue;
9
+ };
10
+ /** One network's full resolved deployment — the per-network UNIT of the
11
+ * multi-network envelope. Carries the connection coordinates (`rpc` is the
12
+ * load-bearing field the app reads synchronously at module load, plus
13
+ * optional `chainId`/`faucet`/`graphql`) FLATTENED inline alongside the
14
+ * on-chain ids/values for that network. The whole document is data — no
15
+ * functions, no devstack imports — so it round-trips through JSON and a
16
+ * `JSON.stringify` into a vite `define` global. */
17
+ declare const NetworkDeploymentSchema: Schema.Struct<{
18
+ /** This network's name (the `networks.<name>` key it sits under).
19
+ * Optional on input — the envelope key is authoritative, so the Vite
20
+ * merge and the boot writer set it from the key; a hand-written
21
+ * committed `deployments/<net>.ts` need not restate it. */
22
+ readonly network: Schema.optional<Schema.String>; /** Load-bearing — the RPC endpoint the app reads synchronously. */
23
+ readonly rpc: Schema.String;
24
+ readonly chainId: Schema.optional<Schema.String>;
25
+ readonly faucet: Schema.optional<Schema.NullOr<Schema.String>>;
26
+ readonly graphql: Schema.optional<Schema.NullOr<Schema.String>>;
27
+ /** Marks a LIVE LOCAL network (a `devstack up` stack). The deploy
28
+ * filter (`command === 'build'`) drops these — a production bundle
29
+ * ships only committed, non-local networks. Optional so a hand-written
30
+ * committed `deployments/<net>.ts` omits it (treated as non-local). */
31
+ readonly local: Schema.optional<Schema.Boolean>;
32
+ /** Resolved package ids (+ object captures). Optional, default `{}`: a
33
+ * network may have zero packages (a network-only stack, or a
34
+ * hand-authored deploy file), and the boot writer always emits the key
35
+ * anyway — defaulting keeps the injected blob well-formed so the app's
36
+ * resolvers throw `DevstackConfigMissingError` rather than a raw
37
+ * TypeError on a missing section. */
38
+ readonly packages: Schema.withDecodingDefaultKey<Schema.$Record<Schema.String, Schema.Struct<{
39
+ readonly id: Schema.String;
40
+ readonly objects: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
41
+ }>>>;
42
+ /** The @mysten MVR override surface an app feeds straight into dapp-kit's
43
+ * `mvr.overrides`, for this network:
44
+ * - `packages`: `@local/<slug>` → resolved package id.
45
+ * - `types`: `@local/<slug>::<module>::<Name>` → resolved
46
+ * `<packageId>::<module>::<Name>` (each KEY passes
47
+ * `isValidNamedType`, each VALUE a concrete struct tag).
48
+ * Both maps optional-default `{}` (a network may declare no packages, and
49
+ * an older injected unit / hand-written committed deployment may omit
50
+ * `types`). */
51
+ readonly mvrOverrides: Schema.withDecodingDefaultKey<Schema.Struct<{
52
+ readonly packages: Schema.withDecodingDefaultKey<Schema.$Record<Schema.String, Schema.String>>;
53
+ readonly types: Schema.withDecodingDefaultKey<Schema.$Record<Schema.String, Schema.String>>;
54
+ }>>;
55
+ /** Generic resolver channel — `values[namespace][key]` carries
56
+ * arbitrary live plugin JSON the typed fields above can't. Optional
57
+ * so older deployment files (no `values`) still decode. */
58
+ readonly values: Schema.optional<Schema.$Record<Schema.String, Schema.$Record<Schema.String, Schema.Codec<JsonValue, JsonValue, never, never>>>>;
59
+ }>;
60
+ type NetworkDeployment = typeof NetworkDeploymentSchema.Type;
61
+ //#endregion
62
+ export { NetworkDeployment };
63
+ //# sourceMappingURL=deployment.d.mts.map
@@ -0,0 +1,153 @@
1
+ import { decodeJsonTextSync, decodeUnknownSync } from "../../substrate/runtime/runtime-decode.mjs";
2
+ import { atomicWriteFile } from "../../substrate/runtime/atomic-write.mjs";
3
+ import { Effect, Schema } from "effect";
4
+ //#region src/orchestrators/codegen/deployment.ts
5
+ /** The all-zero Sui object id used as the placeholder for an unresolved
6
+ * (stack-free) id. A committed `config.ts` resolver treats this value —
7
+ * and a missing id — identically: it THROWS `DevstackConfigMissingError`
8
+ * at access time (see `config-runtime.ts`). Apps never transact with it. */
9
+ const UNRESOLVED_ID = "0x0000000000000000000000000000000000000000000000000000000000000000";
10
+ /** One `packages.<name>` entry — the resolved package id plus any
11
+ * resolved object captures for that network. */
12
+ const DeploymentPackageSchema = Schema.Struct({
13
+ id: Schema.String,
14
+ objects: Schema.optional(Schema.Record(Schema.String, Schema.String))
15
+ });
16
+ /** A JSON-serialisable value carried in the generic `values` channel.
17
+ * The whole deployment round-trips through `JSON.stringify` into the
18
+ * vite `define` global, so the value space is exactly JSON. */
19
+ const DeploymentJsonSchema = Schema.suspend(() => Schema.Union([
20
+ Schema.Null,
21
+ Schema.String,
22
+ Schema.Number,
23
+ Schema.Boolean,
24
+ Schema.Array(DeploymentJsonSchema),
25
+ Schema.Record(Schema.String, DeploymentJsonSchema)
26
+ ]));
27
+ /** The generic resolver channel: a two-level namespaced map of arbitrary
28
+ * JSON values a plugin contributes at boot and the committed `config.ts`
29
+ * reads back via `resolveValue(namespace, key)`. This is the open-ended
30
+ * sibling to the typed `network`/`networks`/`packages`/`mvrOverrides`
31
+ * fields — it carries the plugin live values the fixed
32
+ * channel can't (deepbook pool ids, coin types, walrus/seal endpoints). */
33
+ const DeploymentValuesSchema = Schema.Record(Schema.String, Schema.Record(Schema.String, DeploymentJsonSchema));
34
+ /** One network's full resolved deployment — the per-network UNIT of the
35
+ * multi-network envelope. Carries the connection coordinates (`rpc` is the
36
+ * load-bearing field the app reads synchronously at module load, plus
37
+ * optional `chainId`/`faucet`/`graphql`) FLATTENED inline alongside the
38
+ * on-chain ids/values for that network. The whole document is data — no
39
+ * functions, no devstack imports — so it round-trips through JSON and a
40
+ * `JSON.stringify` into a vite `define` global. */
41
+ const NetworkDeploymentSchema = Schema.Struct({
42
+ /** This network's name (the `networks.<name>` key it sits under).
43
+ * Optional on input — the envelope key is authoritative, so the Vite
44
+ * merge and the boot writer set it from the key; a hand-written
45
+ * committed `deployments/<net>.ts` need not restate it. */
46
+ network: Schema.optional(Schema.String),
47
+ /** Load-bearing — the RPC endpoint the app reads synchronously. */
48
+ rpc: Schema.String,
49
+ chainId: Schema.optional(Schema.String),
50
+ faucet: Schema.optional(Schema.NullOr(Schema.String)),
51
+ graphql: Schema.optional(Schema.NullOr(Schema.String)),
52
+ /** Marks a LIVE LOCAL network (a `devstack up` stack). The deploy
53
+ * filter (`command === 'build'`) drops these — a production bundle
54
+ * ships only committed, non-local networks. Optional so a hand-written
55
+ * committed `deployments/<net>.ts` omits it (treated as non-local). */
56
+ local: Schema.optional(Schema.Boolean),
57
+ /** Resolved package ids (+ object captures). Optional, default `{}`: a
58
+ * network may have zero packages (a network-only stack, or a
59
+ * hand-authored deploy file), and the boot writer always emits the key
60
+ * anyway — defaulting keeps the injected blob well-formed so the app's
61
+ * resolvers throw `DevstackConfigMissingError` rather than a raw
62
+ * TypeError on a missing section. */
63
+ packages: Schema.Record(Schema.String, DeploymentPackageSchema).pipe(Schema.withDecodingDefaultKey(Effect.succeed({}))),
64
+ /** The @mysten MVR override surface an app feeds straight into dapp-kit's
65
+ * `mvr.overrides`, for this network:
66
+ * - `packages`: `@local/<slug>` → resolved package id.
67
+ * - `types`: `@local/<slug>::<module>::<Name>` → resolved
68
+ * `<packageId>::<module>::<Name>` (each KEY passes
69
+ * `isValidNamedType`, each VALUE a concrete struct tag).
70
+ * Both maps optional-default `{}` (a network may declare no packages, and
71
+ * an older injected unit / hand-written committed deployment may omit
72
+ * `types`). */
73
+ mvrOverrides: Schema.Struct({
74
+ packages: Schema.Record(Schema.String, Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed({}))),
75
+ types: Schema.Record(Schema.String, Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed({})))
76
+ }).pipe(Schema.withDecodingDefaultKey(Effect.succeed({
77
+ packages: {},
78
+ types: {}
79
+ }))),
80
+ /** Generic resolver channel — `values[namespace][key]` carries
81
+ * arbitrary live plugin JSON the typed fields above can't. Optional
82
+ * so older deployment files (no `values`) still decode. */
83
+ values: Schema.optional(DeploymentValuesSchema)
84
+ });
85
+ /** The injected multi-network ENVELOPE — every network this deployment
86
+ * supports, keyed by name, plus the default the app opens on. The single
87
+ * on-disk / injected shape: boot writes one (single-network) envelope, the
88
+ * Vite plugin merges live + committed envelopes, and the generated resolver
89
+ * reads it off `__DEVSTACK_DEPLOYMENT__`. */
90
+ const DevstackDeploymentSchema = Schema.Struct({
91
+ /** The network the app opens on (a `networks.<name>` key). */
92
+ defaultNetwork: Schema.String,
93
+ networks: Schema.Record(Schema.String, NetworkDeploymentSchema),
94
+ /** DEV-only account name → address map. Accounts are a network-AGNOSTIC
95
+ * dev concept (keypair gen needs no network), so they live at the
96
+ * envelope level — NOT per-`NetworkDeployment`. They exist only when
97
+ * running THROUGH devstack (dev serve); a real prod deployment carries
98
+ * none. Optional, default `{}` (a hand-written committed envelope, or a
99
+ * pure prod build, omits it). */
100
+ accounts: Schema.Record(Schema.String, Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed({})))
101
+ });
102
+ /** Failure reading or decoding a deployment file. */
103
+ var DeploymentDecodeError = class extends Schema.TaggedErrorClass()("DeploymentDecodeError", {
104
+ source: Schema.String,
105
+ message: Schema.String,
106
+ cause: Schema.optional(Schema.Defect)
107
+ }) {};
108
+ /** Canonical filename of the per-stack deployment under
109
+ * `.devstack/stacks/<name>/`. */
110
+ const DEPLOYMENT_FILENAME = "deployment.json";
111
+ /** Write the deployment as pretty JSON. Idempotent at the byte level for
112
+ * identical input (sorted by the caller's assembly order). */
113
+ const writeDeployment = (path, config) => Effect.gen(function* () {
114
+ const text = `${JSON.stringify(config, null, 2)}\n`;
115
+ yield* atomicWriteFile(path, new TextEncoder().encode(text), { mode: 420 }).pipe(Effect.mapError((cause) => new DeploymentDecodeError({
116
+ source: path,
117
+ message: "failed to write deployment",
118
+ cause
119
+ })));
120
+ });
121
+ /** Project a runtime-decode issue into the typed `DeploymentDecodeError`. */
122
+ const mkDeploymentError = (issue) => new DeploymentDecodeError({
123
+ source: issue.source,
124
+ message: issue.message,
125
+ ...issue.cause === void 0 ? {} : { cause: issue.cause }
126
+ });
127
+ /** Decode + validate a deployment from already-read JSON text. The single
128
+ * schema-decode seam every reader shares (the Vite plugin, the `dump-deployment`
129
+ * verb, the codegen verb) so the parse-and-validate decision lives in ONE
130
+ * place rather than each caller hand-rolling `JSON.parse`. Throws
131
+ * `DeploymentDecodeError` on malformed JSON or a shape that violates
132
+ * `DeploymentSchema`. */
133
+ const decodeDeployment = (text) => decodeJsonTextSync(DevstackDeploymentSchema, text, {
134
+ source: DEPLOYMENT_FILENAME,
135
+ mkError: mkDeploymentError
136
+ });
137
+ /** Decode + validate a single per-network `NetworkDeployment` unit from an
138
+ * already-parsed object (a committed `deployments/<net>.ts` thunk's
139
+ * `deployment`). The Vite plugin's merge layer calls this on each committed
140
+ * thunk so a malformed committed deployment surfaces loudly at config-load
141
+ * (rather than silently injecting a broken network). Throws
142
+ * `DeploymentDecodeError` on a shape that violates `NetworkDeploymentSchema`.
143
+ * The caller overwrites `network`/`local` from the envelope key afterwards,
144
+ * so the unit's own `network` field is optional here. */
145
+ const decodeNetworkDeployment = (value, source) => decodeUnknownSync(NetworkDeploymentSchema, value, {
146
+ source,
147
+ mkError: mkDeploymentError,
148
+ message: "committed deployment is not a valid NetworkDeployment"
149
+ });
150
+ //#endregion
151
+ export { DEPLOYMENT_FILENAME, UNRESOLVED_ID, decodeDeployment, decodeNetworkDeployment, writeDeployment };
152
+
153
+ //# sourceMappingURL=deployment.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment.mjs","names":[],"sources":["../../../src/orchestrators/codegen/deployment.ts"],"sourcesContent":["// The deployment interchange file — the single source of on-chain ids.\n//\n// Stop treating on-chain ids as codegen OUTPUT. Treat them as loaded\n// CONFIG DATA. Codegen is a deterministic, stack-free function\n// `(Move source, deployment?) → generated`. Booting a stack's only new\n// job is to PRODUCE this file; the codegen verb READS it (or runs with\n// none); the vite plugin INJECTS it into the app build/dev server.\n//\n// Four authors/consumers, one shared schema:\n// - WRITER: `orchestrators/boot.ts` post-acquire assembles this from\n// acquired plugin state and writes it to the gitignored\n// `.devstack/stacks/<name>/deployment.json`.\n// - READER: the stack-free `devstack codegen` verb reads it via the\n// optional `--config <file>` flag (absent ⇒ ids unresolved).\n// - CONSUMER: the emitted `config-runtime.ts` resolver reads the same\n// shape off the injected `__DEVSTACK_DEPLOYMENT__` global.\n// - INJECTOR: the vite plugin reads the live file (dev) or committed\n// per-network deployment files (prod, via the `deployments`\n// option / `DEVSTACK_DEPLOYMENT_FILE`), MERGES them, and\n// `define`s the global.\n//\n// The shape is a MULTI-NETWORK ENVELOPE: `{ defaultNetwork, networks }`.\n// Each `networks.<name>` entry is a `NetworkDeployment` — the id-half of\n// today's `config.ts` for that one network: rpc (+ chainId/faucet/graphql),\n// per-package ids (+ captured object ids), the MVR placeholder → id override\n// map an app feeds dapp-kit, and a generic `values` channel. Dev `accounts`\n// (name → address) live at the ENVELOPE level — they are network-agnostic\n// dev identities, not part of any per-network unit. A plain single-network dev stack produces an envelope\n// with exactly one entry (the live local network, `local: true`); a real\n// multi-network deployment keys several. The envelope is the uniform unit\n// the Vite plugin merges live (dev) + committed (prod) sources into.\n\nimport { Effect, FileSystem, Schema } from 'effect';\n\nimport { atomicWriteFile } from '../../substrate/runtime/atomic-write.ts';\nimport {\n\tdecodeJsonTextSync,\n\tdecodeUnknownSync,\n\ttype RuntimeDecodeIssue,\n} from '../../substrate/runtime/runtime-decode.ts';\n\n// -----------------------------------------------------------------------------\n// Sentinel — the all-zero id that marks an UNRESOLVED on-chain id.\n// -----------------------------------------------------------------------------\n\n/** The all-zero Sui object id used as the placeholder for an unresolved\n * (stack-free) id. A committed `config.ts` resolver treats this value —\n * and a missing id — identically: it THROWS `DevstackConfigMissingError`\n * at access time (see `config-runtime.ts`). Apps never transact with it. */\nexport const UNRESOLVED_ID = '0x0000000000000000000000000000000000000000000000000000000000000000';\n\n/** True when `id` is absent or the all-zero sentinel — i.e. not a real\n * resolved on-chain id. */\nexport const isUnresolvedId = (id: string | undefined | null): boolean =>\n\tid === undefined || id === null || id === UNRESOLVED_ID;\n\n// -----------------------------------------------------------------------------\n// JSON value space — the generic `values` channel carries exactly JSON.\n// -----------------------------------------------------------------------------\n\n/** Any JSON-serialisable value. The deployment round-trips through\n * `JSON.stringify` into the injected `__DEVSTACK_DEPLOYMENT__` global, so a\n * plugin live value carried in the generic `values` channel must be JSON. */\nexport type JsonValue =\n\t| null\n\t| string\n\t| number\n\t| boolean\n\t| ReadonlyArray<JsonValue>\n\t| { readonly [key: string]: JsonValue };\n\n// -----------------------------------------------------------------------------\n// Schema\n// -----------------------------------------------------------------------------\n\n/** One `packages.<name>` entry — the resolved package id plus any\n * resolved object captures for that network. */\nexport const DeploymentPackageSchema = Schema.Struct({\n\tid: Schema.String,\n\tobjects: Schema.optional(Schema.Record(Schema.String, Schema.String)),\n});\n\n/** A JSON-serialisable value carried in the generic `values` channel.\n * The whole deployment round-trips through `JSON.stringify` into the\n * vite `define` global, so the value space is exactly JSON. */\n// Typed as `Schema.Codec<JsonValue>` (NOT `Schema.Schema<JsonValue>`): the\n// type-only `Schema<T>` widens `DecodingServices` to `unknown`, which would\n// poison every decoder of `DeploymentSchema`; `Codec<T>` defaults the decode/\n// encode services to `never`, matching the real (service-free) recursion.\nexport const DeploymentJsonSchema: Schema.Codec<JsonValue> = Schema.suspend(() =>\n\tSchema.Union([\n\t\tSchema.Null,\n\t\tSchema.String,\n\t\tSchema.Number,\n\t\tSchema.Boolean,\n\t\tSchema.Array(DeploymentJsonSchema),\n\t\tSchema.Record(Schema.String, DeploymentJsonSchema),\n\t]),\n) as Schema.Codec<JsonValue>;\n\n/** The generic resolver channel: a two-level namespaced map of arbitrary\n * JSON values a plugin contributes at boot and the committed `config.ts`\n * reads back via `resolveValue(namespace, key)`. This is the open-ended\n * sibling to the typed `network`/`networks`/`packages`/`mvrOverrides`\n * fields — it carries the plugin live values the fixed\n * channel can't (deepbook pool ids, coin types, walrus/seal endpoints). */\nexport const DeploymentValuesSchema = Schema.Record(\n\tSchema.String,\n\tSchema.Record(Schema.String, DeploymentJsonSchema),\n);\n\n/** One network's full resolved deployment — the per-network UNIT of the\n * multi-network envelope. Carries the connection coordinates (`rpc` is the\n * load-bearing field the app reads synchronously at module load, plus\n * optional `chainId`/`faucet`/`graphql`) FLATTENED inline alongside the\n * on-chain ids/values for that network. The whole document is data — no\n * functions, no devstack imports — so it round-trips through JSON and a\n * `JSON.stringify` into a vite `define` global. */\nexport const NetworkDeploymentSchema = Schema.Struct({\n\t/** This network's name (the `networks.<name>` key it sits under).\n\t * Optional on input — the envelope key is authoritative, so the Vite\n\t * merge and the boot writer set it from the key; a hand-written\n\t * committed `deployments/<net>.ts` need not restate it. */\n\tnetwork: Schema.optional(Schema.String),\n\t/** Load-bearing — the RPC endpoint the app reads synchronously. */\n\trpc: Schema.String,\n\tchainId: Schema.optional(Schema.String),\n\tfaucet: Schema.optional(Schema.NullOr(Schema.String)),\n\tgraphql: Schema.optional(Schema.NullOr(Schema.String)),\n\t/** Marks a LIVE LOCAL network (a `devstack up` stack). The deploy\n\t * filter (`command === 'build'`) drops these — a production bundle\n\t * ships only committed, non-local networks. Optional so a hand-written\n\t * committed `deployments/<net>.ts` omits it (treated as non-local). */\n\tlocal: Schema.optional(Schema.Boolean),\n\t/** Resolved package ids (+ object captures). Optional, default `{}`: a\n\t * network may have zero packages (a network-only stack, or a\n\t * hand-authored deploy file), and the boot writer always emits the key\n\t * anyway — defaulting keeps the injected blob well-formed so the app's\n\t * resolvers throw `DevstackConfigMissingError` rather than a raw\n\t * TypeError on a missing section. */\n\tpackages: Schema.Record(Schema.String, DeploymentPackageSchema).pipe(\n\t\tSchema.withDecodingDefaultKey(Effect.succeed({})),\n\t),\n\t/** The @mysten MVR override surface an app feeds straight into dapp-kit's\n\t * `mvr.overrides`, for this network:\n\t * - `packages`: `@local/<slug>` → resolved package id.\n\t * - `types`: `@local/<slug>::<module>::<Name>` → resolved\n\t * `<packageId>::<module>::<Name>` (each KEY passes\n\t * `isValidNamedType`, each VALUE a concrete struct tag).\n\t * Both maps optional-default `{}` (a network may declare no packages, and\n\t * an older injected unit / hand-written committed deployment may omit\n\t * `types`). */\n\tmvrOverrides: Schema.Struct({\n\t\tpackages: Schema.Record(Schema.String, Schema.String).pipe(\n\t\t\tSchema.withDecodingDefaultKey(Effect.succeed({})),\n\t\t),\n\t\ttypes: Schema.Record(Schema.String, Schema.String).pipe(\n\t\t\tSchema.withDecodingDefaultKey(Effect.succeed({})),\n\t\t),\n\t}).pipe(Schema.withDecodingDefaultKey(Effect.succeed({ packages: {}, types: {} }))),\n\t/** Generic resolver channel — `values[namespace][key]` carries\n\t * arbitrary live plugin JSON the typed fields above can't. Optional\n\t * so older deployment files (no `values`) still decode. */\n\tvalues: Schema.optional(DeploymentValuesSchema),\n});\n\n/** The injected multi-network ENVELOPE — every network this deployment\n * supports, keyed by name, plus the default the app opens on. The single\n * on-disk / injected shape: boot writes one (single-network) envelope, the\n * Vite plugin merges live + committed envelopes, and the generated resolver\n * reads it off `__DEVSTACK_DEPLOYMENT__`. */\nexport const DevstackDeploymentSchema = Schema.Struct({\n\t/** The network the app opens on (a `networks.<name>` key). */\n\tdefaultNetwork: Schema.String,\n\tnetworks: Schema.Record(Schema.String, NetworkDeploymentSchema),\n\t/** DEV-only account name → address map. Accounts are a network-AGNOSTIC\n\t * dev concept (keypair gen needs no network), so they live at the\n\t * envelope level — NOT per-`NetworkDeployment`. They exist only when\n\t * running THROUGH devstack (dev serve); a real prod deployment carries\n\t * none. Optional, default `{}` (a hand-written committed envelope, or a\n\t * pure prod build, omits it). */\n\taccounts: Schema.Record(Schema.String, Schema.String).pipe(\n\t\tSchema.withDecodingDefaultKey(Effect.succeed({})),\n\t),\n});\n\nexport type DevstackDeployment = typeof DevstackDeploymentSchema.Type;\nexport type NetworkDeployment = typeof NetworkDeploymentSchema.Type;\nexport type DeploymentPackage = typeof DeploymentPackageSchema.Type;\nexport type DeploymentValues = typeof DeploymentValuesSchema.Type;\n\n// -----------------------------------------------------------------------------\n// Error\n// -----------------------------------------------------------------------------\n\n/** Failure reading or decoding a deployment file. */\nexport class DeploymentDecodeError extends Schema.TaggedErrorClass<DeploymentDecodeError>()(\n\t'DeploymentDecodeError',\n\t{\n\t\tsource: Schema.String,\n\t\tmessage: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n// -----------------------------------------------------------------------------\n// Read / write helpers\n// -----------------------------------------------------------------------------\n\n/** Canonical filename of the per-stack deployment under\n * `.devstack/stacks/<name>/`. */\nexport const DEPLOYMENT_FILENAME = 'deployment.json';\n\n/** Write the deployment as pretty JSON. Idempotent at the byte level for\n * identical input (sorted by the caller's assembly order). */\nexport const writeDeployment = (\n\tpath: string,\n\tconfig: DevstackDeployment,\n): Effect.Effect<void, DeploymentDecodeError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst text = `${JSON.stringify(config, null, 2)}\\n`;\n\t\tconst bytes = new TextEncoder().encode(text);\n\t\t// Atomic temp+rename — the Vite plugin reads this file with a plain\n\t\t// `readFileSync` + `JSON.parse`, so a torn / partial write would surface\n\t\t// as a parse error. `atomicWriteFile` ensures the parent dir (the\n\t\t// deployment write may run before the manifest write) and fsyncs before\n\t\t// the rename, so readers only ever see a complete file.\n\t\tyield* atomicWriteFile(path, bytes, { mode: 0o644 }).pipe(\n\t\t\tEffect.mapError(\n\t\t\t\t(cause) =>\n\t\t\t\t\tnew DeploymentDecodeError({ source: path, message: 'failed to write deployment', cause }),\n\t\t\t),\n\t\t);\n\t});\n\n/** Project a runtime-decode issue into the typed `DeploymentDecodeError`. */\nconst mkDeploymentError = (issue: RuntimeDecodeIssue): DeploymentDecodeError =>\n\tnew DeploymentDecodeError({\n\t\tsource: issue.source,\n\t\tmessage: issue.message,\n\t\t...(issue.cause === undefined ? {} : { cause: issue.cause }),\n\t});\n\n/** Decode + validate a deployment from already-read JSON text. The single\n * schema-decode seam every reader shares (the Vite plugin, the `dump-deployment`\n * verb, the codegen verb) so the parse-and-validate decision lives in ONE\n * place rather than each caller hand-rolling `JSON.parse`. Throws\n * `DeploymentDecodeError` on malformed JSON or a shape that violates\n * `DeploymentSchema`. */\nexport const decodeDeployment = (text: string): DevstackDeployment =>\n\tdecodeJsonTextSync(DevstackDeploymentSchema, text, {\n\t\tsource: DEPLOYMENT_FILENAME,\n\t\tmkError: mkDeploymentError,\n\t});\n\n/** Decode + validate a single per-network `NetworkDeployment` unit from an\n * already-parsed object (a committed `deployments/<net>.ts` thunk's\n * `deployment`). The Vite plugin's merge layer calls this on each committed\n * thunk so a malformed committed deployment surfaces loudly at config-load\n * (rather than silently injecting a broken network). Throws\n * `DeploymentDecodeError` on a shape that violates `NetworkDeploymentSchema`.\n * The caller overwrites `network`/`local` from the envelope key afterwards,\n * so the unit's own `network` field is optional here. */\nexport const decodeNetworkDeployment = (value: unknown, source: string): NetworkDeployment =>\n\tdecodeUnknownSync(NetworkDeploymentSchema, value, {\n\t\tsource,\n\t\tmkError: mkDeploymentError,\n\t\tmessage: 'committed deployment is not a valid NetworkDeployment',\n\t});\n"],"mappings":"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"}
@@ -21,17 +21,15 @@ var CodegenEmitterCollision = class extends Schema.TaggedErrorClass()("CodegenEm
21
21
  outputPaths: Schema.Array(Schema.String)
22
22
  }) {};
23
23
  /** Two `Codegenable` contributions target the SAME aggregate
24
- * `bucket` but disagree on its routing/sensitivity metadata
25
- * (`outputLocation` or `sensitive`). The `AggregateContribution`
26
- * contract requires all contributors to a bucket to agree; the
27
- * orchestrator routes the synthesized aggregate from the FIRST
28
- * contributor it sees, so a silent disagreement could misroute a
29
- * sensitive aggregate into the committed `generated` tree. Detected
30
- * while folding contributions so the cycle fails fast rather than
31
- * emitting a secret with non-sensitive handling. */
24
+ * `bucket` but disagree on its `sensitive` metadata. The
25
+ * `AggregateContribution` contract requires all contributors to a
26
+ * bucket to agree; the orchestrator takes the FIRST contributor's
27
+ * sensitivity, so a silent disagreement could emit a secret with
28
+ * non-sensitive handling. Detected while folding contributions so the
29
+ * cycle fails fast. */
32
30
  var CodegenAggregateConflict = class extends Schema.TaggedErrorClass()("CodegenAggregateConflict", {
33
31
  bucket: Schema.String,
34
- field: Schema.Literals(["outputLocation", "sensitive"]),
32
+ field: Schema.Literals(["sensitive"]),
35
33
  /** The value the first contributor established for the bucket. */
36
34
  established: Schema.String,
37
35
  /** The conflicting value a later contributor declared. */
@@ -1 +1 @@
1
- {"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/orchestrators/codegen/errors.ts"],"sourcesContent":["// Typed errors for the Codegen orchestrator.\n//\n// A phase-tagged failure-mode taxonomy: \"emitter-collision at acquire\",\n// \"binary shell-out failure\", and \"pure render failure\" each get a\n// separate tag so callers can `catchTag` on each.\n//\n// Phase tags inside each error name the lifecycle step that failed\n// (resolve → emit → render → write → bindings → finalize) so error\n// attribution stays accurate.\n\nimport { Schema } from 'effect';\n\n/** Path-layer rejection from the codegen orchestrator.\n * - `kind: 'duplicate'` — two `Codegenable` contributions claim the\n * same `outputPath`. Hard failure detected BEFORE write so the\n * user-visible output dir never sees an ambiguous overwrite.\n * - `kind: 'non-relative'` — a plugin-authored `outputPath` escapes\n * the codegen root (contains `..` or is absolute). Defense-in-depth\n * for the file-layout invariants. */\nexport class CodegenPathConflict extends Schema.TaggedErrorClass<CodegenPathConflict>()(\n\t'CodegenPathConflict',\n\t{\n\t\tkind: Schema.Literals(['duplicate', 'non-relative']),\n\t\toutputPath: Schema.String,\n\t\temitters: Schema.Array(Schema.String),\n\t},\n) {}\n\n/** Two `Codegenable` contributions share the SAME `emitterName`\n * literal but resolve to different `outputPath`s — programming\n * bug in plugin code (emitter name should be unique). Distinct\n * from `CodegenPathConflict` so attribution stays clean. */\nexport class CodegenEmitterCollision extends Schema.TaggedErrorClass<CodegenEmitterCollision>()(\n\t'CodegenEmitterCollision',\n\t{\n\t\temitterName: Schema.String,\n\t\toutputPaths: Schema.Array(Schema.String),\n\t},\n) {}\n\n/** Two `Codegenable` contributions target the SAME aggregate\n * `bucket` but disagree on its routing/sensitivity metadata\n * (`outputLocation` or `sensitive`). The `AggregateContribution`\n * contract requires all contributors to a bucket to agree; the\n * orchestrator routes the synthesized aggregate from the FIRST\n * contributor it sees, so a silent disagreement could misroute a\n * sensitive aggregate into the committed `generated` tree. Detected\n * while folding contributions so the cycle fails fast rather than\n * emitting a secret with non-sensitive handling. */\nexport class CodegenAggregateConflict extends Schema.TaggedErrorClass<CodegenAggregateConflict>()(\n\t'CodegenAggregateConflict',\n\t{\n\t\tbucket: Schema.String,\n\t\tfield: Schema.Literals(['outputLocation', 'sensitive']),\n\t\t/** The value the first contributor established for the bucket. */\n\t\testablished: Schema.String,\n\t\t/** The conflicting value a later contributor declared. */\n\t\tconflicting: Schema.String,\n\t\t/** Emitter names: the contributor that established the value and\n\t\t * the one that disagreed. */\n\t\temitters: Schema.Array(Schema.String),\n\t},\n) {}\n\n/** A `Codegenable.emit()` wrote a value that the renderer cannot\n * serialise (a function, a symbol, a circular reference, etc.). */\nexport class CodegenRenderError extends Schema.TaggedErrorClass<CodegenRenderError>()(\n\t'CodegenRenderError',\n\t{\n\t\temitterName: Schema.String,\n\t\toutputPath: Schema.String,\n\t\tdetail: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n/** `Codegenable.emit()` raised. Wraps the inner Effect failure\n * rather than propagating it raw so the orchestrator's error\n * channel stays closed. */\nexport class CodegenEmitFailed extends Schema.TaggedErrorClass<CodegenEmitFailed>()(\n\t'CodegenEmitFailed',\n\t{\n\t\temitterName: Schema.String,\n\t\toutputPath: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n/** Disk write (atomic-write or permission-tighten) failed. */\nexport class CodegenWriteFailed extends Schema.TaggedErrorClass<CodegenWriteFailed>()(\n\t'CodegenWriteFailed',\n\t{\n\t\toutputPath: Schema.String,\n\t\tstage: Schema.Literals(['mkdir-parent', 'write', 'rename', 'chmod', 'gitignore']),\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n/** `@mysten/codegen` shell-out (Move-to-TS bindings) failed or\n * silently no-op'd. Distilled-doc § \"Silent no-op from a\n * downstream tool is a failure\" — we surface a hint about the\n * common `Move.toml` cause. */\nexport class CodegenBindingsFailed extends Schema.TaggedErrorClass<CodegenBindingsFailed>()(\n\t'CodegenBindingsFailed',\n\t{\n\t\tpackage: Schema.String,\n\t\tsourcePath: Schema.String,\n\t\treason: Schema.Literals(['summary-failed', 'no-output', 'render-failed', 'write-failed']),\n\t\thint: Schema.optional(Schema.String),\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n/** The reader of the manifest envelope detected drift between\n * what plugins contributed and what's on disk — e.g. a stale\n * envelope from a previous stack lifecycle. */\nexport class CodegenManifestDrift extends Schema.TaggedErrorClass<CodegenManifestDrift>()(\n\t'CodegenManifestDrift',\n\t{\n\t\tdetail: Schema.String,\n\t},\n) {}\n\nexport type CodegenError =\n\t| CodegenPathConflict\n\t| CodegenEmitterCollision\n\t| CodegenAggregateConflict\n\t| CodegenRenderError\n\t| CodegenEmitFailed\n\t| CodegenWriteFailed\n\t| CodegenBindingsFailed\n\t| CodegenManifestDrift;\n"],"mappings":";;;;;;;;;AAmBA,IAAa,sBAAb,cAAyC,OAAO,iBAAsC,CAAC,CACtF,uBACA;CACC,MAAM,OAAO,SAAS,CAAC,aAAa,cAAc,CAAC;CACnD,YAAY,OAAO;CACnB,UAAU,OAAO,MAAM,OAAO,MAAM;AACrC,CACD,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,0BAAb,cAA6C,OAAO,iBAA0C,CAAC,CAC9F,2BACA;CACC,aAAa,OAAO;CACpB,aAAa,OAAO,MAAM,OAAO,MAAM;AACxC,CACD,CAAC,CAAC,CAAC;;;;;;;;;;AAWH,IAAa,2BAAb,cAA8C,OAAO,iBAA2C,CAAC,CAChG,4BACA;CACC,QAAQ,OAAO;CACf,OAAO,OAAO,SAAS,CAAC,kBAAkB,WAAW,CAAC;;CAEtD,aAAa,OAAO;;CAEpB,aAAa,OAAO;;;CAGpB,UAAU,OAAO,MAAM,OAAO,MAAM;AACrC,CACD,CAAC,CAAC,CAAC;;;AAIH,IAAa,qBAAb,cAAwC,OAAO,iBAAqC,CAAC,CACpF,sBACA;CACC,aAAa,OAAO;CACpB,YAAY,OAAO;CACnB,QAAQ,OAAO;CACf,OAAO,OAAO,SAAS,OAAO,MAAM;AACrC,CACD,CAAC,CAAC,CAAC;;;;AAKH,IAAa,oBAAb,cAAuC,OAAO,iBAAoC,CAAC,CAClF,qBACA;CACC,aAAa,OAAO;CACpB,YAAY,OAAO;CACnB,OAAO,OAAO,SAAS,OAAO,MAAM;AACrC,CACD,CAAC,CAAC,CAAC;;AAGH,IAAa,qBAAb,cAAwC,OAAO,iBAAqC,CAAC,CACpF,sBACA;CACC,YAAY,OAAO;CACnB,OAAO,OAAO,SAAS;EAAC;EAAgB;EAAS;EAAU;EAAS;CAAW,CAAC;CAChF,OAAO,OAAO,SAAS,OAAO,MAAM;AACrC,CACD,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,wBAAb,cAA2C,OAAO,iBAAwC,CAAC,CAC1F,yBACA;CACC,SAAS,OAAO;CAChB,YAAY,OAAO;CACnB,QAAQ,OAAO,SAAS;EAAC;EAAkB;EAAa;EAAiB;CAAc,CAAC;CACxF,MAAM,OAAO,SAAS,OAAO,MAAM;CACnC,OAAO,OAAO,SAAS,OAAO,MAAM;AACrC,CACD,CAAC,CAAC,CAAC;AAKuC,OAAO,iBAAuC,CAAC,CACxF,wBACA,EACC,QAAQ,OAAO,OAChB,CACD"}
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/orchestrators/codegen/errors.ts"],"sourcesContent":["// Typed errors for the Codegen orchestrator.\n//\n// A phase-tagged failure-mode taxonomy: \"emitter-collision at acquire\",\n// \"binary shell-out failure\", and \"pure render failure\" each get a\n// separate tag so callers can `catchTag` on each.\n//\n// Phase tags inside each error name the lifecycle step that failed\n// (resolve → emit → render → write → bindings → finalize) so error\n// attribution stays accurate.\n\nimport { Schema } from 'effect';\n\n/** Path-layer rejection from the codegen orchestrator.\n * - `kind: 'duplicate'` — two `Codegenable` contributions claim the\n * same `outputPath`. Hard failure detected BEFORE write so the\n * user-visible output dir never sees an ambiguous overwrite.\n * - `kind: 'non-relative'` — a plugin-authored `outputPath` escapes\n * the codegen root (contains `..` or is absolute). Defense-in-depth\n * for the file-layout invariants. */\nexport class CodegenPathConflict extends Schema.TaggedErrorClass<CodegenPathConflict>()(\n\t'CodegenPathConflict',\n\t{\n\t\tkind: Schema.Literals(['duplicate', 'non-relative']),\n\t\toutputPath: Schema.String,\n\t\temitters: Schema.Array(Schema.String),\n\t},\n) {}\n\n/** Two `Codegenable` contributions share the SAME `emitterName`\n * literal but resolve to different `outputPath`s — programming\n * bug in plugin code (emitter name should be unique). Distinct\n * from `CodegenPathConflict` so attribution stays clean. */\nexport class CodegenEmitterCollision extends Schema.TaggedErrorClass<CodegenEmitterCollision>()(\n\t'CodegenEmitterCollision',\n\t{\n\t\temitterName: Schema.String,\n\t\toutputPaths: Schema.Array(Schema.String),\n\t},\n) {}\n\n/** Two `Codegenable` contributions target the SAME aggregate\n * `bucket` but disagree on its `sensitive` metadata. The\n * `AggregateContribution` contract requires all contributors to a\n * bucket to agree; the orchestrator takes the FIRST contributor's\n * sensitivity, so a silent disagreement could emit a secret with\n * non-sensitive handling. Detected while folding contributions so the\n * cycle fails fast. */\nexport class CodegenAggregateConflict extends Schema.TaggedErrorClass<CodegenAggregateConflict>()(\n\t'CodegenAggregateConflict',\n\t{\n\t\tbucket: Schema.String,\n\t\tfield: Schema.Literals(['sensitive']),\n\t\t/** The value the first contributor established for the bucket. */\n\t\testablished: Schema.String,\n\t\t/** The conflicting value a later contributor declared. */\n\t\tconflicting: Schema.String,\n\t\t/** Emitter names: the contributor that established the value and\n\t\t * the one that disagreed. */\n\t\temitters: Schema.Array(Schema.String),\n\t},\n) {}\n\n/** A `Codegenable.emit()` wrote a value that the renderer cannot\n * serialise (a function, a symbol, a circular reference, etc.). */\nexport class CodegenRenderError extends Schema.TaggedErrorClass<CodegenRenderError>()(\n\t'CodegenRenderError',\n\t{\n\t\temitterName: Schema.String,\n\t\toutputPath: Schema.String,\n\t\tdetail: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n/** `Codegenable.emit()` raised. Wraps the inner Effect failure\n * rather than propagating it raw so the orchestrator's error\n * channel stays closed. */\nexport class CodegenEmitFailed extends Schema.TaggedErrorClass<CodegenEmitFailed>()(\n\t'CodegenEmitFailed',\n\t{\n\t\temitterName: Schema.String,\n\t\toutputPath: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n/** Disk write (atomic-write or permission-tighten) failed. */\nexport class CodegenWriteFailed extends Schema.TaggedErrorClass<CodegenWriteFailed>()(\n\t'CodegenWriteFailed',\n\t{\n\t\toutputPath: Schema.String,\n\t\tstage: Schema.Literals(['mkdir-parent', 'write', 'rename', 'chmod', 'gitignore']),\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n/** `@mysten/codegen` shell-out (Move-to-TS bindings) failed or\n * silently no-op'd. Distilled-doc § \"Silent no-op from a\n * downstream tool is a failure\" — we surface a hint about the\n * common `Move.toml` cause. */\nexport class CodegenBindingsFailed extends Schema.TaggedErrorClass<CodegenBindingsFailed>()(\n\t'CodegenBindingsFailed',\n\t{\n\t\tpackage: Schema.String,\n\t\tsourcePath: Schema.String,\n\t\treason: Schema.Literals(['summary-failed', 'no-output', 'render-failed', 'write-failed']),\n\t\thint: Schema.optional(Schema.String),\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n/** The reader of the manifest envelope detected drift between\n * what plugins contributed and what's on disk — e.g. a stale\n * envelope from a previous stack lifecycle. */\nexport class CodegenManifestDrift extends Schema.TaggedErrorClass<CodegenManifestDrift>()(\n\t'CodegenManifestDrift',\n\t{\n\t\tdetail: Schema.String,\n\t},\n) {}\n\nexport type CodegenError =\n\t| CodegenPathConflict\n\t| CodegenEmitterCollision\n\t| CodegenAggregateConflict\n\t| CodegenRenderError\n\t| CodegenEmitFailed\n\t| CodegenWriteFailed\n\t| CodegenBindingsFailed\n\t| CodegenManifestDrift;\n"],"mappings":";;;;;;;;;AAmBA,IAAa,sBAAb,cAAyC,OAAO,iBAAsC,CAAC,CACtF,uBACA;CACC,MAAM,OAAO,SAAS,CAAC,aAAa,cAAc,CAAC;CACnD,YAAY,OAAO;CACnB,UAAU,OAAO,MAAM,OAAO,MAAM;AACrC,CACD,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,0BAAb,cAA6C,OAAO,iBAA0C,CAAC,CAC9F,2BACA;CACC,aAAa,OAAO;CACpB,aAAa,OAAO,MAAM,OAAO,MAAM;AACxC,CACD,CAAC,CAAC,CAAC;;;;;;;;AASH,IAAa,2BAAb,cAA8C,OAAO,iBAA2C,CAAC,CAChG,4BACA;CACC,QAAQ,OAAO;CACf,OAAO,OAAO,SAAS,CAAC,WAAW,CAAC;;CAEpC,aAAa,OAAO;;CAEpB,aAAa,OAAO;;;CAGpB,UAAU,OAAO,MAAM,OAAO,MAAM;AACrC,CACD,CAAC,CAAC,CAAC;;;AAIH,IAAa,qBAAb,cAAwC,OAAO,iBAAqC,CAAC,CACpF,sBACA;CACC,aAAa,OAAO;CACpB,YAAY,OAAO;CACnB,QAAQ,OAAO;CACf,OAAO,OAAO,SAAS,OAAO,MAAM;AACrC,CACD,CAAC,CAAC,CAAC;;;;AAKH,IAAa,oBAAb,cAAuC,OAAO,iBAAoC,CAAC,CAClF,qBACA;CACC,aAAa,OAAO;CACpB,YAAY,OAAO;CACnB,OAAO,OAAO,SAAS,OAAO,MAAM;AACrC,CACD,CAAC,CAAC,CAAC;;AAGH,IAAa,qBAAb,cAAwC,OAAO,iBAAqC,CAAC,CACpF,sBACA;CACC,YAAY,OAAO;CACnB,OAAO,OAAO,SAAS;EAAC;EAAgB;EAAS;EAAU;EAAS;CAAW,CAAC;CAChF,OAAO,OAAO,SAAS,OAAO,MAAM;AACrC,CACD,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,wBAAb,cAA2C,OAAO,iBAAwC,CAAC,CAC1F,yBACA;CACC,SAAS,OAAO;CAChB,YAAY,OAAO;CACnB,QAAQ,OAAO,SAAS;EAAC;EAAkB;EAAa;EAAiB;CAAc,CAAC;CACxF,MAAM,OAAO,SAAS,OAAO,MAAM;CACnC,OAAO,OAAO,SAAS,OAAO,MAAM;AACrC,CACD,CAAC,CAAC,CAAC;AAKuC,OAAO,iBAAuC,CAAC,CACxF,wBACA,EACC,QAAQ,OAAO,OAChB,CACD"}
@@ -1,5 +1,5 @@
1
1
  import { CodegenRenderError } from "./errors.mjs";
2
- import { isRawExpr } from "../../contracts/codegenable.mjs";
2
+ import { isForNetworkBucket, isRawExpr } from "../../contracts/codegenable.mjs";
3
3
  //#region src/orchestrators/codegen/format.ts
4
4
  const GENERATED_HEADER = [
5
5
  "// THIS FILE IS AUTO-GENERATED BY @mysten/devstack.",
@@ -39,6 +39,10 @@ const renderFile = (input) => {
39
39
  }
40
40
  lines.push("");
41
41
  }
42
+ if (input.preamble && input.preamble.length > 0) {
43
+ for (const stmt of input.preamble) lines.push(stmt);
44
+ lines.push("");
45
+ }
42
46
  const keys = Object.keys(input.exports).sort();
43
47
  for (const key of keys) {
44
48
  const value = input.exports[key];
@@ -67,6 +71,21 @@ const tryRender = (value, seen, depth = 1) => {
67
71
  if (value === null) return tryOk("null");
68
72
  if (value === void 0) return tryErr("undefined is not serialisable — emit null instead");
69
73
  if (isRawExpr(value)) return tryOk(value.expr);
74
+ if (isForNetworkBucket(value)) {
75
+ const inner = tryRender(value.inner, seen, depth + 2);
76
+ if (!inner.ok) return inner;
77
+ const method = INDENT.repeat(depth);
78
+ const body = INDENT.repeat(depth + 1);
79
+ const close = INDENT.repeat(depth - 1);
80
+ const param = value.needsDep ? "network" : "_network";
81
+ const lines = ["{"];
82
+ lines.push(`${method}forNetwork(${param}: string) {`);
83
+ if (value.needsDep) lines.push(`${body}const dep = loadDeployment().forNetwork(network);`);
84
+ lines.push(`${body}return ${inner.text} as const;`);
85
+ lines.push(`${method}},`);
86
+ lines.push(`${close}}`);
87
+ return tryOk(lines.join("\n"));
88
+ }
70
89
  const t = typeof value;
71
90
  if (t === "string") return tryOk(JSON.stringify(value));
72
91
  if (t === "number") {
@@ -1 +1 @@
1
- {"version":3,"file":"format.mjs","names":[],"sources":["../../../src/orchestrators/codegen/format.ts"],"sourcesContent":["// TypeScript source rendering for codegen-emitted files.\n//\n// Distilled-doc § \"Hand-rolled TS-object-literal rendering ... drifts\n// subtly between emitters; a small shared renderer would absorb the\n// variation and own the bigint policy in one place.\" This module IS\n// that shared renderer.\n//\n// Output shape (one emitted file):\n//\n// <generated-header>\n// <do-not-commit-banner if sensitive>\n// <imports?>\n// <one `export const <name> = <literal> as const;` per export>\n// <trailing newline>\n//\n// The renderer:\n// - sorts object keys (deterministic re-emit, distilled-doc\n// invariant \"Deterministic re-emit\"),\n// - quotes identifier-unsafe keys with double-quoted strings,\n// - serialises bigints as quoted decimal strings for\n// `BigInt(<string>)` consumers,\n// - refuses functions / symbols / undefined / circular refs with a\n// typed render error,\n// - emits arrays and records as readonly literal forms.\n//\n// The renderer does NOT format with prettier; it aims for\n// prettier-compatible spacing (two-space indent, trailing commas,\n// double-quoted strings, semicolons) so a prettier-on-write user\n// hook is a no-op on emitted bytes — that's load-bearing for the\n// \"no mtime touch on identical content\" idempotency invariant.\n\nimport { isRawExpr } from '../../contracts/codegenable.ts';\n\nimport { CodegenRenderError } from './errors.ts';\n\n// -----------------------------------------------------------------------------\n// Headers\n// -----------------------------------------------------------------------------\n\nconst GENERATED_HEADER = [\n\t'// THIS FILE IS AUTO-GENERATED BY @mysten/devstack.',\n\t'// Do not edit by hand — your changes will be overwritten on the next',\n\t'// `devstack up` cycle. Apps consume codegen output; codegen output never',\n\t'// imports from devstack.',\n].join('\\n');\n\nconst SENSITIVE_HEADER = [\n\t'// !!! DO NOT COMMIT THIS FILE !!!',\n\t'// This file carries sensitive material (wallet pair token, database',\n\t'// credentials, or user extras). The output directory ships a .gitignore',\n\t'// covering it; this banner is a belt-and-braces reminder.',\n].join('\\n');\n\n// -----------------------------------------------------------------------------\n// Public render entry\n// -----------------------------------------------------------------------------\n\nexport interface RenderInput {\n\treadonly emitterName: string;\n\treadonly outputPath: string;\n\treadonly sensitive: boolean;\n\t/** The declarations written by `Codegenable.emit(ctx)`. Each entry\n\t * becomes one `export const <key> = <literal> as const;`. */\n\treadonly exports: { readonly [key: string]: unknown };\n\t/** Optional import lines the emitter wants prepended (e.g. a\n\t * type import from `@mysten/dapp-kit` for the wallet config).\n\t * Each entry is a raw import statement string. */\n\treadonly imports?: ReadonlyArray<string>;\n}\n\n/** Result of a render attempt. Discriminated so callers can dispatch\n * on `ok` without resorting to `instanceof` checks (STYLE_GUIDE §2). */\nexport type RenderResult =\n\t| { readonly ok: true; readonly text: string }\n\t| { readonly ok: false; readonly error: CodegenRenderError };\n\nconst renderErr = (\n\tinput: RenderInput,\n\tdetail: string,\n): { readonly ok: false; readonly error: CodegenRenderError } => ({\n\tok: false,\n\terror: new CodegenRenderError({\n\t\temitterName: input.emitterName,\n\t\toutputPath: input.outputPath,\n\t\tdetail,\n\t}),\n});\n\n/** Render an emitted file to a TS source string. Returns a discriminated\n * `RenderResult` — `{ok: true, text}` on success, `{ok: false, error}`\n * when the value is unserialisable or imports forbidden paths. */\nexport const renderFile = (input: RenderInput): RenderResult => {\n\tconst lines: Array<string> = [];\n\tlines.push(GENERATED_HEADER);\n\tif (input.sensitive) {\n\t\tlines.push('');\n\t\tlines.push(SENSITIVE_HEADER);\n\t}\n\tlines.push('');\n\tif (input.imports && input.imports.length > 0) {\n\t\tfor (const imp of input.imports) {\n\t\t\tif (isForbiddenGeneratedImport(imp)) {\n\t\t\t\treturn renderErr(input, `generated files must not import devstack source: ${imp}`);\n\t\t\t}\n\t\t\tlines.push(imp);\n\t\t}\n\t\tlines.push('');\n\t}\n\t// Stable export order: keys lexicographically sorted.\n\tconst keys = Object.keys(input.exports).sort();\n\tfor (const key of keys) {\n\t\tconst value = input.exports[key];\n\t\tconst rendered = tryRender(value, new Set());\n\t\tif (!rendered.ok) {\n\t\t\treturn renderErr(input, `export '${key}': ${rendered.detail}`);\n\t\t}\n\t\tif (!isIdentifier(key)) {\n\t\t\treturn renderErr(input, `export key '${key}' is not a valid TS identifier`);\n\t\t}\n\t\tlines.push(`export const ${key} = ${rendered.text} as const;`);\n\t\tlines.push('');\n\t}\n\t// Single trailing newline; never two.\n\twhile (lines.length > 1 && lines[lines.length - 1] === '' && lines[lines.length - 2] === '') {\n\t\tlines.pop();\n\t}\n\treturn {\n\t\tok: true,\n\t\ttext: lines.join('\\n') + (lines[lines.length - 1] === '' ? '' : '\\n'),\n\t};\n};\n\n// -----------------------------------------------------------------------------\n// Value rendering\n// -----------------------------------------------------------------------------\n\nconst INDENT = '\\t';\n\ntype TryRenderResult =\n\t| { readonly ok: true; readonly text: string }\n\t| { readonly ok: false; readonly detail: string };\n\nconst tryOk = (text: string): TryRenderResult => ({ ok: true, text });\nconst tryErr = (detail: string): TryRenderResult => ({ ok: false, detail });\n\nconst tryRender = (value: unknown, seen: Set<object>, depth = 1): TryRenderResult => {\n\tif (value === null) return tryOk('null');\n\tif (value === undefined) return tryErr('undefined is not serialisable — emit null instead');\n\t// Raw-expression escape hatch: a `RawExpr` is emitted VERBATIM (e.g.\n\t// `resolveId(\"@local/foo\")`) instead of as a quoted literal. The single\n\t// authorized use is id resolution in the emitted `config.ts` — ids are\n\t// LOADED CONFIG DATA injected at app build time, not codegen output, so\n\t// the config calls a runtime resolver rather than embedding a literal id.\n\tif (isRawExpr(value)) return tryOk(value.expr);\n\tconst t = typeof value;\n\tif (t === 'string') return tryOk(JSON.stringify(value));\n\tif (t === 'number') {\n\t\tif (!Number.isFinite(value as number)) return tryErr('non-finite number');\n\t\treturn tryOk(String(value));\n\t}\n\tif (t === 'boolean') return tryOk(String(value));\n\tif (t === 'bigint') return tryOk(JSON.stringify(value.toString()));\n\tif (t === 'function' || t === 'symbol') return tryErr(`${t} is not serialisable`);\n\tif (Array.isArray(value)) {\n\t\tif (seen.has(value)) return tryErr('circular reference');\n\t\tseen.add(value);\n\t\tif (value.length === 0) return tryOk('[]');\n\t\tconst inner: Array<string> = [];\n\t\tfor (const item of value) {\n\t\t\tconst r = tryRender(item, seen, depth + 1);\n\t\t\tif (!r.ok) return r;\n\t\t\tinner.push(INDENT.repeat(depth) + r.text);\n\t\t}\n\t\tseen.delete(value);\n\t\treturn tryOk(`[\\n${inner.join(',\\n')},\\n${INDENT.repeat(depth - 1)}]`);\n\t}\n\tif (t === 'object') {\n\t\tconst obj = value as Record<string, unknown>;\n\t\tif (seen.has(obj)) return tryErr('circular reference');\n\t\tseen.add(obj);\n\t\tconst keys = Object.keys(obj).sort();\n\t\tif (keys.length === 0) return tryOk('{}');\n\t\tconst lines: Array<string> = [];\n\t\tfor (const k of keys) {\n\t\t\tconst v = obj[k];\n\t\t\tif (v === undefined) continue; // drop undefined props (distilled-doc skip-emit-explicit)\n\t\t\tconst r = tryRender(v, seen, depth + 1);\n\t\t\tif (!r.ok) return r;\n\t\t\tconst keyText = isIdentifier(k) ? k : JSON.stringify(k);\n\t\t\tlines.push(`${INDENT.repeat(depth)}${keyText}: ${r.text}`);\n\t\t}\n\t\tseen.delete(obj);\n\t\tif (lines.length === 0) return tryOk('{}');\n\t\treturn tryOk(`{\\n${lines.join(',\\n')},\\n${INDENT.repeat(depth - 1)}}`);\n\t}\n\treturn tryErr(`unsupported value type ${t}`);\n};\n\nconst IDENT_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\nconst isIdentifier = (s: string): boolean => IDENT_RE.test(s);\n\nconst isForbiddenGeneratedImport = (line: string): boolean =>\n\t/\\bfrom\\s+[\"']@mysten-incubation\\/devstack(?:-rewrite)?(?:\\/|[\"'])/.test(line) ||\n\t/\\bimport\\s*\\(\\s*[\"']@mysten-incubation\\/devstack(?:-rewrite)?(?:\\/|[\"'])/.test(line) ||\n\t/\\bfrom\\s+[\"'][./][^\"']*\\/src\\//.test(line) ||\n\t/\\bimport\\s*\\(\\s*[\"'][./][^\"']*\\/src\\//.test(line);\n"],"mappings":";;;AAuCA,MAAM,mBAAmB;CACxB;CACA;CACA;CACA;AACD,CAAC,CAAC,KAAK,IAAI;AAEX,MAAM,mBAAmB;CACxB;CACA;CACA;CACA;AACD,CAAC,CAAC,KAAK,IAAI;AAyBX,MAAM,aACL,OACA,YACiE;CACjE,IAAI;CACJ,OAAO,IAAI,mBAAmB;EAC7B,aAAa,MAAM;EACnB,YAAY,MAAM;EAClB;CACD,CAAC;AACF;;;;AAKA,MAAa,cAAc,UAAqC;CAC/D,MAAM,QAAuB,CAAC;CAC9B,MAAM,KAAK,gBAAgB;CAC3B,IAAI,MAAM,WAAW;EACpB,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,gBAAgB;CAC5B;CACA,MAAM,KAAK,EAAE;CACb,IAAI,MAAM,WAAW,MAAM,QAAQ,SAAS,GAAG;EAC9C,KAAK,MAAM,OAAO,MAAM,SAAS;GAChC,IAAI,2BAA2B,GAAG,GACjC,OAAO,UAAU,OAAO,oDAAoD,KAAK;GAElF,MAAM,KAAK,GAAG;EACf;EACA,MAAM,KAAK,EAAE;CACd;CAEA,MAAM,OAAO,OAAO,KAAK,MAAM,OAAO,CAAC,CAAC,KAAK;CAC7C,KAAK,MAAM,OAAO,MAAM;EACvB,MAAM,QAAQ,MAAM,QAAQ;EAC5B,MAAM,WAAW,UAAU,uBAAO,IAAI,IAAI,CAAC;EAC3C,IAAI,CAAC,SAAS,IACb,OAAO,UAAU,OAAO,WAAW,IAAI,KAAK,SAAS,QAAQ;EAE9D,IAAI,CAAC,aAAa,GAAG,GACpB,OAAO,UAAU,OAAO,eAAe,IAAI,+BAA+B;EAE3E,MAAM,KAAK,gBAAgB,IAAI,KAAK,SAAS,KAAK,WAAW;EAC7D,MAAM,KAAK,EAAE;CACd;CAEA,OAAO,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,OAAO,MAAM,MAAM,MAAM,SAAS,OAAO,IACxF,MAAM,IAAI;CAEX,OAAO;EACN,IAAI;EACJ,MAAM,MAAM,KAAK,IAAI,KAAK,MAAM,MAAM,SAAS,OAAO,KAAK,KAAK;CACjE;AACD;AAMA,MAAM,SAAS;AAMf,MAAM,SAAS,UAAmC;CAAE,IAAI;CAAM;AAAK;AACnE,MAAM,UAAU,YAAqC;CAAE,IAAI;CAAO;AAAO;AAEzE,MAAM,aAAa,OAAgB,MAAmB,QAAQ,MAAuB;CACpF,IAAI,UAAU,MAAM,OAAO,MAAM,MAAM;CACvC,IAAI,UAAU,KAAA,GAAW,OAAO,OAAO,mDAAmD;CAM1F,IAAI,UAAU,KAAK,GAAG,OAAO,MAAM,MAAM,IAAI;CAC7C,MAAM,IAAI,OAAO;CACjB,IAAI,MAAM,UAAU,OAAO,MAAM,KAAK,UAAU,KAAK,CAAC;CACtD,IAAI,MAAM,UAAU;EACnB,IAAI,CAAC,OAAO,SAAS,KAAe,GAAG,OAAO,OAAO,mBAAmB;EACxE,OAAO,MAAM,OAAO,KAAK,CAAC;CAC3B;CACA,IAAI,MAAM,WAAW,OAAO,MAAM,OAAO,KAAK,CAAC;CAC/C,IAAI,MAAM,UAAU,OAAO,MAAM,KAAK,UAAU,MAAM,SAAS,CAAC,CAAC;CACjE,IAAI,MAAM,cAAc,MAAM,UAAU,OAAO,OAAO,GAAG,EAAE,qBAAqB;CAChF,IAAI,MAAM,QAAQ,KAAK,GAAG;EACzB,IAAI,KAAK,IAAI,KAAK,GAAG,OAAO,OAAO,oBAAoB;EACvD,KAAK,IAAI,KAAK;EACd,IAAI,MAAM,WAAW,GAAG,OAAO,MAAM,IAAI;EACzC,MAAM,QAAuB,CAAC;EAC9B,KAAK,MAAM,QAAQ,OAAO;GACzB,MAAM,IAAI,UAAU,MAAM,MAAM,QAAQ,CAAC;GACzC,IAAI,CAAC,EAAE,IAAI,OAAO;GAClB,MAAM,KAAK,OAAO,OAAO,KAAK,IAAI,EAAE,IAAI;EACzC;EACA,KAAK,OAAO,KAAK;EACjB,OAAO,MAAM,MAAM,MAAM,KAAK,KAAK,EAAE,KAAK,OAAO,OAAO,QAAQ,CAAC,EAAE,EAAE;CACtE;CACA,IAAI,MAAM,UAAU;EACnB,MAAM,MAAM;EACZ,IAAI,KAAK,IAAI,GAAG,GAAG,OAAO,OAAO,oBAAoB;EACrD,KAAK,IAAI,GAAG;EACZ,MAAM,OAAO,OAAO,KAAK,GAAG,CAAC,CAAC,KAAK;EACnC,IAAI,KAAK,WAAW,GAAG,OAAO,MAAM,IAAI;EACxC,MAAM,QAAuB,CAAC;EAC9B,KAAK,MAAM,KAAK,MAAM;GACrB,MAAM,IAAI,IAAI;GACd,IAAI,MAAM,KAAA,GAAW;GACrB,MAAM,IAAI,UAAU,GAAG,MAAM,QAAQ,CAAC;GACtC,IAAI,CAAC,EAAE,IAAI,OAAO;GAClB,MAAM,UAAU,aAAa,CAAC,IAAI,IAAI,KAAK,UAAU,CAAC;GACtD,MAAM,KAAK,GAAG,OAAO,OAAO,KAAK,IAAI,QAAQ,IAAI,EAAE,MAAM;EAC1D;EACA,KAAK,OAAO,GAAG;EACf,IAAI,MAAM,WAAW,GAAG,OAAO,MAAM,IAAI;EACzC,OAAO,MAAM,MAAM,MAAM,KAAK,KAAK,EAAE,KAAK,OAAO,OAAO,QAAQ,CAAC,EAAE,EAAE;CACtE;CACA,OAAO,OAAO,0BAA0B,GAAG;AAC5C;AAEA,MAAM,WAAW;AACjB,MAAM,gBAAgB,MAAuB,SAAS,KAAK,CAAC;AAE5D,MAAM,8BAA8B,SACnC,oEAAoE,KAAK,IAAI,KAC7E,2EAA2E,KAAK,IAAI,KACpF,iCAAiC,KAAK,IAAI,KAC1C,wCAAwC,KAAK,IAAI"}
1
+ {"version":3,"file":"format.mjs","names":[],"sources":["../../../src/orchestrators/codegen/format.ts"],"sourcesContent":["// TypeScript source rendering for codegen-emitted files.\n//\n// Distilled-doc § \"Hand-rolled TS-object-literal rendering ... drifts\n// subtly between emitters; a small shared renderer would absorb the\n// variation and own the bigint policy in one place.\" This module IS\n// that shared renderer.\n//\n// Output shape (one emitted file):\n//\n// <generated-header>\n// <do-not-commit-banner if sensitive>\n// <imports?>\n// <one `export const <name> = <literal> as const;` per export>\n// <trailing newline>\n//\n// The renderer:\n// - sorts object keys (deterministic re-emit, distilled-doc\n// invariant \"Deterministic re-emit\"),\n// - quotes identifier-unsafe keys with double-quoted strings,\n// - serialises bigints as quoted decimal strings for\n// `BigInt(<string>)` consumers,\n// - refuses functions / symbols / undefined / circular refs with a\n// typed render error,\n// - emits arrays and records as readonly literal forms.\n//\n// The renderer does NOT format with prettier; it aims for\n// prettier-compatible spacing (two-space indent, trailing commas,\n// double-quoted strings, semicolons) so a prettier-on-write user\n// hook is a no-op on emitted bytes — that's load-bearing for the\n// \"no mtime touch on identical content\" idempotency invariant.\n\nimport { isForNetworkBucket, isRawExpr } from '../../contracts/codegenable.ts';\n\nimport { CodegenRenderError } from './errors.ts';\n\n// -----------------------------------------------------------------------------\n// Headers\n// -----------------------------------------------------------------------------\n\nconst GENERATED_HEADER = [\n\t'// THIS FILE IS AUTO-GENERATED BY @mysten/devstack.',\n\t'// Do not edit by hand — your changes will be overwritten on the next',\n\t'// `devstack up` cycle. Apps consume codegen output; codegen output never',\n\t'// imports from devstack.',\n].join('\\n');\n\nconst SENSITIVE_HEADER = [\n\t'// !!! DO NOT COMMIT THIS FILE !!!',\n\t'// This file carries sensitive material (wallet pair token, database',\n\t'// credentials, or user extras). The output directory ships a .gitignore',\n\t'// covering it; this banner is a belt-and-braces reminder.',\n].join('\\n');\n\n// -----------------------------------------------------------------------------\n// Public render entry\n// -----------------------------------------------------------------------------\n\nexport interface RenderInput {\n\treadonly emitterName: string;\n\treadonly outputPath: string;\n\treadonly sensitive: boolean;\n\t/** The declarations written by `Codegenable.emit(ctx)`. Each entry\n\t * becomes one `export const <key> = <literal> as const;`. */\n\treadonly exports: { readonly [key: string]: unknown };\n\t/** Optional import lines the emitter wants prepended (e.g. a\n\t * type import from `@mysten/dapp-kit` for the wallet config).\n\t * Each entry is a raw import statement string. */\n\treadonly imports?: ReadonlyArray<string>;\n\t/** Optional module-level statements emitted AFTER the imports block and\n\t * BEFORE the exports (e.g. the deployment preamble\n\t * `const __deployment = loadDeployment();` /\n\t * `const dep = __deployment.forNetwork(__deployment.defaultNetwork);` the\n\t * committed `config.ts` + service buckets share). One line per entry,\n\t * followed by a single blank line. Literal-only buckets pass no preamble. */\n\treadonly preamble?: ReadonlyArray<string>;\n}\n\n/** Result of a render attempt. Discriminated so callers can dispatch\n * on `ok` without resorting to `instanceof` checks (STYLE_GUIDE §2). */\nexport type RenderResult =\n\t| { readonly ok: true; readonly text: string }\n\t| { readonly ok: false; readonly error: CodegenRenderError };\n\nconst renderErr = (\n\tinput: RenderInput,\n\tdetail: string,\n): { readonly ok: false; readonly error: CodegenRenderError } => ({\n\tok: false,\n\terror: new CodegenRenderError({\n\t\temitterName: input.emitterName,\n\t\toutputPath: input.outputPath,\n\t\tdetail,\n\t}),\n});\n\n/** Render an emitted file to a TS source string. Returns a discriminated\n * `RenderResult` — `{ok: true, text}` on success, `{ok: false, error}`\n * when the value is unserialisable or imports forbidden paths. */\nexport const renderFile = (input: RenderInput): RenderResult => {\n\tconst lines: Array<string> = [];\n\tlines.push(GENERATED_HEADER);\n\tif (input.sensitive) {\n\t\tlines.push('');\n\t\tlines.push(SENSITIVE_HEADER);\n\t}\n\tlines.push('');\n\tif (input.imports && input.imports.length > 0) {\n\t\tfor (const imp of input.imports) {\n\t\t\tif (isForbiddenGeneratedImport(imp)) {\n\t\t\t\treturn renderErr(input, `generated files must not import devstack source: ${imp}`);\n\t\t\t}\n\t\t\tlines.push(imp);\n\t\t}\n\t\tlines.push('');\n\t}\n\tif (input.preamble && input.preamble.length > 0) {\n\t\tfor (const stmt of input.preamble) {\n\t\t\tlines.push(stmt);\n\t\t}\n\t\tlines.push('');\n\t}\n\t// Stable export order: keys lexicographically sorted.\n\tconst keys = Object.keys(input.exports).sort();\n\tfor (const key of keys) {\n\t\tconst value = input.exports[key];\n\t\tconst rendered = tryRender(value, new Set());\n\t\tif (!rendered.ok) {\n\t\t\treturn renderErr(input, `export '${key}': ${rendered.detail}`);\n\t\t}\n\t\tif (!isIdentifier(key)) {\n\t\t\treturn renderErr(input, `export key '${key}' is not a valid TS identifier`);\n\t\t}\n\t\tlines.push(`export const ${key} = ${rendered.text} as const;`);\n\t\tlines.push('');\n\t}\n\t// Single trailing newline; never two.\n\twhile (lines.length > 1 && lines[lines.length - 1] === '' && lines[lines.length - 2] === '') {\n\t\tlines.pop();\n\t}\n\treturn {\n\t\tok: true,\n\t\ttext: lines.join('\\n') + (lines[lines.length - 1] === '' ? '' : '\\n'),\n\t};\n};\n\n// -----------------------------------------------------------------------------\n// Value rendering\n// -----------------------------------------------------------------------------\n\nconst INDENT = '\\t';\n\ntype TryRenderResult =\n\t| { readonly ok: true; readonly text: string }\n\t| { readonly ok: false; readonly detail: string };\n\nconst tryOk = (text: string): TryRenderResult => ({ ok: true, text });\nconst tryErr = (detail: string): TryRenderResult => ({ ok: false, detail });\n\nconst tryRender = (value: unknown, seen: Set<object>, depth = 1): TryRenderResult => {\n\tif (value === null) return tryOk('null');\n\tif (value === undefined) return tryErr('undefined is not serialisable — emit null instead');\n\t// Raw-expression escape hatch: a `RawExpr` is emitted VERBATIM (e.g.\n\t// `resolveId(\"@local/foo\")`) instead of as a quoted literal. The single\n\t// authorized use is id resolution in the emitted `config.ts` — ids are\n\t// LOADED CONFIG DATA injected at app build time, not codegen output, so\n\t// the config calls a runtime resolver rather than embedding a literal id.\n\tif (isRawExpr(value)) return tryOk(value.expr);\n\t// Per-network service bucket: emit a `{ forNetwork(network) { … } }`\n\t// accessor whose body resolves `dep` for the requested network and returns\n\t// the inner bucket object. The inner object carries the SAME literals +\n\t// `requireValue(dep, …)` raw expressions as before; `dep` is now the\n\t// accessor's per-call local rather than a module-level default-network\n\t// constant, so a runtime `switchNetwork` flips service ids in lockstep.\n\tif (isForNetworkBucket(value)) {\n\t\t// Mirror the object-literal indentation convention: the `forNetwork`\n\t\t// method sits at `depth`, its body at `depth + 1`, the closing brace at\n\t\t// `depth - 1`. The inner bucket object renders one level deeper than the\n\t\t// `return` it follows.\n\t\tconst inner = tryRender(value.inner, seen, depth + 2);\n\t\tif (!inner.ok) return inner;\n\t\tconst method = INDENT.repeat(depth);\n\t\tconst body = INDENT.repeat(depth + 1);\n\t\tconst close = INDENT.repeat(depth - 1);\n\t\t// A pure-literal bucket (e.g. a known/live seal, builtin-only coin) does\n\t\t// not reference `dep`; prefix the param with `_` so it reads as\n\t\t// intentionally-unused and omit the `const dep = …` line.\n\t\tconst param = value.needsDep ? 'network' : '_network';\n\t\tconst lines: Array<string> = ['{'];\n\t\tlines.push(`${method}forNetwork(${param}: string) {`);\n\t\tif (value.needsDep) {\n\t\t\tlines.push(`${body}const dep = loadDeployment().forNetwork(network);`);\n\t\t}\n\t\tlines.push(`${body}return ${inner.text} as const;`);\n\t\tlines.push(`${method}},`);\n\t\tlines.push(`${close}}`);\n\t\treturn tryOk(lines.join('\\n'));\n\t}\n\tconst t = typeof value;\n\tif (t === 'string') return tryOk(JSON.stringify(value));\n\tif (t === 'number') {\n\t\tif (!Number.isFinite(value as number)) return tryErr('non-finite number');\n\t\treturn tryOk(String(value));\n\t}\n\tif (t === 'boolean') return tryOk(String(value));\n\tif (t === 'bigint') return tryOk(JSON.stringify(value.toString()));\n\tif (t === 'function' || t === 'symbol') return tryErr(`${t} is not serialisable`);\n\tif (Array.isArray(value)) {\n\t\tif (seen.has(value)) return tryErr('circular reference');\n\t\tseen.add(value);\n\t\tif (value.length === 0) return tryOk('[]');\n\t\tconst inner: Array<string> = [];\n\t\tfor (const item of value) {\n\t\t\tconst r = tryRender(item, seen, depth + 1);\n\t\t\tif (!r.ok) return r;\n\t\t\tinner.push(INDENT.repeat(depth) + r.text);\n\t\t}\n\t\tseen.delete(value);\n\t\treturn tryOk(`[\\n${inner.join(',\\n')},\\n${INDENT.repeat(depth - 1)}]`);\n\t}\n\tif (t === 'object') {\n\t\tconst obj = value as Record<string, unknown>;\n\t\tif (seen.has(obj)) return tryErr('circular reference');\n\t\tseen.add(obj);\n\t\tconst keys = Object.keys(obj).sort();\n\t\tif (keys.length === 0) return tryOk('{}');\n\t\tconst lines: Array<string> = [];\n\t\tfor (const k of keys) {\n\t\t\tconst v = obj[k];\n\t\t\tif (v === undefined) continue; // drop undefined props (distilled-doc skip-emit-explicit)\n\t\t\tconst r = tryRender(v, seen, depth + 1);\n\t\t\tif (!r.ok) return r;\n\t\t\tconst keyText = isIdentifier(k) ? k : JSON.stringify(k);\n\t\t\tlines.push(`${INDENT.repeat(depth)}${keyText}: ${r.text}`);\n\t\t}\n\t\tseen.delete(obj);\n\t\tif (lines.length === 0) return tryOk('{}');\n\t\treturn tryOk(`{\\n${lines.join(',\\n')},\\n${INDENT.repeat(depth - 1)}}`);\n\t}\n\treturn tryErr(`unsupported value type ${t}`);\n};\n\nconst IDENT_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\nconst isIdentifier = (s: string): boolean => IDENT_RE.test(s);\n\nconst isForbiddenGeneratedImport = (line: string): boolean =>\n\t/\\bfrom\\s+[\"']@mysten-incubation\\/devstack(?:-rewrite)?(?:\\/|[\"'])/.test(line) ||\n\t/\\bimport\\s*\\(\\s*[\"']@mysten-incubation\\/devstack(?:-rewrite)?(?:\\/|[\"'])/.test(line) ||\n\t/\\bfrom\\s+[\"'][./][^\"']*\\/src\\//.test(line) ||\n\t/\\bimport\\s*\\(\\s*[\"'][./][^\"']*\\/src\\//.test(line);\n"],"mappings":"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"}
@@ -33,12 +33,7 @@ const layerCodegenPaths = Layer.effect(CodegenPathsService, Effect.gen(function*
33
33
  const root = yield* CodegenRoot;
34
34
  const path = yield* Path.Path;
35
35
  const outputDir = root.stackSubdir ? path.join(root.outputDir, root.stackSubdir) : root.outputDir;
36
- const extrasDir = root.extrasDir;
37
- const codegenLockFile = `${extrasDir}.codegen.lock`;
38
- const resolveExtras = (outputPath) => Effect.gen(function* () {
39
- yield* assertRelativeCodegenOutputPath(outputPath);
40
- return path.join(extrasDir, outputPath);
41
- });
36
+ const codegenLockFile = `${outputDir}.codegen.lock`;
42
37
  const buildAt = (atRoot) => {
43
38
  const bindingsDir = path.join(atRoot, "bindings");
44
39
  const resolve = (outputPath) => Effect.gen(function* () {
@@ -51,9 +46,7 @@ const layerCodegenPaths = Layer.effect(CodegenPathsService, Effect.gen(function*
51
46
  gitignoreFile: path.join(atRoot, ".gitignore"),
52
47
  bindingsDir,
53
48
  codegenLockFile,
54
- extrasDir,
55
49
  resolve,
56
- resolveExtras,
57
50
  resolveBindingsPackage,
58
51
  withRoot: (newRoot) => buildAt(newRoot)
59
52
  };
@@ -1 +1 @@
1
- {"version":3,"file":"paths.mjs","names":[],"sources":["../../../src/orchestrators/codegen/paths.ts"],"sourcesContent":["// Codegen output-path layout.\n//\n// Distilled-doc § \"All outputs land under a single user-chosen\n// directory (default `./src/generated/`)\" + §\"Stable output paths\".\n// One file owns the layout so emitters never assemble paths by hand.\n//\n// Default root is `<appRoot>/src/generated/`. Tests and parallel\n// stacks pin a per-test root; the substrate's `RuntimeRoot` is for\n// engine-private state (~/.devstack/...), NOT for codegen output.\n// Codegen output lives in the USER's source tree.\n\nimport { Context, Effect, Layer, Path } from 'effect';\n\nimport { CodegenPathConflict } from './errors.ts';\n\n/** Guard plugin-authored `CodegenableDecl.outputPath` against `..`\n * traversal and absolute paths. Fails with a typed\n * `CodegenPathConflict({kind:'non-relative'})` — defense-in-depth\n * for the file-layout invariants. Called once inside `buildAt` so\n * both the root bundle and any `withRoot`-rebased view enforce the\n * same `..`-rejecting `resolve` discipline.\n *\n * POSIX-only: devstack runs on POSIX filesystems (substrate paths\n * are POSIX-shaped throughout), so this check intentionally inspects\n * only `/`-rooted absolutes and the `..` substring. A Windows-style\n * `'foo/bar\\\\..\\\\baz'` would slip past — that's accepted because\n * devstack never executes on a Windows runtime.\n *\n * STYLE_GUIDE §2 rule 5 — orchestrator failures are typed. */\nexport const assertRelativeCodegenOutputPath = (\n\toutputPath: string,\n): Effect.Effect<void, CodegenPathConflict> =>\n\toutputPath.includes('..') || outputPath.startsWith('/')\n\t\t? Effect.fail(\n\t\t\t\tnew CodegenPathConflict({\n\t\t\t\t\tkind: 'non-relative',\n\t\t\t\t\toutputPath,\n\t\t\t\t\temitters: [],\n\t\t\t\t}),\n\t\t\t)\n\t\t: Effect.void;\n\n/**\n * Codegen output root — the directory codegen owns and overwrites.\n * Pinned at boot time from the user's `defineDevstack` options\n * (default `./src/generated/` resolved against the app's cwd).\n *\n * Held as a service so emitters can yield it without a function\n * argument; mirrors the substrate's `RuntimeRoot` pattern.\n */\nexport interface CodegenRootShape {\n\t/** Absolute path to the output directory. The orchestrator\n\t * manages it; the user never writes here. */\n\treadonly outputDir: string;\n\t/** Optional per-stack subdirectory under `outputDir`. When set,\n\t * parallel stacks emit into sibling directories under the same\n\t * output root (distilled-doc §\"per-stack subdirectory\"). */\n\treadonly stackSubdir: string | null;\n\t/** Absolute path to the dev-only + secret `generated-extras` tree\n\t * (`.devstack/stacks/<stack>/generated-extras`). Decls / aggregates\n\t * with `outputLocation: 'generated-extras'` emit here. Outside the\n\t * staging-and-swap tree of `outputDir` — extras are gitignored and\n\t * written in place (no atomic swap), so warm restarts never churn\n\t * the runtime tree's mtimes. */\n\treadonly extrasDir: string;\n}\n\nexport class CodegenRoot extends Context.Service<CodegenRoot, CodegenRootShape>()(\n\t'@devstack/orchestrator/CodegenRoot',\n) {}\n\n/** Build a `CodegenRoot` layer pinned to a literal path. */\nexport const layerCodegenRoot = (root: CodegenRootShape): Layer.Layer<CodegenRoot> =>\n\tLayer.succeed(CodegenRoot)(root);\n\n/** Closed bundle the resolver returns. Mirrors substrate `StackPaths`. */\nexport interface CodegenPaths {\n\t/** The directory the orchestrator emits into (after applying\n\t * per-stack subdirectory if configured). The watcher MUST\n\t * exclude this directory. */\n\treadonly outputDir: string;\n\t/** Path to the `.gitignore` inside `outputDir`. Written every\n\t * emit (the dir is gitignored). */\n\treadonly gitignoreFile: string;\n\t/** Subtree where Move-to-TS bindings land. */\n\treadonly bindingsDir: string;\n\t/** Per-process lock file gating `runEmitCycle`. Sibling to\n\t * `outputDir` so concurrent invocations (e.g. CLI direct-callers\n\t * racing the supervisor) serialize cleanly without blocking the\n\t * short-section substrate `stack.lock`. Codegen cycles can be\n\t * file-system heavy (multi-emitter, Move-bindings compilation);\n\t * the substrate lock is reserved for short sections per the\n\t * cross-process safety protocol. */\n\treadonly codegenLockFile: string;\n\t/** The dev-only + secret `generated-extras` tree. Decls /\n\t * aggregates with `outputLocation: 'generated-extras'` resolve\n\t * against this. Preserved verbatim across `withRoot` — the extras\n\t * tree lives OUTSIDE the staging-and-swap of `outputDir`, so the\n\t * staging rebase must still name the real extras dir. */\n\treadonly extrasDir: string;\n\t/** Helper: resolve an emitter's `outputPath` (e.g. `config.ts`)\n\t * against the output root. Fails with `CodegenPathConflict({kind:\n\t * 'non-relative'})` if the supplied path escapes the root. */\n\treadonly resolve: (outputPath: string) => Effect.Effect<string, CodegenPathConflict>;\n\t/** Helper: resolve an emitter's `outputPath` against the\n\t * `generated-extras` tree (`extrasDir`). Same `..`-rejecting\n\t * discipline as `resolve`. */\n\treadonly resolveExtras: (outputPath: string) => Effect.Effect<string, CodegenPathConflict>;\n\t/** Helper: resolve a per-package bindings subtree path. */\n\treadonly resolveBindingsPackage: (packageName: string) => string;\n\t/** Data-driven rebase: re-root the entire bundle at `newRoot`.\n\t * Preserves the layout (bindings under `<root>/bindings`, gitignore\n\t * at `<root>/.gitignore`) and the `..`-rejecting `resolve`\n\t * discipline; only the prefix changes. Used by the stage-and-swap\n\t * build to redirect the emit pipeline at the staging directory\n\t * WITHOUT string-surgery in the caller.\n\t *\n\t * Note: `codegenLockFile` is preserved verbatim — the lock is\n\t * acquired ONCE outside the staging build, so the rebased view\n\t * must still name the real lock path. */\n\treadonly withRoot: (newRoot: string) => CodegenPaths;\n}\n\nexport class CodegenPathsService extends Context.Service<CodegenPathsService, CodegenPaths>()(\n\t'@devstack/orchestrator/CodegenPaths',\n) {}\n\n/**\n * Materialize the resolver from `CodegenRoot` + Effect's `Path`\n * service. Stack subdir, if present, is appended:\n * `<outputDir>` or `<outputDir>/<stackSubdir>`.\n */\nexport const layerCodegenPaths: Layer.Layer<CodegenPathsService, never, CodegenRoot | Path.Path> =\n\tLayer.effect(\n\t\tCodegenPathsService,\n\t\tEffect.gen(function* () {\n\t\t\tconst root = yield* CodegenRoot;\n\t\t\tconst path = yield* Path.Path;\n\t\t\tconst outputDir = root.stackSubdir\n\t\t\t\t? path.join(root.outputDir, root.stackSubdir)\n\t\t\t\t: root.outputDir;\n\t\t\t// Captured once at boot. Preserved verbatim through `withRoot`\n\t\t\t// (the extras tree is OUTSIDE the staging swap of `outputDir`,\n\t\t\t// so the rebased view must keep naming the real extras dir).\n\t\t\tconst extrasDir = root.extrasDir;\n\t\t\t// Lock the codegen subsystem on a sibling of `extrasDir`, NOT of\n\t\t\t// `outputDir`. At BOOT nothing is ever emitted into the live\n\t\t\t// `outputDir` (`generated`) tree — boot's only codegen write is\n\t\t\t// `emitExtras`, which lands in `extrasDir` — so deriving the lock\n\t\t\t// from `extrasDir` keeps the live `generated` basename entirely\n\t\t\t// out of the boot footprint (no phantom `…/generated.codegen.lock`\n\t\t\t// next to a directory that is never created). The `codegen` verb\n\t\t\t// (the one path that DOES stage into `<outputDir>.staging.<id>`)\n\t\t\t// still gets a stable sibling lock — `<extrasDir>.codegen.lock` —\n\t\t\t// outside both its `outputDir` swap and the `extrasDir` tree.\n\t\t\t// Sibling-NOT-inside is what matters for the stage-and-swap rename\n\t\t\t// of `outputDir`, and an `extrasDir` sibling satisfies that.\n\t\t\t// Captured once at boot — `withRoot` re-roots the rest of the\n\t\t\t// bundle but preserves this lock path verbatim so the rebased\n\t\t\t// staging view names the real cross-process lock (the lock is\n\t\t\t// acquired ONCE outside the staging build).\n\t\t\tconst codegenLockFile = `${extrasDir}.codegen.lock`;\n\t\t\tconst resolveExtras = (outputPath: string): Effect.Effect<string, CodegenPathConflict> =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* assertRelativeCodegenOutputPath(outputPath);\n\t\t\t\t\treturn path.join(extrasDir, outputPath);\n\t\t\t\t});\n\t\t\tconst buildAt = (atRoot: string): CodegenPaths => {\n\t\t\t\tconst bindingsDir = path.join(atRoot, 'bindings');\n\t\t\t\tconst resolve = (outputPath: string): Effect.Effect<string, CodegenPathConflict> =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tyield* assertRelativeCodegenOutputPath(outputPath);\n\t\t\t\t\t\treturn path.join(atRoot, outputPath);\n\t\t\t\t\t});\n\t\t\t\tconst resolveBindingsPackage = (packageName: string): string =>\n\t\t\t\t\tpath.join(bindingsDir, packageName);\n\t\t\t\tconst bundle: CodegenPaths = {\n\t\t\t\t\toutputDir: atRoot,\n\t\t\t\t\tgitignoreFile: path.join(atRoot, '.gitignore'),\n\t\t\t\t\tbindingsDir,\n\t\t\t\t\tcodegenLockFile,\n\t\t\t\t\textrasDir,\n\t\t\t\t\tresolve,\n\t\t\t\t\tresolveExtras,\n\t\t\t\t\tresolveBindingsPackage,\n\t\t\t\t\twithRoot: (newRoot: string) => buildAt(newRoot),\n\t\t\t\t};\n\t\t\t\treturn bundle;\n\t\t\t};\n\t\t\treturn CodegenPathsService.of(buildAt(outputDir));\n\t\t}),\n\t);\n"],"mappings":";;;;;;;;;;;;;;;;;AA6BA,MAAa,mCACZ,eAEA,WAAW,SAAS,IAAI,KAAK,WAAW,WAAW,GAAG,IACnD,OAAO,KACP,IAAI,oBAAoB;CACvB,MAAM;CACN;CACA,UAAU,CAAC;AACZ,CAAC,CACF,IACC,OAAO;AA2BX,IAAa,cAAb,cAAiC,QAAQ,QAAuC,CAAC,CAChF,oCACD,CAAC,CAAC,CAAC;;AAGH,MAAa,oBAAoB,SAChC,MAAM,QAAQ,WAAW,CAAC,CAAC,IAAI;AAkDhC,IAAa,sBAAb,cAAyC,QAAQ,QAA2C,CAAC,CAC5F,qCACD,CAAC,CAAC,CAAC;;;;;;AAOH,MAAa,oBACZ,MAAM,OACL,qBACA,OAAO,IAAI,aAAa;CACvB,MAAM,OAAO,OAAO;CACpB,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,YAAY,KAAK,cACpB,KAAK,KAAK,KAAK,WAAW,KAAK,WAAW,IAC1C,KAAK;CAIR,MAAM,YAAY,KAAK;CAiBvB,MAAM,kBAAkB,GAAG,UAAU;CACrC,MAAM,iBAAiB,eACtB,OAAO,IAAI,aAAa;EACvB,OAAO,gCAAgC,UAAU;EACjD,OAAO,KAAK,KAAK,WAAW,UAAU;CACvC,CAAC;CACF,MAAM,WAAW,WAAiC;EACjD,MAAM,cAAc,KAAK,KAAK,QAAQ,UAAU;EAChD,MAAM,WAAW,eAChB,OAAO,IAAI,aAAa;GACvB,OAAO,gCAAgC,UAAU;GACjD,OAAO,KAAK,KAAK,QAAQ,UAAU;EACpC,CAAC;EACF,MAAM,0BAA0B,gBAC/B,KAAK,KAAK,aAAa,WAAW;EAYnC,OAAO;GAVN,WAAW;GACX,eAAe,KAAK,KAAK,QAAQ,YAAY;GAC7C;GACA;GACA;GACA;GACA;GACA;GACA,WAAW,YAAoB,QAAQ,OAAO;EAEnC;CACb;CACA,OAAO,oBAAoB,GAAG,QAAQ,SAAS,CAAC;AACjD,CAAC,CACF"}
1
+ {"version":3,"file":"paths.mjs","names":[],"sources":["../../../src/orchestrators/codegen/paths.ts"],"sourcesContent":["// Codegen output-path layout.\n//\n// Distilled-doc § \"All outputs land under a single user-chosen\n// directory (default `./src/generated/`)\" + §\"Stable output paths\".\n// One file owns the layout so emitters never assemble paths by hand.\n//\n// Default root is `<appRoot>/src/generated/`. Tests and parallel\n// stacks pin a per-test root; the substrate's `RuntimeRoot` is for\n// engine-private state (~/.devstack/...), NOT for codegen output.\n// Codegen output lives in the USER's source tree.\n\nimport { Context, Effect, Layer, Path } from 'effect';\n\nimport { CodegenPathConflict } from './errors.ts';\n\n/** Guard plugin-authored `CodegenableDecl.outputPath` against `..`\n * traversal and absolute paths. Fails with a typed\n * `CodegenPathConflict({kind:'non-relative'})` — defense-in-depth\n * for the file-layout invariants. Called once inside `buildAt` so\n * both the root bundle and any `withRoot`-rebased view enforce the\n * same `..`-rejecting `resolve` discipline.\n *\n * POSIX-only: devstack runs on POSIX filesystems (substrate paths\n * are POSIX-shaped throughout), so this check intentionally inspects\n * only `/`-rooted absolutes and the `..` substring. A Windows-style\n * `'foo/bar\\\\..\\\\baz'` would slip past — that's accepted because\n * devstack never executes on a Windows runtime.\n *\n * STYLE_GUIDE §2 rule 5 — orchestrator failures are typed. */\nexport const assertRelativeCodegenOutputPath = (\n\toutputPath: string,\n): Effect.Effect<void, CodegenPathConflict> =>\n\toutputPath.includes('..') || outputPath.startsWith('/')\n\t\t? Effect.fail(\n\t\t\t\tnew CodegenPathConflict({\n\t\t\t\t\tkind: 'non-relative',\n\t\t\t\t\toutputPath,\n\t\t\t\t\temitters: [],\n\t\t\t\t}),\n\t\t\t)\n\t\t: Effect.void;\n\n/**\n * Codegen output root — the directory codegen owns and overwrites.\n * Pinned at boot time from the user's `defineDevstack` options\n * (default `./src/generated/` resolved against the app's cwd).\n *\n * Held as a service so emitters can yield it without a function\n * argument; mirrors the substrate's `RuntimeRoot` pattern.\n */\nexport interface CodegenRootShape {\n\t/** Absolute path to the output directory. The orchestrator\n\t * manages it; the user never writes here. */\n\treadonly outputDir: string;\n\t/** Optional per-stack subdirectory under `outputDir`. When set,\n\t * parallel stacks emit into sibling directories under the same\n\t * output root (distilled-doc §\"per-stack subdirectory\"). */\n\treadonly stackSubdir: string | null;\n}\n\nexport class CodegenRoot extends Context.Service<CodegenRoot, CodegenRootShape>()(\n\t'@devstack/orchestrator/CodegenRoot',\n) {}\n\n/** Build a `CodegenRoot` layer pinned to a literal path. */\nexport const layerCodegenRoot = (root: CodegenRootShape): Layer.Layer<CodegenRoot> =>\n\tLayer.succeed(CodegenRoot)(root);\n\n/** Closed bundle the resolver returns. Mirrors substrate `StackPaths`. */\nexport interface CodegenPaths {\n\t/** The directory the orchestrator emits into (after applying\n\t * per-stack subdirectory if configured). The watcher MUST\n\t * exclude this directory. */\n\treadonly outputDir: string;\n\t/** Path to the `.gitignore` inside `outputDir`. Written every\n\t * emit (the dir is gitignored). */\n\treadonly gitignoreFile: string;\n\t/** Subtree where Move-to-TS bindings land. */\n\treadonly bindingsDir: string;\n\t/** Per-process lock file gating `runEmitCycle`. Sibling to\n\t * `outputDir` so concurrent invocations (e.g. CLI direct-callers\n\t * racing the supervisor) serialize cleanly without blocking the\n\t * short-section substrate `stack.lock`. Codegen cycles can be\n\t * file-system heavy (multi-emitter, Move-bindings compilation);\n\t * the substrate lock is reserved for short sections per the\n\t * cross-process safety protocol. */\n\treadonly codegenLockFile: string;\n\t/** Helper: resolve an emitter's `outputPath` (e.g. `config.ts`)\n\t * against the output root. Fails with `CodegenPathConflict({kind:\n\t * 'non-relative'})` if the supplied path escapes the root. */\n\treadonly resolve: (outputPath: string) => Effect.Effect<string, CodegenPathConflict>;\n\t/** Helper: resolve a per-package bindings subtree path. */\n\treadonly resolveBindingsPackage: (packageName: string) => string;\n\t/** Data-driven rebase: re-root the entire bundle at `newRoot`.\n\t * Preserves the layout (bindings under `<root>/bindings`, gitignore\n\t * at `<root>/.gitignore`) and the `..`-rejecting `resolve`\n\t * discipline; only the prefix changes. Used by the stage-and-swap\n\t * build to redirect the emit pipeline at the staging directory\n\t * WITHOUT string-surgery in the caller.\n\t *\n\t * Note: `codegenLockFile` is preserved verbatim — the lock is\n\t * acquired ONCE outside the staging build, so the rebased view\n\t * must still name the real lock path. */\n\treadonly withRoot: (newRoot: string) => CodegenPaths;\n}\n\nexport class CodegenPathsService extends Context.Service<CodegenPathsService, CodegenPaths>()(\n\t'@devstack/orchestrator/CodegenPaths',\n) {}\n\n/**\n * Materialize the resolver from `CodegenRoot` + Effect's `Path`\n * service. Stack subdir, if present, is appended:\n * `<outputDir>` or `<outputDir>/<stackSubdir>`.\n */\nexport const layerCodegenPaths: Layer.Layer<CodegenPathsService, never, CodegenRoot | Path.Path> =\n\tLayer.effect(\n\t\tCodegenPathsService,\n\t\tEffect.gen(function* () {\n\t\t\tconst root = yield* CodegenRoot;\n\t\t\tconst path = yield* Path.Path;\n\t\t\tconst outputDir = root.stackSubdir\n\t\t\t\t? path.join(root.outputDir, root.stackSubdir)\n\t\t\t\t: root.outputDir;\n\t\t\t// Lock the codegen subsystem on a stable SIBLING of the canonical\n\t\t\t// `outputDir` — `<outputDir>.codegen.lock`. Sibling-NOT-inside is\n\t\t\t// what matters: the `codegen` verb stages into `<outputDir>.staging.<id>`\n\t\t\t// and renames it over `outputDir`, so a lock inside either would be\n\t\t\t// swapped out mid-cycle. Captured once at boot — `withRoot` re-roots\n\t\t\t// the rest of the bundle but preserves this lock path verbatim so the\n\t\t\t// rebased staging view names the real cross-process lock (the lock is\n\t\t\t// acquired ONCE outside the staging build).\n\t\t\tconst codegenLockFile = `${outputDir}.codegen.lock`;\n\t\t\tconst buildAt = (atRoot: string): CodegenPaths => {\n\t\t\t\tconst bindingsDir = path.join(atRoot, 'bindings');\n\t\t\t\tconst resolve = (outputPath: string): Effect.Effect<string, CodegenPathConflict> =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tyield* assertRelativeCodegenOutputPath(outputPath);\n\t\t\t\t\t\treturn path.join(atRoot, outputPath);\n\t\t\t\t\t});\n\t\t\t\tconst resolveBindingsPackage = (packageName: string): string =>\n\t\t\t\t\tpath.join(bindingsDir, packageName);\n\t\t\t\tconst bundle: CodegenPaths = {\n\t\t\t\t\toutputDir: atRoot,\n\t\t\t\t\tgitignoreFile: path.join(atRoot, '.gitignore'),\n\t\t\t\t\tbindingsDir,\n\t\t\t\t\tcodegenLockFile,\n\t\t\t\t\tresolve,\n\t\t\t\t\tresolveBindingsPackage,\n\t\t\t\t\twithRoot: (newRoot: string) => buildAt(newRoot),\n\t\t\t\t};\n\t\t\t\treturn bundle;\n\t\t\t};\n\t\t\treturn CodegenPathsService.of(buildAt(outputDir));\n\t\t}),\n\t);\n"],"mappings":";;;;;;;;;;;;;;;;;AA6BA,MAAa,mCACZ,eAEA,WAAW,SAAS,IAAI,KAAK,WAAW,WAAW,GAAG,IACnD,OAAO,KACP,IAAI,oBAAoB;CACvB,MAAM;CACN;CACA,UAAU,CAAC;AACZ,CAAC,CACF,IACC,OAAO;AAoBX,IAAa,cAAb,cAAiC,QAAQ,QAAuC,CAAC,CAChF,oCACD,CAAC,CAAC,CAAC;;AAGH,MAAa,oBAAoB,SAChC,MAAM,QAAQ,WAAW,CAAC,CAAC,IAAI;AAwChC,IAAa,sBAAb,cAAyC,QAAQ,QAA2C,CAAC,CAC5F,qCACD,CAAC,CAAC,CAAC;;;;;;AAOH,MAAa,oBACZ,MAAM,OACL,qBACA,OAAO,IAAI,aAAa;CACvB,MAAM,OAAO,OAAO;CACpB,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,YAAY,KAAK,cACpB,KAAK,KAAK,KAAK,WAAW,KAAK,WAAW,IAC1C,KAAK;CASR,MAAM,kBAAkB,GAAG,UAAU;CACrC,MAAM,WAAW,WAAiC;EACjD,MAAM,cAAc,KAAK,KAAK,QAAQ,UAAU;EAChD,MAAM,WAAW,eAChB,OAAO,IAAI,aAAa;GACvB,OAAO,gCAAgC,UAAU;GACjD,OAAO,KAAK,KAAK,QAAQ,UAAU;EACpC,CAAC;EACF,MAAM,0BAA0B,gBAC/B,KAAK,KAAK,aAAa,WAAW;EAUnC,OAAO;GARN,WAAW;GACX,eAAe,KAAK,KAAK,QAAQ,YAAY;GAC7C;GACA;GACA;GACA;GACA,WAAW,YAAoB,QAAQ,OAAO;EAEnC;CACb;CACA,OAAO,oBAAoB,GAAG,QAAQ,SAAS,CAAC;AACjD,CAAC,CACF"}