@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 CHANGED
@@ -75,31 +75,19 @@ const createD1Database = (runner) => {
75
75
  });
76
76
  };
77
77
  //#endregion
78
- //#region src/d1Schema.ts
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
- * Hot Updater's database on D1 through the Cloudflare REST API, for the CLI
96
- * and console. `hot-updater db generate` writes a D1 migration of the built-in
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 d1Database = (config) => {
88
+ const d1RestRunner = (config) => {
101
89
  const cloudflare$1 = new cloudflare.default({ apiToken: config.cloudflareApiToken });
102
- const execute = async (statements) => {
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
- ...createD1Database({
120
- query: async (statement) => (await execute([statement]))[0],
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
- interface D1DatabaseConfig {
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
- interface D1DatabaseConfig {
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/d1Schema.ts
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
- * Hot Updater's database on D1 through the Cloudflare REST API, for the CLI
72
- * and console. `hot-updater db generate` writes a D1 migration of the built-in
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 d1Database = (config) => {
64
+ const d1RestRunner = (config) => {
77
65
  const cloudflare = new Cloudflare({ apiToken: config.cloudflareApiToken });
78
- const execute = async (statements) => {
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
- ...createD1Database({
96
- query: async (statement) => (await execute([statement]))[0],
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;
@@ -102,31 +102,19 @@ const createD1Database = (runner) => {
102
102
  });
103
103
  };
104
104
  //#endregion
105
- //#region src/d1Schema.ts
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
- * Hot Updater's database on D1 through the Cloudflare REST API, for the CLI
123
- * and console. `hot-updater db generate` writes a D1 migration of the built-in
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 d1Database = (config) => {
115
+ const d1RestRunner = (config) => {
128
116
  const cloudflare$1 = new cloudflare.default({ apiToken: config.cloudflareApiToken });
129
- const execute = async (statements) => {
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
- ...createD1Database({
147
- query: async (statement) => (await execute([statement]))[0],
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;
@@ -95,31 +95,19 @@ const createD1Database = (runner) => {
95
95
  });
96
96
  };
97
97
  //#endregion
98
- //#region src/d1Schema.ts
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
- * Hot Updater's database on D1 through the Cloudflare REST API, for the CLI
116
- * and console. `hot-updater db generate` writes a D1 migration of the built-in
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 d1Database = (config) => {
108
+ const d1RestRunner = (config) => {
121
109
  const cloudflare = new Cloudflare$1({ apiToken: config.cloudflareApiToken });
122
- const execute = async (statements) => {
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
- ...createD1Database({
140
- query: async (statement) => (await execute([statement]))[0],
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.1.4",
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/cli-tools": "1.1.4",
59
- "@hot-updater/protocol": "1.1.4",
60
- "@hot-updater/plugin-core": "1.1.4",
61
- "@hot-updater/server": "1.1.4"
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.1.4"
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.0.0', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value";
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