@hozu/cli 0.21.1 → 0.23.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.
Files changed (72) hide show
  1. package/bin/hozu.js +2 -2
  2. package/dist/commands/add.d.ts.map +1 -1
  3. package/dist/commands/add.js +10 -7
  4. package/dist/commands/add.js.map +1 -1
  5. package/dist/commands/app.d.ts +3 -0
  6. package/dist/commands/app.d.ts.map +1 -1
  7. package/dist/commands/app.js +23 -0
  8. package/dist/commands/app.js.map +1 -1
  9. package/dist/commands/browse-page.d.ts.map +1 -1
  10. package/dist/commands/browse-page.js +6 -1
  11. package/dist/commands/browse-page.js.map +1 -1
  12. package/dist/commands/browse-tab.d.ts +4 -2
  13. package/dist/commands/browse-tab.d.ts.map +1 -1
  14. package/dist/commands/browse-tab.js +36 -5
  15. package/dist/commands/browse-tab.js.map +1 -1
  16. package/dist/commands/browse-world.d.ts +3 -0
  17. package/dist/commands/browse-world.d.ts.map +1 -1
  18. package/dist/commands/browse-world.js +2 -1
  19. package/dist/commands/browse-world.js.map +1 -1
  20. package/dist/commands/browse.d.ts +10 -1
  21. package/dist/commands/browse.d.ts.map +1 -1
  22. package/dist/commands/browse.js +107 -13
  23. package/dist/commands/browse.js.map +1 -1
  24. package/dist/commands/check.d.ts.map +1 -1
  25. package/dist/commands/check.js +6 -1
  26. package/dist/commands/check.js.map +1 -1
  27. package/dist/commands/gen.d.ts +6 -0
  28. package/dist/commands/gen.d.ts.map +1 -0
  29. package/dist/commands/gen.js +56 -0
  30. package/dist/commands/gen.js.map +1 -0
  31. package/dist/commands/request.d.ts +6 -1
  32. package/dist/commands/request.d.ts.map +1 -1
  33. package/dist/commands/request.js +60 -6
  34. package/dist/commands/request.js.map +1 -1
  35. package/dist/commands/serve.d.ts.map +1 -1
  36. package/dist/commands/serve.js +11 -3
  37. package/dist/commands/serve.js.map +1 -1
  38. package/dist/contract.d.ts +50 -3
  39. package/dist/contract.d.ts.map +1 -1
  40. package/dist/gen/go.d.ts +10 -0
  41. package/dist/gen/go.d.ts.map +1 -0
  42. package/dist/gen/go.js +443 -0
  43. package/dist/gen/go.js.map +1 -0
  44. package/dist/main.d.ts +1 -0
  45. package/dist/main.d.ts.map +1 -1
  46. package/dist/main.js +28 -2
  47. package/dist/main.js.map +1 -1
  48. package/dist/migrate/steps.d.ts.map +1 -1
  49. package/dist/migrate/steps.js +24 -0
  50. package/dist/migrate/steps.js.map +1 -1
  51. package/dist/remote.d.ts +15 -0
  52. package/dist/remote.d.ts.map +1 -0
  53. package/dist/remote.js +66 -0
  54. package/dist/remote.js.map +1 -0
  55. package/package.json +8 -8
  56. package/schema/browse.schema.json +77 -5
  57. package/schema/check.schema.json +2 -1
  58. package/schema/gen.schema.json +78 -0
  59. package/schema/inspect.schema.json +20 -0
  60. package/schema/migrate.schema.json +2 -1
  61. package/schema/render.schema.json +2 -1
  62. package/schema/request.schema.json +35 -1
  63. package/skill/SKILL.md +2 -2
  64. package/skill/example/features/bookmarks/views.ts +1 -2
  65. package/skill/topics/data.md +77 -0
  66. package/skill/topics/diagnostics.md +1 -0
  67. package/skill/topics/http.md +2 -1
  68. package/skill/topics/machine.md +4 -5
  69. package/skill/topics/pages.md +1 -1
  70. package/skill/topics/recipes.md +23 -0
  71. package/skill/topics/testing.md +10 -4
  72. package/skill/topics/views.md +15 -3
package/dist/gen/go.js ADDED
@@ -0,0 +1,443 @@
1
+ import { canonicalStringify } from '@hozu/core/ir';
2
+ const pascal = (text) => {
3
+ const name = text
4
+ .replace(/(^|[^A-Za-z0-9])([A-Za-z0-9])/g, (_, __, c) => c.toUpperCase())
5
+ .replace(/[^A-Za-z0-9]/g, '');
6
+ return /^[0-9]/.test(name) ? `X${name}` : name || 'X';
7
+ };
8
+ const methodOf = (ref) => ref.split('.').map(pascal).join('');
9
+ const isRef = (t) => /^(\[\]|map\[|\*|json\.)/.test(t);
10
+ /** Columns padded the way gofmt aligns consecutive struct fields. */
11
+ const aligned = (rows) => {
12
+ const widths = [];
13
+ for (const row of rows)
14
+ row.slice(0, -1).forEach((cell, i) => (widths[i] = Math.max(widths[i] ?? 0, cell.length)));
15
+ return rows.map((row) => `\t${row.map((cell, i) => (i < row.length - 1 ? cell.padEnd(widths[i]) : cell)).join(' ')}`.trimEnd());
16
+ };
17
+ const render = (s) => {
18
+ if (s.members)
19
+ return `type ${s.name} string\n\nconst (\n${aligned(s.members.map(([name, value]) => [name, s.name, `= ${value}`])).join('\n')}\n)`;
20
+ return s.fields.length
21
+ ? `type ${s.name} struct {\n${aligned(s.fields).join('\n')}\n}`
22
+ : `type ${s.name} struct{}`;
23
+ };
24
+ class Types {
25
+ structs = [];
26
+ #byShape = new Map();
27
+ #names = new Set();
28
+ constructor(reserved) {
29
+ for (const r of reserved)
30
+ this.#names.add(r);
31
+ }
32
+ /** The Go type of a schema; `name` is used when it needs a new struct and has no `title`. */
33
+ type(schema, name, root = schema) {
34
+ const ref = schema.$ref;
35
+ if (typeof ref === 'string') {
36
+ const key = ref.replace(/^#\/\$defs\//, '');
37
+ const def = root.$defs?.[key];
38
+ return def ? this.type(def, pascal(key), root) : 'json.RawMessage';
39
+ }
40
+ const any = (schema.anyOf ?? schema.oneOf);
41
+ if (any) {
42
+ const rest = any.filter((s) => s.type !== 'null');
43
+ if (rest.length === 1) {
44
+ const t = this.type(rest[0], name, root);
45
+ return rest.length < any.length && !isRef(t) ? `*${t}` : t;
46
+ }
47
+ return 'json.RawMessage';
48
+ }
49
+ const values = (schema.enum ?? (schema.const !== undefined ? [schema.const] : null));
50
+ if (schema.enum && values?.every((v) => typeof v === 'string'))
51
+ return this.#enum(values, typeof schema.title === 'string' ? pascal(schema.title) : name);
52
+ if (values)
53
+ return values.every((v) => typeof v === 'string') ? 'string' : 'json.RawMessage';
54
+ const types = Array.isArray(schema.type) ? schema.type : [schema.type];
55
+ const nullable = types.includes('null');
56
+ const base = this.#base(types.find((t) => t !== 'null'), schema, name, root);
57
+ return nullable && !isRef(base) ? `*${base}` : base;
58
+ }
59
+ #base(type, schema, name, root) {
60
+ switch (type) {
61
+ case 'string':
62
+ return 'string';
63
+ case 'boolean':
64
+ return 'bool';
65
+ case 'integer':
66
+ return 'int64';
67
+ case 'number':
68
+ return 'float64';
69
+ case 'array':
70
+ return schema.items
71
+ ? `[]${this.type(schema.items, `${name}Item`, root)}`
72
+ : '[]json.RawMessage';
73
+ case 'object': {
74
+ const props = schema.properties;
75
+ const extra = schema.additionalProperties;
76
+ if (!props && extra && typeof extra === 'object')
77
+ return `map[string]${this.type(extra, `${name}Value`, root)}`;
78
+ return this.struct(schema, name, root);
79
+ }
80
+ default:
81
+ return 'json.RawMessage';
82
+ }
83
+ }
84
+ struct(schema, name, root = schema, fixed = false) {
85
+ const { title, description: _, ...shape } = schema;
86
+ const props = Object.entries(schema.properties ?? {});
87
+ if (!fixed && !props.length)
88
+ return this.#empty();
89
+ if (!fixed && typeof title === 'string')
90
+ return this.#named(pascal(title), () => this.#fields(schema, pascal(title), root), canonicalStringify(shape));
91
+ return this.#add(name, (unique) => this.#fields(schema, unique, root));
92
+ }
93
+ #enum(values, name) {
94
+ return this.#named(name, () => [], JSON.stringify(values), (unique) => {
95
+ const used = new Set();
96
+ return values.map((v) => {
97
+ let constant = `${unique}${pascal(v)}`;
98
+ for (let n = 2; used.has(constant) || this.#names.has(constant); n++)
99
+ constant = `${unique}${pascal(v)}${n}`;
100
+ used.add(constant);
101
+ this.#names.add(constant);
102
+ return [constant, JSON.stringify(v)];
103
+ });
104
+ });
105
+ }
106
+ #empty() {
107
+ if (!this.structs.some((s) => s.name === 'Empty'))
108
+ this.structs.push({ name: 'Empty', fields: [] });
109
+ return 'Empty';
110
+ }
111
+ /** A titled shape is one Go type wherever it appears; the same title with another shape gets a number. */
112
+ #named(name, fields, shape = '{}', members) {
113
+ const key = `${name} ${shape}`;
114
+ const known = this.#byShape.get(key);
115
+ if (known)
116
+ return known;
117
+ const unique = this.#add(name, () => fields(), members);
118
+ this.#byShape.set(key, unique);
119
+ return unique;
120
+ }
121
+ #add(wanted, fields, members) {
122
+ let unique = wanted;
123
+ for (let n = 2; this.#names.has(unique); n++)
124
+ unique = `${wanted}${n}`;
125
+ this.#names.add(unique);
126
+ const struct = { name: unique, fields: [] };
127
+ this.structs.push(struct);
128
+ if (members)
129
+ struct.members = members(unique);
130
+ else
131
+ struct.fields = fields(unique);
132
+ return unique;
133
+ }
134
+ #fields(schema, owner, root) {
135
+ const required = new Set(schema.required ?? []);
136
+ return Object.entries(schema.properties ?? {}).map(([prop, s]) => {
137
+ const t = this.type(s, `${owner}${pascal(prop)}`, root);
138
+ const optional = !required.has(prop);
139
+ const values = (s.enum ?? null);
140
+ const named = values?.every((v) => typeof v === 'string');
141
+ return [
142
+ pascal(prop),
143
+ `${optional && !isRef(t) ? '*' : ''}${t}`,
144
+ `\`json:"${prop}${optional ? ',omitempty' : ''}"\``,
145
+ ...(values && !named ? [`// one of ${values.map((v) => JSON.stringify(v)).join(', ')}`] : []),
146
+ ];
147
+ });
148
+ }
149
+ }
150
+ const describe = (e, errors) => {
151
+ const parts = [
152
+ `${e.ref} (${e.kind}${e.session ? ', with the session' : ', no session: public data'})`,
153
+ ...(errors.length ? [`errors: ${errors.join(', ')}`] : []),
154
+ ...(e.invalid ? ['may return Invalid'] : []),
155
+ ];
156
+ return `\t// ${parts.join('; ')}`;
157
+ };
158
+ /** The Go contract of one `remote()` group: types, the Resolvers interface and the HTTP handler (ADR 0068). */
159
+ const tidy = (go) => go.replace(/\n{3,}/g, '\n\n');
160
+ export function goContract(contract, pkg) {
161
+ const types = new Types([
162
+ 'Invalid',
163
+ 'Ctx',
164
+ 'Options',
165
+ 'Resolvers',
166
+ 'Handler',
167
+ 'Fingerprint',
168
+ 'Empty',
169
+ 'Upload',
170
+ 'Forbidden',
171
+ ]);
172
+ const session = contract.session ? types.struct(contract.session, 'Session', contract.session, true) : null;
173
+ const errorTypes = [];
174
+ const methods = contract.effects.map((e) => {
175
+ const base = methodOf(e.ref);
176
+ const input = types.type(e.input, `${base}Input`);
177
+ const output = types.type(e.output, `${base}Output`);
178
+ const errors = Object.entries(e.errors).map(([name, schema]) => {
179
+ const type = types.struct(schema, `${base}${pascal(name)}`, schema, true);
180
+ errorTypes.push(`func (e ${type}) Error() string { return "${e.ref}: ${name}" }\nfunc (e ${type}) failure() (string, string, any) {\n\treturn "${e.ref}", "${name}", e\n}`);
181
+ return type;
182
+ });
183
+ return { e, base, input, output, errors };
184
+ });
185
+ const sessionType = session ?? 'struct{}';
186
+ return tidy(`// Code generated by hozu gen from the Hozu declarations. DO NOT EDIT.
187
+ // Change the declarations in TypeScript, then run hozu gen (ADR 0068).
188
+
189
+ package ${pkg}
190
+
191
+ import (
192
+ \t"context"
193
+ \t"crypto/subtle"
194
+ \t"encoding/json"
195
+ \t"errors"
196
+ \t"log"
197
+ \t"net/http"
198
+ \t"reflect"
199
+ \t"strings"
200
+ )
201
+
202
+ // Fingerprint is the contract of one effect this file was generated from, or "" for another effect. Every call
203
+ // carries its effect's, so changing one declaration answers 409 for that effect alone.
204
+ func Fingerprint(effect string) string {
205
+ \tswitch effect {
206
+ ${contract.effects.map((e) => `\tcase "${e.ref}":\n\t\treturn "${e.fingerprint}"`).join('\n')}
207
+ \t}
208
+ \treturn ""
209
+ }
210
+
211
+ ${types.structs.map(render).join('\n\n')}
212
+
213
+ ${errorTypes.join('\n\n')}
214
+
215
+ // Invalid is the framework error of mutations and endpoints: a message and one message per input field.
216
+ type Invalid struct {
217
+ \tMessage string \`json:"message"\`
218
+ \tFields map[string]string \`json:"fields"\`
219
+ }
220
+
221
+ func (e Invalid) Error() string { return "Invalid: " + e.Message }
222
+ func (e Invalid) failure() (string, string, any) { return "", "Invalid", e }
223
+
224
+ // Forbidden answers as access does when the visitor may not have it (user-scoped queries and mutations).
225
+ type Forbidden struct {
226
+ \tMessage string \`json:"message,omitempty"\`
227
+ }
228
+
229
+ func (e Forbidden) Error() string { return "Forbidden: " + e.Message }
230
+ func (e Forbidden) failure() (string, string, any) { return "", "Forbidden", e }
231
+
232
+ // Ctx is one call. Session is nil when the visitor is signed out or the effect reads no session. Preview is
233
+ // true in preview mode; Header holds an endpoint's request headers (no cookie).
234
+ type Ctx struct {
235
+ \tContext context.Context
236
+ \tSession *${sessionType}
237
+ \tPreview bool
238
+ \tHeader http.Header
239
+ \tfiles map[string]Upload
240
+ \tchanged bool
241
+ \tnext *${sessionType}
242
+ }
243
+
244
+ // Upload is a file a form sent with the call; Data holds its bytes.
245
+ type Upload struct {
246
+ \tName string \`json:"name"\`
247
+ \tType string \`json:"type"\`
248
+ \tSize int64 \`json:"size"\`
249
+ \tData []byte \`json:"data"\`
250
+ }
251
+
252
+ // File returns the upload a form field's token names, or nil (mutations and endpoints only).
253
+ func (c *Ctx) File(token string) *Upload {
254
+ \tif f, ok := c.files[token]; ok {
255
+ \t\treturn &f
256
+ \t}
257
+ \treturn nil
258
+ }
259
+
260
+ // SetSession signs the visitor in as s (mutations and endpoints only).
261
+ func (c *Ctx) SetSession(s ${sessionType}) { c.changed, c.next = true, &s }
262
+
263
+ // SignOut ends the visitor's session (mutations and endpoints only).
264
+ func (c *Ctx) SignOut() { c.changed, c.next = true, nil }
265
+
266
+ // Resolvers implements the effects that app.ts lists in remote(). Return a declared error as the error value.
267
+ type Resolvers interface {
268
+ ${methods.map((m) => `${describe(m.e, m.errors)}\n\t${m.base}(ctx *Ctx, in ${m.input}) (${m.output}, error)`).join('\n')}
269
+ }
270
+
271
+ // Options configures Handler. Secret (16 characters or more) must equal the x-hozu-secret header of every call:
272
+ // the handler trusts the session a call carries, so it answers only the Hozu server.
273
+ type Options struct {
274
+ \tSecret string
275
+ }
276
+
277
+ type failure interface {
278
+ \terror
279
+ \tfailure() (effect string, name string, data any)
280
+ }
281
+
282
+ type request struct {
283
+ ${aligned([
284
+ ['Effect', 'string', '`json:"effect"`'],
285
+ ['Input', 'json.RawMessage', '`json:"input"`'],
286
+ ['Session', `*${sessionType}`, '`json:"session"`'],
287
+ ['Preview', 'bool', '`json:"preview"`'],
288
+ ['Headers', 'map[string]string', '`json:"headers"`'],
289
+ ['Files', 'map[string]Upload', '`json:"files"`'],
290
+ ]).join('\n')}
291
+ }
292
+
293
+ func call[I any, O any](ctx *Ctx, raw json.RawMessage, f func(*Ctx, I) (O, error)) (any, error) {
294
+ \tvar in I
295
+ \tif err := json.Unmarshal(raw, &in); err != nil {
296
+ \t\treturn nil, err
297
+ \t}
298
+ \tout, err := f(ctx, in)
299
+ \treturn filled(out), err
300
+ }
301
+
302
+ // filled replaces nil slices and maps with empty ones, so a list answers [] and never null.
303
+ func filled(v any) any {
304
+ \trv := reflect.New(reflect.TypeOf(v)).Elem()
305
+ \trv.Set(reflect.ValueOf(v))
306
+ \tfill(rv)
307
+ \treturn rv.Interface()
308
+ }
309
+
310
+ func fill(v reflect.Value) {
311
+ \tswitch v.Kind() {
312
+ \tcase reflect.Slice:
313
+ \t\tif v.IsNil() && v.CanSet() {
314
+ \t\t\tv.Set(reflect.MakeSlice(v.Type(), 0, 0))
315
+ \t\t}
316
+ \t\tfor i := 0; i < v.Len(); i++ {
317
+ \t\t\tfill(v.Index(i))
318
+ \t\t}
319
+ \tcase reflect.Map:
320
+ \t\tif v.IsNil() && v.CanSet() {
321
+ \t\t\tv.Set(reflect.MakeMap(v.Type()))
322
+ \t\t}
323
+ \tcase reflect.Pointer:
324
+ \t\tif !v.IsNil() {
325
+ \t\t\tfill(v.Elem())
326
+ \t\t}
327
+ \tcase reflect.Struct:
328
+ \t\tfor i := 0; i < v.NumField(); i++ {
329
+ \t\t\tif v.Type().Field(i).IsExported() {
330
+ \t\t\t\tfill(v.Field(i))
331
+ \t\t\t}
332
+ \t\t}
333
+ \t}
334
+ }
335
+
336
+ // Handler answers the calls of the Hozu server: POST { effect, input, session } → { ok } | { fail } (+ session).
337
+ func Handler(r Resolvers, o Options) http.Handler {
338
+ \tif len(o.Secret) < 16 {
339
+ \t\tpanic("hozu: Options.Secret must hold the 16 or more characters of the app's remote() secret")
340
+ \t}
341
+ \treturn http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
342
+ \t\tif req.Method != http.MethodPost {
343
+ \t\t\thttp.Error(w, "POST only", http.StatusMethodNotAllowed)
344
+ \t\t\treturn
345
+ \t\t}
346
+ \t\tif subtle.ConstantTimeCompare([]byte(req.Header.Get("X-Hozu-Secret")), []byte(o.Secret)) != 1 {
347
+ \t\t\thttp.Error(w, "wrong x-hozu-secret", http.StatusUnauthorized)
348
+ \t\t\treturn
349
+ \t\t}
350
+ \t\tvar body request
351
+ \t\tif err := json.NewDecoder(req.Body).Decode(&body); err != nil {
352
+ \t\t\thttp.Error(w, err.Error(), http.StatusBadRequest)
353
+ \t\t\treturn
354
+ \t\t}
355
+ \t\twant := Fingerprint(body.Effect)
356
+ \t\tif want == "" {
357
+ \t\t\thttp.Error(w, "unknown effect "+body.Effect, http.StatusNotFound)
358
+ \t\t\treturn
359
+ \t\t}
360
+ \t\tif got := req.Header.Get("X-Hozu-Fingerprint"); got != want {
361
+ \t\t\thttp.Error(w, body.Effect+" is "+want+" here, not "+got+": run hozu gen and rebuild", http.StatusConflict)
362
+ \t\t\treturn
363
+ \t\t}
364
+ \t\tid := req.Header.Get("X-Hozu-Call")
365
+ \t\tctx := &Ctx{Context: req.Context(), Session: body.Session, Preview: body.Preview, Header: http.Header{}, files: body.Files}
366
+ \t\tfor k, v := range body.Headers {
367
+ \t\t\tctx.Header.Set(k, v)
368
+ \t\t}
369
+ \t\tvar out any
370
+ \t\tvar err error
371
+ \t\tswitch body.Effect {
372
+ ${methods.map((m) => `\t\tcase "${m.e.ref}":\n\t\t\tout, err = call(ctx, body.Input, r.${m.base})`).join('\n')}
373
+ \t\t}
374
+ \t\tres := map[string]any{}
375
+ \t\tvar f failure
376
+ \t\tswitch {
377
+ \t\tcase err == nil:
378
+ \t\t\tres["ok"] = out
379
+ \t\tcase errors.As(err, &f):
380
+ \t\t\teffect, name, data := f.failure()
381
+ \t\t\tif effect != "" && effect != body.Effect {
382
+ \t\t\t\tlog.Printf("hozu: %s (call %s) returned %s, an error of %s", body.Effect, id, name, effect)
383
+ \t\t\t\thttp.Error(w, "resolver failed: "+name+" is an error of "+effect, http.StatusInternalServerError)
384
+ \t\t\t\treturn
385
+ \t\t\t}
386
+ \t\t\tres["fail"] = map[string]any{"name": name, "data": data}
387
+ \t\tdefault:
388
+ \t\t\tlog.Printf("hozu: %s (call %s): %v", body.Effect, id, err)
389
+ \t\t\tline, _, _ := strings.Cut(err.Error(), "\\n")
390
+ \t\t\thttp.Error(w, "resolver failed: "+line, http.StatusInternalServerError)
391
+ \t\t\treturn
392
+ \t\t}
393
+ \t\tif ctx.changed {
394
+ \t\t\tres["session"] = ctx.next
395
+ \t\t}
396
+ \t\tw.Header().Set("Content-Type", "application/json")
397
+ \t\tif err := json.NewEncoder(w).Encode(res); err != nil {
398
+ \t\t\tlog.Printf("hozu: %s (call %s): %v", body.Effect, id, err)
399
+ \t\t}
400
+ \t})
401
+ }
402
+ `);
403
+ }
404
+ /** The fingerprint of each effect a generated Go contract declares, by ref. */
405
+ export const goFingerprints = (text) => Object.fromEntries([...text.matchAll(/^\tcase "([^"]+)":\n\t\treturn "([0-9a-f]+)"$/gm)].map((m) => [m[1], m[2]]));
406
+ const counted = /^(id|count|qty|quantity|cents)$|(Id|_id|ID|Count|_count|Qty|Quantity|Cents|_cents)$/;
407
+ /**
408
+ * Number fields named like an id or a count: `z.number()` is float64 in Go, so they are notes suggesting `z.int()`
409
+ * (ADR 0070 C4), not diagnostics.
410
+ */
411
+ export function goNotes(contract) {
412
+ const notes = new Set();
413
+ const walk = (schema, path, root, seen) => {
414
+ const ref = schema.$ref;
415
+ if (typeof ref === 'string') {
416
+ const key = ref.replace(/^#\/\$defs\//, '');
417
+ const def = root.$defs?.[key];
418
+ if (def && !seen.has(key))
419
+ walk(def, path, root, new Set([...seen, key]));
420
+ return;
421
+ }
422
+ for (const s of (schema.anyOf ?? schema.oneOf ?? []))
423
+ walk(s, path, root, seen);
424
+ if (schema.items)
425
+ walk(schema.items, `${path}[]`, root, seen);
426
+ for (const [prop, s] of Object.entries(schema.properties ?? {})) {
427
+ const number = [s, ...(s.anyOf ?? s.oneOf ?? [])].some((x) => (Array.isArray(x.type) ? x.type : [x.type]).includes('number'));
428
+ if (number && counted.test(prop))
429
+ notes.add(`${path}.${prop} is a number (float64 in Go); if it holds whole numbers, declare it z.int() (int64)`);
430
+ walk(s, `${path}.${prop}`, root, seen);
431
+ }
432
+ };
433
+ for (const e of contract.effects) {
434
+ walk(e.input, `${e.ref}: input`, e.input, new Set());
435
+ walk(e.output, `${e.ref}: output`, e.output, new Set());
436
+ for (const [name, schema] of Object.entries(e.errors))
437
+ walk(schema, `${e.ref}: ${name}`, schema, new Set());
438
+ }
439
+ if (contract.session)
440
+ walk(contract.session, 'session', contract.session, new Set());
441
+ return [...notes];
442
+ }
443
+ //# sourceMappingURL=go.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"go.js","sourceRoot":"","sources":["../../src/gen/go.ts"],"names":[],"mappings":"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"}
package/dist/main.d.ts CHANGED
@@ -11,6 +11,7 @@ type Token = {
11
11
  value?: string | undefined;
12
12
  };
13
13
  export declare function browsePlan(tokens: Token[]): Pick<BrowseOptions, 'actors' | 'plan'>;
14
+ export declare function cli(argv: string[]): Promise<void>;
14
15
  export declare function main(argv: string[], cwd?: string, out?: (s: string) => void): Promise<number>;
15
16
  export {};
16
17
  //# sourceMappingURL=main.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,KAAK,aAAa,EAKnB,MAAM,sBAAsB,CAAA;AAwH7B,wGAAwG;AACxG,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAaxE;AAED,eAAO,MAAM,cAAc,UAAW,MAAM,GAAG,SAAS,KAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAQzF,CAAA;AAQD,KAAK,KAAK,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAExE,wBAAgB,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,MAAM,CAAC,CA8ClF;AAED,wBAAsB,IAAI,CACxB,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,SAAgB,EACnB,GAAG,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAqC,GACxD,OAAO,CAAC,MAAM,CAAC,CA2VjB"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,KAAK,aAAa,EAKnB,MAAM,sBAAsB,CAAA;AA4H7B,wGAAwG;AACxG,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAaxE;AAED,eAAO,MAAM,cAAc,UAAW,MAAM,GAAG,SAAS,KAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAQzF,CAAA;AAQD,KAAK,KAAK,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAExE,wBAAgB,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,MAAM,CAAC,CA8ClF;AAMD,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CASvD;AAED,wBAAsB,IAAI,CACxB,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,SAAgB,EACnB,GAAG,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAqC,GACxD,OAAO,CAAC,MAAM,CAAC,CAqWjB"}
package/dist/main.js CHANGED
@@ -3,7 +3,7 @@ import { relative, resolve } from 'node:path';
3
3
  import { parseArgs } from 'node:util';
4
4
  import { describeAdd, runAddFeature } from './commands/add.js';
5
5
  import { describeAddComponent, runAddComponent } from './commands/add-component.js';
6
- import { BuildFailed } from './commands/app.js';
6
+ import { BuildFailed, disposeApps } from './commands/app.js';
7
7
  import { browseFailed, describeBrowse, runBrowse, stepsOf, } from './commands/browse.js';
8
8
  import { runBuild } from './commands/build.js';
9
9
  import { describeCall, runCall } from './commands/call.js';
@@ -14,6 +14,7 @@ import { describeDevtoolsMessages, messagesFileOf, runDevtoolsMessages } from '.
14
14
  import { runDocs } from './commands/docs.js';
15
15
  import { describeEnv, runEnv } from './commands/env.js';
16
16
  import { describeExport, runExport } from './commands/export.js';
17
+ import { describeGen, runGen } from './commands/gen.js';
17
18
  import { runInspect } from './commands/inspect.js';
18
19
  import { describeAddKit, runAddKit } from './commands/kits.js';
19
20
  import { describeMap, runMap } from './commands/map.js';
@@ -64,6 +65,9 @@ Commands:
64
65
  get <path>... Request pages in-process (no server): status, title, alerts, visible text, forms
65
66
  env [--example] Every env variable the app reads: side, required, default, set now, internal URL;
66
67
  --example writes .env.example
68
+ gen Write the contract of every remote() in app.ts (a Go file: types, the Resolvers
69
+ interface, the HTTP handler) for resolvers in another language; hozu check reports a
70
+ stale one (HZ093)
67
71
  call <feature>.<effect> Run one query, mutation or endpoint in-process (no server) through the app's own
68
72
  handler: --input '<json>', --session '<json>', --header 'Name: value' (endpoints);
69
73
  a mutation or a POST endpoint writes real data and needs --write
@@ -202,6 +206,18 @@ export function browsePlan(tokens) {
202
206
  }
203
207
  return { actors, plan };
204
208
  }
209
+ let running = false;
210
+ const flushed = (stream) => new Promise((done) => stream.write('', () => done()));
211
+ export async function cli(argv) {
212
+ const code = await main(argv);
213
+ if (running) {
214
+ process.exitCode = code;
215
+ return;
216
+ }
217
+ await disposeApps(undefined, (line) => process.stderr.write(`hozu: ${line}\n`));
218
+ await Promise.all([flushed(process.stdout), flushed(process.stderr)]);
219
+ process.exit(code);
220
+ }
205
221
  export async function main(argv, cwd = process.cwd(), out = (s) => process.stdout.write(s)) {
206
222
  let asJson = false;
207
223
  try {
@@ -272,6 +288,7 @@ export async function main(argv, cwd = process.cwd(), out = (s) => process.stdou
272
288
  'get',
273
289
  'call',
274
290
  'env',
291
+ 'gen',
275
292
  'browse',
276
293
  'add',
277
294
  'inspect',
@@ -390,6 +407,7 @@ export async function main(argv, cwd = process.cwd(), out = (s) => process.stdou
390
407
  await runDev(loaded, values['no-devtools'] === true ? false : (values.devtools ?? 'builder'), (line) => out(`${line}\n`), values['no-devtools'] === true
391
408
  ? null
392
409
  : messagesFileOf(values['devtools-messages'], cwd, (line) => out(`${line}\n`)));
410
+ running = true;
393
411
  return 0;
394
412
  }
395
413
  if (command === 'devtools') {
@@ -432,7 +450,10 @@ export async function main(argv, cwd = process.cwd(), out = (s) => process.stdou
432
450
  return result.ok ? 0 : 1;
433
451
  }
434
452
  if (command === 'serve') {
435
- await runServe(loaded, (line) => out(`${line}\n`));
453
+ const server = await runServe(loaded, (line) => out(`${line}\n`));
454
+ running = true;
455
+ for (const signal of ['SIGINT', 'SIGTERM'])
456
+ process.once(signal, () => void server.close().finally(() => process.exit(0)));
436
457
  return 0;
437
458
  }
438
459
  if (command === 'map') {
@@ -445,6 +466,11 @@ export async function main(argv, cwd = process.cwd(), out = (s) => process.stdou
445
466
  out(asJson ? json(result) : describeEnv(result));
446
467
  return 0;
447
468
  }
469
+ if (command === 'gen') {
470
+ const result = await runGen(loaded, cwd);
471
+ out(asJson ? json(result) : describeGen(result));
472
+ return 0;
473
+ }
448
474
  if (command === 'call') {
449
475
  const result = await runCall(loaded, {
450
476
  target,