@ai-matrx/data 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/files.cjs +175 -0
- package/dist/files.cjs.map +1 -1
- package/dist/files.d.cts +31 -0
- package/dist/files.d.ts +31 -0
- package/dist/files.js +175 -0
- package/dist/files.js.map +1 -1
- package/package.json +3 -3
package/dist/files.js
CHANGED
|
@@ -297,12 +297,165 @@ function createFilesSession(options) {
|
|
|
297
297
|
}
|
|
298
298
|
return { secretSource, status };
|
|
299
299
|
},
|
|
300
|
+
bases() {
|
|
301
|
+
return [...new Set(options.resolveBases())];
|
|
302
|
+
},
|
|
300
303
|
reset() {
|
|
301
304
|
stateByBase.clear();
|
|
302
305
|
}
|
|
303
306
|
};
|
|
304
307
|
}
|
|
305
308
|
|
|
309
|
+
// src/files/heal.ts
|
|
310
|
+
function originOf(base) {
|
|
311
|
+
try {
|
|
312
|
+
return new URL(base).origin;
|
|
313
|
+
} catch {
|
|
314
|
+
return base;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
async function runByteLane(lane, base, input) {
|
|
318
|
+
const now = input.now ?? Date.now;
|
|
319
|
+
const started = now();
|
|
320
|
+
const host = originOf(base);
|
|
321
|
+
if (!input.fileId) {
|
|
322
|
+
return {
|
|
323
|
+
attempt: {
|
|
324
|
+
lane,
|
|
325
|
+
outcome: "skipped",
|
|
326
|
+
ms: 0,
|
|
327
|
+
host,
|
|
328
|
+
detail: "no file id \u2014 the source is not one of our durable file URLs"
|
|
329
|
+
},
|
|
330
|
+
src: null
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
let response;
|
|
334
|
+
try {
|
|
335
|
+
response = await input.fetchBytes(base, input.fileId);
|
|
336
|
+
} catch (err) {
|
|
337
|
+
return {
|
|
338
|
+
attempt: {
|
|
339
|
+
lane,
|
|
340
|
+
outcome: "network-error",
|
|
341
|
+
ms: now() - started,
|
|
342
|
+
host,
|
|
343
|
+
detail: err instanceof Error ? err.message : String(err ?? "fetch failed")
|
|
344
|
+
},
|
|
345
|
+
src: null
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
if (!response.ok) {
|
|
349
|
+
return {
|
|
350
|
+
attempt: {
|
|
351
|
+
lane,
|
|
352
|
+
outcome: "http-error",
|
|
353
|
+
status: response.status,
|
|
354
|
+
ms: now() - started,
|
|
355
|
+
host,
|
|
356
|
+
detail: `GET /files/{id}/download \u2192 HTTP ${response.status}`
|
|
357
|
+
},
|
|
358
|
+
src: null
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
const blob = await response.blob();
|
|
363
|
+
const src = input.cacheBlob(input.fileId, blob);
|
|
364
|
+
return {
|
|
365
|
+
attempt: {
|
|
366
|
+
lane,
|
|
367
|
+
outcome: "ok",
|
|
368
|
+
status: response.status,
|
|
369
|
+
ms: now() - started,
|
|
370
|
+
host,
|
|
371
|
+
detail: `${blob.size} bytes via bearer token \u2192 object URL`
|
|
372
|
+
},
|
|
373
|
+
src
|
|
374
|
+
};
|
|
375
|
+
} catch (err) {
|
|
376
|
+
return {
|
|
377
|
+
attempt: {
|
|
378
|
+
lane,
|
|
379
|
+
outcome: "network-error",
|
|
380
|
+
ms: now() - started,
|
|
381
|
+
host,
|
|
382
|
+
detail: `body read failed: ${err instanceof Error ? err.message : String(err)}`
|
|
383
|
+
},
|
|
384
|
+
src: null
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function diagnoseHeal(attempts, mintStatus) {
|
|
389
|
+
const lane1 = attempts.find((a) => a.lane === "session-refresh-retry");
|
|
390
|
+
const bearer = attempts.find((a) => a.lane === "bearer-blob");
|
|
391
|
+
const alternate = attempts.find((a) => a.lane === "bearer-blob-alternate-host");
|
|
392
|
+
const healedBy = attempts.find((a) => a.outcome === "ok");
|
|
393
|
+
if (healedBy?.lane === "bearer-blob") {
|
|
394
|
+
if (lane1 && mintStatus === void 0) return "cookie-not-sent-by-browser";
|
|
395
|
+
if (mintStatus !== void 0) return "cookie-mint-failed";
|
|
396
|
+
return "unknown";
|
|
397
|
+
}
|
|
398
|
+
if (healedBy?.lane === "bearer-blob-alternate-host") {
|
|
399
|
+
if (bearer && (bearer.outcome === "network-error" || (bearer.status ?? 0) >= 500)) {
|
|
400
|
+
return "primary-host-down-alternate-served";
|
|
401
|
+
}
|
|
402
|
+
return "unknown";
|
|
403
|
+
}
|
|
404
|
+
const byteLanes = [bearer, alternate].filter(Boolean);
|
|
405
|
+
if (byteLanes.some((a) => a.status === 401)) return "bearer-rejected";
|
|
406
|
+
if (byteLanes.some((a) => a.status === 403)) return "access-denied";
|
|
407
|
+
if (byteLanes.some((a) => a.status === 404)) return "file-missing";
|
|
408
|
+
if (byteLanes.some((a) => (a.status ?? 0) >= 500)) return "files-host-error";
|
|
409
|
+
if (byteLanes.length > 0 && byteLanes.every((a) => a.outcome === "network-error")) {
|
|
410
|
+
return "files-host-unreachable";
|
|
411
|
+
}
|
|
412
|
+
if (mintStatus !== void 0) return "cookie-mint-failed";
|
|
413
|
+
return "unknown";
|
|
414
|
+
}
|
|
415
|
+
var SUMMARY = {
|
|
416
|
+
"cookie-not-sent-by-browser": "the files-session cookie was minted (200) but the browser did not send it back on the media element request \u2014 third-party cookies are blocked in this browser (Chrome setting / Incognito, Safari, Brave)",
|
|
417
|
+
"cookie-mint-failed": "the files-session cookie mint itself failed",
|
|
418
|
+
"bearer-rejected": "the bearer token was refused (401): the sign-in on this tab is stale",
|
|
419
|
+
"access-denied": "this identity may not read this file (403)",
|
|
420
|
+
"file-missing": "the file is gone (404)",
|
|
421
|
+
"files-host-error": "the files host answered with a server error (5xx)",
|
|
422
|
+
"files-host-unreachable": "no files host answered (network/DNS/CORS)",
|
|
423
|
+
"primary-host-down-alternate-served": "the primary files host did not serve the bytes; the alternate host did",
|
|
424
|
+
unknown: "the attempt pattern matches no known cause"
|
|
425
|
+
};
|
|
426
|
+
async function healLoadErrorLadder(input) {
|
|
427
|
+
const attempts = [...input.priorAttempts];
|
|
428
|
+
let src = null;
|
|
429
|
+
let healedBy;
|
|
430
|
+
const primary = await runByteLane("bearer-blob", input.primaryBase, input);
|
|
431
|
+
attempts.push(primary.attempt);
|
|
432
|
+
if (primary.src) {
|
|
433
|
+
src = primary.src;
|
|
434
|
+
healedBy = "bearer-blob";
|
|
435
|
+
} else {
|
|
436
|
+
for (const base of input.alternateBases) {
|
|
437
|
+
const alt = await runByteLane("bearer-blob-alternate-host", base, input);
|
|
438
|
+
attempts.push(alt.attempt);
|
|
439
|
+
if (alt.src) {
|
|
440
|
+
src = alt.src;
|
|
441
|
+
healedBy = "bearer-blob-alternate-host";
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const diagnosis = diagnoseHeal(attempts, input.mintStatus);
|
|
447
|
+
const cause = SUMMARY[diagnosis];
|
|
448
|
+
const served = healedBy ? `served via ${healedBy} in ${attempts.find((a) => a.outcome === "ok")?.ms ?? 0}ms` : `every lane died (${attempts.map((a) => `${a.lane}:${a.outcome}${a.status ? ` ${a.status}` : ""}`).join(", ")})`;
|
|
449
|
+
const ifMint = input.mintStatus !== void 0 ? ` (last cookie mint status ${input.mintStatus})` : "";
|
|
450
|
+
return {
|
|
451
|
+
src,
|
|
452
|
+
healedBy,
|
|
453
|
+
attempts,
|
|
454
|
+
diagnosis,
|
|
455
|
+
summary: `${cause}${ifMint}; ${served}`
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
306
459
|
// src/files/client.ts
|
|
307
460
|
var TUS_TRANSPORT_THRESHOLD_BYTES = 80 * 1024 * 1024;
|
|
308
461
|
function resolveUploadTransport(fileSizeBytes, override) {
|
|
@@ -854,6 +1007,28 @@ function createMatrxFilesClient(config) {
|
|
|
854
1007
|
await session.ensure({ force: true });
|
|
855
1008
|
return finish("retry");
|
|
856
1009
|
},
|
|
1010
|
+
async healLoadError(ref, context) {
|
|
1011
|
+
const norm = normalizeRef(ref);
|
|
1012
|
+
const fileId = norm?.kind === "file_id" ? norm.fileId : norm?.kind === "url" ? recognizer.fileIdFromFileEndpointUrl(norm.url) : null;
|
|
1013
|
+
const primary = filesBase();
|
|
1014
|
+
return healLoadErrorLadder({
|
|
1015
|
+
fileId,
|
|
1016
|
+
priorAttempts: context.attempts,
|
|
1017
|
+
primaryBase: primary,
|
|
1018
|
+
alternateBases: session.bases().filter((b) => b !== primary),
|
|
1019
|
+
mintStatus: session.snapshot().status,
|
|
1020
|
+
fetchBytes: async (base, id) => {
|
|
1021
|
+
const headers = await authHeaders();
|
|
1022
|
+
return fetchImpl(fileUrls(base, id).download, { headers });
|
|
1023
|
+
},
|
|
1024
|
+
cacheBlob: (id, blob) => {
|
|
1025
|
+
const already = blobCache.get(id);
|
|
1026
|
+
const objectUrl = already?.url ?? URL.createObjectURL(blob);
|
|
1027
|
+
if (!already) blobCache.set(id, blob, objectUrl, { mimeType: blob.type });
|
|
1028
|
+
return objectUrl;
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
},
|
|
857
1032
|
async recoverBlobLoadError(ref, attempt) {
|
|
858
1033
|
if (attempt > 1) return "terminal";
|
|
859
1034
|
const norm = normalizeRef(ref);
|