threenative-asset-mcp 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 +441 -0
- package/dist/ambientcg/client.d.ts +61 -0
- package/dist/ambientcg/client.d.ts.map +1 -0
- package/dist/ambientcg/client.js +234 -0
- package/dist/ambientcg/client.js.map +1 -0
- package/dist/audio/catalog.d.ts +36 -0
- package/dist/audio/catalog.d.ts.map +1 -0
- package/dist/audio/catalog.js +169 -0
- package/dist/audio/catalog.js.map +1 -0
- package/dist/audio/client.d.ts +46 -0
- package/dist/audio/client.d.ts.map +1 -0
- package/dist/audio/client.js +196 -0
- package/dist/audio/client.js.map +1 -0
- package/dist/bundle/client.d.ts +89 -0
- package/dist/bundle/client.d.ts.map +1 -0
- package/dist/bundle/client.js +324 -0
- package/dist/bundle/client.js.map +1 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +130 -0
- package/dist/config.js.map +1 -0
- package/dist/download/direct-asset-downloader.d.ts +41 -0
- package/dist/download/direct-asset-downloader.d.ts.map +1 -0
- package/dist/download/direct-asset-downloader.js +232 -0
- package/dist/download/direct-asset-downloader.js.map +1 -0
- package/dist/fab/api-download.d.ts +30 -0
- package/dist/fab/api-download.d.ts.map +1 -0
- package/dist/fab/api-download.js +200 -0
- package/dist/fab/api-download.js.map +1 -0
- package/dist/fab/browser-transport.d.ts +79 -0
- package/dist/fab/browser-transport.d.ts.map +1 -0
- package/dist/fab/browser-transport.js +492 -0
- package/dist/fab/browser-transport.js.map +1 -0
- package/dist/fab/cache.d.ts +22 -0
- package/dist/fab/cache.d.ts.map +1 -0
- package/dist/fab/cache.js +55 -0
- package/dist/fab/cache.js.map +1 -0
- package/dist/fab/client.d.ts +61 -0
- package/dist/fab/client.d.ts.map +1 -0
- package/dist/fab/client.js +868 -0
- package/dist/fab/client.js.map +1 -0
- package/dist/fab/direct-transport.d.ts +68 -0
- package/dist/fab/direct-transport.d.ts.map +1 -0
- package/dist/fab/direct-transport.js +184 -0
- package/dist/fab/direct-transport.js.map +1 -0
- package/dist/fab/download-store.d.ts +21 -0
- package/dist/fab/download-store.d.ts.map +1 -0
- package/dist/fab/download-store.js +93 -0
- package/dist/fab/download-store.js.map +1 -0
- package/dist/fab/errors.d.ts +20 -0
- package/dist/fab/errors.d.ts.map +1 -0
- package/dist/fab/errors.js +65 -0
- package/dist/fab/errors.js.map +1 -0
- package/dist/fab/impersonate-fetch.d.ts +48 -0
- package/dist/fab/impersonate-fetch.d.ts.map +1 -0
- package/dist/fab/impersonate-fetch.js +228 -0
- package/dist/fab/impersonate-fetch.js.map +1 -0
- package/dist/fab/normalize.d.ts +3 -0
- package/dist/fab/normalize.d.ts.map +1 -0
- package/dist/fab/normalize.js +285 -0
- package/dist/fab/normalize.js.map +1 -0
- package/dist/fab/schemas.d.ts +10 -0
- package/dist/fab/schemas.d.ts.map +1 -0
- package/dist/fab/schemas.js +105 -0
- package/dist/fab/schemas.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/itch/catalog.d.ts +13 -0
- package/dist/itch/catalog.d.ts.map +1 -0
- package/dist/itch/catalog.js +50 -0
- package/dist/itch/catalog.js.map +1 -0
- package/dist/itch/client.d.ts +46 -0
- package/dist/itch/client.d.ts.map +1 -0
- package/dist/itch/client.js +175 -0
- package/dist/itch/client.js.map +1 -0
- package/dist/polyhaven/client.d.ts +55 -0
- package/dist/polyhaven/client.d.ts.map +1 -0
- package/dist/polyhaven/client.js +210 -0
- package/dist/polyhaven/client.js.map +1 -0
- package/dist/server.d.ts +25 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +448 -0
- package/dist/server.js.map +1 -0
- package/dist/sketchfab/client.d.ts +70 -0
- package/dist/sketchfab/client.d.ts.map +1 -0
- package/dist/sketchfab/client.js +255 -0
- package/dist/sketchfab/client.js.map +1 -0
- package/dist/smithsonian/client.d.ts +32 -0
- package/dist/smithsonian/client.d.ts.map +1 -0
- package/dist/smithsonian/client.js +142 -0
- package/dist/smithsonian/client.js.map +1 -0
- package/dist/tools/ambientcg.d.ts +296 -0
- package/dist/tools/ambientcg.d.ts.map +1 -0
- package/dist/tools/ambientcg.js +183 -0
- package/dist/tools/ambientcg.js.map +1 -0
- package/dist/tools/audio.d.ts +211 -0
- package/dist/tools/audio.d.ts.map +1 -0
- package/dist/tools/audio.js +156 -0
- package/dist/tools/audio.js.map +1 -0
- package/dist/tools/bundle.d.ts +237 -0
- package/dist/tools/bundle.d.ts.map +1 -0
- package/dist/tools/bundle.js +206 -0
- package/dist/tools/bundle.js.map +1 -0
- package/dist/tools/direct-download.d.ts +53 -0
- package/dist/tools/direct-download.d.ts.map +1 -0
- package/dist/tools/direct-download.js +68 -0
- package/dist/tools/direct-download.js.map +1 -0
- package/dist/tools/download-free-asset.d.ts +66 -0
- package/dist/tools/download-free-asset.d.ts.map +1 -0
- package/dist/tools/download-free-asset.js +60 -0
- package/dist/tools/download-free-asset.js.map +1 -0
- package/dist/tools/get-asset.d.ts +161 -0
- package/dist/tools/get-asset.d.ts.map +1 -0
- package/dist/tools/get-asset.js +131 -0
- package/dist/tools/get-asset.js.map +1 -0
- package/dist/tools/itch.d.ts +120 -0
- package/dist/tools/itch.d.ts.map +1 -0
- package/dist/tools/itch.js +120 -0
- package/dist/tools/itch.js.map +1 -0
- package/dist/tools/list-filters.d.ts +127 -0
- package/dist/tools/list-filters.d.ts.map +1 -0
- package/dist/tools/list-filters.js +68 -0
- package/dist/tools/list-filters.js.map +1 -0
- package/dist/tools/list-limited-time-free.d.ts +57 -0
- package/dist/tools/list-limited-time-free.d.ts.map +1 -0
- package/dist/tools/list-limited-time-free.js +47 -0
- package/dist/tools/list-limited-time-free.js.map +1 -0
- package/dist/tools/polyhaven.d.ts +266 -0
- package/dist/tools/polyhaven.d.ts.map +1 -0
- package/dist/tools/polyhaven.js +233 -0
- package/dist/tools/polyhaven.js.map +1 -0
- package/dist/tools/search-assets.d.ts +176 -0
- package/dist/tools/search-assets.d.ts.map +1 -0
- package/dist/tools/search-assets.js +172 -0
- package/dist/tools/search-assets.js.map +1 -0
- package/dist/tools/sketchfab.d.ts +325 -0
- package/dist/tools/sketchfab.d.ts.map +1 -0
- package/dist/tools/sketchfab.js +203 -0
- package/dist/tools/sketchfab.js.map +1 -0
- package/dist/tools/smithsonian.d.ts +189 -0
- package/dist/tools/smithsonian.d.ts.map +1 -0
- package/dist/tools/smithsonian.js +189 -0
- package/dist/tools/smithsonian.js.map +1 -0
- package/dist/tools/source-directory.d.ts +226 -0
- package/dist/tools/source-directory.d.ts.map +1 -0
- package/dist/tools/source-directory.js +583 -0
- package/dist/tools/source-directory.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { createReadStream } from "node:fs";
|
|
3
|
+
import { link, lstat, mkdir, open, realpath, stat, unlink, } from "node:fs/promises";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
6
|
+
import { AUDIO_ASSETS, AUDIO_SOURCES, } from "./catalog.js";
|
|
7
|
+
export class AudioCatalogError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
retryable;
|
|
10
|
+
constructor(code, message, retryable = false) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.retryable = retryable;
|
|
14
|
+
this.name = "AudioCatalogError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function isInside(path, parent) {
|
|
18
|
+
const child = relative(parent, path);
|
|
19
|
+
return child === "" || (!child.startsWith("..") && !isAbsolute(child));
|
|
20
|
+
}
|
|
21
|
+
async function sha256File(path) {
|
|
22
|
+
const hash = createHash("sha256");
|
|
23
|
+
await new Promise((resolveHash, reject) => {
|
|
24
|
+
const stream = createReadStream(path);
|
|
25
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
26
|
+
stream.on("error", reject);
|
|
27
|
+
stream.on("end", resolveHash);
|
|
28
|
+
});
|
|
29
|
+
return hash.digest("hex");
|
|
30
|
+
}
|
|
31
|
+
async function writeAll(file, value) {
|
|
32
|
+
let offset = 0;
|
|
33
|
+
while (offset < value.byteLength) {
|
|
34
|
+
const { bytesWritten } = await file.write(value, offset, value.byteLength - offset, null);
|
|
35
|
+
if (bytesWritten <= 0)
|
|
36
|
+
throw new Error("Audio file write made no progress.");
|
|
37
|
+
offset += bytesWritten;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function positiveIntegerEnvironment(value, fallback, maximum) {
|
|
41
|
+
if (!value)
|
|
42
|
+
return fallback;
|
|
43
|
+
const parsed = Number(value);
|
|
44
|
+
return Number.isSafeInteger(parsed) && parsed > 0 && parsed <= maximum
|
|
45
|
+
? parsed
|
|
46
|
+
: fallback;
|
|
47
|
+
}
|
|
48
|
+
export class AudioCatalogClient {
|
|
49
|
+
fetch;
|
|
50
|
+
downloadDir;
|
|
51
|
+
maxDownloadBytes;
|
|
52
|
+
downloadTimeoutMs;
|
|
53
|
+
allowedDownloadHosts = new Set(AUDIO_ASSETS.map((asset) => new URL(asset.downloadUrl).hostname));
|
|
54
|
+
constructor(options = {}) {
|
|
55
|
+
this.fetch = options.fetch ?? globalThis.fetch;
|
|
56
|
+
this.downloadDir = resolve(options.downloadDir ??
|
|
57
|
+
process.env.AUDIO_DOWNLOAD_DIR ??
|
|
58
|
+
join(homedir(), "Downloads", "threenative-asset-mcp", "audio"));
|
|
59
|
+
this.maxDownloadBytes =
|
|
60
|
+
options.maxDownloadBytes ??
|
|
61
|
+
positiveIntegerEnvironment(process.env.AUDIO_MAX_DOWNLOAD_BYTES, 10_737_418_240, 53_687_091_200);
|
|
62
|
+
this.downloadTimeoutMs =
|
|
63
|
+
options.downloadTimeoutMs ??
|
|
64
|
+
positiveIntegerEnvironment(process.env.AUDIO_DOWNLOAD_TIMEOUT_MS, 1_800_000, 7_200_000);
|
|
65
|
+
}
|
|
66
|
+
listSources() {
|
|
67
|
+
return AUDIO_SOURCES.map((source) => ({ ...source, browseUrls: [...source.browseUrls], kinds: [...source.kinds] }));
|
|
68
|
+
}
|
|
69
|
+
searchAssets(input) {
|
|
70
|
+
const query = input.query?.trim().toLocaleLowerCase();
|
|
71
|
+
return AUDIO_ASSETS.filter((asset) => {
|
|
72
|
+
const haystack = [asset.id, asset.name, asset.description, ...asset.tags]
|
|
73
|
+
.join(" ")
|
|
74
|
+
.toLocaleLowerCase();
|
|
75
|
+
return ((!query || haystack.includes(query)) &&
|
|
76
|
+
(!input.kind || input.kind === "all" || asset.kind === input.kind || asset.kind === "mixed") &&
|
|
77
|
+
(!input.source || input.source === "all" || asset.sourceId === input.source));
|
|
78
|
+
}).map((asset) => ({ ...asset, tags: [...asset.tags] }));
|
|
79
|
+
}
|
|
80
|
+
getAsset(assetId) {
|
|
81
|
+
const asset = AUDIO_ASSETS.find((candidate) => candidate.id === assetId);
|
|
82
|
+
if (!asset) {
|
|
83
|
+
throw new AudioCatalogError("AUDIO_ASSET_NOT_FOUND", "The requested audio asset is not in the curated direct-download catalog.");
|
|
84
|
+
}
|
|
85
|
+
return asset;
|
|
86
|
+
}
|
|
87
|
+
async downloadAsset(assetId) {
|
|
88
|
+
const asset = this.getAsset(assetId);
|
|
89
|
+
const root = await this.ensureDirectory(this.downloadDir);
|
|
90
|
+
const directory = await this.ensureDirectory(join(root, asset.sourceId, asset.id));
|
|
91
|
+
if (!isInside(directory, root)) {
|
|
92
|
+
throw new AudioCatalogError("AUDIO_DOWNLOAD_FAILED", "Refused an audio download path outside the configured directory.");
|
|
93
|
+
}
|
|
94
|
+
const outputPath = join(directory, asset.fileName);
|
|
95
|
+
const existing = await lstat(outputPath).catch(() => undefined);
|
|
96
|
+
if (existing) {
|
|
97
|
+
if (!existing.isFile() || existing.isSymbolicLink()) {
|
|
98
|
+
throw new AudioCatalogError("AUDIO_DOWNLOAD_FAILED", "The audio download destination is not a safe regular file.");
|
|
99
|
+
}
|
|
100
|
+
const metadata = await stat(outputPath);
|
|
101
|
+
return this.result(asset, outputPath, metadata.size, await sha256File(outputPath), true);
|
|
102
|
+
}
|
|
103
|
+
const temporaryPath = join(directory, `.${asset.fileName}.${randomUUID()}.part`);
|
|
104
|
+
try {
|
|
105
|
+
const response = await this.fetchDownload(asset.downloadUrl);
|
|
106
|
+
const declaredLength = Number(response.headers.get("content-length"));
|
|
107
|
+
if (Number.isFinite(declaredLength) && declaredLength > this.maxDownloadBytes) {
|
|
108
|
+
throw new AudioCatalogError("AUDIO_DOWNLOAD_TOO_LARGE", "The audio archive exceeds AUDIO_MAX_DOWNLOAD_BYTES.");
|
|
109
|
+
}
|
|
110
|
+
if (!response.body) {
|
|
111
|
+
throw new AudioCatalogError("AUDIO_DOWNLOAD_FAILED", "The audio provider returned an empty response body.", true);
|
|
112
|
+
}
|
|
113
|
+
const file = await open(temporaryPath, "wx", 0o600);
|
|
114
|
+
const hash = createHash("sha256");
|
|
115
|
+
let sizeBytes = 0;
|
|
116
|
+
try {
|
|
117
|
+
const reader = response.body.getReader();
|
|
118
|
+
while (true) {
|
|
119
|
+
const { done, value } = await reader.read();
|
|
120
|
+
if (done)
|
|
121
|
+
break;
|
|
122
|
+
sizeBytes += value.byteLength;
|
|
123
|
+
if (sizeBytes > this.maxDownloadBytes) {
|
|
124
|
+
await reader.cancel();
|
|
125
|
+
throw new AudioCatalogError("AUDIO_DOWNLOAD_TOO_LARGE", "The audio archive exceeds AUDIO_MAX_DOWNLOAD_BYTES.");
|
|
126
|
+
}
|
|
127
|
+
hash.update(value);
|
|
128
|
+
await writeAll(file, value);
|
|
129
|
+
}
|
|
130
|
+
await file.sync();
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
await file.close();
|
|
134
|
+
}
|
|
135
|
+
await link(temporaryPath, outputPath);
|
|
136
|
+
return this.result(asset, outputPath, sizeBytes, hash.digest("hex"), false);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
if (error instanceof AudioCatalogError)
|
|
140
|
+
throw error;
|
|
141
|
+
throw new AudioCatalogError("AUDIO_DOWNLOAD_FAILED", "The audio archive could not be downloaded or saved safely.", true);
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
await unlink(temporaryPath).catch(() => undefined);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async ensureDirectory(path) {
|
|
148
|
+
await mkdir(path, { recursive: true, mode: 0o700 });
|
|
149
|
+
return realpath(path);
|
|
150
|
+
}
|
|
151
|
+
async fetchDownload(initialUrl) {
|
|
152
|
+
let current = new URL(initialUrl);
|
|
153
|
+
for (let redirects = 0; redirects <= 5; redirects += 1) {
|
|
154
|
+
if (current.protocol !== "https:" || !this.allowedDownloadHosts.has(current.hostname)) {
|
|
155
|
+
throw new AudioCatalogError("AUDIO_UNSAFE_REDIRECT", "The audio provider redirected to a host outside the curated allowlist.");
|
|
156
|
+
}
|
|
157
|
+
const response = await this.fetch(current, {
|
|
158
|
+
redirect: "manual",
|
|
159
|
+
signal: AbortSignal.timeout(this.downloadTimeoutMs),
|
|
160
|
+
headers: { "user-agent": "threenative-asset-mcp/0.4.0" },
|
|
161
|
+
});
|
|
162
|
+
if ([301, 302, 303, 307, 308].includes(response.status)) {
|
|
163
|
+
const location = response.headers.get("location");
|
|
164
|
+
if (!location)
|
|
165
|
+
break;
|
|
166
|
+
current = new URL(location, current);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
throw new AudioCatalogError("AUDIO_UPSTREAM_DENIED", `The audio provider refused the download with HTTP ${response.status}.`, response.status === 429 || response.status >= 500);
|
|
171
|
+
}
|
|
172
|
+
const contentType = response.headers.get("content-type")?.toLocaleLowerCase();
|
|
173
|
+
if (contentType?.includes("text/html")) {
|
|
174
|
+
throw new AudioCatalogError("AUDIO_UPSTREAM_DENIED", "The audio provider returned an HTML page instead of an asset archive.");
|
|
175
|
+
}
|
|
176
|
+
return response;
|
|
177
|
+
}
|
|
178
|
+
throw new AudioCatalogError("AUDIO_UNSAFE_REDIRECT", "The audio provider exceeded the redirect limit.");
|
|
179
|
+
}
|
|
180
|
+
result(asset, path, sizeBytes, sha256, alreadyExisted) {
|
|
181
|
+
return {
|
|
182
|
+
assetId: asset.id,
|
|
183
|
+
sourceId: asset.sourceId,
|
|
184
|
+
fileName: asset.fileName,
|
|
185
|
+
path,
|
|
186
|
+
sizeBytes,
|
|
187
|
+
sha256,
|
|
188
|
+
alreadyExisted,
|
|
189
|
+
license: asset.license,
|
|
190
|
+
attributionRequired: asset.attributionRequired,
|
|
191
|
+
...(asset.attributionText ? { attributionText: asset.attributionText } : {}),
|
|
192
|
+
sourcePageUrl: asset.sourcePageUrl,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/audio/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { ItchPackId } from "../itch/catalog.js";
|
|
2
|
+
import type { ResolvedItchUpload } from "../itch/client.js";
|
|
3
|
+
export interface BundleItchResolver {
|
|
4
|
+
resolveUpload(packId: ItchPackId, uploadId: string): Promise<ResolvedItchUpload>;
|
|
5
|
+
}
|
|
6
|
+
export interface BundleAssetClientOptions {
|
|
7
|
+
itch: BundleItchResolver;
|
|
8
|
+
fetch?: typeof globalThis.fetch;
|
|
9
|
+
downloadDir?: string;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
maxEntryBytes?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface BundleEntryInfo {
|
|
14
|
+
path: string;
|
|
15
|
+
compressedBytes: number;
|
|
16
|
+
uncompressedBytes: number;
|
|
17
|
+
directory: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare class BundleAssetError extends Error {
|
|
20
|
+
readonly code: "BUNDLE_ENTRY_NOT_FOUND" | "BUNDLE_ENTRY_TOO_LARGE" | "BUNDLE_UNSAFE_PATH" | "BUNDLE_UPSTREAM_DENIED" | "BUNDLE_ANIMATION_NOT_FOUND" | "BUNDLE_INVALID_GLTF";
|
|
21
|
+
readonly retryable: boolean;
|
|
22
|
+
constructor(code: "BUNDLE_ENTRY_NOT_FOUND" | "BUNDLE_ENTRY_TOO_LARGE" | "BUNDLE_UNSAFE_PATH" | "BUNDLE_UPSTREAM_DENIED" | "BUNDLE_ANIMATION_NOT_FOUND" | "BUNDLE_INVALID_GLTF", message: string, retryable?: boolean);
|
|
23
|
+
}
|
|
24
|
+
export declare class BundleAssetClient {
|
|
25
|
+
private readonly itch;
|
|
26
|
+
private readonly fetch;
|
|
27
|
+
private readonly downloadDir;
|
|
28
|
+
private readonly timeoutMs;
|
|
29
|
+
private readonly maxEntryBytes;
|
|
30
|
+
private readonly io;
|
|
31
|
+
constructor(options: BundleAssetClientOptions);
|
|
32
|
+
listEntries(packId: ItchPackId, uploadId: string): Promise<{
|
|
33
|
+
pack: import("../itch/catalog.js").ItchPack;
|
|
34
|
+
upload: import("../itch/client.js").ItchDownloadEntry;
|
|
35
|
+
entries: BundleEntryInfo[];
|
|
36
|
+
rangeBytesTransferred: number;
|
|
37
|
+
}>;
|
|
38
|
+
downloadEntry(input: {
|
|
39
|
+
packId: ItchPackId;
|
|
40
|
+
uploadId: string;
|
|
41
|
+
entryPath: string;
|
|
42
|
+
}): Promise<{
|
|
43
|
+
path: string;
|
|
44
|
+
entryPath: string;
|
|
45
|
+
sizeBytes: number;
|
|
46
|
+
sha256: string;
|
|
47
|
+
alreadyCached: boolean;
|
|
48
|
+
rangeBytesTransferred: number;
|
|
49
|
+
}>;
|
|
50
|
+
listAnimations(input: {
|
|
51
|
+
packId: ItchPackId;
|
|
52
|
+
uploadId: string;
|
|
53
|
+
entryPath?: string;
|
|
54
|
+
}): Promise<{
|
|
55
|
+
entryPath: string;
|
|
56
|
+
aggregateEntryBytes: number;
|
|
57
|
+
alreadyCached: boolean;
|
|
58
|
+
rangeBytesTransferred: number;
|
|
59
|
+
animations: {
|
|
60
|
+
index: number;
|
|
61
|
+
name: string;
|
|
62
|
+
channels: number;
|
|
63
|
+
samplers: number;
|
|
64
|
+
}[];
|
|
65
|
+
}>;
|
|
66
|
+
downloadAnimation(input: {
|
|
67
|
+
packId: ItchPackId;
|
|
68
|
+
uploadId: string;
|
|
69
|
+
entryPath?: string;
|
|
70
|
+
animationName: string;
|
|
71
|
+
}): Promise<{
|
|
72
|
+
path: string;
|
|
73
|
+
animationName: string;
|
|
74
|
+
entryPath: string;
|
|
75
|
+
sizeBytes: number;
|
|
76
|
+
aggregateEntryBytes: number;
|
|
77
|
+
sha256: string;
|
|
78
|
+
alreadyExisted: boolean;
|
|
79
|
+
aggregateAlreadyCached: boolean;
|
|
80
|
+
rangeBytesTransferred: number;
|
|
81
|
+
}>;
|
|
82
|
+
private openArchive;
|
|
83
|
+
private readEntry;
|
|
84
|
+
private selectAggregateGlb;
|
|
85
|
+
private readGlb;
|
|
86
|
+
private retainAnimationOnly;
|
|
87
|
+
private entryInfo;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/bundle/client.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,MAAM,WAAW,kBAAkB;IACjC,aAAa,CACX,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,IAAI,EAChB,wBAAwB,GACxB,wBAAwB,GACxB,oBAAoB,GACpB,wBAAwB,GACxB,4BAA4B,GAC5B,qBAAqB;aAET,SAAS;IAT3B,YACkB,IAAI,EAChB,wBAAwB,GACxB,wBAAwB,GACxB,oBAAoB,GACpB,wBAAwB,GACxB,4BAA4B,GAC5B,qBAAqB,EACzB,OAAO,EAAE,MAAM,EACC,SAAS,UAAQ,EAIlC;CACF;AAiHD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0B;IAChD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IAEnC,YAAY,OAAO,EAAE,wBAAwB,EAS5C;IAEK,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM;;;;;OAcrD;IAEK,aAAa,CAAC,KAAK,EAAE;QACzB,MAAM,EAAE,UAAU,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB;;;;;;;OAUA;IAEK,cAAc,CAAC,KAAK,EAAE;QAC1B,MAAM,EAAE,UAAU,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB;;;;;;;;;;;OAoBA;IAEK,iBAAiB,CAAC,KAAK,EAAE;QAC7B,MAAM,EAAE,UAAU,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;KACvB;;;;;;;;;;OAyCA;IAED,OAAO,CAAC,WAAW;YAkCL,SAAS;YAmFT,kBAAkB;YAsBlB,OAAO;IAWrB,OAAO,CAAC,mBAAmB;IA2B3B,OAAO,CAAC,SAAS;CAQlB"}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { Document, NodeIO } from "@gltf-transform/core";
|
|
2
|
+
import { HttpRangeReader, Uint8ArrayWriter, ZipReader, } from "@zip.js/zip.js";
|
|
3
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
+
import { link, mkdir, open, readFile, stat, unlink, } from "node:fs/promises";
|
|
5
|
+
import { basename, join, resolve, sep } from "node:path";
|
|
6
|
+
export class BundleAssetError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
retryable;
|
|
9
|
+
constructor(code, message, retryable = false) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.retryable = retryable;
|
|
13
|
+
this.name = "BundleAssetError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function sourceHash(value) {
|
|
17
|
+
return createHash("sha256").update(value).digest("hex");
|
|
18
|
+
}
|
|
19
|
+
function sha256(value) {
|
|
20
|
+
return createHash("sha256").update(value).digest("hex");
|
|
21
|
+
}
|
|
22
|
+
function safeFileName(value) {
|
|
23
|
+
const cleaned = value
|
|
24
|
+
.replace(/[\\/:*?"<>|\x00-\x1f]/g, "_")
|
|
25
|
+
.replace(/\s+/g, " ")
|
|
26
|
+
.trim()
|
|
27
|
+
.slice(0, 180);
|
|
28
|
+
if (!cleaned || cleaned === "." || cleaned === "..") {
|
|
29
|
+
throw new BundleAssetError("BUNDLE_UNSAFE_PATH", "The selected bundle entry does not have a safe filename.");
|
|
30
|
+
}
|
|
31
|
+
return cleaned;
|
|
32
|
+
}
|
|
33
|
+
function contained(root, ...parts) {
|
|
34
|
+
const absoluteRoot = resolve(root);
|
|
35
|
+
const destination = resolve(absoluteRoot, ...parts);
|
|
36
|
+
if (destination !== absoluteRoot &&
|
|
37
|
+
!destination.startsWith(`${absoluteRoot}${sep}`)) {
|
|
38
|
+
throw new BundleAssetError("BUNDLE_UNSAFE_PATH", "The selected destination escapes guarded bundle storage.");
|
|
39
|
+
}
|
|
40
|
+
return destination;
|
|
41
|
+
}
|
|
42
|
+
async function writeAll(file, value) {
|
|
43
|
+
let offset = 0;
|
|
44
|
+
while (offset < value.byteLength) {
|
|
45
|
+
const result = await file.write(value, offset, value.byteLength - offset);
|
|
46
|
+
if (result.bytesWritten <= 0)
|
|
47
|
+
throw new Error("Unable to write bundle output.");
|
|
48
|
+
offset += result.bytesWritten;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function publishBytes(path, bytes) {
|
|
52
|
+
await mkdir(resolve(path, ".."), { recursive: true });
|
|
53
|
+
try {
|
|
54
|
+
const existing = await stat(path);
|
|
55
|
+
if (existing.isFile()) {
|
|
56
|
+
const existingBytes = new Uint8Array(await readFile(path));
|
|
57
|
+
if (sha256(existingBytes) !== sha256(bytes)) {
|
|
58
|
+
throw new BundleAssetError("BUNDLE_UNSAFE_PATH", "The bundle destination already contains different bytes.");
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
throw new BundleAssetError("BUNDLE_UNSAFE_PATH", "The bundle destination already exists and is not a regular file.");
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (error.code !== "ENOENT")
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
const temporary = `${path}.part-${process.pid}-${randomUUID()}`;
|
|
69
|
+
let handle;
|
|
70
|
+
try {
|
|
71
|
+
handle = await open(temporary, "wx", 0o600);
|
|
72
|
+
await writeAll(handle, bytes);
|
|
73
|
+
await handle.sync();
|
|
74
|
+
await handle.close();
|
|
75
|
+
handle = undefined;
|
|
76
|
+
try {
|
|
77
|
+
await link(temporary, path);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (error.code === "EEXIST") {
|
|
82
|
+
const existingBytes = new Uint8Array(await readFile(path));
|
|
83
|
+
if (sha256(existingBytes) !== sha256(bytes)) {
|
|
84
|
+
throw new BundleAssetError("BUNDLE_UNSAFE_PATH", "A concurrent bundle write published different bytes.");
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
await handle?.close().catch(() => undefined);
|
|
93
|
+
await unlink(temporary).catch(() => undefined);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export class BundleAssetClient {
|
|
97
|
+
itch;
|
|
98
|
+
fetch;
|
|
99
|
+
downloadDir;
|
|
100
|
+
timeoutMs;
|
|
101
|
+
maxEntryBytes;
|
|
102
|
+
io = new NodeIO();
|
|
103
|
+
constructor(options) {
|
|
104
|
+
this.itch = options.itch;
|
|
105
|
+
this.fetch = options.fetch ?? globalThis.fetch;
|
|
106
|
+
this.downloadDir =
|
|
107
|
+
options.downloadDir ??
|
|
108
|
+
process.env.ASSET_DOWNLOAD_DIR ??
|
|
109
|
+
join(process.env.HOME ?? process.cwd(), "Downloads", "threenative-asset-mcp", "assets");
|
|
110
|
+
this.timeoutMs = options.timeoutMs ?? 30_000;
|
|
111
|
+
this.maxEntryBytes = options.maxEntryBytes ?? 512 * 1024 * 1024;
|
|
112
|
+
}
|
|
113
|
+
async listEntries(packId, uploadId) {
|
|
114
|
+
const resolved = await this.itch.resolveUpload(packId, uploadId);
|
|
115
|
+
const archive = this.openArchive(resolved.signedFileUrl);
|
|
116
|
+
try {
|
|
117
|
+
const entries = await archive.reader.getEntries();
|
|
118
|
+
return {
|
|
119
|
+
pack: resolved.pack,
|
|
120
|
+
upload: resolved.upload,
|
|
121
|
+
entries: entries.map((entry) => this.entryInfo(entry)),
|
|
122
|
+
rangeBytesTransferred: archive.transferred(),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
await archive.reader.close();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async downloadEntry(input) {
|
|
130
|
+
const result = await this.readEntry(input);
|
|
131
|
+
return {
|
|
132
|
+
path: result.path,
|
|
133
|
+
entryPath: input.entryPath,
|
|
134
|
+
sizeBytes: result.bytes.byteLength,
|
|
135
|
+
sha256: sha256(result.bytes),
|
|
136
|
+
alreadyCached: result.alreadyCached,
|
|
137
|
+
rangeBytesTransferred: result.rangeBytesTransferred,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
async listAnimations(input) {
|
|
141
|
+
const selectedPath = input.entryPath ?? (await this.selectAggregateGlb(input.packId, input.uploadId));
|
|
142
|
+
const aggregate = await this.readEntry({ ...input, entryPath: selectedPath });
|
|
143
|
+
const document = await this.readGlb(aggregate.bytes);
|
|
144
|
+
return {
|
|
145
|
+
entryPath: selectedPath,
|
|
146
|
+
aggregateEntryBytes: aggregate.bytes.byteLength,
|
|
147
|
+
alreadyCached: aggregate.alreadyCached,
|
|
148
|
+
rangeBytesTransferred: aggregate.rangeBytesTransferred,
|
|
149
|
+
animations: document
|
|
150
|
+
.getRoot()
|
|
151
|
+
.listAnimations()
|
|
152
|
+
.map((animation, index) => ({
|
|
153
|
+
index,
|
|
154
|
+
name: animation.getName() || `animation-${index}`,
|
|
155
|
+
channels: animation.listChannels().length,
|
|
156
|
+
samplers: animation.listSamplers().length,
|
|
157
|
+
})),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
async downloadAnimation(input) {
|
|
161
|
+
const selectedPath = input.entryPath ?? (await this.selectAggregateGlb(input.packId, input.uploadId));
|
|
162
|
+
const aggregate = await this.readEntry({ ...input, entryPath: selectedPath });
|
|
163
|
+
const document = await this.readGlb(aggregate.bytes);
|
|
164
|
+
const animations = document.getRoot().listAnimations();
|
|
165
|
+
const selected = animations.find((animation, index) => animation.getName() === input.animationName ||
|
|
166
|
+
String(index) === input.animationName);
|
|
167
|
+
if (!selected) {
|
|
168
|
+
throw new BundleAssetError("BUNDLE_ANIMATION_NOT_FOUND", `Animation ${input.animationName} is not present in ${selectedPath}.`);
|
|
169
|
+
}
|
|
170
|
+
this.retainAnimationOnly(document, selected);
|
|
171
|
+
const output = await this.io.writeBinary(document);
|
|
172
|
+
const outputPath = contained(this.downloadDir, "bundles", input.packId, input.uploadId, "animations", sourceHash(selectedPath), `${safeFileName(selected.getName() || `animation-${animations.indexOf(selected)}`)}.glb`);
|
|
173
|
+
const alreadyExisted = await publishBytes(outputPath, output);
|
|
174
|
+
return {
|
|
175
|
+
path: outputPath,
|
|
176
|
+
animationName: selected.getName() || input.animationName,
|
|
177
|
+
entryPath: selectedPath,
|
|
178
|
+
sizeBytes: output.byteLength,
|
|
179
|
+
aggregateEntryBytes: aggregate.bytes.byteLength,
|
|
180
|
+
sha256: sha256(output),
|
|
181
|
+
alreadyExisted,
|
|
182
|
+
aggregateAlreadyCached: aggregate.alreadyCached,
|
|
183
|
+
rangeBytesTransferred: aggregate.rangeBytesTransferred,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
openArchive(signedFileUrl) {
|
|
187
|
+
let transferred = 0;
|
|
188
|
+
const trackedFetch = async (input, init) => {
|
|
189
|
+
const response = await this.fetch(input, {
|
|
190
|
+
...init,
|
|
191
|
+
redirect: "error",
|
|
192
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
193
|
+
headers: {
|
|
194
|
+
...Object.fromEntries(new Headers(init?.headers).entries()),
|
|
195
|
+
"user-agent": "threenative-asset-mcp/0.4.0",
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
if (!response.ok) {
|
|
199
|
+
throw new BundleAssetError("BUNDLE_UPSTREAM_DENIED", `Bundle range request returned HTTP ${response.status}.`, response.status === 429 || response.status >= 500);
|
|
200
|
+
}
|
|
201
|
+
if (init?.method !== "HEAD") {
|
|
202
|
+
const length = Number(response.headers.get("content-length"));
|
|
203
|
+
if (Number.isFinite(length) && length > 0)
|
|
204
|
+
transferred += length;
|
|
205
|
+
}
|
|
206
|
+
return response;
|
|
207
|
+
};
|
|
208
|
+
const source = new HttpRangeReader(signedFileUrl, {
|
|
209
|
+
fetch: trackedFetch,
|
|
210
|
+
});
|
|
211
|
+
return {
|
|
212
|
+
reader: new ZipReader(source),
|
|
213
|
+
transferred: () => transferred,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
async readEntry(input) {
|
|
217
|
+
const outputPath = contained(this.downloadDir, "bundles", input.packId, input.uploadId, "entries", sourceHash(input.entryPath), safeFileName(basename(input.entryPath)));
|
|
218
|
+
try {
|
|
219
|
+
const bytes = new Uint8Array(await readFile(outputPath));
|
|
220
|
+
if (bytes.byteLength > this.maxEntryBytes) {
|
|
221
|
+
throw new BundleAssetError("BUNDLE_ENTRY_TOO_LARGE", "The cached bundle entry exceeds the configured byte limit.");
|
|
222
|
+
}
|
|
223
|
+
const expectedHash = await readFile(`${outputPath}.sha256`, "utf8").catch((error) => {
|
|
224
|
+
if (error.code === "ENOENT")
|
|
225
|
+
return undefined;
|
|
226
|
+
throw error;
|
|
227
|
+
});
|
|
228
|
+
if (expectedHash?.trim() === sha256(bytes)) {
|
|
229
|
+
return {
|
|
230
|
+
bytes,
|
|
231
|
+
path: outputPath,
|
|
232
|
+
alreadyCached: true,
|
|
233
|
+
rangeBytesTransferred: 0,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
if (error.code !== "ENOENT")
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
const resolved = await this.itch.resolveUpload(input.packId, input.uploadId);
|
|
242
|
+
const archive = this.openArchive(resolved.signedFileUrl);
|
|
243
|
+
try {
|
|
244
|
+
const entries = await archive.reader.getEntries();
|
|
245
|
+
const entry = entries.find((candidate) => !candidate.directory && candidate.filename === input.entryPath);
|
|
246
|
+
if (!entry || entry.directory) {
|
|
247
|
+
throw new BundleAssetError("BUNDLE_ENTRY_NOT_FOUND", `Bundle entry ${input.entryPath} was not found.`);
|
|
248
|
+
}
|
|
249
|
+
const expectedSize = entry.uncompressedSize ?? 0;
|
|
250
|
+
if (expectedSize > this.maxEntryBytes) {
|
|
251
|
+
throw new BundleAssetError("BUNDLE_ENTRY_TOO_LARGE", `Bundle entry requires ${expectedSize} bytes, above the configured limit.`);
|
|
252
|
+
}
|
|
253
|
+
const bytes = await entry.getData(new Uint8ArrayWriter());
|
|
254
|
+
if (bytes.byteLength > this.maxEntryBytes) {
|
|
255
|
+
throw new BundleAssetError("BUNDLE_ENTRY_TOO_LARGE", "The extracted bundle entry exceeds the configured byte limit.");
|
|
256
|
+
}
|
|
257
|
+
const alreadyCached = await publishBytes(outputPath, bytes);
|
|
258
|
+
await publishBytes(`${outputPath}.sha256`, new TextEncoder().encode(`${sha256(bytes)}\n`));
|
|
259
|
+
return {
|
|
260
|
+
bytes,
|
|
261
|
+
path: outputPath,
|
|
262
|
+
alreadyCached,
|
|
263
|
+
rangeBytesTransferred: archive.transferred(),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
await archive.reader.close();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
async selectAggregateGlb(packId, uploadId) {
|
|
271
|
+
const result = await this.listEntries(packId, uploadId);
|
|
272
|
+
const candidates = result.entries.filter((entry) => !entry.directory && entry.path.toLowerCase().endsWith(".glb"));
|
|
273
|
+
const preferred = candidates.find((entry) => /standard\.glb$/i.test(entry.path) && !/_rm\.glb$/i.test(entry.path)) ?? candidates.find((entry) => !/_rm\.glb$/i.test(entry.path));
|
|
274
|
+
if (!preferred) {
|
|
275
|
+
throw new BundleAssetError("BUNDLE_ENTRY_NOT_FOUND", "The selected bundle does not contain a GLB animation library.");
|
|
276
|
+
}
|
|
277
|
+
return preferred.path;
|
|
278
|
+
}
|
|
279
|
+
async readGlb(bytes) {
|
|
280
|
+
try {
|
|
281
|
+
return await this.io.readBinary(bytes);
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
throw new BundleAssetError("BUNDLE_INVALID_GLTF", "The selected bundle entry is not a readable GLB asset.");
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
retainAnimationOnly(document, selected) {
|
|
288
|
+
const retainedAccessors = new Set(selected
|
|
289
|
+
.listSamplers()
|
|
290
|
+
.flatMap((sampler) => [sampler.getInput(), sampler.getOutput()])
|
|
291
|
+
.filter((accessor) => Boolean(accessor)));
|
|
292
|
+
for (const animation of document.getRoot().listAnimations()) {
|
|
293
|
+
if (animation !== selected)
|
|
294
|
+
animation.dispose();
|
|
295
|
+
}
|
|
296
|
+
for (const node of document.getRoot().listNodes()) {
|
|
297
|
+
node.setMesh(null);
|
|
298
|
+
node.setSkin(null);
|
|
299
|
+
}
|
|
300
|
+
for (const accessor of document.getRoot().listAccessors()) {
|
|
301
|
+
if (!retainedAccessors.has(accessor))
|
|
302
|
+
accessor.dispose();
|
|
303
|
+
}
|
|
304
|
+
for (const mesh of document.getRoot().listMeshes())
|
|
305
|
+
mesh.dispose();
|
|
306
|
+
for (const material of document.getRoot().listMaterials())
|
|
307
|
+
material.dispose();
|
|
308
|
+
for (const texture of document.getRoot().listTextures())
|
|
309
|
+
texture.dispose();
|
|
310
|
+
for (const skin of document.getRoot().listSkins())
|
|
311
|
+
skin.dispose();
|
|
312
|
+
for (const camera of document.getRoot().listCameras())
|
|
313
|
+
camera.dispose();
|
|
314
|
+
}
|
|
315
|
+
entryInfo(entry) {
|
|
316
|
+
return {
|
|
317
|
+
path: entry.filename,
|
|
318
|
+
compressedBytes: entry.compressedSize ?? 0,
|
|
319
|
+
uncompressedBytes: entry.uncompressedSize ?? 0,
|
|
320
|
+
directory: entry.directory,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/bundle/client.ts"],"names":[],"mappings":"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"}
|