@furystack/rest-service 12.2.0 → 12.3.0

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 (74) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/esm/api-manager.d.ts +1 -1
  3. package/esm/api-manager.d.ts.map +1 -1
  4. package/esm/api-manager.js +3 -2
  5. package/esm/api-manager.js.map +1 -1
  6. package/esm/endpoint-generators/create-get-openapi-document-action.d.ts +29 -0
  7. package/esm/endpoint-generators/create-get-openapi-document-action.d.ts.map +1 -0
  8. package/esm/endpoint-generators/create-get-openapi-document-action.js +61 -0
  9. package/esm/endpoint-generators/create-get-openapi-document-action.js.map +1 -0
  10. package/esm/endpoint-generators/index.d.ts +2 -0
  11. package/esm/endpoint-generators/index.d.ts.map +1 -1
  12. package/esm/endpoint-generators/index.js +2 -0
  13. package/esm/endpoint-generators/index.js.map +1 -1
  14. package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts +23 -0
  15. package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts.map +1 -0
  16. package/esm/endpoint-generators/with-schema-and-openapi-action.js +2 -0
  17. package/esm/endpoint-generators/with-schema-and-openapi-action.js.map +1 -0
  18. package/esm/openapi/auth-provider-to-security-scheme.d.ts +14 -0
  19. package/esm/openapi/auth-provider-to-security-scheme.d.ts.map +1 -0
  20. package/esm/openapi/auth-provider-to-security-scheme.js +35 -0
  21. package/esm/openapi/auth-provider-to-security-scheme.js.map +1 -0
  22. package/esm/openapi/auth-provider-to-security-scheme.spec.d.ts +2 -0
  23. package/esm/openapi/auth-provider-to-security-scheme.spec.d.ts.map +1 -0
  24. package/esm/openapi/auth-provider-to-security-scheme.spec.js +42 -0
  25. package/esm/openapi/auth-provider-to-security-scheme.spec.js.map +1 -0
  26. package/esm/openapi/generate-openapi-document.d.ts +21 -0
  27. package/esm/openapi/generate-openapi-document.d.ts.map +1 -0
  28. package/esm/openapi/generate-openapi-document.js +144 -0
  29. package/esm/openapi/generate-openapi-document.js.map +1 -0
  30. package/esm/openapi/generate-openapi-document.spec.d.ts +2 -0
  31. package/esm/openapi/generate-openapi-document.spec.d.ts.map +1 -0
  32. package/esm/openapi/generate-openapi-document.spec.js +643 -0
  33. package/esm/openapi/generate-openapi-document.spec.js.map +1 -0
  34. package/esm/openapi/openapi-round-trip.advanced-api.json +363 -0
  35. package/esm/openapi/openapi-round-trip.crud-api.json +115 -0
  36. package/esm/openapi/openapi-round-trip.example-api.json +71 -0
  37. package/esm/openapi/openapi-round-trip.spec.d.ts +2 -0
  38. package/esm/openapi/openapi-round-trip.spec.d.ts.map +1 -0
  39. package/esm/openapi/openapi-round-trip.spec.js +525 -0
  40. package/esm/openapi/openapi-round-trip.spec.js.map +1 -0
  41. package/esm/swagger/generate-swagger-json.spec.js +1 -1
  42. package/esm/swagger/generate-swagger-json.spec.js.map +1 -1
  43. package/esm/validate.integration.spec.js +153 -32
  44. package/esm/validate.integration.spec.js.map +1 -1
  45. package/package.json +9 -9
  46. package/src/api-manager.ts +7 -3
  47. package/src/endpoint-generators/create-get-openapi-document-action.ts +96 -0
  48. package/src/endpoint-generators/index.ts +2 -0
  49. package/src/endpoint-generators/with-schema-and-openapi-action.ts +14 -0
  50. package/src/openapi/auth-provider-to-security-scheme.spec.ts +50 -0
  51. package/src/openapi/auth-provider-to-security-scheme.ts +41 -0
  52. package/src/openapi/generate-openapi-document.spec.ts +733 -0
  53. package/src/openapi/generate-openapi-document.ts +198 -0
  54. package/src/openapi/openapi-round-trip.advanced-api.json +363 -0
  55. package/src/openapi/openapi-round-trip.crud-api.json +115 -0
  56. package/src/openapi/openapi-round-trip.example-api.json +71 -0
  57. package/src/openapi/openapi-round-trip.spec.ts +621 -0
  58. package/src/swagger/generate-swagger-json.spec.ts +1 -1
  59. package/src/validate.integration.spec.ts +184 -33
  60. package/esm/endpoint-generators/create-get-swagger-json-action.d.ts +0 -14
  61. package/esm/endpoint-generators/create-get-swagger-json-action.d.ts.map +0 -1
  62. package/esm/endpoint-generators/create-get-swagger-json-action.js +0 -18
  63. package/esm/endpoint-generators/create-get-swagger-json-action.js.map +0 -1
  64. package/esm/endpoint-generators/with-schema-and-swagger-action.d.ts +0 -21
  65. package/esm/endpoint-generators/with-schema-and-swagger-action.d.ts.map +0 -1
  66. package/esm/endpoint-generators/with-schema-and-swagger-action.js +0 -2
  67. package/esm/endpoint-generators/with-schema-and-swagger-action.js.map +0 -1
  68. package/esm/swagger/generate-swagger-json.d.ts +0 -14
  69. package/esm/swagger/generate-swagger-json.d.ts.map +0 -1
  70. package/esm/swagger/generate-swagger-json.js +0 -108
  71. package/esm/swagger/generate-swagger-json.js.map +0 -1
  72. package/src/endpoint-generators/create-get-swagger-json-action.ts +0 -26
  73. package/src/endpoint-generators/with-schema-and-swagger-action.ts +0 -11
  74. package/src/swagger/generate-swagger-json.ts +0 -132
@@ -1,6 +1,6 @@
1
1
  import type { ApiEndpointSchema, ParameterObject, ReferenceObject, ResponseObject } from '@furystack/rest'
2
2
  import { describe, expect, it } from 'vitest'
3
- import { generateSwaggerJsonFromApiSchema } from './generate-swagger-json.js'
3
+ import { generateSwaggerJsonFromApiSchema } from '../openapi/generate-openapi-document.js'
4
4
 
5
5
  describe('generateSwaggerJsonFromApiSchema', () => {
6
6
  it('Should generate a basic Swagger document with correct OpenAPI structure', () => {
@@ -3,7 +3,7 @@ import { getStoreManager, InMemoryStore, User } from '@furystack/core'
3
3
  import { getPort } from '@furystack/core/port-generator'
4
4
  import { Injector } from '@furystack/inject'
5
5
  import { getRepository } from '@furystack/repository'
6
- import type { SwaggerDocument, WithSchemaAction } from '@furystack/rest'
6
+ import type { OpenApiDocument, WithSchemaAction } from '@furystack/rest'
7
7
  import { createClient, ResponseError } from '@furystack/rest-client-fetch'
8
8
  import { usingAsync } from '@furystack/utils'
9
9
  import type Ajv from 'ajv'
@@ -14,7 +14,7 @@ import { createGetEntityEndpoint } from './endpoint-generators/create-get-entity
14
14
  import { createPatchEndpoint } from './endpoint-generators/create-patch-endpoint.js'
15
15
  import { createPostEndpoint } from './endpoint-generators/create-post-endpoint.js'
16
16
  import { MockClass } from './endpoint-generators/utils.js'
17
- import { useRestService } from './helpers.js'
17
+ import { useHttpAuthentication, useRestService } from './helpers.js'
18
18
  import { DefaultSession } from './models/default-session.js'
19
19
  import { JsonResult } from './request-action-implementation.js'
20
20
  import type { ValidationApi } from './validate.integration.schema.js'
@@ -109,24 +109,23 @@ const createValidateApi = async (options = { enableGetSchema: false }) => {
109
109
  }
110
110
 
111
111
  describe('Validation integration tests', () => {
112
- describe('swagger.json schema definition', () => {
113
- it('Should include name, description and version in the generated swagger.json', async () => {
112
+ describe('openapi.json schema definition', () => {
113
+ it('Should include name, description and version in the generated openapi.json', async () => {
114
114
  await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
115
115
  const result = await (client as ReturnType<typeof createClient<any>>)({
116
116
  method: 'GET',
117
- action: '/swagger.json',
117
+ action: '/openapi.json',
118
118
  })
119
119
 
120
120
  expect(result.response.status).toBe(200)
121
121
  expect(result.result).toBeDefined()
122
122
 
123
- // Verify swagger document structure
124
- const swaggerJson = result.result as SwaggerDocument
125
- expect(swaggerJson.openapi).toBe('3.1.0')
126
- expect(swaggerJson.info).toBeDefined()
127
- expect(swaggerJson.info?.title).toBe(name)
128
- expect(swaggerJson.info?.description).toBe(description)
129
- expect(swaggerJson.info?.version).toBe(version)
123
+ const openApiDoc = result.result as OpenApiDocument
124
+ expect(openApiDoc.openapi).toBe('3.1.0')
125
+ expect(openApiDoc.info).toBeDefined()
126
+ expect(openApiDoc.info?.title).toBe(name)
127
+ expect(openApiDoc.info?.description).toBe(description)
128
+ expect(openApiDoc.info?.version).toBe(version)
130
129
  })
131
130
  })
132
131
 
@@ -135,7 +134,7 @@ describe('Validation integration tests', () => {
135
134
  try {
136
135
  await (client as ReturnType<typeof createClient<any>>)({
137
136
  method: 'GET',
138
- action: '/swagger.json',
137
+ action: '/openapi.json',
139
138
  })
140
139
  expect.fail('Expected response error but got success')
141
140
  } catch (error) {
@@ -145,44 +144,89 @@ describe('Validation integration tests', () => {
145
144
  })
146
145
  })
147
146
 
148
- it('Should return a generated swagger.json when enabled', async () => {
147
+ it('Should return a generated openapi.json when enabled', async () => {
149
148
  await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
150
149
  const result = await (client as ReturnType<typeof createClient<any>>)({
151
150
  method: 'GET',
152
- action: '/swagger.json',
151
+ action: '/openapi.json',
153
152
  })
154
153
 
155
154
  expect(result.response.status).toBe(200)
156
155
  expect(result.result).toBeDefined()
157
156
 
158
- // Verify swagger document structure
159
- const swaggerJson = result.result as SwaggerDocument
160
- expect(swaggerJson.openapi).toBe('3.1.0')
161
- expect(swaggerJson.info).toBeDefined()
162
- expect(swaggerJson.info?.title).toBe(name)
163
- expect(swaggerJson.info?.description).toBe(description)
164
- expect(swaggerJson.info?.version).toBe(version)
165
- expect(swaggerJson.paths).toBeDefined()
157
+ const openApiDoc = result.result as OpenApiDocument
158
+ expect(openApiDoc.openapi).toBe('3.1.0')
159
+ expect(openApiDoc.info).toBeDefined()
160
+ expect(openApiDoc.info?.title).toBe(name)
161
+ expect(openApiDoc.info?.description).toBe(description)
162
+ expect(openApiDoc.info?.version).toBe(version)
163
+ expect(openApiDoc.paths).toBeDefined()
166
164
 
167
165
  // Verify our API endpoints are included
168
- expect(swaggerJson.paths?.['/validate-query']).toBeDefined()
169
- expect(swaggerJson.paths?.['/validate-url/{id}']).toBeDefined()
170
- expect(swaggerJson.paths?.['/validate-headers']).toBeDefined()
171
- expect(swaggerJson.paths?.['/validate-body']).toBeDefined()
166
+ expect(openApiDoc.paths?.['/validate-query']).toBeDefined()
167
+ expect(openApiDoc.paths?.['/validate-url/{id}']).toBeDefined()
168
+ expect(openApiDoc.paths?.['/validate-headers']).toBeDefined()
169
+ expect(openApiDoc.paths?.['/validate-body']).toBeDefined()
172
170
 
173
171
  // Verify components section
174
- expect(swaggerJson.components).toBeDefined()
175
- expect(swaggerJson.components?.schemas).toBeDefined()
176
- expect(swaggerJson.components?.schemas?.ValidateQuery).toBeDefined()
177
- expect(swaggerJson.components?.schemas?.ValidateUrl).toBeDefined()
178
- expect(swaggerJson.components?.schemas?.ValidateHeaders).toBeDefined()
179
- expect(swaggerJson.components?.schemas?.ValidateBody).toBeDefined()
172
+ expect(openApiDoc.components).toBeDefined()
173
+ expect(openApiDoc.components?.schemas).toBeDefined()
174
+ expect(openApiDoc.components?.schemas?.ValidateQuery).toBeDefined()
175
+ expect(openApiDoc.components?.schemas?.ValidateUrl).toBeDefined()
176
+ expect(openApiDoc.components?.schemas?.ValidateHeaders).toBeDefined()
177
+ expect(openApiDoc.components?.schemas?.ValidateBody).toBeDefined()
178
+ })
179
+ })
180
+ })
181
+
182
+ describe('swagger.json backward compatibility', () => {
183
+ it('Should serve the same OpenAPI document at /swagger.json when enabled', async () => {
184
+ await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
185
+ const result = await (client as ReturnType<typeof createClient<any>>)({
186
+ method: 'GET',
187
+ action: '/swagger.json',
188
+ })
189
+
190
+ expect(result.response.status).toBe(200)
191
+ expect(result.result).toBeDefined()
192
+
193
+ const openApiDoc = result.result as OpenApiDocument
194
+ expect(openApiDoc.openapi).toBe('3.1.0')
195
+ expect(openApiDoc.info?.title).toBe(name)
196
+ })
197
+ })
198
+
199
+ it('Should include Deprecation header on /swagger.json', async () => {
200
+ await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
201
+ const result = await (client as ReturnType<typeof createClient<any>>)({
202
+ method: 'GET',
203
+ action: '/swagger.json',
204
+ })
205
+
206
+ expect(result.response.headers.get('deprecation')).toBe('true')
207
+ expect(result.response.headers.get('link')).toContain('/openapi.json')
208
+ })
209
+ })
210
+
211
+ it('Should return a 404 for /swagger.json when not enabled', async () => {
212
+ await usingAsync(await createValidateApi({ enableGetSchema: false }), async ({ client }) => {
213
+ try {
214
+ await (client as ReturnType<typeof createClient<any>>)({
215
+ method: 'GET',
216
+ action: '/swagger.json',
217
+ })
218
+ expect.fail('Expected response error but got success')
219
+ } catch (error) {
220
+ expect(error).toBeInstanceOf(ResponseError)
221
+ expect((error as ResponseError).response.status).toBe(404)
222
+ }
180
223
  })
181
224
  })
182
225
  })
183
226
 
184
227
  describe('Validation metadata', () => {
185
228
  it('Should return 404 when not enabled', async () => {
229
+ expect.assertions(2)
186
230
  await usingAsync(await createValidateApi({ enableGetSchema: false }), async ({ client }) => {
187
231
  try {
188
232
  await (client as ReturnType<typeof createClient<WithSchemaAction<ValidationApi>>>)({
@@ -192,6 +236,7 @@ describe('Validation integration tests', () => {
192
236
  accept: 'application/schema+json',
193
237
  },
194
238
  })
239
+ expect.fail('Expected response error but got success')
195
240
  } catch (error) {
196
241
  expect(error).toBeInstanceOf(ResponseError)
197
242
  expect((error as ResponseError).response.status).toBe(404)
@@ -444,4 +489,110 @@ describe('Validation integration tests', () => {
444
489
  })
445
490
  })
446
491
  })
492
+
493
+ describe('OpenAPI security schemes from authentication providers', () => {
494
+ const createApiWithAuth = async () => {
495
+ const injector = new Injector()
496
+ const port = getPort()
497
+
498
+ getStoreManager(injector).addStore(new InMemoryStore({ model: User, primaryKey: 'username' }))
499
+ getStoreManager(injector).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }))
500
+ getStoreManager(injector).addStore(new InMemoryStore({ model: MockClass, primaryKey: 'id' }))
501
+ getRepository(injector).createDataSet(MockClass, 'id')
502
+ getRepository(injector).createDataSet(User, 'username')
503
+ getRepository(injector).createDataSet(DefaultSession, 'sessionId')
504
+
505
+ useHttpAuthentication(injector)
506
+
507
+ await useRestService<ValidationApi>({
508
+ injector,
509
+ enableGetSchema: true,
510
+ name,
511
+ description,
512
+ version,
513
+ api: {
514
+ GET: {
515
+ '/validate-query': Validate({ schema, schemaName: 'ValidateQuery' })(async ({ getQuery }) =>
516
+ JsonResult({ ...getQuery() }),
517
+ ),
518
+ '/validate-url/:id': Validate({ schema, schemaName: 'ValidateUrl' })(async ({ getUrlParams }) =>
519
+ JsonResult({ ...getUrlParams() }),
520
+ ),
521
+ '/validate-headers': Validate({ schema, schemaName: 'ValidateHeaders' })(async ({ headers }) =>
522
+ JsonResult({ ...headers }),
523
+ ),
524
+ '/mock': Validate({ schema, schemaName: 'GetMockCollectionEndpoint' })(
525
+ createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
526
+ ),
527
+ '/mock/:id': Validate({ schema, schemaName: 'GetMockEntityEndpoint' })(
528
+ createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
529
+ ),
530
+ },
531
+ POST: {
532
+ '/validate-body': Validate({ schema, schemaName: 'ValidateBody' })(async ({ getBody }) => {
533
+ const body = await getBody()
534
+ return JsonResult({ ...body })
535
+ }),
536
+ '/mock': Validate({ schema, schemaName: 'PostMockEndpoint' })(
537
+ createPostEndpoint({ model: MockClass, primaryKey: 'id' }),
538
+ ),
539
+ },
540
+ PATCH: {
541
+ '/mock/:id': Validate({ schema, schemaName: 'PatchMockEndpoint' })(
542
+ createPatchEndpoint({ model: MockClass, primaryKey: 'id' }),
543
+ ),
544
+ },
545
+ DELETE: {
546
+ '/mock/:id': Validate({ schema, schemaName: 'DeleteMockEndpoint' })(
547
+ createDeleteEndpoint({ model: MockClass, primaryKey: 'id' }),
548
+ ),
549
+ },
550
+ },
551
+ port,
552
+ root: '/api',
553
+ })
554
+ const client = createClient<ValidationApi>({ endpointUrl: `http://127.0.0.1:${port}/api` })
555
+
556
+ return {
557
+ [Symbol.asyncDispose]: injector[Symbol.asyncDispose].bind(injector),
558
+ injector,
559
+ client,
560
+ }
561
+ }
562
+
563
+ it('Should include detected security schemes in the OpenAPI document', async () => {
564
+ await usingAsync(await createApiWithAuth(), async ({ client }) => {
565
+ const result = await (client as ReturnType<typeof createClient<any>>)({
566
+ method: 'GET',
567
+ action: '/openapi.json',
568
+ })
569
+
570
+ const openApiDoc = result.result as OpenApiDocument
571
+ expect(openApiDoc.components?.securitySchemes).toBeDefined()
572
+ expect(openApiDoc.components?.securitySchemes?.basicAuth).toEqual({ type: 'http', scheme: 'basic' })
573
+ expect(openApiDoc.components?.securitySchemes?.cookieAuth).toEqual({
574
+ type: 'apiKey',
575
+ in: 'cookie',
576
+ name: 'session',
577
+ })
578
+ })
579
+ })
580
+
581
+ it('Should include detected security schemes in the deprecated /swagger.json endpoint', async () => {
582
+ await usingAsync(await createApiWithAuth(), async ({ client }) => {
583
+ const result = await (client as ReturnType<typeof createClient<any>>)({
584
+ method: 'GET',
585
+ action: '/swagger.json',
586
+ })
587
+
588
+ const openApiDoc = result.result as OpenApiDocument
589
+ expect(openApiDoc.components?.securitySchemes?.basicAuth).toEqual({ type: 'http', scheme: 'basic' })
590
+ expect(openApiDoc.components?.securitySchemes?.cookieAuth).toEqual({
591
+ type: 'apiKey',
592
+ in: 'cookie',
593
+ name: 'session',
594
+ })
595
+ })
596
+ })
597
+ })
447
598
  })
@@ -1,14 +0,0 @@
1
- import { type ApiEndpointSchema, type SwaggerDocument } from '@furystack/rest';
2
- import type { RestApiImplementation } from '../api-manager.js';
3
- import { type RequestAction } from '../request-action-implementation.js';
4
- export type GetSchemaResult = ApiEndpointSchema['endpoints'];
5
- /**
6
- * Generates a RequestAction that retrieves the Swagger JSON schema from a FuryStack API implementation.
7
- *
8
- * @param api - The API implementation from which to extract the schema.
9
- * @returns A RequestAction that handles the GET request for the schema.
10
- */
11
- export declare const CreateGetSwaggerJsonAction: <T extends RestApiImplementation<any>>(api: T, name?: string, description?: string, version?: string) => RequestAction<{
12
- result: GetSchemaResult | SwaggerDocument;
13
- }>;
14
- //# sourceMappingURL=create-get-swagger-json-action.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create-get-swagger-json-action.d.ts","sourceRoot":"","sources":["../../src/endpoint-generators/create-get-swagger-json-action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAC9E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAE9D,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAGpF,MAAM,MAAM,eAAe,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAA;AAE5D;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,GAAI,CAAC,SAAS,qBAAqB,CAAC,GAAG,CAAC,EAC7E,KAAK,CAAC,EACN,aAAsB,EACtB,oBAAqE,EACrE,gBAAiB,KAChB,aAAa,CAAC;IAAE,MAAM,EAAE,eAAe,GAAG,eAAe,CAAA;CAAE,CAM7D,CAAA"}
@@ -1,18 +0,0 @@
1
- import {} from '@furystack/rest';
2
- import { getSchemaFromApi } from '../get-schema-from-api.js';
3
- import { JsonResult } from '../request-action-implementation.js';
4
- import { generateSwaggerJsonFromApiSchema } from '../swagger/generate-swagger-json.js';
5
- /**
6
- * Generates a RequestAction that retrieves the Swagger JSON schema from a FuryStack API implementation.
7
- *
8
- * @param api - The API implementation from which to extract the schema.
9
- * @returns A RequestAction that handles the GET request for the schema.
10
- */
11
- export const CreateGetSwaggerJsonAction = (api, name = 'FuryStack API', description = 'API documentation generated from FuryStack API schema', version = '1.0.0') => {
12
- const { endpoints } = getSchemaFromApi({ api, name, description, version });
13
- const swaggerJson = generateSwaggerJsonFromApiSchema({ api: endpoints, title: name, description, version });
14
- return async () => {
15
- return JsonResult(swaggerJson, 200);
16
- };
17
- };
18
- //# sourceMappingURL=create-get-swagger-json-action.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create-get-swagger-json-action.js","sourceRoot":"","sources":["../../src/endpoint-generators/create-get-swagger-json-action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgD,MAAM,iBAAiB,CAAA;AAE9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAC5D,OAAO,EAAE,UAAU,EAAsB,MAAM,qCAAqC,CAAA;AACpF,OAAO,EAAE,gCAAgC,EAAE,MAAM,qCAAqC,CAAA;AAItF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,GAAM,EACN,IAAI,GAAG,eAAe,EACtB,WAAW,GAAG,uDAAuD,EACrE,OAAO,GAAG,OAAO,EAC6C,EAAE;IAChE,MAAM,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAA;IAC3E,MAAM,WAAW,GAAG,gCAAgC,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAA;IAC3G,OAAO,KAAK,IAAI,EAAE;QAChB,OAAO,UAAU,CAAC,WAAW,EAAE,GAAG,CAAC,CAAA;IACrC,CAAC,CAAA;AACH,CAAC,CAAA"}
@@ -1,21 +0,0 @@
1
- import type { RestApi, SwaggerDocument } from '@furystack/rest';
2
- /**
3
- * Extends a RestApi with endpoints for both schema.json and swagger.json
4
- */
5
- export type WithSchemaAndSwaggerAction<T extends RestApi> = T & {
6
- GET: {
7
- '/schema': {
8
- result: Record<string, any>;
9
- headers: {
10
- accept: 'application/schema+json';
11
- };
12
- };
13
- '/swagger.json': {
14
- result: SwaggerDocument;
15
- headers: {
16
- accept: 'application/swagger+json';
17
- };
18
- };
19
- };
20
- };
21
- //# sourceMappingURL=with-schema-and-swagger-action.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"with-schema-and-swagger-action.d.ts","sourceRoot":"","sources":["../../src/endpoint-generators/with-schema-and-swagger-action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAE/D;;GAEG;AACH,MAAM,MAAM,0BAA0B,CAAC,CAAC,SAAS,OAAO,IAAI,CAAC,GAAG;IAC9D,GAAG,EAAE;QACH,SAAS,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YAAC,OAAO,EAAE;gBAAE,MAAM,EAAE,yBAAyB,CAAA;aAAE,CAAA;SAAE,CAAA;QAC1F,eAAe,EAAE;YAAE,MAAM,EAAE,eAAe,CAAC;YAAC,OAAO,EAAE;gBAAE,MAAM,EAAE,0BAA0B,CAAA;aAAE,CAAA;SAAE,CAAA;KAC9F,CAAA;CACF,CAAA"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=with-schema-and-swagger-action.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"with-schema-and-swagger-action.js","sourceRoot":"","sources":["../../src/endpoint-generators/with-schema-and-swagger-action.ts"],"names":[],"mappings":""}
@@ -1,14 +0,0 @@
1
- import type { ApiEndpointSchema, SwaggerDocument } from '@furystack/rest';
2
- /**
3
- * Converts a FuryStack API schema to an OpenAPI 3.1 compatible document
4
- *
5
- * @param schema - The FuryStack API schema to convert
6
- * @returns A SwaggerDocument in OpenAPI 3.1 format
7
- */
8
- export declare const generateSwaggerJsonFromApiSchema: ({ api, title, description, version, }: {
9
- api: ApiEndpointSchema["endpoints"];
10
- title?: string;
11
- description?: string;
12
- version?: string;
13
- }) => SwaggerDocument;
14
- //# sourceMappingURL=generate-swagger-json.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"generate-swagger-json.d.ts","sourceRoot":"","sources":["../../src/swagger/generate-swagger-json.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EAIjB,eAAe,EAChB,MAAM,iBAAiB,CAAA;AAExB;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAAI,uCAK9C;IACD,GAAG,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAA;IACnC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,KAAG,eA0GH,CAAA"}
@@ -1,108 +0,0 @@
1
- /**
2
- * Converts a FuryStack API schema to an OpenAPI 3.1 compatible document
3
- *
4
- * @param schema - The FuryStack API schema to convert
5
- * @returns A SwaggerDocument in OpenAPI 3.1 format
6
- */
7
- export const generateSwaggerJsonFromApiSchema = ({ api, title = 'FuryStack API', description = 'API documentation generated from FuryStack API schema', version = '1.0.0', }) => {
8
- const swaggerJson = {
9
- openapi: '3.1.0',
10
- info: {
11
- title,
12
- version,
13
- description,
14
- },
15
- jsonSchemaDialect: 'https://spec.openapis.org/oas/3.1/dialect/base',
16
- servers: [{ url: '/' }],
17
- tags: [],
18
- paths: {},
19
- components: {
20
- schemas: {},
21
- securitySchemes: {
22
- cookieAuth: {
23
- type: 'apiKey',
24
- in: 'cookie',
25
- name: 'session',
26
- },
27
- },
28
- },
29
- };
30
- for (const [methodKey, paths] of Object.entries(api)) {
31
- for (const [path, definition] of Object.entries(paths)) {
32
- // Normalize path to OpenAPI format (convert :param to {param})
33
- const normalizedPath = path.replace(/:([^/]+)/g, '{$1}');
34
- if (!swaggerJson.paths[normalizedPath]) {
35
- swaggerJson.paths[normalizedPath] = {};
36
- }
37
- // Extract path parameters
38
- const pathParams = Array.from(path.matchAll(/:([^/]+)/g), (m) => m[1]);
39
- const parameters = pathParams.map((param) => ({
40
- name: param,
41
- in: 'path',
42
- required: true,
43
- description: `Path parameter: ${param}`,
44
- schema: { type: 'string' },
45
- }));
46
- // Build operation
47
- const method = methodKey.toLowerCase();
48
- const operation = {
49
- summary: `${methodKey} ${path}`,
50
- description: `Endpoint for ${path}`,
51
- operationId: `${method}${path.replace(/\//g, '_').replace(/:/g, '').replace(/-/g, '_')}`,
52
- security: definition.isAuthenticated ? [{ cookieAuth: [] }] : [],
53
- parameters,
54
- responses: {
55
- '200': {
56
- description: 'Successful operation',
57
- content: {
58
- 'application/json': {
59
- schema: definition.schemaName
60
- ? { $ref: `#/components/schemas/${definition.schemaName}` }
61
- : { type: 'object' },
62
- },
63
- },
64
- },
65
- '401': { description: 'Unauthorized' },
66
- '500': { description: 'Internal server error' },
67
- },
68
- };
69
- // Add schema to components if not already there
70
- if (definition.schema && definition.schemaName) {
71
- swaggerJson.components.schemas[definition.schemaName] = definition.schema;
72
- }
73
- // Assign the operation to the correct HTTP method property of PathItem
74
- const pathItem = swaggerJson.paths[normalizedPath];
75
- switch (method) {
76
- case 'get':
77
- pathItem.get = operation;
78
- break;
79
- case 'put':
80
- pathItem.put = operation;
81
- break;
82
- case 'post':
83
- pathItem.post = operation;
84
- break;
85
- case 'delete':
86
- pathItem.delete = operation;
87
- break;
88
- case 'options':
89
- pathItem.options = operation;
90
- break;
91
- case 'head':
92
- pathItem.head = operation;
93
- break;
94
- case 'patch':
95
- pathItem.patch = operation;
96
- break;
97
- case 'trace':
98
- pathItem.trace = operation;
99
- break;
100
- default:
101
- // Ignore unknown methods
102
- break;
103
- }
104
- }
105
- }
106
- return swaggerJson;
107
- };
108
- //# sourceMappingURL=generate-swagger-json.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"generate-swagger-json.js","sourceRoot":"","sources":["../../src/swagger/generate-swagger-json.ts"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,EAC/C,GAAG,EACH,KAAK,GAAG,eAAe,EACvB,WAAW,GAAG,uDAAuD,EACrE,OAAO,GAAG,OAAO,GAMlB,EAAmB,EAAE;IACpB,MAAM,WAAW,GAAoB;QACnC,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE;YACJ,KAAK;YACL,OAAO;YACP,WAAW;SACZ;QACD,iBAAiB,EAAE,gDAAgD;QACnE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QACvB,IAAI,EAAE,EAAE;QACR,KAAK,EAAE,EAAE;QACT,UAAU,EAAE;YACV,OAAO,EAAE,EAAE;YACX,eAAe,EAAE;gBACf,UAAU,EAAE;oBACV,IAAI,EAAE,QAAQ;oBACd,EAAE,EAAE,QAAQ;oBACZ,IAAI,EAAE,SAAS;iBAChB;aACF;SACF;KACF,CAAA;IAED,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAA2D,EAAE,CAAC;QAC/G,KAAK,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACvD,+DAA+D;YAC/D,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;YACxD,IAAI,CAAC,WAAW,CAAC,KAAM,CAAC,cAAc,CAAC,EAAE,CAAC;gBACxC,WAAW,CAAC,KAAM,CAAC,cAAc,CAAC,GAAG,EAAE,CAAA;YACzC,CAAC;YAED,0BAA0B;YAC1B,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YACtE,MAAM,UAAU,GAAsB,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAC/D,IAAI,EAAE,KAAK;gBACX,EAAE,EAAE,MAAM;gBACV,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,mBAAmB,KAAK,EAAE;gBACvC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;aAC3B,CAAC,CAAC,CAAA;YAEH,kBAAkB;YAClB,MAAM,MAAM,GAAG,SAAS,CAAC,WAAW,EAAE,CAAA;YACtC,MAAM,SAAS,GAAc;gBAC3B,OAAO,EAAE,GAAG,SAAS,IAAI,IAAI,EAAE;gBAC/B,WAAW,EAAE,gBAAgB,IAAI,EAAE;gBACnC,WAAW,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE;gBACxF,QAAQ,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;gBAChE,UAAU;gBACV,SAAS,EAAE;oBACT,KAAK,EAAE;wBACL,WAAW,EAAE,sBAAsB;wBACnC,OAAO,EAAE;4BACP,kBAAkB,EAAE;gCAClB,MAAM,EAAE,UAAU,CAAC,UAAU;oCAC3B,CAAC,CAAC,EAAE,IAAI,EAAE,wBAAwB,UAAU,CAAC,UAAU,EAAE,EAAE;oCAC3D,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;6BACvB;yBACF;qBACF;oBACD,KAAK,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE;oBACtC,KAAK,EAAE,EAAE,WAAW,EAAE,uBAAuB,EAAE;iBAChD;aACF,CAAA;YAED,gDAAgD;YAChD,IAAI,UAAU,CAAC,MAAM,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;gBAC/C,WAAW,CAAC,UAAW,CAAC,OAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC,MAAM,CAAA;YAC7E,CAAC;YAED,uEAAuE;YACvE,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAM,CAAC,cAAc,CAAC,CAAA;YACnD,QAAQ,MAAM,EAAE,CAAC;gBACf,KAAK,KAAK;oBACR,QAAQ,CAAC,GAAG,GAAG,SAAS,CAAA;oBACxB,MAAK;gBACP,KAAK,KAAK;oBACR,QAAQ,CAAC,GAAG,GAAG,SAAS,CAAA;oBACxB,MAAK;gBACP,KAAK,MAAM;oBACT,QAAQ,CAAC,IAAI,GAAG,SAAS,CAAA;oBACzB,MAAK;gBACP,KAAK,QAAQ;oBACX,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAA;oBAC3B,MAAK;gBACP,KAAK,SAAS;oBACZ,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAA;oBAC5B,MAAK;gBACP,KAAK,MAAM;oBACT,QAAQ,CAAC,IAAI,GAAG,SAAS,CAAA;oBACzB,MAAK;gBACP,KAAK,OAAO;oBACV,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAA;oBAC1B,MAAK;gBACP,KAAK,OAAO;oBACV,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAA;oBAC1B,MAAK;gBACP;oBACE,yBAAyB;oBACzB,MAAK;YACT,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,WAAW,CAAA;AACpB,CAAC,CAAA"}
@@ -1,26 +0,0 @@
1
- import { type ApiEndpointSchema, type SwaggerDocument } from '@furystack/rest'
2
- import type { RestApiImplementation } from '../api-manager.js'
3
- import { getSchemaFromApi } from '../get-schema-from-api.js'
4
- import { JsonResult, type RequestAction } from '../request-action-implementation.js'
5
- import { generateSwaggerJsonFromApiSchema } from '../swagger/generate-swagger-json.js'
6
-
7
- export type GetSchemaResult = ApiEndpointSchema['endpoints']
8
-
9
- /**
10
- * Generates a RequestAction that retrieves the Swagger JSON schema from a FuryStack API implementation.
11
- *
12
- * @param api - The API implementation from which to extract the schema.
13
- * @returns A RequestAction that handles the GET request for the schema.
14
- */
15
- export const CreateGetSwaggerJsonAction = <T extends RestApiImplementation<any>>(
16
- api: T,
17
- name = 'FuryStack API',
18
- description = 'API documentation generated from FuryStack API schema',
19
- version = '1.0.0',
20
- ): RequestAction<{ result: GetSchemaResult | SwaggerDocument }> => {
21
- const { endpoints } = getSchemaFromApi({ api, name, description, version })
22
- const swaggerJson = generateSwaggerJsonFromApiSchema({ api: endpoints, title: name, description, version })
23
- return async () => {
24
- return JsonResult(swaggerJson, 200)
25
- }
26
- }
@@ -1,11 +0,0 @@
1
- import type { RestApi, SwaggerDocument } from '@furystack/rest'
2
-
3
- /**
4
- * Extends a RestApi with endpoints for both schema.json and swagger.json
5
- */
6
- export type WithSchemaAndSwaggerAction<T extends RestApi> = T & {
7
- GET: {
8
- '/schema': { result: Record<string, any>; headers: { accept: 'application/schema+json' } }
9
- '/swagger.json': { result: SwaggerDocument; headers: { accept: 'application/swagger+json' } }
10
- }
11
- }