create-oke 0.10.1 → 0.10.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.
- package/package.json +1 -1
- package/src/agents-md.ts +1 -1
- package/src/cli.test.ts +8 -6
- package/templates/advanced/oke.config.ts +6 -8
- package/templates/advanced/src/app.ts +8 -11
- package/templates/advanced/src/flows/generated.ts +4 -0
- package/templates/advanced/src/flows/main/index.ts +2 -6
- package/templates/advanced/src/flows/notes/index.ts +8 -24
- package/templates/standard/oke.config.ts +0 -1
- package/templates/standard/src/app.ts +8 -11
- package/templates/standard/src/flows/generated.ts +4 -0
- package/templates/standard/src/flows/main/index.ts +2 -6
- package/templates/standard/src/flows/notes/index.ts +5 -15
package/package.json
CHANGED
package/src/agents-md.ts
CHANGED
|
@@ -89,7 +89,7 @@ App \`:6530\` · Console \`:6533\` · MCP \`:6535\`.
|
|
|
89
89
|
- ❌ Inventing a ninth “element” or a parallel handler stack beside Flows
|
|
90
90
|
- ✅ New capability = new **driver** on an existing element, or a new Flow
|
|
91
91
|
- ❌ Untyped HTTP handlers that skip \`on\` / \`flow\` / contracts
|
|
92
|
-
- ✅ \`on(http.get("/…"), flow({ in, out, do }))\`
|
|
92
|
+
- ✅ \`on(http.get("/…"), flow(name, { in, out, do }))\`
|
|
93
93
|
|
|
94
94
|
## Learn more
|
|
95
95
|
|
package/src/cli.test.ts
CHANGED
|
@@ -261,8 +261,8 @@ describe("interactive branches", () => {
|
|
|
261
261
|
expect(code).toBe(0);
|
|
262
262
|
expect(readFileSync(join(dir, ".oke", "mode"), "utf8").trim()).toBe("docker");
|
|
263
263
|
const notes = readFileSync(join(dir, "src", "flows", "notes", "index.ts"), "utf8");
|
|
264
|
-
expect(notes).toContain('
|
|
265
|
-
expect(notes).toContain('
|
|
264
|
+
expect(notes).toContain('flow("notes.digest"');
|
|
265
|
+
expect(notes).toContain('flow("notes.attach"');
|
|
266
266
|
} finally {
|
|
267
267
|
rmSync(dir, { recursive: true, force: true });
|
|
268
268
|
}
|
|
@@ -564,8 +564,10 @@ describe("scaffold structure", () => {
|
|
|
564
564
|
const appTs = readFileSync(join(result.targetDir, "src/app.ts"), "utf8");
|
|
565
565
|
expect(appTs).not.toMatch(/Object\.assign/);
|
|
566
566
|
expect(appTs).not.toMatch(/env:\s*["']test["']/);
|
|
567
|
-
|
|
568
|
-
|
|
567
|
+
// stores/secrets/signals/channel.templates auto-register — the
|
|
568
|
+
// minimal `oke({ name: "notes" })` shape carries no explicit arrays.
|
|
569
|
+
expect(appTs).not.toMatch(/stores:\s*\[/);
|
|
570
|
+
expect(appTs).toMatch(/oke\(\{\s*name:\s*["']notes["']\s*\}\)/);
|
|
569
571
|
const pkg = JSON.parse(readFileSync(join(result.targetDir, "package.json"), "utf8")) as {
|
|
570
572
|
name: string;
|
|
571
573
|
dependencies: { okengine: string };
|
|
@@ -610,8 +612,8 @@ describe("scaffold structure", () => {
|
|
|
610
612
|
expect(result.files).toContain(path);
|
|
611
613
|
}
|
|
612
614
|
const notes = readFileSync(join(result.targetDir, "src/flows/notes/index.ts"), "utf8");
|
|
613
|
-
expect(notes).toContain('
|
|
614
|
-
expect(notes).not.toContain('
|
|
615
|
+
expect(notes).toContain('flow("notes.create"');
|
|
616
|
+
expect(notes).not.toContain('flow("notes.digest"');
|
|
615
617
|
const all = result.files
|
|
616
618
|
.filter((f) => f.endsWith(".ts") || f.endsWith(".md"))
|
|
617
619
|
.map((f) => readFileSync(join(result.targetDir, f), "utf8"))
|
|
@@ -29,13 +29,12 @@ export default defineConfig({
|
|
|
29
29
|
test: "memory",
|
|
30
30
|
prod: "s3",
|
|
31
31
|
},
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// },
|
|
32
|
+
index: {
|
|
33
|
+
local: "memory",
|
|
34
|
+
docker: "meilisearch",
|
|
35
|
+
test: "memory",
|
|
36
|
+
prod: "meilisearch",
|
|
37
|
+
},
|
|
39
38
|
},
|
|
40
39
|
signal: {
|
|
41
40
|
local: "memory",
|
|
@@ -49,7 +48,6 @@ export default defineConfig({
|
|
|
49
48
|
test: "frozen",
|
|
50
49
|
prod: "postgres",
|
|
51
50
|
},
|
|
52
|
-
// Durable-flow journal (durable: true runs) — shared + leased in docker/prod.
|
|
53
51
|
journal: {
|
|
54
52
|
local: "memory",
|
|
55
53
|
docker: "postgres",
|
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "./core";
|
|
2
2
|
import "./locales/en";
|
|
3
3
|
import "./locales/ar";
|
|
4
4
|
|
|
5
5
|
import { oke } from "okengine";
|
|
6
|
-
import * as
|
|
7
|
-
import * as notes from "./flows/notes";
|
|
8
|
-
import { noteCreated } from "./flows/notes/signals";
|
|
6
|
+
import * as routes from "./flows/generated";
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}).adopt({ main, notes });
|
|
8
|
+
// stores (store.sql/store.files) · secrets (vault.secret) · signals (signal())
|
|
9
|
+
// · channel templates (channel.<medium>().template()) auto-register from
|
|
10
|
+
// `./core` (and `./flows/notes/signals`) — no explicit arrays needed.
|
|
11
|
+
// `./flows/generated` is a real file, regenerated by `oke dev` / `oke build`
|
|
12
|
+
// from every `src/flows/<unit>/index.ts` folder — never edit it by hand.
|
|
13
|
+
export const app = oke({ name: "notes" }).adopt(routes);
|
|
17
14
|
|
|
18
15
|
export type App = typeof app;
|
|
@@ -4,9 +4,7 @@ import { z } from "zod";
|
|
|
4
4
|
/** First-run welcome — visit :6530/ after `oke dev`. */
|
|
5
5
|
export const root = on(
|
|
6
6
|
http.get("/").gate(gate.public),
|
|
7
|
-
flow({
|
|
8
|
-
name: "main.root",
|
|
9
|
-
unit: "main",
|
|
7
|
+
flow("main.root", {
|
|
10
8
|
out: z.object({
|
|
11
9
|
ok: z.literal(true),
|
|
12
10
|
app: z.string(),
|
|
@@ -25,9 +23,7 @@ export const root = on(
|
|
|
25
23
|
/** Liveness for probes and `bun test`. */
|
|
26
24
|
export const health = on(
|
|
27
25
|
http.get("/health").gate(gate.public),
|
|
28
|
-
flow({
|
|
29
|
-
name: "main.health",
|
|
30
|
-
unit: "main",
|
|
26
|
+
flow("main.health", {
|
|
31
27
|
out: z.object({ ok: z.literal(true) }),
|
|
32
28
|
do: () => ({ ok: true as const }),
|
|
33
29
|
}),
|
|
@@ -23,9 +23,7 @@ import "./signals";
|
|
|
23
23
|
/** List active (non-archived) notes, newest first. */
|
|
24
24
|
export const list = on(
|
|
25
25
|
http.get("/notes").gate(gate.public),
|
|
26
|
-
flow({
|
|
27
|
-
name: "notes.list",
|
|
28
|
-
unit: "notes",
|
|
26
|
+
flow("notes.list", {
|
|
29
27
|
out: NoteListOut,
|
|
30
28
|
effects: { reads: ["sql:app"] },
|
|
31
29
|
do: async (_input, fx) => {
|
|
@@ -47,9 +45,7 @@ export const list = on(
|
|
|
47
45
|
/** Create a note, emit `note-created`, touch vault. */
|
|
48
46
|
export const create = on(
|
|
49
47
|
http.post("/notes").gate(gate.public),
|
|
50
|
-
flow({
|
|
51
|
-
name: "notes.create",
|
|
52
|
-
unit: "notes",
|
|
48
|
+
flow("notes.create", {
|
|
53
49
|
in: NoteCreateIn,
|
|
54
50
|
out: NoteOut,
|
|
55
51
|
effects: {
|
|
@@ -84,9 +80,7 @@ export const create = on(
|
|
|
84
80
|
/** Fetch one note by id. */
|
|
85
81
|
export const get = on(
|
|
86
82
|
http.get("/notes/:id").gate(gate.public),
|
|
87
|
-
flow({
|
|
88
|
-
name: "notes.get",
|
|
89
|
-
unit: "notes",
|
|
83
|
+
flow("notes.get", {
|
|
90
84
|
in: NoteIdIn,
|
|
91
85
|
out: NoteOut,
|
|
92
86
|
errors: { NotFound },
|
|
@@ -108,9 +102,7 @@ export const get = on(
|
|
|
108
102
|
/** Soft-archive a note. */
|
|
109
103
|
export const archive = on(
|
|
110
104
|
http.post("/notes/:id/archive").gate(gate.public),
|
|
111
|
-
flow({
|
|
112
|
-
name: "notes.archive",
|
|
113
|
-
unit: "notes",
|
|
105
|
+
flow("notes.archive", {
|
|
114
106
|
in: NoteIdIn,
|
|
115
107
|
out: NoteOut,
|
|
116
108
|
errors: { NotFound },
|
|
@@ -138,9 +130,7 @@ export const archive = on(
|
|
|
138
130
|
/** On create → send the note-created email template. */
|
|
139
131
|
export const onCreated = on(
|
|
140
132
|
noteCreated,
|
|
141
|
-
flow({
|
|
142
|
-
name: "notes.onCreated",
|
|
143
|
-
unit: "notes",
|
|
133
|
+
flow("notes.onCreated", {
|
|
144
134
|
effects: { sends: ["note-created"] },
|
|
145
135
|
do: async (payload, fx) => {
|
|
146
136
|
await fx.send(noteCreatedMail, {
|
|
@@ -154,9 +144,7 @@ export const onCreated = on(
|
|
|
154
144
|
/** Store a text attachment next to a note (`files:uploads`). */
|
|
155
145
|
export const attach = on(
|
|
156
146
|
http.post("/notes/:id/attach").gate(gate.public),
|
|
157
|
-
flow({
|
|
158
|
-
name: "notes.attach",
|
|
159
|
-
unit: "notes",
|
|
147
|
+
flow("notes.attach", {
|
|
160
148
|
in: NoteAttachIn,
|
|
161
149
|
out: NoteAttachOut,
|
|
162
150
|
errors: { NotFound },
|
|
@@ -174,9 +162,7 @@ export const attach = on(
|
|
|
174
162
|
/** Daily count of active notes (frozen under test drivers). */
|
|
175
163
|
export const digest = on(
|
|
176
164
|
every("1d"),
|
|
177
|
-
flow({
|
|
178
|
-
name: "notes.digest",
|
|
179
|
-
unit: "notes",
|
|
165
|
+
flow("notes.digest", {
|
|
180
166
|
out: NoteDigestOut,
|
|
181
167
|
effects: { reads: ["sql:app"] },
|
|
182
168
|
do: async (_input, fx) => {
|
|
@@ -192,9 +178,7 @@ export const digest = on(
|
|
|
192
178
|
*/
|
|
193
179
|
export const summarize = on(
|
|
194
180
|
http.post("/notes/:id/summarize").gate(gate.public),
|
|
195
|
-
flow({
|
|
196
|
-
name: "notes.summarize",
|
|
197
|
-
unit: "notes",
|
|
181
|
+
flow("notes.summarize", {
|
|
198
182
|
in: NoteSummarizeIn,
|
|
199
183
|
out: NoteSummarizeOut,
|
|
200
184
|
errors: { NotFound },
|
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "./core";
|
|
2
2
|
import "./locales/en";
|
|
3
3
|
import "./locales/ar";
|
|
4
4
|
|
|
5
5
|
import { oke } from "okengine";
|
|
6
|
-
import * as
|
|
7
|
-
import * as notes from "./flows/notes";
|
|
8
|
-
import { noteCreated } from "./flows/notes/signals";
|
|
6
|
+
import * as routes from "./flows/generated";
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}).adopt({ main, notes });
|
|
8
|
+
// stores (store.sql) · secrets (vault.secret) · signals (signal()) · channel
|
|
9
|
+
// templates (channel.<medium>().template()) auto-register from `./core`
|
|
10
|
+
// (and `./flows/notes/signals`) — no explicit arrays needed.
|
|
11
|
+
// `./flows/generated` is a real file, regenerated by `oke dev` / `oke build`
|
|
12
|
+
// from every `src/flows/<unit>/index.ts` folder — never edit it by hand.
|
|
13
|
+
export const app = oke({ name: "notes" }).adopt(routes);
|
|
17
14
|
|
|
18
15
|
export type App = typeof app;
|
|
@@ -4,9 +4,7 @@ import { z } from "zod";
|
|
|
4
4
|
/** First-run welcome — visit :6530/ after `oke dev`. */
|
|
5
5
|
export const root = on(
|
|
6
6
|
http.get("/").gate(gate.public),
|
|
7
|
-
flow({
|
|
8
|
-
name: "main.root",
|
|
9
|
-
unit: "main",
|
|
7
|
+
flow("main.root", {
|
|
10
8
|
out: z.object({
|
|
11
9
|
ok: z.literal(true),
|
|
12
10
|
app: z.string(),
|
|
@@ -25,9 +23,7 @@ export const root = on(
|
|
|
25
23
|
/** Liveness for probes and `bun test`. */
|
|
26
24
|
export const health = on(
|
|
27
25
|
http.get("/health").gate(gate.public),
|
|
28
|
-
flow({
|
|
29
|
-
name: "main.health",
|
|
30
|
-
unit: "main",
|
|
26
|
+
flow("main.health", {
|
|
31
27
|
out: z.object({ ok: z.literal(true) }),
|
|
32
28
|
do: () => ({ ok: true as const }),
|
|
33
29
|
}),
|
|
@@ -12,9 +12,7 @@ import "./signals";
|
|
|
12
12
|
/** List active (non-archived) notes, newest first. */
|
|
13
13
|
export const list = on(
|
|
14
14
|
http.get("/notes").gate(gate.public),
|
|
15
|
-
flow({
|
|
16
|
-
name: "notes.list",
|
|
17
|
-
unit: "notes",
|
|
15
|
+
flow("notes.list", {
|
|
18
16
|
out: NoteListOut,
|
|
19
17
|
effects: { reads: ["sql:app"] },
|
|
20
18
|
do: async (_input, fx) => {
|
|
@@ -36,9 +34,7 @@ export const list = on(
|
|
|
36
34
|
/** Create a note, emit `note-created`, touch vault. */
|
|
37
35
|
export const create = on(
|
|
38
36
|
http.post("/notes").gate(gate.public),
|
|
39
|
-
flow({
|
|
40
|
-
name: "notes.create",
|
|
41
|
-
unit: "notes",
|
|
37
|
+
flow("notes.create", {
|
|
42
38
|
in: NoteCreateIn,
|
|
43
39
|
out: NoteOut,
|
|
44
40
|
effects: {
|
|
@@ -73,9 +69,7 @@ export const create = on(
|
|
|
73
69
|
/** Fetch one note by id. */
|
|
74
70
|
export const get = on(
|
|
75
71
|
http.get("/notes/:id").gate(gate.public),
|
|
76
|
-
flow({
|
|
77
|
-
name: "notes.get",
|
|
78
|
-
unit: "notes",
|
|
72
|
+
flow("notes.get", {
|
|
79
73
|
in: NoteIdIn,
|
|
80
74
|
out: NoteOut,
|
|
81
75
|
errors: { NotFound },
|
|
@@ -97,9 +91,7 @@ export const get = on(
|
|
|
97
91
|
/** Soft-archive a note. */
|
|
98
92
|
export const archive = on(
|
|
99
93
|
http.post("/notes/:id/archive").gate(gate.public),
|
|
100
|
-
flow({
|
|
101
|
-
name: "notes.archive",
|
|
102
|
-
unit: "notes",
|
|
94
|
+
flow("notes.archive", {
|
|
103
95
|
in: NoteIdIn,
|
|
104
96
|
out: NoteOut,
|
|
105
97
|
errors: { NotFound },
|
|
@@ -127,9 +119,7 @@ export const archive = on(
|
|
|
127
119
|
/** On create → send the note-created email template. */
|
|
128
120
|
export const onCreated = on(
|
|
129
121
|
noteCreated,
|
|
130
|
-
flow({
|
|
131
|
-
name: "notes.onCreated",
|
|
132
|
-
unit: "notes",
|
|
122
|
+
flow("notes.onCreated", {
|
|
133
123
|
effects: { sends: ["note-created"] },
|
|
134
124
|
do: async (payload, fx) => {
|
|
135
125
|
await fx.send(noteCreatedMail, {
|