@btst/stack 2.9.2 → 2.9.4

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.
Files changed (42) hide show
  1. package/dist/packages/stack/src/plugins/blog/client/plugin.cjs +22 -11
  2. package/dist/packages/stack/src/plugins/blog/client/plugin.mjs +22 -11
  3. package/dist/packages/stack/src/plugins/cms/client/plugin.cjs +71 -39
  4. package/dist/packages/stack/src/plugins/cms/client/plugin.mjs +71 -39
  5. package/dist/packages/stack/src/plugins/comments/client/plugin.cjs +62 -2
  6. package/dist/packages/stack/src/plugins/comments/client/plugin.mjs +63 -3
  7. package/dist/packages/stack/src/plugins/comments/client/utils.cjs +2 -11
  8. package/dist/packages/stack/src/plugins/comments/client/utils.mjs +2 -11
  9. package/dist/packages/stack/src/plugins/comments/error-utils.cjs +15 -0
  10. package/dist/packages/stack/src/plugins/comments/error-utils.mjs +13 -0
  11. package/dist/packages/stack/src/plugins/form-builder/client/plugin.cjs +59 -31
  12. package/dist/packages/stack/src/plugins/form-builder/client/plugin.mjs +59 -31
  13. package/dist/packages/stack/src/plugins/media/db.cjs +10 -2
  14. package/dist/packages/stack/src/plugins/media/db.mjs +10 -2
  15. package/dist/packages/stack/src/plugins/ui-builder/client/plugin.cjs +52 -25
  16. package/dist/packages/stack/src/plugins/ui-builder/client/plugin.mjs +53 -26
  17. package/dist/packages/stack/src/plugins/utils.cjs +6 -0
  18. package/dist/packages/stack/src/plugins/utils.mjs +5 -1
  19. package/dist/plugins/client/index.cjs +2 -0
  20. package/dist/plugins/client/index.d.cts +15 -1
  21. package/dist/plugins/client/index.d.mts +15 -1
  22. package/dist/plugins/client/index.d.ts +15 -1
  23. package/dist/plugins/client/index.mjs +1 -1
  24. package/dist/plugins/comments/client/index.d.cts +5 -0
  25. package/dist/plugins/comments/client/index.d.mts +5 -0
  26. package/dist/plugins/comments/client/index.d.ts +5 -0
  27. package/dist/plugins/comments/query-keys.cjs +4 -4
  28. package/dist/plugins/comments/query-keys.mjs +1 -1
  29. package/package.json +1 -1
  30. package/src/__tests__/client-plugin-ssr-loaders.test.ts +329 -0
  31. package/src/plugins/blog/client/plugin.tsx +23 -14
  32. package/src/plugins/client/index.ts +2 -0
  33. package/src/plugins/cms/client/plugin.tsx +73 -42
  34. package/src/plugins/comments/client/plugin.tsx +82 -2
  35. package/src/plugins/comments/client/utils.ts +2 -14
  36. package/src/plugins/comments/error-utils.ts +17 -0
  37. package/src/plugins/comments/query-keys.ts +1 -1
  38. package/src/plugins/form-builder/client/plugin.tsx +59 -35
  39. package/src/plugins/media/__tests__/plugin.test.ts +4 -4
  40. package/src/plugins/media/db.ts +8 -0
  41. package/src/plugins/ui-builder/client/plugin.tsx +57 -27
  42. package/src/plugins/utils.ts +18 -0
@@ -5,6 +5,7 @@ const React = require('react');
5
5
  const client = require('@btst/stack/plugins/client');
6
6
  const yar = require('@btst/yar');
7
7
  const plugins_cms_queryKeys = require('../../../../../../plugins/cms/query-keys.cjs');
8
+ const utils = require('../../utils.cjs');
8
9
  const schemas = require('../schemas.cjs');
9
10
 
10
11
  const PageListPageComponent = React.lazy(
@@ -29,6 +30,18 @@ function createPageListLoader(config) {
29
30
  apiBasePath,
30
31
  headers
31
32
  };
33
+ const client$1 = client.createApiClient({
34
+ baseURL: apiBaseURL,
35
+ basePath: apiBasePath
36
+ });
37
+ const queries = plugins_cms_queryKeys.createCMSQueryKeys(client$1, headers);
38
+ const limit = 20;
39
+ const listQuery = queries.cmsContent.list({
40
+ typeSlug,
41
+ limit,
42
+ offset: 0
43
+ });
44
+ const uiBuilderListQueryKey = [...listQuery.queryKey, "ui-builder"];
32
45
  try {
33
46
  if (hooks?.beforeLoadPageList) {
34
47
  await client.runClientHookWithShim(
@@ -36,19 +49,8 @@ function createPageListLoader(config) {
36
49
  "Load prevented by beforeLoadPageList hook"
37
50
  );
38
51
  }
39
- const client$1 = client.createApiClient({
40
- baseURL: apiBaseURL,
41
- basePath: apiBasePath
42
- });
43
- const queries = plugins_cms_queryKeys.createCMSQueryKeys(client$1, headers);
44
- const limit = 20;
45
- const listQuery = queries.cmsContent.list({
46
- typeSlug,
47
- limit,
48
- offset: 0
49
- });
50
52
  await queryClient.prefetchInfiniteQuery({
51
- queryKey: [...listQuery.queryKey, "ui-builder"],
53
+ queryKey: uiBuilderListQueryKey,
52
54
  queryFn: async ({ pageParam = 0 }) => {
53
55
  const response = await client$1("/content/:typeSlug", {
54
56
  method: "GET",
@@ -67,14 +69,28 @@ function createPageListLoader(config) {
67
69
  await hooks.afterLoadPageList(context);
68
70
  }
69
71
  const queryState = queryClient.getQueryState([
70
- ...listQuery.queryKey,
71
- "ui-builder"
72
+ ...uiBuilderListQueryKey
72
73
  ]);
73
74
  if (queryState?.error && hooks?.onLoadError) {
74
75
  const error = queryState.error instanceof Error ? queryState.error : new Error(String(queryState.error));
75
76
  await hooks.onLoadError(error, context);
76
77
  }
77
78
  } catch (error) {
79
+ if (client.isConnectionError(error)) {
80
+ console.warn(
81
+ "[btst/ui-builder] route.loader() failed \u2014 no server running at build time. Use myStack.api.uiBuilder.prefetchForRoute() for SSG data prefetching."
82
+ );
83
+ } else {
84
+ const errToStore = utils.createSanitizedSSRLoaderError();
85
+ await queryClient.prefetchInfiniteQuery({
86
+ queryKey: uiBuilderListQueryKey,
87
+ queryFn: () => {
88
+ throw errToStore;
89
+ },
90
+ initialPageParam: 0,
91
+ retry: false
92
+ });
93
+ }
78
94
  if (hooks?.onLoadError) {
79
95
  await hooks.onLoadError(error, context);
80
96
  }
@@ -95,6 +111,12 @@ function createPageBuilderLoader(id, config) {
95
111
  apiBasePath,
96
112
  headers
97
113
  };
114
+ const client$1 = client.createApiClient({
115
+ baseURL: apiBaseURL,
116
+ basePath: apiBasePath
117
+ });
118
+ const queries = plugins_cms_queryKeys.createCMSQueryKeys(client$1, headers);
119
+ const pageQuery = id ? queries.cmsContent.detail(typeSlug, id) : void 0;
98
120
  try {
99
121
  if (hooks?.beforeLoadPageBuilder) {
100
122
  await client.runClientHookWithShim(
@@ -102,29 +124,34 @@ function createPageBuilderLoader(id, config) {
102
124
  "Load prevented by beforeLoadPageBuilder hook"
103
125
  );
104
126
  }
105
- const client$1 = client.createApiClient({
106
- baseURL: apiBaseURL,
107
- basePath: apiBasePath
108
- });
109
- const queries = plugins_cms_queryKeys.createCMSQueryKeys(client$1, headers);
110
127
  if (id) {
111
- await queryClient.prefetchQuery(
112
- queries.cmsContent.detail(typeSlug, id)
113
- );
128
+ await queryClient.prefetchQuery(pageQuery);
114
129
  }
115
130
  if (hooks?.afterLoadPageBuilder) {
116
131
  await hooks.afterLoadPageBuilder(id, context);
117
132
  }
118
133
  if (id) {
119
- const queryState = queryClient.getQueryState(
120
- queries.cmsContent.detail(typeSlug, id).queryKey
121
- );
134
+ const queryState = queryClient.getQueryState(pageQuery.queryKey);
122
135
  if (queryState?.error && hooks?.onLoadError) {
123
136
  const error = queryState.error instanceof Error ? queryState.error : new Error(String(queryState.error));
124
137
  await hooks.onLoadError(error, context);
125
138
  }
126
139
  }
127
140
  } catch (error) {
141
+ if (client.isConnectionError(error)) {
142
+ console.warn(
143
+ "[btst/ui-builder] route.loader() failed \u2014 no server running at build time. Use myStack.api.uiBuilder.prefetchForRoute() for SSG data prefetching."
144
+ );
145
+ } else if (pageQuery) {
146
+ const errToStore = utils.createSanitizedSSRLoaderError();
147
+ await queryClient.prefetchQuery({
148
+ queryKey: pageQuery.queryKey,
149
+ queryFn: () => {
150
+ throw errToStore;
151
+ },
152
+ retry: false
153
+ });
154
+ }
128
155
  if (hooks?.onLoadError) {
129
156
  await hooks.onLoadError(error, context);
130
157
  }
@@ -1,8 +1,9 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { lazy } from 'react';
3
- import { defineClientPlugin, createApiClient, runClientHookWithShim } from '@btst/stack/plugins/client';
3
+ import { defineClientPlugin, createApiClient, runClientHookWithShim, isConnectionError } from '@btst/stack/plugins/client';
4
4
  import { createRoute } from '@btst/yar';
5
5
  import { createCMSQueryKeys } from '../../../../../../plugins/cms/query-keys.mjs';
6
+ import { createSanitizedSSRLoaderError } from '../../utils.mjs';
6
7
  import { UI_BUILDER_TYPE_SLUG } from '../schemas.mjs';
7
8
 
8
9
  const PageListPageComponent = lazy(
@@ -27,6 +28,18 @@ function createPageListLoader(config) {
27
28
  apiBasePath,
28
29
  headers
29
30
  };
31
+ const client = createApiClient({
32
+ baseURL: apiBaseURL,
33
+ basePath: apiBasePath
34
+ });
35
+ const queries = createCMSQueryKeys(client, headers);
36
+ const limit = 20;
37
+ const listQuery = queries.cmsContent.list({
38
+ typeSlug,
39
+ limit,
40
+ offset: 0
41
+ });
42
+ const uiBuilderListQueryKey = [...listQuery.queryKey, "ui-builder"];
30
43
  try {
31
44
  if (hooks?.beforeLoadPageList) {
32
45
  await runClientHookWithShim(
@@ -34,19 +47,8 @@ function createPageListLoader(config) {
34
47
  "Load prevented by beforeLoadPageList hook"
35
48
  );
36
49
  }
37
- const client = createApiClient({
38
- baseURL: apiBaseURL,
39
- basePath: apiBasePath
40
- });
41
- const queries = createCMSQueryKeys(client, headers);
42
- const limit = 20;
43
- const listQuery = queries.cmsContent.list({
44
- typeSlug,
45
- limit,
46
- offset: 0
47
- });
48
50
  await queryClient.prefetchInfiniteQuery({
49
- queryKey: [...listQuery.queryKey, "ui-builder"],
51
+ queryKey: uiBuilderListQueryKey,
50
52
  queryFn: async ({ pageParam = 0 }) => {
51
53
  const response = await client("/content/:typeSlug", {
52
54
  method: "GET",
@@ -65,14 +67,28 @@ function createPageListLoader(config) {
65
67
  await hooks.afterLoadPageList(context);
66
68
  }
67
69
  const queryState = queryClient.getQueryState([
68
- ...listQuery.queryKey,
69
- "ui-builder"
70
+ ...uiBuilderListQueryKey
70
71
  ]);
71
72
  if (queryState?.error && hooks?.onLoadError) {
72
73
  const error = queryState.error instanceof Error ? queryState.error : new Error(String(queryState.error));
73
74
  await hooks.onLoadError(error, context);
74
75
  }
75
76
  } catch (error) {
77
+ if (isConnectionError(error)) {
78
+ console.warn(
79
+ "[btst/ui-builder] route.loader() failed \u2014 no server running at build time. Use myStack.api.uiBuilder.prefetchForRoute() for SSG data prefetching."
80
+ );
81
+ } else {
82
+ const errToStore = createSanitizedSSRLoaderError();
83
+ await queryClient.prefetchInfiniteQuery({
84
+ queryKey: uiBuilderListQueryKey,
85
+ queryFn: () => {
86
+ throw errToStore;
87
+ },
88
+ initialPageParam: 0,
89
+ retry: false
90
+ });
91
+ }
76
92
  if (hooks?.onLoadError) {
77
93
  await hooks.onLoadError(error, context);
78
94
  }
@@ -93,6 +109,12 @@ function createPageBuilderLoader(id, config) {
93
109
  apiBasePath,
94
110
  headers
95
111
  };
112
+ const client = createApiClient({
113
+ baseURL: apiBaseURL,
114
+ basePath: apiBasePath
115
+ });
116
+ const queries = createCMSQueryKeys(client, headers);
117
+ const pageQuery = id ? queries.cmsContent.detail(typeSlug, id) : void 0;
96
118
  try {
97
119
  if (hooks?.beforeLoadPageBuilder) {
98
120
  await runClientHookWithShim(
@@ -100,29 +122,34 @@ function createPageBuilderLoader(id, config) {
100
122
  "Load prevented by beforeLoadPageBuilder hook"
101
123
  );
102
124
  }
103
- const client = createApiClient({
104
- baseURL: apiBaseURL,
105
- basePath: apiBasePath
106
- });
107
- const queries = createCMSQueryKeys(client, headers);
108
125
  if (id) {
109
- await queryClient.prefetchQuery(
110
- queries.cmsContent.detail(typeSlug, id)
111
- );
126
+ await queryClient.prefetchQuery(pageQuery);
112
127
  }
113
128
  if (hooks?.afterLoadPageBuilder) {
114
129
  await hooks.afterLoadPageBuilder(id, context);
115
130
  }
116
131
  if (id) {
117
- const queryState = queryClient.getQueryState(
118
- queries.cmsContent.detail(typeSlug, id).queryKey
119
- );
132
+ const queryState = queryClient.getQueryState(pageQuery.queryKey);
120
133
  if (queryState?.error && hooks?.onLoadError) {
121
134
  const error = queryState.error instanceof Error ? queryState.error : new Error(String(queryState.error));
122
135
  await hooks.onLoadError(error, context);
123
136
  }
124
137
  }
125
138
  } catch (error) {
139
+ if (isConnectionError(error)) {
140
+ console.warn(
141
+ "[btst/ui-builder] route.loader() failed \u2014 no server running at build time. Use myStack.api.uiBuilder.prefetchForRoute() for SSG data prefetching."
142
+ );
143
+ } else if (pageQuery) {
144
+ const errToStore = createSanitizedSSRLoaderError();
145
+ await queryClient.prefetchQuery({
146
+ queryKey: pageQuery.queryKey,
147
+ queryFn: () => {
148
+ throw errToStore;
149
+ },
150
+ retry: false
151
+ });
152
+ }
126
153
  if (hooks?.onLoadError) {
127
154
  await hooks.onLoadError(error, context);
128
155
  }
@@ -47,6 +47,10 @@ function isConnectionError(err) {
47
47
  const code = err.cause?.code ?? err.code;
48
48
  return err.message.includes("ECONNREFUSED") || err.message.includes("fetch failed") || err.message.includes("ERR_CONNECTION_REFUSED") || code === "ECONNREFUSED" || code === "ERR_CONNECTION_REFUSED";
49
49
  }
50
+ const SSR_LOADER_ERROR_MESSAGE = "Failed to load data.";
51
+ function createSanitizedSSRLoaderError() {
52
+ return new Error(SSR_LOADER_ERROR_MESSAGE);
53
+ }
50
54
  function createApiClient(options) {
51
55
  const { baseURL = "", basePath = "/" } = options ?? {};
52
56
  const normalizedBaseURL = baseURL ? baseURL.replace(/\/$/, "") : "";
@@ -58,7 +62,9 @@ function createApiClient(options) {
58
62
  });
59
63
  }
60
64
 
65
+ exports.SSR_LOADER_ERROR_MESSAGE = SSR_LOADER_ERROR_MESSAGE;
61
66
  exports.createApiClient = createApiClient;
67
+ exports.createSanitizedSSRLoaderError = createSanitizedSSRLoaderError;
62
68
  exports.isConnectionError = isConnectionError;
63
69
  exports.runClientHookWithShim = runClientHookWithShim;
64
70
  exports.runHookWithShim = runHookWithShim;
@@ -45,6 +45,10 @@ function isConnectionError(err) {
45
45
  const code = err.cause?.code ?? err.code;
46
46
  return err.message.includes("ECONNREFUSED") || err.message.includes("fetch failed") || err.message.includes("ERR_CONNECTION_REFUSED") || code === "ECONNREFUSED" || code === "ERR_CONNECTION_REFUSED";
47
47
  }
48
+ const SSR_LOADER_ERROR_MESSAGE = "Failed to load data.";
49
+ function createSanitizedSSRLoaderError() {
50
+ return new Error(SSR_LOADER_ERROR_MESSAGE);
51
+ }
48
52
  function createApiClient(options) {
49
53
  const { baseURL = "", basePath = "/" } = options ?? {};
50
54
  const normalizedBaseURL = baseURL ? baseURL.replace(/\/$/, "") : "";
@@ -56,4 +60,4 @@ function createApiClient(options) {
56
60
  });
57
61
  }
58
62
 
59
- export { createApiClient, isConnectionError, runClientHookWithShim, runHookWithShim };
63
+ export { SSR_LOADER_ERROR_MESSAGE, createApiClient, createSanitizedSSRLoaderError, isConnectionError, runClientHookWithShim, runHookWithShim };
@@ -8,7 +8,9 @@ function defineClientPlugin(plugin) {
8
8
  return plugin;
9
9
  }
10
10
 
11
+ exports.SSR_LOADER_ERROR_MESSAGE = utils.SSR_LOADER_ERROR_MESSAGE;
11
12
  exports.createApiClient = utils.createApiClient;
13
+ exports.createSanitizedSSRLoaderError = utils.createSanitizedSSRLoaderError;
12
14
  exports.isConnectionError = utils.isConnectionError;
13
15
  exports.runClientHookWithShim = utils.runClientHookWithShim;
14
16
  exports.createRoute = yar.createRoute;
@@ -20,6 +20,20 @@ declare function runClientHookWithShim<T>(hookFn: () => Promise<T> | T, defaultM
20
20
  * `route.loader()` is called during `next build` with no HTTP server running.
21
21
  */
22
22
  declare function isConnectionError(err: unknown): boolean;
23
+ /**
24
+ * Public-safe message used when SSR loader failures are intentionally seeded
25
+ * into React Query so Error Boundaries can render on the client.
26
+ *
27
+ * Never include raw server error text here because dehydrated query state can
28
+ * be serialized into HTML.
29
+ */
30
+ declare const SSR_LOADER_ERROR_MESSAGE = "Failed to load data.";
31
+ /**
32
+ * Creates a sanitized Error for SSR loader cache seeding.
33
+ *
34
+ * Use this instead of storing raw server errors in dehydrated query state.
35
+ */
36
+ declare function createSanitizedSSRLoaderError(): Error;
23
37
 
24
38
  interface CreateApiClientOptions {
25
39
  baseURL?: string;
@@ -67,4 +81,4 @@ declare function createApiClient<TRouter extends Router | Record<string, Endpoin
67
81
  */
68
82
  declare function defineClientPlugin<TOverrides = Record<string, never>, TRoutes extends Record<string, Route> = Record<string, Route>>(plugin: ClientPlugin<TOverrides, TRoutes>): ClientPlugin<TOverrides, TRoutes>;
69
83
 
70
- export { ClientPlugin, createApiClient, defineClientPlugin, isConnectionError, runClientHookWithShim };
84
+ export { ClientPlugin, SSR_LOADER_ERROR_MESSAGE, createApiClient, createSanitizedSSRLoaderError, defineClientPlugin, isConnectionError, runClientHookWithShim };
@@ -20,6 +20,20 @@ declare function runClientHookWithShim<T>(hookFn: () => Promise<T> | T, defaultM
20
20
  * `route.loader()` is called during `next build` with no HTTP server running.
21
21
  */
22
22
  declare function isConnectionError(err: unknown): boolean;
23
+ /**
24
+ * Public-safe message used when SSR loader failures are intentionally seeded
25
+ * into React Query so Error Boundaries can render on the client.
26
+ *
27
+ * Never include raw server error text here because dehydrated query state can
28
+ * be serialized into HTML.
29
+ */
30
+ declare const SSR_LOADER_ERROR_MESSAGE = "Failed to load data.";
31
+ /**
32
+ * Creates a sanitized Error for SSR loader cache seeding.
33
+ *
34
+ * Use this instead of storing raw server errors in dehydrated query state.
35
+ */
36
+ declare function createSanitizedSSRLoaderError(): Error;
23
37
 
24
38
  interface CreateApiClientOptions {
25
39
  baseURL?: string;
@@ -67,4 +81,4 @@ declare function createApiClient<TRouter extends Router | Record<string, Endpoin
67
81
  */
68
82
  declare function defineClientPlugin<TOverrides = Record<string, never>, TRoutes extends Record<string, Route> = Record<string, Route>>(plugin: ClientPlugin<TOverrides, TRoutes>): ClientPlugin<TOverrides, TRoutes>;
69
83
 
70
- export { ClientPlugin, createApiClient, defineClientPlugin, isConnectionError, runClientHookWithShim };
84
+ export { ClientPlugin, SSR_LOADER_ERROR_MESSAGE, createApiClient, createSanitizedSSRLoaderError, defineClientPlugin, isConnectionError, runClientHookWithShim };
@@ -20,6 +20,20 @@ declare function runClientHookWithShim<T>(hookFn: () => Promise<T> | T, defaultM
20
20
  * `route.loader()` is called during `next build` with no HTTP server running.
21
21
  */
22
22
  declare function isConnectionError(err: unknown): boolean;
23
+ /**
24
+ * Public-safe message used when SSR loader failures are intentionally seeded
25
+ * into React Query so Error Boundaries can render on the client.
26
+ *
27
+ * Never include raw server error text here because dehydrated query state can
28
+ * be serialized into HTML.
29
+ */
30
+ declare const SSR_LOADER_ERROR_MESSAGE = "Failed to load data.";
31
+ /**
32
+ * Creates a sanitized Error for SSR loader cache seeding.
33
+ *
34
+ * Use this instead of storing raw server errors in dehydrated query state.
35
+ */
36
+ declare function createSanitizedSSRLoaderError(): Error;
23
37
 
24
38
  interface CreateApiClientOptions {
25
39
  baseURL?: string;
@@ -67,4 +81,4 @@ declare function createApiClient<TRouter extends Router | Record<string, Endpoin
67
81
  */
68
82
  declare function defineClientPlugin<TOverrides = Record<string, never>, TRoutes extends Record<string, Route> = Record<string, Route>>(plugin: ClientPlugin<TOverrides, TRoutes>): ClientPlugin<TOverrides, TRoutes>;
69
83
 
70
- export { ClientPlugin, createApiClient, defineClientPlugin, isConnectionError, runClientHookWithShim };
84
+ export { ClientPlugin, SSR_LOADER_ERROR_MESSAGE, createApiClient, createSanitizedSSRLoaderError, defineClientPlugin, isConnectionError, runClientHookWithShim };
@@ -1,4 +1,4 @@
1
- export { createApiClient, isConnectionError, runClientHookWithShim } from '../../packages/stack/src/plugins/utils.mjs';
1
+ export { SSR_LOADER_ERROR_MESSAGE, createApiClient, createSanitizedSSRLoaderError, isConnectionError, runClientHookWithShim } from '../../packages/stack/src/plugins/utils.mjs';
2
2
  export { createRoute, createRouter } from '@btst/yar';
3
3
  export { createClient } from 'better-call/client';
4
4
 
@@ -21,6 +21,11 @@ interface LoaderContext {
21
21
  apiBasePath: string;
22
22
  /** Optional headers for the request */
23
23
  headers?: Headers;
24
+ /**
25
+ * Optional current user ID for SSR loaders that need user-scoped query keys.
26
+ * Hooks (e.g. beforeLoadUserComments) may populate this.
27
+ */
28
+ currentUserId?: string;
24
29
  /** Additional context properties */
25
30
  [key: string]: unknown;
26
31
  }
@@ -21,6 +21,11 @@ interface LoaderContext {
21
21
  apiBasePath: string;
22
22
  /** Optional headers for the request */
23
23
  headers?: Headers;
24
+ /**
25
+ * Optional current user ID for SSR loaders that need user-scoped query keys.
26
+ * Hooks (e.g. beforeLoadUserComments) may populate this.
27
+ */
28
+ currentUserId?: string;
24
29
  /** Additional context properties */
25
30
  [key: string]: unknown;
26
31
  }
@@ -21,6 +21,11 @@ interface LoaderContext {
21
21
  apiBasePath: string;
22
22
  /** Optional headers for the request */
23
23
  headers?: Headers;
24
+ /**
25
+ * Optional current user ID for SSR loaders that need user-scoped query keys.
26
+ * Hooks (e.g. beforeLoadUserComments) may populate this.
27
+ */
28
+ currentUserId?: string;
24
29
  /** Additional context properties */
25
30
  [key: string]: unknown;
26
31
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  const queryKeyFactory = require('@lukemorales/query-key-factory');
4
4
  const queryKeyDefs = require('../../packages/stack/src/plugins/comments/api/query-key-defs.cjs');
5
- const utils = require('../../packages/stack/src/plugins/comments/client/utils.cjs');
5
+ const errorUtils = require('../../packages/stack/src/plugins/comments/error-utils.cjs');
6
6
 
7
7
  function isErrorResponse(response) {
8
8
  return typeof response === "object" && response !== null && "error" in response && response.error !== null && response.error !== void 0;
@@ -40,7 +40,7 @@ function createCommentsQueries(client, headers) {
40
40
  headers
41
41
  });
42
42
  if (isErrorResponse(response)) {
43
- throw utils.toError(response.error);
43
+ throw errorUtils.toError(response.error);
44
44
  }
45
45
  const data = response.data;
46
46
  return data ?? { items: [], total: 0, limit: 20, offset: 0 };
@@ -63,7 +63,7 @@ function createCommentCountQueries(client, headers) {
63
63
  headers
64
64
  });
65
65
  if (isErrorResponse(response)) {
66
- throw utils.toError(response.error);
66
+ throw errorUtils.toError(response.error);
67
67
  }
68
68
  const data = response.data;
69
69
  return data?.count ?? 0;
@@ -96,7 +96,7 @@ function createCommentsThreadQueries(client, headers) {
96
96
  headers
97
97
  });
98
98
  if (isErrorResponse(response)) {
99
- throw utils.toError(response.error);
99
+ throw errorUtils.toError(response.error);
100
100
  }
101
101
  const data = response.data;
102
102
  return data ?? {
@@ -1,6 +1,6 @@
1
1
  import { mergeQueryKeys, createQueryKeys } from '@lukemorales/query-key-factory';
2
2
  import { commentsListDiscriminator, commentCountDiscriminator, commentsThreadDiscriminator } from '../../packages/stack/src/plugins/comments/api/query-key-defs.mjs';
3
- import { toError } from '../../packages/stack/src/plugins/comments/client/utils.mjs';
3
+ import { toError } from '../../packages/stack/src/plugins/comments/error-utils.mjs';
4
4
 
5
5
  function isErrorResponse(response) {
6
6
  return typeof response === "object" && response !== null && "error" in response && response.error !== null && response.error !== void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btst/stack",
3
- "version": "2.9.2",
3
+ "version": "2.9.4",
4
4
  "description": "A composable, plugin-based library for building full-stack applications.",
5
5
  "repository": {
6
6
  "type": "git",