@tileflow/interactions 0.0.0-bootstrap.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,639 @@
1
+ // src/contracts.ts
2
+ import { z } from "zod/mini";
3
+ var tileflowInteractionSchemaVersion = 1;
4
+ var tileflowPoiCategories = [
5
+ "arts-entertainment",
6
+ "education",
7
+ "food-drink",
8
+ "landmark",
9
+ "lodging",
10
+ "medical",
11
+ "park-nature",
12
+ "public-services",
13
+ "religion",
14
+ "retail",
15
+ "sport-leisure",
16
+ "transport",
17
+ "visitor-amenity"
18
+ ];
19
+ var tileflowInteractionLimits = {
20
+ maxAnnotations: 1e3,
21
+ maxAriaLabelLength: 256,
22
+ maxBindings: 100,
23
+ maxCategories: 32,
24
+ maxColorLength: 64,
25
+ maxContentTextLength: 4096,
26
+ maxDocumentBytes: 256e3,
27
+ maxDocumentDepth: 64,
28
+ maxDocumentNodes: 2e4,
29
+ maxDocumentProperties: 5e4,
30
+ maxFeatureIdLength: 128,
31
+ maxFieldSelectorLength: 128,
32
+ maxIdLength: 64,
33
+ maxTargetNameLength: 128,
34
+ maxViewNameLength: 64
35
+ };
36
+ function auditTileflowInteractionJsonDocument(input) {
37
+ try {
38
+ const stack = [{ depth: 0, value: input }];
39
+ const seen = /* @__PURE__ */ new WeakSet();
40
+ if (isObject(input)) seen.add(input);
41
+ let bytes = 0;
42
+ let nodes = 1;
43
+ let properties = 0;
44
+ const consumeBytes = (addition) => {
45
+ if (!Number.isSafeInteger(addition) || addition < 0 || addition > tileflowInteractionLimits.maxDocumentBytes - bytes) {
46
+ return false;
47
+ }
48
+ bytes += addition;
49
+ return true;
50
+ };
51
+ const enqueue = (value, depth) => {
52
+ if (depth > tileflowInteractionLimits.maxDocumentDepth) return "depth";
53
+ if (isObject(value)) {
54
+ if (seen.has(value)) return "invalid";
55
+ seen.add(value);
56
+ }
57
+ stack.push({ depth, value });
58
+ return void 0;
59
+ };
60
+ while (stack.length > 0) {
61
+ const entry = stack.pop();
62
+ if (!entry) return { ok: false, reason: "invalid" };
63
+ const { depth, value } = entry;
64
+ if (value === null) {
65
+ if (!consumeBytes(4)) return { ok: false, reason: "bytes" };
66
+ continue;
67
+ }
68
+ if (typeof value === "boolean") {
69
+ if (!consumeBytes(value ? 4 : 5)) return { ok: false, reason: "bytes" };
70
+ continue;
71
+ }
72
+ if (typeof value === "number") {
73
+ if (!Number.isFinite(value)) return { ok: false, reason: "invalid" };
74
+ const length = value === 0 ? 1 : String(value).length;
75
+ if (!consumeBytes(length)) return { ok: false, reason: "bytes" };
76
+ continue;
77
+ }
78
+ if (typeof value === "string") {
79
+ const length = jsonStringByteLength(
80
+ value,
81
+ tileflowInteractionLimits.maxDocumentBytes - bytes
82
+ );
83
+ if (length === void 0) return { ok: false, reason: "bytes" };
84
+ bytes += length;
85
+ continue;
86
+ }
87
+ if (!isObject(value)) return { ok: false, reason: "invalid" };
88
+ const array = Array.isArray(value);
89
+ const prototype = Object.getPrototypeOf(value);
90
+ if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) {
91
+ return { ok: false, reason: "invalid" };
92
+ }
93
+ if (array) {
94
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
95
+ if (!lengthDescriptor || !("value" in lengthDescriptor) || typeof lengthDescriptor.value !== "number" || !Number.isSafeInteger(lengthDescriptor.value) || lengthDescriptor.value < 0) {
96
+ return { ok: false, reason: "invalid" };
97
+ }
98
+ const length = lengthDescriptor.value;
99
+ if (length > tileflowInteractionLimits.maxDocumentProperties - properties) {
100
+ return { ok: false, reason: "properties" };
101
+ }
102
+ properties += length;
103
+ if (length > 0 && depth >= tileflowInteractionLimits.maxDocumentDepth) {
104
+ return { ok: false, reason: "depth" };
105
+ }
106
+ const syntaxBytes = length === 0 ? 2 : length + 1;
107
+ if (!consumeBytes(syntaxBytes)) return { ok: false, reason: "bytes" };
108
+ const keys2 = Reflect.ownKeys(value);
109
+ const children2 = [];
110
+ let sawLength = false;
111
+ for (const key of keys2) {
112
+ if (typeof key !== "string") return { ok: false, reason: "invalid" };
113
+ if (key === "length") {
114
+ sawLength = true;
115
+ continue;
116
+ }
117
+ if (!isCanonicalArrayIndex(key, length)) return { ok: false, reason: "invalid" };
118
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
119
+ if (!isEnumerableDataDescriptor(descriptor)) return { ok: false, reason: "invalid" };
120
+ children2.push(descriptor.value);
121
+ }
122
+ if (!sawLength || children2.length > length) return { ok: false, reason: "invalid" };
123
+ if (children2.length > tileflowInteractionLimits.maxDocumentNodes - nodes) {
124
+ return { ok: false, reason: "nodes" };
125
+ }
126
+ nodes += children2.length;
127
+ const holes = length - children2.length;
128
+ if (!consumeBytes(holes * 4)) return { ok: false, reason: "bytes" };
129
+ for (const child of children2) {
130
+ const failure = enqueue(child, depth + 1);
131
+ if (failure) return { ok: false, reason: failure };
132
+ }
133
+ continue;
134
+ }
135
+ const keys = Reflect.ownKeys(value);
136
+ if (keys.length > tileflowInteractionLimits.maxDocumentProperties - properties) {
137
+ return { ok: false, reason: "properties" };
138
+ }
139
+ if (keys.length > tileflowInteractionLimits.maxDocumentNodes - nodes) {
140
+ return { ok: false, reason: "nodes" };
141
+ }
142
+ if (keys.length > 0 && depth >= tileflowInteractionLimits.maxDocumentDepth) {
143
+ return { ok: false, reason: "depth" };
144
+ }
145
+ properties += keys.length;
146
+ nodes += keys.length;
147
+ if (!consumeBytes(keys.length === 0 ? 2 : keys.length * 2 + 1)) {
148
+ return { ok: false, reason: "bytes" };
149
+ }
150
+ const children = [];
151
+ for (const key of keys) {
152
+ if (typeof key !== "string" || key === "__proto__" || key === "constructor" || key === "prototype") {
153
+ return { ok: false, reason: "invalid" };
154
+ }
155
+ const keyBytes = jsonStringByteLength(
156
+ key,
157
+ tileflowInteractionLimits.maxDocumentBytes - bytes
158
+ );
159
+ if (keyBytes === void 0) return { ok: false, reason: "bytes" };
160
+ bytes += keyBytes;
161
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
162
+ if (!isEnumerableDataDescriptor(descriptor)) return { ok: false, reason: "invalid" };
163
+ children.push(descriptor.value);
164
+ }
165
+ for (const child of children) {
166
+ const failure = enqueue(child, depth + 1);
167
+ if (failure) return { ok: false, reason: failure };
168
+ }
169
+ }
170
+ return { bytes, ok: true };
171
+ } catch {
172
+ return { ok: false, reason: "invalid" };
173
+ }
174
+ }
175
+ function isObject(value) {
176
+ return value !== null && typeof value === "object";
177
+ }
178
+ function isEnumerableDataDescriptor(descriptor) {
179
+ return Boolean(descriptor?.enumerable && "value" in descriptor);
180
+ }
181
+ function isCanonicalArrayIndex(key, length) {
182
+ if (!/^(?:0|[1-9]\d*)$/u.test(key)) return false;
183
+ const index = Number(key);
184
+ return Number.isSafeInteger(index) && index >= 0 && index < length && String(index) === key;
185
+ }
186
+ function jsonStringByteLength(value, maximum) {
187
+ let bytes = 2;
188
+ if (bytes > maximum) return void 0;
189
+ for (let index = 0; index < value.length; index += 1) {
190
+ const code = value.charCodeAt(index);
191
+ let addition;
192
+ if (code === 34 || code === 92) addition = 2;
193
+ else if (code <= 31) {
194
+ addition = code === 8 || code === 9 || code === 10 || code === 12 || code === 13 ? 2 : 6;
195
+ } else if (code <= 127) addition = 1;
196
+ else if (code <= 2047) addition = 2;
197
+ else if (code >= 55296 && code <= 56319) {
198
+ const next = value.charCodeAt(index + 1);
199
+ if (next >= 56320 && next <= 57343) {
200
+ addition = 4;
201
+ index += 1;
202
+ } else addition = 6;
203
+ } else if (code >= 56320 && code <= 57343) addition = 6;
204
+ else addition = 3;
205
+ if (addition > maximum - bytes) return void 0;
206
+ bytes += addition;
207
+ }
208
+ return bytes;
209
+ }
210
+ var portableIdPattern = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u;
211
+ var semanticDomainPattern = /^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*$/u;
212
+ var fieldSelectorPattern = /^[A-Za-z_][A-Za-z0-9_-]*(?:\.[A-Za-z_][A-Za-z0-9_-]*)*$/u;
213
+ var unsafeFieldSegments = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
214
+ var safeCssColorPattern = /^(?:#(?:[\da-f]{3}|[\da-f]{4}|[\da-f]{6}|[\da-f]{8})|[a-z]+|(?:color|hsla?|hwb|lab|lch|oklab|oklch|rgba?)\([A-Za-z0-9.,%+\- /]+\))$/iu;
215
+ var portableIdSchema = z.string().check(
216
+ z.minLength(1),
217
+ z.maxLength(tileflowInteractionLimits.maxIdLength),
218
+ z.regex(portableIdPattern, "Expected a portable identifier")
219
+ );
220
+ var targetNameSchema = z.string().check(
221
+ z.trim(),
222
+ z.minLength(1),
223
+ z.maxLength(tileflowInteractionLimits.maxTargetNameLength),
224
+ z.refine((value) => !hasControlCharacters(value), {
225
+ message: "Target names may not contain control characters"
226
+ })
227
+ );
228
+ function hasControlCharacters(value) {
229
+ return [...value].some((character) => {
230
+ const code = character.charCodeAt(0);
231
+ return code <= 31 || code === 127;
232
+ });
233
+ }
234
+ var coordinateSchema = z.tuple([z.number(), z.number()]).check(
235
+ z.refine(([lng, lat]) => lng >= -180 && lng <= 180 && lat >= -90 && lat <= 90, {
236
+ message: "Expected [longitude, latitude] within world bounds"
237
+ })
238
+ );
239
+ var featureIdSchema = z.union([
240
+ z.int(),
241
+ z.string().check(z.minLength(1), z.maxLength(tileflowInteractionLimits.maxFeatureIdLength))
242
+ ]);
243
+ var tileflowInteractionJsonValueSchema = z.custom(
244
+ (value) => auditTileflowInteractionJsonDocument(value).ok,
245
+ "Expected a bounded, finite JSON value"
246
+ );
247
+ var textContentSchema = z.strictObject({
248
+ kind: z.literal("text"),
249
+ text: z.string().check(z.maxLength(tileflowInteractionLimits.maxContentTextLength))
250
+ });
251
+ var fieldContentSchema = z.strictObject({
252
+ fallback: z.optional(
253
+ z.string().check(z.maxLength(tileflowInteractionLimits.maxContentTextLength))
254
+ ),
255
+ field: z.string().check(
256
+ z.minLength(1),
257
+ z.maxLength(tileflowInteractionLimits.maxFieldSelectorLength),
258
+ z.regex(fieldSelectorPattern, "Expected a dotted declarative field selector"),
259
+ z.refine((field) => field.split(".").every((segment) => !unsafeFieldSegments.has(segment)), {
260
+ message: "Field selectors may not contain unsafe object keys"
261
+ })
262
+ ),
263
+ kind: z.literal("field")
264
+ });
265
+ var viewContentSchema = z.strictObject({
266
+ kind: z.literal("view"),
267
+ name: z.string().check(
268
+ z.minLength(1),
269
+ z.maxLength(tileflowInteractionLimits.maxViewNameLength),
270
+ z.regex(portableIdPattern, "Expected a portable view name")
271
+ )
272
+ });
273
+ var tileflowInteractionContentSchema = z.discriminatedUnion("kind", [
274
+ textContentSchema,
275
+ fieldContentSchema,
276
+ viewContentSchema
277
+ ]);
278
+ var annotationMarkerSchema = z.strictObject({
279
+ color: z.optional(
280
+ z.string().check(
281
+ z.trim(),
282
+ z.minLength(1),
283
+ z.maxLength(tileflowInteractionLimits.maxColorLength),
284
+ z.regex(safeCssColorPattern, "Expected a bounded CSS color without URL or variable input")
285
+ )
286
+ ),
287
+ content: z.optional(tileflowInteractionContentSchema)
288
+ });
289
+ var annotationSurfaceSchema = z.strictObject({ content: tileflowInteractionContentSchema });
290
+ var tileflowAnnotationSchema = z.strictObject({
291
+ ariaLabel: z.string().check(z.trim(), z.minLength(1), z.maxLength(tileflowInteractionLimits.maxAriaLabelLength)),
292
+ coordinate: coordinateSchema,
293
+ data: z.optional(tileflowInteractionJsonValueSchema),
294
+ id: portableIdSchema,
295
+ kind: z.literal("marker"),
296
+ marker: z.optional(annotationMarkerSchema),
297
+ popup: z.optional(annotationSurfaceSchema),
298
+ tooltip: z.optional(annotationSurfaceSchema)
299
+ });
300
+ var tileflowAnnotationsSchema = z.array(tileflowAnnotationSchema).check(z.maxLength(tileflowInteractionLimits.maxAnnotations));
301
+ var annotationTargetSelectorSchema = z.strictObject({
302
+ id: portableIdSchema,
303
+ kind: z.literal("annotation")
304
+ });
305
+ var semanticFeatureTargetSelectorSchema = z.strictObject({
306
+ categories: z.optional(
307
+ z.array(z.enum(tileflowPoiCategories)).check(z.minLength(1), z.maxLength(tileflowInteractionLimits.maxCategories))
308
+ ),
309
+ domain: z.string().check(
310
+ z.minLength(1),
311
+ z.maxLength(tileflowInteractionLimits.maxTargetNameLength),
312
+ z.regex(semanticDomainPattern, "Expected a dotted semantic domain")
313
+ ),
314
+ kind: z.literal("semantic-feature")
315
+ });
316
+ var styleLayerTargetSelectorSchema = z.strictObject({
317
+ kind: z.literal("style-layer"),
318
+ layerId: targetNameSchema
319
+ });
320
+ var mapTargetSelectorSchema = z.strictObject({ kind: z.literal("map") });
321
+ var tileflowInteractionTargetSchema = z.discriminatedUnion("kind", [
322
+ annotationTargetSelectorSchema,
323
+ semanticFeatureTargetSelectorSchema,
324
+ styleLayerTargetSelectorSchema,
325
+ mapTargetSelectorSchema
326
+ ]);
327
+ var tileflowInteractionBindingSchema = z.strictObject({
328
+ id: portableIdSchema,
329
+ popup: z.optional(annotationSurfaceSchema),
330
+ target: tileflowInteractionTargetSchema,
331
+ tooltip: z.optional(annotationSurfaceSchema)
332
+ });
333
+ var tileflowInteractionBindingsSchema = z.array(tileflowInteractionBindingSchema).check(z.maxLength(tileflowInteractionLimits.maxBindings));
334
+ var annotationTargetSchema = z.strictObject({
335
+ id: portableIdSchema,
336
+ kind: z.literal("annotation")
337
+ });
338
+ var semanticFeatureTargetSchema = z.strictObject({
339
+ domain: z.string().check(
340
+ z.minLength(1),
341
+ z.maxLength(tileflowInteractionLimits.maxTargetNameLength),
342
+ z.regex(semanticDomainPattern, "Expected a dotted semantic domain")
343
+ ),
344
+ featureId: featureIdSchema,
345
+ kind: z.literal("semantic-feature")
346
+ });
347
+ var styleFeatureTargetSchema = z.strictObject({
348
+ featureId: featureIdSchema,
349
+ kind: z.literal("style-feature"),
350
+ layerId: targetNameSchema
351
+ });
352
+ var mapTargetSchema = z.strictObject({ coordinate: coordinateSchema, kind: z.literal("map") });
353
+ var tileflowInteractionTargetRefSchema = z.discriminatedUnion("kind", [
354
+ annotationTargetSchema,
355
+ semanticFeatureTargetSchema,
356
+ styleFeatureTargetSchema,
357
+ mapTargetSchema
358
+ ]);
359
+ var tileflowInteractionStateSchema = z.strictObject({
360
+ popup: z.nullable(tileflowInteractionTargetRefSchema)
361
+ });
362
+ var tileflowInteractionActionSchema = z.discriminatedUnion("type", [
363
+ z.strictObject({ target: tileflowInteractionTargetRefSchema, type: z.literal("open-popup") }),
364
+ z.strictObject({ type: z.literal("close-popup") })
365
+ ]);
366
+
367
+ // src/reducer.ts
368
+ var initialTileflowInteractionState = Object.freeze({
369
+ popup: null
370
+ });
371
+ function reduceTileflowInteractionState(state, action) {
372
+ if (action.type === "close-popup") {
373
+ return state.popup === null ? state : { popup: null };
374
+ }
375
+ if (tileflowInteractionTargetRefsEqual(state.popup, action.target)) {
376
+ return state;
377
+ }
378
+ return { popup: action.target };
379
+ }
380
+ function tileflowInteractionTargetRefsEqual(left, right) {
381
+ if (left === right) return true;
382
+ if (left === null || right === null || left.kind !== right.kind) return false;
383
+ switch (left.kind) {
384
+ case "annotation":
385
+ return left.id === right.id;
386
+ case "semantic-feature": {
387
+ const candidate = right;
388
+ return left.domain === candidate.domain && left.featureId === candidate.featureId;
389
+ }
390
+ case "style-feature": {
391
+ const candidate = right;
392
+ return left.layerId === candidate.layerId && left.featureId === candidate.featureId;
393
+ }
394
+ case "map": {
395
+ const candidate = right;
396
+ return left.coordinate[0] === candidate.coordinate[0] && left.coordinate[1] === candidate.coordinate[1];
397
+ }
398
+ }
399
+ }
400
+
401
+ // src/reference.ts
402
+ var tileflowInteractionReference = Object.freeze({
403
+ actions: ["open-popup", "close-popup"],
404
+ annotationKinds: ["marker"],
405
+ contentKinds: ["text", "field", "view"],
406
+ entrypoints: ["@tileflow/interactions", "@tileflow/interactions/maplibre"],
407
+ eventTypes: [
408
+ "target:enter",
409
+ "target:leave",
410
+ "target:focus",
411
+ "target:blur",
412
+ "target:activate",
413
+ "popup:open",
414
+ "popup:close"
415
+ ],
416
+ limits: tileflowInteractionLimits,
417
+ semanticDomains: ["poi"],
418
+ state: { popup: "TileflowInteractionTargetRef | null" },
419
+ targetAvailability: {
420
+ annotation: {
421
+ binding: "reserved",
422
+ inlineSurfaces: "available",
423
+ stateReference: "available"
424
+ },
425
+ map: "reserved",
426
+ "semantic-feature": { availableDomains: ["poi"] },
427
+ "style-layer": "reserved"
428
+ },
429
+ targetKinds: ["annotation", "semantic-feature", "style-layer", "map"],
430
+ version: tileflowInteractionSchemaVersion
431
+ });
432
+
433
+ // src/validation.ts
434
+ function validateTileflowAnnotations(input) {
435
+ const invalidDocument = () => invalidAnnotationsDocument();
436
+ try {
437
+ const topLevelLength = inspectTopLevelArrayLength(input);
438
+ if (!topLevelLength.ok) return invalidDocument();
439
+ if (topLevelLength.length !== void 0 && topLevelLength.length > tileflowInteractionLimits.maxAnnotations) {
440
+ return {
441
+ diagnostics: [
442
+ diagnostic(
443
+ "LIMIT_EXCEEDED",
444
+ "",
445
+ `Annotations exceed the ${tileflowInteractionLimits.maxAnnotations}-item limit.`
446
+ )
447
+ ],
448
+ ok: false
449
+ };
450
+ }
451
+ const audit = auditTileflowInteractionJsonDocument(input);
452
+ if (!audit.ok) {
453
+ return {
454
+ diagnostics: [documentAuditDiagnostic("Annotations", audit, invalidDocumentMessage)],
455
+ ok: false
456
+ };
457
+ }
458
+ let parsed;
459
+ try {
460
+ parsed = tileflowAnnotationsSchema.safeParse(input);
461
+ } catch {
462
+ return invalidDocument();
463
+ }
464
+ if (!parsed.success) {
465
+ return {
466
+ diagnostics: parsed.error.issues.map(
467
+ (issue) => diagnostic("INVALID_ANNOTATION", jsonPointer(issue.path), issue.message)
468
+ ),
469
+ ok: false
470
+ };
471
+ }
472
+ const duplicateDiagnostics = [];
473
+ const firstIndexById = /* @__PURE__ */ new Map();
474
+ for (const [index, annotation] of parsed.data.entries()) {
475
+ const firstIndex = firstIndexById.get(annotation.id);
476
+ if (firstIndex === void 0) {
477
+ firstIndexById.set(annotation.id, index);
478
+ continue;
479
+ }
480
+ duplicateDiagnostics.push(
481
+ diagnostic(
482
+ "DUPLICATE_ANNOTATION_ID",
483
+ `/${index}/id`,
484
+ `Annotation id "${annotation.id}" duplicates /${firstIndex}/id.`
485
+ )
486
+ );
487
+ }
488
+ if (duplicateDiagnostics.length > 0) {
489
+ return { diagnostics: duplicateDiagnostics, ok: false };
490
+ }
491
+ return { annotations: parsed.data, diagnostics: [], ok: true };
492
+ } catch {
493
+ return invalidDocument();
494
+ }
495
+ }
496
+ function validateTileflowInteractionBindings(input) {
497
+ const invalidDocument = () => invalidBindingsDocument();
498
+ try {
499
+ const topLevelLength = inspectTopLevelArrayLength(input);
500
+ if (!topLevelLength.ok) return invalidDocument();
501
+ if (topLevelLength.length !== void 0 && topLevelLength.length > tileflowInteractionLimits.maxBindings) {
502
+ return {
503
+ diagnostics: [
504
+ diagnostic(
505
+ "LIMIT_EXCEEDED",
506
+ "",
507
+ `Interaction bindings exceed the ${tileflowInteractionLimits.maxBindings}-item limit.`
508
+ )
509
+ ],
510
+ ok: false
511
+ };
512
+ }
513
+ const audit = auditTileflowInteractionJsonDocument(input);
514
+ if (!audit.ok) {
515
+ return {
516
+ diagnostics: [
517
+ documentAuditDiagnostic("Interaction bindings", audit, invalidBindingsDocumentMessage)
518
+ ],
519
+ ok: false
520
+ };
521
+ }
522
+ let parsed;
523
+ try {
524
+ parsed = tileflowInteractionBindingsSchema.safeParse(input);
525
+ } catch {
526
+ return invalidDocument();
527
+ }
528
+ if (!parsed.success) {
529
+ return {
530
+ diagnostics: parsed.error.issues.map(
531
+ (issue) => diagnostic("INVALID_DOCUMENT", jsonPointer(issue.path), issue.message)
532
+ ),
533
+ ok: false
534
+ };
535
+ }
536
+ const firstIndexById = /* @__PURE__ */ new Map();
537
+ const diagnostics = [];
538
+ for (const [index, binding] of parsed.data.entries()) {
539
+ const firstIndex = firstIndexById.get(binding.id);
540
+ if (firstIndex === void 0) {
541
+ firstIndexById.set(binding.id, index);
542
+ continue;
543
+ }
544
+ diagnostics.push(
545
+ diagnostic(
546
+ "INVALID_DOCUMENT",
547
+ `/${index}/id`,
548
+ `Interaction id "${binding.id}" duplicates /${firstIndex}/id.`
549
+ )
550
+ );
551
+ }
552
+ return diagnostics.length > 0 ? { diagnostics, ok: false } : { bindings: parsed.data, diagnostics: [], ok: true };
553
+ } catch {
554
+ return invalidDocument();
555
+ }
556
+ }
557
+ function inspectTopLevelArrayLength(input) {
558
+ try {
559
+ if (!Array.isArray(input)) return { ok: true };
560
+ const descriptor = Object.getOwnPropertyDescriptor(input, "length");
561
+ if (!descriptor || !("value" in descriptor) || typeof descriptor.value !== "number" || !Number.isSafeInteger(descriptor.value) || descriptor.value < 0) {
562
+ return { ok: false };
563
+ }
564
+ return { length: descriptor.value, ok: true };
565
+ } catch {
566
+ return { ok: false };
567
+ }
568
+ }
569
+ var invalidDocumentMessage = "Annotations must be a finite, acyclic JSON document.";
570
+ var invalidBindingsDocumentMessage = "Interaction bindings must be a finite, acyclic JSON document.";
571
+ function invalidAnnotationsDocument() {
572
+ return {
573
+ diagnostics: [diagnostic("INVALID_DOCUMENT", "", invalidDocumentMessage)],
574
+ ok: false
575
+ };
576
+ }
577
+ function invalidBindingsDocument() {
578
+ return {
579
+ diagnostics: [diagnostic("INVALID_DOCUMENT", "", invalidBindingsDocumentMessage)],
580
+ ok: false
581
+ };
582
+ }
583
+ function documentAuditDiagnostic(subject, audit, invalidMessage) {
584
+ switch (audit.reason) {
585
+ case "bytes":
586
+ return diagnostic(
587
+ "LIMIT_EXCEEDED",
588
+ "",
589
+ `${subject} exceed the ${tileflowInteractionLimits.maxDocumentBytes}-byte document limit.`
590
+ );
591
+ case "depth":
592
+ return diagnostic(
593
+ "LIMIT_EXCEEDED",
594
+ "",
595
+ `${subject} exceed the ${tileflowInteractionLimits.maxDocumentDepth}-level document depth limit.`
596
+ );
597
+ case "nodes":
598
+ return diagnostic(
599
+ "LIMIT_EXCEEDED",
600
+ "",
601
+ `${subject} exceed the ${tileflowInteractionLimits.maxDocumentNodes}-node document limit.`
602
+ );
603
+ case "properties":
604
+ return diagnostic(
605
+ "LIMIT_EXCEEDED",
606
+ "",
607
+ `${subject} exceed the ${tileflowInteractionLimits.maxDocumentProperties}-property document limit.`
608
+ );
609
+ case "invalid":
610
+ return diagnostic("INVALID_DOCUMENT", "", invalidMessage);
611
+ }
612
+ }
613
+ function diagnostic(code, path, message) {
614
+ return { code, level: "error", message, path };
615
+ }
616
+ function jsonPointer(path) {
617
+ return path.map((segment) => String(segment).replaceAll("~", "~0").replaceAll("/", "~1")).map((segment) => `/${segment}`).join("");
618
+ }
619
+ export {
620
+ initialTileflowInteractionState,
621
+ reduceTileflowInteractionState,
622
+ tileflowAnnotationSchema,
623
+ tileflowAnnotationsSchema,
624
+ tileflowInteractionActionSchema,
625
+ tileflowInteractionBindingSchema,
626
+ tileflowInteractionBindingsSchema,
627
+ tileflowInteractionContentSchema,
628
+ tileflowInteractionJsonValueSchema,
629
+ tileflowInteractionLimits,
630
+ tileflowInteractionReference,
631
+ tileflowInteractionSchemaVersion,
632
+ tileflowInteractionStateSchema,
633
+ tileflowInteractionTargetRefSchema,
634
+ tileflowInteractionTargetRefsEqual,
635
+ tileflowInteractionTargetSchema,
636
+ tileflowPoiCategories,
637
+ validateTileflowAnnotations,
638
+ validateTileflowInteractionBindings
639
+ };