nextjs-cms 0.5.55 → 0.5.58
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/dist/api/index.d.ts +90 -45
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/lib/serverActions.d.ts +68 -5
- package/dist/api/lib/serverActions.d.ts.map +1 -1
- package/dist/api/lib/serverActions.js +33 -8
- package/dist/api/root.d.ts +961 -44
- package/dist/api/root.d.ts.map +1 -1
- package/dist/api/root.js +2 -1
- package/dist/api/routers/accountSettings.d.ts +1 -3
- package/dist/api/routers/accountSettings.d.ts.map +1 -1
- package/dist/api/routers/admins.d.ts +1 -3
- package/dist/api/routers/admins.d.ts.map +1 -1
- package/dist/api/routers/admins.js +1 -1
- package/dist/api/routers/auth.d.ts +9 -3
- package/dist/api/routers/auth.d.ts.map +1 -1
- package/dist/api/routers/categorySection.d.ts +1 -3
- package/dist/api/routers/categorySection.d.ts.map +1 -1
- package/dist/api/routers/cmsSettings.d.ts +1 -3
- package/dist/api/routers/cmsSettings.d.ts.map +1 -1
- package/dist/api/routers/cpanel.d.ts +1 -3
- package/dist/api/routers/cpanel.d.ts.map +1 -1
- package/dist/api/routers/files.d.ts +1 -3
- package/dist/api/routers/files.d.ts.map +1 -1
- package/dist/api/routers/gallery.d.ts +1 -3
- package/dist/api/routers/gallery.d.ts.map +1 -1
- package/dist/api/routers/gallery.js +7 -6
- package/dist/api/routers/googleAnalytics.d.ts +1 -3
- package/dist/api/routers/googleAnalytics.d.ts.map +1 -1
- package/dist/api/routers/hasItemsSection.d.ts +49 -5
- package/dist/api/routers/hasItemsSection.d.ts.map +1 -1
- package/dist/api/routers/navigation.d.ts +1 -3
- package/dist/api/routers/navigation.d.ts.map +1 -1
- package/dist/api/routers/simpleSection.d.ts +21 -4
- package/dist/api/routers/simpleSection.d.ts.map +1 -1
- package/dist/api/trpc/query-client.d.ts +3 -0
- package/dist/api/trpc/query-client.d.ts.map +1 -0
- package/dist/api/trpc/query-client.js +23 -0
- package/dist/api/trpc/server.d.ts +8 -0
- package/dist/api/trpc/server.d.ts.map +1 -0
- package/dist/api/trpc/server.js +23 -0
- package/dist/api/trpc.d.ts +6 -17
- package/dist/api/trpc.d.ts.map +1 -1
- package/dist/api/trpc.js +6 -9
- package/dist/auth/hooks/useRefreshToken.d.ts.map +1 -1
- package/dist/auth/hooks/useRefreshToken.js +11 -9
- package/dist/auth/lib/actions.d.ts +10 -0
- package/dist/auth/lib/actions.d.ts.map +1 -1
- package/dist/auth/lib/actions.js +50 -20
- package/dist/auth/react.d.ts +7 -1
- package/dist/auth/react.d.ts.map +1 -1
- package/dist/auth/react.js +51 -29
- package/dist/auth/trpc.d.ts.map +1 -1
- package/dist/auth/trpc.js +2 -1
- package/dist/core/config/config-loader.d.ts +1 -1
- package/dist/core/config/config-loader.d.ts.map +1 -1
- package/dist/core/config/config-loader.js +9 -6
- package/dist/core/config/index.d.ts +1 -0
- package/dist/core/config/index.d.ts.map +1 -1
- package/dist/core/config/index.js +1 -0
- package/dist/core/config/loader-with-esbuild.d.ts +7 -0
- package/dist/core/config/loader-with-esbuild.d.ts.map +1 -0
- package/dist/core/config/loader-with-esbuild.js +98 -0
- package/dist/core/config/loader-with-jiti.d.ts +13 -0
- package/dist/core/config/loader-with-jiti.d.ts.map +1 -0
- package/dist/core/config/loader-with-jiti.js +162 -0
- package/dist/core/config/loader.d.ts +1 -1
- package/dist/core/config/loader.d.ts.map +1 -1
- package/dist/core/config/loader.js +1 -75
- package/dist/core/factories/SectionFactory.d.ts +1 -109
- package/dist/core/factories/SectionFactory.d.ts.map +1 -1
- package/dist/core/factories/SectionFactory.js +1 -452
- package/dist/core/factories/section-factory-with-esbuild.d.ts +110 -0
- package/dist/core/factories/section-factory-with-esbuild.d.ts.map +1 -0
- package/dist/core/factories/section-factory-with-esbuild.js +509 -0
- package/dist/core/factories/section-factory-with-jiti.d.ts +113 -0
- package/dist/core/factories/section-factory-with-jiti.d.ts.map +1 -0
- package/dist/core/factories/section-factory-with-jiti.js +556 -0
- package/dist/core/fields/document.d.ts +0 -1
- package/dist/core/fields/document.d.ts.map +1 -1
- package/dist/core/fields/document.js +5 -4
- package/dist/core/fields/photo.d.ts +10 -7
- package/dist/core/fields/photo.d.ts.map +1 -1
- package/dist/core/fields/photo.js +44 -17
- package/dist/core/fields/richText.d.ts +0 -1
- package/dist/core/fields/richText.d.ts.map +1 -1
- package/dist/core/fields/richText.js +5 -4
- package/dist/core/fields/video.d.ts +0 -1
- package/dist/core/fields/video.d.ts.map +1 -1
- package/dist/core/fields/video.js +5 -4
- package/dist/core/sections/section.d.ts +17 -15
- package/dist/core/sections/section.d.ts.map +1 -1
- package/dist/core/sections/section.js +28 -5
- package/dist/core/submit/submit.d.ts.map +1 -1
- package/dist/core/submit/submit.js +13 -12
- package/dist/translations/dictionaries/ar.json +1 -0
- package/dist/translations/dictionaries/en.json +1 -0
- package/dist/translations/index.d.ts.map +1 -1
- package/dist/translations/index.js +1 -3
- package/package.json +16 -7
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
1
2
|
import { logout, refreshSession } from '../react.js';
|
|
2
3
|
/**
|
|
3
4
|
* This hook is used to refresh the access token when it expires.
|
|
4
5
|
* It is used in the useAxiosPrivate hook to refresh the access token when a request returns a 401 error.
|
|
5
6
|
*/
|
|
6
7
|
const useRefreshToken = () => {
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
const isRefreshingRef = React.useRef(false); // Is a refresh request being sent?
|
|
9
|
+
// An array of requests that failed because of 401
|
|
10
|
+
const failedQueueRef = React.useRef([]);
|
|
9
11
|
const processQueue = (error, token = null) => {
|
|
10
|
-
|
|
12
|
+
failedQueueRef.current.forEach((prom) => {
|
|
11
13
|
if (error) {
|
|
12
14
|
prom.reject(error);
|
|
13
15
|
}
|
|
@@ -15,7 +17,7 @@ const useRefreshToken = () => {
|
|
|
15
17
|
prom.resolve(token);
|
|
16
18
|
}
|
|
17
19
|
});
|
|
18
|
-
|
|
20
|
+
failedQueueRef.current = [];
|
|
19
21
|
};
|
|
20
22
|
const refresh = async () => {
|
|
21
23
|
try {
|
|
@@ -24,7 +26,7 @@ const useRefreshToken = () => {
|
|
|
24
26
|
/**
|
|
25
27
|
* The refresh request is done
|
|
26
28
|
*/
|
|
27
|
-
|
|
29
|
+
isRefreshingRef.current = false;
|
|
28
30
|
if (response.status !== 200) {
|
|
29
31
|
/**
|
|
30
32
|
* If the refresh token is invalid, we log out the user
|
|
@@ -41,7 +43,7 @@ const useRefreshToken = () => {
|
|
|
41
43
|
/**
|
|
42
44
|
* update the session
|
|
43
45
|
*/
|
|
44
|
-
await refreshSession();
|
|
46
|
+
await refreshSession(data.session);
|
|
45
47
|
}
|
|
46
48
|
/**
|
|
47
49
|
* Process the failed requests
|
|
@@ -65,14 +67,14 @@ const useRefreshToken = () => {
|
|
|
65
67
|
// TODO: Apply this inside useAxiosPrivate.tsx to prevent even the 401 errors from happening
|
|
66
68
|
// Let's use semaphores to prevent multiple refreshes at the same time
|
|
67
69
|
return async () => {
|
|
68
|
-
if (
|
|
70
|
+
if (isRefreshingRef.current) {
|
|
69
71
|
// If a refresh request is being sent, we return a promise
|
|
70
72
|
// that will be resolved when the refresh request is done
|
|
71
73
|
return new Promise((resolve, reject) => {
|
|
72
|
-
|
|
74
|
+
failedQueueRef.current.push({ resolve, reject });
|
|
73
75
|
});
|
|
74
76
|
}
|
|
75
|
-
|
|
77
|
+
isRefreshingRef.current = true; // A refresh request is being sent
|
|
76
78
|
return await refresh(); // Send the refresh request and return the new access token
|
|
77
79
|
};
|
|
78
80
|
};
|
|
@@ -5,10 +5,12 @@ export declare const authRefresh: (refreshToken?: {
|
|
|
5
5
|
status: number;
|
|
6
6
|
code: string;
|
|
7
7
|
state: string;
|
|
8
|
+
session?: undefined;
|
|
8
9
|
user?: undefined;
|
|
9
10
|
} | {
|
|
10
11
|
status: number;
|
|
11
12
|
state: string;
|
|
13
|
+
session: Session;
|
|
12
14
|
user: {
|
|
13
15
|
id: string;
|
|
14
16
|
username: string;
|
|
@@ -21,6 +23,14 @@ export declare const login: ({ username, password }: {
|
|
|
21
23
|
username: string;
|
|
22
24
|
password: string;
|
|
23
25
|
}) => Promise<{
|
|
26
|
+
status: number;
|
|
27
|
+
session: {
|
|
28
|
+
user: {
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
locale: string | null;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
24
34
|
user: {
|
|
25
35
|
id: string;
|
|
26
36
|
username: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../src/auth/lib/actions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,eAAO,MAAM,WAAW,GAAU,eAAe;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../src/auth/lib/actions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,eAAO,MAAM,WAAW,GAAU,eAAe;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;EA4HjE,CAAA;AAED,eAAO,MAAM,KAAK,GAAU,wBAAwB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;EA+GzF,CAAA;AAED,eAAO,MAAM,aAAa,GAAU,UAAU,OAAO,GAAG,IAAI,qBA0B3D,CAAA"}
|
package/dist/auth/lib/actions.js
CHANGED
|
@@ -29,7 +29,7 @@ export const authRefresh = async (refreshToken) => {
|
|
|
29
29
|
.where(and(eq(AccessTokensTable.adminId, decoded.id), eq(AccessTokensTable.refreshToken, refreshToken.value)))
|
|
30
30
|
.limit(1);
|
|
31
31
|
const adminTokenResult = result[0];
|
|
32
|
-
if (!adminTokenResult) {
|
|
32
|
+
if (!adminTokenResult || !adminTokenResult.adminId || !adminTokenResult.username) {
|
|
33
33
|
return {
|
|
34
34
|
status: 401,
|
|
35
35
|
code: 'invalid_refresh_token',
|
|
@@ -39,17 +39,24 @@ export const authRefresh = async (refreshToken) => {
|
|
|
39
39
|
// Let's create a JWT access token and refresh token
|
|
40
40
|
const accessToken = encodeJWT({
|
|
41
41
|
id: decoded.id,
|
|
42
|
-
iss: '
|
|
42
|
+
iss: 'nextjs-cms',
|
|
43
43
|
aud: 'admin',
|
|
44
44
|
sub: adminTokenResult.username,
|
|
45
45
|
locale: adminTokenResult.locale || 'en',
|
|
46
46
|
});
|
|
47
47
|
const newRefreshToken = encodeRefreshToken({
|
|
48
48
|
id: adminTokenResult.adminId,
|
|
49
|
-
iss: '
|
|
49
|
+
iss: 'nextjs-cms',
|
|
50
50
|
aud: 'admin',
|
|
51
51
|
sub: adminTokenResult.username,
|
|
52
52
|
});
|
|
53
|
+
if (!accessToken || !newRefreshToken) {
|
|
54
|
+
return {
|
|
55
|
+
status: 401,
|
|
56
|
+
code: 'failed_to_create_tokens',
|
|
57
|
+
state: 'logged_out',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
53
60
|
// Let's update the refresh token in the database
|
|
54
61
|
await db
|
|
55
62
|
.update(AccessTokensTable)
|
|
@@ -78,10 +85,17 @@ export const authRefresh = async (refreshToken) => {
|
|
|
78
85
|
sameSite: true,
|
|
79
86
|
maxAge: 60 * 60 * 2, // 2 hours
|
|
80
87
|
});
|
|
88
|
+
const session = {
|
|
89
|
+
user: {
|
|
90
|
+
id: adminTokenResult.adminId,
|
|
91
|
+
name: adminTokenResult.username,
|
|
92
|
+
locale: adminTokenResult.locale ?? null,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
81
95
|
return {
|
|
82
96
|
status: 200,
|
|
83
|
-
// accessToken: accessToken,
|
|
84
97
|
state: 'logged_in',
|
|
98
|
+
session,
|
|
85
99
|
user: {
|
|
86
100
|
id: adminTokenResult.adminId,
|
|
87
101
|
username: adminTokenResult.username,
|
|
@@ -114,7 +128,7 @@ export const login = async ({ username, password }) => {
|
|
|
114
128
|
.where(eq(AdminsTable.user, username))
|
|
115
129
|
.limit(1);
|
|
116
130
|
const admin = result[0];
|
|
117
|
-
if (!admin) {
|
|
131
|
+
if (!admin || !admin.password || !admin.id || !admin.username) {
|
|
118
132
|
throw new Error('Invalid credentials');
|
|
119
133
|
}
|
|
120
134
|
// Verify password with bcrypt
|
|
@@ -125,33 +139,41 @@ export const login = async ({ username, password }) => {
|
|
|
125
139
|
// Let's create a JWT access token and refresh token
|
|
126
140
|
const accessToken = encodeJWT({
|
|
127
141
|
id: admin.id,
|
|
128
|
-
iss: '
|
|
142
|
+
iss: 'nextjs-cms',
|
|
129
143
|
aud: 'admin',
|
|
130
144
|
sub: admin.username,
|
|
131
145
|
locale: admin.locale || 'en',
|
|
132
146
|
});
|
|
133
147
|
const refreshToken = encodeRefreshToken({
|
|
134
148
|
id: admin.id,
|
|
135
|
-
iss: '
|
|
149
|
+
iss: 'nextjs-cms',
|
|
136
150
|
aud: 'admin',
|
|
137
151
|
sub: admin.username,
|
|
138
152
|
});
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
})
|
|
148
|
-
.onDuplicateKeyUpdate({
|
|
149
|
-
set: {
|
|
153
|
+
if (!accessToken || !refreshToken) {
|
|
154
|
+
throw new Error('Failed to create access and refresh tokens');
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
// Let's save the refresh token in the database
|
|
158
|
+
await db
|
|
159
|
+
.insert(AccessTokensTable)
|
|
160
|
+
.values({
|
|
150
161
|
accessToken,
|
|
151
162
|
refreshToken,
|
|
163
|
+
adminId: admin.id,
|
|
152
164
|
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
153
|
-
}
|
|
154
|
-
|
|
165
|
+
})
|
|
166
|
+
.onDuplicateKeyUpdate({
|
|
167
|
+
set: {
|
|
168
|
+
accessToken,
|
|
169
|
+
refreshToken,
|
|
170
|
+
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
throw new Error('Failed to save access and refresh tokens to database');
|
|
176
|
+
}
|
|
155
177
|
const cookieStore = await cookies();
|
|
156
178
|
// Let's send the refresh token as a cookie
|
|
157
179
|
cookieStore.set({
|
|
@@ -173,6 +195,14 @@ export const login = async ({ username, password }) => {
|
|
|
173
195
|
});
|
|
174
196
|
// Now, return the access token in body
|
|
175
197
|
return {
|
|
198
|
+
status: 200,
|
|
199
|
+
session: {
|
|
200
|
+
user: {
|
|
201
|
+
id: admin.id,
|
|
202
|
+
name: admin.username,
|
|
203
|
+
locale: admin.locale,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
176
206
|
user: {
|
|
177
207
|
id: admin.id,
|
|
178
208
|
username: admin.username,
|
package/dist/auth/react.d.ts
CHANGED
|
@@ -45,6 +45,12 @@ export interface AuthClientConfig {
|
|
|
45
45
|
* trigger session updates from places like `signIn` or `signOut`
|
|
46
46
|
*/
|
|
47
47
|
_getSession: (...args: any[]) => any;
|
|
48
|
+
/**
|
|
49
|
+
* Directly set the session in the current tab/window,
|
|
50
|
+
* after being fetched from the server during a refresh request.
|
|
51
|
+
* This is to avoid the need to fetch the session from the server again.
|
|
52
|
+
*/
|
|
53
|
+
_setSession: (session: Session | null) => void;
|
|
48
54
|
}
|
|
49
55
|
export declare const __AUTH: AuthClientConfig;
|
|
50
56
|
export declare function logout(options?: {
|
|
@@ -54,7 +60,7 @@ export declare function login({ username, password }: {
|
|
|
54
60
|
username: string;
|
|
55
61
|
password: string;
|
|
56
62
|
}): Promise<void>;
|
|
57
|
-
export declare function refreshSession(): Promise<void>;
|
|
63
|
+
export declare function refreshSession(session: Session): Promise<void>;
|
|
58
64
|
/**
|
|
59
65
|
* Returns the current CSRF token.
|
|
60
66
|
* required to make requests that changes state.
|
package/dist/auth/react.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/auth/react.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEzC,OAAO,eAAe,MAAM,4BAA4B,CAAA;AACxD,OAAO,eAAe,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/auth/react.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEzC,OAAO,eAAe,MAAM,4BAA4B,CAAA;AACxD,OAAO,eAAe,MAAM,4BAA4B,CAAA;AAExD,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;AACnE,MAAM,WAAW,oBAAoB;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IACxB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,KAAK,CAAA;CAC7B;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,OAAO;IAChD,QAAQ,EAAE,CAAC,CAAA;IACX,2BAA2B;IAC3B,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAA;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC7B,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IAC/C,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,SAAS,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAA;IACrC,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,WAAW,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAA;IAEpC;;;;OAIG;IACH,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,KAAK,IAAI,CAAA;CACjD;AAED,eAAO,MAAM,MAAM,EAAE,gBAKpB,CAAA;AAED,wBAAsB,MAAM,CAAC,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,iBA2BjE;AAED,wBAAsB,KAAK,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,iBA0BzF;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,OAAO,iBAWpD;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,oBAGjC;AAwBD,wBAAsB,UAAU,CAAC,MAAM,CAAC,EAAE,gBAAgB,2BAWzD;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,OAAO,GAAG,KAAK,IAAI,CAAC,SAAS,IAAI,GAE/D;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GACjE;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAExD;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GACjE;IACI,MAAM,EAAE,aAAa,CAAA;IACrB,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAA;CACxC,CAAA;AAEb,eAAO,MAAM,cAAc;YAPL,aAAa;UAAQ,OAAO;YAAU,eAAe;;YAEnD,aAAa;UACf,IAAI;YACF,iBAAiB,GAAG,SAAS;cAG0C,CAAA;AAE/F,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CA6BpG;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,qBAuO1D;AAED,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,CAAA;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAA"}
|
package/dist/auth/react.js
CHANGED
|
@@ -11,6 +11,7 @@ export const __AUTH = {
|
|
|
11
11
|
_lastSync: 0,
|
|
12
12
|
_session: undefined,
|
|
13
13
|
_getSession: () => { },
|
|
14
|
+
_setSession: () => { },
|
|
14
15
|
};
|
|
15
16
|
export async function logout(options) {
|
|
16
17
|
const { deleteCookies = true } = options ?? {};
|
|
@@ -51,26 +52,27 @@ export async function login({ username, password }) {
|
|
|
51
52
|
const error = await response.json();
|
|
52
53
|
throw new Error(error.error);
|
|
53
54
|
}
|
|
55
|
+
const data = await response.json();
|
|
54
56
|
/**
|
|
55
57
|
* Broadcast a message to all other tabs/windows to tell them the user has logged in.
|
|
56
58
|
* This is to ensure all tabs/windows trigger a session update.
|
|
57
59
|
*/
|
|
58
|
-
broadcast().postMessage({ event: 'session', data: { trigger: '
|
|
60
|
+
broadcast().postMessage({ event: 'session', data: { trigger: 'refetch' } });
|
|
59
61
|
/**
|
|
60
62
|
* Update the session in the current tab/window.
|
|
61
63
|
*/
|
|
62
|
-
|
|
64
|
+
__AUTH._setSession(data.session);
|
|
63
65
|
}
|
|
64
|
-
export async function refreshSession() {
|
|
66
|
+
export async function refreshSession(session) {
|
|
65
67
|
/**
|
|
66
68
|
* Broadcast a message to all other tabs/windows to tell them the session has been updated.
|
|
67
69
|
* This is to ensure all tabs/windows trigger a session update.
|
|
68
70
|
*/
|
|
69
|
-
broadcast().postMessage({ event: 'session', data: { trigger: '
|
|
71
|
+
broadcast().postMessage({ event: 'session', data: { trigger: 'refetch' } });
|
|
70
72
|
/**
|
|
71
73
|
* Update the session in the current tab/window.
|
|
72
74
|
*/
|
|
73
|
-
|
|
75
|
+
__AUTH._setSession(session);
|
|
74
76
|
}
|
|
75
77
|
/**
|
|
76
78
|
* Returns the current CSRF token.
|
|
@@ -90,8 +92,8 @@ function broadcast() {
|
|
|
90
92
|
if (typeof BroadcastChannel === 'undefined') {
|
|
91
93
|
return {
|
|
92
94
|
postMessage: () => { },
|
|
93
|
-
addEventListener: () => { },
|
|
94
|
-
removeEventListener: () => { },
|
|
95
|
+
addEventListener: (_type, _listener) => { },
|
|
96
|
+
removeEventListener: (_type, _listener) => { },
|
|
95
97
|
};
|
|
96
98
|
}
|
|
97
99
|
if (broadcastChannel === null) {
|
|
@@ -176,6 +178,11 @@ export function SessionProvider(props) {
|
|
|
176
178
|
/** If session was passed, initialize as not loading */
|
|
177
179
|
const [loading, setLoading] = React.useState(!hasInitialSession);
|
|
178
180
|
React.useEffect(() => {
|
|
181
|
+
__AUTH._setSession = (session) => {
|
|
182
|
+
__AUTH._lastSync = now();
|
|
183
|
+
__AUTH._session = session;
|
|
184
|
+
setSession(session);
|
|
185
|
+
};
|
|
179
186
|
async function generateInitialSession() {
|
|
180
187
|
/**
|
|
181
188
|
* Use the `axiosPrivate` instance to make a request to `/api/auth/session` to get the
|
|
@@ -194,7 +201,7 @@ export function SessionProvider(props) {
|
|
|
194
201
|
async function init() {
|
|
195
202
|
/**
|
|
196
203
|
* Make sure the session is created at the start of the app.
|
|
197
|
-
*
|
|
204
|
+
* If session is not set, this means this is the first time
|
|
198
205
|
* the app is being loaded.
|
|
199
206
|
*/
|
|
200
207
|
if (__AUTH._session === undefined) {
|
|
@@ -209,18 +216,15 @@ export function SessionProvider(props) {
|
|
|
209
216
|
*/
|
|
210
217
|
init()
|
|
211
218
|
.then(async (session) => {
|
|
212
|
-
|
|
219
|
+
/**
|
|
220
|
+
* Only update if we fetched a new session (i.e., no initial session was provided).
|
|
221
|
+
* When hasInitialSession is true, __AUTH._session and state are already set,
|
|
222
|
+
* so we skip the redundant update.
|
|
223
|
+
*/
|
|
224
|
+
if (!hasInitialSession && session) {
|
|
213
225
|
__AUTH._session = session;
|
|
214
226
|
setSession(session);
|
|
215
227
|
}
|
|
216
|
-
else {
|
|
217
|
-
/**
|
|
218
|
-
* It's not necessary to log out the user and broadcast the logout event,
|
|
219
|
-
* because there are no cookies set to delete.
|
|
220
|
-
* But it's a good practice to do so.
|
|
221
|
-
*/
|
|
222
|
-
// await logout()
|
|
223
|
-
}
|
|
224
228
|
})
|
|
225
229
|
.then(() => {
|
|
226
230
|
__AUTH._getSession = async ({ event } = {}) => {
|
|
@@ -235,11 +239,11 @@ export function SessionProvider(props) {
|
|
|
235
239
|
return;
|
|
236
240
|
}
|
|
237
241
|
/**
|
|
238
|
-
* If the event is a
|
|
239
|
-
*
|
|
242
|
+
* If the event is a refetch event, we should update the session and not broadcast the event.
|
|
243
|
+
* Refetch events can be triggered when a broadcast message is sent from another
|
|
240
244
|
* tab/window. (see below)
|
|
241
245
|
*/
|
|
242
|
-
if (event === '
|
|
246
|
+
if (event === 'refetch') {
|
|
243
247
|
__AUTH._lastSync = now();
|
|
244
248
|
__AUTH._session = await getSession({
|
|
245
249
|
broadcast: false,
|
|
@@ -254,7 +258,7 @@ export function SessionProvider(props) {
|
|
|
254
258
|
!event ||
|
|
255
259
|
// If the client doesn't have a session then we don't need to call
|
|
256
260
|
// the server to check if it does (if they have signed in via another
|
|
257
|
-
// tab or window that will come through as a "
|
|
261
|
+
// tab or window that will come through as a "refetch" event anyway)
|
|
258
262
|
__AUTH._session === null ||
|
|
259
263
|
// Bail out early if the client session is not stale yet
|
|
260
264
|
now() < __AUTH._lastSync) {
|
|
@@ -281,21 +285,39 @@ export function SessionProvider(props) {
|
|
|
281
285
|
__AUTH._lastSync = 0;
|
|
282
286
|
__AUTH._session = undefined;
|
|
283
287
|
__AUTH._getSession = () => { };
|
|
288
|
+
__AUTH._setSession = () => { };
|
|
284
289
|
};
|
|
285
290
|
}, []);
|
|
286
291
|
React.useEffect(() => {
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
+
const handleBroadcastMessage = (event) => {
|
|
293
|
+
try {
|
|
294
|
+
const message = event.data;
|
|
295
|
+
// Only handle session-related broadcast messages
|
|
296
|
+
if (message.event !== 'session')
|
|
297
|
+
return;
|
|
298
|
+
// Handle logout events by updating the session directly
|
|
299
|
+
if (message.data?.trigger === 'logout') {
|
|
300
|
+
__AUTH._getSession({ event: 'logout' });
|
|
301
|
+
}
|
|
302
|
+
// Handle refetch events by updating the session directly
|
|
303
|
+
if (message.data?.trigger === 'refetch') {
|
|
304
|
+
__AUTH._getSession({ event: 'refetch' });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
// Ignore invalid JSON or malformed messages
|
|
309
|
+
console.warn('Failed to parse broadcast message:', error);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
// Listen for broadcast events fired from another tab/window
|
|
313
|
+
// refetch/logout the current session data depending on the event
|
|
292
314
|
// Note: We could pass session data through and do something like
|
|
293
315
|
// `setData(message.data)` but that can cause problems depending
|
|
294
316
|
// on how the session object is being used in the client; it is
|
|
295
317
|
// more robust to have each window/tab fetch it's own copy of the
|
|
296
318
|
// session object rather than share it across instances.
|
|
297
|
-
broadcast().addEventListener('message',
|
|
298
|
-
return () => broadcast().removeEventListener('message',
|
|
319
|
+
broadcast().addEventListener('message', handleBroadcastMessage);
|
|
320
|
+
return () => broadcast().removeEventListener('message', handleBroadcastMessage);
|
|
299
321
|
}, []);
|
|
300
322
|
React.useEffect(() => {
|
|
301
323
|
const { refetchOnWindowFocus = true } = props;
|
|
@@ -335,7 +357,7 @@ export function SessionProvider(props) {
|
|
|
335
357
|
setSession(newSession);
|
|
336
358
|
broadcast().postMessage({
|
|
337
359
|
event: 'session',
|
|
338
|
-
data: { trigger: '
|
|
360
|
+
data: { trigger: 'refetch' },
|
|
339
361
|
});
|
|
340
362
|
}
|
|
341
363
|
return newSession;
|
package/dist/auth/trpc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trpc.d.ts","sourceRoot":"","sources":["../../src/auth/trpc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAM5C;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAAO,QAAQ,CAAC,aAAa,CAAC,GAAG,
|
|
1
|
+
{"version":3,"file":"trpc.d.ts","sourceRoot":"","sources":["../../src/auth/trpc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAM5C;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAAO,QAAQ,CAAC,aAAa,CAAC,GAAG,CA6E7D,CAAA"}
|
package/dist/auth/trpc.js
CHANGED
|
@@ -25,6 +25,7 @@ export const refreshTokenLink = () => {
|
|
|
25
25
|
refreshPromise = (async () => {
|
|
26
26
|
try {
|
|
27
27
|
const response = await fetch('/api/auth/refresh');
|
|
28
|
+
const data = await response.json();
|
|
28
29
|
if (response.status !== 200) {
|
|
29
30
|
await logout({
|
|
30
31
|
/**
|
|
@@ -34,7 +35,7 @@ export const refreshTokenLink = () => {
|
|
|
34
35
|
});
|
|
35
36
|
return;
|
|
36
37
|
}
|
|
37
|
-
await refreshSession();
|
|
38
|
+
await refreshSession(data.session);
|
|
38
39
|
}
|
|
39
40
|
catch (e) {
|
|
40
41
|
await logout({
|
|
@@ -97,6 +97,6 @@ export interface ComputedCMSConfig {
|
|
|
97
97
|
* @returns A readonly computed CMS configuration with all required fields
|
|
98
98
|
* @throws Error if the config file exists but is invalid
|
|
99
99
|
*/
|
|
100
|
-
export declare function getCMSConfig(): Readonly<ComputedCMSConfig
|
|
100
|
+
export declare function getCMSConfig(): Promise<Readonly<ComputedCMSConfig>>;
|
|
101
101
|
export {};
|
|
102
102
|
//# sourceMappingURL=config-loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../../src/core/config/config-loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAIxB,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+InB,CAAA;AAGF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAGvD,MAAM,WAAW,iBAAiB;IAC9B,EAAE,EAAE;QACA,KAAK,EAAE,MAAM,CAAA;QACb,YAAY,EAAE,MAAM,CAAA;QACpB,IAAI,EAAE,MAAM,CAAA;KACf,CAAA;IACD,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE;QACN,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,OAAO,CAAA;QACf,QAAQ,EAAE;YACN,oBAAoB,EAAE,OAAO,CAAA;SAChC,CAAA;KACJ,CAAA;IACD,KAAK,EAAE;QACH,MAAM,EAAE;YACJ,IAAI,EAAE,MAAM,CAAA;YACZ,iBAAiB,EAAE,OAAO,CAAA;SAC7B,CAAA;QACD,MAAM,EAAE;YACJ,SAAS,EAAE;gBACP,KAAK,EAAE,MAAM,CAAA;gBACb,MAAM,EAAE,MAAM,CAAA;gBACd,IAAI,EAAE,OAAO,CAAA;gBACb,OAAO,EAAE,MAAM,CAAA;aAClB,CAAA;YACD,SAAS,EAAE,OAAO,CAAA;SACrB,CAAA;KACJ,CAAA;IACD,gBAAgB,EAAE;QACd,OAAO,EAAE;YACL,OAAO,EAAE,OAAO,CAAA;YAChB,MAAM,EAAE,MAAM,CAAA;YACd,QAAQ,EAAE,MAAM,CAAA;YAChB,MAAM,EAAE;gBACJ,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;gBAChD,OAAO,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;aACpD,CAAA;SACJ,CAAA;KACJ,CAAA;CACJ;
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../../src/core/config/config-loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAIxB,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+InB,CAAA;AAGF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAGvD,MAAM,WAAW,iBAAiB;IAC9B,EAAE,EAAE;QACA,KAAK,EAAE,MAAM,CAAA;QACb,YAAY,EAAE,MAAM,CAAA;QACpB,IAAI,EAAE,MAAM,CAAA;KACf,CAAA;IACD,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE;QACN,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,OAAO,CAAA;QACf,QAAQ,EAAE;YACN,oBAAoB,EAAE,OAAO,CAAA;SAChC,CAAA;KACJ,CAAA;IACD,KAAK,EAAE;QACH,MAAM,EAAE;YACJ,IAAI,EAAE,MAAM,CAAA;YACZ,iBAAiB,EAAE,OAAO,CAAA;SAC7B,CAAA;QACD,MAAM,EAAE;YACJ,SAAS,EAAE;gBACP,KAAK,EAAE,MAAM,CAAA;gBACb,MAAM,EAAE,MAAM,CAAA;gBACd,IAAI,EAAE,OAAO,CAAA;gBACb,OAAO,EAAE,MAAM,CAAA;aAClB,CAAA;YACD,SAAS,EAAE,OAAO,CAAA;SACrB,CAAA;KACJ,CAAA;IACD,gBAAgB,EAAE;QACd,OAAO,EAAE;YACL,OAAO,EAAE,OAAO,CAAA;YAChB,MAAM,EAAE,MAAM,CAAA;YACd,QAAQ,EAAE,MAAM,CAAA;YAChB,MAAM,EAAE;gBACJ,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;gBAChD,OAAO,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;aACpD,CAAA;SACJ,CAAA;KACJ,CAAA;CACJ;AAoID;;;;;;GAMG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAazE"}
|
|
@@ -4,7 +4,7 @@ if (typeof window !== 'undefined') {
|
|
|
4
4
|
// import 'server-only'
|
|
5
5
|
// import chalk from 'chalk'
|
|
6
6
|
import * as z from 'zod';
|
|
7
|
-
import { loadConfigModule } from './loader.js';
|
|
7
|
+
import { loadConfigModule, getConfigImportVersion } from './loader.js';
|
|
8
8
|
// 1. Define config schema
|
|
9
9
|
const cmsConfigSchema = z.object({
|
|
10
10
|
/**
|
|
@@ -237,6 +237,7 @@ const defaultConfig = {
|
|
|
237
237
|
return `${chalk.bgRed.bold(' Error ')} ${error} ${greyMsg ? chalk.grey(greyMsg) : ''}`.trim()
|
|
238
238
|
}*/
|
|
239
239
|
let cachedConfig;
|
|
240
|
+
let cachedConfigVersion = -1;
|
|
240
241
|
/**
|
|
241
242
|
* Loads and parses the user configuration module.
|
|
242
243
|
* This is called lazily when getCMSConfig() is first invoked.
|
|
@@ -244,9 +245,9 @@ let cachedConfig;
|
|
|
244
245
|
* @returns The parsed user configuration object, or undefined if no config file exists
|
|
245
246
|
* @throws Error if the config file exists but cannot be loaded or parsed
|
|
246
247
|
*/
|
|
247
|
-
function loadUserConfig() {
|
|
248
|
+
async function loadUserConfig() {
|
|
248
249
|
try {
|
|
249
|
-
const mod = loadConfigModule();
|
|
250
|
+
const mod = (await loadConfigModule());
|
|
250
251
|
const config = mod ? (mod.config ?? mod) : undefined;
|
|
251
252
|
if (!config) {
|
|
252
253
|
return undefined;
|
|
@@ -270,13 +271,15 @@ function loadUserConfig() {
|
|
|
270
271
|
* @returns A readonly computed CMS configuration with all required fields
|
|
271
272
|
* @throws Error if the config file exists but is invalid
|
|
272
273
|
*/
|
|
273
|
-
export function getCMSConfig() {
|
|
274
|
-
|
|
274
|
+
export async function getCMSConfig() {
|
|
275
|
+
const currentVersion = getConfigImportVersion();
|
|
276
|
+
if (cachedConfig === undefined || cachedConfigVersion !== currentVersion) {
|
|
275
277
|
// Load user config lazily (only when this function is first called)
|
|
276
|
-
const userConfig = loadUserConfig();
|
|
278
|
+
const userConfig = await loadUserConfig();
|
|
277
279
|
// Merge user config with defaults
|
|
278
280
|
const mergedConfig = mergeConfig(defaultConfig, userConfig ?? {});
|
|
279
281
|
cachedConfig = mergedConfig;
|
|
282
|
+
cachedConfigVersion = currentVersion;
|
|
280
283
|
}
|
|
281
284
|
return cachedConfig;
|
|
282
285
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACpD,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turbopack-safe loader:
|
|
3
|
+
* - In Next 16 Turbopack SSR, dynamic require(found) fails ("expression is too dynamic")
|
|
4
|
+
* - Use runtime import(file://...) and tell turbopack to ignore analyzing it.
|
|
5
|
+
*/
|
|
6
|
+
export declare const loadConfigModule: () => Promise<unknown>;
|
|
7
|
+
//# sourceMappingURL=loader-with-esbuild.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader-with-esbuild.d.ts","sourceRoot":"","sources":["../../../src/core/config/loader-with-esbuild.ts"],"names":[],"mappings":"AAgFA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAa,OAAO,CAAC,OAAO,CA2BxD,CAAA"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// loader.ts (or config-loader.ts)
|
|
2
|
+
// Turbopack-safe config loader for Next.js 16+
|
|
3
|
+
// - Avoids dynamic require(found) which Turbopack rejects ("expression is too dynamic")
|
|
4
|
+
// - Uses runtime import(file://...) with `turbopackIgnore`
|
|
5
|
+
// - Still supports TS configs via esbuild-register
|
|
6
|
+
// - Keeps your Node CJS resolve patch for tests (js specifier -> ts fallback)
|
|
7
|
+
import { existsSync } from 'fs';
|
|
8
|
+
import { resolve, join } from 'path';
|
|
9
|
+
import { createRequire } from 'module';
|
|
10
|
+
import { pathToFileURL } from 'url';
|
|
11
|
+
// Create a require function that works in ES modules
|
|
12
|
+
const safeRequire = createRequire(import.meta.url);
|
|
13
|
+
/**
|
|
14
|
+
* See SectionFactory.ts: during dev/tests we may runtime-load TS that contains NodeNext-style `./x.js` specifiers
|
|
15
|
+
* pointing at `./x.ts` in the source tree. Patch CJS resolution to fall back to `.ts` for relative imports.
|
|
16
|
+
*/
|
|
17
|
+
const withPatchedCjsResolveJsToTs = (fn) => {
|
|
18
|
+
const NodeModule = safeRequire('module');
|
|
19
|
+
const originalResolve = NodeModule._resolveFilename;
|
|
20
|
+
NodeModule._resolveFilename = function (request, parent, isMain, options) {
|
|
21
|
+
try {
|
|
22
|
+
return originalResolve.call(this, request, parent, isMain, options);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
if (typeof request === 'string' && /^(\.{1,2}\/.*)\.js$/.test(request)) {
|
|
26
|
+
const tsRequest = request.replace(/\.js$/, '.ts');
|
|
27
|
+
try {
|
|
28
|
+
return originalResolve.call(this, tsRequest, parent, isMain, options);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// fall through to rethrow original error
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
try {
|
|
38
|
+
return fn();
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
NodeModule._resolveFilename = originalResolve;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const registerEsbuildForTs = () => {
|
|
45
|
+
try {
|
|
46
|
+
const { register } = safeRequire('esbuild-register/dist/node');
|
|
47
|
+
return register({ format: 'cjs', loader: 'ts' }).unregister;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
throw new Error(`Unable to load TypeScript config. Install 'esbuild-register', or provide cms.config.js/json.`);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const findConfigFile = () => {
|
|
54
|
+
const cwd = process.cwd();
|
|
55
|
+
const candidates = [
|
|
56
|
+
'cms.config.js',
|
|
57
|
+
'cms.config.cjs',
|
|
58
|
+
'cms.config.mjs',
|
|
59
|
+
'cms.config.json',
|
|
60
|
+
'cms.config.ts',
|
|
61
|
+
'cms.config.cts',
|
|
62
|
+
'cms.config.mts',
|
|
63
|
+
].map((f) => resolve(join(cwd, f)));
|
|
64
|
+
const found = candidates.find((p) => existsSync(p));
|
|
65
|
+
if (!found) {
|
|
66
|
+
throw new Error(`Config file not found. Searched: ${candidates.join(', ')}`);
|
|
67
|
+
}
|
|
68
|
+
const isTs = found.endsWith('.ts') || found.endsWith('.cts') || found.endsWith('.mts');
|
|
69
|
+
return { found, isTs };
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Turbopack-safe loader:
|
|
73
|
+
* - In Next 16 Turbopack SSR, dynamic require(found) fails ("expression is too dynamic")
|
|
74
|
+
* - Use runtime import(file://...) and tell turbopack to ignore analyzing it.
|
|
75
|
+
*/
|
|
76
|
+
export const loadConfigModule = async () => {
|
|
77
|
+
const { found, isTs } = findConfigFile();
|
|
78
|
+
let unregister;
|
|
79
|
+
try {
|
|
80
|
+
if (isTs) {
|
|
81
|
+
unregister = registerEsbuildForTs();
|
|
82
|
+
}
|
|
83
|
+
// For tests (NODE_ENV=test), keep your CJS resolver patch behavior
|
|
84
|
+
const importConfig = async () => {
|
|
85
|
+
const url = pathToFileURL(found).href;
|
|
86
|
+
// IMPORTANT: prevent Turbopack from trying to statically analyze the specifier
|
|
87
|
+
const mod = await import(/* turbopackIgnore: true */ url);
|
|
88
|
+
return mod;
|
|
89
|
+
};
|
|
90
|
+
const mod = process.env.NODE_ENV === 'test' && isTs
|
|
91
|
+
? await withPatchedCjsResolveJsToTs(() => importConfig())
|
|
92
|
+
: await importConfig();
|
|
93
|
+
return mod?.default ?? mod;
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
unregister?.();
|
|
97
|
+
}
|
|
98
|
+
};
|