@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
@@ -0,0 +1,525 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.generateTypes = generateTypes;
40
+ const openapi_typescript_1 = __importStar(require("openapi-typescript"));
41
+ const typescript_1 = __importDefault(require("typescript"));
42
+ const utils_1 = require("../utils");
43
+ const constants_1 = require("../constants");
44
+ /**
45
+ * Generates TypeScript types based on an OpenAPI V3 schema content.
46
+ *
47
+ * @param {string | OpenAPI3} schema - The OpenAPI V3 schema to generate types from. The value can be a string
48
+ * representing JSON or YAML format, or already parsed OpenAPI3 object.
49
+ * @return {Promise<string>} A promise that resolves to a string containing the generated TypeScript types.
50
+ */
51
+ async function generateTypes(schema) {
52
+ const schemaObject = typeof schema === 'string' ? await (0, utils_1.toSchemaObject)(schema) : schema;
53
+ const ast = await (0, openapi_typescript_1.default)(schemaObject, {
54
+ exportType: true,
55
+ emptyObjectsUnknown: true,
56
+ enum: true,
57
+ enumValues: true,
58
+ transformProperty: (property, schemaObject) => {
59
+ const validationTags = [];
60
+ // Add validation JSDoc tags based on schema constraints
61
+ if (schemaObject['minLength'] !== undefined) {
62
+ validationTags.push(`@minLength ${schemaObject['minLength']}`);
63
+ }
64
+ if (schemaObject['maxLength'] !== undefined) {
65
+ validationTags.push(`@maxLength ${schemaObject['maxLength']}`);
66
+ }
67
+ if (schemaObject['minimum'] !== undefined) {
68
+ validationTags.push(`@minimum ${schemaObject['minimum']}`);
69
+ }
70
+ if (schemaObject['maximum'] !== undefined) {
71
+ validationTags.push(`@maximum ${schemaObject['maximum']}`);
72
+ }
73
+ if (schemaObject['pattern'] !== undefined) {
74
+ validationTags.push(`@pattern ${schemaObject['pattern']}`);
75
+ }
76
+ if (schemaObject['format'] !== undefined) {
77
+ validationTags.push(`@format ${schemaObject['format']}`);
78
+ }
79
+ if (validationTags.length > 0) {
80
+ const newProperty = typescript_1.default.factory.updatePropertySignature(property, property.modifiers, property.name, property.questionToken, property.type);
81
+ const jsDocText = `*\n * ${validationTags.join('\n * ')}\n `;
82
+ typescript_1.default.addSyntheticLeadingComment(newProperty, typescript_1.default.SyntaxKind.MultiLineCommentTrivia, jsDocText, true);
83
+ return newProperty;
84
+ }
85
+ return property;
86
+ }
87
+ });
88
+ let typesContent = (0, openapi_typescript_1.astToString)(deduplicateUnionConstituents(ast));
89
+ typesContent = extractSchemaTypes(typesContent);
90
+ typesContent = combineModuleTypes(typesContent, schemaObject);
91
+ typesContent = deduplicateExportedTypes(typesContent);
92
+ return replaceFileUploadTypes(typesContent);
93
+ }
94
+ /**
95
+ * Extracts inline schema types from the generated `schemas` block.
96
+ *
97
+ * Finds entries like:
98
+ * /** SomeTypeName *\/
99
+ * "def-N": { ... };
100
+ *
101
+ * Lifts each one into:
102
+ * export type SomeTypeName = { ... };
103
+ *
104
+ * And replaces the inline body in `schemas` with a reference to the new type.
105
+ *
106
+ * @param {string} typeContent - The generated TypeScript type content as a string.
107
+ * @return {string} The transformed types content with extracted schema types.
108
+ */
109
+ function extractSchemaTypes(typeContent) {
110
+ // Matches the header: /** TypeName */ "def-N": <cursor here before the opening brace>
111
+ const extractedTypes = [];
112
+ const typeNames = new Set();
113
+ const entries = [];
114
+ // Maps "def-N" key → extracted type name, used to replace cross-references
115
+ const defToTypeName = new Map();
116
+ // Replace Record<...> types with object types so schema type can be extracted
117
+ const normalizedTypes = typeContent.replace(/(\/\*\*\s*\w+\s*\*\/\s*"def-\d+"):\s*Record<(\w+), (\w+)>;/g, `$1: { [key: $2]: $3 };`);
118
+ let m;
119
+ // Also capture the "def-N" key from the header
120
+ const headerPatternWithKey = /\/\*\*\s*(\w+)\s*\*\/\s*("def-\d+"):\s*(?=\{)/g;
121
+ while ((m = headerPatternWithKey.exec(normalizedTypes)) !== null) {
122
+ const typeName = m[1];
123
+ const defKey = m[2]; // e.g. "def-89"
124
+ const headerEnd = m.index + m[0].length; // points at '{'
125
+ // Walk forward counting braces to find the matching closing '}'
126
+ let depth = 0;
127
+ let i = headerEnd;
128
+ for (; i < normalizedTypes.length; i++) {
129
+ if (normalizedTypes[i] === '{') {
130
+ depth++;
131
+ }
132
+ else if (normalizedTypes[i] === '}') {
133
+ depth--;
134
+ if (depth === 0) {
135
+ break;
136
+ }
137
+ }
138
+ }
139
+ // Variable i is now the index of the closing '}'
140
+ // The entry ends after '}' followed by ';'
141
+ const bodyEnd = i; // index of closing '}'
142
+ const entryEnd = normalizedTypes[bodyEnd + 1] === ';' ? bodyEnd + 2 : bodyEnd + 1;
143
+ defToTypeName.set(defKey, typeName);
144
+ entries.push({
145
+ start: m.index,
146
+ headerEnd,
147
+ typeName,
148
+ body: normalizedTypes.slice(headerEnd, bodyEnd + 1),
149
+ end: entryEnd
150
+ });
151
+ }
152
+ if (entries.length === 0) {
153
+ return normalizedTypes;
154
+ }
155
+ // Build updated content by replacing each entry's body with the type name reference
156
+ let updatedBaseContent = '';
157
+ let cursor = 0;
158
+ for (const entry of entries) {
159
+ // Append everything up to the opening brace of the body
160
+ updatedBaseContent += normalizedTypes.slice(cursor, entry.headerEnd);
161
+ // Replace body with the extracted type name
162
+ updatedBaseContent += entry.typeName + ';';
163
+ cursor = entry.end;
164
+ // Collect the exported type (deduplicate)
165
+ if (!typeNames.has(entry.typeName)) {
166
+ typeNames.add(entry.typeName);
167
+ extractedTypes.push(`export type ${entry.typeName} = ${entry.body}\n`);
168
+ }
169
+ }
170
+ updatedBaseContent += normalizedTypes.slice(cursor);
171
+ const enumDefPrefixType = {};
172
+ // Replace all cross-references like components["schemas"]["def-89"] with the type name
173
+ // Also, extract all enum prefixes in a list
174
+ for (const [defKey, typeName] of defToTypeName) {
175
+ // The defKey already includes surrounding quotes, e.g. "def-89"
176
+ const refPattern = new RegExp(`components\\["schemas"]\\[${defKey}\\]`, 'g');
177
+ updatedBaseContent = updatedBaseContent.replace(refPattern, typeName);
178
+ extractedTypes.forEach((t, idx) => {
179
+ extractedTypes[idx] = t.replace(refPattern, typeName);
180
+ });
181
+ // Extract defKey prefix for enums to be replaced them with type name
182
+ const defKeyPrefix = defKey
183
+ .replace(/"/g, '')
184
+ .replace(/(^|-)([a-z])/g, (_, __, ch) => ch.toUpperCase())
185
+ .replace(/-(\d+)/g, '$1');
186
+ enumDefPrefixType[defKeyPrefix] = typeName;
187
+ }
188
+ const getFirstNumber = (value) => {
189
+ const match = value.match(/\d+/);
190
+ return match ? Number(match[0]) : -Infinity;
191
+ };
192
+ // Sort keys descending by definition ID number if present. This is required
193
+ // to prevent replacing the prefix 'Def125' with 'Def12' or 'Def1' sub-prefix.
194
+ const sortedEnumKeys = Object.keys(enumDefPrefixType).sort((a, b) => getFirstNumber(b) - getFirstNumber(a));
195
+ // Replace enum definition prefixes with corresponding type names
196
+ for (const prefix of sortedEnumKeys) {
197
+ const typeName = enumDefPrefixType[prefix];
198
+ const exportEnumPattern = new RegExp(`export enum ${prefix}(.*){`, 'g');
199
+ updatedBaseContent = updatedBaseContent.replace(exportEnumPattern, `export enum ${typeName}$1{`);
200
+ const referenceEnumPattern = new RegExp(`: ${prefix}(.*);`, 'g');
201
+ extractedTypes.forEach((t, idx) => {
202
+ extractedTypes[idx] = t.replace(referenceEnumPattern, `: ${typeName}$1;`);
203
+ });
204
+ }
205
+ // Remove redundant "Format: ..." prose lines since the @format tag already captures this.
206
+ // If the line ends with */ (end-of-comment), preserve the closing; otherwise remove entirely.
207
+ const redundantFormatRegex = /\n(\s*)\* Format: [^\n]*/g;
208
+ const stripFormat = (s) => s.replace(redundantFormatRegex, (m, g) => m.endsWith('*/') ? `\n${g}*/` : '');
209
+ // Merge consecutive multiline comment endings and openings: */ \n /** → *
210
+ const doubleCommentsRegex = /(\s*)\*\/\s*\n\s*\/\*\*/g;
211
+ updatedBaseContent = stripFormat(updatedBaseContent.replace(doubleCommentsRegex, '$1*'));
212
+ const joined = extractedTypes.join('\n');
213
+ const mergedExtractedTypes = stripFormat(joined.replace(doubleCommentsRegex, '$1*'));
214
+ return updatedBaseContent + '\n' + mergedExtractedTypes;
215
+ }
216
+ /**
217
+ * Deduplicates and simplifies union type constituents in the provided TypeScript Abstract Syntax Tree (AST).
218
+ * Ensures that duplicate constituents are removed and shared types across parenthesized union branches are hoisted.
219
+ *
220
+ * @param {ts.Node[]} ast - An array of TypeScript nodes representing the AST to process and deduplicate.
221
+ * @return {ts.Node[]} An array of transformed TypeScript nodes with union type constituents deduplicated and simplified.
222
+ */
223
+ function deduplicateUnionConstituents(ast) {
224
+ const printer = typescript_1.default.createPrinter();
225
+ const dummyFile = typescript_1.default.createSourceFile('_.ts', '', typescript_1.default.ScriptTarget.Latest);
226
+ const printType = (node) => printer.printNode(typescript_1.default.EmitHint.Unspecified, node, dummyFile);
227
+ const transformer = (context) => {
228
+ const visit = (node) => {
229
+ // Bottom-up: fix nested unions before outer ones
230
+ node = typescript_1.default.visitEachChild(node, visit, context);
231
+ if (!typescript_1.default.isUnionTypeNode(node))
232
+ return node;
233
+ const types = Array.from(node.types);
234
+ // Deduplicate direct top-level members
235
+ const seenDirect = new Set();
236
+ const deduped = [];
237
+ for (const t of types) {
238
+ const key = printType(t);
239
+ if (!seenDirect.has(key)) {
240
+ seenDirect.add(key);
241
+ deduped.push(t);
242
+ }
243
+ }
244
+ // Hoist types shared across at least 2 parenthesized union branches
245
+ const parenBranches = deduped
246
+ .map((t, i) => ({ i, t }))
247
+ .filter(({ t }) => typescript_1.default.isParenthesizedTypeNode(t) && typescript_1.default.isUnionTypeNode(t.type));
248
+ if (parenBranches.length < 2) {
249
+ // Nothing to hoist, still emit step-1 deduplication result if it changed
250
+ if (deduped.length === types.length)
251
+ return node;
252
+ return typescript_1.default.factory.createUnionTypeNode(deduped);
253
+ }
254
+ // Count how many branches each inner type key appears in
255
+ const countInBranches = new Map();
256
+ for (const { t } of parenBranches) {
257
+ const innerUnion = t
258
+ .type;
259
+ const seenInBranch = new Set();
260
+ for (const inner of innerUnion.types) {
261
+ const key = printType(inner);
262
+ if (!seenInBranch.has(key)) {
263
+ seenInBranch.add(key);
264
+ if (countInBranches.has(key)) {
265
+ countInBranches.get(key).count++;
266
+ }
267
+ else {
268
+ countInBranches.set(key, { count: 1, node: inner });
269
+ }
270
+ }
271
+ }
272
+ }
273
+ // Types that appear in at least 2 branches, or already exist directly in the outer union
274
+ const toHoist = new Map();
275
+ for (const [key, { count, node: n }] of countInBranches) {
276
+ if (count >= 2 || seenDirect.has(key)) {
277
+ toHoist.set(key, n);
278
+ }
279
+ }
280
+ if (toHoist.size === 0) {
281
+ if (deduped.length === types.length)
282
+ return node;
283
+ return typescript_1.default.factory.createUnionTypeNode(deduped);
284
+ }
285
+ // Rebuild the union
286
+ const result = [];
287
+ const hoistedKeys = new Set(toHoist.keys());
288
+ for (const t of deduped) {
289
+ if (typescript_1.default.isParenthesizedTypeNode(t) && typescript_1.default.isUnionTypeNode(t.type)) {
290
+ const innerUnion = t.type;
291
+ const remaining = innerUnion.types.filter((inner) => !hoistedKeys.has(printType(inner)));
292
+ if (remaining.length === 0) {
293
+ // Branch fully absorbed — discard
294
+ }
295
+ else if (remaining.length === 1) {
296
+ result.push(remaining[0]); // unwrap
297
+ }
298
+ else {
299
+ result.push(typescript_1.default.factory.createParenthesizedType(typescript_1.default.factory.createUnionTypeNode(remaining)));
300
+ }
301
+ }
302
+ else {
303
+ result.push(t);
304
+ }
305
+ }
306
+ // Append each hoisted type once (skip if already present directly)
307
+ for (const [key, hoistedNode] of toHoist) {
308
+ if (!seenDirect.has(key)) {
309
+ result.push(hoistedNode);
310
+ }
311
+ }
312
+ return typescript_1.default.factory.createUnionTypeNode(result);
313
+ };
314
+ return (root) => typescript_1.default.visitNode(root, visit);
315
+ };
316
+ return ast.map((node) => typescript_1.default.transform(node, [transformer]).transformed[0]);
317
+ }
318
+ /**
319
+ * Combines various module types into a single schema definition by analyzing the provided type content.
320
+ * The combined types are then appended to the original type content and used by the client to allow type
321
+ * inference for client modules (e.g., AuthClient<AuthModuleType>, ResourceClient<UserResourceModuleType>, etc.)
322
+ *
323
+ * @param {string} typeContent The TypeScript content containing type definitions to be processed.
324
+ * @param {OpenAPI3} schema The OpenAPI v3 schema object.
325
+ * @return {string} A string representing the modified type content with additional module type definitions.
326
+ */
327
+ function combineModuleTypes(typeContent, schema) {
328
+ // Map of resource names with corresponding used types
329
+ // (e.g., User -> { single: UserSingle, ...})
330
+ const resourceTypes = {};
331
+ // Map of used auth types in generated schema
332
+ // (e.g., { loginRequest: LoginRequest })
333
+ const authTypes = {};
334
+ // Map of used auth types in generated schema
335
+ // (e.g., { statusResponse: AccountStatusResponse })
336
+ const accountTypes = {};
337
+ // Map of used health types in generated schema
338
+ // (e.g., { checkResponse: HealthCheckResponse })
339
+ const healthTypes = {};
340
+ // Utility function for capitalizing type names
341
+ const capitalize = (text) => text.charAt(0).toUpperCase() + text.slice(1);
342
+ // Utility function for collecting used types
343
+ const collectUsedTypes = (sourceTypes, targetObj, getTypeName, typesContent) => {
344
+ for (const type of sourceTypes) {
345
+ const typeName = getTypeName(type);
346
+ const resourcePattern = new RegExp(`export type ${typeName} = {`, 'g');
347
+ if (resourcePattern.test(typesContent)) {
348
+ targetObj[type] = typeName;
349
+ }
350
+ else {
351
+ targetObj[type] = 'never';
352
+ }
353
+ }
354
+ };
355
+ // Utility function for building TypeScript type from used values
356
+ const buildModuleType = (name, types) => {
357
+ const typeValues = Object.entries(types).map(([key, value]) => `${key}: ${value}`);
358
+ return `export type ${name} = { ${typeValues.join(';')} };`;
359
+ };
360
+ // Find all resource types with used schema types
361
+ for (const type of constants_1.RESOURCE_TYPES) {
362
+ const typeSuffix = capitalize(type);
363
+ const resourcePattern = new RegExp(`export type (.+)${typeSuffix} = {`, 'g');
364
+ let match;
365
+ while ((match = resourcePattern.exec(typeContent)) !== null) {
366
+ const resourceName = match[1];
367
+ resourceTypes[resourceName] ??= {};
368
+ resourceTypes[resourceName][type] = `${resourceName}${typeSuffix}`;
369
+ }
370
+ }
371
+ // Resource types that are not found in schema are set to 'never' type to
372
+ // prevent their usage by generated client
373
+ for (const type of constants_1.RESOURCE_TYPES) {
374
+ for (const resourceType of Object.values(resourceTypes)) {
375
+ if (!resourceType[type]) {
376
+ resourceType[type] = 'never';
377
+ }
378
+ }
379
+ }
380
+ // Find all used auth types
381
+ collectUsedTypes(constants_1.AUTH_TYPES, authTypes, (type) => capitalize(type), typeContent);
382
+ // Extract identity type from authentication '/me' success response
383
+ const config = schema[constants_1.CONFIG_FIELD];
384
+ authTypes['identity'] =
385
+ extractResponseType(typeContent, `${config?.routePrefixes?.auth ?? '/auth'}/me`, 'get') ?? 'never';
386
+ // Find all used account types
387
+ collectUsedTypes(constants_1.ACCOUNT_TYPES, accountTypes, (type) => `Account${capitalize(type)}`, typeContent);
388
+ // Find all used health types
389
+ collectUsedTypes(constants_1.HEALTH_TYPES, healthTypes, (type) => `Health${capitalize(type)}`, typeContent);
390
+ // Combine all module types into a single string and append to the type content
391
+ const moduleTypes = [
392
+ buildModuleType(constants_1.AUTH_MODULE_TYPE, authTypes),
393
+ buildModuleType(constants_1.ACCOUNT_MODULE_TYPE, accountTypes),
394
+ buildModuleType(constants_1.HEALTH_MODULE_TYPE, healthTypes),
395
+ ...Object.entries(resourceTypes).map(([name, types]) => buildModuleType(`${name}${constants_1.RESOURCE_MODULE_TYPE}`, types))
396
+ ];
397
+ return typeContent + `\n${moduleTypes.join('\n\n')}`;
398
+ }
399
+ /**
400
+ * Deduplicates exported type and enum names in the provided TypeScript content.
401
+ * Ensures no naming conflicts between types and enums with the same name and resolves duplicate type names.
402
+ *
403
+ * @param {string} content The string content of the TypeScript file to process.
404
+ * @return {string} A modified string with deduplicated names for exported types and enums.
405
+ */
406
+ function deduplicateExportedTypes(content) {
407
+ const escapeRegExp = (s) => {
408
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
409
+ };
410
+ // Collect type names (detect duplicates) and enum names
411
+ const typePattern = /\bexport type (\w+)/g;
412
+ const enumPattern = /\bexport enum (\w+)\s*\{/g;
413
+ const seenTypes = new Set();
414
+ const duplicateTypes = new Set();
415
+ const enumNames = new Set();
416
+ let m;
417
+ while ((m = typePattern.exec(content)) !== null) {
418
+ const name = m[1];
419
+ if (seenTypes.has(name)) {
420
+ duplicateTypes.add(name);
421
+ }
422
+ else {
423
+ seenTypes.add(name);
424
+ }
425
+ }
426
+ while ((m = enumPattern.exec(content)) !== null) {
427
+ enumNames.add(m[1]);
428
+ }
429
+ // Handle type-enum collisions
430
+ const typeEnumCollisions = [...seenTypes].filter((n) => enumNames.has(n));
431
+ for (const name of typeEnumCollisions) {
432
+ const escaped = escapeRegExp(name);
433
+ const placeholder = `__DEDUP_${name}__`;
434
+ // Protect enum declaration from the global replacement
435
+ content = content.replace(new RegExp(`\\bexport enum ${escaped}\\s*\\{`, 'g'), `export enum ${placeholder} {`);
436
+ // Rename all type-position occurrences (declaration + references) → NameType
437
+ content = content.replace(new RegExp(`\\b${escaped}\\b`, 'g'), `${name}Type`);
438
+ // Finalize enum name → NameEnum
439
+ content = content.replace(new RegExp(`\\bexport enum ${escapeRegExp(placeholder)}\\s*\\{`, 'g'), `export enum ${name}Enum {`);
440
+ }
441
+ // Handle duplicate type names (rename second occurrence only)
442
+ for (const name of duplicateTypes) {
443
+ if (typeEnumCollisions.includes(name)) {
444
+ // already handled above
445
+ continue;
446
+ }
447
+ const escaped = escapeRegExp(name);
448
+ let firstSeen = false;
449
+ content = content.replace(new RegExp(`\\bexport type ${escaped}\\b`, 'g'), (match) => {
450
+ if (!firstSeen) {
451
+ firstSeen = true;
452
+ return match;
453
+ }
454
+ return `export type ${name}Type`;
455
+ });
456
+ }
457
+ return content;
458
+ }
459
+ /**
460
+ * Replaces the `string` and `string[]` types with `File` and `File[]` respectively
461
+ * in TypeScript type definitions related to file uploads, only if the type body
462
+ * includes the annotation `@format binary`.
463
+ *
464
+ * This function processes a TypeScript file content string, identifies type definitions
465
+ * with names matching the pattern `export type <name>FileUpload = {...}`, and updates their
466
+ * definitions to reflect file-like objects instead of plain strings for better type safety.
467
+ *
468
+ * @param {string} typeContent - The TypeScript file content as a string, which
469
+ * contains the type definitions to be inspected and replaced.
470
+ * @return {string} The updated TypeScript file content, with the modified type
471
+ * definitions that reflect `File` and `File[]` for annotated file upload types.
472
+ */
473
+ function replaceFileUploadTypes(typeContent) {
474
+ const fileUploadPattern = /export type \w+FileUpload = \{/g;
475
+ let result = typeContent;
476
+ let offset = 0;
477
+ let match;
478
+ while ((match = fileUploadPattern.exec(typeContent)) !== null) {
479
+ const bodyStart = match.index + match[0].length - 1; // index of opening '{'
480
+ // Extract body using brace-depth counting
481
+ let depth = 0;
482
+ let i = bodyStart;
483
+ for (; i < typeContent.length; i++) {
484
+ if (typeContent[i] === '{')
485
+ depth++;
486
+ else if (typeContent[i] === '}') {
487
+ depth--;
488
+ if (depth === 0)
489
+ break;
490
+ }
491
+ }
492
+ const body = typeContent.slice(bodyStart, i + 1);
493
+ // Guard: only transform if the body contains @format binary
494
+ if (!body.includes('@format binary')) {
495
+ continue;
496
+ }
497
+ // Replace string[] before string to avoid double-replacement
498
+ const newBody = body
499
+ .replace(/\bstring\[]/g, 'File[]')
500
+ .replace(/\bstring\b/g, 'File');
501
+ // Splice the replacement into the result, accounting for prior replacements
502
+ const absoluteStart = match.index + match[0].length - 1 + offset;
503
+ const absoluteEnd = absoluteStart + body.length;
504
+ result =
505
+ result.slice(0, absoluteStart) + newBody + result.slice(absoluteEnd);
506
+ offset += newBody.length - body.length;
507
+ }
508
+ return result;
509
+ }
510
+ /**
511
+ * Extracts the response type from a provided string of types based on the specified path, method, and status code.
512
+ *
513
+ * @param {string} types - The string containing type definitions.
514
+ * @param {string} path - The full API path to match in the type definitions.
515
+ * @param {string} method - The HTTP method to look for (e.g., get, post, put, delete).
516
+ * @param {string} [code=200] - The HTTP response status code to match.
517
+ * @param {string} [mimeType='application/json'] - The HTTP response mime type to match.
518
+ * @return {string | undefined} The extracted response type if a match is found, otherwise undefined.
519
+ */
520
+ function extractResponseType(types, path, method, code = '200', mimeType = 'application/json') {
521
+ const escapedPath = path.replace(/[/[\]{}.*+?^$|()\\]/g, '\\$&');
522
+ const escapedMimeType = mimeType.replace(/[/[\]{}.*+?^$|()\\]/g, '\\$&');
523
+ const regex = new RegExp(`"${escapedPath}":\\s*\\{.*?${method}:\\s*\\{.*?"?${code}"?:\\s*\\{.*?"${escapedMimeType}":\\s*(\\w+)`, 's');
524
+ return types.match(regex)?.[1];
525
+ }
@@ -0,0 +1,2 @@
1
+ export * from './generate-client';
2
+ export * from './generate-types';
@@ -0,0 +1,18 @@
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("./generate-client"), exports);
18
+ __exportStar(require("./generate-types"), exports);
package/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './clients';
2
+ export * from './errors';
package/index.js ADDED
@@ -0,0 +1,18 @@
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("./clients"), exports);
18
+ __exportStar(require("./errors"), exports);
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@appweaver/client",
3
+ "version": "1.0.0",
4
+ "description": "Appweaver - simple, fast, and reliable web application builder tool (@client)",
5
+ "author": "Luka Matosevic",
6
+ "license": "UNLICENSED",
7
+ "main": "./index.js",
8
+ "types": "./index.d.ts",
9
+ "bin": {
10
+ "weaver-client": "./weaver-client.js"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/lmatosevic/appweaver.git",
15
+ "directory": "packages/client"
16
+ },
17
+ "maintainers": [
18
+ {
19
+ "name": "Luka Matosevic",
20
+ "email": "lukamatosevic5@gmail.com",
21
+ "web": "https://lukamatosevic.com"
22
+ }
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "engines": {
28
+ "node": ">= 20"
29
+ },
30
+ "scripts": {
31
+ "weaver-client": "node ./dist/weaver-client.js"
32
+ },
33
+ "dependencies": {
34
+ "commander": "14.0.3",
35
+ "js-yaml": "4.1.1",
36
+ "openapi-fetch": "0.17.0",
37
+ "openapi-typescript": "7.13.0",
38
+ "openapi-typescript-helpers": "0.1.0",
39
+ "prettier": "3.8.1",
40
+ "typescript": "5.9.3"
41
+ },
42
+ "devDependencies": {
43
+ "@types/js-yaml": "4.0.9",
44
+ "rxjs": "7.8.2"
45
+ },
46
+ "peerDependencies": {
47
+ "rxjs": "^7.8.0"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "rxjs": {
51
+ "optional": true
52
+ }
53
+ }
54
+ }
@@ -0,0 +1 @@
1
+ export * from './routes';
package/types/index.js ADDED
@@ -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("./routes"), exports);
@@ -0,0 +1,15 @@
1
+ export type RoutePathConfig = {
2
+ resourcePaths: {
3
+ name: string;
4
+ basePath: string;
5
+ }[];
6
+ routePrefixes: {
7
+ api: string;
8
+ static: string;
9
+ health: string;
10
+ auth: string;
11
+ account: string;
12
+ files: string;
13
+ };
14
+ };
15
+ export type RouteType = 'resource' | 'auth' | 'account' | 'health' | 'files' | 'custom';
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export * from './schema-util';