@kubohiroya/turbowarp-structured-data 0.6.2 → 0.6.3

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
@@ -23,6 +23,8 @@ namespaceとloop contextがTurboWarp BlockUtilityのtarget/thread identityを使
23
23
 
24
24
  JSON/YAML入力は256 KiB、深さ64、50,000 valueまでです。YAML alias、warning、unknown tag、duplicate key、string以外のmap key、未対応scalarは拒否します。
25
25
 
26
+ structured-data providerはnamed-data 0.4.0を介してbindingの任意の`schemaRef`を伝播できます。IDは`^[a-z][a-z0-9.-]{0,127}$`に一致し、versionは完全なSemVerである必要があります。Schema identityの解決は起動時固定で、明示的に有効化するまで既定OFFです。provider読み込み前に`globalThis.__TW_NAMED_DATA_FEATURE_FLAGS__ = {NAMED_DATA_SCHEMA_REF: true}`を設定します。参照はbindingの安定IDと完全なSemVer versionに一致する必要があります。providerはSchema本体を保持せず、別versionへのfallbackもしません。consumerは自身のSchema catalogでidentityを解決し、既存のJSON Schema実装で値を検証します。合意するsubsetは`type`、`properties`、`required`、Boolean形式の`additionalProperties`、`items`、`enum`、`const`、数値範囲、文字列/配列の長さ制約です。未対応keywordは検証動作を追加しません。flagをOFFにすればschema identityなしのmetadataへ即時に戻せます。
27
+
26
28
  ## クイックスタート
27
29
 
28
30
  ```text
package/README.md CHANGED
@@ -22,10 +22,22 @@ The `STRUCTURED_DATA_MVP` feature flag is fixed at extension startup and default
22
22
 
23
23
  JSON and YAML input is limited to 256 KiB, nesting depth 64, and 50,000 values. YAML aliases, warnings, unknown tags, duplicate keys, non-string map keys, and unsupported scalar types are rejected.
24
24
 
25
+ The structured-data provider can carry a binding's optional `schemaRef` through named-data 0.4.0.
26
+ The ID must match `^[a-z][a-z0-9.-]{0,127}$` and the version must be a complete SemVer version.
27
+ Schema identity resolution is a startup-fixed opt-in and defaults to `false`; configure
28
+ `globalThis.__TW_NAMED_DATA_FEATURE_FLAGS__ = {NAMED_DATA_SCHEMA_REF: true}` before loading the
29
+ provider. A request must match the binding's stable ID and full SemVer version exactly. The
30
+ provider does not store schema documents or choose a fallback version. Consumers resolve the
31
+ identity in their own schema catalog and validate the returned value with their existing JSON
32
+ Schema implementation. The agreed subset is `type`, `properties`, `required`,
33
+ `additionalProperties` (boolean), `items`, `enum`, `const`, numeric bounds, and string/array
34
+ length bounds; unsupported keywords do not add validation behavior. Turn the flag off to
35
+ immediately return to metadata without schema identities.
36
+
25
37
  ## Installation
26
38
 
27
39
  ```bash
28
- pnpm add --save-exact @kubohiroya/turbowarp-structured-data@0.6.2
40
+ pnpm add --save-exact @kubohiroya/turbowarp-structured-data@0.6.3
29
41
  ```
30
42
 
31
43
  ## Quick start
@@ -7,6 +7,10 @@
7
7
  (function (Scratch) {
8
8
  'use strict';
9
9
 
10
+ function isNamedDataSchemaRefEnabled(source = globalThis) {
11
+ const value = source.__TW_NAMED_DATA_FEATURE_FLAGS__?.NAMED_DATA_SCHEMA_REF;
12
+ return value === void 0 ? false : value === true || value === "true";
13
+ }
10
14
  function isStructuredDataMvpEnabled(source = globalThis) {
11
15
  const value = source.STRUCTURED_DATA_MVP;
12
16
  return value === void 0 ? false : value === true || value === "true";
@@ -6799,6 +6803,7 @@
6799
6803
  ...opened.digest === void 0 ? {} : { digest: opened.digest },
6800
6804
  revision: opened.revision,
6801
6805
  replayable: opened.replayable,
6806
+ ...opened.schemaRef === void 0 ? {} : { schemaRef: opened.schemaRef },
6802
6807
  body: opened.body,
6803
6808
  release: tracked.release
6804
6809
  });
@@ -6964,9 +6969,10 @@
6964
6969
  var STRUCTURED_DATA_NAMESPACE = "structured";
6965
6970
  var STRUCTURED_BODY_MAX_BYTES = 262144;
6966
6971
  var StructuredDataNamedDataProvider = class {
6967
- constructor(store, maxBodyBytes = STRUCTURED_BODY_MAX_BYTES) {
6972
+ constructor(store, maxBodyBytes = STRUCTURED_BODY_MAX_BYTES, schemaRefEnabled = isNamedDataSchemaRefEnabled()) {
6968
6973
  this.store = store;
6969
6974
  this.maxBodyBytes = maxBodyBytes;
6975
+ this.schemaRefEnabled = schemaRefEnabled;
6970
6976
  this.namespace = STRUCTURED_DATA_NAMESPACE;
6971
6977
  this.kind = "structured";
6972
6978
  this.openBodies = /* @__PURE__ */ new Set();
@@ -6977,7 +6983,7 @@
6977
6983
  }
6978
6984
  stat(reference, representation, context) {
6979
6985
  const resolved = this.resolve(reference, representation, context);
6980
- return this.metadata(reference, resolved.nativeRepresentation, representation, resolved.bytes.byteLength, resolved.revision);
6986
+ return this.metadata(reference, resolved.nativeRepresentation, representation, resolved.bytes.byteLength, resolved.revision, resolved.schemaRef);
6981
6987
  }
6982
6988
  openBody(reference, representation, context) {
6983
6989
  const resolved = this.resolve(reference, representation, context);
@@ -6985,7 +6991,7 @@
6985
6991
  this.openBodies.add(handle);
6986
6992
  let released = false;
6987
6993
  return {
6988
- ...this.metadata(reference, resolved.nativeRepresentation, representation, resolved.bytes.byteLength, resolved.revision),
6994
+ ...this.metadata(reference, resolved.nativeRepresentation, representation, resolved.bytes.byteLength, resolved.revision, resolved.schemaRef),
6989
6995
  body: resolved.bytes,
6990
6996
  release: () => {
6991
6997
  if (released) return;
@@ -7007,6 +7013,9 @@
7007
7013
  this.throwIfAborted(context.signal);
7008
7014
  const binding = this.store.get(context.target, reference.name);
7009
7015
  if (!binding) throw new NamedDataError("NAMED_DATA_NOT_FOUND", `Structured data does not exist: ${reference.name}`);
7016
+ const requestedSchemaRef = reference.schemaRef;
7017
+ if (requestedSchemaRef !== void 0 && !this.schemaRefEnabled) throw new NamedDataError("NAMED_DATA_SCHEMA_UNRESOLVED", "Schema reference resolution is disabled.");
7018
+ if (requestedSchemaRef !== void 0 && (binding.schemaRef?.id !== requestedSchemaRef.id || binding.schemaRef?.version !== requestedSchemaRef.version)) throw new NamedDataError("NAMED_DATA_SCHEMA_VERSION_MISMATCH", "Binding schemaRef does not match the requested reference.");
7010
7019
  const text = this.render(binding.value, representation);
7011
7020
  const bytes = new TextEncoder().encode(text);
7012
7021
  if (bytes.byteLength > this.maxBodyBytes) throw new NamedDataError("NAMED_DATA_BODY_TOO_LARGE", `Encoded body is ${bytes.byteLength} bytes; maximum is ${this.maxBodyBytes}.`);
@@ -7014,7 +7023,8 @@
7014
7023
  return {
7015
7024
  bytes,
7016
7025
  revision: binding.revision,
7017
- nativeRepresentation: binding.nativeRepresentation
7026
+ nativeRepresentation: binding.nativeRepresentation,
7027
+ ...requestedSchemaRef === void 0 ? {} : { schemaRef: binding.schemaRef }
7018
7028
  };
7019
7029
  }
7020
7030
  validateReference(reference, context) {
@@ -7027,7 +7037,7 @@
7027
7037
  if (representation === "yaml") return renderYamlDocument(value);
7028
7038
  throw new NamedDataError("NAMED_DATA_REPRESENTATION_UNSUPPORTED", `Structured data cannot be rendered as ${representation}.`);
7029
7039
  }
7030
- metadata(reference, nativeRepresentation, representation, byteLength, revision) {
7040
+ metadata(reference, nativeRepresentation, representation, byteLength, revision, schemaRef) {
7031
7041
  return {
7032
7042
  reference: { ...reference },
7033
7043
  nativeRepresentation,
@@ -7035,7 +7045,8 @@
7035
7045
  mediaType: representation === "json" ? "application/json; charset=utf-8" : "application/yaml; charset=utf-8",
7036
7046
  byteLength,
7037
7047
  revision: String(revision),
7038
- replayable: true
7048
+ replayable: true,
7049
+ ...schemaRef === void 0 ? {} : { schemaRef }
7039
7050
  };
7040
7051
  }
7041
7052
  throwIfAborted(signal) {
@@ -1,5 +1,11 @@
1
1
  export declare const STRUCTURED_DATA_MVP_DEFAULT = false;
2
2
  export declare const NAMED_DATA_REGISTRY_MVP_DEFAULT = false;
3
+ export declare const NAMED_DATA_SCHEMA_REF_DEFAULT = false;
4
+ export declare function isNamedDataSchemaRefEnabled(source?: {
5
+ __TW_NAMED_DATA_FEATURE_FLAGS__?: {
6
+ NAMED_DATA_SCHEMA_REF?: unknown;
7
+ };
8
+ }): boolean;
3
9
  export declare function isStructuredDataMvpEnabled(source?: {
4
10
  STRUCTURED_DATA_MVP?: unknown;
5
11
  }): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubohiroya/turbowarp-structured-data",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
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",
@@ -61,7 +61,7 @@
61
61
  "devDependencies": {
62
62
  "@eslint/js": "^9.39.5",
63
63
  "@kubohiroya/turbowarp-extension-manifest": "0.3.2",
64
- "@kubohiroya/turbowarp-named-data": "0.3.1",
64
+ "@kubohiroya/turbowarp-named-data": "0.4.0",
65
65
  "@kubohiroya/vite-plugin-turbowarp-extension": "0.6.1",
66
66
  "@types/node": "^24.13.3",
67
67
  "eslint": "^9.39.5",
@@ -71,17 +71,18 @@
71
71
  "vite": "^8.2.2",
72
72
  "vitest": "^5.0.0",
73
73
  "react-dom": "18.3.1",
74
- "@kubohiroya/turbowarp-extension-data-editor": "0.2.1"
74
+ "@kubohiroya/turbowarp-extension-data-editor": "0.2.1",
75
+ "@kubohiroya/turbowarp-named-functions": "0.1.1"
75
76
  },
76
77
  "dependencies": {
77
78
  "yaml": "^2.9.1"
78
79
  },
79
80
  "peerDependencies": {
80
- "@kubohiroya/turbowarp-named-data": "0.3.1"
81
+ "@kubohiroya/turbowarp-named-data": ">=0.4.0 <1"
81
82
  },
82
- "gitHead": "385a31c2a7957b51003686fab0b2d76199db705a",
83
+ "gitHead": "870ebcaa4071e0eacb4d36a6d9e98734e51f2361",
83
84
  "x-turbowarp-source": {
84
- "revision": "385a31c2a7957b51003686fab0b2d76199db705a",
85
+ "revision": "870ebcaa4071e0eacb4d36a6d9e98734e51f2361",
85
86
  "revisionRole": "base-commit",
86
87
  "sourceState": "local-release-overlay"
87
88
  },
package/src/config.ts CHANGED
@@ -1,5 +1,16 @@
1
1
  export const STRUCTURED_DATA_MVP_DEFAULT = false;
2
2
  export const NAMED_DATA_REGISTRY_MVP_DEFAULT = false;
3
+ export const NAMED_DATA_SCHEMA_REF_DEFAULT = false;
4
+
5
+ export function isNamedDataSchemaRefEnabled(
6
+ source: {__TW_NAMED_DATA_FEATURE_FLAGS__?: {NAMED_DATA_SCHEMA_REF?: unknown}} =
7
+ globalThis as {__TW_NAMED_DATA_FEATURE_FLAGS__?: {NAMED_DATA_SCHEMA_REF?: unknown}}
8
+ ): boolean {
9
+ const value = source.__TW_NAMED_DATA_FEATURE_FLAGS__?.NAMED_DATA_SCHEMA_REF;
10
+ return value === undefined
11
+ ? NAMED_DATA_SCHEMA_REF_DEFAULT
12
+ : value === true || value === 'true';
13
+ }
3
14
 
4
15
  export function isStructuredDataMvpEnabled(
5
16
  source: {STRUCTURED_DATA_MVP?: unknown} = globalThis as {STRUCTURED_DATA_MVP?: unknown}
@@ -1,4 +1,5 @@
1
1
  import {renderJsonDocument, renderYamlDocument} from './codecs.js';
2
+ import {isNamedDataSchemaRefEnabled} from './config.js';
2
3
  import type {JsonValue} from './core.js';
3
4
  import {
4
5
  NamedDataError,
@@ -6,6 +7,7 @@ import {
6
7
  type NamedDataMetadata,
7
8
  type NamedDataProvider,
8
9
  type NamedDataReference,
10
+ type NamedDataSchemaRef,
9
11
  type NamedDataRepresentation,
10
12
  type NamedDataResolveContext
11
13
  } from '@kubohiroya/turbowarp-named-data/composition';
@@ -17,6 +19,7 @@ export interface StructuredDataBinding {
17
19
  value: JsonValue;
18
20
  revision: number;
19
21
  nativeRepresentation: 'json' | 'yaml';
22
+ schemaRef?: NamedDataSchemaRef;
20
23
  }
21
24
 
22
25
  export interface StructuredDataBindingStore {
@@ -31,7 +34,8 @@ export class StructuredDataNamedDataProvider implements NamedDataProvider {
31
34
 
32
35
  public constructor(
33
36
  private readonly store: StructuredDataBindingStore,
34
- private readonly maxBodyBytes = STRUCTURED_BODY_MAX_BYTES
37
+ private readonly maxBodyBytes = STRUCTURED_BODY_MAX_BYTES,
38
+ private readonly schemaRefEnabled = isNamedDataSchemaRefEnabled()
35
39
  ) {}
36
40
 
37
41
  public canResolve(
@@ -57,7 +61,8 @@ export class StructuredDataNamedDataProvider implements NamedDataProvider {
57
61
  resolved.nativeRepresentation,
58
62
  representation,
59
63
  resolved.bytes.byteLength,
60
- resolved.revision
64
+ resolved.revision,
65
+ resolved.schemaRef
61
66
  );
62
67
  }
63
68
 
@@ -76,7 +81,8 @@ export class StructuredDataNamedDataProvider implements NamedDataProvider {
76
81
  resolved.nativeRepresentation,
77
82
  representation,
78
83
  resolved.bytes.byteLength,
79
- resolved.revision
84
+ resolved.revision,
85
+ resolved.schemaRef
80
86
  ),
81
87
  body: resolved.bytes,
82
88
  release: () => {
@@ -100,7 +106,12 @@ export class StructuredDataNamedDataProvider implements NamedDataProvider {
100
106
  reference: NamedDataReference,
101
107
  representation: NamedDataRepresentation,
102
108
  context: NamedDataResolveContext
103
- ): {bytes: Uint8Array; revision: number; nativeRepresentation: 'json' | 'yaml'} {
109
+ ): {
110
+ bytes: Uint8Array;
111
+ revision: number;
112
+ nativeRepresentation: 'json' | 'yaml';
113
+ schemaRef?: NamedDataSchemaRef;
114
+ } {
104
115
  if (this.released) {
105
116
  throw new NamedDataError('NAMED_DATA_PROVIDER_RELEASED', 'The structured provider was released.');
106
117
  }
@@ -113,6 +124,23 @@ export class StructuredDataNamedDataProvider implements NamedDataProvider {
113
124
  `Structured data does not exist: ${reference.name}`
114
125
  );
115
126
  }
127
+ const requestedSchemaRef = reference.schemaRef;
128
+ if (requestedSchemaRef !== undefined && !this.schemaRefEnabled) {
129
+ throw new NamedDataError(
130
+ 'NAMED_DATA_SCHEMA_UNRESOLVED',
131
+ 'Schema reference resolution is disabled.'
132
+ );
133
+ }
134
+ if (
135
+ requestedSchemaRef !== undefined &&
136
+ (binding.schemaRef?.id !== requestedSchemaRef.id ||
137
+ binding.schemaRef?.version !== requestedSchemaRef.version)
138
+ ) {
139
+ throw new NamedDataError(
140
+ 'NAMED_DATA_SCHEMA_VERSION_MISMATCH',
141
+ 'Binding schemaRef does not match the requested reference.'
142
+ );
143
+ }
116
144
  const text = this.render(binding.value, representation);
117
145
  const bytes = new TextEncoder().encode(text);
118
146
  if (bytes.byteLength > this.maxBodyBytes) {
@@ -125,7 +153,8 @@ export class StructuredDataNamedDataProvider implements NamedDataProvider {
125
153
  return {
126
154
  bytes,
127
155
  revision: binding.revision,
128
- nativeRepresentation: binding.nativeRepresentation
156
+ nativeRepresentation: binding.nativeRepresentation,
157
+ ...(requestedSchemaRef === undefined ? {} : {schemaRef: binding.schemaRef})
129
158
  };
130
159
  }
131
160
 
@@ -164,7 +193,8 @@ export class StructuredDataNamedDataProvider implements NamedDataProvider {
164
193
  nativeRepresentation: 'json' | 'yaml',
165
194
  representation: NamedDataRepresentation,
166
195
  byteLength: number,
167
- revision: number
196
+ revision: number,
197
+ schemaRef?: NamedDataSchemaRef
168
198
  ): NamedDataMetadata {
169
199
  return {
170
200
  reference: {...reference},
@@ -176,7 +206,8 @@ export class StructuredDataNamedDataProvider implements NamedDataProvider {
176
206
  : 'application/yaml; charset=utf-8',
177
207
  byteLength,
178
208
  revision: String(revision),
179
- replayable: true
209
+ replayable: true,
210
+ ...(schemaRef === undefined ? {} : {schemaRef})
180
211
  };
181
212
  }
182
213