@facetlayer/prism-framework 0.4.0 → 0.4.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 (130) hide show
  1. package/README.md +176 -8
  2. package/dist/Errors.d.ts +38 -0
  3. package/dist/Errors.d.ts.map +1 -0
  4. package/dist/Metrics.d.ts +5 -0
  5. package/dist/Metrics.d.ts.map +1 -0
  6. package/dist/RequestContext.d.ts +17 -0
  7. package/dist/RequestContext.d.ts.map +1 -0
  8. package/dist/ServiceDefinition.d.ts +16 -0
  9. package/dist/ServiceDefinition.d.ts.map +1 -0
  10. package/dist/app/PrismApp.d.ts +31 -0
  11. package/dist/app/PrismApp.d.ts.map +1 -0
  12. package/dist/app/callEndpoint.d.ts +13 -0
  13. package/dist/app/callEndpoint.d.ts.map +1 -0
  14. package/dist/app/validateApp.d.ts +20 -0
  15. package/dist/app/validateApp.d.ts.map +1 -0
  16. package/dist/authorization/AuthSource.d.ts +8 -0
  17. package/dist/authorization/AuthSource.d.ts.map +1 -0
  18. package/dist/authorization/Authorization.d.ts +24 -0
  19. package/dist/authorization/Authorization.d.ts.map +1 -0
  20. package/dist/authorization/Resource.d.ts +5 -0
  21. package/dist/authorization/Resource.d.ts.map +1 -0
  22. package/dist/authorization/index.d.ts +5 -0
  23. package/dist/authorization/index.d.ts.map +1 -0
  24. package/dist/cli.js +1 -1
  25. package/dist/databases/DatabaseInitializationOptions.d.ts +9 -0
  26. package/dist/databases/DatabaseInitializationOptions.d.ts.map +1 -0
  27. package/dist/databases/DatabaseSetup.d.ts +3 -0
  28. package/dist/databases/DatabaseSetup.d.ts.map +1 -0
  29. package/dist/endpoints/createEndpoint.d.ts +4 -0
  30. package/dist/endpoints/createEndpoint.d.ts.map +1 -0
  31. package/dist/endpoints/getEffectiveOperationId.d.ts +19 -0
  32. package/dist/endpoints/getEffectiveOperationId.d.ts.map +1 -0
  33. package/dist/env/Env.d.ts +2 -0
  34. package/dist/env/Env.d.ts.map +1 -0
  35. package/dist/index.d.ts +34 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +1364 -0
  38. package/dist/launch/launchConfig.d.ts +18 -0
  39. package/dist/launch/launchConfig.d.ts.map +1 -0
  40. package/dist/logging/index.d.ts +9 -0
  41. package/dist/logging/index.d.ts.map +1 -0
  42. package/dist/sse/ConnectionManager.d.ts +23 -0
  43. package/dist/sse/ConnectionManager.d.ts.map +1 -0
  44. package/dist/stdin/StdinServer.d.ts +38 -0
  45. package/dist/stdin/StdinServer.d.ts.map +1 -0
  46. package/dist/web/EndpointListing.d.ts +3 -0
  47. package/dist/web/EndpointListing.d.ts.map +1 -0
  48. package/dist/web/ExpressAppSetup.d.ts +18 -0
  49. package/dist/web/ExpressAppSetup.d.ts.map +1 -0
  50. package/dist/web/ExpressEndpointSetup.d.ts +31 -0
  51. package/dist/web/ExpressEndpointSetup.d.ts.map +1 -0
  52. package/dist/web/SseResponse.d.ts +15 -0
  53. package/dist/web/SseResponse.d.ts.map +1 -0
  54. package/dist/web/ViteIntegration.d.ts +19 -0
  55. package/dist/web/ViteIntegration.d.ts.map +1 -0
  56. package/dist/web/corsMiddleware.d.ts +14 -0
  57. package/dist/web/corsMiddleware.d.ts.map +1 -0
  58. package/dist/web/localhostOnlyMiddleware.d.ts +3 -0
  59. package/dist/web/localhostOnlyMiddleware.d.ts.map +1 -0
  60. package/dist/web/openapi/OpenAPI.d.ts +37 -0
  61. package/dist/web/openapi/OpenAPI.d.ts.map +1 -0
  62. package/dist/web/openapi/validateServicesForOpenapi.d.ts +32 -0
  63. package/dist/web/openapi/validateServicesForOpenapi.d.ts.map +1 -0
  64. package/dist/web/requestContextMiddleware.d.ts +3 -0
  65. package/dist/web/requestContextMiddleware.d.ts.map +1 -0
  66. package/docs/authorization.md +281 -0
  67. package/docs/cors-setup.md +172 -0
  68. package/docs/creating-services.md +220 -0
  69. package/docs/database-setup.md +134 -0
  70. package/docs/endpoint-tools.md +1 -11
  71. package/docs/env-files.md +12 -1
  72. package/docs/error-handling.md +70 -0
  73. package/docs/getting-started.md +22 -12
  74. package/docs/launch-configuration.md +223 -0
  75. package/docs/overview.md +62 -0
  76. package/docs/server-setup.md +144 -0
  77. package/docs/source-directory-organization.md +115 -0
  78. package/docs/stdin-protocol.md +176 -0
  79. package/package.json +42 -9
  80. package/src/Errors.ts +120 -0
  81. package/src/Metrics.ts +53 -0
  82. package/src/RequestContext.ts +36 -0
  83. package/src/ServiceDefinition.ts +35 -0
  84. package/src/__tests__/Authorization.test.ts +350 -0
  85. package/src/__tests__/Errors.test.ts +378 -0
  86. package/src/__tests__/ListEndpoints.test.ts +98 -0
  87. package/src/__tests__/PrismApp.test.ts +274 -0
  88. package/src/__tests__/RequestContext.test.ts +295 -0
  89. package/src/__tests__/SseResponse.test.ts +189 -0
  90. package/src/__tests__/StdinServer.test.ts +304 -0
  91. package/src/__tests__/corsMiddleware.test.ts +293 -0
  92. package/src/__tests__/createEndpoint.test.ts +412 -0
  93. package/src/__tests__/validateApp.test.ts +206 -0
  94. package/src/app/PrismApp.ts +117 -0
  95. package/src/app/callEndpoint.ts +55 -0
  96. package/src/app/validateApp.ts +78 -0
  97. package/src/authorization/AuthSource.ts +14 -0
  98. package/src/authorization/Authorization.ts +78 -0
  99. package/src/authorization/Resource.ts +8 -0
  100. package/src/authorization/index.ts +4 -0
  101. package/src/databases/DatabaseInitializationOptions.ts +9 -0
  102. package/src/databases/DatabaseSetup.ts +19 -0
  103. package/src/endpoints/createEndpoint.ts +39 -0
  104. package/src/endpoints/getEffectiveOperationId.ts +90 -0
  105. package/src/env/Env.ts +23 -0
  106. package/src/index.ts +78 -0
  107. package/src/launch/launchConfig.ts +59 -0
  108. package/src/list-endpoints-command.ts +1 -1
  109. package/src/logging/index.ts +25 -0
  110. package/src/sse/ConnectionManager.ts +79 -0
  111. package/src/stdin/StdinServer.ts +129 -0
  112. package/src/web/EndpointListing.ts +166 -0
  113. package/src/web/ExpressAppSetup.ts +125 -0
  114. package/src/web/ExpressEndpointSetup.ts +178 -0
  115. package/src/web/SseResponse.ts +78 -0
  116. package/src/web/ViteIntegration.ts +72 -0
  117. package/src/web/__tests__/OpenAPI.invalidZodSchemas.test.ts +250 -0
  118. package/src/web/corsMiddleware.ts +63 -0
  119. package/src/web/localhostOnlyMiddleware.ts +19 -0
  120. package/src/web/openapi/OpenAPI.ts +248 -0
  121. package/src/web/openapi/validateServicesForOpenapi.ts +76 -0
  122. package/src/web/requestContextMiddleware.ts +25 -0
  123. package/.claude/settings.local.json +0 -20
  124. package/CHANGELOG +0 -28
  125. package/CLAUDE.md +0 -44
  126. package/build.mts +0 -8
  127. package/test/call-command.test.ts +0 -96
  128. package/test/generate-api-clients.test.ts +0 -33
  129. package/test/generate-api-clients.test.ts.disabled +0 -75
  130. package/tsconfig.json +0 -21
@@ -0,0 +1,172 @@
1
+ ---
2
+ name: cors-setup
3
+ description: How to configure CORS (Cross-Origin Resource Sharing) for your Prism API server
4
+ ---
5
+
6
+ # CORS Setup
7
+
8
+ This guide explains how to configure CORS (Cross-Origin Resource Sharing) for your Prism Framework API server.
9
+
10
+ ## Overview
11
+
12
+ CORS controls which web origins can make requests to your API. The Prism Framework includes built-in CORS middleware that handles preflight requests and sets appropriate headers.
13
+
14
+ ## Configuration Options
15
+
16
+ Pass a `corsConfig` object to `startServer` (see `server-setup` doc for full `startServer` details):
17
+
18
+ ```typescript
19
+ await startServer({
20
+ app,
21
+ port: 4000,
22
+ corsConfig: {
23
+ webBaseUrl: 'example.com',
24
+ },
25
+ });
26
+ ```
27
+
28
+ ### CorsConfig Interface
29
+
30
+ ```typescript
31
+ interface CorsConfig {
32
+ /** Base URL for web application (e.g., 'example.com' or 'https://example.com') */
33
+ webBaseUrl?: string;
34
+
35
+ /** Allow any localhost origin (http://localhost:*) for local development */
36
+ allowLocalhost?: boolean;
37
+
38
+ /** @deprecated Use `allowLocalhost` instead */
39
+ enableTestEndpoints?: boolean;
40
+ }
41
+ ```
42
+
43
+ ## Option Details
44
+
45
+ ### webBaseUrl
46
+
47
+ Specifies the production web domain that is allowed to make cross-origin requests.
48
+
49
+ - **Format**: Domain only (e.g., `'example.com'`) or full URL (e.g., `'https://example.com'`)
50
+ - **Behavior**: Allows requests from `https://{webBaseUrl}`
51
+ - **Typical usage**: Set via environment variable `WEB_BASE_URL`
52
+
53
+ ```typescript
54
+ corsConfig: {
55
+ webBaseUrl: process.env.WEB_BASE_URL, // e.g., 'myapp.example.com'
56
+ }
57
+ ```
58
+
59
+ ### allowLocalhost
60
+
61
+ Enables CORS for any `http://localhost:*` origin (any port). This is the recommended setting for local development where the API server and frontend run on different ports.
62
+
63
+ - **Default**: `false`
64
+ - **When `true`**: Allows any localhost origin for development
65
+ - **When `false`**: Localhost origins are blocked
66
+ - **Typical usage**: Enable in development, disable in production
67
+
68
+ ```typescript
69
+ corsConfig: {
70
+ allowLocalhost: process.env.NODE_ENV !== 'production',
71
+ }
72
+ ```
73
+
74
+ ### enableTestEndpoints (deprecated)
75
+
76
+ Use `allowLocalhost` instead. When both are set, `allowLocalhost` takes precedence.
77
+
78
+ ## Environment Variable Pattern
79
+
80
+ A common pattern is to configure CORS via environment variables:
81
+
82
+ ```bash
83
+ # .env (development)
84
+ WEB_BASE_URL=localhost:3000
85
+ ALLOW_LOCALHOST=true
86
+
87
+ # .env (production)
88
+ WEB_BASE_URL=myapp.example.com
89
+ ALLOW_LOCALHOST=false
90
+ ```
91
+
92
+ ```typescript
93
+ corsConfig: {
94
+ webBaseUrl: process.env.WEB_BASE_URL,
95
+ allowLocalhost: process.env.ALLOW_LOCALHOST === 'true',
96
+ }
97
+ ```
98
+
99
+ ## CORS Headers
100
+
101
+ The middleware automatically sets these headers on all responses:
102
+
103
+ | Header | Value |
104
+ |--------|-------|
105
+ | `Access-Control-Allow-Credentials` | `true` |
106
+ | `Access-Control-Allow-Methods` | `GET, POST, PUT, DELETE, OPTIONS, PATCH` |
107
+ | `Access-Control-Allow-Headers` | `Origin, X-Requested-With, Content-Type, Accept, Authorization, Cookie, Cache-Control` |
108
+ | `Access-Control-Max-Age` | `86400` (24 hours) |
109
+
110
+ ## Preflight Requests
111
+
112
+ The middleware automatically handles OPTIONS preflight requests by returning HTTP 200 with the appropriate CORS headers.
113
+
114
+ ## Security Notes
115
+
116
+ - **Whitelist-based**: Only explicitly allowed origins receive the `Access-Control-Allow-Origin` header
117
+ - **No wildcards**: The middleware uses specific domain matching instead of `*`
118
+ - **Credentials enabled**: Supports cookie-based authentication
119
+ - **Non-matching origins**: Requests from non-allowed origins do not receive CORS headers, causing the browser to block the response
120
+
121
+ ## Examples
122
+
123
+ ### Development Setup (localhost only)
124
+
125
+ ```typescript
126
+ await startServer({
127
+ app,
128
+ port: 4000,
129
+ corsConfig: {
130
+ allowLocalhost: true,
131
+ },
132
+ });
133
+ ```
134
+
135
+ ### Production Setup
136
+
137
+ ```typescript
138
+ await startServer({
139
+ app,
140
+ port: 4000,
141
+ corsConfig: {
142
+ webBaseUrl: 'myapp.example.com',
143
+ },
144
+ });
145
+ ```
146
+
147
+ ### Combined Setup (development + production)
148
+
149
+ ```typescript
150
+ await startServer({
151
+ app,
152
+ port: parseInt(process.env.PRISM_API_PORT, 10),
153
+ corsConfig: {
154
+ webBaseUrl: process.env.WEB_BASE_URL,
155
+ allowLocalhost: process.env.ALLOW_LOCALHOST === 'true',
156
+ },
157
+ });
158
+ ```
159
+
160
+ ## Troubleshooting
161
+
162
+ ### "No 'Access-Control-Allow-Origin' header" error
163
+
164
+ This means the requesting origin is not allowed. Check:
165
+
166
+ 1. For localhost development: Ensure `allowLocalhost: true` is set
167
+ 2. For production: Ensure `webBaseUrl` matches your frontend's domain
168
+ 3. The request origin uses the correct protocol (https for production)
169
+
170
+ ### Preflight requests failing
171
+
172
+ Ensure your server is running and the CORS middleware is configured. The middleware handles OPTIONS requests automatically.
@@ -0,0 +1,220 @@
1
+ ---
2
+ name: creating-services
3
+ description: How to create services with endpoints, middleware, and database schemas
4
+ ---
5
+
6
+ # Creating Services
7
+
8
+ Services are the building blocks of a Prism Framework application. Each service is self-contained and can define endpoints, middleware, database schemas, and background jobs.
9
+
10
+ ## ServiceDefinition Interface
11
+
12
+ ```typescript
13
+ interface ServiceDefinition {
14
+ /** Unique name identifying this service */
15
+ name: string;
16
+
17
+ /** API endpoints provided by this service */
18
+ endpoints?: EndpointDefinition[];
19
+
20
+ /** Express middleware scoped to specific paths */
21
+ middleware?: MiddlewareDefinition[];
22
+
23
+ /** SQLite database schemas, keyed by database name */
24
+ databases?: Record<string, {
25
+ statements: string[]; // SQL CREATE TABLE / CREATE INDEX statements
26
+ }>;
27
+
28
+ /** Async callback to start background jobs when the app initializes */
29
+ startJobs?: () => Promise<void>;
30
+ }
31
+ ```
32
+
33
+ All fields except `name` are optional. A service can provide any combination of endpoints, middleware, databases, and background jobs.
34
+
35
+ ## Basic Service Structure
36
+
37
+ ```typescript
38
+ import { ServiceDefinition, createEndpoint } from '@facetlayer/prism-framework';
39
+ import { z } from 'zod';
40
+
41
+ export const definition: ServiceDefinition = {
42
+ name: 'my-service',
43
+
44
+ // API endpoints
45
+ endpoints: [
46
+ // ... endpoint definitions
47
+ ],
48
+
49
+ // Optional middleware
50
+ middleware: [
51
+ // ... middleware definitions
52
+ ],
53
+
54
+ // Optional database schemas
55
+ databases: {
56
+ user: {
57
+ statements: [
58
+ // SQL statements for user database
59
+ ],
60
+ },
61
+ },
62
+
63
+ // Optional background jobs
64
+ startJobs: async () => {
65
+ // Initialize background tasks
66
+ },
67
+ };
68
+ ```
69
+
70
+ ## Defining Endpoints
71
+
72
+ Endpoints are defined with type safety using Zod schemas:
73
+
74
+ ```typescript
75
+ const GetUserRequest = z.object({
76
+ userId: z.string(),
77
+ });
78
+
79
+ const GetUserResponse = z.object({
80
+ id: z.string(),
81
+ email: z.string(),
82
+ name: z.string(),
83
+ });
84
+
85
+ const getUserEndpoint = createEndpoint({
86
+ method: 'GET',
87
+ path: '/users/:userId',
88
+ requestSchema: GetUserRequest,
89
+ responseSchema: GetUserResponse,
90
+ requires: ['authenticated-user'], // Optional requirements
91
+ handler: async (input) => {
92
+ // input is typed as z.infer<typeof GetUserRequest>
93
+ const user = await getUserById(input.userId);
94
+ return user; // Must match GetUserResponse schema
95
+ },
96
+ });
97
+ ```
98
+
99
+ ### Endpoint Methods
100
+
101
+ Supported HTTP methods:
102
+ - `GET`
103
+ - `POST`
104
+ - `PUT`
105
+ - `DELETE`
106
+ - `PATCH`
107
+
108
+ ### Request Data
109
+
110
+ The framework automatically combines data from:
111
+ - Request body (`req.body`)
112
+ - URL parameters (`req.params`)
113
+ - Query parameters (`req.query`)
114
+
115
+ All are merged and validated against the `requestSchema`.
116
+
117
+ ### Requirements
118
+
119
+ The `requires` array can specify:
120
+ - `'authenticated-user'` - Requires an authenticated user (checks for user resource in context)
121
+
122
+ Applications can extend this by providing custom middleware or handlers.
123
+
124
+ ## Server-Sent Events (SSE)
125
+
126
+ For streaming responses, return an object with a `startSse` method:
127
+
128
+ ```typescript
129
+ createEndpoint({
130
+ method: 'GET',
131
+ path: '/stream',
132
+ handler: async () => {
133
+ return {
134
+ startSse: (sse: SseResponse) => {
135
+ // Send events
136
+ sse.send({ message: 'Hello' });
137
+ sse.send({ message: 'World' });
138
+
139
+ // Close when done
140
+ sse.close();
141
+
142
+ // Or handle client disconnect
143
+ sse.onClose(() => {
144
+ console.log('Client disconnected');
145
+ });
146
+ },
147
+ };
148
+ },
149
+ });
150
+ ```
151
+
152
+ ## Adding Middleware
153
+
154
+ Middleware can be path-specific:
155
+
156
+ ```typescript
157
+ export const definition: ServiceDefinition = {
158
+ name: 'my-service',
159
+ middleware: [
160
+ {
161
+ path: '/admin/*',
162
+ handler: (req, res, next) => {
163
+ // Check admin permissions
164
+ if (!isAdmin(req)) {
165
+ return res.status(403).json({ error: 'Forbidden' });
166
+ }
167
+ next();
168
+ },
169
+ },
170
+ ],
171
+ };
172
+ ```
173
+
174
+ ## Database Schemas
175
+
176
+ Define database schemas per database:
177
+
178
+ ```typescript
179
+ export const definition: ServiceDefinition = {
180
+ name: 'users',
181
+ databases: {
182
+ user: {
183
+ statements: [
184
+ `CREATE TABLE IF NOT EXISTS users (
185
+ id INTEGER PRIMARY KEY,
186
+ email TEXT UNIQUE NOT NULL,
187
+ name TEXT,
188
+ created_at DATETIME DEFAULT (datetime('now', 'utc') || 'Z')
189
+ )`,
190
+ `CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)`,
191
+ ],
192
+ },
193
+ project: {
194
+ statements: [
195
+ // Project-specific tables
196
+ ],
197
+ },
198
+ },
199
+ };
200
+ ```
201
+
202
+ ## Background Jobs
203
+
204
+ Services can start background jobs when the application initializes:
205
+
206
+ ```typescript
207
+ export const definition: ServiceDefinition = {
208
+ name: 'cleanup',
209
+ startJobs: async () => {
210
+ // Run periodic cleanup
211
+ setInterval(async () => {
212
+ await cleanupOldData();
213
+ }, 60 * 60 * 1000); // Every hour
214
+ },
215
+ };
216
+ ```
217
+
218
+ ## Error Handling
219
+
220
+ Use the built-in HTTP error classes to return proper status codes from handlers. See the `error-handling` doc for the full list of available error classes and usage patterns.
@@ -0,0 +1,134 @@
1
+ ---
2
+ name: database-setup
3
+ description: How to set up a local SQLite database for your backend application
4
+ ---
5
+
6
+ # Database Setup
7
+
8
+ This guide covers setting up a local SQLite database for your Prism Framework backend application using the `@facetlayer/sqlite-wrapper` library.
9
+
10
+ ## Directory Structure
11
+
12
+ Set up the database as a backend "service" with its own folder inside the `./src` directory:
13
+
14
+ ```
15
+ src/
16
+ ├── _main/
17
+ │ └── index.ts
18
+ ├── user-database/
19
+ │ └── db.ts
20
+ └── other-services/
21
+ └── ...
22
+ ```
23
+
24
+ ## Installation
25
+
26
+ Install the required packages:
27
+
28
+ ```bash
29
+ pnpm add @facetlayer/sqlite-wrapper better-sqlite3
30
+ pnpm add -D @types/better-sqlite3
31
+ ```
32
+
33
+ ## Database Configuration
34
+
35
+ Create a database module at `src/user-database/db.ts`:
36
+
37
+ ```typescript
38
+ import { DatabaseLoader, SqliteDatabase } from '@facetlayer/sqlite-wrapper';
39
+ import { toConsoleLog } from '@facetlayer/streams';
40
+ import BetterSqliteDatabase from 'better-sqlite3';
41
+ import Path from 'path';
42
+
43
+ let db: SqliteDatabase | null = null;
44
+
45
+ const schema = {
46
+ name: 'MyAppDatabase',
47
+ statements: [
48
+ `create table users (
49
+ id integer primary key autoincrement,
50
+ email text not null unique,
51
+ created_at integer not null default (strftime('%s', 'now'))
52
+ )`,
53
+ `create index idx_users_email on users(email)`,
54
+ ]
55
+ }
56
+
57
+ export function getDatabase(): SqliteDatabase {
58
+ const DATABASE_DIR = process.env.DATABASE_DIR;
59
+ if (!DATABASE_DIR) {
60
+ throw new Error('DATABASE_DIR is not set');
61
+ }
62
+
63
+ if (!db) {
64
+ const loader = new DatabaseLoader({
65
+ filename: Path.join(DATABASE_DIR, 'app.db'),
66
+ loadDatabase: (filename) => new BetterSqliteDatabase(filename),
67
+ migrationBehavior: 'safe-upgrades',
68
+ schema,
69
+ logs: toConsoleLog('[database]'),
70
+ });
71
+
72
+ db = loader.load();
73
+ }
74
+ return db;
75
+ }
76
+ ```
77
+
78
+ ## Environment Variable
79
+
80
+ The `DATABASE_DIR` environment variable must be set to specify where the database file will be stored.
81
+
82
+ For local development, create a `.env` file:
83
+
84
+ ```
85
+ DATABASE_DIR=./data
86
+ ```
87
+
88
+ Make sure the directory exists:
89
+
90
+ ```bash
91
+ mkdir -p ./data
92
+ ```
93
+
94
+ ## Using the Database
95
+
96
+ Import and use the database in your services:
97
+
98
+ ```typescript
99
+ import { getDatabase } from '../user-database/db';
100
+
101
+ export async function createUser(email: string) {
102
+ const db = getDatabase();
103
+ const stmt = db.prepare('INSERT INTO users (email) VALUES (?)');
104
+ const result = stmt.run(email);
105
+ return result.lastInsertRowid;
106
+ }
107
+
108
+ export async function getUserByEmail(email: string) {
109
+ const db = getDatabase();
110
+ const stmt = db.prepare('SELECT * FROM users WHERE email = ?');
111
+ return stmt.get(email);
112
+ }
113
+ ```
114
+
115
+ ## Schema Migrations
116
+
117
+ The `DatabaseLoader` handles schema migrations automatically with the `migrationBehavior` option:
118
+
119
+ - `'safe-upgrades'` - Automatically applies safe migrations (adding tables, columns, indexes)
120
+ - `'strict'` - Only creates schema on first run, no automatic migrations
121
+
122
+ For complex migrations, add new statements to the `statements` array. The loader will apply them in order.
123
+
124
+ ## Multiple Databases
125
+
126
+ If your application needs multiple databases, create separate modules with different schemas and filenames:
127
+
128
+ ```typescript
129
+ // src/user-database/db.ts
130
+ export function getUserDatabase(): SqliteDatabase { ... }
131
+
132
+ // src/project-database/db.ts
133
+ export function getProjectDatabase(): SqliteDatabase { ... }
134
+ ```
@@ -43,8 +43,6 @@ Calls an endpoint on a running server.
43
43
 
44
44
  ### Basic Usage
45
45
 
46
- **Important:** Do not use the `/api` prefix in endpoint paths. Use the path directly as defined in the endpoint.
47
-
48
46
  ```bash
49
47
  # GET request
50
48
  prism call /users
@@ -101,15 +99,7 @@ Response: {"id":"123","name":"John","email":"john@example.com"}
101
99
 
102
100
  1. **Check the server is running** - Start your API server
103
101
  2. **Verify PRISM_API_PORT** - Ensure `.env` contains `PRISM_API_PORT` matching your server port
104
- 3. **Check dotenv is loaded** - Your server must load the `.env` file:
105
-
106
- ```typescript
107
- import { config } from 'dotenv';
108
- config({ path: '.env' });
109
-
110
- const PORT = parseInt(process.env.PRISM_API_PORT, 10);
111
- await startServer({ app, port: PORT });
112
- ```
102
+ 3. **Check dotenv is loaded** - Your server must load the `.env` file (see `env-files` doc)
113
103
 
114
104
  ### "Endpoint not found"
115
105
 
package/docs/env-files.md CHANGED
@@ -53,6 +53,17 @@ const apiUrl = import.meta.env.VITE_API_URL;
53
53
 
54
54
  See the `vite-setup` doc in `@facetlayer/prism-framework-ui` for more details.
55
55
 
56
+ ## Loading .env in the Backend
57
+
58
+ Use `dotenv` to load your `.env` file early in your server entry point:
59
+
60
+ ```typescript
61
+ import { config } from 'dotenv';
62
+ config({ path: '.env' });
63
+ ```
64
+
65
+ This should be called before accessing any `process.env` values. The `dotenv` package is included when you install `@facetlayer/prism-framework`'s recommended dependencies (see `getting-started` doc).
66
+
56
67
  # Port assignment
57
68
 
58
69
  It's recommended to use the `@facetlayer/port-assignment` tool if you need to assign new unique port numbers.
@@ -61,4 +72,4 @@ Example:
61
72
 
62
73
  npx @facetlayer/port-assignment claim --name <project name>
63
74
 
64
- Run `npx @facetlayer/port-assigment list-docs` for more documentation.
75
+ Run `npx @facetlayer/port-assignment list-docs` for more documentation.
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: error-handling
3
+ description: HTTP error classes for returning proper status codes from endpoint handlers
4
+ ---
5
+
6
+ # Error Handling
7
+
8
+ Prism Framework provides built-in HTTP error classes. Throw these from endpoint handlers or middleware to return the appropriate status code and error message.
9
+
10
+ ## Usage
11
+
12
+ ```typescript
13
+ import { NotFoundError, BadRequestError } from '@facetlayer/prism-framework';
14
+
15
+ handler: async (input) => {
16
+ if (!input.userId) {
17
+ throw new BadRequestError('User ID is required');
18
+ }
19
+
20
+ const user = await getUserById(input.userId);
21
+ if (!user) {
22
+ throw new NotFoundError('User not found');
23
+ }
24
+
25
+ return user;
26
+ }
27
+ ```
28
+
29
+ ## Available Error Classes
30
+
31
+ | Class | Status Code |
32
+ |-------|-------------|
33
+ | `BadRequestError` | 400 |
34
+ | `UnauthorizedError` | 401 |
35
+ | `ForbiddenError` | 403 |
36
+ | `NotFoundError` | 404 |
37
+ | `ConflictError` | 409 |
38
+ | `ValidationError` | 422 |
39
+ | `NotImplementedError` | 501 |
40
+ | `ServiceUnavailableError` | 503 |
41
+ | `HttpError` | Custom status code |
42
+
43
+ ## Custom Status Codes
44
+
45
+ Use `HttpError` for status codes not covered by the named classes:
46
+
47
+ ```typescript
48
+ import { HttpError } from '@facetlayer/prism-framework';
49
+
50
+ throw new HttpError(429, 'Too many requests');
51
+ ```
52
+
53
+ ## Authorization Errors
54
+
55
+ For authorization-related errors, use `UnauthorizedError` (not logged in) and `ForbiddenError` (logged in but not allowed):
56
+
57
+ ```typescript
58
+ import { UnauthorizedError, ForbiddenError } from '@facetlayer/prism-framework';
59
+
60
+ const user = context.auth.getResource('user');
61
+ if (!user) {
62
+ throw new UnauthorizedError('Authentication required');
63
+ }
64
+
65
+ if (!context.auth.hasPermission('delete:projects')) {
66
+ throw new ForbiddenError('Insufficient permissions');
67
+ }
68
+ ```
69
+
70
+ See the `authorization` doc for more on how auth and permissions work.
@@ -12,14 +12,11 @@ description: Guide for setting up a new Prism Framework project with type-safe A
12
12
  This includes the following NPM libraries:
13
13
 
14
14
  ### `@facetlayer/prism-framework`
15
- - Base library with tooling for various development and testing tasks
16
- - Should be installed at the top level in the devDependencies section
17
- - First place to look for documentation (`prism list-docs`)
18
-
19
- ### `@facetlayer/prism-framework-api`
20
- - Backend API framework
21
- - Can be hosted on HTTP with Express.js
15
+ - Base library with backend API framework, CLI tooling, and development tools
16
+ - Includes the server framework (Express.js), endpoint definitions, authorization, and more
22
17
  - Also supports other launch methods such as IPC for Electron
18
+ - Should be installed at the top level
19
+ - First place to look for documentation (`prism list-docs`)
23
20
 
24
21
  ### `@facetlayer/prism-framework-ui`
25
22
  - Helpers for React-based frontend web apps
@@ -41,7 +38,7 @@ Some ways to set up the repo for a Prism project:
41
38
  ### Option 1: API in separate directory
42
39
 
43
40
  - `./api` - Backend API
44
- - `./api/package.json` - Contains prism-framework-api
41
+ - `./api/package.json` - Contains prism-framework
45
42
  - `./api/src/` - Backend service implementation
46
43
  - `./web` or `./ui` - Frontend web app
47
44
  - `./web/package.json` - Contains Next.js or Vite, and prism-framework-ui
@@ -49,7 +46,7 @@ Some ways to set up the repo for a Prism project:
49
46
 
50
47
  ### Option 2: API in top level directory
51
48
 
52
- - `./package.json` - Contains prism-framework-api
49
+ - `./package.json` - Contains prism-framework
53
50
  - `./src` - Backend API source code
54
51
  - `./web` or `./ui` - Frontend web app
55
52
  - `./web/package.json` - Contains Next.js or Vite, and prism-framework-ui
@@ -69,7 +66,20 @@ Some ways to set up the repo for a Prism project:
69
66
 
70
67
  The top level of the project should have these dependencies:
71
68
 
72
- `pnpm add typescript dotenv @facetlayer/prism-framework`
69
+ `pnpm add typescript dotenv @facetlayer/prism-framework zod@^4`
70
+
71
+ ## Environment setup
72
+
73
+ Create a `.env` file next to your backend code with at minimum:
74
+
75
+ PRISM_API_PORT=<port number>
76
+ DATABASE_DIR=data
77
+
78
+ Use `@facetlayer/port-assignment` to claim a unique port for your project:
79
+
80
+ npx @facetlayer/port-assignment claim --name <project name>
81
+
82
+ See the `env-files` doc (`prism get-doc env-files`) for the full list of recommended environment variables for both backend and frontend.
73
83
 
74
84
  ## Local service management
75
85
 
@@ -81,6 +91,6 @@ Examples:
81
91
  `candle list-docs`
82
92
 
83
93
  Set up services in the .candle.json file:
84
- `candle add-service api "node --watch src/_main/api.ts" --root ./api
85
- `candle add-service web "pnpm dev" --root ./web
94
+ `candle add-service api "node --watch src/_main/api.ts" --root ./api`
95
+ `candle add-service web "pnpm dev" --root ./web`
86
96