cursedbelt-server 4.17.0 → 4.17.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/dist/server/d1/pullD1.js +10 -0
- package/package.json +1 -1
- package/src/server/d1/pullD1.spec.ts +20 -0
- package/src/server/d1/pullD1.ts +11 -0
package/dist/server/d1/pullD1.js
CHANGED
|
@@ -139,6 +139,16 @@ export async function pullD1ToSqlite(options) {
|
|
|
139
139
|
// `exec`, not `run`: each is many statements and `run` would execute only the first,
|
|
140
140
|
// leaving a file with a schema and no rows that looks perfectly valid.
|
|
141
141
|
db.exec(options.schemaSql);
|
|
142
|
+
// 🔴 A table D1 holds that the schema does not declare is created from D1's OWN DDL, never
|
|
143
|
+
// dropped: measured on music's first post-cutover pull (2026-09-23), `art_lookups` — a
|
|
144
|
+
// cache its art job creates where it first needs it — made the whole pull refuse with
|
|
145
|
+
// `no such table`, because its rows were exported and its table was not in the schema.
|
|
146
|
+
const have = new Set(db.query("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((r) => r.name));
|
|
147
|
+
for (const row of rows) {
|
|
148
|
+
if (!tables.includes(row.name) || have.has(row.name) || !row.sql)
|
|
149
|
+
continue;
|
|
150
|
+
db.exec(row.sql.replace(/^\s*CREATE\s+TABLE\s+(?!IF\s+NOT\s+EXISTS)/i, 'CREATE TABLE IF NOT EXISTS '));
|
|
151
|
+
}
|
|
142
152
|
db.exec(sql);
|
|
143
153
|
if (options.afterLoadSql)
|
|
144
154
|
db.exec(options.afterLoadSql);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedbelt-server",
|
|
3
|
-
"version": "4.17.
|
|
3
|
+
"version": "4.17.1",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The app-facing Bun/Hono server tier of the cursedbelt split — storage, sharing, activity, guard, sync. React-free; cursedbelt-core below it.",
|
|
@@ -144,6 +144,26 @@ describe('pulling D1 down — table by table, because a whole-database export re
|
|
|
144
144
|
expect(hits.n).toBe(1);
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
+
test('🔴 a table D1 holds that the schema does not declare is created from D1\'s own DDL', async () => {
|
|
148
|
+
const dir = scratch();
|
|
149
|
+
const master = [...MASTER, { name: 'art_lookups', sql: 'CREATE TABLE art_lookups (key TEXT PRIMARY KEY, art_key TEXT)' }];
|
|
150
|
+
const run: Wrangler = (args) => {
|
|
151
|
+
if (args[1] === 'execute') return { code: 0, stdout: JSON.stringify([{ results: master }]), stderr: '' };
|
|
152
|
+
const table = args[args.indexOf('--table') + 1] as string;
|
|
153
|
+
const out = args[args.indexOf('--output') + 1] as string;
|
|
154
|
+
writeFileSync(
|
|
155
|
+
out,
|
|
156
|
+
table === 'items' ? "INSERT INTO items (id) VALUES ('i1');" : table === 'art_lookups' ? "INSERT INTO art_lookups VALUES ('k', 'art/1');" : '',
|
|
157
|
+
);
|
|
158
|
+
return { code: 0, stdout: '', stderr: '' };
|
|
159
|
+
};
|
|
160
|
+
await pull(dir, run);
|
|
161
|
+
const db = new Database(join(dir, 'pulled.sqlite'), { readonly: true });
|
|
162
|
+
const row = db.query('select art_key from art_lookups').get() as { art_key: string } | null;
|
|
163
|
+
db.close();
|
|
164
|
+
expect(row?.art_key).toBe('art/1');
|
|
165
|
+
});
|
|
166
|
+
|
|
147
167
|
test('🔴 a database without the required table is refused as the wrong one', async () => {
|
|
148
168
|
const dir = scratch();
|
|
149
169
|
const { run } = fake(() => '');
|
package/src/server/d1/pullD1.ts
CHANGED
|
@@ -176,6 +176,17 @@ export async function pullD1ToSqlite(options: PullD1Options): Promise<void> {
|
|
|
176
176
|
// `exec`, not `run`: each is many statements and `run` would execute only the first,
|
|
177
177
|
// leaving a file with a schema and no rows that looks perfectly valid.
|
|
178
178
|
db.exec(options.schemaSql);
|
|
179
|
+
// 🔴 A table D1 holds that the schema does not declare is created from D1's OWN DDL, never
|
|
180
|
+
// dropped: measured on music's first post-cutover pull (2026-09-23), `art_lookups` — a
|
|
181
|
+
// cache its art job creates where it first needs it — made the whole pull refuse with
|
|
182
|
+
// `no such table`, because its rows were exported and its table was not in the schema.
|
|
183
|
+
const have = new Set(
|
|
184
|
+
(db.query("SELECT name FROM sqlite_master WHERE type = 'table'").all() as { name: string }[]).map((r) => r.name),
|
|
185
|
+
);
|
|
186
|
+
for (const row of rows) {
|
|
187
|
+
if (!tables.includes(row.name) || have.has(row.name) || !row.sql) continue;
|
|
188
|
+
db.exec(row.sql.replace(/^\s*CREATE\s+TABLE\s+(?!IF\s+NOT\s+EXISTS)/i, 'CREATE TABLE IF NOT EXISTS '));
|
|
189
|
+
}
|
|
179
190
|
db.exec(sql);
|
|
180
191
|
if (options.afterLoadSql) db.exec(options.afterLoadSql);
|
|
181
192
|
} finally {
|