@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,102 @@
|
|
|
1
|
+
import { Box, Button, CssBaseline, GlobalStyles, ThemeProvider, Typography } from '@mui/material';
|
|
2
|
+
import { useSettingsEditor, useSettingsEditorCallbacks } from '@seriousgaming/sdk/ui';
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
6
|
+
import { Settings } from '../types';
|
|
7
|
+
import { useAppTheme } from './theme';
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
This UI requires the following dependencies:
|
|
11
|
+
- @mui/material
|
|
12
|
+
- @emotion/react
|
|
13
|
+
- @emotion/styled
|
|
14
|
+
- stylis
|
|
15
|
+
- @mui/stylis-plugin-rtl
|
|
16
|
+
- @fontsource-variable/roboto-mono
|
|
17
|
+
- @fontsource-variable/work-sans
|
|
18
|
+
- i18next
|
|
19
|
+
- react-i18next
|
|
20
|
+
|
|
21
|
+
Make sure to install them in your project, e.g.:
|
|
22
|
+
pnpm add @mui/material @emotion/react @emotion/styled stylis @mui/stylis-plugin-rtl @fontsource-variable/roboto-mono @fontsource-variable/work-sans i18next react-i18next
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
This is the game's setup screen: a small form the facilitator fills in when
|
|
27
|
+
they create a session. What it changes applies to that session only. It
|
|
28
|
+
includes an example counter that uses local component state. To connect it to
|
|
29
|
+
the game's settings:
|
|
30
|
+
|
|
31
|
+
1. In setup/setup.sdk.json, list the settings resources this screen edits in
|
|
32
|
+
`configures`, e.g. "configures": ["demand"]. The settings it reads and
|
|
33
|
+
writes are keyed by those identifiers.
|
|
34
|
+
|
|
35
|
+
2. In this file (App.tsx), uncomment the lines related to `useSettingsEditor`.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* This surface is embedded in the host's page, so it leaves the ground to the
|
|
40
|
+
* host: a surface painting its own background reads as a panel bolted on rather
|
|
41
|
+
* than part of the page, and never quite matches. Paint cards and panels, which
|
|
42
|
+
* are meant to look raised, not the page.
|
|
43
|
+
*/
|
|
44
|
+
const TRANSPARENT_BODY = { body: { backgroundColor: 'transparent' } };
|
|
45
|
+
|
|
46
|
+
const BUTTON_ROW = { display: 'flex', gap: 1 };
|
|
47
|
+
|
|
48
|
+
function App() {
|
|
49
|
+
// const { settings, updateSettings } = useSettingsEditor<Settings>();
|
|
50
|
+
const { requestPlay } = useSettingsEditorCallbacks();
|
|
51
|
+
const { t } = useTranslation();
|
|
52
|
+
const theme = useAppTheme();
|
|
53
|
+
const [localCounter, setLocalCounter] = useState(0);
|
|
54
|
+
|
|
55
|
+
// This is a local state example.
|
|
56
|
+
// To connect to the settings, comment out the local state above and
|
|
57
|
+
// uncomment the `useSettingsEditor` hook and the `counterValue` line below.
|
|
58
|
+
const counterValue = localCounter;
|
|
59
|
+
// const counterValue = settings.demand?.someField;
|
|
60
|
+
|
|
61
|
+
const handleIncrement = () => {
|
|
62
|
+
// This is a local state update example.
|
|
63
|
+
setLocalCounter((c) => c + 1);
|
|
64
|
+
|
|
65
|
+
// To change a setting, uncomment the `updateSettings` call below. Only
|
|
66
|
+
// what changed reaches the host.
|
|
67
|
+
// void updateSettings({
|
|
68
|
+
// demand: {
|
|
69
|
+
// ...settings.demand,
|
|
70
|
+
// someField: (settings.demand?.someField ?? 0) + 1,
|
|
71
|
+
// },
|
|
72
|
+
// });
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<ThemeProvider theme={theme}>
|
|
77
|
+
<CssBaseline enableColorScheme />
|
|
78
|
+
<GlobalStyles styles={TRANSPARENT_BODY} />
|
|
79
|
+
<Box>
|
|
80
|
+
<Typography variant="body1">{t('welcomeMessage')}</Typography>
|
|
81
|
+
<Typography variant="body2" color="text.secondary">
|
|
82
|
+
{t('dataExample')}
|
|
83
|
+
</Typography>
|
|
84
|
+
<Typography variant="h6" component="p">
|
|
85
|
+
{t('currentValue', { value: counterValue })}
|
|
86
|
+
</Typography>
|
|
87
|
+
<Box sx={BUTTON_ROW}>
|
|
88
|
+
<Button variant="outlined" onClick={handleIncrement}>
|
|
89
|
+
{t('updateButton')}
|
|
90
|
+
</Button>
|
|
91
|
+
{/* The host always shows its own Play control as well, so
|
|
92
|
+
this is a convenience, never the only way out. */}
|
|
93
|
+
<Button variant="contained" onClick={() => requestPlay()}>
|
|
94
|
+
{t('startButton')}
|
|
95
|
+
</Button>
|
|
96
|
+
</Box>
|
|
97
|
+
</Box>
|
|
98
|
+
</ThemeProvider>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export default App;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import i18n from 'i18next';
|
|
2
|
+
import { initReactI18next } from 'react-i18next';
|
|
3
|
+
|
|
4
|
+
// Translation resources
|
|
5
|
+
const resources = {
|
|
6
|
+
en: {
|
|
7
|
+
translation: {
|
|
8
|
+
greeting: 'Hello, World!',
|
|
9
|
+
welcomeMessage: 'Welcome to the setup for this game.',
|
|
10
|
+
dataExample: 'Choose the settings this session plays with.',
|
|
11
|
+
currentValue: 'Current value: {% raw %}{{value}}{% endraw %}',
|
|
12
|
+
updateButton: 'Increment Value',
|
|
13
|
+
startButton: 'Start',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
it: {
|
|
17
|
+
translation: {
|
|
18
|
+
greeting: 'Ciao, mondo!',
|
|
19
|
+
welcomeMessage: 'Benvenuto nella configurazione di questo gioco.',
|
|
20
|
+
dataExample: 'Scegli le impostazioni di questa sessione.',
|
|
21
|
+
currentValue: 'Valore corrente: {% raw %}{{value}}{% endraw %}',
|
|
22
|
+
updateButton: 'Incrementa valore',
|
|
23
|
+
startButton: 'Inizia',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
ar: {
|
|
27
|
+
translation: {
|
|
28
|
+
greeting: 'أهلاً بالعالم!',
|
|
29
|
+
welcomeMessage: 'مرحبًا بك في إعداد هذه اللعبة.',
|
|
30
|
+
dataExample: 'اختر إعدادات هذه الجلسة.',
|
|
31
|
+
currentValue: 'القيمة الحالية: {% raw %}{{value}}{% endraw %}',
|
|
32
|
+
updateButton: 'زيادة القيمة',
|
|
33
|
+
startButton: 'ابدأ',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
i18n.use(initReactI18next).init({
|
|
39
|
+
resources,
|
|
40
|
+
lng: 'en', // default language
|
|
41
|
+
fallbackLng: 'en',
|
|
42
|
+
interpolation: {
|
|
43
|
+
escapeValue: false, // React already escapes
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export default i18n;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"definition": {
|
|
3
|
+
"scope": "shared",
|
|
4
|
+
"lifetime": "session",
|
|
5
|
+
"variablesSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"Message": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Facilitator note shown on every team's screen. Shared scope: one value for the whole session, written only from the session surface. Session lifetime: an undo or a reset leaves it as the facilitator last wrote it.",
|
|
11
|
+
"default": ""
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"required": ["Message"],
|
|
15
|
+
"additionalProperties": false
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"definition": {
|
|
3
|
+
"variablesSchema": {
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"Make": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"description": "How many of each menu item to make today, keyed by item id. Keyed rather than a flat set of fields so that two people changing two different items write different keys, and the host's merge keeps both.",
|
|
9
|
+
"default": {},
|
|
10
|
+
"additionalProperties": { "type": "number" }
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"required": ["Make"],
|
|
14
|
+
"additionalProperties": false
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"definition": {
|
|
3
|
+
"variablesSchema": {
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"Day": {
|
|
7
|
+
"type": "number",
|
|
8
|
+
"description": "The day these results are for",
|
|
9
|
+
"default": 0
|
|
10
|
+
},
|
|
11
|
+
"Weather": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"description": "What the weather actually did",
|
|
14
|
+
"default": "sunny"
|
|
15
|
+
},
|
|
16
|
+
"Lines": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"description": "What happened to each menu item, keyed by item id",
|
|
19
|
+
"default": {},
|
|
20
|
+
"additionalProperties": {
|
|
21
|
+
"title": "DayLine",
|
|
22
|
+
"type": "object",
|
|
23
|
+
"properties": {
|
|
24
|
+
"Made": { "type": "number" },
|
|
25
|
+
"Wanted": {
|
|
26
|
+
"type": "number",
|
|
27
|
+
"description": "How many people asked for one"
|
|
28
|
+
},
|
|
29
|
+
"Sold": { "type": "number" },
|
|
30
|
+
"PouredAway": { "type": "number", "description": "Made and not sold" },
|
|
31
|
+
"TurnedAway": {
|
|
32
|
+
"type": "number",
|
|
33
|
+
"description": "Asked for and not available"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"required": ["Made", "Wanted", "Sold", "PouredAway", "TurnedAway"],
|
|
37
|
+
"additionalProperties": false
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"Takings": { "type": "number", "description": "Money in, in pence", "default": 0 },
|
|
41
|
+
"Spent": {
|
|
42
|
+
"type": "number",
|
|
43
|
+
"description": "Cost of everything made, in pence",
|
|
44
|
+
"default": 0
|
|
45
|
+
},
|
|
46
|
+
"Profit": {
|
|
47
|
+
"type": "number",
|
|
48
|
+
"description": "Takings less spend, in pence",
|
|
49
|
+
"default": 0
|
|
50
|
+
},
|
|
51
|
+
"Purse": {
|
|
52
|
+
"type": "number",
|
|
53
|
+
"description": "Everything made so far, in pence",
|
|
54
|
+
"default": 0
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"required": ["Day", "Weather", "Lines", "Takings", "Spent", "Profit", "Purse"],
|
|
58
|
+
"additionalProperties": false
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"definition": {
|
|
3
|
+
"variablesSchema": {
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"LooksAfter": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"description": "Which menu item each participant is in charge of, keyed by participant id. Written by the participantJoin hook, and the reason two people normally edit two different keys.",
|
|
9
|
+
"default": {},
|
|
10
|
+
"additionalProperties": { "type": "string" }
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"required": ["LooksAfter"],
|
|
14
|
+
"additionalProperties": false
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"definition": {
|
|
3
|
+
"variablesSchema": {
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"Today": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"description": "The forecast for the day about to be played",
|
|
9
|
+
"default": "sunny",
|
|
10
|
+
"enum": ["sunny", "cloudy", "rainy"]
|
|
11
|
+
},
|
|
12
|
+
"Expected": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"description": "Roughly how many of each item people will want today, keyed by item id. A forecast, not a promise: the day lands somewhere near it.",
|
|
15
|
+
"default": {},
|
|
16
|
+
"additionalProperties": { "type": "number" }
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"required": ["Today", "Expected"],
|
|
20
|
+
"additionalProperties": false
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// This file is auto-generated from the schemas the resource declares: the values
|
|
2
|
+
// a session starts with, for use as test fixtures.
|
|
3
|
+
// Do not edit this file directly. It will be regenerated on build.
|
|
4
|
+
|
|
5
|
+
import type { Context, TeamContext, SharedContext, Settings } from './types';
|
|
6
|
+
|
|
7
|
+
export const defaultContext: Context = {
|
|
8
|
+
plan: {
|
|
9
|
+
Make: {},
|
|
10
|
+
},
|
|
11
|
+
weather: {
|
|
12
|
+
Today: 'sunny',
|
|
13
|
+
Expected: {},
|
|
14
|
+
},
|
|
15
|
+
results: {
|
|
16
|
+
Day: 0,
|
|
17
|
+
Weather: 'sunny',
|
|
18
|
+
Lines: {},
|
|
19
|
+
Takings: 0,
|
|
20
|
+
Spent: 0,
|
|
21
|
+
Profit: 0,
|
|
22
|
+
Purse: 0,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const defaultTeamContext: TeamContext = {
|
|
27
|
+
plan: {
|
|
28
|
+
Make: {},
|
|
29
|
+
},
|
|
30
|
+
weather: {
|
|
31
|
+
Today: 'sunny',
|
|
32
|
+
Expected: {},
|
|
33
|
+
},
|
|
34
|
+
results: {
|
|
35
|
+
Day: 0,
|
|
36
|
+
Weather: 'sunny',
|
|
37
|
+
Lines: {},
|
|
38
|
+
Takings: 0,
|
|
39
|
+
Spent: 0,
|
|
40
|
+
Profit: 0,
|
|
41
|
+
Purse: 0,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const defaultSharedContext: SharedContext = {};
|
|
46
|
+
|
|
47
|
+
export const defaultSettings: Settings = {
|
|
48
|
+
menu: {
|
|
49
|
+
Capacity: 120,
|
|
50
|
+
Menu: {
|
|
51
|
+
lemonade: {
|
|
52
|
+
Name: 'Lemonade',
|
|
53
|
+
Order: 1,
|
|
54
|
+
Cost: 15,
|
|
55
|
+
Price: 50,
|
|
56
|
+
},
|
|
57
|
+
icedtea: {
|
|
58
|
+
Name: 'Iced tea',
|
|
59
|
+
Order: 2,
|
|
60
|
+
Cost: 20,
|
|
61
|
+
Price: 60,
|
|
62
|
+
},
|
|
63
|
+
cookies: {
|
|
64
|
+
Name: 'Cookies',
|
|
65
|
+
Order: 3,
|
|
66
|
+
Cost: 25,
|
|
67
|
+
Price: 70,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
engine: {
|
|
72
|
+
UsualDemand: {
|
|
73
|
+
lemonade: 55,
|
|
74
|
+
icedtea: 30,
|
|
75
|
+
cookies: 20,
|
|
76
|
+
},
|
|
77
|
+
SunnyPull: 1.3,
|
|
78
|
+
RainyPull: 0.65,
|
|
79
|
+
Variation: 0.2,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { Init, Run } from './logic';
|
|
4
|
+
import { baseSettings, baseContext, baseMeta, callbacks } from './test-helpers';
|
|
5
|
+
import type { Context } from './types';
|
|
6
|
+
|
|
7
|
+
const init = async (over: Partial<typeof baseMeta> = {}) =>
|
|
8
|
+
(
|
|
9
|
+
await Init({
|
|
10
|
+
participants: [],
|
|
11
|
+
context: baseContext,
|
|
12
|
+
settings: baseSettings,
|
|
13
|
+
meta: { ...baseMeta, ...over },
|
|
14
|
+
callbacks,
|
|
15
|
+
})
|
|
16
|
+
).capabilities;
|
|
17
|
+
|
|
18
|
+
const run = async (context: Context, day = 1, over: Partial<typeof baseMeta> = {}) =>
|
|
19
|
+
(
|
|
20
|
+
await Run({
|
|
21
|
+
participants: [],
|
|
22
|
+
context,
|
|
23
|
+
settings: baseSettings,
|
|
24
|
+
meta: { ...baseMeta, tick: day, ...over },
|
|
25
|
+
callbacks,
|
|
26
|
+
})
|
|
27
|
+
).capabilities;
|
|
28
|
+
|
|
29
|
+
const makeAll = (context: Context, counts: Record<string, number>): Context => ({
|
|
30
|
+
...context,
|
|
31
|
+
plan: { Make: counts },
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('Init', () => {
|
|
35
|
+
it('starts the team somewhere obviously provisional', async () => {
|
|
36
|
+
const result = await init();
|
|
37
|
+
|
|
38
|
+
// Half of an ordinary day's custom: not nothing, which reads as a
|
|
39
|
+
// broken screen, and not the forecast, which would decide day one.
|
|
40
|
+
expect(result.plan.Make).toEqual({ lemonade: 28, icedtea: 15, cookies: 10 });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('expects nobody for an item the demand settings say nothing about', async () => {
|
|
44
|
+
// A scenario can add to the menu without touching the engine's
|
|
45
|
+
// settings. The new item still gets a line, just an empty one.
|
|
46
|
+
const { capabilities: result } = await Init({
|
|
47
|
+
participants: [],
|
|
48
|
+
context: baseContext,
|
|
49
|
+
settings: {
|
|
50
|
+
...baseSettings,
|
|
51
|
+
menu: {
|
|
52
|
+
...baseSettings.menu,
|
|
53
|
+
Menu: {
|
|
54
|
+
...baseSettings.menu.Menu,
|
|
55
|
+
flapjack: { Name: 'Flapjack', Order: 4, Cost: 20, Price: 80 },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
meta: baseMeta,
|
|
60
|
+
callbacks,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
expect(result.plan.Make.flapjack).toBe(0);
|
|
64
|
+
expect(result.weather.Expected.flapjack).toBe(0);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('forecasts the first day', async () => {
|
|
68
|
+
const result = await init();
|
|
69
|
+
|
|
70
|
+
expect(['sunny', 'cloudy', 'rainy']).toContain(result.weather.Today);
|
|
71
|
+
expect(Object.keys(result.weather.Expected).sort()).toEqual([
|
|
72
|
+
'cookies',
|
|
73
|
+
'icedtea',
|
|
74
|
+
'lemonade',
|
|
75
|
+
]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('gives two teams different weather from the one session seed', async () => {
|
|
79
|
+
const first = await init({ index: 0 });
|
|
80
|
+
const second = await init({ index: 3 });
|
|
81
|
+
|
|
82
|
+
// Not a guarantee for any one day, so this asserts the mechanism: the
|
|
83
|
+
// team index reaches the derivation.
|
|
84
|
+
const days = [1, 2, 3, 4, 5, 6];
|
|
85
|
+
const forTeam = async (index: number) => {
|
|
86
|
+
let context = await init({ index });
|
|
87
|
+
const seen: string[] = [];
|
|
88
|
+
for (const day of days) {
|
|
89
|
+
seen.push(context.weather.Today);
|
|
90
|
+
context = await run(context, day, { index });
|
|
91
|
+
}
|
|
92
|
+
return seen.join(',');
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
expect(await forTeam(0)).not.toBe(await forTeam(3));
|
|
96
|
+
expect(first.weather.Today).toBeDefined();
|
|
97
|
+
expect(second.weather.Today).toBeDefined();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('clears everything a previous session left behind', async () => {
|
|
101
|
+
const { capabilities: result } = await Init({
|
|
102
|
+
participants: [],
|
|
103
|
+
context: {
|
|
104
|
+
...baseContext,
|
|
105
|
+
results: { ...baseContext.results, Purse: 9999, Day: 7 },
|
|
106
|
+
},
|
|
107
|
+
settings: baseSettings,
|
|
108
|
+
meta: baseMeta,
|
|
109
|
+
callbacks,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(result.results.Purse).toBe(0);
|
|
113
|
+
expect(result.results.Day).toBe(0);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('Run', () => {
|
|
118
|
+
it('sells what it made when people want more than that', async () => {
|
|
119
|
+
// Nothing made is nothing sold, whatever the weather does.
|
|
120
|
+
const result = await run(makeAll(baseContext, { lemonade: 0, icedtea: 0, cookies: 0 }));
|
|
121
|
+
|
|
122
|
+
expect(result.results.Lines.lemonade?.Sold).toBe(0);
|
|
123
|
+
expect(result.results.Lines.lemonade?.TurnedAway).toBe(
|
|
124
|
+
result.results.Lines.lemonade?.Wanted
|
|
125
|
+
);
|
|
126
|
+
expect(result.results.Takings).toBe(0);
|
|
127
|
+
expect(result.results.Spent).toBe(0);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('pours away what it made and did not sell, having paid for it', async () => {
|
|
131
|
+
const result = await run(
|
|
132
|
+
makeAll(baseContext, { lemonade: 200, icedtea: 200, cookies: 200 })
|
|
133
|
+
);
|
|
134
|
+
const line = result.results.Lines.lemonade;
|
|
135
|
+
|
|
136
|
+
expect(line?.Made).toBe(200);
|
|
137
|
+
expect(line?.Sold).toBe(line?.Wanted);
|
|
138
|
+
expect(line?.PouredAway).toBe(200 - (line?.Wanted ?? 0));
|
|
139
|
+
expect(line?.TurnedAway).toBe(0);
|
|
140
|
+
// 600 items at 15p, 20p and 25p.
|
|
141
|
+
expect(result.results.Spent).toBe(200 * 15 + 200 * 20 + 200 * 25);
|
|
142
|
+
expect(result.results.Profit).toBeLessThan(0);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('never both pours away and turns away the same item', async () => {
|
|
146
|
+
const result = await run(baseContext);
|
|
147
|
+
|
|
148
|
+
for (const line of Object.values(result.results.Lines)) {
|
|
149
|
+
expect(Math.min(line.PouredAway, line.TurnedAway)).toBe(0);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('takes the sale price for every cup it sells', async () => {
|
|
154
|
+
const result = await run(baseContext);
|
|
155
|
+
const expected = Object.entries(result.results.Lines).reduce(
|
|
156
|
+
(total, [id, line]) => total + line.Sold * baseSettings.menu.Menu[id]!.Price,
|
|
157
|
+
0
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
expect(result.results.Takings).toBe(expected);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('keeps a running purse across days', async () => {
|
|
164
|
+
const first = await run(baseContext, 1);
|
|
165
|
+
const second = await run(first, 2);
|
|
166
|
+
|
|
167
|
+
expect(second.results.Purse).toBe(first.results.Profit + second.results.Profit);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('labels the results with the day they are for', async () => {
|
|
171
|
+
// Earlier days are read back with getPastState, so today's results
|
|
172
|
+
// are all the engine keeps, and they say which day they belong to.
|
|
173
|
+
const first = await run(baseContext, 1);
|
|
174
|
+
const second = await run(first, 2);
|
|
175
|
+
|
|
176
|
+
expect(first.results.Day).toBe(1);
|
|
177
|
+
expect(second.results.Day).toBe(2);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('forecasts tomorrow, and tomorrow plays out as forecast', async () => {
|
|
181
|
+
const today = await run(baseContext, 1);
|
|
182
|
+
const tomorrowsForecast = today.weather.Today;
|
|
183
|
+
|
|
184
|
+
const tomorrow = await run(today, 2);
|
|
185
|
+
|
|
186
|
+
expect(tomorrow.results.Weather).toBe(tomorrowsForecast);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('is busier on a sunny day than a rainy one', async () => {
|
|
190
|
+
// Same day number, so the same custom draw: only the weather differs.
|
|
191
|
+
const sunny = await runOnWeather('sunny');
|
|
192
|
+
const rainy = await runOnWeather('rainy');
|
|
193
|
+
|
|
194
|
+
expect(sunny).toBeGreaterThan(rainy);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('is deterministic, so a re-run of one day gives the same answer', async () => {
|
|
198
|
+
const once = await run(baseContext, 3);
|
|
199
|
+
const twice = await run(baseContext, 3);
|
|
200
|
+
|
|
201
|
+
expect(twice).toEqual(once);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('leaves the plan alone, so yesterday carries into today as a starting point', async () => {
|
|
205
|
+
const result = await run(makeAll(baseContext, { lemonade: 44, icedtea: 0, cookies: 0 }));
|
|
206
|
+
|
|
207
|
+
expect(result.plan.Make).toEqual({ lemonade: 44, icedtea: 0, cookies: 0 });
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
/** Total custom on a day whose weather is forced, by searching for a day with it. */
|
|
212
|
+
async function runOnWeather(weather: string): Promise<number> {
|
|
213
|
+
const stocked = makeAll(baseContext, { lemonade: 500, icedtea: 500, cookies: 500 });
|
|
214
|
+
for (let day = 1; day <= 40; day += 1) {
|
|
215
|
+
const result = await run(stocked, day);
|
|
216
|
+
if (result.results.Weather === weather) {
|
|
217
|
+
return Object.values(result.results.Lines).reduce((sum, line) => sum + line.Wanted, 0);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
throw new Error(`no ${weather} day in the first 40`);
|
|
221
|
+
}
|