@seriousgaming/sdk-tools 0.0.0-stage → 1.0.0-alpha.2
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/LICENSE +93 -0
- package/README.md +38 -2
- package/dist/bundling/logic-exports.d.ts +34 -0
- package/dist/bundling/node-builtins.d.ts +21 -0
- package/dist/bundling/transpiler.d.ts +7 -0
- package/dist/cli.js +27404 -0
- package/dist/client/dev-server.d.ts +59 -0
- package/dist/client/index.d.ts +28 -0
- package/dist/client/stub-services.d.ts +32 -0
- package/dist/client/test-session.d.ts +195 -0
- package/dist/client.js +16920 -0
- package/dist/core/asset-reference-translator.d.ts +46 -0
- package/dist/core/clock.d.ts +57 -0
- package/dist/core/faults.d.ts +65 -0
- package/dist/core/game-settings.d.ts +65 -0
- package/dist/core/history.d.ts +89 -0
- package/dist/core/listen-with-fallback.d.ts +30 -0
- package/dist/core/mutation-queue.d.ts +61 -0
- package/dist/core/package-scenario.d.ts +89 -0
- package/dist/core/participant-registry.d.ts +109 -0
- package/dist/core/pending-changes.d.ts +14 -0
- package/dist/core/protocol.d.ts +83 -0
- package/dist/core/scenario-drafts.d.ts +32 -0
- package/dist/core/session-endpoints.d.ts +108 -0
- package/dist/core/session-gates.d.ts +75 -0
- package/dist/core/session-host.d.ts +94 -0
- package/dist/core/session-refusals.d.ts +56 -0
- package/dist/core/session-rewind.d.ts +32 -0
- package/dist/core/session-roster.d.ts +128 -0
- package/dist/core/session-surfaces.d.ts +32 -0
- package/dist/core/session-ticks.d.ts +18 -0
- package/dist/core/session-timers.d.ts +84 -0
- package/dist/core/session-writes.d.ts +40 -0
- package/dist/core/session.d.ts +385 -0
- package/dist/core/settings-locks.d.ts +22 -0
- package/dist/core/team-names.d.ts +52 -0
- package/dist/core/team-state-store.d.ts +218 -0
- package/dist/core/tick-timer.d.ts +75 -0
- package/dist/dev/blob-links.d.ts +10 -0
- package/dist/dev/console/assets/index-DgFglsnA.js +204 -0
- package/dist/dev/console/index.html +12 -0
- package/dist/dev/console-path.d.ts +19 -0
- package/dist/dev/lockfile.d.ts +124 -0
- package/dist/dev/package-watcher.d.ts +21 -0
- package/dist/dev/routes.d.ts +67 -0
- package/dist/dev/shell.d.ts +260 -0
- package/dist/dev/surfaces.d.ts +41 -0
- package/dist/dev/tools-identity.d.ts +23 -0
- package/dist/dev/view/batch.d.ts +172 -0
- package/dist/dev/view/commands.d.ts +75 -0
- package/dist/dev/view/route.d.ts +58 -0
- package/dist/dev/view/view.d.ts +78 -0
- package/dist/dev/view/viewer.d.ts +32 -0
- package/dist/discovery/config.d.ts +9 -0
- package/dist/discovery/manifest-paths.d.ts +21 -0
- package/dist/discovery/removed-fields.d.ts +90 -0
- package/dist/docs/api/deck.md +386 -0
- package/dist/docs/api/index.md +67 -0
- package/dist/docs/api/sdk-commands.md +286 -0
- package/dist/docs/api/sdk-e2e.md +433 -0
- package/dist/docs/api/sdk-projection.md +207 -0
- package/dist/docs/api/sdk-testing.md +214 -0
- package/dist/docs/api/sdk-tools-client.md +560 -0
- package/dist/docs/api/sdk-ui-testing.md +231 -0
- package/dist/docs/api/sdk-ui.md +1269 -0
- package/dist/docs/api/sdk.md +2140 -0
- package/dist/docs/changelog-0.x.md +1156 -0
- package/dist/docs/changelog.md +241 -0
- package/dist/docs/cli-and-dev-server.md +217 -0
- package/dist/docs/cli-error-codes.md +156 -0
- package/dist/docs/cli-reference.md +2489 -0
- package/dist/docs/cli-simulation-testing.md +222 -0
- package/dist/docs/concepts/index.md +46 -0
- package/dist/docs/concepts/terminology.md +322 -0
- package/dist/docs/environment-variables.md +102 -0
- package/dist/docs/getting-started.md +145 -0
- package/dist/docs/guides/ai-facilitation.md +220 -0
- package/dist/docs/guides/capabilities.md +460 -0
- package/dist/docs/guides/endpoints.md +335 -0
- package/dist/docs/guides/engine-testing.md +477 -0
- package/dist/docs/guides/engine.md +527 -0
- package/dist/docs/guides/file-handling.md +405 -0
- package/dist/docs/guides/games.md +176 -0
- package/dist/docs/guides/global-settings.md +94 -0
- package/dist/docs/guides/guide.md +123 -0
- package/dist/docs/guides/hooks.md +183 -0
- package/dist/docs/guides/index.md +51 -0
- package/dist/docs/guides/internationalization.md +288 -0
- package/dist/docs/guides/llm-integration.md +635 -0
- package/dist/docs/guides/multi-team-games.md +586 -0
- package/dist/docs/guides/patterns.md +391 -0
- package/dist/docs/guides/presentation.md +196 -0
- package/dist/docs/guides/production-constraints.md +130 -0
- package/dist/docs/guides/scenario-editor.md +143 -0
- package/dist/docs/guides/scenarios.md +515 -0
- package/dist/docs/guides/session.md +972 -0
- package/dist/docs/guides/settings.md +458 -0
- package/dist/docs/guides/setup.md +193 -0
- package/dist/docs/guides/speech-generation.md +233 -0
- package/dist/docs/guides/starter-game.md +164 -0
- package/dist/docs/guides/testing.md +577 -0
- package/dist/docs/guides/tick-controller.md +569 -0
- package/dist/docs/guides/type-safety.md +516 -0
- package/dist/docs/guides/ui.md +1389 -0
- package/dist/docs/guides/upgrading.md +636 -0
- package/dist/docs/guides/validators.md +217 -0
- package/dist/docs/guides/waiting-room.md +244 -0
- package/dist/docs/index.md +41 -0
- package/dist/docs/recipes/act-on-the-session-from-an-endpoint.md +95 -0
- package/dist/docs/recipes/adapt-a-game-for-walkthroughs.md +133 -0
- package/dist/docs/recipes/adapt-ui-for-observers.md +74 -0
- package/dist/docs/recipes/call-endpoint-from-ui.md +109 -0
- package/dist/docs/recipes/check-a-package-from-a-script.md +117 -0
- package/dist/docs/recipes/create-capability.md +66 -0
- package/dist/docs/recipes/create-endpoint.md +127 -0
- package/dist/docs/recipes/create-engine.md +111 -0
- package/dist/docs/recipes/create-game.md +70 -0
- package/dist/docs/recipes/create-guide.md +75 -0
- package/dist/docs/recipes/create-hook.md +80 -0
- package/dist/docs/recipes/create-presentation.md +142 -0
- package/dist/docs/recipes/create-scenario-editor.md +102 -0
- package/dist/docs/recipes/create-scenario.md +98 -0
- package/dist/docs/recipes/create-session-ui.md +116 -0
- package/dist/docs/recipes/create-settings.md +88 -0
- package/dist/docs/recipes/create-setup.md +115 -0
- package/dist/docs/recipes/create-tick-controller.md +65 -0
- package/dist/docs/recipes/create-user-interface.md +96 -0
- package/dist/docs/recipes/create-validator.md +67 -0
- package/dist/docs/recipes/create-waiting-room-ui.md +109 -0
- package/dist/docs/recipes/declare-api-uses.md +67 -0
- package/dist/docs/recipes/design-state-for-concurrent-writes.md +96 -0
- package/dist/docs/recipes/distribute-package.md +80 -0
- package/dist/docs/recipes/e2e-testing.md +116 -0
- package/dist/docs/recipes/end-game-on-condition.md +154 -0
- package/dist/docs/recipes/fix-duplicate-packages.md +101 -0
- package/dist/docs/recipes/index.md +86 -0
- package/dist/docs/recipes/make-an-endpoint-external.md +101 -0
- package/dist/docs/recipes/negotiate-across-teams.md +164 -0
- package/dist/docs/recipes/observe-other-teams-state.md +119 -0
- package/dist/docs/recipes/publish-a-package.md +139 -0
- package/dist/docs/recipes/remember-a-viewers-choice.md +55 -0
- package/dist/docs/recipes/review-a-past-day.md +73 -0
- package/dist/docs/recipes/share-assets-across-scenarios.md +120 -0
- package/dist/docs/recipes/share-logic-between-engine-and-ui.md +130 -0
- package/dist/docs/recipes/show-all-teams-participants.md +109 -0
- package/dist/docs/recipes/start-from-an-empty-package.md +169 -0
- package/dist/docs/recipes/take-a-file-in-an-endpoint.md +82 -0
- package/dist/docs/recipes/test-a-multi-role-game.md +51 -0
- package/dist/docs/recipes/test-user-interface.md +95 -0
- package/dist/docs/recipes/test-with-engine.md +115 -0
- package/dist/docs/recipes/upload-and-display-files.md +148 -0
- package/dist/docs/recipes/use-request-play.md +65 -0
- package/dist/docs/recipes/use-request-tick.md +75 -0
- package/dist/docs/troubleshooting.md +816 -0
- package/dist/execution/blob-inputs.d.ts +57 -0
- package/dist/execution/bundle-reload.d.ts +42 -0
- package/dist/execution/config-resolver.d.ts +127 -0
- package/dist/execution/configuration-builder.d.ts +66 -0
- package/dist/execution/context-composition.d.ts +161 -0
- package/dist/execution/endpoint-actions.d.ts +9 -0
- package/dist/execution/endpoint-payloads.d.ts +32 -0
- package/dist/execution/endpoint-runner.d.ts +129 -0
- package/dist/execution/engine-state.d.ts +40 -0
- package/dist/execution/executor-callbacks.d.ts +56 -0
- package/dist/execution/executor.d.ts +436 -0
- package/dist/execution/gates.d.ts +45 -0
- package/dist/execution/hook-chain.d.ts +25 -0
- package/dist/execution/logic-calls.d.ts +190 -0
- package/dist/execution/logic-failure.d.ts +84 -0
- package/dist/execution/logic-loader.d.ts +54 -0
- package/dist/execution/logic-result.d.ts +76 -0
- package/dist/execution/tick-controller-call.d.ts +32 -0
- package/dist/execution/types.d.ts +48 -0
- package/dist/generation/ts-type.d.ts +39 -0
- package/dist/guide/locale.d.ts +12 -0
- package/dist/guide/video-registry.d.ts +42 -0
- package/dist/help-examples.d.ts +125 -0
- package/dist/host/adapter.d.ts +67 -0
- package/dist/host/host-channel.d.ts +222 -0
- package/dist/host/index.d.ts +20 -0
- package/dist/host/protocol.d.ts +783 -0
- package/dist/host/surfaces.d.ts +42 -0
- package/dist/host/theme.d.ts +55 -0
- package/dist/host.js +583 -0
- package/dist/prompts/index.d.ts +7 -0
- package/dist/prompts/schema-builder.d.ts +29 -0
- package/dist/prompts/types.d.ts +108 -0
- package/dist/prompts/validators.d.ts +32 -0
- package/dist/runtime/capability-scope.d.ts +6 -0
- package/dist/runtime/context-merge.d.ts +29 -0
- package/dist/runtime/context-projection.d.ts +41 -0
- package/dist/runtime/guarded-callbacks.d.ts +15 -0
- package/dist/runtime/index.d.ts +27 -0
- package/dist/runtime/manifest-version.d.ts +55 -0
- package/dist/runtime/tick-validation.d.ts +25 -0
- package/dist/runtime.js +346 -0
- package/dist/schema.json +1546 -0
- package/dist/server/vite-plugin.d.ts +46 -0
- package/dist/services/tts/client.d.ts +51 -0
- package/dist/services/tts/elevenlabs.d.ts +24 -0
- package/dist/services/tts/types.d.ts +44 -0
- package/dist/storage/asset-resolver.d.ts +49 -0
- package/dist/storage/blob-store.d.ts +41 -0
- package/dist/templates/endpoint/logic.test.ts.liquid +61 -0
- package/dist/templates/endpoint/logic.ts.liquid +25 -0
- package/dist/templates/engine/init.json.liquid +7 -0
- package/dist/templates/engine/logic-aggregate.test.ts.liquid +90 -0
- package/dist/templates/engine/logic-aggregate.ts.liquid +46 -0
- package/dist/templates/engine/logic.test.ts.liquid +61 -0
- package/dist/templates/engine/logic.ts.liquid +32 -0
- package/dist/templates/engine/run.json.liquid +7 -0
- package/dist/templates/guide/01-at-a-glance.md.liquid +19 -0
- package/dist/templates/guide/02-preparation.md.liquid +18 -0
- package/dist/templates/guide/03-running-the-session.md.liquid +37 -0
- package/dist/templates/hook/logic.test.ts.liquid +46 -0
- package/dist/templates/hook/logic.ts.liquid +12 -0
- package/dist/templates/package/CLAUDE.md.template +338 -0
- package/dist/templates/package/DESIGN.md.liquid +144 -0
- package/dist/templates/package/README.md.liquid +143 -0
- package/dist/templates/package/claude/rules/workflow-create-capability.md +26 -0
- package/dist/templates/package/claude/rules/workflow-create-endpoint.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-engine.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-guide.md +40 -0
- package/dist/templates/package/claude/rules/workflow-create-presentation.md +37 -0
- package/dist/templates/package/claude/rules/workflow-create-user-interface.md +31 -0
- package/dist/templates/package/claude/rules/workflow-publish-package.md +54 -0
- package/dist/templates/package/claude/rules/workflow-test-engine-or-ui.md +30 -0
- package/dist/templates/package/claude/rules/workflow-update-sdk-version.md +43 -0
- package/dist/templates/package/claude/settings.json.template +16 -0
- package/dist/templates/package/gitignore.template +15 -0
- package/dist/templates/package/global-setup.ts.template +21 -0
- package/dist/templates/package/globals.d.ts.template +2 -0
- package/dist/templates/package/oxfmtrc.json.template +14 -0
- package/dist/templates/package/oxlintrc.json.template +238 -0
- package/dist/templates/package/package.json.liquid +65 -0
- package/dist/templates/package/playwright.config.ts.liquid +19 -0
- package/dist/templates/package/pnpm-workspace.yaml.template +27 -0
- package/dist/templates/package/test-setup.ts.liquid +7 -0
- package/dist/templates/package/tsconfig.json.liquid +15 -0
- package/dist/templates/package/vite.config.ts.liquid +19 -0
- package/dist/templates/package/vitest.config.ts.liquid +25 -0
- package/dist/templates/package/vitest.engine.config.ts.liquid +12 -0
- package/dist/templates/presentation/index.html.liquid +20 -0
- package/dist/templates/presentation/src/App.tsx.liquid +49 -0
- package/dist/templates/presentation/src/Slides.tsx.liquid +81 -0
- package/dist/templates/presentation/src/i18n.ts.liquid +54 -0
- package/dist/templates/presentation/src/main.tsx.liquid +14 -0
- package/dist/templates/scenario_editor/src/App.test.tsx.liquid +30 -0
- package/dist/templates/scenario_editor/src/App.tsx.liquid +95 -0
- package/dist/templates/scenario_editor/src/i18n.ts.liquid +44 -0
- package/dist/templates/session/src/App.test.tsx.liquid +60 -0
- package/dist/templates/session/src/App.tsx.liquid +128 -0
- package/dist/templates/session/src/i18n.ts.liquid +53 -0
- package/dist/templates/session/src/main.tsx.liquid +48 -0
- package/dist/templates/session/src/theme.ts.liquid +46 -0
- package/dist/templates/settings-editor/index.html.liquid +12 -0
- package/dist/templates/settings-editor/src/main.tsx.liquid +48 -0
- package/dist/templates/settings-editor/src/theme.ts.liquid +24 -0
- package/dist/templates/setup/src/App.test.tsx.liquid +39 -0
- package/dist/templates/setup/src/App.tsx.liquid +102 -0
- package/dist/templates/setup/src/i18n.ts.liquid +47 -0
- package/dist/templates/starter-game/capabilities/briefing/capability.sdk.json +18 -0
- package/dist/templates/starter-game/capabilities/plan/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/results/capability.sdk.json +61 -0
- package/dist/templates/starter-game/capabilities/roles/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/weather/capability.sdk.json +23 -0
- package/dist/templates/starter-game/engine/defaults.ts +81 -0
- package/dist/templates/starter-game/engine/engine.sdk.json +12 -0
- package/dist/templates/starter-game/engine/logic.test.ts +221 -0
- package/dist/templates/starter-game/engine/logic.ts +149 -0
- package/dist/templates/starter-game/engine/test-helpers.ts +74 -0
- package/dist/templates/starter-game/engine/types.ts +139 -0
- package/dist/templates/starter-game/guide/content/en/01-at-a-glance.md +51 -0
- package/dist/templates/starter-game/guide/content/en/02-preparation.md +35 -0
- package/dist/templates/starter-game/guide/content/en/03-running-the-day.md +58 -0
- package/dist/templates/starter-game/guide/guide.sdk.json +10 -0
- package/dist/templates/starter-game/guide/guide.test.ts +108 -0
- package/dist/templates/starter-game/hooks/assignproducts/defaults.ts +37 -0
- package/dist/templates/starter-game/hooks/assignproducts/hook.sdk.json +13 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.test.ts +77 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.ts +47 -0
- package/dist/templates/starter-game/hooks/assignproducts/types.ts +55 -0
- package/dist/templates/starter-game/package.json +68 -0
- package/dist/templates/starter-game/playwright.config.ts +19 -0
- package/dist/templates/starter-game/scenarios/heatwave/scenario.sdk.json +20 -0
- package/dist/templates/starter-game/scenarios/ordinary_week/scenario.sdk.json +12 -0
- package/dist/templates/starter-game/seriousgaming.sdk.json +23 -0
- package/dist/templates/starter-game/session/defaults.ts +80 -0
- package/dist/templates/starter-game/session/index.html +12 -0
- package/dist/templates/starter-game/session/session.sdk.json +11 -0
- package/dist/templates/starter-game/session/src/App.test.tsx +307 -0
- package/dist/templates/starter-game/session/src/App.tsx +309 -0
- package/dist/templates/starter-game/session/src/Section.tsx +37 -0
- package/dist/templates/starter-game/session/src/TeamDoor.tsx +96 -0
- package/dist/templates/starter-game/session/src/format.ts +8 -0
- package/dist/templates/starter-game/session/src/i18n.ts +51 -0
- package/dist/templates/starter-game/session/src/main.tsx +48 -0
- package/dist/templates/starter-game/session/src/theme.ts +45 -0
- package/dist/templates/starter-game/session/types.ts +135 -0
- package/dist/templates/starter-game/settings/engine/settings.sdk.json +50 -0
- package/dist/templates/starter-game/settings/menu/settings.sdk.json +78 -0
- package/dist/templates/starter-game/settings/session/settings.sdk.json +23 -0
- package/dist/templates/starter-game/settings/ui/settings.sdk.json +25 -0
- package/dist/templates/starter-game/test-setup.ts +7 -0
- package/dist/templates/starter-game/testing/e2e/game.spec.ts +139 -0
- package/dist/templates/starter-game/testing/e2e/global-setup.ts +21 -0
- package/dist/templates/starter-game/testing/engine/game.engine.test.ts +171 -0
- package/dist/templates/starter-game/tsconfig.json +19 -0
- package/dist/templates/starter-game/ui/defaults.ts +60 -0
- package/dist/templates/starter-game/ui/index.html +12 -0
- package/dist/templates/starter-game/ui/src/App.test.tsx +138 -0
- package/dist/templates/starter-game/ui/src/App.tsx +175 -0
- package/dist/templates/starter-game/ui/src/DayBar.test.tsx +78 -0
- package/dist/templates/starter-game/ui/src/DayBar.tsx +72 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.test.tsx +74 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.tsx +120 -0
- package/dist/templates/starter-game/ui/src/MakeRow.tsx +129 -0
- package/dist/templates/starter-game/ui/src/Section.tsx +37 -0
- package/dist/templates/starter-game/ui/src/ThemeToggle.tsx +49 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.test.tsx +179 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.tsx +92 -0
- package/dist/templates/starter-game/ui/src/YesterdayPanel.tsx +142 -0
- package/dist/templates/starter-game/ui/src/format.ts +12 -0
- package/dist/templates/starter-game/ui/src/i18n.ts +95 -0
- package/dist/templates/starter-game/ui/src/main.tsx +48 -0
- package/dist/templates/starter-game/ui/src/menu.ts +18 -0
- package/dist/templates/starter-game/ui/src/test-helpers.ts +58 -0
- package/dist/templates/starter-game/ui/src/theme.ts +45 -0
- package/dist/templates/starter-game/ui/src/writes.ts +43 -0
- package/dist/templates/starter-game/ui/types.ts +137 -0
- package/dist/templates/starter-game/ui/ui.sdk.json +11 -0
- package/dist/templates/starter-game/validators/capacity/defaults.ts +37 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.test.ts +96 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.ts +45 -0
- package/dist/templates/starter-game/validators/capacity/types.ts +55 -0
- package/dist/templates/starter-game/validators/capacity/validator.sdk.json +11 -0
- package/dist/templates/starter-game/vite.config.ts +19 -0
- package/dist/templates/starter-game/vitest.config.ts +15 -0
- package/dist/templates/starter-game/vitest.engine.config.ts +19 -0
- package/dist/templates/starter-game/waiting_room/defaults.ts +40 -0
- package/dist/templates/starter-game/waiting_room/index.html +12 -0
- package/dist/templates/starter-game/waiting_room/src/App.test.tsx +102 -0
- package/dist/templates/starter-game/waiting_room/src/App.tsx +194 -0
- package/dist/templates/starter-game/waiting_room/src/i18n.ts +33 -0
- package/dist/templates/starter-game/waiting_room/src/main.tsx +48 -0
- package/dist/templates/starter-game/waiting_room/src/theme.ts +45 -0
- package/dist/templates/starter-game/waiting_room/types.ts +59 -0
- package/dist/templates/starter-game/waiting_room/waiting_room.sdk.json +10 -0
- package/dist/templates/tick-controller/logic.ts.liquid +8 -0
- package/dist/templates/ui/index.html.liquid +12 -0
- package/dist/templates/ui/src/App.test.tsx.liquid +41 -0
- package/dist/templates/ui/src/App.tsx.liquid +103 -0
- package/dist/templates/ui/src/i18n.ts.liquid +47 -0
- package/dist/templates/ui/src/main.tsx.liquid +48 -0
- package/dist/templates/ui/src/theme.ts.liquid +46 -0
- package/dist/templates/validator/logic.test.ts.liquid +42 -0
- package/dist/templates/validator/logic.ts.liquid +21 -0
- package/dist/templates/waiting-room/index.html.liquid +12 -0
- package/dist/templates/waiting-room/src/App.test.tsx.liquid +57 -0
- package/dist/templates/waiting-room/src/App.tsx.liquid +118 -0
- package/dist/templates/waiting-room/src/i18n.ts.liquid +44 -0
- package/dist/templates/waiting-room/src/main.tsx.liquid +48 -0
- package/dist/templates/waiting-room/src/theme.ts.liquid +46 -0
- package/dist/templating/resource-metadata.d.ts +131 -0
- package/dist/types.d.ts +14 -0
- package/dist/utils/atomic-write.d.ts +40 -0
- package/dist/utils/cli-error.d.ts +64 -0
- package/dist/utils/document-parser-client.d.ts +20 -0
- package/dist/utils/env-loader.d.ts +40 -0
- package/dist/utils/error-codes.d.ts +339 -0
- package/dist/utils/errors.d.ts +19 -0
- package/dist/utils/global-settings.d.ts +65 -0
- package/dist/utils/llm-client.d.ts +34 -0
- package/dist/utils/logger.d.ts +36 -0
- package/dist/utils/output.d.ts +109 -0
- package/dist/utils/platform.d.ts +14 -0
- package/dist/utils/prompt.d.ts +36 -0
- package/dist/utils/real-path.d.ts +6 -0
- package/dist/utils/schema-properties.d.ts +35 -0
- package/dist/validation/ajv.d.ts +17 -0
- package/dist/validation/facilitator-guide-validator.d.ts +27 -0
- package/dist/validation/findings.d.ts +40 -0
- package/dist/validation/game-constraints.d.ts +10 -0
- package/dist/validation/game-validator.d.ts +34 -0
- package/dist/validation/package-validation.d.ts +41 -0
- package/dist/validation/package-validator.d.ts +19 -0
- package/dist/validation/resource-validator.d.ts +79 -0
- package/dist/validation/schema-defaults-validator.d.ts +29 -0
- package/dist/validation/schema-validation.d.ts +115 -0
- package/dist/validation/semver.d.ts +9 -0
- package/package.json +123 -3
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Running the session
|
|
2
|
+
|
|
3
|
+
One section per phase. Use the say, do, and timing admonitions so the script is easy to follow live.
|
|
4
|
+
|
|
5
|
+
## Briefing
|
|
6
|
+
|
|
7
|
+
:::say
|
|
8
|
+
Welcome everyone. Today you are running {{ name }}. You will make decisions as a team, see the consequences, and compete with the other teams.
|
|
9
|
+
:::
|
|
10
|
+
|
|
11
|
+
:::do
|
|
12
|
+
Form the teams and hand each team its device.
|
|
13
|
+
:::
|
|
14
|
+
|
|
15
|
+
:::timing
|
|
16
|
+
Keep the briefing under 15 minutes. Energy matters more than completeness; the first tick will teach the mechanics.
|
|
17
|
+
:::
|
|
18
|
+
|
|
19
|
+
## Play
|
|
20
|
+
|
|
21
|
+
:::do
|
|
22
|
+
Run the first tick once every team has submitted its decisions. Walk the room between ticks and coach teams that are stuck.
|
|
23
|
+
:::
|
|
24
|
+
|
|
25
|
+
:::warning
|
|
26
|
+
Avoid revealing how the underlying model works during play. Save it for the debrief.
|
|
27
|
+
:::
|
|
28
|
+
|
|
29
|
+
## Debrief
|
|
30
|
+
|
|
31
|
+
:::say
|
|
32
|
+
What surprised you? Where did the result differ from what you expected when you made the decision?
|
|
33
|
+
:::
|
|
34
|
+
|
|
35
|
+
:::tip
|
|
36
|
+
Tie at least one concrete in-game moment back to each learning objective.
|
|
37
|
+
:::
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { Run } from './logic';
|
|
3
|
+
import type { Context, Settings, Callbacks } from '../types';
|
|
4
|
+
import type { LogicMeta } from '@seriousgaming/sdk';
|
|
5
|
+
|
|
6
|
+
// The run-wide facts the host supplies: the session locale, its random seed,
|
|
7
|
+
// whether this is the game being played or a walkthrough beside it, and when
|
|
8
|
+
// the operation started.
|
|
9
|
+
const meta: LogicMeta = { locale: 'en', seed: 'test-seed', runKind: 'session', now: Date.UTC(2026, 0, 1) };
|
|
10
|
+
|
|
11
|
+
const callbacks = {} as Callbacks;
|
|
12
|
+
|
|
13
|
+
// The participant the signal concerns, and the roster the hook receives.
|
|
14
|
+
const participant = {
|
|
15
|
+
id: '1',
|
|
16
|
+
teamId: 'team-1',
|
|
17
|
+
name: 'Participant 1',
|
|
18
|
+
index: 0,
|
|
19
|
+
connected: true,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe('{{ identifier }}', () => {
|
|
23
|
+
describe('Run', () => {
|
|
24
|
+
it('should update context in response to the signal', async () => {
|
|
25
|
+
const context: Context = {
|
|
26
|
+
// TODO: Add context values based on required/provided capabilities
|
|
27
|
+
} as Context;
|
|
28
|
+
const settings: Settings = {
|
|
29
|
+
// TODO: Add values for the settings resources this reads
|
|
30
|
+
} as Settings;
|
|
31
|
+
|
|
32
|
+
// TODO: replace with the payload fields the hook's signal supplies
|
|
33
|
+
const { capabilities } = await Run({
|
|
34
|
+
context,
|
|
35
|
+
settings,
|
|
36
|
+
callbacks,
|
|
37
|
+
meta,
|
|
38
|
+
participant,
|
|
39
|
+
participants: [participant],
|
|
40
|
+
} as Parameters<typeof Run>[0]);
|
|
41
|
+
|
|
42
|
+
// TODO: Add assertions for updated values
|
|
43
|
+
expect(capabilities).toBeDefined();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HookFunction } from '../types';
|
|
2
|
+
|
|
3
|
+
export const Run: HookFunction = async ({ context, settings, callbacks }) => {
|
|
4
|
+
// Hooks fire after the runtime applies the lifecycle change.
|
|
5
|
+
// Return the capabilities it writes under `capabilities`; the runtime merges
|
|
6
|
+
// and persists them.
|
|
7
|
+
return {
|
|
8
|
+
capabilities: {
|
|
9
|
+
...context
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
};
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Guidance for Claude Code working in this Serious Gaming SDK package. A package is a game.
|
|
4
|
+
|
|
5
|
+
## Non-negotiables
|
|
6
|
+
|
|
7
|
+
- **Logic returns what it writes under `capabilities`**, and removes keys with `deletes` beside it. Never mutate `context`: spread
|
|
8
|
+
- **Declare `reads`, `writes`, `settings` and `uses`.** A resource receives exactly what it declares; a write or a service outside them is refused
|
|
9
|
+
- **Await every `setData`**, through `useMutationReporter`, so a refusal reaches the person. Never retry a write automatically
|
|
10
|
+
- **Never start `pnpm start`, `pnpm run sdk dev` or watch-mode `pnpm test`.** The user starts long-running processes
|
|
11
|
+
- **No host globals in logic.** `meta.now`, not `Date.now()`; `meta.seed` with `deriveSeed` and `seededRandom`, not `Math.random()`; no `fetch`, timers or Node built-ins
|
|
12
|
+
- **Use `update`, not `new`, for a slot that is already filled** (`engine`, `ui`, `session`, …). Change bundles through the CLI, not by hand
|
|
13
|
+
- **Never edit a generated `types.ts`, and never use `any`**
|
|
14
|
+
- **Never publish, and never `pnpm add`, unless the user asks**
|
|
15
|
+
|
|
16
|
+
## Workflows
|
|
17
|
+
|
|
18
|
+
Before touching a resource, read its rule. A tool that loads only this file still finds them here:
|
|
19
|
+
|
|
20
|
+
- `capabilities/`: `.claude/rules/workflow-create-capability.md`
|
|
21
|
+
- `engine/`: `.claude/rules/workflow-create-engine.md`
|
|
22
|
+
- `ui/`, `session/`, `waiting_room/`, `setup/`, `scenario_editor/`: `.claude/rules/workflow-create-user-interface.md`
|
|
23
|
+
- `endpoints/`: `.claude/rules/workflow-create-endpoint.md`
|
|
24
|
+
- `guide/`: `.claude/rules/workflow-create-guide.md`
|
|
25
|
+
- `presentation/`: `.claude/rules/workflow-create-presentation.md`
|
|
26
|
+
- Any `*.test.ts` or `*.test.tsx`: `.claude/rules/workflow-test-engine-or-ui.md`
|
|
27
|
+
- `package.json` and an SDK upgrade: `.claude/rules/workflow-update-sdk-version.md`
|
|
28
|
+
- Publishing: `.claude/rules/workflow-publish-package.md`
|
|
29
|
+
|
|
30
|
+
## Documentation
|
|
31
|
+
|
|
32
|
+
The SDK's documentation ships inside it, so it describes the version installed here. **Read the recipe before you start a task; do not guess.**
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pnpm run --silent sdk docs view # the documentation tree
|
|
36
|
+
pnpm run --silent sdk docs view recipes/index.md # every recipe, one line each
|
|
37
|
+
pnpm run --silent sdk docs view recipes/create-hook.md # one document
|
|
38
|
+
pnpm run --silent sdk docs view cli-reference.md # every command and flag
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The same files are on disk in `node_modules/@seriousgaming/sdk-tools/dist/docs/`, so you can search them with `grep -rn`. Start from `recipes/` for a task, `guides/` for a whole subject, and `concepts/` for the model.
|
|
42
|
+
|
|
43
|
+
## This package
|
|
44
|
+
|
|
45
|
+
Unless it was created with `--empty`, this package holds a complete, playable starter game: a lemonade stand, run for 10 days. Each morning a team decides how many lemonades, iced teas, and cookies to make. Whatever they make they pay for, whatever is left at closing is poured away, and anyone who wanted something that ran out simply leaves.
|
|
46
|
+
|
|
47
|
+
Read it before you change it. It is the reference for four things that are easy to get wrong and expensive to retrofit:
|
|
48
|
+
|
|
49
|
+
- **State keyed so participants cannot overwrite each other.** `plan.Make` is keyed by menu item, and `hooks/assignproducts/` puts each participant in charge of one item, so two people changing two numbers write two different keys
|
|
50
|
+
- **A validator on the one thing they share.** Two changes can each fit while the merged plan exceeds the stall's capacity, so `validators/capacity/` checks the merged result and refuses it out loud
|
|
51
|
+
- **Awaited writes.** Every `setData` in `ui/` goes through the one mutation reporter in `ui/src/writes.ts`
|
|
52
|
+
- **Teams run their own days.** The game is in independent mode; the facilitator watches from `session/` and can undo
|
|
53
|
+
|
|
54
|
+
Replace the model freely. Keep the shape. `pnpm run --silent sdk docs view guides/starter-game.md` walks through every file, and `recipes/start-from-an-empty-package.md` picks up an empty package.
|
|
55
|
+
|
|
56
|
+
### Mental model
|
|
57
|
+
|
|
58
|
+
A game works like a **reactive spreadsheet**:
|
|
59
|
+
|
|
60
|
+
- **Capabilities**: column headers (named groups of variables, schema only)
|
|
61
|
+
- **Settings**: read-only parameters, each property with a default, overridden by scenarios
|
|
62
|
+
- **The engine**: the formulas (`Init` sets up, `Run` computes one tick)
|
|
63
|
+
- **User interfaces**: charts and input cells (`ui/` for participants, `session/` for the facilitator, `waiting_room/` before play, `setup/` and `scenario_editor/` for settings)
|
|
64
|
+
- **Validators**: data validation on input cells (reject a write, never change it)
|
|
65
|
+
- **Hooks**: handlers that run outside the tick on a signal such as `participantJoin`
|
|
66
|
+
- **Endpoints**: server-side functions a surface, or with `visibility: "external"` a caller outside the game, calls by identifier
|
|
67
|
+
- **Guide and presentation**: the facilitator's run-sheet and slide deck
|
|
68
|
+
|
|
69
|
+
Data flows: surfaces write decisions with `setData`, a tick runs the engine, and surfaces show the results.
|
|
70
|
+
|
|
71
|
+
### Layout
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
.
|
|
75
|
+
├── seriousgaming.sdk.json # The package identifier, locales, maxTicks and teams
|
|
76
|
+
├── engine/ # THE engine: Init and Run
|
|
77
|
+
├── ui/ # THE participant interface
|
|
78
|
+
├── session/ # THE facilitator's screen
|
|
79
|
+
├── waiting_room/ # THE holding screen
|
|
80
|
+
├── setup/ # THE settings form at session creation (sdk new setup)
|
|
81
|
+
├── scenario_editor/ # THE scenario authoring screen (sdk new scenario_editor)
|
|
82
|
+
├── presentation/ # THE slide deck (sdk new presentation)
|
|
83
|
+
├── tick_controller/ # THE timer, for timed rounds (sdk new tick_controller)
|
|
84
|
+
├── guide/ # THE facilitator guide (markdown per locale, plus assets)
|
|
85
|
+
├── capabilities/<name>/ # Data definitions
|
|
86
|
+
├── settings/<name>/ # Read-only parameters
|
|
87
|
+
├── endpoints/<name>/ # Server-side functions
|
|
88
|
+
├── hooks/<name>/ # Lifecycle handlers
|
|
89
|
+
├── validators/<name>/ # Reject-only gates on writes and tick requests
|
|
90
|
+
├── scenarios/<name>/ # Settings overrides, plus assets
|
|
91
|
+
├── testing/engine/ # Whole-game tests (createTestSession)
|
|
92
|
+
└── testing/e2e/ # Playwright specs, and the global setup that starts their server
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
A game holds one of each slot resource, so it lives in its slot and carries no name. Everything else sits at `<plural>/<name>/` and is named by its directory: `"identifier": "sales"` makes `capabilities/sales/`, `context.sales`, and `"sales"` in `reads`. A name is lowercase letters, digits, and underscores, starting with a letter. Only the package carries a dotted reverse-DNS identifier, and its game is that identifier with `.game` appended. There is no list to keep in step: a package declares whatever is on disk.
|
|
96
|
+
|
|
97
|
+
## The loop: edit, validate, test
|
|
98
|
+
|
|
99
|
+
Pass `--json` to any command (or set `SERIOUSGAMING_JSON=1`) and it prints exactly one JSON document on stdout, success or failure, with progress on stderr. Always invoke it as `pnpm run --silent sdk …`, so no pnpm banner precedes the JSON.
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{ "ok": true, "command": "tick", "data": { "completedTicks": 1 }, "diagnostics": [], "warnings": [] }
|
|
103
|
+
{ "ok": false, "command": "build", "error": { "code": "PACKAGE_INVALID", "message": "…", "hint": "…" }, "diagnostics": [{ "severity": "error", "code": "SETTINGS_UNKNOWN_KEY", "file": "scenarios/hard/scenario.sdk.json", "pointer": "/definition/settings/engine/BaseDemnd", "hint": "Did you mean 'BaseDemand'?", "message": "…" }], "warnings": [] }
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
After every change, run these in order and stop at the first that fails:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pnpm run --silent sdk build --validate-only --json # the package: bundles, references, settings
|
|
110
|
+
pnpm run typecheck # tsc over the whole package
|
|
111
|
+
pnpm run lint # oxlint, warnings included
|
|
112
|
+
pnpm test:run # unit and component tests beside each resource
|
|
113
|
+
pnpm test:engine # whole games, in-process
|
|
114
|
+
pnpm run --silent sdk tick --no-server --json # one tick in the restricted production runtime
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Branch on `ok`, the exit code and `error.code`, never on the message:
|
|
118
|
+
|
|
119
|
+
| Exit | Meaning | What to do |
|
|
120
|
+
| ---- | ------------------- | -------------------------------------------------------------------------- |
|
|
121
|
+
| 0 | Success | |
|
|
122
|
+
| 1 | A bug in the CLI | Report it; do not retry blindly |
|
|
123
|
+
| 2 | Usage error | Fix the command: a flag, an argument, or input you did not pass |
|
|
124
|
+
| 3 | Invalid package | Fix the package: `diagnostics` says which file and where |
|
|
125
|
+
| 4 | Refused by the game | The game said no (`REFUSED_VALIDATION`, `TICK_FAILED`, …): read the reason |
|
|
126
|
+
| 5 | Environment | No session, a port in use, the platform unreachable: fix the environment |
|
|
127
|
+
|
|
128
|
+
**On a failure, read `diagnostics[]` first.** `file` is relative to the package root; `pointer` is an RFC 6901 pointer into that JSON file (`/definition/reads/0` is the bundle's first read), and `line` and `column` locate a TypeScript file instead. Open it there, apply the `hint`, and rerun the same command. Branch on each diagnostic's `code` (`SCHEMA_INVALID`, `REFERENCE_UNKNOWN`, `SETTINGS_UNKNOWN_KEY`, `REMOVED_FIELD`, `LOGIC_ERROR`, …). Logic that throws during `init`, `tick` or `endpoint call` exits 4 with a `LOGIC_ERROR` naming the source line, `tick` and `team`. Warnings do not fail a command, but are usually worth fixing.
|
|
129
|
+
|
|
130
|
+
The CLI never prompts without a terminal: a command that would ask fails with `PROMPT_REQUIRED` (exit 2). Pass definitions as an argument, with `--file`, or on stdin, and pass `--yes` to `delete`. `pnpm run --silent sdk docs view recipes/check-a-package-from-a-script.md` has the whole contract.
|
|
131
|
+
|
|
132
|
+
## Commands
|
|
133
|
+
|
|
134
|
+
### Resources
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
pnpm run sdk new capability --help # every type prints its JSON format with --help
|
|
138
|
+
pnpm run sdk new capability '{...}' # likewise hook, validator, endpoint, settings, scenario
|
|
139
|
+
pnpm run sdk new setup '{...}' # an empty slot: setup, scenario_editor, presentation, tick_controller
|
|
140
|
+
pnpm run sdk update engine --patch '[{"op":"add","path":"/definition/writes/-","value":"results"}]'
|
|
141
|
+
pnpm run sdk update capability plan '{...}' # full replacement
|
|
142
|
+
pnpm run sdk update package --patch '[{"op":"replace","path":"/ticks","value":12}]'
|
|
143
|
+
pnpm run sdk delete capability plan --yes
|
|
144
|
+
pnpm run generate # regenerate types after a hand edit (new and update already do)
|
|
145
|
+
pnpm run build # validate, generate and bundle into dist/
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
A slot command takes no identifier. The starter game fills every slot but `setup`, `scenario_editor`, `presentation` and `tick_controller`, and `new` refuses a filled slot. `update package` changes the manifest, which takes no identifier either.
|
|
149
|
+
|
|
150
|
+
### Playing the game from the CLI
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
pnpm run --silent sdk init # start the session, or rebuild it at tick 0
|
|
154
|
+
pnpm run --silent sdk tick --patch '[{"op":"replace","path":"/plan/Make/lemonade","value":70}]'
|
|
155
|
+
pnpm run --silent sdk patch '[{"op":"replace","path":"/plan/Make/cookies","value":0}]'
|
|
156
|
+
pnpm run --silent sdk view # the current state (--full for the whole projection)
|
|
157
|
+
pnpm run --silent sdk undo # undo the last tick
|
|
158
|
+
pnpm run --silent sdk reset # back to tick 0
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Patch paths start at the capability. `--team N` picks the team in a game with several. `pnpm run sdk configure '{"menu":{"Capacity":60}}'` overrides settings for the session, keyed by settings identifier, and restarts a running session on the new values.
|
|
162
|
+
|
|
163
|
+
**Know which session a command touches:**
|
|
164
|
+
|
|
165
|
+
- **If a dev server is running**, session commands act on its session, which is what the user is watching
|
|
166
|
+
- **If none is running**, they start one in the background and say so on stderr
|
|
167
|
+
- **With `--no-server`**, they run in-process against a fresh session and leave nothing behind; each starts again at tick 0
|
|
168
|
+
|
|
169
|
+
Edits to logic and bundles reach a running dev server from its next tick, without a reset. Prefer `--no-server` and `pnpm test:engine` for your own checks. Use the shared session only when the user wants to watch. `pnpm run sdk dev status` says whether a server is running; never stop one you did not start. `pnpm test:e2e` starts an isolated server of its own on free ports, beside the user's.
|
|
170
|
+
|
|
171
|
+
## How things work
|
|
172
|
+
|
|
173
|
+
### Reads, writes and what the runtime owns
|
|
174
|
+
|
|
175
|
+
Every resource declares its data flow as arrays of capability identifiers: `reads` for what it consumes, `writes` for what it produces (a capability may appear in both). A capability you forgot to declare is absent from `context` (or a surface's `data`), and the generated `Context` says so. A game holds one engine; a simulation with several steps calls them as plain functions from its `Run`.
|
|
176
|
+
|
|
177
|
+
Do not rebuild runtime concerns in context:
|
|
178
|
+
|
|
179
|
+
- **Ticks**: no `CurrentRound` variable; read `meta.tick`, the tick being run
|
|
180
|
+
- **Time**: `meta.now`, the instant the operation started, which a re-run sees again
|
|
181
|
+
- **Who is on a team**: the read-only `participants` argument, not a roster kept from hooks
|
|
182
|
+
- **Lifecycle**: set `maxTicks` in `seriousgaming.sdk.json`, or return `complete: true` from `Run`. A tick controller is for timed rounds, or ending on a participant's write between ticks
|
|
183
|
+
- **Undo and reset**: built in
|
|
184
|
+
- **Parameters**: a settings resource (`settings/<name>/settings.sdk.json`, a default on every property), read by each resource that lists it in `settings`
|
|
185
|
+
|
|
186
|
+
`setData` updates context immediately, and other surfaces see it at once; the engine's `Run` sees it on the next tick.
|
|
187
|
+
|
|
188
|
+
### Logic
|
|
189
|
+
|
|
190
|
+
Every logic function is async, takes one args object, and returns what it writes under `capabilities`, always by spreading:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import type { InitializationFunction, RunFunction } from '@seriousgaming/sdk';
|
|
194
|
+
import type { Context, Settings } from './types';
|
|
195
|
+
|
|
196
|
+
export const Init: InitializationFunction<Context, Settings> = async ({ context, settings }) => ({
|
|
197
|
+
capabilities: { ...context },
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
export const Run: RunFunction<Context, Settings> = async ({ context, meta }) => ({
|
|
201
|
+
capabilities: {
|
|
202
|
+
...context,
|
|
203
|
+
results: { ...context.results, Day: meta.tick }, // never context.results.Day = …
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
- The args are `{ context, settings, callbacks, meta, participants }`, plus `state` for an engine. `settings` is keyed by settings identifier and already resolved for the session; `guides/settings.md` has the order
|
|
209
|
+
- Hooks, aggregate engines (`{ shared: { capabilities }, teams: [{ capabilities }] }`) and writing endpoints (`{ response, capabilities }`) return the same way. A flat return fails the call
|
|
210
|
+
- **To remove a key**, return its path in `deletes` beside `capabilities` (`deletes: [['orders', 'Open', id]]`), under a capability in `writes`. Leaving a key out never removes it
|
|
211
|
+
- `Init` is optional: without it each team starts from the capabilities' defaults
|
|
212
|
+
- `Run` may return `complete: true` to end the game, and an engine may return `state` beside `capabilities` for private memory between ticks (type it with `RunFunction`'s fourth parameter)
|
|
213
|
+
- A step of a split engine is a `StepFunction<Context, Settings>`: steps pass the context flat, and only the exported function wraps the result
|
|
214
|
+
|
|
215
|
+
### The logic runtime: ECMAScript intrinsics only
|
|
216
|
+
|
|
217
|
+
Engine, hook, validator, endpoint, and tick controller logic runs in a **bare V8 isolate** in production. `process`, `require`, `fetch`, timers, `structuredClone`, `Buffer`, `TextEncoder`, `URL`, `crypto`, `atob`, `window`, `document` and `localStorage` are absent and throw at tick time. `console.log` is fine.
|
|
218
|
+
|
|
219
|
+
| Instead of | Use |
|
|
220
|
+
| ----------------------------- | -------------------------------------------------------------- |
|
|
221
|
+
| `structuredClone(v)` | `JSON.parse(JSON.stringify(v))` |
|
|
222
|
+
| `TextEncoder`, `TextDecoder` | `encodeUtf8`, `decodeUtf8` from `@seriousgaming/sdk` |
|
|
223
|
+
| `fetch(...)` | The injected `callbacks`, or an endpoint |
|
|
224
|
+
| `setTimeout(...)` | Nothing: ticks are the scheduling primitive |
|
|
225
|
+
| `process.env.X` | The `settings` argument |
|
|
226
|
+
| `Math.random()` for kept data | `deriveSeed` and `seededRandom` from the SDK, from `meta.seed` |
|
|
227
|
+
| `Date.now()`, `new Date()` | `meta.now` |
|
|
228
|
+
|
|
229
|
+
Importing a Node built-in (`node:fs`, `path`, `os`, …), directly or through a library, fails `build`, `dev` and `tick` with `LOGIC_NODE_BUILTIN`. `pnpm run lint` refuses the globals above as errors in every logic file, and fails on any warning, so run it before you call logic done. It cannot see `globalThis.fetch`, and it is off for tests, which run in Node: **a green unit test does not prove logic is portable**, so finish with `tick --no-server`.
|
|
230
|
+
|
|
231
|
+
### Type safety
|
|
232
|
+
|
|
233
|
+
`variablesSchema` and `settingsSchema` are full JSON Schema 7 objects, and the generated types are only as precise as they are:
|
|
234
|
+
|
|
235
|
+
- **Wrap every schema**: `type: 'object'`, `properties`, `additionalProperties: false` (and `required` in a `variablesSchema`)
|
|
236
|
+
- **Type every field.** Never a string holding JSON; give an object its `properties` and an array its `items`
|
|
237
|
+
- **Key collections by id** where several people write at once (`additionalProperties` with a schema): two writes to two keys cannot collide, and an entry leaves through `deletes`. See `recipes/design-state-for-concurrent-writes.md`
|
|
238
|
+
- **A `default` must satisfy its own schema**: `build --validate-only` fails one that does not
|
|
239
|
+
|
|
240
|
+
```json
|
|
241
|
+
{
|
|
242
|
+
"definition": {
|
|
243
|
+
"identifier": "market",
|
|
244
|
+
"variablesSchema": {
|
|
245
|
+
"type": "object",
|
|
246
|
+
"properties": {
|
|
247
|
+
"Competitors": {
|
|
248
|
+
"type": "object",
|
|
249
|
+
"properties": {
|
|
250
|
+
"count": { "type": "number", "minimum": 1 },
|
|
251
|
+
"priceList": {
|
|
252
|
+
"type": "array",
|
|
253
|
+
"items": { "type": "number", "minimum": 0 }
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
"required": ["count", "priceList"],
|
|
257
|
+
"additionalProperties": false,
|
|
258
|
+
"default": { "count": 5, "priceList": [10, 15, 20] }
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
"required": ["Competitors"],
|
|
262
|
+
"additionalProperties": false
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Every resource with logic or a surface has a generated `types.ts` beside it: `Context` holds exactly the capabilities it declares, `Settings` the settings resources it lists, and `Callbacks` the services in its `uses`. Import from `./types` (or `../types` from a `logic/` directory), never edit it or `defaults.ts`, and never write your own. `pnpm run --silent sdk docs view guides/type-safety.md` has the rest.
|
|
269
|
+
|
|
270
|
+
### Testing logic
|
|
271
|
+
|
|
272
|
+
Call a logic function with `logicArgs` from `@seriousgaming/sdk/testing`, which fills `meta`, the roster and offline stub callbacks around what you name; pass the generated `defaultSettings`. A whole game runs in `createTestSession<Context, SharedContext>` (`testing/engine/`), whose `setData` and `requestTick` act as a participant, so validators judge them. Never cast an argument or a context:
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
const result = await Run(
|
|
276
|
+
logicArgs.run({ context: defaultContext, settings: defaultSettings, meta: { tick: 3 } })
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
const session = await createTestSession<Context, SharedContext>({ packagePath });
|
|
280
|
+
await session.setData(0, { plan: { Make: { lemonade: 28 } } });
|
|
281
|
+
await session.tick();
|
|
282
|
+
expect(session.team(0).context.results.Day).toBe(1);
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## User interfaces
|
|
286
|
+
|
|
287
|
+
- **Default to MUI** (`@mui/material`), as the starter game does. Another library must integrate with `useUserInterfaceTheme` so colours and dark mode work, and text on a theme colour uses that colour's `contrastText` (`colors.contrastText.primary`)
|
|
288
|
+
- **Await writes.** `setData` rejects when the host refuses the write. Route writes through `useMutationReporter`, as `ui/src/writes.ts` does; on a `conflict`, offer `failure.retry` behind a 'Try again' button
|
|
289
|
+
- **Endpoint calls fail typed.** `callbacks.endpoints['<name>'](request)` rejects with an `EndpointError` whose `kind` says why; a non-empty `applied` means some effects landed. Never retry one automatically
|
|
290
|
+
- **A viewer's own choices** (a tab, a collapsed panel) go in `useViewerStorage(key, initial)`, never `localStorage`, which collides across sessions
|
|
291
|
+
- **Assets and history through the hooks.** `useAssetUrl(ref)` for a `src`, `useAsset(ref, { as })` to read a file (it finds the locale's copy in `<dir>/<locale>/<file>`), and `usePastState(query)` for past rounds, rather than hand-rolled effects. The wrapper already sets `lang`, `dir` and `color-scheme` on the document
|
|
292
|
+
- **Every string** goes through `src/i18n.ts`
|
|
293
|
+
- **The facilitator's screen** is `session/`: rendered full-bleed beneath a thin band of host chrome, it owns teams, participants and game controls (`guides/session.md`)
|
|
294
|
+
|
|
295
|
+
### Testing a surface
|
|
296
|
+
|
|
297
|
+
Every surface but the presentation scaffolds an `App.test.tsx`; the starter game's own tests are worked examples, and `pnpm run --silent sdk docs view guides/testing.md` is the reference.
|
|
298
|
+
|
|
299
|
+
- **Render through the helper**: `renderUserInterface()` from `@seriousgaming/sdk/ui/testing` (or `renderSessionUserInterface<SessionData>()`, or `renderSettingsEditor()` for setup and the scenario editor). It supplies every provider the host does; pass `data`, `settings`, `callbacks` and the rest as options
|
|
300
|
+
- **Never `vi.mock` the SDK, and never wire its providers by hand**
|
|
301
|
+
- **Use `fireEvent.change` on controlled inputs**: the mock `setData` does not update the value between `userEvent.type` keystrokes
|
|
302
|
+
|
|
303
|
+
## Presentations
|
|
304
|
+
|
|
305
|
+
The `presentation/` slot holds the facilitator's slide deck, built with `@seriousgaming/deck` at the SDK's version. It reads every team's state and writes nothing. A facilitator exports it to PDF or PowerPoint and edits the copy, so:
|
|
306
|
+
|
|
307
|
+
- **Use the building blocks** (`Heading`, `Text`, `Bullets`, `Table`) for anything a facilitator might edit; anything else exports as a picture
|
|
308
|
+
- **Render with `session === null`**, showing labelled sample figures where results would go
|
|
309
|
+
- **Branch on settings, not the scenario name**, and draw a custom animation's end state when `useSlide().mode` is `'static'`
|
|
310
|
+
- **Route every word through i18n**, and keep one element per slide
|
|
311
|
+
|
|
312
|
+
## LLM integration
|
|
313
|
+
|
|
314
|
+
Logic calls `callbacks.generateText()` only if its bundle lists `generateText` in `uses`; once listed it is always present on the generated `Callbacks`, so call it without `?.`. A surface reaches an LLM only through an endpoint. The dev server needs `SERIOUSGAMING_API_KEY` in the environment or `.env`, and `createTestSession` stubs it offline. See `guides/llm-integration.md`.
|
|
315
|
+
|
|
316
|
+
## Keeping the SDK up to date
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
pnpm run --silent sdk version # the installed version
|
|
320
|
+
pnpm run sdk docs view changelog.md # newest first: read every entry above your version
|
|
321
|
+
pnpm run sdk docs view guides/upgrading.md # a step for each breaking change
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Upgrade only when the user asks, or when the changelog has a fix the task needs, and then follow `.claude/rules/workflow-update-sdk-version.md`: it bumps the SDK, its tools and the deck together, and adopts the newer scaffold guidance without overwriting yours.
|
|
325
|
+
|
|
326
|
+
## Troubleshooting
|
|
327
|
+
|
|
328
|
+
| Problem | Solution |
|
|
329
|
+
| ------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
330
|
+
| Types not updating | `pnpm run generate` |
|
|
331
|
+
| A capability is missing in code | Add it to the resource's `reads` or `writes`; `update` regenerates the types |
|
|
332
|
+
| A surface's write is refused | Add the capability to that surface's `writes` |
|
|
333
|
+
| Build failing | `pnpm run --silent sdk build --validate-only --json`, then read `diagnostics` |
|
|
334
|
+
| Edit not showing, or `SETTINGS_STALE` | A setting, `teams` or a capability's scope applies only at init: `sdk init` |
|
|
335
|
+
| A hook reads empty data in a surface | Two copies of a package with React context: add it to `resolve.dedupe` in `vite.config.ts` |
|
|
336
|
+
| Anything else | `pnpm run --silent sdk docs view troubleshooting.md` |
|
|
337
|
+
|
|
338
|
+
Keep `DESIGN.md` up to date as the game changes.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# DESIGN.md
|
|
2
|
+
|
|
3
|
+
This document describes the design of the **{{ name }}** game. Keep it up to date as you build.
|
|
4
|
+
|
|
5
|
+
Unless it was created with `--empty`, the package was scaffolded with a working game already in it: a lemonade stand, where each team decides every morning how many lemonades, iced teas, and cookies to make, pays for all of it, and pours away what does not sell. The prompts below are written for the game you are going to build instead. Answer them as you replace the model, and delete the examples as you go.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
**Package identifier:** `{{ identifier }}`
|
|
10
|
+
|
|
11
|
+
A package is a game. The package keeps its dotted reverse-DNS identifier, set in `seriousgaming.sdk.json`; the game's is that with `.game` appended, and is never authored.
|
|
12
|
+
|
|
13
|
+
### Purpose
|
|
14
|
+
|
|
15
|
+
What does this game model, and what decisions does it help players make? Who plays it, and what real-world situation does it stand in for?
|
|
16
|
+
|
|
17
|
+
**Example:** 'Models a retail inventory system, so store managers feel the trade-off between carrying costs and stocking out.'
|
|
18
|
+
|
|
19
|
+
### Key concepts
|
|
20
|
+
|
|
21
|
+
The business concepts the game is made of.
|
|
22
|
+
|
|
23
|
+
**Example:**
|
|
24
|
+
|
|
25
|
+
- **Demand**: customers wanting to buy, per day
|
|
26
|
+
- **Inventory**: stock on hand
|
|
27
|
+
- **Reorder point**: the level that triggers an order
|
|
28
|
+
- **Lead time**: days between ordering and delivery
|
|
29
|
+
|
|
30
|
+
### Assumptions and constraints
|
|
31
|
+
|
|
32
|
+
What the model simplifies, and what it deliberately leaves out.
|
|
33
|
+
|
|
34
|
+
**Example:**
|
|
35
|
+
|
|
36
|
+
- Demand is predictable, with no seasonality
|
|
37
|
+
- Lead times are constant
|
|
38
|
+
- Orders arrive whole rather than split across shipments
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Session shape
|
|
43
|
+
|
|
44
|
+
- **Ticks:** how many rounds, and what one round represents
|
|
45
|
+
- **Teams:** `independent` (each team on its own clock, and the default when `teams.mode` is omitted) or `shared-state` (every team advances together and can interact through shared capabilities)
|
|
46
|
+
- **Participants per team:** and whether several people write the same context at once, which is the thing that decides how state below has to be shaped
|
|
47
|
+
- **Locales:** which, and which is first (the game's name comes from the first)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Capabilities
|
|
52
|
+
|
|
53
|
+
`capabilities/<name>/`: the game's state, one named group of variables each. A capability is named by its directory, so the identifier is a short name and the bundle authors none.
|
|
54
|
+
|
|
55
|
+
### `<name>`
|
|
56
|
+
|
|
57
|
+
**Purpose:** why this data exists.
|
|
58
|
+
|
|
59
|
+
**Variables:**
|
|
60
|
+
|
|
61
|
+
- `VariableName` (type): what it means and its units. Default: value
|
|
62
|
+
|
|
63
|
+
**Example:**
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
- `DailyDemand` (number): customers per day. Default: 50
|
|
67
|
+
- `LeadTime` (number): days to receive an order. Default: 3
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Scope:** `team` (default), `team-readable` (other teams' surfaces may observe it live), `shared` (one value for the whole session, written by an aggregate engine in a shared-state game or by the session interface), or `private` (engine bookkeeping, never delivered to any surface).
|
|
71
|
+
|
|
72
|
+
**Written by:** the engine, a user interface, or both. If several participants on one team write it at the same time, say how it is shaped to survive that: a collection keyed by id rather than an array, and deletion rather than a sentinel.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Engine
|
|
77
|
+
|
|
78
|
+
`engine/`: the code that computes the game. A game holds exactly one.
|
|
79
|
+
|
|
80
|
+
**Reads:** the capabilities it consumes, and why each is needed.
|
|
81
|
+
|
|
82
|
+
**Writes:** the capabilities it produces, and what it computes into them.
|
|
83
|
+
|
|
84
|
+
**Settings:** the settings resources it lists in `settings`: each property, its type, its default, and what it changes.
|
|
85
|
+
|
|
86
|
+
**What `Run` does**, in order. A simulation with several steps calls them as plain functions from its one `Run`:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
1. Read the decisions the team submitted
|
|
90
|
+
2. Work out demand from settings and the round
|
|
91
|
+
3. Update inventory
|
|
92
|
+
4. Compute revenue, costs and profit
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Aggregate?** If a shared-state game's engine needs every team's context at once (a market price, a ranking, anything cross-team), export `InitAggregate` and `RunAggregate` beside `Init`. There is no flag to set: the build reads the exports. A per-team `Run` is optional and runs for every team before `RunAggregate` is called once, so a step that must follow the aggregate is called from inside `RunAggregate` rather than from `Run`. The aggregate functions receive shared capabilities once, as `shared.context`, and each team's own capabilities in `teams`, and write shared ones under `shared` only.
|
|
96
|
+
|
|
97
|
+
**Edge cases:** what happens at the boundaries, and anything that must hold true whatever the input.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## User interfaces
|
|
102
|
+
|
|
103
|
+
**`ui/`**: the participant surface. A game holds one, so several views are components inside it rather than separate resources.
|
|
104
|
+
|
|
105
|
+
- **Reads / writes:** the capabilities it renders and the ones players change through it
|
|
106
|
+
- **Views:** the tabs or panels, and what each is for
|
|
107
|
+
- **How a player submits:** what they change, and what tells them a write did not land
|
|
108
|
+
|
|
109
|
+
**`session/`**: the facilitator surface. What the facilitator watches, and what they can change mid-session.
|
|
110
|
+
|
|
111
|
+
**`waiting_room/`**: what players see before the session opens, if the game has one.
|
|
112
|
+
|
|
113
|
+
**`setup/`** and **`scenario_editor/`**: the form a facilitator fills in at session creation, and the screen for authoring lasting scenarios, if the game has them, and which settings resources each `configures`.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## The rest
|
|
118
|
+
|
|
119
|
+
Only the ones this game uses.
|
|
120
|
+
|
|
121
|
+
- **`hooks/<name>/`**: one lifecycle signal each (a player joining, for example), run outside the tick. Numbered directories where order matters
|
|
122
|
+
- **`validators/<name>/`**: a read-only gate that rejects a UI write. Reach for one where a contested write cannot be fixed by the shape of the state
|
|
123
|
+
- **`endpoints/<name>/`**: server-side work a UI, or with `visibility: "external"` a caller outside the game, calls by identifier. It is how a surface reaches an LLM or a document parser, and, with `writes`, how it changes state outside the tick
|
|
124
|
+
- **`tick_controller/`**: when a round ends on its own rather than on the facilitator's say-so, or when the game must end on a participant's write between ticks. A game ends by setting `maxTicks`, or by returning `complete: true` from the engine's `Run`
|
|
125
|
+
- **`scenarios/<name>/`**: settings overrides, keyed by settings identifier, plus any assets that change with them
|
|
126
|
+
- **`guide/`**: the facilitator's guide, as numbered markdown chapters under `content/<locale>/`
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Design decisions
|
|
131
|
+
|
|
132
|
+
### Why the state is shaped this way
|
|
133
|
+
|
|
134
|
+
Why these capabilities, and why the boundaries fall where they do.
|
|
135
|
+
|
|
136
|
+
**Example:** 'Pricing and sales are separate capabilities because a later scenario changes how price is set without touching how sales are computed.'
|
|
137
|
+
|
|
138
|
+
### Decisions taken and not revisited
|
|
139
|
+
|
|
140
|
+
Choices someone will otherwise reopen, with the reason they were made.
|
|
141
|
+
|
|
142
|
+
### Not built yet
|
|
143
|
+
|
|
144
|
+
What is deliberately out of scope, and what might come later.
|