@hot-updater/postgres 1.0.0-rc.1 → 1.0.0-rc.16

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 CHANGED
@@ -16,283 +16,33 @@ var __copyProps = (to, from, except, desc) => {
16
16
  }
17
17
  return to;
18
18
  };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
20
20
  value: mod,
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
- let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
25
- let _hot_updater_plugin_core_internal = require("@hot-updater/plugin-core/internal");
24
+ let _hot_updater_server_adapters_kysely = require("@hot-updater/server/adapters/kysely");
25
+ let _hot_updater_server_database = require("@hot-updater/server/database");
26
26
  let kysely = require("kysely");
27
27
  let pg = require("pg");
28
- pg = __toESM(pg);
29
- //#region src/postgresQuery.ts
30
- const applyOrder = (order, clause) => {
31
- const directed = clause.direction === "asc" ? order.asc() : order.desc();
32
- return clause.nulls === void 0 ? directed : clause.nulls === "first" ? directed.nullsFirst() : directed.nullsLast();
33
- };
34
- const countPostgresRows = async (db, input, where) => {
35
- switch (input.model) {
36
- case "bundles": {
37
- let rows = db.selectFrom("bundles");
38
- if (where !== void 0) rows = rows.where(where);
39
- const query = input.distinct === void 0 ? rows.select(({ fn }) => fn.countAll().as("count")) : db.selectFrom(rows.select(input.distinct).distinct().as("distinct_rows")).select(({ fn }) => fn.countAll().as("count"));
40
- return Number((await query.executeTakeFirstOrThrow()).count);
41
- }
42
- case "bundle_patches": {
43
- let rows = db.selectFrom("bundle_patches");
44
- if (where !== void 0) rows = rows.where(where);
45
- const query = input.distinct === void 0 ? rows.select(({ fn }) => fn.countAll().as("count")) : db.selectFrom(rows.select(input.distinct).distinct().as("distinct_rows")).select(({ fn }) => fn.countAll().as("count"));
46
- return Number((await query.executeTakeFirstOrThrow()).count);
47
- }
48
- case "releases": {
49
- let rows = db.selectFrom("releases");
50
- if (where !== void 0) rows = rows.where(where);
51
- const query = rows.select(({ fn }) => fn.countAll().as("count"));
52
- return Number((await query.executeTakeFirstOrThrow()).count);
53
- }
54
- }
55
- };
56
- const findManyPostgresRows = async (db, input, where) => {
57
- switch (input.model) {
58
- case "bundles": {
59
- let query = db.selectFrom("bundles").selectAll();
60
- if (where !== void 0) query = query.where(where);
61
- if (input.distinctOn !== void 0) query = query.distinctOn(input.distinctOn.fields);
62
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
63
- return query.limit(input.limit).offset(input.offset).execute();
64
- }
65
- case "release_catalogs": {
66
- let query = db.selectFrom("release_catalogs").selectAll();
67
- if (where !== void 0) query = query.where(where);
68
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
69
- return query.limit(input.limit).offset(input.offset).execute();
70
- }
71
- case "bundle_events": {
72
- let query = db.selectFrom("bundle_events").selectAll();
73
- if (where !== void 0) query = query.where(where);
74
- if (input.distinctOn !== void 0) query = query.distinctOn(input.distinctOn.fields);
75
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
76
- return query.limit(input.limit).offset(input.offset).execute();
77
- }
78
- case "channels": {
79
- let query = db.selectFrom("channels").selectAll();
80
- if (where !== void 0) query = query.where(where);
81
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
82
- return query.limit(input.limit).offset(input.offset).execute();
83
- }
84
- case "api_keys": {
85
- let query = db.selectFrom("api_keys").selectAll();
86
- if (where !== void 0) query = query.where(where);
87
- if (input.distinctOn !== void 0) query = query.distinctOn(input.distinctOn.fields);
88
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
89
- return query.limit(input.limit).offset(input.offset).execute();
90
- }
91
- case "bundle_patches": {
92
- let query = db.selectFrom("bundle_patches").selectAll();
93
- if (where !== void 0) query = query.where(where);
94
- if (input.distinctOn !== void 0) query = query.distinctOn(input.distinctOn.fields);
95
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
96
- return query.limit(input.limit).offset(input.offset).execute();
97
- }
98
- case "releases": {
99
- let query = db.selectFrom("releases").selectAll();
100
- if (where !== void 0) query = query.where(where);
101
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
102
- return query.limit(input.limit).offset(input.offset).execute();
103
- }
104
- }
105
- };
106
- //#endregion
28
+ pg = __toESM(pg, 1);
107
29
  //#region src/postgres.ts
108
30
  const { Pool } = pg.default;
109
- var InvalidPostgresPredicateError = class extends Error {
110
- name = "InvalidPostgresPredicateError";
111
- };
112
- const isForeignKeyViolation = (error) => typeof error === "object" && error !== null && Reflect.get(error, "code") === "23503";
113
- const escapeLikePattern = (value) => value.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
114
- const stringPredicate = (condition, operator) => {
115
- if (typeof condition.value !== "string") throw new InvalidPostgresPredicateError();
116
- const column = kysely.sql.ref(condition.field);
117
- const literal = escapeLikePattern(condition.value);
118
- const pattern = operator === "contains" ? `%${literal}%` : operator === "starts_with" ? `${literal}%` : `%${literal}`;
119
- return "mode" in condition && condition.mode === "insensitive" ? kysely.sql`lower(${column}) like lower(${pattern}) escape '\\'` : kysely.sql`${column} like ${pattern} escape '\\'`;
120
- };
121
- const predicate = (condition) => {
122
- const column = kysely.sql.ref(condition.field);
123
- const operator = condition.operator ?? "eq";
124
- switch (operator) {
125
- case "eq":
126
- case "ne":
127
- if (condition.value === null) return operator === "eq" ? kysely.sql`${column} is null` : kysely.sql`${column} is not null`;
128
- if ("mode" in condition && condition.mode === "insensitive") return operator === "eq" ? kysely.sql`lower(${column}) = lower(${condition.value})` : kysely.sql`lower(${column}) <> lower(${condition.value})`;
129
- return operator === "eq" ? kysely.sql`${column} = ${condition.value}` : kysely.sql`${column} <> ${condition.value}`;
130
- case "gt": return kysely.sql`${column} > ${condition.value}`;
131
- case "gte": return kysely.sql`${column} >= ${condition.value}`;
132
- case "lt": return kysely.sql`${column} < ${condition.value}`;
133
- case "lte": return kysely.sql`${column} <= ${condition.value}`;
134
- case "in":
135
- case "not_in":
136
- if (!Array.isArray(condition.value)) throw new InvalidPostgresPredicateError();
137
- if (condition.value.length === 0) return kysely.sql`${operator === "not_in"}`;
138
- return operator === "in" ? kysely.sql`${column} in (${kysely.sql.join(condition.value)})` : kysely.sql`${column} not in (${kysely.sql.join(condition.value)})`;
139
- case "contains":
140
- case "starts_with":
141
- case "ends_with": return stringPredicate(condition, operator);
142
- }
143
- };
144
- const buildWhere = (where) => {
145
- const [first, ...rest] = where ?? [];
146
- if (first === void 0) return;
147
- let expression = predicate(first);
148
- for (const condition of rest) {
149
- const next = predicate(condition);
150
- expression = condition.connector === "OR" ? kysely.sql`(${expression} or ${next})` : kysely.sql`(${expression} and ${next})`;
151
- }
152
- return expression;
153
- };
154
- const createPostgresImplementation = (db) => ({
155
- async create(input) {
156
- switch (input.model) {
157
- case "bundles": return db.insertInto("bundles").values(input.data).returningAll().executeTakeFirstOrThrow();
158
- case "bundle_patches": return db.insertInto("bundle_patches").values(input.data).returningAll().executeTakeFirstOrThrow();
159
- case "bundle_events": return db.insertInto("bundle_events").values(input.data).returningAll().executeTakeFirstOrThrow();
160
- case "releases": return db.insertInto("releases").values(input.data).returningAll().executeTakeFirstOrThrow();
161
- case "release_catalogs": return db.insertInto("release_catalogs").values(input.data).returningAll().executeTakeFirstOrThrow();
162
- case "channels": {
163
- const query = db.insertInto("channels").values(input.data);
164
- return await (input.onConflict === "ignore" ? query.onConflict((conflict) => conflict.column("name").doNothing()) : query).returningAll().executeTakeFirst() ?? await db.selectFrom("channels").selectAll().where("name", "=", input.data.name).executeTakeFirstOrThrow();
165
- }
166
- case "api_keys": return await (input.onConflict === "ignore" ? db.insertInto("api_keys").values(input.data).onConflict((conflict) => conflict.column("hash").doNothing()) : db.insertInto("api_keys").values(input.data)).returningAll().executeTakeFirst() ?? await db.selectFrom("api_keys").selectAll().where("hash", "=", input.data.hash).executeTakeFirstOrThrow();
167
- }
168
- },
169
- async update(input) {
170
- const where = buildWhere(input.where);
171
- if (input.model === "api_keys") {
172
- let query = db.updateTable("api_keys").set(input.update);
173
- if (where !== void 0) query = query.where(where);
174
- return await query.returningAll().executeTakeFirst() ?? null;
175
- }
176
- if (input.model === "releases") {
177
- let query = db.updateTable("releases").set(input.update);
178
- if (where !== void 0) query = query.where(where);
179
- return await query.returningAll().executeTakeFirst() ?? null;
180
- }
181
- if (input.model === "release_catalogs") {
182
- let query = db.updateTable("release_catalogs").set(input.update);
183
- if (where !== void 0) query = query.where(where);
184
- return await query.returningAll().executeTakeFirst() ?? null;
185
- }
186
- let query = db.updateTable("bundles").set(input.update);
187
- if (where !== void 0) query = query.where(where);
188
- return await query.returningAll().executeTakeFirst() ?? null;
189
- },
190
- async delete(input) {
191
- const where = buildWhere(input.where);
192
- switch (input.model) {
193
- case "bundles": {
194
- let query = db.deleteFrom("bundles");
195
- if (where !== void 0) query = query.where(where);
196
- await query.execute();
197
- return;
198
- }
199
- case "bundle_patches": {
200
- let query = db.deleteFrom("bundle_patches");
201
- if (where !== void 0) query = query.where(where);
202
- await query.execute();
203
- return;
204
- }
205
- case "channels": {
206
- let query = db.deleteFrom("channels");
207
- if (where !== void 0) query = query.where(where);
208
- try {
209
- await query.execute();
210
- } catch (error) {
211
- if (isForeignKeyViolation(error)) throw new _hot_updater_plugin_core_internal.DatabaseRowReferencedError();
212
- throw error;
213
- }
214
- }
215
- case "releases": {
216
- let query = db.deleteFrom("releases");
217
- if (where !== void 0) query = query.where(where);
218
- await query.execute();
219
- }
220
- }
221
- },
222
- count: (input) => countPostgresRows(db, input, buildWhere(input.where)),
223
- async findOne(input) {
224
- const where = buildWhere(input.where);
225
- switch (input.model) {
226
- case "bundles": {
227
- let query = db.selectFrom("bundles").selectAll();
228
- if (where !== void 0) query = query.where(where);
229
- return await query.executeTakeFirst() ?? null;
230
- }
231
- case "api_keys": {
232
- let query = db.selectFrom("api_keys").selectAll();
233
- if (where !== void 0) query = query.where(where);
234
- return await query.executeTakeFirst() ?? null;
235
- }
236
- case "bundle_patches": {
237
- let query = db.selectFrom("bundle_patches").selectAll();
238
- if (where !== void 0) query = query.where(where);
239
- return await query.executeTakeFirst() ?? null;
240
- }
241
- case "channels": {
242
- let query = db.selectFrom("channels").selectAll();
243
- if (where !== void 0) query = query.where(where);
244
- return await query.executeTakeFirst() ?? null;
245
- }
246
- case "releases": {
247
- let query = db.selectFrom("releases").selectAll();
248
- if (where !== void 0) query = query.where(where);
249
- return await query.executeTakeFirst() ?? null;
250
- }
251
- case "release_catalogs": {
252
- let query = db.selectFrom("release_catalogs").selectAll();
253
- if (where !== void 0) query = query.where(where);
254
- return await query.executeTakeFirst() ?? null;
255
- }
256
- }
257
- },
258
- findMany: (input) => findManyPostgresRows(db, input, buildWhere(input.where)),
259
- async insertChannel({ row }) {
260
- const inserted = await db.insertInto("channels").values(row).onConflict((conflict) => conflict.column("name").doNothing()).returningAll().executeTakeFirst();
261
- if (inserted !== void 0) return {
262
- row: inserted,
263
- inserted: true
264
- };
265
- return {
266
- row: await db.selectFrom("channels").selectAll().where("name", "=", row.name).executeTakeFirstOrThrow(),
267
- inserted: false
268
- };
269
- },
270
- async deleteChannel({ id }) {
271
- try {
272
- return await db.deleteFrom("channels").where("id", "=", id).returning("id").executeTakeFirst() === void 0 ? {
273
- deleted: false,
274
- reason: "not_found"
275
- } : { deleted: true };
276
- } catch (error) {
277
- if (isForeignKeyViolation(error)) return {
278
- deleted: false,
279
- reason: "not_empty"
280
- };
281
- throw error;
282
- }
283
- },
284
- transaction: (callback) => db.transaction().execute((transaction) => callback(createPostgresImplementation(transaction))),
285
- dispose: () => db.destroy()
286
- });
31
+ /**
32
+ * Hot Updater's database on PostgreSQL, through a `pg` pool or the given
33
+ * Kysely dialect. Apply `sql/bundles.sql` before first use; the schema fence
34
+ * refuses a database without its settings rows.
35
+ */
287
36
  const postgres = (config) => {
288
37
  const { dialect, ...poolConfig } = config;
289
- const adapter = (0, _hot_updater_plugin_core_internal.createDatabasePluginAdapter)("postgres", dialect !== void 0 ? createPostgresImplementation(new kysely.Kysely({ dialect })) : createPostgresImplementation(new kysely.Kysely({ dialect: new kysely.PostgresDialect({ pool: new Pool(poolConfig) }) })));
290
- return (0, _hot_updater_plugin_core.createDatabasePlugin)({
291
- name: "postgres",
292
- models: adapter.models,
293
- commit: adapter.commit,
294
- dispose: adapter.dispose
295
- });
38
+ const db = new kysely.Kysely({ dialect: dialect ?? new kysely.PostgresDialect({ pool: new Pool(poolConfig) }) });
39
+ return {
40
+ ...(0, _hot_updater_server_database.createEngineDatabase)({
41
+ name: "postgres",
42
+ adapter: (0, _hot_updater_server_database.createSqlAdapter)({ executor: (0, _hot_updater_server_adapters_kysely.kyselyExecutor)(db, "postgresql") })
43
+ }),
44
+ dispose: () => db.destroy()
45
+ };
296
46
  };
297
47
  //#endregion
298
48
  exports.postgres = postgres;
package/dist/index.d.cts CHANGED
@@ -1,11 +1,15 @@
1
- import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
1
+ import { EngineDatabase } from "@hot-updater/plugin-core";
2
2
  import { Dialect } from "kysely";
3
3
  import { PoolConfig } from "pg";
4
-
5
4
  //#region src/postgres.d.ts
6
5
  type PostgresConfig = PoolConfig & {
7
6
  readonly dialect?: Dialect;
8
7
  };
9
- declare const postgres: (config: PostgresConfig) => _$_hot_updater_plugin_core0.DatabasePlugin;
8
+ /**
9
+ * Hot Updater's database on PostgreSQL, through a `pg` pool or the given
10
+ * Kysely dialect. Apply `sql/bundles.sql` before first use; the schema fence
11
+ * refuses a database without its settings rows.
12
+ */
13
+ declare const postgres: (config: PostgresConfig) => EngineDatabase;
10
14
  //#endregion
11
15
  export { PostgresConfig, postgres };
package/dist/index.d.mts CHANGED
@@ -1,11 +1,15 @@
1
- import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
2
1
  import { Dialect } from "kysely";
3
2
  import { PoolConfig } from "pg";
4
-
3
+ import { EngineDatabase } from "@hot-updater/plugin-core";
5
4
  //#region src/postgres.d.ts
6
5
  type PostgresConfig = PoolConfig & {
7
6
  readonly dialect?: Dialect;
8
7
  };
9
- declare const postgres: (config: PostgresConfig) => _$_hot_updater_plugin_core0.DatabasePlugin;
8
+ /**
9
+ * Hot Updater's database on PostgreSQL, through a `pg` pool or the given
10
+ * Kysely dialect. Apply `sql/bundles.sql` before first use; the schema fence
11
+ * refuses a database without its settings rows.
12
+ */
13
+ declare const postgres: (config: PostgresConfig) => EngineDatabase;
10
14
  //#endregion
11
15
  export { PostgresConfig, postgres };
package/dist/index.mjs CHANGED
@@ -1,274 +1,24 @@
1
- import { createDatabasePlugin } from "@hot-updater/plugin-core";
2
- import { DatabaseRowReferencedError, createDatabasePluginAdapter } from "@hot-updater/plugin-core/internal";
3
- import { Kysely, PostgresDialect, sql } from "kysely";
1
+ import { kyselyExecutor } from "@hot-updater/server/adapters/kysely";
2
+ import { createEngineDatabase, createSqlAdapter } from "@hot-updater/server/database";
3
+ import { Kysely, PostgresDialect } from "kysely";
4
4
  import pg from "pg";
5
- //#region src/postgresQuery.ts
6
- const applyOrder = (order, clause) => {
7
- const directed = clause.direction === "asc" ? order.asc() : order.desc();
8
- return clause.nulls === void 0 ? directed : clause.nulls === "first" ? directed.nullsFirst() : directed.nullsLast();
9
- };
10
- const countPostgresRows = async (db, input, where) => {
11
- switch (input.model) {
12
- case "bundles": {
13
- let rows = db.selectFrom("bundles");
14
- if (where !== void 0) rows = rows.where(where);
15
- const query = input.distinct === void 0 ? rows.select(({ fn }) => fn.countAll().as("count")) : db.selectFrom(rows.select(input.distinct).distinct().as("distinct_rows")).select(({ fn }) => fn.countAll().as("count"));
16
- return Number((await query.executeTakeFirstOrThrow()).count);
17
- }
18
- case "bundle_patches": {
19
- let rows = db.selectFrom("bundle_patches");
20
- if (where !== void 0) rows = rows.where(where);
21
- const query = input.distinct === void 0 ? rows.select(({ fn }) => fn.countAll().as("count")) : db.selectFrom(rows.select(input.distinct).distinct().as("distinct_rows")).select(({ fn }) => fn.countAll().as("count"));
22
- return Number((await query.executeTakeFirstOrThrow()).count);
23
- }
24
- case "releases": {
25
- let rows = db.selectFrom("releases");
26
- if (where !== void 0) rows = rows.where(where);
27
- const query = rows.select(({ fn }) => fn.countAll().as("count"));
28
- return Number((await query.executeTakeFirstOrThrow()).count);
29
- }
30
- }
31
- };
32
- const findManyPostgresRows = async (db, input, where) => {
33
- switch (input.model) {
34
- case "bundles": {
35
- let query = db.selectFrom("bundles").selectAll();
36
- if (where !== void 0) query = query.where(where);
37
- if (input.distinctOn !== void 0) query = query.distinctOn(input.distinctOn.fields);
38
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
39
- return query.limit(input.limit).offset(input.offset).execute();
40
- }
41
- case "release_catalogs": {
42
- let query = db.selectFrom("release_catalogs").selectAll();
43
- if (where !== void 0) query = query.where(where);
44
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
45
- return query.limit(input.limit).offset(input.offset).execute();
46
- }
47
- case "bundle_events": {
48
- let query = db.selectFrom("bundle_events").selectAll();
49
- if (where !== void 0) query = query.where(where);
50
- if (input.distinctOn !== void 0) query = query.distinctOn(input.distinctOn.fields);
51
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
52
- return query.limit(input.limit).offset(input.offset).execute();
53
- }
54
- case "channels": {
55
- let query = db.selectFrom("channels").selectAll();
56
- if (where !== void 0) query = query.where(where);
57
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
58
- return query.limit(input.limit).offset(input.offset).execute();
59
- }
60
- case "api_keys": {
61
- let query = db.selectFrom("api_keys").selectAll();
62
- if (where !== void 0) query = query.where(where);
63
- if (input.distinctOn !== void 0) query = query.distinctOn(input.distinctOn.fields);
64
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
65
- return query.limit(input.limit).offset(input.offset).execute();
66
- }
67
- case "bundle_patches": {
68
- let query = db.selectFrom("bundle_patches").selectAll();
69
- if (where !== void 0) query = query.where(where);
70
- if (input.distinctOn !== void 0) query = query.distinctOn(input.distinctOn.fields);
71
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
72
- return query.limit(input.limit).offset(input.offset).execute();
73
- }
74
- case "releases": {
75
- let query = db.selectFrom("releases").selectAll();
76
- if (where !== void 0) query = query.where(where);
77
- for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
78
- return query.limit(input.limit).offset(input.offset).execute();
79
- }
80
- }
81
- };
82
- //#endregion
83
5
  //#region src/postgres.ts
84
6
  const { Pool } = pg;
85
- var InvalidPostgresPredicateError = class extends Error {
86
- name = "InvalidPostgresPredicateError";
87
- };
88
- const isForeignKeyViolation = (error) => typeof error === "object" && error !== null && Reflect.get(error, "code") === "23503";
89
- const escapeLikePattern = (value) => value.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
90
- const stringPredicate = (condition, operator) => {
91
- if (typeof condition.value !== "string") throw new InvalidPostgresPredicateError();
92
- const column = sql.ref(condition.field);
93
- const literal = escapeLikePattern(condition.value);
94
- const pattern = operator === "contains" ? `%${literal}%` : operator === "starts_with" ? `${literal}%` : `%${literal}`;
95
- return "mode" in condition && condition.mode === "insensitive" ? sql`lower(${column}) like lower(${pattern}) escape '\\'` : sql`${column} like ${pattern} escape '\\'`;
96
- };
97
- const predicate = (condition) => {
98
- const column = sql.ref(condition.field);
99
- const operator = condition.operator ?? "eq";
100
- switch (operator) {
101
- case "eq":
102
- case "ne":
103
- if (condition.value === null) return operator === "eq" ? sql`${column} is null` : sql`${column} is not null`;
104
- if ("mode" in condition && condition.mode === "insensitive") return operator === "eq" ? sql`lower(${column}) = lower(${condition.value})` : sql`lower(${column}) <> lower(${condition.value})`;
105
- return operator === "eq" ? sql`${column} = ${condition.value}` : sql`${column} <> ${condition.value}`;
106
- case "gt": return sql`${column} > ${condition.value}`;
107
- case "gte": return sql`${column} >= ${condition.value}`;
108
- case "lt": return sql`${column} < ${condition.value}`;
109
- case "lte": return sql`${column} <= ${condition.value}`;
110
- case "in":
111
- case "not_in":
112
- if (!Array.isArray(condition.value)) throw new InvalidPostgresPredicateError();
113
- if (condition.value.length === 0) return sql`${operator === "not_in"}`;
114
- return operator === "in" ? sql`${column} in (${sql.join(condition.value)})` : sql`${column} not in (${sql.join(condition.value)})`;
115
- case "contains":
116
- case "starts_with":
117
- case "ends_with": return stringPredicate(condition, operator);
118
- }
119
- };
120
- const buildWhere = (where) => {
121
- const [first, ...rest] = where ?? [];
122
- if (first === void 0) return;
123
- let expression = predicate(first);
124
- for (const condition of rest) {
125
- const next = predicate(condition);
126
- expression = condition.connector === "OR" ? sql`(${expression} or ${next})` : sql`(${expression} and ${next})`;
127
- }
128
- return expression;
129
- };
130
- const createPostgresImplementation = (db) => ({
131
- async create(input) {
132
- switch (input.model) {
133
- case "bundles": return db.insertInto("bundles").values(input.data).returningAll().executeTakeFirstOrThrow();
134
- case "bundle_patches": return db.insertInto("bundle_patches").values(input.data).returningAll().executeTakeFirstOrThrow();
135
- case "bundle_events": return db.insertInto("bundle_events").values(input.data).returningAll().executeTakeFirstOrThrow();
136
- case "releases": return db.insertInto("releases").values(input.data).returningAll().executeTakeFirstOrThrow();
137
- case "release_catalogs": return db.insertInto("release_catalogs").values(input.data).returningAll().executeTakeFirstOrThrow();
138
- case "channels": {
139
- const query = db.insertInto("channels").values(input.data);
140
- return await (input.onConflict === "ignore" ? query.onConflict((conflict) => conflict.column("name").doNothing()) : query).returningAll().executeTakeFirst() ?? await db.selectFrom("channels").selectAll().where("name", "=", input.data.name).executeTakeFirstOrThrow();
141
- }
142
- case "api_keys": return await (input.onConflict === "ignore" ? db.insertInto("api_keys").values(input.data).onConflict((conflict) => conflict.column("hash").doNothing()) : db.insertInto("api_keys").values(input.data)).returningAll().executeTakeFirst() ?? await db.selectFrom("api_keys").selectAll().where("hash", "=", input.data.hash).executeTakeFirstOrThrow();
143
- }
144
- },
145
- async update(input) {
146
- const where = buildWhere(input.where);
147
- if (input.model === "api_keys") {
148
- let query = db.updateTable("api_keys").set(input.update);
149
- if (where !== void 0) query = query.where(where);
150
- return await query.returningAll().executeTakeFirst() ?? null;
151
- }
152
- if (input.model === "releases") {
153
- let query = db.updateTable("releases").set(input.update);
154
- if (where !== void 0) query = query.where(where);
155
- return await query.returningAll().executeTakeFirst() ?? null;
156
- }
157
- if (input.model === "release_catalogs") {
158
- let query = db.updateTable("release_catalogs").set(input.update);
159
- if (where !== void 0) query = query.where(where);
160
- return await query.returningAll().executeTakeFirst() ?? null;
161
- }
162
- let query = db.updateTable("bundles").set(input.update);
163
- if (where !== void 0) query = query.where(where);
164
- return await query.returningAll().executeTakeFirst() ?? null;
165
- },
166
- async delete(input) {
167
- const where = buildWhere(input.where);
168
- switch (input.model) {
169
- case "bundles": {
170
- let query = db.deleteFrom("bundles");
171
- if (where !== void 0) query = query.where(where);
172
- await query.execute();
173
- return;
174
- }
175
- case "bundle_patches": {
176
- let query = db.deleteFrom("bundle_patches");
177
- if (where !== void 0) query = query.where(where);
178
- await query.execute();
179
- return;
180
- }
181
- case "channels": {
182
- let query = db.deleteFrom("channels");
183
- if (where !== void 0) query = query.where(where);
184
- try {
185
- await query.execute();
186
- } catch (error) {
187
- if (isForeignKeyViolation(error)) throw new DatabaseRowReferencedError();
188
- throw error;
189
- }
190
- }
191
- case "releases": {
192
- let query = db.deleteFrom("releases");
193
- if (where !== void 0) query = query.where(where);
194
- await query.execute();
195
- }
196
- }
197
- },
198
- count: (input) => countPostgresRows(db, input, buildWhere(input.where)),
199
- async findOne(input) {
200
- const where = buildWhere(input.where);
201
- switch (input.model) {
202
- case "bundles": {
203
- let query = db.selectFrom("bundles").selectAll();
204
- if (where !== void 0) query = query.where(where);
205
- return await query.executeTakeFirst() ?? null;
206
- }
207
- case "api_keys": {
208
- let query = db.selectFrom("api_keys").selectAll();
209
- if (where !== void 0) query = query.where(where);
210
- return await query.executeTakeFirst() ?? null;
211
- }
212
- case "bundle_patches": {
213
- let query = db.selectFrom("bundle_patches").selectAll();
214
- if (where !== void 0) query = query.where(where);
215
- return await query.executeTakeFirst() ?? null;
216
- }
217
- case "channels": {
218
- let query = db.selectFrom("channels").selectAll();
219
- if (where !== void 0) query = query.where(where);
220
- return await query.executeTakeFirst() ?? null;
221
- }
222
- case "releases": {
223
- let query = db.selectFrom("releases").selectAll();
224
- if (where !== void 0) query = query.where(where);
225
- return await query.executeTakeFirst() ?? null;
226
- }
227
- case "release_catalogs": {
228
- let query = db.selectFrom("release_catalogs").selectAll();
229
- if (where !== void 0) query = query.where(where);
230
- return await query.executeTakeFirst() ?? null;
231
- }
232
- }
233
- },
234
- findMany: (input) => findManyPostgresRows(db, input, buildWhere(input.where)),
235
- async insertChannel({ row }) {
236
- const inserted = await db.insertInto("channels").values(row).onConflict((conflict) => conflict.column("name").doNothing()).returningAll().executeTakeFirst();
237
- if (inserted !== void 0) return {
238
- row: inserted,
239
- inserted: true
240
- };
241
- return {
242
- row: await db.selectFrom("channels").selectAll().where("name", "=", row.name).executeTakeFirstOrThrow(),
243
- inserted: false
244
- };
245
- },
246
- async deleteChannel({ id }) {
247
- try {
248
- return await db.deleteFrom("channels").where("id", "=", id).returning("id").executeTakeFirst() === void 0 ? {
249
- deleted: false,
250
- reason: "not_found"
251
- } : { deleted: true };
252
- } catch (error) {
253
- if (isForeignKeyViolation(error)) return {
254
- deleted: false,
255
- reason: "not_empty"
256
- };
257
- throw error;
258
- }
259
- },
260
- transaction: (callback) => db.transaction().execute((transaction) => callback(createPostgresImplementation(transaction))),
261
- dispose: () => db.destroy()
262
- });
7
+ /**
8
+ * Hot Updater's database on PostgreSQL, through a `pg` pool or the given
9
+ * Kysely dialect. Apply `sql/bundles.sql` before first use; the schema fence
10
+ * refuses a database without its settings rows.
11
+ */
263
12
  const postgres = (config) => {
264
13
  const { dialect, ...poolConfig } = config;
265
- const adapter = createDatabasePluginAdapter("postgres", dialect !== void 0 ? createPostgresImplementation(new Kysely({ dialect })) : createPostgresImplementation(new Kysely({ dialect: new PostgresDialect({ pool: new Pool(poolConfig) }) })));
266
- return createDatabasePlugin({
267
- name: "postgres",
268
- models: adapter.models,
269
- commit: adapter.commit,
270
- dispose: adapter.dispose
271
- });
14
+ const db = new Kysely({ dialect: dialect ?? new PostgresDialect({ pool: new Pool(poolConfig) }) });
15
+ return {
16
+ ...createEngineDatabase({
17
+ name: "postgres",
18
+ adapter: createSqlAdapter({ executor: kyselyExecutor(db, "postgresql") })
19
+ }),
20
+ dispose: () => db.destroy()
21
+ };
272
22
  };
273
23
  //#endregion
274
24
  export { postgres };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/postgres",
3
3
  "type": "module",
4
- "version": "1.0.0-rc.1",
4
+ "version": "1.0.0-rc.16",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.mjs",
@@ -34,8 +34,9 @@
34
34
  "dependencies": {
35
35
  "kysely": "0.28.17",
36
36
  "pg": "8.13.1",
37
- "@hot-updater/core": "1.0.0-rc.0",
38
- "@hot-updater/plugin-core": "1.0.0-rc.1"
37
+ "@hot-updater/plugin-core": "1.0.0-rc.16",
38
+ "@hot-updater/core": "1.0.0-rc.15",
39
+ "@hot-updater/server": "1.0.0-rc.16"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@electric-sql/pglite": "0.4.1",
@@ -44,7 +45,7 @@
44
45
  "camelcase-keys": "^9.1.3",
45
46
  "kysely-pglite-dialect": "1.2.0",
46
47
  "pg-minify": "^1.6.5",
47
- "@hot-updater/test-utils": "1.0.0-rc.1"
48
+ "@hot-updater/test-utils": "1.0.0-rc.16"
48
49
  },
49
50
  "scripts": {
50
51
  "build": "tsdown",
package/sql/bundles.sql CHANGED
@@ -1,179 +1,79 @@
1
1
  -- HotUpdater.schema
2
2
 
3
- create table channels (
4
- id varchar(255) primary key not null,
5
- name varchar(255) not null
6
- );
7
-
8
- create table bundles (
9
- id uuid primary key not null,
10
- platform text not null,
11
- file_hash text not null,
12
- git_commit_hash text,
13
- storage_uri text not null,
14
- archive_byte_size double precision not null,
15
- metadata json not null default '{}'::json,
16
- manifest_storage_uri text,
17
- manifest_file_hash text,
18
- asset_base_storage_uri text
19
- );
20
-
21
- create table bundle_patches (
22
- id varchar(255) primary key not null,
23
- bundle_id uuid not null,
24
- base_bundle_id uuid not null,
25
- base_file_hash text not null,
26
- patch_file_hash text not null,
27
- patch_storage_uri text not null,
28
- byte_size double precision not null,
29
- order_index integer not null default 0
30
- );
31
-
32
- create table releases (
33
- id uuid primary key not null,
34
- revision integer not null,
35
- scope_key varchar(2048) not null,
36
- channel_id varchar(255) not null,
37
- platform text not null,
38
- kind text not null,
39
- bundle_id uuid,
40
- strategy text not null,
41
- target_app_version text,
42
- fingerprint_hash text,
43
- enabled boolean not null,
44
- should_force_update boolean not null,
45
- message text,
46
- rollout_cohort_count integer not null default 1000,
47
- target_cohorts json not null default '[]'::json,
48
- operation text not null,
49
- source_release_id uuid,
50
- created_at_ms double precision not null,
51
- updated_at_ms double precision not null
52
- );
53
-
54
- create table release_catalogs (
55
- scope_key varchar(2048) primary key not null,
56
- catalog_id varchar(255) not null,
57
- strategy text not null,
58
- channel_id varchar(255) not null,
59
- channel_key varchar(1400) not null,
60
- platform text not null,
61
- fingerprint_hash text,
62
- generation double precision not null,
63
- payload text not null,
64
- catalog_hash varchar(71) not null,
65
- byte_size integer not null,
66
- is_tombstone boolean not null,
67
- updated_at_ms double precision not null
68
- );
69
-
70
- create table bundle_events (
71
- id uuid primary key not null,
72
- type text not null,
73
- install_id text not null,
74
- user_id text,
75
- username text,
76
- from_release_id uuid,
77
- from_bundle_id uuid,
78
- to_release_id uuid,
79
- to_bundle_id uuid not null,
80
- platform text not null,
81
- app_version text not null,
82
- channel text not null,
83
- cohort text not null,
84
- update_strategy text,
85
- fingerprint_hash text,
86
- sdk_version text,
87
- received_at_ms double precision not null
88
- );
89
-
90
- create table api_keys (
91
- id varchar(255) primary key not null,
92
- hash text not null,
93
- name text not null,
94
- prefix text not null,
95
- role text not null,
96
- created_at_ms double precision not null,
97
- revoked_at_ms double precision
98
- );
99
-
100
- create table private_hot_updater_settings (
101
- key varchar(255) primary key not null,
102
- value text not null default '1.0.0'
103
- );
104
-
105
- create unique index channels_name_key on channels(name);
106
- create index bundle_patches_bundle_id_idx on bundle_patches(bundle_id);
107
- create index bundle_patches_base_bundle_id_idx on bundle_patches(base_bundle_id);
108
- create index releases_scope_order_idx on releases(scope_key, id);
109
- create index releases_channel_platform_order_idx on releases(channel_id, platform, id);
110
- create index releases_bundle_id_idx on releases(bundle_id);
111
- create index releases_fingerprint_hash_idx on releases(fingerprint_hash);
112
- create index releases_enabled_idx on releases(enabled);
113
- create index release_catalogs_channel_idx on release_catalogs(channel_id);
114
- create index bundle_events_received_at_idx on bundle_events(received_at_ms, id);
115
- create index bundle_events_install_idx on bundle_events(install_id, received_at_ms, id);
116
- create index bundle_events_user_id_idx on bundle_events(user_id, received_at_ms, id);
117
- create index bundle_events_username_idx on bundle_events(username, received_at_ms, id);
118
- create index bundle_events_to_bundle_idx on bundle_events(type, to_bundle_id, received_at_ms, id);
119
- create index bundle_events_from_bundle_idx on bundle_events(type, from_bundle_id, received_at_ms, id);
120
- create index bundle_events_to_release_idx on bundle_events(type, to_release_id, received_at_ms, id);
121
- create index bundle_events_from_release_idx on bundle_events(type, from_release_id, received_at_ms, id);
122
- create unique index api_keys_hash_key on api_keys(hash);
123
- create index api_keys_created_at_idx on api_keys(created_at_ms, id);
124
-
125
- alter table channels add constraint channels_id_length_check
126
- check (char_length(id) between 1 and 255);
127
- alter table channels add constraint channels_name_length_check
128
- check (char_length(name) between 1 and 255);
129
- alter table bundles add constraint bundles_archive_byte_size_check
130
- check (archive_byte_size >= 0 and archive_byte_size <= 9007199254740991);
131
- alter table bundle_patches add constraint bundle_patches_byte_size_check
132
- check (byte_size >= 0 and byte_size <= 9007199254740991);
133
- alter table releases add constraint releases_revision_check
134
- check (revision >= 1);
135
- alter table releases add constraint releases_kind_bundle_check
136
- check ((kind = 'BUNDLE' and bundle_id is not null) or (kind = 'EMBEDDED' and bundle_id is null));
137
- alter table releases add constraint releases_strategy_target_check
138
- check ((strategy = 'APP_VERSION' and target_app_version is not null and fingerprint_hash is null) or (strategy = 'FINGERPRINT' and target_app_version is null and fingerprint_hash is not null));
139
- alter table releases add constraint releases_rollout_cohort_count_check
140
- check (rollout_cohort_count >= 0 and rollout_cohort_count <= 1000);
141
- alter table releases add constraint releases_operation_check
142
- check (operation in ('DEPLOY', 'PROMOTE', 'ROLLBACK'));
143
- alter table release_catalogs add constraint release_catalogs_strategy_target_check
144
- check ((strategy = 'APP_VERSION' and fingerprint_hash is null) or (strategy = 'FINGERPRINT' and fingerprint_hash is not null));
145
- alter table release_catalogs add constraint release_catalogs_generation_check
146
- check (generation >= 1 and generation <= 9007199254740991);
147
- alter table release_catalogs add constraint release_catalogs_byte_size_check
148
- check (byte_size >= 0 and byte_size <= 262144);
149
- alter table bundle_events add constraint bundle_events_type_check
150
- check (type in ('UPDATE_APPLIED', 'RECOVERED', 'RELEASE_ADOPTED', 'UNCHANGED'));
151
- alter table bundle_events add constraint bundle_events_platform_check
152
- check (platform in ('ios', 'android'));
153
- alter table bundle_events add constraint bundle_events_shape_check
154
- check (((type in ('UPDATE_APPLIED', 'RECOVERED', 'RELEASE_ADOPTED')) and from_bundle_id is not null and update_strategy is not null and update_strategy in ('fingerprint', 'appVersion')) or (type = 'UNCHANGED' and from_bundle_id is null and update_strategy is null));
155
- alter table bundle_events add constraint bundle_events_received_at_check
156
- check (received_at_ms >= 0);
157
- alter table api_keys add constraint api_keys_role_check
158
- check (role = 'client');
159
- alter table api_keys add constraint api_keys_created_at_check
160
- check (created_at_ms >= 0);
161
- alter table api_keys add constraint api_keys_revoked_at_check
162
- check (revoked_at_ms is null or revoked_at_ms >= 0);
163
-
164
- alter table bundle_patches add constraint bundle_patches_bundle_id_fk
165
- foreign key (bundle_id) references bundles(id) on update restrict on delete cascade;
166
- alter table bundle_patches add constraint bundle_patches_base_bundle_id_fk
167
- foreign key (base_bundle_id) references bundles(id) on update restrict on delete cascade;
168
- alter table releases add constraint releases_channel_id_fk
169
- foreign key (channel_id) references channels(id) on update restrict on delete restrict;
170
- alter table releases add constraint releases_bundle_id_fk
171
- foreign key (bundle_id) references bundles(id) on update restrict on delete restrict;
172
- alter table releases add constraint releases_source_release_id_fk
173
- foreign key (source_release_id) references releases(id) on update restrict on delete set null;
174
- alter table release_catalogs add constraint release_catalogs_channel_id_fk
175
- foreign key (channel_id) references channels(id) on update restrict on delete restrict;
176
-
177
- insert into private_hot_updater_settings (key, value)
178
- values ('schema.core', '1.0.0')
179
- on conflict (key) do update set value = excluded.value;
3
+ CREATE TABLE IF NOT EXISTS "bundles" ("id" varchar(36) COLLATE "C" NOT NULL, "platform" varchar(16) COLLATE "C" NOT NULL, "git_commit_hash" varchar(255) COLLATE "C", "metadata" jsonb NOT NULL, "manifest_storage_uri" text COLLATE "C" NOT NULL, "manifest_file_hash" text COLLATE "C" NOT NULL, "asset_base_storage_uri" text COLLATE "C" NOT NULL, "_refs_bundle_patches_bundle_id" bigint NOT NULL DEFAULT 0, "_refs_bundle_patches_base_bundle_id" bigint NOT NULL DEFAULT 0, "_refs_releases_bundle_id" bigint NOT NULL DEFAULT 0, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("id"));
4
+
5
+ CREATE INDEX IF NOT EXISTS "bundles_byPlatform" ON "bundles" ("platform", "id");
6
+
7
+ CREATE TABLE IF NOT EXISTS "bundle_patches" ("id" varchar(255) COLLATE "C" NOT NULL, "bundle_id" varchar(36) COLLATE "C" NOT NULL, "base_bundle_id" varchar(36) COLLATE "C" NOT NULL, "base_file_hash" text COLLATE "C" NOT NULL, "patch_file_hash" text COLLATE "C" NOT NULL, "patch_storage_uri" text COLLATE "C" NOT NULL, "byte_size" bigint NOT NULL, "order_index" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("id"));
8
+
9
+ CREATE UNIQUE INDEX IF NOT EXISTS "bundle_patches_pair" ON "bundle_patches" ("bundle_id", "base_bundle_id");
10
+
11
+ CREATE INDEX IF NOT EXISTS "bundle_patches_byBundle" ON "bundle_patches" ("bundle_id", "order_index", "id");
12
+
13
+ CREATE INDEX IF NOT EXISTS "bundle_patches_byBase" ON "bundle_patches" ("base_bundle_id", "bundle_id", "id");
14
+
15
+ CREATE TABLE IF NOT EXISTS "releases" ("id" varchar(36) COLLATE "C" NOT NULL, "revision" bigint NOT NULL, "scope_key" varchar(2048) COLLATE "C" NOT NULL, "channel_id" varchar(1408) COLLATE "C" NOT NULL, "platform" varchar(16) COLLATE "C" NOT NULL, "kind" varchar(16) COLLATE "C" NOT NULL, "bundle_id" varchar(36) COLLATE "C", "strategy" varchar(16) COLLATE "C" NOT NULL, "target_app_version" text COLLATE "C", "fingerprint_hash" varchar(255) COLLATE "C", "enabled" boolean NOT NULL, "should_force_update" boolean NOT NULL, "message" text COLLATE "C", "rollout_cohort_count" bigint NOT NULL, "target_cohorts" jsonb NOT NULL, "operation" varchar(16) COLLATE "C" NOT NULL, "source_release_id" varchar(36) COLLATE "C", "created_at_ms" bigint NOT NULL, "updated_at_ms" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("id"));
16
+
17
+ CREATE INDEX IF NOT EXISTS "releases_byScope" ON "releases" ("scope_key", "id");
18
+
19
+ CREATE INDEX IF NOT EXISTS "releases_byScopeEnabled" ON "releases" ("scope_key", "enabled", "id");
20
+
21
+ CREATE INDEX IF NOT EXISTS "releases_byChannelPlatform" ON "releases" ("channel_id", "platform", "id");
22
+
23
+ CREATE INDEX IF NOT EXISTS "releases_byChannelPlatformEnabled" ON "releases" ("channel_id", "platform", "enabled", "id");
24
+
25
+ CREATE INDEX IF NOT EXISTS "releases_byBundle" ON "releases" ("bundle_id", "id");
26
+
27
+ CREATE TABLE IF NOT EXISTS "release_catalogs" ("scope_key" varchar(2048) COLLATE "C" NOT NULL, "catalog_id" varchar(255) COLLATE "C" NOT NULL, "strategy" varchar(16) COLLATE "C" NOT NULL, "channel_id" varchar(1408) COLLATE "C" NOT NULL, "channel_key" varchar(1400) COLLATE "C" NOT NULL, "platform" varchar(16) COLLATE "C" NOT NULL, "fingerprint_hash" varchar(255) COLLATE "C", "generation" bigint NOT NULL, "payload" text COLLATE "C" NOT NULL, "catalog_hash" varchar(71) COLLATE "C" NOT NULL, "byte_size" bigint NOT NULL, "is_tombstone" boolean NOT NULL, "updated_at_ms" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("scope_key"));
28
+
29
+ CREATE TABLE IF NOT EXISTS "channels" ("id" varchar(1408) COLLATE "C" NOT NULL, "name" varchar(255) COLLATE "C" NOT NULL, "_refs_releases_channel_id" bigint NOT NULL DEFAULT 0, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("id"));
30
+
31
+ CREATE INDEX IF NOT EXISTS "channels_all" ON "channels" ("name", "id");
32
+
33
+ CREATE UNIQUE INDEX IF NOT EXISTS "channels_name" ON "channels" ("name");
34
+
35
+ CREATE TABLE IF NOT EXISTS "bundle_totals" ("platform_key" varchar(16) COLLATE "C" NOT NULL, "_shard" bigint NOT NULL, "bundles" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("platform_key", "_shard"));
36
+
37
+ CREATE TABLE IF NOT EXISTS "bundle_events" ("id" varchar(36) COLLATE "C" NOT NULL, "type" varchar(32) COLLATE "C" NOT NULL, "install_id" varchar(255) COLLATE "C" NOT NULL, "user_id" varchar(255) COLLATE "C", "from_release_id" varchar(36) COLLATE "C", "from_bundle_id" varchar(36) COLLATE "C", "to_release_id" varchar(36) COLLATE "C", "to_bundle_id" varchar(36) COLLATE "C" NOT NULL, "platform" varchar(16) COLLATE "C" NOT NULL, "app_version" text COLLATE "C" NOT NULL, "channel" text COLLATE "C" NOT NULL, "metadata" jsonb NOT NULL, "received_at_ms" bigint NOT NULL, "day" bigint, "movement_install_id" text COLLATE "C", "bundle_ref" jsonb, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("id"));
38
+
39
+ CREATE INDEX IF NOT EXISTS "bundle_events_recent" ON "bundle_events" ("channel", "platform", "day", "received_at_ms", "id");
40
+
41
+ CREATE INDEX IF NOT EXISTS "bundle_events_movementsByInstall" ON "bundle_events" ("movement_install_id", "received_at_ms", "id");
42
+
43
+ CREATE TABLE IF NOT EXISTS "bundle_events__byBundle" ("platform" varchar(16) COLLATE "C" NOT NULL, "channel" text COLLATE "C" NOT NULL, "type" varchar(32) COLLATE "C" NOT NULL, "bundle_ref" text COLLATE "C" NOT NULL, "day" bigint NOT NULL, "received_at_ms" bigint NOT NULL, "id" varchar(36) COLLATE "C" NOT NULL, PRIMARY KEY ("platform", "channel", "type", "bundle_ref", "day", "received_at_ms", "id"));
44
+
45
+ CREATE INDEX IF NOT EXISTS "bundle_events_byDay" ON "bundle_events" ("day", "received_at_ms", "id");
46
+
47
+ CREATE TABLE IF NOT EXISTS "bundle_event_heads" ("install_id" varchar(255) COLLATE "C" NOT NULL, "id" varchar(36) COLLATE "C" NOT NULL, "type" varchar(32) COLLATE "C" NOT NULL, "user_id" varchar(255) COLLATE "C", "from_release_id" varchar(36) COLLATE "C", "from_bundle_id" varchar(36) COLLATE "C", "to_release_id" varchar(36) COLLATE "C", "to_bundle_id" varchar(36) COLLATE "C" NOT NULL, "platform" varchar(16) COLLATE "C" NOT NULL, "app_version" text COLLATE "C" NOT NULL, "channel" text COLLATE "C" NOT NULL, "metadata" jsonb NOT NULL, "received_at_ms" bigint NOT NULL, "current_release_id" varchar(36) COLLATE "C", "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("install_id"));
48
+
49
+ CREATE INDEX IF NOT EXISTS "bundle_event_heads_byUser" ON "bundle_event_heads" ("user_id", "install_id");
50
+
51
+ CREATE TABLE IF NOT EXISTS "insights_overview" ("identity" varchar(32) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "downloads" bigint NOT NULL, "launches" bigint NOT NULL, "failed_launches" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard"));
52
+
53
+ CREATE TABLE IF NOT EXISTS "insights_sketches" ("identity" varchar(32) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "launch_users" text COLLATE "C", "activity_users" text COLLATE "C", "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard"));
54
+
55
+ CREATE TABLE IF NOT EXISTS "insights_distribution" ("channel" text COLLATE "C" NOT NULL, "platform" varchar(16) COLLATE "C" NOT NULL, "app_version" text COLLATE "C" NOT NULL, "release_id" varchar(36) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "latest_installations" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("channel", "platform", "app_version", "release_id", "bucket_start_ms", "_shard"));
56
+
57
+ CREATE INDEX IF NOT EXISTS "insights_distribution_byScope" ON "insights_distribution" ("channel", "platform", "bucket_start_ms", "app_version", "release_id", "_shard");
58
+
59
+ CREATE INDEX IF NOT EXISTS "insights_distribution_byVersion" ON "insights_distribution" ("channel", "platform", "app_version", "bucket_start_ms", "release_id", "_shard");
60
+
61
+ CREATE TABLE IF NOT EXISTS "insights_latest_by_bundle" ("platform" varchar(16) COLLATE "C" NOT NULL, "channel" text COLLATE "C" NOT NULL, "bundle_field" varchar(16) COLLATE "C" NOT NULL, "bundle_id" varchar(36) COLLATE "C" NOT NULL, "type" varchar(32) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "installations" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("platform", "channel", "bundle_field", "bundle_id", "type", "bucket_start_ms", "_shard"));
62
+
63
+ CREATE TABLE IF NOT EXISTS "insights_outcomes" ("platform" varchar(16) COLLATE "C" NOT NULL, "channel" text COLLATE "C" NOT NULL, "type" varchar(32) COLLATE "C" NOT NULL, "bundle_ref" varchar(41) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "events" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("platform", "channel", "type", "bundle_ref", "bucket_start_ms", "_shard"));
64
+
65
+ CREATE TABLE IF NOT EXISTS "api_keys" ("id" varchar(255) COLLATE "C" NOT NULL, "hash" varchar(64) COLLATE "C" NOT NULL, "name" varchar(64) COLLATE "C" NOT NULL, "prefix" varchar(16) COLLATE "C" NOT NULL, "role" varchar(16) COLLATE "C" NOT NULL, "created_at_ms" bigint NOT NULL, "revoked_at_ms" bigint, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("id"));
66
+
67
+ CREATE INDEX IF NOT EXISTS "api_keys_byCreated" ON "api_keys" ("created_at_ms", "id");
68
+
69
+ CREATE UNIQUE INDEX IF NOT EXISTS "api_keys_hash" ON "api_keys" ("hash");
70
+
71
+ CREATE TABLE IF NOT EXISTS "private_hot_updater_settings" ("key" varchar(255) COLLATE "C" NOT NULL, "value" varchar(255) COLLATE "C" NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("key"));
72
+
73
+ INSERT INTO "private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.engine', '1', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value";
74
+
75
+ INSERT INTO "private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.core', '1.0.0', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value";
76
+
77
+ INSERT INTO "private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.insights', '1.0.0', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value";
78
+
79
+ INSERT INTO "private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.apiKeys', '1.0.0', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value";