@kubohiroya/turbowarp-structured-data 0.6.3 → 0.7.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.ja.md +36 -0
- package/README.md +49 -7
- package/dist/structured-data.js +26 -16
- package/docs/.nojekyll +0 -0
- package/docs/assets/github-mark.svg +1 -0
- package/docs/assets/site.css +847 -0
- package/docs/index.html +577 -21
- package/docs/ja/index.html +549 -0
- package/lib-module/src/bundle-module.d.ts +7 -0
- package/lib-module/src/bundle-module.js +1384 -0
- package/lib-module/src/codecs.d.ts +10 -0
- package/lib-module/src/config.d.ts +25 -0
- package/lib-module/src/core.d.ts +34 -0
- package/lib-module/src/editor-schema.d.ts +2 -0
- package/lib-module/src/extension.d.ts +74 -0
- package/lib-module/src/fixed-input-editors.d.ts +9 -0
- package/lib-module/src/fixed-input-schema.d.ts +3 -0
- package/lib-module/src/runtime-api.d.ts +41 -0
- package/lib-module/src/source-editor.d.ts +8 -0
- package/lib-module/src/structured-provider.d.ts +33 -0
- package/package.json +15 -8
- package/src/bundle-module.ts +23 -0
- package/src/extension.ts +17 -15
- package/src/globals.d.ts +7 -36
- package/src/index.ts +2 -2
- package/src/runtime-api.ts +43 -0
- package/templates/README.ja.md +36 -0
- package/templates/README.md +48 -6
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type JsonValue } from './core.js';
|
|
2
|
+
export declare const PARSE_LIMITS: {
|
|
3
|
+
readonly maxInputBytes: number;
|
|
4
|
+
readonly maxDepth: 64;
|
|
5
|
+
readonly maxNodes: 50000;
|
|
6
|
+
};
|
|
7
|
+
export declare function parseJsonDocument(source: string): JsonValue;
|
|
8
|
+
export declare function parseYamlDocument(source: string): JsonValue;
|
|
9
|
+
export declare function renderJsonDocument(value: JsonValue): string;
|
|
10
|
+
export declare function renderYamlDocument(value: JsonValue): string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare const STRUCTURED_DATA_MVP_DEFAULT = false;
|
|
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;
|
|
9
|
+
export declare function isStructuredDataMvpEnabled(source?: {
|
|
10
|
+
STRUCTURED_DATA_MVP?: unknown;
|
|
11
|
+
}): boolean;
|
|
12
|
+
export declare function isNamedDataRegistryMvpEnabled(source?: {
|
|
13
|
+
NAMED_DATA_REGISTRY_MVP?: unknown;
|
|
14
|
+
}): boolean;
|
|
15
|
+
export declare const extensionConfig: {
|
|
16
|
+
readonly id: "kubohiroyastructureddata";
|
|
17
|
+
readonly slug: "structured-data";
|
|
18
|
+
readonly name: "Structured Data";
|
|
19
|
+
readonly description: "Named structured data with JSON/YAML codecs for TurboWarp and server compilation.";
|
|
20
|
+
readonly author: "Hiroya Kubo";
|
|
21
|
+
readonly license: "MPL-2.0";
|
|
22
|
+
readonly unsandboxed: true;
|
|
23
|
+
readonly docsURI: "https://kubohiroya.github.io/turbowarp-structured-data/";
|
|
24
|
+
readonly blockIconURI: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OCA0OCI+PHJlY3QgeD0iNCIgeT0iOCIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE0IiByeD0iMyIgZmlsbD0iIzRDOTdGRiIvPjxyZWN0IHg9IjI2IiB5PSI4IiB3aWR0aD0iMTgiIGhlaWdodD0iMTQiIHJ4PSIzIiBmaWxsPSIjNTlDMDU5Ii8+PHJlY3QgeD0iMTUiIHk9IjI2IiB3aWR0aD0iMTgiIGhlaWdodD0iMTQiIHJ4PSIzIiBmaWxsPSIjRkZBQjE5Ii8+PC9zdmc+";
|
|
25
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare const STRUCTURED_DATA_ERROR_CODES: readonly ["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"];
|
|
2
|
+
export type StructuredDataErrorCode = (typeof STRUCTURED_DATA_ERROR_CODES)[number];
|
|
3
|
+
export type JsonPrimitive = null | boolean | number | string;
|
|
4
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
5
|
+
[key: string]: JsonValue;
|
|
6
|
+
};
|
|
7
|
+
export type PathSegment = {
|
|
8
|
+
kind: 'key';
|
|
9
|
+
value: string;
|
|
10
|
+
} | {
|
|
11
|
+
kind: 'index';
|
|
12
|
+
value: number;
|
|
13
|
+
};
|
|
14
|
+
export declare class StructuredDataError extends Error {
|
|
15
|
+
readonly code: StructuredDataErrorCode;
|
|
16
|
+
constructor(code: StructuredDataErrorCode, message: string);
|
|
17
|
+
}
|
|
18
|
+
export declare function parseJson(text: unknown): JsonValue;
|
|
19
|
+
export declare function isValidJson(text: unknown): boolean;
|
|
20
|
+
export declare function normalizeJson(text: unknown): string;
|
|
21
|
+
export declare function stringifyJson(value: JsonValue): string;
|
|
22
|
+
export declare function parsePath(input: unknown): PathSegment[];
|
|
23
|
+
export declare function getAtPath(root: JsonValue, segments: readonly PathSegment[]): JsonValue;
|
|
24
|
+
export declare function hasAtPath(root: JsonValue, segments: readonly PathSegment[]): boolean;
|
|
25
|
+
export declare function setAtPath(root: JsonValue, segments: readonly PathSegment[], replacement: JsonValue): JsonValue;
|
|
26
|
+
export declare function deleteAtPath(root: JsonValue, segments: readonly PathSegment[]): JsonValue;
|
|
27
|
+
export declare function keysAtPath(root: JsonValue, segments: readonly PathSegment[]): string[];
|
|
28
|
+
export declare function lengthAtPath(root: JsonValue, segments: readonly PathSegment[]): number;
|
|
29
|
+
export interface IterationEntry {
|
|
30
|
+
key: string;
|
|
31
|
+
value: JsonValue;
|
|
32
|
+
}
|
|
33
|
+
export declare function iterationEntries(root: JsonValue, segments: readonly PathSegment[], maximum: number): IterationEntry[];
|
|
34
|
+
export declare function compareUnicodeCodePoints(left: string, right: string): number;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ScratchApi, TurboWarpExtension, ScratchBlockUtility } from './runtime-api.js';
|
|
2
|
+
import { type NamedDataRegistryService } from '@kubohiroya/turbowarp-named-data/composition';
|
|
3
|
+
import { StructuredDataNamedDataProvider } from './structured-provider.js';
|
|
4
|
+
interface NamedPathArguments {
|
|
5
|
+
NAME: unknown;
|
|
6
|
+
PATH: unknown;
|
|
7
|
+
}
|
|
8
|
+
export declare class StructuredDataExtension implements TurboWarpExtension {
|
|
9
|
+
private readonly scratch;
|
|
10
|
+
private readonly enabled;
|
|
11
|
+
private disposed;
|
|
12
|
+
private readonly releaseRuntimeListeners;
|
|
13
|
+
private readonly unregisterSourceEditors;
|
|
14
|
+
private readonly unregisterFixedInputEditors;
|
|
15
|
+
private registries;
|
|
16
|
+
private iterationStacks;
|
|
17
|
+
private nextRevision;
|
|
18
|
+
private readonly namedDataProvider?;
|
|
19
|
+
private readonly namedDataRegistry?;
|
|
20
|
+
private readonly namedDataRegistration?;
|
|
21
|
+
private readonly unbindNamedDataRegistryLifecycle?;
|
|
22
|
+
constructor(scratch: ScratchApi, enabled?: boolean, namedDataEnabled?: boolean);
|
|
23
|
+
getNamedDataProvider(): StructuredDataNamedDataProvider | undefined;
|
|
24
|
+
getNamedDataRegistry(): NamedDataRegistryService | undefined;
|
|
25
|
+
dispose(): Promise<void>;
|
|
26
|
+
getInfo(): Record<string, unknown>;
|
|
27
|
+
parseJson(args: {
|
|
28
|
+
TEXT: unknown;
|
|
29
|
+
NAME: unknown;
|
|
30
|
+
}, util: ScratchBlockUtility): void;
|
|
31
|
+
parseYaml(args: {
|
|
32
|
+
TEXT: unknown;
|
|
33
|
+
NAME: unknown;
|
|
34
|
+
}, util: ScratchBlockUtility): void;
|
|
35
|
+
toJson(args: {
|
|
36
|
+
NAME: unknown;
|
|
37
|
+
}, util: ScratchBlockUtility): string;
|
|
38
|
+
toYaml(args: {
|
|
39
|
+
NAME: unknown;
|
|
40
|
+
}, util: ScratchBlockUtility): string;
|
|
41
|
+
hasStructuredData(args: {
|
|
42
|
+
NAME: unknown;
|
|
43
|
+
}, util: ScratchBlockUtility): boolean;
|
|
44
|
+
deleteStructuredData(args: {
|
|
45
|
+
NAME: unknown;
|
|
46
|
+
}, util: ScratchBlockUtility): void;
|
|
47
|
+
getJsonAtPath(args: NamedPathArguments, util: ScratchBlockUtility): string;
|
|
48
|
+
hasPath(args: NamedPathArguments, util: ScratchBlockUtility): boolean;
|
|
49
|
+
setJsonAtPath(args: NamedPathArguments & {
|
|
50
|
+
VALUE: unknown;
|
|
51
|
+
}, util: ScratchBlockUtility): void;
|
|
52
|
+
deleteAtPath(args: NamedPathArguments, util: ScratchBlockUtility): void;
|
|
53
|
+
keysAtPath(args: NamedPathArguments, util: ScratchBlockUtility): string;
|
|
54
|
+
lengthAtPath(args: NamedPathArguments, util: ScratchBlockUtility): number;
|
|
55
|
+
forEachAtPath(args: NamedPathArguments & {
|
|
56
|
+
MAX: unknown;
|
|
57
|
+
}, util: ScratchBlockUtility): void;
|
|
58
|
+
currentKey(_args: Record<string, never>, util: ScratchBlockUtility): string;
|
|
59
|
+
currentIndex(_args: Record<string, never>, util: ScratchBlockUtility): number;
|
|
60
|
+
currentValueJson(_args: Record<string, never>, util: ScratchBlockUtility): string;
|
|
61
|
+
private store;
|
|
62
|
+
private requireData;
|
|
63
|
+
private requireDataByName;
|
|
64
|
+
private registryFor;
|
|
65
|
+
private setBinding;
|
|
66
|
+
private name;
|
|
67
|
+
private dataNotFound;
|
|
68
|
+
private currentIteration;
|
|
69
|
+
private stackFor;
|
|
70
|
+
private path;
|
|
71
|
+
private clearRuntimeState;
|
|
72
|
+
private toScratchBlock;
|
|
73
|
+
}
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FixedInputEditorOptions } from '@kubohiroya/turbowarp-extension-data-editor';
|
|
2
|
+
export declare const fixedInputEditorDefinitions: readonly FixedInputEditorOptions[];
|
|
3
|
+
interface RuntimeLifetime {
|
|
4
|
+
on?: (event: string, listener: () => void) => void;
|
|
5
|
+
off?: (event: string, listener: () => void) => void;
|
|
6
|
+
}
|
|
7
|
+
/** Register metadata only; the GUI startup capability owns editor activation. */
|
|
8
|
+
export declare function registerFixedInputEditors(scratch: unknown, definitions: readonly FixedInputEditorOptions[], runtimeOverride?: RuntimeLifetime): () => void;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface TurboWarpExtension {
|
|
2
|
+
getInfo(): Record<string, unknown>;
|
|
3
|
+
}
|
|
4
|
+
export interface ScratchTranslate {
|
|
5
|
+
(text: string): string;
|
|
6
|
+
(message: {
|
|
7
|
+
default: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
}, placeholders?: Record<string, string | number>): string;
|
|
10
|
+
}
|
|
11
|
+
export interface ScratchBlockUtility {
|
|
12
|
+
thread: object;
|
|
13
|
+
target: object;
|
|
14
|
+
stackFrame: Record<string, unknown>;
|
|
15
|
+
startBranch(branchNumber: number, isLoop: boolean): void;
|
|
16
|
+
}
|
|
17
|
+
export interface ScratchApi {
|
|
18
|
+
extensions: {
|
|
19
|
+
unsandboxed: boolean;
|
|
20
|
+
register(extension: TurboWarpExtension): void;
|
|
21
|
+
};
|
|
22
|
+
BlockType: Record<'COMMAND' | 'REPORTER' | 'BOOLEAN' | 'HAT' | 'LOOP', string>;
|
|
23
|
+
ArgumentType: Record<'STRING' | 'NUMBER' | 'BOOLEAN', string>;
|
|
24
|
+
Cast: {
|
|
25
|
+
toString(value: unknown): string;
|
|
26
|
+
toNumber(value: unknown): number;
|
|
27
|
+
toBoolean(value: unknown): boolean;
|
|
28
|
+
};
|
|
29
|
+
translate: ScratchTranslate;
|
|
30
|
+
vm?: {
|
|
31
|
+
runtime?: {
|
|
32
|
+
on(event: string, listener: () => void): void;
|
|
33
|
+
off?(event: string, listener: () => void): void;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export interface ExtensionModuleMetadata {
|
|
38
|
+
readonly formatVersion: 1;
|
|
39
|
+
readonly id: string;
|
|
40
|
+
readonly unsandboxed: 'required' | 'optional' | 'not-needed';
|
|
41
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SchemaBundle, SchemaDocument, SourceBinding, SourceEditorOptions } from '@kubohiroya/turbowarp-extension-data-editor/schema';
|
|
2
|
+
export declare function canonicalBundle(ownerId: string, contractId: string, document: SchemaDocument, ownerSource: {
|
|
3
|
+
path: string;
|
|
4
|
+
symbol: string;
|
|
5
|
+
}): SchemaBundle;
|
|
6
|
+
export declare function sourceBinding(argument: string, bundle: SchemaBundle, read: (source: string) => unknown, check?: (source: string) => unknown, format?: 'json' | 'yaml', uiSchema?: SourceBinding['variants'][number]['uiSchema']): SourceBinding;
|
|
7
|
+
/** The optional capability keeps legacy hosts and the startup flag safely inactive. */
|
|
8
|
+
export declare function registerSourceEditors(scratch: unknown, definitions: readonly SourceEditorOptions[]): () => void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { JsonValue } from './core.js';
|
|
2
|
+
import { type NamedDataBody, type NamedDataMetadata, type NamedDataProvider, type NamedDataReference, type NamedDataSchemaRef, type NamedDataRepresentation, type NamedDataResolveContext } from '@kubohiroya/turbowarp-named-data/composition';
|
|
3
|
+
export declare const STRUCTURED_DATA_NAMESPACE = "structured";
|
|
4
|
+
export declare const STRUCTURED_BODY_MAX_BYTES: number;
|
|
5
|
+
export interface StructuredDataBinding {
|
|
6
|
+
value: JsonValue;
|
|
7
|
+
revision: number;
|
|
8
|
+
nativeRepresentation: 'json' | 'yaml';
|
|
9
|
+
schemaRef?: NamedDataSchemaRef;
|
|
10
|
+
}
|
|
11
|
+
export interface StructuredDataBindingStore {
|
|
12
|
+
get(target: object, name: string): StructuredDataBinding | undefined;
|
|
13
|
+
}
|
|
14
|
+
export declare class StructuredDataNamedDataProvider implements NamedDataProvider {
|
|
15
|
+
private readonly store;
|
|
16
|
+
private readonly maxBodyBytes;
|
|
17
|
+
private readonly schemaRefEnabled;
|
|
18
|
+
readonly namespace = "structured";
|
|
19
|
+
readonly kind: "structured";
|
|
20
|
+
private readonly openBodies;
|
|
21
|
+
private released;
|
|
22
|
+
constructor(store: StructuredDataBindingStore, maxBodyBytes?: number, schemaRefEnabled?: boolean);
|
|
23
|
+
canResolve(reference: NamedDataReference, representation: NamedDataRepresentation): boolean;
|
|
24
|
+
stat(reference: NamedDataReference, representation: NamedDataRepresentation, context: NamedDataResolveContext): NamedDataMetadata;
|
|
25
|
+
openBody(reference: NamedDataReference, representation: NamedDataRepresentation, context: NamedDataResolveContext): NamedDataBody;
|
|
26
|
+
release(): void;
|
|
27
|
+
clearSession(): void;
|
|
28
|
+
private resolve;
|
|
29
|
+
private validateReference;
|
|
30
|
+
private render;
|
|
31
|
+
private metadata;
|
|
32
|
+
private throwIfAborted;
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubohiroya/turbowarp-structured-data",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"README.ja.md",
|
|
34
34
|
"THIRD_PARTY_NOTICES.md",
|
|
35
35
|
"LICENSE",
|
|
36
|
-
"lib-editor/"
|
|
36
|
+
"lib-editor/",
|
|
37
|
+
"lib-module/"
|
|
37
38
|
],
|
|
38
39
|
"publishConfig": {
|
|
39
40
|
"access": "public",
|
|
@@ -46,17 +47,19 @@
|
|
|
46
47
|
"scripts": {
|
|
47
48
|
"dev": "vite build --watch",
|
|
48
49
|
"typecheck": "tsc --noEmit",
|
|
49
|
-
"build": "vite build && pnpm run build:editor",
|
|
50
|
-
"lint": "eslint src tests scripts vite.config.ts vite.editor.config.ts eslint.config.ts",
|
|
50
|
+
"build": "vite build && pnpm run build:editor && pnpm run build:module",
|
|
51
|
+
"lint": "eslint src tests scripts vite.config.ts vite.editor.config.ts eslint.config.ts vite.module.config.ts",
|
|
51
52
|
"test": "vitest run",
|
|
52
53
|
"docs": "node scripts/generate-readme.ts && node scripts/generate-fixed-input-docs.mjs",
|
|
53
54
|
"docs:check": "node scripts/generate-readme.ts --check && node scripts/generate-fixed-input-docs.mjs --check",
|
|
54
55
|
"template:readme": "node scripts/apply-readme-template.ts",
|
|
55
56
|
"repo:check": "node scripts/check-repo.ts",
|
|
56
57
|
"pack:check": "npm pack --dry-run --ignore-scripts",
|
|
57
|
-
"check": "pnpm run typecheck && pnpm run lint && pnpm run test && pnpm run docs:check && pnpm run check:dist && pnpm run repo:check && pnpm run pack:check",
|
|
58
|
+
"check": "pnpm run typecheck && pnpm run lint && pnpm run test && pnpm run docs:check && pnpm run check:dist && pnpm run check:module && pnpm run repo:check && pnpm run pack:check",
|
|
58
59
|
"check:dist": "pnpm run build && node scripts/check-dist.ts",
|
|
59
|
-
"build:editor": "tsc -p tsconfig.editor.json && vite build --config vite.editor.config.ts"
|
|
60
|
+
"build:editor": "tsc -p tsconfig.editor.json && vite build --config vite.editor.config.ts",
|
|
61
|
+
"build:module": "tsc -p tsconfig.module.json && vite build --config vite.module.config.ts",
|
|
62
|
+
"check:module": "node scripts/check-bundle-module.ts"
|
|
60
63
|
},
|
|
61
64
|
"devDependencies": {
|
|
62
65
|
"@eslint/js": "^9.39.5",
|
|
@@ -80,9 +83,9 @@
|
|
|
80
83
|
"peerDependencies": {
|
|
81
84
|
"@kubohiroya/turbowarp-named-data": ">=0.4.0 <1"
|
|
82
85
|
},
|
|
83
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "e66395b1c2e69b27fafecae7c5afdc3c73e35232",
|
|
84
87
|
"x-turbowarp-source": {
|
|
85
|
-
"revision": "
|
|
88
|
+
"revision": "e66395b1c2e69b27fafecae7c5afdc3c73e35232",
|
|
86
89
|
"revisionRole": "base-commit",
|
|
87
90
|
"sourceState": "local-release-overlay"
|
|
88
91
|
},
|
|
@@ -91,6 +94,10 @@
|
|
|
91
94
|
"./editor-metadata": {
|
|
92
95
|
"types": "./lib-editor/src/editor-metadata.d.ts",
|
|
93
96
|
"import": "./lib-editor/src/editor-metadata.js"
|
|
97
|
+
},
|
|
98
|
+
"./bundle-module": {
|
|
99
|
+
"types": "./lib-module/src/bundle-module.d.ts",
|
|
100
|
+
"import": "./lib-module/src/bundle-module.js"
|
|
94
101
|
}
|
|
95
102
|
}
|
|
96
103
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {extensionConfig} from './config.js';
|
|
2
|
+
import {StructuredDataExtension} from './extension.js';
|
|
3
|
+
import type {ExtensionModuleMetadata, ScratchApi} from './runtime-api.js';
|
|
4
|
+
|
|
5
|
+
export type {
|
|
6
|
+
TurboWarpExtension,
|
|
7
|
+
ScratchTranslate,
|
|
8
|
+
ScratchBlockUtility,
|
|
9
|
+
ScratchApi,
|
|
10
|
+
ExtensionModuleMetadata
|
|
11
|
+
} from './runtime-api.js';
|
|
12
|
+
|
|
13
|
+
/** Static contract: importing this entry never creates or registers an extension. */
|
|
14
|
+
export const bundleModuleMetadata: ExtensionModuleMetadata = Object.freeze({
|
|
15
|
+
formatVersion: 1,
|
|
16
|
+
id: extensionConfig.id,
|
|
17
|
+
unsandboxed: extensionConfig.unsandboxed ? 'required' : 'not-needed'
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
/** The caller supplies the member host and owns registration and lifetime. */
|
|
21
|
+
export function createExtension(scratch: ScratchApi): StructuredDataExtension {
|
|
22
|
+
return new StructuredDataExtension(scratch);
|
|
23
|
+
}
|
package/src/extension.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type {ScratchApi, TurboWarpExtension, ScratchBlockUtility} from './runtime-api.js';
|
|
1
2
|
import {fixedInputEditorDefinitions, registerFixedInputEditors} from './fixed-input-editors.js';
|
|
2
3
|
import {sourceEditorDefinitions} from './editor-schema.js';
|
|
3
4
|
import {registerSourceEditors} from './source-editor.js';
|
|
4
|
-
import definitions from './block-definitions.json';
|
|
5
|
+
import definitions from './block-definitions.json' with {type: 'json'};
|
|
5
6
|
import {
|
|
6
7
|
bindNamedDataRegistryLifecycle,
|
|
7
8
|
installNamedDataRegistry,
|
|
@@ -88,12 +89,13 @@ export class StructuredDataExtension implements TurboWarpExtension {
|
|
|
88
89
|
private readonly unbindNamedDataRegistryLifecycle?: () => void;
|
|
89
90
|
|
|
90
91
|
public constructor(
|
|
92
|
+
private readonly scratch: ScratchApi,
|
|
91
93
|
enabled = isStructuredDataMvpEnabled(),
|
|
92
94
|
namedDataEnabled = isNamedDataRegistryMvpEnabled()
|
|
93
95
|
) {
|
|
94
96
|
this.enabled = enabled;
|
|
95
|
-
this.unregisterSourceEditors = registerSourceEditors(
|
|
96
|
-
const runtime =
|
|
97
|
+
this.unregisterSourceEditors = registerSourceEditors(this.scratch, enabled ? sourceEditorDefinitions : []);
|
|
98
|
+
const runtime = this.scratch.vm?.runtime;
|
|
97
99
|
const onDispose = () => {void this.dispose();};
|
|
98
100
|
const onStop = () => this.clearRuntimeState();
|
|
99
101
|
this.releaseRuntimeListeners = () => {
|
|
@@ -102,13 +104,13 @@ export class StructuredDataExtension implements TurboWarpExtension {
|
|
|
102
104
|
};
|
|
103
105
|
let unregisterFixed = () => {};
|
|
104
106
|
try {
|
|
105
|
-
unregisterFixed = registerFixedInputEditors(
|
|
107
|
+
unregisterFixed = registerFixedInputEditors(this.scratch, enabled ? fixedInputEditorDefinitions : []);
|
|
106
108
|
this.unregisterFixedInputEditors = unregisterFixed;
|
|
107
109
|
if (namedDataEnabled) {
|
|
108
110
|
this.namedDataProvider = new StructuredDataNamedDataProvider({
|
|
109
111
|
get: (target, name) => this.registries.get(target)?.get(name)
|
|
110
112
|
});
|
|
111
|
-
const runtime =
|
|
113
|
+
const runtime = this.scratch.vm?.runtime;
|
|
112
114
|
if (runtime) {
|
|
113
115
|
this.namedDataRegistry = installNamedDataRegistry(runtime);
|
|
114
116
|
this.namedDataRegistration = this.namedDataRegistry.registerProvider(
|
|
@@ -154,7 +156,7 @@ export class StructuredDataExtension implements TurboWarpExtension {
|
|
|
154
156
|
public getInfo(): Record<string, unknown> {
|
|
155
157
|
return {
|
|
156
158
|
id: extensionConfig.id,
|
|
157
|
-
name:
|
|
159
|
+
name: this.scratch.translate(definitions.extensionName),
|
|
158
160
|
docsURI: extensionConfig.docsURI,
|
|
159
161
|
blockIconURI: extensionConfig.blockIconURI,
|
|
160
162
|
blocks: this.enabled ? blockDefinitions.map((block) => this.toScratchBlock(block)) : []
|
|
@@ -165,7 +167,7 @@ export class StructuredDataExtension implements TurboWarpExtension {
|
|
|
165
167
|
this.store(
|
|
166
168
|
util.target,
|
|
167
169
|
args.NAME,
|
|
168
|
-
parseJsonDocument(
|
|
170
|
+
parseJsonDocument(this.scratch.Cast.toString(args.TEXT)),
|
|
169
171
|
'json'
|
|
170
172
|
);
|
|
171
173
|
}
|
|
@@ -174,7 +176,7 @@ export class StructuredDataExtension implements TurboWarpExtension {
|
|
|
174
176
|
this.store(
|
|
175
177
|
util.target,
|
|
176
178
|
args.NAME,
|
|
177
|
-
parseYamlDocument(
|
|
179
|
+
parseYamlDocument(this.scratch.Cast.toString(args.TEXT)),
|
|
178
180
|
'yaml'
|
|
179
181
|
);
|
|
180
182
|
}
|
|
@@ -215,7 +217,7 @@ export class StructuredDataExtension implements TurboWarpExtension {
|
|
|
215
217
|
const updated = setAtPath(
|
|
216
218
|
this.requireDataByName(util.target, name),
|
|
217
219
|
this.path(args.PATH),
|
|
218
|
-
parseJsonValue(
|
|
220
|
+
parseJsonValue(this.scratch.Cast.toString(args.VALUE))
|
|
219
221
|
);
|
|
220
222
|
this.setBinding(util.target, name, updated);
|
|
221
223
|
}
|
|
@@ -251,7 +253,7 @@ export class StructuredDataExtension implements TurboWarpExtension {
|
|
|
251
253
|
entries: iterationEntries(
|
|
252
254
|
this.requireData(util.target, args.NAME),
|
|
253
255
|
this.path(args.PATH),
|
|
254
|
-
|
|
256
|
+
this.scratch.Cast.toNumber(args.MAX)
|
|
255
257
|
),
|
|
256
258
|
cursor: 0,
|
|
257
259
|
active: false
|
|
@@ -333,7 +335,7 @@ export class StructuredDataExtension implements TurboWarpExtension {
|
|
|
333
335
|
}
|
|
334
336
|
|
|
335
337
|
private name(value: unknown): string {
|
|
336
|
-
const name =
|
|
338
|
+
const name = this.scratch.Cast.toString(value).trim();
|
|
337
339
|
if (name.length === 0) {
|
|
338
340
|
throw new StructuredDataError('INVALID_NAME', 'Structured data name cannot be empty.');
|
|
339
341
|
}
|
|
@@ -365,7 +367,7 @@ export class StructuredDataExtension implements TurboWarpExtension {
|
|
|
365
367
|
}
|
|
366
368
|
|
|
367
369
|
private path(value: unknown) {
|
|
368
|
-
return parsePath(
|
|
370
|
+
return parsePath(this.scratch.Cast.toString(value));
|
|
369
371
|
}
|
|
370
372
|
|
|
371
373
|
private clearRuntimeState(): void {
|
|
@@ -378,12 +380,12 @@ export class StructuredDataExtension implements TurboWarpExtension {
|
|
|
378
380
|
private toScratchBlock(block: BlockDefinition): Record<string, unknown> {
|
|
379
381
|
return {
|
|
380
382
|
opcode: block.opcode,
|
|
381
|
-
blockType:
|
|
382
|
-
text:
|
|
383
|
+
blockType: this.scratch.BlockType[block.blockType],
|
|
384
|
+
text: this.scratch.translate(block.text),
|
|
383
385
|
arguments: Object.fromEntries(
|
|
384
386
|
Object.entries(block.arguments).map(([name, argument]) => [
|
|
385
387
|
name,
|
|
386
|
-
{type:
|
|
388
|
+
{type: this.scratch.ArgumentType[argument.type], defaultValue: argument.defaultValue}
|
|
387
389
|
])
|
|
388
390
|
)
|
|
389
391
|
};
|
package/src/globals.d.ts
CHANGED
|
@@ -1,38 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
getInfo(): Record<string, unknown>;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
interface ScratchTranslate {
|
|
6
|
-
(text: string): string;
|
|
7
|
-
(message: {default: string; description?: string}, placeholders?: Record<string, string | number>): string;
|
|
8
|
-
}
|
|
1
|
+
import type * as Runtime from './runtime-api.js';
|
|
9
2
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
declare global {
|
|
4
|
+
type TurboWarpExtension = Runtime.TurboWarpExtension;
|
|
5
|
+
type ScratchTranslate = Runtime.ScratchTranslate;
|
|
6
|
+
type ScratchBlockUtility = Runtime.ScratchBlockUtility;
|
|
7
|
+
type ScratchApi = Runtime.ScratchApi;
|
|
8
|
+
const Scratch: ScratchApi;
|
|
15
9
|
}
|
|
16
|
-
|
|
17
|
-
interface ScratchApi {
|
|
18
|
-
extensions: {
|
|
19
|
-
unsandboxed: boolean;
|
|
20
|
-
register(extension: TurboWarpExtension): void;
|
|
21
|
-
};
|
|
22
|
-
BlockType: Record<'COMMAND' | 'REPORTER' | 'BOOLEAN' | 'HAT' | 'LOOP', string>;
|
|
23
|
-
ArgumentType: Record<'STRING' | 'NUMBER' | 'BOOLEAN', string>;
|
|
24
|
-
Cast: {
|
|
25
|
-
toString(value: unknown): string;
|
|
26
|
-
toNumber(value: unknown): number;
|
|
27
|
-
toBoolean(value: unknown): boolean;
|
|
28
|
-
};
|
|
29
|
-
translate: ScratchTranslate;
|
|
30
|
-
vm?: {
|
|
31
|
-
runtime?: {
|
|
32
|
-
on(event: string, listener: () => void): void;
|
|
33
|
-
off?(event: string, listener: () => void): void;
|
|
34
|
-
};
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
declare const Scratch: ScratchApi;
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {extensionConfig} from './config.js';
|
|
2
|
-
import {
|
|
2
|
+
import {createExtension} from './bundle-module.js';
|
|
3
3
|
|
|
4
4
|
if (extensionConfig.unsandboxed && !Scratch.extensions.unsandboxed) {
|
|
5
5
|
throw new Error(`${extensionConfig.name} must run unsandboxed.`);
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
Scratch.extensions.register(
|
|
8
|
+
Scratch.extensions.register(createExtension(Scratch));
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface TurboWarpExtension {
|
|
2
|
+
getInfo(): Record<string, unknown>;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface ScratchTranslate {
|
|
6
|
+
(text: string): string;
|
|
7
|
+
(message: {default: string; description?: string}, placeholders?: Record<string, string | number>): string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ScratchBlockUtility {
|
|
11
|
+
thread: object;
|
|
12
|
+
target: object;
|
|
13
|
+
stackFrame: Record<string, unknown>;
|
|
14
|
+
startBranch(branchNumber: number, isLoop: boolean): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ScratchApi {
|
|
18
|
+
extensions: {
|
|
19
|
+
unsandboxed: boolean;
|
|
20
|
+
register(extension: TurboWarpExtension): void;
|
|
21
|
+
};
|
|
22
|
+
BlockType: Record<'COMMAND' | 'REPORTER' | 'BOOLEAN' | 'HAT' | 'LOOP', string>;
|
|
23
|
+
ArgumentType: Record<'STRING' | 'NUMBER' | 'BOOLEAN', string>;
|
|
24
|
+
Cast: {
|
|
25
|
+
toString(value: unknown): string;
|
|
26
|
+
toNumber(value: unknown): number;
|
|
27
|
+
toBoolean(value: unknown): boolean;
|
|
28
|
+
};
|
|
29
|
+
translate: ScratchTranslate;
|
|
30
|
+
vm?: {
|
|
31
|
+
runtime?: {
|
|
32
|
+
on(event: string, listener: () => void): void;
|
|
33
|
+
off?(event: string, listener: () => void): void;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
export interface ExtensionModuleMetadata {
|
|
40
|
+
readonly formatVersion: 1;
|
|
41
|
+
readonly id: string;
|
|
42
|
+
readonly unsandboxed: 'required' | 'optional' | 'not-needed';
|
|
43
|
+
}
|
package/templates/README.ja.md
CHANGED
|
@@ -144,6 +144,42 @@ renderer境界、`release()`/`releaseAll()`を説明します。
|
|
|
144
144
|
|
|
145
145
|
extension IDまたはopcode変更時はschema-aware migrationを説明し、保存済み識別子が変わった場合にJavaScriptだけの差替えを案内しません。
|
|
146
146
|
|
|
147
|
+
## 統合ビルド用module API
|
|
148
|
+
|
|
149
|
+
このソースのbuildは、単体JSとは別に、統合用のESM入口`./bundle-module`と
|
|
150
|
+
型宣言を`lib-module/`へ生成します。
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import {bundleModuleMetadata, createExtension, type ScratchApi}
|
|
154
|
+
from '@kubohiroya/<package>/bundle-module';
|
|
155
|
+
|
|
156
|
+
export function createMember(memberScratch: ScratchApi) {
|
|
157
|
+
return createExtension(memberScratch);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
import時はglobal Scratch / window / documentを参照せず、拡張の生成・登録を行いません。
|
|
162
|
+
明示的なcreateExtension(memberScratch)ごとに新しいinstanceを返し、後から実行する
|
|
163
|
+
ブロック・編集用補助処理・lifecycle callbackも同じhostを使います。factoryは
|
|
164
|
+
Scratch.extensions.registerを呼びません。単体entryも同じfactoryを使い、登録は1回です。
|
|
165
|
+
|
|
166
|
+
凍結されたbundleModuleMetadataはformatVersion: 1、元のid、unsandboxed条件を
|
|
167
|
+
宣言します。統合hostが生成前に検査し、登録とdisposeを管理します。ID・opcode・
|
|
168
|
+
保存データ形式を変更しません。
|
|
169
|
+
|
|
170
|
+
既存のSTRUCTURED_DATA_MVP・NAMED_DATA_REGISTRY_MVP・Schema参照の起動フラグと既定値を維持します。生成時の任意の編集登録・named-data初期化には注入hostを使います。dispose()をawaitして、このinstanceの登録とlistenerを解除します。StructuredDataExtensionを直接newする内部コードでは、hostを先頭に渡し、その後に従来の任意の機能フラグ引数を渡します。
|
|
171
|
+
|
|
172
|
+
yaml等のruntime依存とそのsubpathは外部importのまま保持します。既存の小さな編集用
|
|
173
|
+
純粋ヘルパーだけを含め、production consumerには編集packageやGUIエンジンを要求しません。
|
|
174
|
+
公開型は既存tsconfigと同じES2022の型ライブラリを使い、生成JSはES2020を対象とします。
|
|
175
|
+
既存exportsを維持して./bundle-moduleを追加し、templateへのruntime依存は追加しません。
|
|
176
|
+
|
|
177
|
+
pnpm run build:moduleで生成し、pnpm run check:moduleは実tarballをproduction依存だけの
|
|
178
|
+
consumerへinstallしてhost分離・単体版との動作一致・公開型・編集エンジンの非混入を検査します。
|
|
179
|
+
sb3-toolchain側の統合buildは[sb3-toolchain #65](https://github.com/kubohiroya/sb3-toolchain/issues/65)の後続です。
|
|
180
|
+
切戻しはこのmodule入口を選択せず従来のclassic distを読み込みます。新しいruntimeフラグや
|
|
181
|
+
保存データの移行は導入しません。
|
|
182
|
+
|
|
147
183
|
## 開発
|
|
148
184
|
|
|
149
185
|
`engines`のNode.jsと、`packageManager`に宣言したpnpmを使用します。
|