create-oke 0.10.2 → 0.11.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.
- package/README.md +2 -2
- package/package.json +2 -2
- package/src/ai-setup/apply.ts +123 -31
- package/src/ai-setup/catalog.ts +3 -3
- package/src/ai-setup/from-pref.ts +1 -1
- package/src/ai-setup/prompts.ts +1 -1
- package/src/cli.test.ts +183 -142
- package/src/cli.ts +190 -32
- package/src/create-defaults.test.ts +21 -15
- package/src/create-defaults.ts +33 -13
- package/src/customize-flow.test.ts +30 -35
- package/src/customize-flow.ts +68 -229
- package/src/drivers-catalog.ts +42 -48
- package/src/local-okengine.test.ts +50 -41
- package/src/local-okengine.ts +28 -14
- package/src/locales.test.ts +128 -0
- package/src/locales.ts +321 -0
- package/src/scaffold.ts +277 -33
- package/src/templates.ts +2 -8
- package/src/transform.test.ts +102 -38
- package/src/transform.ts +378 -132
- package/templates/advanced/.env.example +23 -3
- package/templates/advanced/README.md +1 -1
- package/templates/advanced/drizzle.config.ts +10 -13
- package/templates/advanced/oke.config.ts +21 -62
- package/templates/advanced/package.json +3 -1
- package/templates/advanced/src/app.ts +3 -10
- package/templates/advanced/src/core.ts +58 -0
- package/templates/advanced/src/db/schema.decl.ts +1 -1
- package/templates/advanced/src/db/seed/index.ts +2 -2
- package/templates/advanced/src/flows/notes/index.ts +3 -15
- package/templates/advanced/src/locales/index.ts +6 -0
- package/templates/advanced/tests/advanced.test.ts +1 -1
- package/templates/advanced/tsconfig.json +3 -0
- package/templates/standard/.env.example +23 -3
- package/templates/standard/README.md +1 -1
- package/templates/standard/drizzle.config.ts +10 -13
- package/templates/standard/oke.config.ts +18 -60
- package/templates/standard/package.json +3 -1
- package/templates/standard/src/app.ts +3 -10
- package/templates/standard/src/core.ts +55 -0
- package/templates/standard/src/db/schema.decl.ts +1 -1
- package/templates/standard/src/db/seed/index.ts +2 -2
- package/templates/standard/src/flows/notes/index.ts +3 -12
- package/templates/standard/src/locales/index.ts +6 -0
- package/templates/standard/tests/standard.test.ts +1 -1
- package/templates/standard/tsconfig.json +3 -0
- package/templates/advanced/src/core/channels.ts +0 -13
- package/templates/advanced/src/core/gates.ts +0 -12
- package/templates/advanced/src/core/index.ts +0 -12
- package/templates/advanced/src/core/store.ts +0 -8
- package/templates/advanced/src/core/vault.ts +0 -7
- package/templates/advanced/src/locales/ar.ts +0 -18
- package/templates/standard/src/core/channels.ts +0 -13
- package/templates/standard/src/core/gates.ts +0 -12
- package/templates/standard/src/core/index.ts +0 -12
- package/templates/standard/src/core/store.ts +0 -5
- package/templates/standard/src/core/vault.ts +0 -7
- package/templates/standard/src/locales/ar.ts +0 -18
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App core — element wiring loaded by `import "@/core"` from `app.ts`.
|
|
3
|
+
*
|
|
4
|
+
* Order matches how you usually extend the starter:
|
|
5
|
+
* locales → store → gate → vault → channel → (AI via `oke ai setup`).
|
|
6
|
+
* Keep this file until a section outgrows it, then split by element.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import "@/locales";
|
|
10
|
+
|
|
11
|
+
import { channel, gate, store, vault } from "okengine";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import * as schema from "@/db/schema.decl";
|
|
14
|
+
|
|
15
|
+
// --- Store -------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
/** SQL store for Notes (`schema.decl`). Drivers: pglite locally · postgres in docker. */
|
|
18
|
+
export const db = store.sql("app", { schema });
|
|
19
|
+
|
|
20
|
+
// --- Gate --------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Write policy for notes. Starter allows everyone — swap in real auth scopes:
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* export const notesWrite = gate.policy("notes:write", ({ auth }) =>
|
|
27
|
+
* auth.scopes.has("notes:write"),
|
|
28
|
+
* );
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export const notesWrite = gate.policy("notes:write", () => true);
|
|
32
|
+
|
|
33
|
+
// --- Vault -------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
/** HMAC secret for outbound note webhooks (`fx.vault.get` on create). */
|
|
36
|
+
export const webhookSecret = vault.secret("APP_WEBHOOK_SECRET", {
|
|
37
|
+
description: "HMAC secret for outbound note webhooks",
|
|
38
|
+
dev: "dev-webhook-secret-change-me",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// --- Channel -----------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
const mail = channel.email({ from: "Notes <notes@localhost>" });
|
|
44
|
+
|
|
45
|
+
/** Fired when a note is created (console driver locally · SMTP in docker). */
|
|
46
|
+
export const noteCreatedMail = mail.template("note-created", {
|
|
47
|
+
locales: ["en"],
|
|
48
|
+
schema: z.object({
|
|
49
|
+
id: z.string(),
|
|
50
|
+
title: z.string(),
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// --- AI ----------------------------------------------------------------------
|
|
55
|
+
// `oke ai setup` / create-oke --ai appends models here.
|
|
@@ -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/db/schema.
|
|
5
|
+
* `src/db/schema.drizzle.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,6 +1,6 @@
|
|
|
1
1
|
import { defineSeed, type Fx } from "okengine";
|
|
2
|
-
import { db } from "
|
|
3
|
-
import { notes } from "
|
|
2
|
+
import { db } from "@/core";
|
|
3
|
+
import { notes } from "@/db/schema.decl";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Seed data — run explicitly with `oke db seed` (never at boot).
|
|
@@ -1,8 +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, noteCreatedMail, webhookSecret } from "
|
|
5
|
-
import { notes } from "
|
|
4
|
+
import { db, noteCreatedMail, webhookSecret } from "@/core";
|
|
5
|
+
import { notes } from "@/db/schema.decl";
|
|
6
6
|
import { NoteCreateIn, NoteIdIn, NoteListOut, NoteOut, NotFound } from "./shapes";
|
|
7
7
|
import { noteCreated } from "./signals";
|
|
8
8
|
|
|
@@ -14,7 +14,6 @@ export const list = on(
|
|
|
14
14
|
http.get("/notes").gate(gate.public),
|
|
15
15
|
flow("notes.list", {
|
|
16
16
|
out: NoteListOut,
|
|
17
|
-
effects: { reads: ["sql:app"] },
|
|
18
17
|
do: async (_input, fx) => {
|
|
19
18
|
const rows = await fx.store(db).select().from(notes).where(isNull(notes.archivedAt));
|
|
20
19
|
const sorted = [...rows].sort((a, b) => Number(b.createdAt) - Number(a.createdAt));
|
|
@@ -37,14 +36,9 @@ export const create = on(
|
|
|
37
36
|
flow("notes.create", {
|
|
38
37
|
in: NoteCreateIn,
|
|
39
38
|
out: NoteOut,
|
|
40
|
-
effects: {
|
|
41
|
-
writes: ["sql:app"],
|
|
42
|
-
emits: ["note-created"],
|
|
43
|
-
secrets: ["APP_WEBHOOK_SECRET"],
|
|
44
|
-
},
|
|
45
39
|
do: async (input, fx) => {
|
|
46
40
|
// Prove Vault is wired (local: env/dev fallback).
|
|
47
|
-
fx.vault(webhookSecret);
|
|
41
|
+
await fx.vault.get(webhookSecret);
|
|
48
42
|
const id = fx.id();
|
|
49
43
|
const createdAt = fx.clock.now();
|
|
50
44
|
await fx.store(db).insert(notes).values({
|
|
@@ -73,7 +67,6 @@ export const get = on(
|
|
|
73
67
|
in: NoteIdIn,
|
|
74
68
|
out: NoteOut,
|
|
75
69
|
errors: { NotFound },
|
|
76
|
-
effects: { reads: ["sql:app"] },
|
|
77
70
|
do: async (input, fx) => {
|
|
78
71
|
const row = await fx.store(db).findById(notes, input.id);
|
|
79
72
|
if (!row) return fail("NotFound", { id: input.id });
|
|
@@ -95,7 +88,6 @@ export const archive = on(
|
|
|
95
88
|
in: NoteIdIn,
|
|
96
89
|
out: NoteOut,
|
|
97
90
|
errors: { NotFound },
|
|
98
|
-
effects: { reads: ["sql:app"], writes: ["sql:app"] },
|
|
99
91
|
do: async (input, fx) => {
|
|
100
92
|
const row = await fx.store(db).findById(notes, input.id);
|
|
101
93
|
if (!row) return fail("NotFound", { id: input.id });
|
|
@@ -120,7 +112,6 @@ export const archive = on(
|
|
|
120
112
|
export const onCreated = on(
|
|
121
113
|
noteCreated,
|
|
122
114
|
flow("notes.onCreated", {
|
|
123
|
-
effects: { sends: ["note-created"] },
|
|
124
115
|
do: async (payload, fx) => {
|
|
125
116
|
await fx.send(noteCreatedMail, {
|
|
126
117
|
to: "you@localhost",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { channel } from "okengine";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
|
|
4
|
-
const mail = channel.email({ from: "Notes <notes@localhost>" });
|
|
5
|
-
|
|
6
|
-
/** Email when a note is created (console driver locally · SMTP in docker). */
|
|
7
|
-
export const noteCreatedMail = mail.template("note-created", {
|
|
8
|
-
locales: ["en", "ar"],
|
|
9
|
-
schema: z.object({
|
|
10
|
-
id: z.string(),
|
|
11
|
-
title: z.string(),
|
|
12
|
-
}),
|
|
13
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { gate } from "okengine";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Starter keeps HTTP public. Replace with real policies when you add auth:
|
|
5
|
-
*
|
|
6
|
-
* ```ts
|
|
7
|
-
* export const canWriteNotes = gate.policy("notes:write", ({ auth }) =>
|
|
8
|
-
* auth.scopes.has("notes:write"),
|
|
9
|
-
* );
|
|
10
|
-
* ```
|
|
11
|
-
*/
|
|
12
|
-
export const notesWrite = gate.policy("notes:write", () => true);
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core wiring — stores, gates, vault, channels (and AI when configured).
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { db, files } 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,8 +0,0 @@
|
|
|
1
|
-
import { store } from "okengine";
|
|
2
|
-
import * as schema from "../db/schema.decl";
|
|
3
|
-
|
|
4
|
-
/** App SQL store — tables from {@link schema}. */
|
|
5
|
-
export const db = store.sql("app", { schema });
|
|
6
|
-
|
|
7
|
-
/** Object storage for note attachments (fs locally · s3 in docker). */
|
|
8
|
-
export const files = store.files("uploads");
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { vault } from "okengine";
|
|
2
|
-
|
|
3
|
-
/** Shared webhook HMAC secret — prove Vault wiring on note create. */
|
|
4
|
-
export const webhookSecret = vault.secret("APP_WEBHOOK_SECRET", {
|
|
5
|
-
description: "HMAC secret for outbound note webhooks",
|
|
6
|
-
dev: "dev-webhook-secret-change-me",
|
|
7
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { defineLocale } from "okengine";
|
|
2
|
-
import type { MessagesFor } from "okengine";
|
|
3
|
-
import type { en } from "./en";
|
|
4
|
-
|
|
5
|
-
const ar = {
|
|
6
|
-
errors: {
|
|
7
|
-
notFound: "غير موجود",
|
|
8
|
-
unauthorized: "غير مصرح",
|
|
9
|
-
},
|
|
10
|
-
notes: {
|
|
11
|
-
created: "تم إنشاء الملاحظة «{title}».",
|
|
12
|
-
archived: "تم أرشفة الملاحظة.",
|
|
13
|
-
empty: "لا توجد ملاحظات نشطة بعد.",
|
|
14
|
-
count: "{count, plural, zero {لا ملاحظات} one {ملاحظة واحدة} two {ملاحظتان} few {# ملاحظات} many {# ملاحظة} other {# ملاحظة}}",
|
|
15
|
-
},
|
|
16
|
-
} satisfies MessagesFor<typeof en>;
|
|
17
|
-
|
|
18
|
-
defineLocale("ar", ar);
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { channel } from "okengine";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
|
|
4
|
-
const mail = channel.email({ from: "Notes <notes@localhost>" });
|
|
5
|
-
|
|
6
|
-
/** Email when a note is created (console driver locally · SMTP in docker). */
|
|
7
|
-
export const noteCreatedMail = mail.template("note-created", {
|
|
8
|
-
locales: ["en", "ar"],
|
|
9
|
-
schema: z.object({
|
|
10
|
-
id: z.string(),
|
|
11
|
-
title: z.string(),
|
|
12
|
-
}),
|
|
13
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { gate } from "okengine";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Starter keeps HTTP public. Replace with real policies when you add auth:
|
|
5
|
-
*
|
|
6
|
-
* ```ts
|
|
7
|
-
* export const canWriteNotes = gate.policy("notes:write", ({ auth }) =>
|
|
8
|
-
* auth.scopes.has("notes:write"),
|
|
9
|
-
* );
|
|
10
|
-
* ```
|
|
11
|
-
*/
|
|
12
|
-
export const notesWrite = gate.policy("notes:write", () => true);
|
|
@@ -1,12 +0,0 @@
|
|
|
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,7 +0,0 @@
|
|
|
1
|
-
import { vault } from "okengine";
|
|
2
|
-
|
|
3
|
-
/** Shared webhook HMAC secret — prove Vault wiring on note create. */
|
|
4
|
-
export const webhookSecret = vault.secret("APP_WEBHOOK_SECRET", {
|
|
5
|
-
description: "HMAC secret for outbound note webhooks",
|
|
6
|
-
dev: "dev-webhook-secret-change-me",
|
|
7
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { defineLocale } from "okengine";
|
|
2
|
-
import type { MessagesFor } from "okengine";
|
|
3
|
-
import type { en } from "./en";
|
|
4
|
-
|
|
5
|
-
const ar = {
|
|
6
|
-
errors: {
|
|
7
|
-
notFound: "غير موجود",
|
|
8
|
-
unauthorized: "غير مصرح",
|
|
9
|
-
},
|
|
10
|
-
notes: {
|
|
11
|
-
created: "تم إنشاء الملاحظة «{title}».",
|
|
12
|
-
archived: "تم أرشفة الملاحظة.",
|
|
13
|
-
empty: "لا توجد ملاحظات نشطة بعد.",
|
|
14
|
-
count: "{count, plural, zero {لا ملاحظات} one {ملاحظة واحدة} two {ملاحظتان} few {# ملاحظات} many {# ملاحظة} other {# ملاحظة}}",
|
|
15
|
-
},
|
|
16
|
-
} satisfies MessagesFor<typeof en>;
|
|
17
|
-
|
|
18
|
-
defineLocale("ar", ar);
|