@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,350 @@
1
+ import { Authorization } from '../authorization/Authorization.ts';
2
+ import { Resource } from '../authorization/Resource.ts';
3
+ import { AuthSource, CookieAuthSource } from '../authorization/AuthSource.ts';
4
+ import { describe, expect, it } from 'vitest';
5
+
6
+ describe('Authorization', () => {
7
+ describe('constructor', () => {
8
+ it('should create an empty Authorization instance', () => {
9
+ const auth = new Authorization();
10
+ expect(auth.getAllResources()).toEqual([]);
11
+ expect(auth.getAuthSources()).toEqual([]);
12
+ });
13
+
14
+ it('should initialize with provided resources', () => {
15
+ const initialResources: Resource[] = [
16
+ { type: 'user', id: 'user123' },
17
+ { type: 'project', id: 'project456' },
18
+ ];
19
+ const auth = new Authorization(initialResources);
20
+ expect(auth.getAllResources()).toHaveLength(2);
21
+ });
22
+
23
+ it('should initialize with provided auth sources', () => {
24
+ const authSources: AuthSource[] = [
25
+ { type: 'cookie', sessionId: 'session123' } as CookieAuthSource,
26
+ ];
27
+ const auth = new Authorization([], authSources);
28
+ expect(auth.getAuthSources()).toHaveLength(1);
29
+ });
30
+
31
+ it('should initialize with both resources and auth sources', () => {
32
+ const resources: Resource[] = [{ type: 'user', id: 'user123' }];
33
+ const authSources: AuthSource[] = [
34
+ { type: 'cookie', sessionId: 'session123' } as CookieAuthSource,
35
+ ];
36
+ const auth = new Authorization(resources, authSources);
37
+ expect(auth.getAllResources()).toHaveLength(1);
38
+ expect(auth.getAuthSources()).toHaveLength(1);
39
+ });
40
+ });
41
+
42
+ describe('addResource', () => {
43
+ it('should add a user resource', () => {
44
+ const auth = new Authorization();
45
+ const resource: Resource = { type: 'user', id: 'user123' };
46
+ auth.addResource(resource);
47
+
48
+ expect(auth.hasResource('user')).toBe(true);
49
+ expect(auth.getResource('user')).toEqual(resource);
50
+ });
51
+
52
+ it('should add a project resource', () => {
53
+ const auth = new Authorization();
54
+ const resource: Resource = { type: 'project', id: 'project456' };
55
+ auth.addResource(resource);
56
+
57
+ expect(auth.hasResource('project')).toBe(true);
58
+ expect(auth.getResource('project')).toEqual(resource);
59
+ });
60
+
61
+ it('should add a session resource', () => {
62
+ const auth = new Authorization();
63
+ const resource: Resource = { type: 'session', id: 'session789' };
64
+ auth.addResource(resource);
65
+
66
+ expect(auth.hasResource('session')).toBe(true);
67
+ expect(auth.getResource('session')).toEqual(resource);
68
+ });
69
+
70
+ it('should replace existing resource of the same type', () => {
71
+ const auth = new Authorization();
72
+ const resource1: Resource = { type: 'user', id: 'user123' };
73
+ const resource2: Resource = { type: 'user', id: 'user456' };
74
+
75
+ auth.addResource(resource1);
76
+ auth.addResource(resource2);
77
+
78
+ expect(auth.getResource('user')).toEqual(resource2);
79
+ expect(auth.getAllResources()).toHaveLength(1);
80
+ });
81
+
82
+ it('should allow multiple resources of different types', () => {
83
+ const auth = new Authorization();
84
+ const userResource: Resource = { type: 'user', id: 'user123' };
85
+ const projectResource: Resource = { type: 'project', id: 'project456' };
86
+
87
+ auth.addResource(userResource);
88
+ auth.addResource(projectResource);
89
+
90
+ expect(auth.getAllResources()).toHaveLength(2);
91
+ expect(auth.hasResource('user')).toBe(true);
92
+ expect(auth.hasResource('project')).toBe(true);
93
+ });
94
+ });
95
+
96
+ describe('hasResource', () => {
97
+ it('should return true when resource type exists', () => {
98
+ const auth = new Authorization();
99
+ auth.addResource({ type: 'user', id: 'user123' });
100
+
101
+ expect(auth.hasResource('user')).toBe(true);
102
+ });
103
+
104
+ it('should return false when resource type does not exist', () => {
105
+ const auth = new Authorization();
106
+ auth.addResource({ type: 'user', id: 'user123' });
107
+
108
+ expect(auth.hasResource('project')).toBe(false);
109
+ });
110
+
111
+ it('should return false for empty authorization', () => {
112
+ const auth = new Authorization();
113
+ expect(auth.hasResource('user')).toBe(false);
114
+ expect(auth.hasResource('project')).toBe(false);
115
+ });
116
+ });
117
+
118
+ describe('getResource', () => {
119
+ it('should return resource when it exists', () => {
120
+ const auth = new Authorization();
121
+ const resource: Resource = { type: 'user', id: 'user123' };
122
+ auth.addResource(resource);
123
+
124
+ expect(auth.getResource('user')).toEqual(resource);
125
+ });
126
+
127
+ it('should return undefined when resource does not exist', () => {
128
+ const auth = new Authorization();
129
+ auth.addResource({ type: 'user', id: 'user123' });
130
+
131
+ expect(auth.getResource('project')).toBeUndefined();
132
+ });
133
+
134
+ it('should return undefined for empty authorization', () => {
135
+ const auth = new Authorization();
136
+ expect(auth.getResource('user')).toBeUndefined();
137
+ });
138
+ });
139
+
140
+ describe('getAllResources', () => {
141
+ it('should return all resources', () => {
142
+ const auth = new Authorization();
143
+ const userResource: Resource = { type: 'user', id: 'user123' };
144
+ const projectResource: Resource = { type: 'project', id: 'project456' };
145
+
146
+ auth.addResource(userResource);
147
+ auth.addResource(projectResource);
148
+
149
+ const allResources = auth.getAllResources();
150
+ expect(allResources).toHaveLength(2);
151
+ expect(allResources).toContainEqual(userResource);
152
+ expect(allResources).toContainEqual(projectResource);
153
+ });
154
+
155
+ it('should return empty array when no resources', () => {
156
+ const auth = new Authorization();
157
+ expect(auth.getAllResources()).toEqual([]);
158
+ });
159
+ });
160
+
161
+ describe('addAuthSource', () => {
162
+ it('should add a cookie auth source', () => {
163
+ const auth = new Authorization();
164
+ const cookieAuth: CookieAuthSource = { type: 'cookie', sessionId: 'session123' };
165
+ auth.addAuthSource(cookieAuth);
166
+
167
+ const sources = auth.getAuthSources();
168
+ expect(sources).toHaveLength(1);
169
+ expect(sources[0]).toEqual(cookieAuth);
170
+ });
171
+
172
+ it('should add multiple auth sources', () => {
173
+ const auth = new Authorization();
174
+ const cookieAuth: CookieAuthSource = { type: 'cookie', sessionId: 'session123' };
175
+ const customAuth: AuthSource = { type: 'api-key' };
176
+
177
+ auth.addAuthSource(cookieAuth);
178
+ auth.addAuthSource(customAuth);
179
+
180
+ expect(auth.getAuthSources()).toHaveLength(2);
181
+ });
182
+ });
183
+
184
+ describe('getAuthSources', () => {
185
+ it('should return all auth sources', () => {
186
+ const auth = new Authorization();
187
+ const cookieAuth: CookieAuthSource = { type: 'cookie', sessionId: 'session123' };
188
+ auth.addAuthSource(cookieAuth);
189
+
190
+ const sources = auth.getAuthSources();
191
+ expect(sources).toHaveLength(1);
192
+ expect(sources[0]).toEqual(cookieAuth);
193
+ });
194
+
195
+ it('should return a copy of auth sources array', () => {
196
+ const auth = new Authorization();
197
+ const cookieAuth: CookieAuthSource = { type: 'cookie', sessionId: 'session123' };
198
+ auth.addAuthSource(cookieAuth);
199
+
200
+ const sources1 = auth.getAuthSources();
201
+ const sources2 = auth.getAuthSources();
202
+
203
+ expect(sources1).not.toBe(sources2);
204
+ expect(sources1).toEqual(sources2);
205
+ });
206
+
207
+ it('should return empty array when no auth sources', () => {
208
+ const auth = new Authorization();
209
+ expect(auth.getAuthSources()).toEqual([]);
210
+ });
211
+ });
212
+
213
+ describe('getCookieAuthSource', () => {
214
+ it('should return cookie auth source when it exists', () => {
215
+ const auth = new Authorization();
216
+ const cookieAuth: CookieAuthSource = { type: 'cookie', sessionId: 'session123' };
217
+ auth.addAuthSource(cookieAuth);
218
+
219
+ const result = auth.getCookieAuthSource();
220
+ expect(result).toEqual(cookieAuth);
221
+ });
222
+
223
+ it('should return undefined when no cookie auth source exists', () => {
224
+ const auth = new Authorization();
225
+ const customAuth: AuthSource = { type: 'api-key' };
226
+ auth.addAuthSource(customAuth);
227
+
228
+ expect(auth.getCookieAuthSource()).toBeUndefined();
229
+ });
230
+
231
+ it('should return first cookie auth source when multiple exist', () => {
232
+ const auth = new Authorization();
233
+ const cookieAuth1: CookieAuthSource = { type: 'cookie', sessionId: 'session123' };
234
+ const cookieAuth2: CookieAuthSource = { type: 'cookie', sessionId: 'session456' };
235
+ auth.addAuthSource(cookieAuth1);
236
+ auth.addAuthSource(cookieAuth2);
237
+
238
+ const result = auth.getCookieAuthSource();
239
+ expect(result).toEqual(cookieAuth1);
240
+ });
241
+
242
+ it('should return undefined for empty auth sources', () => {
243
+ const auth = new Authorization();
244
+ expect(auth.getCookieAuthSource()).toBeUndefined();
245
+ });
246
+ });
247
+
248
+ describe('setUserPermissions', () => {
249
+ it('should set user permissions', () => {
250
+ const auth = new Authorization();
251
+ const permissions = { userId: 'user123', permissions: ['read', 'write'] };
252
+ auth.setUserPermissions(permissions);
253
+
254
+ expect(auth.getUserPermissions()).toEqual(permissions);
255
+ });
256
+
257
+ it('should replace existing user permissions', () => {
258
+ const auth = new Authorization();
259
+ const permissions1 = { userId: 'user123', permissions: ['read'] };
260
+ const permissions2 = { userId: 'user123', permissions: ['read', 'write', 'delete'] };
261
+
262
+ auth.setUserPermissions(permissions1);
263
+ auth.setUserPermissions(permissions2);
264
+
265
+ expect(auth.getUserPermissions()).toEqual(permissions2);
266
+ });
267
+ });
268
+
269
+ describe('getUserPermissions', () => {
270
+ it('should return user permissions when set', () => {
271
+ const auth = new Authorization();
272
+ const permissions = { userId: 'user123', permissions: ['read', 'write'] };
273
+ auth.setUserPermissions(permissions);
274
+
275
+ expect(auth.getUserPermissions()).toEqual(permissions);
276
+ });
277
+
278
+ it('should return undefined when not set', () => {
279
+ const auth = new Authorization();
280
+ expect(auth.getUserPermissions()).toBeUndefined();
281
+ });
282
+ });
283
+
284
+ describe('hasPermission', () => {
285
+ it('should return true when user has the permission', () => {
286
+ const auth = new Authorization();
287
+ auth.setUserPermissions({ userId: 'user123', permissions: ['read', 'write'] });
288
+
289
+ expect(auth.hasPermission('read')).toBe(true);
290
+ expect(auth.hasPermission('write')).toBe(true);
291
+ });
292
+
293
+ it('should return false when user does not have the permission', () => {
294
+ const auth = new Authorization();
295
+ auth.setUserPermissions({ userId: 'user123', permissions: ['read'] });
296
+
297
+ expect(auth.hasPermission('write')).toBe(false);
298
+ expect(auth.hasPermission('delete')).toBe(false);
299
+ });
300
+
301
+ it('should return false when no user permissions are set', () => {
302
+ const auth = new Authorization();
303
+ expect(auth.hasPermission('read')).toBe(false);
304
+ });
305
+
306
+ it('should return false for empty permissions array', () => {
307
+ const auth = new Authorization();
308
+ auth.setUserPermissions({ userId: 'user123', permissions: [] });
309
+
310
+ expect(auth.hasPermission('read')).toBe(false);
311
+ });
312
+
313
+ it('should handle permission checks case-sensitively', () => {
314
+ const auth = new Authorization();
315
+ auth.setUserPermissions({ userId: 'user123', permissions: ['Read'] });
316
+
317
+ expect(auth.hasPermission('Read')).toBe(true);
318
+ expect(auth.hasPermission('read')).toBe(false);
319
+ });
320
+ });
321
+
322
+ describe('integration tests', () => {
323
+ it('should handle complex authorization scenarios', () => {
324
+ const auth = new Authorization();
325
+
326
+ // Add resources
327
+ auth.addResource({ type: 'user', id: 'user123' });
328
+ auth.addResource({ type: 'project', id: 'project456' });
329
+
330
+ // Add auth sources
331
+ const cookieAuth: CookieAuthSource = { type: 'cookie', sessionId: 'session789' };
332
+ auth.addAuthSource(cookieAuth);
333
+
334
+ // Set permissions
335
+ auth.setUserPermissions({
336
+ userId: 'user123',
337
+ permissions: ['read', 'write', 'admin'],
338
+ });
339
+
340
+ // Verify everything
341
+ expect(auth.hasResource('user')).toBe(true);
342
+ expect(auth.hasResource('project')).toBe(true);
343
+ expect(auth.getCookieAuthSource()).toEqual(cookieAuth);
344
+ expect(auth.hasPermission('read')).toBe(true);
345
+ expect(auth.hasPermission('write')).toBe(true);
346
+ expect(auth.hasPermission('admin')).toBe(true);
347
+ expect(auth.hasPermission('delete')).toBe(false);
348
+ });
349
+ });
350
+ });