@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.
Files changed (391) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +36 -2
  3. package/dist/bundling/logic-exports.d.ts +34 -0
  4. package/dist/bundling/node-builtins.d.ts +21 -0
  5. package/dist/bundling/transpiler.d.ts +7 -0
  6. package/dist/cli.js +27368 -0
  7. package/dist/client/dev-server.d.ts +59 -0
  8. package/dist/client/index.d.ts +28 -0
  9. package/dist/client/stub-services.d.ts +32 -0
  10. package/dist/client/test-session.d.ts +195 -0
  11. package/dist/client.js +16884 -0
  12. package/dist/core/asset-reference-translator.d.ts +46 -0
  13. package/dist/core/clock.d.ts +57 -0
  14. package/dist/core/faults.d.ts +65 -0
  15. package/dist/core/game-settings.d.ts +65 -0
  16. package/dist/core/history.d.ts +89 -0
  17. package/dist/core/listen-with-fallback.d.ts +30 -0
  18. package/dist/core/mutation-queue.d.ts +61 -0
  19. package/dist/core/package-scenario.d.ts +89 -0
  20. package/dist/core/participant-registry.d.ts +109 -0
  21. package/dist/core/pending-changes.d.ts +14 -0
  22. package/dist/core/protocol.d.ts +83 -0
  23. package/dist/core/scenario-drafts.d.ts +32 -0
  24. package/dist/core/session-endpoints.d.ts +108 -0
  25. package/dist/core/session-gates.d.ts +75 -0
  26. package/dist/core/session-host.d.ts +94 -0
  27. package/dist/core/session-refusals.d.ts +56 -0
  28. package/dist/core/session-rewind.d.ts +32 -0
  29. package/dist/core/session-roster.d.ts +128 -0
  30. package/dist/core/session-surfaces.d.ts +32 -0
  31. package/dist/core/session-ticks.d.ts +18 -0
  32. package/dist/core/session-timers.d.ts +84 -0
  33. package/dist/core/session-writes.d.ts +40 -0
  34. package/dist/core/session.d.ts +385 -0
  35. package/dist/core/settings-locks.d.ts +22 -0
  36. package/dist/core/team-names.d.ts +52 -0
  37. package/dist/core/team-state-store.d.ts +209 -0
  38. package/dist/core/tick-timer.d.ts +75 -0
  39. package/dist/dev/blob-links.d.ts +10 -0
  40. package/dist/dev/console/assets/index-DgFglsnA.js +204 -0
  41. package/dist/dev/console/index.html +12 -0
  42. package/dist/dev/console-path.d.ts +19 -0
  43. package/dist/dev/lockfile.d.ts +124 -0
  44. package/dist/dev/package-watcher.d.ts +21 -0
  45. package/dist/dev/routes.d.ts +67 -0
  46. package/dist/dev/shell.d.ts +260 -0
  47. package/dist/dev/surfaces.d.ts +41 -0
  48. package/dist/dev/tools-identity.d.ts +23 -0
  49. package/dist/dev/view/batch.d.ts +172 -0
  50. package/dist/dev/view/commands.d.ts +75 -0
  51. package/dist/dev/view/route.d.ts +58 -0
  52. package/dist/dev/view/view.d.ts +78 -0
  53. package/dist/dev/view/viewer.d.ts +32 -0
  54. package/dist/discovery/config.d.ts +9 -0
  55. package/dist/discovery/manifest-paths.d.ts +21 -0
  56. package/dist/discovery/removed-fields.d.ts +90 -0
  57. package/dist/docs/api/deck.md +386 -0
  58. package/dist/docs/api/index.md +67 -0
  59. package/dist/docs/api/sdk-commands.md +286 -0
  60. package/dist/docs/api/sdk-e2e.md +433 -0
  61. package/dist/docs/api/sdk-projection.md +207 -0
  62. package/dist/docs/api/sdk-testing.md +214 -0
  63. package/dist/docs/api/sdk-tools-client.md +560 -0
  64. package/dist/docs/api/sdk-ui-testing.md +231 -0
  65. package/dist/docs/api/sdk-ui.md +1269 -0
  66. package/dist/docs/api/sdk.md +2140 -0
  67. package/dist/docs/changelog-0.x.md +1156 -0
  68. package/dist/docs/changelog.md +236 -0
  69. package/dist/docs/cli-and-dev-server.md +217 -0
  70. package/dist/docs/cli-error-codes.md +154 -0
  71. package/dist/docs/cli-reference.md +2488 -0
  72. package/dist/docs/cli-simulation-testing.md +222 -0
  73. package/dist/docs/concepts/index.md +46 -0
  74. package/dist/docs/concepts/terminology.md +322 -0
  75. package/dist/docs/environment-variables.md +102 -0
  76. package/dist/docs/getting-started.md +145 -0
  77. package/dist/docs/guides/ai-facilitation.md +220 -0
  78. package/dist/docs/guides/capabilities.md +460 -0
  79. package/dist/docs/guides/endpoints.md +335 -0
  80. package/dist/docs/guides/engine-testing.md +477 -0
  81. package/dist/docs/guides/engine.md +527 -0
  82. package/dist/docs/guides/file-handling.md +405 -0
  83. package/dist/docs/guides/games.md +176 -0
  84. package/dist/docs/guides/global-settings.md +94 -0
  85. package/dist/docs/guides/guide.md +123 -0
  86. package/dist/docs/guides/hooks.md +183 -0
  87. package/dist/docs/guides/index.md +51 -0
  88. package/dist/docs/guides/internationalization.md +288 -0
  89. package/dist/docs/guides/llm-integration.md +635 -0
  90. package/dist/docs/guides/multi-team-games.md +586 -0
  91. package/dist/docs/guides/patterns.md +391 -0
  92. package/dist/docs/guides/presentation.md +196 -0
  93. package/dist/docs/guides/production-constraints.md +130 -0
  94. package/dist/docs/guides/scenario-editor.md +143 -0
  95. package/dist/docs/guides/scenarios.md +515 -0
  96. package/dist/docs/guides/session.md +972 -0
  97. package/dist/docs/guides/settings.md +458 -0
  98. package/dist/docs/guides/setup.md +193 -0
  99. package/dist/docs/guides/speech-generation.md +233 -0
  100. package/dist/docs/guides/starter-game.md +164 -0
  101. package/dist/docs/guides/testing.md +575 -0
  102. package/dist/docs/guides/tick-controller.md +569 -0
  103. package/dist/docs/guides/type-safety.md +516 -0
  104. package/dist/docs/guides/ui.md +1389 -0
  105. package/dist/docs/guides/upgrading.md +636 -0
  106. package/dist/docs/guides/validators.md +217 -0
  107. package/dist/docs/guides/waiting-room.md +244 -0
  108. package/dist/docs/index.md +41 -0
  109. package/dist/docs/recipes/act-on-the-session-from-an-endpoint.md +95 -0
  110. package/dist/docs/recipes/adapt-a-game-for-walkthroughs.md +133 -0
  111. package/dist/docs/recipes/adapt-ui-for-observers.md +74 -0
  112. package/dist/docs/recipes/call-endpoint-from-ui.md +109 -0
  113. package/dist/docs/recipes/check-a-package-from-a-script.md +117 -0
  114. package/dist/docs/recipes/create-capability.md +66 -0
  115. package/dist/docs/recipes/create-endpoint.md +127 -0
  116. package/dist/docs/recipes/create-engine.md +111 -0
  117. package/dist/docs/recipes/create-game.md +70 -0
  118. package/dist/docs/recipes/create-guide.md +75 -0
  119. package/dist/docs/recipes/create-hook.md +80 -0
  120. package/dist/docs/recipes/create-presentation.md +142 -0
  121. package/dist/docs/recipes/create-scenario-editor.md +102 -0
  122. package/dist/docs/recipes/create-scenario.md +98 -0
  123. package/dist/docs/recipes/create-session-ui.md +116 -0
  124. package/dist/docs/recipes/create-settings.md +88 -0
  125. package/dist/docs/recipes/create-setup.md +115 -0
  126. package/dist/docs/recipes/create-tick-controller.md +65 -0
  127. package/dist/docs/recipes/create-user-interface.md +96 -0
  128. package/dist/docs/recipes/create-validator.md +67 -0
  129. package/dist/docs/recipes/create-waiting-room-ui.md +109 -0
  130. package/dist/docs/recipes/declare-api-uses.md +67 -0
  131. package/dist/docs/recipes/design-state-for-concurrent-writes.md +96 -0
  132. package/dist/docs/recipes/distribute-package.md +80 -0
  133. package/dist/docs/recipes/e2e-testing.md +116 -0
  134. package/dist/docs/recipes/end-game-on-condition.md +154 -0
  135. package/dist/docs/recipes/fix-duplicate-packages.md +101 -0
  136. package/dist/docs/recipes/index.md +86 -0
  137. package/dist/docs/recipes/make-an-endpoint-external.md +101 -0
  138. package/dist/docs/recipes/negotiate-across-teams.md +164 -0
  139. package/dist/docs/recipes/observe-other-teams-state.md +119 -0
  140. package/dist/docs/recipes/publish-a-package.md +139 -0
  141. package/dist/docs/recipes/remember-a-viewers-choice.md +55 -0
  142. package/dist/docs/recipes/review-a-past-day.md +73 -0
  143. package/dist/docs/recipes/share-assets-across-scenarios.md +120 -0
  144. package/dist/docs/recipes/share-logic-between-engine-and-ui.md +130 -0
  145. package/dist/docs/recipes/show-all-teams-participants.md +109 -0
  146. package/dist/docs/recipes/start-from-an-empty-package.md +169 -0
  147. package/dist/docs/recipes/take-a-file-in-an-endpoint.md +82 -0
  148. package/dist/docs/recipes/test-a-multi-role-game.md +51 -0
  149. package/dist/docs/recipes/test-user-interface.md +95 -0
  150. package/dist/docs/recipes/test-with-engine.md +115 -0
  151. package/dist/docs/recipes/upload-and-display-files.md +148 -0
  152. package/dist/docs/recipes/use-request-play.md +65 -0
  153. package/dist/docs/recipes/use-request-tick.md +75 -0
  154. package/dist/docs/troubleshooting.md +816 -0
  155. package/dist/execution/blob-inputs.d.ts +57 -0
  156. package/dist/execution/bundle-reload.d.ts +42 -0
  157. package/dist/execution/config-resolver.d.ts +127 -0
  158. package/dist/execution/configuration-builder.d.ts +66 -0
  159. package/dist/execution/context-composition.d.ts +161 -0
  160. package/dist/execution/endpoint-actions.d.ts +9 -0
  161. package/dist/execution/endpoint-payloads.d.ts +32 -0
  162. package/dist/execution/endpoint-runner.d.ts +129 -0
  163. package/dist/execution/engine-state.d.ts +40 -0
  164. package/dist/execution/executor-callbacks.d.ts +56 -0
  165. package/dist/execution/executor.d.ts +436 -0
  166. package/dist/execution/gates.d.ts +45 -0
  167. package/dist/execution/hook-chain.d.ts +25 -0
  168. package/dist/execution/logic-calls.d.ts +190 -0
  169. package/dist/execution/logic-failure.d.ts +84 -0
  170. package/dist/execution/logic-loader.d.ts +54 -0
  171. package/dist/execution/logic-result.d.ts +76 -0
  172. package/dist/execution/tick-controller-call.d.ts +32 -0
  173. package/dist/execution/types.d.ts +48 -0
  174. package/dist/generation/ts-type.d.ts +39 -0
  175. package/dist/guide/locale.d.ts +12 -0
  176. package/dist/guide/video-registry.d.ts +42 -0
  177. package/dist/help-examples.d.ts +125 -0
  178. package/dist/host/adapter.d.ts +67 -0
  179. package/dist/host/host-channel.d.ts +222 -0
  180. package/dist/host/index.d.ts +20 -0
  181. package/dist/host/protocol.d.ts +783 -0
  182. package/dist/host/surfaces.d.ts +42 -0
  183. package/dist/host/theme.d.ts +55 -0
  184. package/dist/host.js +583 -0
  185. package/dist/prompts/index.d.ts +7 -0
  186. package/dist/prompts/schema-builder.d.ts +29 -0
  187. package/dist/prompts/types.d.ts +108 -0
  188. package/dist/prompts/validators.d.ts +32 -0
  189. package/dist/runtime/capability-scope.d.ts +6 -0
  190. package/dist/runtime/context-merge.d.ts +29 -0
  191. package/dist/runtime/context-projection.d.ts +41 -0
  192. package/dist/runtime/guarded-callbacks.d.ts +15 -0
  193. package/dist/runtime/index.d.ts +27 -0
  194. package/dist/runtime/manifest-version.d.ts +55 -0
  195. package/dist/runtime/tick-validation.d.ts +25 -0
  196. package/dist/runtime.js +346 -0
  197. package/dist/schema.json +1546 -0
  198. package/dist/server/vite-plugin.d.ts +46 -0
  199. package/dist/services/tts/client.d.ts +51 -0
  200. package/dist/services/tts/elevenlabs.d.ts +24 -0
  201. package/dist/services/tts/types.d.ts +44 -0
  202. package/dist/storage/asset-resolver.d.ts +49 -0
  203. package/dist/storage/blob-store.d.ts +41 -0
  204. package/dist/templates/endpoint/logic.test.ts.liquid +61 -0
  205. package/dist/templates/endpoint/logic.ts.liquid +25 -0
  206. package/dist/templates/engine/init.json.liquid +7 -0
  207. package/dist/templates/engine/logic-aggregate.test.ts.liquid +90 -0
  208. package/dist/templates/engine/logic-aggregate.ts.liquid +46 -0
  209. package/dist/templates/engine/logic.test.ts.liquid +61 -0
  210. package/dist/templates/engine/logic.ts.liquid +32 -0
  211. package/dist/templates/engine/run.json.liquid +7 -0
  212. package/dist/templates/guide/01-at-a-glance.md.liquid +19 -0
  213. package/dist/templates/guide/02-preparation.md.liquid +18 -0
  214. package/dist/templates/guide/03-running-the-session.md.liquid +37 -0
  215. package/dist/templates/hook/logic.test.ts.liquid +46 -0
  216. package/dist/templates/hook/logic.ts.liquid +12 -0
  217. package/dist/templates/package/CLAUDE.md.template +338 -0
  218. package/dist/templates/package/DESIGN.md.liquid +144 -0
  219. package/dist/templates/package/README.md.liquid +143 -0
  220. package/dist/templates/package/claude/rules/workflow-create-capability.md +26 -0
  221. package/dist/templates/package/claude/rules/workflow-create-endpoint.md +35 -0
  222. package/dist/templates/package/claude/rules/workflow-create-engine.md +35 -0
  223. package/dist/templates/package/claude/rules/workflow-create-guide.md +40 -0
  224. package/dist/templates/package/claude/rules/workflow-create-presentation.md +37 -0
  225. package/dist/templates/package/claude/rules/workflow-create-user-interface.md +31 -0
  226. package/dist/templates/package/claude/rules/workflow-publish-package.md +54 -0
  227. package/dist/templates/package/claude/rules/workflow-test-engine-or-ui.md +30 -0
  228. package/dist/templates/package/claude/rules/workflow-update-sdk-version.md +43 -0
  229. package/dist/templates/package/claude/settings.json.template +16 -0
  230. package/dist/templates/package/gitignore.template +15 -0
  231. package/dist/templates/package/global-setup.ts.template +21 -0
  232. package/dist/templates/package/globals.d.ts.template +2 -0
  233. package/dist/templates/package/oxfmtrc.json.template +14 -0
  234. package/dist/templates/package/oxlintrc.json.template +238 -0
  235. package/dist/templates/package/package.json.liquid +65 -0
  236. package/dist/templates/package/playwright.config.ts.liquid +19 -0
  237. package/dist/templates/package/pnpm-workspace.yaml.template +27 -0
  238. package/dist/templates/package/test-setup.ts.liquid +7 -0
  239. package/dist/templates/package/tsconfig.json.liquid +15 -0
  240. package/dist/templates/package/vite.config.ts.liquid +19 -0
  241. package/dist/templates/package/vitest.config.ts.liquid +25 -0
  242. package/dist/templates/package/vitest.engine.config.ts.liquid +12 -0
  243. package/dist/templates/presentation/index.html.liquid +20 -0
  244. package/dist/templates/presentation/src/App.tsx.liquid +49 -0
  245. package/dist/templates/presentation/src/Slides.tsx.liquid +81 -0
  246. package/dist/templates/presentation/src/i18n.ts.liquid +54 -0
  247. package/dist/templates/presentation/src/main.tsx.liquid +14 -0
  248. package/dist/templates/scenario_editor/src/App.test.tsx.liquid +30 -0
  249. package/dist/templates/scenario_editor/src/App.tsx.liquid +95 -0
  250. package/dist/templates/scenario_editor/src/i18n.ts.liquid +44 -0
  251. package/dist/templates/session/src/App.test.tsx.liquid +60 -0
  252. package/dist/templates/session/src/App.tsx.liquid +128 -0
  253. package/dist/templates/session/src/i18n.ts.liquid +53 -0
  254. package/dist/templates/session/src/main.tsx.liquid +48 -0
  255. package/dist/templates/session/src/theme.ts.liquid +46 -0
  256. package/dist/templates/settings-editor/index.html.liquid +12 -0
  257. package/dist/templates/settings-editor/src/main.tsx.liquid +48 -0
  258. package/dist/templates/settings-editor/src/theme.ts.liquid +24 -0
  259. package/dist/templates/setup/src/App.test.tsx.liquid +39 -0
  260. package/dist/templates/setup/src/App.tsx.liquid +102 -0
  261. package/dist/templates/setup/src/i18n.ts.liquid +47 -0
  262. package/dist/templates/starter-game/capabilities/briefing/capability.sdk.json +18 -0
  263. package/dist/templates/starter-game/capabilities/plan/capability.sdk.json +17 -0
  264. package/dist/templates/starter-game/capabilities/results/capability.sdk.json +61 -0
  265. package/dist/templates/starter-game/capabilities/roles/capability.sdk.json +17 -0
  266. package/dist/templates/starter-game/capabilities/weather/capability.sdk.json +23 -0
  267. package/dist/templates/starter-game/engine/defaults.ts +81 -0
  268. package/dist/templates/starter-game/engine/engine.sdk.json +12 -0
  269. package/dist/templates/starter-game/engine/logic.test.ts +221 -0
  270. package/dist/templates/starter-game/engine/logic.ts +149 -0
  271. package/dist/templates/starter-game/engine/test-helpers.ts +74 -0
  272. package/dist/templates/starter-game/engine/types.ts +139 -0
  273. package/dist/templates/starter-game/guide/content/en/01-at-a-glance.md +51 -0
  274. package/dist/templates/starter-game/guide/content/en/02-preparation.md +35 -0
  275. package/dist/templates/starter-game/guide/content/en/03-running-the-day.md +58 -0
  276. package/dist/templates/starter-game/guide/guide.sdk.json +10 -0
  277. package/dist/templates/starter-game/guide/guide.test.ts +108 -0
  278. package/dist/templates/starter-game/hooks/assignproducts/defaults.ts +37 -0
  279. package/dist/templates/starter-game/hooks/assignproducts/hook.sdk.json +13 -0
  280. package/dist/templates/starter-game/hooks/assignproducts/logic/logic.test.ts +77 -0
  281. package/dist/templates/starter-game/hooks/assignproducts/logic/logic.ts +47 -0
  282. package/dist/templates/starter-game/hooks/assignproducts/types.ts +55 -0
  283. package/dist/templates/starter-game/package.json +68 -0
  284. package/dist/templates/starter-game/playwright.config.ts +19 -0
  285. package/dist/templates/starter-game/scenarios/heatwave/scenario.sdk.json +20 -0
  286. package/dist/templates/starter-game/scenarios/ordinary_week/scenario.sdk.json +12 -0
  287. package/dist/templates/starter-game/seriousgaming.sdk.json +23 -0
  288. package/dist/templates/starter-game/session/defaults.ts +80 -0
  289. package/dist/templates/starter-game/session/index.html +12 -0
  290. package/dist/templates/starter-game/session/session.sdk.json +11 -0
  291. package/dist/templates/starter-game/session/src/App.test.tsx +307 -0
  292. package/dist/templates/starter-game/session/src/App.tsx +309 -0
  293. package/dist/templates/starter-game/session/src/Section.tsx +37 -0
  294. package/dist/templates/starter-game/session/src/TeamDoor.tsx +96 -0
  295. package/dist/templates/starter-game/session/src/format.ts +8 -0
  296. package/dist/templates/starter-game/session/src/i18n.ts +51 -0
  297. package/dist/templates/starter-game/session/src/main.tsx +48 -0
  298. package/dist/templates/starter-game/session/src/theme.ts +45 -0
  299. package/dist/templates/starter-game/session/types.ts +135 -0
  300. package/dist/templates/starter-game/settings/engine/settings.sdk.json +50 -0
  301. package/dist/templates/starter-game/settings/menu/settings.sdk.json +78 -0
  302. package/dist/templates/starter-game/settings/session/settings.sdk.json +23 -0
  303. package/dist/templates/starter-game/settings/ui/settings.sdk.json +25 -0
  304. package/dist/templates/starter-game/test-setup.ts +7 -0
  305. package/dist/templates/starter-game/testing/e2e/game.spec.ts +139 -0
  306. package/dist/templates/starter-game/testing/e2e/global-setup.ts +21 -0
  307. package/dist/templates/starter-game/testing/engine/game.engine.test.ts +171 -0
  308. package/dist/templates/starter-game/tsconfig.json +19 -0
  309. package/dist/templates/starter-game/ui/defaults.ts +60 -0
  310. package/dist/templates/starter-game/ui/index.html +12 -0
  311. package/dist/templates/starter-game/ui/src/App.test.tsx +138 -0
  312. package/dist/templates/starter-game/ui/src/App.tsx +175 -0
  313. package/dist/templates/starter-game/ui/src/DayBar.test.tsx +78 -0
  314. package/dist/templates/starter-game/ui/src/DayBar.tsx +72 -0
  315. package/dist/templates/starter-game/ui/src/HistoryPanel.test.tsx +74 -0
  316. package/dist/templates/starter-game/ui/src/HistoryPanel.tsx +120 -0
  317. package/dist/templates/starter-game/ui/src/MakeRow.tsx +129 -0
  318. package/dist/templates/starter-game/ui/src/Section.tsx +37 -0
  319. package/dist/templates/starter-game/ui/src/ThemeToggle.tsx +49 -0
  320. package/dist/templates/starter-game/ui/src/TodayPanel.test.tsx +179 -0
  321. package/dist/templates/starter-game/ui/src/TodayPanel.tsx +92 -0
  322. package/dist/templates/starter-game/ui/src/YesterdayPanel.tsx +142 -0
  323. package/dist/templates/starter-game/ui/src/format.ts +12 -0
  324. package/dist/templates/starter-game/ui/src/i18n.ts +95 -0
  325. package/dist/templates/starter-game/ui/src/main.tsx +48 -0
  326. package/dist/templates/starter-game/ui/src/menu.ts +18 -0
  327. package/dist/templates/starter-game/ui/src/test-helpers.ts +58 -0
  328. package/dist/templates/starter-game/ui/src/theme.ts +45 -0
  329. package/dist/templates/starter-game/ui/src/writes.ts +43 -0
  330. package/dist/templates/starter-game/ui/types.ts +137 -0
  331. package/dist/templates/starter-game/ui/ui.sdk.json +11 -0
  332. package/dist/templates/starter-game/validators/capacity/defaults.ts +37 -0
  333. package/dist/templates/starter-game/validators/capacity/logic/logic.test.ts +96 -0
  334. package/dist/templates/starter-game/validators/capacity/logic/logic.ts +45 -0
  335. package/dist/templates/starter-game/validators/capacity/types.ts +55 -0
  336. package/dist/templates/starter-game/validators/capacity/validator.sdk.json +11 -0
  337. package/dist/templates/starter-game/vite.config.ts +19 -0
  338. package/dist/templates/starter-game/vitest.config.ts +15 -0
  339. package/dist/templates/starter-game/vitest.engine.config.ts +19 -0
  340. package/dist/templates/starter-game/waiting_room/defaults.ts +40 -0
  341. package/dist/templates/starter-game/waiting_room/index.html +12 -0
  342. package/dist/templates/starter-game/waiting_room/src/App.test.tsx +102 -0
  343. package/dist/templates/starter-game/waiting_room/src/App.tsx +194 -0
  344. package/dist/templates/starter-game/waiting_room/src/i18n.ts +33 -0
  345. package/dist/templates/starter-game/waiting_room/src/main.tsx +48 -0
  346. package/dist/templates/starter-game/waiting_room/src/theme.ts +45 -0
  347. package/dist/templates/starter-game/waiting_room/types.ts +59 -0
  348. package/dist/templates/starter-game/waiting_room/waiting_room.sdk.json +10 -0
  349. package/dist/templates/tick-controller/logic.ts.liquid +8 -0
  350. package/dist/templates/ui/index.html.liquid +12 -0
  351. package/dist/templates/ui/src/App.test.tsx.liquid +41 -0
  352. package/dist/templates/ui/src/App.tsx.liquid +103 -0
  353. package/dist/templates/ui/src/i18n.ts.liquid +47 -0
  354. package/dist/templates/ui/src/main.tsx.liquid +48 -0
  355. package/dist/templates/ui/src/theme.ts.liquid +46 -0
  356. package/dist/templates/validator/logic.test.ts.liquid +42 -0
  357. package/dist/templates/validator/logic.ts.liquid +21 -0
  358. package/dist/templates/waiting-room/index.html.liquid +12 -0
  359. package/dist/templates/waiting-room/src/App.test.tsx.liquid +57 -0
  360. package/dist/templates/waiting-room/src/App.tsx.liquid +118 -0
  361. package/dist/templates/waiting-room/src/i18n.ts.liquid +44 -0
  362. package/dist/templates/waiting-room/src/main.tsx.liquid +48 -0
  363. package/dist/templates/waiting-room/src/theme.ts.liquid +46 -0
  364. package/dist/templating/resource-metadata.d.ts +131 -0
  365. package/dist/types.d.ts +14 -0
  366. package/dist/utils/atomic-write.d.ts +40 -0
  367. package/dist/utils/cli-error.d.ts +64 -0
  368. package/dist/utils/document-parser-client.d.ts +20 -0
  369. package/dist/utils/env-loader.d.ts +40 -0
  370. package/dist/utils/error-codes.d.ts +335 -0
  371. package/dist/utils/errors.d.ts +19 -0
  372. package/dist/utils/global-settings.d.ts +65 -0
  373. package/dist/utils/llm-client.d.ts +34 -0
  374. package/dist/utils/logger.d.ts +36 -0
  375. package/dist/utils/output.d.ts +109 -0
  376. package/dist/utils/platform.d.ts +14 -0
  377. package/dist/utils/prompt.d.ts +36 -0
  378. package/dist/utils/real-path.d.ts +6 -0
  379. package/dist/utils/schema-properties.d.ts +35 -0
  380. package/dist/validation/ajv.d.ts +17 -0
  381. package/dist/validation/facilitator-guide-validator.d.ts +27 -0
  382. package/dist/validation/findings.d.ts +40 -0
  383. package/dist/validation/game-constraints.d.ts +10 -0
  384. package/dist/validation/game-validator.d.ts +34 -0
  385. package/dist/validation/package-validation.d.ts +41 -0
  386. package/dist/validation/package-validator.d.ts +19 -0
  387. package/dist/validation/resource-validator.d.ts +79 -0
  388. package/dist/validation/schema-defaults-validator.d.ts +29 -0
  389. package/dist/validation/schema-validation.d.ts +115 -0
  390. package/dist/validation/semver.d.ts +9 -0
  391. package/package.json +122 -3
@@ -0,0 +1,83 @@
1
+ /**
2
+ * What a client may ask the session to do, and what it hears back.
3
+ *
4
+ * One closed command set and one closed event set, shared by every client:
5
+ * the browser console, the CLI, and an agent driving it over HTTP. There is no
6
+ * second vocabulary for the CLI, which is what let the two drift before.
7
+ *
8
+ * The event set is small on purpose. The old protocol had seventeen event
9
+ * kinds, most of which were a slice of session state with its own payload
10
+ * shape: `context-updated`, `config-reloaded`, `initialized`,
11
+ * `participants-updated`, `waiting-room-changed`, `timer-updated`. They are all
12
+ * the same sentence, so there is one {@link StateEvent} carrying the whole
13
+ * projection, and a client renders from it. What remains distinct is what is
14
+ * genuinely not state: diagnostics, transient signals, and correlated replies.
15
+ */
16
+ import type { RunKind, ValidationRejection } from '@seriousgaming/sdk';
17
+ import type { Actor, Command, MutationCommand, QueryCommand, QueryResult, TeamScope, ViewerRole } from '@seriousgaming/sdk/commands';
18
+ import type { MutationResult, StateProjection } from '@seriousgaming/sdk/projection';
19
+ export type { Actor, Command, MutationCommand, QueryCommand, QueryResult, TeamScope, ViewerRole };
20
+ /** Whether a command reads rather than mutates, and so bypasses the queue. */
21
+ export declare function isQuery(command: Command): command is QueryCommand;
22
+ /**
23
+ * Whether a command, addressed to `run`, replaces the walkthrough rather than
24
+ * reaching it.
25
+ *
26
+ * A `reset` sent to the walkthrough is one: a walkthrough always starts at
27
+ * tick 0 with one team (§5.7), so rewinding it has nothing to offer over
28
+ * rebuilding it, and a rewind keeps the scenario, locale and configuration the
29
+ * run was first built on rather than following the session's current ones.
30
+ */
31
+ export declare function rebuildsWalkthrough(command: Command, run: RunKind): boolean;
32
+ /**
33
+ * The session's state, whole, after something changed it.
34
+ *
35
+ * One event rather than a family of partial ones. A client re-renders from the
36
+ * projection and never has to reconcile six differently-shaped updates into a
37
+ * picture of the same session.
38
+ */
39
+ export interface StateEvent {
40
+ type: 'state';
41
+ state: StateProjection;
42
+ /**
43
+ * Why it changed, for diagnostics and for a console that wants to say so.
44
+ * Never load-bearing: a client that ignores it still renders correctly.
45
+ */
46
+ cause: string;
47
+ }
48
+ /** A failure the operator should see (§14.5, and tick failures). */
49
+ export interface ErrorEvent {
50
+ type: 'error';
51
+ message: string;
52
+ /** Where it came from, e.g. `tick`, `hook:participantJoin`, `endpoint:narrate`. */
53
+ source: string;
54
+ teamId?: string;
55
+ }
56
+ /** Validators refused a UI-submitted write (§15). */
57
+ export interface ValidationRejectedEvent {
58
+ type: 'validation-rejected';
59
+ rejections: ValidationRejection[];
60
+ teamIndex?: number;
61
+ }
62
+ /**
63
+ * A round is being processed, so a surface can say so rather than appearing to
64
+ * have stopped responding (§12.4.4). Transient, and never state: a client that
65
+ * missed it is not wrong about the session.
66
+ */
67
+ export interface RoundProcessingEvent {
68
+ type: 'round-processing';
69
+ processing: boolean;
70
+ /** Absent for a cohort tick that genuinely covers every team. */
71
+ teamIndex?: number;
72
+ }
73
+ export type Event = StateEvent | ErrorEvent | ValidationRejectedEvent | RoundProcessingEvent;
74
+ export type { MutationResult };
75
+ /**
76
+ * Why this is not a command, or null when it is one.
77
+ *
78
+ * Deliberately names the field and what was expected. The failure this replaces
79
+ * was `{"type":"addParticipant","team":1}` being accepted for a command whose
80
+ * field is `teamId`, and then failing three steps later with nothing pointing
81
+ * back here.
82
+ */
83
+ export declare function commandError(value: unknown): string | null;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The scenario editor's unsaved edits (§6.5), one draft per scenario being
3
+ * edited.
4
+ *
5
+ * A draft belongs to no session. The dev server runs a session beside the
6
+ * editor, and the editor's edits used to land in that session's override
7
+ * layer, so editing a scenario quietly reconfigured the session and an
8
+ * endpoint the editor called read the session's setup edits as the draft.
9
+ * Kept here instead, each draft is only its own edits, sparse as the session
10
+ * layer is (§8.2), and resolved over the scenario it was opened on. It
11
+ * reaches a session only once it is saved as that scenario.
12
+ *
13
+ * Held by the session object because the dev server has one process and one
14
+ * place for state, not because a draft is part of the session: nothing a
15
+ * session does, a restart included, touches one.
16
+ */
17
+ import type { SessionSettingsOverrides } from '../execution/types';
18
+ export declare class ScenarioDrafts {
19
+ private readonly drafts;
20
+ /** Open a draft of a scenario, keeping one already open on it. */
21
+ open(scenarioId: string | null): void;
22
+ isOpen(scenarioId: string | null): boolean;
23
+ /** The unsaved edits to a scenario, or null when no editor is open on it. */
24
+ edits(scenarioId: string | null): SessionSettingsOverrides | null;
25
+ /** Fold an edit, already reduced to what changed, into an open draft. */
26
+ record(scenarioId: string | null, changed: Record<string, unknown>): void;
27
+ /** Empty a draft once it has been saved: the scenario now holds its edits. */
28
+ saved(scenarioId: string | null): void;
29
+ /** Drop the drafts of scenarios the package no longer declares. */
30
+ retain(scenarioIds: ReadonlySet<string>): void;
31
+ scenarioIds(): (string | null)[];
32
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Endpoint calls as the session serves them (§6.12): who may make one, what
3
+ * the handler is told about the session, and how its effects land.
4
+ *
5
+ * An endpoint is a read that may end in writes and actions, so it runs beside
6
+ * the mutation queue (§9.7) and only its effects go on it, as one job, checked
7
+ * first against the boards the handler read so that nothing lands on a board a
8
+ * tick replaced meanwhile. The caller's role and team are the host's
9
+ * bookkeeping, never the request's, and every call that changed something is
10
+ * kept in an audit, since afterwards an agent's change is otherwise
11
+ * indistinguishable from the game's own. Running the handler itself is the
12
+ * executor's.
13
+ */
14
+ import type { EndpointAuditEntry } from '@seriousgaming/sdk/projection';
15
+ import type { QueryCommand, QueryResult } from './protocol';
16
+ import type { SessionHost } from './session-host';
17
+ type CallEndpoint = Extract<QueryCommand, {
18
+ type: 'callEndpoint';
19
+ }>;
20
+ type EndpointHost = Pick<SessionHost, 'executor' | 'state' | 'participants' | 'faults' | 'clock' | 'timers' | 'pendingChanges' | 'mode' | 'settings' | 'scenarioBeingEdited' | 'waitingRoomOpen' | 'positionOf' | 'indexOfTeamId' | 'teamAt' | 'roster' | 'declaredSurface' | 'enqueue' | 'apply'>;
21
+ export declare class SessionEndpoints {
22
+ private readonly host;
23
+ private readonly audit;
24
+ constructor(host: EndpointHost);
25
+ /** Endpoint calls that changed something, oldest first (§6.12). */
26
+ auditLog(): readonly EndpointAuditEntry[];
27
+ /**
28
+ * Answer an endpoint call. A failure is an answer, so every transport
29
+ * carries it as it stands rather than flattening a throw into a message.
30
+ */
31
+ answer(command: CallEndpoint): Promise<QueryResult>;
32
+ /** The endpoints a caller may see: every one, or the published ones outside the game. */
33
+ list(external: boolean | undefined): QueryResult;
34
+ /**
35
+ * Run one endpoint call (§6.12): who may make it, the handler, its effects
36
+ * and, for an endpoint that answers in two phases, `Respond`. Throws on any
37
+ * failure; {@link answer} turns the throw into an `EndpointFailure`.
38
+ */
39
+ private call;
40
+ /** Whether the scenario editor made this call, which runs with no session (§6.5). */
41
+ private fromScenarioEditor;
42
+ /**
43
+ * A call from the scenario editor (§6.5, §6.12). The editor edits a
44
+ * scenario rather than a session, so the handler is given the settings as
45
+ * that scenario would resolve them, the draft included, and nothing of the
46
+ * session this host happens to be running beside it: no board, no team, no
47
+ * roster, and a caller with no session. The build refuses an editor that
48
+ * lists an endpoint needing more, but the dev server runs a package the
49
+ * build refuses, so the call is refused here as well.
50
+ */
51
+ private callFromEditor;
52
+ /**
53
+ * What a call with no session is told of one (§6.12): the game's mode and
54
+ * the tick limit the scenario being edited resolves to, and nothing played.
55
+ */
56
+ private sessionless;
57
+ /**
58
+ * Carry out what an endpoint returned (§6.12): its write, then its per-team
59
+ * writes, then its actions, as one job on the queue, so no other mutation
60
+ * lands between them and none of them lands on a board a tick replaced
61
+ * while the handler ran.
62
+ *
63
+ * Everything that can be checked without applying is checked before the
64
+ * job is queued, so a bad team id or an undeclared action applies nothing. What cannot be checked in advance, such as an engine
65
+ * failing the tick an action asked for, stops the sequence where it fails:
66
+ * the steps before it have landed, and the caller is told how many.
67
+ */
68
+ private applyEffects;
69
+ /**
70
+ * The session command an endpoint's action stands for (§6.12), with its
71
+ * team resolved. An omitted `teamId` is the call's own team on a
72
+ * team-scoped endpoint and the whole session on a session-scoped one.
73
+ */
74
+ private actionCommand;
75
+ /**
76
+ * Keep a record of an endpoint call that changed something (§6.12): which
77
+ * endpoint, who called it, and what it changed. An agent's change is
78
+ * otherwise indistinguishable from the game's own after the fact.
79
+ */
80
+ private record;
81
+ /**
82
+ * The team a team-scoped endpoint call runs for (§6.12): by index, else by
83
+ * id, else the session's only team.
84
+ *
85
+ * Refused when a session of several teams names none. The handler is
86
+ * promised a `team`, and the only board left to hand it is whichever the
87
+ * executor last worked with, which is one team's figures under no name.
88
+ */
89
+ private position;
90
+ /** Whether this endpoint declared anything that lives on a team board. */
91
+ private readsTeams;
92
+ /**
93
+ * Every team's board, for a session-scoped endpoint's non-shared reads
94
+ * (§6.12). Stable-index order, with the display name resolved as the §20.1
95
+ * projection resolves it: an endpoint commonly answers its caller in words,
96
+ * and it has no other route to a team's name.
97
+ */
98
+ private teams;
99
+ /** The ticks every board stands at now, for an endpoint's read (§6.12, 6a). */
100
+ private boardTicks;
101
+ /**
102
+ * Where the session stands, for an endpoint's `session` (§6.12). Status
103
+ * only: no team's context, so its cost is the team count and not the
104
+ * size of anybody's board.
105
+ */
106
+ private session;
107
+ }
108
+ export {};
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Who stands behind an action, and so whether the validators see it (§15).
3
+ *
4
+ * A validator gates a write or a tick request from a surface a participant or
5
+ * an observer is looking at, and nothing else: the CLI, the session surface,
6
+ * the engine, a hook and an endpoint are system-trusted. Telling those apart
7
+ * from a player surface that lost its participant is the whole difficulty
8
+ * (#566), so the attribution, the refusal of what cannot be attributed, and
9
+ * the putting of an attributed action to its gate live together here, where
10
+ * one reading of an actor serves both gates. Running the validators
11
+ * themselves is the executor's.
12
+ */
13
+ import type { ValidatorActor } from '@seriousgaming/sdk';
14
+ import type { GameContext } from '../execution/executor';
15
+ import type { ParticipantRecord } from './participant-registry';
16
+ import type { Actor, TeamScope } from './protocol';
17
+ import type { SessionHost } from './session-host';
18
+ type ActorHost = Pick<SessionHost, 'participants' | 'state'>;
19
+ type GateHost = Pick<SessionHost, 'executor' | 'participants' | 'state' | 'mode' | 'positionOf' | 'emit'>;
20
+ /**
21
+ * The participant behind a write, or `undefined` where there honestly is
22
+ * none.
23
+ *
24
+ * §15.1 gates validators on a write being participant-bearing, and three
25
+ * callers bear no participant legitimately: the CLI, a session surface,
26
+ * and an observer (§13.3). Absence on its own cannot say which, so a
27
+ * player surface whose slot the console had failed to resolve was read as
28
+ * one more of them, and its writes were applied unvalidated (#566).
29
+ *
30
+ * `viewerRole` is what separates them. A surface the host holds as a
31
+ * participant's, arriving with nobody on it, is a write the session cannot
32
+ * attribute, and an unattributable write is refused rather than trusted:
33
+ * the runtime cannot run an actor-aware check without an actor, so the
34
+ * honest outcome is to refuse rather than to skip.
35
+ *
36
+ * §13.2 step 3 is the same refusal reached from the other side, and is
37
+ * resolved here too. The team is the one the write names, or the one it
38
+ * would land on when it names none, so a write cannot walk past the check
39
+ * by omitting the team.
40
+ */
41
+ export declare function resolveActor(host: ActorHost, command: Actor, position: number): ParticipantRecord | undefined;
42
+ /**
43
+ * Who a validator is told submitted this write, or `undefined` for a write
44
+ * no validator sees.
45
+ *
46
+ * The test is the surface, not the participant (§15.1). An observer is
47
+ * rendered the same play surface a participant is, with the same controls
48
+ * on it, so its writes go to validators on the same terms and the game
49
+ * decides what an observer may do (§13.3). What stays ungated is a caller
50
+ * looking at something else: the session surface, the CLI, the engine, a
51
+ * hook, an endpoint. None of those carries a viewer role.
52
+ *
53
+ * The participant record is already the shape a validator wants (§13.1),
54
+ * so it is passed as it stands rather than rebuilt as a reduced copy.
55
+ */
56
+ export declare function submitterOf(host: ActorHost, command: Actor, position: number): ValidatorActor | undefined;
57
+ /**
58
+ * Put a participant's tick request to the validators that gate it (§15).
59
+ *
60
+ * Only a request from a surface a participant or observer is looking at
61
+ * names a participant or a viewer role, so a facilitator's tick, the CLI's
62
+ * and a timer's pass straight through, exactly as their writes do: pacing is the facilitator's to set, not to be
63
+ * held to. The validators judge the requesting team's board as it stands,
64
+ * which is where a gate such as a `MaxOpenWeek` the facilitator writes
65
+ * lives, and the tick the request would run.
66
+ */
67
+ export declare function gateTickRequest(host: GateHost, command: TeamScope & Actor): Promise<void>;
68
+ /**
69
+ * Put a write to the validators it could affect (§15), refusing it when any
70
+ * of them rejects. Reject-only, and only for a write from a surface a
71
+ * participant is looking at: an engine, endpoint or system-trusted mutation
72
+ * has no `submitter` and is not gated.
73
+ */
74
+ export declare function gateWrite(host: GateHost, before: GameContext, merged: GameContext, submitter: ValidatorActor | undefined, scope: TeamScope): Promise<void>;
75
+ export {};
@@ -0,0 +1,94 @@
1
+ /**
2
+ * What a session shows the parts it is built from.
3
+ *
4
+ * The session is split by concern (timers, endpoints, validator gates, writes,
5
+ * the roster, ticks and rewinds), and each part needs some of the session's
6
+ * state and some of its operations, never all of them. This is the one list
7
+ * of what may be reached, so each part declares the slice it uses as a `Pick`
8
+ * of it, and what one part can touch is read off its own signature rather
9
+ * than off the whole session. The session builds a single object satisfying
10
+ * it, from its own fields, and hands each part that.
11
+ */
12
+ import type { Participant, RunKind, TeamInfo } from '@seriousgaming/sdk';
13
+ import type { MutationResult, SessionMode } from '@seriousgaming/sdk/projection';
14
+ import type { GameContext, GameExecutor } from '../execution/executor';
15
+ import type { BlobRetention } from '../storage/blob-store';
16
+ import type { Clock } from './clock';
17
+ import type { FaultInjector } from './faults';
18
+ import type { ResolvedGameSettings } from './game-settings';
19
+ import type { ParticipantRegistry } from './participant-registry';
20
+ import type { PendingChanges } from './pending-changes';
21
+ import type { Event, MutationCommand } from './protocol';
22
+ import type { ParticipantCounts } from './session-roster';
23
+ import type { SurfaceBundle } from './session-surfaces';
24
+ import type { SessionTimers } from './session-timers';
25
+ import type { TeamStateStore } from './team-state-store';
26
+ /** A team-count range as a game declares it, either end optional (§5.4). */
27
+ export interface TeamBounds {
28
+ min?: number;
29
+ max?: number;
30
+ }
31
+ export interface SessionHost {
32
+ readonly executor: GameExecutor;
33
+ readonly state: TeamStateStore;
34
+ readonly participants: ParticipantRegistry;
35
+ readonly faults: FaultInjector;
36
+ readonly clock: Clock;
37
+ /** Which kind of run this is (§5.7), fixed for the session's life. */
38
+ readonly runKind: RunKind;
39
+ /** The automatic tick timers (§11), which ticks and rewinds re-evaluate. */
40
+ timers(): SessionTimers;
41
+ /** The teams and seeds the session was asked to start with. */
42
+ startingShape(): {
43
+ teams?: number;
44
+ participants?: ParticipantCounts;
45
+ };
46
+ locale(): string;
47
+ waitingRoomOpen(): boolean;
48
+ /** True while the game's `setup` slot still owes a Play (§8.3). */
49
+ inSetup(): boolean;
50
+ /** Edits on disk the session has not applied, as a copy (#640). */
51
+ pendingChanges(): PendingChanges;
52
+ mode(): SessionMode;
53
+ /** The `game` settings as they resolve now, through every layer (§6.10, §8.2). */
54
+ settings(): ResolvedGameSettings;
55
+ /**
56
+ * What a call from the scenario editor resolves against (§6.5, §6.12):
57
+ * the scenario it is editing, and its draft over that scenario's
58
+ * settings, the `game` settings included. Absent names the one in force.
59
+ */
60
+ scenarioBeingEdited(scenarioId: string | null | undefined): {
61
+ settings: Record<string, Record<string, unknown>>;
62
+ };
63
+ teamBounds(): TeamBounds;
64
+ tickLimit(): number | null;
65
+ /** Whether any tick has been committed and not since rewound. */
66
+ hasTicked(): boolean;
67
+ /** The position of a stable team index, or a refusal when it names no team. */
68
+ positionOf(teamIndex: number): number;
69
+ /** The stable index behind a team id, or undefined when it names no team. */
70
+ indexOfTeamId(teamId: string): number | undefined;
71
+ /** A team's display name as it reads now, a rename included (§5.4). */
72
+ teamNameAt(index: number): string;
73
+ /** The team at a position, as an endpoint is told it (§6.12). */
74
+ teamAt(position: number): TeamInfo;
75
+ /** Every participant, in stable-index order and then join order. */
76
+ roster(): Participant[];
77
+ declaredSurface(identifier: string): SurfaceBundle;
78
+ /** Build tick-0 state for every team, and seed the histories. */
79
+ buildInitialState(preserveShared?: GameContext): Promise<void>;
80
+ /** §14.4: re-fire `participantJoin` for everyone after a rebuild. */
81
+ replayParticipantJoins(): Promise<void>;
82
+ /** Start over from the package as it is on disk (#640). */
83
+ applyPendingChanges(): Promise<number>;
84
+ /** Delete this run's blobs of the given retentions (§16.2). */
85
+ clearBlobs(retentions: readonly BlobRetention[]): Promise<void>;
86
+ /** Queue `work` as one operation (§9.7, §10.2). */
87
+ enqueue<T>(work: () => Promise<T>): Promise<T>;
88
+ /** Run one command now, inside whatever job is already running. */
89
+ apply(command: MutationCommand): Promise<MutationResult>;
90
+ submit(command: MutationCommand): Promise<MutationResult>;
91
+ report(message: string, source: string, teamId?: string): void;
92
+ emit(event: Event): void;
93
+ emitState(cause: string): void;
94
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The ways a session operation says no, and how each reaches the caller.
3
+ *
4
+ * A refusal is an answer rather than a crash: nothing reached the session's
5
+ * state, and the outcome is the one every surface is told (§12.5.1). Two
6
+ * kinds carry more beside it for the author's tools, where game logic threw
7
+ * (#639) and which edits on disk refused it (#640), and they ride beside the
8
+ * outcome rather than in it so a surface sees the same shape either way.
9
+ * Shared by every part of the session that refuses, so that one refusal reads
10
+ * the same whichever operation raised it.
11
+ */
12
+ import type { MutationResult } from '@seriousgaming/sdk/projection';
13
+ import { type LogicFailure } from '../execution/logic-failure';
14
+ import type { FaultInjector, FaultKind } from './faults';
15
+ import { type PendingChanges } from './pending-changes';
16
+ /** The failure half of a settled mutation (§12.5.1). */
17
+ export type Refusal = Extract<MutationResult, {
18
+ ok: false;
19
+ }>['outcome'];
20
+ /**
21
+ * A failed result carrying where game logic threw, when it did (#639). Beside
22
+ * the outcome rather than in it: the outcome is what every surface is told,
23
+ * and a source path is for the author's tools alone.
24
+ */
25
+ export type WithLogicError<T> = T & {
26
+ logicError?: LogicFailure;
27
+ };
28
+ export declare function withLogicError<T extends object>(result: T, logicError: LogicFailure | undefined): WithLogicError<T>;
29
+ /** Nothing reached the session's own state; the caller sees why. */
30
+ export declare class RefusedError extends Error {
31
+ readonly outcome: Refusal;
32
+ constructor(message: string, outcome: Refusal);
33
+ }
34
+ /**
35
+ * A failed result carrying the edits on disk that refused it (#640). Beside
36
+ * the outcome for the same reason as {@link WithLogicError}: a surface is told
37
+ * it was denied, and the author's tools are told why in a form they can act on.
38
+ */
39
+ export type WithStale<T> = T & {
40
+ stale?: PendingChanges;
41
+ };
42
+ /** Refused because an edit on disk is pending, and running it would mix the two. */
43
+ export declare class StaleError extends RefusedError {
44
+ readonly pending: PendingChanges;
45
+ constructor(pending: PendingChanges);
46
+ }
47
+ export declare function withStale<T extends object>(result: T, error: unknown): WithStale<T>;
48
+ /** `error`, marked as thrown by game logic when `failure` says it was. */
49
+ export declare function markIfLogic(error: Error, failure: LogicFailure | undefined): unknown;
50
+ /**
51
+ * Fire an armed fault as the refusal it stands in for (§3.3 of the design).
52
+ *
53
+ * The point is that the surface takes the same path it would on the real
54
+ * thing, so the outcome has to be the real outcome rather than a marker.
55
+ */
56
+ export declare function refuseIfArmed(faults: FaultInjector, fault: FaultKind, outcome: Refusal): void;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Undo and reset (§9.4, §19.3): putting a team, or the whole session, back
3
+ * where it was.
4
+ *
5
+ * A rewind restores boards from history, and history holds whole boards, so
6
+ * left alone it would also restore what a rewind must never touch: the shared
7
+ * store other teams still read, and every capability declared
8
+ * `lifetime: 'session'` (§7.6). The rules for what a per-team rewind may
9
+ * reach, when one is refused outright because the cohort moves as one, and
10
+ * how session-lifetime values are carried across, sit here together so that
11
+ * undo and reset cannot come to disagree about them.
12
+ */
13
+ import type { TeamScope } from './protocol';
14
+ import type { SessionHost } from './session-host';
15
+ type RewindHost = Pick<SessionHost, 'executor' | 'state' | 'mode' | 'positionOf' | 'pendingChanges' | 'applyPendingChanges' | 'buildInitialState' | 'replayParticipantJoins' | 'clearBlobs'>;
16
+ /**
17
+ * Run an undo or a reset without letting it touch any `lifetime:
18
+ * 'session'` capability (§7.6).
19
+ *
20
+ * Those values ride the team contexts and the shared store like any other,
21
+ * so a restored snapshot or a rebuilt initial board would carry them back
22
+ * to what they were. Capturing them first and putting them back after is
23
+ * what keeps them out of the tick's reach: facilitator state such as
24
+ * 'setup completed' survives the rewind that would otherwise wipe it.
25
+ * A refused operation changed nothing, so there is nothing to put back.
26
+ */
27
+ export declare function keepingSessionLifetime(host: Pick<SessionHost, 'executor' | 'state'>, run: () => Promise<number>): Promise<number>;
28
+ /** Step back one round, for one team or the session (§9.4). Returns the tick it lands on. */
29
+ export declare function undo(host: RewindHost, scope: TeamScope): Promise<number>;
30
+ /** Return a team, or the session, to tick 0 (§9.4). */
31
+ export declare function reset(host: RewindHost, scope: TeamScope): Promise<number>;
32
+ export {};
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Who is in the session: its teams, the participants on them, and the hooks
3
+ * their arrivals and moves fire (§5.4, §5.6, §13, §14).
4
+ *
5
+ * The roster is declarative in a local session rather than the residue of a
6
+ * join flow, because a player surface is addressed directly and somebody has
7
+ * to exist for it to be. So the team count and seed counts are resolved here
8
+ * against the game's bounds, every team is filled to its seats, and the same
9
+ * rules decide when a team may be added or removed mid-session. Participant
10
+ * signals fire from here too, always after the change they describe, since
11
+ * every one of them is a consequence of a roster change and nothing else.
12
+ */
13
+ import type { Participant } from '@seriousgaming/sdk';
14
+ import type { ResolvedGameSettings } from './game-settings';
15
+ import type { ParticipantRecord } from './participant-registry';
16
+ import type { MutationCommand } from './protocol';
17
+ import type { SessionHost, TeamBounds } from './session-host';
18
+ /**
19
+ * How many participants to seed each team with: one count for every team, or
20
+ * one per team in stable index order.
21
+ */
22
+ export type ParticipantCounts = number | number[];
23
+ /**
24
+ * Why a participant count cannot seed `teams` teams, or null when it can.
25
+ *
26
+ * Zero is a count, not an error: every team is under-full at the start of a
27
+ * session by definition (§5.4), and in a hosted session every team starts
28
+ * empty and fills as people join.
29
+ */
30
+ export declare function participantCountsError(counts: ParticipantCounts, teams: number): string | null;
31
+ export declare function boundsOf({ teams }: ResolvedGameSettings): TeamBounds;
32
+ export declare function refuseTeamCountOutsideBounds(count: number, { min, max }: TeamBounds): void;
33
+ /**
34
+ * Refuse to start play with a team count outside the bounds the settings
35
+ * resolved to (§5.4). Setup can move them after the teams were created, so
36
+ * this is checked again as play starts, and the facilitator adds or removes
37
+ * teams, or changes the setting, before trying again.
38
+ */
39
+ export declare function refuseTeamCountAtPlay(count: number, { min, max }: TeamBounds): void;
40
+ type RosterHost = Pick<SessionHost, 'executor' | 'state' | 'participants' | 'faults' | 'runKind' | 'startingShape' | 'mode' | 'hasTicked' | 'teamBounds' | 'settings' | 'positionOf' | 'buildInitialState' | 'report'>;
41
+ export declare class SessionRoster {
42
+ private readonly host;
43
+ /** Seed counts chosen for one team, by stable index, over the session-wide one. */
44
+ private readonly seats;
45
+ constructor(host: RosterHost);
46
+ /** Every participant, in stable-index order and then join order. */
47
+ list(): Participant[];
48
+ /** Every participant's record, in stable-index order and then join order. */
49
+ records(): ParticipantRecord[];
50
+ /**
51
+ * Mark exactly these participants connected, and every other one not
52
+ * (§13.2). True when anybody's presence changed.
53
+ */
54
+ syncPresence(connected: ReadonlySet<string>): boolean;
55
+ /** Settle the team count and seed counts the session starts with (§5.4, §5.7). */
56
+ resolveTeamCount(): void;
57
+ /**
58
+ * Give every team the participants it is declared to have.
59
+ *
60
+ * The roster is declarative here, not the residue of a join flow: a player
61
+ * surface is addressed directly (`/team/2/player/1`) rather than joined, so
62
+ * somebody has to exist for it to be. A surface with no participant never
63
+ * receives `setParticipant`, which is one of the six messages a player
64
+ * surface blocks its first paint on (§12.6), so it sits on its loading
65
+ * state forever.
66
+ *
67
+ * Seeded to {@link seatsFor}, which may be none: a test of what a game does
68
+ * before anybody joins needs a team with nobody on it.
69
+ */
70
+ materialise(): Promise<void>;
71
+ /**
72
+ * §14.4: `participantJoin` re-fires for every registered participant
73
+ * whenever context is rebuilt, so a hook's contribution is not lost to a
74
+ * reset.
75
+ */
76
+ replayJoins(): Promise<void>;
77
+ /**
78
+ * §5.6. An independent team joins the session as it stands; a shared-state
79
+ * cohort cannot be enlarged, because `InitAggregate` ran over the teams
80
+ * present at initialisation and a late arrival's per-team state and share
81
+ * of the store cannot be retrofitted.
82
+ *
83
+ * An authoring host may relax that before the first tick by re-initialising,
84
+ * so the aggregate observes the enlarged cohort, and this is an authoring
85
+ * host. The relaxation is not part of the runtime contract: a production
86
+ * runtime refuses throughout, and the conformance suite asserts the refusal
87
+ * after a tick, where every reading agrees.
88
+ *
89
+ * 'Before the first tick' is read off the clock rather than latched, so a
90
+ * session returned to tick 0 by a reset can still be enlarged. The price is
91
+ * the same either way: re-initialisation rebuilds every team's context from
92
+ * `Init`, so anything written into the board beforehand goes with it. The
93
+ * configuration does not — it is an input to `Init`, not state.
94
+ */
95
+ addTeam(name?: string, participants?: number): Promise<undefined>;
96
+ /**
97
+ * §5.6. Independent mode only: a shared-state engine initialised over the
98
+ * whole cohort, so a team leaving it cannot be retrofitted, before the
99
+ * first tick or after. And only an empty team: participant records are
100
+ * permanent (§13.2), so a team holding one, connected or not, would leave
101
+ * it pointing at nothing.
102
+ */
103
+ removeTeam(teamIndex: number): Promise<undefined>;
104
+ renameTeam(teamId: string, name: string): undefined;
105
+ renameOwnTeam(command: Extract<MutationCommand, {
106
+ type: 'renameOwnTeam';
107
+ }>): undefined;
108
+ addParticipant(teamId: string, name?: string): Promise<undefined>;
109
+ renameParticipant(participantId: string, name: string): undefined;
110
+ moveParticipant(participantId: string, toTeamId: string): Promise<undefined>;
111
+ findParticipantTeam(participantId: string): string | undefined;
112
+ /**
113
+ * How many participants a team is seeded with.
114
+ *
115
+ * At least one unless somebody asked otherwise, and the game's declared
116
+ * `perTeam.minParticipants` where it has one, because that is the cohort
117
+ * the author designed for.
118
+ */
119
+ private seatsFor;
120
+ /**
121
+ * Run `signal`'s hooks against a team's context: the participant's own
122
+ * team unless `dispatch.teamId` names another, with any signal-specific
123
+ * fields in `dispatch.payload` handed to the hook beside the participant.
124
+ */
125
+ private fireHook;
126
+ private hasHook;
127
+ }
128
+ export {};
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The surfaces a package declares, as the session looks them up (§12).
3
+ *
4
+ * A surface's identifier arrives from the host, which names what it mounted,
5
+ * and every check the session makes against a surface (what it may read back,
6
+ * what it may write, which endpoints it may call) starts by finding its bundle.
7
+ * One lookup over every `kind: 'ui'` resource, so a new surface kind is found
8
+ * without being listed here by hand, and one refusal for an identifier the
9
+ * package does not declare, so no check can treat it as a lesser surface.
10
+ */
11
+ import type { ResolvedPackageManifest } from '@seriousgaming/sdk';
12
+ export type SurfaceBundle = NonNullable<ResolvedPackageManifest['ui' | 'session' | 'waiting_room' | 'presentation' | 'setup' | 'scenario_editor']>;
13
+ /**
14
+ * The UI bundle this package declares under `identifier`, of any kind.
15
+ *
16
+ * Every `kind: 'ui'` resource is searched, so a new surface kind is found
17
+ * without being added here by hand: one left out would be refused as a
18
+ * surface the package does not declare.
19
+ */
20
+ export declare function userInterfaceBundle(config: ResolvedPackageManifest | null, identifier: string): SurfaceBundle | undefined;
21
+ /**
22
+ * The surface a caller names, or a refusal. An identifier is the host's to
23
+ * supply from what it mounted, so one the package does not declare is not
24
+ * a lesser surface: nothing it may be held to exists, and it is refused.
25
+ */
26
+ export declare function declaredSurface(config: ResolvedPackageManifest | null, identifier: string): SurfaceBundle;
27
+ /**
28
+ * What a surface may read back: its declared reads and writes, less any
29
+ * private capability (§7.4). `undefined` for a caller that names no UI,
30
+ * which is system-trusted and unbounded.
31
+ */
32
+ export declare function readableCapabilities(config: ResolvedPackageManifest | null, uiIdentifier: string | undefined): Set<string> | undefined;