@seriousgaming/sdk-tools 0.0.0-stage → 1.0.0-alpha.1
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 +36 -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 +27368 -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 +16884 -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 +209 -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 +236 -0
- package/dist/docs/cli-and-dev-server.md +217 -0
- package/dist/docs/cli-error-codes.md +154 -0
- package/dist/docs/cli-reference.md +2488 -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 +575 -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 +335 -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 +122 -3
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Declare the runtime callbacks a resource uses
|
|
2
|
+
|
|
3
|
+
**Keywords:** api uses, generateText permission, extractText permission, generateSpeech permission, callbacks declaration, allow LLM, allow document parsing, allow text-to-speech
|
|
4
|
+
|
|
5
|
+
A logic-bearing resource (engine, hook, tick controller, endpoint) must declare the runtime callbacks it intends to call. Calls to undeclared callbacks throw at runtime, both in dev and production. The declaration lives in the resource's bundle file under `uses`. User interfaces do not declare these external-service callbacks: a UI reaches them through an [endpoint](create-endpoint.md) instead.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- A package with at least one engine, hook, tick controller, or endpoint that wants to call `callbacks.generateText`, `callbacks.extractText`, or `callbacks.generateSpeech`
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
1. Open the resource's bundle file (for example `engine/engine.sdk.json`).
|
|
14
|
+
2. Add a `uses` array to the `definition`, listing the callback names you intend to call. The values are `generateText`, `extractText`, `generateSpeech`, and `writeBlob`; see [The closed enum](../guides/llm-integration.md#the-apis-it-gates) for what each does:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"definition": {
|
|
19
|
+
"reads": ["text"],
|
|
20
|
+
"writes": ["summary"],
|
|
21
|
+
"uses": ["generateText"]
|
|
22
|
+
},
|
|
23
|
+
"logic": { "directory": ".", "entrypoint": "logic.ts" },
|
|
24
|
+
"rootDirectory": "."
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
3. Run `pnpm run sdk generate` to regenerate `types.ts`. The `Callbacks` type alias is narrowed to the callbacks you declared, each one present rather than optional, so undeclared calls fail at type-check before they fail at runtime and declared ones need no `?.` check.
|
|
29
|
+
4. In your logic file, type the function signature with the `Callbacks` generic so calls are narrowed:
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { getText, type RunFunction } from '@seriousgaming/sdk';
|
|
33
|
+
import type { Context, Settings, Callbacks } from './types';
|
|
34
|
+
|
|
35
|
+
export const Run: RunFunction<Context, Settings, Callbacks> = async ({
|
|
36
|
+
context,
|
|
37
|
+
callbacks,
|
|
38
|
+
}) => {
|
|
39
|
+
const summary = await callbacks.generateText('Summarise: ' + context.text);
|
|
40
|
+
return {
|
|
41
|
+
capabilities: {
|
|
42
|
+
...context,
|
|
43
|
+
summary: { content: getText(summary) },
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Verify
|
|
50
|
+
|
|
51
|
+
- `pnpm run sdk build` typechecks cleanly: when `Callbacks` is threaded through the function generic, undeclared calls fail at compile time
|
|
52
|
+
- Running the game with `pnpm run sdk tick` completes without throwing an `UndeclaredApiUseError`
|
|
53
|
+
|
|
54
|
+
## Troubleshooting
|
|
55
|
+
|
|
56
|
+
- **'UndeclaredApiUseError'** when running a game: the resource called a callback whose name is not in its `uses` array. Add the name and rebuild
|
|
57
|
+
- **TypeScript error: 'Property generateText does not exist on type Callbacks'**: the resource's `Callbacks` type alias was generated with an empty (or partial) `uses` array. Add the missing API to the bundle's `uses` and run `pnpm run sdk generate`
|
|
58
|
+
- **'`uses` has been removed from user interfaces'** when building: UIs cannot call these callbacks directly and carry no `uses`. Delete the field, move the call into an [endpoint](create-endpoint.md), and [invoke the endpoint from the UI](call-endpoint-from-ui.md)
|
|
59
|
+
- **`LLMClientError` or `TtsClientError` from a declared callback**: the service is there but not configured, usually a missing API key. See [Environment variables](../environment-variables.md)
|
|
60
|
+
|
|
61
|
+
## Related
|
|
62
|
+
|
|
63
|
+
- [Declare the APIs a resource uses](../guides/llm-integration.md#declare-the-apis-a-resource-uses): the full list of values and where each may be declared
|
|
64
|
+
- [Call an endpoint from a UI](call-endpoint-from-ui.md): how a UI reaches these services
|
|
65
|
+
- [LLM integration](../guides/llm-integration.md): calling `generateText` from the engine and endpoints
|
|
66
|
+
- [Speech generation](../guides/speech-generation.md): calling `generateSpeech` from the engine and endpoints
|
|
67
|
+
- [Upload and display files](upload-and-display-files.md): the `extractText` callback for document parsing
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Design state for concurrent writes
|
|
2
|
+
|
|
3
|
+
**Keywords:** concurrent writes, lost update, clobber, race condition, id-keyed map, collaborative state, shared array, deep merge, deletes, conflict, mergingSetData
|
|
4
|
+
|
|
5
|
+
Shape capabilities and `setData` calls so that several participants writing at once never overwrite each other. The host deep-merges each write (see [How setData merges](../guides/ui.md#how-setdata-merges)): writes to different keys merge, and writes to the same key resolve last-write-wins, so the aim is that normal play touches different keys.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- A capability your UI writes, declared in its `writes` (see [Create a capability](create-capability.md))
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
1. Send only what changed. Never spread the current snapshot into the write:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
// Merges with a team-mate's concurrent write to another field
|
|
17
|
+
setData({ decisions: { Committed: true } });
|
|
18
|
+
|
|
19
|
+
// Overwrites whatever a team-mate wrote a moment earlier
|
|
20
|
+
setData({ decisions: { ...data.decisions, Committed: true } });
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
2. Model a collection several people add to as an object keyed by a stable id, never an array. Arrays replace wholesale, so two simultaneous appends lose one:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
"Notes": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"default": {},
|
|
29
|
+
"additionalProperties": {
|
|
30
|
+
"title": "PinboardNote",
|
|
31
|
+
"type": "object",
|
|
32
|
+
"properties": {
|
|
33
|
+
"Author": { "type": "string" },
|
|
34
|
+
"Text": { "type": "string" }
|
|
35
|
+
},
|
|
36
|
+
"required": ["Author", "Text"],
|
|
37
|
+
"additionalProperties": false
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
setData({ pinboard: { Notes: { [noteId]: { Author: name, Text: text } } } });
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Sort for display by a field on each entry (a timestamp or the tick). The `title` names the item type in `types.ts` (see [Keyed collection](../guides/capabilities.md#keyed-collection)).
|
|
47
|
+
|
|
48
|
+
3. Remove an entry with `deletes`, never by writing `null`. A merge cannot remove a key, and a `null` stays in the context, in every broadcast, and in every snapshot:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
setData({}, { deletes: [`pinboard.Notes.${noteId}`] });
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Deletions apply after the patch, so one call can replace an entry. A path that resolves to nothing is a no-op, so two people clearing the same note is harmless. For a key containing a dot, pass segments: `[['pinboard', 'Notes', id]]`. A session surface deletes the same way, naming the team first, or from a shared capability with the one-argument form: `setData(teamId, {}, { deletes })` or `setData({}, { deletes })`.
|
|
55
|
+
|
|
56
|
+
4. Where each participant owns a value (a vote, an allocation), key it by `participant.id`, so each only writes their own key:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const participant = useUserInterfaceParticipant(); // null when observed
|
|
60
|
+
if (participant) setData({ votes: { Votes: { [participant.id]: 'yes' } } });
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
5. Await the writes that matter through `useMutationReporter`, and show its `failure` (leaving out `validation-rejected`, which `useUserInterfaceValidationRejections` already explains) and its `late` notice. Never retry on your own: on a `conflict` the host has already retried, so offer `failure.retry` behind a 'Try again' button. See [Awaiting state changes](../guides/ui.md#awaiting-state-changes).
|
|
64
|
+
|
|
65
|
+
6. Arbitrate a slot that is contested by design (two people claiming one role) with a [validator](create-validator.md) that compares `previous` with `next` and rejects the losing write. For a shared numeric pool, reconcile in the engine at tick time instead.
|
|
66
|
+
|
|
67
|
+
7. In component tests, merge as the host does with `mergingSetData`, or a minimal write that is correct in production fails the test. It applies `deletes` after the merge too:
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { mergingSetData } from '@seriousgaming/sdk/ui/testing';
|
|
71
|
+
|
|
72
|
+
const [data, setLocal] = useState(mockContext);
|
|
73
|
+
const setData = mergingSetData(setLocal);
|
|
74
|
+
setData({}, { deletes: [`pinboard.Notes.${noteId}`] });
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Verify
|
|
78
|
+
|
|
79
|
+
1. Open the same team's UI in two browser windows of `pnpm run sdk dev`
|
|
80
|
+
2. Change a different field, or add a different entry, in each at the same time: both changes appear in both windows
|
|
81
|
+
3. Clear an entry: it disappears, and its key is gone from `pnpm run sdk view` rather than holding `null`
|
|
82
|
+
|
|
83
|
+
## Troubleshooting
|
|
84
|
+
|
|
85
|
+
| Problem | Solution |
|
|
86
|
+
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
87
|
+
| One participant's change vanishes when another saves | A write spreads `...data[...]` or rewrites an array; reduce it to the changed entry |
|
|
88
|
+
| A cleared entry will not go away | The clear writes an empty patch; name the path in `deletes`, and check the path resolves |
|
|
89
|
+
| Writes work locally but conflict when hosted | The dev server serialises writes; await through `useMutationReporter` so the participant is told |
|
|
90
|
+
| A test fails on a write that works in the game | The mock merges differently from the host; use `mergingSetData` |
|
|
91
|
+
|
|
92
|
+
## Related
|
|
93
|
+
|
|
94
|
+
- [The starter game](../guides/starter-game.md): every step applied, in `capabilities/plan/`, `ui/src/TodayPanel.tsx`, `ui/src/writes.ts`, and `validators/capacity/`
|
|
95
|
+
- [User interfaces guide](../guides/ui.md#writing-to-context)
|
|
96
|
+
- [Create a validator](create-validator.md)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Distribute a built package
|
|
2
|
+
|
|
3
|
+
Wrap a built `dist/` directory into a single zip archive that a production runtime can ingest as an upload.
|
|
4
|
+
|
|
5
|
+
**Keywords:** distribute, package, bundle, zip, archive, upload, release, ci, production
|
|
6
|
+
|
|
7
|
+
## What you'll build
|
|
8
|
+
|
|
9
|
+
A `package bundle` step in your release pipeline that produces a single zip from `dist/`, then captures the result for CI artefact uploads.
|
|
10
|
+
|
|
11
|
+
The archive is the upload contract between the SDK and a production runtime. Its root mirrors the build layout, with `seriousgaming.json` at the top.
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
|
|
15
|
+
- `seriousgaming.sdk.json` in your working directory
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
### 1. Build the package
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm run sdk build
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This produces `dist/` with the package manifest, bundled logic, and built UIs.
|
|
26
|
+
|
|
27
|
+
### 2. Produce the distribution zip
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm run sdk package bundle
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The default output path is `dist/<identifier>-<version>.zip`, derived from `dist/seriousgaming.json`.
|
|
34
|
+
|
|
35
|
+
Use `-o, --output-file` to write the archive somewhere else:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pnpm run sdk package bundle --output-file artifacts/release.zip
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 3. Capture the result in CI
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pnpm run --silent sdk package bundle --json > bundle.json
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The envelope's `data` (see [JSON output](../cli-reference.md#json-output)) contains:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"outputFile": "/path/to/dist/com.example.pkg-1.2.0.zip",
|
|
52
|
+
"size": 1234567,
|
|
53
|
+
"fileCount": 42,
|
|
54
|
+
"identifier": "com.example.pkg",
|
|
55
|
+
"version": "1.2.0"
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Use this in CI to attach the zip as a build artefact, tag the release with the version, or trigger an upload step.
|
|
60
|
+
|
|
61
|
+
## Verify
|
|
62
|
+
|
|
63
|
+
- The output file exists at the path you expect
|
|
64
|
+
- Unzipping it produces `seriousgaming.json` at the archive root, alongside a directory for each built resource, laid out as in the package: `engine/`, `ui/`, `session/`, `waiting_room/` and `guide/` at their slots, and `hooks/<name>/`, `validators/<name>/` and the other plural kinds beneath their directories. Capabilities and settings have no directory of their own, since their schemas are carried in `seriousgaming.json`, and a scenario with no assets adds nothing to the archive
|
|
65
|
+
- The runtime you target accepts the archive as an upload
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
unzip -l dist/com.example.pkg-1.2.0.zip | head
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Troubleshooting
|
|
72
|
+
|
|
73
|
+
- **'Build directory not found'** - run `pnpm run sdk build` before `pnpm run sdk package bundle`. The bundle command does not build
|
|
74
|
+
- **'Manifest not found'** - the build directory exists but has no `seriousgaming.json`. Re-run the build; check that it completed without errors
|
|
75
|
+
- **Wrong identifier or version in the filename** - both are read from `dist/seriousgaming.json`, but they are authored in different places: the identifier in `seriousgaming.sdk.json`, the version in `package.json` (the manifest carries no version field). Fix whichever is wrong and rebuild
|
|
76
|
+
|
|
77
|
+
## Related
|
|
78
|
+
|
|
79
|
+
- [CLI reference: package bundle](../cli-reference.md#package-a-built-game-for-distribution)
|
|
80
|
+
- [Publish a package to the Serious Gaming platform](./publish-a-package.md)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Test UIs end to end with Playwright
|
|
2
|
+
|
|
3
|
+
Test your game's screens in a real browser against an isolated dev server.
|
|
4
|
+
|
|
5
|
+
**Keywords:** e2e, playwright, browser test, integration test, UI test, iframe, reinitialiseSession, resetSession, runCommand, waitForState, advanceClock, armFault, startDevServer
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- A game package that builds (`pnpm run build`)
|
|
10
|
+
- A package made with `sdk new package` already has the Playwright setup (`playwright.config.ts`, `testing/e2e/global-setup.ts`, a starter spec, and a `test:e2e` script): skip to step 4
|
|
11
|
+
|
|
12
|
+
## Steps
|
|
13
|
+
|
|
14
|
+
1. Install Playwright:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add -D @playwright/test
|
|
18
|
+
pnpm exec playwright install chromium
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
2. Create `testing/e2e/global-setup.ts`, which starts an isolated dev server in Playwright's own process and stops it when the run ends:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { dirname } from 'path';
|
|
25
|
+
|
|
26
|
+
import { startDevServer } from '@seriousgaming/sdk-tools/client';
|
|
27
|
+
import type { FullConfig } from '@playwright/test';
|
|
28
|
+
|
|
29
|
+
export default async function globalSetup(config: FullConfig): Promise<() => Promise<void>> {
|
|
30
|
+
const server = await startDevServer({
|
|
31
|
+
packagePath: config.configFile ? dirname(config.configFile) : process.cwd(),
|
|
32
|
+
});
|
|
33
|
+
process.env.SERIOUSGAMING_DEV_URL = server.url;
|
|
34
|
+
return () => server.stop();
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
3. Create `playwright.config.ts`, and add `"test:e2e": "playwright test"` to `package.json`:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { defineConfig } from '@playwright/test';
|
|
42
|
+
|
|
43
|
+
export default defineConfig({
|
|
44
|
+
testDir: './testing/e2e',
|
|
45
|
+
globalSetup: './testing/e2e/global-setup.ts',
|
|
46
|
+
workers: 1,
|
|
47
|
+
use: { baseURL: process.env.SERIOUSGAMING_DEV_URL },
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
One worker, because the tests share one session. Do not start the server with `webServer` running `pnpm run sdk dev`: Playwright cannot stop it through `pnpm`, and the run hangs.
|
|
52
|
+
|
|
53
|
+
4. Start each test from a fresh session, and open a surface with `?bare` so the page holds your game and none of the console:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { expect, test } from '@playwright/test';
|
|
57
|
+
import { DEV_SERVER, reinitialiseSession } from '@seriousgaming/sdk/e2e';
|
|
58
|
+
|
|
59
|
+
test.beforeEach(async () => {
|
|
60
|
+
await reinitialiseSession();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('the stall opens on day one', async ({ page }) => {
|
|
64
|
+
await page.goto(DEV_SERVER.bare(DEV_SERVER.playerUrl(1)));
|
|
65
|
+
|
|
66
|
+
const frame = page.frameLocator(DEV_SERVER.iframeSelector('ui'));
|
|
67
|
+
await expect(frame.getByTestId('day')).toHaveText('Day 1');
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`playerUrl(team, participant?)` is 1-based. `observeUrl`, `waitingRoomUrl`, `setupUrl`, and the other builders are in the [e2e API reference](../api/sdk-e2e.md#dev_server).
|
|
72
|
+
|
|
73
|
+
5. Act through the game's own controls, or as the facilitator with `runCommand`, and read the result with `getState`:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { getState, runCommand } from '@seriousgaming/sdk/e2e';
|
|
77
|
+
|
|
78
|
+
await runCommand({ type: 'tick' });
|
|
79
|
+
expect((await getState()).session.completedTicks).toBe(1);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
A refused command answers `{ ok: false, outcome }` rather than throwing.
|
|
83
|
+
|
|
84
|
+
6. Reach the failure paths your UI must handle by arming a fault, which fires once:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
await runCommand({ type: 'armFault', fault: 'conflict' });
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The faults are `conflict`, `denied`, `llm-failure`, `hook-throw`, `endpoint-timeout`, and `tick-failure`.
|
|
91
|
+
|
|
92
|
+
7. Wait for what happens later with `waitForState`, never a sleep. For automatic ticks, pass `clock: new FakeClock()` to `startDevServer` and move time with `advanceClock(ms)`.
|
|
93
|
+
|
|
94
|
+
## Verify
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pnpm run test:e2e
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Every spec passes. Add `--headed` to watch, or `--debug` to step through.
|
|
101
|
+
|
|
102
|
+
## Troubleshooting
|
|
103
|
+
|
|
104
|
+
| Problem | Solution |
|
|
105
|
+
| ------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
106
|
+
| The run hangs after the last test | The server was started with `webServer`; start it with `startDevServer` in a global setup (step 2) |
|
|
107
|
+
| Tests pass alone and fail in a suite | Call `reinitialiseSession()` in `beforeEach`; `resetSession()` keeps teams an earlier test added |
|
|
108
|
+
| Iframe content is not there yet | Wait on a locator inside the frame (`await frame.locator('#x').waitFor()`) |
|
|
109
|
+
| The `/e2e` import does not resolve | Set `moduleResolution` to `"bundler"` or `"nodenext"` in `tsconfig.json` |
|
|
110
|
+
| Every spec fails on a missing browser | Run `pnpm exec playwright install chromium` after a Playwright upgrade |
|
|
111
|
+
|
|
112
|
+
## Related
|
|
113
|
+
|
|
114
|
+
- [Testing guide: browser tests](../guides/testing.md#browser-e2e-tests-playwright)
|
|
115
|
+
- [`@seriousgaming/sdk/e2e` reference](../api/sdk-e2e.md)
|
|
116
|
+
- [Test a user interface](test-user-interface.md)
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# End a game when a condition is met
|
|
2
|
+
|
|
3
|
+
Stop a game automatically when a goal is reached, rather than after a fixed number of ticks.
|
|
4
|
+
|
|
5
|
+
**Keywords:** dynamic ticks, goal-based game, end condition, complete, finish game, variable length, no fixed ticks
|
|
6
|
+
|
|
7
|
+
## What you'll build
|
|
8
|
+
|
|
9
|
+
An engine that ends the game once a target value in context is reached, by returning `complete: true` from `Run`. The manifest will have no `maxTicks` field: play continues until the condition fires. No tick controller is needed; that is only for timing ticks.
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
- A package with an engine and at least one capability that your condition will check
|
|
14
|
+
- `seriousgaming.sdk.json` in your working directory
|
|
15
|
+
|
|
16
|
+
## Steps
|
|
17
|
+
|
|
18
|
+
### 1. Give the engine the target
|
|
19
|
+
|
|
20
|
+
Put the target in a settings resource, so a scenario or a facilitator can change it, and list it in the engine's `settings`:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pnpm run sdk new settings '{
|
|
24
|
+
"definition": {
|
|
25
|
+
"identifier": "goal",
|
|
26
|
+
"settingsSchema": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"properties": {
|
|
29
|
+
"ProfitTarget": { "type": "number", "default": 10000 }
|
|
30
|
+
},
|
|
31
|
+
"additionalProperties": false
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}'
|
|
35
|
+
pnpm run sdk update engine --patch '[{
|
|
36
|
+
"op": "add",
|
|
37
|
+
"path": "/definition/settings",
|
|
38
|
+
"value": ["goal"]
|
|
39
|
+
}]'
|
|
40
|
+
pnpm run generate
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If the engine already lists settings, append instead, with `"path": "/definition/settings/-"` and `"value": "goal"`.
|
|
44
|
+
|
|
45
|
+
### 2. Return `complete` from `Run`
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
export const Run: RunFunction<Context, Settings> = async ({ context, settings }) => {
|
|
49
|
+
const next = { ...context /* ...this round's updates */ };
|
|
50
|
+
return {
|
|
51
|
+
capabilities: next,
|
|
52
|
+
complete: next.financials.Profit >= settings.goal.ProfitTarget,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`complete: true` ends the game for the team this `Run` ran for; leaving it out carries on. How that plays out across teams, and what undo does, is in [Game completion](../guides/tick-controller.md#game-completion).
|
|
58
|
+
|
|
59
|
+
### 3. Drop the tick ceiling
|
|
60
|
+
|
|
61
|
+
Remove `maxTicks` from `seriousgaming.sdk.json` (or keep it as a safety ceiling):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pnpm run sdk update package --patch '[{ "op": "remove", "path": "/maxTicks" }]'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
> `build` warns that only the engine can end the game. That is expected here.
|
|
68
|
+
|
|
69
|
+
### 4. Build
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pnpm run build
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Verify
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Initialise
|
|
79
|
+
pnpm run sdk init
|
|
80
|
+
|
|
81
|
+
# Advance ticks until the goal fires
|
|
82
|
+
pnpm run sdk tick
|
|
83
|
+
|
|
84
|
+
# Once the condition is met the team is complete, and another tick exits with an error
|
|
85
|
+
pnpm run sdk view
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
You can also start the dev server: tick requests are rejected and the dev drawer's **Tick** button is disabled once the game is complete.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pnpm run sdk dev
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Troubleshooting
|
|
95
|
+
|
|
96
|
+
| Problem | Solution |
|
|
97
|
+
| ----------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
98
|
+
| `complete` never becomes `true` | Add a `console.log` in `Run` to inspect the context values being checked |
|
|
99
|
+
| Type errors in logic.ts | Run `pnpm run generate` to regenerate `types.ts` |
|
|
100
|
+
| Game completes immediately (tick 0) | Your condition is true on the initial state; check your init logic or raise the target threshold |
|
|
101
|
+
| Want a hard ceiling too | Keep a `maxTicks` field in the manifest: both apply, and whichever triggers first ends the game |
|
|
102
|
+
|
|
103
|
+
## Variations
|
|
104
|
+
|
|
105
|
+
### End after any team reaches the goal (aggregate engine)
|
|
106
|
+
|
|
107
|
+
An aggregate engine sees every team at once. `complete: true` under `shared` ends the game for all of them; on a `teams` entry it ends it for that team alone:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
export const RunAggregate: RunAggregateFunction<TeamContext, SharedContext, Settings> = async ({
|
|
111
|
+
teams,
|
|
112
|
+
settings,
|
|
113
|
+
}) => ({
|
|
114
|
+
shared: {
|
|
115
|
+
capabilities: {},
|
|
116
|
+
complete: teams.some((t) => t.context.financials.Profit >= settings.goal.ProfitTarget),
|
|
117
|
+
},
|
|
118
|
+
teams: teams.map((t) => ({ capabilities: t.context })),
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Replace `.some` with `.every` to require all teams to reach the goal before the game ends.
|
|
123
|
+
|
|
124
|
+
### End the moment a participant's write reaches the goal
|
|
125
|
+
|
|
126
|
+
To end the game on the write itself rather than at the next tick, use a tick controller that reads the capability (see [From the tick controller](../guides/tick-controller.md#from-the-tick-controller)):
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
export const GetTickController: GetTickControllerFunction<Context, Settings> = async ({
|
|
130
|
+
context,
|
|
131
|
+
settings,
|
|
132
|
+
}) => ({
|
|
133
|
+
enabled: false,
|
|
134
|
+
complete: context.financials.Profit >= settings.goal.ProfitTarget,
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The tick controller receives the target only if it lists `goal` in its own `settings` too.
|
|
139
|
+
|
|
140
|
+
### Automatic ticks with a goal
|
|
141
|
+
|
|
142
|
+
Timing is what a tick controller is for. Return `enabled: true` and an `intervalMs`, and keep the goal in the engine's `complete`:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
return { intervalMs: 30_000, enabled: true };
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The game ticks automatically every 30 seconds and stops as soon as `Run` reports the profit target reached.
|
|
149
|
+
|
|
150
|
+
## Related
|
|
151
|
+
|
|
152
|
+
- [Game completion](../guides/tick-controller.md#game-completion): both ways to end a game, what completion does, and multi-team patterns
|
|
153
|
+
- [Create a tick controller](create-tick-controller.md): add timer-based automatic ticking
|
|
154
|
+
- [Create a settings resource](create-settings.md): parameters a scenario or facilitator can change
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Fix duplicate package errors in a UI
|
|
2
|
+
|
|
3
|
+
Resolve 'Invalid hook call' and similar errors caused by multiple copies of the same package
|
|
4
|
+
being loaded by the dev server.
|
|
5
|
+
|
|
6
|
+
**Keywords:** duplicate react, invalid hook call, resolve dedupe, vite config, @react-three/fiber,
|
|
7
|
+
emotion, multiple instances, hooks error, cannot read properties of null useState, shared package,
|
|
8
|
+
empty data, duplicate sdk
|
|
9
|
+
|
|
10
|
+
Two copies of React in one page (a library that bundles its own, such as `@react-three/fiber`) make every hook fail. Two copies of `@seriousgaming/sdk` fail silently instead: the second copy's hooks find none of the host's providers and read empty data. Both are fixed by resolving each package to one copy.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
- A user interface that uses a third-party React library
|
|
15
|
+
- The dev server (`pnpm run sdk dev`) running and showing the error above
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
### 1. Add a `vite.config.ts` to your project root
|
|
20
|
+
|
|
21
|
+
Create `vite.config.ts` alongside your `seriousgaming.sdk.json`:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { defineConfig } from 'vite';
|
|
25
|
+
|
|
26
|
+
export default defineConfig({
|
|
27
|
+
resolve: {
|
|
28
|
+
dedupe: [
|
|
29
|
+
'react',
|
|
30
|
+
'react-dom',
|
|
31
|
+
'@emotion/react',
|
|
32
|
+
'@emotion/styled',
|
|
33
|
+
'@seriousgaming/sdk',
|
|
34
|
+
'@seriousgaming/deck',
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`resolve.dedupe` tells Vite to resolve these packages to the single copy at the project root,
|
|
41
|
+
however many paths in the dependency tree lead to them.
|
|
42
|
+
|
|
43
|
+
If your package was created with `sdk new package` and already has a `vite.config.ts`, add
|
|
44
|
+
entries to its `dedupe` list rather than creating a second file.
|
|
45
|
+
|
|
46
|
+
### 2. Restart the dev server
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pnpm run sdk dev
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The SDK picks up your `vite.config.ts` automatically, and applies the same file to
|
|
53
|
+
`pnpm run sdk build`, so one file fixes both dev and production builds.
|
|
54
|
+
|
|
55
|
+
### 3. Add more entries when you need them
|
|
56
|
+
|
|
57
|
+
Add a package to `dedupe` whenever you install a library that:
|
|
58
|
+
|
|
59
|
+
- Lists React (or Emotion) as a **peer dependency**, and
|
|
60
|
+
- Also `require()`s that peer internally in its own bundled code
|
|
61
|
+
|
|
62
|
+
Or any package that holds React context and that more than one path leads to, as the SDK does. A package created with `sdk new package` lists React, Emotion, the SDK and the deck already, in both `vite.config.ts` and `vitest.config.ts`.
|
|
63
|
+
|
|
64
|
+
Example with additional libraries:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { defineConfig } from 'vite';
|
|
68
|
+
|
|
69
|
+
export default defineConfig({
|
|
70
|
+
resolve: {
|
|
71
|
+
dedupe: [
|
|
72
|
+
'react',
|
|
73
|
+
'react-dom',
|
|
74
|
+
'@emotion/react',
|
|
75
|
+
'@emotion/styled',
|
|
76
|
+
'@seriousgaming/sdk',
|
|
77
|
+
'@seriousgaming/deck',
|
|
78
|
+
'@react-three/fiber',
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Verify
|
|
85
|
+
|
|
86
|
+
Reload the page in the dev server. The component that raised 'Invalid hook call' renders, and
|
|
87
|
+
the browser console shows no hook errors. Run `pnpm run sdk build` to confirm the production
|
|
88
|
+
build is clean too.
|
|
89
|
+
|
|
90
|
+
## Troubleshooting
|
|
91
|
+
|
|
92
|
+
| Problem | Solution |
|
|
93
|
+
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| The error persists after the restart | Add the library that bundles React itself (for example `@react-three/fiber`) to `dedupe` |
|
|
95
|
+
| The SDK warns about two Vite configs | Remove the `vite.config.ts` inside `ui/` and keep one at the project root; see [Customising Vite](../guides/ui.md#customising-vite-dev-and-build) |
|
|
96
|
+
| A Vite option in your file has no effect | The SDK owns structural fields (`root`, `base`, build output, entry points); additive options such as `resolve.dedupe`, `resolve.alias`, `optimizeDeps`, and `plugins` merge in |
|
|
97
|
+
|
|
98
|
+
## Related
|
|
99
|
+
|
|
100
|
+
- [Customising Vite](../guides/ui.md#customising-vite-dev-and-build): where the file goes and what merges
|
|
101
|
+
- [User interfaces guide](../guides/ui.md)
|