@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
@@ -1 +1 @@
1
- {"version":3,"file":"plain-renderer.mjs","names":[],"sources":["../../../src/surfaces/tui/plain-renderer.ts"],"sourcesContent":["// Plain (non-TTY) renderer.\n//\n// Architecture §11 + distilled/21-tui §Renderers: when stdout is not\n// a TTY (CI, pipe, log file), the live ink dashboard is replaced by\n// a structured line stream. One line per event; periodic heartbeat\n// per internally-acquiring row, surfaced to operators as \"starting\".\n//\n// Discipline:\n// - One line per EngineEvent — no batching, no aggregation, no\n// diffing. Each event maps to exactly one structured line.\n// - Writes to stderr (architecture invariant: plain renderer goes\n// to stderr so stdout stays clean for whatever consumes the\n// pipe).\n// - EPIPE swallowed (pipe-safety invariant).\n// - Pure formatting helpers exposed for tests; the Effect-shaped\n// side is the `mount` function.\n// - Heartbeat formatting is pure here; scheduling belongs to the\n// renderer mount path. The architecture says it\n// \"anchors on first sighting in the internal acquiring state; phase\n// changes do NOT reset the clock; a late tick emits exactly one\n// heartbeat (no backlog catch-up)\". The seam is exposed via\n// `formatHeartbeat`.\n\nimport { Effect, Schema, Stream, SubscriptionRef } from 'effect';\n\nimport type { Renderer } from '../../contracts/renderer.ts';\nimport type { EngineEvent } from '../../substrate/events.ts';\nimport type { SubscribableState } from '../../substrate/projection.ts';\nimport {\n\tAccountProjectionSchema,\n\tPackageProjectionSchema,\n} from '../../substrate/runtime/projection/update.ts';\nimport {\n\taccountLine,\n\tendpointLine,\n\tlabelForRow,\n\tnarrationFor,\n\tpackageLine,\n\troleLabel,\n\tstatusLabel,\n} from './display-derivation.ts';\nimport { mountFailed } from './errors.ts';\nimport { eventAt } from '../../substrate/event-time.ts';\n\n// -----------------------------------------------------------------------------\n// Line shape — pure formatters\n// -----------------------------------------------------------------------------\n\n/**\n * Plain-renderer line shape (each call returns exactly one line, no\n * trailing newline).\n *\n * <iso-timestamp> <level> <event-tag> <key>=<value> ...\n *\n * Example:\n * 2026-05-19T20:11:32.001Z INFO lifecycle.statusChanged key=sui from=starting to=ready\n * 2026-05-19T20:11:32.500Z WARN log.appended key=walrus line=\"failed to bind port 9000\"\n * 2026-05-19T20:11:33.000Z INFO endpoint.registered name=aggregator url=http://localhost:9000\n */\nexport const formatEventLine = (event: EngineEvent): string => {\n\tconst ts = isoTimestamp(eventAt(event));\n\tconst level = levelForEvent(event);\n\tconst payload = payloadFor(event);\n\treturn `${ts} ${level} ${event.tag} ${payload}`;\n};\n\n/**\n * Heartbeat line for an in-flight internal acquiring row. Emitted by the\n * scheduler at architecture-blessed intervals; this function is the\n * pure formatter only.\n */\nexport const formatHeartbeat = (\n\tnow: number,\n\tkey: string,\n\tphase: string | null,\n\troleToken: string,\n): string => {\n\tconst ts = isoTimestamp(now);\n\tconst narration = narrationFor(phase, 'acquiring');\n\treturn `${ts} INFO heartbeat key=${key} role=${roleToken} narration=${quote(narration)}`;\n};\n\n// -----------------------------------------------------------------------------\n// Renderer implementation\n// -----------------------------------------------------------------------------\n\nexport interface PlainRendererOptions {\n\t/**\n\t * Quiet mode: emit only the milestones an operator tailing the stream\n\t * cares about — per-plugin readiness, endpoint registrations, codegen,\n\t * hard-kill escalations, and ALL warnings/errors — and drop the routine\n\t * intermediate transitions (starting/acquiring), info `log.appended`\n\t * lines, projection churn, build progress, and teardown chatter.\n\t *\n\t * OFF by default: the normal `--format plain` / CI stream keeps the\n\t * architecture invariant of one line per event (no aggregation). This\n\t * is opted into by embedded consumers that pipe the supervisor's output\n\t * through another tool — notably the Playwright `webServer`, where the\n\t * full firehose surfaces as confusing `[WebServer]`-prefixed noise.\n\t */\n\treadonly quiet?: boolean;\n}\n\n/**\n * Build a plain renderer satisfying the `Renderer` contract.\n *\n * Mount subscribes to the live event stream, writes one line per\n * event to stderr. The state-ref is sampled for the initial sweep\n * (one line per declared row) so a renderer attached after boot\n * still sees a coherent baseline. In quiet mode the sweep collapses\n * to the single `stack.identity` line — at boot the per-row/account\n * lines are all `<pending>` and the real signal arrives as readiness\n * + endpoint events.\n */\nexport const makePlainRenderer = (options: PlainRendererOptions = {}): Renderer => ({\n\tmount: (stateRef, events) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst initial = yield* SubscriptionRef.get(stateRef);\n\t\t\tyield* emitInitialSweep(initial, { quiet: options.quiet });\n\t\t\tconst stream = options.quiet ? events.pipe(Stream.filter(keepInQuiet)) : events;\n\t\t\tyield* stream.pipe(Stream.runForEach((event) => writeStderrLine(formatEventLine(event))));\n\t\t}).pipe(\n\t\t\tEffect.catch((cause: unknown) =>\n\t\t\t\tEffect.fail(mountFailed(cause instanceof Error ? cause.message : String(cause))),\n\t\t\t),\n\t\t),\n\tflush: Effect.void.pipe(Effect.catch(() => Effect.fail(mountFailed('flush')))),\n});\n\n/**\n * Quiet-mode event predicate (pure; exported for tests). Keeps milestone\n * and non-INFO events; drops routine INFO churn. Anything that formats as\n * WARN/ERROR is always kept so failures are never silently filtered out.\n */\nexport const keepInQuiet = (event: EngineEvent): boolean => {\n\tif (levelForEvent(event) !== 'INFO') return true;\n\tswitch (event.tag) {\n\t\tcase 'lifecycle.statusChanged':\n\t\t\t// Only the readiness milestone, not every starting/acquiring tick.\n\t\t\treturn event.to === 'ready';\n\t\tcase 'endpoint.registered':\n\t\tcase 'codegen.emitted':\n\t\tcase 'shutdown.escalated':\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n};\n\n// -----------------------------------------------------------------------------\n// Internals — formatting\n// -----------------------------------------------------------------------------\n\nconst isoTimestamp = (at: number): string => new Date(at || Date.now()).toISOString();\n\nconst levelForEvent = (event: EngineEvent): 'INFO' | 'WARN' | 'ERROR' => {\n\tswitch (event.tag) {\n\t\tcase 'log.appended':\n\t\t\treturn event.level === 'error' ? 'ERROR' : event.level === 'warn' ? 'WARN' : 'INFO';\n\t\tcase 'error.reported':\n\t\t\treturn event.error.severity === 'fatal' || event.error.severity === 'error'\n\t\t\t\t? 'ERROR'\n\t\t\t\t: 'WARN';\n\t\t// A failed buffered contribution replayed through the\n\t\t// `ContributionDispatcher` leaves the plugin `ready` (non-fatal by\n\t\t// design — see `dispatchBufferedContributions` in acquire-node.ts),\n\t\t// so this is the operator's ONLY signal that e.g. RPC/wallet routing\n\t\t// is dead. It must not render as a routine INFO line.\n\t\tcase 'engine.orchestrator.dispatchFailed':\n\t\t\treturn 'WARN';\n\t\tdefault:\n\t\t\treturn 'INFO';\n\t}\n};\n\nconst payloadFor = (event: EngineEvent): string => {\n\tswitch (event.tag) {\n\t\tcase 'lifecycle.statusChanged':\n\t\t\treturn kv({\n\t\t\t\tkey: event.pluginKey,\n\t\t\t\tfrom: statusLabel(event.from),\n\t\t\t\tto: statusLabel(event.to),\n\t\t\t});\n\t\tcase 'lifecycle.phaseSet':\n\t\t\treturn kv({\n\t\t\t\tkey: event.pluginKey,\n\t\t\t\tphase: event.phase ?? '',\n\t\t\t});\n\t\tcase 'log.appended':\n\t\t\treturn kv({\n\t\t\t\tkey: event.pluginKey,\n\t\t\t\tline: event.line,\n\t\t\t});\n\t\tcase 'endpoint.registered':\n\t\t\treturn kv({\n\t\t\t\tkey: event.endpoint.endpointKey,\n\t\t\t\tname: event.endpoint.name,\n\t\t\t\tdisplayUrl: event.endpoint.displayUrl ?? event.endpoint.url,\n\t\t\t\turl: event.endpoint.url,\n\t\t\t\twire: event.endpoint.wireProtocol,\n\t\t\t});\n\t\tcase 'projection.updated':\n\t\t\tif (event.kind === 'account') {\n\t\t\t\t// STYLE_GUIDE §19 — substrate-side `decodeUnknownSync` so a\n\t\t\t\t// malformed payload skips the line rather than rendering\n\t\t\t\t// garbage (`Schema.decodeUnknownSync(...) as A` bare-cast\n\t\t\t\t// is banned). The reducer at\n\t\t\t\t// `substrate/runtime/projection/update.ts` performs the\n\t\t\t\t// same decode; we repeat it here because the renderer\n\t\t\t\t// reads the raw event stream, not the reduced state slice.\n\t\t\t\tconst account = tryDecodeProjection(AccountProjectionSchema, event.payload);\n\t\t\t\tif (account === null) return kv({ kind: event.kind, key: event.key });\n\t\t\t\treturn kv({\n\t\t\t\t\tkey: account.key,\n\t\t\t\t\trow: account.rowKey ?? '',\n\t\t\t\t\tname: account.name,\n\t\t\t\t\taddress: account.address ?? '',\n\t\t\t\t\tscheme: account.scheme ?? '',\n\t\t\t\t\tsource: account.source ?? '',\n\t\t\t\t\tfunding: account.funding.status,\n\t\t\t\t\tfundingEntries: (account.funding.entries ?? [])\n\t\t\t\t\t\t.map((entry) => `${entry.coin}:${entry.amount}:${entry.status}`)\n\t\t\t\t\t\t.join(','),\n\t\t\t\t\trequestedMist: account.funding.requestedMist ?? '',\n\t\t\t\t\tbalanceMist: account.funding.balanceMist ?? '',\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (event.kind === 'package') {\n\t\t\t\tconst pkg = tryDecodeProjection(PackageProjectionSchema, event.payload);\n\t\t\t\tif (pkg === null) return kv({ kind: event.kind, key: event.key });\n\t\t\t\treturn kv({\n\t\t\t\t\tkey: pkg.key,\n\t\t\t\t\trow: pkg.rowKey ?? '',\n\t\t\t\t\tname: pkg.name,\n\t\t\t\t\tkind: pkg.kind,\n\t\t\t\t\tpackageId: pkg.packageId,\n\t\t\t\t\tupgradeCapId: pkg.upgradeCapId ?? '',\n\t\t\t\t\tmvr: pkg.mvrPlaceholder,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn kv({ kind: event.kind, key: event.key });\n\t\tcase 'endpoint.released':\n\t\t\treturn kv({ key: event.endpointKey });\n\t\tcase 'strategy.registered':\n\t\t\treturn kv({\n\t\t\t\tcapability: event.capabilityKey,\n\t\t\t\tautoMounted: event.autoMounted,\n\t\t\t});\n\t\tcase 'strategy.unregistered':\n\t\t\treturn kv({ capability: event.capabilityKey });\n\t\tcase 'manifest.flushed':\n\t\t\treturn kv({ version: event.manifestVersion });\n\t\tcase 'codegen.emitted':\n\t\t\treturn kv({ files: event.files.length });\n\t\tcase 'engine.orchestrator.dispatchFailed':\n\t\t\treturn kv({\n\t\t\t\tkey: event.pluginKey,\n\t\t\t\tkind: event.kind,\n\t\t\t\t// `causeType` (the failing sink error's `_tag`, e.g.\n\t\t\t\t// `RouterBootFailed`) is the discriminator an operator scans\n\t\t\t\t// for to tell dead RPC routing from a codegen collision. `kv`\n\t\t\t\t// drops it when undefined, so untagged causes stay clean.\n\t\t\t\tcauseType: event.causeType ?? '',\n\t\t\t\tsummary: event.message,\n\t\t\t});\n\t\tcase 'error.reported':\n\t\t\treturn kv({\n\t\t\t\tkey: event.error.pluginKey ?? '',\n\t\t\t\ttag: event.error.tag,\n\t\t\t\tsummary: event.error.summary,\n\t\t\t\tcause: event.error.chain.join(' | '),\n\t\t\t\tseverity: event.error.severity,\n\t\t\t});\n\t\tcase 'build.statusChanged':\n\t\t\treturn kv({\n\t\t\t\tkey: event.entry.pluginKey ?? '',\n\t\t\t\tphase: event.entry.phase,\n\t\t\t\tprogress: event.entry.progress,\n\t\t\t});\n\t\tcase 'restart.requested':\n\t\tcase 'restart.completed':\n\t\t\treturn kv({\n\t\t\t\ttarget: event.target === 'stack' ? 'stack' : event.target.pluginKey,\n\t\t\t});\n\t\tcase 'shutdown.escalated':\n\t\t\treturn kv({\n\t\t\t\tmode: 'hard-kill',\n\t\t\t\tsignal: event.signal,\n\t\t\t\texitCode: event.exitCode,\n\t\t\t});\n\t\tcase 'snapshot.captureSkipped':\n\t\t\treturn kv({ reason: event.reason });\n\t\tcase 'snapshot.captureFailed':\n\t\t\treturn kv({ snapshotId: event.snapshotId, name: event.name, summary: event.summary });\n\t\tcase 'snapshot.captured':\n\t\tcase 'snapshot.restored':\n\t\t\treturn kv({ snapshotId: event.snapshotId });\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = event;\n\t\t\tvoid _exhaustive;\n\t\t\treturn '';\n\t\t}\n\t}\n};\n\nconst kv = (record: Readonly<Record<string, unknown>>): string =>\n\tObject.entries(record)\n\t\t.filter(([, v]) => v !== '' && v !== null && v !== undefined)\n\t\t.map(([k, v]) => `${k}=${quoteIfNeeded(String(v))}`)\n\t\t.join(' ');\n\n// Quote anything that contains whitespace, a `\"`, or an ASCII control\n// byte (`\\x00-\\x1f`). Control bytes are the load-bearing case: a\n// projection payload field that contains a stray `\\x1b` (ESC) would\n// otherwise inject an ANSI escape sequence into the plain-renderer's\n// stdout, corrupting subsequent terminal state for callers tailing\n// `--format plain` output.\nconst quoteIfNeeded = (s: string): string => (/[\\s\"\\x00-\\x1f]/.test(s) ? quote(s) : s);\n\nconst quote = (s: string): string => `\"${s.replace(/\"/g, '\\\\\"')}\"`;\n\n/**\n * Structural validate-then-narrow for `projection.updated` payloads\n * before they are formatted as text. Mirrors\n * `tryDecodeProjectionPayload` in the substrate reducer — returns\n * `null` on schema-decode failure so the renderer drops the slice\n * rather than printing fields off a malformed object. The substrate\n * reducer emits the `Effect.logWarning` for the same payload (the\n * decode is deterministic), so we stay silent here to avoid double\n * logging.\n */\nconst tryDecodeProjection = <S extends Schema.Decoder<unknown>>(\n\tschema: S,\n\tpayload: unknown,\n): S['Type'] | null => {\n\ttry {\n\t\treturn Schema.decodeUnknownSync(schema)(payload);\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n// -----------------------------------------------------------------------------\n// Internals — initial sweep\n// -----------------------------------------------------------------------------\n\nconst emitInitialSweep = (\n\tstate: SubscribableState,\n\toptions: { readonly quiet?: boolean } = {},\n): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\tyield* writeStderrLine(\n\t\t\t`${isoTimestamp(state.cycle.startedAt || Date.now())} INFO stack.identity app=${state.identity.app} stack=${state.identity.stack} network=${state.identity.network} cycle=${state.cycle.id}`,\n\t\t);\n\t\t// Quiet mode: stop after the identity line. The per-row/account/\n\t\t// endpoint/package sweep is all `<pending>` at boot; the real signal\n\t\t// arrives as readiness + endpoint events on the (filtered) stream.\n\t\tif (options.quiet) return;\n\t\tfor (const row of state.rows) {\n\t\t\tyield* writeStderrLine(\n\t\t\t\t`${isoTimestamp(Date.now())} INFO row.declared ${kv({\n\t\t\t\t\tkey: row.key,\n\t\t\t\t\tlabel: labelForRow(row.key),\n\t\t\t\t\trole: roleLabel(row.role),\n\t\t\t\t\tstatus: statusLabel(row.status),\n\t\t\t\t})}`,\n\t\t\t);\n\t\t}\n\t\tfor (const endpoint of state.endpoints) {\n\t\t\tyield* writeStderrLine(\n\t\t\t\t`${isoTimestamp(endpoint.registeredAt)} INFO endpoint.registered ${endpointLine(endpoint)}`,\n\t\t\t);\n\t\t}\n\t\tfor (const account of state.accounts) {\n\t\t\tyield* writeStderrLine(\n\t\t\t\t`${isoTimestamp(account.updatedAt)} INFO projection.updated[account] ${accountLine(account)}`,\n\t\t\t);\n\t\t}\n\t\tfor (const pkg of state.packages) {\n\t\t\tyield* writeStderrLine(\n\t\t\t\t`${isoTimestamp(pkg.updatedAt)} INFO projection.updated[package] ${packageLine(pkg)}`,\n\t\t\t);\n\t\t}\n\t});\n\n// -----------------------------------------------------------------------------\n// Internals — IO (pipe-safe)\n// -----------------------------------------------------------------------------\n\nconst writeStderrLine = (line: string): Effect.Effect<void> =>\n\tEffect.sync(() => {\n\t\ttry {\n\t\t\tprocess.stderr.write(`${line}\\n`);\n\t\t} catch (err) {\n\t\t\t// EPIPE swallowed (pipe-safety invariant); other errors\n\t\t\t// likewise swallowed because \"rendering never crashes the\n\t\t\t// stack\" (distilled/21-tui § Invariants).\n\t\t\tvoid err;\n\t\t}\n\t});\n"],"mappings":"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"}
1
+ {"version":3,"file":"plain-renderer.mjs","names":[],"sources":["../../../src/surfaces/tui/plain-renderer.ts"],"sourcesContent":["// Plain (non-TTY) renderer.\n//\n// Architecture §11 + distilled/21-tui §Renderers: when stdout is not\n// a TTY (CI, pipe, log file), the live ink dashboard is replaced by\n// a structured line stream. One line per event; periodic heartbeat\n// per internally-acquiring row, surfaced to operators as \"starting\".\n//\n// Discipline:\n// - One line per EngineEvent — no batching, no aggregation, no\n// diffing. Each event maps to exactly one structured line.\n// - Writes to stderr (architecture invariant: plain renderer goes\n// to stderr so stdout stays clean for whatever consumes the\n// pipe).\n// - EPIPE swallowed (pipe-safety invariant).\n// - Pure formatting helpers exposed for tests; the Effect-shaped\n// side is the `mount` function.\n// - Heartbeat formatting is pure here; scheduling belongs to the\n// renderer mount path. The architecture says it\n// \"anchors on first sighting in the internal acquiring state; phase\n// changes do NOT reset the clock; a late tick emits exactly one\n// heartbeat (no backlog catch-up)\". The seam is exposed via\n// `formatHeartbeat`.\n\nimport { Effect, Schema, Stream, SubscriptionRef } from 'effect';\n\nimport type { Renderer } from '../../contracts/renderer.ts';\nimport type { EngineEvent } from '../../substrate/events.ts';\nimport type { SubscribableState } from '../../substrate/projection.ts';\nimport {\n\tAccountProjectionSchema,\n\tPackageProjectionSchema,\n} from '../../substrate/runtime/projection/update.ts';\nimport {\n\taccountLine,\n\tendpointLine,\n\tlabelForRow,\n\tnarrationFor,\n\tpackageLine,\n\troleLabel,\n\tstatusLabel,\n} from './display-derivation.ts';\nimport { mountFailed } from './errors.ts';\nimport { eventAt } from '../../substrate/event-time.ts';\n\n// -----------------------------------------------------------------------------\n// Line shape — pure formatters\n// -----------------------------------------------------------------------------\n\n/**\n * Plain-renderer line shape (each call returns exactly one line, no\n * trailing newline).\n *\n * <iso-timestamp> <level> <event-tag> <key>=<value> ...\n *\n * Example:\n * 2026-05-19T20:11:32.001Z INFO lifecycle.statusChanged key=sui from=starting to=ready\n * 2026-05-19T20:11:32.500Z WARN log.appended key=walrus line=\"failed to bind port 9000\"\n * 2026-05-19T20:11:33.000Z INFO endpoint.registered name=aggregator url=http://localhost:9000\n */\nexport const formatEventLine = (event: EngineEvent): string => {\n\tconst ts = isoTimestamp(eventAt(event));\n\tconst level = levelForEvent(event);\n\tconst payload = payloadFor(event);\n\treturn `${ts} ${level} ${event.tag} ${payload}`;\n};\n\n/**\n * Heartbeat line for an in-flight internal acquiring row. Emitted by the\n * scheduler at architecture-blessed intervals; this function is the\n * pure formatter only.\n */\nexport const formatHeartbeat = (\n\tnow: number,\n\tkey: string,\n\tphase: string | null,\n\troleToken: string,\n): string => {\n\tconst ts = isoTimestamp(now);\n\tconst narration = narrationFor(phase, 'acquiring');\n\treturn `${ts} INFO heartbeat key=${key} role=${roleToken} narration=${quote(narration)}`;\n};\n\n// -----------------------------------------------------------------------------\n// Renderer implementation\n// -----------------------------------------------------------------------------\n\nexport interface PlainRendererOptions {\n\t/**\n\t * Quiet mode: emit only the milestones an operator tailing the stream\n\t * cares about — per-plugin readiness, endpoint registrations, codegen,\n\t * hard-kill escalations, and ALL warnings/errors — and drop the routine\n\t * intermediate transitions (starting/acquiring), info `log.appended`\n\t * lines, projection churn, build progress, and teardown chatter.\n\t *\n\t * OFF by default: the normal `--format plain` / CI stream keeps the\n\t * architecture invariant of one line per event (no aggregation). This\n\t * is opted into by embedded consumers that pipe the supervisor's output\n\t * through another tool — notably the Playwright `webServer`, where the\n\t * full firehose surfaces as confusing `[WebServer]`-prefixed noise.\n\t */\n\treadonly quiet?: boolean;\n}\n\n/**\n * Build a plain renderer satisfying the `Renderer` contract.\n *\n * Mount subscribes to the live event stream, writes one line per\n * event to stderr. The state-ref is sampled for the initial sweep\n * (one line per declared row) so a renderer attached after boot\n * still sees a coherent baseline. In quiet mode the sweep collapses\n * to the single `stack.identity` line — at boot the per-row/account\n * lines are all `<pending>` and the real signal arrives as readiness\n * + endpoint events.\n */\nexport const makePlainRenderer = (options: PlainRendererOptions = {}): Renderer => ({\n\tmount: (stateRef, events) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst initial = yield* SubscriptionRef.get(stateRef);\n\t\t\tyield* emitInitialSweep(initial, { quiet: options.quiet });\n\t\t\tconst stream = options.quiet ? events.pipe(Stream.filter(keepInQuiet)) : events;\n\t\t\tyield* stream.pipe(Stream.runForEach((event) => writeStderrLine(formatEventLine(event))));\n\t\t}).pipe(\n\t\t\tEffect.catch((cause: unknown) =>\n\t\t\t\tEffect.fail(mountFailed(cause instanceof Error ? cause.message : String(cause))),\n\t\t\t),\n\t\t),\n\tflush: Effect.void.pipe(Effect.catch(() => Effect.fail(mountFailed('flush')))),\n});\n\n/**\n * Quiet-mode event predicate (pure; exported for tests). Keeps milestone\n * and non-INFO events; drops routine INFO churn. Anything that formats as\n * WARN/ERROR is always kept so failures are never silently filtered out.\n */\nexport const keepInQuiet = (event: EngineEvent): boolean => {\n\tif (levelForEvent(event) !== 'INFO') return true;\n\tswitch (event.tag) {\n\t\tcase 'lifecycle.statusChanged':\n\t\t\t// Only the readiness milestone, not every starting/acquiring tick.\n\t\t\treturn event.to === 'ready';\n\t\tcase 'endpoint.registered':\n\t\tcase 'codegen.emitted':\n\t\tcase 'shutdown.escalated':\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n};\n\n// -----------------------------------------------------------------------------\n// Internals — formatting\n// -----------------------------------------------------------------------------\n\nconst isoTimestamp = (at: number): string => new Date(at || Date.now()).toISOString();\n\nconst levelForEvent = (event: EngineEvent): 'INFO' | 'WARN' | 'ERROR' => {\n\tswitch (event.tag) {\n\t\tcase 'log.appended':\n\t\t\treturn event.level === 'error' ? 'ERROR' : event.level === 'warn' ? 'WARN' : 'INFO';\n\t\tcase 'error.reported':\n\t\t\treturn event.error.severity === 'fatal' || event.error.severity === 'error'\n\t\t\t\t? 'ERROR'\n\t\t\t\t: 'WARN';\n\t\t// A failed buffered contribution replayed through the\n\t\t// `ContributionDispatcher` leaves the plugin `ready` (non-fatal by\n\t\t// design — see `dispatchBufferedContributions` in acquire-node.ts),\n\t\t// so this is the operator's ONLY signal that e.g. RPC/wallet routing\n\t\t// is dead. It must not render as a routine INFO line.\n\t\tcase 'engine.orchestrator.dispatchFailed':\n\t\t\treturn 'WARN';\n\t\tdefault:\n\t\t\treturn 'INFO';\n\t}\n};\n\nconst payloadFor = (event: EngineEvent): string => {\n\tswitch (event.tag) {\n\t\tcase 'lifecycle.statusChanged':\n\t\t\treturn kv({\n\t\t\t\tkey: event.pluginKey,\n\t\t\t\tfrom: statusLabel(event.from),\n\t\t\t\tto: statusLabel(event.to),\n\t\t\t});\n\t\tcase 'lifecycle.phaseSet':\n\t\t\treturn kv({\n\t\t\t\tkey: event.pluginKey,\n\t\t\t\tphase: event.phase ?? '',\n\t\t\t});\n\t\tcase 'log.appended':\n\t\t\treturn kv({\n\t\t\t\tkey: event.pluginKey,\n\t\t\t\tline: event.line,\n\t\t\t});\n\t\tcase 'endpoint.registered':\n\t\t\treturn kv({\n\t\t\t\tkey: event.endpoint.endpointKey,\n\t\t\t\tname: event.endpoint.name,\n\t\t\t\tdisplayUrl: event.endpoint.displayUrl ?? event.endpoint.url,\n\t\t\t\turl: event.endpoint.url,\n\t\t\t\twire: event.endpoint.wireProtocol,\n\t\t\t});\n\t\tcase 'projection.updated':\n\t\t\tif (event.kind === 'account') {\n\t\t\t\t// STYLE_GUIDE §19 — substrate-side `decodeUnknownSync` so a\n\t\t\t\t// malformed payload skips the line rather than rendering\n\t\t\t\t// garbage (`Schema.decodeUnknownSync(...) as A` bare-cast\n\t\t\t\t// is banned). The reducer at\n\t\t\t\t// `substrate/runtime/projection/update.ts` performs the\n\t\t\t\t// same decode; we repeat it here because the renderer\n\t\t\t\t// reads the raw event stream, not the reduced state slice.\n\t\t\t\tconst account = tryDecodeProjection(AccountProjectionSchema, event.payload);\n\t\t\t\tif (account === null) return kv({ kind: event.kind, key: event.key });\n\t\t\t\treturn kv({\n\t\t\t\t\tkey: account.key,\n\t\t\t\t\trow: account.rowKey ?? '',\n\t\t\t\t\tname: account.name,\n\t\t\t\t\taddress: account.address ?? '',\n\t\t\t\t\tscheme: account.scheme ?? '',\n\t\t\t\t\tsource: account.source ?? '',\n\t\t\t\t\tfunding: account.funding.status,\n\t\t\t\t\tfundingEntries: (account.funding.entries ?? [])\n\t\t\t\t\t\t.map((entry) => `${entry.coin}:${entry.amount}:${entry.status}`)\n\t\t\t\t\t\t.join(','),\n\t\t\t\t\trequestedMist: account.funding.requestedMist ?? '',\n\t\t\t\t\tbalanceMist: account.funding.balanceMist ?? '',\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (event.kind === 'package') {\n\t\t\t\tconst pkg = tryDecodeProjection(PackageProjectionSchema, event.payload);\n\t\t\t\tif (pkg === null) return kv({ kind: event.kind, key: event.key });\n\t\t\t\treturn kv({\n\t\t\t\t\tkey: pkg.key,\n\t\t\t\t\trow: pkg.rowKey ?? '',\n\t\t\t\t\tname: pkg.name,\n\t\t\t\t\tkind: pkg.kind,\n\t\t\t\t\tpackageId: pkg.packageId,\n\t\t\t\t\tupgradeCapId: pkg.upgradeCapId ?? '',\n\t\t\t\t\tmvr: pkg.mvrPlaceholder,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn kv({ kind: event.kind, key: event.key });\n\t\tcase 'endpoint.released':\n\t\t\treturn kv({ key: event.endpointKey });\n\t\tcase 'strategy.registered':\n\t\t\treturn kv({\n\t\t\t\tcapability: event.capabilityKey,\n\t\t\t\tautoMounted: event.autoMounted,\n\t\t\t});\n\t\tcase 'strategy.unregistered':\n\t\t\treturn kv({ capability: event.capabilityKey });\n\t\tcase 'manifest.flushed':\n\t\t\treturn kv({ version: event.manifestVersion });\n\t\tcase 'codegen.emitted':\n\t\t\treturn kv({ files: event.files.length });\n\t\tcase 'engine.orchestrator.dispatchFailed':\n\t\t\treturn kv({\n\t\t\t\tkey: event.pluginKey,\n\t\t\t\tkind: event.kind,\n\t\t\t\t// `causeType` (the failing sink error's `_tag`, e.g.\n\t\t\t\t// `RouterBootFailed`) is the discriminator an operator scans\n\t\t\t\t// for to tell dead RPC routing from a codegen collision. `kv`\n\t\t\t\t// drops it when undefined, so untagged causes stay clean.\n\t\t\t\tcauseType: event.causeType ?? '',\n\t\t\t\tsummary: event.message,\n\t\t\t});\n\t\tcase 'error.reported':\n\t\t\treturn kv({\n\t\t\t\tkey: event.error.pluginKey ?? '',\n\t\t\t\ttag: event.error.tag,\n\t\t\t\tsummary: event.error.summary,\n\t\t\t\tcause: event.error.chain.join(' | '),\n\t\t\t\tseverity: event.error.severity,\n\t\t\t});\n\t\tcase 'build.statusChanged':\n\t\t\treturn kv({\n\t\t\t\tkey: event.entry.pluginKey ?? '',\n\t\t\t\tphase: event.entry.phase,\n\t\t\t\tprogress: event.entry.progress,\n\t\t\t});\n\t\tcase 'restart.requested':\n\t\tcase 'restart.completed':\n\t\t\treturn kv({\n\t\t\t\ttarget: event.target === 'stack' ? 'stack' : event.target.pluginKey,\n\t\t\t});\n\t\tcase 'shutdown.escalated':\n\t\t\treturn kv({\n\t\t\t\tmode: 'hard-kill',\n\t\t\t\tsignal: event.signal,\n\t\t\t\texitCode: event.exitCode,\n\t\t\t});\n\t\tcase 'snapshot.captureSkipped':\n\t\t\treturn kv({ reason: event.reason });\n\t\tcase 'snapshot.captureFailed':\n\t\t\treturn kv({ snapshotId: event.snapshotId, name: event.name, summary: event.summary });\n\t\tcase 'snapshot.captured':\n\t\tcase 'snapshot.restored':\n\t\t\treturn kv({ snapshotId: event.snapshotId });\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = event;\n\t\t\tvoid _exhaustive;\n\t\t\treturn '';\n\t\t}\n\t}\n};\n\nconst kv = (record: Readonly<Record<string, unknown>>): string =>\n\tObject.entries(record)\n\t\t.filter(([, v]) => v !== '' && v !== null && v !== undefined)\n\t\t.map(([k, v]) => `${k}=${quoteIfNeeded(String(v))}`)\n\t\t.join(' ');\n\n// Quote anything that contains whitespace, a `\"`, or an ASCII control\n// byte (`\\x00-\\x1f`). Control bytes are the load-bearing case: a\n// projection payload field that contains a stray `\\x1b` (ESC) would\n// otherwise inject an ANSI escape sequence into the plain-renderer's\n// stdout, corrupting subsequent terminal state for callers tailing\n// `--format plain` output.\n// eslint-disable-next-line no-control-regex -- intentionally detects control chars that require quoting\nconst quoteIfNeeded = (s: string): string => (/[\\s\"\\x00-\\x1f]/.test(s) ? quote(s) : s);\n\nconst quote = (s: string): string => `\"${s.replace(/\"/g, '\\\\\"')}\"`;\n\n/**\n * Structural validate-then-narrow for `projection.updated` payloads\n * before they are formatted as text. Mirrors\n * `tryDecodeProjectionPayload` in the substrate reducer — returns\n * `null` on schema-decode failure so the renderer drops the slice\n * rather than printing fields off a malformed object. The substrate\n * reducer emits the `Effect.logWarning` for the same payload (the\n * decode is deterministic), so we stay silent here to avoid double\n * logging.\n */\nconst tryDecodeProjection = <S extends Schema.Decoder<unknown>>(\n\tschema: S,\n\tpayload: unknown,\n): S['Type'] | null => {\n\ttry {\n\t\treturn Schema.decodeUnknownSync(schema)(payload);\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n// -----------------------------------------------------------------------------\n// Internals — initial sweep\n// -----------------------------------------------------------------------------\n\nconst emitInitialSweep = (\n\tstate: SubscribableState,\n\toptions: { readonly quiet?: boolean } = {},\n): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\tyield* writeStderrLine(\n\t\t\t`${isoTimestamp(state.cycle.startedAt || Date.now())} INFO stack.identity app=${state.identity.app} stack=${state.identity.stack} network=${state.identity.network} cycle=${state.cycle.id}`,\n\t\t);\n\t\t// Quiet mode: stop after the identity line. The per-row/account/\n\t\t// endpoint/package sweep is all `<pending>` at boot; the real signal\n\t\t// arrives as readiness + endpoint events on the (filtered) stream.\n\t\tif (options.quiet) return;\n\t\tfor (const row of state.rows) {\n\t\t\tyield* writeStderrLine(\n\t\t\t\t`${isoTimestamp(Date.now())} INFO row.declared ${kv({\n\t\t\t\t\tkey: row.key,\n\t\t\t\t\tlabel: labelForRow(row.key),\n\t\t\t\t\trole: roleLabel(row.role),\n\t\t\t\t\tstatus: statusLabel(row.status),\n\t\t\t\t})}`,\n\t\t\t);\n\t\t}\n\t\tfor (const endpoint of state.endpoints) {\n\t\t\tyield* writeStderrLine(\n\t\t\t\t`${isoTimestamp(endpoint.registeredAt)} INFO endpoint.registered ${endpointLine(endpoint)}`,\n\t\t\t);\n\t\t}\n\t\tfor (const account of state.accounts) {\n\t\t\tyield* writeStderrLine(\n\t\t\t\t`${isoTimestamp(account.updatedAt)} INFO projection.updated[account] ${accountLine(account)}`,\n\t\t\t);\n\t\t}\n\t\tfor (const pkg of state.packages) {\n\t\t\tyield* writeStderrLine(\n\t\t\t\t`${isoTimestamp(pkg.updatedAt)} INFO projection.updated[package] ${packageLine(pkg)}`,\n\t\t\t);\n\t\t}\n\t});\n\n// -----------------------------------------------------------------------------\n// Internals — IO (pipe-safe)\n// -----------------------------------------------------------------------------\n\nconst writeStderrLine = (line: string): Effect.Effect<void> =>\n\tEffect.sync(() => {\n\t\ttry {\n\t\t\tprocess.stderr.write(`${line}\\n`);\n\t\t} catch (err) {\n\t\t\t// EPIPE swallowed (pipe-safety invariant); other errors\n\t\t\t// likewise swallowed because \"rendering never crashes the\n\t\t\t// stack\" (distilled/21-tui § Invariants).\n\t\t\tvoid err;\n\t\t}\n\t});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mysten-incubation/devstack",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Next-generation Sui devstack package.",
5
5
  "keywords": [
6
6
  "sui",
@@ -1,86 +0,0 @@
1
- import { ID_CONFIG_FILENAME, decodeIdConfig } from "../../orchestrators/codegen/id-config.mjs";
2
- import { ExitCode } from "../../surfaces/cli/sysexits.mjs";
3
- import { CliInternalError } from "../../surfaces/cli/errors.mjs";
4
- import { emitSuccess } from "../../surfaces/cli/output.mjs";
5
- import { probeSupervisorPresence } from "../../surfaces/cli/commands/supervisor-presence.mjs";
6
- import "../../surfaces/cli/commands/index.mjs";
7
- import { cliErrorFromConfigExit } from "../bail.mjs";
8
- import { makeConfigLoader } from "./config-loader.mjs";
9
- import { resolvedIdentityForStack } from "./identity.mjs";
10
- import { runApplyLive } from "./apply.mjs";
11
- import { Effect, Exit } from "effect";
12
- import { readFileSync, writeFileSync } from "node:fs";
13
- import { resolve } from "node:path";
14
- //#region src/cli/wirings/dump-ids.ts
15
- /** Read the on-disk id-config as raw text. The file is written by boot's
16
- * post-acquire hook (`writeIdConfig`) at `<stackRoot>/devstack-ids.json`;
17
- * a miss here means the boot never produced it (or a hand-removal), which
18
- * is an internal inconsistency the operator can't act on directly. */
19
- const readIdConfigText = (idsFile) => Effect.try({
20
- try: () => readFileSync(idsFile, "utf8"),
21
- catch: (cause) => new CliInternalError({
22
- message: `failed to read id-config at ${idsFile}`,
23
- cause
24
- })
25
- });
26
- const writeOutFile = (out, text) => Effect.try({
27
- try: () => writeFileSync(out, text, "utf8"),
28
- catch: (cause) => new CliInternalError({
29
- message: `failed to write id-config to ${out}`,
30
- cause
31
- })
32
- });
33
- /** Decode the id-config text for the JSON envelope `data`, routed through
34
- * the shared {@link decodeIdConfig} so the parse-and-validate decision is
35
- * centralized (same seam the Vite plugin and codegen verb use). A failure
36
- * here means the on-disk file is corrupt — bad JSON OR a shape that
37
- * violates `IdConfigSchema` — so surface it rather than emit a bad
38
- * envelope. (The file is written by `writeIdConfig` from the same schema,
39
- * so a conforming boot-written file always decodes; only a corrupt or
40
- * hand-edited file trips the stricter validation.) */
41
- const parseIdConfig = (text, idsFile) => Effect.try({
42
- try: () => decodeIdConfig(text),
43
- catch: (cause) => new CliInternalError({
44
- message: `id-config at ${idsFile} is not valid JSON`,
45
- cause
46
- })
47
- });
48
- const runDumpIds = (identity, opts) => {
49
- const loader = makeConfigLoader();
50
- return Effect.gen(function* () {
51
- const started = Date.now();
52
- const loadExit = yield* Effect.exit(loader.load(opts.configPath));
53
- if (Exit.isFailure(loadExit)) return yield* Effect.fail(cliErrorFromConfigExit(loadExit));
54
- const stack = loadExit.value.engine;
55
- const effectiveIdentity = resolvedIdentityForStack(identity, stack);
56
- const idsFile = resolve(effectiveIdentity.stackRoot, ID_CONFIG_FILENAME);
57
- if (!(yield* probeSupervisorPresence(effectiveIdentity.rosterFile).pipe(Effect.catch(() => Effect.succeed({
58
- live: false,
59
- pid: null,
60
- hostname: null
61
- })))).live) yield* runApplyLive(opts.configPath, identity);
62
- const text = yield* readIdConfigText(idsFile);
63
- const data = yield* parseIdConfig(text, idsFile);
64
- if (opts.out !== void 0) {
65
- yield* writeOutFile(opts.out, text);
66
- yield* emitSuccess(opts.io, opts.outputMode, {
67
- command: "dump-ids",
68
- elapsedMs: Date.now() - started,
69
- data,
70
- humanLines: [`wrote id-config to ${opts.out}`]
71
- });
72
- return { exitCode: ExitCode.OK };
73
- }
74
- yield* emitSuccess(opts.io, opts.outputMode, {
75
- command: "dump-ids",
76
- elapsedMs: Date.now() - started,
77
- data,
78
- humanLines: [text.endsWith("\n") ? text.slice(0, -1) : text]
79
- });
80
- return { exitCode: ExitCode.OK };
81
- });
82
- };
83
- //#endregion
84
- export { runDumpIds };
85
-
86
- //# sourceMappingURL=dump-ids.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dump-ids.mjs","names":["resolvePath"],"sources":["../../../src/cli/wirings/dump-ids.ts"],"sourcesContent":["// `devstack dump-ids` verb wiring.\n//\n// Emits the stack's `devstack-ids.json` id-config — the single source of\n// on-chain ids (see `orchestrators/codegen/id-config.ts`). This is the\n// supported way to obtain a committed id-config file for a real-network\n// deploy (docs' \"Deploy to a real network / Option A\"), replacing the\n// manual `cp .devstack/stacks/<stack>/devstack-ids.json …`.\n//\n// Live-aware, mirroring `apply.ts` / `snapshot.ts`:\n// - If a supervisor owns the selected stack (roster probe), the\n// id-config already exists on disk — boot's post-acquire hook wrote\n// it (`boot.ts buildProductionPostAcquireHook`). Read + emit it; NO\n// re-boot.\n// - Otherwise, run the SAME one-shot boot `apply` runs when no\n// supervisor is live (`runApplyLive` → `superviseStackWithProductionBoot`\n// with `lifetime: 'one-shot'`). That boots, writes the id-config to\n// `<stackRoot>/devstack-ids.json`, and tears down. We then read the\n// freshly-written file and emit it.\n//\n// The on-disk file is already pretty-printed by `writeIdConfig`\n// (`JSON.stringify(config, null, 2)\\n`), so `--out` copies its bytes\n// verbatim and stdout prints them verbatim — the format matches the\n// boot-written file exactly.\n\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { resolve as resolvePath } from 'node:path';\n\nimport { Effect, Exit } from 'effect';\n\nimport type { SupervisedStack } from '../../substrate/runtime/index.ts';\nimport { decodeIdConfig, ID_CONFIG_FILENAME } from '../../orchestrators/codegen/id-config.ts';\nimport { type CliError, CliInternalError } from '../../surfaces/cli/errors.ts';\nimport { type CommandResult } from '../../surfaces/cli/commands/index.ts';\nimport { probeSupervisorPresence } from '../../surfaces/cli/commands/index.ts';\nimport { ExitCode } from '../../surfaces/cli/sysexits.ts';\nimport { type CliIO, emitSuccess } from '../../surfaces/cli/output.ts';\nimport type { OutputMode } from '../../surfaces/cli/flags.ts';\nimport type { LoadedConfig } from '../../surfaces/cli/commands/config-loader.ts';\n\nimport { cliErrorFromConfigExit } from '../bail.ts';\nimport { makeConfigLoader } from './config-loader.ts';\nimport { resolvedIdentityForStack, type ResolvedIdentity } from './identity.ts';\nimport { runApplyLive } from './apply.ts';\n\nexport interface DumpIdsOptions {\n\treadonly configPath: string | undefined;\n\t/** Destination for the pretty id-config JSON. When omitted, the JSON\n\t * is printed to stdout. */\n\treadonly out: string | undefined;\n\treadonly io: CliIO;\n\treadonly outputMode: OutputMode;\n}\n\n/** Read the on-disk id-config as raw text. The file is written by boot's\n * post-acquire hook (`writeIdConfig`) at `<stackRoot>/devstack-ids.json`;\n * a miss here means the boot never produced it (or a hand-removal), which\n * is an internal inconsistency the operator can't act on directly. */\nconst readIdConfigText = (idsFile: string): Effect.Effect<string, CliError> =>\n\tEffect.try({\n\t\ttry: () => readFileSync(idsFile, 'utf8'),\n\t\tcatch: (cause) =>\n\t\t\tnew CliInternalError({\n\t\t\t\tmessage: `failed to read id-config at ${idsFile}`,\n\t\t\t\tcause,\n\t\t\t}),\n\t});\n\nconst writeOutFile = (out: string, text: string): Effect.Effect<void, CliError> =>\n\tEffect.try({\n\t\ttry: () => writeFileSync(out, text, 'utf8'),\n\t\tcatch: (cause) =>\n\t\t\tnew CliInternalError({ message: `failed to write id-config to ${out}`, cause }),\n\t});\n\n/** Decode the id-config text for the JSON envelope `data`, routed through\n * the shared {@link decodeIdConfig} so the parse-and-validate decision is\n * centralized (same seam the Vite plugin and codegen verb use). A failure\n * here means the on-disk file is corrupt — bad JSON OR a shape that\n * violates `IdConfigSchema` — so surface it rather than emit a bad\n * envelope. (The file is written by `writeIdConfig` from the same schema,\n * so a conforming boot-written file always decodes; only a corrupt or\n * hand-edited file trips the stricter validation.) */\nconst parseIdConfig = (text: string, idsFile: string): Effect.Effect<unknown, CliError> =>\n\tEffect.try({\n\t\ttry: () => decodeIdConfig(text),\n\t\tcatch: (cause) =>\n\t\t\tnew CliInternalError({ message: `id-config at ${idsFile} is not valid JSON`, cause }),\n\t});\n\nexport const runDumpIds = (\n\tidentity: ResolvedIdentity,\n\topts: DumpIdsOptions,\n): Effect.Effect<CommandResult, CliError> => {\n\tconst loader = makeConfigLoader();\n\treturn Effect.gen(function* () {\n\t\tconst started = Date.now();\n\n\t\t// Resolve identity against the EFFECTIVE stack (explicit\n\t\t// `--stack`/env > `config.stackName` > inferred), matching `apply` /\n\t\t// `snapshot`, so the roster probe + id-config path target the same\n\t\t// stack the operator selected.\n\t\tconst loadExit = yield* Effect.exit(loader.load(opts.configPath));\n\t\tif (Exit.isFailure(loadExit)) {\n\t\t\treturn yield* Effect.fail(cliErrorFromConfigExit(loadExit));\n\t\t}\n\t\tconst stack = (loadExit.value as LoadedConfig & { readonly engine: SupervisedStack }).engine;\n\t\tconst effectiveIdentity = resolvedIdentityForStack(identity, stack);\n\t\tconst idsFile = resolvePath(effectiveIdentity.stackRoot, ID_CONFIG_FILENAME);\n\n\t\t// Live supervisor? The id-config already exists on disk (boot wrote\n\t\t// it). Otherwise run the one-shot boot `apply` runs when nothing is\n\t\t// live — it writes the id-config to `idsFile` and tears down — then\n\t\t// read the file it produced.\n\t\tconst presence = yield* probeSupervisorPresence(effectiveIdentity.rosterFile).pipe(\n\t\t\tEffect.catch(() => Effect.succeed({ live: false, pid: null, hostname: null })),\n\t\t);\n\t\tif (!presence.live) {\n\t\t\t// `runApplyLive` re-probes (sees no live supervisor) and runs the\n\t\t\t// one-shot boot; its config-load / boot failures already surface as\n\t\t\t// typed `CliError`s. We pass the ORIGINAL identity — `runApplyLive`\n\t\t\t// re-derives the effective stack from the same config internally.\n\t\t\tyield* runApplyLive(opts.configPath, identity);\n\t\t}\n\n\t\tconst text = yield* readIdConfigText(idsFile);\n\t\tconst data = yield* parseIdConfig(text, idsFile);\n\n\t\tif (opts.out !== undefined) {\n\t\t\tyield* writeOutFile(opts.out, text);\n\t\t\tyield* emitSuccess(opts.io, opts.outputMode, {\n\t\t\t\tcommand: 'dump-ids',\n\t\t\t\telapsedMs: Date.now() - started,\n\t\t\t\tdata,\n\t\t\t\thumanLines: [`wrote id-config to ${opts.out}`],\n\t\t\t});\n\t\t\treturn { exitCode: ExitCode.OK };\n\t\t}\n\n\t\t// No `--out`: print the id-config JSON to stdout. In human mode the\n\t\t// raw pretty JSON IS the output (trailing newline trimmed by the IO\n\t\t// layer); in JSON mode the same object rides the success envelope's\n\t\t// `data`.\n\t\tyield* emitSuccess(opts.io, opts.outputMode, {\n\t\t\tcommand: 'dump-ids',\n\t\t\telapsedMs: Date.now() - started,\n\t\t\tdata,\n\t\t\thumanLines: [text.endsWith('\\n') ? text.slice(0, -1) : text],\n\t\t});\n\t\treturn { exitCode: ExitCode.OK };\n\t});\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAyDA,MAAM,oBAAoB,YACzB,OAAO,IAAI;CACV,WAAW,aAAa,SAAS,MAAM;CACvC,QAAQ,UACP,IAAI,iBAAiB;EACpB,SAAS,+BAA+B;EACxC;CACD,CAAC;AACH,CAAC;AAEF,MAAM,gBAAgB,KAAa,SAClC,OAAO,IAAI;CACV,WAAW,cAAc,KAAK,MAAM,MAAM;CAC1C,QAAQ,UACP,IAAI,iBAAiB;EAAE,SAAS,gCAAgC;EAAO;CAAM,CAAC;AAChF,CAAC;;;;;;;;;AAUF,MAAM,iBAAiB,MAAc,YACpC,OAAO,IAAI;CACV,WAAW,eAAe,IAAI;CAC9B,QAAQ,UACP,IAAI,iBAAiB;EAAE,SAAS,gBAAgB,QAAQ;EAAqB;CAAM,CAAC;AACtF,CAAC;AAEF,MAAa,cACZ,UACA,SAC4C;CAC5C,MAAM,SAAS,iBAAiB;CAChC,OAAO,OAAO,IAAI,aAAa;EAC9B,MAAM,UAAU,KAAK,IAAI;EAMzB,MAAM,WAAW,OAAO,OAAO,KAAK,OAAO,KAAK,KAAK,UAAU,CAAC;EAChE,IAAI,KAAK,UAAU,QAAQ,GAC1B,OAAO,OAAO,OAAO,KAAK,uBAAuB,QAAQ,CAAC;EAE3D,MAAM,QAAS,SAAS,MAA8D;EACtF,MAAM,oBAAoB,yBAAyB,UAAU,KAAK;EAClE,MAAM,UAAUA,QAAY,kBAAkB,WAAW,kBAAkB;EAS3E,IAAI,EAAC,OAHmB,wBAAwB,kBAAkB,UAAU,CAAC,CAAC,KAC7E,OAAO,YAAY,OAAO,QAAQ;GAAE,MAAM;GAAO,KAAK;GAAM,UAAU;EAAK,CAAC,CAAC,CAC9E,EAAA,CACc,MAKb,OAAO,aAAa,KAAK,YAAY,QAAQ;EAG9C,MAAM,OAAO,OAAO,iBAAiB,OAAO;EAC5C,MAAM,OAAO,OAAO,cAAc,MAAM,OAAO;EAE/C,IAAI,KAAK,QAAQ,KAAA,GAAW;GAC3B,OAAO,aAAa,KAAK,KAAK,IAAI;GAClC,OAAO,YAAY,KAAK,IAAI,KAAK,YAAY;IAC5C,SAAS;IACT,WAAW,KAAK,IAAI,IAAI;IACxB;IACA,YAAY,CAAC,sBAAsB,KAAK,KAAK;GAC9C,CAAC;GACD,OAAO,EAAE,UAAU,SAAS,GAAG;EAChC;EAMA,OAAO,YAAY,KAAK,IAAI,KAAK,YAAY;GAC5C,SAAS;GACT,WAAW,KAAK,IAAI,IAAI;GACxB;GACA,YAAY,CAAC,KAAK,SAAS,IAAI,IAAI,KAAK,MAAM,GAAG,EAAE,IAAI,IAAI;EAC5D,CAAC;EACD,OAAO,EAAE,UAAU,SAAS,GAAG;CAChC,CAAC;AACF"}
@@ -1 +0,0 @@
1
- import { Effect, FileSystem, Schema } from "effect";
@@ -1,107 +0,0 @@
1
- import { decodeJsonTextSync } 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/id-config.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 `networks.<name>` entry — the connection coordinates for a
11
- * network. Mirrors `SuiNetworkConfigEntry` (plugins/sui/codegen.ts);
12
- * `rpc` is the load-bearing field the app reads synchronously at module
13
- * load. */
14
- const IdConfigNetworkSchema = Schema.Struct({
15
- rpc: Schema.String,
16
- chainId: Schema.optional(Schema.String),
17
- faucet: Schema.optional(Schema.NullOr(Schema.String)),
18
- graphql: Schema.optional(Schema.NullOr(Schema.String))
19
- });
20
- /** One `packages.<name>` entry — the resolved package id plus any
21
- * resolved object captures for the active network. */
22
- const IdConfigPackageSchema = Schema.Struct({
23
- id: Schema.String,
24
- objects: Schema.optional(Schema.Record(Schema.String, Schema.String))
25
- });
26
- /** A JSON-serialisable value carried in the generic `values` channel.
27
- * The whole id-config round-trips through `JSON.stringify` into the
28
- * vite `define` global, so the value space is exactly JSON. */
29
- const IdConfigJsonSchema = Schema.suspend(() => Schema.Union([
30
- Schema.Null,
31
- Schema.String,
32
- Schema.Number,
33
- Schema.Boolean,
34
- Schema.Array(IdConfigJsonSchema),
35
- Schema.Record(Schema.String, IdConfigJsonSchema)
36
- ]));
37
- /** The generic resolver channel: a two-level namespaced map of arbitrary
38
- * JSON values a plugin contributes at boot and the committed `config.ts`
39
- * reads back via `resolveValue(namespace, key)`. This is the open-ended
40
- * sibling to the typed `network`/`networks`/`packages`/`accounts`/
41
- * `mvrOverrides` fields — it carries the plugin live values the fixed
42
- * channel can't (deepbook pool ids, coin types, walrus/seal endpoints). */
43
- const IdConfigValuesSchema = Schema.Record(Schema.String, Schema.Record(Schema.String, IdConfigJsonSchema));
44
- /** The id-config interchange shape. The whole document is data — no
45
- * functions, no devstack imports — so it round-trips through JSON and a
46
- * `JSON.stringify` into a vite `define` global. */
47
- const IdConfigSchema = Schema.Struct({
48
- /** Active network name (the `networks.<name>` key the app reads).
49
- * Load-bearing — an id-config MUST name its network + connection. */
50
- network: Schema.String,
51
- networks: Schema.Record(Schema.String, IdConfigNetworkSchema),
52
- /** Resolved package ids (+ object captures). Optional, default `{}`: an
53
- * id-config may have zero packages (a network-only stack, or a
54
- * hand-authored deploy file), and the boot writer always emits the key
55
- * anyway — defaulting keeps the injected blob well-formed so the app's
56
- * resolvers throw `DevstackConfigMissingError` rather than a raw
57
- * TypeError on a missing section. */
58
- packages: Schema.Record(Schema.String, IdConfigPackageSchema).pipe(Schema.withDecodingDefaultKey(Effect.succeed({}))),
59
- accounts: Schema.Record(Schema.String, Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed({}))),
60
- /** MVR placeholder (`@local/<slug>`) → resolved id, for the active
61
- * network. An app feeds this straight into dapp-kit's
62
- * `mvr.overrides.packages`. Optional, default `{}` (see `packages`). */
63
- mvrOverrides: Schema.Record(Schema.String, Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed({}))),
64
- /** Generic resolver channel — `values[namespace][key]` carries
65
- * arbitrary live plugin JSON the typed fields above can't. Optional
66
- * so older id-config files (no `values`) still decode. */
67
- values: Schema.optional(IdConfigValuesSchema)
68
- });
69
- /** Failure reading or decoding an id-config file. */
70
- var IdConfigError = class extends Schema.TaggedErrorClass()("IdConfigError", {
71
- source: Schema.String,
72
- message: Schema.String,
73
- cause: Schema.optional(Schema.Defect)
74
- }) {};
75
- /** Canonical filename of the per-stack id-config under
76
- * `.devstack/stacks/<name>/`. */
77
- const ID_CONFIG_FILENAME = "devstack-ids.json";
78
- /** Write the id-config as pretty JSON. Idempotent at the byte level for
79
- * identical input (sorted by the caller's assembly order). */
80
- const writeIdConfig = (path, config) => Effect.gen(function* () {
81
- const text = `${JSON.stringify(config, null, 2)}\n`;
82
- yield* atomicWriteFile(path, new TextEncoder().encode(text), { mode: 420 }).pipe(Effect.mapError((cause) => new IdConfigError({
83
- source: path,
84
- message: "failed to write id-config",
85
- cause
86
- })));
87
- });
88
- /** Project a runtime-decode issue into the typed `IdConfigError`. */
89
- const mkIdConfigError = (issue) => new IdConfigError({
90
- source: issue.source,
91
- message: issue.message,
92
- ...issue.cause === void 0 ? {} : { cause: issue.cause }
93
- });
94
- /** Decode + validate an id-config from already-read JSON text. The single
95
- * schema-decode seam every reader shares (the Vite plugin, the `dump-ids`
96
- * verb, the codegen verb) so the parse-and-validate decision lives in ONE
97
- * place rather than each caller hand-rolling `JSON.parse`. Throws
98
- * `IdConfigError` on malformed JSON or a shape that violates
99
- * `IdConfigSchema`. */
100
- const decodeIdConfig = (text) => decodeJsonTextSync(IdConfigSchema, text, {
101
- source: ID_CONFIG_FILENAME,
102
- mkError: mkIdConfigError
103
- });
104
- //#endregion
105
- export { ID_CONFIG_FILENAME, UNRESOLVED_ID, decodeIdConfig, writeIdConfig };
106
-
107
- //# sourceMappingURL=id-config.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"id-config.mjs","names":[],"sources":["../../../src/orchestrators/codegen/id-config.ts"],"sourcesContent":["// The id-config 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, id-config?) → 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>/devstack-ids.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_IDS__` global.\n// - INJECTOR: the vite plugin reads the live file (dev) or a committed\n// id-config file (prod, via `ids`/`DEVSTACK_IDS_FILE`) and\n// `define`s the global.\n//\n// The shape is the id-half of today's `config.ts`: networks (rpc + the\n// rest), per-package ids (+ captured object ids), account addresses, and\n// the MVR placeholder → id override map an app feeds dapp-kit.\n\nimport { Effect, FileSystem, Schema } from 'effect';\n\nimport { atomicWriteFile } from '../../substrate/runtime/atomic-write.ts';\nimport {\n\tdecodeJsonTextSync,\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 id-config round-trips through\n * `JSON.stringify` into the injected `__DEVSTACK_IDS__` 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 `networks.<name>` entry — the connection coordinates for a\n * network. Mirrors `SuiNetworkConfigEntry` (plugins/sui/codegen.ts);\n * `rpc` is the load-bearing field the app reads synchronously at module\n * load. */\nexport const IdConfigNetworkSchema = Schema.Struct({\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});\n\n/** One `packages.<name>` entry — the resolved package id plus any\n * resolved object captures for the active network. */\nexport const IdConfigPackageSchema = 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 id-config 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 `IdConfigSchema`; `Codec<T>` defaults the decode/\n// encode services to `never`, matching the real (service-free) recursion.\nexport const IdConfigJsonSchema: 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(IdConfigJsonSchema),\n\t\tSchema.Record(Schema.String, IdConfigJsonSchema),\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`/`accounts`/\n * `mvrOverrides` fields — it carries the plugin live values the fixed\n * channel can't (deepbook pool ids, coin types, walrus/seal endpoints). */\nexport const IdConfigValuesSchema = Schema.Record(\n\tSchema.String,\n\tSchema.Record(Schema.String, IdConfigJsonSchema),\n);\n\n/** The id-config interchange shape. 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 IdConfigSchema = Schema.Struct({\n\t/** Active network name (the `networks.<name>` key the app reads).\n\t * Load-bearing — an id-config MUST name its network + connection. */\n\tnetwork: Schema.String,\n\tnetworks: Schema.Record(Schema.String, IdConfigNetworkSchema),\n\t/** Resolved package ids (+ object captures). Optional, default `{}`: an\n\t * id-config 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, IdConfigPackageSchema).pipe(\n\t\tSchema.withDecodingDefaultKey(Effect.succeed({})),\n\t),\n\taccounts: Schema.Record(Schema.String, Schema.String).pipe(\n\t\tSchema.withDecodingDefaultKey(Effect.succeed({})),\n\t),\n\t/** MVR placeholder (`@local/<slug>`) → resolved id, for the active\n\t * network. An app feeds this straight into dapp-kit's\n\t * `mvr.overrides.packages`. Optional, default `{}` (see `packages`). */\n\tmvrOverrides: Schema.Record(Schema.String, Schema.String).pipe(\n\t\tSchema.withDecodingDefaultKey(Effect.succeed({})),\n\t),\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 id-config files (no `values`) still decode. */\n\tvalues: Schema.optional(IdConfigValuesSchema),\n});\n\nexport type IdConfig = typeof IdConfigSchema.Type;\nexport type IdConfigNetwork = typeof IdConfigNetworkSchema.Type;\nexport type IdConfigPackage = typeof IdConfigPackageSchema.Type;\nexport type IdConfigValues = typeof IdConfigValuesSchema.Type;\n\n// -----------------------------------------------------------------------------\n// Error\n// -----------------------------------------------------------------------------\n\n/** Failure reading or decoding an id-config file. */\nexport class IdConfigError extends Schema.TaggedErrorClass<IdConfigError>()('IdConfigError', {\n\tsource: Schema.String,\n\tmessage: Schema.String,\n\tcause: Schema.optional(Schema.Defect),\n}) {}\n\n// -----------------------------------------------------------------------------\n// Read / write helpers\n// -----------------------------------------------------------------------------\n\n/** Canonical filename of the per-stack id-config under\n * `.devstack/stacks/<name>/`. */\nexport const ID_CONFIG_FILENAME = 'devstack-ids.json';\n\n/** Write the id-config as pretty JSON. Idempotent at the byte level for\n * identical input (sorted by the caller's assembly order). */\nexport const writeIdConfig = (\n\tpath: string,\n\tconfig: IdConfig,\n): Effect.Effect<void, IdConfigError, 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// id-config 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) => new IdConfigError({ source: path, message: 'failed to write id-config', cause }),\n\t\t\t),\n\t\t);\n\t});\n\n/** Project a runtime-decode issue into the typed `IdConfigError`. */\nconst mkIdConfigError = (issue: RuntimeDecodeIssue): IdConfigError =>\n\tnew IdConfigError({\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 an id-config from already-read JSON text. The single\n * schema-decode seam every reader shares (the Vite plugin, the `dump-ids`\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 * `IdConfigError` on malformed JSON or a shape that violates\n * `IdConfigSchema`. */\nexport const decodeIdConfig = (text: string): IdConfig =>\n\tdecodeJsonTextSync(IdConfigSchema, text, {\n\t\tsource: ID_CONFIG_FILENAME,\n\t\tmkError: mkIdConfigError,\n\t});\n"],"mappings":";;;;;;;;AAwCA,MAAa,gBAAgB;;;;;AA8B7B,MAAa,wBAAwB,OAAO,OAAO;CAClD,KAAK,OAAO;CACZ,SAAS,OAAO,SAAS,OAAO,MAAM;CACtC,QAAQ,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CACpD,SAAS,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;AACtD,CAAC;;;AAID,MAAa,wBAAwB,OAAO,OAAO;CAClD,IAAI,OAAO;CACX,SAAS,OAAO,SAAS,OAAO,OAAO,OAAO,QAAQ,OAAO,MAAM,CAAC;AACrE,CAAC;;;;AASD,MAAa,qBAA8C,OAAO,cACjE,OAAO,MAAM;CACZ,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO,MAAM,kBAAkB;CAC/B,OAAO,OAAO,OAAO,QAAQ,kBAAkB;AAChD,CAAC,CACF;;;;;;;AAQA,MAAa,uBAAuB,OAAO,OAC1C,OAAO,QACP,OAAO,OAAO,OAAO,QAAQ,kBAAkB,CAChD;;;;AAKA,MAAa,iBAAiB,OAAO,OAAO;;;CAG3C,SAAS,OAAO;CAChB,UAAU,OAAO,OAAO,OAAO,QAAQ,qBAAqB;;;;;;;CAO5D,UAAU,OAAO,OAAO,OAAO,QAAQ,qBAAqB,CAAC,CAAC,KAC7D,OAAO,uBAAuB,OAAO,QAAQ,CAAC,CAAC,CAAC,CACjD;CACA,UAAU,OAAO,OAAO,OAAO,QAAQ,OAAO,MAAM,CAAC,CAAC,KACrD,OAAO,uBAAuB,OAAO,QAAQ,CAAC,CAAC,CAAC,CACjD;;;;CAIA,cAAc,OAAO,OAAO,OAAO,QAAQ,OAAO,MAAM,CAAC,CAAC,KACzD,OAAO,uBAAuB,OAAO,QAAQ,CAAC,CAAC,CAAC,CACjD;;;;CAIA,QAAQ,OAAO,SAAS,oBAAoB;AAC7C,CAAC;;AAYD,IAAa,gBAAb,cAAmC,OAAO,iBAAgC,CAAC,CAAC,iBAAiB;CAC5F,QAAQ,OAAO;CACf,SAAS,OAAO;CAChB,OAAO,OAAO,SAAS,OAAO,MAAM;AACrC,CAAC,CAAC,CAAC,CAAC;;;AAQJ,MAAa,qBAAqB;;;AAIlC,MAAa,iBACZ,MACA,WAEA,OAAO,IAAI,aAAa;CACvB,MAAM,OAAO,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE;CAOhD,OAAO,gBAAgB,MANT,IAAI,YAAY,CAAC,CAAC,OAAO,IAMN,GAAG,EAAE,MAAM,IAAM,CAAC,CAAC,CAAC,KACpD,OAAO,UACL,UAAU,IAAI,cAAc;EAAE,QAAQ;EAAM,SAAS;EAA6B;CAAM,CAAC,CAC3F,CACD;AACD,CAAC;;AAGF,MAAM,mBAAmB,UACxB,IAAI,cAAc;CACjB,QAAQ,MAAM;CACd,SAAS,MAAM;CACf,GAAI,MAAM,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,MAAM,MAAM;AAC3D,CAAC;;;;;;;AAQF,MAAa,kBAAkB,SAC9B,mBAAmB,gBAAgB,MAAM;CACxC,QAAQ;CACR,SAAS;AACV,CAAC"}
@@ -1,32 +0,0 @@
1
- import { stackSubpath } from "../../substrate/runtime/paths.mjs";
2
- import { join, resolve } from "node:path";
3
- //#region src/orchestrators/codegen/output-location.ts
4
- /** The app-rooted base under which codegen authors its per-stack subtrees:
5
- * `<appRoot>/.devstack`. Mirrors the substrate's `RuntimeRoot` base, but
6
- * rooted at the app source tree (codegen output lives with the user's
7
- * source, not in engine-private state). The `stacks/<stack>/...` shape is
8
- * composed by the shared `stackSubpath` so the two authors (substrate at
9
- * `RuntimeRoot`, codegen here) never drift. */
10
- const codegenStacksBase = (appRoot) => resolve(appRoot, ".devstack");
11
- /** Resolve `<appRoot>/.devstack/stacks/<stack>/generated-extras` — the
12
- * dev-only + secret tree. Always under `.devstack`, so dev-extras never
13
- * land in the committed `src/generated/` tree. */
14
- const extrasDirFor = (appRoot, stack) => stackSubpath(join, codegenStacksBase(appRoot), stack, "generated-extras");
15
- /**
16
- * Resolve the LIVE codegen output location for a stack. See the module
17
- * header for the rule. Pure — same inputs always yield the same absolute
18
- * path. A single fixed per-stack rule (no override): every live stack's
19
- * dev-only `generated-extras` tree lands under
20
- * `<appRoot>/.devstack/stacks/<effectiveStack>/generated-extras`. Boot
21
- * writes ONLY this tree (`emitExtras`); it never emits the committed
22
- * `src/generated` tree — that is owned solely by the stack-free `codegen`
23
- * verb (which resolves its own output path directly).
24
- */
25
- const resolveCodegenOutput = (input) => {
26
- const { appRoot, effectiveStack } = input;
27
- return { extrasDir: extrasDirFor(appRoot, effectiveStack) };
28
- };
29
- //#endregion
30
- export { resolveCodegenOutput };
31
-
32
- //# sourceMappingURL=output-location.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"output-location.mjs","names":[],"sources":["../../../src/orchestrators/codegen/output-location.ts"],"sourcesContent":["// Per-stack codegen output-location resolver.\n//\n// Boot no longer runs codegen — `up` / `apply` write an id-config file to\n// `.devstack/stacks/<stack>/` instead. This resolver still owns the per-\n// stack `.devstack` dev tree (the `generated-extras` overlay the Vite\n// `@devstack-dev` alias reads). The COMMITTED `src/generated` tree is\n// written ONLY by the stack-free `codegen` verb, wired separately.\n//\n// This pure function is the single decision point for the per-stack dev\n// output dir: it maps (appRoot, effective stack) → the `.devstack` dir\n// codegen-adjacent dev artifacts own for THIS stack.\n//\n// - Every live stack → `<appRoot>/.devstack/stacks/<effectiveStack>/generated-extras`\n// — a sibling of that stack's manifest at\n// `.devstack/stacks/<stack>/manifest.json`, already gitignored +\n// tsconfig-excluded. Two stacks of the same app therefore never clobber\n// each other's dev-only artifacts. There is NO output-dir override:\n// `defineDevstack({ codegen })` exposes no public surface for the dev\n// tree (boot writes only the per-stack `generated-extras` overlay + the\n// id-config file), so the location is a single fixed per-stack rule.\n//\n// The resolved absolute `extrasDir` is recorded in the per-stack manifest\n// (`codegen.extrasDir`) so the reader (the Vite plugin's `@devstack-dev`\n// alias) consults the SAME location the writer chose. The committed\n// `src/generated` bindings tree is NOT recorded — the `@generated` alias\n// always resolves to it directly. Pure + unit-testable; no `process.env`,\n// no I/O.\n\nimport { join, resolve } from 'node:path';\n\nimport { stackSubpath } from '../../substrate/runtime/paths.ts';\n\n/** The app-rooted base under which codegen authors its per-stack subtrees:\n * `<appRoot>/.devstack`. Mirrors the substrate's `RuntimeRoot` base, but\n * rooted at the app source tree (codegen output lives with the user's\n * source, not in engine-private state). The `stacks/<stack>/...` shape is\n * composed by the shared `stackSubpath` so the two authors (substrate at\n * `RuntimeRoot`, codegen here) never drift. */\nconst codegenStacksBase = (appRoot: string): string => resolve(appRoot, '.devstack');\n\nexport interface ResolveCodegenOutputInput {\n\t/** The user application root — codegen output is resolved against\n\t * it. */\n\treadonly appRoot: string;\n\t/** The stack the supervisor is actually running (the resolved\n\t * identity's stack: explicit `--stack`/`$DEVSTACK_STACK` >\n\t * `config.stackName` > inferred). */\n\treadonly effectiveStack: string;\n}\n\nexport interface ResolvedCodegenOutput {\n\t/** Absolute path to the dev-only + secret `generated-extras` tree\n\t * for THIS stack — always\n\t * `<appRoot>/.devstack/stacks/<effectiveStack>/generated-extras`\n\t * (gitignored + tsconfig-excluded already). Recorded in the\n\t * manifest as `codegen.extrasDir`; reached via the `@devstack-dev`\n\t * Vite alias. */\n\treadonly extrasDir: string;\n}\n\n/** Resolve `<appRoot>/.devstack/stacks/<stack>/generated-extras` — the\n * dev-only + secret tree. Always under `.devstack`, so dev-extras never\n * land in the committed `src/generated/` tree. */\nconst extrasDirFor = (appRoot: string, stack: string): string =>\n\tstackSubpath(join, codegenStacksBase(appRoot), stack, 'generated-extras');\n\n/**\n * Resolve the LIVE codegen output location for a stack. See the module\n * header for the rule. Pure — same inputs always yield the same absolute\n * path. A single fixed per-stack rule (no override): every live stack's\n * dev-only `generated-extras` tree lands under\n * `<appRoot>/.devstack/stacks/<effectiveStack>/generated-extras`. Boot\n * writes ONLY this tree (`emitExtras`); it never emits the committed\n * `src/generated` tree — that is owned solely by the stack-free `codegen`\n * verb (which resolves its own output path directly).\n */\nexport const resolveCodegenOutput = (input: ResolveCodegenOutputInput): ResolvedCodegenOutput => {\n\tconst { appRoot, effectiveStack } = input;\n\treturn {\n\t\textrasDir: extrasDirFor(appRoot, effectiveStack),\n\t};\n};\n"],"mappings":";;;;;;;;;AAsCA,MAAM,qBAAqB,YAA4B,QAAQ,SAAS,WAAW;;;;AAyBnF,MAAM,gBAAgB,SAAiB,UACtC,aAAa,MAAM,kBAAkB,OAAO,GAAG,OAAO,kBAAkB;;;;;;;;;;;AAYzE,MAAa,wBAAwB,UAA4D;CAChG,MAAM,EAAE,SAAS,mBAAmB;CACpC,OAAO,EACN,WAAW,aAAa,SAAS,cAAc,EAChD;AACD"}