@btst/stack 1.1.0 → 1.1.1

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 (46) hide show
  1. package/README.md +12 -12
  2. package/dist/api/index.cjs +2 -0
  3. package/dist/api/index.d.cts +1 -0
  4. package/dist/api/index.d.mts +1 -0
  5. package/dist/api/index.d.ts +1 -0
  6. package/dist/api/index.mjs +1 -0
  7. package/dist/client/index.cjs +2 -0
  8. package/dist/client/index.d.cts +24 -1
  9. package/dist/client/index.d.mts +24 -1
  10. package/dist/client/index.d.ts +24 -1
  11. package/dist/client/index.mjs +1 -0
  12. package/dist/client/path-utils.cjs +15 -0
  13. package/dist/client/path-utils.mjs +13 -0
  14. package/dist/index.cjs +2 -0
  15. package/dist/index.d.cts +1 -0
  16. package/dist/index.d.mts +1 -0
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.mjs +1 -0
  19. package/dist/plugins/api/index.cjs +0 -2
  20. package/dist/plugins/api/index.d.cts +0 -1
  21. package/dist/plugins/api/index.d.mts +0 -1
  22. package/dist/plugins/api/index.d.ts +0 -1
  23. package/dist/plugins/api/index.mjs +0 -1
  24. package/dist/plugins/blog/api/index.d.cts +1 -1
  25. package/dist/plugins/blog/api/index.d.mts +1 -1
  26. package/dist/plugins/blog/api/index.d.ts +1 -1
  27. package/dist/plugins/blog/client/components/shared/better-blog-attribution.cjs +4 -4
  28. package/dist/plugins/blog/client/components/shared/better-blog-attribution.mjs +4 -4
  29. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  30. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  31. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  32. package/dist/plugins/blog/client/index.d.cts +1 -1
  33. package/dist/plugins/blog/client/index.d.mts +1 -1
  34. package/dist/plugins/blog/client/index.d.ts +1 -1
  35. package/dist/plugins/blog/query-keys.d.cts +2 -2
  36. package/dist/plugins/blog/query-keys.d.mts +2 -2
  37. package/dist/plugins/blog/query-keys.d.ts +2 -2
  38. package/package.json +1 -1
  39. package/src/api/index.ts +2 -0
  40. package/src/client/index.ts +2 -0
  41. package/src/client/path-utils.ts +38 -0
  42. package/src/plugins/api/index.ts +0 -1
  43. package/src/plugins/blog/client/components/shared/better-blog-attribution.tsx +4 -4
  44. package/dist/shared/{stack.Cr2JoQdo.d.cts → stack.CoPoHVfV.d.cts} +1 -1
  45. package/dist/shared/{stack.Cr2JoQdo.d.mts → stack.CoPoHVfV.d.mts} +1 -1
  46. package/dist/shared/{stack.Cr2JoQdo.d.ts → stack.CoPoHVfV.d.ts} +1 -1
package/README.md CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  <div align="center">
4
4
 
5
- **Add complete full-stack features to your React app in minutes, not weeks**
5
+ **Composable full-stack plugin system for React frameworks**
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/@btst/stack.svg)](https://www.npmjs.com/package/@btst/stack)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
9
 
10
- [📖 Documentation](https://www.btst.ai) • [🐛 Issues](https://github.com/olliethedev/better-stack/issues)
10
+ [📖 Documentation](https://www.better-stack.ai) • [🐛 Issues](https://github.com/olliethedev/better-stack/issues)
11
11
 
12
12
  </div>
13
13
 
@@ -215,23 +215,23 @@ Now add scheduling, feedback, or newsletters the same way. Each feature is indep
215
215
 
216
216
  Better Stack transforms how you think about building apps:
217
217
 
218
- **Internal teams** - Build shared features once, use across multiple apps. Your marketing team's blog plugin works in the main app, the docs site, and the landing page
219
- **Open source** - Share complete features, not just code snippets. Someone can add your newsletter feature to their Next.js app in minutes
220
- **Agencies** - Create a library of reusable features. Drop scheduling into client A's app, feedback into client B's app, both using React Router
221
- **SaaS platforms** - Offer feature plugins your customers can compose. They pick blog + scheduling + AI assistant, mix and match to build their ideal app
222
- • **Rapid prototyping** - Add 5 features in an afternoon instead of 5 weeks. Validate ideas faster
218
+ - **Open source** - Share complete features, not just code snippets. Someone can add a newsletter plugin to their Next.js app in minutes
219
+ - **Fast development** - Add 5 features in an afternoon instead of 5 weeks. Validate ideas faster
220
+ - **Agencies** - Create a library of reusable features. Drop scheduling into client A's app, feedback into client B's app.
221
+ - **SaaS platforms** - Offer feature plugins your customers can compose. They pick blog + scheduling + AI assistant, mix and match to build their ideal app
223
222
 
224
- Each feature is a complete, self-contained full-stack capability. No configuration files. No code generation. No framework lock-in. Just add it and it works.
223
+
224
+ Each plugin is a complete, self-contained horizontal full-stack feature. No framework lock-in. Just add it and it works.
225
225
 
226
226
  ## Learn More
227
227
 
228
- For complete documentation, examples, and plugin development guides, visit **[https://www.btst.ai](https://www.btst.ai)**
228
+ For complete documentation, examples, and plugin development guides, visit **[https://www.better-stack.ai](https://www.better-stack.ai)**
229
229
 
230
230
  ## Examples
231
231
 
232
- - [Next.js App Router](./examples/nextjs) - Full SSR with App Router
233
- - [React Router](./examples/react-router) - Client-side routing with React Router
234
- - [TanStack Router](./examples/tanstack) - Type-safe routing with TanStack Router
232
+ - [Next.js App Router](./examples/nextjs) - Next.js App Router example
233
+ - [React Router](./examples/react-router) - React Router example
234
+ - [TanStack Router](./examples/tanstack) - TanStack Router example
235
235
 
236
236
  ## License
237
237
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  const betterCall = require('better-call');
4
4
  const db = require('@btst/db');
5
+ const node = require('better-call/node');
5
6
 
6
7
  function betterStack(config) {
7
8
  const { plugins, adapter, dbSchema, basePath } = config;
@@ -27,4 +28,5 @@ function betterStack(config) {
27
28
  };
28
29
  }
29
30
 
31
+ exports.toNodeHandler = node.toNodeHandler;
30
32
  exports.betterStack = betterStack;
@@ -1,5 +1,6 @@
1
1
  import { d as PrefixedPluginRoutes, e as BackendLibConfig, f as BackendLib } from '../shared/stack.ByOugz9d.cjs';
2
2
  export { B as BackendPlugin } from '../shared/stack.ByOugz9d.cjs';
3
+ export { toNodeHandler } from 'better-call/node';
3
4
  import '@btst/yar';
4
5
  import '@btst/db';
5
6
  import 'better-call';
@@ -1,5 +1,6 @@
1
1
  import { d as PrefixedPluginRoutes, e as BackendLibConfig, f as BackendLib } from '../shared/stack.ByOugz9d.mjs';
2
2
  export { B as BackendPlugin } from '../shared/stack.ByOugz9d.mjs';
3
+ export { toNodeHandler } from 'better-call/node';
3
4
  import '@btst/yar';
4
5
  import '@btst/db';
5
6
  import 'better-call';
@@ -1,5 +1,6 @@
1
1
  import { d as PrefixedPluginRoutes, e as BackendLibConfig, f as BackendLib } from '../shared/stack.ByOugz9d.js';
2
2
  export { B as BackendPlugin } from '../shared/stack.ByOugz9d.js';
3
+ export { toNodeHandler } from 'better-call/node';
3
4
  import '@btst/yar';
4
5
  import '@btst/db';
5
6
  import 'better-call';
@@ -1,5 +1,6 @@
1
1
  import { createRouter } from 'better-call';
2
2
  import { defineDb } from '@btst/db';
3
+ export { toNodeHandler } from 'better-call/node';
3
4
 
4
5
  function betterStack(config) {
5
6
  const { plugins, adapter, dbSchema, basePath } = config;
@@ -3,6 +3,7 @@
3
3
  const yar = require('@btst/yar');
4
4
  const sitemapUtils = require('./sitemap-utils.cjs');
5
5
  const metaUtils = require('./meta-utils.cjs');
6
+ const pathUtils = require('./path-utils.cjs');
6
7
 
7
8
  function createStackClient(config) {
8
9
  const { plugins } = config;
@@ -36,4 +37,5 @@ function createStackClient(config) {
36
37
 
37
38
  exports.sitemapEntryToXmlString = sitemapUtils.sitemapEntryToXmlString;
38
39
  exports.metaElementsToObject = metaUtils.metaElementsToObject;
40
+ exports.normalizePath = pathUtils.normalizePath;
39
41
  exports.createStackClient = createStackClient;
@@ -72,6 +72,29 @@ interface Metadata {
72
72
  */
73
73
  declare function metaElementsToObject(metaElements: Array<React.JSX.IntrinsicElements["meta"] | undefined>): Metadata;
74
74
 
75
+ /**
76
+ * Normalizes path segments from framework route params into a consistent path string.
77
+ *
78
+ * Handles different framework param formats:
79
+ * - Next.js: `pathParams.all` (string[])
80
+ * - React Router: `params["*"]` (string)
81
+ * - TanStack Router: `params._splat` (string)
82
+ *
83
+ * @param path - Path segments as string, string array, or undefined
84
+ * @returns Normalized path string starting with "/" (or "/" for empty/undefined)
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * // Next.js
89
+ * normalizePath(pathParams?.all) // ["blog", "post"] => "/blog/post"
90
+ *
91
+ * // React Router / TanStack Router
92
+ * normalizePath(params["*"]) // "blog/post" => "/blog/post"
93
+ * normalizePath(undefined) // => "/"
94
+ * ```
95
+ */
96
+ declare function normalizePath(path?: string | Array<string> | undefined): string;
97
+
75
98
  /**
76
99
  * Creates the client library with plugin support
77
100
  *
@@ -121,4 +144,4 @@ declare function metaElementsToObject(metaElements: Array<React.JSX.IntrinsicEle
121
144
  */
122
145
  declare function createStackClient<TPlugins extends Record<string, ClientPlugin<any, any>>, TRoutes extends PluginRoutes<TPlugins> = PluginRoutes<TPlugins>>(config: ClientLibConfig<TPlugins>): ClientLib<TRoutes>;
123
146
 
124
- export { ClientLib, ClientLibConfig, ClientPlugin, createStackClient, metaElementsToObject, sitemapEntryToXmlString };
147
+ export { ClientLib, ClientLibConfig, ClientPlugin, createStackClient, metaElementsToObject, normalizePath, sitemapEntryToXmlString };
@@ -72,6 +72,29 @@ interface Metadata {
72
72
  */
73
73
  declare function metaElementsToObject(metaElements: Array<React.JSX.IntrinsicElements["meta"] | undefined>): Metadata;
74
74
 
75
+ /**
76
+ * Normalizes path segments from framework route params into a consistent path string.
77
+ *
78
+ * Handles different framework param formats:
79
+ * - Next.js: `pathParams.all` (string[])
80
+ * - React Router: `params["*"]` (string)
81
+ * - TanStack Router: `params._splat` (string)
82
+ *
83
+ * @param path - Path segments as string, string array, or undefined
84
+ * @returns Normalized path string starting with "/" (or "/" for empty/undefined)
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * // Next.js
89
+ * normalizePath(pathParams?.all) // ["blog", "post"] => "/blog/post"
90
+ *
91
+ * // React Router / TanStack Router
92
+ * normalizePath(params["*"]) // "blog/post" => "/blog/post"
93
+ * normalizePath(undefined) // => "/"
94
+ * ```
95
+ */
96
+ declare function normalizePath(path?: string | Array<string> | undefined): string;
97
+
75
98
  /**
76
99
  * Creates the client library with plugin support
77
100
  *
@@ -121,4 +144,4 @@ declare function metaElementsToObject(metaElements: Array<React.JSX.IntrinsicEle
121
144
  */
122
145
  declare function createStackClient<TPlugins extends Record<string, ClientPlugin<any, any>>, TRoutes extends PluginRoutes<TPlugins> = PluginRoutes<TPlugins>>(config: ClientLibConfig<TPlugins>): ClientLib<TRoutes>;
123
146
 
124
- export { ClientLib, ClientLibConfig, ClientPlugin, createStackClient, metaElementsToObject, sitemapEntryToXmlString };
147
+ export { ClientLib, ClientLibConfig, ClientPlugin, createStackClient, metaElementsToObject, normalizePath, sitemapEntryToXmlString };
@@ -72,6 +72,29 @@ interface Metadata {
72
72
  */
73
73
  declare function metaElementsToObject(metaElements: Array<React.JSX.IntrinsicElements["meta"] | undefined>): Metadata;
74
74
 
75
+ /**
76
+ * Normalizes path segments from framework route params into a consistent path string.
77
+ *
78
+ * Handles different framework param formats:
79
+ * - Next.js: `pathParams.all` (string[])
80
+ * - React Router: `params["*"]` (string)
81
+ * - TanStack Router: `params._splat` (string)
82
+ *
83
+ * @param path - Path segments as string, string array, or undefined
84
+ * @returns Normalized path string starting with "/" (or "/" for empty/undefined)
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * // Next.js
89
+ * normalizePath(pathParams?.all) // ["blog", "post"] => "/blog/post"
90
+ *
91
+ * // React Router / TanStack Router
92
+ * normalizePath(params["*"]) // "blog/post" => "/blog/post"
93
+ * normalizePath(undefined) // => "/"
94
+ * ```
95
+ */
96
+ declare function normalizePath(path?: string | Array<string> | undefined): string;
97
+
75
98
  /**
76
99
  * Creates the client library with plugin support
77
100
  *
@@ -121,4 +144,4 @@ declare function metaElementsToObject(metaElements: Array<React.JSX.IntrinsicEle
121
144
  */
122
145
  declare function createStackClient<TPlugins extends Record<string, ClientPlugin<any, any>>, TRoutes extends PluginRoutes<TPlugins> = PluginRoutes<TPlugins>>(config: ClientLibConfig<TPlugins>): ClientLib<TRoutes>;
123
146
 
124
- export { ClientLib, ClientLibConfig, ClientPlugin, createStackClient, metaElementsToObject, sitemapEntryToXmlString };
147
+ export { ClientLib, ClientLibConfig, ClientPlugin, createStackClient, metaElementsToObject, normalizePath, sitemapEntryToXmlString };
@@ -1,6 +1,7 @@
1
1
  import { createRouter } from '@btst/yar';
2
2
  export { sitemapEntryToXmlString } from './sitemap-utils.mjs';
3
3
  export { metaElementsToObject } from './meta-utils.mjs';
4
+ export { normalizePath } from './path-utils.mjs';
4
5
 
5
6
  function createStackClient(config) {
6
7
  const { plugins } = config;
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ function normalizePath(path) {
4
+ if (!path) {
5
+ return "/";
6
+ }
7
+ if (Array.isArray(path)) {
8
+ const segments2 = path.filter(Boolean);
9
+ return segments2.length > 0 ? `/${segments2.join("/")}` : "/";
10
+ }
11
+ const segments = path.split("/").filter(Boolean);
12
+ return segments.length > 0 ? `/${segments.join("/")}` : "/";
13
+ }
14
+
15
+ exports.normalizePath = normalizePath;
@@ -0,0 +1,13 @@
1
+ function normalizePath(path) {
2
+ if (!path) {
3
+ return "/";
4
+ }
5
+ if (Array.isArray(path)) {
6
+ const segments2 = path.filter(Boolean);
7
+ return segments2.length > 0 ? `/${segments2.join("/")}` : "/";
8
+ }
9
+ const segments = path.split("/").filter(Boolean);
10
+ return segments.length > 0 ? `/${segments.join("/")}` : "/";
11
+ }
12
+
13
+ export { normalizePath };
package/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  const api_index = require('./api/index.cjs');
4
+ const node = require('better-call/node');
4
5
 
5
6
 
6
7
 
7
8
  exports.betterStack = api_index.betterStack;
9
+ exports.toNodeHandler = node.toNodeHandler;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { betterStack } from './api/index.cjs';
2
+ export { toNodeHandler } from 'better-call/node';
2
3
  export { f as BackendLib, e as BackendLibConfig, B as BackendPlugin } from './shared/stack.ByOugz9d.cjs';
3
4
  import '@btst/yar';
4
5
  import '@btst/db';
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { betterStack } from './api/index.mjs';
2
+ export { toNodeHandler } from 'better-call/node';
2
3
  export { f as BackendLib, e as BackendLibConfig, B as BackendPlugin } from './shared/stack.ByOugz9d.mjs';
3
4
  import '@btst/yar';
4
5
  import '@btst/db';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { betterStack } from './api/index.js';
2
+ export { toNodeHandler } from 'better-call/node';
2
3
  export { f as BackendLib, e as BackendLibConfig, B as BackendPlugin } from './shared/stack.ByOugz9d.js';
3
4
  import '@btst/yar';
4
5
  import '@btst/db';
package/dist/index.mjs CHANGED
@@ -1 +1,2 @@
1
1
  export { betterStack } from './api/index.mjs';
2
+ export { toNodeHandler } from 'better-call/node';
@@ -2,7 +2,6 @@
2
2
 
3
3
  const betterCall = require('better-call');
4
4
  const db = require('@btst/db');
5
- const node = require('better-call/node');
6
5
 
7
6
  function defineBackendPlugin(plugin) {
8
7
  return plugin;
@@ -11,5 +10,4 @@ function defineBackendPlugin(plugin) {
11
10
  exports.createEndpoint = betterCall.createEndpoint;
12
11
  exports.createRouter = betterCall.createRouter;
13
12
  exports.createDbPlugin = db.createDbPlugin;
14
- exports.toNodeHandler = node.toNodeHandler;
15
13
  exports.defineBackendPlugin = defineBackendPlugin;
@@ -3,7 +3,6 @@ export { C as ClientPlugin } from '../../shared/stack.ByOugz9d.cjs';
3
3
  import { Endpoint } from 'better-call';
4
4
  export { Endpoint, Router, createEndpoint, createRouter } from 'better-call';
5
5
  export { Adapter, DatabaseDefinition, DbPlugin, createDbPlugin } from '@btst/db';
6
- export { toNodeHandler } from 'better-call/node';
7
6
  import '@btst/yar';
8
7
 
9
8
  /**
@@ -3,7 +3,6 @@ export { C as ClientPlugin } from '../../shared/stack.ByOugz9d.mjs';
3
3
  import { Endpoint } from 'better-call';
4
4
  export { Endpoint, Router, createEndpoint, createRouter } from 'better-call';
5
5
  export { Adapter, DatabaseDefinition, DbPlugin, createDbPlugin } from '@btst/db';
6
- export { toNodeHandler } from 'better-call/node';
7
6
  import '@btst/yar';
8
7
 
9
8
  /**
@@ -3,7 +3,6 @@ export { C as ClientPlugin } from '../../shared/stack.ByOugz9d.js';
3
3
  import { Endpoint } from 'better-call';
4
4
  export { Endpoint, Router, createEndpoint, createRouter } from 'better-call';
5
5
  export { Adapter, DatabaseDefinition, DbPlugin, createDbPlugin } from '@btst/db';
6
- export { toNodeHandler } from 'better-call/node';
7
6
  import '@btst/yar';
8
7
 
9
8
  /**
@@ -1,6 +1,5 @@
1
1
  export { createEndpoint, createRouter } from 'better-call';
2
2
  export { createDbPlugin } from '@btst/db';
3
- export { toNodeHandler } from 'better-call/node';
4
3
 
5
4
  function defineBackendPlugin(plugin) {
6
5
  return plugin;
@@ -2,6 +2,6 @@ export { B as BlogApiContext, c as BlogApiRouter, a as BlogBackendHooks, N as Ne
2
2
  import '@btst/stack/plugins/api';
3
3
  import 'better-call';
4
4
  import 'zod';
5
- import '../../../shared/stack.Cr2JoQdo.cjs';
5
+ import '../../../shared/stack.CoPoHVfV.cjs';
6
6
  import '@tanstack/react-query';
7
7
  import '@btst/stack/plugins/client';
@@ -2,6 +2,6 @@ export { B as BlogApiContext, c as BlogApiRouter, a as BlogBackendHooks, N as Ne
2
2
  import '@btst/stack/plugins/api';
3
3
  import 'better-call';
4
4
  import 'zod';
5
- import '../../../shared/stack.Cr2JoQdo.mjs';
5
+ import '../../../shared/stack.CoPoHVfV.mjs';
6
6
  import '@tanstack/react-query';
7
7
  import '@btst/stack/plugins/client';
@@ -2,6 +2,6 @@ export { B as BlogApiContext, c as BlogApiRouter, a as BlogBackendHooks, N as Ne
2
2
  import '@btst/stack/plugins/api';
3
3
  import 'better-call';
4
4
  import 'zod';
5
- import '../../../shared/stack.Cr2JoQdo.js';
5
+ import '../../../shared/stack.CoPoHVfV.js';
6
6
  import '@tanstack/react-query';
7
7
  import '@btst/stack/plugins/client';
@@ -10,12 +10,12 @@ function BetterBlogAttribution() {
10
10
  "a",
11
11
  {
12
12
  className: "flex items-center gap-1 font-semibold underline",
13
- href: "https://www.btst.ai/",
13
+ href: "https://www.better-stack.ai",
14
14
  target: "_blank",
15
15
  rel: "noreferrer noopener",
16
- "aria-label": "Better Blog \u2014 open-source React blog framework",
17
- title: "Better Blog \u2014 open-source React blog framework",
18
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "cursor-pointer", children: "BTST" })
16
+ "aria-label": "Better Stack \u2014 Composable full-stack plugin system for React frameworks",
17
+ title: "Better Stack \u2014 Composable full-stack plugin system for React frameworks",
18
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "cursor-pointer", children: "Better-Stack" })
19
19
  }
20
20
  )
21
21
  ] }) });
@@ -8,12 +8,12 @@ function BetterBlogAttribution() {
8
8
  "a",
9
9
  {
10
10
  className: "flex items-center gap-1 font-semibold underline",
11
- href: "https://www.btst.ai/",
11
+ href: "https://www.better-stack.ai",
12
12
  target: "_blank",
13
13
  rel: "noreferrer noopener",
14
- "aria-label": "Better Blog \u2014 open-source React blog framework",
15
- title: "Better Blog \u2014 open-source React blog framework",
16
- children: /* @__PURE__ */ jsx("span", { className: "cursor-pointer", children: "BTST" })
14
+ "aria-label": "Better Stack \u2014 Composable full-stack plugin system for React frameworks",
15
+ title: "Better Stack \u2014 Composable full-stack plugin system for React frameworks",
16
+ children: /* @__PURE__ */ jsx("span", { className: "cursor-pointer", children: "Better-Stack" })
17
17
  }
18
18
  )
19
19
  ] }) });
@@ -1,5 +1,5 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
- import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from '../../../../shared/stack.Cr2JoQdo.cjs';
2
+ import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from '../../../../shared/stack.CoPoHVfV.cjs';
3
3
  import { z } from 'zod';
4
4
 
5
5
  interface UsePostsOptions {
@@ -81,8 +81,8 @@ declare function useSuspenseTags(): {
81
81
  };
82
82
  /** Create a new post */
83
83
  declare function useCreatePost(): _tanstack_react_query.UseMutationResult<SerializedPost | null, Error, {
84
- title: string;
85
84
  published: boolean;
85
+ title: string;
86
86
  content: string;
87
87
  excerpt: string;
88
88
  tags: ({
@@ -1,5 +1,5 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
- import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from '../../../../shared/stack.Cr2JoQdo.mjs';
2
+ import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from '../../../../shared/stack.CoPoHVfV.mjs';
3
3
  import { z } from 'zod';
4
4
 
5
5
  interface UsePostsOptions {
@@ -81,8 +81,8 @@ declare function useSuspenseTags(): {
81
81
  };
82
82
  /** Create a new post */
83
83
  declare function useCreatePost(): _tanstack_react_query.UseMutationResult<SerializedPost | null, Error, {
84
- title: string;
85
84
  published: boolean;
85
+ title: string;
86
86
  content: string;
87
87
  excerpt: string;
88
88
  tags: ({
@@ -1,5 +1,5 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
- import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from '../../../../shared/stack.Cr2JoQdo.js';
2
+ import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from '../../../../shared/stack.CoPoHVfV.js';
3
3
  import { z } from 'zod';
4
4
 
5
5
  interface UsePostsOptions {
@@ -81,8 +81,8 @@ declare function useSuspenseTags(): {
81
81
  };
82
82
  /** Create a new post */
83
83
  declare function useCreatePost(): _tanstack_react_query.UseMutationResult<SerializedPost | null, Error, {
84
- title: string;
85
84
  published: boolean;
85
+ title: string;
86
86
  content: string;
87
87
  excerpt: string;
88
88
  tags: ({
@@ -2,7 +2,7 @@ import * as react from 'react';
2
2
  import { ComponentType } from 'react';
3
3
  import * as _btst_yar from '@btst/yar';
4
4
  import { QueryClient } from '@tanstack/react-query';
5
- import { P as Post, S as SerializedPost } from '../../../shared/stack.Cr2JoQdo.cjs';
5
+ import { P as Post, S as SerializedPost } from '../../../shared/stack.CoPoHVfV.cjs';
6
6
  export { UsePostsOptions, UsePostsResult } from './hooks/index.cjs';
7
7
  import 'zod';
8
8
 
@@ -2,7 +2,7 @@ import * as react from 'react';
2
2
  import { ComponentType } from 'react';
3
3
  import * as _btst_yar from '@btst/yar';
4
4
  import { QueryClient } from '@tanstack/react-query';
5
- import { P as Post, S as SerializedPost } from '../../../shared/stack.Cr2JoQdo.mjs';
5
+ import { P as Post, S as SerializedPost } from '../../../shared/stack.CoPoHVfV.mjs';
6
6
  export { UsePostsOptions, UsePostsResult } from './hooks/index.mjs';
7
7
  import 'zod';
8
8
 
@@ -2,7 +2,7 @@ import * as react from 'react';
2
2
  import { ComponentType } from 'react';
3
3
  import * as _btst_yar from '@btst/yar';
4
4
  import { QueryClient } from '@tanstack/react-query';
5
- import { P as Post, S as SerializedPost } from '../../../shared/stack.Cr2JoQdo.js';
5
+ import { P as Post, S as SerializedPost } from '../../../shared/stack.CoPoHVfV.js';
6
6
  export { UsePostsOptions, UsePostsResult } from './hooks/index.js';
7
7
  import 'zod';
8
8
 
@@ -1,7 +1,7 @@
1
1
  import * as _btst_stack_plugins_api from '@btst/stack/plugins/api';
2
2
  import * as better_call from 'better-call';
3
3
  import { z } from 'zod';
4
- import { c as createPostSchema, u as updatePostSchema, P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../shared/stack.Cr2JoQdo.cjs';
4
+ import { c as createPostSchema, u as updatePostSchema, P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../shared/stack.CoPoHVfV.cjs';
5
5
  import * as _tanstack_react_query from '@tanstack/react-query';
6
6
  import { createApiClient } from '@btst/stack/plugins/client';
7
7
 
@@ -172,12 +172,12 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
172
172
  options: {
173
173
  method: "POST";
174
174
  body: z.ZodObject<{
175
- title: z.ZodString;
176
175
  slug: z.ZodOptional<z.ZodString>;
177
176
  published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
178
177
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
179
178
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
180
179
  updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
180
+ title: z.ZodString;
181
181
  content: z.ZodString;
182
182
  excerpt: z.ZodString;
183
183
  image: z.ZodOptional<z.ZodString>;
@@ -1,7 +1,7 @@
1
1
  import * as _btst_stack_plugins_api from '@btst/stack/plugins/api';
2
2
  import * as better_call from 'better-call';
3
3
  import { z } from 'zod';
4
- import { c as createPostSchema, u as updatePostSchema, P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../shared/stack.Cr2JoQdo.mjs';
4
+ import { c as createPostSchema, u as updatePostSchema, P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../shared/stack.CoPoHVfV.mjs';
5
5
  import * as _tanstack_react_query from '@tanstack/react-query';
6
6
  import { createApiClient } from '@btst/stack/plugins/client';
7
7
 
@@ -172,12 +172,12 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
172
172
  options: {
173
173
  method: "POST";
174
174
  body: z.ZodObject<{
175
- title: z.ZodString;
176
175
  slug: z.ZodOptional<z.ZodString>;
177
176
  published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
178
177
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
179
178
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
180
179
  updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
180
+ title: z.ZodString;
181
181
  content: z.ZodString;
182
182
  excerpt: z.ZodString;
183
183
  image: z.ZodOptional<z.ZodString>;
@@ -1,7 +1,7 @@
1
1
  import * as _btst_stack_plugins_api from '@btst/stack/plugins/api';
2
2
  import * as better_call from 'better-call';
3
3
  import { z } from 'zod';
4
- import { c as createPostSchema, u as updatePostSchema, P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../shared/stack.Cr2JoQdo.js';
4
+ import { c as createPostSchema, u as updatePostSchema, P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../shared/stack.CoPoHVfV.js';
5
5
  import * as _tanstack_react_query from '@tanstack/react-query';
6
6
  import { createApiClient } from '@btst/stack/plugins/client';
7
7
 
@@ -172,12 +172,12 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
172
172
  options: {
173
173
  method: "POST";
174
174
  body: z.ZodObject<{
175
- title: z.ZodString;
176
175
  slug: z.ZodOptional<z.ZodString>;
177
176
  published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
178
177
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
179
178
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
180
179
  updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
180
+ title: z.ZodString;
181
181
  content: z.ZodString;
182
182
  excerpt: z.ZodString;
183
183
  image: z.ZodOptional<z.ZodString>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btst/stack",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "A composable, plugin-based library for building full-stack applications.",
5
5
  "repository": {
6
6
  "type": "git",
package/src/api/index.ts CHANGED
@@ -6,6 +6,8 @@ import type {
6
6
  } from "../types";
7
7
  import { defineDb } from "@btst/db";
8
8
 
9
+ export { toNodeHandler } from "better-call/node";
10
+
9
11
  /**
10
12
  * Creates the backend library with plugin support
11
13
  *
@@ -105,3 +105,5 @@ export type { ClientLib, ClientLibConfig };
105
105
  export { sitemapEntryToXmlString } from "./sitemap-utils";
106
106
 
107
107
  export { metaElementsToObject } from "./meta-utils";
108
+
109
+ export { normalizePath } from "./path-utils";
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Normalizes path segments from framework route params into a consistent path string.
3
+ *
4
+ * Handles different framework param formats:
5
+ * - Next.js: `pathParams.all` (string[])
6
+ * - React Router: `params["*"]` (string)
7
+ * - TanStack Router: `params._splat` (string)
8
+ *
9
+ * @param path - Path segments as string, string array, or undefined
10
+ * @returns Normalized path string starting with "/" (or "/" for empty/undefined)
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * // Next.js
15
+ * normalizePath(pathParams?.all) // ["blog", "post"] => "/blog/post"
16
+ *
17
+ * // React Router / TanStack Router
18
+ * normalizePath(params["*"]) // "blog/post" => "/blog/post"
19
+ * normalizePath(undefined) // => "/"
20
+ * ```
21
+ */
22
+ export function normalizePath(
23
+ path?: string | Array<string> | undefined,
24
+ ): string {
25
+ if (!path) {
26
+ return "/";
27
+ }
28
+
29
+ if (Array.isArray(path)) {
30
+ // Handle Next.js format: pathParams.all (string[])
31
+ const segments = path.filter(Boolean);
32
+ return segments.length > 0 ? `/${segments.join("/")}` : "/";
33
+ }
34
+
35
+ // Handle React Router / TanStack Router format: params["*"] or params._splat (string)
36
+ const segments = path.split("/").filter(Boolean);
37
+ return segments.length > 0 ? `/${segments.join("/")}` : "/";
38
+ }
@@ -24,7 +24,6 @@ export type { Adapter, DatabaseDefinition, DbPlugin } from "@btst/db";
24
24
  export type { Endpoint, Router } from "better-call";
25
25
  export { createEndpoint, createRouter } from "better-call";
26
26
  export { createDbPlugin } from "@btst/db";
27
- export { toNodeHandler } from "better-call/node";
28
27
 
29
28
  /**
30
29
  * Helper to define a backend plugin with full type inference
@@ -5,13 +5,13 @@ export function BetterBlogAttribution() {
5
5
  Powered by{" "}
6
6
  <a
7
7
  className="flex items-center gap-1 font-semibold underline"
8
- href="https://www.btst.ai/"
8
+ href="https://www.better-stack.ai"
9
9
  target="_blank"
10
10
  rel="noreferrer noopener"
11
- aria-label="Better Blogopen-source React blog framework"
12
- title="Better Blogopen-source React blog framework"
11
+ aria-label="Better StackComposable full-stack plugin system for React frameworks"
12
+ title="Better StackComposable full-stack plugin system for React frameworks"
13
13
  >
14
- <span className="cursor-pointer">BTST</span>
14
+ <span className="cursor-pointer">Better-Stack</span>
15
15
  </a>
16
16
  </p>
17
17
  </div>
@@ -35,12 +35,12 @@ interface SerializedTag extends Omit<Tag, "createdAt" | "updatedAt"> {
35
35
  }
36
36
 
37
37
  declare const createPostSchema: z.ZodObject<{
38
- title: z.ZodString;
39
38
  slug: z.ZodOptional<z.ZodString>;
40
39
  published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
41
40
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
42
41
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
43
42
  updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
43
+ title: z.ZodString;
44
44
  content: z.ZodString;
45
45
  excerpt: z.ZodString;
46
46
  image: z.ZodOptional<z.ZodString>;
@@ -35,12 +35,12 @@ interface SerializedTag extends Omit<Tag, "createdAt" | "updatedAt"> {
35
35
  }
36
36
 
37
37
  declare const createPostSchema: z.ZodObject<{
38
- title: z.ZodString;
39
38
  slug: z.ZodOptional<z.ZodString>;
40
39
  published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
41
40
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
42
41
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
43
42
  updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
43
+ title: z.ZodString;
44
44
  content: z.ZodString;
45
45
  excerpt: z.ZodString;
46
46
  image: z.ZodOptional<z.ZodString>;
@@ -35,12 +35,12 @@ interface SerializedTag extends Omit<Tag, "createdAt" | "updatedAt"> {
35
35
  }
36
36
 
37
37
  declare const createPostSchema: z.ZodObject<{
38
- title: z.ZodString;
39
38
  slug: z.ZodOptional<z.ZodString>;
40
39
  published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
41
40
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
42
41
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
43
42
  updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
43
+ title: z.ZodString;
44
44
  content: z.ZodString;
45
45
  excerpt: z.ZodString;
46
46
  image: z.ZodOptional<z.ZodString>;