@plasius/asset-contracts 0.5.0 → 0.7.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/CHANGELOG.md +37 -0
- package/README.md +130 -1
- package/dist/index.cjs +345 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +127 -2
- package/dist/index.d.ts +127 -2
- package/dist/index.js +329 -6
- package/dist/index.js.map +1 -1
- package/docs/adrs/adr-0010-public-conversion-diagnostics.md +61 -0
- package/docs/adrs/adr-0011-reloadable-texture-resource-roles.md +92 -0
- package/docs/adrs/index.md +2 -0
- package/docs/tdrs/index.md +2 -0
- package/docs/tdrs/tdr-0005-public-conversion-diagnostics.md +51 -0
- package/docs/tdrs/tdr-0006-reloadable-texture-resource-roles.md +91 -0
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/model-conversion-diagnostics.ts +185 -0
- package/src/model-texture-resources.ts +215 -0
package/dist/index.js
CHANGED
|
@@ -196,11 +196,11 @@ function assertNoDirectUrl(value, fieldName) {
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
function requiredToken(value, fieldName) {
|
|
199
|
-
const
|
|
200
|
-
if (!TOKEN_PATTERN.test(
|
|
199
|
+
const token3 = requiredString(value, fieldName, 128);
|
|
200
|
+
if (!TOKEN_PATTERN.test(token3)) {
|
|
201
201
|
throw new Error(`${fieldName} must be a token up to 128 characters.`);
|
|
202
202
|
}
|
|
203
|
-
return
|
|
203
|
+
return token3;
|
|
204
204
|
}
|
|
205
205
|
function requiredModelPathSegment(value, fieldName) {
|
|
206
206
|
const segment = requiredString(value, fieldName, 128);
|
|
@@ -6701,9 +6701,9 @@ function snapshot(value, budget, depth = 0) {
|
|
|
6701
6701
|
return value;
|
|
6702
6702
|
}
|
|
6703
6703
|
if (Array.isArray(value)) return Object.freeze(list(value).map((item) => snapshot(item, budget, depth + 1)));
|
|
6704
|
-
const
|
|
6705
|
-
for (const key of Object.keys(
|
|
6706
|
-
return Object.freeze(
|
|
6704
|
+
const fields3 = record(value);
|
|
6705
|
+
for (const key of Object.keys(fields3)) fields3[key] = snapshot(fields3[key], budget, depth + 1);
|
|
6706
|
+
return Object.freeze(fields3);
|
|
6707
6707
|
}
|
|
6708
6708
|
function path(value) {
|
|
6709
6709
|
if (typeof value !== "string" || value.length > 1024 || !value.length) fail();
|
|
@@ -6812,6 +6812,323 @@ async function qualify(input) {
|
|
|
6812
6812
|
return Object.freeze({ ...manifest, files: Object.freeze([...manifest.files].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)) });
|
|
6813
6813
|
}
|
|
6814
6814
|
|
|
6815
|
+
// src/model-texture-resources.ts
|
|
6816
|
+
import { isGpuModelResourceGraph } from "@plasius/gpu-model-core";
|
|
6817
|
+
var MODEL_TEXTURE_RESOURCE_LIMITS = Object.freeze({ resources: 16384, derived: 4096, nodes: 65536, values: 262144, characters: 2097152, depth: 16, timeoutMs: 6e4 });
|
|
6818
|
+
var ModelTextureResourceError = class extends TypeError {
|
|
6819
|
+
constructor(code) {
|
|
6820
|
+
super(`Invalid model texture resources: ${code}`);
|
|
6821
|
+
this.code = code;
|
|
6822
|
+
this.name = "ModelTextureResourceError";
|
|
6823
|
+
}
|
|
6824
|
+
code;
|
|
6825
|
+
};
|
|
6826
|
+
function fail2(code = "invalid-input") {
|
|
6827
|
+
throw new ModelTextureResourceError(code);
|
|
6828
|
+
}
|
|
6829
|
+
function fields(value, allowed) {
|
|
6830
|
+
if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail2();
|
|
6831
|
+
const keys = Reflect.ownKeys(value);
|
|
6832
|
+
if (keys.length > 1024) fail2("limit-exceeded");
|
|
6833
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
6834
|
+
for (const key of keys) {
|
|
6835
|
+
if (typeof key !== "string" || key.length > 128 || ["__proto__", "prototype", "constructor"].includes(key) || allowed && !allowed.includes(key)) fail2();
|
|
6836
|
+
const property = Object.getOwnPropertyDescriptor(value, key);
|
|
6837
|
+
if (!("value" in property) || !property.enumerable) fail2();
|
|
6838
|
+
result[key] = property.value;
|
|
6839
|
+
}
|
|
6840
|
+
return result;
|
|
6841
|
+
}
|
|
6842
|
+
function array(value, maximum) {
|
|
6843
|
+
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) fail2();
|
|
6844
|
+
if (value.length > maximum) fail2("limit-exceeded");
|
|
6845
|
+
if (Reflect.ownKeys(value).length !== value.length + 1) fail2();
|
|
6846
|
+
const result = [];
|
|
6847
|
+
for (let i = 0; i < value.length; i++) {
|
|
6848
|
+
const property = Object.getOwnPropertyDescriptor(value, String(i));
|
|
6849
|
+
if (!property || !("value" in property) || !property.enumerable) fail2();
|
|
6850
|
+
result.push(property.value);
|
|
6851
|
+
}
|
|
6852
|
+
return result;
|
|
6853
|
+
}
|
|
6854
|
+
function snapshot2(value, budget, depth = 0) {
|
|
6855
|
+
if (--budget.values < 0 || depth > MODEL_TEXTURE_RESOURCE_LIMITS.depth) fail2("limit-exceeded");
|
|
6856
|
+
if (value === null || value === void 0 || typeof value === "boolean") return value;
|
|
6857
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
6858
|
+
if (typeof value === "string") {
|
|
6859
|
+
budget.characters -= value.length;
|
|
6860
|
+
if (value.length > 2048 || budget.characters < 0) fail2("limit-exceeded");
|
|
6861
|
+
return value;
|
|
6862
|
+
}
|
|
6863
|
+
if (Array.isArray(value)) return Object.freeze(array(value, MODEL_TEXTURE_RESOURCE_LIMITS.resources).map((v) => snapshot2(v, budget, depth + 1)));
|
|
6864
|
+
const result = fields(value);
|
|
6865
|
+
for (const key of Object.keys(result)) {
|
|
6866
|
+
budget.characters -= key.length;
|
|
6867
|
+
if (budget.characters < 0) fail2("limit-exceeded");
|
|
6868
|
+
result[key] = snapshot2(result[key], budget, depth + 1);
|
|
6869
|
+
}
|
|
6870
|
+
return Object.freeze(result);
|
|
6871
|
+
}
|
|
6872
|
+
function token2(value) {
|
|
6873
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value)) fail2();
|
|
6874
|
+
return value;
|
|
6875
|
+
}
|
|
6876
|
+
var compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
6877
|
+
var imageMime = (value) => ["image/png", "image/jpeg", "image/webp", "image/ktx2"].includes(value);
|
|
6878
|
+
var bufferMime = (value) => ["application/octet-stream", "application/vnd.plasius.gpu-buffer"].includes(value);
|
|
6879
|
+
function storageRole(role, file) {
|
|
6880
|
+
const valid = role === "texture" ? file.role === "texture" && imageMime(file.contentType) : role === "material" || role === "animation" ? file.role === "metadata" && file.contentType === "application/json" : role === "mesh" ? file.role === "binary" && bufferMime(file.contentType) || file.role === "model" && (file.contentType.startsWith("model/") || ["application/json", "application/octet-stream", "text/plain"].includes(file.contentType)) : role === "auxiliary" && (file.role === "binary" && bufferMime(file.contentType) || file.role === "metadata" && file.contentType === "application/json");
|
|
6881
|
+
if (!valid) fail2("role-mismatch");
|
|
6882
|
+
}
|
|
6883
|
+
async function qualifyModelTextureResources(input, options = {}) {
|
|
6884
|
+
let timer;
|
|
6885
|
+
let signal;
|
|
6886
|
+
let onAbort;
|
|
6887
|
+
try {
|
|
6888
|
+
const started = performance.now();
|
|
6889
|
+
const controls = fields(options, ["signal", "timeoutMs"]);
|
|
6890
|
+
const timeoutMs = controls.timeoutMs === void 0 ? 1e4 : controls.timeoutMs;
|
|
6891
|
+
if (typeof timeoutMs !== "number" || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MODEL_TEXTURE_RESOURCE_LIMITS.timeoutMs) fail2();
|
|
6892
|
+
signal = controls.signal;
|
|
6893
|
+
const aborted = Object.getOwnPropertyDescriptor(AbortSignal.prototype, "aborted").get;
|
|
6894
|
+
const check = () => {
|
|
6895
|
+
if (signal !== void 0 && aborted.call(signal)) fail2("aborted");
|
|
6896
|
+
if (performance.now() - started >= timeoutMs) fail2("deadline-exceeded");
|
|
6897
|
+
};
|
|
6898
|
+
check();
|
|
6899
|
+
const root = fields(input, ["manifest", "packageManifest", "files", "graph", "resources", "derived"]);
|
|
6900
|
+
const graph = root.graph;
|
|
6901
|
+
if (!isGpuModelResourceGraph(graph)) fail2("unverified-graph");
|
|
6902
|
+
if (graph.nodes.length > MODEL_TEXTURE_RESOURCE_LIMITS.nodes) fail2("limit-exceeded");
|
|
6903
|
+
const budget = { values: MODEL_TEXTURE_RESOURCE_LIMITS.values, characters: MODEL_TEXTURE_RESOURCE_LIMITS.characters };
|
|
6904
|
+
const inventory = snapshot2(root.packageManifest, budget);
|
|
6905
|
+
const bindings = array(snapshot2(root.resources, budget), MODEL_TEXTURE_RESOURCE_LIMITS.resources).map((value) => {
|
|
6906
|
+
const binding = fields(value, ["resourceId", "filePath"]);
|
|
6907
|
+
return Object.freeze({ resourceId: token2(binding.resourceId), filePath: tokenPath(binding.filePath) });
|
|
6908
|
+
});
|
|
6909
|
+
const derived = array(snapshot2(root.derived, budget), MODEL_TEXTURE_RESOURCE_LIMITS.derived).map((value) => {
|
|
6910
|
+
const record2 = fields(value, ["role", "filePath", "sourceResourceIds"]);
|
|
6911
|
+
if (record2.role !== "gpu-upload" && record2.role !== "texture-atlas") fail2("role-mismatch");
|
|
6912
|
+
const ids = array(record2.sourceResourceIds, MODEL_TEXTURE_RESOURCE_LIMITS.resources).map(token2);
|
|
6913
|
+
if (!ids.length || new Set(ids).size !== ids.length) fail2("resource-mismatch");
|
|
6914
|
+
return Object.freeze({ role: record2.role, filePath: tokenPath(record2.filePath), sourceResourceIds: Object.freeze(ids.sort(compare)) });
|
|
6915
|
+
});
|
|
6916
|
+
check();
|
|
6917
|
+
const interrupted = new Promise((_resolve, reject) => {
|
|
6918
|
+
timer = setTimeout(() => reject(new ModelTextureResourceError("deadline-exceeded")), Math.max(1, timeoutMs - (performance.now() - started)));
|
|
6919
|
+
onAbort = () => reject(new ModelTextureResourceError("aborted"));
|
|
6920
|
+
if (signal) AbortSignal.prototype.addEventListener.call(signal, "abort", onAbort, { once: true });
|
|
6921
|
+
});
|
|
6922
|
+
const manifest = await Promise.race([
|
|
6923
|
+
qualifyModelResourceManifest({ manifest: root.manifest, files: root.files, packageManifest: inventory, signal, timeoutMs }),
|
|
6924
|
+
interrupted
|
|
6925
|
+
]);
|
|
6926
|
+
check();
|
|
6927
|
+
const files = new Map(manifest.files.map((file) => [file.path, file]));
|
|
6928
|
+
const roles = new Map(inventory.entries.map((entry) => [entry.path, entry.role]));
|
|
6929
|
+
for (const entry of inventory.entries) storageRole(entry.role, files.get(entry.path));
|
|
6930
|
+
const resources = new Map(graph.nodes.filter((node) => node.kind === "resource").map((node) => [node.id, node.value]));
|
|
6931
|
+
if (bindings.length !== resources.size || new Set(bindings.map((b) => b.resourceId)).size !== bindings.length) fail2("resource-mismatch");
|
|
6932
|
+
const resourceFiles = /* @__PURE__ */ new Set();
|
|
6933
|
+
const qualified = bindings.map((binding) => {
|
|
6934
|
+
const value = resources.get(binding.resourceId);
|
|
6935
|
+
const file = files.get(binding.filePath);
|
|
6936
|
+
if (!value || !file) fail2("resource-mismatch");
|
|
6937
|
+
const { resource, location, exportEmbeddingPolicy } = value;
|
|
6938
|
+
if (location.kind === "external") {
|
|
6939
|
+
if (binding.filePath !== location.relativePath || file.sha256 !== resource.contentHash || file.byteLength !== resource.byteLength || file.contentType !== resource.mimeType) fail2("resource-mismatch");
|
|
6940
|
+
if (resource.kind === "image" ? roles.get(file.path) !== "texture" : file.role !== "binary") fail2("role-mismatch");
|
|
6941
|
+
} else if (binding.filePath !== manifest.entrypoint || file.role !== "model") fail2("resource-mismatch");
|
|
6942
|
+
resourceFiles.add(binding.filePath);
|
|
6943
|
+
return Object.freeze({ ...binding, kind: resource.kind, contentHash: resource.contentHash, byteLength: resource.byteLength, mimeType: resource.mimeType, location, exportEmbeddingPolicy });
|
|
6944
|
+
});
|
|
6945
|
+
const outputFiles = /* @__PURE__ */ new Set();
|
|
6946
|
+
for (const output of derived) {
|
|
6947
|
+
const file = files.get(output.filePath);
|
|
6948
|
+
if (!file || outputFiles.has(output.filePath) || resourceFiles.has(output.filePath)) fail2("resource-mismatch");
|
|
6949
|
+
outputFiles.add(output.filePath);
|
|
6950
|
+
for (const id of output.sourceResourceIds) {
|
|
6951
|
+
const source2 = resources.get(id);
|
|
6952
|
+
if (!source2 || output.role === "texture-atlas" && source2.resource.kind !== "image") fail2("resource-mismatch");
|
|
6953
|
+
}
|
|
6954
|
+
if (output.role === "gpu-upload" ? file.role !== "binary" || !bufferMime(file.contentType) || roles.get(file.path) !== "auxiliary" : file.role !== "texture" || !imageMime(file.contentType) || roles.get(file.path) !== "texture") fail2("role-mismatch");
|
|
6955
|
+
}
|
|
6956
|
+
const materials = graph.nodes.filter((node) => node.kind === "material").map((node) => node.value);
|
|
6957
|
+
const textures = graph.nodes.filter((node) => node.kind === "texture").map((node) => node.value);
|
|
6958
|
+
const source = graph.nodes.find((node) => node.kind === "provenance");
|
|
6959
|
+
if (source.kind !== "provenance") fail2();
|
|
6960
|
+
snapshot2({ materials, textures, source: source.value }, budget);
|
|
6961
|
+
check();
|
|
6962
|
+
return Object.freeze({
|
|
6963
|
+
contractVersion: 1,
|
|
6964
|
+
manifest,
|
|
6965
|
+
source: source.value,
|
|
6966
|
+
resources: Object.freeze(qualified.sort((a, b) => compare(a.resourceId, b.resourceId))),
|
|
6967
|
+
materials: Object.freeze(materials),
|
|
6968
|
+
textures: Object.freeze(textures),
|
|
6969
|
+
derived: Object.freeze(derived.sort((a, b) => compare(a.filePath, b.filePath)))
|
|
6970
|
+
});
|
|
6971
|
+
} catch (error) {
|
|
6972
|
+
if (error instanceof ModelTextureResourceError) throw error;
|
|
6973
|
+
if (error instanceof ModelResourceManifestError) return fail2(error.code === "aborted" || error.code === "deadline-exceeded" || error.code === "limit-exceeded" ? error.code : "package-mismatch");
|
|
6974
|
+
return fail2();
|
|
6975
|
+
} finally {
|
|
6976
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
6977
|
+
if (signal && onAbort) AbortSignal.prototype.removeEventListener.call(signal, "abort", onAbort);
|
|
6978
|
+
}
|
|
6979
|
+
}
|
|
6980
|
+
function tokenPath(value) {
|
|
6981
|
+
if (typeof value !== "string" || !value.length || value.length > 1024) fail2();
|
|
6982
|
+
return value;
|
|
6983
|
+
}
|
|
6984
|
+
|
|
6985
|
+
// src/model-conversion-diagnostics.ts
|
|
6986
|
+
var MODEL_CONVERSION_DIAGNOSTIC_LIMITS = Object.freeze({ issues: 100, resources: 100, values: 16384, depth: 12, string: 2048, characters: 262144, timeoutMs: 6e4 });
|
|
6987
|
+
var ModelConversionDiagnosticsError = class extends TypeError {
|
|
6988
|
+
constructor(code) {
|
|
6989
|
+
super(`Invalid model conversion diagnostics: ${code}`);
|
|
6990
|
+
this.code = code;
|
|
6991
|
+
this.name = "ModelConversionDiagnosticsError";
|
|
6992
|
+
}
|
|
6993
|
+
code;
|
|
6994
|
+
};
|
|
6995
|
+
function fail3(code = "invalid-input") {
|
|
6996
|
+
throw new ModelConversionDiagnosticsError(code);
|
|
6997
|
+
}
|
|
6998
|
+
function fields2(value) {
|
|
6999
|
+
if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail3();
|
|
7000
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
7001
|
+
const keys = Reflect.ownKeys(value);
|
|
7002
|
+
if (keys.length > 1024) fail3("limit-exceeded");
|
|
7003
|
+
for (const key of keys) {
|
|
7004
|
+
if (typeof key !== "string" || key.length > 128 || ["__proto__", "prototype", "constructor"].includes(key)) fail3();
|
|
7005
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
7006
|
+
if (!("value" in descriptor) || !descriptor.enumerable) fail3();
|
|
7007
|
+
result[key] = descriptor.value;
|
|
7008
|
+
}
|
|
7009
|
+
return result;
|
|
7010
|
+
}
|
|
7011
|
+
function only(record2, keys) {
|
|
7012
|
+
if (Object.keys(record2).some((key) => !keys.includes(key))) fail3();
|
|
7013
|
+
}
|
|
7014
|
+
function snapshot3(value, budget, depth = 0) {
|
|
7015
|
+
if (--budget.values < 0 || depth > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.depth) fail3("limit-exceeded");
|
|
7016
|
+
if (value === null || typeof value === "boolean") return value;
|
|
7017
|
+
if (typeof value === "string") {
|
|
7018
|
+
budget.characters -= value.length;
|
|
7019
|
+
if (value.length > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.string || budget.characters < 0) fail3("limit-exceeded");
|
|
7020
|
+
return value;
|
|
7021
|
+
}
|
|
7022
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
7023
|
+
if (Array.isArray(value)) {
|
|
7024
|
+
if (Object.getPrototypeOf(value) !== Array.prototype || value.length > 1024) fail3("limit-exceeded");
|
|
7025
|
+
if (Reflect.ownKeys(value).length !== value.length + 1) fail3();
|
|
7026
|
+
const result = [];
|
|
7027
|
+
for (let i = 0; i < value.length; i++) {
|
|
7028
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(i));
|
|
7029
|
+
if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) fail3();
|
|
7030
|
+
result.push(snapshot3(descriptor.value, budget, depth + 1));
|
|
7031
|
+
}
|
|
7032
|
+
return Object.freeze(result);
|
|
7033
|
+
}
|
|
7034
|
+
const record2 = fields2(value);
|
|
7035
|
+
for (const key of Object.keys(record2)) {
|
|
7036
|
+
budget.characters -= key.length;
|
|
7037
|
+
if (budget.characters < 0) fail3("limit-exceeded");
|
|
7038
|
+
record2[key] = snapshot3(record2[key], budget, depth + 1);
|
|
7039
|
+
}
|
|
7040
|
+
return Object.freeze(record2);
|
|
7041
|
+
}
|
|
7042
|
+
function legacy(value) {
|
|
7043
|
+
return Object.freeze({ severity: value.severity, code: value.code, message: value.message });
|
|
7044
|
+
}
|
|
7045
|
+
async function qualifyModelConversionDiagnostics(input, options = {}) {
|
|
7046
|
+
let timer;
|
|
7047
|
+
let signal;
|
|
7048
|
+
let onAbort;
|
|
7049
|
+
try {
|
|
7050
|
+
const start = performance.now();
|
|
7051
|
+
const controls = fields2(options);
|
|
7052
|
+
only(controls, ["signal", "timeoutMs"]);
|
|
7053
|
+
const timeoutMs = controls.timeoutMs === void 0 ? 1e4 : controls.timeoutMs;
|
|
7054
|
+
if (typeof timeoutMs !== "number" || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.timeoutMs) fail3();
|
|
7055
|
+
signal = controls.signal;
|
|
7056
|
+
const aborted = Object.getOwnPropertyDescriptor(AbortSignal.prototype, "aborted").get;
|
|
7057
|
+
const check = () => {
|
|
7058
|
+
if (signal !== void 0 && aborted.call(signal)) fail3("aborted");
|
|
7059
|
+
if (performance.now() - start >= timeoutMs) fail3("deadline-exceeded");
|
|
7060
|
+
};
|
|
7061
|
+
check();
|
|
7062
|
+
const args = fields2(snapshot3(input, { values: MODEL_CONVERSION_DIAGNOSTIC_LIMITS.values, characters: MODEL_CONVERSION_DIAGNOSTIC_LIMITS.characters }));
|
|
7063
|
+
only(args, ["outcome", "source", "report", "resources"]);
|
|
7064
|
+
if (!["completed", "unsupported", "failed"].includes(args.outcome)) fail3();
|
|
7065
|
+
const source = createModelProvenance(args.source);
|
|
7066
|
+
const reportInput = fields2(args.report);
|
|
7067
|
+
const maximumIssues = MODEL_CONVERSION_DIAGNOSTIC_LIMITS.issues - (args.outcome === "completed" ? 0 : 1);
|
|
7068
|
+
if (!Array.isArray(reportInput.issues) || reportInput.issues.length > maximumIssues) fail3("limit-exceeded");
|
|
7069
|
+
if (!Array.isArray(args.resources) || args.resources.length > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.resources) fail3("limit-exceeded");
|
|
7070
|
+
const resources = /* @__PURE__ */ new Map();
|
|
7071
|
+
const identities = /* @__PURE__ */ new Map();
|
|
7072
|
+
for (const value of args.resources) {
|
|
7073
|
+
const binding = fields2(value);
|
|
7074
|
+
only(binding, ["path", "resource"]);
|
|
7075
|
+
if (typeof binding.path !== "string" || !binding.path.startsWith("/") || binding.path === "/" || resources.has(binding.path)) fail3();
|
|
7076
|
+
const resource = createModelResourceRef(binding.resource);
|
|
7077
|
+
const identity = JSON.stringify(resource);
|
|
7078
|
+
if (identities.has(resource.uri) && identities.get(resource.uri) !== identity) fail3();
|
|
7079
|
+
identities.set(resource.uri, identity);
|
|
7080
|
+
resources.set(binding.path, resource);
|
|
7081
|
+
}
|
|
7082
|
+
check();
|
|
7083
|
+
const interrupted = new Promise((_resolve, reject) => {
|
|
7084
|
+
timer = setTimeout(() => reject(new ModelConversionDiagnosticsError("deadline-exceeded")), Math.max(1, timeoutMs - (performance.now() - start)));
|
|
7085
|
+
onAbort = () => reject(new ModelConversionDiagnosticsError("aborted"));
|
|
7086
|
+
if (signal) AbortSignal.prototype.addEventListener.call(signal, "abort", onAbort, { once: true });
|
|
7087
|
+
});
|
|
7088
|
+
const core = await Promise.race([import("@plasius/gpu-model-core"), interrupted]);
|
|
7089
|
+
check();
|
|
7090
|
+
const report = core.evaluateGpuModelDiagnostics(reportInput);
|
|
7091
|
+
const diagnostics = report.diagnostics.map((diagnostic) => {
|
|
7092
|
+
const resource = resources.get(diagnostic.path);
|
|
7093
|
+
if (diagnostic.path !== "/" && !resource) fail3("unbound-resource");
|
|
7094
|
+
const base = resource ? { resource } : {};
|
|
7095
|
+
switch (diagnostic.kind) {
|
|
7096
|
+
case "warning":
|
|
7097
|
+
return Object.freeze({ ...base, severity: "warning", code: "conversion.warning", message: "Conversion completed with an advisory." });
|
|
7098
|
+
case "repair":
|
|
7099
|
+
return Object.freeze({ ...base, severity: "warning", code: "conversion.repair", message: "Conversion applied a canonical repair fallback.", fallback: diagnostic.ruleId });
|
|
7100
|
+
case "loss":
|
|
7101
|
+
return Object.freeze({ ...base, severity: reportInput.allowLoss === true ? "warning" : "blocking", code: `conversion.loss.${diagnostic.effect}`, message: diagnostic.effect === "dropped" ? "Conversion dropped source data." : "Conversion approximated source data.", effect: diagnostic.effect });
|
|
7102
|
+
case "unsupported":
|
|
7103
|
+
return Object.freeze({ ...base, severity: "blocking", code: "conversion.unsupported", message: "Conversion encountered unsupported data." });
|
|
7104
|
+
case "blocking-error":
|
|
7105
|
+
return Object.freeze({ ...base, severity: "blocking", code: "conversion.blocked", message: "Conversion did not satisfy canonical validation policy." });
|
|
7106
|
+
}
|
|
7107
|
+
});
|
|
7108
|
+
if (args.outcome !== "completed") diagnostics.push(Object.freeze({ severity: "blocking", code: args.outcome === "failed" ? "conversion.failed" : "conversion.unsupported", message: args.outcome === "failed" ? "Conversion failed." : "Conversion encountered unsupported data." }));
|
|
7109
|
+
const technicalAccepted = args.outcome === "completed" && report.accepted;
|
|
7110
|
+
const status = args.outcome === "failed" ? "failed" : args.outcome === "unsupported" || report.diagnostics.some((item) => item.kind === "unsupported") ? "unsupported" : !technicalAccepted ? "failed" : diagnostics.length ? "warning" : "succeeded";
|
|
7111
|
+
const losses = diagnostics.filter((item) => item.effect !== void 0).map(legacy);
|
|
7112
|
+
check();
|
|
7113
|
+
return Object.freeze({
|
|
7114
|
+
contractVersion: 1,
|
|
7115
|
+
status,
|
|
7116
|
+
technicalAccepted,
|
|
7117
|
+
standard: report.standard,
|
|
7118
|
+
mode: report.mode,
|
|
7119
|
+
source,
|
|
7120
|
+
diagnostics: Object.freeze(diagnostics),
|
|
7121
|
+
converter: Object.freeze({ diagnostics: Object.freeze(diagnostics.filter((item) => item.effect === void 0).map(legacy)), losses: Object.freeze(losses) })
|
|
7122
|
+
});
|
|
7123
|
+
} catch (error) {
|
|
7124
|
+
if (error instanceof ModelConversionDiagnosticsError) throw error;
|
|
7125
|
+
return fail3();
|
|
7126
|
+
} finally {
|
|
7127
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
7128
|
+
if (signal && onAbort) AbortSignal.prototype.removeEventListener.call(signal, "abort", onAbort);
|
|
7129
|
+
}
|
|
7130
|
+
}
|
|
7131
|
+
|
|
6815
7132
|
// src/index.ts
|
|
6816
7133
|
var ASSET_CONTRACTS_PACKAGE = "@plasius/asset-contracts";
|
|
6817
7134
|
var UNIFIED_ASSET_PIPELINE_FEATURE_FLAG_ID = "asset.pipeline.unified-ai-assets.enabled";
|
|
@@ -7599,6 +7916,7 @@ export {
|
|
|
7599
7916
|
MODEL_CANDIDATE_HARD_GATE_KINDS,
|
|
7600
7917
|
MODEL_CONFIRMATION_VIEW_KINDS,
|
|
7601
7918
|
MODEL_CONFIRMATION_VIEW_SIZE_PX,
|
|
7919
|
+
MODEL_CONVERSION_DIAGNOSTIC_LIMITS,
|
|
7602
7920
|
MODEL_GENERATOR_DISABLED_REASON_CODE,
|
|
7603
7921
|
MODEL_MATCH_ASSURANCE_BANDS,
|
|
7604
7922
|
MODEL_MATCH_ASSURANCE_THRESHOLDS,
|
|
@@ -7614,8 +7932,11 @@ export {
|
|
|
7614
7932
|
MODEL_RESOLUTION_V2_STATES,
|
|
7615
7933
|
MODEL_RESOURCE_MANIFEST_LIMITS,
|
|
7616
7934
|
MODEL_RIGHTS_STATUSES,
|
|
7935
|
+
MODEL_TEXTURE_RESOURCE_LIMITS,
|
|
7617
7936
|
MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE,
|
|
7937
|
+
ModelConversionDiagnosticsError,
|
|
7618
7938
|
ModelResourceManifestError,
|
|
7939
|
+
ModelTextureResourceError,
|
|
7619
7940
|
PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN,
|
|
7620
7941
|
PVOX_ASSET_MANIFEST_VERSION,
|
|
7621
7942
|
PVOX_BINARY_CLOSURE_HASH_DOMAIN,
|
|
@@ -7806,7 +8127,9 @@ export {
|
|
|
7806
8127
|
normalizeAssetId,
|
|
7807
8128
|
normalizePvoxRootHeaderForHashV1,
|
|
7808
8129
|
projectPvoxNamedJsonClosureV1,
|
|
8130
|
+
qualifyModelConversionDiagnostics,
|
|
7809
8131
|
qualifyModelResourceManifest,
|
|
8132
|
+
qualifyModelTextureResources,
|
|
7810
8133
|
toLegacyModelResolutionState,
|
|
7811
8134
|
validateGpuAssetFiles
|
|
7812
8135
|
};
|