santoid-sdk 1.0.7 → 2.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.
Files changed (58) hide show
  1. package/README.md +113 -29
  2. package/browser/liveness/index.js +1 -0
  3. package/liveness/index.d.ts +9 -0
  4. package/liveness/index.js +29 -0
  5. package/liveness/methods/livenessDetection.d.ts +6 -0
  6. package/{dist/esm/liveness/index.js → liveness/methods/livenessDetection.js} +2 -22
  7. package/liveness/methods/uploadIdentificationDocument.d.ts +91 -0
  8. package/liveness/methods/uploadIdentificationDocument.js +236 -0
  9. package/liveness/utils/index.d.ts +2 -0
  10. package/{dist/esm/liveness → liveness}/utils/index.js +10 -1
  11. package/{dist/esm/liveness → liveness}/utils/types.d.ts +2 -2
  12. package/{dist/node/liveness → liveness}/utils/types.js +1 -1
  13. package/{dist/esm/liveness → liveness}/utils/websocket.js +1 -1
  14. package/{dist/node → node}/auth/index.js +3 -6
  15. package/node/liveness/index.d.ts +9 -0
  16. package/node/liveness/index.js +29 -0
  17. package/node/liveness/methods/livenessDetection.d.ts +6 -0
  18. package/{dist/node/liveness/index.js → node/liveness/methods/livenessDetection.js} +2 -3
  19. package/node/liveness/methods/uploadIdentificationDocument.d.ts +91 -0
  20. package/node/liveness/methods/uploadIdentificationDocument.js +237 -0
  21. package/node/liveness/utils/index.d.ts +2 -0
  22. package/{dist/node → node}/liveness/utils/index.js +10 -1
  23. package/package.json +12 -29
  24. package/tsconfig.json +6 -2
  25. package/tsconfig.prod.json +26 -0
  26. package/{dist/utils → utils}/constants.d.ts +1 -1
  27. package/{dist/utils → utils}/constants.js +1 -1
  28. package/{dist/utils → utils}/types.d.ts +11 -8
  29. package/webpack.config.cjs +12 -2
  30. package/dist/browser/liveness/index.js +0 -1
  31. package/dist/esm/liveness/index.d.ts +0 -9
  32. package/dist/esm/liveness/utils/index.d.ts +0 -1
  33. package/dist/node/liveness/index.d.ts +0 -4
  34. package/dist/node/liveness/utils/index.d.ts +0 -1
  35. package/src/esm/liveness/index.ts +0 -156
  36. package/src/esm/liveness/utils/getNextFrame.ts +0 -76
  37. package/src/esm/liveness/utils/index.ts +0 -16
  38. package/src/esm/liveness/utils/types.ts +0 -7
  39. package/src/esm/liveness/utils/websocket.ts +0 -33
  40. package/src/index.ts +0 -0
  41. package/src/node/auth/index.ts +0 -43
  42. package/src/node/liveness/index.ts +0 -135
  43. package/src/node/liveness/utils/index.ts +0 -3
  44. package/src/node/liveness/utils/types.ts +0 -8
  45. package/src/node/liveness/utils/websocket.ts +0 -35
  46. package/src/utils/constants.ts +0 -3
  47. package/src/utils/types.ts +0 -46
  48. /package/{dist/index.d.ts → index.d.ts} +0 -0
  49. /package/{dist/index.js → index.js} +0 -0
  50. /package/{dist/esm/liveness → liveness}/utils/getNextFrame.d.ts +0 -0
  51. /package/{dist/esm/liveness → liveness}/utils/getNextFrame.js +0 -0
  52. /package/{dist/esm/liveness → liveness}/utils/websocket.d.ts +0 -0
  53. /package/{dist/node → node}/auth/index.d.ts +0 -0
  54. /package/{dist/node → node}/liveness/utils/types.d.ts +0 -0
  55. /package/{dist/esm → node}/liveness/utils/types.js +0 -0
  56. /package/{dist/node → node}/liveness/utils/websocket.d.ts +0 -0
  57. /package/{dist/node → node}/liveness/utils/websocket.js +0 -0
  58. /package/{dist/utils → utils}/types.js +0 -0
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.uploadIdentificationDocument = void 0;
7
+ const form_data_1 = __importDefault(require("form-data"));
8
+ const axios_1 = __importDefault(require("axios"));
9
+ const constants_1 = require("../../../utils/constants");
10
+ const utils_1 = require("../utils");
11
+ const livenessDetection_1 = require("./livenessDetection");
12
+ async function typificationPredict(options, file) {
13
+ var _a, _b, _c;
14
+ const formData = new form_data_1.default();
15
+ formData.append('track', options.track);
16
+ formData.append('imageFile', file, 'imageFile.jpg');
17
+ const decodedToken = (0, utils_1.tryDecodeJwt)(options.token);
18
+ const domain = (_a = options.domain) !== null && _a !== void 0 ? _a : (_b = decodedToken === null || decodedToken === void 0 ? void 0 : decodedToken.claims) === null || _b === void 0 ? void 0 : _b.domain;
19
+ const axiosConfig = {
20
+ headers: {
21
+ 'Content-Type': 'multipart/form-data',
22
+ Authorization: `Bearer ${options.token}`,
23
+ },
24
+ };
25
+ if (axiosConfig.headers && domain) {
26
+ axiosConfig.headers.Domain = domain;
27
+ }
28
+ const response = await axios_1.default.post(`${(_c = options === null || options === void 0 ? void 0 : options.customApiUrl) !== null && _c !== void 0 ? _c : constants_1.API_GATEWAY_URL}/api/v3/typification/predict`, formData, axiosConfig);
29
+ return response.data;
30
+ }
31
+ async function faceMatch(options, firstImage, secondImage) {
32
+ var _a, _b, _c;
33
+ const formData = new form_data_1.default();
34
+ formData.append('track', options.track);
35
+ formData.append('firstImage', firstImage, 'firstImage.jpg');
36
+ formData.append('secondImage', secondImage, 'secondImage.jpg');
37
+ const decodedToken = (0, utils_1.tryDecodeJwt)(options.token);
38
+ const domain = (_a = options.domain) !== null && _a !== void 0 ? _a : (_b = decodedToken === null || decodedToken === void 0 ? void 0 : decodedToken.claims) === null || _b === void 0 ? void 0 : _b.domain;
39
+ const axiosConfig = {
40
+ params: {
41
+ sensitive_data_compliance: true,
42
+ },
43
+ headers: {
44
+ 'Content-Type': 'multipart/form-data',
45
+ Authorization: `Bearer ${options.token}`,
46
+ },
47
+ };
48
+ if (axiosConfig.headers && domain) {
49
+ axiosConfig.headers.Domain = domain;
50
+ }
51
+ const response = await axios_1.default.post(`${(_c = options === null || options === void 0 ? void 0 : options.customApiUrl) !== null && _c !== void 0 ? _c : constants_1.API_GATEWAY_URL}/api/v1/facematch/pair`, formData, axiosConfig);
52
+ return response.data;
53
+ }
54
+ function uploadIdentificationDocument(options) {
55
+ let results = {
56
+ uploadedDocument: null,
57
+ typification: {
58
+ status: null,
59
+ results: null,
60
+ loading: false,
61
+ error: null,
62
+ },
63
+ liveness: {
64
+ status: null,
65
+ results: null,
66
+ loading: false,
67
+ error: null,
68
+ },
69
+ faceMatch: {
70
+ status: null,
71
+ results: null,
72
+ loading: false,
73
+ error: null,
74
+ },
75
+ };
76
+ async function startFaceMatch(firstImage, secondImage) {
77
+ var _a, _b, _c;
78
+ try {
79
+ updateResults({
80
+ faceMatch: {
81
+ status: null,
82
+ results: null,
83
+ loading: true,
84
+ error: null,
85
+ },
86
+ });
87
+ const faceMatchResults = await faceMatch(options, firstImage, secondImage);
88
+ updateResults({
89
+ faceMatch: {
90
+ results: faceMatchResults,
91
+ status: 'success',
92
+ loading: false,
93
+ error: null,
94
+ },
95
+ });
96
+ }
97
+ catch (err) {
98
+ await ((_a = options.errorCallback) === null || _a === void 0 ? void 0 : _a.call(options, { code: 'face-match-error', details: (_b = err === null || err === void 0 ? void 0 : err.response) === null || _b === void 0 ? void 0 : _b.data }));
99
+ updateResults({
100
+ faceMatch: {
101
+ results: null,
102
+ status: 'error',
103
+ loading: false,
104
+ error: (_c = err === null || err === void 0 ? void 0 : err.response) === null || _c === void 0 ? void 0 : _c.data,
105
+ },
106
+ });
107
+ }
108
+ }
109
+ function tryStartFaceMatch() {
110
+ var _a;
111
+ if (results.uploadedDocument &&
112
+ ((_a = results.liveness.results) === null || _a === void 0 ? void 0 : _a.successFrames.front) &&
113
+ !results.faceMatch.results &&
114
+ !results.faceMatch.loading) {
115
+ void startFaceMatch(results.uploadedDocument, results.liveness.results.successFrames.front);
116
+ }
117
+ }
118
+ function updateUploadedDocument(file) {
119
+ results.uploadedDocument = file;
120
+ tryStartFaceMatch();
121
+ }
122
+ function updateResults(updatedResults) {
123
+ var _a, _b, _c;
124
+ results = Object.assign(Object.assign({}, results), updatedResults);
125
+ void ((_a = options.resultChangeCallback) === null || _a === void 0 ? void 0 : _a.call(options, results));
126
+ if (Object.entries(results).every(([key, value]) => key === 'uploadedDocument' || !!value.results)) {
127
+ void ((_b = options.successCallback) === null || _b === void 0 ? void 0 : _b.call(options, results));
128
+ void ((_c = options.endCallback) === null || _c === void 0 ? void 0 : _c.call(options));
129
+ return;
130
+ }
131
+ tryStartFaceMatch();
132
+ }
133
+ async function startDocumentUpload(file) {
134
+ var _a, _b, _c;
135
+ try {
136
+ updateResults({
137
+ typification: {
138
+ status: null,
139
+ results: null,
140
+ loading: true,
141
+ error: null,
142
+ },
143
+ });
144
+ updateUploadedDocument(file);
145
+ const typificationResults = await typificationPredict(options, file);
146
+ updateResults({
147
+ typification: {
148
+ status: 'success',
149
+ results: typificationResults,
150
+ loading: false,
151
+ error: null,
152
+ },
153
+ });
154
+ }
155
+ catch (err) {
156
+ await ((_a = options.errorCallback) === null || _a === void 0 ? void 0 : _a.call(options, { code: 'typification-error', details: (_b = err === null || err === void 0 ? void 0 : err.response) === null || _b === void 0 ? void 0 : _b.data }));
157
+ updateResults({
158
+ typification: {
159
+ status: 'error',
160
+ results: null,
161
+ loading: false,
162
+ error: (_c = err === null || err === void 0 ? void 0 : err.response) === null || _c === void 0 ? void 0 : _c.data,
163
+ },
164
+ });
165
+ }
166
+ }
167
+ async function startLivenessDetection(startLivenessDetectionOptions) {
168
+ var _a, _b, _c, _d, _e;
169
+ const adaptedLivenessOptions = Object.assign({}, startLivenessDetectionOptions);
170
+ delete adaptedLivenessOptions.successCallback;
171
+ delete adaptedLivenessOptions.errorCallback;
172
+ updateResults({
173
+ liveness: {
174
+ results: null,
175
+ status: null,
176
+ loading: true,
177
+ error: null,
178
+ },
179
+ });
180
+ try {
181
+ return await (0, livenessDetection_1.livenessDetection)(Object.assign(Object.assign({ domain: options.domain, customId: options.customId, customWebSocketUrl: options.customWebSocketUrl, successCallback: (livenessResults) => {
182
+ var _a;
183
+ void ((_a = startLivenessDetectionOptions === null || startLivenessDetectionOptions === void 0 ? void 0 : startLivenessDetectionOptions.successCallback) === null || _a === void 0 ? void 0 : _a.call(startLivenessDetectionOptions, livenessResults));
184
+ updateResults({
185
+ liveness: {
186
+ status: 'success',
187
+ results: (livenessResults),
188
+ loading: false,
189
+ error: null,
190
+ },
191
+ });
192
+ }, errorCallback: async (livenessErrorDetails) => {
193
+ var _a, _b;
194
+ if ((livenessErrorDetails === null || livenessErrorDetails === void 0 ? void 0 : livenessErrorDetails.code) === 'wrong-position') {
195
+ void ((_a = startLivenessDetectionOptions === null || startLivenessDetectionOptions === void 0 ? void 0 : startLivenessDetectionOptions.errorCallback) === null || _a === void 0 ? void 0 : _a.call(startLivenessDetectionOptions, livenessErrorDetails));
196
+ }
197
+ else {
198
+ void ((_b = options.errorCallback) === null || _b === void 0 ? void 0 : _b.call(options, livenessErrorDetails));
199
+ }
200
+ updateResults({
201
+ liveness: {
202
+ status: 'error',
203
+ results: null,
204
+ loading: (livenessErrorDetails === null || livenessErrorDetails === void 0 ? void 0 : livenessErrorDetails.code) === 'wrong-position',
205
+ error: livenessErrorDetails,
206
+ },
207
+ });
208
+ } }, adaptedLivenessOptions), { token: (_a = adaptedLivenessOptions.token) !== null && _a !== void 0 ? _a : options.token, track: (_b = adaptedLivenessOptions.track) !== null && _b !== void 0 ? _b : options.track }));
209
+ }
210
+ catch (err) {
211
+ await ((_c = options.errorCallback) === null || _c === void 0 ? void 0 : _c.call(options, { code: 'liveness-error', details: (_d = err === null || err === void 0 ? void 0 : err.response) === null || _d === void 0 ? void 0 : _d.data }));
212
+ updateResults({
213
+ liveness: {
214
+ status: 'error',
215
+ results: null,
216
+ loading: false,
217
+ error: (_e = err === null || err === void 0 ? void 0 : err.response) === null || _e === void 0 ? void 0 : _e.data,
218
+ },
219
+ });
220
+ }
221
+ }
222
+ function startAll(file, startLivenessDetectionOptions) {
223
+ void startDocumentUpload(file);
224
+ void startLivenessDetection(startLivenessDetectionOptions);
225
+ }
226
+ function getResults() {
227
+ return results;
228
+ }
229
+ return {
230
+ getResults,
231
+ startDocumentUpload,
232
+ startLivenessDetection,
233
+ startFaceComparison: tryStartFaceMatch,
234
+ startAll,
235
+ };
236
+ }
237
+ exports.uploadIdentificationDocument = uploadIdentificationDocument;
@@ -0,0 +1,2 @@
1
+ export declare function decodeJwt(token: string): any;
2
+ export declare function tryDecodeJwt(token: string): any;
@@ -1,7 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.decodeJwt = void 0;
3
+ exports.tryDecodeJwt = exports.decodeJwt = void 0;
4
4
  function decodeJwt(token) {
5
5
  return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
6
6
  }
7
7
  exports.decodeJwt = decodeJwt;
8
+ function tryDecodeJwt(token) {
9
+ try {
10
+ return decodeJwt(token);
11
+ }
12
+ catch (_a) {
13
+ return null;
14
+ }
15
+ }
16
+ exports.tryDecodeJwt = tryDecodeJwt;
package/package.json CHANGED
@@ -1,36 +1,17 @@
1
1
  {
2
2
  "name": "santoid-sdk",
3
- "version": "1.0.7",
3
+ "version": "2.0.0",
4
4
  "description": "",
5
- "main": "dist/esm/liveness/index.js",
6
- "types": "dist/esm/liveness/index.d.ts",
5
+ "main": "liveness/index.js",
6
+ "types": "liveness/index.d.ts",
7
7
  "scripts": {
8
- "expose:cmd": "mv -v dist/* ./",
9
- "dev:webpack": "webpack --mode=development --watch --progress",
10
- "build:browser-node": "webpack --mode=production",
11
- "build:typescript": "rm -rf ./dist && tsc",
12
- "build": "npm run build:typescript && npm run build:browser-node",
13
- "test": "jest",
14
- "test:coverage": "jest --coverage"
15
- },
16
- "exports": {
17
- ".": "./dist/index.js",
18
- "./liveness": "./dist/esm/liveness/index.js",
19
- "./node/liveness": "./dist/node/liveness/index.js",
20
- "./node/auth": "./dist/node/auth/index.js"
21
- },
22
- "typesVersions": {
23
- "*": {
24
- "liveness": [
25
- "dist/esm/liveness/index.d.ts"
26
- ],
27
- "node/liveness": [
28
- "dist/node/liveness/index.d.ts"
29
- ],
30
- "node/auth": [
31
- "dist/node/auth/index.d.ts"
32
- ]
33
- }
8
+ "expose:cmd": "mv -v dist/* ./ && rm -rf ./dist",
9
+ "build:clear": "rm -rf ./browser ./liveness ./node ./utils ./index.js ./index.d.ts ./dist",
10
+ "build:webpack": "webpack --mode=production",
11
+ "build:typescript": "npm run build:clear && tsc -p tsconfig.prod.json",
12
+ "build": "npm run build:typescript && npm run build:webpack && npm run expose:cmd",
13
+ "test": "jest --coverage false",
14
+ "test:coverage": "jest --coverage --runInBand"
34
15
  },
35
16
  "keywords": [],
36
17
  "author": "SantoDigital",
@@ -58,6 +39,8 @@
58
39
  "webpack-node-externals": "^3.0.0"
59
40
  },
60
41
  "dependencies": {
42
+ "axios": "^1.5.1",
43
+ "form-data": "^4.0.0",
61
44
  "ws": "^8.14.2"
62
45
  }
63
46
  }
package/tsconfig.json CHANGED
@@ -5,12 +5,16 @@
5
5
  "target": "es2017",
6
6
  "moduleResolution": "node",
7
7
  "outDir": "dist",
8
- "rootDir": "src",
9
8
  "baseUrl": "./",
10
9
  "declaration": true,
11
10
  "esModuleInterop": true,
12
11
  "strictNullChecks": true,
13
12
  },
14
13
 
15
- "include": ["src/**/*.ts", "./.eslintrc.js", "./webpack.config.cjs", "./jest.config.cjs"],
14
+ "include": [
15
+ "./.eslintrc.js",
16
+ "./webpack.config.cjs",
17
+ "./jest.config.cjs",
18
+ "**/*.ts",
19
+ ],
16
20
  }
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "typeRoots": ["@types", "./node_modules/@types"],
4
+ "module": "commonjs",
5
+ "target": "es2017",
6
+ "moduleResolution": "node",
7
+ "outDir": "dist",
8
+ "baseUrl": "./",
9
+ "declaration": true,
10
+ "esModuleInterop": true,
11
+ "strictNullChecks": true,
12
+
13
+ "rootDir": "src",
14
+ },
15
+
16
+ "include": [
17
+ "./.eslintrc.js",
18
+ "./webpack.config.cjs",
19
+ "./jest.config.cjs",
20
+ "src/**/*.ts",
21
+ ],
22
+
23
+ "exclude": [
24
+ "__tests__/**/*.ts"
25
+ ]
26
+ }
@@ -1,3 +1,3 @@
1
1
  export declare const WEBSOCKET_BASE_URL = "wss://santoid-liveness-w4ynos5dlq-ue.a.run.app/api/v1/liveness/ws";
2
- export declare const API_GATEWAY_URL = "https://api.santoid.com.br/api/v1/service-account/refresh-token";
2
+ export declare const API_GATEWAY_URL = "https://api.santoid.com.br";
3
3
  export declare const SEND_FRAMES_INTERVAL_MS = 100;
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SEND_FRAMES_INTERVAL_MS = exports.API_GATEWAY_URL = exports.WEBSOCKET_BASE_URL = void 0;
4
4
  exports.WEBSOCKET_BASE_URL = 'wss://santoid-liveness-w4ynos5dlq-ue.a.run.app/api/v1/liveness/ws';
5
- exports.API_GATEWAY_URL = 'https://api.santoid.com.br/api/v1/service-account/refresh-token';
5
+ exports.API_GATEWAY_URL = 'https://api.santoid.com.br';
6
6
  exports.SEND_FRAMES_INTERVAL_MS = 100;
@@ -1,32 +1,36 @@
1
1
  export type TCallbackFunction = (...args: any[]) => void | Promise<void>;
2
2
  export type TPositions = 'front' | 'right' | 'left' | 'up' | 'bottom';
3
- export type TErrorCode = 'wrong-position' | 'internal-error';
3
+ export type TErrorCode = 'wrong-position' | 'internal-error' | 'typification-error' | 'face-match-error' | 'liveness-error';
4
4
  export interface IDecodedToken {
5
5
  claims: {
6
6
  domain: string;
7
7
  };
8
8
  }
9
- interface ILivenessResults<FrameType> {
9
+ export interface ILivenessResults<FrameType> {
10
10
  livenessId: string;
11
11
  successFrames: Record<TPositions, FrameType>;
12
12
  }
13
- export interface ISDKOptions {
13
+ export interface IBaseSDKOptions {
14
14
  token: string;
15
15
  track: string;
16
16
  customId?: string;
17
17
  domain?: string;
18
- nextStepCallback?: (step: TPositions) => void | Promise<void>;
18
+ customWebSocketUrl?: string;
19
+ customApiUrl?: string;
20
+ }
21
+ export interface ISDKOptions extends IBaseSDKOptions {
22
+ nextStepCallback?: <TStepType = TPositions>(step: TStepType) => void | Promise<void>;
19
23
  frontCallback?: TCallbackFunction;
20
24
  rightCallback?: TCallbackFunction;
21
25
  leftCallback?: TCallbackFunction;
22
26
  upCallback?: TCallbackFunction;
23
27
  bottomCallback?: TCallbackFunction;
24
- errorCallback?: (error: {
28
+ errorCallback?: (error?: {
25
29
  code: TErrorCode;
30
+ details?: any;
26
31
  }) => void | Promise<void>;
27
- successCallback?: <FrameType>(results: ILivenessResults<FrameType>) => void | Promise<void>;
32
+ successCallback?: <FrameType, ResultsType = ILivenessResults<FrameType>>(results?: ResultsType) => void | Promise<void>;
28
33
  endCallback?: TCallbackFunction;
29
- customWebSocketUrl?: string;
30
34
  }
31
35
  export interface IWebSocketMessage {
32
36
  can_send_next_bytes?: boolean;
@@ -36,4 +40,3 @@ export interface IWebSocketMessage {
36
40
  success?: boolean;
37
41
  livenessId: string;
38
42
  }
39
- export {};
@@ -7,6 +7,7 @@ const generalConfig = {
7
7
  aggregateTimeout: 600,
8
8
  ignored: /node_modules/,
9
9
  },
10
+
10
11
  plugins: [
11
12
  new CleanWebpackPlugin({
12
13
  cleanStaleWebpackAssets: false,
@@ -15,22 +16,31 @@ const generalConfig = {
15
16
  ],
16
17
  }),
17
18
  ],
19
+
18
20
  module: {
19
21
  rules: [
20
22
  {
21
23
  test: /\.tsx?$/,
22
- use: 'ts-loader',
24
+ use: [
25
+ {
26
+ loader: 'ts-loader',
27
+ options: {
28
+ configFile: 'tsconfig.prod.json',
29
+ },
30
+ },
31
+ ],
23
32
  exclude: /node_modules/,
24
33
  },
25
34
  ],
26
35
  },
36
+
27
37
  resolve: {
28
38
  extensions: ['.tsx', '.ts', '.js'],
29
39
  },
30
40
  }
31
41
 
32
42
  const browserConfig = {
33
- entry: './src/esm/liveness/index.ts',
43
+ entry: './src/liveness/index.ts',
34
44
  target: 'web',
35
45
  output: {
36
46
  path: path.resolve(__dirname, './dist/browser/liveness'),
@@ -1 +0,0 @@
1
- !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("SantoiDSDK",[],t):"object"==typeof exports?exports.SantoiDSDK=t():e.SantoiDSDK=t()}(this,(()=>(()=>{"use strict";var e={401:function(e,t,o){var n=this&&this.__createBinding||(Object.create?function(e,t,o,n){void 0===n&&(n=o);var a=Object.getOwnPropertyDescriptor(t,o);a&&!("get"in a?!t.__esModule:a.writable||a.configurable)||(a={enumerable:!0,get:function(){return t[o]}}),Object.defineProperty(e,n,a)}:function(e,t,o,n){void 0===n&&(n=o),e[n]=t[o]}),a=this&&this.__exportStar||function(e,t){for(var o in e)"default"===o||Object.prototype.hasOwnProperty.call(t,o)||n(t,e,o)};Object.defineProperty(t,"__esModule",{value:!0}),t.livenessDetection=void 0;const r=o(553),i=o(793),l=o(692);async function c(e){var t,o,n;let a,c=!1,s=!1,d=!1,u=!0;const v={front:void 0,right:void 0,left:void 0,up:void 0,bottom:void 0};let p,f,_,b="front";async function m(e,t,o){var n;o&&clearInterval(o),t&&(0,l.disconnectFromWebSocket)(t),a&&a.stopGetNextFrame(),!d&&await(null===(n=e.endCallback)||void 0===n?void 0:n.call(e)),d=!0,c=!0}try{return _=await(0,l.connectToWebSocket)(e),await(null===(t=e.nextStepCallback)||void 0===t?void 0:t.call(e,"front")),await(null===(o=e.frontCallback)||void 0===o?void 0:o.call(e)),e.getNextFrame||a||(a=await(0,i.builtInGetNextFrame)()),f=setInterval((async()=>{try{if(u){let t;t=e.getNextFrame?await e.getNextFrame():await(null==a?void 0:a.getNextFrame()),t&&(null==_||_.send(t),p=t,u=!1)}}catch(t){await m(e,_,f)}}),r.SEND_FRAMES_INTERVAL_MS),_.addEventListener("message",(async t=>{var o;const n=await async function(e,t){var o,n,a;const r=t.data;let i;try{i=JSON.parse(r)}catch(e){console.error("Cannot parse API message",e)}return(null==i?void 0:i.next_step)&&!i.error&&(await(null===(o=e.nextStepCallback)||void 0===o?void 0:o.call(e,i.next_step)),await(null===(n=e[`${i.next_step}Callback`])||void 0===n?void 0:n.call(e))),(null==i?void 0:i.error)&&await(null===(a=e.errorCallback)||void 0===a?void 0:a.call(e,{code:"wrong-position"})),i}(e,t);n&&(u=!!(null==n?void 0:n.can_send_next_bytes)),(null==n?void 0:n.validation)&&b&&(v[b]=p),(null==n?void 0:n.next_step)&&(b=n.next_step),(null==n?void 0:n.success)&&(v.bottom=p,s=!0,await(null===(o=e.successCallback)||void 0===o?void 0:o.call(e,{livenessId:n.livenessId,successFrames:v})),await m(e,_,f))})),_.addEventListener("error",(async()=>{var t;!s&&(null===(t=e.errorCallback)||void 0===t||t.call(e,{code:"internal-error"})),await m(e,_,f)})),_.addEventListener("close",(async()=>{var t;c||(!s&&(null===(t=e.errorCallback)||void 0===t||t.call(e,{code:"internal-error"})),await m(e,_,f))})),{stopLivenessDetection:async()=>{await m(e,_,f)},videoStream:null==a?void 0:a.videoStream}}catch(t){throw!s&&(null===(n=e.errorCallback)||void 0===n||n.call(e,{code:"internal-error"})),await m(e,_,f),t}}a(o(722),t),t.livenessDetection=c;class s{}s.livenessDetection=c,t.default=s},793:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.builtInGetNextFrame=void 0,t.builtInGetNextFrame=async function(){let e,t,o,n;return e&&t&&o||(e=await navigator.mediaDevices.getUserMedia({video:!0}),t=document.createElement("video"),t.setAttribute("autoplay","true"),t.style.transform="scale(-1, 1)",t.style.position="absolute",t.style.left="99999px",document.body.appendChild(t),t.srcObject=e,o=document.createElement("canvas"),n=o.getContext("2d")),{getNextFrame:async function(){if(o&&t)return o.width=t.videoWidth,o.height=t.videoHeight,null==n||n.drawImage(t,0,0,o.width,o.height),await new Promise(((e,t)=>{o?o.toBlob((t=>{e(t)})):e(null)}));throw new Error("Cannot found video element")},stopGetNextFrame:function(){t&&(document.body.removeChild(t),t=void 0),o&&(o=void 0),n&&(n=void 0),null==e||e.getTracks().forEach((e=>{e.stop()})),e=void 0},videoStream:e}}},268:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.decodeJwt=void 0,t.decodeJwt=function(e){const t=e.split(".")[1].replace(/-/g,"+").replace(/_/g,"/"),o=decodeURIComponent(window.atob(t).split("").map((e=>"%"+("00"+e.charCodeAt(0).toString(16)).slice(-2))).join(""));return JSON.parse(o)}},722:function(e,t,o){var n=this&&this.__createBinding||(Object.create?function(e,t,o,n){void 0===n&&(n=o);var a=Object.getOwnPropertyDescriptor(t,o);a&&!("get"in a?!t.__esModule:a.writable||a.configurable)||(a={enumerable:!0,get:function(){return t[o]}}),Object.defineProperty(e,n,a)}:function(e,t,o,n){void 0===n&&(n=o),e[n]=t[o]}),a=this&&this.__exportStar||function(e,t){for(var o in e)"default"===o||Object.prototype.hasOwnProperty.call(t,o)||n(t,e,o)};Object.defineProperty(t,"__esModule",{value:!0}),a(o(307),t)},692:(e,t,o)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.disconnectFromWebSocket=t.connectToWebSocket=void 0;const n=o(268),a=o(553);t.connectToWebSocket=async function(e){var t,o;const r=(0,n.decodeJwt)(e.token),i=`?x_endpoint_api_userinfo=${e.token}&domain=${null!==(t=e.domain)&&void 0!==t?t:r.claims.domain}&track=${e.track}&customerRequestId=${e.customId}`,l=new WebSocket(`${null!==(o=e.customWebSocketUrl)&&void 0!==o?o:a.WEBSOCKET_BASE_URL}${i}`);return await async function(e){if(await new Promise((t=>{const o=setInterval((()=>{[3,1].includes(e.readyState)&&(clearInterval(o),t())}),5)})),3===e.readyState)throw new Error("Cannot connect to server")}(l),l},t.disconnectFromWebSocket=function(e){var t;[2,3].includes(e.readyState)||null===(t=e.close)||void 0===t||t.call(e)}},553:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SEND_FRAMES_INTERVAL_MS=t.API_GATEWAY_URL=t.WEBSOCKET_BASE_URL=void 0,t.WEBSOCKET_BASE_URL="wss://santoid-liveness-w4ynos5dlq-ue.a.run.app/api/v1/liveness/ws",t.API_GATEWAY_URL="https://api.santoid.com.br/api/v1/service-account/refresh-token",t.SEND_FRAMES_INTERVAL_MS=100},307:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0})}},t={},o=function o(n){var a=t[n];if(void 0!==a)return a.exports;var r=t[n]={exports:{}};return e[n].call(r.exports,r,r.exports,o),r.exports}(401);return o.default})()));
@@ -1,9 +0,0 @@
1
- import { type TWebSDKOptions } from './utils/types';
2
- export * from './utils/types';
3
- export declare function livenessDetection(options: TWebSDKOptions): Promise<{
4
- stopLivenessDetection: () => Promise<void>;
5
- videoStream: MediaStream | undefined;
6
- }>;
7
- export default class SantoiDSDK {
8
- static livenessDetection: typeof livenessDetection;
9
- }
@@ -1 +0,0 @@
1
- export declare function decodeJwt(token: string): any;
@@ -1,4 +0,0 @@
1
- import { type TNodeSDKOptions } from './utils/types';
2
- export declare function livenessDetection(options: TNodeSDKOptions): Promise<{
3
- stopLivenessDetection: () => Promise<void>;
4
- }>;
@@ -1 +0,0 @@
1
- export declare function decodeJwt(token: string): any;
@@ -1,156 +0,0 @@
1
- import { type TPositions, type IWebSocketMessage, type TWebSDKOptions } from './utils/types'
2
- import { SEND_FRAMES_INTERVAL_MS } from '../../utils/constants'
3
- import { builtInGetNextFrame, type builtInGetNextFrameReturn } from './utils/getNextFrame'
4
-
5
- import { connectToWebSocket, disconnectFromWebSocket } from './utils/websocket'
6
-
7
- export * from './utils/types'
8
-
9
- async function onWebSocketMessage (options: TWebSDKOptions, event: MessageEvent) {
10
- const message = event.data
11
-
12
- let messageObj: IWebSocketMessage | undefined
13
-
14
- try {
15
- messageObj = JSON.parse(message)
16
- } catch (err) {
17
- console.error('Cannot parse API message', err)
18
- }
19
-
20
- if (messageObj?.next_step && !messageObj.error) {
21
- await options.nextStepCallback?.(messageObj.next_step)
22
- await options[`${messageObj.next_step}Callback`]?.()
23
- }
24
-
25
- if (messageObj?.error) {
26
- await options.errorCallback?.({ code: 'wrong-position' })
27
- }
28
-
29
- return messageObj
30
- }
31
-
32
- export async function livenessDetection (options: TWebSDKOptions) {
33
- type FrameType = Blob | null | undefined
34
-
35
- let websocketClosedBySDK = false
36
- let builtInGetNextFrameReturn: builtInGetNextFrameReturn | undefined
37
- let validationSuccess = false
38
- let endCalled = false
39
- let canSendNextBytes = true
40
-
41
- const successFrames: Record<TPositions, FrameType> = {
42
- front: undefined,
43
- right: undefined,
44
- left: undefined,
45
- up: undefined,
46
- bottom: undefined,
47
- }
48
-
49
- let lastSentBytes: FrameType
50
- let currentStep: TPositions = 'front'
51
-
52
- async function stopLivenessDetection (options: TWebSDKOptions, websocket?: WebSocket, intervalKey?: any) {
53
- if (intervalKey) {
54
- clearInterval(intervalKey)
55
- }
56
-
57
- if (websocket) {
58
- disconnectFromWebSocket(websocket)
59
- }
60
-
61
- if (builtInGetNextFrameReturn) {
62
- builtInGetNextFrameReturn.stopGetNextFrame()
63
- }
64
-
65
- !endCalled && await options.endCallback?.()
66
- endCalled = true
67
-
68
- websocketClosedBySDK = true
69
- }
70
-
71
- let sendFramesIntervalKey: any
72
- let websocket: WebSocket | undefined
73
-
74
- try {
75
- websocket = await connectToWebSocket(options)
76
-
77
- await options.nextStepCallback?.('front')
78
- await options.frontCallback?.()
79
-
80
- if (!options.getNextFrame && !builtInGetNextFrameReturn) {
81
- builtInGetNextFrameReturn = await builtInGetNextFrame()
82
- }
83
-
84
- sendFramesIntervalKey = setInterval(async () => {
85
- try {
86
- if (canSendNextBytes) {
87
- let bytes
88
-
89
- if (options.getNextFrame) {
90
- bytes = await options.getNextFrame()
91
- } else {
92
- bytes = await builtInGetNextFrameReturn?.getNextFrame()
93
- }
94
-
95
- if (bytes) {
96
- websocket?.send(bytes)
97
- lastSentBytes = bytes
98
- canSendNextBytes = false
99
- }
100
- }
101
- } catch (err) {
102
- await stopLivenessDetection(options, websocket, sendFramesIntervalKey)
103
- }
104
- }, SEND_FRAMES_INTERVAL_MS)
105
-
106
- websocket.addEventListener('message', async (event) => {
107
- const parsedMessage = await onWebSocketMessage(options, event)
108
-
109
- if (parsedMessage) {
110
- canSendNextBytes = !!parsedMessage?.can_send_next_bytes
111
- }
112
-
113
- if (parsedMessage?.validation && currentStep) {
114
- successFrames[currentStep] = lastSentBytes
115
- }
116
-
117
- if (parsedMessage?.next_step) {
118
- currentStep = parsedMessage.next_step
119
- }
120
-
121
- if (parsedMessage?.success) {
122
- successFrames.bottom = lastSentBytes
123
-
124
- validationSuccess = true
125
- await options.successCallback?.<FrameType>({ livenessId: parsedMessage.livenessId, successFrames })
126
- await stopLivenessDetection(options, websocket, sendFramesIntervalKey)
127
- }
128
- })
129
-
130
- websocket.addEventListener('error', async () => {
131
- !validationSuccess && options.errorCallback?.({ code: 'internal-error' })
132
- await stopLivenessDetection(options, websocket, sendFramesIntervalKey)
133
- })
134
-
135
- websocket.addEventListener('close', async () => {
136
- if (!websocketClosedBySDK) {
137
- !validationSuccess && options.errorCallback?.({ code: 'internal-error' })
138
- await stopLivenessDetection(options, websocket, sendFramesIntervalKey)
139
- }
140
- })
141
-
142
- return {
143
- stopLivenessDetection: async () => { await stopLivenessDetection(options, websocket, sendFramesIntervalKey) },
144
- videoStream: builtInGetNextFrameReturn?.videoStream,
145
- }
146
- } catch (err) {
147
- !validationSuccess && options.errorCallback?.({ code: 'internal-error' })
148
- await stopLivenessDetection(options, websocket, sendFramesIntervalKey)
149
- throw err
150
- }
151
- }
152
-
153
- // Class for use in browsers
154
- export default class SantoiDSDK {
155
- static livenessDetection = livenessDetection
156
- }