@hot-updater/cloudflare 1.1.4 → 1.2.0-rc.0
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 +49 -29
- package/dist/index.d.cts +11 -6
- package/dist/index.d.mts +11 -6
- package/dist/index.mjs +49 -29
- package/dist/init/index.cjs +49 -29
- package/dist/init/index.mjs +49 -29
- package/package.json +6 -6
- package/sql/bundles.sql +11 -1
- package/src/d1Database.ts +29 -69
- package/src/d1Rest.ts +54 -0
- package/src/d1Schema.ts +33 -3
- package/worker/dist/README.md +1 -1
- package/worker/dist/index.js +865 -425
- package/worker/dist/index.js.map +4 -4
- package/worker/migrations/0002_hot-updater_1.2.0.sql +21 -0
package/dist/index.cjs
CHANGED
|
@@ -75,31 +75,19 @@ const createD1Database = (runner) => {
|
|
|
75
75
|
});
|
|
76
76
|
};
|
|
77
77
|
//#endregion
|
|
78
|
-
//#region src/
|
|
79
|
-
/**
|
|
80
|
-
* D1's schema: the guard table batch writes need, then the shared SQL
|
|
81
|
-
* schema of core and the target's plugins, settings last. Every statement can
|
|
82
|
-
* run again, so a migration for a server's plugin tables repeats core's.
|
|
83
|
-
*/
|
|
84
|
-
const d1SchemaStatements = ({ schema, settings } = _hot_updater_plugin_core.coreTarget) => [...(0, _hot_updater_plugin_core.createTableStatements)("sqlite", [_hot_updater_plugin_core.WRITE_GUARD_TABLE]), ...(0, _hot_updater_plugin_core.generateEngineSql)("sqlite", schema, settings)];
|
|
85
|
-
/** The schema as a D1 migration, which `wrangler d1 migrations apply` runs. */
|
|
86
|
-
const d1SchemaSql = (target) => `-- HotUpdater.schema\n\n${d1SchemaStatements(target).map((statement) => `${statement};`).join("\n\n")}\n`;
|
|
87
|
-
//#endregion
|
|
88
|
-
//#region src/d1Database.ts
|
|
78
|
+
//#region src/d1Rest.ts
|
|
89
79
|
/** The REST API binds text: each value goes as JSON and is read back with `json_extract`. */
|
|
90
80
|
const encode = ({ sql, params }) => ({
|
|
91
81
|
sql: sql.replaceAll("?", () => "json_extract(?, '$')"),
|
|
92
82
|
params: params.map((value) => JSON.stringify(value ?? null))
|
|
93
83
|
});
|
|
94
84
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* tables and the server's plugin tables to `migrations`, and
|
|
98
|
-
* `hot-updater db migrate` applies them through the API.
|
|
85
|
+
* Statements on a D1 database through the Cloudflare REST API: one at a
|
|
86
|
+
* time, or a batch D1 runs in one transaction.
|
|
99
87
|
*/
|
|
100
|
-
const
|
|
88
|
+
const d1RestRunner = (config) => {
|
|
101
89
|
const cloudflare$1 = new cloudflare.default({ apiToken: config.cloudflareApiToken });
|
|
102
|
-
const
|
|
90
|
+
const batch = async (statements) => {
|
|
103
91
|
const [first] = statements;
|
|
104
92
|
if (first === void 0) return [];
|
|
105
93
|
const body = statements.length === 1 ? {
|
|
@@ -116,21 +104,53 @@ const d1Database = (config) => {
|
|
|
116
104
|
return results;
|
|
117
105
|
};
|
|
118
106
|
return {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
batch: execute
|
|
122
|
-
}),
|
|
123
|
-
generateSchema: ((version, _name, target = _hot_updater_plugin_core.coreTarget) => {
|
|
124
|
-
if (version !== "latest" && version !== _hot_updater_plugin_core.coreSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
|
|
125
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
|
|
126
|
-
return {
|
|
127
|
-
code: d1SchemaSql(target),
|
|
128
|
-
path: `migrations/${timestamp}_hot-updater.sql`
|
|
129
|
-
};
|
|
130
|
-
})
|
|
107
|
+
query: async (statement) => (await batch([statement]))[0],
|
|
108
|
+
batch
|
|
131
109
|
};
|
|
132
110
|
};
|
|
133
111
|
//#endregion
|
|
112
|
+
//#region src/d1Schema.ts
|
|
113
|
+
/**
|
|
114
|
+
* D1's schema: the guard table batch writes need, then the shared SQL
|
|
115
|
+
* schema of core and the target's plugins, settings last. Every statement can
|
|
116
|
+
* run again, so a migration for a server's plugin tables repeats core's.
|
|
117
|
+
*/
|
|
118
|
+
const d1SchemaStatements = ({ schema, settings } = _hot_updater_plugin_core.coreTarget) => [...(0, _hot_updater_plugin_core.createTableStatements)("sqlite", [_hot_updater_plugin_core.WRITE_GUARD_TABLE]), ...(0, _hot_updater_plugin_core.generateEngineSql)("sqlite", schema, settings)];
|
|
119
|
+
const HEADER = "-- HotUpdater.schema\n\n";
|
|
120
|
+
const toSql = (statements) => `${HEADER}${statements.map((statement) => `${statement};`).join("\n\n")}\n`;
|
|
121
|
+
/** The schema as a D1 migration, which `wrangler d1 migrations apply` runs. */
|
|
122
|
+
const d1SchemaSql = (target) => toSql(d1SchemaStatements(target));
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/d1Database.ts
|
|
125
|
+
/**
|
|
126
|
+
* The resource this database reaches, without credentials, under the key
|
|
127
|
+
* `hot-updater doctor` reads to tell the database `hot-updater init` saved
|
|
128
|
+
* from another (a convention CLAUDE.md documents).
|
|
129
|
+
*/
|
|
130
|
+
const DATABASE_TARGET = Symbol.for("@hot-updater/database-target");
|
|
131
|
+
/** `target` under {@link DATABASE_TARGET}, to spread into the database. */
|
|
132
|
+
const marked = (target) => ({ [DATABASE_TARGET]: target });
|
|
133
|
+
/**
|
|
134
|
+
* Hot Updater's database on D1 through the Cloudflare REST API, for the CLI
|
|
135
|
+
* and console. `hot-updater db generate` writes a D1 migration of the built-in
|
|
136
|
+
* tables and the server's plugin tables to `migrations`, and
|
|
137
|
+
* `hot-updater db migrate` applies them through the API. The managed
|
|
138
|
+
* database migrates with the Worker's migrations when `hot-updater init` runs
|
|
139
|
+
* again.
|
|
140
|
+
*/
|
|
141
|
+
const d1Database = (config) => ({
|
|
142
|
+
...createD1Database(d1RestRunner(config)),
|
|
143
|
+
...marked(`d1/${config.databaseId}`),
|
|
144
|
+
generateSchema: ((version, _name, target = _hot_updater_plugin_core.coreTarget) => {
|
|
145
|
+
if (version !== "latest" && version !== _hot_updater_plugin_core.coreSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
|
|
146
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
|
|
147
|
+
return {
|
|
148
|
+
code: d1SchemaSql(target),
|
|
149
|
+
path: `migrations/${timestamp}_hot-updater.sql`
|
|
150
|
+
};
|
|
151
|
+
})
|
|
152
|
+
});
|
|
153
|
+
//#endregion
|
|
134
154
|
//#region src/r2S3Storage.ts
|
|
135
155
|
/** Devices download right after the update check that returns the URL. */
|
|
136
156
|
const PRESIGNED_URL_EXPIRES_IN_SECONDS = 3600;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { StorageAdapterWith, ToolingDatabase } from "@hot-updater/plugin-core";
|
|
2
2
|
import { S3ClientConfig } from "@aws-sdk/client-s3";
|
|
3
|
+
//#region src/d1Rest.d.ts
|
|
4
|
+
interface D1RestConfig {
|
|
5
|
+
readonly databaseId: string;
|
|
6
|
+
readonly accountId: string;
|
|
7
|
+
readonly cloudflareApiToken: string;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
3
10
|
//#region src/d1Executor.d.ts
|
|
4
11
|
declare class D1ExecutionError extends Error {
|
|
5
12
|
readonly name = "D1ExecutionError";
|
|
@@ -7,16 +14,14 @@ declare class D1ExecutionError extends Error {
|
|
|
7
14
|
}
|
|
8
15
|
//#endregion
|
|
9
16
|
//#region src/d1Database.d.ts
|
|
10
|
-
|
|
11
|
-
readonly databaseId: string;
|
|
12
|
-
readonly accountId: string;
|
|
13
|
-
readonly cloudflareApiToken: string;
|
|
14
|
-
}
|
|
17
|
+
type D1DatabaseConfig = D1RestConfig;
|
|
15
18
|
/**
|
|
16
19
|
* Hot Updater's database on D1 through the Cloudflare REST API, for the CLI
|
|
17
20
|
* and console. `hot-updater db generate` writes a D1 migration of the built-in
|
|
18
21
|
* tables and the server's plugin tables to `migrations`, and
|
|
19
|
-
* `hot-updater db migrate` applies them through the API.
|
|
22
|
+
* `hot-updater db migrate` applies them through the API. The managed
|
|
23
|
+
* database migrates with the Worker's migrations when `hot-updater init` runs
|
|
24
|
+
* again.
|
|
20
25
|
*/
|
|
21
26
|
declare const d1Database: (config: D1DatabaseConfig) => ToolingDatabase;
|
|
22
27
|
//#endregion
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { StorageAdapterWith, ToolingDatabase } from "@hot-updater/plugin-core";
|
|
2
2
|
import { S3ClientConfig } from "@aws-sdk/client-s3";
|
|
3
|
+
//#region src/d1Rest.d.ts
|
|
4
|
+
interface D1RestConfig {
|
|
5
|
+
readonly databaseId: string;
|
|
6
|
+
readonly accountId: string;
|
|
7
|
+
readonly cloudflareApiToken: string;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
3
10
|
//#region src/d1Executor.d.ts
|
|
4
11
|
declare class D1ExecutionError extends Error {
|
|
5
12
|
readonly name = "D1ExecutionError";
|
|
@@ -7,16 +14,14 @@ declare class D1ExecutionError extends Error {
|
|
|
7
14
|
}
|
|
8
15
|
//#endregion
|
|
9
16
|
//#region src/d1Database.d.ts
|
|
10
|
-
|
|
11
|
-
readonly databaseId: string;
|
|
12
|
-
readonly accountId: string;
|
|
13
|
-
readonly cloudflareApiToken: string;
|
|
14
|
-
}
|
|
17
|
+
type D1DatabaseConfig = D1RestConfig;
|
|
15
18
|
/**
|
|
16
19
|
* Hot Updater's database on D1 through the Cloudflare REST API, for the CLI
|
|
17
20
|
* and console. `hot-updater db generate` writes a D1 migration of the built-in
|
|
18
21
|
* tables and the server's plugin tables to `migrations`, and
|
|
19
|
-
* `hot-updater db migrate` applies them through the API.
|
|
22
|
+
* `hot-updater db migrate` applies them through the API. The managed
|
|
23
|
+
* database migrates with the Worker's migrations when `hot-updater init` runs
|
|
24
|
+
* again.
|
|
20
25
|
*/
|
|
21
26
|
declare const d1Database: (config: D1DatabaseConfig) => ToolingDatabase;
|
|
22
27
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -51,31 +51,19 @@ const createD1Database = (runner) => {
|
|
|
51
51
|
});
|
|
52
52
|
};
|
|
53
53
|
//#endregion
|
|
54
|
-
//#region src/
|
|
55
|
-
/**
|
|
56
|
-
* D1's schema: the guard table batch writes need, then the shared SQL
|
|
57
|
-
* schema of core and the target's plugins, settings last. Every statement can
|
|
58
|
-
* run again, so a migration for a server's plugin tables repeats core's.
|
|
59
|
-
*/
|
|
60
|
-
const d1SchemaStatements = ({ schema, settings } = coreTarget) => [...createTableStatements("sqlite", [WRITE_GUARD_TABLE]), ...generateEngineSql("sqlite", schema, settings)];
|
|
61
|
-
/** The schema as a D1 migration, which `wrangler d1 migrations apply` runs. */
|
|
62
|
-
const d1SchemaSql = (target) => `-- HotUpdater.schema\n\n${d1SchemaStatements(target).map((statement) => `${statement};`).join("\n\n")}\n`;
|
|
63
|
-
//#endregion
|
|
64
|
-
//#region src/d1Database.ts
|
|
54
|
+
//#region src/d1Rest.ts
|
|
65
55
|
/** The REST API binds text: each value goes as JSON and is read back with `json_extract`. */
|
|
66
56
|
const encode = ({ sql, params }) => ({
|
|
67
57
|
sql: sql.replaceAll("?", () => "json_extract(?, '$')"),
|
|
68
58
|
params: params.map((value) => JSON.stringify(value ?? null))
|
|
69
59
|
});
|
|
70
60
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* tables and the server's plugin tables to `migrations`, and
|
|
74
|
-
* `hot-updater db migrate` applies them through the API.
|
|
61
|
+
* Statements on a D1 database through the Cloudflare REST API: one at a
|
|
62
|
+
* time, or a batch D1 runs in one transaction.
|
|
75
63
|
*/
|
|
76
|
-
const
|
|
64
|
+
const d1RestRunner = (config) => {
|
|
77
65
|
const cloudflare = new Cloudflare({ apiToken: config.cloudflareApiToken });
|
|
78
|
-
const
|
|
66
|
+
const batch = async (statements) => {
|
|
79
67
|
const [first] = statements;
|
|
80
68
|
if (first === void 0) return [];
|
|
81
69
|
const body = statements.length === 1 ? {
|
|
@@ -92,21 +80,53 @@ const d1Database = (config) => {
|
|
|
92
80
|
return results;
|
|
93
81
|
};
|
|
94
82
|
return {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
batch: execute
|
|
98
|
-
}),
|
|
99
|
-
generateSchema: ((version, _name, target = coreTarget) => {
|
|
100
|
-
if (version !== "latest" && version !== coreSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
|
|
101
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
|
|
102
|
-
return {
|
|
103
|
-
code: d1SchemaSql(target),
|
|
104
|
-
path: `migrations/${timestamp}_hot-updater.sql`
|
|
105
|
-
};
|
|
106
|
-
})
|
|
83
|
+
query: async (statement) => (await batch([statement]))[0],
|
|
84
|
+
batch
|
|
107
85
|
};
|
|
108
86
|
};
|
|
109
87
|
//#endregion
|
|
88
|
+
//#region src/d1Schema.ts
|
|
89
|
+
/**
|
|
90
|
+
* D1's schema: the guard table batch writes need, then the shared SQL
|
|
91
|
+
* schema of core and the target's plugins, settings last. Every statement can
|
|
92
|
+
* run again, so a migration for a server's plugin tables repeats core's.
|
|
93
|
+
*/
|
|
94
|
+
const d1SchemaStatements = ({ schema, settings } = coreTarget) => [...createTableStatements("sqlite", [WRITE_GUARD_TABLE]), ...generateEngineSql("sqlite", schema, settings)];
|
|
95
|
+
const HEADER = "-- HotUpdater.schema\n\n";
|
|
96
|
+
const toSql = (statements) => `${HEADER}${statements.map((statement) => `${statement};`).join("\n\n")}\n`;
|
|
97
|
+
/** The schema as a D1 migration, which `wrangler d1 migrations apply` runs. */
|
|
98
|
+
const d1SchemaSql = (target) => toSql(d1SchemaStatements(target));
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/d1Database.ts
|
|
101
|
+
/**
|
|
102
|
+
* The resource this database reaches, without credentials, under the key
|
|
103
|
+
* `hot-updater doctor` reads to tell the database `hot-updater init` saved
|
|
104
|
+
* from another (a convention CLAUDE.md documents).
|
|
105
|
+
*/
|
|
106
|
+
const DATABASE_TARGET = Symbol.for("@hot-updater/database-target");
|
|
107
|
+
/** `target` under {@link DATABASE_TARGET}, to spread into the database. */
|
|
108
|
+
const marked = (target) => ({ [DATABASE_TARGET]: target });
|
|
109
|
+
/**
|
|
110
|
+
* Hot Updater's database on D1 through the Cloudflare REST API, for the CLI
|
|
111
|
+
* and console. `hot-updater db generate` writes a D1 migration of the built-in
|
|
112
|
+
* tables and the server's plugin tables to `migrations`, and
|
|
113
|
+
* `hot-updater db migrate` applies them through the API. The managed
|
|
114
|
+
* database migrates with the Worker's migrations when `hot-updater init` runs
|
|
115
|
+
* again.
|
|
116
|
+
*/
|
|
117
|
+
const d1Database = (config) => ({
|
|
118
|
+
...createD1Database(d1RestRunner(config)),
|
|
119
|
+
...marked(`d1/${config.databaseId}`),
|
|
120
|
+
generateSchema: ((version, _name, target = coreTarget) => {
|
|
121
|
+
if (version !== "latest" && version !== coreSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
|
|
122
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
|
|
123
|
+
return {
|
|
124
|
+
code: d1SchemaSql(target),
|
|
125
|
+
path: `migrations/${timestamp}_hot-updater.sql`
|
|
126
|
+
};
|
|
127
|
+
})
|
|
128
|
+
});
|
|
129
|
+
//#endregion
|
|
110
130
|
//#region src/r2S3Storage.ts
|
|
111
131
|
/** Devices download right after the update check that returns the URL. */
|
|
112
132
|
const PRESIGNED_URL_EXPIRES_IN_SECONDS = 3600;
|
package/dist/init/index.cjs
CHANGED
|
@@ -102,31 +102,19 @@ const createD1Database = (runner) => {
|
|
|
102
102
|
});
|
|
103
103
|
};
|
|
104
104
|
//#endregion
|
|
105
|
-
//#region src/
|
|
106
|
-
/**
|
|
107
|
-
* D1's schema: the guard table batch writes need, then the shared SQL
|
|
108
|
-
* schema of core and the target's plugins, settings last. Every statement can
|
|
109
|
-
* run again, so a migration for a server's plugin tables repeats core's.
|
|
110
|
-
*/
|
|
111
|
-
const d1SchemaStatements = ({ schema, settings } = _hot_updater_plugin_core.coreTarget) => [...(0, _hot_updater_plugin_core.createTableStatements)("sqlite", [_hot_updater_plugin_core.WRITE_GUARD_TABLE]), ...(0, _hot_updater_plugin_core.generateEngineSql)("sqlite", schema, settings)];
|
|
112
|
-
/** The schema as a D1 migration, which `wrangler d1 migrations apply` runs. */
|
|
113
|
-
const d1SchemaSql = (target) => `-- HotUpdater.schema\n\n${d1SchemaStatements(target).map((statement) => `${statement};`).join("\n\n")}\n`;
|
|
114
|
-
//#endregion
|
|
115
|
-
//#region src/d1Database.ts
|
|
105
|
+
//#region src/d1Rest.ts
|
|
116
106
|
/** The REST API binds text: each value goes as JSON and is read back with `json_extract`. */
|
|
117
107
|
const encode = ({ sql, params }) => ({
|
|
118
108
|
sql: sql.replaceAll("?", () => "json_extract(?, '$')"),
|
|
119
109
|
params: params.map((value) => JSON.stringify(value ?? null))
|
|
120
110
|
});
|
|
121
111
|
/**
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* tables and the server's plugin tables to `migrations`, and
|
|
125
|
-
* `hot-updater db migrate` applies them through the API.
|
|
112
|
+
* Statements on a D1 database through the Cloudflare REST API: one at a
|
|
113
|
+
* time, or a batch D1 runs in one transaction.
|
|
126
114
|
*/
|
|
127
|
-
const
|
|
115
|
+
const d1RestRunner = (config) => {
|
|
128
116
|
const cloudflare$1 = new cloudflare.default({ apiToken: config.cloudflareApiToken });
|
|
129
|
-
const
|
|
117
|
+
const batch = async (statements) => {
|
|
130
118
|
const [first] = statements;
|
|
131
119
|
if (first === void 0) return [];
|
|
132
120
|
const body = statements.length === 1 ? {
|
|
@@ -143,21 +131,53 @@ const d1Database = (config) => {
|
|
|
143
131
|
return results;
|
|
144
132
|
};
|
|
145
133
|
return {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
batch: execute
|
|
149
|
-
}),
|
|
150
|
-
generateSchema: ((version, _name, target = _hot_updater_plugin_core.coreTarget) => {
|
|
151
|
-
if (version !== "latest" && version !== _hot_updater_plugin_core.coreSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
|
|
152
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
|
|
153
|
-
return {
|
|
154
|
-
code: d1SchemaSql(target),
|
|
155
|
-
path: `migrations/${timestamp}_hot-updater.sql`
|
|
156
|
-
};
|
|
157
|
-
})
|
|
134
|
+
query: async (statement) => (await batch([statement]))[0],
|
|
135
|
+
batch
|
|
158
136
|
};
|
|
159
137
|
};
|
|
160
138
|
//#endregion
|
|
139
|
+
//#region src/d1Schema.ts
|
|
140
|
+
/**
|
|
141
|
+
* D1's schema: the guard table batch writes need, then the shared SQL
|
|
142
|
+
* schema of core and the target's plugins, settings last. Every statement can
|
|
143
|
+
* run again, so a migration for a server's plugin tables repeats core's.
|
|
144
|
+
*/
|
|
145
|
+
const d1SchemaStatements = ({ schema, settings } = _hot_updater_plugin_core.coreTarget) => [...(0, _hot_updater_plugin_core.createTableStatements)("sqlite", [_hot_updater_plugin_core.WRITE_GUARD_TABLE]), ...(0, _hot_updater_plugin_core.generateEngineSql)("sqlite", schema, settings)];
|
|
146
|
+
const HEADER = "-- HotUpdater.schema\n\n";
|
|
147
|
+
const toSql = (statements) => `${HEADER}${statements.map((statement) => `${statement};`).join("\n\n")}\n`;
|
|
148
|
+
/** The schema as a D1 migration, which `wrangler d1 migrations apply` runs. */
|
|
149
|
+
const d1SchemaSql = (target) => toSql(d1SchemaStatements(target));
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/d1Database.ts
|
|
152
|
+
/**
|
|
153
|
+
* The resource this database reaches, without credentials, under the key
|
|
154
|
+
* `hot-updater doctor` reads to tell the database `hot-updater init` saved
|
|
155
|
+
* from another (a convention CLAUDE.md documents).
|
|
156
|
+
*/
|
|
157
|
+
const DATABASE_TARGET = Symbol.for("@hot-updater/database-target");
|
|
158
|
+
/** `target` under {@link DATABASE_TARGET}, to spread into the database. */
|
|
159
|
+
const marked = (target) => ({ [DATABASE_TARGET]: target });
|
|
160
|
+
/**
|
|
161
|
+
* Hot Updater's database on D1 through the Cloudflare REST API, for the CLI
|
|
162
|
+
* and console. `hot-updater db generate` writes a D1 migration of the built-in
|
|
163
|
+
* tables and the server's plugin tables to `migrations`, and
|
|
164
|
+
* `hot-updater db migrate` applies them through the API. The managed
|
|
165
|
+
* database migrates with the Worker's migrations when `hot-updater init` runs
|
|
166
|
+
* again.
|
|
167
|
+
*/
|
|
168
|
+
const d1Database = (config) => ({
|
|
169
|
+
...createD1Database(d1RestRunner(config)),
|
|
170
|
+
...marked(`d1/${config.databaseId}`),
|
|
171
|
+
generateSchema: ((version, _name, target = _hot_updater_plugin_core.coreTarget) => {
|
|
172
|
+
if (version !== "latest" && version !== _hot_updater_plugin_core.coreSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
|
|
173
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
|
|
174
|
+
return {
|
|
175
|
+
code: d1SchemaSql(target),
|
|
176
|
+
path: `migrations/${timestamp}_hot-updater.sql`
|
|
177
|
+
};
|
|
178
|
+
})
|
|
179
|
+
});
|
|
180
|
+
//#endregion
|
|
161
181
|
//#region ../../node_modules/.pnpm/is-plain-obj@4.1.0/node_modules/is-plain-obj/index.js
|
|
162
182
|
function isPlainObject(value) {
|
|
163
183
|
if (typeof value !== "object" || value === null) return false;
|
package/dist/init/index.mjs
CHANGED
|
@@ -95,31 +95,19 @@ const createD1Database = (runner) => {
|
|
|
95
95
|
});
|
|
96
96
|
};
|
|
97
97
|
//#endregion
|
|
98
|
-
//#region src/
|
|
99
|
-
/**
|
|
100
|
-
* D1's schema: the guard table batch writes need, then the shared SQL
|
|
101
|
-
* schema of core and the target's plugins, settings last. Every statement can
|
|
102
|
-
* run again, so a migration for a server's plugin tables repeats core's.
|
|
103
|
-
*/
|
|
104
|
-
const d1SchemaStatements = ({ schema, settings } = coreTarget) => [...createTableStatements("sqlite", [WRITE_GUARD_TABLE]), ...generateEngineSql("sqlite", schema, settings)];
|
|
105
|
-
/** The schema as a D1 migration, which `wrangler d1 migrations apply` runs. */
|
|
106
|
-
const d1SchemaSql = (target) => `-- HotUpdater.schema\n\n${d1SchemaStatements(target).map((statement) => `${statement};`).join("\n\n")}\n`;
|
|
107
|
-
//#endregion
|
|
108
|
-
//#region src/d1Database.ts
|
|
98
|
+
//#region src/d1Rest.ts
|
|
109
99
|
/** The REST API binds text: each value goes as JSON and is read back with `json_extract`. */
|
|
110
100
|
const encode = ({ sql, params }) => ({
|
|
111
101
|
sql: sql.replaceAll("?", () => "json_extract(?, '$')"),
|
|
112
102
|
params: params.map((value) => JSON.stringify(value ?? null))
|
|
113
103
|
});
|
|
114
104
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* tables and the server's plugin tables to `migrations`, and
|
|
118
|
-
* `hot-updater db migrate` applies them through the API.
|
|
105
|
+
* Statements on a D1 database through the Cloudflare REST API: one at a
|
|
106
|
+
* time, or a batch D1 runs in one transaction.
|
|
119
107
|
*/
|
|
120
|
-
const
|
|
108
|
+
const d1RestRunner = (config) => {
|
|
121
109
|
const cloudflare = new Cloudflare$1({ apiToken: config.cloudflareApiToken });
|
|
122
|
-
const
|
|
110
|
+
const batch = async (statements) => {
|
|
123
111
|
const [first] = statements;
|
|
124
112
|
if (first === void 0) return [];
|
|
125
113
|
const body = statements.length === 1 ? {
|
|
@@ -136,21 +124,53 @@ const d1Database = (config) => {
|
|
|
136
124
|
return results;
|
|
137
125
|
};
|
|
138
126
|
return {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
batch: execute
|
|
142
|
-
}),
|
|
143
|
-
generateSchema: ((version, _name, target = coreTarget) => {
|
|
144
|
-
if (version !== "latest" && version !== coreSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
|
|
145
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
|
|
146
|
-
return {
|
|
147
|
-
code: d1SchemaSql(target),
|
|
148
|
-
path: `migrations/${timestamp}_hot-updater.sql`
|
|
149
|
-
};
|
|
150
|
-
})
|
|
127
|
+
query: async (statement) => (await batch([statement]))[0],
|
|
128
|
+
batch
|
|
151
129
|
};
|
|
152
130
|
};
|
|
153
131
|
//#endregion
|
|
132
|
+
//#region src/d1Schema.ts
|
|
133
|
+
/**
|
|
134
|
+
* D1's schema: the guard table batch writes need, then the shared SQL
|
|
135
|
+
* schema of core and the target's plugins, settings last. Every statement can
|
|
136
|
+
* run again, so a migration for a server's plugin tables repeats core's.
|
|
137
|
+
*/
|
|
138
|
+
const d1SchemaStatements = ({ schema, settings } = coreTarget) => [...createTableStatements("sqlite", [WRITE_GUARD_TABLE]), ...generateEngineSql("sqlite", schema, settings)];
|
|
139
|
+
const HEADER = "-- HotUpdater.schema\n\n";
|
|
140
|
+
const toSql = (statements) => `${HEADER}${statements.map((statement) => `${statement};`).join("\n\n")}\n`;
|
|
141
|
+
/** The schema as a D1 migration, which `wrangler d1 migrations apply` runs. */
|
|
142
|
+
const d1SchemaSql = (target) => toSql(d1SchemaStatements(target));
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/d1Database.ts
|
|
145
|
+
/**
|
|
146
|
+
* The resource this database reaches, without credentials, under the key
|
|
147
|
+
* `hot-updater doctor` reads to tell the database `hot-updater init` saved
|
|
148
|
+
* from another (a convention CLAUDE.md documents).
|
|
149
|
+
*/
|
|
150
|
+
const DATABASE_TARGET = Symbol.for("@hot-updater/database-target");
|
|
151
|
+
/** `target` under {@link DATABASE_TARGET}, to spread into the database. */
|
|
152
|
+
const marked = (target) => ({ [DATABASE_TARGET]: target });
|
|
153
|
+
/**
|
|
154
|
+
* Hot Updater's database on D1 through the Cloudflare REST API, for the CLI
|
|
155
|
+
* and console. `hot-updater db generate` writes a D1 migration of the built-in
|
|
156
|
+
* tables and the server's plugin tables to `migrations`, and
|
|
157
|
+
* `hot-updater db migrate` applies them through the API. The managed
|
|
158
|
+
* database migrates with the Worker's migrations when `hot-updater init` runs
|
|
159
|
+
* again.
|
|
160
|
+
*/
|
|
161
|
+
const d1Database = (config) => ({
|
|
162
|
+
...createD1Database(d1RestRunner(config)),
|
|
163
|
+
...marked(`d1/${config.databaseId}`),
|
|
164
|
+
generateSchema: ((version, _name, target = coreTarget) => {
|
|
165
|
+
if (version !== "latest" && version !== coreSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
|
|
166
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
|
|
167
|
+
return {
|
|
168
|
+
code: d1SchemaSql(target),
|
|
169
|
+
path: `migrations/${timestamp}_hot-updater.sql`
|
|
170
|
+
};
|
|
171
|
+
})
|
|
172
|
+
});
|
|
173
|
+
//#endregion
|
|
154
174
|
//#region ../../node_modules/.pnpm/is-plain-obj@4.1.0/node_modules/is-plain-obj/index.js
|
|
155
175
|
function isPlainObject(value) {
|
|
156
176
|
if (typeof value !== "object" || value === null) return false;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/cloudflare",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.0-rc.0",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.mjs",
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
"cloudflare": "4.2.0",
|
|
56
56
|
"hono": "4.12.34",
|
|
57
57
|
"uuidv7": "^1.0.2",
|
|
58
|
-
"@hot-updater/
|
|
59
|
-
"@hot-updater/
|
|
60
|
-
"@hot-updater/
|
|
61
|
-
"@hot-updater/
|
|
58
|
+
"@hot-updater/protocol": "1.2.0-rc.0",
|
|
59
|
+
"@hot-updater/plugin-core": "1.2.0-rc.0",
|
|
60
|
+
"@hot-updater/server": "1.2.0-rc.0",
|
|
61
|
+
"@hot-updater/cli-tools": "1.2.0-rc.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@cloudflare/vitest-pool-workers": "0.13.0",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"vitest": "4.1.4",
|
|
75
75
|
"wrangler": "^4.69.0",
|
|
76
76
|
"xdg-app-paths": "^8.3.0",
|
|
77
|
-
"@hot-updater/test-utils": "1.
|
|
77
|
+
"@hot-updater/test-utils": "1.2.0-rc.0"
|
|
78
78
|
},
|
|
79
79
|
"scripts": {
|
|
80
80
|
"build": "tsdown && pnpm build:worker",
|
package/sql/bundles.sql
CHANGED
|
@@ -36,6 +36,16 @@ CREATE UNIQUE INDEX IF NOT EXISTS "channels_name" ON "channels" ("name");
|
|
|
36
36
|
|
|
37
37
|
CREATE TABLE IF NOT EXISTS "bundle_totals" ("platform_key" TEXT NOT NULL, "_shard" INTEGER NOT NULL, "bundles" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("platform_key", "_shard"));
|
|
38
38
|
|
|
39
|
+
CREATE TABLE IF NOT EXISTS "native_builds" ("id" TEXT NOT NULL, "scope_key" TEXT NOT NULL, "platform" TEXT NOT NULL, "channel_id" TEXT NOT NULL, "fingerprint_hash" TEXT, "app_version" TEXT, "min_bundle_id" TEXT NOT NULL, "artifact_kind" TEXT NOT NULL, "artifact_storage_uri" TEXT NOT NULL, "byte_size" INTEGER NOT NULL, "git_commit_hash" TEXT, "created_at_ms" INTEGER NOT NULL, "build_key" TEXT, "build_number" TEXT, "submitted_to" TEXT, "submitted_at_ms" INTEGER, "submitted_version" TEXT, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("id"));
|
|
40
|
+
|
|
41
|
+
CREATE INDEX IF NOT EXISTS "native_builds_byScope" ON "native_builds" ("scope_key", "id");
|
|
42
|
+
|
|
43
|
+
CREATE INDEX IF NOT EXISTS "native_builds_byBuildKey" ON "native_builds" ("scope_key", "build_key", "id");
|
|
44
|
+
|
|
45
|
+
CREATE INDEX IF NOT EXISTS "native_builds_byChannel" ON "native_builds" ("channel_id", "id");
|
|
46
|
+
|
|
47
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "native_builds_min_bundle_id" ON "native_builds" ("min_bundle_id");
|
|
48
|
+
|
|
39
49
|
CREATE TABLE IF NOT EXISTS "bundle_events" ("id" TEXT NOT NULL, "type" TEXT NOT NULL, "install_id" TEXT NOT NULL, "user_id" TEXT, "from_release_id" TEXT, "from_bundle_id" TEXT, "to_release_id" TEXT, "to_bundle_id" TEXT NOT NULL, "platform" TEXT NOT NULL, "app_version" TEXT NOT NULL, "channel" TEXT NOT NULL, "metadata" TEXT NOT NULL, "received_at_ms" INTEGER NOT NULL, "day" INTEGER, "movement_install_id" TEXT, "bundle_ref" TEXT, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("id"));
|
|
40
50
|
|
|
41
51
|
CREATE INDEX IF NOT EXISTS "bundle_events_movementsByInstall" ON "bundle_events" ("movement_install_id", "received_at_ms", "id");
|
|
@@ -112,7 +122,7 @@ CREATE TABLE IF NOT EXISTS "private_hot_updater_settings" ("key" TEXT NOT NULL,
|
|
|
112
122
|
|
|
113
123
|
INSERT INTO "private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.engine', '1', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value";
|
|
114
124
|
|
|
115
|
-
INSERT INTO "private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.core', '1.
|
|
125
|
+
INSERT INTO "private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.core', '1.2.0', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value";
|
|
116
126
|
|
|
117
127
|
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";
|
|
118
128
|
|