@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,63 @@
1
+ import express from 'express';
2
+
3
+ export interface CorsConfig {
4
+ /** Base URL for web application (e.g., 'example.com' or 'https://example.com') */
5
+ webBaseUrl?: string;
6
+ /** Allow any localhost origin (http://localhost:*) for local development */
7
+ allowLocalhost?: boolean;
8
+ /**
9
+ * @deprecated Use `allowLocalhost` instead. This field controls both test endpoints and localhost CORS.
10
+ * When `allowLocalhost` is set, it takes precedence for CORS behavior.
11
+ */
12
+ enableTestEndpoints?: boolean;
13
+ }
14
+
15
+ function setACAOHeader(res: express.Response, reqOrigin: string, config: CorsConfig) {
16
+ const webBaseUrl = config.webBaseUrl;
17
+
18
+ if (!reqOrigin) {
19
+ // No origin header - probably a same-origin request.
20
+ if (webBaseUrl) {
21
+ res.header('Access-Control-Allow-Origin', webBaseUrl);
22
+ }
23
+ return;
24
+ }
25
+
26
+ // Origin header is present - check for whitelisted cases
27
+ if (webBaseUrl) {
28
+ const allowedOrigins = [`https://${webBaseUrl}`];
29
+ if (allowedOrigins.includes(reqOrigin)) {
30
+ // Matches whitelist
31
+ res.header('Access-Control-Allow-Origin', reqOrigin);
32
+ return;
33
+ }
34
+ }
35
+
36
+ // Allow any localhost port for local development
37
+ const localhostAllowed = config.allowLocalhost ?? config.enableTestEndpoints;
38
+ if (localhostAllowed && reqOrigin.startsWith('http://localhost:')) {
39
+ res.header('Access-Control-Allow-Origin', reqOrigin);
40
+ return;
41
+ }
42
+ }
43
+
44
+ export function corsMiddleware(config: CorsConfig = {}) {
45
+ return (req: express.Request, res: express.Response, next: express.NextFunction) => {
46
+ setACAOHeader(res, req.headers.origin, config);
47
+ res.header('Access-Control-Allow-Credentials', 'true');
48
+ res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH');
49
+ res.header(
50
+ 'Access-Control-Allow-Headers',
51
+ 'Origin, X-Requested-With, Content-Type, Accept, Authorization, Cookie, Cache-Control'
52
+ );
53
+ res.header('Access-Control-Max-Age', '86400');
54
+
55
+ // Handle preflight requests
56
+ if (req.method === 'OPTIONS') {
57
+ res.sendStatus(200);
58
+ return;
59
+ }
60
+
61
+ next();
62
+ };
63
+ }
@@ -0,0 +1,19 @@
1
+ import type { NextFunction, Request, Response } from 'express';
2
+
3
+ /*
4
+ localhostOnlyMiddleware
5
+
6
+ Special middleware that restricts an endpoint so that it can only be used by localhost client.
7
+ */
8
+
9
+ export function localhostOnlyMiddleware(req: Request, res: Response, next: NextFunction) {
10
+ const allowedIPs = ['127.0.0.1', '::1', '::ffff:127.0.0.1']; // localhost variations
11
+
12
+ const clientIP = req.ip || req.connection?.remoteAddress;
13
+
14
+ if (!allowedIPs.includes(clientIP)) {
15
+ return res.status(404).json({ error: 'Not found' });
16
+ }
17
+
18
+ next();
19
+ }
@@ -0,0 +1,248 @@
1
+ /*
2
+ * OpenAPI
3
+ *
4
+ * Generates OpenAPI schema from service definitions.
5
+ * This module transforms endpoint definitions into a complete OpenAPI 3.1.0 specification.
6
+ */
7
+
8
+ import {
9
+ OpenAPIRegistry,
10
+ OpenApiGeneratorV31,
11
+ type RouteConfig,
12
+ extendZodWithOpenApi,
13
+ } from '@asteasolutions/zod-to-openapi';
14
+ import swaggerUi from 'swagger-ui-express';
15
+ import type { OpenAPIObject } from 'openapi3-ts/oas31';
16
+ import z from 'zod';
17
+ import type { ServiceDefinition } from '../../ServiceDefinition.ts';
18
+ import { PrismApp } from '../../app/PrismApp.ts';
19
+ import express, { type Request, type Response } from 'express';
20
+ import { captureError } from '@facetlayer/streams'
21
+ import { validateServicesForOpenapi } from './validateServicesForOpenapi.ts';
22
+ import { getEffectiveOperationId } from '../../endpoints/createEndpoint.ts';
23
+
24
+ export { validateServicesForOpenapi as validateEndpointForOpenapi } from './validateServicesForOpenapi.ts';
25
+
26
+ type RequestConfig = RouteConfig['request'];
27
+
28
+ export interface OpenAPIConfig {
29
+ enable: boolean
30
+ enableSwagger?: boolean
31
+ }
32
+
33
+ export type ParseExpressPathForOpenAPIResult = {
34
+ openApiPath: string;
35
+ pathParams: string[];
36
+ };
37
+
38
+ export interface OpenAPIDocumentInfo {
39
+ version: string;
40
+ title: string;
41
+ description: string;
42
+ }
43
+
44
+ // Globally modify Zod to support the .openapi() helper method.
45
+ extendZodWithOpenApi(z);
46
+
47
+ /**
48
+ * Transforms path parameters in an Express-style URL path (e.g. :pathParameter) into their OpenAPI equivalents (e.g. {pathParameter})
49
+ * Returns the transformed OpenAPI-style URL path along with any path parameters found.
50
+ *
51
+ * @param {string} expressApiPath - An Express-style URL path
52
+ * @returns {ParseExpressPathForOpenAPIResult} - the transformed OpenAPI-style URL path with any path parameters found
53
+ */
54
+ export function parseExpressPathForOpenAPI(expressApiPath: string): ParseExpressPathForOpenAPIResult {
55
+ const expressApiPathParts: string[] = expressApiPath.split('/');
56
+ const pathParams: string[] = [];
57
+ const openApiPathParts: string[] = [];
58
+
59
+ for (const part of expressApiPathParts) {
60
+ if (part.startsWith(':')) {
61
+ pathParams.push(part.substring(1));
62
+ openApiPathParts.push(`{${part.substring(1)}}`);
63
+ } else {
64
+ openApiPathParts.push(part);
65
+ }
66
+ }
67
+
68
+ return {
69
+ openApiPath: openApiPathParts.join('/'),
70
+ pathParams,
71
+ };
72
+ }
73
+
74
+ /**
75
+ * Generates the Open API Schema for all services.
76
+ *
77
+ * @param {ServiceDefinition[]} services - Array of service definitions to generate OpenAPI schema for
78
+ * @param {OpenAPIDocumentInfo} documentInfo - Metadata for the OpenAPI document
79
+ * @returns {OpenAPIObject} - the Open API schema for all service definitions.
80
+ */
81
+ export function generateOpenAPISchema(
82
+ services: ServiceDefinition[],
83
+ documentInfo: OpenAPIDocumentInfo
84
+ ): OpenAPIObject {
85
+ const registry: OpenAPIRegistry = new OpenAPIRegistry();
86
+
87
+ // Register all endpoints from all services
88
+ for (const service of services) {
89
+ const endpoints = service.endpoints || [];
90
+
91
+ for (const endpoint of endpoints) {
92
+ const { openApiPath, pathParams }: ParseExpressPathForOpenAPIResult =
93
+ parseExpressPathForOpenAPI(endpoint.path);
94
+ const requestConfig: RequestConfig = {};
95
+
96
+ // Specify path parameters: Extract path parameters from the route path and create a Zod schema from it.
97
+ if (pathParams.length > 0) {
98
+ // TODO: specify stricter typing for path parameters (e.g. enums/numeric values)
99
+ const pathParamsSchema: Record<string, z.ZodString> = {};
100
+ for (const param of pathParams) {
101
+ pathParamsSchema[param] = z.string();
102
+ }
103
+ requestConfig.params = z.object(pathParamsSchema);
104
+ }
105
+
106
+ // Specify body parameters: If the endpoint provides a request schema, use that as the body parameter
107
+ if (endpoint.requestSchema) {
108
+ requestConfig.body = {
109
+ content: {
110
+ 'application/json': {
111
+ schema: endpoint.requestSchema,
112
+ },
113
+ },
114
+ };
115
+ }
116
+
117
+ registry.registerPath({
118
+ method: endpoint.method.toLowerCase() as any,
119
+ path: openApiPath,
120
+ description: endpoint.description || `${endpoint.method} ${endpoint.path}`,
121
+ operationId: getEffectiveOperationId(endpoint),
122
+ request: requestConfig,
123
+ responses: {
124
+ 200: {
125
+ description: 'Success',
126
+ content: {
127
+ 'application/json': {
128
+ schema: endpoint.responseSchema ?? z.any(),
129
+ },
130
+ },
131
+ },
132
+ /*
133
+ 400: {
134
+ description: 'Bad Request - Schema validation failed',
135
+ content: {
136
+ 'application/json': {
137
+ schema: z.object({
138
+ error: z.string(),
139
+ details: z.array(z.any()),
140
+ }),
141
+ },
142
+ },
143
+ },
144
+ 401: {
145
+ description: 'Unauthorized',
146
+ content: {
147
+ 'application/json': {
148
+ schema: z.object({
149
+ message: z.string(),
150
+ details: z.any().optional(),
151
+ }),
152
+ },
153
+ },
154
+ },
155
+ 500: {
156
+ description: 'Internal Server Error',
157
+ content: {
158
+ 'application/json': {
159
+ schema: z.object({
160
+ message: z.string(),
161
+ }),
162
+ },
163
+ },
164
+ },
165
+ */
166
+ },
167
+ });
168
+ }
169
+ }
170
+
171
+ const generator: OpenApiGeneratorV31 = new OpenApiGeneratorV31(
172
+ registry.definitions
173
+
174
+ /*
175
+ .concat([
176
+ {
177
+ type: 'component',
178
+ componentType: 'securitySchemes',
179
+ name: 'bearer_auth',
180
+ component: {
181
+ type: 'http',
182
+ scheme: 'bearer',
183
+ bearerFormat: 'JWT',
184
+ },
185
+ },
186
+ ])
187
+ */
188
+ );
189
+
190
+ return generator.generateDocument({
191
+ openapi: '3.1.0',
192
+ info: {
193
+ version: documentInfo.version,
194
+ title: documentInfo.title,
195
+ description: documentInfo.description,
196
+ },
197
+ servers: [
198
+ { url: '/api', description: 'API server' },
199
+ ],
200
+ security: [
201
+ {
202
+ bearer_auth: [],
203
+ },
204
+ ],
205
+ });
206
+ }
207
+
208
+ export function setupSwaggerUI(app: express.Application | express.Router, openApiJsonPath: string = '/openapi.json'): void {
209
+ const router = app as express.Router;
210
+ // Serve Swagger UI on /swagger
211
+ router.use(
212
+ '/swagger',
213
+ swaggerUi.serve,
214
+ swaggerUi.setup(null, {
215
+ swaggerOptions: {
216
+ url: openApiJsonPath,
217
+ },
218
+ })
219
+ );
220
+ }
221
+
222
+ export function mountOpenAPIEndpoints(config: OpenAPIConfig, target: express.Application | express.Router, prismApp: PrismApp): void {
223
+ const router = target as express.Router;
224
+ router.get('/openapi.json', (req: Request, res: Response) => {
225
+ const services = prismApp.getAllServices();
226
+ try {
227
+ res.json(generateOpenAPISchema(services, {
228
+ version: '1.0.0',
229
+ title: prismApp.name,
230
+ description: prismApp.description,
231
+ }));
232
+ } catch (error) {
233
+
234
+ const validationResult = validateServicesForOpenapi(services);
235
+
236
+ console.error("/openapi.json failed to generate schema", {
237
+ cause: captureError(error),
238
+ problemEndpoints: validationResult.problemEndpoints,
239
+ });
240
+
241
+ res.status(500).json({ error: "Internal server error" });
242
+ }
243
+ });
244
+
245
+ if (config.enableSwagger) {
246
+ setupSwaggerUI(router, '/api/openapi.json');
247
+ }
248
+ }
@@ -0,0 +1,76 @@
1
+ import type { ServiceDefinition } from "../../ServiceDefinition.ts";
2
+ import type { EndpointDefinition } from "../ExpressEndpointSetup.ts";
3
+ import { generateOpenAPISchema } from "./OpenAPI.ts";
4
+ import { captureError, type ErrorDetails } from "@facetlayer/streams";
5
+
6
+ export interface EndpointValidationResult {
7
+ error: ErrorDetails
8
+ }
9
+
10
+ export interface FailedEndpoint {
11
+ serviceName: string;
12
+ path: string;
13
+ method: string;
14
+ error: ErrorDetails;
15
+ }
16
+
17
+ export interface ServicesValidationResult {
18
+ problemEndpoints: FailedEndpoint[];
19
+ }
20
+
21
+
22
+ /**
23
+ * Validates a single endpoint for OpenAPI schema generation compatibility.
24
+ *
25
+ * @param serviceName - Name of the service containing the endpoint
26
+ * @param endpoint - The endpoint definition to validate
27
+ * @returns ProblematicEndpoint if validation fails, null if endpoint is valid
28
+ */
29
+ export function validateEndpointForOpenapi(
30
+ endpoint: EndpointDefinition
31
+ ): EndpointValidationResult {
32
+ const testService: ServiceDefinition = {
33
+ name: 'test',
34
+ endpoints: [endpoint],
35
+ };
36
+
37
+ try {
38
+ generateOpenAPISchema([testService], {
39
+ version: '1.0.0',
40
+ title: 'Test',
41
+ description: 'Test',
42
+ });
43
+ return null;
44
+ } catch (error) {
45
+ return { error: captureError(error) };
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Finds endpoints that will fail OpenAPI schema generation.
51
+ * Tests each endpoint individually to identify which ones have unsupported Zod types.
52
+ *
53
+ * @param services - Array of service definitions to check
54
+ * @returns ValidationResult containing any problematic endpoints
55
+ */
56
+ export function validateServicesForOpenapi(services: ServiceDefinition[]): ServicesValidationResult {
57
+ const problemEndpoints: FailedEndpoint[] = [];
58
+
59
+ for (const service of services) {
60
+ const endpoints = service.endpoints || [];
61
+
62
+ for (const endpoint of endpoints) {
63
+ const endpointResult = validateEndpointForOpenapi(endpoint);
64
+ if (endpointResult?.error) {
65
+ problemEndpoints.push({
66
+ serviceName: service.name,
67
+ path: endpoint.path,
68
+ method: endpoint.method,
69
+ error: endpointResult.error,
70
+ });
71
+ }
72
+ }
73
+ }
74
+
75
+ return { problemEndpoints };
76
+ }
@@ -0,0 +1,25 @@
1
+ import { AsyncLocalStorage } from 'async_hooks';
2
+ import type { NextFunction, Request, Response } from 'express';
3
+ import { v4 as uuidv4 } from 'uuid';
4
+ import { Authorization } from '../authorization/Authorization.ts';
5
+ import type { RequestContext } from '../RequestContext.ts';
6
+ import { requestContextStorage } from '../RequestContext.ts';
7
+
8
+ export function requestContextMiddleware(req: Request, res: Response, next: NextFunction): void {
9
+ const requestId = uuidv4();
10
+ const startTime = Date.now();
11
+
12
+ const context: RequestContext = {
13
+ requestId,
14
+ startTime,
15
+ req,
16
+ res,
17
+ auth: new Authorization(),
18
+ };
19
+
20
+ res.setHeader('X-Request-ID', requestId);
21
+
22
+ requestContextStorage.run(context, () => {
23
+ next();
24
+ });
25
+ }
@@ -1,20 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(git mv:*)",
5
- "Bash(prism-endpoint:*)",
6
- "Bash(pnpm build:*)",
7
- "Bash(pnpm install:*)",
8
- "Bash(pnpm typecheck:*)",
9
- "Skill(vibe-code-cleanup)",
10
- "Bash(node build.mts:*)",
11
- "Bash(pnpm test:*)",
12
- "Bash(node dist/cli.js:*)",
13
- "Bash(mkdir:*)",
14
- "Bash(node /Users/andy/node-libraries/prism-framework-tools/dist/cli.js generate-api-clients:*)",
15
- "Bash(pnpm local:install:*)"
16
- ],
17
- "deny": [],
18
- "ask": []
19
- }
20
- }
package/CHANGELOG DELETED
@@ -1,28 +0,0 @@
1
-
2
- 0.4.0
3
- - Renamed from @facetlayer/prism-framework-tools to @facetlayer/prism-framework
4
-
5
- 0.3.0
6
- - Add config file support (.prism.qc) with parent directory search
7
- - Fix API client type generation issues
8
- - Add generate-api-clients-config documentation
9
- - Add endpoint-tools and env-files documentation
10
- - Safety checks for Zod schemas incompatible with OpenAPI
11
- - Improve getting-started docs
12
-
13
- 0.2.5
14
- - Add list-docs and get-doc (with doc-files-helper)
15
-
16
- 0.2.4
17
- - Handle JSONish params in 'call' command
18
- - generate-api-clients now uses --out param
19
-
20
- 0.2.3
21
- - Fix for list-endpoints
22
-
23
- 0.2.1
24
- - Add list-endpoints command
25
- - Call endpoint - don't fail on response schema failure
26
-
27
- 0.1.0
28
- - initial version
package/CLAUDE.md DELETED
@@ -1,44 +0,0 @@
1
- # prism-framework
2
-
3
- Base library and CLI tools for the Prism app framework ecosystem.
4
-
5
- ## Important Files and Directories
6
-
7
- ### Source Code (`src/`)
8
- - `cli.ts` - Main CLI entry point, uses yargs for argument parsing
9
- - `call-command.ts` - Logic for calling endpoints, handles JSON parsing of arguments
10
- - `generate-api-clients.ts` - Generates TypeScript types from OpenAPI schema
11
- - `list-endpoints-command.ts` - Lists available endpoints from the API server
12
- - `loadEnv.ts` - Environment variable loading and validation
13
- - `getPorts.ts` - Port number utilities
14
-
15
- ### Documentation (`docs/`)
16
-
17
- Markdown files for documentation.
18
-
19
- Run `doc-files list-docs` to understand the format.
20
-
21
- - `getting-started.md` - Setup guide for Prism Framework projects
22
- - `run-endpoint-tool.md` - Detailed CLI usage documentation
23
- - `env-files.md` - Environment configuration strategy
24
-
25
- ### Tests (`test/`)
26
- - `call-command.test.ts` - Unit tests for argument parsing logic
27
-
28
- ### Build Output
29
- - `dist/cli.js` - Compiled CLI executable (ES modules)
30
-
31
- ## Build Commands
32
-
33
- ```bash
34
- pnpm build # Build the project
35
- pnpm test # Run tests with Vitest
36
- pnpm typecheck # TypeScript type checking
37
- ```
38
-
39
- ## Key Dependencies
40
-
41
- - `yargs` - CLI argument parsing
42
- - `dotenv` - Environment variable loading
43
- - `@facetlayer/doc-files-helper` - Documentation file management
44
- - `@facetlayer/prism-framework-api` - Prism API framework types
package/build.mts DELETED
@@ -1,8 +0,0 @@
1
- #! /usr/bin/env node
2
-
3
- import { runBuildTool } from '@facetlayer/build-config-nodejs';
4
- import { cpSync } from 'fs';
5
-
6
- await runBuildTool({
7
- entryPoints: ['src/cli.ts'],
8
- });
@@ -1,96 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { parseNamedArgs } from '../src/call-command';
3
-
4
- describe('parseNamedArgs', () => {
5
- describe('basic values', () => {
6
- it('should pass through simple string values', () => {
7
- const result = parseNamedArgs({ name: 'John', email: 'john@example.com' });
8
- expect(result).toEqual({ name: 'John', email: 'john@example.com' });
9
- });
10
-
11
- it('should pass through non-string values', () => {
12
- const result = parseNamedArgs({ count: 42, active: true });
13
- expect(result).toEqual({ count: 42, active: true });
14
- });
15
- });
16
-
17
- describe('JSON parsing', () => {
18
- it('should parse JSON object strings', () => {
19
- const result = parseNamedArgs({ config: '{"timeout": 30}' });
20
- expect(result).toEqual({ config: { timeout: 30 } });
21
- });
22
-
23
- it('should parse JSON array strings', () => {
24
- const result = parseNamedArgs({ items: '["a", "b", "c"]' });
25
- expect(result).toEqual({ items: ['a', 'b', 'c'] });
26
- });
27
-
28
- it('should handle whitespace around JSON', () => {
29
- const result = parseNamedArgs({ data: ' {"key": "value"} ' });
30
- expect(result).toEqual({ data: { key: 'value' } });
31
- });
32
-
33
- it('should keep invalid JSON as string', () => {
34
- const result = parseNamedArgs({ bad: '{not valid json}' });
35
- expect(result).toEqual({ bad: '{not valid json}' });
36
- });
37
-
38
- it('should not parse strings that only start with { or [', () => {
39
- const result = parseNamedArgs({ text: '{hello world' });
40
- expect(result).toEqual({ text: '{hello world' });
41
- });
42
- });
43
-
44
- describe('nested objects from yargs', () => {
45
- it('should parse JSON strings inside nested objects', () => {
46
- // Yargs creates nested objects from dot notation before we see them
47
- const result = parseNamedArgs({
48
- schema: {
49
- name: 'test-schema',
50
- statements: '["CREATE TABLE test (id INT)"]'
51
- }
52
- });
53
- expect(result).toEqual({
54
- schema: {
55
- name: 'test-schema',
56
- statements: ['CREATE TABLE test (id INT)']
57
- }
58
- });
59
- });
60
-
61
- it('should handle deeply nested objects with JSON strings', () => {
62
- const result = parseNamedArgs({
63
- config: {
64
- database: {
65
- options: '{"timeout": 30, "retries": 3}'
66
- }
67
- }
68
- });
69
- expect(result).toEqual({
70
- config: {
71
- database: {
72
- options: { timeout: 30, retries: 3 }
73
- }
74
- }
75
- });
76
- });
77
- });
78
-
79
- describe('real-world example from test.sh', () => {
80
- it('should handle the migration command args', () => {
81
- // Yargs parses --schema.name and --schema.statements into nested object
82
- const result = parseNamedArgs({
83
- schema: {
84
- name: 'test-schema-v2',
85
- statements: '["CREATE TABLE test_products (id INTEGER PRIMARY KEY, name TEXT, price REAL)"]'
86
- }
87
- });
88
- expect(result).toEqual({
89
- schema: {
90
- name: 'test-schema-v2',
91
- statements: ['CREATE TABLE test_products (id INTEGER PRIMARY KEY, name TEXT, price REAL)']
92
- }
93
- });
94
- });
95
- });
96
- });
@@ -1,33 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { convertToExpressPath } from '../src/generate-api-clients';
3
-
4
- describe('convertToExpressPath', () => {
5
- it('should convert single path parameter', () => {
6
- expect(convertToExpressPath('/users/{id}')).toBe('/users/:id');
7
- });
8
-
9
- it('should convert multiple path parameters', () => {
10
- expect(convertToExpressPath('/users/{userId}/posts/{postId}')).toBe('/users/:userId/posts/:postId');
11
- });
12
-
13
- it('should handle paths without parameters', () => {
14
- expect(convertToExpressPath('/users')).toBe('/users');
15
- expect(convertToExpressPath('/api/health')).toBe('/api/health');
16
- });
17
-
18
- it('should handle root path', () => {
19
- expect(convertToExpressPath('/')).toBe('/');
20
- });
21
-
22
- it('should handle complex parameter names', () => {
23
- expect(convertToExpressPath('/designs/{designId}/nodes/{nodeId}')).toBe('/designs/:designId/nodes/:nodeId');
24
- });
25
-
26
- it('should handle parameter at the end of path', () => {
27
- expect(convertToExpressPath('/api/items/{id}')).toBe('/api/items/:id');
28
- });
29
-
30
- it('should handle parameter with underscores', () => {
31
- expect(convertToExpressPath('/api/{user_id}/profile')).toBe('/api/:user_id/profile');
32
- });
33
- });