@dreamboard-games/cli 0.1.30-alpha.2 → 0.1.30-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/agent-verifier/agent-workspace-verifier.mjs +227 -0
  2. package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -0
  3. package/dist/agent-verifier/chunk-3UKQVWLV.mjs +1744 -0
  4. package/dist/agent-verifier/chunk-3UKQVWLV.mjs.map +1 -0
  5. package/dist/agent-verifier/chunk-776W3UGV.mjs +167 -0
  6. package/dist/agent-verifier/chunk-776W3UGV.mjs.map +1 -0
  7. package/dist/agent-verifier/chunk-7WWGFAAU.mjs +729 -0
  8. package/dist/agent-verifier/chunk-7WWGFAAU.mjs.map +1 -0
  9. package/dist/agent-verifier/chunk-A64ZZUZV.mjs +261 -0
  10. package/dist/agent-verifier/chunk-A64ZZUZV.mjs.map +1 -0
  11. package/dist/agent-verifier/chunk-E7SSWJXJ.mjs +3137 -0
  12. package/dist/agent-verifier/chunk-E7SSWJXJ.mjs.map +1 -0
  13. package/dist/agent-verifier/chunk-F2DIOJJZ.mjs +302 -0
  14. package/dist/agent-verifier/chunk-F2DIOJJZ.mjs.map +1 -0
  15. package/dist/agent-verifier/chunk-G42BGGG2.mjs +70 -0
  16. package/dist/agent-verifier/chunk-G42BGGG2.mjs.map +1 -0
  17. package/dist/agent-verifier/chunk-H76MT5UR.mjs +57 -0
  18. package/dist/agent-verifier/chunk-H76MT5UR.mjs.map +1 -0
  19. package/dist/agent-verifier/chunk-HGMUAL33.mjs +39 -0
  20. package/dist/agent-verifier/chunk-HGMUAL33.mjs.map +1 -0
  21. package/dist/agent-verifier/chunk-IAYRNVUC.mjs +49 -0
  22. package/dist/agent-verifier/chunk-IAYRNVUC.mjs.map +1 -0
  23. package/dist/agent-verifier/chunk-JGT4P4UD.mjs +45 -0
  24. package/dist/agent-verifier/chunk-JGT4P4UD.mjs.map +1 -0
  25. package/dist/agent-verifier/chunk-LUZ7KE6H.mjs +79 -0
  26. package/dist/agent-verifier/chunk-LUZ7KE6H.mjs.map +1 -0
  27. package/dist/agent-verifier/chunk-NAK77WXW.mjs +767 -0
  28. package/dist/agent-verifier/chunk-NAK77WXW.mjs.map +1 -0
  29. package/dist/agent-verifier/chunk-O4YCPU7C.mjs +2913 -0
  30. package/dist/agent-verifier/chunk-O4YCPU7C.mjs.map +1 -0
  31. package/dist/agent-verifier/chunk-S34FRJHS.mjs +222 -0
  32. package/dist/agent-verifier/chunk-S34FRJHS.mjs.map +1 -0
  33. package/dist/agent-verifier/chunk-SH5JKYOB.mjs +226 -0
  34. package/dist/agent-verifier/chunk-SH5JKYOB.mjs.map +1 -0
  35. package/dist/agent-verifier/chunk-SKI2ESE5.mjs +44 -0
  36. package/dist/agent-verifier/chunk-SKI2ESE5.mjs.map +1 -0
  37. package/dist/agent-verifier/chunk-TAEQKBJB.mjs +107 -0
  38. package/dist/agent-verifier/chunk-TAEQKBJB.mjs.map +1 -0
  39. package/dist/agent-verifier/chunk-UIOLGH4A.mjs +150 -0
  40. package/dist/agent-verifier/chunk-UIOLGH4A.mjs.map +1 -0
  41. package/dist/agent-verifier/chunk-UIZNWRM6.mjs +2432 -0
  42. package/dist/agent-verifier/chunk-UIZNWRM6.mjs.map +1 -0
  43. package/dist/agent-verifier/chunk-VS573ERH.mjs +14523 -0
  44. package/dist/agent-verifier/chunk-VS573ERH.mjs.map +1 -0
  45. package/dist/agent-verifier/chunk-W3N3QJ4V.mjs +624 -0
  46. package/dist/agent-verifier/chunk-W3N3QJ4V.mjs.map +1 -0
  47. package/dist/agent-verifier/chunk-XGWCY624.mjs +185 -0
  48. package/dist/agent-verifier/chunk-XGWCY624.mjs.map +1 -0
  49. package/dist/agent-verifier/chunk-XQXDOBYB.mjs +382 -0
  50. package/dist/agent-verifier/chunk-XQXDOBYB.mjs.map +1 -0
  51. package/dist/agent-verifier/chunk-YE7UAO3T.mjs +129 -0
  52. package/dist/agent-verifier/chunk-YE7UAO3T.mjs.map +1 -0
  53. package/dist/agent-verifier/chunk-ZEELHSY3.mjs +20 -0
  54. package/dist/agent-verifier/chunk-ZEELHSY3.mjs.map +1 -0
  55. package/dist/agent-verifier/compile-TEQVA46V.mjs +312 -0
  56. package/dist/agent-verifier/compile-TEQVA46V.mjs.map +1 -0
  57. package/dist/agent-verifier/global-config-Y2NTSK4R.mjs +18 -0
  58. package/dist/agent-verifier/global-config-Y2NTSK4R.mjs.map +1 -0
  59. package/dist/agent-verifier/keychain-backend-SPQWGKZN.mjs +135 -0
  60. package/dist/agent-verifier/keychain-backend-SPQWGKZN.mjs.map +1 -0
  61. package/dist/agent-verifier/local-files-JFOQQZDL.mjs +45 -0
  62. package/dist/agent-verifier/local-files-JFOQQZDL.mjs.map +1 -0
  63. package/dist/agent-verifier/local-typecheck-XVGWI75X.mjs +10 -0
  64. package/dist/agent-verifier/local-typecheck-XVGWI75X.mjs.map +1 -0
  65. package/dist/agent-verifier/materialize-workspace-ZAVGQQSF.mjs +89 -0
  66. package/dist/agent-verifier/materialize-workspace-ZAVGQQSF.mjs.map +1 -0
  67. package/dist/agent-verifier/project-state-K576C2TE.mjs +33 -0
  68. package/dist/agent-verifier/project-state-K576C2TE.mjs.map +1 -0
  69. package/dist/agent-verifier/prompt-MJRJMOGQ.mjs +756 -0
  70. package/dist/agent-verifier/prompt-MJRJMOGQ.mjs.map +1 -0
  71. package/dist/agent-verifier/reducer-bundle-preflight-LXNJUBKL.mjs +20 -0
  72. package/dist/agent-verifier/reducer-bundle-preflight-LXNJUBKL.mjs.map +1 -0
  73. package/dist/agent-verifier/reducer-contract-preflight-TUMQ43JV.mjs +11 -0
  74. package/dist/agent-verifier/reducer-contract-preflight-TUMQ43JV.mjs.map +1 -0
  75. package/dist/agent-verifier/reducer-native-test-harness-CHX5MBL5.mjs +50 -0
  76. package/dist/agent-verifier/reducer-native-test-harness-CHX5MBL5.mjs.map +1 -0
  77. package/dist/agent-verifier/static-scaffold-R7SVDRQI.mjs +27 -0
  78. package/dist/agent-verifier/static-scaffold-R7SVDRQI.mjs.map +1 -0
  79. package/dist/agent-verifier/sync-THAI546U.mjs +588 -0
  80. package/dist/agent-verifier/sync-THAI546U.mjs.map +1 -0
  81. package/dist/agent-verifier/test-AFAQFKOB.mjs +353 -0
  82. package/dist/agent-verifier/test-AFAQFKOB.mjs.map +1 -0
  83. package/dist/agent-verifier/workspace-codegen-2ZMQRIKJ.mjs +10 -0
  84. package/dist/agent-verifier/workspace-codegen-2ZMQRIKJ.mjs.map +1 -0
  85. package/dist/agent-verifier/workspace-dependencies-NOOQBK6I.mjs +15 -0
  86. package/dist/agent-verifier/workspace-dependencies-NOOQBK6I.mjs.map +1 -0
  87. package/package.json +3 -2
@@ -0,0 +1,302 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/templates/testing-types-content.ts
4
+ var DEFAULT_REJECTION_CODES = [
5
+ "NOT_YOUR_TURN",
6
+ "action-unavailable",
7
+ "invalid-action-params",
8
+ "prompt-not-owned"
9
+ ];
10
+ function renderLiteralUnion(values) {
11
+ if (values.length === 0) {
12
+ return "never";
13
+ }
14
+ return values.map((value) => JSON.stringify(value)).join(" | ");
15
+ }
16
+ var REDUCER_TESTING_TYPES_WRAPPER_CONTENT = `// Generated by dreamboard \u2014 do not edit by hand.
17
+ import game from "../app/game";
18
+ import {
19
+ contractFingerprint,
20
+ createReducerBundle,
21
+ } from "@dreamboard-games/sdk/reducer";
22
+ import { createTestRuntime as createDreamboardTestRuntime } from "@dreamboard-games/sdk/testing";
23
+ import type { CreateTestRuntimeOptions } from "@dreamboard-games/sdk/testing";
24
+ import { literals } from "../shared/manifest-contract";
25
+ import type { PhaseName } from "../shared/generated/ui-contract";
26
+ import {
27
+ BASE_STATES,
28
+ BASE_STATES_CONTRACT_FINGERPRINT,
29
+ } from "./generated/base-states.generated";
30
+ import type {
31
+ BaseDefinition,
32
+ ScenarioDefinition,
33
+ TestRunner,
34
+ } from "./generated/testing-contract";
35
+
36
+ export * from "./generated/testing-contract";
37
+
38
+ /**
39
+ * Workspace-narrowed \`defineBase\` wrapper. Accepts the generated
40
+ * \`BaseDefinition\` so \`setup({ seat, game })\` is typed against the
41
+ * workspace's player ids and interaction contract.
42
+ */
43
+ export function defineBase<const Definition extends BaseDefinition>(
44
+ definition: Definition,
45
+ ): Definition {
46
+ return definition;
47
+ }
48
+
49
+ /**
50
+ * Workspace-narrowed \`defineScenario\` wrapper. The generated
51
+ * \`ScenarioDefinition\` narrows \`ctx.view(playerId)\` in \`then\` based on
52
+ * the declared \`phase\`, keeps \`when\` union-typed, and constrains
53
+ * \`phase\` / \`stage\` to the manifest-derived literal types.
54
+ */
55
+ export function defineScenario<
56
+ const Runners extends readonly TestRunner[] = readonly ["reducer"],
57
+ const Phase extends PhaseName | undefined = undefined,
58
+ >(
59
+ definition: ScenarioDefinition<Runners, Phase>,
60
+ ): ScenarioDefinition<Runners, Phase> {
61
+ return definition;
62
+ }
63
+
64
+ export function createTestRuntime(options: {
65
+ baseId: keyof typeof BASE_STATES & string;
66
+ phase?: PhaseName;
67
+ controllingPlayerId?: (typeof literals.playerIds)[number];
68
+ userId?: string | null;
69
+ }) {
70
+ const reducerBundle =
71
+ createReducerBundle(game) satisfies CreateTestRuntimeOptions["bundle"];
72
+ const baseStates =
73
+ BASE_STATES satisfies CreateTestRuntimeOptions["baseStates"];
74
+ const runtime = createDreamboardTestRuntime({
75
+ baseId: options.baseId,
76
+ baseStates,
77
+ bundle: reducerBundle,
78
+ contractFingerprint: contractFingerprint(game).value,
79
+ expectedBaseStateFingerprint: BASE_STATES_CONTRACT_FINGERPRINT,
80
+ phase: options.phase,
81
+ userId: options.userId ?? "test-user",
82
+ playerIds: literals.playerIds.slice(
83
+ 0,
84
+ BASE_STATES[options.baseId]?.fingerprint.players ?? literals.playerIds.length,
85
+ ),
86
+ });
87
+
88
+ if (options.controllingPlayerId) {
89
+ runtime.setControllingPlayer(options.controllingPlayerId);
90
+ }
91
+
92
+ return runtime;
93
+ }
94
+ `;
95
+ function buildReducerTestingContractContent(options = {}) {
96
+ const rejectionCodes = Array.from(
97
+ /* @__PURE__ */ new Set([...options.rejectionCodes ?? [], ...DEFAULT_REJECTION_CODES])
98
+ ).sort((left, right) => left.localeCompare(right));
99
+ return `// Generated by dreamboard \u2014 do not edit by hand.
100
+ import type game from "../../app/game";
101
+ import { literals, type SetupProfileId } from "../../shared/manifest-contract";
102
+ import {
103
+ type GameView,
104
+ type InteractionId,
105
+ type InteractionKey,
106
+ type InteractionParamsOf,
107
+ type PhaseName,
108
+ type StageName as WorkspaceStageName,
109
+ } from "../../shared/generated/ui-contract";
110
+ import type {
111
+ ExpectFn as SharedExpectFn,
112
+ TestRunner as SharedTestRunner,
113
+ } from "@dreamboard-games/sdk/testing";
114
+ import type { InteractionDescriptor } from "@dreamboard-games/sdk/runtime";
115
+ import { BASE_STATES } from "./base-states.generated";
116
+
117
+ export type GameDefinition = typeof game;
118
+ export type PlayerId = (typeof literals.playerIds)[number];
119
+ export type StateName = PhaseName;
120
+ export type BaseId = keyof typeof BASE_STATES & string;
121
+ export type InteractionDescriptorFor<Id extends string = string> =
122
+ InteractionDescriptor<Id>;
123
+ export type InteractionExplanation = {
124
+ interactionId: string;
125
+ phase: string;
126
+ step: string | null;
127
+ availability:
128
+ | "available"
129
+ | "notYourTurn"
130
+ | "wrongPhase"
131
+ | "wrongStep"
132
+ | "blocked";
133
+ rules: ReadonlyArray<{
134
+ ruleId: string;
135
+ outcome: "passed" | "failed" | "notEvaluated";
136
+ errorCode?: string;
137
+ message?: string;
138
+ }>;
139
+ actor: { required: readonly string[]; playerIsActor: boolean };
140
+ inputs: ReadonlyArray<{
141
+ key: string;
142
+ kind: string;
143
+ eligibleCount: number | "lazy";
144
+ }>;
145
+ };
146
+ export type TestRunner = SharedTestRunner;
147
+ export type ExpectFn = SharedExpectFn;
148
+ export type KnownRejectionCode = ${renderLiteralUnion(rejectionCodes)};
149
+ export type RejectionCode = [KnownRejectionCode] extends [never]
150
+ ? string
151
+ : KnownRejectionCode;
152
+
153
+ type DefaultRunners = readonly ["reducer"];
154
+ type PhaseTaggedView<Phase extends PhaseName> = Extract<
155
+ GameView,
156
+ { phase: Phase } | { currentPhase: Phase } | { state: Phase }
157
+ >;
158
+ type NarrowedView<Phase extends PhaseName> = [PhaseTaggedView<Phase>] extends [never]
159
+ ? GameView
160
+ : PhaseTaggedView<Phase>;
161
+
162
+ export type ViewByPhase = {
163
+ [Phase in PhaseName]: NarrowedView<Phase>;
164
+ };
165
+
166
+ type InteractionKeyForId<Id extends InteractionId> = Extract<
167
+ InteractionKey,
168
+ \`\${string}.\${Id}\`
169
+ >;
170
+ type InteractionParamsForKey<Key extends InteractionKey> =
171
+ Key extends InteractionKey ? InteractionParamsOf<Key> : never;
172
+ type InteractionParamsOfId<Id extends InteractionId> =
173
+ InteractionParamsForKey<InteractionKeyForId<Id>>;
174
+
175
+ export interface BrowserRunnerSnapshot {
176
+ sessionId: string | null;
177
+ shortCode: string | null;
178
+ version: number;
179
+ currentPhase: string | null;
180
+ controllingPlayerId: string;
181
+ controllablePlayerIds: string[];
182
+ view: unknown;
183
+ availableInteractions?: string[];
184
+ }
185
+
186
+ export interface BrowserRunnerBridge {
187
+ snapshot(): Promise<BrowserRunnerSnapshot>;
188
+ submitInteraction(
189
+ playerId: PlayerId,
190
+ interactionId: string,
191
+ params: unknown,
192
+ ): Promise<void>;
193
+ }
194
+
195
+ export interface BrowserRunnerDriver {
196
+ onReady?(bridge: BrowserRunnerBridge): Promise<void> | void;
197
+ interaction?(
198
+ bridge: BrowserRunnerBridge,
199
+ input: { playerId: PlayerId; interactionId: string; params: unknown },
200
+ ): Promise<boolean | void> | boolean | void;
201
+ }
202
+
203
+ export interface ScenarioGameApi {
204
+ start(): Promise<void>;
205
+ /**
206
+ * Patch the reducer snapshot for deterministic setup-heavy scenarios.
207
+ * This is limited to reducer snapshot materialization and is rejected by
208
+ * live replay/browser runners so authored gameplay verification still
209
+ * submits real interactions.
210
+ */
211
+ patchState(mutator: (state: Record<string, unknown>) => void): Promise<void>;
212
+ /**
213
+ * Submit a player interaction (action-kind or prompt-kind) to the game.
214
+ * The \`interactionId\` matches an \`InteractionId\` from the generated
215
+ * \`ui-contract\`; \`params\` is typed per interaction id.
216
+ */
217
+ submit<Id extends InteractionId>(
218
+ playerId: PlayerId,
219
+ interactionId: Id,
220
+ params?: InteractionParamsOfId<Id>,
221
+ ): Promise<void>;
222
+ }
223
+
224
+ export interface BaseContext {
225
+ game: ScenarioGameApi;
226
+ players(): readonly PlayerId[];
227
+ /**
228
+ * Resolve the seat at \`index\` in the base's players list.
229
+ * Throws if the index is out of range. Prefer \`seat(0)\`/\`seat(1)\` over
230
+ * literal player ids so bases stay portable across player counts and
231
+ * we never hard-code wire-shape assumptions like "player-1".
232
+ */
233
+ seat(index: number): PlayerId;
234
+ }
235
+
236
+ export interface SharedScenarioContext {
237
+ game: ScenarioGameApi;
238
+ players(): readonly PlayerId[];
239
+ /**
240
+ * Resolve the seat at \`index\` in the current scenario's players list.
241
+ * Throws if the index is out of range. Prefer \`seat(0)\`/\`seat(1)\` over
242
+ * literal player ids so scenarios stay portable across player counts and
243
+ * we never hard-code wire-shape assumptions like "player-1".
244
+ */
245
+ seat(index: number): PlayerId;
246
+ state(): StateName;
247
+ view(playerId: PlayerId): GameView;
248
+ interactions(playerId: PlayerId): readonly InteractionDescriptorFor[];
249
+ explain(playerId: PlayerId, interactionId: InteractionId): InteractionExplanation;
250
+ expect: ExpectFn;
251
+ }
252
+
253
+ export type ScenarioContext<
254
+ Phase extends PhaseName | undefined = undefined,
255
+ > = Omit<SharedScenarioContext, "state" | "view"> & {
256
+ state(): Phase extends PhaseName ? Phase : StateName;
257
+ view(playerId: PlayerId): Phase extends PhaseName ? ViewByPhase[Phase] : GameView;
258
+ };
259
+
260
+ export type ScenarioThenContext<
261
+ _Runners extends readonly TestRunner[] = DefaultRunners,
262
+ Phase extends PhaseName | undefined = undefined,
263
+ > = ScenarioContext<Phase>;
264
+
265
+ export interface BaseDefinition {
266
+ id: string;
267
+ seed?: number;
268
+ players?: number;
269
+ setupProfileId?: SetupProfileId;
270
+ extends?: BaseId | string;
271
+ setup: (ctx: BaseContext) => void | Promise<void>;
272
+ }
273
+
274
+ export interface ScenarioDefinition<
275
+ Runners extends readonly TestRunner[] = DefaultRunners,
276
+ Phase extends PhaseName | undefined = undefined,
277
+ > {
278
+ id: string;
279
+ description?: string;
280
+ from: BaseId | string;
281
+ runners?: Runners;
282
+ phase?: Phase;
283
+ stage?: Phase extends PhaseName ? WorkspaceStageName<Phase> : never;
284
+ when: (ctx: ScenarioContext<Phase>) => void | Promise<void>;
285
+ then: (ctx: ScenarioThenContext<Runners, Phase>) => void | Promise<void>;
286
+ }
287
+
288
+ export type {
289
+ GameView,
290
+ InteractionId,
291
+ InteractionParamsOf,
292
+ PhaseName,
293
+ WorkspaceStageName,
294
+ };
295
+ `;
296
+ }
297
+
298
+ export {
299
+ REDUCER_TESTING_TYPES_WRAPPER_CONTENT,
300
+ buildReducerTestingContractContent
301
+ };
302
+ //# sourceMappingURL=chunk-F2DIOJJZ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/templates/testing-types-content.ts"],"sourcesContent":["const DEFAULT_REJECTION_CODES = [\n \"NOT_YOUR_TURN\",\n \"action-unavailable\",\n \"invalid-action-params\",\n \"prompt-not-owned\",\n] as const;\n\nfunction renderLiteralUnion(values: readonly string[]): string {\n if (values.length === 0) {\n return \"never\";\n }\n return values.map((value) => JSON.stringify(value)).join(\" | \");\n}\n\nexport const REDUCER_TESTING_TYPES_WRAPPER_CONTENT = `\\\n// Generated by dreamboard — do not edit by hand.\nimport game from \"../app/game\";\nimport {\n contractFingerprint,\n createReducerBundle,\n} from \"@dreamboard-games/sdk/reducer\";\nimport { createTestRuntime as createDreamboardTestRuntime } from \"@dreamboard-games/sdk/testing\";\nimport type { CreateTestRuntimeOptions } from \"@dreamboard-games/sdk/testing\";\nimport { literals } from \"../shared/manifest-contract\";\nimport type { PhaseName } from \"../shared/generated/ui-contract\";\nimport {\n BASE_STATES,\n BASE_STATES_CONTRACT_FINGERPRINT,\n} from \"./generated/base-states.generated\";\nimport type {\n BaseDefinition,\n ScenarioDefinition,\n TestRunner,\n} from \"./generated/testing-contract\";\n\nexport * from \"./generated/testing-contract\";\n\n/**\n * Workspace-narrowed \\`defineBase\\` wrapper. Accepts the generated\n * \\`BaseDefinition\\` so \\`setup({ seat, game })\\` is typed against the\n * workspace's player ids and interaction contract.\n */\nexport function defineBase<const Definition extends BaseDefinition>(\n definition: Definition,\n): Definition {\n return definition;\n}\n\n/**\n * Workspace-narrowed \\`defineScenario\\` wrapper. The generated\n * \\`ScenarioDefinition\\` narrows \\`ctx.view(playerId)\\` in \\`then\\` based on\n * the declared \\`phase\\`, keeps \\`when\\` union-typed, and constrains\n * \\`phase\\` / \\`stage\\` to the manifest-derived literal types.\n */\nexport function defineScenario<\n const Runners extends readonly TestRunner[] = readonly [\"reducer\"],\n const Phase extends PhaseName | undefined = undefined,\n>(\n definition: ScenarioDefinition<Runners, Phase>,\n): ScenarioDefinition<Runners, Phase> {\n return definition;\n}\n\nexport function createTestRuntime(options: {\n baseId: keyof typeof BASE_STATES & string;\n phase?: PhaseName;\n controllingPlayerId?: (typeof literals.playerIds)[number];\n userId?: string | null;\n}) {\n const reducerBundle =\n createReducerBundle(game) satisfies CreateTestRuntimeOptions[\"bundle\"];\n const baseStates =\n BASE_STATES satisfies CreateTestRuntimeOptions[\"baseStates\"];\n const runtime = createDreamboardTestRuntime({\n baseId: options.baseId,\n baseStates,\n bundle: reducerBundle,\n contractFingerprint: contractFingerprint(game).value,\n expectedBaseStateFingerprint: BASE_STATES_CONTRACT_FINGERPRINT,\n phase: options.phase,\n userId: options.userId ?? \"test-user\",\n playerIds: literals.playerIds.slice(\n 0,\n BASE_STATES[options.baseId]?.fingerprint.players ?? literals.playerIds.length,\n ),\n });\n\n if (options.controllingPlayerId) {\n runtime.setControllingPlayer(options.controllingPlayerId);\n }\n\n return runtime;\n}\n`;\n\nexport function buildReducerTestingContractContent(\n options: {\n rejectionCodes?: readonly string[];\n } = {},\n): string {\n const rejectionCodes = Array.from(\n new Set([...(options.rejectionCodes ?? []), ...DEFAULT_REJECTION_CODES]),\n ).sort((left, right) => left.localeCompare(right));\n\n return `\\\n// Generated by dreamboard — do not edit by hand.\nimport type game from \"../../app/game\";\nimport { literals, type SetupProfileId } from \"../../shared/manifest-contract\";\nimport {\n type GameView,\n type InteractionId,\n type InteractionKey,\n type InteractionParamsOf,\n type PhaseName,\n type StageName as WorkspaceStageName,\n} from \"../../shared/generated/ui-contract\";\nimport type {\n ExpectFn as SharedExpectFn,\n TestRunner as SharedTestRunner,\n} from \"@dreamboard-games/sdk/testing\";\nimport type { InteractionDescriptor } from \"@dreamboard-games/sdk/runtime\";\nimport { BASE_STATES } from \"./base-states.generated\";\n\nexport type GameDefinition = typeof game;\nexport type PlayerId = (typeof literals.playerIds)[number];\nexport type StateName = PhaseName;\nexport type BaseId = keyof typeof BASE_STATES & string;\nexport type InteractionDescriptorFor<Id extends string = string> =\n InteractionDescriptor<Id>;\nexport type InteractionExplanation = {\n interactionId: string;\n phase: string;\n step: string | null;\n availability:\n | \"available\"\n | \"notYourTurn\"\n | \"wrongPhase\"\n | \"wrongStep\"\n | \"blocked\";\n rules: ReadonlyArray<{\n ruleId: string;\n outcome: \"passed\" | \"failed\" | \"notEvaluated\";\n errorCode?: string;\n message?: string;\n }>;\n actor: { required: readonly string[]; playerIsActor: boolean };\n inputs: ReadonlyArray<{\n key: string;\n kind: string;\n eligibleCount: number | \"lazy\";\n }>;\n};\nexport type TestRunner = SharedTestRunner;\nexport type ExpectFn = SharedExpectFn;\nexport type KnownRejectionCode = ${renderLiteralUnion(rejectionCodes)};\nexport type RejectionCode = [KnownRejectionCode] extends [never]\n ? string\n : KnownRejectionCode;\n\ntype DefaultRunners = readonly [\"reducer\"];\ntype PhaseTaggedView<Phase extends PhaseName> = Extract<\n GameView,\n { phase: Phase } | { currentPhase: Phase } | { state: Phase }\n>;\ntype NarrowedView<Phase extends PhaseName> = [PhaseTaggedView<Phase>] extends [never]\n ? GameView\n : PhaseTaggedView<Phase>;\n\nexport type ViewByPhase = {\n [Phase in PhaseName]: NarrowedView<Phase>;\n};\n\ntype InteractionKeyForId<Id extends InteractionId> = Extract<\n InteractionKey,\n \\`\\${string}.\\${Id}\\`\n>;\ntype InteractionParamsForKey<Key extends InteractionKey> =\n Key extends InteractionKey ? InteractionParamsOf<Key> : never;\ntype InteractionParamsOfId<Id extends InteractionId> =\n InteractionParamsForKey<InteractionKeyForId<Id>>;\n\nexport interface BrowserRunnerSnapshot {\n sessionId: string | null;\n shortCode: string | null;\n version: number;\n currentPhase: string | null;\n controllingPlayerId: string;\n controllablePlayerIds: string[];\n view: unknown;\n availableInteractions?: string[];\n}\n\nexport interface BrowserRunnerBridge {\n snapshot(): Promise<BrowserRunnerSnapshot>;\n submitInteraction(\n playerId: PlayerId,\n interactionId: string,\n params: unknown,\n ): Promise<void>;\n}\n\nexport interface BrowserRunnerDriver {\n onReady?(bridge: BrowserRunnerBridge): Promise<void> | void;\n interaction?(\n bridge: BrowserRunnerBridge,\n input: { playerId: PlayerId; interactionId: string; params: unknown },\n ): Promise<boolean | void> | boolean | void;\n}\n\nexport interface ScenarioGameApi {\n start(): Promise<void>;\n /**\n * Patch the reducer snapshot for deterministic setup-heavy scenarios.\n * This is limited to reducer snapshot materialization and is rejected by\n * live replay/browser runners so authored gameplay verification still\n * submits real interactions.\n */\n patchState(mutator: (state: Record<string, unknown>) => void): Promise<void>;\n /**\n * Submit a player interaction (action-kind or prompt-kind) to the game.\n * The \\`interactionId\\` matches an \\`InteractionId\\` from the generated\n * \\`ui-contract\\`; \\`params\\` is typed per interaction id.\n */\n submit<Id extends InteractionId>(\n playerId: PlayerId,\n interactionId: Id,\n params?: InteractionParamsOfId<Id>,\n ): Promise<void>;\n}\n\nexport interface BaseContext {\n game: ScenarioGameApi;\n players(): readonly PlayerId[];\n /**\n * Resolve the seat at \\`index\\` in the base's players list.\n * Throws if the index is out of range. Prefer \\`seat(0)\\`/\\`seat(1)\\` over\n * literal player ids so bases stay portable across player counts and\n * we never hard-code wire-shape assumptions like \"player-1\".\n */\n seat(index: number): PlayerId;\n}\n\nexport interface SharedScenarioContext {\n game: ScenarioGameApi;\n players(): readonly PlayerId[];\n /**\n * Resolve the seat at \\`index\\` in the current scenario's players list.\n * Throws if the index is out of range. Prefer \\`seat(0)\\`/\\`seat(1)\\` over\n * literal player ids so scenarios stay portable across player counts and\n * we never hard-code wire-shape assumptions like \"player-1\".\n */\n seat(index: number): PlayerId;\n state(): StateName;\n view(playerId: PlayerId): GameView;\n interactions(playerId: PlayerId): readonly InteractionDescriptorFor[];\n explain(playerId: PlayerId, interactionId: InteractionId): InteractionExplanation;\n expect: ExpectFn;\n}\n\nexport type ScenarioContext<\n Phase extends PhaseName | undefined = undefined,\n> = Omit<SharedScenarioContext, \"state\" | \"view\"> & {\n state(): Phase extends PhaseName ? Phase : StateName;\n view(playerId: PlayerId): Phase extends PhaseName ? ViewByPhase[Phase] : GameView;\n};\n\nexport type ScenarioThenContext<\n _Runners extends readonly TestRunner[] = DefaultRunners,\n Phase extends PhaseName | undefined = undefined,\n> = ScenarioContext<Phase>;\n\nexport interface BaseDefinition {\n id: string;\n seed?: number;\n players?: number;\n setupProfileId?: SetupProfileId;\n extends?: BaseId | string;\n setup: (ctx: BaseContext) => void | Promise<void>;\n}\n\nexport interface ScenarioDefinition<\n Runners extends readonly TestRunner[] = DefaultRunners,\n Phase extends PhaseName | undefined = undefined,\n> {\n id: string;\n description?: string;\n from: BaseId | string;\n runners?: Runners;\n phase?: Phase;\n stage?: Phase extends PhaseName ? WorkspaceStageName<Phase> : never;\n when: (ctx: ScenarioContext<Phase>) => void | Promise<void>;\n then: (ctx: ScenarioThenContext<Runners, Phase>) => void | Promise<void>;\n}\n\nexport type {\n GameView,\n InteractionId,\n InteractionParamsOf,\n PhaseName,\n WorkspaceStageName,\n};\n`;\n}\n"],"mappings":";;;AAAA,IAAM,0BAA0B;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,mBAAmB,QAAmC;AAC7D,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO;AAAA,EACT;AACA,SAAO,OAAO,IAAI,CAAC,UAAU,KAAK,UAAU,KAAK,CAAC,EAAE,KAAK,KAAK;AAChE;AAEO,IAAM,wCAAwC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiF9C,SAAS,mCACd,UAEI,CAAC,GACG;AACR,QAAM,iBAAiB,MAAM;AAAA,IAC3B,oBAAI,IAAI,CAAC,GAAI,QAAQ,kBAAkB,CAAC,GAAI,GAAG,uBAAuB,CAAC;AAAA,EACzE,EAAE,KAAK,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC;AAEjD,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mCAkD0B,mBAAmB,cAAc,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoJrE;","names":[]}
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ readTextFileIfExists,
4
+ writeTextFile
5
+ } from "./chunk-IAYRNVUC.mjs";
6
+
7
+ // src/services/project/workspace-codegen.ts
8
+ import {
9
+ generateAuthoritativeFiles,
10
+ generateSeedFiles,
11
+ isFrameworkOwnedSetupProfilesSeed
12
+ } from "@dreamboard-games/sdk/codegen";
13
+ var STARTER_UI_SEED_FILES = /* @__PURE__ */ new Set([
14
+ "ui/interaction-routes.tsx",
15
+ "ui/setup-screen.tsx",
16
+ "ui/styles.ts",
17
+ "ui/ui-contract-typing-smoke.tsx"
18
+ ]);
19
+ async function applyWorkspaceCodegen(options) {
20
+ const { projectRoot, manifest } = options;
21
+ const authoritativeFiles = generateAuthoritativeFiles(manifest);
22
+ const seedFiles = generateSeedFiles(manifest);
23
+ const written = [];
24
+ const skipped = [];
25
+ const merged = [];
26
+ const existingUiAppBeforeSeeds = await readTextFileIfExists(
27
+ `${projectRoot}/ui/App.tsx`
28
+ );
29
+ const shouldWriteStarterUiSeedFiles = existingUiAppBeforeSeeds === null || existingUiAppBeforeSeeds.trim().length === 0;
30
+ for (const [relativePath, content] of Object.entries(authoritativeFiles)) {
31
+ const filePath = `${projectRoot}/${relativePath}`;
32
+ const existingContent = await readTextFileIfExists(filePath);
33
+ await writeTextFile(filePath, content);
34
+ if (existingContent !== content) {
35
+ written.push(relativePath);
36
+ }
37
+ }
38
+ for (const [relativePath, content] of Object.entries(seedFiles)) {
39
+ const filePath = `${projectRoot}/${relativePath}`;
40
+ const existingContent = await readTextFileIfExists(filePath);
41
+ if (STARTER_UI_SEED_FILES.has(relativePath) && !shouldWriteStarterUiSeedFiles && existingContent === null) {
42
+ skipped.push(relativePath);
43
+ continue;
44
+ }
45
+ const shouldRefreshFrameworkSeed = relativePath === "app/setup-profiles.ts" && isFrameworkOwnedSetupProfilesSeed(existingContent);
46
+ if (shouldRefreshFrameworkSeed) {
47
+ await writeTextFile(filePath, content);
48
+ if (existingContent !== content) {
49
+ written.push(relativePath);
50
+ }
51
+ continue;
52
+ }
53
+ const hasExistingContent = existingContent !== null && existingContent.trim().length > 0;
54
+ if (hasExistingContent) {
55
+ skipped.push(relativePath);
56
+ continue;
57
+ }
58
+ await writeTextFile(filePath, content);
59
+ written.push(relativePath);
60
+ }
61
+ written.sort();
62
+ skipped.sort();
63
+ merged.sort();
64
+ return { written, skipped, merged };
65
+ }
66
+
67
+ export {
68
+ applyWorkspaceCodegen
69
+ };
70
+ //# sourceMappingURL=chunk-G42BGGG2.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 {\n generateAuthoritativeFiles,\n generateSeedFiles,\n isFrameworkOwnedSetupProfilesSeed,\n} from \"@dreamboard-games/sdk/codegen\";\nimport { readTextFileIfExists, writeTextFile } from \"../../utils/fs.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]);\n\nexport async function applyWorkspaceCodegen(options: {\n projectRoot: string;\n manifest: GameTopologyManifest;\n}): Promise<WorkspaceCodegenWriteResult> {\n const { projectRoot, manifest } = options;\n const authoritativeFiles = generateAuthoritativeFiles(manifest);\n const seedFiles = generateSeedFiles(manifest);\n\n const written: string[] = [];\n const skipped: string[] = [];\n const merged: string[] = [];\n const existingUiAppBeforeSeeds = await readTextFileIfExists(\n `${projectRoot}/ui/App.tsx`,\n );\n const shouldWriteStarterUiSeedFiles =\n existingUiAppBeforeSeeds === null ||\n existingUiAppBeforeSeeds.trim().length === 0;\n\n for (const [relativePath, content] of Object.entries(authoritativeFiles)) {\n const filePath = `${projectRoot}/${relativePath}`;\n const existingContent = await readTextFileIfExists(filePath);\n await writeTextFile(filePath, content);\n if (existingContent !== content) {\n written.push(relativePath);\n }\n }\n\n for (const [relativePath, content] of Object.entries(seedFiles)) {\n const filePath = `${projectRoot}/${relativePath}`;\n const existingContent = await readTextFileIfExists(filePath);\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 writeTextFile(filePath, 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 writeTextFile(filePath, 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;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AASP,IAAM,wBAAwB,oBAAI,IAAI;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,eAAsB,sBAAsB,SAGH;AACvC,QAAM,EAAE,aAAa,SAAS,IAAI;AAClC,QAAM,qBAAqB,2BAA2B,QAAQ;AAC9D,QAAM,YAAY,kBAAkB,QAAQ;AAE5C,QAAM,UAAoB,CAAC;AAC3B,QAAM,UAAoB,CAAC;AAC3B,QAAM,SAAmB,CAAC;AAC1B,QAAM,2BAA2B,MAAM;AAAA,IACrC,GAAG,WAAW;AAAA,EAChB;AACA,QAAM,gCACJ,6BAA6B,QAC7B,yBAAyB,KAAK,EAAE,WAAW;AAE7C,aAAW,CAAC,cAAc,OAAO,KAAK,OAAO,QAAQ,kBAAkB,GAAG;AACxE,UAAM,WAAW,GAAG,WAAW,IAAI,YAAY;AAC/C,UAAM,kBAAkB,MAAM,qBAAqB,QAAQ;AAC3D,UAAM,cAAc,UAAU,OAAO;AACrC,QAAI,oBAAoB,SAAS;AAC/B,cAAQ,KAAK,YAAY;AAAA,IAC3B;AAAA,EACF;AAEA,aAAW,CAAC,cAAc,OAAO,KAAK,OAAO,QAAQ,SAAS,GAAG;AAC/D,UAAM,WAAW,GAAG,WAAW,IAAI,YAAY;AAC/C,UAAM,kBAAkB,MAAM,qBAAqB,QAAQ;AAC3D,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,cAAc,UAAU,OAAO;AACrC,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,cAAc,UAAU,OAAO;AACrC,YAAQ,KAAK,YAAY;AAAA,EAC3B;AAEA,UAAQ,KAAK;AACb,UAAQ,KAAK;AACb,SAAO,KAAK;AACZ,SAAO,EAAE,SAAS,SAAS,OAAO;AACpC;","names":[]}
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/constants.ts
4
+ var DEFAULT_API_BASE_URL = "https://api.dreamboard.games";
5
+ var DEFAULT_WEB_BASE_URL = "https://dreamboard.games";
6
+ var PROJECT_DIR_NAME = ".dreamboard";
7
+ var DEFAULT_CLERK_OAUTH_SCOPE = "openid profile email offline_access";
8
+ var ENVIRONMENT_CONFIGS = {
9
+ local: {
10
+ apiBaseUrl: "http://localhost:8080",
11
+ webBaseUrl: "http://localhost:5173",
12
+ clerkOAuthScope: DEFAULT_CLERK_OAUTH_SCOPE
13
+ },
14
+ staging: {
15
+ apiBaseUrl: "https://api-staging.dreamboard.games",
16
+ webBaseUrl: "https://staging.dreamboard.games",
17
+ clerkOAuthIssuer: "https://happy-caribou-19.clerk.accounts.dev",
18
+ clerkOAuthTokenUrl: "https://happy-caribou-19.clerk.accounts.dev/oauth/token",
19
+ clerkOAuthScope: DEFAULT_CLERK_OAUTH_SCOPE
20
+ },
21
+ prod: {
22
+ apiBaseUrl: "https://api.dreamboard.games",
23
+ webBaseUrl: "https://dreamboard.games",
24
+ clerkOAuthScope: DEFAULT_CLERK_OAUTH_SCOPE
25
+ }
26
+ };
27
+ var PROJECT_CONFIG_FILE = "project.json";
28
+ var PROJECT_STATE_FILE = "state.json";
29
+ var SNAPSHOT_FILE = "snapshot.json";
30
+ var MANIFEST_FILE = "manifest.ts";
31
+ var GENERATED_DIR_NAME = "generated";
32
+ var MATERIALIZED_MANIFEST_FILE = `${PROJECT_DIR_NAME}/${GENERATED_DIR_NAME}/manifest.json`;
33
+ var MANIFEST_TYPECHECK_CONFIG_FILE = "manifest.tsconfig.json";
34
+ var RULE_FILE = "rule.md";
35
+ var DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60 * 1e3;
36
+ var LOCAL_IGNORE_DIRS = /* @__PURE__ */ new Set([
37
+ ".dreamboard",
38
+ ".git",
39
+ "node_modules",
40
+ "dist"
41
+ ]);
42
+
43
+ export {
44
+ DEFAULT_API_BASE_URL,
45
+ DEFAULT_WEB_BASE_URL,
46
+ PROJECT_DIR_NAME,
47
+ ENVIRONMENT_CONFIGS,
48
+ PROJECT_CONFIG_FILE,
49
+ PROJECT_STATE_FILE,
50
+ SNAPSHOT_FILE,
51
+ MANIFEST_FILE,
52
+ MATERIALIZED_MANIFEST_FILE,
53
+ MANIFEST_TYPECHECK_CONFIG_FILE,
54
+ RULE_FILE,
55
+ LOCAL_IGNORE_DIRS
56
+ };
57
+ //# sourceMappingURL=chunk-H76MT5UR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/constants.ts"],"sourcesContent":["import type { EnvironmentConfig } from \"./types.js\";\n\nexport const DEFAULT_API_BASE_URL = \"https://api.dreamboard.games\";\nexport const DEFAULT_WEB_BASE_URL = \"https://dreamboard.games\";\n\nexport const PROJECT_DIR_NAME = \".dreamboard\";\nexport const DEFAULT_CLERK_OAUTH_SCOPE =\n \"openid profile email offline_access\";\n\n// Predefined environment configurations. These are intentionally static:\n// process/env overrides are applied in config resolution so the CLI does not\n// depend on a shell-sourced env file just to know first-party environments.\nexport const ENVIRONMENT_CONFIGS: Record<string, EnvironmentConfig> = {\n local: {\n apiBaseUrl: \"http://localhost:8080\",\n webBaseUrl: \"http://localhost:5173\",\n clerkOAuthScope: DEFAULT_CLERK_OAUTH_SCOPE,\n },\n staging: {\n apiBaseUrl: \"https://api-staging.dreamboard.games\",\n webBaseUrl: \"https://staging.dreamboard.games\",\n clerkOAuthIssuer: \"https://happy-caribou-19.clerk.accounts.dev\",\n clerkOAuthTokenUrl:\n \"https://happy-caribou-19.clerk.accounts.dev/oauth/token\",\n clerkOAuthScope: DEFAULT_CLERK_OAUTH_SCOPE,\n },\n prod: {\n apiBaseUrl: \"https://api.dreamboard.games\",\n webBaseUrl: \"https://dreamboard.games\",\n clerkOAuthScope: DEFAULT_CLERK_OAUTH_SCOPE,\n },\n};\nexport const PROJECT_CONFIG_FILE = \"project.json\";\nexport const PROJECT_STATE_FILE = \"state.json\";\nexport const SNAPSHOT_FILE = \"snapshot.json\";\nexport const MANIFEST_FILE = \"manifest.ts\";\nexport const GENERATED_DIR_NAME = \"generated\";\nexport const MATERIALIZED_MANIFEST_FILE = `${PROJECT_DIR_NAME}/${GENERATED_DIR_NAME}/manifest.json`;\nexport const MANIFEST_TYPECHECK_CONFIG_FILE = \"manifest.tsconfig.json\";\nexport const RULE_FILE = \"rule.md\";\nexport const DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60 * 1000;\nexport const DEFAULT_TURN_DELAY_MS = 250;\n\nexport const LOCAL_IGNORE_DIRS = new Set([\n \".dreamboard\",\n \".git\",\n \"node_modules\",\n \"dist\",\n]);\n"],"mappings":";;;AAEO,IAAM,uBAAuB;AAC7B,IAAM,uBAAuB;AAE7B,IAAM,mBAAmB;AACzB,IAAM,4BACX;AAKK,IAAM,sBAAyD;AAAA,EACpE,OAAO;AAAA,IACL,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,iBAAiB;AAAA,EACnB;AAAA,EACA,SAAS;AAAA,IACP,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,oBACE;AAAA,IACF,iBAAiB;AAAA,EACnB;AAAA,EACA,MAAM;AAAA,IACJ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,iBAAiB;AAAA,EACnB;AACF;AACO,IAAM,sBAAsB;AAC5B,IAAM,qBAAqB;AAC3B,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AACtB,IAAM,qBAAqB;AAC3B,IAAM,6BAA6B,GAAG,gBAAgB,IAAI,kBAAkB;AAC5E,IAAM,iCAAiC;AACvC,IAAM,YAAY;AAClB,IAAM,2BAA2B,IAAI,KAAK;AAG1C,IAAM,oBAAoB,oBAAI,IAAI;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;","names":[]}
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ensureProjectSdk,
4
+ loadRemoteProjectIdentity
5
+ } from "./chunk-W3N3QJ4V.mjs";
6
+ import {
7
+ updateProjectState
8
+ } from "./chunk-776W3UGV.mjs";
9
+
10
+ // src/services/project/remote-project.ts
11
+ async function resolveRemoteProject(options) {
12
+ const identity = await loadRemoteProjectIdentity();
13
+ const project = await ensureProjectSdk({
14
+ projectId: options.projectConfig.projectId,
15
+ slug: options.projectConfig.slug,
16
+ updateAlias: options.updateAlias
17
+ });
18
+ const nextProjectConfig = {
19
+ ...options.projectConfig,
20
+ slug: project.slug,
21
+ deploymentId: identity.deploymentId,
22
+ ownerScopeId: identity.ownerScopeId,
23
+ bindingKey: identity.bindingKey,
24
+ remoteHeadDigest: project.head?.revisionDigest,
25
+ apiBaseUrl: options.config.apiBaseUrl,
26
+ webBaseUrl: options.config.webBaseUrl
27
+ };
28
+ await updateProjectState(options.projectRoot, nextProjectConfig);
29
+ return {
30
+ identity,
31
+ project,
32
+ projectConfig: nextProjectConfig
33
+ };
34
+ }
35
+
36
+ export {
37
+ resolveRemoteProject
38
+ };
39
+ //# sourceMappingURL=chunk-HGMUAL33.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/project/remote-project.ts"],"sourcesContent":["import type { Project } from \"@dreamboard-games/api-client\";\nimport { updateProjectState } from \"../../config/project-config.js\";\nimport type { ProjectConfig, ResolvedConfig } from \"../../types.js\";\nimport {\n ensureProjectSdk,\n loadRemoteProjectIdentity,\n type RemoteProjectIdentity,\n} from \"../api/index.js\";\n\nexport type ResolvedRemoteProject = {\n identity: RemoteProjectIdentity;\n project: Project;\n projectConfig: ProjectConfig;\n};\n\nexport async function resolveRemoteProject(options: {\n projectRoot: string;\n projectConfig: ProjectConfig;\n config: Pick<ResolvedConfig, \"apiBaseUrl\" | \"webBaseUrl\">;\n updateAlias?: boolean;\n}): Promise<ResolvedRemoteProject> {\n const identity = await loadRemoteProjectIdentity();\n const project = await ensureProjectSdk({\n projectId: options.projectConfig.projectId,\n slug: options.projectConfig.slug,\n updateAlias: options.updateAlias,\n });\n const nextProjectConfig: ProjectConfig = {\n ...options.projectConfig,\n slug: project.slug,\n deploymentId: identity.deploymentId,\n ownerScopeId: identity.ownerScopeId,\n bindingKey: identity.bindingKey,\n remoteHeadDigest: project.head?.revisionDigest,\n apiBaseUrl: options.config.apiBaseUrl,\n webBaseUrl: options.config.webBaseUrl,\n };\n\n await updateProjectState(options.projectRoot, nextProjectConfig);\n\n return {\n identity,\n project,\n projectConfig: nextProjectConfig,\n };\n}\n"],"mappings":";;;;;;;;;;AAeA,eAAsB,qBAAqB,SAKR;AACjC,QAAM,WAAW,MAAM,0BAA0B;AACjD,QAAM,UAAU,MAAM,iBAAiB;AAAA,IACrC,WAAW,QAAQ,cAAc;AAAA,IACjC,MAAM,QAAQ,cAAc;AAAA,IAC5B,aAAa,QAAQ;AAAA,EACvB,CAAC;AACD,QAAM,oBAAmC;AAAA,IACvC,GAAG,QAAQ;AAAA,IACX,MAAM,QAAQ;AAAA,IACd,cAAc,SAAS;AAAA,IACvB,cAAc,SAAS;AAAA,IACvB,YAAY,SAAS;AAAA,IACrB,kBAAkB,QAAQ,MAAM;AAAA,IAChC,YAAY,QAAQ,OAAO;AAAA,IAC3B,YAAY,QAAQ,OAAO;AAAA,EAC7B;AAEA,QAAM,mBAAmB,QAAQ,aAAa,iBAAiB;AAE/D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,eAAe;AAAA,EACjB;AACF;","names":[]}
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/utils/fs.ts
4
+ import { mkdir, readFile, stat, writeFile } from "fs/promises";
5
+ import path from "path";
6
+ async function ensureDir(dirPath) {
7
+ await mkdir(dirPath, { recursive: true });
8
+ }
9
+ async function exists(filePath) {
10
+ try {
11
+ await stat(filePath);
12
+ return true;
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
17
+ async function readTextFile(filePath) {
18
+ return readFile(filePath, "utf8");
19
+ }
20
+ async function readTextFileIfExists(filePath) {
21
+ try {
22
+ return await readFile(filePath, "utf8");
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+ async function writeTextFile(filePath, content) {
28
+ await ensureDir(path.dirname(filePath));
29
+ await writeFile(filePath, content, "utf8");
30
+ }
31
+ async function readJsonFile(filePath) {
32
+ const data = await readTextFile(filePath);
33
+ return JSON.parse(data);
34
+ }
35
+ async function writeJsonFile(filePath, data) {
36
+ await writeTextFile(filePath, `${JSON.stringify(data, null, 2)}
37
+ `);
38
+ }
39
+
40
+ export {
41
+ ensureDir,
42
+ exists,
43
+ readTextFile,
44
+ readTextFileIfExists,
45
+ writeTextFile,
46
+ readJsonFile,
47
+ writeJsonFile
48
+ };
49
+ //# sourceMappingURL=chunk-IAYRNVUC.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/fs.ts"],"sourcesContent":["import { mkdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n await mkdir(dirPath, { recursive: true });\n}\n\nexport async function exists(filePath: string): Promise<boolean> {\n try {\n await stat(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readTextFile(filePath: string): Promise<string> {\n return readFile(filePath, \"utf8\");\n}\n\nexport async function readTextFileIfExists(\n filePath: string,\n): Promise<string | null> {\n try {\n return await readFile(filePath, \"utf8\");\n } catch {\n return null;\n }\n}\n\nexport async function writeTextFile(\n filePath: string,\n content: string,\n): Promise<void> {\n await ensureDir(path.dirname(filePath));\n await writeFile(filePath, content, \"utf8\");\n}\n\nexport async function readJsonFile<T>(filePath: string): Promise<T> {\n const data = await readTextFile(filePath);\n return JSON.parse(data) as T;\n}\n\nexport async function writeJsonFile(\n filePath: string,\n data: unknown,\n): Promise<void> {\n await writeTextFile(filePath, `${JSON.stringify(data, null, 2)}\\n`);\n}\n"],"mappings":";;;AAAA,SAAS,OAAO,UAAU,MAAM,iBAAiB;AACjD,OAAO,UAAU;AAEjB,eAAsB,UAAU,SAAgC;AAC9D,QAAM,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAC1C;AAEA,eAAsB,OAAO,UAAoC;AAC/D,MAAI;AACF,UAAM,KAAK,QAAQ;AACnB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,aAAa,UAAmC;AACpE,SAAO,SAAS,UAAU,MAAM;AAClC;AAEA,eAAsB,qBACpB,UACwB;AACxB,MAAI;AACF,WAAO,MAAM,SAAS,UAAU,MAAM;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,cACpB,UACA,SACe;AACf,QAAM,UAAU,KAAK,QAAQ,QAAQ,CAAC;AACtC,QAAM,UAAU,UAAU,SAAS,MAAM;AAC3C;AAEA,eAAsB,aAAgB,UAA8B;AAClE,QAAM,OAAO,MAAM,aAAa,QAAQ;AACxC,SAAO,KAAK,MAAM,IAAI;AACxB;AAEA,eAAsB,cACpB,UACA,MACe;AACf,QAAM,cAAc,UAAU,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,CAAI;AACpE;","names":[]}
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ importTypeScriptModule
4
+ } from "./chunk-LUZ7KE6H.mjs";
5
+
6
+ // src/services/project/reducer-contract-preflight.ts
7
+ import path from "path";
8
+ var GAME_CONTRACT_ENTRY_PATH = path.join("app", "game-contract.ts");
9
+ function normalizeErrorMessage(error) {
10
+ const rawMessage = error instanceof Error ? error.message : String(error ?? "Unknown error");
11
+ return rawMessage.split("\n").map((line) => line.trim()).find(Boolean)?.replace(/^Error:\s*/u, "") ?? "Unknown error";
12
+ }
13
+ function isManifestScopedIdBrandingError(message) {
14
+ return message.includes("defineGameContract:") && message.includes("manifest-scoped") && (message.includes("uses a raw z.string()") || message.includes("uses z.array(z.string())"));
15
+ }
16
+ async function assertReducerContractPreflight(projectRoot) {
17
+ const entryPath = path.join(projectRoot, GAME_CONTRACT_ENTRY_PATH);
18
+ try {
19
+ await importTypeScriptModule(entryPath);
20
+ } catch (error) {
21
+ const message = normalizeErrorMessage(error);
22
+ if (isManifestScopedIdBrandingError(message)) {
23
+ throw new Error(
24
+ [
25
+ `Dreamboard could not validate \`${GAME_CONTRACT_ENTRY_PATH}\` during \`dreamboard sync\`.`,
26
+ "This happens because a state field name looks like a manifest-scoped id, but the schema uses a plain string instead of the manifest-backed id schema.",
27
+ "Workaround: use `gameContract.schemas.<id>` (or `manifest.ids.<id>`) for manifest ids. If the field is intentionally free-form text, rename it so it does not look like a manifest id field.",
28
+ `Original error: ${message}`
29
+ ].join(" ")
30
+ );
31
+ }
32
+ throw new Error(
33
+ [
34
+ `Dreamboard could not validate \`${GAME_CONTRACT_ENTRY_PATH}\` during \`dreamboard sync\`.`,
35
+ "Fix the authored reducer contract module so it can be imported locally, then run `dreamboard sync` again.",
36
+ `Original error: ${message}`
37
+ ].join(" ")
38
+ );
39
+ }
40
+ }
41
+
42
+ export {
43
+ assertReducerContractPreflight
44
+ };
45
+ //# sourceMappingURL=chunk-JGT4P4UD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/project/reducer-contract-preflight.ts"],"sourcesContent":["import path from \"node:path\";\nimport { importTypeScriptModule } from \"../../utils/ts-module-loader.js\";\n\nconst GAME_CONTRACT_ENTRY_PATH = path.join(\"app\", \"game-contract.ts\");\n\nfunction normalizeErrorMessage(error: unknown): string {\n const rawMessage =\n error instanceof Error ? error.message : String(error ?? \"Unknown error\");\n return (\n rawMessage\n .split(\"\\n\")\n .map((line) => line.trim())\n .find(Boolean)\n ?.replace(/^Error:\\s*/u, \"\") ?? \"Unknown error\"\n );\n}\n\nfunction isManifestScopedIdBrandingError(message: string): boolean {\n return (\n message.includes(\"defineGameContract:\") &&\n message.includes(\"manifest-scoped\") &&\n (message.includes(\"uses a raw z.string()\") ||\n message.includes(\"uses z.array(z.string())\"))\n );\n}\n\nexport async function assertReducerContractPreflight(\n projectRoot: string,\n): Promise<void> {\n const entryPath = path.join(projectRoot, GAME_CONTRACT_ENTRY_PATH);\n\n try {\n await importTypeScriptModule(entryPath);\n } catch (error) {\n const message = normalizeErrorMessage(error);\n if (isManifestScopedIdBrandingError(message)) {\n throw new Error(\n [\n `Dreamboard could not validate \\`${GAME_CONTRACT_ENTRY_PATH}\\` during \\`dreamboard sync\\`.`,\n \"This happens because a state field name looks like a manifest-scoped id, but the schema uses a plain string instead of the manifest-backed id schema.\",\n \"Workaround: use `gameContract.schemas.<id>` (or `manifest.ids.<id>`) for manifest ids. If the field is intentionally free-form text, rename it so it does not look like a manifest id field.\",\n `Original error: ${message}`,\n ].join(\" \"),\n );\n }\n\n throw new Error(\n [\n `Dreamboard could not validate \\`${GAME_CONTRACT_ENTRY_PATH}\\` during \\`dreamboard sync\\`.`,\n \"Fix the authored reducer contract module so it can be imported locally, then run `dreamboard sync` again.\",\n `Original error: ${message}`,\n ].join(\" \"),\n );\n }\n}\n"],"mappings":";;;;;;AAAA,OAAO,UAAU;AAGjB,IAAM,2BAA2B,KAAK,KAAK,OAAO,kBAAkB;AAEpE,SAAS,sBAAsB,OAAwB;AACrD,QAAM,aACJ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,SAAS,eAAe;AAC1E,SACE,WACG,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,KAAK,OAAO,GACX,QAAQ,eAAe,EAAE,KAAK;AAEtC;AAEA,SAAS,gCAAgC,SAA0B;AACjE,SACE,QAAQ,SAAS,qBAAqB,KACtC,QAAQ,SAAS,iBAAiB,MACjC,QAAQ,SAAS,uBAAuB,KACvC,QAAQ,SAAS,0BAA0B;AAEjD;AAEA,eAAsB,+BACpB,aACe;AACf,QAAM,YAAY,KAAK,KAAK,aAAa,wBAAwB;AAEjE,MAAI;AACF,UAAM,uBAAuB,SAAS;AAAA,EACxC,SAAS,OAAO;AACd,UAAM,UAAU,sBAAsB,KAAK;AAC3C,QAAI,gCAAgC,OAAO,GAAG;AAC5C,YAAM,IAAI;AAAA,QACR;AAAA,UACE,mCAAmC,wBAAwB;AAAA,UAC3D;AAAA,UACA;AAAA,UACA,mBAAmB,OAAO;AAAA,QAC5B,EAAE,KAAK,GAAG;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,IAAI;AAAA,MACR;AAAA,QACE,mCAAmC,wBAAwB;AAAA,QAC3D;AAAA,QACA,mBAAmB,OAAO;AAAA,MAC5B,EAAE,KAAK,GAAG;AAAA,IACZ;AAAA,EACF;AACF;","names":[]}