@snap-rail/plugin-kit 0.2.0 → 0.2.2
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/lib/build.js +11 -4
- package/lib/index.js +2 -2
- package/lib/manifest.js +21 -2
- package/lib/pack.js +76 -13
- package/lib/seeds.js +1 -0
- package/lib/types/build.d.ts +22 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/manifest.d.ts +18 -0
- package/lib/types/pack.d.ts +47 -0
- package/package.json +17 -2
package/lib/build.js
CHANGED
|
@@ -156,7 +156,8 @@ function hostBundle(entry) {
|
|
|
156
156
|
platform: "node",
|
|
157
157
|
target: "es2024",
|
|
158
158
|
dts: false,
|
|
159
|
-
clean: false
|
|
159
|
+
clean: false,
|
|
160
|
+
fixedExtension: false
|
|
160
161
|
});
|
|
161
162
|
}
|
|
162
163
|
/**
|
|
@@ -172,7 +173,13 @@ function hostBundle(entry) {
|
|
|
172
173
|
* subpath to its module namespace, and the shell seeds
|
|
173
174
|
* `<package>/<subpath>` ids from that map after requiring the face.
|
|
174
175
|
*
|
|
175
|
-
*
|
|
176
|
+
* A package that renders in more than one window calls this once per surface:
|
|
177
|
+
* the main surface keeps the package name as its module id and `client` as its
|
|
178
|
+
* file name, while an auxiliary surface passes `outName` and `moduleId` so its
|
|
179
|
+
* bundle lands beside the main one (`lib-client/ball.js`) and registers as
|
|
180
|
+
* `<package>/ball` — the id that surface's window requires.
|
|
181
|
+
*
|
|
182
|
+
* @param packageName - the plugin's npm name (asset URLs and the default module id).
|
|
176
183
|
* @param entry - the renderer entry source file.
|
|
177
184
|
* @param options - see {@link ClientBundleOptions}.
|
|
178
185
|
*/
|
|
@@ -190,7 +197,7 @@ function clientBundle(packageName, entry, options = {}) {
|
|
|
190
197
|
}
|
|
191
198
|
};
|
|
192
199
|
return defineConfig({
|
|
193
|
-
entry: { client: entry },
|
|
200
|
+
entry: { [options.outName ?? "client"]: entry },
|
|
194
201
|
outDir: "lib-client",
|
|
195
202
|
format: ["cjs"],
|
|
196
203
|
platform: "browser",
|
|
@@ -207,7 +214,7 @@ function clientBundle(packageName, entry, options = {}) {
|
|
|
207
214
|
seedImportGuard()
|
|
208
215
|
],
|
|
209
216
|
outputOptions: {
|
|
210
|
-
banner: `window.__ModuleLoader__.load({ id: ${JSON.stringify(packageName)}, factory: function (require, module, exports) {`,
|
|
217
|
+
banner: `window.__ModuleLoader__.load({ id: ${JSON.stringify(options.moduleId ?? packageName)}, factory: function (require, module, exports) {`,
|
|
211
218
|
footer: "return module.exports && module.exports.__esModule === true && \"default\" in module.exports ? module.exports.default : module.exports; } });"
|
|
212
219
|
}
|
|
213
220
|
});
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CLIENT_EXTERNALS, CONTRIBUTED_SEED_MODULES, SEED_ABI_REVISION, SEED_MODULES, checkSeedAbi } from "./seeds.js";
|
|
2
2
|
import { clientBundle, hostBundle, seedImportGuard } from "./build.js";
|
|
3
3
|
import { PLUGIN_KINDS, clientFaceSchema, seedAbiSchema, snapRailManifestSchema } from "./manifest.js";
|
|
4
|
-
import { releaseFiles, releaseManifest, zipFileName } from "./pack.js";
|
|
4
|
+
import { catalogEntryFromZip, releaseFiles, releaseManifest, zipFileName } from "./pack.js";
|
|
5
5
|
import { packageNameOf, recipeDocumentSchema, recipePackageNames, recipeRowSchema } from "./recipe.js";
|
|
6
6
|
import { RELEASE_METADATA_PATH, RELEASE_SIGNATURE_PATH, canonicalJson, releaseMetadata, sha256, signReleaseFiles, signReleaseMetadata, verifyReleaseFiles } from "./sign.js";
|
|
7
7
|
//#region lib/types/slot-vocabulary.js
|
|
@@ -40,4 +40,4 @@ function defineSlotVocabulary(ids, options) {
|
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
//#endregion
|
|
43
|
-
export { CLIENT_EXTERNALS, CONTRIBUTED_SEED_MODULES, CUSTOM_SLOT_PATTERN, PLUGIN_KINDS, RELEASE_METADATA_PATH, RELEASE_SIGNATURE_PATH, SEED_ABI_REVISION, SEED_MODULES, canonicalJson, checkSeedAbi, clientBundle, clientFaceSchema, defineSlotVocabulary, hostBundle, packageNameOf, recipeDocumentSchema, recipePackageNames, recipeRowSchema, releaseFiles, releaseManifest, releaseMetadata, seedAbiSchema, seedImportGuard, sha256, signReleaseFiles, signReleaseMetadata, snapRailManifestSchema, verifyReleaseFiles, zipFileName };
|
|
43
|
+
export { CLIENT_EXTERNALS, CONTRIBUTED_SEED_MODULES, CUSTOM_SLOT_PATTERN, PLUGIN_KINDS, RELEASE_METADATA_PATH, RELEASE_SIGNATURE_PATH, SEED_ABI_REVISION, SEED_MODULES, canonicalJson, catalogEntryFromZip, checkSeedAbi, clientBundle, clientFaceSchema, defineSlotVocabulary, hostBundle, packageNameOf, recipeDocumentSchema, recipePackageNames, recipeRowSchema, releaseFiles, releaseManifest, releaseMetadata, seedAbiSchema, seedImportGuard, sha256, signReleaseFiles, signReleaseMetadata, snapRailManifestSchema, verifyReleaseFiles, zipFileName };
|
package/lib/manifest.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { AUX_SURFACE_IDS } from "@snap-rail/protocol";
|
|
2
3
|
import { PLUGIN_LAYERS } from "@snap-rail/util/manifest";
|
|
3
4
|
//#region lib/types/manifest.js
|
|
4
5
|
/**
|
|
@@ -25,11 +26,29 @@ const seedAbiSchema = z.object({
|
|
|
25
26
|
/** Base-package versions of the seeds the bundle requires, by seed id. */
|
|
26
27
|
versions: z.record(z.string().min(1), z.string().min(1))
|
|
27
28
|
}).strict();
|
|
29
|
+
/**
|
|
30
|
+
* The auxiliary-surface entry map: one client bundle per extra surface the
|
|
31
|
+
* package renders in. Keys are the auxiliary surface ids (`ball`) —
|
|
32
|
+
* `main` is deliberately absent, since the main window's bundle is
|
|
33
|
+
* `client.entry` and a package gets exactly one entry per window. Every
|
|
34
|
+
* declared path is a CJS factory bundle inside the package, packed and
|
|
35
|
+
* validated like the main entry; the surfaces a package does not declare
|
|
36
|
+
* simply have no face and show the kernel's plain fallback.
|
|
37
|
+
*/
|
|
38
|
+
const clientSurfacesSchema = z.partialRecord(z.enum(AUX_SURFACE_IDS), z.string().min(1));
|
|
28
39
|
/** The renderer face declaration: where the client bundle lives. */
|
|
29
40
|
const clientFaceSchema = z.object({
|
|
30
|
-
/** Path of the CJS factory bundle inside the package (from its root)
|
|
41
|
+
/** Path of the CJS factory bundle inside the package (from its root) —
|
|
42
|
+
* the **main** surface's entry. */
|
|
31
43
|
entry: z.string().min(1).default("lib/client.js"),
|
|
32
44
|
/**
|
|
45
|
+
* Extra surfaces this package renders in: one client bundle per auxiliary
|
|
46
|
+
* window (`ball`). Each bundle is loaded into that surface's own
|
|
47
|
+
* window with the same seed table, and its module id is
|
|
48
|
+
* `<package>/<surface>` (the main entry keeps the plain package name).
|
|
49
|
+
*/
|
|
50
|
+
surfaces: clientSurfacesSchema.optional(),
|
|
51
|
+
/**
|
|
33
52
|
* Subpaths this client face registers as contributed seeds (e.g. framework
|
|
34
53
|
* primitives like `./ui`): each becomes a `__ModuleLoader__` id other
|
|
35
54
|
* plugins' client bundles may `require` — same instance, one execution.
|
|
@@ -119,4 +138,4 @@ const snapRailManifestSchema = z.object({
|
|
|
119
138
|
}).strict().optional()
|
|
120
139
|
});
|
|
121
140
|
//#endregion
|
|
122
|
-
export { PLUGIN_KINDS, PLUGIN_LAYERS, clientFaceSchema, seedAbiSchema, snapRailManifestSchema };
|
|
141
|
+
export { PLUGIN_KINDS, PLUGIN_LAYERS, clientFaceSchema, clientSurfacesSchema, seedAbiSchema, snapRailManifestSchema };
|
package/lib/pack.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { CLIENT_EXTERNALS } from "./seeds.js";
|
|
2
|
+
import { clientSurfacesSchema } from "./manifest.js";
|
|
2
3
|
import { createRequire } from "node:module";
|
|
3
4
|
import { readFile, readdir } from "node:fs/promises";
|
|
4
5
|
import { join, relative } from "node:path";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
import { unzipSync } from "fflate";
|
|
5
8
|
//#region lib/types/pack.js
|
|
6
9
|
/**
|
|
7
10
|
* Final-format assembly: turn a built plugin package into the file set of
|
|
@@ -62,22 +65,25 @@ function basePackageOf(seedId) {
|
|
|
62
65
|
return seedId.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
63
66
|
}
|
|
64
67
|
/**
|
|
65
|
-
* Stamp the seed ABI a built client
|
|
66
|
-
*
|
|
67
|
-
* the only bare requires the welded CJS output carries) plus
|
|
68
|
-
* base-package version resolved from the package's own dependency
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
68
|
+
* Stamp the seed ABI a set of built client faces were built against: the
|
|
69
|
+
* seeds their bundles actually require (a `require("…")` literal scan —
|
|
70
|
+
* externals are the only bare requires the welded CJS output carries) plus
|
|
71
|
+
* each seed's base-package version resolved from the package's own dependency
|
|
72
|
+
* tree. A multi-surface package stamps ONE declaration for all its entries
|
|
73
|
+
* (the union of their requires): every surface shares the shell's seed table,
|
|
74
|
+
* so a per-face stamp would only repeat the same verdict N times. Ids whose
|
|
75
|
+
* base package cannot be resolved here are omitted — the runtime check
|
|
76
|
+
* compares only the intersection, so an omitted id is unchecked, never
|
|
77
|
+
* failed. Returns `undefined` when the bundles require no seeds or none could
|
|
78
|
+
* be versioned (nothing to enforce).
|
|
73
79
|
*
|
|
74
80
|
* @param packageDir - the package being packed (version resolution root).
|
|
75
|
-
* @param
|
|
81
|
+
* @param bundleCodes - the built client bundles' source texts, one per surface.
|
|
76
82
|
*/
|
|
77
|
-
async function seedAbiStamp(packageDir,
|
|
83
|
+
async function seedAbiStamp(packageDir, bundleCodes) {
|
|
78
84
|
const seeds = new Set(CLIENT_EXTERNALS);
|
|
79
85
|
const required = /* @__PURE__ */ new Set();
|
|
80
|
-
for (const match of bundleCode.matchAll(/require\(\s*(['"])([^'"\n]+)\1\s*\)/g)) {
|
|
86
|
+
for (const bundleCode of bundleCodes) for (const match of bundleCode.matchAll(/require\(\s*(['"])([^'"\n]+)\1\s*\)/g)) {
|
|
81
87
|
const id = match[2];
|
|
82
88
|
if (id !== void 0 && seeds.has(id)) required.add(id);
|
|
83
89
|
}
|
|
@@ -135,6 +141,12 @@ async function releaseFiles(spec) {
|
|
|
135
141
|
if (spec.hostFace !== void 0 && spec.hostDir === void 0) throw new Error(`pack: ${spec.name} declares a host face without a hostDir`);
|
|
136
142
|
if (spec.hostFace === void 0 && spec.clientFace === void 0) throw new Error(`pack: ${spec.name} ships neither a host face nor a client bundle`);
|
|
137
143
|
const clientEntry = typeof snapRail?.client?.entry === "string" ? snapRail.client.entry : "lib/client.js";
|
|
144
|
+
let clientSurfaces = {};
|
|
145
|
+
if (snapRail?.client?.surfaces !== void 0) {
|
|
146
|
+
const parsed = clientSurfacesSchema.safeParse(snapRail.client.surfaces);
|
|
147
|
+
if (!parsed.success) throw new Error(`pack: ${spec.name} declares an invalid snapRail.client.surfaces (${parsed.error.issues.map((issue) => issue.message).join("; ")})`);
|
|
148
|
+
clientSurfaces = parsed.data;
|
|
149
|
+
}
|
|
138
150
|
const main = spec.hostFace ?? clientEntry;
|
|
139
151
|
const files = {};
|
|
140
152
|
if (spec.hostDir !== void 0) {
|
|
@@ -144,7 +156,13 @@ async function releaseFiles(spec) {
|
|
|
144
156
|
if (spec.clientFace !== void 0) Object.assign(files, await collectDir(join(spec.dir, spec.clientFace), spec.dir));
|
|
145
157
|
if (spec.hostFace !== void 0 && files[spec.hostFace] === void 0) throw new Error(`pack: ${spec.name} is missing its host face ${spec.hostFace} under ${String(spec.hostDir)}`);
|
|
146
158
|
if (declaredClient && files[clientEntry] === void 0) throw new Error(`pack: ${spec.name} declares client entry ${clientEntry} but the zip does not carry it`);
|
|
147
|
-
const
|
|
159
|
+
for (const [surface, entry] of Object.entries(clientSurfaces)) {
|
|
160
|
+
if (entry === void 0) continue;
|
|
161
|
+
if (files[entry] === void 0) throw new Error(`pack: ${spec.name} declares the ${surface} surface entry ${entry} but the zip does not carry it`);
|
|
162
|
+
if (entry === clientEntry) throw new Error(`pack: ${spec.name} points the ${surface} surface at the main client entry ${entry} — each surface needs its own bundle`);
|
|
163
|
+
}
|
|
164
|
+
const clientBundles = [clientEntry, ...Object.values(clientSurfaces)].filter((entry) => typeof entry === "string" && files[entry] !== void 0).map((entry) => new TextDecoder().decode(files[entry]));
|
|
165
|
+
const seedAbi = declaredClient && clientBundles.length > 0 ? await seedAbiStamp(spec.dir, clientBundles) : void 0;
|
|
148
166
|
files["package.json"] = new TextEncoder().encode(releaseManifest(source, main, spec.exports, seedAbi));
|
|
149
167
|
for (const [subpath, target] of Object.entries(spec.exports ?? {})) if (files[target] === void 0) throw new Error(`pack: ${spec.name} exports ${subpath} to ${target} but the zip does not carry it`);
|
|
150
168
|
for (const path of Object.keys(files)) if (path.startsWith("src/")) throw new Error(`pack: ${spec.name} would ship a source file (${path})`);
|
|
@@ -154,5 +172,50 @@ async function releaseFiles(spec) {
|
|
|
154
172
|
function zipFileName(packageName) {
|
|
155
173
|
return `${packageName.replaceAll("/", "__")}.zip`;
|
|
156
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* The one feed-entry emitter every producer shares: read a plugin zip's own
|
|
177
|
+
* bytes back (release manifest + signature envelope), derive the
|
|
178
|
+
* `index.json` entry from exactly what the zip carries, and digest the exact
|
|
179
|
+
* bytes the client will download — the feed can never advertise something a
|
|
180
|
+
* zip does not. The repo's pack/collect pipeline and the standalone
|
|
181
|
+
* `snap-rail-plugin feed` command all converge here.
|
|
182
|
+
*
|
|
183
|
+
* @param zipped - the exact zip bytes the feed will serve.
|
|
184
|
+
* @param file - the pure file name the entry advertises (no path).
|
|
185
|
+
* @param fallbackName - the entry's name when the manifest carries none.
|
|
186
|
+
* @param options.requireSignature - refuse unsigned zips (feed policy).
|
|
187
|
+
* @param options.repository - an optional source-repository URL.
|
|
188
|
+
* @returns the entry, unknown fields stripped by construction.
|
|
189
|
+
* @throws when the zip has no readable manifest, or no signature while
|
|
190
|
+
* `requireSignature` is set.
|
|
191
|
+
*/
|
|
192
|
+
function catalogEntryFromZip(zipped, file, fallbackName, options = {}) {
|
|
193
|
+
const files = unzipSync(zipped);
|
|
194
|
+
const manifestFile = files["package.json"];
|
|
195
|
+
if (manifestFile === void 0) throw new Error(`catalog: ${file} 缺少 package.json`);
|
|
196
|
+
const manifest = JSON.parse(new TextDecoder().decode(manifestFile));
|
|
197
|
+
const signatureFile = files["META-INF/signature.json"];
|
|
198
|
+
if (signatureFile === void 0 && options.requireSignature === true) throw new Error(`catalog: ${file} 没有签名(META-INF/signature.json 缺失)`);
|
|
199
|
+
const signature = signatureFile === void 0 ? void 0 : JSON.parse(new TextDecoder().decode(signatureFile));
|
|
200
|
+
return {
|
|
201
|
+
name: manifest.name ?? fallbackName,
|
|
202
|
+
version: manifest.version ?? "0.0.0",
|
|
203
|
+
...manifest.description !== void 0 && manifest.description !== "" ? { description: manifest.description } : {},
|
|
204
|
+
...manifest.snapRail?.kind !== void 0 ? { kind: manifest.snapRail.kind } : {},
|
|
205
|
+
...manifest.snapRail?.title !== void 0 ? { title: manifest.snapRail.title } : {},
|
|
206
|
+
...manifest.snapRail?.category !== void 0 ? { category: manifest.snapRail.category } : {},
|
|
207
|
+
...manifest.snapRail?.requires !== void 0 ? { requires: manifest.snapRail.requires } : {},
|
|
208
|
+
...manifest.snapRail?.permissions !== void 0 ? { permissions: manifest.snapRail.permissions } : {},
|
|
209
|
+
...manifest.snapRail?.engines !== void 0 ? { engines: manifest.snapRail.engines } : {},
|
|
210
|
+
file,
|
|
211
|
+
sha256: createHash("sha256").update(zipped).digest("hex"),
|
|
212
|
+
size: zipped.length,
|
|
213
|
+
...signature === void 0 ? {} : { signature: {
|
|
214
|
+
algorithm: signature.algorithm,
|
|
215
|
+
keyId: signature.keyId
|
|
216
|
+
} },
|
|
217
|
+
...options.repository === void 0 ? {} : { repository: options.repository }
|
|
218
|
+
};
|
|
219
|
+
}
|
|
157
220
|
//#endregion
|
|
158
|
-
export { releaseFiles, releaseManifest, zipFileName };
|
|
221
|
+
export { catalogEntryFromZip, releaseFiles, releaseManifest, zipFileName };
|
package/lib/seeds.js
CHANGED
package/lib/types/build.d.ts
CHANGED
|
@@ -50,6 +50,21 @@ export interface ClientBundleOptions {
|
|
|
50
50
|
* by the entry, so the bundle carries its own styling.
|
|
51
51
|
*/
|
|
52
52
|
css?: readonly string[] | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* The emitted file's base name inside `lib-client` (default `client`, so
|
|
55
|
+
* the file is `lib-client/client.js`). Auxiliary surfaces build one bundle
|
|
56
|
+
* per window and name them after the surface (`ball`); the
|
|
57
|
+
* manifest's `client.surfaces` points at those files.
|
|
58
|
+
*/
|
|
59
|
+
outName?: string | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* The module id the bundle registers under (default: the package name).
|
|
62
|
+
* A package's **main** surface registers as the package itself, so
|
|
63
|
+
* `createClientRuntime` can require it by row name; an auxiliary surface
|
|
64
|
+
* registers as `<package>/<surface>` — one package, several windows, no
|
|
65
|
+
* two bundles fighting over one id.
|
|
66
|
+
*/
|
|
67
|
+
moduleId?: string | undefined;
|
|
53
68
|
}
|
|
54
69
|
/**
|
|
55
70
|
* The client face: a CJS browser bundle welded into the module-loader
|
|
@@ -64,7 +79,13 @@ export interface ClientBundleOptions {
|
|
|
64
79
|
* subpath to its module namespace, and the shell seeds
|
|
65
80
|
* `<package>/<subpath>` ids from that map after requiring the face.
|
|
66
81
|
*
|
|
67
|
-
*
|
|
82
|
+
* A package that renders in more than one window calls this once per surface:
|
|
83
|
+
* the main surface keeps the package name as its module id and `client` as its
|
|
84
|
+
* file name, while an auxiliary surface passes `outName` and `moduleId` so its
|
|
85
|
+
* bundle lands beside the main one (`lib-client/ball.js`) and registers as
|
|
86
|
+
* `<package>/ball` — the id that surface's window requires.
|
|
87
|
+
*
|
|
88
|
+
* @param packageName - the plugin's npm name (asset URLs and the default module id).
|
|
68
89
|
* @param entry - the renderer entry source file.
|
|
69
90
|
* @param options - see {@link ClientBundleOptions}.
|
|
70
91
|
*/
|
package/lib/types/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export { SEED_MODULES } from './seeds.ts';
|
|
|
12
12
|
export { CLIENT_EXTERNALS, CONTRIBUTED_SEED_MODULES, SEED_ABI_REVISION, checkSeedAbi, type SeedAbi, type SeedAbiVerdict, } from './seeds.ts';
|
|
13
13
|
export { clientBundle, hostBundle, seedImportGuard } from './build.ts';
|
|
14
14
|
export { CUSTOM_SLOT_PATTERN, defineSlotVocabulary, type SlotIdOf, type SlotVocabulary, } from './slot-vocabulary.ts';
|
|
15
|
-
export { releaseFiles, releaseManifest, zipFileName, type ReleaseSpec } from './pack.ts';
|
|
15
|
+
export { catalogEntryFromZip, releaseFiles, releaseManifest, zipFileName, type FeedEntry, type ReleaseSpec } from './pack.ts';
|
|
16
16
|
export { packageNameOf, recipeDocumentSchema, recipePackageNames, recipeRowSchema, type RecipeDocument, type RecipeRow, } from './recipe.ts';
|
|
17
17
|
export { RELEASE_METADATA_PATH, RELEASE_SIGNATURE_PATH, canonicalJson, releaseMetadata, sha256, signReleaseFiles, signReleaseMetadata, verifyReleaseFiles, type ReleaseFileDigests, type ReleaseMetadata, type ReleaseSignature, type TrustedReleaseKey, } from './sign.ts';
|
|
18
18
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/types/manifest.d.ts
CHANGED
|
@@ -19,9 +19,24 @@ export declare const seedAbiSchema: z.ZodObject<{
|
|
|
19
19
|
revision: z.ZodNumber;
|
|
20
20
|
versions: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
21
21
|
}, z.core.$strict>;
|
|
22
|
+
/**
|
|
23
|
+
* The auxiliary-surface entry map: one client bundle per extra surface the
|
|
24
|
+
* package renders in. Keys are the auxiliary surface ids (`ball`) —
|
|
25
|
+
* `main` is deliberately absent, since the main window's bundle is
|
|
26
|
+
* `client.entry` and a package gets exactly one entry per window. Every
|
|
27
|
+
* declared path is a CJS factory bundle inside the package, packed and
|
|
28
|
+
* validated like the main entry; the surfaces a package does not declare
|
|
29
|
+
* simply have no face and show the kernel's plain fallback.
|
|
30
|
+
*/
|
|
31
|
+
export declare const clientSurfacesSchema: z.ZodRecord<z.ZodEnum<{
|
|
32
|
+
ball: "ball";
|
|
33
|
+
}> & z.core.$partial, z.ZodString>;
|
|
22
34
|
/** The renderer face declaration: where the client bundle lives. */
|
|
23
35
|
export declare const clientFaceSchema: z.ZodObject<{
|
|
24
36
|
entry: z.ZodDefault<z.ZodString>;
|
|
37
|
+
surfaces: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
|
|
38
|
+
ball: "ball";
|
|
39
|
+
}> & z.core.$partial, z.ZodString>>;
|
|
25
40
|
seeds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
26
41
|
externalSeeds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
27
42
|
seedAbi: z.ZodOptional<z.ZodObject<{
|
|
@@ -52,6 +67,9 @@ export declare const snapRailManifestSchema: z.ZodObject<{
|
|
|
52
67
|
clientOnly: z.ZodOptional<z.ZodBoolean>;
|
|
53
68
|
client: z.ZodOptional<z.ZodObject<{
|
|
54
69
|
entry: z.ZodDefault<z.ZodString>;
|
|
70
|
+
surfaces: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
|
|
71
|
+
ball: "ball";
|
|
72
|
+
}> & z.core.$partial, z.ZodString>>;
|
|
55
73
|
seeds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
56
74
|
externalSeeds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
57
75
|
seedAbi: z.ZodOptional<z.ZodObject<{
|
package/lib/types/pack.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ interface SourceManifest {
|
|
|
20
20
|
clientOnly?: unknown;
|
|
21
21
|
client?: {
|
|
22
22
|
entry?: unknown;
|
|
23
|
+
surfaces?: unknown;
|
|
23
24
|
} | undefined;
|
|
24
25
|
} & Record<string, unknown>;
|
|
25
26
|
}
|
|
@@ -75,5 +76,51 @@ export declare function releaseManifest(source: SourceManifest, main: string, ex
|
|
|
75
76
|
export declare function releaseFiles(spec: ReleaseSpec): Promise<Record<string, Uint8Array>>;
|
|
76
77
|
/** The zip file name a package's release takes under dist-plugins/. */
|
|
77
78
|
export declare function zipFileName(packageName: string): string;
|
|
79
|
+
/** One plugin entry the feed's `index.json` carries (feed facts only). */
|
|
80
|
+
export interface FeedEntry {
|
|
81
|
+
name: string;
|
|
82
|
+
version: string;
|
|
83
|
+
description?: string;
|
|
84
|
+
kind?: string;
|
|
85
|
+
title?: string;
|
|
86
|
+
category?: string;
|
|
87
|
+
requires?: string[];
|
|
88
|
+
permissions?: string[];
|
|
89
|
+
engines?: {
|
|
90
|
+
snapRail?: string;
|
|
91
|
+
hostProtocol?: string;
|
|
92
|
+
renderer?: string;
|
|
93
|
+
};
|
|
94
|
+
/** The pure file name the entry advertises (no path). */
|
|
95
|
+
file: string;
|
|
96
|
+
sha256: string;
|
|
97
|
+
size: number;
|
|
98
|
+
signature?: {
|
|
99
|
+
algorithm: 'Ed25519';
|
|
100
|
+
keyId: string;
|
|
101
|
+
};
|
|
102
|
+
repository?: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The one feed-entry emitter every producer shares: read a plugin zip's own
|
|
106
|
+
* bytes back (release manifest + signature envelope), derive the
|
|
107
|
+
* `index.json` entry from exactly what the zip carries, and digest the exact
|
|
108
|
+
* bytes the client will download — the feed can never advertise something a
|
|
109
|
+
* zip does not. The repo's pack/collect pipeline and the standalone
|
|
110
|
+
* `snap-rail-plugin feed` command all converge here.
|
|
111
|
+
*
|
|
112
|
+
* @param zipped - the exact zip bytes the feed will serve.
|
|
113
|
+
* @param file - the pure file name the entry advertises (no path).
|
|
114
|
+
* @param fallbackName - the entry's name when the manifest carries none.
|
|
115
|
+
* @param options.requireSignature - refuse unsigned zips (feed policy).
|
|
116
|
+
* @param options.repository - an optional source-repository URL.
|
|
117
|
+
* @returns the entry, unknown fields stripped by construction.
|
|
118
|
+
* @throws when the zip has no readable manifest, or no signature while
|
|
119
|
+
* `requireSignature` is set.
|
|
120
|
+
*/
|
|
121
|
+
export declare function catalogEntryFromZip(zipped: Uint8Array, file: string, fallbackName: string, options?: {
|
|
122
|
+
requireSignature?: boolean;
|
|
123
|
+
repository?: string;
|
|
124
|
+
}): FeedEntry;
|
|
78
125
|
export {};
|
|
79
126
|
//# sourceMappingURL=pack.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snap-rail/plugin-kit",
|
|
3
|
-
"description": "The plugin author's kit: snapRail manifest vocabulary, the renderer seed whitelist,
|
|
4
|
-
"version": "0.2.
|
|
3
|
+
"description": "The plugin author's kit: snapRail manifest vocabulary, the renderer seed whitelist, tsdown presets for installable host + client faces, release signing, and the final-format packer",
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
8
|
+
"homepage": "https://www.snap-rail.top/dev.html",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/yansijian/snap-rail.git",
|
|
12
|
+
"directory": "packages/base/plugin-kit"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"snap-rail",
|
|
16
|
+
"plugin",
|
|
17
|
+
"cordis",
|
|
18
|
+
"industrial",
|
|
19
|
+
"electron"
|
|
20
|
+
],
|
|
8
21
|
"exports": {
|
|
9
22
|
".": {
|
|
10
23
|
"types": "./lib/types/index.d.ts",
|
|
@@ -43,7 +56,9 @@
|
|
|
43
56
|
],
|
|
44
57
|
"license": "Apache-2.0",
|
|
45
58
|
"dependencies": {
|
|
59
|
+
"fflate": "^0.8.3",
|
|
46
60
|
"zod": "^4.4.3",
|
|
61
|
+
"@snap-rail/protocol": "^0.1.3",
|
|
47
62
|
"@snap-rail/util": "^0.1.2"
|
|
48
63
|
},
|
|
49
64
|
"peerDependencies": {
|