@hot-updater/postgres 0.17.0 → 0.18.1

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/postgres",
3
3
  "type": "module",
4
- "version": "0.17.0",
4
+ "version": "0.18.1",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.js",
@@ -31,18 +31,18 @@
31
31
  "dependencies": {
32
32
  "kysely": "^0.27.5",
33
33
  "pg": "^8.13.1",
34
- "@hot-updater/plugin-core": "0.17.0",
35
- "@hot-updater/core": "0.17.0"
34
+ "@hot-updater/plugin-core": "0.18.1",
35
+ "@hot-updater/core": "0.18.1"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@electric-sql/pglite": "^0.2.15",
39
39
  "@types/pg": "^8.11.10",
40
40
  "camelcase-keys": "^9.1.3",
41
41
  "pg-minify": "^1.6.5",
42
- "@hot-updater/js": "0.17.0"
42
+ "@hot-updater/js": "0.18.1"
43
43
  },
44
44
  "scripts": {
45
- "build": "rslib build",
45
+ "build": "tsdown",
46
46
  "test:type": "tsc --noEmit"
47
47
  }
48
48
  }
package/sql/bundles.sql CHANGED
@@ -5,13 +5,21 @@ CREATE TYPE platforms AS ENUM ('ios', 'android');
5
5
  CREATE TABLE bundles (
6
6
  id uuid PRIMARY KEY,
7
7
  platform platforms NOT NULL,
8
- target_app_version text NOT NULL,
9
8
  should_force_update boolean NOT NULL,
10
9
  enabled boolean NOT NULL,
11
10
  file_hash text NOT NULL,
12
11
  git_commit_hash text,
13
12
  message text,
14
- channel text NOT NULL DEFAULT 'production'
13
+ channel text NOT NULL DEFAULT 'production',
14
+ storage_uri text NOT NULL,
15
+ target_app_version text,
16
+ fingerprint_hash text,
17
+ CONSTRAINT check_version_or_fingerprint CHECK (
18
+ (target_app_version IS NOT NULL) OR (fingerprint_hash IS NOT NULL)
19
+ ),
20
+ metadata jsonb DEFAULT '{}'::jsonb
15
21
  );
16
22
 
17
23
  CREATE INDEX bundles_target_app_version_idx ON bundles(target_app_version);
24
+ CREATE INDEX bundles_fingerprint_hash_idx ON bundles(fingerprint_hash);
25
+ CREATE INDEX bundles_channel_idx ON bundles(channel);
@@ -15,7 +15,7 @@ const createInsertBundleQuery = (bundle: Bundle) => {
15
15
  return `
16
16
  INSERT INTO bundles (
17
17
  id, file_hash, platform, target_app_version,
18
- should_force_update, enabled, git_commit_hash, message, channel
18
+ should_force_update, enabled, git_commit_hash, message, channel, storage_uri, fingerprint_hash
19
19
  ) VALUES (
20
20
  '${bundle.id}',
21
21
  '${bundle.fileHash}',
@@ -25,7 +25,9 @@ const createInsertBundleQuery = (bundle: Bundle) => {
25
25
  ${bundle.enabled},
26
26
  ${bundle.gitCommitHash ? `'${bundle.gitCommitHash}'` : "null"},
27
27
  ${bundle.message ? `'${bundle.message}'` : "null"},
28
- '${bundle.channel}'
28
+ '${bundle.channel}',
29
+ '${bundle.storageUri}',
30
+ '${bundle.fingerprintHash}'
29
31
  );
30
32
  `;
31
33
  };
@@ -34,16 +36,42 @@ const createGetUpdateInfo =
34
36
  (db: PGlite) =>
35
37
  async (
36
38
  bundles: Bundle[],
37
- {
38
- appVersion,
39
+ args: GetBundlesArgs,
40
+ ): Promise<UpdateInfo | null> => {
41
+ const {
39
42
  bundleId,
40
43
  platform,
41
44
  minBundleId = NIL_UUID,
42
45
  channel = "production",
43
- }: GetBundlesArgs,
44
- ): Promise<UpdateInfo | null> => {
46
+ _updateStrategy,
47
+ } = args;
45
48
  await db.exec(createInsertBundleQuerys(bundles));
46
49
 
50
+ if (_updateStrategy === "fingerprint") {
51
+ const fingerprintHash = args.fingerprintHash;
52
+ const result = await db.query<{
53
+ id: string;
54
+ should_force_update: boolean;
55
+ message: string;
56
+ status: string;
57
+ }>(
58
+ `
59
+ SELECT * FROM get_update_info_by_fingerprint_hash(
60
+ '${platform}',
61
+ '${bundleId}',
62
+ '${minBundleId}',
63
+ '${channel}',
64
+ '${fingerprintHash}'
65
+ );
66
+ `,
67
+ );
68
+
69
+ return result.rows[0]
70
+ ? (camelcaseKeys(result.rows[0]) as UpdateInfo)
71
+ : null;
72
+ }
73
+
74
+ const appVersion = args.appVersion;
47
75
  const { rows: appVersionList } = await db.query<{
48
76
  target_app_version: string;
49
77
  }>(
@@ -64,7 +92,7 @@ const createGetUpdateInfo =
64
92
  status: string;
65
93
  }>(
66
94
  `
67
- SELECT * FROM get_update_info(
95
+ SELECT * FROM get_update_info_by_app_version(
68
96
  '${platform}',
69
97
  '${appVersion}',
70
98
  '${bundleId}',
@@ -1,6 +1,6 @@
1
1
  -- HotUpdater.get_update_info
2
2
 
3
- CREATE OR REPLACE FUNCTION get_update_info (
3
+ CREATE OR REPLACE FUNCTION get_update_info_by_app_version (
4
4
  app_platform platforms,
5
5
  app_version text,
6
6
  bundle_id uuid,
@@ -12,7 +12,8 @@ RETURNS TABLE (
12
12
  id uuid,
13
13
  should_force_update boolean,
14
14
  message text,
15
- status text
15
+ status text,
16
+ storage_uri text
16
17
  )
17
18
  LANGUAGE plpgsql
18
19
  AS
@@ -26,7 +27,8 @@ BEGIN
26
27
  b.id,
27
28
  b.should_force_update,
28
29
  b.message,
29
- 'UPDATE' AS status
30
+ 'UPDATE' AS status,
31
+ b.storage_uri
30
32
  FROM bundles b
31
33
  WHERE b.enabled = TRUE
32
34
  AND b.platform = app_platform
@@ -42,7 +44,8 @@ BEGIN
42
44
  b.id,
43
45
  TRUE AS should_force_update,
44
46
  b.message,
45
- 'ROLLBACK' AS status
47
+ 'ROLLBACK' AS status,
48
+ b.storage_uri
46
49
  FROM bundles b
47
50
  WHERE b.enabled = TRUE
48
51
  AND b.platform = app_platform
@@ -67,7 +70,8 @@ BEGIN
67
70
  NIL_UUID AS id,
68
71
  TRUE AS should_force_update,
69
72
  NULL AS message,
70
- 'ROLLBACK' AS status
73
+ 'ROLLBACK' AS status,
74
+ NULL AS storage_uri
71
75
  WHERE (SELECT COUNT(*) FROM final_result) = 0
72
76
  AND bundle_id != NIL_UUID
73
77
  AND bundle_id > min_bundle_id
@@ -0,0 +1,87 @@
1
+ -- HotUpdater.get_update_info
2
+
3
+ CREATE OR REPLACE FUNCTION get_update_info_by_fingerprint_hash (
4
+ app_platform platforms,
5
+ bundle_id uuid,
6
+ min_bundle_id uuid,
7
+ target_channel text,
8
+ target_fingerprint_hash text
9
+ )
10
+ RETURNS TABLE (
11
+ id uuid,
12
+ should_force_update boolean,
13
+ message text,
14
+ status text,
15
+ storage_uri text
16
+ )
17
+ LANGUAGE plpgsql
18
+ AS
19
+ $$
20
+ DECLARE
21
+ NIL_UUID CONSTANT uuid := '00000000-0000-0000-0000-000000000000';
22
+ BEGIN
23
+ RETURN QUERY
24
+ WITH update_candidate AS (
25
+ SELECT
26
+ b.id,
27
+ b.should_force_update,
28
+ b.message,
29
+ 'UPDATE' AS status,
30
+ b.storage_uri
31
+ FROM bundles b
32
+ WHERE b.enabled = TRUE
33
+ AND b.platform = app_platform
34
+ AND b.id >= bundle_id
35
+ AND b.id > min_bundle_id
36
+ AND b.channel = target_channel
37
+ AND b.fingerprint_hash = target_fingerprint_hash
38
+ ORDER BY b.id DESC
39
+ LIMIT 1
40
+ ),
41
+ rollback_candidate AS (
42
+ SELECT
43
+ b.id,
44
+ TRUE AS should_force_update,
45
+ b.message,
46
+ 'ROLLBACK' AS status,
47
+ b.storage_uri
48
+ FROM bundles b
49
+ WHERE b.enabled = TRUE
50
+ AND b.platform = app_platform
51
+ AND b.id < bundle_id
52
+ AND b.id > min_bundle_id
53
+ AND b.channel = target_channel
54
+ AND b.fingerprint_hash = target_fingerprint_hash
55
+ ORDER BY b.id DESC
56
+ LIMIT 1
57
+ ),
58
+ final_result AS (
59
+ SELECT * FROM update_candidate
60
+ UNION ALL
61
+ SELECT * FROM rollback_candidate
62
+ WHERE NOT EXISTS (SELECT 1 FROM update_candidate)
63
+ )
64
+ SELECT *
65
+ FROM final_result
66
+ WHERE final_result.id != bundle_id
67
+
68
+ UNION ALL
69
+
70
+ SELECT
71
+ NIL_UUID AS id,
72
+ TRUE AS should_force_update,
73
+ NULL AS message,
74
+ 'ROLLBACK' AS status,
75
+ NULL AS storage_uri
76
+ WHERE (SELECT COUNT(*) FROM final_result) = 0
77
+ AND bundle_id != NIL_UUID
78
+ AND bundle_id > min_bundle_id
79
+ AND NOT EXISTS (
80
+ SELECT 1
81
+ FROM bundles b
82
+ WHERE b.id = bundle_id
83
+ AND b.enabled = TRUE
84
+ AND b.platform = app_platform
85
+ );
86
+ END;
87
+ $$;
@@ -1,3 +0,0 @@
1
- import { type GetBundlesArgs, type UpdateInfo } from "@hot-updater/core";
2
- import type pg from "pg";
3
- export declare const getUpdateInfo: (pool: pg.Pool, { platform, appVersion, bundleId, minBundleId, channel, }: GetBundlesArgs) => Promise<UpdateInfo | null>;
@@ -1,5 +0,0 @@
1
- import type { DatabasePluginHooks } from "@hot-updater/plugin-core";
2
- import { type PoolConfig } from "pg";
3
- export interface PostgresConfig extends PoolConfig {
4
- }
5
- export declare const postgres: (config: PostgresConfig, hooks?: DatabasePluginHooks) => (options: import("@hot-updater/plugin-core").BasePluginArgs) => import("@hot-updater/plugin-core").DatabasePlugin;
package/dist/types.d.ts DELETED
@@ -1,4 +0,0 @@
1
- import type { SnakeCaseBundle } from "@hot-updater/core";
2
- export interface Database {
3
- bundles: SnakeCaseBundle;
4
- }