@plasius/gpu-model-core 0.2.0 → 0.4.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/README.md +214 -0
- package/dist/index.cjs +786 -221
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +280 -1
- package/dist/index.d.ts +280 -1
- package/dist/index.js +776 -221
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,6 +69,72 @@ UI capability. Synthetic contract fixtures cover glTF, OBJ, FBX and CAD reports;
|
|
|
69
69
|
actual parsing and geometry verification belong to their format adapters.
|
|
70
70
|
See [ADR-0008](docs/adrs/adr-0008-bounded-adapter-diagnostics-and-repair-ledgers.md).
|
|
71
71
|
|
|
72
|
+
## Canonical conversion resource graph
|
|
73
|
+
|
|
74
|
+
`createGpuModelResourceGraph` projects a privately verified `GpuModelDocument`
|
|
75
|
+
into a frozen dependency graph for resources, accessors, textures, materials,
|
|
76
|
+
skeletons (including joints), skins, animations, provenance and conversion
|
|
77
|
+
packages. It reuses the canonical values and immutable `Blob` payloads. Material
|
|
78
|
+
bindings retain samplers and intended usage; texture values retain their hashes,
|
|
79
|
+
MIME types, paths, colour spaces and export embedding policy.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { createGpuModelResourceGraph, gpuModelResourceKey,
|
|
83
|
+
type GpuModelDocument } from "@plasius/gpu-model-core";
|
|
84
|
+
|
|
85
|
+
declare const document: GpuModelDocument; // From the verification factory.
|
|
86
|
+
const graph = createGpuModelResourceGraph(document, {
|
|
87
|
+
resources: document.resources.map(resource => ({
|
|
88
|
+
resourceId: resource.id,
|
|
89
|
+
location: { kind: "embedded" },
|
|
90
|
+
exportEmbeddingPolicy: "inherit-source",
|
|
91
|
+
})),
|
|
92
|
+
packages: [{ id: "conversion", members: document.resources.map(resource => ({
|
|
93
|
+
kind: "resource", id: resource.id,
|
|
94
|
+
})) }],
|
|
95
|
+
});
|
|
96
|
+
const conversion = graph.nodes.find(node =>
|
|
97
|
+
node.key === gpuModelResourceKey("package", "conversion"));
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Supply exactly one binding per document resource. For external origins use
|
|
101
|
+
`location: { kind: "external", relativePath: "textures/albedo.png" }`; the bytes
|
|
102
|
+
must already have been resolved and verified. Paths use portable ASCII letters,
|
|
103
|
+
digits, dots, underscores, hyphens and slash separators, with no absolute paths,
|
|
104
|
+
traversal, URL syntax, device names or trailing dots. Components are at most 255
|
|
105
|
+
characters and paths at most 1024. Case-insensitive duplicate paths and file versus
|
|
106
|
+
directory collisions fail. Descriptors do not authorize network/file access.
|
|
107
|
+
|
|
108
|
+
Resource export policies are `inherit-source`, `prefer-embedded`,
|
|
109
|
+
`prefer-external` and `forbid-embedding`. The last requires an external origin.
|
|
110
|
+
Exporters must satisfy resource and texture restrictions together, resolve
|
|
111
|
+
preferences for the target, enforce destination-root confinement and validate
|
|
112
|
+
their actual output. The graph neither writes packages nor proves export success.
|
|
113
|
+
Asset catalogue admission and promotion remain owned by `@plasius/asset-contracts`.
|
|
114
|
+
|
|
115
|
+
`gpuModelResourceKey(kind, id)` encodes a JSON tuple, independent of declaration
|
|
116
|
+
order and locale. Logical IDs are distinct from byte-content hashes. Nodes and
|
|
117
|
+
dependency keys are sorted; joint/animation sample order stays intact. Package
|
|
118
|
+
members are typed references, may include packages, and must form a DAG. Missing,
|
|
119
|
+
repeated or cyclic references fail with fixed `GpuModelResourceGraphError.code`
|
|
120
|
+
values. Canonical document validation still owns scene and semantic references.
|
|
121
|
+
Animation target associations remain in canonical values, outside dependency
|
|
122
|
+
edges. This is a resource dependency graph, not a replacement scene graph.
|
|
123
|
+
|
|
124
|
+
`GPU_MODEL_RESOURCE_GRAPH_LIMITS` caps 65536 nodes, 262144 dependency declarations
|
|
125
|
+
(including repeated semantic dependencies before deduplication), and 256 packages.
|
|
126
|
+
These ceilings may reject a large otherwise-valid canonical document. Construction
|
|
127
|
+
performs no I/O, retries, logging or hashing; input accessors and malformed
|
|
128
|
+
containers fail closed. Do not treat executable JavaScript Proxies as inert input.
|
|
129
|
+
`isGpuModelResourceGraph` recognizes only locally constructed graphs. Structured
|
|
130
|
+
clones must be rebuilt from a reverified document. The graph retains authored
|
|
131
|
+
metadata and provenance: it is not a redaction boundary or a public logging format.
|
|
132
|
+
|
|
133
|
+
Adoption inherits the remotely evaluated `gpu.model.conversion.enabled` flag;
|
|
134
|
+
rollback disables conversion adoption and pins the prior package release. No
|
|
135
|
+
stored flag or UI capability changes here. See
|
|
136
|
+
[ADR-0009](docs/adrs/adr-0009-canonical-conversion-resource-graph.md).
|
|
137
|
+
|
|
72
138
|
## Implementation tracker
|
|
73
139
|
|
|
74
140
|
The canonical conversion architecture is defined by [ADR 0094 in `plasius-ltd-site`](https://github.com/Plasius-LTD/plasius-ltd-site/blob/main/docs/adrs/adr-0094-gpu-model-family-and-canonical-proxy-conversion.md). The package implementation work is split into Project-tracked Tasks:
|
|
@@ -248,3 +314,151 @@ repository auto-merge is unavailable. Version `0.1.0` may use the explicit,
|
|
|
248
314
|
time-limited `bootstrap_first_publish` production gate only while the package is absent;
|
|
249
315
|
that credential is removed after the npm trusted publisher binding is active.
|
|
250
316
|
<!-- END PLASIUS RELEASE INTEGRITY -->
|
|
317
|
+
|
|
318
|
+
## Adapter contracts and canonical conversion
|
|
319
|
+
|
|
320
|
+
`ModelAdapter` exposes `metadata`, `inspect`, `load`, `validate` and `export`.
|
|
321
|
+
Register one explicit lowercase format ID per adapter using
|
|
322
|
+
`createModelConversionRegistry`. Core has no concrete format parsers, IO
|
|
323
|
+
resolvers, renderer dependencies, format sniffing, discovery or caches. Those
|
|
324
|
+
belong to the dedicated format/runtime packages; inject adapters after a runtime
|
|
325
|
+
has checked the remote `gpu.model.conversion.enabled` flag. Disable that flag
|
|
326
|
+
and pin the prior public version to roll back adoption.
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
import {
|
|
330
|
+
createModelConversionRegistry, type ModelAdapter,
|
|
331
|
+
type ModelSource, type ModelTarget,
|
|
332
|
+
} from "@plasius/gpu-model-core";
|
|
333
|
+
|
|
334
|
+
// Applications supply installed, conformance-tested adapters.
|
|
335
|
+
async function convert(sourceAdapter: ModelAdapter, targetAdapter: ModelAdapter) {
|
|
336
|
+
const registry = createModelConversionRegistry([sourceAdapter, targetAdapter]);
|
|
337
|
+
const source: ModelSource = {
|
|
338
|
+
kind: "uint8-array", bytes: new Uint8Array([1, 2, 3]),
|
|
339
|
+
fileNameHint: "synthetic.input",
|
|
340
|
+
};
|
|
341
|
+
const target: ModelTarget = { kind: "array-buffer" };
|
|
342
|
+
const result = await registry.convert({
|
|
343
|
+
sourceFormat: sourceAdapter.metadata.formatId,
|
|
344
|
+
targetFormat: targetAdapter.metadata.formatId,
|
|
345
|
+
source, target,
|
|
346
|
+
}, { mode: "strict", allowLoss: false, timeoutMs: 30_000 });
|
|
347
|
+
if (!result.accepted) return result.diagnostics;
|
|
348
|
+
return result.exported;
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Conversion calls source `load`, checks the privately verified canonical document,
|
|
353
|
+
records target capability losses, calls target `validate(document)`, and finally
|
|
354
|
+
calls target `export`. Same-format conversion follows the same route. It never
|
|
355
|
+
calls `inspect` automatically, so a single-use stream is not consumed twice.
|
|
356
|
+
`registry.inspect`, `load`, `validate` and `export` also enforce the boundary
|
|
357
|
+
individually; direct export includes canonical capability/validation preflight.
|
|
358
|
+
Unsupported formats or IO kinds are rejected before adapter work. There is no
|
|
359
|
+
public direct-format converter hook, and extra registration keys are rejected.
|
|
360
|
+
Use plain adapter objects with the four operations as own methods.
|
|
361
|
+
|
|
362
|
+
The following are the supported declarative forms. IO hints remain optional;
|
|
363
|
+
paths, URLs and stream handles confer no authorization from core. The injected
|
|
364
|
+
runtime must enforce filesystem roots, network allowlists, size limits while
|
|
365
|
+
streaming, credentials, output overwrite policy, and atomic staging/commit.
|
|
366
|
+
Never log headers or signed URLs; scrub adapter diagnostic/evidence text before
|
|
367
|
+
calling the result factory. Core produces fixed error codes with no input or
|
|
368
|
+
exception causes, but it cannot identify secrets embedded in arbitrary adapter
|
|
369
|
+
messages.
|
|
370
|
+
|
|
371
|
+
```ts
|
|
372
|
+
import type { ModelSource, ModelTarget } from "@plasius/gpu-model-core";
|
|
373
|
+
|
|
374
|
+
const sources: ModelSource[] = [
|
|
375
|
+
{ kind: "file-path", path: "/models/synthetic.input" },
|
|
376
|
+
{ kind: "url", url: "https://example.invalid/model", headers: { Accept: "application/octet-stream" } },
|
|
377
|
+
{ kind: "blob-storage-url", url: "https://example.invalid/model", credentialMode: "runtime-resolved" },
|
|
378
|
+
{ kind: "blob", blob: new Blob(["synthetic"]), fileNameHint: "synthetic.input" },
|
|
379
|
+
{ kind: "array-buffer", bytes: new ArrayBuffer(4) },
|
|
380
|
+
{ kind: "uint8-array", bytes: new Uint8Array(4), mimeTypeHint: "application/octet-stream" },
|
|
381
|
+
{ kind: "stream", stream: new ReadableStream<Uint8Array>(), byteLengthHint: 4 },
|
|
382
|
+
];
|
|
383
|
+
const targets: ModelTarget[] = [
|
|
384
|
+
{ kind: "file-path", path: "/exports/synthetic.output", overwrite: false },
|
|
385
|
+
{ kind: "blob-storage-url", url: "https://example.invalid/output", contentType: "application/octet-stream", metadata: { purpose: "synthetic" } },
|
|
386
|
+
{ kind: "blob", mimeType: "application/octet-stream", fileName: "synthetic.output" },
|
|
387
|
+
{ kind: "array-buffer" },
|
|
388
|
+
{ kind: "uint8-array" },
|
|
389
|
+
{ kind: "stream", stream: new WritableStream<Uint8Array>() },
|
|
390
|
+
{ kind: "package", packageFormat: "zip", destinationHint: "synthetic.zip", includeSourcePayload: false },
|
|
391
|
+
];
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
HTTP(S) URLs with inline username/password are rejected. Storage descriptors
|
|
395
|
+
support anonymous, signed and runtime-resolved credential modes; values are
|
|
396
|
+
passed only to the injected adapter. Browser Web streams and Node
|
|
397
|
+
`AsyncIterable<Uint8Array>` readable / structural `write`+`end` writable streams
|
|
398
|
+
are supported without importing Node types into the public declarations.
|
|
399
|
+
Memory input bytes are copied; Blob values are immutable, and streams remain
|
|
400
|
+
opaque capabilities. Direct memory descriptors are capped at 64 MiB. Larger
|
|
401
|
+
models should use streams with runtime-enforced budgets and worker isolation.
|
|
402
|
+
|
|
403
|
+
Adapters construct their shared envelope with `createModelResultBase`:
|
|
404
|
+
|
|
405
|
+
```ts
|
|
406
|
+
import { createModelResultBase, type ModelOperationOptions } from "@plasius/gpu-model-core";
|
|
407
|
+
|
|
408
|
+
function completedEvidence(options: ModelOperationOptions = {}) {
|
|
409
|
+
const mode = options.mode ?? "strict";
|
|
410
|
+
return createModelResultBase({
|
|
411
|
+
standard: "gltf-glb", // Use the adapter's actual diagnostic profile.
|
|
412
|
+
mode, allowLoss: options.allowLoss ?? false, issues: [],
|
|
413
|
+
...(mode === "forensic" ? { rawSource: { synthetic: true } } : {}),
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
// load: { ...completedEvidence(options), document: verifiedDocument }
|
|
417
|
+
// inspect: { ...completedEvidence(options), capabilities: metadata, detectedFormat: metadata.formatId }
|
|
418
|
+
// validate: { ...completedEvidence(options), valid: true }
|
|
419
|
+
// export: { ...completedEvidence(options), target: target.kind, output: optionalMemoryPayload }
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
The factory reuses `evaluateGpuModelDiagnostics`; its immutable report is privately
|
|
423
|
+
recorded. Spread the returned base unchanged into an operation result. Structural
|
|
424
|
+
clones and invented report/projection arrays are rejected. `warnings` projects
|
|
425
|
+
warning-severity diagnostics, `repairs` retains completed repair evidence, and
|
|
426
|
+
`lossReports` retains dropped/approximated fidelity. The registry checks the
|
|
427
|
+
caller's mode and loss consent independently, so an adapter cannot grant itself
|
|
428
|
+
permission. Unknown/unsupported semantics remain blocking in all modes.
|
|
429
|
+
|
|
430
|
+
`ModelLoadResult.document` is optional only for refused operations; accepted
|
|
431
|
+
loads require a privately verified document from this core module instance.
|
|
432
|
+
`ModelExportResult.output` optionally carries Blob, ArrayBuffer or Uint8Array
|
|
433
|
+
matching the selected memory target. A `ResourcePackageManifest` describes
|
|
434
|
+
portable relative paths, roles and content types; duplicate identities/paths and
|
|
435
|
+
unsafe paths are rejected. It is not proof of output bytes or promotion.
|
|
436
|
+
Canonical resource identity remains in `GpuModelDocument` and the separately
|
|
437
|
+
available resource graph. Cross-worker/module clones must be reverified and
|
|
438
|
+
reports rebuilt before registry use.
|
|
439
|
+
|
|
440
|
+
Registry results retain per-stage diagnostic reports, preserving different source
|
|
441
|
+
and target profiles, plus aggregate diagnostic/repair/loss arrays. The synthetic
|
|
442
|
+
`capabilities` stage uses the core `gltf-glb` report profile solely as a container
|
|
443
|
+
for format-neutral loss records; it applies no format-specific repair rules.
|
|
444
|
+
It reports animation, rig/skin and analytic-geometry downgrade when the target's
|
|
445
|
+
capability flags cannot preserve those values. Other prospective losses must be
|
|
446
|
+
reported by target validation. Adapters must stage writes and reject unconsented
|
|
447
|
+
loss **before** committing; core cannot undo an injected adapter's side effects.
|
|
448
|
+
Always check `accepted`, including after export, and do not equate a manifest or
|
|
449
|
+
adapter result with independently verified publication.
|
|
450
|
+
|
|
451
|
+
Each registry allows at most 128 adapters and eight concurrent operations
|
|
452
|
+
(configurable downward with `maxConcurrent`). Each operation or whole conversion
|
|
453
|
+
has a 30-second default deadline, configurable up to five minutes, and supports
|
|
454
|
+
`AbortSignal`. No retries occur. Cancellation and timeouts stop subsequent stages;
|
|
455
|
+
an unresponsive operation keeps its capacity slot until the underlying promise
|
|
456
|
+
settles. Synchronous parser code cannot be preempted, so isolate untrusted code
|
|
457
|
+
in runtime workers. Descriptor/report/manifest limits bound core work; raw
|
|
458
|
+
JavaScript Proxies and injected adapter/stream implementations are trusted code,
|
|
459
|
+
not a sandbox boundary.
|
|
460
|
+
|
|
461
|
+
The IO examples are covered by `tests/adapter-registry.test.ts`; all TypeScript
|
|
462
|
+
blocks in this section are additionally compiled against the built public
|
|
463
|
+
package during release verification. See [ADR-0010](docs/adrs/adr-0010-canonical-adapter-registry.md)
|
|
464
|
+
for compatibility with the initial site design and package ownership boundaries.
|