@alagoni97/cli 0.72.1
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/LICENSE +201 -0
- package/README.md +336 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.mjs +3 -0
- package/bin/run.cmd +3 -0
- package/bin/run.mjs +4 -0
- package/dist/LoggingInterface.d.ts +123 -0
- package/dist/LoggingInterface.js +300 -0
- package/dist/PersistedConfig.d.ts +46 -0
- package/dist/PersistedConfig.js +94 -0
- package/dist/browser/adapters/output.d.ts +69 -0
- package/dist/browser/adapters/output.js +91 -0
- package/dist/browser/config.d.ts +18 -0
- package/dist/browser/config.js +59 -0
- package/dist/browser/generate.d.ts +20 -0
- package/dist/browser/generate.js +129 -0
- package/dist/browser/index.d.ts +17 -0
- package/dist/browser/index.js +26 -0
- package/dist/browser/parser.d.ts +9 -0
- package/dist/browser/parser.js +61 -0
- package/dist/browser/shims/asyncapi-parser.d.ts +30 -0
- package/dist/browser/shims/asyncapi-parser.js +52 -0
- package/dist/browser/shims/fs.d.ts +190 -0
- package/dist/browser/shims/fs.js +188 -0
- package/dist/browser/shims/json-schema-ref-parser.d.ts +73 -0
- package/dist/browser/shims/json-schema-ref-parser.js +246 -0
- package/dist/browser/shims/swagger-parser.d.ts +20 -0
- package/dist/browser/shims/swagger-parser.js +40 -0
- package/dist/codegen/configurationSchemaBuilder.d.ts +15 -0
- package/dist/codegen/configurationSchemaBuilder.js +48 -0
- package/dist/codegen/configurations.d.ts +29 -0
- package/dist/codegen/configurations.js +260 -0
- package/dist/codegen/detection.d.ts +19 -0
- package/dist/codegen/detection.js +174 -0
- package/dist/codegen/errors.d.ts +111 -0
- package/dist/codegen/errors.js +352 -0
- package/dist/codegen/generators/generic/custom.d.ts +75 -0
- package/dist/codegen/generators/generic/custom.js +66 -0
- package/dist/codegen/generators/index.d.ts +14 -0
- package/dist/codegen/generators/index.js +42 -0
- package/dist/codegen/generators/typescript/channels/asyncapi.d.ts +10 -0
- package/dist/codegen/generators/typescript/channels/asyncapi.js +131 -0
- package/dist/codegen/generators/typescript/channels/index.d.ts +8 -0
- package/dist/codegen/generators/typescript/channels/index.js +142 -0
- package/dist/codegen/generators/typescript/channels/openapi.d.ts +9 -0
- package/dist/codegen/generators/typescript/channels/openapi.js +168 -0
- package/dist/codegen/generators/typescript/channels/protocols/amqp/index.d.ts +7 -0
- package/dist/codegen/generators/typescript/channels/protocols/amqp/index.js +149 -0
- package/dist/codegen/generators/typescript/channels/protocols/amqp/publishExchange.d.ts +5 -0
- package/dist/codegen/generators/typescript/channels/protocols/amqp/publishExchange.js +106 -0
- package/dist/codegen/generators/typescript/channels/protocols/amqp/publishQueue.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/amqp/publishQueue.js +102 -0
- package/dist/codegen/generators/typescript/channels/protocols/amqp/subscribeQueue.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/amqp/subscribeQueue.js +133 -0
- package/dist/codegen/generators/typescript/channels/protocols/eventsource/express.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/eventsource/express.js +100 -0
- package/dist/codegen/generators/typescript/channels/protocols/eventsource/fetch.d.ts +5 -0
- package/dist/codegen/generators/typescript/channels/protocols/eventsource/fetch.js +144 -0
- package/dist/codegen/generators/typescript/channels/protocols/eventsource/index.d.ts +6 -0
- package/dist/codegen/generators/typescript/channels/protocols/eventsource/index.js +132 -0
- package/dist/codegen/generators/typescript/channels/protocols/http/client.d.ts +10 -0
- package/dist/codegen/generators/typescript/channels/protocols/http/client.js +224 -0
- package/dist/codegen/generators/typescript/channels/protocols/http/common-types.d.ts +14 -0
- package/dist/codegen/generators/typescript/channels/protocols/http/common-types.js +800 -0
- package/dist/codegen/generators/typescript/channels/protocols/http/index.d.ts +8 -0
- package/dist/codegen/generators/typescript/channels/protocols/http/index.js +96 -0
- package/dist/codegen/generators/typescript/channels/protocols/http/security.d.ts +48 -0
- package/dist/codegen/generators/typescript/channels/protocols/http/security.js +491 -0
- package/dist/codegen/generators/typescript/channels/protocols/kafka/index.d.ts +6 -0
- package/dist/codegen/generators/typescript/channels/protocols/kafka/index.js +127 -0
- package/dist/codegen/generators/typescript/channels/protocols/kafka/publish.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/kafka/publish.js +108 -0
- package/dist/codegen/generators/typescript/channels/protocols/kafka/subscribe.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/kafka/subscribe.js +154 -0
- package/dist/codegen/generators/typescript/channels/protocols/kafka/utils.d.ts +11 -0
- package/dist/codegen/generators/typescript/channels/protocols/kafka/utils.js +58 -0
- package/dist/codegen/generators/typescript/channels/protocols/mqtt/index.d.ts +6 -0
- package/dist/codegen/generators/typescript/channels/protocols/mqtt/index.js +113 -0
- package/dist/codegen/generators/typescript/channels/protocols/mqtt/publish.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/mqtt/publish.js +99 -0
- package/dist/codegen/generators/typescript/channels/protocols/mqtt/subscribe.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/mqtt/subscribe.js +186 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/corePublish.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/corePublish.js +90 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/coreReply.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/coreReply.js +142 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/coreRequest.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/coreRequest.js +100 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/coreSubscribe.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/coreSubscribe.js +143 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/index.d.ts +11 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/index.js +224 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/jetstreamPublish.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/jetstreamPublish.js +89 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/jetstreamPullSubscribe.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/jetstreamPullSubscribe.js +146 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/jetstreamPushSubscription.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/jetstreamPushSubscription.js +149 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/utils.d.ts +35 -0
- package/dist/codegen/generators/typescript/channels/protocols/nats/utils.js +120 -0
- package/dist/codegen/generators/typescript/channels/protocols/websocket/index.d.ts +7 -0
- package/dist/codegen/generators/typescript/channels/protocols/websocket/index.js +152 -0
- package/dist/codegen/generators/typescript/channels/protocols/websocket/publish.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/websocket/publish.js +64 -0
- package/dist/codegen/generators/typescript/channels/protocols/websocket/register.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/websocket/register.js +115 -0
- package/dist/codegen/generators/typescript/channels/protocols/websocket/subscribe.d.ts +3 -0
- package/dist/codegen/generators/typescript/channels/protocols/websocket/subscribe.js +151 -0
- package/dist/codegen/generators/typescript/channels/types.d.ts +181 -0
- package/dist/codegen/generators/typescript/channels/types.js +131 -0
- package/dist/codegen/generators/typescript/channels/utils.d.ts +64 -0
- package/dist/codegen/generators/typescript/channels/utils.js +163 -0
- package/dist/codegen/generators/typescript/client/index.d.ts +8 -0
- package/dist/codegen/generators/typescript/client/index.js +61 -0
- package/dist/codegen/generators/typescript/client/protocols/nats/corePublish.d.ts +6 -0
- package/dist/codegen/generators/typescript/client/protocols/nats/corePublish.js +53 -0
- package/dist/codegen/generators/typescript/client/protocols/nats/coreSubscribe.d.ts +6 -0
- package/dist/codegen/generators/typescript/client/protocols/nats/coreSubscribe.js +90 -0
- package/dist/codegen/generators/typescript/client/protocols/nats/jetStreamPullSubscription.d.ts +6 -0
- package/dist/codegen/generators/typescript/client/protocols/nats/jetStreamPullSubscription.js +83 -0
- package/dist/codegen/generators/typescript/client/protocols/nats/jetstreamPublish.d.ts +6 -0
- package/dist/codegen/generators/typescript/client/protocols/nats/jetstreamPublish.js +54 -0
- package/dist/codegen/generators/typescript/client/protocols/nats/jetstreamPushSubscription.d.ts +6 -0
- package/dist/codegen/generators/typescript/client/protocols/nats/jetstreamPushSubscription.js +82 -0
- package/dist/codegen/generators/typescript/client/protocols/nats.d.ts +2 -0
- package/dist/codegen/generators/typescript/client/protocols/nats.js +182 -0
- package/dist/codegen/generators/typescript/client/types.d.ts +49 -0
- package/dist/codegen/generators/typescript/client/types.js +37 -0
- package/dist/codegen/generators/typescript/headers.d.ts +54 -0
- package/dist/codegen/generators/typescript/headers.js +138 -0
- package/dist/codegen/generators/typescript/index.d.ts +7 -0
- package/dist/codegen/generators/typescript/index.js +38 -0
- package/dist/codegen/generators/typescript/models.d.ts +21761 -0
- package/dist/codegen/generators/typescript/models.js +64 -0
- package/dist/codegen/generators/typescript/parameters.d.ts +37 -0
- package/dist/codegen/generators/typescript/parameters.js +101 -0
- package/dist/codegen/generators/typescript/payloads.d.ts +73 -0
- package/dist/codegen/generators/typescript/payloads.js +250 -0
- package/dist/codegen/generators/typescript/types.d.ts +34 -0
- package/dist/codegen/generators/typescript/types.js +70 -0
- package/dist/codegen/generators/typescript/utils.d.ts +38 -0
- package/dist/codegen/generators/typescript/utils.js +195 -0
- package/dist/codegen/index.d.ts +7 -0
- package/dist/codegen/index.js +38 -0
- package/dist/codegen/inputs/asyncapi/generators/headers.d.ts +3 -0
- package/dist/codegen/inputs/asyncapi/generators/headers.js +41 -0
- package/dist/codegen/inputs/asyncapi/generators/parameters.d.ts +14 -0
- package/dist/codegen/inputs/asyncapi/generators/parameters.js +120 -0
- package/dist/codegen/inputs/asyncapi/generators/payloads.d.ts +16 -0
- package/dist/codegen/inputs/asyncapi/generators/payloads.js +124 -0
- package/dist/codegen/inputs/asyncapi/generators/types.d.ts +9 -0
- package/dist/codegen/inputs/asyncapi/generators/types.js +61 -0
- package/dist/codegen/inputs/asyncapi/index.d.ts +1 -0
- package/dist/codegen/inputs/asyncapi/index.js +7 -0
- package/dist/codegen/inputs/asyncapi/parser.d.ts +4 -0
- package/dist/codegen/inputs/asyncapi/parser.js +90 -0
- package/dist/codegen/inputs/index.d.ts +3 -0
- package/dist/codegen/inputs/index.js +19 -0
- package/dist/codegen/inputs/jsonschema/generators/index.d.ts +1 -0
- package/dist/codegen/inputs/jsonschema/generators/index.js +5 -0
- package/dist/codegen/inputs/jsonschema/generators/models.d.ts +7 -0
- package/dist/codegen/inputs/jsonschema/generators/models.js +26 -0
- package/dist/codegen/inputs/jsonschema/index.d.ts +1 -0
- package/dist/codegen/inputs/jsonschema/index.js +7 -0
- package/dist/codegen/inputs/jsonschema/parser.d.ts +31 -0
- package/dist/codegen/inputs/jsonschema/parser.js +128 -0
- package/dist/codegen/inputs/openapi/generators/headers.d.ts +3 -0
- package/dist/codegen/inputs/openapi/generators/headers.js +101 -0
- package/dist/codegen/inputs/openapi/generators/parameters.d.ts +10 -0
- package/dist/codegen/inputs/openapi/generators/parameters.js +852 -0
- package/dist/codegen/inputs/openapi/generators/payloads.d.ts +3 -0
- package/dist/codegen/inputs/openapi/generators/payloads.js +231 -0
- package/dist/codegen/inputs/openapi/generators/types.d.ts +9 -0
- package/dist/codegen/inputs/openapi/generators/types.js +86 -0
- package/dist/codegen/inputs/openapi/index.d.ts +2 -0
- package/dist/codegen/inputs/openapi/index.js +8 -0
- package/dist/codegen/inputs/openapi/parser.d.ts +4 -0
- package/dist/codegen/inputs/openapi/parser.js +115 -0
- package/dist/codegen/inputs/openapi/security.d.ts +56 -0
- package/dist/codegen/inputs/openapi/security.js +193 -0
- package/dist/codegen/modelina/index.d.ts +2 -0
- package/dist/codegen/modelina/index.js +20 -0
- package/dist/codegen/modelina/presets/index.d.ts +3 -0
- package/dist/codegen/modelina/presets/index.js +14 -0
- package/dist/codegen/modelina/presets/primitives.d.ts +34 -0
- package/dist/codegen/modelina/presets/primitives.js +211 -0
- package/dist/codegen/modelina/presets/union.d.ts +33 -0
- package/dist/codegen/modelina/presets/union.js +197 -0
- package/dist/codegen/modelina/presets/validation.d.ts +66 -0
- package/dist/codegen/modelina/presets/validation.js +183 -0
- package/dist/codegen/modelina/types.d.ts +61845 -0
- package/dist/codegen/modelina/types.js +412 -0
- package/dist/codegen/output/filesystem.d.ts +30 -0
- package/dist/codegen/output/filesystem.js +61 -0
- package/dist/codegen/output/index.d.ts +16 -0
- package/dist/codegen/output/index.js +57 -0
- package/dist/codegen/output/memory.d.ts +54 -0
- package/dist/codegen/output/memory.js +81 -0
- package/dist/codegen/output/modelina.d.ts +30 -0
- package/dist/codegen/output/modelina.js +30 -0
- package/dist/codegen/output/types.d.ts +47 -0
- package/dist/codegen/output/types.js +6 -0
- package/dist/codegen/renderer.d.ts +13 -0
- package/dist/codegen/renderer.js +309 -0
- package/dist/codegen/schemaPostProcess.d.ts +25 -0
- package/dist/codegen/schemaPostProcess.js +38 -0
- package/dist/codegen/types.d.ts +285187 -0
- package/dist/codegen/types.js +177 -0
- package/dist/codegen/utils.d.ts +126 -0
- package/dist/codegen/utils.js +373 -0
- package/dist/commands/base.d.ts +18 -0
- package/dist/commands/base.js +59 -0
- package/dist/commands/generate.d.ts +46 -0
- package/dist/commands/generate.js +305 -0
- package/dist/commands/init.d.ts +55 -0
- package/dist/commands/init.js +544 -0
- package/dist/commands/telemetry.d.ts +25 -0
- package/dist/commands/telemetry.js +115 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +35 -0
- package/dist/telemetry/anonymize.d.ts +19 -0
- package/dist/telemetry/anonymize.js +73 -0
- package/dist/telemetry/collector.d.ts +26 -0
- package/dist/telemetry/collector.js +57 -0
- package/dist/telemetry/config.d.ts +32 -0
- package/dist/telemetry/config.js +97 -0
- package/dist/telemetry/events.d.ts +72 -0
- package/dist/telemetry/events.js +5 -0
- package/dist/telemetry/index.d.ts +46 -0
- package/dist/telemetry/index.js +91 -0
- package/dist/telemetry/notice.d.ts +8 -0
- package/dist/telemetry/notice.js +50 -0
- package/dist/telemetry/sender.d.ts +14 -0
- package/dist/telemetry/sender.js +125 -0
- package/dist/utils/gitignore.d.ts +66 -0
- package/dist/utils/gitignore.js +111 -0
- package/dist/utils/inputSource.d.ts +3 -0
- package/dist/utils/inputSource.js +26 -0
- package/dist/utils/refResolvers.d.ts +29 -0
- package/dist/utils/refResolvers.js +83 -0
- package/dist/utils/remoteFetch.d.ts +18 -0
- package/dist/utils/remoteFetch.js +64 -0
- package/oclif.manifest.json +510 -0
- package/package.json +155 -0
|
@@ -0,0 +1,800 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderHttpCommonTypes = void 0;
|
|
4
|
+
const security_1 = require("./security");
|
|
5
|
+
/**
|
|
6
|
+
* Generates common types and helper functions shared across all HTTP client functions.
|
|
7
|
+
* This should be called once per protocol generation to avoid code duplication.
|
|
8
|
+
*
|
|
9
|
+
* @param securitySchemes - Optional security schemes extracted from OpenAPI.
|
|
10
|
+
* When provided, only relevant auth types are generated.
|
|
11
|
+
* When undefined/empty, all auth types are generated for backward compatibility.
|
|
12
|
+
*/
|
|
13
|
+
function renderHttpCommonTypes(securitySchemes) {
|
|
14
|
+
const requirements = (0, security_1.analyzeSecuritySchemes)(securitySchemes);
|
|
15
|
+
const securityTypes = (0, security_1.renderSecurityTypes)(securitySchemes, requirements);
|
|
16
|
+
return `// ============================================================================
|
|
17
|
+
// Common Types - Shared across all HTTP client functions
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Standard HTTP response interface that wraps fetch-like responses
|
|
22
|
+
*/
|
|
23
|
+
export interface HttpResponse {
|
|
24
|
+
ok: boolean;
|
|
25
|
+
status: number;
|
|
26
|
+
statusText: string;
|
|
27
|
+
headers?: Headers | Record<string, string>;
|
|
28
|
+
json: () => Record<any, any> | Promise<Record<any, any>>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Pagination info extracted from response
|
|
33
|
+
*/
|
|
34
|
+
export interface PaginationInfo {
|
|
35
|
+
/** Total number of items (if available from headers like X-Total-Count) */
|
|
36
|
+
totalCount?: number;
|
|
37
|
+
/** Total number of pages (if available) */
|
|
38
|
+
totalPages?: number;
|
|
39
|
+
/** Current page/offset */
|
|
40
|
+
currentOffset?: number;
|
|
41
|
+
/** Items per page */
|
|
42
|
+
limit?: number;
|
|
43
|
+
/** Next cursor (for cursor-based pagination) */
|
|
44
|
+
nextCursor?: string;
|
|
45
|
+
/** Previous cursor */
|
|
46
|
+
prevCursor?: string;
|
|
47
|
+
/** Whether there are more items */
|
|
48
|
+
hasMore?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Rich response wrapper returned by HTTP client functions
|
|
53
|
+
*/
|
|
54
|
+
export interface HttpClientResponse<T> {
|
|
55
|
+
/** The deserialized response payload */
|
|
56
|
+
data: T;
|
|
57
|
+
/** HTTP status code */
|
|
58
|
+
status: number;
|
|
59
|
+
/** HTTP status text */
|
|
60
|
+
statusText: string;
|
|
61
|
+
/** Response headers */
|
|
62
|
+
headers: Record<string, string>;
|
|
63
|
+
/** Raw JSON response before deserialization */
|
|
64
|
+
rawData: Record<string, any>;
|
|
65
|
+
/** Pagination info extracted from response (if applicable) */
|
|
66
|
+
pagination?: PaginationInfo;
|
|
67
|
+
/** Fetch the next page (if pagination is configured and more data exists) */
|
|
68
|
+
getNextPage?: () => Promise<HttpClientResponse<T>>;
|
|
69
|
+
/** Fetch the previous page (if pagination is configured) */
|
|
70
|
+
getPrevPage?: () => Promise<HttpClientResponse<T>>;
|
|
71
|
+
/** Check if there's a next page */
|
|
72
|
+
hasNextPage?: () => boolean;
|
|
73
|
+
/** Check if there's a previous page */
|
|
74
|
+
hasPrevPage?: () => boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* HTTP request parameters passed to the request hook
|
|
79
|
+
*/
|
|
80
|
+
export interface HttpRequestParams {
|
|
81
|
+
url: string;
|
|
82
|
+
headers?: Record<string, string | string[]>;
|
|
83
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
|
|
84
|
+
credentials?: RequestCredentials;
|
|
85
|
+
body?: any;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Token response structure for OAuth2 flows
|
|
90
|
+
*/
|
|
91
|
+
export interface TokenResponse {
|
|
92
|
+
accessToken: string;
|
|
93
|
+
refreshToken?: string;
|
|
94
|
+
expiresIn?: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
${securityTypes}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Feature flags indicating which auth types are available.
|
|
101
|
+
* Used internally to conditionally call auth-specific helpers.
|
|
102
|
+
*/
|
|
103
|
+
const AUTH_FEATURES = {
|
|
104
|
+
oauth2: ${requirements.oauth2}
|
|
105
|
+
} as const;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Default values for API key authentication derived from the spec.
|
|
109
|
+
* These match the defaults documented in the ApiKeyAuth interface.
|
|
110
|
+
*/
|
|
111
|
+
const API_KEY_DEFAULTS = {
|
|
112
|
+
name: '${(0, security_1.escapeStringForCodeGen)((0, security_1.getApiKeyDefaults)(requirements.apiKeySchemes).name)}',
|
|
113
|
+
in: '${(0, security_1.escapeStringForCodeGen)((0, security_1.getApiKeyDefaults)(requirements.apiKeySchemes).in)}' as 'header' | 'query' | 'cookie'
|
|
114
|
+
} as const;
|
|
115
|
+
|
|
116
|
+
// ============================================================================
|
|
117
|
+
// Pagination Types
|
|
118
|
+
// ============================================================================
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Where to place pagination parameters
|
|
122
|
+
*/
|
|
123
|
+
export type PaginationLocation = 'query' | 'header';
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Offset-based pagination configuration
|
|
127
|
+
*/
|
|
128
|
+
export interface OffsetPagination {
|
|
129
|
+
type: 'offset';
|
|
130
|
+
in?: PaginationLocation; // Where to place params (default: 'query')
|
|
131
|
+
offset: number;
|
|
132
|
+
limit: number;
|
|
133
|
+
offsetParam?: string; // Param name for offset (default: 'offset' for query, 'X-Offset' for header)
|
|
134
|
+
limitParam?: string; // Param name for limit (default: 'limit' for query, 'X-Limit' for header)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Cursor-based pagination configuration
|
|
139
|
+
*/
|
|
140
|
+
export interface CursorPagination {
|
|
141
|
+
type: 'cursor';
|
|
142
|
+
in?: PaginationLocation; // Where to place params (default: 'query')
|
|
143
|
+
cursor?: string;
|
|
144
|
+
limit?: number;
|
|
145
|
+
cursorParam?: string; // Param name for cursor (default: 'cursor' for query, 'X-Cursor' for header)
|
|
146
|
+
limitParam?: string; // Param name for limit (default: 'limit' for query, 'X-Limit' for header)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Page-based pagination configuration
|
|
151
|
+
*/
|
|
152
|
+
export interface PagePagination {
|
|
153
|
+
type: 'page';
|
|
154
|
+
in?: PaginationLocation; // Where to place params (default: 'query')
|
|
155
|
+
page: number;
|
|
156
|
+
pageSize: number;
|
|
157
|
+
pageParam?: string; // Param name for page (default: 'page' for query, 'X-Page' for header)
|
|
158
|
+
pageSizeParam?: string; // Param name for page size (default: 'pageSize' for query, 'X-Page-Size' for header)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Range-based pagination (typically used with headers)
|
|
163
|
+
* Follows RFC 7233 style: Range: items=0-24
|
|
164
|
+
*/
|
|
165
|
+
export interface RangePagination {
|
|
166
|
+
type: 'range';
|
|
167
|
+
in?: 'header'; // Range pagination is typically header-only
|
|
168
|
+
start: number;
|
|
169
|
+
end: number;
|
|
170
|
+
unit?: string; // Range unit (default: 'items')
|
|
171
|
+
rangeHeader?: string; // Header name (default: 'Range')
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Union type for all pagination methods
|
|
176
|
+
*/
|
|
177
|
+
export type PaginationConfig = OffsetPagination | CursorPagination | PagePagination | RangePagination;
|
|
178
|
+
|
|
179
|
+
// ============================================================================
|
|
180
|
+
// Retry Configuration
|
|
181
|
+
// ============================================================================
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Retry policy configuration for failed requests
|
|
185
|
+
*/
|
|
186
|
+
export interface RetryConfig {
|
|
187
|
+
maxRetries?: number; // Maximum number of retry attempts (default: 3)
|
|
188
|
+
initialDelayMs?: number; // Initial delay before first retry (default: 1000)
|
|
189
|
+
maxDelayMs?: number; // Maximum delay between retries (default: 30000)
|
|
190
|
+
backoffMultiplier?: number; // Multiplier for exponential backoff (default: 2)
|
|
191
|
+
retryableStatusCodes?: number[]; // Status codes to retry (default: [408, 429, 500, 502, 503, 504])
|
|
192
|
+
retryOnNetworkError?: boolean; // Retry on network errors (default: true)
|
|
193
|
+
onRetry?: (attempt: number, delay: number, error: Error) => void; // Callback on each retry
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ============================================================================
|
|
197
|
+
// Hooks Configuration - Extensible callback system
|
|
198
|
+
// ============================================================================
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Hooks for customizing HTTP client behavior
|
|
202
|
+
*/
|
|
203
|
+
export interface HttpHooks {
|
|
204
|
+
/**
|
|
205
|
+
* Called before each request to transform/modify the request parameters
|
|
206
|
+
* Return modified params or undefined to use original
|
|
207
|
+
*/
|
|
208
|
+
beforeRequest?: (params: HttpRequestParams) => HttpRequestParams | Promise<HttpRequestParams>;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The actual request implementation - allows swapping fetch for axios, etc.
|
|
212
|
+
* Default: uses node-fetch
|
|
213
|
+
*/
|
|
214
|
+
makeRequest?: (params: HttpRequestParams) => Promise<HttpResponse>;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Called after each response for logging, metrics, etc.
|
|
218
|
+
* Can transform the response before it's processed
|
|
219
|
+
*/
|
|
220
|
+
afterResponse?: (response: HttpResponse, params: HttpRequestParams) => HttpResponse | Promise<HttpResponse>;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Called on request error for logging, error transformation, etc.
|
|
224
|
+
*/
|
|
225
|
+
onError?: (error: Error, params: HttpRequestParams) => Error | Promise<Error>;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ============================================================================
|
|
229
|
+
// Common Request Context
|
|
230
|
+
// ============================================================================
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Base context shared by all HTTP client functions
|
|
234
|
+
*/
|
|
235
|
+
export interface HttpClientContext {
|
|
236
|
+
server?: string;
|
|
237
|
+
path?: string;
|
|
238
|
+
|
|
239
|
+
// Authentication - grouped for better autocomplete
|
|
240
|
+
auth?: AuthConfig;
|
|
241
|
+
|
|
242
|
+
// Pagination configuration
|
|
243
|
+
pagination?: PaginationConfig;
|
|
244
|
+
|
|
245
|
+
// Retry configuration
|
|
246
|
+
retry?: RetryConfig;
|
|
247
|
+
|
|
248
|
+
// Hooks for extensibility
|
|
249
|
+
hooks?: HttpHooks;
|
|
250
|
+
|
|
251
|
+
// Additional options
|
|
252
|
+
additionalHeaders?: Record<string, string | string[]>;
|
|
253
|
+
|
|
254
|
+
// Query parameters
|
|
255
|
+
queryParams?: Record<string, string | number | boolean | undefined>;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ============================================================================
|
|
259
|
+
// Helper Functions - Shared logic extracted for reuse
|
|
260
|
+
// ============================================================================
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Default retry configuration
|
|
264
|
+
*/
|
|
265
|
+
const DEFAULT_RETRY_CONFIG: Required<RetryConfig> = {
|
|
266
|
+
maxRetries: 3,
|
|
267
|
+
initialDelayMs: 1000,
|
|
268
|
+
maxDelayMs: 30000,
|
|
269
|
+
backoffMultiplier: 2,
|
|
270
|
+
retryableStatusCodes: [408, 429, 500, 502, 503, 504],
|
|
271
|
+
retryOnNetworkError: true,
|
|
272
|
+
onRetry: () => {},
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Default request hook implementation using node-fetch
|
|
277
|
+
*/
|
|
278
|
+
const defaultMakeRequest = async (params: HttpRequestParams): Promise<HttpResponse> => {
|
|
279
|
+
return NodeFetch.default(params.url, {
|
|
280
|
+
body: params.body,
|
|
281
|
+
method: params.method,
|
|
282
|
+
headers: params.headers
|
|
283
|
+
}) as unknown as HttpResponse;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Apply authentication to headers and URL based on auth config
|
|
288
|
+
*/
|
|
289
|
+
function applyAuth(
|
|
290
|
+
auth: AuthConfig | undefined,
|
|
291
|
+
headers: Record<string, string | string[]>,
|
|
292
|
+
url: string
|
|
293
|
+
): { headers: Record<string, string | string[]>; url: string } {
|
|
294
|
+
if (!auth) return { headers, url };
|
|
295
|
+
|
|
296
|
+
switch (auth.type) {
|
|
297
|
+
case 'bearer':
|
|
298
|
+
headers['Authorization'] = \`Bearer \${auth.token}\`;
|
|
299
|
+
break;
|
|
300
|
+
|
|
301
|
+
case 'basic': {
|
|
302
|
+
const credentials = Buffer.from(\`\${auth.username}:\${auth.password}\`).toString('base64');
|
|
303
|
+
headers['Authorization'] = \`Basic \${credentials}\`;
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
case 'apiKey': {
|
|
308
|
+
const keyName = auth.name ?? API_KEY_DEFAULTS.name;
|
|
309
|
+
const keyIn = auth.in ?? API_KEY_DEFAULTS.in;
|
|
310
|
+
|
|
311
|
+
if (keyIn === 'header') {
|
|
312
|
+
headers[keyName] = auth.key;
|
|
313
|
+
} else if (keyIn === 'query') {
|
|
314
|
+
const separator = url.includes('?') ? '&' : '?';
|
|
315
|
+
url = \`\${url}\${separator}\${keyName}=\${encodeURIComponent(auth.key)}\`;
|
|
316
|
+
} else if (keyIn === 'cookie') {
|
|
317
|
+
headers['Cookie'] = \`\${keyName}=\${auth.key}\`;
|
|
318
|
+
}
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
case 'oauth2': {
|
|
323
|
+
// If we have an access token, use it directly
|
|
324
|
+
// Token flows (client_credentials, password) are handled separately
|
|
325
|
+
if (auth.accessToken) {
|
|
326
|
+
headers['Authorization'] = \`Bearer \${auth.accessToken}\`;
|
|
327
|
+
}
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return { headers, url };
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Apply pagination parameters to URL and/or headers based on configuration
|
|
337
|
+
*/
|
|
338
|
+
function applyPagination(
|
|
339
|
+
pagination: PaginationConfig | undefined,
|
|
340
|
+
url: string,
|
|
341
|
+
headers: Record<string, string | string[]>
|
|
342
|
+
): { url: string; headers: Record<string, string | string[]> } {
|
|
343
|
+
if (!pagination) return { url, headers };
|
|
344
|
+
|
|
345
|
+
const location = pagination.in ?? 'query';
|
|
346
|
+
const isHeader = location === 'header';
|
|
347
|
+
|
|
348
|
+
// Helper to get default param names based on location
|
|
349
|
+
const getDefaultName = (queryName: string, headerName: string) =>
|
|
350
|
+
isHeader ? headerName : queryName;
|
|
351
|
+
|
|
352
|
+
const queryParams = new URLSearchParams();
|
|
353
|
+
const headerParams: Record<string, string> = {};
|
|
354
|
+
|
|
355
|
+
const addParam = (name: string, value: string) => {
|
|
356
|
+
if (isHeader) {
|
|
357
|
+
headerParams[name] = value;
|
|
358
|
+
} else {
|
|
359
|
+
queryParams.append(name, value);
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
switch (pagination.type) {
|
|
364
|
+
case 'offset':
|
|
365
|
+
addParam(
|
|
366
|
+
pagination.offsetParam ?? getDefaultName('offset', 'X-Offset'),
|
|
367
|
+
String(pagination.offset)
|
|
368
|
+
);
|
|
369
|
+
addParam(
|
|
370
|
+
pagination.limitParam ?? getDefaultName('limit', 'X-Limit'),
|
|
371
|
+
String(pagination.limit)
|
|
372
|
+
);
|
|
373
|
+
break;
|
|
374
|
+
|
|
375
|
+
case 'cursor':
|
|
376
|
+
if (pagination.cursor) {
|
|
377
|
+
addParam(
|
|
378
|
+
pagination.cursorParam ?? getDefaultName('cursor', 'X-Cursor'),
|
|
379
|
+
pagination.cursor
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
if (pagination.limit !== undefined) {
|
|
383
|
+
addParam(
|
|
384
|
+
pagination.limitParam ?? getDefaultName('limit', 'X-Limit'),
|
|
385
|
+
String(pagination.limit)
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
break;
|
|
389
|
+
|
|
390
|
+
case 'page':
|
|
391
|
+
addParam(
|
|
392
|
+
pagination.pageParam ?? getDefaultName('page', 'X-Page'),
|
|
393
|
+
String(pagination.page)
|
|
394
|
+
);
|
|
395
|
+
addParam(
|
|
396
|
+
pagination.pageSizeParam ?? getDefaultName('pageSize', 'X-Page-Size'),
|
|
397
|
+
String(pagination.pageSize)
|
|
398
|
+
);
|
|
399
|
+
break;
|
|
400
|
+
|
|
401
|
+
case 'range': {
|
|
402
|
+
// Range pagination is always header-based (RFC 7233 style)
|
|
403
|
+
const unit = pagination.unit ?? 'items';
|
|
404
|
+
const headerName = pagination.rangeHeader ?? 'Range';
|
|
405
|
+
headerParams[headerName] = \`\${unit}=\${pagination.start}-\${pagination.end}\`;
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Apply query params to URL
|
|
411
|
+
const queryString = queryParams.toString();
|
|
412
|
+
if (queryString) {
|
|
413
|
+
const separator = url.includes('?') ? '&' : '?';
|
|
414
|
+
url = \`\${url}\${separator}\${queryString}\`;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Merge header params
|
|
418
|
+
const updatedHeaders = { ...headers, ...headerParams };
|
|
419
|
+
|
|
420
|
+
return { url, headers: updatedHeaders };
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Apply query parameters to URL
|
|
425
|
+
*/
|
|
426
|
+
function applyQueryParams(queryParams: Record<string, string | number | boolean | undefined> | undefined, url: string): string {
|
|
427
|
+
if (!queryParams) return url;
|
|
428
|
+
|
|
429
|
+
const params = new URLSearchParams();
|
|
430
|
+
for (const [key, value] of Object.entries(queryParams)) {
|
|
431
|
+
if (value !== undefined) {
|
|
432
|
+
params.append(key, String(value));
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const paramString = params.toString();
|
|
437
|
+
if (!paramString) return url;
|
|
438
|
+
|
|
439
|
+
const separator = url.includes('?') ? '&' : '?';
|
|
440
|
+
return \`\${url}\${separator}\${paramString}\`;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Sleep for a specified number of milliseconds
|
|
445
|
+
*/
|
|
446
|
+
function sleep(ms: number): Promise<void> {
|
|
447
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Calculate delay for exponential backoff
|
|
452
|
+
*/
|
|
453
|
+
function calculateBackoffDelay(
|
|
454
|
+
attempt: number,
|
|
455
|
+
config: Required<RetryConfig>
|
|
456
|
+
): number {
|
|
457
|
+
const delay = config.initialDelayMs * Math.pow(config.backoffMultiplier, attempt - 1);
|
|
458
|
+
return Math.min(delay, config.maxDelayMs);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Determine if a request should be retried based on error/response
|
|
463
|
+
*/
|
|
464
|
+
function shouldRetry(
|
|
465
|
+
error: Error | null,
|
|
466
|
+
response: HttpResponse | null,
|
|
467
|
+
config: Required<RetryConfig>,
|
|
468
|
+
attempt: number
|
|
469
|
+
): boolean {
|
|
470
|
+
if (attempt >= config.maxRetries) return false;
|
|
471
|
+
|
|
472
|
+
if (error && config.retryOnNetworkError) return true;
|
|
473
|
+
|
|
474
|
+
if (response && config.retryableStatusCodes.includes(response.status)) return true;
|
|
475
|
+
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Execute request with retry logic
|
|
481
|
+
*/
|
|
482
|
+
async function executeWithRetry(
|
|
483
|
+
params: HttpRequestParams,
|
|
484
|
+
makeRequest: (params: HttpRequestParams) => Promise<HttpResponse>,
|
|
485
|
+
retryConfig?: RetryConfig
|
|
486
|
+
): Promise<HttpResponse> {
|
|
487
|
+
const config = { ...DEFAULT_RETRY_CONFIG, ...retryConfig };
|
|
488
|
+
let lastError: Error | null = null;
|
|
489
|
+
let lastResponse: HttpResponse | null = null;
|
|
490
|
+
|
|
491
|
+
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
|
|
492
|
+
try {
|
|
493
|
+
if (attempt > 0) {
|
|
494
|
+
const delay = calculateBackoffDelay(attempt, config);
|
|
495
|
+
config.onRetry(attempt, delay, lastError ?? new Error('Retry attempt'));
|
|
496
|
+
await sleep(delay);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const response = await makeRequest(params);
|
|
500
|
+
|
|
501
|
+
// Check if we should retry this response
|
|
502
|
+
if (!shouldRetry(null, response, config, attempt + 1)) {
|
|
503
|
+
return response;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
lastResponse = response;
|
|
507
|
+
lastError = new Error(\`HTTP Error: \${response.status} \${response.statusText}\`);
|
|
508
|
+
} catch (error) {
|
|
509
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
510
|
+
|
|
511
|
+
if (!shouldRetry(lastError, null, config, attempt + 1)) {
|
|
512
|
+
throw lastError;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// All retries exhausted
|
|
518
|
+
if (lastResponse) {
|
|
519
|
+
return lastResponse;
|
|
520
|
+
}
|
|
521
|
+
throw lastError ?? new Error('Request failed after retries');
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Handle HTTP error status codes with standardized messages
|
|
526
|
+
*/
|
|
527
|
+
function handleHttpError(status: number, statusText: string): never {
|
|
528
|
+
switch (status) {
|
|
529
|
+
case 401:
|
|
530
|
+
throw new Error('Unauthorized');
|
|
531
|
+
case 403:
|
|
532
|
+
throw new Error('Forbidden');
|
|
533
|
+
case 404:
|
|
534
|
+
throw new Error('Not Found');
|
|
535
|
+
case 500:
|
|
536
|
+
throw new Error('Internal Server Error');
|
|
537
|
+
default:
|
|
538
|
+
throw new Error(\`HTTP Error: \${status} \${statusText}\`);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Extract headers from response into a plain object
|
|
544
|
+
*/
|
|
545
|
+
function extractHeaders(response: HttpResponse): Record<string, string> {
|
|
546
|
+
const headers: Record<string, string> = {};
|
|
547
|
+
|
|
548
|
+
if (response.headers) {
|
|
549
|
+
if (typeof (response.headers as any).forEach === 'function') {
|
|
550
|
+
// Headers object (fetch API)
|
|
551
|
+
(response.headers as Headers).forEach((value, key) => {
|
|
552
|
+
headers[key.toLowerCase()] = value;
|
|
553
|
+
});
|
|
554
|
+
} else {
|
|
555
|
+
// Plain object
|
|
556
|
+
for (const [key, value] of Object.entries(response.headers)) {
|
|
557
|
+
headers[key.toLowerCase()] = value;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return headers;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Extract pagination info from response headers
|
|
567
|
+
*/
|
|
568
|
+
function extractPaginationInfo(
|
|
569
|
+
headers: Record<string, string>,
|
|
570
|
+
currentPagination?: PaginationConfig
|
|
571
|
+
): PaginationInfo | undefined {
|
|
572
|
+
const info: PaginationInfo = {};
|
|
573
|
+
let hasPaginationInfo = false;
|
|
574
|
+
|
|
575
|
+
// Common total count headers
|
|
576
|
+
const totalCount = headers['x-total-count'] || headers['x-total'] || headers['total-count'];
|
|
577
|
+
if (totalCount) {
|
|
578
|
+
info.totalCount = parseInt(totalCount, 10);
|
|
579
|
+
hasPaginationInfo = true;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Total pages
|
|
583
|
+
const totalPages = headers['x-total-pages'] || headers['x-page-count'];
|
|
584
|
+
if (totalPages) {
|
|
585
|
+
info.totalPages = parseInt(totalPages, 10);
|
|
586
|
+
hasPaginationInfo = true;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Next cursor
|
|
590
|
+
const nextCursor = headers['x-next-cursor'] || headers['x-cursor-next'];
|
|
591
|
+
if (nextCursor) {
|
|
592
|
+
info.nextCursor = nextCursor;
|
|
593
|
+
info.hasMore = true;
|
|
594
|
+
hasPaginationInfo = true;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Previous cursor
|
|
598
|
+
const prevCursor = headers['x-prev-cursor'] || headers['x-cursor-prev'];
|
|
599
|
+
if (prevCursor) {
|
|
600
|
+
info.prevCursor = prevCursor;
|
|
601
|
+
hasPaginationInfo = true;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Has more indicator
|
|
605
|
+
const hasMore = headers['x-has-more'] || headers['x-has-next'];
|
|
606
|
+
if (hasMore) {
|
|
607
|
+
info.hasMore = hasMore.toLowerCase() === 'true' || hasMore === '1';
|
|
608
|
+
hasPaginationInfo = true;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Parse Link header (RFC 5988)
|
|
612
|
+
const linkHeader = headers['link'];
|
|
613
|
+
if (linkHeader) {
|
|
614
|
+
const links = parseLinkHeader(linkHeader);
|
|
615
|
+
if (links.next) {
|
|
616
|
+
info.hasMore = true;
|
|
617
|
+
hasPaginationInfo = true;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Include current pagination state
|
|
622
|
+
if (currentPagination) {
|
|
623
|
+
switch (currentPagination.type) {
|
|
624
|
+
case 'offset':
|
|
625
|
+
info.currentOffset = currentPagination.offset;
|
|
626
|
+
info.limit = currentPagination.limit;
|
|
627
|
+
break;
|
|
628
|
+
case 'cursor':
|
|
629
|
+
info.limit = currentPagination.limit;
|
|
630
|
+
break;
|
|
631
|
+
case 'page':
|
|
632
|
+
info.currentOffset = (currentPagination.page - 1) * currentPagination.pageSize;
|
|
633
|
+
info.limit = currentPagination.pageSize;
|
|
634
|
+
break;
|
|
635
|
+
case 'range':
|
|
636
|
+
info.currentOffset = currentPagination.start;
|
|
637
|
+
info.limit = currentPagination.end - currentPagination.start + 1;
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
hasPaginationInfo = true;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Calculate hasMore based on total count
|
|
644
|
+
if (info.hasMore === undefined && info.totalCount !== undefined &&
|
|
645
|
+
info.currentOffset !== undefined && info.limit !== undefined) {
|
|
646
|
+
info.hasMore = info.currentOffset + info.limit < info.totalCount;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
return hasPaginationInfo ? info : undefined;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Parse RFC 5988 Link header
|
|
654
|
+
*/
|
|
655
|
+
function parseLinkHeader(header: string): Record<string, string> {
|
|
656
|
+
const links: Record<string, string> = {};
|
|
657
|
+
const parts = header.split(',');
|
|
658
|
+
|
|
659
|
+
for (const part of parts) {
|
|
660
|
+
const match = part.match(/<([^>]+)>;\\s*rel="?([^";\\s]+)"?/);
|
|
661
|
+
if (match) {
|
|
662
|
+
links[match[2]] = match[1];
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
return links;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Create pagination helper functions for the response
|
|
671
|
+
*/
|
|
672
|
+
function createPaginationHelpers<T, TContext extends HttpClientContext>(
|
|
673
|
+
currentConfig: TContext,
|
|
674
|
+
paginationInfo: PaginationInfo | undefined,
|
|
675
|
+
requestFn: (config: TContext) => Promise<HttpClientResponse<T>>
|
|
676
|
+
): Pick<HttpClientResponse<T>, 'getNextPage' | 'getPrevPage' | 'hasNextPage' | 'hasPrevPage'> {
|
|
677
|
+
const helpers: Pick<HttpClientResponse<T>, 'getNextPage' | 'getPrevPage' | 'hasNextPage' | 'hasPrevPage'> = {};
|
|
678
|
+
|
|
679
|
+
if (!currentConfig.pagination) {
|
|
680
|
+
return helpers;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const pagination = currentConfig.pagination;
|
|
684
|
+
|
|
685
|
+
helpers.hasNextPage = () => {
|
|
686
|
+
if (paginationInfo?.hasMore !== undefined) return paginationInfo.hasMore;
|
|
687
|
+
if (paginationInfo?.nextCursor) return true;
|
|
688
|
+
if (paginationInfo?.totalCount !== undefined &&
|
|
689
|
+
paginationInfo.currentOffset !== undefined &&
|
|
690
|
+
paginationInfo.limit !== undefined) {
|
|
691
|
+
return paginationInfo.currentOffset + paginationInfo.limit < paginationInfo.totalCount;
|
|
692
|
+
}
|
|
693
|
+
return false;
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
helpers.hasPrevPage = () => {
|
|
697
|
+
if (paginationInfo?.prevCursor) return true;
|
|
698
|
+
if (paginationInfo?.currentOffset !== undefined) {
|
|
699
|
+
return paginationInfo.currentOffset > 0;
|
|
700
|
+
}
|
|
701
|
+
return false;
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
helpers.getNextPage = async () => {
|
|
705
|
+
let nextPagination: PaginationConfig;
|
|
706
|
+
|
|
707
|
+
switch (pagination.type) {
|
|
708
|
+
case 'offset':
|
|
709
|
+
nextPagination = { ...pagination, offset: pagination.offset + pagination.limit };
|
|
710
|
+
break;
|
|
711
|
+
case 'cursor':
|
|
712
|
+
if (!paginationInfo?.nextCursor) throw new Error('No next cursor available');
|
|
713
|
+
nextPagination = { ...pagination, cursor: paginationInfo.nextCursor };
|
|
714
|
+
break;
|
|
715
|
+
case 'page':
|
|
716
|
+
nextPagination = { ...pagination, page: pagination.page + 1 };
|
|
717
|
+
break;
|
|
718
|
+
case 'range':
|
|
719
|
+
const rangeSize = pagination.end - pagination.start + 1;
|
|
720
|
+
nextPagination = { ...pagination, start: pagination.end + 1, end: pagination.end + rangeSize };
|
|
721
|
+
break;
|
|
722
|
+
default:
|
|
723
|
+
throw new Error('Unsupported pagination type');
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
return requestFn({ ...currentConfig, pagination: nextPagination });
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
helpers.getPrevPage = async () => {
|
|
730
|
+
let prevPagination: PaginationConfig;
|
|
731
|
+
|
|
732
|
+
switch (pagination.type) {
|
|
733
|
+
case 'offset':
|
|
734
|
+
prevPagination = { ...pagination, offset: Math.max(0, pagination.offset - pagination.limit) };
|
|
735
|
+
break;
|
|
736
|
+
case 'cursor':
|
|
737
|
+
if (!paginationInfo?.prevCursor) throw new Error('No previous cursor available');
|
|
738
|
+
prevPagination = { ...pagination, cursor: paginationInfo.prevCursor };
|
|
739
|
+
break;
|
|
740
|
+
case 'page':
|
|
741
|
+
prevPagination = { ...pagination, page: Math.max(1, pagination.page - 1) };
|
|
742
|
+
break;
|
|
743
|
+
case 'range':
|
|
744
|
+
const size = pagination.end - pagination.start + 1;
|
|
745
|
+
const newStart = Math.max(0, pagination.start - size);
|
|
746
|
+
prevPagination = { ...pagination, start: newStart, end: newStart + size - 1 };
|
|
747
|
+
break;
|
|
748
|
+
default:
|
|
749
|
+
throw new Error('Unsupported pagination type');
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
return requestFn({ ...currentConfig, pagination: prevPagination });
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
return helpers;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Builds a URL with path parameters replaced
|
|
760
|
+
* @param server - Base server URL
|
|
761
|
+
* @param pathTemplate - Path template with {param} placeholders
|
|
762
|
+
* @param parameters - Parameter object with getChannelWithParameters method
|
|
763
|
+
*/
|
|
764
|
+
function buildUrlWithParameters<T extends { getChannelWithParameters: (path: string) => string }>(
|
|
765
|
+
server: string,
|
|
766
|
+
pathTemplate: string,
|
|
767
|
+
parameters: T
|
|
768
|
+
): string {
|
|
769
|
+
const path = parameters.getChannelWithParameters(pathTemplate);
|
|
770
|
+
return \`\${server}\${path}\`;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Extracts headers from a typed headers object and merges with additional headers
|
|
775
|
+
*/
|
|
776
|
+
function applyTypedHeaders(
|
|
777
|
+
typedHeaders: { marshal: () => string } | undefined,
|
|
778
|
+
additionalHeaders: Record<string, string | string[]> | undefined
|
|
779
|
+
): Record<string, string | string[]> {
|
|
780
|
+
const headers: Record<string, string | string[]> = {
|
|
781
|
+
'Content-Type': 'application/json',
|
|
782
|
+
...additionalHeaders
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
if (typedHeaders) {
|
|
786
|
+
// Parse the marshalled headers and merge them
|
|
787
|
+
const marshalledHeaders = JSON.parse(typedHeaders.marshal());
|
|
788
|
+
for (const [key, value] of Object.entries(marshalledHeaders)) {
|
|
789
|
+
headers[key] = value as string;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
return headers;
|
|
794
|
+
}
|
|
795
|
+
${requirements.oauth2 ? (0, security_1.renderOAuth2Helpers)() : (0, security_1.renderOAuth2Stubs)()}
|
|
796
|
+
// ============================================================================
|
|
797
|
+
// Generated HTTP Client Functions
|
|
798
|
+
// ============================================================================`;
|
|
799
|
+
}
|
|
800
|
+
exports.renderHttpCommonTypes = renderHttpCommonTypes;
|