@dreamboard-games/sdk 0.5.0-alpha.2 → 0.5.0-alpha.3

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 (150) hide show
  1. package/README.md +136 -45
  2. package/dist/chunk-DMFVAZI2.js +16 -0
  3. package/dist/chunk-DMFVAZI2.js.map +1 -0
  4. package/dist/chunk-FMM5LVJW.js +833 -0
  5. package/dist/chunk-FMM5LVJW.js.map +1 -0
  6. package/dist/{chunk-S2JEDVWH.js → chunk-LRILEVKC.js} +6113 -6489
  7. package/dist/chunk-LRILEVKC.js.map +1 -0
  8. package/dist/chunk-SP234IY4.js +1363 -0
  9. package/dist/chunk-SP234IY4.js.map +1 -0
  10. package/dist/chunk-VUWNJXLJ.js +52 -0
  11. package/dist/chunk-VUWNJXLJ.js.map +1 -0
  12. package/dist/chunk-ZPRKWPEP.js +511 -0
  13. package/dist/chunk-ZPRKWPEP.js.map +1 -0
  14. package/dist/definition-BaHNPZol.d.ts +4639 -0
  15. package/dist/diagnostics-B0lgnuZ6.d.ts +65 -0
  16. package/dist/index.d.ts +3553 -1
  17. package/dist/index.js +1045 -6
  18. package/dist/index.js.map +1 -1
  19. package/dist/model-BHro6yXu.d.ts +288 -0
  20. package/dist/react.d.ts +32 -0
  21. package/dist/react.js +80 -0
  22. package/dist/react.js.map +1 -0
  23. package/dist/reducer.d.ts +1194 -79
  24. package/dist/reducer.js +4326 -121
  25. package/dist/reducer.js.map +1 -1
  26. package/dist/testing.d.ts +518 -564
  27. package/dist/testing.js +2448 -915
  28. package/dist/testing.js.map +1 -1
  29. package/dist/types-DimqnJBq.d.ts +86 -0
  30. package/package.json +26 -131
  31. package/dist/HandView-Bi2lxzQP.d.ts +0 -79
  32. package/dist/ResourceCounter-BFxJknqp.d.ts +0 -98
  33. package/dist/ThemeProvider-CElaL2ik.d.ts +0 -99
  34. package/dist/attributes-DbvyMbXw.d.ts +0 -68
  35. package/dist/browser-interaction.d.ts +0 -910
  36. package/dist/browser-interaction.js +0 -119
  37. package/dist/browser-interaction.js.map +0 -1
  38. package/dist/chunk-32PFKDV7.js +0 -11705
  39. package/dist/chunk-32PFKDV7.js.map +0 -1
  40. package/dist/chunk-3BAD4VC2.js +0 -263
  41. package/dist/chunk-3BAD4VC2.js.map +0 -1
  42. package/dist/chunk-3SKDNDPG.js +0 -104
  43. package/dist/chunk-3SKDNDPG.js.map +0 -1
  44. package/dist/chunk-5YV2WHJ4.js +0 -25
  45. package/dist/chunk-5YV2WHJ4.js.map +0 -1
  46. package/dist/chunk-ANRB4XKA.js +0 -90
  47. package/dist/chunk-ANRB4XKA.js.map +0 -1
  48. package/dist/chunk-BI4G3PM2.js +0 -1864
  49. package/dist/chunk-BI4G3PM2.js.map +0 -1
  50. package/dist/chunk-EVCLTY4Q.js +0 -935
  51. package/dist/chunk-EVCLTY4Q.js.map +0 -1
  52. package/dist/chunk-GDPKTBUR.js +0 -564
  53. package/dist/chunk-GDPKTBUR.js.map +0 -1
  54. package/dist/chunk-H6VDGFL5.js +0 -2903
  55. package/dist/chunk-H6VDGFL5.js.map +0 -1
  56. package/dist/chunk-LR3ZTWQF.js +0 -5960
  57. package/dist/chunk-LR3ZTWQF.js.map +0 -1
  58. package/dist/chunk-PF7L4BMG.js +0 -117
  59. package/dist/chunk-PF7L4BMG.js.map +0 -1
  60. package/dist/chunk-PN5O6GG2.js +0 -32
  61. package/dist/chunk-PN5O6GG2.js.map +0 -1
  62. package/dist/chunk-PZ5AY32C.js +0 -10
  63. package/dist/chunk-PZ5AY32C.js.map +0 -1
  64. package/dist/chunk-S2JEDVWH.js.map +0 -1
  65. package/dist/chunk-T3ZKNUZ7.js +0 -1
  66. package/dist/chunk-T3ZKNUZ7.js.map +0 -1
  67. package/dist/chunk-UGH54WZ2.js +0 -223
  68. package/dist/chunk-UGH54WZ2.js.map +0 -1
  69. package/dist/chunk-VCDWP5VL.js +0 -746
  70. package/dist/chunk-VCDWP5VL.js.map +0 -1
  71. package/dist/chunk-VDXOF4FW.js +0 -69
  72. package/dist/chunk-VDXOF4FW.js.map +0 -1
  73. package/dist/chunk-WHR5UW3F.js +0 -1988
  74. package/dist/chunk-WHR5UW3F.js.map +0 -1
  75. package/dist/chunk-Y75CFE77.js +0 -17
  76. package/dist/chunk-Y75CFE77.js.map +0 -1
  77. package/dist/chunk-Z7QCREWI.js +0 -3435
  78. package/dist/chunk-Z7QCREWI.js.map +0 -1
  79. package/dist/chunk-ZZGN2ATS.js +0 -1941
  80. package/dist/chunk-ZZGN2ATS.js.map +0 -1
  81. package/dist/components-gAbIz7hK.d.ts +0 -1284
  82. package/dist/definitions-BgmV_GhC.d.ts +0 -300
  83. package/dist/diagnostics-DIi3ina7.d.ts +0 -50
  84. package/dist/digest.d.ts +0 -8
  85. package/dist/game-CsZScpcn.d.ts +0 -763
  86. package/dist/hex-board-view-aKkblDp6.d.ts +0 -1228
  87. package/dist/index-CcT9Q7N7.d.ts +0 -193
  88. package/dist/index.d-DjzoK7zn.d.ts +0 -2101
  89. package/dist/package-set.d.ts +0 -13
  90. package/dist/package-set.js +0 -12
  91. package/dist/package-set.js.map +0 -1
  92. package/dist/player-state-Cqpyeql0.d.ts +0 -371
  93. package/dist/plugin-runtime-contract.d.ts +0 -17
  94. package/dist/plugin-runtime-contract.js +0 -92
  95. package/dist/plugin-runtime-contract.js.map +0 -1
  96. package/dist/primitive-props-BNHDkgd7.d.ts +0 -16
  97. package/dist/protocol-dYgafTYY.d.ts +0 -314
  98. package/dist/reducer/advanced.d.ts +0 -82
  99. package/dist/reducer/advanced.js +0 -51
  100. package/dist/reducer/advanced.js.map +0 -1
  101. package/dist/reducer-contract.d.ts +0 -11
  102. package/dist/reducer-contract.js +0 -16
  103. package/dist/reducer-contract.js.map +0 -1
  104. package/dist/reference-games/index.d.ts +0 -31
  105. package/dist/reference-games/index.js +0 -48
  106. package/dist/reference-games/index.js.map +0 -1
  107. package/dist/runtime/primitives.d.ts +0 -250
  108. package/dist/runtime/primitives.js +0 -189
  109. package/dist/runtime/primitives.js.map +0 -1
  110. package/dist/runtime/runtime-api.d.ts +0 -2
  111. package/dist/runtime/runtime-api.js +0 -1
  112. package/dist/runtime/runtime-api.js.map +0 -1
  113. package/dist/runtime/workspace-contract.d.ts +0 -392
  114. package/dist/runtime/workspace-contract.js +0 -28
  115. package/dist/runtime/workspace-contract.js.map +0 -1
  116. package/dist/runtime-RJ5orDJU.d.ts +0 -1699
  117. package/dist/runtime-api-Bz5pwNU_.d.ts +0 -296
  118. package/dist/runtime-json-CQ9QbLZ5.d.ts +0 -5
  119. package/dist/runtime.d.ts +0 -83
  120. package/dist/runtime.js +0 -242
  121. package/dist/runtime.js.map +0 -1
  122. package/dist/schema.d.ts +0 -379
  123. package/dist/stale-contract-artifact-error-XLaweZtF.d.ts +0 -18
  124. package/dist/testing-compiler.d.ts +0 -37
  125. package/dist/testing-compiler.js +0 -272
  126. package/dist/testing-compiler.js.map +0 -1
  127. package/dist/testing-runtime.d.ts +0 -119
  128. package/dist/testing-runtime.js +0 -212
  129. package/dist/testing-runtime.js.map +0 -1
  130. package/dist/types-DJj5MJkl.d.ts +0 -256
  131. package/dist/types-DR7DoB1x.d.ts +0 -28
  132. package/dist/types-JWCYHmu7.d.ts +0 -122
  133. package/dist/types.d.ts +0 -1958
  134. package/dist/types.js +0 -14
  135. package/dist/types.js.map +0 -1
  136. package/dist/ui/components.d.ts +0 -17
  137. package/dist/ui/components.js +0 -216
  138. package/dist/ui/components.js.map +0 -1
  139. package/dist/ui/defaults.d.ts +0 -19
  140. package/dist/ui/defaults.js +0 -104
  141. package/dist/ui/defaults.js.map +0 -1
  142. package/dist/ui/player-state.d.ts +0 -2
  143. package/dist/ui/player-state.js +0 -1
  144. package/dist/ui/player-state.js.map +0 -1
  145. package/dist/ui/plugin-styles.css +0 -2
  146. package/dist/ui-contract-GTUPkf8z.d.ts +0 -1168
  147. package/dist/ui.d.ts +0 -317
  148. package/dist/ui.js +0 -277
  149. package/dist/ui.js.map +0 -1
  150. package/dist/views-BpuWcOyN.d.ts +0 -1641
@@ -1,1988 +0,0 @@
1
- import {
2
- BROWSER_ATTRIBUTE_JSON_LIMITS,
3
- RuntimeJsonSchema,
4
- assertJsonWithinLimits,
5
- parseBrowserAttributeJson
6
- } from "./chunk-PF7L4BMG.js";
7
-
8
- // src/browser-interaction/constants.ts
9
- var DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_NAME = "dreamboard-browser-interaction";
10
- var DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_VERSION = "3.0.0";
11
- var BROWSER_INTERACTION_ATTRIBUTES = {
12
- protocol: "data-dreamboard-browser-protocol",
13
- surface: "data-dreamboard-browser-surface",
14
- scope: "data-dreamboard-browser-scope",
15
- role: "data-dreamboard-browser-role",
16
- intent: "data-dreamboard-browser-intent",
17
- interactionKey: "data-dreamboard-interaction-key",
18
- interactionId: "data-dreamboard-interaction-id",
19
- descriptorDigest: "data-dreamboard-descriptor-digest",
20
- draftDigest: "data-dreamboard-draft-digest",
21
- readiness: "data-dreamboard-readiness",
22
- inputKey: "data-dreamboard-input-key",
23
- candidateValue: "data-dreamboard-candidate-value",
24
- candidateState: "data-dreamboard-candidate-state",
25
- actuatorKind: "data-dreamboard-actuator-kind",
26
- enabled: "data-dreamboard-actuator-enabled",
27
- actuatorId: "data-dreamboard-actuator-id",
28
- pointerTargetId: "data-dreamboard-pointer-target-id",
29
- pointerTargetEnabled: "data-dreamboard-pointer-target-enabled",
30
- semanticEffects: "data-dreamboard-semantic-effects",
31
- acceptedEffectPatterns: "data-dreamboard-accepted-effect-patterns",
32
- preparationPatterns: "data-dreamboard-preparation-patterns",
33
- preparesIntent: "data-dreamboard-prepares-intent",
34
- preparesInputKey: "data-dreamboard-prepares-input-key",
35
- preparesCandidateValue: "data-dreamboard-prepares-candidate-value",
36
- preparesActuatorKind: "data-dreamboard-prepares-actuator-kind",
37
- diagnostic: "data-dreamboard-browser-diagnostic"
38
- };
39
- var BROWSER_INTERACTION_RECORD_ROLES = [
40
- "interaction",
41
- "actuator",
42
- "pointer-target"
43
- ];
44
- var GAMEPLAY_BROWSER_INTERACTION_SURFACE = "gameplay";
45
- var GAMEPLAY_BROWSER_INTERACTION_INTENTS = [
46
- "arm",
47
- "reveal",
48
- "invoke",
49
- "select",
50
- "toggle",
51
- "increment",
52
- "decrement",
53
- "fill",
54
- "submit"
55
- ];
56
- var GAMEPLAY_BROWSER_INTERACTION_EFFECT_KINDS = [
57
- "setCandidate",
58
- "adjustResource",
59
- "setScalar",
60
- "commit",
61
- "invoke"
62
- ];
63
- var BROWSER_INTERACTION_READINESS_VALUES = [
64
- "ready",
65
- "blocked",
66
- "unavailable"
67
- ];
68
- var BROWSER_INTERACTION_CANDIDATE_STATES = [
69
- "selected",
70
- "unselected",
71
- "mixed"
72
- ];
73
- var BROWSER_INTERACTION_ACTUATOR_KINDS = [
74
- "click",
75
- "fill",
76
- "keyboard",
77
- "pointer"
78
- ];
79
-
80
- // src/browser-interaction/canonical.ts
81
- function assertCanonicalValue(value) {
82
- if (value === null) return;
83
- switch (typeof value) {
84
- case "boolean":
85
- case "string":
86
- return;
87
- case "number":
88
- if (Number.isFinite(value)) return;
89
- break;
90
- case "object":
91
- if (Array.isArray(value)) {
92
- value.forEach(assertCanonicalValue);
93
- return;
94
- }
95
- if (Object.getPrototypeOf(value) !== Object.prototype) break;
96
- for (const [key, item] of Object.entries(value)) {
97
- if (item === void 0) {
98
- throw new Error(
99
- `Browser interaction candidate '${key}' cannot be undefined.`
100
- );
101
- }
102
- assertCanonicalValue(item);
103
- }
104
- return;
105
- }
106
- throw new Error(
107
- `Browser interaction candidates must be JSON-serializable primitives, arrays or plain objects. Received ${typeof value}.`
108
- );
109
- }
110
- function canonicalizeValue(value) {
111
- if (Array.isArray(value)) {
112
- return value.map(canonicalizeValue);
113
- }
114
- if (value && typeof value === "object") {
115
- const objectValue = value;
116
- return Object.fromEntries(
117
- Object.keys(objectValue).sort().map((key) => [key, canonicalizeValue(objectValue[key])])
118
- );
119
- }
120
- return value;
121
- }
122
- function encodeCanonicalCandidateValue(value) {
123
- parseBrowserAttributeJson(value);
124
- assertCanonicalValue(value);
125
- return JSON.stringify(canonicalizeValue(value));
126
- }
127
- function decodeCanonicalCandidateValue(encoded) {
128
- const parsed = JSON.parse(encoded);
129
- parseBrowserAttributeJson(parsed);
130
- assertCanonicalValue(parsed);
131
- return canonicalizeValue(parsed);
132
- }
133
- function compareStableJson(a, b) {
134
- const encodedA = encodeCanonicalCandidateValue(a);
135
- const encodedB = encodeCanonicalCandidateValue(b);
136
- return encodedA.localeCompare(encodedB);
137
- }
138
-
139
- // src/browser-interaction/effects.ts
140
- function encodeBrowserInteractionEffect(effect) {
141
- return encodeCanonicalCandidateValue(effect);
142
- }
143
- function decodeBrowserInteractionEffect(encoded) {
144
- const decoded = decodeCanonicalCandidateValue(encoded);
145
- if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) {
146
- throw new Error("Browser interaction effect must be a canonical object.");
147
- }
148
- const decodedObject = decoded;
149
- const kind = decodedObject.kind;
150
- if (typeof kind !== "string" || kind.length === 0) {
151
- throw new Error("Browser interaction effect requires a string kind.");
152
- }
153
- return decodedObject;
154
- }
155
- function encodeBrowserInteractionEffectPattern(pattern) {
156
- return encodeCanonicalCandidateValue(pattern);
157
- }
158
- function decodeBrowserInteractionEffectPattern(encoded) {
159
- const decoded = decodeCanonicalCandidateValue(encoded);
160
- assertBrowserInteractionEffectPattern(decoded);
161
- return decoded;
162
- }
163
- function browserInteractionEffectPatternMatches(pattern, effect) {
164
- switch (pattern.kind) {
165
- case "exact":
166
- return encodeBrowserInteractionEffect(pattern.effect) === encodeBrowserInteractionEffect(effect);
167
- case "match": {
168
- if (effect.kind !== pattern.effectKind) return false;
169
- for (const [field, expected] of Object.entries(pattern.fields ?? {})) {
170
- const actual = effect[field];
171
- if (actual === void 0) return false;
172
- if (encodeCanonicalCandidateValue(actual) !== encodeCanonicalCandidateValue(expected)) {
173
- return false;
174
- }
175
- }
176
- const scalar = pattern.scalar;
177
- if (scalar) {
178
- const actual = effect[scalar.field];
179
- if (typeof actual !== "number" || !Number.isFinite(actual)) {
180
- return false;
181
- }
182
- if (scalar.integer === true && !Number.isInteger(actual)) {
183
- return false;
184
- }
185
- if (scalar.min !== void 0 && actual < scalar.min) {
186
- return false;
187
- }
188
- if (scalar.max !== void 0 && actual > scalar.max) {
189
- return false;
190
- }
191
- }
192
- return true;
193
- }
194
- }
195
- }
196
- function assertBrowserInteractionEffectPattern(value) {
197
- if (!value || typeof value !== "object" || Array.isArray(value)) {
198
- throw new Error("Browser interaction effect pattern must be an object.");
199
- }
200
- const pattern = value;
201
- if (pattern.kind === "exact") {
202
- decodeBrowserInteractionEffect(
203
- encodeCanonicalCandidateValue(pattern.effect)
204
- );
205
- return;
206
- }
207
- if (pattern.kind === "match") {
208
- if (typeof pattern.effectKind !== "string" || pattern.effectKind.length === 0) {
209
- throw new Error(
210
- "Browser interaction match pattern requires an effectKind."
211
- );
212
- }
213
- if (pattern.fields !== void 0) {
214
- if (!pattern.fields || typeof pattern.fields !== "object" || Array.isArray(pattern.fields)) {
215
- throw new Error(
216
- "Browser interaction match pattern fields must be an object."
217
- );
218
- }
219
- }
220
- if (pattern.scalar !== void 0) {
221
- if (!pattern.scalar || typeof pattern.scalar !== "object" || Array.isArray(pattern.scalar) || typeof pattern.scalar.field !== "string" || pattern.scalar.field.length === 0) {
222
- throw new Error(
223
- "Browser interaction scalar pattern requires a scalar field."
224
- );
225
- }
226
- }
227
- return;
228
- }
229
- throw new Error("Unknown browser interaction effect pattern kind.");
230
- }
231
- function gameplaySetCandidateEffect(input) {
232
- return { kind: "setCandidate", ...input };
233
- }
234
- function gameplayAdjustResourceEffect(input) {
235
- return { kind: "adjustResource", ...input };
236
- }
237
- function gameplaySetScalarEffect(input) {
238
- return { kind: "setScalar", ...input };
239
- }
240
- function gameplayCommitEffect() {
241
- return { kind: "commit" };
242
- }
243
- function gameplayInvokeEffect() {
244
- return { kind: "invoke" };
245
- }
246
-
247
- // src/browser-interaction/attributes.ts
248
- function baseGameplayAttributes(input) {
249
- return {
250
- [BROWSER_INTERACTION_ATTRIBUTES.protocol]: DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_VERSION,
251
- [BROWSER_INTERACTION_ATTRIBUTES.surface]: input.surface,
252
- [BROWSER_INTERACTION_ATTRIBUTES.scope]: input.scopeId,
253
- [BROWSER_INTERACTION_ATTRIBUTES.interactionKey]: input.interactionKey,
254
- [BROWSER_INTERACTION_ATTRIBUTES.interactionId]: input.interactionId
255
- };
256
- }
257
- function createBrowserInteractionRootAttributes(input) {
258
- return {
259
- ...baseGameplayAttributes(input),
260
- [BROWSER_INTERACTION_ATTRIBUTES.role]: "interaction",
261
- [BROWSER_INTERACTION_ATTRIBUTES.descriptorDigest]: input.descriptorDigest ?? "",
262
- [BROWSER_INTERACTION_ATTRIBUTES.draftDigest]: input.draftDigest ?? "",
263
- [BROWSER_INTERACTION_ATTRIBUTES.readiness]: input.readiness
264
- };
265
- }
266
- function createBrowserInteractionActuatorAttributes(input) {
267
- const attrs = {
268
- ...baseGameplayAttributes(input),
269
- [BROWSER_INTERACTION_ATTRIBUTES.role]: "actuator",
270
- [BROWSER_INTERACTION_ATTRIBUTES.intent]: input.intent,
271
- [BROWSER_INTERACTION_ATTRIBUTES.actuatorKind]: input.actuatorKind,
272
- [BROWSER_INTERACTION_ATTRIBUTES.enabled]: input.enabled === false ? "false" : "true"
273
- };
274
- if (input.descriptorDigest) {
275
- attrs[BROWSER_INTERACTION_ATTRIBUTES.descriptorDigest] = input.descriptorDigest;
276
- }
277
- if (input.draftDigest) {
278
- attrs[BROWSER_INTERACTION_ATTRIBUTES.draftDigest] = input.draftDigest;
279
- }
280
- if (input.actuatorId) {
281
- attrs[BROWSER_INTERACTION_ATTRIBUTES.actuatorId] = input.actuatorId;
282
- }
283
- if (input.inputKey) {
284
- attrs[BROWSER_INTERACTION_ATTRIBUTES.inputKey] = input.inputKey;
285
- }
286
- if ("candidateValue" in input) {
287
- attrs[BROWSER_INTERACTION_ATTRIBUTES.candidateValue] = encodeCanonicalCandidateValue(input.candidateValue);
288
- }
289
- if (input.candidateState) {
290
- attrs[BROWSER_INTERACTION_ATTRIBUTES.candidateState] = input.candidateState;
291
- }
292
- if (input.semanticEffects && input.semanticEffects.length > 0) {
293
- attrs[BROWSER_INTERACTION_ATTRIBUTES.semanticEffects] = JSON.stringify(
294
- input.semanticEffects.map(encodeBrowserInteractionEffect)
295
- );
296
- }
297
- if (input.acceptedEffectPatterns && input.acceptedEffectPatterns.length > 0) {
298
- attrs[BROWSER_INTERACTION_ATTRIBUTES.acceptedEffectPatterns] = JSON.stringify(
299
- input.acceptedEffectPatterns.map(encodeBrowserInteractionEffectPattern)
300
- );
301
- }
302
- if (input.preparationPatterns && input.preparationPatterns.length > 0) {
303
- attrs[BROWSER_INTERACTION_ATTRIBUTES.preparationPatterns] = JSON.stringify(
304
- input.preparationPatterns.map(encodeBrowserInteractionEffectPattern)
305
- );
306
- }
307
- if (input.prepares) {
308
- attrs[BROWSER_INTERACTION_ATTRIBUTES.preparesIntent] = input.prepares.intent;
309
- if (input.prepares.inputKey) {
310
- attrs[BROWSER_INTERACTION_ATTRIBUTES.preparesInputKey] = input.prepares.inputKey;
311
- }
312
- if (input.prepares.candidateValueKey) {
313
- attrs[BROWSER_INTERACTION_ATTRIBUTES.preparesCandidateValue] = input.prepares.candidateValueKey;
314
- } else if ("candidateValue" in input.prepares) {
315
- attrs[BROWSER_INTERACTION_ATTRIBUTES.preparesCandidateValue] = encodeCanonicalCandidateValue(input.prepares.candidateValue);
316
- }
317
- if (input.prepares.actuatorKind) {
318
- attrs[BROWSER_INTERACTION_ATTRIBUTES.preparesActuatorKind] = input.prepares.actuatorKind;
319
- }
320
- }
321
- return attrs;
322
- }
323
- function createBrowserInteractionPointerTargetAttributes(input) {
324
- const attrs = {
325
- ...baseGameplayAttributes(input),
326
- [BROWSER_INTERACTION_ATTRIBUTES.role]: "pointer-target",
327
- [BROWSER_INTERACTION_ATTRIBUTES.pointerTargetId]: input.targetId,
328
- [BROWSER_INTERACTION_ATTRIBUTES.pointerTargetEnabled]: input.enabled === false ? "false" : "true"
329
- };
330
- if (input.descriptorDigest) {
331
- attrs[BROWSER_INTERACTION_ATTRIBUTES.descriptorDigest] = input.descriptorDigest;
332
- }
333
- if (input.acceptedEffectPatterns.length > 0) {
334
- attrs[BROWSER_INTERACTION_ATTRIBUTES.acceptedEffectPatterns] = JSON.stringify(
335
- input.acceptedEffectPatterns.map(encodeBrowserInteractionEffectPattern)
336
- );
337
- }
338
- return attrs;
339
- }
340
- function createGameplayInteractionRootAttributes(input) {
341
- return createBrowserInteractionRootAttributes({
342
- ...input,
343
- surface: GAMEPLAY_BROWSER_INTERACTION_SURFACE
344
- });
345
- }
346
- function createGameplayActuatorAttributes(input) {
347
- return createBrowserInteractionActuatorAttributes({
348
- ...input,
349
- surface: GAMEPLAY_BROWSER_INTERACTION_SURFACE
350
- });
351
- }
352
- function createGameplayPointerTargetAttributes(input) {
353
- return createBrowserInteractionPointerTargetAttributes({
354
- ...input,
355
- surface: GAMEPLAY_BROWSER_INTERACTION_SURFACE
356
- });
357
- }
358
- function createBrowserInteractionActuatorKey(input) {
359
- return [
360
- input.surface,
361
- input.scopeId,
362
- input.interactionKey,
363
- input.intent,
364
- input.inputKey ?? "",
365
- input.candidateValueKey ?? "",
366
- input.actuatorKind
367
- ].map((part) => encodeURIComponent(part)).join("|");
368
- }
369
-
370
- // src/browser-interaction/registry.ts
371
- function defineBrowserInteractionSurface(definition) {
372
- const seen = /* @__PURE__ */ new Set();
373
- for (const intent of definition.intents) {
374
- if (seen.has(intent)) {
375
- throw new Error(
376
- `Browser interaction surface '${definition.surface}' declares duplicate intent '${intent}'.`
377
- );
378
- }
379
- seen.add(intent);
380
- }
381
- return definition;
382
- }
383
- var gameplayBrowserInteractionSurface = defineBrowserInteractionSurface({
384
- surface: GAMEPLAY_BROWSER_INTERACTION_SURFACE,
385
- intents: GAMEPLAY_BROWSER_INTERACTION_INTENTS,
386
- effectKinds: GAMEPLAY_BROWSER_INTERACTION_EFFECT_KINDS
387
- });
388
- function createBrowserInteractionRegistry(definitions = [
389
- gameplayBrowserInteractionSurface
390
- ]) {
391
- const surfaces = /* @__PURE__ */ new Map();
392
- for (const definition of definitions) {
393
- const existing = surfaces.get(definition.surface);
394
- if (existing) {
395
- const existingIntents = existing.intents.join(",");
396
- const nextIntents = definition.intents.join(",");
397
- const existingEffectKinds = (existing.effectKinds ?? []).join(",");
398
- const nextEffectKinds = (definition.effectKinds ?? []).join(",");
399
- if (existingIntents !== nextIntents || existingEffectKinds !== nextEffectKinds) {
400
- throw new Error(
401
- `Browser interaction surface '${definition.surface}' is already registered with a different intent vocabulary or effect vocabulary.`
402
- );
403
- }
404
- continue;
405
- }
406
- surfaces.set(definition.surface, definition);
407
- }
408
- return { surfaces };
409
- }
410
- var defaultBrowserInteractionRegistry = createBrowserInteractionRegistry();
411
-
412
- // src/browser-interaction/normalize.ts
413
- function normalizeBrowserInteractionRecords(records, options = {}) {
414
- const registry = options.registry ?? defaultBrowserInteractionRegistry;
415
- const diagnostics = [];
416
- const surfaces = /* @__PURE__ */ new Map();
417
- const getSurface = (surface, scopeId) => {
418
- const registered = registry.surfaces.get(surface);
419
- const key = `${surface}\0${scopeId}`;
420
- const existing = surfaces.get(key);
421
- if (existing) return existing;
422
- if (!registered) {
423
- const diagnostic = diagnosticFor({
424
- code: "unknown-surface",
425
- message: `Unknown browser interaction surface '${surface}'.`,
426
- surface,
427
- scopeId
428
- });
429
- diagnostics.push(diagnostic);
430
- const pending2 = {
431
- kind: "unknown",
432
- surface,
433
- scopeId,
434
- diagnostics: [diagnostic]
435
- };
436
- surfaces.set(key, pending2);
437
- return pending2;
438
- }
439
- const pending = {
440
- kind: "semantic",
441
- surface,
442
- scopeId,
443
- interactions: /* @__PURE__ */ new Map(),
444
- diagnostics: []
445
- };
446
- surfaces.set(key, pending);
447
- return pending;
448
- };
449
- for (const record of records) {
450
- const attributes = record.attributes;
451
- const protocol = text(attributes, BROWSER_INTERACTION_ATTRIBUTES.protocol);
452
- const surface = text(attributes, BROWSER_INTERACTION_ATTRIBUTES.surface);
453
- const scopeId = text(attributes, BROWSER_INTERACTION_ATTRIBUTES.scope);
454
- const role = text(attributes, BROWSER_INTERACTION_ATTRIBUTES.role);
455
- if (protocol !== DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_VERSION) {
456
- diagnostics.push(
457
- diagnosticFor({
458
- code: "invalid-protocol",
459
- message: `Expected browser interaction protocol ${DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_VERSION}.`,
460
- surface,
461
- scopeId
462
- })
463
- );
464
- continue;
465
- }
466
- if (!surface || !scopeId || role !== "interaction" && role !== "actuator" && role !== "pointer-target") {
467
- diagnostics.push(
468
- diagnosticFor({
469
- code: "invalid-record",
470
- message: "Browser interaction records require protocol, surface, scope and role.",
471
- surface,
472
- scopeId
473
- })
474
- );
475
- continue;
476
- }
477
- const pendingSurface = getSurface(surface, scopeId);
478
- if (!pendingSurface || pendingSurface.kind !== "semantic") {
479
- continue;
480
- }
481
- const interactionKey = text(
482
- attributes,
483
- BROWSER_INTERACTION_ATTRIBUTES.interactionKey
484
- );
485
- const interactionId = text(
486
- attributes,
487
- BROWSER_INTERACTION_ATTRIBUTES.interactionId
488
- );
489
- if (!interactionKey || !interactionId) {
490
- pushSurfaceDiagnostic(
491
- pendingSurface,
492
- diagnostics,
493
- diagnosticFor({
494
- code: "invalid-record",
495
- message: "Browser interaction records require interaction key and id.",
496
- surface,
497
- scopeId
498
- })
499
- );
500
- continue;
501
- }
502
- const interaction = getInteraction(
503
- pendingSurface,
504
- interactionKey,
505
- interactionId
506
- );
507
- if (role === "interaction") {
508
- interaction.rootSeen = true;
509
- interaction.descriptorDigest = optionalText(
510
- attributes,
511
- BROWSER_INTERACTION_ATTRIBUTES.descriptorDigest
512
- );
513
- interaction.draftDigest = optionalText(
514
- attributes,
515
- BROWSER_INTERACTION_ATTRIBUTES.draftDigest
516
- );
517
- interaction.readiness = parseReadiness(
518
- text(attributes, BROWSER_INTERACTION_ATTRIBUTES.readiness)
519
- );
520
- continue;
521
- }
522
- if (role === "pointer-target") {
523
- const pointerTarget = parseGameplayPointerTarget(attributes, {
524
- surface,
525
- scopeId,
526
- interactionKey,
527
- interactionId
528
- });
529
- if (pointerTarget.diagnostics.length > 0) {
530
- interaction.diagnostics.push(...pointerTarget.diagnostics);
531
- diagnostics.push(...pointerTarget.diagnostics);
532
- }
533
- const registered2 = registry.surfaces.get(surface);
534
- if (registered2?.effectKinds) {
535
- for (const pattern of pointerTarget.acceptedEffectPatterns) {
536
- const effectKind = pattern.kind === "exact" ? pattern.effect.kind : pattern.effectKind;
537
- if (!registered2.effectKinds.includes(effectKind)) {
538
- const diagnostic = diagnosticFor({
539
- code: "unknown-surface-effect",
540
- message: `Unknown browser interaction effect '${effectKind}' for surface '${surface}'.`,
541
- surface,
542
- scopeId,
543
- interactionKey,
544
- targetId: pointerTarget.targetId
545
- });
546
- interaction.diagnostics.push(diagnostic);
547
- diagnostics.push(diagnostic);
548
- }
549
- }
550
- }
551
- interaction.pointerTargets.push(pointerTarget);
552
- continue;
553
- }
554
- const actuator = parseGameplayActuator(attributes, {
555
- surface,
556
- scopeId,
557
- interactionKey,
558
- interactionId
559
- });
560
- if (actuator.diagnostics.length > 0) {
561
- interaction.diagnostics.push(...actuator.diagnostics);
562
- diagnostics.push(...actuator.diagnostics);
563
- }
564
- const registered = registry.surfaces.get(surface);
565
- if (registered && !registered.intents.includes(actuator.intent)) {
566
- const diagnostic = diagnosticFor({
567
- code: "unknown-intent",
568
- message: `Unknown browser interaction intent '${actuator.intent}' for surface '${surface}'.`,
569
- surface,
570
- scopeId,
571
- interactionKey,
572
- intent: actuator.intent,
573
- actuatorId: actuator.actuatorId
574
- });
575
- interaction.diagnostics.push(diagnostic);
576
- diagnostics.push(diagnostic);
577
- }
578
- if (registered?.effectKinds) {
579
- for (const effect of actuator.semanticEffects) {
580
- if (!registered.effectKinds.includes(effect.kind)) {
581
- const diagnostic = diagnosticFor({
582
- code: "unknown-surface-effect",
583
- message: `Unknown browser interaction effect '${effect.kind}' for surface '${surface}'.`,
584
- surface,
585
- scopeId,
586
- interactionKey,
587
- intent: actuator.intent,
588
- actuatorId: actuator.actuatorId
589
- });
590
- interaction.diagnostics.push(diagnostic);
591
- diagnostics.push(diagnostic);
592
- }
593
- }
594
- for (const pattern of [
595
- ...actuator.acceptedEffectPatterns,
596
- ...actuator.preparationPatterns
597
- ]) {
598
- const effectKind = pattern.kind === "exact" ? pattern.effect.kind : pattern.effectKind;
599
- if (!registered.effectKinds.includes(effectKind)) {
600
- const diagnostic = diagnosticFor({
601
- code: "unknown-surface-effect",
602
- message: `Unknown browser interaction effect '${effectKind}' for surface '${surface}'.`,
603
- surface,
604
- scopeId,
605
- interactionKey,
606
- intent: actuator.intent,
607
- actuatorId: actuator.actuatorId
608
- });
609
- interaction.diagnostics.push(diagnostic);
610
- diagnostics.push(diagnostic);
611
- }
612
- }
613
- }
614
- interaction.actuators.push(actuator);
615
- }
616
- for (const surface of surfaces.values()) {
617
- if (surface.kind !== "semantic") continue;
618
- for (const interaction of surface.interactions.values()) {
619
- if (interaction.rootSeen || interaction.actuators.length === 0 && interaction.pointerTargets.length === 0) {
620
- continue;
621
- }
622
- const diagnostic = diagnosticFor({
623
- code: "orphan-actuator",
624
- message: "Browser interaction actuators and pointer targets require a rendered semantic root.",
625
- surface: surface.surface,
626
- scopeId: surface.scopeId,
627
- interactionKey: interaction.interactionKey
628
- });
629
- interaction.diagnostics.push(diagnostic);
630
- diagnostics.push(diagnostic);
631
- }
632
- }
633
- const snapshot = {
634
- protocol: {
635
- name: DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_NAME,
636
- version: DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_VERSION
637
- },
638
- surfaces: [...surfaces.values()].map(finalizeSurface).sort(compareSurface),
639
- diagnostics: []
640
- };
641
- const validationDiagnostics = validateBrowserInteractionSnapshot(snapshot);
642
- return {
643
- ...snapshot,
644
- diagnostics: [...diagnostics, ...validationDiagnostics].sort(
645
- compareDiagnostic
646
- )
647
- };
648
- }
649
- function validateBrowserInteractionSnapshot(snapshot) {
650
- const diagnostics = [];
651
- for (const surface of snapshot.surfaces) {
652
- if (!isSemanticSurfaceSnapshot(surface)) continue;
653
- for (const interaction of surface.interactions) {
654
- const enabledByKey = /* @__PURE__ */ new Map();
655
- const enabledTargetsByKey = /* @__PURE__ */ new Map();
656
- for (const actuator of interaction.actuators) {
657
- if (!actuator.enabled) continue;
658
- const key = actuatorIdentityKey({
659
- surface: surface.surface,
660
- scopeId: surface.scopeId,
661
- interactionKey: interaction.interactionKey,
662
- actuator
663
- });
664
- const group = enabledByKey.get(key) ?? [];
665
- group.push(actuator);
666
- enabledByKey.set(key, group);
667
- }
668
- for (const pointerTarget of interaction.pointerTargets) {
669
- if (!pointerTarget.enabled) continue;
670
- const key = pointerTargetIdentityKey({
671
- surface: surface.surface,
672
- scopeId: surface.scopeId,
673
- interactionKey: interaction.interactionKey,
674
- pointerTarget
675
- });
676
- const group = enabledTargetsByKey.get(key) ?? [];
677
- group.push(pointerTarget);
678
- enabledTargetsByKey.set(key, group);
679
- }
680
- for (const [key, actuators] of enabledByKey) {
681
- if (actuators.length > 1) {
682
- diagnostics.push(
683
- diagnosticFor({
684
- code: "duplicate-enabled-actuator",
685
- message: `Duplicate enabled actuator for '${key}'.`,
686
- surface: surface.surface,
687
- scopeId: surface.scopeId,
688
- interactionKey: interaction.interactionKey,
689
- intent: actuators[0]?.intent,
690
- actuatorId: actuators[0]?.actuatorId
691
- })
692
- );
693
- }
694
- }
695
- for (const [key, pointerTargets] of enabledTargetsByKey) {
696
- if (pointerTargets.length > 1) {
697
- diagnostics.push(
698
- diagnosticFor({
699
- code: "duplicate-enabled-pointer-target",
700
- message: `Duplicate enabled pointer target for '${key}'.`,
701
- surface: surface.surface,
702
- scopeId: surface.scopeId,
703
- interactionKey: interaction.interactionKey,
704
- targetId: pointerTargets[0]?.targetId
705
- })
706
- );
707
- }
708
- }
709
- diagnostics.push(
710
- ...diagnosticsForPreparationCycles(surface, interaction),
711
- ...diagnosticsForPreparationPatternAmbiguity(surface, interaction),
712
- ...diagnosticsForInvalidAcceptedPatterns(surface, interaction),
713
- ...diagnosticsForGameplayEffectCompatibility(surface, interaction)
714
- );
715
- }
716
- }
717
- return diagnostics.sort(compareDiagnostic);
718
- }
719
- function diagnosticsForInvalidAcceptedPatterns(surface, interaction) {
720
- const diagnostics = [];
721
- for (const record of [
722
- ...interaction.actuators.map((actuator) => ({
723
- patterns: actuator.acceptedEffectPatterns,
724
- intent: actuator.intent,
725
- actuatorId: actuator.actuatorId,
726
- targetId: void 0
727
- })),
728
- ...interaction.pointerTargets.map((pointerTarget) => ({
729
- patterns: pointerTarget.acceptedEffectPatterns,
730
- intent: void 0,
731
- actuatorId: void 0,
732
- targetId: pointerTarget.targetId
733
- }))
734
- ]) {
735
- for (const pattern of record.patterns) {
736
- if (pattern.kind === "match" && Object.keys(pattern.fields ?? {}).length === 0 && pattern.scalar === void 0) {
737
- diagnostics.push(
738
- diagnosticFor({
739
- code: "invalid-effect-pattern",
740
- message: "Accepted-effect match patterns must be bounded by fields or scalar constraints.",
741
- surface: surface.surface,
742
- scopeId: surface.scopeId,
743
- interactionKey: interaction.interactionKey,
744
- intent: record.intent,
745
- actuatorId: record.actuatorId,
746
- targetId: record.targetId
747
- })
748
- );
749
- }
750
- }
751
- }
752
- return diagnostics;
753
- }
754
- function diagnosticsForGameplayEffectCompatibility(surface, interaction) {
755
- if (surface.surface !== GAMEPLAY_BROWSER_INTERACTION_SURFACE) return [];
756
- const diagnostics = [];
757
- for (const actuator of interaction.actuators) {
758
- for (const effect of actuator.semanticEffects) {
759
- if (!gameplayIntentCanPerformEffect(actuator.intent, effect.kind)) {
760
- diagnostics.push(
761
- diagnosticFor({
762
- code: "effect-intent-incompatibility",
763
- message: "Gameplay browser interaction intent is incompatible with its semantic effect.",
764
- surface: surface.surface,
765
- scopeId: surface.scopeId,
766
- interactionKey: interaction.interactionKey,
767
- intent: actuator.intent,
768
- actuatorId: actuator.actuatorId
769
- })
770
- );
771
- }
772
- }
773
- }
774
- return diagnostics;
775
- }
776
- function gameplayIntentCanPerformEffect(intent, effectKind) {
777
- switch (effectKind) {
778
- case "setCandidate":
779
- return intent === "select" || intent === "toggle";
780
- case "adjustResource":
781
- return intent === "increment" || intent === "decrement";
782
- case "setScalar":
783
- return intent === "fill" || intent === "increment" || intent === "decrement";
784
- case "commit":
785
- return intent === "submit" || intent === "invoke";
786
- case "invoke":
787
- return intent === "invoke" || intent === "select" || intent === "toggle";
788
- default:
789
- return true;
790
- }
791
- }
792
- function parseGameplayActuator(attributes, context) {
793
- const diagnostics = [];
794
- const intent = text(attributes, BROWSER_INTERACTION_ATTRIBUTES.intent);
795
- const actuatorKind = parseActuatorKind(
796
- text(attributes, BROWSER_INTERACTION_ATTRIBUTES.actuatorKind)
797
- );
798
- const inputKey = optionalText(
799
- attributes,
800
- BROWSER_INTERACTION_ATTRIBUTES.inputKey
801
- );
802
- const candidate = parseCandidate(
803
- attributes,
804
- BROWSER_INTERACTION_ATTRIBUTES.candidateValue,
805
- context,
806
- intent,
807
- diagnostics
808
- );
809
- const candidateState = parseCandidateState(
810
- optionalText(attributes, BROWSER_INTERACTION_ATTRIBUTES.candidateState)
811
- );
812
- const explicitId = optionalText(
813
- attributes,
814
- BROWSER_INTERACTION_ATTRIBUTES.actuatorId
815
- );
816
- const enabled = parseEnabled(
817
- attributes[BROWSER_INTERACTION_ATTRIBUTES.enabled]
818
- );
819
- const actuatorId = explicitId ?? createBrowserInteractionActuatorKey({
820
- surface: context.surface,
821
- scopeId: context.scopeId,
822
- interactionKey: context.interactionKey,
823
- intent,
824
- inputKey,
825
- candidateValueKey: candidate?.candidateValueKey,
826
- actuatorKind
827
- });
828
- const preparesIntent = optionalText(
829
- attributes,
830
- BROWSER_INTERACTION_ATTRIBUTES.preparesIntent
831
- );
832
- const preparesCandidate = parseCandidate(
833
- attributes,
834
- BROWSER_INTERACTION_ATTRIBUTES.preparesCandidateValue,
835
- context,
836
- preparesIntent ?? intent,
837
- diagnostics
838
- );
839
- const prepares = preparesIntent === void 0 ? void 0 : {
840
- intent: preparesIntent,
841
- inputKey: optionalText(
842
- attributes,
843
- BROWSER_INTERACTION_ATTRIBUTES.preparesInputKey
844
- ),
845
- candidateValue: preparesCandidate?.candidateValue,
846
- candidateValueKey: preparesCandidate?.candidateValueKey,
847
- actuatorKind: parseOptionalActuatorKind(
848
- optionalText(
849
- attributes,
850
- BROWSER_INTERACTION_ATTRIBUTES.preparesActuatorKind
851
- )
852
- )
853
- };
854
- const semanticEffects = parseEffectList(
855
- attributes,
856
- BROWSER_INTERACTION_ATTRIBUTES.semanticEffects,
857
- context,
858
- intent,
859
- diagnostics
860
- );
861
- const acceptedEffectPatterns = parseEffectPatternList(
862
- attributes,
863
- BROWSER_INTERACTION_ATTRIBUTES.acceptedEffectPatterns,
864
- context,
865
- intent,
866
- diagnostics
867
- );
868
- const preparationPatterns = parseEffectPatternList(
869
- attributes,
870
- BROWSER_INTERACTION_ATTRIBUTES.preparationPatterns,
871
- context,
872
- intent,
873
- diagnostics
874
- );
875
- if (!intent) {
876
- diagnostics.push(
877
- diagnosticFor({
878
- code: "invalid-record",
879
- message: "Browser interaction actuator records require an intent.",
880
- ...context
881
- })
882
- );
883
- }
884
- return {
885
- actuatorId,
886
- intent,
887
- descriptorDigest: optionalText(
888
- attributes,
889
- BROWSER_INTERACTION_ATTRIBUTES.descriptorDigest
890
- ),
891
- draftDigest: optionalText(
892
- attributes,
893
- BROWSER_INTERACTION_ATTRIBUTES.draftDigest
894
- ),
895
- inputKey,
896
- candidateValue: candidate?.candidateValue,
897
- candidateValueKey: candidate?.candidateValueKey,
898
- candidateState,
899
- enabled,
900
- actuatorKind,
901
- semanticEffects,
902
- acceptedEffectPatterns,
903
- preparationPatterns,
904
- prepares,
905
- diagnostics: diagnostics.sort(compareDiagnostic)
906
- };
907
- }
908
- function parseGameplayPointerTarget(attributes, context) {
909
- const diagnostics = [];
910
- const targetId = text(
911
- attributes,
912
- BROWSER_INTERACTION_ATTRIBUTES.pointerTargetId
913
- );
914
- const enabled = parseEnabled(
915
- attributes[BROWSER_INTERACTION_ATTRIBUTES.pointerTargetEnabled]
916
- );
917
- const acceptedEffectPatterns = parseEffectPatternList(
918
- attributes,
919
- BROWSER_INTERACTION_ATTRIBUTES.acceptedEffectPatterns,
920
- context,
921
- "pointer-target",
922
- diagnostics
923
- );
924
- if (!targetId) {
925
- diagnostics.push(
926
- diagnosticFor({
927
- code: "invalid-record",
928
- message: "Browser interaction pointer target records require a target id.",
929
- ...context
930
- })
931
- );
932
- }
933
- return {
934
- targetId,
935
- descriptorDigest: optionalText(
936
- attributes,
937
- BROWSER_INTERACTION_ATTRIBUTES.descriptorDigest
938
- ),
939
- enabled,
940
- acceptedEffectPatterns,
941
- diagnostics: diagnostics.sort(compareDiagnostic)
942
- };
943
- }
944
- function parseEffectList(attributes, attribute, context, intent, diagnostics) {
945
- const encodedList = optionalText(attributes, attribute);
946
- if (encodedList === void 0) return [];
947
- try {
948
- const raw = JSON.parse(encodedList);
949
- if (!Array.isArray(raw) || raw.some((item) => typeof item !== "string")) {
950
- throw new Error("Expected encoded effect array.");
951
- }
952
- return raw.map((encoded) => decodeBrowserInteractionEffect(encoded)).sort(
953
- (a, b) => encodeBrowserInteractionEffect(a).localeCompare(
954
- encodeBrowserInteractionEffect(b)
955
- )
956
- );
957
- } catch {
958
- diagnostics.push(
959
- diagnosticFor({
960
- code: "invalid-effect-payload",
961
- message: "Invalid browser interaction semantic effect payload.",
962
- ...context,
963
- intent
964
- })
965
- );
966
- return [];
967
- }
968
- }
969
- function parseEffectPatternList(attributes, attribute, context, intent, diagnostics) {
970
- const encodedList = optionalText(attributes, attribute);
971
- if (encodedList === void 0) return [];
972
- try {
973
- const raw = JSON.parse(encodedList);
974
- if (!Array.isArray(raw) || raw.some((item) => typeof item !== "string")) {
975
- throw new Error("Expected encoded effect pattern array.");
976
- }
977
- return raw.map((encoded) => decodeBrowserInteractionEffectPattern(encoded)).sort(
978
- (a, b) => encodeBrowserInteractionEffectPattern(a).localeCompare(
979
- encodeBrowserInteractionEffectPattern(b)
980
- )
981
- );
982
- } catch {
983
- diagnostics.push(
984
- diagnosticFor({
985
- code: "invalid-effect-pattern",
986
- message: "Invalid browser interaction semantic effect pattern.",
987
- ...context,
988
- intent
989
- })
990
- );
991
- return [];
992
- }
993
- }
994
- function parseCandidate(attributes, attribute, context, intent, diagnostics) {
995
- const encoded = optionalText(attributes, attribute);
996
- if (encoded === void 0) return void 0;
997
- try {
998
- const candidateValue = decodeCanonicalCandidateValue(encoded);
999
- return {
1000
- candidateValue,
1001
- candidateValueKey: encodeCanonicalCandidateValue(candidateValue)
1002
- };
1003
- } catch {
1004
- diagnostics.push(
1005
- diagnosticFor({
1006
- code: "invalid-candidate",
1007
- message: `Invalid canonical browser interaction candidate '${encoded}'.`,
1008
- ...context,
1009
- intent
1010
- })
1011
- );
1012
- return void 0;
1013
- }
1014
- }
1015
- function getInteraction(surface, interactionKey, interactionId) {
1016
- const existing = surface.interactions.get(interactionKey);
1017
- if (existing) return existing;
1018
- const next = {
1019
- interactionKey,
1020
- interactionId,
1021
- readiness: "ready",
1022
- rootSeen: false,
1023
- actuators: [],
1024
- pointerTargets: [],
1025
- diagnostics: []
1026
- };
1027
- surface.interactions.set(interactionKey, next);
1028
- return next;
1029
- }
1030
- function finalizeSurface(surface) {
1031
- if (surface.kind !== "semantic") {
1032
- return {
1033
- surface: surface.surface,
1034
- scopeId: surface.scopeId,
1035
- diagnostics: surface.diagnostics.sort(compareDiagnostic)
1036
- };
1037
- }
1038
- const interactions = [
1039
- ...surface.interactions.values()
1040
- ].map((interaction) => ({
1041
- interactionKey: interaction.interactionKey,
1042
- interactionId: interaction.interactionId,
1043
- descriptorDigest: interaction.descriptorDigest,
1044
- draftDigest: interaction.draftDigest,
1045
- readiness: interaction.readiness,
1046
- actuators: interaction.actuators.sort(compareActuator),
1047
- pointerTargets: interaction.pointerTargets.sort(comparePointerTarget),
1048
- diagnostics: interaction.diagnostics.sort(compareDiagnostic)
1049
- })).sort((a, b) => a.interactionKey.localeCompare(b.interactionKey));
1050
- return {
1051
- surface: surface.surface,
1052
- scopeId: surface.scopeId,
1053
- interactions,
1054
- diagnostics: surface.diagnostics.sort(compareDiagnostic)
1055
- };
1056
- }
1057
- function diagnosticsForPreparationCycles(surface, interaction) {
1058
- const diagnostics = [];
1059
- diagnostics.push(
1060
- ...diagnosticsForEffectPreparationCycles(surface, interaction)
1061
- );
1062
- const actuators = /* @__PURE__ */ new Map();
1063
- for (const actuator of interaction.actuators) {
1064
- actuators.set(
1065
- actuatorIdentityKey({
1066
- surface: surface.surface,
1067
- scopeId: surface.scopeId,
1068
- interactionKey: interaction.interactionKey,
1069
- actuator
1070
- }),
1071
- actuator
1072
- );
1073
- }
1074
- for (const actuator of interaction.actuators) {
1075
- const visited = /* @__PURE__ */ new Set();
1076
- let current = actuator;
1077
- while (current?.prepares) {
1078
- const key = actuatorIdentityKey({
1079
- surface: surface.surface,
1080
- scopeId: surface.scopeId,
1081
- interactionKey: interaction.interactionKey,
1082
- actuator: current
1083
- });
1084
- if (visited.has(key)) {
1085
- diagnostics.push(
1086
- diagnosticFor({
1087
- code: "preparation-cycle",
1088
- message: `Preparation cycle detected for actuator '${current.actuatorId}'.`,
1089
- surface: surface.surface,
1090
- scopeId: surface.scopeId,
1091
- interactionKey: interaction.interactionKey,
1092
- intent: current.intent,
1093
- actuatorId: current.actuatorId
1094
- })
1095
- );
1096
- break;
1097
- }
1098
- visited.add(key);
1099
- current = actuators.get(
1100
- targetIdentityKey({
1101
- surface: surface.surface,
1102
- scopeId: surface.scopeId,
1103
- interactionKey: interaction.interactionKey,
1104
- target: current.prepares
1105
- })
1106
- );
1107
- }
1108
- }
1109
- return diagnostics;
1110
- }
1111
- function diagnosticsForEffectPreparationCycles(surface, interaction) {
1112
- const diagnostics = [];
1113
- const actuatorKeys = /* @__PURE__ */ new Map();
1114
- interaction.actuators.forEach((actuator, index) => {
1115
- actuatorKeys.set(actuator, `${actuator.actuatorId}\0${index}`);
1116
- });
1117
- for (const actuator of interaction.actuators) {
1118
- const visited = /* @__PURE__ */ new Set();
1119
- let current = actuator;
1120
- while (current) {
1121
- const key = actuatorKeys.get(current);
1122
- if (!key) break;
1123
- if (visited.has(key)) {
1124
- diagnostics.push(
1125
- diagnosticFor({
1126
- code: "preparation-cycle",
1127
- message: `Semantic preparation cycle detected for actuator '${current.actuatorId}'.`,
1128
- surface: surface.surface,
1129
- scopeId: surface.scopeId,
1130
- interactionKey: interaction.interactionKey,
1131
- intent: current.intent,
1132
- actuatorId: current.actuatorId
1133
- })
1134
- );
1135
- break;
1136
- }
1137
- visited.add(key);
1138
- current = interaction.actuators.find(
1139
- (candidate) => current?.preparationPatterns.some(
1140
- (pattern) => candidate.semanticEffects.some(
1141
- (effect) => browserInteractionEffectPatternMatches(pattern, effect)
1142
- )
1143
- )
1144
- );
1145
- }
1146
- }
1147
- return diagnostics;
1148
- }
1149
- function diagnosticsForPreparationPatternAmbiguity(surface, interaction) {
1150
- const diagnostics = [];
1151
- for (const target of interaction.actuators) {
1152
- for (const effect of target.semanticEffects) {
1153
- const matches = interaction.actuators.filter(
1154
- (actuator) => actuator.preparationPatterns.some(
1155
- (pattern) => browserInteractionEffectPatternMatches(pattern, effect)
1156
- )
1157
- );
1158
- const uniqueActuatorIds = new Set(
1159
- matches.map((match) => match.actuatorId)
1160
- );
1161
- if (uniqueActuatorIds.size > 1) {
1162
- diagnostics.push(
1163
- diagnosticFor({
1164
- code: "ambiguous-preparation-pattern",
1165
- message: "Multiple preparation patterns can prepare the same semantic effect.",
1166
- surface: surface.surface,
1167
- scopeId: surface.scopeId,
1168
- interactionKey: interaction.interactionKey,
1169
- intent: target.intent,
1170
- actuatorId: target.actuatorId
1171
- })
1172
- );
1173
- }
1174
- }
1175
- }
1176
- return diagnostics;
1177
- }
1178
- function actuatorIdentityKey(input) {
1179
- return createBrowserInteractionActuatorKey({
1180
- surface: input.surface,
1181
- scopeId: input.scopeId,
1182
- interactionKey: input.interactionKey,
1183
- intent: input.actuator.intent,
1184
- inputKey: input.actuator.inputKey,
1185
- candidateValueKey: input.actuator.candidateValueKey,
1186
- actuatorKind: input.actuator.actuatorKind
1187
- });
1188
- }
1189
- function pointerTargetIdentityKey(input) {
1190
- return [
1191
- input.surface,
1192
- input.scopeId,
1193
- input.interactionKey,
1194
- input.pointerTarget.targetId
1195
- ].map((part) => encodeURIComponent(part)).join("|");
1196
- }
1197
- function isSemanticSurfaceSnapshot(surface) {
1198
- return "interactions" in surface;
1199
- }
1200
- function targetIdentityKey(input) {
1201
- return createBrowserInteractionActuatorKey({
1202
- surface: input.surface,
1203
- scopeId: input.scopeId,
1204
- interactionKey: input.interactionKey,
1205
- intent: input.target.intent,
1206
- inputKey: input.target.inputKey,
1207
- candidateValueKey: input.target.candidateValueKey,
1208
- actuatorKind: input.target.actuatorKind ?? "click"
1209
- });
1210
- }
1211
- function pushSurfaceDiagnostic(surface, all, diagnostic) {
1212
- surface.diagnostics.push(diagnostic);
1213
- all.push(diagnostic);
1214
- }
1215
- function diagnosticFor(input) {
1216
- return { severity: "error", ...input };
1217
- }
1218
- function text(attributes, key) {
1219
- const value = attributes[key];
1220
- if (typeof value === "boolean") return value ? "true" : "false";
1221
- return value ?? "";
1222
- }
1223
- function optionalText(attributes, key) {
1224
- const value = text(attributes, key);
1225
- return value === "" ? void 0 : value;
1226
- }
1227
- function parseReadiness(value) {
1228
- return BROWSER_INTERACTION_READINESS_VALUES.includes(
1229
- value
1230
- ) ? value : "blocked";
1231
- }
1232
- function parseCandidateState(value) {
1233
- return BROWSER_INTERACTION_CANDIDATE_STATES.includes(
1234
- value
1235
- ) ? value : void 0;
1236
- }
1237
- function parseActuatorKind(value) {
1238
- return parseOptionalActuatorKind(value) ?? "click";
1239
- }
1240
- function parseOptionalActuatorKind(value) {
1241
- return BROWSER_INTERACTION_ACTUATOR_KINDS.includes(
1242
- value
1243
- ) ? value : void 0;
1244
- }
1245
- function parseEnabled(value) {
1246
- if (value === false || value === "false") return false;
1247
- return true;
1248
- }
1249
- function compareSurface(a, b) {
1250
- return a.surface.localeCompare(b.surface) || a.scopeId.localeCompare(b.scopeId);
1251
- }
1252
- function compareActuator(a, b) {
1253
- return a.actuatorId.localeCompare(b.actuatorId);
1254
- }
1255
- function comparePointerTarget(a, b) {
1256
- return a.targetId.localeCompare(b.targetId);
1257
- }
1258
- function compareDiagnostic(a, b) {
1259
- return a.code.localeCompare(b.code) || (a.surface ?? "").localeCompare(b.surface ?? "") || (a.scopeId ?? "").localeCompare(b.scopeId ?? "") || (a.interactionKey ?? "").localeCompare(b.interactionKey ?? "") || (a.intent ?? "").localeCompare(b.intent ?? "") || (a.actuatorId ?? "").localeCompare(b.actuatorId ?? "") || a.message.localeCompare(b.message);
1260
- }
1261
-
1262
- // src/browser-interaction/resolve.ts
1263
- function resolveBrowserInteractionIntent(snapshot, request) {
1264
- const snapshotDiagnostics = [
1265
- ...snapshot.diagnostics,
1266
- ...validateBrowserInteractionSnapshot(snapshot)
1267
- ];
1268
- if (snapshotDiagnostics.some((diagnostic) => diagnostic.severity === "error")) {
1269
- return {
1270
- ok: false,
1271
- code: "invalid-snapshot",
1272
- diagnostics: snapshotDiagnostics
1273
- };
1274
- }
1275
- const candidateValueKey = request.candidateValueKey ?? ("candidateValue" in request ? encodeCanonicalCandidateValue(request.candidateValue) : void 0);
1276
- const surfaces = snapshot.surfaces.filter(
1277
- (surface) => isSemanticSurfaceSnapshot(surface) && surface.surface === request.surface && (request.scopeId === void 0 || surface.scopeId === request.scopeId)
1278
- );
1279
- const matches = surfaces.flatMap(
1280
- (surface) => surface.interactions.flatMap((interaction) => {
1281
- if (request.interactionKey !== void 0 && interaction.interactionKey !== request.interactionKey) {
1282
- return [];
1283
- }
1284
- if (request.interactionId !== void 0 && interaction.interactionId !== request.interactionId) {
1285
- return [];
1286
- }
1287
- return interaction.actuators.filter(
1288
- (actuator) => actuatorMatchesRequest(actuator, request, candidateValueKey)
1289
- ).map((actuator) => ({ surface, interaction, actuator }));
1290
- })
1291
- );
1292
- const enabledMatches = matches.filter((match) => match.actuator.enabled);
1293
- const actionable = request.allowDisabled === true ? matches : enabledMatches;
1294
- if (actionable.length === 1) {
1295
- const match = actionable[0];
1296
- if (!match) throw new Error("unreachable browser interaction match");
1297
- return {
1298
- ok: true,
1299
- actuator: match.actuator,
1300
- surface: match.surface.surface,
1301
- scopeId: match.surface.scopeId,
1302
- interactionKey: match.interaction.interactionKey,
1303
- diagnostics: []
1304
- };
1305
- }
1306
- if (actionable.length > 1) {
1307
- return {
1308
- ok: false,
1309
- code: "ambiguous",
1310
- diagnostics: actionable.map(
1311
- (match) => diagnosticFor2({
1312
- code: "ambiguous-actuator",
1313
- message: "Browser interaction intent resolved to multiple actuators.",
1314
- surface: match.surface.surface,
1315
- scopeId: match.surface.scopeId,
1316
- interactionKey: match.interaction.interactionKey,
1317
- intent: match.actuator.intent,
1318
- actuatorId: match.actuator.actuatorId
1319
- })
1320
- )
1321
- };
1322
- }
1323
- const preparation = findPreparationChain(
1324
- surfaces,
1325
- request,
1326
- candidateValueKey
1327
- );
1328
- if (preparation.length > 0) {
1329
- return {
1330
- ok: false,
1331
- code: "preparation-required",
1332
- diagnostics: [
1333
- diagnosticFor2({
1334
- code: "unavailable-actuator",
1335
- message: "Requested browser interaction intent requires preparation before its actuator is available.",
1336
- surface: request.surface,
1337
- scopeId: request.scopeId,
1338
- interactionKey: request.interactionKey,
1339
- intent: request.intent
1340
- })
1341
- ],
1342
- preparation
1343
- };
1344
- }
1345
- return {
1346
- ok: false,
1347
- code: matches.length > 0 ? "unavailable" : "not-found",
1348
- diagnostics: [
1349
- diagnosticFor2({
1350
- code: "unavailable-actuator",
1351
- message: matches.length > 0 ? "Browser interaction intent exists but has no enabled actuator." : "Browser interaction intent is not present in the current snapshot.",
1352
- surface: request.surface,
1353
- scopeId: request.scopeId,
1354
- interactionKey: request.interactionKey,
1355
- intent: request.intent
1356
- })
1357
- ]
1358
- };
1359
- }
1360
- function resolveBrowserInteractionEffect(snapshot, request) {
1361
- const snapshotDiagnostics = [
1362
- ...snapshot.diagnostics,
1363
- ...validateBrowserInteractionSnapshot(snapshot)
1364
- ];
1365
- if (snapshotDiagnostics.some((diagnostic) => diagnostic.severity === "error")) {
1366
- return {
1367
- ok: false,
1368
- code: "invalid-snapshot",
1369
- diagnostics: snapshotDiagnostics
1370
- };
1371
- }
1372
- const effectDiagnostics = validateEffectRequest(request.effect, request);
1373
- if (effectDiagnostics.length > 0) {
1374
- return {
1375
- ok: false,
1376
- code: "invalid-effect",
1377
- diagnostics: effectDiagnostics
1378
- };
1379
- }
1380
- const surfaces = matchingSurfaces(snapshot, request);
1381
- const matches = collectInteractionMatches(surfaces, request);
1382
- const exactMatches = matches.filter(
1383
- (match) => match.actuator.semanticEffects.some(
1384
- (effect) => encodeBrowserInteractionEffect(effect) === encodeBrowserInteractionEffect(request.effect)
1385
- )
1386
- );
1387
- const enabledExactMatches = exactMatches.filter(
1388
- (match) => match.actuator.enabled
1389
- );
1390
- const actionableExactMatches = request.allowDisabled === true ? exactMatches : enabledExactMatches;
1391
- if (actionableExactMatches.length === 1) {
1392
- const match = actionableExactMatches[0];
1393
- if (!match) throw new Error("unreachable browser effect match");
1394
- return effectSuccess(match, request.effect, "exact");
1395
- }
1396
- if (actionableExactMatches.length > 1) {
1397
- return effectAmbiguous(
1398
- actionableExactMatches,
1399
- "duplicate-enabled-effect-actuator",
1400
- "Browser interaction effect resolved to multiple exact actuators."
1401
- );
1402
- }
1403
- if (exactMatches.length > 0 && enabledExactMatches.length === 0) {
1404
- return effectUnavailable(
1405
- request,
1406
- "disabled-effect-actuator",
1407
- "Browser interaction effect exists but has no enabled actuator."
1408
- );
1409
- }
1410
- const acceptedMatches = matches.filter(
1411
- (match) => match.actuator.acceptedEffectPatterns.some(
1412
- (pattern) => browserInteractionEffectPatternMatches(pattern, request.effect)
1413
- )
1414
- );
1415
- const enabledAcceptedMatches = acceptedMatches.filter(
1416
- (match) => match.actuator.enabled
1417
- );
1418
- const actionableAcceptedMatches = request.allowDisabled === true ? acceptedMatches : enabledAcceptedMatches;
1419
- if (actionableAcceptedMatches.length === 1) {
1420
- const match = actionableAcceptedMatches[0];
1421
- if (!match) throw new Error("unreachable browser accepted effect match");
1422
- return effectSuccess(match, request.effect, "accepted-pattern");
1423
- }
1424
- if (actionableAcceptedMatches.length > 1) {
1425
- return effectAmbiguous(
1426
- actionableAcceptedMatches,
1427
- "duplicate-accepted-effect-pattern-match",
1428
- "Browser interaction effect matched multiple accepted-effect patterns."
1429
- );
1430
- }
1431
- if (acceptedMatches.length > 0 && enabledAcceptedMatches.length === 0) {
1432
- return effectUnavailable(
1433
- request,
1434
- "disabled-effect-actuator",
1435
- "Browser interaction effect pattern exists but has no enabled actuator."
1436
- );
1437
- }
1438
- const preparation = findEffectPreparationChain(surfaces, request);
1439
- if (preparation.ok) {
1440
- return {
1441
- ok: false,
1442
- code: "preparation-required",
1443
- diagnostics: [
1444
- diagnosticFor2({
1445
- code: "unavailable-actuator",
1446
- message: "Requested browser interaction effect requires preparation before its actuator is available.",
1447
- surface: request.surface,
1448
- scopeId: request.scopeId,
1449
- interactionKey: request.interactionKey
1450
- })
1451
- ],
1452
- preparation: preparation.preparation
1453
- };
1454
- }
1455
- if (preparation.diagnostics.length > 0) {
1456
- return {
1457
- ok: false,
1458
- code: "ambiguous",
1459
- diagnostics: preparation.diagnostics
1460
- };
1461
- }
1462
- return {
1463
- ok: false,
1464
- code: "not-found",
1465
- diagnostics: [
1466
- diagnosticFor2({
1467
- code: "missing-effect",
1468
- message: "Browser interaction effect is not present in the current snapshot.",
1469
- surface: request.surface,
1470
- scopeId: request.scopeId,
1471
- interactionKey: request.interactionKey
1472
- })
1473
- ]
1474
- };
1475
- }
1476
- function resolveBrowserPointerTarget(snapshot, request) {
1477
- const snapshotDiagnostics = [
1478
- ...snapshot.diagnostics,
1479
- ...validateBrowserInteractionSnapshot(snapshot)
1480
- ];
1481
- if (snapshotDiagnostics.some((diagnostic) => diagnostic.severity === "error")) {
1482
- return {
1483
- ok: false,
1484
- code: "invalid-snapshot",
1485
- diagnostics: snapshotDiagnostics
1486
- };
1487
- }
1488
- const effectDiagnostics = validateEffectRequest(request.effect, request);
1489
- if (effectDiagnostics.length > 0) {
1490
- return {
1491
- ok: false,
1492
- code: "invalid-effect",
1493
- diagnostics: effectDiagnostics
1494
- };
1495
- }
1496
- const surfaces = matchingSurfaces(snapshot, request);
1497
- const matches = collectPointerTargetMatches(surfaces, request).filter(
1498
- (match) => match.pointerTarget.acceptedEffectPatterns.some(
1499
- (pattern) => browserInteractionEffectPatternMatches(pattern, request.effect)
1500
- )
1501
- );
1502
- const enabledMatches = matches.filter((match) => match.pointerTarget.enabled);
1503
- const actionableMatches = request.allowDisabled === true ? matches : enabledMatches;
1504
- if (actionableMatches.length === 1) {
1505
- const match = actionableMatches[0];
1506
- if (!match) throw new Error("unreachable browser pointer target match");
1507
- return {
1508
- ok: true,
1509
- pointerTarget: match.pointerTarget,
1510
- surface: match.surface.surface,
1511
- scopeId: match.surface.scopeId,
1512
- interactionKey: match.interaction.interactionKey,
1513
- match: "accepted-pattern",
1514
- effect: request.effect,
1515
- diagnostics: []
1516
- };
1517
- }
1518
- if (actionableMatches.length > 1) {
1519
- return {
1520
- ok: false,
1521
- code: "ambiguous",
1522
- diagnostics: actionableMatches.map(
1523
- (match) => diagnosticFor2({
1524
- code: "ambiguous-pointer-target",
1525
- message: "Browser interaction effect matched multiple pointer targets.",
1526
- surface: match.surface.surface,
1527
- scopeId: match.surface.scopeId,
1528
- interactionKey: match.interaction.interactionKey,
1529
- targetId: match.pointerTarget.targetId
1530
- })
1531
- )
1532
- };
1533
- }
1534
- if (matches.length > 0 && enabledMatches.length === 0) {
1535
- return {
1536
- ok: false,
1537
- code: "unavailable",
1538
- diagnostics: [
1539
- diagnosticFor2({
1540
- code: "disabled-pointer-target",
1541
- message: "Browser interaction pointer target exists but is not enabled.",
1542
- surface: request.surface,
1543
- scopeId: request.scopeId,
1544
- interactionKey: request.interactionKey
1545
- })
1546
- ]
1547
- };
1548
- }
1549
- return {
1550
- ok: false,
1551
- code: "not-found",
1552
- diagnostics: [
1553
- diagnosticFor2({
1554
- code: "missing-effect",
1555
- message: "Browser interaction effect is not accepted by any pointer target.",
1556
- surface: request.surface,
1557
- scopeId: request.scopeId,
1558
- interactionKey: request.interactionKey
1559
- })
1560
- ]
1561
- };
1562
- }
1563
- function actuatorMatchesRequest(actuator, request, candidateValueKey) {
1564
- return actuator.intent === request.intent && (request.inputKey === void 0 || actuator.inputKey === request.inputKey) && (candidateValueKey === void 0 || actuator.candidateValueKey === candidateValueKey) && (request.actuatorKind === void 0 || actuator.actuatorKind === request.actuatorKind);
1565
- }
1566
- function findPreparationChain(surfaces, request, candidateValueKey) {
1567
- const targetRequest = {
1568
- ...request,
1569
- candidateValueKey
1570
- };
1571
- const preparationMatches = surfaces.flatMap(
1572
- (surface) => surface.interactions.flatMap((interaction) => {
1573
- if (!interactionMatchesRequest(interaction, request)) return [];
1574
- return interaction.actuators.filter(
1575
- (actuator) => actuator.enabled && actuator.prepares && actuator.prepares.intent === targetRequest.intent && (targetRequest.inputKey === void 0 || actuator.prepares.inputKey === targetRequest.inputKey) && (targetRequest.candidateValueKey === void 0 || actuator.prepares.candidateValueKey === targetRequest.candidateValueKey) && (targetRequest.actuatorKind === void 0 || actuator.prepares.actuatorKind === targetRequest.actuatorKind)
1576
- ).map((actuator) => ({ surface, interaction, actuator }));
1577
- })
1578
- );
1579
- if (preparationMatches.length !== 1) return [];
1580
- const match = preparationMatches[0];
1581
- if (!match) return [];
1582
- return expandPreparationChain(
1583
- match.surface,
1584
- match.interaction,
1585
- match.actuator
1586
- );
1587
- }
1588
- function expandPreparationChain(surface, interaction, actuator) {
1589
- const byKey = /* @__PURE__ */ new Map();
1590
- for (const candidate of interaction.actuators) {
1591
- byKey.set(
1592
- actuatorIdentityKey({
1593
- surface: surface.surface,
1594
- scopeId: surface.scopeId,
1595
- interactionKey: interaction.interactionKey,
1596
- actuator: candidate
1597
- }),
1598
- candidate
1599
- );
1600
- }
1601
- const chain = [];
1602
- const visited = /* @__PURE__ */ new Set();
1603
- let current = actuator;
1604
- while (current) {
1605
- const key = actuatorIdentityKey({
1606
- surface: surface.surface,
1607
- scopeId: surface.scopeId,
1608
- interactionKey: interaction.interactionKey,
1609
- actuator: current
1610
- });
1611
- if (visited.has(key)) return [];
1612
- visited.add(key);
1613
- chain.push(current);
1614
- current = current.prepares ? byKey.get(
1615
- targetIdentityKey({
1616
- surface: surface.surface,
1617
- scopeId: surface.scopeId,
1618
- interactionKey: interaction.interactionKey,
1619
- target: current.prepares
1620
- })
1621
- ) : void 0;
1622
- }
1623
- return chain;
1624
- }
1625
- function interactionMatchesRequest(interaction, request) {
1626
- return (request.interactionKey === void 0 || interaction.interactionKey === request.interactionKey) && (request.interactionId === void 0 || interaction.interactionId === request.interactionId);
1627
- }
1628
- function matchingSurfaces(snapshot, request) {
1629
- return snapshot.surfaces.filter(
1630
- (surface) => isSemanticSurfaceSnapshot(surface) && surface.surface === request.surface && (request.scopeId === void 0 || surface.scopeId === request.scopeId)
1631
- );
1632
- }
1633
- function collectInteractionMatches(surfaces, request) {
1634
- return surfaces.flatMap(
1635
- (surface) => surface.interactions.flatMap((interaction) => {
1636
- if (request.interactionKey !== void 0 && interaction.interactionKey !== request.interactionKey) {
1637
- return [];
1638
- }
1639
- if (request.interactionId !== void 0 && interaction.interactionId !== request.interactionId) {
1640
- return [];
1641
- }
1642
- return interaction.actuators.map((actuator) => ({
1643
- surface,
1644
- interaction,
1645
- actuator
1646
- }));
1647
- })
1648
- );
1649
- }
1650
- function collectPointerTargetMatches(surfaces, request) {
1651
- return surfaces.flatMap(
1652
- (surface) => surface.interactions.flatMap((interaction) => {
1653
- if (request.interactionKey !== void 0 && interaction.interactionKey !== request.interactionKey) {
1654
- return [];
1655
- }
1656
- if (request.interactionId !== void 0 && interaction.interactionId !== request.interactionId) {
1657
- return [];
1658
- }
1659
- return interaction.pointerTargets.map((pointerTarget) => ({
1660
- surface,
1661
- interaction,
1662
- pointerTarget
1663
- }));
1664
- })
1665
- );
1666
- }
1667
- function effectSuccess(match, effect, kind) {
1668
- return {
1669
- ok: true,
1670
- actuator: match.actuator,
1671
- surface: match.surface.surface,
1672
- scopeId: match.surface.scopeId,
1673
- interactionKey: match.interaction.interactionKey,
1674
- match: kind,
1675
- effect,
1676
- diagnostics: []
1677
- };
1678
- }
1679
- function effectAmbiguous(matches, code, message) {
1680
- return {
1681
- ok: false,
1682
- code: "ambiguous",
1683
- diagnostics: matches.map(
1684
- (match) => diagnosticFor2({
1685
- code,
1686
- message,
1687
- surface: match.surface.surface,
1688
- scopeId: match.surface.scopeId,
1689
- interactionKey: match.interaction.interactionKey,
1690
- intent: match.actuator.intent,
1691
- actuatorId: match.actuator.actuatorId
1692
- })
1693
- )
1694
- };
1695
- }
1696
- function effectUnavailable(request, code, message) {
1697
- return {
1698
- ok: false,
1699
- code: "unavailable",
1700
- diagnostics: [
1701
- diagnosticFor2({
1702
- code,
1703
- message,
1704
- surface: request.surface,
1705
- scopeId: request.scopeId,
1706
- interactionKey: request.interactionKey
1707
- })
1708
- ]
1709
- };
1710
- }
1711
- function findEffectPreparationChain(surfaces, request) {
1712
- const matches = collectInteractionMatches(surfaces, request).filter(
1713
- (match) => match.actuator.preparationPatterns.some(
1714
- (pattern) => browserInteractionEffectPatternMatches(pattern, request.effect)
1715
- )
1716
- );
1717
- const enabledMatches = matches.filter((match) => match.actuator.enabled);
1718
- if (enabledMatches.length === 1) {
1719
- const match = enabledMatches[0];
1720
- if (!match) return { ok: false, diagnostics: [] };
1721
- return { ok: true, preparation: [match.actuator] };
1722
- }
1723
- if (enabledMatches.length > 1) {
1724
- return {
1725
- ok: false,
1726
- diagnostics: enabledMatches.map(
1727
- (match) => diagnosticFor2({
1728
- code: "ambiguous-preparation-pattern",
1729
- message: "Browser interaction effect resolved to multiple preparation actuators.",
1730
- surface: match.surface.surface,
1731
- scopeId: match.surface.scopeId,
1732
- interactionKey: match.interaction.interactionKey,
1733
- intent: match.actuator.intent,
1734
- actuatorId: match.actuator.actuatorId
1735
- })
1736
- )
1737
- };
1738
- }
1739
- return { ok: false, diagnostics: [] };
1740
- }
1741
- function validateEffectRequest(effect, request) {
1742
- if (!effect || typeof effect.kind !== "string" || effect.kind.length === 0) {
1743
- return [
1744
- diagnosticFor2({
1745
- code: "invalid-effect-payload",
1746
- message: "Browser interaction effect requires a string kind.",
1747
- surface: request.surface,
1748
- scopeId: request.scopeId,
1749
- interactionKey: request.interactionKey
1750
- })
1751
- ];
1752
- }
1753
- if (effect.kind === "setScalar") {
1754
- const value = effect.value;
1755
- if (typeof value !== "number" || !Number.isFinite(value)) {
1756
- return [
1757
- diagnosticFor2({
1758
- code: "invalid-scalar-argument",
1759
- message: "setScalar browser interaction effects require a finite value.",
1760
- surface: request.surface,
1761
- scopeId: request.scopeId,
1762
- interactionKey: request.interactionKey
1763
- })
1764
- ];
1765
- }
1766
- }
1767
- return [];
1768
- }
1769
- function diagnosticFor2(input) {
1770
- return { severity: "error", ...input };
1771
- }
1772
-
1773
- // src/browser-interaction/dom.ts
1774
- function readBrowserInteractionSnapshot(root) {
1775
- const protocol = BROWSER_INTERACTION_ATTRIBUTES.protocol;
1776
- const role = BROWSER_INTERACTION_ATTRIBUTES.role;
1777
- const selector = [
1778
- `[${protocol}="${DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_VERSION}"][${role}="interaction"]`,
1779
- `[${protocol}="${DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_VERSION}"][${role}="actuator"]`,
1780
- `[${protocol}="${DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_VERSION}"][${role}="pointer-target"]`
1781
- ].join(",");
1782
- const records = [...root.querySelectorAll(selector)].map((element) => ({
1783
- attributes: Object.fromEntries(
1784
- [...element.attributes].map((attribute) => [
1785
- attribute.name,
1786
- attribute.value
1787
- ])
1788
- )
1789
- }));
1790
- return normalizeBrowserInteractionRecords(records);
1791
- }
1792
-
1793
- // src/browser-interaction/schemas.ts
1794
- import { z } from "zod";
1795
- var jsonValueSchema = z.preprocess((value) => {
1796
- if (value === void 0) return value;
1797
- assertJsonWithinLimits(
1798
- value,
1799
- BROWSER_ATTRIBUTE_JSON_LIMITS,
1800
- "Browser interaction attribute"
1801
- );
1802
- return value;
1803
- }, RuntimeJsonSchema);
1804
- var browserInteractionEffectSchema = z.object({
1805
- kind: z.string().min(1)
1806
- }).catchall(jsonValueSchema);
1807
- var browserInteractionEffectPatternSchema = z.union([
1808
- z.object({
1809
- kind: z.literal("exact"),
1810
- effect: browserInteractionEffectSchema
1811
- }),
1812
- z.object({
1813
- kind: z.literal("match"),
1814
- effectKind: z.string().min(1),
1815
- fields: z.record(z.string(), jsonValueSchema).optional(),
1816
- scalar: z.object({
1817
- field: z.string().min(1),
1818
- min: z.number().finite().optional(),
1819
- max: z.number().finite().optional(),
1820
- integer: z.boolean().optional()
1821
- }).optional()
1822
- })
1823
- ]);
1824
- var gameplaySemanticEffectSchema = z.union([
1825
- z.object({
1826
- kind: z.literal("setCandidate"),
1827
- inputKey: z.string(),
1828
- candidateValue: jsonValueSchema,
1829
- beforeSelected: z.boolean(),
1830
- afterSelected: z.boolean()
1831
- }),
1832
- z.object({
1833
- kind: z.literal("adjustResource"),
1834
- inputKey: z.string(),
1835
- resourceKey: jsonValueSchema,
1836
- delta: z.union([z.literal(-1), z.literal(1)])
1837
- }),
1838
- z.object({
1839
- kind: z.literal("setScalar"),
1840
- inputKey: z.string(),
1841
- value: z.number().finite()
1842
- }),
1843
- z.object({
1844
- kind: z.literal("commit")
1845
- }),
1846
- z.object({
1847
- kind: z.literal("invoke")
1848
- })
1849
- ]);
1850
- var browserInteractionDiagnosticSchema = z.object({
1851
- code: z.string(),
1852
- severity: z.enum(["error", "warning"]),
1853
- message: z.string(),
1854
- surface: z.string().optional(),
1855
- scopeId: z.string().optional(),
1856
- interactionKey: z.string().optional(),
1857
- intent: z.string().optional(),
1858
- actuatorId: z.string().optional(),
1859
- targetId: z.string().optional()
1860
- });
1861
- var browserInteractionActuatorSchema = z.object({
1862
- actuatorId: z.string(),
1863
- intent: z.string(),
1864
- inputKey: z.string().optional(),
1865
- candidateValue: jsonValueSchema.optional(),
1866
- candidateValueKey: z.string().optional(),
1867
- candidateState: z.enum(BROWSER_INTERACTION_CANDIDATE_STATES).optional(),
1868
- enabled: z.boolean(),
1869
- actuatorKind: z.enum(BROWSER_INTERACTION_ACTUATOR_KINDS),
1870
- semanticEffects: z.array(browserInteractionEffectSchema),
1871
- acceptedEffectPatterns: z.array(browserInteractionEffectPatternSchema),
1872
- preparationPatterns: z.array(browserInteractionEffectPatternSchema),
1873
- prepares: z.object({
1874
- intent: z.string(),
1875
- inputKey: z.string().optional(),
1876
- candidateValue: jsonValueSchema.optional(),
1877
- candidateValueKey: z.string().optional(),
1878
- actuatorKind: z.enum(BROWSER_INTERACTION_ACTUATOR_KINDS).optional()
1879
- }).optional(),
1880
- diagnostics: z.array(browserInteractionDiagnosticSchema)
1881
- });
1882
- var browserInteractionPointerTargetSchema = z.object({
1883
- targetId: z.string(),
1884
- enabled: z.boolean(),
1885
- acceptedEffectPatterns: z.array(browserInteractionEffectPatternSchema),
1886
- descriptorDigest: z.string().optional(),
1887
- diagnostics: z.array(browserInteractionDiagnosticSchema)
1888
- });
1889
- var browserGameplayInteractionSchema = z.object({
1890
- interactionKey: z.string(),
1891
- interactionId: z.string(),
1892
- descriptorDigest: z.string().optional(),
1893
- draftDigest: z.string().optional(),
1894
- readiness: z.enum(BROWSER_INTERACTION_READINESS_VALUES),
1895
- actuators: z.array(browserInteractionActuatorSchema),
1896
- pointerTargets: z.array(browserInteractionPointerTargetSchema),
1897
- diagnostics: z.array(browserInteractionDiagnosticSchema)
1898
- });
1899
- var browserGameplaySurfaceSnapshotSchema = z.object({
1900
- surface: z.literal(GAMEPLAY_BROWSER_INTERACTION_SURFACE),
1901
- scopeId: z.string(),
1902
- interactions: z.array(browserGameplayInteractionSchema),
1903
- diagnostics: z.array(browserInteractionDiagnosticSchema)
1904
- });
1905
- var browserSemanticSurfaceSnapshotSchema = z.object({
1906
- surface: z.string(),
1907
- scopeId: z.string(),
1908
- interactions: z.array(browserGameplayInteractionSchema),
1909
- diagnostics: z.array(browserInteractionDiagnosticSchema)
1910
- });
1911
- var browserUnknownSurfaceSnapshotSchema = z.object({
1912
- surface: z.string(),
1913
- scopeId: z.string(),
1914
- diagnostics: z.array(browserInteractionDiagnosticSchema)
1915
- });
1916
- var browserInteractionSnapshotSchema = z.object({
1917
- protocol: z.object({
1918
- name: z.literal(DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_NAME),
1919
- version: z.literal(DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_VERSION)
1920
- }),
1921
- surfaces: z.array(
1922
- z.union([
1923
- browserSemanticSurfaceSnapshotSchema,
1924
- browserUnknownSurfaceSnapshotSchema
1925
- ])
1926
- ),
1927
- diagnostics: z.array(browserInteractionDiagnosticSchema)
1928
- });
1929
-
1930
- export {
1931
- DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_NAME,
1932
- DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_VERSION,
1933
- BROWSER_INTERACTION_ATTRIBUTES,
1934
- BROWSER_INTERACTION_RECORD_ROLES,
1935
- GAMEPLAY_BROWSER_INTERACTION_SURFACE,
1936
- GAMEPLAY_BROWSER_INTERACTION_INTENTS,
1937
- GAMEPLAY_BROWSER_INTERACTION_EFFECT_KINDS,
1938
- BROWSER_INTERACTION_READINESS_VALUES,
1939
- BROWSER_INTERACTION_CANDIDATE_STATES,
1940
- BROWSER_INTERACTION_ACTUATOR_KINDS,
1941
- encodeCanonicalCandidateValue,
1942
- decodeCanonicalCandidateValue,
1943
- compareStableJson,
1944
- encodeBrowserInteractionEffect,
1945
- decodeBrowserInteractionEffect,
1946
- encodeBrowserInteractionEffectPattern,
1947
- decodeBrowserInteractionEffectPattern,
1948
- browserInteractionEffectPatternMatches,
1949
- assertBrowserInteractionEffectPattern,
1950
- gameplaySetCandidateEffect,
1951
- gameplayAdjustResourceEffect,
1952
- gameplaySetScalarEffect,
1953
- gameplayCommitEffect,
1954
- gameplayInvokeEffect,
1955
- createBrowserInteractionRootAttributes,
1956
- createBrowserInteractionActuatorAttributes,
1957
- createBrowserInteractionPointerTargetAttributes,
1958
- createGameplayInteractionRootAttributes,
1959
- createGameplayActuatorAttributes,
1960
- createGameplayPointerTargetAttributes,
1961
- createBrowserInteractionActuatorKey,
1962
- defineBrowserInteractionSurface,
1963
- gameplayBrowserInteractionSurface,
1964
- createBrowserInteractionRegistry,
1965
- defaultBrowserInteractionRegistry,
1966
- normalizeBrowserInteractionRecords,
1967
- validateBrowserInteractionSnapshot,
1968
- actuatorIdentityKey,
1969
- pointerTargetIdentityKey,
1970
- isSemanticSurfaceSnapshot,
1971
- targetIdentityKey,
1972
- resolveBrowserInteractionIntent,
1973
- resolveBrowserInteractionEffect,
1974
- resolveBrowserPointerTarget,
1975
- readBrowserInteractionSnapshot,
1976
- browserInteractionEffectSchema,
1977
- browserInteractionEffectPatternSchema,
1978
- gameplaySemanticEffectSchema,
1979
- browserInteractionDiagnosticSchema,
1980
- browserInteractionActuatorSchema,
1981
- browserInteractionPointerTargetSchema,
1982
- browserGameplayInteractionSchema,
1983
- browserGameplaySurfaceSnapshotSchema,
1984
- browserSemanticSurfaceSnapshotSchema,
1985
- browserUnknownSurfaceSnapshotSchema,
1986
- browserInteractionSnapshotSchema
1987
- };
1988
- //# sourceMappingURL=chunk-WHR5UW3F.js.map