@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 +2 -0
- package/README.md +13 -1
- package/dist/structured-data.js +17 -6
- package/lib-editor/src/config.d.ts +6 -0
- package/package.json +7 -6
- package/src/config.ts +11 -0
- package/src/structured-provider.ts +38 -7
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.
|
|
40
|
+
pnpm add --save-exact @kubohiroya/turbowarp-structured-data@0.6.3
|
|
29
41
|
```
|
|
30
42
|
|
|
31
43
|
## Quick start
|
package/dist/structured-data.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
81
|
+
"@kubohiroya/turbowarp-named-data": ">=0.4.0 <1"
|
|
81
82
|
},
|
|
82
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "870ebcaa4071e0eacb4d36a6d9e98734e51f2361",
|
|
83
84
|
"x-turbowarp-source": {
|
|
84
|
-
"revision": "
|
|
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
|
-
): {
|
|
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
|
|