@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,49 @@
1
+ /**
2
+ * Multipart/form-data serialization (spec §5.3).
3
+ *
4
+ * Built by hand (rather than via the platform FormData) so the exact wire bytes
5
+ * are known: this lets the golden-fixture tests assert the precise structure
6
+ * and keeps JSON object parts free of the spurious `filename="blob"` the
7
+ * platform adds.
8
+ *
9
+ * Rules applied here:
10
+ * - scalar fields → plain text part;
11
+ * - object / array fields (consent, user_details, partner_params, metadata) →
12
+ * one part, body = JSON, Content-Type: application/json;
13
+ * - single binary fields → one binary part with a filename + content type;
14
+ * - binary arrays (liveness_images) → one repeated part per image, same name.
15
+ */
16
+ /** A scalar text part (e.g. country, id_type). */
17
+ export interface ScalarPart {
18
+ kind: 'scalar';
19
+ name: string;
20
+ value: string;
21
+ }
22
+ /** A JSON object/array part with Content-Type: application/json. */
23
+ export interface JsonPart {
24
+ kind: 'json';
25
+ name: string;
26
+ /** Pre-serialized JSON string. */
27
+ json: string;
28
+ }
29
+ /** A binary part with a filename and content type. */
30
+ export interface BinaryPart {
31
+ kind: 'binary';
32
+ name: string;
33
+ filename: string;
34
+ contentType: string;
35
+ bytes: Buffer;
36
+ }
37
+ export type MultipartPart = ScalarPart | JsonPart | BinaryPart;
38
+ /** The serialized body plus the Content-Type header value carrying the boundary. */
39
+ export interface SerializedMultipart {
40
+ body: Buffer;
41
+ contentType: string;
42
+ }
43
+ /**
44
+ * Strip characters from a caller-supplied filename that would break part
45
+ * framing (RFC 7578): CR, LF, and double quotes.
46
+ */
47
+ export declare function sanitizeFilename(filename: string): string;
48
+ /** Serialize parts into a multipart/form-data body (spec §5.3). */
49
+ export declare function buildMultipart(parts: MultipartPart[]): SerializedMultipart;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ /**
3
+ * Multipart/form-data serialization (spec §5.3).
4
+ *
5
+ * Built by hand (rather than via the platform FormData) so the exact wire bytes
6
+ * are known: this lets the golden-fixture tests assert the precise structure
7
+ * and keeps JSON object parts free of the spurious `filename="blob"` the
8
+ * platform adds.
9
+ *
10
+ * Rules applied here:
11
+ * - scalar fields → plain text part;
12
+ * - object / array fields (consent, user_details, partner_params, metadata) →
13
+ * one part, body = JSON, Content-Type: application/json;
14
+ * - single binary fields → one binary part with a filename + content type;
15
+ * - binary arrays (liveness_images) → one repeated part per image, same name.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.sanitizeFilename = sanitizeFilename;
19
+ exports.buildMultipart = buildMultipart;
20
+ const node_crypto_1 = require("node:crypto");
21
+ const index_js_1 = require("../errors/index.js");
22
+ const CRLF = '\r\n';
23
+ /** RFC 7230 media-type shape, e.g. image/jpeg or application/json. */
24
+ const SAFE_CONTENT_TYPE = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
25
+ /**
26
+ * Strip characters from a caller-supplied filename that would break part
27
+ * framing (RFC 7578): CR, LF, and double quotes.
28
+ */
29
+ function sanitizeFilename(filename) {
30
+ return filename.replace(/[\r\n"]/g, '_');
31
+ }
32
+ /** Reject content types that cannot be interpolated into a part header safely. */
33
+ function assertSafeContentType(contentType) {
34
+ if (!SAFE_CONTENT_TYPE.test(contentType)) {
35
+ throw new index_js_1.ValidationError({
36
+ message: `Invalid content type for a multipart part: ${JSON.stringify(contentType)}.`,
37
+ });
38
+ }
39
+ }
40
+ /** Serialize parts into a multipart/form-data body (spec §5.3). */
41
+ function buildMultipart(parts) {
42
+ const boundary = `----smileidFormBoundary${(0, node_crypto_1.randomBytes)(16).toString('hex')}`;
43
+ const segments = [];
44
+ for (const part of parts) {
45
+ let header = `--${boundary}${CRLF}`;
46
+ if (part.kind === 'binary') {
47
+ assertSafeContentType(part.contentType);
48
+ header += `Content-Disposition: form-data; name="${part.name}"; filename="${sanitizeFilename(part.filename)}"${CRLF}`;
49
+ header += `Content-Type: ${part.contentType}${CRLF}${CRLF}`;
50
+ segments.push(Buffer.from(header, 'utf8'), part.bytes, Buffer.from(CRLF, 'utf8'));
51
+ }
52
+ else if (part.kind === 'json') {
53
+ header += `Content-Disposition: form-data; name="${part.name}"${CRLF}`;
54
+ header += `Content-Type: application/json${CRLF}${CRLF}`;
55
+ segments.push(Buffer.from(header + part.json + CRLF, 'utf8'));
56
+ }
57
+ else {
58
+ header += `Content-Disposition: form-data; name="${part.name}"${CRLF}${CRLF}`;
59
+ segments.push(Buffer.from(header + part.value + CRLF, 'utf8'));
60
+ }
61
+ }
62
+ segments.push(Buffer.from(`--${boundary}--${CRLF}`, 'utf8'));
63
+ return {
64
+ body: Buffer.concat(segments),
65
+ contentType: `multipart/form-data; boundary=${boundary}`,
66
+ };
67
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Polling helper for verifications.waitUntilComplete (spec §6.9).
3
+ *
4
+ * Repeatedly calls the retrieve function until the job completes or the
5
+ * deadline passes. Defaults (interval 2s, timeout 60s) are SDK choices.
6
+ *
7
+ * A job is complete once its status is neither `processing` nor `not_found`,
8
+ * so the helper returns the terminal decision itself (`clear`, `block`,
9
+ * `attention` or `error`).
10
+ */
11
+ import type { JobStatus, WaitOptions } from '../generated/models/index.js';
12
+ /** Wait until a job reaches a terminal decision, or raise {@link TimeoutError}. */
13
+ export declare function waitUntilComplete(retrieve: () => Promise<JobStatus>, opts?: WaitOptions, sleep?: (ms: number) => Promise<void>): Promise<JobStatus>;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ /**
3
+ * Polling helper for verifications.waitUntilComplete (spec §6.9).
4
+ *
5
+ * Repeatedly calls the retrieve function until the job completes or the
6
+ * deadline passes. Defaults (interval 2s, timeout 60s) are SDK choices.
7
+ *
8
+ * A job is complete once its status is neither `processing` nor `not_found`,
9
+ * so the helper returns the terminal decision itself (`clear`, `block`,
10
+ * `attention` or `error`).
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.waitUntilComplete = waitUntilComplete;
14
+ const index_js_1 = require("../errors/index.js");
15
+ /** Wait until a job reaches a terminal decision, or raise {@link TimeoutError}. */
16
+ async function waitUntilComplete(retrieve, opts = {}, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
17
+ const interval = opts.interval ?? 2000;
18
+ const timeout = opts.timeout ?? 60000;
19
+ const treatNotFoundAsPending = opts.treatNotFoundAsPending ?? true;
20
+ const deadline = Date.now() + timeout;
21
+ for (;;) {
22
+ const status = await retrieve();
23
+ if (status.isComplete)
24
+ return status;
25
+ if (status.isNotFound && !treatNotFoundAsPending)
26
+ return status;
27
+ if (Date.now() >= deadline) {
28
+ throw new index_js_1.TimeoutError({
29
+ message: `Timed out after ${timeout}ms waiting for job to complete.`,
30
+ });
31
+ }
32
+ opts.signal?.throwIfAborted();
33
+ await sleep(interval);
34
+ }
35
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Client-side validation performed before a request is sent (spec §5.1, §6.6, §6.11).
3
+ *
4
+ * These raise {@link ValidationError} (a subtype of InvalidRequestError) so a
5
+ * bad call fails fast without a network round trip.
6
+ */
7
+ import type { AuthenticationParams, ReportFraudParams, UserDetails } from '../generated/models/index.js';
8
+ /**
9
+ * URLs given to the SDK must be absolute https URLs (fleet standard). Applies
10
+ * to the base URL (which additionally may not carry a query or fragment) and
11
+ * to callback URLs, both at construction and per request.
12
+ */
13
+ export declare function assertHttpsUrl(value: string, label: string, opts?: {
14
+ forbidQueryAndFragment?: boolean;
15
+ }): void;
16
+ /** user_details must carry at least one of email / phone_number (spec §5.1). */
17
+ export declare function validateUserDetails(userDetails: UserDetails): void;
18
+ /**
19
+ * report_fraud conditional rules (spec §6.11):
20
+ * - reason is required when isFraud is true;
21
+ * - notes is required when isFraud is false OR reason is OTHER.
22
+ */
23
+ export declare function validateReportFraud(params: ReportFraudParams): void;
24
+ /**
25
+ * authentication image rule (spec §6.6): unless useEnrolledImage is true,
26
+ * selfieImage and livenessImages are both required.
27
+ */
28
+ export declare function validateAuthentication(params: AuthenticationParams): void;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ /**
3
+ * Client-side validation performed before a request is sent (spec §5.1, §6.6, §6.11).
4
+ *
5
+ * These raise {@link ValidationError} (a subtype of InvalidRequestError) so a
6
+ * bad call fails fast without a network round trip.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.assertHttpsUrl = assertHttpsUrl;
10
+ exports.validateUserDetails = validateUserDetails;
11
+ exports.validateReportFraud = validateReportFraud;
12
+ exports.validateAuthentication = validateAuthentication;
13
+ const index_js_1 = require("../errors/index.js");
14
+ /**
15
+ * URLs given to the SDK must be absolute https URLs (fleet standard). Applies
16
+ * to the base URL (which additionally may not carry a query or fragment) and
17
+ * to callback URLs, both at construction and per request.
18
+ */
19
+ function assertHttpsUrl(value, label, opts = {}) {
20
+ let url;
21
+ try {
22
+ url = new URL(value);
23
+ }
24
+ catch {
25
+ throw new index_js_1.ValidationError({ message: `${label} must be an absolute https URL.` });
26
+ }
27
+ if (url.protocol !== 'https:') {
28
+ throw new index_js_1.ValidationError({ message: `${label} must use https.` });
29
+ }
30
+ if (opts.forbidQueryAndFragment && (url.search !== '' || url.hash !== '')) {
31
+ throw new index_js_1.ValidationError({
32
+ message: `${label} must not contain a query string or fragment.`,
33
+ });
34
+ }
35
+ }
36
+ /** user_details must carry at least one of email / phone_number (spec §5.1). */
37
+ function validateUserDetails(userDetails) {
38
+ const hasEmail = typeof userDetails.email === 'string' && userDetails.email.length > 0;
39
+ const hasPhone = typeof userDetails.phoneNumber === 'string' && userDetails.phoneNumber.length > 0;
40
+ if (!hasEmail && !hasPhone) {
41
+ throw new index_js_1.ValidationError({
42
+ message: 'userDetails requires at least one of email or phoneNumber.',
43
+ });
44
+ }
45
+ }
46
+ /**
47
+ * report_fraud conditional rules (spec §6.11):
48
+ * - reason is required when isFraud is true;
49
+ * - notes is required when isFraud is false OR reason is OTHER.
50
+ */
51
+ function validateReportFraud(params) {
52
+ const hasNotes = typeof params.notes === 'string' && params.notes.length > 0;
53
+ if (params.isFraud && !params.reason) {
54
+ throw new index_js_1.ValidationError({
55
+ message: 'reason is required when isFraud is true.',
56
+ });
57
+ }
58
+ if (!params.isFraud && !hasNotes) {
59
+ throw new index_js_1.ValidationError({
60
+ message: 'notes is required when isFraud is false.',
61
+ });
62
+ }
63
+ if (params.reason === 'OTHER' && !hasNotes) {
64
+ throw new index_js_1.ValidationError({
65
+ message: 'notes is required when reason is OTHER.',
66
+ });
67
+ }
68
+ if (typeof params.notes === 'string' && params.notes.length > 500) {
69
+ throw new index_js_1.ValidationError({ message: 'notes must be at most 500 characters.' });
70
+ }
71
+ }
72
+ /**
73
+ * authentication image rule (spec §6.6): unless useEnrolledImage is true,
74
+ * selfieImage and livenessImages are both required.
75
+ */
76
+ function validateAuthentication(params) {
77
+ if (params.useEnrolledImage === true)
78
+ return;
79
+ const hasSelfie = params.selfieImage !== undefined && params.selfieImage !== null;
80
+ const hasLiveness = Array.isArray(params.livenessImages) && params.livenessImages.length > 0;
81
+ if (!hasSelfie || !hasLiveness) {
82
+ throw new index_js_1.ValidationError({
83
+ message: 'selfieImage and livenessImages are required unless useEnrolledImage is true.',
84
+ });
85
+ }
86
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @smileid/usesmileid-nodejs — the official Smile ID server-side SDK for JavaScript/TypeScript.
3
+ *
4
+ * ```ts
5
+ * import { SmileID, Consent } from '@smileid/usesmileid-nodejs';
6
+ * ```
7
+ */
8
+ export { VERSION } from './version.js';
9
+ export { SmileID } from './client/client.js';
10
+ export type { SmileIDConfig, FetchLike } from './client/config.js';
11
+ export { Consent } from './helpers/consent.js';
12
+ export type { ConsentGrantedArgs } from './helpers/consent.js';
13
+ export { AcceptedResponse, JobStatus } from './generated/models/index.js';
14
+ export type { Environment, BinaryInput, RequestOptions, WaitOptions, Consent as ConsentType, UserDetails, PartnerParams, MetadataItem, EnhancedKycParams, DocumentVerificationParams, EnhancedDocumentVerificationParams, BiometricKycParams, RegistrationParams, AuthenticationParams, CompareParams, ComparisonImageType, ReplayParams, FraudReason, ReportFraudParams, FlagFraudParams, ClearFraudParams, BankCodesParams, BankCodesResponse, SupportedIdTypesParams, SupportedIdTypesResponse, SupportedDocumentsParams, SupportedDocumentsResponse, IdStatusParams, IdStatusResponse, } from './generated/models/index.js';
15
+ export { SmileIDError, InvalidRequestError, AuthenticationError, PaymentRequiredError, PermissionError, NotFoundError, ConflictError, PayloadTooLargeError, RateLimitError, APIError, ConnectionError, UnexpectedResponseError, TimeoutError, ValidationError, } from './errors/index.js';
16
+ export type { SmileIDErrorFields } from './errors/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ /**
3
+ * @smileid/usesmileid-nodejs — the official Smile ID server-side SDK for JavaScript/TypeScript.
4
+ *
5
+ * ```ts
6
+ * import { SmileID, Consent } from '@smileid/usesmileid-nodejs';
7
+ * ```
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.ValidationError = exports.TimeoutError = exports.UnexpectedResponseError = exports.ConnectionError = exports.APIError = exports.RateLimitError = exports.PayloadTooLargeError = exports.ConflictError = exports.NotFoundError = exports.PermissionError = exports.PaymentRequiredError = exports.AuthenticationError = exports.InvalidRequestError = exports.SmileIDError = exports.JobStatus = exports.AcceptedResponse = exports.Consent = exports.SmileID = exports.VERSION = void 0;
11
+ var version_js_1 = require("./version.js");
12
+ Object.defineProperty(exports, "VERSION", { enumerable: true, get: function () { return version_js_1.VERSION; } });
13
+ // Client + configuration.
14
+ var client_js_1 = require("./client/client.js");
15
+ Object.defineProperty(exports, "SmileID", { enumerable: true, get: function () { return client_js_1.SmileID; } });
16
+ // Builders.
17
+ var consent_js_1 = require("./helpers/consent.js");
18
+ Object.defineProperty(exports, "Consent", { enumerable: true, get: function () { return consent_js_1.Consent; } });
19
+ // Response models.
20
+ var index_js_1 = require("./generated/models/index.js");
21
+ Object.defineProperty(exports, "AcceptedResponse", { enumerable: true, get: function () { return index_js_1.AcceptedResponse; } });
22
+ Object.defineProperty(exports, "JobStatus", { enumerable: true, get: function () { return index_js_1.JobStatus; } });
23
+ // Error hierarchy.
24
+ var index_js_2 = require("./errors/index.js");
25
+ Object.defineProperty(exports, "SmileIDError", { enumerable: true, get: function () { return index_js_2.SmileIDError; } });
26
+ Object.defineProperty(exports, "InvalidRequestError", { enumerable: true, get: function () { return index_js_2.InvalidRequestError; } });
27
+ Object.defineProperty(exports, "AuthenticationError", { enumerable: true, get: function () { return index_js_2.AuthenticationError; } });
28
+ Object.defineProperty(exports, "PaymentRequiredError", { enumerable: true, get: function () { return index_js_2.PaymentRequiredError; } });
29
+ Object.defineProperty(exports, "PermissionError", { enumerable: true, get: function () { return index_js_2.PermissionError; } });
30
+ Object.defineProperty(exports, "NotFoundError", { enumerable: true, get: function () { return index_js_2.NotFoundError; } });
31
+ Object.defineProperty(exports, "ConflictError", { enumerable: true, get: function () { return index_js_2.ConflictError; } });
32
+ Object.defineProperty(exports, "PayloadTooLargeError", { enumerable: true, get: function () { return index_js_2.PayloadTooLargeError; } });
33
+ Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return index_js_2.RateLimitError; } });
34
+ Object.defineProperty(exports, "APIError", { enumerable: true, get: function () { return index_js_2.APIError; } });
35
+ Object.defineProperty(exports, "ConnectionError", { enumerable: true, get: function () { return index_js_2.ConnectionError; } });
36
+ Object.defineProperty(exports, "UnexpectedResponseError", { enumerable: true, get: function () { return index_js_2.UnexpectedResponseError; } });
37
+ Object.defineProperty(exports, "TimeoutError", { enumerable: true, get: function () { return index_js_2.TimeoutError; } });
38
+ Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return index_js_2.ValidationError; } });
@@ -0,0 +1 @@
1
+ export declare const VERSION = "12.0.0";
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VERSION = void 0;
4
+ exports.VERSION = '12.0.0';
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@smileid/usesmileid-nodejs",
3
+ "version": "12.0.0",
4
+ "description": "Official Smile ID server-side SDK for JavaScript/TypeScript — V3 APIs.",
5
+ "private": false,
6
+ "type": "commonjs",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "scripts": {
10
+ "prebuild": "rm -rf dist",
11
+ "build": "tsc",
12
+ "lint": "eslint .",
13
+ "test": "npm run build && node --test $(find dist -name '*.test.js')"
14
+ },
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/smileidentity/smileid-sdk-js.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/smileidentity/smileid-sdk-js/issues"
22
+ },
23
+ "homepage": "https://github.com/smileidentity/smileid-sdk-js#readme",
24
+ "keywords": [
25
+ "smile",
26
+ "identity",
27
+ "kyc",
28
+ "sdk"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "!dist/**/*.test.js",
36
+ "!dist/**/*.test.d.ts",
37
+ "!dist/testing"
38
+ ],
39
+ "devDependencies": {
40
+ "@types/node": "^22.10.0",
41
+ "eslint": "^9.39.2",
42
+ "globals": "^16.0.0",
43
+ "typescript": "^5.7.2",
44
+ "typescript-eslint": "^8.18.0"
45
+ }
46
+ }