bunderstack 0.25.1 → 1.0.0-beta.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/CHANGELOG.md +43 -0
- package/dist/auth-password.d.ts +27 -0
- package/dist/auth-password.d.ts.map +1 -0
- package/dist/auth-password.js +60 -0
- package/dist/auth-password.js.map +1 -0
- package/dist/auth.d.ts +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +1 -0
- package/dist/auth.js.map +1 -1
- package/dist/backend.d.ts +3 -0
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +3 -1
- package/dist/backend.js.map +1 -1
- package/dist/cli.d.ts +11 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +97 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +0 -8
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -18
- package/dist/config.js.map +1 -1
- package/dist/crud-operations.d.ts.map +1 -1
- package/dist/crud-operations.js +19 -3
- package/dist/crud-operations.js.map +1 -1
- package/dist/dev/binaries.d.ts +22 -0
- package/dist/dev/binaries.d.ts.map +1 -0
- package/dist/dev/binaries.js +119 -0
- package/dist/dev/binaries.js.map +1 -0
- package/dist/dev/env.d.ts +11 -0
- package/dist/dev/env.d.ts.map +1 -0
- package/dist/dev/env.js +71 -0
- package/dist/dev/env.js.map +1 -0
- package/dist/dev/index.d.ts +45 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +287 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/dev/processes.d.ts +37 -0
- package/dist/dev/processes.d.ts.map +1 -0
- package/dist/dev/processes.js +104 -0
- package/dist/dev/processes.js.map +1 -0
- package/dist/dev/push.d.ts +2 -0
- package/dist/dev/push.d.ts.map +1 -0
- package/dist/dev/push.js +25 -0
- package/dist/dev/push.js.map +1 -0
- package/dist/env-probe.d.ts.map +1 -1
- package/dist/env-probe.js +0 -1
- package/dist/env-probe.js.map +1 -1
- package/dist/env.d.ts +0 -3
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +0 -7
- package/dist/env.js.map +1 -1
- package/dist/handler.d.ts +2 -0
- package/dist/handler.d.ts.map +1 -1
- package/dist/handler.js +1 -2
- package/dist/handler.js.map +1 -1
- package/dist/hosted-contract.d.ts.map +1 -1
- package/dist/hosted-contract.js +7 -3
- package/dist/hosted-contract.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/jobs/define.d.ts +5 -0
- package/dist/jobs/define.d.ts.map +1 -1
- package/dist/jobs/define.js.map +1 -1
- package/dist/jobs/index.d.ts +2 -4
- package/dist/jobs/index.d.ts.map +1 -1
- package/dist/jobs/index.js +2 -3
- package/dist/jobs/index.js.map +1 -1
- package/dist/jobs/queue.d.ts +1 -0
- package/dist/jobs/queue.d.ts.map +1 -1
- package/dist/jobs/queue.js +6 -3
- package/dist/jobs/queue.js.map +1 -1
- package/dist/jobs/slots.d.ts +5 -0
- package/dist/jobs/slots.d.ts.map +1 -1
- package/dist/jobs/slots.js +12 -0
- package/dist/jobs/slots.js.map +1 -1
- package/dist/jobs/worker.d.ts +6 -0
- package/dist/jobs/worker.d.ts.map +1 -1
- package/dist/jobs/worker.js +32 -1
- package/dist/jobs/worker.js.map +1 -1
- package/dist/platform.d.ts +25 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +24 -0
- package/dist/platform.js.map +1 -0
- package/dist/query/client.js +1 -1
- package/dist/query/client.js.map +1 -1
- package/dist/rate-limit.d.ts +2 -1
- package/dist/rate-limit.d.ts.map +1 -1
- package/dist/rate-limit.js +19 -22
- package/dist/rate-limit.js.map +1 -1
- package/dist/realtime/facade.d.ts +1 -1
- package/dist/realtime/facade.d.ts.map +1 -1
- package/dist/realtime/facade.js.map +1 -1
- package/dist/realtime/publisher.d.ts +0 -4
- package/dist/realtime/publisher.d.ts.map +1 -1
- package/dist/realtime/publisher.js +0 -9
- package/dist/realtime/publisher.js.map +1 -1
- package/dist/runtime.d.ts +5 -19
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +28 -102
- package/dist/runtime.js.map +1 -1
- package/dist/storage/registry.d.ts +2 -1
- package/dist/storage/registry.d.ts.map +1 -1
- package/dist/storage/registry.js +2 -2
- package/dist/storage/registry.js.map +1 -1
- package/dist/storage/s3.d.ts +11 -4
- package/dist/storage/s3.d.ts.map +1 -1
- package/dist/storage/s3.js +88 -34
- package/dist/storage/s3.js.map +1 -1
- package/dist/storage/sigv4.d.ts +24 -0
- package/dist/storage/sigv4.d.ts.map +1 -0
- package/dist/storage/sigv4.js +107 -0
- package/dist/storage/sigv4.js.map +1 -0
- package/dist/testing/fixture.d.ts.map +1 -1
- package/dist/testing/fixture.js +0 -3
- package/dist/testing/fixture.js.map +1 -1
- package/dist/testing/workers-fakes.d.ts +17 -0
- package/dist/testing/workers-fakes.d.ts.map +1 -0
- package/dist/testing/workers-fakes.js +98 -0
- package/dist/testing/workers-fakes.js.map +1 -0
- package/dist/typeid.d.ts +1 -0
- package/dist/typeid.d.ts.map +1 -1
- package/dist/typeid.js +34 -4
- package/dist/typeid.js.map +1 -1
- package/dist/vite.d.ts +30 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +37 -0
- package/dist/vite.js.map +1 -0
- package/dist/workers/app.d.ts +19 -0
- package/dist/workers/app.d.ts.map +1 -0
- package/dist/workers/app.js +56 -0
- package/dist/workers/app.js.map +1 -0
- package/dist/workers/index.d.ts +21 -0
- package/dist/workers/index.d.ts.map +1 -0
- package/dist/workers/index.js +33 -0
- package/dist/workers/index.js.map +1 -0
- package/dist/workers/r2.d.ts +23 -0
- package/dist/workers/r2.d.ts.map +1 -0
- package/dist/workers/r2.js +77 -0
- package/dist/workers/r2.js.map +1 -0
- package/dist/workers/rate-limiter.d.ts +9 -0
- package/dist/workers/rate-limiter.d.ts.map +1 -0
- package/dist/workers/rate-limiter.js +22 -0
- package/dist/workers/rate-limiter.js.map +1 -0
- package/dist/workers/realtime-hub.d.ts +20 -0
- package/dist/workers/realtime-hub.d.ts.map +1 -0
- package/dist/workers/realtime-hub.js +117 -0
- package/dist/workers/realtime-hub.js.map +1 -0
- package/dist/workers/scheduler.d.ts +13 -0
- package/dist/workers/scheduler.d.ts.map +1 -0
- package/dist/workers/scheduler.js +71 -0
- package/dist/workers/scheduler.js.map +1 -0
- package/dist/workers/types.d.ts +55 -0
- package/dist/workers/types.d.ts.map +1 -0
- package/dist/workers/types.js +4 -0
- package/dist/workers/types.js.map +1 -0
- package/dist/workers/wrangler.d.ts +64 -0
- package/dist/workers/wrangler.d.ts.map +1 -0
- package/dist/workers/wrangler.js +115 -0
- package/dist/workers/wrangler.js.map +1 -0
- package/llms-full.txt +10 -1
- package/package.json +11 -3
- package/dist/jobs/runtime.d.ts +0 -17
- package/dist/jobs/runtime.d.ts.map +0 -1
- package/dist/jobs/runtime.js +0 -56
- package/dist/jobs/runtime.js.map +0 -1
package/dist/jobs/worker.js
CHANGED
|
@@ -8,7 +8,7 @@ import { validateStandardSchema } from '../standard-schema.js';
|
|
|
8
8
|
import { parseCron } from './cron.js';
|
|
9
9
|
import { backoffMs, DEFAULT_RETRIES, leaseDurationFor } from './define.js';
|
|
10
10
|
import { enqueueJob } from './queue.js';
|
|
11
|
-
import { CRON_PREFIX, floorSlot, slotsDue, SLOT_MS } from './slots.js';
|
|
11
|
+
import { CRON_PREFIX, floorSlot, nextCronSlot, slotsDue, SLOT_MS, } from './slots.js';
|
|
12
12
|
const CLAIM_BATCH = 10;
|
|
13
13
|
const SUCCEEDED_RETENTION_MS = 24 * 60 * 60 * 1000;
|
|
14
14
|
const REAP_INTERVAL_MS = 60 * 60_000;
|
|
@@ -515,6 +515,37 @@ export function createJobRunner(deps) {
|
|
|
515
515
|
},
|
|
516
516
|
pump,
|
|
517
517
|
drain,
|
|
518
|
+
/**
|
|
519
|
+
* The earliest time that has work: a pending run_at, a running lease end,
|
|
520
|
+
* or a cron slot not yet materialized. Never earlier than `now`; null when
|
|
521
|
+
* nothing is due before `until`.
|
|
522
|
+
*/
|
|
523
|
+
async nextDueAt(now, until) {
|
|
524
|
+
const candidates = [];
|
|
525
|
+
const [pending] = await db
|
|
526
|
+
.select({ at: sql `min(${t.runAt})` })
|
|
527
|
+
.from(t)
|
|
528
|
+
.where(eq(t.status, 'pending'));
|
|
529
|
+
if (pending?.at != null)
|
|
530
|
+
candidates.push(Number(pending.at));
|
|
531
|
+
const [running] = await db
|
|
532
|
+
.select({ at: sql `min(${t.lockedUntil})` })
|
|
533
|
+
.from(t)
|
|
534
|
+
.where(eq(t.status, 'running'));
|
|
535
|
+
if (running?.at != null)
|
|
536
|
+
candidates.push(Number(running.at));
|
|
537
|
+
for (const [name, def] of Object.entries(defs)) {
|
|
538
|
+
if (def.kind !== 'cron')
|
|
539
|
+
continue;
|
|
540
|
+
const cursor = await cronCursor(`${CRON_PREFIX}${name}`, now);
|
|
541
|
+
const slot = nextCronSlot(parseCron(def.schedule), cursor.checkedThrough, until);
|
|
542
|
+
if (slot !== null)
|
|
543
|
+
candidates.push(slot);
|
|
544
|
+
}
|
|
545
|
+
if (candidates.length === 0)
|
|
546
|
+
return null;
|
|
547
|
+
return Math.max(now, Math.min(...candidates));
|
|
548
|
+
},
|
|
518
549
|
async inspect(now) {
|
|
519
550
|
const runnableRows = await db
|
|
520
551
|
.select({ id: t.id })
|
package/dist/jobs/worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/jobs/worker.ts"],"names":[],"mappings":"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","sourcesContent":["// src/jobs/worker.ts — the queue worker. One `tick()` is a full cycle:\n// recover expired leases → reap old succeeded rows → claim and run queue jobs.\nimport {\n and,\n desc,\n eq,\n inArray,\n is,\n isNotNull,\n lt,\n lte,\n sql,\n} from 'drizzle-orm'\nimport { PgDatabase } from 'drizzle-orm/pg-core'\n\nimport type { AnyDb } from '../dialect'\nimport type { BunderstackLogger } from '../logging'\nimport type {\n AnyBackgroundDefinition,\n JobsDefs,\n JobsRuntimeFacade,\n TickResult,\n} from './define'\n\nimport { jobsTableFor } from '../internal-tables'\nimport { consoleLogger } from '../logging'\nimport { validateStandardSchema } from '../standard-schema'\nimport { parseCron } from './cron'\nimport { backoffMs, DEFAULT_RETRIES, leaseDurationFor } from './define'\nimport { enqueueJob } from './queue'\nimport { CRON_PREFIX, floorSlot, slotsDue, SLOT_MS } from './slots'\n\nconst CLAIM_BATCH = 10\nconst SUCCEEDED_RETENTION_MS = 24 * 60 * 60 * 1000\nconst REAP_INTERVAL_MS = 60 * 60_000\n\nclass JobExecutionTimeoutError extends Error {\n constructor(maxRuntime: number) {\n super(`execution timed out after ${maxRuntime}ms`)\n this.name = 'JobExecutionTimeoutError'\n }\n}\n\nclass JobLeaseLostError extends Error {\n constructor() {\n super('job lease ownership was lost')\n this.name = 'JobLeaseLostError'\n }\n}\n\ntype JobRow = {\n id: string\n type: string\n payloadJson: string\n attempts: number\n runAt: number\n}\n\ntype LeaseOwner = {\n attempt: number\n lockedUntil: number\n}\n\ntype ClaimedWork = {\n row: JobRow\n def: AnyBackgroundDefinition\n owner: LeaseOwner\n}\n\ntype LeaseHeartbeat = {\n owner: LeaseOwner\n lost: Promise<void>\n stop(): Promise<void>\n}\n\ntype PumpResult = { wake?: Promise<void> }\n\ntype CronCursor = {\n checkedThrough: number\n}\n\nfunction jobEvent(\n event: string,\n row: JobRow,\n fields: Record<string, unknown> = {},\n) {\n return {\n source: 'bunderstack.jobs',\n event,\n jobId: row.id,\n jobType: row.type,\n attempt: Number(row.attempts),\n ...fields,\n }\n}\n\nfunction capacityFor(def: AnyBackgroundDefinition): number {\n return def.kind === 'job' && def.concurrency !== undefined\n ? def.concurrency\n : CLAIM_BATCH\n}\n\nfunction toError(err: unknown): Error {\n return err instanceof Error ? err : new Error(String(err))\n}\n\nfunction maxAttempts(def: AnyBackgroundDefinition): number {\n return 1 + (def.retries ?? DEFAULT_RETRIES)\n}\n\n/** Resolve a stored row type back to its definition. Cron rows carry the\n * reserved prefix; queue rows use the definition key verbatim. */\nfunction definitionFor(\n defs: JobsDefs,\n type: string,\n): AnyBackgroundDefinition | undefined {\n if (type.startsWith(CRON_PREFIX)) {\n const def = defs[type.slice(CRON_PREFIX.length)]\n return def?.kind === 'cron' ? def : undefined\n }\n const def = defs[type]\n return def?.kind === 'job' ? def : undefined\n}\n\n/** Queue jobs release terminal dedupe keys; cron slots retain durable ownership. */\nfunction terminalPatch(def: AnyBackgroundDefinition) {\n return def.kind === 'cron' ? {} : { dedupeKey: null }\n}\n\n/** `dedupeUntil: 'start'` releases the key at claim; everything else keeps it\n * until a terminal state, so the claim patch stays unchanged for them. */\nfunction claimPatch(def: AnyBackgroundDefinition) {\n return def.kind === 'job' && def.dedupeUntil === 'start'\n ? { dedupeKey: null }\n : {}\n}\n\nexport function createJobRunner(deps: {\n db: AnyDb\n defs: JobsDefs\n /** Handler ctx WITHOUT `jobs`; the facade is injected via setJobsFacade. */\n ctx: Record<string, unknown>\n logger?: BunderstackLogger\n}) {\n const { db, defs } = deps\n const logger = deps.logger ?? consoleLogger\n const t = jobsTableFor(db)\n const ctx = { ...deps.ctx } as Record<string, unknown>\n let lastReapAt = 0\n const active = new Map<string, Set<Promise<void>>>()\n const cronCursors = new Map<string, CronCursor>()\n\n function ownershipPredicate(id: string, owner: LeaseOwner) {\n return and(\n eq(t.id, id),\n eq(t.status, 'running'),\n eq(t.attempts, owner.attempt),\n eq(t.lockedUntil, owner.lockedUntil),\n )\n }\n\n function startLeaseHeartbeat(args: {\n row: JobRow\n def: AnyBackgroundDefinition\n owner: LeaseOwner\n onLost: () => void\n }): LeaseHeartbeat {\n const owner = args.owner\n const intervalMs = Math.max(10, Math.floor(leaseDurationFor(args.def) / 3))\n let stopped = false\n let resolveLost!: () => void\n const lost = new Promise<void>((resolve) => {\n resolveLost = resolve\n })\n let chain = Promise.resolve()\n const lose = () => {\n if (stopped) return\n stopped = true\n clearInterval(timer)\n resolveLost()\n args.onLost()\n }\n const renew = () => {\n chain = chain\n .then(async () => {\n if (stopped) return\n const nextLockedUntil = Date.now() + leaseDurationFor(args.def)\n const updated = await db\n .update(t)\n .set({ lockedUntil: nextLockedUntil })\n .where(ownershipPredicate(args.row.id, owner))\n .returning({ id: t.id })\n if (!updated[0]) return lose()\n owner.lockedUntil = nextLockedUntil\n })\n .catch(() => lose())\n }\n const timer = setInterval(renew, intervalMs)\n return {\n owner,\n lost,\n async stop() {\n stopped = true\n clearInterval(timer)\n await chain\n },\n }\n }\n\n /** Cron rows carry no payload — their handler input is the slot itself. */\n function resolveInput(def: AnyBackgroundDefinition, row: JobRow): unknown {\n if (def.kind === 'cron') {\n return { scheduledFor: new Date(Number(row.runAt)) }\n }\n const raw = JSON.parse(row.payloadJson)\n return def.input\n ? validateStandardSchema(def.input, raw, 'job payload')\n : undefined\n }\n\n /** Hydrate each cron cursor once, then advance it in this worker's memory. */\n async function cronCursor(type: string, now: number): Promise<CronCursor> {\n const cached = cronCursors.get(type)\n if (cached) return cached\n\n const rows = await db\n .select({ runAt: t.runAt })\n .from(t)\n .where(eq(t.type, type))\n .orderBy(desc(t.runAt))\n .limit(1)\n const cursor = {\n checkedThrough:\n rows[0]?.runAt == null\n ? floorSlot(now) - SLOT_MS\n : Number(rows[0].runAt),\n }\n cronCursors.set(type, cursor)\n return cursor\n }\n\n /** Enqueue a row per due slot. The unique(type, dedupeKey) constraint makes\n * this safe to run concurrently in any number of processes. */\n async function materializeCronSlots(now: number) {\n const through = floorSlot(now)\n for (const [name, def] of Object.entries(defs)) {\n if (def.kind !== 'cron') continue\n const type = `${CRON_PREFIX}${name}`\n const cursor = await cronCursor(type, now)\n const slots = slotsDue({\n cron: parseCron(def.schedule),\n from: cursor.checkedThrough,\n to: through,\n catchUp: def.catchUp,\n catchUpWindowMs: def.catchUpWindow,\n })\n for (const slot of slots) {\n await enqueueJob(db, defs, name, null, {\n runAt: slot,\n dedupeKey: String(slot),\n })\n cursor.checkedThrough = slot\n }\n cursor.checkedThrough = Math.max(cursor.checkedThrough, through)\n }\n }\n\n async function fireOnFailed(\n def: AnyBackgroundDefinition,\n input: unknown,\n error: Error,\n ) {\n if (!def.onFailed) return\n try {\n await (def.onFailed as (i: unknown, e: Error, c: unknown) => unknown)(\n input,\n error,\n ctx,\n )\n } catch (hookErr) {\n logger.error('[bunderstack] onFailed hook threw:', hookErr)\n }\n }\n\n /** running rows whose lease expired → pending (or failed when exhausted). */\n async function recoverExpiredLeases(now: number) {\n const expired: (JobRow & {\n lastError: string | null\n lockedUntil: number\n })[] = await db\n .select({\n id: t.id,\n type: t.type,\n payloadJson: t.payloadJson,\n attempts: t.attempts,\n lastError: t.lastError,\n runAt: t.runAt,\n lockedUntil: t.lockedUntil,\n })\n .from(t)\n .where(\n and(\n eq(t.status, 'running'),\n isNotNull(t.lockedUntil),\n lt(t.lockedUntil, now),\n ),\n )\n for (const row of expired) {\n const owner = {\n attempt: Number(row.attempts),\n lockedUntil: Number(row.lockedUntil),\n }\n const def = definitionFor(defs, row.type)\n const error = new Error('lease expired (worker crashed or timed out)')\n if (!def) {\n await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: now,\n lockedUntil: null,\n lastError: `unknown job type \"${row.type}\"`,\n dedupeKey: null,\n })\n .where(ownershipPredicate(row.id, owner))\n continue\n }\n if (Number(row.attempts) >= maxAttempts(def)) {\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: now,\n lockedUntil: null,\n lastError: error.message,\n ...terminalPatch(def),\n })\n .where(ownershipPredicate(row.id, owner))\n .returning({ id: t.id })\n if (updated[0]) await fireOnFailed(def, resolveInput(def, row), error)\n } else {\n await db\n .update(t)\n .set({\n status: 'pending',\n lockedUntil: null,\n runAt: now + backoffMs(def, Number(row.attempts)),\n lastError: error.message,\n })\n .where(ownershipPredicate(row.id, owner))\n }\n }\n }\n\n async function reapSucceeded(now: number) {\n await db\n .delete(t)\n .where(\n and(\n eq(t.status, 'succeeded'),\n lt(t.finishedAt, now - SUCCEEDED_RETENTION_MS),\n ),\n )\n }\n\n /** Atomically claim up to `limit` runnable jobs of one type. */\n async function claim(\n type: string,\n def: AnyBackgroundDefinition,\n limit: number,\n now: number,\n leaseUntil: number,\n ): Promise<JobRow[]> {\n const pendingIds = db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.status, 'pending'), lte(t.runAt, now)))\n .orderBy(t.runAt)\n .limit(limit)\n // PG: lock the selected rows so concurrent replicas skip them. SQLite's\n // single-writer model makes the one-statement UPDATE atomic on its own.\n const sub = is(db, PgDatabase)\n ? (\n pendingIds as unknown as {\n for: (m: string, o: object) => typeof pendingIds\n }\n ).for('update', { skipLocked: true })\n : pendingIds\n const rows: JobRow[] = await db\n .update(t)\n .set({\n status: 'running',\n lockedUntil: leaseUntil,\n attempts: sql`${t.attempts} + 1`,\n ...claimPatch(def),\n })\n .where(and(inArray(t.id, sub), eq(t.status, 'pending')))\n .returning({\n id: t.id,\n type: t.type,\n payloadJson: t.payloadJson,\n attempts: t.attempts,\n runAt: t.runAt,\n })\n return rows\n }\n\n async function claimAvailable(\n type: string,\n def: AnyBackgroundDefinition,\n now: number,\n ): Promise<ClaimedWork[]> {\n const runningRows = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.status, 'running')))\n let available = capacityFor(def) - runningRows.length\n if (available <= 0) return []\n\n const leaseUntil = now + leaseDurationFor(def)\n const work: ClaimedWork[] = []\n while (available > 0) {\n const limit = Math.min(CLAIM_BATCH, available)\n const rows = await claim(type, def, limit, now, leaseUntil)\n for (const row of rows) {\n work.push({\n row,\n def,\n owner: { attempt: Number(row.attempts), lockedUntil: leaseUntil },\n })\n }\n available -= rows.length\n if (rows.length < limit) break\n }\n return work\n }\n\n // `now` is the tick's injected clock: retry runAt math uses it so tests can\n // drive backoff deterministically. finishedAt uses the real clock (a handler\n // may run long past the tick's start).\n async function runJob(\n row: JobRow,\n def: AnyBackgroundDefinition,\n now: number,\n owner: LeaseOwner,\n ): Promise<'ran' | 'failed' | 'lost'> {\n const startedAt = Date.now()\n let input: unknown\n try {\n input = resolveInput(def, row)\n } catch (err) {\n // Stored payload no longer parses (schema drift): retrying can't help.\n const e = toError(err)\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: Date.now(),\n lockedUntil: null,\n lastError: e.message,\n ...terminalPatch(def),\n })\n .where(ownershipPredicate(row.id, owner))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n await fireOnFailed(def, undefined, e)\n return 'failed'\n }\n const controller = new AbortController()\n const handlerCtx = { ...ctx, signal: controller.signal }\n logger.info(JSON.stringify(jobEvent('job.claimed', row)))\n let lost = false\n const heartbeat = startLeaseHeartbeat({\n row,\n def,\n owner,\n onLost: () => {\n lost = true\n controller.abort(new JobLeaseLostError())\n logger.warn(\n JSON.stringify(\n jobEvent('job.lease_lost', row, {\n durationMs: Date.now() - startedAt,\n }),\n ),\n )\n },\n })\n const deadline =\n def.maxRuntime === undefined\n ? undefined\n : setTimeout(() => {\n controller.abort(new JobExecutionTimeoutError(def.maxRuntime!))\n logger.warn(\n JSON.stringify(\n jobEvent('job.execution_timed_out', row, {\n durationMs: Date.now() - startedAt,\n }),\n ),\n )\n }, def.maxRuntime)\n try {\n await (def.handler as (i: unknown, c: unknown) => unknown)(\n input,\n handlerCtx,\n )\n if (controller.signal.reason instanceof JobExecutionTimeoutError) {\n throw controller.signal.reason\n }\n if (deadline) clearTimeout(deadline)\n await heartbeat.stop()\n if (lost) return 'lost'\n const updated = await db\n .update(t)\n .set({\n status: 'succeeded',\n finishedAt: Date.now(),\n lockedUntil: null,\n ...terminalPatch(def),\n })\n .where(ownershipPredicate(row.id, owner))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n logger.info(\n JSON.stringify(\n jobEvent('job.completed', row, {\n durationMs: Date.now() - startedAt,\n }),\n ),\n )\n return 'ran'\n } catch (err) {\n if (deadline) clearTimeout(deadline)\n const e =\n controller.signal.reason instanceof JobExecutionTimeoutError\n ? controller.signal.reason\n : toError(err)\n await heartbeat.stop()\n if (lost) return 'lost'\n if (Number(row.attempts) < maxAttempts(def)) {\n const updated = await db\n .update(t)\n .set({\n status: 'pending',\n lockedUntil: null,\n runAt: now + backoffMs(def, Number(row.attempts)),\n lastError: e.message,\n })\n .where(ownershipPredicate(row.id, owner))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n logger.warn(\n JSON.stringify(\n jobEvent('job.retrying', row, {\n durationMs: Date.now() - startedAt,\n error: e.message,\n }),\n ),\n )\n } else {\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: Date.now(),\n lockedUntil: null,\n lastError: e.message,\n ...terminalPatch(def),\n })\n .where(ownershipPredicate(row.id, owner))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n logger.error(\n JSON.stringify(\n jobEvent('job.failed', row, {\n durationMs: Date.now() - startedAt,\n error: e.message,\n }),\n ),\n )\n await fireOnFailed(def, input, e)\n }\n return 'failed'\n }\n }\n\n async function runClaimable(now: number): Promise<TickResult> {\n const claimedWork: ClaimedWork[] = []\n let totalClaimed = 0\n for (const [name, def] of Object.entries(defs)) {\n const type = def.kind === 'cron' ? `${CRON_PREFIX}${name}` : name\n const claimed = await claimAvailable(type, def, now)\n totalClaimed += claimed.length\n claimedWork.push(...claimed)\n }\n // Claim the whole tick snapshot before starting any handler. Work enqueued\n // by a handler therefore belongs to the next tick, regardless of the\n // declaration order of its target job type.\n const outcomes = await Promise.all(\n claimedWork.map(({ row, def, owner }) => runJob(row, def, now, owner)),\n )\n let ran = 0\n let failed = 0\n for (const outcome of outcomes) {\n if (outcome === 'ran') ran++\n else if (outcome === 'failed') failed++\n }\n return { claimed: totalClaimed, ran, failed }\n }\n\n async function maintain(now: number) {\n await materializeCronSlots(now)\n await recoverExpiredLeases(now)\n if (now - lastReapAt >= REAP_INTERVAL_MS) {\n lastReapAt = now\n await reapSucceeded(now)\n }\n }\n\n function startWork(\n type: string,\n work: ClaimedWork,\n claimedAt: number,\n ): Promise<void> {\n let tasks = active.get(type)\n if (!tasks) {\n tasks = new Set()\n active.set(type, tasks)\n }\n let task: Promise<void>\n task = runJob(work.row, work.def, claimedAt, work.owner)\n .then(() => undefined)\n .catch((error) => {\n logger.error('[bunderstack] background job execution failed:', error)\n })\n .finally(() => {\n tasks!.delete(task)\n if (tasks!.size === 0) active.delete(type)\n })\n tasks.add(task)\n return task\n }\n\n async function pump(now: number = Date.now()): Promise<PumpResult> {\n await maintain(now)\n for (const [name, def] of Object.entries(defs)) {\n const type = def.kind === 'cron' ? `${CRON_PREFIX}${name}` : name\n const work = await claimAvailable(type, def, now)\n for (const item of work) startWork(type, item, now)\n }\n const snapshot = [...active.values()].flatMap((tasks) => [...tasks])\n return snapshot.length === 0\n ? {}\n : { wake: Promise.race(snapshot).then(() => undefined) }\n }\n\n async function drain(): Promise<void> {\n const snapshot = [...active.values()].flatMap((tasks) => [...tasks])\n await Promise.allSettled(snapshot)\n }\n\n return {\n async tick(now: number = Date.now()): Promise<TickResult> {\n await maintain(now)\n return runClaimable(now)\n },\n pump,\n drain,\n async inspect(now: number) {\n const runnableRows = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.status, 'pending'), lte(t.runAt, now)))\n const failed: Array<{\n id: string\n type: string\n attempts: number\n lastError: string | null\n }> = await db\n .select({\n id: t.id,\n type: t.type,\n attempts: t.attempts,\n lastError: t.lastError,\n })\n .from(t)\n .where(eq(t.status, 'failed'))\n const rows: Array<{\n id: string\n type: string\n status: string\n attempts: number\n runAt: number\n dedupeKey: string | null\n lastError: string | null\n createdAt: number\n }> = await db\n .select({\n id: t.id,\n type: t.type,\n status: t.status,\n attempts: t.attempts,\n runAt: t.runAt,\n dedupeKey: t.dedupeKey,\n lastError: t.lastError,\n createdAt: t.createdAt,\n })\n .from(t)\n return {\n runnable: runnableRows.length,\n failed: failed.map((row) => ({\n id: String(row.id),\n name: row.type.startsWith(CRON_PREFIX)\n ? row.type.slice(CRON_PREFIX.length)\n : row.type,\n attempts: Number(row.attempts),\n lastError: row.lastError,\n })),\n jobs: rows\n .sort(\n (left, right) =>\n Number(left.createdAt) - Number(right.createdAt) ||\n String(left.id).localeCompare(String(right.id)),\n )\n .map((row) => ({\n id: String(row.id),\n name: row.type.startsWith(CRON_PREFIX)\n ? row.type.slice(CRON_PREFIX.length)\n : row.type,\n kind: row.type.startsWith(CRON_PREFIX)\n ? ('cron' as const)\n : ('job' as const),\n status: row.status as\n | 'pending'\n | 'running'\n | 'succeeded'\n | 'failed',\n attempts: Number(row.attempts),\n runAt: Number(row.runAt),\n dedupeKey: row.dedupeKey,\n lastError: row.lastError,\n })),\n }\n },\n setJobsFacade(f: JobsRuntimeFacade) {\n ctx.jobs = f\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/jobs/worker.ts"],"names":[],"mappings":"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","sourcesContent":["// src/jobs/worker.ts — the queue worker. One `tick()` is a full cycle:\n// recover expired leases → reap old succeeded rows → claim and run queue jobs.\nimport {\n and,\n desc,\n eq,\n inArray,\n is,\n isNotNull,\n lt,\n lte,\n sql,\n} from 'drizzle-orm'\nimport { PgDatabase } from 'drizzle-orm/pg-core'\n\nimport type { AnyDb } from '../dialect'\nimport type { BunderstackLogger } from '../logging'\nimport type {\n AnyBackgroundDefinition,\n JobsDefs,\n JobsRuntimeFacade,\n TickResult,\n} from './define'\n\nimport { jobsTableFor } from '../internal-tables'\nimport { consoleLogger } from '../logging'\nimport { validateStandardSchema } from '../standard-schema'\nimport { parseCron } from './cron'\nimport { backoffMs, DEFAULT_RETRIES, leaseDurationFor } from './define'\nimport { enqueueJob } from './queue'\nimport {\n CRON_PREFIX,\n floorSlot,\n nextCronSlot,\n slotsDue,\n SLOT_MS,\n} from './slots'\n\nconst CLAIM_BATCH = 10\nconst SUCCEEDED_RETENTION_MS = 24 * 60 * 60 * 1000\nconst REAP_INTERVAL_MS = 60 * 60_000\n\nclass JobExecutionTimeoutError extends Error {\n constructor(maxRuntime: number) {\n super(`execution timed out after ${maxRuntime}ms`)\n this.name = 'JobExecutionTimeoutError'\n }\n}\n\nclass JobLeaseLostError extends Error {\n constructor() {\n super('job lease ownership was lost')\n this.name = 'JobLeaseLostError'\n }\n}\n\ntype JobRow = {\n id: string\n type: string\n payloadJson: string\n attempts: number\n runAt: number\n}\n\ntype LeaseOwner = {\n attempt: number\n lockedUntil: number\n}\n\ntype ClaimedWork = {\n row: JobRow\n def: AnyBackgroundDefinition\n owner: LeaseOwner\n}\n\ntype LeaseHeartbeat = {\n owner: LeaseOwner\n lost: Promise<void>\n stop(): Promise<void>\n}\n\ntype PumpResult = { wake?: Promise<void> }\n\ntype CronCursor = {\n checkedThrough: number\n}\n\nfunction jobEvent(\n event: string,\n row: JobRow,\n fields: Record<string, unknown> = {},\n) {\n return {\n source: 'bunderstack.jobs',\n event,\n jobId: row.id,\n jobType: row.type,\n attempt: Number(row.attempts),\n ...fields,\n }\n}\n\nfunction capacityFor(def: AnyBackgroundDefinition): number {\n return def.kind === 'job' && def.concurrency !== undefined\n ? def.concurrency\n : CLAIM_BATCH\n}\n\nfunction toError(err: unknown): Error {\n return err instanceof Error ? err : new Error(String(err))\n}\n\nfunction maxAttempts(def: AnyBackgroundDefinition): number {\n return 1 + (def.retries ?? DEFAULT_RETRIES)\n}\n\n/** Resolve a stored row type back to its definition. Cron rows carry the\n * reserved prefix; queue rows use the definition key verbatim. */\nfunction definitionFor(\n defs: JobsDefs,\n type: string,\n): AnyBackgroundDefinition | undefined {\n if (type.startsWith(CRON_PREFIX)) {\n const def = defs[type.slice(CRON_PREFIX.length)]\n return def?.kind === 'cron' ? def : undefined\n }\n const def = defs[type]\n return def?.kind === 'job' ? def : undefined\n}\n\n/** Queue jobs release terminal dedupe keys; cron slots retain durable ownership. */\nfunction terminalPatch(def: AnyBackgroundDefinition) {\n return def.kind === 'cron' ? {} : { dedupeKey: null }\n}\n\n/** `dedupeUntil: 'start'` releases the key at claim; everything else keeps it\n * until a terminal state, so the claim patch stays unchanged for them. */\nfunction claimPatch(def: AnyBackgroundDefinition) {\n return def.kind === 'job' && def.dedupeUntil === 'start'\n ? { dedupeKey: null }\n : {}\n}\n\nexport function createJobRunner(deps: {\n db: AnyDb\n defs: JobsDefs\n /** Handler ctx WITHOUT `jobs`; the facade is injected via setJobsFacade. */\n ctx: Record<string, unknown>\n logger?: BunderstackLogger\n}) {\n const { db, defs } = deps\n const logger = deps.logger ?? consoleLogger\n const t = jobsTableFor(db)\n const ctx = { ...deps.ctx } as Record<string, unknown>\n let lastReapAt = 0\n const active = new Map<string, Set<Promise<void>>>()\n const cronCursors = new Map<string, CronCursor>()\n\n function ownershipPredicate(id: string, owner: LeaseOwner) {\n return and(\n eq(t.id, id),\n eq(t.status, 'running'),\n eq(t.attempts, owner.attempt),\n eq(t.lockedUntil, owner.lockedUntil),\n )\n }\n\n function startLeaseHeartbeat(args: {\n row: JobRow\n def: AnyBackgroundDefinition\n owner: LeaseOwner\n onLost: () => void\n }): LeaseHeartbeat {\n const owner = args.owner\n const intervalMs = Math.max(10, Math.floor(leaseDurationFor(args.def) / 3))\n let stopped = false\n let resolveLost!: () => void\n const lost = new Promise<void>((resolve) => {\n resolveLost = resolve\n })\n let chain = Promise.resolve()\n const lose = () => {\n if (stopped) return\n stopped = true\n clearInterval(timer)\n resolveLost()\n args.onLost()\n }\n const renew = () => {\n chain = chain\n .then(async () => {\n if (stopped) return\n const nextLockedUntil = Date.now() + leaseDurationFor(args.def)\n const updated = await db\n .update(t)\n .set({ lockedUntil: nextLockedUntil })\n .where(ownershipPredicate(args.row.id, owner))\n .returning({ id: t.id })\n if (!updated[0]) return lose()\n owner.lockedUntil = nextLockedUntil\n })\n .catch(() => lose())\n }\n const timer = setInterval(renew, intervalMs)\n return {\n owner,\n lost,\n async stop() {\n stopped = true\n clearInterval(timer)\n await chain\n },\n }\n }\n\n /** Cron rows carry no payload — their handler input is the slot itself. */\n function resolveInput(def: AnyBackgroundDefinition, row: JobRow): unknown {\n if (def.kind === 'cron') {\n return { scheduledFor: new Date(Number(row.runAt)) }\n }\n const raw = JSON.parse(row.payloadJson)\n return def.input\n ? validateStandardSchema(def.input, raw, 'job payload')\n : undefined\n }\n\n /** Hydrate each cron cursor once, then advance it in this worker's memory. */\n async function cronCursor(type: string, now: number): Promise<CronCursor> {\n const cached = cronCursors.get(type)\n if (cached) return cached\n\n const rows = await db\n .select({ runAt: t.runAt })\n .from(t)\n .where(eq(t.type, type))\n .orderBy(desc(t.runAt))\n .limit(1)\n const cursor = {\n checkedThrough:\n rows[0]?.runAt == null\n ? floorSlot(now) - SLOT_MS\n : Number(rows[0].runAt),\n }\n cronCursors.set(type, cursor)\n return cursor\n }\n\n /** Enqueue a row per due slot. The unique(type, dedupeKey) constraint makes\n * this safe to run concurrently in any number of processes. */\n async function materializeCronSlots(now: number) {\n const through = floorSlot(now)\n for (const [name, def] of Object.entries(defs)) {\n if (def.kind !== 'cron') continue\n const type = `${CRON_PREFIX}${name}`\n const cursor = await cronCursor(type, now)\n const slots = slotsDue({\n cron: parseCron(def.schedule),\n from: cursor.checkedThrough,\n to: through,\n catchUp: def.catchUp,\n catchUpWindowMs: def.catchUpWindow,\n })\n for (const slot of slots) {\n await enqueueJob(db, defs, name, null, {\n runAt: slot,\n dedupeKey: String(slot),\n })\n cursor.checkedThrough = slot\n }\n cursor.checkedThrough = Math.max(cursor.checkedThrough, through)\n }\n }\n\n async function fireOnFailed(\n def: AnyBackgroundDefinition,\n input: unknown,\n error: Error,\n ) {\n if (!def.onFailed) return\n try {\n await (def.onFailed as (i: unknown, e: Error, c: unknown) => unknown)(\n input,\n error,\n ctx,\n )\n } catch (hookErr) {\n logger.error('[bunderstack] onFailed hook threw:', hookErr)\n }\n }\n\n /** running rows whose lease expired → pending (or failed when exhausted). */\n async function recoverExpiredLeases(now: number) {\n const expired: (JobRow & {\n lastError: string | null\n lockedUntil: number\n })[] = await db\n .select({\n id: t.id,\n type: t.type,\n payloadJson: t.payloadJson,\n attempts: t.attempts,\n lastError: t.lastError,\n runAt: t.runAt,\n lockedUntil: t.lockedUntil,\n })\n .from(t)\n .where(\n and(\n eq(t.status, 'running'),\n isNotNull(t.lockedUntil),\n lt(t.lockedUntil, now),\n ),\n )\n for (const row of expired) {\n const owner = {\n attempt: Number(row.attempts),\n lockedUntil: Number(row.lockedUntil),\n }\n const def = definitionFor(defs, row.type)\n const error = new Error('lease expired (worker crashed or timed out)')\n if (!def) {\n await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: now,\n lockedUntil: null,\n lastError: `unknown job type \"${row.type}\"`,\n dedupeKey: null,\n })\n .where(ownershipPredicate(row.id, owner))\n continue\n }\n if (Number(row.attempts) >= maxAttempts(def)) {\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: now,\n lockedUntil: null,\n lastError: error.message,\n ...terminalPatch(def),\n })\n .where(ownershipPredicate(row.id, owner))\n .returning({ id: t.id })\n if (updated[0]) await fireOnFailed(def, resolveInput(def, row), error)\n } else {\n await db\n .update(t)\n .set({\n status: 'pending',\n lockedUntil: null,\n runAt: now + backoffMs(def, Number(row.attempts)),\n lastError: error.message,\n })\n .where(ownershipPredicate(row.id, owner))\n }\n }\n }\n\n async function reapSucceeded(now: number) {\n await db\n .delete(t)\n .where(\n and(\n eq(t.status, 'succeeded'),\n lt(t.finishedAt, now - SUCCEEDED_RETENTION_MS),\n ),\n )\n }\n\n /** Atomically claim up to `limit` runnable jobs of one type. */\n async function claim(\n type: string,\n def: AnyBackgroundDefinition,\n limit: number,\n now: number,\n leaseUntil: number,\n ): Promise<JobRow[]> {\n const pendingIds = db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.status, 'pending'), lte(t.runAt, now)))\n .orderBy(t.runAt)\n .limit(limit)\n // PG: lock the selected rows so concurrent replicas skip them. SQLite's\n // single-writer model makes the one-statement UPDATE atomic on its own.\n const sub = is(db, PgDatabase)\n ? (\n pendingIds as unknown as {\n for: (m: string, o: object) => typeof pendingIds\n }\n ).for('update', { skipLocked: true })\n : pendingIds\n const rows: JobRow[] = await db\n .update(t)\n .set({\n status: 'running',\n lockedUntil: leaseUntil,\n attempts: sql`${t.attempts} + 1`,\n ...claimPatch(def),\n })\n .where(and(inArray(t.id, sub), eq(t.status, 'pending')))\n .returning({\n id: t.id,\n type: t.type,\n payloadJson: t.payloadJson,\n attempts: t.attempts,\n runAt: t.runAt,\n })\n return rows\n }\n\n async function claimAvailable(\n type: string,\n def: AnyBackgroundDefinition,\n now: number,\n ): Promise<ClaimedWork[]> {\n const runningRows = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.status, 'running')))\n let available = capacityFor(def) - runningRows.length\n if (available <= 0) return []\n\n const leaseUntil = now + leaseDurationFor(def)\n const work: ClaimedWork[] = []\n while (available > 0) {\n const limit = Math.min(CLAIM_BATCH, available)\n const rows = await claim(type, def, limit, now, leaseUntil)\n for (const row of rows) {\n work.push({\n row,\n def,\n owner: { attempt: Number(row.attempts), lockedUntil: leaseUntil },\n })\n }\n available -= rows.length\n if (rows.length < limit) break\n }\n return work\n }\n\n // `now` is the tick's injected clock: retry runAt math uses it so tests can\n // drive backoff deterministically. finishedAt uses the real clock (a handler\n // may run long past the tick's start).\n async function runJob(\n row: JobRow,\n def: AnyBackgroundDefinition,\n now: number,\n owner: LeaseOwner,\n ): Promise<'ran' | 'failed' | 'lost'> {\n const startedAt = Date.now()\n let input: unknown\n try {\n input = resolveInput(def, row)\n } catch (err) {\n // Stored payload no longer parses (schema drift): retrying can't help.\n const e = toError(err)\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: Date.now(),\n lockedUntil: null,\n lastError: e.message,\n ...terminalPatch(def),\n })\n .where(ownershipPredicate(row.id, owner))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n await fireOnFailed(def, undefined, e)\n return 'failed'\n }\n const controller = new AbortController()\n const handlerCtx = { ...ctx, signal: controller.signal }\n logger.info(JSON.stringify(jobEvent('job.claimed', row)))\n let lost = false\n const heartbeat = startLeaseHeartbeat({\n row,\n def,\n owner,\n onLost: () => {\n lost = true\n controller.abort(new JobLeaseLostError())\n logger.warn(\n JSON.stringify(\n jobEvent('job.lease_lost', row, {\n durationMs: Date.now() - startedAt,\n }),\n ),\n )\n },\n })\n const deadline =\n def.maxRuntime === undefined\n ? undefined\n : setTimeout(() => {\n controller.abort(new JobExecutionTimeoutError(def.maxRuntime!))\n logger.warn(\n JSON.stringify(\n jobEvent('job.execution_timed_out', row, {\n durationMs: Date.now() - startedAt,\n }),\n ),\n )\n }, def.maxRuntime)\n try {\n await (def.handler as (i: unknown, c: unknown) => unknown)(\n input,\n handlerCtx,\n )\n if (controller.signal.reason instanceof JobExecutionTimeoutError) {\n throw controller.signal.reason\n }\n if (deadline) clearTimeout(deadline)\n await heartbeat.stop()\n if (lost) return 'lost'\n const updated = await db\n .update(t)\n .set({\n status: 'succeeded',\n finishedAt: Date.now(),\n lockedUntil: null,\n ...terminalPatch(def),\n })\n .where(ownershipPredicate(row.id, owner))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n logger.info(\n JSON.stringify(\n jobEvent('job.completed', row, {\n durationMs: Date.now() - startedAt,\n }),\n ),\n )\n return 'ran'\n } catch (err) {\n if (deadline) clearTimeout(deadline)\n const e =\n controller.signal.reason instanceof JobExecutionTimeoutError\n ? controller.signal.reason\n : toError(err)\n await heartbeat.stop()\n if (lost) return 'lost'\n if (Number(row.attempts) < maxAttempts(def)) {\n const updated = await db\n .update(t)\n .set({\n status: 'pending',\n lockedUntil: null,\n runAt: now + backoffMs(def, Number(row.attempts)),\n lastError: e.message,\n })\n .where(ownershipPredicate(row.id, owner))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n logger.warn(\n JSON.stringify(\n jobEvent('job.retrying', row, {\n durationMs: Date.now() - startedAt,\n error: e.message,\n }),\n ),\n )\n } else {\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: Date.now(),\n lockedUntil: null,\n lastError: e.message,\n ...terminalPatch(def),\n })\n .where(ownershipPredicate(row.id, owner))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n logger.error(\n JSON.stringify(\n jobEvent('job.failed', row, {\n durationMs: Date.now() - startedAt,\n error: e.message,\n }),\n ),\n )\n await fireOnFailed(def, input, e)\n }\n return 'failed'\n }\n }\n\n async function runClaimable(now: number): Promise<TickResult> {\n const claimedWork: ClaimedWork[] = []\n let totalClaimed = 0\n for (const [name, def] of Object.entries(defs)) {\n const type = def.kind === 'cron' ? `${CRON_PREFIX}${name}` : name\n const claimed = await claimAvailable(type, def, now)\n totalClaimed += claimed.length\n claimedWork.push(...claimed)\n }\n // Claim the whole tick snapshot before starting any handler. Work enqueued\n // by a handler therefore belongs to the next tick, regardless of the\n // declaration order of its target job type.\n const outcomes = await Promise.all(\n claimedWork.map(({ row, def, owner }) => runJob(row, def, now, owner)),\n )\n let ran = 0\n let failed = 0\n for (const outcome of outcomes) {\n if (outcome === 'ran') ran++\n else if (outcome === 'failed') failed++\n }\n return { claimed: totalClaimed, ran, failed }\n }\n\n async function maintain(now: number) {\n await materializeCronSlots(now)\n await recoverExpiredLeases(now)\n if (now - lastReapAt >= REAP_INTERVAL_MS) {\n lastReapAt = now\n await reapSucceeded(now)\n }\n }\n\n function startWork(\n type: string,\n work: ClaimedWork,\n claimedAt: number,\n ): Promise<void> {\n let tasks = active.get(type)\n if (!tasks) {\n tasks = new Set()\n active.set(type, tasks)\n }\n let task: Promise<void>\n task = runJob(work.row, work.def, claimedAt, work.owner)\n .then(() => undefined)\n .catch((error) => {\n logger.error('[bunderstack] background job execution failed:', error)\n })\n .finally(() => {\n tasks!.delete(task)\n if (tasks!.size === 0) active.delete(type)\n })\n tasks.add(task)\n return task\n }\n\n async function pump(now: number = Date.now()): Promise<PumpResult> {\n await maintain(now)\n for (const [name, def] of Object.entries(defs)) {\n const type = def.kind === 'cron' ? `${CRON_PREFIX}${name}` : name\n const work = await claimAvailable(type, def, now)\n for (const item of work) startWork(type, item, now)\n }\n const snapshot = [...active.values()].flatMap((tasks) => [...tasks])\n return snapshot.length === 0\n ? {}\n : { wake: Promise.race(snapshot).then(() => undefined) }\n }\n\n async function drain(): Promise<void> {\n const snapshot = [...active.values()].flatMap((tasks) => [...tasks])\n await Promise.allSettled(snapshot)\n }\n\n return {\n async tick(now: number = Date.now()): Promise<TickResult> {\n await maintain(now)\n return runClaimable(now)\n },\n pump,\n drain,\n /**\n * The earliest time that has work: a pending run_at, a running lease end,\n * or a cron slot not yet materialized. Never earlier than `now`; null when\n * nothing is due before `until`.\n */\n async nextDueAt(now: number, until: number): Promise<number | null> {\n const candidates: number[] = []\n const [pending] = await db\n .select({ at: sql<number | string | null>`min(${t.runAt})` })\n .from(t)\n .where(eq(t.status, 'pending'))\n if (pending?.at != null) candidates.push(Number(pending.at))\n const [running] = await db\n .select({ at: sql<number | string | null>`min(${t.lockedUntil})` })\n .from(t)\n .where(eq(t.status, 'running'))\n if (running?.at != null) candidates.push(Number(running.at))\n for (const [name, def] of Object.entries(defs)) {\n if (def.kind !== 'cron') continue\n const cursor = await cronCursor(`${CRON_PREFIX}${name}`, now)\n const slot = nextCronSlot(\n parseCron(def.schedule),\n cursor.checkedThrough,\n until,\n )\n if (slot !== null) candidates.push(slot)\n }\n if (candidates.length === 0) return null\n return Math.max(now, Math.min(...candidates))\n },\n async inspect(now: number) {\n const runnableRows = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.status, 'pending'), lte(t.runAt, now)))\n const failed: Array<{\n id: string\n type: string\n attempts: number\n lastError: string | null\n }> = await db\n .select({\n id: t.id,\n type: t.type,\n attempts: t.attempts,\n lastError: t.lastError,\n })\n .from(t)\n .where(eq(t.status, 'failed'))\n const rows: Array<{\n id: string\n type: string\n status: string\n attempts: number\n runAt: number\n dedupeKey: string | null\n lastError: string | null\n createdAt: number\n }> = await db\n .select({\n id: t.id,\n type: t.type,\n status: t.status,\n attempts: t.attempts,\n runAt: t.runAt,\n dedupeKey: t.dedupeKey,\n lastError: t.lastError,\n createdAt: t.createdAt,\n })\n .from(t)\n return {\n runnable: runnableRows.length,\n failed: failed.map((row) => ({\n id: String(row.id),\n name: row.type.startsWith(CRON_PREFIX)\n ? row.type.slice(CRON_PREFIX.length)\n : row.type,\n attempts: Number(row.attempts),\n lastError: row.lastError,\n })),\n jobs: rows\n .sort(\n (left, right) =>\n Number(left.createdAt) - Number(right.createdAt) ||\n String(left.id).localeCompare(String(right.id)),\n )\n .map((row) => ({\n id: String(row.id),\n name: row.type.startsWith(CRON_PREFIX)\n ? row.type.slice(CRON_PREFIX.length)\n : row.type,\n kind: row.type.startsWith(CRON_PREFIX)\n ? ('cron' as const)\n : ('job' as const),\n status: row.status as\n | 'pending'\n | 'running'\n | 'succeeded'\n | 'failed',\n attempts: Number(row.attempts),\n runAt: Number(row.runAt),\n dedupeKey: row.dedupeKey,\n lastError: row.lastError,\n })),\n }\n },\n setJobsFacade(f: JobsRuntimeFacade) {\n ctx.jobs = f\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { RealtimePublisher } from './realtime/publisher.js';
|
|
2
|
+
import type { ResolvedBackend, ResolvedBucket } from './storage/buckets.js';
|
|
3
|
+
import type { StorageAdapter } from './storage/index.js';
|
|
4
|
+
export interface JobsPlatform {
|
|
5
|
+
/** A job became runnable at `runAt`. The host wakes a tick at that time. */
|
|
6
|
+
notify(runAt: number): void | Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export type RateLimitHit = {
|
|
9
|
+
allowed: boolean;
|
|
10
|
+
resetAt: number;
|
|
11
|
+
};
|
|
12
|
+
export interface RateLimitStore {
|
|
13
|
+
hit(key: string, windowMs: number, max: number, now?: number): Promise<RateLimitHit>;
|
|
14
|
+
}
|
|
15
|
+
export type StorageAdapterFactory = (backend: ResolvedBackend, bucket: ResolvedBucket) => StorageAdapter;
|
|
16
|
+
export interface Platform {
|
|
17
|
+
jobs: JobsPlatform;
|
|
18
|
+
/** Absent: the runtime uses a process-local memory publisher. */
|
|
19
|
+
realtime?: RealtimePublisher;
|
|
20
|
+
rateLimit: RateLimitStore;
|
|
21
|
+
storage: StorageAdapterFactory;
|
|
22
|
+
}
|
|
23
|
+
export declare function createMemoryRateLimitStore(): RateLimitStore;
|
|
24
|
+
export declare function resolvePlatform(partial?: Partial<Platform>): Platform;
|
|
25
|
+
//# sourceMappingURL=platform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIrD,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5C;AAED,MAAM,MAAM,YAAY,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhE,MAAM,WAAW,cAAc;IAC7B,GAAG,CACD,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,CAAC,CAAA;CACzB;AAED,MAAM,MAAM,qBAAqB,GAAG,CAClC,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,cAAc,KACnB,cAAc,CAAA;AAEnB,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,YAAY,CAAA;IAClB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,iBAAiB,CAAA;IAC5B,SAAS,EAAE,cAAc,CAAA;IACzB,OAAO,EAAE,qBAAqB,CAAA;CAC/B;AAED,wBAAgB,0BAA0B,IAAI,cAAc,CAa3D;AAED,wBAAgB,eAAe,CAAC,OAAO,GAAE,OAAO,CAAC,QAAQ,CAAM,GAAG,QAAQ,CAOzE"}
|
package/dist/platform.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createAdapter } from './storage/registry.js';
|
|
2
|
+
export function createMemoryRateLimitStore() {
|
|
3
|
+
const windows = new Map();
|
|
4
|
+
return {
|
|
5
|
+
async hit(key, windowMs, max, now = Date.now()) {
|
|
6
|
+
let window = windows.get(key);
|
|
7
|
+
if (!window || window.resetAt <= now) {
|
|
8
|
+
window = { count: 0, resetAt: now + windowMs };
|
|
9
|
+
windows.set(key, window);
|
|
10
|
+
}
|
|
11
|
+
window.count += 1;
|
|
12
|
+
return { allowed: window.count <= max, resetAt: window.resetAt };
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function resolvePlatform(partial = {}) {
|
|
17
|
+
return {
|
|
18
|
+
jobs: partial.jobs ?? { notify() { } },
|
|
19
|
+
realtime: partial.realtime,
|
|
20
|
+
rateLimit: partial.rateLimit ?? createMemoryRateLimitStore(),
|
|
21
|
+
storage: partial.storage ?? createAdapter,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AA+BlD,MAAM,UAAU,0BAA0B;IACxC,MAAM,OAAO,GAAG,IAAI,GAAG,EAA8C,CAAA;IACrE,OAAO;QACL,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;YAC5C,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAC7B,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,IAAI,GAAG,EAAE,CAAC;gBACrC,MAAM,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,GAAG,QAAQ,EAAE,CAAA;gBAC9C,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;YAC1B,CAAC;YACD,MAAM,CAAC,KAAK,IAAI,CAAC,CAAA;YACjB,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,IAAI,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAA;QAClE,CAAC;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,UAA6B,EAAE;IAC7D,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE,MAAM,KAAI,CAAC,EAAE;QACrC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,0BAA0B,EAAE;QAC5D,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,aAAa;KAC1C,CAAA;AACH,CAAC","sourcesContent":["// src/platform.ts — the services a host supplies to the core. A Worker gives\n// Durable Object backed ones; tests and Bun tooling get these defaults.\nimport type { RealtimePublisher } from './realtime/publisher'\nimport type { ResolvedBackend, ResolvedBucket } from './storage/buckets'\nimport type { StorageAdapter } from './storage/index'\n\nimport { createAdapter } from './storage/registry'\n\nexport interface JobsPlatform {\n /** A job became runnable at `runAt`. The host wakes a tick at that time. */\n notify(runAt: number): void | Promise<void>\n}\n\nexport type RateLimitHit = { allowed: boolean; resetAt: number }\n\nexport interface RateLimitStore {\n hit(\n key: string,\n windowMs: number,\n max: number,\n now?: number,\n ): Promise<RateLimitHit>\n}\n\nexport type StorageAdapterFactory = (\n backend: ResolvedBackend,\n bucket: ResolvedBucket,\n) => StorageAdapter\n\nexport interface Platform {\n jobs: JobsPlatform\n /** Absent: the runtime uses a process-local memory publisher. */\n realtime?: RealtimePublisher\n rateLimit: RateLimitStore\n storage: StorageAdapterFactory\n}\n\nexport function createMemoryRateLimitStore(): RateLimitStore {\n const windows = new Map<string, { count: number; resetAt: number }>()\n return {\n async hit(key, windowMs, max, now = Date.now()) {\n let window = windows.get(key)\n if (!window || window.resetAt <= now) {\n window = { count: 0, resetAt: now + windowMs }\n windows.set(key, window)\n }\n window.count += 1\n return { allowed: window.count <= max, resetAt: window.resetAt }\n },\n }\n}\n\nexport function resolvePlatform(partial: Partial<Platform> = {}): Platform {\n return {\n jobs: partial.jobs ?? { notify() {} },\n realtime: partial.realtime,\n rateLimit: partial.rateLimit ?? createMemoryRateLimitStore(),\n storage: partial.storage ?? createAdapter,\n }\n}\n"]}
|
package/dist/query/client.js
CHANGED
|
@@ -70,7 +70,7 @@ function attachFileHelpers(utils, options) {
|
|
|
70
70
|
throw new Error(`File upload failed (${uploaded.status})`);
|
|
71
71
|
}
|
|
72
72
|
const result = await procedures.confirmUpload.call({
|
|
73
|
-
id: prepared.fileId,
|
|
73
|
+
id: relativeId(property, prepared.fileId),
|
|
74
74
|
});
|
|
75
75
|
return { ...result, url: url(result.fileId), name: file.name };
|
|
76
76
|
};
|
package/dist/query/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/query/client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/query/client.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AACvC,OAAO,EAAE,WAAW,EAAuB,MAAM,SAAS,CAAA;AAmC1D,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;AACjC,CAAC;AAED,SAAS,UAAU,CAAC,MAAc,EAAE,UAAkB;IACpD,MAAM,MAAM,GAAG,GAAG,MAAM,GAAG,CAAA;IAC3B,OAAO,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC;QAClC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;QACjC,CAAC,CAAC,UAAU,CAAA;AAChB,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK;SACT,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACf,kBAAkB,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CACtE;SACA,IAAI,CAAC,GAAG,CAAC,CAAA;AACd,CAAC;AAED,SAAS,iBAAiB,CACxB,KAAQ,EACR,OAAsB;IAEtB,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,CAAA;IACpD,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IACxC,MAAM,KAAK,GAAI,KAAiC,CAAC,OAAO,CAE3C,CAAA;IACb,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IAExB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAmB,CAAA;IAC1C,MAAM,UAAU,GAAG,IAAI,KAAK,CAAC,KAAK,EAAE;QAClC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ;YAC5B,IAAI,OAAO,QAAQ,KAAK,QAAQ;gBAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;YAChD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YACpC,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAA;YAEzB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAGxD,CAAA;YACD,MAAM,IAAI,GAAG,GAAG,OAAO,UAAU,QAAQ,EAAE,CAAA;YAC3C,MAAM,GAAG,GAAG,CACV,UAAkB,EAClB,aAAmC,EAAE,EACrC,EAAE;gBACF,MAAM,IAAI,GAAG,cAAc,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAA;gBAC7D,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAA;gBACpC,IAAI,UAAU,CAAC,CAAC,KAAK,SAAS;oBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;gBACrE,IAAI,UAAU,CAAC,CAAC,KAAK,SAAS;oBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;gBACrE,IAAI,UAAU,CAAC,MAAM;oBAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;gBAC9D,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAA;gBAC/B,OAAO,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;YACrD,CAAC,CAAA;YACD,MAAM,MAAM,GAAG,KAAK,EAAE,IAAU,EAAyB,EAAE;gBACzD,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,aAAc,CAAC,IAAI,CAAC;oBACpD,QAAQ,EAAE,IAAI,CAAC,IAAI;oBACnB,WAAW,EAAE,IAAI,CAAC,IAAI,IAAI,SAAS;iBACpC,CAAC,CAAA;gBACF,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAC9B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAO,CAAC,IAAI,CAAC;wBAC3C,MAAM,EAAE,EAAE;wBACV,KAAK,EAAE,EAAE;wBACT,OAAO,EAAE,EAAE;wBACX,IAAI,EAAE,EAAE,IAAI,EAAE;qBACf,CAAC,CAAA;oBACF,OAAO;wBACL,GAAG,MAAM,CAAC,IAAI;wBACd,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;wBAC5B,IAAI,EAAE,IAAI,CAAC,IAAI;qBAChB,CAAA;gBACH,CAAC;gBAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE;oBAC/C,MAAM,EAAE,QAAQ,CAAC,MAAM;oBACvB,IAAI,EAAE,IAAI;oBACV,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS;iBAC/D,CAAC,CAAA;gBACF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;oBACjB,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;gBAC5D,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,aAAc,CAAC,IAAI,CAAC;oBAClD,EAAE,EAAE,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC;iBAC1C,CAAC,CAAA;gBACF,OAAO,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAA;YAChE,CAAC,CAAA;YAED,MAAM,UAAU,GAAG,CAAC,UAAkB,EAAE,EAAE,CACxC,UAAU,CAAC,MAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC,CAAA;YAErE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE;gBACtD,GAAG;gBACH,MAAM;gBACN,MAAM,EAAE,UAAU;aACnB,CAAC,CAAA;YACF,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YAC7B,OAAO,MAAM,CAAA;QACf,CAAC;KACF,CAAC,CAAA;IAEF,OAAO,IAAI,KAAK,CAAC,KAAK,EAAE;QACtB,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ;YAC5B,OAAO,QAAQ,KAAK,OAAO;gBACzB,CAAC,CAAC,UAAU;gBACZ,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;QAC7C,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,UAAyB,EAAE;IAE3B,OAAO,iBAAiB,CACtB,eAAe,CAAuB,OAAO,CAAC,EAC9C,OAAO,CACmB,CAAA;AAC9B,CAAC","sourcesContent":["import type { QueryClient } from '@tanstack/query-core'\n\nimport type { ApiQueryUtils } from './api'\nimport type { AnyBunderstackApp, InferApiRouter, InferBuckets } from './infer'\n\nimport { createApiClient } from './api'\nimport { createFetch, type TransportFetch } from './fetch'\n\nexport type ClientOptions = {\n baseUrl?: string\n fetch?: TransportFetch\n queryClient?: QueryClient\n}\n\nexport type FileTransformOptions = {\n w?: number\n h?: number\n format?: 'webp' | 'jpeg' | 'png' | 'avif'\n}\n\nexport type UploadedFile = {\n fileId: string\n url: string\n name: string\n}\n\nexport type FileBucketHelpers = {\n url(idOrFileId: string, transforms?: FileTransformOptions): string\n upload(file: File): Promise<UploadedFile>\n delete(idOrFileId: string): Promise<void>\n}\n\ntype FileHelpers<TBuckets extends string> = {\n files: { [K in TBuckets]: FileBucketHelpers }\n}\n\nexport type BunderstackClient<TApp extends AnyBunderstackApp> = ApiQueryUtils<\n InferApiRouter<TApp>\n> &\n FileHelpers<InferBuckets<TApp>>\n\nfunction trimSlash(value: string): string {\n return value.replace(/\\/$/, '')\n}\n\nfunction relativeId(bucket: string, idOrFileId: string): string {\n const prefix = `${bucket}/`\n return idOrFileId.startsWith(prefix)\n ? idOrFileId.slice(prefix.length)\n : idOrFileId\n}\n\nfunction encodeFilePath(value: string): string {\n return value\n .split('/')\n .map((segment) =>\n encodeURIComponent(decodeURIComponent(segment)).replace(/\\./g, '%2E'),\n )\n .join('/')\n}\n\nfunction attachFileHelpers<T extends object>(\n utils: T,\n options: ClientOptions,\n): T {\n const baseUrl = trimSlash(options.baseUrl ?? '/api')\n const fetch = createFetch(options.fetch)\n const files = (utils as Record<string, unknown>)['files'] as\n | Record<string, unknown>\n | undefined\n if (!files) return utils\n\n const buckets = new Map<string, unknown>()\n const filesProxy = new Proxy(files, {\n get(target, property, receiver) {\n if (typeof property !== 'string')\n return Reflect.get(target, property, receiver)\n const cached = buckets.get(property)\n if (cached) return cached\n\n const procedures = Reflect.get(target, property, receiver) as Record<\n string,\n { call(input: unknown): Promise<any> }\n >\n const root = `${baseUrl}/files/${property}`\n const url = (\n idOrFileId: string,\n transforms: FileTransformOptions = {},\n ) => {\n const path = encodeFilePath(relativeId(property, idOrFileId))\n const params = new URLSearchParams()\n if (transforms.w !== undefined) params.set('w', String(transforms.w))\n if (transforms.h !== undefined) params.set('h', String(transforms.h))\n if (transforms.format) params.set('format', transforms.format)\n const query = params.toString()\n return `${root}/${path}${query ? `?${query}` : ''}`\n }\n const upload = async (file: File): Promise<UploadedFile> => {\n const prepared = await procedures.prepareUpload!.call({\n filename: file.name,\n contentType: file.type || undefined,\n })\n if (prepared.mode === 'proxy') {\n const result = await procedures.upload!.call({\n params: {},\n query: {},\n headers: {},\n body: { file },\n })\n return {\n ...result.body,\n url: url(result.body.fileId),\n name: file.name,\n }\n }\n\n const uploaded = await fetch(prepared.uploadUrl, {\n method: prepared.method,\n body: file,\n headers: file.type ? { 'Content-Type': file.type } : undefined,\n })\n if (!uploaded.ok) {\n throw new Error(`File upload failed (${uploaded.status})`)\n }\n const result = await procedures.confirmUpload!.call({\n id: relativeId(property, prepared.fileId),\n })\n return { ...result, url: url(result.fileId), name: file.name }\n }\n\n const deleteFile = (idOrFileId: string) =>\n procedures.delete!.call({ path: relativeId(property, idOrFileId) })\n\n const bucket = Object.assign(Object.create(procedures), {\n url,\n upload,\n delete: deleteFile,\n })\n buckets.set(property, bucket)\n return bucket\n },\n })\n\n return new Proxy(utils, {\n get(target, property, receiver) {\n return property === 'files'\n ? filesProxy\n : Reflect.get(target, property, receiver)\n },\n })\n}\n\nexport function createClient<TApp extends AnyBunderstackApp>(\n options: ClientOptions = {},\n): BunderstackClient<TApp> {\n return attachFileHelpers(\n createApiClient<InferApiRouter<TApp>>(options),\n options,\n ) as BunderstackClient<TApp>\n}\n"]}
|
package/dist/rate-limit.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { type RateLimitStore } from './platform.js';
|
|
1
2
|
export type RateLimitConfig = {
|
|
2
3
|
windowMs?: number;
|
|
3
4
|
max?: number;
|
|
4
5
|
skip?: (req: Request) => boolean;
|
|
5
6
|
};
|
|
6
|
-
export declare function createRateLimiter(config: boolean | RateLimitConfig | undefined): (req: Request) => Promise<Response | null>;
|
|
7
|
+
export declare function createRateLimiter(config: boolean | RateLimitConfig | undefined, store?: RateLimitStore): (req: Request) => Promise<Response | null>;
|
|
7
8
|
//# sourceMappingURL=rate-limit.d.ts.map
|
package/dist/rate-limit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAA;CACjC,CAAA;
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,cAAc,EAAE,MAAM,YAAY,CAAA;AAE5E,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAA;CACjC,CAAA;AAkBD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,EAC7C,KAAK,GAAE,cAA6C,GACnD,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAgC5C"}
|
package/dist/rate-limit.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { createMemoryRateLimitStore } from './platform.js';
|
|
2
2
|
function resolveConfig(config) {
|
|
3
3
|
if (!config)
|
|
4
4
|
return null;
|
|
@@ -7,12 +7,15 @@ function resolveConfig(config) {
|
|
|
7
7
|
return config;
|
|
8
8
|
}
|
|
9
9
|
function clientKey(req) {
|
|
10
|
+
const cloudflare = req.headers.get('cf-connecting-ip');
|
|
11
|
+
if (cloudflare)
|
|
12
|
+
return cloudflare;
|
|
10
13
|
const forwarded = req.headers.get('x-forwarded-for');
|
|
11
14
|
if (forwarded)
|
|
12
15
|
return forwarded.split(',')[0].trim();
|
|
13
16
|
return req.headers.get('x-real-ip') ?? 'local';
|
|
14
17
|
}
|
|
15
|
-
export function createRateLimiter(config) {
|
|
18
|
+
export function createRateLimiter(config, store = createMemoryRateLimitStore()) {
|
|
16
19
|
const resolved = resolveConfig(config);
|
|
17
20
|
if (!resolved) {
|
|
18
21
|
return async (_req) => null;
|
|
@@ -24,26 +27,20 @@ export function createRateLimiter(config) {
|
|
|
24
27
|
return null;
|
|
25
28
|
const key = `${clientKey(req)}:${new URL(req.url).pathname}`;
|
|
26
29
|
const now = Date.now();
|
|
27
|
-
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
'Content-Type': 'application/json',
|
|
42
|
-
'Retry-After': String(retryAfter),
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
return null;
|
|
30
|
+
const { allowed, resetAt } = await store.hit(key, windowMs, max, now);
|
|
31
|
+
if (allowed)
|
|
32
|
+
return null;
|
|
33
|
+
const retryAfter = Math.max(1, Math.ceil((resetAt - now) / 1000));
|
|
34
|
+
return new Response(JSON.stringify({
|
|
35
|
+
error: 'Too many requests',
|
|
36
|
+
code: 'TOO_MANY_REQUESTS',
|
|
37
|
+
}), {
|
|
38
|
+
status: 429,
|
|
39
|
+
headers: {
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
'Retry-After': String(retryAfter),
|
|
42
|
+
},
|
|
43
|
+
});
|
|
47
44
|
};
|
|
48
45
|
}
|
|
49
46
|
//# sourceMappingURL=rate-limit.js.map
|
package/dist/rate-limit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAuB,MAAM,YAAY,CAAA;AAQ5E,SAAS,aAAa,CACpB,MAA6C;IAE7C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IACxB,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,EAAE,CAAA;IAC9B,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,SAAS,CAAC,GAAY;IAC7B,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;IACtD,IAAI,UAAU;QAAE,OAAO,UAAU,CAAA;IACjC,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;IACpD,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAA;IACrD,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,OAAO,CAAA;AAChD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,MAA6C,EAC7C,QAAwB,0BAA0B,EAAE;IAEpD,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;IACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,KAAK,EAAE,IAAa,EAAE,EAAE,CAAC,IAAI,CAAA;IACtC,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,IAAI,MAAM,CAAA;IAC5C,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,IAAI,GAAG,CAAA;IAE/B,OAAO,KAAK,EAAE,GAAY,EAA4B,EAAE;QACtD,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QAErC,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAA;QAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;QACrE,IAAI,OAAO;YAAE,OAAO,IAAI,CAAA;QAExB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;QACjE,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;YACb,KAAK,EAAE,mBAAmB;YAC1B,IAAI,EAAE,mBAAmB;SAC1B,CAAC,EACF;YACE,MAAM,EAAE,GAAG;YACX,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,MAAM,CAAC,UAAU,CAAC;aAClC;SACF,CACF,CAAA;IACH,CAAC,CAAA;AACH,CAAC","sourcesContent":["import { createMemoryRateLimitStore, type RateLimitStore } from './platform'\n\nexport type RateLimitConfig = {\n windowMs?: number\n max?: number\n skip?: (req: Request) => boolean\n}\n\nfunction resolveConfig(\n config: boolean | RateLimitConfig | undefined,\n): RateLimitConfig | null {\n if (!config) return null\n if (config === true) return {}\n return config\n}\n\nfunction clientKey(req: Request): string {\n const cloudflare = req.headers.get('cf-connecting-ip')\n if (cloudflare) return cloudflare\n const forwarded = req.headers.get('x-forwarded-for')\n if (forwarded) return forwarded.split(',')[0]!.trim()\n return req.headers.get('x-real-ip') ?? 'local'\n}\n\nexport function createRateLimiter(\n config: boolean | RateLimitConfig | undefined,\n store: RateLimitStore = createMemoryRateLimitStore(),\n): (req: Request) => Promise<Response | null> {\n const resolved = resolveConfig(config)\n if (!resolved) {\n return async (_req: Request) => null\n }\n\n const windowMs = resolved.windowMs ?? 60_000\n const max = resolved.max ?? 100\n\n return async (req: Request): Promise<Response | null> => {\n if (resolved.skip?.(req)) return null\n\n const key = `${clientKey(req)}:${new URL(req.url).pathname}`\n const now = Date.now()\n const { allowed, resetAt } = await store.hit(key, windowMs, max, now)\n if (allowed) return null\n\n const retryAfter = Math.max(1, Math.ceil((resetAt - now) / 1000))\n return new Response(\n JSON.stringify({\n error: 'Too many requests',\n code: 'TOO_MANY_REQUESTS',\n }),\n {\n status: 429,\n headers: {\n 'Content-Type': 'application/json',\n 'Retry-After': String(retryAfter),\n },\n },\n )\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type InferSelectModel, type Table } from 'drizzle-orm';
|
|
2
2
|
import type { RealtimeAction, RealtimePublisher } from './publisher.js';
|
|
3
|
-
export type RealtimeTransport = 'disabled' | 'memory' | '
|
|
3
|
+
export type RealtimeTransport = 'disabled' | 'memory' | 'platform';
|
|
4
4
|
export type SchemaTable<TSchema extends Record<string, unknown>> = Extract<TSchema[keyof TSchema], Table>;
|
|
5
5
|
export interface RealtimeFacade<TSchema extends Record<string, unknown> = Record<string, unknown>> {
|
|
6
6
|
readonly enabled: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facade.d.ts","sourceRoot":"","sources":["../../src/realtime/facade.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,KAAK,EACX,MAAM,aAAa,CAAA;AAEpB,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEpE,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,QAAQ,GAAG,
|
|
1
|
+
{"version":3,"file":"facade.d.ts","sourceRoot":"","sources":["../../src/realtime/facade.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,KAAK,EACX,MAAM,aAAa,CAAA;AAEpB,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEpE,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAA;AAElE,MAAM,MAAM,WAAW,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,OAAO,CACxE,OAAO,CAAC,MAAM,OAAO,CAAC,EACtB,KAAK,CACN,CAAA;AAED,MAAM,WAAW,cAAc,CAC7B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEjE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAA;IAErC,OAAO,CAAC,MAAM,SAAS,WAAW,CAAC,OAAO,CAAC,EACzC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAChC,QAAQ,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAClC,OAAO,CAAC,IAAI,CAAC,CAAA;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,SAAS,CAAC,EAAE,iBAAiB,EAC7B,SAAS,GAAE,iBAAqD,EAChE,MAAM,CAAC,EAAE,OAAO,GACf,cAAc,CAAC,OAAO,CAAC,CA+BzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facade.js","sourceRoot":"","sources":["../../src/realtime/facade.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,GAGR,MAAM,aAAa,CAAA;AAyBpB;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,SAA6B,EAC7B,YAA+B,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,EAChE,MAAgB;IAEhB,IAAI,CAAC,SAAS,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,kEAAkE,CACnE,CAAA;IACH,CAAC;IACD,IAAI,SAAS,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAA;IACH,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAA;IAChD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;QACxD,IAAI,OAAO,CAAC,KAAK,CAAC;YAAE,cAAc,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAA;IAClE,CAAC;IAED,OAAO;QACL,OAAO,EAAE,SAAS,KAAK,SAAS;QAChC,SAAS;QACT,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ;YAC3C,IAAI,CAAC,SAAS;gBAAE,OAAM;YACtB,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;YACrC,MAAM,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE;gBAChC,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,SAAS;gBACjD,MAAM;gBACN,MAAM,EAAE,MAA4C;gBACpD,GAAG,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxE,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["import {\n getTableName,\n isTable,\n type InferSelectModel,\n type Table,\n} from 'drizzle-orm'\n\nimport type { RealtimeAction, RealtimePublisher } from './publisher'\n\nexport type RealtimeTransport = 'disabled' | 'memory' | '
|
|
1
|
+
{"version":3,"file":"facade.js","sourceRoot":"","sources":["../../src/realtime/facade.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,GAGR,MAAM,aAAa,CAAA;AAyBpB;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,SAA6B,EAC7B,YAA+B,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,EAChE,MAAgB;IAEhB,IAAI,CAAC,SAAS,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,kEAAkE,CACnE,CAAA;IACH,CAAC;IACD,IAAI,SAAS,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAA;IACH,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAA;IAChD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;QACxD,IAAI,OAAO,CAAC,KAAK,CAAC;YAAE,cAAc,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAA;IAClE,CAAC;IAED,OAAO;QACL,OAAO,EAAE,SAAS,KAAK,SAAS;QAChC,SAAS;QACT,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ;YAC3C,IAAI,CAAC,SAAS;gBAAE,OAAM;YACtB,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;YACrC,MAAM,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE;gBAChC,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,SAAS;gBACjD,MAAM;gBACN,MAAM,EAAE,MAA4C;gBACpD,GAAG,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxE,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["import {\n getTableName,\n isTable,\n type InferSelectModel,\n type Table,\n} from 'drizzle-orm'\n\nimport type { RealtimeAction, RealtimePublisher } from './publisher'\n\nexport type RealtimeTransport = 'disabled' | 'memory' | 'platform'\n\nexport type SchemaTable<TSchema extends Record<string, unknown>> = Extract<\n TSchema[keyof TSchema],\n Table\n>\n\nexport interface RealtimeFacade<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n> {\n readonly enabled: boolean\n readonly transport: RealtimeTransport\n\n publish<TTable extends SchemaTable<TSchema>>(\n table: TTable,\n action: RealtimeAction,\n record: InferSelectModel<TTable>,\n metadata?: { operationId?: string },\n ): Promise<void>\n}\n\n/**\n * One name for a table everywhere: events, subscriptions, and the CRUD router\n * all use the schema key. Pass the schema so a key like `creditBalances` is not\n * published as its SQL name `credit_balances` — clients subscribe with the key\n * they call procedures with. Without a schema, the SQL name is the only name\n * available and is used as-is.\n */\nexport function createRealtimeFacade<TSchema extends Record<string, unknown>>(\n publisher?: RealtimePublisher,\n transport: RealtimeTransport = publisher ? 'memory' : 'disabled',\n schema?: TSchema,\n): RealtimeFacade<TSchema> {\n if (!publisher && transport !== 'disabled') {\n throw new Error(\n '[bunderstack] an enabled realtime transport requires a publisher',\n )\n }\n if (publisher && transport === 'disabled') {\n throw new Error(\n '[bunderstack] a realtime publisher cannot use the disabled transport',\n )\n }\n\n const keyByTableName = new Map<string, string>()\n for (const [key, value] of Object.entries(schema ?? {})) {\n if (isTable(value)) keyByTableName.set(getTableName(value), key)\n }\n\n return {\n enabled: publisher !== undefined,\n transport,\n async publish(table, action, record, metadata) {\n if (!publisher) return\n const tableName = getTableName(table)\n await publisher.publish('change', {\n table: keyByTableName.get(tableName) ?? tableName,\n action,\n record: record as unknown as Record<string, unknown>,\n ...(metadata?.operationId ? { operationId: metadata.operationId } : {}),\n })\n },\n }\n}\n"]}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Publisher } from '@orpc/publisher';
|
|
2
|
-
import type { RedisClient } from 'bun';
|
|
3
2
|
export type RealtimeAction = 'create' | 'update' | 'delete';
|
|
4
3
|
export interface RealtimeChange {
|
|
5
4
|
table: string;
|
|
@@ -17,7 +16,4 @@ export interface RealtimePublisherOptions {
|
|
|
17
16
|
resumeSeconds?: number;
|
|
18
17
|
}
|
|
19
18
|
export declare function createMemoryRealtimePublisher(options?: RealtimePublisherOptions): RealtimePublisher;
|
|
20
|
-
export declare function createRedisRealtimePublisher(redis: RedisClient, subscriber: RedisClient | Promise<RedisClient>, options?: RealtimePublisherOptions & {
|
|
21
|
-
prefix?: string;
|
|
22
|
-
}): RealtimePublisher;
|
|
23
19
|
//# sourceMappingURL=publisher.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publisher.d.ts","sourceRoot":"","sources":["../../src/realtime/publisher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"publisher.d.ts","sourceRoot":"","sources":["../../src/realtime/publisher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAIhD,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE3D,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,cAAc,CAAA;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,cAAe,SAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAC5D,MAAM,EAAE,cAAc,CAAA;CACvB;AAED,MAAM,MAAM,iBAAiB,GAAG,SAAS,CAAC,cAAc,CAAC,CAAA;AAEzD,MAAM,WAAW,wBAAwB;IACvC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,wBAA6B,GACrC,iBAAiB,CAKnB"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { BunRedisPublisher } from '@orpc/bun';
|
|
2
1
|
import { MemoryPublisher } from '@orpc/publisher/memory';
|
|
3
2
|
export function createMemoryRealtimePublisher(options = {}) {
|
|
4
3
|
return new MemoryPublisher({
|
|
@@ -6,12 +5,4 @@ export function createMemoryRealtimePublisher(options = {}) {
|
|
|
6
5
|
resume: { enabled: true, seconds: options.resumeSeconds ?? 300 },
|
|
7
6
|
});
|
|
8
7
|
}
|
|
9
|
-
export function createRedisRealtimePublisher(redis, subscriber, options = {}) {
|
|
10
|
-
return new BunRedisPublisher(redis, {
|
|
11
|
-
subscriber,
|
|
12
|
-
prefix: options.prefix ?? 'bunderstack:',
|
|
13
|
-
maxBufferedEvents: options.maxBufferedEvents,
|
|
14
|
-
resume: { enabled: true, seconds: options.resumeSeconds ?? 300 },
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
8
|
//# sourceMappingURL=publisher.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publisher.js","sourceRoot":"","sources":["../../src/realtime/publisher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"publisher.js","sourceRoot":"","sources":["../../src/realtime/publisher.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAuBxD,MAAM,UAAU,6BAA6B,CAC3C,UAAoC,EAAE;IAEtC,OAAO,IAAI,eAAe,CAAiB;QACzC,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;QAC5C,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,aAAa,IAAI,GAAG,EAAE;KACjE,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import type { Publisher } from '@orpc/publisher'\n\nimport { MemoryPublisher } from '@orpc/publisher/memory'\n\nexport type RealtimeAction = 'create' | 'update' | 'delete'\n\nexport interface RealtimeChange {\n table: string\n action: RealtimeAction\n record: Record<string, unknown>\n /** Correlates this confirmed write with the client operation that caused it. */\n operationId?: string\n}\n\nexport interface RealtimeEvents extends Record<string, object> {\n change: RealtimeChange\n}\n\nexport type RealtimePublisher = Publisher<RealtimeEvents>\n\nexport interface RealtimePublisherOptions {\n maxBufferedEvents?: number\n resumeSeconds?: number\n}\n\nexport function createMemoryRealtimePublisher(\n options: RealtimePublisherOptions = {},\n): RealtimePublisher {\n return new MemoryPublisher<RealtimeEvents>({\n maxBufferedEvents: options.maxBufferedEvents,\n resume: { enabled: true, seconds: options.resumeSeconds ?? 300 },\n })\n}\n"]}
|