@org-quicko/silo-client 1.0.0 → 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 +166 -124
- package/dist/collections/collection-handle.d.cts +15 -23
- package/dist/collections/collection-handle.d.ts +15 -23
- package/dist/collections/collections.d.cts +3 -5
- package/dist/collections/collections.d.ts +3 -5
- package/dist/entries/entry-envelope.d.cts +18 -0
- package/dist/entries/entry-envelope.d.ts +18 -0
- package/dist/entries/entry-read-options.d.cts +16 -0
- package/dist/entries/entry-read-options.d.ts +16 -0
- package/dist/entries/entry-reader.d.cts +16 -16
- package/dist/entries/entry-reader.d.ts +16 -16
- package/dist/entries/entry.d.cts +11 -23
- package/dist/entries/entry.d.ts +11 -23
- package/dist/errors/network-error.d.cts +5 -0
- package/dist/errors/network-error.d.ts +5 -0
- package/dist/index.cjs +87 -170
- package/dist/index.d.cts +4 -5
- package/dist/index.d.ts +4 -5
- package/dist/index.js +87 -170
- 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/search/search-hit.d.cts +5 -5
- package/dist/search/search-hit.d.ts +5 -5
- package/dist/search/search.d.cts +4 -1
- package/dist/search/search.d.ts +4 -1
- 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 +58 -58
- package/dist/collections/reserved-field-names.d.cts +0 -9
- package/dist/collections/reserved-field-names.d.ts +0 -9
- package/dist/entries/entry-base.d.cts +0 -31
- package/dist/entries/entry-base.d.ts +0 -31
- package/dist/entries/entry-mapper.d.cts +0 -13
- package/dist/entries/entry-mapper.d.ts +0 -13
- package/dist/entries/entry-payload.d.cts +0 -12
- package/dist/entries/entry-payload.d.ts +0 -12
- package/dist/entries/resolved-entry.d.cts +0 -11
- package/dist/entries/resolved-entry.d.ts +0 -11
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
|
|
@@ -678,96 +701,6 @@ class ProjectVariables {
|
|
|
678
701
|
}
|
|
679
702
|
}
|
|
680
703
|
|
|
681
|
-
// src/entries/entry-mapper.ts
|
|
682
|
-
class EntryMapper {
|
|
683
|
-
static fieldsOf(payload) {
|
|
684
|
-
const { id: _id, rev: _rev, created_at: _createdAt, updated_at: _updatedAt, ...fields } = payload;
|
|
685
|
-
return fields;
|
|
686
|
-
}
|
|
687
|
-
static toPayload(id, rev, fields, createdAt, updatedAt) {
|
|
688
|
-
return {
|
|
689
|
-
id,
|
|
690
|
-
rev,
|
|
691
|
-
...fields,
|
|
692
|
-
created_at: createdAt.toISOString(),
|
|
693
|
-
updated_at: updatedAt.toISOString()
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// src/entries/entry-base.ts
|
|
699
|
-
class EntryBase {
|
|
700
|
-
context;
|
|
701
|
-
id;
|
|
702
|
-
rev;
|
|
703
|
-
createdAt;
|
|
704
|
-
updatedAt;
|
|
705
|
-
fields;
|
|
706
|
-
constructor(context, payload) {
|
|
707
|
-
this.context = context;
|
|
708
|
-
this.id = String(payload.id);
|
|
709
|
-
this.rev = Number(payload.rev);
|
|
710
|
-
this.createdAt = new Date(payload.created_at);
|
|
711
|
-
this.updatedAt = new Date(payload.updated_at);
|
|
712
|
-
this.fields = EntryMapper.fieldsOf(payload);
|
|
713
|
-
}
|
|
714
|
-
toJSON() {
|
|
715
|
-
return EntryMapper.toPayload(this.id, this.rev, this.fields, this.createdAt, this.updatedAt);
|
|
716
|
-
}
|
|
717
|
-
async delete(options = {}) {
|
|
718
|
-
await this.context.transport.empty({
|
|
719
|
-
method: "DELETE",
|
|
720
|
-
path: ApiPath.entry(this.context.project, this.context.environment, this.context.collection, this.id),
|
|
721
|
-
query: { rev: this.rev },
|
|
722
|
-
...options
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
// src/entries/resolved-entry.ts
|
|
728
|
-
class ResolvedEntry extends EntryBase {
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
// src/entries/entry.ts
|
|
732
|
-
class Entry extends ResolvedEntry {
|
|
733
|
-
saving = false;
|
|
734
|
-
async save(options = {}) {
|
|
735
|
-
if (this.saving) {
|
|
736
|
-
throw new Error(`cannot save entry "${this.id}": a previous save() on this instance has not finished yet`);
|
|
737
|
-
}
|
|
738
|
-
this.saving = true;
|
|
739
|
-
try {
|
|
740
|
-
const payload = await this.context.transport.json({
|
|
741
|
-
method: "PUT",
|
|
742
|
-
path: ApiPath.entry(this.context.project, this.context.environment, this.context.collection, this.id),
|
|
743
|
-
query: { rev: this.rev, variables: "raw" },
|
|
744
|
-
body: this.fields,
|
|
745
|
-
...options
|
|
746
|
-
});
|
|
747
|
-
this.adopt(payload);
|
|
748
|
-
return this;
|
|
749
|
-
} finally {
|
|
750
|
-
this.saving = false;
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
async refresh(options = {}) {
|
|
754
|
-
const payload = await this.context.transport.json({
|
|
755
|
-
method: "GET",
|
|
756
|
-
path: ApiPath.entry(this.context.project, this.context.environment, this.context.collection, this.id),
|
|
757
|
-
query: { variables: "raw" },
|
|
758
|
-
...options
|
|
759
|
-
});
|
|
760
|
-
this.adopt(payload);
|
|
761
|
-
return this;
|
|
762
|
-
}
|
|
763
|
-
adopt(payload) {
|
|
764
|
-
this.rev = Number(payload.rev);
|
|
765
|
-
this.createdAt = new Date(payload.created_at);
|
|
766
|
-
this.updatedAt = new Date(payload.updated_at);
|
|
767
|
-
this.fields = EntryMapper.fieldsOf(payload);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
|
|
771
704
|
// src/transport/page-payload.ts
|
|
772
705
|
class PagePayload {
|
|
773
706
|
static read(body) {
|
|
@@ -828,40 +761,39 @@ class EntryStream extends RowStream {
|
|
|
828
761
|
|
|
829
762
|
// src/entries/entry-reader.ts
|
|
830
763
|
class EntryReader {
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
this.
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
const payload = await this.context.transport.json({
|
|
764
|
+
scope;
|
|
765
|
+
collection;
|
|
766
|
+
constructor(scope, collection) {
|
|
767
|
+
this.scope = scope;
|
|
768
|
+
this.collection = collection;
|
|
769
|
+
}
|
|
770
|
+
get(id, options = {}) {
|
|
771
|
+
const { variables, ...request } = options;
|
|
772
|
+
return this.scope.transport.json({
|
|
841
773
|
method: "GET",
|
|
842
|
-
path: ApiPath.entry(this.
|
|
843
|
-
query: { variables
|
|
844
|
-
...
|
|
774
|
+
path: ApiPath.entry(this.scope.project, this.scope.environment, this.collection, id),
|
|
775
|
+
query: { variables },
|
|
776
|
+
...request
|
|
845
777
|
});
|
|
846
|
-
return this.wrap(payload);
|
|
847
778
|
}
|
|
848
779
|
async list(query = {}, options = {}) {
|
|
780
|
+
const { variables, ...request } = options;
|
|
849
781
|
const loader = (window2) => this.list({ ...query, limit: window2.limit, offset: window2.offset }, options);
|
|
850
|
-
const body = await this.
|
|
782
|
+
const body = await this.scope.transport.json({
|
|
851
783
|
method: "GET",
|
|
852
|
-
path: ApiPath.entries(this.
|
|
784
|
+
path: ApiPath.entries(this.scope.project, this.scope.environment, this.collection),
|
|
853
785
|
query: {
|
|
854
786
|
limit: query.limit,
|
|
855
787
|
offset: query.offset,
|
|
856
788
|
filter: query.where?.toJSON(),
|
|
857
789
|
sort: query.sort === undefined ? undefined : String(query.sort),
|
|
858
|
-
variables
|
|
790
|
+
variables
|
|
859
791
|
},
|
|
860
|
-
...
|
|
792
|
+
...request
|
|
861
793
|
});
|
|
862
794
|
const page = PagePayload.read(body);
|
|
863
795
|
const window = new PageWindow(page.limit ?? query.limit ?? 50, page.offset ?? query.offset ?? 0);
|
|
864
|
-
return new EntryPage(page.rows
|
|
796
|
+
return new EntryPage(page.rows, page.total, window, loader);
|
|
865
797
|
}
|
|
866
798
|
all(query = {}, options = {}) {
|
|
867
799
|
const loader = async (window) => {
|
|
@@ -1035,15 +967,15 @@ class Search {
|
|
|
1035
967
|
const window = new PageWindow(page.limit ?? query.limit ?? 50, page.offset ?? query.offset ?? 0);
|
|
1036
968
|
const truncated = body.truncated === true;
|
|
1037
969
|
const engine = body.engine;
|
|
1038
|
-
const hits = page.rows.map((hit) =>
|
|
970
|
+
const hits = page.rows.map((hit) => Search.toHit(hit));
|
|
1039
971
|
return new SearchPage(hits, page.total, window, truncated, engine, loader);
|
|
1040
972
|
}
|
|
1041
|
-
toHit(payload) {
|
|
973
|
+
static toHit(payload) {
|
|
1042
974
|
return {
|
|
1043
975
|
project: payload.project,
|
|
1044
976
|
environment: payload.env,
|
|
1045
977
|
collection: payload.collection,
|
|
1046
|
-
entry:
|
|
978
|
+
entry: payload.entry,
|
|
1047
979
|
snippets: payload.snippets
|
|
1048
980
|
};
|
|
1049
981
|
}
|
|
@@ -1106,36 +1038,24 @@ class CollectionHandle {
|
|
|
1106
1038
|
name;
|
|
1107
1039
|
filter = new TypedFilter;
|
|
1108
1040
|
schema;
|
|
1109
|
-
|
|
1110
|
-
context;
|
|
1111
|
-
resolved;
|
|
1041
|
+
reader;
|
|
1112
1042
|
constructor(scope, name) {
|
|
1113
1043
|
this.scope = scope;
|
|
1114
1044
|
this.name = name;
|
|
1115
1045
|
this.schema = new CollectionSchema(scope, name);
|
|
1116
|
-
this.
|
|
1117
|
-
transport: scope.transport,
|
|
1118
|
-
project: scope.project,
|
|
1119
|
-
environment: scope.environment,
|
|
1120
|
-
collection: name
|
|
1121
|
-
};
|
|
1122
|
-
this.resolved = new EntryReader(this.context, undefined, (payload) => new ResolvedEntry(this.context, payload));
|
|
1123
|
-
this.editable = new EntryReader(this.context, "raw", (payload) => new Entry(this.context, payload));
|
|
1046
|
+
this.reader = new EntryReader(scope, name);
|
|
1124
1047
|
}
|
|
1125
1048
|
get(id, options = {}) {
|
|
1126
|
-
return this.
|
|
1127
|
-
}
|
|
1128
|
-
edit(id, options = {}) {
|
|
1129
|
-
return this.editable.get(id, options);
|
|
1049
|
+
return this.reader.get(id, options);
|
|
1130
1050
|
}
|
|
1131
1051
|
list(query = {}, options = {}) {
|
|
1132
|
-
return this.
|
|
1052
|
+
return this.reader.list(query, options);
|
|
1133
1053
|
}
|
|
1134
1054
|
all(query = {}, options = {}) {
|
|
1135
|
-
return this.
|
|
1055
|
+
return this.reader.all(query, options);
|
|
1136
1056
|
}
|
|
1137
1057
|
pages(query = {}, options = {}) {
|
|
1138
|
-
return this.
|
|
1058
|
+
return this.reader.pages(query, options);
|
|
1139
1059
|
}
|
|
1140
1060
|
create(fields, options = {}) {
|
|
1141
1061
|
return this.write("POST", ApiPath.entries(this.scope.project, this.scope.environment, this.name), fields, undefined, options);
|
|
@@ -1165,23 +1085,14 @@ class CollectionHandle {
|
|
|
1165
1085
|
});
|
|
1166
1086
|
return RenameReport.fromWire(payload);
|
|
1167
1087
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1088
|
+
write(method, path, fields, rev, options) {
|
|
1089
|
+
return this.scope.transport.json({
|
|
1170
1090
|
method,
|
|
1171
1091
|
path,
|
|
1172
1092
|
query: { rev, variables: "raw" },
|
|
1173
1093
|
body: fields,
|
|
1174
1094
|
...options
|
|
1175
1095
|
});
|
|
1176
|
-
return new Entry(this.context, payload);
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
// src/collections/reserved-field-names.ts
|
|
1181
|
-
class ReservedFieldNames {
|
|
1182
|
-
static all = ["id", "rev", "seq", "created_at", "updated_at"];
|
|
1183
|
-
static isReserved(name) {
|
|
1184
|
-
return ReservedFieldNames.all.includes(name);
|
|
1185
1096
|
}
|
|
1186
1097
|
}
|
|
1187
1098
|
|
|
@@ -1200,7 +1111,6 @@ class Collections {
|
|
|
1200
1111
|
return body.items.map(Collections.toSummary);
|
|
1201
1112
|
}
|
|
1202
1113
|
async create(name, schema, options = {}) {
|
|
1203
|
-
Collections.warnOnReservedFields(schema);
|
|
1204
1114
|
return this.scope.transport.json({
|
|
1205
1115
|
method: "POST",
|
|
1206
1116
|
path: ApiPath.collections(this.scope.project, this.scope.environment),
|
|
@@ -1218,13 +1128,6 @@ class Collections {
|
|
|
1218
1128
|
updatedAt: new Date(payload.updated_at)
|
|
1219
1129
|
};
|
|
1220
1130
|
}
|
|
1221
|
-
static warnOnReservedFields(schema) {
|
|
1222
|
-
for (const name of Object.keys(schema.properties ?? {})) {
|
|
1223
|
-
if (ReservedFieldNames.isReserved(name)) {
|
|
1224
|
-
console.warn(`@org-quicko/silo-client: collection schema declares reserved field "${name}", which the server never returns`);
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
1131
|
}
|
|
1229
1132
|
|
|
1230
1133
|
// src/variables/environment-variables.ts
|
|
@@ -1591,12 +1494,19 @@ class NetworkError extends Error {
|
|
|
1591
1494
|
method;
|
|
1592
1495
|
path;
|
|
1593
1496
|
constructor(method, path, cause) {
|
|
1594
|
-
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 });
|
|
1595
1498
|
this.name = "NetworkError";
|
|
1596
1499
|
this.method = method;
|
|
1597
1500
|
this.path = path;
|
|
1598
1501
|
Object.setPrototypeOf(this, NetworkError.prototype);
|
|
1599
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
|
+
}
|
|
1600
1510
|
}
|
|
1601
1511
|
|
|
1602
1512
|
// src/errors/timeout-error.ts
|
|
@@ -1710,7 +1620,7 @@ class Transport {
|
|
|
1710
1620
|
this.key = options.key;
|
|
1711
1621
|
this.headers = options.headers ?? {};
|
|
1712
1622
|
this.timeoutMilliseconds = options.timeoutMilliseconds;
|
|
1713
|
-
this.fetchFunction = options.fetch
|
|
1623
|
+
this.fetchFunction = Transport.resolveFetch(options.fetch);
|
|
1714
1624
|
}
|
|
1715
1625
|
async json(request) {
|
|
1716
1626
|
const response = await this.execute(request);
|
|
@@ -1746,9 +1656,10 @@ class Transport {
|
|
|
1746
1656
|
async execute(request, form) {
|
|
1747
1657
|
const abortSignals = new AbortSignals(request.signal, request.timeoutMilliseconds ?? this.timeoutMilliseconds);
|
|
1748
1658
|
const url = `${this.url}${request.path}${QueryString.build(request.query)}`;
|
|
1659
|
+
const sendRequest = this.fetchFunction;
|
|
1749
1660
|
let response;
|
|
1750
1661
|
try {
|
|
1751
|
-
response = await
|
|
1662
|
+
response = await sendRequest(url, {
|
|
1752
1663
|
method: request.method,
|
|
1753
1664
|
headers: this.buildHeaders(request, Boolean(form)),
|
|
1754
1665
|
body: form ?? (request.body === undefined ? undefined : JSON.stringify(request.body)),
|
|
@@ -1786,6 +1697,14 @@ class Transport {
|
|
|
1786
1697
|
static normalizeUrl(url) {
|
|
1787
1698
|
return url.replace(/\/+$/, "");
|
|
1788
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
|
+
}
|
|
1789
1708
|
}
|
|
1790
1709
|
|
|
1791
1710
|
// src/silo.ts
|
|
@@ -1870,6 +1789,7 @@ class RouteInventory {
|
|
|
1870
1789
|
"PATCH /api/media/:id",
|
|
1871
1790
|
"DELETE /api/media/:id",
|
|
1872
1791
|
"GET /api/media/:id/usages",
|
|
1792
|
+
"POST /api/media/:id/content",
|
|
1873
1793
|
"POST /api/media/delete",
|
|
1874
1794
|
"GET /api/media/folders",
|
|
1875
1795
|
"POST /api/media/folders",
|
|
@@ -1880,6 +1800,7 @@ class RouteInventory {
|
|
|
1880
1800
|
"GET /api/session": "key introspection, an operator surface",
|
|
1881
1801
|
"GET /api/keys": "keys and claims are an operator surface",
|
|
1882
1802
|
"POST /api/keys": "keys and claims are an operator surface",
|
|
1803
|
+
"PATCH /api/keys/:id": "keys and claims are an operator surface",
|
|
1883
1804
|
"DELETE /api/keys/:id": "keys and claims are an operator surface",
|
|
1884
1805
|
"GET /api/audit": "an operator surface",
|
|
1885
1806
|
"GET /api/observability": "an operator surface",
|
|
@@ -2007,8 +1928,6 @@ export {
|
|
|
2007
1928
|
Search,
|
|
2008
1929
|
RowStream,
|
|
2009
1930
|
RouteInventory,
|
|
2010
|
-
ResolvedEntry,
|
|
2011
|
-
ReservedFieldNames,
|
|
2012
1931
|
RequestAbortedError,
|
|
2013
1932
|
RenameReport,
|
|
2014
1933
|
Projects,
|
|
@@ -2044,8 +1963,6 @@ export {
|
|
|
2044
1963
|
EntryReader,
|
|
2045
1964
|
EntryPageStream,
|
|
2046
1965
|
EntryPage,
|
|
2047
|
-
EntryBase,
|
|
2048
|
-
Entry,
|
|
2049
1966
|
ConflictError,
|
|
2050
1967
|
Collections,
|
|
2051
1968
|
CollectionSchema,
|
|
@@ -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
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Entry } from "../entries/entry.cjs";
|
|
2
2
|
import type { SearchSnippet } from "./search-snippet.cjs";
|
|
3
3
|
/**
|
|
4
4
|
* One result. The location sits on the hit rather than on the entry, which is
|
|
5
5
|
* what lets a caller link to a result found outside the scope on screen, and
|
|
6
6
|
* `environment` is mapped from the wire's `env`.
|
|
7
7
|
*
|
|
8
|
-
* The entry is
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The entry is untyped: a result is not addressed to one collection, so there
|
|
9
|
+
* is no `Fields` to give it. Its `{{NAME}}` templates are resolved, so read it
|
|
10
|
+
* again through `collection.get(id, { variables: "raw" })` before editing.
|
|
11
11
|
*/
|
|
12
12
|
export interface SearchHit {
|
|
13
13
|
readonly project: string;
|
|
14
14
|
readonly environment: string;
|
|
15
15
|
readonly collection: string;
|
|
16
|
-
readonly entry:
|
|
16
|
+
readonly entry: Entry;
|
|
17
17
|
readonly snippets: readonly SearchSnippet[];
|
|
18
18
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Entry } from "../entries/entry.js";
|
|
2
2
|
import type { SearchSnippet } from "./search-snippet.js";
|
|
3
3
|
/**
|
|
4
4
|
* One result. The location sits on the hit rather than on the entry, which is
|
|
5
5
|
* what lets a caller link to a result found outside the scope on screen, and
|
|
6
6
|
* `environment` is mapped from the wire's `env`.
|
|
7
7
|
*
|
|
8
|
-
* The entry is
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The entry is untyped: a result is not addressed to one collection, so there
|
|
9
|
+
* is no `Fields` to give it. Its `{{NAME}}` templates are resolved, so read it
|
|
10
|
+
* again through `collection.get(id, { variables: "raw" })` before editing.
|
|
11
11
|
*/
|
|
12
12
|
export interface SearchHit {
|
|
13
13
|
readonly project: string;
|
|
14
14
|
readonly environment: string;
|
|
15
15
|
readonly collection: string;
|
|
16
|
-
readonly entry:
|
|
16
|
+
readonly entry: Entry;
|
|
17
17
|
readonly snippets: readonly SearchSnippet[];
|
|
18
18
|
}
|
package/dist/search/search.d.cts
CHANGED
|
@@ -14,5 +14,8 @@ export declare class Search {
|
|
|
14
14
|
private readonly reach;
|
|
15
15
|
constructor(transport: Transport, reach: SearchReach);
|
|
16
16
|
run(query: SearchQuery, options?: RequestOptions): Promise<SearchPage>;
|
|
17
|
-
|
|
17
|
+
/** `env` is the only rename: the hit's own location, which sits here rather
|
|
18
|
+
* than on the entry so a result found outside the scope on screen can still
|
|
19
|
+
* be linked to. */
|
|
20
|
+
private static toHit;
|
|
18
21
|
}
|
package/dist/search/search.d.ts
CHANGED
|
@@ -14,5 +14,8 @@ export declare class Search {
|
|
|
14
14
|
private readonly reach;
|
|
15
15
|
constructor(transport: Transport, reach: SearchReach);
|
|
16
16
|
run(query: SearchQuery, options?: RequestOptions): Promise<SearchPage>;
|
|
17
|
-
|
|
17
|
+
/** `env` is the only rename: the hit's own location, which sits here rather
|
|
18
|
+
* than on the entry so a result found outside the scope on screen can still
|
|
19
|
+
* be linked to. */
|
|
20
|
+
private static toHit;
|
|
18
21
|
}
|
|
@@ -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
|
}
|