@kubohiroya/turbowarp-structured-data 0.6.1 → 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/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}
@@ -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';
@@ -1,5 +1,5 @@
1
1
  import type {SchemaBundle} from '@kubohiroya/turbowarp-extension-data-editor/schema';
2
- import document from '../schemas/block-editor-fixed-inputs.schema.json';
2
+ import document from '../schemas/block-editor-fixed-inputs.schema.json' with {type: 'json'};
3
3
 
4
4
  const source = {repository: 'turbowarp-structured-data', path: 'schemas/block-editor-fixed-inputs.schema.json',
5
5
  symbol: '#/$defs/forEachAtPath/properties/MAX'};
@@ -1,4 +1,3 @@
1
- import {compileSchemaBundle, validateSchemaInput} from '@kubohiroya/turbowarp-extension-data-editor/schema';
2
1
  import type {SchemaBundle, SchemaDocument, SchemaJsonValue, SourceBinding, SourceEditorOptions} from '@kubohiroya/turbowarp-extension-data-editor/schema';
3
2
 
4
3
  export function canonicalBundle(ownerId: string, contractId: string, document: SchemaDocument, ownerSource: {path: string; symbol: string}): SchemaBundle {
@@ -16,21 +15,25 @@ export function canonicalBundle(ownerId: string, contractId: string, document: S
16
15
  export function sourceBinding(argument: string, bundle: SchemaBundle,
17
16
  read: (source: string) => unknown, check: (source: string) => unknown = read,
18
17
  format: 'json' | 'yaml' = 'json', uiSchema?: SourceBinding['variants'][number]['uiSchema']): SourceBinding {
19
- const compiled = compileSchemaBundle(bundle);
20
18
  return {
21
19
  argument,
22
20
  variants: [{variantId: 'default', format, schemaBundle: bundle, ...(uiSchema ? {uiSchema} : {})}],
23
21
  decode(source) {return {source, format, variantId: 'default', data: read(source) as SchemaJsonValue};},
24
22
  validateSource(source, context) {
25
- if (compiled.kind !== 'compiled') return {source, result: {kind: 'schema-error', diagnostics: [{code: 'source-schema-invalid'}]}};
23
+ try {read(source);}
24
+ catch {return {source, result: {kind: 'invalid', diagnostics: [{code: 'source-decode-invalid'}]}};}
26
25
  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'}]}};}
26
+ const descriptors = Object.getOwnPropertyDescriptors(context);
27
+ if (typeof descriptors.consumerId?.value !== 'string' ||
28
+ typeof descriptors.nowMs?.value !== 'number' || !Number.isFinite(descriptors.nowMs.value) ||
29
+ !descriptors.settings || !('value' in descriptors.settings)) {
30
+ return {source, result: {kind: 'execution-error', diagnostics: [{code: 'owner-context-invalid'}]}};
31
+ }
32
+ } catch {return {source, result: {kind: 'execution-error', diagnostics: [{code: 'owner-context-invalid'}]}};}
33
+ // The GUI checks Schema constraints; the owner checks the exact source without compiling.
34
+ try {check(source);}
35
+ catch {return {source, result: {kind: 'invalid', diagnostics: [{code: 'source-owner-invalid'}]}};}
36
+ return {source, result: {kind: 'valid', diagnostics: []}};
34
37
  }
35
38
  };
36
39
  }
@@ -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