@smileid/usesmileid-nodejs 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,421 @@
1
+ "use strict";
2
+ /**
3
+ * Thin per-operation functions (spec §3, §6).
4
+ *
5
+ * Generator-owned later: each function takes typed camelCase params, maps them
6
+ * to the verbatim snake_case wire (headers / query / path / multipart / JSON),
7
+ * calls the transport, and maps the response back to a public model. All wire
8
+ * field names are snake_case; the mapping boundary lives here.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.enhancedKyc = enhancedKyc;
12
+ exports.documentVerification = documentVerification;
13
+ exports.enhancedDocumentVerification = enhancedDocumentVerification;
14
+ exports.biometricKyc = biometricKyc;
15
+ exports.registration = registration;
16
+ exports.authentication = authentication;
17
+ exports.compare = compare;
18
+ exports.verificationStatus = verificationStatus;
19
+ exports.replayCallback = replayCallback;
20
+ exports.reportFraud = reportFraud;
21
+ exports.bankCodes = bankCodes;
22
+ exports.supportedIdTypes = supportedIdTypes;
23
+ exports.supportedDocuments = supportedDocuments;
24
+ exports.idStatus = idStatus;
25
+ const binary_js_1 = require("../../helpers/binary.js");
26
+ const multipart_js_1 = require("../../helpers/multipart.js");
27
+ const case_js_1 = require("../../helpers/case.js");
28
+ const validation_js_1 = require("../../helpers/validation.js");
29
+ const index_js_1 = require("../models/index.js");
30
+ // ---- shared serialization helpers -----------------------------------------
31
+ function consentJson(c) {
32
+ return JSON.stringify({
33
+ granted: c.granted,
34
+ granted_at: c.grantedAt,
35
+ notice_language: c.noticeLanguage,
36
+ notice_privacy_policy_url: c.noticePrivacyPolicyUrl,
37
+ });
38
+ }
39
+ function userDetailsJson(u) {
40
+ const obj = {
41
+ given_names: u.givenNames,
42
+ last_name: u.lastName,
43
+ };
44
+ if (u.email !== undefined && u.email !== null)
45
+ obj.email = u.email;
46
+ if (u.phoneNumber !== undefined && u.phoneNumber !== null)
47
+ obj.phone_number = u.phoneNumber;
48
+ return JSON.stringify(obj);
49
+ }
50
+ function scalar(name, value) {
51
+ if (value === undefined)
52
+ return [];
53
+ const str = typeof value === 'boolean' ? String(value) : String(value);
54
+ return [{ kind: 'scalar', name, value: str }];
55
+ }
56
+ function jsonPart(name, json) {
57
+ return json === undefined ? [] : [{ kind: 'json', name, json }];
58
+ }
59
+ function optionalJson(name, value) {
60
+ if (value === undefined)
61
+ return [];
62
+ return [{ kind: 'json', name, json: JSON.stringify(value) }];
63
+ }
64
+ const PNG_MAGIC = Buffer.from([0x89, 0x50, 0x4e, 0x47]);
65
+ /**
66
+ * selfie_image / liveness_images / comparison_image parts are always
67
+ * image/jpeg on the wire (spec §5.3, cross-SDK policy), regardless of any
68
+ * type carried by the input.
69
+ */
70
+ async function jpegPart(name, input, defaultName) {
71
+ const resolved = await (0, binary_js_1.resolveBinary)(input, defaultName, 'image/jpeg');
72
+ return {
73
+ kind: 'binary',
74
+ name,
75
+ filename: resolved.filename,
76
+ contentType: 'image/jpeg',
77
+ bytes: resolved.bytes,
78
+ };
79
+ }
80
+ /**
81
+ * document / document_back may be image/jpeg or image/png (spec §5.3). An
82
+ * explicit type on the input wins; otherwise PNG is detected from the file's
83
+ * magic bytes and everything else is treated as JPEG.
84
+ */
85
+ async function documentPart(name, input, defaultName) {
86
+ const resolved = await (0, binary_js_1.resolveBinary)(input, defaultName, '');
87
+ const contentType = resolved.contentType !== ''
88
+ ? resolved.contentType
89
+ : resolved.bytes.subarray(0, 4).equals(PNG_MAGIC)
90
+ ? 'image/png'
91
+ : 'image/jpeg';
92
+ return {
93
+ kind: 'binary',
94
+ name,
95
+ filename: resolved.filename,
96
+ contentType,
97
+ bytes: resolved.bytes,
98
+ };
99
+ }
100
+ async function livenessParts(images) {
101
+ // Repeated parts, all named liveness_images — never CSV/indexed (spec §5.3).
102
+ return Promise.all(images.map((img, i) => jpegPart('liveness_images', img, `liveness${i + 1}.jpg`)));
103
+ }
104
+ function toAccepted(json) {
105
+ const o = (json ?? {});
106
+ return new index_js_1.AcceptedResponse({
107
+ status: typeof o.status === 'string' ? o.status : '',
108
+ message: typeof o.message === 'string' ? o.message : '',
109
+ jobId: typeof o.job_id === 'string' ? o.job_id : null,
110
+ userId: typeof o.user_id === 'string' ? o.user_id : null,
111
+ createdAt: typeof o.created_at === 'string' ? o.created_at : null,
112
+ });
113
+ }
114
+ function toJobStatus(json) {
115
+ const o = (json ?? {});
116
+ return new index_js_1.JobStatus({
117
+ status: typeof o.status === 'string' ? o.status : '',
118
+ jobId: typeof o.job_id === 'string' ? o.job_id : null,
119
+ userId: typeof o.user_id === 'string' ? o.user_id : null,
120
+ message: typeof o.message === 'string' ? o.message : null,
121
+ });
122
+ }
123
+ function effectiveCallback(paramCb, opts, transport) {
124
+ const callback = paramCb ?? opts?.callbackUrl ?? transport.defaultCallbackUrl ?? undefined;
125
+ // Fleet standard: callback URLs must be https; checked before any request
126
+ // (including the token fetch) is made.
127
+ if (callback !== undefined) {
128
+ (0, validation_js_1.assertHttpsUrl)(callback, 'callbackUrl');
129
+ }
130
+ return callback;
131
+ }
132
+ function planExtras(opts) {
133
+ return { timeout: opts?.timeout, signal: opts?.signal };
134
+ }
135
+ // ---- entry endpoints -------------------------------------------------------
136
+ async function enhancedKyc(transport, params, opts) {
137
+ const parts = [
138
+ ...scalar('country', params.country),
139
+ ...scalar('id_type', params.idType),
140
+ ...scalar('id_number', params.idNumber),
141
+ ...scalar('bank_code', params.bankCode),
142
+ ...scalar('operator', params.operator),
143
+ ...scalar('callback_url', effectiveCallback(params.callbackUrl, opts, transport)),
144
+ ...jsonPart('user_details', userDetailsJson(params.userDetails)),
145
+ ...jsonPart('consent', consentJson(params.consent)),
146
+ ...optionalJson('partner_params', params.partnerParams),
147
+ ...optionalJson('metadata', params.metadata),
148
+ ];
149
+ const result = await transport.execute({
150
+ method: 'POST',
151
+ path: '/v3/enhanced_kyc',
152
+ authenticated: true,
153
+ needsPartnerIdHeader: false,
154
+ idempotent: false,
155
+ headers: { 'User-ID': params.userId },
156
+ multipart: (0, multipart_js_1.buildMultipart)(parts),
157
+ ...planExtras(opts),
158
+ });
159
+ return toAccepted(result.json);
160
+ }
161
+ async function documentVerification(transport, params, opts) {
162
+ const parts = [
163
+ ...scalar('country', params.country),
164
+ ...scalar('id_type', params.idType),
165
+ ...scalar('callback_url', effectiveCallback(params.callbackUrl, opts, transport)),
166
+ await jpegPart('selfie_image', params.selfieImage, 'selfie.jpg'),
167
+ ...(await livenessParts(params.livenessImages)),
168
+ await documentPart('document', params.document, 'document.jpg'),
169
+ ...(params.documentBack
170
+ ? [await documentPart('document_back', params.documentBack, 'document_back.jpg')]
171
+ : []),
172
+ ...jsonPart('user_details', userDetailsJson(params.userDetails)),
173
+ ...jsonPart('consent', consentJson(params.consent)),
174
+ ...optionalJson('partner_params', params.partnerParams),
175
+ ...optionalJson('metadata', params.metadata),
176
+ ];
177
+ const result = await transport.execute({
178
+ method: 'POST',
179
+ path: '/v3/document_verification',
180
+ authenticated: true,
181
+ needsPartnerIdHeader: true,
182
+ idempotent: false,
183
+ headers: { 'User-ID': params.userId },
184
+ multipart: (0, multipart_js_1.buildMultipart)(parts),
185
+ ...planExtras(opts),
186
+ });
187
+ return toAccepted(result.json);
188
+ }
189
+ async function enhancedDocumentVerification(transport, params, opts) {
190
+ const parts = [
191
+ ...scalar('country', params.country),
192
+ ...scalar('id_type', params.idType),
193
+ ...scalar('callback_url', effectiveCallback(params.callbackUrl, opts, transport)),
194
+ await jpegPart('selfie_image', params.selfieImage, 'selfie.jpg'),
195
+ ...(await livenessParts(params.livenessImages)),
196
+ await documentPart('document', params.document, 'document.jpg'),
197
+ ...(params.documentBack
198
+ ? [await documentPart('document_back', params.documentBack, 'document_back.jpg')]
199
+ : []),
200
+ ...jsonPart('user_details', userDetailsJson(params.userDetails)),
201
+ ...jsonPart('consent', consentJson(params.consent)),
202
+ ...optionalJson('partner_params', params.partnerParams),
203
+ ...optionalJson('metadata', params.metadata),
204
+ ];
205
+ const result = await transport.execute({
206
+ method: 'POST',
207
+ path: '/v3/enhanced_document_verification',
208
+ authenticated: true,
209
+ needsPartnerIdHeader: true,
210
+ idempotent: false,
211
+ headers: { 'User-ID': params.userId },
212
+ multipart: (0, multipart_js_1.buildMultipart)(parts),
213
+ ...planExtras(opts),
214
+ });
215
+ return toAccepted(result.json);
216
+ }
217
+ async function biometricKyc(transport, params, opts) {
218
+ const parts = [
219
+ ...scalar('country', params.country),
220
+ ...scalar('id_type', params.idType),
221
+ ...scalar('id_number', params.idNumber),
222
+ ...scalar('sandbox_result', params.sandboxResult),
223
+ ...scalar('callback_url', effectiveCallback(params.callbackUrl, opts, transport)),
224
+ await jpegPart('selfie_image', params.selfieImage, 'selfie.jpg'),
225
+ ...(await livenessParts(params.livenessImages)),
226
+ ...jsonPart('user_details', userDetailsJson(params.userDetails)),
227
+ ...jsonPart('consent', consentJson(params.consent)),
228
+ ...optionalJson('partner_params', params.partnerParams),
229
+ ...optionalJson('metadata', params.metadata),
230
+ ];
231
+ const result = await transport.execute({
232
+ method: 'POST',
233
+ path: '/v3/biometric_kyc',
234
+ authenticated: true,
235
+ needsPartnerIdHeader: true,
236
+ idempotent: false,
237
+ headers: { 'User-ID': params.userId },
238
+ multipart: (0, multipart_js_1.buildMultipart)(parts),
239
+ ...planExtras(opts),
240
+ });
241
+ return toAccepted(result.json);
242
+ }
243
+ async function registration(transport, params, opts) {
244
+ const parts = [
245
+ ...scalar('allow_new_enroll', params.allowNewEnroll),
246
+ ...scalar('sandbox_result', params.sandboxResult),
247
+ ...scalar('callback_url', effectiveCallback(params.callbackUrl, opts, transport)),
248
+ await jpegPart('selfie_image', params.selfieImage, 'selfie.jpg'),
249
+ ...(await livenessParts(params.livenessImages)),
250
+ ...jsonPart('user_details', userDetailsJson(params.userDetails)),
251
+ ...jsonPart('consent', consentJson(params.consent)),
252
+ ...optionalJson('partner_params', params.partnerParams),
253
+ ...optionalJson('metadata', params.metadata),
254
+ ];
255
+ const result = await transport.execute({
256
+ method: 'POST',
257
+ path: '/v3/registration',
258
+ authenticated: true,
259
+ needsPartnerIdHeader: false,
260
+ idempotent: false,
261
+ headers: { 'User-ID': params.userId },
262
+ multipart: (0, multipart_js_1.buildMultipart)(parts),
263
+ ...planExtras(opts),
264
+ });
265
+ return toAccepted(result.json);
266
+ }
267
+ async function authentication(transport, params, opts) {
268
+ // user_id is a body field here (required), not the User-ID header (spec §6.6).
269
+ const parts = [
270
+ ...scalar('user_id', params.userId),
271
+ ...scalar('use_enrolled_image', params.useEnrolledImage),
272
+ ...scalar('sandbox_result', params.sandboxResult),
273
+ ...scalar('callback_url', effectiveCallback(params.callbackUrl, opts, transport)),
274
+ ...(params.selfieImage
275
+ ? [await jpegPart('selfie_image', params.selfieImage, 'selfie.jpg')]
276
+ : []),
277
+ ...(params.livenessImages ? await livenessParts(params.livenessImages) : []),
278
+ ...jsonPart('user_details', userDetailsJson(params.userDetails)),
279
+ ...jsonPart('consent', consentJson(params.consent)),
280
+ ...optionalJson('partner_params', params.partnerParams),
281
+ ...optionalJson('metadata', params.metadata),
282
+ ];
283
+ const result = await transport.execute({
284
+ method: 'POST',
285
+ path: '/v3/authentication',
286
+ authenticated: true,
287
+ needsPartnerIdHeader: false,
288
+ idempotent: false,
289
+ multipart: (0, multipart_js_1.buildMultipart)(parts),
290
+ ...planExtras(opts),
291
+ });
292
+ return toAccepted(result.json);
293
+ }
294
+ async function compare(transport, params, opts) {
295
+ // user_id is an optional body field here (spec §6.7).
296
+ const parts = [
297
+ ...scalar('comparison_image_type', params.comparisonImageType),
298
+ ...scalar('allow_new_enroll', params.allowNewEnroll),
299
+ ...scalar('user_id', params.userId),
300
+ ...scalar('sandbox_result', params.sandboxResult),
301
+ ...scalar('callback_url', effectiveCallback(params.callbackUrl, opts, transport)),
302
+ await jpegPart('selfie_image', params.selfieImage, 'selfie.jpg'),
303
+ await jpegPart('comparison_image', params.comparisonImage, 'comparison.jpg'),
304
+ ...(params.livenessImages ? await livenessParts(params.livenessImages) : []),
305
+ ...jsonPart('user_details', userDetailsJson(params.userDetails)),
306
+ ...jsonPart('consent', consentJson(params.consent)),
307
+ ...optionalJson('partner_params', params.partnerParams),
308
+ ...optionalJson('metadata', params.metadata),
309
+ ];
310
+ const result = await transport.execute({
311
+ method: 'POST',
312
+ path: '/v3/compare',
313
+ authenticated: true,
314
+ needsPartnerIdHeader: false,
315
+ idempotent: false,
316
+ multipart: (0, multipart_js_1.buildMultipart)(parts),
317
+ ...planExtras(opts),
318
+ });
319
+ return toAccepted(result.json);
320
+ }
321
+ // ---- status / replay / fraud ----------------------------------------------
322
+ async function verificationStatus(transport, jobId, opts) {
323
+ const result = await transport.execute({
324
+ method: 'GET',
325
+ path: `/v3/status/${encodeURIComponent(jobId)}`,
326
+ authenticated: true,
327
+ needsPartnerIdHeader: false,
328
+ idempotent: true,
329
+ allow404: true, // 404 returns a JobStatus body (spec §6.8), never raises.
330
+ ...planExtras(opts),
331
+ });
332
+ return toJobStatus(result.json);
333
+ }
334
+ async function replayCallback(transport, jobId, params, opts) {
335
+ const callbackUrl = effectiveCallback(params?.callbackUrl, opts, transport);
336
+ const result = await transport.execute({
337
+ method: 'POST',
338
+ path: `/v3/replay/${encodeURIComponent(jobId)}`,
339
+ authenticated: true,
340
+ needsPartnerIdHeader: false,
341
+ idempotent: false, // never auto-retried (spec §2.6, §6.10)
342
+ // Multipart per the corrected replay spec (other content types get a 415);
343
+ // no override means no body at all.
344
+ ...(callbackUrl
345
+ ? { multipart: (0, multipart_js_1.buildMultipart)([{ kind: 'scalar', name: 'callback_url', value: callbackUrl }]) }
346
+ : {}),
347
+ ...planExtras(opts),
348
+ });
349
+ return toAccepted(result.json);
350
+ }
351
+ async function reportFraud(transport, userId, params, opts) {
352
+ const parts = [
353
+ ...scalar('is_fraud', params.isFraud),
354
+ ...scalar('reported_by', params.reportedBy),
355
+ ...scalar('reason', params.reason),
356
+ ...scalar('notes', params.notes),
357
+ ];
358
+ const result = await transport.execute({
359
+ method: 'POST',
360
+ path: `/v3/users/${encodeURIComponent(userId)}/report_fraud`,
361
+ authenticated: true,
362
+ needsPartnerIdHeader: false,
363
+ idempotent: false, // never auto-retried (spec §2.6)
364
+ multipart: (0, multipart_js_1.buildMultipart)(parts),
365
+ ...planExtras(opts),
366
+ });
367
+ return toAccepted(result.json);
368
+ }
369
+ // ---- services --------------------------------------------------------------
370
+ async function bankCodes(transport, params, opts) {
371
+ const result = await transport.execute({
372
+ method: 'GET',
373
+ path: '/v3/services/bank_codes',
374
+ authenticated: false,
375
+ needsPartnerIdHeader: false,
376
+ idempotent: true,
377
+ query: { country: params?.country },
378
+ ...planExtras(opts),
379
+ });
380
+ return (0, case_js_1.camelizeKeys)(result.json);
381
+ }
382
+ async function supportedIdTypes(transport, params, opts) {
383
+ const result = await transport.execute({
384
+ method: 'GET',
385
+ path: '/v3/services/supported_id_types',
386
+ authenticated: false,
387
+ needsPartnerIdHeader: false,
388
+ idempotent: true,
389
+ query: { country: params?.country },
390
+ ...planExtras(opts),
391
+ });
392
+ return (0, case_js_1.camelizeKeys)(result.json);
393
+ }
394
+ async function supportedDocuments(transport, params, opts) {
395
+ const result = await transport.execute({
396
+ method: 'GET',
397
+ path: '/v3/services/supported_documents',
398
+ authenticated: false,
399
+ needsPartnerIdHeader: false,
400
+ idempotent: true,
401
+ query: {
402
+ continent: params?.continent,
403
+ country_code: params?.countryCode,
404
+ locale: params?.locale,
405
+ },
406
+ ...planExtras(opts),
407
+ });
408
+ return (0, case_js_1.camelizeKeys)(result.json);
409
+ }
410
+ async function idStatus(transport, params, opts) {
411
+ const result = await transport.execute({
412
+ method: 'GET',
413
+ path: '/v3/services/id_status',
414
+ authenticated: true,
415
+ needsPartnerIdHeader: false,
416
+ idempotent: true,
417
+ query: { country: params.country, id_type: params.idType },
418
+ ...planExtras(opts),
419
+ });
420
+ return (0, case_js_1.camelizeKeys)(result.json);
421
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Resolve a {@link BinaryInput} to raw bytes plus a filename and content type
3
+ * for a multipart part (spec §5.3, §8).
4
+ *
5
+ * Accepts a filesystem path, a Buffer/Uint8Array, a Blob, a readable stream, or
6
+ * a wrapper `{ data, filename?, contentType? }` to override the defaults.
7
+ */
8
+ import type { BinaryInput } from '../generated/models/index.js';
9
+ /** A binary part ready to serialize. */
10
+ export interface ResolvedBinary {
11
+ bytes: Buffer;
12
+ filename: string;
13
+ contentType: string;
14
+ }
15
+ /**
16
+ * Resolve a binary input to bytes + filename + content type.
17
+ *
18
+ * @param input the caller-supplied binary
19
+ * @param defaultName filename to use when the input does not carry one
20
+ * @param defaultType content type to use when the input does not carry one
21
+ */
22
+ export declare function resolveBinary(input: BinaryInput, defaultName: string, defaultType: string): Promise<ResolvedBinary>;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /**
3
+ * Resolve a {@link BinaryInput} to raw bytes plus a filename and content type
4
+ * for a multipart part (spec §5.3, §8).
5
+ *
6
+ * Accepts a filesystem path, a Buffer/Uint8Array, a Blob, a readable stream, or
7
+ * a wrapper `{ data, filename?, contentType? }` to override the defaults.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.resolveBinary = resolveBinary;
11
+ const promises_1 = require("node:fs/promises");
12
+ const node_path_1 = require("node:path");
13
+ const node_stream_1 = require("node:stream");
14
+ function isReadable(value) {
15
+ return (value instanceof node_stream_1.Readable ||
16
+ (typeof value === 'object' &&
17
+ value !== null &&
18
+ typeof value.pipe === 'function'));
19
+ }
20
+ async function streamToBuffer(stream) {
21
+ const chunks = [];
22
+ for await (const chunk of stream) {
23
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
24
+ }
25
+ return Buffer.concat(chunks);
26
+ }
27
+ async function resolveData(data) {
28
+ if (typeof data === 'string') {
29
+ return { bytes: await (0, promises_1.readFile)(data), filename: (0, node_path_1.basename)(data) };
30
+ }
31
+ if (Buffer.isBuffer(data)) {
32
+ return { bytes: data };
33
+ }
34
+ if (data instanceof Uint8Array) {
35
+ return { bytes: Buffer.from(data) };
36
+ }
37
+ if (typeof Blob !== 'undefined' && data instanceof Blob) {
38
+ const buf = Buffer.from(await data.arrayBuffer());
39
+ return { bytes: buf, contentType: data.type || undefined };
40
+ }
41
+ if (isReadable(data)) {
42
+ return { bytes: await streamToBuffer(data) };
43
+ }
44
+ throw new TypeError('Unsupported binary input type.');
45
+ }
46
+ /**
47
+ * Resolve a binary input to bytes + filename + content type.
48
+ *
49
+ * @param input the caller-supplied binary
50
+ * @param defaultName filename to use when the input does not carry one
51
+ * @param defaultType content type to use when the input does not carry one
52
+ */
53
+ async function resolveBinary(input, defaultName, defaultType) {
54
+ if (typeof input === 'object' &&
55
+ input !== null &&
56
+ 'data' in input &&
57
+ !Buffer.isBuffer(input) &&
58
+ !(input instanceof Uint8Array) &&
59
+ !(typeof Blob !== 'undefined' && input instanceof Blob) &&
60
+ !isReadable(input)) {
61
+ const wrapper = input;
62
+ const resolved = await resolveData(wrapper.data);
63
+ return {
64
+ bytes: resolved.bytes,
65
+ filename: wrapper.filename ?? resolved.filename ?? defaultName,
66
+ contentType: wrapper.contentType ?? resolved.contentType ?? defaultType,
67
+ };
68
+ }
69
+ const resolved = await resolveData(input);
70
+ return {
71
+ bytes: resolved.bytes,
72
+ filename: resolved.filename ?? defaultName,
73
+ contentType: resolved.contentType ?? defaultType,
74
+ };
75
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * snake_case ↔ camelCase conversion at the (de)serialization boundary (spec §8).
3
+ *
4
+ * The public API is camelCase; the wire is always snake_case. Requests map
5
+ * explicitly per field in the operation functions; responses are converted
6
+ * generically here.
7
+ */
8
+ /** Convert a single snake_case key to camelCase. */
9
+ export declare function snakeToCamel(key: string): string;
10
+ /** Recursively convert every object key in a parsed response to camelCase. */
11
+ export declare function camelizeKeys(value: unknown): unknown;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ /**
3
+ * snake_case ↔ camelCase conversion at the (de)serialization boundary (spec §8).
4
+ *
5
+ * The public API is camelCase; the wire is always snake_case. Requests map
6
+ * explicitly per field in the operation functions; responses are converted
7
+ * generically here.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.snakeToCamel = snakeToCamel;
11
+ exports.camelizeKeys = camelizeKeys;
12
+ /** Convert a single snake_case key to camelCase. */
13
+ function snakeToCamel(key) {
14
+ return key.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
15
+ }
16
+ /** Recursively convert every object key in a parsed response to camelCase. */
17
+ function camelizeKeys(value) {
18
+ if (Array.isArray(value)) {
19
+ return value.map(camelizeKeys);
20
+ }
21
+ if (value !== null && typeof value === 'object') {
22
+ const out = {};
23
+ for (const [k, v] of Object.entries(value)) {
24
+ out[snakeToCamel(k)] = camelizeKeys(v);
25
+ }
26
+ return out;
27
+ }
28
+ return value;
29
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Consent builder (spec §5.1, §8).
3
+ *
4
+ * `Consent.granted(...)` sets `granted: true` and normalizes the timestamp to
5
+ * an ISO 8601 string with milliseconds.
6
+ */
7
+ import type { Consent as ConsentShape } from '../generated/models/index.js';
8
+ /** Arguments to {@link Consent.granted}. */
9
+ export interface ConsentGrantedArgs {
10
+ /** A Date or an ISO 8601 string. */
11
+ grantedAt: Date | string;
12
+ /** ISO 639-1 language code, uppercase (e.g. "EN"). */
13
+ noticeLanguage: string;
14
+ noticePrivacyPolicyUrl: string;
15
+ }
16
+ /** Namespace object exposing the `granted` builder. */
17
+ export declare const Consent: {
18
+ /** Build a granted consent block (`granted: true`). */
19
+ granted(args: ConsentGrantedArgs): ConsentShape;
20
+ };
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ /**
3
+ * Consent builder (spec §5.1, §8).
4
+ *
5
+ * `Consent.granted(...)` sets `granted: true` and normalizes the timestamp to
6
+ * an ISO 8601 string with milliseconds.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.Consent = void 0;
10
+ /** Namespace object exposing the `granted` builder. */
11
+ exports.Consent = {
12
+ /** Build a granted consent block (`granted: true`). */
13
+ granted(args) {
14
+ const grantedAt = args.grantedAt instanceof Date ? args.grantedAt.toISOString() : args.grantedAt;
15
+ return {
16
+ granted: true,
17
+ grantedAt,
18
+ noticeLanguage: args.noticeLanguage,
19
+ noticePrivacyPolicyUrl: args.noticePrivacyPolicyUrl,
20
+ };
21
+ },
22
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Minimal JWT `exp` decoding for the token lifecycle (spec §2.3).
3
+ *
4
+ * The token response carries no explicit expiry, so the `exp` claim is the only
5
+ * signal. This reads it without verifying the signature (the SDK is the token's
6
+ * consumer, not its validator). Returns null when the claim cannot be decoded,
7
+ * which the caller treats as "refresh on the next call".
8
+ */
9
+ /** Decode the `exp` claim (seconds since epoch) from a JWT, or null. */
10
+ export declare function decodeJwtExp(jwt: string): number | null;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ /**
3
+ * Minimal JWT `exp` decoding for the token lifecycle (spec §2.3).
4
+ *
5
+ * The token response carries no explicit expiry, so the `exp` claim is the only
6
+ * signal. This reads it without verifying the signature (the SDK is the token's
7
+ * consumer, not its validator). Returns null when the claim cannot be decoded,
8
+ * which the caller treats as "refresh on the next call".
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.decodeJwtExp = decodeJwtExp;
12
+ /** Decode the `exp` claim (seconds since epoch) from a JWT, or null. */
13
+ function decodeJwtExp(jwt) {
14
+ const parts = jwt.split('.');
15
+ if (parts.length < 2)
16
+ return null;
17
+ try {
18
+ const payload = Buffer.from(parts[1], 'base64url').toString('utf8');
19
+ const claims = JSON.parse(payload);
20
+ if (claims && typeof claims === 'object') {
21
+ const exp = claims.exp;
22
+ if (typeof exp === 'number' && Number.isFinite(exp))
23
+ return exp;
24
+ }
25
+ return null;
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }