@org-quicko/silo-client 1.0.1 → 1.1.1
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 +49 -1
- package/dist/cache/cache-mode.d.cts +2 -0
- package/dist/cache/cache-mode.d.ts +2 -0
- package/dist/cache/response-cache.d.cts +16 -0
- package/dist/cache/response-cache.d.ts +16 -0
- package/dist/cache/silo-cache-options.d.cts +7 -0
- package/dist/cache/silo-cache-options.d.ts +7 -0
- package/dist/cache/silo-cache.d.cts +11 -0
- package/dist/cache/silo-cache.d.ts +11 -0
- package/dist/errors/network-error.d.cts +5 -0
- package/dist/errors/network-error.d.ts +5 -0
- package/dist/index.cjs +264 -74
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +264 -74
- package/dist/media/media-asset.d.cts +17 -0
- package/dist/media/media-asset.d.ts +17 -0
- package/dist/media/media-file.d.cts +16 -0
- package/dist/media/media-file.d.ts +16 -0
- package/dist/media/media-page.d.cts +1 -0
- package/dist/media/media-page.d.ts +1 -0
- package/dist/media/media-replace.d.cts +17 -0
- package/dist/media/media-replace.d.ts +17 -0
- package/dist/media/media-usage-page.d.cts +1 -0
- package/dist/media/media-usage-page.d.ts +1 -0
- package/dist/media/media.d.cts +0 -4
- package/dist/media/media.d.ts +0 -4
- package/dist/request-options.d.cts +3 -0
- package/dist/request-options.d.ts +3 -0
- package/dist/silo-options.d.cts +4 -0
- package/dist/silo-options.d.ts +4 -0
- package/dist/silo.d.cts +3 -0
- package/dist/silo.d.ts +3 -0
- package/dist/transport/api-path.d.cts +1 -0
- package/dist/transport/api-path.d.ts +1 -0
- package/dist/transport/caching-transport.d.cts +14 -0
- package/dist/transport/caching-transport.d.ts +14 -0
- package/dist/transport/fetch-transport.d.cts +31 -0
- package/dist/transport/fetch-transport.d.ts +31 -0
- package/dist/transport/prepared-transport-request.d.cts +6 -0
- package/dist/transport/prepared-transport-request.d.ts +6 -0
- package/dist/transport/transport-request.d.cts +2 -0
- package/dist/transport/transport-request.d.ts +2 -0
- package/dist/transport/transport.d.cts +2 -39
- package/dist/transport/transport.d.ts +2 -39
- package/package.json +6 -3
package/dist/index.cjs
CHANGED
|
@@ -47,6 +47,7 @@ __export(exports_src, {
|
|
|
47
47
|
SortTerm: () => SortTerm,
|
|
48
48
|
Sort: () => Sort,
|
|
49
49
|
SiloError: () => SiloError,
|
|
50
|
+
SiloCache: () => SiloCache,
|
|
50
51
|
Silo: () => Silo,
|
|
51
52
|
SearchReach: () => SearchReach,
|
|
52
53
|
SearchPage: () => SearchPage,
|
|
@@ -181,6 +182,9 @@ class ApiPath {
|
|
|
181
182
|
static mediaAssetUsages(id) {
|
|
182
183
|
return `${ApiPath.mediaAsset(id)}/usages`;
|
|
183
184
|
}
|
|
185
|
+
static mediaAssetContent(id) {
|
|
186
|
+
return `${ApiPath.mediaAsset(id)}/content`;
|
|
187
|
+
}
|
|
184
188
|
static mediaBulkDelete() {
|
|
185
189
|
return "/api/media/delete";
|
|
186
190
|
}
|
|
@@ -227,6 +231,22 @@ class MediaAssetMapper {
|
|
|
227
231
|
}
|
|
228
232
|
}
|
|
229
233
|
|
|
234
|
+
// src/media/media-file.ts
|
|
235
|
+
class MediaFile {
|
|
236
|
+
static toBlob(bytes, contentType) {
|
|
237
|
+
if (bytes instanceof Blob)
|
|
238
|
+
return bytes;
|
|
239
|
+
return new Blob([bytes], contentType ? { type: contentType } : undefined);
|
|
240
|
+
}
|
|
241
|
+
static nameOf(input, explicit) {
|
|
242
|
+
if (explicit)
|
|
243
|
+
return explicit;
|
|
244
|
+
if (input instanceof File && input.name)
|
|
245
|
+
return input.name;
|
|
246
|
+
throw new Error("media: a Blob has no filename — pass { filename } explicitly");
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
230
250
|
// src/media/media-reference.ts
|
|
231
251
|
class MediaReference {
|
|
232
252
|
static Scheme = "silo://media/";
|
|
@@ -286,12 +306,14 @@ var DefaultUsageLimit = 50;
|
|
|
286
306
|
class MediaUsagePage extends Page {
|
|
287
307
|
transport;
|
|
288
308
|
assetId;
|
|
309
|
+
requestOptions;
|
|
289
310
|
visible;
|
|
290
311
|
visibleCapped;
|
|
291
|
-
constructor(rows, total, visible, visibleCapped, window, transport, assetId) {
|
|
312
|
+
constructor(rows, total, visible, visibleCapped, window, transport, assetId, requestOptions) {
|
|
292
313
|
super(rows, total, window);
|
|
293
314
|
this.transport = transport;
|
|
294
315
|
this.assetId = assetId;
|
|
316
|
+
this.requestOptions = requestOptions;
|
|
295
317
|
this.visible = visible;
|
|
296
318
|
this.visibleCapped = visibleCapped;
|
|
297
319
|
}
|
|
@@ -306,11 +328,11 @@ class MediaUsagePage extends Page {
|
|
|
306
328
|
}
|
|
307
329
|
async next(options) {
|
|
308
330
|
const window = this.windowForNext();
|
|
309
|
-
return window ? MediaUsagePage.loadWindow(this.transport, this.assetId, window, options) : null;
|
|
331
|
+
return window ? MediaUsagePage.loadWindow(this.transport, this.assetId, window, { ...this.requestOptions, ...options }) : null;
|
|
310
332
|
}
|
|
311
333
|
async previous(options) {
|
|
312
334
|
const window = this.window.previous();
|
|
313
|
-
return window ? MediaUsagePage.loadWindow(this.transport, this.assetId, window, options) : null;
|
|
335
|
+
return window ? MediaUsagePage.loadWindow(this.transport, this.assetId, window, { ...this.requestOptions, ...options }) : null;
|
|
314
336
|
}
|
|
315
337
|
static load(transport, assetId, query, options) {
|
|
316
338
|
const window = new PageWindow(query.limit ?? DefaultUsageLimit, query.offset ?? 0);
|
|
@@ -322,10 +344,11 @@ class MediaUsagePage extends Page {
|
|
|
322
344
|
path: ApiPath.mediaAssetUsages(assetId),
|
|
323
345
|
query: { limit: window.limit, offset: window.offset },
|
|
324
346
|
signal: options?.signal,
|
|
325
|
-
timeoutMilliseconds: options?.timeoutMilliseconds
|
|
347
|
+
timeoutMilliseconds: options?.timeoutMilliseconds,
|
|
348
|
+
cache: options?.cache
|
|
326
349
|
});
|
|
327
350
|
const mapped = MediaAssetMapper.toUsagePage(body);
|
|
328
|
-
return new MediaUsagePage(mapped.items, mapped.total, mapped.visible, mapped.visibleCapped, window, transport, assetId);
|
|
351
|
+
return new MediaUsagePage(mapped.items, mapped.total, mapped.visible, mapped.visibleCapped, window, transport, assetId, options);
|
|
329
352
|
}
|
|
330
353
|
}
|
|
331
354
|
|
|
@@ -388,6 +411,22 @@ class MediaAsset {
|
|
|
388
411
|
async setTags(tags, options) {
|
|
389
412
|
return this.patch({ tags: [...tags] }, options);
|
|
390
413
|
}
|
|
414
|
+
async replace(input, options) {
|
|
415
|
+
const form = new FormData;
|
|
416
|
+
if (input instanceof Blob) {
|
|
417
|
+
form.set("file", input, MediaFile.nameOf(input, options?.filename));
|
|
418
|
+
} else {
|
|
419
|
+
form.set("file", MediaFile.toBlob(input.bytes, input.contentType), input.filename);
|
|
420
|
+
}
|
|
421
|
+
const payload = await this.transport.upload({
|
|
422
|
+
method: "POST",
|
|
423
|
+
path: ApiPath.mediaAssetContent(this.id),
|
|
424
|
+
signal: options?.signal,
|
|
425
|
+
timeoutMilliseconds: options?.timeoutMilliseconds
|
|
426
|
+
}, form);
|
|
427
|
+
this.record = MediaAssetMapper.toRecord(payload);
|
|
428
|
+
return this;
|
|
429
|
+
}
|
|
391
430
|
async delete(options) {
|
|
392
431
|
await this.transport.empty({
|
|
393
432
|
method: "DELETE",
|
|
@@ -405,7 +444,8 @@ class MediaAsset {
|
|
|
405
444
|
method: "GET",
|
|
406
445
|
path: ApiPath.mediaAsset(this.id),
|
|
407
446
|
signal: options?.signal,
|
|
408
|
-
timeoutMilliseconds: options?.timeoutMilliseconds
|
|
447
|
+
timeoutMilliseconds: options?.timeoutMilliseconds,
|
|
448
|
+
cache: options?.cache ?? "refresh"
|
|
409
449
|
});
|
|
410
450
|
this.record = MediaAssetMapper.toRecord(payload);
|
|
411
451
|
return this;
|
|
@@ -467,7 +507,8 @@ class MediaFolders {
|
|
|
467
507
|
method: "GET",
|
|
468
508
|
path: ApiPath.mediaFolders(),
|
|
469
509
|
signal: options?.signal,
|
|
470
|
-
timeoutMilliseconds: options?.timeoutMilliseconds
|
|
510
|
+
timeoutMilliseconds: options?.timeoutMilliseconds,
|
|
511
|
+
cache: options?.cache
|
|
471
512
|
});
|
|
472
513
|
return body.items;
|
|
473
514
|
}
|
|
@@ -509,21 +550,23 @@ class MediaFolders {
|
|
|
509
550
|
class MediaPage extends Page {
|
|
510
551
|
transport;
|
|
511
552
|
wireQuery;
|
|
512
|
-
|
|
553
|
+
requestOptions;
|
|
554
|
+
constructor(rows, total, window, transport, wireQuery, requestOptions) {
|
|
513
555
|
super(rows, total, window);
|
|
514
556
|
this.transport = transport;
|
|
515
557
|
this.wireQuery = wireQuery;
|
|
558
|
+
this.requestOptions = requestOptions;
|
|
516
559
|
}
|
|
517
560
|
get files() {
|
|
518
561
|
return this.rows;
|
|
519
562
|
}
|
|
520
563
|
async next(options) {
|
|
521
564
|
const window = this.windowForNext();
|
|
522
|
-
return window ? MediaPage.loadWindow(this.transport, this.wireQuery, window, options) : null;
|
|
565
|
+
return window ? MediaPage.loadWindow(this.transport, this.wireQuery, window, { ...this.requestOptions, ...options }) : null;
|
|
523
566
|
}
|
|
524
567
|
async previous(options) {
|
|
525
568
|
const window = this.window.previous();
|
|
526
|
-
return window ? MediaPage.loadWindow(this.transport, this.wireQuery, window, options) : null;
|
|
569
|
+
return window ? MediaPage.loadWindow(this.transport, this.wireQuery, window, { ...this.requestOptions, ...options }) : null;
|
|
527
570
|
}
|
|
528
571
|
static async loadWindow(transport, wireQuery, window, options) {
|
|
529
572
|
const body = await transport.json({
|
|
@@ -531,10 +574,11 @@ class MediaPage extends Page {
|
|
|
531
574
|
path: ApiPath.media(),
|
|
532
575
|
query: { ...wireQuery, limit: window.limit, offset: window.offset },
|
|
533
576
|
signal: options?.signal,
|
|
534
|
-
timeoutMilliseconds: options?.timeoutMilliseconds
|
|
577
|
+
timeoutMilliseconds: options?.timeoutMilliseconds,
|
|
578
|
+
cache: options?.cache
|
|
535
579
|
});
|
|
536
580
|
const rows = body.items.map((payload) => new MediaAsset(transport, MediaAssetMapper.toRecord(payload)));
|
|
537
|
-
return new MediaPage(rows, body.total, new PageWindow(body.limit, body.offset), transport, wireQuery);
|
|
581
|
+
return new MediaPage(rows, body.total, new PageWindow(body.limit, body.offset), transport, wireQuery, options);
|
|
538
582
|
}
|
|
539
583
|
}
|
|
540
584
|
|
|
@@ -621,11 +665,11 @@ class Media {
|
|
|
621
665
|
let options;
|
|
622
666
|
if (input instanceof Blob) {
|
|
623
667
|
const fileOptions = second ?? {};
|
|
624
|
-
form.set("file", input,
|
|
668
|
+
form.set("file", input, MediaFile.nameOf(input, fileOptions.filename));
|
|
625
669
|
folder = fileOptions.folder;
|
|
626
670
|
options = third ?? fileOptions;
|
|
627
671
|
} else {
|
|
628
|
-
form.set("file",
|
|
672
|
+
form.set("file", MediaFile.toBlob(input.bytes, input.contentType), input.filename);
|
|
629
673
|
folder = input.folder;
|
|
630
674
|
options = second;
|
|
631
675
|
}
|
|
@@ -654,7 +698,8 @@ class Media {
|
|
|
654
698
|
method: "GET",
|
|
655
699
|
path: ApiPath.mediaAsset(id),
|
|
656
700
|
signal: options?.signal,
|
|
657
|
-
timeoutMilliseconds: options?.timeoutMilliseconds
|
|
701
|
+
timeoutMilliseconds: options?.timeoutMilliseconds,
|
|
702
|
+
cache: options?.cache
|
|
658
703
|
});
|
|
659
704
|
return new MediaAsset(this.transport, MediaAssetMapper.toRecord(payload));
|
|
660
705
|
}
|
|
@@ -663,7 +708,8 @@ class Media {
|
|
|
663
708
|
method: "GET",
|
|
664
709
|
path: ApiPath.mediaExtensions(),
|
|
665
710
|
signal: options?.signal,
|
|
666
|
-
timeoutMilliseconds: options?.timeoutMilliseconds
|
|
711
|
+
timeoutMilliseconds: options?.timeoutMilliseconds,
|
|
712
|
+
cache: options?.cache
|
|
667
713
|
});
|
|
668
714
|
return body.items;
|
|
669
715
|
}
|
|
@@ -693,18 +739,6 @@ class Media {
|
|
|
693
739
|
sort: query.sort
|
|
694
740
|
};
|
|
695
741
|
}
|
|
696
|
-
static toBlob(bytes, contentType) {
|
|
697
|
-
if (bytes instanceof Blob)
|
|
698
|
-
return bytes;
|
|
699
|
-
return new Blob([bytes], contentType ? { type: contentType } : undefined);
|
|
700
|
-
}
|
|
701
|
-
static filenameOf(input, explicit) {
|
|
702
|
-
if (explicit)
|
|
703
|
-
return explicit;
|
|
704
|
-
if (input instanceof File && input.name)
|
|
705
|
-
return input.name;
|
|
706
|
-
throw new Error("media upload: a Blob has no filename — pass { filename } explicitly");
|
|
707
|
-
}
|
|
708
742
|
}
|
|
709
743
|
|
|
710
744
|
// src/variables/variable.ts
|
|
@@ -1388,6 +1422,132 @@ class Projects {
|
|
|
1388
1422
|
}
|
|
1389
1423
|
}
|
|
1390
1424
|
|
|
1425
|
+
// src/transport/caching-transport.ts
|
|
1426
|
+
class CachingTransport {
|
|
1427
|
+
#fetchTransport;
|
|
1428
|
+
#cache;
|
|
1429
|
+
constructor(fetchTransport, cache) {
|
|
1430
|
+
this.#fetchTransport = fetchTransport;
|
|
1431
|
+
this.#cache = cache;
|
|
1432
|
+
}
|
|
1433
|
+
async json(request) {
|
|
1434
|
+
if (request.method !== "GET")
|
|
1435
|
+
return this.write(() => this.#fetchTransport.json(request));
|
|
1436
|
+
if (request.cache === "bypass")
|
|
1437
|
+
return this.#fetchTransport.json(request);
|
|
1438
|
+
if (request.signal?.aborted)
|
|
1439
|
+
throw new RequestAbortedError(request.method, request.path);
|
|
1440
|
+
let prepared;
|
|
1441
|
+
try {
|
|
1442
|
+
prepared = this.#fetchTransport.prepare(request);
|
|
1443
|
+
} catch (caught) {
|
|
1444
|
+
throw this.#fetchTransport.preparationFailure(request, caught);
|
|
1445
|
+
}
|
|
1446
|
+
const key = JSON.stringify([prepared.url, [...prepared.headers]]);
|
|
1447
|
+
if (request.cache !== "refresh") {
|
|
1448
|
+
const cached = this.#cache.get(key);
|
|
1449
|
+
if (cached)
|
|
1450
|
+
return cached.body;
|
|
1451
|
+
}
|
|
1452
|
+
const ticket = this.#cache.start(key);
|
|
1453
|
+
try {
|
|
1454
|
+
const body = await this.#fetchTransport.jsonPrepared(request, prepared);
|
|
1455
|
+
this.#cache.set(key, ticket, body);
|
|
1456
|
+
return body;
|
|
1457
|
+
} finally {
|
|
1458
|
+
this.#cache.finish(key, ticket);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
empty(request) {
|
|
1462
|
+
return request.method === "GET" ? this.#fetchTransport.empty(request) : this.write(() => this.#fetchTransport.empty(request));
|
|
1463
|
+
}
|
|
1464
|
+
stream(request) {
|
|
1465
|
+
return request.method === "GET" ? this.#fetchTransport.stream(request) : this.write(() => this.#fetchTransport.stream(request));
|
|
1466
|
+
}
|
|
1467
|
+
upload(request, form) {
|
|
1468
|
+
return request.method === "GET" ? this.#fetchTransport.upload(request, form) : this.write(() => this.#fetchTransport.upload(request, form));
|
|
1469
|
+
}
|
|
1470
|
+
async write(dispatch) {
|
|
1471
|
+
this.#cache.clear();
|
|
1472
|
+
try {
|
|
1473
|
+
return await dispatch();
|
|
1474
|
+
} finally {
|
|
1475
|
+
this.#cache.clear();
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
// src/cache/response-cache.ts
|
|
1481
|
+
var import_ttlcache = require("@isaacs/ttlcache");
|
|
1482
|
+
|
|
1483
|
+
class ResponseCache {
|
|
1484
|
+
#store;
|
|
1485
|
+
#pending = new Map;
|
|
1486
|
+
constructor(options) {
|
|
1487
|
+
this.#store = new import_ttlcache.TTLCache({
|
|
1488
|
+
ttl: options.ttlMilliseconds,
|
|
1489
|
+
...options.maxEntries === undefined || options.maxEntries === Infinity ? {} : { max: options.maxEntries },
|
|
1490
|
+
checkAgeOnGet: true,
|
|
1491
|
+
updateAgeOnGet: false
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
get size() {
|
|
1495
|
+
return this.#store.size;
|
|
1496
|
+
}
|
|
1497
|
+
get(key) {
|
|
1498
|
+
const cached = this.#store.get(key);
|
|
1499
|
+
return cached === undefined ? undefined : { body: structuredClone(cached.body) };
|
|
1500
|
+
}
|
|
1501
|
+
set(key, ticket, body) {
|
|
1502
|
+
if (this.#pending.get(key) !== ticket)
|
|
1503
|
+
return;
|
|
1504
|
+
this.#store.set(key, { body: structuredClone(body) });
|
|
1505
|
+
}
|
|
1506
|
+
start(key) {
|
|
1507
|
+
const ticket = Symbol();
|
|
1508
|
+
this.#pending.set(key, ticket);
|
|
1509
|
+
return ticket;
|
|
1510
|
+
}
|
|
1511
|
+
finish(key, ticket) {
|
|
1512
|
+
if (this.#pending.get(key) === ticket)
|
|
1513
|
+
this.#pending.delete(key);
|
|
1514
|
+
}
|
|
1515
|
+
clear() {
|
|
1516
|
+
this.#pending.clear();
|
|
1517
|
+
this.#store.clear();
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
// src/cache/silo-cache.ts
|
|
1522
|
+
class SiloCache {
|
|
1523
|
+
#responseCache;
|
|
1524
|
+
constructor(options) {
|
|
1525
|
+
if (options !== undefined)
|
|
1526
|
+
SiloCache.validate(options);
|
|
1527
|
+
this.#responseCache = options === undefined ? undefined : new ResponseCache(options);
|
|
1528
|
+
}
|
|
1529
|
+
get enabled() {
|
|
1530
|
+
return this.#responseCache !== undefined;
|
|
1531
|
+
}
|
|
1532
|
+
get size() {
|
|
1533
|
+
return this.#responseCache?.size ?? 0;
|
|
1534
|
+
}
|
|
1535
|
+
clear() {
|
|
1536
|
+
this.#responseCache?.clear();
|
|
1537
|
+
}
|
|
1538
|
+
wrap(fetchTransport) {
|
|
1539
|
+
return this.#responseCache ? new CachingTransport(fetchTransport, this.#responseCache) : fetchTransport;
|
|
1540
|
+
}
|
|
1541
|
+
static validate(options) {
|
|
1542
|
+
if (!Number.isSafeInteger(options.ttlMilliseconds) || options.ttlMilliseconds <= 0) {
|
|
1543
|
+
throw new TypeError("SiloOptions.cache.ttlMilliseconds must be a positive finite safe integer");
|
|
1544
|
+
}
|
|
1545
|
+
if (options.maxEntries !== undefined && options.maxEntries !== Infinity && (!Number.isSafeInteger(options.maxEntries) || options.maxEntries <= 0)) {
|
|
1546
|
+
throw new TypeError("SiloOptions.cache.maxEntries must be a positive safe integer or Infinity");
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1391
1551
|
// src/errors/silo-error.ts
|
|
1392
1552
|
class SiloError extends Error {
|
|
1393
1553
|
status;
|
|
@@ -1568,12 +1728,19 @@ class NetworkError extends Error {
|
|
|
1568
1728
|
method;
|
|
1569
1729
|
path;
|
|
1570
1730
|
constructor(method, path, cause) {
|
|
1571
|
-
super(`network error on ${method} ${path}: the request never reached the server`, { cause });
|
|
1731
|
+
super(`network error on ${method} ${path}: the request never reached the server${NetworkError.reason(cause)}`, { cause });
|
|
1572
1732
|
this.name = "NetworkError";
|
|
1573
1733
|
this.method = method;
|
|
1574
1734
|
this.path = path;
|
|
1575
1735
|
Object.setPrototypeOf(this, NetworkError.prototype);
|
|
1576
1736
|
}
|
|
1737
|
+
static reason(cause) {
|
|
1738
|
+
if (cause instanceof Error && cause.message)
|
|
1739
|
+
return ` (${cause.message})`;
|
|
1740
|
+
if (typeof cause === "string" && cause)
|
|
1741
|
+
return ` (${cause})`;
|
|
1742
|
+
return "";
|
|
1743
|
+
}
|
|
1577
1744
|
}
|
|
1578
1745
|
|
|
1579
1746
|
// src/errors/timeout-error.ts
|
|
@@ -1675,60 +1842,80 @@ class ResponseDecoder {
|
|
|
1675
1842
|
}
|
|
1676
1843
|
}
|
|
1677
1844
|
|
|
1678
|
-
// src/transport/transport.ts
|
|
1679
|
-
class
|
|
1680
|
-
url;
|
|
1681
|
-
key;
|
|
1682
|
-
headers;
|
|
1683
|
-
timeoutMilliseconds;
|
|
1684
|
-
fetchFunction;
|
|
1845
|
+
// src/transport/fetch-transport.ts
|
|
1846
|
+
class FetchTransport {
|
|
1847
|
+
#url;
|
|
1848
|
+
#key;
|
|
1849
|
+
#headers;
|
|
1850
|
+
#timeoutMilliseconds;
|
|
1851
|
+
#fetchFunction;
|
|
1685
1852
|
constructor(options) {
|
|
1686
|
-
this
|
|
1687
|
-
this
|
|
1688
|
-
this
|
|
1689
|
-
this
|
|
1690
|
-
this
|
|
1853
|
+
this.#url = FetchTransport.normalizeUrl(options.url);
|
|
1854
|
+
this.#key = options.key;
|
|
1855
|
+
this.#headers = options.headers ?? {};
|
|
1856
|
+
this.#timeoutMilliseconds = options.timeoutMilliseconds;
|
|
1857
|
+
this.#fetchFunction = FetchTransport.resolveFetch(options.fetch);
|
|
1691
1858
|
}
|
|
1692
1859
|
async json(request) {
|
|
1693
1860
|
const response = await this.execute(request);
|
|
1694
1861
|
return await ResponseDecoder.decode(response, request, true);
|
|
1695
1862
|
}
|
|
1863
|
+
async jsonPrepared(request, prepared) {
|
|
1864
|
+
const response = await this.execute(request, prepared);
|
|
1865
|
+
return await ResponseDecoder.decode(response, request, true);
|
|
1866
|
+
}
|
|
1696
1867
|
async empty(request) {
|
|
1697
1868
|
const response = await this.execute(request);
|
|
1698
1869
|
await ResponseDecoder.decode(response, request, false);
|
|
1699
1870
|
}
|
|
1700
1871
|
async stream(request) {
|
|
1701
|
-
|
|
1702
|
-
return response.body;
|
|
1872
|
+
return (await this.execute(request)).body;
|
|
1703
1873
|
}
|
|
1704
1874
|
async upload(request, form) {
|
|
1705
|
-
const response = await this.execute(request, form);
|
|
1875
|
+
const response = await this.execute(request, undefined, form);
|
|
1706
1876
|
return await ResponseDecoder.decode(response, request, true);
|
|
1707
1877
|
}
|
|
1708
1878
|
withKey(key) {
|
|
1709
|
-
return new
|
|
1879
|
+
return new FetchTransport({ ...this.snapshot(), key });
|
|
1710
1880
|
}
|
|
1711
1881
|
withUrl(url) {
|
|
1712
|
-
return new
|
|
1882
|
+
return new FetchTransport({ ...this.snapshot(), url });
|
|
1883
|
+
}
|
|
1884
|
+
prepare(request, form) {
|
|
1885
|
+
const rawHeaders = { ...this.#headers, ...request.headers };
|
|
1886
|
+
if (this.#key)
|
|
1887
|
+
rawHeaders.Authorization = `Bearer ${this.#key}`;
|
|
1888
|
+
if (!form && request.body !== undefined && !("Content-Type" in rawHeaders)) {
|
|
1889
|
+
rawHeaders["Content-Type"] = "application/json";
|
|
1890
|
+
}
|
|
1891
|
+
return {
|
|
1892
|
+
url: `${this.#url}${request.path}${QueryString.build(request.query)}`,
|
|
1893
|
+
headers: new Headers(rawHeaders),
|
|
1894
|
+
body: form ?? (request.body === undefined ? undefined : JSON.stringify(request.body))
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
preparationFailure(request, caught) {
|
|
1898
|
+
return new NetworkError(request.method, request.path, caught);
|
|
1713
1899
|
}
|
|
1714
1900
|
snapshot() {
|
|
1715
1901
|
return {
|
|
1716
|
-
url: this
|
|
1717
|
-
key: this
|
|
1718
|
-
headers: this
|
|
1719
|
-
timeoutMilliseconds: this
|
|
1720
|
-
fetch: this
|
|
1902
|
+
url: this.#url,
|
|
1903
|
+
key: this.#key,
|
|
1904
|
+
headers: this.#headers,
|
|
1905
|
+
timeoutMilliseconds: this.#timeoutMilliseconds,
|
|
1906
|
+
fetch: this.#fetchFunction
|
|
1721
1907
|
};
|
|
1722
1908
|
}
|
|
1723
|
-
async execute(request, form) {
|
|
1724
|
-
const abortSignals = new AbortSignals(request.signal, request.timeoutMilliseconds ?? this
|
|
1725
|
-
const
|
|
1909
|
+
async execute(request, prepared, form) {
|
|
1910
|
+
const abortSignals = new AbortSignals(request.signal, request.timeoutMilliseconds ?? this.#timeoutMilliseconds);
|
|
1911
|
+
const sendRequest = this.#fetchFunction;
|
|
1726
1912
|
let response;
|
|
1727
1913
|
try {
|
|
1728
|
-
|
|
1914
|
+
const outbound = prepared ?? this.prepare(request, form);
|
|
1915
|
+
response = await sendRequest(outbound.url, {
|
|
1729
1916
|
method: request.method,
|
|
1730
|
-
headers:
|
|
1731
|
-
body:
|
|
1917
|
+
headers: outbound.headers,
|
|
1918
|
+
body: outbound.body,
|
|
1732
1919
|
signal: abortSignals.signal
|
|
1733
1920
|
});
|
|
1734
1921
|
} catch (caught) {
|
|
@@ -1742,44 +1929,44 @@ class Transport {
|
|
|
1742
1929
|
return response;
|
|
1743
1930
|
}
|
|
1744
1931
|
transportFailure(request, abortSignals, caught) {
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
return new TimeoutError(request.method, request.path, request.timeoutMilliseconds ?? this.timeoutMilliseconds ?? 0);
|
|
1932
|
+
if (abortSignals.firedBy() === "timeout") {
|
|
1933
|
+
return new TimeoutError(request.method, request.path, request.timeoutMilliseconds ?? this.#timeoutMilliseconds ?? 0);
|
|
1748
1934
|
}
|
|
1749
|
-
if (firedBy === "caller")
|
|
1935
|
+
if (abortSignals.firedBy() === "caller")
|
|
1750
1936
|
return new RequestAbortedError(request.method, request.path);
|
|
1751
|
-
}
|
|
1752
1937
|
return new NetworkError(request.method, request.path, caught);
|
|
1753
1938
|
}
|
|
1754
|
-
buildHeaders(request, isUpload) {
|
|
1755
|
-
const headers = { ...this.headers, ...request.headers };
|
|
1756
|
-
if (this.key)
|
|
1757
|
-
headers["Authorization"] = `Bearer ${this.key}`;
|
|
1758
|
-
if (!isUpload && request.body !== undefined && !("Content-Type" in headers)) {
|
|
1759
|
-
headers["Content-Type"] = "application/json";
|
|
1760
|
-
}
|
|
1761
|
-
return headers;
|
|
1762
|
-
}
|
|
1763
1939
|
static normalizeUrl(url) {
|
|
1764
1940
|
return url.replace(/\/+$/, "");
|
|
1765
1941
|
}
|
|
1942
|
+
static resolveFetch(fetchFunction) {
|
|
1943
|
+
if (fetchFunction)
|
|
1944
|
+
return fetchFunction;
|
|
1945
|
+
if (typeof globalThis.fetch !== "function") {
|
|
1946
|
+
throw new Error("this runtime has no global fetch: pass one as SiloOptions.fetch");
|
|
1947
|
+
}
|
|
1948
|
+
return globalThis.fetch.bind(globalThis);
|
|
1949
|
+
}
|
|
1766
1950
|
}
|
|
1767
1951
|
|
|
1768
1952
|
// src/silo.ts
|
|
1769
1953
|
class Silo {
|
|
1770
1954
|
projects;
|
|
1771
1955
|
media;
|
|
1956
|
+
cache;
|
|
1772
1957
|
options;
|
|
1773
1958
|
transport;
|
|
1774
1959
|
constructor(options) {
|
|
1775
|
-
this.
|
|
1776
|
-
this.
|
|
1960
|
+
this.cache = options.cache instanceof SiloCache ? options.cache : new SiloCache(options.cache);
|
|
1961
|
+
this.options = { ...options, cache: this.cache };
|
|
1962
|
+
const fetchTransport = new FetchTransport({
|
|
1777
1963
|
url: options.url,
|
|
1778
1964
|
key: options.key,
|
|
1779
1965
|
headers: options.headers,
|
|
1780
1966
|
timeoutMilliseconds: options.timeoutMilliseconds,
|
|
1781
1967
|
fetch: options.fetch
|
|
1782
1968
|
});
|
|
1969
|
+
this.transport = this.cache.wrap(fetchTransport);
|
|
1783
1970
|
this.projects = new Projects(this.transport);
|
|
1784
1971
|
this.media = new Media(this.transport);
|
|
1785
1972
|
}
|
|
@@ -1797,7 +1984,8 @@ class Silo {
|
|
|
1797
1984
|
method: "GET",
|
|
1798
1985
|
path: ApiPath.health(),
|
|
1799
1986
|
signal: options?.signal,
|
|
1800
|
-
timeoutMilliseconds: options?.timeoutMilliseconds
|
|
1987
|
+
timeoutMilliseconds: options?.timeoutMilliseconds,
|
|
1988
|
+
cache: "bypass"
|
|
1801
1989
|
});
|
|
1802
1990
|
}
|
|
1803
1991
|
withKey(key) {
|
|
@@ -1847,6 +2035,7 @@ class RouteInventory {
|
|
|
1847
2035
|
"PATCH /api/media/:id",
|
|
1848
2036
|
"DELETE /api/media/:id",
|
|
1849
2037
|
"GET /api/media/:id/usages",
|
|
2038
|
+
"POST /api/media/:id/content",
|
|
1850
2039
|
"POST /api/media/delete",
|
|
1851
2040
|
"GET /api/media/folders",
|
|
1852
2041
|
"POST /api/media/folders",
|
|
@@ -1857,6 +2046,7 @@ class RouteInventory {
|
|
|
1857
2046
|
"GET /api/session": "key introspection, an operator surface",
|
|
1858
2047
|
"GET /api/keys": "keys and claims are an operator surface",
|
|
1859
2048
|
"POST /api/keys": "keys and claims are an operator surface",
|
|
2049
|
+
"PATCH /api/keys/:id": "keys and claims are an operator surface",
|
|
1860
2050
|
"DELETE /api/keys/:id": "keys and claims are an operator surface",
|
|
1861
2051
|
"GET /api/audit": "an operator surface",
|
|
1862
2052
|
"GET /api/observability": "an operator surface",
|
package/dist/index.d.cts
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
* change rather than a hand-built request.
|
|
6
6
|
*/
|
|
7
7
|
export { Silo } from "./silo.cjs";
|
|
8
|
+
export { SiloCache } from "./cache/silo-cache.cjs";
|
|
8
9
|
export type { SiloOptions } from "./silo-options.cjs";
|
|
10
|
+
export type { CacheMode } from "./cache/cache-mode.cjs";
|
|
11
|
+
export type { SiloCacheOptions } from "./cache/silo-cache-options.cjs";
|
|
9
12
|
export type { RequestOptions } from "./request-options.cjs";
|
|
10
13
|
export type { FetchFunction } from "./transport/fetch-function.cjs";
|
|
11
14
|
export { RouteInventory } from "./transport/route-inventory.cjs";
|
|
@@ -71,6 +74,7 @@ export type { MediaAssetRecord } from "./media/media-asset.cjs";
|
|
|
71
74
|
export type { MediaQuery } from "./media/media-query.cjs";
|
|
72
75
|
export type { MediaUsage } from "./media/media-usage.cjs";
|
|
73
76
|
export type { MediaUsageQuery } from "./media/media-usage-page.cjs";
|
|
77
|
+
export type { MediaReplace, MediaReplaceBytes, MediaReplaceOptions } from "./media/media-replace.cjs";
|
|
74
78
|
export type { MediaUpload, MediaUploadBytes, MediaUploadFileOptions } from "./media/media-upload.cjs";
|
|
75
79
|
export type { MediaDeleteFailure } from "./media/media-delete-failure.cjs";
|
|
76
80
|
export type { MediaDeleteOptions } from "./media/media-delete-options.cjs";
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
* change rather than a hand-built request.
|
|
6
6
|
*/
|
|
7
7
|
export { Silo } from "./silo.js";
|
|
8
|
+
export { SiloCache } from "./cache/silo-cache.js";
|
|
8
9
|
export type { SiloOptions } from "./silo-options.js";
|
|
10
|
+
export type { CacheMode } from "./cache/cache-mode.js";
|
|
11
|
+
export type { SiloCacheOptions } from "./cache/silo-cache-options.js";
|
|
9
12
|
export type { RequestOptions } from "./request-options.js";
|
|
10
13
|
export type { FetchFunction } from "./transport/fetch-function.js";
|
|
11
14
|
export { RouteInventory } from "./transport/route-inventory.js";
|
|
@@ -71,6 +74,7 @@ export type { MediaAssetRecord } from "./media/media-asset.js";
|
|
|
71
74
|
export type { MediaQuery } from "./media/media-query.js";
|
|
72
75
|
export type { MediaUsage } from "./media/media-usage.js";
|
|
73
76
|
export type { MediaUsageQuery } from "./media/media-usage-page.js";
|
|
77
|
+
export type { MediaReplace, MediaReplaceBytes, MediaReplaceOptions } from "./media/media-replace.js";
|
|
74
78
|
export type { MediaUpload, MediaUploadBytes, MediaUploadFileOptions } from "./media/media-upload.js";
|
|
75
79
|
export type { MediaDeleteFailure } from "./media/media-delete-failure.js";
|
|
76
80
|
export type { MediaDeleteOptions } from "./media/media-delete-options.js";
|