@declaro/core 2.0.0-beta.8 → 2.0.0-y.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 (90) hide show
  1. package/dist/app/app-context.d.ts +8 -0
  2. package/dist/app/app-lifecycle.d.ts +4 -0
  3. package/dist/app/app.d.ts +22 -0
  4. package/dist/app/index.d.ts +3 -20
  5. package/dist/auth/permission-validator.d.ts +34 -0
  6. package/dist/auth/permission-validator.test.d.ts +1 -0
  7. package/dist/context/context.d.ts +88 -13
  8. package/dist/context/legacy-context.test.d.ts +1 -0
  9. package/dist/errors/errors.d.ts +36 -0
  10. package/dist/events/event-manager.d.ts +11 -6
  11. package/dist/http/headers.d.ts +4 -0
  12. package/dist/http/headers.spec.d.ts +1 -0
  13. package/dist/http/request-context.d.ts +12 -0
  14. package/dist/http/request-context.spec.d.ts +1 -0
  15. package/dist/http/request.d.ts +8 -0
  16. package/dist/http/request.spec.d.ts +1 -0
  17. package/dist/http/url.d.ts +8 -0
  18. package/dist/http/url.spec.d.ts +1 -0
  19. package/dist/index.d.ts +9 -3
  20. package/dist/pkg.cjs +30 -2
  21. package/dist/pkg.mjs +56461 -207
  22. package/dist/schema/application.d.ts +83 -0
  23. package/dist/schema/application.test.d.ts +1 -0
  24. package/dist/schema/define-model.d.ts +7 -4
  25. package/dist/schema/index.d.ts +7 -0
  26. package/dist/schema/labels.d.ts +13 -0
  27. package/dist/schema/labels.test.d.ts +1 -0
  28. package/dist/schema/module.d.ts +7 -0
  29. package/dist/schema/module.test.d.ts +1 -0
  30. package/dist/schema/properties.d.ts +19 -0
  31. package/dist/schema/response.d.ts +31 -0
  32. package/dist/schema/response.test.d.ts +1 -0
  33. package/dist/schema/transform-model.d.ts +1 -1
  34. package/dist/schema/types.d.ts +81 -15
  35. package/dist/schema/types.test.d.ts +1 -0
  36. package/dist/typescript/constant-manipulation/snake-case.d.ts +22 -0
  37. package/dist/typescript/index.d.ts +1 -0
  38. package/dist/typescript/objects.d.ts +6 -0
  39. package/package.json +8 -3
  40. package/src/app/app-context.ts +14 -0
  41. package/src/app/app-lifecycle.ts +14 -0
  42. package/src/app/app.ts +45 -0
  43. package/src/app/index.ts +3 -34
  44. package/src/auth/permission-validator.test.ts +209 -0
  45. package/src/auth/permission-validator.ts +135 -0
  46. package/src/context/context.test.ts +585 -94
  47. package/src/context/context.ts +348 -32
  48. package/src/context/legacy-context.test.ts +141 -0
  49. package/src/errors/errors.ts +73 -0
  50. package/src/events/event-manager.spec.ts +54 -8
  51. package/src/events/event-manager.ts +40 -24
  52. package/src/http/headers.spec.ts +48 -0
  53. package/src/http/headers.ts +16 -0
  54. package/src/http/request-context.spec.ts +39 -0
  55. package/src/http/request-context.ts +43 -0
  56. package/src/http/request.spec.ts +52 -0
  57. package/src/http/request.ts +22 -0
  58. package/src/http/url.spec.ts +87 -0
  59. package/src/http/url.ts +48 -0
  60. package/src/index.ts +9 -3
  61. package/src/schema/application.test.ts +286 -0
  62. package/src/schema/application.ts +150 -0
  63. package/src/schema/define-model.test.ts +48 -2
  64. package/src/schema/define-model.ts +40 -9
  65. package/src/schema/index.ts +7 -0
  66. package/src/schema/labels.test.ts +60 -0
  67. package/src/schema/labels.ts +30 -0
  68. package/src/schema/module.test.ts +39 -0
  69. package/src/schema/module.ts +6 -0
  70. package/src/schema/properties.ts +40 -0
  71. package/src/schema/response.test.ts +101 -0
  72. package/src/schema/response.ts +93 -0
  73. package/src/schema/transform-model.ts +1 -1
  74. package/src/schema/types.test.ts +28 -0
  75. package/src/schema/types.ts +135 -15
  76. package/src/typescript/constant-manipulation/snake-case.md +496 -0
  77. package/src/typescript/constant-manipulation/snake-case.ts +76 -0
  78. package/src/typescript/index.ts +1 -0
  79. package/src/typescript/objects.ts +8 -5
  80. package/tsconfig.json +4 -1
  81. package/dist/context/index.d.ts +0 -3
  82. package/dist/interfaces/IDatastoreProvider.d.ts +0 -16
  83. package/dist/interfaces/IStore.d.ts +0 -4
  84. package/dist/interfaces/index.d.ts +0 -2
  85. package/dist/server/index.d.ts +0 -2
  86. package/src/context/index.ts +0 -3
  87. package/src/interfaces/IDatastoreProvider.ts +0 -23
  88. package/src/interfaces/IStore.ts +0 -4
  89. package/src/interfaces/index.ts +0 -2
  90. package/src/server/index.ts +0 -3
@@ -0,0 +1,39 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { Module } from './module'
3
+ import { Application } from './application'
4
+
5
+ describe('Module definition', () => {
6
+ it('should define a module', () => {
7
+ const application = new Application({
8
+ title: 'My API',
9
+ version: '1.0.0',
10
+ description: 'My API description',
11
+ termsOfService: 'https://example.com/terms',
12
+ contact: {
13
+ name: 'API Support',
14
+ url: 'https://example.com/support',
15
+ email: 'test@test.com',
16
+ },
17
+ })
18
+
19
+ const module = new Module(application, {
20
+ name: 'Module',
21
+
22
+ description: 'Module description',
23
+ externalDocs: {
24
+ description: 'External documentation',
25
+ url: 'https://example.com/docs',
26
+ },
27
+ })
28
+
29
+ expect(module.application).toBeInstanceOf(Application)
30
+ expect(module.application.info.title).toBe('My API')
31
+ expect(module.application.info.version).toBe('1.0.0')
32
+ expect(module.application.info.description).toBe('My API description')
33
+
34
+ expect(module.tag.name).toBe('Module')
35
+ expect(module.tag.description).toBe('Module description')
36
+ expect(module.tag.externalDocs.description).toBe('External documentation')
37
+ expect(module.tag.externalDocs.url).toBe('https://example.com/docs')
38
+ })
39
+ })
@@ -0,0 +1,6 @@
1
+ import type { Application } from './application'
2
+ import type { DeclaroSchema } from './types'
3
+
4
+ export class Module {
5
+ constructor(public readonly application: Application, public readonly tag: DeclaroSchema.TagObject) {}
6
+ }
@@ -0,0 +1,40 @@
1
+ import { type DeclaroSchema } from './types'
2
+
3
+ export const t = {
4
+ string: <T extends DeclaroSchema.NonArraySchemaObject<any, any>>(property?: T) => {
5
+ return {
6
+ ...property,
7
+ type: 'string' as const,
8
+ }
9
+ },
10
+ number: <T extends DeclaroSchema.NonArraySchemaObject<any, any>>(property?: T) => {
11
+ return {
12
+ ...property,
13
+ type: 'number' as const,
14
+ }
15
+ },
16
+ boolean: <T extends DeclaroSchema.NonArraySchemaObject<any, any>>(property?: T) => {
17
+ return {
18
+ ...property,
19
+ type: 'boolean' as const,
20
+ }
21
+ },
22
+ integer: <T extends DeclaroSchema.NonArraySchemaObject<any, any>>(property?: T) => {
23
+ return {
24
+ ...property,
25
+ type: 'integer' as const,
26
+ }
27
+ },
28
+ object: <T extends DeclaroSchema.NonArraySchemaObject<any, any>>(property?: T) => {
29
+ return {
30
+ ...property,
31
+ type: 'object' as const,
32
+ }
33
+ },
34
+ array: <T extends DeclaroSchema.NonArraySchemaObject<any, any>>(property?: T) => {
35
+ return {
36
+ ...property,
37
+ type: 'array' as const,
38
+ }
39
+ },
40
+ }
@@ -0,0 +1,101 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { Response } from './response'
3
+ import type { DeclaroSchema } from './types'
4
+
5
+ describe('Response schema', () => {
6
+ it('should define a response schema', () => {
7
+ const response = new Response(200, {
8
+ description: 'Successful response',
9
+ })
10
+ .content('application/json', {
11
+ schema: {
12
+ type: 'object',
13
+ properties: {
14
+ message: {
15
+ type: 'string',
16
+ },
17
+ },
18
+ },
19
+ })
20
+ .content('application/xml', {
21
+ schema: {
22
+ type: 'object',
23
+ properties: {
24
+ message: {
25
+ type: 'string',
26
+ },
27
+ },
28
+ },
29
+ })
30
+ .content('application/json', {
31
+ schema: {
32
+ type: 'object',
33
+ properties: {
34
+ error: {
35
+ type: 'string',
36
+ },
37
+ },
38
+ },
39
+ })
40
+ .content('application/xml', {
41
+ schema: {
42
+ type: 'object',
43
+ properties: {
44
+ error: {
45
+ type: 'string',
46
+ },
47
+ },
48
+ },
49
+ })
50
+
51
+ expect(response.code).toBe(200)
52
+ expect(response).toBeInstanceOf(Response)
53
+
54
+ const jsonSchema = response.schema.content['application/json'].schema as DeclaroSchema.SchemaObject<any>
55
+ expect(jsonSchema).toBeTypeOf('object')
56
+ expect(jsonSchema.oneOf.length).toBe(2)
57
+ expect((jsonSchema.oneOf[0] as any).properties.message.type).toBe('string')
58
+ expect((jsonSchema.oneOf[1] as any).properties.error.type).toBe('string')
59
+ })
60
+
61
+ it('Should merge response schemas', () => {
62
+ const response = new Response(200, {
63
+ description: 'Successful response',
64
+ }).content('application/json', {
65
+ schema: {
66
+ type: 'object',
67
+ properties: {
68
+ message: {
69
+ type: 'string',
70
+ },
71
+ },
72
+ },
73
+ })
74
+
75
+ const response2 = new Response(200, {
76
+ description: 'Successful response',
77
+ }).content('application/xml', {
78
+ schema: {
79
+ type: 'object',
80
+ properties: {
81
+ message: {
82
+ type: 'string',
83
+ },
84
+ },
85
+ },
86
+ })
87
+
88
+ response.merge(response2)
89
+
90
+ expect(response.code).toBe(200)
91
+ expect(response).toBeInstanceOf(Response)
92
+
93
+ const jsonSchema = response.schema.content['application/json'].schema as DeclaroSchema.SchemaObject<any>
94
+ expect(jsonSchema).toBeTypeOf('object')
95
+ expect((jsonSchema as any).properties.message.type).toBe('string')
96
+
97
+ const xmlSchema = response.schema.content['application/xml'].schema as DeclaroSchema.SchemaObject<any>
98
+ expect(xmlSchema).toBeTypeOf('object')
99
+ expect((xmlSchema as any).properties.message.type).toBe('string')
100
+ })
101
+ })
@@ -0,0 +1,93 @@
1
+ import type { DeclaroSchema } from './types'
2
+ import { merge, cloneDeep } from 'lodash-es'
3
+
4
+ export type MediaRecord<T extends DeclaroSchema.AnyObjectProperties> = {
5
+ contentType: string
6
+ media: DeclaroSchema.MediaTypeObject<T>
7
+ }
8
+
9
+ export class Response {
10
+ private _code: number
11
+ private _response: DeclaroSchema.ResponseObject
12
+ private _mediaTypes: MediaRecord<any>[]
13
+
14
+ constructor(code: number, response: DeclaroSchema.ResponseObject) {
15
+ this._mediaTypes = []
16
+ const defaultDescription = code >= 200 && code < 300 ? 'Successful response' : 'Error response'
17
+
18
+ this._code = code
19
+ this._response = {
20
+ ...response,
21
+ description: response.description ?? defaultDescription,
22
+ content: {},
23
+ }
24
+ }
25
+
26
+ /**
27
+ *
28
+ * @param contentType The HTTP content type
29
+ * @param media The openapi media schema defining the response type.
30
+ * @returns An instance of the response object for chaining content types.
31
+ */
32
+ content(contentType: string, ...media: DeclaroSchema.MediaTypeObject<any>[]) {
33
+ this._mediaTypes.push(...media.map((m) => ({ contentType, media: m })))
34
+
35
+ return this
36
+ }
37
+
38
+ merge(response: Response) {
39
+ merge(this._response, response._response)
40
+ this._mediaTypes.push(...response._mediaTypes)
41
+
42
+ return this
43
+ }
44
+
45
+ /**
46
+ * The response code.
47
+ *
48
+ * Note: Read-only. This can only be initialized in the constructor.
49
+ */
50
+ get code() {
51
+ return this._code
52
+ }
53
+
54
+ /**
55
+ * The fully composed openapi response object.
56
+ */
57
+ get schema(): DeclaroSchema.ResponseObject {
58
+ const contentTypes = this.getContentTypes()
59
+
60
+ const response = cloneDeep(this._response)
61
+
62
+ contentTypes.forEach((contentType) => {
63
+ const media = this.getMediaForContentType(contentType)
64
+
65
+ if (media.length > 1) {
66
+ response.content[contentType] = {
67
+ schema: {
68
+ oneOf: media.map((m) => m.schema),
69
+ },
70
+ examples: media.reduce((acc, media) => {
71
+ if (media.examples) {
72
+ return { ...acc, ...media.examples }
73
+ }
74
+
75
+ return acc
76
+ }, {}),
77
+ }
78
+ } else if (media.length === 1) {
79
+ response.content[contentType] = media[0]
80
+ }
81
+ })
82
+
83
+ return response
84
+ }
85
+
86
+ protected getContentTypes() {
87
+ return [...new Set(this._mediaTypes.map(({ contentType }) => contentType))]
88
+ }
89
+
90
+ protected getMediaForContentType(contentType: string) {
91
+ return this._mediaTypes.filter((media) => media.contentType === contentType).map((c) => c.media)
92
+ }
93
+ }
@@ -1,7 +1,7 @@
1
1
  import type { Model } from '.'
2
2
  import { TypescriptMap } from './supported-types'
3
3
 
4
- export function transformModel(model: Model) {
4
+ export function transformModel(model: Model<any>) {
5
5
  return {
6
6
  toTypescript() {
7
7
  return [
@@ -0,0 +1,28 @@
1
+ import { describe, it } from 'vitest'
2
+ import { defineModel } from './define-model'
3
+ import type { DeclaroSchema } from './types'
4
+ import { t } from './properties'
5
+
6
+ describe('Type tests', () => {
7
+ it('should infer payload types', () => {
8
+ const model = defineModel('Test', {
9
+ type: 'object',
10
+ properties: {
11
+ foo: t.string(),
12
+ bar: t.object({
13
+ properties: {
14
+ baz: t.integer(),
15
+ },
16
+ }),
17
+ },
18
+ })
19
+
20
+ const bazModel = defineModel('Baz', {
21
+ properties: {
22
+ baz: {
23
+ type: 'integer',
24
+ },
25
+ },
26
+ })
27
+ })
28
+ })
@@ -1,30 +1,46 @@
1
1
  import type { OpenAPIV3_1 } from 'openapi-types'
2
2
  /// @ts-ignore This import is optional - it should be defined by your project's build settings (@declaro/build).
3
3
  import type { ModelNames } from '$models/reference'
4
+ import type { EntityLabels } from './labels'
4
5
 
5
6
  export declare namespace DeclaroSchema {
7
+ type Modify<T, R> = Omit<T, keyof R> & R
8
+
6
9
  export type NonArraySchemaObjectType = OpenAPIV3_1.NonArraySchemaObjectType
7
10
  export type ArraySchemaObjectType = OpenAPIV3_1.ArraySchemaObjectType
11
+ export type AnySchemaObjectType = NonArraySchemaObjectType | ArraySchemaObjectType
8
12
 
9
- export interface BaseSchemaObject extends OpenAPIV3_1.BaseSchemaObject {
10
- $schema?: ModelNames
11
- additionalProperties?: boolean | ReferenceObject | SchemaObject
12
- properties?: {
13
- [name: string]: ReferenceObject | SchemaObject
14
- }
15
- not?: ReferenceObject | SchemaObject
16
- allOf?: (ReferenceObject | SchemaObject)[]
17
- anyOf?: (ReferenceObject | SchemaObject)[]
18
- oneOf?: (ReferenceObject | SchemaObject)[]
13
+ export type AnyObjectProperties = {
14
+ [name: string]: SchemaObject<any> | ReferenceObject
15
+ }
16
+
17
+ export type AnyObjectParameters = {
18
+ [name: string]: Omit<DeclaroSchema.ParameterObject, 'name'>
19
19
  }
20
20
 
21
- export interface NonArraySchemaObject extends BaseSchemaObject {
21
+ export interface BaseSchemaObject<T extends AnyObjectProperties, N extends ModelNames = ModelNames>
22
+ extends OpenAPIV3_1.BaseSchemaObject {
23
+ $schema?: N
24
+ propertyName?: string
25
+ additionalProperties?: boolean | ReferenceObject | SchemaObject<T>
26
+ properties?: T
27
+ labels?: EntityLabels
28
+ not?: ReferenceObject | SchemaObject<T>
29
+ allOf?: (ReferenceObject | SchemaObject<T>)[]
30
+ anyOf?: (ReferenceObject | SchemaObject<T>)[]
31
+ oneOf?: (ReferenceObject | SchemaObject<T>)[]
32
+ }
33
+
34
+ export interface NonArraySchemaObject<T extends AnyObjectProperties, N extends ModelNames>
35
+ extends BaseSchemaObject<T, N> {
22
36
  type?: NonArraySchemaObjectType
37
+ labels?: EntityLabels
23
38
  }
24
39
 
25
- export interface ArraySchemaObject extends BaseSchemaObject {
40
+ export interface ArraySchemaObject<T extends AnyObjectProperties, N extends ModelNames>
41
+ extends BaseSchemaObject<T> {
26
42
  type: ArraySchemaObjectType
27
- items: ReferenceObject | SchemaObject
43
+ items: ReferenceObject | SchemaObject<T, N>
28
44
  }
29
45
 
30
46
  // Open API supports mixed schema objects, but we strategically don't yet.
@@ -34,10 +50,114 @@ export declare namespace DeclaroSchema {
34
50
  // items?: ReferenceObject | SchemaObject
35
51
  // }
36
52
 
37
- export type SchemaObject = ArraySchemaObject | NonArraySchemaObject
53
+ export type SchemaObject<T extends AnyObjectProperties, N extends string = string> =
54
+ | ArraySchemaObject<T, N>
55
+ | NonArraySchemaObject<T, N>
56
+
57
+ export type PropertyType<T extends SchemaObject<any>> = T['type'] extends 'string'
58
+ ? string
59
+ : T['type'] extends 'boolean'
60
+ ? boolean
61
+ : T['type'] extends 'number'
62
+ ? number
63
+ : T['type'] extends 'integer'
64
+ ? number
65
+ : T['type'] extends 'object'
66
+ ? ObjectPayload<T['properties']>
67
+ : T['type'] extends 'array'
68
+ ? ObjectPayload<T['properties']>[]
69
+ : any
70
+
71
+ export type OperationInput<T extends AnyObjectParameters> = {
72
+ [K in keyof T]: PropertyType<T[K]['schema']>
73
+ }
74
+
75
+ export type ObjectPayload<T extends AnyObjectProperties> = {
76
+ [K in keyof T]: PropertyType<T[K]>
77
+ }
78
+
79
+ export type Payload<T extends AnyObjectProperties, TSchema extends SchemaObject<T>> = any
38
80
 
39
81
  export interface ReferenceObject {
40
82
  $ref: ModelNames
41
- format: string
83
+ format?: string
42
84
  }
85
+
86
+ export type InfoObject = OpenAPIV3_1.InfoObject
87
+
88
+ export type TagObject = OpenAPIV3_1.TagObject
89
+
90
+ export type ExampleObject = OpenAPIV3_1.ExampleObject
91
+ export type MediaTypeObject<T extends AnyObjectProperties> = Modify<
92
+ OpenAPIV3_1.MediaTypeObject,
93
+ {
94
+ schema?: SchemaObject<T> | ReferenceObject
95
+ examples?: Record<string, ReferenceObject | ExampleObject>
96
+ }
97
+ >
98
+ export type ServerObject = OpenAPIV3_1.ServerObject
99
+ export type LinkObject = Modify<
100
+ OpenAPIV3_1.LinkObject,
101
+ {
102
+ server?: ServerObject
103
+ }
104
+ >
105
+
106
+ export type ResponseObject = Modify<
107
+ OpenAPIV3_1.ResponseObject,
108
+ {
109
+ headers?: {
110
+ [header: string]: ReferenceObject | HeaderObject
111
+ }
112
+ content?: {
113
+ [media: string]: MediaTypeObject<any>
114
+ }
115
+ links?: {
116
+ [link: string]: ReferenceObject | LinkObject
117
+ }
118
+ }
119
+ >
120
+ export type ResponsesObject = OpenAPIV3_1.ResponsesObject
121
+
122
+ export type HeaderObject = OpenAPIV3_1.HeaderObject
123
+ export type HttpMethods = OpenAPIV3_1.HttpMethods
124
+
125
+ // export type ParameterObject = OpenAPIV3_1.ParameterObject
126
+ export type ParameterObject = Modify<
127
+ OpenAPIV3_1.ParameterObject,
128
+ {
129
+ in: 'query' | 'header' | 'path' | 'cookie'
130
+ schema?: SchemaObject<any> | ReferenceObject
131
+ }
132
+ >
133
+
134
+ export type RequestBodyObject<T extends AnyObjectProperties> = Modify<
135
+ OpenAPIV3_1.RequestBodyObject,
136
+ {
137
+ content: {
138
+ [media: string]: MediaTypeObject<T>
139
+ }
140
+ }
141
+ >
142
+ export type CallbackObject = Record<string, PathItemObject | ReferenceObject>
143
+ export type PathItemObject<T extends {} = {}> = Modify<
144
+ OpenAPIV3_1.PathItemObject<T>,
145
+ {
146
+ servers?: ServerObject[]
147
+ parameters?: (ReferenceObject | ParameterObject)[]
148
+ }
149
+ > & {
150
+ [method in HttpMethods]?: OperationObject<T>
151
+ }
152
+ export type OperationObject<T extends {} = {}> = Modify<
153
+ OpenAPIV3_1.OperationObject<T>,
154
+ {
155
+ parameters?: ParameterObject[]
156
+ requestBody?: ReferenceObject | RequestBodyObject<T>
157
+ responses?: ResponsesObject
158
+ callbacks?: Record<string, ReferenceObject | CallbackObject>
159
+ servers?: ServerObject[]
160
+ }
161
+ > &
162
+ T
43
163
  }