@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,223 @@
1
+ ---
2
+ name: launch-configuration
3
+ description: How to configure Prism Framework for different execution contexts
4
+ ---
5
+
6
+ # Launch Configuration
7
+
8
+ The launch configuration system is a crucial feature that allows the same code to work in both backend (Express.js) and desktop (Electron) contexts.
9
+
10
+ ## Why Launch Configuration?
11
+
12
+ Different execution contexts need different configurations:
13
+ - **Backend server**: Database files on disk, specific logging configuration
14
+ - **Electron desktop app**: Database files in user data directory, different SQLite loader
15
+
16
+ The `setLaunchConfig()` function centralizes this configuration and must be called **before** any services access databases or logging.
17
+
18
+ ## Basic Setup
19
+
20
+ ```typescript
21
+ import { setLaunchConfig } from '@facetlayer/prism-framework';
22
+
23
+ setLaunchConfig({
24
+ logging: {
25
+ databaseFilename: '/path/to/logs.db',
26
+ enableConsoleLogging: true,
27
+ loadDatabase: await loadBetterSqlite(),
28
+ },
29
+ database: {
30
+ user: {
31
+ migrationBehavior: 'safe-upgrades',
32
+ databasePath: '/path/to/databases',
33
+ services: ALL_SERVICES,
34
+ loadDatabase: await loadBetterSqlite(),
35
+ },
36
+ },
37
+ });
38
+ ```
39
+
40
+ ## Configuration Structure
41
+
42
+ ### Logging Configuration
43
+
44
+ ```typescript
45
+ interface LoggingSettings {
46
+ databaseFilename: string; // Path to logging database
47
+ enableConsoleLogging: boolean; // Also log to console
48
+ loadDatabase: LoadDatabaseFn; // Database loader function
49
+ }
50
+ ```
51
+
52
+ ### Database Configuration
53
+
54
+ ```typescript
55
+ interface DatabaseInitializationOptions {
56
+ migrationBehavior: MigrationBehavior; // How to handle schema changes
57
+ databasePath: string; // Directory for database files
58
+ services: ServiceDefinition[]; // Services to include schemas from
59
+ loadDatabase: LoadDatabaseFn; // Database loader function
60
+ }
61
+ ```
62
+
63
+ ### Migration Behavior
64
+
65
+ ```typescript
66
+ type MigrationBehavior =
67
+ | 'safe-upgrades' // Only allow adding columns/tables
68
+ | 'rebuild' // Drop and recreate on mismatch
69
+ | 'error-on-mismatch'; // Throw error on schema mismatch
70
+ ```
71
+
72
+ ## Backend Server Example
73
+
74
+ ```typescript
75
+ // main.ts (backend server)
76
+ import { setLaunchConfig, startServer, App } from '@facetlayer/prism-framework';
77
+ import { loadBetterSqlite } from '@facetlayer/sqlite-wrapper';
78
+ import path from 'path';
79
+ import fs from 'fs';
80
+
81
+ async function main() {
82
+ const databasePath = process.env.SQLITE_DATABASE_PATH || './databases';
83
+
84
+ // Ensure directory exists
85
+ if (!fs.existsSync(databasePath)) {
86
+ fs.mkdirSync(databasePath, { recursive: true });
87
+ }
88
+
89
+ // Configure for backend
90
+ setLaunchConfig({
91
+ logging: {
92
+ databaseFilename: path.join(databasePath, 'logs.db'),
93
+ enableConsoleLogging: true,
94
+ loadDatabase: await loadBetterSqlite(),
95
+ },
96
+ database: {
97
+ user: {
98
+ migrationBehavior: 'safe-upgrades',
99
+ databasePath,
100
+ services: ALL_SERVICES,
101
+ loadDatabase: await loadBetterSqlite(),
102
+ },
103
+ },
104
+ });
105
+
106
+ // Start server (see server-setup doc for full startServer options)
107
+ const app = new App({ services: ALL_SERVICES });
108
+ await startServer({ app, port: parseInt(process.env.PRISM_API_PORT!, 10) });
109
+ }
110
+
111
+ main().catch(console.error);
112
+ ```
113
+
114
+ ## Electron Desktop App Example
115
+
116
+ ```typescript
117
+ // main.ts (Electron main process)
118
+ import { app } from 'electron';
119
+ import { setLaunchConfig } from '@facetlayer/prism-framework';
120
+ import { getSqliteLoader } from './database-helper';
121
+ import path from 'path';
122
+
123
+ async function initializeApp() {
124
+ await app.whenReady();
125
+
126
+ const userDataPath = app.getPath('userData');
127
+ const databasePath = path.join(userDataPath, 'databases');
128
+
129
+ // Get Electron-compatible SQLite loader
130
+ const loadDatabase = await getSqliteLoader();
131
+
132
+ // Configure for Electron
133
+ setLaunchConfig({
134
+ logging: {
135
+ databaseFilename: path.join(databasePath, 'logs.db'),
136
+ enableConsoleLogging: true,
137
+ loadDatabase,
138
+ },
139
+ database: {
140
+ 'desktop-app': {
141
+ migrationBehavior: 'safe-upgrades',
142
+ databasePath,
143
+ services: ALL_SERVICES,
144
+ loadDatabase,
145
+ },
146
+ },
147
+ });
148
+
149
+ // Continue with Electron app initialization...
150
+ }
151
+ ```
152
+
153
+ ## Accessing Configuration
154
+
155
+ Services and framework code can access the configuration:
156
+
157
+ ```typescript
158
+ import {
159
+ getLaunchConfig,
160
+ getDatabaseConfig,
161
+ getLoggingConfig
162
+ } from '@facetlayer/prism-framework';
163
+
164
+ // Get full config
165
+ const config = getLaunchConfig();
166
+
167
+ // Get database config for a specific database
168
+ const userDbConfig = getDatabaseConfig('user');
169
+
170
+ // Get logging config
171
+ const loggingConfig = getLoggingConfig();
172
+ ```
173
+
174
+ ## Multiple Databases
175
+
176
+ You can configure multiple databases:
177
+
178
+ ```typescript
179
+ setLaunchConfig({
180
+ logging: { /* ... */ },
181
+ database: {
182
+ user: {
183
+ migrationBehavior: 'safe-upgrades',
184
+ databasePath: '/path/to/databases',
185
+ services: ALL_SERVICES.filter(s => s.databases?.user),
186
+ loadDatabase: await loadBetterSqlite(),
187
+ },
188
+ project: {
189
+ migrationBehavior: 'safe-upgrades',
190
+ databasePath: '/path/to/databases',
191
+ services: ALL_SERVICES.filter(s => s.databases?.project),
192
+ loadDatabase: await loadBetterSqlite(),
193
+ },
194
+ 'desktop-app': {
195
+ migrationBehavior: 'rebuild',
196
+ databasePath: '/path/to/databases',
197
+ services: ALL_SERVICES.filter(s => s.databases?.['desktop-app']),
198
+ loadDatabase: await loadBetterSqlite(),
199
+ },
200
+ },
201
+ });
202
+ ```
203
+
204
+ ## Database Statements Collection
205
+
206
+ The framework automatically collects SQL statements from all services:
207
+
208
+ ```typescript
209
+ import { getStatementsForDatabase } from '@facetlayer/prism-framework';
210
+
211
+ const statements = getStatementsForDatabase('user', ALL_SERVICES);
212
+ // Returns all SQL statements from services that define databases.user
213
+ ```
214
+
215
+ This is used internally when initializing databases with `@facetlayer/sqlite-wrapper`.
216
+
217
+ ## Best Practices
218
+
219
+ 1. **Call setLaunchConfig early** - Before any database or logging operations
220
+ 2. **Call it only once** - The function throws if called multiple times
221
+ 3. **Use environment variables** - Make paths configurable
222
+ 4. **Ensure directories exist** - Create database directories before setting config
223
+ 5. **Use appropriate migration behavior** - `safe-upgrades` for production, `rebuild` for development
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: overview
3
+ description: Introduction to Prism Framework and its core concepts
4
+ ---
5
+
6
+ # Prism Framework Overview
7
+
8
+ Prism Framework is a TypeScript framework for building web-based SaaS applications and desktop Electron apps. It provides a unified approach to creating applications that can run in both backend (Express.js) and desktop (Electron) contexts.
9
+
10
+ ## Key Features
11
+
12
+ 1. **Service-Based Architecture** - Organize your application into self-contained services
13
+ 2. **Type-Safe Endpoints** - Define endpoints with Zod schemas for request/response validation
14
+ 3. **Launch Configuration** - Single configuration system that works for both web and desktop
15
+ 4. **Database Management** - Integration with `@facetlayer/sqlite-wrapper` for database operations
16
+ 5. **Request Context** - AsyncLocalStorage-based request context tracking
17
+ 6. **Authorization** - Built-in authorization system with resources and auth sources
18
+ 7. **Metrics** - Prometheus metrics integration
19
+ 8. **SSE Support** - Server-Sent Events for real-time communication
20
+ 9. **Error Handling** - Comprehensive HTTP error classes
21
+
22
+ ## Core Concepts
23
+
24
+ ### Services
25
+
26
+ A service is a self-contained module that can include API endpoints, middleware, database schemas, and background jobs. Services are the building blocks of a Prism application.
27
+
28
+ See the `creating-services` doc for the full `ServiceDefinition` interface and examples.
29
+
30
+ ### Launch Configuration
31
+
32
+ The launch configuration system allows the same code to work in different contexts (backend server, Electron desktop app). Call `setLaunchConfig()` early in your app to configure logging and database settings.
33
+
34
+ See the `launch-configuration` doc for setup details and examples.
35
+
36
+ ### Request Context
37
+
38
+ Every request has an associated context (via AsyncLocalStorage) that flows through all async operations. Access it with `getCurrentRequestContext()` to get request ID, auth info, and more.
39
+
40
+ See the `authorization` doc for how auth integrates with request context.
41
+
42
+ ### Error Handling
43
+
44
+ The framework provides built-in HTTP error classes (`BadRequestError`, `NotFoundError`, `ForbiddenError`, etc.) that automatically map to the correct status codes.
45
+
46
+ See the `error-handling` doc for available error classes and usage patterns.
47
+
48
+ ## Project Structure
49
+
50
+ See the `source-directory-organization` doc for the recommended directory layout and conventions.
51
+
52
+ ## Getting Started
53
+
54
+ 1. Install the framework (requires **Zod v4** — Zod v3 is not compatible):
55
+ ```bash
56
+ pnpm add @facetlayer/prism-framework zod@^4
57
+ ```
58
+
59
+ 2. Set up environment variables (see `env-files` doc)
60
+ 3. Create your first service (see `creating-services` doc)
61
+ 4. Set up the launch configuration (see `launch-configuration` doc)
62
+ 5. Start the server (see `server-setup` doc)
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: server-setup
3
+ description: How to set up and start an Express.js server using Prism Framework
4
+ ---
5
+
6
+ # Server Setup
7
+
8
+ This guide explains how to set up and start an Express.js server using Prism Framework.
9
+
10
+ ## Quick Outline
11
+
12
+ Brief summary of the steps to setup a Prism server:
13
+
14
+ - Use the `@facetlayer/prism-framework` library
15
+ - Create an instance of `App` and use `startServer`
16
+ - API endpoints are automatically mounted at `/api/`
17
+ - Optionally serve web files (HTML/JS/CSS) from the same server
18
+ - Use the env var `PRISM_API_PORT` as the service port.
19
+
20
+ ## Server Setup Example
21
+
22
+ ### API-only server
23
+
24
+ ```typescript
25
+ import { App, startServer } from '@facetlayer/prism-framework';
26
+
27
+ const app = new App({ services: ALL_SERVICES });
28
+
29
+ await startServer({
30
+ app,
31
+ port: parseInt(process.env.PRISM_API_PORT || '4000', 10),
32
+ openapiConfig: {
33
+ enable: true,
34
+ enableSwagger: true,
35
+ },
36
+ });
37
+ ```
38
+
39
+ All endpoints are mounted under `/api/`. For example, an endpoint defined with
40
+ `path: '/users'` is accessible at `GET /api/users`.
41
+
42
+ ### Unified server with web files
43
+
44
+ To serve both API and web pages from a single process on one port, add the
45
+ `web` config option:
46
+
47
+ ```typescript
48
+ import { App, startServer } from '@facetlayer/prism-framework';
49
+ import { join, dirname } from 'path';
50
+ import { fileURLToPath } from 'url';
51
+
52
+ const __dirname = dirname(fileURLToPath(import.meta.url));
53
+
54
+ const app = new App({ services: ALL_SERVICES });
55
+
56
+ await startServer({
57
+ app,
58
+ port: parseInt(process.env.PRISM_API_PORT || '4000', 10),
59
+ openapiConfig: {
60
+ enable: true,
61
+ enableSwagger: true,
62
+ },
63
+ web: {
64
+ dir: join(__dirname, '..', 'web'),
65
+ },
66
+ });
67
+ ```
68
+
69
+ When `web` is configured:
70
+ - API endpoints are served under `/api/` (e.g. `/api/users`, `/api/health`)
71
+ - Web files are served from the specified directory at the root path (`/`)
72
+ - In development: uses Vite dev server middleware if `vite` is installed (for HMR)
73
+ - In production: serves static files from `web/dist/` (or `web/` if no dist folder)
74
+ - SPA fallback: unmatched GET requests serve `index.html`
75
+
76
+ ### Available framework endpoints
77
+
78
+ These are automatically available under `/api/`:
79
+
80
+ | Path | Description |
81
+ |------|-------------|
82
+ | `/api/health` | Health check (localhost only) |
83
+ | `/api/metrics` | Prometheus metrics (localhost only) |
84
+ | `/api/endpoints` | HTML endpoint listing |
85
+ | `/api/endpoints.json` | JSON endpoint listing |
86
+ | `/api/openapi.json` | OpenAPI schema (if enabled) |
87
+ | `/api/swagger` | Swagger UI (if enabled) |
88
+
89
+ ## Launching with Candle
90
+
91
+ Use the `candle` process manager to run the server:
92
+
93
+ ### Setup
94
+
95
+ Register the service in your `.candle.json`:
96
+
97
+ ```json
98
+ {
99
+ "services": [
100
+ {
101
+ "name": "my-app",
102
+ "shell": "node src/main.ts"
103
+ }
104
+ ]
105
+ }
106
+ ```
107
+
108
+ ### Usage
109
+
110
+ ```bash
111
+ # Start the service
112
+ candle start my-app
113
+
114
+ # Check status
115
+ candle ls
116
+
117
+ # View logs
118
+ candle logs my-app
119
+
120
+ # Restart after changes
121
+ candle restart my-app
122
+ ```
123
+
124
+ ### Recommended .env setup
125
+
126
+ ```bash
127
+ PRISM_API_PORT=4000
128
+ ```
129
+
130
+ ## Vite Integration
131
+
132
+ To use Vite for frontend development with HMR:
133
+
134
+ 1. Install `vite` as a dependency in your project
135
+ 2. Place your web files in a `web/` directory with an `index.html`
136
+ 3. Pass `web: { dir: './web' }` to `startServer`
137
+
138
+ In development mode, Vite's dev server middleware handles all non-API requests,
139
+ providing hot module replacement. In production, the built static files from
140
+ `web/dist/` are served.
141
+
142
+ ## Testing
143
+
144
+ Run `prism list-endpoints` to check that the local server is running and the endpoint listing is working.
@@ -0,0 +1,115 @@
1
+ ---
2
+ name: source-directory-organization
3
+ description: Recommended directory structure for organizing Prism API source code
4
+ ---
5
+
6
+ # Source Directory Organization
7
+
8
+ This document describes the recommended directory structure for organizing your Prism API source code. Following this pattern ensures consistency across projects and makes navigation intuitive.
9
+
10
+ ## Directory Structure
11
+
12
+ ```
13
+ src/
14
+ ├── _main/ # Main application bootstrap
15
+ │ ├── api.ts # Entry point - starts the server
16
+ │ └── services.ts # Exports ALL_SERVICES array
17
+ ├── <service-name>/ # One folder per service
18
+ │ ├── index.ts # Service definition export
19
+ │ └── ... # Additional service files
20
+ ├── <service-name>/
21
+ │ └── index.ts
22
+ └── <shared-resource>/ # Shared resources (databases, utils)
23
+ └── ...
24
+ ```
25
+
26
+ ## Key Principles
27
+
28
+ ### 1. The `_main` Folder
29
+
30
+ The `_main` folder contains the application entry point and service aggregation. The underscore prefix ensures it sorts to the top of the directory listing.
31
+
32
+ **`_main/api.ts`** - The main entry point. Loads environment variables, creates the App, and starts the server. See the `server-setup` doc for full `startServer` examples and the `env-files` doc for environment configuration.
33
+
34
+ **`_main/services.ts`** - Aggregates all services:
35
+
36
+ ```typescript
37
+ import { type ServiceDefinition } from '@facetlayer/prism-framework';
38
+ import { definition as usersDefinition } from '../users/index.ts';
39
+ import { definition as projectsDefinition } from '../projects/index.ts';
40
+
41
+ export const ALL_SERVICES: ServiceDefinition[] = [
42
+ usersDefinition,
43
+ projectsDefinition,
44
+ ];
45
+ ```
46
+
47
+ ### 2. Service Folders
48
+
49
+ Each service lives in its own top-level folder named after the service. The folder contains an `index.ts` that exports the service definition.
50
+
51
+ **Example: `users/index.ts`**
52
+
53
+ ```typescript
54
+ import { createEndpoint, type ServiceDefinition } from '@facetlayer/prism-framework';
55
+ import { z } from 'zod';
56
+
57
+ const getUserEndpoint = createEndpoint({
58
+ method: 'GET',
59
+ path: '/api/users/:id',
60
+ requestSchema: z.object({ id: z.string() }),
61
+ responseSchema: z.object({ id: z.string(), email: z.string() }),
62
+ handler: async (input) => {
63
+ // Implementation
64
+ },
65
+ });
66
+
67
+ export const definition: ServiceDefinition = {
68
+ name: 'users',
69
+ endpoints: [getUserEndpoint],
70
+ };
71
+ ```
72
+
73
+ ### 3. No `services` Directory
74
+
75
+ Services should NOT be placed in a `services/` subdirectory. Each service folder exists at the top level of `src/`:
76
+
77
+ **Correct:**
78
+ ```
79
+ src/
80
+ ├── _main/
81
+ ├── users/
82
+ ├── projects/
83
+ └── billing/
84
+ ```
85
+
86
+ **Incorrect:**
87
+ ```
88
+ src/
89
+ ├── index.ts
90
+ └── services/
91
+ ├── users.ts
92
+ ├── projects.ts
93
+ └── billing.ts
94
+ ```
95
+
96
+ ### 4. Shared Resources
97
+
98
+ Non-service folders (databases, utilities) can also exist at the top level:
99
+
100
+ ```
101
+ src/
102
+ ├── _main/
103
+ ├── users/
104
+ ├── projects/
105
+ └── user-database/ # Shared database module
106
+ └── db.ts
107
+ ```
108
+
109
+ ## Benefits
110
+
111
+ 1. **Discoverability** - The `_main` folder clearly identifies the entry point
112
+ 2. **Modularity** - Each service is self-contained in its own folder
113
+ 3. **Scalability** - Adding services means adding folders, not modifying existing structure
114
+ 4. **Consistency** - All projects follow the same pattern
115
+ 5. **Clarity** - The `ALL_SERVICES` array provides a single source of truth for what services exist