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/src/cli.ts
CHANGED
|
@@ -1,25 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `create-oke` CLI —
|
|
2
|
+
* `create-oke` CLI — one standard starter.
|
|
3
3
|
*
|
|
4
4
|
* ```bash
|
|
5
|
-
* bunx create-oke@latest <name> [--template
|
|
6
|
-
* bunx create-oke@latest <name> --from-example notes|linkly|provisions|skyport
|
|
5
|
+
* bunx create-oke@latest <name> [--template standard]
|
|
7
6
|
* bunx create-oke@latest <name> --yes
|
|
8
7
|
* bunx create-oke@latest # interactive when stdin is a TTY
|
|
9
8
|
* ```
|
|
10
9
|
*/
|
|
11
10
|
|
|
12
|
-
import {
|
|
13
|
-
cancel,
|
|
14
|
-
confirm,
|
|
15
|
-
intro,
|
|
16
|
-
isCancel,
|
|
17
|
-
note,
|
|
18
|
-
outro,
|
|
19
|
-
select,
|
|
20
|
-
spinner,
|
|
21
|
-
text,
|
|
22
|
-
} from "@clack/prompts";
|
|
11
|
+
import { cancel, confirm, intro, isCancel, note, outro, spinner, text } from "@clack/prompts";
|
|
23
12
|
import { basename, relative, resolve } from "node:path";
|
|
24
13
|
import { existsSync, rmSync } from "node:fs";
|
|
25
14
|
import { agentsMdContent } from "./agents-md.ts";
|
|
@@ -27,23 +16,23 @@ import { docsUrl } from "./docs-origin.ts";
|
|
|
27
16
|
import { scaffold, type ScaffoldResult, type ScaffoldSource } from "./scaffold.ts";
|
|
28
17
|
import {
|
|
29
18
|
DEFAULT_TEMPLATE,
|
|
30
|
-
EXAMPLE_NEW_IDEAS,
|
|
31
|
-
EXAMPLES,
|
|
32
19
|
TEMPLATE_PURPOSES,
|
|
33
20
|
TEMPLATES,
|
|
34
|
-
isExampleId,
|
|
35
21
|
isTemplateId,
|
|
36
|
-
type ExampleId,
|
|
37
22
|
type TemplateId,
|
|
38
23
|
} from "./templates.ts";
|
|
24
|
+
import { DEFAULT_SQL_DRIVER, SQL_DRIVERS, isSqlDriverId, type SqlDriverId } from "./transform.ts";
|
|
39
25
|
|
|
40
26
|
/** Parsed CLI arguments. */
|
|
41
27
|
export type CliArgs = {
|
|
42
28
|
readonly name: string | undefined;
|
|
43
29
|
readonly template: TemplateId;
|
|
44
|
-
readonly fromExample: ExampleId | undefined;
|
|
45
30
|
/** True when `--template` / `-t` was present on the argv. */
|
|
46
31
|
readonly templateExplicit: boolean;
|
|
32
|
+
/** Store SQL driver (`sqlite` default). */
|
|
33
|
+
readonly sqlDriver: SqlDriverId;
|
|
34
|
+
/** True when `--sql` was present on the argv. */
|
|
35
|
+
readonly sqlDriverExplicit: boolean;
|
|
47
36
|
readonly help: boolean;
|
|
48
37
|
/** Skip all prompts; use defaults. */
|
|
49
38
|
readonly yes: boolean;
|
|
@@ -57,27 +46,13 @@ export type CliArgs = {
|
|
|
57
46
|
readonly targetDir: string | undefined;
|
|
58
47
|
};
|
|
59
48
|
|
|
60
|
-
/**
|
|
61
|
-
* Sentinel choice for "Start from a worked example" in interactive answers.
|
|
62
|
-
* Distinct from clack's internal select value (`__example__`).
|
|
63
|
-
*/
|
|
64
|
-
export const FROM_EXAMPLE_CHOICE = "from-example" as const;
|
|
65
|
-
|
|
66
49
|
/** Answers collected by the interactive ask step (no clack types). */
|
|
67
|
-
export type InteractiveAnswers =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
| {
|
|
75
|
-
readonly name: string;
|
|
76
|
-
readonly choice: typeof FROM_EXAMPLE_CHOICE;
|
|
77
|
-
readonly example: ExampleId;
|
|
78
|
-
readonly installAndRun: boolean;
|
|
79
|
-
readonly agentsMd: boolean;
|
|
80
|
-
};
|
|
50
|
+
export type InteractiveAnswers = {
|
|
51
|
+
readonly name: string;
|
|
52
|
+
readonly choice: TemplateId;
|
|
53
|
+
readonly installAndRun: boolean;
|
|
54
|
+
readonly agentsMd: boolean;
|
|
55
|
+
};
|
|
81
56
|
|
|
82
57
|
/**
|
|
83
58
|
* Canonical scaffold invocation — shared by interactive and flag-driven paths.
|
|
@@ -87,6 +62,7 @@ export type ScaffoldCallArgs = {
|
|
|
87
62
|
readonly targetDir: string;
|
|
88
63
|
readonly source: ScaffoldSource;
|
|
89
64
|
readonly agentsMd: boolean;
|
|
65
|
+
readonly sqlDriver: SqlDriverId;
|
|
90
66
|
};
|
|
91
67
|
|
|
92
68
|
/**
|
|
@@ -97,8 +73,9 @@ export type ScaffoldCallArgs = {
|
|
|
97
73
|
export function parseArgs(argv: readonly string[]): CliArgs {
|
|
98
74
|
let name: string | undefined;
|
|
99
75
|
let template: TemplateId = DEFAULT_TEMPLATE;
|
|
100
|
-
let fromExample: ExampleId | undefined;
|
|
101
76
|
let templateExplicit = false;
|
|
77
|
+
let sqlDriver: SqlDriverId = DEFAULT_SQL_DRIVER;
|
|
78
|
+
let sqlDriverExplicit = false;
|
|
102
79
|
let help = false;
|
|
103
80
|
let yes = false;
|
|
104
81
|
let install: boolean | undefined;
|
|
@@ -131,12 +108,28 @@ export function parseArgs(argv: readonly string[]): CliArgs {
|
|
|
131
108
|
agentsMd = true;
|
|
132
109
|
continue;
|
|
133
110
|
}
|
|
111
|
+
if (a === "--sql") {
|
|
112
|
+
const next = argv[++i];
|
|
113
|
+
if (!next || !isSqlDriverId(next)) {
|
|
114
|
+
throw new Error(`create-oke: --sql must be one of ${SQL_DRIVERS.join("|")}`);
|
|
115
|
+
}
|
|
116
|
+
sqlDriver = next;
|
|
117
|
+
sqlDriverExplicit = true;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (a.startsWith("--sql=")) {
|
|
121
|
+
const value = a.slice("--sql=".length);
|
|
122
|
+
if (!isSqlDriverId(value)) {
|
|
123
|
+
throw new Error(`create-oke: --sql must be one of ${SQL_DRIVERS.join("|")}`);
|
|
124
|
+
}
|
|
125
|
+
sqlDriver = value;
|
|
126
|
+
sqlDriverExplicit = true;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
134
129
|
if (a === "--template" || a === "-t") {
|
|
135
130
|
const next = argv[++i];
|
|
136
131
|
if (!next || !isTemplateId(next)) {
|
|
137
|
-
throw new Error(
|
|
138
|
-
`create-oke: --template must be one of ${TEMPLATES.join("|")}`,
|
|
139
|
-
);
|
|
132
|
+
throw new Error(`create-oke: --template must be one of ${TEMPLATES.join("|")}`);
|
|
140
133
|
}
|
|
141
134
|
template = next;
|
|
142
135
|
templateExplicit = true;
|
|
@@ -145,34 +138,12 @@ export function parseArgs(argv: readonly string[]): CliArgs {
|
|
|
145
138
|
if (a.startsWith("--template=")) {
|
|
146
139
|
const value = a.slice("--template=".length);
|
|
147
140
|
if (!isTemplateId(value)) {
|
|
148
|
-
throw new Error(
|
|
149
|
-
`create-oke: --template must be one of ${TEMPLATES.join("|")}`,
|
|
150
|
-
);
|
|
141
|
+
throw new Error(`create-oke: --template must be one of ${TEMPLATES.join("|")}`);
|
|
151
142
|
}
|
|
152
143
|
template = value;
|
|
153
144
|
templateExplicit = true;
|
|
154
145
|
continue;
|
|
155
146
|
}
|
|
156
|
-
if (a === "--from-example") {
|
|
157
|
-
const next = argv[++i];
|
|
158
|
-
if (!next || !isExampleId(next)) {
|
|
159
|
-
throw new Error(
|
|
160
|
-
`create-oke: --from-example must be one of ${EXAMPLES.join("|")}`,
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
fromExample = next;
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
if (a.startsWith("--from-example=")) {
|
|
167
|
-
const value = a.slice("--from-example=".length);
|
|
168
|
-
if (!isExampleId(value)) {
|
|
169
|
-
throw new Error(
|
|
170
|
-
`create-oke: --from-example must be one of ${EXAMPLES.join("|")}`,
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
fromExample = value;
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
147
|
if (a.startsWith("-")) {
|
|
177
148
|
throw new Error(`create-oke: unknown option ${a}`);
|
|
178
149
|
}
|
|
@@ -183,12 +154,6 @@ export function parseArgs(argv: readonly string[]): CliArgs {
|
|
|
183
154
|
throw new Error(`create-oke: unexpected argument ${a}`);
|
|
184
155
|
}
|
|
185
156
|
|
|
186
|
-
if (templateExplicit && fromExample !== undefined) {
|
|
187
|
-
throw new Error(
|
|
188
|
-
"create-oke: use either --template or --from-example, not both",
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
157
|
if (argv.includes("--install") && argv.includes("--no-install")) {
|
|
193
158
|
throw new Error("create-oke: use either --install or --no-install, not both");
|
|
194
159
|
}
|
|
@@ -200,8 +165,9 @@ export function parseArgs(argv: readonly string[]): CliArgs {
|
|
|
200
165
|
return {
|
|
201
166
|
name,
|
|
202
167
|
template,
|
|
203
|
-
fromExample,
|
|
204
168
|
templateExplicit,
|
|
169
|
+
sqlDriver,
|
|
170
|
+
sqlDriverExplicit,
|
|
205
171
|
help,
|
|
206
172
|
yes,
|
|
207
173
|
install,
|
|
@@ -248,21 +214,18 @@ export function helpText(): string {
|
|
|
248
214
|
(id) =>
|
|
249
215
|
` ${id.padEnd(12)}${TEMPLATE_PURPOSES[id]}${id === DEFAULT_TEMPLATE ? " (default)" : ""}`,
|
|
250
216
|
).join("\n");
|
|
251
|
-
const exampleLines = EXAMPLES.map(
|
|
252
|
-
(id) => ` ${id.padEnd(12)}${EXAMPLE_NEW_IDEAS[id]}`,
|
|
253
|
-
).join("\n");
|
|
254
|
-
|
|
255
217
|
return `create-oke — scaffold an okengine app
|
|
256
218
|
|
|
257
219
|
Usage:
|
|
258
|
-
bunx create-oke@latest <name> [--template
|
|
259
|
-
bunx create-oke@latest <name> --from-example notes|linkly|provisions|skyport
|
|
220
|
+
bunx create-oke@latest <name> [--template standard]
|
|
260
221
|
bunx create-oke@latest <name> --yes
|
|
261
222
|
bunx create-oke@latest # interactive (TTY only)
|
|
262
223
|
|
|
263
224
|
Options:
|
|
264
225
|
-t, --template <id> Clean starter (default: ${DEFAULT_TEMPLATE})
|
|
265
|
-
--
|
|
226
|
+
--sql <id> Opt-in Store SQL dialect: ${SQL_DRIVERS.join("|")}
|
|
227
|
+
Default keeps local sqlite · docker/prod postgres.
|
|
228
|
+
--sql postgres pins store.sql local/docker/prod to postgres.
|
|
266
229
|
-y, --yes No prompts; defaults + bun install (no oke dev)
|
|
267
230
|
--install Run bun install after scaffold
|
|
268
231
|
--no-install Skip bun install
|
|
@@ -270,16 +233,11 @@ Options:
|
|
|
270
233
|
--no-agents-md Skip AGENTS.md
|
|
271
234
|
-h, --help Show this help
|
|
272
235
|
|
|
273
|
-
|
|
236
|
+
Template:
|
|
274
237
|
${templateLines}
|
|
275
238
|
|
|
276
|
-
--from-example (copies a teaching example, including its business logic and
|
|
277
|
-
comments — most new projects want --template instead):
|
|
278
|
-
${exampleLines}
|
|
279
|
-
|
|
280
239
|
No telemetry. Bun only. On a TTY, a project name alone still opens the wizard
|
|
281
|
-
(confirm name
|
|
282
|
-
--from-example stay fully scriptable.
|
|
240
|
+
(confirm name and install). Non-TTY / --yes / --template stay fully scriptable.
|
|
283
241
|
`;
|
|
284
242
|
}
|
|
285
243
|
|
|
@@ -287,20 +245,16 @@ No telemetry. Bun only. On a TTY, a project name alone still opens the wizard
|
|
|
287
245
|
* Whether the CLI should open the interactive Clack flow.
|
|
288
246
|
*
|
|
289
247
|
* TTY humans get the wizard even when a name is pre-filled. Config flags
|
|
290
|
-
*
|
|
248
|
+
* `--template` or `--yes` skips prompts for CI/agents.
|
|
291
249
|
*
|
|
292
250
|
* @param args - Parsed args
|
|
293
251
|
* @param stdinIsTTY - `process.stdin.isTTY`
|
|
294
252
|
*/
|
|
295
|
-
export function shouldPrompt(
|
|
296
|
-
args: CliArgs,
|
|
297
|
-
stdinIsTTY: boolean | undefined,
|
|
298
|
-
): boolean {
|
|
253
|
+
export function shouldPrompt(args: CliArgs, stdinIsTTY: boolean | undefined): boolean {
|
|
299
254
|
if (!stdinIsTTY) return false;
|
|
300
255
|
if (args.help) return false;
|
|
301
256
|
if (args.yes) return false;
|
|
302
257
|
if (args.templateExplicit) return false;
|
|
303
|
-
if (args.fromExample !== undefined) return false;
|
|
304
258
|
return true;
|
|
305
259
|
}
|
|
306
260
|
|
|
@@ -310,9 +264,6 @@ export function shouldPrompt(
|
|
|
310
264
|
* @param args - Parsed args
|
|
311
265
|
*/
|
|
312
266
|
export function sourceFromArgs(args: CliArgs): ScaffoldSource {
|
|
313
|
-
if (args.fromExample !== undefined) {
|
|
314
|
-
return { kind: "example", id: args.fromExample };
|
|
315
|
-
}
|
|
316
267
|
return { kind: "template", id: args.template };
|
|
317
268
|
}
|
|
318
269
|
|
|
@@ -332,6 +283,7 @@ export function scaffoldArgsFromCli(args: CliArgs): ScaffoldCallArgs {
|
|
|
332
283
|
targetDir: args.targetDir,
|
|
333
284
|
source: sourceFromArgs(args),
|
|
334
285
|
agentsMd: args.agentsMd,
|
|
286
|
+
sqlDriver: args.sqlDriver,
|
|
335
287
|
};
|
|
336
288
|
}
|
|
337
289
|
|
|
@@ -339,28 +291,21 @@ export function scaffoldArgsFromCli(args: CliArgs): ScaffoldCallArgs {
|
|
|
339
291
|
* Pure map from interactive answers → scaffold call args.
|
|
340
292
|
*
|
|
341
293
|
* Independent of clack — unit-tested against {@link scaffoldArgsFromCli}
|
|
342
|
-
* for
|
|
294
|
+
* for the standard template.
|
|
343
295
|
*
|
|
344
296
|
* @param answers - Collected interactive answers
|
|
345
297
|
*/
|
|
346
|
-
export function scaffoldArgsFromAnswers(
|
|
347
|
-
answers: InteractiveAnswers,
|
|
348
|
-
): ScaffoldCallArgs {
|
|
298
|
+
export function scaffoldArgsFromAnswers(answers: InteractiveAnswers): ScaffoldCallArgs {
|
|
349
299
|
const targetDir = resolve(answers.name.trim());
|
|
350
300
|
const name = basename(targetDir);
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
name,
|
|
354
|
-
targetDir,
|
|
355
|
-
source: { kind: "example", id: answers.example },
|
|
356
|
-
agentsMd: answers.agentsMd,
|
|
357
|
-
};
|
|
358
|
-
}
|
|
301
|
+
// Interactive always keeps the dual-mode default (local sqlite ·
|
|
302
|
+
// docker/prod postgres). Opt into postgres-everywhere with `--sql postgres`.
|
|
359
303
|
return {
|
|
360
304
|
name,
|
|
361
305
|
targetDir,
|
|
362
306
|
source: { kind: "template", id: answers.choice },
|
|
363
307
|
agentsMd: answers.agentsMd,
|
|
308
|
+
sqlDriver: DEFAULT_SQL_DRIVER,
|
|
364
309
|
};
|
|
365
310
|
}
|
|
366
311
|
|
|
@@ -371,9 +316,10 @@ export function scaffoldArgsFromAnswers(
|
|
|
371
316
|
*
|
|
372
317
|
* @param partial - Name already known (pre-filled in the prompt)
|
|
373
318
|
*/
|
|
374
|
-
export type AskInteractiveFn = (
|
|
375
|
-
|
|
376
|
-
|
|
319
|
+
export type AskInteractiveFn = (partial: {
|
|
320
|
+
readonly name?: string;
|
|
321
|
+
readonly agentsMd?: boolean;
|
|
322
|
+
}) => Promise<InteractiveAnswers | null>;
|
|
377
323
|
|
|
378
324
|
/**
|
|
379
325
|
* Collect interactive answers via `@clack/prompts`.
|
|
@@ -396,52 +342,8 @@ export async function askInteractiveAnswers(
|
|
|
396
342
|
if (isCancel(nameValue)) return null;
|
|
397
343
|
const name = String(nameValue).trim();
|
|
398
344
|
|
|
399
|
-
const templateValue = await select({
|
|
400
|
-
message: "Template",
|
|
401
|
-
options: [
|
|
402
|
-
...TEMPLATES.map((id) => ({
|
|
403
|
-
value: id as string,
|
|
404
|
-
label: id,
|
|
405
|
-
hint: TEMPLATE_PURPOSES[id],
|
|
406
|
-
})),
|
|
407
|
-
{
|
|
408
|
-
value: "__example__",
|
|
409
|
-
label: "Start from a worked example",
|
|
410
|
-
hint: "Teaching apps with business logic — most projects want a template",
|
|
411
|
-
},
|
|
412
|
-
],
|
|
413
|
-
initialValue: DEFAULT_TEMPLATE,
|
|
414
|
-
});
|
|
415
|
-
if (isCancel(templateValue)) return null;
|
|
416
|
-
|
|
417
345
|
const agentsMd = partial.agentsMd ?? true;
|
|
418
346
|
|
|
419
|
-
if (templateValue === "__example__") {
|
|
420
|
-
const exampleValue = await select({
|
|
421
|
-
message: "Example",
|
|
422
|
-
options: EXAMPLES.map((id) => ({
|
|
423
|
-
value: id,
|
|
424
|
-
label: id,
|
|
425
|
-
hint: EXAMPLE_NEW_IDEAS[id],
|
|
426
|
-
})),
|
|
427
|
-
});
|
|
428
|
-
if (isCancel(exampleValue)) return null;
|
|
429
|
-
|
|
430
|
-
const installAndRunValue = await confirm({
|
|
431
|
-
message: "Install dependencies and start oke dev?",
|
|
432
|
-
initialValue: true,
|
|
433
|
-
});
|
|
434
|
-
if (isCancel(installAndRunValue)) return null;
|
|
435
|
-
|
|
436
|
-
return {
|
|
437
|
-
name,
|
|
438
|
-
choice: FROM_EXAMPLE_CHOICE,
|
|
439
|
-
example: exampleValue as ExampleId,
|
|
440
|
-
installAndRun: Boolean(installAndRunValue),
|
|
441
|
-
agentsMd,
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
|
|
445
347
|
const installAndRunValue = await confirm({
|
|
446
348
|
message: "Install dependencies and start oke dev?",
|
|
447
349
|
initialValue: true,
|
|
@@ -450,7 +352,7 @@ export async function askInteractiveAnswers(
|
|
|
450
352
|
|
|
451
353
|
return {
|
|
452
354
|
name,
|
|
453
|
-
choice:
|
|
355
|
+
choice: DEFAULT_TEMPLATE,
|
|
454
356
|
installAndRun: Boolean(installAndRunValue),
|
|
455
357
|
agentsMd,
|
|
456
358
|
};
|
|
@@ -503,9 +405,7 @@ export async function run(
|
|
|
503
405
|
|
|
504
406
|
if (args.name === undefined) {
|
|
505
407
|
console.log(helpText());
|
|
506
|
-
console.error(
|
|
507
|
-
"create-oke: missing <name>. Pass a name, or run in a TTY for the wizard.",
|
|
508
|
-
);
|
|
408
|
+
console.error("create-oke: missing <name>. Pass a name, or run in a TTY for the wizard.");
|
|
509
409
|
console.error(" Example: bunx create-oke@latest my-app --yes");
|
|
510
410
|
return 1;
|
|
511
411
|
}
|
|
@@ -513,7 +413,7 @@ export async function run(
|
|
|
513
413
|
if (args.yes) {
|
|
514
414
|
const source = sourceFromArgs(args);
|
|
515
415
|
console.log(
|
|
516
|
-
`Using defaults: ${source.kind}=${source.id} agents-md=${args.agentsMd} install=${shouldInstall(args)}`,
|
|
416
|
+
`Using defaults: ${source.kind}=${source.id} sql=${args.sqlDriver} agents-md=${args.agentsMd} install=${shouldInstall(args)}`,
|
|
517
417
|
);
|
|
518
418
|
}
|
|
519
419
|
|
|
@@ -573,6 +473,7 @@ async function runScaffold(
|
|
|
573
473
|
targetDir,
|
|
574
474
|
source,
|
|
575
475
|
agentsMd,
|
|
476
|
+
sqlDriver,
|
|
576
477
|
interactive,
|
|
577
478
|
install,
|
|
578
479
|
startDev,
|
|
@@ -605,6 +506,7 @@ async function runScaffold(
|
|
|
605
506
|
name,
|
|
606
507
|
source,
|
|
607
508
|
writeAgentsMd: agentsMd,
|
|
509
|
+
sqlDriver,
|
|
608
510
|
});
|
|
609
511
|
if (spun) spun.stop("Scaffolded.");
|
|
610
512
|
|
package/src/docs-origin.ts
CHANGED
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
* Canonical public docs origin for scaffolded AGENTS.md and next-steps.
|
|
3
3
|
*
|
|
4
4
|
* Keep in lockstep with `src/docs-origin.ts` in the okengine package.
|
|
5
|
-
* Flip both when the custom domain is attached: `https://oke.omqkhafi.dev`.
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
|
-
/** Live docs site
|
|
9
|
-
export const DOCS_ORIGIN = "https://
|
|
7
|
+
/** Live docs site. */
|
|
8
|
+
export const DOCS_ORIGIN = "https://oke.omqkhafi.dev" as const;
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Absolute docs URL for a path under the handbook / site.
|
package/src/index.ts
CHANGED
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
* ```bash
|
|
6
6
|
* bunx create-oke@latest my-app
|
|
7
7
|
* bunx create-oke@latest my-app --yes
|
|
8
|
-
* bunx create-oke@latest my-app --template
|
|
9
|
-
* bunx create-oke@latest my-app --from-example notes
|
|
8
|
+
* bunx create-oke@latest my-app --template standard
|
|
10
9
|
* ```
|
|
11
10
|
*
|
|
12
11
|
* @module
|
package/src/scaffold.ts
CHANGED
|
@@ -13,26 +13,21 @@ import {
|
|
|
13
13
|
writeFileSync,
|
|
14
14
|
} from "node:fs";
|
|
15
15
|
import { join, relative, resolve } from "node:path";
|
|
16
|
-
import {
|
|
17
|
-
resolveExampleDir,
|
|
18
|
-
resolveLocalOkengineRoot,
|
|
19
|
-
resolveTemplateDir,
|
|
20
|
-
type ExampleId,
|
|
21
|
-
type TemplateId,
|
|
22
|
-
} from "./templates.ts";
|
|
16
|
+
import { resolveLocalOkengineRoot, resolveTemplateDir, type TemplateId } from "./templates.ts";
|
|
23
17
|
import { agentsMdContent } from "./agents-md.ts";
|
|
24
18
|
import {
|
|
19
|
+
DEFAULT_SQL_DRIVER,
|
|
25
20
|
sanitizeProjectName,
|
|
26
21
|
shouldSkipTemplatePath,
|
|
22
|
+
transformConfigForSqlDriver,
|
|
27
23
|
transformPackageJson,
|
|
28
24
|
resolveOkengineDependency,
|
|
29
25
|
type ScaffoldPackageJson,
|
|
26
|
+
type SqlDriverId,
|
|
30
27
|
} from "./transform.ts";
|
|
31
28
|
|
|
32
|
-
/**
|
|
33
|
-
export type ScaffoldSource =
|
|
34
|
-
| { readonly kind: "template"; readonly id: TemplateId }
|
|
35
|
-
| { readonly kind: "example"; readonly id: ExampleId };
|
|
29
|
+
/** The bundled starter copied by the scaffold. */
|
|
30
|
+
export type ScaffoldSource = { readonly kind: "template"; readonly id: TemplateId };
|
|
36
31
|
|
|
37
32
|
/** Options for {@link scaffold}. */
|
|
38
33
|
export type ScaffoldOptions = {
|
|
@@ -40,10 +35,15 @@ export type ScaffoldOptions = {
|
|
|
40
35
|
readonly targetDir: string;
|
|
41
36
|
/** npm package / folder name. */
|
|
42
37
|
readonly name: string;
|
|
43
|
-
/**
|
|
38
|
+
/** Starter template source. */
|
|
44
39
|
readonly source: ScaffoldSource;
|
|
45
40
|
/** Write root `AGENTS.md` (default true). */
|
|
46
41
|
readonly writeAgentsMd?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Store SQL driver — pins `oke.config.ts` `store.sql` local/docker/prod
|
|
44
|
+
* when `postgres`. Default `sqlite` keeps the dual-mode config.
|
|
45
|
+
*/
|
|
46
|
+
readonly sqlDriver?: SqlDriverId;
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
/** Result of a successful scaffold. */
|
|
@@ -54,39 +54,35 @@ export type ScaffoldResult = {
|
|
|
54
54
|
/** Display label (`standard`, `notes`, …). */
|
|
55
55
|
readonly label: string;
|
|
56
56
|
readonly okengineDependency: string;
|
|
57
|
+
/** Store SQL driver applied to schema + config. */
|
|
58
|
+
readonly sqlDriver: SqlDriverId;
|
|
57
59
|
/** Relative paths written (POSIX), sorted. */
|
|
58
60
|
readonly files: readonly string[];
|
|
59
61
|
};
|
|
60
62
|
|
|
61
63
|
/**
|
|
62
|
-
* Scaffold a new okengine project from
|
|
64
|
+
* Scaffold a new okengine project from the standard template.
|
|
63
65
|
*
|
|
64
66
|
* @param options - Name, source, destination
|
|
65
67
|
*/
|
|
66
68
|
export function scaffold(options: ScaffoldOptions): ScaffoldResult {
|
|
67
69
|
const name = sanitizeProjectName(options.name);
|
|
68
70
|
const targetDir = resolve(options.targetDir);
|
|
69
|
-
const sourceDir =
|
|
70
|
-
options.source.kind === "template"
|
|
71
|
-
? resolveTemplateDir(options.source.id)
|
|
72
|
-
: resolveExampleDir(options.source.id);
|
|
71
|
+
const sourceDir = resolveTemplateDir(options.source.id);
|
|
73
72
|
const label = options.source.id;
|
|
73
|
+
const sqlDriver = options.sqlDriver ?? DEFAULT_SQL_DRIVER;
|
|
74
74
|
|
|
75
75
|
if (existsSync(targetDir)) {
|
|
76
76
|
const entries = readdirSync(targetDir);
|
|
77
77
|
if (entries.length > 0) {
|
|
78
|
-
throw new Error(
|
|
79
|
-
`create-oke: target directory is not empty: ${targetDir}`,
|
|
80
|
-
);
|
|
78
|
+
throw new Error(`create-oke: target directory is not empty: ${targetDir}`);
|
|
81
79
|
}
|
|
82
80
|
} else {
|
|
83
81
|
mkdirSync(targetDir, { recursive: true });
|
|
84
82
|
}
|
|
85
83
|
|
|
86
84
|
try {
|
|
87
|
-
const okengineDependency = resolveOkengineDependency(
|
|
88
|
-
resolveLocalOkengineRoot(),
|
|
89
|
-
);
|
|
85
|
+
const okengineDependency = resolveOkengineDependency(resolveLocalOkengineRoot());
|
|
90
86
|
const written: string[] = [];
|
|
91
87
|
|
|
92
88
|
copyTree(sourceDir, targetDir, "", written);
|
|
@@ -95,18 +91,25 @@ export function scaffold(options: ScaffoldOptions): ScaffoldResult {
|
|
|
95
91
|
if (!existsSync(pkgPath)) {
|
|
96
92
|
throw new Error(`create-oke: source "${label}" has no package.json`);
|
|
97
93
|
}
|
|
98
|
-
const sourcePkg = JSON.parse(
|
|
99
|
-
readFileSync(pkgPath, "utf8"),
|
|
100
|
-
) as ScaffoldPackageJson;
|
|
94
|
+
const sourcePkg = JSON.parse(readFileSync(pkgPath, "utf8")) as ScaffoldPackageJson;
|
|
101
95
|
const nextPkg = transformPackageJson(sourcePkg, name, okengineDependency);
|
|
102
96
|
writeFileSync(pkgPath, `${JSON.stringify(nextPkg, null, 2)}\n`, "utf8");
|
|
103
97
|
|
|
98
|
+
applySqlDriverTransforms(targetDir, sqlDriver);
|
|
99
|
+
|
|
104
100
|
if (options.writeAgentsMd !== false) {
|
|
105
101
|
const agentsPath = join(targetDir, "AGENTS.md");
|
|
106
102
|
writeFileSync(agentsPath, agentsMdContent(name), "utf8");
|
|
107
103
|
if (!written.includes("AGENTS.md")) written.push("AGENTS.md");
|
|
108
104
|
}
|
|
109
105
|
|
|
106
|
+
const envExample = join(targetDir, ".env.example");
|
|
107
|
+
const envLocal = join(targetDir, ".env.local");
|
|
108
|
+
if (existsSync(envExample) && !existsSync(envLocal)) {
|
|
109
|
+
cpSync(envExample, envLocal);
|
|
110
|
+
if (!written.includes(".env.local")) written.push(".env.local");
|
|
111
|
+
}
|
|
112
|
+
|
|
110
113
|
written.sort();
|
|
111
114
|
return {
|
|
112
115
|
targetDir,
|
|
@@ -114,6 +117,7 @@ export function scaffold(options: ScaffoldOptions): ScaffoldResult {
|
|
|
114
117
|
source: options.source,
|
|
115
118
|
label,
|
|
116
119
|
okengineDependency,
|
|
120
|
+
sqlDriver,
|
|
117
121
|
files: written,
|
|
118
122
|
};
|
|
119
123
|
} catch (e) {
|
|
@@ -122,6 +126,27 @@ export function scaffold(options: ScaffoldOptions): ScaffoldResult {
|
|
|
122
126
|
}
|
|
123
127
|
}
|
|
124
128
|
|
|
129
|
+
/**
|
|
130
|
+
* For postgres, pin `store.sql` local/docker/prod in `oke.config.ts`.
|
|
131
|
+
*
|
|
132
|
+
* The default template ships abstract `src/schema.decl.ts` — dialect is
|
|
133
|
+
* emitted from the active `store.sql` driver, so there is no hand-written
|
|
134
|
+
* `sqliteTable`/`pgTable` source to rewrite. `sqlite` keeps the template
|
|
135
|
+
* dual-mode config (`local: sqlite` · `docker`/`prod: postgres`) untouched.
|
|
136
|
+
*
|
|
137
|
+
* @param targetDir - Scaffolded project root
|
|
138
|
+
* @param sqlDriver - Chosen store.sql driver
|
|
139
|
+
*/
|
|
140
|
+
function applySqlDriverTransforms(targetDir: string, sqlDriver: SqlDriverId): void {
|
|
141
|
+
if (sqlDriver !== "postgres") return;
|
|
142
|
+
|
|
143
|
+
const configPath = join(targetDir, "oke.config.ts");
|
|
144
|
+
if (existsSync(configPath)) {
|
|
145
|
+
const next = transformConfigForSqlDriver(readFileSync(configPath, "utf8"), sqlDriver);
|
|
146
|
+
writeFileSync(configPath, next, "utf8");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
125
150
|
/**
|
|
126
151
|
* Recursively copy template files, applying {@link shouldSkipTemplatePath}.
|
|
127
152
|
*
|
|
@@ -130,12 +155,7 @@ export function scaffold(options: ScaffoldOptions): ScaffoldResult {
|
|
|
130
155
|
* @param rel - Relative path under the roots
|
|
131
156
|
* @param written - Accumulator for relative paths written
|
|
132
157
|
*/
|
|
133
|
-
function copyTree(
|
|
134
|
-
srcRoot: string,
|
|
135
|
-
dstRoot: string,
|
|
136
|
-
rel: string,
|
|
137
|
-
written: string[],
|
|
138
|
-
): void {
|
|
158
|
+
function copyTree(srcRoot: string, dstRoot: string, rel: string, written: string[]): void {
|
|
139
159
|
const src = rel ? join(srcRoot, rel) : srcRoot;
|
|
140
160
|
const st = statSync(src);
|
|
141
161
|
if (st.isDirectory()) {
|