@adonisjs/inertia 1.2.1 → 2.0.0-beta.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.
@@ -0,0 +1,371 @@
1
+ // src/server_renderer.ts
2
+ import { pathToFileURL } from "node:url";
3
+ var ServerRenderer = class _ServerRenderer {
4
+ constructor(config, vite) {
5
+ this.config = config;
6
+ this.vite = vite;
7
+ }
8
+ static runtime;
9
+ /**
10
+ * Render the page on the server
11
+ *
12
+ * On development, we use the Vite Runtime API
13
+ * On production, we just import and use the SSR bundle generated by Vite
14
+ */
15
+ async render(pageObject) {
16
+ let render;
17
+ const devServer = this.vite?.getDevServer();
18
+ if (devServer) {
19
+ _ServerRenderer.runtime ??= await this.vite.createRuntime();
20
+ render = await _ServerRenderer.runtime.executeEntrypoint(this.config.ssr.entrypoint);
21
+ } else {
22
+ render = await import(pathToFileURL(this.config.ssr.bundle).href);
23
+ }
24
+ const result = await render.default(pageObject);
25
+ return { head: result.head, body: result.body };
26
+ }
27
+ };
28
+
29
+ // src/props.ts
30
+ var ignoreFirstLoadSymbol = Symbol("ignoreFirstLoad");
31
+ var MergeableProp = class {
32
+ shouldMerge = false;
33
+ merge() {
34
+ this.shouldMerge = true;
35
+ return this;
36
+ }
37
+ };
38
+ var OptionalProp = class {
39
+ constructor(callback) {
40
+ this.callback = callback;
41
+ }
42
+ [ignoreFirstLoadSymbol] = true;
43
+ };
44
+ var DeferProp = class extends MergeableProp {
45
+ constructor(callback, group) {
46
+ super();
47
+ this.callback = callback;
48
+ this.group = group;
49
+ }
50
+ [ignoreFirstLoadSymbol] = true;
51
+ getGroup() {
52
+ return this.group;
53
+ }
54
+ };
55
+ var MergeProp = class extends MergeableProp {
56
+ constructor(callback) {
57
+ super();
58
+ this.callback = callback;
59
+ this.shouldMerge = true;
60
+ }
61
+ };
62
+ var AlwaysProp = class extends MergeableProp {
63
+ constructor(callback) {
64
+ super();
65
+ this.callback = callback;
66
+ }
67
+ };
68
+
69
+ // src/headers.ts
70
+ var InertiaHeaders = {
71
+ Inertia: "x-inertia",
72
+ Reset: "x-inertia-reset",
73
+ Version: "x-inertia-version",
74
+ Location: "x-inertia-location",
75
+ PartialOnly: "x-inertia-partial-data",
76
+ PartialExcept: "x-inertia-partial-except",
77
+ PartialComponent: "x-inertia-partial-component"
78
+ };
79
+
80
+ // src/inertia.ts
81
+ var Inertia = class {
82
+ constructor(ctx, config, vite) {
83
+ this.ctx = ctx;
84
+ this.config = config;
85
+ this.vite = vite;
86
+ this.#sharedData = config.sharedData;
87
+ this.#serverRenderer = new ServerRenderer(config, vite);
88
+ this.#shouldClearHistory = false;
89
+ this.#shouldEncryptHistory = config.history.encrypt;
90
+ }
91
+ #sharedData = {};
92
+ #serverRenderer;
93
+ #shouldClearHistory = false;
94
+ #shouldEncryptHistory = false;
95
+ /**
96
+ * Check if the current request is a partial request
97
+ */
98
+ #isPartial(component) {
99
+ return this.ctx.request.header(InertiaHeaders.PartialComponent) === component;
100
+ }
101
+ /**
102
+ * Resolve the `only` partial request props.
103
+ * Only the props listed in the `x-inertia-partial-data` header
104
+ * will be returned
105
+ */
106
+ #resolveOnly(props) {
107
+ const partialOnlyHeader = this.ctx.request.header(InertiaHeaders.PartialOnly);
108
+ const only = partialOnlyHeader.split(",").filter(Boolean);
109
+ let newProps = {};
110
+ for (const key of only) newProps[key] = props[key];
111
+ return newProps;
112
+ }
113
+ /**
114
+ * Resolve the `except` partial request props.
115
+ * Remove the props listed in the `x-inertia-partial-except` header
116
+ */
117
+ #resolveExcept(props) {
118
+ const partialExceptHeader = this.ctx.request.header(InertiaHeaders.PartialExcept);
119
+ const except = partialExceptHeader.split(",").filter(Boolean);
120
+ for (const key of except) delete props[key];
121
+ return props;
122
+ }
123
+ /**
124
+ * Resolve the props for the current request
125
+ * by filtering out the props that are not needed
126
+ * based on the request headers
127
+ */
128
+ #pickPropsToResolve(component, props = {}) {
129
+ const isPartial = this.#isPartial(component);
130
+ let newProps = props;
131
+ if (!isPartial) {
132
+ newProps = Object.fromEntries(
133
+ Object.entries(props).filter(([_, value]) => !value[ignoreFirstLoadSymbol])
134
+ );
135
+ }
136
+ const partialOnlyHeader = this.ctx.request.header(InertiaHeaders.PartialOnly);
137
+ if (isPartial && partialOnlyHeader) newProps = this.#resolveOnly(props);
138
+ const partialExceptHeader = this.ctx.request.header(InertiaHeaders.PartialExcept);
139
+ if (isPartial && partialExceptHeader) newProps = this.#resolveExcept(newProps);
140
+ for (const [key, value] of Object.entries(props)) {
141
+ if (value instanceof AlwaysProp) newProps[key] = props[key];
142
+ }
143
+ return newProps;
144
+ }
145
+ /**
146
+ * Resolve a single prop by calling the callback or resolving the promise
147
+ */
148
+ async #resolvePageProps(props = {}) {
149
+ return Object.fromEntries(
150
+ await Promise.all(
151
+ Object.entries(props).map(async ([key, value]) => {
152
+ if (typeof value === "function") {
153
+ return [key, await value(this.ctx)];
154
+ }
155
+ if (value instanceof OptionalProp || value instanceof MergeProp || value instanceof DeferProp || value instanceof AlwaysProp) {
156
+ return [key, await value.callback()];
157
+ }
158
+ return [key, value];
159
+ })
160
+ )
161
+ );
162
+ }
163
+ /**
164
+ * Resolve the deferred props listing. Will be returned only
165
+ * on the first visit to the page and will be used to make
166
+ * subsequent partial requests
167
+ */
168
+ #resolveDeferredProps(component, pageProps) {
169
+ if (this.#isPartial(component)) return {};
170
+ const deferredProps = Object.entries(pageProps || {}).filter(([_, value]) => value instanceof DeferProp).map(([key, value]) => ({ key, group: value.getGroup() })).reduce(
171
+ (groups, { key, group }) => {
172
+ if (!groups[group]) groups[group] = [];
173
+ groups[group].push(key);
174
+ return groups;
175
+ },
176
+ {}
177
+ );
178
+ return Object.keys(deferredProps).length ? { deferredProps } : {};
179
+ }
180
+ /**
181
+ * Resolve the props that should be merged
182
+ */
183
+ #resolveMergeProps(pageProps) {
184
+ const inertiaResetHeader = this.ctx.request.header(InertiaHeaders.Reset) || "";
185
+ const resetProps = new Set(inertiaResetHeader.split(",").filter(Boolean));
186
+ const mergeProps = Object.entries(pageProps || {}).filter(([_, value]) => value instanceof MergeableProp && value.shouldMerge).map(([key]) => key).filter((key) => !resetProps.has(key));
187
+ return mergeProps.length ? { mergeProps } : {};
188
+ }
189
+ /**
190
+ * Build the page object that will be returned to the client
191
+ *
192
+ * See https://inertiajs.com/the-protocol#the-page-object
193
+ */
194
+ async #buildPageObject(component, pageProps) {
195
+ const propsToResolve = this.#pickPropsToResolve(component, {
196
+ ...this.#sharedData,
197
+ ...pageProps
198
+ });
199
+ return {
200
+ component,
201
+ url: this.ctx.request.url(true),
202
+ version: this.config.versionCache.getVersion(),
203
+ props: await this.#resolvePageProps(propsToResolve),
204
+ clearHistory: this.#shouldClearHistory,
205
+ encryptHistory: this.#shouldEncryptHistory,
206
+ ...this.#resolveMergeProps(pageProps),
207
+ ...this.#resolveDeferredProps(component, pageProps)
208
+ };
209
+ }
210
+ /**
211
+ * If the page should be rendered on the server or not
212
+ *
213
+ * The ssr.pages config can be a list of pages or a function that returns a boolean
214
+ */
215
+ async #shouldRenderOnServer(component) {
216
+ const isSsrEnabled = this.config.ssr.enabled;
217
+ if (!isSsrEnabled) return false;
218
+ let isSsrEnabledForPage = false;
219
+ if (typeof this.config.ssr.pages === "function") {
220
+ isSsrEnabledForPage = await this.config.ssr.pages(this.ctx, component);
221
+ } else if (this.config.ssr.pages) {
222
+ isSsrEnabledForPage = this.config.ssr.pages?.includes(component);
223
+ } else {
224
+ isSsrEnabledForPage = true;
225
+ }
226
+ return isSsrEnabledForPage;
227
+ }
228
+ /**
229
+ * Resolve the root view
230
+ */
231
+ #resolveRootView() {
232
+ return typeof this.config.rootView === "function" ? this.config.rootView(this.ctx) : this.config.rootView;
233
+ }
234
+ /**
235
+ * Render the page on the server
236
+ */
237
+ async #renderOnServer(pageObject, viewProps) {
238
+ const { head, body } = await this.#serverRenderer.render(pageObject);
239
+ return this.ctx.view.render(this.#resolveRootView(), {
240
+ ...viewProps,
241
+ page: { ssrHead: head, ssrBody: body, ...pageObject }
242
+ });
243
+ }
244
+ /**
245
+ * Share data for the current request.
246
+ * This data will override any shared data defined in the config.
247
+ */
248
+ share(data) {
249
+ this.#sharedData = { ...this.#sharedData, ...data };
250
+ }
251
+ /**
252
+ * Render a page using Inertia
253
+ */
254
+ async render(component, pageProps, viewProps) {
255
+ const pageObject = await this.#buildPageObject(component, pageProps);
256
+ const isInertiaRequest = !!this.ctx.request.header(InertiaHeaders.Inertia);
257
+ if (!isInertiaRequest) {
258
+ const shouldRenderOnServer = await this.#shouldRenderOnServer(component);
259
+ if (shouldRenderOnServer) return this.#renderOnServer(pageObject, viewProps);
260
+ return this.ctx.view.render(this.#resolveRootView(), { ...viewProps, page: pageObject });
261
+ }
262
+ this.ctx.response.header(InertiaHeaders.Inertia, "true");
263
+ return pageObject;
264
+ }
265
+ /**
266
+ * Clear history state.
267
+ *
268
+ * See https://v2.inertiajs.com/history-encryption#clearing-history
269
+ */
270
+ clearHistory() {
271
+ this.#shouldClearHistory = true;
272
+ }
273
+ /**
274
+ * Encrypt history
275
+ *
276
+ * See https://v2.inertiajs.com/history-encryption
277
+ */
278
+ encryptHistory(encrypt = true) {
279
+ this.#shouldEncryptHistory = encrypt;
280
+ }
281
+ /**
282
+ * Create a lazy prop
283
+ *
284
+ * Lazy props are never resolved on first visit, but only when the client
285
+ * request a partial reload explicitely with this value.
286
+ *
287
+ * See https://inertiajs.com/partial-reloads#lazy-data-evaluation
288
+ *
289
+ * @deprecated use `optional` instead
290
+ */
291
+ lazy(callback) {
292
+ return new OptionalProp(callback);
293
+ }
294
+ /**
295
+ * Create an optional prop
296
+ *
297
+ * See https://inertiajs.com/partial-reloads#lazy-data-evaluation
298
+ */
299
+ optional(callback) {
300
+ return new OptionalProp(callback);
301
+ }
302
+ /**
303
+ * Create a mergeable prop
304
+ *
305
+ * See https://v2.inertiajs.com/merging-props
306
+ */
307
+ merge(callback) {
308
+ return new MergeProp(callback);
309
+ }
310
+ /**
311
+ * Create an always prop
312
+ *
313
+ * Always props are resolved on every request, no matter if it's a partial
314
+ * request or not.
315
+ *
316
+ * See https://inertiajs.com/partial-reloads#lazy-data-evaluation
317
+ */
318
+ always(callback) {
319
+ return new AlwaysProp(callback);
320
+ }
321
+ /**
322
+ * Create a deferred prop
323
+ *
324
+ * Deferred props feature allows you to defer the loading of certain
325
+ * page data until after the initial page render.
326
+ *
327
+ * See https://v2.inertiajs.com/deferred-props
328
+ */
329
+ defer(callback, group = "default") {
330
+ return new DeferProp(callback, group);
331
+ }
332
+ /**
333
+ * This method can be used to redirect the user to an external website
334
+ * or even a non-inertia route of your application.
335
+ *
336
+ * See https://inertiajs.com/redirects#external-redirects
337
+ */
338
+ async location(url) {
339
+ this.ctx.response.header(InertiaHeaders.Location, url);
340
+ this.ctx.response.status(409);
341
+ }
342
+ };
343
+
344
+ // src/inertia_middleware.ts
345
+ var InertiaMiddleware = class {
346
+ constructor(config, vite) {
347
+ this.config = config;
348
+ this.vite = vite;
349
+ }
350
+ async handle(ctx, next) {
351
+ const { response, request } = ctx;
352
+ ctx.inertia = new Inertia(ctx, this.config, this.vite);
353
+ await next();
354
+ const isInertiaRequest = !!request.header("x-inertia");
355
+ if (!isInertiaRequest) return;
356
+ response.header("Vary", "Accept");
357
+ const method = request.method();
358
+ if (response.getStatus() === 302 && ["PUT", "PATCH", "DELETE"].includes(method)) {
359
+ response.status(303);
360
+ }
361
+ const version = this.config.versionCache.getVersion().toString();
362
+ if (method === "GET" && request.header("x-inertia-version", "") !== version) {
363
+ response.header("x-inertia-location", request.url());
364
+ response.status(409);
365
+ }
366
+ }
367
+ };
368
+
369
+ export {
370
+ InertiaMiddleware
371
+ };
package/build/index.d.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  import Configure from '@adonisjs/core/commands/configure';
2
2
  import { ConfigProvider } from '@adonisjs/core/types';
3
- import { S as SharedData, I as InertiaConfig, R as ResolvedConfig } from './types-fb05P61I.js';
3
+ import { S as SharedData, I as InertiaConfig, R as ResolvedConfig } from './types-AyvioK68.js';
4
4
  import '@adonisjs/core/http';
5
5
  import '@tuyau/utils/types';
6
- import '@adonisjs/vite';
7
6
 
8
7
  /**
9
8
  * Configures the package
package/build/index.js CHANGED
@@ -290,6 +290,7 @@ function defineConfig(config) {
290
290
  versionCache,
291
291
  rootView: config.rootView ?? "inertia_layout",
292
292
  sharedData: config.sharedData || {},
293
+ history: { encrypt: config.history?.encrypt ?? false },
293
294
  entrypoint: slash(
294
295
  config.entrypoint ?? await detector.detectEntrypoint("inertia/app/app.ts")
295
296
  ),
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  InertiaMiddleware
3
- } from "../chunk-TSAZ2E7H.js";
3
+ } from "../chunk-HUEPL6EC.js";
4
4
 
5
5
  // providers/inertia_provider.ts
6
6
  import { configProvider } from "@adonisjs/core";
@@ -1,10 +1,90 @@
1
1
  import { Vite } from '@adonisjs/vite';
2
2
  import { HttpContext } from '@adonisjs/core/http';
3
3
  import { NextFn } from '@adonisjs/core/types/http';
4
- import { a as Inertia, R as ResolvedConfig } from '../types-fb05P61I.js';
4
+ import { R as ResolvedConfig, D as Data, P as PageObject, M as MaybePromise, O as OptionalProp, a as MergeProp, A as AlwaysProp, b as DeferProp } from '../types-AyvioK68.js';
5
5
  import '@adonisjs/core/types';
6
6
  import '@tuyau/utils/types';
7
7
 
8
+ /**
9
+ * Main class used to interact with Inertia
10
+ */
11
+ declare class Inertia {
12
+ #private;
13
+ protected ctx: HttpContext;
14
+ protected config: ResolvedConfig;
15
+ protected vite?: Vite | undefined;
16
+ constructor(ctx: HttpContext, config: ResolvedConfig, vite?: Vite | undefined);
17
+ /**
18
+ * Share data for the current request.
19
+ * This data will override any shared data defined in the config.
20
+ */
21
+ share(data: Record<string, Data>): void;
22
+ /**
23
+ * Render a page using Inertia
24
+ */
25
+ render<TPageProps extends Record<string, any> = {}, TViewProps extends Record<string, any> = {}>(component: string, pageProps?: TPageProps, viewProps?: TViewProps): Promise<string | PageObject<TPageProps>>;
26
+ /**
27
+ * Clear history state.
28
+ *
29
+ * See https://v2.inertiajs.com/history-encryption#clearing-history
30
+ */
31
+ clearHistory(): void;
32
+ /**
33
+ * Encrypt history
34
+ *
35
+ * See https://v2.inertiajs.com/history-encryption
36
+ */
37
+ encryptHistory(encrypt?: boolean): void;
38
+ /**
39
+ * Create a lazy prop
40
+ *
41
+ * Lazy props are never resolved on first visit, but only when the client
42
+ * request a partial reload explicitely with this value.
43
+ *
44
+ * See https://inertiajs.com/partial-reloads#lazy-data-evaluation
45
+ *
46
+ * @deprecated use `optional` instead
47
+ */
48
+ lazy<T>(callback: () => MaybePromise<T>): OptionalProp<() => MaybePromise<T>>;
49
+ /**
50
+ * Create an optional prop
51
+ *
52
+ * See https://inertiajs.com/partial-reloads#lazy-data-evaluation
53
+ */
54
+ optional<T>(callback: () => MaybePromise<T>): OptionalProp<() => MaybePromise<T>>;
55
+ /**
56
+ * Create a mergeable prop
57
+ *
58
+ * See https://v2.inertiajs.com/merging-props
59
+ */
60
+ merge<T>(callback: () => MaybePromise<T>): MergeProp<() => MaybePromise<T>>;
61
+ /**
62
+ * Create an always prop
63
+ *
64
+ * Always props are resolved on every request, no matter if it's a partial
65
+ * request or not.
66
+ *
67
+ * See https://inertiajs.com/partial-reloads#lazy-data-evaluation
68
+ */
69
+ always<T>(callback: () => MaybePromise<T>): AlwaysProp<() => MaybePromise<T>>;
70
+ /**
71
+ * Create a deferred prop
72
+ *
73
+ * Deferred props feature allows you to defer the loading of certain
74
+ * page data until after the initial page render.
75
+ *
76
+ * See https://v2.inertiajs.com/deferred-props
77
+ */
78
+ defer<T>(callback: () => MaybePromise<T>, group?: string): DeferProp<() => MaybePromise<T>>;
79
+ /**
80
+ * This method can be used to redirect the user to an external website
81
+ * or even a non-inertia route of your application.
82
+ *
83
+ * See https://inertiajs.com/redirects#external-redirects
84
+ */
85
+ location(url: string): Promise<void>;
86
+ }
87
+
8
88
  /**
9
89
  * HttpContext augmentations
10
90
  */
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  InertiaMiddleware
3
- } from "../chunk-TSAZ2E7H.js";
3
+ } from "../chunk-HUEPL6EC.js";
4
4
  export {
5
5
  InertiaMiddleware as default
6
6
  };
@@ -1,9 +1,8 @@
1
1
  import { PluginFn } from '@japa/runner/types';
2
2
  import { ApplicationService } from '@adonisjs/core/types';
3
- import { P as PageProps } from '../../../types-fb05P61I.js';
3
+ import { c as PageProps } from '../../../types-AyvioK68.js';
4
4
  import '@adonisjs/core/http';
5
5
  import '@tuyau/utils/types';
6
- import '@adonisjs/vite';
7
6
 
8
7
  declare module '@japa/api-client' {
9
8
  interface ApiRequest {
@@ -1,5 +1,4 @@
1
1
  import '@adonisjs/core/types';
2
2
  import '@adonisjs/core/http';
3
3
  import '@tuyau/utils/types';
4
- export { A as AssetsVersion, D as Data, I as InertiaConfig, f as InferPageProps, d as InferSharedProps, M as MaybePromise, c as PageObject, P as PageProps, g as RenderInertiaSsrApp, R as ResolvedConfig, S as SharedData, b as SharedDatumFactory, e as SharedProps } from '../types-fb05P61I.js';
5
- import '@adonisjs/vite';
4
+ export { e as AssetsVersion, D as Data, I as InertiaConfig, h as InferPageProps, f as InferSharedProps, M as MaybePromise, P as PageObject, c as PageProps, i as RenderInertiaSsrApp, R as ResolvedConfig, S as SharedData, d as SharedDatumFactory, g as SharedProps } from '../types-AyvioK68.js';
@@ -1,49 +1,6 @@
1
1
  import { ConfigProvider } from '@adonisjs/core/types';
2
2
  import { HttpContext } from '@adonisjs/core/http';
3
3
  import { Simplify, Serialize } from '@tuyau/utils/types';
4
- import { Vite } from '@adonisjs/vite';
5
-
6
- /**
7
- * Symbol used to identify lazy props
8
- */
9
- declare const kLazySymbol: unique symbol;
10
- /**
11
- * Main class used to interact with Inertia
12
- */
13
- declare class Inertia {
14
- #private;
15
- protected ctx: HttpContext;
16
- protected config: ResolvedConfig;
17
- protected vite?: Vite | undefined;
18
- constructor(ctx: HttpContext, config: ResolvedConfig, vite?: Vite | undefined);
19
- /**
20
- * Share data for the current request.
21
- * This data will override any shared data defined in the config.
22
- */
23
- share(data: Record<string, Data>): void;
24
- /**
25
- * Render a page using Inertia
26
- */
27
- render<TPageProps extends Record<string, any> = {}, TViewProps extends Record<string, any> = {}>(component: string, pageProps?: TPageProps, viewProps?: TViewProps): Promise<string | PageObject<TPageProps>>;
28
- /**
29
- * Create a lazy prop
30
- *
31
- * Lazy props are never resolved on first visit, but only when the client
32
- * request a partial reload explicitely with this value.
33
- *
34
- * See https://inertiajs.com/partial-reloads#lazy-data-evaluation
35
- */
36
- lazy<T>(callback: () => MaybePromise<T>): {
37
- [kLazySymbol]: () => MaybePromise<T>;
38
- };
39
- /**
40
- * This method can be used to redirect the user to an external website
41
- * or even a non-inertia route of your application.
42
- *
43
- * See https://inertiajs.com/redirects#external-redirects
44
- */
45
- location(url: string): Promise<void>;
46
- }
47
4
 
48
5
  /**
49
6
  * VersionCache is used to cache the version of the assets.
@@ -71,6 +28,47 @@ declare class VersionCache {
71
28
  setVersion(version: AssetsVersion): Promise<void>;
72
29
  }
73
30
 
31
+ declare const ignoreFirstLoadSymbol: unique symbol;
32
+ /**
33
+ * Base class for Mergeable props
34
+ */
35
+ declare abstract class MergeableProp {
36
+ shouldMerge: boolean;
37
+ merge(): this;
38
+ }
39
+ /**
40
+ * Optional prop
41
+ */
42
+ declare class OptionalProp<T extends MaybePromise<any>> {
43
+ callback: T;
44
+ [ignoreFirstLoadSymbol]: boolean;
45
+ constructor(callback: T);
46
+ }
47
+ /**
48
+ * Defer prop
49
+ */
50
+ declare class DeferProp<T extends MaybePromise<any>> extends MergeableProp {
51
+ callback: T;
52
+ private group;
53
+ [ignoreFirstLoadSymbol]: true;
54
+ constructor(callback: T, group: string);
55
+ getGroup(): string;
56
+ }
57
+ /**
58
+ * Merge prop
59
+ */
60
+ declare class MergeProp<T extends MaybePromise<any>> extends MergeableProp {
61
+ callback: T;
62
+ constructor(callback: T);
63
+ }
64
+ /**
65
+ * Always prop
66
+ */
67
+ declare class AlwaysProp<T extends MaybePromise<any>> extends MergeableProp {
68
+ callback: T;
69
+ constructor(callback: T);
70
+ }
71
+
74
72
  type MaybePromise<T> = T | Promise<T>;
75
73
  /**
76
74
  * Props that will be passed to inertia render method
@@ -105,6 +103,14 @@ interface InertiaConfig<T extends SharedData = SharedData> {
105
103
  * Data that should be shared with all rendered pages
106
104
  */
107
105
  sharedData?: T;
106
+ /**
107
+ * History encryption
108
+ *
109
+ * See https://v2.inertiajs.com/history-encryption
110
+ */
111
+ history?: {
112
+ encrypt?: boolean;
113
+ };
108
114
  /**
109
115
  * Options to configure SSR
110
116
  */
@@ -134,6 +140,9 @@ interface ResolvedConfig<T extends SharedData = SharedData> {
134
140
  rootView: string | ((ctx: HttpContext) => string);
135
141
  versionCache: VersionCache;
136
142
  sharedData: T;
143
+ history: {
144
+ encrypt: boolean;
145
+ };
137
146
  ssr: {
138
147
  enabled: boolean;
139
148
  entrypoint: string;
@@ -142,22 +151,50 @@ interface ResolvedConfig<T extends SharedData = SharedData> {
142
151
  };
143
152
  }
144
153
  interface PageObject<TPageProps extends PageProps = PageProps> {
154
+ ssrHead?: string;
155
+ ssrBody?: string;
156
+ /**
157
+ * The name of the JavaScript page component.
158
+ */
145
159
  component: string;
160
+ /**
161
+ * The current asset version.
162
+ */
146
163
  version: string | number;
164
+ /**
165
+ * The page props (data).
166
+ */
147
167
  props: TPageProps;
168
+ /**
169
+ * The page URL.
170
+ */
148
171
  url: string;
149
- ssrHead?: string;
150
- ssrBody?: string;
172
+ /**
173
+ * List of deferred props that will be loaded with subsequent requests
174
+ */
175
+ deferredProps?: Record<string, string[]>;
176
+ /**
177
+ * List of mergeable props that will be merged with subsequent requests
178
+ */
179
+ mergeProps?: string[];
180
+ /**
181
+ * Whether or not to encrypt the current page's history state.
182
+ */
183
+ encryptHistory?: boolean;
184
+ /**
185
+ * Whether or not to clear any encrypted history state.
186
+ */
187
+ clearHistory?: boolean;
151
188
  }
152
- type IsLazyProp<T> = T extends {
153
- [kLazySymbol]: () => MaybePromise<any>;
154
- } ? true : false;
189
+ type IsOptionalProp<T> = T extends OptionalProp<any> ? true : T extends DeferProp<any> ? true : false;
155
190
  type InferProps<T> = {
156
- [K in keyof T as IsLazyProp<T[K]> extends true ? K : never]+?: T[K] extends {
157
- [kLazySymbol]: () => MaybePromise<infer U>;
191
+ [K in keyof T as IsOptionalProp<T[K]> extends true ? K : never]+?: T[K] extends {
192
+ callback: () => MaybePromise<infer U>;
158
193
  } ? U : T[K];
159
194
  } & {
160
- [K in keyof T as IsLazyProp<T[K]> extends true ? never : K]: T[K];
195
+ [K in keyof T as IsOptionalProp<T[K]> extends true ? never : K]: T[K] extends {
196
+ callback: () => MaybePromise<infer U>;
197
+ } ? U : T[K];
161
198
  };
162
199
  type ReturnsTypesSharedData<T extends SharedData> = {} extends T ? {} : {
163
200
  [K in keyof T]: T[K] extends (...args: any[]) => MaybePromise<infer U> ? U : T[K];
@@ -191,7 +228,7 @@ interface SharedProps {
191
228
  * }
192
229
  * ```
193
230
  */
194
- type InferPageProps<Controller, Method extends keyof Controller> = Controller[Method] extends (...args: any[]) => any ? Simplify<Serialize<InferProps<Exclude<Awaited<ReturnType<Controller[Method]>>, string>['props']> & SharedProps>> : never;
231
+ type InferPageProps<Controller, Method extends keyof Controller> = Controller[Method] extends (...args: any[]) => any ? Simplify<Serialize<InferProps<Extract<Awaited<ReturnType<Controller[Method]>>, PageObject>['props']> & SharedProps>> : never;
195
232
  /**
196
233
  * Signature for the method in the SSR entrypoint file
197
234
  */
@@ -200,4 +237,4 @@ type RenderInertiaSsrApp = (page: PageObject) => Promise<{
200
237
  body: string;
201
238
  }>;
202
239
 
203
- export { type AssetsVersion as A, type Data as D, type InertiaConfig as I, type MaybePromise as M, type PageProps as P, type ResolvedConfig as R, type SharedData as S, Inertia as a, type SharedDatumFactory as b, type PageObject as c, type InferSharedProps as d, type SharedProps as e, type InferPageProps as f, type RenderInertiaSsrApp as g };
240
+ export { AlwaysProp as A, type Data as D, type InertiaConfig as I, type MaybePromise as M, OptionalProp as O, type PageObject as P, type ResolvedConfig as R, type SharedData as S, MergeProp as a, DeferProp as b, type PageProps as c, type SharedDatumFactory as d, type AssetsVersion as e, type InferSharedProps as f, type SharedProps as g, type InferPageProps as h, type RenderInertiaSsrApp as i };
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": "1.2.1",
4
+ "version": "2.0.0-beta.0",
5
5
  "engines": {
6
6
  "node": ">=20.6.0"
7
7
  },
@@ -21,29 +21,13 @@
21
21
  "./client": "./build/src/plugins/vite.js",
22
22
  "./helpers": "./build/src/helpers.js"
23
23
  },
24
- "scripts": {
25
- "clean": "del-cli build",
26
- "copy:templates": "copyfiles --up 1 \"stubs/**/*.stub\" build",
27
- "typecheck": "tsc --noEmit",
28
- "lint": "eslint . --ext=.ts",
29
- "format": "prettier --write .",
30
- "quick:test": "node --enable-source-maps --loader=ts-node/esm bin/test.ts",
31
- "pretest": "npm run lint",
32
- "test": "c8 npm run quick:test",
33
- "prebuild": "npm run lint && npm run clean",
34
- "build": "tsup-node",
35
- "postbuild": "npm run copy:templates",
36
- "release": "release-it",
37
- "version": "npm run build",
38
- "prepublishOnly": "npm run build"
39
- },
40
24
  "devDependencies": {
41
- "@adonisjs/assembler": "^7.7.0",
42
- "@adonisjs/core": "6.9.1",
43
- "@adonisjs/eslint-config": "^1.3.0",
44
- "@adonisjs/prettier-config": "^1.3.0",
45
- "@adonisjs/session": "7.4.0",
46
- "@adonisjs/tsconfig": "^1.3.0",
25
+ "@adonisjs/assembler": "^7.8.2",
26
+ "@adonisjs/core": "6.14.0",
27
+ "@adonisjs/eslint-config": "^2.0.0-beta.7",
28
+ "@adonisjs/prettier-config": "^1.4.0",
29
+ "@adonisjs/session": "7.5.0",
30
+ "@adonisjs/tsconfig": "^1.4.0",
47
31
  "@adonisjs/vite": "^3.0.0",
48
32
  "@japa/api-client": "^2.0.3",
49
33
  "@japa/assert": "3.0.0",
@@ -51,35 +35,36 @@
51
35
  "@japa/file-system": "^2.3.0",
52
36
  "@japa/plugin-adonisjs": "^3.0.1",
53
37
  "@japa/runner": "3.1.4",
54
- "@japa/snapshot": "^2.0.5",
55
- "@swc/core": "^1.5.24",
56
- "@types/node": "^20.13.0",
57
- "@types/qs": "^6.9.15",
38
+ "@japa/snapshot": "^2.0.6",
39
+ "@release-it/conventional-changelog": "^8.0.2",
40
+ "@swc/core": "^1.7.26",
41
+ "@types/node": "^22.7.4",
42
+ "@types/qs": "^6.9.16",
58
43
  "@types/supertest": "^6.0.2",
59
- "@vavite/multibuild": "^4.1.1",
60
- "c8": "^9.1.0",
44
+ "@vavite/multibuild": "^4.1.3",
45
+ "c8": "^10.1.2",
61
46
  "copyfiles": "^2.4.1",
62
47
  "del-cli": "^5.1.0",
63
- "edge-parser": "^9.0.2",
64
- "edge.js": "^6.0.2",
65
- "eslint": "^8.57.0",
48
+ "edge-parser": "^9.0.3",
49
+ "edge.js": "^6.2.0",
50
+ "eslint": "^9.11.1",
66
51
  "get-port": "^7.1.0",
67
- "prettier": "^3.3.0",
68
- "release-it": "^17.3.0",
52
+ "prettier": "^3.3.3",
53
+ "release-it": "^17.6.0",
69
54
  "supertest": "^7.0.0",
70
- "ts-node": "^10.9.2",
71
- "tsup": "^8.0.2",
72
- "typescript": "~5.4.5",
73
- "vite": "^5.2.12"
55
+ "ts-node-maintained": "^10.9.4",
56
+ "tsup": "^8.3.0",
57
+ "typescript": "~5.6.2",
58
+ "vite": "^5.4.8"
74
59
  },
75
60
  "dependencies": {
76
- "@poppinss/utils": "^6.7.3",
61
+ "@poppinss/utils": "^6.8.3",
77
62
  "@tuyau/utils": "^0.0.4",
78
63
  "crc-32": "^1.2.2",
79
64
  "edge-error": "^4.0.1",
80
65
  "html-entities": "^2.5.2",
81
66
  "locate-path": "^7.2.0",
82
- "qs": "^6.12.1"
67
+ "qs": "^6.13.0"
83
68
  },
84
69
  "peerDependencies": {
85
70
  "@adonisjs/core": "^6.9.1",
@@ -93,30 +78,39 @@
93
78
  "optional": true
94
79
  }
95
80
  },
81
+ "publishConfig": {
82
+ "access": "public",
83
+ "tag": "beta"
84
+ },
96
85
  "author": "Julien Ripouteau <julien@ripouteau.com>,adonisjs",
97
86
  "license": "MIT",
98
87
  "keywords": [
99
88
  "inertia",
100
89
  "adonisjs"
101
90
  ],
102
- "eslintConfig": {
103
- "extends": "@adonisjs/eslint-config/package"
104
- },
105
91
  "prettier": "@adonisjs/prettier-config",
106
- "publishConfig": {
107
- "access": "public",
108
- "tag": "latest"
109
- },
110
92
  "release-it": {
111
93
  "git": {
94
+ "requireCleanWorkingDir": true,
95
+ "requireUpstream": true,
112
96
  "commitMessage": "chore(release): ${version}",
113
97
  "tagAnnotation": "v${version}",
98
+ "push": true,
114
99
  "tagName": "v${version}"
115
100
  },
116
101
  "github": {
117
- "release": true,
118
- "releaseName": "v${version}",
119
- "web": true
102
+ "release": true
103
+ },
104
+ "npm": {
105
+ "publish": true,
106
+ "skipChecks": true
107
+ },
108
+ "plugins": {
109
+ "@release-it/conventional-changelog": {
110
+ "preset": {
111
+ "name": "angular"
112
+ }
113
+ }
120
114
  }
121
115
  },
122
116
  "c8": {
@@ -146,5 +140,20 @@
146
140
  "format": "esm",
147
141
  "dts": true,
148
142
  "target": "esnext"
143
+ },
144
+ "scripts": {
145
+ "clean": "del-cli build",
146
+ "copy:templates": "copyfiles --up 1 \"stubs/**/*.stub\" build",
147
+ "typecheck": "tsc --noEmit",
148
+ "lint": "eslint",
149
+ "format": "prettier --write .",
150
+ "quick:test": "node --enable-source-maps --import=ts-node-maintained/register/esm bin/test.ts",
151
+ "pretest": "npm run lint",
152
+ "test": "c8 npm run quick:test",
153
+ "prebuild": "npm run lint && npm run clean",
154
+ "build": "tsup-node",
155
+ "postbuild": "npm run copy:templates",
156
+ "release": "release-it",
157
+ "version": "npm run build"
149
158
  }
150
- }
159
+ }
@@ -1,198 +0,0 @@
1
- // src/server_renderer.ts
2
- import { pathToFileURL } from "node:url";
3
- var ServerRenderer = class {
4
- constructor(config, vite) {
5
- this.config = config;
6
- this.vite = vite;
7
- }
8
- /**
9
- * Render the page on the server
10
- *
11
- * On development, we use the Vite Runtime API
12
- * On production, we just import and use the SSR bundle generated by Vite
13
- */
14
- async render(pageObject) {
15
- let render;
16
- const devServer = this.vite?.getDevServer();
17
- if (devServer) {
18
- const runtime = await this.vite.createRuntime();
19
- render = await runtime.executeEntrypoint(this.config.ssr.entrypoint);
20
- } else {
21
- render = await import(pathToFileURL(this.config.ssr.bundle).href);
22
- }
23
- const result = await render.default(pageObject);
24
- return { head: result.head, body: result.body };
25
- }
26
- };
27
-
28
- // src/inertia.ts
29
- var kLazySymbol = Symbol("lazy");
30
- var Inertia = class {
31
- constructor(ctx, config, vite) {
32
- this.ctx = ctx;
33
- this.config = config;
34
- this.vite = vite;
35
- this.#sharedData = config.sharedData;
36
- this.#serverRenderer = new ServerRenderer(config, vite);
37
- }
38
- #sharedData = {};
39
- #serverRenderer;
40
- /**
41
- * Check if a value is a lazy prop
42
- */
43
- #isLazyProps(value) {
44
- return typeof value === "object" && value && kLazySymbol in value;
45
- }
46
- /**
47
- * Pick props to resolve based on x-inertia-partial-data header
48
- *
49
- * If header is not present, resolve all props except lazy props
50
- * If header is present, resolve only the props that are listed in the header
51
- */
52
- #pickPropsToResolve(component, props) {
53
- const partialData = this.ctx.request.header("x-inertia-partial-data")?.split(",").filter(Boolean);
54
- const partialComponent = this.ctx.request.header("x-inertia-partial-component");
55
- let entriesToResolve = Object.entries(props);
56
- if (partialData && partialComponent === component) {
57
- entriesToResolve = entriesToResolve.filter(([key]) => partialData.includes(key));
58
- } else {
59
- entriesToResolve = entriesToResolve.filter(([key]) => !this.#isLazyProps(props[key]));
60
- }
61
- return entriesToResolve;
62
- }
63
- /**
64
- * Resolve the props that will be sent to the client
65
- */
66
- async #resolvePageProps(component, props) {
67
- const entriesToResolve = this.#pickPropsToResolve(component, props);
68
- const entries = entriesToResolve.map(async ([key, value]) => {
69
- if (typeof value === "function") {
70
- return [key, await value(this.ctx)];
71
- }
72
- if (this.#isLazyProps(value)) {
73
- const lazyValue = value[kLazySymbol];
74
- return [key, await lazyValue()];
75
- }
76
- return [key, value];
77
- });
78
- return Object.fromEntries(await Promise.all(entries));
79
- }
80
- /**
81
- * Build the page object that will be returned to the client
82
- *
83
- * See https://inertiajs.com/the-protocol#the-page-object
84
- */
85
- async #buildPageObject(component, pageProps) {
86
- return {
87
- component,
88
- version: this.config.versionCache.getVersion(),
89
- props: await this.#resolvePageProps(component, { ...this.#sharedData, ...pageProps }),
90
- url: this.ctx.request.url(true)
91
- };
92
- }
93
- /**
94
- * If the page should be rendered on the server or not
95
- *
96
- * The ssr.pages config can be a list of pages or a function that returns a boolean
97
- */
98
- async #shouldRenderOnServer(component) {
99
- const isSsrEnabled = this.config.ssr.enabled;
100
- if (!isSsrEnabled) return false;
101
- let isSsrEnabledForPage = false;
102
- if (typeof this.config.ssr.pages === "function") {
103
- isSsrEnabledForPage = await this.config.ssr.pages(this.ctx, component);
104
- } else if (this.config.ssr.pages) {
105
- isSsrEnabledForPage = this.config.ssr.pages?.includes(component);
106
- } else {
107
- isSsrEnabledForPage = true;
108
- }
109
- return isSsrEnabledForPage;
110
- }
111
- /**
112
- * Resolve the root view
113
- */
114
- #resolveRootView() {
115
- return typeof this.config.rootView === "function" ? this.config.rootView(this.ctx) : this.config.rootView;
116
- }
117
- /**
118
- * Render the page on the server
119
- */
120
- async #renderOnServer(pageObject, viewProps) {
121
- const { head, body } = await this.#serverRenderer.render(pageObject);
122
- return this.ctx.view.render(this.#resolveRootView(), {
123
- ...viewProps,
124
- page: { ssrHead: head, ssrBody: body, ...pageObject }
125
- });
126
- }
127
- /**
128
- * Share data for the current request.
129
- * This data will override any shared data defined in the config.
130
- */
131
- share(data) {
132
- this.#sharedData = { ...this.#sharedData, ...data };
133
- }
134
- /**
135
- * Render a page using Inertia
136
- */
137
- async render(component, pageProps, viewProps) {
138
- const pageObject = await this.#buildPageObject(component, pageProps);
139
- const isInertiaRequest = !!this.ctx.request.header("x-inertia");
140
- if (!isInertiaRequest) {
141
- const shouldRenderOnServer = await this.#shouldRenderOnServer(component);
142
- if (shouldRenderOnServer) return this.#renderOnServer(pageObject, viewProps);
143
- return this.ctx.view.render(this.#resolveRootView(), { ...viewProps, page: pageObject });
144
- }
145
- this.ctx.response.header("x-inertia", "true");
146
- return pageObject;
147
- }
148
- /**
149
- * Create a lazy prop
150
- *
151
- * Lazy props are never resolved on first visit, but only when the client
152
- * request a partial reload explicitely with this value.
153
- *
154
- * See https://inertiajs.com/partial-reloads#lazy-data-evaluation
155
- */
156
- lazy(callback) {
157
- return { [kLazySymbol]: callback };
158
- }
159
- /**
160
- * This method can be used to redirect the user to an external website
161
- * or even a non-inertia route of your application.
162
- *
163
- * See https://inertiajs.com/redirects#external-redirects
164
- */
165
- async location(url) {
166
- this.ctx.response.header("X-Inertia-Location", url);
167
- this.ctx.response.status(409);
168
- }
169
- };
170
-
171
- // src/inertia_middleware.ts
172
- var InertiaMiddleware = class {
173
- constructor(config, vite) {
174
- this.config = config;
175
- this.vite = vite;
176
- }
177
- async handle(ctx, next) {
178
- const { response, request } = ctx;
179
- ctx.inertia = new Inertia(ctx, this.config, this.vite);
180
- await next();
181
- const isInertiaRequest = !!request.header("x-inertia");
182
- if (!isInertiaRequest) return;
183
- response.header("Vary", "Accept");
184
- const method = request.method();
185
- if (response.getStatus() === 302 && ["PUT", "PATCH", "DELETE"].includes(method)) {
186
- response.status(303);
187
- }
188
- const version = this.config.versionCache.getVersion().toString();
189
- if (method === "GET" && request.header("x-inertia-version", "") !== version) {
190
- response.header("x-inertia-location", request.url());
191
- response.status(409);
192
- }
193
- }
194
- };
195
-
196
- export {
197
- InertiaMiddleware
198
- };