@hozu/cli 0.2.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.
@@ -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":"AAAA,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAA;AAI9E,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAEjE,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC5E,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAA;IACnD,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;IACrB,OAAO;QACL,EAAE,EAAE,IAAI;QACR,GAAG;QACH,IAAI;QACJ,IAAI;QACJ,GAAG,EAAE,GAAG,IAAI,KAAK;QACjB,GAAG,EAAE,MAAM,IAAI,EAAE;QACjB,IAAI,EAAE,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE;QACxB,GAAG,EAAE,MAAM,IAAI,EAAE;QACjB,GAAG,EAAE,MAAM,IAAI,EAAE;QACjB,MAAM,EAAE,SAAS,IAAI,EAAE;QACvB,MAAM,EAAE,SAAS,IAAI,EAAE;QACvB,GAAG,EAAE,GAAG,IAAI,KAAK;QACjB,OAAO,EAAE,GAAG,IAAI,SAAS;QACzB,IAAI,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO;QACzB,MAAM,EAAE,GAAG,IAAI,QAAQ;QACvB,WAAW,EAAE,GAAG,GAAG,MAAM;QACzB,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,GAAG,IAAI,WAAW;QAC7B,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC;KACjB,CAAA;AACH,CAAC;AAID,MAAM,KAAK,GAAG,CAAC,GAAG,KAAyB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAE3F,MAAM,MAAM,GAAG,CAAC,CAAO,EAAE,EAAE,CACzB,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;AAErG,MAAM,UAAU,KAAK,CAAC,CAAQ,EAAE,CAAO;IACrC,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAA;IAC9C,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAA;IACjC,MAAM,MAAM,GAAG,YAAY,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAA;IACnD,MAAM,IAAI,GAAG;QACX,OAAO;QACP,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3B,QAAQ;QACR,SAAS;QACT,UAAU;QACV,IAAI;QACJ,IAAI;QACJ,OAAO;QACP,KAAK;KACN,CAAA;IACD,MAAM,WAAW,GAAG,CAAC,KAAa,EAAE,MAAc,EAAE,EAAE,CAAC,OAAO,KAAK;QAC7D,MAAM;uBACS,MAAM;;;;;;;;OAQtB,CAAA;IACL,OAAO,GAAG,KAAK,CACb,YAAY,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,EACjD,yBAAyB,EACzB,EAAE,EACF,gBAAgB,CAAC,CAAC,IAAI,kDAAkD,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,KAAK,EAClH,KAAK,IAAI,gBAAgB,CAAC,CAAC,GAAG,iCAAiC,EAC/D,gBAAgB,CAAC,CAAC,GAAG,eAAe,EACpC,+FAA+F,EAC/F,IAAI,EACJ,CAAC,CAAC,MAAM,IAAI,qDAAqD,EACjE,EAAE,EACF,yEAAyE,EACzE,wEAAwE,EACxE,CAAC,CAAC,MAAM,IAAI,0CAA0C,CAAC,CAAC,GAAG,KAAK,EAChE,CAAC,CAAC,MAAM,IAAI,0CAA0C,CAAC,CAAC,GAAG,KAAK,EAChE,CAAC,CAAC,MAAM,IAAI,qEAAqE,EACjF,EAAE,EACF,gBAAgB,CAAC,CAAC,GAAG,yBAAyB,EAC9C,EAAE,EACF,gBAAgB,CAAC,CAAC,IAAI,YAAY,EAClC,wBAAwB,EACxB,qBAAqB,CAAC,CAAC,IAAI,IAAI,EAC/B,CAAC,CAAC,IAAI,IAAI,2CAA2C,EACrD,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,IAAI,EAC3C,wBAAwB,EACxB,kBAAkB,CAAC,CAAC,GAAG,MAAM,EAC7B,IAAI,EACJ,CAAC,CAAC,MAAM;QACN;eACS,CAAC,CAAC,GAAG;WACT,CAAC,CAAC,GAAG;YACJ,CAAC,CAAC,IAAI;wBACM,CAAC,CAAC,GAAG;YACjB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;;iBAErB,CAAC,CAAC,GAAG;GACnB,EACC,EAAE,EACF,gBAAgB,CAAC,CAAC,GAAG,eAAe,EACpC,YAAY,CAAC,CAAC,GAAG,GAAG,EACpB,aAAa,CAAC,CAAC,IAAI,GAAG,EACtB,2DAA2D,EAC3D,yBAAyB,CAAC,CAAC,GAAG,MAAM,EACpC,IAAI,EACJ,CAAC,CAAC,MAAM;QACN;eACS,CAAC,CAAC,MAAM;WACZ,CAAC,CAAC,GAAG;YACJ,CAAC,CAAC,IAAI;wBACM,CAAC,CAAC,GAAG;wBACL,CAAC,CAAC,GAAG;GAC1B,EACC,CAAC,CAAC,MAAM;QACN;eACS,CAAC,CAAC,MAAM;WACZ,CAAC,CAAC,GAAG;YACJ,CAAC,CAAC,GAAG;wBACO,CAAC,CAAC,GAAG;wBACL,CAAC,CAAC,GAAG;GAC1B,EACC,CAAC,CAAC,MAAM;QACN;4CACsC,CAAC,CAAC,IAAI;;;;oBAI9B,CAAC,CAAC,IAAI;;;;;;;;GAQvB,EACC,EAAE,EACF,kCAAkC,CAAC,CAAC,GAAG,kBAAkB,EACzD,IAAI,IAAI,kCAAkC,CAAC,CAAC,GAAG,oBAAoB,EACnE,EAAE,EACF,gBAAgB,CAAC,CAAC,OAAO,cAAc,EACvC,uBAAuB,EACvB,wBAAwB,EACxB,mCAAmC,EACnC,yDAAyD,EACzD,IAAI,IAAI,yBAAyB,EACjC,CAAC,CAAC,MAAM,IAAI,iBAAiB,EAC7B,OAAO,EACP,sEAAsE,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,KAAK,EACvI,oBAAoB,EACpB,2BAA2B,EAC3B,aAAa,EACb,aAAa,EACb,oFAAoF,EACpF,mBAAmB,EACnB,6BAA6B,EAC7B,4BAA4B,EAC5B,yCAAyC,EACzC,sCAAsC,EACtC,kDAAkD,EAClD,cAAc,EACd,aAAa,EACb,CAAC,CAAC,MAAM;QACN,iHAAiH,EACnH,CAAC,CAAC,MAAM;QACN,iHAAiH,EACnH,CAAC,CAAC,MAAM,IAAI,qFAAqF,EACjG,UAAU,EACV,QAAQ,EACR,eAAe,EACf,SAAS,MAAM,EAAE,EACjB,wBAAwB,CAAC,CAAC,GAAG,KAAK,EAClC,sCAAsC,EACtC,4EAA4E,EAC5E,mBAAmB,EACnB,0FAA0F,EAC1F,yFAAyF,EACzF,4FAA4F,EAC5F,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,CAAC,CAAC,MAAM,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,EAC7C,CAAC,CAAC,MAAM,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,EAC7C,OAAO,EACP,IAAI,CACL,IAAI,CAAA;AACP,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,CAAQ,EAAE,CAAO,EAAE,SAAwB;IAC/D,MAAM,UAAU,GAAG;QACjB,KAAK;QACL,CAAC,CAAC,GAAG;QACL,WAAW;QACX,OAAO;QACP,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,GAAG;QACL,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5B,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACvD,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;IAChE,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAE,KAAa,EAAE,EAAE,CAChD,oDAAoD,KAAK;;mFAEsB,KAAK;uBACjE,CAAA;IACrB,MAAM,YAAY,GAAG;QACnB,CAAC,CAAC,MAAM;YACN,CAAC,CAAC,wBAAwB,CAAC,CAAC,WAAW,gEAAgE;YACvG,CAAC,CAAC,4CAA4C;QAChD,GAAG,CAAC,CAAC,CAAC,MAAM;YACV,CAAC,CAAC;gBACE,mGAAmG;gBACnG,QAAQ,CAAC,QAAQ,EAAE,6DAA6D,CAAC;aAClF;YACH,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACtD,CAAA;IACD,MAAM,IAAI,GAAG;0BACW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC,OAAO;;sBAE7D,YAAY,CAAC,IAAI,CAAC,yBAAyB,CAAC;;;iBAGjD,CAAA;IACf,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM;QACpB,CAAC,CAAC;;;;gBAIU,IAAI;;cAEN;QACV,CAAC,CAAC,IAAI,CAAA;IACR,MAAM,eAAe,GAAG,CAAC,KAAa,EAAE,KAAa,EAAE,MAAc,EAAE,IAAY,EAAE,MAAc,EAAE,EAAE,CAAC;eAC3F,IAAI,gBAAgB,CAAC,CAAC,OAAO;;;cAG9B,KAAK;cACL,MAAM,aAAa,MAAM;;4EAEqC,MAAM;;;eAGnE,IAAI,sBAAsB,CAAC,CAAC,OAAO;qBAC7B,KAAK;qBACL,MAAM;;;;eAIZ,IAAI,oBAAoB,CAAC,CAAC,OAAO;qBAC3B,KAAK;qBACL,MAAM;;GAExB,CAAA;IACD,MAAM,UAAU,GAAG,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,IAAI,CAAA;IACjF,MAAM,SAAS,GAAG;QAChB,YAAY;QACZ,MAAM;QACN,kBAAkB;QAClB,gBAAgB;QAChB,UAAU;QACV,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACjC,CAAA;IACD,OAAO,GAAG,KAAK,CACb,wDAAwD,EACxD,CAAC,CAAC,MAAM,IAAI,YAAY,CAAC,SAAS,IAAI,MAAM,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,2BAA2B,EACzG,eAAe,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,wBAAwB,EAC/D,EAAE,EACF,CAAC,CAAC,MAAM;QACN;;;;;CAKL,EACG,gBAAgB,CAAC,CAAC,IAAI,cAAc,EACpC,cAAc,CAAC,CAAC,OAAO,GAAG,EAC1B,8BAA8B,EAC9B,mEAAmE,EACnE,kDAAkD,CAAC,CAAC,KAAK,MAAM,EAC/D,yGAAyG,EACzG,4BAA4B,CAAC,CAAC,EAAE,0CAA0C,EAC1E,oBAAoB,EACpB,kBAAkB,CAAC,CAAC,EAAE,UAAU,EAChC,0BAA0B,EAC1B,2BAA2B,EAC3B,yBAAyB,EACzB,0BAA0B,EAC1B,6BAA6B,EAC7B,2DAA2D,EAC3D,kCAAkC,CAAC,CAAC,EAAE,gBAAgB,EACtD,qDAAqD,EACrD,kEAAkE,EAClE,aAAa,EACb,sGAAsG,EACtG,WAAW,EACX,qBAAqB,CAAC,CAAC,EAAE,uEAAuE,EAChG,2GAA2G,EAC3G,4GAA4G,EAC5G,CAAC,CAAC,MAAM;QACN;;;;;;;;;;;;;SAaG,EACL,iBAAiB,EACjB,WAAW,CAAC,CAAC,IAAI,GAAG,EACpB,aAAa,EACb,WAAW,EACX,6BAA6B,EAC7B,eAAe,KAAK,GAAG,EACvB,4CAA4C,EAC5C,uBAAuB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,+DAA+D,CAAC,CAAC,CAAC,EAAE,+DAA+D,EACnK,YAAY,EACZ,UAAU,EACV,SAAS,EACT,IAAI,EACJ,CAAC,CAAC,MAAM;QACN;eACS,CAAC,CAAC,MAAM;WACZ,CAAC,CAAC,WAAW;;;;UAId,CAAC,CAAC,GAAG;;;;;qEAMC,CAAC,CAAC,MAAM;YACN,CAAC,CAAC;yFACqE;YACvE,CAAC,CAAC,EACN;;;;;;;;;6BASe,SAAS,IAAI,MAAM;;GAE7C,EACC;qCACiC,CAAC,CAAC,OAAO;;;;;;+BAMf,CAAC,CAAC,OAAO;;;;cAI1B,CAAC,CAAC,GAAG,sCAAsC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE;;iDAEvC,CAAC,CAAC,GAAG;;;2CAGX,CAAC,CAAC,OAAO;;qBAE/B,CAAC,CAAC,GAAG;;;;yCAIe,CAAC,CAAC,OAAO;;;;gBAIlC,CAAC,CAAC,GAAG;;;;;;;;mCAQc,CAAC,CAAC,OAAO;;qBAEvB,CAAC,CAAC,GAAG;;GAEvB,EACC,CAAC,CAAC,MAAM,IAAI,eAAe,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,EACjF,CAAC,CAAC,MAAM,IAAI,eAAe,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,cAAc,CAAC,EACrF,CAAC,CAAC,MAAM;QACN;kCAC4B,CAAC,CAAC,OAAO;;;;GAIxC,EACC;eACW,CAAC,CAAC,OAAO;SACf,CAAC,CAAC,EAAE;wBACW,CAAC,CAAC,KAAK;;MAEzB;QACA,GAAG,MAAM,CAAC,CAAC,CAAC;QACZ,CAAC,CAAC,GAAG;QACL,CAAC,CAAC,IAAI;QACN,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5B,CAAC,CAAC,GAAG;QACL,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/B,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/B,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,IAAI;QACN,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/B,GAAG,SAAS;KACb,CAAC,IAAI,CAAC,SAAS,CAAC;;GAElB,CACA,IAAI,CAAA;AACP,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,CAAQ,EAAE,CAAO;IACtC,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAA;IACjC,MAAM,OAAO,GAAG;QACd,CAAC,CAAC,GAAG;QACL,CAAC,CAAC,IAAI;QACN,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5B,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/B,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAChC,CAAA;IACD,MAAM,GAAG,GAAG,8BAA8B,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,IAAI,CAAA;IAC/E,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE,CAC3B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAA;IAC/F,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,wCAAwC,CAAC,CAAC,CAAC,EAAE,CAAA;IACnE,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC,MAAM,IAAI,SAAS,CAAC,CAAA;IAC5F,OAAO,GAAG,KAAK,CACb,6CAA6C,EAC7C,YAAY,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAC3D,EAAE,EACF,CAAC,CAAC,IAAI;QACJ,CAAC,CAAC,mBAAmB,CAAC,CAAC,SAAS,sDAAsD;QACtF,CAAC,CAAC,mBAAmB,CAAC,CAAC,SAAS,sDAAsD,EACxF,CAAC,CAAC,IAAI;QACJ,CAAC,CAAC,mCAAmC,GAAG;;;;;;IAM1C;QACE,CAAC,CAAC,kBAAkB,GAAG,SAAS,EAClC,eAAe,EACf,IAAI,IAAI,0BAA0B,GAAG,yDAAyD,EAC9F,YAAY,EACZ,CAAC,CAAC,IAAI;QACJ,CAAC,CAAC,iBAAiB,CAAC,CAAC,IAAI;;OAExB;QACD,CAAC,CAAC,iBAAiB,CAAC,CAAC,IAAI,8CAA8C,EACzE,CAAC,CAAC,MAAM;QACN,iBAAiB,CAAC,CAAC,GAAG,cAAc,GAAG,CAAC,MAAM,CAAC,QAAQ,MAAM,CAAC;2DACT,CAAC,IAAI,EAC5D,iBAAiB,CAAC,CAAC,GAAG,iBAAiB,GAAG,CAAC,MAAM,CAAC,QAAQ,MAAM,CAAC,GAC/D,CAAC,CAAC,IAAI;QACJ,CAAC,CAAC,6GAA6G;QAC/G,CAAC,CAAC,EACN;;;6BAGyB,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE;;yBAE9E,CAAC,IAAI,EAC1B,CAAC,CAAC,MAAM;QACN,iBAAiB,CAAC,CAAC,MAAM,cAAc,GAAG,CAAC,MAAM,CAAC,QAAQ,MAAM,CAAC;;;yBAG9C,CAAC,IAAI,EAC1B,CAAC,CAAC,MAAM;QACN,iBAAiB,CAAC,CAAC,MAAM,cAAc,GAAG,CAAC,MAAM,CAAC,QAAQ,MAAM,CAAC;;;oBAGnD,CAAC,IAAI,EACrB,KAAK,EACL,GAAG,CACJ,IAAI,CAAA;AACP,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,OAAO;;WAEE,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2DAyCU,SAAS;;;;;;;;;;;;;;;;;CAiBnE,CAAA;AACD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yEA0DgE,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDhF,CAAA;AACD,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;CAgBlC,CAAA"}
@@ -129,13 +129,98 @@ export interface RequestStep {
129
129
  alerts: string[];
130
130
  text: string | null;
131
131
  truncated: boolean;
132
+ elements: RequestElement[];
133
+ forms: RequestForm[];
134
+ }
135
+ export interface RequestElement {
136
+ selector: string;
137
+ tag: string;
138
+ attrs: Record<string, string>;
139
+ text: string;
140
+ }
141
+ export interface RequestForm {
142
+ action: string;
143
+ fields: Record<string, string>;
144
+ buttons: string[];
132
145
  }
133
146
  export interface RequestOutput {
134
147
  steps: RequestStep[];
135
148
  }
149
+ export interface MapRoute {
150
+ id: string;
151
+ path: string;
152
+ search: string[];
153
+ views: string[];
154
+ head: string | null;
155
+ at: string | null;
156
+ }
157
+ export interface MapState {
158
+ name: string;
159
+ initial: boolean;
160
+ final: boolean;
161
+ on: {
162
+ event: string;
163
+ targets: string[];
164
+ }[];
165
+ invoke: {
166
+ effect: string;
167
+ done: string[];
168
+ failed: Record<string, string[]>;
169
+ } | null;
170
+ ignore: string[];
171
+ after: {
172
+ ms: number;
173
+ target: string;
174
+ }[];
175
+ at: string | null;
176
+ }
177
+ export interface MapFeature {
178
+ id: string;
179
+ queries: {
180
+ name: string;
181
+ scope: string;
182
+ freshness: string;
183
+ errors: string[];
184
+ tags: string[];
185
+ at: string | null;
186
+ }[];
187
+ mutations: {
188
+ name: string;
189
+ errors: string[];
190
+ invalidates: string[];
191
+ at: string | null;
192
+ }[];
193
+ events: {
194
+ name: string;
195
+ fields: string[];
196
+ at: string | null;
197
+ }[];
198
+ fns: string[];
199
+ context: string[];
200
+ machineAt: string | null;
201
+ states: MapState[];
202
+ views: {
203
+ name: string;
204
+ machine: boolean;
205
+ route: string | null;
206
+ at: string | null;
207
+ }[];
208
+ contracts: number;
209
+ contractsAt: string | null;
210
+ }
211
+ export interface MapOutput {
212
+ routes: MapRoute[];
213
+ features: MapFeature[];
214
+ }
136
215
  export interface AddOutput {
137
216
  created: string[];
138
217
  edited: string[];
139
218
  manual: string[];
219
+ declarations: Record<string, string[]>;
220
+ texts: {
221
+ file: string;
222
+ line: number;
223
+ text: string;
224
+ }[];
140
225
  }
141
226
  //# sourceMappingURL=contract.d.ts.map