@workglow/util 0.2.19 → 0.2.21

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 (60) hide show
  1. package/dist/browser.js +81 -16
  2. package/dist/browser.js.map +4 -4
  3. package/dist/bun.js +81 -16
  4. package/dist/bun.js.map +4 -4
  5. package/dist/json-schema/SchemaUtils.d.ts.map +1 -1
  6. package/dist/json-schema/SchemaValidation.d.ts +6 -0
  7. package/dist/json-schema/SchemaValidation.d.ts.map +1 -1
  8. package/dist/media/cpuImage.d.ts +15 -16
  9. package/dist/media/cpuImage.d.ts.map +1 -1
  10. package/dist/media/encode.d.ts +3 -5
  11. package/dist/media/encode.d.ts.map +1 -1
  12. package/dist/media/gpuImage.d.ts +21 -30
  13. package/dist/media/gpuImage.d.ts.map +1 -1
  14. package/dist/media/imageCacheCodec.d.ts +19 -7
  15. package/dist/media/imageCacheCodec.d.ts.map +1 -1
  16. package/dist/media/imageRasterCodecRegistry.d.ts +3 -3
  17. package/dist/media/imageRasterCodecRegistry.d.ts.map +1 -1
  18. package/dist/media/imageTypes.d.ts +0 -16
  19. package/dist/media/imageTypes.d.ts.map +1 -1
  20. package/dist/media/imageValue.d.ts +44 -0
  21. package/dist/media/imageValue.d.ts.map +1 -0
  22. package/dist/media/imageValue.test.d.ts +7 -0
  23. package/dist/media/imageValue.test.d.ts.map +1 -0
  24. package/dist/media/imageValueSchema.d.ts +15 -0
  25. package/dist/media/imageValueSchema.d.ts.map +1 -0
  26. package/dist/media/previewBudget.d.ts +10 -13
  27. package/dist/media/previewBudget.d.ts.map +1 -1
  28. package/dist/media/rawPixelBuffer.d.ts +20 -0
  29. package/dist/media/rawPixelBuffer.d.ts.map +1 -0
  30. package/dist/media/{sharpImage.node.d.ts → sharpImage.server.d.ts} +42 -14
  31. package/dist/media/sharpImage.server.d.ts.map +1 -0
  32. package/dist/media/webGpuImage.browser.d.ts +11 -14
  33. package/dist/media/webGpuImage.browser.d.ts.map +1 -1
  34. package/dist/media-browser.d.ts +9 -3
  35. package/dist/media-browser.d.ts.map +1 -1
  36. package/dist/media-browser.js +429 -342
  37. package/dist/media-browser.js.map +14 -14
  38. package/dist/media-node.d.ts +8 -6
  39. package/dist/media-node.d.ts.map +1 -1
  40. package/dist/media-node.js +479 -254
  41. package/dist/media-node.js.map +14 -14
  42. package/dist/node.js +81 -16
  43. package/dist/node.js.map +4 -4
  44. package/dist/schema-entry.js +166 -165
  45. package/dist/schema-entry.js.map +5 -5
  46. package/dist/utilities/Misc.d.ts +1 -1
  47. package/dist/utilities/Misc.d.ts.map +1 -1
  48. package/dist/worker/WorkerManager.d.ts.map +1 -1
  49. package/dist/worker-browser.js +5 -2
  50. package/dist/worker-browser.js.map +3 -3
  51. package/dist/worker-bun.js +5 -2
  52. package/dist/worker-bun.js.map +3 -3
  53. package/dist/worker-node.js +5 -2
  54. package/dist/worker-node.js.map +3 -3
  55. package/package.json +1 -1
  56. package/dist/media/gpuImageSchema.d.ts +0 -8
  57. package/dist/media/gpuImageSchema.d.ts.map +0 -1
  58. package/dist/media/sharpImage.bun.d.ts +0 -7
  59. package/dist/media/sharpImage.bun.d.ts.map +0 -1
  60. package/dist/media/sharpImage.node.d.ts.map +0 -1
@@ -1,39 +1,13 @@
1
1
  // src/media/imageCacheCodec.ts
2
2
  import { registerPortCodec } from "@workglow/util";
3
3
 
4
- // src/media/gpuImage.ts
5
- var GLOBAL_FACTORY_KEY = Symbol.for("@workglow/util/media/gpuImageFactory");
6
- var _g = globalThis;
7
- if (!_g[GLOBAL_FACTORY_KEY]) {
8
- _g[GLOBAL_FACTORY_KEY] = {};
9
- }
10
- var factory = _g[GLOBAL_FACTORY_KEY];
11
- function registerGpuImageFactory(key, fn) {
12
- factory[key] = fn;
13
- }
14
- function getGpuImageFactory(key) {
15
- const fn = factory[key];
16
- return typeof fn === "function" ? fn : undefined;
17
- }
18
- var GpuImage = new Proxy({}, {
19
- get(_t, prop) {
20
- if (typeof prop !== "string" || prop === "then")
21
- return;
22
- const fn = factory[prop];
23
- if (typeof fn !== "function") {
24
- throw new Error(`GpuImage.${prop} is not registered. Import the platform entry point.`);
25
- }
26
- return fn;
27
- }
28
- });
29
-
30
4
  // src/media/imageRasterCodecRegistry.ts
31
5
  var GLOBAL_CODEC_KEY = Symbol.for("@workglow/util/media/imageRasterCodec");
32
- var _g2 = globalThis;
33
- if (!_g2[GLOBAL_CODEC_KEY]) {
34
- _g2[GLOBAL_CODEC_KEY] = { value: null };
6
+ var _g = globalThis;
7
+ if (!_g[GLOBAL_CODEC_KEY]) {
8
+ _g[GLOBAL_CODEC_KEY] = { value: null };
35
9
  }
36
- var slot = _g2[GLOBAL_CODEC_KEY];
10
+ var slot = _g[GLOBAL_CODEC_KEY];
37
11
  function registerImageRasterCodec(next) {
38
12
  slot.value = next;
39
13
  }
@@ -44,130 +18,170 @@ function getImageRasterCodec() {
44
18
  return slot.value;
45
19
  }
46
20
 
47
- // src/media/cpuImage.ts
48
- var FORMAT_TO_MIME = {
49
- png: "image/png",
50
- jpeg: "image/jpeg",
51
- webp: "image/webp"
52
- };
53
- function dataUriToBytes(dataUri) {
54
- const comma = dataUri.indexOf(",");
55
- const b64 = dataUri.slice(comma + 1);
56
- const bin = atob(b64);
57
- const bytes = new Uint8Array(bin.length);
58
- for (let i = 0;i < bin.length; i++)
59
- bytes[i] = bin.charCodeAt(i);
60
- return bytes;
21
+ // src/media/imageValue.ts
22
+ function imageValueFromBitmap(bitmap, width, height, previewScale = 1) {
23
+ return { bitmap, width, height, previewScale };
61
24
  }
62
- function expandToRgba(bin) {
63
- if (bin.channels === 4)
64
- return bin.data;
65
- const px = bin.width * bin.height;
66
- const out = new Uint8ClampedArray(px * 4);
67
- if (bin.channels === 3) {
68
- for (let i = 0;i < px; i++) {
69
- out[i * 4 + 0] = bin.data[i * 3 + 0] ?? 0;
70
- out[i * 4 + 1] = bin.data[i * 3 + 1] ?? 0;
71
- out[i * 4 + 2] = bin.data[i * 3 + 2] ?? 0;
72
- out[i * 4 + 3] = 255;
73
- }
74
- } else if (bin.channels === 1) {
75
- for (let i = 0;i < px; i++) {
76
- const g = bin.data[i] ?? 0;
77
- out[i * 4 + 0] = g;
78
- out[i * 4 + 1] = g;
79
- out[i * 4 + 2] = g;
80
- out[i * 4 + 3] = 255;
81
- }
82
- }
83
- return out;
25
+ function imageValueFromBuffer(buffer, format, width, height, previewScale = 1) {
26
+ return { buffer, format, width, height, previewScale };
84
27
  }
85
-
86
- class CpuImage {
87
- bin;
88
- backend = "cpu";
89
- _previewScale;
90
- constructor(bin, previewScale = 1) {
91
- this.bin = bin;
92
- this._previewScale = previewScale;
28
+ function isImageValue(v) {
29
+ return isBrowserImageValue(v) || isNodeImageValue(v);
30
+ }
31
+ function isBrowserImageValue(v) {
32
+ if (v === null || typeof v !== "object")
33
+ return false;
34
+ const o = v;
35
+ return typeof o.width === "number" && typeof o.height === "number" && typeof o.previewScale === "number" && typeof ImageBitmap !== "undefined" && o.bitmap instanceof ImageBitmap;
36
+ }
37
+ function isNodeImageValue(v) {
38
+ if (v === null || typeof v !== "object")
39
+ return false;
40
+ const o = v;
41
+ return typeof o.width === "number" && typeof o.height === "number" && typeof o.previewScale === "number" && typeof Buffer !== "undefined" && Buffer.isBuffer(o.buffer) && (o.format === "png" || o.format === "jpeg" || o.format === "raw-rgba");
42
+ }
43
+ async function normalizeToImageValue(value) {
44
+ if (value === null || value === undefined)
45
+ return;
46
+ if (isImageValue(value))
47
+ return value;
48
+ if (typeof Blob !== "undefined" && value instanceof Blob) {
49
+ if (typeof createImageBitmap === "function") {
50
+ const bitmap = await createImageBitmap(value);
51
+ return imageValueFromBitmap(bitmap, bitmap.width, bitmap.height);
52
+ }
53
+ return;
93
54
  }
94
- get width() {
95
- return this.bin.width;
55
+ if (typeof ImageBitmap !== "undefined" && value instanceof ImageBitmap) {
56
+ return imageValueFromBitmap(value, value.width, value.height);
96
57
  }
97
- get height() {
98
- return this.bin.height;
58
+ if (typeof value === "string" && value.startsWith("data:")) {
59
+ if (typeof createImageBitmap === "function" && typeof fetch === "function") {
60
+ const blob = await (await fetch(value)).blob();
61
+ const bitmap = await createImageBitmap(blob);
62
+ return imageValueFromBitmap(bitmap, bitmap.width, bitmap.height);
63
+ }
64
+ if (typeof Buffer !== "undefined") {
65
+ return decodeDataUriToNodeImageValue(value);
66
+ }
67
+ return;
99
68
  }
100
- get channels() {
101
- return this.bin.channels;
69
+ return;
70
+ }
71
+ async function decodeDataUriToNodeImageValue(dataUri) {
72
+ const match = /^data:([^;,]+);base64,(.+)$/.exec(dataUri);
73
+ if (!match)
74
+ return;
75
+ const mime = match[1] ?? "image/png";
76
+ const base64 = match[2] ?? "";
77
+ const buffer = Buffer.from(base64, "base64");
78
+ const format = /jpe?g/i.test(mime) ? "jpeg" : "png";
79
+ try {
80
+ const decoded = await getImageRasterCodec().decodeDataUri(dataUri);
81
+ return imageValueFromBuffer(buffer, format, decoded.width, decoded.height);
82
+ } catch (err) {
83
+ throw new Error("normalizeToImageValue: failed to probe data URI dimensions", {
84
+ cause: err
85
+ });
102
86
  }
103
- get previewScale() {
104
- return this._previewScale;
87
+ }
88
+
89
+ // src/media/imageCacheCodec.ts
90
+ function isImageValueWire(v) {
91
+ if (v === null || typeof v !== "object")
92
+ return false;
93
+ const o = v;
94
+ return o.__imageValueWire === 1 && typeof o.base64 === "string" && (o.format === "png" || o.format === "jpeg" || o.format === "raw-rgba") && typeof o.width === "number" && typeof o.height === "number" && typeof o.previewScale === "number";
95
+ }
96
+ function bytesToBase64(bytes) {
97
+ if (typeof Buffer !== "undefined") {
98
+ return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
105
99
  }
106
- _setPreviewScale(scale) {
107
- this._previewScale = scale;
108
- return this;
100
+ let bin = "";
101
+ for (let i = 0;i < bytes.length; i++)
102
+ bin += String.fromCharCode(bytes[i] ?? 0);
103
+ return btoa(bin);
104
+ }
105
+ function base64ToBytes(b64) {
106
+ if (typeof Buffer !== "undefined") {
107
+ const buf = Buffer.from(b64, "base64");
108
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
109
109
  }
110
- async materialize() {
111
- return this.bin;
110
+ const bin = atob(b64);
111
+ const out = new Uint8Array(bin.length);
112
+ for (let i = 0;i < bin.length; i++)
113
+ out[i] = bin.charCodeAt(i);
114
+ return out;
115
+ }
116
+ async function browserToPngBase64(value) {
117
+ if (typeof OffscreenCanvas === "undefined") {
118
+ throw new Error("imageCacheCodec.serialize: BrowserImageValue requires OffscreenCanvas");
112
119
  }
113
- getBinary() {
114
- return this.bin;
120
+ const off = new OffscreenCanvas(value.width, value.height);
121
+ const ctx = off.getContext("2d");
122
+ if (!ctx)
123
+ throw new Error("imageCacheCodec.serialize: could not acquire 2D context");
124
+ ctx.drawImage(value.bitmap, 0, 0);
125
+ const blob = await off.convertToBlob({ type: "image/png" });
126
+ const bytes = new Uint8Array(await blob.arrayBuffer());
127
+ return bytesToBase64(bytes);
128
+ }
129
+ async function wireToBrowserImageValue(wire) {
130
+ if (typeof createImageBitmap !== "function") {
131
+ throw new Error("imageCacheCodec.deserialize: browser path requires createImageBitmap");
115
132
  }
116
- async toCanvas(canvas) {
133
+ const bytes = base64ToBytes(wire.base64);
134
+ if (wire.format === "raw-rgba") {
117
135
  if (typeof ImageData === "undefined") {
118
- throw new Error("CpuImage.toCanvas requires a browser environment with ImageData");
136
+ throw new Error("imageCacheCodec.deserialize: raw-rgba decode requires ImageData");
119
137
  }
120
- const rgba = expandToRgba(this.bin);
121
- const id = new ImageData(new Uint8ClampedArray(rgba.buffer, rgba.byteOffset, rgba.byteLength), this.bin.width, this.bin.height);
122
- if (canvas.width !== this.bin.width)
123
- canvas.width = this.bin.width;
124
- if (canvas.height !== this.bin.height)
125
- canvas.height = this.bin.height;
126
- const ctx = canvas.getContext("2d");
127
- if (!ctx)
128
- throw new Error("CpuImage.toCanvas could not acquire a 2D context");
129
- ctx.putImageData(id, 0, 0);
130
- }
131
- async encode(format, _quality) {
132
- const codec = getImageRasterCodec();
133
- const dataUri = await codec.encodeDataUri(this.bin, FORMAT_TO_MIME[format]);
134
- return dataUriToBytes(dataUri);
135
- }
136
- retain(_n = 1) {
137
- return this;
138
- }
139
- release() {}
140
- static fromImageBinary(bin, previewScale = 1) {
141
- return new CpuImage(bin, previewScale);
138
+ const data = new Uint8ClampedArray(bytes.buffer, bytes.byteOffset, bytes.byteLength);
139
+ const imageData = new ImageData(data, wire.width, wire.height);
140
+ const bitmap2 = await createImageBitmap(imageData);
141
+ return { bitmap: bitmap2, width: wire.width, height: wire.height, previewScale: wire.previewScale };
142
142
  }
143
+ const mime = wire.format === "jpeg" ? "image/jpeg" : "image/png";
144
+ const blob = new Blob([bytes.buffer], { type: mime });
145
+ const bitmap = await createImageBitmap(blob);
146
+ return { bitmap, width: wire.width, height: wire.height, previewScale: wire.previewScale };
143
147
  }
144
- registerGpuImageFactory("fromImageBinary", CpuImage.fromImageBinary);
145
-
146
- // src/media/imageCacheCodec.ts
147
148
  registerPortCodec("image", {
148
149
  async serialize(value) {
149
- if (typeof value.materialize !== "function") {
150
+ if (typeof value === "string")
150
151
  return value;
152
+ if (isNodeImageValue(value)) {
153
+ return {
154
+ __imageValueWire: 1,
155
+ format: value.format,
156
+ base64: value.buffer.toString("base64"),
157
+ width: value.width,
158
+ height: value.height,
159
+ previewScale: value.previewScale
160
+ };
151
161
  }
152
- const bin = await value.materialize();
153
- return {
154
- kind: "image-binary",
155
- width: bin.width,
156
- height: bin.height,
157
- channels: bin.channels,
158
- data: bin.data
159
- };
162
+ if (isBrowserImageValue(value)) {
163
+ const base64 = await browserToPngBase64(value);
164
+ return {
165
+ __imageValueWire: 1,
166
+ format: "png",
167
+ base64,
168
+ width: value.width,
169
+ height: value.height,
170
+ previewScale: value.previewScale
171
+ };
172
+ }
173
+ throw new Error("imageCacheCodec.serialize: value is not an ImageValue or string");
160
174
  },
161
- async deserialize(cached) {
162
- if (cached.kind !== "image-binary") {
163
- return cached;
175
+ async deserialize(wire) {
176
+ if (typeof wire === "string")
177
+ return wire;
178
+ if (!isImageValueWire(wire)) {
179
+ throw new Error("imageCacheCodec.deserialize: input is not an ImageValueWire or string");
164
180
  }
165
- return CpuImage.fromImageBinary({
166
- data: cached.data,
167
- width: cached.width,
168
- height: cached.height,
169
- channels: cached.channels
170
- });
181
+ if (typeof Buffer !== "undefined") {
182
+ return imageValueFromBuffer(Buffer.from(wire.base64, "base64"), wire.format, wire.width, wire.height, wire.previewScale);
183
+ }
184
+ return wireToBrowserImageValue(wire);
171
185
  }
172
186
  });
173
187
 
@@ -177,17 +191,17 @@ class Container {
177
191
  factories = new Map;
178
192
  singletons = new Set;
179
193
  resolving = [];
180
- register(token, factory2, singleton = true) {
181
- this.factories.set(token, factory2);
194
+ register(token, factory, singleton = true) {
195
+ this.factories.set(token, factory);
182
196
  if (singleton) {
183
197
  this.singletons.add(token);
184
198
  }
185
199
  }
186
- registerIfAbsent(token, factory2, singleton = true) {
200
+ registerIfAbsent(token, factory, singleton = true) {
187
201
  if (this.factories.has(token) || this.services.has(token)) {
188
202
  return;
189
203
  }
190
- this.register(token, factory2, singleton);
204
+ this.register(token, factory, singleton);
191
205
  }
192
206
  registerInstance(token, instance) {
193
207
  this.services.set(token, instance);
@@ -197,8 +211,8 @@ class Container {
197
211
  if (this.services.has(token)) {
198
212
  return this.services.get(token);
199
213
  }
200
- const factory2 = this.factories.get(token);
201
- if (!factory2) {
214
+ const factory = this.factories.get(token);
215
+ if (!factory) {
202
216
  throw new Error(`Service not registered: ${String(token)}`);
203
217
  }
204
218
  if (this.resolving.includes(token)) {
@@ -207,7 +221,7 @@ class Container {
207
221
  }
208
222
  this.resolving.push(token);
209
223
  try {
210
- const instance = factory2();
224
+ const instance = factory();
211
225
  if (this.singletons.has(token)) {
212
226
  this.services.set(token, instance);
213
227
  }
@@ -256,8 +270,8 @@ class Container {
256
270
  }
257
271
  createChildContainer() {
258
272
  const child = new Container;
259
- this.factories.forEach((factory2, token) => {
260
- child.factories.set(token, factory2);
273
+ this.factories.forEach((factory, token) => {
274
+ child.factories.set(token, factory);
261
275
  if (this.singletons.has(token)) {
262
276
  child.singletons.add(token);
263
277
  }
@@ -272,11 +286,11 @@ class Container {
272
286
  }
273
287
  }
274
288
  var GLOBAL_CONTAINER_KEY = Symbol.for("@workglow/util/di/globalContainer");
275
- var _g3 = globalThis;
276
- if (!_g3[GLOBAL_CONTAINER_KEY]) {
277
- _g3[GLOBAL_CONTAINER_KEY] = new Container;
289
+ var _g2 = globalThis;
290
+ if (!_g2[GLOBAL_CONTAINER_KEY]) {
291
+ _g2[GLOBAL_CONTAINER_KEY] = new Container;
278
292
  }
279
- var globalContainer = _g3[GLOBAL_CONTAINER_KEY];
293
+ var globalContainer = _g2[GLOBAL_CONTAINER_KEY];
280
294
 
281
295
  // src/di/ServiceRegistry.ts
282
296
  function createServiceToken(id) {
@@ -288,11 +302,11 @@ class ServiceRegistry {
288
302
  constructor(container = globalContainer) {
289
303
  this.container = container;
290
304
  }
291
- register(token, factory2, singleton = true) {
292
- this.container.register(token.id, factory2, singleton);
305
+ register(token, factory, singleton = true) {
306
+ this.container.register(token.id, factory, singleton);
293
307
  }
294
- registerIfAbsent(token, factory2, singleton = true) {
295
- this.container.registerIfAbsent(token.id, factory2, singleton);
308
+ registerIfAbsent(token, factory, singleton = true) {
309
+ this.container.registerIfAbsent(token.id, factory, singleton);
296
310
  }
297
311
  registerInstance(token, instance) {
298
312
  this.container.registerInstance(token.id, instance);
@@ -321,16 +335,17 @@ function registerInputResolver(formatPrefix, resolver) {
321
335
  }
322
336
 
323
337
  // src/media/imageHydrationResolver.ts
324
- async function resolveImageString(id, _format, _registry) {
325
- if (typeof id !== "string")
326
- return id;
327
- if (id.startsWith("data:")) {
328
- return GpuImage.fromDataUri(id);
338
+ async function resolveImage(id, _format, _registry) {
339
+ const normalized = await normalizeToImageValue(id);
340
+ if (normalized !== undefined)
341
+ return normalized;
342
+ if (typeof id === "string") {
343
+ const preview = id.length > 32 ? `${id.slice(0, 32)}...` : id;
344
+ throw new Error(`format:"image" resolver received an unsupported string "${preview}". ` + `Only data: URIs are handled. Register a sub-resolver for other schemes.`);
329
345
  }
330
- const preview = id.length > 32 ? `${id.slice(0, 32)}...` : id;
331
- throw new Error(`format:"image" resolver received an unsupported string "${preview}". ` + `Only data: URIs are handled. For other schemes register a sub-resolver, ` + `e.g. registerInputResolver("image:http", fn).`);
346
+ return id;
332
347
  }
333
- registerInputResolver("image", resolveImageString);
348
+ registerInputResolver("image", resolveImage);
334
349
 
335
350
  // src/media/color.ts
336
351
  var HEX_PATTERN = /^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
@@ -440,8 +455,153 @@ function resolveColor(value) {
440
455
  a: value.a ?? 255
441
456
  };
442
457
  }
458
+ // src/media/cpuImage.ts
459
+ var FORMAT_TO_MIME = {
460
+ png: "image/png",
461
+ jpeg: "image/jpeg",
462
+ webp: "image/webp"
463
+ };
464
+
465
+ class CpuImage {
466
+ bin;
467
+ backend = "cpu";
468
+ constructor(bin) {
469
+ this.bin = bin;
470
+ }
471
+ get width() {
472
+ if (!this.bin)
473
+ throw new Error("CpuImage.width on a disposed image");
474
+ return this.bin.width;
475
+ }
476
+ get height() {
477
+ if (!this.bin)
478
+ throw new Error("CpuImage.height on a disposed image");
479
+ return this.bin.height;
480
+ }
481
+ get channels() {
482
+ if (!this.bin)
483
+ throw new Error("CpuImage.channels on a disposed image");
484
+ return this.bin.channels;
485
+ }
486
+ getBinary() {
487
+ if (!this.bin)
488
+ throw new Error("CpuImage.getBinary on a disposed image");
489
+ return this.bin;
490
+ }
491
+ static async from(value) {
492
+ if (isBrowserImageValue(value)) {
493
+ if (typeof OffscreenCanvas === "undefined") {
494
+ throw new Error("CpuImage.from(BrowserImageValue) requires OffscreenCanvas");
495
+ }
496
+ const off = new OffscreenCanvas(value.width, value.height);
497
+ const ctx = off.getContext("2d");
498
+ if (!ctx)
499
+ throw new Error("CpuImage.from: could not acquire 2D context");
500
+ ctx.drawImage(value.bitmap, 0, 0);
501
+ const id = ctx.getImageData(0, 0, value.width, value.height);
502
+ return new CpuImage({ data: id.data, width: value.width, height: value.height, channels: 4 });
503
+ }
504
+ if (isNodeImageValue(value)) {
505
+ const bin = await decodeNodeImageValue(value);
506
+ return new CpuImage(bin);
507
+ }
508
+ throw new Error("CpuImage.from: unrecognized ImageValue shape");
509
+ }
510
+ static fromRaw(bin) {
511
+ return new CpuImage(bin);
512
+ }
513
+ async toImageValue(previewScale) {
514
+ if (!this.bin)
515
+ throw new Error("CpuImage.toImageValue on a disposed image");
516
+ if (typeof OffscreenCanvas !== "undefined" && typeof createImageBitmap === "function") {
517
+ const off = new OffscreenCanvas(this.bin.width, this.bin.height);
518
+ const ctx = off.getContext("2d");
519
+ if (!ctx)
520
+ throw new Error("CpuImage.toImageValue could not acquire a 2D context");
521
+ const rgba2 = expandToRgba(this.bin);
522
+ ctx.putImageData(new ImageData(new Uint8ClampedArray(rgba2.buffer, rgba2.byteOffset, rgba2.byteLength), this.bin.width, this.bin.height), 0, 0);
523
+ const bitmap = await createImageBitmap(off);
524
+ const out2 = {
525
+ bitmap,
526
+ width: this.bin.width,
527
+ height: this.bin.height,
528
+ previewScale
529
+ };
530
+ this.bin = null;
531
+ return out2;
532
+ }
533
+ const rgba = expandToRgba(this.bin);
534
+ const buffer = Buffer.from(rgba.buffer, rgba.byteOffset, rgba.byteLength);
535
+ const out = {
536
+ buffer,
537
+ format: "raw-rgba",
538
+ width: this.bin.width,
539
+ height: this.bin.height,
540
+ previewScale
541
+ };
542
+ this.bin = null;
543
+ return out;
544
+ }
545
+ async encode(format, _quality) {
546
+ if (!this.bin)
547
+ throw new Error("CpuImage.encode on a disposed image");
548
+ const codec = getImageRasterCodec();
549
+ const dataUri = await codec.encodeDataUri(this.bin, FORMAT_TO_MIME[format]);
550
+ return dataUriToBytes(dataUri);
551
+ }
552
+ dispose() {
553
+ this.bin = null;
554
+ }
555
+ }
556
+ function expandToRgba(bin) {
557
+ if (bin.channels === 4)
558
+ return bin.data;
559
+ const px = bin.width * bin.height;
560
+ const out = new Uint8ClampedArray(px * 4);
561
+ if (bin.channels === 3) {
562
+ for (let i = 0;i < px; i++) {
563
+ out[i * 4 + 0] = bin.data[i * 3 + 0] ?? 0;
564
+ out[i * 4 + 1] = bin.data[i * 3 + 1] ?? 0;
565
+ out[i * 4 + 2] = bin.data[i * 3 + 2] ?? 0;
566
+ out[i * 4 + 3] = 255;
567
+ }
568
+ } else if (bin.channels === 1) {
569
+ for (let i = 0;i < px; i++) {
570
+ const g = bin.data[i] ?? 0;
571
+ out[i * 4 + 0] = g;
572
+ out[i * 4 + 1] = g;
573
+ out[i * 4 + 2] = g;
574
+ out[i * 4 + 3] = 255;
575
+ }
576
+ }
577
+ return out;
578
+ }
579
+ function dataUriToBytes(dataUri) {
580
+ const comma = dataUri.indexOf(",");
581
+ const b64 = dataUri.slice(comma + 1);
582
+ const bin = atob(b64);
583
+ const bytes = new Uint8Array(bin.length);
584
+ for (let i = 0;i < bin.length; i++)
585
+ bytes[i] = bin.charCodeAt(i);
586
+ return bytes;
587
+ }
588
+ async function decodeNodeImageValue(value) {
589
+ if (value.format === "raw-rgba") {
590
+ const data = new Uint8ClampedArray(value.buffer.buffer, value.buffer.byteOffset, value.buffer.byteLength);
591
+ return { data, width: value.width, height: value.height, channels: 4 };
592
+ }
593
+ const codec = getImageRasterCodec();
594
+ const dataUri = `data:image/${value.format};base64,${value.buffer.toString("base64")}`;
595
+ const decoded = await codec.decodeDataUri(dataUri);
596
+ return {
597
+ data: decoded.data,
598
+ width: decoded.width,
599
+ height: decoded.height,
600
+ channels: decoded.channels
601
+ };
602
+ }
443
603
  // src/media/encode.ts
444
- async function encodeImageBinaryBytes(bin, mimeType) {
604
+ async function rawPixelBufferToBytes(bin, mimeType) {
445
605
  const dataUri = await getImageRasterCodec().encodeDataUri(bin, mimeType);
446
606
  const b64 = dataUri.slice(dataUri.indexOf(",") + 1);
447
607
  const decoded = atob(b64);
@@ -450,28 +610,21 @@ async function encodeImageBinaryBytes(bin, mimeType) {
450
610
  bytes[i] = decoded.charCodeAt(i);
451
611
  return bytes;
452
612
  }
453
- async function encodeImageBinaryToPng(bin) {
454
- return encodeImageBinaryBytes(bin, "image/png");
455
- }
456
- async function imageBinaryToBase64Png(bin) {
457
- const dataUri = await getImageRasterCodec().encodeDataUri(bin, "image/png");
458
- return dataUri.slice(dataUri.indexOf(",") + 1);
459
- }
460
- async function imageBinaryToDataUri(bin, mimeType = "image/png") {
613
+ async function rawPixelBufferToDataUri(bin, mimeType = "image/png") {
461
614
  return getImageRasterCodec().encodeDataUri(bin, mimeType);
462
615
  }
463
- async function imageBinaryToBlob(bin, mimeType = "image/png") {
464
- const bytes = await encodeImageBinaryBytes(bin, mimeType);
616
+ async function rawPixelBufferToBlob(bin, mimeType = "image/png") {
617
+ const bytes = await rawPixelBufferToBytes(bin, mimeType);
465
618
  return new Blob([bytes.buffer], { type: mimeType });
466
619
  }
467
620
  // src/media/filterRegistry.ts
468
621
  var GLOBAL_REGISTRY_KEY = Symbol.for("@workglow/util/media/filterRegistry");
469
- var _g4 = globalThis;
622
+ var _g3 = globalThis;
470
623
  function getRegistry() {
471
- let reg = _g4[GLOBAL_REGISTRY_KEY];
624
+ let reg = _g3[GLOBAL_REGISTRY_KEY];
472
625
  if (!reg) {
473
626
  reg = new Map;
474
- _g4[GLOBAL_REGISTRY_KEY] = reg;
627
+ _g3[GLOBAL_REGISTRY_KEY] = reg;
475
628
  }
476
629
  return reg;
477
630
  }
@@ -514,28 +667,42 @@ async function getGpuDevice() {
514
667
  function resetGpuDeviceForTests() {
515
668
  cached = null;
516
669
  }
517
- // src/media/gpuImageSchema.ts
518
- function GpuImageSchema(annotations = {}) {
670
+ // src/media/gpuImage.ts
671
+ var GLOBAL_FACTORY_KEY = Symbol.for("@workglow/util/media/gpuImageFactory");
672
+ var _g4 = globalThis;
673
+ if (!_g4[GLOBAL_FACTORY_KEY]) {
674
+ _g4[GLOBAL_FACTORY_KEY] = {};
675
+ }
676
+ var factory = _g4[GLOBAL_FACTORY_KEY];
677
+ function registerGpuImageFactory(key2, fn) {
678
+ factory[key2] = fn;
679
+ }
680
+ function getGpuImageFactory(key2) {
681
+ const fn = factory[key2];
682
+ return typeof fn === "function" ? fn : undefined;
683
+ }
684
+ var GpuImage = new Proxy({}, {
685
+ get(_t, prop) {
686
+ if (typeof prop !== "string" || prop === "then")
687
+ return;
688
+ const fn = factory[prop];
689
+ if (typeof fn !== "function") {
690
+ throw new Error(`GpuImage.${prop} is not registered. Import the platform entry point.`);
691
+ }
692
+ return fn;
693
+ }
694
+ });
695
+ // src/media/imageValueSchema.ts
696
+ function ImageValueSchema(annotations = {}) {
519
697
  return {
520
698
  type: ["string", "object"],
521
699
  properties: {},
522
700
  title: "Image",
523
- description: "Image (hydrated to GpuImage by the runner)",
701
+ description: "Image (hydrated to ImageValue at task entry)",
524
702
  ...annotations,
525
703
  format: "image"
526
704
  };
527
705
  }
528
- // src/media/imageTypes.ts
529
- function parseDataUri(dataUri) {
530
- const match = dataUri.match(/^data:([^;]+);base64,(.+)$/);
531
- if (!match) {
532
- throw new Error("Invalid base64 data URI");
533
- }
534
- return {
535
- mimeType: match[1],
536
- base64: match[2]
537
- };
538
- }
539
706
  // src/media/MediaRawImage.ts
540
707
  class MediaRawImage {
541
708
  data;
@@ -578,9 +745,7 @@ function setPreviewBudget(px) {
578
745
  }
579
746
  _g5[GLOBAL_BUDGET_KEY] = Math.floor(px);
580
747
  }
581
- function previewSource(image) {
582
- if (image.backend !== "webgpu")
583
- return image;
748
+ async function previewSource(image) {
584
749
  const budget = getPreviewBudget();
585
750
  const long = Math.max(image.width, image.height);
586
751
  if (long <= budget)
@@ -589,9 +754,14 @@ function previewSource(image) {
589
754
  const resize = getPreviewResizeFn();
590
755
  if (!resize)
591
756
  return image;
592
- const result = resize(image, Math.round(image.width * ratio), Math.round(image.height * ratio));
593
- const composed = image.previewScale * ratio;
594
- return result._setPreviewScale(composed);
757
+ const targetW = Math.max(1, Math.round(image.width * ratio));
758
+ const targetH = Math.max(1, Math.round(image.height * ratio));
759
+ const result = await resize(image, targetW, targetH);
760
+ const composedScale = image.previewScale * ratio;
761
+ return {
762
+ ...result,
763
+ previewScale: composedScale
764
+ };
595
765
  }
596
766
  // src/media/shaderRegistry.browser.ts
597
767
  var VERTEX_PRELUDE = `
@@ -708,37 +878,6 @@ function resetTexturePoolForTests() {
708
878
  }
709
879
  // src/media/webGpuImage.browser.ts
710
880
  var TEX_FORMAT = "rgba8unorm";
711
- var finalizers = typeof FinalizationRegistry !== "undefined" ? new FinalizationRegistry((fn) => {
712
- try {
713
- fn();
714
- } catch {}
715
- }) : undefined;
716
- function expandToRgba2(bin) {
717
- if (bin.channels === 4)
718
- return bin.data;
719
- const px = bin.width * bin.height;
720
- const out = new Uint8ClampedArray(px * 4);
721
- if (bin.channels === 3) {
722
- for (let i = 0;i < px; i++) {
723
- out[i * 4 + 0] = bin.data[i * 3 + 0] ?? 0;
724
- out[i * 4 + 1] = bin.data[i * 3 + 1] ?? 0;
725
- out[i * 4 + 2] = bin.data[i * 3 + 2] ?? 0;
726
- out[i * 4 + 3] = 255;
727
- }
728
- } else if (bin.channels === 1) {
729
- for (let i = 0;i < px; i++) {
730
- const g = bin.data[i] ?? 0;
731
- out[i * 4 + 0] = g;
732
- out[i * 4 + 1] = g;
733
- out[i * 4 + 2] = g;
734
- out[i * 4 + 3] = 255;
735
- }
736
- }
737
- return out;
738
- }
739
- function align(n, m) {
740
- return Math.ceil(n / m) * m;
741
- }
742
881
 
743
882
  class WebGpuImage {
744
883
  device;
@@ -747,43 +886,29 @@ class WebGpuImage {
747
886
  height;
748
887
  backend = "webgpu";
749
888
  channels = 4;
750
- refcount = 1;
751
- _previewScale;
752
- constructor(device, texture, width, height, previewScale = 1) {
889
+ constructor(device, texture, width, height) {
753
890
  this.device = device;
754
891
  this.texture = texture;
755
892
  this.width = width;
756
893
  this.height = height;
757
- this._previewScale = previewScale;
758
- if (finalizers && texture) {
759
- const dev = device;
760
- const tex = texture;
761
- finalizers.register(this, () => {
762
- try {
763
- getTexturePool(dev).release(tex);
764
- } catch {}
765
- }, this);
766
- }
767
- }
768
- get previewScale() {
769
- return this._previewScale;
770
894
  }
771
- _setPreviewScale(scale) {
772
- this._previewScale = scale;
773
- return this;
774
- }
775
- static async fromImageBinary(bin) {
895
+ static async from(value) {
776
896
  const dev = await getGpuDevice();
777
897
  if (!dev)
778
- throw new Error("WebGPU device unavailable; use CpuImage.fromImageBinary instead");
779
- const tex = getTexturePool(dev).acquire(bin.width, bin.height, TEX_FORMAT);
780
- const rgba = bin.channels === 4 ? bin.data : expandToRgba2(bin);
781
- dev.queue.writeTexture({ texture: tex }, rgba, { bytesPerRow: bin.width * 4, rowsPerImage: bin.height }, [bin.width, bin.height, 1]);
782
- return new WebGpuImage(dev, tex, bin.width, bin.height);
898
+ throw new Error("WebGpuImage.from: WebGPU device unavailable");
899
+ if (isNodeImageValue(value)) {
900
+ throw new Error("WebGpuImage.from: NodeImageValue not supported in browser runtime");
901
+ }
902
+ if (!isBrowserImageValue(value)) {
903
+ throw new Error("WebGpuImage.from: unrecognized ImageValue shape");
904
+ }
905
+ const tex = getTexturePool(dev).acquire(value.width, value.height, TEX_FORMAT);
906
+ dev.queue.copyExternalImageToTexture({ source: value.bitmap }, { texture: tex }, [value.width, value.height, 1]);
907
+ return new WebGpuImage(dev, tex, value.width, value.height);
783
908
  }
784
909
  apply(params) {
785
910
  if (!this.texture)
786
- throw new Error("WebGpuImage.apply called on a released image");
911
+ throw new Error("WebGpuImage.apply called on a disposed image");
787
912
  const outW = params.outSize?.width ?? this.width;
788
913
  const outH = params.outSize?.height ?? this.height;
789
914
  const out = getTexturePool(this.device).acquire(outW, outH, TEX_FORMAT);
@@ -825,44 +950,18 @@ class WebGpuImage {
825
950
  pass.draw(3);
826
951
  pass.end();
827
952
  this.device.queue.submit([enc.finish()]);
828
- return new WebGpuImage(this.device, out, outW, outH, this._previewScale);
953
+ return new WebGpuImage(this.device, out, outW, outH);
829
954
  }
830
- async materialize() {
955
+ async transferToImageBitmap() {
831
956
  if (!this.texture)
832
- throw new Error("WebGpuImage.materialize called on a released image");
833
- const bytesPerRow = align(this.width * 4, 256);
834
- const buffer = this.device.createBuffer({
835
- size: bytesPerRow * this.height,
836
- usage: 1 | 8
837
- });
838
- const enc = this.device.createCommandEncoder();
839
- enc.copyTextureToBuffer({ texture: this.texture }, { buffer, bytesPerRow, rowsPerImage: this.height }, [this.width, this.height, 1]);
840
- this.device.queue.submit([enc.finish()]);
841
- await buffer.mapAsync(1);
842
- const mapped = new Uint8Array(buffer.getMappedRange());
843
- const tightStride = this.width * 4;
844
- const tight = new Uint8ClampedArray(this.width * this.height * 4);
845
- if (bytesPerRow === tightStride) {
846
- tight.set(mapped);
847
- } else {
848
- for (let y = 0;y < this.height; y++) {
849
- tight.set(mapped.subarray(y * bytesPerRow, y * bytesPerRow + tightStride), y * tightStride);
850
- }
957
+ throw new Error("WebGpuImage.transferToImageBitmap on a disposed image");
958
+ if (typeof OffscreenCanvas === "undefined") {
959
+ throw new Error("WebGpuImage.transferToImageBitmap requires OffscreenCanvas");
851
960
  }
852
- buffer.unmap();
853
- buffer.destroy();
854
- return { data: tight, width: this.width, height: this.height, channels: 4 };
855
- }
856
- async toCanvas(canvas) {
857
- if (!this.texture)
858
- throw new Error("WebGpuImage.toCanvas called on a released image");
859
- if (canvas.width !== this.width)
860
- canvas.width = this.width;
861
- if (canvas.height !== this.height)
862
- canvas.height = this.height;
863
- const ctx = canvas.getContext("webgpu");
961
+ const off = new OffscreenCanvas(this.width, this.height);
962
+ const ctx = off.getContext("webgpu");
864
963
  if (!ctx)
865
- throw new Error("WebGpuImage.toCanvas requires a webgpu context");
964
+ throw new Error("WebGpuImage.transferToImageBitmap: no webgpu context");
866
965
  const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
867
966
  ctx.configure({ device: this.device, format: presentationFormat, alphaMode: "premultiplied" });
868
967
  const view = ctx.getCurrentTexture().createView();
@@ -890,72 +989,54 @@ class WebGpuImage {
890
989
  pass.draw(3);
891
990
  pass.end();
892
991
  this.device.queue.submit([enc.finish()]);
992
+ try {
993
+ return await createImageBitmap(off);
994
+ } finally {
995
+ this.dispose();
996
+ }
997
+ }
998
+ async toImageValue(previewScale) {
999
+ const bitmap = await this.transferToImageBitmap();
1000
+ return imageValueFromBitmap(bitmap, this.width, this.height, previewScale);
893
1001
  }
894
1002
  async encode(format, quality) {
895
- const bin = await this.materialize();
896
- if (typeof OffscreenCanvas === "undefined") {
897
- throw new Error("WebGpuImage.encode requires an OffscreenCanvas environment");
898
- }
1003
+ if (!this.texture)
1004
+ throw new Error("WebGpuImage.encode on a disposed image");
899
1005
  const off = new OffscreenCanvas(this.width, this.height);
900
1006
  const ctx = off.getContext("2d");
901
1007
  if (!ctx)
902
1008
  throw new Error("WebGpuImage.encode could not acquire a 2D context");
903
- ctx.putImageData(new ImageData(bin.data, this.width, this.height), 0, 0);
1009
+ const bitmap = await this.transferToImageBitmap();
1010
+ ctx.drawImage(bitmap, 0, 0);
904
1011
  const blob = await off.convertToBlob({ type: `image/${format}`, quality });
905
1012
  return new Uint8Array(await blob.arrayBuffer());
906
1013
  }
907
- retain(n = 1) {
908
- if (this.refcount <= 0) {
909
- throw new Error("WebGpuImage.retain called on a released image");
910
- }
911
- this.refcount += n;
912
- return this;
913
- }
914
- release() {
915
- if (this.refcount <= 0) {
916
- throw new Error("WebGpuImage.release called on a released image");
917
- }
918
- this.refcount -= 1;
919
- if (this.refcount > 0)
1014
+ dispose() {
1015
+ if (!this.texture)
920
1016
  return;
921
- if (this.texture) {
922
- const tex = this.texture;
923
- this.texture = null;
924
- if (finalizers)
925
- finalizers.unregister(this);
1017
+ const tex = this.texture;
1018
+ this.texture = null;
1019
+ try {
926
1020
  getTexturePool(this.device).release(tex);
927
- }
1021
+ } catch {}
928
1022
  }
929
1023
  }
1024
+ registerGpuImageFactory("from", WebGpuImage.from.bind(WebGpuImage));
930
1025
  // src/media-browser.ts
931
- async function _preferGpu(bin) {
1026
+ async function probeImageDimensions() {
1027
+ throw new Error("probeImageDimensions: not available in browser runtime");
1028
+ }
1029
+ async function decodeBufferToRaw() {
1030
+ throw new Error("decodeBufferToRaw: not available in browser runtime");
1031
+ }
1032
+ async function encodeRawPixels() {
1033
+ throw new Error("encodeRawPixels: not available in browser runtime");
1034
+ }
1035
+ async function _preferGpu(value) {
932
1036
  const dev = await getGpuDevice();
933
- return dev ? WebGpuImage.fromImageBinary(bin) : CpuImage.fromImageBinary(bin);
1037
+ return dev ? WebGpuImage.from(value) : CpuImage.from(value);
934
1038
  }
935
- registerGpuImageFactory("fromImageBinaryAsync", _preferGpu);
936
- registerGpuImageFactory("fromDataUri", async (dataUri) => {
937
- const bin = await getImageRasterCodec().decodeDataUri(dataUri);
938
- return _preferGpu(bin);
939
- });
940
- registerGpuImageFactory("fromBlob", async (blob) => {
941
- const bitmap = await createImageBitmap(blob);
942
- const off = new OffscreenCanvas(bitmap.width, bitmap.height);
943
- const ctx = off.getContext("2d");
944
- if (!ctx)
945
- throw new Error("fromBlob: could not acquire 2D context");
946
- ctx.drawImage(bitmap, 0, 0);
947
- const id = ctx.getImageData(0, 0, bitmap.width, bitmap.height);
948
- return _preferGpu({ data: id.data, width: bitmap.width, height: bitmap.height, channels: 4 });
949
- });
950
- registerGpuImageFactory("fromImageBitmap", async (bitmap) => {
951
- const off = new OffscreenCanvas(bitmap.width, bitmap.height);
952
- const ctx = off.getContext("2d");
953
- if (!ctx)
954
- throw new Error("fromImageBitmap: could not acquire 2D context");
955
- ctx.drawImage(bitmap, 0, 0);
956
- const id = ctx.getImageData(0, 0, bitmap.width, bitmap.height);
957
- return _preferGpu({ data: id.data, width: bitmap.width, height: bitmap.height, channels: 4 });
958
- });
1039
+ registerGpuImageFactory("from", _preferGpu);
959
1040
  export {
960
1041
  toHexColor,
961
1042
  setPreviewBudget,
@@ -966,15 +1047,20 @@ export {
966
1047
  registerImageRasterCodec,
967
1048
  registerGpuImageFactory,
968
1049
  registerFilterOp,
1050
+ rawPixelBufferToDataUri,
1051
+ rawPixelBufferToBlob,
1052
+ probeImageDimensions,
969
1053
  previewSource,
970
1054
  parseHexColor,
971
- parseDataUri,
1055
+ normalizeToImageValue,
1056
+ isNodeImageValue,
972
1057
  isMediaRawImageShape,
1058
+ isImageValue,
973
1059
  isHexColor,
974
1060
  isColorObject,
975
- imageBinaryToDataUri,
976
- imageBinaryToBlob,
977
- imageBinaryToBase64Png,
1061
+ isBrowserImageValue,
1062
+ imageValueFromBuffer,
1063
+ imageValueFromBitmap,
978
1064
  hasFilterOp,
979
1065
  getTexturePool,
980
1066
  getShaderCache,
@@ -982,7 +1068,8 @@ export {
982
1068
  getImageRasterCodec,
983
1069
  getGpuImageFactory,
984
1070
  getGpuDevice,
985
- encodeImageBinaryToPng,
1071
+ encodeRawPixels,
1072
+ decodeBufferToRaw,
986
1073
  createTexturePool,
987
1074
  createShaderCache,
988
1075
  applyFilter,
@@ -991,9 +1078,9 @@ export {
991
1078
  VERTEX_PRELUDE,
992
1079
  PASSTHROUGH_SHADER_SRC,
993
1080
  MediaRawImage,
994
- GpuImageSchema,
1081
+ ImageValueSchema,
995
1082
  GpuImage as GpuImageFactory,
996
1083
  CpuImage
997
1084
  };
998
1085
 
999
- //# debugId=006D8DBD8329F1E964756E2164756E21
1086
+ //# debugId=4003D4BA5EB9040764756E2164756E21