@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.
- package/README.md +3 -1
- package/dist/agent-verifier/agent-workspace-verifier.mjs +12 -12
- package/dist/agent-verifier/{chunk-LKQ557TJ.mjs → chunk-334H4LE4.mjs} +3 -3
- package/dist/agent-verifier/{chunk-H3XNWKJU.mjs → chunk-7LFDFXLS.mjs} +2 -2
- package/dist/agent-verifier/{chunk-DWLTCUUX.mjs → chunk-7MAOGFFP.mjs} +6 -6
- package/dist/agent-verifier/{chunk-CO3BRUD6.mjs → chunk-AG5J3SMN.mjs} +11 -3
- package/dist/agent-verifier/chunk-AG5J3SMN.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-AXXUGU7Q.mjs → chunk-AQ6UQHPT.mjs} +4 -30
- package/dist/agent-verifier/chunk-AQ6UQHPT.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-A67WUYN2.mjs → chunk-B42OHJNY.mjs} +3 -3
- package/dist/agent-verifier/{chunk-V6AQDR7W.mjs → chunk-HUBV22JQ.mjs} +3 -3
- package/dist/agent-verifier/chunk-HUBV22JQ.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-5GCZZ6NW.mjs → chunk-JB7VXCMB.mjs} +2 -2
- package/dist/agent-verifier/{chunk-WAFBU5U7.mjs → chunk-OJFZVGEL.mjs} +38 -13
- package/dist/agent-verifier/chunk-OJFZVGEL.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-G2ECODRB.mjs → chunk-PLXXH5LY.mjs} +2 -2
- package/dist/agent-verifier/{chunk-655VJLXA.mjs → chunk-PWPOLHTW.mjs} +9 -12
- package/dist/agent-verifier/chunk-PWPOLHTW.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-DPYC2NDB.mjs → chunk-RCYO6HWW.mjs} +2 -2
- package/dist/agent-verifier/{chunk-NFL3Z4Z7.mjs → chunk-RP7ZWFVH.mjs} +12 -8
- package/dist/agent-verifier/{chunk-NFL3Z4Z7.mjs.map → chunk-RP7ZWFVH.mjs.map} +1 -1
- package/dist/agent-verifier/{compile-4VSMC275.mjs → compile-VOBO2I6D.mjs} +12 -12
- package/dist/agent-verifier/{global-config-6UGFPLDA.mjs → global-config-L7PLLUK5.mjs} +3 -3
- package/dist/agent-verifier/{keychain-backend-BQLW5VEC.mjs → keychain-backend-UF3Z26JM.mjs} +1 -1
- package/dist/agent-verifier/keychain-backend-UF3Z26JM.mjs.map +1 -0
- package/dist/agent-verifier/{local-files-WPHUV6GU.mjs → local-files-DAFIR7SN.mjs} +4 -4
- package/dist/agent-verifier/{materialize-workspace-CV2JNXLU.mjs → materialize-workspace-PAC75NSP.mjs} +6 -6
- package/dist/agent-verifier/{reducer-native-test-harness-GY2CCQWN.mjs → reducer-native-test-harness-HSXRUGOR.mjs} +8 -8
- package/dist/agent-verifier/{static-scaffold-DJJRKMNB.mjs → static-scaffold-KSOTKJNQ.mjs} +4 -4
- package/dist/agent-verifier/{sync-YWK4SPWV.mjs → sync-MQJJEZAA.mjs} +13 -13
- package/dist/agent-verifier/{test-LQAGEQLY.mjs → test-R6HC6CYZ.mjs} +11 -11
- package/dist/agent-verifier/{workspace-codegen-4IWICKLB.mjs → workspace-codegen-SPPVHURX.mjs} +3 -3
- package/dist/authoring-compatibility-internal.js +1 -1
- package/dist/{chunk-2R4L2YDX.js → chunk-2WB3DYW4.js} +17 -8
- package/dist/chunk-2WB3DYW4.js.map +1 -0
- package/dist/{chunk-PW7D2W5S.js → chunk-2XMBZPL5.js} +45 -23
- package/dist/{chunk-PW7D2W5S.js.map → chunk-2XMBZPL5.js.map} +1 -1
- package/dist/{chunk-AVOAT522.js → chunk-J3CWZHY7.js} +4 -30
- package/dist/chunk-J3CWZHY7.js.map +1 -0
- package/dist/{global-config-NLGAFSRU.js → global-config-VQWFTIAV.js} +2 -2
- package/dist/index.js +5 -5
- package/dist/internal.js +3 -3
- package/dist/{keychain-backend-47LZ5IX5.js → keychain-backend-GO34KGTG.js} +1 -1
- package/dist/keychain-backend-GO34KGTG.js.map +1 -0
- package/package.json +1 -1
- package/release/authoring-release-set.json +4 -4
- package/skills/dreamboard/SKILL.md +8 -0
- package/skills/dreamboard/references/building-your-first-game.md +37 -15
- package/skills/dreamboard/references/canonical-concepts.md +74 -0
- package/skills/dreamboard/references/game-interface.md +15 -2
- package/skills/dreamboard/references/manifest-authoring.md +8 -0
- package/skills/dreamboard/references/reducer.md +47 -2
- package/skills/dreamboard/references/rule-authoring.md +10 -0
- package/skills/dreamboard/references/testing.md +7 -3
- package/dist/agent-verifier/chunk-655VJLXA.mjs.map +0 -1
- package/dist/agent-verifier/chunk-AXXUGU7Q.mjs.map +0 -1
- package/dist/agent-verifier/chunk-CO3BRUD6.mjs.map +0 -1
- package/dist/agent-verifier/chunk-V6AQDR7W.mjs.map +0 -1
- package/dist/agent-verifier/chunk-WAFBU5U7.mjs.map +0 -1
- package/dist/agent-verifier/keychain-backend-BQLW5VEC.mjs.map +0 -1
- package/dist/chunk-2R4L2YDX.js.map +0 -1
- package/dist/chunk-AVOAT522.js.map +0 -1
- package/dist/keychain-backend-47LZ5IX5.js.map +0 -1
- /package/dist/agent-verifier/{chunk-LKQ557TJ.mjs.map → chunk-334H4LE4.mjs.map} +0 -0
- /package/dist/agent-verifier/{chunk-H3XNWKJU.mjs.map → chunk-7LFDFXLS.mjs.map} +0 -0
- /package/dist/agent-verifier/{chunk-DWLTCUUX.mjs.map → chunk-7MAOGFFP.mjs.map} +0 -0
- /package/dist/agent-verifier/{chunk-A67WUYN2.mjs.map → chunk-B42OHJNY.mjs.map} +0 -0
- /package/dist/agent-verifier/{chunk-5GCZZ6NW.mjs.map → chunk-JB7VXCMB.mjs.map} +0 -0
- /package/dist/agent-verifier/{chunk-G2ECODRB.mjs.map → chunk-PLXXH5LY.mjs.map} +0 -0
- /package/dist/agent-verifier/{chunk-DPYC2NDB.mjs.map → chunk-RCYO6HWW.mjs.map} +0 -0
- /package/dist/agent-verifier/{compile-4VSMC275.mjs.map → compile-VOBO2I6D.mjs.map} +0 -0
- /package/dist/agent-verifier/{global-config-6UGFPLDA.mjs.map → global-config-L7PLLUK5.mjs.map} +0 -0
- /package/dist/agent-verifier/{local-files-WPHUV6GU.mjs.map → local-files-DAFIR7SN.mjs.map} +0 -0
- /package/dist/agent-verifier/{materialize-workspace-CV2JNXLU.mjs.map → materialize-workspace-PAC75NSP.mjs.map} +0 -0
- /package/dist/agent-verifier/{reducer-native-test-harness-GY2CCQWN.mjs.map → reducer-native-test-harness-HSXRUGOR.mjs.map} +0 -0
- /package/dist/agent-verifier/{static-scaffold-DJJRKMNB.mjs.map → static-scaffold-KSOTKJNQ.mjs.map} +0 -0
- /package/dist/agent-verifier/{sync-YWK4SPWV.mjs.map → sync-MQJJEZAA.mjs.map} +0 -0
- /package/dist/agent-verifier/{test-LQAGEQLY.mjs.map → test-R6HC6CYZ.mjs.map} +0 -0
- /package/dist/agent-verifier/{workspace-codegen-4IWICKLB.mjs.map → workspace-codegen-SPPVHURX.mjs.map} +0 -0
- /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
|
|
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-
|
|
6
|
+
} from "./chunk-RP7ZWFVH.mjs";
|
|
7
7
|
import "./chunk-WSIYUUSD.mjs";
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
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-
|
|
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-
|
|
88
|
-
import("./compile-
|
|
89
|
-
import("./test-
|
|
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-
|
|
154
|
-
import("./local-files-
|
|
155
|
-
import("./workspace-codegen-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
192
|
+
//# sourceMappingURL=chunk-334H4LE4.mjs.map
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
workspacePathExists,
|
|
15
15
|
writeWorkspaceJsonFile,
|
|
16
16
|
writeWorkspaceTextFile
|
|
17
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
10
|
+
} from "./chunk-PLXXH5LY.mjs";
|
|
11
11
|
import {
|
|
12
12
|
createUserTokenManager,
|
|
13
13
|
resolveLocalHarnessAccessToken
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-AG5J3SMN.mjs";
|
|
15
15
|
import {
|
|
16
16
|
loadManifest
|
|
17
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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":";;;;;;AAEA,IAAM,uBACJ,OACI,gBACA;AAEC,IAAM,gBACX,yBAAyB,cAAc,cAAc;AAEhD,IAAM,qBAAqB,kBAAkB;AAC7C,IAAM,wBAAwB;;;ACXrC,OAAO,YAAY;AA2DnB,eAAsB,uBAAuB,OAGR;AACnC,QAAM,EAAE,UAAU,SAAS,IAAI,iBAAiB,MAAM,MAAM;AAC5D,QAAM,OAAO,IAAI,gBAAgB;AAAA,IAC/B,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,eAAe,MAAM;AAAA,EACvB,CAAC;AACD,SAAO,kBAAkB,UAAU,IAAI;AACzC;AAEA,SAAS,iBAAiB,QAKxB;AACA,QAAM,SAAS,OAAO,QAAQ,KAAK,EAAE,QAAQ,OAAO,EAAE;AACtD,QAAM,WAAW,OAAO,UAAU,KAAK;AACvC,MAAI,CAAC,UAAU,CAAC,UAAU;AACxB,UAAM,IAAI;AAAA,MACR;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,GAAG;AAAA,IACZ;AAAA,EACF;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,UACE,OAAO,UAAU,KAAK,KAAK,IAAI,IAAI,gBAAgB,MAAM,EAAE,SAAS;AAAA,IACtE,OAAO,OAAO,OAAO,KAAK,KAAK;AAAA,EACjC;AACF;AAEA,eAAe,kBACb,UACA,MACkC;AAClC,QAAM,WAAW,MAAM,MAAM,UAAU;AAAA,IACrC,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,QAAQ;AAAA,IACV;AAAA,IACA;AAAA,EACF,CAAC;AACD,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,SAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI;AAAA,MACR,qCAAqC,SAAS,MAAM,MAAM,MAAM;AAAA,IAClE;AAAA,EACF;AACA,QAAM,UAAW,MAAM,SAAS,KAAK;AAKrC,MAAI,OAAO,QAAQ,iBAAiB,UAAU;AAC5C,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC5E;AACA,MAAI,OAAO,QAAQ,kBAAkB,UAAU;AAC7C,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,YACJ,OAAO,QAAQ,eAAe,WAC1B,IAAI,KAAK,KAAK,IAAI,IAAI,QAAQ,aAAa,GAAI,EAAE,YAAY,IAC7D;AACN,SAAO;AAAA,IACL,aAAa,QAAQ;AAAA,IACrB,cAAc,QAAQ;AAAA,IACtB;AAAA,IACA;AAAA,EACF;AACF;;;ACnIA,eAAsB,4BAA4B,OAKb;AACnC,QAAM,YAAY,MAAM,aAAa,WAAW,MAAM,KAAK,UAAU;AACrE,QAAM,WAAW,MAAM;AAAA,IACrB,IAAI,IAAI,4BAA4B,MAAM,UAAU;AAAA,IACpD;AAAA,MACE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,MAAM,gBAAgB;AAAA,QAC/C,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MACV;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,UAAU,MAAM,SAAS,CAAC;AAAA,IACnD;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI;AAAA,MACR,qCAAqC,SAAS,MAAM;AAAA,IACtD;AAAA,EACF;AAEA,QAAM,UAAW,MAAM,SAAS,KAAK;AAOrC,MAAI,OAAO,QAAQ,gBAAgB,YAAY,QAAQ,gBAAgB,IAAI;AACzE,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,MAAI,QAAQ,cAAc,UAAU;AAClC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,QAAQ,aAAa,MAAM,UAAU;AACvC,UAAM,IAAI,MAAM,wDAAwD;AAAA,EAC1E;AAEA,QAAM,YACJ,OAAO,QAAQ,cAAc,YAAY,OAAO,SAAS,QAAQ,SAAS,IACtE,QAAQ,YACR;AAEN,SAAO;AAAA,IACL,aAAa,QAAQ;AAAA,IACrB,WAAW;AAAA,IACX,UAAU,MAAM;AAAA,IAChB;AAAA,IACA,WACE,cAAc,SACV,SACA,IAAI,KAAK,KAAK,IAAI,IAAI,YAAY,GAAI,EAAE,YAAY;AAAA,EAC5D;AACF;;;AC1DA,IAAM,0BAA0B,KAAK;AAa9B,SAAS,uBACd,QACkB;AAClB,SAAO;AAAA,IACL,MAAM,kBAAkB;AACtB,YAAM,kBAAkB,sBAAsB,QAAQ,gBAAgB;AACtE,UAAI,gBAAiB,QAAO;AAE5B,UAAI,CAAC,kBAAkB,MAAM,EAAG,QAAO;AAEvC,aAAO,mBAAmB,OAAO,QAAQ;AACvC,cAAM,SAAS,MAAM,IAAI,KAAK;AAC9B,cAAM,WAAW,oBAAoB,MAAM;AAC3C,YAAI,SAAU,QAAO;AAErB,cAAM,QAAQ,MAAM,6BAA6B,QAAQ,MAAM;AAC/D,cAAM,YAAY,MAAM,4BAA4B;AAAA,UAClD,YAAY,OAAO;AAAA,UACnB,kBAAkB,MAAM;AAAA,UACxB,UAAU;AAAA,QACZ,CAAC;AAED,cAAM,IAAI,UAAU;AAAA,UAClB,GAAG;AAAA,UACH,oBAAoB,UAAU;AAAA,UAC9B,wBAAwB,UAAU;AAAA,QACpC,CAAC;AAED,eAAO;AAAA,UACL,OAAO,UAAU;AAAA,UACjB,WAAW,UAAU;AAAA,UACrB,UAAU;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,kBAAkB;AACtB,YAAM,kBAAkB,sBAAsB,QAAQ,gBAAgB;AACtE,UAAI,gBAAiB,QAAO;AAE5B,UAAI,CAAC,kBAAkB,MAAM,GAAG;AAC9B,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,aAAO,mBAAmB,OAAO,QAAQ;AACvC,cAAM,SAAS,MAAM,IAAI,KAAK;AAC9B,cAAM,QAAQ,MAAM,6BAA6B,QAAQ,MAAM;AAC/D,cAAM,YAAY,MAAM,4BAA4B;AAAA,UAClD,YAAY,OAAO;AAAA,UACnB,kBAAkB,MAAM;AAAA,UACxB,UAAU;AAAA,QACZ,CAAC;AAED,cAAM,IAAI,UAAU,KAAK;AAEzB,eAAO;AAAA,UACL,OAAO,UAAU;AAAA,UACjB,WAAW,UAAU;AAAA,UACrB,UAAU;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,SAAS,sBACP,QACA,UACoB;AACpB,MAAI,kBAAkB,MAAM,EAAG,QAAO;AACtC,MAAI,CAAC,OAAO,UAAW,QAAO;AAC9B,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,IACd,WAAW,OAAO;AAAA,IAClB;AAAA,EACF;AACF;AAEA,SAAS,oBACP,QACoB;AACpB,MAAI,CAAC,QAAQ,mBAAoB,QAAO;AACxC,MAAI,QAAQ,OAAO,wBAAwB,OAAO,kBAAkB,GAAG;AACrE,WAAO;AAAA,MACL,OAAO,OAAO;AAAA,MACd,WAAW,OAAO;AAAA,MAClB,UAAU;AAAA,IACZ;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,6BACb,QACA,QACsB;AACtB,QAAM,cAAc,QAAQ,eAAe,OAAO;AAClD,QAAM,eAAe,QAAQ,gBAAgB,OAAO;AACpD,QAAM,iBAAiB,QAAQ,kBAAkB,OAAO;AAExD,MAAI,CAAC,cAAc;AACjB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,eAAe,QAAQ,gBAAgB,WAAW,GAAG;AACvD,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,oBAAoB,QAAQ;AAAA,MAC5B,wBAAwB,QAAQ;AAAA,MAChC,kBAAkB,QAAQ,oBAAoB,OAAO;AAAA,MACrD,oBACE,QAAQ,sBAAsB,OAAO;AAAA,MACvC,oBACE,QAAQ,sBAAsB,OAAO;AAAA,MACvC,aAAa,QAAQ,eAAe,OAAO;AAAA,IAC7C;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,uBAAuB;AAAA,IAC3C,QAAQ;AAAA,MACN,QAAQ,QAAQ,oBAAoB,OAAO;AAAA,MAC3C,UAAU,QAAQ,sBAAsB,OAAO;AAAA,MAC/C,UAAU,QAAQ,sBAAsB,OAAO;AAAA,IACjD;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,aAAa,QAAQ;AAAA,IACrB,cAAc,QAAQ;AAAA,IACtB,gBAAgB,QAAQ;AAAA,IACxB,kBAAkB,QAAQ,oBAAoB,OAAO;AAAA,IACrD,oBAAoB,QAAQ,sBAAsB,OAAO;AAAA,IACzD,oBAAoB,QAAQ;AAAA,IAC5B,aAAa,QAAQ,eAAe,OAAO;AAAA,EAC7C;AACF;AAEA,SAAS,QAAQ,WAA+B,OAAwB;AACtE,QAAM,SAAS,YAAY,IAAI,KAAK,SAAS,IAAI,aAAa,KAAK;AACnE,SACE,WAAW,QACX,OAAO,SAAS,OAAO,QAAQ,CAAC,KAChC,OAAO,QAAQ,IAAI,KAAK,IAAI,IAAI;AAEpC;AAEA,SAAS,aAAa,aAA8C;AAClE,MAAI,CAAC,YAAa,QAAO;AACzB,QAAM,QAAQ,YAAY,MAAM,GAAG;AACnC,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,MAAI;AACF,UAAM,UAAU,KAAK;AAAA,MACnB,OAAO,KAAK,MAAM,CAAC,GAAI,WAAW,EAAE,SAAS,MAAM;AAAA,IACrD;AACA,QAAI,OAAO,QAAQ,QAAQ,YAAY,CAAC,OAAO,SAAS,QAAQ,GAAG,GAAG;AACpE,aAAO;AAAA,IACT;AACA,WAAO,IAAI,KAAK,QAAQ,MAAM,GAAI;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBAAkB,QAAiC;AAC1D,SAAO,OAAO,uBAAuB;AACvC;;;ACrMA,SAAS,YAAY,kBAAkB;AAMvC,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AACzB,IAAM,sBAAsB,IAAI,KAAK;AAErC,IAAM,eAAe,oBAAI,IAAoB;AAEtC,SAAS,+BACd,QACoB;AACpB,MAAI,sBAAsB,OAAO,gBAAgB,SAAS;AACxD,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,yBAAyB,MAAM;AAC/C,MACE,OAAO,cACN,YAAY,eAAe,sBAAsB,OAAO,eAAe,IACxE;AACA,WAAO;AAAA,EACT;AAEA,QAAM,WAAW;AAAA,IACf;AAAA,IACA,QAAQ,IAAI,8BAA8B;AAAA,IAC1C,QAAQ,IAAI,8BAA8B;AAAA,IAC1C,QAAQ,IAAI,yBAAyB;AAAA,IACrC,QAAQ,IAAI,sBAAsB;AAAA,IAClC,QAAQ,IAAI,uBAAuB;AAAA,IACnC,QAAQ,IAAI,mCAAmC;AAAA,EACjD,EAAE,KAAK,IAAI;AACX,QAAM,SAAS,aAAa,IAAI,QAAQ;AACxC,MAAI,OAAQ,QAAO;AAEnB,QAAM,QAAQ,sBAAsB,OAAO;AAC3C,eAAa,IAAI,UAAU,KAAK;AAChC,SAAO;AACT;AAEA,SAAS,sBACP,QACS;AACT,SAAO,WAAW,UAAU,WAAW,SAAS,WAAW;AAC7D;AAEO,SAAS,yBACd,QACqB;AACrB,SAAO,cAAc,OAAO,UAAU,KAAK,cAAc,OAAO,UAAU,IACtE,cACA;AACN;AAEA,SAAS,sBAAsB,SAAsC;AACnE,QAAM,UACJ,SAAS,QAAQ,IAAI,qBAAqB,KAC1C,SAAS,QAAQ,IAAI,kBAAkB,KACvC;AACF,QAAM,QACJ,SAAS,QAAQ,IAAI,mBAAmB,MACvC,QAAQ,SAAS,GAAG,IAAI,UAAU;AACrC,QAAM,SACJ,SAAS,QAAQ,IAAI,0BAA0B,MAC9C,YAAY,cAAc,mBAAmB;AAChD,QAAM,SACJ,SAAS,QAAQ,IAAI,0BAA0B,MAC9C,YAAY,cAAc,mBAAmB;AAChD,QAAM,aAAa;AAAA,IACjB,SAAS,QAAQ,IAAI,+BAA+B,KAClD,OAAO,mBAAmB;AAAA,EAC9B;AACA,MAAI,CAAC,OAAO,SAAS,UAAU,KAAK,cAAc,GAAG;AACnD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,UAAU;AAAA,IACd,KAAK;AAAA,IACL,qBAAqB;AAAA,IACrB,6BAA6B;AAAA,IAC7B,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,IACzB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK,MAAM,KAAK,MAAM,UAAU;AAAA,IAChC,KAAK,WAAW;AAAA,EAClB;AAEA,QAAM,aAAa,cAAc,EAAE,KAAK,SAAS,KAAK,MAAM,CAAC;AAC7D,QAAM,cAAc,cAAc,OAAO;AACzC,QAAM,YAAY,WAAW,UAAU,MAAM,EAC1C,OAAO,GAAG,UAAU,IAAI,WAAW,EAAE,EACrC,OAAO,WAAW;AACrB,SAAO,GAAG,UAAU,IAAI,WAAW,IAAI,SAAS;AAClD;AAEA,SAAS,cAAc,OAAwB;AAC7C,SAAO,OAAO,KAAK,KAAK,UAAU,KAAK,GAAG,MAAM,EAAE,SAAS,WAAW;AACxE;AAEA,SAAS,SAAS,KAA6C;AAC7D,SAAO,OAAO,QAAQ,YAAY,IAAI,KAAK,EAAE,SAAS,IAClD,IAAI,KAAK,IACT;AACN;AAEA,SAAS,cAAc,QAAqC;AAC1D,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,MAAM;AAC1B,YACG,IAAI,aAAa,eAAe,IAAI,aAAa,iBACjD,IAAI,SAAS,WAAW,IAAI,SAAS;AAAA,EAE1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
618
|
+
//# sourceMappingURL=chunk-B42OHJNY.mjs.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
readWorkspaceTextFileIfExists,
|
|
5
5
|
validateGeneratedArtifacts,
|
|
6
6
|
writeWorkspaceTextFile
|
|
7
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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 (
|
|
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-
|
|
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"]}
|