@hot-updater/postgres 1.0.0-rc.0 → 1.0.0-rc.2
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 +50 -3
- package/dist/index.d.cts +1 -3
- package/dist/index.d.mts +1 -3
- package/dist/index.mjs +48 -1
- package/package.json +3 -3
- package/sql/bundles.sql +31 -11
package/dist/index.cjs
CHANGED
|
@@ -16,7 +16,7 @@ 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));
|
|
@@ -25,7 +25,7 @@ let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
|
25
25
|
let _hot_updater_plugin_core_internal = require("@hot-updater/plugin-core/internal");
|
|
26
26
|
let kysely = require("kysely");
|
|
27
27
|
let pg = require("pg");
|
|
28
|
-
pg = __toESM(pg);
|
|
28
|
+
pg = __toESM(pg, 1);
|
|
29
29
|
//#region src/postgresQuery.ts
|
|
30
30
|
const applyOrder = (order, clause) => {
|
|
31
31
|
const directed = clause.direction === "asc" ? order.asc() : order.desc();
|
|
@@ -51,6 +51,13 @@ const countPostgresRows = async (db, input, where) => {
|
|
|
51
51
|
const query = rows.select(({ fn }) => fn.countAll().as("count"));
|
|
52
52
|
return Number((await query.executeTakeFirstOrThrow()).count);
|
|
53
53
|
}
|
|
54
|
+
case "bundle_events":
|
|
55
|
+
case "bundle_installations": {
|
|
56
|
+
let rows = db.selectFrom(input.model);
|
|
57
|
+
if (where !== void 0) rows = rows.where(where);
|
|
58
|
+
const query = rows.select(({ fn }) => fn.countAll().as("count"));
|
|
59
|
+
return Number((await query.executeTakeFirstOrThrow()).count);
|
|
60
|
+
}
|
|
54
61
|
}
|
|
55
62
|
};
|
|
56
63
|
const findManyPostgresRows = async (db, input, where) => {
|
|
@@ -75,6 +82,12 @@ const findManyPostgresRows = async (db, input, where) => {
|
|
|
75
82
|
for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
|
|
76
83
|
return query.limit(input.limit).offset(input.offset).execute();
|
|
77
84
|
}
|
|
85
|
+
case "bundle_installations": {
|
|
86
|
+
let query = db.selectFrom("bundle_installations").selectAll();
|
|
87
|
+
if (where !== void 0) query = query.where(where);
|
|
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
|
+
}
|
|
78
91
|
case "channels": {
|
|
79
92
|
let query = db.selectFrom("channels").selectAll();
|
|
80
93
|
if (where !== void 0) query = query.where(where);
|
|
@@ -152,11 +165,31 @@ const buildWhere = (where) => {
|
|
|
152
165
|
return expression;
|
|
153
166
|
};
|
|
154
167
|
const createPostgresImplementation = (db) => ({
|
|
168
|
+
async recordInsights({ event, installation }) {
|
|
169
|
+
const eventColumns = Object.keys(event);
|
|
170
|
+
const installationColumns = Object.keys(installation);
|
|
171
|
+
await kysely.sql`
|
|
172
|
+
WITH accepted_event AS (
|
|
173
|
+
INSERT INTO bundle_events (${kysely.sql.join(eventColumns.map(kysely.sql.ref))})
|
|
174
|
+
VALUES (${kysely.sql.join(Object.values(event))})
|
|
175
|
+
ON CONFLICT (id) DO NOTHING RETURNING id
|
|
176
|
+
)
|
|
177
|
+
INSERT INTO bundle_installations (${kysely.sql.join(installationColumns.map(kysely.sql.ref))})
|
|
178
|
+
SELECT ${kysely.sql.join(Object.values(installation))} FROM accepted_event
|
|
179
|
+
ON CONFLICT (install_id) DO UPDATE SET ${kysely.sql.join(installationColumns.filter((column) => column !== "install_id").map((column) => kysely.sql`${kysely.sql.ref(column)} = ${kysely.sql.ref(`excluded.${column}`)}`))}
|
|
180
|
+
WHERE (excluded.received_at_ms, excluded.id) >
|
|
181
|
+
(bundle_installations.received_at_ms, bundle_installations.id)
|
|
182
|
+
`.execute(db);
|
|
183
|
+
},
|
|
155
184
|
async create(input) {
|
|
156
185
|
switch (input.model) {
|
|
157
186
|
case "bundles": return db.insertInto("bundles").values(input.data).returningAll().executeTakeFirstOrThrow();
|
|
158
187
|
case "bundle_patches": return db.insertInto("bundle_patches").values(input.data).returningAll().executeTakeFirstOrThrow();
|
|
159
188
|
case "bundle_events": return db.insertInto("bundle_events").values(input.data).returningAll().executeTakeFirstOrThrow();
|
|
189
|
+
case "bundle_installations": {
|
|
190
|
+
const query = db.insertInto("bundle_installations").values(input.data);
|
|
191
|
+
return await (input.onConflict === "ignore" ? query.onConflict((conflict) => conflict.column("install_id").doNothing()) : query).returningAll().executeTakeFirst() ?? await db.selectFrom("bundle_installations").selectAll().where("install_id", "=", input.data.install_id).executeTakeFirstOrThrow();
|
|
192
|
+
}
|
|
160
193
|
case "releases": return db.insertInto("releases").values(input.data).returningAll().executeTakeFirstOrThrow();
|
|
161
194
|
case "release_catalogs": return db.insertInto("release_catalogs").values(input.data).returningAll().executeTakeFirstOrThrow();
|
|
162
195
|
case "channels": {
|
|
@@ -183,6 +216,11 @@ const createPostgresImplementation = (db) => ({
|
|
|
183
216
|
if (where !== void 0) query = query.where(where);
|
|
184
217
|
return await query.returningAll().executeTakeFirst() ?? null;
|
|
185
218
|
}
|
|
219
|
+
if (input.model === "bundle_installations") {
|
|
220
|
+
let query = db.updateTable("bundle_installations").set(input.update);
|
|
221
|
+
if (where !== void 0) query = query.where(where);
|
|
222
|
+
return await query.returningAll().executeTakeFirst() ?? null;
|
|
223
|
+
}
|
|
186
224
|
let query = db.updateTable("bundles").set(input.update);
|
|
187
225
|
if (where !== void 0) query = query.where(where);
|
|
188
226
|
return await query.returningAll().executeTakeFirst() ?? null;
|
|
@@ -253,6 +291,11 @@ const createPostgresImplementation = (db) => ({
|
|
|
253
291
|
if (where !== void 0) query = query.where(where);
|
|
254
292
|
return await query.executeTakeFirst() ?? null;
|
|
255
293
|
}
|
|
294
|
+
case "bundle_installations": {
|
|
295
|
+
let query = db.selectFrom("bundle_installations").selectAll();
|
|
296
|
+
if (where !== void 0) query = query.where(where);
|
|
297
|
+
return await query.executeTakeFirst() ?? null;
|
|
298
|
+
}
|
|
256
299
|
}
|
|
257
300
|
},
|
|
258
301
|
findMany: (input) => findManyPostgresRows(db, input, buildWhere(input.where)),
|
|
@@ -286,7 +329,11 @@ const createPostgresImplementation = (db) => ({
|
|
|
286
329
|
});
|
|
287
330
|
const postgres = (config) => {
|
|
288
331
|
const { dialect, ...poolConfig } = config;
|
|
289
|
-
const
|
|
332
|
+
const implementation = dialect !== void 0 ? createPostgresImplementation(new kysely.Kysely({ dialect })) : (() => {
|
|
333
|
+
const pool = new Pool(poolConfig);
|
|
334
|
+
return createPostgresImplementation(new kysely.Kysely({ dialect: new kysely.PostgresDialect({ pool }) }));
|
|
335
|
+
})();
|
|
336
|
+
const adapter = (0, _hot_updater_plugin_core_internal.createDatabasePluginAdapter)("postgres", implementation);
|
|
290
337
|
return (0, _hot_updater_plugin_core.createDatabasePlugin)({
|
|
291
338
|
name: "postgres",
|
|
292
339
|
models: adapter.models,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
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
|
-
|
|
5
3
|
//#region src/postgres.d.ts
|
|
6
4
|
type PostgresConfig = PoolConfig & {
|
|
7
5
|
readonly dialect?: Dialect;
|
|
8
6
|
};
|
|
9
|
-
declare const postgres: (config: PostgresConfig) =>
|
|
7
|
+
declare const postgres: (config: PostgresConfig) => import("@hot-updater/plugin-core").DatabasePlugin;
|
|
10
8
|
//#endregion
|
|
11
9
|
export { PostgresConfig, postgres };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
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
|
-
|
|
5
3
|
//#region src/postgres.d.ts
|
|
6
4
|
type PostgresConfig = PoolConfig & {
|
|
7
5
|
readonly dialect?: Dialect;
|
|
8
6
|
};
|
|
9
|
-
declare const postgres: (config: PostgresConfig) =>
|
|
7
|
+
declare const postgres: (config: PostgresConfig) => import("@hot-updater/plugin-core").DatabasePlugin;
|
|
10
8
|
//#endregion
|
|
11
9
|
export { PostgresConfig, postgres };
|
package/dist/index.mjs
CHANGED
|
@@ -27,6 +27,13 @@ const countPostgresRows = async (db, input, where) => {
|
|
|
27
27
|
const query = rows.select(({ fn }) => fn.countAll().as("count"));
|
|
28
28
|
return Number((await query.executeTakeFirstOrThrow()).count);
|
|
29
29
|
}
|
|
30
|
+
case "bundle_events":
|
|
31
|
+
case "bundle_installations": {
|
|
32
|
+
let rows = db.selectFrom(input.model);
|
|
33
|
+
if (where !== void 0) rows = rows.where(where);
|
|
34
|
+
const query = rows.select(({ fn }) => fn.countAll().as("count"));
|
|
35
|
+
return Number((await query.executeTakeFirstOrThrow()).count);
|
|
36
|
+
}
|
|
30
37
|
}
|
|
31
38
|
};
|
|
32
39
|
const findManyPostgresRows = async (db, input, where) => {
|
|
@@ -51,6 +58,12 @@ const findManyPostgresRows = async (db, input, where) => {
|
|
|
51
58
|
for (const clause of input.orderBy ?? []) query = query.orderBy(clause.field, (order) => applyOrder(order, clause));
|
|
52
59
|
return query.limit(input.limit).offset(input.offset).execute();
|
|
53
60
|
}
|
|
61
|
+
case "bundle_installations": {
|
|
62
|
+
let query = db.selectFrom("bundle_installations").selectAll();
|
|
63
|
+
if (where !== void 0) query = query.where(where);
|
|
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
|
+
}
|
|
54
67
|
case "channels": {
|
|
55
68
|
let query = db.selectFrom("channels").selectAll();
|
|
56
69
|
if (where !== void 0) query = query.where(where);
|
|
@@ -128,11 +141,31 @@ const buildWhere = (where) => {
|
|
|
128
141
|
return expression;
|
|
129
142
|
};
|
|
130
143
|
const createPostgresImplementation = (db) => ({
|
|
144
|
+
async recordInsights({ event, installation }) {
|
|
145
|
+
const eventColumns = Object.keys(event);
|
|
146
|
+
const installationColumns = Object.keys(installation);
|
|
147
|
+
await sql`
|
|
148
|
+
WITH accepted_event AS (
|
|
149
|
+
INSERT INTO bundle_events (${sql.join(eventColumns.map(sql.ref))})
|
|
150
|
+
VALUES (${sql.join(Object.values(event))})
|
|
151
|
+
ON CONFLICT (id) DO NOTHING RETURNING id
|
|
152
|
+
)
|
|
153
|
+
INSERT INTO bundle_installations (${sql.join(installationColumns.map(sql.ref))})
|
|
154
|
+
SELECT ${sql.join(Object.values(installation))} FROM accepted_event
|
|
155
|
+
ON CONFLICT (install_id) DO UPDATE SET ${sql.join(installationColumns.filter((column) => column !== "install_id").map((column) => sql`${sql.ref(column)} = ${sql.ref(`excluded.${column}`)}`))}
|
|
156
|
+
WHERE (excluded.received_at_ms, excluded.id) >
|
|
157
|
+
(bundle_installations.received_at_ms, bundle_installations.id)
|
|
158
|
+
`.execute(db);
|
|
159
|
+
},
|
|
131
160
|
async create(input) {
|
|
132
161
|
switch (input.model) {
|
|
133
162
|
case "bundles": return db.insertInto("bundles").values(input.data).returningAll().executeTakeFirstOrThrow();
|
|
134
163
|
case "bundle_patches": return db.insertInto("bundle_patches").values(input.data).returningAll().executeTakeFirstOrThrow();
|
|
135
164
|
case "bundle_events": return db.insertInto("bundle_events").values(input.data).returningAll().executeTakeFirstOrThrow();
|
|
165
|
+
case "bundle_installations": {
|
|
166
|
+
const query = db.insertInto("bundle_installations").values(input.data);
|
|
167
|
+
return await (input.onConflict === "ignore" ? query.onConflict((conflict) => conflict.column("install_id").doNothing()) : query).returningAll().executeTakeFirst() ?? await db.selectFrom("bundle_installations").selectAll().where("install_id", "=", input.data.install_id).executeTakeFirstOrThrow();
|
|
168
|
+
}
|
|
136
169
|
case "releases": return db.insertInto("releases").values(input.data).returningAll().executeTakeFirstOrThrow();
|
|
137
170
|
case "release_catalogs": return db.insertInto("release_catalogs").values(input.data).returningAll().executeTakeFirstOrThrow();
|
|
138
171
|
case "channels": {
|
|
@@ -159,6 +192,11 @@ const createPostgresImplementation = (db) => ({
|
|
|
159
192
|
if (where !== void 0) query = query.where(where);
|
|
160
193
|
return await query.returningAll().executeTakeFirst() ?? null;
|
|
161
194
|
}
|
|
195
|
+
if (input.model === "bundle_installations") {
|
|
196
|
+
let query = db.updateTable("bundle_installations").set(input.update);
|
|
197
|
+
if (where !== void 0) query = query.where(where);
|
|
198
|
+
return await query.returningAll().executeTakeFirst() ?? null;
|
|
199
|
+
}
|
|
162
200
|
let query = db.updateTable("bundles").set(input.update);
|
|
163
201
|
if (where !== void 0) query = query.where(where);
|
|
164
202
|
return await query.returningAll().executeTakeFirst() ?? null;
|
|
@@ -229,6 +267,11 @@ const createPostgresImplementation = (db) => ({
|
|
|
229
267
|
if (where !== void 0) query = query.where(where);
|
|
230
268
|
return await query.executeTakeFirst() ?? null;
|
|
231
269
|
}
|
|
270
|
+
case "bundle_installations": {
|
|
271
|
+
let query = db.selectFrom("bundle_installations").selectAll();
|
|
272
|
+
if (where !== void 0) query = query.where(where);
|
|
273
|
+
return await query.executeTakeFirst() ?? null;
|
|
274
|
+
}
|
|
232
275
|
}
|
|
233
276
|
},
|
|
234
277
|
findMany: (input) => findManyPostgresRows(db, input, buildWhere(input.where)),
|
|
@@ -262,7 +305,11 @@ const createPostgresImplementation = (db) => ({
|
|
|
262
305
|
});
|
|
263
306
|
const postgres = (config) => {
|
|
264
307
|
const { dialect, ...poolConfig } = config;
|
|
265
|
-
const
|
|
308
|
+
const implementation = dialect !== void 0 ? createPostgresImplementation(new Kysely({ dialect })) : (() => {
|
|
309
|
+
const pool = new Pool(poolConfig);
|
|
310
|
+
return createPostgresImplementation(new Kysely({ dialect: new PostgresDialect({ pool }) }));
|
|
311
|
+
})();
|
|
312
|
+
const adapter = createDatabasePluginAdapter("postgres", implementation);
|
|
266
313
|
return createDatabasePlugin({
|
|
267
314
|
name: "postgres",
|
|
268
315
|
models: adapter.models,
|
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.
|
|
4
|
+
"version": "1.0.0-rc.2",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.mjs",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"kysely": "0.28.17",
|
|
36
36
|
"pg": "8.13.1",
|
|
37
37
|
"@hot-updater/core": "1.0.0-rc.0",
|
|
38
|
-
"@hot-updater/plugin-core": "1.0.0-rc.
|
|
38
|
+
"@hot-updater/plugin-core": "1.0.0-rc.2"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@electric-sql/pglite": "0.4.1",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"camelcase-keys": "^9.1.3",
|
|
45
45
|
"kysely-pglite-dialect": "1.2.0",
|
|
46
46
|
"pg-minify": "^1.6.5",
|
|
47
|
-
"@hot-updater/test-utils": "1.0.0-rc.
|
|
47
|
+
"@hot-updater/test-utils": "1.0.0-rc.2"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsdown",
|
package/sql/bundles.sql
CHANGED
|
@@ -70,16 +70,16 @@ create table release_catalogs (
|
|
|
70
70
|
create table bundle_events (
|
|
71
71
|
id uuid primary key not null,
|
|
72
72
|
type text not null,
|
|
73
|
-
install_id text not null,
|
|
74
|
-
user_id text,
|
|
73
|
+
install_id text collate "C" not null,
|
|
74
|
+
user_id text collate "C",
|
|
75
75
|
username text,
|
|
76
76
|
from_release_id uuid,
|
|
77
77
|
from_bundle_id uuid,
|
|
78
78
|
to_release_id uuid,
|
|
79
79
|
to_bundle_id uuid not null,
|
|
80
|
-
platform text not null,
|
|
80
|
+
platform text collate "C" not null,
|
|
81
81
|
app_version text not null,
|
|
82
|
-
channel text not null,
|
|
82
|
+
channel text collate "C" not null,
|
|
83
83
|
cohort text not null,
|
|
84
84
|
update_strategy text,
|
|
85
85
|
fingerprint_hash text,
|
|
@@ -87,6 +87,20 @@ create table bundle_events (
|
|
|
87
87
|
received_at_ms double precision not null
|
|
88
88
|
);
|
|
89
89
|
|
|
90
|
+
create table bundle_installations (
|
|
91
|
+
install_id text collate "C" primary key not null,
|
|
92
|
+
id uuid not null,
|
|
93
|
+
user_id text collate "C",
|
|
94
|
+
username text,
|
|
95
|
+
to_bundle_id uuid not null,
|
|
96
|
+
type text not null,
|
|
97
|
+
platform text collate "C" not null,
|
|
98
|
+
app_version text not null,
|
|
99
|
+
channel text collate "C" not null,
|
|
100
|
+
cohort text not null,
|
|
101
|
+
received_at_ms double precision not null
|
|
102
|
+
);
|
|
103
|
+
|
|
90
104
|
create table api_keys (
|
|
91
105
|
id varchar(255) primary key not null,
|
|
92
106
|
hash text not null,
|
|
@@ -112,13 +126,13 @@ create index releases_fingerprint_hash_idx on releases(fingerprint_hash);
|
|
|
112
126
|
create index releases_enabled_idx on releases(enabled);
|
|
113
127
|
create index release_catalogs_channel_idx on release_catalogs(channel_id);
|
|
114
128
|
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
|
|
117
|
-
create index
|
|
118
|
-
create index
|
|
119
|
-
create index
|
|
120
|
-
create index
|
|
121
|
-
create index
|
|
129
|
+
create index bundle_events_install_idx on bundle_events(install_id, type, received_at_ms, id);
|
|
130
|
+
create index bundle_installations_user_id_idx on bundle_installations(user_id, install_id);
|
|
131
|
+
create index bundle_installations_received_at_idx on bundle_installations(received_at_ms);
|
|
132
|
+
create index bundle_events_from_bundle_idx on bundle_events(type, platform, channel, from_bundle_id, received_at_ms, id);
|
|
133
|
+
create index bundle_events_to_bundle_idx on bundle_events(type, platform, channel, to_bundle_id, received_at_ms, id);
|
|
134
|
+
create index bundle_installations_scope_idx on bundle_installations(platform, channel, received_at_ms);
|
|
135
|
+
create index bundle_installations_bundle_idx on bundle_installations(platform, channel, to_bundle_id, received_at_ms);
|
|
122
136
|
create unique index api_keys_hash_key on api_keys(hash);
|
|
123
137
|
create index api_keys_created_at_idx on api_keys(created_at_ms, id);
|
|
124
138
|
|
|
@@ -154,6 +168,12 @@ alter table bundle_events add constraint bundle_events_shape_check
|
|
|
154
168
|
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
169
|
alter table bundle_events add constraint bundle_events_received_at_check
|
|
156
170
|
check (received_at_ms >= 0);
|
|
171
|
+
alter table bundle_installations add constraint bundle_installations_type_check
|
|
172
|
+
check (type in ('UPDATE_APPLIED', 'RECOVERED', 'RELEASE_ADOPTED', 'UNCHANGED'));
|
|
173
|
+
alter table bundle_installations add constraint bundle_installations_platform_check
|
|
174
|
+
check (platform in ('ios', 'android'));
|
|
175
|
+
alter table bundle_installations add constraint bundle_installations_received_at_check
|
|
176
|
+
check (received_at_ms >= 0);
|
|
157
177
|
alter table api_keys add constraint api_keys_role_check
|
|
158
178
|
check (role = 'client');
|
|
159
179
|
alter table api_keys add constraint api_keys_created_at_check
|