@c15t/node-sdk 2.2.1 → 3.0.0-alpha.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.
- package/README.md +2 -2
- package/dist/index.js +205 -179
- package/dist/testing.js +61 -45
- package/dist-types/client.d.ts +433 -232
- package/dist-types/endpoints/consent.d.ts +1 -1
- package/dist-types/endpoints/init.d.ts +1 -1
- package/dist-types/endpoints/status.d.ts +1 -1
- package/dist-types/endpoints/subjects.d.ts +4 -4
- package/dist-types/error.d.ts +1 -1
- package/dist-types/fetcher.d.ts +4 -4
- package/dist-types/headers.d.ts +1 -1
- package/dist-types/index.d.ts +1 -1
- package/dist-types/testing.d.ts +5 -26
- package/dist-types/types.d.ts +4 -4
- package/dist-types/version.d.ts +1 -1
- package/package.json +24 -25
- package/readme.json +1 -1
- package/dist/index.cjs +0 -427
- package/dist/testing.cjs +0 -123
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<a href="https://github.com/c15t/c15t/blob/main/LICENSE.md"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/github/c15t/c15t/license.svg?variant=outline&mode=dark"><img src="https://shieldcn.dev/github/c15t/c15t/license.svg?variant=outline&mode=light" alt="License"></picture></a>
|
|
16
16
|
<a href="https://c15t.link/discord"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/discord/1312171102268690493.svg?variant=outline&mode=dark"><img src="https://shieldcn.dev/discord/1312171102268690493.svg?variant=outline&mode=light" alt="Discord"></picture></a>
|
|
17
17
|
<a href="https://skills.sh/c15t/skills/c15t"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/skills/c15t/skills/c15t.svg?variant=outline&mode=dark"><img src="https://shieldcn.dev/skills/c15t/skills/c15t.svg?variant=outline&mode=light" alt="Skills"></picture></a>
|
|
18
|
-
<a href="https://inth.com?utm_source=npm&utm_medium=readme&utm_campaign=oss_readme&utm_content=%40c15t%2Fnode-sdk"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/badge/Made%20By-Inth-ffc803.svg?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAzOTMgNDAwIj48cGF0aCBmaWxsPSIjMDAwIiBkPSJNMTgyLjY2MiAwdjM2Ljg5NWgtNTkuMDMxdjgyLjczM2g1OS4wMzF2MzYuODkzSDI3LjQ4MnYtMzYuODkzaDU5LjAzVjM2Ljg5NWgtNTkuMDNWMHpNMzIxLjk0MSA4OS44NVYwaDM1LjM1NXYxNTYuNTIxaC0yNS43MTNsLTg2LjEzNy05MC4zNjR2OTAuMzY0aC0zNS4zNTVWMGgyNi4zNTV6Ii8%2BPHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzE4LjU3MSAxODUuNzE0aDc0LjI4NlY0MDBIMFYxODUuNzE0aDI3Mi44NTd2LTQ3LjE0M3ptLTI5MS4wOSAyOC45Njl2MzcuMTE4aDU4LjEzN3YxMTkuNjI4aDM2Ljg5NVYyNTEuODAxaDU4LjU4NHYtMzcuMTE4em0xODIuNjEuMjI0djE1Ni41MjJoMzYuODk0VjMxMy41OWg3My4zNDF2NTcuODM5aDM3LjExOFYyMTQuOTA3aC0zNy4xMTh2NjEuNzg4aC03My4zNDF2LTYxLjc4OHoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg%3D%3D&
|
|
18
|
+
<a href="https://inth.com?utm_source=npm&utm_medium=readme&utm_campaign=oss_readme&utm_content=%40c15t%2Fnode-sdk"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/badge/Made%20By-Inth-ffc803.svg?color=ffc803&labelTextColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAzOTMgNDAwIj48cGF0aCBmaWxsPSIjMDAwIiBkPSJNMTgyLjY2MiAwdjM2Ljg5NWgtNTkuMDMxdjgyLjczM2g1OS4wMzF2MzYuODkzSDI3LjQ4MnYtMzYuODkzaDU5LjAzVjM2Ljg5NWgtNTkuMDNWMHpNMzIxLjk0MSA4OS44NVYwaDM1LjM1NXYxNTYuNTIxaC0yNS43MTNsLTg2LjEzNy05MC4zNjR2OTAuMzY0aC0zNS4zNTVWMGgyNi4zNTV6Ii8%2BPHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzE4LjU3MSAxODUuNzE0aDc0LjI4NlY0MDBIMFYxODUuNzE0aDI3Mi44NTd2LTQ3LjE0M3ptLTI5MS4wOSAyOC45Njl2MzcuMTE4aDU4LjEzN3YxMTkuNjI4aDM2Ljg5NVYyNTEuODAxaDU4LjU4NHYtMzcuMTE4em0xODIuNjEuMjI0djE1Ni41MjJoMzYuODk0VjMxMy41OWg3My4zNDF2NTcuODM5aDM3LjExOFYyMTQuOTA3aC0zNy4xMTh2NjEuNzg4aC03My4zNDF2LTYxLjc4OHoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg%3D%3D&valueColor=000000&mode=dark"><img src="https://shieldcn.dev/badge/Made%20By-Inth-ffc803.svg?color=ffc803&labelTextColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAzOTMgNDAwIj48cGF0aCBmaWxsPSIjMDAwIiBkPSJNMTgyLjY2MiAwdjM2Ljg5NWgtNTkuMDMxdjgyLjczM2g1OS4wMzF2MzYuODkzSDI3LjQ4MnYtMzYuODkzaDU5LjAzVjM2Ljg5NWgtNTkuMDNWMHpNMzIxLjk0MSA4OS44NVYwaDM1LjM1NXYxNTYuNTIxaC0yNS43MTNsLTg2LjEzNy05MC4zNjR2OTAuMzY0aC0zNS4zNTVWMGgyNi4zNTV6Ii8%2BPHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzE4LjU3MSAxODUuNzE0aDc0LjI4NlY0MDBIMFYxODUuNzE0aDI3Mi44NTd2LTQ3LjE0M3ptLTI5MS4wOSAyOC45Njl2MzcuMTE4aDU4LjEzN3YxMTkuNjI4aDM2Ljg5NVYyNTEuODAxaDU4LjU4NHYtMzcuMTE4em0xODIuNjEuMjI0djE1Ni41MjJoMzYuODk0VjMxMy41OWg3My4zNDF2NTcuODM5aDM3LjExOFYyMTQuOTA3aC0zNy4xMTh2NjEuNzg4aC03My4zNDF2LTYxLjc4OHoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg%3D%3D&valueColor=000000&mode=light" alt="Made by Inth"></picture></a>
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
21
|
Type-safe Node.js SDK for hosted and self-hosted c15t consent APIs, consent records, privacy preferences, and backend automation.
|
|
@@ -32,7 +32,7 @@ Type-safe Node.js SDK for hosted and self-hosted c15t consent APIs, consent reco
|
|
|
32
32
|
|
|
33
33
|
## Prerequisites
|
|
34
34
|
|
|
35
|
-
- Node.js
|
|
35
|
+
- Node.js 20.19 or later
|
|
36
36
|
- A hosted [c15t instance](https://inth.com) (free sign-up) or [self-hosted deployment](https://c15t.com/docs/self-host/quickstart)
|
|
37
37
|
|
|
38
38
|
## Manual Installation
|
package/dist/index.js
CHANGED
|
@@ -35,93 +35,185 @@ class C15TError extends Error {
|
|
|
35
35
|
}
|
|
36
36
|
toJSON() {
|
|
37
37
|
return {
|
|
38
|
-
name: this.name,
|
|
39
|
-
message: this.message,
|
|
40
|
-
status: this.status,
|
|
41
38
|
code: this.code,
|
|
42
|
-
details: this.details
|
|
39
|
+
details: this.details,
|
|
40
|
+
message: this.message,
|
|
41
|
+
name: this.name,
|
|
42
|
+
status: this.status
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
function
|
|
46
|
+
const error_isC15TError = function(error) {
|
|
47
47
|
return error instanceof C15TError;
|
|
48
|
-
}
|
|
48
|
+
};
|
|
49
|
+
const version = '3.0.0-alpha.0';
|
|
49
50
|
const C15T_VERSION_HEADERS = {
|
|
50
|
-
'x-c15t-version':
|
|
51
|
+
'x-c15t-version': version
|
|
51
52
|
};
|
|
52
53
|
const DEFAULT_RETRY_CONFIG = {
|
|
53
|
-
maxRetries: 3,
|
|
54
|
-
initialDelayMs: 100,
|
|
55
54
|
backoffFactor: 2,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
502,
|
|
59
|
-
503,
|
|
60
|
-
504
|
|
61
|
-
],
|
|
55
|
+
initialDelayMs: 100,
|
|
56
|
+
maxRetries: 3,
|
|
62
57
|
nonRetryableStatusCodes: [
|
|
63
58
|
400,
|
|
64
59
|
401,
|
|
65
60
|
403,
|
|
66
61
|
404
|
|
67
62
|
],
|
|
68
|
-
retryOnNetworkError: true
|
|
63
|
+
retryOnNetworkError: true,
|
|
64
|
+
retryableStatusCodes: [
|
|
65
|
+
500,
|
|
66
|
+
502,
|
|
67
|
+
503,
|
|
68
|
+
504
|
|
69
|
+
]
|
|
69
70
|
};
|
|
70
|
-
function
|
|
71
|
+
const fetcher_debugLog = function(debug, method, path, durationMs, status) {
|
|
71
72
|
if (!debug) return;
|
|
72
73
|
const timestamp = new Date().toISOString();
|
|
73
74
|
console.log(`[c15t] ${timestamp} ${method} ${path} (${durationMs}ms) -> ${status}`);
|
|
74
|
-
}
|
|
75
|
-
const delay = (ms)=>new Promise((resolve)=>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
};
|
|
76
|
+
const delay = (ms)=>new Promise((resolve)=>{
|
|
77
|
+
setTimeout(resolve, ms);
|
|
78
|
+
});
|
|
79
|
+
const fetcher_generateUUID = function() {
|
|
80
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/gu, (c)=>{
|
|
81
|
+
const r = Math.floor(16 * Math.random());
|
|
82
|
+
const v = 'x' === c ? r : r % 4 + 8;
|
|
80
83
|
return v.toString(16);
|
|
81
84
|
});
|
|
82
|
-
}
|
|
83
|
-
function createResponseContext(isSuccess, data = null, error = null, response = null) {
|
|
85
|
+
};
|
|
86
|
+
const fetcher_createResponseContext = function createResponseContext(isSuccess, data = null, error = null, response = null) {
|
|
84
87
|
return {
|
|
85
88
|
data,
|
|
86
89
|
error,
|
|
87
|
-
|
|
88
|
-
response,
|
|
89
|
-
unwrap () {
|
|
90
|
+
expect (message) {
|
|
90
91
|
if (!isSuccess || null === data) throw new C15TError({
|
|
91
|
-
|
|
92
|
-
status: error?.status || 0,
|
|
92
|
+
cause: error?.cause,
|
|
93
93
|
code: error?.code,
|
|
94
94
|
details: error?.details,
|
|
95
|
-
|
|
95
|
+
message,
|
|
96
|
+
status: error?.status || 0
|
|
96
97
|
});
|
|
97
98
|
return data;
|
|
98
99
|
},
|
|
99
|
-
|
|
100
|
-
if (!isSuccess || null === data) return
|
|
101
|
-
return data;
|
|
100
|
+
map (fn) {
|
|
101
|
+
if (!isSuccess || null === data) return createResponseContext(false, null, error, response);
|
|
102
|
+
return createResponseContext(true, fn(data), null, response);
|
|
102
103
|
},
|
|
103
|
-
|
|
104
|
+
ok: isSuccess,
|
|
105
|
+
response,
|
|
106
|
+
unwrap () {
|
|
104
107
|
if (!isSuccess || null === data) throw new C15TError({
|
|
105
|
-
|
|
106
|
-
status: error?.status || 0,
|
|
108
|
+
cause: error?.cause,
|
|
107
109
|
code: error?.code,
|
|
108
110
|
details: error?.details,
|
|
109
|
-
|
|
111
|
+
message: error?.message || 'Request failed',
|
|
112
|
+
status: error?.status || 0
|
|
110
113
|
});
|
|
111
114
|
return data;
|
|
112
115
|
},
|
|
113
|
-
|
|
114
|
-
if (!isSuccess || null === data) return
|
|
115
|
-
return
|
|
116
|
+
unwrapOr (defaultValue) {
|
|
117
|
+
if (!isSuccess || null === data) return defaultValue;
|
|
118
|
+
return data;
|
|
116
119
|
}
|
|
117
120
|
};
|
|
118
|
-
}
|
|
119
|
-
function
|
|
121
|
+
};
|
|
122
|
+
const fetcher_resolveUrl = function(baseUrl, path) {
|
|
120
123
|
const cleanBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
|
121
124
|
const cleanPath = path.startsWith('/') ? path.slice(1) : path;
|
|
122
125
|
return `${cleanBase}/${cleanPath}`;
|
|
123
|
-
}
|
|
124
|
-
|
|
126
|
+
};
|
|
127
|
+
const parseResponseData = async (response)=>{
|
|
128
|
+
try {
|
|
129
|
+
const contentType = response.headers.get('content-type');
|
|
130
|
+
if (contentType?.includes('application/json') && 204 !== response.status && '0' !== response.headers.get('content-length')) return {
|
|
131
|
+
data: await response.json(),
|
|
132
|
+
parseError: null
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
data: null,
|
|
136
|
+
parseError: null
|
|
137
|
+
};
|
|
138
|
+
} catch (parseError) {
|
|
139
|
+
return {
|
|
140
|
+
data: null,
|
|
141
|
+
parseError
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
const processApiError = (response, data, requestMethod, path, durationMs, context, options, retryableStatusCodes, nonRetryableStatusCodes, attemptsMade, maxRetries)=>{
|
|
146
|
+
const errorData = data;
|
|
147
|
+
const errorResponse = fetcher_createResponseContext(false, null, {
|
|
148
|
+
code: errorData?.code || 'API_ERROR',
|
|
149
|
+
details: errorData?.details || null,
|
|
150
|
+
message: errorData?.message || `Request failed with status ${response.status}`,
|
|
151
|
+
status: response.status
|
|
152
|
+
}, response);
|
|
153
|
+
const retry = !nonRetryableStatusCodes.includes(response.status) && retryableStatusCodes.includes(response.status) && attemptsMade < maxRetries;
|
|
154
|
+
if (retry) return {
|
|
155
|
+
retry: true
|
|
156
|
+
};
|
|
157
|
+
fetcher_debugLog(context.debug, requestMethod, path, durationMs, response.status);
|
|
158
|
+
options?.onError?.(errorResponse, path);
|
|
159
|
+
if (options?.throw) throw new Error(errorResponse.error?.message || 'Request failed');
|
|
160
|
+
return {
|
|
161
|
+
response: errorResponse,
|
|
162
|
+
retry: false
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
const processResponse = async (response, requestMethod, path, durationMs, context, options, retryableStatusCodes, nonRetryableStatusCodes, attemptsMade, maxRetries)=>{
|
|
166
|
+
const { data, parseError } = await parseResponseData(response);
|
|
167
|
+
if (parseError) {
|
|
168
|
+
const errorResponse = fetcher_createResponseContext(false, null, {
|
|
169
|
+
cause: parseError,
|
|
170
|
+
code: 'PARSE_ERROR',
|
|
171
|
+
message: 'Failed to parse response',
|
|
172
|
+
status: response.status
|
|
173
|
+
}, response);
|
|
174
|
+
options?.onError?.(errorResponse, path);
|
|
175
|
+
if (options?.throw) throw new Error('Failed to parse response');
|
|
176
|
+
return {
|
|
177
|
+
response: errorResponse,
|
|
178
|
+
retry: false
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (response.ok) {
|
|
182
|
+
fetcher_debugLog(context.debug, requestMethod, path, durationMs, response.status);
|
|
183
|
+
const successResponse = fetcher_createResponseContext(true, data, null, response);
|
|
184
|
+
options?.onSuccess?.(successResponse);
|
|
185
|
+
return {
|
|
186
|
+
response: successResponse,
|
|
187
|
+
retry: false
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
return processApiError(response, data, requestMethod, path, durationMs, context, options, retryableStatusCodes, nonRetryableStatusCodes, attemptsMade, maxRetries);
|
|
191
|
+
};
|
|
192
|
+
const processFetchError = (fetchError, requestMethod, path, startTime, timeoutMs, context, options, retryOnNetworkError, attemptsMade, maxRetries)=>{
|
|
193
|
+
if (fetchError instanceof Error && 'Failed to parse response' === fetchError.message) throw fetchError;
|
|
194
|
+
const isAbortError = fetchError instanceof Error && 'AbortError' === fetchError.name;
|
|
195
|
+
let message = String(fetchError);
|
|
196
|
+
if (fetchError instanceof Error) ({ message } = fetchError);
|
|
197
|
+
if (isAbortError) message = `Request timed out after ${timeoutMs}ms`;
|
|
198
|
+
const errorResponse = fetcher_createResponseContext(false, null, {
|
|
199
|
+
cause: fetchError,
|
|
200
|
+
code: isAbortError ? 'TIMEOUT' : 'NETWORK_ERROR',
|
|
201
|
+
message,
|
|
202
|
+
status: 0
|
|
203
|
+
}, null);
|
|
204
|
+
const retry = !(fetchError instanceof Response) && retryOnNetworkError && attemptsMade < maxRetries;
|
|
205
|
+
if (retry) return {
|
|
206
|
+
retry: true
|
|
207
|
+
};
|
|
208
|
+
fetcher_debugLog(context.debug, requestMethod, path, Date.now() - startTime, 'ERROR');
|
|
209
|
+
options?.onError?.(errorResponse, path);
|
|
210
|
+
if (options?.throw) throw fetchError;
|
|
211
|
+
return {
|
|
212
|
+
response: errorResponse,
|
|
213
|
+
retry: false
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
const fetcher_fetcher = function(context, path, options) {
|
|
125
217
|
const finalRetryConfig = {
|
|
126
218
|
...DEFAULT_RETRY_CONFIG,
|
|
127
219
|
...context.retryConfig,
|
|
@@ -140,20 +232,18 @@ async function fetcher(context, path, options) {
|
|
|
140
232
|
], retryOnNetworkError = true } = finalRetryConfig;
|
|
141
233
|
let attemptsMade = 0;
|
|
142
234
|
let currentDelay = initialDelayMs;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
const resolvedUrl = resolveUrl(context.baseUrl, path);
|
|
235
|
+
const executeAttempt = async ()=>{
|
|
236
|
+
const requestId = fetcher_generateUUID();
|
|
237
|
+
const resolvedUrl = fetcher_resolveUrl(context.baseUrl, path);
|
|
147
238
|
const url = new URL(resolvedUrl);
|
|
148
|
-
if (options?.query) {
|
|
149
|
-
|
|
150
|
-
}
|
|
239
|
+
if (options?.query) Object.entries(options.query).forEach(([key, value])=>{
|
|
240
|
+
if (null != value) url.searchParams.append(key, String(value));
|
|
241
|
+
});
|
|
151
242
|
const timeoutMs = options?.timeout ?? context.timeout;
|
|
152
243
|
const controller = new AbortController();
|
|
153
244
|
let timeoutId;
|
|
154
245
|
if (timeoutMs > 0) timeoutId = setTimeout(()=>controller.abort(), timeoutMs);
|
|
155
246
|
const requestOptions = {
|
|
156
|
-
method: options?.method || 'GET',
|
|
157
247
|
headers: {
|
|
158
248
|
'Content-Type': 'application/json',
|
|
159
249
|
...C15T_VERSION_HEADERS,
|
|
@@ -161,152 +251,88 @@ async function fetcher(context, path, options) {
|
|
|
161
251
|
'X-Request-ID': requestId,
|
|
162
252
|
...options?.headers
|
|
163
253
|
},
|
|
254
|
+
method: options?.method || 'GET',
|
|
164
255
|
signal: controller.signal
|
|
165
256
|
};
|
|
166
257
|
if (options?.body && 'GET' !== requestOptions.method) requestOptions.body = JSON.stringify(options.body);
|
|
167
258
|
const startTime = Date.now();
|
|
259
|
+
const requestMethod = requestOptions.method || 'GET';
|
|
168
260
|
try {
|
|
169
261
|
const response = await fetch(url.toString(), requestOptions);
|
|
170
262
|
if (timeoutId) clearTimeout(timeoutId);
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
let parseError = null;
|
|
174
|
-
try {
|
|
175
|
-
const contentType = response.headers.get('content-type');
|
|
176
|
-
if (contentType?.includes('application/json') && 204 !== response.status && '0' !== response.headers.get('content-length')) data = await response.json();
|
|
177
|
-
else if (204 === response.status) data = null;
|
|
178
|
-
} catch (err) {
|
|
179
|
-
parseError = err;
|
|
180
|
-
}
|
|
181
|
-
if (parseError) {
|
|
182
|
-
const errorResponse = createResponseContext(false, null, {
|
|
183
|
-
message: 'Failed to parse response',
|
|
184
|
-
status: response.status,
|
|
185
|
-
code: 'PARSE_ERROR',
|
|
186
|
-
cause: parseError
|
|
187
|
-
}, response);
|
|
188
|
-
options?.onError?.(errorResponse, path);
|
|
189
|
-
if (options?.throw) throw new Error('Failed to parse response');
|
|
190
|
-
return errorResponse;
|
|
191
|
-
}
|
|
192
|
-
const isSuccess = response.status >= 200 && response.status < 300;
|
|
193
|
-
if (isSuccess) {
|
|
194
|
-
debugLog(context.debug, requestOptions.method || 'GET', path, durationMs, response.status);
|
|
195
|
-
const successResponse = createResponseContext(true, data, null, response);
|
|
196
|
-
options?.onSuccess?.(successResponse);
|
|
197
|
-
return successResponse;
|
|
198
|
-
}
|
|
199
|
-
const errorData = data;
|
|
200
|
-
const errorResponse = createResponseContext(false, null, {
|
|
201
|
-
message: errorData?.message || `Request failed with status ${response.status}`,
|
|
202
|
-
status: response.status,
|
|
203
|
-
code: errorData?.code || 'API_ERROR',
|
|
204
|
-
details: errorData?.details || null
|
|
205
|
-
}, response);
|
|
206
|
-
lastErrorResponse = errorResponse;
|
|
207
|
-
let shouldRetryThisRequest = false;
|
|
208
|
-
shouldRetryThisRequest = nonRetryableStatusCodes.includes(response.status) ? false : retryableStatusCodes.includes(response.status);
|
|
209
|
-
if (!shouldRetryThisRequest || attemptsMade >= maxRetries) {
|
|
210
|
-
debugLog(context.debug, requestOptions.method || 'GET', path, durationMs, response.status);
|
|
211
|
-
options?.onError?.(errorResponse, path);
|
|
212
|
-
if (options?.throw) throw new Error(errorResponse.error?.message || 'Request failed');
|
|
213
|
-
return errorResponse;
|
|
214
|
-
}
|
|
215
|
-
attemptsMade++;
|
|
216
|
-
await delay(currentDelay);
|
|
217
|
-
currentDelay *= backoffFactor;
|
|
263
|
+
const outcome = await processResponse(response, requestMethod, path, Date.now() - startTime, context, options, retryableStatusCodes, nonRetryableStatusCodes, attemptsMade, maxRetries);
|
|
264
|
+
if (!outcome.retry) return outcome.response;
|
|
218
265
|
} catch (fetchError) {
|
|
219
266
|
if (timeoutId) clearTimeout(timeoutId);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const isNetworkError = !(fetchError instanceof Response);
|
|
223
|
-
const errorResponse = createResponseContext(false, null, {
|
|
224
|
-
message: isAbortError ? `Request timed out after ${timeoutMs}ms` : fetchError instanceof Error ? fetchError.message : String(fetchError),
|
|
225
|
-
status: 0,
|
|
226
|
-
code: isAbortError ? 'TIMEOUT' : 'NETWORK_ERROR',
|
|
227
|
-
cause: fetchError
|
|
228
|
-
}, null);
|
|
229
|
-
lastErrorResponse = errorResponse;
|
|
230
|
-
const shouldRetryThisRequest = isNetworkError && retryOnNetworkError;
|
|
231
|
-
if (!shouldRetryThisRequest || attemptsMade >= maxRetries) {
|
|
232
|
-
debugLog(context.debug, requestOptions.method || 'GET', path, Date.now() - startTime, 'ERROR');
|
|
233
|
-
options?.onError?.(errorResponse, path);
|
|
234
|
-
if (options?.throw) throw fetchError;
|
|
235
|
-
return errorResponse;
|
|
236
|
-
}
|
|
237
|
-
attemptsMade++;
|
|
238
|
-
await delay(currentDelay);
|
|
239
|
-
currentDelay *= backoffFactor;
|
|
267
|
+
const outcome = processFetchError(fetchError, requestMethod, path, startTime, timeoutMs, context, options, retryOnNetworkError, attemptsMade, maxRetries);
|
|
268
|
+
if (!outcome.retry) return outcome.response;
|
|
240
269
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (options?.throw) throw new Error(`Request failed after ${maxRetries} retries`);
|
|
249
|
-
return maxRetriesErrorResponse;
|
|
250
|
-
}
|
|
270
|
+
attemptsMade += 1;
|
|
271
|
+
await delay(currentDelay);
|
|
272
|
+
currentDelay *= backoffFactor;
|
|
273
|
+
return executeAttempt();
|
|
274
|
+
};
|
|
275
|
+
return executeAttempt();
|
|
276
|
+
};
|
|
251
277
|
const CONSENT_CHECK_PATH = '/consents/check';
|
|
252
|
-
|
|
253
|
-
return
|
|
278
|
+
const consent_checkConsent = function(context, query, options) {
|
|
279
|
+
return fetcher_fetcher(context, CONSENT_CHECK_PATH, {
|
|
254
280
|
method: 'GET',
|
|
255
281
|
query,
|
|
256
282
|
...options
|
|
257
283
|
});
|
|
258
|
-
}
|
|
284
|
+
};
|
|
259
285
|
const INIT_PATH = '/init';
|
|
260
|
-
|
|
261
|
-
return
|
|
286
|
+
const init_init = function(context, options) {
|
|
287
|
+
return fetcher_fetcher(context, INIT_PATH, {
|
|
262
288
|
method: 'GET',
|
|
263
289
|
...options
|
|
264
290
|
});
|
|
265
|
-
}
|
|
291
|
+
};
|
|
266
292
|
const STATUS_PATH = '/status';
|
|
267
|
-
|
|
268
|
-
return
|
|
293
|
+
const status_status = function(context, options) {
|
|
294
|
+
return fetcher_fetcher(context, STATUS_PATH, {
|
|
269
295
|
method: 'GET',
|
|
270
296
|
...options
|
|
271
297
|
});
|
|
272
|
-
}
|
|
298
|
+
};
|
|
273
299
|
const SUBJECTS_PATH = '/subjects';
|
|
274
|
-
|
|
275
|
-
return
|
|
276
|
-
method: 'POST',
|
|
300
|
+
const subjects_createSubject = function(context, input, options) {
|
|
301
|
+
return fetcher_fetcher(context, SUBJECTS_PATH, {
|
|
277
302
|
body: input,
|
|
303
|
+
method: 'POST',
|
|
278
304
|
...options
|
|
279
305
|
});
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
return
|
|
306
|
+
};
|
|
307
|
+
const subjects_getSubject = function(context, id, query, options) {
|
|
308
|
+
return fetcher_fetcher(context, `${SUBJECTS_PATH}/${id}`, {
|
|
283
309
|
method: 'GET',
|
|
284
310
|
query,
|
|
285
311
|
...options
|
|
286
312
|
});
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return
|
|
290
|
-
method: 'PATCH',
|
|
313
|
+
};
|
|
314
|
+
const subjects_patchSubject = function(context, id, input, options) {
|
|
315
|
+
return fetcher_fetcher(context, `${SUBJECTS_PATH}/${id}`, {
|
|
291
316
|
body: input,
|
|
317
|
+
method: 'PATCH',
|
|
292
318
|
...options
|
|
293
319
|
});
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return
|
|
320
|
+
};
|
|
321
|
+
const subjects_listSubjects = function(context, query, options) {
|
|
322
|
+
return fetcher_fetcher(context, SUBJECTS_PATH, {
|
|
297
323
|
method: 'GET',
|
|
298
324
|
query,
|
|
299
325
|
...options
|
|
300
326
|
});
|
|
301
|
-
}
|
|
327
|
+
};
|
|
302
328
|
class C15TClient {
|
|
303
329
|
context;
|
|
304
330
|
constructor(options = {}){
|
|
305
|
-
const baseUrlString = options.baseUrl || ("u"
|
|
331
|
+
const baseUrlString = options.baseUrl || ("u" < typeof process ? void 0 : process.env?.C15T_API_URL);
|
|
306
332
|
if (!baseUrlString) throw new TypeError('baseUrl is required. Provide it in options or set C15T_API_URL environment variable.');
|
|
307
333
|
const baseUrl = new URL(baseUrlString);
|
|
308
334
|
if (options.prefix) baseUrl.pathname = options.prefix;
|
|
309
|
-
const token = options.token || ("u"
|
|
335
|
+
const token = options.token || ("u" < typeof process ? void 0 : process.env?.C15T_API_TOKEN);
|
|
310
336
|
const authHeaders = token ? {
|
|
311
337
|
Authorization: `Bearer ${token}`
|
|
312
338
|
} : {};
|
|
@@ -314,42 +340,42 @@ class C15TClient {
|
|
|
314
340
|
...DEFAULT_RETRY_CONFIG,
|
|
315
341
|
...options.retryConfig
|
|
316
342
|
};
|
|
317
|
-
const debug = options.debug ?? ("u"
|
|
343
|
+
const debug = options.debug ?? ("u" < typeof process ? false : process.env?.C15T_DEBUG === 'true');
|
|
318
344
|
const timeout = options.timeout ?? 30000;
|
|
319
345
|
this.context = {
|
|
320
346
|
baseUrl: baseUrl.toString(),
|
|
347
|
+
debug,
|
|
321
348
|
headers: {
|
|
322
349
|
...authHeaders,
|
|
323
350
|
...options.headers
|
|
324
351
|
},
|
|
325
352
|
retryConfig,
|
|
326
|
-
debug,
|
|
327
353
|
timeout
|
|
328
354
|
};
|
|
329
355
|
}
|
|
330
|
-
|
|
356
|
+
status(options) {
|
|
331
357
|
return status_status(this.context, options);
|
|
332
358
|
}
|
|
333
|
-
|
|
334
|
-
return
|
|
359
|
+
init(options) {
|
|
360
|
+
return init_init(this.context, options);
|
|
335
361
|
}
|
|
336
|
-
|
|
337
|
-
return
|
|
362
|
+
createSubject(input, options) {
|
|
363
|
+
return subjects_createSubject(this.context, input, options);
|
|
338
364
|
}
|
|
339
|
-
|
|
340
|
-
return
|
|
365
|
+
getSubject(id, query, options) {
|
|
366
|
+
return subjects_getSubject(this.context, id, query, options);
|
|
341
367
|
}
|
|
342
|
-
|
|
343
|
-
return
|
|
368
|
+
patchSubject(id, input, options) {
|
|
369
|
+
return subjects_patchSubject(this.context, id, input, options);
|
|
344
370
|
}
|
|
345
|
-
|
|
346
|
-
return
|
|
371
|
+
listSubjects(query, options) {
|
|
372
|
+
return subjects_listSubjects(this.context, query, options);
|
|
347
373
|
}
|
|
348
|
-
|
|
349
|
-
return
|
|
374
|
+
checkConsent(query, options) {
|
|
375
|
+
return consent_checkConsent(this.context, query, options);
|
|
350
376
|
}
|
|
351
|
-
|
|
352
|
-
return
|
|
377
|
+
$fetch(path, options) {
|
|
378
|
+
return fetcher_fetcher(this.context, path, options);
|
|
353
379
|
}
|
|
354
380
|
consent = {
|
|
355
381
|
check: (query, options)=>this.checkConsent(query, options)
|
|
@@ -357,15 +383,15 @@ class C15TClient {
|
|
|
357
383
|
subjects = {
|
|
358
384
|
create: (input, options)=>this.createSubject(input, options),
|
|
359
385
|
get: (id, query, options)=>this.getSubject(id, query, options),
|
|
360
|
-
|
|
361
|
-
|
|
386
|
+
list: (query, options)=>this.listSubjects(query, options),
|
|
387
|
+
patch: (id, input, options)=>this.patchSubject(id, input, options)
|
|
362
388
|
};
|
|
363
389
|
meta = {
|
|
364
|
-
|
|
365
|
-
|
|
390
|
+
init: (options)=>this.init(options),
|
|
391
|
+
status: (options)=>this.status(options)
|
|
366
392
|
};
|
|
367
393
|
}
|
|
368
|
-
function
|
|
394
|
+
const src_c15tClient = function(options) {
|
|
369
395
|
return new C15TClient(options);
|
|
370
|
-
}
|
|
371
|
-
export { C15TClient, C15TError,
|
|
396
|
+
};
|
|
397
|
+
export { C15TClient, C15TError, error_isC15TError as isC15TError, fetcher_createResponseContext as createResponseContext, fetcher_fetcher as fetcher, fetcher_resolveUrl as resolveUrl, src_c15tClient as c15tClient };
|