cursedbelt-server 4.9.0 → 4.11.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.
@@ -39,6 +39,14 @@
39
39
  * It wraps either driver, because it sits ABOVE the seam and only counts. That is what
40
40
  * makes the local gate able to prove a production-only limit: wrap a local database in a
41
41
  * test, run the loop, and the laptop fails exactly where the Worker would.
42
+ *
43
+ * ## The other thing that is true for the span of one request: {@link createPendingWrites}
44
+ *
45
+ * A Worker port's synchronous stores answer from a snapshot and queue their durable writes
46
+ * as promises, which the entry point awaits BEFORE the response leaves. That collector has
47
+ * the same lifetime as the budget above — one per invocation, constructed by the caller in
48
+ * the same breath — so it lives in the same file. See its own header for why it was moved
49
+ * here on 2026-09-19.
42
50
  */
43
51
 
44
52
  import { LIMITS } from './limits';
@@ -205,3 +213,81 @@ export function perInvocation(db: D1LikeDatabase, opts: { max?: number } = {}):
205
213
  },
206
214
  };
207
215
  }
216
+
217
+ /**
218
+ * Where a durable write goes on its way out of one invocation.
219
+ *
220
+ * Deliberately two members, and the narrower `add`-only half is what a STORE takes. A store
221
+ * that also held `settle` could end the request's writes from inside the request, which is
222
+ * the entry point's job and nobody else's. `cursedauth/d1-stores` declares exactly this
223
+ * two-method shape under its own name (`WriteCollector`) because that package sits UNDER
224
+ * this one and may not import it — structural typing means an object made here satisfies it
225
+ * verbatim, with no dependency edge in either direction.
226
+ */
227
+ export interface WriteCollector {
228
+ add(promise: Promise<unknown>): void;
229
+ }
230
+
231
+ /** A write that must land before the response is returned. See {@link createPendingWrites}. */
232
+ export interface PendingWrites extends WriteCollector {
233
+ /** Await every collected write. Called BEFORE the response is returned, never after. */
234
+ settle(): Promise<void>;
235
+ }
236
+
237
+ /**
238
+ * Collect the durable writes of ONE invocation, to be awaited before the response leaves.
239
+ *
240
+ * ## Why this is the Worker-write primitive and not a convenience
241
+ *
242
+ * A ported app's synchronous stores cannot await — their signatures are somebody else's
243
+ * published contract — so the shape every port lands on is: one read fills a snapshot, every
244
+ * sync call answers from it, and every mutation both updates the snapshot and pushes its
245
+ * statement's promise in here. The entry point then does the awaiting:
246
+ *
247
+ * ```ts
248
+ * export default {
249
+ * async fetch(req: Request, env: Env) {
250
+ * const db = perInvocation(createRemoteD1(env.DB));
251
+ * const writes = createPendingWrites();
252
+ * const res = await app.fetch(req, { db, writes });
253
+ * await writes.settle(); // 🔴 BEFORE the response, never after
254
+ * return res;
255
+ * },
256
+ * };
257
+ * ```
258
+ *
259
+ * 🔴 **`settle()` before the response, never `ctx.waitUntil`.** A Worker isolate is per-colo
260
+ * and short-lived; a write acknowledged by a 200 and then dropped means "sign out" reported
261
+ * success and left the credential live — a failure invisible to any single-process test and
262
+ * indistinguishable, to the person, from the feature being broken.
263
+ *
264
+ * 🔴 **`Promise.all`, not `allSettled`.** A failed durable write must not be reported as a
265
+ * success. It surfaces as a 500 from the entry point, which is the honest answer to "did my
266
+ * sign-out take". `allSettled` here would convert every lost write into a silent 200 — the
267
+ * same outcome as not collecting them at all, reached by a road that looks careful.
268
+ *
269
+ * ## Why it lives here
270
+ *
271
+ * It was written three times before it was published once — `apps/patterns/worker/stores.ts`,
272
+ * `apps/collections/worker/stores.ts` and `libs/cursedauth/src/d1Stores.ts`, byte-identical
273
+ * but for a comment, with six more ports queued behind them. It is a D1-INVOCATION primitive
274
+ * rather than an auth one, so it belongs beside {@link perInvocation}: both are constructed
275
+ * per request, both describe what is true for the span of one, and neither knows anything
276
+ * about who is signed in.
277
+ *
278
+ * Ordering is NOT what this gives you. Everything added is already in flight, so two writes
279
+ * that must land in order go in one `batch()`, never in two `add`s — a DELETE that must
280
+ * precede its INSERT will otherwise race and trip a unique index.
281
+ */
282
+ export function createPendingWrites(): PendingWrites {
283
+ const pending: Promise<unknown>[] = [];
284
+ return {
285
+ add: (promise) => void pending.push(promise),
286
+ async settle() {
287
+ // `all`, not `allSettled`: a failed durable write must not be reported as a
288
+ // success. It surfaces as a 500 from the entry point, which is the honest answer
289
+ // to "did my sign-out take".
290
+ await Promise.all(pending);
291
+ },
292
+ };
293
+ }
@@ -30,7 +30,11 @@ const stuckIngest = (failed = 42): IncidentReport => ({
30
30
  title: "collections: every queued upload is failing",
31
31
  whatBroke: "The scheduled ingest ran and every one of its items failed.",
32
32
  whatItBlocks: "Nothing new reaches the collections app, and the queue keeps growing.",
33
- whatToDo: "cd ~/code/local/binary-server && bun run storage-drift",
33
+ // 🔴 Not a `cd ~/…`. This is a PUBLISHED library: a fixture here is read by whoever
34
+ // installs it, and the string that stood until 2026-09-22 named a directory on one
35
+ // machine that had not existed for days. `check-paths`'s `deadCd` rule is what found
36
+ // it, and a fixture is the one kind of violation that can simply be reworded.
37
+ whatToDo: "Run the storage-drift sweep for this tenant and clear the failed uploads.",
34
38
  facts: { failed, pending: failed },
35
39
  });
36
40
 
@@ -80,9 +80,14 @@ const noStore = { "cache-control": "no-store, no-cache, must-revalidate", pragma
80
80
  * loosening it: nothing loads from anywhere but this origin, the form posts nowhere (the
81
81
  * script does the POST), and the page cannot be framed.
82
82
  *
83
- * 🔴 **EXPORTED so that nothing has to copy it.** `apps/collections` had a hand-typed
84
- * duplicate of this string in a test until 2026-09-18, which is a policy that can drift
85
- * without anything going red. It is also what
83
+ * 🔴 **EXPORTED so that nothing has to copy it** — and exporting it is not the same as nothing
84
+ * copying it. This sentence used to read *"`apps/collections` had a hand-typed duplicate … until
85
+ * 2026-09-18"*, and it was false the day it was written: the export landed, and BOTH re-typed
86
+ * copies stayed — one in `apps/collections/scripts/injectedScripts.test.ts` and one three
87
+ * directories from here in `../analytics/injectedScripts.spec.ts`. They were collapsed onto this
88
+ * constant on 2026-09-21. A policy that is typed twice can be loosened in one place and stay
89
+ * green in the other, which is a wall that no longer refuses what its own test says it does.
90
+ * It is also what
86
91
  * `beaconNeverReachesTheWall.spec.ts` measures the beacon against: the wall refuses
87
92
  * `static.cloudflareinsights.com`, so the analytics tag and this response are mutually
88
93
  * exclusive BY CONSTRUCTION rather than by anyone remembering. The owner's reason, 2026-09-18: