@hot-updater/standalone 1.0.0-rc.2 → 1.0.0-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +171 -594
- package/dist/index.d.cts +25 -19
- package/dist/index.d.mts +25 -19
- package/dist/index.mjs +171 -596
- package/package.json +10 -4
package/dist/index.mjs
CHANGED
|
@@ -1,226 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//#region src/standaloneBundleRows.ts
|
|
3
|
-
async function loadRows(remote, model) {
|
|
4
|
-
return model === "bundles" ? remote.loadBundleRows() : (await remote.loadBundles()).flatMap(bundleToPatchRows);
|
|
5
|
-
}
|
|
6
|
-
//#endregion
|
|
7
|
-
//#region src/standaloneDatabaseQuery.ts
|
|
8
|
-
const compare$1 = (left, right) => {
|
|
9
|
-
if (typeof left === "number" && typeof right === "number") return left - right;
|
|
10
|
-
return String(left).localeCompare(String(right));
|
|
11
|
-
};
|
|
12
|
-
const compareString = (actual, expected, mode, predicate) => {
|
|
13
|
-
if (typeof actual !== "string") return false;
|
|
14
|
-
return mode === "insensitive" ? predicate(actual.toLocaleLowerCase(), expected.toLocaleLowerCase()) : predicate(actual, expected);
|
|
15
|
-
};
|
|
16
|
-
const matchesCondition = (row, condition) => {
|
|
17
|
-
const actual = Reflect.get(row, condition.field);
|
|
18
|
-
const expected = Reflect.get(condition, "value");
|
|
19
|
-
switch (Reflect.get(condition, "operator") ?? "eq") {
|
|
20
|
-
case "eq":
|
|
21
|
-
if (typeof expected === "string") return compareString(actual, expected, Reflect.get(condition, "mode"), (value, query) => value === query);
|
|
22
|
-
return actual === expected;
|
|
23
|
-
case "ne":
|
|
24
|
-
if (actual === null || actual === void 0) return false;
|
|
25
|
-
if (typeof expected === "string") return !compareString(actual, expected, Reflect.get(condition, "mode"), (value, query) => value === query);
|
|
26
|
-
return actual !== expected;
|
|
27
|
-
case "gt":
|
|
28
|
-
if (actual === null || actual === void 0) return false;
|
|
29
|
-
return compare$1(actual, expected) > 0;
|
|
30
|
-
case "gte":
|
|
31
|
-
if (actual === null || actual === void 0) return false;
|
|
32
|
-
return compare$1(actual, expected) >= 0;
|
|
33
|
-
case "lt":
|
|
34
|
-
if (actual === null || actual === void 0) return false;
|
|
35
|
-
return compare$1(actual, expected) < 0;
|
|
36
|
-
case "lte":
|
|
37
|
-
if (actual === null || actual === void 0) return false;
|
|
38
|
-
return compare$1(actual, expected) <= 0;
|
|
39
|
-
case "in": return Array.isArray(expected) && expected.some((candidate) => candidate === actual);
|
|
40
|
-
case "not_in": return Array.isArray(expected) && (expected.length === 0 || actual !== null && actual !== void 0 && expected.every((candidate) => candidate !== actual));
|
|
41
|
-
case "contains": return typeof expected === "string" ? compareString(actual, expected, Reflect.get(condition, "mode"), (value, query) => value.includes(query)) : false;
|
|
42
|
-
case "starts_with": return typeof expected === "string" ? compareString(actual, expected, Reflect.get(condition, "mode"), (value, query) => value.startsWith(query)) : false;
|
|
43
|
-
case "ends_with": return typeof expected === "string" ? compareString(actual, expected, Reflect.get(condition, "mode"), (value, query) => value.endsWith(query)) : false;
|
|
44
|
-
default: return false;
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
const matchesStandaloneWhere = (row, where) => {
|
|
48
|
-
if (!where || where.length === 0) return true;
|
|
49
|
-
let result = matchesCondition(row, where[0]);
|
|
50
|
-
for (const condition of where.slice(1)) {
|
|
51
|
-
const current = matchesCondition(row, condition);
|
|
52
|
-
result = condition.connector === "OR" ? result || current : result && current;
|
|
53
|
-
}
|
|
54
|
-
return result;
|
|
55
|
-
};
|
|
56
|
-
const queryStandaloneRows = (rows, input) => {
|
|
57
|
-
const filtered = rows.filter((row) => matchesStandaloneWhere(row, input.where));
|
|
58
|
-
if (input.orderBy) {
|
|
59
|
-
const direction = input.orderBy.direction === "asc" ? 1 : -1;
|
|
60
|
-
filtered.sort((left, right) => compare$1(Reflect.get(left, input.orderBy?.field ?? "id"), Reflect.get(right, input.orderBy?.field ?? "id")) * direction);
|
|
61
|
-
}
|
|
62
|
-
const offset = input.offset ?? 0;
|
|
63
|
-
return filtered.slice(offset, offset + (input.limit ?? filtered.length));
|
|
64
|
-
};
|
|
65
|
-
//#endregion
|
|
66
|
-
//#region src/standaloneBundleReads.ts
|
|
67
|
-
const compare = (left, right) => {
|
|
68
|
-
if (typeof left === "number" && typeof right === "number") return left - right;
|
|
69
|
-
return String(left).localeCompare(String(right));
|
|
70
|
-
};
|
|
71
|
-
const queryBundleRows = (rows, input) => {
|
|
72
|
-
const filtered = queryStandaloneRows(rows, { where: input.where });
|
|
73
|
-
const orderBy = input.orderBy;
|
|
74
|
-
if (orderBy !== void 0) filtered.sort((left, right) => {
|
|
75
|
-
for (const clause of orderBy) {
|
|
76
|
-
const leftValue = Reflect.get(left, clause.field);
|
|
77
|
-
const rightValue = Reflect.get(right, clause.field);
|
|
78
|
-
if (leftValue === rightValue) continue;
|
|
79
|
-
if (leftValue === null || rightValue === null) {
|
|
80
|
-
const nulls = clause.nulls ?? (clause.direction === "asc" ? "last" : "first");
|
|
81
|
-
return leftValue === null ? nulls === "first" ? -1 : 1 : nulls === "first" ? 1 : -1;
|
|
82
|
-
}
|
|
83
|
-
const result = compare(leftValue, rightValue);
|
|
84
|
-
if (result !== 0) return clause.direction === "asc" ? result : -result;
|
|
85
|
-
}
|
|
86
|
-
return 0;
|
|
87
|
-
});
|
|
88
|
-
return (input.distinctOn === void 0 ? filtered : filtered.filter((row, index, ordered) => ordered.findIndex((candidate) => JSON.stringify(input.distinctOn?.fields.map((field) => Reflect.get(candidate, field))) === JSON.stringify(input.distinctOn?.fields.map((field) => Reflect.get(row, field)))) === index)).slice(input.offset, input.offset + input.limit);
|
|
89
|
-
};
|
|
90
|
-
const countDistinctRows = (rows, fields) => fields === void 0 ? rows.length : new Set(rows.map((row) => JSON.stringify(fields.map((field) => Reflect.get(row, field))))).size;
|
|
91
|
-
const createBundleReads = (remote) => ({
|
|
92
|
-
async count(input) {
|
|
93
|
-
switch (input.model) {
|
|
94
|
-
case "bundles": {
|
|
95
|
-
const where = input.where;
|
|
96
|
-
if (input.distinct === void 0) {
|
|
97
|
-
const remoteWindow = await remote.loadBundleWindow({
|
|
98
|
-
where,
|
|
99
|
-
limit: 1,
|
|
100
|
-
offset: 0
|
|
101
|
-
});
|
|
102
|
-
if (remoteWindow) return remoteWindow.total;
|
|
103
|
-
}
|
|
104
|
-
const rows = queryStandaloneRows(await loadRows(remote, "bundles"), { where });
|
|
105
|
-
return countDistinctRows(rows, input.distinct);
|
|
106
|
-
}
|
|
107
|
-
case "bundle_patches": {
|
|
108
|
-
const rows = queryStandaloneRows(await loadRows(remote, "bundle_patches"), { where: input.where });
|
|
109
|
-
return countDistinctRows(rows, input.distinct);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
async findOne(input) {
|
|
114
|
-
if (input.model === "bundles") {
|
|
115
|
-
const idSelector = input.where?.length === 1 ? input.where[0] : void 0;
|
|
116
|
-
if (idSelector?.field === "id" && (idSelector.operator === void 0 || idSelector.operator === "eq") && typeof idSelector.value === "string") {
|
|
117
|
-
const row = await remote.loadBundleRow(idSelector.value);
|
|
118
|
-
return row && matchesStandaloneWhere(row, input.where) ? row : null;
|
|
119
|
-
}
|
|
120
|
-
return queryStandaloneRows(await loadRows(remote, "bundles"), {
|
|
121
|
-
where: input.where,
|
|
122
|
-
limit: 1
|
|
123
|
-
})[0] ?? null;
|
|
124
|
-
}
|
|
125
|
-
return queryStandaloneRows(await loadRows(remote, "bundle_patches"), {
|
|
126
|
-
where: input.where,
|
|
127
|
-
limit: 1
|
|
128
|
-
})[0] ?? null;
|
|
129
|
-
},
|
|
130
|
-
async findMany(input) {
|
|
131
|
-
switch (input.model) {
|
|
132
|
-
case "bundles": {
|
|
133
|
-
const where = input.where;
|
|
134
|
-
const orderBy = input.orderBy;
|
|
135
|
-
const remoteSort = orderBy?.length === 1 && orderBy[0]?.field === "id" ? orderBy[0] : void 0;
|
|
136
|
-
if (input.distinctOn === void 0 && (orderBy === void 0 || remoteSort)) {
|
|
137
|
-
const remoteWindow = await remote.loadBundleWindow({
|
|
138
|
-
where,
|
|
139
|
-
limit: input.limit,
|
|
140
|
-
offset: input.offset,
|
|
141
|
-
orderBy: remoteSort
|
|
142
|
-
});
|
|
143
|
-
if (remoteWindow) return remoteWindow.rows;
|
|
144
|
-
}
|
|
145
|
-
return queryBundleRows(await loadRows(remote, "bundles"), {
|
|
146
|
-
distinctOn: input.distinctOn,
|
|
147
|
-
where,
|
|
148
|
-
limit: input.limit,
|
|
149
|
-
offset: input.offset,
|
|
150
|
-
orderBy: input.orderBy
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
case "bundle_patches": return queryBundleRows(await loadRows(remote, "bundle_patches"), {
|
|
154
|
-
distinctOn: input.distinctOn,
|
|
155
|
-
where: input.where,
|
|
156
|
-
limit: input.limit,
|
|
157
|
-
offset: input.offset,
|
|
158
|
-
orderBy: input.orderBy
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
//#endregion
|
|
164
|
-
//#region src/standaloneBundleReader.ts
|
|
165
|
-
const createStandaloneBundleReader = (remote) => createBundleReads(remote);
|
|
166
|
-
//#endregion
|
|
167
|
-
//#region src/standaloneBundleWhere.ts
|
|
168
|
-
const appendBundleWhere = (url, where) => {
|
|
169
|
-
const usedParameters = /* @__PURE__ */ new Set();
|
|
170
|
-
const setParameter = (parameter, value) => {
|
|
171
|
-
if (usedParameters.has(parameter)) return false;
|
|
172
|
-
usedParameters.add(parameter);
|
|
173
|
-
url.searchParams.set(parameter, value);
|
|
174
|
-
return true;
|
|
175
|
-
};
|
|
176
|
-
const appendParameter = (parameter, values) => {
|
|
177
|
-
if (values.length === 0 || usedParameters.has(parameter)) return false;
|
|
178
|
-
usedParameters.add(parameter);
|
|
179
|
-
for (const value of values) url.searchParams.append(parameter, value);
|
|
180
|
-
return true;
|
|
181
|
-
};
|
|
182
|
-
for (const [index, condition] of (where ?? []).entries()) {
|
|
183
|
-
if (index > 0 && condition.connector === "OR") return false;
|
|
184
|
-
const operator = condition.operator ?? "eq";
|
|
185
|
-
switch (condition.field) {
|
|
186
|
-
case "platform":
|
|
187
|
-
if (operator !== "eq" || typeof condition.value !== "string") return false;
|
|
188
|
-
if (!setParameter("platform", condition.value)) return false;
|
|
189
|
-
break;
|
|
190
|
-
case "id": {
|
|
191
|
-
let parameter;
|
|
192
|
-
switch (operator) {
|
|
193
|
-
case "eq":
|
|
194
|
-
parameter = "idEq";
|
|
195
|
-
break;
|
|
196
|
-
case "gt":
|
|
197
|
-
parameter = "idGt";
|
|
198
|
-
break;
|
|
199
|
-
case "gte":
|
|
200
|
-
parameter = "idGte";
|
|
201
|
-
break;
|
|
202
|
-
case "lt":
|
|
203
|
-
parameter = "idLt";
|
|
204
|
-
break;
|
|
205
|
-
case "lte": parameter = "idLte";
|
|
206
|
-
}
|
|
207
|
-
if (parameter && typeof condition.value === "string") {
|
|
208
|
-
if (!setParameter(parameter, condition.value)) return false;
|
|
209
|
-
break;
|
|
210
|
-
}
|
|
211
|
-
if (operator === "in" && Array.isArray(condition.value)) {
|
|
212
|
-
if (!condition.value.every((value) => typeof value === "string")) return false;
|
|
213
|
-
if (!appendParameter("idIn", condition.value)) return false;
|
|
214
|
-
break;
|
|
215
|
-
}
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
218
|
-
default: return false;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
return true;
|
|
222
|
-
};
|
|
223
|
-
//#endregion
|
|
1
|
+
import { DatabaseBundleNotFoundError, ReleaseCatalogMutationError, ReleaseManagementError, createStorageAdapter, parseStorageUri } from "@hot-updater/plugin-core";
|
|
224
2
|
//#region src/standaloneHttp.ts
|
|
225
3
|
var StandaloneDatabaseError = class extends Error {
|
|
226
4
|
code;
|
|
@@ -232,7 +10,7 @@ var StandaloneDatabaseError = class extends Error {
|
|
|
232
10
|
this.status = status;
|
|
233
11
|
}
|
|
234
12
|
};
|
|
235
|
-
const isRecord$
|
|
13
|
+
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
236
14
|
const createStandaloneHttp = (config) => {
|
|
237
15
|
const baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
238
16
|
const buildUrl = (path) => `${baseUrl}${path}`;
|
|
@@ -245,8 +23,8 @@ const createStandaloneHttp = (config) => {
|
|
|
245
23
|
let message = `Database request failed with status ${response.status}.`;
|
|
246
24
|
try {
|
|
247
25
|
const body = await response.json();
|
|
248
|
-
if (isRecord$
|
|
249
|
-
else if (isRecord$
|
|
26
|
+
if (isRecord$1(body) && typeof body.message === "string") message = body.message;
|
|
27
|
+
else if (isRecord$1(body) && typeof body.error === "string") message = body.error;
|
|
250
28
|
} catch (error) {
|
|
251
29
|
if (!(error instanceof SyntaxError)) throw error;
|
|
252
30
|
}
|
|
@@ -260,409 +38,206 @@ const createStandaloneHttp = (config) => {
|
|
|
260
38
|
throw new StandaloneDatabaseError("invalid-response", "Database response must contain JSON.", response.status);
|
|
261
39
|
}
|
|
262
40
|
};
|
|
263
|
-
const load = async (route, searchParams, isResult, invalidMessage, signal) => {
|
|
264
|
-
const url = new URL(buildUrl(route.path));
|
|
265
|
-
for (const [key, value] of Object.entries(searchParams)) url.searchParams.set(key, value);
|
|
266
|
-
const response = await fetch(url, {
|
|
267
|
-
method: "GET",
|
|
268
|
-
headers: headers(route.headers),
|
|
269
|
-
signal
|
|
270
|
-
});
|
|
271
|
-
const value = await parseJson(response);
|
|
272
|
-
if (!isResult(value)) throw new StandaloneDatabaseError("invalid-response", invalidMessage, response.status);
|
|
273
|
-
return value;
|
|
274
|
-
};
|
|
275
41
|
return {
|
|
276
42
|
buildUrl,
|
|
277
43
|
headers,
|
|
278
|
-
|
|
279
|
-
parseJson,
|
|
280
|
-
requestFailed
|
|
44
|
+
parseJson
|
|
281
45
|
};
|
|
282
46
|
};
|
|
283
47
|
//#endregion
|
|
284
|
-
//#region src/
|
|
285
|
-
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
48
|
+
//#region src/standaloneCore.ts
|
|
49
|
+
/** The admin API protocol this client speaks; the server's `/version` must report it or a later one. */
|
|
50
|
+
const STANDALONE_ADMIN_PROTOCOL = 2;
|
|
51
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
52
|
+
const MANAGEMENT_CODES = /* @__PURE__ */ new Set([
|
|
53
|
+
"ENABLED_RELEASE",
|
|
54
|
+
"RELEASE_NOT_FOUND",
|
|
55
|
+
"SCOPE_MOVE_UNSUPPORTED",
|
|
56
|
+
"TARGET_RELEASE_INVALID",
|
|
57
|
+
"VERSION_CONFLICT"
|
|
58
|
+
]);
|
|
59
|
+
const CATALOG_CODES = /* @__PURE__ */ new Set([
|
|
60
|
+
"CATALOG_GENERATION_EXHAUSTED",
|
|
61
|
+
"CATALOG_IDENTITY_MISSING",
|
|
62
|
+
"INVALID_SCOPE",
|
|
63
|
+
"NON_MONOTONIC_RELEASE_ID"
|
|
64
|
+
]);
|
|
65
|
+
/** A refusal the server named, as the error core throws in process; anything else as a request failure. */
|
|
66
|
+
const refusal = (status, value, fallback) => {
|
|
67
|
+
const code = isRecord(value) ? value.code : void 0;
|
|
68
|
+
const message = isRecord(value) && typeof value.error === "string" ? value.error : fallback;
|
|
69
|
+
if (typeof code === "string" && MANAGEMENT_CODES.has(code)) return new ReleaseManagementError(code, message);
|
|
70
|
+
if (typeof code === "string" && CATALOG_CODES.has(code)) return new ReleaseCatalogMutationError(code, message);
|
|
71
|
+
if (code === "BUNDLE_NOT_FOUND" && isRecord(value)) {
|
|
72
|
+
const id = /"([^"]+)"/u.exec(message)?.[1] ?? "";
|
|
73
|
+
return new DatabaseBundleNotFoundError(id);
|
|
295
74
|
}
|
|
296
|
-
return
|
|
297
|
-
};
|
|
298
|
-
const isChannelRow = (value) => isRecord$1(value) && isChannelText(value.id) && isChannelText(value.name);
|
|
299
|
-
const isBundle = (value) => isRecord$1(value) && typeof value.id === "string" && (value.platform === "ios" || value.platform === "android") && typeof value.fileHash === "string" && typeof value.storageUri === "string" && isByteSize(value.archiveByteSize) && (value.patches === void 0 || value.patches === null || Array.isArray(value.patches) && value.patches.every(isBundlePatch));
|
|
300
|
-
const isPaginationInfo = (value) => isRecord$1(value) && isNonNegativeInteger(value.total) && typeof value.hasNextPage === "boolean" && typeof value.hasPreviousPage === "boolean" && isNonNegativeInteger(value.currentPage) && value.currentPage >= 1 && isNonNegativeInteger(value.totalPages);
|
|
301
|
-
const isPaginatedResult = (value) => isRecord$1(value) && Array.isArray(value.data) && value.data.every(isBundle) && isPaginationInfo(value.pagination);
|
|
302
|
-
const hasChannels = (value) => isRecord$1(value) && isRecord$1(value.data) && Array.isArray(value.data.channels) && value.data.channels.every(isChannelRow);
|
|
303
|
-
const hasChannelInsertResult = (value) => isRecord$1(value) && isRecord$1(value.data) && isChannelRow(value.data.row) && typeof value.data.inserted === "boolean";
|
|
304
|
-
const hasChannelDeleteResult = (value) => isRecord$1(value) && isRecord$1(value.data) && (value.data.deleted === true || value.data.deleted === false && (value.data.reason === "not_empty" || value.data.reason === "not_found"));
|
|
305
|
-
//#endregion
|
|
306
|
-
//#region src/standaloneRoutes.ts
|
|
307
|
-
function bundlePath(bundleId) {
|
|
308
|
-
return `/bundles/${encodeURIComponent(bundleId)}`;
|
|
309
|
-
}
|
|
310
|
-
function channelPath(channelId) {
|
|
311
|
-
return `/channels/${encodeURIComponent(channelId)}`;
|
|
312
|
-
}
|
|
313
|
-
const defaultRoutes$1 = {
|
|
314
|
-
create: () => ({ path: "/bundles" }),
|
|
315
|
-
update: (bundleId) => ({ path: bundlePath(bundleId) }),
|
|
316
|
-
list: () => ({
|
|
317
|
-
path: "/bundles",
|
|
318
|
-
headers: { "Cache-Control": "no-cache" }
|
|
319
|
-
}),
|
|
320
|
-
channels: () => ({
|
|
321
|
-
path: "/channels",
|
|
322
|
-
headers: { "Cache-Control": "no-cache" }
|
|
323
|
-
}),
|
|
324
|
-
deleteChannel: (channelId) => ({
|
|
325
|
-
path: channelPath(channelId),
|
|
326
|
-
headers: {}
|
|
327
|
-
}),
|
|
328
|
-
retrieve: (bundleId) => ({
|
|
329
|
-
path: bundlePath(bundleId),
|
|
330
|
-
headers: { Accept: "application/json" }
|
|
331
|
-
}),
|
|
332
|
-
delete: (bundleId) => ({ path: bundlePath(bundleId) })
|
|
75
|
+
return new StandaloneDatabaseError("request-failed", message, status);
|
|
333
76
|
};
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
77
|
+
const keysetParams = ({ order, after, limit }) => ({
|
|
78
|
+
v: String(2),
|
|
79
|
+
limit: String(limit),
|
|
80
|
+
...order === void 0 ? {} : { order },
|
|
81
|
+
...after === void 0 ? {} : { cursor: after }
|
|
340
82
|
});
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
deleteChannel: defaultRoutes$1.deleteChannel,
|
|
349
|
-
create: () => createRoute$1(defaultRoutes$1.create(), config.routes?.create?.()),
|
|
350
|
-
update: (bundleId) => createRoute$1(defaultRoutes$1.update(bundleId), config.routes?.update?.(bundleId)),
|
|
351
|
-
retrieve: (bundleId) => createRoute$1(defaultRoutes$1.retrieve(bundleId), config.routes?.retrieve?.(bundleId)),
|
|
352
|
-
delete: (bundleId) => createRoute$1(defaultRoutes$1.delete(bundleId), config.routes?.delete?.(bundleId))
|
|
353
|
-
};
|
|
354
|
-
const http = createStandaloneHttp(config);
|
|
355
|
-
const loadBundles = async () => {
|
|
356
|
-
const bundles = [];
|
|
357
|
-
for (let page = 1;; page += 1) {
|
|
358
|
-
const route = routes.list();
|
|
359
|
-
const url = new URL(http.buildUrl(route.path));
|
|
360
|
-
url.searchParams.set("limit", String(PAGE_SIZE));
|
|
361
|
-
url.searchParams.set("page", String(page));
|
|
362
|
-
const response = await fetch(url, {
|
|
363
|
-
method: "GET",
|
|
364
|
-
headers: http.headers(route.headers)
|
|
365
|
-
});
|
|
366
|
-
const value = await http.parseJson(response);
|
|
367
|
-
if (!isPaginatedResult(value)) throw new StandaloneDatabaseError("invalid-response", "Invalid bundle list response.", response.status);
|
|
368
|
-
bundles.push(...value.data);
|
|
369
|
-
if (value.data.length < PAGE_SIZE || value.pagination.hasNextPage === false || bundles.length >= value.pagination.total) return bundles;
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
const loadChannels = async () => {
|
|
373
|
-
const route = routes.channels();
|
|
374
|
-
const response = await fetch(http.buildUrl(route.path), {
|
|
375
|
-
method: "GET",
|
|
376
|
-
headers: http.headers(route.headers)
|
|
377
|
-
});
|
|
378
|
-
const value = await http.parseJson(response);
|
|
379
|
-
if (!hasChannels(value)) throw new StandaloneDatabaseError("invalid-response", "Invalid channels response.", response.status);
|
|
380
|
-
return value.data.channels;
|
|
381
|
-
};
|
|
382
|
-
const bundlesToRows = async (bundles) => bundles.map((bundle) => bundleToRow(bundle));
|
|
383
|
-
const loadBundleWindow = async (input) => {
|
|
384
|
-
if (input.limit === 0) return {
|
|
385
|
-
rows: [],
|
|
386
|
-
total: 0
|
|
83
|
+
const filterParams = (filter) => {
|
|
84
|
+
switch (filter.kind) {
|
|
85
|
+
case "all": return {};
|
|
86
|
+
case "bundle": return { bundleId: filter.bundleId };
|
|
87
|
+
case "scope": return {
|
|
88
|
+
scopeKey: filter.scopeKey,
|
|
89
|
+
...filter.enabled === void 0 ? {} : { enabled: String(filter.enabled) }
|
|
387
90
|
};
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if (input.orderBy !== void 0) url.searchParams.set("orderDirection", input.orderBy.direction);
|
|
393
|
-
const pageAligned = input.limit > 0 && input.offset % input.limit === 0;
|
|
394
|
-
const remoteLimit = pageAligned ? input.limit : input.offset + input.limit;
|
|
395
|
-
if (remoteLimit > PAGE_SIZE) return null;
|
|
396
|
-
url.searchParams.set("limit", String(remoteLimit));
|
|
397
|
-
url.searchParams.set("page", String(pageAligned ? input.offset / input.limit + 1 : 1));
|
|
398
|
-
const response = await fetch(url, {
|
|
399
|
-
method: "GET",
|
|
400
|
-
headers: http.headers(route.headers)
|
|
401
|
-
});
|
|
402
|
-
const value = await http.parseJson(response);
|
|
403
|
-
if (!isPaginatedResult(value)) throw new StandaloneDatabaseError("invalid-response", "Invalid bundle list response.", response.status);
|
|
404
|
-
const bundles = pageAligned ? value.data : value.data.slice(input.offset, input.offset + input.limit);
|
|
405
|
-
return {
|
|
406
|
-
rows: await bundlesToRows(bundles),
|
|
407
|
-
total: value.pagination.total
|
|
91
|
+
case "channelPlatform": return {
|
|
92
|
+
channelId: filter.channelId,
|
|
93
|
+
platform: filter.platform,
|
|
94
|
+
...filter.enabled === void 0 ? {} : { enabled: String(filter.enabled) }
|
|
408
95
|
};
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Core's API over a self-hosted server's admin handler, protocol 2: the CLI's
|
|
100
|
+
* path to a database it cannot open itself. The first call checks that the
|
|
101
|
+
* server speaks protocol 2, so an older server fails fast.
|
|
102
|
+
*/
|
|
103
|
+
const createStandaloneCoreApi = (config) => {
|
|
104
|
+
const http = createStandaloneHttp(config);
|
|
105
|
+
let checked;
|
|
106
|
+
const checkProtocol = async () => {
|
|
107
|
+
const response = await fetch(http.buildUrl("/version"), { headers: http.headers({ "Cache-Control": "no-cache" }) });
|
|
108
|
+
const body = response.status === 404 ? {} : await http.parseJson(response);
|
|
109
|
+
const protocol = isRecord(body) ? body.adminProtocol : void 0;
|
|
110
|
+
if (typeof protocol !== "number" || protocol < 2) throw new StandaloneDatabaseError("request-failed", `The server at ${config.baseUrl} speaks admin API protocol ${typeof protocol === "number" ? protocol : 1}, and this CLI needs 2. Upgrade @hot-updater/server on the server.`, response.status);
|
|
409
111
|
};
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
body: JSON.stringify(input)
|
|
112
|
+
/** One admin request's JSON body, or null for 204 and for a GET that finds nothing; `/version` is checked once before the first. */
|
|
113
|
+
const request = async (method, path, { query = {}, body } = {}) => {
|
|
114
|
+
checked ??= checkProtocol().catch((error) => {
|
|
115
|
+
checked = void 0;
|
|
116
|
+
throw error;
|
|
416
117
|
});
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const response = await fetch(http.buildUrl(route.path), {
|
|
425
|
-
method: "DELETE",
|
|
426
|
-
headers: http.headers(route.headers)
|
|
118
|
+
await checked;
|
|
119
|
+
const url = new URL(http.buildUrl(path));
|
|
120
|
+
for (const [key, value] of Object.entries(query)) url.searchParams.set(key, value);
|
|
121
|
+
const response = await fetch(url, {
|
|
122
|
+
method,
|
|
123
|
+
headers: http.headers(method === "GET" ? { "Cache-Control": "no-cache" } : void 0),
|
|
124
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
427
125
|
});
|
|
428
|
-
if (response.status === 204) return
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
throw
|
|
126
|
+
if (response.status === 204) return null;
|
|
127
|
+
if (method === "GET" && response.status === 404) return null;
|
|
128
|
+
if (response.status === 503) throw new StandaloneDatabaseError("request-failed", `The server at ${config.baseUrl} answered ${method} ${path} with 503: its database is not ready. If it needs migrations, run \`hot-updater db migrate\` where the server's database is configured.`, 503);
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
const failure = await response.json().catch(() => null);
|
|
131
|
+
throw refusal(response.status, failure, `${method} ${path} failed with status ${response.status}.`);
|
|
434
132
|
}
|
|
435
|
-
if (!hasChannelDeleteResult(value)) throw new StandaloneDatabaseError("invalid-response", "Invalid Channel delete response.", response.status);
|
|
436
|
-
return value.data;
|
|
437
|
-
};
|
|
438
|
-
const loadBundle = async (bundleId) => {
|
|
439
|
-
const route = routes.retrieve(bundleId);
|
|
440
|
-
const response = await fetch(http.buildUrl(route.path), {
|
|
441
|
-
method: "GET",
|
|
442
|
-
headers: http.headers(route.headers)
|
|
443
|
-
});
|
|
444
|
-
if (response.status === 404) return null;
|
|
445
133
|
const value = await http.parseJson(response);
|
|
446
|
-
if (!
|
|
134
|
+
if (!isRecord(value)) throw new StandaloneDatabaseError("invalid-response", `The server answered ${method} ${path} with no JSON object.`, response.status);
|
|
447
135
|
return value;
|
|
448
136
|
};
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const route = routes.update(bundle.id);
|
|
456
|
-
const response = await fetch(http.buildUrl(route.path), {
|
|
457
|
-
method: "PATCH",
|
|
458
|
-
headers: http.headers(route.headers),
|
|
459
|
-
body: JSON.stringify(bundle)
|
|
460
|
-
});
|
|
461
|
-
await http.parseJson(response);
|
|
462
|
-
};
|
|
463
|
-
const createBundles = async (bundles) => {
|
|
464
|
-
const route = routes.create();
|
|
465
|
-
const response = await fetch(http.buildUrl(route.path), {
|
|
466
|
-
method: "POST",
|
|
467
|
-
headers: http.headers(route.headers),
|
|
468
|
-
body: JSON.stringify(bundles)
|
|
469
|
-
});
|
|
470
|
-
await http.parseJson(response);
|
|
471
|
-
};
|
|
472
|
-
const deleteBundle = async (bundleId) => {
|
|
473
|
-
const route = routes.delete(bundleId);
|
|
474
|
-
const response = await fetch(http.buildUrl(route.path), {
|
|
475
|
-
method: "DELETE",
|
|
476
|
-
headers: http.headers(route.headers)
|
|
477
|
-
});
|
|
478
|
-
await http.parseJson(response);
|
|
479
|
-
};
|
|
480
|
-
return {
|
|
481
|
-
createBundle: (bundle) => createBundles([bundle]),
|
|
482
|
-
createBundles,
|
|
483
|
-
deleteBundle,
|
|
484
|
-
deleteChannel,
|
|
485
|
-
insertChannel,
|
|
486
|
-
loadBundle,
|
|
487
|
-
loadBundleRow,
|
|
488
|
-
loadBundleRows,
|
|
489
|
-
loadBundles,
|
|
490
|
-
loadBundleWindow,
|
|
491
|
-
loadChannels,
|
|
492
|
-
updateBundle
|
|
137
|
+
/** A request's `data`, or null when it found nothing. */
|
|
138
|
+
const call = async (...args) => {
|
|
139
|
+
const value = await request(...args);
|
|
140
|
+
if (value === null) return null;
|
|
141
|
+
if (!("data" in value)) throw new StandaloneDatabaseError("invalid-response", `The server answered ${args[0]} ${args[1]} without data.`);
|
|
142
|
+
return value.data;
|
|
493
143
|
};
|
|
494
|
-
};
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
498
|
-
const data = (value, message) => {
|
|
499
|
-
if (!isRecord(value) || !("data" in value)) throw new StandaloneDatabaseError("invalid-response", message);
|
|
500
|
-
return value.data;
|
|
501
|
-
};
|
|
502
|
-
const createStandaloneReleaseRemote = (config) => {
|
|
503
|
-
const http = createStandaloneHttp(config);
|
|
504
|
-
const headers = () => http.headers({ "Cache-Control": "no-cache" });
|
|
144
|
+
const list = async (path, query) => await call("GET", path, { query }) ?? [];
|
|
145
|
+
const v2 = { v: String(2) };
|
|
146
|
+
const encode = encodeURIComponent;
|
|
505
147
|
return {
|
|
506
|
-
async
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
148
|
+
ready: async () => {
|
|
149
|
+
await list("/release-catalogs", {
|
|
150
|
+
...v2,
|
|
151
|
+
limit: "1"
|
|
152
|
+
});
|
|
510
153
|
},
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
154
|
+
getBundle: (id) => call("GET", `/bundles/${encode(id)}`, { query: v2 }),
|
|
155
|
+
listBundles: ({ platform, ...input }) => list("/bundles", {
|
|
156
|
+
...keysetParams(input),
|
|
157
|
+
...platform === void 0 ? {} : { platform }
|
|
158
|
+
}),
|
|
159
|
+
countBundles: async (platform) => {
|
|
160
|
+
const total = await call("GET", "/bundles/count", { query: {
|
|
161
|
+
...v2,
|
|
162
|
+
...platform === void 0 ? {} : { platform }
|
|
163
|
+
} });
|
|
164
|
+
if (typeof total !== "number") throw new StandaloneDatabaseError("invalid-response", "The server answered a bundle count without a number.");
|
|
165
|
+
return total;
|
|
520
166
|
},
|
|
521
|
-
async
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
return rows;
|
|
167
|
+
countBundleChildren: async (baseBundleIds) => {
|
|
168
|
+
const ids = [...new Set(baseBundleIds)];
|
|
169
|
+
if (ids.length === 0) return {};
|
|
170
|
+
return await call("GET", "/bundles/child-counts", { query: {
|
|
171
|
+
...v2,
|
|
172
|
+
ids: ids.join(",")
|
|
173
|
+
} }) ?? {};
|
|
529
174
|
},
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
175
|
+
listPatchesFromBase: (baseBundleId, input) => list(`/bundles/${encode(baseBundleId)}/children`, keysetParams(input)),
|
|
176
|
+
findBaseBundleIds: (candidateKey, bundleId, limit) => list(`/base-candidates/${encode(candidateKey)}`, {
|
|
177
|
+
before: bundleId,
|
|
178
|
+
limit: String(limit)
|
|
179
|
+
}),
|
|
180
|
+
getRelease: (id) => call("GET", `/releases/${encode(id)}`),
|
|
181
|
+
listReleases: ({ filter, ...input }) => list("/releases", {
|
|
182
|
+
...keysetParams(input),
|
|
183
|
+
...filterParams(filter)
|
|
184
|
+
}),
|
|
185
|
+
getReleaseCatalogRow: (scopeKey) => call("GET", `/release-catalogs/${encode(scopeKey)}`),
|
|
186
|
+
listReleaseCatalogs: (input) => list("/release-catalogs", keysetParams(input)),
|
|
187
|
+
listChannels: () => list("/channels", v2),
|
|
188
|
+
findChannelByName: (name) => call("GET", "/channels", { query: {
|
|
189
|
+
...v2,
|
|
190
|
+
name
|
|
191
|
+
} }),
|
|
192
|
+
deploy: async (deployments) => await call("POST", "/releases", { body: { deployments } }),
|
|
193
|
+
updateReleasePolicy: async ({ releaseId, ...input }) => await call("PATCH", `/releases/${encode(releaseId)}`, { body: input }),
|
|
194
|
+
preflightReleasePolicy: async ({ releaseId, ...input }) => await call("POST", `/releases/${encode(releaseId)}/preflight`, { body: input }),
|
|
195
|
+
deleteRelease: async ({ releaseId, expectedRevision }) => await call("DELETE", `/releases/${encode(releaseId)}`, { query: {
|
|
196
|
+
confirm: releaseId,
|
|
197
|
+
...expectedRevision === void 0 ? {} : { expectedRevision: String(expectedRevision) }
|
|
198
|
+
} }),
|
|
199
|
+
promoteRelease: async ({ releaseId, ...input }) => await call("POST", `/releases/${encode(releaseId)}/promote`, { body: input }),
|
|
200
|
+
rebuildReleaseCatalog: async (scopeKey) => await call("POST", `/release-catalogs/${encode(scopeKey)}/rebuild`),
|
|
201
|
+
preflightReleaseCatalogRebuild: async (scopeKey) => await call("POST", `/release-catalogs/${encode(scopeKey)}/preflight`),
|
|
202
|
+
ensureChannel: async (name) => await call("POST", "/channels", {
|
|
203
|
+
query: v2,
|
|
204
|
+
body: { name }
|
|
205
|
+
}),
|
|
206
|
+
deleteChannel: async (id) => {
|
|
207
|
+
return await call("DELETE", `/channels/${encode(id)}`).catch((error) => {
|
|
208
|
+
if (error instanceof StandaloneDatabaseError && error.status === 409) return {
|
|
209
|
+
deleted: false,
|
|
210
|
+
reason: "not_empty"
|
|
211
|
+
};
|
|
212
|
+
if (error instanceof StandaloneDatabaseError && error.status === 404) return {
|
|
213
|
+
deleted: false,
|
|
214
|
+
reason: "not_found"
|
|
215
|
+
};
|
|
216
|
+
throw error;
|
|
217
|
+
}) ?? { deleted: true };
|
|
534
218
|
},
|
|
535
|
-
async
|
|
536
|
-
|
|
537
|
-
url.searchParams.set("limit", String(input.limit));
|
|
538
|
-
if (input.afterScopeKey !== void 0) url.searchParams.set("afterScopeKey", input.afterScopeKey);
|
|
539
|
-
const response = await fetch(url, { headers: headers() });
|
|
540
|
-
const rows = data(await http.parseJson(response), "Invalid Release catalog list response.");
|
|
541
|
-
if (!Array.isArray(rows)) throw new StandaloneDatabaseError("invalid-response", "Invalid Release catalog list response.", response.status);
|
|
542
|
-
return rows;
|
|
219
|
+
updateBundle: async (id, update) => {
|
|
220
|
+
await request("PATCH", `/bundles/${encode(id)}`, { body: update });
|
|
543
221
|
},
|
|
544
|
-
async
|
|
545
|
-
|
|
546
|
-
body: JSON.stringify(input),
|
|
547
|
-
headers: http.headers(),
|
|
548
|
-
method: "POST"
|
|
549
|
-
});
|
|
550
|
-
return data(await http.parseJson(response), "Invalid database commit response.");
|
|
222
|
+
deleteBundles: async (ids) => {
|
|
223
|
+
await request("POST", "/bundles/delete", { body: { ids } });
|
|
551
224
|
}
|
|
552
225
|
};
|
|
553
226
|
};
|
|
554
227
|
//#endregion
|
|
555
228
|
//#region src/standaloneRepository.ts
|
|
556
|
-
const toBundleWhere = (where) => {
|
|
557
|
-
if (!where) return [];
|
|
558
|
-
const filters = [];
|
|
559
|
-
if (where.platform !== void 0) filters.push({
|
|
560
|
-
field: "platform",
|
|
561
|
-
value: where.platform
|
|
562
|
-
});
|
|
563
|
-
if (where.id?.eq !== void 0) filters.push({
|
|
564
|
-
field: "id",
|
|
565
|
-
value: where.id.eq
|
|
566
|
-
});
|
|
567
|
-
if (where.id?.gt !== void 0) filters.push({
|
|
568
|
-
field: "id",
|
|
569
|
-
operator: "gt",
|
|
570
|
-
value: where.id.gt
|
|
571
|
-
});
|
|
572
|
-
if (where.id?.gte !== void 0) filters.push({
|
|
573
|
-
field: "id",
|
|
574
|
-
operator: "gte",
|
|
575
|
-
value: where.id.gte
|
|
576
|
-
});
|
|
577
|
-
if (where.id?.lt !== void 0) filters.push({
|
|
578
|
-
field: "id",
|
|
579
|
-
operator: "lt",
|
|
580
|
-
value: where.id.lt
|
|
581
|
-
});
|
|
582
|
-
if (where.id?.lte !== void 0) filters.push({
|
|
583
|
-
field: "id",
|
|
584
|
-
operator: "lte",
|
|
585
|
-
value: where.id.lte
|
|
586
|
-
});
|
|
587
|
-
if (where.id?.in !== void 0) filters.push({
|
|
588
|
-
field: "id",
|
|
589
|
-
operator: "in",
|
|
590
|
-
value: where.id.in
|
|
591
|
-
});
|
|
592
|
-
return filters;
|
|
593
|
-
};
|
|
594
229
|
/**
|
|
595
|
-
*
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
230
|
+
* A self-hosted server's database for the CLI and console: core's API over
|
|
231
|
+
* the server's admin API protocol 2, and GETs on its admin handler for the
|
|
232
|
+
* routes core does not cover, which its plugins serve. Plugins' tables belong
|
|
233
|
+
* to the server's own database.
|
|
599
234
|
*/
|
|
600
235
|
const standaloneRepository = (config) => {
|
|
601
|
-
const
|
|
602
|
-
const releaseRemote = createStandaloneReleaseRemote(config);
|
|
603
|
-
const bundleReader = createStandaloneBundleReader(remote);
|
|
236
|
+
const http = createStandaloneHttp(config);
|
|
604
237
|
return Object.freeze({
|
|
605
238
|
name: "standalone-repository",
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
async findById(id) {
|
|
609
|
-
return await bundleReader.findOne({
|
|
610
|
-
model: "bundles",
|
|
611
|
-
where: [{
|
|
612
|
-
field: "id",
|
|
613
|
-
value: id
|
|
614
|
-
}]
|
|
615
|
-
});
|
|
616
|
-
},
|
|
617
|
-
async findMany(query) {
|
|
618
|
-
return await bundleReader.findMany({
|
|
619
|
-
model: "bundles",
|
|
620
|
-
where: toBundleWhere(query.where),
|
|
621
|
-
limit: query.limit,
|
|
622
|
-
offset: query.offset,
|
|
623
|
-
orderBy: [query.orderBy]
|
|
624
|
-
});
|
|
625
|
-
},
|
|
626
|
-
count: (where) => bundleReader.count({
|
|
627
|
-
model: "bundles",
|
|
628
|
-
where: toBundleWhere(where)
|
|
629
|
-
})
|
|
630
|
-
},
|
|
631
|
-
bundlePatches: { async findByBundleIds(bundleIds) {
|
|
632
|
-
if (bundleIds.length === 0) return [];
|
|
633
|
-
return await bundleReader.findMany({
|
|
634
|
-
model: "bundle_patches",
|
|
635
|
-
where: [{
|
|
636
|
-
field: "bundle_id",
|
|
637
|
-
operator: "in",
|
|
638
|
-
value: bundleIds
|
|
639
|
-
}],
|
|
640
|
-
orderBy: [{
|
|
641
|
-
field: "id",
|
|
642
|
-
direction: "asc"
|
|
643
|
-
}],
|
|
644
|
-
limit: Number.MAX_SAFE_INTEGER,
|
|
645
|
-
offset: 0
|
|
646
|
-
});
|
|
647
|
-
} },
|
|
648
|
-
releases: {
|
|
649
|
-
findById: (id) => releaseRemote.findReleaseById(id),
|
|
650
|
-
findMany: (input) => releaseRemote.findReleases(input),
|
|
651
|
-
findManyByScope: (input) => releaseRemote.findReleasesByScope(input)
|
|
652
|
-
},
|
|
653
|
-
releaseCatalogs: {
|
|
654
|
-
findByScopeKey: (scopeKey) => releaseRemote.findCatalogByScopeKey(scopeKey),
|
|
655
|
-
findMany: (input) => releaseRemote.findCatalogs(input)
|
|
656
|
-
},
|
|
657
|
-
channels: {
|
|
658
|
-
insert: (input) => remote.insertChannel(input),
|
|
659
|
-
delete: (input) => remote.deleteChannel(input),
|
|
660
|
-
async list() {
|
|
661
|
-
return { channels: await remote.loadChannels() };
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
},
|
|
665
|
-
commit: (input) => releaseRemote.commit(input)
|
|
239
|
+
core: createStandaloneCoreApi(config),
|
|
240
|
+
fetchAdmin: (path) => fetch(http.buildUrl(path), { headers: http.headers({ "Cache-Control": "no-cache" }) })
|
|
666
241
|
});
|
|
667
242
|
};
|
|
668
243
|
//#endregion
|
|
@@ -704,7 +279,7 @@ const standaloneStorage = (config) => {
|
|
|
704
279
|
if (!response.ok && response.status !== 404) throw new Error(`Failed to ${routeName} storage object: ${response.statusText}`);
|
|
705
280
|
return response;
|
|
706
281
|
};
|
|
707
|
-
return
|
|
282
|
+
return createStorageAdapter({
|
|
708
283
|
name: "standaloneStorage",
|
|
709
284
|
protocol: config.protocol,
|
|
710
285
|
async put({ key, body, contentType, contentLength }) {
|
|
@@ -745,4 +320,4 @@ const standaloneStorage = (config) => {
|
|
|
745
320
|
});
|
|
746
321
|
};
|
|
747
322
|
//#endregion
|
|
748
|
-
export { StandaloneDatabaseError, standaloneRepository, standaloneStorage };
|
|
323
|
+
export { STANDALONE_ADMIN_PROTOCOL, StandaloneDatabaseError, createStandaloneCoreApi, standaloneRepository, standaloneStorage };
|