@org-quicko/silo-client 1.0.1 → 1.1.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 +7 -0
- package/dist/errors/network-error.d.cts +5 -0
- package/dist/errors/network-error.d.ts +5 -0
- package/dist/index.cjs +58 -17
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +58 -17
- 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-replace.d.cts +17 -0
- package/dist/media/media-replace.d.ts +17 -0
- package/dist/media/media.d.cts +0 -4
- package/dist/media/media.d.ts +0 -4
- package/dist/transport/api-path.d.cts +1 -0
- package/dist/transport/api-path.d.ts +1 -0
- package/dist/transport/transport.d.cts +4 -0
- package/dist/transport/transport.d.ts +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -208,6 +208,7 @@ value you store. Storing the URL instead is what a later rename breaks.
|
|
|
208
208
|
await poster.rename("arrival-2016.jpg")
|
|
209
209
|
await poster.moveTo("posters/2016")
|
|
210
210
|
await poster.setTags(["poster"]) // replaces the whole list
|
|
211
|
+
await poster.replace(file) // new bytes, same id, name and URL
|
|
211
212
|
await poster.delete() // refused while an entry refers to it
|
|
212
213
|
await poster.delete({ force: true })
|
|
213
214
|
|
|
@@ -217,6 +218,12 @@ usage.total // the true count
|
|
|
217
218
|
usage.visible // how many of them this key may see
|
|
218
219
|
```
|
|
219
220
|
|
|
221
|
+
`replace` swaps the file behind an asset. The id, the reference, the name and
|
|
222
|
+
the URL all stay, so every entry that refers to it shows the new file and none
|
|
223
|
+
of them is rewritten. The new file must keep the same extension. It needs the
|
|
224
|
+
`media:replace` claim, and `entries:update` on each scope that refers to the
|
|
225
|
+
asset.
|
|
226
|
+
|
|
220
227
|
Folders, and a bulk delete that takes up to 100 ids:
|
|
221
228
|
|
|
222
229
|
```ts
|
|
@@ -8,4 +8,9 @@ export declare class NetworkError extends Error {
|
|
|
8
8
|
readonly method: string;
|
|
9
9
|
readonly path: string;
|
|
10
10
|
constructor(method: string, path: string, cause: unknown);
|
|
11
|
+
/** What `fetch` rejected with, in the message itself: a `cause` chain is
|
|
12
|
+
* printed by some consoles and by no log line, and "never reached the
|
|
13
|
+
* server" alone reads as a verdict on the network when the fault can be the
|
|
14
|
+
* call. */
|
|
15
|
+
private static reason;
|
|
11
16
|
}
|
|
@@ -8,4 +8,9 @@ export declare class NetworkError extends Error {
|
|
|
8
8
|
readonly method: string;
|
|
9
9
|
readonly path: string;
|
|
10
10
|
constructor(method: string, path: string, cause: unknown);
|
|
11
|
+
/** What `fetch` rejected with, in the message itself: a `cause` chain is
|
|
12
|
+
* printed by some consoles and by no log line, and "never reached the
|
|
13
|
+
* server" alone reads as a verdict on the network when the fault can be the
|
|
14
|
+
* call. */
|
|
15
|
+
private static reason;
|
|
11
16
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -181,6 +181,9 @@ class ApiPath {
|
|
|
181
181
|
static mediaAssetUsages(id) {
|
|
182
182
|
return `${ApiPath.mediaAsset(id)}/usages`;
|
|
183
183
|
}
|
|
184
|
+
static mediaAssetContent(id) {
|
|
185
|
+
return `${ApiPath.mediaAsset(id)}/content`;
|
|
186
|
+
}
|
|
184
187
|
static mediaBulkDelete() {
|
|
185
188
|
return "/api/media/delete";
|
|
186
189
|
}
|
|
@@ -227,6 +230,22 @@ class MediaAssetMapper {
|
|
|
227
230
|
}
|
|
228
231
|
}
|
|
229
232
|
|
|
233
|
+
// src/media/media-file.ts
|
|
234
|
+
class MediaFile {
|
|
235
|
+
static toBlob(bytes, contentType) {
|
|
236
|
+
if (bytes instanceof Blob)
|
|
237
|
+
return bytes;
|
|
238
|
+
return new Blob([bytes], contentType ? { type: contentType } : undefined);
|
|
239
|
+
}
|
|
240
|
+
static nameOf(input, explicit) {
|
|
241
|
+
if (explicit)
|
|
242
|
+
return explicit;
|
|
243
|
+
if (input instanceof File && input.name)
|
|
244
|
+
return input.name;
|
|
245
|
+
throw new Error("media: a Blob has no filename — pass { filename } explicitly");
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
230
249
|
// src/media/media-reference.ts
|
|
231
250
|
class MediaReference {
|
|
232
251
|
static Scheme = "silo://media/";
|
|
@@ -388,6 +407,22 @@ class MediaAsset {
|
|
|
388
407
|
async setTags(tags, options) {
|
|
389
408
|
return this.patch({ tags: [...tags] }, options);
|
|
390
409
|
}
|
|
410
|
+
async replace(input, options) {
|
|
411
|
+
const form = new FormData;
|
|
412
|
+
if (input instanceof Blob) {
|
|
413
|
+
form.set("file", input, MediaFile.nameOf(input, options?.filename));
|
|
414
|
+
} else {
|
|
415
|
+
form.set("file", MediaFile.toBlob(input.bytes, input.contentType), input.filename);
|
|
416
|
+
}
|
|
417
|
+
const payload = await this.transport.upload({
|
|
418
|
+
method: "POST",
|
|
419
|
+
path: ApiPath.mediaAssetContent(this.id),
|
|
420
|
+
signal: options?.signal,
|
|
421
|
+
timeoutMilliseconds: options?.timeoutMilliseconds
|
|
422
|
+
}, form);
|
|
423
|
+
this.record = MediaAssetMapper.toRecord(payload);
|
|
424
|
+
return this;
|
|
425
|
+
}
|
|
391
426
|
async delete(options) {
|
|
392
427
|
await this.transport.empty({
|
|
393
428
|
method: "DELETE",
|
|
@@ -621,11 +656,11 @@ class Media {
|
|
|
621
656
|
let options;
|
|
622
657
|
if (input instanceof Blob) {
|
|
623
658
|
const fileOptions = second ?? {};
|
|
624
|
-
form.set("file", input,
|
|
659
|
+
form.set("file", input, MediaFile.nameOf(input, fileOptions.filename));
|
|
625
660
|
folder = fileOptions.folder;
|
|
626
661
|
options = third ?? fileOptions;
|
|
627
662
|
} else {
|
|
628
|
-
form.set("file",
|
|
663
|
+
form.set("file", MediaFile.toBlob(input.bytes, input.contentType), input.filename);
|
|
629
664
|
folder = input.folder;
|
|
630
665
|
options = second;
|
|
631
666
|
}
|
|
@@ -693,18 +728,6 @@ class Media {
|
|
|
693
728
|
sort: query.sort
|
|
694
729
|
};
|
|
695
730
|
}
|
|
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
731
|
}
|
|
709
732
|
|
|
710
733
|
// src/variables/variable.ts
|
|
@@ -1568,12 +1591,19 @@ class NetworkError extends Error {
|
|
|
1568
1591
|
method;
|
|
1569
1592
|
path;
|
|
1570
1593
|
constructor(method, path, cause) {
|
|
1571
|
-
super(`network error on ${method} ${path}: the request never reached the server`, { cause });
|
|
1594
|
+
super(`network error on ${method} ${path}: the request never reached the server${NetworkError.reason(cause)}`, { cause });
|
|
1572
1595
|
this.name = "NetworkError";
|
|
1573
1596
|
this.method = method;
|
|
1574
1597
|
this.path = path;
|
|
1575
1598
|
Object.setPrototypeOf(this, NetworkError.prototype);
|
|
1576
1599
|
}
|
|
1600
|
+
static reason(cause) {
|
|
1601
|
+
if (cause instanceof Error && cause.message)
|
|
1602
|
+
return ` (${cause.message})`;
|
|
1603
|
+
if (typeof cause === "string" && cause)
|
|
1604
|
+
return ` (${cause})`;
|
|
1605
|
+
return "";
|
|
1606
|
+
}
|
|
1577
1607
|
}
|
|
1578
1608
|
|
|
1579
1609
|
// src/errors/timeout-error.ts
|
|
@@ -1687,7 +1717,7 @@ class Transport {
|
|
|
1687
1717
|
this.key = options.key;
|
|
1688
1718
|
this.headers = options.headers ?? {};
|
|
1689
1719
|
this.timeoutMilliseconds = options.timeoutMilliseconds;
|
|
1690
|
-
this.fetchFunction = options.fetch
|
|
1720
|
+
this.fetchFunction = Transport.resolveFetch(options.fetch);
|
|
1691
1721
|
}
|
|
1692
1722
|
async json(request) {
|
|
1693
1723
|
const response = await this.execute(request);
|
|
@@ -1723,9 +1753,10 @@ class Transport {
|
|
|
1723
1753
|
async execute(request, form) {
|
|
1724
1754
|
const abortSignals = new AbortSignals(request.signal, request.timeoutMilliseconds ?? this.timeoutMilliseconds);
|
|
1725
1755
|
const url = `${this.url}${request.path}${QueryString.build(request.query)}`;
|
|
1756
|
+
const sendRequest = this.fetchFunction;
|
|
1726
1757
|
let response;
|
|
1727
1758
|
try {
|
|
1728
|
-
response = await
|
|
1759
|
+
response = await sendRequest(url, {
|
|
1729
1760
|
method: request.method,
|
|
1730
1761
|
headers: this.buildHeaders(request, Boolean(form)),
|
|
1731
1762
|
body: form ?? (request.body === undefined ? undefined : JSON.stringify(request.body)),
|
|
@@ -1763,6 +1794,14 @@ class Transport {
|
|
|
1763
1794
|
static normalizeUrl(url) {
|
|
1764
1795
|
return url.replace(/\/+$/, "");
|
|
1765
1796
|
}
|
|
1797
|
+
static resolveFetch(fetchFunction) {
|
|
1798
|
+
if (fetchFunction)
|
|
1799
|
+
return fetchFunction;
|
|
1800
|
+
if (typeof globalThis.fetch !== "function") {
|
|
1801
|
+
throw new Error("this runtime has no global fetch: pass one as SiloOptions.fetch");
|
|
1802
|
+
}
|
|
1803
|
+
return globalThis.fetch.bind(globalThis);
|
|
1804
|
+
}
|
|
1766
1805
|
}
|
|
1767
1806
|
|
|
1768
1807
|
// src/silo.ts
|
|
@@ -1847,6 +1886,7 @@ class RouteInventory {
|
|
|
1847
1886
|
"PATCH /api/media/:id",
|
|
1848
1887
|
"DELETE /api/media/:id",
|
|
1849
1888
|
"GET /api/media/:id/usages",
|
|
1889
|
+
"POST /api/media/:id/content",
|
|
1850
1890
|
"POST /api/media/delete",
|
|
1851
1891
|
"GET /api/media/folders",
|
|
1852
1892
|
"POST /api/media/folders",
|
|
@@ -1857,6 +1897,7 @@ class RouteInventory {
|
|
|
1857
1897
|
"GET /api/session": "key introspection, an operator surface",
|
|
1858
1898
|
"GET /api/keys": "keys and claims are an operator surface",
|
|
1859
1899
|
"POST /api/keys": "keys and claims are an operator surface",
|
|
1900
|
+
"PATCH /api/keys/:id": "keys and claims are an operator surface",
|
|
1860
1901
|
"DELETE /api/keys/:id": "keys and claims are an operator surface",
|
|
1861
1902
|
"GET /api/audit": "an operator surface",
|
|
1862
1903
|
"GET /api/observability": "an operator surface",
|
package/dist/index.d.cts
CHANGED
|
@@ -71,6 +71,7 @@ export type { MediaAssetRecord } from "./media/media-asset.cjs";
|
|
|
71
71
|
export type { MediaQuery } from "./media/media-query.cjs";
|
|
72
72
|
export type { MediaUsage } from "./media/media-usage.cjs";
|
|
73
73
|
export type { MediaUsageQuery } from "./media/media-usage-page.cjs";
|
|
74
|
+
export type { MediaReplace, MediaReplaceBytes, MediaReplaceOptions } from "./media/media-replace.cjs";
|
|
74
75
|
export type { MediaUpload, MediaUploadBytes, MediaUploadFileOptions } from "./media/media-upload.cjs";
|
|
75
76
|
export type { MediaDeleteFailure } from "./media/media-delete-failure.cjs";
|
|
76
77
|
export type { MediaDeleteOptions } from "./media/media-delete-options.cjs";
|
package/dist/index.d.ts
CHANGED
|
@@ -71,6 +71,7 @@ export type { MediaAssetRecord } from "./media/media-asset.js";
|
|
|
71
71
|
export type { MediaQuery } from "./media/media-query.js";
|
|
72
72
|
export type { MediaUsage } from "./media/media-usage.js";
|
|
73
73
|
export type { MediaUsageQuery } from "./media/media-usage-page.js";
|
|
74
|
+
export type { MediaReplace, MediaReplaceBytes, MediaReplaceOptions } from "./media/media-replace.js";
|
|
74
75
|
export type { MediaUpload, MediaUploadBytes, MediaUploadFileOptions } from "./media/media-upload.js";
|
|
75
76
|
export type { MediaDeleteFailure } from "./media/media-delete-failure.js";
|
|
76
77
|
export type { MediaDeleteOptions } from "./media/media-delete-options.js";
|
package/dist/index.js
CHANGED
|
@@ -84,6 +84,9 @@ class ApiPath {
|
|
|
84
84
|
static mediaAssetUsages(id) {
|
|
85
85
|
return `${ApiPath.mediaAsset(id)}/usages`;
|
|
86
86
|
}
|
|
87
|
+
static mediaAssetContent(id) {
|
|
88
|
+
return `${ApiPath.mediaAsset(id)}/content`;
|
|
89
|
+
}
|
|
87
90
|
static mediaBulkDelete() {
|
|
88
91
|
return "/api/media/delete";
|
|
89
92
|
}
|
|
@@ -130,6 +133,22 @@ class MediaAssetMapper {
|
|
|
130
133
|
}
|
|
131
134
|
}
|
|
132
135
|
|
|
136
|
+
// src/media/media-file.ts
|
|
137
|
+
class MediaFile {
|
|
138
|
+
static toBlob(bytes, contentType) {
|
|
139
|
+
if (bytes instanceof Blob)
|
|
140
|
+
return bytes;
|
|
141
|
+
return new Blob([bytes], contentType ? { type: contentType } : undefined);
|
|
142
|
+
}
|
|
143
|
+
static nameOf(input, explicit) {
|
|
144
|
+
if (explicit)
|
|
145
|
+
return explicit;
|
|
146
|
+
if (input instanceof File && input.name)
|
|
147
|
+
return input.name;
|
|
148
|
+
throw new Error("media: a Blob has no filename — pass { filename } explicitly");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
133
152
|
// src/media/media-reference.ts
|
|
134
153
|
class MediaReference {
|
|
135
154
|
static Scheme = "silo://media/";
|
|
@@ -291,6 +310,22 @@ class MediaAsset {
|
|
|
291
310
|
async setTags(tags, options) {
|
|
292
311
|
return this.patch({ tags: [...tags] }, options);
|
|
293
312
|
}
|
|
313
|
+
async replace(input, options) {
|
|
314
|
+
const form = new FormData;
|
|
315
|
+
if (input instanceof Blob) {
|
|
316
|
+
form.set("file", input, MediaFile.nameOf(input, options?.filename));
|
|
317
|
+
} else {
|
|
318
|
+
form.set("file", MediaFile.toBlob(input.bytes, input.contentType), input.filename);
|
|
319
|
+
}
|
|
320
|
+
const payload = await this.transport.upload({
|
|
321
|
+
method: "POST",
|
|
322
|
+
path: ApiPath.mediaAssetContent(this.id),
|
|
323
|
+
signal: options?.signal,
|
|
324
|
+
timeoutMilliseconds: options?.timeoutMilliseconds
|
|
325
|
+
}, form);
|
|
326
|
+
this.record = MediaAssetMapper.toRecord(payload);
|
|
327
|
+
return this;
|
|
328
|
+
}
|
|
294
329
|
async delete(options) {
|
|
295
330
|
await this.transport.empty({
|
|
296
331
|
method: "DELETE",
|
|
@@ -524,11 +559,11 @@ class Media {
|
|
|
524
559
|
let options;
|
|
525
560
|
if (input instanceof Blob) {
|
|
526
561
|
const fileOptions = second ?? {};
|
|
527
|
-
form.set("file", input,
|
|
562
|
+
form.set("file", input, MediaFile.nameOf(input, fileOptions.filename));
|
|
528
563
|
folder = fileOptions.folder;
|
|
529
564
|
options = third ?? fileOptions;
|
|
530
565
|
} else {
|
|
531
|
-
form.set("file",
|
|
566
|
+
form.set("file", MediaFile.toBlob(input.bytes, input.contentType), input.filename);
|
|
532
567
|
folder = input.folder;
|
|
533
568
|
options = second;
|
|
534
569
|
}
|
|
@@ -596,18 +631,6 @@ class Media {
|
|
|
596
631
|
sort: query.sort
|
|
597
632
|
};
|
|
598
633
|
}
|
|
599
|
-
static toBlob(bytes, contentType) {
|
|
600
|
-
if (bytes instanceof Blob)
|
|
601
|
-
return bytes;
|
|
602
|
-
return new Blob([bytes], contentType ? { type: contentType } : undefined);
|
|
603
|
-
}
|
|
604
|
-
static filenameOf(input, explicit) {
|
|
605
|
-
if (explicit)
|
|
606
|
-
return explicit;
|
|
607
|
-
if (input instanceof File && input.name)
|
|
608
|
-
return input.name;
|
|
609
|
-
throw new Error("media upload: a Blob has no filename — pass { filename } explicitly");
|
|
610
|
-
}
|
|
611
634
|
}
|
|
612
635
|
|
|
613
636
|
// src/variables/variable.ts
|
|
@@ -1471,12 +1494,19 @@ class NetworkError extends Error {
|
|
|
1471
1494
|
method;
|
|
1472
1495
|
path;
|
|
1473
1496
|
constructor(method, path, cause) {
|
|
1474
|
-
super(`network error on ${method} ${path}: the request never reached the server`, { cause });
|
|
1497
|
+
super(`network error on ${method} ${path}: the request never reached the server${NetworkError.reason(cause)}`, { cause });
|
|
1475
1498
|
this.name = "NetworkError";
|
|
1476
1499
|
this.method = method;
|
|
1477
1500
|
this.path = path;
|
|
1478
1501
|
Object.setPrototypeOf(this, NetworkError.prototype);
|
|
1479
1502
|
}
|
|
1503
|
+
static reason(cause) {
|
|
1504
|
+
if (cause instanceof Error && cause.message)
|
|
1505
|
+
return ` (${cause.message})`;
|
|
1506
|
+
if (typeof cause === "string" && cause)
|
|
1507
|
+
return ` (${cause})`;
|
|
1508
|
+
return "";
|
|
1509
|
+
}
|
|
1480
1510
|
}
|
|
1481
1511
|
|
|
1482
1512
|
// src/errors/timeout-error.ts
|
|
@@ -1590,7 +1620,7 @@ class Transport {
|
|
|
1590
1620
|
this.key = options.key;
|
|
1591
1621
|
this.headers = options.headers ?? {};
|
|
1592
1622
|
this.timeoutMilliseconds = options.timeoutMilliseconds;
|
|
1593
|
-
this.fetchFunction = options.fetch
|
|
1623
|
+
this.fetchFunction = Transport.resolveFetch(options.fetch);
|
|
1594
1624
|
}
|
|
1595
1625
|
async json(request) {
|
|
1596
1626
|
const response = await this.execute(request);
|
|
@@ -1626,9 +1656,10 @@ class Transport {
|
|
|
1626
1656
|
async execute(request, form) {
|
|
1627
1657
|
const abortSignals = new AbortSignals(request.signal, request.timeoutMilliseconds ?? this.timeoutMilliseconds);
|
|
1628
1658
|
const url = `${this.url}${request.path}${QueryString.build(request.query)}`;
|
|
1659
|
+
const sendRequest = this.fetchFunction;
|
|
1629
1660
|
let response;
|
|
1630
1661
|
try {
|
|
1631
|
-
response = await
|
|
1662
|
+
response = await sendRequest(url, {
|
|
1632
1663
|
method: request.method,
|
|
1633
1664
|
headers: this.buildHeaders(request, Boolean(form)),
|
|
1634
1665
|
body: form ?? (request.body === undefined ? undefined : JSON.stringify(request.body)),
|
|
@@ -1666,6 +1697,14 @@ class Transport {
|
|
|
1666
1697
|
static normalizeUrl(url) {
|
|
1667
1698
|
return url.replace(/\/+$/, "");
|
|
1668
1699
|
}
|
|
1700
|
+
static resolveFetch(fetchFunction) {
|
|
1701
|
+
if (fetchFunction)
|
|
1702
|
+
return fetchFunction;
|
|
1703
|
+
if (typeof globalThis.fetch !== "function") {
|
|
1704
|
+
throw new Error("this runtime has no global fetch: pass one as SiloOptions.fetch");
|
|
1705
|
+
}
|
|
1706
|
+
return globalThis.fetch.bind(globalThis);
|
|
1707
|
+
}
|
|
1669
1708
|
}
|
|
1670
1709
|
|
|
1671
1710
|
// src/silo.ts
|
|
@@ -1750,6 +1789,7 @@ class RouteInventory {
|
|
|
1750
1789
|
"PATCH /api/media/:id",
|
|
1751
1790
|
"DELETE /api/media/:id",
|
|
1752
1791
|
"GET /api/media/:id/usages",
|
|
1792
|
+
"POST /api/media/:id/content",
|
|
1753
1793
|
"POST /api/media/delete",
|
|
1754
1794
|
"GET /api/media/folders",
|
|
1755
1795
|
"POST /api/media/folders",
|
|
@@ -1760,6 +1800,7 @@ class RouteInventory {
|
|
|
1760
1800
|
"GET /api/session": "key introspection, an operator surface",
|
|
1761
1801
|
"GET /api/keys": "keys and claims are an operator surface",
|
|
1762
1802
|
"POST /api/keys": "keys and claims are an operator surface",
|
|
1803
|
+
"PATCH /api/keys/:id": "keys and claims are an operator surface",
|
|
1763
1804
|
"DELETE /api/keys/:id": "keys and claims are an operator surface",
|
|
1764
1805
|
"GET /api/audit": "an operator surface",
|
|
1765
1806
|
"GET /api/observability": "an operator surface",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RequestOptions } from "../request-options.cjs";
|
|
2
2
|
import type { Transport } from "../transport/transport.cjs";
|
|
3
3
|
import type { MediaDeleteOptions } from "./media-delete-options.cjs";
|
|
4
|
+
import type { MediaReplace, MediaReplaceOptions } from "./media-replace.cjs";
|
|
4
5
|
import type { MediaUsageQuery } from "./media-usage-page.cjs";
|
|
5
6
|
import { MediaUsagePage } from "./media-usage-page.cjs";
|
|
6
7
|
/** `MediaAsset`'s mapped state — `MediaAssetMapper.toRecord`'s output. */
|
|
@@ -53,6 +54,22 @@ export declare class MediaAsset {
|
|
|
53
54
|
moveTo(folder: string, options?: RequestOptions): Promise<this>;
|
|
54
55
|
/** REPLACES the tag list — `PATCH` replaces, it does not append. */
|
|
55
56
|
setTags(tags: readonly string[], options?: RequestOptions): Promise<this>;
|
|
57
|
+
/**
|
|
58
|
+
* Swaps the bytes behind this asset (D67). `id`, `reference`, `url`,
|
|
59
|
+
* `filename` and `folder` all survive; `hash`, `sizeInBytes` and
|
|
60
|
+
* `contentType` are re-read from the server's answer.
|
|
61
|
+
*
|
|
62
|
+
* Every entry referencing it now resolves to the new file, without any of
|
|
63
|
+
* them being rewritten — which is the point, and the reason the server asks
|
|
64
|
+
* for `media:replace` **and** `entries:update` at every scope that refers
|
|
65
|
+
* to it.
|
|
66
|
+
*
|
|
67
|
+
* The new file must keep the current extension: the blob key's suffix is
|
|
68
|
+
* derived from the filename at upload and is the visible tail of the URL on
|
|
69
|
+
* a bucket-backed instance, so a `.png` asset takes a `.png` replacement.
|
|
70
|
+
* Converting a file is a new upload, not a replacement of this one.
|
|
71
|
+
*/
|
|
72
|
+
replace(input: MediaReplace, options?: MediaReplaceOptions): Promise<this>;
|
|
56
73
|
/** Refused while an entry references it; `{ force: true }` also needs
|
|
57
74
|
* `entries:update` on every scope this asset reaches. */
|
|
58
75
|
delete(options?: MediaDeleteOptions): Promise<void>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RequestOptions } from "../request-options.js";
|
|
2
2
|
import type { Transport } from "../transport/transport.js";
|
|
3
3
|
import type { MediaDeleteOptions } from "./media-delete-options.js";
|
|
4
|
+
import type { MediaReplace, MediaReplaceOptions } from "./media-replace.js";
|
|
4
5
|
import type { MediaUsageQuery } from "./media-usage-page.js";
|
|
5
6
|
import { MediaUsagePage } from "./media-usage-page.js";
|
|
6
7
|
/** `MediaAsset`'s mapped state — `MediaAssetMapper.toRecord`'s output. */
|
|
@@ -53,6 +54,22 @@ export declare class MediaAsset {
|
|
|
53
54
|
moveTo(folder: string, options?: RequestOptions): Promise<this>;
|
|
54
55
|
/** REPLACES the tag list — `PATCH` replaces, it does not append. */
|
|
55
56
|
setTags(tags: readonly string[], options?: RequestOptions): Promise<this>;
|
|
57
|
+
/**
|
|
58
|
+
* Swaps the bytes behind this asset (D67). `id`, `reference`, `url`,
|
|
59
|
+
* `filename` and `folder` all survive; `hash`, `sizeInBytes` and
|
|
60
|
+
* `contentType` are re-read from the server's answer.
|
|
61
|
+
*
|
|
62
|
+
* Every entry referencing it now resolves to the new file, without any of
|
|
63
|
+
* them being rewritten — which is the point, and the reason the server asks
|
|
64
|
+
* for `media:replace` **and** `entries:update` at every scope that refers
|
|
65
|
+
* to it.
|
|
66
|
+
*
|
|
67
|
+
* The new file must keep the current extension: the blob key's suffix is
|
|
68
|
+
* derived from the filename at upload and is the visible tail of the URL on
|
|
69
|
+
* a bucket-backed instance, so a `.png` asset takes a `.png` replacement.
|
|
70
|
+
* Converting a file is a new upload, not a replacement of this one.
|
|
71
|
+
*/
|
|
72
|
+
replace(input: MediaReplace, options?: MediaReplaceOptions): Promise<this>;
|
|
56
73
|
/** Refused while an entry references it; `{ force: true }` also needs
|
|
57
74
|
* `entries:update` on every scope this asset reaches. */
|
|
58
75
|
delete(options?: MediaDeleteOptions): Promise<void>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning what a caller passed into the two things a multipart part needs: a
|
|
3
|
+
* `Blob` and a filename.
|
|
4
|
+
*
|
|
5
|
+
* Shared by `Media.upload` and `MediaAsset.replace` (D67) rather than copied
|
|
6
|
+
* into the second. The filename is not cosmetic on either path — the server
|
|
7
|
+
* reads the extension off it to decide what the library accepts, and on a
|
|
8
|
+
* replace to decide whether the file's type is changing at all — so the two
|
|
9
|
+
* entry points must derive it the same way.
|
|
10
|
+
*/
|
|
11
|
+
export declare class MediaFile {
|
|
12
|
+
static toBlob(bytes: Uint8Array | ArrayBuffer | Blob, contentType?: string): Blob;
|
|
13
|
+
/** A `File` carries its own name; a bare `Blob` does not, and sending it
|
|
14
|
+
* as "blob" is worse than refusing outright. */
|
|
15
|
+
static nameOf(input: Blob, explicit?: string): string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning what a caller passed into the two things a multipart part needs: a
|
|
3
|
+
* `Blob` and a filename.
|
|
4
|
+
*
|
|
5
|
+
* Shared by `Media.upload` and `MediaAsset.replace` (D67) rather than copied
|
|
6
|
+
* into the second. The filename is not cosmetic on either path — the server
|
|
7
|
+
* reads the extension off it to decide what the library accepts, and on a
|
|
8
|
+
* replace to decide whether the file's type is changing at all — so the two
|
|
9
|
+
* entry points must derive it the same way.
|
|
10
|
+
*/
|
|
11
|
+
export declare class MediaFile {
|
|
12
|
+
static toBlob(bytes: Uint8Array | ArrayBuffer | Blob, contentType?: string): Blob;
|
|
13
|
+
/** A `File` carries its own name; a bare `Blob` does not, and sending it
|
|
14
|
+
* as "blob" is worse than refusing outright. */
|
|
15
|
+
static nameOf(input: Blob, explicit?: string): string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RequestOptions } from "../request-options.cjs";
|
|
2
|
+
/** The fully-described replacement: raw bytes plus the name whose extension
|
|
3
|
+
* the server checks against the one the asset already has. No `folder` — a
|
|
4
|
+
* replace changes bytes and nothing else about where the asset sits. */
|
|
5
|
+
export interface MediaReplaceBytes {
|
|
6
|
+
bytes: Uint8Array | ArrayBuffer | Blob;
|
|
7
|
+
filename: string;
|
|
8
|
+
contentType?: string;
|
|
9
|
+
}
|
|
10
|
+
/** The filename to read when the input carries none — a `File` has `.name`,
|
|
11
|
+
* a bare `Blob` does not, and the extension is what the server checks. */
|
|
12
|
+
export interface MediaReplaceOptions extends RequestOptions {
|
|
13
|
+
filename?: string;
|
|
14
|
+
}
|
|
15
|
+
/** `MediaAsset.replace()`'s input: a fully-described replacement, or a
|
|
16
|
+
* runtime `File`/`Blob`. */
|
|
17
|
+
export type MediaReplace = MediaReplaceBytes | File | Blob;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RequestOptions } from "../request-options.js";
|
|
2
|
+
/** The fully-described replacement: raw bytes plus the name whose extension
|
|
3
|
+
* the server checks against the one the asset already has. No `folder` — a
|
|
4
|
+
* replace changes bytes and nothing else about where the asset sits. */
|
|
5
|
+
export interface MediaReplaceBytes {
|
|
6
|
+
bytes: Uint8Array | ArrayBuffer | Blob;
|
|
7
|
+
filename: string;
|
|
8
|
+
contentType?: string;
|
|
9
|
+
}
|
|
10
|
+
/** The filename to read when the input carries none — a `File` has `.name`,
|
|
11
|
+
* a bare `Blob` does not, and the extension is what the server checks. */
|
|
12
|
+
export interface MediaReplaceOptions extends RequestOptions {
|
|
13
|
+
filename?: string;
|
|
14
|
+
}
|
|
15
|
+
/** `MediaAsset.replace()`'s input: a fully-described replacement, or a
|
|
16
|
+
* runtime `File`/`Blob`. */
|
|
17
|
+
export type MediaReplace = MediaReplaceBytes | File | Blob;
|
package/dist/media/media.d.cts
CHANGED
|
@@ -30,8 +30,4 @@ export declare class Media {
|
|
|
30
30
|
* per-id outcomes live in the report, not in a thrown error. */
|
|
31
31
|
deleteMany(ids: readonly string[], options?: MediaDeleteOptions): Promise<MediaDeleteReport>;
|
|
32
32
|
private static toWireQuery;
|
|
33
|
-
private static toBlob;
|
|
34
|
-
/** A `File` carries its own name; a bare `Blob` does not, and uploading it
|
|
35
|
-
* as "blob" is worse than refusing outright. */
|
|
36
|
-
private static filenameOf;
|
|
37
33
|
}
|
package/dist/media/media.d.ts
CHANGED
|
@@ -30,8 +30,4 @@ export declare class Media {
|
|
|
30
30
|
* per-id outcomes live in the report, not in a thrown error. */
|
|
31
31
|
deleteMany(ids: readonly string[], options?: MediaDeleteOptions): Promise<MediaDeleteReport>;
|
|
32
32
|
private static toWireQuery;
|
|
33
|
-
private static toBlob;
|
|
34
|
-
/** A `File` carries its own name; a bare `Blob` does not, and uploading it
|
|
35
|
-
* as "blob" is worse than refusing outright. */
|
|
36
|
-
private static filenameOf;
|
|
37
33
|
}
|
|
@@ -31,6 +31,7 @@ export declare class ApiPath {
|
|
|
31
31
|
static mediaExtensions(): string;
|
|
32
32
|
static mediaAsset(id: string): string;
|
|
33
33
|
static mediaAssetUsages(id: string): string;
|
|
34
|
+
static mediaAssetContent(id: string): string;
|
|
34
35
|
static mediaBulkDelete(): string;
|
|
35
36
|
static mediaFolders(): string;
|
|
36
37
|
}
|
|
@@ -31,6 +31,7 @@ export declare class ApiPath {
|
|
|
31
31
|
static mediaExtensions(): string;
|
|
32
32
|
static mediaAsset(id: string): string;
|
|
33
33
|
static mediaAssetUsages(id: string): string;
|
|
34
|
+
static mediaAssetContent(id: string): string;
|
|
34
35
|
static mediaBulkDelete(): string;
|
|
35
36
|
static mediaFolders(): string;
|
|
36
37
|
}
|
|
@@ -42,4 +42,8 @@ export declare class Transport {
|
|
|
42
42
|
private transportFailure;
|
|
43
43
|
private buildHeaders;
|
|
44
44
|
private static normalizeUrl;
|
|
45
|
+
/** The caller's own `fetch`, or the runtime's bound to the global. A
|
|
46
|
+
* browser's `fetch` is a `Window` method and refuses every other receiver,
|
|
47
|
+
* so the default is stored already bound rather than bare. */
|
|
48
|
+
private static resolveFetch;
|
|
45
49
|
}
|
|
@@ -42,4 +42,8 @@ export declare class Transport {
|
|
|
42
42
|
private transportFailure;
|
|
43
43
|
private buildHeaders;
|
|
44
44
|
private static normalizeUrl;
|
|
45
|
+
/** The caller's own `fetch`, or the runtime's bound to the global. A
|
|
46
|
+
* browser's `fetch` is a `Window` method and refuses every other receiver,
|
|
47
|
+
* so the default is stored already bound rather than bare. */
|
|
48
|
+
private static resolveFetch;
|
|
45
49
|
}
|