@kubohiroya/turbowarp-structured-data 0.6.3 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1384 @@
1
+ import { LineCounter, isAlias, isMap, isScalar, isSeq, parseDocument } from "yaml";
2
+ import { NamedDataError, bindNamedDataRegistryLifecycle, installNamedDataRegistry } from "@kubohiroya/turbowarp-named-data/composition";
3
+ function isNamedDataSchemaRefEnabled(source = globalThis) {
4
+ const value = source.__TW_NAMED_DATA_FEATURE_FLAGS__?.NAMED_DATA_SCHEMA_REF;
5
+ return value === void 0 ? false : value === true || value === "true";
6
+ }
7
+ function isStructuredDataMvpEnabled(source = globalThis) {
8
+ const value = source.STRUCTURED_DATA_MVP;
9
+ return value === void 0 ? false : value === true || value === "true";
10
+ }
11
+ function isNamedDataRegistryMvpEnabled(source = globalThis) {
12
+ const value = source.NAMED_DATA_REGISTRY_MVP;
13
+ return value === void 0 ? false : value === true || value === "true";
14
+ }
15
+ var extensionConfig = {
16
+ id: "kubohiroyastructureddata",
17
+ slug: "structured-data",
18
+ name: "Structured Data",
19
+ description: "Named structured data with JSON/YAML codecs for TurboWarp and server compilation.",
20
+ author: "Hiroya Kubo",
21
+ license: "MPL-2.0",
22
+ unsandboxed: true,
23
+ docsURI: "https://kubohiroya.github.io/turbowarp-structured-data/",
24
+ blockIconURI: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OCA0OCI+PHJlY3QgeD0iNCIgeT0iOCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjMyIiByeD0iNiIgZmlsbD0iIzhBNTVENyIvPjxwYXRoIGQ9Ik0xOSAxNWMtMyAwLTIgNy01IDkgMyAyIDIgOSA1IDkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMjkgMTVjMyAwIDIgNyA1IDktMyAyLTIgOS01IDkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48Y2lyY2xlIGN4PSIyNCIgY3k9IjI0IiByPSIyLjIiIGZpbGw9IiNmZmYiLz48L3N2Zz4="
25
+ };
26
+ var numberToken = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/u;
27
+ var invalidNumber = Object.freeze({
28
+ kind: "invalid",
29
+ code: "fixed-number-input-invalid"
30
+ });
31
+ /**
32
+ * Parse a NEW numeric draft according to NP-finite-new-input-v1.
33
+ * Never apply this to opening/no-op raw values or use it as an owner validator.
34
+ * Keep the validated token so negative zero, precision and exponent spelling survive.
35
+ */
36
+ function parseFixedNumberInput(input) {
37
+ if (typeof input === "number") {
38
+ if (!Number.isFinite(input)) return invalidNumber;
39
+ return Object.freeze({
40
+ kind: "valid",
41
+ token: Object.is(input, -0) ? "-0" : String(input),
42
+ value: input
43
+ });
44
+ }
45
+ if (typeof input !== "string") return invalidNumber;
46
+ const token = input.trim();
47
+ if (!numberToken.test(token)) return invalidNumber;
48
+ const value = Number(token);
49
+ return Number.isFinite(value) ? Object.freeze({
50
+ kind: "valid",
51
+ token,
52
+ value
53
+ }) : invalidNumber;
54
+ }
55
+ var block_editor_fixed_inputs_schema_default = {
56
+ $schema: "https://json-schema.org/draft/2020-12/schema",
57
+ $id: "https://kubohiroya.github.io/turbowarp-structured-data/schemas/block-editor-fixed-inputs.schema.json",
58
+ $defs: { "forEachAtPath": {
59
+ "type": "object",
60
+ "properties": { "MAX": {
61
+ "type": "integer",
62
+ "minimum": 1,
63
+ "maximum": 1e3
64
+ } },
65
+ "required": ["MAX"],
66
+ "additionalProperties": true
67
+ } }
68
+ };
69
+ var source$1 = {
70
+ repository: "turbowarp-structured-data",
71
+ path: "schemas/block-editor-fixed-inputs.schema.json",
72
+ symbol: "#/$defs/forEachAtPath/properties/MAX"
73
+ };
74
+ /** The on-disk owner Schema is canonical; this is its single-field projection. */
75
+ var fixedInputSchemaBundle = {
76
+ identity: {
77
+ ownerId: "turbowarp-structured-data",
78
+ contractId: "forEachAtPath.MAX",
79
+ revision: "fixed-inputs-v1",
80
+ variantId: "default"
81
+ },
82
+ dialect: "2020-12",
83
+ root: {
84
+ retrievalUri: "https://kubohiroya.github.io/turbowarp-structured-data/schemas/forEachAtPath-MAX.schema.json",
85
+ document: block_editor_fixed_inputs_schema_default.$defs.forEachAtPath.properties.MAX,
86
+ artifact: {
87
+ role: "canonical-schema",
88
+ source: source$1
89
+ }
90
+ },
91
+ resources: [],
92
+ provenance: {
93
+ canonicalSources: [source$1, {
94
+ repository: "turbowarp-structured-data",
95
+ path: "src/core.ts",
96
+ symbol: "iterationEntries"
97
+ }],
98
+ generatedFiles: [{
99
+ repository: "turbowarp-structured-data",
100
+ path: "docs/schemas/block-editor-fixed-inputs.schema.json"
101
+ }],
102
+ referenceSchemas: [],
103
+ generationSources: [{
104
+ repository: "turbowarp-structured-data",
105
+ path: "scripts/generate-fixed-input-docs.mjs"
106
+ }]
107
+ }
108
+ };
109
+ var fixedInputEditorDefinitions = [{
110
+ apiVersion: 1,
111
+ extensionId: extensionConfig.id,
112
+ editorId: "forEachAtPath-fixed-inputs",
113
+ opcodes: [`${extensionConfig.id}_forEachAtPath`],
114
+ contractId: "turbowarp-structured-data:forEachAtPath.MAX",
115
+ contractRevision: "fixed-inputs-v1",
116
+ bindings: [{
117
+ argument: "MAX",
118
+ schemaBundle: fixedInputSchemaBundle,
119
+ control: {
120
+ kind: "number",
121
+ label: "MAX"
122
+ },
123
+ read(value) {
124
+ if (typeof value !== "string" && typeof value !== "number") throw new TypeError("Expected a literal maximum.");
125
+ if (typeof value === "string" && value.trim() === "") throw new TypeError("Blank maximum stays readonly.");
126
+ const number = Number(value);
127
+ if (!Number.isFinite(number)) throw new TypeError("Maximum must be finite.");
128
+ return number;
129
+ },
130
+ encode(value) {
131
+ const parsed = parseFixedNumberInput(value);
132
+ if (parsed.kind !== "valid") throw new TypeError("Expected a finite numeric token.");
133
+ return parsed.token;
134
+ },
135
+ validateCandidate(value) {
136
+ const parsed = parseFixedNumberInput(value);
137
+ return parsed.kind === "valid" && Number.isInteger(parsed.value) && parsed.value >= 1 && parsed.value <= 1e3 ? [] : [{
138
+ path: "/inputs/MAX",
139
+ message: "Maximum must be an integer from 1 to 1000."
140
+ }];
141
+ }
142
+ }],
143
+ validationContext: () => ({
144
+ consumerId: extensionConfig.id,
145
+ nowMs: Date.now(),
146
+ settings: null
147
+ })
148
+ }];
149
+ /** Register metadata only; the GUI startup capability owns editor activation. */
150
+ function registerFixedInputEditors(scratch, definitions, runtimeOverride) {
151
+ const host = scratch;
152
+ const api = host.gui?.blockEditors;
153
+ if (!definitions.length || api?.apiVersion !== 1 || typeof api.registerFixedInputEditor !== "function") return () => {};
154
+ const runtime = runtimeOverride ?? host.vm?.runtime;
155
+ const removers = [];
156
+ let active = true;
157
+ const dispose = () => {
158
+ if (!active) return;
159
+ active = false;
160
+ runtime?.off?.("RUNTIME_DISPOSED", dispose);
161
+ for (const remove of removers.splice(0).reverse()) remove();
162
+ };
163
+ try {
164
+ for (const definition of definitions) {
165
+ const remove = api.registerFixedInputEditor.call(api, definition);
166
+ if (typeof remove !== "function") throw new TypeError("Invalid fixed input registration disposer.");
167
+ removers.push(remove);
168
+ }
169
+ runtime?.on?.("RUNTIME_DISPOSED", dispose);
170
+ } catch (error) {
171
+ dispose();
172
+ throw error;
173
+ }
174
+ return dispose;
175
+ }
176
+ function sourceBinding(argument, bundle, read, check = read, format = "json", uiSchema) {
177
+ return {
178
+ argument,
179
+ variants: [{
180
+ variantId: "default",
181
+ format,
182
+ schemaBundle: bundle,
183
+ ...uiSchema ? { uiSchema } : {}
184
+ }],
185
+ decode(source) {
186
+ return {
187
+ source,
188
+ format,
189
+ variantId: "default",
190
+ data: read(source)
191
+ };
192
+ },
193
+ validateSource(source, context) {
194
+ try {
195
+ read(source);
196
+ } catch {
197
+ return {
198
+ source,
199
+ result: {
200
+ kind: "invalid",
201
+ diagnostics: [{ code: "source-decode-invalid" }]
202
+ }
203
+ };
204
+ }
205
+ try {
206
+ const descriptors = Object.getOwnPropertyDescriptors(context);
207
+ if (typeof descriptors.consumerId?.value !== "string" || typeof descriptors.nowMs?.value !== "number" || !Number.isFinite(descriptors.nowMs.value) || !descriptors.settings || !("value" in descriptors.settings)) return {
208
+ source,
209
+ result: {
210
+ kind: "execution-error",
211
+ diagnostics: [{ code: "owner-context-invalid" }]
212
+ }
213
+ };
214
+ } catch {
215
+ return {
216
+ source,
217
+ result: {
218
+ kind: "execution-error",
219
+ diagnostics: [{ code: "owner-context-invalid" }]
220
+ }
221
+ };
222
+ }
223
+ try {
224
+ check(source);
225
+ } catch {
226
+ return {
227
+ source,
228
+ result: {
229
+ kind: "invalid",
230
+ diagnostics: [{ code: "source-owner-invalid" }]
231
+ }
232
+ };
233
+ }
234
+ return {
235
+ source,
236
+ result: {
237
+ kind: "valid",
238
+ diagnostics: []
239
+ }
240
+ };
241
+ }
242
+ };
243
+ }
244
+ /** The optional capability keeps legacy hosts and the startup flag safely inactive. */
245
+ function registerSourceEditors(scratch, definitions) {
246
+ const host = scratch;
247
+ const api = host.gui?.blockEditors;
248
+ if (api?.apiVersion !== 1 || typeof api.registerSourceEditor !== "function") return () => {};
249
+ const removers = [];
250
+ let active = true;
251
+ const dispose = () => {
252
+ if (!active) return;
253
+ active = false;
254
+ host.vm?.runtime?.off?.("RUNTIME_DISPOSED", dispose);
255
+ for (const remove of removers.splice(0).reverse()) remove();
256
+ };
257
+ try {
258
+ for (const definition of definitions) removers.push(api.registerSourceEditor.call(api, definition));
259
+ host.vm?.runtime?.on?.("RUNTIME_DISPOSED", dispose);
260
+ } catch (error) {
261
+ dispose();
262
+ throw error;
263
+ }
264
+ return dispose;
265
+ }
266
+ var source = {
267
+ repository: "https://github.com/kubohiroya/turbowarp-extension-data-editor",
268
+ path: "src/json-value.ts",
269
+ symbol: "jsonValueSchema"
270
+ };
271
+ var jsonValueSchemaBundle = Object.freeze({
272
+ identity: {
273
+ ownerId: "@kubohiroya/turbowarp-extension-data-editor",
274
+ contractId: "json-value",
275
+ revision: "1",
276
+ variantId: "json-value"
277
+ },
278
+ dialect: "2020-12",
279
+ root: {
280
+ retrievalUri: "https://kubohiroya.github.io/turbowarp-extension-data-editor/schemas/json-value",
281
+ document: true,
282
+ artifact: {
283
+ role: "canonical-schema",
284
+ source
285
+ }
286
+ },
287
+ resources: [],
288
+ provenance: {
289
+ canonicalSources: [source],
290
+ generatedFiles: [],
291
+ referenceSchemas: [],
292
+ generationSources: []
293
+ }
294
+ });
295
+ var StructuredDataError = class extends Error {
296
+ constructor(code, message) {
297
+ super(`${code}: ${message}`);
298
+ this.name = "StructuredDataError";
299
+ this.code = code;
300
+ }
301
+ };
302
+ function parseJson(text) {
303
+ try {
304
+ const value = JSON.parse(String(text));
305
+ assertJsonValue(value);
306
+ return value;
307
+ } catch (error) {
308
+ if (error instanceof StructuredDataError) throw error;
309
+ throw new StructuredDataError("INVALID_JSON", "Input must be valid JSON.");
310
+ }
311
+ }
312
+ function stringifyJson(value) {
313
+ if (Array.isArray(value)) return `[${value.map(stringifyJson).join(",")}]`;
314
+ if (isJsonObject(value)) return `{${Object.keys(value).sort(compareUnicodeCodePoints).map((key) => `${JSON.stringify(key)}:${stringifyJson(value[key])}`).join(",")}}`;
315
+ return JSON.stringify(value);
316
+ }
317
+ function parsePath(input) {
318
+ const path = String(input);
319
+ if (!path.startsWith("$")) invalidPath();
320
+ const segments = [];
321
+ let cursor = 1;
322
+ while (cursor < path.length) {
323
+ if (path[cursor] === ".") {
324
+ const match = /^[A-Za-z_][A-Za-z0-9_]*/u.exec(path.slice(cursor + 1));
325
+ if (!match) invalidPath();
326
+ segments.push({
327
+ kind: "key",
328
+ value: match[0]
329
+ });
330
+ cursor += match[0].length + 1;
331
+ continue;
332
+ }
333
+ if (path[cursor] !== "[") invalidPath();
334
+ cursor += 1;
335
+ if (path[cursor] === "\"") {
336
+ const end = scanJsonString(path, cursor);
337
+ let key;
338
+ try {
339
+ key = JSON.parse(path.slice(cursor, end));
340
+ } catch {
341
+ invalidPath();
342
+ }
343
+ if (typeof key !== "string" || path[end] !== "]") invalidPath();
344
+ segments.push({
345
+ kind: "key",
346
+ value: key
347
+ });
348
+ cursor = end + 1;
349
+ continue;
350
+ }
351
+ const match = /^(0|[1-9][0-9]*)\]/u.exec(path.slice(cursor));
352
+ if (!match) invalidPath();
353
+ const index = Number(match[1]);
354
+ if (!Number.isSafeInteger(index)) invalidPath();
355
+ segments.push({
356
+ kind: "index",
357
+ value: index
358
+ });
359
+ cursor += match[0].length;
360
+ }
361
+ return segments;
362
+ }
363
+ function getAtPath(root, segments) {
364
+ let current = root;
365
+ for (const segment of segments) current = readSegment(current, segment);
366
+ return current;
367
+ }
368
+ function hasAtPath(root, segments) {
369
+ try {
370
+ getAtPath(root, segments);
371
+ return true;
372
+ } catch (error) {
373
+ if (error instanceof StructuredDataError && (error.code === "PATH_NOT_FOUND" || error.code === "INDEX_OUT_OF_RANGE")) return false;
374
+ throw error;
375
+ }
376
+ }
377
+ function setAtPath(root, segments, replacement) {
378
+ if (segments.length === 0) return cloneJson(replacement);
379
+ return updateParent(root, segments, (parent, segment) => {
380
+ if (segment.kind === "key") {
381
+ if (!isJsonObject(parent)) typeMismatch("Object path segment requires an object.");
382
+ return {
383
+ ...parent,
384
+ [segment.value]: cloneJson(replacement)
385
+ };
386
+ }
387
+ if (!Array.isArray(parent)) typeMismatch("Index path segment requires an array.");
388
+ if (segment.value >= parent.length) indexOutOfRange();
389
+ const result = parent.slice();
390
+ result[segment.value] = cloneJson(replacement);
391
+ return result;
392
+ });
393
+ }
394
+ function deleteAtPath(root, segments) {
395
+ if (segments.length === 0) throw new StructuredDataError("INVALID_PATH", "The root value cannot be deleted.");
396
+ return updateParent(root, segments, (parent, segment) => {
397
+ if (segment.kind === "key") {
398
+ if (!isJsonObject(parent)) typeMismatch("Object path segment requires an object.");
399
+ if (!Object.hasOwn(parent, segment.value)) pathNotFound();
400
+ const result = { ...parent };
401
+ delete result[segment.value];
402
+ return result;
403
+ }
404
+ if (!Array.isArray(parent)) typeMismatch("Index path segment requires an array.");
405
+ if (segment.value >= parent.length) indexOutOfRange();
406
+ const result = parent.slice();
407
+ result.splice(segment.value, 1);
408
+ return result;
409
+ });
410
+ }
411
+ function keysAtPath(root, segments) {
412
+ const value = getAtPath(root, segments);
413
+ if (!isJsonObject(value)) typeMismatch("Keys require an object.");
414
+ return Object.keys(value).sort(compareUnicodeCodePoints);
415
+ }
416
+ function lengthAtPath(root, segments) {
417
+ const value = getAtPath(root, segments);
418
+ if (!Array.isArray(value)) typeMismatch("Length requires an array.");
419
+ return value.length;
420
+ }
421
+ function iterationEntries(root, segments, maximum) {
422
+ if (!Number.isInteger(maximum) || maximum < 1 || maximum > 1e3) throw new StructuredDataError("ITERATION_LIMIT_EXCEEDED", "Maximum must be from 1 to 1000.");
423
+ const value = getAtPath(root, segments);
424
+ const entries = Array.isArray(value) ? value.map((item, index) => ({
425
+ key: String(index),
426
+ value: item
427
+ })) : isJsonObject(value) ? Object.keys(value).sort(compareUnicodeCodePoints).map((key) => ({
428
+ key,
429
+ value: value[key]
430
+ })) : typeMismatch("Iteration requires an array or object.");
431
+ if (entries.length > maximum) throw new StructuredDataError("ITERATION_LIMIT_EXCEEDED", `Collection contains ${entries.length} items, above maximum ${maximum}.`);
432
+ return entries;
433
+ }
434
+ function updateParent(root, segments, update) {
435
+ const [segment, ...remaining] = segments;
436
+ if (!segment) return root;
437
+ if (remaining.length === 0) return update(root, segment);
438
+ const next = updateParent(readSegment(root, segment), remaining, update);
439
+ if (segment.kind === "key") {
440
+ if (!isJsonObject(root)) typeMismatch("Object path segment requires an object.");
441
+ return {
442
+ ...root,
443
+ [segment.value]: next
444
+ };
445
+ }
446
+ if (!Array.isArray(root)) typeMismatch("Index path segment requires an array.");
447
+ const result = root.slice();
448
+ result[segment.value] = next;
449
+ return result;
450
+ }
451
+ function readSegment(value, segment) {
452
+ if (segment.kind === "key") {
453
+ if (!isJsonObject(value)) typeMismatch("Object path segment requires an object.");
454
+ if (!Object.hasOwn(value, segment.value)) pathNotFound();
455
+ return value[segment.value];
456
+ }
457
+ if (!Array.isArray(value)) typeMismatch("Index path segment requires an array.");
458
+ if (segment.value >= value.length) indexOutOfRange();
459
+ return value[segment.value];
460
+ }
461
+ function cloneJson(value) {
462
+ if (Array.isArray(value)) return value.map(cloneJson);
463
+ if (isJsonObject(value)) return Object.fromEntries(Object.keys(value).map((key) => [key, cloneJson(value[key])]));
464
+ return value;
465
+ }
466
+ function assertJsonValue(value) {
467
+ if (value === null || typeof value === "string" || typeof value === "boolean") return;
468
+ if (typeof value === "number" && Number.isFinite(value)) return;
469
+ if (Array.isArray(value)) {
470
+ for (const item of value) assertJsonValue(item);
471
+ return;
472
+ }
473
+ if (typeof value === "object") {
474
+ for (const item of Object.values(value)) assertJsonValue(item);
475
+ return;
476
+ }
477
+ throw new StructuredDataError("INVALID_JSON", "JSON contains an unsupported value.");
478
+ }
479
+ function isJsonObject(value) {
480
+ return typeof value === "object" && value !== null && !Array.isArray(value);
481
+ }
482
+ function scanJsonString(path, start) {
483
+ let escaped = false;
484
+ for (let index = start + 1; index < path.length; index += 1) {
485
+ const character = path[index];
486
+ if (escaped) escaped = false;
487
+ else if (character === "\\") escaped = true;
488
+ else if (character === "\"") return index + 1;
489
+ }
490
+ invalidPath();
491
+ }
492
+ function compareUnicodeCodePoints(left, right) {
493
+ const leftPoints = Array.from(left, (character) => character.codePointAt(0));
494
+ const rightPoints = Array.from(right, (character) => character.codePointAt(0));
495
+ const length = Math.min(leftPoints.length, rightPoints.length);
496
+ for (let index = 0; index < length; index += 1) {
497
+ const difference = leftPoints[index] - rightPoints[index];
498
+ if (difference !== 0) return difference;
499
+ }
500
+ return leftPoints.length - rightPoints.length;
501
+ }
502
+ function invalidPath() {
503
+ throw new StructuredDataError("INVALID_PATH", "Path does not match the supported syntax.");
504
+ }
505
+ function pathNotFound() {
506
+ throw new StructuredDataError("PATH_NOT_FOUND", "Path does not exist.");
507
+ }
508
+ function typeMismatch(message) {
509
+ throw new StructuredDataError("TYPE_MISMATCH", message);
510
+ }
511
+ function indexOutOfRange() {
512
+ throw new StructuredDataError("INDEX_OUT_OF_RANGE", "Array index is out of range.");
513
+ }
514
+ var PARSE_LIMITS = {
515
+ maxInputBytes: 262144,
516
+ maxDepth: 64,
517
+ maxNodes: 5e4
518
+ };
519
+ var SIMPLE_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_-]*$/u;
520
+ function parseJsonDocument(source) {
521
+ validateInputSize(source);
522
+ const value = parseJson(source);
523
+ validateValueBudget(value, { nodes: 0 }, 0);
524
+ return value;
525
+ }
526
+ function parseYamlDocument(source) {
527
+ validateInputSize(source);
528
+ const lineCounter = new LineCounter();
529
+ const document = parseDocument(source, {
530
+ lineCounter,
531
+ prettyErrors: false,
532
+ schema: "core",
533
+ uniqueKeys: true
534
+ });
535
+ const problem = document.errors[0] ?? document.warnings[0];
536
+ if (problem) {
537
+ const { line, column } = yamlPosition(lineCounter, problem.pos[0]);
538
+ throw new StructuredDataError("INVALID_YAML", `${problem.code} at line ${line}, column ${column}: ${problem.message}`);
539
+ }
540
+ try {
541
+ return valueFromYamlNode(document.contents, { nodes: 0 }, 0, lineCounter);
542
+ } catch (error) {
543
+ if (error instanceof StructuredDataError) throw error;
544
+ throw new StructuredDataError("INVALID_YAML", error instanceof Error ? error.message : String(error));
545
+ }
546
+ }
547
+ function renderJsonDocument(value) {
548
+ return stringifyJson(value);
549
+ }
550
+ function renderYamlDocument(value) {
551
+ return renderYamlValue(value, 0);
552
+ }
553
+ function valueFromYamlNode(node, budget, depth, lineCounter) {
554
+ consumeBudget(budget, depth);
555
+ if (node === null) return null;
556
+ if (isAlias(node)) throw new StructuredDataError("INVALID_YAML", "YAML aliases are not allowed.");
557
+ if (isScalar(node)) {
558
+ const value = node.value;
559
+ if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return value;
560
+ throw new StructuredDataError("INVALID_YAML", "YAML contains an unsupported scalar value.");
561
+ }
562
+ if (isSeq(node)) return node.items.map((item) => valueFromYamlNode(item, budget, depth + 1, lineCounter));
563
+ if (isMap(node)) return Object.fromEntries(node.items.map((item) => yamlPairToEntry(item, budget, depth, lineCounter)));
564
+ throw new StructuredDataError("INVALID_YAML", "YAML contains an unsupported node type.");
565
+ }
566
+ function yamlPairToEntry(pair, budget, depth, lineCounter) {
567
+ if (!isScalar(pair.key) || typeof pair.key.value !== "string") throw new StructuredDataError("INVALID_YAML", "YAML map keys must be strings.");
568
+ return [pair.key.value, valueFromYamlNode(pair.value, budget, depth + 1, lineCounter)];
569
+ }
570
+ function validateValueBudget(value, budget, depth) {
571
+ consumeBudget(budget, depth);
572
+ if (Array.isArray(value)) for (const item of value) validateValueBudget(item, budget, depth + 1);
573
+ else if (typeof value === "object" && value !== null) for (const item of Object.values(value)) validateValueBudget(item, budget, depth + 1);
574
+ }
575
+ function consumeBudget(budget, depth) {
576
+ if (depth > PARSE_LIMITS.maxDepth) throw new StructuredDataError("PARSE_LIMIT_EXCEEDED", `Data exceeds maximum nesting depth ${PARSE_LIMITS.maxDepth}.`);
577
+ budget.nodes += 1;
578
+ if (budget.nodes > PARSE_LIMITS.maxNodes) throw new StructuredDataError("PARSE_LIMIT_EXCEEDED", `Data exceeds maximum node count ${PARSE_LIMITS.maxNodes}.`);
579
+ }
580
+ function validateInputSize(source) {
581
+ const bytes = new TextEncoder().encode(source).byteLength;
582
+ if (bytes > PARSE_LIMITS.maxInputBytes) throw new StructuredDataError("PARSE_LIMIT_EXCEEDED", `Input is ${bytes} bytes; maximum is ${PARSE_LIMITS.maxInputBytes}.`);
583
+ }
584
+ function renderYamlValue(value, indent) {
585
+ if (isObject(value)) {
586
+ const entries = Object.keys(value).sort(compareUnicodeCodePoints);
587
+ if (entries.length === 0) return `${spaces(indent)}{}`;
588
+ return entries.map((key) => renderValuePair(key, value[key], indent)).join("\n");
589
+ }
590
+ if (Array.isArray(value)) {
591
+ if (value.length === 0) return `${spaces(indent)}[]`;
592
+ return value.map((item) => renderSequenceItem(item, indent)).join("\n");
593
+ }
594
+ return `${spaces(indent)}${renderScalar(value)}`;
595
+ }
596
+ function renderValuePair(key, value, indent) {
597
+ const prefix = `${spaces(indent)}${renderKey(key)}:`;
598
+ if (!isObject(value) && !Array.isArray(value)) return `${prefix} ${renderScalar(value)}`;
599
+ return `${prefix}\n${renderYamlValue(value, indent + 2)}`;
600
+ }
601
+ function renderSequenceItem(value, indent) {
602
+ const prefix = `${spaces(indent)}-`;
603
+ if (!isObject(value) && !Array.isArray(value)) return `${prefix} ${renderScalar(value)}`;
604
+ return `${prefix}\n${renderYamlValue(value, indent + 2)}`;
605
+ }
606
+ function renderKey(key) {
607
+ return SIMPLE_KEY_PATTERN.test(key) ? key : JSON.stringify(key);
608
+ }
609
+ function renderScalar(value) {
610
+ if (value === null) return "null";
611
+ if (typeof value === "boolean") return value ? "true" : "false";
612
+ if (typeof value === "number") return String(value);
613
+ return JSON.stringify(value);
614
+ }
615
+ function isObject(value) {
616
+ return typeof value === "object" && value !== null && !Array.isArray(value);
617
+ }
618
+ function yamlPosition(counter, offset) {
619
+ const position = counter.linePos(offset);
620
+ return {
621
+ line: position.line,
622
+ column: position.col
623
+ };
624
+ }
625
+ function spaces(count) {
626
+ return " ".repeat(count);
627
+ }
628
+ var sourceEditorDefinitions = [
629
+ {
630
+ editorId: "json-document-source",
631
+ opcodes: ["parseJson"],
632
+ bindings: [sourceBinding("TEXT", jsonValueSchemaBundle, parseJsonDocument)]
633
+ },
634
+ {
635
+ editorId: "yaml-document-source",
636
+ opcodes: ["parseYaml"],
637
+ bindings: [sourceBinding("TEXT", jsonValueSchemaBundle, parseYamlDocument, parseYamlDocument, "yaml")]
638
+ },
639
+ {
640
+ editorId: "json-path-value-source",
641
+ opcodes: ["setJsonAtPath"],
642
+ bindings: [sourceBinding("VALUE", jsonValueSchemaBundle, parseJson)]
643
+ }
644
+ ].map(({ editorId, opcodes, bindings }) => ({
645
+ apiVersion: 1,
646
+ extensionId: extensionConfig.id,
647
+ editorId,
648
+ opcodes: opcodes.map((opcode) => `${extensionConfig.id}_${opcode}`),
649
+ bindings,
650
+ validationContext: () => ({
651
+ consumerId: extensionConfig.id,
652
+ nowMs: Date.now(),
653
+ settings: null
654
+ })
655
+ }));
656
+ var block_definitions_default = {
657
+ extensionName: "Structured Data",
658
+ blocks: [
659
+ {
660
+ "opcode": "parseJson",
661
+ "blockType": "COMMAND",
662
+ "text": "parse JSON [TEXT] as [NAME]",
663
+ "description": "Parse JSON text into the target-local structured data namespace.",
664
+ "arguments": {
665
+ "TEXT": {
666
+ "type": "STRING",
667
+ "defaultValue": "{\"name\":\"TurboWarp\"}"
668
+ },
669
+ "NAME": {
670
+ "type": "STRING",
671
+ "defaultValue": "data"
672
+ }
673
+ },
674
+ "resultType": "void",
675
+ "effect": "state",
676
+ "immutable": true,
677
+ "errors": [
678
+ "INVALID_JSON",
679
+ "INVALID_NAME",
680
+ "PARSE_LIMIT_EXCEEDED"
681
+ ],
682
+ "server": {
683
+ "supported": true,
684
+ "irOperation": "structuredData.parseJson"
685
+ }
686
+ },
687
+ {
688
+ "opcode": "parseYaml",
689
+ "blockType": "COMMAND",
690
+ "text": "parse YAML [TEXT] as [NAME]",
691
+ "description": "Parse safe YAML text into the target-local structured data namespace.",
692
+ "arguments": {
693
+ "TEXT": {
694
+ "type": "STRING",
695
+ "defaultValue": "name: TurboWarp"
696
+ },
697
+ "NAME": {
698
+ "type": "STRING",
699
+ "defaultValue": "data"
700
+ }
701
+ },
702
+ "resultType": "void",
703
+ "effect": "state",
704
+ "immutable": true,
705
+ "errors": [
706
+ "INVALID_YAML",
707
+ "INVALID_NAME",
708
+ "PARSE_LIMIT_EXCEEDED"
709
+ ],
710
+ "server": {
711
+ "supported": true,
712
+ "irOperation": "structuredData.parseYaml"
713
+ }
714
+ },
715
+ {
716
+ "opcode": "toJson",
717
+ "blockType": "REPORTER",
718
+ "text": "to JSON [NAME]",
719
+ "description": "Render named structured data as canonical JSON.",
720
+ "arguments": { "NAME": {
721
+ "type": "STRING",
722
+ "defaultValue": "data"
723
+ } },
724
+ "resultType": "jsonText",
725
+ "effect": "pure",
726
+ "immutable": true,
727
+ "errors": ["INVALID_NAME", "DATA_NOT_FOUND"],
728
+ "server": {
729
+ "supported": true,
730
+ "irOperation": "structuredData.toJson"
731
+ }
732
+ },
733
+ {
734
+ "opcode": "toYaml",
735
+ "blockType": "REPORTER",
736
+ "text": "to YAML [NAME]",
737
+ "description": "Render named structured data as deterministic YAML.",
738
+ "arguments": { "NAME": {
739
+ "type": "STRING",
740
+ "defaultValue": "data"
741
+ } },
742
+ "resultType": "yamlText",
743
+ "effect": "pure",
744
+ "immutable": true,
745
+ "errors": ["INVALID_NAME", "DATA_NOT_FOUND"],
746
+ "server": {
747
+ "supported": true,
748
+ "irOperation": "structuredData.toYaml"
749
+ }
750
+ },
751
+ {
752
+ "opcode": "hasStructuredData",
753
+ "blockType": "BOOLEAN",
754
+ "text": "structured data [NAME] exists?",
755
+ "description": "Report whether the current target has named structured data.",
756
+ "arguments": { "NAME": {
757
+ "type": "STRING",
758
+ "defaultValue": "data"
759
+ } },
760
+ "resultType": "boolean",
761
+ "effect": "pure",
762
+ "immutable": true,
763
+ "errors": ["INVALID_NAME"],
764
+ "server": {
765
+ "supported": true,
766
+ "irOperation": "structuredData.hasData"
767
+ }
768
+ },
769
+ {
770
+ "opcode": "deleteStructuredData",
771
+ "blockType": "COMMAND",
772
+ "text": "delete structured data [NAME]",
773
+ "description": "Delete named structured data from the current target.",
774
+ "arguments": { "NAME": {
775
+ "type": "STRING",
776
+ "defaultValue": "data"
777
+ } },
778
+ "resultType": "void",
779
+ "effect": "state",
780
+ "immutable": true,
781
+ "errors": ["INVALID_NAME", "DATA_NOT_FOUND"],
782
+ "server": {
783
+ "supported": true,
784
+ "irOperation": "structuredData.deleteData"
785
+ }
786
+ },
787
+ {
788
+ "opcode": "getJsonAtPath",
789
+ "blockType": "REPORTER",
790
+ "text": "get JSON from [NAME] at path [PATH]",
791
+ "description": "Return a named value at a path as JSON.",
792
+ "arguments": {
793
+ "NAME": {
794
+ "type": "STRING",
795
+ "defaultValue": "data"
796
+ },
797
+ "PATH": {
798
+ "type": "STRING",
799
+ "defaultValue": "$.name",
800
+ "normalizesTo": "pathSegments"
801
+ }
802
+ },
803
+ "resultType": "jsonText",
804
+ "effect": "pure",
805
+ "immutable": true,
806
+ "errors": [
807
+ "INVALID_NAME",
808
+ "DATA_NOT_FOUND",
809
+ "INVALID_PATH",
810
+ "PATH_NOT_FOUND",
811
+ "TYPE_MISMATCH",
812
+ "INDEX_OUT_OF_RANGE"
813
+ ],
814
+ "server": {
815
+ "supported": true,
816
+ "irOperation": "structuredData.get"
817
+ }
818
+ },
819
+ {
820
+ "opcode": "hasPath",
821
+ "blockType": "BOOLEAN",
822
+ "text": "structured data [NAME] has path [PATH]?",
823
+ "description": "Report whether a path exists in named structured data.",
824
+ "arguments": {
825
+ "NAME": {
826
+ "type": "STRING",
827
+ "defaultValue": "data"
828
+ },
829
+ "PATH": {
830
+ "type": "STRING",
831
+ "defaultValue": "$.name",
832
+ "normalizesTo": "pathSegments"
833
+ }
834
+ },
835
+ "resultType": "boolean",
836
+ "effect": "pure",
837
+ "immutable": true,
838
+ "errors": [
839
+ "INVALID_NAME",
840
+ "DATA_NOT_FOUND",
841
+ "INVALID_PATH",
842
+ "TYPE_MISMATCH"
843
+ ],
844
+ "server": {
845
+ "supported": true,
846
+ "irOperation": "structuredData.has"
847
+ }
848
+ },
849
+ {
850
+ "opcode": "setJsonAtPath",
851
+ "blockType": "COMMAND",
852
+ "text": "set [NAME] at path [PATH] to JSON [VALUE]",
853
+ "description": "Immutably update a named value and replace its binding.",
854
+ "arguments": {
855
+ "NAME": {
856
+ "type": "STRING",
857
+ "defaultValue": "data"
858
+ },
859
+ "PATH": {
860
+ "type": "STRING",
861
+ "defaultValue": "$.name",
862
+ "normalizesTo": "pathSegments"
863
+ },
864
+ "VALUE": {
865
+ "type": "STRING",
866
+ "defaultValue": "\"Scratch\""
867
+ }
868
+ },
869
+ "resultType": "void",
870
+ "effect": "state",
871
+ "immutable": true,
872
+ "errors": [
873
+ "INVALID_NAME",
874
+ "DATA_NOT_FOUND",
875
+ "INVALID_JSON",
876
+ "INVALID_PATH",
877
+ "PATH_NOT_FOUND",
878
+ "TYPE_MISMATCH",
879
+ "INDEX_OUT_OF_RANGE"
880
+ ],
881
+ "server": {
882
+ "supported": true,
883
+ "irOperation": "structuredData.set"
884
+ }
885
+ },
886
+ {
887
+ "opcode": "deleteAtPath",
888
+ "blockType": "COMMAND",
889
+ "text": "delete path [PATH] from [NAME]",
890
+ "description": "Immutably delete a path and replace its named binding.",
891
+ "arguments": {
892
+ "NAME": {
893
+ "type": "STRING",
894
+ "defaultValue": "data"
895
+ },
896
+ "PATH": {
897
+ "type": "STRING",
898
+ "defaultValue": "$.name",
899
+ "normalizesTo": "pathSegments"
900
+ }
901
+ },
902
+ "resultType": "void",
903
+ "effect": "state",
904
+ "immutable": true,
905
+ "errors": [
906
+ "INVALID_NAME",
907
+ "DATA_NOT_FOUND",
908
+ "INVALID_PATH",
909
+ "PATH_NOT_FOUND",
910
+ "TYPE_MISMATCH",
911
+ "INDEX_OUT_OF_RANGE"
912
+ ],
913
+ "server": {
914
+ "supported": true,
915
+ "irOperation": "structuredData.delete"
916
+ }
917
+ },
918
+ {
919
+ "opcode": "keysAtPath",
920
+ "blockType": "REPORTER",
921
+ "text": "keys in [NAME] at path [PATH]",
922
+ "description": "Return sorted object keys as JSON.",
923
+ "arguments": {
924
+ "NAME": {
925
+ "type": "STRING",
926
+ "defaultValue": "data"
927
+ },
928
+ "PATH": {
929
+ "type": "STRING",
930
+ "defaultValue": "$",
931
+ "normalizesTo": "pathSegments"
932
+ }
933
+ },
934
+ "resultType": "jsonText",
935
+ "effect": "pure",
936
+ "immutable": true,
937
+ "errors": [
938
+ "INVALID_NAME",
939
+ "DATA_NOT_FOUND",
940
+ "INVALID_PATH",
941
+ "PATH_NOT_FOUND",
942
+ "TYPE_MISMATCH",
943
+ "INDEX_OUT_OF_RANGE"
944
+ ],
945
+ "server": {
946
+ "supported": true,
947
+ "irOperation": "structuredData.keys"
948
+ }
949
+ },
950
+ {
951
+ "opcode": "lengthAtPath",
952
+ "blockType": "REPORTER",
953
+ "text": "length of array in [NAME] at path [PATH]",
954
+ "description": "Return the array length.",
955
+ "arguments": {
956
+ "NAME": {
957
+ "type": "STRING",
958
+ "defaultValue": "data"
959
+ },
960
+ "PATH": {
961
+ "type": "STRING",
962
+ "defaultValue": "$",
963
+ "normalizesTo": "pathSegments"
964
+ }
965
+ },
966
+ "resultType": "number",
967
+ "effect": "pure",
968
+ "immutable": true,
969
+ "errors": [
970
+ "INVALID_NAME",
971
+ "DATA_NOT_FOUND",
972
+ "INVALID_PATH",
973
+ "PATH_NOT_FOUND",
974
+ "TYPE_MISMATCH",
975
+ "INDEX_OUT_OF_RANGE"
976
+ ],
977
+ "server": {
978
+ "supported": true,
979
+ "irOperation": "structuredData.length"
980
+ }
981
+ },
982
+ {
983
+ "opcode": "forEachAtPath",
984
+ "blockType": "LOOP",
985
+ "text": "for each in [NAME] at path [PATH] max [MAX]",
986
+ "description": "Iterate named data deterministically with a static maximum.",
987
+ "arguments": {
988
+ "NAME": {
989
+ "type": "STRING",
990
+ "defaultValue": "data"
991
+ },
992
+ "PATH": {
993
+ "type": "STRING",
994
+ "defaultValue": "$",
995
+ "normalizesTo": "pathSegments"
996
+ },
997
+ "MAX": {
998
+ "type": "NUMBER",
999
+ "defaultValue": 100,
1000
+ "staticLiteral": true,
1001
+ "minimum": 1,
1002
+ "maximum": 1e3
1003
+ }
1004
+ },
1005
+ "resultType": "void",
1006
+ "effect": "control",
1007
+ "immutable": true,
1008
+ "errors": [
1009
+ "INVALID_NAME",
1010
+ "DATA_NOT_FOUND",
1011
+ "INVALID_PATH",
1012
+ "PATH_NOT_FOUND",
1013
+ "TYPE_MISMATCH",
1014
+ "INDEX_OUT_OF_RANGE",
1015
+ "ITERATION_LIMIT_EXCEEDED"
1016
+ ],
1017
+ "server": {
1018
+ "supported": true,
1019
+ "irOperation": "structuredData.forEach"
1020
+ }
1021
+ },
1022
+ {
1023
+ "opcode": "currentKey",
1024
+ "blockType": "REPORTER",
1025
+ "text": "current key",
1026
+ "description": "Return the current object key or array index string.",
1027
+ "arguments": {},
1028
+ "resultType": "string",
1029
+ "effect": "control",
1030
+ "immutable": true,
1031
+ "errors": ["ITERATION_CONTEXT_REQUIRED"],
1032
+ "server": {
1033
+ "supported": true,
1034
+ "irOperation": "structuredData.currentKey"
1035
+ }
1036
+ },
1037
+ {
1038
+ "opcode": "currentIndex",
1039
+ "blockType": "REPORTER",
1040
+ "text": "current index",
1041
+ "description": "Return the zero-based iteration number.",
1042
+ "arguments": {},
1043
+ "resultType": "number",
1044
+ "effect": "control",
1045
+ "immutable": true,
1046
+ "errors": ["ITERATION_CONTEXT_REQUIRED"],
1047
+ "server": {
1048
+ "supported": true,
1049
+ "irOperation": "structuredData.currentIndex"
1050
+ }
1051
+ },
1052
+ {
1053
+ "opcode": "currentValueJson",
1054
+ "blockType": "REPORTER",
1055
+ "text": "current value JSON",
1056
+ "description": "Return the current iteration value as JSON.",
1057
+ "arguments": {},
1058
+ "resultType": "jsonText",
1059
+ "effect": "control",
1060
+ "immutable": true,
1061
+ "errors": ["ITERATION_CONTEXT_REQUIRED"],
1062
+ "server": {
1063
+ "supported": true,
1064
+ "irOperation": "structuredData.currentValue"
1065
+ }
1066
+ }
1067
+ ]
1068
+ };
1069
+ var STRUCTURED_DATA_NAMESPACE = "structured";
1070
+ var STRUCTURED_BODY_MAX_BYTES = 262144;
1071
+ var StructuredDataNamedDataProvider = class {
1072
+ constructor(store, maxBodyBytes = STRUCTURED_BODY_MAX_BYTES, schemaRefEnabled = isNamedDataSchemaRefEnabled()) {
1073
+ this.store = store;
1074
+ this.maxBodyBytes = maxBodyBytes;
1075
+ this.schemaRefEnabled = schemaRefEnabled;
1076
+ this.namespace = STRUCTURED_DATA_NAMESPACE;
1077
+ this.kind = "structured";
1078
+ this.openBodies = /* @__PURE__ */ new Set();
1079
+ this.released = false;
1080
+ }
1081
+ canResolve(reference, representation) {
1082
+ return reference.namespace === this.namespace && reference.kind === this.kind && reference.scope === "target" && (representation === "json" || representation === "yaml");
1083
+ }
1084
+ stat(reference, representation, context) {
1085
+ const resolved = this.resolve(reference, representation, context);
1086
+ return this.metadata(reference, resolved.nativeRepresentation, representation, resolved.bytes.byteLength, resolved.revision, resolved.schemaRef);
1087
+ }
1088
+ openBody(reference, representation, context) {
1089
+ const resolved = this.resolve(reference, representation, context);
1090
+ const handle = Symbol("structured-body");
1091
+ this.openBodies.add(handle);
1092
+ let released = false;
1093
+ return {
1094
+ ...this.metadata(reference, resolved.nativeRepresentation, representation, resolved.bytes.byteLength, resolved.revision, resolved.schemaRef),
1095
+ body: resolved.bytes,
1096
+ release: () => {
1097
+ if (released) return;
1098
+ released = true;
1099
+ this.openBodies.delete(handle);
1100
+ }
1101
+ };
1102
+ }
1103
+ release() {
1104
+ this.released = true;
1105
+ this.openBodies.clear();
1106
+ }
1107
+ clearSession() {
1108
+ this.openBodies.clear();
1109
+ }
1110
+ resolve(reference, representation, context) {
1111
+ if (this.released) throw new NamedDataError("NAMED_DATA_PROVIDER_RELEASED", "The structured provider was released.");
1112
+ this.validateReference(reference, context);
1113
+ this.throwIfAborted(context.signal);
1114
+ const binding = this.store.get(context.target, reference.name);
1115
+ if (!binding) throw new NamedDataError("NAMED_DATA_NOT_FOUND", `Structured data does not exist: ${reference.name}`);
1116
+ const requestedSchemaRef = reference.schemaRef;
1117
+ if (requestedSchemaRef !== void 0 && !this.schemaRefEnabled) throw new NamedDataError("NAMED_DATA_SCHEMA_UNRESOLVED", "Schema reference resolution is disabled.");
1118
+ if (requestedSchemaRef !== void 0 && (binding.schemaRef?.id !== requestedSchemaRef.id || binding.schemaRef?.version !== requestedSchemaRef.version)) throw new NamedDataError("NAMED_DATA_SCHEMA_VERSION_MISMATCH", "Binding schemaRef does not match the requested reference.");
1119
+ const text = this.render(binding.value, representation);
1120
+ const bytes = new TextEncoder().encode(text);
1121
+ if (bytes.byteLength > this.maxBodyBytes) throw new NamedDataError("NAMED_DATA_BODY_TOO_LARGE", `Encoded body is ${bytes.byteLength} bytes; maximum is ${this.maxBodyBytes}.`);
1122
+ this.throwIfAborted(context.signal);
1123
+ return {
1124
+ bytes,
1125
+ revision: binding.revision,
1126
+ nativeRepresentation: binding.nativeRepresentation,
1127
+ ...requestedSchemaRef === void 0 ? {} : { schemaRef: binding.schemaRef }
1128
+ };
1129
+ }
1130
+ validateReference(reference, context) {
1131
+ if (reference.namespace !== this.namespace || reference.name.trim().length === 0) throw new NamedDataError("NAMED_DATA_INVALID_REF", "Invalid structured data reference.");
1132
+ if (reference.kind !== this.kind) throw new NamedDataError("NAMED_DATA_KIND_MISMATCH", `Expected ${this.kind}, received ${reference.kind}.`);
1133
+ if (reference.scope !== "target" || !context.target) throw new NamedDataError("NAMED_DATA_SCOPE_MISMATCH", "Structured data currently requires a target scope and target context.");
1134
+ }
1135
+ render(value, representation) {
1136
+ if (representation === "json") return renderJsonDocument(value);
1137
+ if (representation === "yaml") return renderYamlDocument(value);
1138
+ throw new NamedDataError("NAMED_DATA_REPRESENTATION_UNSUPPORTED", `Structured data cannot be rendered as ${representation}.`);
1139
+ }
1140
+ metadata(reference, nativeRepresentation, representation, byteLength, revision, schemaRef) {
1141
+ return {
1142
+ reference: { ...reference },
1143
+ nativeRepresentation,
1144
+ representation,
1145
+ mediaType: representation === "json" ? "application/json; charset=utf-8" : "application/yaml; charset=utf-8",
1146
+ byteLength,
1147
+ revision: String(revision),
1148
+ replayable: true,
1149
+ ...schemaRef === void 0 ? {} : { schemaRef }
1150
+ };
1151
+ }
1152
+ throwIfAborted(signal) {
1153
+ if (signal?.aborted) throw new NamedDataError("NAMED_DATA_ABORTED", "Body resolution was aborted.");
1154
+ }
1155
+ };
1156
+ var LOOP_STATE = "structuredDataLoopState";
1157
+ var blockDefinitions = block_definitions_default.blocks;
1158
+ var StructuredDataExtension = class {
1159
+ constructor(scratch, enabled = isStructuredDataMvpEnabled(), namedDataEnabled = isNamedDataRegistryMvpEnabled()) {
1160
+ this.scratch = scratch;
1161
+ this.disposed = false;
1162
+ this.registries = /* @__PURE__ */ new WeakMap();
1163
+ this.iterationStacks = /* @__PURE__ */ new WeakMap();
1164
+ this.nextRevision = 1;
1165
+ this.enabled = enabled;
1166
+ this.unregisterSourceEditors = registerSourceEditors(this.scratch, enabled ? sourceEditorDefinitions : []);
1167
+ const runtime = this.scratch.vm?.runtime;
1168
+ const onDispose = () => {
1169
+ this.dispose();
1170
+ };
1171
+ const onStop = () => this.clearRuntimeState();
1172
+ this.releaseRuntimeListeners = () => {
1173
+ runtime?.off?.("RUNTIME_DISPOSED", onDispose);
1174
+ runtime?.off?.("PROJECT_STOP_ALL", onStop);
1175
+ };
1176
+ let unregisterFixed = () => {};
1177
+ try {
1178
+ unregisterFixed = registerFixedInputEditors(this.scratch, enabled ? fixedInputEditorDefinitions : []);
1179
+ this.unregisterFixedInputEditors = unregisterFixed;
1180
+ if (namedDataEnabled) {
1181
+ this.namedDataProvider = new StructuredDataNamedDataProvider({ get: (target, name) => this.registries.get(target)?.get(name) });
1182
+ const runtime = this.scratch.vm?.runtime;
1183
+ if (runtime) {
1184
+ this.namedDataRegistry = installNamedDataRegistry(runtime);
1185
+ this.namedDataRegistration = this.namedDataRegistry.registerProvider(this.namedDataProvider, { lifetime: "persistent" });
1186
+ this.unbindNamedDataRegistryLifecycle = bindNamedDataRegistryLifecycle(runtime, this.namedDataRegistry);
1187
+ runtime.on?.("RUNTIME_DISPOSED", onDispose);
1188
+ }
1189
+ }
1190
+ runtime?.on?.("PROJECT_STOP_ALL", onStop);
1191
+ } catch (error) {
1192
+ this.releaseRuntimeListeners();
1193
+ unregisterFixed();
1194
+ this.unregisterSourceEditors();
1195
+ this.unbindNamedDataRegistryLifecycle?.();
1196
+ this.namedDataRegistration?.unregister().catch(() => {});
1197
+ throw error;
1198
+ }
1199
+ }
1200
+ getNamedDataProvider() {
1201
+ return this.namedDataProvider;
1202
+ }
1203
+ getNamedDataRegistry() {
1204
+ return this.namedDataRegistry;
1205
+ }
1206
+ async dispose() {
1207
+ if (this.disposed) return;
1208
+ this.disposed = true;
1209
+ this.releaseRuntimeListeners();
1210
+ this.unregisterFixedInputEditors();
1211
+ this.unregisterSourceEditors();
1212
+ await this.namedDataRegistration?.unregister();
1213
+ this.unbindNamedDataRegistryLifecycle?.();
1214
+ }
1215
+ getInfo() {
1216
+ return {
1217
+ id: extensionConfig.id,
1218
+ name: this.scratch.translate(block_definitions_default.extensionName),
1219
+ docsURI: extensionConfig.docsURI,
1220
+ blockIconURI: extensionConfig.blockIconURI,
1221
+ blocks: this.enabled ? blockDefinitions.map((block) => this.toScratchBlock(block)) : []
1222
+ };
1223
+ }
1224
+ parseJson(args, util) {
1225
+ this.store(util.target, args.NAME, parseJsonDocument(this.scratch.Cast.toString(args.TEXT)), "json");
1226
+ }
1227
+ parseYaml(args, util) {
1228
+ this.store(util.target, args.NAME, parseYamlDocument(this.scratch.Cast.toString(args.TEXT)), "yaml");
1229
+ }
1230
+ toJson(args, util) {
1231
+ return renderJsonDocument(this.requireData(util.target, args.NAME));
1232
+ }
1233
+ toYaml(args, util) {
1234
+ return renderYamlDocument(this.requireData(util.target, args.NAME));
1235
+ }
1236
+ hasStructuredData(args, util) {
1237
+ return this.registryFor(util.target).has(this.name(args.NAME));
1238
+ }
1239
+ deleteStructuredData(args, util) {
1240
+ const registry = this.registryFor(util.target);
1241
+ const name = this.name(args.NAME);
1242
+ if (!registry.delete(name)) this.dataNotFound(name);
1243
+ }
1244
+ getJsonAtPath(args, util) {
1245
+ return stringifyJson(getAtPath(this.requireData(util.target, args.NAME), this.path(args.PATH)));
1246
+ }
1247
+ hasPath(args, util) {
1248
+ return hasAtPath(this.requireData(util.target, args.NAME), this.path(args.PATH));
1249
+ }
1250
+ setJsonAtPath(args, util) {
1251
+ const name = this.name(args.NAME);
1252
+ const updated = setAtPath(this.requireDataByName(util.target, name), this.path(args.PATH), parseJson(this.scratch.Cast.toString(args.VALUE)));
1253
+ this.setBinding(util.target, name, updated);
1254
+ }
1255
+ deleteAtPath(args, util) {
1256
+ const name = this.name(args.NAME);
1257
+ const updated = deleteAtPath(this.requireDataByName(util.target, name), this.path(args.PATH));
1258
+ this.setBinding(util.target, name, updated);
1259
+ }
1260
+ keysAtPath(args, util) {
1261
+ return JSON.stringify(keysAtPath(this.requireData(util.target, args.NAME), this.path(args.PATH)));
1262
+ }
1263
+ lengthAtPath(args, util) {
1264
+ return lengthAtPath(this.requireData(util.target, args.NAME), this.path(args.PATH));
1265
+ }
1266
+ forEachAtPath(args, util) {
1267
+ let state = util.stackFrame[LOOP_STATE];
1268
+ const stack = this.stackFor(util.thread);
1269
+ if (!state) {
1270
+ state = {
1271
+ entries: iterationEntries(this.requireData(util.target, args.NAME), this.path(args.PATH), this.scratch.Cast.toNumber(args.MAX)),
1272
+ cursor: 0,
1273
+ active: false
1274
+ };
1275
+ util.stackFrame[LOOP_STATE] = state;
1276
+ } else if (state.active) {
1277
+ if (stack.at(-1)?.index === state.cursor) stack.pop();
1278
+ state.cursor += 1;
1279
+ state.active = false;
1280
+ }
1281
+ const entry = state.entries[state.cursor];
1282
+ if (!entry) {
1283
+ delete util.stackFrame[LOOP_STATE];
1284
+ if (stack.length === 0) this.iterationStacks.delete(util.thread);
1285
+ return;
1286
+ }
1287
+ stack.push({
1288
+ ...entry,
1289
+ index: state.cursor
1290
+ });
1291
+ state.active = true;
1292
+ util.startBranch(1, true);
1293
+ }
1294
+ currentKey(_args, util) {
1295
+ return this.currentIteration(util.thread).key;
1296
+ }
1297
+ currentIndex(_args, util) {
1298
+ return this.currentIteration(util.thread).index;
1299
+ }
1300
+ currentValueJson(_args, util) {
1301
+ return stringifyJson(this.currentIteration(util.thread).value);
1302
+ }
1303
+ store(target, rawName, value, nativeRepresentation) {
1304
+ this.setBinding(target, this.name(rawName), value, nativeRepresentation);
1305
+ }
1306
+ requireData(target, rawName) {
1307
+ return this.requireDataByName(target, this.name(rawName));
1308
+ }
1309
+ requireDataByName(target, name) {
1310
+ const binding = this.registryFor(target).get(name);
1311
+ if (binding === void 0) this.dataNotFound(name);
1312
+ return binding.value;
1313
+ }
1314
+ registryFor(target) {
1315
+ let registry = this.registries.get(target);
1316
+ if (!registry) {
1317
+ registry = /* @__PURE__ */ new Map();
1318
+ this.registries.set(target, registry);
1319
+ }
1320
+ return registry;
1321
+ }
1322
+ setBinding(target, name, value, nativeRepresentation) {
1323
+ const registry = this.registryFor(target);
1324
+ const retainedRepresentation = nativeRepresentation ?? registry.get(name)?.nativeRepresentation ?? "json";
1325
+ registry.set(name, {
1326
+ value,
1327
+ revision: this.nextRevision,
1328
+ nativeRepresentation: retainedRepresentation
1329
+ });
1330
+ this.nextRevision += 1;
1331
+ }
1332
+ name(value) {
1333
+ const name = this.scratch.Cast.toString(value).trim();
1334
+ if (name.length === 0) throw new StructuredDataError("INVALID_NAME", "Structured data name cannot be empty.");
1335
+ return name;
1336
+ }
1337
+ dataNotFound(name) {
1338
+ throw new StructuredDataError("DATA_NOT_FOUND", `Structured data does not exist: ${name}`);
1339
+ }
1340
+ currentIteration(thread) {
1341
+ const current = this.iterationStacks.get(thread)?.at(-1);
1342
+ if (!current) throw new StructuredDataError("ITERATION_CONTEXT_REQUIRED", "This reporter is only valid inside an active iteration.");
1343
+ return current;
1344
+ }
1345
+ stackFor(thread) {
1346
+ let stack = this.iterationStacks.get(thread);
1347
+ if (!stack) {
1348
+ stack = [];
1349
+ this.iterationStacks.set(thread, stack);
1350
+ }
1351
+ return stack;
1352
+ }
1353
+ path(value) {
1354
+ return parsePath(this.scratch.Cast.toString(value));
1355
+ }
1356
+ clearRuntimeState() {
1357
+ this.namedDataProvider?.clearSession();
1358
+ this.registries = /* @__PURE__ */ new WeakMap();
1359
+ this.iterationStacks = /* @__PURE__ */ new WeakMap();
1360
+ this.nextRevision = 1;
1361
+ }
1362
+ toScratchBlock(block) {
1363
+ return {
1364
+ opcode: block.opcode,
1365
+ blockType: this.scratch.BlockType[block.blockType],
1366
+ text: this.scratch.translate(block.text),
1367
+ arguments: Object.fromEntries(Object.entries(block.arguments).map(([name, argument]) => [name, {
1368
+ type: this.scratch.ArgumentType[argument.type],
1369
+ defaultValue: argument.defaultValue
1370
+ }]))
1371
+ };
1372
+ }
1373
+ };
1374
+ /** Static contract: importing this entry never creates or registers an extension. */
1375
+ var bundleModuleMetadata = Object.freeze({
1376
+ formatVersion: 1,
1377
+ id: extensionConfig.id,
1378
+ unsandboxed: extensionConfig.unsandboxed ? "required" : "not-needed"
1379
+ });
1380
+ /** The caller supplies the member host and owns registration and lifetime. */
1381
+ function createExtension(scratch) {
1382
+ return new StructuredDataExtension(scratch);
1383
+ }
1384
+ export { bundleModuleMetadata, createExtension };