@crowdedkingdoms/crowdyjs 17.13.0-dev.1 → 18.0.0-dev.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 (284) hide show
  1. package/MIGRATION.md +163 -1
  2. package/README.md +142 -146
  3. package/dist/crowdy-client.d.ts +12 -31
  4. package/dist/crowdy-client.d.ts.map +1 -1
  5. package/dist/crowdy-client.js +15 -37
  6. package/dist/crowdy-dsh/bridge.d.ts.map +1 -1
  7. package/dist/crowdy-dsh/bridge.js +3 -6
  8. package/dist/crowdy-studio/controller.d.ts +59 -57
  9. package/dist/crowdy-studio/controller.d.ts.map +1 -1
  10. package/dist/crowdy-studio/controller.js +268 -206
  11. package/dist/crowdy-studio/diagnostics.d.ts +3 -12
  12. package/dist/crowdy-studio/diagnostics.d.ts.map +1 -1
  13. package/dist/crowdy-studio/dom-shell.d.ts +2 -4
  14. package/dist/crowdy-studio/dom-shell.d.ts.map +1 -1
  15. package/dist/crowdy-studio/dom-shell.js +16 -47
  16. package/dist/crowdy-studio/embed/hud-layer.d.ts +1 -1
  17. package/dist/crowdy-studio/embed/panel.d.ts +3 -12
  18. package/dist/crowdy-studio/embed/panel.d.ts.map +1 -1
  19. package/dist/crowdy-studio/embed/panel.js +3 -6
  20. package/dist/crowdy-studio/index.d.ts +1 -3
  21. package/dist/crowdy-studio/index.d.ts.map +1 -1
  22. package/dist/crowdy-studio/index.js +0 -2
  23. package/dist/crowdy-studio/models.d.ts +3 -4
  24. package/dist/crowdy-studio/models.d.ts.map +1 -1
  25. package/dist/crowdy-studio/starter-projects.d.ts +8 -4
  26. package/dist/crowdy-studio/starter-projects.d.ts.map +1 -1
  27. package/dist/crowdy-studio/starter-projects.js +83 -100
  28. package/dist/domains/appAccess.d.ts +14 -1
  29. package/dist/domains/appAccess.d.ts.map +1 -1
  30. package/dist/domains/appAccess.js +37 -1
  31. package/dist/domains/exec.d.ts +162 -15
  32. package/dist/domains/exec.d.ts.map +1 -1
  33. package/dist/domains/exec.js +180 -18
  34. package/dist/domains/host.d.ts +3 -3
  35. package/dist/domains/host.js +3 -3
  36. package/dist/domains/marketplace.d.ts +9 -58
  37. package/dist/domains/marketplace.d.ts.map +1 -1
  38. package/dist/domains/marketplace.js +9 -106
  39. package/dist/domains/playerWallet.d.ts +6 -12
  40. package/dist/domains/playerWallet.d.ts.map +1 -1
  41. package/dist/domains/playerWallet.js +6 -21
  42. package/dist/errors.d.ts +7 -13
  43. package/dist/errors.d.ts.map +1 -1
  44. package/dist/errors.js +6 -10
  45. package/dist/generated/graphql.d.ts +1384 -7977
  46. package/dist/generated/graphql.d.ts.map +1 -1
  47. package/dist/generated/graphql.js +22 -321
  48. package/dist/grid-mods/exec-client-halves.d.ts +156 -0
  49. package/dist/grid-mods/exec-client-halves.d.ts.map +1 -0
  50. package/dist/grid-mods/exec-client-halves.js +356 -0
  51. package/dist/grid-mods/grid-host-calls.d.ts +29 -15
  52. package/dist/grid-mods/grid-host-calls.d.ts.map +1 -1
  53. package/dist/grid-mods/grid-host-calls.js +40 -44
  54. package/dist/grid-mods/index.d.ts +1 -0
  55. package/dist/grid-mods/index.d.ts.map +1 -1
  56. package/dist/grid-mods/index.js +1 -0
  57. package/dist/grid-mods/mod-runtime.d.ts +21 -6
  58. package/dist/grid-mods/mod-runtime.d.ts.map +1 -1
  59. package/dist/grid-mods/mod-runtime.js +6 -2
  60. package/dist/grid-scope.d.ts +0 -188
  61. package/dist/grid-scope.d.ts.map +1 -1
  62. package/dist/grid-scope.js +0 -69
  63. package/dist/index.d.ts +12 -18
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +11 -16
  66. package/dist/kit/actions.d.ts +5 -9
  67. package/dist/kit/actions.d.ts.map +1 -1
  68. package/dist/kit/actions.js +5 -9
  69. package/dist/kit/index.d.ts +2 -33
  70. package/dist/kit/index.d.ts.map +1 -1
  71. package/dist/kit/index.js +1 -32
  72. package/dist/kit/kit.d.ts +8 -208
  73. package/dist/kit/kit.d.ts.map +1 -1
  74. package/dist/kit/kit.js +5 -163
  75. package/dist/kit/social.d.ts +1 -3
  76. package/dist/kit/social.d.ts.map +1 -1
  77. package/dist/kit/social.js +1 -3
  78. package/dist/kit/wire.d.ts +16 -5
  79. package/dist/kit/wire.d.ts.map +1 -1
  80. package/dist/kit/wire.js +19 -5
  81. package/dist/live-coding/assets/browser-authoring-index.json +1 -1
  82. package/dist/live-coding/assets/manifest.json +1 -1
  83. package/dist/live-coding/browser-authoring-index.generated.d.ts.map +1 -1
  84. package/dist/live-coding/browser-authoring-index.generated.js +1496 -4386
  85. package/dist/live-coding/rust-analysis.d.ts.map +1 -1
  86. package/dist/live-coding/rust-analysis.js +31 -23
  87. package/dist/player-runtime/client-host-calls.d.ts +8 -0
  88. package/dist/player-runtime/client-host-calls.d.ts.map +1 -0
  89. package/dist/player-runtime/client-host-calls.js +28 -0
  90. package/dist/player-runtime/glue-runtime.d.ts +27 -10
  91. package/dist/player-runtime/glue-runtime.d.ts.map +1 -1
  92. package/dist/player-runtime/glue-runtime.js +40 -45
  93. package/dist/player-runtime/glue-sab.d.ts +2 -2
  94. package/dist/player-runtime/glue-sab.js +2 -2
  95. package/dist/player-runtime/player-code-broker.d.ts +80 -24
  96. package/dist/player-runtime/player-code-broker.d.ts.map +1 -1
  97. package/dist/player-runtime/player-code-broker.js +169 -41
  98. package/dist/player-runtime/player-glue-worker.d.ts +8 -8
  99. package/dist/player-runtime/player-glue-worker.d.ts.map +1 -1
  100. package/dist/player-runtime/player-glue-worker.js +26 -9
  101. package/dist/stores/actors.d.ts +12 -2
  102. package/dist/stores/actors.d.ts.map +1 -1
  103. package/dist/stores/actors.js +43 -18
  104. package/dist/stores/chunks.d.ts +12 -0
  105. package/dist/stores/chunks.d.ts.map +1 -1
  106. package/dist/stores/chunks.js +112 -39
  107. package/dist/stores/durable.d.ts.map +1 -1
  108. package/dist/stores/durable.js +2 -1
  109. package/dist/stores/inbox.d.ts +1 -1
  110. package/dist/stores/inbox.d.ts.map +1 -1
  111. package/dist/stores/index.d.ts +0 -11
  112. package/dist/stores/index.d.ts.map +1 -1
  113. package/dist/stores/index.js +0 -4
  114. package/dist/stores/session.d.ts +0 -2
  115. package/dist/stores/session.d.ts.map +1 -1
  116. package/package.json +1 -1
  117. package/dist/crowdy-studio/model-lint-log.d.ts +0 -76
  118. package/dist/crowdy-studio/model-lint-log.d.ts.map +0 -1
  119. package/dist/crowdy-studio/model-lint-log.js +0 -93
  120. package/dist/crowdy-studio/model-lint.d.ts +0 -61
  121. package/dist/crowdy-studio/model-lint.d.ts.map +0 -1
  122. package/dist/crowdy-studio/model-lint.js +0 -45
  123. package/dist/domains/compute.d.ts +0 -208
  124. package/dist/domains/compute.d.ts.map +0 -1
  125. package/dist/domains/compute.js +0 -274
  126. package/dist/domains/controlPlane.d.ts +0 -38
  127. package/dist/domains/controlPlane.d.ts.map +0 -1
  128. package/dist/domains/controlPlane.js +0 -43
  129. package/dist/domains/gameModel.d.ts +0 -1174
  130. package/dist/domains/gameModel.d.ts.map +0 -1
  131. package/dist/domains/gameModel.js +0 -1333
  132. package/dist/domains/playerCompute.d.ts +0 -91
  133. package/dist/domains/playerCompute.d.ts.map +0 -1
  134. package/dist/domains/playerCompute.js +0 -136
  135. package/dist/domains/playerModel.d.ts +0 -20
  136. package/dist/domains/playerModel.d.ts.map +0 -1
  137. package/dist/domains/playerModel.js +0 -54
  138. package/dist/kit/abilities.d.ts +0 -66
  139. package/dist/kit/abilities.d.ts.map +0 -1
  140. package/dist/kit/abilities.js +0 -71
  141. package/dist/kit/blueprints/combat.d.ts +0 -81
  142. package/dist/kit/blueprints/combat.d.ts.map +0 -1
  143. package/dist/kit/blueprints/combat.js +0 -357
  144. package/dist/kit/blueprints/core.d.ts +0 -222
  145. package/dist/kit/blueprints/core.d.ts.map +0 -1
  146. package/dist/kit/blueprints/core.js +0 -185
  147. package/dist/kit/blueprints/decks.d.ts +0 -50
  148. package/dist/kit/blueprints/decks.d.ts.map +0 -1
  149. package/dist/kit/blueprints/decks.js +0 -163
  150. package/dist/kit/blueprints/economy.d.ts +0 -88
  151. package/dist/kit/blueprints/economy.d.ts.map +0 -1
  152. package/dist/kit/blueprints/economy.js +0 -533
  153. package/dist/kit/blueprints/guild.d.ts +0 -51
  154. package/dist/kit/blueprints/guild.d.ts.map +0 -1
  155. package/dist/kit/blueprints/guild.js +0 -50
  156. package/dist/kit/blueprints/index.d.ts +0 -23
  157. package/dist/kit/blueprints/index.d.ts.map +0 -1
  158. package/dist/kit/blueprints/index.js +0 -22
  159. package/dist/kit/blueprints/inventory.d.ts +0 -84
  160. package/dist/kit/blueprints/inventory.d.ts.map +0 -1
  161. package/dist/kit/blueprints/inventory.js +0 -257
  162. package/dist/kit/blueprints/leaderboards.d.ts +0 -49
  163. package/dist/kit/blueprints/leaderboards.d.ts.map +0 -1
  164. package/dist/kit/blueprints/leaderboards.js +0 -126
  165. package/dist/kit/blueprints/liveops.d.ts +0 -42
  166. package/dist/kit/blueprints/liveops.d.ts.map +0 -1
  167. package/dist/kit/blueprints/liveops.js +0 -170
  168. package/dist/kit/blueprints/locks.d.ts +0 -82
  169. package/dist/kit/blueprints/locks.d.ts.map +0 -1
  170. package/dist/kit/blueprints/locks.js +0 -155
  171. package/dist/kit/blueprints/loot.d.ts +0 -90
  172. package/dist/kit/blueprints/loot.d.ts.map +0 -1
  173. package/dist/kit/blueprints/loot.js +0 -229
  174. package/dist/kit/blueprints/matches.d.ts +0 -84
  175. package/dist/kit/blueprints/matches.d.ts.map +0 -1
  176. package/dist/kit/blueprints/matches.js +0 -349
  177. package/dist/kit/blueprints/moderation.d.ts +0 -32
  178. package/dist/kit/blueprints/moderation.d.ts.map +0 -1
  179. package/dist/kit/blueprints/moderation.js +0 -140
  180. package/dist/kit/blueprints/npcs.d.ts +0 -78
  181. package/dist/kit/blueprints/npcs.d.ts.map +0 -1
  182. package/dist/kit/blueprints/npcs.js +0 -125
  183. package/dist/kit/blueprints/plots.d.ts +0 -63
  184. package/dist/kit/blueprints/plots.d.ts.map +0 -1
  185. package/dist/kit/blueprints/plots.js +0 -151
  186. package/dist/kit/blueprints/progression.d.ts +0 -68
  187. package/dist/kit/blueprints/progression.d.ts.map +0 -1
  188. package/dist/kit/blueprints/progression.js +0 -324
  189. package/dist/kit/blueprints/quests.d.ts +0 -72
  190. package/dist/kit/blueprints/quests.d.ts.map +0 -1
  191. package/dist/kit/blueprints/quests.js +0 -285
  192. package/dist/kit/blueprints/worldsim.d.ts +0 -91
  193. package/dist/kit/blueprints/worldsim.d.ts.map +0 -1
  194. package/dist/kit/blueprints/worldsim.js +0 -468
  195. package/dist/kit/combat.d.ts +0 -141
  196. package/dist/kit/combat.d.ts.map +0 -1
  197. package/dist/kit/combat.js +0 -196
  198. package/dist/kit/decks.d.ts +0 -157
  199. package/dist/kit/decks.d.ts.map +0 -1
  200. package/dist/kit/decks.js +0 -186
  201. package/dist/kit/director.d.ts +0 -88
  202. package/dist/kit/director.d.ts.map +0 -1
  203. package/dist/kit/director.js +0 -86
  204. package/dist/kit/economy.d.ts +0 -289
  205. package/dist/kit/economy.d.ts.map +0 -1
  206. package/dist/kit/economy.js +0 -427
  207. package/dist/kit/engine.d.ts +0 -48
  208. package/dist/kit/engine.d.ts.map +0 -1
  209. package/dist/kit/engine.js +0 -113
  210. package/dist/kit/features.d.ts +0 -60
  211. package/dist/kit/features.d.ts.map +0 -1
  212. package/dist/kit/features.js +0 -65
  213. package/dist/kit/instances.d.ts +0 -52
  214. package/dist/kit/instances.d.ts.map +0 -1
  215. package/dist/kit/instances.js +0 -60
  216. package/dist/kit/inventory.d.ts +0 -129
  217. package/dist/kit/inventory.d.ts.map +0 -1
  218. package/dist/kit/inventory.js +0 -200
  219. package/dist/kit/leaderboards.d.ts +0 -86
  220. package/dist/kit/leaderboards.d.ts.map +0 -1
  221. package/dist/kit/leaderboards.js +0 -133
  222. package/dist/kit/liveops.d.ts +0 -112
  223. package/dist/kit/liveops.d.ts.map +0 -1
  224. package/dist/kit/liveops.js +0 -183
  225. package/dist/kit/loot.d.ts +0 -128
  226. package/dist/kit/loot.d.ts.map +0 -1
  227. package/dist/kit/loot.js +0 -160
  228. package/dist/kit/matches.d.ts +0 -301
  229. package/dist/kit/matches.d.ts.map +0 -1
  230. package/dist/kit/matches.js +0 -458
  231. package/dist/kit/matchmaking.d.ts +0 -57
  232. package/dist/kit/matchmaking.d.ts.map +0 -1
  233. package/dist/kit/matchmaking.js +0 -59
  234. package/dist/kit/minigames.d.ts +0 -30
  235. package/dist/kit/minigames.d.ts.map +0 -1
  236. package/dist/kit/minigames.js +0 -38
  237. package/dist/kit/mobs.d.ts +0 -90
  238. package/dist/kit/mobs.d.ts.map +0 -1
  239. package/dist/kit/mobs.js +0 -98
  240. package/dist/kit/moderation.d.ts +0 -73
  241. package/dist/kit/moderation.d.ts.map +0 -1
  242. package/dist/kit/moderation.js +0 -103
  243. package/dist/kit/movement.d.ts +0 -78
  244. package/dist/kit/movement.d.ts.map +0 -1
  245. package/dist/kit/movement.js +0 -103
  246. package/dist/kit/notifications.d.ts +0 -45
  247. package/dist/kit/notifications.d.ts.map +0 -1
  248. package/dist/kit/notifications.js +0 -67
  249. package/dist/kit/npcs.d.ts +0 -226
  250. package/dist/kit/npcs.d.ts.map +0 -1
  251. package/dist/kit/npcs.js +0 -141
  252. package/dist/kit/objects.d.ts +0 -116
  253. package/dist/kit/objects.d.ts.map +0 -1
  254. package/dist/kit/objects.js +0 -119
  255. package/dist/kit/pets.d.ts +0 -85
  256. package/dist/kit/pets.d.ts.map +0 -1
  257. package/dist/kit/pets.js +0 -96
  258. package/dist/kit/plots.d.ts +0 -85
  259. package/dist/kit/plots.d.ts.map +0 -1
  260. package/dist/kit/plots.js +0 -113
  261. package/dist/kit/progression.d.ts +0 -181
  262. package/dist/kit/progression.d.ts.map +0 -1
  263. package/dist/kit/progression.js +0 -265
  264. package/dist/kit/quests.d.ts +0 -171
  265. package/dist/kit/quests.d.ts.map +0 -1
  266. package/dist/kit/quests.js +0 -237
  267. package/dist/kit/racing.d.ts +0 -85
  268. package/dist/kit/racing.d.ts.map +0 -1
  269. package/dist/kit/racing.js +0 -99
  270. package/dist/kit/shared.d.ts +0 -73
  271. package/dist/kit/shared.d.ts.map +0 -1
  272. package/dist/kit/shared.js +0 -101
  273. package/dist/kit/telemetry.d.ts +0 -66
  274. package/dist/kit/telemetry.d.ts.map +0 -1
  275. package/dist/kit/telemetry.js +0 -133
  276. package/dist/kit/territory.d.ts +0 -97
  277. package/dist/kit/territory.d.ts.map +0 -1
  278. package/dist/kit/territory.js +0 -114
  279. package/dist/kit/worldsim.d.ts +0 -263
  280. package/dist/kit/worldsim.d.ts.map +0 -1
  281. package/dist/kit/worldsim.js +0 -268
  282. package/dist/stores/model.d.ts +0 -81
  283. package/dist/stores/model.d.ts.map +0 -1
  284. package/dist/stores/model.js +0 -163
@@ -0,0 +1,156 @@
1
+ import type { ExecAPI, ExecClientCapabilitySummary, ExecGridClientMod } from '../domains/exec.js';
2
+ import { type PlayerCodeBrokerOptions, type PlayerCodeGridBounds, type PlayerCodeHostCall, type PlayerCodeLogLine, type PlayerCodePresentation } from '../player-runtime/player-code-broker.js';
3
+ /** The grid a player stands in: its id and chunk box. */
4
+ export type ExecClientHalvesGrid = PlayerCodeGridBounds & {
5
+ gridId: string;
6
+ };
7
+ /**
8
+ * One question for the player. `author`: trust every CLIENT half of `authorId` on the grid, at
9
+ * the hash of their union (`exec.trustAuthor`). `mod`: consent to one CLIENT half at its hash
10
+ * (`exec.consentClientMod`).
11
+ */
12
+ export interface ExecClientHalfPrompt {
13
+ kind: 'author' | 'mod';
14
+ gridId: string;
15
+ authorId: string;
16
+ /** What a yes consents to: the author's `authorCapabilityHash`, or the mod's `capabilityHash`. */
17
+ capabilityHash: string;
18
+ /** The summary to show: the author's union, or the one CLIENT half's. */
19
+ capabilitySummaryJson: string;
20
+ capabilitySummary: ExecClientCapabilitySummary | null;
21
+ /** The CLIENT halves a yes covers now. */
22
+ mods: readonly ExecGridClientMod[];
23
+ }
24
+ /**
25
+ * Why a CLIENT half stopped: no longer listed (`removed`), a new digest, capability hash or tick
26
+ * interval (`changed`), listed without the player's consent or trust (`unconsented`), refused by
27
+ * `filter` (`filtered`), the runner left the grid (`left-grid`) or was stopped (`stopped`), or
28
+ * the broker's circuit breaker tripped (`circuit-open`).
29
+ */
30
+ export type ExecClientHalfStopReason = 'removed' | 'changed' | 'unconsented' | 'filtered' | 'left-grid' | 'stopped' | 'circuit-open';
31
+ /** A consent, fetch or start that failed, and when that CLIENT half is tried again. */
32
+ export interface ExecClientHalfError {
33
+ mod: ExecGridClientMod;
34
+ stage: 'consent' | 'fetch' | 'start';
35
+ /**
36
+ * `not-found`: not served to the player now (every refusal of the artifact, and a trust while
37
+ * not standing in the grid); `rate-limited`: 12 fetches a minute per player and mod;
38
+ * `conflict`: the hash moved on; `refused`: the served module was, for bytes that differ from
39
+ * its digest, an unknown CLIENT ABI or a capability summary that does not parse; `failed`:
40
+ * anything else.
41
+ */
42
+ reason: 'not-found' | 'rate-limited' | 'conflict' | 'refused' | 'failed';
43
+ error: unknown;
44
+ /** When the runner fetches or starts it again (epoch ms); absent for a consent. */
45
+ retryAt?: number;
46
+ }
47
+ /** The part of `PlayerCodeBroker` the runner drives. */
48
+ export interface ExecClientHalfBroker {
49
+ start(artifact: ArrayBuffer): Promise<void>;
50
+ stop(): void;
51
+ /** `PlayerCodeBroker.invoke`: the module's `handle_invoke`. */
52
+ invoke?(payload: Uint8Array, options?: {
53
+ timeoutMs?: number;
54
+ }): Promise<Uint8Array>;
55
+ }
56
+ export interface ExecClientHalvesOptions {
57
+ exec: Pick<ExecAPI, 'gridClientMods' | 'consentClientMod' | 'trustAuthor' | 'modClientArtifactBytes'>;
58
+ appId: string;
59
+ /** The platform glue worker (`@crowdedkingdoms/crowdyjs/player-glue-worker`), same origin. */
60
+ workerUrl: string | URL;
61
+ /**
62
+ * Answers the host calls a CLIENT half makes that the broker does not answer itself, for
63
+ * example with `createGridHostCalls` over the grid's scope.
64
+ */
65
+ onHostCall: (call: PlayerCodeHostCall, mod: ExecGridClientMod) => Promise<unknown>;
66
+ onPresentation?: (presentation: PlayerCodePresentation, mod: ExecGridClientMod) => void;
67
+ /**
68
+ * Each CLIENT half's `crowdy::log` lines, bounded by the broker (see
69
+ * `PlayerCodeBrokerOptions.onLog`). The text is the mod author's: render it as text.
70
+ */
71
+ onLog?: (line: PlayerCodeLogLine, mod: ExecGridClientMod) => void;
72
+ /**
73
+ * Asks the player about CLIENT halves they neither consented to nor trust the author of; true
74
+ * consents. Without it, only what the player already agreed to runs. A declined question is
75
+ * not asked again on this grid until its hash changes.
76
+ */
77
+ confirm?: (prompt: ExecClientHalfPrompt) => boolean | Promise<boolean>;
78
+ /** `author` (the default): one question per author; `mod`: one per CLIENT half. */
79
+ ask?: 'author' | 'mod';
80
+ /** Leaves out the CLIENT halves it returns false for, e.g. the one Crowdy Studio previews. */
81
+ filter?: (mod: ExecGridClientMod) => boolean;
82
+ onStarted?: (mod: ExecGridClientMod) => void;
83
+ onStopped?: (mod: ExecGridClientMod, reason: ExecClientHalfStopReason) => void;
84
+ onError?: (error: ExecClientHalfError) => void;
85
+ /** How long a CLIENT half waits after `NOT_FOUND` before it is fetched again. Default 15 s. */
86
+ notFoundRetryMs?: number;
87
+ /** How long after `RATE_LIMITED`. Default 60 s, the limit's window. */
88
+ rateLimitedRetryMs?: number;
89
+ /** How long after any other failure, refused bytes included. Default 60 s. */
90
+ failedRetryMs?: number;
91
+ /** Modules kept by digest, across grids. Default 16. */
92
+ cacheSize?: number;
93
+ now?: () => number;
94
+ brokerFactory?: (options: PlayerCodeBrokerOptions) => ExecClientHalfBroker;
95
+ }
96
+ /**
97
+ * Runs the CLIENT halves of ck-exec mods that the grid a player stands in serves, the way a game
98
+ * should: tell it the grid ({@link enterGrid}) and call {@link refresh} on a cadence (every 10 s
99
+ * or so). Each refresh lists the grid's CLIENT halves (`exec.gridClientMods`), stops the workers
100
+ * whose mod is gone or whose `modId`, `digest`, `capabilityHash` or tick interval changed, asks
101
+ * the player once per author (or per CLIENT half) about the rest, fetches each consented one
102
+ * (`exec.modClientArtifactBytes`, which refuses bytes that differ from their digest) and runs it
103
+ * in a `PlayerCodeBroker` with `engine: 'ck-exec'`, its fuel budget and tick interval, bounded
104
+ * to the host calls of the capability summary the player consented to.
105
+ *
106
+ * Modules are cached by digest, since a digest never changes its bytes and the artifact query
107
+ * allows 12 fetches a minute per player and mod. A cached module starts without a fetch, so the
108
+ * serve checks that query makes (`run_client_code`, standing in the grid) are those of the fetch
109
+ * that filled the cache; consent and trust are the listing's, on every refresh.
110
+ *
111
+ * `NOT_FOUND` (not served to this player now) and `RATE_LIMITED` hold that CLIENT half back for
112
+ * a while instead of fetching on every refresh.
113
+ */
114
+ export declare class ExecClientHalves {
115
+ private readonly options;
116
+ private grid;
117
+ private generation;
118
+ private readonly workers;
119
+ private readonly cache;
120
+ private readonly retryAt;
121
+ private readonly declined;
122
+ private readonly approved;
123
+ private inFlight;
124
+ constructor(options: ExecClientHalvesOptions);
125
+ /** The grid the player stands in, or null. Any change stops every CLIENT half of the old one. */
126
+ enterGrid(grid: ExecClientHalvesGrid | null): void;
127
+ /** The CLIENT halves running now. */
128
+ get running(): readonly ExecGridClientMod[];
129
+ /**
130
+ * One reconcile of the current grid; a call while one runs for the same grid joins it. Rejects
131
+ * when the grid's CLIENT halves cannot be listed, and what runs keeps running.
132
+ */
133
+ refresh(): Promise<void>;
134
+ /**
135
+ * Calls the `handle_invoke` export of the running CLIENT half of mod `modId` with `payload`
136
+ * and resolves with its reply bytes (see `PlayerCodeBroker.invoke`). Rejects when that CLIENT
137
+ * half is not running.
138
+ */
139
+ invoke(modId: string, payload: Uint8Array, options?: {
140
+ timeoutMs?: number;
141
+ }): Promise<Uint8Array>;
142
+ /** Stops every CLIENT half and leaves the grid; the module cache stays. */
143
+ stop(): void;
144
+ private reconcile;
145
+ private stopStale;
146
+ /** Asks about what the player has not agreed to; true when a consent or trust went through. */
147
+ private askAbout;
148
+ private start;
149
+ private fail;
150
+ private remember;
151
+ private wanted;
152
+ private stopWorker;
153
+ private stopAll;
154
+ private now;
155
+ }
156
+ //# sourceMappingURL=exec-client-halves.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec-client-halves.d.ts","sourceRoot":"","sources":["../../src/grid-mods/exec-client-halves.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,356 @@
1
+ import { CrowdyGraphQLError, CrowdyProtocolError } from '../errors.js';
2
+ import { PlayerCodeBroker, } from '../player-runtime/player-code-broker.js';
3
+ const identity = (m) => `${m.modId}\u0000${m.digest}\u0000${m.capabilityHash}\u0000${m.tickIntervalMs}`;
4
+ function refusal(error) {
5
+ if (error instanceof CrowdyProtocolError)
6
+ return 'refused';
7
+ if (error instanceof CrowdyGraphQLError) {
8
+ if (error.code === 'NOT_FOUND')
9
+ return 'not-found';
10
+ if (error.code === 'RATE_LIMITED')
11
+ return 'rate-limited';
12
+ if (error.code === 'CONFLICT')
13
+ return 'conflict';
14
+ }
15
+ return 'failed';
16
+ }
17
+ /**
18
+ * Runs the CLIENT halves of ck-exec mods that the grid a player stands in serves, the way a game
19
+ * should: tell it the grid ({@link enterGrid}) and call {@link refresh} on a cadence (every 10 s
20
+ * or so). Each refresh lists the grid's CLIENT halves (`exec.gridClientMods`), stops the workers
21
+ * whose mod is gone or whose `modId`, `digest`, `capabilityHash` or tick interval changed, asks
22
+ * the player once per author (or per CLIENT half) about the rest, fetches each consented one
23
+ * (`exec.modClientArtifactBytes`, which refuses bytes that differ from their digest) and runs it
24
+ * in a `PlayerCodeBroker` with `engine: 'ck-exec'`, its fuel budget and tick interval, bounded
25
+ * to the host calls of the capability summary the player consented to.
26
+ *
27
+ * Modules are cached by digest, since a digest never changes its bytes and the artifact query
28
+ * allows 12 fetches a minute per player and mod. A cached module starts without a fetch, so the
29
+ * serve checks that query makes (`run_client_code`, standing in the grid) are those of the fetch
30
+ * that filled the cache; consent and trust are the listing's, on every refresh.
31
+ *
32
+ * `NOT_FOUND` (not served to this player now) and `RATE_LIMITED` hold that CLIENT half back for
33
+ * a while instead of fetching on every refresh.
34
+ */
35
+ export class ExecClientHalves {
36
+ constructor(options) {
37
+ this.options = options;
38
+ this.grid = null;
39
+ this.generation = 0;
40
+ this.workers = new Map();
41
+ this.cache = new Map();
42
+ this.retryAt = new Map();
43
+ this.declined = new Set();
44
+ this.approved = new Set();
45
+ this.inFlight = null;
46
+ }
47
+ /** The grid the player stands in, or null. Any change stops every CLIENT half of the old one. */
48
+ enterGrid(grid) {
49
+ if (sameGrid(this.grid, grid))
50
+ return;
51
+ this.stopAll('left-grid');
52
+ this.grid = grid ? { ...grid } : null;
53
+ this.generation++;
54
+ this.declined.clear();
55
+ this.approved.clear();
56
+ }
57
+ /** The CLIENT halves running now. */
58
+ get running() {
59
+ return [...this.workers.values()].filter((w) => w.started).map((w) => w.mod);
60
+ }
61
+ /**
62
+ * One reconcile of the current grid; a call while one runs for the same grid joins it. Rejects
63
+ * when the grid's CLIENT halves cannot be listed, and what runs keeps running.
64
+ */
65
+ refresh() {
66
+ if (this.inFlight?.generation === this.generation)
67
+ return this.inFlight.done;
68
+ const generation = this.generation;
69
+ const done = this.reconcile(generation).finally(() => {
70
+ if (this.inFlight?.done === done)
71
+ this.inFlight = null;
72
+ });
73
+ this.inFlight = { generation, done };
74
+ return done;
75
+ }
76
+ /**
77
+ * Calls the `handle_invoke` export of the running CLIENT half of mod `modId` with `payload`
78
+ * and resolves with its reply bytes (see `PlayerCodeBroker.invoke`). Rejects when that CLIENT
79
+ * half is not running.
80
+ */
81
+ invoke(modId, payload, options) {
82
+ const worker = this.workers.get(modId);
83
+ if (!worker?.started) {
84
+ return Promise.reject(new Error(`the CLIENT half of mod ${modId} is not running`));
85
+ }
86
+ if (!worker.broker.invoke) {
87
+ return Promise.reject(new Error('this broker cannot invoke its module'));
88
+ }
89
+ return worker.broker.invoke(payload, options);
90
+ }
91
+ /** Stops every CLIENT half and leaves the grid; the module cache stays. */
92
+ stop() {
93
+ this.stopAll('stopped');
94
+ this.grid = null;
95
+ this.generation++;
96
+ this.declined.clear();
97
+ this.approved.clear();
98
+ }
99
+ async reconcile(generation) {
100
+ const grid = this.grid;
101
+ if (!grid)
102
+ return;
103
+ const { exec, appId } = this.options;
104
+ const current = () => generation === this.generation;
105
+ let listed = await exec.gridClientMods(appId, grid.gridId);
106
+ if (!current())
107
+ return;
108
+ this.stopStale(listed);
109
+ if (await this.askAbout(listed, grid, current)) {
110
+ if (!current())
111
+ return;
112
+ listed = await exec.gridClientMods(appId, grid.gridId);
113
+ if (!current())
114
+ return;
115
+ this.stopStale(listed);
116
+ }
117
+ for (const mod of listed) {
118
+ if (!current())
119
+ return;
120
+ if (!this.wanted(mod) || !(mod.callerConsented || mod.callerTrustsAuthor))
121
+ continue;
122
+ if (this.workers.has(mod.modId))
123
+ continue;
124
+ if ((this.retryAt.get(identity(mod)) ?? 0) > this.now())
125
+ continue;
126
+ await this.start(mod, grid, current);
127
+ }
128
+ }
129
+ stopStale(listed) {
130
+ const byId = new Map(listed.map((m) => [m.modId, m]));
131
+ for (const [modId, worker] of [...this.workers]) {
132
+ const next = byId.get(modId);
133
+ const reason = !next
134
+ ? 'removed'
135
+ : identity(next) !== identity(worker.mod)
136
+ ? 'changed'
137
+ : !this.wanted(next)
138
+ ? 'filtered'
139
+ : !(next.callerConsented || next.callerTrustsAuthor)
140
+ ? 'unconsented'
141
+ : null;
142
+ if (reason)
143
+ this.stopWorker(modId, reason);
144
+ }
145
+ }
146
+ /** Asks about what the player has not agreed to; true when a consent or trust went through. */
147
+ async askAbout(listed, grid, current) {
148
+ const pending = listed.filter((m) => this.wanted(m) && !m.callerConsented && !m.callerTrustsAuthor);
149
+ if (pending.length === 0)
150
+ return false;
151
+ const prompts = [];
152
+ if ((this.options.ask ?? 'author') === 'author') {
153
+ for (const authorId of new Set(pending.map((m) => m.authorId))) {
154
+ const theirs = listed.filter((m) => m.authorId === authorId);
155
+ const [first] = theirs;
156
+ prompts.push({
157
+ key: `author\u0000${authorId}\u0000${first.authorCapabilityHash}`,
158
+ prompt: {
159
+ kind: 'author',
160
+ gridId: grid.gridId,
161
+ authorId,
162
+ capabilityHash: first.authorCapabilityHash,
163
+ capabilitySummaryJson: first.authorCapabilitySummaryJson,
164
+ capabilitySummary: first.authorCapabilitySummary,
165
+ mods: theirs,
166
+ },
167
+ });
168
+ }
169
+ }
170
+ else {
171
+ for (const mod of pending) {
172
+ prompts.push({
173
+ key: `mod\u0000${mod.modId}\u0000${mod.capabilityHash}`,
174
+ prompt: {
175
+ kind: 'mod',
176
+ gridId: grid.gridId,
177
+ authorId: mod.authorId,
178
+ capabilityHash: mod.capabilityHash,
179
+ capabilitySummaryJson: mod.capabilitySummaryJson,
180
+ capabilitySummary: mod.capabilitySummary,
181
+ mods: [mod],
182
+ },
183
+ });
184
+ }
185
+ }
186
+ let agreed = false;
187
+ for (const { key, prompt } of prompts) {
188
+ if (!current())
189
+ return agreed;
190
+ if (this.declined.has(key))
191
+ continue;
192
+ if (!this.approved.has(key)) {
193
+ if (!this.options.confirm)
194
+ continue;
195
+ const yes = await this.options.confirm(prompt);
196
+ if (!current())
197
+ return agreed;
198
+ if (!yes) {
199
+ this.declined.add(key);
200
+ continue;
201
+ }
202
+ // Kept until it goes through: a trust refused while the player's presence is not yet
203
+ // registered is retried on the next refresh without asking again.
204
+ this.approved.add(key);
205
+ }
206
+ try {
207
+ const { exec, appId } = this.options;
208
+ if (prompt.kind === 'author') {
209
+ await exec.trustAuthor(appId, grid.gridId, prompt.authorId, prompt.capabilityHash);
210
+ }
211
+ else {
212
+ await exec.consentClientMod(appId, prompt.mods[0].modId, prompt.capabilityHash);
213
+ }
214
+ this.approved.delete(key);
215
+ agreed = true;
216
+ }
217
+ catch (error) {
218
+ const reason = refusal(error);
219
+ // The hash moved on: the next listing brings the new one, and a new question.
220
+ if (reason === 'conflict')
221
+ this.approved.delete(key);
222
+ this.options.onError?.({ mod: prompt.mods[0], stage: 'consent', reason, error });
223
+ }
224
+ }
225
+ return agreed;
226
+ }
227
+ async start(mod, grid, current) {
228
+ // What the player consented to, and what the served artifact itself lists, bound the
229
+ // module's host calls in the broker.
230
+ const consented = mod.capabilitySummary?.hostFunctions;
231
+ if (!Array.isArray(consented)) {
232
+ this.fail(mod, 'start', new CrowdyProtocolError({
233
+ message: `CLIENT half of mod ${mod.modId}: its capability summary does not parse`,
234
+ }));
235
+ return;
236
+ }
237
+ let module = this.cache.get(mod.digest);
238
+ if (module) {
239
+ this.cache.delete(mod.digest);
240
+ this.cache.set(mod.digest, module);
241
+ }
242
+ else {
243
+ try {
244
+ const a = await this.options.exec.modClientArtifactBytes(this.options.appId, mod.modId);
245
+ module = {
246
+ bytes: a.bytes,
247
+ fuelPerDispatch: a.fuelPerDispatch,
248
+ hostFunctions: a.capabilitySummary.hostFunctions,
249
+ };
250
+ this.remember(a.digest, module);
251
+ if (!current())
252
+ return;
253
+ // Changed between the listing and the fetch: the next listing starts the new one.
254
+ if (a.digest !== mod.digest ||
255
+ a.capabilityHash !== mod.capabilityHash ||
256
+ a.tickIntervalMs !== mod.tickIntervalMs) {
257
+ return;
258
+ }
259
+ }
260
+ catch (error) {
261
+ if (!current())
262
+ return;
263
+ this.fail(mod, 'fetch', error);
264
+ return;
265
+ }
266
+ }
267
+ const served = module.hostFunctions;
268
+ const broker = (this.options.brokerFactory ?? ((o) => new PlayerCodeBroker(o)))({
269
+ engine: 'ck-exec',
270
+ workerUrl: this.options.workerUrl,
271
+ grid: { low: grid.low, high: grid.high, gridId: grid.gridId },
272
+ moduleName: mod.name,
273
+ artifactHash: mod.digest,
274
+ fuelPerDispatch: module.fuelPerDispatch,
275
+ tickIntervalMs: mod.tickIntervalMs,
276
+ consentedHostCalls: consented.filter((fn) => served.includes(fn)),
277
+ onHostCall: (call) => this.options.onHostCall(call, mod),
278
+ onPresentation: (presentation) => this.options.onPresentation?.(presentation, mod),
279
+ ...(this.options.onLog ? { onLog: (line) => this.options.onLog?.(line, mod) } : {}),
280
+ onCircuitOpen: () => {
281
+ if (this.workers.get(mod.modId)?.broker !== broker)
282
+ return;
283
+ this.retryAt.set(identity(mod), this.now() + (this.options.failedRetryMs ?? 60000));
284
+ this.stopWorker(mod.modId, 'circuit-open');
285
+ },
286
+ });
287
+ const worker = { mod, broker, started: false };
288
+ this.workers.set(mod.modId, worker);
289
+ try {
290
+ await broker.start(module.bytes);
291
+ }
292
+ catch (error) {
293
+ if (this.workers.get(mod.modId) !== worker)
294
+ return;
295
+ this.workers.delete(mod.modId);
296
+ broker.stop();
297
+ if (current())
298
+ this.fail(mod, 'start', error);
299
+ return;
300
+ }
301
+ if (this.workers.get(mod.modId) !== worker)
302
+ return;
303
+ worker.started = true;
304
+ this.options.onStarted?.(mod);
305
+ }
306
+ fail(mod, stage, error) {
307
+ const reason = refusal(error);
308
+ const wait = reason === 'not-found'
309
+ ? (this.options.notFoundRetryMs ?? 15000)
310
+ : reason === 'rate-limited'
311
+ ? (this.options.rateLimitedRetryMs ?? 60000)
312
+ : (this.options.failedRetryMs ?? 60000);
313
+ const retryAt = this.now() + wait;
314
+ this.retryAt.set(identity(mod), retryAt);
315
+ this.options.onError?.({ mod, stage, reason, error, retryAt });
316
+ }
317
+ remember(digest, module) {
318
+ this.cache.set(digest, module);
319
+ const limit = Math.max(1, this.options.cacheSize ?? 16);
320
+ for (const oldest of this.cache.keys()) {
321
+ if (this.cache.size <= limit)
322
+ break;
323
+ this.cache.delete(oldest);
324
+ }
325
+ }
326
+ wanted(mod) {
327
+ return this.options.filter ? this.options.filter(mod) : true;
328
+ }
329
+ stopWorker(modId, reason) {
330
+ const worker = this.workers.get(modId);
331
+ if (!worker)
332
+ return;
333
+ this.workers.delete(modId);
334
+ worker.broker.stop();
335
+ if (worker.started)
336
+ this.options.onStopped?.(worker.mod, reason);
337
+ }
338
+ stopAll(reason) {
339
+ for (const modId of [...this.workers.keys()])
340
+ this.stopWorker(modId, reason);
341
+ }
342
+ now() {
343
+ return (this.options.now ?? Date.now)();
344
+ }
345
+ }
346
+ function sameGrid(a, b) {
347
+ if (!a || !b)
348
+ return a === b;
349
+ return (a.gridId === b.gridId &&
350
+ a.low.x === b.low.x &&
351
+ a.low.y === b.low.y &&
352
+ a.low.z === b.low.z &&
353
+ a.high.x === b.high.x &&
354
+ a.high.y === b.high.y &&
355
+ a.high.z === b.high.z);
356
+ }
@@ -1,19 +1,32 @@
1
- import type { GridScope } from '../grid-scope.js';
1
+ import type { GridChunk, GridScope } from '../grid-scope.js';
2
2
  import type { PlayerCodeHostCall } from '../player-runtime/player-code-broker.js';
3
+ import type { AvatarsAPI } from '../domains/avatars.js';
3
4
  import type { ChunksAPI } from '../domains/chunks.js';
4
5
  import type { VoxelsAPI } from '../domains/voxels.js';
5
6
  import type { StateAPI } from '../domains/state.js';
6
- import type { GameModelAPI } from '../domains/gameModel.js';
7
7
  /**
8
- * Game-local fast paths. A game that already holds the world in memory (a
9
- * chunk store, an actor store) answers reads from there instead of a round
10
- * trip; anything it leaves out falls through to the server path.
8
+ * Game-local fast paths and knowledge. A game that already holds the world in memory (a chunk
9
+ * store, an actor store) answers reads from there instead of a round trip; anything it leaves
10
+ * out falls through to the server path. The calls only the game can answer (`actors_list*`,
11
+ * `avatar_state_get`, `grid_permission_check`, `pointer_clicks`) are refused without their
12
+ * hook.
11
13
  */
12
14
  export interface GridHostLocal {
13
15
  chunkVoxels?(x: bigint, y: bigint, z: bigint): {
14
16
  voxelsBase64: string | null;
15
17
  } | null;
16
18
  actorsInChunk?(x: bigint, y: bigint, z: bigint): Array<Record<string, unknown>>;
19
+ /**
20
+ * The chunk of the avatar's live actor, from the game's actor store, or null when the game
21
+ * sees none. `avatar_state_get` answers only for an avatar whose actor is inside the grid.
22
+ */
23
+ avatarChunk?(avatarId: string): GridChunk | null | undefined | Promise<GridChunk | null | undefined>;
24
+ /**
25
+ * The runtime permission keys the visiting player holds on this grid, as the game knows them
26
+ * (where Crowdy Studio's `targetPermissions` come from). `grid_permission_check` answers from
27
+ * these, for that player and this grid only; the server still enforces the permissions.
28
+ */
29
+ gridPermissionKeys?(): Iterable<string> | Promise<Iterable<string>>;
17
30
  setVoxel?(input: {
18
31
  chunk: {
19
32
  x: number;
@@ -36,15 +49,15 @@ export interface GridHostCallsOptions {
36
49
  chunks: ChunksAPI;
37
50
  voxels: VoxelsAPI;
38
51
  state: StateAPI;
39
- gameModel: GameModelAPI;
52
+ /** For `avatar_state_get`, a public read (`avatarAppState`). */
53
+ avatars?: Pick<AvatarsAPI, 'appState'>;
40
54
  };
41
55
  local?: GridHostLocal;
42
56
  /**
43
- * Let mods call Studio model functions as the player (`model_invoke`, the
44
- * ordinary player authority path). Off by default: a game opts in when its
45
- * functions are meant to be scriptable by visitors' mods.
57
+ * The visiting player's user id. `grid_permission_check` answers only about them, so without
58
+ * it the call is refused.
46
59
  */
47
- allowModelInvoke?: boolean;
60
+ userId?: string;
48
61
  /**
49
62
  * Channels a mod may post to. Default: the grid's own channels only (the
50
63
  * server rule for grid code). A game may widen it to channels the player
@@ -60,11 +73,12 @@ export declare class GridHostCallRefused extends Error {
60
73
  constructor(fn: string, reason?: string);
61
74
  }
62
75
  /**
63
- * The page-side answer to every CLIENT host call in the platform catalog,
64
- * through ordinary CrowdyJS confined to one grid (DN-10 §4). Hand the result
65
- * to `PlayerCodeBroker({ onHostCall })`; the broker has already applied the
66
- * allowlist, rate caps and chunk clamps, and answers `grid_info`, `emit_event`,
67
- * `hud_set` and `overlay_draw` itself.
76
+ * The page-side answer to a CLIENT half's host calls (`EXEC_CLIENT_HOST_CALLS`,
77
+ * what crowdy-client-sdk wraps), through ordinary CrowdyJS confined to one grid
78
+ * (DN-10 §4). Hand the result to `PlayerCodeBroker({ onHostCall })`; the broker
79
+ * has already applied the allowlist, the player's consent, rate caps and chunk
80
+ * clamps, and answers `grid_info`, `emit_event`, `hud_set` and `overlay_draw`
81
+ * itself.
68
82
  */
69
83
  export declare function createGridHostCalls(options: GridHostCallsOptions): (call: PlayerCodeHostCall) => Promise<unknown>;
70
84
  //# sourceMappingURL=grid-host-calls.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"grid-host-calls.d.ts","sourceRoot":"","sources":["../../src/grid-mods/grid-host-calls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAClF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG;QAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IACtF,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAChF,QAAQ,CAAC,CAAC,KAAK,EAAE;QACf,KAAK,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC3C,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrB,kBAAkB,CAAC,IAAI,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,yEAAyE;IACzE,KAAK,EAAE,SAAS,CAAC;IACjB,wEAAwE;IACxE,MAAM,EAAE;QACN,MAAM,EAAE,SAAS,CAAC;QAClB,MAAM,EAAE,SAAS,CAAC;QAClB,KAAK,EAAE,QAAQ,CAAC;QAChB,SAAS,EAAE,YAAY,CAAC;KACzB,CAAC;IACF,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,sEAAsE;AACtE,qBAAa,mBAAoB,SAAQ,KAAK;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM;gBAAV,EAAE,EAAE,MAAM,EAAE,MAAM,SAA+B;CAIvE;AAID;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,oBAAoB,GAC5B,CAAC,IAAI,EAAE,kBAAkB,KAAK,OAAO,CAAC,OAAO,CAAC,CA+KhD"}
1
+ {"version":3,"file":"grid-host-calls.d.ts","sourceRoot":"","sources":["../../src/grid-mods/grid-host-calls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAClF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG;QAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IACtF,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAChF;;;OAGG;IACH,WAAW,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACrG;;;;OAIG;IACH,kBAAkB,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACpE,QAAQ,CAAC,CAAC,KAAK,EAAE;QACf,KAAK,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC3C,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrB,kBAAkB,CAAC,IAAI,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,yEAAyE;IACzE,KAAK,EAAE,SAAS,CAAC;IACjB,wEAAwE;IACxE,MAAM,EAAE;QACN,MAAM,EAAE,SAAS,CAAC;QAClB,MAAM,EAAE,SAAS,CAAC;QAClB,KAAK,EAAE,QAAQ,CAAC;QAChB,gEAAgE;QAChE,OAAO,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;KACxC,CAAC;IACF,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,sEAAsE;AACtE,qBAAa,mBAAoB,SAAQ,KAAK;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM;gBAAV,EAAE,EAAE,MAAM,EAAE,MAAM,SAA+B;CAIvE;AAID;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,oBAAoB,GAC5B,CAAC,IAAI,EAAE,kBAAkB,KAAK,OAAO,CAAC,OAAO,CAAC,CA6JhD"}