@kubohiroya/turbowarp-structured-data 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/structured-data.js +14121 -6283
- package/docs/index.html +35 -1
- package/docs/source-forms.ja.md +20 -0
- package/docs/stage2-release-verification.ja.md +28 -0
- package/package.json +5 -3
- package/src/editor-schema.ts +16 -0
- package/src/extension.ts +5 -0
- package/src/source-editor.ts +57 -0
package/docs/index.html
CHANGED
|
@@ -9,8 +9,17 @@
|
|
|
9
9
|
code{background:#f3f3f3;padding:.1em .3em;border-radius:.25em}
|
|
10
10
|
pre{background:#f6f8fa;padding:1rem;overflow:auto;border-radius:.5rem}
|
|
11
11
|
</style>
|
|
12
|
+
<style id="block-reference-style">
|
|
13
|
+
#extension-block-reference{max-width:72rem;margin:2rem auto;padding:1rem;line-height:1.6;overflow-wrap:anywhere}
|
|
14
|
+
#extension-block-reference .block-reference-entry{display:block;scroll-margin-top:6rem;margin:2rem 0;padding:1rem;border:1px solid currentColor;border-radius:.5rem}
|
|
15
|
+
#extension-block-reference .block-reference-table{overflow-x:auto}
|
|
16
|
+
#extension-block-reference table{border-collapse:collapse;width:100%}
|
|
17
|
+
#extension-block-reference th,#extension-block-reference td{text-align:left;vertical-align:top;border:1px solid currentColor;padding:.5rem}
|
|
18
|
+
#extension-block-reference code{white-space:pre-wrap}
|
|
19
|
+
#extension-block-reference a:focus-visible{outline:3px solid currentColor;outline-offset:3px}
|
|
20
|
+
</style>
|
|
12
21
|
</head>
|
|
13
|
-
<body>
|
|
22
|
+
<body><p lang="en" class="complete-block-reference-link"><a href="#extension-block-reference">Complete block reference (16 blocks)</a></p>
|
|
14
23
|
<h1>Structured Data</h1>
|
|
15
24
|
<p>Named structured data with safe JSON/YAML codecs and deterministic path operations.</p>
|
|
16
25
|
|
|
@@ -22,5 +31,30 @@
|
|
|
22
31
|
|
|
23
32
|
<h2>Development</h2>
|
|
24
33
|
<p>See the repository README and architecture documentation for build, test, release, and compatibility details.</p>
|
|
34
|
+
<!-- BEGIN COMPLETE BLOCK REFERENCE -->
|
|
35
|
+
<section lang="en" id="extension-block-reference" aria-labelledby="block-reference-title">
|
|
36
|
+
<h2 id="block-reference-title">Structured Data: complete block reference</h2>
|
|
37
|
+
<p>All 16 blocks are documented below, including compatibility blocks and blocks available through optional features. Select a block to read its behavior, arguments, default values, and menu choices. Each block has a permanent link.</p>
|
|
38
|
+
<nav aria-label="Block reference"><ul><li><a href="#block-parseJson"><code>parse JSON [TEXT] as [NAME]</code></a></li>
|
|
39
|
+
<li><a href="#block-parseYaml"><code>parse YAML [TEXT] as [NAME]</code></a></li>
|
|
40
|
+
<li><a href="#block-toJson"><code>to JSON [NAME]</code></a></li>
|
|
41
|
+
<li><a href="#block-toYaml"><code>to YAML [NAME]</code></a></li>
|
|
42
|
+
<li><a href="#block-hasStructuredData"><code>structured data [NAME] exists?</code></a></li>
|
|
43
|
+
<li><a href="#block-deleteStructuredData"><code>delete structured data [NAME]</code></a></li>
|
|
44
|
+
<li><a href="#block-getJsonAtPath"><code>get JSON from [NAME] at path [PATH]</code></a></li>
|
|
45
|
+
<li><a href="#block-hasPath"><code>structured data [NAME] has path [PATH]?</code></a></li>
|
|
46
|
+
<li><a href="#block-setJsonAtPath"><code>set [NAME] at path [PATH] to JSON [VALUE]</code></a></li>
|
|
47
|
+
<li><a href="#block-deleteAtPath"><code>delete path [PATH] from [NAME]</code></a></li>
|
|
48
|
+
<li><a href="#block-keysAtPath"><code>keys in [NAME] at path [PATH]</code></a></li>
|
|
49
|
+
<li><a href="#block-lengthAtPath"><code>length of array in [NAME] at path [PATH]</code></a></li>
|
|
50
|
+
<li><a href="#block-forEachAtPath"><code>for each in [NAME] at path [PATH] max [MAX]</code></a></li>
|
|
51
|
+
<li><a href="#block-currentKey"><code>current key</code></a></li>
|
|
52
|
+
<li><a href="#block-currentIndex"><code>current index</code></a></li>
|
|
53
|
+
<li><a href="#block-currentValueJson"><code>current value JSON</code></a></li>
|
|
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></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
|
+
<p><a href="https://github.com/kubohiroya/turbowarp-structured-data/blob/main/README.md">Repository guide and examples</a></p>
|
|
57
|
+
</section>
|
|
58
|
+
<!-- END COMPLETE BLOCK REFERENCE -->
|
|
25
59
|
</body>
|
|
26
60
|
</html>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# 段階2 Source/Form編集
|
|
2
|
+
|
|
3
|
+
この文書はローカル実装時の契約を示す。npm公開は実施しない。
|
|
4
|
+
|
|
5
|
+
- 対象: parseJson.TEXT / parseYaml.TEXT / setJsonAtPath.VALUE
|
|
6
|
+
- 契約: json-value
|
|
7
|
+
- Schema正本: 共通エディター src/json-value.ts。Schema文書はcanonical-schema、bundleは正本参照とprovenanceを保持する。
|
|
8
|
+
- owner受理条件: src/codecs.ts / core.ts の各ブロックと同じparse関数。JSON/YAML文書の256KiB、深さ64、ノード50000制限、YAML alias・重複キー・未知tag拒否を維持。setJsonAtPath.VALUEは従来のparseJson契約を維持。Named data名/path候補は段階4。
|
|
9
|
+
|
|
10
|
+
UIはSourceとFormを切り替える。無変更・cancel・編集後の元の値への復帰は入力文字列をそのまま保持する。Formの部分編集でも未知キー・null・空文字・配列の順序と重複・未編集箇所の表記を維持する。既定値の暗黙補完、variant/formatの暗黙移行、runtime正規化後の値の書き戻しは行わない。不正なsourceもSourceとして開き修復できるが、ownerに拒否される候補は保存できない。
|
|
11
|
+
|
|
12
|
+
利用条件は、GUI起動時に `blockEditorDialogs=1` と `blockEditorSourceForms=1` の両方を指定し、信頼した拡張をunsandboxedで読み込むこと。`Scratch.gui.blockEditors` はunsandboxed拡張APIにのみ提供される。既存のsandbox実行権限や拡張の既定の読み込み方法は変更しない。sandboxed実行では従来のブロックを使用し、編集capabilityがないためフォーム登録を省略する。
|
|
13
|
+
|
|
14
|
+
登録入口はoptionalな `Scratch.gui.blockEditors.registerSourceEditor`。GUIの起動時固定 `blockEditorSourceForms` は既定OFF、既存 `blockEditorDialogs` が前提となる。旧GUIやcapabilityを持たないhostでは登録を行わず、既存ブロックのAPI・引数・実行挙動は維持する。`dispose()` またはruntime破棄で登録を解除する。既存のruntime処理を実行せず、pure parser/validatorで保存候補の同じsourceを検査する。複数入力のブロックは単一editor definitionとして一度に検査・commitする。
|
|
15
|
+
|
|
16
|
+
依存: 共通エディターの新 `./schema` 型・json-value契約と新prepared save経路、およびGUIのcapabilityが必要。ローカル検証は独立cloneから新共通artifactへaliasする。遠隔配布用のpin・lockは最終共通artifactのSHA確定後に揃える。架空のSHAを記載しない。
|
|
17
|
+
|
|
18
|
+
受け入れ基準: 全対象inputの登録、既存受理との互換、Source修復、不正保存の拒否、no-opでundoを作らないこと、部分編集でraw値を維持すること、runtime破棄時の解除、既存APIのテスト・型検査・buildを確認する。原文不変・部分編集・拒否は `tests/source-editor.test.ts` から実際の共通serviceと接続して検証する。
|
|
19
|
+
|
|
20
|
+
ロールバック: 起動時 `blockEditorSourceForms` をOFFに戻してGUIを再起動する。登録capabilityを外すと新フォームは使用されず、既存のブロックと保存済みsourceは引き続き実行できる。Schema自体の編集・実行時の値からの動的Schema・値候補との接続は別工程。
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# 段階2の安定版検証記録
|
|
2
|
+
|
|
3
|
+
公開予定版: `@kubohiroya/turbowarp-structured-data@0.6.0`。JSON/YAML のTEXTとpath操作のVALUEを接続し、既存サイズ・深度・node・alias制限を維持します。
|
|
4
|
+
|
|
5
|
+
| opcode | 入力 |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| parseJson | TEXT |
|
|
8
|
+
| parseYaml | TEXT |
|
|
9
|
+
| setJsonAtPath | VALUE |
|
|
10
|
+
|
|
11
|
+
正本は共通 `src/json-value.ts` と既存 `src/codecs.ts` / `src/core.ts` のpure検証です。JSON/YAMLのroot型・配列・object・scalarを明示操作で編集します。runtimeの任意Schemaとの連動は追加しません。
|
|
12
|
+
|
|
13
|
+
共通依存は `76fa1d50a3c188082ceef723ea018a9d8769ea6e`(`@kubohiroya/turbowarp-extension-data-editor` 0.2.0)へGitの完全SHAで固定しています。manifest・lock・build許可・fresh実解決を揃え、直接・AJV経由のfast-uriを3.1.8へ統一しています。古い共有aliasの実解決を安定版の検証として流用していません。
|
|
14
|
+
|
|
15
|
+
`react-dom@18.3.1` は実際のRJSF registration回帰テストに必要なdev依存として追加しました。拡張のruntimeコードはGUIのoptional APIを使い、ReactDOMを拡張bundleへ追加しません。
|
|
16
|
+
|
|
17
|
+
Node v22.23.3 / pnpm 11.11.0で、空の専用storeからscripts有効の `pnpm install --frozen-lockfile` を実施しました。Git prepareから得た共通packageにはsrcが含まれず、build-infoは上記SHAと一致します。
|
|
18
|
+
|
|
19
|
+
- 全テスト 40件、型検査、全lint、build、文書/リポジトリ検査、pack検査が成功しました。
|
|
20
|
+
- production auditはcritical/high/moderate/lowすべて0件です。
|
|
21
|
+
- dist 2件は2回のbuildでSHA-256が一致し、異なるcheckout pathのfresh fixtureと作業cloneでも全件のbytesが一致しました。
|
|
22
|
+
- Vite8を使う場合の生成region行にはpackage storeのpathが入るため、そのマーカー行だけを省きます。licenseコメントと実行コードは保持します。Vite7のrepoにはこの追加処理を適用しません。
|
|
23
|
+
|
|
24
|
+
過去の段階2ローカル全体659件・実GUI5ケース(各8手順)は別の保存済み記録です。今回の数は当該repoのfresh full結果であり、繰り返し実行を合算していません。GitHub CIの結果はPRで別途確認します。
|
|
25
|
+
|
|
26
|
+
GUIの起動時固定・既定OFFの `blockEditorSourceForms` と `blockEditorDialogs` の両方、および信頼した拡張のunsandboxed読込を必要とします。旧API、ブロックのopcode/入力、raw保存形式を保持し、無変更・Cancelでは元の入力を維持します。検査した候補だけを保存し、複数入力はatomicに保存します。Schema自体の編集・動的Schema・既存候補への新規項目追加はこの段階に含みません。
|
|
27
|
+
|
|
28
|
+
ロールバックはflagをOFFにして再読込します。コードを戻す場合はconsumer→producer→共通の逆順でrevertし、manifest・lock・実解決を一体で戻します。以前の公開版 `@kubohiroya/turbowarp-structured-data@0.5.1` へ固定できます。保存形式を自動移行しません。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubohiroya/turbowarp-structured-data",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
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",
|
|
@@ -66,10 +66,12 @@
|
|
|
66
66
|
"typescript": "^5.9.3",
|
|
67
67
|
"typescript-eslint": "^8.66.0",
|
|
68
68
|
"vite": "^8.2.2",
|
|
69
|
-
"vitest": "^5.0.0"
|
|
69
|
+
"vitest": "^5.0.0",
|
|
70
|
+
"react-dom": "18.3.1"
|
|
70
71
|
},
|
|
71
72
|
"dependencies": {
|
|
72
|
-
"yaml": "^2.9.1"
|
|
73
|
+
"yaml": "^2.9.1",
|
|
74
|
+
"@kubohiroya/turbowarp-extension-data-editor": "git+https://github.com/kubohiroya/turbowarp-extension-data-editor.git#76fa1d50a3c188082ceef723ea018a9d8769ea6e"
|
|
73
75
|
},
|
|
74
76
|
"peerDependencies": {
|
|
75
77
|
"@kubohiroya/turbowarp-named-data": ">=0.1.0 <1"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type {SourceEditorOptions} from '@kubohiroya/turbowarp-extension-data-editor/schema';
|
|
2
|
+
import {extensionConfig} from './config.js';
|
|
3
|
+
import {sourceBinding} from './source-editor.js';
|
|
4
|
+
import {jsonValueSchemaBundle} from '@kubohiroya/turbowarp-extension-data-editor/schema';
|
|
5
|
+
import {parseJsonDocument, parseYamlDocument} from './codecs.js';
|
|
6
|
+
import {parseJson} from './core.js';
|
|
7
|
+
const definitions = [
|
|
8
|
+
{editorId: 'json-document-source', opcodes: ['parseJson'], bindings: [sourceBinding('TEXT', jsonValueSchemaBundle, parseJsonDocument)]},
|
|
9
|
+
{editorId: 'yaml-document-source', opcodes: ['parseYaml'], bindings: [sourceBinding('TEXT', jsonValueSchemaBundle, parseYamlDocument, parseYamlDocument, 'yaml')]},
|
|
10
|
+
{editorId: 'json-path-value-source', opcodes: ['setJsonAtPath'], bindings: [sourceBinding('VALUE', jsonValueSchemaBundle, parseJson)]}
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export const sourceEditorDefinitions: readonly SourceEditorOptions[] = definitions.map(({editorId, opcodes, bindings}) => ({
|
|
14
|
+
apiVersion: 1, extensionId: extensionConfig.id, editorId, opcodes: opcodes.map(opcode => `${extensionConfig.id}_${opcode}`), bindings,
|
|
15
|
+
validationContext: () => ({consumerId: extensionConfig.id, nowMs: Date.now(), settings: null})
|
|
16
|
+
}));
|
package/src/extension.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {sourceEditorDefinitions} from './editor-schema.js';
|
|
2
|
+
import {registerSourceEditors} from './source-editor.js';
|
|
1
3
|
import definitions from './block-definitions.json';
|
|
2
4
|
import {
|
|
3
5
|
bindNamedDataRegistryLifecycle,
|
|
@@ -72,6 +74,7 @@ const blockDefinitions = definitions.blocks as readonly BlockDefinition[];
|
|
|
72
74
|
|
|
73
75
|
export class StructuredDataExtension implements TurboWarpExtension {
|
|
74
76
|
private readonly enabled: boolean;
|
|
77
|
+
private readonly unregisterSourceEditors: () => void;
|
|
75
78
|
private registries = new WeakMap<object, Map<string, StructuredDataBinding>>();
|
|
76
79
|
private iterationStacks = new WeakMap<object, ActiveIteration[]>();
|
|
77
80
|
private nextRevision = 1;
|
|
@@ -85,6 +88,7 @@ export class StructuredDataExtension implements TurboWarpExtension {
|
|
|
85
88
|
namedDataEnabled = isNamedDataRegistryMvpEnabled()
|
|
86
89
|
) {
|
|
87
90
|
this.enabled = enabled;
|
|
91
|
+
this.unregisterSourceEditors = registerSourceEditors(Scratch, enabled ? sourceEditorDefinitions : []);
|
|
88
92
|
if (namedDataEnabled) {
|
|
89
93
|
this.namedDataProvider = new StructuredDataNamedDataProvider({
|
|
90
94
|
get: (target, name) => this.registries.get(target)?.get(name)
|
|
@@ -117,6 +121,7 @@ export class StructuredDataExtension implements TurboWarpExtension {
|
|
|
117
121
|
}
|
|
118
122
|
|
|
119
123
|
public async dispose(): Promise<void> {
|
|
124
|
+
this.unregisterSourceEditors();
|
|
120
125
|
await this.namedDataRegistration?.unregister();
|
|
121
126
|
this.unbindNamedDataRegistryLifecycle?.();
|
|
122
127
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {compileSchemaBundle, validateSchemaInput} from '@kubohiroya/turbowarp-extension-data-editor/schema';
|
|
2
|
+
import type {SchemaBundle, SchemaDocument, SchemaJsonValue, SourceBinding, SourceEditorOptions} from '@kubohiroya/turbowarp-extension-data-editor/schema';
|
|
3
|
+
|
|
4
|
+
export function canonicalBundle(ownerId: string, contractId: string, document: SchemaDocument, ownerSource: {path: string; symbol: string}): SchemaBundle {
|
|
5
|
+
const path = `schemas/${contractId}.schema.json`;
|
|
6
|
+
const source = {repository: ownerId, path};
|
|
7
|
+
return {
|
|
8
|
+
identity: {ownerId, contractId, revision: 'source-forms-v1', variantId: 'default'},
|
|
9
|
+
dialect: '2020-12',
|
|
10
|
+
root: {retrievalUri: `https://kubohiroya.github.io/${ownerId}/schemas/${contractId}.schema.json`, document,
|
|
11
|
+
artifact: {role: 'canonical-schema', source}},
|
|
12
|
+
resources: [], provenance: {canonicalSources: [source, {repository: ownerId, ...ownerSource}], generatedFiles: [], referenceSchemas: [], generationSources: []}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function sourceBinding(argument: string, bundle: SchemaBundle,
|
|
17
|
+
read: (source: string) => unknown, check: (source: string) => unknown = read,
|
|
18
|
+
format: 'json' | 'yaml' = 'json', uiSchema?: SourceBinding['variants'][number]['uiSchema']): SourceBinding {
|
|
19
|
+
const compiled = compileSchemaBundle(bundle);
|
|
20
|
+
return {
|
|
21
|
+
argument,
|
|
22
|
+
variants: [{variantId: 'default', format, schemaBundle: bundle, ...(uiSchema ? {uiSchema} : {})}],
|
|
23
|
+
decode(source) {return {source, format, variantId: 'default', data: read(source) as SchemaJsonValue};},
|
|
24
|
+
validateSource(source, context) {
|
|
25
|
+
if (compiled.kind !== 'compiled') return {source, result: {kind: 'schema-error', diagnostics: [{code: 'source-schema-invalid'}]}};
|
|
26
|
+
try {
|
|
27
|
+
const data = read(source) as SchemaJsonValue;
|
|
28
|
+
return {source, result: validateSchemaInput(compiled.validator, data, {context,
|
|
29
|
+
ownerValidate: () => {
|
|
30
|
+
try {check(source); return {kind: 'valid', issues: []};}
|
|
31
|
+
catch {return {kind: 'invalid', issues: [{dataPath: '', ruleId: 'source-owner-invalid'}]};}
|
|
32
|
+
}})};
|
|
33
|
+
} catch {return {source, result: {kind: 'invalid', diagnostics: [{code: 'source-decode-invalid'}]}};}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The optional capability keeps legacy hosts and the startup flag safely inactive. */
|
|
39
|
+
export function registerSourceEditors(scratch: unknown, definitions: readonly SourceEditorOptions[]): () => void {
|
|
40
|
+
const host = scratch as {gui?: {blockEditors?: {apiVersion?: number; registerSourceEditor?: (options: SourceEditorOptions) => () => void}};
|
|
41
|
+
vm?: {runtime?: {on?: (event: string, listener: () => void) => void; off?: (event: string, listener: () => void) => void}}};
|
|
42
|
+
const api = host.gui?.blockEditors;
|
|
43
|
+
if (api?.apiVersion !== 1 || typeof api.registerSourceEditor !== 'function') return () => {};
|
|
44
|
+
const removers: (() => void)[] = [];
|
|
45
|
+
let active = true;
|
|
46
|
+
const dispose = () => {
|
|
47
|
+
if (!active) return;
|
|
48
|
+
active = false;
|
|
49
|
+
host.vm?.runtime?.off?.('RUNTIME_DISPOSED', dispose);
|
|
50
|
+
for (const remove of removers.splice(0).reverse()) remove();
|
|
51
|
+
};
|
|
52
|
+
try {
|
|
53
|
+
for (const definition of definitions) removers.push(api.registerSourceEditor.call(api, definition));
|
|
54
|
+
host.vm?.runtime?.on?.('RUNTIME_DISPOSED', dispose);
|
|
55
|
+
} catch (error) {dispose(); throw error;}
|
|
56
|
+
return dispose;
|
|
57
|
+
}
|