@zerotal/arch 1.8.0 → 1.9.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 (59) hide show
  1. package/docs/admin/actions.md +15 -0
  2. package/docs/admin/auth.md +10 -0
  3. package/docs/admin/dashboard.md +12 -0
  4. package/docs/admin/extending-ui.md +14 -0
  5. package/docs/admin/forms.md +15 -0
  6. package/docs/admin/operations.md +12 -0
  7. package/docs/admin/resources.md +6 -0
  8. package/docs/admin/tables.md +21 -0
  9. package/docs/audit.md +5 -0
  10. package/docs/authentication.md +110 -1
  11. package/docs/broadcasting/references.md +17 -0
  12. package/docs/cache.md +5 -0
  13. package/docs/carbon.md +5 -0
  14. package/docs/changelog.md +159 -1
  15. package/docs/client/index.md +17 -0
  16. package/docs/commands.md +6 -0
  17. package/docs/components.md +73 -0
  18. package/docs/config-system.md +54 -0
  19. package/docs/cookies.md +6 -0
  20. package/docs/deployment.md +103 -13
  21. package/docs/devtools.md +5 -0
  22. package/docs/email-verification.md +26 -1
  23. package/docs/encryption.md +21 -0
  24. package/docs/errors.md +2 -0
  25. package/docs/flow/components.md +54 -0
  26. package/docs/flow/forms.md +57 -0
  27. package/docs/flow/references.md +14 -0
  28. package/docs/getting-started.md +38 -0
  29. package/docs/health.md +19 -0
  30. package/docs/helpers.md +99 -0
  31. package/docs/i18n.md +5 -0
  32. package/docs/inertia/props.md +70 -0
  33. package/docs/lock.md +15 -0
  34. package/docs/logger.md +38 -0
  35. package/docs/migrations.md +47 -0
  36. package/docs/monitor.md +59 -0
  37. package/docs/notifications.md +11 -0
  38. package/docs/orm/casts.md +6 -0
  39. package/docs/orm/lifecycle.md +18 -0
  40. package/docs/orm/queries.md +10 -0
  41. package/docs/orm/relationships.md +30 -0
  42. package/docs/queue.md +10 -0
  43. package/docs/rate-limiting.md +39 -4
  44. package/docs/responses.md +23 -0
  45. package/docs/routing.md +16 -0
  46. package/docs/scheduler.md +11 -0
  47. package/docs/session.md +6 -0
  48. package/docs/social.md +10 -0
  49. package/docs/storage.md +21 -0
  50. package/docs/support-policy.md +13 -1
  51. package/docs/telemetry.md +8 -0
  52. package/docs/tenancy.md +6 -0
  53. package/docs/testing/index.md +6 -0
  54. package/docs/validator.md +9 -0
  55. package/docs/view.md +6 -0
  56. package/package.json +3 -3
  57. package/src/install/ArchInstallCommand.ts +4 -2
  58. package/src/probe/topics.ts +40 -0
  59. package/src/provider/ArchProvider.ts +2 -2
package/docs/routing.md CHANGED
@@ -1081,6 +1081,22 @@ Every route registration returns a chainable handle:
1081
1081
  | `bun zt route:list` | — | Print every route (`-m` method, `-p` path, `--name`, `-v` middleware). |
1082
1082
  | `bun zt route:types` | `--check` | Write `types/routes.generated.ts`; `--check` fails when it is stale. |
1083
1083
 
1084
+ ## Types
1085
+
1086
+ | Type | What it is |
1087
+ | -------------------- | --------------------------------------------------------------------------------------------------------- |
1088
+ | `HttpMethod` | The verbs a route can answer on. |
1089
+ | `HttpResponse` | What a handler may return when it does not write to `ctx.response`. |
1090
+ | `ControllerResponse` | A controller action's return type — `void`, a `ResponseBuilder`, or a `MarkdownBuilder`. |
1091
+ | `RouteParams` | The params a pattern captures, as a record. |
1092
+ | `ParamsOf<P>` | The params of one pattern, derived from the pattern string — what makes `route()` refuse a missing `:id`. |
1093
+ | `RouteRegistry` | The generated name → pattern table, augmented by `types/routes.generated.ts`. |
1094
+ | `RouteMiddleware` | What a route's middleware list accepts — a class, an array, or the method map. |
1095
+ | `GroupOptions` | `Router.group({ prefix, middleware, domain })`. |
1096
+ | `RoutingConfig` | The `routing` config namespace. |
1097
+ | `FileRoutingConfig` | File-router settings within it. |
1098
+ | `WebSocketHandlers` | The handler set a WebSocket route registers. |
1099
+
1084
1100
  ## Next steps
1085
1101
 
1086
1102
  - [Controllers](/docs/controllers) — move route logic out of closures.
package/docs/scheduler.md CHANGED
@@ -525,6 +525,17 @@ The `Scheduler` facade resolves the `scheduler` container binding — a
525
525
  | `lastDurationMs` | `get lastDurationMs(): number \| undefined` | Duration of the last run in ms. |
526
526
  | `isRunning` | `get isRunning(): boolean` | `true` while a run is in flight. |
527
527
 
528
+ ## Types
529
+
530
+ | Type | What it is |
531
+ | --------------------------------------- | -------------------------------------------------------------------------------------------- |
532
+ | `CronExpression` | The schedule string a task declares. |
533
+ | `TaskGuard` | A condition deciding whether a due run actually happens — a feature flag, a leader election. |
534
+ | `TaskHook` | What runs before or after a task. |
535
+ | `ScheduleRunRecord`, `ScheduleRunStore` | One recorded run, and where the log is kept. |
536
+ | `RunLogConfig` | How much of that log is retained. |
537
+ | `OutputMailer` | Sending a task's output somewhere when it finishes. |
538
+
528
539
  ## Next steps
529
540
 
530
541
  - [Queue](/docs/queue) — schedules typically dispatch jobs; the worker runs both.
package/docs/session.md CHANGED
@@ -444,6 +444,12 @@ try {
444
444
  The fix is nearly always to store an identifier rather than the object, or to
445
445
  move to a server-side driver where the cookie holds only the session id.
446
446
 
447
+ ## Types
448
+
449
+ `SessionDriver` is the storage contract — implement it to keep sessions somewhere the built-ins
450
+ do not reach. `SessionPayload` is what a driver loads and saves: the id and the data bag.
451
+ `SessionAccessor` is the request-scoped handle `ctx.session` exposes.
452
+
447
453
  ## Next steps
448
454
 
449
455
  - [Middleware](/docs/middleware) — where `SessionMiddleware` and `CsrfMiddleware`
package/docs/social.md CHANGED
@@ -516,6 +516,16 @@ validation errors is exactly the string you forward to `?error=`.
516
516
  | `OAuthUserFetchError` | provider message | User-profile fetch failed. |
517
517
  | `AppleClientSecretError` | Apple credentials message | Apple driver has neither a JWT nor raw key trio. |
518
518
 
519
+ ## Types
520
+
521
+ | Type | What it is |
522
+ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
523
+ | `OAuth2Config` | What a provider entry in `config/services.ts` holds — id, secret, redirect URI. |
524
+ | `AppleOAuth2Config` | Apple's, which needs more: a team id, a key id, and the private key it signs with. |
525
+ | `SocialSession` | What the driver stashes between the redirect out and the callback back. |
526
+ | `GoogleDriver` | Exported so a custom driver can extend it rather than reimplement OIDC. |
527
+ | `FakeSocialDriver` | What `Social.fake()` installs — returns a canned profile instead of exchanging a code. See [Mocking](/docs/testing/mocking). |
528
+
519
529
  ## Next steps
520
530
 
521
531
  - [Authentication](/docs/authentication) — establish the session after a social login.
package/docs/storage.md CHANGED
@@ -626,6 +626,27 @@ Manager and exports from `zerotal/storage`:
626
626
  | `StorageProvider` | `class` | Registers the `storage` binding. |
627
627
  | `DiskNotConfiguredError` | `class` | Thrown when a named disk is missing from config. |
628
628
 
629
+ ### Types
630
+
631
+ The config shapes are exported, so a `config/storage.ts` can be annotated and a helper that
632
+ builds disk options can be typed:
633
+
634
+ | Type | What it is |
635
+ | --------------------- | -------------------------------------------------------------------------- |
636
+ | `DiskConfig` | The discriminated union of every disk config — `local` and `s3` today. |
637
+ | `LocalDiskConfig` | A local disk: `root`, plus `serve` when it is reachable over HTTP. |
638
+ | `S3DiskConfig` | An S3 disk: credentials, `region`, `bucket`, and an optional public `url`. |
639
+ | `DiskServeConfig` | How a disk is exposed over HTTP — the `serve` block `public` uses. |
640
+ | `StorageFilesOptions` | Options for `StorageFilesMiddleware`, which serves a disk's files. |
641
+ | `FakeStoredFile` | What a faked disk records in tests, for asserting on what was written. |
642
+
643
+ `StorageError` is the base every storage failure extends, and `UnsupportedOperationError` is
644
+ what a driver throws for something it genuinely cannot do — a local disk asked for a signed
645
+ URL, for instance — rather than failing quietly and returning nothing.
646
+
647
+ `S3Driver` is exported so a driver for another S3-compatible service can extend it rather than
648
+ reimplement request signing.
649
+
629
650
  ## Next steps
630
651
 
631
652
  - [Validator](/docs/validator) — validate file uploads before storing them.
@@ -109,7 +109,19 @@ outcomes — promote, keep with a new date and the reason, or withdraw.
109
109
 
110
110
  | Package | Now | Reviewed by |
111
111
  | ------------- | -------------- | ----------- |
112
- | `@zerotal/ai` | `experimental` | **1.9.0** |
112
+ | `@zerotal/ai` | `experimental` | **1.11.0** |
113
+
114
+ `@zerotal/ai`'s date moved once, from 1.9.0, and the reason is the same one that made it
115
+ experimental in the first place: **it graduates in the release after its first real users, and
116
+ it has not had them yet.** Promoting on a date rather than on evidence is how a label becomes
117
+ decoration — a `stable` promise is only worth making about an API that something has pushed
118
+ against.
119
+
120
+ The date is a forcing function, not a prediction. It moved once; a second move needs a better
121
+ reason than the first, or the honest answer is to withdraw the package rather than keep
122
+ re-dating it. Its surface triage and the tests for its SSE parser and prompt redaction are worth
123
+ doing meanwhile, and are tracked separately — they improve the package whichever way the review
124
+ goes, and they are what would otherwise turn the deadline into a scramble.
113
125
 
114
126
  `@zerotal/arch` held `beta` with the same date, was reviewed ahead of it, and is
115
127
  `stable` — the release that carried the promotion is the one its
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`.
@@ -256,6 +256,12 @@ its full surface.
256
256
  | `fake` | `typeof fake` | South-African-flavoured random data generator. |
257
257
  | `fakeFile` | `typeof fakeFile` | Real PNG/JPEG/GIF/PDF files for upload tests. |
258
258
 
259
+ ### Types
260
+
261
+ `TestResponseContext` is what an assertion receives, `SessionDecoder` reads the session out of a
262
+ response so a test can assert on it, and `FakeFile` / `TestFileInput` / `TestFormValue` are the
263
+ shapes a multipart submission takes in a test.
264
+
259
265
  ## Next steps
260
266
 
261
267
  - [HTTP Tests](/docs/testing/http) — the full `TestApp` and `TestResponse` API.
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.8.0",
3
+ "version": "1.9.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.8.0"
38
+ "@zerotal/core": "1.9.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "typescript": "^5.8.0",
42
- "@zerotal/orm": "1.8.0"
42
+ "@zerotal/orm": "1.9.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": [
@@ -16,7 +16,7 @@ import type { Application } from "@zerotal/core";
16
16
  import { ArchConfig } from "../config.ts";
17
17
  import type { ArchConfigShape } from "../config.ts";
18
18
  import { NoProjectRootError } from "../errors.ts";
19
- import { installedPackages } from "../probe/topics.ts";
19
+ import { declaredPackages } from "../probe/topics.ts";
20
20
  import { detectAgents } from "./detect.ts";
21
21
  import { agentsPreamble, buildGuidelines, claudeShim } from "./guidelines.ts";
22
22
  import { detectShape } from "./shape.ts";
@@ -54,7 +54,9 @@ export class ArchInstallCommand extends Command {
54
54
  const config = this._config();
55
55
 
56
56
  const detected = await detectAgents(root);
57
- const packages = (await installedPackages(root)).map((pkg) => pkg.name);
57
+ // Declared, not installed: what an install layout hoists into node_modules
58
+ // differs between machines, and the guidance must not.
59
+ const packages = (await declaredPackages(root)).map((pkg) => pkg.name);
58
60
  // Read off disk rather than from a booted app: a project that will not boot is
59
61
  // often exactly why someone is installing the agent surface.
60
62
  const shape = await detectShape(root);
@@ -304,3 +304,43 @@ export async function installedPackages(root = process.cwd()): Promise<Installed
304
304
  function controllerLabel(rawName: string): string {
305
305
  return rawName.startsWith("FileRoute<") ? "file" : rawName;
306
306
  }
307
+
308
+ /**
309
+ * The `@zerotal/*` packages this app *declares*, with their installed versions.
310
+ *
311
+ * {@link installedPackages} answers "what is on disk here", which is the right
312
+ * answer for a version report and the wrong one for guidance. Two things get into
313
+ * `node_modules/@zerotal` besides an app's own dependencies: transitive ones, and
314
+ * whatever an install layout decides to hoist — and layouts disagree. The same
315
+ * commit of one app resolved seventeen packages on a developer's machine and
316
+ * eleven on its server, because the server hoisted the shared ones to the
317
+ * workspace root instead.
318
+ *
319
+ * That is not a cosmetic difference when the list decides what an agent is told.
320
+ * `@zerotal/queue` arriving as a transitive dependency does not mean the app runs
321
+ * jobs, and a block explaining where jobs live is confidently wrong for an app
322
+ * that has none. It also made the generated file unstable between machines, so a
323
+ * check comparing it against the project could not tell drift from a difference
324
+ * of layout.
325
+ *
326
+ * Direct dependencies only, which is both deterministic and the honest reading of
327
+ * "what this app has": you import what you declare.
328
+ */
329
+ export async function declaredPackages(root = process.cwd()): Promise<InstalledPackage[]> {
330
+ let declared: Set<string>;
331
+ try {
332
+ const manifest = (await Bun.file(`${root}/package.json`).json()) as {
333
+ dependencies?: Record<string, string>;
334
+ devDependencies?: Record<string, string>;
335
+ };
336
+ declared = new Set([
337
+ ...Object.keys(manifest.dependencies ?? {}),
338
+ ...Object.keys(manifest.devDependencies ?? {}),
339
+ ]);
340
+ } catch {
341
+ // No manifest to read — every installed package is as good a guess as any.
342
+ return installedPackages(root);
343
+ }
344
+
345
+ return (await installedPackages(root)).filter((pkg) => declared.has(pkg.name));
346
+ }
@@ -142,7 +142,7 @@ export async function agentsFileCheck(
142
142
  };
143
143
  }
144
144
 
145
- const [{ buildGuidelines }, { detectShape }, { installedPackages }, markers] = await Promise.all([
145
+ const [{ buildGuidelines }, { detectShape }, { declaredPackages }, markers] = await Promise.all([
146
146
  import("../install/guidelines.ts"),
147
147
  import("../install/shape.ts"),
148
148
  import("../probe/topics.ts"),
@@ -160,7 +160,7 @@ export async function agentsFileCheck(
160
160
  };
161
161
  }
162
162
 
163
- const packages = (await installedPackages(root)).map((pkg) => pkg.name);
163
+ const packages = (await declaredPackages(root)).map((pkg) => pkg.name);
164
164
  const shape = await detectShape(root);
165
165
  const expected = buildGuidelines({
166
166
  packages,