@hozu/cli 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/commands/add.d.ts +6 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +269 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/check.d.ts +5 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +55 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/map.d.ts +5 -0
- package/dist/commands/map.d.ts.map +1 -0
- package/dist/commands/map.js +125 -0
- package/dist/commands/map.js.map +1 -0
- package/dist/commands/request.d.ts +24 -0
- package/dist/commands/request.d.ts.map +1 -0
- package/dist/commands/request.js +305 -0
- package/dist/commands/request.js.map +1 -0
- package/dist/commands/scaffold.d.ts +32 -0
- package/dist/commands/scaffold.d.ts.map +1 -0
- package/dist/commands/scaffold.js +533 -0
- package/dist/commands/scaffold.js.map +1 -0
- package/dist/contract.d.ts +119 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +88 -1
- package/dist/main.js.map +1 -1
- package/package.json +6 -6
- package/schema/add.schema.json +69 -0
- package/schema/check.schema.json +405 -0
- package/schema/map.schema.json +383 -0
- package/schema/request.schema.json +144 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
export const PARTS = ['auth', 'detail', 'toggle', 'filter', 'remove'];
|
|
2
|
+
const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
3
|
+
export function namesOf(name) {
|
|
4
|
+
const one = name.endsWith('s') && name.length > 1 ? name.slice(0, -1) : name;
|
|
5
|
+
const many = name.endsWith('s') ? name : `${name}s`;
|
|
6
|
+
const Item = cap(one);
|
|
7
|
+
return {
|
|
8
|
+
id: name,
|
|
9
|
+
one,
|
|
10
|
+
many,
|
|
11
|
+
Item,
|
|
12
|
+
Key: `${Item}Key`,
|
|
13
|
+
New: `New${Item}`,
|
|
14
|
+
list: `list${cap(many)}`,
|
|
15
|
+
get: `get${Item}`,
|
|
16
|
+
add: `add${Item}`,
|
|
17
|
+
toggle: `toggle${Item}`,
|
|
18
|
+
remove: `remove${Item}`,
|
|
19
|
+
tag: `${name}Tag`,
|
|
20
|
+
machine: `${name}Machine`,
|
|
21
|
+
View: `${cap(name)}Board`,
|
|
22
|
+
Detail: `${Item}Detail`,
|
|
23
|
+
detailRoute: `${one}Page`,
|
|
24
|
+
feature: name,
|
|
25
|
+
resolvers: `${name}Resolvers`,
|
|
26
|
+
title: cap(name),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const lines = (...parts) => parts.filter((p) => p !== false).join('\n');
|
|
30
|
+
const events = (w) => ['Draft', 'Add', w.toggle && 'Toggle', w.remove && 'Remove', w.filter && 'SetShow'].filter(Boolean);
|
|
31
|
+
export function model(n, w) {
|
|
32
|
+
const keyed = w.detail || w.toggle || w.remove;
|
|
33
|
+
const busy = w.toggle || w.remove;
|
|
34
|
+
const ignore = `ignore: [${events(w).join(', ')}],`;
|
|
35
|
+
const core = [
|
|
36
|
+
'event',
|
|
37
|
+
...(w.filter ? ['fn'] : []),
|
|
38
|
+
'invoke',
|
|
39
|
+
'machine',
|
|
40
|
+
'mutation',
|
|
41
|
+
'on',
|
|
42
|
+
'op',
|
|
43
|
+
'query',
|
|
44
|
+
'tag',
|
|
45
|
+
];
|
|
46
|
+
const actionState = (state, effect) => ` ${state}: {
|
|
47
|
+
${ignore}
|
|
48
|
+
invoke: invoke(${effect}, {
|
|
49
|
+
input: { id: ctx.target },
|
|
50
|
+
done: [{ target: 'idle' }],
|
|
51
|
+
failed: {
|
|
52
|
+
NotFound: [{ target: 'idle', assign: () => [op.set(ctx.error, NOT_FOUND)] }],
|
|
53
|
+
Unexpected: [{ target: 'idle', assign: (e) => [op.set(ctx.error, e.message)] }],
|
|
54
|
+
},
|
|
55
|
+
}),
|
|
56
|
+
},`;
|
|
57
|
+
return `${lines(`import { ${core.join(', ')} } from '@hozu/core'`, `import { z } from 'zod'`, '', `export const ${n.Item} = z.object({ id: z.string(), title: z.string()${w.toggle ? ', done: z.boolean()' : ''} })`, keyed && `export const ${n.Key} = z.object({ id: z.string() })`, `export const ${n.New} = z.object({`, ` title: z.string().min(2, 'Use at least 2 characters').max(80, 'Use at most 80 characters'),`, '})', w.filter && `export const Show = z.enum(['all', 'open', 'done'])`, '', `export const Draft = event({ payload: z.object({ text: z.string() }) })`, `export const Add = event({ payload: z.object({ title: z.string() }) })`, w.toggle && `export const Toggle = event({ payload: ${n.Key} })`, w.remove && `export const Remove = event({ payload: ${n.Key} })`, w.filter && `export const SetShow = event({ payload: z.object({ show: Show }) })`, '', `export const ${n.tag} = tag({ param: null })`, '', `export const ${n.list} = query({`, ` input: z.object({}),`, ` output: z.array(${n.Item}),`, w.auth && ` errors: { Unauthorized: z.object({}) },`, ` scope: '${w.auth ? 'user' : 'public'}',`, ` freshness: 'static',`, ` tags: () => [${n.tag}()],`, '})', w.detail &&
|
|
58
|
+
`
|
|
59
|
+
export const ${n.get} = query({
|
|
60
|
+
input: ${n.Key},
|
|
61
|
+
output: ${n.Item},
|
|
62
|
+
errors: { NotFound: ${n.Key} },
|
|
63
|
+
scope: '${w.auth ? 'user' : 'public'}',
|
|
64
|
+
freshness: 'static',
|
|
65
|
+
tags: () => [${n.tag}()],
|
|
66
|
+
})`, '', `export const ${n.add} = mutation({`, ` input: ${n.New},`, ` output: ${n.Item},`, ` errors: { Duplicate: z.object({ title: z.string() }) },`, ` invalidates: () => [${n.tag}()],`, '})', w.toggle &&
|
|
67
|
+
`
|
|
68
|
+
export const ${n.toggle} = mutation({
|
|
69
|
+
input: ${n.Key},
|
|
70
|
+
output: ${n.Item},
|
|
71
|
+
errors: { NotFound: ${n.Key} },
|
|
72
|
+
invalidates: () => [${n.tag}()],
|
|
73
|
+
})`, w.remove &&
|
|
74
|
+
`
|
|
75
|
+
export const ${n.remove} = mutation({
|
|
76
|
+
input: ${n.Key},
|
|
77
|
+
output: ${n.Key},
|
|
78
|
+
errors: { NotFound: ${n.Key} },
|
|
79
|
+
invalidates: () => [${n.tag}()],
|
|
80
|
+
})`, w.filter &&
|
|
81
|
+
`
|
|
82
|
+
const Visible = z.object({ items: z.array(${n.Item}), show: Show })
|
|
83
|
+
|
|
84
|
+
export const visible = fn({
|
|
85
|
+
input: Visible,
|
|
86
|
+
output: z.array(${n.Item}),
|
|
87
|
+
impl: ({ items, show }) => items.filter((item) => show === 'all' || (show === 'done') === item.done),
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
export const isEmpty = fn({
|
|
91
|
+
input: Visible,
|
|
92
|
+
output: z.boolean(),
|
|
93
|
+
impl: ({ items, show }) => !items.some((item) => show === 'all' || (show === 'done') === item.done),
|
|
94
|
+
})`, '', `export const DUPLICATE = 'This ${n.one} already exists'`, busy && `export const NOT_FOUND = 'This ${n.one} no longer exists'`, '', `export const ${n.machine} = machine({`, ' context: z.object({', ' draft: z.string(),', ' error: z.string().nullable(),', ' fields: z.object({ title: z.string().nullable() }),', busy && ' target: z.string(),', w.filter && ' show: Show,', ' }),', ` initialContext: { draft: '', error: null, fields: { title: null }${busy ? ", target: ''" : ''}${w.filter ? ", show: 'all'" : ''} },`, ` initial: 'idle',`, ' states: ({ ctx }) => ({', ' idle: {', ' on: [', ` on(Draft, { target: 'idle', assign: (e) => [op.set(ctx.draft, e.text)] }),`, ' on(Add, {', ` target: 'adding',`, ' assign: (e) => [', ' op.set(ctx.draft, e.title),', ' op.set(ctx.error, null),', ' op.set(ctx.fields, { title: null }),', ' ],', ' }),', w.toggle &&
|
|
95
|
+
` on(Toggle, { target: 'toggling', assign: (e) => [op.set(ctx.target, e.id), op.set(ctx.error, null)] }),`, w.remove &&
|
|
96
|
+
` on(Remove, { target: 'removing', assign: (e) => [op.set(ctx.target, e.id), op.set(ctx.error, null)] }),`, w.filter && ` on(SetShow, { target: 'idle', assign: (e) => [op.set(ctx.show, e.show)] }),`, ' ],', ' },', ' adding: {', ` ${ignore}`, ` invoke: invoke(${n.add}, {`, ' input: { title: ctx.draft },', ` done: [{ target: 'idle', assign: () => [op.set(ctx.draft, '')] }],`, ' failed: {', ` Duplicate: [{ target: 'idle', assign: () => [op.set(ctx.error, DUPLICATE)] }],`, ` Invalid: [{ target: 'idle', assign: (e) => [op.set(ctx.fields, e.fields)] }],`, ` Unexpected: [{ target: 'idle', assign: (e) => [op.set(ctx.error, e.message)] }],`, ' },', ' }),', ' },', w.toggle && actionState('toggling', n.toggle), w.remove && actionState('removing', n.remove), ' }),', '})')}\n`;
|
|
97
|
+
}
|
|
98
|
+
export function views(n, w, listRoute) {
|
|
99
|
+
const modelNames = [
|
|
100
|
+
'Add',
|
|
101
|
+
n.add,
|
|
102
|
+
'DUPLICATE',
|
|
103
|
+
'Draft',
|
|
104
|
+
n.list,
|
|
105
|
+
n.machine,
|
|
106
|
+
n.tag,
|
|
107
|
+
...(w.detail ? [n.get] : []),
|
|
108
|
+
...(w.toggle ? ['Toggle', n.toggle] : []),
|
|
109
|
+
...(w.remove ? ['Remove', n.remove] : []),
|
|
110
|
+
...(w.toggle || w.remove ? ['NOT_FOUND'] : []),
|
|
111
|
+
...(w.filter ? ['SetShow', 'visible', 'isEmpty'] : []),
|
|
112
|
+
].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
113
|
+
const itemForm = (event, label) => `ui.form({ class: 'inline', on: { submit: ui.send(${event}, { id: ui.dom.form('id') }) } }, [
|
|
114
|
+
ui.input({ type: 'hidden', name: 'id', value: item.id }),
|
|
115
|
+
ui.button({ type: 'submit', class: 'text-sm underline' }, [${label}]),
|
|
116
|
+
])`;
|
|
117
|
+
const itemChildren = [
|
|
118
|
+
w.detail
|
|
119
|
+
? `ui.a({ href: ui.link(${n.detailRoute}, { id: item.id }), class: 'flex-1 underline' }, [item.title])`
|
|
120
|
+
: `ui.span({ class: 'flex-1' }, [item.title])`,
|
|
121
|
+
...(w.toggle
|
|
122
|
+
? [
|
|
123
|
+
`ui.span({ class: 'text-xs text-slate-500' }, [ui.if(op.eq(item.done, true), ['done'], ['open'])])`,
|
|
124
|
+
itemForm('Toggle', `ui.if(op.eq(item.done, true), ['Mark open'], ['Mark done'])`),
|
|
125
|
+
]
|
|
126
|
+
: []),
|
|
127
|
+
...(w.remove ? [itemForm('Remove', `'Delete'`)] : []),
|
|
128
|
+
];
|
|
129
|
+
const list = `ui.ul({ class: 'divide-y rounded border' }, [
|
|
130
|
+
ui.each(${w.filter ? 'visible({ items, show: ctx.show })' : 'items'}, 'id', (item) =>
|
|
131
|
+
ui.li({ class: 'flex items-center gap-3 px-4 py-3' }, [
|
|
132
|
+
${itemChildren.join(',\n ')},
|
|
133
|
+
]),
|
|
134
|
+
),
|
|
135
|
+
])`;
|
|
136
|
+
const ready = w.filter
|
|
137
|
+
? `ui.if(
|
|
138
|
+
isEmpty({ items, show: ctx.show }),
|
|
139
|
+
[ui.p({ class: 'text-slate-500' }, ['No items'])],
|
|
140
|
+
[
|
|
141
|
+
${list},
|
|
142
|
+
],
|
|
143
|
+
)`
|
|
144
|
+
: list;
|
|
145
|
+
const actionContracts = (event, state, effect, name, result) => `
|
|
146
|
+
export const ${name}s = contract(${n.machine}, {
|
|
147
|
+
given: { state: 'idle' },
|
|
148
|
+
when: [
|
|
149
|
+
{ send: ${event}, payload: { id: 'x1' } },
|
|
150
|
+
{ done: ${effect}, result: ${result} },
|
|
151
|
+
],
|
|
152
|
+
expect: { state: 'idle', changes: { target: 'x1' }, effects: [{ effect: ${effect}, input: { id: 'x1' } }] },
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
export const ${name}Missing = contract(${n.machine}, {
|
|
156
|
+
given: { state: '${state}' },
|
|
157
|
+
when: [{ failed: ${effect}, error: 'NotFound', data: { id: 'x1' } }],
|
|
158
|
+
expect: { state: 'idle', changes: { error: NOT_FOUND } },
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
export const ${name}Fails = contract(${n.machine}, {
|
|
162
|
+
given: { state: '${state}' },
|
|
163
|
+
when: [{ failed: ${effect}, error: 'Unexpected', data: { message: 'offline' } }],
|
|
164
|
+
expect: { state: 'idle', changes: { error: 'offline' } },
|
|
165
|
+
})`;
|
|
166
|
+
const itemResult = `{ id: 'x1', title: 'Ship'${w.toggle ? ', done: true' : ''} }`;
|
|
167
|
+
const contracts = [
|
|
168
|
+
'typesDraft',
|
|
169
|
+
'adds',
|
|
170
|
+
'rejectsDuplicate',
|
|
171
|
+
'rejectsInvalid',
|
|
172
|
+
'addFails',
|
|
173
|
+
...(w.toggle ? ['toggles', 'toggleMissing', 'toggleFails'] : []),
|
|
174
|
+
...(w.remove ? ['removes', 'removeMissing', 'removeFails'] : []),
|
|
175
|
+
...(w.filter ? ['filters'] : []),
|
|
176
|
+
];
|
|
177
|
+
return `${lines(`import { contract, feature, op, ui } from '@hozu/core'`, w.detail && `import { ${[listRoute ?? 'home', n.detailRoute].sort().join(', ')} } from '../../routes.ts'`, `import {\n ${modelNames.join(',\n ')},\n} from './model.ts'`, '', w.filter &&
|
|
178
|
+
`const shows = [
|
|
179
|
+
{ value: 'all', label: 'All' },
|
|
180
|
+
{ value: 'open', label: 'Open' },
|
|
181
|
+
{ value: 'done', label: 'Done' },
|
|
182
|
+
] as const
|
|
183
|
+
`, `export const ${n.View} = ui.view({`, ` machine: ${n.machine},`, ' render: ({ ctx, when }) =>', ` ui.main({ class: 'mx-auto max-w-xl space-y-6 px-4 py-12' }, [`, ` ui.h1({ class: 'text-3xl font-bold' }, ['${n.title}']),`, ` ui.form({ class: 'flex gap-2', on: { submit: ui.send(Add, { title: ui.dom.form('title') }) } }, [`, ` ui.label({ for: '${n.id}-title', class: 'sr-only' }, ['Title']),`, ' ui.input({', ` id: '${n.id}-title',`, ` name: 'title',`, ' required: true,', ' minlength: 2,', ' maxlength: 80,', ' value: ctx.draft,', ` 'aria-invalid': op.neq(ctx.fields.title, null),`, ` 'aria-describedby': '${n.id}-title-error',`, ` class: 'flex-1 rounded border px-3 py-2',`, ' on: { input: ui.send(Draft, { text: ui.dom.value }) },', ' }),', ` ui.button({ type: 'submit', class: 'rounded bg-indigo-600 px-4 py-2 text-white' }, ['Add']),`, ' ]),', ` ui.p({ id: '${n.id}-title-error', class: 'text-sm text-rose-600' }, [ctx.fields.title]),`, ` ui.if(op.neq(ctx.error, null), [ui.p({ role: 'alert', class: 'text-rose-600' }, [ctx.error])], []),`, ` when(['adding'], [ui.p({ class: 'opacity-50', 'aria-busy': 'true' }, ['Adding ', ctx.draft, '…'])]),`, w.filter &&
|
|
184
|
+
` ui.nav(
|
|
185
|
+
{ class: 'flex gap-2', 'aria-label': 'Show' },
|
|
186
|
+
shows.map((s) =>
|
|
187
|
+
ui.button(
|
|
188
|
+
{
|
|
189
|
+
type: 'button',
|
|
190
|
+
'aria-pressed': op.eq(ctx.show, s.value),
|
|
191
|
+
class: 'rounded-full border px-3 py-1 aria-pressed:bg-indigo-600 aria-pressed:text-white',
|
|
192
|
+
on: { click: ui.send(SetShow, { show: s.value }) },
|
|
193
|
+
},
|
|
194
|
+
[s.label],
|
|
195
|
+
),
|
|
196
|
+
),
|
|
197
|
+
),`, ' ui.query(', ` ${n.list},`, ' {},', ' {', ' ready: (items) =>', ` ${ready},`, ` pending: ui.p({}, ['Loading…']),`, ` failed: { ${w.auth ? "Unauthorized: () => ui.p({ role: 'alert' }, ['Signed out']), " : ''}Unexpected: () => ui.p({ role: 'alert' }, ['Unavailable']) },`, ' },', ' ),', ' ]),', '})', w.detail &&
|
|
198
|
+
`
|
|
199
|
+
export const ${n.Detail} = ui.view({
|
|
200
|
+
route: ${n.detailRoute},
|
|
201
|
+
render: ({ params }) =>
|
|
202
|
+
ui.main({ class: 'mx-auto max-w-xl space-y-4 px-4 py-12' }, [
|
|
203
|
+
ui.query(
|
|
204
|
+
${n.get},
|
|
205
|
+
{ id: params.id },
|
|
206
|
+
{
|
|
207
|
+
ready: (item) =>
|
|
208
|
+
ui.article({}, [
|
|
209
|
+
ui.h1({ class: 'text-3xl font-bold' }, [item.title]),${w.toggle
|
|
210
|
+
? `
|
|
211
|
+
ui.p({}, ['Status: ', ui.if(op.eq(item.done, true), ['done'], ['open'])]),`
|
|
212
|
+
: ''}
|
|
213
|
+
]),
|
|
214
|
+
pending: null,
|
|
215
|
+
failed: {
|
|
216
|
+
NotFound: () => ui.p({ role: 'alert' }, ['Not found']),
|
|
217
|
+
Unexpected: () => ui.p({ role: 'alert' }, ['Unavailable']),
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
),
|
|
221
|
+
ui.a({ href: ui.link(${listRoute ?? 'home'}, null), class: 'underline' }, ['Back']),
|
|
222
|
+
]),
|
|
223
|
+
})`, `
|
|
224
|
+
export const typesDraft = contract(${n.machine}, {
|
|
225
|
+
given: { state: 'idle' },
|
|
226
|
+
when: [{ send: Draft, payload: { text: 'Ship' } }],
|
|
227
|
+
expect: { state: 'idle', changes: { draft: 'Ship' } },
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
export const adds = contract(${n.machine}, {
|
|
231
|
+
given: { state: 'idle' },
|
|
232
|
+
when: [
|
|
233
|
+
{ send: Add, payload: { title: 'Ship' } },
|
|
234
|
+
{ done: ${n.add}, result: { id: 'x1', title: 'Ship'${w.toggle ? ', done: false' : ''} } },
|
|
235
|
+
],
|
|
236
|
+
expect: { state: 'idle', effects: [{ effect: ${n.add}, input: { title: 'Ship' } }] },
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
export const rejectsDuplicate = contract(${n.machine}, {
|
|
240
|
+
given: { state: 'adding' },
|
|
241
|
+
when: [{ failed: ${n.add}, error: 'Duplicate', data: { title: 'Ship' } }],
|
|
242
|
+
expect: { state: 'idle', changes: { error: DUPLICATE } },
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
export const rejectsInvalid = contract(${n.machine}, {
|
|
246
|
+
given: { state: 'adding' },
|
|
247
|
+
when: [
|
|
248
|
+
{
|
|
249
|
+
failed: ${n.add},
|
|
250
|
+
error: 'Invalid',
|
|
251
|
+
data: { message: 'title: Use at least 2 characters', fields: { title: 'Use at least 2 characters' } },
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
expect: { state: 'idle', changes: { fields: { title: 'Use at least 2 characters' } } },
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
export const addFails = contract(${n.machine}, {
|
|
258
|
+
given: { state: 'adding' },
|
|
259
|
+
when: [{ failed: ${n.add}, error: 'Unexpected', data: { message: 'offline' } }],
|
|
260
|
+
expect: { state: 'idle', changes: { error: 'offline' } },
|
|
261
|
+
})`, w.toggle && actionContracts('Toggle', 'toggling', n.toggle, 'toggle', itemResult), w.remove && actionContracts('Remove', 'removing', n.remove, 'remove', `{ id: 'x1' }`), w.filter &&
|
|
262
|
+
`
|
|
263
|
+
export const filters = contract(${n.machine}, {
|
|
264
|
+
given: { state: 'idle' },
|
|
265
|
+
when: [{ send: SetShow, payload: { show: 'done' } }],
|
|
266
|
+
expect: { state: 'idle', changes: { show: 'done' } },
|
|
267
|
+
})`, `
|
|
268
|
+
export const ${n.feature} = feature({
|
|
269
|
+
id: '${n.id}',
|
|
270
|
+
intent: { summary: '${n.title}: add one with a title; titles are unique, case-insensitive.' },
|
|
271
|
+
declarations: {
|
|
272
|
+
${[
|
|
273
|
+
...events(w),
|
|
274
|
+
n.tag,
|
|
275
|
+
n.list,
|
|
276
|
+
...(w.detail ? [n.get] : []),
|
|
277
|
+
n.add,
|
|
278
|
+
...(w.toggle ? [n.toggle] : []),
|
|
279
|
+
...(w.remove ? [n.remove] : []),
|
|
280
|
+
...(w.filter ? ['visible', 'isEmpty'] : []),
|
|
281
|
+
n.machine,
|
|
282
|
+
n.View,
|
|
283
|
+
...(w.detail ? [n.Detail] : []),
|
|
284
|
+
...contracts,
|
|
285
|
+
].join(',\n ')},
|
|
286
|
+
},
|
|
287
|
+
})`)}\n`;
|
|
288
|
+
}
|
|
289
|
+
export function server(n, w) {
|
|
290
|
+
const find = w.detail || w.toggle;
|
|
291
|
+
const imports = [
|
|
292
|
+
n.add,
|
|
293
|
+
n.list,
|
|
294
|
+
...(w.detail ? [n.get] : []),
|
|
295
|
+
...(w.toggle ? [n.toggle] : []),
|
|
296
|
+
...(w.remove ? [n.remove] : []),
|
|
297
|
+
];
|
|
298
|
+
const Row = `{ id: string; title: string${w.toggle ? '; done: boolean' : ''} }`;
|
|
299
|
+
const ctx = (rest) => w.auth ? `{ ${[...rest.split(', ').filter(Boolean), 'session'].join(', ')} }` : `{ ${rest} }`;
|
|
300
|
+
const take = w.auth ? ' const items = itemsOf(session)\n' : '';
|
|
301
|
+
const scoped = (body) => (w.auth ? `{\n${take}${body}\n }` : `{\n${body}\n }`);
|
|
302
|
+
return `${lines(`import type { Implement } from '@hozu/data'`, `import { ${imports.sort().join(', ')} } from './model.ts'`, '', w.auth
|
|
303
|
+
? `export function ${n.resolvers}<Env>(implement: Implement<{ user: string }, Env>) {`
|
|
304
|
+
: `export function ${n.resolvers}<Session, Env>(implement: Implement<Session, Env>) {`, w.auth
|
|
305
|
+
? ` const store = new Map<string, ${Row}[]>()
|
|
306
|
+
const itemsOf = (session: { user: string } | null) => {
|
|
307
|
+
if (!session) return []
|
|
308
|
+
const list = store.get(session.user) ?? []
|
|
309
|
+
store.set(session.user, list)
|
|
310
|
+
return list
|
|
311
|
+
}`
|
|
312
|
+
: ` const items: ${Row}[] = []`, ' let seq = 0', find && ` const find = (items: ${Row}[], id: string) => items.find((item) => item.id === id)`, ' return [', w.auth
|
|
313
|
+
? ` implement(${n.list}, (_, { session, fail }) =>
|
|
314
|
+
session ? itemsOf(session).map((item) => ({ ...item })) : fail('Unauthorized', {}),
|
|
315
|
+
),`
|
|
316
|
+
: ` implement(${n.list}, () => items.map((item) => ({ ...item }))),`, w.detail &&
|
|
317
|
+
` implement(${n.get}, ({ id }, ${ctx('fail')}) => ${scoped(` const item = find(items, id)
|
|
318
|
+
return item ? { ...item } : fail('NotFound', { id })`)}),`, ` implement(${n.add}, ({ title }, ${ctx('fail')}) => ${scoped(`${w.auth
|
|
319
|
+
? ` if (!session) return fail('Invalid', { message: 'Signed out', fields: { title: 'Sign in first' } })\n`
|
|
320
|
+
: ''} const clean = title.trim()
|
|
321
|
+
if (items.some((item) => item.title.toLowerCase() === clean.toLowerCase()))
|
|
322
|
+
return fail('Duplicate', { title: clean })
|
|
323
|
+
const item = { id: \`${n.one.charAt(0)}\${++seq}\`, title: clean${w.toggle ? ', done: false' : ''} }
|
|
324
|
+
items.unshift(item)
|
|
325
|
+
return { ...item }`)}),`, w.toggle &&
|
|
326
|
+
` implement(${n.toggle}, ({ id }, ${ctx('fail')}) => ${scoped(` const item = find(items, id)
|
|
327
|
+
if (!item) return fail('NotFound', { id })
|
|
328
|
+
item.done = !item.done
|
|
329
|
+
return { ...item }`)}),`, w.remove &&
|
|
330
|
+
` implement(${n.remove}, ({ id }, ${ctx('fail')}) => ${scoped(` const at = items.findIndex((item) => item.id === id)
|
|
331
|
+
if (at < 0) return fail('NotFound', { id })
|
|
332
|
+
items.splice(at, 1)
|
|
333
|
+
return { id }`)}),`, ' ]', '}')}\n`;
|
|
334
|
+
}
|
|
335
|
+
export function accountModel(homeRoute) {
|
|
336
|
+
return `import { event, invoke, machine, mutation, on, op, query, ui } from '@hozu/core'
|
|
337
|
+
import { z } from 'zod'
|
|
338
|
+
import { ${[homeRoute, 'login'].sort().join(', ')} } from '../../routes.ts'
|
|
339
|
+
|
|
340
|
+
export const Session = z.object({ user: z.string() })
|
|
341
|
+
export const Name = z.object({ name: z.string().regex(/^\\s*[A-Za-z]{2,20}\\s*$/, 'Use 2–20 letters') })
|
|
342
|
+
|
|
343
|
+
export const SignIn = event({ payload: z.object({ name: z.string() }) })
|
|
344
|
+
export const SignOut = event({ payload: z.object({}) })
|
|
345
|
+
|
|
346
|
+
export const me = query({
|
|
347
|
+
input: z.object({}),
|
|
348
|
+
output: z.object({ name: z.string() }),
|
|
349
|
+
errors: { Unauthorized: z.object({}) },
|
|
350
|
+
scope: 'user',
|
|
351
|
+
freshness: 'static',
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
export const signIn = mutation({ input: Name, output: z.object({}) })
|
|
355
|
+
export const signOut = mutation({ input: z.object({}), output: z.object({}) })
|
|
356
|
+
|
|
357
|
+
export const accountMachine = machine({
|
|
358
|
+
context: z.object({
|
|
359
|
+
draft: z.string(),
|
|
360
|
+
error: z.string().nullable(),
|
|
361
|
+
fields: z.object({ name: z.string().nullable() }),
|
|
362
|
+
}),
|
|
363
|
+
initialContext: { draft: '', error: null, fields: { name: null } },
|
|
364
|
+
initial: 'idle',
|
|
365
|
+
states: ({ ctx }) => ({
|
|
366
|
+
idle: {
|
|
367
|
+
on: [
|
|
368
|
+
on(SignIn, {
|
|
369
|
+
target: 'signingIn',
|
|
370
|
+
assign: (e) => [op.set(ctx.draft, e.name), op.set(ctx.error, null), op.set(ctx.fields, { name: null })],
|
|
371
|
+
}),
|
|
372
|
+
on(SignOut, { target: 'signingOut' }),
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
signingIn: {
|
|
376
|
+
ignore: [SignIn, SignOut],
|
|
377
|
+
invoke: invoke(signIn, {
|
|
378
|
+
input: { name: ctx.draft },
|
|
379
|
+
done: [{ target: 'idle', navigate: () => ui.link(${homeRoute}, null) }],
|
|
380
|
+
failed: {
|
|
381
|
+
Invalid: [{ target: 'idle', assign: (e) => [op.set(ctx.fields, e.fields)] }],
|
|
382
|
+
Unexpected: [{ target: 'idle', assign: (e) => [op.set(ctx.error, e.message)] }],
|
|
383
|
+
},
|
|
384
|
+
}),
|
|
385
|
+
},
|
|
386
|
+
signingOut: {
|
|
387
|
+
ignore: [SignIn, SignOut],
|
|
388
|
+
invoke: invoke(signOut, {
|
|
389
|
+
input: {},
|
|
390
|
+
done: [{ target: 'idle', navigate: () => ui.link(login, null) }],
|
|
391
|
+
failed: { Unexpected: [{ target: 'idle', assign: (e) => [op.set(ctx.error, e.message)] }] },
|
|
392
|
+
}),
|
|
393
|
+
},
|
|
394
|
+
}),
|
|
395
|
+
})
|
|
396
|
+
`;
|
|
397
|
+
}
|
|
398
|
+
export function accountViews(homePath) {
|
|
399
|
+
return `import { contract, feature, op, ui } from '@hozu/core'
|
|
400
|
+
import { login } from '../../routes.ts'
|
|
401
|
+
import { accountMachine, me, SignIn, SignOut, signIn, signOut } from './model.ts'
|
|
402
|
+
|
|
403
|
+
export const Login = ui.view({
|
|
404
|
+
machine: accountMachine,
|
|
405
|
+
route: login,
|
|
406
|
+
render: ({ ctx }) =>
|
|
407
|
+
ui.main({ class: 'mx-auto max-w-sm space-y-6 px-4 py-16' }, [
|
|
408
|
+
ui.h1({ class: 'text-3xl font-bold' }, ['Sign in']),
|
|
409
|
+
ui.form({ class: 'space-y-3', on: { submit: ui.send(SignIn, { name: ui.dom.form('name') }) } }, [
|
|
410
|
+
ui.label({ for: 'name', class: 'block text-sm font-medium' }, ['Name']),
|
|
411
|
+
ui.input({
|
|
412
|
+
id: 'name',
|
|
413
|
+
name: 'name',
|
|
414
|
+
required: true,
|
|
415
|
+
minlength: 2,
|
|
416
|
+
maxlength: 20,
|
|
417
|
+
autocomplete: 'username',
|
|
418
|
+
'aria-invalid': op.neq(ctx.fields.name, null),
|
|
419
|
+
'aria-describedby': 'name-error',
|
|
420
|
+
class: 'w-full rounded border px-3 py-2',
|
|
421
|
+
}),
|
|
422
|
+
ui.p({ id: 'name-error', class: 'text-sm text-rose-600' }, [ctx.fields.name]),
|
|
423
|
+
ui.button({ type: 'submit', class: 'w-full rounded bg-indigo-600 px-4 py-2 text-white' }, ['Sign in']),
|
|
424
|
+
]),
|
|
425
|
+
ui.if(op.neq(ctx.error, null), [ui.p({ role: 'alert', class: 'text-rose-600' }, [ctx.error])], []),
|
|
426
|
+
]),
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
export const AccountBar = ui.view({
|
|
430
|
+
machine: accountMachine,
|
|
431
|
+
render: () =>
|
|
432
|
+
ui.header({ class: 'mx-auto flex max-w-xl items-center justify-between px-4 pt-8 text-sm text-slate-600' }, [
|
|
433
|
+
ui.query(
|
|
434
|
+
me,
|
|
435
|
+
{},
|
|
436
|
+
{
|
|
437
|
+
ready: (user) => ui.p({}, ['Signed in as ', user.name]),
|
|
438
|
+
pending: null,
|
|
439
|
+
failed: { Unauthorized: () => ui.p({}, ['Signed out']), Unexpected: () => ui.p({}, ['']) },
|
|
440
|
+
},
|
|
441
|
+
),
|
|
442
|
+
ui.form({ on: { submit: ui.send(SignOut, {}) } }, [
|
|
443
|
+
ui.button({ type: 'submit', class: 'underline' }, ['Sign out']),
|
|
444
|
+
]),
|
|
445
|
+
]),
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
export const signsIn = contract(accountMachine, {
|
|
449
|
+
given: { state: 'idle' },
|
|
450
|
+
when: [
|
|
451
|
+
{ send: SignIn, payload: { name: 'ada' } },
|
|
452
|
+
{ done: signIn, result: {} },
|
|
453
|
+
],
|
|
454
|
+
expect: {
|
|
455
|
+
state: 'idle',
|
|
456
|
+
changes: { draft: 'ada' },
|
|
457
|
+
effects: [{ effect: signIn, input: { name: 'ada' } }, { navigate: '${homePath}' }],
|
|
458
|
+
},
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
export const rejectsName = contract(accountMachine, {
|
|
462
|
+
given: { state: 'signingIn' },
|
|
463
|
+
when: [
|
|
464
|
+
{
|
|
465
|
+
failed: signIn,
|
|
466
|
+
error: 'Invalid',
|
|
467
|
+
data: { message: 'name: Use 2–20 letters', fields: { name: 'Use 2–20 letters' } },
|
|
468
|
+
},
|
|
469
|
+
],
|
|
470
|
+
expect: { state: 'idle', changes: { fields: { name: 'Use 2–20 letters' } } },
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
export const signInFails = contract(accountMachine, {
|
|
474
|
+
given: { state: 'signingIn' },
|
|
475
|
+
when: [{ failed: signIn, error: 'Unexpected', data: { message: 'offline' } }],
|
|
476
|
+
expect: { state: 'idle', changes: { error: 'offline' } },
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
export const signsOut = contract(accountMachine, {
|
|
480
|
+
given: { state: 'idle' },
|
|
481
|
+
when: [
|
|
482
|
+
{ send: SignOut, payload: {} },
|
|
483
|
+
{ done: signOut, result: {} },
|
|
484
|
+
],
|
|
485
|
+
expect: { state: 'idle', effects: [{ effect: signOut, input: {} }, { navigate: '/login' }] },
|
|
486
|
+
})
|
|
487
|
+
|
|
488
|
+
export const signOutFails = contract(accountMachine, {
|
|
489
|
+
given: { state: 'signingOut' },
|
|
490
|
+
when: [{ failed: signOut, error: 'Unexpected', data: { message: 'offline' } }],
|
|
491
|
+
expect: { state: 'idle', changes: { error: 'offline' } },
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
export const account = feature({
|
|
495
|
+
id: 'account',
|
|
496
|
+
intent: { summary: 'Sign in with a name, sign out; the session identifies the user' },
|
|
497
|
+
exports: [me],
|
|
498
|
+
declarations: {
|
|
499
|
+
SignIn,
|
|
500
|
+
SignOut,
|
|
501
|
+
me,
|
|
502
|
+
signIn,
|
|
503
|
+
signOut,
|
|
504
|
+
accountMachine,
|
|
505
|
+
Login,
|
|
506
|
+
AccountBar,
|
|
507
|
+
signsIn,
|
|
508
|
+
rejectsName,
|
|
509
|
+
signInFails,
|
|
510
|
+
signsOut,
|
|
511
|
+
signOutFails,
|
|
512
|
+
},
|
|
513
|
+
})
|
|
514
|
+
`;
|
|
515
|
+
}
|
|
516
|
+
export const accountServer = () => `import type { Implement } from '@hozu/data'
|
|
517
|
+
import { me, signIn, signOut } from './model.ts'
|
|
518
|
+
|
|
519
|
+
export function accountResolvers<Env>(implement: Implement<{ user: string }, Env>) {
|
|
520
|
+
return [
|
|
521
|
+
implement(me, (_, { session, fail }) => (session ? { name: session.user } : fail('Unauthorized', {}))),
|
|
522
|
+
implement(signIn, ({ name }, { setSession }) => {
|
|
523
|
+
setSession({ user: name.trim().toLowerCase() })
|
|
524
|
+
return {}
|
|
525
|
+
}),
|
|
526
|
+
implement(signOut, (_, { setSession }) => {
|
|
527
|
+
setSession(null)
|
|
528
|
+
return {}
|
|
529
|
+
}),
|
|
530
|
+
]
|
|
531
|
+
}
|
|
532
|
+
`;
|
|
533
|
+
//# sourceMappingURL=scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/commands/scaffold.ts"],"names":[],"mappings":"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"}
|