@workglow/util 0.2.20 → 0.2.22

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 (58) hide show
  1. package/dist/browser.js +5 -2
  2. package/dist/browser.js.map +3 -3
  3. package/dist/bun.js +5 -2
  4. package/dist/bun.js.map +3 -3
  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 +5 -2
  43. package/dist/node.js.map +3 -3
  44. package/dist/schema-entry.js +166 -165
  45. package/dist/schema-entry.js.map +5 -5
  46. package/dist/worker/WorkerManager.d.ts.map +1 -1
  47. package/dist/worker-browser.js +5 -2
  48. package/dist/worker-browser.js.map +3 -3
  49. package/dist/worker-bun.js +5 -2
  50. package/dist/worker-bun.js.map +3 -3
  51. package/dist/worker-node.js +5 -2
  52. package/dist/worker-node.js.map +3 -3
  53. package/package.json +1 -1
  54. package/dist/media/gpuImageSchema.d.ts +0 -8
  55. package/dist/media/gpuImageSchema.d.ts.map +0 -1
  56. package/dist/media/sharpImage.bun.d.ts +0 -7
  57. package/dist/media/sharpImage.bun.d.ts.map +0 -1
  58. package/dist/media/sharpImage.node.d.ts.map +0 -1
@@ -4,39 +4,13 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
  // src/media/imageCacheCodec.ts
5
5
  import { registerPortCodec } from "@workglow/util";
6
6
 
7
- // src/media/gpuImage.ts
8
- var GLOBAL_FACTORY_KEY = Symbol.for("@workglow/util/media/gpuImageFactory");
9
- var _g = globalThis;
10
- if (!_g[GLOBAL_FACTORY_KEY]) {
11
- _g[GLOBAL_FACTORY_KEY] = {};
12
- }
13
- var factory = _g[GLOBAL_FACTORY_KEY];
14
- function registerGpuImageFactory(key, fn) {
15
- factory[key] = fn;
16
- }
17
- function getGpuImageFactory(key) {
18
- const fn = factory[key];
19
- return typeof fn === "function" ? fn : undefined;
20
- }
21
- var GpuImage = new Proxy({}, {
22
- get(_t, prop) {
23
- if (typeof prop !== "string" || prop === "then")
24
- return;
25
- const fn = factory[prop];
26
- if (typeof fn !== "function") {
27
- throw new Error(`GpuImage.${prop} is not registered. Import the platform entry point.`);
28
- }
29
- return fn;
30
- }
31
- });
32
-
33
7
  // src/media/imageRasterCodecRegistry.ts
34
8
  var GLOBAL_CODEC_KEY = Symbol.for("@workglow/util/media/imageRasterCodec");
35
- var _g2 = globalThis;
36
- if (!_g2[GLOBAL_CODEC_KEY]) {
37
- _g2[GLOBAL_CODEC_KEY] = { value: null };
9
+ var _g = globalThis;
10
+ if (!_g[GLOBAL_CODEC_KEY]) {
11
+ _g[GLOBAL_CODEC_KEY] = { value: null };
38
12
  }
39
- var slot = _g2[GLOBAL_CODEC_KEY];
13
+ var slot = _g[GLOBAL_CODEC_KEY];
40
14
  function registerImageRasterCodec(next) {
41
15
  slot.value = next;
42
16
  }
@@ -47,130 +21,170 @@ function getImageRasterCodec() {
47
21
  return slot.value;
48
22
  }
49
23
 
50
- // src/media/cpuImage.ts
51
- var FORMAT_TO_MIME = {
52
- png: "image/png",
53
- jpeg: "image/jpeg",
54
- webp: "image/webp"
55
- };
56
- function dataUriToBytes(dataUri) {
57
- const comma = dataUri.indexOf(",");
58
- const b64 = dataUri.slice(comma + 1);
59
- const bin = atob(b64);
60
- const bytes = new Uint8Array(bin.length);
61
- for (let i = 0;i < bin.length; i++)
62
- bytes[i] = bin.charCodeAt(i);
63
- return bytes;
24
+ // src/media/imageValue.ts
25
+ function imageValueFromBitmap(bitmap, width, height, previewScale = 1) {
26
+ return { bitmap, width, height, previewScale };
64
27
  }
65
- function expandToRgba(bin) {
66
- if (bin.channels === 4)
67
- return bin.data;
68
- const px = bin.width * bin.height;
69
- const out = new Uint8ClampedArray(px * 4);
70
- if (bin.channels === 3) {
71
- for (let i = 0;i < px; i++) {
72
- out[i * 4 + 0] = bin.data[i * 3 + 0] ?? 0;
73
- out[i * 4 + 1] = bin.data[i * 3 + 1] ?? 0;
74
- out[i * 4 + 2] = bin.data[i * 3 + 2] ?? 0;
75
- out[i * 4 + 3] = 255;
76
- }
77
- } else if (bin.channels === 1) {
78
- for (let i = 0;i < px; i++) {
79
- const g = bin.data[i] ?? 0;
80
- out[i * 4 + 0] = g;
81
- out[i * 4 + 1] = g;
82
- out[i * 4 + 2] = g;
83
- out[i * 4 + 3] = 255;
84
- }
85
- }
86
- return out;
28
+ function imageValueFromBuffer(buffer, format, width, height, previewScale = 1) {
29
+ return { buffer, format, width, height, previewScale };
87
30
  }
88
-
89
- class CpuImage {
90
- bin;
91
- backend = "cpu";
92
- _previewScale;
93
- constructor(bin, previewScale = 1) {
94
- this.bin = bin;
95
- this._previewScale = previewScale;
31
+ function isImageValue(v) {
32
+ return isBrowserImageValue(v) || isNodeImageValue(v);
33
+ }
34
+ function isBrowserImageValue(v) {
35
+ if (v === null || typeof v !== "object")
36
+ return false;
37
+ const o = v;
38
+ return typeof o.width === "number" && typeof o.height === "number" && typeof o.previewScale === "number" && typeof ImageBitmap !== "undefined" && o.bitmap instanceof ImageBitmap;
39
+ }
40
+ function isNodeImageValue(v) {
41
+ if (v === null || typeof v !== "object")
42
+ return false;
43
+ const o = v;
44
+ 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");
45
+ }
46
+ async function normalizeToImageValue(value) {
47
+ if (value === null || value === undefined)
48
+ return;
49
+ if (isImageValue(value))
50
+ return value;
51
+ if (typeof Blob !== "undefined" && value instanceof Blob) {
52
+ if (typeof createImageBitmap === "function") {
53
+ const bitmap = await createImageBitmap(value);
54
+ return imageValueFromBitmap(bitmap, bitmap.width, bitmap.height);
55
+ }
56
+ return;
96
57
  }
97
- get width() {
98
- return this.bin.width;
58
+ if (typeof ImageBitmap !== "undefined" && value instanceof ImageBitmap) {
59
+ return imageValueFromBitmap(value, value.width, value.height);
99
60
  }
100
- get height() {
101
- return this.bin.height;
61
+ if (typeof value === "string" && value.startsWith("data:")) {
62
+ if (typeof createImageBitmap === "function" && typeof fetch === "function") {
63
+ const blob = await (await fetch(value)).blob();
64
+ const bitmap = await createImageBitmap(blob);
65
+ return imageValueFromBitmap(bitmap, bitmap.width, bitmap.height);
66
+ }
67
+ if (typeof Buffer !== "undefined") {
68
+ return decodeDataUriToNodeImageValue(value);
69
+ }
70
+ return;
102
71
  }
103
- get channels() {
104
- return this.bin.channels;
72
+ return;
73
+ }
74
+ async function decodeDataUriToNodeImageValue(dataUri) {
75
+ const match = /^data:([^;,]+);base64,(.+)$/.exec(dataUri);
76
+ if (!match)
77
+ return;
78
+ const mime = match[1] ?? "image/png";
79
+ const base64 = match[2] ?? "";
80
+ const buffer = Buffer.from(base64, "base64");
81
+ const format = /jpe?g/i.test(mime) ? "jpeg" : "png";
82
+ try {
83
+ const decoded = await getImageRasterCodec().decodeDataUri(dataUri);
84
+ return imageValueFromBuffer(buffer, format, decoded.width, decoded.height);
85
+ } catch (err) {
86
+ throw new Error("normalizeToImageValue: failed to probe data URI dimensions", {
87
+ cause: err
88
+ });
105
89
  }
106
- get previewScale() {
107
- return this._previewScale;
90
+ }
91
+
92
+ // src/media/imageCacheCodec.ts
93
+ function isImageValueWire(v) {
94
+ if (v === null || typeof v !== "object")
95
+ return false;
96
+ const o = v;
97
+ 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";
98
+ }
99
+ function bytesToBase64(bytes) {
100
+ if (typeof Buffer !== "undefined") {
101
+ return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
108
102
  }
109
- _setPreviewScale(scale) {
110
- this._previewScale = scale;
111
- return this;
103
+ let bin = "";
104
+ for (let i = 0;i < bytes.length; i++)
105
+ bin += String.fromCharCode(bytes[i] ?? 0);
106
+ return btoa(bin);
107
+ }
108
+ function base64ToBytes(b64) {
109
+ if (typeof Buffer !== "undefined") {
110
+ const buf = Buffer.from(b64, "base64");
111
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
112
112
  }
113
- async materialize() {
114
- return this.bin;
113
+ const bin = atob(b64);
114
+ const out = new Uint8Array(bin.length);
115
+ for (let i = 0;i < bin.length; i++)
116
+ out[i] = bin.charCodeAt(i);
117
+ return out;
118
+ }
119
+ async function browserToPngBase64(value) {
120
+ if (typeof OffscreenCanvas === "undefined") {
121
+ throw new Error("imageCacheCodec.serialize: BrowserImageValue requires OffscreenCanvas");
115
122
  }
116
- getBinary() {
117
- return this.bin;
123
+ const off = new OffscreenCanvas(value.width, value.height);
124
+ const ctx = off.getContext("2d");
125
+ if (!ctx)
126
+ throw new Error("imageCacheCodec.serialize: could not acquire 2D context");
127
+ ctx.drawImage(value.bitmap, 0, 0);
128
+ const blob = await off.convertToBlob({ type: "image/png" });
129
+ const bytes = new Uint8Array(await blob.arrayBuffer());
130
+ return bytesToBase64(bytes);
131
+ }
132
+ async function wireToBrowserImageValue(wire) {
133
+ if (typeof createImageBitmap !== "function") {
134
+ throw new Error("imageCacheCodec.deserialize: browser path requires createImageBitmap");
118
135
  }
119
- async toCanvas(canvas) {
136
+ const bytes = base64ToBytes(wire.base64);
137
+ if (wire.format === "raw-rgba") {
120
138
  if (typeof ImageData === "undefined") {
121
- throw new Error("CpuImage.toCanvas requires a browser environment with ImageData");
139
+ throw new Error("imageCacheCodec.deserialize: raw-rgba decode requires ImageData");
122
140
  }
123
- const rgba = expandToRgba(this.bin);
124
- const id = new ImageData(new Uint8ClampedArray(rgba.buffer, rgba.byteOffset, rgba.byteLength), this.bin.width, this.bin.height);
125
- if (canvas.width !== this.bin.width)
126
- canvas.width = this.bin.width;
127
- if (canvas.height !== this.bin.height)
128
- canvas.height = this.bin.height;
129
- const ctx = canvas.getContext("2d");
130
- if (!ctx)
131
- throw new Error("CpuImage.toCanvas could not acquire a 2D context");
132
- ctx.putImageData(id, 0, 0);
133
- }
134
- async encode(format, _quality) {
135
- const codec = getImageRasterCodec();
136
- const dataUri = await codec.encodeDataUri(this.bin, FORMAT_TO_MIME[format]);
137
- return dataUriToBytes(dataUri);
138
- }
139
- retain(_n = 1) {
140
- return this;
141
- }
142
- release() {}
143
- static fromImageBinary(bin, previewScale = 1) {
144
- return new CpuImage(bin, previewScale);
141
+ const data = new Uint8ClampedArray(bytes.buffer, bytes.byteOffset, bytes.byteLength);
142
+ const imageData = new ImageData(data, wire.width, wire.height);
143
+ const bitmap2 = await createImageBitmap(imageData);
144
+ return { bitmap: bitmap2, width: wire.width, height: wire.height, previewScale: wire.previewScale };
145
145
  }
146
+ const mime = wire.format === "jpeg" ? "image/jpeg" : "image/png";
147
+ const blob = new Blob([bytes.buffer], { type: mime });
148
+ const bitmap = await createImageBitmap(blob);
149
+ return { bitmap, width: wire.width, height: wire.height, previewScale: wire.previewScale };
146
150
  }
147
- registerGpuImageFactory("fromImageBinary", CpuImage.fromImageBinary);
148
-
149
- // src/media/imageCacheCodec.ts
150
151
  registerPortCodec("image", {
151
152
  async serialize(value) {
152
- if (typeof value.materialize !== "function") {
153
+ if (typeof value === "string")
153
154
  return value;
155
+ if (isNodeImageValue(value)) {
156
+ return {
157
+ __imageValueWire: 1,
158
+ format: value.format,
159
+ base64: value.buffer.toString("base64"),
160
+ width: value.width,
161
+ height: value.height,
162
+ previewScale: value.previewScale
163
+ };
154
164
  }
155
- const bin = await value.materialize();
156
- return {
157
- kind: "image-binary",
158
- width: bin.width,
159
- height: bin.height,
160
- channels: bin.channels,
161
- data: bin.data
162
- };
165
+ if (isBrowserImageValue(value)) {
166
+ const base64 = await browserToPngBase64(value);
167
+ return {
168
+ __imageValueWire: 1,
169
+ format: "png",
170
+ base64,
171
+ width: value.width,
172
+ height: value.height,
173
+ previewScale: value.previewScale
174
+ };
175
+ }
176
+ throw new Error("imageCacheCodec.serialize: value is not an ImageValue or string");
163
177
  },
164
- async deserialize(cached) {
165
- if (cached.kind !== "image-binary") {
166
- return cached;
178
+ async deserialize(wire) {
179
+ if (typeof wire === "string")
180
+ return wire;
181
+ if (!isImageValueWire(wire)) {
182
+ throw new Error("imageCacheCodec.deserialize: input is not an ImageValueWire or string");
167
183
  }
168
- return CpuImage.fromImageBinary({
169
- data: cached.data,
170
- width: cached.width,
171
- height: cached.height,
172
- channels: cached.channels
173
- });
184
+ if (typeof Buffer !== "undefined") {
185
+ return imageValueFromBuffer(Buffer.from(wire.base64, "base64"), wire.format, wire.width, wire.height, wire.previewScale);
186
+ }
187
+ return wireToBrowserImageValue(wire);
174
188
  }
175
189
  });
176
190
 
@@ -180,17 +194,17 @@ class Container {
180
194
  factories = new Map;
181
195
  singletons = new Set;
182
196
  resolving = [];
183
- register(token, factory2, singleton = true) {
184
- this.factories.set(token, factory2);
197
+ register(token, factory, singleton = true) {
198
+ this.factories.set(token, factory);
185
199
  if (singleton) {
186
200
  this.singletons.add(token);
187
201
  }
188
202
  }
189
- registerIfAbsent(token, factory2, singleton = true) {
203
+ registerIfAbsent(token, factory, singleton = true) {
190
204
  if (this.factories.has(token) || this.services.has(token)) {
191
205
  return;
192
206
  }
193
- this.register(token, factory2, singleton);
207
+ this.register(token, factory, singleton);
194
208
  }
195
209
  registerInstance(token, instance) {
196
210
  this.services.set(token, instance);
@@ -200,8 +214,8 @@ class Container {
200
214
  if (this.services.has(token)) {
201
215
  return this.services.get(token);
202
216
  }
203
- const factory2 = this.factories.get(token);
204
- if (!factory2) {
217
+ const factory = this.factories.get(token);
218
+ if (!factory) {
205
219
  throw new Error(`Service not registered: ${String(token)}`);
206
220
  }
207
221
  if (this.resolving.includes(token)) {
@@ -210,7 +224,7 @@ class Container {
210
224
  }
211
225
  this.resolving.push(token);
212
226
  try {
213
- const instance = factory2();
227
+ const instance = factory();
214
228
  if (this.singletons.has(token)) {
215
229
  this.services.set(token, instance);
216
230
  }
@@ -259,8 +273,8 @@ class Container {
259
273
  }
260
274
  createChildContainer() {
261
275
  const child = new Container;
262
- this.factories.forEach((factory2, token) => {
263
- child.factories.set(token, factory2);
276
+ this.factories.forEach((factory, token) => {
277
+ child.factories.set(token, factory);
264
278
  if (this.singletons.has(token)) {
265
279
  child.singletons.add(token);
266
280
  }
@@ -275,11 +289,11 @@ class Container {
275
289
  }
276
290
  }
277
291
  var GLOBAL_CONTAINER_KEY = Symbol.for("@workglow/util/di/globalContainer");
278
- var _g3 = globalThis;
279
- if (!_g3[GLOBAL_CONTAINER_KEY]) {
280
- _g3[GLOBAL_CONTAINER_KEY] = new Container;
292
+ var _g2 = globalThis;
293
+ if (!_g2[GLOBAL_CONTAINER_KEY]) {
294
+ _g2[GLOBAL_CONTAINER_KEY] = new Container;
281
295
  }
282
- var globalContainer = _g3[GLOBAL_CONTAINER_KEY];
296
+ var globalContainer = _g2[GLOBAL_CONTAINER_KEY];
283
297
 
284
298
  // src/di/ServiceRegistry.ts
285
299
  function createServiceToken(id) {
@@ -291,11 +305,11 @@ class ServiceRegistry {
291
305
  constructor(container = globalContainer) {
292
306
  this.container = container;
293
307
  }
294
- register(token, factory2, singleton = true) {
295
- this.container.register(token.id, factory2, singleton);
308
+ register(token, factory, singleton = true) {
309
+ this.container.register(token.id, factory, singleton);
296
310
  }
297
- registerIfAbsent(token, factory2, singleton = true) {
298
- this.container.registerIfAbsent(token.id, factory2, singleton);
311
+ registerIfAbsent(token, factory, singleton = true) {
312
+ this.container.registerIfAbsent(token.id, factory, singleton);
299
313
  }
300
314
  registerInstance(token, instance) {
301
315
  this.container.registerInstance(token.id, instance);
@@ -324,16 +338,17 @@ function registerInputResolver(formatPrefix, resolver) {
324
338
  }
325
339
 
326
340
  // src/media/imageHydrationResolver.ts
327
- async function resolveImageString(id, _format, _registry) {
328
- if (typeof id !== "string")
329
- return id;
330
- if (id.startsWith("data:")) {
331
- return GpuImage.fromDataUri(id);
341
+ async function resolveImage(id, _format, _registry) {
342
+ const normalized = await normalizeToImageValue(id);
343
+ if (normalized !== undefined)
344
+ return normalized;
345
+ if (typeof id === "string") {
346
+ const preview = id.length > 32 ? `${id.slice(0, 32)}...` : id;
347
+ throw new Error(`format:"image" resolver received an unsupported string "${preview}". ` + `Only data: URIs are handled. Register a sub-resolver for other schemes.`);
332
348
  }
333
- const preview = id.length > 32 ? `${id.slice(0, 32)}...` : id;
334
- 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).`);
349
+ return id;
335
350
  }
336
- registerInputResolver("image", resolveImageString);
351
+ registerInputResolver("image", resolveImage);
337
352
 
338
353
  // src/media/color.ts
339
354
  var HEX_PATTERN = /^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
@@ -443,8 +458,153 @@ function resolveColor(value) {
443
458
  a: value.a ?? 255
444
459
  };
445
460
  }
461
+ // src/media/cpuImage.ts
462
+ var FORMAT_TO_MIME = {
463
+ png: "image/png",
464
+ jpeg: "image/jpeg",
465
+ webp: "image/webp"
466
+ };
467
+
468
+ class CpuImage {
469
+ bin;
470
+ backend = "cpu";
471
+ constructor(bin) {
472
+ this.bin = bin;
473
+ }
474
+ get width() {
475
+ if (!this.bin)
476
+ throw new Error("CpuImage.width on a disposed image");
477
+ return this.bin.width;
478
+ }
479
+ get height() {
480
+ if (!this.bin)
481
+ throw new Error("CpuImage.height on a disposed image");
482
+ return this.bin.height;
483
+ }
484
+ get channels() {
485
+ if (!this.bin)
486
+ throw new Error("CpuImage.channels on a disposed image");
487
+ return this.bin.channels;
488
+ }
489
+ getBinary() {
490
+ if (!this.bin)
491
+ throw new Error("CpuImage.getBinary on a disposed image");
492
+ return this.bin;
493
+ }
494
+ static async from(value) {
495
+ if (isBrowserImageValue(value)) {
496
+ if (typeof OffscreenCanvas === "undefined") {
497
+ throw new Error("CpuImage.from(BrowserImageValue) requires OffscreenCanvas");
498
+ }
499
+ const off = new OffscreenCanvas(value.width, value.height);
500
+ const ctx = off.getContext("2d");
501
+ if (!ctx)
502
+ throw new Error("CpuImage.from: could not acquire 2D context");
503
+ ctx.drawImage(value.bitmap, 0, 0);
504
+ const id = ctx.getImageData(0, 0, value.width, value.height);
505
+ return new CpuImage({ data: id.data, width: value.width, height: value.height, channels: 4 });
506
+ }
507
+ if (isNodeImageValue(value)) {
508
+ const bin = await decodeNodeImageValue(value);
509
+ return new CpuImage(bin);
510
+ }
511
+ throw new Error("CpuImage.from: unrecognized ImageValue shape");
512
+ }
513
+ static fromRaw(bin) {
514
+ return new CpuImage(bin);
515
+ }
516
+ async toImageValue(previewScale) {
517
+ if (!this.bin)
518
+ throw new Error("CpuImage.toImageValue on a disposed image");
519
+ if (typeof OffscreenCanvas !== "undefined" && typeof createImageBitmap === "function") {
520
+ const off = new OffscreenCanvas(this.bin.width, this.bin.height);
521
+ const ctx = off.getContext("2d");
522
+ if (!ctx)
523
+ throw new Error("CpuImage.toImageValue could not acquire a 2D context");
524
+ const rgba2 = expandToRgba(this.bin);
525
+ ctx.putImageData(new ImageData(new Uint8ClampedArray(rgba2.buffer, rgba2.byteOffset, rgba2.byteLength), this.bin.width, this.bin.height), 0, 0);
526
+ const bitmap = await createImageBitmap(off);
527
+ const out2 = {
528
+ bitmap,
529
+ width: this.bin.width,
530
+ height: this.bin.height,
531
+ previewScale
532
+ };
533
+ this.bin = null;
534
+ return out2;
535
+ }
536
+ const rgba = expandToRgba(this.bin);
537
+ const buffer = Buffer.from(rgba.buffer, rgba.byteOffset, rgba.byteLength);
538
+ const out = {
539
+ buffer,
540
+ format: "raw-rgba",
541
+ width: this.bin.width,
542
+ height: this.bin.height,
543
+ previewScale
544
+ };
545
+ this.bin = null;
546
+ return out;
547
+ }
548
+ async encode(format, _quality) {
549
+ if (!this.bin)
550
+ throw new Error("CpuImage.encode on a disposed image");
551
+ const codec = getImageRasterCodec();
552
+ const dataUri = await codec.encodeDataUri(this.bin, FORMAT_TO_MIME[format]);
553
+ return dataUriToBytes(dataUri);
554
+ }
555
+ dispose() {
556
+ this.bin = null;
557
+ }
558
+ }
559
+ function expandToRgba(bin) {
560
+ if (bin.channels === 4)
561
+ return bin.data;
562
+ const px = bin.width * bin.height;
563
+ const out = new Uint8ClampedArray(px * 4);
564
+ if (bin.channels === 3) {
565
+ for (let i = 0;i < px; i++) {
566
+ out[i * 4 + 0] = bin.data[i * 3 + 0] ?? 0;
567
+ out[i * 4 + 1] = bin.data[i * 3 + 1] ?? 0;
568
+ out[i * 4 + 2] = bin.data[i * 3 + 2] ?? 0;
569
+ out[i * 4 + 3] = 255;
570
+ }
571
+ } else if (bin.channels === 1) {
572
+ for (let i = 0;i < px; i++) {
573
+ const g = bin.data[i] ?? 0;
574
+ out[i * 4 + 0] = g;
575
+ out[i * 4 + 1] = g;
576
+ out[i * 4 + 2] = g;
577
+ out[i * 4 + 3] = 255;
578
+ }
579
+ }
580
+ return out;
581
+ }
582
+ function dataUriToBytes(dataUri) {
583
+ const comma = dataUri.indexOf(",");
584
+ const b64 = dataUri.slice(comma + 1);
585
+ const bin = atob(b64);
586
+ const bytes = new Uint8Array(bin.length);
587
+ for (let i = 0;i < bin.length; i++)
588
+ bytes[i] = bin.charCodeAt(i);
589
+ return bytes;
590
+ }
591
+ async function decodeNodeImageValue(value) {
592
+ if (value.format === "raw-rgba") {
593
+ const data = new Uint8ClampedArray(value.buffer.buffer, value.buffer.byteOffset, value.buffer.byteLength);
594
+ return { data, width: value.width, height: value.height, channels: 4 };
595
+ }
596
+ const codec = getImageRasterCodec();
597
+ const dataUri = `data:image/${value.format};base64,${value.buffer.toString("base64")}`;
598
+ const decoded = await codec.decodeDataUri(dataUri);
599
+ return {
600
+ data: decoded.data,
601
+ width: decoded.width,
602
+ height: decoded.height,
603
+ channels: decoded.channels
604
+ };
605
+ }
446
606
  // src/media/encode.ts
447
- async function encodeImageBinaryBytes(bin, mimeType) {
607
+ async function rawPixelBufferToBytes(bin, mimeType) {
448
608
  const dataUri = await getImageRasterCodec().encodeDataUri(bin, mimeType);
449
609
  const b64 = dataUri.slice(dataUri.indexOf(",") + 1);
450
610
  const decoded = atob(b64);
@@ -453,28 +613,21 @@ async function encodeImageBinaryBytes(bin, mimeType) {
453
613
  bytes[i] = decoded.charCodeAt(i);
454
614
  return bytes;
455
615
  }
456
- async function encodeImageBinaryToPng(bin) {
457
- return encodeImageBinaryBytes(bin, "image/png");
458
- }
459
- async function imageBinaryToBase64Png(bin) {
460
- const dataUri = await getImageRasterCodec().encodeDataUri(bin, "image/png");
461
- return dataUri.slice(dataUri.indexOf(",") + 1);
462
- }
463
- async function imageBinaryToDataUri(bin, mimeType = "image/png") {
616
+ async function rawPixelBufferToDataUri(bin, mimeType = "image/png") {
464
617
  return getImageRasterCodec().encodeDataUri(bin, mimeType);
465
618
  }
466
- async function imageBinaryToBlob(bin, mimeType = "image/png") {
467
- const bytes = await encodeImageBinaryBytes(bin, mimeType);
619
+ async function rawPixelBufferToBlob(bin, mimeType = "image/png") {
620
+ const bytes = await rawPixelBufferToBytes(bin, mimeType);
468
621
  return new Blob([bytes.buffer], { type: mimeType });
469
622
  }
470
623
  // src/media/filterRegistry.ts
471
624
  var GLOBAL_REGISTRY_KEY = Symbol.for("@workglow/util/media/filterRegistry");
472
- var _g4 = globalThis;
625
+ var _g3 = globalThis;
473
626
  function getRegistry() {
474
- let reg = _g4[GLOBAL_REGISTRY_KEY];
627
+ let reg = _g3[GLOBAL_REGISTRY_KEY];
475
628
  if (!reg) {
476
629
  reg = new Map;
477
- _g4[GLOBAL_REGISTRY_KEY] = reg;
630
+ _g3[GLOBAL_REGISTRY_KEY] = reg;
478
631
  }
479
632
  return reg;
480
633
  }
@@ -495,28 +648,42 @@ function hasFilterOp(backend, filter) {
495
648
  function _resetFilterRegistryForTests() {
496
649
  getRegistry().clear();
497
650
  }
498
- // src/media/gpuImageSchema.ts
499
- function GpuImageSchema(annotations = {}) {
651
+ // src/media/gpuImage.ts
652
+ var GLOBAL_FACTORY_KEY = Symbol.for("@workglow/util/media/gpuImageFactory");
653
+ var _g4 = globalThis;
654
+ if (!_g4[GLOBAL_FACTORY_KEY]) {
655
+ _g4[GLOBAL_FACTORY_KEY] = {};
656
+ }
657
+ var factory = _g4[GLOBAL_FACTORY_KEY];
658
+ function registerGpuImageFactory(key2, fn) {
659
+ factory[key2] = fn;
660
+ }
661
+ function getGpuImageFactory(key2) {
662
+ const fn = factory[key2];
663
+ return typeof fn === "function" ? fn : undefined;
664
+ }
665
+ var GpuImage = new Proxy({}, {
666
+ get(_t, prop) {
667
+ if (typeof prop !== "string" || prop === "then")
668
+ return;
669
+ const fn = factory[prop];
670
+ if (typeof fn !== "function") {
671
+ throw new Error(`GpuImage.${prop} is not registered. Import the platform entry point.`);
672
+ }
673
+ return fn;
674
+ }
675
+ });
676
+ // src/media/imageValueSchema.ts
677
+ function ImageValueSchema(annotations = {}) {
500
678
  return {
501
679
  type: ["string", "object"],
502
680
  properties: {},
503
681
  title: "Image",
504
- description: "Image (hydrated to GpuImage by the runner)",
682
+ description: "Image (hydrated to ImageValue at task entry)",
505
683
  ...annotations,
506
684
  format: "image"
507
685
  };
508
686
  }
509
- // src/media/imageTypes.ts
510
- function parseDataUri(dataUri) {
511
- const match = dataUri.match(/^data:([^;]+);base64,(.+)$/);
512
- if (!match) {
513
- throw new Error("Invalid base64 data URI");
514
- }
515
- return {
516
- mimeType: match[1],
517
- base64: match[2]
518
- };
519
- }
520
687
  // src/media/MediaRawImage.ts
521
688
  class MediaRawImage {
522
689
  data;
@@ -559,9 +726,7 @@ function setPreviewBudget(px) {
559
726
  }
560
727
  _g5[GLOBAL_BUDGET_KEY] = Math.floor(px);
561
728
  }
562
- function previewSource(image) {
563
- if (image.backend !== "webgpu")
564
- return image;
729
+ async function previewSource(image) {
565
730
  const budget = getPreviewBudget();
566
731
  const long = Math.max(image.width, image.height);
567
732
  if (long <= budget)
@@ -570,9 +735,14 @@ function previewSource(image) {
570
735
  const resize = getPreviewResizeFn();
571
736
  if (!resize)
572
737
  return image;
573
- const result = resize(image, Math.round(image.width * ratio), Math.round(image.height * ratio));
574
- const composed = image.previewScale * ratio;
575
- return result._setPreviewScale(composed);
738
+ const targetW = Math.max(1, Math.round(image.width * ratio));
739
+ const targetH = Math.max(1, Math.round(image.height * ratio));
740
+ const result = await resize(image, targetW, targetH);
741
+ const composedScale = image.previewScale * ratio;
742
+ return {
743
+ ...result,
744
+ previewScale: composedScale
745
+ };
576
746
  }
577
747
  // src/media/shaderRegistry.browser.ts
578
748
  var VERTEX_PRELUDE = `
@@ -687,12 +857,17 @@ function resetTexturePoolForTests() {
687
857
  singleton2?.pool.drain();
688
858
  singleton2 = null;
689
859
  }
690
- // src/media/sharpImage.node.ts
860
+ // src/media/sharpImage.server.ts
691
861
  var cachedSharp = null;
692
862
  async function loadSharp() {
693
863
  if (cachedSharp)
694
864
  return cachedSharp;
695
- const mod = await import("sharp");
865
+ let mod;
866
+ try {
867
+ mod = await import("sharp");
868
+ } catch {
869
+ throw new Error("Server-side image processing requires the optional 'sharp' package. Install it with: npm install sharp (or bun add sharp)");
870
+ }
696
871
  cachedSharp = mod.default ?? mod;
697
872
  return cachedSharp;
698
873
  }
@@ -703,61 +878,69 @@ class SharpImage {
703
878
  height;
704
879
  channels;
705
880
  backend = "sharp";
706
- _previewScale;
707
- constructor(pipeline, width, height, channels, previewScale = 1) {
881
+ constructor(pipeline, width, height, channels) {
708
882
  this.pipeline = pipeline;
709
883
  this.width = width;
710
884
  this.height = height;
711
885
  this.channels = channels;
712
- this._previewScale = previewScale;
713
886
  }
714
- get previewScale() {
715
- return this._previewScale;
716
- }
717
- _setPreviewScale(scale) {
718
- this._previewScale = scale;
719
- return this;
720
- }
721
- static async fromImageBinary(bin, previewScale = 1) {
722
- const sharp = await loadSharp();
723
- const buf = Buffer.from(bin.data.buffer, bin.data.byteOffset, bin.data.byteLength);
724
- const pipeline = sharp(buf, {
725
- raw: { width: bin.width, height: bin.height, channels: bin.channels }
726
- });
727
- return new SharpImage(pipeline, bin.width, bin.height, bin.channels, previewScale);
728
- }
729
- static async fromBuffer(buf) {
887
+ static async from(value) {
888
+ if (isBrowserImageValue(value)) {
889
+ throw new Error("SharpImage.from: BrowserImageValue not supported in node runtime");
890
+ }
891
+ if (!isNodeImageValue(value)) {
892
+ throw new Error("SharpImage.from: unrecognized ImageValue shape");
893
+ }
730
894
  const sharp = await loadSharp();
731
- const pipeline = sharp(buf);
732
- const meta = await pipeline.clone().metadata();
733
- if (typeof meta.width !== "number" || typeof meta.height !== "number") {
734
- throw new Error("SharpImage.fromBuffer: input has no width/height metadata");
895
+ if (value.format === "raw-rgba") {
896
+ const pipeline2 = sharp(value.buffer, {
897
+ raw: { width: value.width, height: value.height, channels: 4 }
898
+ });
899
+ return new SharpImage(pipeline2, value.width, value.height, 4);
735
900
  }
736
- return new SharpImage(pipeline, meta.width, meta.height, meta.channels ?? 4);
901
+ const pipeline = sharp(value.buffer);
902
+ const meta = await pipeline.clone().metadata();
903
+ const channels = meta.channels ?? 4;
904
+ return new SharpImage(pipeline, value.width, value.height, channels);
737
905
  }
738
906
  apply(op, outSize) {
907
+ if (!this.pipeline)
908
+ throw new Error("SharpImage.apply on a disposed image");
739
909
  const next = op(this.pipeline.clone());
740
- return new SharpImage(next, outSize?.width ?? this.width, outSize?.height ?? this.height, outSize?.channels ?? this.channels, this._previewScale);
910
+ return new SharpImage(next, outSize?.width ?? this.width, outSize?.height ?? this.height, outSize?.channels ?? this.channels);
741
911
  }
742
- async materialize() {
743
- const result = await this.pipeline.clone().raw().toBuffer({ resolveWithObject: true });
744
- if (!isObjectResult(result)) {
745
- throw new Error("SharpImage.materialize: expected resolveWithObject result");
912
+ async toBuffer(format) {
913
+ if (!this.pipeline)
914
+ throw new Error("SharpImage.toBuffer on a disposed image");
915
+ const p = this.pipeline.clone();
916
+ if (format === "raw-rgba") {
917
+ const result = await p.raw().toBuffer({ resolveWithObject: true });
918
+ if (!isObjectResult(result))
919
+ throw new Error("SharpImage.toBuffer: expected resolveWithObject result");
920
+ return result.data;
746
921
  }
747
- const { data, info } = result;
748
- const out = new Uint8ClampedArray(data.length);
749
- out.set(data);
750
- return {
751
- data: out,
752
- width: info.width,
753
- height: info.height,
754
- channels: info.channels
755
- };
922
+ if (format === "png")
923
+ return await p.png().toBuffer();
924
+ return await p.jpeg().toBuffer();
756
925
  }
757
- async toCanvas(_canvas) {
758
- throw new Error("SharpImage.toCanvas is not supported in node/bun environments");
926
+ async toImageValue(previewScale) {
927
+ try {
928
+ const buffer = await this.toBuffer("png");
929
+ const out = {
930
+ buffer,
931
+ format: "png",
932
+ width: this.width,
933
+ height: this.height,
934
+ previewScale
935
+ };
936
+ return out;
937
+ } finally {
938
+ this.dispose();
939
+ }
759
940
  }
760
941
  async encode(format, quality) {
942
+ if (!this.pipeline)
943
+ throw new Error("SharpImage.encode on a disposed image");
761
944
  const p = this.pipeline.clone();
762
945
  let result;
763
946
  if (format === "png")
@@ -769,28 +952,64 @@ class SharpImage {
769
952
  const buf = result;
770
953
  return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
771
954
  }
772
- retain(_n = 1) {
773
- return this;
955
+ dispose() {
956
+ this.pipeline = null;
774
957
  }
775
- release() {}
776
958
  }
777
959
  function isObjectResult(r) {
778
960
  return !!r && typeof r === "object" && "data" in r && "info" in r;
779
961
  }
962
+ async function probeImageDimensions(buffer) {
963
+ const sharp = await loadSharp();
964
+ const meta = await sharp(buffer).metadata();
965
+ if (typeof meta.width !== "number" || typeof meta.height !== "number") {
966
+ throw new Error("probeImageDimensions: sharp could not read image dimensions");
967
+ }
968
+ return { width: meta.width, height: meta.height, channels: meta.channels };
969
+ }
970
+ async function decodeBufferToRaw(buffer, options) {
971
+ const sharp = await loadSharp();
972
+ const sharpOpts = {};
973
+ if (options?.limitInputPixels !== undefined)
974
+ sharpOpts.limitInputPixels = options.limitInputPixels;
975
+ if (options?.sequentialRead !== undefined)
976
+ sharpOpts.sequentialRead = options.sequentialRead;
977
+ let pipeline = sharp(buffer, sharpOpts);
978
+ if (options?.ensureAlpha)
979
+ pipeline = pipeline.ensureAlpha();
980
+ const result = await pipeline.raw().toBuffer({ resolveWithObject: true });
981
+ if (!isObjectResult(result))
982
+ throw new Error("decodeBufferToRaw: expected resolveWithObject result");
983
+ return {
984
+ data: result.data,
985
+ width: result.info.width,
986
+ height: result.info.height,
987
+ channels: result.info.channels
988
+ };
989
+ }
990
+ async function encodeRawPixels(raw, options) {
991
+ const sharp = await loadSharp();
992
+ const inputBuffer = raw.data instanceof Uint8ClampedArray ? Buffer.from(raw.data.buffer, raw.data.byteOffset, raw.data.byteLength) : raw.data;
993
+ const pipeline = sharp(inputBuffer, {
994
+ raw: { width: raw.width, height: raw.height, channels: raw.channels }
995
+ });
996
+ let encoded;
997
+ if (options.format === "png") {
998
+ encoded = await pipeline.png({ compressionLevel: options.compressionLevel }).toBuffer();
999
+ } else if (options.format === "jpeg") {
1000
+ encoded = await pipeline.jpeg({ quality: options.quality, mozjpeg: options.mozjpeg }).toBuffer();
1001
+ } else {
1002
+ encoded = await pipeline.webp({ quality: options.quality }).toBuffer();
1003
+ }
1004
+ return encoded;
1005
+ }
1006
+ registerGpuImageFactory("from", SharpImage.from.bind(SharpImage));
780
1007
  // src/media-node.ts
781
1008
  async function getGpuDevice() {
782
1009
  return null;
783
1010
  }
784
1011
  function resetGpuDeviceForTests() {}
785
- registerGpuImageFactory("fromImageBinaryAsync", (bin) => SharpImage.fromImageBinary(bin));
786
- registerGpuImageFactory("fromDataUri", async (dataUri) => {
787
- const bin = await getImageRasterCodec().decodeDataUri(dataUri);
788
- return SharpImage.fromImageBinary(bin);
789
- });
790
- registerGpuImageFactory("fromBlob", async (blob) => {
791
- const buf = Buffer.from(await blob.arrayBuffer());
792
- return SharpImage.fromBuffer(buf);
793
- });
1012
+ registerGpuImageFactory("from", (value) => SharpImage.from(value));
794
1013
  export {
795
1014
  toHexColor,
796
1015
  setPreviewBudget,
@@ -801,15 +1020,20 @@ export {
801
1020
  registerImageRasterCodec,
802
1021
  registerGpuImageFactory,
803
1022
  registerFilterOp,
1023
+ rawPixelBufferToDataUri,
1024
+ rawPixelBufferToBlob,
1025
+ probeImageDimensions,
804
1026
  previewSource,
805
1027
  parseHexColor,
806
- parseDataUri,
1028
+ normalizeToImageValue,
1029
+ isNodeImageValue,
807
1030
  isMediaRawImageShape,
1031
+ isImageValue,
808
1032
  isHexColor,
809
1033
  isColorObject,
810
- imageBinaryToDataUri,
811
- imageBinaryToBlob,
812
- imageBinaryToBase64Png,
1034
+ isBrowserImageValue,
1035
+ imageValueFromBuffer,
1036
+ imageValueFromBitmap,
813
1037
  hasFilterOp,
814
1038
  getTexturePool,
815
1039
  getShaderCache,
@@ -817,7 +1041,8 @@ export {
817
1041
  getImageRasterCodec,
818
1042
  getGpuImageFactory,
819
1043
  getGpuDevice,
820
- encodeImageBinaryToPng,
1044
+ encodeRawPixels,
1045
+ decodeBufferToRaw,
821
1046
  createTexturePool,
822
1047
  createShaderCache,
823
1048
  applyFilter,
@@ -826,9 +1051,9 @@ export {
826
1051
  SharpImage,
827
1052
  PASSTHROUGH_SHADER_SRC,
828
1053
  MediaRawImage,
829
- GpuImageSchema,
1054
+ ImageValueSchema,
830
1055
  GpuImage as GpuImageFactory,
831
1056
  CpuImage
832
1057
  };
833
1058
 
834
- //# debugId=2970EA408E9A184364756E2164756E21
1059
+ //# debugId=49C9406160EA2AA164756E2164756E21