@btst/stack 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,138 @@
1
+ import { Route, createRouter } from '@btst/yar';
2
+ import { Adapter, DbPlugin, DatabaseDefinition } from '@btst/db';
3
+ import { Endpoint, Router } from 'better-call';
4
+
5
+ /**
6
+ * Backend plugin definition
7
+ * Defines API routes and data access for a feature
8
+ *
9
+ * Note: Each plugin defines its own schema using createDbPlugin().
10
+ * Better Stack composes all plugin schemas together at runtime using Better DB's .use() method.
11
+ * You can optionally provide a base schema via the dbSchema config option.
12
+ *
13
+ * @template TRoutes - The exact shape of routes this plugin provides (preserves keys and endpoint types)
14
+ */
15
+ interface BackendPlugin<TRoutes extends Record<string, Endpoint> = Record<string, Endpoint>> {
16
+ name: string;
17
+ /**
18
+ * Create API endpoints for this plugin
19
+ * Returns an object with named endpoints that will be composed into the router
20
+ *
21
+ * @param adapter - Better DB adapter instance with methods:
22
+ * create, update, updateMany, delete, deleteMany, findOne, findMany, count
23
+ */
24
+ routes: (adapter: Adapter) => TRoutes;
25
+ dbPlugin: DbPlugin;
26
+ }
27
+ /**
28
+ * Hook function type
29
+ * Generic function type for React hooks returned by plugins
30
+ */
31
+ type HookFunction = (...args: unknown[]) => unknown;
32
+ /**
33
+ * Frontend plugin definition
34
+ * Defines pages, components, loaders, and React Query hooks for a feature
35
+ *
36
+ * @template TOverrides - The shape of overridable components/functions this plugin requires
37
+ * Example: { Link: ComponentType<{href: string}>, navigate: (path: string) => void }
38
+ * @template TRoutes - The exact shape of routes this plugin provides (preserves keys and route types)
39
+ */
40
+ interface ClientPlugin<TOverrides = Record<string, never>, TRoutes extends Record<string, Route> = Record<string, Route>> {
41
+ name: string;
42
+ /**
43
+ * Define routes (pages) for this plugin
44
+ * Returns yar routes that will be composed into the router
45
+ */
46
+ routes: () => TRoutes;
47
+ /**
48
+ * Optional: Create React Query hooks for this plugin
49
+ * These can be used directly in components without the loader
50
+ */
51
+ hooks?: () => Record<string, HookFunction>;
52
+ /**
53
+ * Optional: Default implementations for overridable components/functions
54
+ * These will be used if no override is provided in BetterStackContext
55
+ */
56
+ defaultOverrides?: Partial<TOverrides>;
57
+ }
58
+ /**
59
+ * Configuration for creating the backend library
60
+ */
61
+ interface BackendLibConfig<TPlugins extends Record<string, BackendPlugin<any>> = Record<string, BackendPlugin<any>>> {
62
+ dbSchema?: DatabaseDefinition;
63
+ plugins: TPlugins;
64
+ adapter: (db: DatabaseDefinition) => Adapter;
65
+ }
66
+ /**
67
+ * Configuration for creating the client library
68
+ */
69
+ interface ClientLibConfig<TPlugins extends Record<string, ClientPlugin<any, any>> = Record<string, ClientPlugin<any, any>>> {
70
+ plugins: TPlugins;
71
+ baseURL?: string;
72
+ basePath?: string;
73
+ }
74
+ /**
75
+ * Utility type to extract override types from plugins
76
+ * Maps plugin names to their override types
77
+ */
78
+ type InferPluginOverrides<TPlugins extends Record<string, ClientPlugin<any, any>>> = {
79
+ [K in keyof TPlugins]: TPlugins[K] extends ClientPlugin<infer TOverrides, any> ? TOverrides : never;
80
+ };
81
+ /**
82
+ * Type for the pluginOverrides prop in BetterStackContext
83
+ * Allows partial overrides per plugin
84
+ */
85
+ type PluginOverrides<TPlugins extends Record<string, ClientPlugin<any, any>>> = {
86
+ [K in keyof TPlugins]?: Partial<InferPluginOverrides<TPlugins>[K]>;
87
+ };
88
+ /**
89
+ * Extract all routes from all client plugins, merging them into a single record
90
+ */
91
+ type PluginRoutes<TPlugins extends Record<string, ClientPlugin<any, any>>> = MergeAllPluginRoutes<TPlugins>;
92
+ /**
93
+ * Extract all hooks from all client plugins, organized by plugin name
94
+ * For plugins without hooks, the type will be an empty object
95
+ */
96
+ type PluginHooks<TPlugins extends Record<string, ClientPlugin<any, any>>> = {
97
+ [K in keyof TPlugins]: TPlugins[K]["hooks"] extends () => infer H ? H : {};
98
+ };
99
+ /**
100
+ * Prefix all backend plugin route keys with the plugin name
101
+ * Example: { messages: { list: Endpoint } } => { messages_list: Endpoint }
102
+ */
103
+ type PrefixedPluginRoutes<TPlugins extends Record<string, BackendPlugin<any>>> = UnionToIntersection<{
104
+ [PluginKey in keyof TPlugins]: TPlugins[PluginKey] extends BackendPlugin<infer TRoutes> ? {
105
+ [RouteKey in keyof TRoutes as `${PluginKey & string}_${RouteKey & string}`]: TRoutes[RouteKey];
106
+ } : never;
107
+ }[keyof TPlugins]> extends infer U ? U extends Record<string, Endpoint> ? U : Record<string, Endpoint> : Record<string, Endpoint>;
108
+ /**
109
+ * Result of creating the backend library
110
+ */
111
+ interface BackendLib<TRoutes extends Record<string, Endpoint> = Record<string, Endpoint>> {
112
+ handler: (request: Request) => Promise<Response>;
113
+ router: Router;
114
+ dbSchema: DatabaseDefinition;
115
+ }
116
+ /**
117
+ * Helper type to extract routes from a client plugin
118
+ */
119
+ type ExtractPluginRoutes<T> = T extends ClientPlugin<any, infer TRoutes> ? TRoutes : never;
120
+ /**
121
+ * Helper type to merge all routes from all plugins into a single record
122
+ */
123
+ type MergeAllPluginRoutes<TPlugins extends Record<string, ClientPlugin<any, any>>> = UnionToIntersection<{
124
+ [K in keyof TPlugins]: ExtractPluginRoutes<TPlugins[K]>;
125
+ }[keyof TPlugins]> extends infer U ? U extends Record<string, Route> ? U : Record<string, Route> : Record<string, Route>;
126
+ /**
127
+ * Utility type to convert union to intersection
128
+ */
129
+ type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
130
+ /**
131
+ * Result of creating the client library
132
+ */
133
+ interface ClientLib<TRoutes extends Record<string, Route> = Record<string, Route>, THooks extends Record<string, any> = Record<string, any>> {
134
+ router: ReturnType<typeof createRouter<TRoutes, {}>>;
135
+ hooks: THooks;
136
+ }
137
+
138
+ export type { BackendPlugin as B, ClientPlugin as C, InferPluginOverrides as I, PluginRoutes as P, PluginHooks as a, ClientLibConfig as b, ClientLib as c, PluginOverrides as d, PrefixedPluginRoutes as e, BackendLibConfig as f, BackendLib as g };
@@ -0,0 +1,138 @@
1
+ import { Route, createRouter } from '@btst/yar';
2
+ import { Adapter, DbPlugin, DatabaseDefinition } from '@btst/db';
3
+ import { Endpoint, Router } from 'better-call';
4
+
5
+ /**
6
+ * Backend plugin definition
7
+ * Defines API routes and data access for a feature
8
+ *
9
+ * Note: Each plugin defines its own schema using createDbPlugin().
10
+ * Better Stack composes all plugin schemas together at runtime using Better DB's .use() method.
11
+ * You can optionally provide a base schema via the dbSchema config option.
12
+ *
13
+ * @template TRoutes - The exact shape of routes this plugin provides (preserves keys and endpoint types)
14
+ */
15
+ interface BackendPlugin<TRoutes extends Record<string, Endpoint> = Record<string, Endpoint>> {
16
+ name: string;
17
+ /**
18
+ * Create API endpoints for this plugin
19
+ * Returns an object with named endpoints that will be composed into the router
20
+ *
21
+ * @param adapter - Better DB adapter instance with methods:
22
+ * create, update, updateMany, delete, deleteMany, findOne, findMany, count
23
+ */
24
+ routes: (adapter: Adapter) => TRoutes;
25
+ dbPlugin: DbPlugin;
26
+ }
27
+ /**
28
+ * Hook function type
29
+ * Generic function type for React hooks returned by plugins
30
+ */
31
+ type HookFunction = (...args: unknown[]) => unknown;
32
+ /**
33
+ * Frontend plugin definition
34
+ * Defines pages, components, loaders, and React Query hooks for a feature
35
+ *
36
+ * @template TOverrides - The shape of overridable components/functions this plugin requires
37
+ * Example: { Link: ComponentType<{href: string}>, navigate: (path: string) => void }
38
+ * @template TRoutes - The exact shape of routes this plugin provides (preserves keys and route types)
39
+ */
40
+ interface ClientPlugin<TOverrides = Record<string, never>, TRoutes extends Record<string, Route> = Record<string, Route>> {
41
+ name: string;
42
+ /**
43
+ * Define routes (pages) for this plugin
44
+ * Returns yar routes that will be composed into the router
45
+ */
46
+ routes: () => TRoutes;
47
+ /**
48
+ * Optional: Create React Query hooks for this plugin
49
+ * These can be used directly in components without the loader
50
+ */
51
+ hooks?: () => Record<string, HookFunction>;
52
+ /**
53
+ * Optional: Default implementations for overridable components/functions
54
+ * These will be used if no override is provided in BetterStackContext
55
+ */
56
+ defaultOverrides?: Partial<TOverrides>;
57
+ }
58
+ /**
59
+ * Configuration for creating the backend library
60
+ */
61
+ interface BackendLibConfig<TPlugins extends Record<string, BackendPlugin<any>> = Record<string, BackendPlugin<any>>> {
62
+ dbSchema?: DatabaseDefinition;
63
+ plugins: TPlugins;
64
+ adapter: (db: DatabaseDefinition) => Adapter;
65
+ }
66
+ /**
67
+ * Configuration for creating the client library
68
+ */
69
+ interface ClientLibConfig<TPlugins extends Record<string, ClientPlugin<any, any>> = Record<string, ClientPlugin<any, any>>> {
70
+ plugins: TPlugins;
71
+ baseURL?: string;
72
+ basePath?: string;
73
+ }
74
+ /**
75
+ * Utility type to extract override types from plugins
76
+ * Maps plugin names to their override types
77
+ */
78
+ type InferPluginOverrides<TPlugins extends Record<string, ClientPlugin<any, any>>> = {
79
+ [K in keyof TPlugins]: TPlugins[K] extends ClientPlugin<infer TOverrides, any> ? TOverrides : never;
80
+ };
81
+ /**
82
+ * Type for the pluginOverrides prop in BetterStackContext
83
+ * Allows partial overrides per plugin
84
+ */
85
+ type PluginOverrides<TPlugins extends Record<string, ClientPlugin<any, any>>> = {
86
+ [K in keyof TPlugins]?: Partial<InferPluginOverrides<TPlugins>[K]>;
87
+ };
88
+ /**
89
+ * Extract all routes from all client plugins, merging them into a single record
90
+ */
91
+ type PluginRoutes<TPlugins extends Record<string, ClientPlugin<any, any>>> = MergeAllPluginRoutes<TPlugins>;
92
+ /**
93
+ * Extract all hooks from all client plugins, organized by plugin name
94
+ * For plugins without hooks, the type will be an empty object
95
+ */
96
+ type PluginHooks<TPlugins extends Record<string, ClientPlugin<any, any>>> = {
97
+ [K in keyof TPlugins]: TPlugins[K]["hooks"] extends () => infer H ? H : {};
98
+ };
99
+ /**
100
+ * Prefix all backend plugin route keys with the plugin name
101
+ * Example: { messages: { list: Endpoint } } => { messages_list: Endpoint }
102
+ */
103
+ type PrefixedPluginRoutes<TPlugins extends Record<string, BackendPlugin<any>>> = UnionToIntersection<{
104
+ [PluginKey in keyof TPlugins]: TPlugins[PluginKey] extends BackendPlugin<infer TRoutes> ? {
105
+ [RouteKey in keyof TRoutes as `${PluginKey & string}_${RouteKey & string}`]: TRoutes[RouteKey];
106
+ } : never;
107
+ }[keyof TPlugins]> extends infer U ? U extends Record<string, Endpoint> ? U : Record<string, Endpoint> : Record<string, Endpoint>;
108
+ /**
109
+ * Result of creating the backend library
110
+ */
111
+ interface BackendLib<TRoutes extends Record<string, Endpoint> = Record<string, Endpoint>> {
112
+ handler: (request: Request) => Promise<Response>;
113
+ router: Router;
114
+ dbSchema: DatabaseDefinition;
115
+ }
116
+ /**
117
+ * Helper type to extract routes from a client plugin
118
+ */
119
+ type ExtractPluginRoutes<T> = T extends ClientPlugin<any, infer TRoutes> ? TRoutes : never;
120
+ /**
121
+ * Helper type to merge all routes from all plugins into a single record
122
+ */
123
+ type MergeAllPluginRoutes<TPlugins extends Record<string, ClientPlugin<any, any>>> = UnionToIntersection<{
124
+ [K in keyof TPlugins]: ExtractPluginRoutes<TPlugins[K]>;
125
+ }[keyof TPlugins]> extends infer U ? U extends Record<string, Route> ? U : Record<string, Route> : Record<string, Route>;
126
+ /**
127
+ * Utility type to convert union to intersection
128
+ */
129
+ type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
130
+ /**
131
+ * Result of creating the client library
132
+ */
133
+ interface ClientLib<TRoutes extends Record<string, Route> = Record<string, Route>, THooks extends Record<string, any> = Record<string, any>> {
134
+ router: ReturnType<typeof createRouter<TRoutes, {}>>;
135
+ hooks: THooks;
136
+ }
137
+
138
+ export type { BackendPlugin as B, ClientPlugin as C, InferPluginOverrides as I, PluginRoutes as P, PluginHooks as a, ClientLibConfig as b, ClientLib as c, PluginOverrides as d, PrefixedPluginRoutes as e, BackendLibConfig as f, BackendLib as g };
package/package.json ADDED
@@ -0,0 +1,129 @@
1
+ {
2
+ "name": "@btst/stack",
3
+ "version": "1.0.0",
4
+ "description": "A composable, plugin-based library for building full-stack applications.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/olliethedev/better-stack.git"
8
+ },
9
+ "keywords": [
10
+ "fullstack",
11
+ "react",
12
+ "nextjs"
13
+ ],
14
+ "author": "olliethedev",
15
+ "license": "MIT",
16
+ "bugs": {
17
+ "url": "https://github.com/olliethedev/better-stack/issues"
18
+ },
19
+ "homepage": "https://github.com/olliethedev/better-stack#readme",
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "scripts": {
24
+ "build": "unbuild --clean",
25
+ "stub": "unbuild --stub",
26
+ "test": "vitest",
27
+ "typecheck": "tsc --project tsconfig.json"
28
+ },
29
+ "main": "./dist/index.cjs",
30
+ "module": "./dist/index.mjs",
31
+ "exports": {
32
+ ".": {
33
+ "import": {
34
+ "types": "./dist/index.d.ts",
35
+ "default": "./dist/index.mjs"
36
+ },
37
+ "require": {
38
+ "types": "./dist/index.d.cts",
39
+ "default": "./dist/index.cjs"
40
+ }
41
+ },
42
+ "./api": {
43
+ "import": {
44
+ "types": "./dist/api/index.d.ts",
45
+ "default": "./dist/api/index.mjs"
46
+ },
47
+ "require": {
48
+ "types": "./dist/api/index.d.cts",
49
+ "default": "./dist/api/index.cjs"
50
+ }
51
+ },
52
+ "./client": {
53
+ "import": {
54
+ "types": "./dist/client/index.d.ts",
55
+ "default": "./dist/client/index.mjs"
56
+ },
57
+ "require": {
58
+ "types": "./dist/client/index.d.cts",
59
+ "default": "./dist/client/index.cjs"
60
+ }
61
+ },
62
+ "./context": {
63
+ "import": {
64
+ "types": "./dist/context/index.d.ts",
65
+ "default": "./dist/context/index.mjs"
66
+ },
67
+ "require": {
68
+ "types": "./dist/context/index.d.cts",
69
+ "default": "./dist/context/index.cjs"
70
+ }
71
+ },
72
+ "./plugins": {
73
+ "import": {
74
+ "types": "./dist/plugins/index.d.ts",
75
+ "default": "./dist/plugins/index.mjs"
76
+ },
77
+ "require": {
78
+ "types": "./dist/plugins/index.d.cts",
79
+ "default": "./dist/plugins/index.cjs"
80
+ }
81
+ },
82
+ "./package.json": "./package.json"
83
+ },
84
+ "typesVersions": {
85
+ "*": {
86
+ "*": [
87
+ "./dist/index.d.ts"
88
+ ],
89
+ "api": [
90
+ "./dist/api/index.d.ts"
91
+ ],
92
+ "client": [
93
+ "./dist/client/index.d.ts"
94
+ ],
95
+ "context": [
96
+ "./dist/context/index.d.ts"
97
+ ],
98
+ "plugins": [
99
+ "./dist/plugins/index.d.ts"
100
+ ]
101
+ }
102
+ },
103
+ "dependencies": {
104
+ "@btst/db": "1.0.2",
105
+ "@btst/yar": "1.1.0",
106
+ "better-call": "1.0.19",
107
+ "zod": "^4.1.5"
108
+ },
109
+ "peerDependencies": {
110
+ "react": "^18.0.0 || ^19.0.0",
111
+ "react-dom": "^18.0.0 || ^19.0.0",
112
+ "@tanstack/react-query": "^5.0.0"
113
+ },
114
+ "devDependencies": {
115
+ "@btst/adapter-memory": "1.0.2",
116
+ "@tanstack/react-query": "^5.0.0",
117
+ "@types/react": "^18.3.23",
118
+ "react": "^19.1.1",
119
+ "react-dom": "^19.1.1",
120
+ "typescript": "catalog:",
121
+ "unbuild": "catalog:",
122
+ "vitest": "catalog:",
123
+ "vue": "^3.5.18"
124
+ },
125
+ "sideEffects": false,
126
+ "files": [
127
+ "dist"
128
+ ]
129
+ }