@dreamboard-games/cli 0.1.30-alpha.12 → 0.1.30-alpha.13

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 (143) hide show
  1. package/README.md +2 -6
  2. package/dist/agent-verifier/agent-workspace-verifier.mjs +18 -17
  3. package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -1
  4. package/dist/agent-verifier/{chunk-TLYGTHXU.mjs → chunk-5GCZZ6NW.mjs} +3 -3
  5. package/dist/agent-verifier/{chunk-YR664DJX.mjs → chunk-A67WUYN2.mjs} +42 -68
  6. package/dist/agent-verifier/chunk-A67WUYN2.mjs.map +1 -0
  7. package/dist/agent-verifier/chunk-AXXUGU7Q.mjs +255 -0
  8. package/dist/agent-verifier/chunk-AXXUGU7Q.mjs.map +1 -0
  9. package/dist/agent-verifier/chunk-CO3BRUD6.mjs +342 -0
  10. package/dist/agent-verifier/chunk-CO3BRUD6.mjs.map +1 -0
  11. package/dist/agent-verifier/chunk-DPYC2NDB.mjs +59 -0
  12. package/dist/agent-verifier/chunk-DPYC2NDB.mjs.map +1 -0
  13. package/dist/agent-verifier/{chunk-4GU3PCHV.mjs → chunk-DWLTCUUX.mjs} +576 -393
  14. package/dist/agent-verifier/chunk-DWLTCUUX.mjs.map +1 -0
  15. package/dist/agent-verifier/{chunk-COB56ESI.mjs → chunk-G2ECODRB.mjs} +2 -2
  16. package/dist/agent-verifier/{chunk-6XRC5PWB.mjs → chunk-H3XNWKJU.mjs} +217 -232
  17. package/dist/agent-verifier/chunk-H3XNWKJU.mjs.map +1 -0
  18. package/dist/agent-verifier/{chunk-Z6OZWUIZ.mjs → chunk-HLHT57AW.mjs} +64 -16
  19. package/dist/agent-verifier/chunk-HLHT57AW.mjs.map +1 -0
  20. package/dist/agent-verifier/{chunk-YDIOW2BO.mjs → chunk-INIK6LHK.mjs} +2 -2
  21. package/dist/agent-verifier/{chunk-VLOIZDR6.mjs → chunk-JPN62WDY.mjs} +199 -190
  22. package/dist/agent-verifier/chunk-JPN62WDY.mjs.map +1 -0
  23. package/dist/agent-verifier/{chunk-UWJIZML3.mjs → chunk-LKQ557TJ.mjs} +30 -23
  24. package/dist/agent-verifier/chunk-LKQ557TJ.mjs.map +1 -0
  25. package/dist/agent-verifier/{chunk-NAK77WXW.mjs → chunk-MYMVXTZT.mjs} +4 -5
  26. package/dist/agent-verifier/chunk-MYMVXTZT.mjs.map +1 -0
  27. package/dist/agent-verifier/{chunk-UIJ2NDG6.mjs → chunk-NFL3Z4Z7.mjs} +31 -238
  28. package/dist/agent-verifier/chunk-NFL3Z4Z7.mjs.map +1 -0
  29. package/dist/agent-verifier/{chunk-XKCJBIRY.mjs → chunk-QD4SQNUP.mjs} +2 -2
  30. package/dist/agent-verifier/{chunk-IAYRNVUC.mjs → chunk-RDYXWXXC.mjs} +1 -3
  31. package/dist/agent-verifier/{chunk-QBAF7EYR.mjs → chunk-TTB7AIHZ.mjs} +4 -4
  32. package/dist/agent-verifier/{chunk-QBAF7EYR.mjs.map → chunk-TTB7AIHZ.mjs.map} +1 -1
  33. package/dist/agent-verifier/chunk-V6AQDR7W.mjs +89 -0
  34. package/dist/agent-verifier/chunk-V6AQDR7W.mjs.map +1 -0
  35. package/dist/agent-verifier/{chunk-RHI6S4SU.mjs → chunk-V7ABTZXW.mjs} +1 -3
  36. package/dist/agent-verifier/{chunk-RHI6S4SU.mjs.map → chunk-V7ABTZXW.mjs.map} +1 -1
  37. package/dist/agent-verifier/chunk-WAFBU5U7.mjs +467 -0
  38. package/dist/agent-verifier/chunk-WAFBU5U7.mjs.map +1 -0
  39. package/dist/agent-verifier/{chunk-3IJBOLGT.mjs → chunk-WSIYUUSD.mjs} +2 -2
  40. package/dist/agent-verifier/{compile-WZ7X6I2A.mjs → compile-H6KCBCVH.mjs} +22 -18
  41. package/dist/agent-verifier/compile-H6KCBCVH.mjs.map +1 -0
  42. package/dist/agent-verifier/{global-config-XHL7BCKN.mjs → global-config-6UGFPLDA.mjs} +4 -3
  43. package/dist/agent-verifier/{keychain-backend-A3MRWLPF.mjs → keychain-backend-BQLW5VEC.mjs} +11 -6
  44. package/dist/agent-verifier/keychain-backend-BQLW5VEC.mjs.map +1 -0
  45. package/dist/agent-verifier/{local-files-ZW52HSVT.mjs → local-files-WPHUV6GU.mjs} +6 -6
  46. package/dist/agent-verifier/{materialize-workspace-BKZLLFI4.mjs → materialize-workspace-EHCQB4UU.mjs} +17 -17
  47. package/dist/agent-verifier/materialize-workspace-EHCQB4UU.mjs.map +1 -0
  48. package/dist/agent-verifier/{reducer-bundle-preflight-7NYZF5ZT.mjs → reducer-bundle-preflight-3DSXIELT.mjs} +4 -4
  49. package/dist/agent-verifier/reducer-contract-preflight-FQB7M4PU.mjs +11 -0
  50. package/dist/agent-verifier/{reducer-native-test-harness-D4VWPIAC.mjs → reducer-native-test-harness-GY2CCQWN.mjs} +12 -9
  51. package/dist/agent-verifier/{static-scaffold-JCRBDKEH.mjs → static-scaffold-3O543YTZ.mjs} +7 -9
  52. package/dist/agent-verifier/{sync-ELLJEWMB.mjs → sync-URBFMM6H.mjs} +24 -22
  53. package/dist/agent-verifier/{sync-ELLJEWMB.mjs.map → sync-URBFMM6H.mjs.map} +1 -1
  54. package/dist/agent-verifier/{test-OSXBPLSP.mjs → test-LQAGEQLY.mjs} +19 -17
  55. package/dist/agent-verifier/test-LQAGEQLY.mjs.map +1 -0
  56. package/dist/agent-verifier/{workspace-codegen-WPZHMATU.mjs → workspace-codegen-4IWICKLB.mjs} +3 -3
  57. package/dist/agent-verifier/{workspace-dependencies-ULZZZPNX.mjs → workspace-dependencies-ZMHPHVQV.mjs} +2 -2
  58. package/dist/authoring-compatibility-internal.js +12 -0
  59. package/dist/{agent-verifier/chunk-W2MDP5ZN.mjs → chunk-AVOAT522.js} +118 -21
  60. package/dist/chunk-AVOAT522.js.map +1 -0
  61. package/dist/chunk-EV7Q6BIF.js +4298 -0
  62. package/dist/chunk-EV7Q6BIF.js.map +1 -0
  63. package/dist/chunk-FFO2IJL3.js +204 -0
  64. package/dist/chunk-FFO2IJL3.js.map +1 -0
  65. package/dist/{chunk-P5TITCD3.js → chunk-GS6A7T53.js} +2240 -4554
  66. package/dist/chunk-GS6A7T53.js.map +1 -0
  67. package/dist/{global-config-WPJRXVDO.js → global-config-NLGAFSRU.js} +3 -2
  68. package/dist/global-config-NLGAFSRU.js.map +1 -0
  69. package/dist/index.js +1371 -3545
  70. package/dist/index.js.map +1 -1
  71. package/dist/internal.js +14 -8
  72. package/dist/{keychain-backend-JHTXAKWC.js → keychain-backend-47LZ5IX5.js} +11 -6
  73. package/dist/keychain-backend-47LZ5IX5.js.map +1 -0
  74. package/package.json +9 -19
  75. package/release/authoring-release-set.json +38 -0
  76. package/skills/dreamboard/references/manifest-authoring.md +11 -3
  77. package/dist/agent-verifier/chunk-4GU3PCHV.mjs.map +0 -1
  78. package/dist/agent-verifier/chunk-6XRC5PWB.mjs.map +0 -1
  79. package/dist/agent-verifier/chunk-G42BGGG2.mjs +0 -70
  80. package/dist/agent-verifier/chunk-G42BGGG2.mjs.map +0 -1
  81. package/dist/agent-verifier/chunk-KK47X7RV.mjs +0 -14
  82. package/dist/agent-verifier/chunk-KK47X7RV.mjs.map +0 -1
  83. package/dist/agent-verifier/chunk-NAK77WXW.mjs.map +0 -1
  84. package/dist/agent-verifier/chunk-UIJ2NDG6.mjs.map +0 -1
  85. package/dist/agent-verifier/chunk-UWJIZML3.mjs.map +0 -1
  86. package/dist/agent-verifier/chunk-VLOIZDR6.mjs.map +0 -1
  87. package/dist/agent-verifier/chunk-W2MDP5ZN.mjs.map +0 -1
  88. package/dist/agent-verifier/chunk-YR664DJX.mjs.map +0 -1
  89. package/dist/agent-verifier/chunk-Z6OZWUIZ.mjs.map +0 -1
  90. package/dist/agent-verifier/compile-WZ7X6I2A.mjs.map +0 -1
  91. package/dist/agent-verifier/keychain-backend-A3MRWLPF.mjs.map +0 -1
  92. package/dist/agent-verifier/materialize-workspace-BKZLLFI4.mjs.map +0 -1
  93. package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs +0 -11
  94. package/dist/agent-verifier/test-OSXBPLSP.mjs.map +0 -1
  95. package/dist/chunk-GXM7RRZJ.js +0 -433
  96. package/dist/chunk-GXM7RRZJ.js.map +0 -1
  97. package/dist/chunk-P5TITCD3.js.map +0 -1
  98. package/dist/dev-host/components/drawer.tsx +0 -132
  99. package/dist/dev-host/components/input.tsx +0 -21
  100. package/dist/dev-host/dev-api-proxy-plugin.ts +0 -328
  101. package/dist/dev-host/dev-author-dom-warnings.ts +0 -100
  102. package/dist/dev-host/dev-diagnostics.ts +0 -62
  103. package/dist/dev-host/dev-fallback-stylesheet.ts +0 -53
  104. package/dist/dev-host/dev-hmr-guard-plugin.ts +0 -47
  105. package/dist/dev-host/dev-host-controller.ts +0 -674
  106. package/dist/dev-host/dev-host-player-query.ts +0 -17
  107. package/dist/dev-host/dev-host-session-transport.ts +0 -52
  108. package/dist/dev-host/dev-host-storage.ts +0 -56
  109. package/dist/dev-host/dev-log-relay-plugin.ts +0 -510
  110. package/dist/dev-host/dev-runtime-config.ts +0 -14
  111. package/dist/dev-host/dev-runtime-platform.ts +0 -335
  112. package/dist/dev-host/dev-virtual-modules-plugin.ts +0 -64
  113. package/dist/dev-host/host-main.css +0 -224
  114. package/dist/dev-host/host-main.tsx +0 -954
  115. package/dist/dev-host/index.html +0 -56
  116. package/dist/dev-host/lib/utils.ts +0 -6
  117. package/dist/dev-host/plugin-main.ts +0 -61
  118. package/dist/dev-host/plugin.html +0 -24
  119. package/dist/dev-host/shared-styles.css +0 -144
  120. package/dist/dev-host/start-dev-server.ts +0 -140
  121. package/dist/dev-host/virtual-modules.d.ts +0 -27
  122. package/dist/keychain-backend-JHTXAKWC.js.map +0 -1
  123. /package/dist/agent-verifier/{chunk-TLYGTHXU.mjs.map → chunk-5GCZZ6NW.mjs.map} +0 -0
  124. /package/dist/agent-verifier/{chunk-COB56ESI.mjs.map → chunk-G2ECODRB.mjs.map} +0 -0
  125. /package/dist/agent-verifier/{chunk-YDIOW2BO.mjs.map → chunk-INIK6LHK.mjs.map} +0 -0
  126. /package/dist/agent-verifier/{chunk-XKCJBIRY.mjs.map → chunk-QD4SQNUP.mjs.map} +0 -0
  127. /package/dist/agent-verifier/{chunk-IAYRNVUC.mjs.map → chunk-RDYXWXXC.mjs.map} +0 -0
  128. /package/dist/agent-verifier/{chunk-3IJBOLGT.mjs.map → chunk-WSIYUUSD.mjs.map} +0 -0
  129. /package/dist/agent-verifier/{global-config-XHL7BCKN.mjs.map → global-config-6UGFPLDA.mjs.map} +0 -0
  130. /package/dist/agent-verifier/{local-files-ZW52HSVT.mjs.map → local-files-WPHUV6GU.mjs.map} +0 -0
  131. /package/dist/agent-verifier/{reducer-bundle-preflight-7NYZF5ZT.mjs.map → reducer-bundle-preflight-3DSXIELT.mjs.map} +0 -0
  132. /package/dist/agent-verifier/{reducer-contract-preflight-COD2CO22.mjs.map → reducer-contract-preflight-FQB7M4PU.mjs.map} +0 -0
  133. /package/dist/agent-verifier/{reducer-native-test-harness-D4VWPIAC.mjs.map → reducer-native-test-harness-GY2CCQWN.mjs.map} +0 -0
  134. /package/dist/agent-verifier/{static-scaffold-JCRBDKEH.mjs.map → static-scaffold-3O543YTZ.mjs.map} +0 -0
  135. /package/dist/agent-verifier/{workspace-codegen-WPZHMATU.mjs.map → workspace-codegen-4IWICKLB.mjs.map} +0 -0
  136. /package/dist/agent-verifier/{workspace-dependencies-ULZZZPNX.mjs.map → workspace-dependencies-ZMHPHVQV.mjs.map} +0 -0
  137. /package/dist/{global-config-WPJRXVDO.js.map → authoring-compatibility-internal.js.map} +0 -0
  138. /package/{dist/scaffold → scaffold}/assets/static/app/tsconfig.framework.json +0 -0
  139. /package/{dist/scaffold → scaffold}/assets/static/app/tsconfig.json +0 -0
  140. /package/{dist/scaffold → scaffold}/assets/static/ui/index.tsx +0 -0
  141. /package/{dist/scaffold → scaffold}/assets/static/ui/style.css +0 -0
  142. /package/{dist/scaffold → scaffold}/assets/static/ui/tsconfig.framework.json +0 -0
  143. /package/{dist/scaffold → scaffold}/assets/static/ui/tsconfig.json +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/services/project/static-scaffold.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { readdir, readFile, rm, rmdir, unlink } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { GameTopologyManifest } from \"@dreamboard-games/sdk/types\";\nimport { IS_PUBLISHED_BUILD } from \"../../build-target.js\";\nimport { REDUCER_TESTING_TYPES_WRAPPER_CONTENT } from \"../../templates/testing-types-content.js\";\nimport {\n MANIFEST_TYPECHECK_CONFIG_FILE,\n PROJECT_CONFIG_FILE,\n PROJECT_DIR_NAME,\n} from \"../../constants.js\";\nimport type { LocalMaintainerRegistryConfig } from \"../../types.js\";\nimport {\n ensureDir,\n exists,\n readJsonFile,\n readTextFile,\n readTextFileIfExists,\n writeTextFile,\n} from \"../../utils/fs.js\";\nimport { resolveCliRepoRoot } from \"../../utils/repo-root.js\";\nimport { materializeManifest } from \"./manifest-authoring.js\";\nimport { isDynamicSeedPath } from \"./scaffold-ownership.js\";\nimport {\n FRAMEWORK_PNPM_OVERRIDES,\n FRAMEWORK_REACT_DEPENDENCIES,\n FRAMEWORK_ZOD_VERSION,\n} from \"./framework-dependencies.js\";\n\ntype StaticScaffoldMode = \"new\" | \"update\";\ntype StaticAssetEntry = {\n targetPath: string;\n content: string;\n};\ntype StaticScaffoldOptions = {\n localMaintainerRegistry?: LocalMaintainerRegistryConfig | null;\n};\ntype RootPackageJsonShape = {\n private?: boolean;\n packageManager?: string;\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n optionalDependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n overrides?: Record<string, unknown>;\n pnpm?: Record<string, unknown>;\n [key: string]: unknown;\n};\n\nconst DREAMBOARD_SYNC_COMMAND = \"dreamboard sync\";\nconst DREAMBOARD_GITIGNORE_BLOCK = [\n \"# Dreamboard local state\",\n \".dreamboard/state.json\",\n \".dreamboard/snapshot.json\",\n \".dreamboard/dev/\",\n \".dreamboard/generated/\",\n \"\",\n].join(\"\\n\");\nconst TESTING_TYPES_STUB =\n \"export function defineScenario(scenario) { return scenario; }\\n\";\nconst GENERATED_TESTING_TYPES_PREFIX = \"// Generated by dreamboard\";\nconst GENERATED_SCENARIO_PREFIX = \"// Generated by dreamboard scaffold.\";\nconst LEGACY_DREAMBOARD_COMPONENT_INDEX_CONTENT = `export {\n ErrorBoundary,\n type ErrorBoundaryProps,\n} from \"@dreamboard-games/sdk/ui\";\nexport { PluginRuntime, type PluginRuntimeProps } from \"@dreamboard-games/sdk/runtime\";\n`;\nconst OLD_LEGACY_DREAMBOARD_COMPONENT_INDEX_CONTENT = [\n \"export {\",\n \" ErrorBoundary,\",\n \" PluginRuntime,\",\n \" type ErrorBoundaryProps,\",\n \" type PluginRuntimeProps,\",\n `} from \"@dreamboard/ui-${\"sdk\"}\";`,\n \"\",\n].join(\"\\n\");\nconst OLD_PUBLIC_DREAMBOARD_COMPONENT_INDEX_CONTENT = [\n \"export {\",\n \" ErrorBoundary,\",\n \" PluginRuntime,\",\n \" type ErrorBoundaryProps,\",\n \" type PluginRuntimeProps,\",\n `} from \"@dreamboard-games/ui-${\"sdk\"}\";`,\n \"\",\n].join(\"\\n\");\nconst INITIAL_SCENARIO_CONTENT = `${GENERATED_SCENARIO_PREFIX}\nimport { defineScenario } from \"../testing-types\";\n\nexport default defineScenario({\n id: \"smoke-initial-turn\",\n description:\n \"Sanity check that the scaffolded workspace boots into its initial phase.\",\n from: \"initial-turn\",\n when: async () => undefined,\n then: ({ expect, interactions, players, state }) => {\n const playerIds = players();\n expect(playerIds).toHaveLength(playerIds.length);\n expect(playerIds.length).toBeGreaterThanOrEqual(1);\n expect(state()).toBe(\"setup\");\n for (const playerId of playerIds) {\n expect(interactions(playerId)).toEqual([]);\n }\n },\n});\n`;\nconst STATIC_ASSET_ROOT = resolveStaticAssetRoot();\nconst SDK_PACKAGE_PATHS = {\n \"@dreamboard-games/sdk\": [\"packages\", \"sdk\", \"package.json\"],\n} as const;\nconst DEFAULT_SDK_DEPENDENCY_RANGES = {\n \"@dreamboard-games/sdk\": \"0.4.0-alpha.0\",\n} as const;\nconst SDK_DEPENDENCY_RANGES = {\n \"@dreamboard-games/sdk\": resolveSdkDependencyRange(\"@dreamboard-games/sdk\"),\n} as const;\n\nconst FRAMEWORK_SCRIPTS = {\n dev: \"dreamboard dev\",\n \"test:ui\":\n \"tsx --tsconfig test/tsconfig.tsx-runtime.json --test test/ui/**/*.test.tsx\",\n typecheck: `tsc --noEmit -p ${MANIFEST_TYPECHECK_CONFIG_FILE} && tsc --noEmit -p app/tsconfig.json && tsc --noEmit -p ui/tsconfig.json`,\n \"typecheck:manifest\": `tsc --noEmit -p ${MANIFEST_TYPECHECK_CONFIG_FILE}`,\n \"typecheck:app\": \"tsc --noEmit -p app/tsconfig.json\",\n \"typecheck:ui\": \"tsc --noEmit -p ui/tsconfig.json\",\n} as const;\nconst SHARED_DEPENDENCIES = {\n ...FRAMEWORK_REACT_DEPENDENCIES,\n} as const;\nconst ROOT_APP_DEPENDENCIES = {\n zod: FRAMEWORK_ZOD_VERSION,\n} as const;\nconst SHARED_DEV_DEPENDENCIES = {\n typescript: \"^5.9.2\",\n \"@types/node\": \"^24.5.2\",\n \"@types/react\": \"^19.0.0\",\n \"@types/react-dom\": \"^19.0.0\",\n csstype: \"^3.1.3\",\n tsx: \"^4.20.5\",\n} as const;\n\nexport async function scaffoldStaticWorkspace(\n projectRoot: string,\n mode: StaticScaffoldMode,\n options: StaticScaffoldOptions = {},\n): Promise<void> {\n await writeFrameworkStaticFiles(projectRoot, mode, options);\n await ensureDreamboardGitignore(projectRoot);\n await writeManifestTypecheckTsconfig(\n path.join(projectRoot, MANIFEST_TYPECHECK_CONFIG_FILE),\n );\n await removeLegacyVendoredSdkPaths(projectRoot);\n await removeLegacyDreamboardComponentPath(projectRoot);\n\n const testDir = path.join(projectRoot, \"test\");\n const basesDir = path.join(testDir, \"bases\");\n const scenariosDir = path.join(testDir, \"scenarios\");\n const generatedDir = path.join(testDir, \"generated\");\n\n await ensureDir(basesDir);\n await ensureDir(scenariosDir);\n await ensureDir(generatedDir);\n\n await writeTestReadme(path.join(testDir, \"README.md\"));\n await writeGeneratedTestingStubs(generatedDir, mode);\n const initialTestPlayerCount = await inferInitialTestPlayerCount(projectRoot);\n await writeInitialBase(\n path.join(basesDir, \"initial-turn.base.ts\"),\n mode,\n initialTestPlayerCount,\n );\n await writeInitialScenario(\n path.join(scenariosDir, \"smoke-initial-turn.scenario.ts\"),\n mode,\n );\n await writeTestingTypes(path.join(testDir, \"testing-types.ts\"), mode);\n await writeTestTsconfig(path.join(testDir, \"tsconfig.json\"));\n\n const staleDtsPath = path.join(testDir, \"testing-types.d.ts\");\n if (await exists(staleDtsPath)) {\n await unlink(staleDtsPath);\n }\n const staleBaseScenariosPath = path.join(testDir, \"base-scenarios.json\");\n if (await exists(staleBaseScenariosPath)) {\n await unlink(staleBaseScenariosPath);\n }\n\n await migrateLegacyScenarioImports(projectRoot);\n}\n\nasync function ensureDreamboardGitignore(projectRoot: string): Promise<void> {\n const gitignorePath = path.join(projectRoot, \".gitignore\");\n const existing = await readTextFileIfExists(gitignorePath);\n if (existing?.includes(\".dreamboard/state.json\")) {\n return;\n }\n await writeTextFile(\n gitignorePath,\n `${existing ? `${existing.trimEnd()}\\n\\n` : \"\"}${DREAMBOARD_GITIGNORE_BLOCK}`,\n );\n}\n\nexport async function assertCliStaticScaffoldComplete(\n projectRoot: string,\n deletedPaths: readonly string[] = [],\n): Promise<void> {\n const expectedEntries = await getExpectedStaticEntries(projectRoot);\n const missingOrBlankPaths: string[] = [];\n\n for (const entry of expectedEntries) {\n const fullPath = path.join(projectRoot, entry.targetPath);\n const content = await readTextFileIfExists(fullPath);\n\n if (content === null || content.trim().length === 0) {\n missingOrBlankPaths.push(entry.targetPath);\n }\n }\n\n const staticPaths = new Set(expectedEntries.map((entry) => entry.targetPath));\n const deletedStaticPaths = deletedPaths\n .map(normalizeProjectPath)\n .filter((filePath) => staticPaths.has(filePath))\n .sort();\n\n if (missingOrBlankPaths.length === 0 && deletedStaticPaths.length === 0) {\n return;\n }\n\n const problems: string[] = [];\n if (missingOrBlankPaths.length > 0) {\n problems.push(\n `missing or blank: ${summarizePaths(missingOrBlankPaths.sort())}`,\n );\n }\n if (deletedStaticPaths.length > 0) {\n problems.push(`deleted: ${summarizePaths(deletedStaticPaths)}`);\n }\n\n throw new Error(\n `CLI static scaffold is incomplete (${problems.join(\"; \")}). Run \\`${DREAMBOARD_SYNC_COMMAND}\\` to refresh the scaffold before compiling.`,\n );\n}\n\nasync function writeFrameworkStaticFiles(\n projectRoot: string,\n mode: StaticScaffoldMode,\n options: StaticScaffoldOptions,\n): Promise<void> {\n const assetEntries = await getStaticAssetEntries();\n\n for (const entry of assetEntries) {\n const fullPath = path.join(projectRoot, entry.targetPath);\n // Dynamic seed files are user-customizable: only write them on first\n // scaffold; preserve existing content on subsequent updates.\n if (mode === \"update\" && isDynamicSeedPath(entry.targetPath)) {\n const existing = await readTextFileIfExists(fullPath);\n if (existing !== null && existing.trim().length > 0) {\n continue;\n }\n }\n await writeTextFile(fullPath, entry.content);\n }\n\n for (const entry of await getDynamicStaticEntries(\n projectRoot,\n mode,\n options,\n )) {\n await writeTextFile(\n path.join(projectRoot, entry.targetPath),\n entry.content,\n );\n }\n\n if (!options.localMaintainerRegistry) {\n await rm(path.join(projectRoot, \".npmrc\"), { force: true });\n }\n}\n\nasync function removeLegacyVendoredSdkPaths(\n projectRoot: string,\n): Promise<void> {\n await rm(path.join(projectRoot, \"app\", \"sdk\"), {\n recursive: true,\n force: true,\n });\n await rm(path.join(projectRoot, \"ui\", \"sdk\"), {\n recursive: true,\n force: true,\n });\n}\n\nasync function removeLegacyDreamboardComponentPath(\n projectRoot: string,\n): Promise<void> {\n const legacyDirPath = path.join(\n projectRoot,\n \"ui\",\n \"components\",\n \"dreamboard\",\n );\n const legacyIndexPath = path.join(legacyDirPath, \"index.ts\");\n const existing = await readTextFileIfExists(legacyIndexPath);\n\n const removableLegacyContents = new Set([\n LEGACY_DREAMBOARD_COMPONENT_INDEX_CONTENT.trim(),\n OLD_LEGACY_DREAMBOARD_COMPONENT_INDEX_CONTENT.trim(),\n OLD_PUBLIC_DREAMBOARD_COMPONENT_INDEX_CONTENT.trim(),\n ]);\n\n if (existing === null || !removableLegacyContents.has(existing.trim())) {\n return;\n }\n\n await unlink(legacyIndexPath);\n const remainingEntries = await readdir(legacyDirPath).catch(() => []);\n if (remainingEntries.length === 0) {\n await rmdir(legacyDirPath);\n }\n}\n\nasync function writeTestReadme(filePath: string): Promise<void> {\n await writeTextFile(\n filePath,\n \"# Dreamboard Test Workspace\\n\\nTypeScript bases live in `test/bases/*.base.ts` and scenarios live in `test/scenarios/*.scenario.ts`.\\n\\n1. Define reusable seeded bases with `defineBase({ id, seed, players, setupProfileId?, setup })`.\\n2. Define scenarios with `defineScenario({ id, from, when, then })`.\\n3. Scenario assertions can read `players()`, `state()`, `view(playerId)`, and `interactions(playerId)`.\\n4. Generate deterministic base snapshots: `dreamboard test generate`.\\n5. Run tests: `dreamboard test run`.\\n\\nImport test helpers from `../testing-types`.\\n\\nGenerated artifacts are written to `test/generated/*` and should not be edited manually.\\n\",\n );\n}\n\nasync function writeInitialBase(\n filePath: string,\n mode: StaticScaffoldMode,\n players: number,\n): Promise<void> {\n if (mode === \"update\") {\n return;\n }\n\n await writeTextFile(\n filePath,\n `import { defineBase } from \"../testing-types\";\n\nexport default defineBase({\n id: \"initial-turn\",\n seed: 1337,\n players: ${players},\n setup: async () => undefined,\n});\n`,\n );\n}\n\nasync function writeInitialScenario(\n filePath: string,\n mode: StaticScaffoldMode,\n): Promise<void> {\n if (mode === \"new\") {\n await writeTextFile(filePath, INITIAL_SCENARIO_CONTENT);\n return;\n }\n\n const existing = await readTextFileIfExists(filePath);\n if (\n existing === null ||\n existing.trim().length === 0 ||\n existing === INITIAL_SCENARIO_CONTENT\n ) {\n await writeTextFile(filePath, INITIAL_SCENARIO_CONTENT);\n }\n}\n\nasync function writeTestingTypes(\n filePath: string,\n mode: StaticScaffoldMode,\n): Promise<void> {\n if (mode === \"new\") {\n await writeTextFile(filePath, REDUCER_TESTING_TYPES_WRAPPER_CONTENT);\n return;\n }\n\n const existing = await readTextFileIfExists(filePath);\n if (shouldRefreshGeneratedTestingTypes(existing)) {\n await writeTextFile(filePath, REDUCER_TESTING_TYPES_WRAPPER_CONTENT);\n }\n}\n\nasync function writeGeneratedTestingStubs(\n generatedDir: string,\n mode: StaticScaffoldMode,\n): Promise<void> {\n const header = \"// Generated by dreamboard scaffold. Do not edit by hand.\\n\";\n await writeGeneratedTestingStubFile(\n path.join(generatedDir, \"base-states.generated.ts\"),\n `${header}export const BASE_STATES = {} as const;\\nexport const BASE_STATES_CONTRACT_FINGERPRINT = undefined;\\n`,\n mode,\n );\n await writeGeneratedTestingStubFile(\n path.join(generatedDir, \"base-states.generated.d.ts\"),\n `${header}export declare const BASE_STATES: Record<string, unknown>;\\nexport declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;\\n`,\n mode,\n );\n await writeGeneratedTestingStubFile(\n path.join(generatedDir, \"testing-contract.ts\"),\n `${header}export type BaseId = string;\\nexport type GameView = unknown;\\nexport type InteractionId = string;\\nexport type InteractionParamsOf<_Id extends string> = Record<string, unknown>;\\nexport type PhaseName = string;\\nexport type PlayerId = string;\\nexport type RejectionCode = string;\\nexport type StateName = string;\\nexport type TestRunner = \"reducer\" | \"remote\" | \"browser\";\\nexport type ViewByPhase = Record<string, GameView>;\\nexport type WorkspaceStageName<_Phase extends string = string> = string;\\nexport type Expectation = { [matcher: string]: (...args: unknown[]) => unknown; not: Expectation };\\nexport type ExpectFn = (actual: unknown) => Expectation;\\nexport type InteractionExplanation = { interactionId: string; phase: string; step: string | null; availability: \"available\" | \"notYourTurn\" | \"wrongPhase\" | \"wrongStep\" | \"blocked\"; rules: readonly { ruleId: string; outcome: \"passed\" | \"failed\" | \"notEvaluated\"; errorCode?: string; message?: string; }[]; actor: { required: readonly string[]; playerIsActor: boolean }; inputs: readonly { key: string; kind: string; eligibleCount: number | \"lazy\"; }[]; };\\nexport interface InteractionDescriptorFor<Id extends string = string> { interactionId: Id; [key: string]: unknown; }\\nexport interface ScenarioGameApi { start(): Promise<void>; submit<Id extends InteractionId>(playerId: PlayerId, interactionId: Id, params?: InteractionParamsOf<Id>): Promise<void>; }\\nexport interface BaseContext { game: ScenarioGameApi; players(): readonly PlayerId[]; seat(index: number): PlayerId; }\\nexport interface SharedScenarioContext { game: ScenarioGameApi; players(): readonly PlayerId[]; seat(index: number): PlayerId; state(): StateName; view(playerId: PlayerId): GameView; interactions(playerId: PlayerId): readonly InteractionDescriptorFor[]; explain(playerId: PlayerId, interactionId: InteractionId): InteractionExplanation; expect: ExpectFn; }\\nexport type ScenarioContext<Phase extends PhaseName | undefined = undefined> = Omit<SharedScenarioContext, \"state\" | \"view\"> & { state(): Phase extends PhaseName ? Phase : StateName; view(playerId: PlayerId): Phase extends PhaseName ? ViewByPhase[Phase] : GameView; };\\nexport type ScenarioThenContext<_Runners extends readonly TestRunner[] = readonly [\"reducer\"], Phase extends PhaseName | undefined = undefined> = ScenarioContext<Phase>;\\nexport interface BaseDefinition { id: string; seed?: number; players?: number; setupProfileId?: string; extends?: BaseId | string; setup: (ctx: BaseContext) => void | Promise<void>; }\\nexport interface ScenarioDefinition<Runners extends readonly TestRunner[] = readonly [\"reducer\"], Phase extends PhaseName | undefined = undefined> { id: string; description?: string; from: BaseId | string; runners?: Runners; phase?: Phase; stage?: Phase extends PhaseName ? WorkspaceStageName<Phase> : never; when: (ctx: ScenarioContext<Phase>) => void | Promise<void>; then: (ctx: ScenarioThenContext<Runners, Phase>) => void | Promise<void>; }\\n`,\n mode,\n );\n await writeGeneratedTestingStubFile(\n path.join(generatedDir, \"scenario-manifest.generated.ts\"),\n `${header}export const SCENARIO_MANIFEST = [] as const;\\n`,\n mode,\n );\n}\n\nasync function writeGeneratedTestingStubFile(\n filePath: string,\n content: string,\n mode: StaticScaffoldMode,\n): Promise<void> {\n if (mode === \"new\") {\n await writeTextFile(filePath, content);\n return;\n }\n\n const existing = await readTextFileIfExists(filePath);\n if (\n existing === null ||\n existing.trim().length === 0 ||\n existing.startsWith(GENERATED_SCENARIO_PREFIX)\n ) {\n await writeTextFile(filePath, content);\n }\n}\n\nasync function writeTestTsconfig(filePath: string): Promise<void> {\n await writeTextFile(\n filePath,\n `${JSON.stringify(\n {\n compilerOptions: {\n target: \"ES2022\",\n module: \"ESNext\",\n moduleResolution: \"bundler\",\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n noEmit: true,\n },\n include: [\n \"./**/*.ts\",\n \"./**/*.d.ts\",\n \"../shared/**/*.ts\",\n \"../shared/**/*.d.ts\",\n ],\n },\n null,\n 2,\n )}\\n`,\n );\n}\n\nasync function writeManifestTypecheckTsconfig(filePath: string): Promise<void> {\n await writeTextFile(\n filePath,\n `${JSON.stringify(\n {\n compilerOptions: {\n target: \"ES2022\",\n module: \"ESNext\",\n moduleResolution: \"bundler\",\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n noEmit: true,\n allowImportingTsExtensions: true,\n },\n include: [\"./manifest.ts\"],\n },\n null,\n 2,\n )}\\n`,\n );\n}\n\nexport async function migrateLegacyScenarioImports(\n projectRoot: string,\n): Promise<void> {\n const scenariosRoot = path.join(projectRoot, \"test\", \"scenarios\");\n if (!(await exists(scenariosRoot))) return;\n\n const scenarioFiles = await collectScenarioFiles(scenariosRoot);\n for (const filePath of scenarioFiles) {\n const content = await readTextFile(filePath);\n if (!content.includes(\"@dreamboard/cli/testing\")) continue;\n\n const relativeToTestingTypes = normalizeImportPath(\n path.relative(\n path.dirname(filePath),\n path.join(projectRoot, \"test\", \"testing-types\"),\n ),\n );\n\n const migrated = content\n .replaceAll('\"@dreamboard/cli/testing\"', `\"${relativeToTestingTypes}\"`)\n .replaceAll(\"'@dreamboard/cli/testing'\", `'${relativeToTestingTypes}'`);\n\n if (migrated !== content) {\n await writeTextFile(filePath, migrated);\n }\n }\n}\n\nfunction shouldRefreshGeneratedTestingTypes(\n existingContent: string | null,\n): boolean {\n if (existingContent === null || existingContent.trim().length === 0) {\n return true;\n }\n if (existingContent === TESTING_TYPES_STUB) {\n return true;\n }\n return existingContent.startsWith(GENERATED_TESTING_TYPES_PREFIX);\n}\n\nasync function inferInitialTestPlayerCount(\n projectRoot: string,\n): Promise<number> {\n const manifestPath = path.join(projectRoot, \"manifest.ts\");\n if (!(await exists(manifestPath))) {\n return 4;\n }\n\n try {\n const manifest = await materializeManifest(projectRoot);\n return manifest.players.optimalPlayers ?? manifest.players.minPlayers ?? 4;\n } catch {\n return 4;\n }\n}\n\nasync function collectScenarioFiles(rootDir: string): Promise<string[]> {\n const files: string[] = [];\n const stack = [rootDir];\n\n while (stack.length > 0) {\n const dir = stack.pop();\n if (!dir) continue;\n\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n stack.push(fullPath);\n } else if (entry.isFile() && entry.name.endsWith(\".ts\")) {\n files.push(fullPath);\n }\n }\n }\n\n return files;\n}\n\nexport function resolveStaticAssetRoot(\n importUrl: string = import.meta.url,\n): string {\n const candidates = [\n fileURLToPath(new URL(\"../../scaffold/assets/static/\", importUrl)),\n fileURLToPath(new URL(\"./scaffold/assets/static/\", importUrl)),\n fileURLToPath(new URL(\"../scaffold/assets/static/\", importUrl)),\n ];\n\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n\n throw new Error(\n `Unable to locate CLI static scaffold assets. Checked: ${candidates.join(\", \")}`,\n );\n}\n\nasync function getStaticAssetEntries(): Promise<StaticAssetEntry[]> {\n const files = await walkFiles(STATIC_ASSET_ROOT);\n const entries: StaticAssetEntry[] = [];\n\n for (const filePath of files) {\n const targetPath = normalizeProjectPath(\n path.relative(STATIC_ASSET_ROOT, filePath),\n );\n entries.push({\n targetPath,\n content: await readFile(filePath, \"utf8\"),\n });\n }\n\n entries.sort((left, right) =>\n left.targetPath.localeCompare(right.targetPath),\n );\n return entries;\n}\n\nasync function getDynamicStaticEntries(\n projectRoot: string,\n mode: StaticScaffoldMode,\n options: StaticScaffoldOptions = {},\n): Promise<StaticAssetEntry[]> {\n const entries: StaticAssetEntry[] = [\n {\n targetPath: \"package.json\",\n content: await buildRootPackageJson(projectRoot, mode, options),\n },\n {\n targetPath: \"ui/package.json\",\n content: buildUiPackageJson(),\n },\n ];\n\n if (options.localMaintainerRegistry) {\n entries.push({\n targetPath: \".npmrc\",\n content: buildWorkspaceNpmrc(options.localMaintainerRegistry.registryUrl),\n });\n }\n\n return entries;\n}\n\nasync function getExpectedStaticEntries(\n projectRoot: string,\n): Promise<StaticAssetEntry[]> {\n const entries = [\n ...(await getStaticAssetEntries()).filter(\n (entry) => entry.targetPath !== \".npmrc\",\n ),\n ...(await getDynamicStaticEntries(projectRoot, \"update\")),\n ];\n entries.sort((left, right) =>\n left.targetPath.localeCompare(right.targetPath),\n );\n return entries;\n}\n\nasync function walkFiles(rootDir: string): Promise<string[]> {\n const files: string[] = [];\n const stack = [rootDir];\n\n while (stack.length > 0) {\n const currentDir = stack.pop();\n if (!currentDir) continue;\n\n const entries = await readdir(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n if (entry.isDirectory()) {\n stack.push(fullPath);\n } else if (entry.isFile()) {\n files.push(fullPath);\n }\n }\n }\n\n files.sort((left, right) => left.localeCompare(right));\n return files;\n}\n\nasync function buildRootPackageJson(\n projectRoot: string,\n mode: StaticScaffoldMode,\n options: StaticScaffoldOptions,\n): Promise<string> {\n const sdkPackageRanges = {\n ...SDK_DEPENDENCY_RANGES,\n ...(options.localMaintainerRegistry?.packages ?? {}),\n };\n const packageJsonPath = path.join(projectRoot, \"package.json\");\n const existingPackageJson =\n mode === \"update\" && (await exists(packageJsonPath))\n ? await readJsonFile<RootPackageJsonShape>(packageJsonPath)\n : null;\n const {\n dreamboardFrameworkVersion: _legacyFrameworkVersion,\n ...existingPackageJsonWithoutLegacyVersion\n } = existingPackageJson ?? {};\n const frameworkDependencies = {\n \"@dreamboard-games/sdk\":\n sdkPackageRanges[\"@dreamboard-games/sdk\"] ??\n SDK_DEPENDENCY_RANGES[\"@dreamboard-games/sdk\"],\n ...SHARED_DEPENDENCIES,\n ...ROOT_APP_DEPENDENCIES,\n };\n const frameworkDevDependencies = {\n ...SHARED_DEV_DEPENDENCIES,\n };\n const nextPackageJson: RootPackageJsonShape = {\n ...existingPackageJsonWithoutLegacyVersion,\n private: true,\n scripts: {\n ...(existingPackageJson?.scripts ?? {}),\n ...FRAMEWORK_SCRIPTS,\n },\n dependencies: {\n ...(existingPackageJson?.dependencies ?? {}),\n ...frameworkDependencies,\n },\n devDependencies: {\n ...(existingPackageJson?.devDependencies ?? {}),\n ...frameworkDevDependencies,\n },\n pnpm: mergePnpmConfig(existingPackageJson?.pnpm),\n };\n return `${JSON.stringify(nextPackageJson, null, 2)}\\n`;\n}\n\nfunction mergePnpmConfig(\n existingPnpm: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n const existingOverrides =\n existingPnpm?.overrides &&\n typeof existingPnpm.overrides === \"object\" &&\n !Array.isArray(existingPnpm.overrides)\n ? (existingPnpm.overrides as Record<string, unknown>)\n : {};\n return {\n ...(existingPnpm ?? {}),\n overrides: {\n ...existingOverrides,\n ...FRAMEWORK_PNPM_OVERRIDES,\n },\n };\n}\n\nfunction buildWorkspaceNpmrc(registryUrl: string): string {\n return `@dreamboard-games:registry=${registryUrl}\\n`;\n}\n\nfunction buildUiPackageJson(): string {\n return `${JSON.stringify(\n {\n private: true,\n dependencies: SHARED_DEPENDENCIES,\n devDependencies: SHARED_DEV_DEPENDENCIES,\n },\n null,\n 2,\n )}\\n`;\n}\n\nfunction readPackageVersion(\n packageJsonPath: string,\n packageName: string,\n): string | null {\n const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as {\n version?: unknown;\n };\n\n if (\n typeof packageJson.version !== \"string\" ||\n packageJson.version.trim().length === 0\n ) {\n return null;\n }\n\n return `^${packageJson.version.trim()}`;\n}\n\nfunction findNearestPackageJsonPath(\n importMetaUrl: string = import.meta.url,\n): string | null {\n let current = path.dirname(fileURLToPath(importMetaUrl));\n\n while (true) {\n const candidate = path.join(current, \"package.json\");\n if (existsSync(candidate)) {\n return candidate;\n }\n\n const parent = path.dirname(current);\n if (parent === current) {\n return null;\n }\n current = parent;\n }\n}\n\nfunction isSourceCheckoutCliPackageJsonPath(\n packageJsonPath: string,\n importMetaUrl: string = import.meta.url,\n): boolean {\n try {\n return (\n path.resolve(packageJsonPath) ===\n path.join(\n resolveCliRepoRoot(importMetaUrl),\n \"apps\",\n \"dreamboard-cli\",\n \"package.json\",\n )\n );\n } catch {\n return false;\n }\n}\n\nfunction readPackagedSdkDependencyRange(\n packageName: keyof typeof SDK_PACKAGE_PATHS,\n importMetaUrl: string = import.meta.url,\n): string | null {\n const packageJsonPath = findNearestPackageJsonPath(importMetaUrl);\n if (!packageJsonPath) {\n return null;\n }\n\n const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as {\n dependencies?: Record<string, unknown>;\n };\n\n const packagedRange = packageJson.dependencies?.[packageName];\n if (\n typeof packagedRange !== \"string\" ||\n packagedRange.trim().length === 0 ||\n packagedRange.startsWith(\"workspace:\") ||\n packagedRange.startsWith(\"link:\") ||\n (packagedRange.startsWith(\"file:\") &&\n !IS_PUBLISHED_BUILD &&\n isSourceCheckoutCliPackageJsonPath(packageJsonPath, importMetaUrl))\n ) {\n return null;\n }\n\n return packagedRange.trim();\n}\n\n/**\n * Local Verdaccio snapshot pins (`x.y.z-local.<timestamp>.<fingerprint>`) are\n * only resolvable on the maintainer machine that published them. Scaffolded\n * workspaces must always start from a registry-publishable version, so\n * normalize any inherited snapshot pin to its public base version.\n */\nfunction stripLocalSnapshotSuffix(range: string): string {\n return range.replace(/-local\\..*$/, \"\");\n}\n\nexport function resolveSdkDependencyRange(\n packageName: keyof typeof SDK_PACKAGE_PATHS,\n importMetaUrl: string = import.meta.url,\n): string {\n const packagedRange = readPackagedSdkDependencyRange(\n packageName,\n importMetaUrl,\n );\n if (packagedRange) {\n return stripLocalSnapshotSuffix(packagedRange);\n }\n\n try {\n const repoRoot = resolveCliRepoRoot(importMetaUrl);\n const packageJsonPath = path.join(\n repoRoot,\n ...SDK_PACKAGE_PATHS[packageName],\n );\n const repoRange = readPackageVersion(packageJsonPath, packageName);\n if (repoRange) {\n return stripLocalSnapshotSuffix(repoRange);\n }\n } catch {\n // Published installs do not include the monorepo layout.\n }\n\n return DEFAULT_SDK_DEPENDENCY_RANGES[packageName];\n}\n\nfunction normalizeImportPath(relativePath: string): string {\n const normalized = relativePath.replaceAll(\"\\\\\", \"/\");\n if (normalized.startsWith(\".\")) return normalized;\n return `./${normalized}`;\n}\n\nfunction normalizeProjectPath(filePath: string): string {\n return filePath.replace(/^\\.\\//, \"\").replace(/^\\/+/, \"\").replace(/\\\\/g, \"/\");\n}\n\nfunction summarizePaths(paths: readonly string[]): string {\n const maxShown = 5;\n const shown = paths.slice(0, maxShown).join(\", \");\n if (paths.length <= maxShown) return shown;\n return `${shown}, and ${paths.length - maxShown} more`;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/config/global-config.ts","../../src/config/credential-store.ts"],"sourcesContent":["import os from \"node:os\";\nimport path from \"node:path\";\nimport type { CredentialBackendPreference, GlobalConfig } from \"../types.js\";\nimport { PROJECT_DIR_NAME } from \"../constants.js\";\nimport { ensureDir, readJsonFile } from \"../utils/fs.js\";\nimport { atomicWriteFile } from \"../utils/atomic-file.js\";\nimport { getCredentialFilePath } from \"./credential-store.js\";\n\nfunction normalizeCredentialBackend(\n value: unknown,\n): CredentialBackendPreference | undefined {\n if (value === \"file\" || value === \"keychain\") return value;\n // Tolerate unknown / malformed values rather than refusing to load the\n // whole config - an unrecognised backend name should degrade to \"use\n // the default\" instead of locking the user out of their CLI.\n return undefined;\n}\n\nexport function getGlobalConfigPath(): string {\n return path.join(os.homedir(), PROJECT_DIR_NAME, \"config.json\");\n}\n\n/**\n * Path to the on-disk credential file used by the file backend of\n * `CredentialStore`. Re-exported here to avoid circular / ad-hoc imports\n * in UI surface (`auth status`, `config show`, etc).\n */\nexport function getGlobalAuthPath(): string {\n return getCredentialFilePath();\n}\n\n/**\n * Load non-credential CLI configuration.\n *\n * Note: this function used to also load `authToken` / `refreshToken`\n * from `auth.json` and flatten them onto `GlobalConfig`. That shape\n * enabled the refresh-token-wipe bug: `saveGlobalConfig({ ...config })`\n * without explicit auth fields erased the stored refresh token.\n *\n * Credentials are now owned exclusively by `CredentialStore`. Callers\n * that need them must import `getCredentials()` directly.\n */\nexport async function loadGlobalConfig(): Promise<GlobalConfig> {\n const config = await readJsonFile<GlobalConfig>(getGlobalConfigPath()).catch(\n () => ({}) as GlobalConfig,\n );\n return {\n environment: config.environment,\n credentialBackend: normalizeCredentialBackend(config.credentialBackend),\n };\n}\n\n/**\n * Persist non-credential CLI configuration.\n *\n * This function cannot write credentials, by construction: the\n * `GlobalConfig` type has no credential fields. Credentials must be\n * persisted through `setCredentials` / `clearCredentials` from\n * `credential-store.ts`.\n */\nexport async function saveGlobalConfig(config: GlobalConfig): Promise<void> {\n const configDir = path.join(os.homedir(), PROJECT_DIR_NAME);\n await ensureDir(configDir);\n const normalized: GlobalConfig = {\n environment: config.environment,\n credentialBackend: normalizeCredentialBackend(config.credentialBackend),\n };\n await atomicWriteFile(\n getGlobalConfigPath(),\n `${JSON.stringify(normalized, null, 2)}\\n`,\n { mode: 0o600 },\n );\n}\n","/**\n * Single writer for the long-lived Dreamboard session credentials.\n *\n * Design invariants (enforced at the type level and tested in\n * `credential-store.test.ts`):\n *\n * 1. This module is the ONLY place in the CLI that writes credentials to\n * disk or the OS keychain. `global-config.ts` used to own both the\n * config and the credentials via `saveGlobalConfig`, which made it\n * trivial to wipe a refresh token by accident. The `GlobalConfig` type\n * no longer carries credentials, so attempting to persist one through\n * the config path is a type error.\n *\n * 2. The mutating surface is intentionally narrow:\n * - `setCredentials(c)` for refreshable sessions (both tokens present)\n * - `setAccessOnlySession(accessToken)` for the `auth set` / `config set\n * --token` power-user path, which has no refresh token by\n * construction\n * - `clearCredentials()` wipes the file entirely\n * There is no \"partial update\" API. `Credentials` requires both\n * `accessToken` and `refreshToken`, so it is impossible to persist a\n * half-populated refreshable session.\n *\n * 3. Writes go through `atomicWriteFile` + `withFileLock`, so a crash or\n * interrupt during `dreamboard sync`/`compile` cannot leave `auth.json`\n * truncated, and parallel CLI invocations cannot clobber each other's\n * rotated refresh tokens.\n *\n * 4. The on-disk JSON shape for the file backend is kept backward\n * compatible: we continue to read/write `authToken` + `refreshToken`\n * so existing users are not forced to log in again after this change.\n * A newer `accessToken` key is also accepted for read to ease any\n * future format bump.\n *\n * 5. The file backend is the default. The OS keychain is opt-in via\n * `credentialBackend: \"keychain\"` in `~/.dreamboard/config.json`\n * because on macOS the first keychain write triggers a login-password\n * prompt, and re-prompts whenever the executing Node binary's code\n * signature changes (e.g. after an `nvm`/`volta` upgrade). Users who\n * want encrypted-at-rest storage can opt in explicitly; everyone else\n * gets a zero-prompt experience.\n */\n\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { promises as fs } from \"node:fs\";\nimport { PROJECT_DIR_NAME } from \"../constants.js\";\nimport {\n atomicWriteFile,\n withFileLock,\n type FileLockOptions,\n} from \"../utils/atomic-file.js\";\n\n/** Fully refreshable session: both tokens required. */\nexport type Credentials = {\n readonly accessToken: string;\n readonly refreshToken: string;\n readonly tokenExpiresAt?: string;\n readonly clerkOAuthIssuer?: string;\n readonly clerkOAuthClientId?: string;\n readonly clerkOAuthTokenUrl?: string;\n readonly environment?: string;\n};\n\n/**\n * Raw on-disk snapshot. Either or both fields may be present. The refresh\n * coordinator only acts on snapshots that have both tokens populated.\n */\nexport type StoredSessionSnapshot = {\n readonly accessToken?: string;\n readonly refreshToken?: string;\n readonly tokenExpiresAt?: string;\n readonly clerkOAuthIssuer?: string;\n readonly clerkOAuthClientId?: string;\n readonly clerkOAuthTokenUrl?: string;\n readonly environment?: string;\n};\n\nexport type CredentialBackendName = \"file\" | \"keychain\";\n\nexport type CredentialBackend = {\n readonly name: CredentialBackendName;\n read(): Promise<StoredSessionSnapshot | null>;\n writeFull(creds: Credentials): Promise<void>;\n writeAccessOnly(accessToken: string): Promise<void>;\n clear(): Promise<void>;\n};\n\nexport type CredentialLockOps = {\n readonly backendName: CredentialBackendName;\n read(): Promise<StoredSessionSnapshot | null>;\n writeFull(creds: Credentials): Promise<void>;\n writeAccessOnly(accessToken: string): Promise<void>;\n clear(): Promise<void>;\n};\n\ntype DiskShape = Partial<{\n accessToken: string;\n authToken: string;\n refreshToken: string;\n tokenExpiresAt: string;\n clerkOAuthIssuer: string;\n clerkOAuthClientId: string;\n clerkOAuthTokenUrl: string;\n environment: string;\n}>;\n\nexport function getCredentialFilePath(): string {\n return path.join(os.homedir(), PROJECT_DIR_NAME, \"auth.json\");\n}\n\nfunction getCredentialLockPath(): string {\n return `${getCredentialFilePath()}.lock`;\n}\n\nasync function fileRead(): Promise<StoredSessionSnapshot | null> {\n const filePath = getCredentialFilePath();\n let data: string;\n try {\n data = await fs.readFile(filePath, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n if (data.trim().length === 0) {\n return null;\n }\n let parsed: DiskShape;\n try {\n parsed = JSON.parse(data) as DiskShape;\n } catch {\n return null;\n }\n const accessToken = parsed.accessToken ?? parsed.authToken;\n const refreshToken = parsed.refreshToken;\n if (!accessToken && !refreshToken) return null;\n return {\n accessToken: accessToken || undefined,\n refreshToken: refreshToken || undefined,\n tokenExpiresAt: parsed.tokenExpiresAt || undefined,\n clerkOAuthIssuer: parsed.clerkOAuthIssuer || undefined,\n clerkOAuthClientId: parsed.clerkOAuthClientId || undefined,\n clerkOAuthTokenUrl: parsed.clerkOAuthTokenUrl || undefined,\n environment: parsed.environment || undefined,\n };\n}\n\nasync function writeFilePayload(payload: DiskShape): Promise<void> {\n await atomicWriteFile(\n getCredentialFilePath(),\n `${JSON.stringify(payload, null, 2)}\\n`,\n { mode: 0o600 },\n );\n}\n\nasync function fileWriteFull(creds: Credentials): Promise<void> {\n if (!creds.accessToken || !creds.refreshToken) {\n throw new Error(\n \"Refusing to persist credentials with an empty accessToken or refreshToken.\",\n );\n }\n await writeFilePayload({\n authToken: creds.accessToken,\n refreshToken: creds.refreshToken,\n tokenExpiresAt: creds.tokenExpiresAt,\n clerkOAuthIssuer: creds.clerkOAuthIssuer,\n clerkOAuthClientId: creds.clerkOAuthClientId,\n clerkOAuthTokenUrl: creds.clerkOAuthTokenUrl,\n environment: creds.environment,\n });\n}\n\nasync function fileWriteAccessOnly(accessToken: string): Promise<void> {\n if (!accessToken) {\n throw new Error(\"Refusing to persist an empty access token.\");\n }\n await writeFilePayload({ authToken: accessToken });\n}\n\nasync function fileClear(): Promise<void> {\n const filePath = getCredentialFilePath();\n try {\n await fs.unlink(filePath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n}\n\nexport const fileCredentialBackend: CredentialBackend = {\n name: \"file\",\n read: fileRead,\n writeFull: fileWriteFull,\n writeAccessOnly: fileWriteAccessOnly,\n clear: fileClear,\n};\n\nexport type BackendResolver = () =>\n | CredentialBackend\n | Promise<CredentialBackend>;\n\nlet cachedBackend: CredentialBackend | null = null;\nlet migrationCompleted = false;\nlet backendResolver: BackendResolver = defaultBackendResolver;\n\n/**\n * Default resolver precedence:\n *\n * 1. `DREAMBOARD_CREDENTIAL_BACKEND` env var (debugging / CI override).\n * - \"file\" -> force file\n * - \"keychain\" -> force keychain (falls back to file if the native\n * module or the OS keyring is unavailable)\n * - \"auto\" -> same as unset (use config)\n * - unknown -> throw so typos fail loud\n * 2. `credentialBackend` in `~/.dreamboard/config.json`.\n * - \"keychain\" -> opt in to the OS keychain (with file fallback)\n * - \"file\" / unset / malformed -> file\n * 3. Default: file backend.\n *\n * Keychain is opt-in because on macOS the OS login-keychain prompts for\n * the user's password the first time a new binary tries to write to an\n * item, and re-prompts whenever the Node binary signature changes. We\n * would rather ship a zero-prompt default and let users who care about\n * encrypted-at-rest storage enable it.\n *\n * The resolver is async because the keychain probe requires a dynamic\n * `@napi-rs/keyring` import.\n */\nasync function defaultBackendResolver(): Promise<CredentialBackend> {\n const override = (process.env.DREAMBOARD_CREDENTIAL_BACKEND ?? \"\")\n .trim()\n .toLowerCase();\n if (override === \"file\") {\n return fileCredentialBackend;\n }\n if (override && override !== \"keychain\" && override !== \"auto\") {\n // Fail loud on typos rather than silently falling back: this env\n // var exists specifically for users who are debugging auth issues\n // and need to know their override took effect.\n throw new Error(\n `Unknown DREAMBOARD_CREDENTIAL_BACKEND value \"${override}\" (expected \"file\", \"keychain\", or \"auto\").`,\n );\n }\n\n const useKeychain =\n override === \"keychain\" || (await readCredentialBackendPreference());\n if (!useKeychain) {\n return fileCredentialBackend;\n }\n\n const { tryKeychainBackend } = await import(\"./keychain-backend.js\");\n const keychain = await tryKeychainBackend();\n if (keychain.available) {\n return keychain.backend;\n }\n // The user explicitly asked for keychain but the platform can't\n // provide one (no libsecret on Linux, missing native module, etc).\n // Silently degrade to the file backend so the CLI stays usable; the\n // active backend is still visible through `dreamboard auth status`.\n return fileCredentialBackend;\n}\n\nasync function readCredentialBackendPreference(): Promise<boolean> {\n try {\n // Dynamic import to avoid a top-level cycle with `global-config.ts`\n // (which imports `getCredentialFilePath` from this module). Using\n // the async path keeps the cycle purely lazy.\n const { loadGlobalConfig } = await import(\"./global-config.js\");\n const config = await loadGlobalConfig();\n return config.credentialBackend === \"keychain\";\n } catch {\n // If the config file is unreadable or the dynamic import fails\n // (e.g. during early bootstrap), fall back to the file-backed\n // default rather than crashing credential lookups.\n return false;\n }\n}\n\n/**\n * Override which backend is used. Tests use this to inject in-memory\n * backends; production code uses the default keychain-first resolver.\n */\nexport function setCredentialBackendResolver(resolver: BackendResolver): void {\n backendResolver = resolver;\n cachedBackend = null;\n migrationCompleted = false;\n}\n\nexport async function getCredentialBackend(): Promise<CredentialBackend> {\n if (cachedBackend === null) {\n cachedBackend = await backendResolver();\n // One-time migration: if we resolved to a non-file backend and\n // `auth.json` still has credentials from the old layout, copy them\n // over and remove the file. We only do this when the new backend is\n // empty, so repeated migrations cannot stomp a newer keychain\n // session with a stale file session.\n if (!migrationCompleted && cachedBackend.name !== \"file\") {\n await migrateFromFileBackendIfNeeded(cachedBackend);\n }\n migrationCompleted = true;\n }\n return cachedBackend;\n}\n\nasync function migrateFromFileBackendIfNeeded(\n target: CredentialBackend,\n): Promise<void> {\n try {\n const [onDisk, onTarget] = await Promise.all([\n fileCredentialBackend.read(),\n target.read(),\n ]);\n if (!onDisk) return;\n if (onTarget) {\n // Target already has a session - the user has already migrated.\n // Remove the file so it cannot get re-used accidentally.\n await fileCredentialBackend.clear();\n return;\n }\n if (onDisk.accessToken && onDisk.refreshToken) {\n await target.writeFull({\n accessToken: onDisk.accessToken,\n refreshToken: onDisk.refreshToken,\n });\n } else if (onDisk.accessToken) {\n await target.writeAccessOnly(onDisk.accessToken);\n } else {\n return;\n }\n await fileCredentialBackend.clear();\n } catch {\n // Migration is best-effort. A failure here should not block CLI\n // operation; on next run the file backend is still consulted\n // directly because the keychain backend's `read` returns null and\n // callers fall through to \"missing session\" → login prompt.\n }\n}\n\nexport async function getActiveCredentialBackendName(): Promise<CredentialBackendName> {\n const backend = await getCredentialBackend();\n return backend.name;\n}\n\n/** Loose read: returns whatever is on disk, including access-only sessions. */\nexport async function getStoredSession(): Promise<StoredSessionSnapshot | null> {\n const backend = await getCredentialBackend();\n return backend.read();\n}\n\n/** Strict read: returns a refreshable pair, or null if either token is missing. */\nexport async function getCredentials(): Promise<Credentials | null> {\n const snapshot = await getStoredSession();\n if (!snapshot) return null;\n const { accessToken, refreshToken } = snapshot;\n if (!accessToken || !refreshToken) return null;\n return { accessToken, refreshToken };\n}\n\nexport async function setCredentials(creds: Credentials): Promise<void> {\n await withFileLock(getCredentialLockPath(), async () => {\n const backend = await getCredentialBackend();\n await backend.writeFull(creds);\n });\n}\n\nexport async function setAccessOnlySession(accessToken: string): Promise<void> {\n await withFileLock(getCredentialLockPath(), async () => {\n const backend = await getCredentialBackend();\n await backend.writeAccessOnly(accessToken);\n });\n}\n\nexport async function clearCredentials(): Promise<void> {\n await withFileLock(getCredentialLockPath(), async () => {\n const backend = await getCredentialBackend();\n await backend.clear();\n });\n}\n\n/**\n * Run `fn` while holding the cross-process credential lock. `fn` receives\n * an ops handle that reads/writes the active backend without re-acquiring\n * the lock (avoiding deadlock).\n *\n * This is the only correct way to perform a read-modify-write on stored\n * credentials (e.g. CLI refresh rotation) in the presence of\n * concurrent CLI invocations.\n */\nexport async function withCredentialLock<T>(\n fn: (ops: CredentialLockOps) => Promise<T>,\n options?: FileLockOptions,\n): Promise<T> {\n return withFileLock(\n getCredentialLockPath(),\n async () => {\n const backend = await getCredentialBackend();\n const ops: CredentialLockOps = {\n backendName: backend.name,\n read: () => backend.read(),\n writeFull: (creds) => backend.writeFull(creds),\n writeAccessOnly: (accessToken) => backend.writeAccessOnly(accessToken),\n clear: () => backend.clear(),\n };\n return fn(ops);\n },\n options,\n );\n}\n\n/** Test-only reset of module state. Not exported through the barrel. */\nexport function _resetCredentialStoreForTests(): void {\n cachedBackend = null;\n migrationCompleted = false;\n backendResolver = defaultBackendResolver;\n}\n"],"mappings":"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","names":["os","path","loadGlobalConfig","path","os"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../node_modules/.pnpm/citty@0.2.2/node_modules/citty/dist/index.mjs","../../../../packages/api-client/dist/source-revisions.js","../../src/command-args.ts","../../src/flags.ts","../../src/utils/strings.ts","../../src/services/api/compiled-results-api.ts","../../src/services/api/project-api.ts","../../src/services/api/preview-api.ts","../../src/services/api/source-revisions-api.ts"],"sourcesContent":["import { n as kebabCase, r as snakeCase, t as camelCase } from \"./_chunks/libs/scule.mjs\";\nimport { parseArgs as parseArgs$1 } from \"node:util\";\n//#region src/_utils.ts\nfunction toArray(val) {\n\tif (Array.isArray(val)) return val;\n\treturn val === void 0 ? [] : [val];\n}\nfunction formatLineColumns(lines, linePrefix = \"\") {\n\tconst maxLength = [];\n\tfor (const line of lines) for (const [i, element] of line.entries()) maxLength[i] = Math.max(maxLength[i] || 0, element.length);\n\treturn lines.map((l) => l.map((c, i) => linePrefix + c[i === 0 ? \"padStart\" : \"padEnd\"](maxLength[i])).join(\" \")).join(\"\\n\");\n}\nfunction resolveValue(input) {\n\treturn typeof input === \"function\" ? input() : input;\n}\nvar CLIError = class extends Error {\n\tcode;\n\tconstructor(message, code) {\n\t\tsuper(message);\n\t\tthis.name = \"CLIError\";\n\t\tthis.code = code;\n\t}\n};\n//#endregion\n//#region src/_parser.ts\nfunction parseRawArgs(args = [], opts = {}) {\n\tconst booleans = new Set(opts.boolean || []);\n\tconst strings = new Set(opts.string || []);\n\tconst aliasMap = opts.alias || {};\n\tconst defaults = opts.default || {};\n\tconst aliasToMain = /* @__PURE__ */ new Map();\n\tconst mainToAliases = /* @__PURE__ */ new Map();\n\tfor (const [key, value] of Object.entries(aliasMap)) {\n\t\tconst targets = value;\n\t\tfor (const target of targets) {\n\t\t\taliasToMain.set(key, target);\n\t\t\tif (!mainToAliases.has(target)) mainToAliases.set(target, []);\n\t\t\tmainToAliases.get(target).push(key);\n\t\t\taliasToMain.set(target, key);\n\t\t\tif (!mainToAliases.has(key)) mainToAliases.set(key, []);\n\t\t\tmainToAliases.get(key).push(target);\n\t\t}\n\t}\n\tconst options = {};\n\tfunction getType(name) {\n\t\tif (booleans.has(name)) return \"boolean\";\n\t\tconst aliases = mainToAliases.get(name) || [];\n\t\tfor (const alias of aliases) if (booleans.has(alias)) return \"boolean\";\n\t\treturn \"string\";\n\t}\n\tfunction isStringType(name) {\n\t\tif (strings.has(name)) return true;\n\t\tconst aliases = mainToAliases.get(name) || [];\n\t\tfor (const alias of aliases) if (strings.has(alias)) return true;\n\t\treturn false;\n\t}\n\tconst allOptions = new Set([\n\t\t...booleans,\n\t\t...strings,\n\t\t...Object.keys(aliasMap),\n\t\t...Object.values(aliasMap).flat(),\n\t\t...Object.keys(defaults)\n\t]);\n\tfor (const name of allOptions) if (!options[name]) options[name] = {\n\t\ttype: getType(name),\n\t\tdefault: defaults[name]\n\t};\n\tfor (const [alias, main] of aliasToMain.entries()) if (alias.length === 1 && options[main] && !options[main].short) options[main].short = alias;\n\tconst processedArgs = [];\n\tconst negatedFlags = {};\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\t\tif (arg === \"--\") {\n\t\t\tprocessedArgs.push(...args.slice(i));\n\t\t\tbreak;\n\t\t}\n\t\tif (arg.startsWith(\"--no-\")) {\n\t\t\tconst flagName = arg.slice(5);\n\t\t\tnegatedFlags[flagName] = true;\n\t\t\tcontinue;\n\t\t}\n\t\tprocessedArgs.push(arg);\n\t}\n\tlet parsed;\n\ttry {\n\t\tparsed = parseArgs$1({\n\t\t\targs: processedArgs,\n\t\t\toptions: Object.keys(options).length > 0 ? options : void 0,\n\t\t\tallowPositionals: true,\n\t\t\tstrict: false\n\t\t});\n\t} catch {\n\t\tparsed = {\n\t\t\tvalues: {},\n\t\t\tpositionals: processedArgs\n\t\t};\n\t}\n\tconst out = { _: [] };\n\tout._ = parsed.positionals;\n\tfor (const [key, value] of Object.entries(parsed.values)) {\n\t\tlet coerced = value;\n\t\tif (getType(key) === \"boolean\" && typeof value === \"string\") coerced = value !== \"false\";\n\t\telse if (isStringType(key) && typeof value === \"boolean\") coerced = \"\";\n\t\tout[key] = coerced;\n\t}\n\tfor (const [name] of Object.entries(negatedFlags)) {\n\t\tout[name] = false;\n\t\tconst mainName = aliasToMain.get(name);\n\t\tif (mainName) out[mainName] = false;\n\t\tconst aliases = mainToAliases.get(name);\n\t\tif (aliases) for (const alias of aliases) out[alias] = false;\n\t}\n\tfor (const [alias, main] of aliasToMain.entries()) {\n\t\tif (out[alias] !== void 0 && out[main] === void 0) out[main] = out[alias];\n\t\tif (out[main] !== void 0 && out[alias] === void 0) out[alias] = out[main];\n\t\tif (out[alias] !== out[main] && defaults[main] === out[main]) out[main] = out[alias];\n\t}\n\treturn out;\n}\n//#endregion\n//#region src/_color.ts\nconst noColor = /* @__PURE__ */ (() => {\n\tconst env = globalThis.process?.env ?? {};\n\treturn env.NO_COLOR === \"1\" || env.TERM === \"dumb\" || env.TEST || env.CI;\n})();\nconst _c = (c, r = 39) => (t) => noColor ? t : `\\u001B[${c}m${t}\\u001B[${r}m`;\nconst bold = /* @__PURE__ */ _c(1, 22);\nconst cyan = /* @__PURE__ */ _c(36);\nconst gray = /* @__PURE__ */ _c(90);\nconst underline = /* @__PURE__ */ _c(4, 24);\n//#endregion\n//#region src/args.ts\nfunction parseArgs(rawArgs, argsDef) {\n\tconst parseOptions = {\n\t\tboolean: [],\n\t\tstring: [],\n\t\talias: {},\n\t\tdefault: {}\n\t};\n\tconst args = resolveArgs(argsDef);\n\tfor (const arg of args) {\n\t\tif (arg.type === \"positional\") continue;\n\t\tif (arg.type === \"string\" || arg.type === \"enum\") parseOptions.string.push(arg.name);\n\t\telse if (arg.type === \"boolean\") parseOptions.boolean.push(arg.name);\n\t\tif (arg.default !== void 0) parseOptions.default[arg.name] = arg.default;\n\t\tif (arg.alias) parseOptions.alias[arg.name] = arg.alias;\n\t\tconst camelName = camelCase(arg.name);\n\t\tconst kebabName = kebabCase(arg.name);\n\t\tif (camelName !== arg.name || kebabName !== arg.name) {\n\t\t\tconst existingAliases = toArray(parseOptions.alias[arg.name] || []);\n\t\t\tif (camelName !== arg.name && !existingAliases.includes(camelName)) existingAliases.push(camelName);\n\t\t\tif (kebabName !== arg.name && !existingAliases.includes(kebabName)) existingAliases.push(kebabName);\n\t\t\tif (existingAliases.length > 0) parseOptions.alias[arg.name] = existingAliases;\n\t\t}\n\t}\n\tconst parsed = parseRawArgs(rawArgs, parseOptions);\n\tconst [ ...positionalArguments] = parsed._;\n\tconst parsedArgsProxy = new Proxy(parsed, { get(target, prop) {\n\t\treturn target[prop] ?? target[camelCase(prop)] ?? target[kebabCase(prop)];\n\t} });\n\tfor (const [, arg] of args.entries()) if (arg.type === \"positional\") {\n\t\tconst nextPositionalArgument = positionalArguments.shift();\n\t\tif (nextPositionalArgument !== void 0) parsedArgsProxy[arg.name] = nextPositionalArgument;\n\t\telse if (arg.default === void 0 && arg.required !== false) throw new CLIError(`Missing required positional argument: ${arg.name.toUpperCase()}`, \"EARG\");\n\t\telse parsedArgsProxy[arg.name] = arg.default;\n\t} else if (arg.type === \"enum\") {\n\t\tconst argument = parsedArgsProxy[arg.name];\n\t\tconst options = arg.options || [];\n\t\tif (argument !== void 0 && options.length > 0 && !options.includes(argument)) throw new CLIError(`Invalid value for argument: ${cyan(`--${arg.name}`)} (${cyan(argument)}). Expected one of: ${options.map((o) => cyan(o)).join(\", \")}.`, \"EARG\");\n\t} else if (arg.required && parsedArgsProxy[arg.name] === void 0) throw new CLIError(`Missing required argument: --${arg.name}`, \"EARG\");\n\treturn parsedArgsProxy;\n}\nfunction resolveArgs(argsDef) {\n\tconst args = [];\n\tfor (const [name, argDef] of Object.entries(argsDef || {})) args.push({\n\t\t...argDef,\n\t\tname,\n\t\talias: toArray(argDef.alias)\n\t});\n\treturn args;\n}\n//#endregion\n//#region src/plugin.ts\nfunction defineCittyPlugin(plugin) {\n\treturn plugin;\n}\nasync function resolvePlugins(plugins) {\n\treturn Promise.all(plugins.map((p) => resolveValue(p)));\n}\n//#endregion\n//#region src/command.ts\nfunction defineCommand(def) {\n\treturn def;\n}\nasync function runCommand(cmd, opts) {\n\tconst cmdArgs = await resolveValue(cmd.args || {});\n\tconst parsedArgs = parseArgs(opts.rawArgs, cmdArgs);\n\tconst context = {\n\t\trawArgs: opts.rawArgs,\n\t\targs: parsedArgs,\n\t\tdata: opts.data,\n\t\tcmd\n\t};\n\tconst plugins = await resolvePlugins(cmd.plugins ?? []);\n\tlet result;\n\tlet runError;\n\ttry {\n\t\tfor (const plugin of plugins) await plugin.setup?.(context);\n\t\tif (typeof cmd.setup === \"function\") await cmd.setup(context);\n\t\tconst subCommands = await resolveValue(cmd.subCommands);\n\t\tif (subCommands && Object.keys(subCommands).length > 0) {\n\t\t\tconst subCommandArgIndex = findSubCommandIndex(opts.rawArgs, cmdArgs);\n\t\t\tconst explicitName = opts.rawArgs[subCommandArgIndex];\n\t\t\tif (explicitName) {\n\t\t\t\tconst subCommand = await _findSubCommand(subCommands, explicitName);\n\t\t\t\tif (!subCommand) throw new CLIError(`Unknown command ${cyan(explicitName)}`, \"E_UNKNOWN_COMMAND\");\n\t\t\t\tawait runCommand(subCommand, { rawArgs: opts.rawArgs.slice(subCommandArgIndex + 1) });\n\t\t\t} else {\n\t\t\t\tconst defaultSubCommand = await resolveValue(cmd.default);\n\t\t\t\tif (defaultSubCommand) {\n\t\t\t\t\tif (cmd.run) throw new CLIError(`Cannot specify both 'run' and 'default' on the same command.`, \"E_DEFAULT_CONFLICT\");\n\t\t\t\t\tconst subCommand = await _findSubCommand(subCommands, defaultSubCommand);\n\t\t\t\t\tif (!subCommand) throw new CLIError(`Default sub command ${cyan(defaultSubCommand)} not found in subCommands.`, \"E_UNKNOWN_COMMAND\");\n\t\t\t\t\tawait runCommand(subCommand, { rawArgs: opts.rawArgs });\n\t\t\t\t} else if (!cmd.run) throw new CLIError(`No command specified.`, \"E_NO_COMMAND\");\n\t\t\t}\n\t\t}\n\t\tif (typeof cmd.run === \"function\") result = await cmd.run(context);\n\t} catch (error) {\n\t\trunError = error;\n\t}\n\tconst cleanupErrors = [];\n\tif (typeof cmd.cleanup === \"function\") try {\n\t\tawait cmd.cleanup(context);\n\t} catch (error) {\n\t\tcleanupErrors.push(error);\n\t}\n\tfor (const plugin of [...plugins].reverse()) try {\n\t\tawait plugin.cleanup?.(context);\n\t} catch (error) {\n\t\tcleanupErrors.push(error);\n\t}\n\tif (runError) throw runError;\n\tif (cleanupErrors.length === 1) throw cleanupErrors[0];\n\tif (cleanupErrors.length > 1) throw new Error(\"Multiple cleanup errors\", { cause: cleanupErrors });\n\treturn { result };\n}\nasync function resolveSubCommand(cmd, rawArgs, parent) {\n\tconst subCommands = await resolveValue(cmd.subCommands);\n\tif (subCommands && Object.keys(subCommands).length > 0) {\n\t\tconst subCommandArgIndex = findSubCommandIndex(rawArgs, await resolveValue(cmd.args || {}));\n\t\tconst subCommandName = rawArgs[subCommandArgIndex];\n\t\tconst subCommand = await _findSubCommand(subCommands, subCommandName);\n\t\tif (subCommand) return resolveSubCommand(subCommand, rawArgs.slice(subCommandArgIndex + 1), cmd);\n\t}\n\treturn [cmd, parent];\n}\nasync function _findSubCommand(subCommands, name) {\n\tif (name in subCommands) return resolveValue(subCommands[name]);\n\tfor (const sub of Object.values(subCommands)) {\n\t\tconst resolved = await resolveValue(sub);\n\t\tconst meta = await resolveValue(resolved?.meta);\n\t\tif (meta?.alias) {\n\t\t\tif (toArray(meta.alias).includes(name)) return resolved;\n\t\t}\n\t}\n}\nfunction findSubCommandIndex(rawArgs, argsDef) {\n\tfor (let i = 0; i < rawArgs.length; i++) {\n\t\tconst arg = rawArgs[i];\n\t\tif (arg === \"--\") return -1;\n\t\tif (arg.startsWith(\"-\")) {\n\t\t\tif (!arg.includes(\"=\") && _isValueFlag(arg, argsDef)) i++;\n\t\t\tcontinue;\n\t\t}\n\t\treturn i;\n\t}\n\treturn -1;\n}\nfunction _isValueFlag(flag, argsDef) {\n\tconst name = flag.replace(/^-{1,2}/, \"\");\n\tconst normalized = camelCase(name);\n\tfor (const [key, def] of Object.entries(argsDef)) {\n\t\tif (def.type !== \"string\" && def.type !== \"enum\") continue;\n\t\tif (normalized === camelCase(key)) return true;\n\t\tif ((Array.isArray(def.alias) ? def.alias : def.alias ? [def.alias] : []).includes(name)) return true;\n\t}\n\treturn false;\n}\n//#endregion\n//#region src/usage.ts\nasync function showUsage(cmd, parent) {\n\ttry {\n\t\tconsole.log(await renderUsage(cmd, parent) + \"\\n\");\n\t} catch (error) {\n\t\tconsole.error(error);\n\t}\n}\nconst negativePrefixRe = /^no[-A-Z]/;\nasync function renderUsage(cmd, parent) {\n\tconst cmdMeta = await resolveValue(cmd.meta || {});\n\tconst cmdArgs = resolveArgs(await resolveValue(cmd.args || {}));\n\tconst parentMeta = await resolveValue(parent?.meta || {});\n\tconst commandName = `${parentMeta.name ? `${parentMeta.name} ` : \"\"}` + (cmdMeta.name || process.argv[1]);\n\tconst argLines = [];\n\tconst posLines = [];\n\tconst commandsLines = [];\n\tconst usageLine = [];\n\tfor (const arg of cmdArgs) if (arg.type === \"positional\") {\n\t\tconst name = arg.name.toUpperCase();\n\t\tconst isRequired = arg.required !== false && arg.default === void 0;\n\t\tposLines.push([cyan(name + renderValueHint(arg)), renderDescription(arg, isRequired)]);\n\t\tusageLine.push(isRequired ? `<${name}>` : `[${name}]`);\n\t} else {\n\t\tconst isRequired = arg.required === true && arg.default === void 0;\n\t\tconst argStr = [...(arg.alias || []).map((a) => `-${a}`), `--${arg.name}`].join(\", \") + renderValueHint(arg);\n\t\targLines.push([cyan(argStr), renderDescription(arg, isRequired)]);\n\t\t/**\n\t\t* print negative boolean arg variant usage when\n\t\t* - enabled by default or has `negativeDescription`\n\t\t* - not prefixed with `no-` or `no[A-Z]`\n\t\t*/\n\t\tif (arg.type === \"boolean\" && (arg.default === true || arg.negativeDescription) && !negativePrefixRe.test(arg.name)) {\n\t\t\tconst negativeArgStr = [...(arg.alias || []).map((a) => `--no-${a}`), `--no-${arg.name}`].join(\", \");\n\t\t\targLines.push([cyan(negativeArgStr), [arg.negativeDescription, isRequired ? gray(\"(Required)\") : \"\"].filter(Boolean).join(\" \")]);\n\t\t}\n\t\tif (isRequired) usageLine.push(`--${arg.name}` + renderValueHint(arg));\n\t}\n\tif (cmd.subCommands) {\n\t\tconst commandNames = [];\n\t\tconst subCommands = await resolveValue(cmd.subCommands);\n\t\tfor (const [name, sub] of Object.entries(subCommands)) {\n\t\t\tconst meta = await resolveValue((await resolveValue(sub))?.meta);\n\t\t\tif (meta?.hidden) continue;\n\t\t\tconst aliases = toArray(meta?.alias);\n\t\t\tconst label = [name, ...aliases].join(\", \");\n\t\t\tcommandsLines.push([cyan(label), meta?.description || \"\"]);\n\t\t\tcommandNames.push(name, ...aliases);\n\t\t}\n\t\tusageLine.push(commandNames.join(\"|\"));\n\t}\n\tconst usageLines = [];\n\tconst version = cmdMeta.version || parentMeta.version;\n\tusageLines.push(gray(`${cmdMeta.description} (${commandName + (version ? ` v${version}` : \"\")})`), \"\");\n\tconst hasOptions = argLines.length > 0 || posLines.length > 0;\n\tusageLines.push(`${underline(bold(\"USAGE\"))} ${cyan(`${commandName}${hasOptions ? \" [OPTIONS]\" : \"\"} ${usageLine.join(\" \")}`)}`, \"\");\n\tif (posLines.length > 0) {\n\t\tusageLines.push(underline(bold(\"ARGUMENTS\")), \"\");\n\t\tusageLines.push(formatLineColumns(posLines, \" \"));\n\t\tusageLines.push(\"\");\n\t}\n\tif (argLines.length > 0) {\n\t\tusageLines.push(underline(bold(\"OPTIONS\")), \"\");\n\t\tusageLines.push(formatLineColumns(argLines, \" \"));\n\t\tusageLines.push(\"\");\n\t}\n\tif (commandsLines.length > 0) {\n\t\tusageLines.push(underline(bold(\"COMMANDS\")), \"\");\n\t\tusageLines.push(formatLineColumns(commandsLines, \" \"));\n\t\tusageLines.push(\"\", `Use ${cyan(`${commandName} <command> --help`)} for more information about a command.`);\n\t}\n\treturn usageLines.filter((l) => typeof l === \"string\").join(\"\\n\");\n}\nfunction renderValueHint(arg) {\n\tconst valueHint = arg.valueHint ? `=<${arg.valueHint}>` : \"\";\n\tconst fallbackValueHint = valueHint || `=<${snakeCase(arg.name)}>`;\n\tif (!arg.type || arg.type === \"positional\" || arg.type === \"boolean\") return valueHint;\n\tif (arg.type === \"enum\" && arg.options?.length) return `=<${arg.options.join(\"|\")}>`;\n\treturn fallbackValueHint;\n}\nfunction renderDescription(arg, required) {\n\tconst requiredHint = required ? gray(\"(Required)\") : \"\";\n\tconst defaultHint = arg.default === void 0 ? \"\" : gray(`(Default: ${arg.default})`);\n\treturn [\n\t\targ.description,\n\t\trequiredHint,\n\t\tdefaultHint\n\t].filter(Boolean).join(\" \");\n}\n//#endregion\n//#region src/main.ts\nasync function runMain(cmd, opts = {}) {\n\tconst rawArgs = opts.rawArgs || process.argv.slice(2);\n\tconst showUsage$1 = opts.showUsage || showUsage;\n\ttry {\n\t\tconst builtinFlags = await _resolveBuiltinFlags(cmd);\n\t\tif (builtinFlags.help.length > 0 && rawArgs.some((arg) => builtinFlags.help.includes(arg))) {\n\t\t\tawait showUsage$1(...await resolveSubCommand(cmd, rawArgs));\n\t\t\tprocess.exit(0);\n\t\t} else if (rawArgs.length === 1 && builtinFlags.version.includes(rawArgs[0])) {\n\t\t\tconst meta = typeof cmd.meta === \"function\" ? await cmd.meta() : await cmd.meta;\n\t\t\tif (!meta?.version) throw new CLIError(\"No version specified\", \"E_NO_VERSION\");\n\t\t\tconsole.log(meta.version);\n\t\t} else await runCommand(cmd, { rawArgs });\n\t} catch (error) {\n\t\tif (error instanceof CLIError) {\n\t\t\tawait showUsage$1(...await resolveSubCommand(cmd, rawArgs));\n\t\t\tconsole.error(error.message);\n\t\t} else console.error(error, \"\\n\");\n\t\tprocess.exit(1);\n\t}\n}\nfunction createMain(cmd) {\n\treturn (opts = {}) => runMain(cmd, opts);\n}\nasync function _resolveBuiltinFlags(cmd) {\n\tconst argsDef = await resolveValue(cmd.args || {});\n\tconst userNames = /* @__PURE__ */ new Set();\n\tconst userAliases = /* @__PURE__ */ new Set();\n\tfor (const [name, def] of Object.entries(argsDef)) {\n\t\tuserNames.add(name);\n\t\tfor (const alias of toArray(def.alias)) userAliases.add(alias);\n\t}\n\treturn {\n\t\thelp: _getBuiltinFlags(\"help\", \"h\", userNames, userAliases),\n\t\tversion: _getBuiltinFlags(\"version\", \"v\", userNames, userAliases)\n\t};\n}\nfunction _getBuiltinFlags(long, short, userNames, userAliases) {\n\tif (userNames.has(long) || userAliases.has(long)) return [];\n\tif (userNames.has(short) || userAliases.has(short)) return [`--${long}`];\n\treturn [`--${long}`, `-${short}`];\n}\n//#endregion\nexport { createMain, defineCittyPlugin, defineCommand, parseArgs, renderUsage, runCommand, runMain, showUsage };\n","import { createProjectSourceBlobUploadSession, createSourceBlobUploadSession, } from \"./sdk.gen.js\";\nconst textEncoder = new TextEncoder();\nconst SOURCE_BLOB_UPLOAD_SESSION_BATCH_SIZE = 20;\nfunction bytesToHex(bytes) {\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\nasync function sha256Hex(bytes) {\n const normalizedBytes = new Uint8Array(bytes.byteLength);\n normalizedBytes.set(bytes);\n const digest = await crypto.subtle.digest(\"SHA-256\", normalizedBytes.buffer);\n return bytesToHex(new Uint8Array(digest));\n}\nfunction getUtf8ByteSize(content) {\n return textEncoder.encode(content).byteLength;\n}\nasync function computeSourceContentHash(content) {\n return sha256Hex(textEncoder.encode(content));\n}\nasync function describeSourceBlob(content) {\n return {\n contentHash: await computeSourceContentHash(content),\n byteSize: getUtf8ByteSize(content),\n };\n}\nexport async function materializeSourceChangeOperations(changes) {\n const blobsByHash = new Map();\n const materialized = await Promise.all(Array.from(changes, async (change) => {\n if (change.kind === \"delete\") {\n return change;\n }\n const blob = await describeSourceBlob(change.content);\n const existing = blobsByHash.get(blob.contentHash);\n if (!existing) {\n blobsByHash.set(blob.contentHash, blob);\n }\n return {\n kind: \"upsert\",\n path: change.path,\n contentHash: blob.contentHash,\n byteSize: blob.byteSize,\n };\n }));\n return {\n blobs: Array.from(blobsByHash.values()).sort((left, right) => left.contentHash.localeCompare(right.contentHash)),\n changes: materialized,\n };\n}\nexport function mapUpsertBlobContentsByContentHash(localChanges, materializedChanges) {\n const uploadBlobs = new Map();\n const length = Math.min(localChanges.length, materializedChanges.length);\n for (let index = 0; index < length; index += 1) {\n const localChange = localChanges[index];\n const materializedChange = materializedChanges[index];\n if (localChange?.kind !== \"upsert\" ||\n materializedChange?.kind !== \"upsert\") {\n continue;\n }\n uploadBlobs.set(materializedChange.contentHash, {\n contentHash: materializedChange.contentHash,\n byteSize: materializedChange.byteSize,\n content: localChange.content,\n });\n }\n return uploadBlobs;\n}\nclass SourceBlobUploadError extends Error {\n constructor(status, details) {\n const suffix = details.trim().length > 0 ? `: ${details.trim()}` : \"\";\n super(`Failed to upload source blob (HTTP ${status}${suffix})`);\n this.name = \"SourceBlobUploadError\";\n this.status = status;\n this.details = details;\n }\n}\nfunction isDuplicateDirectUploadError(error) {\n if (!(error instanceof SourceBlobUploadError)) {\n return false;\n }\n if (error.status === 409) {\n return true;\n }\n const normalizedDetails = error.details.toLowerCase();\n return (normalizedDetails.includes(\"duplicate\") ||\n normalizedDetails.includes(\"already exists\") ||\n normalizedDetails.includes(\"resource already exists\"));\n}\nasync function uploadSourceBlob(uploadTarget, content) {\n const response = await fetch(uploadTarget.url, {\n method: uploadTarget.method,\n headers: uploadTarget.headers,\n body: textEncoder.encode(content),\n });\n if (response.ok) {\n return;\n }\n const details = await response.text().catch(() => \"\");\n throw new SourceBlobUploadError(response.status, details);\n}\nexport class SourceBlobSessionRequestError extends Error {\n constructor(message, apiError, response) {\n super(message);\n this.name = \"SourceBlobSessionRequestError\";\n this.apiError = apiError;\n this.response = response;\n }\n}\nfunction assertSourceBlobUploadSession(data, response) {\n if (!data ||\n typeof data !== \"object\" ||\n !Array.isArray(data.uploads)) {\n throw new SourceBlobSessionRequestError(\"Source blob upload session response did not include an uploads array\", data, response);\n }\n}\nasync function confirmSourceBlobAlreadyExists(options) {\n const { requestUploadSession, blob } = options;\n const { data, error, response } = await requestUploadSession([\n {\n contentHash: blob.contentHash,\n byteSize: blob.byteSize,\n },\n ]);\n if (error || !data) {\n throw new SourceBlobSessionRequestError(\"Failed to create source blob upload session\", error, response);\n }\n assertSourceBlobUploadSession(data, response);\n return data.uploads[0]?.status === \"exists\";\n}\nasync function uploadSourceBlobs(options) {\n const { blobs, requestUploadSession } = options;\n const uniqueBlobs = new Map();\n for (const blob of blobs) {\n const existing = uniqueBlobs.get(blob.contentHash);\n if (!existing) {\n uniqueBlobs.set(blob.contentHash, blob);\n continue;\n }\n if (existing.byteSize !== blob.byteSize) {\n throw new Error(`Source blob ${blob.contentHash} has conflicting byte sizes.`);\n }\n }\n if (uniqueBlobs.size === 0) {\n return;\n }\n for (const uploadBatch of chunkSourceBlobs(Array.from(uniqueBlobs.values()), SOURCE_BLOB_UPLOAD_SESSION_BATCH_SIZE)) {\n const { data, error, response } = await requestUploadSession(uploadBatch.map(({ contentHash, byteSize }) => ({\n contentHash,\n byteSize,\n })));\n if (error || !data) {\n throw new SourceBlobSessionRequestError(\"Failed to create source blob upload session\", error, response);\n }\n assertSourceBlobUploadSession(data, response);\n for (const upload of data.uploads) {\n if (upload.status !== \"upload_required\") {\n continue;\n }\n const blob = uniqueBlobs.get(upload.contentHash);\n if (!blob) {\n throw new Error(`Upload session referenced unknown source blob ${upload.contentHash}.`);\n }\n if (!upload.uploadTarget) {\n throw new Error(`Upload target missing for source blob ${upload.contentHash}.`);\n }\n try {\n await uploadSourceBlob(upload.uploadTarget, blob.content);\n if (!(await confirmSourceBlobAlreadyExists({ requestUploadSession, blob }))) {\n throw new Error(`Source blob ${blob.contentHash} was uploaded but not registered.`);\n }\n }\n catch (error) {\n if (isDuplicateDirectUploadError(error) &&\n (await confirmSourceBlobAlreadyExists({ requestUploadSession, blob }))) {\n continue;\n }\n throw error;\n }\n }\n }\n}\nfunction chunkSourceBlobs(blobs, batchSize) {\n const chunks = [];\n for (let index = 0; index < blobs.length; index += batchSize) {\n chunks.push(blobs.slice(index, index + batchSize));\n }\n return chunks;\n}\nexport async function uploadGameSourceBlobs(options) {\n const { gameId, blobs } = options;\n return uploadSourceBlobs({\n blobs,\n requestUploadSession: (uploadBlobs) => createSourceBlobUploadSession({\n path: { gameId },\n body: { blobs: uploadBlobs },\n }),\n });\n}\nexport async function uploadProjectSourceBlobs(options) {\n const { projectId, blobs } = options;\n return uploadSourceBlobs({\n blobs,\n requestUploadSession: (uploadBlobs) => createProjectSourceBlobUploadSession({\n path: { projectId },\n body: { blobs: uploadBlobs },\n }),\n });\n}\n","export const CONFIG_FLAG_ARGS = {\n env: {\n type: \"string\" as const,\n description: \"Environment: local | staging | prod\",\n },\n token: {\n type: \"string\" as const,\n description: \"Auth token (Dreamboard bearer JWT)\",\n },\n};\n","import { z } from \"zod\";\n\nconst configFlagsSchema = z.object({\n env: z.enum([\"local\", \"staging\", \"prod\"]).optional(),\n token: z.string().optional(),\n});\n\nconst ruleInputFlagsSchema = z.object({\n \"rule-file\": z.string().optional(),\n rule: z.string().optional(),\n});\n\nconst playerCountFlagsSchema = z.object({\n players: z.string().optional(),\n \"player-count\": z.string().optional(),\n});\n\nconst newCommandArgsSchema = configFlagsSchema.extend({\n slug: z.string().min(1),\n description: z.string().min(1),\n force: z.boolean().default(false),\n});\n\nconst cloneCommandArgsSchema = configFlagsSchema.extend({\n slug: z.string().min(1),\n});\n\nconst queryCommandArgsSchema = configFlagsSchema.extend({\n title: z.string().min(1),\n});\n\nconst pullCommandArgsSchema = configFlagsSchema.extend({\n force: z.boolean().default(false),\n});\n\nconst syncCommandArgsSchema = configFlagsSchema.extend({\n force: z.boolean().default(false),\n yes: z.boolean().default(false),\n});\n\nconst compileCommandArgsSchema = configFlagsSchema.extend({\n debug: z.boolean().default(false),\n \"skip-local-check\": z.boolean().default(false),\n});\n\nconst statusCommandArgsSchema = configFlagsSchema.extend({\n json: z.boolean().default(false),\n});\n\nconst devCommandArgsSchema = configFlagsSchema.extend({\n seed: z.string().optional(),\n \"setup-profile\": z.string().optional(),\n players: z.string().optional(),\n \"player-count\": z.string().optional(),\n debug: z.boolean().default(false),\n resume: z.string().optional(),\n \"from-scenario\": z.string().optional(),\n \"new-session\": z.boolean().default(false),\n open: z.boolean().default(false),\n port: z.string().optional(),\n host: z.union([z.string(), z.boolean()]).optional(),\n \"allowed-host\": z.string().optional(),\n});\n\nconst joinCommandArgsSchema = configFlagsSchema.extend({\n session: z.string().min(1).optional(),\n player: z.string().min(1),\n \"raw-events\": z.boolean().default(false),\n});\n\nconst loginCommandArgsSchema = configFlagsSchema;\n\nconst configCommandArgsSchema = configFlagsSchema.extend({\n action: z.string().optional().default(\"show\"),\n scope: z.enum([\"global\", \"workspace\"]).optional().default(\"global\"),\n});\n\nconst authCommandArgsSchema = z.object({\n action: z.enum([\"set\", \"clear\", \"login\", \"env\", \"status\"]),\n tokenValue: z.string().optional(),\n token: z.string().optional(),\n jwt: z.boolean().optional(),\n env: z.enum([\"local\", \"staging\", \"prod\"]).optional(),\n});\n\nexport type ConfigFlags = z.infer<typeof configFlagsSchema>;\nexport type RuleInputFlags = z.infer<typeof ruleInputFlagsSchema>;\nexport type PlayerCountFlags = z.infer<typeof playerCountFlagsSchema>;\n\nexport type NewCommandArgs = z.infer<typeof newCommandArgsSchema>;\nexport type CloneCommandArgs = z.infer<typeof cloneCommandArgsSchema>;\nexport type QueryCommandArgs = z.infer<typeof queryCommandArgsSchema>;\nexport type PullCommandArgs = z.infer<typeof pullCommandArgsSchema>;\nexport type SyncCommandArgs = z.infer<typeof syncCommandArgsSchema>;\nexport type CompileCommandArgs = z.infer<typeof compileCommandArgsSchema>;\nexport type StatusCommandArgs = z.infer<typeof statusCommandArgsSchema>;\nexport type DevCommandArgs = z.infer<typeof devCommandArgsSchema>;\nexport type JoinCommandArgs = z.infer<typeof joinCommandArgsSchema>;\nexport type LoginCommandArgs = z.infer<typeof loginCommandArgsSchema>;\nexport type ConfigCommandArgs = z.infer<typeof configCommandArgsSchema>;\nexport type AuthCommandArgs = z.infer<typeof authCommandArgsSchema>;\n\nfunction parseArgs<TOutput>(\n commandName: string,\n schema: z.ZodType<TOutput>,\n args: unknown,\n): TOutput {\n const parsed = schema.safeParse(args);\n if (parsed.success) {\n return parsed.data;\n }\n\n const details = parsed.error.issues\n .map((issue) => {\n const field = issue.path.length > 0 ? issue.path.join(\".\") : \"args\";\n return `${field}: ${issue.message}`;\n })\n .join(\"; \");\n throw new Error(`Invalid arguments for '${commandName}': ${details}`);\n}\n\nexport function parseConfigFlags(args: unknown): ConfigFlags {\n return parseArgs(\"config-flags\", configFlagsSchema, args);\n}\n\nexport function parseRuleInputFlags(args: unknown): RuleInputFlags {\n return parseArgs(\"rule-flags\", ruleInputFlagsSchema, args);\n}\n\nexport function parsePlayerCountFlags(args: unknown): PlayerCountFlags {\n return parseArgs(\"player-count\", playerCountFlagsSchema, args);\n}\n\nexport function parseNewCommandArgs(args: unknown): NewCommandArgs {\n return parseArgs(\"new\", newCommandArgsSchema, args);\n}\n\nexport function parseCloneCommandArgs(args: unknown): CloneCommandArgs {\n return parseArgs(\"clone\", cloneCommandArgsSchema, args);\n}\n\nexport function parseQueryCommandArgs(args: unknown): QueryCommandArgs {\n return parseArgs(\"query\", queryCommandArgsSchema, args);\n}\n\nexport function parsePullCommandArgs(args: unknown): PullCommandArgs {\n return parseArgs(\"pull\", pullCommandArgsSchema, args);\n}\n\nexport function parseSyncCommandArgs(args: unknown): SyncCommandArgs {\n return parseArgs(\"sync\", syncCommandArgsSchema, args);\n}\n\nexport function parseCompileCommandArgs(args: unknown): CompileCommandArgs {\n return parseArgs(\"compile\", compileCommandArgsSchema, args);\n}\n\nexport function parseStatusCommandArgs(args: unknown): StatusCommandArgs {\n return parseArgs(\"status\", statusCommandArgsSchema, args);\n}\n\nexport function parseDevCommandArgs(args: unknown): DevCommandArgs {\n return parseArgs(\"dev\", devCommandArgsSchema, args);\n}\n\nexport function parseJoinCommandArgs(args: unknown): JoinCommandArgs {\n return parseArgs(\"join\", joinCommandArgsSchema, args);\n}\n\nexport function parseLoginCommandArgs(args: unknown): LoginCommandArgs {\n return parseArgs(\"login\", loginCommandArgsSchema, args);\n}\n\nexport function parseConfigCommandArgs(args: unknown): ConfigCommandArgs {\n return parseArgs(\"config\", configCommandArgsSchema, args);\n}\n\nexport function parseAuthCommandArgs(args: unknown): AuthCommandArgs {\n return parseArgs(\"auth\", authCommandArgsSchema, args);\n}\n","import path from \"node:path\";\nimport type { RuleInputFlags } from \"../flags.js\";\nimport { readTextFile } from \"./fs.js\";\n\nexport function normalizeSlug(input: string): string {\n const lowered = input.trim().toLowerCase();\n const replaced = lowered.replace(/[^a-z0-9]+/g, \"-\");\n return replaced.replace(/^-+/, \"\").replace(/-+$/, \"\");\n}\n\nexport function titleFromSlug(slug: string): string {\n return slug\n .split(\"-\")\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\" \");\n}\n\nexport async function readRuleInput(flags: RuleInputFlags): Promise<string> {\n if (flags[\"rule-file\"] && typeof flags[\"rule-file\"] === \"string\") {\n const filePath = path.resolve(process.cwd(), flags[\"rule-file\"]);\n return readTextFile(filePath);\n }\n if (flags.rule && typeof flags.rule === \"string\") {\n return flags.rule;\n }\n throw new Error('Provide rule input via --rule-file or --rule \"...\".');\n}\n\nexport function parsePositiveInt(value: string, label: string): number {\n const parsed = Number.parseInt(value, 10);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n throw new Error(`${label} must be a positive integer.`);\n }\n return parsed;\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import {\n getCompiledResult,\n getJob,\n getLatestCompiledResult,\n getProjectCompiledResult,\n listProjectCompiledResults,\n listCompiledResults,\n queueProjectRevisionCompile,\n type CompiledResult,\n type JobDetailResponse,\n type QueueCompiledResultJobResponse,\n} from \"@dreamboard-games/api-client\";\nimport { toDreamboardApiError } from \"../../utils/errors.js\";\n\nconst COMPILE_JOB_POLL_INTERVAL_MS = 1000;\nconst DEFAULT_COMPILE_JOB_WAIT_TIMEOUT_MS = 10 * 60 * 1000;\nimport { sleep } from \"../../utils/strings.js\";\n\nfunction firstNonEmpty(\n ...values: Array<string | null | undefined>\n): string | null {\n for (const value of values) {\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value.trim();\n }\n }\n return null;\n}\n\nfunction formatTerminalCompileJobMessage(\n job: Pick<\n JobDetailResponse,\n \"createdAt\" | \"errorMessage\" | \"jobId\" | \"message\" | \"phase\" | \"status\"\n >,\n): string {\n const detail = firstNonEmpty(job.errorMessage, job.message);\n const phase = firstNonEmpty(job.phase);\n const prefix = `Compile ${job.status.toLowerCase()}${phase ? ` [${phase}]` : \"\"}`;\n return detail\n ? `${prefix}: ${detail}`\n : `${prefix}: job ${job.jobId} ended before a compiled result was created.`;\n}\n\nfunction compareCreatedAtDesc(\n left: Pick<CompiledResult, \"createdAt\">,\n right: Pick<CompiledResult, \"createdAt\">,\n): number {\n const leftTime = Date.parse(left.createdAt);\n const rightTime = Date.parse(right.createdAt);\n if (Number.isFinite(leftTime) && Number.isFinite(rightTime)) {\n return rightTime - leftTime;\n }\n if (Number.isFinite(rightTime)) {\n return 1;\n }\n if (Number.isFinite(leftTime)) {\n return -1;\n }\n return 0;\n}\n\nasync function findFallbackCompiledResultForJob(options: {\n gameId?: string;\n projectId?: string;\n job: Pick<JobDetailResponse, \"createdAt\">;\n}): Promise<CompiledResult | null> {\n const { gameId, projectId, job } = options;\n const results = projectId\n ? await listProjectCompiledResults({\n path: { projectId },\n query: { limit: 100 },\n })\n : await listCompiledResults({\n path: { gameId: gameId! },\n query: { limit: 100 },\n });\n if (results.error || !results.data) {\n return null;\n }\n if (results.data.results.length === 0) {\n return null;\n }\n\n const jobCreatedAtMs = Date.parse(job.createdAt);\n const resultsCreatedAfterJob = Number.isFinite(jobCreatedAtMs)\n ? results.data.results.filter((result) => {\n const resultCreatedAtMs = Date.parse(result.createdAt);\n return (\n !Number.isFinite(resultCreatedAtMs) ||\n resultCreatedAtMs >= jobCreatedAtMs\n );\n })\n : results.data.results;\n const candidateResults =\n resultsCreatedAfterJob.length > 0\n ? resultsCreatedAfterJob\n : results.data.results;\n\n return [...candidateResults].sort(compareCreatedAtDesc)[0] ?? null;\n}\n\nexport async function findLatestSuccessfulCompiledResult(\n gameId: string,\n): Promise<CompiledResult | null> {\n const { data } = await getLatestCompiledResult({\n path: { gameId },\n query: { successOnly: true },\n });\n return data ?? null;\n}\n\nexport async function findCompiledResultsForAuthoringState(options: {\n gameId: string;\n authoringStateId: string;\n}): Promise<CompiledResult[]> {\n const { gameId, authoringStateId } = options;\n const { data, error, response } = await listCompiledResults({\n path: { gameId },\n query: {\n limit: 100,\n authoringStateId,\n },\n });\n if (error || !data) {\n throw toDreamboardApiError(\n error,\n response,\n \"Failed to list compiled results\",\n );\n }\n return data.results;\n}\n\nexport async function getCompiledResultSdk(\n gameId: string,\n compiledResultId: string,\n): Promise<CompiledResult> {\n const { data, error, response } = await getCompiledResult({\n path: {\n gameId,\n compiledResultId,\n },\n });\n if (error || !data) {\n throw toDreamboardApiError(\n error,\n response,\n \"Failed to fetch compiled result\",\n );\n }\n return data;\n}\n\nexport async function findProjectCompiledResultsForRevision(options: {\n projectId: string;\n revisionDigest: string;\n}): Promise<CompiledResult[]> {\n const { projectId, revisionDigest } = options;\n const { data, error, response } = await listProjectCompiledResults({\n path: { projectId },\n query: { limit: 100 },\n });\n if (error || !data) {\n throw toDreamboardApiError(\n error,\n response,\n \"Failed to list compiled results\",\n );\n }\n return data.results.filter(\n (result) => {\n const maybeRevisionDigest = (result as { revisionDigest?: string })\n .revisionDigest;\n return (\n maybeRevisionDigest === revisionDigest ||\n result.authoringStateId === revisionDigest\n );\n },\n );\n}\n\nexport async function getProjectCompiledResultSdk(\n projectId: string,\n compiledResultId: string,\n): Promise<CompiledResult> {\n const { data, error, response } = await getProjectCompiledResult({\n path: { projectId, compiledResultId },\n });\n if (error || !data) {\n throw toDreamboardApiError(\n error,\n response,\n \"Failed to fetch compiled result\",\n );\n }\n return data;\n}\n\nexport async function queueProjectRevisionCompileSdk(options: {\n projectId: string;\n revisionDigest: string;\n}): Promise<QueueCompiledResultJobResponse> {\n const { data, error, response } = await queueProjectRevisionCompile({\n path: {\n projectId: options.projectId,\n revisionDigest: options.revisionDigest,\n },\n });\n\n if (error || !data) {\n throw toDreamboardApiError(error, response, \"Failed to create compile job\");\n }\n\n return data;\n}\n\nexport async function waitForCompiledResultJobSdk(options: {\n gameId?: string;\n projectId?: string;\n jobId: string;\n onProgress?: (job: JobDetailResponse) => void;\n}): Promise<{\n job: JobDetailResponse;\n compiledResult: CompiledResult;\n}> {\n const { gameId, projectId, jobId, onProgress } = options;\n let previousTransitionKey: string | null = null;\n const startedAt = Date.now();\n const timeoutMs = readCompileJobWaitTimeoutMs();\n\n while (Date.now() - startedAt < timeoutMs) {\n const {\n data: job,\n error,\n response,\n } = await getJob({\n path: { jobId },\n });\n if (error || !job) {\n if (isTransientJobPollError(error, response)) {\n await sleep(COMPILE_JOB_POLL_INTERVAL_MS);\n continue;\n }\n throw toDreamboardApiError(error, response, \"Failed to get job\");\n }\n\n const transitionKey = `${job.status}:${job.phase ?? \"\"}`;\n if (transitionKey !== previousTransitionKey) {\n previousTransitionKey = transitionKey;\n onProgress?.(job);\n }\n\n if (job.status === \"COMPLETED\" || job.status === \"FAILED\") {\n const compiledResultId =\n job.createdCompiledResultId ?? job.createdAppScriptId;\n if (compiledResultId) {\n if (projectId) {\n const compiledResult = await getProjectCompiledResultSdk(\n projectId,\n compiledResultId,\n );\n return { job, compiledResult };\n }\n const compiledResult = await getCompiledResultSdk(\n gameId!,\n compiledResultId,\n );\n return { job, compiledResult };\n }\n\n const fallbackCompiledResult = await findFallbackCompiledResultForJob({\n gameId,\n projectId,\n job,\n });\n if (fallbackCompiledResult) {\n return { job, compiledResult: fallbackCompiledResult };\n }\n\n throw new Error(formatTerminalCompileJobMessage(job));\n }\n\n if (job.status === \"CANCELLED\" || job.status === \"INTERRUPTED\") {\n throw new Error(formatTerminalCompileJobMessage(job));\n }\n\n await sleep(COMPILE_JOB_POLL_INTERVAL_MS);\n }\n\n throw new Error(`Compile job ${jobId} did not complete in time.`);\n}\n\nfunction readCompileJobWaitTimeoutMs(): number {\n const raw = process.env.DREAMBOARD_COMPILE_WAIT_TIMEOUT_MS;\n if (!raw) return DEFAULT_COMPILE_JOB_WAIT_TIMEOUT_MS;\n const parsed = Number(raw);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n return DEFAULT_COMPILE_JOB_WAIT_TIMEOUT_MS;\n }\n return parsed;\n}\n\nfunction isTransientJobPollError(error: unknown, response: unknown): boolean {\n if (response) return false;\n if (!error) return false;\n if (error instanceof Error) {\n return isTransientJobPollMessage(error.message);\n }\n if (typeof error === \"object\" && error !== null && \"message\" in error) {\n return isTransientJobPollMessage(String(error.message));\n }\n return isTransientJobPollMessage(String(error));\n}\n\nfunction isTransientJobPollMessage(message: string): boolean {\n const normalized = message.toLowerCase();\n return (\n normalized.includes(\"fetch failed\") ||\n normalized.includes(\"network\") ||\n normalized.includes(\"timeout\") ||\n normalized.includes(\"econnreset\") ||\n normalized.includes(\"econnrefused\") ||\n normalized.includes(\"socket\")\n );\n}\n","import {\n createGameRevision,\n createProjectSession,\n createProjectSessionFromReducerSnapshot,\n ensureProject,\n ensureProjectDevCompile,\n getApiVersion,\n getCurrentAuthUser,\n getProjectBySlug,\n getProjectRevisionSources,\n getProjectSources,\n type CreateSessionFromReducerSnapshotRequest,\n type CreateSessionRequest,\n type CreateSessionResponse,\n type CreateGameRevisionRequest,\n type EnsureDevCompileRequest,\n type EnsureDevCompileResponse,\n type GameRevision,\n type GameSourcesResponse,\n type HostSessionSnapshot,\n type Project,\n type ProjectRevisionSourcesResponse,\n} from \"@dreamboard-games/api-client\";\nimport { toDreamboardApiError } from \"../../utils/errors.js\";\nimport { titleFromSlug } from \"../../utils/strings.js\";\n\nexport type RemoteProjectIdentity = {\n deploymentId: string;\n ownerScopeId: string;\n bindingKey: string;\n};\n\nexport async function loadRemoteProjectIdentity(): Promise<RemoteProjectIdentity> {\n const [versionResponse, userResponse] = await Promise.all([\n getApiVersion(),\n getCurrentAuthUser(),\n ]);\n\n if (versionResponse.error || !versionResponse.data) {\n throw toDreamboardApiError(\n versionResponse.error as Parameters<typeof toDreamboardApiError>[0],\n versionResponse.response,\n \"Failed to resolve backend deployment identity\",\n );\n }\n if (userResponse.error || !userResponse.data) {\n throw toDreamboardApiError(\n userResponse.error,\n userResponse.response,\n \"Failed to resolve authenticated owner scope\",\n );\n }\n\n const deploymentId = versionResponse.data.deploymentId;\n const ownerScopeId = userResponse.data.ownerScopeId;\n return {\n deploymentId,\n ownerScopeId,\n bindingKey: `${deploymentId}:${ownerScopeId}`,\n };\n}\n\nexport async function ensureProjectSdk(options: {\n projectId: string;\n slug: string;\n description?: string;\n updateAlias?: boolean;\n}): Promise<Project> {\n const { data, error, response } = await ensureProject({\n path: { projectId: options.projectId },\n body: {\n slug: options.slug,\n name: titleFromSlug(options.slug),\n description:\n options.description ?? `Dreamboard workspace for ${options.slug}.`,\n ...(options.updateAlias ? { updateAlias: true } : {}),\n },\n });\n\n if (error || !data) {\n throw toDreamboardApiError(error, response, \"Failed to ensure project\");\n }\n\n return data;\n}\n\nexport async function getProjectBySlugSdk(slug: string): Promise<Project> {\n const { data, error, response } = await getProjectBySlug({\n path: { slug },\n });\n\n if (error || !data) {\n throw toDreamboardApiError(\n error,\n response,\n `Project '${slug}' not found`,\n );\n }\n\n return data;\n}\n\nexport async function createGameRevisionSdk(options: {\n projectId: string;\n request: CreateGameRevisionRequest;\n}): Promise<GameRevision> {\n const { data, error, response } = await createGameRevision({\n path: { projectId: options.projectId },\n body: options.request,\n });\n\n if (error || !data) {\n throw toDreamboardApiError(\n error,\n response,\n \"Failed to create game revision\",\n );\n }\n\n return data;\n}\n\nexport async function getProjectSourcesSdk(\n projectId: string,\n): Promise<GameSourcesResponse | null> {\n const { data, error, response } = await getProjectSources({\n path: { projectId },\n });\n\n if (response?.status === 404) {\n return null;\n }\n if (error || !data) {\n throw toDreamboardApiError(\n error,\n response,\n \"Failed to fetch project sources\",\n );\n }\n\n return data;\n}\n\nexport async function getProjectRevisionSourcesSdk(options: {\n projectId: string;\n revisionDigest: string;\n}): Promise<ProjectRevisionSourcesResponse> {\n const { data, error, response } = await getProjectRevisionSources({\n path: {\n projectId: options.projectId,\n revisionDigest: options.revisionDigest,\n },\n });\n\n if (error || !data) {\n throw toDreamboardApiError(\n error,\n response,\n \"Failed to fetch project revision sources\",\n );\n }\n\n return data;\n}\n\nexport async function ensureProjectDevCompileSdk(options: {\n projectId: string;\n request: EnsureDevCompileRequest;\n}): Promise<EnsureDevCompileResponse> {\n const { data, error, response } = await ensureProjectDevCompile({\n path: { projectId: options.projectId },\n body: options.request,\n });\n\n if (error || !data) {\n throw toDreamboardApiError(error, response, \"Failed to ensure dev compile\");\n }\n\n return data;\n}\n\nexport async function createProjectSessionSdk(options: {\n projectId: string;\n request: CreateSessionRequest;\n}): Promise<CreateSessionResponse> {\n const { data, error, response } = await createProjectSession({\n path: { projectId: options.projectId },\n body: options.request,\n });\n\n if (error || !data) {\n throw toDreamboardApiError(error, response, \"Failed to create session\");\n }\n\n return data;\n}\n\nexport async function createProjectSessionFromReducerSnapshotSdk(options: {\n projectId: string;\n request: CreateSessionFromReducerSnapshotRequest;\n}): Promise<HostSessionSnapshot> {\n const { data, error, response } =\n await createProjectSessionFromReducerSnapshot({\n path: { projectId: options.projectId },\n body: options.request,\n });\n\n if (error || !data) {\n throw toDreamboardApiError(\n error,\n response,\n \"Failed to create session from reducer snapshot\",\n );\n }\n\n return data;\n}\n","import { uploadInitialProjection } from \"@dreamboard-games/api-client\";\nimport { toDreamboardApiError } from \"../../utils/errors.js\";\n\nexport async function uploadInitialProjectionSdk(\n gameId: string,\n projectionJson: string,\n): Promise<void> {\n const { error, response } = await uploadInitialProjection({\n path: { gameId },\n body: { projectionJson },\n });\n if (error) {\n throw toDreamboardApiError(\n error,\n response,\n \"Failed to upload initial preview projection\",\n );\n }\n}\n","import {\n createSourceRevision,\n type CreateSourceRevisionRequest,\n queueCompiledResultJob,\n type QueueCompiledResultJobResponse,\n type SourceRevision,\n} from \"@dreamboard-games/api-client\";\nimport {\n SourceBlobSessionRequestError,\n uploadGameSourceBlobs,\n uploadProjectSourceBlobs,\n type SourceBlobUploadInput,\n} from \"@dreamboard-games/api-client/source-revisions\";\nimport { toDreamboardApiError } from \"../../utils/errors.js\";\n\nconst SOURCE_BLOB_UPLOAD_BATCH_SIZE = 20;\n\nexport async function createSourceRevisionSdk(\n gameId: string,\n request: CreateSourceRevisionRequest,\n): Promise<SourceRevision> {\n const response = await createSourceRevision({\n path: { gameId },\n body: request,\n });\n\n if (response.error || !response.data) {\n throw toDreamboardApiError(\n response.error,\n response.response,\n \"Failed to create source revision\",\n );\n }\n\n return response.data;\n}\n\nexport async function uploadSourceBlobsSdk(\n gameId: string,\n blobs: SourceBlobUploadInput[],\n): Promise<void> {\n try {\n for (const batch of chunkSourceBlobs(blobs)) {\n await uploadGameSourceBlobs({ gameId, blobs: batch });\n }\n } catch (error) {\n if (error instanceof SourceBlobSessionRequestError) {\n throw toDreamboardApiError(\n error.apiError as Parameters<typeof toDreamboardApiError>[0],\n error.response,\n error.message,\n );\n }\n throw error;\n }\n}\n\nexport async function uploadProjectSourceBlobsSdk(\n projectId: string,\n blobs: SourceBlobUploadInput[],\n): Promise<void> {\n try {\n for (const batch of chunkSourceBlobs(blobs)) {\n await uploadProjectSourceBlobs({ projectId, blobs: batch });\n }\n } catch (error) {\n if (error instanceof SourceBlobSessionRequestError) {\n throw toDreamboardApiError(\n error.apiError as Parameters<typeof toDreamboardApiError>[0],\n error.response,\n error.message,\n );\n }\n throw error;\n }\n}\n\nfunction chunkSourceBlobs(\n blobs: SourceBlobUploadInput[],\n): SourceBlobUploadInput[][] {\n const chunks: SourceBlobUploadInput[][] = [];\n for (let index = 0; index < blobs.length; index += SOURCE_BLOB_UPLOAD_BATCH_SIZE) {\n chunks.push(blobs.slice(index, index + SOURCE_BLOB_UPLOAD_BATCH_SIZE));\n }\n return chunks;\n}\n\nexport async function queueCompiledResultJobSdk(options: {\n gameId: string;\n authoringStateId: string;\n}): Promise<QueueCompiledResultJobResponse> {\n const { gameId, authoringStateId } = options;\n const { data, error, response } = await queueCompiledResultJob({\n path: { gameId },\n body: {\n authoringStateId,\n },\n });\n\n if (error || !data) {\n throw toDreamboardApiError(error, response, \"Failed to create compile job\");\n }\n\n return data;\n}\n"],"mappings":"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","names":["error","compiledResult","chunkSourceBlobs"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/services/project/reducer-bundle-preflight.ts"],"sourcesContent":["import path from \"node:path\";\nimport type {\n GameTopologyManifest,\n SetupProfileSpec,\n} from \"@dreamboard-games/sdk/types\";\nimport { isPerPlayer, perPlayerSchema } from \"@dreamboard-games/sdk/reducer\";\nimport {\n type ReducerBundleContract,\n type ReducerWire as Wire,\n} from \"@dreamboard-games/sdk/reducer-contract\";\nimport { materializeManifestTable } from \"@dreamboard-games/sdk/codegen\";\nimport { z } from \"zod\";\nimport { importTypeScriptModule } from \"../../utils/ts-module-loader.js\";\n\n// Opt in to SDK authoring warnings (e.g. concrete dependent-choice defaults).\n// Trusted reducer code cannot read process.env, so the SDK gates authoring\n// warnings on this host-set global instead.\n(globalThis as Record<string, unknown>).__DREAMBOARD_AUTHORING_WARNINGS__ =\n true;\n\n/**\n * Path to the reducer bundle entry, relative to the project root. Matches\n * the scaffold produced by `dreamboard new`.\n */\nconst REDUCER_BUNDLE_ENTRY_PATH = path.join(\"app\", \"index.ts\");\n\n/**\n * Deterministic rng seed used to drive the reducer during preflight.\n * Kept stable so local preflight failures reproduce across CLI runs.\n */\nconst PREFLIGHT_RNG_SEED = 1337;\n\n/**\n * Phase of the preflight smoke test that a failure occurred in.\n */\nexport type ReducerBundleSmokePhase =\n | \"BUILD_GAME_STATE\"\n | \"INITIALIZE\"\n | \"PROJECT_SEATS\"\n | \"VALIDATE_VIEW\";\n\nexport interface ReducerBundleSmokeScenario {\n readonly setupProfileId: string | null;\n readonly playerCount: number;\n}\n\nexport interface ReducerBundleSmokeFailure {\n readonly scenario: ReducerBundleSmokeScenario;\n readonly phase: ReducerBundleSmokePhase;\n readonly playerId?: string;\n readonly headline: string;\n readonly detail?: string;\n}\n\n/**\n * Shape of a single seat's slice in `projectSeatsDynamic(...)`. Only `view`\n * is consumed by the preflight, but the full wire DTO keeps local tests\n * aligned with the canonical reducer boundary.\n */\nexport type ReducerBundleSeatProjection = Wire.SeatProjection;\n\n/** Result of calling `bundle.projectSeatsDynamic({ state, playerIds })`. */\nexport type ReducerBundleSeatProjectionBundle = Wire.SeatProjectionBundle;\n\n/**\n * Minimal structural shape the preflight needs from the authored reducer\n * bundle. Matches the subset of `createReducerBundle(game)` used by the\n * reducer runtime.\n */\nexport type ReducerBundleLike = Pick<\n ReducerBundleContract,\n \"initialize\" | \"projectSeatsDynamic\"\n>;\n\nfunction buildPlayerIds(playerCount: number): string[] {\n const ids: string[] = [];\n for (let index = 1; index <= playerCount; index += 1) {\n ids.push(`player-${index}`);\n }\n return ids;\n}\n\n/**\n * Build the matrix of scenarios to drive. Exported for tests so we can\n * assert scenario generation without loading a full reducer bundle.\n */\nexport function buildScenarios(\n manifest: GameTopologyManifest,\n): ReducerBundleSmokeScenario[] {\n const rawCounts = [\n manifest.players?.minPlayers,\n manifest.players?.maxPlayers,\n ].filter(\n (count): count is number =>\n typeof count === \"number\" && Number.isFinite(count) && count > 0,\n );\n const playerCounts = Array.from(new Set(rawCounts));\n if (playerCounts.length === 0) {\n return [];\n }\n const profiles: SetupProfileSpec[] = manifest.setupProfiles ?? [];\n const profileIds: Array<string | null> =\n profiles.length > 0 ? profiles.map((profile) => profile.id) : [null];\n\n const scenarios: ReducerBundleSmokeScenario[] = [];\n const seen = new Set<string>();\n for (const profileId of profileIds) {\n for (const count of playerCounts) {\n const key = `${profileId ?? \"<default>\"}:${count}`;\n if (seen.has(key)) continue;\n seen.add(key);\n scenarios.push({ setupProfileId: profileId, playerCount: count });\n }\n }\n return scenarios;\n}\n\nfunction describeScenario(scenario: ReducerBundleSmokeScenario): string {\n const profileDesc = scenario.setupProfileId\n ? `setup profile '${scenario.setupProfileId}'`\n : \"no setup profile\";\n return `${profileDesc}, ${scenario.playerCount} player${\n scenario.playerCount === 1 ? \"\" : \"s\"\n }`;\n}\n\nfunction summarizeError(error: unknown): { headline: string; detail?: string } {\n if (error instanceof Error) {\n const firstLine =\n error.message\n .split(\"\\n\")\n .map((line) => line.trim())\n .find((line) => line.length > 0) ?? \"Unknown failure\";\n const detail =\n error.stack && error.stack !== error.message ? error.stack : undefined;\n return { headline: firstLine, detail };\n }\n return { headline: String(error ?? \"Unknown failure\") };\n}\n\n/**\n * Walk `view` recursively and, for every embedded `PerPlayer<T>` shape,\n * assert its seat list matches `expectedPlayerIds` using\n * `perPlayerSchema({ players })`. Returns `null` on success, or a headline\n * describing the first mismatch.\n *\n * This catches the class of bug where an author returns a `PerPlayer<T>`\n * that was built from the wrong seat list (e.g. a manifest-derived\n * `maxPlayers` literal union) while the runtime session has a different\n * number of seats. The Zod bound schema produces an identical error shape\n * to the one the backend raises in production so authors see the same\n * diagnostic locally.\n */\nexport function assertViewPerPlayerSeatsValid(\n view: unknown,\n expectedPlayerIds: readonly string[],\n breadcrumb: string[] = [],\n): string | null {\n if (view === null || view === undefined) {\n return null;\n }\n\n if (isPerPlayer(view)) {\n const schema = perPlayerSchema(z.unknown(), {\n players: expectedPlayerIds as never,\n });\n const result = schema.safeParse(view);\n if (!result.success) {\n const firstIssue = result.error.issues[0];\n const location =\n breadcrumb.length > 0 ? ` at view.${breadcrumb.join(\".\")}` : \"\";\n const message = firstIssue?.message ?? \"PerPlayer seat mismatch\";\n return `PerPlayer seat mismatch${location}: ${message}`;\n }\n return null;\n }\n\n if (Array.isArray(view)) {\n for (let index = 0; index < view.length; index += 1) {\n const result = assertViewPerPlayerSeatsValid(\n view[index],\n expectedPlayerIds,\n [...breadcrumb, `[${index}]`],\n );\n if (result) return result;\n }\n return null;\n }\n\n if (typeof view === \"object\") {\n for (const [key, value] of Object.entries(\n view as Record<string, unknown>,\n )) {\n const result = assertViewPerPlayerSeatsValid(value, expectedPlayerIds, [\n ...breadcrumb,\n key,\n ]);\n if (result) return result;\n }\n return null;\n }\n\n return null;\n}\n\nfunction identityShuffle<Value>(values: readonly Value[]): Value[] {\n return [...values];\n}\n\n/**\n * Drive an already-loaded reducer bundle through the provided scenarios.\n * Shared between the full `runReducerBundleSmoke` entrypoint and the\n * unit tests, which feed a fake bundle directly.\n *\n * For each (setup profile × player count) scenario this:\n *\n * 1. materializes an initial table via `materializeManifestTable`,\n * 2. calls `bundle.initialize({ table, playerIds, rngSeed, setup })`,\n * 3. calls `bundle.projectSeatsDynamic({ state, playerIds })` once, and\n * 4. validates any embedded `PerPlayer<T>` views match the runtime seat\n * list using `perPlayerSchema({ players: playerIds })`.\n *\n * Failures are collected instead of thrown so authors get the full list\n * of broken scenarios in one preflight pass.\n */\nexport async function driveReducerBundleThroughScenarios(options: {\n manifest: GameTopologyManifest;\n bundle: ReducerBundleLike;\n scenarios: readonly ReducerBundleSmokeScenario[];\n rngSeed?: number;\n}): Promise<ReducerBundleSmokeFailure[]> {\n const { manifest, bundle, scenarios } = options;\n const rngSeed = options.rngSeed ?? PREFLIGHT_RNG_SEED;\n\n const failures: ReducerBundleSmokeFailure[] = [];\n const tableCache = new Map<\n number,\n { table: unknown; playerIds: string[] } | { error: unknown }\n >();\n\n for (const scenario of scenarios) {\n let tableEntry = tableCache.get(scenario.playerCount);\n if (!tableEntry) {\n try {\n const playerIds = buildPlayerIds(scenario.playerCount);\n // JSON-roundtrip the materialized table to mirror the backend wire\n // boundary: real flows serialize the table over HTTP, which drops\n // explicit `undefined` property values (e.g. optional card text) that\n // the bundle's strict JSON state schema would otherwise reject.\n const table: unknown = JSON.parse(\n JSON.stringify(\n materializeManifestTable({\n manifest,\n playerIds,\n shuffleItems: identityShuffle,\n }),\n ),\n );\n tableEntry = { table, playerIds };\n } catch (error) {\n tableEntry = { error };\n }\n tableCache.set(scenario.playerCount, tableEntry);\n }\n if (\"error\" in tableEntry) {\n const summary = summarizeError(tableEntry.error);\n failures.push({\n scenario,\n phase: \"BUILD_GAME_STATE\",\n headline: summary.headline,\n detail: summary.detail,\n });\n continue;\n }\n\n const { table, playerIds } = tableEntry;\n\n let sessionState: unknown;\n try {\n sessionState = await bundle.initialize({\n table: table as Wire.JsonValue,\n playerIds: [...playerIds],\n rngSeed,\n setup: scenario.setupProfileId\n ? { profileId: scenario.setupProfileId, optionValues: {} }\n : null,\n });\n } catch (error) {\n const summary = summarizeError(error);\n failures.push({\n scenario,\n phase: \"INITIALIZE\",\n headline: summary.headline,\n detail: summary.detail,\n });\n continue;\n }\n\n let projection: ReducerBundleSeatProjectionBundle | undefined;\n try {\n projection = bundle.projectSeatsDynamic({\n state: sessionState as Wire.ReducerSessionState,\n playerIds: [...playerIds],\n });\n } catch (error) {\n const summary = summarizeError(error);\n failures.push({\n scenario,\n phase: \"PROJECT_SEATS\",\n headline: summary.headline,\n detail: summary.detail,\n });\n continue;\n }\n\n const seats = projection?.seats ?? {};\n for (const playerId of playerIds) {\n const seat = seats[playerId];\n if (!seat) {\n failures.push({\n scenario,\n phase: \"PROJECT_SEATS\",\n playerId,\n headline: `projectSeatsDynamic() did not return an entry for seat '${playerId}'.`,\n });\n continue;\n }\n const mismatch = assertViewPerPlayerSeatsValid(seat.view, playerIds);\n if (mismatch) {\n failures.push({\n scenario,\n phase: \"VALIDATE_VIEW\",\n playerId,\n headline: mismatch,\n });\n }\n }\n }\n\n return failures;\n}\n\n/**\n * Import the authored reducer bundle from `projectRoot/app/index.ts` and\n * drive it through every (setup profile × player count) scenario the\n * manifest declares. Returns the collected failures without throwing so\n * callers can decide whether to surface them.\n */\nexport async function runReducerBundleSmoke(options: {\n projectRoot: string;\n manifest: GameTopologyManifest;\n}): Promise<ReducerBundleSmokeFailure[]> {\n const { projectRoot, manifest } = options;\n const scenarios = buildScenarios(manifest);\n if (scenarios.length === 0) {\n return [];\n }\n\n const entryPath = path.join(projectRoot, REDUCER_BUNDLE_ENTRY_PATH);\n let module: { default?: ReducerBundleLike };\n try {\n module = await importTypeScriptModule<{ default?: ReducerBundleLike }>(\n entryPath,\n );\n } catch (error) {\n const summary = summarizeError(error);\n throw new Error(\n [\n `Dreamboard could not import \\`${REDUCER_BUNDLE_ENTRY_PATH}\\` during \\`dreamboard sync\\`.`,\n \"Fix the reducer bundle entry so it can be imported locally, then run `dreamboard sync` again.\",\n `Original error: ${summary.headline}`,\n ].join(\" \"),\n );\n }\n const bundle = module.default;\n if (!bundle || typeof bundle.initialize !== \"function\") {\n throw new Error(\n [\n `\\`${REDUCER_BUNDLE_ENTRY_PATH}\\` does not export a reducer bundle as its default export.`,\n \"Export `createReducerBundle(game)` from `app/index.ts` so the compile pipeline can smoke-test it.\",\n ].join(\" \"),\n );\n }\n\n return driveReducerBundleThroughScenarios({ manifest, bundle, scenarios });\n}\n\nfunction formatFailureLines(\n failures: readonly ReducerBundleSmokeFailure[],\n): string {\n return failures\n .map((failure) => {\n const location = failure.playerId ? ` (seat ${failure.playerId})` : \"\";\n return `• [${failure.phase}] ${describeScenario(failure.scenario)}${location}: ${failure.headline}`;\n })\n .join(\"\\n\");\n}\n\n/**\n * Preflight wrapper used by `dreamboard sync`. Runs\n * `runReducerBundleSmoke` and throws an aggregated error if any scenario\n * failed so the author sees every broken seat/profile in one shot.\n *\n * Sync already guarantees that the authored contract imports cleanly\n * (via `assertReducerContractPreflight`) and that `tsc --noEmit` is\n * green (via `runLocalTypecheck`) before this runs, so failures from\n * this step are always runtime-shaped (`initialize`/`projectSeatsDynamic` rejecting,\n * `perPlayer` seat mismatches, …) rather than static type or import\n * errors.\n */\nexport async function assertReducerBundleSmoke(options: {\n projectRoot: string;\n manifest: GameTopologyManifest;\n}): Promise<void> {\n const failures = await runReducerBundleSmoke(options);\n if (failures.length === 0) {\n return;\n }\n throw new Error(\n [\n \"Reducer bundle preflight failed during `dreamboard sync`.\",\n \"Fix the reported scenarios locally before syncing so the backend does not catch them after start-game:\",\n formatFailureLines(failures),\n ].join(\"\\n\"),\n );\n}\n"],"mappings":"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","names":["playerIds","table"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/commands/compile.ts"],"sourcesContent":["import type { CompiledResult } from \"@dreamboard-games/api-client\";\nimport type { ProjectConfig } from \"../types.js\";\nimport { defineCommand } from \"citty\";\nimport consola from \"consola\";\nimport { CONFIG_FLAG_ARGS } from \"../command-args.js\";\nimport { resolveProjectContext } from \"../config/resolve.js\";\nimport { updateProjectState } from \"../config/project-config.js\";\nimport { parseCompileCommandArgs } from \"../flags.js\";\nimport { getLocalDiff } from \"../services/project/local-files.js\";\nimport { assertCliStaticScaffoldComplete } from \"../services/project/static-scaffold.js\";\nimport {\n assertCompilerPortableDependencies,\n assertReleaseEnvironmentPortableDependencies,\n} from \"../services/project/dependency-portability.js\";\nimport { runLocalTypecheck } from \"../services/project/local-typecheck.js\";\nimport {\n findProjectCompiledResultsForRevision,\n queueProjectRevisionCompileSdk,\n waitForCompiledResultJobSdk,\n} from \"../services/api/index.js\";\nimport {\n getProjectAuthoringState,\n getProjectPendingAuthoringSync,\n setLatestCompileAttempt,\n} from \"../services/project/project-state.js\";\nimport { formatCliError } from \"../utils/errors.js\";\nimport { resolveRemoteProject } from \"../services/project/remote-project.js\";\n\nfunction formatDiagnosticsSummary(\n diagnostics: Array<{ message?: string }> | null | undefined,\n): string | undefined {\n const firstMessage = diagnostics?.find(\n (diagnostic) => diagnostic.message,\n )?.message;\n return firstMessage?.trim() || undefined;\n}\n\nfunction formatCompileJobProgressMessage(job: {\n status: string;\n phase?: string;\n queuePosition?: number;\n message?: string;\n}): string {\n const phase = job.phase ? ` [${job.phase}]` : \"\";\n const detail = job.message ? ` ${job.message}` : \"\";\n if (job.status === \"PENDING\") {\n const queue =\n typeof job.queuePosition === \"number\"\n ? ` (queue ${job.queuePosition + 1})`\n : \"\";\n return `Compile queued${queue}${phase}${detail}`.trim();\n }\n if (job.status === \"RUNNING\") {\n return `Compile running${phase}${detail}`.trim();\n }\n if (job.status === \"FAILED\") {\n return `Compile failed${phase}${detail}`.trim();\n }\n return `Compile ${job.status.toLowerCase()}${phase}${detail}`.trim();\n}\n\nfunction formatFailedCompileJobSummary(job: {\n phase?: string;\n message?: string | null;\n errorMessage?: string | null;\n}): string {\n return formatCompileJobProgressMessage({\n status: \"FAILED\",\n phase: job.phase,\n message: job.errorMessage ?? job.message ?? undefined,\n });\n}\n\nfunction formatFailedCompileJobWithCompiledResultMessage(options: {\n compiledResultId: string;\n job: {\n phase?: string;\n message?: string | null;\n errorMessage?: string | null;\n };\n}): string {\n return `${formatFailedCompileJobSummary(options.job)}. The backend created compiled result ${options.compiledResultId}, but the compile job did not complete cleanly. Run 'dreamboard compile' again after fixing the backend/compiler issue.`;\n}\n\nfunction formatRemoteCompileCommandError(options: {\n message: string;\n jobId?: string;\n}): string {\n const detail = options.message.trim();\n const hasActionableTerminalContext =\n /^Compile\\s+(failed|completed|cancelled|interrupted)\\b/i.test(detail);\n if (options.jobId) {\n if (hasActionableTerminalContext) {\n return `Remote compile job ${options.jobId} could not be completed. ${detail}`;\n }\n return `Remote compile job ${options.jobId} could not be completed. ${detail} Check backend health and try 'dreamboard compile' again.`;\n }\n return `Remote compile could not be started. ${detail} Check backend health and try 'dreamboard compile' again.`;\n}\n\nasync function persistFailedCompileAttempt(options: {\n projectRoot: string;\n projectConfig: ProjectConfig;\n revisionDigest: string;\n authoringStateId?: string;\n diagnosticsSummary: string;\n jobId?: string;\n}): Promise<void> {\n const nextProjectConfig = setLatestCompileAttempt(options.projectConfig, {\n resultId: undefined,\n jobId: options.jobId,\n revisionDigest: options.revisionDigest,\n authoringStateId: options.authoringStateId ?? options.revisionDigest,\n status: \"failed\",\n diagnosticsSummary: options.diagnosticsSummary,\n });\n await updateProjectState(options.projectRoot, nextProjectConfig);\n}\n\nexport default defineCommand({\n meta: {\n name: \"compile\",\n description: \"Compile the current remote authoring head\",\n },\n args: {\n debug: {\n type: \"boolean\",\n description: \"Print additional compile progress context\",\n default: false,\n },\n \"skip-local-check\": {\n type: \"boolean\",\n description: \"Skip the best-effort local typecheck before compiling\",\n default: false,\n },\n ...CONFIG_FLAG_ARGS,\n },\n async run({ args }) {\n const parsedArgs = parseCompileCommandArgs(args);\n const { projectRoot, projectConfig, config } =\n await resolveProjectContext(parsedArgs);\n let nextProjectConfig = projectConfig;\n await assertReleaseEnvironmentPortableDependencies({\n projectRoot,\n projectConfig: nextProjectConfig,\n environment: config.environment,\n });\n await assertCompilerPortableDependencies({\n projectRoot,\n projectConfig: nextProjectConfig,\n });\n\n const diff = await getLocalDiff(projectRoot);\n await assertCliStaticScaffoldComplete(projectRoot, diff.deleted);\n if (\n diff.modified.length > 0 ||\n diff.added.length > 0 ||\n diff.deleted.length > 0\n ) {\n throw new Error(\n \"Local authored changes are not synced yet. Run 'dreamboard sync' before compiling.\",\n );\n }\n\n const remoteProject = await resolveRemoteProject({\n projectRoot,\n projectConfig: nextProjectConfig,\n config,\n });\n nextProjectConfig = remoteProject.projectConfig;\n\n const localAuthoring = getProjectAuthoringState(nextProjectConfig);\n const pendingSync = getProjectPendingAuthoringSync(nextProjectConfig);\n if (pendingSync) {\n if (pendingSync.phase === \"authoring_state_created\") {\n throw new Error(\n \"Previous sync reached the remote authored head, but local scaffold finalization did not complete. Run 'dreamboard sync' again before compiling.\",\n );\n }\n throw new Error(\n \"Previous sync uploaded source changes but did not finish creating the authored head. Run 'dreamboard sync' again before compiling.\",\n );\n }\n const localRevisionDigest =\n localAuthoring.revisionDigest ?? nextProjectConfig.remoteHeadDigest;\n if (!localRevisionDigest) {\n throw new Error(\n \"This workspace does not know its authored revision yet. Run 'dreamboard sync' first.\",\n );\n }\n\n const remoteRevisionDigest = remoteProject.project.head?.revisionDigest;\n if (!remoteRevisionDigest) {\n throw new Error(\"Remote has no authored project revision to compile yet.\");\n }\n if (remoteRevisionDigest !== localRevisionDigest) {\n throw new Error(\n `Remote project head is ${remoteRevisionDigest} but this workspace is based on ${localRevisionDigest}. Run 'dreamboard pull' before compiling.`,\n );\n }\n\n if (!parsedArgs[\"skip-local-check\"]) {\n consola.start(\"Running local typecheck...\");\n const typecheckResult = await runLocalTypecheck(projectRoot);\n if (typecheckResult.skipped) {\n if (typecheckResult.output) {\n consola.warn(typecheckResult.output);\n }\n } else if (!typecheckResult.success) {\n if (typecheckResult.output) {\n consola.error(typecheckResult.output);\n }\n throw new Error(\n \"Local typecheck failed. Fix the diagnostics or re-run with --skip-local-check.\",\n );\n } else {\n consola.success(\"Local typecheck passed.\");\n }\n }\n\n const existingCompiledResult = (\n await findProjectCompiledResultsForRevision({\n projectId: nextProjectConfig.projectId,\n revisionDigest: localRevisionDigest,\n })\n ).find((result) => result.success);\n if (existingCompiledResult) {\n nextProjectConfig = setLatestCompileAttempt(nextProjectConfig, {\n resultId: existingCompiledResult.id,\n jobId: undefined,\n revisionDigest: localRevisionDigest,\n authoringStateId:\n existingCompiledResult.authoringStateId ?? localRevisionDigest,\n status: \"successful\",\n diagnosticsSummary: undefined,\n });\n await updateProjectState(projectRoot, nextProjectConfig);\n consola.success(\n `Reusing compiled ${existingCompiledResult.id} for authored state ${existingCompiledResult.authoringStateId}.`,\n );\n return;\n }\n\n let compileJobId: string | undefined;\n let compileJobStatus: string | undefined;\n let compileJobPhase: string | undefined;\n let compileJobMessage: string | null | undefined;\n let compileJobErrorMessage: string | null | undefined;\n let compiledResult: CompiledResult;\n try {\n const compileJob = await queueProjectRevisionCompileSdk({\n projectId: nextProjectConfig.projectId,\n revisionDigest: localRevisionDigest,\n });\n compileJobId = compileJob.jobId;\n if (!compileJobId) {\n throw new Error(\"Failed to create compile job: missing jobId.\");\n }\n\n ({\n job: {\n status: compileJobStatus,\n phase: compileJobPhase,\n message: compileJobMessage,\n errorMessage: compileJobErrorMessage,\n },\n compiledResult,\n } = await waitForCompiledResultJobSdk({\n projectId: nextProjectConfig.projectId,\n jobId: compileJobId,\n onProgress: (job) => {\n const message = formatCompileJobProgressMessage(job);\n if (parsedArgs.debug) {\n consola.info(message);\n } else {\n consola.start(message);\n }\n },\n }));\n } catch (error) {\n const message = formatCliError(error);\n if (compileJobId) {\n await persistFailedCompileAttempt({\n projectRoot,\n projectConfig: nextProjectConfig,\n revisionDigest: localRevisionDigest,\n authoringStateId: localAuthoring.authoringStateId,\n diagnosticsSummary: message,\n jobId: compileJobId,\n });\n }\n throw new Error(\n formatRemoteCompileCommandError({\n message,\n jobId: compileJobId,\n }),\n );\n }\n\n const failedJobProducedCompiledResult =\n compileJobStatus === \"FAILED\" && compiledResult.success;\n const failedJobWithCompiledResultSummary = failedJobProducedCompiledResult\n ? formatFailedCompileJobSummary({\n phase: compileJobPhase,\n message: compileJobMessage,\n errorMessage: compileJobErrorMessage,\n })\n : undefined;\n\n nextProjectConfig = setLatestCompileAttempt(nextProjectConfig, {\n resultId: compiledResult.id,\n jobId: compileJobId,\n revisionDigest: localRevisionDigest,\n authoringStateId: compiledResult.authoringStateId ?? localRevisionDigest,\n status:\n compiledResult.success && !failedJobProducedCompiledResult\n ? \"successful\"\n : \"failed\",\n diagnosticsSummary:\n failedJobWithCompiledResultSummary ??\n formatDiagnosticsSummary(compiledResult.diagnostics),\n });\n await updateProjectState(projectRoot, nextProjectConfig);\n\n if (failedJobProducedCompiledResult) {\n throw new Error(\n formatFailedCompileJobWithCompiledResultMessage({\n compiledResultId: compiledResult.id,\n job: {\n phase: compileJobPhase,\n message: compileJobMessage,\n errorMessage: compileJobErrorMessage,\n },\n }),\n );\n }\n\n if (!compiledResult.success) {\n for (const diagnostic of compiledResult.diagnostics ?? []) {\n if (diagnostic.message) {\n consola.error(diagnostic.message);\n }\n }\n throw new Error(\n \"Remote compile failed, but your authored state is synced. Fix the diagnostics and run 'dreamboard compile' again.\",\n );\n }\n\n consola.success(\n `Compiled ${compiledResult.id} for revision ${localRevisionDigest}.`,\n );\n },\n});\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/config/keychain-backend.ts"],"sourcesContent":["/**\n * OS keychain-backed `CredentialBackend` built on top of `@napi-rs/keyring`.\n *\n * Keychain is an *opt-in* storage backend, enabled by setting\n * `credentialBackend: \"keychain\"` in `~/.dreamboard/config.json` (see\n * `credential-store.ts` for the resolver). When enabled, it gives us:\n * - A refresh token encrypted at rest by the OS (Keychain on macOS,\n * Credential Vault on Windows, Secret Service on Linux).\n * - Protection against other processes running as the same user tailing\n * `~/.dreamboard/auth.json` to scrape the token.\n *\n * It is not the default because on macOS the first keychain write\n * triggers a login-password prompt, and macOS re-prompts whenever the\n * executing Node binary's code signature changes (e.g. after an\n * `nvm`/`volta` upgrade). The zero-prompt file backend is a better\n * out-of-the-box experience for CLI users.\n *\n * This module is loaded optionally: `@napi-rs/keyring` is declared as an\n * `optionalDependencies` entry so environments where the native binary is\n * unavailable (e.g. Alpine containers, Linux without libsecret, some CI\n * images) still get a working CLI via the file backend fallback.\n *\n * One-time migration: when a user opts into the keychain and `auth.json`\n * still has tokens, `credential-store.ts` copies them into the keychain\n * and deletes the file. This is the only path that intentionally mutates\n * both backends.\n */\n\nimport type {\n CredentialBackend,\n Credentials,\n StoredSessionSnapshot,\n} from \"./credential-store.js\";\n\n/** Keychain service id. Shared across all Dreamboard CLI builds. */\nconst KEYCHAIN_SERVICE = \"dreamboard-cli\";\n/**\n * Keychain account id. The `user@host` shape is conventional but we keep\n * it fixed for now because the CLI only cares about \"the session for this\n * OS user\", not per-process sessions.\n */\nconst KEYCHAIN_ACCOUNT = \"session\";\n\ntype EntryInstance = {\n setPassword(value: string): void;\n getPassword(): string | null | undefined;\n deletePassword(): boolean;\n};\n\ntype KeyringModule = {\n Entry: new (service: string, account: string) => EntryInstance;\n};\n\nlet cachedModule: KeyringModule | null | undefined;\n\nasync function loadKeyringModule(): Promise<KeyringModule | null> {\n if (cachedModule !== undefined) return cachedModule;\n try {\n // `@napi-rs/keyring` is an optional dependency. If the native binary is\n // missing on this platform the dynamic import throws; we swallow that\n // and fall back to the file backend.\n const mod = (await import(\"@napi-rs/keyring\")) as unknown as KeyringModule;\n cachedModule = mod;\n } catch {\n cachedModule = null;\n }\n return cachedModule;\n}\n\nfunction keychainProbe(entry: EntryInstance): boolean {\n // Some platforms have the module installed but no accessible keyring\n // (e.g. headless Linux without DBus). Touch getPassword to verify we\n // can talk to the service without side effects.\n try {\n entry.getPassword();\n return true;\n } catch {\n return false;\n }\n}\n\ntype KeychainPayload = {\n accessToken?: string;\n refreshToken?: string;\n tokenExpiresAt?: string;\n clerkOAuthIssuer?: string;\n clerkOAuthClientId?: string;\n clerkOAuthTokenUrl?: string;\n environment?: string;\n};\n\nfunction parsePayload(\n raw: string | null | undefined,\n): StoredSessionSnapshot | null {\n if (raw === null || raw === undefined) return null;\n const trimmed = raw.trim();\n if (trimmed.length === 0) return null;\n try {\n const parsed = JSON.parse(trimmed) as KeychainPayload;\n if (!parsed.accessToken && !parsed.refreshToken) return null;\n return {\n accessToken: parsed.accessToken || undefined,\n refreshToken: parsed.refreshToken || undefined,\n tokenExpiresAt: parsed.tokenExpiresAt || undefined,\n clerkOAuthIssuer: parsed.clerkOAuthIssuer || undefined,\n clerkOAuthClientId: parsed.clerkOAuthClientId || undefined,\n clerkOAuthTokenUrl: parsed.clerkOAuthTokenUrl || undefined,\n environment: parsed.environment || undefined,\n };\n } catch {\n return null;\n }\n}\n\nfunction writeFull(entry: EntryInstance, creds: Credentials): void {\n if (!creds.accessToken || !creds.refreshToken) {\n throw new Error(\n \"Refusing to persist credentials with an empty accessToken or refreshToken.\",\n );\n }\n const payload: KeychainPayload = {\n accessToken: creds.accessToken,\n refreshToken: creds.refreshToken,\n tokenExpiresAt: creds.tokenExpiresAt,\n clerkOAuthIssuer: creds.clerkOAuthIssuer,\n clerkOAuthClientId: creds.clerkOAuthClientId,\n clerkOAuthTokenUrl: creds.clerkOAuthTokenUrl,\n environment: creds.environment,\n };\n entry.setPassword(JSON.stringify(payload));\n}\n\nfunction writeAccessOnly(entry: EntryInstance, accessToken: string): void {\n if (!accessToken) {\n throw new Error(\"Refusing to persist an empty access token.\");\n }\n const payload: KeychainPayload = { accessToken };\n entry.setPassword(JSON.stringify(payload));\n}\n\nfunction clear(entry: EntryInstance): void {\n try {\n entry.deletePassword();\n } catch {\n // keyring-rs throws when the entry does not exist. That is fine -\n // `clearCredentials` contracts as idempotent.\n }\n}\n\nexport type KeychainAvailability =\n | { available: true; backend: CredentialBackend }\n | { available: false; reason: string };\n\n/**\n * Attempt to construct a keychain-backed `CredentialBackend`. Returns an\n * `available: false` result (with a reason) if the native module, the\n * OS keyring, or the probe fails.\n */\nexport async function tryKeychainBackend(): Promise<KeychainAvailability> {\n const mod = await loadKeyringModule();\n if (!mod) {\n return {\n available: false,\n reason: \"@napi-rs/keyring is not installed for this platform\",\n };\n }\n\n let entry: EntryInstance;\n try {\n entry = new mod.Entry(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);\n } catch (err) {\n return {\n available: false,\n reason: `Failed to construct keyring entry: ${String((err as Error).message ?? err)}`,\n };\n }\n\n if (!keychainProbe(entry)) {\n return {\n available: false,\n reason: \"OS keyring is not accessible from this process\",\n };\n }\n\n const backend: CredentialBackend = {\n name: \"keychain\",\n async read() {\n try {\n return parsePayload(entry.getPassword());\n } catch (err) {\n const message = String((err as Error).message ?? err);\n // Transient keychain access errors (e.g. Touch ID prompt\n // cancelled) should not surface as \"session wiped\". Treat the\n // unreadable state as \"no session\" so the caller can fall back\n // to prompting for login.\n if (/no matching entry|not found/i.test(message)) {\n return null;\n }\n throw err;\n }\n },\n async writeFull(creds) {\n writeFull(entry, creds);\n },\n async writeAccessOnly(accessToken) {\n writeAccessOnly(entry, accessToken);\n },\n async clear() {\n clear(entry);\n },\n };\n return { available: true, backend };\n}\n\n/**\n * Test-only escape hatch so unit tests can install a fake keyring module\n * without going through the dynamic import cache.\n */\nexport function _setKeyringModuleForTests(mod: KeyringModule | null): void {\n cachedModule = mod;\n}\n\nexport function _resetKeyringModuleForTests(): void {\n cachedModule = undefined;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/services/project/materialize-workspace.ts"],"sourcesContent":["import path from \"node:path\";\nimport type { GameTopologyManifest } from \"@dreamboard-games/sdk/types\";\nimport type {\n LocalMaintainerRegistryConfig,\n ProjectConfig,\n} from \"../../types.js\";\nimport { ensureDir } from \"../../utils/fs.js\";\nimport { writeManifest, writeRule, writeSnapshot } from \"./local-files.js\";\nimport { scaffoldStaticWorkspace } from \"./static-scaffold.js\";\nimport { applyWorkspaceCodegen } from \"./workspace-codegen.js\";\nimport { installWorkspaceDependencies } from \"./workspace-dependencies.js\";\nimport {\n updateProjectAuthoringState,\n updateProjectLocalMaintainerRegistry,\n} from \"./project-state.js\";\nimport { updateProjectState } from \"../../config/project-config.js\";\n\nexport type MaterializeWorkspaceProjectInput = {\n targetDir: string;\n projectId?: string;\n slug: string;\n gameId: string;\n deploymentId?: string;\n ownerScopeId?: string;\n bindingKey?: string;\n remoteHeadDigest?: string;\n apiBaseUrl: string;\n webBaseUrl: string;\n manifest: GameTopologyManifest;\n ruleText: string;\n ruleId?: string;\n manifestId?: string;\n manifestContentHash?: string;\n localMaintainerRegistry?: LocalMaintainerRegistryConfig | null;\n installDependencies?: boolean;\n agentManaged?: boolean;\n workspacePrepared?: boolean;\n allowCreateGame?: boolean;\n jobId?: string;\n packageManifest?: Record<string, unknown>;\n environmentManifest?: Record<string, unknown>;\n};\n\nexport async function materializeWorkspaceProject(\n input: MaterializeWorkspaceProjectInput,\n): Promise<ProjectConfig> {\n const targetDir = path.resolve(input.targetDir);\n await ensureDir(targetDir);\n await writeManifest(targetDir, input.manifest);\n await writeRule(targetDir, input.ruleText);\n\n await scaffoldStaticWorkspace(targetDir, \"new\", {\n localMaintainerRegistry: input.localMaintainerRegistry,\n });\n await applyWorkspaceCodegen({\n projectRoot: targetDir,\n manifest: input.manifest,\n });\n if (input.installDependencies ?? true) {\n await installWorkspaceDependencies(targetDir);\n }\n\n const authoringConfig =\n input.ruleId && input.manifestId && input.manifestContentHash\n ? updateProjectAuthoringState(baseProjectConfig(input), {\n ruleId: input.ruleId,\n manifestId: input.manifestId,\n manifestContentHash: input.manifestContentHash,\n })\n : baseProjectConfig(input);\n const projectConfig = updateProjectLocalMaintainerRegistry(\n authoringConfig,\n input.localMaintainerRegistry ?? undefined,\n );\n await updateProjectState(targetDir, projectConfig);\n await writeSnapshot(targetDir);\n return projectConfig;\n}\n\nfunction baseProjectConfig(\n input: MaterializeWorkspaceProjectInput,\n): ProjectConfig {\n return {\n schemaVersion: 2,\n projectId: input.projectId ?? input.gameId,\n gameId: input.gameId,\n deploymentId: input.deploymentId ?? \"legacy\",\n ownerScopeId: input.ownerScopeId ?? \"default\",\n bindingKey: input.bindingKey,\n remoteHeadDigest: input.remoteHeadDigest,\n slug: input.slug,\n jobId: input.jobId,\n agentManaged: input.agentManaged,\n workspacePrepared: input.workspacePrepared,\n allowCreateGame: input.allowCreateGame,\n apiBaseUrl: input.apiBaseUrl,\n webBaseUrl: input.webBaseUrl,\n packageManifest: input.packageManifest,\n environmentManifest: input.environmentManifest,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,UAAU;AA2CjB,eAAsB,4BACpB,OACwB;AACxB,QAAM,YAAY,KAAK,QAAQ,MAAM,SAAS;AAC9C,QAAM,UAAU,SAAS;AACzB,QAAM,cAAc,WAAW,MAAM,QAAQ;AAC7C,QAAM,UAAU,WAAW,MAAM,QAAQ;AAEzC,QAAM,wBAAwB,WAAW,OAAO;AAAA,IAC9C,yBAAyB,MAAM;AAAA,EACjC,CAAC;AACD,QAAM,sBAAsB;AAAA,IAC1B,aAAa;AAAA,IACb,UAAU,MAAM;AAAA,EAClB,CAAC;AACD,MAAI,MAAM,uBAAuB,MAAM;AACrC,UAAM,6BAA6B,SAAS;AAAA,EAC9C;AAEA,QAAM,kBACJ,MAAM,UAAU,MAAM,cAAc,MAAM,sBACtC,4BAA4B,kBAAkB,KAAK,GAAG;AAAA,IACpD,QAAQ,MAAM;AAAA,IACd,YAAY,MAAM;AAAA,IAClB,qBAAqB,MAAM;AAAA,EAC7B,CAAC,IACD,kBAAkB,KAAK;AAC7B,QAAM,gBAAgB;AAAA,IACpB;AAAA,IACA,MAAM,2BAA2B;AAAA,EACnC;AACA,QAAM,mBAAmB,WAAW,aAAa;AACjD,QAAM,cAAc,SAAS;AAC7B,SAAO;AACT;AAEA,SAAS,kBACP,OACe;AACf,SAAO;AAAA,IACL,eAAe;AAAA,IACf,WAAW,MAAM,aAAa,MAAM;AAAA,IACpC,QAAQ,MAAM;AAAA,IACd,cAAc,MAAM,gBAAgB;AAAA,IACpC,cAAc,MAAM,gBAAgB;AAAA,IACpC,YAAY,MAAM;AAAA,IAClB,kBAAkB,MAAM;AAAA,IACxB,MAAM,MAAM;AAAA,IACZ,OAAO,MAAM;AAAA,IACb,cAAc,MAAM;AAAA,IACpB,mBAAmB,MAAM;AAAA,IACzB,iBAAiB,MAAM;AAAA,IACvB,YAAY,MAAM;AAAA,IAClB,YAAY,MAAM;AAAA,IAClB,iBAAiB,MAAM;AAAA,IACvB,qBAAqB,MAAM;AAAA,EAC7B;AACF;","names":[]}
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- assertReducerContractPreflight
4
- } from "./chunk-YDIOW2BO.mjs";
5
- import "./chunk-XKCJBIRY.mjs";
6
- import "./chunk-QBAF7EYR.mjs";
7
- import "./chunk-H6XDQJ3N.mjs";
8
- export {
9
- assertReducerContractPreflight
10
- };
11
- //# sourceMappingURL=reducer-contract-preflight-COD2CO22.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/commands/test.ts","../../src/services/testing/runtime-mode.ts","../../src/services/workflows/resolve-latest-compiled-result.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport consola from \"consola\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { CONFIG_FLAG_ARGS } from \"../command-args.js\";\nimport { configureClient, resolveProjectContext } from \"../config/resolve.js\";\nimport { parseConfigFlags } from \"../flags.js\";\nimport { uploadInitialProjectionSdk } from \"../services/api/index.js\";\nimport { assertReleaseEnvironmentPortableDependencies } from \"../services/project/dependency-portability.js\";\nimport {\n generateReducerNativeArtifacts,\n isReducerNativeTestingWorkspace,\n runReducerNativeScenarios,\n type ReducerNativeScenarioSummary,\n} from \"../services/testing/reducer-native-test-harness.js\";\nimport { shouldUseRemoteTestRuntime } from \"../services/testing/runtime-mode.js\";\nimport { resolveLatestCompiledResult } from \"../services/workflows/resolve-latest-compiled-result.js\";\nimport type { ProjectConfig } from \"../types.js\";\nimport {\n isDreamboardApiError,\n isStaleContractArtifactMessage,\n STALE_CONTRACT_ARTIFACT_CODE,\n STALE_CONTRACT_ARTIFACT_EXIT_CODE,\n} from \"../utils/errors.js\";\n\ntype RequestedTestRunner = \"reducer\" | \"remote\" | \"browser\";\n\nexport const REDUCER_NATIVE_TEST_WORKSPACE_ERROR =\n \"dreamboard test now requires a reducer-native workspace with app/game.ts, shared/generated/ui-contract.ts, test/bases/*.base.ts, and test/scenarios/*.scenario.ts. Legacy test/base-scenarios.json workspaces are no longer supported.\";\n\nexport const NO_REDUCER_NATIVE_BASES_FOUND_ERROR =\n \"No bases found under test/bases/*.base.ts\";\n\nexport const NO_REDUCER_NATIVE_SCENARIOS_FOUND_ERROR =\n \"No scenarios found under test/scenarios/*.scenario.ts\";\n\nexport function isPreviewProjectionEndpointUnavailable(\n error: unknown,\n): boolean {\n if (!isDreamboardApiError(error) || error.status !== 404) {\n return false;\n }\n\n const endpoint = error.problem.instance ?? error.problem.context?.endpoint;\n const message = error.problem.detail ?? error.problem.title;\n return (\n endpoint?.includes(\"/preview/initial-projection\") === true &&\n message?.toLowerCase() === \"not found\"\n );\n}\n\nasync function uploadGeneratedPreviewProjection(options: {\n projectRoot: string;\n gameId: string;\n bases: Array<{ definition: { id: string } }>;\n}): Promise<void> {\n const previewBase =\n options.bases.find((base) => base.definition.id === \"initial-turn\") ??\n options.bases[0];\n if (!previewBase) {\n return;\n }\n const projectionPath = path.join(\n options.projectRoot,\n \"test\",\n \"generated\",\n \"bases\",\n previewBase.definition.id,\n \"player-1.projection.json\",\n );\n const projectionJson = await readFile(projectionPath, \"utf8\");\n try {\n await uploadInitialProjectionSdk(options.gameId, projectionJson);\n } catch (error) {\n if (isPreviewProjectionEndpointUnavailable(error)) {\n consola.warn(\n \"Skipping preview projection upload because the selected backend does not expose the preview projection endpoint.\",\n );\n return;\n }\n throw error;\n }\n}\n\nexport function resolveRequestedRunner(\n value: unknown,\n): RequestedTestRunner | undefined {\n if (value == null || value === \"\") {\n return undefined;\n }\n if (value === \"reducer\" || value === \"remote\" || value === \"browser\") {\n return value;\n }\n throw new Error(\n `Unsupported test runner '${String(value)}'. Expected one of reducer, remote, browser.`,\n );\n}\n\nfunction isStaleContractArtifactResult(\n result: ReducerNativeScenarioSummary[\"results\"][number],\n): boolean {\n return (\n result.errorCode === STALE_CONTRACT_ARTIFACT_CODE ||\n (result.error ? isStaleContractArtifactMessage(result.error) : false)\n );\n}\n\nexport function resolveTestRunExitCode(\n summary: ReducerNativeScenarioSummary,\n): number {\n if (summary.failed === 0) {\n return 0;\n }\n return summary.results.some(\n (result) => !result.success && isStaleContractArtifactResult(result),\n )\n ? STALE_CONTRACT_ARTIFACT_EXIT_CODE\n : 1;\n}\n\nasync function assertReducerNativeTestingWorkspace(\n projectRoot: string,\n): Promise<void> {\n if (await isReducerNativeTestingWorkspace(projectRoot)) {\n return;\n }\n\n throw new Error(REDUCER_NATIVE_TEST_WORKSPACE_ERROR);\n}\n\nasync function resolveReducerNativeRuntimeIdentity(options: {\n projectRoot: string;\n projectConfig: ProjectConfig;\n useRemoteRuntime: boolean;\n runner?: RequestedTestRunner;\n}): Promise<{\n gameId: string;\n compiledResultId?: string;\n}> {\n if (\n options.useRemoteRuntime ||\n options.runner === \"remote\" ||\n options.runner === \"browser\"\n ) {\n const latestCompiledResult = await resolveLatestCompiledResult(\n options.projectRoot,\n options.projectConfig,\n );\n return {\n gameId: options.projectConfig.gameId,\n compiledResultId: latestCompiledResult.id,\n };\n }\n\n return {\n gameId: options.projectConfig.gameId,\n compiledResultId: options.projectConfig.compile?.latestSuccessful?.resultId,\n };\n}\n\nconst generateCommand = defineCommand({\n meta: {\n name: \"generate\",\n description: \"Generate reducer-native base artifacts for typed scenarios\",\n },\n args: {\n scenario: {\n type: \"string\",\n description: \"Optional scenario file path under test/scenarios\",\n },\n debug: {\n type: \"boolean\",\n description: \"Print full reducer-native validation details\",\n default: false,\n },\n \"update-snapshots\": {\n type: \"boolean\",\n description: \"Refresh generated projection and scenario snapshots\",\n default: false,\n },\n ...CONFIG_FLAG_ARGS,\n },\n async run({ args }) {\n const parsedFlags = parseConfigFlags(args);\n const useRemoteRuntime = shouldUseRemoteTestRuntime(parsedFlags.env);\n const { projectRoot, projectConfig, config } = await resolveProjectContext(\n parsedFlags,\n { requireAuth: useRemoteRuntime },\n );\n await assertReleaseEnvironmentPortableDependencies({\n projectRoot,\n projectConfig,\n environment: config.environment,\n });\n\n await assertReducerNativeTestingWorkspace(projectRoot);\n\n const runtimeIdentity = await resolveReducerNativeRuntimeIdentity({\n projectRoot,\n projectConfig,\n useRemoteRuntime,\n });\n const { bases, scenarios } = await generateReducerNativeArtifacts({\n projectRoot,\n scenarioPath: args.scenario,\n compiledResultId: runtimeIdentity.compiledResultId,\n gameId: runtimeIdentity.gameId,\n debug: Boolean(args.debug),\n });\n\n if (bases.length === 0) {\n throw new Error(NO_REDUCER_NATIVE_BASES_FOUND_ERROR);\n }\n if (scenarios.length === 0) {\n throw new Error(NO_REDUCER_NATIVE_SCENARIOS_FOUND_ERROR);\n }\n\n if (useRemoteRuntime && config.authToken) {\n await configureClient(config);\n await uploadGeneratedPreviewProjection({\n projectRoot,\n gameId: runtimeIdentity.gameId,\n bases,\n });\n } else {\n consola.info(\n \"Skipping preview projection upload because this test generation is local-only.\",\n );\n }\n\n consola.success(\n `Generated ${bases.length} base state(s) for ${scenarios.length} scenario(s).`,\n );\n },\n});\n\nconst runCommand = defineCommand({\n meta: {\n name: \"run\",\n description: \"Run reducer-native scenarios from test/scenarios\",\n },\n args: {\n scenario: {\n type: \"string\",\n description: \"Optional scenario file path under test/scenarios\",\n },\n debug: {\n type: \"boolean\",\n description: \"Print full reducer-native validation details\",\n default: false,\n },\n \"update-snapshots\": {\n type: \"boolean\",\n description: \"Refresh generated projection and scenario snapshots\",\n default: false,\n },\n runner: {\n type: \"string\",\n valueHint: \"reducer|remote|browser\",\n description:\n \"Scenario runner: reducer (in-process, default), remote (live sessions against the configured backend), or browser (local web stack).\",\n },\n ...CONFIG_FLAG_ARGS,\n },\n async run({ args }) {\n const parsedFlags = parseConfigFlags(args);\n const useRemoteRuntime = shouldUseRemoteTestRuntime(parsedFlags.env);\n const runner = resolveRequestedRunner(args.runner) ?? \"reducer\";\n const { projectRoot, projectConfig, config } = await resolveProjectContext(\n parsedFlags,\n {\n requireAuth:\n useRemoteRuntime || runner === \"remote\" || runner === \"browser\",\n },\n );\n await assertReleaseEnvironmentPortableDependencies({\n projectRoot,\n projectConfig,\n environment: config.environment,\n });\n\n await assertReducerNativeTestingWorkspace(projectRoot);\n\n const runtimeIdentity = await resolveReducerNativeRuntimeIdentity({\n projectRoot,\n projectConfig,\n useRemoteRuntime,\n runner,\n });\n const summary = await runReducerNativeScenarios({\n projectRoot,\n projectConfig,\n resolvedConfig: config,\n runner,\n scenarioPath: args.scenario,\n compiledResultId: runtimeIdentity.compiledResultId,\n gameId: runtimeIdentity.gameId,\n debug: Boolean(args.debug),\n updateSnapshots: Boolean(args[\"update-snapshots\"]),\n });\n\n for (const result of summary.results) {\n if (result.success) {\n consola.success(`PASS ${result.id}`);\n } else if (\n result.errorCode === STALE_CONTRACT_ARTIFACT_CODE &&\n result.error\n ) {\n consola.error(result.error);\n } else {\n consola.error(\n `FAIL ${result.id}: ${result.error ?? \"Scenario failed\"}`,\n );\n }\n }\n\n consola.info(\n `Test summary: ${summary.passed} passed, ${summary.failed} failed.`,\n );\n if (summary.failed > 0) {\n process.exitCode = resolveTestRunExitCode(summary);\n }\n },\n});\n\nexport default defineCommand({\n meta: {\n name: \"test\",\n description: \"Reducer-native test runner with typed bases and scenarios\",\n },\n subCommands: {\n generate: generateCommand,\n run: runCommand,\n },\n});\n","import { IS_PUBLISHED_BUILD } from \"../../build-target.js\";\n\nexport function isRemoteTestEnvironment(\n environment: string | undefined,\n): environment is \"staging\" | \"prod\" {\n return environment === \"staging\" || environment === \"prod\";\n}\n\nexport function shouldUseRemoteTestRuntime(\n environment: string | undefined,\n): boolean {\n return IS_PUBLISHED_BUILD || isRemoteTestEnvironment(environment);\n}\n","import consola from \"consola\";\nimport type { CompiledResult } from \"@dreamboard-games/api-client\";\nimport type { ProjectConfig } from \"../../types.js\";\nimport {\n findProjectCompiledResultsForRevision,\n getProjectCompiledResultSdk,\n} from \"../api/compiled-results-api.js\";\nimport {\n getProjectCompileState,\n getProjectAuthoringState,\n getProjectPendingAuthoringSync,\n} from \"../project/project-state.js\";\n\nexport async function resolveLatestCompiledResult(\n projectRoot: string,\n projectConfig: ProjectConfig,\n): Promise<CompiledResult> {\n void projectRoot;\n const authoring = getProjectAuthoringState(projectConfig);\n if (getProjectPendingAuthoringSync(projectConfig)) {\n throw new Error(\n \"Previous sync did not finish updating local scaffold files. Run 'dreamboard sync' again first.\",\n );\n }\n if (authoring.revisionDigest) {\n const compile = getProjectCompileState(projectConfig);\n const latestSuccess = (\n await findProjectCompiledResultsForRevision({\n projectId: projectConfig.projectId,\n revisionDigest: authoring.revisionDigest,\n })\n ).find((result) => result.success);\n const matchingLocalSuccess =\n compile.latestSuccessful?.revisionDigest === authoring.revisionDigest\n ? compile.latestSuccessful\n : undefined;\n const resolvedSuccess =\n latestSuccess ??\n (matchingLocalSuccess?.resultId\n ? await getProjectCompiledResultSdk(\n projectConfig.projectId,\n matchingLocalSuccess.resultId,\n )\n : undefined);\n\n if (!resolvedSuccess?.success) {\n throw new Error(\n \"No successful compile exists for the current authored revision. Run 'dreamboard compile' first.\",\n );\n }\n\n const resultRevisionDigest = (resolvedSuccess as { revisionDigest?: string })\n .revisionDigest;\n if (\n resultRevisionDigest &&\n resultRevisionDigest !== authoring.revisionDigest\n ) {\n consola.warn(\n `Latest successful compile ${resolvedSuccess.id} belongs to ${resultRevisionDigest}, not ${authoring.revisionDigest}.`,\n );\n }\n\n consola.info(\n `Project summary:\\n compiledResultId: ${resolvedSuccess.id}\\n revisionDigest: ${authoring.revisionDigest}`,\n );\n\n return resolvedSuccess;\n }\n\n throw new Error(\n \"This workspace does not know its project revision yet. Run 'dreamboard sync' first.\",\n );\n}\n"],"mappings":"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","names":[]}