@hot-updater/server 1.2.0-rc.12 → 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.
@@ -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;
@@ -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
- const { rows } = buildKey === void 0 ? await db.findMany("native_builds", {
159
- index: "byScope",
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
- build_key: buildKey
168
- },
169
- order: "desc",
170
- limit: 1
171
- });
172
- return rows[0] === void 0 ? null : pick(rows[0]);
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
- const { rows } = buildKey === void 0 ? await db.findMany("native_builds", {
159
- index: "byScope",
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
- build_key: buildKey
168
- },
169
- order: "desc",
170
- limit: 1
171
- });
172
- return rows[0] === void 0 ? null : pick(rows[0]);
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 aggregate changes the database batches that are still pending:
46
- * this process's buffer, and the log rows no compaction has merged yet.
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 aggregate changes the database batches that are still pending:
46
- * this process's buffer, and the log rows no compaction has merged yet.
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
- return json({ data: await refusingInvalid(() => buildKey === null ? core.findNativeBuild(scope) : core.findNativeBuild(scope, { buildKey })) });
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
- return json({ data: await refusingInvalid(() => buildKey === null ? core.findNativeBuild(scope) : core.findNativeBuild(scope, { buildKey })) });
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
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "1.2.0-rc.12";
2
+ var version = "1.2.0-rc.13";
3
3
  //#endregion
4
4
  Object.defineProperty(exports, "version", {
5
5
  enumerable: true,
package/dist/package.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "1.2.0-rc.12";
2
+ var version = "1.2.0-rc.13";
3
3
  //#endregion
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hot-updater/server",
3
- "version": "1.2.0-rc.12",
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/protocol": "1.2.0-rc.12",
54
- "@hot-updater/plugin-core": "1.2.0-rc.12",
55
- "@hot-updater/plugin-api-keys": "1.2.0-rc.12",
56
- "@hot-updater/plugin-insights": "1.2.0-rc.12",
57
- "@hot-updater/plugin-remote-config": "1.2.0-rc.12"
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": "*",