@btst/stack 3.0.0-rc.1 → 3.0.0-rc.2

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 (87) hide show
  1. package/README.md +4 -8
  2. package/dist/next/index.d.cts +13 -7
  3. package/dist/next/index.d.mts +13 -7
  4. package/dist/next/index.d.ts +13 -7
  5. package/dist/packages/stack/src/next/page.cjs +8 -6
  6. package/dist/packages/stack/src/next/page.mjs +8 -6
  7. package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.cjs +1 -1
  8. package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.mjs +1 -1
  9. package/dist/packages/stack/src/react-router/page.cjs +17 -11
  10. package/dist/packages/stack/src/react-router/page.mjs +17 -11
  11. package/dist/packages/stack/src/tanstack/page.cjs +5 -3
  12. package/dist/packages/stack/src/tanstack/page.mjs +5 -3
  13. package/dist/plugins/blog/api/index.d.cts +2 -2
  14. package/dist/plugins/blog/api/index.d.mts +2 -2
  15. package/dist/plugins/blog/api/index.d.ts +2 -2
  16. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  17. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  18. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  19. package/dist/plugins/blog/client/index.d.cts +2 -2
  20. package/dist/plugins/blog/client/index.d.mts +2 -2
  21. package/dist/plugins/blog/client/index.d.ts +2 -2
  22. package/dist/plugins/blog/query-keys.d.cts +2 -2
  23. package/dist/plugins/blog/query-keys.d.mts +2 -2
  24. package/dist/plugins/blog/query-keys.d.ts +2 -2
  25. package/dist/plugins/cms/api/index.d.cts +1 -1
  26. package/dist/plugins/cms/api/index.d.mts +1 -1
  27. package/dist/plugins/cms/api/index.d.ts +1 -1
  28. package/dist/plugins/cms/query-keys.d.cts +1 -1
  29. package/dist/plugins/cms/query-keys.d.mts +1 -1
  30. package/dist/plugins/cms/query-keys.d.ts +1 -1
  31. package/dist/plugins/comments/client/components/index.d.cts +5 -0
  32. package/dist/plugins/comments/client/components/index.d.mts +5 -0
  33. package/dist/plugins/comments/client/components/index.d.ts +5 -0
  34. package/dist/plugins/kanban/api/index.d.cts +1 -1
  35. package/dist/plugins/kanban/api/index.d.mts +1 -1
  36. package/dist/plugins/kanban/api/index.d.ts +1 -1
  37. package/dist/plugins/kanban/client/hooks/index.d.cts +1 -1
  38. package/dist/plugins/kanban/client/hooks/index.d.mts +1 -1
  39. package/dist/plugins/kanban/client/hooks/index.d.ts +1 -1
  40. package/dist/plugins/kanban/query-keys.d.cts +1 -1
  41. package/dist/plugins/kanban/query-keys.d.mts +1 -1
  42. package/dist/plugins/kanban/query-keys.d.ts +1 -1
  43. package/dist/plugins/ui-builder/client/hooks/index.d.cts +1 -1
  44. package/dist/plugins/ui-builder/client/hooks/index.d.mts +1 -1
  45. package/dist/plugins/ui-builder/client/hooks/index.d.ts +1 -1
  46. package/dist/plugins/ui-builder/client/index.d.cts +1 -1
  47. package/dist/plugins/ui-builder/client/index.d.mts +1 -1
  48. package/dist/plugins/ui-builder/client/index.d.ts +1 -1
  49. package/dist/plugins/ui-builder/query-keys.d.cts +1 -1
  50. package/dist/plugins/ui-builder/query-keys.d.mts +1 -1
  51. package/dist/plugins/ui-builder/query-keys.d.ts +1 -1
  52. package/dist/react-router/index.d.cts +14 -4
  53. package/dist/react-router/index.d.mts +14 -4
  54. package/dist/react-router/index.d.ts +14 -4
  55. package/dist/shared/{stack.H5B8miAV.d.cts → stack.CKGjOI3Z.d.cts} +11 -11
  56. package/dist/shared/{stack.80-y2TTR.d.mts → stack.DZHEHd-S.d.mts} +11 -11
  57. package/dist/shared/{stack.CRelKJfH.d.ts → stack.DseBJmGF.d.ts} +4 -4
  58. package/dist/shared/{stack.INBa9-TO.d.cts → stack.DzwZw-Ck.d.cts} +4 -4
  59. package/dist/shared/{stack.DrUBX-QE.d.mts → stack.EOPVnJrF.d.cts} +6 -1
  60. package/dist/shared/{stack.DrUBX-QE.d.ts → stack.EOPVnJrF.d.mts} +6 -1
  61. package/dist/shared/{stack.DrUBX-QE.d.cts → stack.EOPVnJrF.d.ts} +6 -1
  62. package/dist/shared/{stack.Bk5UB_BT.d.ts → stack.WELCDuMs.d.ts} +11 -11
  63. package/dist/shared/{stack.3di7vQs6.d.mts → stack.oVIdV7XS.d.mts} +4 -4
  64. package/dist/tanstack/index.d.cts +20 -12
  65. package/dist/tanstack/index.d.mts +20 -12
  66. package/dist/tanstack/index.d.ts +20 -12
  67. package/package.json +6 -6
  68. package/src/__tests__/entry-factories.test.tsx +104 -4
  69. package/src/__tests__/package-metadata.test.ts +21 -0
  70. package/src/next/index.tsx +2 -0
  71. package/src/next/page.tsx +17 -9
  72. package/src/plugins/comments/__tests__/client-sweep.test.tsx +23 -0
  73. package/src/plugins/comments/client/components/comment-thread.tsx +6 -2
  74. package/src/react-router/index.tsx +1 -0
  75. package/src/react-router/page.tsx +34 -12
  76. package/src/shared/entry-factories.ts +9 -0
  77. package/src/tanstack/index.tsx +2 -0
  78. package/src/tanstack/page.tsx +28 -11
  79. package/dist/shared/{stack.BvxHSZYT.d.cts → stack.BsmDN8r9.d.cts} +3 -3
  80. package/dist/shared/{stack.eq5eg1yt.d.cts → stack.CuI_-z-_.d.cts} +22 -22
  81. package/dist/shared/{stack.B0muOwUF.d.mts → stack.D18Dq3ve.d.cts} +7 -7
  82. package/dist/shared/{stack.B0muOwUF.d.ts → stack.D18Dq3ve.d.mts} +7 -7
  83. package/dist/shared/{stack.B0muOwUF.d.cts → stack.D18Dq3ve.d.ts} +7 -7
  84. package/dist/shared/{stack.Dj04W2c3.d.mts → stack.D2RVkQD8.d.mts} +22 -22
  85. package/dist/shared/{stack.B-hEIoUw.d.ts → stack.DOzsgzkY.d.ts} +3 -3
  86. package/dist/shared/{stack.CMbX8Q5C.d.ts → stack.DXrOtuek.d.ts} +22 -22
  87. package/dist/shared/{stack.f8Vke-eH.d.mts → stack.NGKtwA6S.d.mts} +3 -3
package/README.md CHANGED
@@ -140,16 +140,12 @@ for QueryClient wiring, database adapters, all three frameworks, and auth.
140
140
 
141
141
  ## Database schemas & migrations
142
142
 
143
- Optional CLI to generate schemas and run migrations from enabled plugins:
143
+ Generate schemas and run migrations through the v3 codegen CLI. It runs the
144
+ aligned Better DB CLI in isolation, so its dependencies and `btst` binary do
145
+ not enter your application graph:
144
146
 
145
147
  ```bash
146
- npm install -D @btst/cli
147
- ```
148
-
149
- Generate drizzle schema:
150
-
151
- ```bash
152
- npx @btst/cli generate --orm drizzle --config lib/stack.ts --output db/schema.ts
148
+ npx @btst/codegen@next generate --orm drizzle --config lib/stack.ts --output db/schema.ts
153
149
  ```
154
150
 
155
151
  Supports Prisma, Drizzle, MongoDB and Kysely SQL dialects.
@@ -1,4 +1,5 @@
1
- import { S as StackRequestHandler, G as GetStackClient } from '../shared/stack.DrUBX-QE.cjs';
1
+ import { S as StackRequestHandler, R as ResolveStackClient } from '../shared/stack.EOPVnJrF.cjs';
2
+ export { G as GetStackClient } from '../shared/stack.EOPVnJrF.cjs';
2
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
4
  import { QueryClient, DehydrateOptions } from '@tanstack/react-query';
4
5
  import { Metadata } from 'next';
@@ -31,9 +32,14 @@ interface NextPageProps {
31
32
  all?: string[];
32
33
  }>;
33
34
  }
35
+ /** Resolves the stack client for a Next.js page or metadata request. */
36
+ type GetNextStackClient = ResolveStackClient<NextPageProps>;
34
37
  interface CreateNextPageOptions {
35
- /** Returns the stack client for a given QueryClient (`lib/stack-client`). */
36
- getStackClient: GetStackClient;
38
+ /**
39
+ * Returns the stack client for the current request. May be async and receives
40
+ * the page props so request-aware clients can resolve request-local state.
41
+ */
42
+ getStackClient: GetNextStackClient;
37
43
  /** Returns the QueryClient for the current context (`lib/query-client`). */
38
44
  getQueryClient: () => QueryClient;
39
45
  /**
@@ -71,8 +77,8 @@ interface CreateNextPageOptions {
71
77
  * ```
72
78
  */
73
79
  declare function createNextPage(options: CreateNextPageOptions): {
74
- Page: ({ params }: NextPageProps) => Promise<react_jsx_runtime.JSX.Element>;
75
- generateMetadata: ({ params, }: NextPageProps) => Promise<Metadata>;
80
+ Page: (pageProps: NextPageProps) => Promise<react_jsx_runtime.JSX.Element>;
81
+ generateMetadata: (pageProps: NextPageProps) => Promise<Metadata>;
76
82
  };
77
83
 
78
84
  /**
@@ -87,5 +93,5 @@ declare function createNextPage(options: CreateNextPageOptions): {
87
93
  */
88
94
  declare function nextRouter(): StackRouterConfig;
89
95
 
90
- export { GetStackClient, StackRequestHandler, createNextPage, nextRouter, toNextRouteHandlers };
91
- export type { CreateNextPageOptions, NextPageProps };
96
+ export { ResolveStackClient, StackRequestHandler, createNextPage, nextRouter, toNextRouteHandlers };
97
+ export type { CreateNextPageOptions, GetNextStackClient, NextPageProps };
@@ -1,4 +1,5 @@
1
- import { S as StackRequestHandler, G as GetStackClient } from '../shared/stack.DrUBX-QE.mjs';
1
+ import { S as StackRequestHandler, R as ResolveStackClient } from '../shared/stack.EOPVnJrF.mjs';
2
+ export { G as GetStackClient } from '../shared/stack.EOPVnJrF.mjs';
2
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
4
  import { QueryClient, DehydrateOptions } from '@tanstack/react-query';
4
5
  import { Metadata } from 'next';
@@ -31,9 +32,14 @@ interface NextPageProps {
31
32
  all?: string[];
32
33
  }>;
33
34
  }
35
+ /** Resolves the stack client for a Next.js page or metadata request. */
36
+ type GetNextStackClient = ResolveStackClient<NextPageProps>;
34
37
  interface CreateNextPageOptions {
35
- /** Returns the stack client for a given QueryClient (`lib/stack-client`). */
36
- getStackClient: GetStackClient;
38
+ /**
39
+ * Returns the stack client for the current request. May be async and receives
40
+ * the page props so request-aware clients can resolve request-local state.
41
+ */
42
+ getStackClient: GetNextStackClient;
37
43
  /** Returns the QueryClient for the current context (`lib/query-client`). */
38
44
  getQueryClient: () => QueryClient;
39
45
  /**
@@ -71,8 +77,8 @@ interface CreateNextPageOptions {
71
77
  * ```
72
78
  */
73
79
  declare function createNextPage(options: CreateNextPageOptions): {
74
- Page: ({ params }: NextPageProps) => Promise<react_jsx_runtime.JSX.Element>;
75
- generateMetadata: ({ params, }: NextPageProps) => Promise<Metadata>;
80
+ Page: (pageProps: NextPageProps) => Promise<react_jsx_runtime.JSX.Element>;
81
+ generateMetadata: (pageProps: NextPageProps) => Promise<Metadata>;
76
82
  };
77
83
 
78
84
  /**
@@ -87,5 +93,5 @@ declare function createNextPage(options: CreateNextPageOptions): {
87
93
  */
88
94
  declare function nextRouter(): StackRouterConfig;
89
95
 
90
- export { GetStackClient, StackRequestHandler, createNextPage, nextRouter, toNextRouteHandlers };
91
- export type { CreateNextPageOptions, NextPageProps };
96
+ export { ResolveStackClient, StackRequestHandler, createNextPage, nextRouter, toNextRouteHandlers };
97
+ export type { CreateNextPageOptions, GetNextStackClient, NextPageProps };
@@ -1,4 +1,5 @@
1
- import { S as StackRequestHandler, G as GetStackClient } from '../shared/stack.DrUBX-QE.js';
1
+ import { S as StackRequestHandler, R as ResolveStackClient } from '../shared/stack.EOPVnJrF.js';
2
+ export { G as GetStackClient } from '../shared/stack.EOPVnJrF.js';
2
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
4
  import { QueryClient, DehydrateOptions } from '@tanstack/react-query';
4
5
  import { Metadata } from 'next';
@@ -31,9 +32,14 @@ interface NextPageProps {
31
32
  all?: string[];
32
33
  }>;
33
34
  }
35
+ /** Resolves the stack client for a Next.js page or metadata request. */
36
+ type GetNextStackClient = ResolveStackClient<NextPageProps>;
34
37
  interface CreateNextPageOptions {
35
- /** Returns the stack client for a given QueryClient (`lib/stack-client`). */
36
- getStackClient: GetStackClient;
38
+ /**
39
+ * Returns the stack client for the current request. May be async and receives
40
+ * the page props so request-aware clients can resolve request-local state.
41
+ */
42
+ getStackClient: GetNextStackClient;
37
43
  /** Returns the QueryClient for the current context (`lib/query-client`). */
38
44
  getQueryClient: () => QueryClient;
39
45
  /**
@@ -71,8 +77,8 @@ interface CreateNextPageOptions {
71
77
  * ```
72
78
  */
73
79
  declare function createNextPage(options: CreateNextPageOptions): {
74
- Page: ({ params }: NextPageProps) => Promise<react_jsx_runtime.JSX.Element>;
75
- generateMetadata: ({ params, }: NextPageProps) => Promise<Metadata>;
80
+ Page: (pageProps: NextPageProps) => Promise<react_jsx_runtime.JSX.Element>;
81
+ generateMetadata: (pageProps: NextPageProps) => Promise<Metadata>;
76
82
  };
77
83
 
78
84
  /**
@@ -87,5 +93,5 @@ declare function createNextPage(options: CreateNextPageOptions): {
87
93
  */
88
94
  declare function nextRouter(): StackRouterConfig;
89
95
 
90
- export { GetStackClient, StackRequestHandler, createNextPage, nextRouter, toNextRouteHandlers };
91
- export type { CreateNextPageOptions, NextPageProps };
96
+ export { ResolveStackClient, StackRequestHandler, createNextPage, nextRouter, toNextRouteHandlers };
97
+ export type { CreateNextPageOptions, GetNextStackClient, NextPageProps };
@@ -15,24 +15,26 @@ function createNextPage(options) {
15
15
  wrapPage,
16
16
  dehydrateOptions = entryFactories.stackDehydrateOptions
17
17
  } = options;
18
- async function Page({ params }) {
18
+ async function Page(pageProps) {
19
+ const { params } = pageProps;
19
20
  const pathParams = await params;
20
21
  const path = pathUtils.normalizePath(pathParams?.all);
21
22
  const queryClient = getQueryClient();
22
- const route = getStackClient(queryClient).router.getRoute(path);
23
+ const stackClient = await getStackClient(queryClient, pageProps);
24
+ const route = stackClient.router.getRoute(path);
23
25
  if (route?.loader) {
24
26
  await route.loader();
25
27
  }
26
28
  const page = route?.PageComponent ? /* @__PURE__ */ jsxRuntime.jsx(route.PageComponent, {}) : notFound();
27
29
  return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.HydrationBoundary, { state: reactQuery.dehydrate(queryClient, dehydrateOptions), children: wrapPage ? wrapPage(page) : page });
28
30
  }
29
- async function generateMetadata({
30
- params
31
- }) {
31
+ async function generateMetadata(pageProps) {
32
+ const { params } = pageProps;
32
33
  const pathParams = await params;
33
34
  const path = pathUtils.normalizePath(pathParams?.all);
34
35
  const queryClient = getQueryClient();
35
- const route = getStackClient(queryClient).router.getRoute(path);
36
+ const stackClient = await getStackClient(queryClient, pageProps);
37
+ const route = stackClient.router.getRoute(path);
36
38
  if (!route) {
37
39
  return notFound();
38
40
  }
@@ -13,24 +13,26 @@ function createNextPage(options) {
13
13
  wrapPage,
14
14
  dehydrateOptions = stackDehydrateOptions
15
15
  } = options;
16
- async function Page({ params }) {
16
+ async function Page(pageProps) {
17
+ const { params } = pageProps;
17
18
  const pathParams = await params;
18
19
  const path = normalizePath(pathParams?.all);
19
20
  const queryClient = getQueryClient();
20
- const route = getStackClient(queryClient).router.getRoute(path);
21
+ const stackClient = await getStackClient(queryClient, pageProps);
22
+ const route = stackClient.router.getRoute(path);
21
23
  if (route?.loader) {
22
24
  await route.loader();
23
25
  }
24
26
  const page = route?.PageComponent ? /* @__PURE__ */ jsx(route.PageComponent, {}) : notFound$1();
25
27
  return /* @__PURE__ */ jsx(HydrationBoundary, { state: dehydrate(queryClient, dehydrateOptions), children: wrapPage ? wrapPage(page) : page });
26
28
  }
27
- async function generateMetadata({
28
- params
29
- }) {
29
+ async function generateMetadata(pageProps) {
30
+ const { params } = pageProps;
30
31
  const pathParams = await params;
31
32
  const path = normalizePath(pathParams?.all);
32
33
  const queryClient = getQueryClient();
33
- const route = getStackClient(queryClient).router.getRoute(path);
34
+ const stackClient = await getStackClient(queryClient, pageProps);
35
+ const route = stackClient.router.getRoute(path);
34
36
  if (!route) {
35
37
  return notFound$1();
36
38
  }
@@ -548,7 +548,7 @@ function CommentThread(props) {
548
548
  apiBaseURL: api?.baseURL ?? "",
549
549
  apiBasePath: api?.basePath ?? "",
550
550
  currentUserId,
551
- loginHref: auth?.loginPath
551
+ loginHref: props.loginHref ?? auth?.loginPath
552
552
  };
553
553
  return /* @__PURE__ */ jsxRuntime.jsx("div", { id: "comments", className: props.className, children: /* @__PURE__ */ jsxRuntime.jsx(whenVisible.WhenVisible, { fallback: /* @__PURE__ */ jsxRuntime.jsx(CommentThreadSkeleton, {}), rootMargin: "300px", children: isIdentityPending ? /* @__PURE__ */ jsxRuntime.jsx(CommentThreadSkeleton, {}) : /* @__PURE__ */ jsxRuntime.jsx(CommentThreadInner, { ...resolvedProps }) }) });
554
554
  }
@@ -546,7 +546,7 @@ function CommentThread(props) {
546
546
  apiBaseURL: api?.baseURL ?? "",
547
547
  apiBasePath: api?.basePath ?? "",
548
548
  currentUserId,
549
- loginHref: auth?.loginPath
549
+ loginHref: props.loginHref ?? auth?.loginPath
550
550
  };
551
551
  return /* @__PURE__ */ jsx("div", { id: "comments", className: props.className, children: /* @__PURE__ */ jsx(WhenVisible, { fallback: /* @__PURE__ */ jsx(CommentThreadSkeleton, {}), rootMargin: "300px", children: isIdentityPending ? /* @__PURE__ */ jsx(CommentThreadSkeleton, {}) : /* @__PURE__ */ jsx(CommentThreadInner, { ...resolvedProps }) }) });
552
552
  }
@@ -16,19 +16,24 @@ function createReactRouterPage(options) {
16
16
  wrapPage,
17
17
  dehydrateOptions = entryFactories.stackDehydrateOptions
18
18
  } = options;
19
- async function loader({ params }) {
20
- const queryClient = getQueryClient();
21
- const path = pathUtils.normalizePath(params["*"]);
22
- const route = getStackClient(queryClient).router.getRoute(path);
23
- if (route?.loader) {
24
- await route.loader();
25
- }
26
- return {
27
- path,
28
- dehydratedState: reactQuery.dehydrate(queryClient, dehydrateOptions),
29
- meta: await route?.meta?.()
19
+ function createLoader(getLoaderStackClient) {
20
+ return async function loader2(loaderArgs) {
21
+ const { params } = loaderArgs;
22
+ const queryClient = getQueryClient();
23
+ const path = pathUtils.normalizePath(params["*"]);
24
+ const stackClient = getLoaderStackClient ? await getLoaderStackClient(queryClient, loaderArgs) : getStackClient(queryClient);
25
+ const route = stackClient.router.getRoute(path);
26
+ if (route?.loader) {
27
+ await route.loader();
28
+ }
29
+ return {
30
+ path,
31
+ dehydratedState: reactQuery.dehydrate(queryClient, dehydrateOptions),
32
+ meta: await route?.meta?.()
33
+ };
30
34
  };
31
35
  }
36
+ const loader = createLoader();
32
37
  function meta(args) {
33
38
  return (args.loaderData ?? args.data)?.meta;
34
39
  }
@@ -48,6 +53,7 @@ function createReactRouterPage(options) {
48
53
  }
49
54
  return {
50
55
  loader,
56
+ createLoader,
51
57
  meta,
52
58
  Component,
53
59
  ErrorBoundary: CustomErrorBoundary ?? DefaultErrorBoundary
@@ -14,19 +14,24 @@ function createReactRouterPage(options) {
14
14
  wrapPage,
15
15
  dehydrateOptions = stackDehydrateOptions
16
16
  } = options;
17
- async function loader({ params }) {
18
- const queryClient = getQueryClient();
19
- const path = normalizePath(params["*"]);
20
- const route = getStackClient(queryClient).router.getRoute(path);
21
- if (route?.loader) {
22
- await route.loader();
23
- }
24
- return {
25
- path,
26
- dehydratedState: dehydrate(queryClient, dehydrateOptions),
27
- meta: await route?.meta?.()
17
+ function createLoader(getLoaderStackClient) {
18
+ return async function loader2(loaderArgs) {
19
+ const { params } = loaderArgs;
20
+ const queryClient = getQueryClient();
21
+ const path = normalizePath(params["*"]);
22
+ const stackClient = getLoaderStackClient ? await getLoaderStackClient(queryClient, loaderArgs) : getStackClient(queryClient);
23
+ const route = stackClient.router.getRoute(path);
24
+ if (route?.loader) {
25
+ await route.loader();
26
+ }
27
+ return {
28
+ path,
29
+ dehydratedState: dehydrate(queryClient, dehydrateOptions),
30
+ meta: await route?.meta?.()
31
+ };
28
32
  };
29
33
  }
34
+ const loader = createLoader();
30
35
  function meta(args) {
31
36
  return (args.loaderData ?? args.data)?.meta;
32
37
  }
@@ -46,6 +51,7 @@ function createReactRouterPage(options) {
46
51
  }
47
52
  return {
48
53
  loader,
54
+ createLoader,
49
55
  meta,
50
56
  Component,
51
57
  ErrorBoundary: CustomErrorBoundary ?? DefaultErrorBoundary
@@ -6,7 +6,7 @@ const React = require('react');
6
6
  const pathUtils = require('../client/path-utils.cjs');
7
7
 
8
8
  function createTanStackPageOptions(options) {
9
- const { getStackClient, getQueryClient } = options;
9
+ const { getStackClient, getLoaderStackClient, getQueryClient } = options;
10
10
  function resolveQueryClient(context) {
11
11
  const fromContext = context?.queryClient;
12
12
  const queryClient = getQueryClient?.() ?? fromContext;
@@ -31,10 +31,12 @@ function createTanStackPageOptions(options) {
31
31
  return {
32
32
  ssr: true,
33
33
  component: PageComponent,
34
- loader: async ({ params, context }) => {
34
+ loader: async (loaderArgs) => {
35
+ const { params, context } = loaderArgs;
35
36
  const queryClient = resolveQueryClient(context);
36
37
  const routePath = pathUtils.normalizePath(params._splat);
37
- const route = getStackClient(queryClient).router.getRoute(routePath);
38
+ const stackClient = getLoaderStackClient ? await getLoaderStackClient(queryClient, loaderArgs) : getStackClient(queryClient);
39
+ const route = stackClient.router.getRoute(routePath);
38
40
  if (!route) throw reactRouter.notFound();
39
41
  if (route.loader) await route.loader();
40
42
  return { meta: await route.meta?.() };
@@ -4,7 +4,7 @@ import { useMemo } from 'react';
4
4
  import { normalizePath } from '../client/path-utils.mjs';
5
5
 
6
6
  function createTanStackPageOptions(options) {
7
- const { getStackClient, getQueryClient } = options;
7
+ const { getStackClient, getLoaderStackClient, getQueryClient } = options;
8
8
  function resolveQueryClient(context) {
9
9
  const fromContext = context?.queryClient;
10
10
  const queryClient = getQueryClient?.() ?? fromContext;
@@ -29,10 +29,12 @@ function createTanStackPageOptions(options) {
29
29
  return {
30
30
  ssr: true,
31
31
  component: PageComponent,
32
- loader: async ({ params, context }) => {
32
+ loader: async (loaderArgs) => {
33
+ const { params, context } = loaderArgs;
33
34
  const queryClient = resolveQueryClient(context);
34
35
  const routePath = normalizePath(params._splat);
35
- const route = getStackClient(queryClient).router.getRoute(routePath);
36
+ const stackClient = getLoaderStackClient ? await getLoaderStackClient(queryClient, loaderArgs) : getStackClient(queryClient);
37
+ const route = stackClient.router.getRoute(routePath);
36
38
  if (!route) throw notFound();
37
39
  if (route.loader) await route.loader();
38
40
  return { meta: await route.meta?.() };
@@ -1,8 +1,8 @@
1
- export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.H5B8miAV.cjs';
1
+ export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.CKGjOI3Z.cjs';
2
2
  import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.Dsatjh0B.cjs';
3
3
  import '@btst/stack/plugins/client';
4
4
  import 'zod';
5
- import '../../../shared/stack.B0muOwUF.cjs';
5
+ import '../../../shared/stack.D18Dq3ve.cjs';
6
6
  import '@btst/stack/plugins/api';
7
7
  import '@btst/db';
8
8
  import 'better-call';
@@ -1,8 +1,8 @@
1
- export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.80-y2TTR.mjs';
1
+ export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.DZHEHd-S.mjs';
2
2
  import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.Dsatjh0B.mjs';
3
3
  import '@btst/stack/plugins/client';
4
4
  import 'zod';
5
- import '../../../shared/stack.B0muOwUF.mjs';
5
+ import '../../../shared/stack.D18Dq3ve.mjs';
6
6
  import '@btst/stack/plugins/api';
7
7
  import '@btst/db';
8
8
  import 'better-call';
@@ -1,8 +1,8 @@
1
- export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.Bk5UB_BT.js';
1
+ export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.WELCDuMs.js';
2
2
  import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.Dsatjh0B.js';
3
3
  import '@btst/stack/plugins/client';
4
4
  import 'zod';
5
- import '../../../shared/stack.B0muOwUF.js';
5
+ import '../../../shared/stack.D18Dq3ve.js';
6
6
  import '@btst/stack/plugins/api';
7
7
  import '@btst/db';
8
8
  import 'better-call';
@@ -1,9 +1,9 @@
1
- export { P as PostCreateInput, e as PostUpdateInput, p as UseNextPreviousPostsOptions, q as UseNextPreviousPostsResult, d as UsePostResult, b as UsePostSearchOptions, c as UsePostSearchResult, U as UsePostsOptions, a as UsePostsResult, s as UseRecentPostsOptions, t as UseRecentPostsResult, l as useCreatePost, n as useDeletePost, r as useNextPreviousPosts, g as usePost, k as usePostForm, o as usePostSearch, u as usePosts, v as useRecentPosts, h as useSuspensePost, f as useSuspensePosts, j as useSuspenseTags, i as useTags, m as useUpdatePost } from '../../../../shared/stack.INBa9-TO.cjs';
1
+ export { P as PostCreateInput, e as PostUpdateInput, p as UseNextPreviousPostsOptions, q as UseNextPreviousPostsResult, d as UsePostResult, b as UsePostSearchOptions, c as UsePostSearchResult, U as UsePostsOptions, a as UsePostsResult, s as UseRecentPostsOptions, t as UseRecentPostsResult, l as useCreatePost, n as useDeletePost, r as useNextPreviousPosts, g as usePost, k as usePostForm, o as usePostSearch, u as usePosts, v as useRecentPosts, h as useSuspensePost, f as useSuspensePosts, j as useSuspenseTags, i as useTags, m as useUpdatePost } from '../../../../shared/stack.DzwZw-Ck.cjs';
2
2
  export { useDebounce } from '@btst/stack/plugins/client/hooks';
3
3
  import '@tanstack/react-query';
4
4
  import 'zod';
5
5
  import '../../../../shared/stack.Dsatjh0B.cjs';
6
- import '../../../../shared/stack.B0muOwUF.cjs';
6
+ import '../../../../shared/stack.D18Dq3ve.cjs';
7
7
 
8
8
  declare function useThrottle<T>(value: T, wait?: number): T;
9
9
 
@@ -1,9 +1,9 @@
1
- export { P as PostCreateInput, e as PostUpdateInput, p as UseNextPreviousPostsOptions, q as UseNextPreviousPostsResult, d as UsePostResult, b as UsePostSearchOptions, c as UsePostSearchResult, U as UsePostsOptions, a as UsePostsResult, s as UseRecentPostsOptions, t as UseRecentPostsResult, l as useCreatePost, n as useDeletePost, r as useNextPreviousPosts, g as usePost, k as usePostForm, o as usePostSearch, u as usePosts, v as useRecentPosts, h as useSuspensePost, f as useSuspensePosts, j as useSuspenseTags, i as useTags, m as useUpdatePost } from '../../../../shared/stack.3di7vQs6.mjs';
1
+ export { P as PostCreateInput, e as PostUpdateInput, p as UseNextPreviousPostsOptions, q as UseNextPreviousPostsResult, d as UsePostResult, b as UsePostSearchOptions, c as UsePostSearchResult, U as UsePostsOptions, a as UsePostsResult, s as UseRecentPostsOptions, t as UseRecentPostsResult, l as useCreatePost, n as useDeletePost, r as useNextPreviousPosts, g as usePost, k as usePostForm, o as usePostSearch, u as usePosts, v as useRecentPosts, h as useSuspensePost, f as useSuspensePosts, j as useSuspenseTags, i as useTags, m as useUpdatePost } from '../../../../shared/stack.oVIdV7XS.mjs';
2
2
  export { useDebounce } from '@btst/stack/plugins/client/hooks';
3
3
  import '@tanstack/react-query';
4
4
  import 'zod';
5
5
  import '../../../../shared/stack.Dsatjh0B.mjs';
6
- import '../../../../shared/stack.B0muOwUF.mjs';
6
+ import '../../../../shared/stack.D18Dq3ve.mjs';
7
7
 
8
8
  declare function useThrottle<T>(value: T, wait?: number): T;
9
9
 
@@ -1,9 +1,9 @@
1
- export { P as PostCreateInput, e as PostUpdateInput, p as UseNextPreviousPostsOptions, q as UseNextPreviousPostsResult, d as UsePostResult, b as UsePostSearchOptions, c as UsePostSearchResult, U as UsePostsOptions, a as UsePostsResult, s as UseRecentPostsOptions, t as UseRecentPostsResult, l as useCreatePost, n as useDeletePost, r as useNextPreviousPosts, g as usePost, k as usePostForm, o as usePostSearch, u as usePosts, v as useRecentPosts, h as useSuspensePost, f as useSuspensePosts, j as useSuspenseTags, i as useTags, m as useUpdatePost } from '../../../../shared/stack.CRelKJfH.js';
1
+ export { P as PostCreateInput, e as PostUpdateInput, p as UseNextPreviousPostsOptions, q as UseNextPreviousPostsResult, d as UsePostResult, b as UsePostSearchOptions, c as UsePostSearchResult, U as UsePostsOptions, a as UsePostsResult, s as UseRecentPostsOptions, t as UseRecentPostsResult, l as useCreatePost, n as useDeletePost, r as useNextPreviousPosts, g as usePost, k as usePostForm, o as usePostSearch, u as usePosts, v as useRecentPosts, h as useSuspensePost, f as useSuspensePosts, j as useSuspenseTags, i as useTags, m as useUpdatePost } from '../../../../shared/stack.DseBJmGF.js';
2
2
  export { useDebounce } from '@btst/stack/plugins/client/hooks';
3
3
  import '@tanstack/react-query';
4
4
  import 'zod';
5
5
  import '../../../../shared/stack.Dsatjh0B.js';
6
- import '../../../../shared/stack.B0muOwUF.js';
6
+ import '../../../../shared/stack.D18Dq3ve.js';
7
7
 
8
8
  declare function useThrottle<T>(value: T, wait?: number): T;
9
9
 
@@ -5,10 +5,10 @@ import { ComponentType, ReactNode } from 'react';
5
5
  import { QueryClient } from '@tanstack/react-query';
6
6
  import { P as Post, S as SerializedPost } from '../../../shared/stack.Dsatjh0B.cjs';
7
7
  import { N as NewPostPageComponent } from '../../../shared/stack.Bz-YPsuX.cjs';
8
- export { U as UsePostsOptions, a as UsePostsResult } from '../../../shared/stack.INBa9-TO.cjs';
8
+ export { U as UsePostsOptions, a as UsePostsResult } from '../../../shared/stack.DzwZw-Ck.cjs';
9
9
  import 'zod';
10
10
  import '@btst/stack/plugins/client/hooks';
11
- import '../../../shared/stack.B0muOwUF.cjs';
11
+ import '../../../shared/stack.D18Dq3ve.cjs';
12
12
 
13
13
  /**
14
14
  * Context passed to route hooks
@@ -5,10 +5,10 @@ import { ComponentType, ReactNode } from 'react';
5
5
  import { QueryClient } from '@tanstack/react-query';
6
6
  import { P as Post, S as SerializedPost } from '../../../shared/stack.Dsatjh0B.mjs';
7
7
  import { N as NewPostPageComponent } from '../../../shared/stack.Bz-YPsuX.mjs';
8
- export { U as UsePostsOptions, a as UsePostsResult } from '../../../shared/stack.3di7vQs6.mjs';
8
+ export { U as UsePostsOptions, a as UsePostsResult } from '../../../shared/stack.oVIdV7XS.mjs';
9
9
  import 'zod';
10
10
  import '@btst/stack/plugins/client/hooks';
11
- import '../../../shared/stack.B0muOwUF.mjs';
11
+ import '../../../shared/stack.D18Dq3ve.mjs';
12
12
 
13
13
  /**
14
14
  * Context passed to route hooks
@@ -5,10 +5,10 @@ import { ComponentType, ReactNode } from 'react';
5
5
  import { QueryClient } from '@tanstack/react-query';
6
6
  import { P as Post, S as SerializedPost } from '../../../shared/stack.Dsatjh0B.js';
7
7
  import { N as NewPostPageComponent } from '../../../shared/stack.Bz-YPsuX.js';
8
- export { U as UsePostsOptions, a as UsePostsResult } from '../../../shared/stack.CRelKJfH.js';
8
+ export { U as UsePostsOptions, a as UsePostsResult } from '../../../shared/stack.DseBJmGF.js';
9
9
  import 'zod';
10
10
  import '@btst/stack/plugins/client/hooks';
11
- import '../../../shared/stack.B0muOwUF.js';
11
+ import '../../../shared/stack.D18Dq3ve.js';
12
12
 
13
13
  /**
14
14
  * Context passed to route hooks
@@ -1,7 +1,7 @@
1
1
  import '@btst/stack/plugins/client';
2
- export { n as blogResources, f as createBlogQueryKeys } from '../../shared/stack.H5B8miAV.cjs';
2
+ export { n as blogResources, f as createBlogQueryKeys } from '../../shared/stack.CKGjOI3Z.cjs';
3
3
  import 'zod';
4
- import '../../shared/stack.B0muOwUF.cjs';
4
+ import '../../shared/stack.D18Dq3ve.cjs';
5
5
  import '../../shared/stack.Dsatjh0B.cjs';
6
6
  import '@btst/stack/plugins/api';
7
7
  import '@btst/db';
@@ -1,7 +1,7 @@
1
1
  import '@btst/stack/plugins/client';
2
- export { n as blogResources, f as createBlogQueryKeys } from '../../shared/stack.80-y2TTR.mjs';
2
+ export { n as blogResources, f as createBlogQueryKeys } from '../../shared/stack.DZHEHd-S.mjs';
3
3
  import 'zod';
4
- import '../../shared/stack.B0muOwUF.mjs';
4
+ import '../../shared/stack.D18Dq3ve.mjs';
5
5
  import '../../shared/stack.Dsatjh0B.mjs';
6
6
  import '@btst/stack/plugins/api';
7
7
  import '@btst/db';
@@ -1,7 +1,7 @@
1
1
  import '@btst/stack/plugins/client';
2
- export { n as blogResources, f as createBlogQueryKeys } from '../../shared/stack.Bk5UB_BT.js';
2
+ export { n as blogResources, f as createBlogQueryKeys } from '../../shared/stack.WELCDuMs.js';
3
3
  import 'zod';
4
- import '../../shared/stack.B0muOwUF.js';
4
+ import '../../shared/stack.D18Dq3ve.js';
5
5
  import '../../shared/stack.Dsatjh0B.js';
6
6
  import '@btst/stack/plugins/api';
7
7
  import '@btst/db';
@@ -1,4 +1,4 @@
1
- export { a as CMSApiRouter, b as CMSRouteKey, m as CMS_QUERY_KEYS, k as CreateCMSContentItemInput, l as CreateCMSContentItemOptions, c as cmsBackendPlugin, j as createCMSContentItem, d as getAllContentItems, g as getAllContentTypes, f as getContentItemById, e as getContentItemBySlug, h as serializeContentItem, i as serializeContentItemWithType, s as serializeContentType } from '../../../shared/stack.BvxHSZYT.cjs';
1
+ export { a as CMSApiRouter, b as CMSRouteKey, m as CMS_QUERY_KEYS, k as CreateCMSContentItemInput, l as CreateCMSContentItemOptions, c as cmsBackendPlugin, j as createCMSContentItem, d as getAllContentItems, g as getAllContentTypes, f as getContentItemById, e as getContentItemBySlug, h as serializeContentItem, i as serializeContentItemWithType, s as serializeContentType } from '../../../shared/stack.BsmDN8r9.cjs';
2
2
  export { createCMSQueryKeys } from '../query-keys.cjs';
3
3
  import '@btst/stack/plugins/api';
4
4
  import '../../../shared/stack.aXdBOJAR.cjs';
@@ -1,4 +1,4 @@
1
- export { a as CMSApiRouter, b as CMSRouteKey, m as CMS_QUERY_KEYS, k as CreateCMSContentItemInput, l as CreateCMSContentItemOptions, c as cmsBackendPlugin, j as createCMSContentItem, d as getAllContentItems, g as getAllContentTypes, f as getContentItemById, e as getContentItemBySlug, h as serializeContentItem, i as serializeContentItemWithType, s as serializeContentType } from '../../../shared/stack.f8Vke-eH.mjs';
1
+ export { a as CMSApiRouter, b as CMSRouteKey, m as CMS_QUERY_KEYS, k as CreateCMSContentItemInput, l as CreateCMSContentItemOptions, c as cmsBackendPlugin, j as createCMSContentItem, d as getAllContentItems, g as getAllContentTypes, f as getContentItemById, e as getContentItemBySlug, h as serializeContentItem, i as serializeContentItemWithType, s as serializeContentType } from '../../../shared/stack.NGKtwA6S.mjs';
2
2
  export { createCMSQueryKeys } from '../query-keys.mjs';
3
3
  import '@btst/stack/plugins/api';
4
4
  import '../../../shared/stack.aXdBOJAR.mjs';
@@ -1,4 +1,4 @@
1
- export { a as CMSApiRouter, b as CMSRouteKey, m as CMS_QUERY_KEYS, k as CreateCMSContentItemInput, l as CreateCMSContentItemOptions, c as cmsBackendPlugin, j as createCMSContentItem, d as getAllContentItems, g as getAllContentTypes, f as getContentItemById, e as getContentItemBySlug, h as serializeContentItem, i as serializeContentItemWithType, s as serializeContentType } from '../../../shared/stack.B-hEIoUw.js';
1
+ export { a as CMSApiRouter, b as CMSRouteKey, m as CMS_QUERY_KEYS, k as CreateCMSContentItemInput, l as CreateCMSContentItemOptions, c as cmsBackendPlugin, j as createCMSContentItem, d as getAllContentItems, g as getAllContentTypes, f as getContentItemById, e as getContentItemBySlug, h as serializeContentItem, i as serializeContentItemWithType, s as serializeContentType } from '../../../shared/stack.DOzsgzkY.js';
2
2
  export { createCMSQueryKeys } from '../query-keys.js';
3
3
  import '@btst/stack/plugins/api';
4
4
  import '../../../shared/stack.aXdBOJAR.js';
@@ -1,4 +1,4 @@
1
- import { a as CMSApiRouter, C as ContentListDiscriminator } from '../../shared/stack.BvxHSZYT.cjs';
1
+ import { a as CMSApiRouter, C as ContentListDiscriminator } from '../../shared/stack.BsmDN8r9.cjs';
2
2
  import * as _btst_stack_plugins_client from '@btst/stack/plugins/client';
3
3
  import { createApiClient } from '@btst/stack/plugins/client';
4
4
  import { S as SerializedContentType, I as InverseRelation, a as SerializedContentItemWithType, P as PaginatedContentItems } from '../../shared/stack.aXdBOJAR.cjs';
@@ -1,4 +1,4 @@
1
- import { a as CMSApiRouter, C as ContentListDiscriminator } from '../../shared/stack.f8Vke-eH.mjs';
1
+ import { a as CMSApiRouter, C as ContentListDiscriminator } from '../../shared/stack.NGKtwA6S.mjs';
2
2
  import * as _btst_stack_plugins_client from '@btst/stack/plugins/client';
3
3
  import { createApiClient } from '@btst/stack/plugins/client';
4
4
  import { S as SerializedContentType, I as InverseRelation, a as SerializedContentItemWithType, P as PaginatedContentItems } from '../../shared/stack.aXdBOJAR.mjs';
@@ -1,4 +1,4 @@
1
- import { a as CMSApiRouter, C as ContentListDiscriminator } from '../../shared/stack.B-hEIoUw.js';
1
+ import { a as CMSApiRouter, C as ContentListDiscriminator } from '../../shared/stack.DOzsgzkY.js';
2
2
  import * as _btst_stack_plugins_client from '@btst/stack/plugins/client';
3
3
  import { createApiClient } from '@btst/stack/plugins/client';
4
4
  import { S as SerializedContentType, I as InverseRelation, a as SerializedContentItemWithType, P as PaginatedContentItems } from '../../shared/stack.aXdBOJAR.js';
@@ -30,6 +30,11 @@ interface CommentThreadProps {
30
30
  className?: string;
31
31
  /** Localization strings — defaults to English */
32
32
  localization?: Partial<CommentsLocalization>;
33
+ /**
34
+ * Sign-in URL for unauthenticated users in this thread.
35
+ * Overrides the `loginPath` from the nearest `StackProvider`.
36
+ */
37
+ loginHref?: string;
33
38
  /**
34
39
  * Number of top-level comments to load per page.
35
40
  * Clicking "Load more" fetches the next page. Default: 10.
@@ -30,6 +30,11 @@ interface CommentThreadProps {
30
30
  className?: string;
31
31
  /** Localization strings — defaults to English */
32
32
  localization?: Partial<CommentsLocalization>;
33
+ /**
34
+ * Sign-in URL for unauthenticated users in this thread.
35
+ * Overrides the `loginPath` from the nearest `StackProvider`.
36
+ */
37
+ loginHref?: string;
33
38
  /**
34
39
  * Number of top-level comments to load per page.
35
40
  * Clicking "Load more" fetches the next page. Default: 10.