@kubohiroya/turbowarp-structured-data 0.4.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/LICENSE +373 -0
- package/README.ja.md +73 -0
- package/README.md +245 -0
- package/THIRD_PARTY_NOTICES.md +25 -0
- package/dist/extension-manifest.json +426 -0
- package/dist/structured-data.js +7071 -0
- package/docs/architecture.ja.md +32 -0
- package/docs/architecture.md +30 -0
- package/docs/index.html +26 -0
- package/docs/named-data-integration-plan.ja.md +375 -0
- package/package.json +77 -0
- package/schemas/extension-manifest.schema.json +103 -0
- package/src/block-definitions.json +98 -0
- package/src/codecs.ts +204 -0
- package/src/config.ts +31 -0
- package/src/core.ts +309 -0
- package/src/extension-manifest.ts +176 -0
- package/src/extension.ts +361 -0
- package/src/globals.d.ts +38 -0
- package/src/index.ts +8 -0
- package/src/structured-provider.ts +188 -0
- package/templates/README.ja.md +183 -0
- package/templates/README.md +198 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import {renderJsonDocument, renderYamlDocument} from './codecs.js';
|
|
2
|
+
import type {JsonValue} from './core.js';
|
|
3
|
+
import {
|
|
4
|
+
NamedDataError,
|
|
5
|
+
type NamedDataBody,
|
|
6
|
+
type NamedDataMetadata,
|
|
7
|
+
type NamedDataProvider,
|
|
8
|
+
type NamedDataReference,
|
|
9
|
+
type NamedDataRepresentation,
|
|
10
|
+
type NamedDataResolveContext
|
|
11
|
+
} from '@kubohiroya/turbowarp-named-data/composition';
|
|
12
|
+
|
|
13
|
+
export const STRUCTURED_DATA_NAMESPACE = 'structured';
|
|
14
|
+
export const STRUCTURED_BODY_MAX_BYTES = 256 * 1024;
|
|
15
|
+
|
|
16
|
+
export interface StructuredDataBinding {
|
|
17
|
+
value: JsonValue;
|
|
18
|
+
revision: number;
|
|
19
|
+
nativeRepresentation: 'json' | 'yaml';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface StructuredDataBindingStore {
|
|
23
|
+
get(target: object, name: string): StructuredDataBinding | undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class StructuredDataNamedDataProvider implements NamedDataProvider {
|
|
27
|
+
public readonly namespace = STRUCTURED_DATA_NAMESPACE;
|
|
28
|
+
public readonly kind = 'structured' as const;
|
|
29
|
+
private readonly openBodies = new Set<symbol>();
|
|
30
|
+
private released = false;
|
|
31
|
+
|
|
32
|
+
public constructor(
|
|
33
|
+
private readonly store: StructuredDataBindingStore,
|
|
34
|
+
private readonly maxBodyBytes = STRUCTURED_BODY_MAX_BYTES
|
|
35
|
+
) {}
|
|
36
|
+
|
|
37
|
+
public canResolve(
|
|
38
|
+
reference: NamedDataReference,
|
|
39
|
+
representation: NamedDataRepresentation
|
|
40
|
+
): boolean {
|
|
41
|
+
return (
|
|
42
|
+
reference.namespace === this.namespace &&
|
|
43
|
+
reference.kind === this.kind &&
|
|
44
|
+
reference.scope === 'target' &&
|
|
45
|
+
(representation === 'json' || representation === 'yaml')
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public stat(
|
|
50
|
+
reference: NamedDataReference,
|
|
51
|
+
representation: NamedDataRepresentation,
|
|
52
|
+
context: NamedDataResolveContext
|
|
53
|
+
): NamedDataMetadata {
|
|
54
|
+
const resolved = this.resolve(reference, representation, context);
|
|
55
|
+
return this.metadata(
|
|
56
|
+
reference,
|
|
57
|
+
resolved.nativeRepresentation,
|
|
58
|
+
representation,
|
|
59
|
+
resolved.bytes.byteLength,
|
|
60
|
+
resolved.revision
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public openBody(
|
|
65
|
+
reference: NamedDataReference,
|
|
66
|
+
representation: NamedDataRepresentation,
|
|
67
|
+
context: NamedDataResolveContext
|
|
68
|
+
): NamedDataBody {
|
|
69
|
+
const resolved = this.resolve(reference, representation, context);
|
|
70
|
+
const handle = Symbol('structured-body');
|
|
71
|
+
this.openBodies.add(handle);
|
|
72
|
+
let released = false;
|
|
73
|
+
return {
|
|
74
|
+
...this.metadata(
|
|
75
|
+
reference,
|
|
76
|
+
resolved.nativeRepresentation,
|
|
77
|
+
representation,
|
|
78
|
+
resolved.bytes.byteLength,
|
|
79
|
+
resolved.revision
|
|
80
|
+
),
|
|
81
|
+
body: resolved.bytes,
|
|
82
|
+
release: () => {
|
|
83
|
+
if (released) return;
|
|
84
|
+
released = true;
|
|
85
|
+
this.openBodies.delete(handle);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public release(): void {
|
|
91
|
+
this.released = true;
|
|
92
|
+
this.openBodies.clear();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public clearSession(): void {
|
|
96
|
+
this.openBodies.clear();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private resolve(
|
|
100
|
+
reference: NamedDataReference,
|
|
101
|
+
representation: NamedDataRepresentation,
|
|
102
|
+
context: NamedDataResolveContext
|
|
103
|
+
): {bytes: Uint8Array; revision: number; nativeRepresentation: 'json' | 'yaml'} {
|
|
104
|
+
if (this.released) {
|
|
105
|
+
throw new NamedDataError('NAMED_DATA_PROVIDER_RELEASED', 'The structured provider was released.');
|
|
106
|
+
}
|
|
107
|
+
this.validateReference(reference, context);
|
|
108
|
+
this.throwIfAborted(context.signal);
|
|
109
|
+
const binding = this.store.get(context.target as object, reference.name);
|
|
110
|
+
if (!binding) {
|
|
111
|
+
throw new NamedDataError(
|
|
112
|
+
'NAMED_DATA_NOT_FOUND',
|
|
113
|
+
`Structured data does not exist: ${reference.name}`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
const text = this.render(binding.value, representation);
|
|
117
|
+
const bytes = new TextEncoder().encode(text);
|
|
118
|
+
if (bytes.byteLength > this.maxBodyBytes) {
|
|
119
|
+
throw new NamedDataError(
|
|
120
|
+
'NAMED_DATA_BODY_TOO_LARGE',
|
|
121
|
+
`Encoded body is ${bytes.byteLength} bytes; maximum is ${this.maxBodyBytes}.`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
this.throwIfAborted(context.signal);
|
|
125
|
+
return {
|
|
126
|
+
bytes,
|
|
127
|
+
revision: binding.revision,
|
|
128
|
+
nativeRepresentation: binding.nativeRepresentation
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private validateReference(
|
|
133
|
+
reference: NamedDataReference,
|
|
134
|
+
context: NamedDataResolveContext
|
|
135
|
+
): void {
|
|
136
|
+
if (reference.namespace !== this.namespace || reference.name.trim().length === 0) {
|
|
137
|
+
throw new NamedDataError('NAMED_DATA_INVALID_REF', 'Invalid structured data reference.');
|
|
138
|
+
}
|
|
139
|
+
if (reference.kind !== this.kind) {
|
|
140
|
+
throw new NamedDataError(
|
|
141
|
+
'NAMED_DATA_KIND_MISMATCH',
|
|
142
|
+
`Expected ${this.kind}, received ${reference.kind}.`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
if (reference.scope !== 'target' || !context.target) {
|
|
146
|
+
throw new NamedDataError(
|
|
147
|
+
'NAMED_DATA_SCOPE_MISMATCH',
|
|
148
|
+
'Structured data currently requires a target scope and target context.'
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private render(value: JsonValue, representation: NamedDataRepresentation): string {
|
|
154
|
+
if (representation === 'json') return renderJsonDocument(value);
|
|
155
|
+
if (representation === 'yaml') return renderYamlDocument(value);
|
|
156
|
+
throw new NamedDataError(
|
|
157
|
+
'NAMED_DATA_REPRESENTATION_UNSUPPORTED',
|
|
158
|
+
`Structured data cannot be rendered as ${representation}.`
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private metadata(
|
|
163
|
+
reference: NamedDataReference,
|
|
164
|
+
nativeRepresentation: 'json' | 'yaml',
|
|
165
|
+
representation: NamedDataRepresentation,
|
|
166
|
+
byteLength: number,
|
|
167
|
+
revision: number
|
|
168
|
+
): NamedDataMetadata {
|
|
169
|
+
return {
|
|
170
|
+
reference: {...reference},
|
|
171
|
+
nativeRepresentation,
|
|
172
|
+
representation,
|
|
173
|
+
mediaType:
|
|
174
|
+
representation === 'json'
|
|
175
|
+
? 'application/json; charset=utf-8'
|
|
176
|
+
: 'application/yaml; charset=utf-8',
|
|
177
|
+
byteLength,
|
|
178
|
+
revision: String(revision),
|
|
179
|
+
replayable: true
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private throwIfAborted(signal: AbortSignal | undefined): void {
|
|
184
|
+
if (signal?.aborted) {
|
|
185
|
+
throw new NamedDataError('NAMED_DATA_ABORTED', 'Body resolution was aborted.');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# TurboWarp-<Extension-Name>
|
|
2
|
+
|
|
3
|
+
[English](README.md) | [日本語](README.ja.md)
|
|
4
|
+
|
|
5
|
+
<何をするかを一文で説明>ためのTurboWarp機能拡張です。
|
|
6
|
+
|
|
7
|
+
**利用ガイド:** [English](<PAGES_URL>/)
|
|
8
|
+
|
|
9
|
+
## できること
|
|
10
|
+
|
|
11
|
+
- <利用者ができること1>
|
|
12
|
+
- <利用者ができること2>
|
|
13
|
+
- <利用者ができること3>
|
|
14
|
+
|
|
15
|
+
実装ファイルの説明ではなく、利用者から見た結果を書きます。
|
|
16
|
+
|
|
17
|
+
## ドキュメントとブロックアイコン
|
|
18
|
+
|
|
19
|
+
この機能拡張は、英語版の利用者向けドキュメントをGitHub Pagesで公開します。
|
|
20
|
+
|
|
21
|
+
- `docsURI` はGitHub Pages上の英語版ドキュメント `<PAGES_URL>/` を指します。
|
|
22
|
+
- `blockIconURI` は `data:image/svg+xml;base64,...` 形式の自己完結したSVGとします。
|
|
23
|
+
- ブロックアイコンは機能拡張の内容を直感的に表す、単純で識別しやすい図柄にします。公開版では汎用の仮アイコンをそのまま使用しません。
|
|
24
|
+
|
|
25
|
+
## 動作条件と安全上の注意
|
|
26
|
+
|
|
27
|
+
- <TurboWarp Desktop/Web/Packagerの対応>
|
|
28
|
+
- <Browser APIまたはcompanion extension>
|
|
29
|
+
- <sandbox条件>
|
|
30
|
+
|
|
31
|
+
> [!IMPORTANT]
|
|
32
|
+
> この機能拡張は<理由>のため、サンドボックスなしで実行する必要があります。
|
|
33
|
+
> 信頼できる配布元の機能拡張だけを読み込んでください。
|
|
34
|
+
|
|
35
|
+
sandbox互換の場合は上の注意書きを削除します。
|
|
36
|
+
|
|
37
|
+
## インストール
|
|
38
|
+
|
|
39
|
+
### 完成済みJavaScript
|
|
40
|
+
|
|
41
|
+
1. [`dist/<file>.js`](dist/<file>.js?raw=1)をダウンロードします。
|
|
42
|
+
2. TurboWarpで**機能拡張**を開きます。
|
|
43
|
+
3. **カスタム機能拡張**からfileを読み込みます。
|
|
44
|
+
4. 必要な場合は**サンドボックスなしで実行する**を許可します。
|
|
45
|
+
|
|
46
|
+
完成済みの検証対象JavaScriptをrepositoryへcommitしているため、利用者が
|
|
47
|
+
Node.jsやbuild環境を用意する必要はありません。
|
|
48
|
+
|
|
49
|
+
### npm package
|
|
50
|
+
|
|
51
|
+
検証済みのversionをexact pinします。
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pnpm add --save-exact @kubohiroya/<package>@<version>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
standalone bundle:
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
node_modules/@kubohiroya/<package>/dist/<file>.js
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
version固定CDN URL:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
https://cdn.jsdelivr.net/npm/@kubohiroya/<package>@<version>/dist/<file>.js
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`<version>`を手作業で保守する場合は、`package.json`との一致をCIで検査します。
|
|
70
|
+
|
|
71
|
+
## クイックスタート
|
|
72
|
+
|
|
73
|
+
1. <手順1>
|
|
74
|
+
2. <手順2>
|
|
75
|
+
3. <手順3>
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
<最小のTurboWarp block flow>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## ブロックリファレンス
|
|
82
|
+
|
|
83
|
+
block referenceは
|
|
84
|
+
[`src/block-definitions.json`](src/block-definitions.json)から生成します。
|
|
85
|
+
生成範囲を直接編集しないでください。
|
|
86
|
+
|
|
87
|
+
<!-- BEGIN GENERATED BLOCKS -->
|
|
88
|
+
|
|
89
|
+
<自動生成block文書>
|
|
90
|
+
|
|
91
|
+
<!-- END GENERATED BLOCKS -->
|
|
92
|
+
|
|
93
|
+
## 重要な動作
|
|
94
|
+
|
|
95
|
+
| 状況 | 動作 |
|
|
96
|
+
|---|---|
|
|
97
|
+
| <case> | <正確な動作> |
|
|
98
|
+
| project停止 | <cleanup> |
|
|
99
|
+
| project再読込 | <cleanupまたは復元> |
|
|
100
|
+
| 無効な入力 | <errorまたはfallback> |
|
|
101
|
+
|
|
102
|
+
## Composition API
|
|
103
|
+
|
|
104
|
+
Composition APIがないpackageでは、このsectionを削除します。
|
|
105
|
+
|
|
106
|
+
Composition APIをimportしても、standalone TurboWarp機能拡張は自動登録されません。
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import {create<Feature>Composition} from '@kubohiroya/<package>/composition';
|
|
110
|
+
|
|
111
|
+
const feature = create<Feature>Composition({
|
|
112
|
+
// 明示的な依存
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// capabilityを利用する。
|
|
116
|
+
|
|
117
|
+
feature.releaseAll();
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
所有、取消し、`AbortSignal`、project lifecycle、副作用、browser/network/storage/
|
|
121
|
+
renderer境界、`release()`/`releaseAll()`を説明します。
|
|
122
|
+
|
|
123
|
+
完全なmethod referenceは
|
|
124
|
+
[`docs/composition-api.md`](docs/composition-api.md)へ分けます。
|
|
125
|
+
|
|
126
|
+
## 連携
|
|
127
|
+
|
|
128
|
+
| 利用側 | 関係 |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `<consumer package>` | <required/optional/capability injection> |
|
|
131
|
+
|
|
132
|
+
依存方向はconsumerからproviderとし、private fieldを公開連携方法として書きません。
|
|
133
|
+
|
|
134
|
+
## 互換性
|
|
135
|
+
|
|
136
|
+
| 識別子 | 値 | 安定性 |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| 製品名 | `TurboWarp-<Extension-Name>` | 利用者向け表示 |
|
|
139
|
+
| repository | `kubohiroya/<repository>` | current source |
|
|
140
|
+
| npm package | `@kubohiroya/<package>` | public package contract |
|
|
141
|
+
| extension ID | `<extensionid>` | SB3に保存。変更にはmigrationが必要 |
|
|
142
|
+
|
|
143
|
+
複数語からなるTurboWarp機能拡張の正式表示名では、すべての構成語をハイフンで連結します。例: `TurboWarp-Asset-Manager`、`TurboWarp-Runtime-Expression`、`TurboWarp-Diagnostic-Overlay`。
|
|
144
|
+
|
|
145
|
+
extension IDまたはopcode変更時はschema-aware migrationを説明し、保存済み識別子が変わった場合にJavaScriptだけの差替えを案内しません。
|
|
146
|
+
|
|
147
|
+
## 開発
|
|
148
|
+
|
|
149
|
+
`engines`のNode.jsと、`packageManager`に宣言したpnpmを使用します。
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
corepack enable
|
|
153
|
+
pnpm install --frozen-lockfile
|
|
154
|
+
pnpm check
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
| command | 用途 |
|
|
158
|
+
|---|---|
|
|
159
|
+
| `pnpm dev` | source変更中の継続build |
|
|
160
|
+
| `pnpm test` | test |
|
|
161
|
+
| `pnpm docs` | block文書生成 |
|
|
162
|
+
| `pnpm check:dist` | commit済みbuild artifactの検証 |
|
|
163
|
+
| `pnpm pack:check` | npm package内容の確認 |
|
|
164
|
+
| `pnpm release:check` | release dry run |
|
|
165
|
+
|
|
166
|
+
## リリース
|
|
167
|
+
|
|
168
|
+
1. `CHANGELOG.md`がある場合は更新する。
|
|
169
|
+
2. `package.json`のversionを更新する。
|
|
170
|
+
3. `pnpm check`とrepository固有のrelease checkを実行する。
|
|
171
|
+
4. release PRをmergeする。
|
|
172
|
+
5. 検証済みcommitへ`v<version>`tagを付ける。
|
|
173
|
+
6. 必要に応じて同じversionをnpmへpublishする。
|
|
174
|
+
7. GitHub Release、npm tarball、GitHub Pages、`docsURI`、block icon、CDNを確認する。
|
|
175
|
+
|
|
176
|
+
release consistency checkでは、exact-version README/CDN例に加え、`docsURI`が英語版Pagesを指すこと、`blockIconURI`が`data:image/svg+xml;base64,...`形式であることを検証します。
|
|
177
|
+
|
|
178
|
+
## ライセンス
|
|
179
|
+
|
|
180
|
+
[Mozilla Public License 2.0](LICENSE)(SPDX: `MPL-2.0`)で提供します。
|
|
181
|
+
|
|
182
|
+
third-party componentを含む場合は
|
|
183
|
+
[`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md)に記載します。
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# TurboWarp-<Extension-Name>
|
|
2
|
+
|
|
3
|
+
[English](README.md) | [日本語](README.ja.md)
|
|
4
|
+
|
|
5
|
+
A TurboWarp extension that <one-sentence purpose>.
|
|
6
|
+
|
|
7
|
+
**User guide:** [English](<PAGES_URL>/)
|
|
8
|
+
|
|
9
|
+
## What it does
|
|
10
|
+
|
|
11
|
+
- <User-visible capability 1>
|
|
12
|
+
- <User-visible capability 2>
|
|
13
|
+
- <User-visible capability 3>
|
|
14
|
+
|
|
15
|
+
Keep this section about outcomes, not implementation files.
|
|
16
|
+
|
|
17
|
+
## Documentation and block icon
|
|
18
|
+
|
|
19
|
+
This extension publishes its English user documentation with GitHub Pages.
|
|
20
|
+
|
|
21
|
+
- `docsURI` points to the English GitHub Pages documentation: `<PAGES_URL>/`.
|
|
22
|
+
- `blockIconURI` is a self-contained SVG encoded as `data:image/svg+xml;base64,...`.
|
|
23
|
+
- The block icon should be a simple, recognizable symbol that represents this extension and improves block readability. Do not use a generic placeholder icon in a released extension.
|
|
24
|
+
|
|
25
|
+
## Requirements and safety
|
|
26
|
+
|
|
27
|
+
- <TurboWarp Desktop / Web / Packager support>
|
|
28
|
+
- <Browser API or companion extension requirements>
|
|
29
|
+
- <Sandbox requirement>
|
|
30
|
+
|
|
31
|
+
> [!IMPORTANT]
|
|
32
|
+
> This extension must run without the sandbox because <reason>. Load unsandboxed
|
|
33
|
+
> extension code only from a source you trust.
|
|
34
|
+
|
|
35
|
+
Delete the notice above when the extension is sandbox-compatible.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
### Ready-to-use JavaScript
|
|
40
|
+
|
|
41
|
+
1. Download [`dist/<file>.js`](dist/<file>.js?raw=1).
|
|
42
|
+
2. Open **Extensions** in TurboWarp.
|
|
43
|
+
3. Choose **Custom Extension** and load the file.
|
|
44
|
+
4. Enable **Run extension without sandbox** when required.
|
|
45
|
+
|
|
46
|
+
The reviewed JavaScript build is committed to this repository, so users do not
|
|
47
|
+
need Node.js to install the extension.
|
|
48
|
+
|
|
49
|
+
### npm package
|
|
50
|
+
|
|
51
|
+
Install an exact version that you have reviewed:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pnpm add --save-exact @kubohiroya/<package>@<version>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Load the standalone bundle from:
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
node_modules/@kubohiroya/<package>/dist/<file>.js
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
A version-pinned CDN URL is:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
https://cdn.jsdelivr.net/npm/@kubohiroya/<package>@<version>/dist/<file>.js
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Do not maintain `<version>` manually unless CI checks it against `package.json`.
|
|
70
|
+
|
|
71
|
+
## Quick start
|
|
72
|
+
|
|
73
|
+
1. <Step 1>
|
|
74
|
+
2. <Step 2>
|
|
75
|
+
3. <Step 3>
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
<Minimal TurboWarp block flow>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Block reference
|
|
82
|
+
|
|
83
|
+
The block reference is generated from
|
|
84
|
+
[`src/block-definitions.json`](src/block-definitions.json). Do not edit the
|
|
85
|
+
generated section manually.
|
|
86
|
+
|
|
87
|
+
<!-- BEGIN GENERATED BLOCKS -->
|
|
88
|
+
|
|
89
|
+
<Generated block documentation>
|
|
90
|
+
|
|
91
|
+
<!-- END GENERATED BLOCKS -->
|
|
92
|
+
|
|
93
|
+
## Important behavior
|
|
94
|
+
|
|
95
|
+
| Situation | Behavior |
|
|
96
|
+
|---|---|
|
|
97
|
+
| <Case> | <Exact behavior> |
|
|
98
|
+
| Project stop | <Cleanup behavior> |
|
|
99
|
+
| Project reload | <Cleanup or restoration behavior> |
|
|
100
|
+
| Invalid input | <Error or fallback behavior> |
|
|
101
|
+
|
|
102
|
+
## Composition API
|
|
103
|
+
|
|
104
|
+
Delete this section when the package has no Composition API.
|
|
105
|
+
|
|
106
|
+
Importing the Composition API does not register the standalone TurboWarp
|
|
107
|
+
extension.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import {create<Feature>Composition} from '@kubohiroya/<package>/composition';
|
|
111
|
+
|
|
112
|
+
const feature = create<Feature>Composition({
|
|
113
|
+
// Explicit dependencies
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Use the capability.
|
|
117
|
+
|
|
118
|
+
feature.releaseAll();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Explain:
|
|
122
|
+
|
|
123
|
+
- ownership;
|
|
124
|
+
- cancellation and `AbortSignal`;
|
|
125
|
+
- target/project lifecycle;
|
|
126
|
+
- side effects;
|
|
127
|
+
- browser, network, storage, and renderer boundaries;
|
|
128
|
+
- `release()` / `releaseAll()` behavior.
|
|
129
|
+
|
|
130
|
+
Move the complete method reference to [`docs/composition-api.md`](docs/composition-api.md).
|
|
131
|
+
|
|
132
|
+
## Integration
|
|
133
|
+
|
|
134
|
+
| Consumer | Relationship |
|
|
135
|
+
|---|---|
|
|
136
|
+
| `<consumer package>` | <required / optional / capability injection> |
|
|
137
|
+
|
|
138
|
+
Dependencies must point from the consumer to the provider. Do not document
|
|
139
|
+
private-field access as an integration API.
|
|
140
|
+
|
|
141
|
+
## Compatibility
|
|
142
|
+
|
|
143
|
+
| Identifier | Value | Stability |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| Product name | `TurboWarp-<Extension-Name>` | Human-facing |
|
|
146
|
+
| Repository | `kubohiroya/<repository>` | Current source location |
|
|
147
|
+
| npm package | `@kubohiroya/<package>` | Public package contract |
|
|
148
|
+
| Extension ID | `<extensionid>` | Stored in SB3; migration required to change |
|
|
149
|
+
|
|
150
|
+
Use hyphens between every component of a multi-word TurboWarp extension name,
|
|
151
|
+
for example `TurboWarp-Asset-Manager`, `TurboWarp-Runtime-Expression`, and
|
|
152
|
+
`TurboWarp-Diagnostic-Overlay`.
|
|
153
|
+
|
|
154
|
+
Document schema-aware migrations when an extension ID or opcode changes. Never
|
|
155
|
+
instruct users to replace JavaScript alone when stored identifiers have changed.
|
|
156
|
+
|
|
157
|
+
## Development
|
|
158
|
+
|
|
159
|
+
Use Node.js `<minimum version>` and the pnpm version declared by
|
|
160
|
+
`packageManager`.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
corepack enable
|
|
164
|
+
pnpm install --frozen-lockfile
|
|
165
|
+
pnpm check
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Useful commands:
|
|
169
|
+
|
|
170
|
+
| Command | Purpose |
|
|
171
|
+
|---|---|
|
|
172
|
+
| `pnpm dev` | Rebuild while source files change |
|
|
173
|
+
| `pnpm test` | Run tests |
|
|
174
|
+
| `pnpm docs` | Regenerate block documentation |
|
|
175
|
+
| `pnpm check:dist` | Verify committed build artifacts |
|
|
176
|
+
| `pnpm pack:check` | Inspect the npm package |
|
|
177
|
+
| `pnpm release:check` | Dry-run the release |
|
|
178
|
+
|
|
179
|
+
## Release
|
|
180
|
+
|
|
181
|
+
1. Update `CHANGELOG.md` when present.
|
|
182
|
+
2. Update the version in `package.json`.
|
|
183
|
+
3. Run `pnpm check` and the repository release checks.
|
|
184
|
+
4. Merge the release PR.
|
|
185
|
+
5. Create tag `v<version>` on the verified commit.
|
|
186
|
+
6. Publish the same version to npm when applicable.
|
|
187
|
+
7. Verify GitHub Release, npm tarball, GitHub Pages, `docsURI`, block icon, and CDN artifacts.
|
|
188
|
+
|
|
189
|
+
The release consistency check should verify every exact-version README and CDN
|
|
190
|
+
example, and should ensure that `docsURI` still resolves to the English Pages
|
|
191
|
+
site and `blockIconURI` remains a `data:image/svg+xml;base64,...` URI.
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
[Mozilla Public License 2.0](LICENSE) (SPDX: `MPL-2.0`).
|
|
196
|
+
|
|
197
|
+
Third-party components, when present, are listed in
|
|
198
|
+
[`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md).
|