Service: job event stream, async in-memory backend, preview routes
Ported from PR #15 (packages-side surface only; the fork's dev UI page is intentionally dropped - the API surface is the product): - job_events table + repository, worker emits stage transitions, additive GET /v1/clones/:id/events polling route. The fork's hand-written migration was never registered in Drizzle's journal (it silently never applied); regenerated properly via drizzle-kit from schema.ts with the (job_id, seq) index declared, and verified against a live Postgres - In-memory backend moves to the documented enqueue-202 + poll contract with single-flight BUSY guard; POST honors backend httpStatus - app-preview static serving routes; the preview BUILD half is deferred until the compiler exports buildApp/DEFAULT_HARNESS_DIR - routes 404 cleanly until then - preview option threaded through core types; cache key intentionally unchanged while the flag is inert All workspaces typecheck; api 18/18, core 13/13, worker 1/1 pass; migration verified applying (table + index + FK) on postgres:16. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
f991f469cf
commit
bbe4a5643b
+22
-1
@@ -1,6 +1,6 @@
|
||||
import { and, desc, eq, gt, isNull, sql } from "drizzle-orm";
|
||||
import type { Db } from "./client.js";
|
||||
import { jobs, clones, cache, apiKeys, signupTokens, type Job, type NewJob, type Clone, type NewClone, type CacheRow, type ApiKey, type SignupToken } from "./schema.js";
|
||||
import { jobs, clones, cache, apiKeys, signupTokens, jobEvents, type Job, type NewJob, type Clone, type NewClone, type CacheRow, type ApiKey, type SignupToken } from "./schema.js";
|
||||
|
||||
// ---- jobs ----
|
||||
|
||||
@@ -111,3 +111,24 @@ export async function consumeSignupToken(db: Db, tokenHash: string): Promise<Sig
|
||||
.returning();
|
||||
return row;
|
||||
}
|
||||
|
||||
// ---- job events ----
|
||||
|
||||
export async function appendJobEvent(db: Db, jobId: string, payload: Record<string, unknown>): Promise<number> {
|
||||
const [row] = await db
|
||||
.select({ n: sql<number>`coalesce(max(${jobEvents.seq}), 0)` })
|
||||
.from(jobEvents)
|
||||
.where(eq(jobEvents.jobId, jobId));
|
||||
const seq = (row?.n ?? 0) + 1;
|
||||
await db.insert(jobEvents).values({ jobId, seq, payload: { t: Date.now(), ...payload } });
|
||||
return seq;
|
||||
}
|
||||
|
||||
export async function listJobEvents(db: Db, jobId: string, after = 0): Promise<Array<Record<string, unknown>>> {
|
||||
const rows = await db
|
||||
.select()
|
||||
.from(jobEvents)
|
||||
.where(and(eq(jobEvents.jobId, jobId), sql`${jobEvents.seq} > ${after}`))
|
||||
.orderBy(jobEvents.seq);
|
||||
return rows.map((r) => r.payload as Record<string, unknown>);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { pgTable, text, jsonb, integer, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||
import { pgTable, text, jsonb, integer, timestamp, uuid, index } from "drizzle-orm/pg-core";
|
||||
|
||||
/** Job lifecycle row. Status mirrors the queue; `cacheKey` ties it to the cache row.
|
||||
* `options`/`timings` are jsonb (the service's typed shapes). */
|
||||
@@ -66,6 +66,17 @@ export const signupTokens = pgTable("signup_tokens", {
|
||||
consumedAt: timestamp("consumed_at", { withTimezone: true }),
|
||||
});
|
||||
|
||||
/** Structured clone progress events (mirrors in-memory backend event stream). */
|
||||
export const jobEvents = pgTable("job_events", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
jobId: uuid("job_id")
|
||||
.notNull()
|
||||
.references(() => jobs.id, { onDelete: "cascade" }),
|
||||
seq: integer("seq").notNull(),
|
||||
payload: jsonb("payload").notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
}, (t) => [index("job_events_job_id_seq_idx").on(t.jobId, t.seq)]);
|
||||
|
||||
export type Job = typeof jobs.$inferSelect;
|
||||
export type NewJob = typeof jobs.$inferInsert;
|
||||
export type Clone = typeof clones.$inferSelect;
|
||||
@@ -74,3 +85,4 @@ export type CacheRow = typeof cache.$inferSelect;
|
||||
export type ApiKey = typeof apiKeys.$inferSelect;
|
||||
export type SignupToken = typeof signupTokens.$inferSelect;
|
||||
export type NewSignupToken = typeof signupTokens.$inferInsert;
|
||||
export type JobEvent = typeof jobEvents.$inferSelect;
|
||||
|
||||
Reference in New Issue
Block a user