@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,82 @@
|
|
|
1
|
+
# Take a file in an endpoint
|
|
2
|
+
|
|
3
|
+
**Keywords:** file upload, upload a file to an endpoint, image upload, PDF upload, blob input, data URI, base64, attach file, AI agent upload, MCP file, API upload, script upload
|
|
4
|
+
|
|
5
|
+
Let an endpoint accept a file, such as a team photo or a research PDF, from a game UI, a script calling the API, or an AI agent.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- An endpoint that already works (see [Create an endpoint](create-endpoint.md))
|
|
10
|
+
- Somewhere for the file to go: usually a capability holding its `blob:` reference, which a screen shows with `getAssetUrl` or logic reads with `readAsset` or `extractText`
|
|
11
|
+
|
|
12
|
+
## Steps
|
|
13
|
+
|
|
14
|
+
1. Mark the request property as a file with `blob`, saying which types it takes and how large:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"definition": {
|
|
19
|
+
"requestSchema": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {
|
|
22
|
+
"document": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "The research, as a PDF.",
|
|
25
|
+
"blob": { "accept": ["application/pdf"], "maxBytes": 5242880 }
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"required": ["document"],
|
|
29
|
+
"additionalProperties": false
|
|
30
|
+
},
|
|
31
|
+
"writes": ["upload"]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`accept` takes media types and `type/*` wildcards (`"image/*"`), and allows any type when left out. The property must be `"type": "string"`, or the build fails.
|
|
37
|
+
|
|
38
|
+
2. Regenerate the types, so the field is typed as a reference:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pnpm run sdk generate
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`Request['document']` is now `` `blob:${string}` ``.
|
|
45
|
+
|
|
46
|
+
3. Use the reference in the handler:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
export const Handle: Handler = async ({ request, context }) => ({
|
|
50
|
+
response: { replaced: context.upload.DocumentBlobId !== '' },
|
|
51
|
+
capabilities: { upload: { ...context.upload, DocumentBlobId: request.document } },
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The runtime has already checked the file against the declaration, including its real type from its bytes rather than the one the caller claimed, and stored it. There is nothing to decode or check here. To read the bytes, call `callbacks.readAsset(request.document)`.
|
|
56
|
+
|
|
57
|
+
4. If the endpoint is for callers outside the game, say how to send the file in its description (see [Make an endpoint callable from outside the game](make-an-endpoint-external.md)):
|
|
58
|
+
|
|
59
|
+
> Send a PDF of at most 5 MB: inline as a data: URI, or upload it first and send the blob: reference.
|
|
60
|
+
|
|
61
|
+
A caller sends either a `blob:` reference or a `data:` URI, never a URL (see [Files in and out](../guides/endpoints.md#files-in-and-out)).
|
|
62
|
+
|
|
63
|
+
## Verify
|
|
64
|
+
|
|
65
|
+
- `pnpm run sdk endpoint call attachresearch --external --team 1 --attach document=./research.pdf` succeeds, and `pnpm run sdk view` shows the reference on the team's board
|
|
66
|
+
- `pnpm run sdk blob upload ./research.pdf`, then the call with `--request '{"document": "blob:..."}'`, succeeds the same way
|
|
67
|
+
- The same call with a PNG is refused before your handler runs
|
|
68
|
+
|
|
69
|
+
## Troubleshooting
|
|
70
|
+
|
|
71
|
+
- _'must be a blob: reference or a data: URI'_: The caller sent something else, commonly a URL or a bare id without `blob:`
|
|
72
|
+
- _'claims image/png, but its bytes are application/pdf'_: The `data:` URI's type does not match the file. Fix the type the caller sends
|
|
73
|
+
- _'is image/gif, which is not one of ...'_: Widen `accept`, or send a type it lists
|
|
74
|
+
- _'names a blob this session does not hold'_: The reference is wrong, or the file was uploaded to a different session
|
|
75
|
+
- _'carries more than ... bytes a data: URI may carry inline'_: Upload the file first and send the reference
|
|
76
|
+
- _'declares `blob` but is not `"type": "string"`'_: A file field is a string; change its type
|
|
77
|
+
|
|
78
|
+
## Related
|
|
79
|
+
|
|
80
|
+
- [Endpoints (concept)](../guides/endpoints.md)
|
|
81
|
+
- [File handling](../guides/file-handling.md)
|
|
82
|
+
- [Make an endpoint callable from outside the game](make-an-endpoint-external.md)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Test a multi-role game in the dev server
|
|
2
|
+
|
|
3
|
+
Drive a game that needs several participants per team (for example a buyer and a seller who each act before a tick advances) from a single dev-server session, using the per-team participant roster.
|
|
4
|
+
|
|
5
|
+
**Keywords:** multiple participants, roles, multi-role, participant roster, add participant, rename participant, open participant, buyer seller, connected, /play participant
|
|
6
|
+
|
|
7
|
+
## What you'll build
|
|
8
|
+
|
|
9
|
+
A running session where one team has several named participants, each opened in its own browser tab and acting independently, so a role-based interaction can be played through end to end.
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
- A game whose UI branches on participant identity via `useUserInterfaceParticipant()` (see [Create a user interface](create-user-interface.md))
|
|
14
|
+
- The dev server running (`pnpm run sdk dev`)
|
|
15
|
+
|
|
16
|
+
## Steps
|
|
17
|
+
|
|
18
|
+
### 1. Open the console and expand a team
|
|
19
|
+
|
|
20
|
+
A fresh team starts with one participant, at `/team/<n>/player/1`. Participants are positions in the URL, 1-based, so there is no id to look up and nothing to resolve.
|
|
21
|
+
|
|
22
|
+
### 2. Add and name the participants you need
|
|
23
|
+
|
|
24
|
+
Use **Add participant** on the team's row in the dev drawer to create each role, or `pnpm run sdk participant add Buyer --team 1` from the CLI, which names it too. To rename one later, use `pnpm run sdk participant rename <participantId> <name>`; `pnpm run sdk view` lists the ids. Creating a participant fires the `participantJoin` hook, exactly as it would in production, so any join-driven setup runs.
|
|
25
|
+
|
|
26
|
+
If the game declares `teams.perTeam.minParticipants` / `maxParticipants`, the roster shows an advisory caption (for example '2–5 recommended for this scenario'). It is guidance only and never blocks adding or removing.
|
|
27
|
+
|
|
28
|
+
### 3. Open each participant in its own tab
|
|
29
|
+
|
|
30
|
+
Open `/team/<n>/player/<p>` for each role, in its own tab, and arrange them side by side. Each tab holds its own live connection, so every participant shows as connected at the same time, which is what a multi-role game gates on.
|
|
31
|
+
|
|
32
|
+
### 4. Drive the interaction
|
|
33
|
+
|
|
34
|
+
Act as each role in its tab. Because each tab carries its own participant identity, every state mutation is attributed to the right actor, so per-participant validators see the correct actor and the game advances once the roles have acted.
|
|
35
|
+
|
|
36
|
+
## Verify
|
|
37
|
+
|
|
38
|
+
- The team's row in the dev drawer counts every participant you added
|
|
39
|
+
- Opening two participants in two tabs shows both as connected in `pnpm run sdk view` (each participant carries `connected`)
|
|
40
|
+
- Renaming a participant updates its name in every open view
|
|
41
|
+
|
|
42
|
+
## Troubleshooting
|
|
43
|
+
|
|
44
|
+
- **'No such participant' when opening a link.** The URL asks for a seat the team does not have: opening `/team/1/player/3` before adding a third participant, or following a stale link. The screen says how many the team holds and offers **Add participant**. A second tab is not a second participant, so add them in step 2 first. The dev server does not invent one, and does not serve an unidentified surface: a write it cannot attribute to a participant would skip the game's validators, so it is refused instead
|
|
45
|
+
- **A participant shows as disconnected.** Its tab is closed. Connection state is live only; it is never persisted, so every participant starts disconnected after a dev-server restart even though its name is restored
|
|
46
|
+
- **A new participant did not trigger role setup.** Role assignment usually lives in a `participantJoin` hook. Confirm the game declares the hook (see [Create a hook](create-hook.md))
|
|
47
|
+
|
|
48
|
+
## Related
|
|
49
|
+
|
|
50
|
+
- [Accessing participant info](../guides/ui.md#accessing-participant-info)
|
|
51
|
+
- [Test UIs end to end with Playwright](e2e-testing.md): drive several participants from a test
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Test a user interface
|
|
2
|
+
|
|
3
|
+
Write and run component tests for a user interface with Vitest, React Testing Library, and the SDK's render helpers.
|
|
4
|
+
|
|
5
|
+
**Keywords:** test UI, unit test, React testing, App.test.tsx, vitest, testing library, mock context, setData, controlled input, renderUserInterface, renderSessionUserInterface, renderSettingsEditor, mock participant, participant roster, sessionKey
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- A user interface, with the scaffolded `vitest.config.ts`, `test-setup.ts`, and `src/App.test.tsx`
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
1. Open the test beside the component: `ui/src/App.test.tsx` for the game UI (`session/src/`, `waiting_room/src/`, `setup/src/` and the others for theirs).
|
|
14
|
+
|
|
15
|
+
2. Render through the helper for the surface, giving it only what the test cares about. Every other context value has a complete default, so never build the providers by hand:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { fireEvent, screen } from '@testing-library/react';
|
|
19
|
+
import { renderUserInterface } from '@seriousgaming/sdk/ui/testing';
|
|
20
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
21
|
+
|
|
22
|
+
import { defaultContext, defaultSettings } from '../defaults';
|
|
23
|
+
import './i18n';
|
|
24
|
+
import App from './App';
|
|
25
|
+
|
|
26
|
+
describe('Dashboard', () => {
|
|
27
|
+
it('shows the current stock', () => {
|
|
28
|
+
renderUserInterface(<App />, {
|
|
29
|
+
data: { ...defaultContext, inventory: { Stock: 100 } },
|
|
30
|
+
settings: defaultSettings,
|
|
31
|
+
});
|
|
32
|
+
expect(screen.getByLabelText(/stock/i)).toHaveValue(100);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Use `renderSessionUserInterface` for a session UI and `renderSettingsEditor` for setup and the scenario editor (see [Using render helpers](../guides/testing.md#using-render-helpers)).
|
|
38
|
+
|
|
39
|
+
3. Assert on writes with a mock `setData`. Drive a controlled input with `fireEvent.change`, since a mocked `setData` never updates the displayed value between keystrokes:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
it('writes the new price', () => {
|
|
43
|
+
const setData = vi.fn();
|
|
44
|
+
renderUserInterface(<App />, { data: defaultContext, settings: defaultSettings, setData });
|
|
45
|
+
|
|
46
|
+
fireEvent.change(screen.getByLabelText(/price/i), { target: { value: '50' } });
|
|
47
|
+
|
|
48
|
+
expect(setData).toHaveBeenCalledWith({ pricing: { Price: 50 } });
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
4. Pass any callback you assert on. Callbacks you leave out default to no-ops:
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
const requestTick = vi.fn();
|
|
56
|
+
renderUserInterface(<App />, {
|
|
57
|
+
data: defaultContext,
|
|
58
|
+
settings: defaultSettings,
|
|
59
|
+
callbacks: { requestTick },
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
5. For a participant-aware UI, pass `participant` and `participants`, built with `createMockParticipant` and `createMockParticipants`. The roster defaults to `[]`, the real first-render state:
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { createMockParticipants } from '@seriousgaming/sdk/ui/testing';
|
|
67
|
+
|
|
68
|
+
renderUserInterface(<App />, { data: defaultContext, participants: createMockParticipants(3) });
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
6. Run the tests:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pnpm run test
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Verify
|
|
78
|
+
|
|
79
|
+
Every test passes, and `pnpm typecheck` accepts the mock data: `defaultContext` and `defaultSettings` come from the generated `defaults.ts`, so a missing capability is a type error.
|
|
80
|
+
|
|
81
|
+
## Troubleshooting
|
|
82
|
+
|
|
83
|
+
| Problem | Solution |
|
|
84
|
+
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
85
|
+
| 'Invalid hook call' | Two copies of React; see [Fix duplicate package errors in a UI](fix-duplicate-packages.md) |
|
|
86
|
+
| 'Found multiple elements with the text: X' | `test-setup.ts` must run `cleanup` after each test |
|
|
87
|
+
| `setData` is never called | `userEvent.type` on a controlled input; use `fireEvent.change` |
|
|
88
|
+
| A hook throws 'must be used inside' | The component was rendered without a helper. Render through `renderUserInterface` or its siblings |
|
|
89
|
+
| A write the game accepts fails the test | The mock does not merge as the host does; use `mergingSetData` (see [Design state for concurrent writes](design-state-for-concurrent-writes.md)) |
|
|
90
|
+
|
|
91
|
+
## Related
|
|
92
|
+
|
|
93
|
+
- [Testing guide](../guides/testing.md): every render helper and option
|
|
94
|
+
- [Test a whole game in-process](test-with-engine.md)
|
|
95
|
+
- [Test UIs end to end with Playwright](e2e-testing.md)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Test a whole game in-process
|
|
2
|
+
|
|
3
|
+
Test your whole game (engine, hooks, validators, endpoints, and completion) in one Node.js process, with no dev server, browser, or subprocess.
|
|
4
|
+
|
|
5
|
+
**Keywords:** engine test, createTestSession, test session, in-process test, vitest engine, test game logic, programmatic client, setData, tick
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- A game package with its dependencies installed. One made with `sdk new package` already has the engine test setup: skip to step 4
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
1. Add the test session's package, if it is not already a dependency:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add -D @seriousgaming/sdk-tools
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
2. Create `vitest.engine.config.ts` in the package root:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { defineConfig } from 'vitest/config';
|
|
23
|
+
|
|
24
|
+
export default defineConfig({
|
|
25
|
+
test: {
|
|
26
|
+
environment: 'node',
|
|
27
|
+
testTimeout: 60_000,
|
|
28
|
+
include: ['testing/engine/**/*.engine.test.ts'],
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
In `vitest.config.ts`, add `exclude: ['testing/**', '**/node_modules/**']` to `test`, so the jsdom and engine configs do not pick up each other's files.
|
|
34
|
+
|
|
35
|
+
3. Add the script to `package.json`:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{ "scripts": { "test:engine": "vitest run --config vitest.engine.config.ts" } }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
4. Write `testing/engine/game.engine.test.ts`. Type the session with the engine's generated `Context` and `SharedContext`, so its helpers need no casts:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { fileURLToPath } from 'url';
|
|
45
|
+
|
|
46
|
+
import { createTestSession, type TestSession } from '@seriousgaming/sdk-tools/client';
|
|
47
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
48
|
+
|
|
49
|
+
import type { Context, SharedContext } from '../../engine/types';
|
|
50
|
+
|
|
51
|
+
const PACKAGE_PATH = fileURLToPath(new URL('../..', import.meta.url));
|
|
52
|
+
|
|
53
|
+
describe('mygame', () => {
|
|
54
|
+
let session: TestSession<Context, SharedContext>;
|
|
55
|
+
|
|
56
|
+
beforeEach(async () => {
|
|
57
|
+
session = await createTestSession<Context, SharedContext>({
|
|
58
|
+
packagePath: PACKAGE_PATH,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
afterEach(async () => {
|
|
63
|
+
await session.close();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('calculates revenue from workers and price', async () => {
|
|
67
|
+
await session.setData(0, { workers: { WorkerCount: 20 }, pricing: { Price: 10 } });
|
|
68
|
+
|
|
69
|
+
expect((await session.tick()).ok).toBe(true);
|
|
70
|
+
|
|
71
|
+
const { context } = session.team(0);
|
|
72
|
+
expect(context.production.Production).toBe(1000);
|
|
73
|
+
expect(context.revenue.Revenue).toBe(10_000);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`setData(0, ...)` writes as team 0's first participant through the game UI, so the write is held to the UI's `writes` and your validators judge it. Assert on a computed value, not only on the value you wrote.
|
|
79
|
+
|
|
80
|
+
5. Optional: choose what the model answers. A test session answers `generateText`, `generateSpeech`, and `extractText` from offline stubs; override one with `callbacks`:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
session = await createTestSession<Context, SharedContext>({
|
|
84
|
+
packagePath: PACKAGE_PATH,
|
|
85
|
+
callbacks: {
|
|
86
|
+
generateText: async () => ({
|
|
87
|
+
content: [{ type: 'text' as const, text: 'mock response' }],
|
|
88
|
+
stopReason: 'endTurn' as const,
|
|
89
|
+
}),
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Verify
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pnpm test:engine
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Every test passes, each in well under a second.
|
|
101
|
+
|
|
102
|
+
## Troubleshooting
|
|
103
|
+
|
|
104
|
+
| Problem | Solution |
|
|
105
|
+
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
106
|
+
| `TextEncoder` invariant in the default test run | The jsdom config picked up the engine test. Add `testing/**` to `exclude` in `vitest.config.ts` (step 2) |
|
|
107
|
+
| A context field is a type error | The capability is not in the `Context` you passed; intersect the hook's `Context` if a hook keeps it |
|
|
108
|
+
| `setData` answers `validation-rejected` | Your validator refused it. Pass `{ as: 'system' }` to arrange state the test then plays on |
|
|
109
|
+
| Tests are slow or time out | A test passes `services: 'platform'`, or a callback override calls the network |
|
|
110
|
+
|
|
111
|
+
## Related
|
|
112
|
+
|
|
113
|
+
- [Engine testing guide](../guides/engine-testing.md): every `createTestSession` option, completion, endpoints, timers, and scenarios
|
|
114
|
+
- [Testing guide](../guides/testing.md): unit tests with `logicArgs`, and component tests
|
|
115
|
+
- [Test UIs end to end with Playwright](e2e-testing.md)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Upload and display files
|
|
2
|
+
|
|
3
|
+
Let a participant attach a file (a PDF, CSV, or image) in the game UI, show it back, and optionally summarise it in the engine.
|
|
4
|
+
|
|
5
|
+
**Keywords:** upload file, file upload, attach document, blob, asset reference, uploadBlob, getAssetUrl, useAssetUrl, extractText, embed PDF
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- A game with an engine and a game UI
|
|
10
|
+
- For step 5, platform access for the LLM (see [LLM integration](../guides/llm-integration.md#configuration))
|
|
11
|
+
|
|
12
|
+
## Steps
|
|
13
|
+
|
|
14
|
+
1. Create a capability to hold the file's reference:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm run sdk new capability '{
|
|
18
|
+
"definition": {
|
|
19
|
+
"identifier": "upload",
|
|
20
|
+
"variablesSchema": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"DocumentRef": { "type": "string", "default": "" }
|
|
24
|
+
},
|
|
25
|
+
"required": ["DocumentRef"],
|
|
26
|
+
"additionalProperties": false
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
2. Let the game UI write it (`writes` makes it readable too):
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pnpm run sdk update ui --patch '[
|
|
36
|
+
{ "op": "add", "path": "/definition/writes/-", "value": "upload" }
|
|
37
|
+
]'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
3. Upload from the UI and store the reference. Read the empty string as 'nothing uploaded':
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import {
|
|
44
|
+
useAssetUrl,
|
|
45
|
+
useUserInterfaceCallbacks,
|
|
46
|
+
useUserInterfaceData,
|
|
47
|
+
} from '@seriousgaming/sdk/ui';
|
|
48
|
+
|
|
49
|
+
import type { Context, Settings } from '../types';
|
|
50
|
+
|
|
51
|
+
export function ResearchPanel() {
|
|
52
|
+
const { uploadBlob } = useUserInterfaceCallbacks();
|
|
53
|
+
const { data, setData } = useUserInterfaceData<Context, Settings>();
|
|
54
|
+
const reference = data.upload.DocumentRef;
|
|
55
|
+
const url = useAssetUrl(reference); // undefined while the reference is ''
|
|
56
|
+
|
|
57
|
+
const onFile = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
58
|
+
const file = event.target.files?.[0];
|
|
59
|
+
if (!file) return;
|
|
60
|
+
await setData({ upload: { DocumentRef: await uploadBlob(file) } });
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return url ? (
|
|
64
|
+
<embed src={url} type="application/pdf" style={{ width: '100%', height: 500 }} />
|
|
65
|
+
) : (
|
|
66
|
+
<input type="file" accept=".pdf" onChange={onFile} />
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
4. Optional: create the capability the engine writes its summary to, and let the engine read the upload, write the summary, and call the two services. Calls to a service the engine does not list in `uses` throw:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pnpm run sdk new capability '{
|
|
75
|
+
"definition": {
|
|
76
|
+
"identifier": "summary",
|
|
77
|
+
"variablesSchema": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"properties": { "Summary": { "type": "string", "default": "" } },
|
|
80
|
+
"required": ["Summary"],
|
|
81
|
+
"additionalProperties": false
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}'
|
|
85
|
+
pnpm run sdk update engine --patch '[
|
|
86
|
+
{ "op": "add", "path": "/definition/reads/-", "value": "upload" },
|
|
87
|
+
{ "op": "add", "path": "/definition/writes/-", "value": "summary" },
|
|
88
|
+
{ "op": "add", "path": "/definition/uses", "value": ["extractText", "generateText"] }
|
|
89
|
+
]'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
If the engine already declares `uses`, append to it instead (`"path": "/definition/uses/-"`).
|
|
93
|
+
|
|
94
|
+
5. Optional: summarise the file in the engine's `Run`:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { getText, type RunFunction } from '@seriousgaming/sdk';
|
|
98
|
+
|
|
99
|
+
import type { Callbacks, Context, Settings } from './types';
|
|
100
|
+
|
|
101
|
+
export const Run: RunFunction<Context, Settings, Callbacks> = async ({
|
|
102
|
+
context,
|
|
103
|
+
callbacks,
|
|
104
|
+
}) => {
|
|
105
|
+
const reference = context.upload.DocumentRef;
|
|
106
|
+
if (!reference) return { capabilities: context };
|
|
107
|
+
|
|
108
|
+
const { content } = await callbacks.extractText(reference);
|
|
109
|
+
const result = await callbacks.generateText({
|
|
110
|
+
prompt: `Summarise this market research:\n\n${content}`,
|
|
111
|
+
tier: 'standard',
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
capabilities: { ...context, summary: { ...context.summary, Summary: getText(result) } },
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Typed with the generated `Callbacks`, both services are present rather than optional. For raw bytes rather than text, use `readAsset` (see [Reading an asset in an engine](../guides/file-handling.md#reading-an-asset-in-an-engine)).
|
|
120
|
+
|
|
121
|
+
## Verify
|
|
122
|
+
|
|
123
|
+
From the CLI:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
REFERENCE=$(pnpm run --silent sdk blob upload ./test.pdf --json | jq -r '.data.reference')
|
|
127
|
+
pnpm run sdk patch "[{\"op\":\"replace\",\"path\":\"/upload/DocumentRef\",\"value\":\"$REFERENCE\"}]"
|
|
128
|
+
pnpm run sdk tick
|
|
129
|
+
pnpm run sdk view
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`summary.Summary` holds the model's summary. In the dev server, uploading a PDF from a team's screen replaces the input with the embedded document.
|
|
133
|
+
|
|
134
|
+
## Troubleshooting
|
|
135
|
+
|
|
136
|
+
| Problem | Solution |
|
|
137
|
+
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
|
|
138
|
+
| `data.upload` is a type error | `upload` is not in the UI's `reads` or `writes`; step 2 regenerates `types.ts` |
|
|
139
|
+
| `UndeclaredApiUseError` at tick time | The engine calls a service missing from its `uses` (step 4) |
|
|
140
|
+
| The engine never sees the reference | Add `upload` to the engine's `reads` |
|
|
141
|
+
| The PDF does not render | Match the `<embed>` type to the file; see [Supported file types](../guides/file-handling.md#supported-file-types) |
|
|
142
|
+
| `LLMClientError` from `generateText` | Platform access is not configured; see [Environment variables](../environment-variables.md) |
|
|
143
|
+
|
|
144
|
+
## Related
|
|
145
|
+
|
|
146
|
+
- [File handling guide](../guides/file-handling.md): references, uploading, displaying, and reading files
|
|
147
|
+
- [Declare the runtime callbacks a resource uses](declare-api-uses.md)
|
|
148
|
+
- [Take a file in an endpoint](take-a-file-in-an-endpoint.md)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Use requestPlay in a setup screen
|
|
2
|
+
|
|
3
|
+
Leave the configuration step and start the game from within your setup screen, so operators can move to play from your own control as well as from the host's Play button.
|
|
4
|
+
|
|
5
|
+
**Keywords:** requestPlay, request play, start game, setup play, custom play button, setup submit, leave configuration step
|
|
6
|
+
|
|
7
|
+
## What you'll build
|
|
8
|
+
|
|
9
|
+
A setup screen with a custom 'Start' button that moves the session from its configuration step to play when clicked. The host's own Play button stays: a runtime always renders it, and no game can hide it.
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
- An existing setup screen (see [Create a setup screen](create-setup.md))
|
|
14
|
+
|
|
15
|
+
## Steps
|
|
16
|
+
|
|
17
|
+
### 1. Call `requestPlay` from your setup screen
|
|
18
|
+
|
|
19
|
+
Use the `useSettingsEditorCallbacks` hook and call `requestPlay` when the operator is ready to move on:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { useSettingsEditorCallbacks } from '@seriousgaming/sdk/ui';
|
|
23
|
+
|
|
24
|
+
function SetupPanel() {
|
|
25
|
+
const { requestPlay } = useSettingsEditorCallbacks();
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<button onClick={() => requestPlay()}>
|
|
29
|
+
Start game
|
|
30
|
+
</button>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Start the dev server and verify
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pnpm run sdk dev
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Click your custom button. The session should leave the configuration step and start play.
|
|
42
|
+
|
|
43
|
+
## Verify
|
|
44
|
+
|
|
45
|
+
- [ ] Clicking the custom button moves the session from the configuration step to play
|
|
46
|
+
- [ ] Any setting changes made before clicking are preserved
|
|
47
|
+
|
|
48
|
+
## Troubleshooting
|
|
49
|
+
|
|
50
|
+
**Nothing happens when clicking the button in the scenario editor**
|
|
51
|
+
|
|
52
|
+
That is expected. `requestPlay` means something only in `setup`; the host ignores it from `scenario_editor`, which may have no session to start. The callback is present in both because one wrapper, `SettingsEditorWrapper`, serves both slots.
|
|
53
|
+
|
|
54
|
+
**Two Play controls are visible**
|
|
55
|
+
|
|
56
|
+
That is expected. The host always renders its own, so your control is an addition rather than a replacement: a form with its own finish button at the bottom, say. A game cannot hide the host's, because a bug in your own control would otherwise strand a facilitator on the configuration step with no way forward.
|
|
57
|
+
|
|
58
|
+
**`useSettingsEditorCallbacks` throws an error**
|
|
59
|
+
|
|
60
|
+
The hook must be called inside a component rendered within `SettingsEditorWrapper`. The scaffolded `src/main.tsx` provides it, so check your component is a descendant of it.
|
|
61
|
+
|
|
62
|
+
## Related
|
|
63
|
+
|
|
64
|
+
- [Use requestTick in a UI](./use-request-tick.md)
|
|
65
|
+
- [Building a setup screen](../guides/setup.md)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Use requestTick in a UI
|
|
2
|
+
|
|
3
|
+
Trigger the next game tick from within your user interface, so participants submit their decisions via your own button. This is how games advance: there is no built-in next tick button, so every game provides its own tick control.
|
|
4
|
+
|
|
5
|
+
**Keywords:** requestTick, request tick, submit, next tick, end turn, custom submit button, tick from UI
|
|
6
|
+
|
|
7
|
+
## What you'll build
|
|
8
|
+
|
|
9
|
+
A user interface with a submit button that advances the game to the next tick when clicked.
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
- An existing user interface (see [Create a user interface](create-user-interface.md))
|
|
14
|
+
- An existing game that includes the UI (see [Shape your game](create-game.md))
|
|
15
|
+
|
|
16
|
+
## Steps
|
|
17
|
+
|
|
18
|
+
### 1. Call `requestTick` from your UI component
|
|
19
|
+
|
|
20
|
+
Use the `useUserInterfaceCallbacks` hook and call `requestTick` when the participant submits:
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { useUserInterfaceCallbacks } from '@seriousgaming/sdk/ui';
|
|
24
|
+
|
|
25
|
+
function DecisionPanel() {
|
|
26
|
+
const callbacks = useUserInterfaceCallbacks();
|
|
27
|
+
|
|
28
|
+
const handleSubmit = async () => {
|
|
29
|
+
await callbacks.requestTick();
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<button onClick={handleSubmit}>
|
|
34
|
+
Submit Decision
|
|
35
|
+
</button>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`requestTick` is a required member of `UserInterfaceCallbacks` and returns a `Promise<void>`. It resolves once the tick has finished and context is updated, and rejects with a `MutationError` when the tick cannot run (for example, the tick limit has been reached).
|
|
41
|
+
|
|
42
|
+
The same callback is available to session UIs, so a session UI provides its own tick controls the same way (the scaffolded starter package wires all of them).
|
|
43
|
+
|
|
44
|
+
### 2. Start the dev server and verify
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pnpm run sdk dev
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Click your custom button. The game should advance to the next tick and your UI should receive the updated context.
|
|
51
|
+
|
|
52
|
+
During development you can also advance ticks without a game control: the dev drawer (press the backtick key) has a developer-only **Tick** button.
|
|
53
|
+
|
|
54
|
+
## Verify
|
|
55
|
+
|
|
56
|
+
- [ ] Clicking the custom button advances the tick counter
|
|
57
|
+
- [ ] Context updates are reflected in your UI after each tick
|
|
58
|
+
|
|
59
|
+
## Troubleshooting
|
|
60
|
+
|
|
61
|
+
**Nothing happens when clicking the button**
|
|
62
|
+
|
|
63
|
+
Await the returned promise and catch the rejection: a tick the host refuses rejects with a `MutationError` rather than failing silently.
|
|
64
|
+
|
|
65
|
+
**`useUserInterfaceCallbacks` throws an error**
|
|
66
|
+
|
|
67
|
+
The hook must be called inside a component rendered within `UserInterfaceWrapper`. This is provided automatically by the SDK template, so check your component is a descendant of `UserInterfaceWrapper`.
|
|
68
|
+
|
|
69
|
+
**UI shows a brief flicker after calling `setData` then `requestTick`**
|
|
70
|
+
|
|
71
|
+
When you call `setData({ ... }); callbacks.requestTick();`, the `setData` change is visible immediately but the tick is asynchronous. Between the two, UIs show the raw `setData` values without any engine-computed results. This resolves once the tick completes (typically a few milliseconds). See [Combining setData with requestTick](../guides/ui.md#combining-setdata-with-requesttick) for strategies to handle the intermediate state.
|
|
72
|
+
|
|
73
|
+
## Related
|
|
74
|
+
|
|
75
|
+
- [User interfaces guide: requesting a tick](../guides/ui.md#requesting-a-tick)
|