okengine 0.10.0 → 0.10.2

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.
Files changed (105) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/site/content/docs/elements/clock.mdx +2 -2
  4. package/site/content/docs/elements/flow.mdx +10 -10
  5. package/site/content/docs/elements/gate.mdx +4 -4
  6. package/site/content/docs/elements/signal.mdx +4 -7
  7. package/site/content/docs/elements/store.mdx +1 -1
  8. package/site/content/docs/elements/vault.mdx +1 -1
  9. package/site/content/docs/get-started/basic-usage.mdx +1 -3
  10. package/site/content/docs/get-started/introduction.mdx +1 -1
  11. package/site/content/docs/reference/cli.md +12 -12
  12. package/site/content/docs/reference/fx.mdx +3 -4
  13. package/src/auth/auth.test.ts +1 -2
  14. package/src/auth/bindings.ts +5 -15
  15. package/src/auth/gate-auth.test.ts +2 -2
  16. package/src/cli/ai-setup/apply.ts +13 -0
  17. package/src/cli/build.test.ts +67 -0
  18. package/src/cli/build.ts +34 -0
  19. package/src/cli/db-seed.ts +1 -1
  20. package/src/cli/dev-app-runner.ts +6 -0
  21. package/src/cli/dev-controls.test.ts +43 -48
  22. package/src/cli/dev-controls.ts +23 -172
  23. package/src/cli/dev.test.ts +2 -4
  24. package/src/cli/dev.ts +63 -16
  25. package/src/cli/docker-cli.test.ts +15 -0
  26. package/src/cli/start.ts +12 -1
  27. package/src/client/notes-contract.test.ts +3 -4
  28. package/src/compiler/aot.test.ts +1 -2
  29. package/src/compiler/effects-infer.ts +37 -8
  30. package/src/compiler/extract.test.ts +186 -6
  31. package/src/compiler/extract.ts +31 -6
  32. package/src/compiler/fixtures/raw/raw-unannotated.ts +2 -4
  33. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +4 -8
  34. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -2
  35. package/src/compiler/fixtures/skyport/src/flows/support/index.ts +1 -2
  36. package/src/compiler/fixtures/skyport.expected.json +3 -3
  37. package/src/compiler/fixtures/triggers/five-triggers.ts +5 -10
  38. package/src/compiler/generate-adopt.test.ts +85 -0
  39. package/src/compiler/generate-adopt.ts +85 -0
  40. package/src/console/server/dry-run.audit.test.ts +2 -2
  41. package/src/console/server/flows.ts +50 -150
  42. package/src/docker/compose.ts +13 -3
  43. package/src/docker/recipes/llama-cpp.ts +10 -1
  44. package/src/docker/stack-id.ts +1 -0
  45. package/src/elements/channel/declare.ts +27 -1
  46. package/src/elements/clock/chaos-child.ts +3 -6
  47. package/src/elements/clock/chaos.test.ts +1 -2
  48. package/src/elements/clock.test.ts +2 -4
  49. package/src/elements/signal/declare.ts +25 -1
  50. package/src/elements/store/cache.test.ts +28 -0
  51. package/src/elements/store/declare.ts +27 -1
  52. package/src/elements/store/resource.ts +6 -11
  53. package/src/elements/store/upsert-app.test.ts +1 -2
  54. package/src/elements/vault/declare.ts +29 -1
  55. package/src/kernel/adopt-barrel-fresh.test.ts +103 -0
  56. package/src/kernel/app.ts +80 -9
  57. package/src/kernel/auto-registry.test.ts +198 -0
  58. package/src/kernel/boot-bind/honor-config.test.ts +5 -5
  59. package/src/kernel/boot.test.ts +19 -15
  60. package/src/kernel/boot.ts +186 -8
  61. package/src/kernel/call.test.ts +5 -10
  62. package/src/kernel/compensate.test.ts +3 -6
  63. package/src/kernel/concurrency.test.ts +3 -5
  64. package/src/kernel/correlation.test.ts +4 -8
  65. package/src/kernel/edge.test.ts +1 -1
  66. package/src/kernel/effects-stamping.test.ts +204 -0
  67. package/src/kernel/element-registries.ts +27 -0
  68. package/src/kernel/errors.ts +22 -0
  69. package/src/kernel/flow.test.ts +8 -15
  70. package/src/kernel/flow.ts +12 -14
  71. package/src/kernel/fx.ts +42 -13
  72. package/src/kernel/hook-timing.test.ts +2 -2
  73. package/src/kernel/hooks.test.ts +5 -10
  74. package/src/kernel/horizontal-child.ts +5 -10
  75. package/src/kernel/journal-boot.test.ts +2 -4
  76. package/src/kernel/pipeline.test.ts +6 -12
  77. package/src/kernel/plugin/capabilities.test.ts +1 -1
  78. package/src/kernel/plugin/decorate.test.ts +3 -8
  79. package/src/kernel/plugin/scoping.test.ts +6 -16
  80. package/src/kernel/plugin-elements.test.ts +1 -1
  81. package/src/kernel/plugin-needs.test.ts +1 -1
  82. package/src/kernel/ready.test.ts +1 -2
  83. package/src/kernel/registry-isolation.test.ts +5 -5
  84. package/src/kernel/triggers.ts +1 -1
  85. package/src/manifest/sql-resource.ts +41 -0
  86. package/src/plugins/anonymous.ts +1 -3
  87. package/src/plugins/compression.test.ts +6 -8
  88. package/src/plugins/config-source.test.ts +1 -1
  89. package/src/plugins/config-source.ts +1 -2
  90. package/src/plugins/cors.test.ts +10 -10
  91. package/src/plugins/csrf.test.ts +2 -2
  92. package/src/plugins/headers.test.ts +14 -15
  93. package/src/plugins/ip-allowlist.test.ts +9 -9
  94. package/src/plugins/magic-link.ts +2 -6
  95. package/src/plugins/maintenance-mode.test.ts +6 -6
  96. package/src/plugins/otp.ts +5 -15
  97. package/src/plugins/passkey.ts +4 -12
  98. package/src/plugins/two-factor.ts +3 -9
  99. package/src/plugins/username.ts +2 -6
  100. package/src/release/measure.ts +1 -1
  101. package/src/runs/runs.test.ts +3 -5
  102. package/src/runtime/serve.test.ts +2 -4
  103. package/src/test/create-test-app.test.ts +3 -7
  104. package/src/test/provisions.integration.test.ts +4 -9
  105. package/src/test/reset-element-registries.ts +31 -0
@@ -18,7 +18,7 @@ beforeEach(() => {
18
18
 
19
19
  describe("headers plugin", () => {
20
20
  test("sets the default trio on a successful response", async () => {
21
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
21
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
22
22
  const app = oke({ autoBoot: false, name: "sec" }).plug(headers());
23
23
 
24
24
  const res = await app.fetch(new Request("http://localhost/x"));
@@ -31,7 +31,7 @@ describe("headers plugin", () => {
31
31
  });
32
32
 
33
33
  test("helmet-parity defaults: agent cluster, dns prefetch, download, cross-domain, xss filter", async () => {
34
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
34
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
35
35
  const app = oke({ autoBoot: false, name: "sec-parity" }).plug(headers());
36
36
 
37
37
  const res = await app.fetch(new Request("http://localhost/x"));
@@ -44,7 +44,7 @@ describe("headers plugin", () => {
44
44
  });
45
45
 
46
46
  test("each parity default can be switched off", async () => {
47
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
47
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
48
48
  const app = oke({ autoBoot: false, name: "sec-off" }).plug(
49
49
  headers({
50
50
  originAgentCluster: false,
@@ -64,7 +64,7 @@ describe("headers plugin", () => {
64
64
  });
65
65
 
66
66
  test("dnsPrefetchControl allow, custom cross-domain policy, COEP stamps when configured", async () => {
67
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
67
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
68
68
  const app = oke({ autoBoot: false, name: "sec-tuned" }).plug(
69
69
  headers({
70
70
  dnsPrefetchControl: { allow: true },
@@ -84,8 +84,7 @@ describe("headers plugin", () => {
84
84
  const poweredByApp = (options: Parameters<typeof headers>[0], name: string) => {
85
85
  on(
86
86
  http.get("/x"),
87
- flow({
88
- name: "x.get",
87
+ flow("x.get", {
89
88
  do: () =>
90
89
  new Response("{}", {
91
90
  headers: { "content-type": "application/json", "x-powered-by": "Express" },
@@ -111,7 +110,7 @@ describe("headers plugin", () => {
111
110
  });
112
111
 
113
112
  test("structured CSP merges over helmet's defaults, camelCase keys, report-only mode", async () => {
114
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
113
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
115
114
  const app = oke({ autoBoot: false, name: "sec-csp-builder" }).plug(
116
115
  headers({
117
116
  contentSecurityPolicy: {
@@ -131,7 +130,7 @@ describe("headers plugin", () => {
131
130
  });
132
131
 
133
132
  test("structured CSP with useDefaults: false emits exactly the given directives", async () => {
134
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
133
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
135
134
  const app = oke({ autoBoot: false, name: "sec-csp-bare" }).plug(
136
135
  headers({
137
136
  contentSecurityPolicy: { useDefaults: false, directives: { "default-src": ["'none'"] } },
@@ -151,7 +150,7 @@ describe("headers plugin", () => {
151
150
  test("headers land on failures too (onResponse runs after onError)", async () => {
152
151
  on(
153
152
  http.get("/deny"),
154
- flow({ name: "x.deny", do: () => ({ ok: true }) }).hook("beforeHandle", () =>
153
+ flow("x.deny", { do: () => ({ ok: true }) }).hook("beforeHandle", () =>
155
154
  fail("Forbidden", {}),
156
155
  ),
157
156
  );
@@ -164,7 +163,7 @@ describe("headers plugin", () => {
164
163
  });
165
164
 
166
165
  test("adds CSP only when configured", async () => {
167
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
166
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
168
167
  const app = oke({ autoBoot: false, name: "sec-csp" }).plug(
169
168
  headers({ contentSecurityPolicy: "default-src 'self'" }),
170
169
  );
@@ -175,7 +174,7 @@ describe("headers plugin", () => {
175
174
  });
176
175
 
177
176
  test("HSTS is off by default", async () => {
178
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
177
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
179
178
  const app = oke({ autoBoot: false, name: "sec-hsts-off" }).plug(headers());
180
179
 
181
180
  const res = await app.fetch(new Request("http://localhost/x"));
@@ -184,7 +183,7 @@ describe("headers plugin", () => {
184
183
  });
185
184
 
186
185
  test("hsts: true stamps a one-year max-age", async () => {
187
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
186
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
188
187
  const app = oke({ autoBoot: false, name: "sec-hsts-on" }).plug(headers({ hsts: true }));
189
188
 
190
189
  const res = await app.fetch(new Request("http://localhost/x"));
@@ -193,7 +192,7 @@ describe("headers plugin", () => {
193
192
  });
194
193
 
195
194
  test("hsts object tunes max-age, subdomains, preload", async () => {
196
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
195
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
197
196
  const app = oke({ autoBoot: false, name: "sec-hsts-tuned" }).plug(
198
197
  headers({ hsts: { maxAge: 63072000, includeSubDomains: true, preload: true } }),
199
198
  );
@@ -206,7 +205,7 @@ describe("headers plugin", () => {
206
205
  });
207
206
 
208
207
  test("permissions-policy and cross-origin policies stamp when configured", async () => {
209
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
208
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
210
209
  const app = oke({ autoBoot: false, name: "sec-extra" }).plug(
211
210
  headers({
212
211
  permissionsPolicy: "camera=(), microphone=()",
@@ -223,7 +222,7 @@ describe("headers plugin", () => {
223
222
  });
224
223
 
225
224
  test("an explicit app-set value wins by default", async () => {
226
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
225
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
227
226
  const app = oke({ autoBoot: false, name: "sec-keep" });
228
227
  app.hook("onResponse", (ctx) => {
229
228
  if (!ctx.response) return;
@@ -25,7 +25,7 @@ function get(ip?: string): Request {
25
25
 
26
26
  describe("ipAllowlist plugin", () => {
27
27
  test("allow list: listed IPs pass, others get 403 Forbidden", async () => {
28
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
28
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
29
29
  const app = oke({ autoBoot: false, name: "ips" }).plug(ipAllowlist({ allow: ["203.0.113.7"] }));
30
30
 
31
31
  const ok = await app.fetch(get("203.0.113.7"));
@@ -39,7 +39,7 @@ describe("ipAllowlist plugin", () => {
39
39
  });
40
40
 
41
41
  test("deny list: blocked IPs get 403 ip_denied, others pass", async () => {
42
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
42
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
43
43
  const app = oke({ autoBoot: false, name: "ips-deny" }).plug(
44
44
  ipAllowlist({ deny: ["198.51.100.9"] }),
45
45
  );
@@ -54,7 +54,7 @@ describe("ipAllowlist plugin", () => {
54
54
  });
55
55
 
56
56
  test("deny wins over allow on overlap", async () => {
57
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
57
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
58
58
  const app = oke({ autoBoot: false, name: "ips-both" }).plug(
59
59
  ipAllowlist({ allow: ["203.0.113.7"], deny: ["203.0.113.7"] }),
60
60
  );
@@ -64,7 +64,7 @@ describe("ipAllowlist plugin", () => {
64
64
  });
65
65
 
66
66
  test("XFF last hop is the client; a spoofed first hop cannot bypass allow", async () => {
67
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
67
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
68
68
  const app = oke({ autoBoot: false, name: "ips-xff" }).plug(
69
69
  ipAllowlist({ allow: ["203.0.113.7"] }),
70
70
  );
@@ -78,7 +78,7 @@ describe("ipAllowlist plugin", () => {
78
78
  });
79
79
 
80
80
  test("spoofed first hop cannot bypass a deny rule", async () => {
81
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
81
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
82
82
  const app = oke({ autoBoot: false, name: "ips-xff-deny" }).plug(
83
83
  ipAllowlist({ deny: ["198.51.100.9"] }),
84
84
  );
@@ -92,7 +92,7 @@ describe("ipAllowlist plugin", () => {
92
92
  });
93
93
 
94
94
  test("trustedProxyDepth selects the hop behind N trusted proxies", async () => {
95
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
95
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
96
96
  // Chain: spoofed, real-client, cdn-egress — depth 2 skips the nearest proxy hop.
97
97
  const app = oke({ autoBoot: false, name: "ips-depth" }).plug(
98
98
  ipAllowlist({ allow: ["203.0.113.7"], trustedProxyDepth: 2 }),
@@ -113,7 +113,7 @@ describe("ipAllowlist plugin", () => {
113
113
  });
114
114
 
115
115
  test("missing header: denied when allow is set, permitted for deny-only", async () => {
116
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
116
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
117
117
  const strict = oke({ autoBoot: false, name: "ips-missing-allow" }).plug(
118
118
  ipAllowlist({ allow: ["203.0.113.7"] }),
119
119
  );
@@ -123,7 +123,7 @@ describe("ipAllowlist plugin", () => {
123
123
 
124
124
  resetBindings();
125
125
  resetFlowSeq();
126
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
126
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
127
127
 
128
128
  const lax = oke({ autoBoot: false, name: "ips-missing-deny" }).plug(
129
129
  ipAllowlist({ deny: ["198.51.100.9"] }),
@@ -133,7 +133,7 @@ describe("ipAllowlist plugin", () => {
133
133
  });
134
134
 
135
135
  test("custom header name is honored", async () => {
136
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
136
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
137
137
  const app = oke({ autoBoot: false, name: "ips-custom" }).plug(
138
138
  ipAllowlist({ allow: ["203.0.113.7"], header: "x-real-ip" }),
139
139
  );
@@ -107,9 +107,7 @@ export function magicLink(opts: MagicLinkOptions = {}): PluginDef {
107
107
  })
108
108
  : magicLinkTemplate;
109
109
 
110
- const request = flow({
111
- name: "auth.requestMagicLink",
112
- unit: "auth",
110
+ const request = flow("auth.requestMagicLink", {
113
111
  plane: "user",
114
112
  in: z.object({ email: z.string().min(3) }),
115
113
  out: z.object({
@@ -144,9 +142,7 @@ export function magicLink(opts: MagicLinkOptions = {}): PluginDef {
144
142
  },
145
143
  });
146
144
 
147
- const verify = flow({
148
- name: "auth.verifyMagicLink",
149
- unit: "auth",
145
+ const verify = flow("auth.verifyMagicLink", {
150
146
  plane: "user",
151
147
  in: z.object({ token: z.string().min(1) }),
152
148
  out: SessionTokensOut,
@@ -17,7 +17,7 @@ beforeEach(() => {
17
17
 
18
18
  describe("maintenanceMode plugin", () => {
19
19
  test("returns 503 with ServiceUnavailable envelope and Retry-After", async () => {
20
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
20
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
21
21
  const app = oke({ autoBoot: false, name: "maint" }).plug(maintenanceMode({ retryAfter: 120 }));
22
22
 
23
23
  const res = await app.fetch(new Request("http://localhost/x"));
@@ -35,7 +35,7 @@ describe("maintenanceMode plugin", () => {
35
35
  });
36
36
 
37
37
  test("enabled: false passes traffic through", async () => {
38
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
38
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
39
39
  const app = oke({ autoBoot: false, name: "maint-off" }).plug(
40
40
  maintenanceMode({ enabled: false }),
41
41
  );
@@ -46,8 +46,8 @@ describe("maintenanceMode plugin", () => {
46
46
  });
47
47
 
48
48
  test("allowPaths prefixes keep serving", async () => {
49
- on(http.get("/health"), flow({ name: "health.get", do: () => ({ up: true }) }));
50
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
49
+ on(http.get("/health"), flow("health.get", { do: () => ({ up: true }) }));
50
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
51
51
  const app = oke({ autoBoot: false, name: "maint-allow" }).plug(
52
52
  maintenanceMode({ allowPaths: ["/health"] }),
53
53
  );
@@ -60,7 +60,7 @@ describe("maintenanceMode plugin", () => {
60
60
  });
61
61
 
62
62
  test("bypass header lets operators through (any non-empty value)", async () => {
63
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
63
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
64
64
  const app = oke({ autoBoot: false, name: "maint-bypass" }).plug(
65
65
  maintenanceMode({ bypassHeader: "x-ops-token" }),
66
66
  );
@@ -75,7 +75,7 @@ describe("maintenanceMode plugin", () => {
75
75
  });
76
76
 
77
77
  test("the 503 still flows through onResponse (other plugins stamp it)", async () => {
78
- on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
78
+ on(http.get("/x"), flow("x.get", { do: () => ({ ok: true }) }));
79
79
  const app = oke({ autoBoot: false, name: "maint-stamp" }).plug(maintenanceMode());
80
80
  app.hook("onResponse", (ctx) => {
81
81
  if (!ctx.response) return;
@@ -236,9 +236,7 @@ export function otp(opts: OtpOptions): PluginDef {
236
236
  });
237
237
 
238
238
  if (opts.mode === "provider") {
239
- const request = flow({
240
- name: "auth.requestOtp",
241
- unit: "auth",
239
+ const request = flow("auth.requestOtp", {
242
240
  plane: "user",
243
241
  in: z.object({
244
242
  phone: z.string().min(8),
@@ -277,9 +275,7 @@ export function otp(opts: OtpOptions): PluginDef {
277
275
  },
278
276
  });
279
277
 
280
- const verify = flow({
281
- name: "auth.verifyOtp",
282
- unit: "auth",
278
+ const verify = flow("auth.verifyOtp", {
283
279
  plane: "user",
284
280
  in: z.object({
285
281
  phone: z.string().min(8),
@@ -347,9 +343,7 @@ export function otp(opts: OtpOptions): PluginDef {
347
343
  }
348
344
 
349
345
  // ── App mode ───────────────────────────────────────────────────────────
350
- const request = flow({
351
- name: "auth.requestOtp",
352
- unit: "auth",
346
+ const request = flow("auth.requestOtp", {
353
347
  plane: "user",
354
348
  in: z.object({
355
349
  email: z.string().min(3).optional(),
@@ -415,9 +409,7 @@ export function otp(opts: OtpOptions): PluginDef {
415
409
  },
416
410
  });
417
411
 
418
- const resend = flow({
419
- name: "auth.resendOtp",
420
- unit: "auth",
412
+ const resend = flow("auth.resendOtp", {
421
413
  plane: "user",
422
414
  in: z.object({
423
415
  email: z.string().min(3).optional(),
@@ -486,9 +478,7 @@ export function otp(opts: OtpOptions): PluginDef {
486
478
  },
487
479
  });
488
480
 
489
- const verify = flow({
490
- name: "auth.verifyOtp",
491
- unit: "auth",
481
+ const verify = flow("auth.verifyOtp", {
492
482
  plane: "user",
493
483
  in: z.object({
494
484
  email: z.string().min(3).optional(),
@@ -87,9 +87,7 @@ export function passkey(opts: PasskeyOptions = {}): PluginDef {
87
87
  const rpId = opts.rpId ?? "localhost";
88
88
  const origins = opts.origins ?? ["http://localhost", "https://localhost"];
89
89
 
90
- const registerOptions = flow({
91
- name: "auth.passkeyRegisterOptions",
92
- unit: "auth",
90
+ const registerOptions = flow("auth.passkeyRegisterOptions", {
93
91
  plane: "user",
94
92
  out: z.object({
95
93
  challenge: z.string(),
@@ -115,9 +113,7 @@ export function passkey(opts: PasskeyOptions = {}): PluginDef {
115
113
  },
116
114
  });
117
115
 
118
- const register = flow({
119
- name: "auth.passkeyRegister",
120
- unit: "auth",
116
+ const register = flow("auth.passkeyRegister", {
121
117
  plane: "user",
122
118
  in: CeremonyIn.extend({
123
119
  publicKey: z.string().min(1),
@@ -171,9 +167,7 @@ export function passkey(opts: PasskeyOptions = {}): PluginDef {
171
167
  },
172
168
  });
173
169
 
174
- const authenticateOptions = flow({
175
- name: "auth.passkeyAuthenticateOptions",
176
- unit: "auth",
170
+ const authenticateOptions = flow("auth.passkeyAuthenticateOptions", {
177
171
  plane: "user",
178
172
  in: z.object({ email: z.string().optional() }),
179
173
  out: z.object({
@@ -199,9 +193,7 @@ export function passkey(opts: PasskeyOptions = {}): PluginDef {
199
193
  },
200
194
  });
201
195
 
202
- const authenticate = flow({
203
- name: "auth.passkeyAuthenticate",
204
- unit: "auth",
196
+ const authenticate = flow("auth.passkeyAuthenticate", {
205
197
  plane: "user",
206
198
  in: CeremonyIn.extend({
207
199
  challenge: z.string().min(1),
@@ -59,9 +59,7 @@ export function twoFactor(opts: TwoFactorOptions = {}): PluginDef {
59
59
  const factors = opts.factors ?? createTwoFactorStore();
60
60
  const issuer = opts.issuer ?? "oke";
61
61
 
62
- const enable = flow({
63
- name: "auth.twoFactorEnable",
64
- unit: "auth",
62
+ const enable = flow("auth.twoFactorEnable", {
65
63
  plane: "user",
66
64
  out: z.object({
67
65
  secret: z.string(),
@@ -91,9 +89,7 @@ export function twoFactor(opts: TwoFactorOptions = {}): PluginDef {
91
89
  },
92
90
  });
93
91
 
94
- const verify = flow({
95
- name: "auth.twoFactorVerify",
96
- unit: "auth",
92
+ const verify = flow("auth.twoFactorVerify", {
97
93
  plane: "user",
98
94
  in: z.object({
99
95
  userId: z.string().min(1),
@@ -127,9 +123,7 @@ export function twoFactor(opts: TwoFactorOptions = {}): PluginDef {
127
123
  },
128
124
  });
129
125
 
130
- const disable = flow({
131
- name: "auth.twoFactorDisable",
132
- unit: "auth",
126
+ const disable = flow("auth.twoFactorDisable", {
133
127
  plane: "user",
134
128
  out: z.object({ ok: z.literal(true) }),
135
129
  errors: { AuthFailed },
@@ -319,9 +319,7 @@ export function username(opts: UsernamePluginOptions = {}): PluginDef {
319
319
  resolveUsernamePolicy(usernamePolicy);
320
320
  const crypto = createBunCrypto();
321
321
 
322
- const signUp = flow({
323
- name: "auth.signUpUsername",
324
- unit: "auth",
322
+ const signUp = flow("auth.signUpUsername", {
325
323
  plane: "user",
326
324
  in: UsernameIn,
327
325
  out: SessionTokensOut,
@@ -385,9 +383,7 @@ export function username(opts: UsernamePluginOptions = {}): PluginDef {
385
383
  },
386
384
  });
387
385
 
388
- const signIn = flow({
389
- name: "auth.signInUsername",
390
- unit: "auth",
386
+ const signIn = flow("auth.signInUsername", {
391
387
  plane: "user",
392
388
  in: UsernameIn,
393
389
  out: SessionTokensOut,
@@ -302,7 +302,7 @@ const { flow } = await import(${JSON.stringify(paths.flow)});
302
302
  const { on, resetBindings } = await import(${JSON.stringify(paths.on)});
303
303
  const { http } = await import(${JSON.stringify(paths.http)});
304
304
  resetBindings();
305
- on(http.get("/ping"), flow({ name: "ping", do: () => ({ ok: true }) }));
305
+ on(http.get("/ping"), flow("ping", { do: () => ({ ok: true }) }));
306
306
  const app = oke({ name: "cold-start" });
307
307
  const rt = createBunRuntime();
308
308
  const server = rt.serve(app, { port: 0, hostname: "127.0.0.1" });
@@ -59,9 +59,7 @@ describe("zero-instrumentation dimensions", () => {
59
59
 
60
60
  on(
61
61
  http.get("/book").gate(member),
62
- flow({
63
- name: "book.create",
64
- unit: "bookings",
62
+ flow("bookings.create", {
65
63
  plane: "user",
66
64
  effects: { reads: ["sql:bookings"] },
67
65
  do: async (_input, fx) => {
@@ -92,7 +90,7 @@ describe("zero-instrumentation dimensions", () => {
92
90
  const events = await runs.all();
93
91
  expect(events.length).toBe(1);
94
92
  const e = events[0]!;
95
- expect(e.flow).toBe("book.create");
93
+ expect(e.flow).toBe("bookings.create");
96
94
  expect(e.unit).toBe("bookings");
97
95
  expect(e.trigger).toBe("http");
98
96
  expect(e.plane).toBe("user");
@@ -103,7 +101,7 @@ describe("zero-instrumentation dimensions", () => {
103
101
  expect(e.buildVersion).toBe("1.2.3");
104
102
  expect(e.logs.length).toBe(1);
105
103
  expect(e.logs[0]!.message).toBe("booking started");
106
- expect(e.dimensions.flow).toBe("book.create");
104
+ expect(e.dimensions.flow).toBe("bookings.create");
107
105
  expect(e.dimensions.tenant).toBe("org_a41");
108
106
  expect(e.dimensions.cache).toBe("hit");
109
107
 
@@ -57,15 +57,13 @@ function rawHttp(port: number, payload: string): Promise<string> {
57
57
  function buildApp() {
58
58
  on(
59
59
  http.get("/ping").gate(gate.public),
60
- flow({
61
- name: "ping",
60
+ flow("ping", {
62
61
  do: () => ({ ok: true as const, n: 1 }),
63
62
  }),
64
63
  );
65
64
  on(
66
65
  http.get("/notes/:id").gate(gate.public),
67
- flow({
68
- name: "notes.get",
66
+ flow("notes.get", {
69
67
  do: ({ id }: { id: string }) => ({ id }),
70
68
  }),
71
69
  );
@@ -37,9 +37,7 @@ describe("createTestApp — four-applications surface", () => {
37
37
 
38
38
  on(
39
39
  http.post("/orders").gate(member),
40
- flow({
41
- name: "orders.create",
42
- unit: "orders",
40
+ flow("orders.create", {
43
41
  in: z.object({ sku: z.string(), qty: z.number() }),
44
42
  out: z.object({ id: z.string() }),
45
43
  effects: { emits: ["order-placed"] },
@@ -53,8 +51,7 @@ describe("createTestApp — four-applications surface", () => {
53
51
 
54
52
  on(
55
53
  orderPlaced,
56
- flow({
57
- name: "orders.onPlaced",
54
+ flow("orders.onPlaced", {
58
55
  effects: { sends: ["order-confirmed"] },
59
56
  do: async ({ orderId }, fx) => {
60
57
  await fx.send(orderConfirmed, {
@@ -67,8 +64,7 @@ describe("createTestApp — four-applications surface", () => {
67
64
 
68
65
  on(
69
66
  every("1h"),
70
- flow({
71
- name: "orders.expire",
67
+ flow("orders.expire", {
72
68
  do: (_i, fx) => {
73
69
  fx.log.info("expire");
74
70
  },
@@ -48,8 +48,7 @@ describe("Provisions integration", () => {
48
48
  }),
49
49
  });
50
50
 
51
- const chargeOrder = flow({
52
- name: "payments.chargeOrder",
51
+ const chargeOrder = flow("payments.chargeOrder", {
53
52
  durable: true,
54
53
  in: z.object({ orderId: z.string() }),
55
54
  out: z.boolean(),
@@ -69,9 +68,7 @@ describe("Provisions integration", () => {
69
68
 
70
69
  on(
71
70
  http.post("/orders").gate(member, canOrder),
72
- flow({
73
- name: "orders.create",
74
- unit: "orders",
71
+ flow("orders.create", {
75
72
  in: z.object({ sku: z.string(), qty: z.number() }),
76
73
  out: z.object({ id: z.string() }),
77
74
  effects: { emits: ["order-placed"], calls: ["payments.chargeOrder"] },
@@ -85,8 +82,7 @@ describe("Provisions integration", () => {
85
82
 
86
83
  on(
87
84
  orderPlaced,
88
- flow({
89
- name: "orders.onPlaced",
85
+ flow("orders.onPlaced", {
90
86
  effects: { calls: ["payments.chargeOrder"] },
91
87
  do: async ({ orderId }, fx) => {
92
88
  await fx.call(chargeOrder, { orderId });
@@ -96,8 +92,7 @@ describe("Provisions integration", () => {
96
92
 
97
93
  on(
98
94
  orderNews,
99
- flow({
100
- name: "notifications.onNews",
95
+ flow("notifications.onNews", {
101
96
  effects: { sends: ["order-confirmed"] },
102
97
  do: async ({ orderId, status }, fx) => {
103
98
  if (status !== "confirmed") return;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Global safety net for the `store.sql` / `store.files` / `vault.secret` /
3
+ * `signal()` / `channel.<medium>().template()` auto-registries
4
+ * (`src/kernel/element-registries.ts`).
5
+ *
6
+ * Unlike `on()` bindings — created almost exclusively to wire a real app —
7
+ * these four factories are called throughout the suite as bare value
8
+ * constructors, completely unrelated to booting an app (hundreds of call
9
+ * sites across `src/elements/*.test.ts`). Since the registries are plain
10
+ * module-level arrays shared by every test file in one `bun test` process,
11
+ * leaving cleanup to per-file discipline (the convention `on.ts` relies on —
12
+ * see `resetBindings()` / `registry: "ignore"`) would let stray decls from
13
+ * one file silently reach a default-registry `oke()` call in a completely
14
+ * unrelated file. Reset after every test, globally, via `bunfig.toml`
15
+ * `[test].preload`.
16
+ */
17
+
18
+ import { afterEach } from "bun:test";
19
+ import {
20
+ channelTemplateRegistry,
21
+ secretRegistry,
22
+ signalRegistry,
23
+ storeRegistry,
24
+ } from "../kernel/element-registries.ts";
25
+
26
+ afterEach(() => {
27
+ storeRegistry.length = 0;
28
+ secretRegistry.length = 0;
29
+ signalRegistry.length = 0;
30
+ channelTemplateRegistry.length = 0;
31
+ });