@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.
- package/LICENSE +367 -0
- package/LICENSE-GPL-3.0 +674 -0
- package/README.md +98 -0
- package/dist/cli.d.ts +27 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +167 -0
- package/dist/cli.js.map +1 -0
- package/dist/filesystem.d.ts +25 -0
- package/dist/filesystem.d.ts.map +1 -0
- package/dist/filesystem.js +75 -0
- package/dist/filesystem.js.map +1 -0
- package/dist/handler.d.ts +62 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +91 -0
- package/dist/handler.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +74 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +181 -0
- package/dist/loader.js.map +1 -0
- package/dist/operations.d.ts +45 -0
- package/dist/operations.d.ts.map +1 -0
- package/dist/operations.js +94 -0
- package/dist/operations.js.map +1 -0
- package/dist/staging.d.ts +37 -0
- package/dist/staging.d.ts.map +1 -0
- package/dist/staging.js +157 -0
- package/dist/staging.js.map +1 -0
- package/dist/tiff-header.d.ts +16 -0
- package/dist/tiff-header.d.ts.map +1 -0
- package/dist/tiff-header.js +97 -0
- package/dist/tiff-header.js.map +1 -0
- package/dist/types.d.ts +128 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +47 -0
- package/dist/types.js.map +1 -0
- package/dist/wenblend.js +2 -0
- package/dist/wenblend.wasm +0 -0
- package/dist/wenfuse.js +2 -0
- package/dist/wenfuse.wasm +0 -0
- package/dist/wopfs.d.ts +37 -0
- package/dist/wopfs.d.ts.map +1 -0
- package/dist/wopfs.js +38 -0
- package/dist/wopfs.js.map +1 -0
- package/dist/worker.d.ts +16 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +29 -0
- package/dist/worker.js.map +1 -0
- package/package.json +67 -0
- package/src/cli.ts +201 -0
- package/src/filesystem.ts +85 -0
- package/src/handler.ts +112 -0
- package/src/index.ts +41 -0
- package/src/loader.ts +223 -0
- package/src/operations.ts +147 -0
- package/src/staging.ts +173 -0
- package/src/tiff-header.ts +94 -0
- package/src/types.ts +153 -0
- package/src/wopfs.ts +70 -0
- package/src/worker.ts +31 -0
- 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
|
package/dist/cli.js.map
ADDED
|
@@ -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"}
|
package/dist/handler.js
ADDED
|
@@ -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":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,YAAY,EAAmB,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAkB3C,MAAM,GAAG,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAW,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAsB;IACxD,4EAA4E;IAC5E,2EAA2E;IAC3E,MAAM,EAAE,GAAG,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7E,IAAI,CAAC;QACH,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACpD,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,EAAE,CAAC;QACjF,CAAC;QACD,wEAAwE;QACxE,oEAAoE;QACpE,sEAAsE;QACtE,cAAc;QACd,MAAM,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;QAC1B,MAAM,EAAE,GAAG,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9F,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,MAAM,KAAK,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,OAAO,MAAM,CAAC;YACzE,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,KAAK,EAAE,EAAE,EAAE,CAAC;QACrF,CAAC;QACD,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC7E,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,4BAA4B,EAAE,EAAE,CAAC;QAC5F,CAAC;QACD,6DAA6D;QAC7D,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC5E,IAAI,UAAU,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC;YAC9C,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,sCAAsC,EAAE,EAAE,CAAC;QACtG,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;QACzG,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAClC,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAA6C,CAAC;QACnF,OAAO;YACL,EAAE;YACF,EAAE,EAAE,KAAK;YACT,KAAK,EAAE;gBACL,IAAI,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO;gBAC3C,OAAO,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;gBACnD,MAAM,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;gBACvD,mEAAmE;gBACnE,gEAAgE;gBAChE,QAAQ,EAAE,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;aAC9D;SACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,CACL,OAAO,iBAAiB,KAAK,WAAW;QACxC,OAAO,IAAI,KAAK,WAAW;QAC3B,IAAI,YAAY,iBAAiB,CAClC,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
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, 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"}
|