@cosmicdrift/kumiko-framework 0.146.1 → 0.146.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-framework",
3
- "version": "0.146.1",
3
+ "version": "0.146.3",
4
4
  "description": "Framework core — engine, pipeline, API, DB, and every other bit that makes Kumiko go.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -189,7 +189,7 @@
189
189
  "zod": "^4.4.3"
190
190
  },
191
191
  "devDependencies": {
192
- "@cosmicdrift/kumiko-dispatcher-live": "0.146.1",
192
+ "@cosmicdrift/kumiko-dispatcher-live": "0.146.3",
193
193
  "bun-types": "^1.3.13",
194
194
  "pino-pretty": "^13.1.3"
195
195
  },
@@ -2,7 +2,7 @@ import type { Context } from "hono";
2
2
  import { Hono } from "hono";
3
3
  import { deleteCookie, setCookie } from "hono/cookie";
4
4
  import { z } from "zod";
5
- import { stripForbiddenMembershipRoles } from "../engine/membership-roles";
5
+ import { buildSessionRoles } from "../engine/membership-roles";
6
6
  import { createSystemUser } from "../engine/system-user";
7
7
  import { type SessionUser, SYSTEM_TENANT_ID, type TenantId } from "../engine/types";
8
8
  import { NotFoundError } from "../errors";
@@ -870,13 +870,12 @@ export function createAuthRoutes(
870
870
  // tenant A accidentally surviving into tenant B's session). The
871
871
  // resolver runs each feature's r.authClaims() hook under the new
872
872
  // TenantDb scope.
873
- // Strip reserved roles from the membership portion only — globalRoles
873
+ // buildSessionRoles calls stripForbiddenMembershipRoles internally and
874
+ // strips reserved roles from the membership side only — globalRoles
874
875
  // (where SystemAdmin legitimately lives) is never filtered. Backstop for a
875
876
  // membership role that a projection rebuild resurrected past command-time
876
877
  // validation (see engine/membership-roles).
877
- const mergedRoles = Array.from(
878
- new Set([...globalRoles, ...stripForbiddenMembershipRoles(membership.roles)]),
879
- );
878
+ const mergedRoles = buildSessionRoles(globalRoles, membership.roles);
880
879
  const targetSession: SessionUser = {
881
880
  id: user.id,
882
881
  tenantId: targetTenantId,
@@ -103,14 +103,14 @@ export const myHandler = defineWriteHandler({
103
103
  `;
104
104
 
105
105
  const alreadyPipelineContent = `\
106
- import { access, defineWriteHandler, pipeline } from "@cosmicdrift/kumiko-framework/engine";
106
+ import { access, defineWriteHandler, stepsPipeline } from "@cosmicdrift/kumiko-framework/engine";
107
107
  import { z } from "zod";
108
108
 
109
109
  export const myHandler = defineWriteHandler({
110
110
  name: "test:pipeline",
111
111
  schema: z.object({}),
112
112
  access: { roles: access.authenticated },
113
- perform: pipeline(({ event, r }) => [
113
+ perform: stepsPipeline(({ event, r }) => [
114
114
  r.step.return((ctx) => ({ isSuccess: true, data: { ok: true } })),
115
115
  ]),
116
116
  });
@@ -173,14 +173,14 @@ describe("analyzeFile", () => {
173
173
 
174
174
  describe("convertFile", () => {
175
175
  describe("static return handler", () => {
176
- it("converts handler to perform: pipeline(...)", async () => {
176
+ it("converts handler to perform: stepsPipeline(...)", async () => {
177
177
  const p = writeFixture("static-convert.write.ts", staticReturnContent);
178
178
  const result = await convertFile(p);
179
179
  expect(result.status).toBe("converted");
180
180
  const content = readFileSync(p, "utf8");
181
- expect(content).toContain("perform: pipeline");
181
+ expect(content).toContain("perform: stepsPipeline");
182
182
  expect(content).toContain("r.step.return");
183
- expect(content).toContain("import { access, defineWriteHandler, pipeline }");
183
+ expect(content).toContain("import { access, defineWriteHandler, stepsPipeline }");
184
184
  expect(content).not.toContain("handler:");
185
185
  });
186
186
 
@@ -198,7 +198,7 @@ export const h = defineWriteHandler({
198
198
  const result = await convertFile(p);
199
199
  expect(result.status).toBe("converted");
200
200
  const converted = readFileSync(p, "utf8");
201
- expect(converted).toContain("pipeline<typeof MySchema, unknown>");
201
+ expect(converted).toContain("stepsPipeline<typeof MySchema, unknown>");
202
202
  });
203
203
  });
204
204
 
@@ -271,12 +271,12 @@ export const h = defineWriteHandler({
271
271
  const result = await convertFile(p);
272
272
  expect(result.status).toBe("converted");
273
273
  const content = readFileSync(p, "utf8");
274
- expect(content).toContain("import { access, defineWriteHandler, pipeline }");
274
+ expect(content).toContain("import { access, defineWriteHandler, stepsPipeline }");
275
275
  });
276
276
 
277
277
  it("does not duplicate pipeline import when already present", async () => {
278
278
  const content = `\
279
- import { access, defineWriteHandler, pipeline } from "@cosmicdrift/kumiko-framework/engine";
279
+ import { access, defineWriteHandler, stepsPipeline } from "@cosmicdrift/kumiko-framework/engine";
280
280
  import { z } from "zod";
281
281
 
282
282
  export const h = defineWriteHandler({
@@ -288,7 +288,9 @@ export const h = defineWriteHandler({
288
288
  const result = await convertFile(p);
289
289
  expect(result.status).toBe("converted");
290
290
  const final = readFileSync(p, "utf8").split("\n");
291
- const pipelineImports = final.filter((l) => l.includes("import") && l.includes("pipeline"));
291
+ const pipelineImports = final.filter(
292
+ (l) => l.includes("import") && l.includes("stepsPipeline"),
293
+ );
292
294
  // Only one import line should contain "pipeline"
293
295
  expect(pipelineImports.length).toBe(1);
294
296
  });
@@ -513,14 +515,14 @@ describe("generatePerformBlock", () => {
513
515
  it("generates pipeline block for static return", () => {
514
516
  const analysis = analyzeHandlerArrow("async () => ({ isSuccess: true, data: { ok: true } })");
515
517
  const block = generatePerformBlock(analysis, "", " ");
516
- expect(block).toContain("perform: pipeline(");
518
+ expect(block).toContain("perform: stepsPipeline(");
517
519
  expect(block).toContain("r.step.return((ctx) => ({ isSuccess: true, data: { ok: true } })");
518
520
  });
519
521
 
520
522
  it("generates pipeline block with schema type parameter", () => {
521
523
  const analysis = analyzeHandlerArrow("async () => ({ isSuccess: true, data: { ok: true } })");
522
524
  const block = generatePerformBlock(analysis, "typeof InvoiceSchema", " ");
523
- expect(block).toContain("pipeline<typeof InvoiceSchema, unknown>");
525
+ expect(block).toContain("stepsPipeline<typeof InvoiceSchema, unknown>");
524
526
  });
525
527
 
526
528
  it("returns null for non-convertible analysis", () => {
@@ -1,4 +1,4 @@
1
- // Pipeline-engine unit tests — defineWriteHandler({ perform: pipeline(...) })
1
+ // Pipeline-engine unit tests — defineWriteHandler({ perform: stepsPipeline(...) })
2
2
  // boundary, run-pipeline runner contract, defineStep registry guards.
3
3
  // Sub-step-builders (branch, forEach) live in pipeline-sub-pipelines.test.ts;
4
4
  // boot-validator in validate-projection-allowlist.test.ts.
@@ -9,7 +9,7 @@ import { z } from "zod";
9
9
  import { TestUsers } from "../../stack";
10
10
  import { defineWriteHandler } from "../define-handler";
11
11
  import { defineStep } from "../define-step";
12
- import { pipeline } from "../pipeline";
12
+ import { stepsPipeline } from "../pipeline";
13
13
  import type { WriteEvent } from "../types/handlers";
14
14
  import { buildMinimalCtx } from "./_pipeline-test-utils";
15
15
 
@@ -19,7 +19,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
19
19
  name: "demo:noop",
20
20
  schema: z.object({ greeting: z.string() }),
21
21
  access: { roles: ["User"] },
22
- perform: pipeline<{ greeting: string }, { echoed: string }>(({ event, r }) => [
22
+ perform: stepsPipeline<{ greeting: string }, { echoed: string }>(({ event, r }) => [
23
23
  r.step.return(() => ({
24
24
  isSuccess: true as const,
25
25
  data: { echoed: event.payload.greeting },
@@ -49,7 +49,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
49
49
  name: "demo:static",
50
50
  schema: z.object({}),
51
51
  access: { roles: ["User"] },
52
- perform: pipeline<Record<string, never>, { ok: boolean }>(({ r }) => [
52
+ perform: stepsPipeline<Record<string, never>, { ok: boolean }>(({ r }) => [
53
53
  r.step.return({ isSuccess: true as const, data: { ok: true } }),
54
54
  ]),
55
55
  });
@@ -89,7 +89,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
89
89
  name: "demo:no-return",
90
90
  schema: z.object({}),
91
91
  access: { roles: ["User"] },
92
- perform: pipeline<Record<string, never>, never>(() => []),
92
+ perform: stepsPipeline<Record<string, never>, never>(() => []),
93
93
  });
94
94
 
95
95
  await expect(
@@ -105,7 +105,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
105
105
  name: "demo:thread",
106
106
  schema: z.object({ base: z.number() }),
107
107
  access: { roles: ["User"] },
108
- perform: pipeline<{ base: number }, { sum: number }>(({ event, r }) => [
108
+ perform: stepsPipeline<{ base: number }, { sum: number }>(({ event, r }) => [
109
109
  r.step.compute("offset", () => 10),
110
110
  r.step.compute("doubledBase", () => event.payload.base * 2),
111
111
  r.step.return(({ steps }) => ({
@@ -133,7 +133,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
133
133
  name: "demo:fresh",
134
134
  schema: z.object({}),
135
135
  access: { roles: ["User"] },
136
- perform: pipeline<Record<string, never>, { tick: number }>(({ r }) => [
136
+ perform: stepsPipeline<Record<string, never>, { tick: number }>(({ r }) => [
137
137
  r.step.compute("tick", () => ++counter),
138
138
  r.step.return(({ steps }) => ({
139
139
  isSuccess: true as const,
@@ -161,7 +161,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
161
161
  name: "demo:unknown-kind",
162
162
  schema: z.object({}),
163
163
  access: { roles: ["User"] },
164
- perform: pipeline<Record<string, never>, never>(() => [
164
+ perform: stepsPipeline<Record<string, never>, never>(() => [
165
165
  // Hand-crafted instance with a kind that's never been registered —
166
166
  // simulates a typo in a future step-builder factory.
167
167
  { kind: "this-step-does-not-exist", args: {} },
@@ -190,7 +190,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
190
190
  schema: z.object({}),
191
191
  access: { roles: ["User"] },
192
192
  handler: async () => ({ isSuccess: true as const, data: {} }),
193
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
193
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
194
194
  r.step.return({ isSuccess: true as const, data: { ok: true } }),
195
195
  ]),
196
196
  } as unknown as Parameters<typeof defineWriteHandler>[0];
@@ -60,7 +60,7 @@ import {
60
60
  import { defineFeature } from "../define-feature";
61
61
  import { defineWriteHandler } from "../define-handler";
62
62
  import { createEntity, createTextField } from "../factories";
63
- import { pipeline } from "../pipeline";
63
+ import { stepsPipeline } from "../pipeline";
64
64
 
65
65
  const echoSchema = z.object({ greeting: z.string() });
66
66
 
@@ -70,7 +70,7 @@ const echoHandler = defineWriteHandler({
70
70
  name: "echo",
71
71
  schema: echoSchema,
72
72
  access: { roles: ["Admin"] },
73
- perform: pipeline<z.infer<typeof echoSchema>, { echoed: string; from: string }>(
73
+ perform: stepsPipeline<z.infer<typeof echoSchema>, { echoed: string; from: string }>(
74
74
  ({ event, r }) => [
75
75
  r.step.return(() => ({
76
76
  isSuccess: true as const,
@@ -90,7 +90,7 @@ const explodeHandler = defineWriteHandler({
90
90
  name: "explode",
91
91
  schema: explodeSchema,
92
92
  access: { roles: ["Admin"] },
93
- perform: pipeline<z.infer<typeof explodeSchema>, never>(({ r }) => [
93
+ perform: stepsPipeline<z.infer<typeof explodeSchema>, never>(({ r }) => [
94
94
  r.step.return(() => {
95
95
  throw new Error("boom");
96
96
  }),
@@ -106,7 +106,7 @@ const compoundHandler = defineWriteHandler({
106
106
  name: "compound",
107
107
  schema: compoundSchema,
108
108
  access: { roles: ["Admin"] },
109
- perform: pipeline<z.infer<typeof compoundSchema>, { sum: number; userId: string }>(
109
+ perform: stepsPipeline<z.infer<typeof compoundSchema>, { sum: number; userId: string }>(
110
110
  ({ event, r }) => [
111
111
  r.step.compute("offset", () => 100),
112
112
  r.step.compute("doubledBase", () => event.payload.base * 2),
@@ -147,7 +147,7 @@ const logHandler = defineWriteHandler({
147
147
  schema: logSchema,
148
148
  access: { roles: ["Admin"] },
149
149
  // Outer-`event`-capture pattern — see compoundHandler above for the why.
150
- perform: pipeline<z.infer<typeof logSchema>, { correlationId: string }>(({ event, r }) => [
150
+ perform: stepsPipeline<z.infer<typeof logSchema>, { correlationId: string }>(({ event, r }) => [
151
151
  r.step.unsafeProjectionUpsert({
152
152
  table: pipelineDemoLogTable,
153
153
  on: ["correlationId"],
@@ -182,7 +182,7 @@ const widgetCreateHandler = defineWriteHandler({
182
182
  name: "widget:create",
183
183
  schema: widgetSchema,
184
184
  access: { roles: ["Admin"] },
185
- perform: pipeline<z.infer<typeof widgetSchema>, { id: string }>(({ event, r }) => [
185
+ perform: stepsPipeline<z.infer<typeof widgetSchema>, { id: string }>(({ event, r }) => [
186
186
  r.step.aggregate.create("widget", {
187
187
  executor: widgetExecutor,
188
188
  data: () => ({ label: event.payload.label }),
@@ -202,7 +202,7 @@ const annotateHandler = defineWriteHandler({
202
202
  name: "widget:annotate",
203
203
  schema: annotateSchema,
204
204
  access: { roles: ["Admin"] },
205
- perform: pipeline<z.infer<typeof annotateSchema>, { id: string }>(({ event, r }) => [
205
+ perform: stepsPipeline<z.infer<typeof annotateSchema>, { id: string }>(({ event, r }) => [
206
206
  r.step.aggregate.create("widget", {
207
207
  executor: widgetExecutor,
208
208
  data: () => ({ label: event.payload.label }),
@@ -230,7 +230,7 @@ const widgetUpdateHandler = defineWriteHandler({
230
230
  name: "widget:update",
231
231
  schema: widgetUpdateSchema,
232
232
  access: { roles: ["Admin"] },
233
- perform: pipeline<z.infer<typeof widgetUpdateSchema>, { id: string }>(({ event, r }) => [
233
+ perform: stepsPipeline<z.infer<typeof widgetUpdateSchema>, { id: string }>(({ event, r }) => [
234
234
  r.step.aggregate.update("widget", {
235
235
  executor: widgetExecutor,
236
236
  id: () => event.payload.id,
@@ -254,7 +254,7 @@ const widgetBrokenHandler = defineWriteHandler({
254
254
  name: "widget:create-broken",
255
255
  schema: z.object({}),
256
256
  access: { roles: ["Admin"] },
257
- perform: pipeline<Record<string, never>, { id: string }>(({ r }) => [
257
+ perform: stepsPipeline<Record<string, never>, { id: string }>(({ r }) => [
258
258
  r.step.aggregate.create("widget", {
259
259
  executor: widgetExecutor,
260
260
  // Intentionally omits the `label` field that the entity declares
@@ -278,7 +278,7 @@ const widgetCreateThenThrowHandler = defineWriteHandler({
278
278
  name: "widget:create-then-throw",
279
279
  schema: z.object({ label: z.string() }),
280
280
  access: { roles: ["Admin"] },
281
- perform: pipeline<{ label: string }, never>(({ event, r }) => [
281
+ perform: stepsPipeline<{ label: string }, never>(({ event, r }) => [
282
282
  r.step.aggregate.create("widget", {
283
283
  executor: widgetExecutor,
284
284
  data: () => ({ label: event.payload.label }),
@@ -303,7 +303,7 @@ const forEachThenThrowHandler = defineWriteHandler({
303
303
  name: "widget:foreach-then-throw",
304
304
  schema: forEachThenThrowSchema,
305
305
  access: { roles: ["Admin"] },
306
- perform: pipeline<{ labels: string[] }, never>(({ event, r }) => [
306
+ perform: stepsPipeline<{ labels: string[] }, never>(({ event, r }) => [
307
307
  r.step.forEach({
308
308
  over: () => event.payload.labels,
309
309
  as: "label",
@@ -337,7 +337,7 @@ const lookupHandler = defineWriteHandler({
337
337
  name: "widget:lookup",
338
338
  schema: lookupSchema,
339
339
  access: { roles: ["Admin"] },
340
- perform: pipeline<z.infer<typeof lookupSchema>, { found: boolean; label: string | null }>(
340
+ perform: stepsPipeline<z.infer<typeof lookupSchema>, { found: boolean; label: string | null }>(
341
341
  ({ event, r }) => [
342
342
  r.step.read.findOne("widget", {
343
343
  table: widgetTable,
@@ -358,7 +358,7 @@ const listAllHandler = defineWriteHandler({
358
358
  name: "widget:list",
359
359
  schema: z.object({}),
360
360
  access: { roles: ["Admin"] },
361
- perform: pipeline<Record<string, never>, { count: number }>(({ r }) => [
361
+ perform: stepsPipeline<Record<string, never>, { count: number }>(({ r }) => [
362
362
  r.step.read.findMany("widgets", { table: widgetTable }),
363
363
  r.step.return(({ steps }) => ({
364
364
  isSuccess: true as const,
@@ -374,7 +374,7 @@ const listLimitedHandler = defineWriteHandler({
374
374
  name: "widget:list-one",
375
375
  schema: z.object({}),
376
376
  access: { roles: ["Admin"] },
377
- perform: pipeline<Record<string, never>, { count: number }>(({ r }) => [
377
+ perform: stepsPipeline<Record<string, never>, { count: number }>(({ r }) => [
378
378
  r.step.read.findMany("widgets", { table: widgetTable, limit: 1 }),
379
379
  r.step.return(({ steps }) => ({
380
380
  isSuccess: true as const,
@@ -388,7 +388,7 @@ const purgeLogHandler = defineWriteHandler({
388
388
  name: "log:purge",
389
389
  schema: purgeLogSchema,
390
390
  access: { roles: ["Admin"] },
391
- perform: pipeline<z.infer<typeof purgeLogSchema>, { ok: true }>(({ event, r }) => [
391
+ perform: stepsPipeline<z.infer<typeof purgeLogSchema>, { ok: true }>(({ event, r }) => [
392
392
  r.step.unsafeProjectionDelete({
393
393
  table: pipelineDemoLogTable,
394
394
  where: () => ({ correlationId: event.payload.correlationId }),
@@ -403,7 +403,7 @@ const conditionalLogHandler = defineWriteHandler({
403
403
  name: "log:conditional",
404
404
  schema: conditionalLogSchema,
405
405
  access: { roles: ["Admin"] },
406
- perform: pipeline<z.infer<typeof conditionalLogSchema>, { ok: true }>(({ event, r }) => [
406
+ perform: stepsPipeline<z.infer<typeof conditionalLogSchema>, { ok: true }>(({ event, r }) => [
407
407
  r.step.branch({
408
408
  if: () => event.payload.message.length > 0,
409
409
  onTrue: [
@@ -428,7 +428,7 @@ const bulkLogHandler = defineWriteHandler({
428
428
  name: "log:bulk",
429
429
  schema: bulkLogSchema,
430
430
  access: { roles: ["Admin"] },
431
- perform: pipeline<z.infer<typeof bulkLogSchema>, { count: number }>(({ event, r }) => [
431
+ perform: stepsPipeline<z.infer<typeof bulkLogSchema>, { count: number }>(({ event, r }) => [
432
432
  r.step.forEach({
433
433
  over: () => event.payload.correlationIds,
434
434
  as: "correlationId",
@@ -476,7 +476,7 @@ const demoPipelineFeature = defineFeature("demoPipeline", (r) => {
476
476
  let stack: TestStack;
477
477
  const admin = TestUsers.admin;
478
478
 
479
- describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher path", () => {
479
+ describe("defineWriteHandler({ perform: stepsPipeline(...) }) — real dispatcher path", () => {
480
480
  beforeAll(async () => {
481
481
  stack = await setupTestStack({ features: [demoPipelineFeature] });
482
482
  // Push the read-side-projection table — not registered as an entity,
@@ -20,7 +20,7 @@ import { createRecordingProvider, type RecordingProvider } from "../../testing";
20
20
  import { defineFeature } from "../define-feature";
21
21
  import { defineWriteHandler } from "../define-handler";
22
22
  import { createEntity, createTextField } from "../factories";
23
- import { pipeline } from "../pipeline";
23
+ import { stepsPipeline } from "../pipeline";
24
24
 
25
25
  // Handler whose step bodies emit a span + a metric. The recording
26
26
  // provider afterwards lets us assert both landed.
@@ -30,7 +30,7 @@ const observedHandler = defineWriteHandler({
30
30
  name: "observed",
31
31
  schema: observedSchema,
32
32
  access: { roles: ["Admin"] },
33
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
33
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
34
34
  r.step.compute("traced", (ctx) => {
35
35
  // Span emitted from inside a step. End it explicitly because
36
36
  // step.run is sync-or-async-but-not-otel-instrumented; the
@@ -1,6 +1,6 @@
1
1
  // Pipeline-engine performance smoke-test.
2
2
  //
3
- // Compares the M.1 pipeline-form handler ({ perform: pipeline(...) })
3
+ // Compares the M.1 pipeline-form handler ({ perform: stepsPipeline(...) })
4
4
  // against the equivalent free-form handler ({ handler: async (...) })
5
5
  // over N identical writes against the real Postgres stack. The point
6
6
  // is NOT to optimise — the pipeline-form is a thin wrapper, not a
@@ -28,7 +28,7 @@ import { setupTestStack, type TestStack, TestUsers, unsafeCreateEntityTable } fr
28
28
  import { defineFeature } from "../define-feature";
29
29
  import { defineWriteHandler } from "../define-handler";
30
30
  import { createEntity, createNumberField, createTextField } from "../factories";
31
- import { pipeline } from "../pipeline";
31
+ import { stepsPipeline } from "../pipeline";
32
32
 
33
33
  // Same logical operation in both handler-forms: read input, return a
34
34
  // trivial transformed payload. No DB-write — the goal is to compare
@@ -41,7 +41,7 @@ const trivialPipeline = defineWriteHandler({
41
41
  name: "trivial:pipeline",
42
42
  schema: trivialSchema,
43
43
  access: { roles: ["Admin"] },
44
- perform: pipeline<{ n: number }, { doubled: number }>(({ event, r }) => [
44
+ perform: stepsPipeline<{ n: number }, { doubled: number }>(({ event, r }) => [
45
45
  r.step.return(() => ({
46
46
  isSuccess: true as const,
47
47
  data: { doubled: event.payload.n * 2 },
@@ -9,7 +9,7 @@ import { describe, expect, it } from "bun:test";
9
9
  import { z } from "zod";
10
10
  import { TestUsers } from "../../stack";
11
11
  import { defineWriteHandler } from "../define-handler";
12
- import { pipeline } from "../pipeline";
12
+ import { stepsPipeline } from "../pipeline";
13
13
  import { buildBranchStep } from "../steps/branch";
14
14
  import { buildForEachStep } from "../steps/for-each";
15
15
  import { buildReturnStep } from "../steps/return";
@@ -21,7 +21,7 @@ describe("r.step.branch", () => {
21
21
  name: "demo:branch-then",
22
22
  schema: z.object({}),
23
23
  access: { roles: ["User"] },
24
- perform: pipeline<Record<string, never>, { value: number }>(({ r }) => [
24
+ perform: stepsPipeline<Record<string, never>, { value: number }>(({ r }) => [
25
25
  r.step.branch({
26
26
  if: () => true,
27
27
  onTrue: [r.step.compute("inThen", () => 42)],
@@ -47,7 +47,7 @@ describe("r.step.branch", () => {
47
47
  name: "demo:branch-else",
48
48
  schema: z.object({}),
49
49
  access: { roles: ["User"] },
50
- perform: pipeline<Record<string, never>, { value: number }>(({ r }) => [
50
+ perform: stepsPipeline<Record<string, never>, { value: number }>(({ r }) => [
51
51
  r.step.branch({
52
52
  if: () => false,
53
53
  onTrue: [r.step.compute("inThen", () => 42)],
@@ -72,7 +72,7 @@ describe("r.step.branch", () => {
72
72
  name: "demo:branch-noop",
73
73
  schema: z.object({}),
74
74
  access: { roles: ["User"] },
75
- perform: pipeline<Record<string, never>, { ran: boolean }>(({ r }) => [
75
+ perform: stepsPipeline<Record<string, never>, { ran: boolean }>(({ r }) => [
76
76
  r.step.branch({
77
77
  if: () => false,
78
78
  onTrue: [r.step.compute("ran", () => true)],
@@ -122,7 +122,7 @@ describe("r.step.forEach", () => {
122
122
  name: "demo:foreach",
123
123
  schema: z.object({}),
124
124
  access: { roles: ["User"] },
125
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
125
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
126
126
  r.step.forEach({
127
127
  over: () => [10, 20, 30],
128
128
  as: "n",
@@ -153,7 +153,7 @@ describe("r.step.forEach", () => {
153
153
  name: "demo:foreach-cleanup",
154
154
  schema: z.object({}),
155
155
  access: { roles: ["User"] },
156
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
156
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
157
157
  r.step.forEach({
158
158
  over: () => [1],
159
159
  as: "tmp",
@@ -180,7 +180,7 @@ describe("r.step.forEach", () => {
180
180
  name: "demo:foreach-empty",
181
181
  schema: z.object({}),
182
182
  access: { roles: ["User"] },
183
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
183
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
184
184
  r.step.forEach({
185
185
  over: () => [],
186
186
  as: "n",
@@ -233,7 +233,7 @@ describe("r.step.forEach", () => {
233
233
  name: "demo:foreach-throws",
234
234
  schema: z.object({}),
235
235
  access: { roles: ["User"] },
236
- perform: pipeline<Record<string, never>, never>(({ r }) => [
236
+ perform: stepsPipeline<Record<string, never>, never>(({ r }) => [
237
237
  r.step.forEach({
238
238
  over: () => [1, 2, 3],
239
239
  as: "item",
@@ -266,7 +266,7 @@ describe("r.step.forEach", () => {
266
266
  name: "demo:foreach-bad-over",
267
267
  schema: z.object({}),
268
268
  access: { roles: ["User"] },
269
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
269
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
270
270
  r.step.forEach({
271
271
  // Cast: TypeScript would normally catch this; the test exercises
272
272
  // the runtime guard for hand-crafted / dynamically built cases.
@@ -11,7 +11,7 @@ import { defineFeature } from "../define-feature";
11
11
  import { defineWriteHandler } from "../define-handler";
12
12
  import { defineStep } from "../define-step";
13
13
  import { createEntity, createTextField } from "../factories";
14
- import { pipeline } from "../pipeline";
14
+ import { stepsPipeline } from "../pipeline";
15
15
  import { validateProjectionAllowlist } from "../validate-projection-allowlist";
16
16
 
17
17
  describe("validateProjectionAllowlist", () => {
@@ -28,7 +28,7 @@ describe("validateProjectionAllowlist", () => {
28
28
  name: "log",
29
29
  schema: z.object({ msg: z.string() }),
30
30
  access: { roles: ["User"] },
31
- perform: pipeline<{ msg: string }, { ok: true }>(({ event, r }) => [
31
+ perform: stepsPipeline<{ msg: string }, { ok: true }>(({ event, r }) => [
32
32
  r.step.unsafeProjectionUpsert({
33
33
  table: demoLogTable,
34
34
  on: ["id"],
@@ -70,7 +70,7 @@ describe("validateProjectionAllowlist", () => {
70
70
  name: "sneaky",
71
71
  schema: z.object({}),
72
72
  access: { roles: ["User"] },
73
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
73
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
74
74
  r.step.unsafeProjectionUpsert({
75
75
  table: widgetsTable,
76
76
  on: ["id"],
@@ -113,7 +113,7 @@ describe("validateProjectionAllowlist", () => {
113
113
  name: "sneaky-delete",
114
114
  schema: z.object({}),
115
115
  access: { roles: ["User"] },
116
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
116
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
117
117
  r.step.unsafeProjectionDelete({
118
118
  table: widgetsTable,
119
119
  where: () => ({ id: "anything" }),
@@ -136,7 +136,7 @@ describe("validateProjectionAllowlist", () => {
136
136
  name: "purge",
137
137
  schema: z.object({}),
138
138
  access: { roles: ["User"] },
139
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
139
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
140
140
  r.step.unsafeProjectionDelete({
141
141
  table: demoLogTable,
142
142
  where: () => ({ id: "anything" }),
@@ -162,7 +162,7 @@ describe("validateProjectionAllowlist", () => {
162
162
  name: "branchedWrite",
163
163
  schema: z.object({}),
164
164
  access: { roles: ["User"] },
165
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
165
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
166
166
  r.step.branch({
167
167
  if: () => true,
168
168
  onTrue: [
@@ -195,7 +195,7 @@ describe("validateProjectionAllowlist", () => {
195
195
  name: "nestedWrite",
196
196
  schema: z.object({}),
197
197
  access: { roles: ["User"] },
198
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
198
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
199
199
  r.step.forEach({
200
200
  over: () => [],
201
201
  as: "item",
@@ -230,7 +230,7 @@ describe("validateProjectionAllowlist", () => {
230
230
  name: "loopedWrite",
231
231
  schema: z.object({}),
232
232
  access: { roles: ["User"] },
233
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
233
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
234
234
  r.step.forEach({
235
235
  over: () => [],
236
236
  as: "x",
@@ -267,7 +267,7 @@ describe("validateProjectionAllowlist", () => {
267
267
  name: "futureBuilder",
268
268
  schema: z.object({}),
269
269
  access: { roles: ["User"] },
270
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
270
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
271
271
  // Hand-crafted StepInstance simulating a future builder
272
272
  // whose kind isn't registered via defineStep yet.
273
273
  {
@@ -316,7 +316,7 @@ describe("validateProjectionAllowlist", () => {
316
316
  name: "futureBuilder",
317
317
  schema: z.object({}),
318
318
  access: { roles: ["User"] },
319
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
319
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
320
320
  {
321
321
  kind: futureKind,
322
322
  args: {
@@ -380,7 +380,7 @@ describe("validateProjectionAllowlist", () => {
380
380
  name: "sneak",
381
381
  schema: z.object({}),
382
382
  access: { roles: ["Admin"] },
383
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
383
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
384
384
  r.step.webhook.send({
385
385
  url: "https://hooks.example/sneak",
386
386
  mode: "deferred",
@@ -404,7 +404,7 @@ describe("validateProjectionAllowlist", () => {
404
404
  name: "sneak",
405
405
  schema: z.object({}),
406
406
  access: { roles: ["Admin"] },
407
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
407
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
408
408
  r.step.mail.send({
409
409
  to: "x@y.com",
410
410
  subject: "hi",
@@ -428,7 +428,7 @@ describe("validateProjectionAllowlist", () => {
428
428
  name: "sneak",
429
429
  schema: z.object({}),
430
430
  access: { roles: ["Admin"] },
431
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
431
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
432
432
  r.step.callFeature("subResult", {
433
433
  handler: "other:write:do",
434
434
  payload: () => ({}),
@@ -451,7 +451,7 @@ describe("validateProjectionAllowlist", () => {
451
451
  name: "ok",
452
452
  schema: z.object({}),
453
453
  access: { roles: ["Admin"] },
454
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
454
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
455
455
  r.step.webhook.send({
456
456
  url: "https://hooks.example/ok",
457
457
  mode: "deferred",
@@ -473,7 +473,7 @@ describe("validateProjectionAllowlist", () => {
473
473
  name: "log",
474
474
  schema: z.object({ msg: z.string() }),
475
475
  access: { roles: ["User"] },
476
- perform: pipeline<{ msg: string }, { ok: true }>(({ event, r }) => [
476
+ perform: stepsPipeline<{ msg: string }, { ok: true }>(({ event, r }) => [
477
477
  r.step.unsafeProjectionUpsert({
478
478
  table: demoLogTable,
479
479
  on: ["id"],