@hot-updater/server 1.2.0-rc.11 → 1.2.0-rc.13
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/core/api.d.cts +5 -0
- package/dist/core/api.d.mts +5 -0
- package/dist/core/nativeBuilds.cjs +98 -16
- package/dist/core/nativeBuilds.mjs +99 -17
- package/dist/createHotUpdaterCore.d.cts +2 -4
- package/dist/createHotUpdaterCore.d.mts +2 -4
- package/dist/handlerAdminRoutes.cjs +18 -1
- package/dist/handlerAdminRoutes.mjs +18 -1
- package/dist/package.cjs +1 -1
- package/dist/package.mjs +1 -1
- package/package.json +6 -6
package/dist/core/api.d.cts
CHANGED
|
@@ -13,6 +13,8 @@ declare const createCoreApi: (db: CoreDatabase, storage: CoreStorage, options?:
|
|
|
13
13
|
getNativeBuild: (id: string) => Promise<import("@hot-updater/plugin-core").NativeBuildRow | null>;
|
|
14
14
|
findNativeBuild: (scope: import("@hot-updater/plugin-core").NativeBuildScope, options?: {
|
|
15
15
|
readonly buildKey?: string;
|
|
16
|
+
readonly storeBuild?: true;
|
|
17
|
+
readonly builtInOlderThan?: string;
|
|
16
18
|
}) => Promise<import("@hot-updater/plugin-core").NativeBuildRow | null>;
|
|
17
19
|
listNativeBuilds: (input: import("@hot-updater/plugin-core").KeysetInput & {
|
|
18
20
|
readonly channelId?: string;
|
|
@@ -22,6 +24,9 @@ declare const createCoreApi: (db: CoreDatabase, storage: CoreStorage, options?:
|
|
|
22
24
|
readonly destination: import("@hot-updater/plugin-core").NativeBuildSubmissionDestination;
|
|
23
25
|
readonly version: string;
|
|
24
26
|
}) => Promise<import("@hot-updater/plugin-core").NativeBuildRow | null>;
|
|
27
|
+
markNativeBuild: (id: string, mark: {
|
|
28
|
+
readonly storeBuild: boolean;
|
|
29
|
+
}) => Promise<import("@hot-updater/plugin-core").NativeBuildMarkResult | null>;
|
|
25
30
|
deploy: (deployments: readonly import("@hot-updater/plugin-core").Deployment[]) => Promise<import("@hot-updater/plugin-core").ReleaseCatalogMutationResult[]>;
|
|
26
31
|
updateReleasePolicy: (input: import("@hot-updater/plugin-core").ReleaseTarget & {
|
|
27
32
|
readonly patch: import("@hot-updater/plugin-core").ReleasePolicyPatch;
|
package/dist/core/api.d.mts
CHANGED
|
@@ -13,6 +13,8 @@ declare const createCoreApi: (db: CoreDatabase, storage: CoreStorage, options?:
|
|
|
13
13
|
getNativeBuild: (id: string) => Promise<import("@hot-updater/plugin-core").NativeBuildRow | null>;
|
|
14
14
|
findNativeBuild: (scope: import("@hot-updater/plugin-core").NativeBuildScope, options?: {
|
|
15
15
|
readonly buildKey?: string;
|
|
16
|
+
readonly storeBuild?: true;
|
|
17
|
+
readonly builtInOlderThan?: string;
|
|
16
18
|
}) => Promise<import("@hot-updater/plugin-core").NativeBuildRow | null>;
|
|
17
19
|
listNativeBuilds: (input: import("@hot-updater/plugin-core").KeysetInput & {
|
|
18
20
|
readonly channelId?: string;
|
|
@@ -22,6 +24,9 @@ declare const createCoreApi: (db: CoreDatabase, storage: CoreStorage, options?:
|
|
|
22
24
|
readonly destination: import("@hot-updater/plugin-core").NativeBuildSubmissionDestination;
|
|
23
25
|
readonly version: string;
|
|
24
26
|
}) => Promise<import("@hot-updater/plugin-core").NativeBuildRow | null>;
|
|
27
|
+
markNativeBuild: (id: string, mark: {
|
|
28
|
+
readonly storeBuild: boolean;
|
|
29
|
+
}) => Promise<import("@hot-updater/plugin-core").NativeBuildMarkResult | null>;
|
|
25
30
|
deploy: (deployments: readonly import("@hot-updater/plugin-core").Deployment[]) => Promise<import("@hot-updater/plugin-core").ReleaseCatalogMutationResult[]>;
|
|
26
31
|
updateReleasePolicy: (input: import("@hot-updater/plugin-core").ReleaseTarget & {
|
|
27
32
|
readonly patch: import("@hot-updater/plugin-core").ReleasePolicyPatch;
|
|
@@ -39,8 +39,22 @@ const pick = (row) => ({
|
|
|
39
39
|
build_number: row.build_number ?? null,
|
|
40
40
|
submitted_to: row.submitted_to ?? null,
|
|
41
41
|
submitted_at_ms: row.submitted_at_ms ?? null,
|
|
42
|
-
submitted_version: row.submitted_version ?? null
|
|
42
|
+
submitted_version: row.submitted_version ?? null,
|
|
43
|
+
store_build: row.store_build === true ? true : null
|
|
43
44
|
});
|
|
45
|
+
/**
|
|
46
|
+
* The channels only sandbox builds register on, which no store sells: the
|
|
47
|
+
* registry's convention, as `@hot-updater/protocol/native-artifact` checks it
|
|
48
|
+
* for a build's artifact.
|
|
49
|
+
*/
|
|
50
|
+
const SANDBOX_CHANNEL_PREFIX = "sandbox-";
|
|
51
|
+
/**
|
|
52
|
+
* How many older builds of a scope one read takes, looking for one older than
|
|
53
|
+
* a bundle when a build made after the bundle is the newest.
|
|
54
|
+
*/
|
|
55
|
+
const OLDER_BUILDS_PAGE = 20;
|
|
56
|
+
/** How many times a mark runs again after losing its scope's mark to another. */
|
|
57
|
+
const MARK_ATTEMPTS = 8;
|
|
44
58
|
/** The store track each platform's builds are submitted to. */
|
|
45
59
|
const SUBMISSION_DESTINATIONS = {
|
|
46
60
|
android: "play-internal",
|
|
@@ -117,7 +131,8 @@ const createNativeBuildApi = (db, { now = Date.now } = {}) => ({
|
|
|
117
131
|
build_number: registration.buildNumber ?? null,
|
|
118
132
|
submitted_to: null,
|
|
119
133
|
submitted_at_ms: null,
|
|
120
|
-
submitted_version: null
|
|
134
|
+
submitted_version: null,
|
|
135
|
+
store_build: null
|
|
121
136
|
};
|
|
122
137
|
for (let attempt = 1;; attempt += 1) {
|
|
123
138
|
await require_writes.insertChannel(db, {
|
|
@@ -155,21 +170,45 @@ const createNativeBuildApi = (db, { now = Date.now } = {}) => ({
|
|
|
155
170
|
const scopeKey = nativeBuildScopeKey(scope);
|
|
156
171
|
const buildKey = options?.buildKey;
|
|
157
172
|
if (buildKey !== void 0 && !BUILD_KEY.test(buildKey)) throw new NativeBuildInputError("Invalid native build key: it must be nbk1: and a SHA-256.");
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
where: { scope_key: scopeKey },
|
|
161
|
-
order: "desc",
|
|
162
|
-
limit: 1
|
|
163
|
-
}) : await db.findMany("native_builds", {
|
|
164
|
-
index: "byBuildKey",
|
|
165
|
-
where: {
|
|
173
|
+
if (options?.storeBuild) {
|
|
174
|
+
const marked = await db.findOne("native_builds", {
|
|
166
175
|
scope_key: scopeKey,
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
176
|
+
store_build: true
|
|
177
|
+
});
|
|
178
|
+
return marked === null ? null : pick(marked);
|
|
179
|
+
}
|
|
180
|
+
const read = (limit, before) => {
|
|
181
|
+
const page = {
|
|
182
|
+
order: "desc",
|
|
183
|
+
limit,
|
|
184
|
+
...before === void 0 ? {} : { range: { lt: before } }
|
|
185
|
+
};
|
|
186
|
+
return buildKey === void 0 ? db.findMany("native_builds", {
|
|
187
|
+
index: "byScope",
|
|
188
|
+
where: { scope_key: scopeKey },
|
|
189
|
+
...page
|
|
190
|
+
}) : db.findMany("native_builds", {
|
|
191
|
+
index: "byBuildKey",
|
|
192
|
+
where: {
|
|
193
|
+
scope_key: scopeKey,
|
|
194
|
+
build_key: buildKey
|
|
195
|
+
},
|
|
196
|
+
...page
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
const { rows } = await read(1);
|
|
200
|
+
const newest = rows[0];
|
|
201
|
+
if (newest === void 0) return null;
|
|
202
|
+
const bundleId = options?.builtInOlderThan;
|
|
203
|
+
if (bundleId === void 0 || newest.min_bundle_id < bundleId) return pick(newest);
|
|
204
|
+
for (let before = newest.id;;) {
|
|
205
|
+
const { rows: older } = await read(OLDER_BUILDS_PAGE, before);
|
|
206
|
+
const usable = older.find(({ min_bundle_id }) => min_bundle_id < bundleId);
|
|
207
|
+
if (usable !== void 0) return pick(usable);
|
|
208
|
+
const last = older.at(-1);
|
|
209
|
+
if (older.length < OLDER_BUILDS_PAGE || last === void 0) return null;
|
|
210
|
+
before = last.id;
|
|
211
|
+
}
|
|
173
212
|
},
|
|
174
213
|
async recordNativeBuildSubmission(id, { destination, version }) {
|
|
175
214
|
if (version.trim().length === 0 || version.length > SUBMITTED_VERSION_MAX_LENGTH) throw new NativeBuildInputError(`Invalid native build submission: version must be the store's label of the build, up to ${SUBMITTED_VERSION_MAX_LENGTH} characters.`);
|
|
@@ -190,6 +229,49 @@ const createNativeBuildApi = (db, { now = Date.now } = {}) => ({
|
|
|
190
229
|
};
|
|
191
230
|
});
|
|
192
231
|
},
|
|
232
|
+
async markNativeBuild(id, { storeBuild }) {
|
|
233
|
+
for (let attempt = 1;; attempt += 1) try {
|
|
234
|
+
return await db.transaction(async (tx) => {
|
|
235
|
+
const row = await tx.findOne("native_builds", { id });
|
|
236
|
+
if (row === null) return null;
|
|
237
|
+
const build = pick(row);
|
|
238
|
+
if (!storeBuild) {
|
|
239
|
+
if (build.store_build !== null) tx.update("native_builds", row, { store_build: null });
|
|
240
|
+
return {
|
|
241
|
+
nativeBuild: {
|
|
242
|
+
...build,
|
|
243
|
+
store_build: null
|
|
244
|
+
},
|
|
245
|
+
previous: null
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if ((0, _hot_updater_protocol.decodeChannelKey)(build.channel_id.slice(8)).startsWith(SANDBOX_CHANNEL_PREFIX)) throw new NativeBuildInputError("A build on a sandbox channel can't be the store build.");
|
|
249
|
+
if (build.store_build !== null) return {
|
|
250
|
+
nativeBuild: build,
|
|
251
|
+
previous: null
|
|
252
|
+
};
|
|
253
|
+
const holder = await tx.findOne("native_builds", {
|
|
254
|
+
scope_key: build.scope_key,
|
|
255
|
+
store_build: true
|
|
256
|
+
});
|
|
257
|
+
if (holder !== null) tx.update("native_builds", holder, { store_build: null });
|
|
258
|
+
tx.update("native_builds", row, { store_build: true });
|
|
259
|
+
return {
|
|
260
|
+
nativeBuild: {
|
|
261
|
+
...build,
|
|
262
|
+
store_build: true
|
|
263
|
+
},
|
|
264
|
+
previous: holder === null ? null : {
|
|
265
|
+
...pick(holder),
|
|
266
|
+
store_build: null
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
} catch (error) {
|
|
271
|
+
if (attempt < MARK_ATTEMPTS && error instanceof _hot_updater_plugin_core.DatabaseConstraintError && error.reason === "unique") continue;
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
},
|
|
193
275
|
async deleteNativeBuild(id) {
|
|
194
276
|
return db.transaction(async (tx) => {
|
|
195
277
|
const row = await tx.findOne("native_builds", { id });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { after } from "./reads.mjs";
|
|
2
2
|
import { insertChannel } from "./writes.mjs";
|
|
3
3
|
import { DatabaseConstraintError, isUUIDv7 } from "@hot-updater/plugin-core";
|
|
4
|
-
import { createReleaseCatalogScopeKey, encodeChannelKey } from "@hot-updater/protocol";
|
|
4
|
+
import { createReleaseCatalogScopeKey, decodeChannelKey, encodeChannelKey } from "@hot-updater/protocol";
|
|
5
5
|
//#region src/core/nativeBuilds.ts
|
|
6
6
|
/** The artifacts each platform's build produces. */
|
|
7
7
|
const ARTIFACT_KINDS = {
|
|
@@ -39,8 +39,22 @@ const pick = (row) => ({
|
|
|
39
39
|
build_number: row.build_number ?? null,
|
|
40
40
|
submitted_to: row.submitted_to ?? null,
|
|
41
41
|
submitted_at_ms: row.submitted_at_ms ?? null,
|
|
42
|
-
submitted_version: row.submitted_version ?? null
|
|
42
|
+
submitted_version: row.submitted_version ?? null,
|
|
43
|
+
store_build: row.store_build === true ? true : null
|
|
43
44
|
});
|
|
45
|
+
/**
|
|
46
|
+
* The channels only sandbox builds register on, which no store sells: the
|
|
47
|
+
* registry's convention, as `@hot-updater/protocol/native-artifact` checks it
|
|
48
|
+
* for a build's artifact.
|
|
49
|
+
*/
|
|
50
|
+
const SANDBOX_CHANNEL_PREFIX = "sandbox-";
|
|
51
|
+
/**
|
|
52
|
+
* How many older builds of a scope one read takes, looking for one older than
|
|
53
|
+
* a bundle when a build made after the bundle is the newest.
|
|
54
|
+
*/
|
|
55
|
+
const OLDER_BUILDS_PAGE = 20;
|
|
56
|
+
/** How many times a mark runs again after losing its scope's mark to another. */
|
|
57
|
+
const MARK_ATTEMPTS = 8;
|
|
44
58
|
/** The store track each platform's builds are submitted to. */
|
|
45
59
|
const SUBMISSION_DESTINATIONS = {
|
|
46
60
|
android: "play-internal",
|
|
@@ -117,7 +131,8 @@ const createNativeBuildApi = (db, { now = Date.now } = {}) => ({
|
|
|
117
131
|
build_number: registration.buildNumber ?? null,
|
|
118
132
|
submitted_to: null,
|
|
119
133
|
submitted_at_ms: null,
|
|
120
|
-
submitted_version: null
|
|
134
|
+
submitted_version: null,
|
|
135
|
+
store_build: null
|
|
121
136
|
};
|
|
122
137
|
for (let attempt = 1;; attempt += 1) {
|
|
123
138
|
await insertChannel(db, {
|
|
@@ -155,21 +170,45 @@ const createNativeBuildApi = (db, { now = Date.now } = {}) => ({
|
|
|
155
170
|
const scopeKey = nativeBuildScopeKey(scope);
|
|
156
171
|
const buildKey = options?.buildKey;
|
|
157
172
|
if (buildKey !== void 0 && !BUILD_KEY.test(buildKey)) throw new NativeBuildInputError("Invalid native build key: it must be nbk1: and a SHA-256.");
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
where: { scope_key: scopeKey },
|
|
161
|
-
order: "desc",
|
|
162
|
-
limit: 1
|
|
163
|
-
}) : await db.findMany("native_builds", {
|
|
164
|
-
index: "byBuildKey",
|
|
165
|
-
where: {
|
|
173
|
+
if (options?.storeBuild) {
|
|
174
|
+
const marked = await db.findOne("native_builds", {
|
|
166
175
|
scope_key: scopeKey,
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
176
|
+
store_build: true
|
|
177
|
+
});
|
|
178
|
+
return marked === null ? null : pick(marked);
|
|
179
|
+
}
|
|
180
|
+
const read = (limit, before) => {
|
|
181
|
+
const page = {
|
|
182
|
+
order: "desc",
|
|
183
|
+
limit,
|
|
184
|
+
...before === void 0 ? {} : { range: { lt: before } }
|
|
185
|
+
};
|
|
186
|
+
return buildKey === void 0 ? db.findMany("native_builds", {
|
|
187
|
+
index: "byScope",
|
|
188
|
+
where: { scope_key: scopeKey },
|
|
189
|
+
...page
|
|
190
|
+
}) : db.findMany("native_builds", {
|
|
191
|
+
index: "byBuildKey",
|
|
192
|
+
where: {
|
|
193
|
+
scope_key: scopeKey,
|
|
194
|
+
build_key: buildKey
|
|
195
|
+
},
|
|
196
|
+
...page
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
const { rows } = await read(1);
|
|
200
|
+
const newest = rows[0];
|
|
201
|
+
if (newest === void 0) return null;
|
|
202
|
+
const bundleId = options?.builtInOlderThan;
|
|
203
|
+
if (bundleId === void 0 || newest.min_bundle_id < bundleId) return pick(newest);
|
|
204
|
+
for (let before = newest.id;;) {
|
|
205
|
+
const { rows: older } = await read(OLDER_BUILDS_PAGE, before);
|
|
206
|
+
const usable = older.find(({ min_bundle_id }) => min_bundle_id < bundleId);
|
|
207
|
+
if (usable !== void 0) return pick(usable);
|
|
208
|
+
const last = older.at(-1);
|
|
209
|
+
if (older.length < OLDER_BUILDS_PAGE || last === void 0) return null;
|
|
210
|
+
before = last.id;
|
|
211
|
+
}
|
|
173
212
|
},
|
|
174
213
|
async recordNativeBuildSubmission(id, { destination, version }) {
|
|
175
214
|
if (version.trim().length === 0 || version.length > SUBMITTED_VERSION_MAX_LENGTH) throw new NativeBuildInputError(`Invalid native build submission: version must be the store's label of the build, up to ${SUBMITTED_VERSION_MAX_LENGTH} characters.`);
|
|
@@ -190,6 +229,49 @@ const createNativeBuildApi = (db, { now = Date.now } = {}) => ({
|
|
|
190
229
|
};
|
|
191
230
|
});
|
|
192
231
|
},
|
|
232
|
+
async markNativeBuild(id, { storeBuild }) {
|
|
233
|
+
for (let attempt = 1;; attempt += 1) try {
|
|
234
|
+
return await db.transaction(async (tx) => {
|
|
235
|
+
const row = await tx.findOne("native_builds", { id });
|
|
236
|
+
if (row === null) return null;
|
|
237
|
+
const build = pick(row);
|
|
238
|
+
if (!storeBuild) {
|
|
239
|
+
if (build.store_build !== null) tx.update("native_builds", row, { store_build: null });
|
|
240
|
+
return {
|
|
241
|
+
nativeBuild: {
|
|
242
|
+
...build,
|
|
243
|
+
store_build: null
|
|
244
|
+
},
|
|
245
|
+
previous: null
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (decodeChannelKey(build.channel_id.slice(8)).startsWith(SANDBOX_CHANNEL_PREFIX)) throw new NativeBuildInputError("A build on a sandbox channel can't be the store build.");
|
|
249
|
+
if (build.store_build !== null) return {
|
|
250
|
+
nativeBuild: build,
|
|
251
|
+
previous: null
|
|
252
|
+
};
|
|
253
|
+
const holder = await tx.findOne("native_builds", {
|
|
254
|
+
scope_key: build.scope_key,
|
|
255
|
+
store_build: true
|
|
256
|
+
});
|
|
257
|
+
if (holder !== null) tx.update("native_builds", holder, { store_build: null });
|
|
258
|
+
tx.update("native_builds", row, { store_build: true });
|
|
259
|
+
return {
|
|
260
|
+
nativeBuild: {
|
|
261
|
+
...build,
|
|
262
|
+
store_build: true
|
|
263
|
+
},
|
|
264
|
+
previous: holder === null ? null : {
|
|
265
|
+
...pick(holder),
|
|
266
|
+
store_build: null
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
} catch (error) {
|
|
271
|
+
if (attempt < MARK_ATTEMPTS && error instanceof DatabaseConstraintError && error.reason === "unique") continue;
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
},
|
|
193
275
|
async deleteNativeBuild(id) {
|
|
194
276
|
return db.transaction(async (tx) => {
|
|
195
277
|
const row = await tx.findOne("native_builds", { id });
|
|
@@ -42,10 +42,8 @@ type RuntimeHotUpdaterAPI<TPlugins extends readonly AnyHotUpdaterPlugin[] = read
|
|
|
42
42
|
/** The database's name, which `hot-updater db` commands read. */
|
|
43
43
|
readonly adapterName: string;
|
|
44
44
|
/**
|
|
45
|
-
* Applies
|
|
46
|
-
*
|
|
47
|
-
* A long-lived server with `aggregateBatching: { mode: "memory" }` calls
|
|
48
|
-
* it before it exits.
|
|
45
|
+
* Applies the log rows of batched aggregates that no compaction has merged
|
|
46
|
+
* yet. Nothing waits on it: they are durable, and a read compacts first.
|
|
49
47
|
*/
|
|
50
48
|
readonly flush: () => Promise<void>;
|
|
51
49
|
/**
|
|
@@ -42,10 +42,8 @@ type RuntimeHotUpdaterAPI<TPlugins extends readonly AnyHotUpdaterPlugin[] = read
|
|
|
42
42
|
/** The database's name, which `hot-updater db` commands read. */
|
|
43
43
|
readonly adapterName: string;
|
|
44
44
|
/**
|
|
45
|
-
* Applies
|
|
46
|
-
*
|
|
47
|
-
* A long-lived server with `aggregateBatching: { mode: "memory" }` calls
|
|
48
|
-
* it before it exits.
|
|
45
|
+
* Applies the log rows of batched aggregates that no compaction has merged
|
|
46
|
+
* yet. Nothing waits on it: they are durable, and a read compacts first.
|
|
49
47
|
*/
|
|
50
48
|
readonly flush: () => Promise<void>;
|
|
51
49
|
/**
|
|
@@ -298,7 +298,13 @@ const createAdminRouteHandlers = () => ({
|
|
|
298
298
|
const url = new URL(request.url);
|
|
299
299
|
const scope = nativeBuildScopeOf(url);
|
|
300
300
|
const buildKey = url.searchParams.get("buildKey");
|
|
301
|
-
|
|
301
|
+
const builtInOlderThan = url.searchParams.get("builtInOlderThan");
|
|
302
|
+
const options = {
|
|
303
|
+
...buildKey === null ? {} : { buildKey },
|
|
304
|
+
...url.searchParams.get("storeBuild") === "true" ? { storeBuild: true } : {},
|
|
305
|
+
...builtInOlderThan === null ? {} : { builtInOlderThan }
|
|
306
|
+
};
|
|
307
|
+
return json({ data: await refusingInvalid(() => Object.keys(options).length === 0 ? core.findNativeBuild(scope) : core.findNativeBuild(scope, options)) });
|
|
302
308
|
}),
|
|
303
309
|
getNativeBuild: onCore(async (core, _request, params) => json({ data: await core.getNativeBuild(require_handlerParameters.requireRouteParam(params, "id")) })),
|
|
304
310
|
listNativeBuilds: onCore(async (core, request) => {
|
|
@@ -319,6 +325,12 @@ const createAdminRouteHandlers = () => ({
|
|
|
319
325
|
};
|
|
320
326
|
return json({ data: await refusingInvalid(() => core.recordNativeBuildSubmission(require_handlerParameters.requireRouteParam(params, "id"), submission)) });
|
|
321
327
|
}),
|
|
328
|
+
markNativeBuild: onCore(async (core, request, params) => {
|
|
329
|
+
const input = await body(request);
|
|
330
|
+
if (!isRecord(input) || typeof input.storeBuild !== "boolean") throw new require_handlerErrors.HandlerBadRequestError("A native build's store build mark needs storeBuild, true or false.");
|
|
331
|
+
const mark = { storeBuild: input.storeBuild };
|
|
332
|
+
return json({ data: await refusingInvalid(() => core.markNativeBuild(require_handlerParameters.requireRouteParam(params, "id"), mark)) });
|
|
333
|
+
}),
|
|
322
334
|
deleteNativeBuild: onCore(async (core, _request, params) => json({ data: await core.deleteNativeBuild(require_handlerParameters.requireRouteParam(params, "id")) })),
|
|
323
335
|
deleteBundles: onCore(async (core, request) => {
|
|
324
336
|
const { ids } = await body(request);
|
|
@@ -464,6 +476,11 @@ const ADMIN_ROUTES = [
|
|
|
464
476
|
path: "/native-builds/:id/submission",
|
|
465
477
|
handler: "recordNativeBuildSubmission"
|
|
466
478
|
},
|
|
479
|
+
{
|
|
480
|
+
method: "POST",
|
|
481
|
+
path: "/native-builds/:id/store-build",
|
|
482
|
+
handler: "markNativeBuild"
|
|
483
|
+
},
|
|
467
484
|
{
|
|
468
485
|
method: "DELETE",
|
|
469
486
|
path: "/native-builds/:id",
|
|
@@ -298,7 +298,13 @@ const createAdminRouteHandlers = () => ({
|
|
|
298
298
|
const url = new URL(request.url);
|
|
299
299
|
const scope = nativeBuildScopeOf(url);
|
|
300
300
|
const buildKey = url.searchParams.get("buildKey");
|
|
301
|
-
|
|
301
|
+
const builtInOlderThan = url.searchParams.get("builtInOlderThan");
|
|
302
|
+
const options = {
|
|
303
|
+
...buildKey === null ? {} : { buildKey },
|
|
304
|
+
...url.searchParams.get("storeBuild") === "true" ? { storeBuild: true } : {},
|
|
305
|
+
...builtInOlderThan === null ? {} : { builtInOlderThan }
|
|
306
|
+
};
|
|
307
|
+
return json({ data: await refusingInvalid(() => Object.keys(options).length === 0 ? core.findNativeBuild(scope) : core.findNativeBuild(scope, options)) });
|
|
302
308
|
}),
|
|
303
309
|
getNativeBuild: onCore(async (core, _request, params) => json({ data: await core.getNativeBuild(requireRouteParam(params, "id")) })),
|
|
304
310
|
listNativeBuilds: onCore(async (core, request) => {
|
|
@@ -319,6 +325,12 @@ const createAdminRouteHandlers = () => ({
|
|
|
319
325
|
};
|
|
320
326
|
return json({ data: await refusingInvalid(() => core.recordNativeBuildSubmission(requireRouteParam(params, "id"), submission)) });
|
|
321
327
|
}),
|
|
328
|
+
markNativeBuild: onCore(async (core, request, params) => {
|
|
329
|
+
const input = await body(request);
|
|
330
|
+
if (!isRecord(input) || typeof input.storeBuild !== "boolean") throw new HandlerBadRequestError("A native build's store build mark needs storeBuild, true or false.");
|
|
331
|
+
const mark = { storeBuild: input.storeBuild };
|
|
332
|
+
return json({ data: await refusingInvalid(() => core.markNativeBuild(requireRouteParam(params, "id"), mark)) });
|
|
333
|
+
}),
|
|
322
334
|
deleteNativeBuild: onCore(async (core, _request, params) => json({ data: await core.deleteNativeBuild(requireRouteParam(params, "id")) })),
|
|
323
335
|
deleteBundles: onCore(async (core, request) => {
|
|
324
336
|
const { ids } = await body(request);
|
|
@@ -464,6 +476,11 @@ const ADMIN_ROUTES = [
|
|
|
464
476
|
path: "/native-builds/:id/submission",
|
|
465
477
|
handler: "recordNativeBuildSubmission"
|
|
466
478
|
},
|
|
479
|
+
{
|
|
480
|
+
method: "POST",
|
|
481
|
+
path: "/native-builds/:id/store-build",
|
|
482
|
+
handler: "markNativeBuild"
|
|
483
|
+
},
|
|
467
484
|
{
|
|
468
485
|
method: "DELETE",
|
|
469
486
|
path: "/native-builds/:id",
|
package/dist/package.cjs
CHANGED
package/dist/package.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/server",
|
|
3
|
-
"version": "1.2.0-rc.
|
|
3
|
+
"version": "1.2.0-rc.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -50,11 +50,11 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"rou3": "0.7.9",
|
|
52
52
|
"verkit": "0.4.0",
|
|
53
|
-
"@hot-updater/
|
|
54
|
-
"@hot-updater/plugin-insights": "1.2.0-rc.
|
|
55
|
-
"@hot-updater/plugin-
|
|
56
|
-
"@hot-updater/
|
|
57
|
-
"@hot-updater/plugin-
|
|
53
|
+
"@hot-updater/protocol": "1.2.0-rc.13",
|
|
54
|
+
"@hot-updater/plugin-insights": "1.2.0-rc.13",
|
|
55
|
+
"@hot-updater/plugin-core": "1.2.0-rc.13",
|
|
56
|
+
"@hot-updater/plugin-remote-config": "1.2.0-rc.13",
|
|
57
|
+
"@hot-updater/plugin-api-keys": "1.2.0-rc.13"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"@prisma/client": "*",
|