cursedops 0.10.13 → 0.10.15
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 +1 -1
- package/src/d1Import.ts +46 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedops",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.15",
|
|
4
4
|
"description": "The build-and-ops answers this generation's apps wrote independently and identically: finding a generation's roots — and printing a command that runs when pasted — without knowing a path, the generation's whole-tree laws run over one repo from a checkout or a worktree, macOS launchd agent install/replace/remove and the live port a job serves, the scaffolding and verdicts of a deployed smoke (origin probe, the smoke's own environment, a network that lies about DNS, a settled version), the static-serving helpers eight apps copied — the path-traversal guard among them — the API floor that keeps an unmatched /api/... from ever being answered with the app shell, the commit and dirty flag a checkout-served process reports, the Cloudflare Worker deploy toolkit four apps copied (the deploy sequence, exact-set secrets over a pipe, origin-first rollback, the curl edge fetch, the row-for-row D1 import proof, the billed-CPU tail check around a deploy's walk and smoke, and each app's worker:secrets and worker:smoke main as one function of its data), the relay a Worker fronts a Mac-bound app with (the Durable Object, the frames, the Mac's dialer and key rotation — lifted from station for roms — and the signed-in stage walk's skeleton and the relay app's whole worker:deploy), the Worker import-graph and await-port checks every Worker app's suite runs over its own source, and the public-surface ratchet three published libraries each carried a forked copy of. Mechanism only — no app knows its name from here. Bun, zero runtime dependencies (typescript is an optional peer, for public-surface only), ships source.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
package/src/d1Import.ts
CHANGED
|
@@ -163,3 +163,49 @@ export function d1SchemaText(options: {
|
|
|
163
163
|
];
|
|
164
164
|
return `${header.join("\n")}${options.statements.join("\n")}\n`;
|
|
165
165
|
}
|
|
166
|
+
|
|
167
|
+
/** Rows per page of {@link readD1Table}: a few hundred KB of JSON, well inside what wrangler writes whole. */
|
|
168
|
+
export const D1_READ_PAGE_ROWS = 500;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Every row of `table` over `select` (the column list, as SQL — `"id", "title"`), read in
|
|
172
|
+
* rowid-keyed pages through `read` (the app's `sql → rows`, normally `wranglerRows` of a
|
|
173
|
+
* `wrangler d1 execute --command`).
|
|
174
|
+
*
|
|
175
|
+
* 🔴 Never one `SELECT … FROM table` for a comparison. Measured 2026-09-26: `wrangler --json` handed
|
|
176
|
+
* back flix's 9,296 `films` EMPTY or cut mid-string (`JSON Parse error: Unterminated string`) and
|
|
177
|
+
* music's tracks cut mid-property, so both apps' `worker:rollback --check` — whose one job is to
|
|
178
|
+
* print what a rollback would lose — crashed before printing it. Every app's `worker-import.ts`
|
|
179
|
+
* read that way.
|
|
180
|
+
*
|
|
181
|
+
* 🔴 And no FIXED page is small enough for every table: desk's sheet rows cut 500 at a time the
|
|
182
|
+
* same way. So a page that fails is asked again at half the size, and every page that succeeds
|
|
183
|
+
* doubles back toward `pageRows` (`cursedbelt-server`'s `pullD1ToSqlite` learned the second half
|
|
184
|
+
* the same week). Only a failure at ONE row throws — with the read's own error. A `WITHOUT ROWID`
|
|
185
|
+
* table has no rowid to page on, so it throws D1's `no such column: rowid` that way, loudly.
|
|
186
|
+
*/
|
|
187
|
+
export function readD1Table(
|
|
188
|
+
read: (sql: string) => Array<Record<string, unknown>>,
|
|
189
|
+
table: string,
|
|
190
|
+
select: string,
|
|
191
|
+
pageRows: number = D1_READ_PAGE_ROWS,
|
|
192
|
+
): Array<Record<string, unknown>> {
|
|
193
|
+
const rows: Array<Record<string, unknown>> = [];
|
|
194
|
+
let after: number | null = null;
|
|
195
|
+
let limit = pageRows;
|
|
196
|
+
for (;;) {
|
|
197
|
+
const where = after === null ? "" : ` WHERE rowid > ${after}`;
|
|
198
|
+
let page: Array<Record<string, unknown>>;
|
|
199
|
+
try {
|
|
200
|
+
page = read(`SELECT rowid AS "__page_key", ${select} FROM "${table}"${where} ORDER BY rowid LIMIT ${limit}`);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
if (limit === 1) throw error;
|
|
203
|
+
limit = Math.max(1, Math.floor(limit / 2));
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
for (const { __page_key, ...row } of page) rows.push(row);
|
|
207
|
+
if (page.length < limit) return rows;
|
|
208
|
+
after = Number(page[page.length - 1]?.__page_key);
|
|
209
|
+
limit = Math.min(pageRows, limit * 2);
|
|
210
|
+
}
|
|
211
|
+
}
|