@kubohiroya/turbowarp-runtime-adapter 0.2.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/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0
4
+
5
+ - Add target enumeration to the runtime host: `targets()` returns a copy of every loaded target and `spriteTargets()` drops the Stage. An absent target list reads as empty; a non-array one is a runtime fault.
6
+ - Add renderer access: `getRenderer()` returns the attached renderer and throws when there is none, while `requestRedraw()` stays a no-op on a runtime that does not provide it.
7
+ - Add monitor access: `getMonitorBlocks()` and `getMonitorState()` validate the `data_showvariable` / `data_hidevariable` block store and monitor record map before handing them back. `getMonitorState` is invoked with the runtime as its receiver.
8
+ - Add `createBlockSurfaceBuilder`, which builds the repetitive parts of an extension `getInfo()` block list from an injected `Scratch.BlockType` / `Scratch.ArgumentType` pair. It owns record shape, palette visibility, reporter monitor defaults, and duplicate-opcode detection; opcodes, block text, and menu items stay with the caller.
9
+ - Add `coerceScalarBlockValue` for the string/number/boolean coercion a writable scalar block surface needs, with the same injectable `errorCodePrefix` the broadcast port uses.
10
+ - No behavior change to any 0.1.0 export.
11
+
12
+ ## 0.1.0
13
+
14
+ - Initial public package shape for app-neutral TurboWarp runtime host adapters.
15
+ - Add broadcast action port support with exact Stage broadcast lookup, owned-thread waiting, abort, and dispose behavior.
16
+ - Add compatibility-oriented error code prefixing for consumers that need stable app-specific error codes.
package/LICENSE ADDED
@@ -0,0 +1,6 @@
1
+ Mozilla Public License Version 2.0
2
+ ==================================
3
+
4
+ This repository uses the Mozilla Public License Version 2.0.
5
+
6
+ See https://www.mozilla.org/MPL/2.0/ for the full license text.
package/README.ja.md ADDED
@@ -0,0 +1,108 @@
1
+ # turbowarp-runtime-adapter
2
+
3
+ [English](README.md)
4
+
5
+ `@kubohiroya/turbowarp-runtime-adapter` は、unsandboxed extension や packaged TurboWarp app から TurboWarp runtime を操作するための、アプリ非依存の adapter を提供します。
6
+
7
+ この package は、アプリ固有の DSL semantics、block metadata、story/navigation、pose、3D scene graph policy を定義しません。それらは `tm-kamishibai` や `tm-3d-app` などの app package 側に残します。
8
+
9
+ ## 役割
10
+
11
+ - `Scratch.vm.runtime` または明示された runtime object から runtime を解決する。
12
+ - runtime event を disposable listener として購読する。
13
+ - 小さく検証された host adapter 経由で hats を起動する。
14
+ - Stage target、sprite target、renderer、monitor store を読む。
15
+ - Stage に宣言された exact broadcast name 用の汎用 `broadcastMessageAndWait` port を提供する。
16
+ - extension の `getInfo()` block list のうち定型部分を組み立てる。
17
+ - Scratch VM への直接アクセスを狭い interface に閉じ込める。
18
+
19
+ ## runtime surface
20
+
21
+ `createTurboWarpRuntimeHost` は session ごとに 1 つの adapter を返します。raw runtime を持ち回すのではなく
22
+ この adapter を注入してください。Stage しか必要としない consumer は、runtime 全体の形ではなく
23
+ `getStageTarget()` だけに依存できます。
24
+
25
+ | member | 参照先 |
26
+ | --- | --- |
27
+ | `runtime` | 検証済み runtime。この package がまだ所有していない操作向け |
28
+ | `onRuntimeEvent(event, listener)` | 購読し、解除関数を返す |
29
+ | `startHats(opcode, matchFields?)` | 常に配列。`undefined` を返さない |
30
+ | `stopThread(thread)` / `currentThreads()` | `_stopThread` / `threads` |
31
+ | `getStageTarget()` | `getTargetForStage()` |
32
+ | `targets()` / `spriteTargets()` | `targets` の複製。`spriteTargets` は Stage を除く |
33
+ | `getRenderer()` / `requestRedraw()` | `renderer`。`requestRedraw` は未提供なら no-op |
34
+ | `getMonitorBlocks()` / `getMonitorState()` | 検証済みの monitor block store と record map |
35
+
36
+ ## block surface
37
+
38
+ `createBlockSurfaceBuilder` は、extension が既に持っている `Scratch.BlockType` / `Scratch.ArgumentType`
39
+ を受け取って block record を組み立てます。record の形、palette 可視性、reporter の monitor 既定値、
40
+ opcode 重複検出だけを所有します。opcode・block text・menu 項目は caller 側に残るため、アプリ固有の
41
+ 語彙はこの package に入りません。
42
+
43
+ ```ts
44
+ import {coerceScalarBlockValue, createBlockSurfaceBuilder} from '@kubohiroya/turbowarp-runtime-adapter';
45
+
46
+ const build = createBlockSurfaceBuilder({ArgumentType, BlockType}, {visible: stateVisible});
47
+ const surface = build.surface(
48
+ [
49
+ build.reporter({opcode: 'sceneId', text: 'current scene id'}),
50
+ ...build.reporters([
51
+ ['status', 'story status'],
52
+ ['version', 'runtime version']
53
+ ]),
54
+ build.command({
55
+ opcode: 'setVariable',
56
+ text: 'set [NAME] to [VALUE] as [TYPE]',
57
+ arguments: {
58
+ NAME: build.stringArgument(),
59
+ VALUE: build.stringArgument(),
60
+ TYPE: build.menuArgument('scalarTypes')
61
+ },
62
+ visible: writeVisible
63
+ })
64
+ ],
65
+ {scalarTypes: build.menu(['string', 'number', 'boolean'])}
66
+ );
67
+
68
+ const written = coerceScalarBlockValue(args.VALUE, args.TYPE, {errorCodePrefix: 'K4'});
69
+ ```
70
+
71
+ ## 互換性
72
+
73
+ 既存 app が安定した app 固有 diagnostics を公開している場合は、`errorCodePrefix` を渡せます。たとえば `tm-kamishibai` は runtime interaction mechanics をこの package に委譲しつつ、`K4-BROADCAST-*` error を維持できます。
74
+
75
+ ## 使用例
76
+
77
+ ```ts
78
+ import {
79
+ createTurboWarpBroadcastPort,
80
+ createTurboWarpRuntimeHost
81
+ } from '@kubohiroya/turbowarp-runtime-adapter';
82
+
83
+ const host = createTurboWarpRuntimeHost({Scratch});
84
+ const broadcast = createTurboWarpBroadcastPort({
85
+ runtime: host.runtime,
86
+ errorCodePrefix: 'K4'
87
+ });
88
+
89
+ const dispose = host.onRuntimeEvent('PROJECT_STOP_ALL', () => {
90
+ console.log('stopped');
91
+ });
92
+
93
+ await broadcast.broadcastMessageAndWait(
94
+ {message: 'openScene'},
95
+ {signal: new AbortController().signal}
96
+ );
97
+
98
+ dispose();
99
+ ```
100
+
101
+ ## 開発
102
+
103
+ ```bash
104
+ corepack enable
105
+ pnpm install --frozen-lockfile
106
+ pnpm run check
107
+ pnpm run release:check
108
+ ```
package/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # turbowarp-runtime-adapter
2
+
3
+ [日本語](README.ja.md)
4
+
5
+ `@kubohiroya/turbowarp-runtime-adapter` provides app-neutral adapters for interacting with the TurboWarp runtime from unsandboxed extensions and packaged TurboWarp apps.
6
+
7
+ This package intentionally does not define app-specific DSL semantics, block metadata, story/navigation behavior, pose handling, or 3D scene graph policy. Those remain in app packages such as `tm-kamishibai` and `tm-3d-app`.
8
+
9
+ ## Scope
10
+
11
+ - Resolve a runtime from `Scratch.vm.runtime` or an explicit runtime object.
12
+ - Subscribe to runtime events with disposable listeners.
13
+ - Start hats through a small validated host adapter.
14
+ - Read the Stage target, the sprite targets, the renderer, and the monitor stores.
15
+ - Provide a generic `broadcastMessageAndWait` port for exact Stage broadcast names.
16
+ - Build the repetitive parts of an extension `getInfo()` block list.
17
+ - Keep Scratch VM access behind a narrow interface.
18
+
19
+ ## Runtime surface
20
+
21
+ `createTurboWarpRuntimeHost` returns one adapter per session. Inject it rather than passing a raw
22
+ runtime around: a consumer that only needs the Stage then depends on `getStageTarget()` alone, not
23
+ on the whole runtime shape.
24
+
25
+ | Member | Reads |
26
+ | --- | --- |
27
+ | `runtime` | the validated runtime, for anything this package does not own yet |
28
+ | `onRuntimeEvent(event, listener)` | subscribes and returns a disposer |
29
+ | `startHats(opcode, matchFields?)` | always an array, never `undefined` |
30
+ | `stopThread(thread)` / `currentThreads()` | `_stopThread` / `threads` |
31
+ | `getStageTarget()` | `getTargetForStage()` |
32
+ | `targets()` / `spriteTargets()` | `targets`, copied; `spriteTargets` drops the Stage |
33
+ | `getRenderer()` / `requestRedraw()` | `renderer`; `requestRedraw` is a no-op when absent |
34
+ | `getMonitorBlocks()` / `getMonitorState()` | the validated monitor block store and record map |
35
+
36
+ ## Block surfaces
37
+
38
+ `createBlockSurfaceBuilder` takes the `Scratch.BlockType` / `Scratch.ArgumentType` pair an
39
+ extension already has and builds block records from it. It owns record shape, palette visibility,
40
+ reporter monitor defaults, and duplicate-opcode detection. Opcodes, block text, and menu items stay
41
+ with the caller, so no app vocabulary enters this package.
42
+
43
+ ```ts
44
+ import {coerceScalarBlockValue, createBlockSurfaceBuilder} from '@kubohiroya/turbowarp-runtime-adapter';
45
+
46
+ const build = createBlockSurfaceBuilder({ArgumentType, BlockType}, {visible: stateVisible});
47
+ const surface = build.surface(
48
+ [
49
+ build.reporter({opcode: 'sceneId', text: 'current scene id'}),
50
+ ...build.reporters([
51
+ ['status', 'story status'],
52
+ ['version', 'runtime version']
53
+ ]),
54
+ build.command({
55
+ opcode: 'setVariable',
56
+ text: 'set [NAME] to [VALUE] as [TYPE]',
57
+ arguments: {
58
+ NAME: build.stringArgument(),
59
+ VALUE: build.stringArgument(),
60
+ TYPE: build.menuArgument('scalarTypes')
61
+ },
62
+ visible: writeVisible
63
+ })
64
+ ],
65
+ {scalarTypes: build.menu(['string', 'number', 'boolean'])}
66
+ );
67
+
68
+ const written = coerceScalarBlockValue(args.VALUE, args.TYPE, {errorCodePrefix: 'K4'});
69
+ ```
70
+
71
+ ## Compatibility
72
+
73
+ Consumers that already expose stable app-specific diagnostics can pass `errorCodePrefix`. For example, `tm-kamishibai` can keep `K4-BROADCAST-*` errors while delegating the runtime interaction mechanics to this package.
74
+
75
+ ## Example
76
+
77
+ ```ts
78
+ import {
79
+ createTurboWarpBroadcastPort,
80
+ createTurboWarpRuntimeHost
81
+ } from '@kubohiroya/turbowarp-runtime-adapter';
82
+
83
+ const host = createTurboWarpRuntimeHost({Scratch});
84
+ const broadcast = createTurboWarpBroadcastPort({
85
+ runtime: host.runtime,
86
+ errorCodePrefix: 'K4'
87
+ });
88
+
89
+ const dispose = host.onRuntimeEvent('PROJECT_STOP_ALL', () => {
90
+ console.log('stopped');
91
+ });
92
+
93
+ await broadcast.broadcastMessageAndWait(
94
+ {message: 'openScene'},
95
+ {signal: new AbortController().signal}
96
+ );
97
+
98
+ dispose();
99
+ ```
100
+
101
+ ## Development
102
+
103
+ ```bash
104
+ corepack enable
105
+ pnpm install --frozen-lockfile
106
+ pnpm run check
107
+ pnpm run release:check
108
+ ```
@@ -0,0 +1,164 @@
1
+ export interface ScratchRuntimeHost {
2
+ vm?: {
3
+ runtime?: unknown;
4
+ };
5
+ extensions?: {
6
+ unsandboxed?: boolean;
7
+ };
8
+ }
9
+ export interface TurboWarpRuntimeLike {
10
+ on(event: string, listener: (...args: unknown[]) => void): void;
11
+ off?: (event: string, listener: (...args: unknown[]) => void) => void;
12
+ removeListener?: (event: string, listener: (...args: unknown[]) => void) => void;
13
+ startHats(opcode: string, matchFields?: Record<string, string>): unknown[] | undefined;
14
+ getTargetForStage?: () => unknown;
15
+ _stopThread?: (thread: unknown) => void;
16
+ threads?: unknown[];
17
+ targets?: unknown[];
18
+ renderer?: unknown;
19
+ requestRedraw?: () => void;
20
+ monitorBlocks?: unknown;
21
+ getMonitorState?: () => unknown;
22
+ }
23
+ /**
24
+ * The Scratch monitor block store, as used by the `data_showvariable` and `data_hidevariable`
25
+ * primitives. Only the members needed to read and toggle a monitor are declared.
26
+ */
27
+ export interface TurboWarpMonitorBlocks {
28
+ getBlock(id: string): unknown;
29
+ getScripts(): string[];
30
+ changeBlock(change: {
31
+ id: string;
32
+ element: string;
33
+ value: unknown;
34
+ }): void;
35
+ }
36
+ /** The immutable monitor record map the runtime keeps alongside {@link TurboWarpMonitorBlocks}. */
37
+ export interface TurboWarpMonitorState {
38
+ has(id: string): boolean;
39
+ get(id: string): unknown;
40
+ valueSeq(): Iterable<unknown>;
41
+ }
42
+ export interface TurboWarpRuntimeHostOptions {
43
+ Scratch?: ScratchRuntimeHost;
44
+ runtime?: unknown;
45
+ requireUnsandboxed?: boolean;
46
+ }
47
+ export interface TurboWarpRuntimeHost {
48
+ runtime: TurboWarpRuntimeLike;
49
+ onRuntimeEvent(event: string, listener: (...args: unknown[]) => void): () => void;
50
+ startHats(opcode: string, matchFields?: Record<string, string>): unknown[];
51
+ stopThread(thread: unknown): void;
52
+ getStageTarget(): unknown;
53
+ currentThreads(): unknown[];
54
+ /** Every loaded target, Stage included, as a copy the caller may not mutate in place. */
55
+ targets(): unknown[];
56
+ /** Every loaded target except the Stage, in runtime order. */
57
+ spriteTargets(): unknown[];
58
+ /** The renderer the VM attached. Throws when the runtime has none. */
59
+ getRenderer(): unknown;
60
+ /** Ask the renderer for a frame. A runtime without `requestRedraw` is a no-op, not an error. */
61
+ requestRedraw(): void;
62
+ getMonitorBlocks(): TurboWarpMonitorBlocks;
63
+ getMonitorState(): TurboWarpMonitorState;
64
+ }
65
+ export interface BroadcastPortContext {
66
+ signal: AbortSignal;
67
+ }
68
+ export interface TurboWarpBroadcastPort {
69
+ broadcastMessageAndWait(payload: {
70
+ message: string;
71
+ }, context: BroadcastPortContext): Promise<void>;
72
+ dispose(): void;
73
+ }
74
+ export interface TurboWarpBroadcastPortOptions {
75
+ runtime: unknown;
76
+ errorCodePrefix?: string;
77
+ }
78
+ export declare function createTurboWarpRuntimeHost(options?: TurboWarpRuntimeHostOptions): TurboWarpRuntimeHost;
79
+ export declare function createTurboWarpBroadcastPort(options: TurboWarpBroadcastPortOptions): TurboWarpBroadcastPort;
80
+ /**
81
+ * The `Scratch.BlockType` / `Scratch.ArgumentType` pair an extension's `getInfo()` builds against.
82
+ * Passing them in keeps this package free of any ambient `Scratch` global.
83
+ */
84
+ export interface ScratchBlockVocabulary {
85
+ ArgumentType: Record<string, string>;
86
+ BlockType: Record<string, string>;
87
+ }
88
+ export interface BlockArgumentRecord {
89
+ type: string;
90
+ defaultValue?: string | number | boolean;
91
+ menu?: string;
92
+ }
93
+ export interface BlockRecord {
94
+ opcode: string;
95
+ blockType: string;
96
+ text: string;
97
+ arguments?: Readonly<Record<string, BlockArgumentRecord>>;
98
+ hideFromPalette?: boolean;
99
+ disableMonitor?: boolean;
100
+ }
101
+ export interface BlockMenuRecord {
102
+ acceptReporters: boolean;
103
+ items: readonly string[];
104
+ }
105
+ export interface BlockSpec {
106
+ opcode: string;
107
+ text: string;
108
+ arguments?: Readonly<Record<string, BlockArgumentRecord>>;
109
+ /** Defaults to the builder's own visibility. */
110
+ visible?: boolean;
111
+ /** Reporters default to `true`; other block types ignore it. */
112
+ monitor?: boolean;
113
+ }
114
+ export interface BlockSurfaceBuilderOptions {
115
+ /** Default palette visibility for every block this builder makes. Defaults to `true`. */
116
+ visible?: boolean;
117
+ }
118
+ export interface BlockSurfaceBuilder {
119
+ reporter(spec: BlockSpec): BlockRecord;
120
+ boolean(spec: BlockSpec): BlockRecord;
121
+ command(spec: BlockSpec): BlockRecord;
122
+ hat(spec: BlockSpec): BlockRecord;
123
+ /** Build a run of plain reporters from `[opcode, text]` pairs. */
124
+ reporters(entries: readonly (readonly [string, string])[], options?: {
125
+ visible?: boolean;
126
+ monitor?: boolean;
127
+ }): readonly BlockRecord[];
128
+ stringArgument(defaultValue?: string): BlockArgumentRecord;
129
+ numberArgument(defaultValue?: number): BlockArgumentRecord;
130
+ menuArgument(menu: string): BlockArgumentRecord;
131
+ menu(items: readonly string[], options?: {
132
+ acceptReporters?: boolean;
133
+ }): BlockMenuRecord;
134
+ surface(blocks: readonly BlockRecord[], menus?: Readonly<Record<string, BlockMenuRecord>>): Readonly<{
135
+ blocks: readonly BlockRecord[];
136
+ menus: Readonly<Record<string, BlockMenuRecord>>;
137
+ }>;
138
+ }
139
+ /**
140
+ * Build the repetitive parts of an extension's `getInfo()` block list.
141
+ *
142
+ * The builder owns record shape and palette visibility only. Opcodes, block text, menu items, and
143
+ * every other app-specific decision stay with the caller, so nothing about a concrete app leaks
144
+ * into this package.
145
+ */
146
+ export declare function createBlockSurfaceBuilder(vocabulary: ScratchBlockVocabulary, options?: BlockSurfaceBuilderOptions): BlockSurfaceBuilder;
147
+ export type ScalarBlockValueType = 'string' | 'number' | 'boolean';
148
+ export type ScalarBlockValueResult = Readonly<{
149
+ ok: true;
150
+ value: string | number | boolean;
151
+ }> | Readonly<{
152
+ ok: false;
153
+ code: string;
154
+ }>;
155
+ /**
156
+ * Coerce one Scratch block argument to a scalar of the named type.
157
+ *
158
+ * Scratch hands every argument over as a string, so a writable scalar surface has to decide what
159
+ * counts as a number or a boolean. That decision is the same in every app, while the diagnostic
160
+ * codes are not, so the prefix is injectable the way {@link createTurboWarpBroadcastPort} does it.
161
+ */
162
+ export declare function coerceScalarBlockValue(value: unknown, type: string, options?: {
163
+ errorCodePrefix?: string;
164
+ }): ScalarBlockValueResult;