@openfairygui/functions 0.2.0-alpha.19 → 0.2.0-alpha.20
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/README.md +7 -2
- package/dist/index.cjs +3 -920
- package/dist/index.d.cts +2 -46
- package/dist/index.d.ts +3 -47
- package/dist/index.js +2 -919
- package/dist/node.cjs +138 -10
- package/dist/node.d.cts +7 -2
- package/dist/node.d.ts +8 -3
- package/dist/node.js +138 -11
- package/dist/{publish-DsPK0SJ1.js → publish-BJ_eelME.js} +1973 -2070
- package/dist/{publish-D7268_u4.cjs → publish-xFWT9Slz.cjs} +1973 -2070
- package/dist/restore-BW2xacB3.cjs +936 -0
- package/dist/{codegen-B8ZM1F4j.d.cts → restore-BeWaJNjR.d.cts} +49 -3
- package/dist/restore-Clk62n0O.js +931 -0
- package/dist/{codegen-CfbDHuFt.d.ts → restore-Dh0-Nvms.d.ts} +50 -4
- package/dist/web.cjs +4 -2
- package/dist/web.d.ts +1 -1
- package/dist/web.js +4 -2
- package/package.json +5 -2
- package/src/adapters/node/restore.ts +187 -0
- package/src/adapters/web/publish.ts +1 -247
- package/src/adapters/web/raster.ts +251 -0
- package/src/atlas/font.ts +95 -0
- package/src/atlas/inputs.ts +515 -0
- package/src/atlas/jta.ts +211 -0
- package/src/atlas/packing.ts +767 -0
- package/src/atlas.ts +23 -1639
- package/src/node.ts +4 -0
- package/src/publish/external-resources.ts +117 -0
- package/src/publish/options.ts +180 -0
- package/src/publish/package-context.ts +608 -0
- package/src/publish/resource-references.ts +210 -0
- package/src/publish.ts +21 -1130
- package/src/restore-internals/font.ts +100 -0
- package/src/restore-internals/movie-clip.ts +104 -0
- package/src/restore-internals/output-transaction.ts +164 -0
- package/src/restore.ts +14 -329
- /package/dist/{atlas-CDn6TirX.d.ts → atlas-C6tbl7nn.d.ts} +0 -0
package/dist/node.cjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_publish = require("./publish-
|
|
2
|
+
const require_publish = require("./publish-xFWT9Slz.cjs");
|
|
3
|
+
const require_restore = require("./restore-BW2xacB3.cjs");
|
|
3
4
|
//#region src/adapters/node/plugins.ts
|
|
4
|
-
const importNative$
|
|
5
|
+
const importNative$2 = new Function("id", "return import(id)");
|
|
5
6
|
async function loadPlugins(doc, pluginsDir) {
|
|
6
7
|
if (!pluginsDir) return [];
|
|
7
|
-
const fs = await importNative$
|
|
8
|
-
const path = await importNative$
|
|
8
|
+
const fs = await importNative$2("node:fs/promises");
|
|
9
|
+
const path = await importNative$2("node:path");
|
|
9
10
|
let entries;
|
|
10
11
|
try {
|
|
11
12
|
entries = await fs.readdir(pluginsDir, { withFileTypes: true });
|
|
@@ -45,7 +46,7 @@ function resolvePluginMain(path, pluginDir, manifest) {
|
|
|
45
46
|
return mainPath;
|
|
46
47
|
}
|
|
47
48
|
async function loadPlugin(mainPath) {
|
|
48
|
-
const { createJiti } = await importNative$
|
|
49
|
+
const { createJiti } = await importNative$2("jiti");
|
|
49
50
|
const mod = await createJiti(require("url").pathToFileURL(__filename).href).import(mainPath);
|
|
50
51
|
const defaultExport = mod.default;
|
|
51
52
|
return isObject(defaultExport) ? defaultExport : mod;
|
|
@@ -55,9 +56,9 @@ function isObject(value) {
|
|
|
55
56
|
}
|
|
56
57
|
//#endregion
|
|
57
58
|
//#region src/adapters/node/publish.ts
|
|
58
|
-
const importNative = new Function("id", "return import(id)");
|
|
59
|
+
const importNative$1 = new Function("id", "return import(id)");
|
|
59
60
|
async function createNodePublishFileSystem() {
|
|
60
|
-
const [fs, path] = await Promise.all([importNative("node:fs/promises"), importNative("node:path")]);
|
|
61
|
+
const [fs, path] = await Promise.all([importNative$1("node:fs/promises"), importNative$1("node:path")]);
|
|
61
62
|
return {
|
|
62
63
|
async readFileRaw(filePath) {
|
|
63
64
|
const data = await fs.readFile(filePath);
|
|
@@ -85,11 +86,11 @@ async function resolveNodeAssetsPath(document, assetsPath) {
|
|
|
85
86
|
if (assetsPath) return assetsPath;
|
|
86
87
|
const projectDir = document.getProjectDir?.() ?? "";
|
|
87
88
|
if (!projectDir) return void 0;
|
|
88
|
-
return (await importNative("node:path")).join(projectDir, "assets");
|
|
89
|
+
return (await importNative$1("node:path")).join(projectDir, "assets");
|
|
89
90
|
}
|
|
90
91
|
async function loadSharpBackend() {
|
|
91
92
|
try {
|
|
92
|
-
const loaded = await importNative("sharp");
|
|
93
|
+
const loaded = await importNative$1("sharp");
|
|
93
94
|
return loaded.default ?? loaded;
|
|
94
95
|
} catch {
|
|
95
96
|
return;
|
|
@@ -98,7 +99,7 @@ async function loadSharpBackend() {
|
|
|
98
99
|
async function loadNodePublishPlugins(document, assetsPath) {
|
|
99
100
|
const projectDir = document.getProjectDir?.() || (assetsPath ? require_publish.resolveProjectBasePath(assetsPath) : "");
|
|
100
101
|
if (!projectDir) return [];
|
|
101
|
-
return loadPlugins(document, (await importNative("node:path")).join(projectDir, "plugins"));
|
|
102
|
+
return loadPlugins(document, (await importNative$1("node:path")).join(projectDir, "plugins"));
|
|
102
103
|
}
|
|
103
104
|
/**
|
|
104
105
|
* Publish a FairyGUI project through the standard Node host adapter.
|
|
@@ -125,4 +126,131 @@ async function publishNode(options) {
|
|
|
125
126
|
}));
|
|
126
127
|
}
|
|
127
128
|
//#endregion
|
|
129
|
+
//#region src/adapters/node/restore.ts
|
|
130
|
+
const importNative = new Function("id", "return import(id)");
|
|
131
|
+
async function createNodeRestoreFileSystem() {
|
|
132
|
+
const [fs, path] = await Promise.all([importNative("node:fs/promises"), importNative("node:path")]);
|
|
133
|
+
return {
|
|
134
|
+
async readFile(filePath) {
|
|
135
|
+
return fs.readFile(filePath, "utf-8");
|
|
136
|
+
},
|
|
137
|
+
async readFileRaw(filePath) {
|
|
138
|
+
const buffer = await fs.readFile(filePath);
|
|
139
|
+
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
140
|
+
},
|
|
141
|
+
async writeFile(filePath, content) {
|
|
142
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
143
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
144
|
+
},
|
|
145
|
+
async writeFileRaw(filePath, data) {
|
|
146
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
147
|
+
await fs.writeFile(filePath, data);
|
|
148
|
+
},
|
|
149
|
+
async mkdir(dirPath) {
|
|
150
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
151
|
+
},
|
|
152
|
+
async readdir(dirPath) {
|
|
153
|
+
return fs.readdir(dirPath);
|
|
154
|
+
},
|
|
155
|
+
async exists(filePath) {
|
|
156
|
+
try {
|
|
157
|
+
await fs.access(filePath);
|
|
158
|
+
return true;
|
|
159
|
+
} catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
async isFile(filePath) {
|
|
164
|
+
try {
|
|
165
|
+
return (await fs.stat(filePath)).isFile();
|
|
166
|
+
} catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
async resolvePath(filePath) {
|
|
171
|
+
try {
|
|
172
|
+
return await fs.realpath(filePath);
|
|
173
|
+
} catch {
|
|
174
|
+
return path.resolve(filePath);
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
async rm(targetPath, options) {
|
|
178
|
+
await fs.rm(targetPath, {
|
|
179
|
+
recursive: options?.recursive ?? false,
|
|
180
|
+
force: options?.force ?? false
|
|
181
|
+
});
|
|
182
|
+
},
|
|
183
|
+
async rename(from, to) {
|
|
184
|
+
await fs.rename(from, to);
|
|
185
|
+
},
|
|
186
|
+
join(...paths) {
|
|
187
|
+
return path.join(...paths);
|
|
188
|
+
},
|
|
189
|
+
dirname(filePath) {
|
|
190
|
+
return path.dirname(filePath);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
async function createRestoreImageProcessors() {
|
|
195
|
+
let sharp;
|
|
196
|
+
try {
|
|
197
|
+
const loaded = await importNative("sharp");
|
|
198
|
+
sharp = loaded.default ?? loaded;
|
|
199
|
+
} catch {
|
|
200
|
+
throw new Error("restoreNode: Sharp is required to crop atlas images. Install sharp before restoring.");
|
|
201
|
+
}
|
|
202
|
+
async function extractImage(input) {
|
|
203
|
+
const targetPath = input.outputPath ?? input.sourcePath;
|
|
204
|
+
let image = sharp(input.sourcePath).extract({
|
|
205
|
+
left: input.left,
|
|
206
|
+
top: input.top,
|
|
207
|
+
width: input.width,
|
|
208
|
+
height: input.height
|
|
209
|
+
});
|
|
210
|
+
if (input.rotated) image = image.rotate(90);
|
|
211
|
+
const { data, info } = await image.png().toBuffer({ resolveWithObject: true });
|
|
212
|
+
if (input.expectedWidth > 0 && input.expectedHeight > 0 && (input.offsetX !== 0 || input.offsetY !== 0 || info.width !== input.expectedWidth || info.height !== input.expectedHeight)) {
|
|
213
|
+
if (input.offsetX < 0 || input.offsetY < 0 || input.offsetX + info.width > input.expectedWidth || input.offsetY + info.height > input.expectedHeight) throw new Error(`restore: Cropped image does not fit original canvas for ${targetPath}: crop ${info.width}x${info.height} at ${input.offsetX},${input.offsetY}, canvas ${input.expectedWidth}x${input.expectedHeight}`);
|
|
214
|
+
const composed = await sharp({ create: {
|
|
215
|
+
width: input.expectedWidth,
|
|
216
|
+
height: input.expectedHeight,
|
|
217
|
+
channels: 4,
|
|
218
|
+
background: {
|
|
219
|
+
r: 0,
|
|
220
|
+
g: 0,
|
|
221
|
+
b: 0,
|
|
222
|
+
alpha: 0
|
|
223
|
+
}
|
|
224
|
+
} }).composite([{
|
|
225
|
+
input: data,
|
|
226
|
+
left: input.offsetX,
|
|
227
|
+
top: input.offsetY
|
|
228
|
+
}]).png().toBuffer({ resolveWithObject: true });
|
|
229
|
+
if (composed.info.width !== input.expectedWidth || composed.info.height !== input.expectedHeight) throw new Error(`restore: Cropped image size mismatch for ${targetPath}: expected ${input.expectedWidth}x${input.expectedHeight}, got ${composed.info.width}x${composed.info.height}`);
|
|
230
|
+
return composed.data;
|
|
231
|
+
}
|
|
232
|
+
if (input.expectedWidth > 0 && input.expectedHeight > 0 && (info.width !== input.expectedWidth || info.height !== input.expectedHeight)) throw new Error(`restore: Cropped image size mismatch for ${targetPath}: expected ${input.expectedWidth}x${input.expectedHeight}, got ${info.width}x${info.height}`);
|
|
233
|
+
return data;
|
|
234
|
+
}
|
|
235
|
+
const fs = await importNative("node:fs/promises");
|
|
236
|
+
const path = await importNative("node:path");
|
|
237
|
+
return {
|
|
238
|
+
extractImage,
|
|
239
|
+
cropImage: async (input) => {
|
|
240
|
+
await fs.mkdir(path.dirname(input.outputPath), { recursive: true });
|
|
241
|
+
await fs.writeFile(input.outputPath, await extractImage(input));
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/** Restore trusted local published artifacts through the standard Node host adapter. */
|
|
246
|
+
async function restoreNode(options) {
|
|
247
|
+
const [fs, imageProcessors] = await Promise.all([createNodeRestoreFileSystem(), createRestoreImageProcessors()]);
|
|
248
|
+
return require_restore.restore({
|
|
249
|
+
...options,
|
|
250
|
+
fs,
|
|
251
|
+
...imageProcessors
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
//#endregion
|
|
128
255
|
exports.publishNode = publishNode;
|
|
256
|
+
exports.restoreNode = restoreNode;
|
package/dist/node.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as AtlasRasterBackend } from "./atlas-CHsu2Y8i.cjs";
|
|
2
|
-
import {
|
|
2
|
+
import { L as PublishOptions, S as LoadedPlugin, o as RestoreOptions, s as RestoreResult } from "./restore-BeWaJNjR.cjs";
|
|
3
3
|
import { Document } from "@openfairygui/core";
|
|
4
4
|
|
|
5
5
|
//#region src/adapters/node/publish.d.ts
|
|
@@ -28,4 +28,9 @@ interface PublishNodeOptions extends Omit<PublishOptions, 'atlas' | 'basePath' |
|
|
|
28
28
|
*/
|
|
29
29
|
declare function publishNode(options: PublishNodeOptions): Promise<void>;
|
|
30
30
|
//#endregion
|
|
31
|
-
|
|
31
|
+
//#region src/adapters/node/restore.d.ts
|
|
32
|
+
interface RestoreNodeOptions extends Omit<RestoreOptions, 'fs' | 'cropImage' | 'extractImage'> {}
|
|
33
|
+
/** Restore trusted local published artifacts through the standard Node host adapter. */
|
|
34
|
+
declare function restoreNode(options: RestoreNodeOptions): Promise<RestoreResult>;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { type PublishNodeOptions, type RestoreNodeOptions, publishNode, restoreNode };
|
package/dist/node.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as AtlasRasterBackend } from "./atlas-
|
|
2
|
-
import {
|
|
1
|
+
import { r as AtlasRasterBackend } from "./atlas-C6tbl7nn.js";
|
|
2
|
+
import { L as PublishOptions, S as LoadedPlugin, o as RestoreOptions, s as RestoreResult } from "./restore-Dh0-Nvms.js";
|
|
3
3
|
import { Document } from "@openfairygui/core";
|
|
4
4
|
|
|
5
5
|
//#region src/adapters/node/publish.d.ts
|
|
@@ -28,4 +28,9 @@ interface PublishNodeOptions extends Omit<PublishOptions, 'atlas' | 'basePath' |
|
|
|
28
28
|
*/
|
|
29
29
|
declare function publishNode(options: PublishNodeOptions): Promise<void>;
|
|
30
30
|
//#endregion
|
|
31
|
-
|
|
31
|
+
//#region src/adapters/node/restore.d.ts
|
|
32
|
+
interface RestoreNodeOptions extends Omit<RestoreOptions, 'fs' | 'cropImage' | 'extractImage'> {}
|
|
33
|
+
/** Restore trusted local published artifacts through the standard Node host adapter. */
|
|
34
|
+
declare function restoreNode(options: RestoreNodeOptions): Promise<RestoreResult>;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { type PublishNodeOptions, type RestoreNodeOptions, publishNode, restoreNode };
|
package/dist/node.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { l as resolveProjectBasePath, t as publish, u as formatPluginError } from "./publish-
|
|
1
|
+
import { l as resolveProjectBasePath, t as publish, u as formatPluginError } from "./publish-BJ_eelME.js";
|
|
2
|
+
import { t as restore } from "./restore-Clk62n0O.js";
|
|
2
3
|
//#region src/adapters/node/plugins.ts
|
|
3
|
-
const importNative$
|
|
4
|
+
const importNative$2 = new Function("id", "return import(id)");
|
|
4
5
|
async function loadPlugins(doc, pluginsDir) {
|
|
5
6
|
if (!pluginsDir) return [];
|
|
6
|
-
const fs = await importNative$
|
|
7
|
-
const path = await importNative$
|
|
7
|
+
const fs = await importNative$2("node:fs/promises");
|
|
8
|
+
const path = await importNative$2("node:path");
|
|
8
9
|
let entries;
|
|
9
10
|
try {
|
|
10
11
|
entries = await fs.readdir(pluginsDir, { withFileTypes: true });
|
|
@@ -44,7 +45,7 @@ function resolvePluginMain(path, pluginDir, manifest) {
|
|
|
44
45
|
return mainPath;
|
|
45
46
|
}
|
|
46
47
|
async function loadPlugin(mainPath) {
|
|
47
|
-
const { createJiti } = await importNative$
|
|
48
|
+
const { createJiti } = await importNative$2("jiti");
|
|
48
49
|
const mod = await createJiti(import.meta.url).import(mainPath);
|
|
49
50
|
const defaultExport = mod.default;
|
|
50
51
|
return isObject(defaultExport) ? defaultExport : mod;
|
|
@@ -54,9 +55,9 @@ function isObject(value) {
|
|
|
54
55
|
}
|
|
55
56
|
//#endregion
|
|
56
57
|
//#region src/adapters/node/publish.ts
|
|
57
|
-
const importNative = new Function("id", "return import(id)");
|
|
58
|
+
const importNative$1 = new Function("id", "return import(id)");
|
|
58
59
|
async function createNodePublishFileSystem() {
|
|
59
|
-
const [fs, path] = await Promise.all([importNative("node:fs/promises"), importNative("node:path")]);
|
|
60
|
+
const [fs, path] = await Promise.all([importNative$1("node:fs/promises"), importNative$1("node:path")]);
|
|
60
61
|
return {
|
|
61
62
|
async readFileRaw(filePath) {
|
|
62
63
|
const data = await fs.readFile(filePath);
|
|
@@ -84,11 +85,11 @@ async function resolveNodeAssetsPath(document, assetsPath) {
|
|
|
84
85
|
if (assetsPath) return assetsPath;
|
|
85
86
|
const projectDir = document.getProjectDir?.() ?? "";
|
|
86
87
|
if (!projectDir) return void 0;
|
|
87
|
-
return (await importNative("node:path")).join(projectDir, "assets");
|
|
88
|
+
return (await importNative$1("node:path")).join(projectDir, "assets");
|
|
88
89
|
}
|
|
89
90
|
async function loadSharpBackend() {
|
|
90
91
|
try {
|
|
91
|
-
const loaded = await importNative("sharp");
|
|
92
|
+
const loaded = await importNative$1("sharp");
|
|
92
93
|
return loaded.default ?? loaded;
|
|
93
94
|
} catch {
|
|
94
95
|
return;
|
|
@@ -97,7 +98,7 @@ async function loadSharpBackend() {
|
|
|
97
98
|
async function loadNodePublishPlugins(document, assetsPath) {
|
|
98
99
|
const projectDir = document.getProjectDir?.() || (assetsPath ? resolveProjectBasePath(assetsPath) : "");
|
|
99
100
|
if (!projectDir) return [];
|
|
100
|
-
return loadPlugins(document, (await importNative("node:path")).join(projectDir, "plugins"));
|
|
101
|
+
return loadPlugins(document, (await importNative$1("node:path")).join(projectDir, "plugins"));
|
|
101
102
|
}
|
|
102
103
|
/**
|
|
103
104
|
* Publish a FairyGUI project through the standard Node host adapter.
|
|
@@ -124,4 +125,130 @@ async function publishNode(options) {
|
|
|
124
125
|
}));
|
|
125
126
|
}
|
|
126
127
|
//#endregion
|
|
127
|
-
|
|
128
|
+
//#region src/adapters/node/restore.ts
|
|
129
|
+
const importNative = new Function("id", "return import(id)");
|
|
130
|
+
async function createNodeRestoreFileSystem() {
|
|
131
|
+
const [fs, path] = await Promise.all([importNative("node:fs/promises"), importNative("node:path")]);
|
|
132
|
+
return {
|
|
133
|
+
async readFile(filePath) {
|
|
134
|
+
return fs.readFile(filePath, "utf-8");
|
|
135
|
+
},
|
|
136
|
+
async readFileRaw(filePath) {
|
|
137
|
+
const buffer = await fs.readFile(filePath);
|
|
138
|
+
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
139
|
+
},
|
|
140
|
+
async writeFile(filePath, content) {
|
|
141
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
142
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
143
|
+
},
|
|
144
|
+
async writeFileRaw(filePath, data) {
|
|
145
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
146
|
+
await fs.writeFile(filePath, data);
|
|
147
|
+
},
|
|
148
|
+
async mkdir(dirPath) {
|
|
149
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
150
|
+
},
|
|
151
|
+
async readdir(dirPath) {
|
|
152
|
+
return fs.readdir(dirPath);
|
|
153
|
+
},
|
|
154
|
+
async exists(filePath) {
|
|
155
|
+
try {
|
|
156
|
+
await fs.access(filePath);
|
|
157
|
+
return true;
|
|
158
|
+
} catch {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
async isFile(filePath) {
|
|
163
|
+
try {
|
|
164
|
+
return (await fs.stat(filePath)).isFile();
|
|
165
|
+
} catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
async resolvePath(filePath) {
|
|
170
|
+
try {
|
|
171
|
+
return await fs.realpath(filePath);
|
|
172
|
+
} catch {
|
|
173
|
+
return path.resolve(filePath);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
async rm(targetPath, options) {
|
|
177
|
+
await fs.rm(targetPath, {
|
|
178
|
+
recursive: options?.recursive ?? false,
|
|
179
|
+
force: options?.force ?? false
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
async rename(from, to) {
|
|
183
|
+
await fs.rename(from, to);
|
|
184
|
+
},
|
|
185
|
+
join(...paths) {
|
|
186
|
+
return path.join(...paths);
|
|
187
|
+
},
|
|
188
|
+
dirname(filePath) {
|
|
189
|
+
return path.dirname(filePath);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
async function createRestoreImageProcessors() {
|
|
194
|
+
let sharp;
|
|
195
|
+
try {
|
|
196
|
+
const loaded = await importNative("sharp");
|
|
197
|
+
sharp = loaded.default ?? loaded;
|
|
198
|
+
} catch {
|
|
199
|
+
throw new Error("restoreNode: Sharp is required to crop atlas images. Install sharp before restoring.");
|
|
200
|
+
}
|
|
201
|
+
async function extractImage(input) {
|
|
202
|
+
const targetPath = input.outputPath ?? input.sourcePath;
|
|
203
|
+
let image = sharp(input.sourcePath).extract({
|
|
204
|
+
left: input.left,
|
|
205
|
+
top: input.top,
|
|
206
|
+
width: input.width,
|
|
207
|
+
height: input.height
|
|
208
|
+
});
|
|
209
|
+
if (input.rotated) image = image.rotate(90);
|
|
210
|
+
const { data, info } = await image.png().toBuffer({ resolveWithObject: true });
|
|
211
|
+
if (input.expectedWidth > 0 && input.expectedHeight > 0 && (input.offsetX !== 0 || input.offsetY !== 0 || info.width !== input.expectedWidth || info.height !== input.expectedHeight)) {
|
|
212
|
+
if (input.offsetX < 0 || input.offsetY < 0 || input.offsetX + info.width > input.expectedWidth || input.offsetY + info.height > input.expectedHeight) throw new Error(`restore: Cropped image does not fit original canvas for ${targetPath}: crop ${info.width}x${info.height} at ${input.offsetX},${input.offsetY}, canvas ${input.expectedWidth}x${input.expectedHeight}`);
|
|
213
|
+
const composed = await sharp({ create: {
|
|
214
|
+
width: input.expectedWidth,
|
|
215
|
+
height: input.expectedHeight,
|
|
216
|
+
channels: 4,
|
|
217
|
+
background: {
|
|
218
|
+
r: 0,
|
|
219
|
+
g: 0,
|
|
220
|
+
b: 0,
|
|
221
|
+
alpha: 0
|
|
222
|
+
}
|
|
223
|
+
} }).composite([{
|
|
224
|
+
input: data,
|
|
225
|
+
left: input.offsetX,
|
|
226
|
+
top: input.offsetY
|
|
227
|
+
}]).png().toBuffer({ resolveWithObject: true });
|
|
228
|
+
if (composed.info.width !== input.expectedWidth || composed.info.height !== input.expectedHeight) throw new Error(`restore: Cropped image size mismatch for ${targetPath}: expected ${input.expectedWidth}x${input.expectedHeight}, got ${composed.info.width}x${composed.info.height}`);
|
|
229
|
+
return composed.data;
|
|
230
|
+
}
|
|
231
|
+
if (input.expectedWidth > 0 && input.expectedHeight > 0 && (info.width !== input.expectedWidth || info.height !== input.expectedHeight)) throw new Error(`restore: Cropped image size mismatch for ${targetPath}: expected ${input.expectedWidth}x${input.expectedHeight}, got ${info.width}x${info.height}`);
|
|
232
|
+
return data;
|
|
233
|
+
}
|
|
234
|
+
const fs = await importNative("node:fs/promises");
|
|
235
|
+
const path = await importNative("node:path");
|
|
236
|
+
return {
|
|
237
|
+
extractImage,
|
|
238
|
+
cropImage: async (input) => {
|
|
239
|
+
await fs.mkdir(path.dirname(input.outputPath), { recursive: true });
|
|
240
|
+
await fs.writeFile(input.outputPath, await extractImage(input));
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/** Restore trusted local published artifacts through the standard Node host adapter. */
|
|
245
|
+
async function restoreNode(options) {
|
|
246
|
+
const [fs, imageProcessors] = await Promise.all([createNodeRestoreFileSystem(), createRestoreImageProcessors()]);
|
|
247
|
+
return restore({
|
|
248
|
+
...options,
|
|
249
|
+
fs,
|
|
250
|
+
...imageProcessors
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
//#endregion
|
|
254
|
+
export { publishNode, restoreNode };
|