okengine 0.10.1 → 0.10.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.
Files changed (141) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/site/content/docs/ai/mcp.mdx +0 -1
  4. package/site/content/docs/elements/ai.mdx +0 -6
  5. package/site/content/docs/elements/channel.mdx +0 -6
  6. package/site/content/docs/elements/clock.mdx +2 -8
  7. package/site/content/docs/elements/flow.mdx +10 -17
  8. package/site/content/docs/elements/gate.mdx +4 -10
  9. package/site/content/docs/elements/signal.mdx +4 -13
  10. package/site/content/docs/elements/store.mdx +9 -16
  11. package/site/content/docs/elements/vault.mdx +1 -7
  12. package/site/content/docs/get-started/basic-usage.mdx +1 -9
  13. package/site/content/docs/get-started/introduction.mdx +1 -1
  14. package/site/content/docs/get-started/why.mdx +1 -2
  15. package/site/content/docs/index.mdx +0 -1
  16. package/site/content/docs/meta.json +0 -1
  17. package/site/content/docs/reference/cli.md +12 -12
  18. package/site/content/docs/reference/fx.mdx +6 -7
  19. package/src/auth/auth.test.ts +1 -2
  20. package/src/auth/bindings.ts +5 -15
  21. package/src/auth/gate-auth.test.ts +2 -2
  22. package/src/cli/ai-setup/ai-setup.test.ts +3 -1
  23. package/src/cli/ai-setup/apply.ts +13 -0
  24. package/src/cli/build.test.ts +67 -0
  25. package/src/cli/build.ts +33 -0
  26. package/src/cli/db-seed.ts +1 -1
  27. package/src/cli/dev-controls.test.ts +43 -48
  28. package/src/cli/dev-controls.ts +23 -172
  29. package/src/cli/dev.test.ts +90 -5
  30. package/src/cli/dev.ts +39 -16
  31. package/src/cli/doctor.ts +98 -5
  32. package/src/cli/load-config.images.test.ts +31 -1
  33. package/src/cli/load-config.ts +2 -2
  34. package/src/cli/safe-defaults.test.ts +1 -1
  35. package/src/client/notes-contract.test.ts +3 -4
  36. package/src/compiler/aot.test.ts +1 -2
  37. package/src/compiler/extract.test.ts +76 -21
  38. package/src/compiler/extract.ts +15 -3
  39. package/src/compiler/fixtures/raw/raw-unannotated.ts +2 -4
  40. package/src/compiler/fixtures/skyport/oke.config.ts +4 -2
  41. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +4 -8
  42. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -2
  43. package/src/compiler/fixtures/skyport/src/flows/support/index.ts +1 -2
  44. package/src/compiler/fixtures/skyport.expected.json +3 -3
  45. package/src/compiler/fixtures/triggers/five-triggers.ts +5 -10
  46. package/src/compiler/generate-adopt.test.ts +85 -0
  47. package/src/compiler/generate-adopt.ts +111 -0
  48. package/src/config/driver-defaults.test.ts +122 -0
  49. package/src/config/driver-defaults.ts +186 -0
  50. package/src/config/index.ts +97 -7
  51. package/src/console/server/dry-run.audit.test.ts +2 -2
  52. package/src/console/server/flows.ts +50 -150
  53. package/src/docker/compose.ts +13 -3
  54. package/src/docker/images-config.test.ts +168 -0
  55. package/src/docker/recipes/llama-cpp.ts +10 -1
  56. package/src/docker/stack-id.ts +1 -0
  57. package/src/elements/channel/declare.ts +27 -1
  58. package/src/elements/clock/chaos-child.ts +3 -6
  59. package/src/elements/clock/chaos.test.ts +1 -2
  60. package/src/elements/clock.test.ts +2 -4
  61. package/src/elements/signal/declare.ts +25 -1
  62. package/src/elements/store/declare.ts +27 -1
  63. package/src/elements/store/resource.ts +6 -11
  64. package/src/elements/store/upsert-app.test.ts +1 -2
  65. package/src/elements/vault/declare.ts +29 -1
  66. package/src/kernel/adopt-barrel-fresh.test.ts +103 -0
  67. package/src/kernel/app.ts +66 -9
  68. package/src/kernel/auto-registry.test.ts +198 -0
  69. package/src/kernel/boot-bind/channel.ts +12 -4
  70. package/src/kernel/boot-bind/clock.ts +3 -3
  71. package/src/kernel/boot-bind/gate.ts +7 -4
  72. package/src/kernel/boot-bind/honor-config.test.ts +5 -5
  73. package/src/kernel/boot-bind/journal.ts +3 -3
  74. package/src/kernel/boot-bind/signal.ts +3 -1
  75. package/src/kernel/boot-bind/store.test.ts +19 -0
  76. package/src/kernel/boot-bind/store.ts +24 -9
  77. package/src/kernel/boot-bind/vault.ts +3 -3
  78. package/src/kernel/boot.test.ts +19 -15
  79. package/src/kernel/boot.ts +81 -1
  80. package/src/kernel/call.test.ts +5 -10
  81. package/src/kernel/compensate.test.ts +3 -6
  82. package/src/kernel/concurrency.test.ts +3 -5
  83. package/src/kernel/correlation.test.ts +4 -8
  84. package/src/kernel/edge.test.ts +1 -1
  85. package/src/kernel/effects-stamping.test.ts +4 -7
  86. package/src/kernel/element-registries.ts +27 -0
  87. package/src/kernel/errors.ts +11 -0
  88. package/src/kernel/flow.test.ts +8 -15
  89. package/src/kernel/flow.ts +12 -14
  90. package/src/kernel/hook-timing.test.ts +2 -2
  91. package/src/kernel/hooks.test.ts +5 -10
  92. package/src/kernel/horizontal-child.ts +5 -10
  93. package/src/kernel/journal-boot.test.ts +2 -4
  94. package/src/kernel/pipeline.test.ts +6 -12
  95. package/src/kernel/plugin/capabilities.test.ts +1 -1
  96. package/src/kernel/plugin/decorate.test.ts +3 -8
  97. package/src/kernel/plugin/scoping.test.ts +6 -16
  98. package/src/kernel/plugin-elements.test.ts +1 -1
  99. package/src/kernel/plugin-needs.test.ts +1 -1
  100. package/src/kernel/ready.test.ts +1 -2
  101. package/src/kernel/registry-isolation.test.ts +5 -5
  102. package/src/kernel/triggers.ts +1 -1
  103. package/src/plugins/anonymous.ts +1 -3
  104. package/src/plugins/compression.test.ts +6 -8
  105. package/src/plugins/config-source.test.ts +1 -1
  106. package/src/plugins/config-source.ts +1 -2
  107. package/src/plugins/cors.test.ts +10 -10
  108. package/src/plugins/csrf.test.ts +2 -2
  109. package/src/plugins/headers.test.ts +14 -15
  110. package/src/plugins/ip-allowlist.test.ts +9 -9
  111. package/src/plugins/magic-link.ts +2 -6
  112. package/src/plugins/maintenance-mode.test.ts +6 -6
  113. package/src/plugins/otp.ts +5 -15
  114. package/src/plugins/passkey.ts +4 -12
  115. package/src/plugins/two-factor.ts +3 -9
  116. package/src/plugins/username.ts +2 -6
  117. package/src/release/measure.ts +1 -1
  118. package/src/runs/runs.test.ts +3 -5
  119. package/src/runtime/serve.test.ts +2 -4
  120. package/src/test/create-test-app.test.ts +3 -7
  121. package/src/test/provisions.integration.test.ts +4 -9
  122. package/src/test/reset-element-registries.ts +31 -0
  123. package/site/content/docs/console/access.mdx +0 -29
  124. package/site/content/docs/console/ai.mdx +0 -35
  125. package/site/content/docs/console/architecture.mdx +0 -35
  126. package/site/content/docs/console/channels.mdx +0 -37
  127. package/site/content/docs/console/clock.mdx +0 -51
  128. package/site/content/docs/console/flows.mdx +0 -31
  129. package/site/content/docs/console/gates.mdx +0 -73
  130. package/site/content/docs/console/index.mdx +0 -54
  131. package/site/content/docs/console/manifest-diff.mdx +0 -34
  132. package/site/content/docs/console/meta.json +0 -24
  133. package/site/content/docs/console/overview.mdx +0 -40
  134. package/site/content/docs/console/plugins.mdx +0 -41
  135. package/site/content/docs/console/privacy.mdx +0 -32
  136. package/site/content/docs/console/runs.mdx +0 -50
  137. package/site/content/docs/console/signals.mdx +0 -40
  138. package/site/content/docs/console/store.mdx +0 -32
  139. package/site/content/docs/console/tenancy.mdx +0 -32
  140. package/site/content/docs/console/traces.mdx +0 -38
  141. package/site/content/docs/console/vault.mdx +0 -41
@@ -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
+ });
@@ -1,29 +0,0 @@
1
- ---
2
- title: "Access"
3
- description: "Identities, roles, API keys."
4
- icon: "Users"
5
- source: "docs/spec/console.md"
6
- ---
7
-
8
- Answers: **identities, roles, API keys**
9
-
10
- <Callout title="Governing rule">
11
- Granting an application scope to an operator is impossible in the UI — taught by absence, not
12
- refusal.
13
- </Callout>
14
-
15
- ## What this panel shows
16
-
17
- <Cards>
18
- <Card title="Two planes" description="Operators and application users never mix." />
19
- <Card title="Key creation" description="Value shown once; attenuation to creator scopes only." />
20
- <Card title="Revocation" description="Blast radius plus honest JWT validity delay." />
21
- <Card title="Hygiene" description="Unused keys, never-signed-in operators, expired invites." />
22
- </Cards>
23
-
24
- ## Catalog
25
-
26
- <Cards>
27
- <Card title="Dev" description="✓" />
28
- <Card title="Prod" description="✓ (admin)" />
29
- </Cards>
@@ -1,35 +0,0 @@
1
- ---
2
- title: "AI"
3
- description: "Prompt versions, eval scores, cost, agent runs."
4
- icon: "Sparkles"
5
- source: "docs/spec/console.md"
6
- ---
7
-
8
- Answers: **prompt versions, eval scores, cost, agent runs**
9
-
10
- <Callout title="Governing rule">
11
- Everything else in the Console is deterministic; this panel is built on distributions, not single
12
- values.
13
- </Callout>
14
-
15
- ## What this panel shows
16
-
17
- <Cards>
18
- <Card title="Prompt versions" description="A version bump is Manifest Diff blast radius." />
19
- <Card title="Eval + shape" description="Schema-validation failure is its own class." />
20
- <Card
21
- title="Agent tools"
22
- description="Tools are the app’s own flows — undeclared reach is denied and shown."
23
- />
24
- <Card
25
- title="Budgets"
26
- description="Cost, semantic cache, and `allowPii` as standing review surfaces."
27
- />
28
- </Cards>
29
-
30
- ## Catalog
31
-
32
- <Cards>
33
- <Card title="Dev" description="full" />
34
- <Card title="Prod" description="read + budgets" />
35
- </Cards>
@@ -1,35 +0,0 @@
1
- ---
2
- title: "Architecture"
3
- description: "How it all connects — the diagram that is the code."
4
- icon: "Network"
5
- source: "docs/spec/console.md"
6
- ---
7
-
8
- Answers: **how it all connects — the diagram that _is_ the code**
9
-
10
- <Callout title="Governing rule">
11
- Flows answers “which one”; Architecture answers “what shape”. Never shows the whole system by
12
- default.
13
- </Callout>
14
-
15
- ## What this panel shows
16
-
17
- <Cards>
18
- <Card title="Unit clusters" description="Default aggregated edges; focus one or two hops." />
19
- <Card title="Element layers" description="Toggle data, messaging, time, external edges." />
20
- <Card
21
- title="Live traffic"
22
- description="Edge thickness from real traffic; dashed = never traversed."
23
- />
24
- <Card
25
- title="Pathologies"
26
- description="Cycles, god nodes, orphan signals, SPOFs — computed from the graph."
27
- />
28
- </Cards>
29
-
30
- ## Catalog
31
-
32
- <Cards>
33
- <Card title="Dev" description="✓" />
34
- <Card title="Prod" description="✓" />
35
- </Cards>
@@ -1,37 +0,0 @@
1
- ---
2
- title: "Channels"
3
- description: "Templates, delivery receipts, bounces, opt-outs."
4
- icon: "Mailbox"
5
- source: "docs/spec/console.md"
6
- ---
7
-
8
- Answers: **templates, delivery receipts, bounces, opt-outs, deliverability (SPF/DKIM/DMARC)**
9
-
10
- <Callout title="Governing rule">
11
- Suppression is not failure — the taxonomy of “did not arrive” has seven states with verdicts.
12
- </Callout>
13
-
14
- ## What this panel shows
15
-
16
- <Cards>
17
- <Card title="Dev inbox" description="`console` driver lands every medium locally." />
18
- <Card
19
- title="Deliverability"
20
- description="SPF/DKIM/DMARC, bounces, complaints, suppression list."
21
- />
22
- <Card
23
- title="Fallback chains"
24
- description="Recorded as chains (e.g. WhatsApp → SMS), not single outcomes."
25
- />
26
- <Card
27
- title="Locale previews"
28
- description="Template data bound to schema; RTL-accurate previews."
29
- />
30
- </Cards>
31
-
32
- ## Catalog
33
-
34
- <Cards>
35
- <Card title="Dev" description="full + console inbox" />
36
- <Card title="Prod" description="send test, inspect delivery" />
37
- </Cards>
@@ -1,51 +0,0 @@
1
- ---
2
- title: "Clock"
3
- description: "Upcoming crons, sleeping durable flows, journal."
4
- icon: "Clock"
5
- source: "docs/spec/console.md"
6
- ---
7
-
8
- Answers: **upcoming crons, sleeping durable flows, journal**
9
-
10
- <Callout title="Governing rule">
11
- Looks like time — a forward timeline for schedules and pending wakes, not a flat grid.
12
- </Callout>
13
-
14
- ## What this panel shows
15
-
16
- <Cards>
17
- <Card title="Schedules" description="Drift, overdue, missed runs · catch-up one, lease holder." />
18
- <Card title="Pending wakes" description="Every sleeping durable flow and when it wakes." />
19
- <Card
20
- title="DST warnings"
21
- description="Gap or overlap badge — detect only, no schedule rewrite."
22
- />
23
- <Card title="Actions" description="Run now, wake early, pause; edit when overridable." />
24
- </Cards>
25
-
26
- ## Health numbers
27
-
28
- Each cron row projects four numbers plus the lease holder:
29
-
30
- | Field | Meaning |
31
- | --------------- | ----------------------------------------------------------------- |
32
- | Drift | How far the last run sat from the expected slot |
33
- | Overdue | Whether the schedule is past due |
34
- | Missed + policy | Slot count while down · always catch-up `"one"` (one fire, not N) |
35
- | Lease | Which instance holds the leader lease (empty when none) |
36
-
37
- ## Actions
38
-
39
- | Action | When |
40
- | ------------- | ---------------------------------------------------------------------- |
41
- | Pause | Any cron — not gated by `overridable` |
42
- | Edit schedule | Only when the declaration has `overridable: true`; else loud refuse |
43
- | Wake early | Sleeping durable run — advances `wakeAt` and can resume the journal |
44
- | Run now | Still requires the leader lease across replicas that share a CronStore |
45
-
46
- ## Catalog
47
-
48
- <Cards>
49
- <Card title="Dev" description="full" />
50
- <Card title="Prod" description="trigger/pause; edit if `overridable`" />
51
- </Cards>