@ai-matrx/data 0.15.1 → 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/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);