@hozu/cli 0.1.0 → 0.2.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.
@@ -0,0 +1,18 @@
1
+ import type { AddOutput } from '../contract.ts';
2
+ export declare function namesOf(name: string): {
3
+ id: string;
4
+ Item: string;
5
+ New: string;
6
+ list: string;
7
+ add: string;
8
+ tag: string;
9
+ machine: string;
10
+ View: string;
11
+ feature: string;
12
+ resolvers: string;
13
+ title: string;
14
+ one: string;
15
+ };
16
+ export declare function runAddFeature(cwd: string, config: string | undefined, name: string | undefined, page: string | undefined): Promise<AddOutput>;
17
+ export declare function describeAdd(out: AddOutput): string;
18
+ //# sourceMappingURL=add.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAK/C,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM;;;;;;;;;;;;;EAiBnC;AAiND,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,OAAO,CAAC,SAAS,CAAC,CAyGpB;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAQlD"}
@@ -0,0 +1,316 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { dirname, join, relative, resolve } from 'node:path';
4
+ import { HozuCliError } from '../errors.js';
5
+ const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
6
+ export function namesOf(name) {
7
+ const one = name.endsWith('s') && name.length > 1 ? name.slice(0, -1) : name;
8
+ const many = name.endsWith('s') ? name : `${name}s`;
9
+ return {
10
+ id: name,
11
+ Item: cap(one),
12
+ New: `New${cap(one)}`,
13
+ list: `list${cap(many)}`,
14
+ add: `add${cap(one)}`,
15
+ tag: `${name}Tag`,
16
+ machine: `${name}Machine`,
17
+ View: `${cap(name)}Board`,
18
+ feature: name,
19
+ resolvers: `${name}Resolvers`,
20
+ title: cap(name),
21
+ one,
22
+ };
23
+ }
24
+ const model = (n) => `import { event, invoke, machine, mutation, on, op, query, tag } from '@hozu/core'
25
+ import { z } from 'zod'
26
+
27
+ export const ${n.Item} = z.object({ id: z.string(), title: z.string() })
28
+ export const ${n.New} = z.object({
29
+ title: z.string().min(2, 'Use at least 2 characters').max(80, 'Use at most 80 characters'),
30
+ })
31
+
32
+ export const Draft = event({ payload: z.object({ text: z.string() }) })
33
+ export const Add = event({ payload: z.object({ title: z.string() }) })
34
+
35
+ export const ${n.tag} = tag({ param: null })
36
+
37
+ export const ${n.list} = query({
38
+ input: z.object({}),
39
+ output: z.array(${n.Item}),
40
+ scope: 'public',
41
+ freshness: 'static',
42
+ tags: () => [${n.tag}()],
43
+ })
44
+
45
+ export const ${n.add} = mutation({
46
+ input: ${n.New},
47
+ output: ${n.Item},
48
+ errors: { Duplicate: z.object({ title: z.string() }) },
49
+ invalidates: () => [${n.tag}()],
50
+ })
51
+
52
+ export const DUPLICATE = 'This ${n.one} already exists'
53
+
54
+ export const ${n.machine} = machine({
55
+ context: z.object({
56
+ draft: z.string(),
57
+ error: z.string().nullable(),
58
+ fields: z.object({ title: z.string().nullable() }),
59
+ }),
60
+ initialContext: { draft: '', error: null, fields: { title: null } },
61
+ initial: 'idle',
62
+ states: ({ ctx }) => ({
63
+ idle: {
64
+ on: [
65
+ on(Draft, { target: 'idle', assign: (e) => [op.set(ctx.draft, e.text)] }),
66
+ on(Add, {
67
+ target: 'adding',
68
+ assign: (e) => [
69
+ op.set(ctx.draft, e.title),
70
+ op.set(ctx.error, null),
71
+ op.set(ctx.fields, { title: null }),
72
+ ],
73
+ }),
74
+ ],
75
+ },
76
+ adding: {
77
+ ignore: [Draft, Add],
78
+ invoke: invoke(${n.add}, {
79
+ input: { title: ctx.draft },
80
+ done: [{ target: 'idle', assign: () => [op.set(ctx.draft, '')] }],
81
+ failed: {
82
+ Duplicate: [{ target: 'idle', assign: () => [op.set(ctx.error, DUPLICATE)] }],
83
+ Invalid: [{ target: 'idle', assign: (e) => [op.set(ctx.fields, e.fields)] }],
84
+ Unexpected: [{ target: 'idle', assign: (e) => [op.set(ctx.error, e.message)] }],
85
+ },
86
+ }),
87
+ },
88
+ }),
89
+ })
90
+ `;
91
+ const views = (n) => `import { contract, feature, op, ui } from '@hozu/core'
92
+ import { Add, ${n.add}, DUPLICATE, Draft, ${n.list}, ${n.machine}, ${n.tag} } from './model.ts'
93
+
94
+ export const ${n.View} = ui.view({
95
+ machine: ${n.machine},
96
+ render: ({ ctx, when }) =>
97
+ ui.main({ class: 'mx-auto max-w-xl space-y-6 px-4 py-12' }, [
98
+ ui.h1({ class: 'text-3xl font-bold' }, ['${n.title}']),
99
+ ui.form({ class: 'flex gap-2', on: { submit: ui.send(Add, { title: ui.dom.form('title') }) } }, [
100
+ ui.label({ for: '${n.id}-title', class: 'sr-only' }, ['Title']),
101
+ ui.input({
102
+ id: '${n.id}-title',
103
+ name: 'title',
104
+ required: true,
105
+ minlength: 2,
106
+ maxlength: 80,
107
+ value: ctx.draft,
108
+ 'aria-invalid': op.neq(ctx.fields.title, null),
109
+ 'aria-describedby': '${n.id}-title-error',
110
+ class: 'flex-1 rounded border px-3 py-2',
111
+ on: { input: ui.send(Draft, { text: ui.dom.value }) },
112
+ }),
113
+ ui.button({ type: 'submit', class: 'rounded bg-indigo-600 px-4 py-2 text-white' }, ['Add']),
114
+ ]),
115
+ ui.p({ id: '${n.id}-title-error', class: 'text-sm text-rose-600' }, [ctx.fields.title]),
116
+ ui.if(op.neq(ctx.error, null), [ui.p({ role: 'alert', class: 'text-rose-600' }, [ctx.error])], []),
117
+ when(['adding'], [ui.p({ class: 'opacity-50', 'aria-busy': 'true' }, ['Adding ', ctx.draft, '…'])]),
118
+ ui.query(
119
+ ${n.list},
120
+ {},
121
+ {
122
+ ready: (items) =>
123
+ ui.ul({ class: 'divide-y rounded border' }, [
124
+ ui.each(items, 'id', (item) => ui.li({ class: 'px-4 py-3' }, [item.title])),
125
+ ]),
126
+ pending: ui.p({}, ['Loading…']),
127
+ failed: { Unexpected: () => ui.p({ role: 'alert' }, ['Unavailable']) },
128
+ },
129
+ ),
130
+ ]),
131
+ })
132
+
133
+ export const typesDraft = contract(${n.machine}, {
134
+ given: { state: 'idle' },
135
+ when: [{ send: Draft, payload: { text: 'Ship' } }],
136
+ expect: { state: 'idle', changes: { draft: 'Ship' } },
137
+ })
138
+
139
+ export const adds = contract(${n.machine}, {
140
+ given: { state: 'idle' },
141
+ when: [
142
+ { send: Add, payload: { title: 'Ship' } },
143
+ { done: ${n.add}, result: { id: 'x1', title: 'Ship' } },
144
+ ],
145
+ expect: { state: 'idle', effects: [{ effect: ${n.add}, input: { title: 'Ship' } }] },
146
+ })
147
+
148
+ export const rejectsDuplicate = contract(${n.machine}, {
149
+ given: { state: 'adding' },
150
+ when: [{ failed: ${n.add}, error: 'Duplicate', data: { title: 'Ship' } }],
151
+ expect: { state: 'idle', changes: { error: DUPLICATE } },
152
+ })
153
+
154
+ export const rejectsInvalid = contract(${n.machine}, {
155
+ given: { state: 'adding' },
156
+ when: [
157
+ {
158
+ failed: ${n.add},
159
+ error: 'Invalid',
160
+ data: { message: 'title: Use at least 2 characters', fields: { title: 'Use at least 2 characters' } },
161
+ },
162
+ ],
163
+ expect: { state: 'idle', changes: { fields: { title: 'Use at least 2 characters' } } },
164
+ })
165
+
166
+ export const addFails = contract(${n.machine}, {
167
+ given: { state: 'adding' },
168
+ when: [{ failed: ${n.add}, error: 'Unexpected', data: { message: 'offline' } }],
169
+ expect: { state: 'idle', changes: { error: 'offline' } },
170
+ })
171
+
172
+ export const ${n.feature} = feature({
173
+ id: '${n.id}',
174
+ intent: { summary: '${n.title}: add one with a title; titles are unique, case-insensitive.' },
175
+ declarations: {
176
+ Draft,
177
+ Add,
178
+ ${n.tag},
179
+ ${n.list},
180
+ ${n.add},
181
+ ${n.machine},
182
+ ${n.View},
183
+ typesDraft,
184
+ adds,
185
+ rejectsDuplicate,
186
+ rejectsInvalid,
187
+ addFails,
188
+ },
189
+ })
190
+ `;
191
+ const server = (n) => `import type { Implement } from '@hozu/data'
192
+ import { ${n.add}, ${n.list} } from './model.ts'
193
+
194
+ export function ${n.resolvers}<Session, Env>(implement: Implement<Session, Env>) {
195
+ const items: { id: string; title: string }[] = []
196
+ let seq = 0
197
+ return [
198
+ implement(${n.list}, () => items.map((item) => ({ ...item }))),
199
+ implement(${n.add}, ({ title }, { fail }) => {
200
+ const clean = title.trim()
201
+ if (items.some((item) => item.title.toLowerCase() === clean.toLowerCase()))
202
+ return fail('Duplicate', { title: clean })
203
+ const item = { id: \`${n.one.charAt(0)}\${++seq}\`, title: clean }
204
+ items.unshift(item)
205
+ return { ...item }
206
+ }),
207
+ ]
208
+ }
209
+ `;
210
+ const addImport = (source, line) => {
211
+ const imports = [...source.matchAll(/^import[\s\S]*?from '[^']+'\n/gm)];
212
+ const last = imports.at(-1);
213
+ if (!last)
214
+ return null;
215
+ const at = last.index + last[0].length;
216
+ return source.slice(0, at) + line + source.slice(at);
217
+ };
218
+ const append = (source, pattern, item) => {
219
+ const m = pattern.exec(source);
220
+ if (!m)
221
+ return null;
222
+ const inner = m[1].trim().replace(/,$/, '');
223
+ const replaced = m[0].replace(m[1], inner ? `${inner}, ${item}` : item);
224
+ return source.slice(0, m.index) + replaced + source.slice(m.index + m[0].length);
225
+ };
226
+ export async function runAddFeature(cwd, config, name, page) {
227
+ if (!name || !/^[a-z][a-zA-Z0-9]*$/.test(name))
228
+ throw new HozuCliError('usage', 'Give the feature a lower-case identifier', [
229
+ 'hozu add feature tasks --page /',
230
+ ]);
231
+ if (page !== undefined && !/^\/[\w/-]*$/.test(page))
232
+ throw new HozuCliError('usage', '--page must be a static path such as / or /tasks', [
233
+ 'Routes with params are written by hand: route({ path: "/tasks/:id", params: z.object({ id: z.string() }), search: null })',
234
+ ]);
235
+ const configPath = resolve(cwd, config ?? 'hozu.config.ts');
236
+ const root = dirname(configPath);
237
+ const dir = join(root, 'features', name);
238
+ if (existsSync(dir))
239
+ throw new HozuCliError('usage', `features/${name} already exists`, []);
240
+ const n = namesOf(name);
241
+ const out = { created: [], edited: [], manual: [] };
242
+ await mkdir(dir, { recursive: true });
243
+ for (const [file, text] of [
244
+ ['model.ts', model(n)],
245
+ ['views.ts', views(n)],
246
+ ['server.ts', server(n)],
247
+ ]) {
248
+ await writeFile(join(dir, file), text);
249
+ out.created.push(relative(cwd, join(dir, file)));
250
+ }
251
+ const edit = async (file, change, manual) => {
252
+ const path = join(root, file);
253
+ const source = existsSync(path) ? await readFile(path, 'utf8') : null;
254
+ const next = source === null ? null : change(source);
255
+ if (next === null)
256
+ out.manual.push(`${file}: ${manual}`);
257
+ else {
258
+ await writeFile(path, next);
259
+ if (!out.edited.includes(relative(cwd, path)))
260
+ out.edited.push(relative(cwd, path));
261
+ }
262
+ };
263
+ await edit('server.ts', (s) => {
264
+ const wired = s.replace(/resolvers\(project,\s*\((?:implement)?\)\s*=>\s*\[/, `resolvers(project, (implement) => [\n ...${n.resolvers}(implement),`);
265
+ return wired === s
266
+ ? null
267
+ : addImport(wired, `import { ${n.resolvers} } from './features/${name}/server.ts'\n`);
268
+ }, `import { ${n.resolvers} } from './features/${name}/server.ts' and add ...${n.resolvers}(implement) to resolvers(project, (implement) => [...])`);
269
+ let pageRoute = null;
270
+ let newRoute = false;
271
+ if (page !== undefined) {
272
+ const routesPath = join(root, 'routes.ts');
273
+ const routes = existsSync(routesPath) ? await readFile(routesPath, 'utf8') : '';
274
+ const existing = new RegExp(`export const (\\w+) = route\\(\\{\\s*path: '${page.replace(/[/-]/g, '\\$&')}'`).exec(routes);
275
+ pageRoute = existing?.[1] ?? `${name}Page`;
276
+ newRoute = !existing;
277
+ if (newRoute)
278
+ await edit('routes.ts', (s) => `${s.trimEnd()}\nexport const ${pageRoute} = route({ path: '${page}', params: null, search: null })\n`, `export const ${pageRoute} = route({ path: '${page}', params: null, search: null })`);
279
+ }
280
+ await edit(config ?? 'hozu.config.ts', (s) => {
281
+ let next = addImport(s, `import { ${n.View}, ${n.feature} } from './features/${name}/views.ts'\n`);
282
+ if (next)
283
+ next = append(next, /features:\s*\[([^\]]*)\]/, n.feature);
284
+ if (next && pageRoute && newRoute) {
285
+ next = append(next, /routes:\s*\{([^}]*)\}/, pageRoute);
286
+ next =
287
+ next?.replace(/import \{([^}]*)\} from '\.\/routes\.ts'/, (all, names) => all.replace(names, ` ${[
288
+ ...names
289
+ .split(',')
290
+ .map((x) => x.trim())
291
+ .filter(Boolean),
292
+ pageRoute,
293
+ ]
294
+ .sort()
295
+ .join(', ')} `)) ?? null;
296
+ next =
297
+ next?.replace(/pages:\s*\[/, (m) => `${m}\n ui.page(${pageRoute}, { views: [${n.View}], head: { render: () => ({ title: '${n.title}' }) } }),`) ?? null;
298
+ }
299
+ else if (next && pageRoute) {
300
+ const views = new RegExp(`ui\\.page\\(\\s*${pageRoute}\\s*,\\s*\\{\\s*views:\\s*\\[[^\\]]*\\]`);
301
+ next = views.test(next) ? next.replace(views, `ui.page(${pageRoute}, { views: [${n.View}]`) : null;
302
+ }
303
+ return next;
304
+ }, `import { ${n.View}, ${n.feature} } from './features/${name}/views.ts', add ${n.feature} to features${pageRoute ? ` and ui.page(${pageRoute}, { views: [${n.View}], head: { render: () => ({ title: '${n.title}' }) } }) to pages` : ''}`);
305
+ return out;
306
+ }
307
+ export function describeAdd(out) {
308
+ const lines = [
309
+ ...out.created.map((f) => `created ${f}`),
310
+ ...out.edited.map((f) => `edited ${f}`),
311
+ ...out.manual.map((m) => `todo ${m}`),
312
+ 'next hozu check',
313
+ ];
314
+ return `${lines.join('\n')}\n`;
315
+ }
316
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { CheckOutput, TypeIssue } from '../contract.ts';
2
+ import type { Loaded } from '../load.ts';
3
+ export declare function typeErrors(output: string, root: string): TypeIssue[];
4
+ export declare function runCheck(loaded: Loaded, cwd: string, updateLock: boolean): Promise<CheckOutput>;
5
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAcxC,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CAcpE;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAsBrG"}
@@ -0,0 +1,55 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { readFileSync } from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import { dirname, join, relative } from 'node:path';
5
+ import { runValidate } from './validate.js';
6
+ function typescriptBin(from) {
7
+ try {
8
+ const require = createRequire(from);
9
+ const manifest = require.resolve('typescript/package.json');
10
+ const bin = JSON.parse(readFileSync(manifest, 'utf8')).bin?.tsc;
11
+ return bin ? join(dirname(manifest), bin) : null;
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ }
17
+ export function typeErrors(output, root) {
18
+ const errors = [];
19
+ for (const line of output.split('\n')) {
20
+ const m = /^(.+?)\((\d+),(\d+)\): error (TS\d+): (.*)$/.exec(line.trim());
21
+ if (m)
22
+ errors.push({
23
+ file: relative(root, join(root, m[1])),
24
+ line: Number(m[2]),
25
+ column: Number(m[3]),
26
+ code: m[4],
27
+ message: m[5],
28
+ });
29
+ }
30
+ return errors;
31
+ }
32
+ export async function runCheck(loaded, cwd, updateLock) {
33
+ const root = dirname(loaded.path);
34
+ const tsc = typescriptBin(loaded.path);
35
+ let types;
36
+ if (!tsc)
37
+ types = { ok: false, skipped: true, errors: [] };
38
+ else {
39
+ const run = spawnSync(process.execPath, [tsc, '--noEmit', '--pretty', 'false', '-p', root], {
40
+ cwd: root,
41
+ encoding: 'utf8',
42
+ });
43
+ const errors = typeErrors(`${run.stdout}\n${run.stderr}`, root);
44
+ types = {
45
+ ok: run.status === 0,
46
+ skipped: false,
47
+ errors: run.status === 0 || errors.length
48
+ ? errors
49
+ : [{ file: '', line: 0, column: 0, code: 'tsc', message: (run.stderr || run.stdout).trim() }],
50
+ };
51
+ }
52
+ const validate = await runValidate(loaded, undefined, cwd, updateLock);
53
+ return { ok: types.ok && validate.ok, types, validate };
54
+ }
55
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { RequestOutput } from '../contract.ts';
2
+ import type { Loaded } from '../load.ts';
3
+ interface Form {
4
+ action: string;
5
+ fields: Record<string, string>;
6
+ buttons: string[];
7
+ }
8
+ export declare function formsOf(html: string, at: string): Form[];
9
+ export interface RequestOptions {
10
+ method: 'GET' | 'POST';
11
+ paths: string[];
12
+ fields: string[];
13
+ next: string[];
14
+ button: string | undefined;
15
+ session: string | undefined;
16
+ full: boolean;
17
+ }
18
+ export declare function runRequest(loaded: Loaded, options: RequestOptions): Promise<RequestOutput>;
19
+ export declare function describeRequest(out: RequestOutput): string;
20
+ export {};
21
+ //# sourceMappingURL=request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/commands/request.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,gBAAgB,CAAA;AAEhE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAqCxC,UAAU,IAAI;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,EAAE,CAqCxD;AAYD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,KAAK,GAAG,MAAM,CAAA;IACtB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,IAAI,EAAE,OAAO,CAAA;CACd;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAqIhG;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAU1D"}