@btst/stack 2.9.3 → 2.10.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.
- package/README.md +12 -0
- package/dist/packages/stack/src/plugins/blog/client/plugin.cjs +22 -11
- package/dist/packages/stack/src/plugins/blog/client/plugin.mjs +22 -11
- package/dist/packages/stack/src/plugins/cms/client/plugin.cjs +71 -39
- package/dist/packages/stack/src/plugins/cms/client/plugin.mjs +71 -39
- package/dist/packages/stack/src/plugins/comments/client/plugin.cjs +62 -2
- package/dist/packages/stack/src/plugins/comments/client/plugin.mjs +63 -3
- package/dist/packages/stack/src/plugins/comments/client/utils.cjs +2 -11
- package/dist/packages/stack/src/plugins/comments/client/utils.mjs +2 -11
- package/dist/packages/stack/src/plugins/comments/error-utils.cjs +15 -0
- package/dist/packages/stack/src/plugins/comments/error-utils.mjs +13 -0
- package/dist/packages/stack/src/plugins/form-builder/client/plugin.cjs +59 -31
- package/dist/packages/stack/src/plugins/form-builder/client/plugin.mjs +59 -31
- package/dist/packages/stack/src/plugins/ui-builder/client/plugin.cjs +52 -25
- package/dist/packages/stack/src/plugins/ui-builder/client/plugin.mjs +53 -26
- package/dist/packages/stack/src/plugins/utils.cjs +6 -0
- package/dist/packages/stack/src/plugins/utils.mjs +5 -1
- package/dist/plugins/blog/api/index.d.cts +2 -2
- package/dist/plugins/blog/api/index.d.mts +2 -2
- package/dist/plugins/blog/api/index.d.ts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
- package/dist/plugins/blog/client/index.d.cts +2 -2
- package/dist/plugins/blog/client/index.d.mts +2 -2
- package/dist/plugins/blog/client/index.d.ts +2 -2
- package/dist/plugins/blog/query-keys.d.cts +2 -2
- package/dist/plugins/blog/query-keys.d.mts +2 -2
- package/dist/plugins/blog/query-keys.d.ts +2 -2
- package/dist/plugins/client/index.cjs +2 -0
- package/dist/plugins/client/index.d.cts +15 -1
- package/dist/plugins/client/index.d.mts +15 -1
- package/dist/plugins/client/index.d.ts +15 -1
- package/dist/plugins/client/index.mjs +1 -1
- package/dist/plugins/comments/client/index.d.cts +5 -0
- package/dist/plugins/comments/client/index.d.mts +5 -0
- package/dist/plugins/comments/client/index.d.ts +5 -0
- package/dist/plugins/comments/query-keys.cjs +4 -4
- package/dist/plugins/comments/query-keys.mjs +1 -1
- package/dist/plugins/kanban/api/index.d.cts +1 -1
- package/dist/plugins/kanban/api/index.d.mts +1 -1
- package/dist/plugins/kanban/api/index.d.ts +1 -1
- package/dist/plugins/kanban/query-keys.d.cts +1 -1
- package/dist/plugins/kanban/query-keys.d.mts +1 -1
- package/dist/plugins/kanban/query-keys.d.ts +1 -1
- package/dist/shared/{stack.IUeyQKrm.d.mts → stack.BSqJrCTM.d.cts} +5 -5
- package/dist/shared/{stack.D7HSzZdG.d.ts → stack.BXxrFL9R.d.ts} +5 -5
- package/dist/shared/{stack.6mEHS2WH.d.mts → stack.DOZ1EXjM.d.mts} +3 -3
- package/dist/shared/{stack.AJTXI7kw.d.cts → stack.DX-tQ93o.d.cts} +3 -3
- package/dist/shared/{stack.DjgpFWq3.d.cts → stack.DzOhpIYM.d.mts} +5 -5
- package/dist/shared/{stack.QYn-Px94.d.ts → stack.VF6FhyZw.d.ts} +3 -3
- package/package.json +1 -1
- package/src/__tests__/client-plugin-ssr-loaders.test.ts +329 -0
- package/src/plugins/blog/client/plugin.tsx +23 -14
- package/src/plugins/client/index.ts +2 -0
- package/src/plugins/cms/client/plugin.tsx +73 -42
- package/src/plugins/comments/client/plugin.tsx +82 -2
- package/src/plugins/comments/client/utils.ts +2 -14
- package/src/plugins/comments/error-utils.ts +17 -0
- package/src/plugins/comments/query-keys.ts +1 -1
- package/src/plugins/form-builder/client/plugin.tsx +59 -35
- package/src/plugins/ui-builder/client/plugin.tsx +57 -27
- package/src/plugins/utils.ts +18 -0
- package/dist/shared/{stack.eq5eg1yt.d.cts → stack.BOokfhZD.d.cts} +13 -13
- package/dist/shared/{stack.BQmuNl5p.d.ts → stack.BWp0hcm9.d.cts} +3 -3
- package/dist/shared/{stack.BQmuNl5p.d.cts → stack.BWp0hcm9.d.mts} +3 -3
- package/dist/shared/{stack.BQmuNl5p.d.mts → stack.BWp0hcm9.d.ts} +3 -3
- package/dist/shared/{stack.CMbX8Q5C.d.ts → stack.BvCR4-9H.d.ts} +13 -13
- package/dist/shared/{stack.Dj04W2c3.d.mts → stack.CWxAl9K3.d.mts} +13 -13
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import { createRoute } from "@btst/yar";
|
|
9
9
|
import type { ComponentType } from "react";
|
|
10
10
|
import type { QueryClient } from "@tanstack/react-query";
|
|
11
|
+
import { createSanitizedSSRLoaderError } from "../../utils";
|
|
11
12
|
import type { CMSApiRouter } from "../api";
|
|
12
13
|
import { createCMSQueryKeys } from "../query-keys";
|
|
13
14
|
|
|
@@ -163,6 +164,12 @@ function createDashboardLoader(config: CMSClientConfig) {
|
|
|
163
164
|
apiBasePath,
|
|
164
165
|
headers,
|
|
165
166
|
};
|
|
167
|
+
const client = createApiClient<CMSApiRouter>({
|
|
168
|
+
baseURL: apiBaseURL,
|
|
169
|
+
basePath: apiBasePath,
|
|
170
|
+
});
|
|
171
|
+
const queries = createCMSQueryKeys(client, headers);
|
|
172
|
+
const typesQuery = queries.cmsTypes.list();
|
|
166
173
|
|
|
167
174
|
try {
|
|
168
175
|
// Before hook - authorization check
|
|
@@ -173,13 +180,7 @@ function createDashboardLoader(config: CMSClientConfig) {
|
|
|
173
180
|
);
|
|
174
181
|
}
|
|
175
182
|
|
|
176
|
-
|
|
177
|
-
baseURL: apiBaseURL,
|
|
178
|
-
basePath: apiBasePath,
|
|
179
|
-
});
|
|
180
|
-
const queries = createCMSQueryKeys(client, headers);
|
|
181
|
-
|
|
182
|
-
await queryClient.prefetchQuery(queries.cmsTypes.list());
|
|
183
|
+
await queryClient.prefetchQuery(typesQuery);
|
|
183
184
|
|
|
184
185
|
// After hook
|
|
185
186
|
if (hooks?.afterLoadDashboard) {
|
|
@@ -187,9 +188,7 @@ function createDashboardLoader(config: CMSClientConfig) {
|
|
|
187
188
|
}
|
|
188
189
|
|
|
189
190
|
// Check if there was an error
|
|
190
|
-
const queryState = queryClient.getQueryState(
|
|
191
|
-
queries.cmsTypes.list().queryKey,
|
|
192
|
-
);
|
|
191
|
+
const queryState = queryClient.getQueryState(typesQuery.queryKey);
|
|
193
192
|
if (queryState?.error && hooks?.onLoadError) {
|
|
194
193
|
const error =
|
|
195
194
|
queryState.error instanceof Error
|
|
@@ -205,6 +204,15 @@ function createDashboardLoader(config: CMSClientConfig) {
|
|
|
205
204
|
"[btst/cms] route.loader() failed — no server running at build time. " +
|
|
206
205
|
"Use myStack.api.cms.prefetchForRoute() for SSG data prefetching.",
|
|
207
206
|
);
|
|
207
|
+
} else {
|
|
208
|
+
const errToStore = createSanitizedSSRLoaderError();
|
|
209
|
+
await queryClient.prefetchQuery({
|
|
210
|
+
queryKey: typesQuery.queryKey,
|
|
211
|
+
queryFn: () => {
|
|
212
|
+
throw errToStore;
|
|
213
|
+
},
|
|
214
|
+
retry: false,
|
|
215
|
+
});
|
|
208
216
|
}
|
|
209
217
|
if (hooks?.onLoadError) {
|
|
210
218
|
await hooks.onLoadError(error as Error, context);
|
|
@@ -231,6 +239,18 @@ function createContentListLoader(typeSlug: string, config: CMSClientConfig) {
|
|
|
231
239
|
apiBasePath,
|
|
232
240
|
headers,
|
|
233
241
|
};
|
|
242
|
+
const client = createApiClient<CMSApiRouter>({
|
|
243
|
+
baseURL: apiBaseURL,
|
|
244
|
+
basePath: apiBasePath,
|
|
245
|
+
});
|
|
246
|
+
const queries = createCMSQueryKeys(client, headers);
|
|
247
|
+
const limit = 20;
|
|
248
|
+
const typesQuery = queries.cmsTypes.list();
|
|
249
|
+
const listQuery = queries.cmsContent.list({
|
|
250
|
+
typeSlug,
|
|
251
|
+
limit,
|
|
252
|
+
offset: 0,
|
|
253
|
+
});
|
|
234
254
|
|
|
235
255
|
try {
|
|
236
256
|
// Before hook - authorization check
|
|
@@ -241,22 +261,10 @@ function createContentListLoader(typeSlug: string, config: CMSClientConfig) {
|
|
|
241
261
|
);
|
|
242
262
|
}
|
|
243
263
|
|
|
244
|
-
const client = createApiClient<CMSApiRouter>({
|
|
245
|
-
baseURL: apiBaseURL,
|
|
246
|
-
basePath: apiBasePath,
|
|
247
|
-
});
|
|
248
|
-
const queries = createCMSQueryKeys(client, headers);
|
|
249
|
-
const limit = 20;
|
|
250
|
-
|
|
251
264
|
// Prefetch content types
|
|
252
|
-
await queryClient.prefetchQuery(
|
|
265
|
+
await queryClient.prefetchQuery(typesQuery);
|
|
253
266
|
|
|
254
267
|
// Prefetch content list using infinite query (matches useSuspenseInfiniteQuery in hooks)
|
|
255
|
-
const listQuery = queries.cmsContent.list({
|
|
256
|
-
typeSlug,
|
|
257
|
-
limit,
|
|
258
|
-
offset: 0,
|
|
259
|
-
});
|
|
260
268
|
await queryClient.prefetchInfiniteQuery({
|
|
261
269
|
queryKey: listQuery.queryKey,
|
|
262
270
|
queryFn: async ({ pageParam = 0 }) => {
|
|
@@ -285,9 +293,7 @@ function createContentListLoader(typeSlug: string, config: CMSClientConfig) {
|
|
|
285
293
|
}
|
|
286
294
|
|
|
287
295
|
// Check if there was an error in either query
|
|
288
|
-
const typesState = queryClient.getQueryState(
|
|
289
|
-
queries.cmsTypes.list().queryKey,
|
|
290
|
-
);
|
|
296
|
+
const typesState = queryClient.getQueryState(typesQuery.queryKey);
|
|
291
297
|
const listState = queryClient.getQueryState(listQuery.queryKey);
|
|
292
298
|
const queryError = typesState?.error || listState?.error;
|
|
293
299
|
if (queryError && hooks?.onLoadError) {
|
|
@@ -305,6 +311,16 @@ function createContentListLoader(typeSlug: string, config: CMSClientConfig) {
|
|
|
305
311
|
"[btst/cms] route.loader() failed — no server running at build time. " +
|
|
306
312
|
"Use myStack.api.cms.prefetchForRoute() for SSG data prefetching.",
|
|
307
313
|
);
|
|
314
|
+
} else {
|
|
315
|
+
const errToStore = createSanitizedSSRLoaderError();
|
|
316
|
+
await queryClient.prefetchInfiniteQuery({
|
|
317
|
+
queryKey: listQuery.queryKey,
|
|
318
|
+
queryFn: () => {
|
|
319
|
+
throw errToStore;
|
|
320
|
+
},
|
|
321
|
+
initialPageParam: 0,
|
|
322
|
+
retry: false,
|
|
323
|
+
});
|
|
308
324
|
}
|
|
309
325
|
if (hooks?.onLoadError) {
|
|
310
326
|
await hooks.onLoadError(error as Error, context);
|
|
@@ -335,6 +351,15 @@ function createContentEditorLoader(
|
|
|
335
351
|
apiBasePath,
|
|
336
352
|
headers,
|
|
337
353
|
};
|
|
354
|
+
const client = createApiClient<CMSApiRouter>({
|
|
355
|
+
baseURL: apiBaseURL,
|
|
356
|
+
basePath: apiBasePath,
|
|
357
|
+
});
|
|
358
|
+
const queries = createCMSQueryKeys(client, headers);
|
|
359
|
+
const typesQuery = queries.cmsTypes.list();
|
|
360
|
+
const detailQuery = id
|
|
361
|
+
? queries.cmsContent.detail(typeSlug, id)
|
|
362
|
+
: undefined;
|
|
338
363
|
|
|
339
364
|
try {
|
|
340
365
|
// Before hook - authorization check
|
|
@@ -345,17 +370,9 @@ function createContentEditorLoader(
|
|
|
345
370
|
);
|
|
346
371
|
}
|
|
347
372
|
|
|
348
|
-
const
|
|
349
|
-
baseURL: apiBaseURL,
|
|
350
|
-
basePath: apiBasePath,
|
|
351
|
-
});
|
|
352
|
-
const queries = createCMSQueryKeys(client, headers);
|
|
353
|
-
|
|
354
|
-
const promises = [queryClient.prefetchQuery(queries.cmsTypes.list())];
|
|
373
|
+
const promises = [queryClient.prefetchQuery(typesQuery)];
|
|
355
374
|
if (id) {
|
|
356
|
-
promises.push(
|
|
357
|
-
queryClient.prefetchQuery(queries.cmsContent.detail(typeSlug, id)),
|
|
358
|
-
);
|
|
375
|
+
promises.push(queryClient.prefetchQuery(detailQuery!));
|
|
359
376
|
}
|
|
360
377
|
await Promise.all(promises);
|
|
361
378
|
|
|
@@ -365,13 +382,9 @@ function createContentEditorLoader(
|
|
|
365
382
|
}
|
|
366
383
|
|
|
367
384
|
// Check if there was an error
|
|
368
|
-
const typesState = queryClient.getQueryState(
|
|
369
|
-
queries.cmsTypes.list().queryKey,
|
|
370
|
-
);
|
|
385
|
+
const typesState = queryClient.getQueryState(typesQuery.queryKey);
|
|
371
386
|
const itemState = id
|
|
372
|
-
? queryClient.getQueryState(
|
|
373
|
-
queries.cmsContent.detail(typeSlug, id).queryKey,
|
|
374
|
-
)
|
|
387
|
+
? queryClient.getQueryState(detailQuery!.queryKey)
|
|
375
388
|
: null;
|
|
376
389
|
const queryError = typesState?.error || itemState?.error;
|
|
377
390
|
if (queryError && hooks?.onLoadError) {
|
|
@@ -389,6 +402,24 @@ function createContentEditorLoader(
|
|
|
389
402
|
"[btst/cms] route.loader() failed — no server running at build time. " +
|
|
390
403
|
"Use myStack.api.cms.prefetchForRoute() for SSG data prefetching.",
|
|
391
404
|
);
|
|
405
|
+
} else {
|
|
406
|
+
const errToStore = createSanitizedSSRLoaderError();
|
|
407
|
+
await queryClient.prefetchQuery({
|
|
408
|
+
queryKey: typesQuery.queryKey,
|
|
409
|
+
queryFn: () => {
|
|
410
|
+
throw errToStore;
|
|
411
|
+
},
|
|
412
|
+
retry: false,
|
|
413
|
+
});
|
|
414
|
+
if (detailQuery) {
|
|
415
|
+
await queryClient.prefetchQuery({
|
|
416
|
+
queryKey: detailQuery.queryKey,
|
|
417
|
+
queryFn: () => {
|
|
418
|
+
throw errToStore;
|
|
419
|
+
},
|
|
420
|
+
retry: false,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
392
423
|
}
|
|
393
424
|
if (hooks?.onLoadError) {
|
|
394
425
|
await hooks.onLoadError(error as Error, context);
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
import { lazy } from "react";
|
|
3
3
|
import {
|
|
4
4
|
defineClientPlugin,
|
|
5
|
+
createApiClient,
|
|
5
6
|
isConnectionError,
|
|
6
7
|
} from "@btst/stack/plugins/client";
|
|
7
8
|
import { createRoute } from "@btst/yar";
|
|
8
9
|
import type { QueryClient } from "@tanstack/react-query";
|
|
10
|
+
import type { CommentsApiRouter } from "../api";
|
|
11
|
+
import { createCommentsQueryKeys } from "../query-keys";
|
|
12
|
+
import { createSanitizedSSRLoaderError } from "../../utils";
|
|
9
13
|
|
|
10
14
|
// Lazy load page components for code splitting
|
|
11
15
|
const ModerationPageComponent = lazy(() =>
|
|
@@ -36,6 +40,11 @@ export interface LoaderContext {
|
|
|
36
40
|
apiBasePath: string;
|
|
37
41
|
/** Optional headers for the request */
|
|
38
42
|
headers?: Headers;
|
|
43
|
+
/**
|
|
44
|
+
* Optional current user ID for SSR loaders that need user-scoped query keys.
|
|
45
|
+
* Hooks (e.g. beforeLoadUserComments) may populate this.
|
|
46
|
+
*/
|
|
47
|
+
currentUserId?: string;
|
|
39
48
|
/** Additional context properties */
|
|
40
49
|
[key: string]: unknown;
|
|
41
50
|
}
|
|
@@ -81,7 +90,7 @@ export interface CommentsClientConfig {
|
|
|
81
90
|
function createModerationLoader(config: CommentsClientConfig) {
|
|
82
91
|
return async () => {
|
|
83
92
|
if (typeof window === "undefined") {
|
|
84
|
-
const { apiBasePath, apiBaseURL, headers, hooks } = config;
|
|
93
|
+
const { queryClient, apiBasePath, apiBaseURL, headers, hooks } = config;
|
|
85
94
|
const context: LoaderContext = {
|
|
86
95
|
path: "/comments/moderation",
|
|
87
96
|
isSSR: true,
|
|
@@ -89,15 +98,43 @@ function createModerationLoader(config: CommentsClientConfig) {
|
|
|
89
98
|
apiBasePath,
|
|
90
99
|
headers,
|
|
91
100
|
};
|
|
101
|
+
const client = createApiClient<CommentsApiRouter>({
|
|
102
|
+
baseURL: apiBaseURL,
|
|
103
|
+
basePath: apiBasePath,
|
|
104
|
+
});
|
|
105
|
+
const queries = createCommentsQueryKeys(client, headers);
|
|
106
|
+
const listQuery = queries.comments.list({
|
|
107
|
+
status: "pending",
|
|
108
|
+
limit: 20,
|
|
109
|
+
offset: 0,
|
|
110
|
+
});
|
|
92
111
|
try {
|
|
93
112
|
if (hooks?.beforeLoadModeration) {
|
|
94
113
|
await hooks.beforeLoadModeration(context);
|
|
95
114
|
}
|
|
115
|
+
await queryClient.prefetchQuery(listQuery);
|
|
116
|
+
const queryState = queryClient.getQueryState(listQuery.queryKey);
|
|
117
|
+
if (queryState?.error && hooks?.onLoadError) {
|
|
118
|
+
const error =
|
|
119
|
+
queryState.error instanceof Error
|
|
120
|
+
? queryState.error
|
|
121
|
+
: new Error(String(queryState.error));
|
|
122
|
+
await hooks.onLoadError(error, context);
|
|
123
|
+
}
|
|
96
124
|
} catch (error) {
|
|
97
125
|
if (isConnectionError(error)) {
|
|
98
126
|
console.warn(
|
|
99
127
|
"[btst/comments] route.loader() failed — no server running at build time.",
|
|
100
128
|
);
|
|
129
|
+
} else {
|
|
130
|
+
const errToStore = createSanitizedSSRLoaderError();
|
|
131
|
+
await queryClient.prefetchQuery({
|
|
132
|
+
queryKey: listQuery.queryKey,
|
|
133
|
+
queryFn: () => {
|
|
134
|
+
throw errToStore;
|
|
135
|
+
},
|
|
136
|
+
retry: false,
|
|
137
|
+
});
|
|
101
138
|
}
|
|
102
139
|
if (hooks?.onLoadError) {
|
|
103
140
|
await hooks.onLoadError(error as Error, context);
|
|
@@ -110,7 +147,7 @@ function createModerationLoader(config: CommentsClientConfig) {
|
|
|
110
147
|
function createUserCommentsLoader(config: CommentsClientConfig) {
|
|
111
148
|
return async () => {
|
|
112
149
|
if (typeof window === "undefined") {
|
|
113
|
-
const { apiBasePath, apiBaseURL, headers, hooks } = config;
|
|
150
|
+
const { queryClient, apiBasePath, apiBaseURL, headers, hooks } = config;
|
|
114
151
|
const context: LoaderContext = {
|
|
115
152
|
path: "/comments",
|
|
116
153
|
isSSR: true,
|
|
@@ -118,15 +155,58 @@ function createUserCommentsLoader(config: CommentsClientConfig) {
|
|
|
118
155
|
apiBasePath,
|
|
119
156
|
headers,
|
|
120
157
|
};
|
|
158
|
+
const client = createApiClient<CommentsApiRouter>({
|
|
159
|
+
baseURL: apiBaseURL,
|
|
160
|
+
basePath: apiBasePath,
|
|
161
|
+
});
|
|
162
|
+
const queries = createCommentsQueryKeys(client, headers);
|
|
163
|
+
const getUserListQuery = (currentUserId: string) =>
|
|
164
|
+
queries.comments.list({
|
|
165
|
+
authorId: currentUserId,
|
|
166
|
+
sort: "desc",
|
|
167
|
+
limit: 20,
|
|
168
|
+
offset: 0,
|
|
169
|
+
});
|
|
121
170
|
try {
|
|
122
171
|
if (hooks?.beforeLoadUserComments) {
|
|
123
172
|
await hooks.beforeLoadUserComments(context);
|
|
124
173
|
}
|
|
174
|
+
const currentUserId =
|
|
175
|
+
typeof context.currentUserId === "string"
|
|
176
|
+
? context.currentUserId
|
|
177
|
+
: undefined;
|
|
178
|
+
if (currentUserId) {
|
|
179
|
+
const listQuery = getUserListQuery(currentUserId);
|
|
180
|
+
await queryClient.prefetchQuery(listQuery);
|
|
181
|
+
const queryState = queryClient.getQueryState(listQuery.queryKey);
|
|
182
|
+
if (queryState?.error && hooks?.onLoadError) {
|
|
183
|
+
const error =
|
|
184
|
+
queryState.error instanceof Error
|
|
185
|
+
? queryState.error
|
|
186
|
+
: new Error(String(queryState.error));
|
|
187
|
+
await hooks.onLoadError(error, context);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
125
190
|
} catch (error) {
|
|
126
191
|
if (isConnectionError(error)) {
|
|
127
192
|
console.warn(
|
|
128
193
|
"[btst/comments] route.loader() failed — no server running at build time.",
|
|
129
194
|
);
|
|
195
|
+
} else {
|
|
196
|
+
const currentUserId =
|
|
197
|
+
typeof context.currentUserId === "string"
|
|
198
|
+
? context.currentUserId
|
|
199
|
+
: undefined;
|
|
200
|
+
if (currentUserId) {
|
|
201
|
+
const errToStore = createSanitizedSSRLoaderError();
|
|
202
|
+
await queryClient.prefetchQuery({
|
|
203
|
+
queryKey: getUserListQuery(currentUserId).queryKey,
|
|
204
|
+
queryFn: () => {
|
|
205
|
+
throw errToStore;
|
|
206
|
+
},
|
|
207
|
+
retry: false,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
130
210
|
}
|
|
131
211
|
if (hooks?.onLoadError) {
|
|
132
212
|
await hooks.onLoadError(error as Error, context);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect } from "react";
|
|
2
2
|
import type { CommentsPluginOverrides } from "./overrides";
|
|
3
|
+
import { toError as toErrorShared } from "../error-utils";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Resolves `currentUserId` from the plugin overrides, supporting both a static
|
|
@@ -40,20 +41,7 @@ export function useResolvedCurrentUserId(
|
|
|
40
41
|
* copied onto the Error via Object.assign so callers can inspect them.
|
|
41
42
|
* 3. Anything else — converted via String().
|
|
42
43
|
*/
|
|
43
|
-
export
|
|
44
|
-
if (error instanceof Error) return error;
|
|
45
|
-
if (typeof error === "object" && error !== null) {
|
|
46
|
-
const obj = error as Record<string, unknown>;
|
|
47
|
-
const message =
|
|
48
|
-
(typeof obj.message === "string" ? obj.message : null) ||
|
|
49
|
-
(typeof obj.error === "string" ? obj.error : null) ||
|
|
50
|
-
JSON.stringify(error);
|
|
51
|
-
const err = new Error(message);
|
|
52
|
-
Object.assign(err, error);
|
|
53
|
-
return err;
|
|
54
|
-
}
|
|
55
|
-
return new Error(String(error));
|
|
56
|
-
}
|
|
44
|
+
export const toError = toErrorShared;
|
|
57
45
|
|
|
58
46
|
export function getInitials(name: string | null | undefined): string {
|
|
59
47
|
if (!name) return "?";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize any thrown value into an Error.
|
|
3
|
+
*/
|
|
4
|
+
export function toError(error: unknown): Error {
|
|
5
|
+
if (error instanceof Error) return error;
|
|
6
|
+
if (typeof error === "object" && error !== null) {
|
|
7
|
+
const obj = error as Record<string, unknown>;
|
|
8
|
+
const message =
|
|
9
|
+
(typeof obj.message === "string" ? obj.message : null) ||
|
|
10
|
+
(typeof obj.error === "string" ? obj.error : null) ||
|
|
11
|
+
JSON.stringify(error);
|
|
12
|
+
const err = new Error(message);
|
|
13
|
+
Object.assign(err, error);
|
|
14
|
+
return err;
|
|
15
|
+
}
|
|
16
|
+
return new Error(String(error));
|
|
17
|
+
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { createRoute } from "@btst/yar";
|
|
10
10
|
import type { ComponentType } from "react";
|
|
11
11
|
import type { QueryClient } from "@tanstack/react-query";
|
|
12
|
+
import { createSanitizedSSRLoaderError } from "../../utils";
|
|
12
13
|
import type { FormBuilderApiRouter } from "../api";
|
|
13
14
|
import { createFormBuilderQueryKeys } from "../query-keys";
|
|
14
15
|
|
|
@@ -160,6 +161,13 @@ function createFormListLoader(config: FormBuilderClientConfig) {
|
|
|
160
161
|
apiBasePath,
|
|
161
162
|
headers,
|
|
162
163
|
};
|
|
164
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
165
|
+
baseURL: apiBaseURL,
|
|
166
|
+
basePath: apiBasePath,
|
|
167
|
+
});
|
|
168
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
169
|
+
const limit = 20;
|
|
170
|
+
const listQuery = queries.forms.list({ limit, offset: 0 });
|
|
163
171
|
|
|
164
172
|
try {
|
|
165
173
|
// Before hook - authorization check
|
|
@@ -170,15 +178,7 @@ function createFormListLoader(config: FormBuilderClientConfig) {
|
|
|
170
178
|
);
|
|
171
179
|
}
|
|
172
180
|
|
|
173
|
-
const client = createApiClient<FormBuilderApiRouter>({
|
|
174
|
-
baseURL: apiBaseURL,
|
|
175
|
-
basePath: apiBasePath,
|
|
176
|
-
});
|
|
177
|
-
const queries = createFormBuilderQueryKeys(client, headers);
|
|
178
|
-
const limit = 20;
|
|
179
|
-
|
|
180
181
|
// Prefetch forms using infinite query
|
|
181
|
-
const listQuery = queries.forms.list({ limit, offset: 0 });
|
|
182
182
|
await queryClient.prefetchInfiniteQuery({
|
|
183
183
|
queryKey: listQuery.queryKey,
|
|
184
184
|
queryFn: async ({ pageParam = 0 }) => {
|
|
@@ -221,6 +221,16 @@ function createFormListLoader(config: FormBuilderClientConfig) {
|
|
|
221
221
|
"[btst/form-builder] route.loader() failed — no server running at build time. " +
|
|
222
222
|
"Use myStack.api.formBuilder.prefetchForRoute() for SSG data prefetching.",
|
|
223
223
|
);
|
|
224
|
+
} else {
|
|
225
|
+
const errToStore = createSanitizedSSRLoaderError();
|
|
226
|
+
await queryClient.prefetchInfiniteQuery({
|
|
227
|
+
queryKey: listQuery.queryKey,
|
|
228
|
+
queryFn: () => {
|
|
229
|
+
throw errToStore;
|
|
230
|
+
},
|
|
231
|
+
initialPageParam: 0,
|
|
232
|
+
retry: false,
|
|
233
|
+
});
|
|
224
234
|
}
|
|
225
235
|
if (hooks?.onLoadError) {
|
|
226
236
|
await hooks.onLoadError(error as Error, context);
|
|
@@ -249,6 +259,12 @@ function createFormBuilderLoader(
|
|
|
249
259
|
apiBasePath,
|
|
250
260
|
headers,
|
|
251
261
|
};
|
|
262
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
263
|
+
baseURL: apiBaseURL,
|
|
264
|
+
basePath: apiBasePath,
|
|
265
|
+
});
|
|
266
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
267
|
+
const formQuery = id ? queries.forms.byId(id) : undefined;
|
|
252
268
|
|
|
253
269
|
try {
|
|
254
270
|
// Before hook - authorization check
|
|
@@ -259,15 +275,9 @@ function createFormBuilderLoader(
|
|
|
259
275
|
);
|
|
260
276
|
}
|
|
261
277
|
|
|
262
|
-
const client = createApiClient<FormBuilderApiRouter>({
|
|
263
|
-
baseURL: apiBaseURL,
|
|
264
|
-
basePath: apiBasePath,
|
|
265
|
-
});
|
|
266
|
-
const queries = createFormBuilderQueryKeys(client, headers);
|
|
267
|
-
|
|
268
278
|
// Prefetch form if editing
|
|
269
279
|
if (id) {
|
|
270
|
-
await queryClient.prefetchQuery(
|
|
280
|
+
await queryClient.prefetchQuery(formQuery!);
|
|
271
281
|
}
|
|
272
282
|
|
|
273
283
|
// After hook
|
|
@@ -277,9 +287,7 @@ function createFormBuilderLoader(
|
|
|
277
287
|
|
|
278
288
|
// Check if there was an error
|
|
279
289
|
if (id) {
|
|
280
|
-
const queryState = queryClient.getQueryState(
|
|
281
|
-
queries.forms.byId(id).queryKey,
|
|
282
|
-
);
|
|
290
|
+
const queryState = queryClient.getQueryState(formQuery!.queryKey);
|
|
283
291
|
if (queryState?.error && hooks?.onLoadError) {
|
|
284
292
|
const error =
|
|
285
293
|
queryState.error instanceof Error
|
|
@@ -295,6 +303,15 @@ function createFormBuilderLoader(
|
|
|
295
303
|
"[btst/form-builder] route.loader() failed — no server running at build time. " +
|
|
296
304
|
"Use myStack.api.formBuilder.prefetchForRoute() for SSG data prefetching.",
|
|
297
305
|
);
|
|
306
|
+
} else if (formQuery) {
|
|
307
|
+
const errToStore = createSanitizedSSRLoaderError();
|
|
308
|
+
await queryClient.prefetchQuery({
|
|
309
|
+
queryKey: formQuery.queryKey,
|
|
310
|
+
queryFn: () => {
|
|
311
|
+
throw errToStore;
|
|
312
|
+
},
|
|
313
|
+
retry: false,
|
|
314
|
+
});
|
|
298
315
|
}
|
|
299
316
|
if (hooks?.onLoadError) {
|
|
300
317
|
await hooks.onLoadError(error as Error, context);
|
|
@@ -323,6 +340,18 @@ function createSubmissionsLoader(
|
|
|
323
340
|
apiBasePath,
|
|
324
341
|
headers,
|
|
325
342
|
};
|
|
343
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
344
|
+
baseURL: apiBaseURL,
|
|
345
|
+
basePath: apiBasePath,
|
|
346
|
+
});
|
|
347
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
348
|
+
const limit = 20;
|
|
349
|
+
const formQuery = queries.forms.byId(formId);
|
|
350
|
+
const submissionsQuery = queries.formSubmissions.list({
|
|
351
|
+
formId,
|
|
352
|
+
limit,
|
|
353
|
+
offset: 0,
|
|
354
|
+
});
|
|
326
355
|
|
|
327
356
|
try {
|
|
328
357
|
// Before hook - authorization check
|
|
@@ -333,21 +362,8 @@ function createSubmissionsLoader(
|
|
|
333
362
|
);
|
|
334
363
|
}
|
|
335
364
|
|
|
336
|
-
const client = createApiClient<FormBuilderApiRouter>({
|
|
337
|
-
baseURL: apiBaseURL,
|
|
338
|
-
basePath: apiBasePath,
|
|
339
|
-
});
|
|
340
|
-
const queries = createFormBuilderQueryKeys(client, headers);
|
|
341
|
-
const limit = 20;
|
|
342
|
-
|
|
343
365
|
// Prefetch form and submissions
|
|
344
|
-
await queryClient.prefetchQuery(
|
|
345
|
-
|
|
346
|
-
const submissionsQuery = queries.formSubmissions.list({
|
|
347
|
-
formId,
|
|
348
|
-
limit,
|
|
349
|
-
offset: 0,
|
|
350
|
-
});
|
|
366
|
+
await queryClient.prefetchQuery(formQuery);
|
|
351
367
|
await queryClient.prefetchInfiniteQuery({
|
|
352
368
|
queryKey: submissionsQuery.queryKey,
|
|
353
369
|
queryFn: async ({ pageParam = 0 }) => {
|
|
@@ -379,9 +395,7 @@ function createSubmissionsLoader(
|
|
|
379
395
|
}
|
|
380
396
|
|
|
381
397
|
// Check if there was an error
|
|
382
|
-
const formState = queryClient.getQueryState(
|
|
383
|
-
queries.forms.byId(formId).queryKey,
|
|
384
|
-
);
|
|
398
|
+
const formState = queryClient.getQueryState(formQuery.queryKey);
|
|
385
399
|
const submissionsState = queryClient.getQueryState(
|
|
386
400
|
submissionsQuery.queryKey,
|
|
387
401
|
);
|
|
@@ -400,6 +414,16 @@ function createSubmissionsLoader(
|
|
|
400
414
|
"[btst/form-builder] route.loader() failed — no server running at build time. " +
|
|
401
415
|
"Use myStack.api.formBuilder.prefetchForRoute() for SSG data prefetching.",
|
|
402
416
|
);
|
|
417
|
+
} else {
|
|
418
|
+
const errToStore = createSanitizedSSRLoaderError();
|
|
419
|
+
await queryClient.prefetchInfiniteQuery({
|
|
420
|
+
queryKey: submissionsQuery.queryKey,
|
|
421
|
+
queryFn: () => {
|
|
422
|
+
throw errToStore;
|
|
423
|
+
},
|
|
424
|
+
initialPageParam: 0,
|
|
425
|
+
retry: false,
|
|
426
|
+
});
|
|
403
427
|
}
|
|
404
428
|
if (hooks?.onLoadError) {
|
|
405
429
|
await hooks.onLoadError(error as Error, context);
|