create-oke 0.9.1 → 0.10.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 (56) hide show
  1. package/README.md +7 -5
  2. package/package.json +2 -3
  3. package/src/agents-md.ts +1 -1
  4. package/src/ai-setup/apply.ts +66 -23
  5. package/src/ai-setup/catalog.ts +1316 -35
  6. package/src/ai-setup/detect-ollama.ts +48 -0
  7. package/src/ai-setup/from-pref.ts +32 -0
  8. package/src/ai-setup/prompts.ts +430 -486
  9. package/src/ai-setup/recommend.ts +118 -101
  10. package/src/cli.test.ts +36 -7
  11. package/src/cli.ts +21 -16
  12. package/src/customize-flow.test.ts +43 -13
  13. package/src/customize-flow.ts +136 -61
  14. package/src/drivers-catalog.ts +29 -12
  15. package/src/local-okengine.test.ts +59 -0
  16. package/src/local-okengine.ts +137 -0
  17. package/src/scaffold.ts +1 -1
  18. package/src/transform.test.ts +54 -32
  19. package/src/transform.ts +46 -9
  20. package/src/wizard-select.ts +5 -6
  21. package/templates/advanced/.github/workflows/ci.yml +24 -0
  22. package/templates/advanced/.vscode/settings.json +15 -0
  23. package/templates/advanced/README.md +15 -4
  24. package/templates/advanced/drizzle.config.ts +6 -4
  25. package/templates/advanced/oke.config.ts +6 -2
  26. package/templates/advanced/package.json +5 -2
  27. package/templates/advanced/src/app.ts +2 -14
  28. package/templates/advanced/src/core/index.ts +12 -0
  29. package/templates/advanced/src/{core.ts → core/store.ts} +1 -1
  30. package/templates/advanced/src/db/migrations/.gitkeep +0 -0
  31. package/templates/{standard/src → advanced/src/db}/schema.decl.ts +1 -1
  32. package/templates/advanced/src/{seed → db/seed}/index.ts +2 -2
  33. package/templates/advanced/src/flows/notes/index.ts +2 -4
  34. package/templates/advanced/tests/advanced.test.ts +10 -9
  35. package/templates/advanced/tsconfig.json +23 -0
  36. package/templates/standard/.github/workflows/ci.yml +24 -0
  37. package/templates/standard/.vscode/settings.json +15 -0
  38. package/templates/standard/README.md +20 -9
  39. package/templates/standard/drizzle.config.ts +6 -4
  40. package/templates/standard/oke.config.ts +4 -0
  41. package/templates/standard/package.json +5 -2
  42. package/templates/standard/src/app.ts +2 -14
  43. package/templates/standard/src/core/index.ts +12 -0
  44. package/templates/standard/src/{core.ts → core/store.ts} +1 -1
  45. package/templates/standard/src/db/migrations/.gitkeep +0 -0
  46. package/templates/{advanced/src → standard/src/db}/schema.decl.ts +1 -1
  47. package/templates/standard/src/{seed → db/seed}/index.ts +2 -2
  48. package/templates/standard/src/flows/notes/index.ts +2 -4
  49. package/templates/standard/tests/standard.test.ts +13 -10
  50. package/templates/standard/tsconfig.json +23 -0
  51. /package/templates/advanced/src/{channels.ts → core/channels.ts} +0 -0
  52. /package/templates/advanced/src/{gates.ts → core/gates.ts} +0 -0
  53. /package/templates/advanced/src/{vault.ts → core/vault.ts} +0 -0
  54. /package/templates/standard/src/{channels.ts → core/channels.ts} +0 -0
  55. /package/templates/standard/src/{gates.ts → core/gates.ts} +0 -0
  56. /package/templates/standard/src/{vault.ts → core/vault.ts} +0 -0
@@ -1,17 +1,17 @@
1
1
  # Notes (standard)
2
2
 
3
3
  Local-first [okengine](https://oke.omqkhafi.dev) starter: a small **Notes** API with
4
- named Flows, Store, Signal → email, and Vault.
4
+ named Flows, Store, Signal → email, and Vault. This is a scaffold, not a finished
5
+ product — every Flow and table here is real wiring you keep or replace.
5
6
 
6
7
  ```bash
7
8
  bun install
8
- oke dev
9
- oke db seed # essential welcome note + local/docker sample notes
9
+ oke dev # auto db push; asks once whether to seed
10
10
  ```
11
11
 
12
12
  | Surface | URL |
13
13
  | ------- | --- |
14
- | App | http://127.0.0.1:6530 |
14
+ | Backend | http://127.0.0.1:6530 |
15
15
  | Console | http://127.0.0.1:6533 |
16
16
  | MCP | http://127.0.0.1:6535 |
17
17
 
@@ -28,21 +28,32 @@ curl -s http://127.0.0.1:6530/notes
28
28
  Flow names in the `oke dev` log are stable (`main.health`, `notes.create`, …) —
29
29
  never anonymous `flow_1`.
30
30
 
31
+ ## Included vs you build
32
+
33
+ | Ships ready | You still own |
34
+ | ----------- | ------------- |
35
+ | `main.health` + notes CRUD Flows | your domain Flows and routes |
36
+ | `notes` table + `oke db seed` | schema growth and seed policy |
37
+ | `note-created` Signal → email template | delivery rules, more channels |
38
+ | `APP_WEBHOOK_SECRET` Vault contract | more secrets / `vault.config` as needed |
39
+ | `.github/workflows/ci.yml` (typecheck + test) | lint, docker, deploy when you need them |
40
+
31
41
  ## Layout
32
42
 
33
43
  | Path | Role |
34
44
  | ---- | ---- |
35
- | `src/schema.decl.ts` | `notes` table |
36
- | `src/seed/` | `oke db seed` essential + dev sample notes |
45
+ | `src/core/` | stores · gates · vault · channels (barrel `index.ts`) |
46
+ | `src/db/schema.decl.ts` | `notes` table |
47
+ | `src/db/seed/` | `oke db seed` — essential + dev sample notes |
48
+ | `src/db/migrations/` | versioned drizzle migrations |
37
49
  | `src/flows/notes/` | list · create · get · archive · onCreated |
38
50
  | `src/flows/main/` | root · health |
39
- | `src/channels.ts` | `note-created` email template |
40
- | `src/vault.ts` | `APP_WEBHOOK_SECRET` |
41
51
  | `oke.config.ts` | local sqlite · docker postgres (+ redis/s3/openbao/smtp) |
52
+ | `.github/workflows/ci.yml` | `bun run typecheck` + `bun test` on push/PR |
42
53
 
43
54
  Switch mode anytime: `oke mode docker` then `oke dev` (or `oke dev --docker`).
44
55
 
45
56
  ## AI
46
57
 
47
58
  Not baked into this starter. Re-run create-oke with `--ai`, or `oke ai setup`, to
48
- add `drivers.ai` + `src/ai.ts`.
59
+ add `drivers.ai` + `src/core/ai.ts`.
@@ -5,17 +5,19 @@ import { defineConfig } from "drizzle-kit";
5
5
  *
6
6
  * Dialect comes from OKE's `store.sql` driver map (resolved by the CLI and
7
7
  * injected as `OKE_DRIZZLE_DIALECT`) — never inferred from `DATABASE_URL`
8
- * presence. `src/schema.generated.ts` is emitted from `src/schema.decl.ts`
8
+ * presence. `src/db/schema.generated.ts` is emitted from `src/db/schema.decl.ts`
9
9
  * for the active dialect as a pre-step of `oke db` / `oke dev`.
10
10
  *
11
- * Versioned prod migrations land in `./drizzle` — never mix with `.oke/`.
11
+ * Versioned prod migrations land in `./src/db/migrations` — never mix with `.oke/`.
12
12
  */
13
13
  const dialect = (process.env.OKE_DRIZZLE_DIALECT ?? "sqlite") as "sqlite" | "postgresql";
14
14
 
15
15
  export default defineConfig({
16
16
  dialect,
17
- schema: "./src/schema.generated.ts",
18
- out: "./drizzle",
17
+ schema: "./src/db/schema.generated.ts",
18
+ out: "./src/db/migrations",
19
+ // Core runtime tables (oke_crons, …) are created by drivers — not domain schema.
20
+ tablesFilter: ["!oke_*"],
19
21
  dbCredentials: {
20
22
  url:
21
23
  dialect === "postgresql"
@@ -5,6 +5,10 @@ import { defineConfig } from "okengine/config";
5
5
  * Docker/prod pins stay ready when you switch `oke mode docker`.
6
6
  */
7
7
  export default defineConfig({
8
+ db: {
9
+ declare: "src/db/schema.decl.ts",
10
+ generated: "src/db/schema.generated.ts",
11
+ },
8
12
  drivers: {
9
13
  store: {
10
14
  sql: {
@@ -5,13 +5,16 @@
5
5
  "type": "module",
6
6
  "dependencies": {
7
7
  "okengine": "file:../../../..",
8
- "drizzle-orm": "^1.0.0-rc.4",
8
+ "drizzle-orm": "1.0.0-rc.4",
9
9
  "zod": "^4.4.3"
10
10
  },
11
11
  "devDependencies": {
12
- "drizzle-kit": "^1.0.0-rc.4"
12
+ "@types/bun": "latest",
13
+ "drizzle-kit": "1.0.0-rc.4",
14
+ "typescript": "^7.0.2"
13
15
  },
14
16
  "scripts": {
17
+ "typecheck": "tsc --noEmit",
15
18
  "test": "bun test",
16
19
  "dev": "oke dev"
17
20
  }
@@ -1,7 +1,4 @@
1
- import { db } from "./core";
2
- import "./gates";
3
- import "./vault";
4
- import "./channels";
1
+ import { db, noteCreatedMail, webhookSecret } from "./core";
5
2
  import "./locales/en";
6
3
  import "./locales/ar";
7
4
 
@@ -9,22 +6,13 @@ import { oke } from "okengine";
9
6
  import * as main from "./flows/main";
10
7
  import * as notes from "./flows/notes";
11
8
  import { noteCreated } from "./flows/notes/signals";
12
- import { noteCreatedMail } from "./channels";
13
- import { webhookSecret } from "./vault";
14
9
 
15
10
  export const app = oke({
16
11
  name: "notes",
12
+ stores: [db],
17
13
  secrets: [webhookSecret],
18
14
  signals: [noteCreated],
19
15
  channel: { templates: [noteCreatedMail] },
20
16
  }).adopt({ main, notes });
21
17
 
22
18
  export type App = typeof app;
23
-
24
- Object.assign(app.$options, {
25
- env: "test",
26
- stores: [db],
27
- secrets: [webhookSecret],
28
- signals: [noteCreated],
29
- channel: { templates: [noteCreatedMail] },
30
- });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Core wiring — stores, gates, vault, channels (and AI when configured).
3
+ */
4
+
5
+ export { db } from "./store";
6
+ export { notesWrite } from "./gates";
7
+ export { webhookSecret } from "./vault";
8
+ export { noteCreatedMail } from "./channels";
9
+
10
+ import "./gates";
11
+ import "./vault";
12
+ import "./channels";
@@ -1,5 +1,5 @@
1
1
  import { store } from "okengine";
2
- import * as schema from "./schema.decl";
2
+ import * as schema from "../db/schema.decl";
3
3
 
4
4
  /** App SQL store — tables from {@link schema}. */
5
5
  export const db = store.sql("app", { schema });
File without changes
@@ -2,7 +2,7 @@ import { store, field, id, now } from "okengine";
2
2
 
3
3
  /**
4
4
  * Notes domain — abstract declarations, emitted to
5
- * `src/schema.generated.ts` for the active dialect by `oke db` / `oke dev`.
5
+ * `src/db/schema.generated.ts` for the active dialect by `oke db` / `oke dev`.
6
6
  */
7
7
  export const notes = store.schema.table("notes", {
8
8
  id: field.text().primaryKey().defaultFn(id),
@@ -1,12 +1,12 @@
1
1
  import { defineSeed, type Fx } from "okengine";
2
- import { db } from "../core";
2
+ import { db } from "../../core";
3
3
  import { notes } from "../schema.decl";
4
4
 
5
5
  /**
6
6
  * Seed data — run explicitly with `oke db seed` (never at boot).
7
7
  *
8
8
  * Categories: `essential` (every env) · `dev` (local|docker) · `prod` (prod only).
9
- * For multi-file composition (`src/seed/essential/*.ts` + arrays here), see
9
+ * For multi-file composition (`src/db/seed/essential/*.ts` + arrays here), see
10
10
  * Store docs → Seeding.
11
11
  */
12
12
 
@@ -1,10 +1,8 @@
1
1
  import { on, flow, http, gate, fail } from "okengine";
2
2
  import { eq, isNull } from "drizzle-orm";
3
3
 
4
- import { db } from "../../core";
5
- import { notes } from "../../schema.decl";
6
- import { webhookSecret } from "../../vault";
7
- import { noteCreatedMail } from "../../channels";
4
+ import { db, noteCreatedMail, webhookSecret } from "../../core";
5
+ import { notes } from "../../db/schema.decl";
8
6
  import { NoteCreateIn, NoteIdIn, NoteListOut, NoteOut, NotFound } from "./shapes";
9
7
  import { noteCreated } from "./signals";
10
8
 
@@ -13,33 +13,36 @@ afterAll(async () => {
13
13
  });
14
14
 
15
15
  test("boots — health flow is named main.health", async () => {
16
- const { data, error } = await t.api.main.health({});
16
+ const { data, error } = await t.api.main!.health!({});
17
17
  expect(error).toBeNull();
18
18
  expect(data).toEqual({ ok: true });
19
19
  });
20
20
 
21
21
  test("notes create → list → archive", async () => {
22
- const created = await t.api.notes.create({
22
+ const created = await t.api.notes!.create!({
23
23
  title: "Hello",
24
24
  body: "World from the standard starter",
25
25
  });
26
26
  expect(created.error).toBeNull();
27
- expect(created.data?.title).toBe("Hello");
28
- expect(created.data?.id).toBeTruthy();
27
+ const row = created.data as { id: string; title: string };
28
+ expect(row.title).toBe("Hello");
29
+ expect(row.id).toBeTruthy();
29
30
 
30
31
  await t.signals.drain();
31
32
  expect(t.channels.sent().some((s) => s.template === "note-created")).toBe(true);
32
33
 
33
- const listed = await t.api.notes.list({});
34
+ const listed = await t.api.notes!.list!({});
34
35
  expect(listed.error).toBeNull();
35
- expect(listed.data?.notes.some((n) => n.id === created.data!.id)).toBe(true);
36
+ const notes = (listed.data as { notes: { id: string }[] }).notes;
37
+ expect(notes.some((n) => n.id === row.id)).toBe(true);
36
38
 
37
- const archived = await t.api.notes.archive({ id: created.data!.id });
39
+ const archived = await t.api.notes!.archive!({ id: row.id });
38
40
  expect(archived.error).toBeNull();
39
- expect(archived.data?.archivedAt).toBeTypeOf("number");
41
+ expect((archived.data as { archivedAt: number }).archivedAt).toBeTypeOf("number");
40
42
 
41
- const after = await t.api.notes.list({});
42
- expect(after.data?.notes.some((n) => n.id === created.data!.id)).toBe(false);
43
+ const after = await t.api.notes!.list!({});
44
+ const afterNotes = (after.data as { notes: { id: string }[] }).notes;
45
+ expect(afterNotes.some((n) => n.id === row.id)).toBe(false);
43
46
  });
44
47
 
45
48
  /** Flag-free `app.fetch` must auto-boot. */
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["ESNext"],
4
+ "target": "ESNext",
5
+ "module": "ESNext",
6
+ "moduleDetection": "force",
7
+ "types": ["bun"],
8
+ "moduleResolution": "bundler",
9
+ "allowImportingTsExtensions": true,
10
+ "verbatimModuleSyntax": true,
11
+ "noEmit": true,
12
+ "rewriteRelativeImportExtensions": true,
13
+ "strict": true,
14
+ "skipLibCheck": true,
15
+ "noFallthroughCasesInSwitch": true,
16
+ "noUncheckedIndexedAccess": true,
17
+ "noImplicitOverride": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "erasableSyntaxOnly": true
21
+ },
22
+ "include": ["src/**/*.ts", "tests/**/*.ts", "oke.config.ts", "drizzle.config.ts"]
23
+ }