@hozu/cli 0.22.0 → 0.24.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 +73 -8
- package/dist/commands/browse.js.map +1 -1
- package/dist/commands/call.d.ts.map +1 -1
- package/dist/commands/call.js +26 -4
- package/dist/commands/call.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/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +12 -4
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/request.d.ts +1 -0
- package/dist/commands/request.d.ts.map +1 -1
- package/dist/commands/request.js +126 -33
- package/dist/commands/request.js.map +1 -1
- package/dist/contract.d.ts +19 -4
- package/dist/contract.d.ts.map +1 -1
- package/dist/gen/go.d.ts +9 -2
- package/dist/gen/go.d.ts.map +1 -1
- package/dist/gen/go.js +127 -23
- package/dist/gen/go.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/migrate/steps.d.ts +2 -2
- package/dist/migrate/steps.d.ts.map +1 -1
- package/dist/migrate/steps.js +98 -12
- package/dist/migrate/steps.js.map +1 -1
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +17 -4
- package/dist/remote.js.map +1 -1
- package/package.json +8 -8
- package/schema/browse.schema.json +22 -1
- package/schema/call.schema.json +1 -1
- package/schema/gen.schema.json +24 -7
- package/schema/inspect.schema.json +2 -1
- package/schema/migrate.schema.json +10 -2
- package/skill/SKILL.md +2 -2
- package/skill/topics/data.md +38 -6
- package/skill/topics/recipes.md +6 -4
- package/skill/topics/testing.md +2 -0
- package/skill/topics/views.md +6 -2
package/dist/remote.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAoB,KAAK,EAAwC,cAAc,EAAE,MAAM,eAAe,CAAA;AAC7G,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hozu/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "The hozu command: check, map, why, show, get, browse, call, plan, add, build, dev, serve, docs, migrate and skill (all with --json)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hozu",
|
|
@@ -48,14 +48,14 @@
|
|
|
48
48
|
"access": "public"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@hozu/core": "0.
|
|
52
|
-
"@hozu/
|
|
53
|
-
"@hozu/
|
|
54
|
-
"@hozu/
|
|
55
|
-
"@hozu/
|
|
56
|
-
"@hozu/transform": "0.
|
|
51
|
+
"@hozu/core": "0.24.0",
|
|
52
|
+
"@hozu/validator": "0.24.0",
|
|
53
|
+
"@hozu/machine": "0.24.0",
|
|
54
|
+
"@hozu/compiler": "0.24.0",
|
|
55
|
+
"@hozu/devtools": "0.24.0",
|
|
56
|
+
"@hozu/transform": "0.24.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"create-hozu": "0.
|
|
59
|
+
"create-hozu": "0.24.0"
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -110,7 +110,28 @@
|
|
|
110
110
|
}
|
|
111
111
|
},
|
|
112
112
|
"differs": {
|
|
113
|
-
"type": "boolean"
|
|
113
|
+
"type": "boolean",
|
|
114
|
+
"description": "Both modes made a request and the resulting text differs."
|
|
115
|
+
},
|
|
116
|
+
"differences": {
|
|
117
|
+
"type": "array",
|
|
118
|
+
"items": {
|
|
119
|
+
"type": "object",
|
|
120
|
+
"properties": {
|
|
121
|
+
"on": {
|
|
122
|
+
"type": "string"
|
|
123
|
+
},
|
|
124
|
+
"off": {
|
|
125
|
+
"type": "string"
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"required": [
|
|
129
|
+
"on",
|
|
130
|
+
"off"
|
|
131
|
+
],
|
|
132
|
+
"additionalProperties": false
|
|
133
|
+
},
|
|
134
|
+
"description": "The words that differ, at most three lines (ADR 0070 B6); a URL pair first when the pages differ."
|
|
114
135
|
},
|
|
115
136
|
"elsewhere": {
|
|
116
137
|
"type": "array",
|
package/schema/call.schema.json
CHANGED
package/schema/gen.schema.json
CHANGED
|
@@ -17,15 +17,25 @@
|
|
|
17
17
|
"package": {
|
|
18
18
|
"type": "string"
|
|
19
19
|
},
|
|
20
|
-
"fingerprint": {
|
|
21
|
-
"type": "string"
|
|
22
|
-
},
|
|
23
20
|
"effects": {
|
|
24
21
|
"type": "array",
|
|
25
22
|
"items": {
|
|
26
|
-
"type": "
|
|
23
|
+
"type": "object",
|
|
24
|
+
"properties": {
|
|
25
|
+
"ref": {
|
|
26
|
+
"type": "string"
|
|
27
|
+
},
|
|
28
|
+
"fingerprint": {
|
|
29
|
+
"type": "string"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"required": [
|
|
33
|
+
"ref",
|
|
34
|
+
"fingerprint"
|
|
35
|
+
],
|
|
36
|
+
"additionalProperties": false
|
|
27
37
|
},
|
|
28
|
-
"description": "The effects it implements
|
|
38
|
+
"description": "The effects it implements (feature.symbol), each with the fingerprint its calls carry (ADR 0070 C3)."
|
|
29
39
|
},
|
|
30
40
|
"written": {
|
|
31
41
|
"type": "boolean",
|
|
@@ -37,15 +47,22 @@
|
|
|
37
47
|
"type": "string"
|
|
38
48
|
},
|
|
39
49
|
"description": "Effects that cannot be remote (the server refuses to start with them, HZ093)."
|
|
50
|
+
},
|
|
51
|
+
"notes": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": {
|
|
54
|
+
"type": "string"
|
|
55
|
+
},
|
|
56
|
+
"description": "Number fields named like an id or a count, which may want z.int() (ADR 0070 C4), and enums without a title that became several Go types, which a title makes one; not diagnostics."
|
|
40
57
|
}
|
|
41
58
|
},
|
|
42
59
|
"required": [
|
|
43
60
|
"file",
|
|
44
61
|
"package",
|
|
45
|
-
"fingerprint",
|
|
46
62
|
"effects",
|
|
47
63
|
"written",
|
|
48
|
-
"problems"
|
|
64
|
+
"problems",
|
|
65
|
+
"notes"
|
|
49
66
|
],
|
|
50
67
|
"additionalProperties": false
|
|
51
68
|
}
|
|
@@ -41,15 +41,23 @@
|
|
|
41
41
|
},
|
|
42
42
|
"summary": {
|
|
43
43
|
"type": "string"
|
|
44
|
+
},
|
|
45
|
+
"changes": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"items": {
|
|
48
|
+
"type": "string"
|
|
49
|
+
}
|
|
44
50
|
}
|
|
45
51
|
},
|
|
46
52
|
"required": [
|
|
47
53
|
"from",
|
|
48
54
|
"to",
|
|
49
|
-
"summary"
|
|
55
|
+
"summary",
|
|
56
|
+
"changes"
|
|
50
57
|
],
|
|
51
58
|
"additionalProperties": false
|
|
52
|
-
}
|
|
59
|
+
},
|
|
60
|
+
"description": "Each step's changes: `summary` joins `changes` with \"; \"."
|
|
53
61
|
},
|
|
54
62
|
"changed": {
|
|
55
63
|
"type": "array",
|
package/skill/SKILL.md
CHANGED
|
@@ -46,10 +46,10 @@ Hozu is not in your training data: this file and `npx hozu docs <topic>` are the
|
|
|
46
46
|
## Topics (`npx hozu docs <topic>`; `--more`: options, edge cases)
|
|
47
47
|
| Task | Topic |
|
|
48
48
|
|---|---|
|
|
49
|
-
| a new feature,
|
|
49
|
+
| a new feature, its files, an example | `feature` |
|
|
50
50
|
| elements, attributes, events, lists, links, reuse | `views` |
|
|
51
51
|
| states, events, invoke, timers, guards | `machine` |
|
|
52
|
-
| queries, mutations, tags, `fn()`, resolvers | `data` |
|
|
52
|
+
| queries, mutations, tags, `fn()`, resolvers, a database | `data` |
|
|
53
53
|
| where effects run, `fetch.ts`, browser storage, static hosts | `fetch` |
|
|
54
54
|
| contracts and the lock | `contracts` |
|
|
55
55
|
| routes, search, pages, `head`, 403 / 404 | `pages` |
|
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
|
|
@@ -118,7 +146,11 @@ export default app({
|
|
|
118
146
|
- Data the whole staff shares but only staff may read is `scope: 'user'` with `access` (it is never cached across
|
|
119
147
|
requests). Share one rule: `const staffOnly = part(({ session }) => session.role !== 'editor')`, then
|
|
120
148
|
`access: { allow: staffOnly }` on each effect.
|
|
121
|
-
- A resolver may answer `fail('Forbidden', { message })` (a
|
|
122
|
-
|
|
149
|
+
- A resolver may answer `fail('Forbidden', { message })` (a check `access` cannot make); with any access but
|
|
150
|
+
`'anyone'` its `session` is never null.
|
|
151
|
+
- A failure you can expect (a row gone, a state that forbids the change) is a declared error of the effect
|
|
152
|
+
(`errors: { NotFound }`, `fail('NotFound', …)`), handled in `failed` with and without JavaScript. A thrown error is
|
|
153
|
+
`Unexpected`: a server fault (500 on a native post, `Internal error` in production).
|
|
123
154
|
- Another app writing the same database: give the reading app a signed `endpoint` that `invalidates` the tags, and
|
|
124
|
-
call it after a write (`hozu docs endpoints`).
|
|
155
|
+
call it after a write (`hozu docs endpoints`). Every query that app's writes change needs a tag, or only its
|
|
156
|
+
freshness time refreshes it.
|
package/skill/topics/recipes.md
CHANGED
|
@@ -89,16 +89,18 @@ const staffHead = { query: me, render: (m) => ({ title: `${m.name} · Admin` }),
|
|
|
89
89
|
const staff = (route, View) => ui.page(route, { views: [Sidebar, View], head: staffHead })
|
|
90
90
|
export default project({ /* … */ pages: [staff(orders, OrderList), staff(orderDetail, OrderPage), …] })
|
|
91
91
|
```
|
|
92
|
-
The sidebar
|
|
92
|
+
The sidebar marks its sections with `current(route)` from its render:
|
|
93
|
+
`'aria-current': current(orders) || current(orderDetail)`, styled `aria-[current]:font-bold`.
|
|
93
94
|
|
|
94
95
|
## Screens with different state
|
|
95
96
|
One machine per feature: an order list (filters, selection) and an order page (shipping, refund) are two features,
|
|
96
97
|
`orders` and `order`, sharing declarations through `exports`. Each machine stays small and its contracts few.
|
|
97
98
|
|
|
98
99
|
## A multi-step checkout that also works without JavaScript
|
|
99
|
-
Each step is a state
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
Each step is a state and each step's form posts only its own fields: after a native post the server renders the next
|
|
101
|
+
step, and every form on that page carries the machine's state in a signed hidden field, so the next post continues
|
|
102
|
+
from it (going back to edit a step too). Prefill from the member with
|
|
103
|
+
`seed: ({ query }) => ({ email: query(me, {}).email })`.
|
|
102
104
|
|
|
103
105
|
## A notice after saving
|
|
104
106
|
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,13 @@ 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
|
|
70
|
+
- A link to the address shown gets `aria-current="page"`. Which links mark a section is yours to say: the render's
|
|
71
|
+
`current(route)` is true on that route's pages, so a menu writes
|
|
72
|
+
`ui.a({ href: ui.link(orders, null), 'aria-current': current(orders) || current(orderDetail) }, ['Orders'])` and
|
|
73
|
+
styles `aria-[current]:font-bold`: `true` is written `"page"` on the address itself, `false` writes nothing.
|
|
71
74
|
- `ui.dialog({ open: is(['editing']), on: { close: ui.send(Cancel, {}) } }, [...])` opens as a modal and closes with
|
|
72
|
-
the machine; Escape sends `close`.
|
|
75
|
+
the machine; Escape sends `close`. It needs JavaScript: a dialog that must open without it uses the native
|
|
76
|
+
`commandfor` button (the short form) and closes when the data that shows it changes.
|
|
73
77
|
- `ui.format.plural(n, { one: '# item', other: '# items' })` picks the case for the page's language (`=0` works).
|
|
74
78
|
- `null` and `false` render nothing, also inside a constant list:
|
|
75
79
|
`ui.ul({}, [...kinds.map((k) => (k === 'draft' ? null : ui.li({}, [k])))])`.
|