@sealpixel/core 1.0.0-rc.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.md +3 -0
- package/dist/assets.d.ts +37 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/assets.js +96 -0
- package/dist/assets.js.map +1 -0
- package/dist/canonical-json.d.ts +11 -0
- package/dist/canonical-json.d.ts.map +1 -0
- package/dist/canonical-json.js +48 -0
- package/dist/canonical-json.js.map +1 -0
- package/dist/compile.d.ts +30 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +64 -0
- package/dist/compile.js.map +1 -0
- package/dist/embedded.d.ts +13 -0
- package/dist/embedded.d.ts.map +1 -0
- package/dist/embedded.js +145 -0
- package/dist/embedded.js.map +1 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +19 -0
- package/dist/errors.js.map +1 -0
- package/dist/external-kernel.d.ts +13 -0
- package/dist/external-kernel.d.ts.map +1 -0
- package/dist/external-kernel.js +68 -0
- package/dist/external-kernel.js.map +1 -0
- package/dist/generated/geometry.d.ts +33 -0
- package/dist/generated/geometry.d.ts.map +1 -0
- package/dist/generated/geometry.js +3 -0
- package/dist/generated/geometry.js.map +1 -0
- package/dist/generated/output.d.ts +40 -0
- package/dist/generated/output.d.ts.map +1 -0
- package/dist/generated/output.js +3 -0
- package/dist/generated/output.js.map +1 -0
- package/dist/generated/plan.d.ts +292 -0
- package/dist/generated/plan.d.ts.map +1 -0
- package/dist/generated/plan.js +3 -0
- package/dist/generated/plan.js.map +1 -0
- package/dist/history.d.ts +16 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +38 -0
- package/dist/history.js.map +1 -0
- package/dist/image-size.d.ts +12 -0
- package/dist/image-size.d.ts.map +1 -0
- package/dist/image-size.js +86 -0
- package/dist/image-size.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/inline-renderer.d.ts +28 -0
- package/dist/inline-renderer.d.ts.map +1 -0
- package/dist/inline-renderer.js +48 -0
- package/dist/inline-renderer.js.map +1 -0
- package/dist/options.d.ts +13 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +35 -0
- package/dist/options.js.map +1 -0
- package/dist/plan.d.ts +16 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +15 -0
- package/dist/plan.js.map +1 -0
- package/dist/plugin.d.ts +200 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +2 -0
- package/dist/plugin.js.map +1 -0
- package/dist/registry.d.ts +58 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +187 -0
- package/dist/registry.js.map +1 -0
- package/dist/render.d.ts +25 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +164 -0
- package/dist/render.js.map +1 -0
- package/dist/renderer-core.d.ts +82 -0
- package/dist/renderer-core.d.ts.map +1 -0
- package/dist/renderer-core.js +228 -0
- package/dist/renderer-core.js.map +1 -0
- package/dist/renderer.d.ts +29 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +2 -0
- package/dist/renderer.js.map +1 -0
- package/dist/session.d.ts +188 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +513 -0
- package/dist/session.js.map +1 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/worker-protocol.d.ts +100 -0
- package/dist/worker-protocol.d.ts.map +1 -0
- package/dist/worker-protocol.js +30 -0
- package/dist/worker-protocol.js.map +1 -0
- package/dist/worker-renderer.d.ts +35 -0
- package/dist/worker-renderer.d.ts.map +1 -0
- package/dist/worker-renderer.js +85 -0
- package/dist/worker-renderer.js.map +1 -0
- package/dist/worker.d.ts +23 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +81 -0
- package/dist/worker.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type RgbaBuffer, type SealpixelWasm } from '@sealpixel/wasm';
|
|
2
|
+
import type { Renderer } from './renderer.js';
|
|
3
|
+
import { RendererCore, type RendererCoreOptions } from './renderer-core.js';
|
|
4
|
+
import type { SourceDescriptor } from './types.js';
|
|
5
|
+
/** Runs the renderer on the calling thread. Fallback where Workers are unavailable, and the server's renderer. */
|
|
6
|
+
export declare class InlineRenderer implements Renderer {
|
|
7
|
+
private core;
|
|
8
|
+
private readonly ready;
|
|
9
|
+
constructor(options: RendererCoreOptions & {
|
|
10
|
+
wasm?: SealpixelWasm;
|
|
11
|
+
});
|
|
12
|
+
init(): Promise<{
|
|
13
|
+
coreVersion: string;
|
|
14
|
+
plugins: Record<string, number>;
|
|
15
|
+
}>;
|
|
16
|
+
load(bytes: Uint8Array, id: string): Promise<import("./renderer-core.js").LoadedSource>;
|
|
17
|
+
loadProxy(proxy: RgbaBuffer, descriptor: SourceDescriptor): Promise<import("./renderer-core.js").LoadedSource>;
|
|
18
|
+
loadProxyEncoded(bytes: Uint8Array, descriptor: SourceDescriptor): Promise<import("./renderer-core.js").LoadedSource>;
|
|
19
|
+
render(req: Parameters<Renderer['render']>[0]): Promise<RgbaBuffer>;
|
|
20
|
+
export(req: Parameters<Renderer['export']>[0]): Promise<import("./renderer-core.js").ExportResult>;
|
|
21
|
+
geometry(req: Parameters<Renderer['geometry']>[0]): Promise<import("./plan.js").Geometry>;
|
|
22
|
+
unload(): Promise<void>;
|
|
23
|
+
putAsset(id: string, bytes: Uint8Array): Promise<void>;
|
|
24
|
+
dispose(): void;
|
|
25
|
+
/** For tests. */
|
|
26
|
+
coreForTest(): Promise<RendererCore>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=inline-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-renderer.d.ts","sourceRoot":"","sources":["../src/inline-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACzF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,kHAAkH;AAClH,qBAAa,cAAe,YAAW,QAAQ;IAC7C,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwB;gBAElC,OAAO,EAAE,mBAAmB,GAAG;QAAE,IAAI,CAAC,EAAE,aAAa,CAAA;KAAE;IAS7D,IAAI;;;;IAGJ,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM;IAGlC,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB;IAGzD,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB;IAGhE,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAG7C,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAG7C,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAGjD,MAAM;IAGN,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU;IAG5C,OAAO;IAGP,iBAAiB;IACX,WAAW,IAAI,OAAO,CAAC,YAAY,CAAC;CAG3C"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { loadSealpixelWasm } from '@sealpixel/wasm';
|
|
2
|
+
import { RendererCore } from './renderer-core.js';
|
|
3
|
+
/** Runs the renderer on the calling thread. Fallback where Workers are unavailable, and the server's renderer. */
|
|
4
|
+
export class InlineRenderer {
|
|
5
|
+
core = null;
|
|
6
|
+
ready;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.ready = (options.wasm ? Promise.resolve(options.wasm) : loadSealpixelWasm()).then((wasm) => {
|
|
9
|
+
this.core = new RendererCore(wasm, options);
|
|
10
|
+
return this.core;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
async init() {
|
|
14
|
+
return (await this.ready).info();
|
|
15
|
+
}
|
|
16
|
+
async load(bytes, id) {
|
|
17
|
+
return (await this.ready).load(bytes, id);
|
|
18
|
+
}
|
|
19
|
+
async loadProxy(proxy, descriptor) {
|
|
20
|
+
return (await this.ready).loadProxy(proxy, descriptor);
|
|
21
|
+
}
|
|
22
|
+
async loadProxyEncoded(bytes, descriptor) {
|
|
23
|
+
return (await this.ready).loadProxyEncoded(bytes, descriptor);
|
|
24
|
+
}
|
|
25
|
+
async render(req) {
|
|
26
|
+
return (await this.ready).render(req);
|
|
27
|
+
}
|
|
28
|
+
async export(req) {
|
|
29
|
+
return (await this.ready).export(req);
|
|
30
|
+
}
|
|
31
|
+
async geometry(req) {
|
|
32
|
+
return (await this.ready).geometry(req.log, req.target, req.upTo);
|
|
33
|
+
}
|
|
34
|
+
async unload() {
|
|
35
|
+
(await this.ready).unload();
|
|
36
|
+
}
|
|
37
|
+
async putAsset(id, bytes) {
|
|
38
|
+
(await this.ready).putAsset(id, bytes);
|
|
39
|
+
}
|
|
40
|
+
dispose() {
|
|
41
|
+
this.core?.unload();
|
|
42
|
+
}
|
|
43
|
+
/** For tests. */
|
|
44
|
+
async coreForTest() {
|
|
45
|
+
return this.ready;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=inline-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-renderer.js","sourceRoot":"","sources":["../src/inline-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAuC,MAAM,iBAAiB,CAAC;AAEzF,OAAO,EAAE,YAAY,EAA4B,MAAM,oBAAoB,CAAC;AAG5E,kHAAkH;AAClH,MAAM,OAAO,cAAc;IACjB,IAAI,GAAwB,IAAI,CAAC;IACxB,KAAK,CAAwB;IAE9C,YAAY,OAAuD;QACjE,IAAI,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC,CAAC,IAAI,CACpF,CAAC,IAAI,EAAE,EAAE;YACP,IAAI,CAAC,IAAI,GAAG,IAAI,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC5C,OAAO,IAAI,CAAC,IAAI,CAAC;QACnB,CAAC,CACF,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,KAAiB,EAAE,EAAU;QACtC,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,SAAS,CAAC,KAAiB,EAAE,UAA4B;QAC7D,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IACzD,CAAC;IACD,KAAK,CAAC,gBAAgB,CAAC,KAAiB,EAAE,UAA4B;QACpE,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAChE,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,GAAsC;QACjD,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACxC,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,GAAsC;QACjD,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACxC,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,GAAwC;QACrD,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC;IACD,KAAK,CAAC,MAAM;QACV,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;IAC9B,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,EAAU,EAAE,KAAiB;QAC1C,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC;IACD,OAAO;QACL,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IACtB,CAAC;IACD,iBAAiB;IACjB,KAAK,CAAC,WAAW;QACf,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AnyPlugin } from './plugin.js';
|
|
2
|
+
/** `{ pluginId: partialOptions }`, as accepted by the element, the wrappers and the server. */
|
|
3
|
+
export type PluginOptionsMap = Readonly<Record<string, unknown>>;
|
|
4
|
+
/**
|
|
5
|
+
* Applies `options[plugin.id]` to each plugin through its `configure()`.
|
|
6
|
+
* Plugins without an entry are returned as they are. An entry for an unknown
|
|
7
|
+
* plugin id, or for a plugin without options, is an error: silently ignoring
|
|
8
|
+
* it would hide a typo in a security-relevant constraint.
|
|
9
|
+
*/
|
|
10
|
+
export declare function configurePlugins(plugins: readonly AnyPlugin[], options: PluginOptionsMap | null | undefined): readonly AnyPlugin[];
|
|
11
|
+
/** The resolved options of every plugin that has any, keyed by id. */
|
|
12
|
+
export declare function pluginOptionsOf(plugins: readonly AnyPlugin[]): Record<string, unknown>;
|
|
13
|
+
//# sourceMappingURL=options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,+FAA+F;AAC/F,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEjE;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,SAAS,SAAS,EAAE,EAC7B,OAAO,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAC3C,SAAS,SAAS,EAAE,CAsBtB;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAItF"}
|
package/dist/options.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { SealpixelError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Applies `options[plugin.id]` to each plugin through its `configure()`.
|
|
4
|
+
* Plugins without an entry are returned as they are. An entry for an unknown
|
|
5
|
+
* plugin id, or for a plugin without options, is an error: silently ignoring
|
|
6
|
+
* it would hide a typo in a security-relevant constraint.
|
|
7
|
+
*/
|
|
8
|
+
export function configurePlugins(plugins, options) {
|
|
9
|
+
if (!options || Object.keys(options).length === 0)
|
|
10
|
+
return plugins;
|
|
11
|
+
const ids = new Set(plugins.map((p) => p.id));
|
|
12
|
+
for (const id of Object.keys(options)) {
|
|
13
|
+
if (!ids.has(id)) {
|
|
14
|
+
throw new SealpixelError('INVALID_REQUEST', `pluginOptions: plugin "${id}" is not registered`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return plugins.map((plugin) => {
|
|
18
|
+
const partial = options[plugin.id];
|
|
19
|
+
if (partial === undefined)
|
|
20
|
+
return plugin;
|
|
21
|
+
if (!plugin.configure) {
|
|
22
|
+
throw new SealpixelError('INVALID_REQUEST', `pluginOptions: plugin "${plugin.id}" has no options`);
|
|
23
|
+
}
|
|
24
|
+
return plugin.configure(partial);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** The resolved options of every plugin that has any, keyed by id. */
|
|
28
|
+
export function pluginOptionsOf(plugins) {
|
|
29
|
+
const out = {};
|
|
30
|
+
for (const p of plugins)
|
|
31
|
+
if (p.options !== undefined)
|
|
32
|
+
out[p.id] = p.options;
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAM7C;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAA6B,EAC7B,OAA4C;IAE5C,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IAClE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9C,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACjB,MAAM,IAAI,cAAc,CACtB,iBAAiB,EACjB,0BAA0B,EAAE,qBAAqB,CAClD,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QAC5B,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YACtB,MAAM,IAAI,cAAc,CACtB,iBAAiB,EACjB,0BAA0B,MAAM,CAAC,EAAE,kBAAkB,CACtD,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC,SAAS,CAAC,OAA2C,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;AACL,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,eAAe,CAAC,OAA6B;IAC3D,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS;YAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;IAC5E,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/plan.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RenderPlan, Stage } from './generated/plan.js';
|
|
2
|
+
import type { EditLog } from './types.js';
|
|
3
|
+
export type { Geometry, Size, StageGeometry } from './generated/geometry.js';
|
|
4
|
+
export type { ChromaSubsampling, Format as CoreFormat, OutputOptions as CoreOutputOptions, } from './generated/output.js';
|
|
5
|
+
export type { ColorStage, CropStage, Fit, Rect as PlanRect, RenderPlan, ResizeStage, SourceInfo, Stage, } from './generated/plan.js';
|
|
6
|
+
/** Mirrors `sealpixel_core::plan::PLAN_VERSION`. */
|
|
7
|
+
export declare const PLAN_VERSION = 1;
|
|
8
|
+
/**
|
|
9
|
+
* Builds the plan for `log` executed on a buffer of `actual` size. Until the
|
|
10
|
+
* plugin registry lands (S1.9) every log compiles to the identity plan.
|
|
11
|
+
*/
|
|
12
|
+
export declare function toRenderPlan(log: EditLog, actual: {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
}, stages?: Stage[]): RenderPlan;
|
|
16
|
+
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7E,YAAY,EACV,iBAAiB,EACjB,MAAM,IAAI,UAAU,EACpB,aAAa,IAAI,iBAAiB,GACnC,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,UAAU,EACV,SAAS,EACT,GAAG,EACH,IAAI,IAAI,QAAQ,EAChB,UAAU,EACV,WAAW,EACX,UAAU,EACV,KAAK,GACN,MAAM,qBAAqB,CAAC;AAE7B,oDAAoD;AACpD,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACzC,MAAM,GAAE,KAAK,EAAO,GACnB,UAAU,CAOZ"}
|
package/dist/plan.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Mirrors `sealpixel_core::plan::PLAN_VERSION`. */
|
|
2
|
+
export const PLAN_VERSION = 1;
|
|
3
|
+
/**
|
|
4
|
+
* Builds the plan for `log` executed on a buffer of `actual` size. Until the
|
|
5
|
+
* plugin registry lands (S1.9) every log compiles to the identity plan.
|
|
6
|
+
*/
|
|
7
|
+
export function toRenderPlan(log, actual, stages = []) {
|
|
8
|
+
return {
|
|
9
|
+
version: PLAN_VERSION,
|
|
10
|
+
source: { width: log.source.width, height: log.source.height },
|
|
11
|
+
proxyScale: actual.width / log.source.width,
|
|
12
|
+
stages,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=plan.js.map
|
package/dist/plan.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAoBA,oDAAoD;AACpD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAE9B;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,GAAY,EACZ,MAAyC,EACzC,SAAkB,EAAE;IAEpB,OAAO;QACL,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE;QAC9D,UAAU,EAAE,MAAM,CAAC,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK;QAC3C,MAAM;KACP,CAAC;AACJ,CAAC"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin contract. These are types only; `@sealpixel/plugin-sdk` provides the
|
|
3
|
+
* `definePlugin` / `defineOperation` helpers and deterministic math, and
|
|
4
|
+
* `@sealpixel/core` provides the registry that consumes plugins.
|
|
5
|
+
*/
|
|
6
|
+
import type { RgbaBuffer } from '@sealpixel/wasm';
|
|
7
|
+
import type { Size, Stage } from './plan.js';
|
|
8
|
+
import type { Operation, OperationBase, SourceDescriptor } from './types.js';
|
|
9
|
+
/** Augmented by plugins: `declare module '@sealpixel/core' { interface DocumentRegistry { finetune: FinetuneDoc } }`. */
|
|
10
|
+
export interface DocumentRegistry {
|
|
11
|
+
}
|
|
12
|
+
/** Derived state: one slice per plugin, keyed by plugin id. */
|
|
13
|
+
export type Document = {
|
|
14
|
+
[K in keyof DocumentRegistry]: DocumentRegistry[K];
|
|
15
|
+
};
|
|
16
|
+
/** Runtime validator for one operation. Structural so any zod-like schema works. */
|
|
17
|
+
export interface OperationSchema<Op> {
|
|
18
|
+
/** Returns the parsed value or throws. */
|
|
19
|
+
parse(value: unknown): Op;
|
|
20
|
+
/** Optional non-throwing variant used for aggregated error reporting. */
|
|
21
|
+
safeParse?(value: unknown): {
|
|
22
|
+
success: true;
|
|
23
|
+
data: Op;
|
|
24
|
+
} | {
|
|
25
|
+
success: false;
|
|
26
|
+
error: unknown;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export interface OperationDef<Doc, Op extends OperationBase<string> = OperationBase<string>> {
|
|
30
|
+
type: Op['type'];
|
|
31
|
+
schema: OperationSchema<Op>;
|
|
32
|
+
/** Pure: returns the plugin's next document slice. */
|
|
33
|
+
reduce: (slice: Doc, op: Op) => Doc;
|
|
34
|
+
/**
|
|
35
|
+
* Upgrades an op written by an older plugin version. Called with the raw
|
|
36
|
+
* value before validation. Return the op in the current shape.
|
|
37
|
+
*/
|
|
38
|
+
migrate?: (fromVersion: number, op: unknown) => unknown;
|
|
39
|
+
}
|
|
40
|
+
export interface PlanContext {
|
|
41
|
+
/** The plugin's resolved options (see `SealpixelPlugin.options`). */
|
|
42
|
+
options?: unknown;
|
|
43
|
+
/** Logical (full-resolution) size at this stage's input. */
|
|
44
|
+
logical: Size;
|
|
45
|
+
/** Actual buffer size at this stage's input. */
|
|
46
|
+
actual: Size;
|
|
47
|
+
/** `actual.width / logical.width`. */
|
|
48
|
+
scale: number;
|
|
49
|
+
/** `truth` or `preview`. Plugins may cheapen work in preview. */
|
|
50
|
+
mode: 'truth' | 'preview';
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Parameters a plugin contributes to its stage. For a core kernel this is the
|
|
54
|
+
* core stage minus `kind`; stages with the same name are shallow-merged in
|
|
55
|
+
* plugin order, which is how finetune and filter share one `color` stage.
|
|
56
|
+
*/
|
|
57
|
+
export type StageParams = Record<string, unknown>;
|
|
58
|
+
export interface KernelContext {
|
|
59
|
+
logical: Size;
|
|
60
|
+
actual: Size;
|
|
61
|
+
scale: number;
|
|
62
|
+
mode: 'truth' | 'preview';
|
|
63
|
+
/** Resolves a declared asset by id; throws if unknown or hash mismatch. */
|
|
64
|
+
asset: (id: string) => Uint8Array;
|
|
65
|
+
signal: AbortSignal;
|
|
66
|
+
}
|
|
67
|
+
export type KernelDef =
|
|
68
|
+
/** First-party only: the kernel is compiled into the core wasm. */
|
|
69
|
+
{
|
|
70
|
+
kind: 'core';
|
|
71
|
+
}
|
|
72
|
+
/** Plain JavaScript. Determinism is the author's responsibility. */
|
|
73
|
+
| {
|
|
74
|
+
kind: 'js';
|
|
75
|
+
run: (image: RgbaBuffer, params: StageParams, ctx: KernelContext) => RgbaBuffer | Promise<RgbaBuffer>;
|
|
76
|
+
}
|
|
77
|
+
/** Separate WebAssembly module implementing the kernel ABI. */
|
|
78
|
+
| {
|
|
79
|
+
kind: 'wasm';
|
|
80
|
+
module: () => Promise<WebAssembly.Module>;
|
|
81
|
+
export?: string;
|
|
82
|
+
};
|
|
83
|
+
export interface AssetDeclaration {
|
|
84
|
+
id: string;
|
|
85
|
+
/** sha256 of the bytes; the resolver verifies it on both client and server. */
|
|
86
|
+
hash: string;
|
|
87
|
+
/** Where the default resolver fetches it from. */
|
|
88
|
+
url?: string;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* UI contribution. Consumed by `@sealpixel/ui`, which calls the functions with
|
|
92
|
+
* its `UiToolContext`. Parameters are typed `never` here so a plugin can
|
|
93
|
+
* declare the context shape it needs without depending on the UI package.
|
|
94
|
+
*/
|
|
95
|
+
export interface ToolDef {
|
|
96
|
+
id?: string;
|
|
97
|
+
icon: unknown;
|
|
98
|
+
/** i18n key or literal label. */
|
|
99
|
+
label: string;
|
|
100
|
+
panel: (ctx: never) => unknown;
|
|
101
|
+
overlay?: (ctx: never) => unknown;
|
|
102
|
+
activate?: (ctx: never) => void;
|
|
103
|
+
deactivate?: (ctx: never) => void;
|
|
104
|
+
shortcuts?: Record<string, (ctx: never) => void>;
|
|
105
|
+
preview?: (ctx: never) => PreviewHint;
|
|
106
|
+
}
|
|
107
|
+
export type PreviewHint = {
|
|
108
|
+
css: string;
|
|
109
|
+
} | {
|
|
110
|
+
none: true;
|
|
111
|
+
};
|
|
112
|
+
export interface ToolContext {
|
|
113
|
+
document: Document;
|
|
114
|
+
dispatch: (op: Operation) => void;
|
|
115
|
+
t: (key: string, params?: Record<string, string | number>) => string;
|
|
116
|
+
}
|
|
117
|
+
/** Runtime validator for a plugin's options. Structural so any zod-like schema works. */
|
|
118
|
+
export interface OptionsSchema<Options> {
|
|
119
|
+
parse(value: unknown): Options;
|
|
120
|
+
safeParse?(value: unknown): {
|
|
121
|
+
success: true;
|
|
122
|
+
data: Options;
|
|
123
|
+
} | {
|
|
124
|
+
success: false;
|
|
125
|
+
error: unknown;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/** Context handed to `validate`: the resolved options and the slice before the op. */
|
|
129
|
+
export interface ValidateContext<Doc, Options> {
|
|
130
|
+
options: Options;
|
|
131
|
+
slice: Doc;
|
|
132
|
+
/** All slices as reduced so far. */
|
|
133
|
+
document: Document;
|
|
134
|
+
/** The source image the log belongs to (dimensions in pixels). */
|
|
135
|
+
source: SourceDescriptor;
|
|
136
|
+
}
|
|
137
|
+
export interface ValidateDocumentContext<Options> {
|
|
138
|
+
options: Options;
|
|
139
|
+
document: Document;
|
|
140
|
+
source: SourceDescriptor;
|
|
141
|
+
}
|
|
142
|
+
export interface SealpixelPlugin<Id extends string = string, Doc = unknown, Options = unknown> {
|
|
143
|
+
/** Namespaced id such as `acme.invert`. Op types are `<id>.<verb>`. */
|
|
144
|
+
id: Id;
|
|
145
|
+
/** Bumped when the op schema or the kernel output changes. */
|
|
146
|
+
version: number;
|
|
147
|
+
operations: ReadonlyArray<OperationDef<Doc, any>>;
|
|
148
|
+
initialDocument: Doc;
|
|
149
|
+
stage: {
|
|
150
|
+
name: string;
|
|
151
|
+
order: number;
|
|
152
|
+
};
|
|
153
|
+
/** Returns this plugin's stage parameters, or `null` to contribute nothing. */
|
|
154
|
+
toPlan: (slice: Doc, ctx: PlanContext) => StageParams | null;
|
|
155
|
+
kernel: KernelDef;
|
|
156
|
+
tool?: () => Promise<ToolDef>;
|
|
157
|
+
assets?: AssetDeclaration[];
|
|
158
|
+
/**
|
|
159
|
+
* Resolved options: defaults merged with what `configure()` received.
|
|
160
|
+
* Options tune behaviour and constrain edits; they never carry pixel data
|
|
161
|
+
* so a render never depends on them.
|
|
162
|
+
*/
|
|
163
|
+
options?: Options;
|
|
164
|
+
/** Validates a full options object. Used by `configure()` and the docs generator. */
|
|
165
|
+
optionsSchema?: OptionsSchema<Options>;
|
|
166
|
+
/** One-line descriptions per top-level option key, for generated documentation. */
|
|
167
|
+
optionsDescribe?: Readonly<Record<string, string>>;
|
|
168
|
+
/** The defaults `configure()` merges over. */
|
|
169
|
+
optionsDefaults?: Options;
|
|
170
|
+
/** Returns a new plugin instance with `partial` merged over the current options. */
|
|
171
|
+
configure?: (partial: Partial<Options>) => SealpixelPlugin<Id, Doc, Options>;
|
|
172
|
+
/**
|
|
173
|
+
* Constraint check for one operation against the options. Return a message
|
|
174
|
+
* to reject the op (`OPTION_VIOLATION`), or null to accept. Runs in
|
|
175
|
+
* `parseEditLog`, in `session.dispatch` and on the server.
|
|
176
|
+
*/
|
|
177
|
+
validate?: (op: Operation, ctx: ValidateContext<Doc, Options>) => string | null;
|
|
178
|
+
/**
|
|
179
|
+
* Constraint check for the final state, for example a locked watermark that
|
|
180
|
+
* must be present. Runs before export and before every server render.
|
|
181
|
+
*/
|
|
182
|
+
validateDocument?: (slice: Doc, ctx: ValidateDocumentContext<Options>) => string | null;
|
|
183
|
+
/**
|
|
184
|
+
* Operations the editor dispatches when a source loads with an empty log,
|
|
185
|
+
* so option defaults (a default preset, a locked watermark) become part of
|
|
186
|
+
* the log instead of hidden state. Ids are assigned by the caller.
|
|
187
|
+
*/
|
|
188
|
+
initialOps?: (ctx: {
|
|
189
|
+
options: Options;
|
|
190
|
+
source: SourceDescriptor;
|
|
191
|
+
}) => Omit<Operation, 'id'>[];
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* A plugin with its document type erased. Registries accept this so plugins
|
|
195
|
+
* with concrete `Doc` types can be mixed in one array.
|
|
196
|
+
*/
|
|
197
|
+
export type AnyPlugin = SealpixelPlugin<string, any, any>;
|
|
198
|
+
/** Core stage kinds a `kernel: { kind: 'core' }` plugin may target. */
|
|
199
|
+
export type CoreStageName = Stage['kind'];
|
|
200
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { AnyPlugin, Document, OperationDef } from './plugin.js';
|
|
2
|
+
import type { EditLog, Operation, SourceDescriptor } from './types.js';
|
|
3
|
+
export interface ParseOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Keep ops whose type is unknown instead of failing. They are ignored by
|
|
6
|
+
* `compile`. Only for forward-compatible previews; servers must not set it.
|
|
7
|
+
*/
|
|
8
|
+
tolerateUnknown?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface ParseIssue {
|
|
11
|
+
/** Index into `ops`, or -1 for a top-level problem. */
|
|
12
|
+
index: number;
|
|
13
|
+
code: 'INVALID_EDIT_LOG' | 'UNKNOWN_PLUGIN' | 'UNKNOWN_OPERATION' | 'OPTION_VIOLATION';
|
|
14
|
+
message: string;
|
|
15
|
+
}
|
|
16
|
+
export type ParseResult = {
|
|
17
|
+
ok: true;
|
|
18
|
+
log: EditLog;
|
|
19
|
+
} | {
|
|
20
|
+
ok: false;
|
|
21
|
+
issues: ParseIssue[];
|
|
22
|
+
};
|
|
23
|
+
export interface RegisteredOperation {
|
|
24
|
+
plugin: AnyPlugin;
|
|
25
|
+
def: OperationDef<any, any>;
|
|
26
|
+
}
|
|
27
|
+
/** Plugins plus everything derived from them: schemas, reducers, stage order. */
|
|
28
|
+
export interface Registry {
|
|
29
|
+
/** Sorted by stage order, then id. */
|
|
30
|
+
readonly plugins: readonly AnyPlugin[];
|
|
31
|
+
readonly byId: ReadonlyMap<string, AnyPlugin>;
|
|
32
|
+
readonly byOpType: ReadonlyMap<string, RegisteredOperation>;
|
|
33
|
+
/** `{ pluginId: version }` for every registered plugin. */
|
|
34
|
+
pluginVersions(): Record<string, number>;
|
|
35
|
+
initialDocument(): Document;
|
|
36
|
+
/** Validates and migrates an unknown value into an `EditLog`. Throws `SealpixelError`. */
|
|
37
|
+
parseEditLog(value: unknown, options?: ParseOptions): EditLog;
|
|
38
|
+
safeParseEditLog(value: unknown, options?: ParseOptions): ParseResult;
|
|
39
|
+
/**
|
|
40
|
+
* Runs the owning plugin's `validate` for one op against the document it
|
|
41
|
+
* would apply to. Returns the violation message, or null.
|
|
42
|
+
*/
|
|
43
|
+
validateOperation(op: Operation, document: Document, source: SourceDescriptor): string | null;
|
|
44
|
+
/**
|
|
45
|
+
* Runs every plugin's `validateDocument` over a compiled document. Returns
|
|
46
|
+
* all violations; empty when the state satisfies the configured options.
|
|
47
|
+
*/
|
|
48
|
+
validateDocument(document: Document, source: SourceDescriptor): {
|
|
49
|
+
plugin: string;
|
|
50
|
+
message: string;
|
|
51
|
+
}[];
|
|
52
|
+
/** `{ pluginId: options }` for every plugin with options. */
|
|
53
|
+
pluginOptions(): Record<string, unknown>;
|
|
54
|
+
/** Ops that seed an empty log from option defaults, in stage order. */
|
|
55
|
+
initialOps(source: SourceDescriptor): Omit<Operation, 'id'>[];
|
|
56
|
+
}
|
|
57
|
+
export declare function createRegistry(plugins: readonly AnyPlugin[]): Registry;
|
|
58
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAoB,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEvE,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,kBAAkB,CAAC;IACvF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC;AAE3F,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,SAAS,CAAC;IAElB,GAAG,EAAE,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;CAC7B;AAED,iFAAiF;AACjF,MAAM,WAAW,QAAQ;IACvB,sCAAsC;IACtC,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC5D,2DAA2D;IAC3D,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,eAAe,IAAI,QAAQ,CAAC;IAC5B,0FAA0F;IAC1F,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC;IAC9D,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,WAAW,CAAC;IACtE;;;OAGG;IACH,iBAAiB,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAAC;IAC9F;;;OAGG;IACH,gBAAgB,CACd,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,gBAAgB,GACvB;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACzC,6DAA6D;IAC7D,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,uEAAuE;IACvE,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC;CAC/D;AA6BD,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,GAAG,QAAQ,CAiKtE"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { SealpixelError } from './errors.js';
|
|
3
|
+
const SourceSchema = z.object({
|
|
4
|
+
id: z.string().min(1),
|
|
5
|
+
width: z.number().int().positive(),
|
|
6
|
+
height: z.number().int().positive(),
|
|
7
|
+
hash: z
|
|
8
|
+
.string()
|
|
9
|
+
.regex(/^[0-9a-f]{64}$/)
|
|
10
|
+
.optional(),
|
|
11
|
+
mimeType: z.string().optional(),
|
|
12
|
+
});
|
|
13
|
+
const AssetSchema = z
|
|
14
|
+
.object({
|
|
15
|
+
id: z.string().min(1),
|
|
16
|
+
hash: z.string().regex(/^[0-9a-f]{64}$/),
|
|
17
|
+
url: z.string().min(1).optional(),
|
|
18
|
+
})
|
|
19
|
+
.strict();
|
|
20
|
+
const LogShape = z.object({
|
|
21
|
+
version: z.literal(1),
|
|
22
|
+
source: SourceSchema,
|
|
23
|
+
plugins: z.record(z.string(), z.number().int().positive()).default({}),
|
|
24
|
+
assets: z.array(AssetSchema).max(1000).optional(),
|
|
25
|
+
ops: z.array(z.unknown()).default([]),
|
|
26
|
+
});
|
|
27
|
+
export function createRegistry(plugins) {
|
|
28
|
+
const byId = new Map();
|
|
29
|
+
const byOpType = new Map();
|
|
30
|
+
for (const plugin of plugins) {
|
|
31
|
+
if (byId.has(plugin.id)) {
|
|
32
|
+
throw new SealpixelError('INVALID_EDIT_LOG', `plugin "${plugin.id}" registered twice`);
|
|
33
|
+
}
|
|
34
|
+
byId.set(plugin.id, plugin);
|
|
35
|
+
for (const def of plugin.operations) {
|
|
36
|
+
if (byOpType.has(def.type)) {
|
|
37
|
+
throw new SealpixelError('INVALID_EDIT_LOG', `operation "${def.type}" registered twice`);
|
|
38
|
+
}
|
|
39
|
+
byOpType.set(def.type, { plugin, def });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const sorted = [...plugins].sort((a, b) => a.stage.order - b.stage.order || a.id.localeCompare(b.id));
|
|
43
|
+
const initialDocument = () => Object.fromEntries(sorted.map((p) => [p.id, p.initialDocument]));
|
|
44
|
+
const validateOperation = (op, document, source) => {
|
|
45
|
+
const entry = byOpType.get(op.type);
|
|
46
|
+
if (!entry?.plugin.validate)
|
|
47
|
+
return null;
|
|
48
|
+
const slice = document[entry.plugin.id];
|
|
49
|
+
return entry.plugin.validate(op, { options: entry.plugin.options, slice, document, source });
|
|
50
|
+
};
|
|
51
|
+
const validateDocument = (document, source) => {
|
|
52
|
+
const out = [];
|
|
53
|
+
for (const plugin of sorted) {
|
|
54
|
+
if (!plugin.validateDocument)
|
|
55
|
+
continue;
|
|
56
|
+
const slice = document[plugin.id];
|
|
57
|
+
const message = plugin.validateDocument(slice, { options: plugin.options, document, source });
|
|
58
|
+
if (message)
|
|
59
|
+
out.push({ plugin: plugin.id, message });
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
};
|
|
63
|
+
const safeParseEditLog = (value, options = {}) => {
|
|
64
|
+
const shape = LogShape.safeParse(value);
|
|
65
|
+
if (!shape.success) {
|
|
66
|
+
return {
|
|
67
|
+
ok: false,
|
|
68
|
+
issues: shape.error.issues.map((i) => ({
|
|
69
|
+
index: -1,
|
|
70
|
+
code: 'INVALID_EDIT_LOG',
|
|
71
|
+
message: `${i.path.join('.') || '(root)'}: ${i.message}`,
|
|
72
|
+
})),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const issues = [];
|
|
76
|
+
const ops = [];
|
|
77
|
+
// Slices reduced so far, so constraints can look at the state an op applies to.
|
|
78
|
+
const document = initialDocument();
|
|
79
|
+
shape.data.ops.forEach((raw, index) => {
|
|
80
|
+
const type = typeof raw === 'object' && raw !== null ? raw.type : undefined;
|
|
81
|
+
if (typeof type !== 'string') {
|
|
82
|
+
issues.push({ index, code: 'INVALID_EDIT_LOG', message: 'op has no string "type"' });
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const entry = byOpType.get(type);
|
|
86
|
+
if (!entry) {
|
|
87
|
+
if (options.tolerateUnknown) {
|
|
88
|
+
ops.push(raw);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const pluginId = type.slice(0, type.lastIndexOf('.'));
|
|
92
|
+
issues.push(byId.has(pluginId)
|
|
93
|
+
? { index, code: 'UNKNOWN_OPERATION', message: `unknown operation "${type}"` }
|
|
94
|
+
: { index, code: 'UNKNOWN_PLUGIN', message: `plugin "${pluginId}" is not registered` });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const written = shape.data.plugins[entry.plugin.id];
|
|
98
|
+
let candidate = raw;
|
|
99
|
+
if (written !== undefined && written < entry.plugin.version) {
|
|
100
|
+
if (!entry.def.migrate) {
|
|
101
|
+
issues.push({
|
|
102
|
+
index,
|
|
103
|
+
code: 'INVALID_EDIT_LOG',
|
|
104
|
+
message: `op "${type}" was written by ${entry.plugin.id} v${written}; v${entry.plugin.version} has no migration`,
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
candidate = entry.def.migrate(written, raw);
|
|
109
|
+
}
|
|
110
|
+
const parsed = entry.def.schema.safeParse
|
|
111
|
+
? entry.def.schema.safeParse(candidate)
|
|
112
|
+
: tryParse(entry.def.schema.parse, candidate);
|
|
113
|
+
if (parsed.success) {
|
|
114
|
+
const op = parsed.data;
|
|
115
|
+
const violation = validateOperation(op, document, stripUndefined(shape.data.source));
|
|
116
|
+
if (violation) {
|
|
117
|
+
issues.push({ index, code: 'OPTION_VIOLATION', message: `op "${type}": ${violation}` });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
ops.push(op);
|
|
121
|
+
document[entry.plugin.id] = entry.def.reduce(document[entry.plugin.id], op);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
issues.push({
|
|
125
|
+
index,
|
|
126
|
+
code: 'INVALID_EDIT_LOG',
|
|
127
|
+
message: `op "${type}": ${describeError(parsed.error)}`,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
if (issues.length > 0)
|
|
132
|
+
return { ok: false, issues };
|
|
133
|
+
const log = {
|
|
134
|
+
version: 1,
|
|
135
|
+
source: stripUndefined(shape.data.source),
|
|
136
|
+
plugins: { ...shape.data.plugins },
|
|
137
|
+
...(shape.data.assets
|
|
138
|
+
? { assets: shape.data.assets.map((a) => stripUndefined(a)) }
|
|
139
|
+
: {}),
|
|
140
|
+
ops,
|
|
141
|
+
};
|
|
142
|
+
return { ok: true, log };
|
|
143
|
+
};
|
|
144
|
+
return {
|
|
145
|
+
plugins: sorted,
|
|
146
|
+
byId,
|
|
147
|
+
byOpType,
|
|
148
|
+
pluginVersions: () => Object.fromEntries(sorted.map((p) => [p.id, p.version])),
|
|
149
|
+
pluginOptions: () => Object.fromEntries(sorted.filter((p) => p.options !== undefined).map((p) => [p.id, p.options])),
|
|
150
|
+
initialDocument,
|
|
151
|
+
initialOps: (source) => sorted.flatMap((p) => p.initialOps?.({ options: p.options, source }) ?? []),
|
|
152
|
+
validateOperation,
|
|
153
|
+
validateDocument,
|
|
154
|
+
safeParseEditLog,
|
|
155
|
+
parseEditLog: (value, options) => {
|
|
156
|
+
const result = safeParseEditLog(value, options);
|
|
157
|
+
if (result.ok)
|
|
158
|
+
return result.log;
|
|
159
|
+
const first = result.issues[0];
|
|
160
|
+
const code = result.issues.every((i) => i.code === 'UNKNOWN_PLUGIN')
|
|
161
|
+
? 'UNKNOWN_PLUGIN'
|
|
162
|
+
: result.issues.some((i) => i.code === 'OPTION_VIOLATION')
|
|
163
|
+
? 'OPTION_VIOLATION'
|
|
164
|
+
: 'INVALID_EDIT_LOG';
|
|
165
|
+
throw new SealpixelError(code, first ? `${first.message}` : 'invalid edit log', result.issues);
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function tryParse(parse, value) {
|
|
170
|
+
try {
|
|
171
|
+
return { success: true, data: parse(value) };
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
return { success: false, error };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function describeError(error) {
|
|
178
|
+
if (error && typeof error === 'object' && 'issues' in error) {
|
|
179
|
+
const issues = error.issues;
|
|
180
|
+
return issues.map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`).join('; ');
|
|
181
|
+
}
|
|
182
|
+
return error instanceof Error ? error.message : String(error);
|
|
183
|
+
}
|
|
184
|
+
function stripUndefined(value) {
|
|
185
|
+
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== undefined));
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=registry.js.map
|