@kubohiroya/turbowarp-structured-data 0.6.0 → 0.6.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.
package/README.ja.md CHANGED
@@ -71,3 +71,16 @@ pnpm run check
71
71
  SPDX-License-Identifier: MPL-2.0
72
72
 
73
73
  bundleへ含まれるthird-party softwareのlicenseは[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)に記載します。
74
+
75
+ ## 固定引数の編集フォーム(S3-4)
76
+
77
+ 共通エディターの起動時フラグ `blockEditorFixedInputs` とDialogsがONのGUIでは、`forEachAtPath` の `MAX` だけを固定フォームとして登録します。旧GUI・フラグOFFでは追加登録を行いません。Schemaの正本は `schemas/block-editor-fixed-inputs.schema.json#/$defs/forEachAtPath/properties/MAX`、codecと登録は `src/fixed-input-editors.ts` です。既存raw値・未変更alias・未知enumは無操作保存で書き換えず、reporterを評価しません。明示変更だけを検査し、他の引数やブロック宣言、保存形式を変更しません。
78
+
79
+ MAXは整数1〜1000です。新しい入力には共通の有限数値token規則も適用し、途中入力を保存しません。MVP flagがOFFなら登録せず、NAME/PATH・staticLiteral由来の接続可否を変更しません。
80
+
81
+ 登録はoptional API(apiVersion:1)に限定し、RUNTIME_DISPOSED/明示disposeで解除します。起動フラグOFF+再読込で従来経路へ切り戻せます。依存を戻す場合はmanifest/lock/実解決を一組で復旧します。npm公開や後続段階の動的候補は含みません。
82
+
83
+ Schemaが記述する契約の根拠は実getInfoの宣言と既存の純粋なruntime変換です。SchemaBundleのretrievalUriはこのローカル文書の識別子であり、URL配信済み・HTTP取得によるref解決を意味しません。Schemaは同梱文書だけでcompileします。
84
+ 既存MAXの非空finiteなNumber alias(例`001`・`+1`・`0x10`)はNumberによる表示投影だけを行い、無変更rawを保存し直しません。空白・非finite・投影不能値はreadonlyとし、0/defaultを補いません。新しい入力にはこれらaliasを許可せずNP-finite-new-input-v1を適用します。
85
+
86
+ 登録は拡張インスタンスの構築時に存在する任意の editor API を使用します。後からの API 追加や差し替えには自動追従しません。起動設定を変更した場合は GUI を再読み込みし、拡張インスタンスを再構築してください。
package/README.md CHANGED
@@ -23,7 +23,7 @@ JSON and YAML input is limited to 256 KiB, nesting depth 64, and 50,000 values.
23
23
  ## Installation
24
24
 
25
25
  ```bash
26
- pnpm add --save-exact @kubohiroya/turbowarp-structured-data@0.6.0
26
+ pnpm add --save-exact @kubohiroya/turbowarp-structured-data@0.6.1
27
27
  ```
28
28
 
29
29
  ## Quick start
@@ -243,3 +243,16 @@ pnpm run check
243
243
  SPDX-License-Identifier: MPL-2.0
244
244
 
245
245
  Bundled third-party license terms are listed in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
246
+
247
+ ## 固定引数の編集フォーム(S3-4)
248
+
249
+ 共通エディターの起動時フラグ `blockEditorFixedInputs` とDialogsがONのGUIでは、`forEachAtPath` の `MAX` だけを固定フォームとして登録します。旧GUI・フラグOFFでは追加登録を行いません。Schemaの正本は `schemas/block-editor-fixed-inputs.schema.json#/$defs/forEachAtPath/properties/MAX`、codecと登録は `src/fixed-input-editors.ts` です。既存raw値・未変更alias・未知enumは無操作保存で書き換えず、reporterを評価しません。明示変更だけを検査し、他の引数やブロック宣言、保存形式を変更しません。
250
+
251
+ MAXは整数1〜1000です。新しい入力には共通の有限数値token規則も適用し、途中入力を保存しません。MVP flagがOFFなら登録せず、NAME/PATH・staticLiteral由来の接続可否を変更しません。
252
+
253
+ 登録はoptional API(apiVersion:1)に限定し、RUNTIME_DISPOSED/明示disposeで解除します。起動フラグOFF+再読込で従来経路へ切り戻せます。依存を戻す場合はmanifest/lock/実解決を一組で復旧します。npm公開や後続段階の動的候補は含みません。
254
+
255
+ Schemaが記述する契約の根拠は実getInfoの宣言と既存の純粋なruntime変換です。SchemaBundleのretrievalUriはこのローカル文書の識別子であり、URL配信済み・HTTP取得によるref解決を意味しません。Schemaは同梱文書だけでcompileします。
256
+ 既存MAXの非空finiteなNumber alias(例`001`・`+1`・`0x10`)はNumberによる表示投影だけを行い、無変更rawを保存し直しません。空白・非finite・投影不能値はreadonlyとし、0/defaultを補いません。新しい入力にはこれらaliasを許可せずNP-finite-new-input-v1を適用します。
257
+
258
+ Registration uses the optional editor API available when the extension instance is constructed. Adding or replacing that API later is not followed automatically; reload the GUI and reconstruct the extension instance after changing the startup configuration.
@@ -65,6 +65,66 @@
65
65
  docsURI: "https://kubohiroya.github.io/turbowarp-structured-data/",
66
66
  blockIconURI: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OCA0OCI+PHJlY3QgeD0iNCIgeT0iOCIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE0IiByeD0iMyIgZmlsbD0iIzRDOTdGRiIvPjxyZWN0IHg9IjI2IiB5PSI4IiB3aWR0aD0iMTgiIGhlaWdodD0iMTQiIHJ4PSIzIiBmaWxsPSIjNTlDMDU5Ii8+PHJlY3QgeD0iMTUiIHk9IjI2IiB3aWR0aD0iMTgiIGhlaWdodD0iMTQiIHJ4PSIzIiBmaWxsPSIjRkZBQjE5Ii8+PC9zdmc+"
67
67
  };
68
+ var JsonSnapshotError = class extends Error {
69
+ constructor(kind, dataPath) {
70
+ super("JSON snapshot failed.");
71
+ this.kind = kind;
72
+ this.dataPath = dataPath;
73
+ }
74
+ };
75
+ function appendPointer(path, key) {
76
+ return `${path}/${key.replace(/~/g, "~0").replace(/\//g, "~1")}`;
77
+ }
78
+ /** Copy JSON values without invoking accessors, toJSON, or input iterators. */
79
+ function snapshotJson(value) {
80
+ const active = /* @__PURE__ */ new WeakSet();
81
+ let remaining = 1e5;
82
+ const visit = (input, path, depth) => {
83
+ if (--remaining < 0 || depth > 128) throw new JsonSnapshotError("limit", path);
84
+ if (input === null || typeof input === "string" || typeof input === "boolean") return input;
85
+ if (typeof input === "number" && Number.isFinite(input)) return input;
86
+ if (typeof input !== "object" || input === null || active.has(input)) throw new JsonSnapshotError("invalid", path);
87
+ const array = Array.isArray(input);
88
+ const prototype = Object.getPrototypeOf(input);
89
+ if (!array && prototype !== null && prototype !== Object.prototype) throw new JsonSnapshotError("invalid", path);
90
+ if (Object.getOwnPropertySymbols(input).length) throw new JsonSnapshotError("invalid", path);
91
+ active.add(input);
92
+ try {
93
+ if (array) {
94
+ const length = input.length;
95
+ if (!Number.isSafeInteger(length) || length > remaining) throw new JsonSnapshotError("limit", path);
96
+ const result = [];
97
+ for (let i = 0; i < length; i++) {
98
+ const child = appendPointer(path, String(i));
99
+ const descriptor = Object.getOwnPropertyDescriptor(input, i);
100
+ if (!descriptor || !("value" in descriptor)) throw new JsonSnapshotError("invalid", child);
101
+ result.push(visit(descriptor.value, child, depth + 1));
102
+ }
103
+ for (const key of Object.keys(input)) if (!/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= length) throw new JsonSnapshotError("invalid", appendPointer(path, key));
104
+ return result;
105
+ }
106
+ const result = {};
107
+ for (const key of Object.keys(input)) {
108
+ const child = appendPointer(path, key);
109
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
110
+ if (!descriptor || !("value" in descriptor)) throw new JsonSnapshotError("invalid", child);
111
+ Object.defineProperty(result, key, {
112
+ value: visit(descriptor.value, child, depth + 1),
113
+ enumerable: true,
114
+ writable: true,
115
+ configurable: true
116
+ });
117
+ }
118
+ return result;
119
+ } finally {
120
+ active.delete(input);
121
+ }
122
+ };
123
+ return visit(value, "", 0);
124
+ }
125
+ function isJsonRecord(value) {
126
+ return value !== null && typeof value === "object" && !Array.isArray(value);
127
+ }
68
128
  var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
69
129
  Object.defineProperty(exports, "__esModule", { value: true });
70
130
  exports.regexpCode = exports.getEsmExportName = exports.getProperty = exports.safeStringify = exports.stringify = exports.strConcat = exports.addCodeArg = exports.str = exports._ = exports.nil = exports._Code = exports.Name = exports.IDENTIFIER = exports._CodeOrName = void 0;
@@ -2727,16 +2787,16 @@
2727
2787
  }
2728
2788
  }));
2729
2789
  var data_exports = /* @__PURE__ */ __exportAll({
2730
- $id: () => $id$9,
2790
+ $id: () => $id$10,
2731
2791
  additionalProperties: () => false,
2732
2792
  default: () => data_default,
2733
2793
  description: () => description,
2734
2794
  properties: () => properties$9,
2735
2795
  required: () => required,
2736
2796
  type: () => type$9
2737
- }), $id$9, description, type$9, required, properties$9, data_default;
2797
+ }), $id$10, description, type$9, required, properties$9, data_default;
2738
2798
  var init_data = __esmMin((() => {
2739
- $id$9 = "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#";
2799
+ $id$10 = "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#";
2740
2800
  description = "Meta-schema for $data reference (JSON AnySchema extension proposal)";
2741
2801
  type$9 = "object";
2742
2802
  required = ["$data"];
@@ -2745,7 +2805,7 @@
2745
2805
  "anyOf": [{ "format": "relative-json-pointer" }, { "format": "json-pointer" }]
2746
2806
  } };
2747
2807
  data_default = {
2748
- $id: $id$9,
2808
+ $id: $id$10,
2749
2809
  description,
2750
2810
  type: type$9,
2751
2811
  required,
@@ -5997,18 +6057,18 @@
5997
6057
  };
5998
6058
  }));
5999
6059
  var json_schema_draft_07_exports = /* @__PURE__ */ __exportAll({
6000
- $id: () => $id$8,
6001
- $schema: () => $schema$8,
6060
+ $id: () => $id$9,
6061
+ $schema: () => $schema$9,
6002
6062
  default: () => json_schema_draft_07_default,
6003
6063
  definitions: () => definitions$1,
6004
6064
  properties: () => properties$8,
6005
6065
  title: () => title$8,
6006
6066
  type: () => type$8
6007
6067
  });
6008
- var $schema$8, $id$8, title$8, definitions$1, type$8, properties$8, json_schema_draft_07_default;
6068
+ var $schema$9, $id$9, title$8, definitions$1, type$8, properties$8, json_schema_draft_07_default;
6009
6069
  var init_json_schema_draft_07 = __esmMin((() => {
6010
- $schema$8 = "http://json-schema.org/draft-07/schema#";
6011
- $id$8 = "http://json-schema.org/draft-07/schema#";
6070
+ $schema$9 = "http://json-schema.org/draft-07/schema#";
6071
+ $id$9 = "http://json-schema.org/draft-07/schema#";
6012
6072
  title$8 = "Core schema meta-schema";
6013
6073
  definitions$1 = {
6014
6074
  "schemaArray": {
@@ -6139,8 +6199,8 @@
6139
6199
  "not": { "$ref": "#" }
6140
6200
  };
6141
6201
  json_schema_draft_07_default = {
6142
- $schema: $schema$8,
6143
- $id: $id$8,
6202
+ $schema: $schema$9,
6203
+ $id: $id$9,
6144
6204
  title: title$8,
6145
6205
  definitions: definitions$1,
6146
6206
  type: type$8,
@@ -6512,8 +6572,8 @@
6512
6572
  var schema_exports = /* @__PURE__ */ __exportAll({
6513
6573
  $comment: () => $comment,
6514
6574
  $dynamicAnchor: () => $dynamicAnchor$7,
6515
- $id: () => $id$7,
6516
- $schema: () => $schema$7,
6575
+ $id: () => $id$8,
6576
+ $schema: () => $schema$8,
6517
6577
  $vocabulary: () => $vocabulary$7,
6518
6578
  allOf: () => allOf,
6519
6579
  default: () => schema_default,
@@ -6521,10 +6581,10 @@
6521
6581
  title: () => title$7,
6522
6582
  type: () => type$7
6523
6583
  });
6524
- var $schema$7, $id$7, $vocabulary$7, $dynamicAnchor$7, title$7, allOf, type$7, $comment, properties$7, schema_default;
6584
+ var $schema$8, $id$8, $vocabulary$7, $dynamicAnchor$7, title$7, allOf, type$7, $comment, properties$7, schema_default;
6525
6585
  var init_schema = __esmMin((() => {
6526
- $schema$7 = "https://json-schema.org/draft/2020-12/schema";
6527
- $id$7 = "https://json-schema.org/draft/2020-12/schema";
6586
+ $schema$8 = "https://json-schema.org/draft/2020-12/schema";
6587
+ $id$8 = "https://json-schema.org/draft/2020-12/schema";
6528
6588
  $vocabulary$7 = {
6529
6589
  "https://json-schema.org/draft/2020-12/vocab/core": true,
6530
6590
  "https://json-schema.org/draft/2020-12/vocab/applicator": true,
@@ -6574,8 +6634,8 @@
6574
6634
  }
6575
6635
  };
6576
6636
  schema_default = {
6577
- $schema: $schema$7,
6578
- $id: $id$7,
6637
+ $schema: $schema$8,
6638
+ $id: $id$8,
6579
6639
  $vocabulary: $vocabulary$7,
6580
6640
  $dynamicAnchor: $dynamicAnchor$7,
6581
6641
  title: title$7,
@@ -6586,20 +6646,20 @@
6586
6646
  };
6587
6647
  }));
6588
6648
  var applicator_exports = /* @__PURE__ */ __exportAll({
6589
- $defs: () => $defs$2,
6649
+ $defs: () => $defs$3,
6590
6650
  $dynamicAnchor: () => $dynamicAnchor$6,
6591
- $id: () => $id$6,
6592
- $schema: () => $schema$6,
6651
+ $id: () => $id$7,
6652
+ $schema: () => $schema$7,
6593
6653
  $vocabulary: () => $vocabulary$6,
6594
6654
  default: () => applicator_default,
6595
6655
  properties: () => properties$6,
6596
6656
  title: () => title$6,
6597
6657
  type: () => type$6
6598
6658
  });
6599
- var $schema$6, $id$6, $vocabulary$6, $dynamicAnchor$6, title$6, type$6, properties$6, $defs$2, applicator_default;
6659
+ var $schema$7, $id$7, $vocabulary$6, $dynamicAnchor$6, title$6, type$6, properties$6, $defs$3, applicator_default;
6600
6660
  var init_applicator = __esmMin((() => {
6601
- $schema$6 = "https://json-schema.org/draft/2020-12/schema";
6602
- $id$6 = "https://json-schema.org/draft/2020-12/meta/applicator";
6661
+ $schema$7 = "https://json-schema.org/draft/2020-12/schema";
6662
+ $id$7 = "https://json-schema.org/draft/2020-12/meta/applicator";
6603
6663
  $vocabulary$6 = { "https://json-schema.org/draft/2020-12/vocab/applicator": true };
6604
6664
  $dynamicAnchor$6 = "meta";
6605
6665
  title$6 = "Applicator vocabulary meta-schema";
@@ -6634,36 +6694,36 @@
6634
6694
  "oneOf": { "$ref": "#/$defs/schemaArray" },
6635
6695
  "not": { "$dynamicRef": "#meta" }
6636
6696
  };
6637
- $defs$2 = { "schemaArray": {
6697
+ $defs$3 = { "schemaArray": {
6638
6698
  "type": "array",
6639
6699
  "minItems": 1,
6640
6700
  "items": { "$dynamicRef": "#meta" }
6641
6701
  } };
6642
6702
  applicator_default = {
6643
- $schema: $schema$6,
6644
- $id: $id$6,
6703
+ $schema: $schema$7,
6704
+ $id: $id$7,
6645
6705
  $vocabulary: $vocabulary$6,
6646
6706
  $dynamicAnchor: $dynamicAnchor$6,
6647
6707
  title: title$6,
6648
6708
  type: type$6,
6649
6709
  properties: properties$6,
6650
- $defs: $defs$2
6710
+ $defs: $defs$3
6651
6711
  };
6652
6712
  }));
6653
6713
  var unevaluated_exports = /* @__PURE__ */ __exportAll({
6654
6714
  $dynamicAnchor: () => $dynamicAnchor$5,
6655
- $id: () => $id$5,
6656
- $schema: () => $schema$5,
6715
+ $id: () => $id$6,
6716
+ $schema: () => $schema$6,
6657
6717
  $vocabulary: () => $vocabulary$5,
6658
6718
  default: () => unevaluated_default,
6659
6719
  properties: () => properties$5,
6660
6720
  title: () => title$5,
6661
6721
  type: () => type$5
6662
6722
  });
6663
- var $schema$5, $id$5, $vocabulary$5, $dynamicAnchor$5, title$5, type$5, properties$5, unevaluated_default;
6723
+ var $schema$6, $id$6, $vocabulary$5, $dynamicAnchor$5, title$5, type$5, properties$5, unevaluated_default;
6664
6724
  var init_unevaluated = __esmMin((() => {
6665
- $schema$5 = "https://json-schema.org/draft/2020-12/schema";
6666
- $id$5 = "https://json-schema.org/draft/2020-12/meta/unevaluated";
6725
+ $schema$6 = "https://json-schema.org/draft/2020-12/schema";
6726
+ $id$6 = "https://json-schema.org/draft/2020-12/meta/unevaluated";
6667
6727
  $vocabulary$5 = { "https://json-schema.org/draft/2020-12/vocab/unevaluated": true };
6668
6728
  $dynamicAnchor$5 = "meta";
6669
6729
  title$5 = "Unevaluated applicator vocabulary meta-schema";
@@ -6673,8 +6733,8 @@
6673
6733
  "unevaluatedProperties": { "$dynamicRef": "#meta" }
6674
6734
  };
6675
6735
  unevaluated_default = {
6676
- $schema: $schema$5,
6677
- $id: $id$5,
6736
+ $schema: $schema$6,
6737
+ $id: $id$6,
6678
6738
  $vocabulary: $vocabulary$5,
6679
6739
  $dynamicAnchor: $dynamicAnchor$5,
6680
6740
  title: title$5,
@@ -6684,18 +6744,18 @@
6684
6744
  }));
6685
6745
  var content_exports = /* @__PURE__ */ __exportAll({
6686
6746
  $dynamicAnchor: () => $dynamicAnchor$4,
6687
- $id: () => $id$4,
6688
- $schema: () => $schema$4,
6747
+ $id: () => $id$5,
6748
+ $schema: () => $schema$5,
6689
6749
  $vocabulary: () => $vocabulary$4,
6690
6750
  default: () => content_default,
6691
6751
  properties: () => properties$4,
6692
6752
  title: () => title$4,
6693
6753
  type: () => type$4
6694
6754
  });
6695
- var $schema$4, $id$4, $vocabulary$4, $dynamicAnchor$4, title$4, type$4, properties$4, content_default;
6755
+ var $schema$5, $id$5, $vocabulary$4, $dynamicAnchor$4, title$4, type$4, properties$4, content_default;
6696
6756
  var init_content = __esmMin((() => {
6697
- $schema$4 = "https://json-schema.org/draft/2020-12/schema";
6698
- $id$4 = "https://json-schema.org/draft/2020-12/meta/content";
6757
+ $schema$5 = "https://json-schema.org/draft/2020-12/schema";
6758
+ $id$5 = "https://json-schema.org/draft/2020-12/meta/content";
6699
6759
  $vocabulary$4 = { "https://json-schema.org/draft/2020-12/vocab/content": true };
6700
6760
  $dynamicAnchor$4 = "meta";
6701
6761
  title$4 = "Content vocabulary meta-schema";
@@ -6706,8 +6766,8 @@
6706
6766
  "contentSchema": { "$dynamicRef": "#meta" }
6707
6767
  };
6708
6768
  content_default = {
6709
- $schema: $schema$4,
6710
- $id: $id$4,
6769
+ $schema: $schema$5,
6770
+ $id: $id$5,
6711
6771
  $vocabulary: $vocabulary$4,
6712
6772
  $dynamicAnchor: $dynamicAnchor$4,
6713
6773
  title: title$4,
@@ -6716,20 +6776,20 @@
6716
6776
  };
6717
6777
  }));
6718
6778
  var core_exports = /* @__PURE__ */ __exportAll({
6719
- $defs: () => $defs$1,
6779
+ $defs: () => $defs$2,
6720
6780
  $dynamicAnchor: () => $dynamicAnchor$3,
6721
- $id: () => $id$3,
6722
- $schema: () => $schema$3,
6781
+ $id: () => $id$4,
6782
+ $schema: () => $schema$4,
6723
6783
  $vocabulary: () => $vocabulary$3,
6724
6784
  default: () => core_default,
6725
6785
  properties: () => properties$3,
6726
6786
  title: () => title$3,
6727
6787
  type: () => type$3
6728
6788
  });
6729
- var $schema$3, $id$3, $vocabulary$3, $dynamicAnchor$3, title$3, type$3, properties$3, $defs$1, core_default;
6789
+ var $schema$4, $id$4, $vocabulary$3, $dynamicAnchor$3, title$3, type$3, properties$3, $defs$2, core_default;
6730
6790
  var init_core = __esmMin((() => {
6731
- $schema$3 = "https://json-schema.org/draft/2020-12/schema";
6732
- $id$3 = "https://json-schema.org/draft/2020-12/meta/core";
6791
+ $schema$4 = "https://json-schema.org/draft/2020-12/schema";
6792
+ $id$4 = "https://json-schema.org/draft/2020-12/meta/core";
6733
6793
  $vocabulary$3 = { "https://json-schema.org/draft/2020-12/vocab/core": true };
6734
6794
  $dynamicAnchor$3 = "meta";
6735
6795
  title$3 = "Core vocabulary meta-schema";
@@ -6756,7 +6816,7 @@
6756
6816
  "additionalProperties": { "$dynamicRef": "#meta" }
6757
6817
  }
6758
6818
  };
6759
- $defs$1 = {
6819
+ $defs$2 = {
6760
6820
  "anchorString": {
6761
6821
  "type": "string",
6762
6822
  "pattern": "^[A-Za-z_][-A-Za-z0-9._]*$"
@@ -6771,38 +6831,38 @@
6771
6831
  }
6772
6832
  };
6773
6833
  core_default = {
6774
- $schema: $schema$3,
6775
- $id: $id$3,
6834
+ $schema: $schema$4,
6835
+ $id: $id$4,
6776
6836
  $vocabulary: $vocabulary$3,
6777
6837
  $dynamicAnchor: $dynamicAnchor$3,
6778
6838
  title: title$3,
6779
6839
  type: type$3,
6780
6840
  properties: properties$3,
6781
- $defs: $defs$1
6841
+ $defs: $defs$2
6782
6842
  };
6783
6843
  }));
6784
6844
  var format_annotation_exports = /* @__PURE__ */ __exportAll({
6785
6845
  $dynamicAnchor: () => $dynamicAnchor$2,
6786
- $id: () => $id$2,
6787
- $schema: () => $schema$2,
6846
+ $id: () => $id$3,
6847
+ $schema: () => $schema$3,
6788
6848
  $vocabulary: () => $vocabulary$2,
6789
6849
  default: () => format_annotation_default,
6790
6850
  properties: () => properties$2,
6791
6851
  title: () => title$2,
6792
6852
  type: () => type$2
6793
6853
  });
6794
- var $schema$2, $id$2, $vocabulary$2, $dynamicAnchor$2, title$2, type$2, properties$2, format_annotation_default;
6854
+ var $schema$3, $id$3, $vocabulary$2, $dynamicAnchor$2, title$2, type$2, properties$2, format_annotation_default;
6795
6855
  var init_format_annotation = __esmMin((() => {
6796
- $schema$2 = "https://json-schema.org/draft/2020-12/schema";
6797
- $id$2 = "https://json-schema.org/draft/2020-12/meta/format-annotation";
6856
+ $schema$3 = "https://json-schema.org/draft/2020-12/schema";
6857
+ $id$3 = "https://json-schema.org/draft/2020-12/meta/format-annotation";
6798
6858
  $vocabulary$2 = { "https://json-schema.org/draft/2020-12/vocab/format-annotation": true };
6799
6859
  $dynamicAnchor$2 = "meta";
6800
6860
  title$2 = "Format vocabulary meta-schema for annotation results";
6801
6861
  type$2 = ["object", "boolean"];
6802
6862
  properties$2 = { "format": { "type": "string" } };
6803
6863
  format_annotation_default = {
6804
- $schema: $schema$2,
6805
- $id: $id$2,
6864
+ $schema: $schema$3,
6865
+ $id: $id$3,
6806
6866
  $vocabulary: $vocabulary$2,
6807
6867
  $dynamicAnchor: $dynamicAnchor$2,
6808
6868
  title: title$2,
@@ -6812,18 +6872,18 @@
6812
6872
  }));
6813
6873
  var meta_data_exports = /* @__PURE__ */ __exportAll({
6814
6874
  $dynamicAnchor: () => $dynamicAnchor$1,
6815
- $id: () => $id$1,
6816
- $schema: () => $schema$1,
6875
+ $id: () => $id$2,
6876
+ $schema: () => $schema$2,
6817
6877
  $vocabulary: () => $vocabulary$1,
6818
6878
  default: () => meta_data_default,
6819
6879
  properties: () => properties$1,
6820
6880
  title: () => title$1,
6821
6881
  type: () => type$1
6822
6882
  });
6823
- var $schema$1, $id$1, $vocabulary$1, $dynamicAnchor$1, title$1, type$1, properties$1, meta_data_default;
6883
+ var $schema$2, $id$2, $vocabulary$1, $dynamicAnchor$1, title$1, type$1, properties$1, meta_data_default;
6824
6884
  var init_meta_data = __esmMin((() => {
6825
- $schema$1 = "https://json-schema.org/draft/2020-12/schema";
6826
- $id$1 = "https://json-schema.org/draft/2020-12/meta/meta-data";
6885
+ $schema$2 = "https://json-schema.org/draft/2020-12/schema";
6886
+ $id$2 = "https://json-schema.org/draft/2020-12/meta/meta-data";
6827
6887
  $vocabulary$1 = { "https://json-schema.org/draft/2020-12/vocab/meta-data": true };
6828
6888
  $dynamicAnchor$1 = "meta";
6829
6889
  title$1 = "Meta-data vocabulary meta-schema";
@@ -6850,8 +6910,8 @@
6850
6910
  }
6851
6911
  };
6852
6912
  meta_data_default = {
6853
- $schema: $schema$1,
6854
- $id: $id$1,
6913
+ $schema: $schema$2,
6914
+ $id: $id$2,
6855
6915
  $vocabulary: $vocabulary$1,
6856
6916
  $dynamicAnchor: $dynamicAnchor$1,
6857
6917
  title: title$1,
@@ -6860,20 +6920,20 @@
6860
6920
  };
6861
6921
  }));
6862
6922
  var validation_exports = /* @__PURE__ */ __exportAll({
6863
- $defs: () => $defs,
6923
+ $defs: () => $defs$1,
6864
6924
  $dynamicAnchor: () => $dynamicAnchor,
6865
- $id: () => $id,
6866
- $schema: () => $schema,
6925
+ $id: () => $id$1,
6926
+ $schema: () => $schema$1,
6867
6927
  $vocabulary: () => $vocabulary,
6868
6928
  default: () => validation_default,
6869
6929
  properties: () => properties,
6870
6930
  title: () => title,
6871
6931
  type: () => type
6872
6932
  });
6873
- var $schema, $id, $vocabulary, $dynamicAnchor, title, type, properties, $defs, validation_default;
6933
+ var $schema$1, $id$1, $vocabulary, $dynamicAnchor, title, type, properties, $defs$1, validation_default;
6874
6934
  var init_validation = __esmMin((() => {
6875
- $schema = "https://json-schema.org/draft/2020-12/schema";
6876
- $id = "https://json-schema.org/draft/2020-12/meta/validation";
6935
+ $schema$1 = "https://json-schema.org/draft/2020-12/schema";
6936
+ $id$1 = "https://json-schema.org/draft/2020-12/meta/validation";
6877
6937
  $vocabulary = { "https://json-schema.org/draft/2020-12/vocab/validation": true };
6878
6938
  $dynamicAnchor = "meta";
6879
6939
  title = "Validation vocabulary meta-schema";
@@ -6923,7 +6983,7 @@
6923
6983
  "additionalProperties": { "$ref": "#/$defs/stringArray" }
6924
6984
  }
6925
6985
  };
6926
- $defs = {
6986
+ $defs$1 = {
6927
6987
  "nonNegativeInteger": {
6928
6988
  "type": "integer",
6929
6989
  "minimum": 0
@@ -6949,14 +7009,14 @@
6949
7009
  }
6950
7010
  };
6951
7011
  validation_default = {
6952
- $schema,
6953
- $id,
7012
+ $schema: $schema$1,
7013
+ $id: $id$1,
6954
7014
  $vocabulary,
6955
7015
  $dynamicAnchor,
6956
7016
  title,
6957
7017
  type,
6958
7018
  properties,
6959
- $defs
7019
+ $defs: $defs$1
6960
7020
  };
6961
7021
  }));
6962
7022
  var require_json_schema_2020_12 = /* @__PURE__ */ __commonJSMin(((exports) => {
@@ -7349,66 +7409,6 @@
7349
7409
  Object.defineProperty(exports, "__esModule", { value: true });
7350
7410
  exports.default = formatsPlugin;
7351
7411
  }));
7352
- var JsonSnapshotError = class extends Error {
7353
- constructor(kind, dataPath) {
7354
- super("JSON snapshot failed.");
7355
- this.kind = kind;
7356
- this.dataPath = dataPath;
7357
- }
7358
- };
7359
- function appendPointer(path, key) {
7360
- return `${path}/${key.replace(/~/g, "~0").replace(/\//g, "~1")}`;
7361
- }
7362
- /** Copy JSON values without invoking accessors, toJSON, or input iterators. */
7363
- function snapshotJson(value) {
7364
- const active = /* @__PURE__ */ new WeakSet();
7365
- let remaining = 1e5;
7366
- const visit = (input, path, depth) => {
7367
- if (--remaining < 0 || depth > 128) throw new JsonSnapshotError("limit", path);
7368
- if (input === null || typeof input === "string" || typeof input === "boolean") return input;
7369
- if (typeof input === "number" && Number.isFinite(input)) return input;
7370
- if (typeof input !== "object" || input === null || active.has(input)) throw new JsonSnapshotError("invalid", path);
7371
- const array = Array.isArray(input);
7372
- const prototype = Object.getPrototypeOf(input);
7373
- if (!array && prototype !== null && prototype !== Object.prototype) throw new JsonSnapshotError("invalid", path);
7374
- if (Object.getOwnPropertySymbols(input).length) throw new JsonSnapshotError("invalid", path);
7375
- active.add(input);
7376
- try {
7377
- if (array) {
7378
- const length = input.length;
7379
- if (!Number.isSafeInteger(length) || length > remaining) throw new JsonSnapshotError("limit", path);
7380
- const result = [];
7381
- for (let i = 0; i < length; i++) {
7382
- const child = appendPointer(path, String(i));
7383
- const descriptor = Object.getOwnPropertyDescriptor(input, i);
7384
- if (!descriptor || !("value" in descriptor)) throw new JsonSnapshotError("invalid", child);
7385
- result.push(visit(descriptor.value, child, depth + 1));
7386
- }
7387
- for (const key of Object.keys(input)) if (!/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= length) throw new JsonSnapshotError("invalid", appendPointer(path, key));
7388
- return result;
7389
- }
7390
- const result = {};
7391
- for (const key of Object.keys(input)) {
7392
- const child = appendPointer(path, key);
7393
- const descriptor = Object.getOwnPropertyDescriptor(input, key);
7394
- if (!descriptor || !("value" in descriptor)) throw new JsonSnapshotError("invalid", child);
7395
- Object.defineProperty(result, key, {
7396
- value: visit(descriptor.value, child, depth + 1),
7397
- enumerable: true,
7398
- writable: true,
7399
- configurable: true
7400
- });
7401
- }
7402
- return result;
7403
- } finally {
7404
- active.delete(input);
7405
- }
7406
- };
7407
- return visit(value, "", 0);
7408
- }
7409
- function isJsonRecord(value) {
7410
- return value !== null && typeof value === "object" && !Array.isArray(value);
7411
- }
7412
7412
  var import_ajv = require_ajv();
7413
7413
  var import__2020 = require__2020();
7414
7414
  var import_dist = /* @__PURE__ */ __toESM(require_dist(), 1);
@@ -7753,6 +7753,156 @@
7753
7753
  });
7754
7754
  }
7755
7755
  }
7756
+ var numberToken = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/u;
7757
+ var invalidNumber = Object.freeze({
7758
+ kind: "invalid",
7759
+ code: "fixed-number-input-invalid"
7760
+ });
7761
+ /**
7762
+ * Parse a NEW numeric draft according to NP-finite-new-input-v1.
7763
+ * Never apply this to opening/no-op raw values or use it as an owner validator.
7764
+ * Keep the validated token so negative zero, precision and exponent spelling survive.
7765
+ */
7766
+ function parseFixedNumberInput(input) {
7767
+ if (typeof input === "number") {
7768
+ if (!Number.isFinite(input)) return invalidNumber;
7769
+ return Object.freeze({
7770
+ kind: "valid",
7771
+ token: Object.is(input, -0) ? "-0" : String(input),
7772
+ value: input
7773
+ });
7774
+ }
7775
+ if (typeof input !== "string") return invalidNumber;
7776
+ const token = input.trim();
7777
+ if (!numberToken.test(token)) return invalidNumber;
7778
+ const value = Number(token);
7779
+ return Number.isFinite(value) ? Object.freeze({
7780
+ kind: "valid",
7781
+ token,
7782
+ value
7783
+ }) : invalidNumber;
7784
+ }
7785
+ var block_editor_fixed_inputs_schema_default = {
7786
+ $schema: "https://json-schema.org/draft/2020-12/schema",
7787
+ $id: "https://kubohiroya.github.io/turbowarp-structured-data/schemas/block-editor-fixed-inputs.schema.json",
7788
+ $defs: { "forEachAtPath": {
7789
+ "type": "object",
7790
+ "properties": { "MAX": {
7791
+ "type": "integer",
7792
+ "minimum": 1,
7793
+ "maximum": 1e3
7794
+ } },
7795
+ "required": ["MAX"],
7796
+ "additionalProperties": true
7797
+ } }
7798
+ };
7799
+ var source$1 = {
7800
+ repository: "turbowarp-structured-data",
7801
+ path: "schemas/block-editor-fixed-inputs.schema.json",
7802
+ symbol: "#/$defs/forEachAtPath/properties/MAX"
7803
+ };
7804
+ /** The on-disk owner Schema is canonical; this is its single-field projection. */
7805
+ var fixedInputSchemaBundle = {
7806
+ identity: {
7807
+ ownerId: "turbowarp-structured-data",
7808
+ contractId: "forEachAtPath.MAX",
7809
+ revision: "fixed-inputs-v1",
7810
+ variantId: "default"
7811
+ },
7812
+ dialect: "2020-12",
7813
+ root: {
7814
+ retrievalUri: "https://kubohiroya.github.io/turbowarp-structured-data/schemas/forEachAtPath-MAX.schema.json",
7815
+ document: block_editor_fixed_inputs_schema_default.$defs.forEachAtPath.properties.MAX,
7816
+ artifact: {
7817
+ role: "canonical-schema",
7818
+ source: source$1
7819
+ }
7820
+ },
7821
+ resources: [],
7822
+ provenance: {
7823
+ canonicalSources: [source$1, {
7824
+ repository: "turbowarp-structured-data",
7825
+ path: "src/core.ts",
7826
+ symbol: "iterationEntries"
7827
+ }],
7828
+ generatedFiles: [{
7829
+ repository: "turbowarp-structured-data",
7830
+ path: "docs/schemas/block-editor-fixed-inputs.schema.json"
7831
+ }],
7832
+ referenceSchemas: [],
7833
+ generationSources: [{
7834
+ repository: "turbowarp-structured-data",
7835
+ path: "scripts/generate-fixed-input-docs.mjs"
7836
+ }]
7837
+ }
7838
+ };
7839
+ var fixedInputEditorDefinitions = [{
7840
+ apiVersion: 1,
7841
+ extensionId: extensionConfig.id,
7842
+ editorId: "forEachAtPath-fixed-inputs",
7843
+ opcodes: [`${extensionConfig.id}_forEachAtPath`],
7844
+ contractId: "turbowarp-structured-data:forEachAtPath.MAX",
7845
+ contractRevision: "fixed-inputs-v1",
7846
+ bindings: [{
7847
+ argument: "MAX",
7848
+ schemaBundle: fixedInputSchemaBundle,
7849
+ control: {
7850
+ kind: "number",
7851
+ label: "MAX"
7852
+ },
7853
+ read(value) {
7854
+ if (typeof value !== "string" && typeof value !== "number") throw new TypeError("Expected a literal maximum.");
7855
+ if (typeof value === "string" && value.trim() === "") throw new TypeError("Blank maximum stays readonly.");
7856
+ const number = Number(value);
7857
+ if (!Number.isFinite(number)) throw new TypeError("Maximum must be finite.");
7858
+ return number;
7859
+ },
7860
+ encode(value) {
7861
+ const parsed = parseFixedNumberInput(value);
7862
+ if (parsed.kind !== "valid") throw new TypeError("Expected a finite numeric token.");
7863
+ return parsed.token;
7864
+ },
7865
+ validateCandidate(value) {
7866
+ const parsed = parseFixedNumberInput(value);
7867
+ return parsed.kind === "valid" && Number.isInteger(parsed.value) && parsed.value >= 1 && parsed.value <= 1e3 ? [] : [{
7868
+ path: "/inputs/MAX",
7869
+ message: "Maximum must be an integer from 1 to 1000."
7870
+ }];
7871
+ }
7872
+ }],
7873
+ validationContext: () => ({
7874
+ consumerId: extensionConfig.id,
7875
+ nowMs: Date.now(),
7876
+ settings: null
7877
+ })
7878
+ }];
7879
+ /** Register metadata only; the GUI startup capability owns editor activation. */
7880
+ function registerFixedInputEditors(scratch, definitions, runtimeOverride) {
7881
+ const host = scratch;
7882
+ const api = host.gui?.blockEditors;
7883
+ if (!definitions.length || api?.apiVersion !== 1 || typeof api.registerFixedInputEditor !== "function") return () => {};
7884
+ const runtime = runtimeOverride ?? host.vm?.runtime;
7885
+ const removers = [];
7886
+ let active = true;
7887
+ const dispose = () => {
7888
+ if (!active) return;
7889
+ active = false;
7890
+ runtime?.off?.("RUNTIME_DISPOSED", dispose);
7891
+ for (const remove of removers.splice(0).reverse()) remove();
7892
+ };
7893
+ try {
7894
+ for (const definition of definitions) {
7895
+ const remove = api.registerFixedInputEditor.call(api, definition);
7896
+ if (typeof remove !== "function") throw new TypeError("Invalid fixed input registration disposer.");
7897
+ removers.push(remove);
7898
+ }
7899
+ runtime?.on?.("RUNTIME_DISPOSED", dispose);
7900
+ } catch (error) {
7901
+ dispose();
7902
+ throw error;
7903
+ }
7904
+ return dispose;
7905
+ }
7756
7906
  function deepFreeze(value) {
7757
7907
  if (value !== null && typeof value === "object") {
7758
7908
  Object.values(value).forEach(deepFreeze);
@@ -14361,6 +14511,8 @@
14361
14511
  "markdown",
14362
14512
  "raw"
14363
14513
  ];
14514
+ var NAMED_DATA_SCHEMA_ID_PATTERN = /^[a-z][a-z0-9.-]{0,127}$/u;
14515
+ var NAMED_DATA_SCHEMA_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/u;
14364
14516
  var NAMED_DATA_ERROR_CODES = [
14365
14517
  "NAMED_DATA_INVALID_REF",
14366
14518
  "NAMED_DATA_INVALID_REGISTRY",
@@ -14369,6 +14521,9 @@
14369
14521
  "NAMED_DATA_NOT_FOUND",
14370
14522
  "NAMED_DATA_KIND_MISMATCH",
14371
14523
  "NAMED_DATA_SCOPE_MISMATCH",
14524
+ "NAMED_DATA_SCHEMA_NOT_FOUND",
14525
+ "NAMED_DATA_SCHEMA_VERSION_MISMATCH",
14526
+ "NAMED_DATA_SCHEMA_UNRESOLVED",
14372
14527
  "NAMED_DATA_REPRESENTATION_UNSUPPORTED",
14373
14528
  "NAMED_DATA_INVALID_METADATA",
14374
14529
  "NAMED_DATA_BODY_TOO_LARGE",
@@ -14589,9 +14744,21 @@
14589
14744
  if (!reference || typeof reference !== "object") throw invalidReference();
14590
14745
  requireNamespace(reference.namespace);
14591
14746
  if (typeof reference.name !== "string" || reference.name.length === 0 || reference.name.length > 256 || containsControlCharacter(reference.name) || !NAMED_DATA_KINDS.includes(reference.kind) || !NAMED_DATA_SCOPES.includes(reference.scope) || !NAMED_DATA_REPRESENTATIONS.includes(representation)) throw invalidReference();
14747
+ if (reference.schemaRef !== void 0) validateSchemaRef(reference.kind, reference.schemaRef);
14592
14748
  }
14593
14749
  function validateMetadata(metadata, reference, representation) {
14594
14750
  if (metadata.representation !== representation || !isNativeRepresentation(metadata.reference.kind, metadata.nativeRepresentation) || metadata.reference.namespace !== reference.namespace || metadata.reference.name !== reference.name || metadata.reference.kind !== reference.kind || metadata.reference.scope !== reference.scope || typeof metadata.mediaType !== "string" || metadata.mediaType.length === 0 || typeof metadata.revision !== "string" || metadata.revision.length === 0 || typeof metadata.replayable !== "boolean" || metadata.byteLength !== void 0 && (!Number.isSafeInteger(metadata.byteLength) || metadata.byteLength < 0) || metadata.digest !== void 0 && !/^sha256-[A-Za-z0-9_-]+$/u.test(metadata.digest)) throw new NamedDataError("NAMED_DATA_INVALID_METADATA", "Provider returned invalid metadata.");
14751
+ const expectedSchema = reference.schemaRef;
14752
+ const actualSchema = metadata.schemaRef;
14753
+ if (actualSchema !== void 0) validateSchemaRef(metadata.reference.kind, actualSchema, "NAMED_DATA_INVALID_METADATA");
14754
+ if (metadata.reference.schemaRef !== void 0) validateSchemaRef(metadata.reference.kind, metadata.reference.schemaRef, "NAMED_DATA_INVALID_METADATA");
14755
+ if (!sameSchemaRef(expectedSchema, actualSchema) || !sameSchemaRef(expectedSchema, metadata.reference.schemaRef)) throw new NamedDataError("NAMED_DATA_SCHEMA_VERSION_MISMATCH", "Provider schemaRef does not match the requested reference.");
14756
+ }
14757
+ function validateSchemaRef(kind, schemaRef, errorCode = "NAMED_DATA_INVALID_REF") {
14758
+ if (kind !== "structured" || typeof schemaRef !== "object" || schemaRef === null || !NAMED_DATA_SCHEMA_ID_PATTERN.test(String(schemaRef.id ?? "")) || !NAMED_DATA_SCHEMA_VERSION_PATTERN.test(String(schemaRef.version ?? "")) || Object.keys(schemaRef).some((key) => key !== "id" && key !== "version")) throw new NamedDataError(errorCode, "Invalid schemaRef; schema references require a structured value, stable id, and full SemVer version.");
14759
+ }
14760
+ function sameSchemaRef(left, right) {
14761
+ return left === void 0 ? right === void 0 : right !== void 0 && left.id === right.id && left.version === right.version;
14595
14762
  }
14596
14763
  function isNativeRepresentation(kind, representation) {
14597
14764
  if (kind === "structured") return representation === "json" || representation === "yaml";
@@ -14628,7 +14795,10 @@
14628
14795
  const value = globalThis.__TW_NAMED_DATA_FEATURE_FLAGS__?.[name];
14629
14796
  return value === true || value === "true";
14630
14797
  }
14631
- Object.freeze({ NAMED_DATA_REGISTRY_MVP: configuredFlag("NAMED_DATA_REGISTRY_MVP") });
14798
+ Object.freeze({
14799
+ NAMED_DATA_REGISTRY_MVP: configuredFlag("NAMED_DATA_REGISTRY_MVP"),
14800
+ NAMED_DATA_SCHEMA_REF: configuredFlag("NAMED_DATA_SCHEMA_REF")
14801
+ });
14632
14802
  var STRUCTURED_DATA_NAMESPACE = "structured";
14633
14803
  var STRUCTURED_BODY_MAX_BYTES = 262144;
14634
14804
  var StructuredDataNamedDataProvider = class {
@@ -14714,24 +14884,44 @@
14714
14884
  var blockDefinitions = block_definitions_default.blocks;
14715
14885
  var StructuredDataExtension = class {
14716
14886
  constructor(enabled = isStructuredDataMvpEnabled(), namedDataEnabled = isNamedDataRegistryMvpEnabled()) {
14887
+ this.disposed = false;
14717
14888
  this.registries = /* @__PURE__ */ new WeakMap();
14718
14889
  this.iterationStacks = /* @__PURE__ */ new WeakMap();
14719
14890
  this.nextRevision = 1;
14720
14891
  this.enabled = enabled;
14721
14892
  this.unregisterSourceEditors = registerSourceEditors(Scratch, enabled ? sourceEditorDefinitions : []);
14722
- if (namedDataEnabled) {
14723
- this.namedDataProvider = new StructuredDataNamedDataProvider({ get: (target, name) => this.registries.get(target)?.get(name) });
14724
- const runtime = Scratch.vm?.runtime;
14725
- if (runtime) {
14726
- this.namedDataRegistry = installNamedDataRegistry(runtime);
14727
- this.namedDataRegistration = this.namedDataRegistry.registerProvider(this.namedDataProvider, { lifetime: "persistent" });
14728
- this.unbindNamedDataRegistryLifecycle = bindNamedDataRegistryLifecycle(runtime, this.namedDataRegistry);
14729
- runtime.on?.("RUNTIME_DISPOSED", () => {
14730
- this.dispose();
14731
- });
14893
+ const runtime = Scratch.vm?.runtime;
14894
+ const onDispose = () => {
14895
+ this.dispose();
14896
+ };
14897
+ const onStop = () => this.clearRuntimeState();
14898
+ this.releaseRuntimeListeners = () => {
14899
+ runtime?.off?.("RUNTIME_DISPOSED", onDispose);
14900
+ runtime?.off?.("PROJECT_STOP_ALL", onStop);
14901
+ };
14902
+ let unregisterFixed = () => {};
14903
+ try {
14904
+ unregisterFixed = registerFixedInputEditors(Scratch, enabled ? fixedInputEditorDefinitions : []);
14905
+ this.unregisterFixedInputEditors = unregisterFixed;
14906
+ if (namedDataEnabled) {
14907
+ this.namedDataProvider = new StructuredDataNamedDataProvider({ get: (target, name) => this.registries.get(target)?.get(name) });
14908
+ const runtime = Scratch.vm?.runtime;
14909
+ if (runtime) {
14910
+ this.namedDataRegistry = installNamedDataRegistry(runtime);
14911
+ this.namedDataRegistration = this.namedDataRegistry.registerProvider(this.namedDataProvider, { lifetime: "persistent" });
14912
+ this.unbindNamedDataRegistryLifecycle = bindNamedDataRegistryLifecycle(runtime, this.namedDataRegistry);
14913
+ runtime.on?.("RUNTIME_DISPOSED", onDispose);
14914
+ }
14732
14915
  }
14916
+ runtime?.on?.("PROJECT_STOP_ALL", onStop);
14917
+ } catch (error) {
14918
+ this.releaseRuntimeListeners();
14919
+ unregisterFixed();
14920
+ this.unregisterSourceEditors();
14921
+ this.unbindNamedDataRegistryLifecycle?.();
14922
+ this.namedDataRegistration?.unregister().catch(() => {});
14923
+ throw error;
14733
14924
  }
14734
- Scratch.vm?.runtime?.on?.("PROJECT_STOP_ALL", () => this.clearRuntimeState());
14735
14925
  }
14736
14926
  getNamedDataProvider() {
14737
14927
  return this.namedDataProvider;
@@ -14740,6 +14930,10 @@
14740
14930
  return this.namedDataRegistry;
14741
14931
  }
14742
14932
  async dispose() {
14933
+ if (this.disposed) return;
14934
+ this.disposed = true;
14935
+ this.releaseRuntimeListeners();
14936
+ this.unregisterFixedInputEditors();
14743
14937
  this.unregisterSourceEditors();
14744
14938
  await this.namedDataRegistration?.unregister();
14745
14939
  this.unbindNamedDataRegistryLifecycle?.();
package/docs/index.html CHANGED
@@ -52,7 +52,7 @@
52
52
  <li><a href="#block-currentIndex"><code>current index</code></a></li>
53
53
  <li><a href="#block-currentValueJson"><code>current value JSON</code></a></li>
54
54
  </ul></nav>
55
- <article id="block-parseJson" class="block-reference-entry"><h3><code>parse JSON [TEXT] as [NAME]</code></h3><p>Parse JSON text into the target-local structured data namespace.</p><p>Block type: COMMAND. Opcode: <code>parseJson</code>. <a href="#block-parseJson">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>TEXT</code></th><td>STRING</td><td><code>{&quot;name&quot;:&quot;TurboWarp&quot;}</code></td><td></td></tr><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr></tbody></table></div></article><article id="block-parseYaml" class="block-reference-entry"><h3><code>parse YAML [TEXT] as [NAME]</code></h3><p>Parse safe YAML text into the target-local structured data namespace.</p><p>Block type: COMMAND. Opcode: <code>parseYaml</code>. <a href="#block-parseYaml">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>TEXT</code></th><td>STRING</td><td><code>name: TurboWarp</code></td><td></td></tr><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr></tbody></table></div></article><article id="block-toJson" class="block-reference-entry"><h3><code>to JSON [NAME]</code></h3><p>Render named structured data as canonical JSON.</p><p>Block type: REPORTER. Opcode: <code>toJson</code>. <a href="#block-toJson">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr></tbody></table></div></article><article id="block-toYaml" class="block-reference-entry"><h3><code>to YAML [NAME]</code></h3><p>Render named structured data as deterministic YAML.</p><p>Block type: REPORTER. Opcode: <code>toYaml</code>. <a href="#block-toYaml">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr></tbody></table></div></article><article id="block-hasStructuredData" class="block-reference-entry"><h3><code>structured data [NAME] exists?</code></h3><p>Report whether the current target has named structured data.</p><p>Block type: BOOLEAN. Opcode: <code>hasStructuredData</code>. <a href="#block-hasStructuredData">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr></tbody></table></div></article><article id="block-deleteStructuredData" class="block-reference-entry"><h3><code>delete structured data [NAME]</code></h3><p>Delete named structured data from the current target.</p><p>Block type: COMMAND. Opcode: <code>deleteStructuredData</code>. <a href="#block-deleteStructuredData">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr></tbody></table></div></article><article id="block-getJsonAtPath" class="block-reference-entry"><h3><code>get JSON from [NAME] at path [PATH]</code></h3><p>Return a named value at a path as JSON.</p><p>Block type: REPORTER. Opcode: <code>getJsonAtPath</code>. <a href="#block-getJsonAtPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$.name</code></td><td></td></tr></tbody></table></div></article><article id="block-hasPath" class="block-reference-entry"><h3><code>structured data [NAME] has path [PATH]?</code></h3><p>Report whether a path exists in named structured data.</p><p>Block type: BOOLEAN. Opcode: <code>hasPath</code>. <a href="#block-hasPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$.name</code></td><td></td></tr></tbody></table></div></article><article id="block-setJsonAtPath" class="block-reference-entry"><h3><code>set [NAME] at path [PATH] to JSON [VALUE]</code></h3><p>Immutably update a named value and replace its binding.</p><p>Block type: COMMAND. Opcode: <code>setJsonAtPath</code>. <a href="#block-setJsonAtPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$.name</code></td><td></td></tr><tr><th scope="row"><code>VALUE</code></th><td>STRING</td><td><code>&quot;Scratch&quot;</code></td><td></td></tr></tbody></table></div></article><article id="block-deleteAtPath" class="block-reference-entry"><h3><code>delete path [PATH] from [NAME]</code></h3><p>Immutably delete a path and replace its named binding.</p><p>Block type: COMMAND. Opcode: <code>deleteAtPath</code>. <a href="#block-deleteAtPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$.name</code></td><td></td></tr></tbody></table></div></article><article id="block-keysAtPath" class="block-reference-entry"><h3><code>keys in [NAME] at path [PATH]</code></h3><p>Return sorted object keys as JSON.</p><p>Block type: REPORTER. Opcode: <code>keysAtPath</code>. <a href="#block-keysAtPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$</code></td><td></td></tr></tbody></table></div></article><article id="block-lengthAtPath" class="block-reference-entry"><h3><code>length of array in [NAME] at path [PATH]</code></h3><p>Return the array length.</p><p>Block type: REPORTER. Opcode: <code>lengthAtPath</code>. <a href="#block-lengthAtPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$</code></td><td></td></tr></tbody></table></div></article><article id="block-forEachAtPath" class="block-reference-entry"><h3><code>for each in [NAME] at path [PATH] max [MAX]</code></h3><p>Iterate named data deterministically with a static maximum.</p><p>Block type: LOOP. Opcode: <code>forEachAtPath</code>. <a href="#block-forEachAtPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$</code></td><td></td></tr><tr><th scope="row"><code>MAX</code></th><td>NUMBER</td><td><code>100</code></td><td></td></tr></tbody></table></div></article><article id="block-currentKey" class="block-reference-entry"><h3><code>current key</code></h3><p>Return the current object key or array index string.</p><p>Block type: REPORTER. Opcode: <code>currentKey</code>. <a href="#block-currentKey">Permanent link</a></p><p>This block has no arguments.</p></article><article id="block-currentIndex" class="block-reference-entry"><h3><code>current index</code></h3><p>Return the zero-based iteration number.</p><p>Block type: REPORTER. Opcode: <code>currentIndex</code>. <a href="#block-currentIndex">Permanent link</a></p><p>This block has no arguments.</p></article><article id="block-currentValueJson" class="block-reference-entry"><h3><code>current value JSON</code></h3><p>Return the current iteration value as JSON.</p><p>Block type: REPORTER. Opcode: <code>currentValueJson</code>. <a href="#block-currentValueJson">Permanent link</a></p><p>This block has no arguments.</p></article>
55
+ <article id="block-parseJson" class="block-reference-entry"><h3><code>parse JSON [TEXT] as [NAME]</code></h3><p>Parse JSON text into the target-local structured data namespace.</p><p>Block type: COMMAND. Opcode: <code>parseJson</code>. <a href="#block-parseJson">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>TEXT</code></th><td>STRING</td><td><code>{&quot;name&quot;:&quot;TurboWarp&quot;}</code></td><td></td></tr><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr></tbody></table></div></article><article id="block-parseYaml" class="block-reference-entry"><h3><code>parse YAML [TEXT] as [NAME]</code></h3><p>Parse safe YAML text into the target-local structured data namespace.</p><p>Block type: COMMAND. Opcode: <code>parseYaml</code>. <a href="#block-parseYaml">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>TEXT</code></th><td>STRING</td><td><code>name: TurboWarp</code></td><td></td></tr><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr></tbody></table></div></article><article id="block-toJson" class="block-reference-entry"><h3><code>to JSON [NAME]</code></h3><p>Render named structured data as canonical JSON.</p><p>Block type: REPORTER. Opcode: <code>toJson</code>. <a href="#block-toJson">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr></tbody></table></div></article><article id="block-toYaml" class="block-reference-entry"><h3><code>to YAML [NAME]</code></h3><p>Render named structured data as deterministic YAML.</p><p>Block type: REPORTER. Opcode: <code>toYaml</code>. <a href="#block-toYaml">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr></tbody></table></div></article><article id="block-hasStructuredData" class="block-reference-entry"><h3><code>structured data [NAME] exists?</code></h3><p>Report whether the current target has named structured data.</p><p>Block type: BOOLEAN. Opcode: <code>hasStructuredData</code>. <a href="#block-hasStructuredData">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr></tbody></table></div></article><article id="block-deleteStructuredData" class="block-reference-entry"><h3><code>delete structured data [NAME]</code></h3><p>Delete named structured data from the current target.</p><p>Block type: COMMAND. Opcode: <code>deleteStructuredData</code>. <a href="#block-deleteStructuredData">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr></tbody></table></div></article><article id="block-getJsonAtPath" class="block-reference-entry"><h3><code>get JSON from [NAME] at path [PATH]</code></h3><p>Return a named value at a path as JSON.</p><p>Block type: REPORTER. Opcode: <code>getJsonAtPath</code>. <a href="#block-getJsonAtPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$.name</code></td><td></td></tr></tbody></table></div></article><article id="block-hasPath" class="block-reference-entry"><h3><code>structured data [NAME] has path [PATH]?</code></h3><p>Report whether a path exists in named structured data.</p><p>Block type: BOOLEAN. Opcode: <code>hasPath</code>. <a href="#block-hasPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$.name</code></td><td></td></tr></tbody></table></div></article><article id="block-setJsonAtPath" class="block-reference-entry"><h3><code>set [NAME] at path [PATH] to JSON [VALUE]</code></h3><p>Immutably update a named value and replace its binding.</p><p>Block type: COMMAND. Opcode: <code>setJsonAtPath</code>. <a href="#block-setJsonAtPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$.name</code></td><td></td></tr><tr><th scope="row"><code>VALUE</code></th><td>STRING</td><td><code>&quot;Scratch&quot;</code></td><td></td></tr></tbody></table></div></article><article id="block-deleteAtPath" class="block-reference-entry"><h3><code>delete path [PATH] from [NAME]</code></h3><p>Immutably delete a path and replace its named binding.</p><p>Block type: COMMAND. Opcode: <code>deleteAtPath</code>. <a href="#block-deleteAtPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$.name</code></td><td></td></tr></tbody></table></div></article><article id="block-keysAtPath" class="block-reference-entry"><h3><code>keys in [NAME] at path [PATH]</code></h3><p>Return sorted object keys as JSON.</p><p>Block type: REPORTER. Opcode: <code>keysAtPath</code>. <a href="#block-keysAtPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$</code></td><td></td></tr></tbody></table></div></article><article id="block-lengthAtPath" class="block-reference-entry"><h3><code>length of array in [NAME] at path [PATH]</code></h3><p>Return the array length.</p><p>Block type: REPORTER. Opcode: <code>lengthAtPath</code>. <a href="#block-lengthAtPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$</code></td><td></td></tr></tbody></table></div></article><article id="block-forEachAtPath" class="block-reference-entry"><h3><code>for each in [NAME] at path [PATH] max [MAX]</code></h3><p>Iterate named data deterministically with a static maximum.</p><p>Block type: LOOP. Opcode: <code>forEachAtPath</code>. <a href="#block-forEachAtPath">Permanent link</a></p><div class="block-reference-table"><table><thead><tr><th>Argument</th><th>Type</th><th>Default value</th><th>Menu choices</th></tr></thead><tbody><tr><th scope="row"><code>NAME</code></th><td>STRING</td><td><code>data</code></td><td></td></tr><tr><th scope="row"><code>PATH</code></th><td>STRING</td><td><code>$</code></td><td></td></tr><tr><th scope="row"><code>MAX</code></th><td>NUMBER</td><td><code>100</code></td><td></td></tr></tbody></table></div><!-- BEGIN FIXED INPUT HELP --><p id="fixed-input-forEachAtPath-MAX" class="fixed-input-editor">MVP機能を有効にした場合だけ、MAXを整数1〜1000で編集します。入力途中・空白・非finiteな新値は保存できません。NAMEとPATHは変更しません。 GUIのDialogsとFixedInputsは起動時の既定OFFです。両方を有効にして再読込すると固定フォームが使えます。未変更のraw値を保存し直さず、接続reporterは評価しません。</p><!-- END FIXED INPUT HELP --></article><article id="block-currentKey" class="block-reference-entry"><h3><code>current key</code></h3><p>Return the current object key or array index string.</p><p>Block type: REPORTER. Opcode: <code>currentKey</code>. <a href="#block-currentKey">Permanent link</a></p><p>This block has no arguments.</p></article><article id="block-currentIndex" class="block-reference-entry"><h3><code>current index</code></h3><p>Return the zero-based iteration number.</p><p>Block type: REPORTER. Opcode: <code>currentIndex</code>. <a href="#block-currentIndex">Permanent link</a></p><p>This block has no arguments.</p></article><article id="block-currentValueJson" class="block-reference-entry"><h3><code>current value JSON</code></h3><p>Return the current iteration value as JSON.</p><p>Block type: REPORTER. Opcode: <code>currentValueJson</code>. <a href="#block-currentValueJson">Permanent link</a></p><p>This block has no arguments.</p></article>
56
56
  <p><a href="https://github.com/kubohiroya/turbowarp-structured-data/blob/main/README.md">Repository guide and examples</a></p>
57
57
  </section>
58
58
  <!-- END COMPLETE BLOCK REFERENCE -->
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://kubohiroya.github.io/turbowarp-structured-data/schemas/block-editor-fixed-inputs.schema.json",
4
+ "$defs": {
5
+ "forEachAtPath": {
6
+ "type": "object",
7
+ "properties": {
8
+ "MAX": {
9
+ "type": "integer",
10
+ "minimum": 1,
11
+ "maximum": 1000
12
+ }
13
+ },
14
+ "required": [
15
+ "MAX"
16
+ ],
17
+ "additionalProperties": true
18
+ }
19
+ }
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubohiroya/turbowarp-structured-data",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Named structured data with JSON/YAML codecs and deterministic path operations for TurboWarp.",
5
5
  "author": "Hiroya Kubo <hiroya@cuc.ac.jp>",
6
6
  "license": "MPL-2.0",
@@ -27,6 +27,7 @@
27
27
  "dist/",
28
28
  "docs/",
29
29
  "src/",
30
+ "schemas/",
30
31
  "templates/",
31
32
  "README.md",
32
33
  "README.ja.md",
@@ -47,8 +48,8 @@
47
48
  "build": "vite build",
48
49
  "lint": "eslint src tests scripts vite.config.ts eslint.config.ts",
49
50
  "test": "vitest run",
50
- "docs": "node scripts/generate-readme.ts",
51
- "docs:check": "node scripts/generate-readme.ts --check",
51
+ "docs": "node scripts/generate-readme.ts && node scripts/generate-fixed-input-docs.mjs",
52
+ "docs:check": "node scripts/generate-readme.ts --check && node scripts/generate-fixed-input-docs.mjs --check",
52
53
  "template:readme": "node scripts/apply-readme-template.ts",
53
54
  "repo:check": "node scripts/check-repo.ts",
54
55
  "pack:check": "npm pack --dry-run --ignore-scripts",
@@ -57,9 +58,9 @@
57
58
  },
58
59
  "devDependencies": {
59
60
  "@eslint/js": "^9.39.5",
60
- "@kubohiroya/turbowarp-extension-manifest": "0.3.0",
61
- "@kubohiroya/turbowarp-named-data": "0.1.0",
62
- "@kubohiroya/vite-plugin-turbowarp-extension": "0.3.0",
61
+ "@kubohiroya/turbowarp-extension-manifest": "0.3.2",
62
+ "@kubohiroya/turbowarp-named-data": "0.3.1",
63
+ "@kubohiroya/vite-plugin-turbowarp-extension": "0.6.1",
63
64
  "@types/node": "^24.13.3",
64
65
  "eslint": "^9.39.5",
65
66
  "jiti": "^2.7.0",
@@ -71,9 +72,15 @@
71
72
  },
72
73
  "dependencies": {
73
74
  "yaml": "^2.9.1",
74
- "@kubohiroya/turbowarp-extension-data-editor": "git+https://github.com/kubohiroya/turbowarp-extension-data-editor.git#76fa1d50a3c188082ceef723ea018a9d8769ea6e"
75
+ "@kubohiroya/turbowarp-extension-data-editor": "0.2.1"
75
76
  },
76
77
  "peerDependencies": {
77
- "@kubohiroya/turbowarp-named-data": ">=0.1.0 <1"
78
+ "@kubohiroya/turbowarp-named-data": "0.3.1"
79
+ },
80
+ "gitHead": "7429d0707e0c46099fa89d09f456a43a29919395",
81
+ "x-turbowarp-source": {
82
+ "revision": "7429d0707e0c46099fa89d09f456a43a29919395",
83
+ "revisionRole": "base-commit",
84
+ "sourceState": "local-release-overlay"
78
85
  }
79
86
  }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://kubohiroya.github.io/turbowarp-structured-data/schemas/block-editor-fixed-inputs.schema.json",
4
+ "$defs": {
5
+ "forEachAtPath": {
6
+ "type": "object",
7
+ "properties": {
8
+ "MAX": {
9
+ "type": "integer",
10
+ "minimum": 1,
11
+ "maximum": 1000
12
+ }
13
+ },
14
+ "required": [
15
+ "MAX"
16
+ ],
17
+ "additionalProperties": true
18
+ }
19
+ }
20
+ }
package/src/extension.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import {fixedInputEditorDefinitions, registerFixedInputEditors} from './fixed-input-editors.js';
1
2
  import {sourceEditorDefinitions} from './editor-schema.js';
2
3
  import {registerSourceEditors} from './source-editor.js';
3
4
  import definitions from './block-definitions.json';
@@ -74,7 +75,10 @@ const blockDefinitions = definitions.blocks as readonly BlockDefinition[];
74
75
 
75
76
  export class StructuredDataExtension implements TurboWarpExtension {
76
77
  private readonly enabled: boolean;
78
+ private disposed = false;
79
+ private readonly releaseRuntimeListeners: () => void;
77
80
  private readonly unregisterSourceEditors: () => void;
81
+ private readonly unregisterFixedInputEditors: () => void;
78
82
  private registries = new WeakMap<object, Map<string, StructuredDataBinding>>();
79
83
  private iterationStacks = new WeakMap<object, ActiveIteration[]>();
80
84
  private nextRevision = 1;
@@ -89,6 +93,17 @@ export class StructuredDataExtension implements TurboWarpExtension {
89
93
  ) {
90
94
  this.enabled = enabled;
91
95
  this.unregisterSourceEditors = registerSourceEditors(Scratch, enabled ? sourceEditorDefinitions : []);
96
+ const runtime = Scratch.vm?.runtime;
97
+ const onDispose = () => {void this.dispose();};
98
+ const onStop = () => this.clearRuntimeState();
99
+ this.releaseRuntimeListeners = () => {
100
+ runtime?.off?.('RUNTIME_DISPOSED', onDispose);
101
+ runtime?.off?.('PROJECT_STOP_ALL', onStop);
102
+ };
103
+ let unregisterFixed = () => {};
104
+ try {
105
+ unregisterFixed = registerFixedInputEditors(Scratch, enabled ? fixedInputEditorDefinitions : []);
106
+ this.unregisterFixedInputEditors = unregisterFixed;
92
107
  if (namedDataEnabled) {
93
108
  this.namedDataProvider = new StructuredDataNamedDataProvider({
94
109
  get: (target, name) => this.registries.get(target)?.get(name)
@@ -104,12 +119,18 @@ export class StructuredDataExtension implements TurboWarpExtension {
104
119
  runtime,
105
120
  this.namedDataRegistry
106
121
  );
107
- runtime.on?.('RUNTIME_DISPOSED', () => {
108
- void this.dispose();
109
- });
122
+ runtime.on?.('RUNTIME_DISPOSED', onDispose);
110
123
  }
111
124
  }
112
- Scratch.vm?.runtime?.on?.('PROJECT_STOP_ALL', () => this.clearRuntimeState());
125
+ runtime?.on?.('PROJECT_STOP_ALL', onStop);
126
+ } catch (error) {
127
+ this.releaseRuntimeListeners();
128
+ unregisterFixed();
129
+ this.unregisterSourceEditors();
130
+ this.unbindNamedDataRegistryLifecycle?.();
131
+ void this.namedDataRegistration?.unregister().catch(() => {});
132
+ throw error;
133
+ }
113
134
  }
114
135
 
115
136
  public getNamedDataProvider(): StructuredDataNamedDataProvider | undefined {
@@ -121,6 +142,10 @@ export class StructuredDataExtension implements TurboWarpExtension {
121
142
  }
122
143
 
123
144
  public async dispose(): Promise<void> {
145
+ if (this.disposed) return;
146
+ this.disposed = true;
147
+ this.releaseRuntimeListeners();
148
+ this.unregisterFixedInputEditors();
124
149
  this.unregisterSourceEditors();
125
150
  await this.namedDataRegistration?.unregister();
126
151
  this.unbindNamedDataRegistryLifecycle?.();
@@ -0,0 +1,67 @@
1
+ import {parseFixedNumberInput} from '@kubohiroya/turbowarp-extension-data-editor';
2
+ import type {FixedInputEditorOptions} from '@kubohiroya/turbowarp-extension-data-editor';
3
+ import {extensionConfig} from './config.js';
4
+ import {fixedInputSchemaBundle} from './fixed-input-schema.js';
5
+
6
+
7
+ export const fixedInputEditorDefinitions: readonly FixedInputEditorOptions[] = [{
8
+ apiVersion: 1, extensionId: extensionConfig.id, editorId: 'forEachAtPath-fixed-inputs',
9
+ opcodes: [`${extensionConfig.id}_forEachAtPath`],
10
+ contractId: 'turbowarp-structured-data:forEachAtPath.MAX', contractRevision: 'fixed-inputs-v1',
11
+ bindings: [{argument: 'MAX', schemaBundle: fixedInputSchemaBundle,
12
+ control: {kind: 'number', label: 'MAX'},
13
+ read(value) {
14
+ if (typeof value !== 'string' && typeof value !== 'number') throw new TypeError('Expected a literal maximum.');
15
+ if (typeof value === 'string' && value.trim() === '') throw new TypeError('Blank maximum stays readonly.');
16
+ // Existing nonempty finite Number aliases may project; no-op saving retains the exact raw token.
17
+ const number = Number(value);
18
+ if (!Number.isFinite(number)) throw new TypeError('Maximum must be finite.');
19
+ return number;
20
+ },
21
+ encode(value) {
22
+ const parsed = parseFixedNumberInput(value);
23
+ if (parsed.kind !== 'valid') throw new TypeError('Expected a finite numeric token.');
24
+ return parsed.token;
25
+ },
26
+ validateCandidate(value) {
27
+ const parsed = parseFixedNumberInput(value);
28
+ return parsed.kind === 'valid' && Number.isInteger(parsed.value) && parsed.value >= 1 && parsed.value <= 1000 ? []
29
+ : [{path: '/inputs/MAX', message: 'Maximum must be an integer from 1 to 1000.'}];
30
+ }
31
+ }],
32
+ validationContext: () => ({consumerId: extensionConfig.id, nowMs: Date.now(), settings: null})
33
+ }];
34
+
35
+ interface RuntimeLifetime {
36
+ on?: (event: string, listener: () => void) => void;
37
+ off?: (event: string, listener: () => void) => void;
38
+ }
39
+
40
+ /** Register metadata only; the GUI startup capability owns editor activation. */
41
+ export function registerFixedInputEditors(
42
+ scratch: unknown, definitions: readonly FixedInputEditorOptions[], runtimeOverride?: RuntimeLifetime
43
+ ): () => void {
44
+ const host = scratch as {gui?: {blockEditors?: {apiVersion?: number;
45
+ registerFixedInputEditor?: (options: FixedInputEditorOptions) => () => void}};
46
+ vm?: {runtime?: RuntimeLifetime}};
47
+ const api = host.gui?.blockEditors;
48
+ if (!definitions.length || api?.apiVersion !== 1 || typeof api.registerFixedInputEditor !== 'function') return () => {};
49
+ const runtime = runtimeOverride ?? host.vm?.runtime;
50
+ const removers: (() => void)[] = [];
51
+ let active = true;
52
+ const dispose = () => {
53
+ if (!active) return;
54
+ active = false;
55
+ runtime?.off?.('RUNTIME_DISPOSED', dispose);
56
+ for (const remove of removers.splice(0).reverse()) remove();
57
+ };
58
+ try {
59
+ for (const definition of definitions) {
60
+ const remove = api.registerFixedInputEditor.call(api, definition);
61
+ if (typeof remove !== 'function') throw new TypeError('Invalid fixed input registration disposer.');
62
+ removers.push(remove);
63
+ }
64
+ runtime?.on?.('RUNTIME_DISPOSED', dispose);
65
+ } catch (error) {dispose(); throw error;}
66
+ return dispose;
67
+ }
@@ -0,0 +1,16 @@
1
+ import type {SchemaBundle} from '@kubohiroya/turbowarp-extension-data-editor/schema';
2
+ import document from '../schemas/block-editor-fixed-inputs.schema.json';
3
+
4
+ const source = {repository: 'turbowarp-structured-data', path: 'schemas/block-editor-fixed-inputs.schema.json',
5
+ symbol: '#/$defs/forEachAtPath/properties/MAX'};
6
+
7
+ /** The on-disk owner Schema is canonical; this is its single-field projection. */
8
+ export const fixedInputSchemaBundle: SchemaBundle = {
9
+ identity: {ownerId: 'turbowarp-structured-data', contractId: 'forEachAtPath.MAX', revision: 'fixed-inputs-v1', variantId: 'default'},
10
+ dialect: '2020-12',
11
+ root: {retrievalUri: 'https://kubohiroya.github.io/turbowarp-structured-data/schemas/forEachAtPath-MAX.schema.json',
12
+ document: document.$defs.forEachAtPath.properties.MAX, artifact: {role: 'canonical-schema', source}},
13
+ resources: [],
14
+ provenance: {canonicalSources: [source, {repository: 'turbowarp-structured-data', path: 'src/core.ts', symbol: 'iterationEntries'}],
15
+ generatedFiles: [{repository: 'turbowarp-structured-data', path: 'docs/schemas/block-editor-fixed-inputs.schema.json'}], referenceSchemas: [], generationSources: [{repository: 'turbowarp-structured-data', path: 'scripts/generate-fixed-input-docs.mjs'}]}
16
+ };
@@ -181,3 +181,16 @@ release consistency checkでは、exact-version README/CDN例に加え、`docs
181
181
 
182
182
  third-party componentを含む場合は
183
183
  [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md)に記載します。
184
+
185
+ ## 固定引数の編集フォーム(S3-4)
186
+
187
+ 共通エディターの起動時フラグ `blockEditorFixedInputs` とDialogsがONのGUIでは、`forEachAtPath` の `MAX` だけを固定フォームとして登録します。旧GUI・フラグOFFでは追加登録を行いません。Schemaの正本は `schemas/block-editor-fixed-inputs.schema.json#/$defs/forEachAtPath/properties/MAX`、codecと登録は `src/fixed-input-editors.ts` です。既存raw値・未変更alias・未知enumは無操作保存で書き換えず、reporterを評価しません。明示変更だけを検査し、他の引数やブロック宣言、保存形式を変更しません。
188
+
189
+ MAXは整数1〜1000です。新しい入力には共通の有限数値token規則も適用し、途中入力を保存しません。MVP flagがOFFなら登録せず、NAME/PATH・staticLiteral由来の接続可否を変更しません。
190
+
191
+ 登録はoptional API(apiVersion:1)に限定し、RUNTIME_DISPOSED/明示disposeで解除します。起動フラグOFF+再読込で従来経路へ切り戻せます。依存を戻す場合はmanifest/lock/実解決を一組で復旧します。npm公開や後続段階の動的候補は含みません。
192
+
193
+ Schemaが記述する契約の根拠は実getInfoの宣言と既存の純粋なruntime変換です。SchemaBundleのretrievalUriはこのローカル文書の識別子であり、URL配信済み・HTTP取得によるref解決を意味しません。Schemaは同梱文書だけでcompileします。
194
+ 既存MAXの非空finiteなNumber alias(例`001`・`+1`・`0x10`)はNumberによる表示投影だけを行い、無変更rawを保存し直しません。空白・非finite・投影不能値はreadonlyとし、0/defaultを補いません。新しい入力にはこれらaliasを許可せずNP-finite-new-input-v1を適用します。
195
+
196
+ 登録は拡張インスタンスの構築時に存在する任意の editor API を使用します。後からの API 追加や差し替えには自動追従しません。起動設定を変更した場合は GUI を再読み込みし、拡張インスタンスを再構築してください。
@@ -196,3 +196,16 @@ site and `blockIconURI` remains a `data:image/svg+xml;base64,...` URI.
196
196
 
197
197
  Third-party components, when present, are listed in
198
198
  [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md).
199
+
200
+ ## 固定引数の編集フォーム(S3-4)
201
+
202
+ 共通エディターの起動時フラグ `blockEditorFixedInputs` とDialogsがONのGUIでは、`forEachAtPath` の `MAX` だけを固定フォームとして登録します。旧GUI・フラグOFFでは追加登録を行いません。Schemaの正本は `schemas/block-editor-fixed-inputs.schema.json#/$defs/forEachAtPath/properties/MAX`、codecと登録は `src/fixed-input-editors.ts` です。既存raw値・未変更alias・未知enumは無操作保存で書き換えず、reporterを評価しません。明示変更だけを検査し、他の引数やブロック宣言、保存形式を変更しません。
203
+
204
+ MAXは整数1〜1000です。新しい入力には共通の有限数値token規則も適用し、途中入力を保存しません。MVP flagがOFFなら登録せず、NAME/PATH・staticLiteral由来の接続可否を変更しません。
205
+
206
+ 登録はoptional API(apiVersion:1)に限定し、RUNTIME_DISPOSED/明示disposeで解除します。起動フラグOFF+再読込で従来経路へ切り戻せます。依存を戻す場合はmanifest/lock/実解決を一組で復旧します。npm公開や後続段階の動的候補は含みません。
207
+
208
+ Schemaが記述する契約の根拠は実getInfoの宣言と既存の純粋なruntime変換です。SchemaBundleのretrievalUriはこのローカル文書の識別子であり、URL配信済み・HTTP取得によるref解決を意味しません。Schemaは同梱文書だけでcompileします。
209
+ 既存MAXの非空finiteなNumber alias(例`001`・`+1`・`0x10`)はNumberによる表示投影だけを行い、無変更rawを保存し直しません。空白・非finite・投影不能値はreadonlyとし、0/defaultを補いません。新しい入力にはこれらaliasを許可せずNP-finite-new-input-v1を適用します。
210
+
211
+ Registration uses the optional editor API available when the extension instance is constructed. Adding or replacing that API later is not followed automatically; reload the GUI and reconstruct the extension instance after changing the startup configuration.