@zerotal/arch 1.8.1 → 1.10.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/api-surface.md +6 -6
- package/docs/admin/actions.md +15 -0
- package/docs/admin/auth.md +10 -0
- package/docs/admin/dashboard.md +12 -0
- package/docs/admin/extending-ui.md +14 -0
- package/docs/admin/forms.md +15 -0
- package/docs/admin/operations.md +12 -0
- package/docs/admin/resources.md +6 -0
- package/docs/admin/tables.md +21 -0
- package/docs/audit.md +5 -0
- package/docs/authentication.md +110 -1
- package/docs/broadcasting/references.md +17 -0
- package/docs/cache.md +5 -0
- package/docs/carbon.md +5 -0
- package/docs/changelog.md +281 -0
- package/docs/client/index.md +17 -0
- package/docs/commands.md +6 -0
- package/docs/components.md +73 -0
- package/docs/config-system.md +54 -0
- package/docs/cookies.md +6 -0
- package/docs/deployment.md +151 -13
- package/docs/devtools.md +5 -0
- package/docs/email-verification.md +26 -1
- package/docs/encryption.md +21 -0
- package/docs/errors.md +2 -0
- package/docs/flow/components.md +54 -0
- package/docs/flow/forms.md +57 -0
- package/docs/flow/references.md +14 -0
- package/docs/getting-started.md +38 -0
- package/docs/health.md +19 -0
- package/docs/helpers.md +150 -0
- package/docs/i18n.md +5 -0
- package/docs/inertia/middleware.md +44 -0
- package/docs/inertia/props.md +70 -0
- package/docs/inertia/ssr.md +95 -10
- package/docs/lock.md +15 -0
- package/docs/logger.md +38 -0
- package/docs/middleware.md +31 -0
- package/docs/migrations.md +47 -0
- package/docs/monitor.md +59 -0
- package/docs/notifications.md +11 -0
- package/docs/orm/casts.md +6 -0
- package/docs/orm/lifecycle.md +18 -0
- package/docs/orm/queries.md +10 -0
- package/docs/orm/relationships.md +30 -0
- package/docs/queue.md +10 -0
- package/docs/rate-limiting.md +84 -21
- package/docs/responses.md +23 -0
- package/docs/routing.md +16 -0
- package/docs/scheduler.md +82 -8
- package/docs/session.md +6 -0
- package/docs/social.md +10 -0
- package/docs/storage.md +21 -0
- package/docs/support-policy.md +13 -1
- package/docs/telemetry.md +8 -0
- package/docs/tenancy.md +6 -0
- package/docs/testing/index.md +105 -0
- package/docs/upgrade.md +48 -0
- package/docs/validator.md +9 -0
- package/docs/view.md +6 -0
- package/package.json +3 -3
- package/src/install/guidelines.ts +1 -1
- package/src/mcp/stdio.ts +3 -3
- package/src/tools/_probe.ts +2 -2
package/docs/telemetry.md
CHANGED
|
@@ -497,6 +497,14 @@ console.log(mem.spans[0]?.attributes["x"]); // 1
|
|
|
497
497
|
| `'producer'` | Enqueuing a message. |
|
|
498
498
|
| `'consumer'` | Processing a queued message. |
|
|
499
499
|
|
|
500
|
+
## Types
|
|
501
|
+
|
|
502
|
+
| Type | What it is |
|
|
503
|
+
| ------------------------------ | ---------------------------------------------------------------------------------------- |
|
|
504
|
+
| `TelemetryOptions` | What `TelemetryConfig()` accepts. |
|
|
505
|
+
| `OtlpExporterOptions` | Where spans are shipped, and how. |
|
|
506
|
+
| `SpanStatus`, `SpanStatusCode` | A span's outcome — `unset`, `ok` or `error`, which is what a backend colours a trace by. |
|
|
507
|
+
|
|
500
508
|
## Next steps
|
|
501
509
|
|
|
502
510
|
- [Logger](/docs/logger) — pair traces with structured logs.
|
package/docs/tenancy.md
CHANGED
|
@@ -593,6 +593,12 @@ Resolved from the container binding `"tenancy"`; the `Tenant` value is the facad
|
|
|
593
593
|
| `evict` | `evict(tenant): void` | Close and drop a tenant's connection. |
|
|
594
594
|
| `closeAll` | `closeAll(): void` | Close every open connection. |
|
|
595
595
|
|
|
596
|
+
## Types
|
|
597
|
+
|
|
598
|
+
`TenancyStrategy` is how a tenant is resolved from a request — subdomain, path, header, or the
|
|
599
|
+
authenticated user. `TenantManagerOptions` configures the manager, and `TenantDeletedHook` runs
|
|
600
|
+
when a tenant is removed, which is where the data belonging to it gets cleaned up.
|
|
601
|
+
|
|
596
602
|
## Next steps
|
|
597
603
|
|
|
598
604
|
- [ORM](/docs/orm) — how `Tenantable` composes onto your models via `Model.using`.
|
package/docs/testing/index.md
CHANGED
|
@@ -209,6 +209,105 @@ observers, global scopes, and state-machine callbacks, plus framework event
|
|
|
209
209
|
subscriptions. `createTestApp()` and `testApp.close()` call it for you, so suites
|
|
210
210
|
using those helpers don't need the explicit `afterEach`.
|
|
211
211
|
|
|
212
|
+
## `bun test` vs `bun zt test`
|
|
213
|
+
|
|
214
|
+
Both run the same files. `bun zt test` is a wrapper that sets up three things Bun's
|
|
215
|
+
runner does not, and each of them has cost somebody a day:
|
|
216
|
+
|
|
217
|
+
| | `bun test` | `bun zt test` |
|
|
218
|
+
| ---------------- | --------------------- | ---------------------------------------------------------- |
|
|
219
|
+
| Per-test timeout | Bun's default, 5000ms | 30000ms (`--timeout`, override with `--timeout=`) |
|
|
220
|
+
| Runtime check | none | refuses a Bun below the project's `engines.bun` |
|
|
221
|
+
| DB wiring | none | preloads `@zerotal/testing/preload` and passes `ZT_DB_URL` |
|
|
222
|
+
|
|
223
|
+
### The timeout
|
|
224
|
+
|
|
225
|
+
Bun's default per-test timeout is 5000ms, and a suite that boots an app per file
|
|
226
|
+
exceeds it on a loaded machine — CI, or a laptop that has just run `tsc`. The
|
|
227
|
+
failures look like flakes, which is the expensive part: a flake gets re-run, and a
|
|
228
|
+
re-run passes.
|
|
229
|
+
|
|
230
|
+
`bun zt test` sets `--timeout=30000`. If you run `bun test` directly, pass it
|
|
231
|
+
yourself, because **the two documented-looking alternatives do not work**:
|
|
232
|
+
|
|
233
|
+
- `[test] timeout` in `bunfig.toml` — ignored.
|
|
234
|
+
- `setDefaultTimeout()` in a preload — applies to the first test file only. Bun
|
|
235
|
+
re-imports the preload per file, but the setting does not survive.
|
|
236
|
+
|
|
237
|
+
The command-line flag is the only mechanism that covers hooks as well as tests,
|
|
238
|
+
which matters because it is usually a `beforeAll` that boots the app.
|
|
239
|
+
|
|
240
|
+
### The runtime
|
|
241
|
+
|
|
242
|
+
`engines.bun` in your `package.json` is a floor, and until you enforce it, it is a
|
|
243
|
+
comment. The shell's `bun` and the project's can differ, and the difference between
|
|
244
|
+
two Bun releases is real and narrow: `Intl` formatting, the SQLite bindings and
|
|
245
|
+
`node:` compatibility all move. So a handful of currency or date assertions go red
|
|
246
|
+
and the rest pass, and you go looking for a bug in the code they touch, because
|
|
247
|
+
nothing in the failure says "wrong binary".
|
|
248
|
+
|
|
249
|
+
`bun zt test` refuses to run below the declared floor. Direct `bun test` runs get the
|
|
250
|
+
same check as a warning if you load the preload:
|
|
251
|
+
|
|
252
|
+
```toml
|
|
253
|
+
# bunfig.toml
|
|
254
|
+
[test]
|
|
255
|
+
preload = ["@zerotal/testing/preload"]
|
|
256
|
+
timeout = 30000 # note: currently ignored by Bun — pass --timeout on the command line
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Set `ZT_ALLOW_RUNTIME_MISMATCH=1` to downgrade the refusal to a warning while you
|
|
260
|
+
are mid-upgrade.
|
|
261
|
+
|
|
262
|
+
### `@zerotal/core/runtime`
|
|
263
|
+
|
|
264
|
+
The checks behind the two paragraphs above, exported so a script or a test of your
|
|
265
|
+
own can make the same assertion. `zt` runs both at the top of every command; the test
|
|
266
|
+
preload runs the floor check as a warning.
|
|
267
|
+
|
|
268
|
+
| Export | Signature | What it answers |
|
|
269
|
+
| -------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
270
|
+
| `declaredBunFloor` | `declaredBunFloor(cwd): { range, manifest } \| null` | The nearest `engines.bun` up the tree from `cwd`. |
|
|
271
|
+
| `runtimeBelowFloor` | `runtimeBelowFloor(cwd?): RuntimeFloor \| null` | Is this process below that floor? `null` when it is met or none is declared. |
|
|
272
|
+
| `runtimeBelowFloorMessage` | `runtimeBelowFloorMessage(floor): string` | The explanation to print — both versions, the manifest, and the way out. |
|
|
273
|
+
| `installedBunVersion` | `installedBunVersion(cwd): { version, manifest } \| null` | The Bun in `node_modules`, if the project installs one as a package. |
|
|
274
|
+
| `runtimeMismatch` | `runtimeMismatch(cwd?): RuntimeMismatch \| null` | Does the running Bun differ from the installed one? Compared exactly — a patch is a binary. |
|
|
275
|
+
| `runtimeMismatchMessage` | `runtimeMismatchMessage(mismatch): string` | The explanation for that one. |
|
|
276
|
+
| `runtimeMismatchAllowed` | `runtimeMismatchAllowed(): boolean` | Whether `ZT_ALLOW_RUNTIME_MISMATCH` is set. |
|
|
277
|
+
| `bunBinary` | `bunBinary(): string` | The binary to spawn a child with — `process.execPath`, never the name PATH resolves. |
|
|
278
|
+
| `RUNTIME_MISMATCH_ESCAPE` | `"ZT_ALLOW_RUNTIME_MISMATCH"` | The env var name, so a script can set it without hardcoding the string. |
|
|
279
|
+
|
|
280
|
+
`RuntimeFloor` is `{ running, required, manifest }`; `RuntimeMismatch` is
|
|
281
|
+
`{ running, installed, manifest }`. Both name the file the second version came from,
|
|
282
|
+
because "which one is wrong" is the question you actually have.
|
|
283
|
+
|
|
284
|
+
## Configuration is per-process, and `bun test` is one process
|
|
285
|
+
|
|
286
|
+
Zerotal resolves configuration once, at boot. `bun test` runs every file in the same
|
|
287
|
+
process, so **whichever file boots the app first fixes the configuration for all of
|
|
288
|
+
them.**
|
|
289
|
+
|
|
290
|
+
A test that sets an environment variable in its own `beforeAll` and then asserts on
|
|
291
|
+
the resulting behaviour passes alone and fails in the suite — or worse, passes in the
|
|
292
|
+
suite for a reason unrelated to what it claims to test:
|
|
293
|
+
|
|
294
|
+
```typescript fragment
|
|
295
|
+
// Passes alone. In a suite, the app may already be booted with CSRF on, and the
|
|
296
|
+
// three "rejects without a token" assertions below pass on a 419 they would have
|
|
297
|
+
// got anyway — never reaching the guard they name.
|
|
298
|
+
beforeAll(() => {
|
|
299
|
+
Bun.env.CSRF_DISABLED = "1";
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Assert on the _relationship_ rather than on a literal — the published origin equals
|
|
304
|
+
the configured one, whatever it is — or boot a dedicated app for the case:
|
|
305
|
+
|
|
306
|
+
```typescript fragment
|
|
307
|
+
const app = await createTestApp({ config: { app: { url: "https://example.test" } } });
|
|
308
|
+
expect(page.canonical).toBe(config("app.url"));
|
|
309
|
+
```
|
|
310
|
+
|
|
212
311
|
## Running the suite from a script
|
|
213
312
|
|
|
214
313
|
A script that gates on the tests has to read the tests' exit status, and a pipe hides it:
|
|
@@ -256,6 +355,12 @@ its full surface.
|
|
|
256
355
|
| `fake` | `typeof fake` | South-African-flavoured random data generator. |
|
|
257
356
|
| `fakeFile` | `typeof fakeFile` | Real PNG/JPEG/GIF/PDF files for upload tests. |
|
|
258
357
|
|
|
358
|
+
### Types
|
|
359
|
+
|
|
360
|
+
`TestResponseContext` is what an assertion receives, `SessionDecoder` reads the session out of a
|
|
361
|
+
response so a test can assert on it, and `FakeFile` / `TestFileInput` / `TestFormValue` are the
|
|
362
|
+
shapes a multipart submission takes in a test.
|
|
363
|
+
|
|
259
364
|
## Next steps
|
|
260
365
|
|
|
261
366
|
- [HTTP Tests](/docs/testing/http) — the full `TestApp` and `TestResponse` API.
|
package/docs/upgrade.md
CHANGED
|
@@ -169,6 +169,54 @@ these are the changes that need action. Full detail is in the
|
|
|
169
169
|
interface so pages that read them do not look unpassed; see
|
|
170
170
|
[Typed props](/docs/inertia/props#typed-props).
|
|
171
171
|
|
|
172
|
+
## 1.9 to 1.10
|
|
173
|
+
|
|
174
|
+
Three settings changed meaning. Each is quiet if it does not apply to you, and each is
|
|
175
|
+
worth thirty seconds of checking if it does.
|
|
176
|
+
|
|
177
|
+
1. **`scheduler.timezone` is honoured.** It was documented as informational and read by
|
|
178
|
+
nothing, so whatever you put there had no effect and your schedules ran in the
|
|
179
|
+
server's zone. It is now the zone every schedule is evaluated in unless the task sets
|
|
180
|
+
its own.
|
|
181
|
+
|
|
182
|
+
Its default moved from the literal `"UTC"` to **the system zone**, so an app that never
|
|
183
|
+
set the key keeps doing exactly what it did. The case to check is an app that _did_:
|
|
184
|
+
|
|
185
|
+
```ts fragment
|
|
186
|
+
// config/scheduler.ts
|
|
187
|
+
export default SchedulerConfig({ timezone: env("APP_TIMEZONE", "UTC") });
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
On a server that is not on UTC, that line used to do nothing and now moves every
|
|
191
|
+
schedule. Either set it to the zone you actually want your crons read in — which is
|
|
192
|
+
the point of the setting — or delete the key to keep the server's zone.
|
|
193
|
+
|
|
194
|
+
`bun zt schedule:list` prints each task's next run in its own zone, which is the
|
|
195
|
+
quickest way to see whether anything moved.
|
|
196
|
+
|
|
197
|
+
2. **Named rate limiters need `.trustedProxies(n)` behind a proxy.** `RateLimiter`'s
|
|
198
|
+
`.byIp()`, `.byUser()` and `.byApiKey()` ignored the proxy count entirely and read
|
|
199
|
+
`X-Forwarded-For` unconditionally. They now follow the same rule `ThrottleMiddleware`
|
|
200
|
+
already did — the header is consulted only when you say how many proxies sit in front:
|
|
201
|
+
|
|
202
|
+
```ts fragment
|
|
203
|
+
RateLimiter.for("login").limit(5).every(60).byIp().trustedProxies(1).register();
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Without it the address used is the socket's, which behind a proxy is the _proxy's_, and
|
|
207
|
+
every visitor shares one bucket. `bun zt doctor` reports any limiter that needs this —
|
|
208
|
+
it could not before, because its check exempted custom key resolvers and all three of
|
|
209
|
+
these are one.
|
|
210
|
+
|
|
211
|
+
3. **React apps using SSR need `@inertiajs/react` installed.** The same adapter your
|
|
212
|
+
browser entry point already uses. Server-side rendering now goes through its `<App>`,
|
|
213
|
+
which is what makes `<Head>` produce a title and an og: card in the HTML your server
|
|
214
|
+
actually sends. If it is missing you get a named error at render time, not a silent
|
|
215
|
+
omission.
|
|
216
|
+
|
|
217
|
+
Nothing to change if you already have it as a dependency, which every React Inertia app
|
|
218
|
+
does.
|
|
219
|
+
|
|
172
220
|
## The managed zt.ts
|
|
173
221
|
|
|
174
222
|
`zt.ts` is framework-managed — the header says _do not modify_. If a release
|
package/docs/validator.md
CHANGED
|
@@ -540,6 +540,15 @@ res.assertUnprocessable(); // 422 for a JSON request
|
|
|
540
540
|
| `validate` | `validate(ctx, factory): Promise<Infer<…>>` | One-off HTTP validation; throws on failure. |
|
|
541
541
|
| `Validator.check` | `check(data, factory): ValidationOutcome<…>` | Non-HTTP validation; returns a result, never throws. |
|
|
542
542
|
|
|
543
|
+
## Types
|
|
544
|
+
|
|
545
|
+
| Type | What it is |
|
|
546
|
+
| --------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
547
|
+
| `ValidationErrors` | The error bag — field name to messages, which is what `withErrors()` and the `errors` shared prop carry. |
|
|
548
|
+
| `FieldRuleDefinition` | One field's rules as declared. |
|
|
549
|
+
| `InferFieldType<R>` | The type a rule set produces, so validated data is typed rather than `unknown`. |
|
|
550
|
+
| `PrecognitionResponseError` | What a precognition request returns when a field fails ahead of submission. |
|
|
551
|
+
|
|
543
552
|
## Next steps
|
|
544
553
|
|
|
545
554
|
- [Requests Context](/docs/context#reading-input) — read the input that `FormRequest` validates.
|
package/docs/view.md
CHANGED
|
@@ -444,6 +444,12 @@ res.assertDontSee("Draft"); // unpublished posts stay hidden
|
|
|
444
444
|
| `Children` | Type for the `children` prop. |
|
|
445
445
|
| `HttpContext<T>` | The request context a view component or controller action receives; route params and model bindings live on `ctx.params`. |
|
|
446
446
|
|
|
447
|
+
## Types
|
|
448
|
+
|
|
449
|
+
`ViewComponent` is what `view()` accepts — a function taking the request context and your props,
|
|
450
|
+
returning markup. `ViewLayout` is the wrapper form a layout takes. Both are exported so a helper
|
|
451
|
+
that returns a component, or a registry that holds several, can be typed.
|
|
452
|
+
|
|
447
453
|
## Next steps
|
|
448
454
|
|
|
449
455
|
- [Flow](/docs/flow) — server-driven interactive components over WebSocket.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zerotal/arch",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"maturity": "stable",
|
|
6
6
|
"private": false,
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"typecheck": "tsc --noEmit"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@zerotal/core": "1.
|
|
38
|
+
"@zerotal/core": "1.10.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"typescript": "^5.8.0",
|
|
42
|
-
"@zerotal/orm": "1.
|
|
42
|
+
"@zerotal/orm": "1.10.0"
|
|
43
43
|
},
|
|
44
44
|
"description": "The Zerotal agent surface — an MCP server that hands coding agents the framework's machine-readable truth: exact API signatures, live routes and schema, version-matched docs, and `zt doctor`.",
|
|
45
45
|
"keywords": [
|
|
@@ -150,7 +150,7 @@ export interface GuidelineOptions {
|
|
|
150
150
|
* How this project is configured, from {@link detectShape}. Omitted, the block
|
|
151
151
|
* is what it always was — a function of the package list.
|
|
152
152
|
*/
|
|
153
|
-
shape?: ProjectShape;
|
|
153
|
+
shape?: ProjectShape | undefined;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
/**
|
package/src/mcp/stdio.ts
CHANGED
|
@@ -27,11 +27,11 @@ import type { JsonRpcResponse } from "./types.ts";
|
|
|
27
27
|
export interface StdioOptions {
|
|
28
28
|
server: McpServer;
|
|
29
29
|
/** Byte source. Defaults to this process's stdin. */
|
|
30
|
-
input?: ReadableStream<Uint8Array
|
|
30
|
+
input?: ReadableStream<Uint8Array> | undefined;
|
|
31
31
|
/** Frame sink. Defaults to this process's stdout. Injected in tests. */
|
|
32
|
-
write?: (frame: string) => void;
|
|
32
|
+
write?: ((frame: string) => void) | undefined;
|
|
33
33
|
/** Diagnostics sink. Defaults to stderr — never stdout. */
|
|
34
|
-
log?: (message: string) => void;
|
|
34
|
+
log?: ((message: string) => void) | undefined;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
package/src/tools/_probe.ts
CHANGED
|
@@ -35,8 +35,8 @@ export interface ProbeRunner {
|
|
|
35
35
|
|
|
36
36
|
export interface SpawnProbeOptions {
|
|
37
37
|
/** Where to start looking for the app. Defaults to the server's working directory. */
|
|
38
|
-
cwd?: string;
|
|
39
|
-
timeoutMs?: number;
|
|
38
|
+
cwd?: string | undefined;
|
|
39
|
+
timeoutMs?: number | undefined;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|