@appweaver/client 1.0.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 (59) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +7 -0
  3. package/clients/angular-client.d.ts +20 -0
  4. package/clients/angular-client.js +57 -0
  5. package/clients/base-client-interface.d.ts +71 -0
  6. package/clients/base-client-interface.js +2 -0
  7. package/clients/base-client.d.ts +216 -0
  8. package/clients/base-client.js +249 -0
  9. package/clients/fetch-client.d.ts +6 -0
  10. package/clients/fetch-client.js +10 -0
  11. package/clients/index.d.ts +5 -0
  12. package/clients/index.js +21 -0
  13. package/clients/modules/account-client.d.ts +69 -0
  14. package/clients/modules/account-client.js +111 -0
  15. package/clients/modules/auth-client.d.ts +56 -0
  16. package/clients/modules/auth-client.js +78 -0
  17. package/clients/modules/base-module.d.ts +17 -0
  18. package/clients/modules/base-module.js +53 -0
  19. package/clients/modules/files-client.d.ts +27 -0
  20. package/clients/modules/files-client.js +46 -0
  21. package/clients/modules/health-client.d.ts +25 -0
  22. package/clients/modules/health-client.js +34 -0
  23. package/clients/modules/index.d.ts +6 -0
  24. package/clients/modules/index.js +22 -0
  25. package/clients/modules/resource-client.d.ts +90 -0
  26. package/clients/modules/resource-client.js +177 -0
  27. package/clients/responses/file-data-response.d.ts +78 -0
  28. package/clients/responses/file-data-response.js +109 -0
  29. package/clients/responses/index.d.ts +1 -0
  30. package/clients/responses/index.js +17 -0
  31. package/commands/generate-command.d.ts +3 -0
  32. package/commands/generate-command.js +129 -0
  33. package/commands/index.d.ts +1 -0
  34. package/commands/index.js +17 -0
  35. package/constants.d.ts +67 -0
  36. package/constants.js +97 -0
  37. package/errors/client-error.d.ts +6 -0
  38. package/errors/client-error.js +15 -0
  39. package/errors/index.d.ts +1 -0
  40. package/errors/index.js +17 -0
  41. package/generators/generate-client.d.ts +16 -0
  42. package/generators/generate-client.js +394 -0
  43. package/generators/generate-types.d.ts +9 -0
  44. package/generators/generate-types.js +525 -0
  45. package/generators/index.d.ts +2 -0
  46. package/generators/index.js +18 -0
  47. package/index.d.ts +2 -0
  48. package/index.js +18 -0
  49. package/package.json +54 -0
  50. package/types/index.d.ts +1 -0
  51. package/types/index.js +17 -0
  52. package/types/routes.d.ts +15 -0
  53. package/types/routes.js +2 -0
  54. package/utils/index.d.ts +1 -0
  55. package/utils/index.js +17 -0
  56. package/utils/schema-util.d.ts +3 -0
  57. package/utils/schema-util.js +46 -0
  58. package/weaver-client.d.ts +2 -0
  59. package/weaver-client.js +20 -0
package/constants.js ADDED
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ // CAUTION: The constants in this file should not be changed without good reason.
3
+ // They are typically only modified when changes occur in other Appweaver packages
4
+ // (such as core and common) to reflect new route paths or methods.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FILE_OPERATIONS = exports.HEALTH_TYPES = exports.HEALTH_OPERATIONS = exports.HEALTH_MODULE_TYPE = exports.ACCOUNT_TYPES = exports.ACCOUNT_OPERATIONS = exports.ACCOUNT_MODULE_TYPE = exports.AUTH_TYPES = exports.AUTH_OPERATIONS = exports.AUTH_MODULE_TYPE = exports.RESOURCE_TYPES = exports.RESOURCE_OPERATIONS = exports.RESOURCE_MODULE_TYPE = exports.CONFIG_RESOURCE_FIELD = exports.CONFIG_FIELD = exports.FRAMEWORKS = void 0;
7
+ /** Supported frameworks for generating the client class. */
8
+ exports.FRAMEWORKS = ['fetch', 'angular'];
9
+ /** Custom OpenAPI extension key used for extracting route prefixes and base paths to their resources. */
10
+ exports.CONFIG_FIELD = 'x-appweaver-config';
11
+ /** Custom OpenAPI extension key used for extracting resources names from schema CRUD objects. */
12
+ exports.CONFIG_RESOURCE_FIELD = 'x-appweaver-resource';
13
+ /** Suffix used when generating the TypeScript module type name for a resource. */
14
+ exports.RESOURCE_MODULE_TYPE = 'ResourceModuleType';
15
+ /** Maps CRUD operation names to HTTP methods used for matching OpenAPI paths to `ResourceClient` methods. */
16
+ exports.RESOURCE_OPERATIONS = {
17
+ find: 'get',
18
+ query: 'post',
19
+ aggregate: 'post',
20
+ create: 'post',
21
+ update: 'put',
22
+ delete: 'delete',
23
+ export: 'post',
24
+ uploadFiles: 'post',
25
+ deleteFiles: 'post'
26
+ };
27
+ /** Expected type keys for a resource used for type inference. */
28
+ exports.RESOURCE_TYPES = [
29
+ 'single',
30
+ 'multiple',
31
+ 'create',
32
+ 'update',
33
+ 'queryRequest',
34
+ 'queryResponse',
35
+ 'aggregateRequest',
36
+ 'aggregateResponse',
37
+ 'exportRequest',
38
+ 'files',
39
+ 'fileUpload',
40
+ 'fileDelete'
41
+ ];
42
+ /** Type name used when generating the TypeScript module type for the auth module. */
43
+ exports.AUTH_MODULE_TYPE = 'AuthModuleType';
44
+ /** Maps auth operation names to HTTP methods used for matchingOpenAPI paths to `AuthClient` methods.
45
+ * The operation names are matching the real API paths (without a prefix and in camelCase format) from the OpenAPI. */
46
+ exports.AUTH_OPERATIONS = {
47
+ login: 'post',
48
+ logout: 'post',
49
+ refresh: 'post',
50
+ changePassword: 'post',
51
+ exchangeToken: 'post',
52
+ me: 'get'
53
+ };
54
+ /** Expected type keys for the auth module used for type safety. */
55
+ exports.AUTH_TYPES = [
56
+ 'loginRequest',
57
+ 'authenticationResponse',
58
+ 'logoutResponse',
59
+ 'changePasswordRequest',
60
+ 'exchangeTokenRequest',
61
+ 'identity'
62
+ ];
63
+ /** Type name used when generating the TypeScript module type for the account module. */
64
+ exports.ACCOUNT_MODULE_TYPE = 'AccountModuleType';
65
+ /** Maps account operation names to HTTP methods used for matching OpenAPI paths to `AccountClient` methods.
66
+ * The operation names are matching the real API paths (without a prefix and in camelCase format) from the OpenAPI. */
67
+ exports.ACCOUNT_OPERATIONS = {
68
+ sendVerifyEmail: 'post',
69
+ verifyEmail: 'post',
70
+ verifyEmailRedirect: 'get',
71
+ sendResetPassword: 'post',
72
+ resetPassword: 'post',
73
+ send2FACode: 'post',
74
+ verify2FACode: 'post'
75
+ };
76
+ /** Expected type keys for the account module which covers email verification, password reset, and 2FA req/resp. */
77
+ exports.ACCOUNT_TYPES = [
78
+ 'sendEmailVerificationRequest',
79
+ 'statusResponse',
80
+ 'emailVerificationRequest',
81
+ 'sendResetPasswordRequest',
82
+ 'resetPasswordRequest',
83
+ 'send2FACodeRequest',
84
+ 'send2FAResponse',
85
+ 'verify2FARequest',
86
+ 'verify2FAResponse'
87
+ ];
88
+ /** Type name used when generating the TypeScript module type for the health module. */
89
+ exports.HEALTH_MODULE_TYPE = 'HealthModuleType';
90
+ /** Maps health check operations to HTTP GET methods used for matching OpenAPI paths to `HealthClient` methods.
91
+ * The operation names are matching the real API paths (without a prefix and in camelCase format) from the OpenAPI. */
92
+ exports.HEALTH_OPERATIONS = { check: 'get', ready: 'get' };
93
+ /** Expected type keys for the health module used for `HealthClient` response typing. */
94
+ exports.HEALTH_TYPES = ['checkResponse', 'readyResponse'];
95
+ /** Maps file-serving operations to HTTP GET methods used for identifying public and protected file endpoints.
96
+ * The operation names are matching the real API paths (without a prefix and in camelCase format) from the OpenAPI. */
97
+ exports.FILE_OPERATIONS = { public: 'get', protected: 'get' };
@@ -0,0 +1,6 @@
1
+ export declare class ClientError extends Error {
2
+ errorCode: number;
3
+ response?: Response | undefined;
4
+ data?: any | undefined;
5
+ constructor(text: string, errorCode: number, response?: Response | undefined, data?: any | undefined);
6
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClientError = void 0;
4
+ class ClientError extends Error {
5
+ errorCode;
6
+ response;
7
+ data;
8
+ constructor(text, errorCode, response, data) {
9
+ super(text);
10
+ this.errorCode = errorCode;
11
+ this.response = response;
12
+ this.data = data;
13
+ }
14
+ }
15
+ exports.ClientError = ClientError;
@@ -0,0 +1 @@
1
+ export * from './client-error';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./client-error"), exports);
@@ -0,0 +1,16 @@
1
+ import { OpenAPI3 } from 'openapi-typescript';
2
+ import { FRAMEWORKS } from '../constants';
3
+ /**
4
+ * Generates a client class based on the provided OpenAPI schema and already generated types.
5
+ *
6
+ * @param {string | OpenAPI3} schema - The OpenAPI schema used to generate the client. Can be a string
7
+ * (URL or JSON/YAML) or an object.
8
+ * @param {string} [clientName] - Optional name of the client class name to be generated. (default: derived from OpenAPI
9
+ * title or WeaverClient if title is missing)
10
+ * @param {string} [framework='fetch'] - Optional framework for which to generate the client class.
11
+ * @param {string} [typesPath] - Optional path to the types module, for importing related type definitions.
12
+ * @param {boolean} [noTypes=false] - Optional flag to disable client class generic types, useful for environments
13
+ * without type support.
14
+ * @return {Promise<string>} A Promise that resolves to the generated client class as a string.
15
+ */
16
+ export declare function generateClient(schema: string | OpenAPI3, clientName?: string, framework?: (typeof FRAMEWORKS)[number], typesPath?: string, noTypes?: boolean): Promise<string>;
@@ -0,0 +1,394 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateClient = generateClient;
4
+ const clients_1 = require("../clients");
5
+ const utils_1 = require("../utils");
6
+ const constants_1 = require("../constants");
7
+ /**
8
+ * Generates a client class based on the provided OpenAPI schema and already generated types.
9
+ *
10
+ * @param {string | OpenAPI3} schema - The OpenAPI schema used to generate the client. Can be a string
11
+ * (URL or JSON/YAML) or an object.
12
+ * @param {string} [clientName] - Optional name of the client class name to be generated. (default: derived from OpenAPI
13
+ * title or WeaverClient if title is missing)
14
+ * @param {string} [framework='fetch'] - Optional framework for which to generate the client class.
15
+ * @param {string} [typesPath] - Optional path to the types module, for importing related type definitions.
16
+ * @param {boolean} [noTypes=false] - Optional flag to disable client class generic types, useful for environments
17
+ * without type support.
18
+ * @return {Promise<string>} A Promise that resolves to the generated client class as a string.
19
+ */
20
+ async function generateClient(schema, clientName, framework = 'fetch', typesPath, noTypes = false) {
21
+ const schemaObject = typeof schema === 'string' ? await (0, utils_1.toSchemaObject)(schema) : schema;
22
+ // Resolve the client class name
23
+ let className = clientName;
24
+ if (!className) {
25
+ const openApiTitle = schemaObject.info?.title || 'Weaver';
26
+ className = `${openApiTitle.replace(' ', '')}Client`;
27
+ }
28
+ // Resolve the type import statement and types prefix
29
+ const typeName = 'Type';
30
+ const typePrefix = typesPath ? `${typeName}.` : '';
31
+ const pathsTypeImport = typesPath && !noTypes
32
+ ? `import * as ${typeName} from '${typesPath}';\n`
33
+ : '';
34
+ const pathsTypeGeneric = !noTypes ? `<${typePrefix}paths>` : '';
35
+ const config = schemaObject[constants_1.CONFIG_FIELD];
36
+ const schemaRoutes = {
37
+ resource: {},
38
+ auth: [],
39
+ account: [],
40
+ health: [],
41
+ files: [],
42
+ custom: []
43
+ };
44
+ for (const [path, item] of Object.entries(schemaObject.paths || {})) {
45
+ // Skip object references
46
+ if (item['$ref']) {
47
+ continue;
48
+ }
49
+ for (const [method, operation] of Object.entries(item)) {
50
+ // Pass only for valid HTTP methods
51
+ if (['servers', 'parameters'].includes(method)) {
52
+ continue;
53
+ }
54
+ const { type, operationName, resourceName } = routeDetails(path, method, operation, config);
55
+ // Populate schema routes based on route details
56
+ if (type === 'resource') {
57
+ if (!resourceName) {
58
+ continue;
59
+ }
60
+ schemaRoutes.resource[resourceName] ??= [];
61
+ schemaRoutes.resource[resourceName].push(operationName);
62
+ }
63
+ else if (type === 'custom') {
64
+ if (path.startsWith(`${config.routePrefixes.auth}/login/`)) {
65
+ continue;
66
+ }
67
+ schemaRoutes.custom.push({
68
+ method,
69
+ path,
70
+ operationName
71
+ });
72
+ }
73
+ else {
74
+ schemaRoutes[type].push(operationName);
75
+ }
76
+ }
77
+ }
78
+ const clientMethods = [];
79
+ // Utility function for generating generic types for client methods using
80
+ // module type prefix and omitted types based on operations usage
81
+ const makeGenericTypes = (moduleTypeName, operations, usedOperations) => {
82
+ if (noTypes) {
83
+ return '';
84
+ }
85
+ const genericTypes = [];
86
+ if (moduleTypeName) {
87
+ genericTypes.push(`${typePrefix}${moduleTypeName}`);
88
+ }
89
+ const omittedFields = Object.keys(operations).filter((o) => !usedOperations.includes(o));
90
+ if (omittedFields.length > 0) {
91
+ genericTypes.push(`[${omittedFields.map((field) => "'" + field + "'").join(', ')}]`);
92
+ }
93
+ return genericTypes.length > 0 ? `<${genericTypes.join(', ')}>` : '';
94
+ };
95
+ // Add auth client
96
+ if (schemaRoutes.auth.length > 0) {
97
+ const genericTypes = makeGenericTypes(constants_1.AUTH_MODULE_TYPE, constants_1.AUTH_OPERATIONS, schemaRoutes.auth);
98
+ clientMethods.push({
99
+ name: 'auth',
100
+ expression: `this.authClient${genericTypes}('${config.routePrefixes.auth}')`
101
+ });
102
+ }
103
+ // Add account client
104
+ if (schemaRoutes.account.length > 0) {
105
+ const genericTypes = makeGenericTypes(constants_1.ACCOUNT_MODULE_TYPE, constants_1.ACCOUNT_OPERATIONS, schemaRoutes.account);
106
+ clientMethods.push({
107
+ name: 'account',
108
+ expression: `this.accountClient${genericTypes}('${config.routePrefixes.account}')`
109
+ });
110
+ }
111
+ // Add health client
112
+ if (schemaRoutes.health.length > 0) {
113
+ const genericTypes = makeGenericTypes(constants_1.HEALTH_MODULE_TYPE, constants_1.HEALTH_OPERATIONS, schemaRoutes.health);
114
+ clientMethods.push({
115
+ name: 'health',
116
+ expression: `this.healthClient${genericTypes}('${config.routePrefixes.health}')`
117
+ });
118
+ }
119
+ // Add file client
120
+ if (schemaRoutes.files.length > 0) {
121
+ const genericTypes = makeGenericTypes(undefined, constants_1.FILE_OPERATIONS, schemaRoutes.files);
122
+ clientMethods.push({
123
+ name: 'files',
124
+ expression: `this.filesClient${genericTypes}('${config.routePrefixes.files}')`
125
+ });
126
+ }
127
+ // Add resource clients
128
+ for (const [name, operations] of Object.entries(schemaRoutes.resource)) {
129
+ const basePath = config.resourcePaths.find((p) => p.name === name)?.basePath;
130
+ if (!basePath) {
131
+ continue;
132
+ }
133
+ const lowerName = name.charAt(0).toLowerCase() + name.slice(1);
134
+ const resourcePath = `${config.routePrefixes.api}${basePath}`;
135
+ const genericTypes = makeGenericTypes(`${name}${constants_1.RESOURCE_MODULE_TYPE}`, constants_1.RESOURCE_OPERATIONS, operations);
136
+ clientMethods.push({
137
+ name: lowerName,
138
+ expression: `this.resourceClient${genericTypes}('${resourcePath}')`
139
+ });
140
+ }
141
+ // Add custom requests
142
+ for (const customRoute of schemaRoutes.custom) {
143
+ const { method, path, operationName } = customRoute;
144
+ clientMethods.push({
145
+ name: operationName,
146
+ expression: `this.customRequest('${method}', '${path}')`
147
+ });
148
+ }
149
+ // Combine and resolve all client methods
150
+ let clientMethodContent = '';
151
+ const usedMethodNames = new Set(Object.getOwnPropertyNames(clients_1.FetchClient.prototype));
152
+ for (const { name, expression } of clientMethods) {
153
+ // Uppercase the method name if it conflicts with existing class properties
154
+ const propName = usedMethodNames.has(name)
155
+ ? name.charAt(0).toUpperCase() + name.slice(1)
156
+ : name;
157
+ clientMethodContent += `public ${propName} = ${expression};\n\n`;
158
+ usedMethodNames.add(propName);
159
+ }
160
+ // Generate framework-specific client code
161
+ return framework === 'angular'
162
+ ? generateAngularClient(pathsTypeImport, pathsTypeGeneric, className, clientMethodContent)
163
+ : generateFetchClient(pathsTypeImport, pathsTypeGeneric, className, clientMethodContent);
164
+ }
165
+ function generateFetchClient(pathsTypeImport, pathsTypeGeneric, className, clientMethodContent) {
166
+ return `import { ClientConfig, ClientError, FetchClient } from '@appweaver/client';
167
+ ${pathsTypeImport}
168
+ export class ${className} extends FetchClient${pathsTypeGeneric} {
169
+ ${clientMethodContent}
170
+ }
171
+
172
+ export function createClient(config: ClientConfig): ${className} {
173
+ return new ${className}(config);
174
+ }
175
+
176
+ export { ClientError };
177
+ `;
178
+ }
179
+ function generateAngularClient(pathsTypeImport, pathsTypeGeneric, className, clientMethodContent) {
180
+ return `import { ClientConfig, ClientError, AngularClient } from '@appweaver/client';
181
+ import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
182
+ import { firstValueFrom } from 'rxjs';
183
+ ${pathsTypeImport}
184
+ export class ${className} extends AngularClient${pathsTypeGeneric} {
185
+ constructor(http: HttpClient, config: ClientConfig) {
186
+ super(${className}.fetchHandler(http), config);
187
+ }
188
+
189
+ ${clientMethodContent}
190
+
191
+ private static fetchHandler(http: HttpClient) {
192
+ return async (
193
+ input: RequestInfo | URL,
194
+ init?: RequestInit
195
+ ): Promise<Response> => {
196
+ const request = input instanceof Request ? input : null;
197
+
198
+ const url = request
199
+ ? request.url
200
+ : input instanceof URL
201
+ ? input.toString()
202
+ : input;
203
+
204
+ const method = init?.method ?? request?.method ?? 'GET';
205
+
206
+ const headers = new HttpHeaders(
207
+ Object.fromEntries(
208
+ new Headers(init?.headers ?? request?.headers ?? {}).entries()
209
+ )
210
+ );
211
+
212
+ const body =
213
+ method === 'GET' || method === 'HEAD'
214
+ ? undefined
215
+ : (init?.body ?? request?.body ?? undefined);
216
+
217
+ let parsedBody: any;
218
+ if (body) {
219
+ if (headers.get('Content-Type')?.includes('multipart')) {
220
+ parsedBody = await new Response(body).blob();
221
+ } else {
222
+ try {
223
+ parsedBody = JSON.parse(await new Response(body).text());
224
+ } catch (e) {
225
+ parsedBody = body;
226
+ }
227
+ }
228
+ }
229
+
230
+ const angularResponse: HttpResponse<Blob> = await firstValueFrom(
231
+ http.request(method, url as string, {
232
+ body: parsedBody,
233
+ headers: headers,
234
+ observe: 'response',
235
+ responseType: 'blob',
236
+ withCredentials: init?.credentials === 'include'
237
+ })
238
+ );
239
+
240
+ const responseHeaders = new Headers();
241
+
242
+ angularResponse.headers.keys().forEach((key: string) => {
243
+ const value = angularResponse.headers.get(key);
244
+ if (value !== null) {
245
+ responseHeaders.set(key, value);
246
+ }
247
+ });
248
+
249
+ return new Response(angularResponse.body, {
250
+ status: angularResponse.status,
251
+ statusText: angularResponse.statusText,
252
+ headers: responseHeaders
253
+ });
254
+ };
255
+ }
256
+ }
257
+
258
+ export { ClientError };
259
+ `;
260
+ }
261
+ /**
262
+ * Retrieves the details for a specific route based on the provided path, HTTP method, and configuration.
263
+ *
264
+ * @param {string} path - The URL path for which route details are determined.
265
+ * @param {HttpMethod} method - The HTTP method associated with the route (e.g., GET, POST).
266
+ * @param {OperationObject} operation - The operation object containing metadata related to the route.
267
+ * @param {RoutePathConfig} config - The schema configuration object containing route prefixes and resource paths.
268
+ * @return {RouteDetails} An object representing the type of route, optionally including the operation name and
269
+ * resource name if applicable.
270
+ */
271
+ function routeDetails(path, method, operation, config) {
272
+ try {
273
+ const prefixChecks = [
274
+ ['auth', config.routePrefixes.auth, constants_1.AUTH_OPERATIONS],
275
+ ['account', config.routePrefixes.account, constants_1.ACCOUNT_OPERATIONS],
276
+ ['health', config.routePrefixes.health, constants_1.HEALTH_OPERATIONS],
277
+ ['files', config.routePrefixes.files, constants_1.FILE_OPERATIONS]
278
+ ];
279
+ // Try to detect predefined module-specific routes
280
+ for (const [routeType, prefix, operations] of prefixChecks) {
281
+ const operationName = routeOperation(path, method, prefix, operations);
282
+ if (operationName) {
283
+ return { type: routeType, operationName };
284
+ }
285
+ }
286
+ // Try to detect resource routes using resource name and route prefix
287
+ const resourceName = operation[constants_1.CONFIG_RESOURCE_FIELD];
288
+ const resourcePath = config.resourcePaths.find((rp) => rp.name === resourceName)?.basePath;
289
+ const resourcePathPrefix = `${config.routePrefixes.api}${resourcePath}`;
290
+ if (resourceName && resourcePath && path.startsWith(resourcePathPrefix)) {
291
+ const pathSuffix = path.replace(resourcePathPrefix, '');
292
+ const operationName = resourceOperation(pathSuffix, method);
293
+ if (operationName) {
294
+ return { type: 'resource', resourceName, operationName };
295
+ }
296
+ }
297
+ }
298
+ catch {
299
+ // If schema config is invalid or missing, default to custom route type
300
+ return {
301
+ type: 'custom',
302
+ operationName: createOperationName(path, method, config)
303
+ };
304
+ }
305
+ return {
306
+ type: 'custom',
307
+ operationName: createOperationName(path, method, config)
308
+ };
309
+ }
310
+ /**
311
+ * Generates an operation name based on the provided API path, HTTP method, and schema configuration.
312
+ *
313
+ * @param {string} path - The API path for which the operation name is being generated.
314
+ * @param {HttpMethod} method - The HTTP method (e.g., GET, POST, PUT) associated with the operation.
315
+ * @param {RoutePathConfig} config - The configuration object containing schema details, including route prefixes.
316
+ * @return {string} A generated string representing the operation name.
317
+ */
318
+ function createOperationName(path, method, config) {
319
+ if (path.replace(config.routePrefixes.api, '').replace('/', '') === '') {
320
+ return 'info';
321
+ }
322
+ const capitalize = (str) => {
323
+ return str.charAt(0).toUpperCase() + str.slice(1);
324
+ };
325
+ const parts = path.split('/');
326
+ const camelCasePath = parts
327
+ .filter((part) => part.length > 0)
328
+ .map((part, index) => {
329
+ if (part.startsWith('{') && part.endsWith('}')) {
330
+ const nextPart = parts[index + 1];
331
+ const partSlice = part.slice(1, -1).replace('*', 'Any');
332
+ if (nextPart && nextPart.length > 0) {
333
+ return 'By' + capitalize(partSlice);
334
+ }
335
+ return 'By' + capitalize(partSlice);
336
+ }
337
+ return capitalize(part);
338
+ })
339
+ .join('')
340
+ .replace(/-[a-z0-9]/g, (match) => match.replace('-', '').toUpperCase())
341
+ .replace(/^By(.*)/g, 'by$1');
342
+ return `${method}${camelCasePath}`;
343
+ }
344
+ /**
345
+ * Finds a route operation based on the given path, method, prefix, and operations mappings.
346
+ *
347
+ * @param {string} path - The request path to be matched against the operations.
348
+ * @param {HttpMethod} method - The HTTP method to be matched (e.g., GET, POST).
349
+ * @param {string} prefix - The prefix to be removed from the path before processing.
350
+ * @param {Record<string, HttpMethod>} operations - A record mapping operation names to their corresponding HTTP methods.
351
+ * @return {string | undefined} The matching operation name, or `undefined` if no match is found.
352
+ */
353
+ function routeOperation(path, method, prefix, operations) {
354
+ if (!path.startsWith(prefix)) {
355
+ return undefined;
356
+ }
357
+ const normalizedRoute = path
358
+ .replace(prefix, '')
359
+ .replace(/^\//, '')
360
+ .replace(/\/\{[\w*]+}$/, '')
361
+ .replace(/-/g, '')
362
+ .toLowerCase();
363
+ return Object.entries(operations).find(([operation, operationMethod]) => normalizedRoute === operation.toLowerCase() && method === operationMethod)?.[0];
364
+ }
365
+ /**
366
+ * Determines the resource operation based on the provided path suffix and HTTP method.
367
+ *
368
+ * @param {string} pathSuffix - The suffix of the path used to identify a specific resource operation.
369
+ * @param {HttpMethod} method - The HTTP method (e.g., GET, POST, PUT, DELETE) corresponding to the operation.
370
+ * @return {string | undefined} - The matching resource operation key, or undefined if no
371
+ * match is found.
372
+ */
373
+ function resourceOperation(pathSuffix, method) {
374
+ const operationMap = {
375
+ get: {
376
+ '{id}': 'find'
377
+ },
378
+ post: {
379
+ query: 'query',
380
+ aggregate: 'aggregate',
381
+ '': 'create',
382
+ export: 'export',
383
+ '{id}/files': 'uploadFiles',
384
+ '{id}/delete-files': 'deleteFiles'
385
+ },
386
+ put: {
387
+ '{id}': 'update'
388
+ },
389
+ delete: {
390
+ '{id}': 'delete'
391
+ }
392
+ };
393
+ return operationMap[method]?.[pathSuffix.replace(/^\//, '')];
394
+ }
@@ -0,0 +1,9 @@
1
+ import { OpenAPI3 } from 'openapi-typescript';
2
+ /**
3
+ * Generates TypeScript types based on an OpenAPI V3 schema content.
4
+ *
5
+ * @param {string | OpenAPI3} schema - The OpenAPI V3 schema to generate types from. The value can be a string
6
+ * representing JSON or YAML format, or already parsed OpenAPI3 object.
7
+ * @return {Promise<string>} A promise that resolves to a string containing the generated TypeScript types.
8
+ */
9
+ export declare function generateTypes(schema: string | OpenAPI3): Promise<string>;