@adonisjs/inertia 5.0.0-next.0 → 5.0.0-next.1

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.
@@ -2,7 +2,7 @@ import type { HttpContext } from '@adonisjs/core/http';
2
2
  import { type ContainerResolver } from '@adonisjs/core/container';
3
3
  import type { JSONDataTypes } from '@adonisjs/core/types/transformers';
4
4
  import type { AsyncOrSync, DeepPartial, Prettify } from '@adonisjs/core/types/common';
5
- import { type DEEP_MERGE, type ALWAYS_PROP, type OPTIONAL_PROP, type TO_BE_MERGED, type DEFERRED_PROP } from './symbols.ts';
5
+ import { type ONCE_PROP, type DEEP_MERGE, type SCROLL_PROP, type ALWAYS_PROP, type OPTIONAL_PROP, type TO_BE_MERGED, type DEFERRED_PROP, type MERGE_PREPEND, type SCROLL_DEFERRED, type MERGE_MATCH_ON } from './symbols.ts';
6
6
  /**
7
7
  * Representation of a resource item, collection and paginator that can be resolved to
8
8
  * get normalized objects
@@ -46,6 +46,155 @@ export type RequestInfo = {
46
46
  resetProps?: string[];
47
47
  /** Error bag identifier for validation errors */
48
48
  errorBag?: string;
49
+ /** Once-keys the client already holds a fresh, cached value for */
50
+ exceptOnceProps?: string[];
51
+ /**
52
+ * Infinite-scroll merge direction requested by the client. `prepend` loads an
53
+ * earlier page, `append` (the default when the header is absent) loads a later
54
+ * one.
55
+ */
56
+ mergeIntent?: 'append' | 'prepend';
57
+ };
58
+ /**
59
+ * The infinite-scroll cursor a provider returns and the adapter auto-derives
60
+ * from a transformer paginator. Does not include `reset` — that is driven by the
61
+ * `X-Inertia-Reset` request header, not user code.
62
+ */
63
+ export type ScrollProps = {
64
+ /** Query-string parameter the client uses to request a page (e.g. `page`). */
65
+ pageName: string;
66
+ /** Identifier for the page currently in the response. */
67
+ currentPage: number | string | null;
68
+ /** Identifier for the next page, or `null` when there is none. */
69
+ nextPage: number | string | null;
70
+ /** Identifier for the previous page, or `null` when there is none. */
71
+ previousPage: number | string | null;
72
+ };
73
+ /**
74
+ * Full per-prop entry emitted under the page object's `scrollProps` map: the
75
+ * cursor plus the `reset` flag the client uses to discard cached items.
76
+ */
77
+ export type ScrollMetaData = ScrollProps & {
78
+ /** When `true`, the client discards cached items for this prop before merging. */
79
+ reset: boolean;
80
+ };
81
+ /**
82
+ * Callback that computes the infinite-scroll cursor from the resolved prop value.
83
+ * Required whenever the value is not a transformer paginator the adapter can
84
+ * auto-derive from. Runs only when the prop is resolved, so a deferred scroll
85
+ * prop computes its cursor on the partial reload rather than the initial visit.
86
+ *
87
+ * @template T - The resolved prop value handed to the provider
88
+ */
89
+ export type ScrollPropsProvider<T = any> = (value: T) => AsyncOrSync<ScrollProps>;
90
+ /**
91
+ * The resolved value shape a scroll prop accepts: any object exposing a typed
92
+ * `data` array. Only `data` is type-checked; the rest of the object is opaque
93
+ * display data the adapter reads only to auto-derive the cursor.
94
+ *
95
+ * @template Item - The item type of the paginated `data` array
96
+ */
97
+ export type ScrollResolvedValue<Item> = {
98
+ data: Item[];
99
+ } & Record<string, any>;
100
+ /**
101
+ * The two forms a scroll prop value can take once resolved: a transformer
102
+ * paginator (a resolvable that resolves to `{ data, metadata }`) or a plain
103
+ * object with a typed `data` array.
104
+ *
105
+ * @template Item - The item type of the paginated `data` array
106
+ */
107
+ export type ScrollValue<Item> = ScrollResolvedValue<Item> | ResolvableOf<ScrollResolvedValue<Item>>;
108
+ /**
109
+ * Phantom brand distinguishing a `Scroll<Item>` marker from a plain object so
110
+ * `AsPageProps` can require the `scroll()` helper for that prop. Never present at
111
+ * runtime.
112
+ */
113
+ declare const SCROLL_BRAND: unique symbol;
114
+ /**
115
+ * Marker a client component uses to declare an infinite-scroll prop. The
116
+ * component receives the resolved `data` array; on the server, `AsPageProps`
117
+ * requires the matching prop to be built with `inertia.scroll()`.
118
+ *
119
+ * @template Item - The item type of the paginated `data` array
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * declare module '@adonisjs/inertia/types' {
124
+ * interface InertiaPages {
125
+ * 'users/index': { users: Scroll<User> }
126
+ * }
127
+ * }
128
+ * ```
129
+ */
130
+ export type Scroll<Item> = {
131
+ data: Item[];
132
+ readonly [SCROLL_BRAND]: true;
133
+ };
134
+ /**
135
+ * Detects a `Scroll<Item>` marker by its phantom brand.
136
+ *
137
+ * @template V - The client prop value to test
138
+ */
139
+ export type IsScrollMarker<V> = [V] extends [never] ? false : [V] extends [{
140
+ readonly [SCROLL_BRAND]: any;
141
+ }] ? true : false;
142
+ /**
143
+ * Extracts the item type from a `Scroll<Item>` marker.
144
+ *
145
+ * @template V - The `Scroll` marker to read
146
+ */
147
+ export type ScrollItemOf<V> = V extends {
148
+ data: (infer Item)[];
149
+ } ? Item : never;
150
+ /**
151
+ * Pagination metadata produced by an AdonisJS Lucid paginator's `getMeta()`.
152
+ * Exposed for typing the `meta` of a scroll prop value when desired.
153
+ */
154
+ export type PaginationMeta = {
155
+ total: number;
156
+ perPage: number;
157
+ currentPage: number;
158
+ pageName: string;
159
+ lastPage: number;
160
+ firstPage: number;
161
+ firstPageUrl: string;
162
+ lastPageUrl: string;
163
+ nextPageUrl: string | null;
164
+ previousPageUrl: string | null;
165
+ };
166
+ /**
167
+ * Represents an infinite-scroll prop: a mergeable, paginated value carrying the
168
+ * pagination cursor the client needs to keep loading pages as the user scrolls.
169
+ * The cursor is auto-derived from a transformer paginator or supplied by a
170
+ * provider callback.
171
+ *
172
+ * @template Item - The item type of the paginated `data` array
173
+ * @template Deferred - `true` once `.deferred()` is chained; makes the prop
174
+ * optional on the client (absent on the initial load), so a deferred scroll
175
+ * prop only satisfies an optional client prop — never a required one.
176
+ */
177
+ export type ScrollProp<Item = any, Deferred extends boolean = false> = {
178
+ /** The paginated value (or a callback resolving to it) */
179
+ value: ScrollValue<Item> | (() => AsyncOrSync<ScrollValue<Item>>);
180
+ /**
181
+ * Computes the cursor from the resolved value. Defaults to a paginator-aware
182
+ * provider that auto-derives from a transformer paginator and throws when the
183
+ * value is not paginator data.
184
+ */
185
+ provider: ScrollPropsProvider<ScrollResolvedValue<Item>>;
186
+ /** Defer group when deferred via `.deferred()`; `undefined` ⇒ not deferred */
187
+ group?: string;
188
+ /** Exclude the first page from the initial load; loaded on demand */
189
+ deferred(group?: string): ScrollProp<Item, true>;
190
+ /** Dedupe/replace incoming items by the given key, relative to `data` */
191
+ matchOn(key: string): ScrollProp<Item, Deferred>;
192
+ /** Brand symbol to identify this as a scroll prop */
193
+ [SCROLL_PROP]: true;
194
+ /** Type-only flag: `true` once deferred. Never present at runtime. */
195
+ [SCROLL_DEFERRED]?: Deferred;
196
+ /** Match key for keyed merges; `undefined` when unkeyed */
197
+ [MERGE_MATCH_ON]?: string;
49
198
  };
50
199
  /**
51
200
  * Represents a prop that is always included in responses and cannot be removed during cherry-picking
@@ -67,6 +216,8 @@ export type AlwaysProp<T extends UnPackedPageProps> = {
67
216
  export type OptionalProp<T extends UnPackedPageProps> = {
68
217
  /** Function that computes the prop value when requested */
69
218
  compute: () => AsyncOrSync<T>;
219
+ /** Remember this prop on the client across visits */
220
+ once(options?: OnceOptions): OnceProp<OptionalProp<T>>;
70
221
  /** Brand symbol to identify this as an optional prop */
71
222
  [OPTIONAL_PROP]: true;
72
223
  };
@@ -78,15 +229,49 @@ export type OptionalProp<T extends UnPackedPageProps> = {
78
229
  */
79
230
  export type DeferProp<T extends UnPackedPageProps> = {
80
231
  group: string;
232
+ /**
233
+ * When `true`, a resolution error is caught: the prop is omitted from the
234
+ * response and its path is reported to the client via `rescuedProps` so the
235
+ * `<Deferred>` component can render its `rescue` slot instead of staying in a
236
+ * loading state. The error is reported out of band (see {@link RescueListener}).
237
+ */
238
+ rescue: boolean;
81
239
  /** Function that computes the prop value when requested */
82
240
  compute: () => AsyncOrSync<T>;
83
241
  /** Creates a mergeable version of this deferred prop */
84
242
  merge(): MergeableProp<DeferProp<T>>;
85
243
  /** Creates a deep-mergeable version of this deferred prop */
86
244
  deepMerge(): MergeableProp<DeferProp<T>>;
245
+ /** Remember this prop on the client across visits */
246
+ once(options?: OnceOptions): OnceProp<DeferProp<T>>;
87
247
  /** Brand symbol to identify this as a deferred prop */
88
248
  [DEFERRED_PROP]: true;
89
249
  };
250
+ /**
251
+ * Options for creating a deferred prop. The second argument to `defer` also
252
+ * accepts a bare group name string for backwards compatibility.
253
+ */
254
+ export type DeferOptions = {
255
+ /** Group deferred props so the client fetches them together */
256
+ group?: string;
257
+ /**
258
+ * Opt into graceful failure: catch resolution errors, omit the prop, and
259
+ * report its path via `rescuedProps`. Defaults to `false`.
260
+ */
261
+ rescue?: boolean;
262
+ };
263
+ /**
264
+ * Listener invoked when a rescuable deferred prop's resolution throws. Register
265
+ * one via `Inertia.onRescue(...)`; when none is registered, the error is logged
266
+ * through the request logger (`ctx.logger.error`).
267
+ *
268
+ * @param error - The thrown error that was rescued
269
+ * @param context - The prop path that failed and its HTTP context
270
+ */
271
+ export type RescueListener = (error: unknown, context: {
272
+ prop: string;
273
+ ctx: HttpContext;
274
+ }) => void;
90
275
  /**
91
276
  * Represents a prop that should be merged with existing props on the page rather than replaced
92
277
  *
@@ -95,9 +280,83 @@ export type DeferProp<T extends UnPackedPageProps> = {
95
280
  export type MergeableProp<T extends UnPackedPageProps | DeferProp<UnPackedPageProps>> = {
96
281
  /** The prop value to be merged */
97
282
  value: T;
283
+ /** Prepend incoming array items instead of appending (shallow merge only) */
284
+ prepend(): MergeableProp<T>;
285
+ /** Append incoming array items (the default; restores append after `prepend`) */
286
+ append(): MergeableProp<T>;
287
+ /** Dedupe/replace incoming array items by the given match path */
288
+ matchOn(key: string): MergeableProp<T>;
289
+ /** Remember this prop on the client across visits */
290
+ once(options?: OnceOptions): OnceProp<MergeableProp<T>>;
98
291
  /** Brand symbol to identify this prop for merging */
99
292
  [TO_BE_MERGED]: true;
293
+ /** Whether the merge is deep (recursive) rather than a shallow array merge */
100
294
  [DEEP_MERGE]: boolean;
295
+ /** Direction flag for shallow array merges: `true` prepends, `false` appends */
296
+ [MERGE_PREPEND]: boolean;
297
+ /** Match path for keyed merges, relative to the prop; `undefined` when unkeyed */
298
+ [MERGE_MATCH_ON]?: string;
299
+ };
300
+ /**
301
+ * Expiry configuration for a once prop. Accepts either a relative TTL or an
302
+ * absolute expiry; both normalize to epoch-milliseconds at response-build time.
303
+ *
304
+ * @example
305
+ * ```ts
306
+ * { expiresIn: '2h' } // relative, parsed via @poppinss/utils
307
+ * { expiresIn: 3_600_000 } // relative, milliseconds
308
+ * { expiresAt: new Date(...) } // absolute
309
+ * ```
310
+ */
311
+ export type OnceExpiry = {
312
+ /** Relative time-to-live: milliseconds (number) or a duration string like '2h' */
313
+ expiresIn?: number | string;
314
+ /** Absolute expiry: a Date or epoch-milliseconds */
315
+ expiresAt?: Date | number;
316
+ };
317
+ /**
318
+ * Options accepted by `inertia.once()` and the `.once()` chaining method.
319
+ */
320
+ export type OnceOptions = OnceExpiry & {
321
+ /**
322
+ * Custom once-key the client uses to identify the cached value across pages.
323
+ * Defaults to the prop's top-level path.
324
+ */
325
+ key?: string;
326
+ /**
327
+ * Resolve the value for this response regardless of the client cache. Returns
328
+ * to normal once semantics on the next response.
329
+ */
330
+ fresh?: boolean;
331
+ };
332
+ /**
333
+ * Represents a prop that is remembered by the client across visits. The server
334
+ * skips re-resolving it when the client reports a fresh cached value, and always
335
+ * emits the prop's caching metadata in the page object's `onceProps` field.
336
+ *
337
+ * @template T - The wrapped value or inner prop wrapper (`defer`/`optional`/`merge`)
338
+ */
339
+ export type OnceProp<T> = {
340
+ /** The wrapped value or inner prop wrapper */
341
+ value: T;
342
+ /** Custom once-key; `undefined` means "use the prop path" */
343
+ onceKey?: string;
344
+ /** Raw expiry config, normalized to epoch-ms at build time */
345
+ expiry: OnceExpiry;
346
+ /** Force resolution for this response, ignoring the client cache */
347
+ fresh: boolean;
348
+ /** Brand symbol to identify this as a once prop */
349
+ [ONCE_PROP]: true;
350
+ };
351
+ /**
352
+ * Per-request context that drives the once-prop resolution gate while building
353
+ * props. Passed from the Inertia instance into the prop builders.
354
+ */
355
+ export type OnceContext = {
356
+ /** Once-keys the client already holds a fresh cached value for */
357
+ exceptOnce: Set<string>;
358
+ /** Reference timestamp (epoch-ms) used to normalize relative expiry */
359
+ now: number;
101
360
  };
102
361
  /**
103
362
  * Lazy props are never included during standard Inertia visits
@@ -157,12 +416,47 @@ export type PagePropsDataTypes<T extends JSONDataTypes = JSONDataTypes> = PagePr
157
416
  * Record type representing all page props that can be passed to an Inertia page
158
417
  * Maps prop names to their corresponding data types, including branded types for special behavior
159
418
  */
160
- export type PageProps = Record<string, PagePropsDataTypes | MergeableProp<UnPackedPageProps | DeferProp<UnPackedPageProps>>>;
419
+ export type PageProps = Record<string, PagePropsDataTypes | MergeableProp<UnPackedPageProps | DeferProp<UnPackedPageProps>> | ScrollProp<any, boolean> | OnceProp<PagePropsDataTypes | MergeableProp<UnPackedPageProps | DeferProp<UnPackedPageProps>>>>;
161
420
  /**
162
421
  * Record type representing component props as they appear on the frontend after serialization
163
422
  * Maps prop names to JSON-serializable values that components can consume directly
164
423
  */
165
424
  export type ComponentProps = Record<string, JSONDataTypes>;
425
+ /**
426
+ * Map of once-prop caching metadata, keyed by once-key, accumulated while
427
+ * building a response.
428
+ */
429
+ export type OncePropsMap = {
430
+ [onceKey: string]: {
431
+ prop: string;
432
+ expiresAt?: number | null;
433
+ };
434
+ };
435
+ /**
436
+ * A pending resolution entry collected while classifying props: either a plain
437
+ * value/callback to serialize into a prop, or a scroll prop whose serialized
438
+ * value and pagination cursor are resolved together.
439
+ */
440
+ export type UnpackEntry = {
441
+ key: string;
442
+ value: UnPackedPageProps | (() => AsyncOrSync<UnPackedPageProps>);
443
+ /**
444
+ * When `true`, a resolution error is caught: the value is omitted and the
445
+ * key is recorded in `rescuedProps`. Set only for rescuable deferred props.
446
+ */
447
+ rescue?: boolean;
448
+ } | {
449
+ key: string;
450
+ scroll: ScrollProp<any, boolean>;
451
+ };
452
+ /**
453
+ * Predicate that resolves to `true` when a prop value may be absent on the
454
+ * client (optional/deferred, possibly undefined, a deferred merge, or a once
455
+ * prop wrapping any of those).
456
+ *
457
+ * @template Value - The prop value type to classify
458
+ */
459
+ export type IsOptionalPropValue<Value> = [Value] extends [OptionalProp<any>] ? true : [Value] extends [DeferProp<any>] ? true : [undefined] extends [Value] ? true : [Value] extends [MergeableProp<infer A>] ? [A] extends [DeferProp<any>] ? true : false : [Value] extends [ScrollProp<any, true>] ? true : [Value] extends [ScrollProp<any, false>] ? false : [Value] extends [OnceProp<infer Inner>] ? IsOptionalPropValue<Inner> : false;
166
460
  /**
167
461
  * Utility type to extract optional and deferred prop keys from a props object
168
462
  * Identifies props that are not required and may not be present in the component
@@ -170,7 +464,7 @@ export type ComponentProps = Record<string, JSONDataTypes>;
170
464
  * @template Props - The page props object type to analyze
171
465
  */
172
466
  export type GetOptionalProps<Props> = {
173
- [K in keyof Props]: Props[K] extends OptionalProp<any> ? K : Props[K] extends DeferProp<any> ? K : [undefined] extends [Props[K]] ? K : Props[K] extends MergeableProp<infer A> ? A extends DeferProp<any> ? K : never : never;
467
+ [K in keyof Props]: IsOptionalPropValue<Props[K]> extends true ? K : never;
174
468
  }[keyof Props];
175
469
  /**
176
470
  * Utility type to extract required prop keys from a props object
@@ -179,7 +473,7 @@ export type GetOptionalProps<Props> = {
179
473
  * @template Props - The page props object type to analyze
180
474
  */
181
475
  export type GetRequiredProps<Props> = {
182
- [K in keyof Props]: Props[K] extends OptionalProp<any> ? never : Props[K] extends DeferProp<any> ? never : [undefined] extends [Props[K]] ? never : Props[K] extends MergeableProp<infer A> ? A extends DeferProp<any> ? never : K : K;
476
+ [K in keyof Props]: IsOptionalPropValue<Props[K]> extends true ? never : K;
183
477
  }[keyof Props];
184
478
  /**
185
479
  * Utility type to simplify value of a required prop by unwrapping branded types
@@ -187,14 +481,14 @@ export type GetRequiredProps<Props> = {
187
481
  *
188
482
  * @template Value - The prop value type to unwrap
189
483
  */
190
- export type GetRequiredPropValue<Value> = Value extends AlwaysProp<infer A> ? UnpackProp<A> : Value extends MergeableProp<infer B> ? UnpackProp<B> : Value extends () => AsyncOrSync<infer C> ? UnpackProp<C> : UnpackProp<Value>;
484
+ export type GetRequiredPropValue<Value> = Value extends OnceProp<infer Inner> ? GetRequiredPropValue<Inner> : Value extends AlwaysProp<infer A> ? UnpackProp<A> : Value extends MergeableProp<infer B> ? UnpackProp<B> : Value extends ScrollProp<infer Item, any> ? Scroll<Item> : Value extends () => AsyncOrSync<infer C> ? UnpackProp<C> : UnpackProp<Value>;
191
485
  /**
192
486
  * Utility type to simplify value of an optional prop by unwrapping branded types
193
487
  * Extracts the actual value type from wrapped optional prop types like DeferProp, OptionalProp, etc.
194
488
  *
195
489
  * @template Value - The optional prop value type to unwrap
196
490
  */
197
- export type GetOptionalPropValue<Value> = Value extends DeferProp<infer A> ? UnpackProp<A> : Value extends MergeableProp<infer B> ? B extends DeferProp<infer BA> ? UnpackProp<BA> : UnpackProp<B> : Value extends OptionalProp<infer C> ? UnpackProp<C> : Value extends () => AsyncOrSync<infer D> ? UnpackProp<D> : UnpackProp<Value>;
491
+ export type GetOptionalPropValue<Value> = Value extends OnceProp<infer Inner> ? GetOptionalPropValue<Inner> : Value extends DeferProp<infer A> ? UnpackProp<A> : Value extends MergeableProp<infer B> ? B extends DeferProp<infer BA> ? UnpackProp<BA> : UnpackProp<B> : Value extends ScrollProp<infer Item, any> ? Scroll<Item> : Value extends OptionalProp<infer C> ? UnpackProp<C> : Value extends () => AsyncOrSync<infer D> ? UnpackProp<D> : UnpackProp<Value>;
198
492
  /**
199
493
  * Converts the Page props to Component props that will be available to the frontend
200
494
  * app after serialization. Maps server-side prop definitions to client-side prop types
@@ -216,11 +510,11 @@ export type ToComponentProps<Props extends PageProps> = Prettify<{
216
510
  export type AsPageProps<Props extends ComponentProps> = Prettify<{
217
511
  [K in {
218
512
  [O in keyof Props]: [undefined] extends [Props[O]] ? O : never;
219
- }[keyof Props]]?: PagePropsDataTypes<Props[K]> | MergeableProp<UnPackedPageProps<Props[K]> | DeferProp<UnPackedPageProps<Props[K]>>>;
513
+ }[keyof Props]]?: PagePropsDataTypes<Props[K]> | MergeableProp<UnPackedPageProps<Props[K]> | DeferProp<UnPackedPageProps<Props[K]>>> | ScrollProp<ScrollItemOf<Props[K]>, boolean> | OnceProp<PagePropsDataTypes<Props[K]> | MergeableProp<UnPackedPageProps<Props[K]> | DeferProp<UnPackedPageProps<Props[K]>>>>;
220
514
  } & {
221
515
  [K in {
222
516
  [O in keyof Props]: [undefined] extends [Props[O]] ? never : O;
223
- }[keyof Props]]: PagePropsEagerDataTypes<Props[K]> | MergeableProp<UnPackedPageProps<Props[K]>>;
517
+ }[keyof Props]]: PagePropsEagerDataTypes<Props[K]> | MergeableProp<UnPackedPageProps<Props[K]>> | ScrollProp<ScrollItemOf<Props[K]>, false> | OnceProp<PagePropsEagerDataTypes<Props[K]> | MergeableProp<UnPackedPageProps<Props[K]>>>;
224
518
  }>;
225
519
  /**
226
520
  * Allowed values for the assets version used for cache busting
@@ -297,6 +591,22 @@ export type PageObject<Props> = {
297
591
  * Current URL of the page
298
592
  */
299
593
  url: string;
594
+ /**
595
+ * Top-level keys registered through the `share()` pipeline (Inertia v3). The
596
+ * client uses it to carry shared prop values forward across instant
597
+ * (client-side) visits, where it swaps to the target component before the
598
+ * server responds and needs to know which props to keep from the current page.
599
+ *
600
+ * This is registration metadata, not a snapshot of `props`: every shared key is
601
+ * listed regardless of whether its value is present this response. A shared prop
602
+ * skipped as deferred/optional, or filtered out by a partial reload, is still
603
+ * listed — so the client keeps treating it as shared. Page-prop overrides keep
604
+ * the key too. Mirrors inertia-laravel, which collects the keys before
605
+ * resolution and filtering. Omitted entirely when no shared keys exist, so the
606
+ * default wire format is unchanged and the v2 client (which has no such field)
607
+ * is unaffected.
608
+ */
609
+ sharedProps?: string[];
300
610
  /**
301
611
  * Grouped deferred props that can be loaded after the initial page
302
612
  * load
@@ -314,6 +624,52 @@ export type PageObject<Props> = {
314
624
  * existing props on the page
315
625
  */
316
626
  deepMergeProps?: string[];
627
+ /**
628
+ * An array with the keys of props whose incoming array value should be
629
+ * prepended to (rather than appended onto) the existing array on the page
630
+ */
631
+ prependProps?: string[];
632
+ /**
633
+ * Keyed-merge configuration. Each entry is `"<propPath>.<matchField>"`; the
634
+ * client dedupes/replaces incoming array items by the match field instead of
635
+ * concatenating. The client splits each entry on its last dot.
636
+ */
637
+ matchPropsOn?: string[];
638
+ /**
639
+ * Pagination cursors for infinite-scroll props, keyed by prop name. Each entry
640
+ * tells the client which page-name query parameter to use and the identifiers
641
+ * for the previous/current/next pages, plus a `reset` flag.
642
+ */
643
+ scrollProps?: {
644
+ [prop: string]: ScrollMetaData;
645
+ };
646
+ /**
647
+ * Paths of deferred props whose resolution threw and was rescued. The prop is
648
+ * omitted from `props`; the client renders the `<Deferred>` `rescue` slot for
649
+ * each listed path. Emitted on every response (as `[]` when nothing was
650
+ * rescued) to match the non-optional v3 `Page.rescuedProps` field.
651
+ */
652
+ rescuedProps?: string[];
653
+ /**
654
+ * Metadata for props the client should remember across visits, keyed by
655
+ * once-key. Emitted even when the value itself is skipped, so the client can
656
+ * keep using its cached copy.
657
+ */
658
+ onceProps?: {
659
+ [onceKey: string]: {
660
+ prop: string;
661
+ expiresAt?: number | null;
662
+ };
663
+ };
664
+ /**
665
+ * First-class flash bag (Inertia v2.3+). Lives alongside `props` rather than
666
+ * inside it; the client treats it as ephemeral — stripped from history state
667
+ * and surfaced via the `onFlash` visit callback and the `inertia:flash` event.
668
+ * Populated from the Inertia middleware's `flash()` method; omitted entirely
669
+ * when no flash provider is registered, so the default wire format is
670
+ * unchanged.
671
+ */
672
+ flash?: FlashData;
317
673
  /**
318
674
  * Encrypt history flag to be sent to the client with every request.
319
675
  */
@@ -323,6 +679,12 @@ export type PageObject<Props> = {
323
679
  */
324
680
  clearHistory?: boolean;
325
681
  };
682
+ /**
683
+ * Default shape of the first-class flash bag. Apps narrow the actual shape by
684
+ * typing the middleware's `flash()` method return type and bridging it into the
685
+ * client via {@link InferFlashData}.
686
+ */
687
+ export type FlashData = Record<string, any>;
326
688
  /**
327
689
  * The shared props inferred from the user-land
328
690
  * Should be augmented in the host application to define globally available props
@@ -390,4 +752,32 @@ export type RenderInertiaSsrApp = (page: PageObject<any>) => Promise<{
390
752
  export type InferSharedProps<T> = T extends {
391
753
  share(...args: any[]): infer R;
392
754
  } ? Awaited<R> extends PageProps ? ToComponentProps<Awaited<R>> : never : never;
755
+ /**
756
+ * Type helper to infer the flash-bag shape from the `flash()` method of an
757
+ * Inertia middleware, mirroring {@link InferSharedProps}. Unlike shared props,
758
+ * flash is plain JSON (no branded prop wrappers), so the method's return type is
759
+ * used as-is. Bridge the result into the client's `@inertiajs/core`
760
+ * `flashDataType` config so `page.flash`, the `onFlash` callback, and
761
+ * `router.flash()` are typed end-to-end from the server's `flash()` method.
762
+ *
763
+ * @template T - The middleware class type that defines a `flash` method
764
+ *
765
+ * @example
766
+ * ```typescript
767
+ * class InertiaMiddleware extends BaseInertiaMiddleware {
768
+ * flash(ctx: HttpContext) {
769
+ * return ctx.session.flashMessages.all()
770
+ * }
771
+ * }
772
+ *
773
+ * declare module '@inertiajs/core' {
774
+ * interface InertiaConfig {
775
+ * flashDataType: InferFlashData<InertiaMiddleware>
776
+ * }
777
+ * }
778
+ * ```
779
+ */
780
+ export type InferFlashData<T> = T extends {
781
+ flash(...args: any[]): infer R;
782
+ } ? Awaited<R> extends Record<string, any> ? Awaited<R> : never : never;
393
783
  export {};
@@ -1,10 +1,36 @@
1
1
  import { type InlineConfig } from 'vite';
2
2
  import type { Test } from '@japa/runner/core';
3
+ import { BaseTransformer } from '@adonisjs/core/transformers';
3
4
  import { type ProviderNode } from '@adonisjs/core/types/app';
4
5
  import { type ApplicationService } from '@adonisjs/core/types';
5
6
  import { Vite } from '@adonisjs/vite';
6
7
  import { type IncomingMessage, type ServerResponse } from 'node:http';
7
8
  export declare const BASE_URL: import("node:url").URL;
9
+ /**
10
+ * Shared fixtures for the infinite-scroll tests: a pass-through transformer used
11
+ * to build transformer paginators, a sample row set, and a Lucid-`getMeta()`-
12
+ * shaped metadata builder for a given page.
13
+ */
14
+ export type User = {
15
+ id: number;
16
+ name: string;
17
+ };
18
+ export declare class UserTransformer extends BaseTransformer<User> {
19
+ toObject(): User;
20
+ }
21
+ export declare const userRows: User[];
22
+ export declare function paginatorMeta(currentPage: number, lastPage: number, pageName?: string): {
23
+ total: number;
24
+ perPage: number;
25
+ currentPage: number;
26
+ pageName: string;
27
+ lastPage: number;
28
+ firstPage: number;
29
+ firstPageUrl: string;
30
+ lastPageUrl: string;
31
+ nextPageUrl: string | null;
32
+ previousPageUrl: string | null;
33
+ };
8
34
  /**
9
35
  * Create a http server that will be closed automatically
10
36
  * when the test ends
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@adonisjs/inertia",
3
3
  "description": "Official Inertia.js adapter for AdonisJS",
4
- "version": "5.0.0-next.0",
4
+ "version": "5.0.0-next.1",
5
5
  "engines": {
6
6
  "node": ">=24.0.0"
7
7
  },
@@ -40,51 +40,53 @@
40
40
  "format": "prettier --write .",
41
41
  "prepublishOnly": "npm run build",
42
42
  "lint": "eslint",
43
+ "lint:ast": "ast-grep scan",
43
44
  "index:commands": "adonis-kit index build/commands",
44
45
  "quick:test": "cross-env NODE_DEBUG=adonisjs:inertia node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts",
45
46
  "docs": "typedoc"
46
47
  },
47
48
  "devDependencies": {
48
- "@adonisjs/assembler": "^8.0.0",
49
- "@adonisjs/core": "^7.0.0",
50
- "@adonisjs/eslint-config": "^3.0.0",
51
- "@adonisjs/prettier-config": "^1.4.5",
52
- "@adonisjs/session": "^8.0.0",
49
+ "@adonisjs/assembler": "^8.4.0",
50
+ "@adonisjs/core": "^7.3.5",
51
+ "@adonisjs/eslint-config": "^3.1.0",
52
+ "@adonisjs/prettier-config": "^1.5.0",
53
+ "@adonisjs/session": "^8.1.0",
53
54
  "@adonisjs/tsconfig": "^2.0.0",
54
- "@adonisjs/vite": "^6.0.0-next.0",
55
- "@inertiajs/react": "^3.4.0",
56
- "@inertiajs/vue3": "^3.4.0",
55
+ "@adonisjs/vite": "^6.0.0-next.1",
56
+ "@ast-grep/cli": "^0.44.1",
57
+ "@inertiajs/react": "^3.6.1",
58
+ "@inertiajs/vue3": "^3.6.1",
57
59
  "@japa/api-client": "^3.2.1",
58
60
  "@japa/assert": "4.2.0",
59
61
  "@japa/expect-type": "^2.0.4",
60
62
  "@japa/file-system": "^3.0.0",
61
- "@japa/plugin-adonisjs": "^5.1.0",
63
+ "@japa/plugin-adonisjs": "^5.2.0",
62
64
  "@japa/runner": "5.3.0",
63
65
  "@japa/snapshot": "^2.0.10",
64
66
  "@poppinss/ts-exec": "^1.4.4",
65
- "@release-it/conventional-changelog": "^10.0.5",
66
- "@tuyau/core": "^1.0.0",
67
- "@types/node": "^25.3.0",
68
- "@types/react": "^19.2.14",
69
- "@types/supertest": "^6.0.3",
67
+ "@release-it/conventional-changelog": "^11.0.1",
68
+ "@tuyau/core": "^1.2.2",
69
+ "@types/node": "^26.1.1",
70
+ "@types/react": "^19.2.17",
71
+ "@types/supertest": "^7.2.1",
70
72
  "c8": "^11.0.0",
71
73
  "copyfiles": "^2.4.1",
72
74
  "cross-env": "^10.1.0",
73
75
  "del-cli": "^7.0.0",
74
- "edge.js": "^6.5.0",
75
- "eslint": "^10.0.2",
76
- "get-port": "^7.1.0",
77
- "prettier": "^3.8.1",
78
- "react": "^19.2.4",
79
- "release-it": "^19.2.4",
76
+ "edge.js": "^6.5.1",
77
+ "eslint": "^10.7.0",
78
+ "get-port": "^7.2.0",
79
+ "prettier": "^3.9.5",
80
+ "react": "^19.2.7",
81
+ "release-it": "^20.2.1",
80
82
  "supertest": "^7.2.2",
81
- "tsdown": "^0.20.3",
82
- "typescript": "~5.9.3",
83
- "vite": "^8.0.0",
84
- "vue": "^3.5.29"
83
+ "tsdown": "^0.22.7",
84
+ "typescript": "~6.0.3",
85
+ "vite": "^8.1.4",
86
+ "vue": "^3.5.39"
85
87
  },
86
88
  "dependencies": {
87
- "@poppinss/utils": "^7.0.0",
89
+ "@poppinss/utils": "^7.0.1",
88
90
  "edge-error": "^4.0.2",
89
91
  "html-entities": "^2.6.0"
90
92
  },
@@ -1,3 +0,0 @@
1
- import { debuglog } from "node:util";
2
- var debug_default = debuglog("adonisjs:inertia");
3
- export { debug_default as t };