@wasmedia/wenblend 0.1.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.
Files changed (64) hide show
  1. package/LICENSE +367 -0
  2. package/LICENSE-GPL-3.0 +674 -0
  3. package/README.md +98 -0
  4. package/dist/cli.d.ts +27 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +167 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/filesystem.d.ts +25 -0
  9. package/dist/filesystem.d.ts.map +1 -0
  10. package/dist/filesystem.js +75 -0
  11. package/dist/filesystem.js.map +1 -0
  12. package/dist/handler.d.ts +62 -0
  13. package/dist/handler.d.ts.map +1 -0
  14. package/dist/handler.js +91 -0
  15. package/dist/handler.js.map +1 -0
  16. package/dist/index.d.ts +17 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +17 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/loader.d.ts +74 -0
  21. package/dist/loader.d.ts.map +1 -0
  22. package/dist/loader.js +181 -0
  23. package/dist/loader.js.map +1 -0
  24. package/dist/operations.d.ts +45 -0
  25. package/dist/operations.d.ts.map +1 -0
  26. package/dist/operations.js +94 -0
  27. package/dist/operations.js.map +1 -0
  28. package/dist/staging.d.ts +37 -0
  29. package/dist/staging.d.ts.map +1 -0
  30. package/dist/staging.js +157 -0
  31. package/dist/staging.js.map +1 -0
  32. package/dist/tiff-header.d.ts +16 -0
  33. package/dist/tiff-header.d.ts.map +1 -0
  34. package/dist/tiff-header.js +97 -0
  35. package/dist/tiff-header.js.map +1 -0
  36. package/dist/types.d.ts +128 -0
  37. package/dist/types.d.ts.map +1 -0
  38. package/dist/types.js +47 -0
  39. package/dist/types.js.map +1 -0
  40. package/dist/wenblend.js +2 -0
  41. package/dist/wenblend.wasm +0 -0
  42. package/dist/wenfuse.js +2 -0
  43. package/dist/wenfuse.wasm +0 -0
  44. package/dist/wopfs.d.ts +37 -0
  45. package/dist/wopfs.d.ts.map +1 -0
  46. package/dist/wopfs.js +38 -0
  47. package/dist/wopfs.js.map +1 -0
  48. package/dist/worker.d.ts +16 -0
  49. package/dist/worker.d.ts.map +1 -0
  50. package/dist/worker.js +29 -0
  51. package/dist/worker.js.map +1 -0
  52. package/package.json +67 -0
  53. package/src/cli.ts +201 -0
  54. package/src/filesystem.ts +85 -0
  55. package/src/handler.ts +112 -0
  56. package/src/index.ts +41 -0
  57. package/src/loader.ts +223 -0
  58. package/src/operations.ts +147 -0
  59. package/src/staging.ts +173 -0
  60. package/src/tiff-header.ts +94 -0
  61. package/src/types.ts +153 -0
  62. package/src/wopfs.ts +70 -0
  63. package/src/worker.ts +31 -0
  64. package/types/wasm.d.ts +12 -0
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # wenblend
2
+
3
+ [Enblend and Enfuse](https://sourceforge.net/projects/enblend/) cross-compiled to WebAssembly, so
4
+ panorama blending and exposure fusion run in a browser. `enblend` joins overlapping images into one
5
+ image with a multi-resolution spline; `enfuse` merges a bracketed exposure stack into a single fused
6
+ image. Both are C++ command-line tools from the Hugin ecosystem.
7
+
8
+ Part of the [wasmedia](https://gitlab.com/wasmedia) family.
9
+
10
+ ## Status
11
+
12
+ **The native layer and the TypeScript wrapper are both built.** `blend`/`fuse` and the CLI-style
13
+ `enblend`/`enfuse` are documented in `docs/api.md`. The wasm build is tested by a smoke suite and by a
14
+ differential comparison against native enblend/enfuse; the wrapper by unit and integration tests. All
15
+ of it passes locally and in GitLab CI, which runs it on every pipeline — the first green run was
16
+ pipeline 2868936176, on real amd64 runners. The browser playground is built; see below.
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ npm install @wasmedia/wenblend
22
+ ```
23
+
24
+ ```typescript
25
+ import { blend, fuse } from "@wasmedia/wenblend";
26
+ const panorama = await blend([left, right]); // positioned RGBA TIFFs, as Uint8Array
27
+ ```
28
+
29
+ ESM only; browsers and Node. Read the first section of `docs/api.md` before blending anything: enblend
30
+ places images by their TIFF position tags and aligns nothing. The wasm in the package is
31
+ **GPLv3-or-later** (see Licensing below), so anything that ships it inherits GPL obligations.
32
+
33
+ ## Playground
34
+
35
+ `npm run build && npm run playground` serves a browser playground at `http://localhost:8000/` (set
36
+ `PORT=` to change it). On `main` it deploys to GitLab Pages at
37
+ [enblend.wasmedia.org](https://enblend.wasmedia.org/). It runs enblend and enfuse on built-in sample
38
+ sets or on your own positioned RGBA TIFFs, in a worker, and nothing leaves the browser.
39
+
40
+ ## Artifacts
41
+
42
+ One WebAssembly module per tool, not one module with a dispatcher — the two programs cannot share a
43
+ symbol space (see `docs/building.md`).
44
+
45
+ | Artifact | Emscripten export | Size |
46
+ |---|---|---|
47
+ | `dist/wenblend.{js,wasm}` | `WEnblend` | ~3 MB wasm |
48
+ | `dist/wenfuse.{js,wasm}` | `WEnfuse` | ~3 MB wasm |
49
+
50
+ Each module takes its tool's own argv, passed straight to Emscripten's `callMain` with no tool-name
51
+ prefix. There is no C wrapper API to call instead.
52
+
53
+ ## Build
54
+
55
+ Docker is the only prerequisite; never install a local emsdk.
56
+
57
+ ```bash
58
+ make -C native # full pipeline -> the four artifacts in dist/
59
+ make -C native smoke # fixtures, both modules under Node, 42 image invariants
60
+ npm install && npm run build:ts && npm test # the TypeScript wrapper and its tests
61
+ ```
62
+
63
+ A cold build is about 7 minutes. Other targets (`verify`, `differential`, `measure-ceiling`, `shell`,
64
+ `clean`, `distclean`) are listed in `docs/building.md`.
65
+
66
+ ## Documentation
67
+
68
+ - **`docs/api.md`** — the TypeScript API. Read its first section before blending anything.
69
+ - **`docs/building.md`** — read this before touching `native/`. Dependency order, sentinels, source
70
+ integrity, and the gotchas that cost this build real time.
71
+ - **`CLAUDE.md`** — project overview, architecture, and the constraints the wrapper inherits.
72
+ - `docs/superpowers/specs/2026-09-20-wenblend-design.md` — the design of record.
73
+
74
+ ## File mode
75
+
76
+ Inputs and outputs can stay in files (OPFS handles in a worker, host paths under Node) instead of
77
+ passing through memory; see `docs/api.md` § File mode. It uses
78
+ [`@wasmedia/wopfs`](https://www.npmjs.com/package/@wasmedia/wopfs), installed from npm like any other
79
+ dependency.
80
+
81
+ ## Licensing
82
+
83
+ **wenblend's own source is GPL-2.0.** `LICENSE` in the repository root is the GPLv2 text and covers the
84
+ code here.
85
+
86
+ **The built artifacts in `dist/` are GPLv3-or-later.** Enblend and Enfuse are "GPL version 2, or (at
87
+ your option) any later version", and GNU GSL — linked into both modules for the Mersenne Twister on the
88
+ output-dither path — is GPLv3-or-later. The combination is legal precisely because of that "or later"
89
+ clause, which permits the upgrade; the effect is that redistributing `dist/wenblend.{js,wasm}` or
90
+ `dist/wenfuse.{js,wasm}` binds you to GPLv3-or-later terms, not GPLv2. GSL is the only copyleft
91
+ dependency in the stack — VIGRA, libtiff, libpng, zlib-ng, mozjpeg, lcms2 and Boost are all permissive.
92
+
93
+ File mode loads [wopfs](https://gitlab.com/wasmedia/wopfs) (MIT), a runtime dependency that is loaded
94
+ only on the first file-mode call and is not part of the wasm artifacts.
95
+
96
+ Anything that links these modules inherits GPL obligations. This section is a documentation of fact
97
+ about the licences in the source tree, **not legal advice**; a real licence review is warranted before
98
+ any public release.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * CLI-style invocation of enblend and enfuse.
3
+ *
4
+ * Argv is passed through UNCHANGED — exactly what the caller would type
5
+ * in a terminal. There is no dispatcher and no filename rewriting: each
6
+ * tool is its own module with its own main(), and relative filenames
7
+ * resolve because we chdir into the work directory first.
8
+ */
9
+ import type { CLIFileResult, CLIResult, FileInput, FileModeOptions, InputImage, LoaderOptions } from "./types.js";
10
+ import { type Tool } from "./loader.js";
11
+ import { tiffPixelCount } from "./tiff-header.js";
12
+ export { tiffPixelCount };
13
+ /**
14
+ * Run one tool once.
15
+ *
16
+ * A fresh module instance is built for this call and discarded after it:
17
+ * both tools keep state in C++ globals that neither resets.
18
+ */
19
+ export declare function runTool(tool: Tool, argv: string[], inputs?: InputImage[], options?: LoaderOptions): Promise<CLIResult>;
20
+ export declare function runTool(tool: Tool, argv: string[], inputs: FileInput[], options: FileModeOptions): Promise<CLIFileResult>;
21
+ /** Run enblend. Argv is the tool's own, unchanged. */
22
+ export declare function enblend(argv: string[], inputs?: InputImage[], options?: LoaderOptions): Promise<CLIResult>;
23
+ export declare function enblend(argv: string[], inputs: FileInput[], options: FileModeOptions): Promise<CLIFileResult>;
24
+ /** Run enfuse. Argv is the tool's own, unchanged. */
25
+ export declare function enfuse(argv: string[], inputs?: InputImage[], options?: LoaderOptions): Promise<CLIResult>;
26
+ export declare function enfuse(argv: string[], inputs: FileInput[], options: FileModeOptions): Promise<CLIFileResult>;
27
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,aAAa,EAA0B,MAAM,YAAY,CAAC;AAE1I,OAAO,EAAkB,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGlD,OAAO,EAAE,cAAc,EAAE,CAAC;AAsE1B;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;AACxH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AA+F3H,sDAAsD;AACtD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;AAC5G,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AAK/G,qDAAqD;AACrD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;AAC3G,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC"}
package/dist/cli.js ADDED
@@ -0,0 +1,167 @@
1
+ /**
2
+ * CLI-style invocation of enblend and enfuse.
3
+ *
4
+ * Argv is passed through UNCHANGED — exactly what the caller would type
5
+ * in a terminal. There is no dispatcher and no filename rewriting: each
6
+ * tool is its own module with its own main(), and relative filenames
7
+ * resolve because we chdir into the work directory first.
8
+ */
9
+ import { OutOfMemoryError } from "./types.js";
10
+ import { createInstance } from "./loader.js";
11
+ import { isFileInput, stageBuffers, stageFiles } from "./staging.js";
12
+ import { tiffPixelCount } from "./tiff-header.js";
13
+ // Re-exported: the public name has always lived here.
14
+ export { tiffPixelCount };
15
+ /**
16
+ * Recognise enblend's or enfuse's own out-of-memory exit, from stderr.
17
+ *
18
+ * Nothing bounds memory in this port — enblend's image cache was removed
19
+ * upstream in 4.2.1 — so exhausting the heap is a realistic outcome on
20
+ * large inputs rather than an exotic one.
21
+ *
22
+ * Measured (`make -C native measure-ceiling`): when enblend exhausts the
23
+ * 4 GB heap, nothing is thrown. Enblend catches `std::bad_alloc` itself,
24
+ * prints `enblend: out of memory` and `enblend: std::bad_alloc` to stderr,
25
+ * and main RETURNS 1, leaving an 8-byte TIFF header behind as the output
26
+ * file. That line — the tool's name, a colon, `out of memory`, on a line
27
+ * of its own — is the only shape matched.
28
+ *
29
+ * Anchored on purpose. The vendored libtiff alone carries ~35 messages
30
+ * beginning "Out of memory", which a corrupt TIFF or a huge declared
31
+ * dimension will print, and a filename can contain the phrase too. An
32
+ * unanchored match reported all of those as OutOfMemoryError and lost the
33
+ * tool's real diagnostic, which is specific and worth surfacing verbatim.
34
+ */
35
+ function isToolOutOfMemory(stderr) {
36
+ return /^(enblend|enfuse): out of memory$/m.test(stderr);
37
+ }
38
+ /**
39
+ * Emscripten's own allocation-failure messages, for an exception thrown out
40
+ * of callMain rather than a clean exit.
41
+ *
42
+ * NOT known to fire in this build. The measured ceiling never reached it
43
+ * (a failed heap growth makes malloc return null, and C++'s `new` turns
44
+ * that into the bad_alloc handled above). And this build's abort() prints
45
+ * `Aborted(...)` to stderr and then calls ___trap(), so what callMain
46
+ * throws is `RuntimeError: unreachable`, which this does not match. Kept,
47
+ * narrow, only in case a growth failure ever surfaces as a thrown message.
48
+ */
49
+ function isThrownMemoryFailure(message) {
50
+ return /cannot enlarge memory|aborted\(oom\)/i.test(message);
51
+ }
52
+ function hostProcess() {
53
+ const p = globalThis.process;
54
+ return typeof p === "object" && p !== null ? p : undefined;
55
+ }
56
+ /** Emscripten throws this when the program calls exit(). It is not necessarily an error. */
57
+ function exitStatusOf(e) {
58
+ if (typeof e === "object" && e !== null && "status" in e) {
59
+ const { name, status } = e;
60
+ if (name === "ExitStatus" && typeof status === "number")
61
+ return status;
62
+ }
63
+ return undefined;
64
+ }
65
+ export async function runTool(tool, argv, inputs = [], options) {
66
+ // File mode when every input is a FileInput; buffers otherwise (including
67
+ // no inputs). Decided before an instance exists, so a bad call costs nothing.
68
+ const files = inputs.length > 0 && inputs.every(isFileInput);
69
+ if (!files && inputs.some(isFileInput)) {
70
+ throw new Error("runTool: inputs must be all buffers or all files, not a mix");
71
+ }
72
+ if (files && options?.outputDir === undefined) {
73
+ throw new Error("runTool: file inputs need options.outputDir, where outputs are committed");
74
+ }
75
+ let stdout = "";
76
+ let stderr = "";
77
+ // Hooks must be bound at construction: Emscripten reads Module["print"]/
78
+ // Module["printErr"] exactly once, inside the factory body, before the
79
+ // factory's promise resolves. Assigning module.print afterwards is inert.
80
+ const module = await createInstance(tool, options, {
81
+ print: (text) => { stdout += `${text}\n`; },
82
+ printErr: (text) => { stderr += `${text}\n`; },
83
+ });
84
+ // Staging failures (a locked input, a missing file) propagate before
85
+ // callMain, and there is nothing to release yet.
86
+ const staging = files
87
+ ? await stageFiles(module, inputs, options.outputDir, argv, options)
88
+ : stageBuffers(module, inputs);
89
+ let exitCode = 0;
90
+ let outOfMemory = false;
91
+ let outputs = new Map();
92
+ let inFlight;
93
+ const host = hostProcess();
94
+ const hostExitCode = host?.exitCode;
95
+ try {
96
+ try {
97
+ module.FS.chdir(staging.workDir);
98
+ // A fresh array every call: callMain mutates the array it is handed,
99
+ // and reusing one corrupts the next invocation's arguments.
100
+ const returned = module.callMain([...argv]);
101
+ exitCode = typeof returned === "number" ? returned : 0;
102
+ }
103
+ catch (e) {
104
+ const status = exitStatusOf(e);
105
+ if (status !== undefined) {
106
+ // A clean run may return normally or throw ExitStatus; Emscripten
107
+ // does not guarantee which. Status 0 here is success.
108
+ exitCode = status;
109
+ }
110
+ else {
111
+ const message = e instanceof Error ? e.message : String(e);
112
+ stderr += `Exception: ${message}\n`;
113
+ if (isThrownMemoryFailure(message))
114
+ outOfMemory = true;
115
+ else
116
+ exitCode = 1;
117
+ }
118
+ }
119
+ finally {
120
+ // See hostProcess(): the tool's exit status is not the host's.
121
+ if (host)
122
+ host.exitCode = hostExitCode;
123
+ }
124
+ // The measured OOM shape: a clean non-zero return with the tool's own
125
+ // diagnostic on stderr, and a truncated output file that must not be
126
+ // handed back as if it were a result.
127
+ if (!outOfMemory && exitCode !== 0 && isToolOutOfMemory(stderr))
128
+ outOfMemory = true;
129
+ // Read output BEFORE releasing. This works only because the modules are
130
+ // linked with EXIT_RUNTIME=0; under EXIT_RUNTIME=1 the runtime is torn
131
+ // down when main returns and every FS read here would throw.
132
+ // File mode commits only a successful run: a truncated output must never
133
+ // land in the caller's directory. Buffer mode returns whatever was written.
134
+ if (!outOfMemory && (!files || exitCode === 0))
135
+ outputs = await staging.collect();
136
+ }
137
+ catch (e) {
138
+ inFlight = { error: e };
139
+ throw e;
140
+ }
141
+ finally {
142
+ // release() can throw (wopfs). It must not replace an error already
143
+ // propagating; with nothing in flight, its error is the one to surface.
144
+ // An out-of-memory result counts as in flight: OutOfMemoryError is thrown
145
+ // after this block and a release error must not replace it.
146
+ try {
147
+ await staging.release();
148
+ }
149
+ catch (releaseError) {
150
+ if (!inFlight && !outOfMemory)
151
+ throw releaseError;
152
+ }
153
+ }
154
+ if (outOfMemory)
155
+ throw new OutOfMemoryError(await staging.pixelCount(), stderr.trimEnd());
156
+ const base = { exitCode, stdout: stdout.trimEnd(), stderr: stderr.trimEnd() };
157
+ return files
158
+ ? { ...base, written: outputs }
159
+ : { ...base, outputFiles: outputs };
160
+ }
161
+ export function enblend(argv, inputs, options) {
162
+ return runTool("enblend", argv, inputs, options);
163
+ }
164
+ export function enfuse(argv, inputs, options) {
165
+ return runTool("enfuse", argv, inputs, options);
166
+ }
167
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * MEMFS staging for N-input invocations.
3
+ *
4
+ * Enblend is N inputs to one output, so every call stages a set of files
5
+ * into a per-invocation directory and sweeps the whole directory
6
+ * afterwards. Nothing persists between calls.
7
+ */
8
+ import type { WasmModule, InputImage } from "./types.js";
9
+ export declare function toBytes(data: Uint8Array | ArrayBuffer): Uint8Array;
10
+ /** A fresh, unique work directory path. Unique naming ensures no collisions across invocations. */
11
+ export declare function nextWorkDirName(): string;
12
+ /** Create a unique work directory. */
13
+ export declare function createWorkDir(module: WasmModule): string;
14
+ /**
15
+ * Write each input into the work directory under its caller-chosen name.
16
+ *
17
+ * Returns the set of input names so `collectOutputs` can tell the tool's
18
+ * output from what we put there.
19
+ */
20
+ export declare function stageInputs(module: WasmModule, workDir: string, inputs: InputImage[]): Set<string>;
21
+ /** Read back everything in the work directory that was not staged as an input. */
22
+ export declare function collectOutputs(module: WasmModule, workDir: string, inputNames: Set<string>): Map<string, Uint8Array>;
23
+ /** Remove every file in the work directory, then the directory itself. */
24
+ export declare function cleanWorkDir(module: WasmModule, workDir: string): void;
25
+ //# sourceMappingURL=filesystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../src/filesystem.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIzD,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,UAAU,CAElE;AAED,mGAAmG;AACnG,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,sCAAsC;AACtC,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAIxD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,UAAU,EAAE,GACnB,GAAG,CAAC,MAAM,CAAC,CAOb;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAC5B,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GACtB,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAQzB;AAED,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAqBtE"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * MEMFS staging for N-input invocations.
3
+ *
4
+ * Enblend is N inputs to one output, so every call stages a set of files
5
+ * into a per-invocation directory and sweeps the whole directory
6
+ * afterwards. Nothing persists between calls.
7
+ */
8
+ let counter = 0;
9
+ export function toBytes(data) {
10
+ return data instanceof ArrayBuffer ? new Uint8Array(data) : data;
11
+ }
12
+ /** A fresh, unique work directory path. Unique naming ensures no collisions across invocations. */
13
+ export function nextWorkDirName() {
14
+ return `/work-${Date.now().toString(36)}-${(counter++).toString(36)}`;
15
+ }
16
+ /** Create a unique work directory. */
17
+ export function createWorkDir(module) {
18
+ const dir = nextWorkDirName();
19
+ module.FS.mkdir(dir);
20
+ return dir;
21
+ }
22
+ /**
23
+ * Write each input into the work directory under its caller-chosen name.
24
+ *
25
+ * Returns the set of input names so `collectOutputs` can tell the tool's
26
+ * output from what we put there.
27
+ */
28
+ export function stageInputs(module, workDir, inputs) {
29
+ const names = new Set();
30
+ for (const { name, data } of inputs) {
31
+ module.FS.writeFile(`${workDir}/${name}`, toBytes(data));
32
+ names.add(name);
33
+ }
34
+ return names;
35
+ }
36
+ /** Read back everything in the work directory that was not staged as an input. */
37
+ export function collectOutputs(module, workDir, inputNames) {
38
+ const outputs = new Map();
39
+ for (const entry of module.FS.readdir(workDir)) {
40
+ if (entry === "." || entry === "..")
41
+ continue;
42
+ if (inputNames.has(entry))
43
+ continue;
44
+ outputs.set(entry, module.FS.readFile(`${workDir}/${entry}`));
45
+ }
46
+ return outputs;
47
+ }
48
+ /** Remove every file in the work directory, then the directory itself. */
49
+ export function cleanWorkDir(module, workDir) {
50
+ for (const entry of module.FS.readdir(workDir)) {
51
+ if (entry === "." || entry === "..")
52
+ continue;
53
+ const path = `${workDir}/${entry}`;
54
+ try {
55
+ module.FS.unlink(path);
56
+ }
57
+ catch {
58
+ // Entry might be a directory. Attempt rmdir as fallback; continue
59
+ // either way to ensure the whole directory is swept as defense-in-depth.
60
+ try {
61
+ module.FS.rmdir(path);
62
+ }
63
+ catch {
64
+ // Best effort: one bad entry must not abort the sweep.
65
+ }
66
+ }
67
+ }
68
+ try {
69
+ module.FS.rmdir(workDir);
70
+ }
71
+ catch {
72
+ // Best effort: work directory cleanup failure does not abort.
73
+ }
74
+ }
75
+ //# sourceMappingURL=filesystem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.js","sourceRoot":"","sources":["../src/filesystem.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,IAAI,OAAO,GAAG,CAAC,CAAC;AAEhB,MAAM,UAAU,OAAO,CAAC,IAA8B;IACpD,OAAO,IAAI,YAAY,WAAW,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACnE,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,eAAe;IAC7B,OAAO,SAAS,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;AACxE,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,aAAa,CAAC,MAAkB;IAC9C,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;IAC9B,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrB,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,MAAkB,EAClB,OAAe,EACf,MAAoB;IAEpB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,MAAM,EAAE,CAAC;QACpC,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,OAAO,IAAI,IAAI,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,cAAc,CAC5B,MAAkB,EAClB,OAAe,EACf,UAAuB;IAEvB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC9C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/C,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,IAAI;YAAE,SAAS;QAC9C,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS;QACpC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,YAAY,CAAC,MAAkB,EAAE,OAAe;IAC9D,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/C,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,IAAI;YAAE,SAAS;QAC9C,MAAM,IAAI,GAAG,GAAG,OAAO,IAAI,KAAK,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,kEAAkE;YAClE,yEAAyE;YACzE,IAAI,CAAC;gBACH,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACxB,CAAC;YAAC,MAAM,CAAC;gBACP,uDAAuD;YACzD,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,8DAA8D;IAChE,CAAC;AACH,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The worker's message handler and its scope test, with NO import-time
3
+ * side effects.
4
+ *
5
+ * This module is what the package's main entry (`src/index.ts`) re-exports.
6
+ * It must never register anything when imported: a consumer who writes
7
+ * their own Web Worker and does `import { blend } from "@wasmedia/wenblend"`
8
+ * inside it would otherwise get our `message` listener attached to THEIR
9
+ * worker, and any of their own messages carrying an `images` array would
10
+ * run a blend or fuse they never asked for. Registration lives only in
11
+ * `src/worker.ts`, the package's dedicated `./worker` entry.
12
+ */
13
+ import type { BlendOptions, FuseOptions, FileInput, OutputDir, WrittenFile } from "./types.js";
14
+ import { type ImageInput } from "./operations.js";
15
+ export interface WorkerRequest {
16
+ id: string;
17
+ op: "blend" | "fuse";
18
+ images: ImageInput[] | FileInput[];
19
+ options?: (BlendOptions | FuseOptions) & {
20
+ outputDir?: OutputDir;
21
+ };
22
+ /** Where file mode loads @wasmedia/wopfs from; see LoaderOptions.wopfsPath. */
23
+ wopfsPath?: string;
24
+ }
25
+ export interface WorkerResponse {
26
+ id: string;
27
+ ok: boolean;
28
+ result?: Uint8Array | WrittenFile;
29
+ error?: {
30
+ name: string;
31
+ message: string;
32
+ stderr?: string;
33
+ exitCode?: number;
34
+ };
35
+ }
36
+ /**
37
+ * Handle one request. Never throws and never rejects: worker.ts posts
38
+ * whatever this resolves to, so a throw would leave the caller waiting for a
39
+ * reply that never comes. Messages arrive untyped at runtime, so nothing
40
+ * about `request` is trusted — not its shape, not its `op`.
41
+ */
42
+ export declare function handleMessage(request: WorkerRequest): Promise<WorkerResponse>;
43
+ /**
44
+ * True only inside an actual dedicated/shared/service worker.
45
+ *
46
+ * `typeof self !== "undefined"` is NOT a worker test: on a browser main
47
+ * thread `self === window`, and `window.postMessage` is a perfectly
48
+ * ordinary function, so a guard built on those two checks alone is true
49
+ * on every web page, not just inside a worker. Importing the dedicated
50
+ * `./worker` entry (`src/worker.ts`) would then attach a `message`
51
+ * listener to `window` on any ordinary page and treat arbitrary
52
+ * postMessage traffic (other scripts, extensions, iframes) as a
53
+ * WorkerRequest.
54
+ *
55
+ * `WorkerGlobalScope` only exists as a global inside a worker context,
56
+ * and `self` is an instance of it there. Both `typeof` checks are
57
+ * ordered so the `instanceof` is only reached once both globals are
58
+ * confirmed to exist, so this can never throw — including in plain
59
+ * Node, which has neither global.
60
+ */
61
+ export declare function isWorkerScope(): boolean;
62
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGhE,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,GAAG,MAAM,CAAC;IACrB,MAAM,EAAE,UAAU,EAAE,GAAG,SAAS,EAAE,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG;QAAE,SAAS,CAAC,EAAE,SAAS,CAAA;KAAE,CAAC;IACnE,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,UAAU,GAAG,WAAW,CAAC;IAClC,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/E;AAID;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CA4CnF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAMvC"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * The worker's message handler and its scope test, with NO import-time
3
+ * side effects.
4
+ *
5
+ * This module is what the package's main entry (`src/index.ts`) re-exports.
6
+ * It must never register anything when imported: a consumer who writes
7
+ * their own Web Worker and does `import { blend } from "@wasmedia/wenblend"`
8
+ * inside it would otherwise get our `message` listener attached to THEIR
9
+ * worker, and any of their own messages carrying an `images` array would
10
+ * run a blend or fuse they never asked for. Registration lives only in
11
+ * `src/worker.ts`, the package's dedicated `./worker` entry.
12
+ */
13
+ import { runOperation } from "./operations.js";
14
+ import { isFileInput } from "./staging.js";
15
+ const OPS = { blend: "enblend", fuse: "enfuse" };
16
+ /**
17
+ * Handle one request. Never throws and never rejects: worker.ts posts
18
+ * whatever this resolves to, so a throw would leave the caller waiting for a
19
+ * reply that never comes. Messages arrive untyped at runtime, so nothing
20
+ * about `request` is trusted — not its shape, not its `op`.
21
+ */
22
+ export async function handleMessage(request) {
23
+ // Echo the caller's id unchanged, whatever its runtime type: it exists only
24
+ // so the caller can correlate the reply. Plain-JS callers may use numbers.
25
+ const id = typeof request === "object" && request !== null ? request.id : "";
26
+ try {
27
+ if (typeof request !== "object" || request === null) {
28
+ return { id, ok: false, error: { name: "Error", message: "invalid request" } };
29
+ }
30
+ // Route only the two ops we know; anything else is rejected rather than
31
+ // quietly run as one of the tools. Only a string can name one, so a
32
+ // non-string op (which could carry a throwing toString) never reaches
33
+ // the lookup.
34
+ const opName = request.op;
35
+ const op = typeof opName === "string" && Object.hasOwn(OPS, opName) ? OPS[opName] : undefined;
36
+ if (!op) {
37
+ const shown = typeof opName === "string" ? `"${opName}"` : typeof opName;
38
+ return { id, ok: false, error: { name: "Error", message: `unknown op ${shown}` } };
39
+ }
40
+ if (request.wopfsPath !== undefined && typeof request.wopfsPath !== "string") {
41
+ return { id, ok: false, error: { name: "Error", message: "wopfsPath must be a string" } };
42
+ }
43
+ // Check if file mode is requested: all images are FileInputs
44
+ const imagesArray = Array.isArray(request.images) ? request.images : [];
45
+ const isFileMode = imagesArray.length > 0 && imagesArray.every(isFileInput);
46
+ if (isFileMode && !request.options?.outputDir) {
47
+ return { id, ok: false, error: { name: "Error", message: "file mode requires options.outputDir" } };
48
+ }
49
+ const result = await runOperation(op, request.images, request.options, { wopfsPath: request.wopfsPath });
50
+ return { id, ok: true, result };
51
+ }
52
+ catch (e) {
53
+ const { stderr, exitCode } = (e ?? {});
54
+ return {
55
+ id,
56
+ ok: false,
57
+ error: {
58
+ name: e instanceof Error ? e.name : "Error",
59
+ message: e instanceof Error ? e.message : String(e),
60
+ stderr: typeof stderr === "string" ? stderr : undefined,
61
+ // Error instances do not survive postMessage as themselves, so the
62
+ // exit code has to travel as plain data or the caller loses it.
63
+ exitCode: typeof exitCode === "number" ? exitCode : undefined,
64
+ },
65
+ };
66
+ }
67
+ }
68
+ /**
69
+ * True only inside an actual dedicated/shared/service worker.
70
+ *
71
+ * `typeof self !== "undefined"` is NOT a worker test: on a browser main
72
+ * thread `self === window`, and `window.postMessage` is a perfectly
73
+ * ordinary function, so a guard built on those two checks alone is true
74
+ * on every web page, not just inside a worker. Importing the dedicated
75
+ * `./worker` entry (`src/worker.ts`) would then attach a `message`
76
+ * listener to `window` on any ordinary page and treat arbitrary
77
+ * postMessage traffic (other scripts, extensions, iframes) as a
78
+ * WorkerRequest.
79
+ *
80
+ * `WorkerGlobalScope` only exists as a global inside a worker context,
81
+ * and `self` is an instance of it there. Both `typeof` checks are
82
+ * ordered so the `instanceof` is only reached once both globals are
83
+ * confirmed to exist, so this can never throw — including in plain
84
+ * Node, which has neither global.
85
+ */
86
+ export function isWorkerScope() {
87
+ return (typeof WorkerGlobalScope !== "undefined" &&
88
+ typeof self !== "undefined" &&
89
+ self instanceof WorkerGlobalScope);
90
+ }
91
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * wenblend — Enblend and Enfuse compiled to WebAssembly.
3
+ *
4
+ * @example
5
+ * ```typescript
6
+ * import { blend } from "@wasmedia/wenblend";
7
+ *
8
+ * // Inputs must be RGBA TIFFs carrying correct XPosition/YPosition tags.
9
+ * const panorama = await blend([left, right]);
10
+ * ```
11
+ */
12
+ export { blend, fuse, normalizeInputs, runOperation, type ImageInput } from "./operations.js";
13
+ export { enblend, enfuse, runTool } from "./cli.js";
14
+ export { createInstance, loadFactory, type Tool, type ModuleFactory, type InstanceHooks, } from "./loader.js";
15
+ export { handleMessage, isWorkerScope, type WorkerRequest, type WorkerResponse } from "./handler.js";
16
+ export { EnblendError, OutOfMemoryError, type WasmModule, type EmscriptenFS, type FSStat, type CLIResult, type FileInput, type OutputDir, type WrittenFile, type CLIFileResult, type FileModeOptions, type InputImage, type BlendOptions, type FuseOptions, type CommonOptions, type LoaderOptions, type LoadPhase, } from "./types.js";
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC9F,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EACL,cAAc,EACd,WAAW,EACX,KAAK,IAAI,EACT,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AACrG,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,MAAM,EACX,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,SAAS,GACf,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * wenblend — Enblend and Enfuse compiled to WebAssembly.
3
+ *
4
+ * @example
5
+ * ```typescript
6
+ * import { blend } from "@wasmedia/wenblend";
7
+ *
8
+ * // Inputs must be RGBA TIFFs carrying correct XPosition/YPosition tags.
9
+ * const panorama = await blend([left, right]);
10
+ * ```
11
+ */
12
+ export { blend, fuse, normalizeInputs, runOperation } from "./operations.js";
13
+ export { enblend, enfuse, runTool } from "./cli.js";
14
+ export { createInstance, loadFactory, } from "./loader.js";
15
+ export { handleMessage, isWorkerScope } from "./handler.js";
16
+ export { EnblendError, OutOfMemoryError, } from "./types.js";
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,YAAY,EAAmB,MAAM,iBAAiB,CAAC;AAC9F,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EACL,cAAc,EACd,WAAW,GAIZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,aAAa,EAA2C,MAAM,cAAc,CAAC;AACrG,OAAO,EACL,YAAY,EACZ,gBAAgB,GAgBjB,MAAM,YAAY,CAAC"}