@opra/common 0.25.4 → 0.26.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 (197) hide show
  1. package/browser.js +1843 -1355
  2. package/cjs/document/api-document.js +16 -220
  3. package/cjs/document/data-type/complex-type-class.js +41 -19
  4. package/cjs/document/data-type/complex-type.js +1 -1
  5. package/cjs/document/data-type/data-type.js +2 -1
  6. package/cjs/document/data-type/enum-type-class.js +15 -12
  7. package/cjs/document/data-type/enum-type.js +28 -14
  8. package/cjs/document/data-type/field-class.js +21 -3
  9. package/cjs/document/data-type/field-decorator.js +0 -19
  10. package/cjs/document/data-type/mapped-type-class.js +8 -10
  11. package/cjs/document/data-type/mapped-type.js +1 -1
  12. package/cjs/document/data-type/simple-type-class.js +17 -8
  13. package/cjs/document/data-type/simple-type.js +1 -1
  14. package/cjs/document/data-type/union-type-class.js +15 -13
  15. package/cjs/document/document-base.js +24 -0
  16. package/cjs/document/factory/api-document-factory.js +231 -0
  17. package/cjs/document/factory/type-document-factory.js +324 -0
  18. package/cjs/document/index.js +7 -1
  19. package/cjs/document/resource/action-decorator.js +27 -0
  20. package/cjs/document/resource/collection-class.js +95 -44
  21. package/cjs/document/resource/collection-decorator.js +182 -0
  22. package/cjs/document/resource/collection.js +4 -4
  23. package/cjs/document/resource/container-class.js +82 -0
  24. package/cjs/document/resource/container-decorator.js +24 -0
  25. package/cjs/document/resource/container.js +29 -0
  26. package/cjs/document/resource/crud-resource.js +31 -0
  27. package/cjs/document/resource/endpoint.js +28 -21
  28. package/cjs/document/resource/operation-decorator.js +27 -0
  29. package/cjs/document/resource/parameter.js +51 -0
  30. package/cjs/document/{decorators/resource.decorator.js → resource/resource-decorator.js} +9 -14
  31. package/cjs/document/resource/resource.js +20 -5
  32. package/cjs/document/resource/singleton-class.js +60 -40
  33. package/cjs/document/resource/singleton-decorator.js +57 -0
  34. package/cjs/document/resource/singleton.js +4 -4
  35. package/cjs/document/resource/storage-class.js +11 -17
  36. package/cjs/document/resource/storage-decorator.js +66 -0
  37. package/cjs/document/resource/storage.js +3 -3
  38. package/cjs/document/type-document.js +195 -0
  39. package/cjs/exception/http-errors/internal-server.error.js +1 -0
  40. package/cjs/exception/opra-exception.js +18 -12
  41. package/cjs/exception/wrap-exception.js +2 -1
  42. package/cjs/helpers/responsive-map.js +3 -3
  43. package/cjs/http/opra-url-path.js +5 -1
  44. package/cjs/schema/document/document-base.interface.js +2 -0
  45. package/cjs/schema/document/type-document.interface.js +2 -0
  46. package/cjs/schema/opra-schema.ns.js +3 -1
  47. package/cjs/schema/type-guards.js +3 -3
  48. package/esm/document/api-document.js +16 -220
  49. package/esm/document/data-type/complex-type-class.js +40 -19
  50. package/esm/document/data-type/complex-type.js +1 -1
  51. package/esm/document/data-type/data-type.js +2 -1
  52. package/esm/document/data-type/enum-type-class.js +15 -12
  53. package/esm/document/data-type/enum-type.js +26 -12
  54. package/esm/document/data-type/field-class.js +20 -3
  55. package/esm/document/data-type/field-decorator.js +0 -19
  56. package/esm/document/data-type/mapped-type-class.js +9 -11
  57. package/esm/document/data-type/mapped-type.js +1 -1
  58. package/esm/document/data-type/simple-type-class.js +15 -6
  59. package/esm/document/data-type/simple-type.js +1 -1
  60. package/esm/document/data-type/union-type-class.js +11 -9
  61. package/esm/document/document-base.js +20 -0
  62. package/esm/document/factory/api-document-factory.js +227 -0
  63. package/esm/document/factory/type-document-factory.js +320 -0
  64. package/esm/document/index.js +7 -1
  65. package/esm/document/resource/action-decorator.js +23 -0
  66. package/esm/document/resource/collection-class.js +95 -44
  67. package/esm/document/resource/collection-decorator.js +178 -0
  68. package/esm/document/resource/collection.js +4 -4
  69. package/esm/document/resource/container-class.js +78 -0
  70. package/esm/document/resource/container-decorator.js +20 -0
  71. package/esm/document/resource/container.js +25 -0
  72. package/esm/document/resource/crud-resource.js +27 -0
  73. package/esm/document/resource/endpoint.js +27 -21
  74. package/esm/document/resource/operation-decorator.js +23 -0
  75. package/esm/document/resource/parameter.js +46 -0
  76. package/esm/document/resource/resource-decorator.js +28 -0
  77. package/esm/document/resource/resource.js +20 -5
  78. package/esm/document/resource/singleton-class.js +58 -38
  79. package/esm/document/resource/singleton-decorator.js +53 -0
  80. package/esm/document/resource/singleton.js +4 -4
  81. package/esm/document/resource/storage-class.js +9 -15
  82. package/esm/document/resource/storage-decorator.js +62 -0
  83. package/esm/document/resource/storage.js +3 -3
  84. package/esm/document/type-document.js +191 -0
  85. package/esm/exception/http-errors/internal-server.error.js +1 -0
  86. package/esm/exception/opra-exception.js +18 -12
  87. package/esm/exception/wrap-exception.js +2 -1
  88. package/esm/helpers/responsive-map.js +3 -3
  89. package/esm/http/opra-url-path.js +4 -1
  90. package/esm/schema/document/document-base.interface.js +1 -0
  91. package/esm/schema/document/type-document.interface.js +1 -0
  92. package/esm/schema/opra-schema.ns.js +3 -1
  93. package/esm/schema/type-guards.js +1 -1
  94. package/package.json +2 -2
  95. package/types/document/api-document.d.ts +32 -73
  96. package/types/document/data-type/complex-type-class.d.ts +5 -5
  97. package/types/document/data-type/complex-type.d.ts +8 -3
  98. package/types/document/{decorators → data-type}/complex-type.decorator.d.ts +1 -1
  99. package/types/document/data-type/data-type.d.ts +11 -1
  100. package/types/document/data-type/enum-type-class.d.ts +6 -6
  101. package/types/document/data-type/enum-type.d.ts +12 -6
  102. package/types/document/data-type/field-class.d.ts +9 -1
  103. package/types/document/data-type/field-decorator.d.ts +1 -1
  104. package/types/document/data-type/field.d.ts +4 -3
  105. package/types/document/data-type/mapped-type-class.d.ts +5 -9
  106. package/types/document/data-type/mapped-type.d.ts +6 -7
  107. package/types/document/data-type/simple-type-class.d.ts +6 -3
  108. package/types/document/{decorators → data-type}/simple-type.decorator.d.ts +1 -1
  109. package/types/document/data-type/union-type-class.d.ts +2 -8
  110. package/types/document/data-type/union-type.d.ts +7 -6
  111. package/types/document/document-base.d.ts +10 -0
  112. package/types/document/factory/api-document-factory.d.ts +50 -0
  113. package/types/document/factory/type-document-factory.d.ts +56 -0
  114. package/types/document/index.d.ts +7 -1
  115. package/types/document/resource/action-decorator.d.ts +6 -0
  116. package/types/document/resource/collection-class.d.ts +17 -12
  117. package/types/document/resource/collection-decorator.d.ts +192 -0
  118. package/types/document/resource/collection.d.ts +11 -19
  119. package/types/document/resource/container-class.d.ts +66 -0
  120. package/types/document/resource/container-decorator.d.ts +37 -0
  121. package/types/document/resource/container.d.ts +25 -0
  122. package/types/document/resource/crud-resource.d.ts +16 -0
  123. package/types/document/resource/endpoint.d.ts +26 -19
  124. package/types/document/resource/operation-decorator.d.ts +6 -0
  125. package/types/document/resource/parameter.d.ts +35 -0
  126. package/types/document/resource/resource-decorator.d.ts +32 -0
  127. package/types/document/resource/resource.d.ts +15 -13
  128. package/types/document/resource/singleton-class.d.ts +15 -14
  129. package/types/document/resource/singleton-decorator.d.ts +125 -0
  130. package/types/document/resource/singleton.d.ts +11 -16
  131. package/types/document/resource/storage-class.d.ts +13 -6
  132. package/types/document/resource/storage-decorator.d.ts +98 -0
  133. package/types/document/resource/storage.d.ts +9 -12
  134. package/types/document/type-document.d.ts +68 -0
  135. package/types/exception/opra-exception.d.ts +1 -1
  136. package/types/http/opra-url-path.d.ts +2 -1
  137. package/types/schema/data-type/complex-type.interface.d.ts +1 -1
  138. package/types/schema/data-type/data-type.interface.d.ts +3 -3
  139. package/types/schema/data-type/enum-type.interface.d.ts +6 -7
  140. package/types/schema/data-type/mapped-type.interface.d.ts +4 -3
  141. package/types/schema/data-type/simple-type.interface.d.ts +3 -3
  142. package/types/schema/data-type/union-type.interface.d.ts +6 -4
  143. package/types/schema/document/api-document.interface.d.ts +11 -0
  144. package/types/schema/document/document-base.interface.d.ts +24 -0
  145. package/types/schema/document/type-document.interface.d.ts +6 -0
  146. package/types/schema/opra-schema.ns.d.ts +3 -1
  147. package/types/schema/resource/collection.interface.d.ts +31 -31
  148. package/types/schema/resource/container.interface.d.ts +4 -3
  149. package/types/schema/resource/resource.interface.d.ts +1 -1
  150. package/types/schema/resource/singleton.interface.d.ts +15 -10
  151. package/types/schema/resource/storage.interface.d.ts +49 -45
  152. package/types/schema/type-guards.d.ts +3 -2
  153. package/cjs/document/decorators/collection-decorator.js +0 -38
  154. package/cjs/document/decorators/singleton.decorator.js +0 -36
  155. package/cjs/document/decorators/storage.decorator.js +0 -35
  156. package/cjs/document/factory/add-references.js +0 -20
  157. package/cjs/document/factory/create-document.js +0 -83
  158. package/cjs/document/factory/factory.js +0 -66
  159. package/cjs/document/factory/import-resource-class.js +0 -54
  160. package/cjs/document/factory/import-type-class.js +0 -146
  161. package/cjs/document/factory/index.js +0 -4
  162. package/cjs/document/factory/process-resources.js +0 -70
  163. package/cjs/document/factory/process-types.js +0 -191
  164. package/cjs/document/utils/generate-codec.js +0 -39
  165. package/esm/document/decorators/collection-decorator.js +0 -34
  166. package/esm/document/decorators/resource.decorator.js +0 -33
  167. package/esm/document/decorators/singleton.decorator.js +0 -32
  168. package/esm/document/decorators/storage.decorator.js +0 -31
  169. package/esm/document/factory/add-references.js +0 -16
  170. package/esm/document/factory/create-document.js +0 -77
  171. package/esm/document/factory/factory.js +0 -62
  172. package/esm/document/factory/import-resource-class.js +0 -48
  173. package/esm/document/factory/import-type-class.js +0 -136
  174. package/esm/document/factory/index.js +0 -1
  175. package/esm/document/factory/process-resources.js +0 -63
  176. package/esm/document/factory/process-types.js +0 -185
  177. package/esm/document/utils/generate-codec.js +0 -33
  178. package/types/document/decorators/collection-decorator.d.ts +0 -30
  179. package/types/document/decorators/resource.decorator.d.ts +0 -9
  180. package/types/document/decorators/singleton.decorator.d.ts +0 -25
  181. package/types/document/decorators/storage.decorator.d.ts +0 -22
  182. package/types/document/factory/add-references.d.ts +0 -4
  183. package/types/document/factory/create-document.d.ts +0 -12
  184. package/types/document/factory/factory.d.ts +0 -63
  185. package/types/document/factory/import-resource-class.d.ts +0 -10
  186. package/types/document/factory/import-type-class.d.ts +0 -17
  187. package/types/document/factory/index.d.ts +0 -1
  188. package/types/document/factory/process-resources.d.ts +0 -9
  189. package/types/document/factory/process-types.d.ts +0 -6
  190. package/types/document/utils/generate-codec.d.ts +0 -10
  191. package/types/schema/document.interface.d.ts +0 -34
  192. /package/cjs/document/{decorators → data-type}/complex-type.decorator.js +0 -0
  193. /package/cjs/document/{decorators → data-type}/simple-type.decorator.js +0 -0
  194. /package/cjs/schema/{document.interface.js → document/api-document.interface.js} +0 -0
  195. /package/esm/document/{decorators → data-type}/complex-type.decorator.js +0 -0
  196. /package/esm/document/{decorators → data-type}/simple-type.decorator.js +0 -0
  197. /package/esm/schema/{document.interface.js → document/api-document.interface.js} +0 -0
@@ -0,0 +1,227 @@
1
+ import { cloneObject, resolveThunk, ResponsiveMap } from '../../helpers/index.js';
2
+ import { OpraSchema } from '../../schema/index.js';
3
+ import { ApiDocument } from '../api-document.js';
4
+ import { RESOURCE_METADATA } from '../constants.js';
5
+ import { ComplexType } from '../data-type/complex-type.js';
6
+ import { EnumType } from '../data-type/enum-type.js';
7
+ import { Collection } from '../resource/collection.js';
8
+ import { Container } from '../resource/container.js';
9
+ import { Singleton } from '../resource/singleton.js';
10
+ import { Storage } from '../resource/storage.js';
11
+ import { TypeDocumentFactory } from './type-document-factory.js';
12
+ /**
13
+ * @class ApiDocumentFactory
14
+ */
15
+ export class ApiDocumentFactory extends TypeDocumentFactory {
16
+ constructor() {
17
+ super(...arguments);
18
+ this.resourceQueue = new ResponsiveMap();
19
+ }
20
+ /**
21
+ * Creates ApiDocument instance from given schema object
22
+ * @param init
23
+ */
24
+ static async createDocument(init) {
25
+ const factory = new ApiDocumentFactory();
26
+ const document = factory.document = new ApiDocument();
27
+ await factory.initDocument(init);
28
+ return document;
29
+ }
30
+ /**
31
+ * Downloads schema from the given URL and creates the document instance * @param url
32
+ */
33
+ static async createDocumentFromUrl(url) {
34
+ const factory = new ApiDocumentFactory();
35
+ const document = factory.document = new ApiDocument();
36
+ await factory.initDocumentFromUrl(url);
37
+ return document;
38
+ }
39
+ async initDocument(init) {
40
+ await super.initDocument(init);
41
+ const processContainer = async (container, containerInit) => {
42
+ if (!containerInit.resources)
43
+ return;
44
+ if (Array.isArray(containerInit.resources)) {
45
+ for (const thunk of containerInit.resources) {
46
+ const initArguments = await this.importResourceInstance(thunk);
47
+ container.resources.set(initArguments.name, await this.createResource(container, initArguments));
48
+ }
49
+ }
50
+ else
51
+ for (const [name, schema] of Object.entries(containerInit.resources)) {
52
+ const initArguments = await this.importResourceSchema(name, schema);
53
+ container.resources.set(initArguments.name, await this.createResource(container, initArguments));
54
+ }
55
+ container.resources.sort();
56
+ };
57
+ if (init.root) {
58
+ this.curPath.push('/root');
59
+ await processContainer(this.document.root, init.root);
60
+ this.curPath.pop();
61
+ this.document.invalidate();
62
+ }
63
+ return this.document;
64
+ }
65
+ async importResourceSchema(name, schema) {
66
+ const convertEndpoints = async (source) => {
67
+ if (!source)
68
+ return;
69
+ const output = {};
70
+ for (const [kA, oA] of Object.entries(source)) {
71
+ /* istanbul ignore next */
72
+ if (!oA)
73
+ continue;
74
+ let parameters;
75
+ if (oA.parameters) {
76
+ parameters = {};
77
+ for (const [kP, oP] of Object.entries(oA.parameters)) {
78
+ if (oP.enum) {
79
+ oP.type = EnumType(oP.enum, { name: kP + 'Enum' });
80
+ }
81
+ parameters[kP] = {
82
+ ...oP,
83
+ type: await this.importDataType(oP.type || 'any')
84
+ };
85
+ }
86
+ }
87
+ output[kA] = { ...oA[kA], parameters };
88
+ }
89
+ return output;
90
+ };
91
+ if (schema.kind === 'Collection') {
92
+ return {
93
+ ...schema,
94
+ kind: schema.kind,
95
+ name,
96
+ type: await this.importDataType(schema.type),
97
+ actions: await convertEndpoints(schema.actions),
98
+ operations: await convertEndpoints(schema.operations),
99
+ };
100
+ }
101
+ else if (schema.kind === 'Singleton') {
102
+ return {
103
+ ...schema,
104
+ kind: schema.kind,
105
+ name,
106
+ type: await this.importDataType(schema.type),
107
+ actions: await convertEndpoints(schema.actions),
108
+ operations: await convertEndpoints(schema.operations),
109
+ };
110
+ }
111
+ else if (schema.kind === 'Storage') {
112
+ return {
113
+ ...schema,
114
+ name,
115
+ actions: await convertEndpoints(schema.actions),
116
+ operations: await convertEndpoints(schema.operations),
117
+ };
118
+ }
119
+ else if (schema.kind === 'Container') {
120
+ const resources = [];
121
+ if (schema.resources) {
122
+ for (const [k, o] of Object.entries(schema.resources)) {
123
+ const rinit = await this.importResourceSchema(k, o);
124
+ resources.push(rinit);
125
+ }
126
+ }
127
+ return {
128
+ ...schema,
129
+ name,
130
+ resources,
131
+ actions: await convertEndpoints(schema.actions)
132
+ };
133
+ }
134
+ throw new TypeError(`Can not import resource schema (${schema.kind})`);
135
+ }
136
+ async importResourceInstance(thunk) {
137
+ thunk = await resolveThunk(thunk);
138
+ let ctor;
139
+ let metadata;
140
+ let instance;
141
+ if (typeof thunk === 'function') {
142
+ ctor = thunk;
143
+ }
144
+ else {
145
+ ctor = Object.getPrototypeOf(thunk).constructor;
146
+ instance = thunk;
147
+ if (!Reflect.hasMetadata(RESOURCE_METADATA, ctor) &&
148
+ OpraSchema.isResource(thunk) && typeof thunk.controller === 'object') {
149
+ ctor = Object.getPrototypeOf(thunk.controller).constructor;
150
+ metadata = thunk;
151
+ instance = thunk.controller;
152
+ }
153
+ }
154
+ metadata = metadata || Reflect.getMetadata(RESOURCE_METADATA, ctor);
155
+ if (!metadata && OpraSchema.isResource(metadata))
156
+ throw new TypeError(`Class "${ctor.name}" doesn't have a valid Resource metadata`);
157
+ const convertEndpoints = async (source) => {
158
+ if (!source)
159
+ return;
160
+ const output = {};
161
+ for (const [kA, oA] of Object.entries(source)) {
162
+ let parameters;
163
+ if (oA.parameters) {
164
+ parameters = {};
165
+ for (const [kP, oP] of Object.entries(oA.parameters)) {
166
+ if (oP.enum) {
167
+ oP.type = EnumType(oP.enum, { name: kP + 'Enum' });
168
+ }
169
+ parameters[kP] = {
170
+ ...oP,
171
+ type: await this.importDataType(oP.type || 'any')
172
+ };
173
+ }
174
+ }
175
+ output[kA] = { ...oA, parameters };
176
+ }
177
+ return output;
178
+ };
179
+ // Clone metadata to prevent changing its contents
180
+ const initArguments = cloneObject(metadata);
181
+ initArguments.controller = instance;
182
+ initArguments.ctor = ctor;
183
+ if (initArguments.actions)
184
+ initArguments.actions = await convertEndpoints(initArguments.actions);
185
+ if (initArguments.kind === 'Collection' || initArguments.kind === 'Singleton') {
186
+ const dataType = await this.importDataType((metadata).type);
187
+ if (!dataType)
188
+ throw new TypeError(`Unable to determine data type of "${initArguments.name}" resource`);
189
+ if (!(dataType instanceof ComplexType))
190
+ throw new TypeError(`Data type of "${initArguments.name}" resource is not a ComplexType`);
191
+ initArguments.type = dataType;
192
+ if (initArguments.operations)
193
+ initArguments.operations = await convertEndpoints(initArguments.operations);
194
+ }
195
+ else if (initArguments.kind === 'Container') {
196
+ const oldResources = initArguments.resources;
197
+ if (Array.isArray(oldResources)) {
198
+ initArguments.resources = [];
199
+ for (const t of oldResources) {
200
+ const rinit = await this.importResourceInstance(t);
201
+ initArguments.resources.push(rinit);
202
+ }
203
+ }
204
+ }
205
+ return initArguments;
206
+ }
207
+ async createResource(container, initArguments) {
208
+ if (initArguments.kind === 'Collection')
209
+ return new Collection(container, initArguments);
210
+ if (initArguments.kind === 'Singleton')
211
+ return new Singleton(container, initArguments);
212
+ if (initArguments.kind === 'Storage')
213
+ return new Storage(container, initArguments);
214
+ if (initArguments.kind === 'Container') {
215
+ const newContainer = new Container(container, { ...initArguments, resources: undefined });
216
+ if (initArguments.resources) {
217
+ for (const r of initArguments.resources) {
218
+ const res = await this.createResource(newContainer, r);
219
+ newContainer.resources.set(res.name, res);
220
+ }
221
+ }
222
+ return newContainer;
223
+ }
224
+ else
225
+ throw new Error(`Unknown resource type ${initArguments.kind}`);
226
+ }
227
+ }
@@ -0,0 +1,320 @@
1
+ import { validator } from 'valgen';
2
+ import { cloneObject, isConstructor, resolveThunk, ResponsiveMap } from '../../helpers/index.js';
3
+ import { OpraSchema } from '../../schema/index.js';
4
+ import { DATATYPE_METADATA } from '../constants.js';
5
+ import { AnyType, Base64Type, BigintType, BooleanType, DateType, IntegerType, NullType, NumberType, ObjectIdType, ObjectType, StringType, TimestampType, TimeType, UuidType } from '../data-type/builtin/index.js';
6
+ import { ComplexType } from '../data-type/complex-type.js';
7
+ import { EnumType } from '../data-type/enum-type.js';
8
+ import { MappedType } from '../data-type/mapped-type.js';
9
+ import { SimpleType } from '../data-type/simple-type.js';
10
+ import { UnionType } from '../data-type/union-type.js';
11
+ import { TypeDocument } from '../type-document.js';
12
+ /**
13
+ * @class TypeDocumentFactory
14
+ */
15
+ export class TypeDocumentFactory {
16
+ constructor() {
17
+ this.typeQueue = new ResponsiveMap();
18
+ this.circularRefs = new Map();
19
+ this.curPath = [];
20
+ this.cache = new Map();
21
+ }
22
+ /**
23
+ * Creates ApiDocument instance from given schema object
24
+ */
25
+ static async createDocument(init) {
26
+ const factory = new TypeDocumentFactory();
27
+ const document = factory.document = new TypeDocument();
28
+ await factory.initDocument(init);
29
+ return document;
30
+ }
31
+ /**
32
+ * Downloads schema from the given URL and creates the document instance * @param url
33
+ */
34
+ static async createDocumentFromUrl(url) {
35
+ const factory = new TypeDocumentFactory();
36
+ const document = factory.document = new TypeDocument();
37
+ await factory.initDocumentFromUrl(url);
38
+ return document;
39
+ }
40
+ async initDocument(init) {
41
+ this.document.url = init.url;
42
+ if (init.info)
43
+ Object.assign(this.document.info, init.info);
44
+ if (!init?.noBuiltinTypes) {
45
+ const builtinDocument = await this.createBuiltinTypeDocument();
46
+ this.document.references.set('Opra', builtinDocument);
47
+ }
48
+ if (init.references)
49
+ await this.addReferences(init.references);
50
+ if (init.types) {
51
+ this.curPath.push('Types->');
52
+ // Add type sources into typeQueue
53
+ if (Array.isArray(init.types)) {
54
+ let i = 0;
55
+ for (const thunk of init.types) {
56
+ const metadata = Reflect.getMetadata(DATATYPE_METADATA, thunk) || thunk[DATATYPE_METADATA];
57
+ if (!(metadata && metadata.name))
58
+ throw new TypeError(`Metadata information not found at types[${i++}] "${String(thunk)}"`);
59
+ this.typeQueue.set(metadata.name, thunk);
60
+ }
61
+ }
62
+ else
63
+ for (const [name, schema] of Object.entries(init.types)) {
64
+ this.typeQueue.set(name, { ...schema, name });
65
+ }
66
+ // Create type instances
67
+ for (const thunk of this.typeQueue.values()) {
68
+ await this.importDataType(thunk);
69
+ }
70
+ this.document.types.sort();
71
+ this.curPath.pop();
72
+ }
73
+ this.document.invalidate();
74
+ return this.document;
75
+ }
76
+ async initDocumentFromUrl(url) {
77
+ const resp = await fetch(url, { method: 'GET' });
78
+ const init = await resp.json();
79
+ if (!init)
80
+ throw new TypeError(`Invalid response returned from url: ${url}`);
81
+ return await this.initDocument({ ...init, url });
82
+ }
83
+ async createBuiltinTypeDocument() {
84
+ const init = {
85
+ version: OpraSchema.SpecVersion,
86
+ info: {
87
+ version: OpraSchema.SpecVersion,
88
+ title: 'Opra built-in types',
89
+ contact: [{
90
+ url: 'https://github.com/oprajs/opra'
91
+ }
92
+ ],
93
+ license: {
94
+ url: 'https://github.com/oprajs/opra/blob/main/LICENSE',
95
+ name: 'MIT'
96
+ }
97
+ },
98
+ types: [AnyType, Base64Type, BigintType, BooleanType,
99
+ DateType, UuidType, IntegerType, NullType,
100
+ NumberType, ObjectType, ObjectIdType, StringType,
101
+ TimeType, TimestampType
102
+ ]
103
+ };
104
+ const factory = new TypeDocumentFactory();
105
+ factory.document = new TypeDocument();
106
+ return await factory.initDocument({ ...init, noBuiltinTypes: true });
107
+ }
108
+ async addReferences(references) {
109
+ const { document } = this;
110
+ for (const [ns, r] of Object.entries(references)) {
111
+ if (typeof r === 'string') {
112
+ document.references.set(ns, await this.initDocumentFromUrl(r));
113
+ }
114
+ else if (r instanceof TypeDocument)
115
+ document.references.set(ns, r);
116
+ else if (typeof r === 'object') {
117
+ document.references.set(ns, await this.initDocument(r));
118
+ }
119
+ else
120
+ throw new TypeError(`Invalid document reference (${ns}) in schema`);
121
+ }
122
+ }
123
+ async importDataType(thunk) {
124
+ thunk = await resolveThunk(thunk);
125
+ let name = '';
126
+ let schema;
127
+ let ctor;
128
+ if (typeof thunk === 'string') {
129
+ name = thunk;
130
+ schema = this.typeQueue.get(name);
131
+ }
132
+ else if (typeof thunk === 'function') {
133
+ const metadata = Reflect.getMetadata(DATATYPE_METADATA, thunk);
134
+ if (!metadata) {
135
+ // Check if is an internal type class like String, Number etc
136
+ const dataType = this.document.getDataType(thunk, true);
137
+ if (dataType)
138
+ return dataType;
139
+ throw new TypeError(`Class "${thunk.name}" doesn't have a valid DataType metadata`);
140
+ }
141
+ name = metadata.name;
142
+ schema = metadata;
143
+ ctor = thunk;
144
+ }
145
+ else if (typeof thunk === 'object') {
146
+ if (OpraSchema.isDataType(thunk)) {
147
+ name = thunk.name;
148
+ ctor = thunk.ctor || ctor;
149
+ schema = thunk;
150
+ }
151
+ else {
152
+ // It should be an enum object
153
+ const metadata = thunk[DATATYPE_METADATA];
154
+ if (!metadata)
155
+ throw new TypeError(`No EnumType metadata found for object ${JSON.stringify(thunk).substring(0, 20)}...`);
156
+ name = metadata.name;
157
+ const dataType = this.document.getDataType(name, true);
158
+ if (dataType)
159
+ return dataType;
160
+ schema = cloneObject(metadata);
161
+ }
162
+ }
163
+ ctor = ctor ?? (schema && (isConstructor(schema.ctor)) ? schema.ctor : undefined);
164
+ if (name) {
165
+ if (this.circularRefs.has(name.toLowerCase()))
166
+ throw new TypeError('Circular reference detected');
167
+ const dataType = this.document.getDataType(name, true);
168
+ if (dataType)
169
+ return dataType;
170
+ this.curPath.push('/' + name);
171
+ this.circularRefs.set(name, 1);
172
+ }
173
+ if (ctor) {
174
+ if (this.circularRefs.has(ctor))
175
+ throw new TypeError('Circular reference detected');
176
+ const dataType = this.document.getDataType(ctor, true);
177
+ if (dataType)
178
+ return dataType;
179
+ this.circularRefs.set(ctor, 1);
180
+ }
181
+ try {
182
+ if (!OpraSchema.isDataType(schema))
183
+ throw new TypeError(`No DataType schema determined`);
184
+ // Create an empty DataType instance and add in to document.
185
+ // This will help us for circular dependent data types
186
+ const instance = this.createDataTypeInstance(schema.kind, name);
187
+ if (name)
188
+ this.document.types.set(name, instance);
189
+ const initArguments = cloneObject(schema);
190
+ await this.prepareDataTypeInitArguments(initArguments, ctor);
191
+ if (initArguments.kind === 'ComplexType')
192
+ ComplexType.apply(instance, [this.document, initArguments]);
193
+ else if (initArguments.kind === 'SimpleType')
194
+ SimpleType.apply(instance, [this.document, initArguments]);
195
+ else if (initArguments.kind === 'EnumType')
196
+ EnumType.apply(instance, [this.document, initArguments]);
197
+ else if (initArguments.kind === 'MappedType')
198
+ MappedType.apply(instance, [this.document, initArguments]);
199
+ else if (initArguments.kind === 'UnionType')
200
+ UnionType.apply(instance, [this.document, initArguments]);
201
+ else
202
+ throw new TypeError(`Invalid data type schema: ${String(schema)}`);
203
+ return instance;
204
+ }
205
+ finally {
206
+ if (name) {
207
+ this.curPath.pop();
208
+ this.circularRefs.delete(name.toLowerCase());
209
+ }
210
+ if (ctor)
211
+ this.circularRefs.delete(ctor);
212
+ }
213
+ }
214
+ async prepareDataTypeInitArguments(schema, ctor) {
215
+ const initArguments = schema;
216
+ // Import extending class first
217
+ if (initArguments.kind === 'SimpleType' || initArguments.kind === 'ComplexType' ||
218
+ initArguments.kind === 'EnumType') {
219
+ if (ctor) {
220
+ const baseClass = Object.getPrototypeOf(ctor.prototype).constructor;
221
+ const baseMeta = Reflect.getMetadata(DATATYPE_METADATA, baseClass);
222
+ if (baseMeta) {
223
+ initArguments.base = await this.importDataType(baseClass);
224
+ }
225
+ }
226
+ else if (initArguments.base) {
227
+ initArguments.base = await this.importDataType(initArguments.base);
228
+ }
229
+ }
230
+ if (initArguments.kind === 'SimpleType' && ctor) {
231
+ if (typeof ctor.prototype.decode === 'function')
232
+ initArguments.decoder = initArguments.name
233
+ ? validator(initArguments.name, ctor.prototype.decode) : validator(ctor.prototype.decode);
234
+ if (typeof ctor.prototype.encode === 'function')
235
+ initArguments.decoder = initArguments.name
236
+ ? validator(initArguments.name, ctor.prototype.encode) : validator(ctor.prototype.encode);
237
+ return;
238
+ }
239
+ if (initArguments.kind === 'ComplexType') {
240
+ initArguments.ctor = ctor;
241
+ if (initArguments.fields) {
242
+ const srcFields = initArguments.fields;
243
+ const trgFields = initArguments.fields = {};
244
+ for (const [fieldName, o] of Object.entries(srcFields)) {
245
+ try {
246
+ this.curPath.push('.' + fieldName);
247
+ const srcMeta = typeof o === 'string' ? { type: o } : o;
248
+ const fieldInit = trgFields[fieldName] = {
249
+ ...srcMeta,
250
+ name: fieldName
251
+ };
252
+ if (srcMeta.enum) {
253
+ const enumObject = srcMeta.enum;
254
+ delete srcMeta.enum;
255
+ if (enumObject[DATATYPE_METADATA]) {
256
+ fieldInit.type = await this.importDataType(enumObject);
257
+ }
258
+ else {
259
+ const enumMeta = EnumType(enumObject);
260
+ fieldInit.type = await this.importDataType({ ...enumMeta, kind: 'EnumType', base: undefined });
261
+ }
262
+ }
263
+ else {
264
+ if (srcMeta.isArray && !srcMeta.type)
265
+ throw new TypeError(`"type" must be defined explicitly for array properties`);
266
+ fieldInit.type = await this.importDataType(srcMeta.type || srcMeta.designType || 'any');
267
+ }
268
+ this.curPath.pop();
269
+ }
270
+ catch (e) {
271
+ e.message = `Error in resource "${initArguments.name}.${fieldName}". ` + e.message;
272
+ throw e;
273
+ }
274
+ }
275
+ }
276
+ }
277
+ if (initArguments.kind === 'MappedType') {
278
+ const dataType = await this.importDataType(initArguments.base);
279
+ // istanbul ignore next
280
+ if (!(dataType instanceof ComplexType))
281
+ throw new TypeError('MappedType.type property must address to a ComplexType');
282
+ initArguments.base = dataType;
283
+ }
284
+ if (initArguments.kind === 'UnionType') {
285
+ const oldTypes = initArguments.types;
286
+ initArguments.types = [];
287
+ for (const type of oldTypes)
288
+ initArguments.types.push(await this.importDataType(type));
289
+ }
290
+ return initArguments;
291
+ }
292
+ createDataTypeInstance(kind, name) {
293
+ const dataType = {
294
+ document: this.document,
295
+ kind,
296
+ name
297
+ };
298
+ switch (kind) {
299
+ case OpraSchema.ComplexType.Kind:
300
+ Object.setPrototypeOf(dataType, ComplexType.prototype);
301
+ break;
302
+ case OpraSchema.EnumType.Kind:
303
+ Object.setPrototypeOf(dataType, EnumType.prototype);
304
+ break;
305
+ case OpraSchema.MappedType.Kind:
306
+ Object.setPrototypeOf(dataType, MappedType.prototype);
307
+ break;
308
+ case OpraSchema.SimpleType.Kind:
309
+ Object.setPrototypeOf(dataType, SimpleType.prototype);
310
+ break;
311
+ case OpraSchema.UnionType.Kind:
312
+ Object.setPrototypeOf(dataType, UnionType.prototype);
313
+ break;
314
+ default:
315
+ throw new TypeError(`Unknown DataType kind (${kind})`);
316
+ }
317
+ return dataType;
318
+ }
319
+ }
320
+ TypeDocumentFactory.designTypeMap = new Map();
@@ -1,7 +1,9 @@
1
1
  import 'reflect-metadata';
2
2
  export * from './constants.js';
3
3
  export * from './api-document.js';
4
- export * from './factory/index.js';
4
+ export * from './type-document.js';
5
+ export * from './factory/type-document-factory.js';
6
+ export * from './factory/api-document-factory.js';
5
7
  export * from './data-type/data-type.js';
6
8
  export * from './data-type/complex-type.js';
7
9
  export * from './data-type/field.js';
@@ -10,9 +12,13 @@ export * from './data-type/mapped-type.js';
10
12
  export * from './data-type/simple-type.js';
11
13
  export * from './data-type/union-type.js';
12
14
  export * from './resource/resource.js';
15
+ export * from './resource/crud-resource.js';
13
16
  export * from './resource/collection.js';
17
+ export * from './resource/container.js';
14
18
  export * from './resource/singleton.js';
15
19
  export * from './resource/storage.js';
20
+ export * from './resource/endpoint.js';
21
+ export * from './resource/parameter.js';
16
22
  export * from './interfaces/collection.interface.js';
17
23
  export * from './interfaces/singleton.interface.js';
18
24
  export * from './interfaces/storage.interface.js';
@@ -0,0 +1,23 @@
1
+ import { RESOURCE_METADATA } from '../constants.js';
2
+ export function createActionDecorator(options, bannedProperties, list) {
3
+ const decorator = ((target, propertyKey) => {
4
+ if (typeof propertyKey === 'string' && bannedProperties.includes(propertyKey))
5
+ throw new TypeError(`The "${propertyKey}" property is reserved for "${propertyKey}" operations and cannot be used as an action'`);
6
+ const resourceMetadata = (Reflect.getOwnMetadata(RESOURCE_METADATA, target.constructor) || {});
7
+ resourceMetadata.actions = resourceMetadata.actions || {};
8
+ const actionMeta = { ...options };
9
+ resourceMetadata.actions[propertyKey] = actionMeta;
10
+ for (const fn of list)
11
+ fn(actionMeta);
12
+ Reflect.defineMetadata(RESOURCE_METADATA, resourceMetadata, target.constructor);
13
+ });
14
+ decorator.Parameter = (name, arg0) => {
15
+ const parameterOptions = typeof arg0 === 'string' || typeof arg0 === 'function' ? { type: arg0 } : { ...arg0 };
16
+ list.push((operationMeta) => {
17
+ operationMeta.parameters = operationMeta.parameters || {};
18
+ operationMeta.parameters[name] = { ...parameterOptions };
19
+ });
20
+ return decorator;
21
+ };
22
+ return decorator;
23
+ }