@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,176 @@
1
+ # Stdin/Stdout Protocol
2
+
3
+ Prism Framework apps can run in **stdin protocol mode** instead of starting an HTTP server. This is useful for:
4
+
5
+ - Embedding a Prism app as a subprocess in a larger application
6
+ - Composing multiple Prism apps to serve parts of a web UI
7
+ - Running in environments where opening a port is not desirable
8
+ - Building process-based microservice architectures
9
+
10
+ ## How It Works
11
+
12
+ When started with `--stdin`, the app communicates over **newline-delimited JSON (NDJSON)** on stdin and stdout:
13
+
14
+ - The parent process sends **request messages** as JSON lines to the app's stdin
15
+ - The app sends **response messages** as JSON lines to its stdout
16
+ - All logging is redirected to stderr so it doesn't interfere with the protocol
17
+
18
+ The same endpoints defined with `createEndpoint` work identically in both modes.
19
+
20
+ ## Usage
21
+
22
+ ### Setting Up Your App
23
+
24
+ Use `startStdinServer` as an alternative to `startServer`:
25
+
26
+ ```typescript
27
+ import {
28
+ createEndpoint, App, startServer, startStdinServer,
29
+ type ServiceDefinition,
30
+ } from '@facetlayer/prism-framework';
31
+ import { z } from 'zod';
32
+
33
+ const myService: ServiceDefinition = {
34
+ name: 'items',
35
+ endpoints: [
36
+ createEndpoint({
37
+ method: 'GET',
38
+ path: '/items',
39
+ responseSchema: z.array(z.object({ id: z.string(), name: z.string() })),
40
+ handler: async () => [{ id: '1', name: 'Item 1' }],
41
+ }),
42
+ ],
43
+ };
44
+
45
+ const app = new App({ services: [myService] });
46
+
47
+ if (process.argv.includes('--stdin')) {
48
+ startStdinServer({ app });
49
+ } else {
50
+ await startServer({ app, port: 3000 });
51
+ }
52
+ ```
53
+
54
+ ### Launching as a Subprocess
55
+
56
+ From the parent process, spawn the app and communicate over pipes:
57
+
58
+ ```typescript
59
+ import { spawn } from 'child_process';
60
+
61
+ const child = spawn('node', ['my-app.js', '--stdin'], {
62
+ stdio: ['pipe', 'pipe', 'pipe'],
63
+ });
64
+
65
+ // Send a request
66
+ child.stdin.write(JSON.stringify({
67
+ id: 'req-1',
68
+ method: 'GET',
69
+ path: '/items',
70
+ }) + '\n');
71
+
72
+ // Read responses line by line
73
+ let buffer = '';
74
+ child.stdout.on('data', (data) => {
75
+ buffer += data.toString();
76
+ const lines = buffer.split('\n');
77
+ buffer = lines.pop();
78
+ for (const line of lines) {
79
+ if (!line.trim()) continue;
80
+ const response = JSON.parse(line);
81
+ console.log('Response:', response);
82
+ }
83
+ });
84
+ ```
85
+
86
+ ## Protocol Specification
87
+
88
+ ### Request Message
89
+
90
+ Each request is a single JSON line written to the app's stdin:
91
+
92
+ ```json
93
+ {
94
+ "id": "unique-request-id",
95
+ "method": "GET",
96
+ "path": "/items/123",
97
+ "body": { "optional": "data" }
98
+ }
99
+ ```
100
+
101
+ | Field | Type | Required | Description |
102
+ |----------|--------|----------|------------------------------------------------|
103
+ | `id` | string | Yes | Unique ID to correlate the response |
104
+ | `method` | string | Yes | HTTP method: GET, POST, PUT, DELETE, PATCH |
105
+ | `path` | string | Yes | Endpoint path, e.g. `/items` or `/items/123` |
106
+ | `body` | object | No | Request body / input data |
107
+
108
+ ### Response Message
109
+
110
+ Each response is a single JSON line written to stdout:
111
+
112
+ ```json
113
+ {
114
+ "id": "unique-request-id",
115
+ "status": 200,
116
+ "body": { "id": "123", "name": "Item" }
117
+ }
118
+ ```
119
+
120
+ | Field | Type | Description |
121
+ |----------|--------|------------------------------------------------|
122
+ | `id` | string | Matches the request ID |
123
+ | `status` | number | HTTP-style status code (200, 400, 404, 500, etc.) |
124
+ | `body` | any | Response data or error details |
125
+
126
+ ### Ready Signal
127
+
128
+ When the app starts, it sends a ready message:
129
+
130
+ ```json
131
+ { "id": "_ready", "status": 200, "body": { "message": "stdin server ready" } }
132
+ ```
133
+
134
+ Wait for this message before sending requests.
135
+
136
+ ### Error Responses
137
+
138
+ Errors from handlers (using `NotFoundError`, `BadRequestError`, etc.) are returned with the appropriate status code:
139
+
140
+ ```json
141
+ { "id": "req-1", "status": 404, "body": { "message": "Item not found" } }
142
+ ```
143
+
144
+ Invalid JSON or missing fields return status 400:
145
+
146
+ ```json
147
+ { "id": "unknown", "status": 400, "body": { "message": "Invalid JSON" } }
148
+ ```
149
+
150
+ ### Process Lifecycle
151
+
152
+ - The app exits when stdin is closed (EOF)
153
+ - The parent should close stdin to signal the app to shut down
154
+ - All logging output goes to stderr, keeping stdout clean for the protocol
155
+
156
+ ## Composing Multiple Subprocess UIs
157
+
158
+ A key use case is running multiple Prism apps as subprocesses that each render a portion of a larger web UI. The parent process acts as a coordinator:
159
+
160
+ ```
161
+ ┌──────────────────────────────────────────┐
162
+ │ Parent Web Server │
163
+ │ │
164
+ │ ┌─────────────┐ ┌─────────────┐ │
165
+ │ │ App A │ │ App B │ │
166
+ │ │ (--stdin) │ │ (--stdin) │ │
167
+ │ │ /dashboard/* │ │ /settings/* │ │
168
+ │ └──────┬───────┘ └──────┬──────┘ │
169
+ │ │stdin/stdout │stdin/stdout │
170
+ │ └─────────┬────────┘ │
171
+ │ │ │
172
+ │ Request Router │
173
+ └──────────────────────────────────────────┘
174
+ ```
175
+
176
+ The parent process routes incoming HTTP requests to the appropriate subprocess based on path prefix, translates them to stdin protocol messages, and sends the subprocess responses back to the browser.
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@facetlayer/prism-framework",
3
- "version": "0.4.0",
4
- "description": "Base library and CLI tools for the Prism app framework",
3
+ "version": "0.4.1",
4
+ "description": "Base library, server framework, and CLI tools for the Prism app framework",
5
5
  "type": "module",
6
- "main": "dist/cli.js",
7
- "types": "dist/cli.d.ts",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
8
  "bin": {
9
9
  "prism": "dist/cli.js"
10
10
  },
@@ -17,26 +17,59 @@
17
17
  },
18
18
  "keywords": [
19
19
  "framework",
20
- "testing",
21
- "tools",
22
- "typescript"
20
+ "express",
21
+ "electron",
22
+ "typescript",
23
+ "saas",
24
+ "tools"
23
25
  ],
24
26
  "author": "",
25
27
  "license": "MIT",
28
+ "files": [
29
+ "src",
30
+ "dist",
31
+ "docs",
32
+ "README.md"
33
+ ],
26
34
  "packageManager": "pnpm@10.15.1",
27
35
  "dependencies": {
36
+ "@asteasolutions/zod-to-openapi": "^8.1.0",
28
37
  "@facetlayer/doc-files-helper": "0.1.2",
29
- "@facetlayer/prism-framework-api": "0.2.0",
30
38
  "@facetlayer/qc": "^0.1.0",
39
+ "@facetlayer/sqlite-wrapper": "^1.2.0",
40
+ "@facetlayer/streams": "^1.0.0",
41
+ "cookie-parser": "^1.4.7",
31
42
  "dotenv": "^16.4.7",
43
+ "express": "^4.21.1",
44
+ "openapi3-ts": "^4.5.0",
45
+ "prom-client": "^15.1.3",
46
+ "swagger-ui-express": "^5.0.1",
32
47
  "uuid": "^11.1.0",
33
- "yargs": "18.0.0"
48
+ "yargs": "18.0.0",
49
+ "zod": "^4.1.12"
50
+ },
51
+ "peerDependencies": {
52
+ "vite": "^6.0.0",
53
+ "zod": "^4.0.5"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "vite": {
57
+ "optional": true
58
+ },
59
+ "zod": {
60
+ "optional": false
61
+ }
34
62
  },
35
63
  "devDependencies": {
36
64
  "@facetlayer/build-config-nodejs": "^0.3.0",
65
+ "@facetlayer/subprocess": "^1.1.1",
66
+ "@types/cookie-parser": "^1.4.7",
67
+ "@types/express": "^5.0.0",
37
68
  "@types/node": "^22.10.2",
69
+ "@types/swagger-ui-express": "^4.1.8",
38
70
  "@types/uuid": "^10.0.0",
39
71
  "@types/yargs": "17.0.34",
72
+ "esbuild": "^0.25.12",
40
73
  "typescript": "^5.7.2",
41
74
  "vitest": "^3.0.0"
42
75
  }
package/src/Errors.ts ADDED
@@ -0,0 +1,120 @@
1
+ /*
2
+ Errors
3
+
4
+ Helper classes for various HTTP error codes.
5
+ */
6
+
7
+ export class HttpError extends Error {
8
+ public statusCode: number;
9
+ public details?: any;
10
+
11
+ constructor(statusCode: number, message: string, details?: any) {
12
+ super(message);
13
+ this.statusCode = statusCode;
14
+ this.details = details;
15
+ this.name = 'HttpError';
16
+ }
17
+ }
18
+
19
+ export class BadRequestError extends HttpError {
20
+ constructor(message: string = 'Bad Request', details?: any) {
21
+ super(400, message, details);
22
+ this.name = 'BadRequestError';
23
+ }
24
+ }
25
+
26
+ export class SchemaValidationError extends HttpError {
27
+ constructor(message: string = 'Schema Validation Error', details?: any) {
28
+ super(422, message, details);
29
+ this.name = 'SchemaValidationError';
30
+ }
31
+ }
32
+
33
+ export class ResponseSchemaValidationError extends HttpError {
34
+ constructor(message: string = 'Response Schema Validation Error', details?: any) {
35
+ super(500, message, details);
36
+ this.name = 'ResponseSchemaValidationError';
37
+ }
38
+ }
39
+
40
+ export class UnauthorizedError extends HttpError {
41
+ constructor(message: string = 'Unauthorized', details?: any) {
42
+ super(401, message, details);
43
+ this.name = 'UnauthorizedError';
44
+ }
45
+ }
46
+
47
+ export class ForbiddenError extends HttpError {
48
+ constructor(message: string = 'Forbidden', details?: any) {
49
+ super(403, message, details);
50
+ this.name = 'ForbiddenError';
51
+ }
52
+ }
53
+
54
+ export class NotFoundError extends HttpError {
55
+ constructor(message: string = 'Not Found', details?: any) {
56
+ super(404, message, details);
57
+ this.name = 'NotFoundError';
58
+ }
59
+ }
60
+
61
+ export class ConflictError extends HttpError {
62
+ constructor(message: string = 'Conflict', details?: any) {
63
+ super(409, message, details);
64
+ this.name = 'ConflictError';
65
+ }
66
+ }
67
+
68
+ export class ValidationError extends HttpError {
69
+ constructor(message: string = 'Validation Error', details?: any) {
70
+ super(422, message, details);
71
+ this.name = 'ValidationError';
72
+ }
73
+ }
74
+
75
+ export class NotImplementedError extends HttpError {
76
+ constructor(message: string = 'Not Implemented', details?: any) {
77
+ super(501, message, details);
78
+ this.name = 'NotImplementedError';
79
+ }
80
+ }
81
+
82
+ export class ServiceUnavailableError extends HttpError {
83
+ constructor(message: string = 'Service Unavailable', details?: any) {
84
+ super(503, message, details);
85
+ this.name = 'ServiceUnavailableError';
86
+ }
87
+ }
88
+
89
+ export function createErrorFromStatus(
90
+ statusCode: number,
91
+ message?: string,
92
+ details?: any
93
+ ): HttpError {
94
+ switch (statusCode) {
95
+ case 400:
96
+ return new BadRequestError(message, details);
97
+ case 401:
98
+ return new UnauthorizedError(message, details);
99
+ case 403:
100
+ return new ForbiddenError(message, details);
101
+ case 404:
102
+ return new NotFoundError(message, details);
103
+ case 409:
104
+ return new ConflictError(message, details);
105
+ case 422:
106
+ return new ValidationError(message, details);
107
+ case 500:
108
+ return new HttpError(500, message || 'Internal Server Error', details);
109
+ case 501:
110
+ return new NotImplementedError(message, details);
111
+ case 503:
112
+ return new ServiceUnavailableError(message, details);
113
+ default:
114
+ return new HttpError(statusCode, message || 'Unknown Error', details);
115
+ }
116
+ }
117
+
118
+ export function isHttpError(error: any): error is HttpError {
119
+ return error instanceof HttpError;
120
+ }
package/src/Metrics.ts ADDED
@@ -0,0 +1,53 @@
1
+ import PromClient from 'prom-client';
2
+
3
+ let _hasSetupMetrics = false;
4
+ let httpRequests: PromClient.Counter;
5
+ let httpResponses: PromClient.Counter;
6
+
7
+ export function setupMetrics(): void {
8
+ _hasSetupMetrics = true;
9
+
10
+ PromClient.collectDefaultMetrics({
11
+ // prefix: ...
12
+ // labels: ...
13
+ });
14
+
15
+ httpRequests = new PromClient.Counter({
16
+ name: 'http_request_counter',
17
+ help: 'HTTP requests',
18
+ labelNames: ['method', 'endpoint'],
19
+ });
20
+ httpResponses = new PromClient.Counter({
21
+ name: 'http_response_counter',
22
+ help: 'HTTP responses',
23
+ labelNames: ['method', 'endpoint', 'status_code', 'duration'],
24
+ });
25
+ }
26
+
27
+ // Function to record an HTTP request
28
+ export function recordHttpRequest(method: string, endpoint: string): void {
29
+ if (!_hasSetupMetrics) {
30
+ setupMetrics();
31
+ }
32
+ httpRequests.inc({ method, endpoint });
33
+ }
34
+
35
+ export function recordHttpResponse(
36
+ method: string,
37
+ endpoint: string,
38
+ statusCode: number,
39
+ duration: number
40
+ ): void {
41
+ if (!_hasSetupMetrics) {
42
+ setupMetrics();
43
+ }
44
+ httpResponses.inc({ method, endpoint, status_code: statusCode.toString(), duration });
45
+ }
46
+
47
+ // Function to get metrics in Prometheus format
48
+ export function getMetrics(): Promise<string> {
49
+ if (!_hasSetupMetrics) {
50
+ setupMetrics();
51
+ }
52
+ return PromClient.register.metrics();
53
+ }
@@ -0,0 +1,36 @@
1
+ import { AsyncLocalStorage } from 'async_hooks';
2
+ import type { Request, Response } from 'express';
3
+ import type { Authorization } from './authorization/Authorization.ts';
4
+
5
+ /*
6
+ * RequestContext
7
+ *
8
+ * Helper object stored on every incoming request using AsyncLocalStorage.
9
+ *
10
+ * Includes
11
+ * - The request and response objects
12
+ * - Authorization data
13
+ */
14
+
15
+ export interface RequestContext {
16
+ requestId: string;
17
+ startTime: number;
18
+ req?: Request;
19
+ res?: Response;
20
+ auth: Authorization;
21
+ }
22
+
23
+ export const requestContextStorage = new AsyncLocalStorage<RequestContext>();
24
+
25
+
26
+ export function withRequestContext<T>(context: RequestContext, fn: () => T): T {
27
+ return requestContextStorage.run(context, fn);
28
+ }
29
+
30
+ export function getCurrentRequestContext(): RequestContext | undefined {
31
+ return requestContextStorage.getStore();
32
+ }
33
+
34
+ export const RequestContext = {
35
+ getCurrentRequestContext,
36
+ };
@@ -0,0 +1,35 @@
1
+ import type { NextFunction, Request, Response } from 'express';
2
+ import type { EndpointDefinition } from './web/ExpressEndpointSetup.ts';
3
+
4
+ export interface MiddlewareDefinition {
5
+ path: string;
6
+ handler: (req: Request, res: Response, next: NextFunction) => void;
7
+ }
8
+
9
+ /*
10
+ * ServiceDefinition
11
+ *
12
+ * A service is a self-contained module that can contain:
13
+ * - API endpoints
14
+ * - Middleware
15
+ * - Database schemas
16
+ * - Background jobs
17
+ */
18
+
19
+ export interface ServiceDefinition {
20
+ name: string;
21
+
22
+ // API endpoints
23
+ endpoints?: EndpointDefinition[];
24
+
25
+ // Middleware
26
+ middleware?: MiddlewareDefinition[];
27
+
28
+ // Database schemas
29
+ databases?: Record<string, {
30
+ statements: string[];
31
+ }>;
32
+
33
+ // Callback used to launch background jobs
34
+ startJobs?: () => Promise<void>;
35
+ }