@kubohiroya/turbowarp-structured-data 0.6.3 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # TurboWarp-Structured-Data
2
2
 
3
+ <img src="assets/block-icon.svg" alt="" width="48" height="48">
4
+
3
5
  [English](README.md)
4
6
 
5
7
  名前付き構造化データ、安全なJSON/YAML codec、決定的なpath操作、上限付き反復、server compiler契約を提供するTurboWarp機能拡張です。
@@ -65,6 +67,42 @@ arrayはindex昇順、objectはUnicodeコードポイント順で反復します
65
67
 
66
68
  `dist/extension-manifest.json` format version 3はtarget-local named reference、型付きpath、argument/result型、effect、error、IR v2 operationを宣言します。[architecture](docs/architecture.ja.md)、[JSON Schema](schemas/extension-manifest.schema.json)、[semantic parity fixture](tests/fixtures/semantic-parity.json)を参照してください。
67
69
 
70
+ ## 統合ビルド用module API
71
+
72
+ このソースのbuildは、単体JSとは別に、統合用のESM入口`./bundle-module`と
73
+ 型宣言を`lib-module/`へ生成します。
74
+
75
+ ```ts
76
+ import {bundleModuleMetadata, createExtension, type ScratchApi}
77
+ from '@kubohiroya/turbowarp-structured-data/bundle-module';
78
+
79
+ export function createMember(memberScratch: ScratchApi) {
80
+ return createExtension(memberScratch);
81
+ }
82
+ ```
83
+
84
+ import時はglobal Scratch / window / documentを参照せず、拡張の生成・登録を行いません。
85
+ 明示的なcreateExtension(memberScratch)ごとに新しいinstanceを返し、後から実行する
86
+ ブロック・編集用補助処理・lifecycle callbackも同じhostを使います。factoryは
87
+ Scratch.extensions.registerを呼びません。単体entryも同じfactoryを使い、登録は1回です。
88
+
89
+ 凍結されたbundleModuleMetadataはformatVersion: 1、元のid、unsandboxed条件を
90
+ 宣言します。統合hostが生成前に検査し、登録とdisposeを管理します。ID・opcode・
91
+ 保存データ形式を変更しません。
92
+
93
+ 既存のSTRUCTURED_DATA_MVP・NAMED_DATA_REGISTRY_MVP・Schema参照の起動フラグと既定値を維持します。生成時の任意の編集登録・named-data初期化には注入hostを使います。dispose()をawaitして、このinstanceの登録とlistenerを解除します。StructuredDataExtensionを直接newする内部コードでは、hostを先頭に渡し、その後に従来の任意の機能フラグ引数を渡します。
94
+
95
+ yaml等のruntime依存とそのsubpathは外部importのまま保持します。既存の小さな編集用
96
+ 純粋ヘルパーだけを含め、production consumerには編集packageやGUIエンジンを要求しません。
97
+ 公開型は既存tsconfigと同じES2022の型ライブラリを使い、生成JSはES2020を対象とします。
98
+ 既存exportsを維持して./bundle-moduleを追加し、templateへのruntime依存は追加しません。
99
+
100
+ pnpm run build:moduleで生成し、pnpm run check:moduleは実tarballをproduction依存だけの
101
+ consumerへinstallしてhost分離・単体版との動作一致・公開型・編集エンジンの非混入を検査します。
102
+ sb3-toolchain側の統合buildは[sb3-toolchain #65](https://github.com/kubohiroya/sb3-toolchain/issues/65)の後続です。
103
+ 切戻しはこのmodule入口を選択せず従来のclassic distを読み込みます。新しいruntimeフラグや
104
+ 保存データの移行は導入しません。
105
+
68
106
  ## 開発
69
107
 
70
108
  ```bash
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # TurboWarp-Structured-Data
2
2
 
3
+ <img src="assets/block-icon.svg" alt="" width="48" height="48">
4
+
3
5
  [日本語](README.ja.md)
4
6
 
5
7
  Named structured data with safe JSON/YAML codecs, deterministic path operations, bounded iteration, and a server compiler contract.
@@ -37,7 +39,7 @@ immediately return to metadata without schema identities.
37
39
  ## Installation
38
40
 
39
41
  ```bash
40
- pnpm add --save-exact @kubohiroya/turbowarp-structured-data@0.6.3
42
+ pnpm add --save-exact @kubohiroya/turbowarp-structured-data@0.7.1
41
43
  ```
42
44
 
43
45
  ## Quick start
@@ -244,6 +246,48 @@ Return the current iteration value as JSON.
244
246
 
245
247
  `dist/extension-manifest.json` format version 3 declares the target-local named reference model, typed path segments, block argument/result types, effects, stable errors, and IR v2 operations. See [architecture](docs/architecture.md), the [JSON Schema](schemas/extension-manifest.schema.json), and [semantic parity fixtures](tests/fixtures/semantic-parity.json).
246
248
 
249
+ ## Integrated-build module API
250
+
251
+ Building this source also produces a `./bundle-module` ESM entry and declarations
252
+ in `lib-module/`. This entry is separate from the standalone JavaScript file.
253
+
254
+ ```ts
255
+ import {bundleModuleMetadata, createExtension, type ScratchApi}
256
+ from '@kubohiroya/turbowarp-structured-data/bundle-module';
257
+
258
+ export function createMember(memberScratch: ScratchApi) {
259
+ return createExtension(memberScratch);
260
+ }
261
+ ```
262
+
263
+ Importing the entry does not access global Scratch / window / document or create
264
+ and register an extension. Each explicit createExtension(memberScratch) call
265
+ returns a fresh instance bound to that host, including deferred block execution,
266
+ editor helpers and lifecycle callbacks. The factory does not call
267
+ Scratch.extensions.register; the standalone entry uses the same factory and
268
+ registers exactly once.
269
+
270
+ The frozen bundleModuleMetadata declares formatVersion: 1, the original id and
271
+ unsandboxed requirement. An integrated host validates these before creation and
272
+ owns registration and disposal. Existing IDs, opcodes and saved data stay unchanged.
273
+
274
+ The existing STRUCTURED_DATA_MVP, NAMED_DATA_REGISTRY_MVP and Schema reference startup flags retain their defaults. Creation performs the existing optional editor and named-data initialization against the injected host. Call and await dispose() to release the instance registrations and listeners. Internal callers constructing StructuredDataExtension directly now pass the host first, followed by the existing optional feature arguments.
275
+
276
+ Runtime dependencies (including yaml and their subpaths) remain external imports.
277
+ Only the existing small, pure development-time editor helpers are included, so
278
+ production consumers need no editor package or GUI engine. Public types use the
279
+ same ES2022 libraries as the project tsconfig; generated JavaScript targets ES2020.
280
+ The package retains its existing exports and adds ./bundle-module; no template
281
+ runtime dependency is required.
282
+
283
+ pnpm run build:module builds this entry; pnpm run check:module packs and installs
284
+ the actual package with production dependencies only, checks host isolation,
285
+ standalone parity and public types, and rejects editor-engine dependency leaks.
286
+ The integrated sb3-toolchain build remains a later step in
287
+ [sb3-toolchain #65](https://github.com/kubohiroya/sb3-toolchain/issues/65).
288
+ To roll back, continue loading the existing classic dist artifact without selecting
289
+ this module entry. No runtime flag or saved-data migration is introduced.
290
+
247
291
  ## Development
248
292
 
249
293
  ```bash
@@ -258,15 +302,15 @@ SPDX-License-Identifier: MPL-2.0
258
302
 
259
303
  Bundled third-party license terms are listed in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
260
304
 
261
- ## 固定引数の編集フォーム(S3-4)
305
+ ## Fixed-input editor form (S3-4)
262
306
 
263
- 共通エディターの起動時フラグ `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を評価しません。明示変更だけを検査し、他の引数やブロック宣言、保存形式を変更しません。
307
+ When the shared editor's startup flag `blockEditorFixedInputs` and Dialogs are both on in the GUI, the extension registers a fixed form for `forEachAtPath`'s `MAX` input only. Older GUIs, or a GUI with the flag off, get no extra registration. The canonical Schema is `schemas/block-editor-fixed-inputs.schema.json#/$defs/forEachAtPath/properties/MAX`; the codec and registration live in `src/fixed-input-editors.ts`. Saving without changes never rewrites existing raw values, unchanged aliases, or unknown enum values, and connected reporters are never evaluated. Only explicit changes are validated; other arguments, block declarations, and the saved format are unchanged.
264
308
 
265
- MAXは整数1〜1000です。新しい入力には共通の有限数値token規則も適用し、途中入力を保存しません。MVP flagがOFFなら登録せず、NAME/PATH・staticLiteral由来の接続可否を変更しません。
309
+ `MAX` is an integer from 1 to 1000. New input also follows the shared finite-number token rule, and partial input is not saved. When the MVP flag is off nothing is registered, and whether `NAME`/`PATH` accept connected reporters (derived from `staticLiteral`) does not change.
266
310
 
267
- 登録はoptional API(apiVersion:1)に限定し、RUNTIME_DISPOSED/明示disposeで解除します。起動フラグOFF+再読込で従来経路へ切り戻せます。依存を戻す場合はmanifest/lock/実解決を一組で復旧します。npm公開や後続段階の動的候補は含みません。
311
+ Registration is limited to the optional API (`apiVersion: 1`) and is released on `RUNTIME_DISPOSED` or an explicit dispose. Turning the startup flag off and reloading restores the previous behavior. When rolling back the dependency, restore the manifest, lockfile, and actual resolution together. This stage does not include an npm release or dynamic candidates from later stages.
268
312
 
269
- Schemaが記述する契約の根拠は実getInfoの宣言と既存の純粋なruntime変換です。SchemaBundleのretrievalUriはこのローカル文書の識別子であり、URL配信済み・HTTP取得によるref解決を意味しません。Schemaは同梱文書だけでcompileします。
270
- 既存MAXの非空finiteなNumber alias(例`001`・`+1`・`0x10`)はNumberによる表示投影だけを行い、無変更rawを保存し直しません。空白・非finite・投影不能値はreadonlyとし、0/defaultを補いません。新しい入力にはこれらaliasを許可せずNP-finite-new-input-v1を適用します。
313
+ The contract the Schema describes is grounded in the actual `getInfo` declarations and the existing pure runtime conversions. The SchemaBundle `retrievalUri` identifies this local document; it does not mean the Schema is served from that URL or that `$ref` is resolved over HTTP. The Schema compiles from the bundled documents only.
314
+ Existing non-empty finite Number aliases for `MAX` (for example `001`, `+1`, or `0x10`) are only projected through `Number` for display; unchanged raw values are not saved again. Blank, non-finite, or unprojectable values are read-only, and no `0` or default is substituted. New input does not accept these aliases and follows NP-finite-new-input-v1.
271
315
 
272
316
  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.
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><rect x="4" y="8" width="40" height="32" rx="6" fill="#8A55D7"/><path d="M19 15c-3 0-2 7-5 9 3 2 2 9 5 9" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M29 15c3 0 2 7 5 9-3 2-2 9-5 9" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><circle cx="24" cy="24" r="2.2" fill="#fff"/></svg>
@@ -28,7 +28,7 @@
28
28
  license: "MPL-2.0",
29
29
  unsandboxed: true,
30
30
  docsURI: "https://kubohiroya.github.io/turbowarp-structured-data/",
31
- blockIconURI: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OCA0OCI+PHJlY3QgeD0iNCIgeT0iOCIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE0IiByeD0iMyIgZmlsbD0iIzRDOTdGRiIvPjxyZWN0IHg9IjI2IiB5PSI4IiB3aWR0aD0iMTgiIGhlaWdodD0iMTQiIHJ4PSIzIiBmaWxsPSIjNTlDMDU5Ii8+PHJlY3QgeD0iMTUiIHk9IjI2IiB3aWR0aD0iMTgiIGhlaWdodD0iMTQiIHJ4PSIzIiBmaWxsPSIjRkZBQjE5Ii8+PC9zdmc+"
31
+ blockIconURI: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OCA0OCI+PHJlY3QgeD0iNCIgeT0iOCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjMyIiByeD0iNiIgZmlsbD0iIzhBNTVENyIvPjxwYXRoIGQ9Ik0xOSAxNWMtMyAwLTIgNy01IDkgMyAyIDIgOSA1IDkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMjkgMTVjMyAwIDIgNyA1IDktMyAyLTIgOS01IDkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48Y2lyY2xlIGN4PSIyNCIgY3k9IjI0IiByPSIyLjIiIGZpbGw9IiNmZmYiLz48L3N2Zz4="
32
32
  };
33
33
  var numberToken = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/u;
34
34
  var invalidNumber = Object.freeze({
@@ -7056,14 +7056,15 @@
7056
7056
  var LOOP_STATE = "structuredDataLoopState";
7057
7057
  var blockDefinitions = block_definitions_default.blocks;
7058
7058
  var StructuredDataExtension = class {
7059
- constructor(enabled = isStructuredDataMvpEnabled(), namedDataEnabled = isNamedDataRegistryMvpEnabled()) {
7059
+ constructor(scratch, enabled = isStructuredDataMvpEnabled(), namedDataEnabled = isNamedDataRegistryMvpEnabled()) {
7060
+ this.scratch = scratch;
7060
7061
  this.disposed = false;
7061
7062
  this.registries = /* @__PURE__ */ new WeakMap();
7062
7063
  this.iterationStacks = /* @__PURE__ */ new WeakMap();
7063
7064
  this.nextRevision = 1;
7064
7065
  this.enabled = enabled;
7065
- this.unregisterSourceEditors = registerSourceEditors(Scratch, enabled ? sourceEditorDefinitions : []);
7066
- const runtime = Scratch.vm?.runtime;
7066
+ this.unregisterSourceEditors = registerSourceEditors(this.scratch, enabled ? sourceEditorDefinitions : []);
7067
+ const runtime = this.scratch.vm?.runtime;
7067
7068
  const onDispose = () => {
7068
7069
  this.dispose();
7069
7070
  };
@@ -7074,11 +7075,11 @@
7074
7075
  };
7075
7076
  let unregisterFixed = () => {};
7076
7077
  try {
7077
- unregisterFixed = registerFixedInputEditors(Scratch, enabled ? fixedInputEditorDefinitions : []);
7078
+ unregisterFixed = registerFixedInputEditors(this.scratch, enabled ? fixedInputEditorDefinitions : []);
7078
7079
  this.unregisterFixedInputEditors = unregisterFixed;
7079
7080
  if (namedDataEnabled) {
7080
7081
  this.namedDataProvider = new StructuredDataNamedDataProvider({ get: (target, name) => this.registries.get(target)?.get(name) });
7081
- const runtime = Scratch.vm?.runtime;
7082
+ const runtime = this.scratch.vm?.runtime;
7082
7083
  if (runtime) {
7083
7084
  this.namedDataRegistry = installNamedDataRegistry(runtime);
7084
7085
  this.namedDataRegistration = this.namedDataRegistry.registerProvider(this.namedDataProvider, { lifetime: "persistent" });
@@ -7114,17 +7115,17 @@
7114
7115
  getInfo() {
7115
7116
  return {
7116
7117
  id: extensionConfig.id,
7117
- name: Scratch.translate(block_definitions_default.extensionName),
7118
+ name: this.scratch.translate(block_definitions_default.extensionName),
7118
7119
  docsURI: extensionConfig.docsURI,
7119
7120
  blockIconURI: extensionConfig.blockIconURI,
7120
7121
  blocks: this.enabled ? blockDefinitions.map((block) => this.toScratchBlock(block)) : []
7121
7122
  };
7122
7123
  }
7123
7124
  parseJson(args, util) {
7124
- this.store(util.target, args.NAME, parseJsonDocument(Scratch.Cast.toString(args.TEXT)), "json");
7125
+ this.store(util.target, args.NAME, parseJsonDocument(this.scratch.Cast.toString(args.TEXT)), "json");
7125
7126
  }
7126
7127
  parseYaml(args, util) {
7127
- this.store(util.target, args.NAME, parseYamlDocument(Scratch.Cast.toString(args.TEXT)), "yaml");
7128
+ this.store(util.target, args.NAME, parseYamlDocument(this.scratch.Cast.toString(args.TEXT)), "yaml");
7128
7129
  }
7129
7130
  toJson(args, util) {
7130
7131
  return renderJsonDocument(this.requireData(util.target, args.NAME));
@@ -7148,7 +7149,7 @@
7148
7149
  }
7149
7150
  setJsonAtPath(args, util) {
7150
7151
  const name = this.name(args.NAME);
7151
- const updated = setAtPath(this.requireDataByName(util.target, name), this.path(args.PATH), parseJson(Scratch.Cast.toString(args.VALUE)));
7152
+ const updated = setAtPath(this.requireDataByName(util.target, name), this.path(args.PATH), parseJson(this.scratch.Cast.toString(args.VALUE)));
7152
7153
  this.setBinding(util.target, name, updated);
7153
7154
  }
7154
7155
  deleteAtPath(args, util) {
@@ -7167,7 +7168,7 @@
7167
7168
  const stack = this.stackFor(util.thread);
7168
7169
  if (!state) {
7169
7170
  state = {
7170
- entries: iterationEntries(this.requireData(util.target, args.NAME), this.path(args.PATH), Scratch.Cast.toNumber(args.MAX)),
7171
+ entries: iterationEntries(this.requireData(util.target, args.NAME), this.path(args.PATH), this.scratch.Cast.toNumber(args.MAX)),
7171
7172
  cursor: 0,
7172
7173
  active: false
7173
7174
  };
@@ -7229,7 +7230,7 @@
7229
7230
  this.nextRevision += 1;
7230
7231
  }
7231
7232
  name(value) {
7232
- const name = Scratch.Cast.toString(value).trim();
7233
+ const name = this.scratch.Cast.toString(value).trim();
7233
7234
  if (name.length === 0) throw new StructuredDataError("INVALID_NAME", "Structured data name cannot be empty.");
7234
7235
  return name;
7235
7236
  }
@@ -7250,7 +7251,7 @@
7250
7251
  return stack;
7251
7252
  }
7252
7253
  path(value) {
7253
- return parsePath(Scratch.Cast.toString(value));
7254
+ return parsePath(this.scratch.Cast.toString(value));
7254
7255
  }
7255
7256
  clearRuntimeState() {
7256
7257
  this.namedDataProvider?.clearSession();
@@ -7261,16 +7262,25 @@
7261
7262
  toScratchBlock(block) {
7262
7263
  return {
7263
7264
  opcode: block.opcode,
7264
- blockType: Scratch.BlockType[block.blockType],
7265
- text: Scratch.translate(block.text),
7265
+ blockType: this.scratch.BlockType[block.blockType],
7266
+ text: this.scratch.translate(block.text),
7266
7267
  arguments: Object.fromEntries(Object.entries(block.arguments).map(([name, argument]) => [name, {
7267
- type: Scratch.ArgumentType[argument.type],
7268
+ type: this.scratch.ArgumentType[argument.type],
7268
7269
  defaultValue: argument.defaultValue
7269
7270
  }]))
7270
7271
  };
7271
7272
  }
7272
7273
  };
7274
+ Object.freeze({
7275
+ formatVersion: 1,
7276
+ id: extensionConfig.id,
7277
+ unsandboxed: extensionConfig.unsandboxed ? "required" : "not-needed"
7278
+ });
7279
+ /** The caller supplies the member host and owns registration and lifetime. */
7280
+ function createExtension(scratch) {
7281
+ return new StructuredDataExtension(scratch);
7282
+ }
7273
7283
  if (extensionConfig.unsandboxed && !Scratch.extensions.unsandboxed) throw new Error(`${extensionConfig.name} must run unsandboxed.`);
7274
- Scratch.extensions.register(new StructuredDataExtension());
7284
+ Scratch.extensions.register(createExtension(Scratch));
7275
7285
 
7276
7286
  })(Scratch);
package/docs/.nojekyll ADDED
File without changes
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10.226 17.284c-2.965-.36-5.054-2.493-5.054-5.256 0-1.123.404-2.336 1.078-3.144-.292-.741-.247-2.314.09-2.965.898-.112 2.111.36 2.83 1.01.853-.269 1.752-.404 2.853-.404 1.1 0 1.999.135 2.807.382.696-.629 1.932-1.1 2.83-.988.315.606.36 2.179.067 2.942.72.854 1.101 2 1.101 3.167 0 2.763-2.089 4.852-5.098 5.234.763.494 1.28 1.572 1.28 2.807v2.336c0 .674.561 1.056 1.235.786 4.066-1.55 7.255-5.615 7.255-10.646C23.5 6.188 18.334 1 11.978 1 5.62 1 .5 6.188.5 12.545c0 4.986 3.167 9.12 7.435 10.669.606.225 1.19-.18 1.19-.786V20.63a2.9 2.9 0 0 1-1.078.224c-1.483 0-2.359-.808-2.987-2.313-.247-.607-.517-.966-1.034-1.033-.27-.023-.359-.135-.359-.27 0-.27.45-.471.898-.471.652 0 1.213.404 1.797 1.235.45.651.921.943 1.483.943.561 0 .92-.202 1.437-.719.382-.381.674-.718.944-.943"/></svg>