@bagelink/sdk 1.20.54 → 1.20.56
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.
- package/package.json +1 -1
- package/src/index.js +715 -0
- package/src/openAPITools/StreamController.js +62 -0
- package/src/openAPITools/functionGenerator.js +979 -0
- package/src/openAPITools/index.js +40 -0
- package/src/openAPITools/moduleGenerator.js +125 -0
- package/src/openAPITools/streamClient.js +276 -0
- package/src/openAPITools/streamDetector.js +204 -0
- package/src/openAPITools/typeGenerator.js +48 -0
- package/src/openAPITools/types/index.js +19 -0
- package/src/openAPITools/types/utils.js +30 -0
- package/src/openAPITools/utils.js +282 -0
- package/src/unwrap.js +30 -0
- package/src/utils.js +58 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { isSchemaObject } from './types/utils';
|
|
2
|
+
import { formatType, formatVarType, schemaToType } from './utils';
|
|
3
|
+
export function generateTypes(schemas) {
|
|
4
|
+
if (!schemas) {
|
|
5
|
+
return '';
|
|
6
|
+
}
|
|
7
|
+
const schemaEntries = Object.entries(schemas);
|
|
8
|
+
return schemaEntries
|
|
9
|
+
.map(([_typeName, schema]) => {
|
|
10
|
+
const typeName = formatType(_typeName);
|
|
11
|
+
if (!isSchemaObject(schema)) {
|
|
12
|
+
return '';
|
|
13
|
+
}
|
|
14
|
+
if (schema?.enum) {
|
|
15
|
+
return `export type ${typeName} = ${schema.enum.map((item) => `'${item}'`).join(' | ')};\n`;
|
|
16
|
+
}
|
|
17
|
+
if (schema?.type === 'array' && schema.items) {
|
|
18
|
+
return `export type ${typeName} = (${schemaToType(schema.items)})[];\n`;
|
|
19
|
+
}
|
|
20
|
+
if (!schema.properties) {
|
|
21
|
+
// Handle case where schema has additionalProperties: true
|
|
22
|
+
if (schema.additionalProperties === true) {
|
|
23
|
+
return `export type ${typeName} = { [key: string]: any };\n`;
|
|
24
|
+
}
|
|
25
|
+
// #region Debug
|
|
26
|
+
// console.log('No properties found for schema:', typeName)
|
|
27
|
+
// console.log(JSON.stringify({ typeName, schema }, null, 2))
|
|
28
|
+
// #endregion Debug
|
|
29
|
+
return '';
|
|
30
|
+
}
|
|
31
|
+
// Handle empty properties with additionalProperties
|
|
32
|
+
if (Object.keys(schema.properties).length === 0 && schema.additionalProperties === true) {
|
|
33
|
+
return `export type ${typeName} = { [key: string]: any };\n`;
|
|
34
|
+
}
|
|
35
|
+
const properties = Object.entries(schema.properties)
|
|
36
|
+
.map(([key, value]) => {
|
|
37
|
+
// Wrap key in quotes if it contains hyphens or isn't a valid identifier
|
|
38
|
+
const quotedKey = /^[a-z_$][\w$]*$/i.test(key) ? key : `'${key}'`;
|
|
39
|
+
const varType = formatVarType({ varName: quotedKey, schema: value });
|
|
40
|
+
return `\t\t${varType}`;
|
|
41
|
+
})
|
|
42
|
+
.join(';\n ');
|
|
43
|
+
// Add index signature for additionalProperties if true
|
|
44
|
+
const indexSignature = schema.additionalProperties === true ? '\n\t\t[key: string]: any;' : '';
|
|
45
|
+
return `export type ${typeName} = {\n ${properties};${indexSignature}\n };\n`;
|
|
46
|
+
})
|
|
47
|
+
.join('\n');
|
|
48
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Typed interfaces for OpenAPI 3.1.0
|
|
2
|
+
// see https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md
|
|
3
|
+
const TYPE_FORMATS = [
|
|
4
|
+
'uuid',
|
|
5
|
+
'uri',
|
|
6
|
+
'date-time',
|
|
7
|
+
'date',
|
|
8
|
+
'email',
|
|
9
|
+
'binary',
|
|
10
|
+
'password',
|
|
11
|
+
'phone',
|
|
12
|
+
'uuid4',
|
|
13
|
+
'int32',
|
|
14
|
+
'int64',
|
|
15
|
+
'float',
|
|
16
|
+
'double',
|
|
17
|
+
'byte'
|
|
18
|
+
];
|
|
19
|
+
export * from './utils';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function getPath(pathsObject, path) {
|
|
2
|
+
return pathsObject ? pathsObject[path] : undefined;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* A type guard to check if the given value is a `ReferenceObject`.
|
|
6
|
+
* See https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types
|
|
7
|
+
*
|
|
8
|
+
* @param obj The value to check.
|
|
9
|
+
*/
|
|
10
|
+
export function isReferenceObject(obj) {
|
|
11
|
+
if (!obj) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return Object.hasOwn(obj, '$ref');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A type guard to check if the given object is a `SchemaObject`.
|
|
18
|
+
* Useful to distinguish from `ReferenceObject` values that can be used
|
|
19
|
+
* in most places where `SchemaObject` is allowed.
|
|
20
|
+
*
|
|
21
|
+
* See https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types
|
|
22
|
+
*
|
|
23
|
+
* @param schema The value to check.
|
|
24
|
+
*/
|
|
25
|
+
export function isSchemaObject(schema) {
|
|
26
|
+
return !Object.hasOwn(schema, '$ref');
|
|
27
|
+
}
|
|
28
|
+
export function dereference(property) {
|
|
29
|
+
return property;
|
|
30
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { isReferenceObject, isSchemaObject } from './types/utils';
|
|
2
|
+
export function toCamelCase(str) {
|
|
3
|
+
return str
|
|
4
|
+
?.replaceAll(/[-._\s]+(.)?/g, (_, c) => c?.toUpperCase() || '')
|
|
5
|
+
.replace(/^./, str => str.toLowerCase())
|
|
6
|
+
|| str
|
|
7
|
+
|| '';
|
|
8
|
+
}
|
|
9
|
+
export function toPascalCase(str) {
|
|
10
|
+
return str
|
|
11
|
+
?.replaceAll(/[-_\s]+(.)?/g, (_, c) => c?.toUpperCase() || '')
|
|
12
|
+
.replace(/^./, str => str.toUpperCase())
|
|
13
|
+
|| str
|
|
14
|
+
|| '';
|
|
15
|
+
}
|
|
16
|
+
export function formatType(typeName) {
|
|
17
|
+
typeName = typeName
|
|
18
|
+
.replaceAll('-', '')
|
|
19
|
+
.replaceAll(/post|put$/gi, '')
|
|
20
|
+
.replaceAll(/^body_/gi, '');
|
|
21
|
+
return toPascalCase(typeName);
|
|
22
|
+
}
|
|
23
|
+
function resolveReference(ref) {
|
|
24
|
+
const t = ref.split('/').pop();
|
|
25
|
+
if (!t) {
|
|
26
|
+
return 'any';
|
|
27
|
+
}
|
|
28
|
+
return formatType(t);
|
|
29
|
+
}
|
|
30
|
+
function handleArrayItems(schema) {
|
|
31
|
+
// Handle array items that might be union types
|
|
32
|
+
const itemType = schemaToType(schema.items);
|
|
33
|
+
// Add parentheses around union types to ensure correct syntax
|
|
34
|
+
const needsParentheses = itemType.includes('|') || itemType.includes('&');
|
|
35
|
+
let res = needsParentheses ? `(${itemType})[]` : `${itemType}[]`;
|
|
36
|
+
if (itemType.includes('string | string')) {
|
|
37
|
+
res = res.replace('string | string', 'string');
|
|
38
|
+
}
|
|
39
|
+
if (itemType.includes('string |')) {
|
|
40
|
+
return res.replace('string |', '(string & {}) |');
|
|
41
|
+
}
|
|
42
|
+
if (itemType.includes('| string')) {
|
|
43
|
+
return res.replace('| string', '| (string & {})');
|
|
44
|
+
}
|
|
45
|
+
return res;
|
|
46
|
+
}
|
|
47
|
+
export function schemaToType(schema) {
|
|
48
|
+
if (!schema) {
|
|
49
|
+
return 'any';
|
|
50
|
+
}
|
|
51
|
+
if (isReferenceObject(schema)) {
|
|
52
|
+
if (schema.$ref) {
|
|
53
|
+
return resolveReference(schema.$ref);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!isSchemaObject(schema)) {
|
|
57
|
+
console.warn('Schema is not a SchemaObject:', schema);
|
|
58
|
+
return 'any';
|
|
59
|
+
}
|
|
60
|
+
if (schema.anyOf) {
|
|
61
|
+
let _t = schema.anyOf
|
|
62
|
+
.map(s => schemaToType(s))
|
|
63
|
+
.filter(p => p !== 'any')
|
|
64
|
+
.join(' | ');
|
|
65
|
+
if (_t === '' || _t === 'null') {
|
|
66
|
+
_t = 'any';
|
|
67
|
+
}
|
|
68
|
+
return _t;
|
|
69
|
+
}
|
|
70
|
+
if (schema.oneOf) {
|
|
71
|
+
let _t = schema.oneOf
|
|
72
|
+
.map(s => schemaToType(s))
|
|
73
|
+
.filter(p => p !== 'any')
|
|
74
|
+
.join(' | ');
|
|
75
|
+
if (_t === '' || _t === 'null') {
|
|
76
|
+
_t = 'any';
|
|
77
|
+
}
|
|
78
|
+
return _t;
|
|
79
|
+
}
|
|
80
|
+
if (schema.allOf) {
|
|
81
|
+
return schema.allOf
|
|
82
|
+
.map(s => schemaToType(s))
|
|
83
|
+
.filter(p => p !== 'any')
|
|
84
|
+
.join(' & ');
|
|
85
|
+
}
|
|
86
|
+
// TODO: handle Array.isArray(schema.type) case b4 switch
|
|
87
|
+
switch (schema.type) {
|
|
88
|
+
case 'object': return '{ [key: string]: any }';
|
|
89
|
+
case 'string':
|
|
90
|
+
if (schema.const) {
|
|
91
|
+
return `'${schema.const}'`;
|
|
92
|
+
}
|
|
93
|
+
return 'string';
|
|
94
|
+
case 'integer': return 'number';
|
|
95
|
+
case 'number': return 'number';
|
|
96
|
+
case 'boolean': return 'boolean';
|
|
97
|
+
case 'array': return handleArrayItems(schema);
|
|
98
|
+
case 'undefined': return 'undefined';
|
|
99
|
+
case 'null': return 'null';
|
|
100
|
+
case undefined: return 'any';
|
|
101
|
+
default:
|
|
102
|
+
console.log('Unknown type', schema.type);
|
|
103
|
+
return 'unknown';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export function isOptional(schema) {
|
|
107
|
+
/// / !schema?.required?.includes(schema.title || '')
|
|
108
|
+
const type = schemaToType(schema);
|
|
109
|
+
const splitType = type.split(/\s+\|\s+/);
|
|
110
|
+
const includesNull = splitType.includes('null');
|
|
111
|
+
const includesUndefined = splitType.includes('undefined');
|
|
112
|
+
return includesNull || includesUndefined || schema?.default !== undefined;
|
|
113
|
+
}
|
|
114
|
+
export function cleanOptionals(str) {
|
|
115
|
+
return str
|
|
116
|
+
.split(' | ')
|
|
117
|
+
.filter(t => t !== 'null' && t !== 'undefined')
|
|
118
|
+
.join(' | ');
|
|
119
|
+
}
|
|
120
|
+
export function formatVarType({ varName, schema, required = false, defaultValue, }) {
|
|
121
|
+
let type = schemaToType(schema);
|
|
122
|
+
type = cleanOptionals(type);
|
|
123
|
+
let defaultStr = '';
|
|
124
|
+
if (defaultValue) {
|
|
125
|
+
if (typeof defaultValue === 'string') {
|
|
126
|
+
defaultStr = ` = '${defaultValue}'`;
|
|
127
|
+
}
|
|
128
|
+
else if (typeof defaultValue === 'object') {
|
|
129
|
+
defaultStr = ` = ${JSON.stringify(defaultValue)}`;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
defaultStr = ` = ${defaultValue}`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
let optionalStr = (!required && isOptional(schema)) ? '?' : '';
|
|
136
|
+
if (defaultStr) {
|
|
137
|
+
optionalStr = '';
|
|
138
|
+
}
|
|
139
|
+
return `${varName}${optionalStr}: ${type}${defaultStr}`;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* List of JavaScript/TypeScript reserved keywords and common identifiers to avoid
|
|
143
|
+
*/
|
|
144
|
+
const RESERVED_KEYWORDS = new Set([
|
|
145
|
+
// JavaScript reserved words
|
|
146
|
+
'break',
|
|
147
|
+
'case',
|
|
148
|
+
'catch',
|
|
149
|
+
'class',
|
|
150
|
+
'const',
|
|
151
|
+
'continue',
|
|
152
|
+
'debugger',
|
|
153
|
+
'default',
|
|
154
|
+
'delete',
|
|
155
|
+
'do',
|
|
156
|
+
'else',
|
|
157
|
+
'enum',
|
|
158
|
+
'export',
|
|
159
|
+
'extends',
|
|
160
|
+
'false',
|
|
161
|
+
'finally',
|
|
162
|
+
'for',
|
|
163
|
+
'function',
|
|
164
|
+
'if',
|
|
165
|
+
'import',
|
|
166
|
+
'in',
|
|
167
|
+
'instanceof',
|
|
168
|
+
'new',
|
|
169
|
+
'null',
|
|
170
|
+
'return',
|
|
171
|
+
'super',
|
|
172
|
+
'switch',
|
|
173
|
+
'this',
|
|
174
|
+
'throw',
|
|
175
|
+
'true',
|
|
176
|
+
'try',
|
|
177
|
+
'typeof',
|
|
178
|
+
'var',
|
|
179
|
+
'void',
|
|
180
|
+
'while',
|
|
181
|
+
'with',
|
|
182
|
+
'yield',
|
|
183
|
+
// TypeScript reserved words
|
|
184
|
+
'abstract',
|
|
185
|
+
'any',
|
|
186
|
+
'as',
|
|
187
|
+
'asserts',
|
|
188
|
+
'bigint',
|
|
189
|
+
'boolean',
|
|
190
|
+
'constructor',
|
|
191
|
+
'declare',
|
|
192
|
+
'get',
|
|
193
|
+
'infer',
|
|
194
|
+
'intrinsic',
|
|
195
|
+
'is',
|
|
196
|
+
'keyof',
|
|
197
|
+
'module',
|
|
198
|
+
'namespace',
|
|
199
|
+
'never',
|
|
200
|
+
'number',
|
|
201
|
+
'object',
|
|
202
|
+
'package',
|
|
203
|
+
'private',
|
|
204
|
+
'protected',
|
|
205
|
+
'public',
|
|
206
|
+
'readonly',
|
|
207
|
+
'require',
|
|
208
|
+
'set',
|
|
209
|
+
'static',
|
|
210
|
+
'string',
|
|
211
|
+
'symbol',
|
|
212
|
+
'type',
|
|
213
|
+
'undefined',
|
|
214
|
+
'unique',
|
|
215
|
+
'unknown',
|
|
216
|
+
// Strict mode reserved words
|
|
217
|
+
'implements',
|
|
218
|
+
'interface',
|
|
219
|
+
'let',
|
|
220
|
+
'package',
|
|
221
|
+
'private',
|
|
222
|
+
'protected',
|
|
223
|
+
'public',
|
|
224
|
+
'static',
|
|
225
|
+
// Common global identifiers to avoid
|
|
226
|
+
'arguments',
|
|
227
|
+
'eval',
|
|
228
|
+
'Infinity',
|
|
229
|
+
'NaN',
|
|
230
|
+
'undefined',
|
|
231
|
+
'globalThis',
|
|
232
|
+
'window',
|
|
233
|
+
'document',
|
|
234
|
+
'console',
|
|
235
|
+
'Object',
|
|
236
|
+
'Array',
|
|
237
|
+
'Function',
|
|
238
|
+
'String',
|
|
239
|
+
'Number',
|
|
240
|
+
'Boolean',
|
|
241
|
+
'Date',
|
|
242
|
+
'RegExp',
|
|
243
|
+
'Error',
|
|
244
|
+
'Promise',
|
|
245
|
+
'Map',
|
|
246
|
+
'Set',
|
|
247
|
+
'WeakMap',
|
|
248
|
+
'WeakSet',
|
|
249
|
+
'Symbol',
|
|
250
|
+
'Proxy',
|
|
251
|
+
'Reflect'
|
|
252
|
+
]);
|
|
253
|
+
/**
|
|
254
|
+
* Sanitizes a variable name to ensure it's not a reserved keyword
|
|
255
|
+
* @param varName - The variable name to sanitize
|
|
256
|
+
* @returns A safe variable name
|
|
257
|
+
*/
|
|
258
|
+
export function sanitizeVarName(varName) {
|
|
259
|
+
if (!varName) {
|
|
260
|
+
return varName;
|
|
261
|
+
}
|
|
262
|
+
// If it's a reserved keyword, append underscore
|
|
263
|
+
if (RESERVED_KEYWORDS.has(varName.toLowerCase())) {
|
|
264
|
+
return `${varName}_`;
|
|
265
|
+
}
|
|
266
|
+
return varName;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Converts a string to camelCase and ensures it's not a reserved keyword
|
|
270
|
+
* @param str - The string to convert
|
|
271
|
+
* @returns A safe camelCase variable name
|
|
272
|
+
*/
|
|
273
|
+
export function toCamelCaseSafe(str) {
|
|
274
|
+
const camelCased = toCamelCase(str);
|
|
275
|
+
return sanitizeVarName(camelCased);
|
|
276
|
+
}
|
|
277
|
+
export function cleanPath(path) {
|
|
278
|
+
return path
|
|
279
|
+
.split('/')
|
|
280
|
+
.filter(p => p && !/\{|\}/.test(p))
|
|
281
|
+
.join('/');
|
|
282
|
+
}
|
package/src/unwrap.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unwrap utility for ApiResponse
|
|
3
|
+
*
|
|
4
|
+
* Use this when you need to assign API responses to plain types
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Unwrap an ApiResponse to get the plain data
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import { unwrap } from '@bagelink/sdk'
|
|
11
|
+
*
|
|
12
|
+
* const agent = ref<AgentResponse | null>(null)
|
|
13
|
+
* agent.value = unwrap(await api.agents.getByAgentId({ agentId }))
|
|
14
|
+
*/
|
|
15
|
+
export function unwrap(response) {
|
|
16
|
+
// If it's already unwrapped, return as-is
|
|
17
|
+
if (!response || typeof response !== 'object') {
|
|
18
|
+
return response;
|
|
19
|
+
}
|
|
20
|
+
// If it's an array (ApiResponse extends Array), return as-is
|
|
21
|
+
if (Array.isArray(response)) {
|
|
22
|
+
return response;
|
|
23
|
+
}
|
|
24
|
+
// If it has $raw, it's an enhanced object - return without metadata
|
|
25
|
+
if ('$raw' in response) {
|
|
26
|
+
const { $raw, $metadata, totalCount, page, perPage, totalPages, rateLimit, rateLimitReset, getHeader, hasPagination, hasNextPage, hasPrevPage, ...data } = response;
|
|
27
|
+
return data;
|
|
28
|
+
}
|
|
29
|
+
return response;
|
|
30
|
+
}
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export function formatAPIErrorMessage(err) {
|
|
2
|
+
const error = err;
|
|
3
|
+
// Handle case where error or response is undefined
|
|
4
|
+
if (!error || !error.response) {
|
|
5
|
+
return 'Network error occurred. Please check your connection.';
|
|
6
|
+
}
|
|
7
|
+
const { status, data } = error.response;
|
|
8
|
+
// Handle validation errors (422)
|
|
9
|
+
if (data.detail && Array.isArray(data.detail)) {
|
|
10
|
+
return data.detail
|
|
11
|
+
.map((err) => {
|
|
12
|
+
// Handle nested field paths properly
|
|
13
|
+
const fieldPath = err.loc.slice(1).join('.');
|
|
14
|
+
const field = fieldPath
|
|
15
|
+
.split('.')
|
|
16
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
17
|
+
.join(' ');
|
|
18
|
+
const message = err.msg
|
|
19
|
+
// Common validation messages
|
|
20
|
+
.replace(/^(?:field|value|string|none) required$/i, 'is required')
|
|
21
|
+
.replace(/^value is not a valid/i, 'must be a valid')
|
|
22
|
+
.replace(/^ensure this value/i, 'this value must')
|
|
23
|
+
.replace(/^str type expected$/i, 'must be text')
|
|
24
|
+
.replace(/^value could not be parsed to/i, 'must be a')
|
|
25
|
+
.replace(/^ensure this value has at least/i, 'must have at least')
|
|
26
|
+
.replace(/^ensure this value has at most/i, 'must have at most')
|
|
27
|
+
.replace(/^invalid datetime format/i, 'must be a valid date/time')
|
|
28
|
+
.replace(/^value is not a valid email address/i, 'must be a valid email address')
|
|
29
|
+
.replace(/^value is not a valid integer/i, 'must be a whole number')
|
|
30
|
+
.replace(/^value is not a valid float/i, 'must be a valid number');
|
|
31
|
+
return `${field} ${message.toLowerCase()}`;
|
|
32
|
+
})
|
|
33
|
+
.join('\n');
|
|
34
|
+
}
|
|
35
|
+
// Handle specific HTTP status codes
|
|
36
|
+
switch (status) {
|
|
37
|
+
case 401:
|
|
38
|
+
return 'Authentication required. Please log in.';
|
|
39
|
+
case 403:
|
|
40
|
+
return 'You do not have permission to perform this action.';
|
|
41
|
+
case 404:
|
|
42
|
+
return 'The requested resource was not found.';
|
|
43
|
+
case 429:
|
|
44
|
+
return 'Too many requests. Please try again later.';
|
|
45
|
+
case 500:
|
|
46
|
+
return 'An internal server error occurred. Please try again later.';
|
|
47
|
+
}
|
|
48
|
+
// Handle other errors with detail
|
|
49
|
+
if (data.detail && typeof data.detail === 'string') {
|
|
50
|
+
return data.detail;
|
|
51
|
+
}
|
|
52
|
+
// Handle errors with a message field
|
|
53
|
+
if (data.message) {
|
|
54
|
+
return data.message;
|
|
55
|
+
}
|
|
56
|
+
// Generic error fallback
|
|
57
|
+
return `An error occurred (Status ${status}): ${error.message || 'Unknown error'}`;
|
|
58
|
+
}
|