@shotstack/shotstack-canvas 1.2.9 → 1.3.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.
@@ -163,10 +163,49 @@ var RichTextAssetSchema = import_joi.default.object({
163
163
  }).unknown(false);
164
164
 
165
165
  // src/wasm/hb-loader.ts
166
+ var import_meta = {};
166
167
  var hbSingleton = null;
167
168
  function isNode() {
168
169
  return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
169
170
  }
171
+ function bufferToArrayBuffer(buffer) {
172
+ if (buffer.buffer instanceof ArrayBuffer) {
173
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
174
+ }
175
+ const arrayBuffer = new ArrayBuffer(buffer.byteLength);
176
+ const view = new Uint8Array(arrayBuffer);
177
+ for (let i = 0; i < buffer.byteLength; i++) {
178
+ view[i] = buffer[i];
179
+ }
180
+ return arrayBuffer;
181
+ }
182
+ async function loadWasmNode() {
183
+ try {
184
+ const { readFile: readFile2 } = await import("fs/promises");
185
+ const { fileURLToPath } = await import("url");
186
+ const path = await import("path");
187
+ const currentDir = path.dirname(fileURLToPath(import_meta.url));
188
+ const candidates = [
189
+ path.join(currentDir, "../../dist/hb.wasm"),
190
+ path.join(currentDir, "../dist/hb.wasm"),
191
+ path.join(currentDir, "../../assets/wasm/hb.wasm"),
192
+ path.join(currentDir, "../assets/wasm/hb.wasm"),
193
+ path.join(currentDir, "./hb.wasm"),
194
+ path.join(currentDir, "../hb.wasm")
195
+ ];
196
+ for (const candidate of candidates) {
197
+ try {
198
+ const buffer = await readFile2(candidate);
199
+ return bufferToArrayBuffer(buffer);
200
+ } catch {
201
+ continue;
202
+ }
203
+ }
204
+ return void 0;
205
+ } catch {
206
+ return void 0;
207
+ }
208
+ }
170
209
  async function loadWasmWeb(wasmBaseURL) {
171
210
  try {
172
211
  if (wasmBaseURL) {
@@ -188,14 +227,14 @@ async function loadWasmWeb(wasmBaseURL) {
188
227
  return void 0;
189
228
  }
190
229
  }
191
- function setupWasmFetchInterceptor(wasmBinary) {
230
+ function setupWasmInterceptors(wasmBinary) {
192
231
  const originalFetch = window.fetch;
193
232
  window.fetch = function(input, init) {
194
233
  const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
195
- if (url.includes("hb.wasm") || url.endsWith(".wasm")) {
234
+ if (url.includes("hb.wasm") || url.endsWith(".wasm") && !url.includes("source.wasm")) {
196
235
  console.log(`\u{1F504} Intercepted fetch for: ${url}`);
197
236
  return Promise.resolve(
198
- new Response(wasmBinary, {
237
+ new Response(wasmBinary.slice(0), {
199
238
  status: 200,
200
239
  statusText: "OK",
201
240
  headers: {
@@ -207,28 +246,57 @@ function setupWasmFetchInterceptor(wasmBinary) {
207
246
  }
208
247
  return originalFetch.apply(this, [input, init]);
209
248
  };
249
+ const originalInstantiateStreaming = WebAssembly.instantiateStreaming;
250
+ if (originalInstantiateStreaming) {
251
+ WebAssembly.instantiateStreaming = async function(source, importObject) {
252
+ try {
253
+ const response = await source;
254
+ const url = response.url || "";
255
+ if (url.includes("hb.wasm") || url.endsWith(".wasm") && !url.includes("source.wasm")) {
256
+ console.log(`\u{1F504} Intercepted instantiateStreaming for: ${url}`);
257
+ const module2 = await WebAssembly.compile(wasmBinary);
258
+ const instance = await WebAssembly.instantiate(module2, importObject);
259
+ return { module: module2, instance };
260
+ }
261
+ return originalInstantiateStreaming.call(WebAssembly, response, importObject);
262
+ } catch (err) {
263
+ console.log("\u{1F504} instantiateStreaming failed, using pre-loaded binary");
264
+ const module2 = await WebAssembly.compile(wasmBinary);
265
+ const instance = await WebAssembly.instantiate(module2, importObject);
266
+ return { module: module2, instance };
267
+ }
268
+ };
269
+ }
210
270
  }
211
271
  async function initHB(wasmBaseURL) {
212
272
  if (hbSingleton) return hbSingleton;
213
273
  try {
214
274
  let wasmBinary;
215
- wasmBinary = await loadWasmWeb(wasmBaseURL);
275
+ if (isNode()) {
276
+ wasmBinary = await loadWasmNode();
277
+ } else {
278
+ wasmBinary = await loadWasmWeb(wasmBaseURL);
279
+ }
216
280
  if (!wasmBinary) {
217
281
  throw new Error("Failed to load WASM binary from any source");
218
282
  }
219
283
  console.log(`\u2705 WASM binary loaded successfully (${wasmBinary.byteLength} bytes)`);
220
284
  if (!isNode()) {
221
- setupWasmFetchInterceptor(wasmBinary);
285
+ setupWasmInterceptors(wasmBinary);
222
286
  }
223
287
  const mod = await import("harfbuzzjs");
224
- const candidate = mod.default;
225
288
  let hb;
289
+ const candidate = mod.default || mod;
226
290
  if (typeof candidate === "function") {
227
- hb = await candidate();
291
+ hb = await candidate({
292
+ wasmBinary
293
+ });
228
294
  } else if (candidate && typeof candidate.then === "function") {
229
295
  hb = await candidate;
230
- } else {
296
+ } else if (candidate && typeof candidate.createBuffer === "function") {
231
297
  hb = candidate;
298
+ } else {
299
+ throw new Error(`Unexpected harfbuzzjs export type: ${typeof candidate}`);
232
300
  }
233
301
  if (!hb || typeof hb.createBuffer !== "function" || typeof hb.createFont !== "function") {
234
302
  throw new Error("Failed to initialize HarfBuzz: unexpected export shape from 'harfbuzzjs'.");
@@ -1620,7 +1688,7 @@ function tokenizePath2(d) {
1620
1688
  var import_promises = require("fs/promises");
1621
1689
  var http = __toESM(require("http"), 1);
1622
1690
  var https = __toESM(require("https"), 1);
1623
- function bufferToArrayBuffer(buf) {
1691
+ function bufferToArrayBuffer2(buf) {
1624
1692
  const { buffer, byteOffset, byteLength } = buf;
1625
1693
  if (typeof SharedArrayBuffer !== "undefined" && buffer instanceof SharedArrayBuffer) {
1626
1694
  const ab2 = new ArrayBuffer(byteLength);
@@ -1669,10 +1737,10 @@ async function loadFileOrHttpToArrayBuffer(pathOrUrl) {
1669
1737
  reject(new Error(`Request timeout after 30s for ${pathOrUrl}`));
1670
1738
  });
1671
1739
  });
1672
- return bufferToArrayBuffer(buf2);
1740
+ return bufferToArrayBuffer2(buf2);
1673
1741
  }
1674
1742
  const buf = await (0, import_promises.readFile)(pathOrUrl);
1675
- return bufferToArrayBuffer(buf);
1743
+ return bufferToArrayBuffer2(buf);
1676
1744
  } catch (err) {
1677
1745
  if (err instanceof Error) {
1678
1746
  throw new Error(`Failed to load ${pathOrUrl}: ${err.message}`);
@@ -129,6 +129,44 @@ var hbSingleton = null;
129
129
  function isNode() {
130
130
  return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
131
131
  }
132
+ function bufferToArrayBuffer(buffer) {
133
+ if (buffer.buffer instanceof ArrayBuffer) {
134
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
135
+ }
136
+ const arrayBuffer = new ArrayBuffer(buffer.byteLength);
137
+ const view = new Uint8Array(arrayBuffer);
138
+ for (let i = 0; i < buffer.byteLength; i++) {
139
+ view[i] = buffer[i];
140
+ }
141
+ return arrayBuffer;
142
+ }
143
+ async function loadWasmNode() {
144
+ try {
145
+ const { readFile: readFile2 } = await import("fs/promises");
146
+ const { fileURLToPath } = await import("url");
147
+ const path = await import("path");
148
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
149
+ const candidates = [
150
+ path.join(currentDir, "../../dist/hb.wasm"),
151
+ path.join(currentDir, "../dist/hb.wasm"),
152
+ path.join(currentDir, "../../assets/wasm/hb.wasm"),
153
+ path.join(currentDir, "../assets/wasm/hb.wasm"),
154
+ path.join(currentDir, "./hb.wasm"),
155
+ path.join(currentDir, "../hb.wasm")
156
+ ];
157
+ for (const candidate of candidates) {
158
+ try {
159
+ const buffer = await readFile2(candidate);
160
+ return bufferToArrayBuffer(buffer);
161
+ } catch {
162
+ continue;
163
+ }
164
+ }
165
+ return void 0;
166
+ } catch {
167
+ return void 0;
168
+ }
169
+ }
132
170
  async function loadWasmWeb(wasmBaseURL) {
133
171
  try {
134
172
  if (wasmBaseURL) {
@@ -150,14 +188,14 @@ async function loadWasmWeb(wasmBaseURL) {
150
188
  return void 0;
151
189
  }
152
190
  }
153
- function setupWasmFetchInterceptor(wasmBinary) {
191
+ function setupWasmInterceptors(wasmBinary) {
154
192
  const originalFetch = window.fetch;
155
193
  window.fetch = function(input, init) {
156
194
  const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
157
- if (url.includes("hb.wasm") || url.endsWith(".wasm")) {
195
+ if (url.includes("hb.wasm") || url.endsWith(".wasm") && !url.includes("source.wasm")) {
158
196
  console.log(`\u{1F504} Intercepted fetch for: ${url}`);
159
197
  return Promise.resolve(
160
- new Response(wasmBinary, {
198
+ new Response(wasmBinary.slice(0), {
161
199
  status: 200,
162
200
  statusText: "OK",
163
201
  headers: {
@@ -169,28 +207,57 @@ function setupWasmFetchInterceptor(wasmBinary) {
169
207
  }
170
208
  return originalFetch.apply(this, [input, init]);
171
209
  };
210
+ const originalInstantiateStreaming = WebAssembly.instantiateStreaming;
211
+ if (originalInstantiateStreaming) {
212
+ WebAssembly.instantiateStreaming = async function(source, importObject) {
213
+ try {
214
+ const response = await source;
215
+ const url = response.url || "";
216
+ if (url.includes("hb.wasm") || url.endsWith(".wasm") && !url.includes("source.wasm")) {
217
+ console.log(`\u{1F504} Intercepted instantiateStreaming for: ${url}`);
218
+ const module = await WebAssembly.compile(wasmBinary);
219
+ const instance = await WebAssembly.instantiate(module, importObject);
220
+ return { module, instance };
221
+ }
222
+ return originalInstantiateStreaming.call(WebAssembly, response, importObject);
223
+ } catch (err) {
224
+ console.log("\u{1F504} instantiateStreaming failed, using pre-loaded binary");
225
+ const module = await WebAssembly.compile(wasmBinary);
226
+ const instance = await WebAssembly.instantiate(module, importObject);
227
+ return { module, instance };
228
+ }
229
+ };
230
+ }
172
231
  }
173
232
  async function initHB(wasmBaseURL) {
174
233
  if (hbSingleton) return hbSingleton;
175
234
  try {
176
235
  let wasmBinary;
177
- wasmBinary = await loadWasmWeb(wasmBaseURL);
236
+ if (isNode()) {
237
+ wasmBinary = await loadWasmNode();
238
+ } else {
239
+ wasmBinary = await loadWasmWeb(wasmBaseURL);
240
+ }
178
241
  if (!wasmBinary) {
179
242
  throw new Error("Failed to load WASM binary from any source");
180
243
  }
181
244
  console.log(`\u2705 WASM binary loaded successfully (${wasmBinary.byteLength} bytes)`);
182
245
  if (!isNode()) {
183
- setupWasmFetchInterceptor(wasmBinary);
246
+ setupWasmInterceptors(wasmBinary);
184
247
  }
185
248
  const mod = await import("harfbuzzjs");
186
- const candidate = mod.default;
187
249
  let hb;
250
+ const candidate = mod.default || mod;
188
251
  if (typeof candidate === "function") {
189
- hb = await candidate();
252
+ hb = await candidate({
253
+ wasmBinary
254
+ });
190
255
  } else if (candidate && typeof candidate.then === "function") {
191
256
  hb = await candidate;
192
- } else {
257
+ } else if (candidate && typeof candidate.createBuffer === "function") {
193
258
  hb = candidate;
259
+ } else {
260
+ throw new Error(`Unexpected harfbuzzjs export type: ${typeof candidate}`);
194
261
  }
195
262
  if (!hb || typeof hb.createBuffer !== "function" || typeof hb.createFont !== "function") {
196
263
  throw new Error("Failed to initialize HarfBuzz: unexpected export shape from 'harfbuzzjs'.");
@@ -1582,7 +1649,7 @@ function tokenizePath2(d) {
1582
1649
  import { readFile } from "fs/promises";
1583
1650
  import * as http from "http";
1584
1651
  import * as https from "https";
1585
- function bufferToArrayBuffer(buf) {
1652
+ function bufferToArrayBuffer2(buf) {
1586
1653
  const { buffer, byteOffset, byteLength } = buf;
1587
1654
  if (typeof SharedArrayBuffer !== "undefined" && buffer instanceof SharedArrayBuffer) {
1588
1655
  const ab2 = new ArrayBuffer(byteLength);
@@ -1631,10 +1698,10 @@ async function loadFileOrHttpToArrayBuffer(pathOrUrl) {
1631
1698
  reject(new Error(`Request timeout after 30s for ${pathOrUrl}`));
1632
1699
  });
1633
1700
  });
1634
- return bufferToArrayBuffer(buf2);
1701
+ return bufferToArrayBuffer2(buf2);
1635
1702
  }
1636
1703
  const buf = await readFile(pathOrUrl);
1637
- return bufferToArrayBuffer(buf);
1704
+ return bufferToArrayBuffer2(buf);
1638
1705
  } catch (err) {
1639
1706
  if (err instanceof Error) {
1640
1707
  throw new Error(`Failed to load ${pathOrUrl}: ${err.message}`);
package/dist/entry.web.js CHANGED
@@ -133,6 +133,44 @@ var hbSingleton = null;
133
133
  function isNode() {
134
134
  return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
135
135
  }
136
+ function bufferToArrayBuffer(buffer) {
137
+ if (buffer.buffer instanceof ArrayBuffer) {
138
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
139
+ }
140
+ const arrayBuffer = new ArrayBuffer(buffer.byteLength);
141
+ const view = new Uint8Array(arrayBuffer);
142
+ for (let i = 0; i < buffer.byteLength; i++) {
143
+ view[i] = buffer[i];
144
+ }
145
+ return arrayBuffer;
146
+ }
147
+ async function loadWasmNode() {
148
+ try {
149
+ const { readFile } = await import("fs/promises");
150
+ const { fileURLToPath } = await import("url");
151
+ const path = await import("path");
152
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
153
+ const candidates = [
154
+ path.join(currentDir, "../../dist/hb.wasm"),
155
+ path.join(currentDir, "../dist/hb.wasm"),
156
+ path.join(currentDir, "../../assets/wasm/hb.wasm"),
157
+ path.join(currentDir, "../assets/wasm/hb.wasm"),
158
+ path.join(currentDir, "./hb.wasm"),
159
+ path.join(currentDir, "../hb.wasm")
160
+ ];
161
+ for (const candidate of candidates) {
162
+ try {
163
+ const buffer = await readFile(candidate);
164
+ return bufferToArrayBuffer(buffer);
165
+ } catch {
166
+ continue;
167
+ }
168
+ }
169
+ return void 0;
170
+ } catch {
171
+ return void 0;
172
+ }
173
+ }
136
174
  async function loadWasmWeb(wasmBaseURL) {
137
175
  try {
138
176
  if (wasmBaseURL) {
@@ -154,14 +192,14 @@ async function loadWasmWeb(wasmBaseURL) {
154
192
  return void 0;
155
193
  }
156
194
  }
157
- function setupWasmFetchInterceptor(wasmBinary) {
195
+ function setupWasmInterceptors(wasmBinary) {
158
196
  const originalFetch = window.fetch;
159
197
  window.fetch = function(input, init) {
160
198
  const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
161
- if (url.includes("hb.wasm") || url.endsWith(".wasm")) {
199
+ if (url.includes("hb.wasm") || url.endsWith(".wasm") && !url.includes("source.wasm")) {
162
200
  console.log(`\u{1F504} Intercepted fetch for: ${url}`);
163
201
  return Promise.resolve(
164
- new Response(wasmBinary, {
202
+ new Response(wasmBinary.slice(0), {
165
203
  status: 200,
166
204
  statusText: "OK",
167
205
  headers: {
@@ -173,28 +211,57 @@ function setupWasmFetchInterceptor(wasmBinary) {
173
211
  }
174
212
  return originalFetch.apply(this, [input, init]);
175
213
  };
214
+ const originalInstantiateStreaming = WebAssembly.instantiateStreaming;
215
+ if (originalInstantiateStreaming) {
216
+ WebAssembly.instantiateStreaming = async function(source, importObject) {
217
+ try {
218
+ const response = await source;
219
+ const url = response.url || "";
220
+ if (url.includes("hb.wasm") || url.endsWith(".wasm") && !url.includes("source.wasm")) {
221
+ console.log(`\u{1F504} Intercepted instantiateStreaming for: ${url}`);
222
+ const module = await WebAssembly.compile(wasmBinary);
223
+ const instance = await WebAssembly.instantiate(module, importObject);
224
+ return { module, instance };
225
+ }
226
+ return originalInstantiateStreaming.call(WebAssembly, response, importObject);
227
+ } catch (err) {
228
+ console.log("\u{1F504} instantiateStreaming failed, using pre-loaded binary");
229
+ const module = await WebAssembly.compile(wasmBinary);
230
+ const instance = await WebAssembly.instantiate(module, importObject);
231
+ return { module, instance };
232
+ }
233
+ };
234
+ }
176
235
  }
177
236
  async function initHB(wasmBaseURL) {
178
237
  if (hbSingleton) return hbSingleton;
179
238
  try {
180
239
  let wasmBinary;
181
- wasmBinary = await loadWasmWeb(wasmBaseURL);
240
+ if (isNode()) {
241
+ wasmBinary = await loadWasmNode();
242
+ } else {
243
+ wasmBinary = await loadWasmWeb(wasmBaseURL);
244
+ }
182
245
  if (!wasmBinary) {
183
246
  throw new Error("Failed to load WASM binary from any source");
184
247
  }
185
248
  console.log(`\u2705 WASM binary loaded successfully (${wasmBinary.byteLength} bytes)`);
186
249
  if (!isNode()) {
187
- setupWasmFetchInterceptor(wasmBinary);
250
+ setupWasmInterceptors(wasmBinary);
188
251
  }
189
252
  const mod = await import("harfbuzzjs");
190
- const candidate = mod.default;
191
253
  let hb;
254
+ const candidate = mod.default || mod;
192
255
  if (typeof candidate === "function") {
193
- hb = await candidate();
256
+ hb = await candidate({
257
+ wasmBinary
258
+ });
194
259
  } else if (candidate && typeof candidate.then === "function") {
195
260
  hb = await candidate;
196
- } else {
261
+ } else if (candidate && typeof candidate.createBuffer === "function") {
197
262
  hb = candidate;
263
+ } else {
264
+ throw new Error(`Unexpected harfbuzzjs export type: ${typeof candidate}`);
198
265
  }
199
266
  if (!hb || typeof hb.createBuffer !== "function" || typeof hb.createFont !== "function") {
200
267
  throw new Error("Failed to initialize HarfBuzz: unexpected export shape from 'harfbuzzjs'.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shotstack/shotstack-canvas",
3
- "version": "1.2.9",
3
+ "version": "1.3.0",
4
4
  "description": "Text layout & animation engine (HarfBuzz) for Node & Web - fully self-contained.",
5
5
  "type": "module",
6
6
  "main": "./dist/entry.node.cjs",