okengine 0.1.6 → 0.2.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 (80) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +36 -29
  3. package/docs/spec/example.md +1187 -0
  4. package/docs/spec/unified-theory.md +1 -1
  5. package/package.json +20 -6
  6. package/src/cli/dev-app-runner.ts +6 -2
  7. package/src/cli/dev.ts +19 -10
  8. package/src/cli/doc-drift.ts +54 -21
  9. package/src/console/index.ts +25 -13
  10. package/src/console/server/app.ts +44 -7
  11. package/src/console/server/claim.test.ts +7 -4
  12. package/src/console/server/claim.ts +2 -7
  13. package/src/console/server/flows.ts +2 -6
  14. package/src/console/server/index.ts +2 -1
  15. package/src/console/server/lazy-panels.test.ts +27 -0
  16. package/src/console/server/panel-load.ts +28 -0
  17. package/src/console/server/plugin.ts +1 -1
  18. package/src/console/server/plugins.ts +7 -6
  19. package/src/console/server/public-flows.ts +12 -0
  20. package/src/console/server/state.ts +159 -122
  21. package/src/console/server/store.ts +13 -10
  22. package/src/console/ui/dist/assets/index-B71Yl_SS.js +10 -0
  23. package/src/console/ui/dist/assets/panel-access-Dd37LU2c.js +64 -0
  24. package/src/console/ui/dist/assets/panel-ai-CC7LR6-J.js +1 -0
  25. package/src/console/ui/dist/assets/panel-architecture-B5b3iKCz.js +1 -0
  26. package/src/console/ui/dist/assets/panel-channels-CeNjTKXp.js +1 -0
  27. package/src/console/ui/dist/assets/panel-clock-DgFTLoHV.js +1 -0
  28. package/src/console/ui/dist/assets/panel-diff-DxehccqB.js +1 -0
  29. package/src/console/ui/dist/assets/panel-flows-BtrVn-Eg.js +45 -0
  30. package/src/console/ui/dist/assets/panel-gates-Z9MKRGdH.js +1 -0
  31. package/src/console/ui/dist/assets/panel-overview-Bd48d9km.js +1 -0
  32. package/src/console/ui/dist/assets/panel-plugins-DWd0TowH.js +1 -0
  33. package/src/console/ui/dist/assets/panel-runs-BwsWqKeB.js +1 -0
  34. package/src/console/ui/dist/assets/panel-signals-9najbZY2.js +1 -0
  35. package/src/console/ui/dist/assets/panel-store-OHkP2pDp.js +1 -0
  36. package/src/console/ui/dist/assets/panel-traces-tn2JoY8U.js +1 -0
  37. package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +1 -0
  38. package/src/console/ui/dist/assets/rolldown-runtime-CNC7AqOf.js +1 -0
  39. package/src/console/ui/dist/assets/style-Cnl7WLya.css +3 -0
  40. package/src/console/ui/dist/index.html +14 -0
  41. package/src/docs-origin.ts +19 -0
  42. package/src/drivers/index.ts +1 -6
  43. package/src/drivers/vault-sops.ts +20 -1
  44. package/src/kernel/app.ts +1 -1
  45. package/src/kernel/boot-bind/ai.ts +31 -0
  46. package/src/kernel/boot-bind/channel.ts +27 -0
  47. package/src/kernel/boot-bind/clock.ts +74 -0
  48. package/src/kernel/boot-bind/gate.ts +28 -0
  49. package/src/kernel/boot-bind/runs.ts +28 -0
  50. package/src/kernel/boot-bind/signal.ts +68 -0
  51. package/src/kernel/boot-bind/store.ts +47 -0
  52. package/src/kernel/boot-bind/vault.ts +36 -0
  53. package/src/kernel/boot.test.ts +85 -1
  54. package/src/kernel/boot.ts +250 -212
  55. package/src/kernel/errors.registry.test.ts +4 -2
  56. package/src/kernel/errors.ts +3 -1
  57. package/src/kernel/fx.test.ts +2 -2
  58. package/src/kernel/index.ts +2 -0
  59. package/src/mcp/data.ts +1 -0
  60. package/src/mcp/docs-index.ts +252 -0
  61. package/src/mcp/docs-mcp.test.ts +176 -0
  62. package/src/mcp/docs-server.ts +233 -0
  63. package/src/mcp/docs-tools.ts +143 -0
  64. package/src/mcp/index.ts +29 -5
  65. package/src/mcp/protocol.ts +2 -1
  66. package/src/release/exports.test.ts +71 -0
  67. package/src/release/exports.ts +156 -0
  68. package/src/release/index.ts +21 -0
  69. package/src/release/limits.ts +9 -0
  70. package/src/release/measure.exports.test.ts +82 -0
  71. package/src/release/measure.ts +297 -14
  72. package/src/release/publish.ts +14 -3
  73. package/src/release/readme.test.ts +61 -0
  74. package/src/release/readme.ts +13 -0
  75. package/src/runtime/index.ts +1 -0
  76. package/src/runtime/security.test.ts +6 -1
  77. package/src/runtime/types.ts +6 -0
  78. package/src/term.test.ts +51 -0
  79. package/src/term.ts +158 -0
  80. package/src/test/create-test-app.ts +2 -0
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Lazy channel binder — loaded only when Channel is declared.
3
+ */
4
+
5
+ import { openConsoleChannel } from "../../drivers/channel-console.ts";
6
+ import {
7
+ createChannelRuntime,
8
+ type ChannelRuntime,
9
+ } from "../../elements/channel.ts";
10
+ import type { BootOptions } from "../boot.ts";
11
+
12
+ /**
13
+ * Construct a Channel runtime (console inbox default).
14
+ *
15
+ * @param options - Boot options
16
+ * @param now - Clock
17
+ */
18
+ export function bindChannel(
19
+ options: BootOptions,
20
+ now: () => number,
21
+ ): ChannelRuntime {
22
+ return createChannelRuntime({
23
+ ...(options.channel ?? {}),
24
+ drivers: options.channel?.drivers ?? [openConsoleChannel()],
25
+ now,
26
+ });
27
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Lazy clock binder — loaded only when Clock is declared.
3
+ */
4
+
5
+ import {
6
+ clock as declareClock,
7
+ createClockRuntime,
8
+ createTestClockRuntime,
9
+ type ClockDecl,
10
+ type ClockRuntime,
11
+ } from "../../elements/clock.ts";
12
+ import {
13
+ resolveDriverId,
14
+ type ConfigEnv,
15
+ } from "../../config/index.ts";
16
+ import type { BootOptions } from "../boot.ts";
17
+
18
+ /** Result of binding a clock runtime. */
19
+ export interface BindClockResult {
20
+ readonly clock: ClockRuntime;
21
+ readonly clockDecls: ReadonlyMap<string, ClockDecl>;
22
+ }
23
+
24
+ /**
25
+ * Construct / adopt a Clock runtime, register decls, reconcile.
26
+ *
27
+ * @param options - Boot options
28
+ * @param env - Active environment
29
+ * @param now - Clock
30
+ * @param prebuilt - Optional injected runtime
31
+ */
32
+ export async function bindClock(
33
+ options: BootOptions,
34
+ env: ConfigEnv,
35
+ now: () => number,
36
+ prebuilt?: ClockRuntime,
37
+ ): Promise<BindClockResult> {
38
+ const clockDriver =
39
+ resolveDriverId(options.config?.drivers?.clock, env) ??
40
+ (env === "test" ? "frozen" : "memory");
41
+ const clock =
42
+ prebuilt ??
43
+ (clockDriver === "frozen" || env === "test"
44
+ ? createTestClockRuntime(now(), { instanceId: options.instanceId })
45
+ : createClockRuntime({ instanceId: options.instanceId, now }));
46
+
47
+ const clockDecls = new Map<string, ClockDecl>();
48
+ for (const c of options.clocks ?? []) {
49
+ clockDecls.set(c.name, c);
50
+ }
51
+ for (const b of options.bindings ?? []) {
52
+ if (b.trigger.kind === "every" && !clockDecls.has(b.trigger.interval)) {
53
+ clockDecls.set(
54
+ b.trigger.interval,
55
+ declareClock(b.trigger.interval, { every: b.trigger.interval }),
56
+ );
57
+ }
58
+ }
59
+ for (const decl of clockDecls.values()) {
60
+ clock.register(decl);
61
+ }
62
+ await clock.reconcile();
63
+
64
+ if (options.onCronFire) {
65
+ const fire = options.onCronFire;
66
+ for (const name of clockDecls.keys()) {
67
+ clock.onCron(name, async () => {
68
+ await fire(name);
69
+ });
70
+ }
71
+ }
72
+
73
+ return { clock, clockDecls };
74
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Lazy gate binder — loaded only when Gate is declared (or required by AI).
3
+ */
4
+
5
+ import { memoryDrivers } from "../../drivers/memory.ts";
6
+ import {
7
+ createGateRuntime,
8
+ type GateRuntime,
9
+ } from "../../elements/gate.ts";
10
+ import type { BootOptions } from "../boot.ts";
11
+
12
+ /**
13
+ * Construct a Gate runtime backed by a memory kv namespace.
14
+ *
15
+ * @param options - Boot options
16
+ * @param now - Clock
17
+ */
18
+ export async function bindGate(
19
+ options: BootOptions,
20
+ now: () => number,
21
+ ): Promise<GateRuntime> {
22
+ const kvNs = await memoryDrivers.kv.open({ name: "oke:gates" });
23
+ return createGateRuntime({
24
+ gates: options.gates ?? [],
25
+ kv: kvNs,
26
+ now,
27
+ });
28
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Lazy runs binder — loaded only when runs are requested.
3
+ */
4
+
5
+ import {
6
+ createRunsRuntime,
7
+ memoryRunsDriver,
8
+ type CreateRunsRuntimeOptions,
9
+ type RunsRuntime,
10
+ } from "../../runs/index.ts";
11
+
12
+ /**
13
+ * Construct and open a runs runtime.
14
+ *
15
+ * @param options - Create options (when not already a runtime)
16
+ */
17
+ export async function bindRuns(
18
+ options?: CreateRunsRuntimeOptions,
19
+ ): Promise<RunsRuntime> {
20
+ const runs = createRunsRuntime({
21
+ driver: options?.driver ?? memoryRunsDriver,
22
+ ...(options ?? {}),
23
+ });
24
+ if (!runs.store) {
25
+ await runs.open();
26
+ }
27
+ return runs;
28
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Lazy signal binder — loaded only when Signal is declared.
3
+ */
4
+
5
+ import { memorySignalDriver } from "../../drivers/signal-memory.ts";
6
+ import {
7
+ createSignalRuntime,
8
+ type SignalRuntime,
9
+ } from "../../elements/signal.ts";
10
+ import {
11
+ resolveDriverId,
12
+ type ConfigEnv,
13
+ } from "../../config/index.ts";
14
+ import type { BootOptions } from "../boot.ts";
15
+
16
+ /**
17
+ * Construct a Signal runtime, register decls / binding names, start the bus.
18
+ *
19
+ * @param options - Boot options
20
+ * @param env - Active environment
21
+ * @param now - Clock
22
+ */
23
+ export async function bindSignal(
24
+ options: BootOptions,
25
+ env: ConfigEnv,
26
+ now: () => number,
27
+ ): Promise<SignalRuntime> {
28
+ const signalId =
29
+ resolveDriverId(options.config?.drivers?.signal, env) ?? "memory";
30
+ void signalId;
31
+ const signal = createSignalRuntime({
32
+ driver: memorySignalDriver,
33
+ now,
34
+ });
35
+ for (const decl of options.signals ?? []) {
36
+ signal.register(decl);
37
+ }
38
+ for (const b of options.bindings ?? []) {
39
+ if (b.trigger.kind === "signal") {
40
+ if (!signal.declarations.has(b.trigger.name)) {
41
+ signal.register({
42
+ name: b.trigger.name,
43
+ delivery: "once",
44
+ retries: 3,
45
+ deadLetter: true,
46
+ optional: true,
47
+ });
48
+ }
49
+ }
50
+ }
51
+ const bus = await signal.start();
52
+
53
+ if (options.onSignal) {
54
+ const handler = options.onSignal;
55
+ const seen = new Set<string>();
56
+ for (const b of options.bindings ?? []) {
57
+ if (b.trigger.kind !== "signal") continue;
58
+ const name = b.trigger.name;
59
+ if (seen.has(name)) continue;
60
+ seen.add(name);
61
+ await bus.subscribe(name, `oke:${name}`, async (msg) => {
62
+ await handler(name, msg.payload);
63
+ });
64
+ }
65
+ }
66
+
67
+ return signal;
68
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Lazy store binder — loaded only when Store is declared.
3
+ */
4
+
5
+ import { memoryDrivers } from "../../drivers/memory.ts";
6
+ import {
7
+ createStoreRuntime,
8
+ type StoreRuntime,
9
+ } from "../../elements/store.ts";
10
+ import {
11
+ resolveDriverId,
12
+ type ConfigEnv,
13
+ } from "../../config/index.ts";
14
+ import type { BootOptions } from "../boot.ts"; // type-only — no cycle at runtime
15
+
16
+ /**
17
+ * Construct a Store runtime and register facet declarations.
18
+ *
19
+ * @param options - Boot options
20
+ * @param env - Active environment
21
+ * @param now - Clock
22
+ */
23
+ export function bindStore(
24
+ options: BootOptions,
25
+ env: ConfigEnv,
26
+ now: () => number,
27
+ ): StoreRuntime {
28
+ const sqlId =
29
+ resolveDriverId(options.config?.drivers?.store?.sql, env) ?? "memory";
30
+ const kvId =
31
+ resolveDriverId(options.config?.drivers?.store?.kv, env) ?? "memory";
32
+ void sqlId;
33
+ void kvId;
34
+ const store = createStoreRuntime({
35
+ drivers: {
36
+ sql: memoryDrivers.sql,
37
+ kv: memoryDrivers.kv,
38
+ files: memoryDrivers.files,
39
+ index: memoryDrivers.index,
40
+ },
41
+ now,
42
+ });
43
+ for (const decl of options.stores ?? []) {
44
+ store.register?.(decl);
45
+ }
46
+ return store;
47
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Lazy vault binder — loaded only when secrets are declared.
3
+ */
4
+
5
+ import { memoryVaultDriver } from "../../drivers/vault-memory.ts";
6
+ import {
7
+ createVaultRuntime,
8
+ type VaultRuntime,
9
+ } from "../../elements/vault.ts";
10
+ import type { ConfigEnv } from "../../config/index.ts";
11
+ import type { BootOptions } from "../boot.ts";
12
+
13
+ /**
14
+ * Construct and boot a Vault runtime from BootOptions.
15
+ *
16
+ * @param options - Boot options
17
+ * @param env - Active environment
18
+ */
19
+ export async function bindVault(
20
+ options: BootOptions,
21
+ env: ConfigEnv,
22
+ ): Promise<VaultRuntime> {
23
+ const vaultSecrets = options.vault?.secrets ?? options.secrets ?? [];
24
+ const vault = createVaultRuntime({
25
+ secrets: vaultSecrets,
26
+ chain: options.vault?.chain ?? [
27
+ {
28
+ driver: memoryVaultDriver,
29
+ options: { secrets: {} },
30
+ },
31
+ ],
32
+ allowDevFallbacks: options.vault?.allowDevFallbacks ?? env !== "prod",
33
+ });
34
+ await vault.boot();
35
+ return vault;
36
+ }
@@ -7,10 +7,14 @@
7
7
  */
8
8
 
9
9
  import { afterEach, describe, expect, jest, test } from "bun:test";
10
+ import { mkdtemp, rm } from "node:fs/promises";
11
+ import { tmpdir } from "node:os";
12
+ import { join } from "node:path";
10
13
  import { memoryVaultDriver } from "../drivers/index.ts";
11
14
  import { clock } from "../elements/clock.ts";
15
+ import { store } from "../elements/store.ts";
12
16
  import { vault, VaultBootError } from "../elements/vault.ts";
13
- import { bootApplication } from "./boot.ts";
17
+ import { bootApplication, resolveElementNeeds } from "./boot.ts";
14
18
  import { flow, resetFlowSeq } from "./flow.ts";
15
19
  import { every, http } from "./triggers.ts";
16
20
  import { oke } from "./app.ts";
@@ -42,6 +46,86 @@ describe("boot — vault gaps", () => {
42
46
  expect(boot.message).toContain("DATABASE_URL");
43
47
  }
44
48
  });
49
+
50
+ test("three missing secrets still list every gap at once", async () => {
51
+ try {
52
+ await bootApplication({
53
+ env: "prod",
54
+ secrets: [
55
+ vault("A", { description: "one" }),
56
+ vault("B", { description: "two" }),
57
+ vault("C", { description: "three" }),
58
+ ],
59
+ vault: {
60
+ allowDevFallbacks: false,
61
+ chain: [{ driver: memoryVaultDriver, options: { secrets: {} } }],
62
+ },
63
+ });
64
+ expect.unreachable("boot should fail");
65
+ } catch (err) {
66
+ expect(err).toBeInstanceOf(VaultBootError);
67
+ const boot = err as VaultBootError;
68
+ expect(boot.gaps.map((g) => g.name).sort()).toEqual(["A", "B", "C"]);
69
+ }
70
+ });
71
+ });
72
+
73
+ describe("boot — lazy element needs", () => {
74
+ test("Store-only declarations do not require AI/channel/vault", () => {
75
+ const needs = resolveElementNeeds({
76
+ stores: [store.sql("notes", { schema: {} as never })],
77
+ });
78
+ expect(needs.store).toBe(true);
79
+ expect(needs.ai).toBe(false);
80
+ expect(needs.channel).toBe(false);
81
+ expect(needs.vault).toBe(false);
82
+ expect(needs.signal).toBe(false);
83
+ });
84
+
85
+ test("oke() Store-only graph stays under the prior 41.4 kB baseline", async () => {
86
+ const dir = await mkdtemp(join(tmpdir(), "oke-store-only-"));
87
+ const entry = join(dir, "entry.ts");
88
+ const appPath = join(import.meta.dir, "app.ts");
89
+ const storePath = join(import.meta.dir, "../elements/store.ts");
90
+ await Bun.write(
91
+ entry,
92
+ `import { oke } from ${JSON.stringify(appPath)};\n` +
93
+ `import { store } from ${JSON.stringify(storePath)};\n` +
94
+ `export const app = oke({ name: "notes" });\n` +
95
+ `export { store };\n`,
96
+ );
97
+ try {
98
+ const result = await Bun.build({
99
+ entrypoints: [entry],
100
+ minify: true,
101
+ target: "bun",
102
+ format: "esm",
103
+ external: [
104
+ "@duckdb/node-api",
105
+ "@duckdb/*",
106
+ "age-encryption",
107
+ "sently",
108
+ "sently/*",
109
+ "ajv",
110
+ "ajv/*",
111
+ "ajv-formats",
112
+ "oxc-parser",
113
+ "zod",
114
+ ],
115
+ });
116
+ expect(result.success).toBe(true);
117
+ let total = 0;
118
+ for (const o of result.outputs) {
119
+ const raw = await o.arrayBuffer();
120
+ if (raw.byteLength === 0) continue;
121
+ total += Bun.gzipSync(new Uint8Array(raw)).byteLength;
122
+ }
123
+ // Prior eager-bind baseline: ~41.4 kB for oke() alone.
124
+ expect(total).toBeLessThan(41_400);
125
+ } finally {
126
+ await rm(dir, { recursive: true, force: true });
127
+ }
128
+ });
45
129
  });
46
130
 
47
131
  describe("boot — capabilities from Manifest effects", () => {