okengine 0.21.0 → 0.22.0

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 (160) hide show
  1. package/AGENTS.md +4 -4
  2. package/LICENSE +202 -0
  3. package/NOTICE +2 -0
  4. package/README.md +15 -9
  5. package/manifest.v1.schema.json +10 -0
  6. package/package.json +22 -25
  7. package/site/content/docs/ai/skills.mdx +1 -1
  8. package/site/content/docs/client/calling.mdx +27 -19
  9. package/site/content/docs/client/index.mdx +1 -1
  10. package/site/content/docs/elements/channel/email.mdx +1 -1
  11. package/site/content/docs/elements/channel/index.mdx +1 -1
  12. package/site/content/docs/elements/flow/http.mdx +4 -4
  13. package/site/content/docs/elements/flow/index.mdx +3 -3
  14. package/site/content/docs/elements/flow/meta.json +1 -1
  15. package/site/content/docs/elements/gate/tenancy.mdx +1 -1
  16. package/site/content/docs/elements/vault/rotation.mdx +15 -0
  17. package/site/content/docs/plugins/otp.mdx +31 -31
  18. package/site/content/docs/recipes/mailpit.mdx +1 -1
  19. package/site/content/docs/recipes/meilisearch.mdx +1 -1
  20. package/site/content/docs/recipes/pgdog.mdx +1 -1
  21. package/site/content/docs/reference/configuration.mdx +2 -2
  22. package/site/content/docs/reference/errors.mdx +12 -2
  23. package/site/content/docs/reference/fx.mdx +28 -15
  24. package/site/content/docs/reference/idempotency.mdx +192 -0
  25. package/site/content/docs/reference/index.mdx +6 -1
  26. package/site/content/docs/reference/meta.json +2 -1
  27. package/site/content/docs/understand/meta.json +1 -1
  28. package/site/content/docs/{elements/flow → understand}/routing.mdx +11 -10
  29. package/site/content/docs/understand/the-architecture.mdx +14 -12
  30. package/site/content/docs/understand/try-it.mdx +3 -3
  31. package/src/cli/competitor-mention-removal.test.ts +3 -3
  32. package/src/cli/dev.test.ts +105 -2
  33. package/src/cli/dev.ts +37 -2
  34. package/src/cli/docker-cli.test.ts +1 -1
  35. package/src/cli/index.ts +9 -1
  36. package/src/cli/load-config.images.test.ts +10 -10
  37. package/src/cli/load-config.ts +1 -1
  38. package/src/cli/vault-cmd.test.ts +23 -0
  39. package/src/cli/vault-cmd.ts +7 -0
  40. package/src/client/create.ts +31 -27
  41. package/src/client/live.ts +44 -101
  42. package/src/client/sse.ts +20 -66
  43. package/src/client/stream.ts +25 -67
  44. package/src/client/transport.test.ts +225 -0
  45. package/src/client/transport.ts +158 -90
  46. package/src/client/types.ts +29 -3
  47. package/src/client/wire.ts +119 -0
  48. package/src/compiler/aot.ts +3 -32
  49. package/src/compiler/dynamic.ts +13 -11
  50. package/src/compiler/effects-infer.ts +726 -16
  51. package/src/compiler/extract.test.ts +37 -0
  52. package/src/compiler/extract.ts +113 -12
  53. package/src/compiler/fixtures/skyport.expected.json +24 -0
  54. package/src/compiler/fx-follow.test.ts +164 -0
  55. package/src/compiler/fx-index.ts +399 -0
  56. package/src/compiler/interpret.ts +45 -0
  57. package/src/console/server/flows-invoke.test.ts +2 -2
  58. package/src/console/ui-next/dist/assets/{access-page-tIbsiphz.js → access-page-Ze6ckjdd.js} +2 -2
  59. package/src/console/ui-next/dist/assets/agent-disclosure-Bkohc_8X.js +1 -0
  60. package/src/console/ui-next/dist/assets/{cache-glyph-BCC-DxKT.js → cache-glyph-rL0lHxat.js} +1 -1
  61. package/src/console/ui-next/dist/assets/{call-pii-button-jOmYISdc.js → call-pii-button-Deo4PP18.js} +1 -1
  62. package/src/console/ui-next/dist/assets/{collapsible-DC2xNaAb.js → collapsible-CoJ6amHf.js} +1 -1
  63. package/src/console/ui-next/dist/assets/{copy-inline-button-CAYD18cr.js → copy-inline-button-CWG5_ZOh.js} +1 -1
  64. package/src/console/ui-next/dist/assets/{detail-header-DVWNjWjg.js → detail-header-b_ZzIsmU.js} +1 -1
  65. package/src/console/ui-next/dist/assets/{dropdown-menu-4h2LOVXM.js → dropdown-menu-nwXEO1Ac.js} +1 -1
  66. package/src/console/ui-next/dist/assets/duration-tone-BbQ_8z50.js +9 -0
  67. package/src/console/ui-next/dist/assets/element-icons-CwyLpVXz.js +1 -0
  68. package/src/console/ui-next/dist/assets/{explorer-empty-CJs5A-wm.js → explorer-empty-C8sSCqYT.js} +1 -1
  69. package/src/console/ui-next/dist/assets/{flows-page-gT1lsWQK.js → flows-page-D2E7BtKK.js} +1 -1
  70. package/src/console/ui-next/dist/assets/{highlighted-json-C2GZEJNI.js → highlighted-json-CPYBzDh0.js} +1 -1
  71. package/src/console/ui-next/dist/assets/{http-method-BdYjcIrD.js → http-method-DTPevKmM.js} +1 -1
  72. package/src/console/ui-next/dist/assets/index-vTuwmeQz.js +58 -0
  73. package/src/console/ui-next/dist/assets/observability-page-SJTSzHFJ.js +4 -0
  74. package/src/console/ui-next/dist/assets/{replica-lag-C4QdAF7J.js → replica-lag-BM27lXld.js} +3 -3
  75. package/src/console/ui-next/dist/assets/{request-meta-C43DHyld.js → request-meta-BjFT7DNl.js} +1 -1
  76. package/src/console/ui-next/dist/assets/shortcut-keys-CUNegEV4.js +1 -0
  77. package/src/console/ui-next/dist/assets/store-page-Qj9ThRE-.js +41 -0
  78. package/src/console/ui-next/dist/assets/{trace-detail-sheet-Dcpo4Us_.js → trace-detail-sheet-Cmitq3da.js} +2 -2
  79. package/src/console/ui-next/dist/assets/{tree-expand-toggle-CKJTuv43.js → tree-expand-toggle-CbDIB-7x.js} +2 -2
  80. package/src/console/ui-next/dist/assets/units-page-_PuYqFty.js +1 -0
  81. package/src/console/ui-next/dist/assets/{use-vault-list-CT4-gajj.js → use-vault-list-Cs45Czkt.js} +1 -1
  82. package/src/console/ui-next/dist/assets/{vault-page-BwZ9YjTW.js → vault-page-BOMr0go9.js} +2 -2
  83. package/src/console/ui-next/dist/index.html +2 -2
  84. package/src/console/ui-next/src/features/units/detail/flow-contract-panel.tsx +9 -0
  85. package/src/docker/docker.test.ts +13 -13
  86. package/src/docker/images-config.test.ts +12 -12
  87. package/src/docker/stack-id.test.ts +1 -1
  88. package/src/drivers/journal-postgres.ts +168 -3
  89. package/src/drivers/meilisearch.ts +2 -0
  90. package/src/drivers/vault-builtin.test.ts +10 -0
  91. package/src/drivers/vault-builtin.ts +13 -1
  92. package/src/drivers/vault-remote-bag.ts +5 -1
  93. package/src/drivers/vault-types.ts +6 -1
  94. package/src/elements/store/emit-drizzle.ts +2 -2
  95. package/src/elements/store/live-default.test.ts +2 -0
  96. package/src/elements/store/live-http.test.ts +1 -0
  97. package/src/elements/store/resource-list-docs.test.ts +2 -2
  98. package/src/elements/store/resource.test.ts +3 -3
  99. package/src/elements/store/schema-decl.test.ts +1 -0
  100. package/src/elements/store/sql-condition.ts +24 -7
  101. package/src/elements/store/sql-session.ts +20 -4
  102. package/src/elements/store/upsert-app.test.ts +1 -1
  103. package/src/elements/vault/audit.test.ts +137 -0
  104. package/src/elements/vault/audit.ts +106 -13
  105. package/src/elements/vault/boot-chain.ts +9 -1
  106. package/src/elements/vault/builtin-adapter.ts +33 -3
  107. package/src/i18n/catalogs/ar.ts +4 -0
  108. package/src/i18n/catalogs/en.ts +4 -0
  109. package/src/kernel/abort-scope.ts +33 -2
  110. package/src/kernel/app.ts +200 -20
  111. package/src/kernel/auto-cache.test.ts +6 -6
  112. package/src/kernel/boot-bind/vault.ts +1 -0
  113. package/src/kernel/boot.ts +19 -1
  114. package/src/kernel/builtin-errors.ts +8 -0
  115. package/src/kernel/client-descriptor.test.ts +78 -0
  116. package/src/kernel/client-descriptor.ts +24 -0
  117. package/src/kernel/concurrency.test.ts +33 -0
  118. package/src/kernel/effects-stamping.test.ts +2 -2
  119. package/src/kernel/errors-compiler.ts +20 -0
  120. package/src/kernel/errors-text.ts +99 -0
  121. package/src/kernel/errors.ts +90 -138
  122. package/src/kernel/external-effects.test.ts +36 -0
  123. package/src/kernel/flow.ts +24 -0
  124. package/src/kernel/fx-fetch.ts +5 -1
  125. package/src/kernel/fx-sql-handle.ts +305 -0
  126. package/src/kernel/fx.ts +42 -330
  127. package/src/kernel/idempotency-store.ts +578 -0
  128. package/src/kernel/idempotency.test.ts +556 -0
  129. package/src/kernel/idempotency.ts +304 -0
  130. package/src/kernel/index.ts +1 -0
  131. package/src/kernel/journal.ts +56 -0
  132. package/src/kernel/json-result.ts +59 -0
  133. package/src/kernel/project-out.ts +6 -1
  134. package/src/manifest/types.ts +8 -0
  135. package/src/okid-extended.ts +175 -0
  136. package/src/okid-shared.ts +103 -0
  137. package/src/okid.ts +30 -213
  138. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  139. package/src/plugins/otp.test.ts +65 -1
  140. package/src/plugins/otp.ts +59 -7
  141. package/src/release/absolute-regression.test.ts +63 -0
  142. package/src/release/build-lib.ts +9 -0
  143. package/src/release/http-graph.test.ts +25 -0
  144. package/src/release/http-graph.ts +90 -0
  145. package/src/release/index.ts +3 -0
  146. package/src/release/limits.ts +17 -2
  147. package/src/release/measure.ts +105 -14
  148. package/src/test/create-test-app.test.ts +3 -1
  149. package/src/test/create-test-app.ts +40 -0
  150. package/src/test/live-signals.test.ts +1 -0
  151. package/src/test/provisions.integration.test.ts +1 -0
  152. package/src/test/tenant-isolation.test.ts +1 -0
  153. package/src/console/ui-next/dist/assets/agent-disclosure-CSKumwS2.js +0 -1
  154. package/src/console/ui-next/dist/assets/duration-tone-CwoV56jn.js +0 -9
  155. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +0 -1
  156. package/src/console/ui-next/dist/assets/index-Cul17AcV.js +0 -63
  157. package/src/console/ui-next/dist/assets/observability-page-oY9vdYBk.js +0 -4
  158. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +0 -1
  159. package/src/console/ui-next/dist/assets/store-page-CL0D9dOq.js +0 -41
  160. package/src/console/ui-next/dist/assets/units-page-1PlT19ft.js +0 -1
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Driver-backed `fx.store` SQL handle — lazy chunk.
3
+ *
4
+ * The in-memory stub stays on the edge profile. This query builder loads
5
+ * the first time a SQL facet is opened.
6
+ */
7
+
8
+ import type { SqlRow } from "../drivers/types.ts";
9
+ import type {
10
+ SelectFromBuilder,
11
+ SelectOrderBuilder,
12
+ SqlStoreHandle,
13
+ StoreDecl,
14
+ } from "../elements/store.ts";
15
+ import { schemaTableName, sqlTableRef } from "../manifest/sql-resource.ts";
16
+ import type { CapabilityToken } from "./capability.ts";
17
+ import { DryRunWriteIsolationError, isDryRun } from "./dry-run.ts";
18
+ import type { EffectExternal } from "./effects.ts";
19
+
20
+ /** Capability-gated effect runner from {@link createFx}. */
21
+ type Gated = <T>(
22
+ kind: Parameters<CapabilityToken["assert"]>[0],
23
+ resource: string,
24
+ body: () => T | Promise<T>,
25
+ externalOf?:
26
+ | EffectExternal
27
+ | ((result: T | undefined, error: unknown) => EffectExternal | undefined),
28
+ ) => Promise<T>;
29
+
30
+ /** Inputs for {@link createGatedSqlHandle}. */
31
+ export interface GatedSqlHandleOptions {
32
+ /** Store declaration (`facet: "sql"`). */
33
+ readonly decl: StoreDecl;
34
+ /** Opens (and caches) the runtime handle. */
35
+ readonly open: () => Promise<SqlStoreHandle>;
36
+ /** Flow `gated` helper. */
37
+ readonly gated: Gated;
38
+ /** Capability token for per-table ref checks. */
39
+ readonly capability: CapabilityToken;
40
+ /** `fx.ask` — optional search rerank. */
41
+ readonly ask: (model: string, input: unknown) => Promise<unknown>;
42
+ }
43
+
44
+ /**
45
+ * Lazy, capability-gated proxy over a driver-backed {@link SqlStoreHandle}.
46
+ *
47
+ * @param options - Declaration, opener, gate, and rerank hook
48
+ */
49
+ export function createGatedSqlHandle(options: GatedSqlHandleOptions): SqlStoreHandle {
50
+ const { decl, open, gated, capability, ask } = options;
51
+ const ref = decl.ref as `sql:${string}`;
52
+ let cached: SqlStoreHandle | undefined;
53
+ const ensure = async (): Promise<SqlStoreHandle> => {
54
+ if (!cached) cached = await open();
55
+ return cached;
56
+ };
57
+ /** Driver-backed SQL has no dry-run transaction — refuse writes. */
58
+ const refuseDryRunWrite = (): void => {
59
+ if (isDryRun()) {
60
+ throw new DryRunWriteIsolationError(
61
+ `Driver-backed store "${ref}" cannot isolate writes during dry-run; dry-run refused rather than risk a double-write.`,
62
+ );
63
+ }
64
+ };
65
+ /**
66
+ * Gate a table-scoped SQL operation. Prefers the precise `sql:<table>`
67
+ * ref (matches what the compiler's AST inference derives from the same
68
+ * call site); falls back to the store-level ref when the table ref isn't
69
+ * declared — flows that hand-declared `effects: { writes: ["sql:<store>"] }`
70
+ * keep working. Ledger / journal record whichever ref the capability check
71
+ * actually matched.
72
+ *
73
+ * @param kind - Effect kind
74
+ * @param table - Table argument passed to a `SqlStoreHandle` method
75
+ * @param body - Work to run under the gate
76
+ */
77
+ const gatedTable = <T>(
78
+ kind: Parameters<CapabilityToken["assert"]>[0],
79
+ table: unknown,
80
+ body: () => T | Promise<T>,
81
+ ): Promise<T> => {
82
+ const externalOf = (): EffectExternal | undefined => cached?.external;
83
+ const name = schemaTableName(table);
84
+ if (name !== undefined) {
85
+ const perTable = sqlTableRef(name);
86
+ if (perTable !== ref && capability.allows(kind, perTable)) {
87
+ return gated(kind, perTable, body, externalOf);
88
+ }
89
+ }
90
+ return gated(kind, ref, body, externalOf);
91
+ };
92
+
93
+ return {
94
+ ref,
95
+ get routedRole() {
96
+ return cached?.routedRole ?? "primary";
97
+ },
98
+ get driverId() {
99
+ return cached?.driverId ?? "memory";
100
+ },
101
+ select: ((columns?: unknown) => {
102
+ return {
103
+ from(table: unknown) {
104
+ const run = (plan: {
105
+ where?: unknown;
106
+ orders?: readonly unknown[];
107
+ limit?: number;
108
+ offset?: number;
109
+ }): Promise<SqlRow[]> =>
110
+ gatedTable("read", table, async () => {
111
+ const h = await ensure();
112
+ const from = h.select(columns).from(table) as SelectFromBuilder;
113
+ const filtered = plan.where === undefined ? from : from.where(plan.where);
114
+ const ordered =
115
+ plan.orders === undefined ? filtered : filtered.orderBy(...plan.orders);
116
+ if (plan.offset !== undefined) return ordered.offset(plan.offset);
117
+ return plan.limit === undefined ? ordered : ordered.limit(plan.limit);
118
+ });
119
+
120
+ const tail = (plan: {
121
+ where?: unknown;
122
+ orders?: readonly unknown[];
123
+ }): SelectOrderBuilder => ({
124
+ limit(n) {
125
+ return run({ ...plan, limit: n });
126
+ },
127
+ offset(n) {
128
+ return run({ ...plan, offset: n });
129
+ },
130
+ then(onfulfilled, onrejected) {
131
+ return run(plan).then(onfulfilled, onrejected);
132
+ },
133
+ });
134
+
135
+ return {
136
+ where(where: unknown) {
137
+ return {
138
+ ...tail({ where }),
139
+ orderBy: (...orders: readonly unknown[]) => tail({ where, orders }),
140
+ };
141
+ },
142
+ orderBy: (...orders: readonly unknown[]) => tail({ orders }),
143
+ limit(n: number) {
144
+ return run({ limit: n });
145
+ },
146
+ offset(n: number) {
147
+ return run({ offset: n });
148
+ },
149
+ then(
150
+ onfulfilled: (value: SqlRow[]) => unknown,
151
+ onrejected?: (reason: unknown) => unknown,
152
+ ) {
153
+ return run({}).then(onfulfilled, onrejected);
154
+ },
155
+ };
156
+ },
157
+ };
158
+ }) as SqlStoreHandle["select"],
159
+ insert(table) {
160
+ return {
161
+ values(row) {
162
+ const runExecute = () =>
163
+ gatedTable("write", table, async () => {
164
+ refuseDryRunWrite();
165
+ const h = await ensure();
166
+ await h.insert(table).values(row).execute();
167
+ });
168
+ return {
169
+ returning() {
170
+ return gatedTable("write", table, async () => {
171
+ refuseDryRunWrite();
172
+ const h = await ensure();
173
+ return h.insert(table).values(row).returning();
174
+ });
175
+ },
176
+ execute: runExecute,
177
+ then(onfulfilled, onrejected) {
178
+ return runExecute().then(onfulfilled, onrejected);
179
+ },
180
+ };
181
+ },
182
+ };
183
+ },
184
+ update(table) {
185
+ return {
186
+ set(row) {
187
+ return {
188
+ where(where) {
189
+ return gatedTable("write", table, async () => {
190
+ refuseDryRunWrite();
191
+ const h = await ensure();
192
+ return h.update(table).set(row).where(where);
193
+ });
194
+ },
195
+ };
196
+ },
197
+ };
198
+ },
199
+ findById(table, id) {
200
+ return gatedTable("read", table, async () => {
201
+ const h = await ensure();
202
+ return h.findById(table, id);
203
+ });
204
+ },
205
+ delete(table: Parameters<SqlStoreHandle["delete"]>[0], id?: string) {
206
+ if (id !== undefined) {
207
+ return gatedTable("write", table, async () => {
208
+ refuseDryRunWrite();
209
+ const h = await ensure();
210
+ return h.delete(table, id);
211
+ });
212
+ }
213
+ return {
214
+ where(where: unknown) {
215
+ return gatedTable("write", table, async () => {
216
+ refuseDryRunWrite();
217
+ const h = await ensure();
218
+ return h.delete(table).where(where);
219
+ });
220
+ },
221
+ };
222
+ },
223
+ exists(table, idOrWhere) {
224
+ return gatedTable("read", table, async () => {
225
+ const h = await ensure();
226
+ return h.exists(table, idOrWhere);
227
+ });
228
+ },
229
+ upsert(table, matchOn, values, upsertOptions) {
230
+ return gatedTable("write", table, async () => {
231
+ refuseDryRunWrite();
232
+ const h = await ensure();
233
+ return h.upsert(table, matchOn, values, upsertOptions);
234
+ });
235
+ },
236
+ increment(table, id, column, by) {
237
+ return gatedTable("write", table, async () => {
238
+ refuseDryRunWrite();
239
+ const h = await ensure();
240
+ return h.increment(table, id, column, by);
241
+ });
242
+ },
243
+ raw(sql, params) {
244
+ return gated("read", ref, async () => {
245
+ const h = await ensure();
246
+ return h.raw(sql, params);
247
+ });
248
+ },
249
+ count(table, where) {
250
+ return gatedTable("read", table, async () => {
251
+ const h = await ensure();
252
+ return h.count(table, where);
253
+ });
254
+ },
255
+ page(table, pageOptions) {
256
+ return gatedTable("read", table, async () => {
257
+ const h = await ensure();
258
+ return h.page(table, pageOptions);
259
+ });
260
+ },
261
+ search(table, searchOptions) {
262
+ return gatedTable("read", table, async () => {
263
+ const h = await ensure();
264
+ const result = await h.search(table, searchOptions);
265
+ // Optional rerank via fx.ask — only when explicitly requested.
266
+ if (
267
+ searchOptions.rerank &&
268
+ typeof searchOptions.rerank === "object" &&
269
+ searchOptions.rerank.model
270
+ ) {
271
+ const model = searchOptions.rerank.model;
272
+ const pk = "id";
273
+ const docs = result.data.map((row) => ({
274
+ id: String(row[pk] ?? ""),
275
+ text: Object.values(row)
276
+ .filter((v) => typeof v === "string")
277
+ .join("\n"),
278
+ score: 0,
279
+ }));
280
+ const out = (await ask(model, {
281
+ query: searchOptions.query,
282
+ docs,
283
+ })) as { rankedIds?: string[] };
284
+ if (out.rankedIds && out.rankedIds.length > 0) {
285
+ const byId = new Map(result.data.map((r) => [String(r[pk] ?? ""), r]));
286
+ return {
287
+ ...result,
288
+ data: out.rankedIds
289
+ .map((id) => byId.get(id))
290
+ .filter((r): r is NonNullable<typeof r> => r !== undefined),
291
+ };
292
+ }
293
+ }
294
+ return result;
295
+ });
296
+ },
297
+ ensureTable(table) {
298
+ return gatedTable("write", table, async () => {
299
+ refuseDryRunWrite();
300
+ const h = await ensure();
301
+ return h.ensureTable(table);
302
+ });
303
+ },
304
+ } as SqlStoreHandle;
305
+ }