@adonisjs/inertia 1.2.2 → 2.0.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,374 @@
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]) => {
134
+ if (value && value[ignoreFirstLoadSymbol]) return false;
135
+ return true;
136
+ })
137
+ );
138
+ }
139
+ const partialOnlyHeader = this.ctx.request.header(InertiaHeaders.PartialOnly);
140
+ if (isPartial && partialOnlyHeader) newProps = this.#resolveOnly(props);
141
+ const partialExceptHeader = this.ctx.request.header(InertiaHeaders.PartialExcept);
142
+ if (isPartial && partialExceptHeader) newProps = this.#resolveExcept(newProps);
143
+ for (const [key, value] of Object.entries(props)) {
144
+ if (value instanceof AlwaysProp) newProps[key] = props[key];
145
+ }
146
+ return newProps;
147
+ }
148
+ /**
149
+ * Resolve a single prop by calling the callback or resolving the promise
150
+ */
151
+ async #resolvePageProps(props = {}) {
152
+ return Object.fromEntries(
153
+ await Promise.all(
154
+ Object.entries(props).map(async ([key, value]) => {
155
+ if (typeof value === "function") {
156
+ return [key, await value(this.ctx)];
157
+ }
158
+ if (value instanceof OptionalProp || value instanceof MergeProp || value instanceof DeferProp || value instanceof AlwaysProp) {
159
+ return [key, await value.callback()];
160
+ }
161
+ return [key, value];
162
+ })
163
+ )
164
+ );
165
+ }
166
+ /**
167
+ * Resolve the deferred props listing. Will be returned only
168
+ * on the first visit to the page and will be used to make
169
+ * subsequent partial requests
170
+ */
171
+ #resolveDeferredProps(component, pageProps) {
172
+ if (this.#isPartial(component)) return {};
173
+ const deferredProps = Object.entries(pageProps || {}).filter(([_, value]) => value instanceof DeferProp).map(([key, value]) => ({ key, group: value.getGroup() })).reduce(
174
+ (groups, { key, group }) => {
175
+ if (!groups[group]) groups[group] = [];
176
+ groups[group].push(key);
177
+ return groups;
178
+ },
179
+ {}
180
+ );
181
+ return Object.keys(deferredProps).length ? { deferredProps } : {};
182
+ }
183
+ /**
184
+ * Resolve the props that should be merged
185
+ */
186
+ #resolveMergeProps(pageProps) {
187
+ const inertiaResetHeader = this.ctx.request.header(InertiaHeaders.Reset) || "";
188
+ const resetProps = new Set(inertiaResetHeader.split(",").filter(Boolean));
189
+ const mergeProps = Object.entries(pageProps || {}).filter(([_, value]) => value instanceof MergeableProp && value.shouldMerge).map(([key]) => key).filter((key) => !resetProps.has(key));
190
+ return mergeProps.length ? { mergeProps } : {};
191
+ }
192
+ /**
193
+ * Build the page object that will be returned to the client
194
+ *
195
+ * See https://inertiajs.com/the-protocol#the-page-object
196
+ */
197
+ async #buildPageObject(component, pageProps) {
198
+ const propsToResolve = this.#pickPropsToResolve(component, {
199
+ ...this.#sharedData,
200
+ ...pageProps
201
+ });
202
+ return {
203
+ component,
204
+ url: this.ctx.request.url(true),
205
+ version: this.config.versionCache.getVersion(),
206
+ props: await this.#resolvePageProps(propsToResolve),
207
+ clearHistory: this.#shouldClearHistory,
208
+ encryptHistory: this.#shouldEncryptHistory,
209
+ ...this.#resolveMergeProps(pageProps),
210
+ ...this.#resolveDeferredProps(component, pageProps)
211
+ };
212
+ }
213
+ /**
214
+ * If the page should be rendered on the server or not
215
+ *
216
+ * The ssr.pages config can be a list of pages or a function that returns a boolean
217
+ */
218
+ async #shouldRenderOnServer(component) {
219
+ const isSsrEnabled = this.config.ssr.enabled;
220
+ if (!isSsrEnabled) return false;
221
+ let isSsrEnabledForPage = false;
222
+ if (typeof this.config.ssr.pages === "function") {
223
+ isSsrEnabledForPage = await this.config.ssr.pages(this.ctx, component);
224
+ } else if (this.config.ssr.pages) {
225
+ isSsrEnabledForPage = this.config.ssr.pages?.includes(component);
226
+ } else {
227
+ isSsrEnabledForPage = true;
228
+ }
229
+ return isSsrEnabledForPage;
230
+ }
231
+ /**
232
+ * Resolve the root view
233
+ */
234
+ #resolveRootView() {
235
+ return typeof this.config.rootView === "function" ? this.config.rootView(this.ctx) : this.config.rootView;
236
+ }
237
+ /**
238
+ * Render the page on the server
239
+ */
240
+ async #renderOnServer(pageObject, viewProps) {
241
+ const { head, body } = await this.#serverRenderer.render(pageObject);
242
+ return this.ctx.view.render(this.#resolveRootView(), {
243
+ ...viewProps,
244
+ page: { ssrHead: head, ssrBody: body, ...pageObject }
245
+ });
246
+ }
247
+ /**
248
+ * Share data for the current request.
249
+ * This data will override any shared data defined in the config.
250
+ */
251
+ share(data) {
252
+ this.#sharedData = { ...this.#sharedData, ...data };
253
+ }
254
+ /**
255
+ * Render a page using Inertia
256
+ */
257
+ async render(component, pageProps, viewProps) {
258
+ const pageObject = await this.#buildPageObject(component, pageProps);
259
+ const isInertiaRequest = !!this.ctx.request.header(InertiaHeaders.Inertia);
260
+ if (!isInertiaRequest) {
261
+ const shouldRenderOnServer = await this.#shouldRenderOnServer(component);
262
+ if (shouldRenderOnServer) return this.#renderOnServer(pageObject, viewProps);
263
+ return this.ctx.view.render(this.#resolveRootView(), { ...viewProps, page: pageObject });
264
+ }
265
+ this.ctx.response.header(InertiaHeaders.Inertia, "true");
266
+ return pageObject;
267
+ }
268
+ /**
269
+ * Clear history state.
270
+ *
271
+ * See https://v2.inertiajs.com/history-encryption#clearing-history
272
+ */
273
+ clearHistory() {
274
+ this.#shouldClearHistory = true;
275
+ }
276
+ /**
277
+ * Encrypt history
278
+ *
279
+ * See https://v2.inertiajs.com/history-encryption
280
+ */
281
+ encryptHistory(encrypt = true) {
282
+ this.#shouldEncryptHistory = encrypt;
283
+ }
284
+ /**
285
+ * Create a lazy prop
286
+ *
287
+ * Lazy props are never resolved on first visit, but only when the client
288
+ * request a partial reload explicitely with this value.
289
+ *
290
+ * See https://inertiajs.com/partial-reloads#lazy-data-evaluation
291
+ *
292
+ * @deprecated use `optional` instead
293
+ */
294
+ lazy(callback) {
295
+ return new OptionalProp(callback);
296
+ }
297
+ /**
298
+ * Create an optional prop
299
+ *
300
+ * See https://inertiajs.com/partial-reloads#lazy-data-evaluation
301
+ */
302
+ optional(callback) {
303
+ return new OptionalProp(callback);
304
+ }
305
+ /**
306
+ * Create a mergeable prop
307
+ *
308
+ * See https://v2.inertiajs.com/merging-props
309
+ */
310
+ merge(callback) {
311
+ return new MergeProp(callback);
312
+ }
313
+ /**
314
+ * Create an always prop
315
+ *
316
+ * Always props are resolved on every request, no matter if it's a partial
317
+ * request or not.
318
+ *
319
+ * See https://inertiajs.com/partial-reloads#lazy-data-evaluation
320
+ */
321
+ always(callback) {
322
+ return new AlwaysProp(callback);
323
+ }
324
+ /**
325
+ * Create a deferred prop
326
+ *
327
+ * Deferred props feature allows you to defer the loading of certain
328
+ * page data until after the initial page render.
329
+ *
330
+ * See https://v2.inertiajs.com/deferred-props
331
+ */
332
+ defer(callback, group = "default") {
333
+ return new DeferProp(callback, group);
334
+ }
335
+ /**
336
+ * This method can be used to redirect the user to an external website
337
+ * or even a non-inertia route of your application.
338
+ *
339
+ * See https://inertiajs.com/redirects#external-redirects
340
+ */
341
+ async location(url) {
342
+ this.ctx.response.header(InertiaHeaders.Location, url);
343
+ this.ctx.response.status(409);
344
+ }
345
+ };
346
+
347
+ // src/inertia_middleware.ts
348
+ var InertiaMiddleware = class {
349
+ constructor(config, vite) {
350
+ this.config = config;
351
+ this.vite = vite;
352
+ }
353
+ async handle(ctx, next) {
354
+ const { response, request } = ctx;
355
+ ctx.inertia = new Inertia(ctx, this.config, this.vite);
356
+ await next();
357
+ const isInertiaRequest = !!request.header("x-inertia");
358
+ if (!isInertiaRequest) return;
359
+ response.header("Vary", "Accept");
360
+ const method = request.method();
361
+ if (response.getStatus() === 302 && ["PUT", "PATCH", "DELETE"].includes(method)) {
362
+ response.status(303);
363
+ }
364
+ const version = this.config.versionCache.getVersion().toString();
365
+ if (method === "GET" && request.header("x-inertia-version", "") !== version) {
366
+ response.header("x-inertia-location", request.url());
367
+ response.status(409);
368
+ }
369
+ }
370
+ };
371
+
372
+ export {
373
+ InertiaMiddleware
374
+ };
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-YRkkep62.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-G6UKVO4B.js";
3
+ } from "../chunk-AI7SD6T7.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-YRkkep62.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-G6UKVO4B.js";
3
+ } from "../chunk-AI7SD6T7.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-YRkkep62.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-YRkkep62.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];
@@ -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.2",
4
+ "version": "2.0.0",
5
5
  "engines": {
6
6
  "node": ">=20.6.0"
7
7
  },
@@ -27,7 +27,7 @@
27
27
  "typecheck": "tsc --noEmit",
28
28
  "lint": "eslint",
29
29
  "format": "prettier --write .",
30
- "quick:test": "node --import=ts-node-maintained/register/esm bin/test.ts",
30
+ "quick:test": "node --enable-source-maps --import=ts-node-maintained/register/esm bin/test.ts",
31
31
  "pretest": "npm run lint",
32
32
  "test": "c8 npm run quick:test",
33
33
  "prebuild": "npm run lint && npm run clean",
@@ -94,6 +94,10 @@
94
94
  "optional": true
95
95
  }
96
96
  },
97
+ "publishConfig": {
98
+ "access": "public",
99
+ "tag": "beta"
100
+ },
97
101
  "author": "Julien Ripouteau <julien@ripouteau.com>,adonisjs",
98
102
  "license": "MIT",
99
103
  "keywords": [
@@ -1,199 +0,0 @@
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/inertia.ts
30
- var kLazySymbol = Symbol("lazy");
31
- var Inertia = class {
32
- constructor(ctx, config, vite) {
33
- this.ctx = ctx;
34
- this.config = config;
35
- this.vite = vite;
36
- this.#sharedData = config.sharedData;
37
- this.#serverRenderer = new ServerRenderer(config, vite);
38
- }
39
- #sharedData = {};
40
- #serverRenderer;
41
- /**
42
- * Check if a value is a lazy prop
43
- */
44
- #isLazyProps(value) {
45
- return typeof value === "object" && value && kLazySymbol in value;
46
- }
47
- /**
48
- * Pick props to resolve based on x-inertia-partial-data header
49
- *
50
- * If header is not present, resolve all props except lazy props
51
- * If header is present, resolve only the props that are listed in the header
52
- */
53
- #pickPropsToResolve(component, props) {
54
- const partialData = this.ctx.request.header("x-inertia-partial-data")?.split(",").filter(Boolean);
55
- const partialComponent = this.ctx.request.header("x-inertia-partial-component");
56
- let entriesToResolve = Object.entries(props);
57
- if (partialData && partialComponent === component) {
58
- entriesToResolve = entriesToResolve.filter(([key]) => partialData.includes(key));
59
- } else {
60
- entriesToResolve = entriesToResolve.filter(([key]) => !this.#isLazyProps(props[key]));
61
- }
62
- return entriesToResolve;
63
- }
64
- /**
65
- * Resolve the props that will be sent to the client
66
- */
67
- async #resolvePageProps(component, props) {
68
- const entriesToResolve = this.#pickPropsToResolve(component, props);
69
- const entries = entriesToResolve.map(async ([key, value]) => {
70
- if (typeof value === "function") {
71
- return [key, await value(this.ctx)];
72
- }
73
- if (this.#isLazyProps(value)) {
74
- const lazyValue = value[kLazySymbol];
75
- return [key, await lazyValue()];
76
- }
77
- return [key, value];
78
- });
79
- return Object.fromEntries(await Promise.all(entries));
80
- }
81
- /**
82
- * Build the page object that will be returned to the client
83
- *
84
- * See https://inertiajs.com/the-protocol#the-page-object
85
- */
86
- async #buildPageObject(component, pageProps) {
87
- return {
88
- component,
89
- version: this.config.versionCache.getVersion(),
90
- props: await this.#resolvePageProps(component, { ...this.#sharedData, ...pageProps }),
91
- url: this.ctx.request.url(true)
92
- };
93
- }
94
- /**
95
- * If the page should be rendered on the server or not
96
- *
97
- * The ssr.pages config can be a list of pages or a function that returns a boolean
98
- */
99
- async #shouldRenderOnServer(component) {
100
- const isSsrEnabled = this.config.ssr.enabled;
101
- if (!isSsrEnabled) return false;
102
- let isSsrEnabledForPage = false;
103
- if (typeof this.config.ssr.pages === "function") {
104
- isSsrEnabledForPage = await this.config.ssr.pages(this.ctx, component);
105
- } else if (this.config.ssr.pages) {
106
- isSsrEnabledForPage = this.config.ssr.pages?.includes(component);
107
- } else {
108
- isSsrEnabledForPage = true;
109
- }
110
- return isSsrEnabledForPage;
111
- }
112
- /**
113
- * Resolve the root view
114
- */
115
- #resolveRootView() {
116
- return typeof this.config.rootView === "function" ? this.config.rootView(this.ctx) : this.config.rootView;
117
- }
118
- /**
119
- * Render the page on the server
120
- */
121
- async #renderOnServer(pageObject, viewProps) {
122
- const { head, body } = await this.#serverRenderer.render(pageObject);
123
- return this.ctx.view.render(this.#resolveRootView(), {
124
- ...viewProps,
125
- page: { ssrHead: head, ssrBody: body, ...pageObject }
126
- });
127
- }
128
- /**
129
- * Share data for the current request.
130
- * This data will override any shared data defined in the config.
131
- */
132
- share(data) {
133
- this.#sharedData = { ...this.#sharedData, ...data };
134
- }
135
- /**
136
- * Render a page using Inertia
137
- */
138
- async render(component, pageProps, viewProps) {
139
- const pageObject = await this.#buildPageObject(component, pageProps);
140
- const isInertiaRequest = !!this.ctx.request.header("x-inertia");
141
- if (!isInertiaRequest) {
142
- const shouldRenderOnServer = await this.#shouldRenderOnServer(component);
143
- if (shouldRenderOnServer) return this.#renderOnServer(pageObject, viewProps);
144
- return this.ctx.view.render(this.#resolveRootView(), { ...viewProps, page: pageObject });
145
- }
146
- this.ctx.response.header("x-inertia", "true");
147
- return pageObject;
148
- }
149
- /**
150
- * Create a lazy prop
151
- *
152
- * Lazy props are never resolved on first visit, but only when the client
153
- * request a partial reload explicitely with this value.
154
- *
155
- * See https://inertiajs.com/partial-reloads#lazy-data-evaluation
156
- */
157
- lazy(callback) {
158
- return { [kLazySymbol]: callback };
159
- }
160
- /**
161
- * This method can be used to redirect the user to an external website
162
- * or even a non-inertia route of your application.
163
- *
164
- * See https://inertiajs.com/redirects#external-redirects
165
- */
166
- async location(url) {
167
- this.ctx.response.header("X-Inertia-Location", url);
168
- this.ctx.response.status(409);
169
- }
170
- };
171
-
172
- // src/inertia_middleware.ts
173
- var InertiaMiddleware = class {
174
- constructor(config, vite) {
175
- this.config = config;
176
- this.vite = vite;
177
- }
178
- async handle(ctx, next) {
179
- const { response, request } = ctx;
180
- ctx.inertia = new Inertia(ctx, this.config, this.vite);
181
- await next();
182
- const isInertiaRequest = !!request.header("x-inertia");
183
- if (!isInertiaRequest) return;
184
- response.header("Vary", "Accept");
185
- const method = request.method();
186
- if (response.getStatus() === 302 && ["PUT", "PATCH", "DELETE"].includes(method)) {
187
- response.status(303);
188
- }
189
- const version = this.config.versionCache.getVersion().toString();
190
- if (method === "GET" && request.header("x-inertia-version", "") !== version) {
191
- response.header("x-inertia-location", request.url());
192
- response.status(409);
193
- }
194
- }
195
- };
196
-
197
- export {
198
- InertiaMiddleware
199
- };