@openfairygui/functions 0.2.0 → 0.3.0-alpha.2
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 -4
- package/dist/index.cjs +3 -123
- package/dist/index.d.cts +11 -50
- package/dist/index.d.ts +11 -50
- package/dist/index.js +3 -122
- package/dist/node.cjs +83 -16
- package/dist/node.d.cts +5 -2
- package/dist/node.d.ts +5 -2
- package/dist/node.js +83 -17
- package/dist/{publish-CykUJfVa.cjs → publish-C7qHwEiP.cjs} +23 -0
- package/dist/{publish-DXoaC1Nl.js → publish-CyBj2o7n.js} +19 -2
- package/dist/{restore-BQp01WY3.js → restore-DVo1hXpN.js} +1 -1
- package/dist/{restore-CEywQUHz.cjs → restore-LXbDQqmT.cjs} +1 -1
- package/dist/uam-transaction.cjs +1 -1
- package/dist/uam-transaction.js +1 -1
- package/dist/web.cjs +46 -1
- package/dist/web.d.cts +5 -2
- package/dist/web.d.ts +5 -2
- package/dist/web.js +47 -3
- package/package.json +2 -2
- package/src/adapters/node/validate.ts +93 -0
- package/src/adapters/web/raster.ts +4 -0
- package/src/adapters/web/validate.ts +60 -0
- package/src/index.ts +2 -5
- package/src/node.ts +1 -0
- package/src/uam-transaction.ts +1 -1
- package/src/validate.ts +34 -183
- package/src/web.ts +1 -0
package/dist/node.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as AtlasRasterBackend } from "./atlas-CHsu2Y8i.cjs";
|
|
2
2
|
import { L as PublishOptions, S as LoadedPlugin, o as RestoreOptions, s as RestoreResult } from "./restore-BeWaJNjR.cjs";
|
|
3
|
-
import { Document } from "@openfairygui/core";
|
|
3
|
+
import { Document, ProjectValidationReport } from "@openfairygui/core";
|
|
4
4
|
|
|
5
5
|
//#region src/adapters/node/publish.d.ts
|
|
6
6
|
interface PublishNodeOptions extends Omit<PublishOptions, 'atlas' | 'basePath' | 'encoder' | 'fs' | 'plugins'> {
|
|
@@ -33,4 +33,7 @@ interface RestoreNodeOptions extends Omit<RestoreOptions, 'fs' | 'cropImage' | '
|
|
|
33
33
|
/** Restore trusted local published artifacts through the standard Node host adapter. */
|
|
34
34
|
declare function restoreNode(options: RestoreNodeOptions): Promise<RestoreResult>;
|
|
35
35
|
//#endregion
|
|
36
|
-
|
|
36
|
+
//#region src/adapters/node/validate.d.ts
|
|
37
|
+
declare function validateProjectNode(projectPath: string): Promise<ProjectValidationReport>;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { type PublishNodeOptions, type RestoreNodeOptions, publishNode, restoreNode, validateProjectNode };
|
package/dist/node.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as AtlasRasterBackend } from "./atlas-C6tbl7nn.js";
|
|
2
2
|
import { L as PublishOptions, S as LoadedPlugin, o as RestoreOptions, s as RestoreResult } from "./restore-Dh0-Nvms.js";
|
|
3
|
-
import { Document } from "@openfairygui/core";
|
|
3
|
+
import { Document, ProjectValidationReport } from "@openfairygui/core";
|
|
4
4
|
|
|
5
5
|
//#region src/adapters/node/publish.d.ts
|
|
6
6
|
interface PublishNodeOptions extends Omit<PublishOptions, 'atlas' | 'basePath' | 'encoder' | 'fs' | 'plugins'> {
|
|
@@ -33,4 +33,7 @@ interface RestoreNodeOptions extends Omit<RestoreOptions, 'fs' | 'cropImage' | '
|
|
|
33
33
|
/** Restore trusted local published artifacts through the standard Node host adapter. */
|
|
34
34
|
declare function restoreNode(options: RestoreNodeOptions): Promise<RestoreResult>;
|
|
35
35
|
//#endregion
|
|
36
|
-
|
|
36
|
+
//#region src/adapters/node/validate.d.ts
|
|
37
|
+
declare function validateProjectNode(projectPath: string): Promise<ProjectValidationReport>;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { type PublishNodeOptions, type RestoreNodeOptions, publishNode, restoreNode, validateProjectNode };
|
package/dist/node.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { m as formatPluginError, t as publish, u as resolveProjectBasePath } from "./publish-
|
|
2
|
-
import { t as restore } from "./restore-
|
|
1
|
+
import { _ as validateProject, m as formatPluginError, t as publish, u as resolveProjectBasePath } from "./publish-CyBj2o7n.js";
|
|
2
|
+
import { t as restore } from "./restore-DVo1hXpN.js";
|
|
3
|
+
import { createProjectValidationReport, liftDocumentToUamProject } from "@openfairygui/core";
|
|
4
|
+
import { NodeIO } from "@openfairygui/core/node";
|
|
3
5
|
//#region src/adapters/node/plugins.ts
|
|
4
|
-
const importNative$
|
|
6
|
+
const importNative$3 = new Function("id", "return import(id)");
|
|
5
7
|
async function loadPlugins(doc, pluginsDir) {
|
|
6
8
|
if (!pluginsDir) return [];
|
|
7
|
-
const fs = await importNative$
|
|
8
|
-
const path = await importNative$
|
|
9
|
+
const fs = await importNative$3("node:fs/promises");
|
|
10
|
+
const path = await importNative$3("node:path");
|
|
9
11
|
let entries;
|
|
10
12
|
try {
|
|
11
13
|
entries = await fs.readdir(pluginsDir, { withFileTypes: true });
|
|
@@ -45,7 +47,7 @@ function resolvePluginMain(path, pluginDir, manifest) {
|
|
|
45
47
|
return mainPath;
|
|
46
48
|
}
|
|
47
49
|
async function loadPlugin(mainPath) {
|
|
48
|
-
const { createJiti } = await importNative$
|
|
50
|
+
const { createJiti } = await importNative$3("jiti");
|
|
49
51
|
const mod = await createJiti(import.meta.url).import(mainPath);
|
|
50
52
|
const defaultExport = mod.default;
|
|
51
53
|
return isObject(defaultExport) ? defaultExport : mod;
|
|
@@ -55,9 +57,9 @@ function isObject(value) {
|
|
|
55
57
|
}
|
|
56
58
|
//#endregion
|
|
57
59
|
//#region src/adapters/node/publish.ts
|
|
58
|
-
const importNative$
|
|
60
|
+
const importNative$2 = new Function("id", "return import(id)");
|
|
59
61
|
async function createNodePublishFileSystem() {
|
|
60
|
-
const [fs, path] = await Promise.all([importNative$
|
|
62
|
+
const [fs, path] = await Promise.all([importNative$2("node:fs/promises"), importNative$2("node:path")]);
|
|
61
63
|
return {
|
|
62
64
|
async readFileRaw(filePath) {
|
|
63
65
|
const data = await fs.readFile(filePath);
|
|
@@ -85,11 +87,11 @@ async function resolveNodeAssetsPath(document, assetsPath) {
|
|
|
85
87
|
if (assetsPath) return assetsPath;
|
|
86
88
|
const projectDir = document.getProjectDir?.() ?? "";
|
|
87
89
|
if (!projectDir) return void 0;
|
|
88
|
-
return (await importNative$
|
|
90
|
+
return (await importNative$2("node:path")).join(projectDir, "assets");
|
|
89
91
|
}
|
|
90
92
|
async function loadSharpBackend() {
|
|
91
93
|
try {
|
|
92
|
-
const loaded = await importNative$
|
|
94
|
+
const loaded = await importNative$2("sharp");
|
|
93
95
|
return loaded.default ?? loaded;
|
|
94
96
|
} catch {
|
|
95
97
|
return;
|
|
@@ -98,7 +100,7 @@ async function loadSharpBackend() {
|
|
|
98
100
|
async function loadNodePublishPlugins(document, assetsPath) {
|
|
99
101
|
const projectDir = document.getProjectDir?.() || (assetsPath ? resolveProjectBasePath(assetsPath) : "");
|
|
100
102
|
if (!projectDir) return [];
|
|
101
|
-
return loadPlugins(document, (await importNative$
|
|
103
|
+
return loadPlugins(document, (await importNative$2("node:path")).join(projectDir, "plugins"));
|
|
102
104
|
}
|
|
103
105
|
/**
|
|
104
106
|
* Publish a FairyGUI project through the standard Node host adapter.
|
|
@@ -126,9 +128,9 @@ async function publishNode(options) {
|
|
|
126
128
|
}
|
|
127
129
|
//#endregion
|
|
128
130
|
//#region src/adapters/node/restore.ts
|
|
129
|
-
const importNative = new Function("id", "return import(id)");
|
|
131
|
+
const importNative$1 = new Function("id", "return import(id)");
|
|
130
132
|
async function createNodeRestoreFileSystem() {
|
|
131
|
-
const [fs, path] = await Promise.all([importNative("node:fs/promises"), importNative("node:path")]);
|
|
133
|
+
const [fs, path] = await Promise.all([importNative$1("node:fs/promises"), importNative$1("node:path")]);
|
|
132
134
|
return {
|
|
133
135
|
async readFile(filePath) {
|
|
134
136
|
return fs.readFile(filePath, "utf-8");
|
|
@@ -193,7 +195,7 @@ async function createNodeRestoreFileSystem() {
|
|
|
193
195
|
async function createRestoreImageProcessors() {
|
|
194
196
|
let sharp;
|
|
195
197
|
try {
|
|
196
|
-
const loaded = await importNative("sharp");
|
|
198
|
+
const loaded = await importNative$1("sharp");
|
|
197
199
|
sharp = loaded.default ?? loaded;
|
|
198
200
|
} catch {
|
|
199
201
|
throw new Error("restoreNode: Sharp is required to crop atlas images. Install sharp before restoring.");
|
|
@@ -231,8 +233,8 @@ async function createRestoreImageProcessors() {
|
|
|
231
233
|
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
234
|
return data;
|
|
233
235
|
}
|
|
234
|
-
const fs = await importNative("node:fs/promises");
|
|
235
|
-
const path = await importNative("node:path");
|
|
236
|
+
const fs = await importNative$1("node:fs/promises");
|
|
237
|
+
const path = await importNative$1("node:path");
|
|
236
238
|
return {
|
|
237
239
|
extractImage,
|
|
238
240
|
cropImage: async (input) => {
|
|
@@ -251,4 +253,68 @@ async function restoreNode(options) {
|
|
|
251
253
|
});
|
|
252
254
|
}
|
|
253
255
|
//#endregion
|
|
254
|
-
|
|
256
|
+
//#region src/adapters/node/validate.ts
|
|
257
|
+
const importNative = new Function("id", "return import(id)");
|
|
258
|
+
function sourceName(resource) {
|
|
259
|
+
return resource.sourcePath ?? resource.fileName ?? resource.file ?? resource.name;
|
|
260
|
+
}
|
|
261
|
+
async function validateProjectNode(projectPath) {
|
|
262
|
+
const read = await new NodeIO().readProjectDetailed(projectPath, { hydrateResourceBytes: true });
|
|
263
|
+
if (!read.document) return createProjectValidationReport(read.diagnostics, false);
|
|
264
|
+
let project;
|
|
265
|
+
try {
|
|
266
|
+
project = liftDocumentToUamProject(read.document);
|
|
267
|
+
} catch (error) {
|
|
268
|
+
return createProjectValidationReport([...read.diagnostics, {
|
|
269
|
+
severity: "error",
|
|
270
|
+
code: "invalid_uam",
|
|
271
|
+
path: "project",
|
|
272
|
+
message: `Project cannot be represented as UAM: ${error instanceof Error ? error.message : String(error)}`,
|
|
273
|
+
sourcePath: projectPath
|
|
274
|
+
}], false);
|
|
275
|
+
}
|
|
276
|
+
const base = validateProject(project, {
|
|
277
|
+
readDiagnostics: read.diagnostics,
|
|
278
|
+
complete: read.complete,
|
|
279
|
+
validateSources: true
|
|
280
|
+
});
|
|
281
|
+
let diagnostics = base.diagnostics;
|
|
282
|
+
const knownCorruptPaths = new Set(base.diagnostics.filter((diagnostic) => diagnostic.code === "corrupt_source").map((diagnostic) => diagnostic.path));
|
|
283
|
+
const images = project.packages.flatMap((pkg, packageIndex) => pkg.resources.map((resource, resourceIndex) => ({
|
|
284
|
+
pkg,
|
|
285
|
+
packageIndex,
|
|
286
|
+
resource,
|
|
287
|
+
resourceIndex
|
|
288
|
+
})).filter(({ resource, packageIndex, resourceIndex }) => resource.kind === "image" && resource.sourceBytes instanceof Uint8Array && !knownCorruptPaths.has(`packages[${packageIndex}].resources[${resourceIndex}]`)));
|
|
289
|
+
if (images.length === 0) return base;
|
|
290
|
+
let sharp;
|
|
291
|
+
try {
|
|
292
|
+
const loaded = await importNative("sharp");
|
|
293
|
+
sharp = loaded.default ?? loaded;
|
|
294
|
+
} catch {
|
|
295
|
+
return createProjectValidationReport([...base.diagnostics, {
|
|
296
|
+
severity: "warning",
|
|
297
|
+
code: "decode_capability_unavailable",
|
|
298
|
+
path: "project",
|
|
299
|
+
message: "Sharp is unavailable, so Node image decoding could not be completed."
|
|
300
|
+
}], false);
|
|
301
|
+
}
|
|
302
|
+
const decodedPaths = new Set(images.map(({ packageIndex, resourceIndex }) => `packages[${packageIndex}].resources[${resourceIndex}]`));
|
|
303
|
+
diagnostics = diagnostics.filter((diagnostic) => diagnostic.code !== "decode_capability_unavailable" || !decodedPaths.has(diagnostic.path));
|
|
304
|
+
for (const { pkg, packageIndex, resource, resourceIndex } of images) try {
|
|
305
|
+
await sharp(resource.sourceBytes).raw().toBuffer();
|
|
306
|
+
} catch (error) {
|
|
307
|
+
diagnostics.push({
|
|
308
|
+
severity: "error",
|
|
309
|
+
code: "corrupt_source",
|
|
310
|
+
path: `packages[${packageIndex}].resources[${resourceIndex}]`,
|
|
311
|
+
message: `Image source cannot be decoded: ${error instanceof Error ? error.message : String(error)}`,
|
|
312
|
+
packageId: pkg.id,
|
|
313
|
+
resourceId: resource.id,
|
|
314
|
+
sourcePath: sourceName(resource)
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
return createProjectValidationReport(diagnostics, read.complete && !diagnostics.some((diagnostic) => diagnostic.code === "decode_capability_unavailable"));
|
|
318
|
+
}
|
|
319
|
+
//#endregion
|
|
320
|
+
export { publishNode, restoreNode, validateProjectNode };
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
let _openfairygui_core = require("@openfairygui/core");
|
|
2
|
+
//#region src/validate.ts
|
|
3
|
+
/** Validate one authoritative UAM snapshot without mutating it. */
|
|
4
|
+
function validateProject(project, options = {}) {
|
|
5
|
+
const diagnostics = [
|
|
6
|
+
...options.readDiagnostics ?? [],
|
|
7
|
+
...(0, _openfairygui_core.validateUamProject)(project),
|
|
8
|
+
...(0, _openfairygui_core.validateUamReferences)(project)
|
|
9
|
+
];
|
|
10
|
+
let complete = options.complete ?? true;
|
|
11
|
+
if (options.validateSources) {
|
|
12
|
+
const source = (0, _openfairygui_core.validateUamSourceBytes)(project);
|
|
13
|
+
diagnostics.push(...source.diagnostics);
|
|
14
|
+
complete = complete && source.complete;
|
|
15
|
+
}
|
|
16
|
+
return (0, _openfairygui_core.createProjectValidationReport)(diagnostics, complete);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
2
19
|
//#region src/utils.ts
|
|
3
20
|
/**
|
|
4
21
|
* Wraps a transform function, assigning it a name for the transform stack.
|
|
@@ -3267,3 +3284,9 @@ Object.defineProperty(exports, "trimTrailingSlashes", {
|
|
|
3267
3284
|
return trimTrailingSlashes;
|
|
3268
3285
|
}
|
|
3269
3286
|
});
|
|
3287
|
+
Object.defineProperty(exports, "validateProject", {
|
|
3288
|
+
enumerable: true,
|
|
3289
|
+
get: function() {
|
|
3290
|
+
return validateProject;
|
|
3291
|
+
}
|
|
3292
|
+
});
|
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
import { BinaryWriter, GearType, ProjectType, TransitionActionType, deriveMovieClipModel, parseJta, probeRasterImage } from "@openfairygui/core";
|
|
1
|
+
import { BinaryWriter, GearType, ProjectType, TransitionActionType, createProjectValidationReport, deriveMovieClipModel, parseJta, probeRasterImage, validateUamProject, validateUamReferences, validateUamSourceBytes } from "@openfairygui/core";
|
|
2
|
+
//#region src/validate.ts
|
|
3
|
+
/** Validate one authoritative UAM snapshot without mutating it. */
|
|
4
|
+
function validateProject(project, options = {}) {
|
|
5
|
+
const diagnostics = [
|
|
6
|
+
...options.readDiagnostics ?? [],
|
|
7
|
+
...validateUamProject(project),
|
|
8
|
+
...validateUamReferences(project)
|
|
9
|
+
];
|
|
10
|
+
let complete = options.complete ?? true;
|
|
11
|
+
if (options.validateSources) {
|
|
12
|
+
const source = validateUamSourceBytes(project);
|
|
13
|
+
diagnostics.push(...source.diagnostics);
|
|
14
|
+
complete = complete && source.complete;
|
|
15
|
+
}
|
|
16
|
+
return createProjectValidationReport(diagnostics, complete);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
2
19
|
//#region src/utils.ts
|
|
3
20
|
/**
|
|
4
21
|
* Wraps a transform function, assigning it a name for the transform stack.
|
|
@@ -3171,4 +3188,4 @@ function _computeDependencies(doc, pkg, pkgMap) {
|
|
|
3171
3188
|
}
|
|
3172
3189
|
}
|
|
3173
3190
|
//#endregion
|
|
3174
|
-
export { decodeText as a, resolveCodeGenerationSettings as c, basename as d, normalizeComparablePath as f, createTransform as g, atlas as h, buildCodegenClasses as i, resolvePackageCodegenPlan as l, formatPluginError as m, resolvePublishOptions as n, encodeText as o, trimTrailingSlashes as p, AUTO_GENERATED_CODE_MARK as r, publishCodeGeneration as s, publish as t, resolveProjectBasePath as u };
|
|
3191
|
+
export { validateProject as _, decodeText as a, resolveCodeGenerationSettings as c, basename as d, normalizeComparablePath as f, createTransform as g, atlas as h, buildCodegenClasses as i, resolvePackageCodegenPlan as l, formatPluginError as m, resolvePublishOptions as n, encodeText as o, trimTrailingSlashes as p, AUTO_GENERATED_CODE_MARK as r, publishCodeGeneration as s, publish as t, resolveProjectBasePath as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as basename, f as normalizeComparablePath, p as trimTrailingSlashes } from "./publish-
|
|
1
|
+
import { d as basename, f as normalizeComparablePath, p as trimTrailingSlashes } from "./publish-CyBj2o7n.js";
|
|
2
2
|
import { BinaryReader, ProjectType, ProjectWriter, generateId } from "@openfairygui/core";
|
|
3
3
|
//#region src/restore-internals/output-transaction.ts
|
|
4
4
|
function isPathWithin(root, candidate) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_publish = require("./publish-
|
|
1
|
+
const require_publish = require("./publish-C7qHwEiP.cjs");
|
|
2
2
|
let _openfairygui_core = require("@openfairygui/core");
|
|
3
3
|
//#region src/restore-internals/output-transaction.ts
|
|
4
4
|
function isPathWithin(root, candidate) {
|
package/dist/uam-transaction.cjs
CHANGED
|
@@ -13,7 +13,7 @@ function compactDiagnostic(diagnostic) {
|
|
|
13
13
|
return Object.fromEntries(Object.entries(diagnostic).filter(([, value]) => value !== void 0));
|
|
14
14
|
}
|
|
15
15
|
function isTransactionSupportIssue(issue) {
|
|
16
|
-
return "
|
|
16
|
+
return !("severity" in issue);
|
|
17
17
|
}
|
|
18
18
|
function mapTransactionDiagnostics(error) {
|
|
19
19
|
if (error.issues?.length) return error.issues.map((issue) => {
|
package/dist/uam-transaction.js
CHANGED
|
@@ -12,7 +12,7 @@ function compactDiagnostic(diagnostic) {
|
|
|
12
12
|
return Object.fromEntries(Object.entries(diagnostic).filter(([, value]) => value !== void 0));
|
|
13
13
|
}
|
|
14
14
|
function isTransactionSupportIssue(issue) {
|
|
15
|
-
return "
|
|
15
|
+
return !("severity" in issue);
|
|
16
16
|
}
|
|
17
17
|
function mapTransactionDiagnostics(error) {
|
|
18
18
|
if (error.issues?.length) return error.issues.map((issue) => {
|
package/dist/web.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_publish = require("./publish-
|
|
2
|
+
const require_publish = require("./publish-C7qHwEiP.cjs");
|
|
3
3
|
let _openfairygui_core = require("@openfairygui/core");
|
|
4
4
|
let fast_xml_parser = require("fast-xml-parser");
|
|
5
5
|
//#region src/adapters/web/raster.ts
|
|
@@ -186,6 +186,9 @@ async function decodeRaster(bytes, mimeType) {
|
|
|
186
186
|
bitmap.close();
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
|
+
async function validateBrowserImageSource(bytes, path) {
|
|
190
|
+
await decodeRaster(bytes, imageMimeType(path));
|
|
191
|
+
}
|
|
189
192
|
async function decodeSvgWithDom(blob) {
|
|
190
193
|
if (typeof globalThis.Image !== "function" || typeof globalThis.URL?.createObjectURL !== "function" || typeof globalThis.URL?.revokeObjectURL !== "function") throw new Error("publishBrowser: createImageBitmap rejected SVG and DOM image decoding is unavailable.");
|
|
191
194
|
const url = globalThis.URL.createObjectURL(blob);
|
|
@@ -437,4 +440,46 @@ async function publishBrowser(options) {
|
|
|
437
440
|
}
|
|
438
441
|
}
|
|
439
442
|
//#endregion
|
|
443
|
+
//#region src/adapters/web/validate.ts
|
|
444
|
+
function sourceName(resource) {
|
|
445
|
+
return resource.sourcePath ?? resource.fileName ?? resource.name;
|
|
446
|
+
}
|
|
447
|
+
async function validateProjectWeb(project) {
|
|
448
|
+
const base = require_publish.validateProject(project, { validateSources: true });
|
|
449
|
+
if (!project.packages.some((pkg) => pkg.resources.some((resource) => resource.kind === "image" && resource.sourceBytes instanceof Uint8Array))) return base;
|
|
450
|
+
try {
|
|
451
|
+
assertBrowserImageSupport();
|
|
452
|
+
} catch (error) {
|
|
453
|
+
return (0, _openfairygui_core.createProjectValidationReport)([...base.diagnostics, {
|
|
454
|
+
severity: "warning",
|
|
455
|
+
code: "decode_capability_unavailable",
|
|
456
|
+
path: "project",
|
|
457
|
+
message: error instanceof Error ? error.message : String(error)
|
|
458
|
+
}], false);
|
|
459
|
+
}
|
|
460
|
+
const decodedPaths = /* @__PURE__ */ new Set();
|
|
461
|
+
const diagnostics = [...base.diagnostics];
|
|
462
|
+
for (const [packageIndex, pkg] of project.packages.entries()) for (const [resourceIndex, resource] of pkg.resources.entries()) {
|
|
463
|
+
if (resource.kind !== "image" || !(resource.sourceBytes instanceof Uint8Array)) continue;
|
|
464
|
+
try {
|
|
465
|
+
await validateBrowserImageSource(resource.sourceBytes, sourceName(resource));
|
|
466
|
+
decodedPaths.add(`packages[${packageIndex}].resources[${resourceIndex}]`);
|
|
467
|
+
} catch (error) {
|
|
468
|
+
const capabilityUnavailable = error instanceof Error && /(?:required|unavailable)/iu.test(error.message);
|
|
469
|
+
diagnostics.push({
|
|
470
|
+
severity: capabilityUnavailable ? "warning" : "error",
|
|
471
|
+
code: capabilityUnavailable ? "decode_capability_unavailable" : "corrupt_source",
|
|
472
|
+
path: `packages[${packageIndex}].resources[${resourceIndex}]`,
|
|
473
|
+
message: `Image source cannot be decoded: ${error instanceof Error ? error.message : String(error)}`,
|
|
474
|
+
packageId: pkg.id,
|
|
475
|
+
resourceId: resource.id,
|
|
476
|
+
sourcePath: sourceName(resource)
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
const resolved = diagnostics.filter((diagnostic) => diagnostic.code !== "decode_capability_unavailable" || !decodedPaths.has(diagnostic.path));
|
|
481
|
+
return (0, _openfairygui_core.createProjectValidationReport)(resolved, !resolved.some((diagnostic) => diagnostic.code === "decode_capability_unavailable"));
|
|
482
|
+
}
|
|
483
|
+
//#endregion
|
|
440
484
|
exports.publishBrowser = publishBrowser;
|
|
485
|
+
exports.validateProjectWeb = validateProjectWeb;
|
package/dist/web.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { d as PublishSourceFileSystem, t as AtlasOptions, u as PublishOutputFileSystem } from "./atlas-CHsu2Y8i.cjs";
|
|
2
|
-
import { Document } from "@openfairygui/core";
|
|
2
|
+
import { Document, ProjectValidationReport, UamProject } from "@openfairygui/core";
|
|
3
3
|
|
|
4
4
|
//#region src/adapters/web/publish.d.ts
|
|
5
5
|
type BrowserPublishProjectType = 'layabox';
|
|
@@ -41,4 +41,7 @@ interface BrowserPublishResult {
|
|
|
41
41
|
*/
|
|
42
42
|
declare function publishBrowser(options: BrowserPublishOptions): Promise<BrowserPublishResult>;
|
|
43
43
|
//#endregion
|
|
44
|
-
|
|
44
|
+
//#region src/adapters/web/validate.d.ts
|
|
45
|
+
declare function validateProjectWeb(project: UamProject): Promise<ProjectValidationReport>;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { type BrowserPublishAtlasOptions, type BrowserPublishDiagnostic, type BrowserPublishOptions, type BrowserPublishOutputFileSystem, type BrowserPublishProjectType, type BrowserPublishResult, type BrowserPublishSourceFileSystem, type BrowserPublishedFile, publishBrowser, validateProjectWeb };
|
package/dist/web.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { d as PublishSourceFileSystem, t as AtlasOptions, u as PublishOutputFileSystem } from "./atlas-C6tbl7nn.js";
|
|
2
|
-
import { Document } from "@openfairygui/core";
|
|
2
|
+
import { Document, ProjectValidationReport, UamProject } from "@openfairygui/core";
|
|
3
3
|
|
|
4
4
|
//#region src/adapters/web/publish.d.ts
|
|
5
5
|
type BrowserPublishProjectType = 'layabox';
|
|
@@ -41,4 +41,7 @@ interface BrowserPublishResult {
|
|
|
41
41
|
*/
|
|
42
42
|
declare function publishBrowser(options: BrowserPublishOptions): Promise<BrowserPublishResult>;
|
|
43
43
|
//#endregion
|
|
44
|
-
|
|
44
|
+
//#region src/adapters/web/validate.d.ts
|
|
45
|
+
declare function validateProjectWeb(project: UamProject): Promise<ProjectValidationReport>;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { type BrowserPublishAtlasOptions, type BrowserPublishDiagnostic, type BrowserPublishOptions, type BrowserPublishOutputFileSystem, type BrowserPublishProjectType, type BrowserPublishResult, type BrowserPublishSourceFileSystem, type BrowserPublishedFile, publishBrowser, validateProjectWeb };
|
package/dist/web.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as resolveCodeGenerationSettings, n as resolvePublishOptions, t as publish } from "./publish-
|
|
2
|
-
import { ProjectType } from "@openfairygui/core";
|
|
1
|
+
import { _ as validateProject, c as resolveCodeGenerationSettings, n as resolvePublishOptions, t as publish } from "./publish-CyBj2o7n.js";
|
|
2
|
+
import { ProjectType, createProjectValidationReport } from "@openfairygui/core";
|
|
3
3
|
import { XMLParser, XMLValidator } from "fast-xml-parser";
|
|
4
4
|
//#region src/adapters/web/raster.ts
|
|
5
5
|
const MAX_SVG_SOURCE_BYTES = 8 * 1024 * 1024;
|
|
@@ -185,6 +185,9 @@ async function decodeRaster(bytes, mimeType) {
|
|
|
185
185
|
bitmap.close();
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
|
+
async function validateBrowserImageSource(bytes, path) {
|
|
189
|
+
await decodeRaster(bytes, imageMimeType(path));
|
|
190
|
+
}
|
|
188
191
|
async function decodeSvgWithDom(blob) {
|
|
189
192
|
if (typeof globalThis.Image !== "function" || typeof globalThis.URL?.createObjectURL !== "function" || typeof globalThis.URL?.revokeObjectURL !== "function") throw new Error("publishBrowser: createImageBitmap rejected SVG and DOM image decoding is unavailable.");
|
|
190
193
|
const url = globalThis.URL.createObjectURL(blob);
|
|
@@ -436,4 +439,45 @@ async function publishBrowser(options) {
|
|
|
436
439
|
}
|
|
437
440
|
}
|
|
438
441
|
//#endregion
|
|
439
|
-
|
|
442
|
+
//#region src/adapters/web/validate.ts
|
|
443
|
+
function sourceName(resource) {
|
|
444
|
+
return resource.sourcePath ?? resource.fileName ?? resource.name;
|
|
445
|
+
}
|
|
446
|
+
async function validateProjectWeb(project) {
|
|
447
|
+
const base = validateProject(project, { validateSources: true });
|
|
448
|
+
if (!project.packages.some((pkg) => pkg.resources.some((resource) => resource.kind === "image" && resource.sourceBytes instanceof Uint8Array))) return base;
|
|
449
|
+
try {
|
|
450
|
+
assertBrowserImageSupport();
|
|
451
|
+
} catch (error) {
|
|
452
|
+
return createProjectValidationReport([...base.diagnostics, {
|
|
453
|
+
severity: "warning",
|
|
454
|
+
code: "decode_capability_unavailable",
|
|
455
|
+
path: "project",
|
|
456
|
+
message: error instanceof Error ? error.message : String(error)
|
|
457
|
+
}], false);
|
|
458
|
+
}
|
|
459
|
+
const decodedPaths = /* @__PURE__ */ new Set();
|
|
460
|
+
const diagnostics = [...base.diagnostics];
|
|
461
|
+
for (const [packageIndex, pkg] of project.packages.entries()) for (const [resourceIndex, resource] of pkg.resources.entries()) {
|
|
462
|
+
if (resource.kind !== "image" || !(resource.sourceBytes instanceof Uint8Array)) continue;
|
|
463
|
+
try {
|
|
464
|
+
await validateBrowserImageSource(resource.sourceBytes, sourceName(resource));
|
|
465
|
+
decodedPaths.add(`packages[${packageIndex}].resources[${resourceIndex}]`);
|
|
466
|
+
} catch (error) {
|
|
467
|
+
const capabilityUnavailable = error instanceof Error && /(?:required|unavailable)/iu.test(error.message);
|
|
468
|
+
diagnostics.push({
|
|
469
|
+
severity: capabilityUnavailable ? "warning" : "error",
|
|
470
|
+
code: capabilityUnavailable ? "decode_capability_unavailable" : "corrupt_source",
|
|
471
|
+
path: `packages[${packageIndex}].resources[${resourceIndex}]`,
|
|
472
|
+
message: `Image source cannot be decoded: ${error instanceof Error ? error.message : String(error)}`,
|
|
473
|
+
packageId: pkg.id,
|
|
474
|
+
resourceId: resource.id,
|
|
475
|
+
sourcePath: sourceName(resource)
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
const resolved = diagnostics.filter((diagnostic) => diagnostic.code !== "decode_capability_unavailable" || !decodedPaths.has(diagnostic.path));
|
|
480
|
+
return createProjectValidationReport(resolved, !resolved.some((diagnostic) => diagnostic.code === "decode_capability_unavailable"));
|
|
481
|
+
}
|
|
482
|
+
//#endregion
|
|
483
|
+
export { publishBrowser, validateProjectWeb };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openfairygui/functions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-alpha.2",
|
|
4
4
|
"description": "FairyGUI Headless Authoring SDK — composable transform functions.",
|
|
5
5
|
"author": "OpenFairyGUI Contributors",
|
|
6
6
|
"license": "MIT",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"fast-xml-parser": "^5.0.0",
|
|
77
77
|
"jiti": "^2.6.1",
|
|
78
|
-
"@openfairygui/core": "0.
|
|
78
|
+
"@openfairygui/core": "0.3.0-alpha.2"
|
|
79
79
|
},
|
|
80
80
|
"optionalDependencies": {
|
|
81
81
|
"sharp": ">=0.33.0"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createProjectValidationReport,
|
|
3
|
+
liftDocumentToUamProject,
|
|
4
|
+
type ProjectDiagnostic,
|
|
5
|
+
type ProjectValidationReport,
|
|
6
|
+
type UamProject,
|
|
7
|
+
} from '@openfairygui/core';
|
|
8
|
+
import { NodeIO } from '@openfairygui/core/node';
|
|
9
|
+
import { validateProject } from '../../validate.js';
|
|
10
|
+
|
|
11
|
+
const importNative = new Function('id', 'return import(id)') as <T>(id: string) => Promise<T>;
|
|
12
|
+
|
|
13
|
+
function sourceName(resource: {
|
|
14
|
+
kind: string;
|
|
15
|
+
name: string;
|
|
16
|
+
fileName?: string;
|
|
17
|
+
file?: string;
|
|
18
|
+
sourcePath?: string;
|
|
19
|
+
}): string {
|
|
20
|
+
return resource.sourcePath ?? resource.fileName ?? resource.file ?? resource.name;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function validateProjectNode(projectPath: string): Promise<ProjectValidationReport> {
|
|
24
|
+
const read = await new NodeIO().readProjectDetailed(projectPath, { hydrateResourceBytes: true });
|
|
25
|
+
if (!read.document) return createProjectValidationReport(read.diagnostics, false);
|
|
26
|
+
|
|
27
|
+
let project: UamProject;
|
|
28
|
+
try {
|
|
29
|
+
project = liftDocumentToUamProject(read.document);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
return createProjectValidationReport([...read.diagnostics, {
|
|
32
|
+
severity: 'error',
|
|
33
|
+
code: 'invalid_uam',
|
|
34
|
+
path: 'project',
|
|
35
|
+
message: `Project cannot be represented as UAM: ${error instanceof Error ? error.message : String(error)}`,
|
|
36
|
+
sourcePath: projectPath,
|
|
37
|
+
}], false);
|
|
38
|
+
}
|
|
39
|
+
const base = validateProject(project, {
|
|
40
|
+
readDiagnostics: read.diagnostics,
|
|
41
|
+
complete: read.complete,
|
|
42
|
+
validateSources: true,
|
|
43
|
+
});
|
|
44
|
+
let diagnostics: ProjectDiagnostic[] = base.diagnostics;
|
|
45
|
+
const knownCorruptPaths = new Set(base.diagnostics
|
|
46
|
+
.filter((diagnostic) => diagnostic.code === 'corrupt_source')
|
|
47
|
+
.map((diagnostic) => diagnostic.path));
|
|
48
|
+
const images = project.packages.flatMap((pkg, packageIndex) => pkg.resources
|
|
49
|
+
.map((resource, resourceIndex) => ({ pkg, packageIndex, resource, resourceIndex }))
|
|
50
|
+
.filter(({ resource, packageIndex, resourceIndex }) => (
|
|
51
|
+
resource.kind === 'image'
|
|
52
|
+
&& resource.sourceBytes instanceof Uint8Array
|
|
53
|
+
&& !knownCorruptPaths.has(`packages[${packageIndex}].resources[${resourceIndex}]`)
|
|
54
|
+
)));
|
|
55
|
+
if (images.length === 0) return base;
|
|
56
|
+
|
|
57
|
+
let sharp: ((bytes: Uint8Array) => { raw(): { toBuffer(): Promise<unknown> } });
|
|
58
|
+
try {
|
|
59
|
+
const loaded = await importNative<typeof import('sharp')>('sharp');
|
|
60
|
+
sharp = (loaded as unknown as { default?: typeof loaded }).default ?? loaded;
|
|
61
|
+
} catch {
|
|
62
|
+
return createProjectValidationReport([...base.diagnostics, {
|
|
63
|
+
severity: 'warning',
|
|
64
|
+
code: 'decode_capability_unavailable',
|
|
65
|
+
path: 'project',
|
|
66
|
+
message: 'Sharp is unavailable, so Node image decoding could not be completed.',
|
|
67
|
+
}], false);
|
|
68
|
+
}
|
|
69
|
+
const decodedPaths = new Set(images.map(({ packageIndex, resourceIndex }) => `packages[${packageIndex}].resources[${resourceIndex}]`));
|
|
70
|
+
diagnostics = diagnostics.filter((diagnostic) => (
|
|
71
|
+
diagnostic.code !== 'decode_capability_unavailable' || !decodedPaths.has(diagnostic.path)
|
|
72
|
+
));
|
|
73
|
+
|
|
74
|
+
for (const { pkg, packageIndex, resource, resourceIndex } of images) {
|
|
75
|
+
try {
|
|
76
|
+
await sharp(resource.sourceBytes!).raw().toBuffer();
|
|
77
|
+
} catch (error) {
|
|
78
|
+
diagnostics.push({
|
|
79
|
+
severity: 'error',
|
|
80
|
+
code: 'corrupt_source',
|
|
81
|
+
path: `packages[${packageIndex}].resources[${resourceIndex}]`,
|
|
82
|
+
message: `Image source cannot be decoded: ${error instanceof Error ? error.message : String(error)}`,
|
|
83
|
+
packageId: pkg.id,
|
|
84
|
+
resourceId: resource.id,
|
|
85
|
+
sourcePath: sourceName(resource),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return createProjectValidationReport(
|
|
90
|
+
diagnostics,
|
|
91
|
+
read.complete && !diagnostics.some((diagnostic) => diagnostic.code === 'decode_capability_unavailable'),
|
|
92
|
+
);
|
|
93
|
+
}
|
|
@@ -265,6 +265,10 @@ async function decodeRaster(bytes: Uint8Array, mimeType: string): Promise<Browse
|
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
+
export async function validateBrowserImageSource(bytes: Uint8Array, path: string): Promise<void> {
|
|
269
|
+
await decodeRaster(bytes, imageMimeType(path));
|
|
270
|
+
}
|
|
271
|
+
|
|
268
272
|
async function decodeSvgWithDom(blob: Blob): Promise<BrowserRaster> {
|
|
269
273
|
if (typeof globalThis.Image !== 'function'
|
|
270
274
|
|| typeof globalThis.URL?.createObjectURL !== 'function'
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createProjectValidationReport,
|
|
3
|
+
type ProjectDiagnostic,
|
|
4
|
+
type ProjectValidationReport,
|
|
5
|
+
type UamProject,
|
|
6
|
+
} from '@openfairygui/core';
|
|
7
|
+
import { validateProject } from '../../validate.js';
|
|
8
|
+
import { assertBrowserImageSupport, validateBrowserImageSource } from './raster.js';
|
|
9
|
+
|
|
10
|
+
function sourceName(resource: {
|
|
11
|
+
name: string;
|
|
12
|
+
fileName?: string;
|
|
13
|
+
sourcePath?: string;
|
|
14
|
+
}): string {
|
|
15
|
+
return resource.sourcePath ?? resource.fileName ?? resource.name;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function validateProjectWeb(project: UamProject): Promise<ProjectValidationReport> {
|
|
19
|
+
const base = validateProject(project, { validateSources: true });
|
|
20
|
+
const hasHydratedImages = project.packages.some((pkg) => pkg.resources.some(
|
|
21
|
+
(resource) => resource.kind === 'image' && resource.sourceBytes instanceof Uint8Array,
|
|
22
|
+
));
|
|
23
|
+
if (!hasHydratedImages) return base;
|
|
24
|
+
try {
|
|
25
|
+
assertBrowserImageSupport();
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return createProjectValidationReport([...base.diagnostics, {
|
|
28
|
+
severity: 'warning',
|
|
29
|
+
code: 'decode_capability_unavailable',
|
|
30
|
+
path: 'project',
|
|
31
|
+
message: error instanceof Error ? error.message : String(error),
|
|
32
|
+
}], false);
|
|
33
|
+
}
|
|
34
|
+
const decodedPaths = new Set<string>();
|
|
35
|
+
const diagnostics: ProjectDiagnostic[] = [...base.diagnostics];
|
|
36
|
+
for (const [packageIndex, pkg] of project.packages.entries()) {
|
|
37
|
+
for (const [resourceIndex, resource] of pkg.resources.entries()) {
|
|
38
|
+
if (resource.kind !== 'image' || !(resource.sourceBytes instanceof Uint8Array)) continue;
|
|
39
|
+
try {
|
|
40
|
+
await validateBrowserImageSource(resource.sourceBytes, sourceName(resource));
|
|
41
|
+
decodedPaths.add(`packages[${packageIndex}].resources[${resourceIndex}]`);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
const capabilityUnavailable = error instanceof Error && /(?:required|unavailable)/iu.test(error.message);
|
|
44
|
+
diagnostics.push({
|
|
45
|
+
severity: capabilityUnavailable ? 'warning' : 'error',
|
|
46
|
+
code: capabilityUnavailable ? 'decode_capability_unavailable' : 'corrupt_source',
|
|
47
|
+
path: `packages[${packageIndex}].resources[${resourceIndex}]`,
|
|
48
|
+
message: `Image source cannot be decoded: ${error instanceof Error ? error.message : String(error)}`,
|
|
49
|
+
packageId: pkg.id,
|
|
50
|
+
resourceId: resource.id,
|
|
51
|
+
sourcePath: sourceName(resource),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const resolved = diagnostics.filter((diagnostic) => (
|
|
57
|
+
diagnostic.code !== 'decode_capability_unavailable' || !decodedPaths.has(diagnostic.path)
|
|
58
|
+
));
|
|
59
|
+
return createProjectValidationReport(resolved, !resolved.some((diagnostic) => diagnostic.code === 'decode_capability_unavailable'));
|
|
60
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
export { inspect, type InspectReport, type InspectCategoryReport } from './inspect.js';
|
|
2
2
|
export {
|
|
3
|
-
|
|
4
|
-
type
|
|
5
|
-
type ValidationResult,
|
|
6
|
-
type ValidationIssue,
|
|
7
|
-
ValidationSeverity,
|
|
3
|
+
validateProject,
|
|
4
|
+
type ValidateProjectOptions,
|
|
8
5
|
} from './validate.js';
|
|
9
6
|
export { prune, type PruneOptions } from './prune.js';
|
|
10
7
|
export { rename, type RenameOptions } from './rename.js';
|