@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,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What each kind of surface is, in protocol terms.
|
|
3
|
+
*
|
|
4
|
+
* §12.6 fixes the set of messages a surface must receive before it may paint.
|
|
5
|
+
* Holding that as a table rather than as four near-identical components is what
|
|
6
|
+
* makes a new surface kind an entry here instead of a new host implementation.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A surface the host can mount, named by what the specification calls it.
|
|
10
|
+
*
|
|
11
|
+
* `game` and `waiting-room` are both **player** surfaces (§6.14): a waiting room
|
|
12
|
+
* is a player UI in a different display context, not a reduced one, so every
|
|
13
|
+
* gate that asks 'is this a player surface?' admits both.
|
|
14
|
+
*/
|
|
15
|
+
export type SurfaceKind = 'game' | 'session' | 'setup' | 'scenario-editor' | 'waiting-room' | 'presentation';
|
|
16
|
+
export interface SurfaceDescriptor {
|
|
17
|
+
/**
|
|
18
|
+
* Messages that must all arrive before the guest paints (§12.6). The guest
|
|
19
|
+
* enforces this for itself; the host carries the list so it can fail loudly
|
|
20
|
+
* when an embedder builds an incomplete initial batch, rather than leaving
|
|
21
|
+
* the frame stuck on its loading state with nothing to say why.
|
|
22
|
+
*/
|
|
23
|
+
readonly required: readonly string[];
|
|
24
|
+
/**
|
|
25
|
+
* Messages the host sends in the initial batch that do not block painting.
|
|
26
|
+
* Listed so `initialBatch` can order them after the required ones.
|
|
27
|
+
*/
|
|
28
|
+
readonly alsoSent: readonly string[];
|
|
29
|
+
/** True for the player surfaces: game and waiting room (§6.14). */
|
|
30
|
+
readonly isPlayerSurface: boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare const SURFACES: Record<SurfaceKind, SurfaceDescriptor>;
|
|
33
|
+
/**
|
|
34
|
+
* An observer render receives no `setParticipant` (§13.3): the wrapper opens on
|
|
35
|
+
* `setViewerRole: 'observer'` instead, so the surface still paints with a null
|
|
36
|
+
* participant. Only the player surfaces have an observer render at all.
|
|
37
|
+
*/
|
|
38
|
+
export declare function requiredMessagesFor(surface: SurfaceKind, options?: {
|
|
39
|
+
observer?: boolean;
|
|
40
|
+
}): readonly string[];
|
|
41
|
+
/** True for the surfaces a participant sits in front of (§6.14). */
|
|
42
|
+
export declare function isPlayerSurface(surface: SurfaceKind): boolean;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `contrastText` every theme colour carries (§12.3).
|
|
3
|
+
*
|
|
4
|
+
* A host sends a text colour for each palette colour, meeting WCAG AA (4.5:1)
|
|
5
|
+
* against it, so a game drawing text on a theme colour need not work one out.
|
|
6
|
+
* Black or white always reaches at least 4.58:1 against any opaque colour, so
|
|
7
|
+
* the requirement can always be met, and an author's own choice is kept when
|
|
8
|
+
* it meets it too.
|
|
9
|
+
*
|
|
10
|
+
* Platform-facing: exported from
|
|
11
|
+
* `@seriousgaming/sdk-tools/host`.
|
|
12
|
+
*/
|
|
13
|
+
import type { ThemeColorRole, ThemeContrastText, UserInterfaceTheme } from '@seriousgaming/sdk/ui';
|
|
14
|
+
/** The roles a theme sends, in the order §12.3 lists them. */
|
|
15
|
+
export declare const THEME_COLOR_ROLES: readonly ThemeColorRole[];
|
|
16
|
+
/** The WCAG AA minimum for normal text. */
|
|
17
|
+
export declare const AA_CONTRAST = 4.5;
|
|
18
|
+
interface Rgba {
|
|
19
|
+
r: number;
|
|
20
|
+
g: number;
|
|
21
|
+
b: number;
|
|
22
|
+
a: number;
|
|
23
|
+
}
|
|
24
|
+
/** Read a CSS hex or `rgb()` / `rgba()` colour, or null for anything else. */
|
|
25
|
+
export declare function parseColour(value: string): Rgba | null;
|
|
26
|
+
/** WCAG 2 relative luminance of an opaque colour. */
|
|
27
|
+
export declare function relativeLuminance({ r, g, b }: Rgba): number;
|
|
28
|
+
/** WCAG 2 contrast ratio between two opaque colours, from 1 to 21. */
|
|
29
|
+
export declare function contrastRatio(a: Rgba, b: Rgba): number;
|
|
30
|
+
/**
|
|
31
|
+
* The text colour to draw on `colour`, as an opaque `#rrggbb`.
|
|
32
|
+
*
|
|
33
|
+
* `preferred` is an author's choice, kept (made opaque by painting it over
|
|
34
|
+
* the colour, as the browser would) when it meets AA; otherwise black or
|
|
35
|
+
* white, whichever contrasts more. A translucent colour is measured as drawn
|
|
36
|
+
* on white. A colour that is neither hex nor `rgb()` cannot be measured, and
|
|
37
|
+
* gets black.
|
|
38
|
+
*/
|
|
39
|
+
export declare function contrastTextFor(colour: string, preferred?: string): string;
|
|
40
|
+
/** One mode's colours before a host has given them their text colours. */
|
|
41
|
+
export type ModeColoursInput = Record<ThemeColorRole, string> & {
|
|
42
|
+
contrastText?: Partial<ThemeContrastText>;
|
|
43
|
+
};
|
|
44
|
+
/** A theme before a host has given its colours their text colours. */
|
|
45
|
+
export interface ThemeInput {
|
|
46
|
+
mode: 'light' | 'dark';
|
|
47
|
+
light: ModeColoursInput;
|
|
48
|
+
dark: ModeColoursInput;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The `setTheme` payload for a theme: every colour of both modes given its
|
|
52
|
+
* `contrastText`, keeping any the author supplied that meet AA.
|
|
53
|
+
*/
|
|
54
|
+
export declare function withContrastText(theme: ThemeInput): UserInterfaceTheme;
|
|
55
|
+
export {};
|
package/dist/host.js
ADDED
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
// cli/host/protocol.ts
|
|
2
|
+
var UI_PROTOCOL_VERSION = 4;
|
|
3
|
+
|
|
4
|
+
// cli/host/surfaces.ts
|
|
5
|
+
var SURFACES = {
|
|
6
|
+
game: {
|
|
7
|
+
required: [
|
|
8
|
+
"stateChanged",
|
|
9
|
+
"setSettings",
|
|
10
|
+
"setTheme",
|
|
11
|
+
"setLocale",
|
|
12
|
+
"setAssetUrls",
|
|
13
|
+
"setParticipant"
|
|
14
|
+
],
|
|
15
|
+
alsoSent: [
|
|
16
|
+
"setTimer",
|
|
17
|
+
"setViewerRole",
|
|
18
|
+
"setOwnTeam",
|
|
19
|
+
"setOwnTeamJoinLink",
|
|
20
|
+
"setParticipantList",
|
|
21
|
+
"setTeamList",
|
|
22
|
+
"setRunKind",
|
|
23
|
+
"setRoundProcessing",
|
|
24
|
+
"setCrossTeamData",
|
|
25
|
+
"themePreferenceChanged"
|
|
26
|
+
],
|
|
27
|
+
isPlayerSurface: true
|
|
28
|
+
},
|
|
29
|
+
"waiting-room": {
|
|
30
|
+
required: [
|
|
31
|
+
"stateChanged",
|
|
32
|
+
"setSettings",
|
|
33
|
+
"setTheme",
|
|
34
|
+
"setLocale",
|
|
35
|
+
"setAssetUrls",
|
|
36
|
+
"setParticipant"
|
|
37
|
+
],
|
|
38
|
+
alsoSent: [
|
|
39
|
+
"setTimer",
|
|
40
|
+
"setViewerRole",
|
|
41
|
+
"setOwnTeam",
|
|
42
|
+
"setOwnTeamJoinLink",
|
|
43
|
+
"setParticipantList",
|
|
44
|
+
"setTeamList",
|
|
45
|
+
"setRunKind",
|
|
46
|
+
"setCrossTeamData",
|
|
47
|
+
"themePreferenceChanged"
|
|
48
|
+
],
|
|
49
|
+
isPlayerSurface: true
|
|
50
|
+
},
|
|
51
|
+
session: {
|
|
52
|
+
required: [
|
|
53
|
+
"setSessionMeta",
|
|
54
|
+
"setSessionData",
|
|
55
|
+
"setSettings",
|
|
56
|
+
"setTheme",
|
|
57
|
+
"setLocale",
|
|
58
|
+
"setAssetUrls",
|
|
59
|
+
"setTeamConfiguration"
|
|
60
|
+
],
|
|
61
|
+
alsoSent: [
|
|
62
|
+
"setTeamList",
|
|
63
|
+
"setParticipantList",
|
|
64
|
+
"setTeamLinks",
|
|
65
|
+
"setAvailableOverlays",
|
|
66
|
+
"setRunKind",
|
|
67
|
+
"setWalkthroughLink",
|
|
68
|
+
"themePreferenceChanged"
|
|
69
|
+
],
|
|
70
|
+
isPlayerSurface: false
|
|
71
|
+
},
|
|
72
|
+
presentation: {
|
|
73
|
+
// `setPresentation` carries a `null` session when there is none, so a
|
|
74
|
+
// presentation is the one surface a host can mount without one.
|
|
75
|
+
required: ["setPresentation", "setSettings", "setTheme", "setLocale", "setAssetUrls"],
|
|
76
|
+
alsoSent: ["themePreferenceChanged"],
|
|
77
|
+
isPlayerSurface: false
|
|
78
|
+
},
|
|
79
|
+
// The two settings editors (§6.5) share a wrapper and a batch. Neither is
|
|
80
|
+
// told which slot it is: each is its own bundle, so it knows.
|
|
81
|
+
setup: {
|
|
82
|
+
required: ["setSettings", "setTheme", "setLocale", "setAssetUrls"],
|
|
83
|
+
alsoSent: ["themePreferenceChanged"],
|
|
84
|
+
isPlayerSurface: false
|
|
85
|
+
},
|
|
86
|
+
"scenario-editor": {
|
|
87
|
+
required: ["setSettings", "setTheme", "setLocale", "setAssetUrls"],
|
|
88
|
+
alsoSent: ["themePreferenceChanged"],
|
|
89
|
+
isPlayerSurface: false
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
function requiredMessagesFor(surface, options = {}) {
|
|
93
|
+
const { required, isPlayerSurface: isPlayerSurface2 } = SURFACES[surface];
|
|
94
|
+
if (!options.observer || !isPlayerSurface2) return required;
|
|
95
|
+
return required.filter((type) => type !== "setParticipant");
|
|
96
|
+
}
|
|
97
|
+
function isPlayerSurface(surface) {
|
|
98
|
+
return SURFACES[surface].isPlayerSurface;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// cli/host/host-channel.ts
|
|
102
|
+
var ADAPTER_REQUESTS = /* @__PURE__ */ new Set([
|
|
103
|
+
"callEndpoint",
|
|
104
|
+
"uploadBlob",
|
|
105
|
+
"getPastState",
|
|
106
|
+
"getAllTeamsParticipants"
|
|
107
|
+
]);
|
|
108
|
+
var HostChannel = class {
|
|
109
|
+
options;
|
|
110
|
+
host;
|
|
111
|
+
logger;
|
|
112
|
+
port = null;
|
|
113
|
+
readyListener = null;
|
|
114
|
+
loadListener = null;
|
|
115
|
+
handshakeTimer = null;
|
|
116
|
+
disposed = false;
|
|
117
|
+
/**
|
|
118
|
+
* Last payload sent per message type, serialised.
|
|
119
|
+
*
|
|
120
|
+
* `setState` compares against this so an unchanged slice is not re-pushed.
|
|
121
|
+
* At a large cohort the session payload is the thing that grows, and
|
|
122
|
+
* re-sending a whole snapshot because one team's tick moved is the cost
|
|
123
|
+
* §12.4.2 warns about.
|
|
124
|
+
*/
|
|
125
|
+
lastSent = /* @__PURE__ */ new Map();
|
|
126
|
+
/**
|
|
127
|
+
* The last value of each state message, in the order the types were first
|
|
128
|
+
* sent, replayed whole on every reconnect (§12.7 step 3).
|
|
129
|
+
*
|
|
130
|
+
* A reloading frame has seen nothing, and the embedder has no reason to
|
|
131
|
+
* push again: its state has not changed, only the frame under it. Without
|
|
132
|
+
* this the guest re-handshakes into silence and blocks its first paint for
|
|
133
|
+
* good, which is what a surface fixed after a crash used to do (#564).
|
|
134
|
+
*
|
|
135
|
+
* Only the state slices are kept. A one-shot the host sent once, such as a
|
|
136
|
+
* validator's refusal or an asset-auth request, belongs to the moment it
|
|
137
|
+
* happened, and replaying it would show the new frame a rejection of a
|
|
138
|
+
* submission it never made.
|
|
139
|
+
*/
|
|
140
|
+
sticky = /* @__PURE__ */ new Map();
|
|
141
|
+
/** One-shot messages queued until the handshake completes, flushed in order. */
|
|
142
|
+
outbound = [];
|
|
143
|
+
/** In-flight host → guest requests, keyed by `requestId`. */
|
|
144
|
+
pending = /* @__PURE__ */ new Map();
|
|
145
|
+
requestCounter = 0;
|
|
146
|
+
constructor(options) {
|
|
147
|
+
this.options = options;
|
|
148
|
+
this.host = options.window ?? globalThis.window;
|
|
149
|
+
this.logger = options.logger ?? console;
|
|
150
|
+
this.listenForReady();
|
|
151
|
+
}
|
|
152
|
+
/** True once the guest has handshaked and the port is open. */
|
|
153
|
+
get connected() {
|
|
154
|
+
return this.port !== null;
|
|
155
|
+
}
|
|
156
|
+
// -----------------------------------------------------------------------
|
|
157
|
+
// Sending
|
|
158
|
+
// -----------------------------------------------------------------------
|
|
159
|
+
/**
|
|
160
|
+
* Push one message, queueing it until the handshake completes.
|
|
161
|
+
*
|
|
162
|
+
* Queueing rather than dropping is what lets an embedder build the initial
|
|
163
|
+
* batch as soon as it mounts the frame, without racing the guest's `ready`.
|
|
164
|
+
*/
|
|
165
|
+
send(message) {
|
|
166
|
+
if (this.disposed) return;
|
|
167
|
+
this.lastSent.set(message.type, stableStringify(message));
|
|
168
|
+
if (this.port) this.port.postMessage(message);
|
|
169
|
+
else this.outbound.push(message);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Push a state slice: delivered now if the guest is listening, and kept so
|
|
173
|
+
* a frame that reloads is told the same thing again.
|
|
174
|
+
*/
|
|
175
|
+
sendState(message) {
|
|
176
|
+
if (this.disposed) return;
|
|
177
|
+
this.sticky.set(message.type, message);
|
|
178
|
+
this.lastSent.set(message.type, stableStringify(message));
|
|
179
|
+
this.port?.postMessage(message);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Push a message only when its payload differs from the last one sent of
|
|
183
|
+
* that type. Returns true when it went out.
|
|
184
|
+
*/
|
|
185
|
+
sendIfChanged(message) {
|
|
186
|
+
if (this.disposed) return false;
|
|
187
|
+
if (this.lastSent.get(message.type) === stableStringify(message)) return false;
|
|
188
|
+
this.sendState(message);
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Send the initial batch (§12.6), verifying it carries every message the
|
|
193
|
+
* surface blocks its first paint on.
|
|
194
|
+
*
|
|
195
|
+
* The guest enforces the same list for itself by simply not painting. That
|
|
196
|
+
* failure mode is a frame stuck on its loading state with nothing to say
|
|
197
|
+
* why, which is a bad afternoon; failing here names the missing message.
|
|
198
|
+
*/
|
|
199
|
+
sendInitialBatch(messages) {
|
|
200
|
+
const required = requiredMessagesFor(this.options.surface, {
|
|
201
|
+
observer: this.options.observer
|
|
202
|
+
});
|
|
203
|
+
const supplied = new Set(messages.map((message) => message.type));
|
|
204
|
+
const missing = required.filter((type) => !supplied.has(type));
|
|
205
|
+
if (missing.length > 0) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Initial batch for a "${this.options.surface}" surface is missing ${missing.join(", ")}. The guest blocks its first paint on ${required.join(", ")}.`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
for (const message of messages) this.sendState(message);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Send a request the guest answers, resolving when it does.
|
|
214
|
+
*
|
|
215
|
+
* Only `refreshAssetAuth` uses this today (§16.4): the host hands the frame
|
|
216
|
+
* a token and the frame reports back whether redeeming it worked.
|
|
217
|
+
*/
|
|
218
|
+
request(message, timeoutMs = 3e4) {
|
|
219
|
+
const requestId = `host-${++this.requestCounter}`;
|
|
220
|
+
return new Promise((resolve, reject) => {
|
|
221
|
+
const timer = setTimeout(() => {
|
|
222
|
+
this.pending.delete(requestId);
|
|
223
|
+
reject(new Error(`Guest did not answer "${message.type}" in ${timeoutMs}ms`));
|
|
224
|
+
}, timeoutMs);
|
|
225
|
+
this.pending.set(requestId, {
|
|
226
|
+
resolve: () => {
|
|
227
|
+
clearTimeout(timer);
|
|
228
|
+
resolve();
|
|
229
|
+
},
|
|
230
|
+
reject: (error) => {
|
|
231
|
+
clearTimeout(timer);
|
|
232
|
+
reject(error);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
this.send({ ...message, requestId });
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
dispose() {
|
|
239
|
+
if (this.disposed) return;
|
|
240
|
+
this.disposed = true;
|
|
241
|
+
this.clearHandshakeTimer();
|
|
242
|
+
if (this.readyListener) {
|
|
243
|
+
this.host.removeEventListener("message", this.readyListener);
|
|
244
|
+
this.readyListener = null;
|
|
245
|
+
}
|
|
246
|
+
if (this.loadListener) {
|
|
247
|
+
this.options.iframe.removeEventListener?.("load", this.loadListener);
|
|
248
|
+
this.loadListener = null;
|
|
249
|
+
}
|
|
250
|
+
this.closePort();
|
|
251
|
+
this.rejectPending("Channel disposed");
|
|
252
|
+
this.options.onTeardown?.("disposed");
|
|
253
|
+
}
|
|
254
|
+
// -----------------------------------------------------------------------
|
|
255
|
+
// Handshake
|
|
256
|
+
// -----------------------------------------------------------------------
|
|
257
|
+
listenForReady() {
|
|
258
|
+
const listener = (event) => {
|
|
259
|
+
if (this.disposed) return;
|
|
260
|
+
const data = event.data;
|
|
261
|
+
if (!data || data.type !== "ready") return;
|
|
262
|
+
if (event.source !== this.options.iframe.contentWindow) return;
|
|
263
|
+
if (event.origin !== this.options.iframeOrigin) {
|
|
264
|
+
this.logger.warn(
|
|
265
|
+
`[${this.options.uiIdentifier}] ignoring a handshake from ${event.origin}; this frame is pinned to ${this.options.iframeOrigin}`
|
|
266
|
+
);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const peerVersion = data.protocolVersion;
|
|
270
|
+
if (typeof peerVersion === "number" && peerVersion !== UI_PROTOCOL_VERSION) {
|
|
271
|
+
this.logger.warn(
|
|
272
|
+
`[${this.options.uiIdentifier}] host\u2194iframe protocol version mismatch: iframe ${peerVersion}, host ${UI_PROTOCOL_VERSION}`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
this.connect();
|
|
276
|
+
};
|
|
277
|
+
this.readyListener = listener;
|
|
278
|
+
this.host.addEventListener("message", listener);
|
|
279
|
+
this.armHandshakeTimer();
|
|
280
|
+
this.listenForLoads();
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Bound every document the frame loads, not only the first.
|
|
284
|
+
*
|
|
285
|
+
* A frame that connected and then replaced its own document leaves the
|
|
286
|
+
* host holding a port nobody answers, so `connected` alone proves nothing.
|
|
287
|
+
* A document's `ready` may land either side of its `load`, so a live port
|
|
288
|
+
* is pinged as well: a `pong` shows the document that handshaked is still
|
|
289
|
+
* the one in the frame, and a fresh `ready` shows a reload.
|
|
290
|
+
*/
|
|
291
|
+
listenForLoads() {
|
|
292
|
+
const { iframe } = this.options;
|
|
293
|
+
if (!iframe.addEventListener) return;
|
|
294
|
+
const listener = () => {
|
|
295
|
+
if (this.disposed) return;
|
|
296
|
+
this.armHandshakeTimer();
|
|
297
|
+
this.port?.postMessage({ type: "ping" });
|
|
298
|
+
};
|
|
299
|
+
this.loadListener = listener;
|
|
300
|
+
iframe.addEventListener("load", listener);
|
|
301
|
+
}
|
|
302
|
+
armHandshakeTimer() {
|
|
303
|
+
this.clearHandshakeTimer();
|
|
304
|
+
const timeoutMs = this.options.handshakeTimeoutMs ?? 1e4;
|
|
305
|
+
this.handshakeTimer = setTimeout(() => {
|
|
306
|
+
this.handshakeTimer = null;
|
|
307
|
+
if (this.disposed) return;
|
|
308
|
+
this.closePort();
|
|
309
|
+
this.options.onTeardown?.("handshake-timeout");
|
|
310
|
+
}, timeoutMs);
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Open a fresh port and hand the guest its end.
|
|
314
|
+
*
|
|
315
|
+
* Runs on every `ready`, not only the first: a reload or HMR re-announces,
|
|
316
|
+
* and §12.7 requires the old port closed, a new channel created, and the
|
|
317
|
+
* whole initial batch re-sent rather than the live port re-identified.
|
|
318
|
+
*/
|
|
319
|
+
connect() {
|
|
320
|
+
this.closePort();
|
|
321
|
+
this.rejectPending("Connection reset");
|
|
322
|
+
this.lastSent.clear();
|
|
323
|
+
const replay = [...this.sticky.values()];
|
|
324
|
+
const channel = new MessageChannel();
|
|
325
|
+
this.port = channel.port1;
|
|
326
|
+
this.port.onmessage = (event) => this.receive(event.data);
|
|
327
|
+
this.port.start();
|
|
328
|
+
this.clearHandshakeTimer();
|
|
329
|
+
this.options.iframe.contentWindow?.postMessage(
|
|
330
|
+
{
|
|
331
|
+
type: "init",
|
|
332
|
+
protocolVersion: UI_PROTOCOL_VERSION,
|
|
333
|
+
sessionKey: this.options.sessionKey
|
|
334
|
+
},
|
|
335
|
+
this.options.iframeOrigin,
|
|
336
|
+
[channel.port2]
|
|
337
|
+
);
|
|
338
|
+
for (const message of replay) this.sendState(message);
|
|
339
|
+
const queued = this.outbound;
|
|
340
|
+
this.outbound = [];
|
|
341
|
+
for (const message of queued) this.port.postMessage(message);
|
|
342
|
+
this.options.onConnect?.();
|
|
343
|
+
}
|
|
344
|
+
closePort() {
|
|
345
|
+
if (!this.port) return;
|
|
346
|
+
this.port.onmessage = null;
|
|
347
|
+
this.port.close();
|
|
348
|
+
this.port = null;
|
|
349
|
+
}
|
|
350
|
+
clearHandshakeTimer() {
|
|
351
|
+
if (this.handshakeTimer === null) return;
|
|
352
|
+
clearTimeout(this.handshakeTimer);
|
|
353
|
+
this.handshakeTimer = null;
|
|
354
|
+
}
|
|
355
|
+
rejectPending(reason) {
|
|
356
|
+
for (const { reject } of this.pending.values()) reject(new Error(reason));
|
|
357
|
+
this.pending.clear();
|
|
358
|
+
}
|
|
359
|
+
// -----------------------------------------------------------------------
|
|
360
|
+
// Receiving
|
|
361
|
+
// -----------------------------------------------------------------------
|
|
362
|
+
receive(message) {
|
|
363
|
+
if (!message || typeof message.type !== "string") return;
|
|
364
|
+
switch (message.type) {
|
|
365
|
+
case "pong":
|
|
366
|
+
this.clearHandshakeTimer();
|
|
367
|
+
return;
|
|
368
|
+
case "refreshAssetAuth:response": {
|
|
369
|
+
const { requestId, ok, error } = message;
|
|
370
|
+
if (typeof requestId !== "string") return;
|
|
371
|
+
const waiting = this.pending.get(requestId);
|
|
372
|
+
this.pending.delete(requestId);
|
|
373
|
+
if (ok) waiting?.resolve();
|
|
374
|
+
else waiting?.reject(new Error(error ?? "Asset auth refresh failed"));
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (ADAPTER_REQUESTS.has(message.type)) {
|
|
379
|
+
void this.answer(message);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
this.options.onMessage?.(message);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Run one guest request against the adapter and frame the answer.
|
|
386
|
+
*
|
|
387
|
+
* A throwing adapter becomes an `ok: false` response on the same
|
|
388
|
+
* `requestId`, never an unhandled rejection and never a dropped request:
|
|
389
|
+
* the guest's promise has to settle either way or the surface waits forever.
|
|
390
|
+
*/
|
|
391
|
+
async answer(message) {
|
|
392
|
+
const requestId = message.requestId;
|
|
393
|
+
if (typeof requestId !== "string") {
|
|
394
|
+
this.logger.warn(
|
|
395
|
+
`[${this.options.uiIdentifier}] dropped a "${message.type}" with no requestId`
|
|
396
|
+
);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const responseType = `${message.type}:response`;
|
|
400
|
+
try {
|
|
401
|
+
const result = await this.dispatch(message);
|
|
402
|
+
this.send({ type: responseType, requestId, ok: true, ...result });
|
|
403
|
+
} catch (error) {
|
|
404
|
+
this.send(
|
|
405
|
+
message.type === "callEndpoint" ? { type: responseType, requestId, ok: false, ...endpointFailureOf(error) } : {
|
|
406
|
+
type: responseType,
|
|
407
|
+
requestId,
|
|
408
|
+
ok: false,
|
|
409
|
+
error: error instanceof Error ? error.message : String(error)
|
|
410
|
+
}
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/** Route one request to its adapter method and shape the success payload. */
|
|
415
|
+
async dispatch(message) {
|
|
416
|
+
const adapter = this.options.adapter;
|
|
417
|
+
const origin = {
|
|
418
|
+
uiIdentifier: this.options.uiIdentifier,
|
|
419
|
+
...this.options.origin
|
|
420
|
+
};
|
|
421
|
+
switch (message.type) {
|
|
422
|
+
case "callEndpoint": {
|
|
423
|
+
if (!adapter?.callEndpoint) throw new Error("This host serves no endpoints");
|
|
424
|
+
const response = await adapter.callEndpoint(
|
|
425
|
+
{
|
|
426
|
+
endpointIdentifier: String(message.endpointIdentifier),
|
|
427
|
+
request: message.request
|
|
428
|
+
},
|
|
429
|
+
origin
|
|
430
|
+
);
|
|
431
|
+
return { response };
|
|
432
|
+
}
|
|
433
|
+
case "uploadBlob": {
|
|
434
|
+
if (!adapter?.uploadBlob) throw new Error("This host stores no blobs");
|
|
435
|
+
const reference = await adapter.uploadBlob(
|
|
436
|
+
{
|
|
437
|
+
data: message.data,
|
|
438
|
+
name: String(message.name),
|
|
439
|
+
mimeType: String(message.mimeType)
|
|
440
|
+
},
|
|
441
|
+
origin
|
|
442
|
+
);
|
|
443
|
+
return { reference };
|
|
444
|
+
}
|
|
445
|
+
case "getPastState": {
|
|
446
|
+
if (!adapter?.getPastState) throw new Error("This host serves no past state");
|
|
447
|
+
const entries = await adapter.getPastState(
|
|
448
|
+
message.query,
|
|
449
|
+
origin
|
|
450
|
+
);
|
|
451
|
+
return { entries };
|
|
452
|
+
}
|
|
453
|
+
case "getAllTeamsParticipants": {
|
|
454
|
+
const participants = await adapter?.getAllTeamsParticipants?.(origin) ?? [];
|
|
455
|
+
return { participants };
|
|
456
|
+
}
|
|
457
|
+
default:
|
|
458
|
+
throw new Error(`Unroutable request "${message.type}"`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
function stableStringify(value) {
|
|
463
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "undefined";
|
|
464
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
465
|
+
const entries = Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`);
|
|
466
|
+
return `{${entries.join(",")}}`;
|
|
467
|
+
}
|
|
468
|
+
function endpointFailureOf(error) {
|
|
469
|
+
const failure = typeof error === "object" && error !== null ? error.failure : void 0;
|
|
470
|
+
if (typeof failure === "object" && failure !== null && typeof failure.outcome?.kind === "string" && Array.isArray(failure.applied)) {
|
|
471
|
+
return failure;
|
|
472
|
+
}
|
|
473
|
+
return {
|
|
474
|
+
outcome: {
|
|
475
|
+
kind: "error",
|
|
476
|
+
message: error instanceof Error ? error.message : String(error)
|
|
477
|
+
},
|
|
478
|
+
applied: []
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// cli/host/theme.ts
|
|
483
|
+
var THEME_COLOR_ROLES = [
|
|
484
|
+
"primary",
|
|
485
|
+
"secondary",
|
|
486
|
+
"success",
|
|
487
|
+
"error",
|
|
488
|
+
"warning",
|
|
489
|
+
"info"
|
|
490
|
+
];
|
|
491
|
+
var AA_CONTRAST = 4.5;
|
|
492
|
+
function parseColour(value) {
|
|
493
|
+
const text = value.trim().toLowerCase();
|
|
494
|
+
const hex = /^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/.exec(text);
|
|
495
|
+
if (hex) {
|
|
496
|
+
let digits = hex[1];
|
|
497
|
+
if (digits.length <= 4) digits = [...digits].map((digit) => digit + digit).join("");
|
|
498
|
+
const channel = (at) => parseInt(digits.slice(at, at + 2), 16);
|
|
499
|
+
return {
|
|
500
|
+
r: channel(0),
|
|
501
|
+
g: channel(2),
|
|
502
|
+
b: channel(4),
|
|
503
|
+
a: digits.length === 8 ? channel(6) / 255 : 1
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
const functional = /^rgba?\(([^)]*)\)$/.exec(text);
|
|
507
|
+
if (functional) {
|
|
508
|
+
const parts = functional[1].split(/[\s,/]+/).filter(Boolean);
|
|
509
|
+
if (parts.length !== 3 && parts.length !== 4) return null;
|
|
510
|
+
const [r, g, b] = parts.slice(0, 3).map(
|
|
511
|
+
(part) => part.endsWith("%") ? parseFloat(part) / 100 * 255 : parseFloat(part)
|
|
512
|
+
);
|
|
513
|
+
const alpha = parts[3];
|
|
514
|
+
const a = alpha === void 0 ? 1 : alpha.endsWith("%") ? parseFloat(alpha) / 100 : parseFloat(alpha);
|
|
515
|
+
if ([r, g, b, a].some((n) => !Number.isFinite(n))) return null;
|
|
516
|
+
return { r: clamp(r, 255), g: clamp(g, 255), b: clamp(b, 255), a: clamp(a, 1) };
|
|
517
|
+
}
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
function clamp(value, max) {
|
|
521
|
+
return Math.min(max, Math.max(0, value));
|
|
522
|
+
}
|
|
523
|
+
function composite(top, bottom) {
|
|
524
|
+
const mix = (over, under) => over * top.a + under * (1 - top.a);
|
|
525
|
+
return { r: mix(top.r, bottom.r), g: mix(top.g, bottom.g), b: mix(top.b, bottom.b), a: 1 };
|
|
526
|
+
}
|
|
527
|
+
function relativeLuminance({ r, g, b }) {
|
|
528
|
+
const linear = (channel) => {
|
|
529
|
+
const c = channel / 255;
|
|
530
|
+
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
531
|
+
};
|
|
532
|
+
return 0.2126 * linear(r) + 0.7152 * linear(g) + 0.0722 * linear(b);
|
|
533
|
+
}
|
|
534
|
+
function contrastRatio(a, b) {
|
|
535
|
+
const [light, dark] = [relativeLuminance(a), relativeLuminance(b)].sort((x, y) => y - x);
|
|
536
|
+
return (light + 0.05) / (dark + 0.05);
|
|
537
|
+
}
|
|
538
|
+
function toHex({ r, g, b }) {
|
|
539
|
+
const byte = (n) => Math.round(n).toString(16).padStart(2, "0");
|
|
540
|
+
return `#${byte(r)}${byte(g)}${byte(b)}`;
|
|
541
|
+
}
|
|
542
|
+
var BLACK = { r: 0, g: 0, b: 0, a: 1 };
|
|
543
|
+
var WHITE = { r: 255, g: 255, b: 255, a: 1 };
|
|
544
|
+
function contrastTextFor(colour, preferred) {
|
|
545
|
+
const parsed = parseColour(colour);
|
|
546
|
+
if (!parsed) return toHex(BLACK);
|
|
547
|
+
const background = parsed.a < 1 ? composite(parsed, WHITE) : parsed;
|
|
548
|
+
const author = preferred === void 0 ? null : parseColour(preferred);
|
|
549
|
+
if (author) {
|
|
550
|
+
const painted = composite(author, background);
|
|
551
|
+
if (contrastRatio(painted, background) >= AA_CONTRAST) return toHex(painted);
|
|
552
|
+
}
|
|
553
|
+
return contrastRatio(BLACK, background) >= contrastRatio(WHITE, background) ? toHex(BLACK) : toHex(WHITE);
|
|
554
|
+
}
|
|
555
|
+
function modeWithContrast(colours) {
|
|
556
|
+
const contrastText = Object.fromEntries(
|
|
557
|
+
THEME_COLOR_ROLES.map((role) => [
|
|
558
|
+
role,
|
|
559
|
+
contrastTextFor(colours[role], colours.contrastText?.[role])
|
|
560
|
+
])
|
|
561
|
+
);
|
|
562
|
+
const roles = Object.fromEntries(
|
|
563
|
+
THEME_COLOR_ROLES.map((role) => [role, colours[role]])
|
|
564
|
+
);
|
|
565
|
+
return { ...roles, contrastText };
|
|
566
|
+
}
|
|
567
|
+
function withContrastText(theme) {
|
|
568
|
+
return {
|
|
569
|
+
mode: theme.mode,
|
|
570
|
+
light: modeWithContrast(theme.light),
|
|
571
|
+
dark: modeWithContrast(theme.dark)
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
export {
|
|
575
|
+
HostChannel,
|
|
576
|
+
SURFACES,
|
|
577
|
+
THEME_COLOR_ROLES,
|
|
578
|
+
UI_PROTOCOL_VERSION,
|
|
579
|
+
contrastTextFor,
|
|
580
|
+
isPlayerSurface,
|
|
581
|
+
requiredMessagesFor,
|
|
582
|
+
withContrastText
|
|
583
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public exports for the prompts module
|
|
3
|
+
* Provides interactive prompts for building JSON schema properties
|
|
4
|
+
*/
|
|
5
|
+
export { promptForSchemaProperty, promptForSchemaProperties, editSchemaProperty, reviewSchemaProperties, } from './schema-builder.js';
|
|
6
|
+
export { validatePropertyName, validateNumberValue, validateMaximumValue, PASCAL_CASE_PATTERN, } from './validators.js';
|
|
7
|
+
export type { PropertySchema, NumberPropertyDefaults, StringPropertyDefaults, BooleanPropertyDefaults, PropertyContext, PromptPropertyOptions, PromptPropertiesOptions, EditPropertyOptions, EditPropertyResult, ReviewPropertiesOptions, ReviewPropertiesResult, } from './types.js';
|