@kubohiroya/turbowarp-structured-data 0.6.0 → 0.6.2
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 +16 -0
- package/README.md +16 -1
- package/THIRD_PARTY_NOTICES.md +9 -0
- package/dist/structured-data.js +270 -7914
- package/docs/editor-runtime-boundary.ja.md +38 -0
- package/docs/index.html +1 -1
- package/docs/schemas/block-editor-fixed-inputs.schema.json +20 -0
- package/lib-editor/src/codecs.d.ts +10 -0
- package/lib-editor/src/config.d.ts +19 -0
- package/lib-editor/src/core.d.ts +34 -0
- package/lib-editor/src/editor-metadata.d.ts +5 -0
- package/lib-editor/src/editor-metadata.js +443 -0
- package/lib-editor/src/editor-schema.d.ts +2 -0
- package/lib-editor/src/fixed-input-editors.d.ts +9 -0
- package/lib-editor/src/fixed-input-schema.d.ts +3 -0
- package/lib-editor/src/source-editor.d.ts +8 -0
- package/package.json +30 -14
- package/schemas/block-editor-fixed-inputs.schema.json +20 -0
- package/src/editor-metadata.ts +5 -0
- package/src/extension.ts +29 -4
- package/src/fixed-input-editors.ts +67 -0
- package/src/fixed-input-schema.ts +16 -0
- package/src/source-editor.ts +13 -10
- package/templates/README.ja.md +13 -0
- package/templates/README.md +13 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 編集metadataとruntimeの境界
|
|
2
|
+
|
|
3
|
+
追跡: [共通data-editor #72](https://github.com/kubohiroya/turbowarp-extension-data-editor/issues/72)。
|
|
4
|
+
Camera Source #52と同じ責務分担を適用する。
|
|
5
|
+
|
|
6
|
+
| 担当 | 責務 |
|
|
7
|
+
| --- | --- |
|
|
8
|
+
| Structured Data | JSON/YAMLの既存parser、名前付きデータ、path/反復、正本Schema、原文decoder、pure owner検証、編集binding |
|
|
9
|
+
| 共通data-editor/GUI | 編集時のSchema compile、Schema制約の診断、フォーム描画、保存/Cancel、transaction |
|
|
10
|
+
|
|
11
|
+
`sourceBinding.validateSource` は原文を保持して既存ownerのreader/checkerを実行し、Schemaをcompileしない。
|
|
12
|
+
共通GUIは宣言されたSchemaBundleを検証してから保存する。独自consumerがSchema keyword別診断を必要とする場合は、
|
|
13
|
+
consumer自身が共通data-editorのSchema検証を組み合わせる。owner callbackの診断はSchema診断の代わりにはしない。
|
|
14
|
+
|
|
15
|
+
`@kubohiroya/turbowarp-structured-data/editor-metadata` はSource/Formの3定義と固定MAX入力の1定義を公開する。
|
|
16
|
+
この入口はGUI/DOM/VMを参照せず、実行時importは既存production依存の`yaml`だけである。
|
|
17
|
+
共通のJSON値SchemaBundleと新数値入力parserは開発依存の正本をビルド時に取り込み、手書きの複製を作らない。
|
|
18
|
+
型宣言で共通型を利用するTypeScript consumerは、自身の開発依存にdata-editorを追加する。
|
|
19
|
+
|
|
20
|
+
data-editorの直接依存をdevDependenciesへ移す。standalone JSとmetadataにはAjv、ajv-formats、Schema compiler、
|
|
21
|
+
React/RJSFを含めない。Viteのmodule graph guardと隔離importテストで再流入を検出する。
|
|
22
|
+
通常のYAML解析はruntime機能なので維持する。yaml本体の複数拡張間共有は[sb3-toolchain #65](https://github.com/kubohiroya/sb3-toolchain/issues/65)の後続課題である。
|
|
23
|
+
|
|
24
|
+
既存のMVP・GUI編集フラグ、Extension ID、opcode、MAXの文法・範囲、既存raw値と無変更保存、Undo transactionを維持する。
|
|
25
|
+
問題時はGUI編集フラグをOFFにして再読込し、必要なら検証済みのexact pin・lock・生成物へ一組で戻す。
|
|
26
|
+
保存済みデータを自動移行しない。
|
|
27
|
+
|
|
28
|
+
開発時は`pnpm run build`でstandaloneと公開metadataを生成してから対象テストを実行する。
|
|
29
|
+
ローカルの対象テストは実GUIや実機の代替であり、ブラウザ上の表示確認を実施した意味にはしない。
|
|
30
|
+
|
|
31
|
+
## ローカル検証記録(2026-10-24)
|
|
32
|
+
|
|
33
|
+
- 基準: 公開main `4ae44bd6ee1d`。standalone JSは560,962 Bから256,729 Bへ変更。
|
|
34
|
+
- gzip level 9(参考値)は119,711 Bから58,861 B。SB3削減量や実行時RAMの測定値ではない。
|
|
35
|
+
- 対象テスト82件、型検査、変更したTypeScriptのESLint、standalone/metadata build、package export・repo policy確認が成功。
|
|
36
|
+
- GUIなしVMで通常登録1回、Ajv生成0回、編集用SchemaBundle compile 0回。2拡張をsb3-toolchain 0.16.0で束ねても同じ結果。
|
|
37
|
+
- 共通サービス経由のSchema制約拒否、owner受理条件、raw no-op/変更時commit、旧GUIでの非登録、隔離metadata importを確認。
|
|
38
|
+
- 実ブラウザ・3D描画・実カメラの確認、push、PR、merge、npm公開はこのローカル検証に含めない。
|
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>{"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-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>"Scratch"</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
|
|
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>{"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-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>"Scratch"</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
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type JsonValue } from './core.js';
|
|
2
|
+
export declare const PARSE_LIMITS: {
|
|
3
|
+
readonly maxInputBytes: number;
|
|
4
|
+
readonly maxDepth: 64;
|
|
5
|
+
readonly maxNodes: 50000;
|
|
6
|
+
};
|
|
7
|
+
export declare function parseJsonDocument(source: string): JsonValue;
|
|
8
|
+
export declare function parseYamlDocument(source: string): JsonValue;
|
|
9
|
+
export declare function renderJsonDocument(value: JsonValue): string;
|
|
10
|
+
export declare function renderYamlDocument(value: JsonValue): string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const STRUCTURED_DATA_MVP_DEFAULT = false;
|
|
2
|
+
export declare const NAMED_DATA_REGISTRY_MVP_DEFAULT = false;
|
|
3
|
+
export declare function isStructuredDataMvpEnabled(source?: {
|
|
4
|
+
STRUCTURED_DATA_MVP?: unknown;
|
|
5
|
+
}): boolean;
|
|
6
|
+
export declare function isNamedDataRegistryMvpEnabled(source?: {
|
|
7
|
+
NAMED_DATA_REGISTRY_MVP?: unknown;
|
|
8
|
+
}): boolean;
|
|
9
|
+
export declare const extensionConfig: {
|
|
10
|
+
readonly id: "kubohiroyastructureddata";
|
|
11
|
+
readonly slug: "structured-data";
|
|
12
|
+
readonly name: "Structured Data";
|
|
13
|
+
readonly description: "Named structured data with JSON/YAML codecs for TurboWarp and server compilation.";
|
|
14
|
+
readonly author: "Hiroya Kubo";
|
|
15
|
+
readonly license: "MPL-2.0";
|
|
16
|
+
readonly unsandboxed: true;
|
|
17
|
+
readonly docsURI: "https://kubohiroya.github.io/turbowarp-structured-data/";
|
|
18
|
+
readonly blockIconURI: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OCA0OCI+PHJlY3QgeD0iNCIgeT0iOCIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE0IiByeD0iMyIgZmlsbD0iIzRDOTdGRiIvPjxyZWN0IHg9IjI2IiB5PSI4IiB3aWR0aD0iMTgiIGhlaWdodD0iMTQiIHJ4PSIzIiBmaWxsPSIjNTlDMDU5Ii8+PHJlY3QgeD0iMTUiIHk9IjI2IiB3aWR0aD0iMTgiIGhlaWdodD0iMTQiIHJ4PSIzIiBmaWxsPSIjRkZBQjE5Ii8+PC9zdmc+";
|
|
19
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare const STRUCTURED_DATA_ERROR_CODES: readonly ["INVALID_JSON", "INVALID_YAML", "INVALID_NAME", "DATA_NOT_FOUND", "INVALID_PATH", "PATH_NOT_FOUND", "TYPE_MISMATCH", "INDEX_OUT_OF_RANGE", "ITERATION_LIMIT_EXCEEDED", "ITERATION_CONTEXT_REQUIRED", "PARSE_LIMIT_EXCEEDED"];
|
|
2
|
+
export type StructuredDataErrorCode = (typeof STRUCTURED_DATA_ERROR_CODES)[number];
|
|
3
|
+
export type JsonPrimitive = null | boolean | number | string;
|
|
4
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
5
|
+
[key: string]: JsonValue;
|
|
6
|
+
};
|
|
7
|
+
export type PathSegment = {
|
|
8
|
+
kind: 'key';
|
|
9
|
+
value: string;
|
|
10
|
+
} | {
|
|
11
|
+
kind: 'index';
|
|
12
|
+
value: number;
|
|
13
|
+
};
|
|
14
|
+
export declare class StructuredDataError extends Error {
|
|
15
|
+
readonly code: StructuredDataErrorCode;
|
|
16
|
+
constructor(code: StructuredDataErrorCode, message: string);
|
|
17
|
+
}
|
|
18
|
+
export declare function parseJson(text: unknown): JsonValue;
|
|
19
|
+
export declare function isValidJson(text: unknown): boolean;
|
|
20
|
+
export declare function normalizeJson(text: unknown): string;
|
|
21
|
+
export declare function stringifyJson(value: JsonValue): string;
|
|
22
|
+
export declare function parsePath(input: unknown): PathSegment[];
|
|
23
|
+
export declare function getAtPath(root: JsonValue, segments: readonly PathSegment[]): JsonValue;
|
|
24
|
+
export declare function hasAtPath(root: JsonValue, segments: readonly PathSegment[]): boolean;
|
|
25
|
+
export declare function setAtPath(root: JsonValue, segments: readonly PathSegment[], replacement: JsonValue): JsonValue;
|
|
26
|
+
export declare function deleteAtPath(root: JsonValue, segments: readonly PathSegment[]): JsonValue;
|
|
27
|
+
export declare function keysAtPath(root: JsonValue, segments: readonly PathSegment[]): string[];
|
|
28
|
+
export declare function lengthAtPath(root: JsonValue, segments: readonly PathSegment[]): number;
|
|
29
|
+
export interface IterationEntry {
|
|
30
|
+
key: string;
|
|
31
|
+
value: JsonValue;
|
|
32
|
+
}
|
|
33
|
+
export declare function iterationEntries(root: JsonValue, segments: readonly PathSegment[], maximum: number): IterationEntry[];
|
|
34
|
+
export declare function compareUnicodeCodePoints(left: string, right: string): number;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Lightweight declarations and owner callbacks; the optional GUI owns Schema compilation. */
|
|
2
|
+
export { sourceEditorDefinitions } from './editor-schema.js';
|
|
3
|
+
export { canonicalBundle, registerSourceEditors, sourceBinding } from './source-editor.js';
|
|
4
|
+
export { fixedInputEditorDefinitions, registerFixedInputEditors } from './fixed-input-editors.js';
|
|
5
|
+
export { fixedInputSchemaBundle } from './fixed-input-schema.js';
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import { LineCounter, isAlias, isMap, isScalar, isSeq, parseDocument } from "yaml";
|
|
2
|
+
var extensionConfig = {
|
|
3
|
+
id: "kubohiroyastructureddata",
|
|
4
|
+
slug: "structured-data",
|
|
5
|
+
name: "Structured Data",
|
|
6
|
+
description: "Named structured data with JSON/YAML codecs for TurboWarp and server compilation.",
|
|
7
|
+
author: "Hiroya Kubo",
|
|
8
|
+
license: "MPL-2.0",
|
|
9
|
+
unsandboxed: true,
|
|
10
|
+
docsURI: "https://kubohiroya.github.io/turbowarp-structured-data/",
|
|
11
|
+
blockIconURI: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OCA0OCI+PHJlY3QgeD0iNCIgeT0iOCIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE0IiByeD0iMyIgZmlsbD0iIzRDOTdGRiIvPjxyZWN0IHg9IjI2IiB5PSI4IiB3aWR0aD0iMTgiIGhlaWdodD0iMTQiIHJ4PSIzIiBmaWxsPSIjNTlDMDU5Ii8+PHJlY3QgeD0iMTUiIHk9IjI2IiB3aWR0aD0iMTgiIGhlaWdodD0iMTQiIHJ4PSIzIiBmaWxsPSIjRkZBQjE5Ii8+PC9zdmc+"
|
|
12
|
+
};
|
|
13
|
+
function canonicalBundle(ownerId, contractId, document, ownerSource) {
|
|
14
|
+
const source = {
|
|
15
|
+
repository: ownerId,
|
|
16
|
+
path: `schemas/${contractId}.schema.json`
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
identity: {
|
|
20
|
+
ownerId,
|
|
21
|
+
contractId,
|
|
22
|
+
revision: "source-forms-v1",
|
|
23
|
+
variantId: "default"
|
|
24
|
+
},
|
|
25
|
+
dialect: "2020-12",
|
|
26
|
+
root: {
|
|
27
|
+
retrievalUri: `https://kubohiroya.github.io/${ownerId}/schemas/${contractId}.schema.json`,
|
|
28
|
+
document,
|
|
29
|
+
artifact: {
|
|
30
|
+
role: "canonical-schema",
|
|
31
|
+
source
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
resources: [],
|
|
35
|
+
provenance: {
|
|
36
|
+
canonicalSources: [source, {
|
|
37
|
+
repository: ownerId,
|
|
38
|
+
...ownerSource
|
|
39
|
+
}],
|
|
40
|
+
generatedFiles: [],
|
|
41
|
+
referenceSchemas: [],
|
|
42
|
+
generationSources: []
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function sourceBinding(argument, bundle, read, check = read, format = "json", uiSchema) {
|
|
47
|
+
return {
|
|
48
|
+
argument,
|
|
49
|
+
variants: [{
|
|
50
|
+
variantId: "default",
|
|
51
|
+
format,
|
|
52
|
+
schemaBundle: bundle,
|
|
53
|
+
...uiSchema ? { uiSchema } : {}
|
|
54
|
+
}],
|
|
55
|
+
decode(source) {
|
|
56
|
+
return {
|
|
57
|
+
source,
|
|
58
|
+
format,
|
|
59
|
+
variantId: "default",
|
|
60
|
+
data: read(source)
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
validateSource(source, context) {
|
|
64
|
+
try {
|
|
65
|
+
read(source);
|
|
66
|
+
} catch {
|
|
67
|
+
return {
|
|
68
|
+
source,
|
|
69
|
+
result: {
|
|
70
|
+
kind: "invalid",
|
|
71
|
+
diagnostics: [{ code: "source-decode-invalid" }]
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const descriptors = Object.getOwnPropertyDescriptors(context);
|
|
77
|
+
if (typeof descriptors.consumerId?.value !== "string" || typeof descriptors.nowMs?.value !== "number" || !Number.isFinite(descriptors.nowMs.value) || !descriptors.settings || !("value" in descriptors.settings)) return {
|
|
78
|
+
source,
|
|
79
|
+
result: {
|
|
80
|
+
kind: "execution-error",
|
|
81
|
+
diagnostics: [{ code: "owner-context-invalid" }]
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
} catch {
|
|
85
|
+
return {
|
|
86
|
+
source,
|
|
87
|
+
result: {
|
|
88
|
+
kind: "execution-error",
|
|
89
|
+
diagnostics: [{ code: "owner-context-invalid" }]
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
check(source);
|
|
95
|
+
} catch {
|
|
96
|
+
return {
|
|
97
|
+
source,
|
|
98
|
+
result: {
|
|
99
|
+
kind: "invalid",
|
|
100
|
+
diagnostics: [{ code: "source-owner-invalid" }]
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
source,
|
|
106
|
+
result: {
|
|
107
|
+
kind: "valid",
|
|
108
|
+
diagnostics: []
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** The optional capability keeps legacy hosts and the startup flag safely inactive. */
|
|
115
|
+
function registerSourceEditors(scratch, definitions) {
|
|
116
|
+
const host = scratch;
|
|
117
|
+
const api = host.gui?.blockEditors;
|
|
118
|
+
if (api?.apiVersion !== 1 || typeof api.registerSourceEditor !== "function") return () => {};
|
|
119
|
+
const removers = [];
|
|
120
|
+
let active = true;
|
|
121
|
+
const dispose = () => {
|
|
122
|
+
if (!active) return;
|
|
123
|
+
active = false;
|
|
124
|
+
host.vm?.runtime?.off?.("RUNTIME_DISPOSED", dispose);
|
|
125
|
+
for (const remove of removers.splice(0).reverse()) remove();
|
|
126
|
+
};
|
|
127
|
+
try {
|
|
128
|
+
for (const definition of definitions) removers.push(api.registerSourceEditor.call(api, definition));
|
|
129
|
+
host.vm?.runtime?.on?.("RUNTIME_DISPOSED", dispose);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
dispose();
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
return dispose;
|
|
135
|
+
}
|
|
136
|
+
var source$1 = {
|
|
137
|
+
repository: "https://github.com/kubohiroya/turbowarp-extension-data-editor",
|
|
138
|
+
path: "src/json-value.ts",
|
|
139
|
+
symbol: "jsonValueSchema"
|
|
140
|
+
};
|
|
141
|
+
var jsonValueSchemaBundle = Object.freeze({
|
|
142
|
+
identity: {
|
|
143
|
+
ownerId: "@kubohiroya/turbowarp-extension-data-editor",
|
|
144
|
+
contractId: "json-value",
|
|
145
|
+
revision: "1",
|
|
146
|
+
variantId: "json-value"
|
|
147
|
+
},
|
|
148
|
+
dialect: "2020-12",
|
|
149
|
+
root: {
|
|
150
|
+
retrievalUri: "https://kubohiroya.github.io/turbowarp-extension-data-editor/schemas/json-value",
|
|
151
|
+
document: true,
|
|
152
|
+
artifact: {
|
|
153
|
+
role: "canonical-schema",
|
|
154
|
+
source: source$1
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
resources: [],
|
|
158
|
+
provenance: {
|
|
159
|
+
canonicalSources: [source$1],
|
|
160
|
+
generatedFiles: [],
|
|
161
|
+
referenceSchemas: [],
|
|
162
|
+
generationSources: []
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
var numberToken = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/u;
|
|
166
|
+
var invalidNumber = Object.freeze({
|
|
167
|
+
kind: "invalid",
|
|
168
|
+
code: "fixed-number-input-invalid"
|
|
169
|
+
});
|
|
170
|
+
/**
|
|
171
|
+
* Parse a NEW numeric draft according to NP-finite-new-input-v1.
|
|
172
|
+
* Never apply this to opening/no-op raw values or use it as an owner validator.
|
|
173
|
+
* Keep the validated token so negative zero, precision and exponent spelling survive.
|
|
174
|
+
*/
|
|
175
|
+
function parseFixedNumberInput(input) {
|
|
176
|
+
if (typeof input === "number") {
|
|
177
|
+
if (!Number.isFinite(input)) return invalidNumber;
|
|
178
|
+
return Object.freeze({
|
|
179
|
+
kind: "valid",
|
|
180
|
+
token: Object.is(input, -0) ? "-0" : String(input),
|
|
181
|
+
value: input
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if (typeof input !== "string") return invalidNumber;
|
|
185
|
+
const token = input.trim();
|
|
186
|
+
if (!numberToken.test(token)) return invalidNumber;
|
|
187
|
+
const value = Number(token);
|
|
188
|
+
return Number.isFinite(value) ? Object.freeze({
|
|
189
|
+
kind: "valid",
|
|
190
|
+
token,
|
|
191
|
+
value
|
|
192
|
+
}) : invalidNumber;
|
|
193
|
+
}
|
|
194
|
+
var StructuredDataError = class extends Error {
|
|
195
|
+
constructor(code, message) {
|
|
196
|
+
super(`${code}: ${message}`);
|
|
197
|
+
this.name = "StructuredDataError";
|
|
198
|
+
this.code = code;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
function parseJson(text) {
|
|
202
|
+
try {
|
|
203
|
+
const value = JSON.parse(String(text));
|
|
204
|
+
assertJsonValue(value);
|
|
205
|
+
return value;
|
|
206
|
+
} catch (error) {
|
|
207
|
+
if (error instanceof StructuredDataError) throw error;
|
|
208
|
+
throw new StructuredDataError("INVALID_JSON", "Input must be valid JSON.");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function assertJsonValue(value) {
|
|
212
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return;
|
|
213
|
+
if (typeof value === "number" && Number.isFinite(value)) return;
|
|
214
|
+
if (Array.isArray(value)) {
|
|
215
|
+
for (const item of value) assertJsonValue(item);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (typeof value === "object") {
|
|
219
|
+
for (const item of Object.values(value)) assertJsonValue(item);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
throw new StructuredDataError("INVALID_JSON", "JSON contains an unsupported value.");
|
|
223
|
+
}
|
|
224
|
+
var PARSE_LIMITS = {
|
|
225
|
+
maxInputBytes: 262144,
|
|
226
|
+
maxDepth: 64,
|
|
227
|
+
maxNodes: 5e4
|
|
228
|
+
};
|
|
229
|
+
function parseJsonDocument(source) {
|
|
230
|
+
validateInputSize(source);
|
|
231
|
+
const value = parseJson(source);
|
|
232
|
+
validateValueBudget(value, { nodes: 0 }, 0);
|
|
233
|
+
return value;
|
|
234
|
+
}
|
|
235
|
+
function parseYamlDocument(source) {
|
|
236
|
+
validateInputSize(source);
|
|
237
|
+
const lineCounter = new LineCounter();
|
|
238
|
+
const document = parseDocument(source, {
|
|
239
|
+
lineCounter,
|
|
240
|
+
prettyErrors: false,
|
|
241
|
+
schema: "core",
|
|
242
|
+
uniqueKeys: true
|
|
243
|
+
});
|
|
244
|
+
const problem = document.errors[0] ?? document.warnings[0];
|
|
245
|
+
if (problem) {
|
|
246
|
+
const { line, column } = yamlPosition(lineCounter, problem.pos[0]);
|
|
247
|
+
throw new StructuredDataError("INVALID_YAML", `${problem.code} at line ${line}, column ${column}: ${problem.message}`);
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
return valueFromYamlNode(document.contents, { nodes: 0 }, 0, lineCounter);
|
|
251
|
+
} catch (error) {
|
|
252
|
+
if (error instanceof StructuredDataError) throw error;
|
|
253
|
+
throw new StructuredDataError("INVALID_YAML", error instanceof Error ? error.message : String(error));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
function valueFromYamlNode(node, budget, depth, lineCounter) {
|
|
257
|
+
consumeBudget(budget, depth);
|
|
258
|
+
if (node === null) return null;
|
|
259
|
+
if (isAlias(node)) throw new StructuredDataError("INVALID_YAML", "YAML aliases are not allowed.");
|
|
260
|
+
if (isScalar(node)) {
|
|
261
|
+
const value = node.value;
|
|
262
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return value;
|
|
263
|
+
throw new StructuredDataError("INVALID_YAML", "YAML contains an unsupported scalar value.");
|
|
264
|
+
}
|
|
265
|
+
if (isSeq(node)) return node.items.map((item) => valueFromYamlNode(item, budget, depth + 1, lineCounter));
|
|
266
|
+
if (isMap(node)) return Object.fromEntries(node.items.map((item) => yamlPairToEntry(item, budget, depth, lineCounter)));
|
|
267
|
+
throw new StructuredDataError("INVALID_YAML", "YAML contains an unsupported node type.");
|
|
268
|
+
}
|
|
269
|
+
function yamlPairToEntry(pair, budget, depth, lineCounter) {
|
|
270
|
+
if (!isScalar(pair.key) || typeof pair.key.value !== "string") throw new StructuredDataError("INVALID_YAML", "YAML map keys must be strings.");
|
|
271
|
+
return [pair.key.value, valueFromYamlNode(pair.value, budget, depth + 1, lineCounter)];
|
|
272
|
+
}
|
|
273
|
+
function validateValueBudget(value, budget, depth) {
|
|
274
|
+
consumeBudget(budget, depth);
|
|
275
|
+
if (Array.isArray(value)) for (const item of value) validateValueBudget(item, budget, depth + 1);
|
|
276
|
+
else if (typeof value === "object" && value !== null) for (const item of Object.values(value)) validateValueBudget(item, budget, depth + 1);
|
|
277
|
+
}
|
|
278
|
+
function consumeBudget(budget, depth) {
|
|
279
|
+
if (depth > PARSE_LIMITS.maxDepth) throw new StructuredDataError("PARSE_LIMIT_EXCEEDED", `Data exceeds maximum nesting depth ${PARSE_LIMITS.maxDepth}.`);
|
|
280
|
+
budget.nodes += 1;
|
|
281
|
+
if (budget.nodes > PARSE_LIMITS.maxNodes) throw new StructuredDataError("PARSE_LIMIT_EXCEEDED", `Data exceeds maximum node count ${PARSE_LIMITS.maxNodes}.`);
|
|
282
|
+
}
|
|
283
|
+
function validateInputSize(source) {
|
|
284
|
+
const bytes = new TextEncoder().encode(source).byteLength;
|
|
285
|
+
if (bytes > PARSE_LIMITS.maxInputBytes) throw new StructuredDataError("PARSE_LIMIT_EXCEEDED", `Input is ${bytes} bytes; maximum is ${PARSE_LIMITS.maxInputBytes}.`);
|
|
286
|
+
}
|
|
287
|
+
function yamlPosition(counter, offset) {
|
|
288
|
+
const position = counter.linePos(offset);
|
|
289
|
+
return {
|
|
290
|
+
line: position.line,
|
|
291
|
+
column: position.col
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
var sourceEditorDefinitions = [
|
|
295
|
+
{
|
|
296
|
+
editorId: "json-document-source",
|
|
297
|
+
opcodes: ["parseJson"],
|
|
298
|
+
bindings: [sourceBinding("TEXT", jsonValueSchemaBundle, parseJsonDocument)]
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
editorId: "yaml-document-source",
|
|
302
|
+
opcodes: ["parseYaml"],
|
|
303
|
+
bindings: [sourceBinding("TEXT", jsonValueSchemaBundle, parseYamlDocument, parseYamlDocument, "yaml")]
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
editorId: "json-path-value-source",
|
|
307
|
+
opcodes: ["setJsonAtPath"],
|
|
308
|
+
bindings: [sourceBinding("VALUE", jsonValueSchemaBundle, parseJson)]
|
|
309
|
+
}
|
|
310
|
+
].map(({ editorId, opcodes, bindings }) => ({
|
|
311
|
+
apiVersion: 1,
|
|
312
|
+
extensionId: extensionConfig.id,
|
|
313
|
+
editorId,
|
|
314
|
+
opcodes: opcodes.map((opcode) => `${extensionConfig.id}_${opcode}`),
|
|
315
|
+
bindings,
|
|
316
|
+
validationContext: () => ({
|
|
317
|
+
consumerId: extensionConfig.id,
|
|
318
|
+
nowMs: Date.now(),
|
|
319
|
+
settings: null
|
|
320
|
+
})
|
|
321
|
+
}));
|
|
322
|
+
var block_editor_fixed_inputs_schema_default = {
|
|
323
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
324
|
+
$id: "https://kubohiroya.github.io/turbowarp-structured-data/schemas/block-editor-fixed-inputs.schema.json",
|
|
325
|
+
$defs: { "forEachAtPath": {
|
|
326
|
+
"type": "object",
|
|
327
|
+
"properties": { "MAX": {
|
|
328
|
+
"type": "integer",
|
|
329
|
+
"minimum": 1,
|
|
330
|
+
"maximum": 1e3
|
|
331
|
+
} },
|
|
332
|
+
"required": ["MAX"],
|
|
333
|
+
"additionalProperties": true
|
|
334
|
+
} }
|
|
335
|
+
};
|
|
336
|
+
var source = {
|
|
337
|
+
repository: "turbowarp-structured-data",
|
|
338
|
+
path: "schemas/block-editor-fixed-inputs.schema.json",
|
|
339
|
+
symbol: "#/$defs/forEachAtPath/properties/MAX"
|
|
340
|
+
};
|
|
341
|
+
/** The on-disk owner Schema is canonical; this is its single-field projection. */
|
|
342
|
+
var fixedInputSchemaBundle = {
|
|
343
|
+
identity: {
|
|
344
|
+
ownerId: "turbowarp-structured-data",
|
|
345
|
+
contractId: "forEachAtPath.MAX",
|
|
346
|
+
revision: "fixed-inputs-v1",
|
|
347
|
+
variantId: "default"
|
|
348
|
+
},
|
|
349
|
+
dialect: "2020-12",
|
|
350
|
+
root: {
|
|
351
|
+
retrievalUri: "https://kubohiroya.github.io/turbowarp-structured-data/schemas/forEachAtPath-MAX.schema.json",
|
|
352
|
+
document: block_editor_fixed_inputs_schema_default.$defs.forEachAtPath.properties.MAX,
|
|
353
|
+
artifact: {
|
|
354
|
+
role: "canonical-schema",
|
|
355
|
+
source
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
resources: [],
|
|
359
|
+
provenance: {
|
|
360
|
+
canonicalSources: [source, {
|
|
361
|
+
repository: "turbowarp-structured-data",
|
|
362
|
+
path: "src/core.ts",
|
|
363
|
+
symbol: "iterationEntries"
|
|
364
|
+
}],
|
|
365
|
+
generatedFiles: [{
|
|
366
|
+
repository: "turbowarp-structured-data",
|
|
367
|
+
path: "docs/schemas/block-editor-fixed-inputs.schema.json"
|
|
368
|
+
}],
|
|
369
|
+
referenceSchemas: [],
|
|
370
|
+
generationSources: [{
|
|
371
|
+
repository: "turbowarp-structured-data",
|
|
372
|
+
path: "scripts/generate-fixed-input-docs.mjs"
|
|
373
|
+
}]
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
var fixedInputEditorDefinitions = [{
|
|
377
|
+
apiVersion: 1,
|
|
378
|
+
extensionId: extensionConfig.id,
|
|
379
|
+
editorId: "forEachAtPath-fixed-inputs",
|
|
380
|
+
opcodes: [`${extensionConfig.id}_forEachAtPath`],
|
|
381
|
+
contractId: "turbowarp-structured-data:forEachAtPath.MAX",
|
|
382
|
+
contractRevision: "fixed-inputs-v1",
|
|
383
|
+
bindings: [{
|
|
384
|
+
argument: "MAX",
|
|
385
|
+
schemaBundle: fixedInputSchemaBundle,
|
|
386
|
+
control: {
|
|
387
|
+
kind: "number",
|
|
388
|
+
label: "MAX"
|
|
389
|
+
},
|
|
390
|
+
read(value) {
|
|
391
|
+
if (typeof value !== "string" && typeof value !== "number") throw new TypeError("Expected a literal maximum.");
|
|
392
|
+
if (typeof value === "string" && value.trim() === "") throw new TypeError("Blank maximum stays readonly.");
|
|
393
|
+
const number = Number(value);
|
|
394
|
+
if (!Number.isFinite(number)) throw new TypeError("Maximum must be finite.");
|
|
395
|
+
return number;
|
|
396
|
+
},
|
|
397
|
+
encode(value) {
|
|
398
|
+
const parsed = parseFixedNumberInput(value);
|
|
399
|
+
if (parsed.kind !== "valid") throw new TypeError("Expected a finite numeric token.");
|
|
400
|
+
return parsed.token;
|
|
401
|
+
},
|
|
402
|
+
validateCandidate(value) {
|
|
403
|
+
const parsed = parseFixedNumberInput(value);
|
|
404
|
+
return parsed.kind === "valid" && Number.isInteger(parsed.value) && parsed.value >= 1 && parsed.value <= 1e3 ? [] : [{
|
|
405
|
+
path: "/inputs/MAX",
|
|
406
|
+
message: "Maximum must be an integer from 1 to 1000."
|
|
407
|
+
}];
|
|
408
|
+
}
|
|
409
|
+
}],
|
|
410
|
+
validationContext: () => ({
|
|
411
|
+
consumerId: extensionConfig.id,
|
|
412
|
+
nowMs: Date.now(),
|
|
413
|
+
settings: null
|
|
414
|
+
})
|
|
415
|
+
}];
|
|
416
|
+
/** Register metadata only; the GUI startup capability owns editor activation. */
|
|
417
|
+
function registerFixedInputEditors(scratch, definitions, runtimeOverride) {
|
|
418
|
+
const host = scratch;
|
|
419
|
+
const api = host.gui?.blockEditors;
|
|
420
|
+
if (!definitions.length || api?.apiVersion !== 1 || typeof api.registerFixedInputEditor !== "function") return () => {};
|
|
421
|
+
const runtime = runtimeOverride ?? host.vm?.runtime;
|
|
422
|
+
const removers = [];
|
|
423
|
+
let active = true;
|
|
424
|
+
const dispose = () => {
|
|
425
|
+
if (!active) return;
|
|
426
|
+
active = false;
|
|
427
|
+
runtime?.off?.("RUNTIME_DISPOSED", dispose);
|
|
428
|
+
for (const remove of removers.splice(0).reverse()) remove();
|
|
429
|
+
};
|
|
430
|
+
try {
|
|
431
|
+
for (const definition of definitions) {
|
|
432
|
+
const remove = api.registerFixedInputEditor.call(api, definition);
|
|
433
|
+
if (typeof remove !== "function") throw new TypeError("Invalid fixed input registration disposer.");
|
|
434
|
+
removers.push(remove);
|
|
435
|
+
}
|
|
436
|
+
runtime?.on?.("RUNTIME_DISPOSED", dispose);
|
|
437
|
+
} catch (error) {
|
|
438
|
+
dispose();
|
|
439
|
+
throw error;
|
|
440
|
+
}
|
|
441
|
+
return dispose;
|
|
442
|
+
}
|
|
443
|
+
export { canonicalBundle, fixedInputEditorDefinitions, fixedInputSchemaBundle, registerFixedInputEditors, registerSourceEditors, sourceBinding, sourceEditorDefinitions };
|