@bagelink/sdk 1.15.558 → 1.15.562

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/sdk",
3
3
  "type": "module",
4
- "version": "1.15.558",
4
+ "version": "1.15.562",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
package/src/index.ts CHANGED
@@ -1,7 +1,14 @@
1
- import type { AxiosResponse, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosError } from 'axios'
1
+ import type { AxiosResponse, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosError, AxiosInstance } from 'axios'
2
2
  import type { Ref } from 'vue'
3
3
  import ax from 'axios'
4
4
  import { ref } from 'vue'
5
+ import type { StreamEventMap } from './openAPITools/StreamController'
6
+ import {
7
+ createSSEStream,
8
+ createSSEStreamPost,
9
+ type SSEStreamOptions,
10
+ type StreamController
11
+ } from './openAPITools/streamClient'
5
12
 
6
13
  export * from './openAPITools'
7
14
  export { default as openAPI } from './openAPITools'
@@ -324,6 +331,155 @@ export interface ApiConfig {
324
331
  axiosConfig?: AxiosRequestConfig
325
332
  }
326
333
 
334
+ /**
335
+ * Instance-scoped HTTP and SSE transport used by generated clients.
336
+ */
337
+ export interface ApiTransport {
338
+ axios: AxiosInstance
339
+ config: Ref<ApiConfig | null>
340
+ lastResponse: Ref<AxiosResponse | null>
341
+ getStreamBaseURL: () => string
342
+ createSSEStream: <TEventMap extends StreamEventMap = StreamEventMap>(
343
+ path: string,
344
+ options?: SSEStreamOptions
345
+ ) => StreamController<TEventMap>
346
+ createSSEStreamPost: <TEventMap extends StreamEventMap = StreamEventMap, TBody = any>(
347
+ path: string,
348
+ body: TBody,
349
+ options?: SSEStreamOptions
350
+ ) => StreamController<TEventMap>
351
+ }
352
+
353
+ function joinURL(baseURL: string, path: string): string {
354
+ if (/^https?:\/\//i.test(path)) return path
355
+ if (!baseURL) return path
356
+ return `${baseURL.replace(/\/$/, '')}/${path.replace(/^\//, '')}`
357
+ }
358
+
359
+ async function getTransportHeaders(config: ApiConfig, options: SSEStreamOptions): Promise<Record<string, string>> {
360
+ const optionHeaders = typeof options.headers === 'function'
361
+ ? await options.headers()
362
+ : (options.headers ?? {})
363
+ const configuredHeaders = config.axiosConfig?.headers
364
+ const headers = typeof configuredHeaders?.toJSON === 'function'
365
+ ? configuredHeaders.toJSON()
366
+ : (configuredHeaders ?? {})
367
+ const token = await config.auth?.getToken?.()
368
+
369
+ return {
370
+ ...(headers as Record<string, string>),
371
+ ...(token ? { Authorization: `${config.auth?.tokenPrefix ?? 'Bearer'} ${token}` } : {}),
372
+ ...optionHeaders,
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Create an isolated transport. Multiple generated clients can safely use
378
+ * different base URLs, credentials, auth providers, and interceptors.
379
+ */
380
+ export function createApiTransport(config: ApiConfig = {}): ApiTransport {
381
+ const configRef = ref<ApiConfig | null>(null)
382
+ const lastResponse = ref<AxiosResponse | null>(null)
383
+ const axios = ax.create({
384
+ baseURL: config.baseURL,
385
+ withCredentials: config.withCredentials ?? config.axiosConfig?.withCredentials ?? true,
386
+ paramsSerializer: { indexes: null },
387
+ ...config.axiosConfig,
388
+ })
389
+ const streamOptions = async (options: SSEStreamOptions = {}): Promise<SSEStreamOptions> => {
390
+ const currentConfig = configRef.value ?? {}
391
+ return {
392
+ ...options,
393
+ withCredentials: options.withCredentials ?? currentConfig.withCredentials,
394
+ headers: await getTransportHeaders(currentConfig, options),
395
+ }
396
+ }
397
+ const streamURL = (path: string) => joinURL(
398
+ configRef.value?.streamBaseURL ?? axios.defaults.baseURL ?? '',
399
+ path
400
+ )
401
+
402
+ const transport: ApiTransport = {
403
+ axios,
404
+ config: configRef,
405
+ lastResponse,
406
+ getStreamBaseURL: () => configRef.value?.streamBaseURL ?? axios.defaults.baseURL ?? '',
407
+ createSSEStream: <TEventMap extends StreamEventMap = StreamEventMap>(
408
+ path: string,
409
+ options: SSEStreamOptions = {}
410
+ ) => createSSEStream<TEventMap>(streamURL(path), {
411
+ ...options,
412
+ headers: async () => (await streamOptions(options)).headers as Record<string, string>,
413
+ withCredentials: options.withCredentials ?? configRef.value?.withCredentials,
414
+ }),
415
+ createSSEStreamPost: <TEventMap extends StreamEventMap = StreamEventMap, TBody = any>(
416
+ path: string,
417
+ body: TBody,
418
+ options: SSEStreamOptions = {}
419
+ ) => createSSEStreamPost<TEventMap, TBody>(streamURL(path), body, {
420
+ ...options,
421
+ headers: async () => (await streamOptions(options)).headers as Record<string, string>,
422
+ withCredentials: options.withCredentials ?? configRef.value?.withCredentials,
423
+ }),
424
+ }
425
+ return configureApiTransport(transport, config)
426
+ }
427
+
428
+ /**
429
+ * Reconfigure an existing transport without changing its Axios identity.
430
+ * This supports legacy generated `axios` exports while keeping bindings isolated.
431
+ */
432
+ export function configureApiTransport(transport: ApiTransport, config: ApiConfig = {}): ApiTransport {
433
+ const previousConfig = transport.config.value ?? {}
434
+ const resolvedConfig: ApiConfig = {
435
+ ...previousConfig,
436
+ ...config,
437
+ axiosConfig: {
438
+ ...previousConfig.axiosConfig,
439
+ ...config.axiosConfig,
440
+ },
441
+ withCredentials: config.withCredentials
442
+ ?? config.axiosConfig?.withCredentials
443
+ ?? previousConfig.withCredentials
444
+ ?? true,
445
+ }
446
+ transport.config.value = resolvedConfig
447
+ transport.axios.defaults.baseURL = resolvedConfig.baseURL
448
+ transport.axios.defaults.withCredentials = resolvedConfig.withCredentials
449
+ if (resolvedConfig.axiosConfig) {
450
+ Object.assign(transport.axios.defaults, resolvedConfig.axiosConfig)
451
+ }
452
+ transport.axios.interceptors.request.clear()
453
+ transport.axios.interceptors.response.clear()
454
+
455
+ if (resolvedConfig.auth) {
456
+ transport.axios.interceptors.request.use(async (requestConfig) => {
457
+ const token = await resolvedConfig.auth!.getToken?.()
458
+ if (token) {
459
+ requestConfig.headers.Authorization = `${resolvedConfig.auth!.tokenPrefix ?? 'Bearer'} ${token}`
460
+ }
461
+ return requestConfig
462
+ })
463
+ }
464
+ if (resolvedConfig.interceptors?.request) {
465
+ transport.axios.interceptors.request.use(resolvedConfig.interceptors.request)
466
+ }
467
+ transport.axios.interceptors.response.use(
468
+ (response) => {
469
+ transport.lastResponse.value = response
470
+ return resolvedConfig.interceptors?.response?.(response) ?? response
471
+ },
472
+ async (error) => {
473
+ if (error.response?.status === 401) {
474
+ await resolvedConfig.auth?.onUnauthorized?.()
475
+ }
476
+ resolvedConfig.onError?.(parseApiError(error), error)
477
+ throw error
478
+ }
479
+ )
480
+ return transport
481
+ }
482
+
327
483
  /**
328
484
  * Request state for useApiRequest composable
329
485
  */
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { generateFunctions } from './functionGenerator'
3
+
4
+ describe('generateFunctions transport bindings', () => {
5
+ it('preserves direct exports and emits transport-bound client factories', () => {
6
+ const code = generateFunctions({
7
+ '/items': {
8
+ get: {
9
+ responses: {
10
+ '200': {
11
+ description: 'OK',
12
+ content: {
13
+ 'application/json': {
14
+ schema: { type: 'array', items: { type: 'string' } },
15
+ },
16
+ },
17
+ },
18
+ },
19
+ },
20
+ },
21
+ '/events': {
22
+ get: {
23
+ responses: {
24
+ '200': {
25
+ description: 'SSE',
26
+ content: {
27
+ 'text/event-stream': {
28
+ schema: { type: 'string' },
29
+ },
30
+ },
31
+ },
32
+ },
33
+ },
34
+ },
35
+ }, '/api')
36
+
37
+ expect(code).toContain('export function createApiClient(transport: ApiTransport)')
38
+ expect(code).toContain('export const items = defaultBindings.items')
39
+ expect(code).toContain('export const axios = defaultTransport.axios')
40
+ expect(code).toContain('__transport.axios.get')
41
+ expect(code).toContain('__transport.createSSEStream')
42
+ expect(code).toContain('return defaultTransport.getStreamBaseURL()')
43
+ })
44
+ })
@@ -419,8 +419,6 @@ function generateStreamFunction(
419
419
  // const paramNames = allParams ? allParams.match(/\w+(?=\s*[?:])/g) || [] : []
420
420
 
421
421
  const bodyVar = requestBodyPayload || '{}'
422
- const baseUrlRef = 'getStreamBaseURL()'
423
-
424
422
  // Generate type name for this stream
425
423
  const streamTypeName = generateStreamTypeName(path)
426
424
 
@@ -441,12 +439,6 @@ function generateStreamFunction(
441
439
  ? `StreamController<${streamTypeName}>`
442
440
  : 'StreamController'
443
441
 
444
- // Convert formattedPath from string representation to actual path for URL construction
445
- // formattedPath can be either 'path' or `path/${param}` (as strings)
446
- const pathForUrl = formattedPath.startsWith('`')
447
- ? formattedPath.slice(1, -1) // Remove backticks to get the content
448
- : formattedPath.slice(1, -1) // Remove quotes to get the content
449
-
450
442
  const streamParams = allParams ? `${allParams}, options?: SSEStreamOptions` : `options?: SSEStreamOptions`
451
443
  const regularParams = allParams
452
444
 
@@ -464,14 +456,13 @@ function generateStreamFunction(
464
456
  * stream.close()
465
457
  */
466
458
  stream: (${streamParams}): ${typeAnnotation} => {
467
- const url = \`\${${baseUrlRef}}${pathForUrl}\`
468
- return createSSEStreamPost<${streamTypeName}>(url, ${bodyVar}, options)
459
+ return __transport.createSSEStreamPost<${streamTypeName}>(${formattedPath}, ${bodyVar}, options)
469
460
  },
470
461
  /**
471
462
  * Regular POST request (non-streaming)
472
463
  */
473
464
  post: async (${regularParams}): Promise<AxiosResponse<any>> => {
474
- return axios.post(${formattedPath}, ${bodyVar})
465
+ return __transport.axios.post(${formattedPath}, ${bodyVar})
475
466
  }
476
467
  }`
477
468
  } else {
@@ -500,8 +491,8 @@ function generateStreamFunction(
500
491
 
501
492
  // For GET streams with query params, build URL with suffix; otherwise plain URL
502
493
  const urlLine = queryParams?.length
503
- ? `const url = \`\${${baseUrlRef}}${pathForUrl}\` + __qsSuffix`
504
- : `const url = \`\${${baseUrlRef}}${pathForUrl}\``
494
+ ? `const url = ${formattedPath} + __qsSuffix`
495
+ : `const url = ${formattedPath}`
505
496
 
506
497
  const streamBody = queryParams?.length
507
498
  ? `\n ${queryStringCode}\n ${urlLine}`
@@ -520,13 +511,13 @@ function generateStreamFunction(
520
511
  * stream.close()
521
512
  */
522
513
  stream: (${streamParams}): ${typeAnnotation} => {${streamBody}
523
- return createSSEStream<${streamTypeName}>(url, options)
514
+ return __transport.createSSEStream<${streamTypeName}>(url, options)
524
515
  },
525
516
  /**
526
517
  * Regular GET request (non-streaming)
527
518
  */
528
519
  get: async (${regularParams}): Promise<AxiosResponse<any>> => {
529
- return axios.get(${formattedPath}${regularParamStr})
520
+ return __transport.axios.get(${formattedPath}${regularParamStr})
530
521
  }
531
522
  }`
532
523
  }
@@ -574,7 +565,7 @@ function generateAxiosFunction(
574
565
  const formData = new FormData()
575
566
  formData.append('file', file)
576
567
  formData.append('upload', file)
577
- const response = await axios.${method}(${formattedPath}, formData, {
568
+ const response = await __transport.axios.${method}(${formattedPath}, formData, {
578
569
  headers: { 'Content-Type': 'multipart/form-data' },
579
570
  onUploadProgress: options?.onUploadProgress${paramStr ? `, ${paramStr}` : ''}
580
571
  })
@@ -582,7 +573,7 @@ function generateAxiosFunction(
582
573
  } else {
583
574
  // Generic FormData handling
584
575
  axiosFunction += `
585
- const response = await axios.${method}(${formattedPath}, formData, {
576
+ const response = await __transport.axios.${method}(${formattedPath}, formData, {
586
577
  headers: { 'Content-Type': 'multipart/form-data' }${paramStr ? `, ${paramStr}` : ''}
587
578
  })
588
579
  return ${returnExpression}`
@@ -591,7 +582,7 @@ function generateAxiosFunction(
591
582
  const configParams = paramStr ? `, { ${paramStr} }` : ''
592
583
  const bodyVar = requestBodyPayload || '{}'
593
584
  axiosFunction += `
594
- const response = await axios.${method}(${formattedPath}${
585
+ const response = await __transport.axios.${method}(${formattedPath}${
595
586
  ['get', 'delete'].includes(method)
596
587
  ? configParams
597
588
  : `, ${bodyVar}${configParams}`
@@ -847,9 +838,22 @@ function handlePathSegment(
847
838
  * @param baseUrl - The base URL for the API
848
839
  * @returns The generated TypeScript code
849
840
  */
850
- export function generateFunctions(paths: OpenAPIPaths, baseUrl: string): string {
841
+ function generateFunctionsInternal(
842
+ paths: OpenAPIPaths,
843
+ baseUrl: string,
844
+ moduleFactoryName?: string,
845
+ ): string {
846
+ // Generation can run more than once in the same process (for example, one
847
+ // invocation per explicitly owned module). Keep each result independent.
848
+ allTypes.length = 0
849
+ pathOperations.length = 0
850
+ for (const inventory of [functionsInventory, streamEventTypes, streamEventInfo, streamEventSchemas]) {
851
+ for (const key of Object.keys(inventory)) delete inventory[key]
852
+ }
853
+
851
854
  const body: Record<string, any> = {}
852
855
  const allPathsClean = Object.keys(paths).map(cleanPath)
856
+ const httpMethods = new Set(['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'])
853
857
 
854
858
  // Process all paths to generate functions
855
859
  for (const [path, operation] of Object.entries(paths)) {
@@ -859,7 +863,9 @@ export function generateFunctions(paths: OpenAPIPaths, baseUrl: string): string
859
863
  const objFuncKey = toCamelCaseSafe(key)
860
864
  if (!objFuncKey) { return acc }
861
865
 
862
- const methods = Object.keys(operation) as Array<keyof OpenAPIPath>
866
+ const methods = Object.keys(operation)
867
+ .filter(method => httpMethods.has(method)) as Array<keyof OpenAPIPath>
868
+ if (methods.length === 0) return acc
863
869
  const currentPathClean = cleanPath(path)
864
870
  const currentPathPrefix = splitPath.slice(0, index + 1).join('/')
865
871
 
@@ -899,7 +905,7 @@ export function generateFunctions(paths: OpenAPIPaths, baseUrl: string): string
899
905
  }
900
906
 
901
907
  // Generate TypeScript code from processed paths
902
- let tsString = ''
908
+ let tsString = 'function createEndpointNamespaces(__transport: ApiTransport) {\n'
903
909
  const apiObjectEntries: string[] = []
904
910
 
905
911
  // Convert object structure to TypeScript
@@ -908,7 +914,7 @@ export function generateFunctions(paths: OpenAPIPaths, baseUrl: string): string
908
914
  /**
909
915
  * API functions for ${parent} endpoints
910
916
  */
911
- export const ${parent} = ${JSON.stringify(object, undefined, 2)};\n`
917
+ const ${parent} = ${JSON.stringify(object, undefined, 2)};\n`
912
918
  apiObjectEntries.push(parent)
913
919
  }
914
920
 
@@ -917,9 +923,11 @@ export const ${parent} = ${JSON.stringify(object, undefined, 2)};\n`
917
923
  /**
918
924
  * Aggregated API object containing all endpoints
919
925
  */
920
- export const api = {
926
+ const api = {
921
927
  ${apiObjectEntries.map(name => ` ${name},`).join('\n')}
922
- };\n`
928
+ };
929
+ return { api, ${apiObjectEntries.join(', ')} }
930
+ }\n`
923
931
 
924
932
  // Replace placeholders with actual function bodies
925
933
  Object.entries(functionsInventory).forEach(([key, value]) => {
@@ -927,7 +935,22 @@ ${apiObjectEntries.map(name => ` ${name},`).join('\n')}
927
935
  })
928
936
 
929
937
  // Generate the final TypeScript file
930
- return fileTemplate(tsString, allTypes, baseUrl)
938
+ return moduleFactoryName
939
+ ? moduleFileTemplate(tsString, allTypes, moduleFactoryName)
940
+ : fileTemplate(tsString, allTypes, baseUrl, apiObjectEntries)
941
+ }
942
+
943
+ export function generateFunctions(paths: OpenAPIPaths, baseUrl: string): string {
944
+ return generateFunctionsInternal(paths, baseUrl)
945
+ }
946
+
947
+ /** Generate a transport-bound module client without legacy singleton state. */
948
+ export function generateModuleFunctions(
949
+ paths: OpenAPIPaths,
950
+ baseUrl: string,
951
+ moduleFactoryName: string,
952
+ ): string {
953
+ return generateFunctionsInternal(paths, baseUrl, moduleFactoryName)
931
954
  }
932
955
 
933
956
  /**
@@ -1018,26 +1041,23 @@ function generateStreamEventTypeDefinitions(): string {
1018
1041
  function fileTemplate(
1019
1042
  tsString: string,
1020
1043
  typeForImport: string[],
1021
- baseURL: string
1044
+ baseURL: string,
1045
+ apiObjectEntries: string[]
1022
1046
  ): string {
1023
1047
  const streamTypeDefs = generateStreamEventTypeDefinitions()
1024
1048
 
1025
1049
  const hasStreamEndpoints = Object.keys(streamEventTypes).length > 0
1026
1050
 
1027
- // Determine which stream functions are actually used by checking functionsInventory
1028
- const allFunctionBodies = Object.values(functionsInventory).join('\n')
1029
- const hasGetStreams = allFunctionBodies.includes('createSSEStream<')
1030
- const hasPostStreams = allFunctionBodies.includes('createSSEStreamPost<')
1031
-
1032
1051
  const streamImports = hasStreamEndpoints
1033
- ? `${hasPostStreams ? 'createSSEStreamPost, ' : ''}${hasGetStreams ? 'createSSEStream, ' : ''}type StreamController, type SSEStreamOptions,`
1052
+ ? `type StreamController, type SSEStreamOptions,`
1034
1053
  : ''
1035
1054
 
1036
- const templateCode = `import ax from 'axios';
1037
- import type { AxiosResponse } from 'axios';
1055
+ const templateCode = `import type { AxiosResponse } from 'axios';
1038
1056
  import type { ${typeForImport.join(', ')} } from './types.d';
1039
1057
  import {
1040
1058
  ApiResponse,
1059
+ createApiTransport,
1060
+ configureApiTransport,
1041
1061
  parseApiError,
1042
1062
  formatFieldErrors,
1043
1063
  useApiRequest,
@@ -1052,9 +1072,10 @@ import {
1052
1072
  type ApiRequestState,
1053
1073
  type RequestInterceptor,
1054
1074
  type ResponseInterceptor,
1075
+ type ApiTransport,
1055
1076
  type Unwrapped
1056
1077
  } from '@bagelink/sdk';
1057
- import { ref, type Ref } from 'vue';
1078
+ import type { Ref } from 'vue';
1058
1079
 
1059
1080
  /**
1060
1081
  * Options for file upload operations
@@ -1070,22 +1091,29 @@ export interface UploadOptions {
1070
1091
 
1071
1092
  ${streamTypeDefs}
1072
1093
 
1073
- /**
1074
- * Configured axios instance for API requests
1075
- */
1076
- export const axios = ax.create({baseURL: '${baseURL}', withCredentials: true, paramsSerializer: { indexes: null }});
1094
+ const defaultTransport = createApiTransport({ baseURL: '${baseURL}' })
1077
1095
 
1078
1096
  ${tsString}
1079
1097
 
1098
+ const defaultBindings = createEndpointNamespaces(defaultTransport)
1099
+ export const api = defaultBindings.api
1100
+ ${apiObjectEntries.map(name => `export const ${name} = defaultBindings.${name}`).join('\n')}
1101
+ export const axios = defaultTransport.axios
1102
+
1103
+ /** Bind all generated endpoint namespaces to an isolated transport. */
1104
+ export function createApiClient(transport: ApiTransport) {
1105
+ return createEndpointNamespaces(transport)
1106
+ }
1107
+
1108
+ /** Resolve the default client's SSE base URL. */
1109
+ export function getStreamBaseURL(): string {
1110
+ return defaultTransport.getStreamBaseURL()
1111
+ }
1112
+
1080
1113
  // ============================================================================
1081
1114
  // API Instance & Configuration
1082
1115
  // ============================================================================
1083
1116
 
1084
- /**
1085
- * Last response metadata (accessible via useApi().$lastResponse)
1086
- */
1087
- const lastResponse = ref<AxiosResponse | null>(null)
1088
-
1089
1117
  /**
1090
1118
  * API instance type with enhanced response objects
1091
1119
  */
@@ -1099,13 +1127,6 @@ type ApiInstance = typeof api & {
1099
1127
 
1100
1128
  // Global state
1101
1129
  let apiInstance: ApiInstance | null = null
1102
- let apiConfig = ref<ApiConfig | null>(null)
1103
- let _streamBaseURL: string | undefined
1104
-
1105
- /** Resolve base URL for SSE stream endpoints. Uses streamBaseURL if set, otherwise falls back to axios baseURL. */
1106
- export function getStreamBaseURL(): string {
1107
- return _streamBaseURL || axios.defaults.baseURL || ""
1108
- }
1109
1130
 
1110
1131
  /**
1111
1132
  * Create and configure the API instance
@@ -1113,77 +1134,17 @@ export function getStreamBaseURL(): string {
1113
1134
  * @returns Configured API instance
1114
1135
  */
1115
1136
  export function createApi(config: ApiConfig = {}): ApiInstance {
1116
- // Store config for access via useApi().$config
1117
- apiConfig.value = config
1118
-
1119
- // Configure axios defaults
1120
- if (config.baseURL) {
1121
- axios.defaults.baseURL = config.baseURL
1122
- }
1123
- _streamBaseURL = config.streamBaseURL
1124
- if (config.withCredentials !== undefined) {
1125
- axios.defaults.withCredentials = config.withCredentials
1126
- }
1127
- if (config.axiosConfig) {
1128
- Object.assign(axios.defaults, config.axiosConfig)
1129
- }
1130
-
1131
- // Clear existing interceptors
1132
- axios.interceptors.request.clear()
1133
- axios.interceptors.response.clear()
1134
-
1135
- // Setup auth interceptor
1136
- if (config.auth) {
1137
- axios.interceptors.request.use(async (reqConfig) => {
1138
- const token = await config.auth!.getToken?.()
1139
- if (token) {
1140
- const prefix = config.auth!.tokenPrefix ?? 'Bearer'
1141
- reqConfig.headers.Authorization = \`\${prefix} \${token}\`
1142
- }
1143
- return reqConfig
1144
- })
1145
- }
1146
-
1147
- // Setup custom request interceptor
1148
- if (config.interceptors?.request) {
1149
- axios.interceptors.request.use(config.interceptors.request)
1150
- }
1151
-
1152
- // Setup response interceptor for error handling
1153
- axios.interceptors.response.use(
1154
- (response) => {
1155
- // Store last response for metadata access
1156
- lastResponse.value = response
1157
-
1158
- // Apply custom response interceptor if provided
1159
- if (config.interceptors?.response) {
1160
- return config.interceptors.response(response)
1161
- }
1162
- return response
1163
- },
1164
- async (error) => {
1165
- // Handle 401 Unauthorized
1166
- if (error.response?.status === 401) {
1167
- await config.auth?.onUnauthorized?.()
1168
- }
1169
-
1170
- // Parse and handle error
1171
- const parsedError = parseApiError(error)
1172
- config.onError?.(parsedError, error)
1173
-
1174
- throw error
1175
- }
1176
- )
1137
+ configureApiTransport(defaultTransport, config)
1177
1138
 
1178
1139
  // API already returns ApiResponse directly!
1179
1140
  // Just add the special properties
1180
1141
  apiInstance = {
1181
1142
  ...api,
1182
1143
  $raw: api,
1183
- $lastResponse: lastResponse,
1144
+ $lastResponse: defaultTransport.lastResponse,
1184
1145
  $axios: axios,
1185
1146
  $ApiResponse: ApiResponse,
1186
- $config: apiConfig
1147
+ $config: defaultTransport.config
1187
1148
  } as ApiInstance
1188
1149
 
1189
1150
  return apiInstance
@@ -1232,3 +1193,43 @@ export {
1232
1193
  // Remove potential double quotes around property names
1233
1194
  return templateCode.replace(/"([^"]+)":/g, '$1:')
1234
1195
  }
1196
+
1197
+ function moduleFileTemplate(
1198
+ tsString: string,
1199
+ typeForImport: string[],
1200
+ moduleFactoryName: string,
1201
+ ): string {
1202
+ const streamTypeDefs = generateStreamEventTypeDefinitions()
1203
+ const hasStreamEndpoints = Object.keys(streamEventTypes).length > 0
1204
+ const streamImports = hasStreamEndpoints
1205
+ ? 'type StreamController, type SSEStreamOptions,'
1206
+ : ''
1207
+ const typeImport = typeForImport.length
1208
+ ? `import type { ${typeForImport.join(', ')} } from './types.d';`
1209
+ : ''
1210
+
1211
+ const templateCode = `${typeImport}
1212
+ import {
1213
+ \tApiResponse,
1214
+ \t${streamImports}
1215
+ \ttype ApiTransport
1216
+ } from '@bagelink/sdk';
1217
+
1218
+ export interface UploadOptions {
1219
+ \tonUploadProgress?: (progressEvent: any) => void
1220
+ \tdirPath?: string
1221
+ \ttags?: string[]
1222
+ }
1223
+
1224
+ ${streamTypeDefs}
1225
+
1226
+ ${tsString}
1227
+
1228
+ /** Bind this module's generated endpoint namespaces to the app transport. */
1229
+ export function ${moduleFactoryName}(transport: ApiTransport) {
1230
+ \treturn createEndpointNamespaces(transport)
1231
+ }
1232
+ `
1233
+
1234
+ return templateCode.replace(/"([^"]+)":/g, '$1:')
1235
+ }
@@ -37,4 +37,5 @@ export default async (openApiUrl: string, baseUrl: string): Promise<OpenAPIRespo
37
37
 
38
38
  export * from './streamClient'
39
39
  export * from './streamDetector'
40
+ export * from './moduleGenerator'
40
41
  export * from './types'