okengine 0.3.5 → 0.4.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/AGENTS.md +6 -0
- package/manifest.v1.schema.json +43 -1
- package/package.json +27 -12
- package/site/content/docs/ai/skills.mdx +11 -7
- package/site/content/docs/console/vault.mdx +4 -0
- package/site/content/docs/elements/ai.mdx +2 -0
- package/site/content/docs/elements/channel.mdx +7 -4
- package/site/content/docs/elements/clock.mdx +2 -4
- package/site/content/docs/elements/flow.mdx +19 -10
- package/site/content/docs/elements/gate.mdx +18 -11
- package/site/content/docs/elements/signal.mdx +9 -12
- package/site/content/docs/elements/store.mdx +69 -8
- package/site/content/docs/elements/vault.mdx +11 -12
- package/site/content/docs/get-started/basic-usage.mdx +76 -41
- package/site/content/docs/get-started/installation.mdx +95 -43
- package/site/content/docs/get-started/introduction.mdx +128 -75
- package/site/content/docs/get-started/meta.json +1 -1
- package/site/content/docs/get-started/why.mdx +141 -0
- package/site/content/docs/plugins/ip-allowlist.mdx +1 -2
- package/site/content/docs/plugins/security-headers.mdx +1 -1
- package/site/content/docs/reference/configuration.mdx +2 -2
- package/site/content/docs/reference/environment-variables.mdx +10 -0
- package/site/content/docs/reference/fx.mdx +56 -3
- package/site/content/docs/reference/plugins.mdx +18 -18
- package/src/cli/competitor-mention-removal.test.ts +117 -0
- package/src/cli/dev.ts +20 -0
- package/src/cli/meilisearch-local.test.ts +69 -0
- package/src/cli/meilisearch-local.ts +188 -0
- package/src/compiler/extract.test.ts +63 -0
- package/src/compiler/extract.ts +36 -15
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +5 -1
- package/src/console/server/channels.ts +2 -0
- package/src/console/server/clock.ts +3 -0
- package/src/console/server/flows.ts +13 -0
- package/src/console/server/gates.ts +2 -0
- package/src/console/server/plugins.ts +20 -1
- package/src/console/server/signals.ts +5 -0
- package/src/console/server/store.test.ts +17 -0
- package/src/console/server/store.ts +35 -1
- package/src/console/ui/channels/types.ts +1 -0
- package/src/console/ui/clock/types.ts +1 -0
- package/src/console/ui/display.test.ts +14 -0
- package/src/console/ui/display.ts +9 -0
- package/src/console/ui/dist/assets/index-CjxwRGVv.js +10 -0
- package/src/console/ui/dist/assets/panel-access-BGv45snf.js +64 -0
- package/src/console/ui/dist/assets/{panel-ai-D_m6WQI8.js → panel-ai-B2S7LEii.js} +1 -1
- package/src/console/ui/dist/assets/{panel-architecture-CKnXFyUx.js → panel-architecture-D7UJh91v.js} +1 -1
- package/src/console/ui/dist/assets/panel-channels-9T3ybqRu.js +1 -0
- package/src/console/ui/dist/assets/panel-clock-Cb1UXGRQ.js +1 -0
- package/src/console/ui/dist/assets/{panel-diff-cdonmH8c.js → panel-diff-DmYbKWmN.js} +1 -1
- package/src/console/ui/dist/assets/panel-flows-PiHwT55z.js +48 -0
- package/src/console/ui/dist/assets/panel-gates-BQGYXvjT.js +1 -0
- package/src/console/ui/dist/assets/panel-overview-BBnRO18l.js +1 -0
- package/src/console/ui/dist/assets/panel-plugins-D0PsmVw2.js +1 -0
- package/src/console/ui/dist/assets/panel-runs-CWuRDe0r.js +1 -0
- package/src/console/ui/dist/assets/panel-signals-Bbg4ewpP.js +1 -0
- package/src/console/ui/dist/assets/panel-store-CPCbsDRa.js +1 -0
- package/src/console/ui/dist/assets/panel-traces-DVAzuA_S.js +1 -0
- package/src/console/ui/dist/assets/panel-vault-D1_MvOmo.js +1 -0
- package/src/console/ui/dist/assets/{rolldown-runtime-CNC7AqOf.js → rolldown-runtime-B0Z9INg1.js} +1 -1
- package/src/console/ui/dist/index.html +2 -2
- package/src/console/ui/gates/types.ts +1 -0
- package/src/console/ui/plugins/fixture.ts +7 -0
- package/src/console/ui/plugins/types.ts +3 -0
- package/src/console/ui/shell/client.ts +3 -0
- package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +7 -2
- package/src/console/ui/shell/panels/clock/ClockPanel.tsx +9 -2
- package/src/console/ui/shell/panels/gates/GatesPanel.tsx +12 -5
- package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +18 -4
- package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +13 -2
- package/src/console/ui/shell/panels/store/StorePanel.tsx +11 -4
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +9 -3
- package/src/console/ui/signals/types.ts +1 -0
- package/src/console/ui/store/fixture.ts +5 -0
- package/src/console/ui/store/types.ts +2 -0
- package/src/docker/compose.ts +5 -0
- package/src/docker/docker.test.ts +41 -0
- package/src/docker/recipes/index.ts +10 -2
- package/src/docker/recipes/meilisearch.ts +31 -0
- package/src/drivers/conformance.test.ts +26 -0
- package/src/drivers/conformance.ts +40 -3
- package/src/drivers/drizzle-dialect.test.ts +4 -0
- package/src/drivers/drizzle-dialect.ts +8 -4
- package/src/drivers/index.ts +18 -2
- package/src/drivers/libsql.ts +179 -0
- package/src/drivers/meilisearch.integration.test.ts +77 -0
- package/src/drivers/meilisearch.test.ts +181 -0
- package/src/drivers/meilisearch.ts +208 -0
- package/src/drivers/memory.ts +4 -4
- package/src/drivers/pglite.ts +79 -0
- package/src/drivers/pgvector.ts +60 -25
- package/src/drivers/types.ts +106 -16
- package/src/drivers/vault-driver-removal.test.ts +6 -0
- package/src/drivers/vault-types.ts +4 -4
- package/src/elements/ai/runtime.ts +6 -0
- package/src/elements/ai.test.ts +22 -0
- package/src/elements/channel/declare.ts +5 -0
- package/src/elements/clock/declare.ts +5 -0
- package/src/elements/clock/durable.ts +7 -1
- package/src/elements/gate/declare.ts +28 -5
- package/src/elements/gate.ts +1 -0
- package/src/elements/signal/declare.ts +5 -0
- package/src/elements/store/declare.ts +10 -2
- package/src/elements/store/index-boot.test.ts +299 -0
- package/src/elements/store/runtime.ts +110 -17
- package/src/elements/store/schema-decl.ts +7 -0
- package/src/elements/store.ts +2 -0
- package/src/elements/vault.test.ts +27 -4
- package/src/elements/vault.ts +1 -1
- package/src/index.ts +4 -0
- package/src/kernel/abort-scope.ts +116 -0
- package/src/kernel/app.ts +12 -2
- package/src/kernel/boot-bind/store.test.ts +68 -1
- package/src/kernel/boot-bind/store.ts +92 -2
- package/src/kernel/concurrency.test.ts +294 -0
- package/src/kernel/concurrency.ts +220 -0
- package/src/kernel/flow.ts +9 -0
- package/src/kernel/fx.test.ts +23 -2
- package/src/kernel/fx.ts +80 -4
- package/src/kernel/index.ts +23 -0
- package/src/kernel/journal.ts +9 -0
- package/src/kernel/plugin/capabilities.test.ts +18 -0
- package/src/kernel/plugin.ts +11 -3
- package/src/kernel/redacted.ts +74 -0
- package/src/kernel/registry.ts +29 -6
- package/src/kernel/router.ts +3 -3
- package/src/manifest/types.ts +21 -0
- package/src/release/measure.ts +4 -0
- package/src/test/provisions.integration.test.ts +1 -1
- package/site/content/docs/get-started/comparison.mdx +0 -65
- package/src/console/ui/dist/assets/index-BWo8R7NR.js +0 -10
- package/src/console/ui/dist/assets/panel-access-C0J2D-a2.js +0 -64
- package/src/console/ui/dist/assets/panel-channels-BOmQ-onL.js +0 -1
- package/src/console/ui/dist/assets/panel-clock-giAq0Ccv.js +0 -1
- package/src/console/ui/dist/assets/panel-flows-DlCU5zjA.js +0 -45
- package/src/console/ui/dist/assets/panel-gates-XclZxWD5.js +0 -1
- package/src/console/ui/dist/assets/panel-overview-BznEOTnb.js +0 -1
- package/src/console/ui/dist/assets/panel-plugins-CcGM1g64.js +0 -1
- package/src/console/ui/dist/assets/panel-runs-CGWNHLR4.js +0 -1
- package/src/console/ui/dist/assets/panel-signals-CNywkdak.js +0 -1
- package/src/console/ui/dist/assets/panel-store-KmTbFHMH.js +0 -1
- package/src/console/ui/dist/assets/panel-traces-DBLx2ilD.js +0 -1
- package/src/console/ui/dist/assets/panel-vault-CEnFc0dk.js +0 -1
- package/src/drivers/vault-infisical.ts +0 -57
package/src/kernel/fx.ts
CHANGED
|
@@ -32,9 +32,21 @@ import {
|
|
|
32
32
|
touchDryRunStore,
|
|
33
33
|
} from "./dry-run.ts";
|
|
34
34
|
import { fail, type FailOptions, type FlowFailure } from "./errors.ts";
|
|
35
|
+
import { currentAbortSignal } from "./abort-scope.ts";
|
|
36
|
+
import {
|
|
37
|
+
fxAll,
|
|
38
|
+
fxRace,
|
|
39
|
+
fxRetry,
|
|
40
|
+
fxUsing,
|
|
41
|
+
type FxRetryOptions,
|
|
42
|
+
type FxThunk,
|
|
43
|
+
} from "./concurrency.ts";
|
|
44
|
+
import { maskRedactedDeep, Redacted } from "./redacted.ts";
|
|
35
45
|
import type { JournalSession } from "./journal.ts";
|
|
36
46
|
import type { RunTelemetry } from "./run-telemetry.ts";
|
|
37
47
|
|
|
48
|
+
export type { FxRetryOptions, FxThunk } from "./concurrency.ts";
|
|
49
|
+
|
|
38
50
|
/** Named ref: plain string or `{ name }` element handle. */
|
|
39
51
|
export type NamedRef = string | { readonly name: string };
|
|
40
52
|
|
|
@@ -258,9 +270,13 @@ export interface Fx {
|
|
|
258
270
|
/**
|
|
259
271
|
* Read a vault secret (records `secret`).
|
|
260
272
|
*
|
|
273
|
+
* Returns a {@link Redacted} — printing / logging / serializing it yields a
|
|
274
|
+
* placeholder, never the value. Call `.reveal()` at the one boundary that
|
|
275
|
+
* needs the real value (e.g. passing a credential to a driver).
|
|
276
|
+
*
|
|
261
277
|
* @param secret - Secret name or handle
|
|
262
278
|
*/
|
|
263
|
-
vault(secret: NamedRef): string
|
|
279
|
+
vault(secret: NamedRef): Redacted<string>;
|
|
264
280
|
/** Cache surface. */
|
|
265
281
|
readonly cache: FxCache;
|
|
266
282
|
/**
|
|
@@ -337,6 +353,48 @@ export interface Fx {
|
|
|
337
353
|
* @param fn - Step body
|
|
338
354
|
*/
|
|
339
355
|
step<T>(name: string, fn: () => T | Promise<T>): Promise<T>;
|
|
356
|
+
/**
|
|
357
|
+
* Ambient abort signal for the current structured-concurrency branch.
|
|
358
|
+
* Outside `all` / `race`, a never-aborted signal.
|
|
359
|
+
*/
|
|
360
|
+
readonly signal: AbortSignal;
|
|
361
|
+
/**
|
|
362
|
+
* Run thunks in parallel. On first rejection, abort sibling branches and
|
|
363
|
+
* rethrow. Pass thunks (not started Promises) so abort scopes exist first.
|
|
364
|
+
*
|
|
365
|
+
* @param thunks - Parallel work units
|
|
366
|
+
*/
|
|
367
|
+
all<const T extends readonly unknown[]>(thunks: {
|
|
368
|
+
readonly [K in keyof T]: FxThunk<T[K]>;
|
|
369
|
+
}): Promise<{ -readonly [K in keyof T]: Awaited<T[K]> }>;
|
|
370
|
+
/**
|
|
371
|
+
* Race thunks. The first settle wins; losers are aborted.
|
|
372
|
+
*
|
|
373
|
+
* @param thunks - Competing work units
|
|
374
|
+
*/
|
|
375
|
+
race<T>(thunks: ReadonlyArray<FxThunk<T>>): Promise<T>;
|
|
376
|
+
/**
|
|
377
|
+
* Retry a thunk with exponential backoff and optional full jitter.
|
|
378
|
+
* Prefer wrapping inside {@link Fx.step} so durable replay skips completed work.
|
|
379
|
+
*
|
|
380
|
+
* @param fn - Operation
|
|
381
|
+
* @param opts - Retry policy
|
|
382
|
+
*/
|
|
383
|
+
retry<T>(fn: FxThunk<T>, opts?: FxRetryOptions): Promise<T>;
|
|
384
|
+
/**
|
|
385
|
+
* Scope a resource to `use` — `release` runs exactly once when `use`
|
|
386
|
+
* settles or the ambient abort signal fires (e.g. a sibling `fx.race`
|
|
387
|
+
* winner). Same-attempt cleanup; not journaled.
|
|
388
|
+
*
|
|
389
|
+
* @param acquire - Open the resource
|
|
390
|
+
* @param release - Cleanup, always run
|
|
391
|
+
* @param use - Work with the resource
|
|
392
|
+
*/
|
|
393
|
+
using<A, T>(
|
|
394
|
+
acquire: () => A | Promise<A>,
|
|
395
|
+
release: (resource: A) => void | Promise<void>,
|
|
396
|
+
use: (resource: A) => T | Promise<T>,
|
|
397
|
+
): Promise<T>;
|
|
340
398
|
}
|
|
341
399
|
|
|
342
400
|
/**
|
|
@@ -818,9 +876,12 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
818
876
|
data?: Record<string, unknown>,
|
|
819
877
|
): { message: string; data?: Record<string, unknown> } {
|
|
820
878
|
const vault = options.vaultRuntime;
|
|
821
|
-
|
|
879
|
+
// Redacted<T> never yields the real value, but replace instances with a
|
|
880
|
+
// placeholder so payloads stay plain JSON (and never re-wrap on replay).
|
|
881
|
+
const maskedData = data ? maskRedactedDeep(data) : undefined;
|
|
882
|
+
if (!vault) return { message, data: maskedData };
|
|
822
883
|
const safeMessage = vault.redactString(message);
|
|
823
|
-
const safeData =
|
|
884
|
+
const safeData = maskedData ? (vault.redact(maskedData) as Record<string, unknown>) : undefined;
|
|
824
885
|
return { message: safeMessage, data: safeData };
|
|
825
886
|
}
|
|
826
887
|
|
|
@@ -893,7 +954,7 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
893
954
|
duration: Math.max(0, now() - timestamp),
|
|
894
955
|
reversibility: reversibilityOf("secret"),
|
|
895
956
|
});
|
|
896
|
-
return value;
|
|
957
|
+
return new Redacted(value);
|
|
897
958
|
},
|
|
898
959
|
cache,
|
|
899
960
|
send(template, opts) {
|
|
@@ -1035,6 +1096,21 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1035
1096
|
}
|
|
1036
1097
|
return await fn();
|
|
1037
1098
|
},
|
|
1099
|
+
get signal(): AbortSignal {
|
|
1100
|
+
return currentAbortSignal();
|
|
1101
|
+
},
|
|
1102
|
+
all(thunks) {
|
|
1103
|
+
return fxAll(thunks);
|
|
1104
|
+
},
|
|
1105
|
+
race(thunks) {
|
|
1106
|
+
return fxRace(thunks);
|
|
1107
|
+
},
|
|
1108
|
+
retry(fn, opts) {
|
|
1109
|
+
return fxRetry(fn, opts);
|
|
1110
|
+
},
|
|
1111
|
+
using(acquire, release, use) {
|
|
1112
|
+
return fxUsing(acquire, release, use);
|
|
1113
|
+
},
|
|
1038
1114
|
};
|
|
1039
1115
|
|
|
1040
1116
|
return { fx, ledger, capability };
|
package/src/kernel/index.ts
CHANGED
|
@@ -118,14 +118,36 @@ export {
|
|
|
118
118
|
type FxJson,
|
|
119
119
|
type FxLog,
|
|
120
120
|
type FxOperator,
|
|
121
|
+
type FxRetryOptions,
|
|
121
122
|
type FxSearchOptions,
|
|
122
123
|
type FxSendOptions,
|
|
123
124
|
type FxStoreHandle,
|
|
124
125
|
type FxTenant,
|
|
126
|
+
type FxThunk,
|
|
125
127
|
type JsonResult,
|
|
126
128
|
type NamedRef,
|
|
127
129
|
} from "./fx.ts";
|
|
128
130
|
|
|
131
|
+
export {
|
|
132
|
+
abortError,
|
|
133
|
+
abortableSleep,
|
|
134
|
+
currentAbortSignal,
|
|
135
|
+
isAbortError,
|
|
136
|
+
linkAbort,
|
|
137
|
+
withAbortSignal,
|
|
138
|
+
} from "./abort-scope.ts";
|
|
139
|
+
|
|
140
|
+
export {
|
|
141
|
+
defaultRetryWhen,
|
|
142
|
+
fxAll,
|
|
143
|
+
fxRace,
|
|
144
|
+
fxRetry,
|
|
145
|
+
fxUsing,
|
|
146
|
+
resolveRetryDelayMs,
|
|
147
|
+
} from "./concurrency.ts";
|
|
148
|
+
|
|
149
|
+
export { isRedacted, maskRedactedDeep, REDACTED_PLACEHOLDER, Redacted } from "./redacted.ts";
|
|
150
|
+
|
|
129
151
|
export {
|
|
130
152
|
DryRunWriteIsolationError,
|
|
131
153
|
dryRunWouldHaveFired,
|
|
@@ -200,6 +222,7 @@ export {
|
|
|
200
222
|
type PluginOptions,
|
|
201
223
|
type PluginRegistration,
|
|
202
224
|
type TableContribution,
|
|
225
|
+
type PluginTableMeta,
|
|
203
226
|
type PluginTableOptions,
|
|
204
227
|
} from "./plugin.ts";
|
|
205
228
|
|
package/src/kernel/journal.ts
CHANGED
|
@@ -201,6 +201,12 @@ export interface JournalSession {
|
|
|
201
201
|
* @param execute - Side-effecting body
|
|
202
202
|
*/
|
|
203
203
|
effect<T>(effectKind: string, resource: string, execute: () => T | Promise<T>): Promise<T>;
|
|
204
|
+
/**
|
|
205
|
+
* Rewind the replay cursor to the start of the entry list.
|
|
206
|
+
* Used by flow-level retry so a re-entered `do` replays completed steps
|
|
207
|
+
* instead of treating the cursor as past them.
|
|
208
|
+
*/
|
|
209
|
+
rewind(): void;
|
|
204
210
|
/** Persist current run status / output. */
|
|
205
211
|
commit(
|
|
206
212
|
status: JournalRunStatus,
|
|
@@ -325,6 +331,9 @@ export function createJournal(options: CreateJournalOptions): Journal {
|
|
|
325
331
|
await persist();
|
|
326
332
|
return value;
|
|
327
333
|
},
|
|
334
|
+
rewind() {
|
|
335
|
+
cursor = 0;
|
|
336
|
+
},
|
|
328
337
|
async commit(status, patch) {
|
|
329
338
|
run.status = status;
|
|
330
339
|
if (patch?.wakeAt !== undefined) run.wakeAt = patch.wakeAt;
|
|
@@ -81,4 +81,22 @@ describe("plugin capability capture", () => {
|
|
|
81
81
|
},
|
|
82
82
|
});
|
|
83
83
|
});
|
|
84
|
+
|
|
85
|
+
test("table description lands on capabilities.tables", () => {
|
|
86
|
+
const audit = plugin("audit", { version: "1.0.0" }).table("audit_events", undefined, {
|
|
87
|
+
description: "Immutable audit log",
|
|
88
|
+
plane: "operator",
|
|
89
|
+
});
|
|
90
|
+
const { api, snapshot } = createRecordingApi({
|
|
91
|
+
name: "audit",
|
|
92
|
+
version: "1.0.0",
|
|
93
|
+
});
|
|
94
|
+
audit.register(api);
|
|
95
|
+
expect(snapshot().capabilities.tables).toEqual({
|
|
96
|
+
audit_events: {
|
|
97
|
+
description: "Immutable audit log",
|
|
98
|
+
plane: "operator",
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
});
|
|
84
102
|
});
|
package/src/kernel/plugin.ts
CHANGED
|
@@ -111,9 +111,7 @@ export interface TableContribution {
|
|
|
111
111
|
* Non-column metadata (e.g. data plane). Prefer this over stuffing `plane`
|
|
112
112
|
* into the column map.
|
|
113
113
|
*/
|
|
114
|
-
readonly options?:
|
|
115
|
-
readonly plane?: "operator" | "user" | "shared" | string;
|
|
116
|
-
};
|
|
114
|
+
readonly options?: PluginTableOptions;
|
|
117
115
|
/**
|
|
118
116
|
* Opaque schema descriptor (legacy). Prefer {@link columns} + {@link options}.
|
|
119
117
|
* Still set for plane-only contributions so older readers keep working.
|
|
@@ -124,6 +122,14 @@ export interface TableContribution {
|
|
|
124
122
|
/** Options for {@link PluginApi.table} / {@link PluginDef.table}. */
|
|
125
123
|
export interface PluginTableOptions {
|
|
126
124
|
readonly plane?: "operator" | "user" | "shared" | string;
|
|
125
|
+
/** Optional human description for Console / docs (falls back to the table name). */
|
|
126
|
+
readonly description?: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Per-table metadata captured on {@link PluginCapabilities} / Manifest Plugin. */
|
|
130
|
+
export interface PluginTableMeta {
|
|
131
|
+
readonly plane?: string;
|
|
132
|
+
readonly description?: string;
|
|
127
133
|
}
|
|
128
134
|
|
|
129
135
|
/** Driver contribution. */
|
|
@@ -253,6 +259,8 @@ export interface PluginCapabilities {
|
|
|
253
259
|
readonly intercepts: readonly string[];
|
|
254
260
|
/** Declared dependencies from `.needs()`. */
|
|
255
261
|
readonly needs: readonly string[];
|
|
262
|
+
/** Optional metadata for contributed tables (description / plane). */
|
|
263
|
+
readonly tables?: Readonly<Record<string, PluginTableMeta>>;
|
|
256
264
|
}
|
|
257
265
|
|
|
258
266
|
/** Snapshot of everything a registration requested. */
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Redacted<T>` — a plain value wrapper that keeps a secret value out of
|
|
3
|
+
* logs, traces, and accidental serialization.
|
|
4
|
+
*
|
|
5
|
+
* Not an effect type: effect tracking only records which secret *names* a
|
|
6
|
+
* flow touches. This is value hygiene inside a flow body.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Placeholder used for any representation of a {@link Redacted}. */
|
|
10
|
+
export const REDACTED_PLACEHOLDER = "[redacted]";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Wrap a value so printing / logging / JSON serialization never yields the
|
|
14
|
+
* real value. The only way to read the wrapped value is {@link reveal}.
|
|
15
|
+
*/
|
|
16
|
+
export class Redacted<T> {
|
|
17
|
+
readonly #value: T;
|
|
18
|
+
|
|
19
|
+
constructor(value: T) {
|
|
20
|
+
this.#value = value;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The one explicit way to get the real value. */
|
|
24
|
+
reveal(): T {
|
|
25
|
+
return this.#value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
toString(): string {
|
|
29
|
+
return REDACTED_PLACEHOLDER;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
toJSON(): string {
|
|
33
|
+
return REDACTED_PLACEHOLDER;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
[Symbol.toPrimitive](): string {
|
|
37
|
+
return REDACTED_PLACEHOLDER;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** `util.inspect` / `console.log` — never the real value. */
|
|
41
|
+
[Symbol.for("nodejs.util.inspect.custom")](): string {
|
|
42
|
+
return REDACTED_PLACEHOLDER;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Type guard for {@link Redacted}. */
|
|
47
|
+
export function isRedacted(value: unknown): value is Redacted<unknown> {
|
|
48
|
+
return value instanceof Redacted;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Deep-walk a log/trace payload: replace every {@link Redacted} with the
|
|
53
|
+
* placeholder (never the wrapped value); leave everything else untouched.
|
|
54
|
+
*
|
|
55
|
+
* @param value - Arbitrary structured data
|
|
56
|
+
*/
|
|
57
|
+
export function maskRedactedDeep<T>(value: T): T {
|
|
58
|
+
return maskRedactedValue(value) as T;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function maskRedactedValue(value: unknown): unknown {
|
|
62
|
+
if (isRedacted(value)) return REDACTED_PLACEHOLDER;
|
|
63
|
+
if (value === null || value === undefined) return value;
|
|
64
|
+
if (Array.isArray(value)) return value.map(maskRedactedValue);
|
|
65
|
+
if (typeof value === "object") {
|
|
66
|
+
const obj = value as Record<string, unknown>;
|
|
67
|
+
const out: Record<string, unknown> = {};
|
|
68
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
69
|
+
out[k] = maskRedactedValue(v);
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
return value;
|
|
74
|
+
}
|
package/src/kernel/registry.ts
CHANGED
|
@@ -166,6 +166,7 @@ export function createRecordingApi(identity: { readonly name: string; readonly v
|
|
|
166
166
|
return {
|
|
167
167
|
api,
|
|
168
168
|
snapshot(): PluginRegistration {
|
|
169
|
+
const tableMeta = tablesMetaFromContributions(tables);
|
|
169
170
|
return {
|
|
170
171
|
capabilities: {
|
|
171
172
|
name: identity.name,
|
|
@@ -173,6 +174,7 @@ export function createRecordingApi(identity: { readonly name: string; readonly v
|
|
|
173
174
|
declares: declares.slice(),
|
|
174
175
|
intercepts: intercepts.slice(),
|
|
175
176
|
needs: needs.slice(),
|
|
177
|
+
...(tableMeta ? { tables: tableMeta } : {}),
|
|
176
178
|
},
|
|
177
179
|
hooks: { ...hooks },
|
|
178
180
|
edges: edges.slice(),
|
|
@@ -440,9 +442,12 @@ function normalizeTableContribution(
|
|
|
440
442
|
};
|
|
441
443
|
}
|
|
442
444
|
|
|
443
|
-
if (columnsOrOptions &&
|
|
445
|
+
if (columnsOrOptions && isLegacyTableOptions(columnsOrOptions)) {
|
|
444
446
|
const planeOptions: PluginTableOptions = {
|
|
445
|
-
plane: columnsOrOptions.plane
|
|
447
|
+
...(typeof columnsOrOptions.plane === "string" ? { plane: columnsOrOptions.plane } : {}),
|
|
448
|
+
...(typeof columnsOrOptions.description === "string"
|
|
449
|
+
? { description: columnsOrOptions.description }
|
|
450
|
+
: {}),
|
|
446
451
|
};
|
|
447
452
|
return {
|
|
448
453
|
name,
|
|
@@ -462,9 +467,27 @@ function normalizeTableContribution(
|
|
|
462
467
|
return { name };
|
|
463
468
|
}
|
|
464
469
|
|
|
465
|
-
function
|
|
466
|
-
value: Readonly<Record<string, unknown>>,
|
|
467
|
-
): value is { readonly plane: string } {
|
|
470
|
+
function isLegacyTableOptions(value: Readonly<Record<string, unknown>>): boolean {
|
|
468
471
|
const keys = Object.keys(value);
|
|
469
|
-
|
|
472
|
+
if (keys.length === 0) return false;
|
|
473
|
+
return keys.every((k) => k === "plane" || k === "description");
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function tablesMetaFromContributions(
|
|
477
|
+
contributions: readonly TableContribution[],
|
|
478
|
+
): Record<string, { readonly plane?: string; readonly description?: string }> | undefined {
|
|
479
|
+
if (contributions.length === 0) return undefined;
|
|
480
|
+
const out: Record<string, { readonly plane?: string; readonly description?: string }> = {};
|
|
481
|
+
let any = false;
|
|
482
|
+
for (const t of contributions) {
|
|
483
|
+
const plane = t.options?.plane;
|
|
484
|
+
const description = t.options?.description;
|
|
485
|
+
if (plane === undefined && description === undefined) continue;
|
|
486
|
+
any = true;
|
|
487
|
+
out[t.name] = {
|
|
488
|
+
...(plane !== undefined ? { plane } : {}),
|
|
489
|
+
...(description !== undefined ? { description } : {}),
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
return any ? out : undefined;
|
|
470
493
|
}
|
package/src/kernel/router.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTTP router — compiled RegExp matching with a Trie fallback
|
|
3
|
-
*
|
|
2
|
+
* HTTP router — compiled RegExp matching with a Trie fallback, plus a linear
|
|
3
|
+
* preset for cold-start edge builds.
|
|
4
4
|
*
|
|
5
5
|
* Chosen at build/startup: try RegExp first; on unsupported patterns fall
|
|
6
6
|
* back to Trie. The `edge` preset uses LinearRouter for fast registration.
|
|
@@ -141,7 +141,7 @@ function matchCompiled<T>(dyn: CompiledDynamic<T>, path: string): RouteMatch<T>
|
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
143
|
* Compiled RegExp router — O(1) static map, plus per-bucket compiled
|
|
144
|
-
* regular expressions for parametric routes
|
|
144
|
+
* regular expressions for parametric routes.
|
|
145
145
|
*/
|
|
146
146
|
export class RegExpRouter<T> implements Router<T> {
|
|
147
147
|
readonly name = "RegExpRouter";
|
package/src/manifest/types.ts
CHANGED
|
@@ -165,6 +165,8 @@ export interface Flow {
|
|
|
165
165
|
/** One Signal declaration. */
|
|
166
166
|
export interface Signal {
|
|
167
167
|
delivery: SignalDelivery;
|
|
168
|
+
/** Optional human description (falls back to the signal map key). */
|
|
169
|
+
description?: string;
|
|
168
170
|
retries?: number;
|
|
169
171
|
deadLetter?: boolean;
|
|
170
172
|
schema?: JsonSchema;
|
|
@@ -201,6 +203,8 @@ export interface DeclaredColumn extends ColumnClassification {
|
|
|
201
203
|
default?: string | number | boolean | null;
|
|
202
204
|
/** Database column name (snake_case by default). */
|
|
203
205
|
sqlName?: string;
|
|
206
|
+
/** Optional human description (falls back to the column map key). */
|
|
207
|
+
description?: string;
|
|
204
208
|
/** Foreign key when `.references()` was declared. */
|
|
205
209
|
references?: DeclaredColumnReference;
|
|
206
210
|
}
|
|
@@ -217,6 +221,8 @@ export interface Table {
|
|
|
217
221
|
/** One Store declaration. */
|
|
218
222
|
export interface Store {
|
|
219
223
|
facet: StoreFacet;
|
|
224
|
+
/** Optional human description (falls back to the store map key). */
|
|
225
|
+
description?: string;
|
|
220
226
|
tables?: Record<string, Table>;
|
|
221
227
|
namespaces?: string[];
|
|
222
228
|
buckets?: string[];
|
|
@@ -230,6 +236,8 @@ export interface Clock {
|
|
|
230
236
|
every?: string;
|
|
231
237
|
timezone?: string;
|
|
232
238
|
overridable?: boolean;
|
|
239
|
+
/** Optional human description (falls back to the clock map key). */
|
|
240
|
+
description?: string;
|
|
233
241
|
}
|
|
234
242
|
|
|
235
243
|
/** Named gate — policy or rate strategy. */
|
|
@@ -242,6 +250,8 @@ export interface Gate {
|
|
|
242
250
|
keyBy?: string;
|
|
243
251
|
scopes?: string[];
|
|
244
252
|
roles?: string[];
|
|
253
|
+
/** Optional human description (falls back to the gate map key). */
|
|
254
|
+
description?: string;
|
|
245
255
|
}
|
|
246
256
|
|
|
247
257
|
/** Vault secret / config contract (never a secret value). */
|
|
@@ -258,6 +268,8 @@ export interface SecretContract {
|
|
|
258
268
|
|
|
259
269
|
/** Channel template. */
|
|
260
270
|
export interface Channel {
|
|
271
|
+
/** Optional human description (falls back to the channel map key). */
|
|
272
|
+
description?: string;
|
|
261
273
|
medium?: ChannelMedium;
|
|
262
274
|
locales?: string[];
|
|
263
275
|
schema?: JsonSchema;
|
|
@@ -302,12 +314,21 @@ export interface Ai {
|
|
|
302
314
|
agents?: Record<string, AiAgent>;
|
|
303
315
|
}
|
|
304
316
|
|
|
317
|
+
/** Per-table metadata on a Manifest {@link Plugin}. */
|
|
318
|
+
export interface PluginTable {
|
|
319
|
+
plane?: string;
|
|
320
|
+
/** Optional human description (falls back to the table name). */
|
|
321
|
+
description?: string;
|
|
322
|
+
}
|
|
323
|
+
|
|
305
324
|
/** Plugin capability declaration. */
|
|
306
325
|
export interface Plugin {
|
|
307
326
|
origin?: PluginOrigin;
|
|
308
327
|
version?: string;
|
|
309
328
|
declares?: string[];
|
|
310
329
|
intercepts?: string[];
|
|
330
|
+
/** Optional metadata for `table:*` contributions. */
|
|
331
|
+
tables?: Record<string, PluginTable>;
|
|
311
332
|
}
|
|
312
333
|
|
|
313
334
|
/** Tenancy configuration (resolver is code; isolation is data). */
|
package/src/release/measure.ts
CHANGED
|
@@ -56,7 +56,7 @@ describe("Provisions integration", () => {
|
|
|
56
56
|
effects: { secrets: ["STRIPE_KEY"], emits: ["order-news"] },
|
|
57
57
|
do: async ({ orderId }, fx) => {
|
|
58
58
|
const key = fx.vault(stripeKey);
|
|
59
|
-
expect(key.startsWith("sk_")).toBe(true);
|
|
59
|
+
expect(key.reveal().startsWith("sk_")).toBe(true);
|
|
60
60
|
|
|
61
61
|
await fx.step("create-intent", async () => ({ id: `pi_${orderId}` }));
|
|
62
62
|
await fx.clock.sleep("verify-window", "2m");
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Comparison
|
|
3
|
-
description: How OKE compares to Hono, Elysia, Encore.ts, and iii — and when to pick it.
|
|
4
|
-
source: docs/spec/unified-theory.md
|
|
5
|
-
icon: Scale
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
> **OKE is the batteries-included TypeScript backend for the Bun era:** contract-first APIs with end-to-end type safety, declarative infrastructure primitives, an OpenTelemetry-native Console, secure-by-default auth and ABAC — pure TypeScript, Web-Standards portable, MIT-licensed, self-hostable with zero cloud lock-in.
|
|
9
|
-
|
|
10
|
-
_"Encore's batteries and dashboard, Elysia's speed and DX, Hono's portability — without the Rust lock-in, the cloud gravity, or the source-available license."_
|
|
11
|
-
|
|
12
|
-
## When to pick OKE
|
|
13
|
-
|
|
14
|
-
Choose OKE if you want:
|
|
15
|
-
|
|
16
|
-
- **One mental model** for HTTP, jobs, consumers, and durable work (`on(Trigger) → Effects`)
|
|
17
|
-
- **Batteries included** — store, signals, clock, gates, vault, channels, and AI as first-class elements
|
|
18
|
-
- **A local Console** derived from your code (not a separate SaaS product you must adopt)
|
|
19
|
-
- **MIT + self-host** with drivers named after protocols (`postgres`, `redis`, `s3`), not vendors
|
|
20
|
-
- Effects that power cache invalidation, live queries, and least privilege **without hand annotations**
|
|
21
|
-
|
|
22
|
-
Prefer a thinner router (Hono / Elysia alone) if you only need HTTP and will assemble queues, cron, and auth yourself.
|
|
23
|
-
|
|
24
|
-
## Matrix
|
|
25
|
-
|
|
26
|
-
| | Hono | Elysia | Encore.ts | iii | **OKE** |
|
|
27
|
-
| ----------------------------------------------- | --------------- | ----------- | ------------------- | --------------------- | --------------------------------- |
|
|
28
|
-
| Primary runtime | Multi (Web Std) | Bun-first | Node + Rust core | Rust engine, polyglot | **Bun-first, Web-Std portable** |
|
|
29
|
-
| License | MIT | MIT | MPL-2.0 | ELv2 engine | **MIT** |
|
|
30
|
-
| Typed client | hc RPC | Eden Treaty | generated | SDK | **contract-first + live queries** |
|
|
31
|
-
| DB · queue · cron · storage | ❌ | ❌ | ✅ | ✅ | **✅ (7 elements)** |
|
|
32
|
-
| Durable workflows | ❌ | ❌ | ❌ | partial | **✅ (a flag)** |
|
|
33
|
-
| Local Console | ❌ | ❌ | ✅ | ✅ | **✅ (dev + prod)** |
|
|
34
|
-
| Auto cache invalidation | ❌ | ❌ | ❌ | ❌ | **✅ (from effects)** |
|
|
35
|
-
| Least-privilege by compiler | ❌ | ❌ | ❌ | ❌ | **✅** |
|
|
36
|
-
| Human channels (email/SMS/WA) | ❌ | ❌ | ❌ | ❌ | **✅ (7th element)** |
|
|
37
|
-
| AI in the application (models, prompts, agents) | ❌ | ❌ | ❌ | ❌ | **✅ (8th element)** |
|
|
38
|
-
| Self-host, no lock-in | ✅ | ✅ | ✅ (Cloud optional) | ⚠️ | **✅ first-class** |
|
|
39
|
-
|
|
40
|
-
## vs Collections of libraries
|
|
41
|
-
|
|
42
|
-
Frameworks that ship a router plus a queue library plus a cron library plus websockets share one flaw: the concepts do not derive from one another. Documentation sprawls; each new fashion adds a concept that never gets removed.
|
|
43
|
-
|
|
44
|
-
OKE starts from one law so docs, traces, hooks, and agent surfaces stay **one shape**.
|
|
45
|
-
|
|
46
|
-
## vs Cloud-gravity platforms
|
|
47
|
-
|
|
48
|
-
OKE is MIT, self-hostable, and names drivers after **protocols** — not vendors. Vendor choice lives in images keyed by role. There is no separate “deploy to our cloud” product you must adopt to get the Console.
|
|
49
|
-
|
|
50
|
-
## vs Rolling your own effect system
|
|
51
|
-
|
|
52
|
-
Effects are inferred from what a Flow touches through `fx`. Cache invalidation, live queries, least privilege, deterministic tests, and Manifest Diff fall out of that one decision — without hand-written annotations for each capability.
|
|
53
|
-
|
|
54
|
-
<Cards>
|
|
55
|
-
<Card
|
|
56
|
-
title="Installation"
|
|
57
|
-
description="Install and scaffold in minutes."
|
|
58
|
-
href="/docs/get-started/installation"
|
|
59
|
-
/>
|
|
60
|
-
<Card
|
|
61
|
-
title="Introduction"
|
|
62
|
-
description="The one law, eight elements, ten exports."
|
|
63
|
-
href="/docs/get-started/introduction"
|
|
64
|
-
/>
|
|
65
|
-
</Cards>
|