@aura3d/asset-index 1.0.5
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/dist/CanonicalAsset.d.ts +79 -0
- package/dist/CanonicalAsset.d.ts.map +1 -0
- package/dist/CanonicalAsset.js +50 -0
- package/dist/CanonicalAsset.js.map +1 -0
- package/dist/IndexStore.d.ts +67 -0
- package/dist/IndexStore.d.ts.map +1 -0
- package/dist/IndexStore.js +112 -0
- package/dist/IndexStore.js.map +1 -0
- package/dist/SourceAdapter.d.ts +54 -0
- package/dist/SourceAdapter.d.ts.map +1 -0
- package/dist/SourceAdapter.js +9 -0
- package/dist/SourceAdapter.js.map +1 -0
- package/dist/adapters/aura-index.d.ts +11 -0
- package/dist/adapters/aura-index.d.ts.map +1 -0
- package/dist/adapters/aura-index.js +64 -0
- package/dist/adapters/aura-index.js.map +1 -0
- package/dist/adapters/jsdelivr-mirror.d.ts +7 -0
- package/dist/adapters/jsdelivr-mirror.d.ts.map +1 -0
- package/dist/adapters/jsdelivr-mirror.js +80 -0
- package/dist/adapters/jsdelivr-mirror.js.map +1 -0
- package/dist/adapters/khronos.d.ts +3 -0
- package/dist/adapters/khronos.d.ts.map +1 -0
- package/dist/adapters/khronos.js +78 -0
- package/dist/adapters/khronos.js.map +1 -0
- package/dist/adapters/marketplace.d.ts +3 -0
- package/dist/adapters/marketplace.d.ts.map +1 -0
- package/dist/adapters/marketplace.js +70 -0
- package/dist/adapters/marketplace.js.map +1 -0
- package/dist/adapters/os3a.d.ts +3 -0
- package/dist/adapters/os3a.d.ts.map +1 -0
- package/dist/adapters/os3a.js +110 -0
- package/dist/adapters/os3a.js.map +1 -0
- package/dist/adapters/poly-haven.d.ts +3 -0
- package/dist/adapters/poly-haven.d.ts.map +1 -0
- package/dist/adapters/poly-haven.js +110 -0
- package/dist/adapters/poly-haven.js.map +1 -0
- package/dist/adapters/poly-pizza.d.ts +35 -0
- package/dist/adapters/poly-pizza.d.ts.map +1 -0
- package/dist/adapters/poly-pizza.js +109 -0
- package/dist/adapters/poly-pizza.js.map +1 -0
- package/dist/adapters/sketchfab.d.ts +20 -0
- package/dist/adapters/sketchfab.d.ts.map +1 -0
- package/dist/adapters/sketchfab.js +183 -0
- package/dist/adapters/sketchfab.js.map +1 -0
- package/dist/embedding.d.ts +56 -0
- package/dist/embedding.d.ts.map +1 -0
- package/dist/embedding.js +121 -0
- package/dist/embedding.js.map +1 -0
- package/dist/federate.d.ts +56 -0
- package/dist/federate.d.ts.map +1 -0
- package/dist/federate.js +80 -0
- package/dist/federate.js.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/ranking.d.ts +6 -0
- package/dist/ranking.d.ts.map +1 -0
- package/dist/ranking.js +71 -0
- package/dist/ranking.js.map +1 -0
- package/dist/refresh.d.ts +54 -0
- package/dist/refresh.d.ts.map +1 -0
- package/dist/refresh.js +48 -0
- package/dist/refresh.js.map +1 -0
- package/package.json +19 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical, source-agnostic asset record.
|
|
3
|
+
*
|
|
4
|
+
* Every source adapter (Khronos, OS3A, Poly Pizza, Sketchfab, ...) normalizes
|
|
5
|
+
* its native records into this one shape so the federation/ranking layer never
|
|
6
|
+
* has to know which source an asset came from.
|
|
7
|
+
*
|
|
8
|
+
* Design rule that mirrors the rest of Aura3D: this index RECORDS license; it
|
|
9
|
+
* never guesses one. An asset whose license could not be verified is marked
|
|
10
|
+
* `UNVERIFIED` and is NOT auto-pullable until a verification pass resolves it.
|
|
11
|
+
*/
|
|
12
|
+
/** Spec-style license identifiers we normalize source license strings into. */
|
|
13
|
+
export type AuraAssetLicenseSpdx = "CC0-1.0" | "CC-BY-4.0" | "CC-BY-3.0" | "UNVERIFIED";
|
|
14
|
+
export interface AuraAssetLicense {
|
|
15
|
+
/** Normalized SPDX-style id, or `UNVERIFIED` when the source did not state one. */
|
|
16
|
+
readonly spdx: AuraAssetLicenseSpdx;
|
|
17
|
+
/** Raw license string as it appeared in the source, for provenance/debugging. */
|
|
18
|
+
readonly raw: string;
|
|
19
|
+
/** True only when the license came from an authoritative field in the source. */
|
|
20
|
+
readonly verified: boolean;
|
|
21
|
+
/** True when downstream use must carry attribution (CC-BY family). */
|
|
22
|
+
readonly attributionRequired: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* True when Aura may auto-pull and copy the file into a user project.
|
|
25
|
+
* CC0 and CC-BY qualify (CC-BY also sets `attributionRequired`).
|
|
26
|
+
* `UNVERIFIED` and marketplace deep-links do NOT qualify.
|
|
27
|
+
*/
|
|
28
|
+
readonly redistributable: boolean;
|
|
29
|
+
/** Human-checkable page where the license can be confirmed. */
|
|
30
|
+
readonly sourcePage?: string;
|
|
31
|
+
}
|
|
32
|
+
export type AuraAssetFormat = "glb" | "gltf";
|
|
33
|
+
/** Whether the file can be fetched directly or is a discovery deep-link only. */
|
|
34
|
+
export type AuraAssetAccess = "direct-download" | "deep-link-only";
|
|
35
|
+
export interface AuraAssetBounds {
|
|
36
|
+
readonly size: readonly [number, number, number];
|
|
37
|
+
}
|
|
38
|
+
export interface AuraCanonicalAsset {
|
|
39
|
+
/** Globally unique, namespaced id: `${source}:${sourceLocalId}`. */
|
|
40
|
+
readonly id: string;
|
|
41
|
+
/** Adapter id that produced this record (e.g. `"khronos"`, `"os3a"`). */
|
|
42
|
+
readonly source: string;
|
|
43
|
+
readonly title: string;
|
|
44
|
+
readonly description?: string;
|
|
45
|
+
/** Direct file URL, or the deep-link page when access is `deep-link-only`. */
|
|
46
|
+
readonly url: string;
|
|
47
|
+
readonly access: AuraAssetAccess;
|
|
48
|
+
readonly format: AuraAssetFormat;
|
|
49
|
+
readonly license: AuraAssetLicense;
|
|
50
|
+
readonly thumbnailUrl?: string;
|
|
51
|
+
readonly fileSizeBytes?: number;
|
|
52
|
+
/** Triangle count when the source exposes it; otherwise undefined (enriched later). */
|
|
53
|
+
readonly triangles?: number;
|
|
54
|
+
readonly hasAnimations?: boolean;
|
|
55
|
+
readonly bounds?: AuraAssetBounds;
|
|
56
|
+
/** Source popularity signal (e.g. Sketchfab likes) — feeds quality ranking. */
|
|
57
|
+
readonly likeCount?: number;
|
|
58
|
+
/** Source popularity signal (e.g. Sketchfab views) — feeds quality ranking. */
|
|
59
|
+
readonly viewCount?: number;
|
|
60
|
+
/** Lowercased keywords used for relevance ranking. */
|
|
61
|
+
readonly tags: readonly string[];
|
|
62
|
+
/** Authoritative human page for the asset (license, author, terms). */
|
|
63
|
+
readonly sourcePage?: string;
|
|
64
|
+
/** Free-form attribution credit captured at index time (author/creator). */
|
|
65
|
+
readonly attribution?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* True when an asset may be auto-resolved and pulled into a project without a
|
|
69
|
+
* human licensing decision: it must be directly downloadable and carry a
|
|
70
|
+
* verified, redistributable license.
|
|
71
|
+
*/
|
|
72
|
+
export declare function isAutoPullable(asset: AuraCanonicalAsset): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Normalize a raw source license string into an {@link AuraAssetLicense}.
|
|
75
|
+
* Anything not recognized becomes `UNVERIFIED` (non-redistributable) so the
|
|
76
|
+
* resolver can never auto-pull a file whose terms we have not confirmed.
|
|
77
|
+
*/
|
|
78
|
+
export declare function normalizeLicense(raw: string | null | undefined, sourcePage?: string): AuraAssetLicense;
|
|
79
|
+
//# sourceMappingURL=CanonicalAsset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CanonicalAsset.d.ts","sourceRoot":"","sources":["../../../../packages/asset-index/src/CanonicalAsset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,+EAA+E;AAC/E,MAAM,MAAM,oBAAoB,GAC5B,SAAS,GACT,WAAW,GACX,WAAW,GACX,YAAY,CAAC;AAEjB,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,iFAAiF;IACjF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,MAAM,CAAC;AAE7C,iFAAiF;AACjF,MAAM,MAAM,eAAe,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAEnE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,8EAA8E;IAC9E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,uFAAuF;IACvF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;IAClC,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,uEAAuE;IACvE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAMjE;AAWD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC9B,UAAU,CAAC,EAAE,MAAM,GAClB,gBAAgB,CAclB"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical, source-agnostic asset record.
|
|
3
|
+
*
|
|
4
|
+
* Every source adapter (Khronos, OS3A, Poly Pizza, Sketchfab, ...) normalizes
|
|
5
|
+
* its native records into this one shape so the federation/ranking layer never
|
|
6
|
+
* has to know which source an asset came from.
|
|
7
|
+
*
|
|
8
|
+
* Design rule that mirrors the rest of Aura3D: this index RECORDS license; it
|
|
9
|
+
* never guesses one. An asset whose license could not be verified is marked
|
|
10
|
+
* `UNVERIFIED` and is NOT auto-pullable until a verification pass resolves it.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* True when an asset may be auto-resolved and pulled into a project without a
|
|
14
|
+
* human licensing decision: it must be directly downloadable and carry a
|
|
15
|
+
* verified, redistributable license.
|
|
16
|
+
*/
|
|
17
|
+
export function isAutoPullable(asset) {
|
|
18
|
+
return (asset.access === "direct-download" &&
|
|
19
|
+
asset.license.verified &&
|
|
20
|
+
asset.license.redistributable);
|
|
21
|
+
}
|
|
22
|
+
const LICENSE_TABLE = {
|
|
23
|
+
cc0: { spdx: "CC0-1.0", verified: true, attributionRequired: false, redistributable: true },
|
|
24
|
+
"cc0-1.0": { spdx: "CC0-1.0", verified: true, attributionRequired: false, redistributable: true },
|
|
25
|
+
"public domain": { spdx: "CC0-1.0", verified: true, attributionRequired: false, redistributable: true },
|
|
26
|
+
"cc-by": { spdx: "CC-BY-4.0", verified: true, attributionRequired: true, redistributable: true },
|
|
27
|
+
"cc-by-4.0": { spdx: "CC-BY-4.0", verified: true, attributionRequired: true, redistributable: true },
|
|
28
|
+
"cc-by-3.0": { spdx: "CC-BY-3.0", verified: true, attributionRequired: true, redistributable: true },
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Normalize a raw source license string into an {@link AuraAssetLicense}.
|
|
32
|
+
* Anything not recognized becomes `UNVERIFIED` (non-redistributable) so the
|
|
33
|
+
* resolver can never auto-pull a file whose terms we have not confirmed.
|
|
34
|
+
*/
|
|
35
|
+
export function normalizeLicense(raw, sourcePage) {
|
|
36
|
+
const key = (raw ?? "").trim().toLowerCase();
|
|
37
|
+
const hit = LICENSE_TABLE[key];
|
|
38
|
+
if (hit) {
|
|
39
|
+
return { ...hit, raw: raw ?? "", sourcePage };
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
spdx: "UNVERIFIED",
|
|
43
|
+
raw: raw ?? "",
|
|
44
|
+
verified: false,
|
|
45
|
+
attributionRequired: true,
|
|
46
|
+
redistributable: false,
|
|
47
|
+
sourcePage,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=CanonicalAsset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CanonicalAsset.js","sourceRoot":"","sources":["../../../../packages/asset-index/src/CanonicalAsset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAmEH;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAyB;IACtD,OAAO,CACL,KAAK,CAAC,MAAM,KAAK,iBAAiB;QAClC,KAAK,CAAC,OAAO,CAAC,QAAQ;QACtB,KAAK,CAAC,OAAO,CAAC,eAAe,CAC9B,CAAC;AACJ,CAAC;AAED,MAAM,aAAa,GAAiE;IAClF,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE;IAC3F,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE;IACjG,eAAe,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE;IACvG,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;IAChG,WAAW,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;IACpG,WAAW,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;CACrG,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAA8B,EAC9B,UAAmB;IAEnB,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC7C,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,GAAG,EAAE,CAAC;QACR,OAAO,EAAE,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,EAAE,UAAU,EAAE,CAAC;IAChD,CAAC;IACD,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,GAAG,EAAE,GAAG,IAAI,EAAE;QACd,QAAQ,EAAE,KAAK;QACf,mBAAmB,EAAE,IAAI;QACzB,eAAe,EAAE,KAAK;QACtB,UAAU;KACX,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { AuraCanonicalAsset } from "./CanonicalAsset.js";
|
|
2
|
+
/**
|
|
3
|
+
* On-disk schema tag. Bump when the persisted shape changes incompatibly so a
|
|
4
|
+
* stale file can be detected (and, in future, migrated) rather than misread.
|
|
5
|
+
*/
|
|
6
|
+
export declare const INDEX_STORE_SCHEMA: "aura3d-asset-index/1";
|
|
7
|
+
/**
|
|
8
|
+
* Persisted JSON shape of an {@link IndexStore}.
|
|
9
|
+
*
|
|
10
|
+
* - `watermarks` maps a source id to the opaque cursor returned by that
|
|
11
|
+
* adapter's `fetchSince`, so the next refresh resumes incrementally.
|
|
12
|
+
* - `assets` is the flat, de-duplicated canonical catalog keyed (logically) by
|
|
13
|
+
* `id`; we store it as an array for stable, diff-friendly JSON.
|
|
14
|
+
* - `updatedAt` is caller-supplied (never read from the wall clock here) to keep
|
|
15
|
+
* the store deterministic and testable.
|
|
16
|
+
*/
|
|
17
|
+
export interface IndexStoreFile {
|
|
18
|
+
readonly schema: typeof INDEX_STORE_SCHEMA;
|
|
19
|
+
readonly updatedAt: string | null;
|
|
20
|
+
readonly watermarks: Record<string, string>;
|
|
21
|
+
readonly assets: AuraCanonicalAsset[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A file-backed JSON store of {@link AuraCanonicalAsset} records keyed by
|
|
25
|
+
* canonical id, plus a per-source watermark map.
|
|
26
|
+
*
|
|
27
|
+
* This is the durable catalog the "live" cron writes into (via
|
|
28
|
+
* {@link refreshIndex}) and the resolver reads out of. It is intentionally
|
|
29
|
+
* deterministic: it never reads wall-clock time or randomness internally. The
|
|
30
|
+
* caller threads an `updatedAt` string into {@link save} when one is wanted.
|
|
31
|
+
*/
|
|
32
|
+
export declare class IndexStore {
|
|
33
|
+
private readonly path;
|
|
34
|
+
/** id -> canonical asset (last write wins on upsert). */
|
|
35
|
+
private readonly assets;
|
|
36
|
+
private readonly watermarks;
|
|
37
|
+
private updatedAt;
|
|
38
|
+
private constructor();
|
|
39
|
+
/**
|
|
40
|
+
* Load a store from `path`. A missing file is tolerated and yields an empty
|
|
41
|
+
* store (the cron's first run). A present-but-corrupt file throws so the
|
|
42
|
+
* problem is surfaced rather than silently discarded.
|
|
43
|
+
*/
|
|
44
|
+
static load(path: string): Promise<IndexStore>;
|
|
45
|
+
/**
|
|
46
|
+
* Insert or replace many records from one source, de-duping by canonical id
|
|
47
|
+
* with last-write-wins semantics. The `source` argument is accepted for call
|
|
48
|
+
* symmetry with the watermark API and to document provenance at the call
|
|
49
|
+
* site; the canonical id already namespaces the source.
|
|
50
|
+
*/
|
|
51
|
+
upsertMany(_source: string, assets: Iterable<AuraCanonicalAsset>): void;
|
|
52
|
+
/** The last watermark recorded for `source`, or `null` if never refreshed. */
|
|
53
|
+
getWatermark(source: string): string | null;
|
|
54
|
+
/** Record the next watermark for `source`. */
|
|
55
|
+
setWatermark(source: string, watermark: string): void;
|
|
56
|
+
/** All canonical assets currently held, in stable insertion order. */
|
|
57
|
+
all(): AuraCanonicalAsset[];
|
|
58
|
+
/**
|
|
59
|
+
* Atomically persist the store: serialize, write to a sibling temp file, then
|
|
60
|
+
* `rename` over the real path so a reader never sees a half-written file.
|
|
61
|
+
*
|
|
62
|
+
* `updatedAt` is caller-supplied to keep the store deterministic; when
|
|
63
|
+
* omitted the previously loaded/saved value is preserved.
|
|
64
|
+
*/
|
|
65
|
+
save(updatedAt?: string): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=IndexStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IndexStore.d.ts","sourceRoot":"","sources":["../../../../packages/asset-index/src/IndexStore.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAG,sBAA+B,CAAC;AAElE;;;;;;;;;GASG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,kBAAkB,CAAC;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,kBAAkB,EAAE,CAAC;CACvC;AAED;;;;;;;;GAQG;AACH,qBAAa,UAAU;IAMD,OAAO,CAAC,QAAQ,CAAC,IAAI;IALzC,yDAAyD;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyC;IAChE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IACxD,OAAO,CAAC,SAAS,CAAuB;IAExC,OAAO;IAEP;;;;OAIG;WACU,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IA0BpD;;;;;OAKG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,kBAAkB,CAAC,GAAG,IAAI;IAQvE,8EAA8E;IAC9E,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAI3C,8CAA8C;IAC9C,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrD,sEAAsE;IACtE,GAAG,IAAI,kBAAkB,EAAE;IAI3B;;;;;;OAMG;IACG,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAc9C"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { readFile, writeFile, rename, mkdir } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* On-disk schema tag. Bump when the persisted shape changes incompatibly so a
|
|
5
|
+
* stale file can be detected (and, in future, migrated) rather than misread.
|
|
6
|
+
*/
|
|
7
|
+
export const INDEX_STORE_SCHEMA = "aura3d-asset-index/1";
|
|
8
|
+
/**
|
|
9
|
+
* A file-backed JSON store of {@link AuraCanonicalAsset} records keyed by
|
|
10
|
+
* canonical id, plus a per-source watermark map.
|
|
11
|
+
*
|
|
12
|
+
* This is the durable catalog the "live" cron writes into (via
|
|
13
|
+
* {@link refreshIndex}) and the resolver reads out of. It is intentionally
|
|
14
|
+
* deterministic: it never reads wall-clock time or randomness internally. The
|
|
15
|
+
* caller threads an `updatedAt` string into {@link save} when one is wanted.
|
|
16
|
+
*/
|
|
17
|
+
export class IndexStore {
|
|
18
|
+
path;
|
|
19
|
+
/** id -> canonical asset (last write wins on upsert). */
|
|
20
|
+
assets = new Map();
|
|
21
|
+
watermarks = new Map();
|
|
22
|
+
updatedAt = null;
|
|
23
|
+
constructor(path) {
|
|
24
|
+
this.path = path;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Load a store from `path`. A missing file is tolerated and yields an empty
|
|
28
|
+
* store (the cron's first run). A present-but-corrupt file throws so the
|
|
29
|
+
* problem is surfaced rather than silently discarded.
|
|
30
|
+
*/
|
|
31
|
+
static async load(path) {
|
|
32
|
+
const store = new IndexStore(path);
|
|
33
|
+
let text;
|
|
34
|
+
try {
|
|
35
|
+
text = await readFile(path, "utf8");
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (isNotFound(err))
|
|
39
|
+
return store;
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
const parsed = JSON.parse(text);
|
|
43
|
+
if (Array.isArray(parsed.assets)) {
|
|
44
|
+
for (const asset of parsed.assets) {
|
|
45
|
+
if (asset && typeof asset.id === "string") {
|
|
46
|
+
store.assets.set(asset.id, asset);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (parsed.watermarks && typeof parsed.watermarks === "object") {
|
|
51
|
+
for (const [source, w] of Object.entries(parsed.watermarks)) {
|
|
52
|
+
if (typeof w === "string")
|
|
53
|
+
store.watermarks.set(source, w);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
store.updatedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : null;
|
|
57
|
+
return store;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Insert or replace many records from one source, de-duping by canonical id
|
|
61
|
+
* with last-write-wins semantics. The `source` argument is accepted for call
|
|
62
|
+
* symmetry with the watermark API and to document provenance at the call
|
|
63
|
+
* site; the canonical id already namespaces the source.
|
|
64
|
+
*/
|
|
65
|
+
upsertMany(_source, assets) {
|
|
66
|
+
for (const asset of assets) {
|
|
67
|
+
if (asset && typeof asset.id === "string") {
|
|
68
|
+
this.assets.set(asset.id, asset);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** The last watermark recorded for `source`, or `null` if never refreshed. */
|
|
73
|
+
getWatermark(source) {
|
|
74
|
+
return this.watermarks.get(source) ?? null;
|
|
75
|
+
}
|
|
76
|
+
/** Record the next watermark for `source`. */
|
|
77
|
+
setWatermark(source, watermark) {
|
|
78
|
+
this.watermarks.set(source, watermark);
|
|
79
|
+
}
|
|
80
|
+
/** All canonical assets currently held, in stable insertion order. */
|
|
81
|
+
all() {
|
|
82
|
+
return [...this.assets.values()];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Atomically persist the store: serialize, write to a sibling temp file, then
|
|
86
|
+
* `rename` over the real path so a reader never sees a half-written file.
|
|
87
|
+
*
|
|
88
|
+
* `updatedAt` is caller-supplied to keep the store deterministic; when
|
|
89
|
+
* omitted the previously loaded/saved value is preserved.
|
|
90
|
+
*/
|
|
91
|
+
async save(updatedAt) {
|
|
92
|
+
if (updatedAt !== undefined)
|
|
93
|
+
this.updatedAt = updatedAt;
|
|
94
|
+
const file = {
|
|
95
|
+
schema: INDEX_STORE_SCHEMA,
|
|
96
|
+
updatedAt: this.updatedAt,
|
|
97
|
+
watermarks: Object.fromEntries(this.watermarks),
|
|
98
|
+
assets: this.all(),
|
|
99
|
+
};
|
|
100
|
+
const json = `${JSON.stringify(file, null, 2)}\n`;
|
|
101
|
+
await mkdir(dirname(this.path), { recursive: true });
|
|
102
|
+
const tmp = `${this.path}.tmp`;
|
|
103
|
+
await writeFile(tmp, json, "utf8");
|
|
104
|
+
await rename(tmp, this.path);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function isNotFound(err) {
|
|
108
|
+
return (typeof err === "object" &&
|
|
109
|
+
err !== null &&
|
|
110
|
+
err.code === "ENOENT");
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=IndexStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IndexStore.js","sourceRoot":"","sources":["../../../../packages/asset-index/src/IndexStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { AuraAssetFormat, AuraCanonicalAsset } from "./CanonicalAsset.js";
|
|
2
|
+
/**
|
|
3
|
+
* Injected JSON fetcher. Defaults to global `fetch` in production, but is
|
|
4
|
+
* overridable so adapters can be tested deterministically with fixtures and no
|
|
5
|
+
* network access.
|
|
6
|
+
*/
|
|
7
|
+
export type FetchJson = (url: string) => Promise<unknown>;
|
|
8
|
+
export interface AdapterContext {
|
|
9
|
+
readonly fetchJson: FetchJson;
|
|
10
|
+
}
|
|
11
|
+
/** Constraints an agent/resolver can impose on candidate assets. */
|
|
12
|
+
export interface ResolveConstraints {
|
|
13
|
+
/** Restrict to these license families (matched against normalized SPDX). */
|
|
14
|
+
readonly license?: readonly ("CC0" | "CC-BY")[];
|
|
15
|
+
readonly maxTriangles?: number;
|
|
16
|
+
readonly animated?: boolean;
|
|
17
|
+
readonly format?: AuraAssetFormat;
|
|
18
|
+
/**
|
|
19
|
+
* When true (the default for auto-pull flows), drop anything that is not
|
|
20
|
+
* directly downloadable under a verified, redistributable license.
|
|
21
|
+
*/
|
|
22
|
+
readonly redistributableOnly?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface ResolveQuery {
|
|
25
|
+
/** Natural-language intent, e.g. "battle-worn knight helmet". */
|
|
26
|
+
readonly text: string;
|
|
27
|
+
readonly constraints?: ResolveConstraints;
|
|
28
|
+
}
|
|
29
|
+
export interface AdapterRefreshResult {
|
|
30
|
+
/** Opaque per-source watermark to pass back on the next refresh. */
|
|
31
|
+
readonly watermark: string;
|
|
32
|
+
readonly assets: readonly AuraCanonicalAsset[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* One source of free GLB/glTF assets. A static-index source (Khronos, OS3A)
|
|
36
|
+
* fetches and caches its whole catalog inside `search`; a large API source
|
|
37
|
+
* (Sketchfab) translates the query into a server-side request. Either way the
|
|
38
|
+
* adapter returns canonical records and the federation layer ranks/filters.
|
|
39
|
+
*/
|
|
40
|
+
export interface SourceAdapter {
|
|
41
|
+
readonly id: string;
|
|
42
|
+
readonly label: string;
|
|
43
|
+
/** Return candidate canonical assets for a query (pre-ranking). */
|
|
44
|
+
search(query: ResolveQuery, ctx: AdapterContext): Promise<readonly AuraCanonicalAsset[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Optional incremental refresh used by the cron index. Given the last
|
|
47
|
+
* watermark, return new/changed assets and the next watermark. Sources
|
|
48
|
+
* without a usable change signal can omit this.
|
|
49
|
+
*/
|
|
50
|
+
fetchSince?(watermark: string | null, ctx: AdapterContext): Promise<AdapterRefreshResult>;
|
|
51
|
+
}
|
|
52
|
+
/** Default `FetchJson` backed by the platform `fetch`. */
|
|
53
|
+
export declare const defaultFetchJson: FetchJson;
|
|
54
|
+
//# sourceMappingURL=SourceAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SourceAdapter.d.ts","sourceRoot":"","sources":["../../../../packages/asset-index/src/SourceAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE/E;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC/B;AAED,oEAAoE;AACpE,MAAM,WAAW,kBAAkB;IACjC,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,KAAK,GAAG,OAAO,CAAC,EAAE,CAAC;IAChD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAC3C;AAED,MAAM,WAAW,oBAAoB;IACnC,oEAAoE;IACpE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,CAAC;CAChD;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,mEAAmE;IACnE,MAAM,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;IAEzF;;;;OAIG;IACH,UAAU,CAAC,CACT,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAClC;AAED,0DAA0D;AAC1D,eAAO,MAAM,gBAAgB,EAAE,SAM9B,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Default `FetchJson` backed by the platform `fetch`. */
|
|
2
|
+
export const defaultFetchJson = async (url) => {
|
|
3
|
+
const res = await fetch(url, { headers: { accept: "application/json" } });
|
|
4
|
+
if (!res.ok) {
|
|
5
|
+
throw new Error(`asset-index fetch failed ${res.status} for ${url}`);
|
|
6
|
+
}
|
|
7
|
+
return res.json();
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=SourceAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SourceAdapter.js","sourceRoot":"","sources":["../../../../packages/asset-index/src/SourceAdapter.ts"],"names":[],"mappings":"AA+DA,0DAA0D;AAC1D,MAAM,CAAC,MAAM,gBAAgB,GAAc,KAAK,EAAE,GAAG,EAAE,EAAE;IACvD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;IAC1E,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,CAAC,MAAM,QAAQ,GAAG,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SourceAdapter } from "../SourceAdapter.js";
|
|
2
|
+
export interface AuraIndexAdapterOptions {
|
|
3
|
+
/** Hosted `/search` endpoint URL. Defaults to the Aura3D catalog worker. */
|
|
4
|
+
readonly searchUrl?: string;
|
|
5
|
+
/** Max results to request. Default 20. */
|
|
6
|
+
readonly limit?: number;
|
|
7
|
+
/** Restrict to commercially-redistributable assets (default true). */
|
|
8
|
+
readonly commercialOnly?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function createAuraIndexAdapter(options?: AuraIndexAdapterOptions): SourceAdapter;
|
|
11
|
+
//# sourceMappingURL=aura-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aura-index.d.ts","sourceRoot":"","sources":["../../../../../packages/asset-index/src/adapters/aura-index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,aAAa,EACd,MAAM,qBAAqB,CAAC;AA4C7B,MAAM,WAAW,uBAAuB;IACtC,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AASD,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,uBAA4B,GACpC,aAAa,CAgCf"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { normalizeLicense } from "../CanonicalAsset.js";
|
|
2
|
+
/**
|
|
3
|
+
* Map the catalog's SPDX string to a license. The `/search` endpoint already
|
|
4
|
+
* filtered to redistributable when `commercial=true`, so CC-BY-SA/ND collapse to
|
|
5
|
+
* the CC-BY family (attribution-required, redistributable) rather than UNVERIFIED.
|
|
6
|
+
*/
|
|
7
|
+
function catalogLicense(spdx) {
|
|
8
|
+
if (spdx.startsWith("CC0")) {
|
|
9
|
+
return { spdx: "CC0-1.0", raw: spdx, verified: true, attributionRequired: false, redistributable: true };
|
|
10
|
+
}
|
|
11
|
+
if (spdx.startsWith("CC-BY")) {
|
|
12
|
+
return { spdx: "CC-BY-4.0", raw: spdx, verified: true, attributionRequired: true, redistributable: true };
|
|
13
|
+
}
|
|
14
|
+
return normalizeLicense(spdx);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Aura3D hosted-catalog adapter.
|
|
18
|
+
*
|
|
19
|
+
* Queries the hosted `/search` endpoint — hybrid keyword + semantic + quality
|
|
20
|
+
* search over the full ~850k-asset D1 catalog the cron maintains. This is the
|
|
21
|
+
* read path the resolver / CLI / prompt feature use: one request returns a
|
|
22
|
+
* ranked, license-verified shortlist for an intent, instead of loading the whole
|
|
23
|
+
* catalog or live-federating each upstream source.
|
|
24
|
+
*/
|
|
25
|
+
const DEFAULT_SEARCH_URL = "https://aura3d-asset-index-cron.newsroom.workers.dev/search";
|
|
26
|
+
function tokenize(text) {
|
|
27
|
+
return text
|
|
28
|
+
.toLowerCase()
|
|
29
|
+
.split(/[^a-z0-9]+/)
|
|
30
|
+
.filter((t) => t.length > 1);
|
|
31
|
+
}
|
|
32
|
+
export function createAuraIndexAdapter(options = {}) {
|
|
33
|
+
const base = options.searchUrl ?? DEFAULT_SEARCH_URL;
|
|
34
|
+
const limit = options.limit ?? 20;
|
|
35
|
+
const commercialOnly = options.commercialOnly ?? true;
|
|
36
|
+
return {
|
|
37
|
+
id: "aura-index",
|
|
38
|
+
label: "Aura3D hosted catalog (~850k)",
|
|
39
|
+
async search(query, ctx) {
|
|
40
|
+
const text = query.text.trim();
|
|
41
|
+
if (!text)
|
|
42
|
+
return [];
|
|
43
|
+
const url = `${base}?q=${encodeURIComponent(text)}&limit=${limit}` +
|
|
44
|
+
`&commercial=${commercialOnly}`;
|
|
45
|
+
const res = (await ctx.fetchJson(url));
|
|
46
|
+
const results = Array.isArray(res.results) ? res.results : [];
|
|
47
|
+
return results
|
|
48
|
+
.filter((r) => r && typeof r.id === "string" && r.url)
|
|
49
|
+
.map((r) => ({
|
|
50
|
+
id: r.id,
|
|
51
|
+
source: r.source ?? "aura-index",
|
|
52
|
+
title: r.title ?? r.id,
|
|
53
|
+
url: r.url,
|
|
54
|
+
access: "direct-download",
|
|
55
|
+
format: "glb",
|
|
56
|
+
license: catalogLicense(r.license ?? ""),
|
|
57
|
+
thumbnailUrl: r.thumbnail ?? undefined,
|
|
58
|
+
tags: tokenize(r.title ?? ""),
|
|
59
|
+
attribution: r.attribution ?? undefined,
|
|
60
|
+
}));
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=aura-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aura-index.js","sourceRoot":"","sources":["../../../../../packages/asset-index/src/adapters/aura-index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SourceAdapter } from "../SourceAdapter.js";
|
|
2
|
+
export interface JsDelivrMirrorOptions {
|
|
3
|
+
/** Manifest URL. Defaults to the Aura3D CC0 mirror on jsDelivr. */
|
|
4
|
+
readonly manifestUrl?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function createJsDelivrMirrorAdapter(options?: JsDelivrMirrorOptions): SourceAdapter;
|
|
7
|
+
//# sourceMappingURL=jsdelivr-mirror.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsdelivr-mirror.d.ts","sourceRoot":"","sources":["../../../../../packages/asset-index/src/adapters/jsdelivr-mirror.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,aAAa,EACd,MAAM,qBAAqB,CAAC;AA0E7B,MAAM,WAAW,qBAAqB;IACpC,mEAAmE;IACnE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,2BAA2B,CACzC,OAAO,GAAE,qBAA0B,GAClC,aAAa,CA6Bf"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { normalizeLicense } from "../CanonicalAsset.js";
|
|
2
|
+
/**
|
|
3
|
+
* jsDelivr CC0 mirror adapter.
|
|
4
|
+
*
|
|
5
|
+
* Reads a manifest of CC0 GLBs that an extraction pipeline mirrored from
|
|
6
|
+
* ZIP-pack sources (Kenney, Quaternius, curated OpenGameArt) into a public
|
|
7
|
+
* GitHub repo, which jsDelivr fronts as a free CDN. We host nothing ourselves:
|
|
8
|
+
* the files live in the mirror repo and are served from `cdn.jsdelivr.net/gh/...`.
|
|
9
|
+
*
|
|
10
|
+
* Manifest shape (schema "aura3d-cc0-mirror/1"):
|
|
11
|
+
* { schema, cdnBase, assets: [
|
|
12
|
+
* { id, source, pack?, title, path, license, tags?, triangles?, animated? }
|
|
13
|
+
* ] }
|
|
14
|
+
* Each asset's download URL is `${cdnBase}/${path}`. Only CC0/CC-BY entries are
|
|
15
|
+
* mirrored, so every record is verified and auto-pullable.
|
|
16
|
+
*/
|
|
17
|
+
// Manifest is read from raw GitHub (always fresh — no CDN cache lag), while the
|
|
18
|
+
// GLB files it points at are served from jsDelivr (fast, content-stable).
|
|
19
|
+
const DEFAULT_MANIFEST_URL = "https://raw.githubusercontent.com/gchahal1982/aura3d-cc0-assets/main/manifest.json";
|
|
20
|
+
function tokenize(text) {
|
|
21
|
+
return text
|
|
22
|
+
.replace(/[_-]+/g, " ")
|
|
23
|
+
.toLowerCase()
|
|
24
|
+
.split(/[^a-z0-9]+/)
|
|
25
|
+
.filter((t) => t.length > 1);
|
|
26
|
+
}
|
|
27
|
+
function toCanonical(asset, cdnBase) {
|
|
28
|
+
if (!asset.path || typeof asset.id !== "string")
|
|
29
|
+
return null;
|
|
30
|
+
const title = asset.title ?? asset.id;
|
|
31
|
+
const provenance = asset.source ?? "mirror";
|
|
32
|
+
const tags = Array.from(new Set([
|
|
33
|
+
...tokenize(title),
|
|
34
|
+
...(asset.tags ?? []).flatMap(tokenize),
|
|
35
|
+
...tokenize(asset.pack ?? ""),
|
|
36
|
+
]));
|
|
37
|
+
return {
|
|
38
|
+
id: asset.id,
|
|
39
|
+
source: `mirror:${provenance}`,
|
|
40
|
+
title,
|
|
41
|
+
url: `${cdnBase}/${asset.path}`,
|
|
42
|
+
access: "direct-download",
|
|
43
|
+
format: "glb",
|
|
44
|
+
license: normalizeLicense(asset.license ?? "CC0"),
|
|
45
|
+
triangles: asset.triangles,
|
|
46
|
+
hasAnimations: asset.animated,
|
|
47
|
+
tags,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function createJsDelivrMirrorAdapter(options = {}) {
|
|
51
|
+
const manifestUrl = options.manifestUrl ?? DEFAULT_MANIFEST_URL;
|
|
52
|
+
let cache = null;
|
|
53
|
+
async function load(ctx) {
|
|
54
|
+
if (cache)
|
|
55
|
+
return cache;
|
|
56
|
+
const manifest = (await ctx.fetchJson(manifestUrl));
|
|
57
|
+
const cdnBase = manifest.cdnBase;
|
|
58
|
+
const assets = Array.isArray(manifest.assets) ? manifest.assets : [];
|
|
59
|
+
if (!cdnBase || assets.length === 0) {
|
|
60
|
+
cache = [];
|
|
61
|
+
return cache;
|
|
62
|
+
}
|
|
63
|
+
const out = [];
|
|
64
|
+
for (const asset of assets) {
|
|
65
|
+
const canonical = toCanonical(asset, cdnBase);
|
|
66
|
+
if (canonical)
|
|
67
|
+
out.push(canonical);
|
|
68
|
+
}
|
|
69
|
+
cache = out;
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
id: "jsdelivr-mirror",
|
|
74
|
+
label: "Aura3D CC0 mirror (jsDelivr)",
|
|
75
|
+
async search(_query, ctx) {
|
|
76
|
+
return load(ctx);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=jsdelivr-mirror.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsdelivr-mirror.js","sourceRoot":"","sources":["../../../../../packages/asset-index/src/adapters/jsdelivr-mirror.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAOxD;;;;;;;;;;;;;;GAcG;AAEH,gFAAgF;AAChF,0EAA0E;AAC1E,MAAM,oBAAoB,GACxB,oFAAoF,CAAC;AAoBvF,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI;SACR,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,WAAW,EAAE;SACb,KAAK,CAAC,YAAY,CAAC;SACnB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,WAAW,CAAC,KAAkB,EAAE,OAAe;IACtD,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC7D,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,EAAE,CAAC;IACtC,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,IAAI,QAAQ,CAAC;IAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CACrB,IAAI,GAAG,CAAC;QACN,GAAG,QAAQ,CAAC,KAAK,CAAC;QAClB,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;QACvC,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;KAC9B,CAAC,CACH,CAAC;IACF,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,MAAM,EAAE,UAAU,UAAU,EAAE;QAC9B,KAAK;QACL,GAAG,EAAE,GAAG,OAAO,IAAI,KAAK,CAAC,IAAI,EAAE;QAC/B,MAAM,EAAE,iBAAiB;QACzB,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC;QACjD,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,aAAa,EAAE,KAAK,CAAC,QAAQ;QAC7B,IAAI;KACL,CAAC;AACJ,CAAC;AAOD,MAAM,UAAU,2BAA2B,CACzC,UAAiC,EAAE;IAEnC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,oBAAoB,CAAC;IAChE,IAAI,KAAK,GAAyC,IAAI,CAAC;IAEvD,KAAK,UAAU,IAAI,CAAC,GAAmB;QACrC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;QACxB,MAAM,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAmB,CAAC;QACtE,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;QACjC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpC,KAAK,GAAG,EAAE,CAAC;YACX,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,GAAG,GAAyB,EAAE,CAAC;QACrC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAC9C,IAAI,SAAS;gBAAE,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACrC,CAAC;QACD,KAAK,GAAG,GAAG,CAAC;QACZ,OAAO,GAAG,CAAC;IACb,CAAC;IAED,OAAO;QACL,EAAE,EAAE,iBAAiB;QACrB,KAAK,EAAE,8BAA8B;QACrC,KAAK,CAAC,MAAM,CAAC,MAAoB,EAAE,GAAmB;YACpD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"khronos.d.ts","sourceRoot":"","sources":["../../../../../packages/asset-index/src/adapters/khronos.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,aAAa,EACd,MAAM,qBAAqB,CAAC;AAgE7B,wBAAgB,oBAAoB,IAAI,aAAa,CA2BpD"}
|