create-oke 0.2.6 → 0.3.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/README.md +26 -35
- package/package.json +10 -12
- package/src/cli.test.ts +124 -144
- package/src/cli.ts +62 -160
- package/src/docs-origin.ts +2 -3
- package/src/index.ts +1 -2
- package/src/scaffold.ts +52 -32
- package/src/templates.ts +12 -91
- package/src/transform.ts +60 -10
- package/template/.env.example +56 -0
- package/template/README.md +77 -0
- package/template/bun.lock +845 -0
- package/template/drizzle.config.ts +25 -0
- package/{templates/standard → template}/oke.config.ts +22 -25
- package/{templates/standard → template}/package.json +3 -0
- package/{templates/minimal → template}/src/core.ts +1 -1
- package/template/src/schema.decl.ts +12 -0
- package/examples/linkly/oke.config.ts +0 -10
- package/examples/linkly/package.json +0 -15
- package/examples/linkly/src/app.ts +0 -21
- package/examples/linkly/src/core.ts +0 -4
- package/examples/linkly/src/flows/analytics/index.ts +0 -24
- package/examples/linkly/src/flows/links/index.ts +0 -59
- package/examples/linkly/src/flows/links/shapes.ts +0 -22
- package/examples/linkly/src/flows/links/signals.ts +0 -13
- package/examples/linkly/src/gates.ts +0 -8
- package/examples/linkly/src/schema.ts +0 -17
- package/examples/linkly/tests/linkly.test.ts +0 -19
- package/examples/notes/oke.config.ts +0 -14
- package/examples/notes/package.json +0 -16
- package/examples/notes/src/app.ts +0 -13
- package/examples/notes/src/core.ts +0 -4
- package/examples/notes/src/flows/notes/index.ts +0 -44
- package/examples/notes/src/schema.ts +0 -9
- package/examples/notes/tests/notes.test.ts +0 -10
- package/examples/provisions/oke.config.ts +0 -17
- package/examples/provisions/package.json +0 -15
- package/examples/provisions/src/app.ts +0 -60
- package/examples/provisions/src/channels.ts +0 -14
- package/examples/provisions/src/core.ts +0 -4
- package/examples/provisions/src/flows/notifications/index.ts +0 -19
- package/examples/provisions/src/flows/orders/index.ts +0 -70
- package/examples/provisions/src/flows/orders/shapes.ts +0 -21
- package/examples/provisions/src/flows/orders/signals.ts +0 -19
- package/examples/provisions/src/flows/payments/index.ts +0 -37
- package/examples/provisions/src/flows/payments/shapes.ts +0 -3
- package/examples/provisions/src/flows/payments/stripe.ts +0 -18
- package/examples/provisions/src/gates.ts +0 -4
- package/examples/provisions/src/locales/ar.ts +0 -1
- package/examples/provisions/src/locales/en.ts +0 -1
- package/examples/provisions/src/plugins/audit-schema.ts +0 -2
- package/examples/provisions/src/plugins/audit.ts +0 -14
- package/examples/provisions/src/schema.ts +0 -16
- package/examples/provisions/src/vault.ts +0 -16
- package/examples/provisions/tests/orders.test.ts +0 -18
- package/examples/skyport/evals/triage.jsonl +0 -1
- package/examples/skyport/oke.config.ts +0 -44
- package/examples/skyport/oke.images.lock +0 -4
- package/examples/skyport/package.json +0 -16
- package/examples/skyport/src/ai.ts +0 -25
- package/examples/skyport/src/app.ts +0 -57
- package/examples/skyport/src/channels.ts +0 -12
- package/examples/skyport/src/core.ts +0 -4
- package/examples/skyport/src/flows/bookings/index.ts +0 -50
- package/examples/skyport/src/flows/bookings/shapes.ts +0 -6
- package/examples/skyport/src/flows/bookings/signals.ts +0 -9
- package/examples/skyport/src/flows/notifications/index.ts +0 -16
- package/examples/skyport/src/flows/payments/index.ts +0 -14
- package/examples/skyport/src/flows/payments/shapes.ts +0 -5
- package/examples/skyport/src/flows/support/index.ts +0 -38
- package/examples/skyport/src/flows/users/elements.ts +0 -2
- package/examples/skyport/src/flows/users/index.ts +0 -12
- package/examples/skyport/src/flows/users/shapes.ts +0 -6
- package/examples/skyport/src/gates.ts +0 -10
- package/examples/skyport/src/journeys.ts +0 -9
- package/examples/skyport/src/locales/ar.ts +0 -4
- package/examples/skyport/src/locales/en.ts +0 -4
- package/examples/skyport/src/plugins/audit-schema.ts +0 -7
- package/examples/skyport/src/plugins/audit.ts +0 -14
- package/examples/skyport/src/plugins/panel.tsx +0 -4
- package/examples/skyport/src/schema.ts +0 -22
- package/examples/skyport/src/vault.ts +0 -24
- package/examples/skyport/tests/support.test.ts +0 -16
- package/src/sync-templates.ts +0 -86
- package/templates/full/README.md +0 -49
- package/templates/full/oke.config.ts +0 -68
- package/templates/full/package.json +0 -15
- package/templates/full/src/ai.ts +0 -11
- package/templates/full/src/app.ts +0 -34
- package/templates/full/src/channels.ts +0 -9
- package/templates/full/src/core.ts +0 -4
- package/templates/full/src/flows/main/index.ts +0 -45
- package/templates/full/src/flows/main/shapes.ts +0 -3
- package/templates/full/src/flows/main/signals.ts +0 -8
- package/templates/full/src/gates.ts +0 -4
- package/templates/full/src/locales/ar.ts +0 -3
- package/templates/full/src/locales/en.ts +0 -3
- package/templates/full/src/schema.ts +0 -8
- package/templates/full/src/vault.ts +0 -9
- package/templates/full/tests/full.test.ts +0 -11
- package/templates/hello/README.md +0 -38
- package/templates/hello/oke.config.ts +0 -3
- package/templates/hello/package.json +0 -14
- package/templates/hello/src/app.ts +0 -10
- package/templates/hello/src/flows/hello/index.ts +0 -27
- package/templates/hello/tests/hello.test.ts +0 -10
- package/templates/minimal/README.md +0 -41
- package/templates/minimal/oke.config.ts +0 -14
- package/templates/minimal/package.json +0 -15
- package/templates/minimal/src/app.ts +0 -13
- package/templates/minimal/src/flows/main/index.ts +0 -49
- package/templates/minimal/src/schema.ts +0 -8
- package/templates/minimal/tests/minimal.test.ts +0 -13
- package/templates/standard/README.md +0 -47
- package/templates/standard/src/core.ts +0 -4
- package/templates/standard/src/schema.ts +0 -9
- /package/{templates/standard → template}/src/app.ts +0 -0
- /package/{templates/standard → template}/src/channels.ts +0 -0
- /package/{templates/standard → template}/src/flows/main/index.ts +0 -0
- /package/{templates/standard → template}/src/flows/main/shapes.ts +0 -0
- /package/{templates/standard → template}/src/flows/main/signals.ts +0 -0
- /package/{templates/standard → template}/src/gates.ts +0 -0
- /package/{templates/standard → template}/src/locales/ar.ts +0 -0
- /package/{templates/standard → template}/src/locales/en.ts +0 -0
- /package/{templates/standard → template}/src/vault.ts +0 -0
- /package/{templates/standard → template}/tests/standard.test.ts +0 -0
package/README.md
CHANGED
|
@@ -8,52 +8,43 @@ cd my-app
|
|
|
8
8
|
oke dev
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
On a TTY, create-oke opens a Clack wizard
|
|
12
|
-
|
|
13
|
-
`--from-example` for non-interactive (CI / agents).
|
|
11
|
+
On a TTY, create-oke opens a Clack wizard to confirm the name and optionally
|
|
12
|
+
install dependencies and start `oke dev`. Pass `--yes` for non-interactive use.
|
|
14
13
|
|
|
15
|
-
| Port
|
|
16
|
-
|
|
17
|
-
| `:6530` | your app
|
|
18
|
-
| `:6533` | Console
|
|
19
|
-
| `:6535` | MCP |
|
|
14
|
+
| Port | What |
|
|
15
|
+
| ------- | ------------------- |
|
|
16
|
+
| `:6530` | your app |
|
|
17
|
+
| `:6533` | Console |
|
|
18
|
+
| `:6535` | MCP (live Manifest) |
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
The single bundled template is **standard**: the full recommended file layout
|
|
21
|
+
with empty scaffolding.
|
|
23
22
|
|
|
24
23
|
```bash
|
|
25
|
-
bunx create-oke@latest my-app --template
|
|
26
|
-
bunx create-oke@latest my-app --
|
|
27
|
-
bunx create-oke@latest my-app --template full
|
|
24
|
+
bunx create-oke@latest my-app --template standard
|
|
25
|
+
bunx create-oke@latest my-app --sql postgres
|
|
28
26
|
bunx create-oke@latest my-app --yes
|
|
29
|
-
bunx create-oke@latest my-notes --from-example notes
|
|
30
27
|
```
|
|
31
28
|
|
|
32
|
-
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
`--sql sqlite|postgres` is opt-in. The default keeps the dual-mode Store
|
|
30
|
+
config (`local: sqlite` · `docker`/`prod: postgres`) and abstract
|
|
31
|
+
`src/schema.decl.ts` (dialect emitted from the active driver). Pass
|
|
32
|
+
`--sql postgres` to pin `oke.config.ts` `store.sql` local/docker/prod to
|
|
33
|
+
`postgres`. The interactive wizard does not ask and keeps the same dual-mode
|
|
34
|
+
default.
|
|
35
|
+
|
|
36
|
+
Store-bearing scaffolds ship `drizzle.config.ts` and `.env.example`. Local
|
|
37
|
+
`oke dev` auto-runs `oke db push` on schema change; use `oke mode docker` /
|
|
38
|
+
`oke dev --docker` for compose infra.
|
|
38
39
|
|
|
39
40
|
Every scaffold writes an `AGENTS.md` so coding agents know the OKE contract
|
|
40
41
|
(`--no-agents-md` to skip).
|
|
41
42
|
|
|
42
|
-
`--from-example` copies a teaching example (including its business logic and
|
|
43
|
-
comments) — most new projects want `--template` instead.
|
|
44
|
-
|
|
45
|
-
| Example | Adds |
|
|
46
|
-
|---|---|
|
|
47
|
-
| `notes` | `oke` · `flow` · `http` · `store.sql` · typed client |
|
|
48
|
-
| `linkly` | `signal` · `clock` · `gate` |
|
|
49
|
-
| `provisions` | durable flows · `vault` · `channel` · plugins |
|
|
50
|
-
| `skyport` | `ai` · multi-tenancy · distributed topology |
|
|
51
|
-
|
|
52
43
|
## Registries
|
|
53
44
|
|
|
54
|
-
| Registry
|
|
55
|
-
|
|
56
|
-
| npm (CLI) | [`create-oke`](https://www.npmjs.com/package/create-oke) — use this with `bunx`
|
|
57
|
-
| JSR
|
|
45
|
+
| Registry | Package |
|
|
46
|
+
| --------- | ------------------------------------------------------------------------------------------------------- |
|
|
47
|
+
| npm (CLI) | [`create-oke`](https://www.npmjs.com/package/create-oke) — use this with `bunx` |
|
|
48
|
+
| JSR | [`@omqkhafi/create-oke`](https://jsr.io/@omqkhafi/create-oke) — same sources; prefer npm for the binary |
|
|
58
49
|
|
|
59
|
-
Requires **Bun ≥ 1.3**. Docs: [
|
|
50
|
+
Requires **Bun ≥ 1.3**. Docs: [oke.omqkhafi.dev](https://oke.omqkhafi.dev) · [handbook](https://oke.omqkhafi.dev/docs).
|
package/package.json
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-oke",
|
|
3
|
-
"version": "0.2
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Scaffold an okengine app — bunx create-oke@latest <name>",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"sideEffects": false,
|
|
8
6
|
"repository": {
|
|
9
7
|
"type": "git",
|
|
10
8
|
"url": "git+https://github.com/omqkhafi/okengine.git",
|
|
@@ -13,28 +11,28 @@
|
|
|
13
11
|
"bin": {
|
|
14
12
|
"create-oke": "src/index.ts"
|
|
15
13
|
},
|
|
16
|
-
"exports": {
|
|
17
|
-
".": "./src/index.ts"
|
|
18
|
-
},
|
|
19
14
|
"files": [
|
|
20
15
|
"src",
|
|
21
|
-
"
|
|
22
|
-
"examples"
|
|
16
|
+
"template"
|
|
23
17
|
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"exports": {
|
|
21
|
+
".": "./src/index.ts"
|
|
22
|
+
},
|
|
24
23
|
"scripts": {
|
|
25
|
-
"prepack": "bun ./src/sync-templates.ts",
|
|
26
24
|
"typecheck": "tsc --noEmit",
|
|
27
25
|
"test": "bun test",
|
|
28
26
|
"test:integration": "CREATE_OKE_INTEGRATION=1 bun test tests/scaffold.integration.test.ts tests/dev.integration.test.ts"
|
|
29
27
|
},
|
|
30
|
-
"engines": {
|
|
31
|
-
"bun": ">=1.3"
|
|
32
|
-
},
|
|
33
28
|
"dependencies": {
|
|
34
29
|
"@clack/prompts": "^1.7.0"
|
|
35
30
|
},
|
|
36
31
|
"devDependencies": {
|
|
37
32
|
"@types/bun": "latest",
|
|
38
33
|
"typescript": "^7.0.2"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"bun": ">=1.3"
|
|
39
37
|
}
|
|
40
38
|
}
|
package/src/cli.test.ts
CHANGED
|
@@ -3,17 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { describe, expect, test } from "bun:test";
|
|
6
|
-
import {
|
|
7
|
-
existsSync,
|
|
8
|
-
mkdtempSync,
|
|
9
|
-
readFileSync,
|
|
10
|
-
readdirSync,
|
|
11
|
-
rmSync,
|
|
12
|
-
} from "node:fs";
|
|
6
|
+
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync } from "node:fs";
|
|
13
7
|
import { tmpdir } from "node:os";
|
|
14
8
|
import { join } from "node:path";
|
|
15
9
|
import {
|
|
16
|
-
FROM_EXAMPLE_CHOICE,
|
|
17
10
|
formatCdPath,
|
|
18
11
|
nextStepsText,
|
|
19
12
|
parseArgs,
|
|
@@ -25,18 +18,13 @@ import {
|
|
|
25
18
|
type InteractiveAnswers,
|
|
26
19
|
} from "./cli.ts";
|
|
27
20
|
import { listTemplateFiles, scaffold } from "./scaffold.ts";
|
|
28
|
-
import {
|
|
29
|
-
DEFAULT_TEMPLATE,
|
|
30
|
-
EXAMPLES,
|
|
31
|
-
TEMPLATES,
|
|
32
|
-
packageRoot,
|
|
33
|
-
resolveExampleDir,
|
|
34
|
-
resolveTemplateDir,
|
|
35
|
-
} from "./templates.ts";
|
|
21
|
+
import { DEFAULT_TEMPLATE, TEMPLATES, packageRoot, resolveTemplateDir } from "./templates.ts";
|
|
36
22
|
import {
|
|
37
23
|
sanitizeProjectName,
|
|
38
24
|
shouldSkipTemplatePath,
|
|
25
|
+
transformConfigForSqlDriver,
|
|
39
26
|
transformPackageJson,
|
|
27
|
+
transformSchemaForSqlDriver,
|
|
40
28
|
} from "./transform.ts";
|
|
41
29
|
|
|
42
30
|
describe("parseArgs", () => {
|
|
@@ -45,31 +33,27 @@ describe("parseArgs", () => {
|
|
|
45
33
|
expect(a.name).toBe("my-app");
|
|
46
34
|
expect(a.template).toBe("standard");
|
|
47
35
|
expect(a.templateExplicit).toBe(false);
|
|
48
|
-
expect(a.fromExample).toBeUndefined();
|
|
49
36
|
});
|
|
50
37
|
|
|
51
38
|
test("accepts --template and -t", () => {
|
|
52
|
-
expect(parseArgs(["x", "--template", "
|
|
53
|
-
expect(parseArgs(["x", "-t", "
|
|
54
|
-
expect(parseArgs(["x", "--template=
|
|
55
|
-
expect(parseArgs(["x", "--template", "
|
|
39
|
+
expect(parseArgs(["x", "--template", "standard"]).template).toBe("standard");
|
|
40
|
+
expect(parseArgs(["x", "-t", "standard"]).template).toBe("standard");
|
|
41
|
+
expect(parseArgs(["x", "--template=standard"]).template).toBe("standard");
|
|
42
|
+
expect(parseArgs(["x", "--template", "standard"]).templateExplicit).toBe(true);
|
|
56
43
|
});
|
|
57
44
|
|
|
58
|
-
test("accepts --
|
|
59
|
-
expect(parseArgs(["x"
|
|
60
|
-
|
|
61
|
-
);
|
|
62
|
-
expect(parseArgs(["x", "--
|
|
63
|
-
|
|
64
|
-
);
|
|
45
|
+
test("accepts --sql sqlite|postgres", () => {
|
|
46
|
+
expect(parseArgs(["x"]).sqlDriver).toBe("sqlite");
|
|
47
|
+
expect(parseArgs(["x"]).sqlDriverExplicit).toBe(false);
|
|
48
|
+
expect(parseArgs(["x", "--sql", "postgres"]).sqlDriver).toBe("postgres");
|
|
49
|
+
expect(parseArgs(["x", "--sql=sqlite"]).sqlDriver).toBe("sqlite");
|
|
50
|
+
expect(parseArgs(["x", "--sql", "postgres"]).sqlDriverExplicit).toBe(true);
|
|
65
51
|
});
|
|
66
52
|
|
|
67
|
-
test("rejects unknown template
|
|
53
|
+
test("rejects unknown template, option, and sql", () => {
|
|
68
54
|
expect(() => parseArgs(["x", "--template", "nope"])).toThrow(/template/);
|
|
69
|
-
expect(() => parseArgs(["x", "--
|
|
70
|
-
expect(() =>
|
|
71
|
-
parseArgs(["x", "--template", "hello", "--from-example", "notes"]),
|
|
72
|
-
).toThrow(/either/);
|
|
55
|
+
expect(() => parseArgs(["x", "--unknown"])).toThrow(/unknown option/);
|
|
56
|
+
expect(() => parseArgs(["x", "--sql", "mysql"])).toThrow(/sql/);
|
|
73
57
|
});
|
|
74
58
|
});
|
|
75
59
|
|
|
@@ -86,13 +70,8 @@ describe("shouldPrompt", () => {
|
|
|
86
70
|
expect(shouldPrompt(parseArgs([]), false)).toBe(false);
|
|
87
71
|
expect(shouldPrompt(parseArgs(["my-app"]), false)).toBe(false);
|
|
88
72
|
expect(shouldPrompt(parseArgs(["my-app", "--yes"]), true)).toBe(false);
|
|
89
|
-
expect(shouldPrompt(parseArgs(["my-app", "--template", "
|
|
90
|
-
|
|
91
|
-
);
|
|
92
|
-
expect(shouldPrompt(parseArgs(["--template", "hello"]), true)).toBe(false);
|
|
93
|
-
expect(shouldPrompt(parseArgs(["--from-example", "notes"]), true)).toBe(
|
|
94
|
-
false,
|
|
95
|
-
);
|
|
73
|
+
expect(shouldPrompt(parseArgs(["my-app", "--template", "standard"]), true)).toBe(false);
|
|
74
|
+
expect(shouldPrompt(parseArgs(["--template", "standard"]), true)).toBe(false);
|
|
96
75
|
expect(shouldPrompt(parseArgs(["--help"]), true)).toBe(false);
|
|
97
76
|
});
|
|
98
77
|
});
|
|
@@ -108,17 +87,12 @@ describe("parseArgs flags", () => {
|
|
|
108
87
|
});
|
|
109
88
|
|
|
110
89
|
test("rejects --install with --no-install", () => {
|
|
111
|
-
expect(() => parseArgs(["x", "--install", "--no-install"])).toThrow(
|
|
112
|
-
/install/,
|
|
113
|
-
);
|
|
90
|
+
expect(() => parseArgs(["x", "--install", "--no-install"])).toThrow(/install/);
|
|
114
91
|
});
|
|
115
92
|
});
|
|
116
93
|
|
|
117
94
|
describe("sourceFromArgs", () => {
|
|
118
|
-
test("
|
|
119
|
-
expect(sourceFromArgs(parseArgs(["x", "--from-example", "linkly"]))).toEqual(
|
|
120
|
-
{ kind: "example", id: "linkly" },
|
|
121
|
-
);
|
|
95
|
+
test("uses the standard template", () => {
|
|
122
96
|
expect(sourceFromArgs(parseArgs(["x"]))).toEqual({
|
|
123
97
|
kind: "template",
|
|
124
98
|
id: DEFAULT_TEMPLATE,
|
|
@@ -136,34 +110,29 @@ describe("scaffoldArgsFromAnswers ≡ flag-driven", () => {
|
|
|
136
110
|
agentsMd: true,
|
|
137
111
|
};
|
|
138
112
|
const fromAnswers = scaffoldArgsFromAnswers(answers);
|
|
139
|
-
const fromFlags = scaffoldArgsFromCli(
|
|
140
|
-
parseArgs(["x", "--template", id]),
|
|
141
|
-
);
|
|
113
|
+
const fromFlags = scaffoldArgsFromCli(parseArgs(["x", "--template", id]));
|
|
142
114
|
expect(fromAnswers).toEqual(fromFlags);
|
|
143
115
|
expect(fromAnswers.source).toEqual({ kind: "template", id });
|
|
116
|
+
expect(fromAnswers.sqlDriver).toBe("sqlite");
|
|
144
117
|
}
|
|
145
118
|
});
|
|
146
119
|
|
|
147
|
-
test("
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
);
|
|
160
|
-
expect(fromAnswers).toEqual(fromFlags);
|
|
161
|
-
expect(fromAnswers.source).toEqual({ kind: "example", id });
|
|
162
|
-
}
|
|
120
|
+
test("interactive never opts into --sql postgres (flag-only)", () => {
|
|
121
|
+
const answers: InteractiveAnswers = {
|
|
122
|
+
name: "x",
|
|
123
|
+
choice: "standard",
|
|
124
|
+
installAndRun: false,
|
|
125
|
+
agentsMd: true,
|
|
126
|
+
};
|
|
127
|
+
expect(scaffoldArgsFromAnswers(answers).sqlDriver).toBe("sqlite");
|
|
128
|
+
expect(
|
|
129
|
+
scaffoldArgsFromCli(parseArgs(["x", "--template", "standard", "--sql", "postgres"]))
|
|
130
|
+
.sqlDriver,
|
|
131
|
+
).toBe("postgres");
|
|
163
132
|
});
|
|
164
133
|
|
|
165
|
-
test("
|
|
166
|
-
expect(TEMPLATES
|
|
134
|
+
test("only standard is available", () => {
|
|
135
|
+
expect(TEMPLATES).toEqual(["standard"]);
|
|
167
136
|
});
|
|
168
137
|
});
|
|
169
138
|
|
|
@@ -171,7 +140,7 @@ describe("transformPackageJson", () => {
|
|
|
171
140
|
test("rewrites name and okengine file:../.. to installable ref", () => {
|
|
172
141
|
const next = transformPackageJson(
|
|
173
142
|
{
|
|
174
|
-
name: "@oke/template-
|
|
143
|
+
name: "@oke/template-standard",
|
|
175
144
|
private: true,
|
|
176
145
|
dependencies: {
|
|
177
146
|
okengine: "file:../..",
|
|
@@ -189,11 +158,51 @@ describe("transformPackageJson", () => {
|
|
|
189
158
|
});
|
|
190
159
|
});
|
|
191
160
|
|
|
161
|
+
describe("transformSchemaForSqlDriver", () => {
|
|
162
|
+
const sqliteSchema = `import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
|
163
|
+
export const pings = sqliteTable("pings", {});
|
|
164
|
+
`;
|
|
165
|
+
|
|
166
|
+
test("postgres swaps dialect imports and table helper", () => {
|
|
167
|
+
const next = transformSchemaForSqlDriver(sqliteSchema, "postgres");
|
|
168
|
+
expect(next).toContain('from "drizzle-orm/pg-core"');
|
|
169
|
+
expect(next).toContain("pgTable(");
|
|
170
|
+
expect(next).not.toContain("sqliteTable");
|
|
171
|
+
expect(next).not.toContain("sqlite-core");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("sqlite is idempotent on template sources", () => {
|
|
175
|
+
expect(transformSchemaForSqlDriver(sqliteSchema, "sqlite")).toBe(sqliteSchema);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe("transformConfigForSqlDriver", () => {
|
|
180
|
+
const config = `export default defineConfig({
|
|
181
|
+
drivers: {
|
|
182
|
+
store: {
|
|
183
|
+
sql: {
|
|
184
|
+
local: "sqlite",
|
|
185
|
+
docker: "postgres",
|
|
186
|
+
test: "memory",
|
|
187
|
+
prod: "postgres",
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
`;
|
|
193
|
+
|
|
194
|
+
test("postgres pins local/docker/prod and leaves test memory", () => {
|
|
195
|
+
const next = transformConfigForSqlDriver(config, "postgres");
|
|
196
|
+
expect(next).toContain('local: "postgres"');
|
|
197
|
+
expect(next).toContain('docker: "postgres"');
|
|
198
|
+
expect(next).toContain('prod: "postgres"');
|
|
199
|
+
expect(next).toContain('test: "memory"');
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
192
203
|
describe("shouldSkipTemplatePath", () => {
|
|
193
204
|
test("skips node_modules, locks, and monorepo docker test", () => {
|
|
194
|
-
expect(shouldSkipTemplatePath("node_modules/okengine/package.json")).toBe(
|
|
195
|
-
true,
|
|
196
|
-
);
|
|
205
|
+
expect(shouldSkipTemplatePath("node_modules/okengine/package.json")).toBe(true);
|
|
197
206
|
expect(shouldSkipTemplatePath("bun.lock")).toBe(true);
|
|
198
207
|
expect(shouldSkipTemplatePath("tests/docker.test.ts")).toBe(true);
|
|
199
208
|
expect(shouldSkipTemplatePath("tests/support.test.ts")).toBe(false);
|
|
@@ -225,23 +234,23 @@ describe("scaffold structure", () => {
|
|
|
225
234
|
name: `app-${id}`,
|
|
226
235
|
source: { kind: "template", id },
|
|
227
236
|
});
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
);
|
|
237
|
+
const extras = ["AGENTS.md"];
|
|
238
|
+
if (expected.includes(".env.example")) extras.push(".env.local");
|
|
239
|
+
expect([...result.files].sort()).toEqual([...expected, ...extras].sort());
|
|
231
240
|
expect(result.files).toContain(".gitignore");
|
|
232
241
|
expect(result.files).toContain("README.md");
|
|
233
|
-
expect(
|
|
234
|
-
|
|
235
|
-
|
|
242
|
+
expect(result.sqlDriver).toBe("sqlite");
|
|
243
|
+
expect(readFileSync(join(result.targetDir, "AGENTS.md"), "utf8")).toMatch(
|
|
244
|
+
/one law|on\(Trigger\)/i,
|
|
245
|
+
);
|
|
236
246
|
const readme = readFileSync(join(result.targetDir, "README.md"), "utf8");
|
|
237
247
|
expect(readme).toMatch(/oke dev/);
|
|
238
248
|
expect(readme).toMatch(new RegExp(`^# ${id}`, "m"));
|
|
239
|
-
expect(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
) as { name: string; dependencies: { okengine: string } };
|
|
249
|
+
expect(readFileSync(join(result.targetDir, ".gitignore"), "utf8")).toMatch(/node_modules/);
|
|
250
|
+
const pkg = JSON.parse(readFileSync(join(result.targetDir, "package.json"), "utf8")) as {
|
|
251
|
+
name: string;
|
|
252
|
+
dependencies: { okengine: string };
|
|
253
|
+
};
|
|
245
254
|
expect(pkg.name).toBe(`app-${id}`);
|
|
246
255
|
expect(pkg.dependencies.okengine).not.toBe("file:../..");
|
|
247
256
|
} finally {
|
|
@@ -250,37 +259,6 @@ describe("scaffold structure", () => {
|
|
|
250
259
|
}
|
|
251
260
|
});
|
|
252
261
|
|
|
253
|
-
test("hello has exactly one flow and no Store", () => {
|
|
254
|
-
const dir = mkdtempSync(join(tmpdir(), "create-oke-hello-assert-"));
|
|
255
|
-
try {
|
|
256
|
-
const result = scaffold({
|
|
257
|
-
targetDir: join(dir, "hello"),
|
|
258
|
-
name: "hello-app",
|
|
259
|
-
source: { kind: "template", id: "hello" },
|
|
260
|
-
});
|
|
261
|
-
const flowFiles = result.files.filter(
|
|
262
|
-
(f) => f.startsWith("src/flows/") && f.endsWith(".ts"),
|
|
263
|
-
);
|
|
264
|
-
expect(flowFiles).toEqual(["src/flows/hello/index.ts"]);
|
|
265
|
-
const flowSrc = readFileSync(
|
|
266
|
-
join(result.targetDir, "src/flows/hello/index.ts"),
|
|
267
|
-
"utf8",
|
|
268
|
-
);
|
|
269
|
-
expect(flowSrc).toMatch(/export const hello/);
|
|
270
|
-
expect(flowSrc).not.toMatch(/\bstore\b/);
|
|
271
|
-
expect(result.files.some((f) => f.includes("core.ts"))).toBe(false);
|
|
272
|
-
expect(result.files.some((f) => f.includes("schema.ts"))).toBe(false);
|
|
273
|
-
const all = result.files
|
|
274
|
-
.filter((f) => f.endsWith(".ts"))
|
|
275
|
-
.map((f) => readFileSync(join(result.targetDir, f), "utf8"))
|
|
276
|
-
.join("\n");
|
|
277
|
-
expect(all).not.toMatch(/store\.sql/);
|
|
278
|
-
expect(all).not.toMatch(/\bbookings\b|\borders\b|\blinks\b|\bnotes\b/);
|
|
279
|
-
} finally {
|
|
280
|
-
rmSync(dir, { recursive: true, force: true });
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
|
|
284
262
|
test("standard has full four-applications layout and no business domain", () => {
|
|
285
263
|
const dir = mkdtempSync(join(tmpdir(), "create-oke-standard-assert-"));
|
|
286
264
|
try {
|
|
@@ -298,7 +276,7 @@ describe("scaffold structure", () => {
|
|
|
298
276
|
"src/flows/main/shapes.ts",
|
|
299
277
|
"src/flows/main/signals.ts",
|
|
300
278
|
"src/core.ts",
|
|
301
|
-
"src/schema.ts",
|
|
279
|
+
"src/schema.decl.ts",
|
|
302
280
|
"src/app.ts",
|
|
303
281
|
]) {
|
|
304
282
|
expect(result.files).toContain(path);
|
|
@@ -313,20 +291,29 @@ describe("scaffold structure", () => {
|
|
|
313
291
|
}
|
|
314
292
|
});
|
|
315
293
|
|
|
316
|
-
test("--
|
|
317
|
-
const dir = mkdtempSync(join(tmpdir(), "create-oke-
|
|
294
|
+
test("--sql postgres pins store.sql (abstract schema stays dialect-agnostic)", () => {
|
|
295
|
+
const dir = mkdtempSync(join(tmpdir(), "create-oke-sql-pg-"));
|
|
318
296
|
try {
|
|
319
|
-
const exampleDir = resolveExampleDir("notes");
|
|
320
|
-
const expected = listTemplateFiles(exampleDir);
|
|
321
297
|
const result = scaffold({
|
|
322
|
-
targetDir: join(dir, "
|
|
323
|
-
name: "
|
|
324
|
-
source: { kind: "
|
|
298
|
+
targetDir: join(dir, "pg-app"),
|
|
299
|
+
name: "pg-app",
|
|
300
|
+
source: { kind: "template", id: "standard" },
|
|
301
|
+
sqlDriver: "postgres",
|
|
325
302
|
});
|
|
326
|
-
expect(
|
|
327
|
-
|
|
328
|
-
);
|
|
329
|
-
expect(
|
|
303
|
+
expect(result.sqlDriver).toBe("postgres");
|
|
304
|
+
// Abstract decl is dialect-agnostic — emit picks pgTable at sync time.
|
|
305
|
+
const decl = readFileSync(join(result.targetDir, "src/schema.decl.ts"), "utf8");
|
|
306
|
+
expect(decl).toContain("store.schema.table(");
|
|
307
|
+
expect(decl).not.toContain("sqliteTable");
|
|
308
|
+
expect(decl).not.toContain("pgTable");
|
|
309
|
+
const drizzle = readFileSync(join(result.targetDir, "drizzle.config.ts"), "utf8");
|
|
310
|
+
expect(drizzle).toContain("OKE_DRIZZLE_DIALECT");
|
|
311
|
+
expect(drizzle).toContain("schema.generated.ts");
|
|
312
|
+
const config = readFileSync(join(result.targetDir, "oke.config.ts"), "utf8");
|
|
313
|
+
expect(config).toMatch(/sql:\s*\{[\s\S]*local:\s*"postgres"/);
|
|
314
|
+
expect(config).toMatch(/sql:\s*\{[\s\S]*docker:\s*"postgres"/);
|
|
315
|
+
expect(config).toMatch(/sql:\s*\{[\s\S]*prod:\s*"postgres"/);
|
|
316
|
+
expect(config).toMatch(/sql:\s*\{[\s\S]*test:\s*"memory"/);
|
|
330
317
|
} finally {
|
|
331
318
|
rmSync(dir, { recursive: true, force: true });
|
|
332
319
|
}
|
|
@@ -336,9 +323,9 @@ describe("scaffold structure", () => {
|
|
|
336
323
|
const dir = mkdtempSync(join(tmpdir(), "create-oke-no-agents-"));
|
|
337
324
|
try {
|
|
338
325
|
const result = scaffold({
|
|
339
|
-
targetDir: join(dir, "
|
|
326
|
+
targetDir: join(dir, "standard"),
|
|
340
327
|
name: "no-agents",
|
|
341
|
-
source: { kind: "template", id: "
|
|
328
|
+
source: { kind: "template", id: "standard" },
|
|
342
329
|
writeAgentsMd: false,
|
|
343
330
|
});
|
|
344
331
|
expect(result.files).not.toContain("AGENTS.md");
|
|
@@ -376,34 +363,27 @@ describe("non-TTY CLI", () => {
|
|
|
376
363
|
const root = mkdtempSync(join(tmpdir(), "create-oke-flag-"));
|
|
377
364
|
const target = join(root, "flag-app");
|
|
378
365
|
try {
|
|
379
|
-
const code = await run(
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
);
|
|
366
|
+
const code = await run([target, "--template", "standard", "--no-install"], {
|
|
367
|
+
stdinIsTTY: false,
|
|
368
|
+
runPostScaffold: false,
|
|
369
|
+
});
|
|
383
370
|
expect(code).toBe(0);
|
|
384
371
|
expect(readdirSync(target).length).toBeGreaterThan(0);
|
|
385
372
|
expect(existsSync(join(target, "AGENTS.md"))).toBe(true);
|
|
386
373
|
const result = {
|
|
387
374
|
targetDir: target,
|
|
388
375
|
name: "flag-app",
|
|
389
|
-
source: { kind: "template" as const, id: "
|
|
390
|
-
label: "
|
|
376
|
+
source: { kind: "template" as const, id: "standard" as const },
|
|
377
|
+
label: "standard",
|
|
391
378
|
okengineDependency: "x",
|
|
392
379
|
files: [],
|
|
380
|
+
sqlDriver: "sqlite" as const,
|
|
393
381
|
};
|
|
394
382
|
expect(nextStepsText(result)).toContain("oke dev");
|
|
395
383
|
expect(nextStepsText(result)).toContain("bun install");
|
|
396
|
-
expect(nextStepsText(result)).toContain("
|
|
384
|
+
expect(nextStepsText(result)).toContain("oke.omqkhafi.dev");
|
|
397
385
|
} finally {
|
|
398
386
|
rmSync(root, { recursive: true, force: true });
|
|
399
387
|
}
|
|
400
388
|
});
|
|
401
389
|
});
|
|
402
|
-
|
|
403
|
-
describe("examples catalogue", () => {
|
|
404
|
-
test("all four teaching examples resolve", () => {
|
|
405
|
-
for (const id of EXAMPLES) {
|
|
406
|
-
expect(resolveExampleDir(id)).toBeTruthy();
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
});
|