@dreamboard-games/cli 0.1.30-alpha.16 → 0.1.30-alpha.18

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 (80) hide show
  1. package/README.md +3 -1
  2. package/dist/agent-verifier/agent-workspace-verifier.mjs +12 -12
  3. package/dist/agent-verifier/{chunk-LKQ557TJ.mjs → chunk-334H4LE4.mjs} +3 -3
  4. package/dist/agent-verifier/{chunk-H3XNWKJU.mjs → chunk-7LFDFXLS.mjs} +2 -2
  5. package/dist/agent-verifier/{chunk-DWLTCUUX.mjs → chunk-7MAOGFFP.mjs} +6 -6
  6. package/dist/agent-verifier/{chunk-CO3BRUD6.mjs → chunk-AG5J3SMN.mjs} +11 -3
  7. package/dist/agent-verifier/chunk-AG5J3SMN.mjs.map +1 -0
  8. package/dist/agent-verifier/{chunk-AXXUGU7Q.mjs → chunk-AQ6UQHPT.mjs} +4 -30
  9. package/dist/agent-verifier/chunk-AQ6UQHPT.mjs.map +1 -0
  10. package/dist/agent-verifier/{chunk-A67WUYN2.mjs → chunk-B42OHJNY.mjs} +3 -3
  11. package/dist/agent-verifier/{chunk-V6AQDR7W.mjs → chunk-HUBV22JQ.mjs} +3 -3
  12. package/dist/agent-verifier/chunk-HUBV22JQ.mjs.map +1 -0
  13. package/dist/agent-verifier/{chunk-5GCZZ6NW.mjs → chunk-JB7VXCMB.mjs} +2 -2
  14. package/dist/agent-verifier/{chunk-WAFBU5U7.mjs → chunk-OJFZVGEL.mjs} +38 -13
  15. package/dist/agent-verifier/chunk-OJFZVGEL.mjs.map +1 -0
  16. package/dist/agent-verifier/{chunk-G2ECODRB.mjs → chunk-PLXXH5LY.mjs} +2 -2
  17. package/dist/agent-verifier/{chunk-655VJLXA.mjs → chunk-PWPOLHTW.mjs} +9 -12
  18. package/dist/agent-verifier/chunk-PWPOLHTW.mjs.map +1 -0
  19. package/dist/agent-verifier/{chunk-DPYC2NDB.mjs → chunk-RCYO6HWW.mjs} +2 -2
  20. package/dist/agent-verifier/{chunk-NFL3Z4Z7.mjs → chunk-RP7ZWFVH.mjs} +12 -8
  21. package/dist/agent-verifier/{chunk-NFL3Z4Z7.mjs.map → chunk-RP7ZWFVH.mjs.map} +1 -1
  22. package/dist/agent-verifier/{compile-4VSMC275.mjs → compile-VOBO2I6D.mjs} +12 -12
  23. package/dist/agent-verifier/{global-config-6UGFPLDA.mjs → global-config-L7PLLUK5.mjs} +3 -3
  24. package/dist/agent-verifier/{keychain-backend-BQLW5VEC.mjs → keychain-backend-UF3Z26JM.mjs} +1 -1
  25. package/dist/agent-verifier/keychain-backend-UF3Z26JM.mjs.map +1 -0
  26. package/dist/agent-verifier/{local-files-WPHUV6GU.mjs → local-files-DAFIR7SN.mjs} +4 -4
  27. package/dist/agent-verifier/{materialize-workspace-CV2JNXLU.mjs → materialize-workspace-PAC75NSP.mjs} +6 -6
  28. package/dist/agent-verifier/{reducer-native-test-harness-GY2CCQWN.mjs → reducer-native-test-harness-HSXRUGOR.mjs} +8 -8
  29. package/dist/agent-verifier/{static-scaffold-DJJRKMNB.mjs → static-scaffold-KSOTKJNQ.mjs} +4 -4
  30. package/dist/agent-verifier/{sync-YWK4SPWV.mjs → sync-MQJJEZAA.mjs} +13 -13
  31. package/dist/agent-verifier/{test-LQAGEQLY.mjs → test-R6HC6CYZ.mjs} +11 -11
  32. package/dist/agent-verifier/{workspace-codegen-4IWICKLB.mjs → workspace-codegen-SPPVHURX.mjs} +3 -3
  33. package/dist/authoring-compatibility-internal.js +1 -1
  34. package/dist/{chunk-2R4L2YDX.js → chunk-2WB3DYW4.js} +17 -8
  35. package/dist/chunk-2WB3DYW4.js.map +1 -0
  36. package/dist/{chunk-PW7D2W5S.js → chunk-2XMBZPL5.js} +45 -23
  37. package/dist/{chunk-PW7D2W5S.js.map → chunk-2XMBZPL5.js.map} +1 -1
  38. package/dist/{chunk-AVOAT522.js → chunk-J3CWZHY7.js} +4 -30
  39. package/dist/chunk-J3CWZHY7.js.map +1 -0
  40. package/dist/{global-config-NLGAFSRU.js → global-config-VQWFTIAV.js} +2 -2
  41. package/dist/index.js +5 -5
  42. package/dist/internal.js +3 -3
  43. package/dist/{keychain-backend-47LZ5IX5.js → keychain-backend-GO34KGTG.js} +1 -1
  44. package/dist/keychain-backend-GO34KGTG.js.map +1 -0
  45. package/package.json +1 -1
  46. package/release/authoring-release-set.json +4 -4
  47. package/skills/dreamboard/SKILL.md +8 -0
  48. package/skills/dreamboard/references/building-your-first-game.md +37 -15
  49. package/skills/dreamboard/references/canonical-concepts.md +74 -0
  50. package/skills/dreamboard/references/game-interface.md +15 -2
  51. package/skills/dreamboard/references/manifest-authoring.md +8 -0
  52. package/skills/dreamboard/references/reducer.md +47 -2
  53. package/skills/dreamboard/references/rule-authoring.md +10 -0
  54. package/skills/dreamboard/references/testing.md +7 -3
  55. package/dist/agent-verifier/chunk-655VJLXA.mjs.map +0 -1
  56. package/dist/agent-verifier/chunk-AXXUGU7Q.mjs.map +0 -1
  57. package/dist/agent-verifier/chunk-CO3BRUD6.mjs.map +0 -1
  58. package/dist/agent-verifier/chunk-V6AQDR7W.mjs.map +0 -1
  59. package/dist/agent-verifier/chunk-WAFBU5U7.mjs.map +0 -1
  60. package/dist/agent-verifier/keychain-backend-BQLW5VEC.mjs.map +0 -1
  61. package/dist/chunk-2R4L2YDX.js.map +0 -1
  62. package/dist/chunk-AVOAT522.js.map +0 -1
  63. package/dist/keychain-backend-47LZ5IX5.js.map +0 -1
  64. /package/dist/agent-verifier/{chunk-LKQ557TJ.mjs.map → chunk-334H4LE4.mjs.map} +0 -0
  65. /package/dist/agent-verifier/{chunk-H3XNWKJU.mjs.map → chunk-7LFDFXLS.mjs.map} +0 -0
  66. /package/dist/agent-verifier/{chunk-DWLTCUUX.mjs.map → chunk-7MAOGFFP.mjs.map} +0 -0
  67. /package/dist/agent-verifier/{chunk-A67WUYN2.mjs.map → chunk-B42OHJNY.mjs.map} +0 -0
  68. /package/dist/agent-verifier/{chunk-5GCZZ6NW.mjs.map → chunk-JB7VXCMB.mjs.map} +0 -0
  69. /package/dist/agent-verifier/{chunk-G2ECODRB.mjs.map → chunk-PLXXH5LY.mjs.map} +0 -0
  70. /package/dist/agent-verifier/{chunk-DPYC2NDB.mjs.map → chunk-RCYO6HWW.mjs.map} +0 -0
  71. /package/dist/agent-verifier/{compile-4VSMC275.mjs.map → compile-VOBO2I6D.mjs.map} +0 -0
  72. /package/dist/agent-verifier/{global-config-6UGFPLDA.mjs.map → global-config-L7PLLUK5.mjs.map} +0 -0
  73. /package/dist/agent-verifier/{local-files-WPHUV6GU.mjs.map → local-files-DAFIR7SN.mjs.map} +0 -0
  74. /package/dist/agent-verifier/{materialize-workspace-CV2JNXLU.mjs.map → materialize-workspace-PAC75NSP.mjs.map} +0 -0
  75. /package/dist/agent-verifier/{reducer-native-test-harness-GY2CCQWN.mjs.map → reducer-native-test-harness-HSXRUGOR.mjs.map} +0 -0
  76. /package/dist/agent-verifier/{static-scaffold-DJJRKMNB.mjs.map → static-scaffold-KSOTKJNQ.mjs.map} +0 -0
  77. /package/dist/agent-verifier/{sync-YWK4SPWV.mjs.map → sync-MQJJEZAA.mjs.map} +0 -0
  78. /package/dist/agent-verifier/{test-LQAGEQLY.mjs.map → test-R6HC6CYZ.mjs.map} +0 -0
  79. /package/dist/agent-verifier/{workspace-codegen-4IWICKLB.mjs.map → workspace-codegen-SPPVHURX.mjs.map} +0 -0
  80. /package/dist/{global-config-NLGAFSRU.js.map → global-config-VQWFTIAV.js.map} +0 -0
package/README.md CHANGED
@@ -35,7 +35,9 @@ Use browser login:
35
35
  dreamboard login
36
36
  ```
37
37
 
38
- The published CLI stores your refreshable session in the operating system credential store via `@napi-rs/keyring`. It does not fall back to plaintext `~/.dreamboard/auth.json`.
38
+ The CLI stores your refreshable session in `~/.dreamboard/auth.json` by default. The file is written atomically with owner-only permissions (`0600`).
39
+
40
+ The operating system keychain is optional. Set `"credentialBackend": "keychain"` in `~/.dreamboard/config.json`, or use `DREAMBOARD_CREDENTIAL_BACKEND=keychain`, to opt in.
39
41
 
40
42
  That stored session includes the Clerk refresh token the CLI needs to renew and exchange for short-lived Dreamboard API tokens automatically. Direct JWT injection is intentionally not part of the published CLI flow.
41
43
 
@@ -3,11 +3,11 @@ import {
3
3
  assertCompilerPortableDependencies,
4
4
  consola,
5
5
  resolveProjectContext
6
- } from "./chunk-NFL3Z4Z7.mjs";
6
+ } from "./chunk-RP7ZWFVH.mjs";
7
7
  import "./chunk-WSIYUUSD.mjs";
8
- import "./chunk-CO3BRUD6.mjs";
9
- import "./chunk-DPYC2NDB.mjs";
10
- import "./chunk-AXXUGU7Q.mjs";
8
+ import "./chunk-AG5J3SMN.mjs";
9
+ import "./chunk-RCYO6HWW.mjs";
10
+ import "./chunk-AQ6UQHPT.mjs";
11
11
  import "./chunk-TAEQKBJB.mjs";
12
12
  import "./chunk-RDYXWXXC.mjs";
13
13
  import "./chunk-MYMVXTZT.mjs";
@@ -48,7 +48,7 @@ Usage:
48
48
  }
49
49
  async function materializePreparedWorkspace(args) {
50
50
  const inputPath = readRequiredOption(args, "--input");
51
- const { materializeWorkspaceProject } = await import("./materialize-workspace-CV2JNXLU.mjs");
51
+ const { materializeWorkspaceProject } = await import("./materialize-workspace-PAC75NSP.mjs");
52
52
  const input = JSON.parse(
53
53
  await readFile(inputPath, "utf8")
54
54
  );
@@ -84,9 +84,9 @@ async function verifyAgentWorkspace(rawMode, args) {
84
84
  }
85
85
  async function runFullBackendConnectedVerification(parsedFlags) {
86
86
  const [{ default: cmdSync }, { default: cmdCompile }, { default: cmdTest }] = await Promise.all([
87
- import("./sync-YWK4SPWV.mjs"),
88
- import("./compile-4VSMC275.mjs"),
89
- import("./test-LQAGEQLY.mjs")
87
+ import("./sync-MQJJEZAA.mjs"),
88
+ import("./compile-VOBO2I6D.mjs"),
89
+ import("./test-R6HC6CYZ.mjs")
90
90
  ]);
91
91
  await runCommandDefinition(cmdSync, { ...parsedFlags, force: true });
92
92
  await runCommandDefinition(cmdCompile, parsedFlags);
@@ -150,9 +150,9 @@ async function runCloudLocalVerification(projectRoot, projectConfig, config) {
150
150
  { assertReducerContractPreflight },
151
151
  { getProjectLocalMaintainerRegistry }
152
152
  ] = await Promise.all([
153
- import("./static-scaffold-DJJRKMNB.mjs"),
154
- import("./local-files-WPHUV6GU.mjs"),
155
- import("./workspace-codegen-4IWICKLB.mjs"),
153
+ import("./static-scaffold-KSOTKJNQ.mjs"),
154
+ import("./local-files-DAFIR7SN.mjs"),
155
+ import("./workspace-codegen-SPPVHURX.mjs"),
156
156
  import("./workspace-dependencies-ZMHPHVQV.mjs"),
157
157
  import("./reducer-contract-preflight-FQB7M4PU.mjs"),
158
158
  import("./project-state-XKUSCFSV.mjs")
@@ -188,7 +188,7 @@ async function runCloudLocalVerification(projectRoot, projectConfig, config) {
188
188
  generateReducerNativeArtifacts,
189
189
  isReducerNativeTestingWorkspace,
190
190
  runReducerNativeScenarios
191
- } = await import("./reducer-native-test-harness-GY2CCQWN.mjs");
191
+ } = await import("./reducer-native-test-harness-HSXRUGOR.mjs");
192
192
  if (await isReducerNativeTestingWorkspace(projectRoot)) {
193
193
  const { bases } = await generateReducerNativeArtifacts({
194
194
  projectRoot,
@@ -9,14 +9,14 @@ import {
9
9
  isLibraryPath,
10
10
  materializeManifest,
11
11
  writeManifestSource
12
- } from "./chunk-H3XNWKJU.mjs";
12
+ } from "./chunk-7LFDFXLS.mjs";
13
13
  import {
14
14
  readWorkspaceTextFile,
15
15
  readWorkspaceTextFileIfExists,
16
16
  resolveWorkspacePath,
17
17
  unlinkWorkspaceFile,
18
18
  writeWorkspaceTextFile
19
- } from "./chunk-WAFBU5U7.mjs";
19
+ } from "./chunk-OJFZVGEL.mjs";
20
20
  import {
21
21
  LOCAL_IGNORE_DIRS,
22
22
  MANIFEST_FILE,
@@ -189,4 +189,4 @@ export {
189
189
  writeSnapshotFromFiles,
190
190
  getLocalDiff
191
191
  };
192
- //# sourceMappingURL=chunk-LKQ557TJ.mjs.map
192
+ //# sourceMappingURL=chunk-334H4LE4.mjs.map
@@ -14,7 +14,7 @@ import {
14
14
  workspacePathExists,
15
15
  writeWorkspaceJsonFile,
16
16
  writeWorkspaceTextFile
17
- } from "./chunk-WAFBU5U7.mjs";
17
+ } from "./chunk-OJFZVGEL.mjs";
18
18
  import {
19
19
  MANIFEST_FILE,
20
20
  MATERIALIZED_MANIFEST_FILE
@@ -2603,4 +2603,4 @@ export {
2603
2603
  isDynamicSeedPath,
2604
2604
  isLibraryPath
2605
2605
  };
2606
- //# sourceMappingURL=chunk-H3XNWKJU.mjs.map
2606
+ //# sourceMappingURL=chunk-7LFDFXLS.mjs.map
@@ -7,14 +7,14 @@ import {
7
7
  STALE_CONTRACT_ARTIFACT_CODE,
8
8
  isStaleContractArtifactError,
9
9
  toDreamboardApiError
10
- } from "./chunk-G2ECODRB.mjs";
10
+ } from "./chunk-PLXXH5LY.mjs";
11
11
  import {
12
12
  createUserTokenManager,
13
13
  resolveLocalHarnessAccessToken
14
- } from "./chunk-CO3BRUD6.mjs";
14
+ } from "./chunk-AG5J3SMN.mjs";
15
15
  import {
16
16
  loadManifest
17
- } from "./chunk-LKQ557TJ.mjs";
17
+ } from "./chunk-334H4LE4.mjs";
18
18
  import {
19
19
  REDUCER_TESTING_TYPES_WRAPPER_CONTENT,
20
20
  buildReducerTestingContractContent
@@ -31,7 +31,7 @@ import {
31
31
  getSessionSnapshot,
32
32
  hashContent,
33
33
  startGame
34
- } from "./chunk-H3XNWKJU.mjs";
34
+ } from "./chunk-7LFDFXLS.mjs";
35
35
  import {
36
36
  external_exports
37
37
  } from "./chunk-JZTH3EMV.mjs";
@@ -41,7 +41,7 @@ import {
41
41
  workspacePathExists,
42
42
  writeWorkspaceJsonFile,
43
43
  writeWorkspaceTextFile
44
- } from "./chunk-WAFBU5U7.mjs";
44
+ } from "./chunk-OJFZVGEL.mjs";
45
45
  import {
46
46
  PROJECT_DIR_NAME
47
47
  } from "./chunk-M7UVBANQ.mjs";
@@ -3317,4 +3317,4 @@ export {
3317
3317
  generateReducerNativeArtifacts,
3318
3318
  runReducerNativeScenarios
3319
3319
  };
3320
- //# sourceMappingURL=chunk-DWLTCUUX.mjs.map
3320
+ //# sourceMappingURL=chunk-7MAOGFFP.mjs.map
@@ -1,8 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- IS_PUBLISHED_BUILD,
4
3
  withCredentialLock
5
- } from "./chunk-AXXUGU7Q.mjs";
4
+ } from "./chunk-AQ6UQHPT.mjs";
5
+
6
+ // src/build-target.ts
7
+ var injectedBuildChannel = true ? "development" : void 0;
8
+ var BUILD_CHANNEL = injectedBuildChannel === "published" ? "published" : "development";
9
+ var IS_PUBLISHED_BUILD = BUILD_CHANNEL === "published";
10
+ var PUBLISHED_ENVIRONMENT = "prod";
6
11
 
7
12
  // src/auth/clerk-oauth.ts
8
13
  import crypto from "crypto";
@@ -336,7 +341,10 @@ function isLocalAwsUrl(rawUrl) {
336
341
  }
337
342
 
338
343
  export {
344
+ BUILD_CHANNEL,
345
+ IS_PUBLISHED_BUILD,
346
+ PUBLISHED_ENVIRONMENT,
339
347
  createUserTokenManager,
340
348
  resolveLocalHarnessAccessToken
341
349
  };
342
- //# sourceMappingURL=chunk-CO3BRUD6.mjs.map
350
+ //# sourceMappingURL=chunk-AG5J3SMN.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/build-target.ts","../../src/auth/clerk-oauth.ts","../../src/auth/token-exchange.ts","../../src/auth/user-token-manager.ts","../../src/config/local-harness-auth.ts"],"sourcesContent":["declare const __DREAMBOARD_BUILD_CHANNEL__: string | undefined;\n\nconst injectedBuildChannel =\n typeof __DREAMBOARD_BUILD_CHANNEL__ === \"string\"\n ? __DREAMBOARD_BUILD_CHANNEL__\n : undefined;\n\nexport const BUILD_CHANNEL =\n injectedBuildChannel === \"published\" ? \"published\" : \"development\";\n\nexport const IS_PUBLISHED_BUILD = BUILD_CHANNEL === \"published\";\nexport const PUBLISHED_ENVIRONMENT = \"prod\" as const;\n","import crypto from \"node:crypto\";\n\nexport type ClerkOAuthConfig = {\n issuer?: string;\n clientId?: string;\n tokenUrl?: string;\n scope?: string;\n};\n\nexport type ClerkOAuthTokenResponse = {\n accessToken: string;\n refreshToken: string;\n expiresAt?: string;\n tokenUrl: string;\n};\n\nexport function createPkcePair(): { verifier: string; challenge: string } {\n const verifier = base64Url(crypto.randomBytes(32));\n const challenge = base64Url(\n crypto.createHash(\"sha256\").update(verifier).digest(),\n );\n return { verifier, challenge };\n}\n\nexport function buildClerkAuthorizationUrl(input: {\n config: ClerkOAuthConfig;\n redirectUri: string;\n state: string;\n codeChallenge: string;\n}): URL {\n const { issuer, clientId, scope } = assertConfigured(input.config);\n const url = new URL(\"/oauth/authorize\", issuer);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", clientId);\n url.searchParams.set(\"redirect_uri\", input.redirectUri);\n url.searchParams.set(\"state\", input.state);\n url.searchParams.set(\"code_challenge\", input.codeChallenge);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n url.searchParams.set(\"scope\", scope ?? \"openid profile email offline_access\");\n return url;\n}\n\nexport async function exchangeClerkOAuthCode(input: {\n config: ClerkOAuthConfig;\n code: string;\n redirectUri: string;\n codeVerifier: string;\n}): Promise<ClerkOAuthTokenResponse> {\n const { clientId, tokenUrl } = assertConfigured(input.config);\n const body = new URLSearchParams({\n grant_type: \"authorization_code\",\n client_id: clientId,\n code: input.code,\n redirect_uri: input.redirectUri,\n code_verifier: input.codeVerifier,\n });\n return requestClerkToken(tokenUrl, body);\n}\n\nexport async function refreshClerkOAuthToken(input: {\n config: ClerkOAuthConfig;\n refreshToken: string;\n}): Promise<ClerkOAuthTokenResponse> {\n const { clientId, tokenUrl } = assertConfigured(input.config);\n const body = new URLSearchParams({\n grant_type: \"refresh_token\",\n client_id: clientId,\n refresh_token: input.refreshToken,\n });\n return requestClerkToken(tokenUrl, body);\n}\n\nfunction assertConfigured(config: ClerkOAuthConfig): {\n issuer: string;\n clientId: string;\n tokenUrl: string;\n scope?: string;\n} {\n const issuer = config.issuer?.trim().replace(/\\/$/, \"\");\n const clientId = config.clientId?.trim();\n if (!issuer || !clientId) {\n throw new Error(\n [\n \"Clerk OAuth CLI is not configured for this environment.\",\n \"The CLI expects first-party environments to be configured in its built-in registry.\",\n \"If this environment has no registered public Clerk OAuth client, create one and release a CLI with its client id.\",\n \"For emergency overrides, set the environment-specific DREAMBOARD_<ENV>_CLERK_OAUTH_* variables or DREAMBOARD_CLERK_OAUTH_*.\",\n \"For local harness auth, use `pnpm auth:local` or the auto-bootstrapped local harness flows instead.\",\n ].join(\" \"),\n );\n }\n return {\n issuer,\n clientId,\n tokenUrl:\n config.tokenUrl?.trim() || new URL(\"/oauth/token\", issuer).toString(),\n scope: config.scope?.trim() || undefined,\n };\n}\n\nasync function requestClerkToken(\n tokenUrl: string,\n body: URLSearchParams,\n): Promise<ClerkOAuthTokenResponse> {\n const response = await fetch(tokenUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body,\n });\n if (!response.ok) {\n const detail = await response.text();\n throw new Error(\n `Clerk OAuth token request failed (${response.status}): ${detail}`,\n );\n }\n const payload = (await response.json()) as {\n access_token?: unknown;\n refresh_token?: unknown;\n expires_in?: unknown;\n };\n if (typeof payload.access_token !== \"string\") {\n throw new Error(\"Clerk OAuth token response did not include access_token.\");\n }\n if (typeof payload.refresh_token !== \"string\") {\n throw new Error(\n \"Clerk OAuth token response did not include refresh_token.\",\n );\n }\n const expiresAt =\n typeof payload.expires_in === \"number\"\n ? new Date(Date.now() + payload.expires_in * 1000).toISOString()\n : undefined;\n return {\n accessToken: payload.access_token,\n refreshToken: payload.refresh_token,\n expiresAt,\n tokenUrl,\n };\n}\n\nfunction base64Url(bytes: Buffer): string {\n return bytes\n .toString(\"base64\")\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/g, \"\");\n}\n","export type DreamboardTokenAudience = \"dreamboard-api\" | \"dreamboard-git\";\n\nexport type DreamboardTokenResponse = {\n accessToken: string;\n tokenType: \"Bearer\";\n audience: DreamboardTokenAudience;\n expiresIn?: number;\n expiresAt?: string;\n};\n\nexport async function exchangeDreamboardUserToken(input: {\n apiBaseUrl: string;\n clerkAccessToken: string;\n audience: DreamboardTokenAudience;\n fetchImpl?: typeof fetch;\n}): Promise<DreamboardTokenResponse> {\n const fetchImpl = input.fetchImpl ?? globalThis.fetch.bind(globalThis);\n const response = await fetchImpl(\n new URL(\"/api/auth/token-exchange\", input.apiBaseUrl),\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${input.clerkAccessToken}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify({ audience: input.audience }),\n },\n );\n\n if (!response.ok) {\n throw new Error(\n `Dreamboard token exchange failed (${response.status}). Run \\`dreamboard login\\` to authenticate again.`,\n );\n }\n\n const payload = (await response.json()) as {\n accessToken?: unknown;\n tokenType?: unknown;\n audience?: unknown;\n expiresIn?: unknown;\n };\n\n if (typeof payload.accessToken !== \"string\" || payload.accessToken === \"\") {\n throw new Error(\"Dreamboard token exchange response omitted accessToken.\");\n }\n if (payload.tokenType !== \"Bearer\") {\n throw new Error(\n \"Dreamboard token exchange response had invalid tokenType.\",\n );\n }\n if (payload.audience !== input.audience) {\n throw new Error(\"Dreamboard token exchange response had wrong audience.\");\n }\n\n const expiresIn =\n typeof payload.expiresIn === \"number\" && Number.isFinite(payload.expiresIn)\n ? payload.expiresIn\n : undefined;\n\n return {\n accessToken: payload.accessToken,\n tokenType: \"Bearer\",\n audience: input.audience,\n expiresIn,\n expiresAt:\n expiresIn === undefined\n ? undefined\n : new Date(Date.now() + expiresIn * 1000).toISOString(),\n };\n}\n","import type {\n Credentials,\n StoredSessionSnapshot,\n} from \"../config/credential-store.js\";\nimport { withCredentialLock } from \"../config/credential-store.js\";\nimport { refreshClerkOAuthToken } from \"./clerk-oauth.js\";\nimport {\n exchangeDreamboardUserToken,\n type DreamboardTokenAudience,\n} from \"./token-exchange.js\";\nimport type { ResolvedConfig } from \"../types.js\";\n\nconst TOKEN_REFRESH_WINDOW_MS = 60 * 1000;\n\nexport type AccessToken = {\n readonly token: string;\n readonly expiresAt?: string;\n readonly audience: DreamboardTokenAudience;\n};\n\nexport interface UserTokenManager {\n resolveApiToken(): Promise<AccessToken | null>;\n resolveGitToken(): Promise<AccessToken>;\n}\n\nexport function createUserTokenManager(\n config: ResolvedConfig,\n): UserTokenManager {\n return {\n async resolveApiToken() {\n const localOrInjected = resolveNonStoredToken(config, \"dreamboard-api\");\n if (localOrInjected) return localOrInjected;\n\n if (!usesStoredSession(config)) return null;\n\n return withCredentialLock(async (ops) => {\n const stored = await ops.read();\n const apiToken = freshStoredApiToken(stored);\n if (apiToken) return apiToken;\n\n const clerk = await resolveFreshClerkAccessToken(config, stored);\n const exchanged = await exchangeDreamboardUserToken({\n apiBaseUrl: config.apiBaseUrl,\n clerkAccessToken: clerk.accessToken,\n audience: \"dreamboard-api\",\n });\n\n await ops.writeFull({\n ...clerk,\n dreamboardApiToken: exchanged.accessToken,\n dreamboardApiExpiresAt: exchanged.expiresAt,\n });\n\n return {\n token: exchanged.accessToken,\n expiresAt: exchanged.expiresAt,\n audience: \"dreamboard-api\",\n };\n });\n },\n\n async resolveGitToken() {\n const localOrInjected = resolveNonStoredToken(config, \"dreamboard-git\");\n if (localOrInjected) return localOrInjected;\n\n if (!usesStoredSession(config)) {\n throw new Error(\n \"Missing Dreamboard session. Run `dreamboard login` to authenticate.\",\n );\n }\n\n return withCredentialLock(async (ops) => {\n const stored = await ops.read();\n const clerk = await resolveFreshClerkAccessToken(config, stored);\n const exchanged = await exchangeDreamboardUserToken({\n apiBaseUrl: config.apiBaseUrl,\n clerkAccessToken: clerk.accessToken,\n audience: \"dreamboard-git\",\n });\n\n await ops.writeFull(clerk);\n\n return {\n token: exchanged.accessToken,\n expiresAt: exchanged.expiresAt,\n audience: \"dreamboard-git\",\n };\n });\n },\n };\n}\n\nfunction resolveNonStoredToken(\n config: ResolvedConfig,\n audience: DreamboardTokenAudience,\n): AccessToken | null {\n if (usesStoredSession(config)) return null;\n if (!config.authToken) return null;\n return {\n token: config.authToken,\n expiresAt: config.tokenExpiresAt,\n audience,\n };\n}\n\nfunction freshStoredApiToken(\n stored: StoredSessionSnapshot | null,\n): AccessToken | null {\n if (!stored?.dreamboardApiToken) return null;\n if (isFresh(stored.dreamboardApiExpiresAt, stored.dreamboardApiToken)) {\n return {\n token: stored.dreamboardApiToken,\n expiresAt: stored.dreamboardApiExpiresAt,\n audience: \"dreamboard-api\",\n };\n }\n return null;\n}\n\nasync function resolveFreshClerkAccessToken(\n config: ResolvedConfig,\n stored: StoredSessionSnapshot | null,\n): Promise<Credentials> {\n const accessToken = stored?.accessToken ?? config.clerkAccessToken;\n const refreshToken = stored?.refreshToken ?? config.refreshToken;\n const tokenExpiresAt = stored?.tokenExpiresAt ?? config.clerkAccessExpiresAt;\n\n if (!refreshToken) {\n throw new Error(\n \"Stored Dreamboard session is missing its refresh token. Run `dreamboard login` to authenticate again.\",\n );\n }\n\n if (accessToken && isFresh(tokenExpiresAt, accessToken)) {\n return {\n accessToken,\n refreshToken,\n tokenExpiresAt,\n dreamboardApiToken: stored?.dreamboardApiToken,\n dreamboardApiExpiresAt: stored?.dreamboardApiExpiresAt,\n clerkOAuthIssuer: stored?.clerkOAuthIssuer ?? config.clerkOAuthIssuer,\n clerkOAuthClientId:\n stored?.clerkOAuthClientId ?? config.clerkOAuthClientId,\n clerkOAuthTokenUrl:\n stored?.clerkOAuthTokenUrl ?? config.clerkOAuthTokenUrl,\n environment: stored?.environment ?? config.environment,\n };\n }\n\n const payload = await refreshClerkOAuthToken({\n config: {\n issuer: stored?.clerkOAuthIssuer ?? config.clerkOAuthIssuer,\n clientId: stored?.clerkOAuthClientId ?? config.clerkOAuthClientId,\n tokenUrl: stored?.clerkOAuthTokenUrl ?? config.clerkOAuthTokenUrl,\n },\n refreshToken,\n });\n\n return {\n accessToken: payload.accessToken,\n refreshToken: payload.refreshToken,\n tokenExpiresAt: payload.expiresAt,\n clerkOAuthIssuer: stored?.clerkOAuthIssuer ?? config.clerkOAuthIssuer,\n clerkOAuthClientId: stored?.clerkOAuthClientId ?? config.clerkOAuthClientId,\n clerkOAuthTokenUrl: payload.tokenUrl,\n environment: stored?.environment ?? config.environment,\n };\n}\n\nfunction isFresh(expiresAt: string | undefined, token: string): boolean {\n const expiry = expiresAt ? new Date(expiresAt) : getJwtExpiry(token);\n return (\n expiry !== null &&\n Number.isFinite(expiry.getTime()) &&\n expiry.getTime() > Date.now() + TOKEN_REFRESH_WINDOW_MS\n );\n}\n\nfunction getJwtExpiry(accessToken: string | undefined): Date | null {\n if (!accessToken) return null;\n const parts = accessToken.split(\".\");\n if (parts.length !== 3) return null;\n try {\n const payload = JSON.parse(\n Buffer.from(parts[1]!, \"base64url\").toString(\"utf8\"),\n ) as { exp?: unknown };\n if (typeof payload.exp !== \"number\" || !Number.isFinite(payload.exp)) {\n return null;\n }\n return new Date(payload.exp * 1000);\n } catch {\n return null;\n }\n}\n\nfunction usesStoredSession(config: ResolvedConfig): boolean {\n return config.refreshTokenSource === \"global\";\n}\n","import { createHmac, randomUUID } from \"node:crypto\";\nimport type { ResolvedConfig } from \"../types.js\";\nimport { IS_PUBLISHED_BUILD } from \"../build-target.js\";\n\ntype LocalHarnessProfile = \"local\" | \"local-aws\";\n\nconst DEFAULT_SUBJECT = \"harness-smoke-local@dreamboard.local\";\nconst DEFAULT_ISSUER = \"dreamboard-local-harness\";\nconst DEFAULT_SECRET = \"dreamboard-local-harness-token-secret\";\nconst LOCAL_AWS_ISSUER = \"dreamboard-local-aws-harness\";\nconst LOCAL_AWS_SECRET = \"dreamboard-local-aws-harness-token-secret\";\nconst DEFAULT_TTL_SECONDS = 8 * 60 * 60;\n\nconst mintedTokens = new Map<string, string>();\n\nexport function resolveLocalHarnessAccessToken(\n config: ResolvedConfig,\n): string | undefined {\n if (IS_PUBLISHED_BUILD || config.environment !== \"local\") {\n return undefined;\n }\n\n const profile = inferLocalHarnessProfile(config);\n if (\n config.authToken &&\n (profile !== \"local-aws\" || isExplicitTokenSource(config.authTokenSource))\n ) {\n return undefined;\n }\n\n const cacheKey = [\n profile,\n process.env.LOCAL_HARNESS_TOKEN_ISSUER ?? \"\",\n process.env.LOCAL_HARNESS_TOKEN_SECRET ?? \"\",\n process.env.LOCAL_HARNESS_SUBJECT ?? \"\",\n process.env.HARNESS_USER_EMAIL ?? \"\",\n process.env.LOCAL_HARNESS_EMAIL ?? \"\",\n process.env.LOCAL_HARNESS_TOKEN_TTL_SECONDS ?? \"\",\n ].join(\"\\0\");\n const cached = mintedTokens.get(cacheKey);\n if (cached) return cached;\n\n const token = mintLocalHarnessToken(profile);\n mintedTokens.set(cacheKey, token);\n return token;\n}\n\nfunction isExplicitTokenSource(\n source: ResolvedConfig[\"authTokenSource\"],\n): boolean {\n return source === \"flag\" || source === \"env\" || source === \"agent-env\";\n}\n\nexport function inferLocalHarnessProfile(\n config: Pick<ResolvedConfig, \"apiBaseUrl\" | \"webBaseUrl\">,\n): LocalHarnessProfile {\n return isLocalAwsUrl(config.apiBaseUrl) || isLocalAwsUrl(config.webBaseUrl)\n ? \"local-aws\"\n : \"local\";\n}\n\nfunction mintLocalHarnessToken(profile: LocalHarnessProfile): string {\n const subject =\n envValue(process.env.LOCAL_HARNESS_SUBJECT) ??\n envValue(process.env.HARNESS_USER_EMAIL) ??\n DEFAULT_SUBJECT;\n const email =\n envValue(process.env.LOCAL_HARNESS_EMAIL) ??\n (subject.includes(\"@\") ? subject : undefined);\n const issuer =\n envValue(process.env.LOCAL_HARNESS_TOKEN_ISSUER) ??\n (profile === \"local-aws\" ? LOCAL_AWS_ISSUER : DEFAULT_ISSUER);\n const secret =\n envValue(process.env.LOCAL_HARNESS_TOKEN_SECRET) ??\n (profile === \"local-aws\" ? LOCAL_AWS_SECRET : DEFAULT_SECRET);\n const ttlSeconds = Number(\n envValue(process.env.LOCAL_HARNESS_TOKEN_TTL_SECONDS) ??\n String(DEFAULT_TTL_SECONDS),\n );\n if (!Number.isFinite(ttlSeconds) || ttlSeconds <= 0) {\n throw new Error(\n \"LOCAL_HARNESS_TOKEN_TTL_SECONDS must be a positive number.\",\n );\n }\n\n const now = Math.floor(Date.now() / 1000);\n const payload = {\n typ: \"local_harness_access\",\n dreamboard_provider: \"local-harness\",\n dreamboard_provider_subject: subject,\n ...(email ? { email } : {}),\n iss: issuer,\n sub: subject,\n iat: now,\n exp: now + Math.floor(ttlSeconds),\n jti: randomUUID(),\n };\n\n const headerPart = base64UrlJson({ alg: \"HS256\", typ: \"JWT\" });\n const payloadPart = base64UrlJson(payload);\n const signature = createHmac(\"sha256\", secret)\n .update(`${headerPart}.${payloadPart}`)\n .digest(\"base64url\");\n return `${headerPart}.${payloadPart}.${signature}`;\n}\n\nfunction base64UrlJson(value: unknown): string {\n return Buffer.from(JSON.stringify(value), \"utf8\").toString(\"base64url\");\n}\n\nfunction envValue(raw: string | undefined): string | undefined {\n return typeof raw === \"string\" && raw.trim().length > 0\n ? raw.trim()\n : undefined;\n}\n\nfunction isLocalAwsUrl(rawUrl: string | undefined): boolean {\n if (!rawUrl) return false;\n try {\n const url = new URL(rawUrl);\n return (\n (url.hostname === \"localhost\" || url.hostname === \"127.0.0.1\") &&\n (url.port === \"18080\" || url.port === \"8088\")\n );\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":[]}
@@ -11,14 +11,6 @@ import {
11
11
  import os from "os";
12
12
  import path from "path";
13
13
  import { promises as fs } from "fs";
14
-
15
- // src/build-target.ts
16
- var injectedBuildChannel = true ? "development" : void 0;
17
- var BUILD_CHANNEL = injectedBuildChannel === "published" ? "published" : "development";
18
- var IS_PUBLISHED_BUILD = BUILD_CHANNEL === "published";
19
- var PUBLISHED_ENVIRONMENT = "prod";
20
-
21
- // src/config/credential-store.ts
22
14
  function getCredentialFilePath() {
23
15
  return path.join(os.homedir(), PROJECT_DIR_NAME, "auth.json");
24
16
  }
@@ -117,19 +109,6 @@ var migrationCompleted = false;
117
109
  var backendResolver = defaultBackendResolver;
118
110
  async function defaultBackendResolver() {
119
111
  const override = (process.env.DREAMBOARD_CREDENTIAL_BACKEND ?? "").trim().toLowerCase();
120
- if (IS_PUBLISHED_BUILD) {
121
- if (override && override !== "keychain" && override !== "auto") {
122
- throw new CredentialStoreUnavailableError(
123
- "published builds require the OS credential store"
124
- );
125
- }
126
- const { tryKeychainBackend: tryKeychainBackend2 } = await import("./keychain-backend-BQLW5VEC.mjs");
127
- const keychain2 = await tryKeychainBackend2();
128
- if (keychain2.available) {
129
- return keychain2.backend;
130
- }
131
- throw new CredentialStoreUnavailableError(keychain2.reason);
132
- }
133
112
  if (override === "file") {
134
113
  return fileCredentialBackend;
135
114
  }
@@ -142,7 +121,7 @@ async function defaultBackendResolver() {
142
121
  if (!useKeychain) {
143
122
  return fileCredentialBackend;
144
123
  }
145
- const { tryKeychainBackend } = await import("./keychain-backend-BQLW5VEC.mjs");
124
+ const { tryKeychainBackend } = await import("./keychain-backend-UF3Z26JM.mjs");
146
125
  const keychain = await tryKeychainBackend();
147
126
  if (keychain.available) {
148
127
  return keychain.backend;
@@ -151,7 +130,7 @@ async function defaultBackendResolver() {
151
130
  }
152
131
  async function readCredentialBackendPreference() {
153
132
  try {
154
- const { loadGlobalConfig } = await import("./global-config-6UGFPLDA.mjs");
133
+ const { loadGlobalConfig } = await import("./global-config-L7PLLUK5.mjs");
155
134
  const config = await loadGlobalConfig();
156
135
  return config.credentialBackend === "keychain";
157
136
  } catch {
@@ -162,9 +141,7 @@ async function getCredentialBackend() {
162
141
  if (cachedBackend === null) {
163
142
  cachedBackend = await backendResolver();
164
143
  if (!migrationCompleted && cachedBackend.name !== "file") {
165
- await migrateFromFileBackendIfNeeded(cachedBackend, {
166
- failClosed: IS_PUBLISHED_BUILD
167
- });
144
+ await migrateFromFileBackendIfNeeded(cachedBackend);
168
145
  }
169
146
  migrationCompleted = true;
170
147
  }
@@ -245,11 +222,8 @@ async function withCredentialLock(fn, options) {
245
222
  }
246
223
 
247
224
  export {
248
- BUILD_CHANNEL,
249
- IS_PUBLISHED_BUILD,
250
- PUBLISHED_ENVIRONMENT,
251
225
  getCredentialFilePath,
252
226
  getStoredSession,
253
227
  withCredentialLock
254
228
  };
255
- //# sourceMappingURL=chunk-AXXUGU7Q.mjs.map
229
+ //# sourceMappingURL=chunk-AQ6UQHPT.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/config/credential-store.ts"],"sourcesContent":["/**\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. All builds default to the file backend. The OS keychain is an explicit\n * opt-in through config or `DREAMBOARD_CREDENTIAL_BACKEND=keychain`.\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/**\n * Fully refreshable session. `accessToken` is the Clerk OAuth bootstrap token\n * retained for refresh/exchange compatibility; ordinary API calls use\n * `dreamboardApiToken`.\n */\nexport type Credentials = {\n readonly accessToken: string;\n readonly refreshToken: string;\n readonly tokenExpiresAt?: string;\n readonly dreamboardApiToken?: string;\n readonly dreamboardApiExpiresAt?: 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 dreamboardApiToken?: string;\n readonly dreamboardApiExpiresAt?: 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 clerkAccessToken: string;\n clerkAccessExpiresAt: string;\n accessToken: string;\n authToken: string;\n refreshToken: string;\n tokenExpiresAt: string;\n dreamboardApiToken: string;\n dreamboardApiExpiresAt: 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 =\n parsed.clerkAccessToken ?? 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:\n parsed.clerkAccessExpiresAt || parsed.tokenExpiresAt || undefined,\n dreamboardApiToken: parsed.dreamboardApiToken || undefined,\n dreamboardApiExpiresAt: parsed.dreamboardApiExpiresAt || 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 clerkAccessToken: creds.accessToken,\n refreshToken: creds.refreshToken,\n clerkAccessExpiresAt: creds.tokenExpiresAt,\n dreamboardApiToken: creds.dreamboardApiToken,\n dreamboardApiExpiresAt: creds.dreamboardApiExpiresAt,\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\nexport class CredentialStoreUnavailableError extends Error {\n readonly code = \"CREDENTIAL_STORE_UNAVAILABLE\";\n\n constructor(reason: string) {\n super(`Credential store unavailable: ${reason}`);\n this.name = \"CredentialStoreUnavailableError\";\n }\n}\n\nlet cachedBackend: CredentialBackend | null = null;\nlet migrationCompleted = false;\nlet backendResolver: BackendResolver = defaultBackendResolver;\n\n/**\n * Resolver precedence for all builds:\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 file-default 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 options: { failClosed?: boolean } = {},\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 const migrated: Credentials = {\n accessToken: onDisk.accessToken,\n refreshToken: onDisk.refreshToken,\n tokenExpiresAt: onDisk.tokenExpiresAt,\n dreamboardApiToken: onDisk.dreamboardApiToken,\n dreamboardApiExpiresAt: onDisk.dreamboardApiExpiresAt,\n clerkOAuthIssuer: onDisk.clerkOAuthIssuer,\n clerkOAuthClientId: onDisk.clerkOAuthClientId,\n clerkOAuthTokenUrl: onDisk.clerkOAuthTokenUrl,\n environment: onDisk.environment,\n };\n await target.writeFull(migrated);\n await verifyMigratedSession(target, migrated);\n } else if (onDisk.accessToken) {\n await target.writeAccessOnly(onDisk.accessToken);\n const migrated = await target.read();\n if (migrated?.accessToken !== onDisk.accessToken) {\n throw new Error(\"Credential migration verification failed.\");\n }\n } else {\n return;\n }\n await fileCredentialBackend.clear();\n } catch (error) {\n if (options.failClosed) {\n throw new CredentialStoreUnavailableError(\n error instanceof Error ? error.message : String(error),\n );\n }\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\nasync function verifyMigratedSession(\n target: CredentialBackend,\n expected: Credentials,\n): Promise<void> {\n const migrated = await target.read();\n if (\n migrated?.accessToken !== expected.accessToken ||\n migrated.refreshToken !== expected.refreshToken\n ) {\n throw new Error(\"Credential migration verification failed.\");\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 if (process.env.DREAMBOARD_AGENT_TOKEN?.trim()) {\n return null;\n }\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 {\n accessToken,\n refreshToken,\n tokenExpiresAt: snapshot.tokenExpiresAt,\n dreamboardApiToken: snapshot.dreamboardApiToken,\n dreamboardApiExpiresAt: snapshot.dreamboardApiExpiresAt,\n clerkOAuthIssuer: snapshot.clerkOAuthIssuer,\n clerkOAuthClientId: snapshot.clerkOAuthClientId,\n clerkOAuthTokenUrl: snapshot.clerkOAuthTokenUrl,\n environment: snapshot.environment,\n };\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":";;;;;;;;;;AAsCA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,YAAY,UAAU;AA0ExB,SAAS,wBAAgC;AAC9C,SAAO,KAAK,KAAK,GAAG,QAAQ,GAAG,kBAAkB,WAAW;AAC9D;AAEA,SAAS,wBAAgC;AACvC,SAAO,GAAG,sBAAsB,CAAC;AACnC;AAEA,eAAe,WAAkD;AAC/D,QAAM,WAAW,sBAAsB;AACvC,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,GAAG,SAAS,UAAU,MAAM;AAAA,EAC3C,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,UAAM;AAAA,EACR;AACA,MAAI,KAAK,KAAK,EAAE,WAAW,GAAG;AAC5B,WAAO;AAAA,EACT;AACA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,cACJ,OAAO,oBAAoB,OAAO,eAAe,OAAO;AAC1D,QAAM,eAAe,OAAO;AAC5B,MAAI,CAAC,eAAe,CAAC,aAAc,QAAO;AAC1C,SAAO;AAAA,IACL,aAAa,eAAe;AAAA,IAC5B,cAAc,gBAAgB;AAAA,IAC9B,gBACE,OAAO,wBAAwB,OAAO,kBAAkB;AAAA,IAC1D,oBAAoB,OAAO,sBAAsB;AAAA,IACjD,wBAAwB,OAAO,0BAA0B;AAAA,IACzD,kBAAkB,OAAO,oBAAoB;AAAA,IAC7C,oBAAoB,OAAO,sBAAsB;AAAA,IACjD,oBAAoB,OAAO,sBAAsB;AAAA,IACjD,aAAa,OAAO,eAAe;AAAA,EACrC;AACF;AAEA,eAAe,iBAAiB,SAAmC;AACjE,QAAM;AAAA,IACJ,sBAAsB;AAAA,IACtB,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA;AAAA,IACnC,EAAE,MAAM,IAAM;AAAA,EAChB;AACF;AAEA,eAAe,cAAc,OAAmC;AAC9D,MAAI,CAAC,MAAM,eAAe,CAAC,MAAM,cAAc;AAC7C,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,iBAAiB;AAAA,IACrB,kBAAkB,MAAM;AAAA,IACxB,cAAc,MAAM;AAAA,IACpB,sBAAsB,MAAM;AAAA,IAC5B,oBAAoB,MAAM;AAAA,IAC1B,wBAAwB,MAAM;AAAA,IAC9B,kBAAkB,MAAM;AAAA,IACxB,oBAAoB,MAAM;AAAA,IAC1B,oBAAoB,MAAM;AAAA,IAC1B,aAAa,MAAM;AAAA,EACrB,CAAC;AACH;AAEA,eAAe,oBAAoB,aAAoC;AACrE,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AACA,QAAM,iBAAiB,EAAE,WAAW,YAAY,CAAC;AACnD;AAEA,eAAe,YAA2B;AACxC,QAAM,WAAW,sBAAsB;AACvC,MAAI;AACF,UAAM,GAAG,OAAO,QAAQ;AAAA,EAC1B,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,OAAM;AAAA,EAC9D;AACF;AAEO,IAAM,wBAA2C;AAAA,EACtD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,OAAO;AACT;AAMO,IAAM,kCAAN,cAA8C,MAAM;AAAA,EAChD,OAAO;AAAA,EAEhB,YAAY,QAAgB;AAC1B,UAAM,iCAAiC,MAAM,EAAE;AAC/C,SAAK,OAAO;AAAA,EACd;AACF;AAEA,IAAI,gBAA0C;AAC9C,IAAI,qBAAqB;AACzB,IAAI,kBAAmC;AAyBvC,eAAe,yBAAqD;AAClE,QAAM,YAAY,QAAQ,IAAI,iCAAiC,IAC5D,KAAK,EACL,YAAY;AACf,MAAI,aAAa,QAAQ;AACvB,WAAO;AAAA,EACT;AACA,MAAI,YAAY,aAAa,cAAc,aAAa,QAAQ;AAI9D,UAAM,IAAI;AAAA,MACR,gDAAgD,QAAQ;AAAA,IAC1D;AAAA,EACF;AAEA,QAAM,cACJ,aAAa,cAAe,MAAM,gCAAgC;AACpE,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,iCAAuB;AACnE,QAAM,WAAW,MAAM,mBAAmB;AAC1C,MAAI,SAAS,WAAW;AACtB,WAAO,SAAS;AAAA,EAClB;AAKA,SAAO;AACT;AAEA,eAAe,kCAAoD;AACjE,MAAI;AAIF,UAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,8BAAoB;AAC9D,UAAM,SAAS,MAAM,iBAAiB;AACtC,WAAO,OAAO,sBAAsB;AAAA,EACtC,QAAQ;AAIN,WAAO;AAAA,EACT;AACF;AAYA,eAAsB,uBAAmD;AACvE,MAAI,kBAAkB,MAAM;AAC1B,oBAAgB,MAAM,gBAAgB;AAMtC,QAAI,CAAC,sBAAsB,cAAc,SAAS,QAAQ;AACxD,YAAM,+BAA+B,aAAa;AAAA,IACpD;AACA,yBAAqB;AAAA,EACvB;AACA,SAAO;AACT;AAEA,eAAe,+BACb,QACA,UAAoC,CAAC,GACtB;AACf,MAAI;AACF,UAAM,CAAC,QAAQ,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC3C,sBAAsB,KAAK;AAAA,MAC3B,OAAO,KAAK;AAAA,IACd,CAAC;AACD,QAAI,CAAC,OAAQ;AACb,QAAI,UAAU;AAGZ,YAAM,sBAAsB,MAAM;AAClC;AAAA,IACF;AACA,QAAI,OAAO,eAAe,OAAO,cAAc;AAC7C,YAAM,WAAwB;AAAA,QAC5B,aAAa,OAAO;AAAA,QACpB,cAAc,OAAO;AAAA,QACrB,gBAAgB,OAAO;AAAA,QACvB,oBAAoB,OAAO;AAAA,QAC3B,wBAAwB,OAAO;AAAA,QAC/B,kBAAkB,OAAO;AAAA,QACzB,oBAAoB,OAAO;AAAA,QAC3B,oBAAoB,OAAO;AAAA,QAC3B,aAAa,OAAO;AAAA,MACtB;AACA,YAAM,OAAO,UAAU,QAAQ;AAC/B,YAAM,sBAAsB,QAAQ,QAAQ;AAAA,IAC9C,WAAW,OAAO,aAAa;AAC7B,YAAM,OAAO,gBAAgB,OAAO,WAAW;AAC/C,YAAM,WAAW,MAAM,OAAO,KAAK;AACnC,UAAI,UAAU,gBAAgB,OAAO,aAAa;AAChD,cAAM,IAAI,MAAM,2CAA2C;AAAA,MAC7D;AAAA,IACF,OAAO;AACL;AAAA,IACF;AACA,UAAM,sBAAsB,MAAM;AAAA,EACpC,SAAS,OAAO;AACd,QAAI,QAAQ,YAAY;AACtB,YAAM,IAAI;AAAA,QACR,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MACvD;AAAA,IACF;AAAA,EAKF;AACF;AAEA,eAAe,sBACb,QACA,UACe;AACf,QAAM,WAAW,MAAM,OAAO,KAAK;AACnC,MACE,UAAU,gBAAgB,SAAS,eACnC,SAAS,iBAAiB,SAAS,cACnC;AACA,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACF;AAQA,eAAsB,mBAA0D;AAC9E,MAAI,QAAQ,IAAI,wBAAwB,KAAK,GAAG;AAC9C,WAAO;AAAA,EACT;AACA,QAAM,UAAU,MAAM,qBAAqB;AAC3C,SAAO,QAAQ,KAAK;AACtB;AAmDA,eAAsB,mBACpB,IACA,SACY;AACZ,SAAO;AAAA,IACL,sBAAsB;AAAA,IACtB,YAAY;AACV,YAAM,UAAU,MAAM,qBAAqB;AAC3C,YAAM,MAAyB;AAAA,QAC7B,aAAa,QAAQ;AAAA,QACrB,MAAM,MAAM,QAAQ,KAAK;AAAA,QACzB,WAAW,CAAC,UAAU,QAAQ,UAAU,KAAK;AAAA,QAC7C,iBAAiB,CAAC,gBAAgB,QAAQ,gBAAgB,WAAW;AAAA,QACrE,OAAO,MAAM,QAAQ,MAAM;AAAA,MAC7B;AACA,aAAO,GAAG,GAAG;AAAA,IACf;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  toDreamboardApiError
4
- } from "./chunk-G2ECODRB.mjs";
4
+ } from "./chunk-PLXXH5LY.mjs";
5
5
  import {
6
6
  createGameRevision,
7
7
  createProjectSourceBlobUploadSession,
@@ -13,7 +13,7 @@ import {
13
13
  listProjectCompiledResults,
14
14
  queueProjectRevisionCompile,
15
15
  uploadProjectInitialProjection
16
- } from "./chunk-H3XNWKJU.mjs";
16
+ } from "./chunk-7LFDFXLS.mjs";
17
17
  import {
18
18
  external_exports
19
19
  } from "./chunk-JZTH3EMV.mjs";
@@ -615,4 +615,4 @@ export {
615
615
  uploadInitialProjectionSdk,
616
616
  uploadProjectSourceBlobsSdk
617
617
  };
618
- //# sourceMappingURL=chunk-A67WUYN2.mjs.map
618
+ //# sourceMappingURL=chunk-B42OHJNY.mjs.map
@@ -4,7 +4,7 @@ import {
4
4
  readWorkspaceTextFileIfExists,
5
5
  validateGeneratedArtifacts,
6
6
  writeWorkspaceTextFile
7
- } from "./chunk-WAFBU5U7.mjs";
7
+ } from "./chunk-OJFZVGEL.mjs";
8
8
 
9
9
  // src/services/project/workspace-codegen.ts
10
10
  var STARTER_UI_SEED_FILES = /* @__PURE__ */ new Set([
@@ -24,7 +24,7 @@ function isFrameworkOwnedSetupProfilesSeed(content) {
24
24
  async function applyWorkspaceCodegen(options) {
25
25
  const { projectRoot, manifest } = options;
26
26
  const { adapter } = await loadProjectAuthoringAdapter(projectRoot);
27
- const artifacts = validateGeneratedArtifacts([
27
+ const artifacts = validateGeneratedArtifacts(adapter, [
28
28
  ...adapter.generateWorkspaceArtifacts(manifest),
29
29
  ...adapter.generateTestArtifacts({ manifest })
30
30
  ]);
@@ -86,4 +86,4 @@ async function applyWorkspaceCodegen(options) {
86
86
  export {
87
87
  applyWorkspaceCodegen
88
88
  };
89
- //# sourceMappingURL=chunk-V6AQDR7W.mjs.map
89
+ //# sourceMappingURL=chunk-HUBV22JQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/project/workspace-codegen.ts"],"sourcesContent":["import type { GameTopologyManifest } from \"@dreamboard-games/sdk/types\";\nimport { loadProjectAuthoringAdapter } from \"../project-authoring/loader.js\";\nimport { validateGeneratedArtifacts } from \"../project-authoring/validation.js\";\nimport {\n readWorkspaceTextFileIfExists,\n writeWorkspaceTextFile,\n} from \"./workspace-path.js\";\n\nexport interface WorkspaceCodegenWriteResult {\n written: string[];\n skipped: string[];\n merged: string[];\n}\n\nconst STARTER_UI_SEED_FILES = new Set([\n \"ui/interaction-routes.tsx\",\n \"ui/setup-screen.tsx\",\n \"ui/styles.ts\",\n \"ui/ui-contract-typing-smoke.tsx\",\n]);\nconst SETUP_PROFILES_SEED_MARKER = \"Dreamboard generated setup profile seeds.\";\n\nfunction isFrameworkOwnedSetupProfilesSeed(\n content: string | null | undefined,\n): boolean {\n if (content === null || content === undefined) {\n return false;\n }\n const trimmed = content.trim();\n return trimmed.length === 0 || trimmed.includes(SETUP_PROFILES_SEED_MARKER);\n}\n\nexport async function applyWorkspaceCodegen(options: {\n projectRoot: string;\n manifest: GameTopologyManifest;\n}): Promise<WorkspaceCodegenWriteResult> {\n const { projectRoot, manifest } = options;\n const { adapter } = await loadProjectAuthoringAdapter(projectRoot);\n const artifacts = validateGeneratedArtifacts(adapter, [\n ...adapter.generateWorkspaceArtifacts(manifest),\n ...adapter.generateTestArtifacts({ manifest }),\n ]);\n const authoritativeFiles = new Map(\n artifacts\n .filter((artifact) => artifact.ownership !== \"seed\")\n .map((artifact) => [artifact.path, artifact.content]),\n );\n const seedFiles = new Map(\n artifacts\n .filter((artifact) => artifact.ownership === \"seed\")\n .map((artifact) => [artifact.path, artifact.content]),\n );\n\n const written: string[] = [];\n const skipped: string[] = [];\n const merged: string[] = [];\n const existingUiAppBeforeSeeds = await readWorkspaceTextFileIfExists(\n projectRoot,\n \"ui/App.tsx\",\n );\n const shouldWriteStarterUiSeedFiles =\n existingUiAppBeforeSeeds === null ||\n existingUiAppBeforeSeeds.trim().length === 0;\n\n for (const [relativePath, content] of authoritativeFiles) {\n const existingContent = await readWorkspaceTextFileIfExists(\n projectRoot,\n relativePath,\n );\n await writeWorkspaceTextFile(projectRoot, relativePath, content);\n if (existingContent !== content) {\n written.push(relativePath);\n }\n }\n\n for (const [relativePath, content] of seedFiles) {\n const existingContent = await readWorkspaceTextFileIfExists(\n projectRoot,\n relativePath,\n );\n if (\n STARTER_UI_SEED_FILES.has(relativePath) &&\n !shouldWriteStarterUiSeedFiles &&\n existingContent === null\n ) {\n skipped.push(relativePath);\n continue;\n }\n\n const shouldRefreshFrameworkSeed =\n relativePath === \"app/setup-profiles.ts\" &&\n isFrameworkOwnedSetupProfilesSeed(existingContent);\n\n if (shouldRefreshFrameworkSeed) {\n await writeWorkspaceTextFile(projectRoot, relativePath, content);\n if (existingContent !== content) {\n written.push(relativePath);\n }\n continue;\n }\n\n const hasExistingContent =\n existingContent !== null && existingContent.trim().length > 0;\n if (hasExistingContent) {\n skipped.push(relativePath);\n continue;\n }\n\n await writeWorkspaceTextFile(projectRoot, relativePath, content);\n written.push(relativePath);\n }\n\n written.sort();\n skipped.sort();\n merged.sort();\n return { written, skipped, merged };\n}\n"],"mappings":";;;;;;;;;AAcA,IAAM,wBAAwB,oBAAI,IAAI;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AACD,IAAM,6BAA6B;AAEnC,SAAS,kCACP,SACS;AACT,MAAI,YAAY,QAAQ,YAAY,QAAW;AAC7C,WAAO;AAAA,EACT;AACA,QAAM,UAAU,QAAQ,KAAK;AAC7B,SAAO,QAAQ,WAAW,KAAK,QAAQ,SAAS,0BAA0B;AAC5E;AAEA,eAAsB,sBAAsB,SAGH;AACvC,QAAM,EAAE,aAAa,SAAS,IAAI;AAClC,QAAM,EAAE,QAAQ,IAAI,MAAM,4BAA4B,WAAW;AACjE,QAAM,YAAY,2BAA2B,SAAS;AAAA,IACpD,GAAG,QAAQ,2BAA2B,QAAQ;AAAA,IAC9C,GAAG,QAAQ,sBAAsB,EAAE,SAAS,CAAC;AAAA,EAC/C,CAAC;AACD,QAAM,qBAAqB,IAAI;AAAA,IAC7B,UACG,OAAO,CAAC,aAAa,SAAS,cAAc,MAAM,EAClD,IAAI,CAAC,aAAa,CAAC,SAAS,MAAM,SAAS,OAAO,CAAC;AAAA,EACxD;AACA,QAAM,YAAY,IAAI;AAAA,IACpB,UACG,OAAO,CAAC,aAAa,SAAS,cAAc,MAAM,EAClD,IAAI,CAAC,aAAa,CAAC,SAAS,MAAM,SAAS,OAAO,CAAC;AAAA,EACxD;AAEA,QAAM,UAAoB,CAAC;AAC3B,QAAM,UAAoB,CAAC;AAC3B,QAAM,SAAmB,CAAC;AAC1B,QAAM,2BAA2B,MAAM;AAAA,IACrC;AAAA,IACA;AAAA,EACF;AACA,QAAM,gCACJ,6BAA6B,QAC7B,yBAAyB,KAAK,EAAE,WAAW;AAE7C,aAAW,CAAC,cAAc,OAAO,KAAK,oBAAoB;AACxD,UAAM,kBAAkB,MAAM;AAAA,MAC5B;AAAA,MACA;AAAA,IACF;AACA,UAAM,uBAAuB,aAAa,cAAc,OAAO;AAC/D,QAAI,oBAAoB,SAAS;AAC/B,cAAQ,KAAK,YAAY;AAAA,IAC3B;AAAA,EACF;AAEA,aAAW,CAAC,cAAc,OAAO,KAAK,WAAW;AAC/C,UAAM,kBAAkB,MAAM;AAAA,MAC5B;AAAA,MACA;AAAA,IACF;AACA,QACE,sBAAsB,IAAI,YAAY,KACtC,CAAC,iCACD,oBAAoB,MACpB;AACA,cAAQ,KAAK,YAAY;AACzB;AAAA,IACF;AAEA,UAAM,6BACJ,iBAAiB,2BACjB,kCAAkC,eAAe;AAEnD,QAAI,4BAA4B;AAC9B,YAAM,uBAAuB,aAAa,cAAc,OAAO;AAC/D,UAAI,oBAAoB,SAAS;AAC/B,gBAAQ,KAAK,YAAY;AAAA,MAC3B;AACA;AAAA,IACF;AAEA,UAAM,qBACJ,oBAAoB,QAAQ,gBAAgB,KAAK,EAAE,SAAS;AAC9D,QAAI,oBAAoB;AACtB,cAAQ,KAAK,YAAY;AACzB;AAAA,IACF;AAEA,UAAM,uBAAuB,aAAa,cAAc,OAAO;AAC/D,YAAQ,KAAK,YAAY;AAAA,EAC3B;AAEA,UAAQ,KAAK;AACb,UAAQ,KAAK;AACb,SAAO,KAAK;AACZ,SAAO,EAAE,SAAS,SAAS,OAAO;AACpC;","names":[]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  ensureProjectSdk,
4
4
  loadRemoteProjectIdentity
5
- } from "./chunk-A67WUYN2.mjs";
5
+ } from "./chunk-B42OHJNY.mjs";
6
6
  import {
7
7
  updateProjectState
8
8
  } from "./chunk-WSIYUUSD.mjs";
@@ -36,4 +36,4 @@ async function resolveRemoteProject(options) {
36
36
  export {
37
37
  resolveRemoteProject
38
38
  };
39
- //# sourceMappingURL=chunk-5GCZZ6NW.mjs.map
39
+ //# sourceMappingURL=chunk-JB7VXCMB.mjs.map
@@ -28,8 +28,19 @@ function isValidGeneratedPath(relativePath) {
28
28
  }
29
29
  return !relativePath.split("/").some((segment) => segment.length === 0 || segment === "." || segment === "..");
30
30
  }
31
+ var ALLOWED_GENERATED_PREFIXES = [
32
+ "app/",
33
+ "shared/",
34
+ "test/generated/",
35
+ "ui/"
36
+ ];
37
+ function isAllowedGeneratedPath(relativePath) {
38
+ return ALLOWED_GENERATED_PREFIXES.some(
39
+ (prefix) => relativePath.startsWith(prefix)
40
+ );
41
+ }
31
42
  function assertGeneratedPath(pathValue, label) {
32
- if (typeof pathValue !== "string" || !isValidGeneratedPath(pathValue)) {
43
+ if (typeof pathValue !== "string" || !isValidGeneratedPath(pathValue) || !isAllowedGeneratedPath(pathValue)) {
33
44
  throw new ProjectAuthoringError(
34
45
  "GENERATED_PATH_CONTRACT_INVALID",
35
46
  `${label} must be a normalized relative workspace path.`
@@ -131,9 +142,23 @@ function validateProjectAuthoringAdapter(adapter) {
131
142
  }
132
143
  seen.add(normalized);
133
144
  }
145
+ if (adapter.generatedPathPatterns !== void 0 && !Array.isArray(adapter.generatedPathPatterns)) {
146
+ throw new ProjectAuthoringError(
147
+ "GENERATED_PATH_CONTRACT_INVALID",
148
+ "SDK authoring adapter generatedPathPatterns must be an array."
149
+ );
150
+ }
151
+ for (const [index, pattern] of (adapter.generatedPathPatterns ?? []).entries()) {
152
+ if (!isRecord(pattern) || typeof pattern.prefix !== "string" || typeof pattern.suffix !== "string" || !isValidGeneratedPath(`${pattern.prefix}placeholder${pattern.suffix}`) || !isAllowedGeneratedPath(`${pattern.prefix}placeholder${pattern.suffix}`)) {
153
+ throw new ProjectAuthoringError(
154
+ "GENERATED_PATH_CONTRACT_INVALID",
155
+ `generatedPathPatterns[${index}] must describe a normalized allowlisted workspace path.`
156
+ );
157
+ }
158
+ }
134
159
  return adapter;
135
160
  }
136
- function validateGeneratedArtifacts(artifacts) {
161
+ function validateGeneratedArtifacts(adapter, artifacts) {
137
162
  const seen = /* @__PURE__ */ new Set();
138
163
  const validated = artifacts.map(assertGeneratedArtifact);
139
164
  for (const artifact of validated) {
@@ -143,6 +168,15 @@ function validateGeneratedArtifacts(artifacts) {
143
168
  `Generated artifact path '${artifact.path}' was emitted more than once.`
144
169
  );
145
170
  }
171
+ const declared = adapter.generatedPaths.includes(artifact.path) || (adapter.generatedPathPatterns ?? []).some(
172
+ (pattern) => artifact.path.startsWith(pattern.prefix) && artifact.path.endsWith(pattern.suffix) && artifact.path.length > pattern.prefix.length + pattern.suffix.length
173
+ );
174
+ if (!declared) {
175
+ throw new ProjectAuthoringError(
176
+ "GENERATED_PATH_CONTRACT_INVALID",
177
+ `Generated artifact path '${artifact.path}' is not declared by the SDK authoring adapter.`
178
+ );
179
+ }
146
180
  seen.add(artifact.path);
147
181
  }
148
182
  return validated;
@@ -176,12 +210,6 @@ function assertResolvedInsidePackage(options) {
176
210
  );
177
211
  }
178
212
  }
179
- function isAuthoringMetadataVersionCompatible(options) {
180
- if (options.metadataVersion === options.packageVersion) {
181
- return true;
182
- }
183
- return options.packageVersion.startsWith(`${options.metadataVersion}-local.`);
184
- }
185
213
  function isRecord2(value) {
186
214
  return typeof value === "object" && value !== null && !Array.isArray(value);
187
215
  }
@@ -286,10 +314,7 @@ async function loadProjectAuthoringAdapter(projectRoot) {
286
314
  const adapter = validateProjectAuthoringAdapter(
287
315
  moduleRecord.projectAuthoringAdapter ?? moduleRecord.default
288
316
  );
289
- if (!isAuthoringMetadataVersionCompatible({
290
- metadataVersion: adapter.metadata.sdkVersion,
291
- packageVersion: packageJson.version
292
- })) {
317
+ if (adapter.metadata.sdkVersion !== packageJson.version) {
293
318
  throw new ProjectAuthoringError(
294
319
  "SDK_METADATA_MISMATCH",
295
320
  `SDK authoring adapter reports version ${adapter.metadata.sdkVersion}, but package metadata is ${packageJson.version}.`
@@ -464,4 +489,4 @@ export {
464
489
  unlinkWorkspaceFile,
465
490
  removeWorkspacePath
466
491
  };
467
- //# sourceMappingURL=chunk-WAFBU5U7.mjs.map
492
+ //# sourceMappingURL=chunk-OJFZVGEL.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/project-authoring/validation.ts","../../src/services/project-authoring/contract.ts","../../src/services/project-authoring/loader.ts","../../src/services/project/workspace-path.ts"],"sourcesContent":["import path from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport {\n ProjectAuthoringError,\n type GeneratedArtifactV1,\n type ProjectAuthoringAdapterV1,\n} from \"./contract.js\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isValidGeneratedPath(relativePath: string): boolean {\n if (\n relativePath.length === 0 ||\n relativePath.startsWith(\"/\") ||\n relativePath.includes(\"\\\\\")\n ) {\n return false;\n }\n const normalized = path.posix.normalize(relativePath);\n if (normalized !== relativePath) {\n return false;\n }\n return !relativePath\n .split(\"/\")\n .some((segment) => segment.length === 0 || segment === \".\" || segment === \"..\");\n}\n\nconst ALLOWED_GENERATED_PREFIXES = [\n \"app/\",\n \"shared/\",\n \"test/generated/\",\n \"ui/\",\n] as const;\n\nfunction isAllowedGeneratedPath(relativePath: string): boolean {\n return ALLOWED_GENERATED_PREFIXES.some((prefix) =>\n relativePath.startsWith(prefix),\n );\n}\n\nfunction assertGeneratedPath(pathValue: unknown, label: string): string {\n if (\n typeof pathValue !== \"string\" ||\n !isValidGeneratedPath(pathValue) ||\n !isAllowedGeneratedPath(pathValue)\n ) {\n throw new ProjectAuthoringError(\n \"GENERATED_PATH_CONTRACT_INVALID\",\n `${label} must be a normalized relative workspace path.`,\n );\n }\n return pathValue;\n}\n\nfunction assertGeneratedArtifact(value: unknown): GeneratedArtifactV1 {\n if (!isRecord(value)) {\n throw new ProjectAuthoringError(\n \"GENERATED_PATH_CONTRACT_INVALID\",\n \"Generated artifact must be an object.\",\n );\n }\n const artifactPath = assertGeneratedPath(value.path, \"Generated artifact path\");\n if (\n value.ownership !== \"authoritative\" &&\n value.ownership !== \"seed\" &&\n value.ownership !== \"derived-test\"\n ) {\n throw new ProjectAuthoringError(\n \"GENERATED_PATH_CONTRACT_INVALID\",\n `Generated artifact '${artifactPath}' has invalid ownership.`,\n );\n }\n if (typeof value.content !== \"string\") {\n throw new ProjectAuthoringError(\n \"GENERATED_PATH_CONTRACT_INVALID\",\n `Generated artifact '${artifactPath}' content must be a string.`,\n );\n }\n const expectedHash = createHash(\"sha256\")\n .update(value.content)\n .digest(\"hex\");\n if (value.contentSha256 !== expectedHash) {\n throw new ProjectAuthoringError(\n \"GENERATED_PATH_CONTRACT_INVALID\",\n `Generated artifact '${artifactPath}' has a stale content hash.`,\n );\n }\n return value as GeneratedArtifactV1;\n}\n\nexport function validateProjectAuthoringAdapter(\n adapter: unknown,\n): ProjectAuthoringAdapterV1 {\n if (!isRecord(adapter)) {\n throw new ProjectAuthoringError(\n \"AUTHORING_PROTOCOL_UNSUPPORTED\",\n \"SDK authoring export did not provide an adapter object.\",\n );\n }\n if (adapter.protocolVersion !== 1) {\n throw new ProjectAuthoringError(\n \"AUTHORING_PROTOCOL_UNSUPPORTED\",\n `Unsupported SDK authoring protocol '${String(adapter.protocolVersion)}'.`,\n );\n }\n if (!isRecord(adapter.metadata)) {\n throw new ProjectAuthoringError(\n \"AUTHORING_PROTOCOL_UNSUPPORTED\",\n \"SDK authoring adapter metadata is missing.\",\n );\n }\n for (const key of [\n \"sdkVersion\",\n \"codegenVersion\",\n \"manifestSchemaVersion\",\n \"generatedArtifactSchemaVersion\",\n ] as const) {\n if (\n adapter.metadata[key] === undefined ||\n (key.endsWith(\"Version\") && typeof adapter.metadata[key] !== \"string\" && typeof adapter.metadata[key] !== \"number\")\n ) {\n throw new ProjectAuthoringError(\n \"AUTHORING_PROTOCOL_UNSUPPORTED\",\n `SDK authoring adapter metadata '${key}' is missing.`,\n );\n }\n }\n for (const method of [\n \"validateManifest\",\n \"materializeManifest\",\n \"generateWorkspaceArtifacts\",\n \"generateTestArtifacts\",\n ] as const) {\n if (typeof adapter[method] !== \"function\") {\n throw new ProjectAuthoringError(\n \"AUTHORING_PROTOCOL_UNSUPPORTED\",\n `SDK authoring adapter method '${method}' is missing.`,\n );\n }\n }\n if (!Array.isArray(adapter.generatedPaths)) {\n throw new ProjectAuthoringError(\n \"GENERATED_PATH_CONTRACT_INVALID\",\n \"SDK authoring adapter generatedPaths must be an array.\",\n );\n }\n const seen = new Set<string>();\n for (const [index, generatedPath] of adapter.generatedPaths.entries()) {\n const normalized = assertGeneratedPath(\n generatedPath,\n `generatedPaths[${index}]`,\n );\n if (seen.has(normalized)) {\n throw new ProjectAuthoringError(\n \"GENERATED_PATH_CONTRACT_INVALID\",\n `Generated path '${normalized}' is declared more than once.`,\n );\n }\n seen.add(normalized);\n }\n if (\n adapter.generatedPathPatterns !== undefined &&\n !Array.isArray(adapter.generatedPathPatterns)\n ) {\n throw new ProjectAuthoringError(\n \"GENERATED_PATH_CONTRACT_INVALID\",\n \"SDK authoring adapter generatedPathPatterns must be an array.\",\n );\n }\n for (const [index, pattern] of (\n adapter.generatedPathPatterns ?? []\n ).entries()) {\n if (\n !isRecord(pattern) ||\n typeof pattern.prefix !== \"string\" ||\n typeof pattern.suffix !== \"string\" ||\n !isValidGeneratedPath(`${pattern.prefix}placeholder${pattern.suffix}`) ||\n !isAllowedGeneratedPath(`${pattern.prefix}placeholder${pattern.suffix}`)\n ) {\n throw new ProjectAuthoringError(\n \"GENERATED_PATH_CONTRACT_INVALID\",\n `generatedPathPatterns[${index}] must describe a normalized allowlisted workspace path.`,\n );\n }\n }\n return adapter as ProjectAuthoringAdapterV1;\n}\n\nexport function validateGeneratedArtifacts(\n adapter: ProjectAuthoringAdapterV1,\n artifacts: readonly unknown[],\n): readonly GeneratedArtifactV1[] {\n const seen = new Set<string>();\n const validated = artifacts.map(assertGeneratedArtifact);\n for (const artifact of validated) {\n if (seen.has(artifact.path)) {\n throw new ProjectAuthoringError(\n \"GENERATED_PATH_CONTRACT_INVALID\",\n `Generated artifact path '${artifact.path}' was emitted more than once.`,\n );\n }\n const declared =\n adapter.generatedPaths.includes(artifact.path) ||\n (adapter.generatedPathPatterns ?? []).some(\n (pattern) =>\n artifact.path.startsWith(pattern.prefix) &&\n artifact.path.endsWith(pattern.suffix) &&\n artifact.path.length > pattern.prefix.length + pattern.suffix.length,\n );\n if (!declared) {\n throw new ProjectAuthoringError(\n \"GENERATED_PATH_CONTRACT_INVALID\",\n `Generated artifact path '${artifact.path}' is not declared by the SDK authoring adapter.`,\n );\n }\n seen.add(artifact.path);\n }\n return validated;\n}\n","export type ProjectAuthoringProblemCode =\n | \"SDK_NOT_INSTALLED\"\n | \"AUTHORING_ADAPTER_NOT_EXPORTED\"\n | \"AUTHORING_PROTOCOL_UNSUPPORTED\"\n | \"SDK_METADATA_MISMATCH\"\n | \"GENERATED_PATH_CONTRACT_INVALID\";\n\nexport class ProjectAuthoringError extends Error {\n readonly code: ProjectAuthoringProblemCode;\n\n constructor(code: ProjectAuthoringProblemCode, message: string) {\n super(message);\n this.name = \"ProjectAuthoringError\";\n this.code = code;\n }\n}\n\nexport type GeneratedAuthoringMetadataV1 = {\n sdkVersion: string;\n codegenVersion: string;\n manifestSchemaVersion: number;\n generatedArtifactSchemaVersion: number;\n};\n\nexport type AuthoringValidationResultV1 = {\n valid: boolean;\n errors: readonly string[];\n warnings: readonly string[];\n};\n\nexport type GeneratedArtifactV1 = {\n path: string;\n ownership: \"authoritative\" | \"seed\" | \"derived-test\";\n content: string;\n contentSha256: string;\n};\n\nexport type GeneratedPathPatternV1 = {\n prefix: string;\n suffix: string;\n};\n\nexport type AuthoringManifestConformanceCaseV1 = {\n id: string;\n manifest: unknown;\n expected:\n | {\n valid: true;\n transportValid: true;\n materializedSha256: string;\n }\n | {\n valid: false;\n transportValid: boolean;\n diagnosticCodes: readonly string[];\n };\n};\n\nexport type ProjectAuthoringAdapterV1 = {\n protocolVersion: 1;\n metadata: GeneratedAuthoringMetadataV1;\n generatedPaths: readonly string[];\n generatedPathPatterns?: readonly GeneratedPathPatternV1[];\n manifestConformanceCases: readonly AuthoringManifestConformanceCaseV1[];\n validateManifest(manifest: unknown): AuthoringValidationResultV1;\n materializeManifest(manifest: unknown): unknown;\n generateWorkspaceArtifacts(manifest: unknown): readonly GeneratedArtifactV1[];\n generateTestArtifacts(input: {\n manifest: unknown;\n }): readonly GeneratedArtifactV1[];\n};\n\nexport type LoadedProjectAuthoringAdapterV1 = {\n packageRoot: string;\n packageVersion: string;\n adapterPath: string;\n adapter: ProjectAuthoringAdapterV1;\n};\n","import { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport {\n ProjectAuthoringError,\n type LoadedProjectAuthoringAdapterV1,\n} from \"./contract.js\";\nimport { validateProjectAuthoringAdapter } from \"./validation.js\";\n\ntype PackageJson = {\n name?: string;\n version?: string;\n exports?: unknown;\n};\n\ntype ResolvedProjectAuthoringAdapter = {\n packageJsonPath: string;\n adapterPath: string;\n};\n\nconst PROJECT_SDK_RESOLUTION_RETRY_DELAYS_MS = [50, 150, 300] as const;\n\nfunction problemFromResolveError(error: unknown): ProjectAuthoringError {\n if (error instanceof ProjectAuthoringError) {\n return error;\n }\n const code =\n (error as NodeJS.ErrnoException | undefined)?.code ===\n \"ERR_PACKAGE_PATH_NOT_EXPORTED\"\n ? \"AUTHORING_ADAPTER_NOT_EXPORTED\"\n : \"SDK_NOT_INSTALLED\";\n return new ProjectAuthoringError(\n code,\n code === \"AUTHORING_ADAPTER_NOT_EXPORTED\"\n ? \"Installed @dreamboard-games/sdk does not export @dreamboard-games/sdk/authoring.\"\n : \"Install @dreamboard-games/sdk in this workspace before running authoring commands.\",\n );\n}\n\nfunction assertResolvedInsidePackage(options: {\n packageRoot: string;\n resolvedPath: string;\n label: string;\n}): void {\n const packageRoot = path.resolve(options.packageRoot);\n const resolvedPath = path.resolve(options.resolvedPath);\n const relative = path.relative(packageRoot, resolvedPath);\n if (relative.startsWith(\"..\") || path.isAbsolute(relative)) {\n throw new ProjectAuthoringError(\n \"AUTHORING_ADAPTER_NOT_EXPORTED\",\n `${options.label} resolved outside the installed @dreamboard-games/sdk package.`,\n );\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction resolveExportTarget(value: unknown): string | null {\n if (typeof value === \"string\") {\n return value;\n }\n if (!isRecord(value)) {\n return null;\n }\n for (const condition of [\"import\", \"default\"]) {\n const resolved = resolveExportTarget(value[condition]);\n if (resolved) {\n return resolved;\n }\n }\n return null;\n}\n\nasync function resolveDirectProjectAuthoringAdapter(\n projectRoot: string,\n): Promise<ResolvedProjectAuthoringAdapter | null> {\n const packageRoot = path.join(\n projectRoot,\n \"node_modules\",\n \"@dreamboard-games\",\n \"sdk\",\n );\n const packageJsonPath = path.join(packageRoot, \"package.json\");\n let packageJson: PackageJson;\n try {\n packageJson = JSON.parse(await readFile(packageJsonPath, \"utf8\")) as PackageJson;\n } catch {\n return null;\n }\n const authoringExport = isRecord(packageJson.exports)\n ? resolveExportTarget(packageJson.exports[\"./authoring\"])\n : null;\n if (!authoringExport) {\n throw new ProjectAuthoringError(\n \"AUTHORING_ADAPTER_NOT_EXPORTED\",\n \"Installed @dreamboard-games/sdk does not export @dreamboard-games/sdk/authoring.\",\n );\n }\n const adapterPath = path.resolve(packageRoot, authoringExport);\n assertResolvedInsidePackage({\n packageRoot,\n resolvedPath: adapterPath,\n label: \"@dreamboard-games/sdk/authoring\",\n });\n return { packageJsonPath, adapterPath };\n}\n\nasync function resolveProjectAuthoringAdapter(\n projectRoot: string,\n): Promise<ResolvedProjectAuthoringAdapter> {\n const requireFromProject = createRequire(path.join(projectRoot, \"package.json\"));\n let lastError: unknown;\n\n for (\n let attempt = 0;\n attempt <= PROJECT_SDK_RESOLUTION_RETRY_DELAYS_MS.length;\n attempt += 1\n ) {\n try {\n const packageJsonPath = requireFromProject.resolve(\n \"@dreamboard-games/sdk/package.json\",\n );\n const adapterPath = requireFromProject.resolve(\n \"@dreamboard-games/sdk/authoring\",\n );\n return { packageJsonPath, adapterPath };\n } catch (error) {\n lastError = error;\n if (\n (error as NodeJS.ErrnoException | undefined)?.code ===\n \"ERR_PACKAGE_PATH_NOT_EXPORTED\"\n ) {\n throw error;\n }\n const directResolved =\n await resolveDirectProjectAuthoringAdapter(projectRoot);\n if (directResolved) {\n return directResolved;\n }\n const retryDelay = PROJECT_SDK_RESOLUTION_RETRY_DELAYS_MS[attempt];\n if (retryDelay === undefined) {\n break;\n }\n await delay(retryDelay);\n }\n }\n\n throw lastError;\n}\n\nexport async function loadProjectAuthoringAdapter(\n projectRoot: string,\n): Promise<LoadedProjectAuthoringAdapterV1> {\n let packageJsonPath: string;\n let adapterPath: string;\n try {\n ({ packageJsonPath, adapterPath } =\n await resolveProjectAuthoringAdapter(projectRoot));\n } catch (error) {\n throw problemFromResolveError(error);\n }\n\n const packageRoot = path.dirname(packageJsonPath);\n assertResolvedInsidePackage({\n packageRoot,\n resolvedPath: adapterPath,\n label: \"@dreamboard-games/sdk/authoring\",\n });\n\n const packageJson = JSON.parse(\n await readFile(packageJsonPath, \"utf8\"),\n ) as PackageJson;\n if (\n packageJson.name !== \"@dreamboard-games/sdk\" ||\n typeof packageJson.version !== \"string\" ||\n packageJson.version.trim().length === 0\n ) {\n throw new ProjectAuthoringError(\n \"SDK_METADATA_MISMATCH\",\n \"Installed SDK package metadata is invalid.\",\n );\n }\n\n const moduleRecord = (await import(pathToFileURL(adapterPath).href)) as {\n projectAuthoringAdapter?: unknown;\n default?: unknown;\n };\n const adapter = validateProjectAuthoringAdapter(\n moduleRecord.projectAuthoringAdapter ?? moduleRecord.default,\n );\n if (adapter.metadata.sdkVersion !== packageJson.version) {\n throw new ProjectAuthoringError(\n \"SDK_METADATA_MISMATCH\",\n `SDK authoring adapter reports version ${adapter.metadata.sdkVersion}, but package metadata is ${packageJson.version}.`,\n );\n }\n\n return {\n packageRoot,\n packageVersion: packageJson.version,\n adapterPath,\n adapter,\n };\n}\n","import {\n mkdir,\n readFile,\n realpath,\n rm,\n stat,\n unlink,\n writeFile,\n} from \"node:fs/promises\";\nimport path from \"node:path\";\n\nconst CONTROL_CHARS = /[\\x00-\\x1f\\x7f]/;\nconst URL_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*:/;\nconst WINDOWS_DEVICE_NAME = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\\..*)?$/i;\nconst ENCODED_SEPARATOR = /%(?:2f|5c)/i;\n\nfunction isWindowsDeviceSegment(segment: string): boolean {\n return WINDOWS_DEVICE_NAME.test(segment.replace(/[. ]+$/g, \"\"));\n}\n\nfunction isPathInside(parent: string, candidate: string): boolean {\n const relative = path.relative(parent, candidate);\n return (\n relative === \"\" ||\n (!relative.startsWith(\"..\") && !path.isAbsolute(relative))\n );\n}\n\nfunction assertContained(\n parent: string,\n candidate: string,\n label: string,\n): void {\n if (!isPathInside(parent, candidate)) {\n throw new Error(`${label} escapes the workspace.`);\n }\n}\n\nfunction isMissingFileError(error: unknown): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n (error as { code?: unknown }).code === \"ENOENT\"\n );\n}\n\nexport function normalizeOwnedProjectPath(input: string): string | null {\n if (\n input.length === 0 ||\n input.trim().length === 0 ||\n input.startsWith(\"/\") ||\n input.includes(\"\\\\\") ||\n input.includes(\":\") ||\n URL_SCHEME.test(input) ||\n CONTROL_CHARS.test(input) ||\n ENCODED_SEPARATOR.test(input) ||\n path.win32.isAbsolute(input)\n ) {\n return null;\n }\n\n const segments = input.split(\"/\");\n if (\n segments.some(\n (segment) =>\n segment.length === 0 ||\n segment.trim().length === 0 ||\n segment === \".\" ||\n segment === \"..\" ||\n isWindowsDeviceSegment(segment),\n )\n ) {\n return null;\n }\n\n return segments.join(\"/\");\n}\n\nexport function resolveWorkspacePath(\n rootDir: string,\n projectPath: string,\n): string {\n const normalized = normalizeOwnedProjectPath(projectPath);\n if (normalized === null) {\n throw new Error(`Unsafe project path: ${projectPath}`);\n }\n\n const rootPath = path.resolve(rootDir);\n const resolvedPath = path.resolve(rootPath, normalized);\n assertContained(rootPath, resolvedPath, `Project path ${projectPath}`);\n return resolvedPath;\n}\n\nasync function realpathIfExists(filePath: string): Promise<string | null> {\n try {\n return await realpath(filePath);\n } catch (error) {\n if (isMissingFileError(error)) return null;\n throw error;\n }\n}\n\nasync function nearestExistingAncestor(filePath: string): Promise<string> {\n let current = filePath;\n while (true) {\n try {\n await stat(current);\n return current;\n } catch (error) {\n if (!isMissingFileError(error)) throw error;\n const parent = path.dirname(current);\n if (parent === current) throw error;\n current = parent;\n }\n }\n}\n\nasync function assertRealpathContained(\n rootDir: string,\n filePath: string,\n label: string,\n): Promise<void> {\n const rootRealpath = await realpath(rootDir);\n const targetRealpath = await realpath(filePath);\n assertContained(rootRealpath, targetRealpath, label);\n}\n\nasync function assertNearestParentContained(\n rootDir: string,\n filePath: string,\n label: string,\n): Promise<void> {\n const rootRealpath = await realpath(rootDir);\n const nearestParent = await nearestExistingAncestor(path.dirname(filePath));\n const nearestParentRealpath = await realpath(nearestParent);\n assertContained(rootRealpath, nearestParentRealpath, label);\n}\n\nasync function assertExistingTargetContained(\n rootDir: string,\n filePath: string,\n label: string,\n): Promise<void> {\n const targetRealpath = await realpathIfExists(filePath);\n if (targetRealpath === null) return;\n const rootRealpath = await realpath(rootDir);\n assertContained(rootRealpath, targetRealpath, label);\n}\n\nasync function prepareWorkspaceWriteTarget(\n rootDir: string,\n filePath: string,\n): Promise<void> {\n await assertNearestParentContained(rootDir, filePath, \"Project path\");\n await mkdir(path.dirname(filePath), { recursive: true });\n await assertRealpathContained(\n rootDir,\n path.dirname(filePath),\n \"Project path\",\n );\n await assertExistingTargetContained(rootDir, filePath, \"Project path\");\n}\n\nexport async function readWorkspaceTextFile(\n rootDir: string,\n projectPath: string,\n): Promise<string> {\n const filePath = resolveWorkspacePath(rootDir, projectPath);\n await assertExistingTargetContained(rootDir, filePath, \"Project path\");\n return readFile(filePath, \"utf8\");\n}\n\nexport async function readWorkspaceTextFileIfExists(\n rootDir: string,\n projectPath: string,\n): Promise<string | null> {\n const filePath = resolveWorkspacePath(rootDir, projectPath);\n const targetRealpath = await realpathIfExists(filePath);\n if (targetRealpath === null) return null;\n const rootRealpath = await realpath(rootDir);\n assertContained(rootRealpath, targetRealpath, \"Project path\");\n return readFile(filePath, \"utf8\");\n}\n\nexport async function writeWorkspaceTextFile(\n rootDir: string,\n projectPath: string,\n content: string,\n): Promise<void> {\n const filePath = resolveWorkspacePath(rootDir, projectPath);\n await prepareWorkspaceWriteTarget(rootDir, filePath);\n await writeFile(filePath, content, \"utf8\");\n}\n\nexport async function writeWorkspaceJsonFile(\n rootDir: string,\n projectPath: string,\n data: unknown,\n): Promise<void> {\n await writeWorkspaceTextFile(\n rootDir,\n projectPath,\n `${JSON.stringify(data, null, 2)}\\n`,\n );\n}\n\nexport async function workspacePathExists(\n rootDir: string,\n projectPath: string,\n): Promise<boolean> {\n const filePath = resolveWorkspacePath(rootDir, projectPath);\n const targetRealpath = await realpathIfExists(filePath);\n if (targetRealpath === null) return false;\n const rootRealpath = await realpath(rootDir);\n assertContained(rootRealpath, targetRealpath, \"Project path\");\n return true;\n}\n\nexport async function unlinkWorkspaceFile(\n rootDir: string,\n projectPath: string,\n): Promise<void> {\n const filePath = resolveWorkspacePath(rootDir, projectPath);\n await assertNearestParentContained(rootDir, filePath, \"Project path\");\n await assertExistingTargetContained(rootDir, filePath, \"Project path\");\n await unlink(filePath);\n}\n\nexport async function removeWorkspacePath(\n rootDir: string,\n projectPath: string,\n options: { recursive?: boolean; force?: boolean } = {},\n): Promise<void> {\n const filePath = resolveWorkspacePath(rootDir, projectPath);\n await assertNearestParentContained(rootDir, filePath, \"Project path\");\n await assertExistingTargetContained(rootDir, filePath, \"Project path\");\n await rm(filePath, options);\n}\n"],"mappings":"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","names":["path","path","isRecord","readFile","path"]}