@kubohiroya/turbowarp-extension-loader 0.5.1 → 0.6.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 +6 -0
- package/README.md +7 -0
- package/dist/project-authoring.d.ts +10 -0
- package/dist/project-authoring.js +5 -3
- package/dist/project-catalog.d.ts +10 -0
- package/dist/project-catalog.js +5 -3
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -223,6 +223,12 @@ Manifest は `uri` を持てます。その作品をダウンロードできる
|
|
|
223
223
|
するたびに変わるので、ブラウザーのキャッシュも開く人の選択の記録も、触るたびに同じ作品を別名で
|
|
224
224
|
綴じてしまいます。住所は中身が変わっても変わりません。
|
|
225
225
|
|
|
226
|
+
`prepareCatalogExtensionChanges` と `prepareExtensionAddition` は `uri` を受け取ります。作品が
|
|
227
|
+
最初の住所を得るのも、作者が選んだ住所へ移るのも、この経路です。何も言わなければ作品が持って
|
|
228
|
+
いる住所がそのまま残り、`readProjectAuthoringState` がその住所を報告するので、ホストは初回保存
|
|
229
|
+
かどうかを決める前に見分けられます。割り当てるかどうかはホストの判断です。作者が住所を選んだ
|
|
230
|
+
のか、既定が必要な初回保存なのかを知っているのはホストだけだからです。
|
|
231
|
+
|
|
226
232
|
`./project-authoring` の `createProjectUUID` と `defaultProjectURI` が、作者が住所を選んで
|
|
227
233
|
いないときに与える住所を組みます。
|
|
228
234
|
|
package/README.md
CHANGED
|
@@ -226,6 +226,13 @@ fingerprint changes on every save, so a browser cache and a record of what the o
|
|
|
226
226
|
file one work under a new name each time it was touched. An address does not change when the bytes
|
|
227
227
|
do.
|
|
228
228
|
|
|
229
|
+
`prepareCatalogExtensionChanges` and `prepareExtensionAddition` take a `uri` option, which is how
|
|
230
|
+
a project gets its first address and how it moves to one the author chose. Saying nothing keeps
|
|
231
|
+
the address the project already has, and `readProjectAuthoringState` reports that address, so a
|
|
232
|
+
host can tell a first save from a later one before deciding. Whether to assign one is the host's
|
|
233
|
+
call: only the host knows whether the author chose an address or whether this is a first save
|
|
234
|
+
that needs a default.
|
|
235
|
+
|
|
229
236
|
`createProjectUUID` and `defaultProjectURI` in `./project-authoring` build the address a project
|
|
230
237
|
is given when its author has not chosen one:
|
|
231
238
|
|
|
@@ -23,6 +23,16 @@ export interface ExtensionAdditionOptions {
|
|
|
23
23
|
signal?: AbortSignal;
|
|
24
24
|
loaderOptions?: LoaderOptions;
|
|
25
25
|
archiveOptions?: ArchiveLimits;
|
|
26
|
+
/**
|
|
27
|
+
* The address to give this project, or nothing to keep the one it has.
|
|
28
|
+
*
|
|
29
|
+
* Whether to assign one is the host's call, not this function's: only the host knows whether
|
|
30
|
+
* the author chose an address or whether this is a first save that needs a default. So this
|
|
31
|
+
* sets what it is given and leaves the existing address alone when it is given nothing —
|
|
32
|
+
* `readProjectAuthoringState` reports what a project already carries, so the host can tell
|
|
33
|
+
* the two apart before deciding.
|
|
34
|
+
*/
|
|
35
|
+
uri?: string;
|
|
26
36
|
}
|
|
27
37
|
export interface PreparedExtensionAddition {
|
|
28
38
|
readonly projectBytes: Uint8Array;
|
|
@@ -21,7 +21,7 @@ function snapshotProject(input) {
|
|
|
21
21
|
* Add a file-backed Embedded or URL-backed Remote extension without evaluating it.
|
|
22
22
|
* A host approval is required before the candidate is returned; the original is untouched.
|
|
23
23
|
*/
|
|
24
|
-
export async function prepareExtensionAddition(input, request, ports, { zip: Zip, signal: externalSignal, loaderOptions = {}, archiveOptions } = {}) {
|
|
24
|
+
export async function prepareExtensionAddition(input, request, ports, { zip: Zip, signal: externalSignal, loaderOptions = {}, archiveOptions, uri } = {}) {
|
|
25
25
|
if (typeof Zip !== 'function' || typeof Zip.loadAsync !== 'function')
|
|
26
26
|
throw new TypeError('A compatible JSZip constructor is required');
|
|
27
27
|
if (!plainObject(request) || Object.keys(request).some(key => !['id', 'source', 'bytes', 'execution'].includes(key))) {
|
|
@@ -95,8 +95,10 @@ export async function prepareExtensionAddition(input, request, ports, { zip: Zip
|
|
|
95
95
|
fail('INVALID_PROJECT', 'Project extensionURLs must be an object');
|
|
96
96
|
const previous = prepared.manifest?.extensions || [];
|
|
97
97
|
// Carried through every rebuild below: an address that changed when an extension was
|
|
98
|
-
// added would not be an address.
|
|
99
|
-
|
|
98
|
+
// added would not be an address. The host may set one, which is how a project gets its
|
|
99
|
+
// first; saying nothing keeps what is there.
|
|
100
|
+
const address = uri ?? prepared.manifest?.uri;
|
|
101
|
+
const carried = address === undefined ? {} : { uri: address };
|
|
100
102
|
if (previous.some(spec => spec.id === draft.id) || (project.extensions || []).includes(draft.id) ||
|
|
101
103
|
Object.hasOwn(project.extensionURLs || {}, draft.id))
|
|
102
104
|
fail('DUPLICATE_EXTENSION', 'This extension ID already belongs to the project', draft.id);
|
|
@@ -23,6 +23,16 @@ export interface CatalogOptions {
|
|
|
23
23
|
signal?: AbortSignal;
|
|
24
24
|
loaderOptions?: LoaderOptions;
|
|
25
25
|
archiveOptions?: ArchiveLimits;
|
|
26
|
+
/**
|
|
27
|
+
* The address to give this project, or nothing to keep the one it has.
|
|
28
|
+
*
|
|
29
|
+
* Whether to assign one is the host's call, not this function's: only the host knows whether
|
|
30
|
+
* the author chose an address or whether this is a first save that needs a default. So this
|
|
31
|
+
* sets what it is given and leaves the existing address alone when it is given nothing —
|
|
32
|
+
* `readProjectAuthoringState` reports what a project already carries, so the host can tell
|
|
33
|
+
* the two apart before deciding.
|
|
34
|
+
*/
|
|
35
|
+
uri?: string;
|
|
26
36
|
}
|
|
27
37
|
export type CatalogPorts = Pick<LoaderPorts, 'fetcher' | 'trustProvider' | 'cache' | 'diagnostics'>;
|
|
28
38
|
/** Read project-owned distribution requests; these records never grant execution permission. */
|
package/dist/project-catalog.js
CHANGED
|
@@ -19,7 +19,7 @@ export async function readProjectAuthoringState(input, { zip, signal, archiveOpt
|
|
|
19
19
|
const prepared = await io.prepare(input, { signal });
|
|
20
20
|
return prepared.manifest;
|
|
21
21
|
}
|
|
22
|
-
export async function prepareCatalogExtensionChanges(input, requests, ports, { zip: Zip, signal: externalSignal, loaderOptions = {}, archiveOptions } = {}) {
|
|
22
|
+
export async function prepareCatalogExtensionChanges(input, requests, ports, { zip: Zip, signal: externalSignal, loaderOptions = {}, archiveOptions, uri } = {}) {
|
|
23
23
|
const limits = { ...DEFAULT_LOADER_LIMITS, ...loaderOptions };
|
|
24
24
|
for (const key of ['maxExtensions', 'maxBytes', 'maxTotalBytes', 'timeoutMs']) {
|
|
25
25
|
if (!Number.isSafeInteger(limits[key]) || limits[key] < 1)
|
|
@@ -176,8 +176,10 @@ export async function prepareCatalogExtensionChanges(input, requests, ports, { z
|
|
|
176
176
|
// The project's own address survives a change to its extensions. Rebuilding the manifest
|
|
177
177
|
// without it would file the same work under a new name the next time it was saved, which
|
|
178
178
|
// is the one thing an address is for.
|
|
179
|
-
const
|
|
180
|
-
|
|
179
|
+
const address = uri ?? prepared.manifest?.uri;
|
|
180
|
+
const manifest = validateManifest({ schemaVersion: 1,
|
|
181
|
+
...(address === undefined ? {} : { uri: address }),
|
|
182
|
+
extensions: [...extensions.values()] }, loaderOptions);
|
|
181
183
|
for (const spec of manifest.extensions)
|
|
182
184
|
if (spec.source.type === 'embedded-file')
|
|
183
185
|
replacedPaths.delete(spec.source.path);
|