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.
Files changed (144) hide show
  1. package/AGENTS.md +6 -0
  2. package/manifest.v1.schema.json +43 -1
  3. package/package.json +27 -12
  4. package/site/content/docs/ai/skills.mdx +11 -7
  5. package/site/content/docs/console/vault.mdx +4 -0
  6. package/site/content/docs/elements/ai.mdx +2 -0
  7. package/site/content/docs/elements/channel.mdx +7 -4
  8. package/site/content/docs/elements/clock.mdx +2 -4
  9. package/site/content/docs/elements/flow.mdx +19 -10
  10. package/site/content/docs/elements/gate.mdx +18 -11
  11. package/site/content/docs/elements/signal.mdx +9 -12
  12. package/site/content/docs/elements/store.mdx +69 -8
  13. package/site/content/docs/elements/vault.mdx +11 -12
  14. package/site/content/docs/get-started/basic-usage.mdx +76 -41
  15. package/site/content/docs/get-started/installation.mdx +95 -43
  16. package/site/content/docs/get-started/introduction.mdx +128 -75
  17. package/site/content/docs/get-started/meta.json +1 -1
  18. package/site/content/docs/get-started/why.mdx +141 -0
  19. package/site/content/docs/plugins/ip-allowlist.mdx +1 -2
  20. package/site/content/docs/plugins/security-headers.mdx +1 -1
  21. package/site/content/docs/reference/configuration.mdx +2 -2
  22. package/site/content/docs/reference/environment-variables.mdx +10 -0
  23. package/site/content/docs/reference/fx.mdx +56 -3
  24. package/site/content/docs/reference/plugins.mdx +18 -18
  25. package/src/cli/competitor-mention-removal.test.ts +117 -0
  26. package/src/cli/dev.ts +20 -0
  27. package/src/cli/meilisearch-local.test.ts +69 -0
  28. package/src/cli/meilisearch-local.ts +188 -0
  29. package/src/compiler/extract.test.ts +63 -0
  30. package/src/compiler/extract.ts +36 -15
  31. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +5 -1
  32. package/src/console/server/channels.ts +2 -0
  33. package/src/console/server/clock.ts +3 -0
  34. package/src/console/server/flows.ts +13 -0
  35. package/src/console/server/gates.ts +2 -0
  36. package/src/console/server/plugins.ts +20 -1
  37. package/src/console/server/signals.ts +5 -0
  38. package/src/console/server/store.test.ts +17 -0
  39. package/src/console/server/store.ts +35 -1
  40. package/src/console/ui/channels/types.ts +1 -0
  41. package/src/console/ui/clock/types.ts +1 -0
  42. package/src/console/ui/display.test.ts +14 -0
  43. package/src/console/ui/display.ts +9 -0
  44. package/src/console/ui/dist/assets/index-CjxwRGVv.js +10 -0
  45. package/src/console/ui/dist/assets/panel-access-BGv45snf.js +64 -0
  46. package/src/console/ui/dist/assets/{panel-ai-D_m6WQI8.js → panel-ai-B2S7LEii.js} +1 -1
  47. package/src/console/ui/dist/assets/{panel-architecture-CKnXFyUx.js → panel-architecture-D7UJh91v.js} +1 -1
  48. package/src/console/ui/dist/assets/panel-channels-9T3ybqRu.js +1 -0
  49. package/src/console/ui/dist/assets/panel-clock-Cb1UXGRQ.js +1 -0
  50. package/src/console/ui/dist/assets/{panel-diff-cdonmH8c.js → panel-diff-DmYbKWmN.js} +1 -1
  51. package/src/console/ui/dist/assets/panel-flows-PiHwT55z.js +48 -0
  52. package/src/console/ui/dist/assets/panel-gates-BQGYXvjT.js +1 -0
  53. package/src/console/ui/dist/assets/panel-overview-BBnRO18l.js +1 -0
  54. package/src/console/ui/dist/assets/panel-plugins-D0PsmVw2.js +1 -0
  55. package/src/console/ui/dist/assets/panel-runs-CWuRDe0r.js +1 -0
  56. package/src/console/ui/dist/assets/panel-signals-Bbg4ewpP.js +1 -0
  57. package/src/console/ui/dist/assets/panel-store-CPCbsDRa.js +1 -0
  58. package/src/console/ui/dist/assets/panel-traces-DVAzuA_S.js +1 -0
  59. package/src/console/ui/dist/assets/panel-vault-D1_MvOmo.js +1 -0
  60. package/src/console/ui/dist/assets/{rolldown-runtime-CNC7AqOf.js → rolldown-runtime-B0Z9INg1.js} +1 -1
  61. package/src/console/ui/dist/index.html +2 -2
  62. package/src/console/ui/gates/types.ts +1 -0
  63. package/src/console/ui/plugins/fixture.ts +7 -0
  64. package/src/console/ui/plugins/types.ts +3 -0
  65. package/src/console/ui/shell/client.ts +3 -0
  66. package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +7 -2
  67. package/src/console/ui/shell/panels/clock/ClockPanel.tsx +9 -2
  68. package/src/console/ui/shell/panels/gates/GatesPanel.tsx +12 -5
  69. package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +18 -4
  70. package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +13 -2
  71. package/src/console/ui/shell/panels/store/StorePanel.tsx +11 -4
  72. package/src/console/ui/shell/panels/vault/VaultPanel.tsx +9 -3
  73. package/src/console/ui/signals/types.ts +1 -0
  74. package/src/console/ui/store/fixture.ts +5 -0
  75. package/src/console/ui/store/types.ts +2 -0
  76. package/src/docker/compose.ts +5 -0
  77. package/src/docker/docker.test.ts +41 -0
  78. package/src/docker/recipes/index.ts +10 -2
  79. package/src/docker/recipes/meilisearch.ts +31 -0
  80. package/src/drivers/conformance.test.ts +26 -0
  81. package/src/drivers/conformance.ts +40 -3
  82. package/src/drivers/drizzle-dialect.test.ts +4 -0
  83. package/src/drivers/drizzle-dialect.ts +8 -4
  84. package/src/drivers/index.ts +18 -2
  85. package/src/drivers/libsql.ts +179 -0
  86. package/src/drivers/meilisearch.integration.test.ts +77 -0
  87. package/src/drivers/meilisearch.test.ts +181 -0
  88. package/src/drivers/meilisearch.ts +208 -0
  89. package/src/drivers/memory.ts +4 -4
  90. package/src/drivers/pglite.ts +79 -0
  91. package/src/drivers/pgvector.ts +60 -25
  92. package/src/drivers/types.ts +106 -16
  93. package/src/drivers/vault-driver-removal.test.ts +6 -0
  94. package/src/drivers/vault-types.ts +4 -4
  95. package/src/elements/ai/runtime.ts +6 -0
  96. package/src/elements/ai.test.ts +22 -0
  97. package/src/elements/channel/declare.ts +5 -0
  98. package/src/elements/clock/declare.ts +5 -0
  99. package/src/elements/clock/durable.ts +7 -1
  100. package/src/elements/gate/declare.ts +28 -5
  101. package/src/elements/gate.ts +1 -0
  102. package/src/elements/signal/declare.ts +5 -0
  103. package/src/elements/store/declare.ts +10 -2
  104. package/src/elements/store/index-boot.test.ts +299 -0
  105. package/src/elements/store/runtime.ts +110 -17
  106. package/src/elements/store/schema-decl.ts +7 -0
  107. package/src/elements/store.ts +2 -0
  108. package/src/elements/vault.test.ts +27 -4
  109. package/src/elements/vault.ts +1 -1
  110. package/src/index.ts +4 -0
  111. package/src/kernel/abort-scope.ts +116 -0
  112. package/src/kernel/app.ts +12 -2
  113. package/src/kernel/boot-bind/store.test.ts +68 -1
  114. package/src/kernel/boot-bind/store.ts +92 -2
  115. package/src/kernel/concurrency.test.ts +294 -0
  116. package/src/kernel/concurrency.ts +220 -0
  117. package/src/kernel/flow.ts +9 -0
  118. package/src/kernel/fx.test.ts +23 -2
  119. package/src/kernel/fx.ts +80 -4
  120. package/src/kernel/index.ts +23 -0
  121. package/src/kernel/journal.ts +9 -0
  122. package/src/kernel/plugin/capabilities.test.ts +18 -0
  123. package/src/kernel/plugin.ts +11 -3
  124. package/src/kernel/redacted.ts +74 -0
  125. package/src/kernel/registry.ts +29 -6
  126. package/src/kernel/router.ts +3 -3
  127. package/src/manifest/types.ts +21 -0
  128. package/src/release/measure.ts +4 -0
  129. package/src/test/provisions.integration.test.ts +1 -1
  130. package/site/content/docs/get-started/comparison.mdx +0 -65
  131. package/src/console/ui/dist/assets/index-BWo8R7NR.js +0 -10
  132. package/src/console/ui/dist/assets/panel-access-C0J2D-a2.js +0 -64
  133. package/src/console/ui/dist/assets/panel-channels-BOmQ-onL.js +0 -1
  134. package/src/console/ui/dist/assets/panel-clock-giAq0Ccv.js +0 -1
  135. package/src/console/ui/dist/assets/panel-flows-DlCU5zjA.js +0 -45
  136. package/src/console/ui/dist/assets/panel-gates-XclZxWD5.js +0 -1
  137. package/src/console/ui/dist/assets/panel-overview-BznEOTnb.js +0 -1
  138. package/src/console/ui/dist/assets/panel-plugins-CcGM1g64.js +0 -1
  139. package/src/console/ui/dist/assets/panel-runs-CGWNHLR4.js +0 -1
  140. package/src/console/ui/dist/assets/panel-signals-CNywkdak.js +0 -1
  141. package/src/console/ui/dist/assets/panel-store-KmTbFHMH.js +0 -1
  142. package/src/console/ui/dist/assets/panel-traces-DBLx2ilD.js +0 -1
  143. package/src/console/ui/dist/assets/panel-vault-CEnFc0dk.js +0 -1
  144. package/src/drivers/vault-infisical.ts +0 -57
@@ -21,6 +21,7 @@ import {
21
21
  } from "../../../plugins/index.ts";
22
22
  import { consoleCalls } from "../../client.ts";
23
23
  import { Button, Input } from "../../components/ui.tsx";
24
+ import { displayLabel } from "../../../display.ts";
24
25
 
25
26
  /**
26
27
  * Plugins panel — read-only catalogue + supply-chain surface.
@@ -176,10 +177,23 @@ function PluginDetail(props: {
176
177
  {p.declares.length === 0 ? (
177
178
  <p className="mt-2 text-sm text-[var(--oke-muted)]">None</p>
178
179
  ) : (
179
- <ul className="mt-2 list-inside list-disc font-mono text-sm">
180
- {p.declares.map((d) => (
181
- <li key={d}>{d}</li>
182
- ))}
180
+ <ul className="mt-2 list-inside list-disc text-sm">
181
+ {p.declares.map((d) => {
182
+ const tableName = d.startsWith("table:") ? d.slice("table:".length) : null;
183
+ const description = tableName ? p.tables[tableName]?.description : undefined;
184
+ return (
185
+ <li key={d}>
186
+ {tableName && description ? (
187
+ <>
188
+ <span>{displayLabel(tableName, description)}</span>
189
+ <code className="ml-2 font-mono text-[var(--oke-muted)]">{d}</code>
190
+ </>
191
+ ) : (
192
+ <code className="font-mono">{d}</code>
193
+ )}
194
+ </li>
195
+ );
196
+ })}
183
197
  </ul>
184
198
  )}
185
199
  </section>
@@ -31,6 +31,7 @@ import {
31
31
  } from "../../../signals/index.ts";
32
32
  import { consoleCalls } from "../../client.ts";
33
33
  import { Button } from "../../components/ui.tsx";
34
+ import { displayLabel } from "../../../display.ts";
34
35
 
35
36
  /**
36
37
  * Signals panel. List + detail + DLQ state lives in URL search params.
@@ -248,13 +249,18 @@ export function SignalsPanel() {
248
249
  onClick={() => setSearch(openSignal(search, s.name))}
249
250
  >
250
251
  <span className="font-medium">
251
- {s.name}
252
+ {displayLabel(s.name, s.description)}
252
253
  {s.orphaned ? (
253
254
  <span role="status" className="ml-2 text-xs text-[var(--oke-muted)]">
254
255
  orphaned
255
256
  </span>
256
257
  ) : null}
257
258
  </span>
259
+ {s.description ? (
260
+ <span className="font-mono text-xs text-[var(--oke-muted)]">
261
+ {s.name}
262
+ </span>
263
+ ) : null}
258
264
  <span className="text-xs text-[var(--oke-muted)]">
259
265
  {s.delivery === "once" && (
260
266
  <>
@@ -299,7 +305,12 @@ export function SignalsPanel() {
299
305
  <div className="flex flex-col gap-6">
300
306
  <div className="flex items-start justify-between gap-4">
301
307
  <div>
302
- <h2 className="text-lg font-semibold">{open.name}</h2>
308
+ <h2 className="text-lg font-semibold">
309
+ {displayLabel(open.name, open.description)}
310
+ </h2>
311
+ {open.description ? (
312
+ <p className="font-mono text-sm text-[var(--oke-muted)]">{open.name}</p>
313
+ ) : null}
303
314
  <p
304
315
  role="status"
305
316
  className="mt-2 max-w-prose text-sm leading-relaxed"
@@ -24,6 +24,7 @@ import {
24
24
  } from "../../../store/index.ts";
25
25
  import { consoleCalls } from "../../client.ts";
26
26
  import { Button } from "../../components/ui.tsx";
27
+ import { displayLabel } from "../../../display.ts";
27
28
 
28
29
  /**
29
30
  * Store panel. Tenant lives in the header when tenancy is declared.
@@ -320,7 +321,10 @@ export function StorePanel() {
320
321
  )}
321
322
  onClick={() => setSearch(openStore(search, s.ref))}
322
323
  >
323
- <span>{s.name}</span>
324
+ <span>{displayLabel(s.name, s.description)}</span>
325
+ {s.description ? (
326
+ <span className="font-mono text-xs text-[var(--oke-muted)]">{s.name}</span>
327
+ ) : null}
324
328
  <span className="text-xs text-[var(--oke-muted)]">
325
329
  {s.children.length} resource(s)
326
330
  {s.replicaLagMs != null ? ` · lag ${s.replicaLagMs}ms` : ""}
@@ -469,8 +473,11 @@ function StoreDetail(props: {
469
473
  return (
470
474
  <div className="flex flex-col gap-4">
471
475
  <div className="flex flex-wrap items-baseline gap-2">
472
- <h2 className="text-base font-medium">{open.name}</h2>
473
- <span className="text-sm text-[var(--oke-muted)]">{open.ref}</span>
476
+ <h2 className="text-base font-medium">{displayLabel(open.name, open.description)}</h2>
477
+ <span className="text-sm text-[var(--oke-muted)]">
478
+ {open.description ? `${open.name} · ` : ""}
479
+ {open.ref}
480
+ </span>
474
481
  {open.contentAddressed ? (
475
482
  <span role="status" className="text-xs text-[var(--oke-muted)]">
476
483
  content-addressed keys
@@ -610,7 +617,7 @@ function StoreDetail(props: {
610
617
  <tr>
611
618
  {Object.keys(browse.rows[0] ?? { id: 1 }).map((col) => (
612
619
  <th key={col} scope="col" className="border-b px-2 py-1">
613
- {col}
620
+ {displayLabel(col, child?.columnDescriptions[col])}
614
621
  </th>
615
622
  ))}
616
623
  <th scope="col" className="border-b px-2 py-1">
@@ -20,6 +20,7 @@ import {
20
20
  type VaultListResponse,
21
21
  type VaultSearch,
22
22
  } from "../../../vault/index.ts";
23
+ import { displayLabel } from "../../../display.ts";
23
24
  import { consoleCalls } from "../../client.ts";
24
25
  import { Button, Input } from "../../components/ui.tsx";
25
26
 
@@ -191,7 +192,10 @@ export function VaultPanel() {
191
192
  )}
192
193
  onClick={() => setSearch(openVault(search, s.name))}
193
194
  >
194
- <span className="font-mono">{s.name}</span>
195
+ <span>{displayLabel(s.name, s.description)}</span>
196
+ {s.description ? (
197
+ <span className="font-mono text-xs text-[var(--oke-muted)]">{s.name}</span>
198
+ ) : null}
195
199
  <span className="truncate text-xs">
196
200
  {s.sensitive ? (s.fingerprint ?? "unset") : (s.cleartext ?? "unset")}
197
201
  </span>
@@ -225,9 +229,11 @@ export function VaultPanel() {
225
229
  ) : (
226
230
  <div className="flex max-w-2xl flex-col gap-6">
227
231
  <div>
228
- <h2 className="font-mono text-lg text-[var(--oke-fg)]">{open.name}</h2>
232
+ <h2 className="text-lg text-[var(--oke-fg)]">
233
+ {displayLabel(open.name, open.description)}
234
+ </h2>
229
235
  {open.description ? (
230
- <p className="text-sm text-[var(--oke-muted)]">{open.description}</p>
236
+ <p className="font-mono text-sm text-[var(--oke-muted)]">{open.name}</p>
231
237
  ) : null}
232
238
  {open.rotate ? (
233
239
  <p className="text-sm text-[var(--oke-muted)]">Rotate hint: {open.rotate}</p>
@@ -39,6 +39,7 @@ export interface SignalEndpoint {
39
39
  /** One signal in the operator list. */
40
40
  export interface SignalRecord {
41
41
  readonly name: string;
42
+ readonly description?: string;
42
43
  readonly delivery: SignalDelivery;
43
44
  readonly retries: number;
44
45
  readonly deadLetterEnabled: boolean;
@@ -27,6 +27,7 @@ export const STORE_FIXTURE: readonly StoreRecord[] = [
27
27
  channels: [],
28
28
  },
29
29
  piiColumns: ["email"],
30
+ columnDescriptions: {},
30
31
  },
31
32
  {
32
33
  name: "shipments",
@@ -44,6 +45,7 @@ export const STORE_FIXTURE: readonly StoreRecord[] = [
44
45
  channels: ["booking-confirmed"],
45
46
  },
46
47
  piiColumns: [],
48
+ columnDescriptions: {},
47
49
  },
48
50
  ],
49
51
  replicaLagMs: 240,
@@ -76,6 +78,7 @@ export const STORE_FIXTURE: readonly StoreRecord[] = [
76
78
  channels: [],
77
79
  },
78
80
  piiColumns: [],
81
+ columnDescriptions: {},
79
82
  },
80
83
  ],
81
84
  replicaLagMs: null,
@@ -104,6 +107,7 @@ export const STORE_FIXTURE: readonly StoreRecord[] = [
104
107
  channels: [],
105
108
  },
106
109
  piiColumns: [],
110
+ columnDescriptions: {},
107
111
  },
108
112
  ],
109
113
  replicaLagMs: null,
@@ -138,6 +142,7 @@ export const STORE_FIXTURE: readonly StoreRecord[] = [
138
142
  channels: [],
139
143
  },
140
144
  piiColumns: [],
145
+ columnDescriptions: {},
141
146
  },
142
147
  ],
143
148
  replicaLagMs: null,
@@ -28,6 +28,7 @@ export interface StoreChild {
28
28
  readonly cache: StoreCacheView;
29
29
  readonly willNotFire: WillNotFire;
30
30
  readonly piiColumns: readonly string[];
31
+ readonly columnDescriptions: Readonly<Record<string, string>>;
31
32
  }
32
33
 
33
34
  /** One store row from `console.store.list`. */
@@ -35,6 +36,7 @@ export interface StoreRecord {
35
36
  readonly ref: string;
36
37
  readonly facet: StoreFacet;
37
38
  readonly name: string;
39
+ readonly description?: string;
38
40
  readonly children: readonly StoreChild[];
39
41
  readonly replicaLagMs: number | null;
40
42
  readonly migrationDrift: {
@@ -250,6 +250,11 @@ export function buildStackEnv(
250
250
  // Token is minted by the bootstrap — never a generated password here.
251
251
  env[`${prefix}_URL`] = url;
252
252
  env.OKE_VAULT_URL = url;
253
+ } else if (spec.role === "store.index") {
254
+ // Meilisearch: standalone HTTP URL + the generated master key.
255
+ env[`${prefix}_URL`] = url;
256
+ env.OKE_STORE_INDEX_URL = url;
257
+ env.OKE_STORE_INDEX_KEY = spec.credentials.password;
253
258
  } else {
254
259
  env[`${prefix}_USER`] = spec.credentials.user;
255
260
  env[`${prefix}_PASSWORD`] = spec.credentials.password;
@@ -43,6 +43,29 @@ describe("image recipes", () => {
43
43
  expect(redis.match("redis:7")).toBe(true);
44
44
  });
45
45
 
46
+ test("meilisearch matches the official image and emits a http URL", () => {
47
+ expect(recipeFor("getmeili/meilisearch:v1.37").id).toBe("meilisearch");
48
+ const spec: ServiceSpec = {
49
+ role: "store.index",
50
+ serviceName: "store-index",
51
+ image: "getmeili/meilisearch:v1.37",
52
+ port: 7700,
53
+ hostPort: 7700,
54
+ credentials: { user: "oke", password: "meili-master-key", database: "oke" },
55
+ };
56
+ const applied = recipeFor(spec.image).apply(spec);
57
+ expect(applied.environment?.MEILI_MASTER_KEY).toBe("${OKE_STORE_INDEX_KEY}");
58
+ expect(applied.volumes).toContain("store-index-data:/meili_data");
59
+ const url = recipeFor(spec.image).url(spec, {
60
+ host: "127.0.0.1",
61
+ port: 7700,
62
+ user: "oke",
63
+ password: "meili-master-key",
64
+ database: "oke",
65
+ });
66
+ expect(url).toBe("http://127.0.0.1:7700");
67
+ });
68
+
46
69
  test("a new image recipe is ≤15 lines", async () => {
47
70
  const src = await Bun.file(`${import.meta.dir}/recipes/postgres.ts`).text();
48
71
  const exportLines = src
@@ -197,6 +220,24 @@ describe("deriveInfrastructure", () => {
197
220
  expect(formatStackEnv(result.stackEnv)).toContain("OKE_STORE_SQL_PASSWORD=");
198
221
  });
199
222
 
223
+ test("store.index meilisearch emits its own URL + master key env", () => {
224
+ const result = deriveInfrastructure({
225
+ images: { "store.index": "getmeili/meilisearch:v1.37" },
226
+ credentials: {
227
+ "store.index": { user: "oke", password: "meili-master-key", database: "oke" },
228
+ },
229
+ app: "skyport",
230
+ });
231
+ const yml = result.files.find((f) => f.path === "compose.store.index.yml")!.content;
232
+ expect(yml).toContain("getmeili/meilisearch:v1.37");
233
+ expect(yml).toContain("${OKE_STORE_INDEX_KEY}");
234
+ expect(yml).toContain("meili_data");
235
+ expect(yml).not.toContain("meili-master-key");
236
+ expect(result.stackEnv.OKE_STORE_INDEX_URL).toBe("http://127.0.0.1:7700");
237
+ expect(result.stackEnv.OKE_STORE_INDEX_KEY).toBe("meili-master-key");
238
+ expect(result.stackEnv.DATABASE_URL).toBeUndefined();
239
+ });
240
+
200
241
  test("emits protocol-specific env keys plus optional control notes", () => {
201
242
  const result = deriveInfrastructure({
202
243
  images: {
@@ -4,15 +4,23 @@
4
4
 
5
5
  import type { ImageRecipe } from "../types.ts";
6
6
  import { mailpit } from "./mailpit.ts";
7
+ import { meilisearch } from "./meilisearch.ts";
7
8
  import { openbao } from "./openbao.ts";
8
9
  import { postgres } from "./postgres.ts";
9
10
  import { redis } from "./redis.ts";
10
11
  import { rustfs } from "./rustfs.ts";
11
12
 
12
13
  /** Default recipe catalogue. */
13
- export const builtinRecipes: readonly ImageRecipe[] = [postgres, redis, mailpit, rustfs, openbao];
14
+ export const builtinRecipes: readonly ImageRecipe[] = [
15
+ postgres,
16
+ redis,
17
+ mailpit,
18
+ rustfs,
19
+ openbao,
20
+ meilisearch,
21
+ ];
14
22
 
15
- export { mailpit, openbao, postgres, redis, rustfs };
23
+ export { mailpit, meilisearch, openbao, postgres, redis, rustfs };
16
24
 
17
25
  /**
18
26
  * Resolve the recipe for an image reference.
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Meilisearch image recipe — full-text search service (`getmeili/meilisearch`).
3
+ *
4
+ * Data lives on a named volume under `/meili_data`; the master key is injected
5
+ * via `${VAR}` (never cleartext in YAML). The official image is Alpine-based
6
+ * and musl-clean — the glibc binary gap only affects raw host installs.
7
+ */
8
+
9
+ import type { ImageRecipe } from "../types.ts";
10
+
11
+ /** Meilisearch FTS service. API on 7700. */
12
+ export const meilisearch: ImageRecipe = {
13
+ id: "meilisearch",
14
+ port: 7700,
15
+ match: (i) => /meilisearch|getmeili/i.test(i),
16
+ apply: (s) => ({
17
+ environment: {
18
+ MEILI_MASTER_KEY: "${OKE_STORE_INDEX_KEY}",
19
+ MEILI_ENV: "${OKE_MEILI_ENV:-production}",
20
+ MEILI_NO_ANALYTICS: "true",
21
+ },
22
+ volumes: [`${s.serviceName}-data:/meili_data`],
23
+ healthcheck: {
24
+ test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:7700/health || exit 1"],
25
+ interval: "5s",
26
+ timeout: "3s",
27
+ retries: 12,
28
+ },
29
+ }),
30
+ url: (_s, c) => `http://${c.host}:${c.port}`,
31
+ };
@@ -9,9 +9,14 @@ import {
9
9
  runIndexConformance,
10
10
  runKvConformance,
11
11
  runSqlConformance,
12
+ runTextIndexConformance,
12
13
  } from "./conformance.ts";
13
14
  import { fsDriver } from "./fs.ts";
15
+ import { libsqlDriver, libsqlIndexDriver } from "./libsql.ts";
14
16
  import { memoryFilesDriver, memoryIndexDriver, memoryKvDriver, memorySqlDriver } from "./memory.ts";
17
+ import { meilisearchDriver } from "./meilisearch.ts";
18
+ import { createMeilisearchFakeFetch } from "./meilisearch.test.ts";
19
+ import { pgliteDriver } from "./pglite.ts";
15
20
  import { pgvectorDriver } from "./pgvector.ts";
16
21
  import { createPostgresFakeClient, postgresDriver } from "./postgres.ts";
17
22
  import { createRedisFakeClient, redisDriver } from "./redis.ts";
@@ -21,6 +26,9 @@ import { sqliteDriver } from "./sqlite.ts";
21
26
  describe("sql conformance", () => {
22
27
  test("memory", () => runSqlConformance(memorySqlDriver));
23
28
  test("sqlite", () => runSqlConformance(sqliteDriver, { url: ":memory:" }));
29
+ test("libsql", () => runSqlConformance(libsqlDriver, { url: ":memory:" }));
30
+ // pglite cold-starts WASM + extension load — slow disks / CI need > default 5s
31
+ test("pglite", () => runSqlConformance(pgliteDriver, { url: "memory://" }), 15_000);
24
32
  test("postgres (fake Bun.SQL client)", () =>
25
33
  runSqlConformance(postgresDriver, {
26
34
  client: createPostgresFakeClient(),
@@ -46,6 +54,16 @@ describe("files conformance", () => {
46
54
  describe("index conformance", () => {
47
55
  test("memory", () => runIndexConformance(memoryIndexDriver, { dims: 3 }));
48
56
  test("pgvector", () => runIndexConformance(pgvectorDriver, { dims: 3 }));
57
+ test("libsql", () => runIndexConformance(libsqlIndexDriver, { dims: 3 }));
58
+ });
59
+
60
+ describe("text index conformance", () => {
61
+ test("meilisearch (injected fetch)", () =>
62
+ runTextIndexConformance(meilisearchDriver, {
63
+ url: "http://127.0.0.1:7700",
64
+ apiKey: "master-key",
65
+ fetch: createMeilisearchFakeFetch(),
66
+ }));
49
67
  });
50
68
 
51
69
  describe("protocol naming", () => {
@@ -53,6 +71,8 @@ describe("protocol naming", () => {
53
71
  const ids = [
54
72
  memorySqlDriver.id,
55
73
  sqliteDriver.id,
74
+ libsqlDriver.id,
75
+ pgliteDriver.id,
56
76
  postgresDriver.id,
57
77
  memoryKvDriver.id,
58
78
  redisDriver.id,
@@ -61,10 +81,14 @@ describe("protocol naming", () => {
61
81
  s3Driver.id,
62
82
  memoryIndexDriver.id,
63
83
  pgvectorDriver.id,
84
+ libsqlIndexDriver.id,
85
+ meilisearchDriver.id,
64
86
  ];
65
87
  expect(ids).toEqual([
66
88
  "memory",
67
89
  "sqlite",
90
+ "libsql",
91
+ "pglite",
68
92
  "postgres",
69
93
  "memory",
70
94
  "redis",
@@ -73,6 +97,8 @@ describe("protocol naming", () => {
73
97
  "s3",
74
98
  "memory",
75
99
  "pgvector",
100
+ "libsql",
101
+ "meilisearch",
76
102
  ]);
77
103
  for (const id of ids) {
78
104
  expect(id).not.toMatch(/neon|dragonfly|minio|upstash|cloudflare/i);
@@ -8,7 +8,14 @@
8
8
  import { expect } from "bun:test";
9
9
  import { createSqlStoreHandle } from "../elements/store/sql-session.ts";
10
10
  import { defineTable } from "../elements/store/table.ts";
11
- import type { FilesDriver, IndexDriver, KvDriver, SqlDriver, SqlConnectOptions } from "./types.ts";
11
+ import type {
12
+ FilesDriver,
13
+ KvDriver,
14
+ SqlDriver,
15
+ SqlConnectOptions,
16
+ TextIndexDriver,
17
+ VectorIndexDriver,
18
+ } from "./types.ts";
12
19
 
13
20
  /** SQL conformance against one driver. */
14
21
  export async function runSqlConformance(
@@ -118,9 +125,9 @@ export async function runFilesConformance(
118
125
  }
119
126
  }
120
127
 
121
- /** Index conformance against one driver. */
128
+ /** Vector index conformance against one driver. */
122
129
  export async function runIndexConformance(
123
- driver: IndexDriver,
130
+ driver: VectorIndexDriver,
124
131
  openOpts: {
125
132
  name?: string;
126
133
  dims?: number;
@@ -147,3 +154,33 @@ export async function runIndexConformance(
147
154
  await index.close();
148
155
  }
149
156
  }
157
+
158
+ /** Full-text index conformance against one driver. */
159
+ export async function runTextIndexConformance(
160
+ driver: TextIndexDriver,
161
+ openOpts: {
162
+ name?: string;
163
+ url?: string;
164
+ apiKey?: string;
165
+ fetch?: typeof fetch;
166
+ } = {},
167
+ ): Promise<void> {
168
+ const index = await driver.open({
169
+ name: openOpts.name ?? "conf-text",
170
+ dims: 0,
171
+ url: openOpts.url,
172
+ apiKey: openOpts.apiKey,
173
+ fetch: openOpts.fetch,
174
+ });
175
+ try {
176
+ await index.upsert("d1", { id: "d1", title: "hello world" });
177
+ await index.upsert("d2", { id: "d2", title: "goodbye world" });
178
+ const found = await index.search("hello", { topK: 1 });
179
+ expect(found.hits.length).toBeGreaterThan(0);
180
+ expect(found.hits[0]?.id).toBe("d1");
181
+ expect(await index.delete("d1")).toBe(true);
182
+ expect(index.driverId).toBe(driver.id);
183
+ } finally {
184
+ await index.close();
185
+ }
186
+ }
@@ -10,11 +10,15 @@ describe("SQL_DRIVER_TO_DRIZZLE_DIALECT", () => {
10
10
  expect(SQL_DRIVER_TO_DRIZZLE_DIALECT).toEqual({
11
11
  sqlite: "sqlite",
12
12
  postgres: "postgresql",
13
+ libsql: "sqlite",
14
+ pglite: "postgresql",
13
15
  });
14
16
  });
15
17
 
16
18
  test("drizzleDialectFromSqlDriver resolves", () => {
17
19
  expect(drizzleDialectFromSqlDriver("sqlite")).toBe("sqlite");
18
20
  expect(drizzleDialectFromSqlDriver("postgres")).toBe("postgresql");
21
+ expect(drizzleDialectFromSqlDriver("libsql")).toBe("sqlite");
22
+ expect(drizzleDialectFromSqlDriver("pglite")).toBe("postgresql");
19
23
  });
20
24
  });
@@ -4,7 +4,9 @@
4
4
  * drizzle-kit `1.0.0-rc.4` dialects (from its schema validator):
5
5
  * `postgresql` · `mysql` · `sqlite` · `turso` · `singlestore` · `mssql` ·
6
6
  * `cockroach` · `duckdb`. OKE owns kit-facing SQL drivers `sqlite` /
7
- * `postgres` only — map those, never infer from connection URLs.
7
+ * `postgres` / `libsql` / `pglite` only — map those, never infer from
8
+ * connection URLs. `libsql` speaks the sqlite wire dialect; `pglite` speaks
9
+ * postgresql (same dialect as `postgres`, different transport).
8
10
  */
9
11
 
10
12
  import type { StoreDriverId } from "./types.ts";
@@ -18,20 +20,22 @@ export type OkeDrizzleKitDialect = "sqlite" | "postgresql";
18
20
  * dialect months later.
19
21
  */
20
22
  export const SQL_DRIVER_TO_DRIZZLE_DIALECT: Record<
21
- Extract<StoreDriverId, "sqlite" | "postgres">,
23
+ Extract<StoreDriverId, "sqlite" | "postgres" | "libsql" | "pglite">,
22
24
  OkeDrizzleKitDialect
23
25
  > = {
24
26
  sqlite: "sqlite",
25
27
  postgres: "postgresql",
28
+ libsql: "sqlite",
29
+ pglite: "postgresql",
26
30
  };
27
31
 
28
32
  /**
29
33
  * Resolve the drizzle-kit dialect for an OKE SQL driver id.
30
34
  *
31
- * @param driverId - Protocol driver id (`sqlite` | `postgres`)
35
+ * @param driverId - Protocol driver id (`sqlite` | `postgres` | `libsql` | `pglite`)
32
36
  */
33
37
  export function drizzleDialectFromSqlDriver(
34
- driverId: Extract<StoreDriverId, "sqlite" | "postgres">,
38
+ driverId: Extract<StoreDriverId, "sqlite" | "postgres" | "libsql" | "pglite">,
35
39
  ): OkeDrizzleKitDialect {
36
40
  return SQL_DRIVER_TO_DRIZZLE_DIALECT[driverId];
37
41
  }
@@ -23,12 +23,18 @@ export type {
23
23
  FilesDriver,
24
24
  IndexHit,
25
25
  IndexStore,
26
+ VectorIndexStore,
27
+ TextIndexStore,
28
+ TextIndexSearchOptions,
29
+ TextIndexSearchResult,
26
30
  IndexOpenOptions,
27
31
  IndexDriver,
32
+ VectorIndexDriver,
33
+ TextIndexDriver,
28
34
  StoreDriver,
29
35
  ClassificationMap,
30
36
  } from "./types.ts";
31
- export { classificationKey } from "./types.ts";
37
+ export { classificationKey, indexDriverNeedsSql } from "./types.ts";
32
38
 
33
39
  export type {
34
40
  SignalDriverId,
@@ -77,6 +83,17 @@ export { s3Driver, openS3Bucket, createS3FakeClient } from "./s3.ts";
77
83
 
78
84
  export { pgvectorDriver, openPgvectorIndex } from "./pgvector.ts";
79
85
 
86
+ export { libsqlDriver, connectLibsql, libsqlIndexDriver, openLibsqlIndex } from "./libsql.ts";
87
+
88
+ export {
89
+ meilisearchDriver,
90
+ openMeilisearchIndex,
91
+ MeilisearchUnavailableError,
92
+ type MeilisearchIndexOptions,
93
+ } from "./meilisearch.ts";
94
+
95
+ export { pgliteDriver, connectPglite } from "./pglite.ts";
96
+
80
97
  export { memorySignalDriver, openMemorySignal } from "./signal-memory.ts";
81
98
 
82
99
  export {
@@ -103,7 +120,6 @@ export type { VaultDriverId, VaultOpenOptions, VaultBag, VaultDriver } from "./v
103
120
  export { memoryVaultDriver } from "./vault-memory.ts";
104
121
  export { envVaultDriver } from "./vault-env.ts";
105
122
  export { openbaoVaultDriver, OpenBaoUnavailableError } from "./vault-openbao.ts";
106
- export { infisicalVaultDriver } from "./vault-infisical.ts";
107
123
  export { managedVaultDriver } from "./vault-managed.ts";
108
124
 
109
125
  export type {