import { AssetGuid } from '@forgeax/engine-pack/guid'; import { type Result, type RhiError } from '@forgeax/engine-rhi'; import { type Asset, type AssetCompression, AssetError, type AssetRef, type CatalogSubject, type ImageError, type ImageMetadata, type LoadContext, type ParseErrorDetail } from '@forgeax/engine-types'; import type { AssetRegistry } from '../asset-registry'; import { type MaterialLoadError, type MaterialLoadRequest, type MaterialReady } from '../material/loader'; import { type CatalogRecord } from './catalog'; /** * Load an asset and all its transitively referenced sub-assets by GUID; * returns `ok(handle)` only when the asset and every sub-asset are in the * registry. * * **Post-condition:** `ok(payload)` is returned ONLY when the asset AND every * transitively referenced sub-asset (per the asset envelope's `refs[]`) are * present in this registry. The implementation walks `envelope.refs` and * recursively calls `loadByGuid` on each ref before cataloguing the top-level * asset. The resolved value is the PAYLOAD `T` * (D-17), never a handle -- mint a column handle with * `world.allocSharedRef('Kind', payload)` when one is needed (e.g. before * `instantiate`). * * Two paths: * - **Dev / fallback** (no `configurePackIndex` call): synchronous catalogue * lookup wrapped in `Promise.resolve`. Returns `Err(asset-not-found)` if not * catalogued. * - **Prod** (after `configurePackIndex(url)`): fetches `pack-index.json` * on the first call (cached as a `Map`), then * fetches the individual resource URL and parses the asset payload, then * catalogues it (GUID -> payload) and returns the payload. * * Error union: `AssetError | PackError | ImageError | RhiError` (closed -- no * new codes were introduced by the recursive walk; every code is pre-existing). * * An in-flight `Map` (D-5) deduplicates concurrent calls for the same GUID and * prevents stack overflow on cycles (A->B->A). * * **Breaking-change classification:** this is a semantic strengthening, not a * shape change. Sub-assets catalogued by a prior `catalog(guid, payload)` / * `loadByGuid` call are protected by the catalogue fast-path: the recursive * walk hits cache on every node and incurs zero additional fetch. * * @example * ```ts * const res = await engine.assets.loadByGuid(sceneGuid); * if (!res.ok) { * switch (res.error.code) { * case 'asset-not-found': * // top GUID or any sub-asset GUID is missing from the catalog * break; * case 'asset-fetch-failed': * // network / CORS * break; * case 'asset-parse-failed': * // payload malformed * break; * // ... AssetErrorCode | PackErrorCode | ImageErrorCode | RhiErrorCode exhaustive * } * return; * } * ``` */ export declare function loadByGuid(registry: AssetRegistry, guid: AssetGuid, parentContext?: { sceneEntityKey?: string; componentField?: string; }): Promise>; /** Reuse the accepted Catalog projection; no second dependency graph for the legacy reader. */ export declare function validateRegistryReferences(registry: AssetRegistry, guid: string): Result; export declare function loadMaterialReadyByGuid(registry: AssetRegistry, request: MaterialLoadRequest): Promise; /** * Internal: prod fetch path for `loadByGuid`. * Fetches pack-index.json (cached), then fetches the pack file, parses the * asset payload, and registers it. */ export declare function loadByGuidProd(registry: AssetRegistry, guid: AssetGuid, guidKey: string, parentContext?: { sceneEntityKey?: string; componentField?: string; }, ancestry?: ReadonlySet, isCurrent?: () => boolean): Promise>; /** * Resolve the catalog entry for a GUID, lazily fetching the pack-index on * first call. A successful miss is `ok(undefined)`; admission failures retain * their original error and never become an absent GUID or import fallback. */ export declare function resolveCatalogEntry(registry: AssetRegistry, guidKey: string): Promise>; /** * feat-20260618 M3 (D-2): once the pack-index is parsed, group every row by * its `packageUrl` and register each package fully -- all of its GUIDs and * their entry display names in one `registerPackage` call. Registering the * whole package at once (rather than one GUID per load) means the package * cardinality is known up front, so `resolveName` preserves an explicit entry * name for both single- and multi-asset packages and uses the package basename * only as a fallback. There is no incremental 1->N promotion needed on the * prod path. The name travels entry -> Package, never through the payload * (Risk-3 JSON-roundtrip safety), and covers both the sync (parseAssetPayload) * and async (texture/font) loads. */ export declare function registerPackagesFromIndex(registry: AssetRegistry, catalog: Map): void; /** * Load an asset through the DDC (catalog entry -> fetch pack -> loader.load * -> register). Returns `Err(asset-not-found)` or `Err(asset-fetch-failed)` * on DDC miss (the caller then decides whether to route through the * import transport). * * This path is IDENTICAL in studio and shipped forms -- the only difference * between the two is whether `registry.importTransport` exists when the caller * falls back to `transportOrFail` (AC-23 key invariant). */ export declare function ddcLoad(registry: AssetRegistry, guid: AssetGuid, guidKey: string, entry: { packageUrl: string; kind: string; name?: string; metadata?: ImageMetadata | undefined; compression?: AssetCompression; }, parentContext?: { sceneEntityKey?: string; componentField?: string; }, ancestry?: ReadonlySet, isCurrent?: () => boolean): Promise>; /** * M4 transport fallback: try the injected {@link ImportTransport} to lazily * import a missing DDC, then re-enter the DDC load path. When no transport * is wired (shipped form), fail fast with `asset-not-imported` (AC-22). */ export declare function transportOrFail(registry: AssetRegistry, guid: AssetGuid, guidKey: string, miss: AssetError | undefined, isCurrent?: () => boolean): Promise>; /** * Register a parsed asset POD (the synchronous tail of the DDC load path: * `registerWithGuid`). Material parent preload is handled asynchronously * inside `ddcLoad` before calling this method; the registered asset is * always fully resolved by the time it reaches here. * * Extracted from the old `loadByGuidProd` body so `ddcLoad` and * `transportOrFail` share an identical load path (AC-23 key invariant). */ export declare function registerParsedAsset(registry: AssetRegistry, guid: AssetGuid, asset: Asset, _guidKey: string, refs?: readonly AssetRef[]): Result; /** * Fetch one pack file, locate the requested asset entry, and either parse it * inline or expose the entry to the caller (for kinds that need to preload * `refs[]` BEFORE running the loader โ€” currently 'material', whose * values handle fields are rewritten to their refs[] GUID strings * (feat-20260614 M8 / D-19: GUID verbatim, no handle minting at load time)). * * bug-20260610 Fix B (M3 / D-4): the fetch+parse result is cached per * `packageUrl` in `packFiles`; concurrent calls for the same URL share * a single in-flight promise via `packFiles`. Only the raw parsed * body is cached โ€” `parseAssetPayload` still runs per-call (CON-2). */ export declare function fetchPackFile(registry: AssetRegistry, packageUrl: string, guidKey: string, _kind: string): Promise>; /** * Parse the asset payload from a pack-file entry and return the result. * Extracted so cache-hit and in-flight-dedup paths share the same * parseAssetPayload + error-wrapping logic. */ export declare function parseAndReturnAsset(registry: AssetRegistry, assetEntry: { kind: string; payload: Record; refs?: string[]; }): Result<{ asset: Asset; refs: readonly string[]; }, AssetError>; /** * Fetch a pack file from the network, parse the JSON body, store the * result in the cache, and return the requested asset entry. * * Registers the in-flight promise in `packFiles` so concurrent * callers share a single fetch. On success the body moves to * `packFiles`; on failure the in-flight entry is removed so * subsequent retries re-fetch (D-7). */ export declare function fetchAndCachePackFile(registry: AssetRegistry, packageUrl: string, guidKey: string, fetcher?: typeof globalThis.fetch, isCurrent?: () => boolean): Promise>; /** * Reconstruct a typed `Asset` from a raw payload object. * * @param kind The asset kind discriminant (matches the pack entry or * dev-register dispatch). * @param payload The serialised asset payload (keys mirror the asset * interface field names). * @param refs Pack-file refs array for Handle fields โ€” when a field * value is `number` it resolves to `refs[N]` (glTF-style index). * Optional to preserve compatibility with callers outside the pack * ingestion path (e.g., direct `registerWithGuid`). */ export declare function parseAssetPayload(registry: AssetRegistry, kind: string, payload: Record, refs?: string[]): Asset | Record | undefined | { readonly ok: false; readonly error: ParseErrorDetail; }; /** * Build the {@link LoadContext} passed to a loader's `load`. * `fetchBinary` / `resolveRef` / `device` are wired for the async texture / * font loaders (w6). */ export declare function makeLoadContext(registry: AssetRegistry, fetcher?: typeof globalThis.fetch): LoadContext; /** * Return a runtime snapshot of every catalogued asset. Each entry exposes * `{ guid, kind, name }` where `kind` is the asset discriminant string * from `payload.kind`. feat-20260614 M8 (D-15): the registry holds no * handles -- entries are keyed by GUID (the catalogue key). * * AI-user narrowing flow (AC-11 + plan-strategy ยง7.4): * ```ts * for (const e of registry.inspect().assets) { * if (e.kind === 'texture') { * // re-query via registry.lookup(e.guid) to get the typed Asset value. * } * } * ``` */ //# sourceMappingURL=load-by-guid.d.ts.map