polotno-node 3.10.0 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/instance.d.ts CHANGED
@@ -13,16 +13,7 @@ declare global {
13
13
  __polotnoVideoHeartbeat?: () => Promise<void>;
14
14
  }
15
15
  }
16
- export declare const URL_FIELDS: string[];
17
- /**
18
- * Visit every URL-holding field in a design: `visit(node, key, value)` for each
19
- * string under one of `URL_FIELDS`, `custom` skipped whole.
20
- *
21
- * Exported because `@polotno/fidelity` inlines a fixture's local assets over
22
- * the same fields — which subtrees a rewrite may enter is one rule, and two
23
- * copies of it drift. Changing either one needs that package's `test:unit`.
24
- */
25
- export declare const forEveryUrlField: (node: any, visit: any) => void;
16
+ export { URL_FIELDS, forEveryUrlField } from '@polotno/schema/asset-urls';
26
17
  /**
27
18
  * Rewrite every `file:` source in a design to a URL the client can load with
28
19
  * any API. Designs without one — the common case — come back untouched, so this
package/lib/instance.js CHANGED
@@ -7,6 +7,7 @@ import { pathToFileURL, fileURLToPath } from 'url';
7
7
  import { Readable } from 'stream';
8
8
  import { pipeline } from 'stream/promises';
9
9
  import { assertValidDesign } from '@polotno/schema';
10
+ import { forEveryUrlField } from '@polotno/schema/asset-urls';
10
11
  import * as fontCache from './font-cache.js';
11
12
  import * as localMedia from './local-media.js';
12
13
  import { polotnoError, rehydrateError } from './errors.js';
@@ -53,48 +54,8 @@ const collectMediaSourcePointers = (json) => {
53
54
  }
54
55
  return pointers;
55
56
  };
56
- // Design fields that carry a URL the renderer loads. A `file:` value in any of
57
- // them is served over the local-media channel instead — see `local-media.ts`
58
- // for why the browser must never see that scheme. Every other value is passed
59
- // through exactly as the caller wrote it.
60
- //
61
- // A list, rather than "rewrite every `file:` string anywhere", because a design
62
- // carries text: a text element whose content reads `file:///notes.txt` is
63
- // content, and rewriting it corrupts the render. `custom` is arbitrary caller
64
- // data with the same problem, so the walk does not enter it at all. And
65
- // `test/unit/local-media.test.mjs` fails when the schema grows a field named
66
- // like one that is missing here.
67
- export const URL_FIELDS = ['src', 'clipSrc', 'maskSrc', 'background', 'url'];
68
- const URL_FIELD_SET = new Set(URL_FIELDS);
69
- /**
70
- * Visit every URL-holding field in a design: `visit(node, key, value)` for each
71
- * string under one of `URL_FIELDS`, `custom` skipped whole.
72
- *
73
- * Exported because `@polotno/fidelity` inlines a fixture's local assets over
74
- * the same fields — which subtrees a rewrite may enter is one rule, and two
75
- * copies of it drift. Changing either one needs that package's `test:unit`.
76
- */
77
- export const forEveryUrlField = (node, visit) => {
78
- if (!node || typeof node !== 'object')
79
- return;
80
- if (Array.isArray(node)) {
81
- for (const item of node)
82
- forEveryUrlField(item, visit);
83
- return;
84
- }
85
- for (const key of Object.keys(node)) {
86
- if (key === 'custom')
87
- continue;
88
- const value = node[key];
89
- if (typeof value === 'string') {
90
- if (URL_FIELD_SET.has(key))
91
- visit(node, key, value);
92
- }
93
- else if (value && typeof value === 'object') {
94
- forEveryUrlField(value, visit);
95
- }
96
- }
97
- };
57
+ // Keep the published deep-import API while schema owns the asset vocabulary.
58
+ export { URL_FIELDS, forEveryUrlField } from '@polotno/schema/asset-urls';
98
59
  // Not `toLowerCase().startsWith`: a `src` is routinely a data URL of several
99
60
  // MB, and that would copy every one of them on every render.
100
61
  const isFileUrl = (value) => /^file:/i.test(value);
@@ -1160,11 +1121,12 @@ export const createInstance = async ({ key, url, useParallelPages = true, browse
1160
1121
  });
1161
1122
  const jsonToVideoDataURL = (json, attrs = {}) => renderVideoString(json, attrs, true);
1162
1123
  const jsonToVideoBase64 = (json, attrs = {}) => renderVideoString(json, attrs, false);
1124
+ let closing;
1163
1125
  const instance = {
1164
- close: async () => {
1126
+ close: () => (closing ?? (closing = (async () => {
1165
1127
  createdInstances.splice(createdInstances.indexOf(instance), 1);
1166
1128
  await browser.close();
1167
- },
1129
+ })())),
1168
1130
  firstPage,
1169
1131
  browser,
1170
1132
  run,
@@ -1185,7 +1147,7 @@ export const createInstance = async ({ key, url, useParallelPages = true, browse
1185
1147
  };
1186
1148
  // Function to close all created instances in parallel
1187
1149
  const closeAllInstances = async () => {
1188
- await Promise.all(createdInstances.map((instance) => instance.close()));
1150
+ await Promise.all([...createdInstances].map((instance) => instance.close()));
1189
1151
  };
1190
1152
  // Also handle other termination signals
1191
1153
  ['SIGINT', 'SIGTERM', 'SIGQUIT', 'exit'].forEach((signal) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polotno-node",
3
- "version": "3.10.0",
3
+ "version": "3.11.0",
4
4
  "description": "Polotno workflow from NodeJS",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "type": "module",
@@ -40,11 +40,11 @@
40
40
  "@types/node": "^26.6.1",
41
41
  "dotenv": "^17.4.2",
42
42
  "jspdf": "^4.2.1",
43
- "konva": "^10.5.0",
43
+ "konva": "^10.7.0",
44
44
  "mediabunny": "^1.56.3",
45
45
  "pixelmatch": "^7.2.0",
46
46
  "pngjs": "^7.0.0",
47
- "polotno": "4.13.0",
47
+ "polotno": "4.14.0",
48
48
  "react": "^19.3.0",
49
49
  "react-dom": "^19.3.0",
50
50
  "tmp": "^0.2.7",
@@ -61,7 +61,7 @@
61
61
  },
62
62
  "author": "Anton Lavrenov",
63
63
  "dependencies": {
64
- "@polotno/schema": "0.6.2",
64
+ "@polotno/schema": "0.7.0",
65
65
  "@sparticuz/chromium": "^153.0.0",
66
66
  "puppeteer": "25.11.0",
67
67
  "puppeteer-core": "25.11.0"
@@ -76,7 +76,7 @@
76
76
  "typecheck": "tsc --noEmit -p tsconfig.json",
77
77
  "test": "if [ -z \"$TURBO_HASH\" ]; then pnpm exec turbo run build --filter=polotno-node --output-logs=new-only --log-order=stream; fi && vitest run test/unit test/static",
78
78
  "test:update-goldens": "rm -f test/static/goldens/*.png test/static/goldens/matrix/*.png && pnpm run test",
79
- "test:video": "pnpm exec turbo run build --filter=polotno-node && VITEST_MAX_CONCURRENCY=${VITEST_MAX_CONCURRENCY:-3} vitest run test/video/video.test.mjs",
79
+ "test:video": "if [ -z \"$TURBO_HASH\" ]; then pnpm exec turbo run build --filter=polotno-node --output-logs=new-only; fi && vitest run test/video/video.test.mjs",
80
80
  "test:all": "pnpm exec turbo run build --filter=polotno-node && vitest run test/unit test/static test/video",
81
81
  "dev": "vite ./client.html"
82
82
  }