@kubohiroya/turbowarp-structured-data 0.4.0 → 0.5.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 CHANGED
@@ -23,7 +23,7 @@ JSON and YAML input is limited to 256 KiB, nesting depth 64, and 50,000 values.
23
23
  ## Installation
24
24
 
25
25
  ```bash
26
- pnpm add --save-exact @kubohiroya/turbowarp-structured-data@0.4.0
26
+ pnpm add --save-exact @kubohiroya/turbowarp-structured-data@0.5.0
27
27
  ```
28
28
 
29
29
  ## Quick start
@@ -1,5 +1,5 @@
1
1
  {
2
- "formatVersion": 3,
2
+ "formatVersion": 2,
3
3
  "id": "kubohiroyastructureddata",
4
4
  "pathSegmentType": {
5
5
  "kind": "discriminatedUnion",
@@ -422,5 +422,6 @@
422
422
  "irOperation": "structuredData.toYaml"
423
423
  }
424
424
  }
425
- ]
425
+ ],
426
+ "menus": []
426
427
  }
@@ -440,7 +440,7 @@
440
440
  ]
441
441
  };
442
442
  //#endregion
443
- //#region ../turbowarp-named-data/dist/composition.js
443
+ //#region node_modules/.pnpm/@kubohiroya+turbowarp-named-data@0.1.0/node_modules/@kubohiroya/turbowarp-named-data/dist/composition.js
444
444
  var NAMED_DATA_REGISTRY_SYMBOL_KEY = "@kubohiroya/turbowarp-named-data/registry";
445
445
  var NAMED_DATA_REGISTRY_SYMBOL = Symbol.for(NAMED_DATA_REGISTRY_SYMBOL_KEY);
446
446
  var NAMED_DATA_NAMESPACE_PATTERN = /^[a-z][a-z0-9.-]{0,63}$/u;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubohiroya/turbowarp-structured-data",
3
- "version": "0.4.0",
3
+ "version": "0.5.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",
@@ -26,7 +26,6 @@
26
26
  "files": [
27
27
  "dist/",
28
28
  "docs/",
29
- "schemas/",
30
29
  "src/",
31
30
  "templates/",
32
31
  "README.md",
@@ -58,7 +57,8 @@
58
57
  },
59
58
  "devDependencies": {
60
59
  "@eslint/js": "^9.39.5",
61
- "@kubohiroya/turbowarp-named-data": "github:kubohiroya/turbowarp-named-data#bc3be0e669f501479ef45877e5d06e27714184d2",
60
+ "@kubohiroya/turbowarp-extension-manifest": "0.3.0",
61
+ "@kubohiroya/turbowarp-named-data": "0.1.0",
62
62
  "@kubohiroya/vite-plugin-turbowarp-extension": "0.3.0",
63
63
  "@types/node": "^24.13.3",
64
64
  "eslint": "^9.39.5",
@@ -0,0 +1,15 @@
1
+ {
2
+ "pathSegmentType": {
3
+ "kind": "discriminatedUnion",
4
+ "variants": [
5
+ {"kind": "key", "valueType": "string"},
6
+ {"kind": "index", "valueType": "nonNegativeInteger"}
7
+ ]
8
+ },
9
+ "dataReferenceType": {
10
+ "kind": "named",
11
+ "scope": "target",
12
+ "lifetime": "untilProjectStop",
13
+ "valueType": "jsonValue"
14
+ }
15
+ }
@@ -1,103 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://raw.githubusercontent.com/kubohiroya/turbowarp-structured-data/main/schemas/extension-manifest.schema.json",
4
- "title": "TurboWarp Structured Data Compiler Manifest",
5
- "type": "object",
6
- "additionalProperties": false,
7
- "required": ["formatVersion", "id", "pathSegmentType", "dataReferenceType", "blocks"],
8
- "properties": {
9
- "formatVersion": {"const": 3},
10
- "id": {"type": "string", "pattern": "^[a-z0-9]+$"},
11
- "pathSegmentType": {
12
- "type": "object",
13
- "additionalProperties": false,
14
- "required": ["kind", "variants"],
15
- "properties": {
16
- "kind": {"const": "discriminatedUnion"},
17
- "variants": {
18
- "type": "array",
19
- "prefixItems": [
20
- {"$ref": "#/$defs/keyPathSegment"},
21
- {"$ref": "#/$defs/indexPathSegment"}
22
- ],
23
- "items": false,
24
- "minItems": 2,
25
- "maxItems": 2
26
- }
27
- }
28
- },
29
- "dataReferenceType": {
30
- "type": "object",
31
- "additionalProperties": false,
32
- "required": ["kind", "scope", "lifetime", "valueType"],
33
- "properties": {
34
- "kind": {"const": "named"},
35
- "scope": {"const": "target"},
36
- "lifetime": {"const": "untilProjectStop"},
37
- "valueType": {"const": "jsonValue"}
38
- }
39
- },
40
- "blocks": {
41
- "type": "array",
42
- "minItems": 1,
43
- "items": {"$ref": "#/$defs/block"}
44
- }
45
- },
46
- "$defs": {
47
- "keyPathSegment": {
48
- "type": "object",
49
- "additionalProperties": false,
50
- "required": ["kind", "valueType"],
51
- "properties": {"kind": {"const": "key"}, "valueType": {"const": "string"}}
52
- },
53
- "indexPathSegment": {
54
- "type": "object",
55
- "additionalProperties": false,
56
- "required": ["kind", "valueType"],
57
- "properties": {"kind": {"const": "index"}, "valueType": {"const": "nonNegativeInteger"}}
58
- },
59
- "argument": {
60
- "type": "object",
61
- "additionalProperties": false,
62
- "required": ["id", "type"],
63
- "properties": {
64
- "id": {"type": "string", "minLength": 1},
65
- "type": {"enum": ["STRING", "NUMBER", "BOOLEAN"]},
66
- "normalizesTo": {"const": "pathSegments"},
67
- "staticLiteral": {"type": "boolean"},
68
- "minimum": {"type": "number"},
69
- "maximum": {"type": "number"}
70
- }
71
- },
72
- "server": {
73
- "type": "object",
74
- "additionalProperties": false,
75
- "required": ["supported", "irOperation"],
76
- "properties": {
77
- "supported": {"type": "boolean"},
78
- "irOperation": {"type": "string", "pattern": "^structuredData\\.[A-Za-z][A-Za-z0-9]*$"}
79
- }
80
- },
81
- "block": {
82
- "type": "object",
83
- "additionalProperties": false,
84
- "required": ["opcode", "blockType", "arguments", "resultType", "effect", "immutable", "errors", "server"],
85
- "properties": {
86
- "opcode": {"type": "string", "minLength": 1},
87
- "blockType": {"enum": ["COMMAND", "REPORTER", "BOOLEAN", "LOOP"]},
88
- "arguments": {"type": "array", "items": {"$ref": "#/$defs/argument"}},
89
- "resultType": {"enum": ["json", "jsonText", "yamlText", "boolean", "number", "string", "void"]},
90
- "effect": {"enum": ["pure", "immutable", "state", "control"]},
91
- "immutable": {"type": "boolean"},
92
- "errors": {
93
- "type": "array",
94
- "uniqueItems": true,
95
- "items": {
96
- "enum": ["INVALID_JSON", "INVALID_YAML", "INVALID_NAME", "DATA_NOT_FOUND", "INVALID_PATH", "PATH_NOT_FOUND", "TYPE_MISMATCH", "INDEX_OUT_OF_RANGE", "ITERATION_LIMIT_EXCEEDED", "ITERATION_CONTEXT_REQUIRED", "PARSE_LIMIT_EXCEEDED"]
97
- }
98
- },
99
- "server": {"$ref": "#/$defs/server"}
100
- }
101
- }
102
- }
103
- }
@@ -1,176 +0,0 @@
1
- import type {Plugin} from 'vite';
2
-
3
- export const EXTENSION_MANIFEST_FORMAT_VERSION = 3 as const;
4
-
5
- export interface ExtensionManifestArgument {
6
- id: string;
7
- type: string;
8
- normalizesTo?: 'pathSegments';
9
- staticLiteral?: boolean;
10
- minimum?: number;
11
- maximum?: number;
12
- }
13
-
14
- export interface ExtensionManifestBlock {
15
- opcode: string;
16
- blockType: string;
17
- arguments: ExtensionManifestArgument[];
18
- resultType: string;
19
- effect: 'pure' | 'immutable' | 'state' | 'control';
20
- immutable: boolean;
21
- errors: string[];
22
- server: {supported: boolean; irOperation: string};
23
- }
24
-
25
- export interface ExtensionManifest {
26
- formatVersion: typeof EXTENSION_MANIFEST_FORMAT_VERSION;
27
- id: string;
28
- pathSegmentType: {
29
- kind: 'discriminatedUnion';
30
- variants: Array<{kind: 'key' | 'index'; valueType: 'string' | 'nonNegativeInteger'}>;
31
- };
32
- dataReferenceType: {
33
- kind: 'named';
34
- scope: 'target';
35
- lifetime: 'untilProjectStop';
36
- valueType: 'jsonValue';
37
- };
38
- blocks: ExtensionManifestBlock[];
39
- }
40
-
41
- export interface ExtensionManifestPluginOptions {
42
- id: string;
43
- definitions: unknown;
44
- fileName?: string;
45
- }
46
-
47
- export function createExtensionManifest(id: string, definitions: unknown): ExtensionManifest {
48
- if (!/^[a-z0-9]+$/.test(id)) {
49
- throw new TypeError('Extension manifest ID must contain only lowercase letters and numbers.');
50
- }
51
- const source = requireRecord(definitions, 'Block definitions');
52
- if (!Array.isArray(source.blocks)) {
53
- throw new TypeError('Block definitions must contain a blocks array.');
54
- }
55
- const seenOpcodes = new Set<string>();
56
- const blocks = source.blocks.map((block, index) => {
57
- const normalized = normalizeBlock(block, index);
58
- if (seenOpcodes.has(normalized.opcode)) {
59
- throw new TypeError(`Duplicate block opcode: ${normalized.opcode}`);
60
- }
61
- seenOpcodes.add(normalized.opcode);
62
- return normalized;
63
- });
64
- return {
65
- formatVersion: EXTENSION_MANIFEST_FORMAT_VERSION,
66
- id,
67
- pathSegmentType: {
68
- kind: 'discriminatedUnion',
69
- variants: [
70
- {kind: 'key', valueType: 'string'},
71
- {kind: 'index', valueType: 'nonNegativeInteger'}
72
- ]
73
- },
74
- dataReferenceType: {
75
- kind: 'named',
76
- scope: 'target',
77
- lifetime: 'untilProjectStop',
78
- valueType: 'jsonValue'
79
- },
80
- blocks: blocks.sort((left, right) => compareIds(left.opcode, right.opcode))
81
- };
82
- }
83
-
84
- export function serializeExtensionManifest(id: string, definitions: unknown): string {
85
- return `${JSON.stringify(createExtensionManifest(id, definitions), null, 2)}\n`;
86
- }
87
-
88
- export function extensionManifestPlugin(options: ExtensionManifestPluginOptions): Plugin {
89
- return {
90
- name: 'extension-api-manifest',
91
- apply: 'build',
92
- enforce: 'post',
93
- generateBundle() {
94
- this.emitFile({
95
- type: 'asset',
96
- fileName: options.fileName ?? 'extension-manifest.json',
97
- source: serializeExtensionManifest(options.id, options.definitions)
98
- });
99
- }
100
- };
101
- }
102
-
103
- function normalizeBlock(value: unknown, index: number): ExtensionManifestBlock {
104
- const block = requireRecord(value, `Block at index ${index}`);
105
- const opcode = requireNonEmptyString(block.opcode, `Block at index ${index} opcode`);
106
- const argumentRecord = requireRecord(block.arguments ?? {}, `Block ${opcode} arguments`);
107
- const argumentsList = Object.entries(argumentRecord).map(([id, value]) => {
108
- const argument = requireRecord(value, `Block ${opcode} argument ${id}`);
109
- const result: ExtensionManifestArgument = {
110
- id: requireNonEmptyString(id, `Block ${opcode} argument ID`),
111
- type: requireNonEmptyString(argument.type, `Block ${opcode} argument ${id} type`)
112
- };
113
- if (argument.normalizesTo !== undefined) {
114
- if (argument.normalizesTo !== 'pathSegments') {
115
- throw new TypeError(`Block ${opcode} argument ${id} has an unknown normalized type.`);
116
- }
117
- result.normalizesTo = argument.normalizesTo;
118
- }
119
- if (argument.staticLiteral !== undefined) result.staticLiteral = requireBoolean(argument.staticLiteral, `${opcode}.${id}.staticLiteral`);
120
- if (argument.minimum !== undefined) result.minimum = requireNumber(argument.minimum, `${opcode}.${id}.minimum`);
121
- if (argument.maximum !== undefined) result.maximum = requireNumber(argument.maximum, `${opcode}.${id}.maximum`);
122
- return result;
123
- });
124
- const server = requireRecord(block.server, `Block ${opcode} server contract`);
125
- const effect = block.effect;
126
- if (
127
- effect !== 'pure' &&
128
- effect !== 'immutable' &&
129
- effect !== 'state' &&
130
- effect !== 'control'
131
- ) {
132
- throw new TypeError(`Block ${opcode} must define a supported effect.`);
133
- }
134
- if (!Array.isArray(block.errors) || !block.errors.every((code) => typeof code === 'string')) {
135
- throw new TypeError(`Block ${opcode} errors must be an array of strings.`);
136
- }
137
- return {
138
- opcode,
139
- blockType: requireNonEmptyString(block.blockType, `Block ${opcode} blockType`),
140
- arguments: argumentsList.sort((left, right) => compareIds(left.id, right.id)),
141
- resultType: requireNonEmptyString(block.resultType, `Block ${opcode} resultType`),
142
- effect,
143
- immutable: requireBoolean(block.immutable, `Block ${opcode} immutable`),
144
- errors: [...block.errors],
145
- server: {
146
- supported: requireBoolean(server.supported, `Block ${opcode} server.supported`),
147
- irOperation: requireNonEmptyString(server.irOperation, `Block ${opcode} server.irOperation`)
148
- }
149
- };
150
- }
151
-
152
- function requireRecord(value: unknown, label: string): Record<string, unknown> {
153
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
154
- throw new TypeError(`${label} must be an object.`);
155
- }
156
- return value as Record<string, unknown>;
157
- }
158
-
159
- function requireNonEmptyString(value: unknown, label: string): string {
160
- if (typeof value !== 'string' || value.length === 0) throw new TypeError(`${label} must be a non-empty string.`);
161
- return value;
162
- }
163
-
164
- function requireBoolean(value: unknown, label: string): boolean {
165
- if (typeof value !== 'boolean') throw new TypeError(`${label} must be a boolean.`);
166
- return value;
167
- }
168
-
169
- function requireNumber(value: unknown, label: string): number {
170
- if (typeof value !== 'number' || !Number.isFinite(value)) throw new TypeError(`${label} must be a finite number.`);
171
- return value;
172
- }
173
-
174
- function compareIds(left: string, right: string): number {
175
- return left < right ? -1 : left > right ? 1 : 0;
176
- }