@adonisjs/inertia 4.0.0-next.0 → 4.0.0-next.10
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/README.md +8 -5
- package/build/bin/test.d.ts +1 -0
- package/build/chunk-4EZ2J6OA.js +7 -0
- package/build/chunk-5QRJHXXQ.js +91 -0
- package/build/chunk-DISC5OYC.js +46 -0
- package/build/chunk-MLKGABMK.js +9 -0
- package/build/chunk-YQ72YL64.js +813 -0
- package/build/factories/inertia_factory.d.ts +137 -0
- package/build/factories/main.d.ts +1 -0
- package/build/factories/main.js +175 -0
- package/build/index.d.ts +7 -19
- package/build/index.js +21 -307
- package/build/providers/inertia_provider.d.ts +86 -13
- package/build/providers/inertia_provider.js +48 -18
- package/build/src/client/helpers.d.ts +27 -0
- package/build/src/client/helpers.js +30 -0
- package/build/src/client/react/context.d.ts +24 -0
- package/build/src/client/react/index.d.ts +3 -0
- package/build/src/client/react/index.js +72 -0
- package/build/src/client/react/link.d.ts +64 -0
- package/build/src/client/react/router.d.ts +33 -0
- package/build/src/client/vite.d.ts +65 -0
- package/build/src/{plugins → client}/vite.js +6 -2
- package/build/src/debug.d.ts +22 -0
- package/build/src/define_config.d.ts +30 -0
- package/build/src/headers.d.ts +61 -0
- package/build/src/index_pages.d.ts +32 -0
- package/build/src/inertia.d.ts +261 -0
- package/build/src/inertia_manager.d.ts +47 -0
- package/build/src/inertia_middleware.d.ts +76 -86
- package/build/src/inertia_middleware.js +110 -3
- package/build/src/plugins/edge/plugin.d.ts +30 -6
- package/build/src/plugins/edge/plugin.js +13 -9
- package/build/src/plugins/edge/tags.d.ts +47 -0
- package/build/src/plugins/edge/utils.d.ts +26 -0
- package/build/src/plugins/japa/api_client.d.ts +136 -22
- package/build/src/plugins/japa/api_client.js +36 -48
- package/build/src/props.d.ts +276 -0
- package/build/src/server_renderer.d.ts +54 -0
- package/build/src/symbols.d.ts +25 -0
- package/build/src/types.d.ts +400 -4
- package/build/tests/helpers.d.ts +35 -0
- package/build/tests/index_pages.spec.d.ts +1 -0
- package/build/tests/inertia.spec.d.ts +1 -0
- package/build/tests/inertia_page.spec.d.ts +1 -0
- package/build/tests/middleware.spec.d.ts +1 -0
- package/build/tests/plugins/api_client.spec.d.ts +1 -0
- package/build/tests/plugins/edge.plugin.spec.d.ts +1 -0
- package/build/tests/provider.spec.d.ts +1 -0
- package/build/tests/types/react.spec.d.ts +65 -0
- package/build/tests/types/shared_props.spec.d.ts +1 -0
- package/build/tests/types/to_component_props.spec.d.ts +1 -0
- package/build/tests/types/to_page_props.spec.d.ts +1 -0
- package/package.json +99 -71
- package/build/app.css.stub +0 -13
- package/build/chunk-AWCR2NAY.js +0 -412
- package/build/config.stub +0 -33
- package/build/react/app.tsx.stub +0 -38
- package/build/react/errors/not_found.tsx.stub +0 -14
- package/build/react/errors/server_error.tsx.stub +0 -14
- package/build/react/home.tsx.stub +0 -349
- package/build/react/root.edge.stub +0 -76
- package/build/react/ssr.tsx.stub +0 -17
- package/build/react/tsconfig.json.stub +0 -15
- package/build/solid/app.tsx.stub +0 -38
- package/build/solid/errors/not_found.tsx.stub +0 -14
- package/build/solid/errors/server_error.tsx.stub +0 -14
- package/build/solid/home.tsx.stub +0 -358
- package/build/solid/root.edge.stub +0 -73
- package/build/solid/ssr.tsx.stub +0 -19
- package/build/solid/tsconfig.json.stub +0 -16
- package/build/src/helpers.d.ts +0 -12
- package/build/src/helpers.js +0 -14
- package/build/src/plugins/vite.d.ts +0 -26
- package/build/svelte/app.ts.stub +0 -32
- package/build/svelte/errors/not_found.svelte.stub +0 -10
- package/build/svelte/errors/server_error.svelte.stub +0 -14
- package/build/svelte/home.svelte.stub +0 -339
- package/build/svelte/root.edge.stub +0 -75
- package/build/svelte/ssr.ts.stub +0 -19
- package/build/svelte/tsconfig.json.stub +0 -14
- package/build/types-DVqEHBD1.d.ts +0 -240
- package/build/vue/app.ts.stub +0 -41
- package/build/vue/errors/not_found.vue.stub +0 -10
- package/build/vue/errors/server_error.vue.stub +0 -14
- package/build/vue/home.vue.stub +0 -343
- package/build/vue/root.edge.stub +0 -75
- package/build/vue/ssr.ts.stub +0 -22
- package/build/vue/tsconfig.json.stub +0 -16
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
debug_default
|
|
3
|
+
} from "../../../chunk-4EZ2J6OA.js";
|
|
4
|
+
import "../../../chunk-MLKGABMK.js";
|
|
5
|
+
|
|
1
6
|
// src/plugins/edge/plugin.ts
|
|
2
7
|
import { encode } from "html-entities";
|
|
3
8
|
|
|
4
|
-
// src/debug.ts
|
|
5
|
-
import { debuglog } from "util";
|
|
6
|
-
var debug_default = debuglog("adonisjs:inertia");
|
|
7
|
-
|
|
8
9
|
// src/plugins/edge/tags.ts
|
|
9
10
|
import { EdgeError } from "edge-error";
|
|
10
11
|
|
|
@@ -40,10 +41,11 @@ var inertiaTag = {
|
|
|
40
41
|
);
|
|
41
42
|
});
|
|
42
43
|
const attributes = parser.utils.stringify(parsed);
|
|
43
|
-
buffer.
|
|
44
|
-
`
|
|
44
|
+
buffer.outputExpression(
|
|
45
|
+
`state.inertia(state.page, ${attributes})`,
|
|
45
46
|
filename,
|
|
46
|
-
loc.start.line
|
|
47
|
+
loc.start.line,
|
|
48
|
+
false
|
|
47
49
|
);
|
|
48
50
|
}
|
|
49
51
|
};
|
|
@@ -52,7 +54,7 @@ var inertiaHeadTag = {
|
|
|
52
54
|
tagName: "inertiaHead",
|
|
53
55
|
seekable: false,
|
|
54
56
|
compile(_, buffer, { filename, loc }) {
|
|
55
|
-
buffer.
|
|
57
|
+
buffer.outputExpression("state.inertiaHead(state.page)", filename, loc.start.line, false);
|
|
56
58
|
}
|
|
57
59
|
};
|
|
58
60
|
|
|
@@ -63,7 +65,9 @@ var edgePluginInertia = () => {
|
|
|
63
65
|
edge.global(
|
|
64
66
|
"inertia",
|
|
65
67
|
(page = {}, attributes = {}) => {
|
|
66
|
-
if (page.ssrBody)
|
|
68
|
+
if (page.ssrBody) {
|
|
69
|
+
return page.ssrBody;
|
|
70
|
+
}
|
|
67
71
|
const className = attributes?.class ? ` class="${attributes.class}"` : "";
|
|
68
72
|
const id = attributes?.id ? ` id="${attributes.id}"` : ' id="app"';
|
|
69
73
|
const tag = attributes?.as || "div";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type TagContract } from 'edge.js/types';
|
|
2
|
+
/**
|
|
3
|
+
* Edge tag that generates the root element for Inertia.js applications
|
|
4
|
+
*
|
|
5
|
+
* The @inertia tag creates a container element with encoded page data that Inertia.js
|
|
6
|
+
* uses to hydrate the client-side application. Supports customization through attributes.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```edge
|
|
10
|
+
* {{-- Basic usage with default div element and id="app" --}}
|
|
11
|
+
* @inertia()
|
|
12
|
+
*
|
|
13
|
+
* {{-- Custom element and attributes --}}
|
|
14
|
+
* @inertia({ as: 'main', id: 'app-root', class: 'min-h-screen' })
|
|
15
|
+
*
|
|
16
|
+
* {{-- Results in: --}}
|
|
17
|
+
* {{-- <main id="app-root" class="min-h-screen" data-page="{...encoded page data...}"></main> --}}
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Supported attributes:
|
|
21
|
+
* - `as`: HTML tag name for the root element (defaults to 'div')
|
|
22
|
+
* - `id`: Element ID (defaults to 'app')
|
|
23
|
+
* - `class`: CSS class names for the element
|
|
24
|
+
*/
|
|
25
|
+
export declare const inertiaTag: TagContract;
|
|
26
|
+
/**
|
|
27
|
+
* Edge tag that renders server-side rendered head content for Inertia.js
|
|
28
|
+
*
|
|
29
|
+
* The @inertiaHead tag outputs head tags (title, meta, etc.) that were generated
|
|
30
|
+
* during server-side rendering. Only relevant when SSR is enabled.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```edge
|
|
34
|
+
* <!DOCTYPE html>
|
|
35
|
+
* <html>
|
|
36
|
+
* <head>
|
|
37
|
+
* <meta charset="utf-8">
|
|
38
|
+
* <meta name="viewport" content="width=device-width, initial-scale=1">
|
|
39
|
+
* @inertiaHead()
|
|
40
|
+
* </head>
|
|
41
|
+
* <body>
|
|
42
|
+
* @inertia()
|
|
43
|
+
* </body>
|
|
44
|
+
* </html>
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare const inertiaHeadTag: TagContract;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates that an AST expression is one of the allowed expression types
|
|
3
|
+
*
|
|
4
|
+
* This utility function is used during Edge template compilation to ensure
|
|
5
|
+
* that only specific expression types are allowed in certain contexts.
|
|
6
|
+
*
|
|
7
|
+
* @param expression - The AST expression to validate
|
|
8
|
+
* @param expressions - Array of allowed expression type names
|
|
9
|
+
* @param errorCallback - Function to call when validation fails
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```js
|
|
13
|
+
* // Ensure expression is either a literal or identifier
|
|
14
|
+
* isSubsetOf(astNode, ['Literal', 'Identifier'], () => {
|
|
15
|
+
* throw new Error('Invalid expression type')
|
|
16
|
+
* })
|
|
17
|
+
*
|
|
18
|
+
* // Allow only object expressions
|
|
19
|
+
* isSubsetOf(astNode, ['ObjectExpression'], () => {
|
|
20
|
+
* throw new EdgeError('Expected object expression')
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function isSubsetOf(expression: {
|
|
25
|
+
type: string;
|
|
26
|
+
}, expressions: string[], errorCallback: () => void): void;
|
|
@@ -1,43 +1,157 @@
|
|
|
1
|
-
import { PluginFn } from '@japa/runner/types';
|
|
2
|
-
import { ApplicationService } from '@adonisjs/core/types';
|
|
3
|
-
import {
|
|
4
|
-
import '@adonisjs/core/http';
|
|
5
|
-
import '@tuyau/utils/types';
|
|
6
|
-
|
|
1
|
+
import type { PluginFn } from '@japa/runner/types';
|
|
2
|
+
import type { ApplicationService } from '@adonisjs/core/types';
|
|
3
|
+
import type { PageProps, InertiaPages } from '../../types.js';
|
|
7
4
|
declare module '@japa/api-client' {
|
|
5
|
+
/**
|
|
6
|
+
* Extended ApiRequest interface with Inertia.js specific methods
|
|
7
|
+
*
|
|
8
|
+
* Adds methods to configure requests for testing Inertia applications,
|
|
9
|
+
* including setting required headers and configuring partial reloads.
|
|
10
|
+
*/
|
|
8
11
|
interface ApiRequest {
|
|
9
12
|
/**
|
|
10
|
-
* Set `X-Inertia` header on the request
|
|
13
|
+
* Set `X-Inertia` header on the request to mark it as an Inertia request
|
|
14
|
+
*
|
|
15
|
+
* This method configures the request to be treated as an Inertia AJAX request
|
|
16
|
+
* by setting the required headers that Inertia.js uses for identification.
|
|
17
|
+
*
|
|
18
|
+
* @returns The ApiRequest instance for method chaining
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```js
|
|
22
|
+
* const response = await client
|
|
23
|
+
* .get('/dashboard')
|
|
24
|
+
* .withInertia()
|
|
25
|
+
* ```
|
|
11
26
|
*/
|
|
12
|
-
withInertia(): this;
|
|
27
|
+
withInertia(this: ApiRequest): this;
|
|
13
28
|
/**
|
|
14
|
-
* Set
|
|
29
|
+
* Set headers for partial data requests (partial reloads)
|
|
30
|
+
*
|
|
31
|
+
* Configures the request to only fetch specific props from a component,
|
|
32
|
+
* simulating Inertia's partial reload functionality in tests.
|
|
33
|
+
*
|
|
34
|
+
* @param component - The component name to partially reload
|
|
35
|
+
* @param props - Array of prop names to include in the partial request
|
|
36
|
+
* @returns The ApiRequest instance for method chaining
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```js
|
|
40
|
+
* const response = await client
|
|
41
|
+
* .get('/users')
|
|
42
|
+
* .withInertiaPartialReload('Users/Index', ['users', 'pagination'])
|
|
43
|
+
* ```
|
|
15
44
|
*/
|
|
16
|
-
withInertiaPartialReload(component:
|
|
45
|
+
withInertiaPartialReload<K extends keyof InertiaPages>(this: ApiRequest, component: K, props: (keyof InertiaPages[K])[]): this;
|
|
17
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Extended ApiResponse interface with Inertia.js specific properties and assertions
|
|
49
|
+
*
|
|
50
|
+
* Provides getters for accessing Inertia response data and assertion methods
|
|
51
|
+
* for validating Inertia responses in tests.
|
|
52
|
+
*/
|
|
18
53
|
interface ApiResponse {
|
|
19
54
|
/**
|
|
20
|
-
* The
|
|
55
|
+
* The name of the Inertia component returned in the response
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```js
|
|
59
|
+
* console.log(response.inertiaComponent) // 'Users/Index'
|
|
60
|
+
* ```
|
|
21
61
|
*/
|
|
22
|
-
inertiaComponent?:
|
|
62
|
+
inertiaComponent?: keyof InertiaPages;
|
|
23
63
|
/**
|
|
24
|
-
* The
|
|
64
|
+
* The props data returned in the Inertia response
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```js
|
|
68
|
+
* console.log(response.inertiaProps.users) // [{ id: 1, name: 'John' }]
|
|
69
|
+
* ```
|
|
25
70
|
*/
|
|
26
71
|
inertiaProps: Record<string, any>;
|
|
27
72
|
/**
|
|
28
|
-
* Assert
|
|
73
|
+
* Assert that the response contains the expected Inertia component
|
|
74
|
+
*
|
|
75
|
+
* @param component - Expected component name
|
|
76
|
+
* @returns The ApiResponse instance for method chaining
|
|
77
|
+
*
|
|
78
|
+
* @throws AssertionError when component names don't match
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```js
|
|
82
|
+
* response.assertInertiaComponent('Users/Index')
|
|
83
|
+
* ```
|
|
29
84
|
*/
|
|
30
|
-
assertInertiaComponent(component: string): this;
|
|
85
|
+
assertInertiaComponent(this: ApiResponse, component: string): this;
|
|
31
86
|
/**
|
|
32
|
-
* Assert
|
|
87
|
+
* Assert that the response props exactly match the provided props
|
|
88
|
+
*
|
|
89
|
+
* @param props - Expected props object to match exactly
|
|
90
|
+
* @returns The ApiResponse instance for method chaining
|
|
91
|
+
*
|
|
92
|
+
* @throws AssertionError when props don't match exactly
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```js
|
|
96
|
+
* response.assertInertiaProps({
|
|
97
|
+
* users: [{ id: 1, name: 'John' }],
|
|
98
|
+
* total: 1
|
|
99
|
+
* })
|
|
100
|
+
* ```
|
|
33
101
|
*/
|
|
34
|
-
assertInertiaProps(props: PageProps): this;
|
|
102
|
+
assertInertiaProps(this: ApiResponse, props: PageProps): this;
|
|
35
103
|
/**
|
|
36
|
-
* Assert
|
|
104
|
+
* Assert that the response props contain a subset of the provided props
|
|
105
|
+
*
|
|
106
|
+
* @param props - Expected subset of props to be present
|
|
107
|
+
* @returns The ApiResponse instance for method chaining
|
|
108
|
+
*
|
|
109
|
+
* @throws AssertionError when expected props are not found
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```js
|
|
113
|
+
* response.assertInertiaPropsContains({
|
|
114
|
+
* user: { name: 'John' }
|
|
115
|
+
* })
|
|
116
|
+
* ```
|
|
37
117
|
*/
|
|
38
|
-
assertInertiaPropsContains(props: PageProps): this;
|
|
118
|
+
assertInertiaPropsContains(this: ApiResponse, props: PageProps): this;
|
|
39
119
|
}
|
|
40
120
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Japa plugin that extends the API client with Inertia.js testing capabilities
|
|
123
|
+
*
|
|
124
|
+
* This plugin adds methods to ApiRequest and ApiResponse classes to support
|
|
125
|
+
* testing Inertia applications, including partial reloads and response assertions.
|
|
126
|
+
*
|
|
127
|
+
* @param app - The AdonisJS application service instance
|
|
128
|
+
* @returns Japa plugin function
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```js
|
|
132
|
+
* // Configure in tests/bootstrap.ts
|
|
133
|
+
* import { inertiaApiClient } from '@adonisjs/inertia/plugins/japa/api_client'
|
|
134
|
+
*
|
|
135
|
+
* export const plugins: Config['plugins'] = [
|
|
136
|
+
* assert(),
|
|
137
|
+
* apiClient(app),
|
|
138
|
+
* inertiaApiClient(app)
|
|
139
|
+
* ]
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```js
|
|
144
|
+
* // Use in tests
|
|
145
|
+
* test('renders dashboard page', async ({ client }) => {
|
|
146
|
+
* const response = await client
|
|
147
|
+
* .get('/dashboard')
|
|
148
|
+
* .withInertia()
|
|
149
|
+
*
|
|
150
|
+
* response.assertInertiaComponent('Dashboard')
|
|
151
|
+
* response.assertInertiaPropsContains({
|
|
152
|
+
* user: { name: 'John' }
|
|
153
|
+
* })
|
|
154
|
+
* })
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export declare function inertiaApiClient(app: ApplicationService): PluginFn;
|
|
@@ -1,37 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InertiaHeaders
|
|
3
|
+
} from "../../../chunk-DISC5OYC.js";
|
|
4
|
+
import "../../../chunk-MLKGABMK.js";
|
|
5
|
+
|
|
1
6
|
// src/plugins/japa/api_client.ts
|
|
2
|
-
import { configProvider } from "@adonisjs/core";
|
|
3
|
-
import { RuntimeException } from "@poppinss/utils";
|
|
4
7
|
import { ApiRequest, ApiResponse } from "@japa/api-client";
|
|
5
8
|
function ensureIsInertiaResponse() {
|
|
6
9
|
if (!this.header("x-inertia")) {
|
|
7
10
|
throw new Error(
|
|
8
|
-
|
|
11
|
+
'Not an Inertia response. Make sure to use "withInertia()" method when making the request'
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function ensureHasAssert(assertLib) {
|
|
16
|
+
if (!assertLib) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
"Response assertions are not available. Make sure to install the @japa/assert plugin"
|
|
9
19
|
);
|
|
10
20
|
}
|
|
11
21
|
}
|
|
12
22
|
function inertiaApiClient(app) {
|
|
13
23
|
return async () => {
|
|
14
|
-
const
|
|
15
|
-
const config = await configProvider.resolve(app, inertiaConfigProvider);
|
|
16
|
-
if (!config) {
|
|
17
|
-
throw new RuntimeException(
|
|
18
|
-
'Invalid "config/inertia.ts" file. Make sure you are using the "defineConfig" method'
|
|
19
|
-
);
|
|
20
|
-
}
|
|
24
|
+
const inertiaConfig = app.config.get("inertia");
|
|
21
25
|
ApiRequest.macro("withInertia", function() {
|
|
22
|
-
this.header(
|
|
23
|
-
this.header(
|
|
26
|
+
this.header(InertiaHeaders.Inertia, "true");
|
|
27
|
+
this.header(InertiaHeaders.Version, String(inertiaConfig.assetsVersion ?? "1"));
|
|
28
|
+
return this;
|
|
29
|
+
});
|
|
30
|
+
ApiRequest.macro("withInertiaPartialReload", function(component, data) {
|
|
31
|
+
this.withInertia();
|
|
32
|
+
this.header(InertiaHeaders.PartialComponent, component);
|
|
33
|
+
this.header(InertiaHeaders.PartialOnly, data.join(","));
|
|
24
34
|
return this;
|
|
25
35
|
});
|
|
26
|
-
ApiRequest.macro(
|
|
27
|
-
"withInertiaPartialReload",
|
|
28
|
-
function(component, data) {
|
|
29
|
-
this.withInertia();
|
|
30
|
-
this.header("X-Inertia-Partial-Data", data.join(","));
|
|
31
|
-
this.header("X-Inertia-Partial-Component", component);
|
|
32
|
-
return this;
|
|
33
|
-
}
|
|
34
|
-
);
|
|
35
36
|
ApiResponse.getter("inertiaComponent", function() {
|
|
36
37
|
ensureIsInertiaResponse.call(this);
|
|
37
38
|
return this.body().component;
|
|
@@ -42,35 +43,22 @@ function inertiaApiClient(app) {
|
|
|
42
43
|
});
|
|
43
44
|
ApiResponse.macro("assertInertiaComponent", function(component) {
|
|
44
45
|
ensureIsInertiaResponse.call(this);
|
|
45
|
-
this.assert
|
|
46
|
+
ensureHasAssert(this.assert);
|
|
47
|
+
this.assert.equal(this.body().component, component);
|
|
48
|
+
return this;
|
|
49
|
+
});
|
|
50
|
+
ApiResponse.macro("assertInertiaProps", function(props) {
|
|
51
|
+
ensureIsInertiaResponse.call(this);
|
|
52
|
+
ensureHasAssert(this.assert);
|
|
53
|
+
this.assert.deepEqual(this.body().props, props);
|
|
54
|
+
return this;
|
|
55
|
+
});
|
|
56
|
+
ApiResponse.macro("assertInertiaPropsContains", function(props) {
|
|
57
|
+
ensureIsInertiaResponse.call(this);
|
|
58
|
+
ensureHasAssert(this.assert);
|
|
59
|
+
this.assert.containSubset(this.body().props, props);
|
|
46
60
|
return this;
|
|
47
61
|
});
|
|
48
|
-
ApiResponse.macro(
|
|
49
|
-
"assertInertiaProps",
|
|
50
|
-
function(props) {
|
|
51
|
-
if (!this.assert) {
|
|
52
|
-
throw new Error(
|
|
53
|
-
"Response assertions are not available. Make sure to install the @japa/assert plugin"
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
ensureIsInertiaResponse.call(this);
|
|
57
|
-
this.assert.deepEqual(this.body().props, props);
|
|
58
|
-
return this;
|
|
59
|
-
}
|
|
60
|
-
);
|
|
61
|
-
ApiResponse.macro(
|
|
62
|
-
"assertInertiaPropsContains",
|
|
63
|
-
function(props) {
|
|
64
|
-
if (!this.assert) {
|
|
65
|
-
throw new Error(
|
|
66
|
-
"Response assertions are not available. Make sure to install the @japa/assert plugin"
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
ensureIsInertiaResponse.call(this);
|
|
70
|
-
this.assert.containsSubset(this.body().props, props);
|
|
71
|
-
return this;
|
|
72
|
-
}
|
|
73
|
-
);
|
|
74
62
|
};
|
|
75
63
|
}
|
|
76
64
|
export {
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { type AsyncOrSync } from '@adonisjs/core/types/common';
|
|
2
|
+
import { type DeferProp, type PageProps, type AlwaysProp, type OptionalProp, type MergeableProp, type ComponentProps, type UnPackedPageProps } from './types.ts';
|
|
3
|
+
import { type ContainerResolver } from '@adonisjs/core/container';
|
|
4
|
+
/**
|
|
5
|
+
* Creates a deferred prop that is never included in standard visits but must be shared with
|
|
6
|
+
* the client during standard visits. Can be explicitly requested and supports merging.
|
|
7
|
+
*
|
|
8
|
+
* Deferred props are useful for expensive computations that should only be loaded when
|
|
9
|
+
* specifically requested by the client.
|
|
10
|
+
*
|
|
11
|
+
* @param fn - Function that computes the prop value when requested
|
|
12
|
+
* @returns A deferred prop object with compute and merge capabilities
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```javascript
|
|
16
|
+
* // Create a deferred prop for expensive user statistics
|
|
17
|
+
* const userStats = defer(() => {
|
|
18
|
+
* return calculateExpensiveUserStats(userId)
|
|
19
|
+
* })
|
|
20
|
+
*
|
|
21
|
+
* // Use in page props
|
|
22
|
+
* return inertia.render('dashboard', {
|
|
23
|
+
* user: user,
|
|
24
|
+
* stats: userStats // Only loaded when explicitly requested
|
|
25
|
+
* })
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function defer<T extends UnPackedPageProps>(fn: () => AsyncOrSync<T>, group?: string): DeferProp<T>;
|
|
29
|
+
/**
|
|
30
|
+
* Creates an optional prop that is never included in standard visits and can only be
|
|
31
|
+
* explicitly requested by the client. Unlike deferred props, optional props are not
|
|
32
|
+
* shared with the client during standard visits.
|
|
33
|
+
*
|
|
34
|
+
* Optional props are ideal for data that is rarely needed and should only be loaded
|
|
35
|
+
* on demand to optimize performance.
|
|
36
|
+
*
|
|
37
|
+
* @param fn - Function that computes the prop value when requested
|
|
38
|
+
* @returns An optional prop object that computes values lazily
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```javascript
|
|
42
|
+
* // Create an optional prop for detailed audit logs
|
|
43
|
+
* const auditLogs = optional(() => {
|
|
44
|
+
* return fetchDetailedAuditLogs(resourceId)
|
|
45
|
+
* })
|
|
46
|
+
*
|
|
47
|
+
* // Use in page props
|
|
48
|
+
* return inertia.render('resource/show', {
|
|
49
|
+
* resource: resource,
|
|
50
|
+
* auditLogs: auditLogs // Only loaded when explicitly requested
|
|
51
|
+
* })
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function optional<T extends UnPackedPageProps>(fn: () => AsyncOrSync<T>): OptionalProp<T>;
|
|
55
|
+
/**
|
|
56
|
+
* Creates a prop that is always included in responses and cannot be removed during
|
|
57
|
+
* cherry-picking. This ensures the prop is always available to the frontend component.
|
|
58
|
+
*
|
|
59
|
+
* Always props are useful for critical data that the frontend component must have
|
|
60
|
+
* to function properly, regardless of what props are specifically requested.
|
|
61
|
+
*
|
|
62
|
+
* @param value - The value to always include in the response
|
|
63
|
+
* @returns An always prop object that cannot be cherry-picked away
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```javascript
|
|
67
|
+
* // Create an always prop for critical user permissions
|
|
68
|
+
* const userPermissions = always(user.permissions)
|
|
69
|
+
*
|
|
70
|
+
* // Use in page props
|
|
71
|
+
* return inertia.render('admin/dashboard', {
|
|
72
|
+
* users: users,
|
|
73
|
+
* permissions: userPermissions // Always included, never filtered out
|
|
74
|
+
* })
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function always<T extends UnPackedPageProps>(value: T): AlwaysProp<T>;
|
|
78
|
+
/**
|
|
79
|
+
* Creates a prop that should be merged with existing props on the page rather than
|
|
80
|
+
* replaced. This is useful for incremental updates where you want to combine new
|
|
81
|
+
* data with existing data on the client side.
|
|
82
|
+
*
|
|
83
|
+
* Mergeable props enable efficient partial updates by allowing the client to
|
|
84
|
+
* merge new prop values with existing ones instead of replacing them entirely.
|
|
85
|
+
*
|
|
86
|
+
* @param value - The value to be merged with existing props
|
|
87
|
+
* @returns A mergeable prop object marked for merging behavior
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```javascript
|
|
91
|
+
* // Create a mergeable prop for incremental notifications
|
|
92
|
+
* const newNotifications = merge([
|
|
93
|
+
* { id: 1, message: 'New message received' },
|
|
94
|
+
* { id: 2, message: 'Task completed' }
|
|
95
|
+
* ])
|
|
96
|
+
*
|
|
97
|
+
* // Use in page props - will merge with existing notifications
|
|
98
|
+
* return inertia.render('dashboard', {
|
|
99
|
+
* user: user,
|
|
100
|
+
* notifications: newNotifications // Merges with existing notifications array
|
|
101
|
+
* })
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export declare function merge<T extends UnPackedPageProps | DeferProp<UnPackedPageProps>>(value: T): MergeableProp<T>;
|
|
105
|
+
/**
|
|
106
|
+
* Creates a prop that should be deeply merged with existing props on the page.
|
|
107
|
+
*
|
|
108
|
+
* Unlike shallow merge, deep merge recursively merges nested objects and arrays,
|
|
109
|
+
* allowing for more granular updates to complex data structures.
|
|
110
|
+
*
|
|
111
|
+
* @param value - The value to be deeply merged with existing props
|
|
112
|
+
* @returns A mergeable prop object marked for deep merging behavior
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```javascript
|
|
116
|
+
* // Create a deep mergeable prop for nested user settings
|
|
117
|
+
* const updatedSettings = deepMerge({
|
|
118
|
+
* notifications: {
|
|
119
|
+
* email: true,
|
|
120
|
+
* push: false
|
|
121
|
+
* },
|
|
122
|
+
* privacy: {
|
|
123
|
+
* profile: 'public'
|
|
124
|
+
* }
|
|
125
|
+
* })
|
|
126
|
+
*
|
|
127
|
+
* // Use in page props - will deeply merge with existing settings
|
|
128
|
+
* return inertia.render('settings', {
|
|
129
|
+
* user: user,
|
|
130
|
+
* settings: updatedSettings // Deep merges with existing settings object
|
|
131
|
+
* })
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export declare function deepMerge<T extends UnPackedPageProps | DeferProp<UnPackedPageProps>>(value: T): MergeableProp<T>;
|
|
135
|
+
/**
|
|
136
|
+
* Type guard that checks if a prop value is a deferred prop.
|
|
137
|
+
*
|
|
138
|
+
* Deferred props contain the DEFERRED_PROP symbol and have compute/merge capabilities.
|
|
139
|
+
* This function is useful for runtime type checking and conditional prop handling.
|
|
140
|
+
*
|
|
141
|
+
* @param propValue - The object to check for deferred prop characteristics
|
|
142
|
+
* @returns True if the prop value is a deferred prop
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```js
|
|
146
|
+
* const prop = defer(() => ({ data: 'value' }))
|
|
147
|
+
*
|
|
148
|
+
* if (isDeferredProp(prop)) {
|
|
149
|
+
* // prop is now typed as DeferProp<T>
|
|
150
|
+
* const result = prop.compute()
|
|
151
|
+
* }
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
export declare function isDeferredProp<T extends UnPackedPageProps>(propValue: Object): propValue is DeferProp<T>;
|
|
155
|
+
/**
|
|
156
|
+
* Type guard that checks if a prop value is a mergeable prop.
|
|
157
|
+
*
|
|
158
|
+
* Mergeable props contain the TO_BE_MERGED symbol and should be merged with
|
|
159
|
+
* existing props rather than replaced during updates.
|
|
160
|
+
*
|
|
161
|
+
* @param propValue - The object to check for mergeable prop characteristics
|
|
162
|
+
* @returns True if the prop value is a mergeable prop
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```js
|
|
166
|
+
* const prop = merge({ items: [1, 2, 3] })
|
|
167
|
+
*
|
|
168
|
+
* if (isMergeableProp(prop)) {
|
|
169
|
+
* // prop is now typed as MergeableProp<T>
|
|
170
|
+
* const value = prop.value
|
|
171
|
+
* }
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
export declare function isMergeableProp<T extends UnPackedPageProps | DeferProp<UnPackedPageProps>>(propValue: Object): propValue is MergeableProp<T>;
|
|
175
|
+
/**
|
|
176
|
+
* Type guard that checks if a prop value is an always prop.
|
|
177
|
+
*
|
|
178
|
+
* Always props contain the ALWAYS_PROP symbol and are always included in
|
|
179
|
+
* responses, regardless of cherry-picking or selective prop requests.
|
|
180
|
+
*
|
|
181
|
+
* @param propValue - The object to check for always prop characteristics
|
|
182
|
+
* @returns True if the prop value is an always prop
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```js
|
|
186
|
+
* const prop = always({ userId: 123, permissions: ['read', 'write'] })
|
|
187
|
+
*
|
|
188
|
+
* if (isAlwaysProp(prop)) {
|
|
189
|
+
* // prop is now typed as AlwaysProp<T>
|
|
190
|
+
* const value = prop.value
|
|
191
|
+
* }
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
export declare function isAlwaysProp<T extends UnPackedPageProps>(propValue: Object): propValue is AlwaysProp<T>;
|
|
195
|
+
/**
|
|
196
|
+
* Type guard that checks if a prop value is an optional prop.
|
|
197
|
+
*
|
|
198
|
+
* Optional props contain the OPTIONAL_PROP symbol and are only included
|
|
199
|
+
* when explicitly requested by the client, never in standard visits.
|
|
200
|
+
*
|
|
201
|
+
* @param propValue - The object to check for optional prop characteristics
|
|
202
|
+
* @returns True if the prop value is an optional prop
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```js
|
|
206
|
+
* const prop = optional(() => ({ detailedData: 'expensive computation' }))
|
|
207
|
+
*
|
|
208
|
+
* if (isOptionalProp(prop)) {
|
|
209
|
+
* // prop is now typed as OptionalProp<T>
|
|
210
|
+
* const result = prop.compute()
|
|
211
|
+
* }
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
export declare function isOptionalProp<T extends UnPackedPageProps>(propValue: Object): propValue is OptionalProp<T>;
|
|
215
|
+
/**
|
|
216
|
+
* Builds props for standard (non-partial) Inertia visits.
|
|
217
|
+
*
|
|
218
|
+
* This function processes page props and categorizes them based on their type:
|
|
219
|
+
* - Deferred props: Skipped but communicated to client
|
|
220
|
+
* - Optional props: Skipped entirely
|
|
221
|
+
* - Always props: Always included
|
|
222
|
+
* - Mergeable props: Included and marked for merging
|
|
223
|
+
* - Regular props: Included normally
|
|
224
|
+
*
|
|
225
|
+
* @param pageProps - The page props to process
|
|
226
|
+
* @param containerResolver - Container resolver for dependency injection
|
|
227
|
+
* @returns Promise resolving to object containing processed props, deferred props list, and merge props list
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```js
|
|
231
|
+
* const result = await buildStandardVisitProps({
|
|
232
|
+
* user: { name: 'John' },
|
|
233
|
+
* posts: defer(() => getPosts()),
|
|
234
|
+
* settings: merge({ theme: 'dark' })
|
|
235
|
+
* })
|
|
236
|
+
* // Returns: { props: { user: {...} }, deferredProps: { default: ['posts'] }, mergeProps: ['settings'] }
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
export declare function buildStandardVisitProps(pageProps: PageProps, containerResolver: ContainerResolver<any>): Promise<{
|
|
240
|
+
props: ComponentProps;
|
|
241
|
+
mergeProps: string[];
|
|
242
|
+
deepMergeProps: string[];
|
|
243
|
+
deferredProps: {
|
|
244
|
+
[group: string]: string[];
|
|
245
|
+
};
|
|
246
|
+
}>;
|
|
247
|
+
/**
|
|
248
|
+
* Builds props for partial (cherry-picked) Inertia requests.
|
|
249
|
+
*
|
|
250
|
+
* This function processes page props for partial requests where only specific
|
|
251
|
+
* props are requested. It handles:
|
|
252
|
+
* - Always props: Always included regardless of cherry picking
|
|
253
|
+
* - Cherry-picked props: Only included if in the cherryPickProps list
|
|
254
|
+
* - Mergeable props: Included and marked for merging
|
|
255
|
+
* - Regular props: Included if cherry-picked
|
|
256
|
+
*
|
|
257
|
+
* @param pageProps - The page props to process
|
|
258
|
+
* @param cherryPickProps - Array of prop names to include
|
|
259
|
+
* @param containerResolver - Container resolver for dependency injection
|
|
260
|
+
* @returns Promise resolving to object containing processed props and merge props list
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```js
|
|
264
|
+
* const result = await buildPartialRequestProps(
|
|
265
|
+
* { user: { name: 'John' }, posts: defer(() => getPosts()), stats: optional(() => getStats()) },
|
|
266
|
+
* ['posts', 'stats']
|
|
267
|
+
* )
|
|
268
|
+
* // Returns: { props: { posts: [...], stats: [...] }, mergeProps: [], deferredProps: {} }
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
export declare function buildPartialRequestProps(pageProps: PageProps, cherryPickProps: string[], containerResolver: ContainerResolver<any>): Promise<{
|
|
272
|
+
props: ComponentProps;
|
|
273
|
+
mergeProps: string[];
|
|
274
|
+
deepMergeProps: string[];
|
|
275
|
+
deferredProps: {};
|
|
276
|
+
}>;
|