@hozu/cli 0.22.0 → 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.
- package/dist/commands/browse-page.d.ts.map +1 -1
- package/dist/commands/browse-page.js +6 -1
- package/dist/commands/browse-page.js.map +1 -1
- package/dist/commands/browse.d.ts +9 -1
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +61 -6
- package/dist/commands/browse.js.map +1 -1
- package/dist/commands/gen.d.ts.map +1 -1
- package/dist/commands/gen.js +8 -5
- package/dist/commands/gen.js.map +1 -1
- package/dist/commands/request.js +2 -2
- package/dist/commands/request.js.map +1 -1
- package/dist/contract.d.ts +13 -3
- package/dist/contract.d.ts.map +1 -1
- package/dist/gen/go.d.ts +7 -2
- package/dist/gen/go.d.ts.map +1 -1
- package/dist/gen/go.js +96 -22
- package/dist/gen/go.js.map +1 -1
- package/dist/migrate/steps.d.ts.map +1 -1
- package/dist/migrate/steps.js +7 -0
- package/dist/migrate/steps.js.map +1 -1
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +12 -4
- package/dist/remote.js.map +1 -1
- package/package.json +8 -8
- package/schema/browse.schema.json +22 -1
- package/schema/gen.schema.json +24 -7
- package/skill/SKILL.md +2 -2
- package/skill/topics/data.md +31 -3
- package/skill/topics/recipes.md +4 -3
- package/skill/topics/testing.md +2 -0
- package/skill/topics/views.md +4 -2
package/skill/topics/data.md
CHANGED
|
@@ -97,16 +97,44 @@ export default app({
|
|
|
97
97
|
```
|
|
98
98
|
- The loop: change the declaration in TypeScript → `npx hozu gen` (writes the contract: types, the `Resolvers`
|
|
99
99
|
interface, `Handler`) → implement the interface until `go test ./...` passes → restart the service → `hozu check`.
|
|
100
|
-
A contract older than the declarations is HZ093
|
|
100
|
+
A contract older than the declarations is HZ093 naming the changed effects; each effect has its own fingerprint, so
|
|
101
|
+
the service answers 409 only to calls of an effect that changed.
|
|
101
102
|
- In Go: return a declared error as the error value (`hozu.NotesAddNoteDuplicate{Text: t}`), `hozu.Invalid{…}` for
|
|
102
103
|
input problems; `ctx.Session` is nil when signed out; `ctx.SetSession(…)` / `ctx.SignOut()` in mutations. Public
|
|
103
104
|
queries never receive the session. `ctx.File(token)` reads an upload, `ctx.Header` an endpoint's request headers
|
|
104
105
|
(no cookie), `ctx.Preview` preview mode. The secret is required (16+ characters, the same value on both sides,
|
|
105
106
|
HZ093): the service trusts the session it is sent, so serve `hozu.Handler(r, hozu.Options{Secret: …})` on a private
|
|
106
107
|
address (it refuses to start without one).
|
|
108
|
+
- Any other Go error answers 500 with its first line, which reaches `onError` and `Unexpected` as a thrown TypeScript
|
|
109
|
+
error does, with the call's id (`x-hozu-call`) that the service's log line names
|
|
110
|
+
(`hozu: notes.listNotes (call 3fa2c1d0): …`). A service that is not running is `no service answers at <url>`.
|
|
107
111
|
- `.meta({ title: 'Note' })` on a schema makes it one Go type wherever it appears; `z.int()` is `int64`, a plain
|
|
108
|
-
number `float64
|
|
109
|
-
|
|
112
|
+
number `float64` (`hozu gen` notes number fields named like ids or counts); a string `z.enum` is a named type with
|
|
113
|
+
one constant per member (`hozu.OrderStatusPending`, named by its title or its field). Only `runs: 'server'` effects
|
|
114
|
+
and JSON endpoints can be remote (HZ093).
|
|
115
|
+
- [`examples/notes-go`](https://github.com/olevatorr/Hozu/tree/main/examples/notes-go) is the reference: the notes
|
|
116
|
+
app with every resolver in Go. Its `service/main.go` is all a service needs besides the resolvers (`go.mod` is
|
|
117
|
+
yours; the contract's folder is the `hozu` package):
|
|
118
|
+
```go
|
|
119
|
+
func main() {
|
|
120
|
+
secret := os.Getenv("NOTES_SERVICE_SECRET") // the app's remote() secret, 16+ characters
|
|
121
|
+
addr := os.Getenv("NOTES_SERVICE_ADDR") // the app's NOTES_SERVICE_URL is http://<addr>/effect
|
|
122
|
+
mux := http.NewServeMux()
|
|
123
|
+
mux.Handle("/effect", hozu.Handler(newResolvers(), hozu.Options{Secret: secret}))
|
|
124
|
+
server := &http.Server{Addr: addr, Handler: mux}
|
|
125
|
+
go func() {
|
|
126
|
+
if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
|
127
|
+
log.Fatal(err)
|
|
128
|
+
}
|
|
129
|
+
}()
|
|
130
|
+
stop, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
|
131
|
+
defer cancel()
|
|
132
|
+
<-stop.Done() // finish the calls in flight, then exit
|
|
133
|
+
ctx, done := context.WithTimeout(context.Background(), 5*time.Second)
|
|
134
|
+
defer done()
|
|
135
|
+
server.Shutdown(ctx)
|
|
136
|
+
}
|
|
137
|
+
```
|
|
110
138
|
|
|
111
139
|
## A database
|
|
112
140
|
- One pool per process, made in `app.ts`; close it in `app({ dispose: () => pool.end() })` so `hozu get`, `call` and
|
package/skill/topics/recipes.md
CHANGED
|
@@ -96,9 +96,10 @@ One machine per feature: an order list (filters, selection) and an order page (s
|
|
|
96
96
|
`orders` and `order`, sharing declarations through `exports`. Each machine stays small and its contracts few.
|
|
97
97
|
|
|
98
98
|
## A multi-step checkout that also works without JavaScript
|
|
99
|
-
Each step is a state
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
Each step is a state and each step's form posts only its own fields: after a native post the server renders the next
|
|
100
|
+
step, and every form on that page carries the machine's state in a signed hidden field, so the next post continues
|
|
101
|
+
from it (going back to edit a step too). Prefill from the member with
|
|
102
|
+
`seed: ({ query }) => ({ email: query(me, {}).email })`.
|
|
102
103
|
|
|
103
104
|
## A notice after saving
|
|
104
105
|
A `notice` context field set in `done` and cleared by `after: [{ ms: 4000, target: 'idle' }]` on a `saved` state;
|
package/skill/topics/testing.md
CHANGED
|
@@ -69,6 +69,8 @@
|
|
|
69
69
|
navigation prints `→ <path>` and the new page's lines; a live update on another actor's page prints under the step
|
|
70
70
|
(`bob: + Milk`).
|
|
71
71
|
- `≠ DIFFERS` marks a step where both modes made a request and the resulting text differs: a no-JS/JS parity bug.
|
|
72
|
+
It names the differing words (`≠ DIFFERS (on vs off): "#1307" vs "#1306"`): the two modes write twice to the same
|
|
73
|
+
data, so a new row per mode (an order number, a count) differs without a bug.
|
|
72
74
|
- Errors: uncaught exceptions, `console.error`s, CSP violations and failed requests, each with the page, the
|
|
73
75
|
resource type and the mode. A 400 re-render of an invalid native post is not an error, and a page answering
|
|
74
76
|
401, 403, 404 or 410 is the step's status (`→ /notes/n1 (403)`), so an access check exits 0.
|
package/skill/topics/views.md
CHANGED
|
@@ -67,9 +67,11 @@ export const Board = ui.view({
|
|
|
67
67
|
## Menus, dialogs, counting
|
|
68
68
|
- When a query's input changes, the rows stay (`aria-busy` on the parent) and update by key; `pending` shows only
|
|
69
69
|
before the first answer.
|
|
70
|
-
- A link to the page shown gets `aria-current="page"
|
|
70
|
+
- A link to the page shown gets `aria-current="page"`, a link to a section above it (`/orders` on `/orders/7`)
|
|
71
|
+
`"true"`; the same path with another search (a next page) gets nothing. An `aria-current` you set wins.
|
|
71
72
|
- `ui.dialog({ open: is(['editing']), on: { close: ui.send(Cancel, {}) } }, [...])` opens as a modal and closes with
|
|
72
|
-
the machine; Escape sends `close`.
|
|
73
|
+
the machine; Escape sends `close`. It needs JavaScript: a dialog that must open without it uses the native
|
|
74
|
+
`commandfor` button (the short form) and closes when the data that shows it changes.
|
|
73
75
|
- `ui.format.plural(n, { one: '# item', other: '# items' })` picks the case for the page's language (`=0` works).
|
|
74
76
|
- `null` and `false` render nothing, also inside a constant list:
|
|
75
77
|
`ui.ul({}, [...kinds.map((k) => (k === 'draft' ? null : ui.li({}, [k])))])`.
|