@zerotal/arch 1.7.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/CHANGELOG.md +31 -0
- package/LICENSE +21 -0
- package/README.md +67 -0
- package/api-surface.md +283 -0
- package/docs/about.md +676 -0
- package/docs/admin/actions.md +183 -0
- package/docs/admin/auth.md +89 -0
- package/docs/admin/dashboard.md +155 -0
- package/docs/admin/extending-ui.md +229 -0
- package/docs/admin/extending.md +205 -0
- package/docs/admin/forms.md +155 -0
- package/docs/admin/index.md +144 -0
- package/docs/admin/operations.md +224 -0
- package/docs/admin/references.md +174 -0
- package/docs/admin/resources.md +132 -0
- package/docs/admin/structure.md +158 -0
- package/docs/admin/tables.md +185 -0
- package/docs/admin/testing.md +138 -0
- package/docs/ai.md +373 -0
- package/docs/application.md +435 -0
- package/docs/arch.md +263 -0
- package/docs/assets.md +755 -0
- package/docs/audit.md +367 -0
- package/docs/authentication.md +1000 -0
- package/docs/authorization.md +529 -0
- package/docs/broadcasting/channels.md +170 -0
- package/docs/broadcasting/client.md +108 -0
- package/docs/broadcasting/events.md +190 -0
- package/docs/broadcasting/index.md +163 -0
- package/docs/broadcasting/references.md +84 -0
- package/docs/broadcasting/testing.md +132 -0
- package/docs/cache.md +387 -0
- package/docs/carbon.md +830 -0
- package/docs/changelog.md +465 -0
- package/docs/client/auth.md +113 -0
- package/docs/client/errors.md +139 -0
- package/docs/client/files.md +118 -0
- package/docs/client/index.md +128 -0
- package/docs/client/references.md +58 -0
- package/docs/client/requests.md +131 -0
- package/docs/client/resilience.md +141 -0
- package/docs/client/testing.md +146 -0
- package/docs/commands.md +450 -0
- package/docs/components.md +2427 -0
- package/docs/config-system.md +258 -0
- package/docs/container.md +596 -0
- package/docs/context.md +579 -0
- package/docs/contributing.md +164 -0
- package/docs/controllers.md +354 -0
- package/docs/conventions.md +466 -0
- package/docs/cookies.md +101 -0
- package/docs/csrf.md +206 -0
- package/docs/database.md +476 -0
- package/docs/deployment.md +434 -0
- package/docs/devtools.md +1013 -0
- package/docs/email-verification.md +320 -0
- package/docs/encryption.md +380 -0
- package/docs/errors.md +317 -0
- package/docs/events.md +420 -0
- package/docs/flow/components.md +846 -0
- package/docs/flow/decorators.md +458 -0
- package/docs/flow/events.md +420 -0
- package/docs/flow/forms.md +551 -0
- package/docs/flow/index.md +568 -0
- package/docs/flow/layouts.md +793 -0
- package/docs/flow/lifecycle.md +336 -0
- package/docs/flow/pagination.md +402 -0
- package/docs/flow/performance.md +262 -0
- package/docs/flow/references.md +317 -0
- package/docs/flow/routing.md +550 -0
- package/docs/flow/testing.md +447 -0
- package/docs/getting-started.md +286 -0
- package/docs/health.md +269 -0
- package/docs/helpers.md +423 -0
- package/docs/i18n.md +414 -0
- package/docs/index.md +65 -0
- package/docs/inertia/build.md +95 -0
- package/docs/inertia/devtools.md +197 -0
- package/docs/inertia/index.md +267 -0
- package/docs/inertia/middleware.md +81 -0
- package/docs/inertia/props.md +507 -0
- package/docs/inertia/references.md +144 -0
- package/docs/inertia/rendering.md +189 -0
- package/docs/inertia/ssr.md +102 -0
- package/docs/inspirations.md +257 -0
- package/docs/lifecycle.md +331 -0
- package/docs/lock.md +454 -0
- package/docs/logger.md +517 -0
- package/docs/media.md +582 -0
- package/docs/middleware.md +509 -0
- package/docs/migrations.md +529 -0
- package/docs/monitor.md +377 -0
- package/docs/notifications.md +848 -0
- package/docs/orm/casts.md +365 -0
- package/docs/orm/factories.md +231 -0
- package/docs/orm/index.md +663 -0
- package/docs/orm/lifecycle.md +421 -0
- package/docs/orm/queries.md +692 -0
- package/docs/orm/relationships.md +440 -0
- package/docs/orm/serialization.md +251 -0
- package/docs/package-development.md +536 -0
- package/docs/pagination.md +340 -0
- package/docs/password-reset.md +335 -0
- package/docs/providers.md +447 -0
- package/docs/query-builder.md +465 -0
- package/docs/queue.md +549 -0
- package/docs/rate-limiting.md +253 -0
- package/docs/responses.md +336 -0
- package/docs/roles-and-2fa.md +418 -0
- package/docs/routing.md +1008 -0
- package/docs/scaffolding.md +335 -0
- package/docs/scheduler.md +533 -0
- package/docs/seeding.md +229 -0
- package/docs/session.md +451 -0
- package/docs/social.md +524 -0
- package/docs/storage.md +622 -0
- package/docs/structure.md +230 -0
- package/docs/support-policy.md +114 -0
- package/docs/telemetry.md +505 -0
- package/docs/tenancy.md +608 -0
- package/docs/testing/browser.md +153 -0
- package/docs/testing/console.md +130 -0
- package/docs/testing/database.md +238 -0
- package/docs/testing/flow-browser.md +216 -0
- package/docs/testing/http.md +441 -0
- package/docs/testing/index.md +240 -0
- package/docs/testing/mocking.md +410 -0
- package/docs/upgrade.md +192 -0
- package/docs/validator.md +549 -0
- package/docs/view.md +453 -0
- package/package.json +60 -0
- package/src/bin/mcp.ts +73 -0
- package/src/config.ts +63 -0
- package/src/errors.ts +27 -0
- package/src/index.ts +56 -0
- package/src/install/ArchInstallCommand.ts +189 -0
- package/src/install/detect.ts +98 -0
- package/src/install/guidelines.ts +290 -0
- package/src/install/markers.ts +77 -0
- package/src/install/mcpConfig.ts +81 -0
- package/src/mcp/index.ts +27 -0
- package/src/mcp/jsonrpc.ts +133 -0
- package/src/mcp/server.ts +307 -0
- package/src/mcp/stdio.ts +111 -0
- package/src/mcp/types.ts +146 -0
- package/src/probe/ArchProbeCommand.ts +48 -0
- package/src/probe/sentinel.ts +13 -0
- package/src/probe/topics.ts +291 -0
- package/src/provider/ArchProvider.ts +101 -0
- package/src/tools/_probe.ts +164 -0
- package/src/tools/apiSurface.ts +241 -0
- package/src/tools/appInfo.ts +88 -0
- package/src/tools/baselines.ts +210 -0
- package/src/tools/context.ts +37 -0
- package/src/tools/doctor.ts +93 -0
- package/src/tools/index.ts +50 -0
- package/src/tools/logs.ts +265 -0
- package/src/tools/routes.ts +133 -0
- package/src/tools/schema.ts +119 -0
- package/src/tools/searchDocs.ts +345 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Inertia Props
|
|
3
|
+
description: Pass data to a page — eager, lazy, deferred, and merged props, plus the shared props every page receives.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Data props
|
|
7
|
+
|
|
8
|
+
Zerotal implements the full **Inertia v3** server-side data-props protocol. These features let you
|
|
9
|
+
control _which_ props are sent, _when_ they're evaluated, and _how_ the client merges them — the
|
|
10
|
+
foundation for fast pages, "load more" lists, and deferred content. They work with the stock
|
|
11
|
+
`@inertiajs/react` / `@inertiajs/vue3` clients; no extra client setup is required.
|
|
12
|
+
|
|
13
|
+
Import the helpers directly or use the `Inertia` facade:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
// in a controller
|
|
17
|
+
import { inertia, optional, always, defer, merge, deepMerge } from "@zerotal/inertia";
|
|
18
|
+
// or
|
|
19
|
+
import { Inertia } from "@zerotal/inertia";
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
> **Tip** — Not sure which wrapper to reach for? The short version: a bare value is
|
|
23
|
+
> always sent and always evaluated; a `() => …` function is always sent but only
|
|
24
|
+
> evaluated when included; `optional()` is sent only when a partial reload asks for it;
|
|
25
|
+
> `defer()` loads after first paint; `merge()`/`scroll()` combine new data with what
|
|
26
|
+
> the client already has.
|
|
27
|
+
|
|
28
|
+
## Partial reloads
|
|
29
|
+
|
|
30
|
+
On a visit to the _same page_, the client can request a subset of props with `only` / `except`.
|
|
31
|
+
The server evaluates and returns just those props; the client keeps the rest. Zerotal reads the
|
|
32
|
+
`X-Inertia-Partial-Data` / `X-Inertia-Partial-Except` / `X-Inertia-Partial-Component` headers
|
|
33
|
+
automatically — you don't write any special code for the route, you just make props lazy.
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
// client
|
|
37
|
+
router.reload({ only: ["users"] });
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Lazy evaluation
|
|
41
|
+
|
|
42
|
+
Wrap optional data in a function so it's only evaluated when actually included:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// in a controller
|
|
46
|
+
return inertia("Users/Index", {
|
|
47
|
+
users: () => User.all(), // evaluated every full visit, and on partial reloads that ask for it
|
|
48
|
+
companies: () => Company.all(),
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
A function prop is **always sent** on a full visit but only **evaluated when included**, so excluding
|
|
53
|
+
it from a partial reload also skips the query.
|
|
54
|
+
|
|
55
|
+
## Optional and always props
|
|
56
|
+
|
|
57
|
+
`optional(fn)` — never sent on a normal visit; only when explicitly requested via `only`. Ideal for
|
|
58
|
+
expensive data the page can load on demand.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// in a controller
|
|
62
|
+
return inertia("Users/Index", {
|
|
63
|
+
users: optional(() => User.all()), // only when reloaded with only: ["users"]
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`always(value)` — always sent, even when a partial reload's `only`/`except` would exclude it. (The
|
|
68
|
+
shared `errors` bag uses this internally.)
|
|
69
|
+
|
|
70
|
+
| Approach | Full visit | Partial reload | Evaluated |
|
|
71
|
+
| ---------------------------- | ---------- | -------------- | ---------------- |
|
|
72
|
+
| `User.all()` | always | optionally | always |
|
|
73
|
+
| `() => User.all()` | always | optionally | only when needed |
|
|
74
|
+
| `optional(() => User.all())` | never | optionally | only when needed |
|
|
75
|
+
| `always(User.all())` | always | always | always |
|
|
76
|
+
|
|
77
|
+
> **Tip** — `lazy(fn)` is an alias of `optional(fn)`, kept for parity with Inertia's
|
|
78
|
+
> historical name. New code should prefer `optional`.
|
|
79
|
+
|
|
80
|
+
## Deferred props
|
|
81
|
+
|
|
82
|
+
`defer(fn)` excludes a prop from the initial render and tells the client to fetch it in a follow-up
|
|
83
|
+
request — great for below-the-fold or slow data. Group props to control parallelism (each group is
|
|
84
|
+
one request).
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// in a controller
|
|
88
|
+
return inertia("Users/Index", {
|
|
89
|
+
users: () => User.all(),
|
|
90
|
+
permissions: defer(() => Permission.all()), // default group
|
|
91
|
+
teams: defer(() => Team.all(), "attributes"), // grouped together…
|
|
92
|
+
projects: defer(() => Project.all(), "attributes"), // …fetched in one request
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The first response carries a `deferredProps` map; the client then partial-reloads each group. On the
|
|
97
|
+
client, wrap the UI in `<Deferred>`:
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
// in a page component
|
|
101
|
+
import { Deferred } from "@inertiajs/react";
|
|
102
|
+
|
|
103
|
+
<Deferred data="permissions" fallback={<div>Loading…</div>}>
|
|
104
|
+
<Permissions />
|
|
105
|
+
</Deferred>;
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Error handling
|
|
109
|
+
|
|
110
|
+
Pass `{ rescue: true }` so a thrown error is swallowed and the key reported in `rescuedProps` (the
|
|
111
|
+
client renders the `<Deferred rescue>` slot) instead of failing the whole response:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
// in a controller
|
|
115
|
+
permissions: defer(() => Permission.all(), "default", { rescue: true }),
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Merging props
|
|
119
|
+
|
|
120
|
+
By default a reloaded prop _replaces_ the client value. `merge()` / `deepMerge()` make the client
|
|
121
|
+
**combine** the new data with what it already has — the basis for paginated "load more" lists. Merging
|
|
122
|
+
only happens on partial reloads (full visits always replace).
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
// in a controller
|
|
126
|
+
return inertia("Feed", {
|
|
127
|
+
posts: merge(() => Post.paginate(15, page)), // append at root
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Chainable targeting:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
// in a controller
|
|
135
|
+
merge(users).append("data").matchOn("data.id"); // append to users.data, replace items matching id
|
|
136
|
+
merge(items).prepend(); // prepend at root
|
|
137
|
+
deepMerge(chat).matchOn("messages.id"); // deep-merge the whole structure
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
These populate the page object's `mergeProps` / `prependProps` / `deepMergeProps` / `matchPropsOn`,
|
|
141
|
+
which the client uses to merge correctly. Use the client's `reset: ["posts"]` option to clear a prop
|
|
142
|
+
before merging fresh data (e.g. on a new search) — Zerotal honors the `X-Inertia-Reset` header.
|
|
143
|
+
|
|
144
|
+
## Infinite scroll
|
|
145
|
+
|
|
146
|
+
`scroll(paginator)` is purpose-built for the client's `<InfiniteScroll>` component. It merges the
|
|
147
|
+
paginator's `data` array and emits a `scrollProps` entry describing the current/next/previous page,
|
|
148
|
+
so the client knows when (and which way) to load more:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
// app/controllers/PostController.ts
|
|
152
|
+
import { inertia, scroll } from "@zerotal/inertia";
|
|
153
|
+
|
|
154
|
+
async index(ctx: HttpContext) {
|
|
155
|
+
const page = Number(ctx.query("page", "1"));
|
|
156
|
+
return inertia("Posts/Index", {
|
|
157
|
+
posts: scroll(() => Post.paginate(15, page)), // merges posts.data, emits scrollProps
|
|
158
|
+
// scroll(() => Post.paginate(15, page), { pageName: "p", dataPath: "data" })
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
This produces a page object like:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"mergeProps": ["posts.data"],
|
|
168
|
+
"scrollProps": {
|
|
169
|
+
"posts": { "pageName": "page", "previousPage": null, "nextPage": 2, "currentPage": 1 }
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`next`/`previous` page are derived from the paginator (`currentPage`/`page`, `lastPage`, or
|
|
175
|
+
`total`+`perPage`). When the user scrolls **up**, the client sends
|
|
176
|
+
`X-Inertia-Infinite-Scroll-Merge-Intent: prepend` and Zerotal prepends the new page instead of
|
|
177
|
+
appending — no controller change needed. On the client:
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
// in a page component
|
|
181
|
+
import { InfiniteScroll } from "@inertiajs/react";
|
|
182
|
+
|
|
183
|
+
<InfiniteScroll data="posts">
|
|
184
|
+
{posts.data.map((post) => (
|
|
185
|
+
<Post key={post.id} post={post} />
|
|
186
|
+
))}
|
|
187
|
+
</InfiniteScroll>;
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Once props
|
|
191
|
+
|
|
192
|
+
Chain `.once()` onto an optional/merge/defer prop so it's resolved a single time and remembered by the
|
|
193
|
+
client across navigations. The client sends `X-Inertia-Except-Once-Props` with the keys it already
|
|
194
|
+
holds; the server skips re-resolving them.
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
// in a controller
|
|
198
|
+
return inertia("Billing/Plans", {
|
|
199
|
+
plans: optional(() => Plan.all()).once(),
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## History encryption
|
|
204
|
+
|
|
205
|
+
Encrypt the current page's browser history state (so sensitive data isn't readable from
|
|
206
|
+
`window.history` after logout), or clear it:
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
// in a controller
|
|
210
|
+
import { Inertia } from "@zerotal/inertia";
|
|
211
|
+
|
|
212
|
+
Inertia.encryptHistory(); // encrypt this page's history entry
|
|
213
|
+
Inertia.clearHistory(); // e.g. in your logout action
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Set a global default in config:
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
// config/inertia.ts
|
|
220
|
+
import { InertiaConfig } from "@zerotal/inertia";
|
|
221
|
+
|
|
222
|
+
export default InertiaConfig({ encryptHistory: true });
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
> **Danger** — Without history encryption, sensitive props remain readable from
|
|
226
|
+
> `window.history` after a user logs out. Call `Inertia.clearHistory()` in your logout
|
|
227
|
+
> action (or set `encryptHistory: true`) for pages that render private data.
|
|
228
|
+
|
|
229
|
+
## External & fragment redirects
|
|
230
|
+
|
|
231
|
+
`Inertia.location(url)` performs a full-page visit to an external URL — a `409` with
|
|
232
|
+
`X-Inertia-Location` for Inertia requests, a `302` otherwise:
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
// in a controller
|
|
236
|
+
return Inertia.location("https://billing.stripe.com/session/abc");
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Redirects whose target carries a URL fragment (`/page#section`) are automatically converted to a
|
|
240
|
+
`409` + `X-Inertia-Redirect` on Inertia requests, so the client performs an Inertia visit that
|
|
241
|
+
preserves the fragment.
|
|
242
|
+
|
|
243
|
+
## Precognition
|
|
244
|
+
|
|
245
|
+
Precognition lets a form validate against the server's real rules **without running the controller's
|
|
246
|
+
side effects** — perfect for live, inline validation. Register `PrecognitionMiddleware` and use a
|
|
247
|
+
`FormRequest`; when the client sends `Precognition: true`, `FormRequest.validate()` short-circuits
|
|
248
|
+
with a `204` (valid) or `422` (errors), optionally limited to the fields in `Precognition-Validate-Only`.
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
// bootstrap — register PrecognitionMiddleware globally
|
|
252
|
+
import { PrecognitionMiddleware } from "@zerotal/inertia";
|
|
253
|
+
|
|
254
|
+
// app/controllers/PostController.ts — unchanged; validate() becomes precognition-aware automatically
|
|
255
|
+
async store(ctx: HttpContext) {
|
|
256
|
+
const data = await StorePostRequest.validate();
|
|
257
|
+
// ...only runs on a real (non-precognition) submit
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The middleware itself only stamps `Vary: Precognition` on precognitive responses; the validation
|
|
262
|
+
short-circuit happens inside `FormRequest.validate()`. On the client, use the `@inertiajs/react`
|
|
263
|
+
precognition `useForm().validate(...)` helpers as usual.
|
|
264
|
+
|
|
265
|
+
## Page object reference
|
|
266
|
+
|
|
267
|
+
Each feature contributes fields to the JSON [page object](https://inertiajs.com/docs/v3/core-concepts/the-protocol#the-page-object).
|
|
268
|
+
Zerotal emits these automatically; you never build them by hand. Fields are omitted when empty.
|
|
269
|
+
|
|
270
|
+
| Field | Set by |
|
|
271
|
+
| ------------------------------------------------ | --------------------------------------------------- |
|
|
272
|
+
| `component`, `props`, `url`, `version` | always (core) |
|
|
273
|
+
| `deferredProps` | `defer()` — `{ group: [keys] }` on first load |
|
|
274
|
+
| `rescuedProps` | a rescued `defer()` prop that threw |
|
|
275
|
+
| `mergeProps` / `prependProps` / `deepMergeProps` | `merge()` / `.prepend()` / `deepMerge()` |
|
|
276
|
+
| `matchPropsOn` | `.matchOn(...)` |
|
|
277
|
+
| `scrollProps` | `scroll()` — infinite-scroll pagination config |
|
|
278
|
+
| `onceProps` | `.once()` |
|
|
279
|
+
| `encryptHistory` / `clearHistory` | `Inertia.encryptHistory()` / `clearHistory()` |
|
|
280
|
+
| `sharedProps` | keys registered via `Inertia.share()` (+ built-ins) |
|
|
281
|
+
|
|
282
|
+
### Request headers Zerotal reads
|
|
283
|
+
|
|
284
|
+
`X-Inertia`, `X-Inertia-Version`, `X-Inertia-Partial-Data`, `X-Inertia-Partial-Except`,
|
|
285
|
+
`X-Inertia-Partial-Component`, `X-Inertia-Reset`, `X-Inertia-Except-Once-Props`,
|
|
286
|
+
`X-Inertia-Error-Bag`, and `Precognition` / `Precognition-Validate-Only`.
|
|
287
|
+
|
|
288
|
+
## Shared props
|
|
289
|
+
|
|
290
|
+
Shared props are merged into **every** Inertia page automatically, so common data
|
|
291
|
+
like the authenticated user and flash messages are always available to your
|
|
292
|
+
components without each controller passing them explicitly.
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
// in a controller
|
|
296
|
+
import { sharedProps } from "@zerotal/inertia";
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
`inertia()` calls `sharedProps()` internally and spreads the result **under** your
|
|
300
|
+
controller's props (`{ ...sharedProps(), ...props }`), so a prop you pass with the
|
|
301
|
+
same key wins.
|
|
302
|
+
|
|
303
|
+
### What's provided
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
// the shared bag sharedProps() returns
|
|
307
|
+
{
|
|
308
|
+
auth: {
|
|
309
|
+
user: ctx.user ?? null, // reduced to plain scalars — never a live ORM model
|
|
310
|
+
},
|
|
311
|
+
flash: {
|
|
312
|
+
success: session.get("success") ?? null,
|
|
313
|
+
error: session.get("error") ?? null,
|
|
314
|
+
},
|
|
315
|
+
errors: always(session.get("errors") ?? {}), // from a validation redirect
|
|
316
|
+
old: session.get("old") ?? {}, // previous form input
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
`errors` is wrapped in [`always()`](#optional-and-always-props) so it survives
|
|
321
|
+
[partial reloads](#partial-reloads) — the Inertia client always expects an
|
|
322
|
+
`errors` bag. When the request carries an `X-Inertia-Error-Bag` header, errors are namespaced under
|
|
323
|
+
that bag. The other shared props (`auth`/`flash`/`old`) are ordinary props, so a partial reload's
|
|
324
|
+
`only`/`except` filter applies to them, matching Inertia's semantics.
|
|
325
|
+
|
|
326
|
+
Every page component can read these without the controller passing them:
|
|
327
|
+
|
|
328
|
+
```tsx
|
|
329
|
+
// resources/js/pages/Page.tsx
|
|
330
|
+
import { usePage } from "@inertiajs/react";
|
|
331
|
+
|
|
332
|
+
export default function Page() {
|
|
333
|
+
const { auth, flash, errors } = usePage<{
|
|
334
|
+
auth: { user: { name: string } | null };
|
|
335
|
+
flash: { success: string | null; error: string | null };
|
|
336
|
+
errors: Record<string, string>;
|
|
337
|
+
}>().props;
|
|
338
|
+
|
|
339
|
+
return (
|
|
340
|
+
<>
|
|
341
|
+
{flash.success && <div className="toast">{flash.success}</div>}
|
|
342
|
+
{auth.user ? <span>{auth.user.name}</span> : <a href="/login">Sign in</a>}
|
|
343
|
+
</>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
#### Why auth.user is a plain object
|
|
349
|
+
|
|
350
|
+
`sharedProps()` serializes the authenticated user to **scalar fields only** — it
|
|
351
|
+
skips methods and array-valued properties. This is deliberate: a live model with an
|
|
352
|
+
unloaded `@hasMany` relation throws when `JSON.stringify` touches the relation getter.
|
|
353
|
+
The plain object is always safe to send. If a page needs related data, load it
|
|
354
|
+
explicitly in the controller and pass it as a normal prop.
|
|
355
|
+
|
|
356
|
+
> **Warning** — Don't pass a live ORM model as a prop expecting all its relations.
|
|
357
|
+
> `JSON.stringify` triggers relation getters, which throw if the relation wasn't
|
|
358
|
+
> eager-loaded. Eager-load (`.with(...)`) or map to a plain shape first.
|
|
359
|
+
|
|
360
|
+
### Adding custom shared props
|
|
361
|
+
|
|
362
|
+
#### Register with Inertia.share
|
|
363
|
+
|
|
364
|
+
Register props once — typically in a provider's boot or in middleware — and they're merged into
|
|
365
|
+
every page. Values may be plain values, factory functions (evaluated lazily per request), or any
|
|
366
|
+
[prop wrapper](#data-props):
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
// in a provider's boot or middleware
|
|
370
|
+
import { Inertia } from "@zerotal/inertia";
|
|
371
|
+
|
|
372
|
+
// Single key, or a map:
|
|
373
|
+
Inertia.share("appName", "Acme");
|
|
374
|
+
Inertia.share({
|
|
375
|
+
appName: "Acme",
|
|
376
|
+
year: () => new Date().getFullYear(), // evaluated per request
|
|
377
|
+
flags: Inertia.optional(() => FeatureFlag.all()), // only on partial reloads
|
|
378
|
+
});
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Shared props are subject to partial-reload filtering just like page props (except `errors`, which is
|
|
382
|
+
`always()`), and they appear in the page object's `sharedProps` list so the client can carry them
|
|
383
|
+
over during instant visits.
|
|
384
|
+
|
|
385
|
+
#### Merge in the controller
|
|
386
|
+
|
|
387
|
+
```ts
|
|
388
|
+
// in a controller
|
|
389
|
+
return inertia("Dashboard", {
|
|
390
|
+
notifications: await Notification.query().where("user_id", user.id).unread().get(),
|
|
391
|
+
// auth, flash, errors, old are merged automatically — no need to repeat them
|
|
392
|
+
});
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
#### Share from middleware via the context
|
|
396
|
+
|
|
397
|
+
When several pages need the same extra data, set it on the context in a middleware
|
|
398
|
+
and read it back in the controller — keeping the controller body clean:
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
// in a middleware:
|
|
402
|
+
ctx.setInternal("unreadCount", await Notification.unreadCount(ctx.user!.id));
|
|
403
|
+
|
|
404
|
+
// in the controller:
|
|
405
|
+
return inertia("Layout", {
|
|
406
|
+
unreadCount: ctx.getInternal<number>("unreadCount"),
|
|
407
|
+
});
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
This pattern pairs well with a persistent [layout](/docs/inertia/build#generating-a-page) that
|
|
411
|
+
displays the value on every page.
|
|
412
|
+
|
|
413
|
+
## Typed props
|
|
414
|
+
|
|
415
|
+
The props a controller passes are checked against the props the page component
|
|
416
|
+
declares:
|
|
417
|
+
|
|
418
|
+
```tsx
|
|
419
|
+
// resources/js/pages/Posts/Show.tsx
|
|
420
|
+
interface Props {
|
|
421
|
+
post: Post;
|
|
422
|
+
related: Post[];
|
|
423
|
+
stats?: { views: number }; // optional — it arrives after first paint
|
|
424
|
+
}
|
|
425
|
+
export default function Show({ post, related, stats }: Props) { … }
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
```ts
|
|
429
|
+
// in a controller
|
|
430
|
+
return Inertia.render("Posts/Show", {
|
|
431
|
+
post,
|
|
432
|
+
related: [],
|
|
433
|
+
stats: defer(() => computeStats()),
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
Inertia.render("Posts/Shwo", { … }); // ✗ not a page
|
|
437
|
+
Inertia.render("Posts/Show", { post }); // ✗ Property 'related' is missing
|
|
438
|
+
Inertia.render("Posts/Show", { post, relatd: [] }); // ✗ Did you mean 'related'?
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Nothing is annotated to make this work. The component already declares its props,
|
|
442
|
+
and `resources/js/pages.generated.ts` already holds an `import()` thunk per page —
|
|
443
|
+
and an `import()` thunk carries the module's full type. The registry is written
|
|
444
|
+
with `satisfies`, so those types survive, and one type-only line in that same file
|
|
445
|
+
hands them to the server. Rebuild it with `bun zt inertia:build` (or just run
|
|
446
|
+
`zt dev`, which rebuilds it on every change).
|
|
447
|
+
|
|
448
|
+
The check runs in the direction that costs nothing: the **component** declares
|
|
449
|
+
the shape and the **controller** is checked against it.
|
|
450
|
+
|
|
451
|
+
### Wrappers are unwrapped
|
|
452
|
+
|
|
453
|
+
The two sides genuinely differ — the controller passes `merge(() => posts)` and
|
|
454
|
+
the component receives `Post[]` — so each prop accepts its value, a factory for
|
|
455
|
+
it, or a wrapper carrying it, and the wrapper's payload is checked against the
|
|
456
|
+
prop it fills:
|
|
457
|
+
|
|
458
|
+
```ts
|
|
459
|
+
Inertia.render("Posts/Show", { post, related: merge(() => [1, 2]) });
|
|
460
|
+
// ✗ number[] is not Post[]
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
> **Warning** — `optional()` and `defer()` are only accepted where the component
|
|
464
|
+
> declares the prop as optional. They are _absent on first paint_ by definition,
|
|
465
|
+
> so a component that types such a prop as required is wrong about its own
|
|
466
|
+
> contract. Types that accepted it anyway would launder that bug into something
|
|
467
|
+
> the compiler had signed off on. Add the `?` — and handle the undefined.
|
|
468
|
+
|
|
469
|
+
### Shared props are never required
|
|
470
|
+
|
|
471
|
+
`auth`, `flash`, `errors` and `old` are merged into every page, so a controller
|
|
472
|
+
never has to pass them — even when the page component declares them. Props you
|
|
473
|
+
register yourself with `Inertia.share()` are a runtime call that nothing can
|
|
474
|
+
generate, so declare them once:
|
|
475
|
+
|
|
476
|
+
```ts
|
|
477
|
+
// resources/js/types.ts (next to the interface your pages read with usePage)
|
|
478
|
+
declare module "@zerotal/inertia" {
|
|
479
|
+
interface SharedProps {
|
|
480
|
+
appName: string;
|
|
481
|
+
flags: Record<string, boolean>;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
They then become optional-but-accepted in every `Inertia.render` call: a page may
|
|
487
|
+
still override one, and no page is forced to pass it.
|
|
488
|
+
|
|
489
|
+
### What is not checked
|
|
490
|
+
|
|
491
|
+
- **A page name only known at runtime** — an error page chosen by status code, a
|
|
492
|
+
component from config — has nothing to check against. Use
|
|
493
|
+
`Inertia.render.dynamic(name, props)`, which takes any name and any props.
|
|
494
|
+
- **Vue pages.** A `.vue` SFC resolves through a `declare module '*.vue'` shim
|
|
495
|
+
that types the default export as `DefineComponent<{}, {}, any>`, so its props
|
|
496
|
+
are not visible to TypeScript unless `vue-tsc` is in your typecheck path. Those
|
|
497
|
+
pages fall back to accepting any props rather than failing on a shape nobody
|
|
498
|
+
can see. The page **name** is still checked; only its props are not.
|
|
499
|
+
- **A component wrapped in `React.memo()`** (or anything else that returns an
|
|
500
|
+
object rather than a function) falls back the same way.
|
|
501
|
+
- **Before you rebuild the registry**, every name and every prop bag compiles, as
|
|
502
|
+
it always did.
|
|
503
|
+
|
|
504
|
+
## Next steps
|
|
505
|
+
|
|
506
|
+
- [Inertia overview](/docs/inertia) — the guide's front page and the rest of the sections.
|
|
507
|
+
- [Reference](/docs/inertia/references) — the full API surface in one table.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Inertia Reference
|
|
3
|
+
description: Every helper, prop type, and config key in one table.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# References
|
|
7
|
+
|
|
8
|
+
A consolidated cheat-sheet for `@zerotal/inertia`. Each entry links to the section above where it's explained in full.
|
|
9
|
+
|
|
10
|
+
## Commands
|
|
11
|
+
|
|
12
|
+
| Command | Purpose |
|
|
13
|
+
| -------------------------------------------------- | --------------------------------------------------------------- |
|
|
14
|
+
| `bun zt make:page <Name>` | Scaffold a page component under your pages directory. |
|
|
15
|
+
| `bun zt make:page <Name> --layout <Layout>` | Scaffold a page wrapped in a persistent layout. |
|
|
16
|
+
| `bun zt make:page <Name> --framework <vue\|react>` | Force the frontend framework instead of auto-detecting. |
|
|
17
|
+
| `bun zt inertia:build` | Bundle the frontend — development build (external source maps). |
|
|
18
|
+
| `bun zt inertia:build -p` | Production build (minified, no source maps). |
|
|
19
|
+
|
|
20
|
+
## Server exports
|
|
21
|
+
|
|
22
|
+
| Export | Purpose |
|
|
23
|
+
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
24
|
+
| `inertia(component, props?)` | Render an Inertia page from a controller action. See [the inertia helper](/docs/inertia/rendering#the-inertia-helper). |
|
|
25
|
+
| `inertia.dynamic(component, props?)` | Render a page whose name is only known at runtime — no compile-time checking. See [what is not checked](/docs/inertia/props#what-is-not-checked). |
|
|
26
|
+
| `inertiaStream(component, props?)` | Streaming SSR alternative to `inertia()`. See [Streaming SSR](/docs/inertia/ssr#streaming-ssr). |
|
|
27
|
+
| `inertiaStream.dynamic(component, props?)` | The streaming counterpart of `inertia.dynamic()`. |
|
|
28
|
+
| `optional(fn)` / `lazy(fn)` | Prop sent only on a partial reload that requests it. See [optional and always props](/docs/inertia/props#optional-and-always-props). |
|
|
29
|
+
| `always(value)` | Prop always sent, even when a partial reload would exclude it. |
|
|
30
|
+
| `defer(fn, group?, opts?)` | Prop fetched in a follow-up request after first paint. See [deferred props](/docs/inertia/props#deferred-props). |
|
|
31
|
+
| `merge(value)` / `deepMerge(value)` | Combine reloaded data with existing client data. See [merging props](/docs/inertia/props#merging-props). |
|
|
32
|
+
| `scroll(paginator, opts?)` | Infinite-scroll pagination wired to `<InfiniteScroll>`. See [infinite scroll](/docs/inertia/props#infinite-scroll). |
|
|
33
|
+
| `sharedProps()` | The auto-merged `auth` / `flash` / `errors` / `old` bag. See [Shared Props](/docs/inertia/props). |
|
|
34
|
+
| `share(key, value)` / `share(map)` | Register custom shared props. See [adding custom shared props](/docs/inertia/props#adding-custom-shared-props). |
|
|
35
|
+
| `setAssetVersion(v)` / `assetVersion()` | Set / read the current asset version. See [asset versioning](/docs/inertia/middleware#asset-versioning). |
|
|
36
|
+
| `generatePageRegistry(cwd?)` | Regenerate the page registry module. See [page registry](/docs/inertia/build#page-registry). |
|
|
37
|
+
| `inertiaRoute(path, component, props?, middleware?)` | The function behind the `Router.inertia()` macro. See [controller-less routes](/docs/inertia/rendering#controller-less-routes). |
|
|
38
|
+
| `InertiaProvider` | Wires the middleware, template, asset version, and optional SSR endpoint. See [Register the provider](/docs/inertia#register-the-provider). |
|
|
39
|
+
| `InertiaMiddleware` | Protocol mechanics; auto-registered by the provider. See [Middleware & versioning](/docs/inertia/middleware). |
|
|
40
|
+
| `PrecognitionMiddleware` | Enables precognition validation. See [precognition](/docs/inertia/props#precognition). |
|
|
41
|
+
| `InertiaConfig(options?)` | Build a typed `config/inertia.ts` object with defaults. See [Configuration](/docs/inertia#configuration). |
|
|
42
|
+
| `Inertia` | Unified facade — see below. |
|
|
43
|
+
|
|
44
|
+
## The Inertia facade
|
|
45
|
+
|
|
46
|
+
| Method | Equivalent / purpose |
|
|
47
|
+
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
48
|
+
| `Inertia.render(component, props?)` | Same as `inertia()`. |
|
|
49
|
+
| `Inertia.stream(component, props?)` | Same as `inertiaStream()`. |
|
|
50
|
+
| `Inertia.optional(fn)` / `Inertia.lazy(fn)` | Same as `optional()`. |
|
|
51
|
+
| `Inertia.always(value)` | Same as `always()`. |
|
|
52
|
+
| `Inertia.defer(fn, group?, opts?)` | Same as `defer()`. |
|
|
53
|
+
| `Inertia.merge(value)` / `Inertia.deepMerge(value)` | Same as `merge()` / `deepMerge()`. |
|
|
54
|
+
| `Inertia.scroll(paginator, opts?)` | Same as `scroll()`. |
|
|
55
|
+
| `Inertia.share(key, value)` / `Inertia.share(map)` | Register custom shared props. See [adding custom shared props](/docs/inertia/props#adding-custom-shared-props). |
|
|
56
|
+
| `Inertia.location(url)` | External / full-page redirect (`409` + `X-Inertia-Location`). See [external & fragment redirects](/docs/inertia/props#external-fragment-redirects). |
|
|
57
|
+
| `Inertia.encryptHistory()` | Encrypt this page's history entry. See [history encryption](/docs/inertia/props#history-encryption). |
|
|
58
|
+
| `Inertia.clearHistory()` | Clear encrypted history (e.g. on logout). |
|
|
59
|
+
|
|
60
|
+
## Prop helpers
|
|
61
|
+
|
|
62
|
+
| Helper / chain | Behaviour |
|
|
63
|
+
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
64
|
+
| `optional(fn)` | Never sent on a normal visit; only on a partial reload that asks for it. |
|
|
65
|
+
| `lazy(fn)` | Alias of `optional(fn)`. |
|
|
66
|
+
| `always(value)` | Always sent, even past a partial reload's include / exclude filter. |
|
|
67
|
+
| `defer(fn, group?, { rescue? })` | Excluded from the first render; fetched in a grouped follow-up request. |
|
|
68
|
+
| `merge(value)` / `deepMerge(value)` | Append / deep-merge reloaded data instead of replacing it. |
|
|
69
|
+
| `.append(path)` | Append merged data at `path`. |
|
|
70
|
+
| `.prepend(path?)` | Prepend merged data at the root (no arg) or at `path`. |
|
|
71
|
+
| `.matchOn(path)` | Replace items matching the key at `path`. |
|
|
72
|
+
| `.once(expiresAt?)` | Resolve a single time; the client remembers it across navigations. See [once props](/docs/inertia/props#once-props). |
|
|
73
|
+
|
|
74
|
+
Each helper returns a wrapper class — `OptionalProp`, `AlwaysProp`, `DeferProp`,
|
|
75
|
+
`MergeProp`, `InfiniteScrollProp`, all extending `InertiaProp`. You build them
|
|
76
|
+
through the helpers above rather than constructing them; the names matter only for
|
|
77
|
+
annotating a variable or narrowing a union, and `InertiaProp` is the type to accept
|
|
78
|
+
when a function takes "any wrapped prop".
|
|
79
|
+
|
|
80
|
+
## Errors
|
|
81
|
+
|
|
82
|
+
| Error | Thrown when |
|
|
83
|
+
| ------------------------------- | ---------------------------------------------------------------------------------- |
|
|
84
|
+
| `InertiaError` | Base class for the rest — catch this to handle any Inertia failure. |
|
|
85
|
+
| `InvalidComponentError` | A page component name is empty or not a string. |
|
|
86
|
+
| `InertiaTemplateNotLoadedError` | The root template was never loaded, so there is no HTML shell to render a page in. |
|
|
87
|
+
|
|
88
|
+
## Server-side rendering
|
|
89
|
+
|
|
90
|
+
`SsrHandler` is the class behind `InertiaConfig({ ssr: true })` — it owns the
|
|
91
|
+
`POST /__ssr` route the config registers. Configure SSR through the config flag; the
|
|
92
|
+
class is exported for tests that drive the handler directly. See
|
|
93
|
+
[SSR](/docs/inertia/ssr).
|
|
94
|
+
|
|
95
|
+
## Configuration options
|
|
96
|
+
|
|
97
|
+
Set in `config/inertia.ts` via `InertiaConfig({ … })` — every field is optional.
|
|
98
|
+
|
|
99
|
+
| Option | Default | Purpose |
|
|
100
|
+
| ---------------- | ------------------------ | ---------------------------------------------------------------------------------------------------- |
|
|
101
|
+
| `htmlTemplate` | `"./resources/app.html"` | Path to the root HTML template (must contain `<!-- @inertia -->`); falls back to a built-in default. |
|
|
102
|
+
| `version` | `"1"` | Asset version string embedded in every page object; bump on each deploy. |
|
|
103
|
+
| `assetsUrl` | `"/"` | Public URL prefix for built assets. |
|
|
104
|
+
| `pagesDir` | `"resources/js/pages"` | Directory where Inertia page components live. |
|
|
105
|
+
| `ssr` | `false` | Register `POST /__ssr` for endpoint SSR. |
|
|
106
|
+
| `encryptHistory` | `false` | Encrypt history state globally. |
|
|
107
|
+
|
|
108
|
+
## Protocol headers
|
|
109
|
+
|
|
110
|
+
| Header | Direction | Used for |
|
|
111
|
+
| ----------------------------------------------------- | ------------------ | ------------------------------------------------------------ |
|
|
112
|
+
| `X-Inertia` | request / response | Marks an Inertia XHR visit; echoed on the response. |
|
|
113
|
+
| `X-Inertia-Version` | request | Client's asset version — a mismatch triggers a `409` reload. |
|
|
114
|
+
| `X-Inertia-Partial-Data` / `X-Inertia-Partial-Except` | request | Partial-reload prop include / exclude lists. |
|
|
115
|
+
| `X-Inertia-Partial-Component` | request | The component a partial reload targets. |
|
|
116
|
+
| `X-Inertia-Reset` | request | Props to clear before merging fresh data. |
|
|
117
|
+
| `X-Inertia-Except-Once-Props` | request | Once-props the client already holds. |
|
|
118
|
+
| `X-Inertia-Error-Bag` | request | Namespaces validation errors under a named bag. |
|
|
119
|
+
| `X-Inertia-Infinite-Scroll-Merge-Intent` | request | `prepend` vs append intent when scrolling up. |
|
|
120
|
+
| `Precognition` / `Precognition-Validate-Only` | request | Precognition validation and field scoping. |
|
|
121
|
+
| `Vary: X-Inertia` | response | Keeps cached HTML and JSON variants separate. |
|
|
122
|
+
| `X-Inertia-Location` | response | `409` full-page reload / external redirect target. |
|
|
123
|
+
| `X-Inertia-Redirect` | response | `409` redirect that preserves a URL fragment. |
|
|
124
|
+
|
|
125
|
+
## Page object fields
|
|
126
|
+
|
|
127
|
+
See [page object reference](/docs/inertia/props#page-object-reference) for the full table of fields — `component`, `props`, `url`, `version`, `deferredProps`, `mergeProps`, `scrollProps`, `onceProps`, and the rest — and which API sets each.
|
|
128
|
+
|
|
129
|
+
## Types
|
|
130
|
+
|
|
131
|
+
Everything here is type-only, and exists so `Inertia.render` can be checked against
|
|
132
|
+
the page it names. See [Typed props](/docs/inertia/props#typed-props).
|
|
133
|
+
|
|
134
|
+
| Type | Purpose |
|
|
135
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
136
|
+
| `InertiaPageRegistry` | Augmentation target the generated `pages.generated.ts` fills with `{ pages: typeof pages }`. Empty until the registry is built. |
|
|
137
|
+
| `SharedProps` | Declare your own `Inertia.share()` keys here; they become optional-but-accepted in every render call. |
|
|
138
|
+
| `PageName` | Union of the generated page names (`never` before the registry exists). |
|
|
139
|
+
| `PageTarget` | What the helpers accept as a name: `PageName` once generated, `string` before that. |
|
|
140
|
+
| `PropsOf<N>` | The props page `N`'s component declares. |
|
|
141
|
+
| `PropInput<T>` | What may be passed for a prop typed `T` — the value, a factory, or a wrapper carrying it. |
|
|
142
|
+
| `RenderProps<N>` | The full props bag `Inertia.render(N, …)` accepts, shared props subtracted. |
|
|
143
|
+
| `RenderArgs<N>` | `render()`'s argument tuple; the props bag is optional only when the page requires nothing. |
|
|
144
|
+
| `PageRenderer` | The shape of `inertia` / `inertiaStream`: a checked call signature plus `.dynamic`. |
|