@kubohiroya/turbowarp-structured-data 0.6.3 → 0.7.1

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.
@@ -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+PHJlY3QgeD0iNCIgeT0iOCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjMyIiByeD0iNiIgZmlsbD0iIzhBNTVENyIvPjxwYXRoIGQ9Ik0xOSAxNWMtMyAwLTIgNy01IDkgMyAyIDIgOSA1IDkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMjkgMTVjMyAwIDIgNyA1IDktMyAyLTIgOS01IDkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48Y2lyY2xlIGN4PSIyNCIgY3k9IjI0IiByPSIyLjIiIGZpbGw9IiNmZmYiLz48L3N2Zz4=";
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,2 @@
1
+ import type { SourceEditorOptions } from '@kubohiroya/turbowarp-extension-data-editor/schema';
2
+ export declare const sourceEditorDefinitions: readonly SourceEditorOptions[];
@@ -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,3 @@
1
+ import type { SchemaBundle } from '@kubohiroya/turbowarp-extension-data-editor/schema';
2
+ /** The on-disk owner Schema is canonical; this is its single-field projection. */
3
+ export declare const fixedInputSchemaBundle: SchemaBundle;
@@ -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.6.3",
3
+ "version": "0.7.1",
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,9 @@
33
33
  "README.ja.md",
34
34
  "THIRD_PARTY_NOTICES.md",
35
35
  "LICENSE",
36
- "lib-editor/"
36
+ "lib-editor/",
37
+ "lib-module/",
38
+ "assets/block-icon.svg"
37
39
  ],
38
40
  "publishConfig": {
39
41
  "access": "public",
@@ -46,17 +48,19 @@
46
48
  "scripts": {
47
49
  "dev": "vite build --watch",
48
50
  "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",
51
+ "build": "vite build && pnpm run build:editor && pnpm run build:module",
52
+ "lint": "eslint src tests scripts vite.config.ts vite.editor.config.ts eslint.config.ts vite.module.config.ts",
51
53
  "test": "vitest run",
52
54
  "docs": "node scripts/generate-readme.ts && node scripts/generate-fixed-input-docs.mjs",
53
55
  "docs:check": "node scripts/generate-readme.ts --check && node scripts/generate-fixed-input-docs.mjs --check",
54
56
  "template:readme": "node scripts/apply-readme-template.ts",
55
57
  "repo:check": "node scripts/check-repo.ts",
56
58
  "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",
59
+ "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
60
  "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"
61
+ "build:editor": "tsc -p tsconfig.editor.json && vite build --config vite.editor.config.ts",
62
+ "build:module": "tsc -p tsconfig.module.json && vite build --config vite.module.config.ts",
63
+ "check:module": "node scripts/check-bundle-module.ts"
60
64
  },
61
65
  "devDependencies": {
62
66
  "@eslint/js": "^9.39.5",
@@ -80,9 +84,9 @@
80
84
  "peerDependencies": {
81
85
  "@kubohiroya/turbowarp-named-data": ">=0.4.0 <1"
82
86
  },
83
- "gitHead": "870ebcaa4071e0eacb4d36a6d9e98734e51f2361",
87
+ "gitHead": "0cd886abf134b9833f29e7f5119adec70d0e3185",
84
88
  "x-turbowarp-source": {
85
- "revision": "870ebcaa4071e0eacb4d36a6d9e98734e51f2361",
89
+ "revision": "0cd886abf134b9833f29e7f5119adec70d0e3185",
86
90
  "revisionRole": "base-commit",
87
91
  "sourceState": "local-release-overlay"
88
92
  },
@@ -91,6 +95,10 @@
91
95
  "./editor-metadata": {
92
96
  "types": "./lib-editor/src/editor-metadata.d.ts",
93
97
  "import": "./lib-editor/src/editor-metadata.js"
98
+ },
99
+ "./bundle-module": {
100
+ "types": "./lib-module/src/bundle-module.d.ts",
101
+ "import": "./lib-module/src/bundle-module.js"
94
102
  }
95
103
  }
96
104
  }
@@ -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/config.ts CHANGED
@@ -38,5 +38,5 @@ export const extensionConfig = {
38
38
  unsandboxed: true,
39
39
  docsURI: 'https://kubohiroya.github.io/turbowarp-structured-data/',
40
40
  blockIconURI:
41
- 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OCA0OCI+PHJlY3QgeD0iNCIgeT0iOCIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE0IiByeD0iMyIgZmlsbD0iIzRDOTdGRiIvPjxyZWN0IHg9IjI2IiB5PSI4IiB3aWR0aD0iMTgiIGhlaWdodD0iMTQiIHJ4PSIzIiBmaWxsPSIjNTlDMDU5Ii8+PHJlY3QgeD0iMTUiIHk9IjI2IiB3aWR0aD0iMTgiIGhlaWdodD0iMTQiIHJ4PSIzIiBmaWxsPSIjRkZBQjE5Ii8+PC9zdmc+'
41
+ 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OCA0OCI+PHJlY3QgeD0iNCIgeT0iOCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjMyIiByeD0iNiIgZmlsbD0iIzhBNTVENyIvPjxwYXRoIGQ9Ik0xOSAxNWMtMyAwLTIgNy01IDkgMyAyIDIgOSA1IDkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMjkgMTVjMyAwIDIgNyA1IDktMyAyLTIgOS01IDkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48Y2lyY2xlIGN4PSIyNCIgY3k9IjI0IiByPSIyLjIiIGZpbGw9IiNmZmYiLz48L3N2Zz4='
42
42
  } as const;
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(Scratch, enabled ? sourceEditorDefinitions : []);
96
- const runtime = Scratch.vm?.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(Scratch, enabled ? fixedInputEditorDefinitions : []);
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 = Scratch.vm?.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: Scratch.translate(definitions.extensionName),
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(Scratch.Cast.toString(args.TEXT)),
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(Scratch.Cast.toString(args.TEXT)),
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(Scratch.Cast.toString(args.VALUE))
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
- Scratch.Cast.toNumber(args.MAX)
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 = Scratch.Cast.toString(value).trim();
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(Scratch.Cast.toString(value));
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: Scratch.BlockType[block.blockType],
382
- text: Scratch.translate(block.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: Scratch.ArgumentType[argument.type], defaultValue: argument.defaultValue}
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
- interface TurboWarpExtension {
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
- interface ScratchBlockUtility {
11
- thread: object;
12
- target: object;
13
- stackFrame: Record<string, unknown>;
14
- startBranch(branchNumber: number, isLoop: boolean): void;
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 {StructuredDataExtension} from './extension.js';
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(new StructuredDataExtension());
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
+ }
@@ -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を使用します。