santoid-sdk 1.0.5 → 1.0.6

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 (38) hide show
  1. package/README.md +1 -1
  2. package/dist/browser/liveness/index.js +1 -1
  3. package/dist/{src/esm → esm}/liveness/index.js +18 -1
  4. package/dist/node/liveness/index.js +18 -1
  5. package/dist/utils/types.d.ts +10 -3
  6. package/jest.config.cjs +17 -0
  7. package/package.json +4 -4
  8. package/src/esm/liveness/index.ts +26 -2
  9. package/src/node/liveness/index.ts +27 -2
  10. package/src/utils/types.ts +6 -4
  11. package/tsconfig.json +2 -1
  12. package/dist/jest.config.d.ts +0 -3
  13. package/dist/jest.config.js +0 -18
  14. package/dist/src/index.d.ts +0 -0
  15. package/dist/src/index.js +0 -0
  16. package/dist/src/node/auth/index.d.ts +0 -5
  17. package/dist/src/node/auth/index.js +0 -44
  18. package/dist/src/node/liveness/index.d.ts +0 -4
  19. package/dist/src/node/liveness/index.js +0 -98
  20. package/dist/src/node/liveness/utils/index.d.ts +0 -1
  21. package/dist/src/node/liveness/utils/index.js +0 -7
  22. package/dist/src/node/liveness/utils/types.d.ts +0 -6
  23. package/dist/src/node/liveness/utils/types.js +0 -17
  24. package/dist/src/node/liveness/utils/websocket.d.ts +0 -5
  25. package/dist/src/node/liveness/utils/websocket.js +0 -39
  26. package/dist/src/utils/constants.d.ts +0 -3
  27. package/dist/src/utils/constants.js +0 -6
  28. package/dist/src/utils/types.d.ts +0 -36
  29. package/dist/src/utils/types.js +0 -2
  30. /package/dist/{src/esm → esm}/liveness/index.d.ts +0 -0
  31. /package/dist/{src/esm → esm}/liveness/utils/getNextFrame.d.ts +0 -0
  32. /package/dist/{src/esm → esm}/liveness/utils/getNextFrame.js +0 -0
  33. /package/dist/{src/esm → esm}/liveness/utils/index.d.ts +0 -0
  34. /package/dist/{src/esm → esm}/liveness/utils/index.js +0 -0
  35. /package/dist/{src/esm → esm}/liveness/utils/types.d.ts +0 -0
  36. /package/dist/{src/esm → esm}/liveness/utils/types.js +0 -0
  37. /package/dist/{src/esm → esm}/liveness/utils/websocket.d.ts +0 -0
  38. /package/dist/{src/esm → esm}/liveness/utils/websocket.js +0 -0
package/README.md CHANGED
@@ -148,7 +148,7 @@ Ao importar a biblioteca via tag script, a função **livenessDetection** ficar
148
148
  <body>
149
149
  <button onclick="startLiveness()">Iniciar</button>
150
150
 
151
- <script src="https://cdn.jsdelivr.net/npm/santoid-sdk@1.0.5/dist/browser/liveness/index.js"></script>
151
+ <script src="https://cdn.jsdelivr.net/npm/santoid-sdk@1.0.6/dist/browser/liveness/index.js"></script>
152
152
 
153
153
  <script>
154
154
  async function startLiveness () {
@@ -1 +1 @@
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),c=o(692);async function l(e){var t,o,n;let a,l,s,d=!1,u=!1,v=!1,p=!0;async function f(e,t,o){var n;o&&clearInterval(o),t&&(0,c.disconnectFromWebSocket)(t),a&&a.stopGetNextFrame(),!v&&await(null===(n=e.endCallback)||void 0===n?void 0:n.call(e)),v=!0,d=!0}try{return s=await(0,c.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)()),l=setInterval((async()=>{try{if(p){let t;t=e.getNextFrame?await e.getNextFrame():await(null==a?void 0:a.getNextFrame()),t&&(null==s||s.send(t),p=!1)}}catch(t){await f(e,s,l)}}),r.SEND_FRAMES_INTERVAL_MS),s.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&&(p=!!(null==n?void 0:n.can_send_next_bytes)),(null==n?void 0:n.success)&&(u=!0,await(null===(o=e.successCallback)||void 0===o?void 0:o.call(e,{livenessId:n.livenessId})),await f(e,s,l))})),s.addEventListener("error",(async()=>{var t;!u&&(null===(t=e.errorCallback)||void 0===t||t.call(e,{code:"internal-error"})),await f(e,s,l)})),s.addEventListener("close",(async()=>{var t;d||(!u&&(null===(t=e.errorCallback)||void 0===t||t.call(e,{code:"internal-error"})),await f(e,s,l))})),{stopLivenessDetection:async()=>{await f(e,s,l)},videoStream:null==a?void 0:a.videoStream}}catch(t){throw!u&&(null===(n=e.errorCallback)||void 0===n||n.call(e,{code:"internal-error"})),await f(e,s,l),t}}a(o(722),t),t.livenessDetection=l;class s{}s.livenessDetection=l,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}`,c=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")}(c),c},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
+ !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})()));
@@ -45,6 +45,15 @@ async function livenessDetection(options) {
45
45
  let validationSuccess = false;
46
46
  let endCalled = false;
47
47
  let canSendNextBytes = true;
48
+ const successFrames = {
49
+ front: undefined,
50
+ right: undefined,
51
+ left: undefined,
52
+ up: undefined,
53
+ bottom: undefined,
54
+ };
55
+ let lastSentBytes;
56
+ let currentStep = 'front';
48
57
  async function stopLivenessDetection(options, websocket, intervalKey) {
49
58
  var _a;
50
59
  if (intervalKey) {
@@ -81,6 +90,7 @@ async function livenessDetection(options) {
81
90
  }
82
91
  if (bytes) {
83
92
  websocket === null || websocket === void 0 ? void 0 : websocket.send(bytes);
93
+ lastSentBytes = bytes;
84
94
  canSendNextBytes = false;
85
95
  }
86
96
  }
@@ -95,9 +105,16 @@ async function livenessDetection(options) {
95
105
  if (parsedMessage) {
96
106
  canSendNextBytes = !!(parsedMessage === null || parsedMessage === void 0 ? void 0 : parsedMessage.can_send_next_bytes);
97
107
  }
108
+ if ((parsedMessage === null || parsedMessage === void 0 ? void 0 : parsedMessage.validation) && currentStep) {
109
+ successFrames[currentStep] = lastSentBytes;
110
+ }
111
+ if (parsedMessage === null || parsedMessage === void 0 ? void 0 : parsedMessage.next_step) {
112
+ currentStep = parsedMessage.next_step;
113
+ }
98
114
  if (parsedMessage === null || parsedMessage === void 0 ? void 0 : parsedMessage.success) {
115
+ successFrames.bottom = lastSentBytes;
99
116
  validationSuccess = true;
100
- await ((_a = options.successCallback) === null || _a === void 0 ? void 0 : _a.call(options, { livenessId: parsedMessage.livenessId }));
117
+ await ((_a = options.successCallback) === null || _a === void 0 ? void 0 : _a.call(options, { livenessId: parsedMessage.livenessId, successFrames }));
101
118
  await stopLivenessDetection(options, websocket, sendFramesIntervalKey);
102
119
  }
103
120
  });
@@ -28,6 +28,15 @@ async function livenessDetection(options) {
28
28
  let validationSuccess = false;
29
29
  let endCalled = false;
30
30
  let canSendNextBytes = true;
31
+ const successFrames = {
32
+ front: undefined,
33
+ right: undefined,
34
+ left: undefined,
35
+ up: undefined,
36
+ bottom: undefined,
37
+ };
38
+ let lastSentBytes;
39
+ let currentStep = 'front';
31
40
  async function stopLivenessDetection(options, websocket, intervalKey) {
32
41
  var _a;
33
42
  if (intervalKey) {
@@ -52,6 +61,7 @@ async function livenessDetection(options) {
52
61
  const bytes = await options.getNextFrame();
53
62
  if (bytes) {
54
63
  websocket === null || websocket === void 0 ? void 0 : websocket.send(bytes);
64
+ lastSentBytes = bytes;
55
65
  canSendNextBytes = false;
56
66
  }
57
67
  }
@@ -66,9 +76,16 @@ async function livenessDetection(options) {
66
76
  if (parsedMessage) {
67
77
  canSendNextBytes = !!(parsedMessage === null || parsedMessage === void 0 ? void 0 : parsedMessage.can_send_next_bytes);
68
78
  }
79
+ if ((parsedMessage === null || parsedMessage === void 0 ? void 0 : parsedMessage.validation) && currentStep) {
80
+ successFrames[currentStep] = lastSentBytes;
81
+ }
82
+ if (parsedMessage === null || parsedMessage === void 0 ? void 0 : parsedMessage.next_step) {
83
+ currentStep = parsedMessage.next_step;
84
+ }
69
85
  if (parsedMessage === null || parsedMessage === void 0 ? void 0 : parsedMessage.success) {
86
+ successFrames.bottom = lastSentBytes;
70
87
  validationSuccess = true;
71
- await ((_a = options.successCallback) === null || _a === void 0 ? void 0 : _a.call(options));
88
+ await ((_a = options.successCallback) === null || _a === void 0 ? void 0 : _a.call(options, { livenessId: parsedMessage.livenessId, successFrames }));
72
89
  await stopLivenessDetection(options, websocket, sendFramesIntervalKey);
73
90
  }
74
91
  });
@@ -6,6 +6,10 @@ export interface IDecodedToken {
6
6
  domain: string;
7
7
  };
8
8
  }
9
+ interface ILivenessResults<FrameType> {
10
+ livenessId: string;
11
+ successFrames: Record<TPositions, FrameType>;
12
+ }
9
13
  export interface ISDKOptions {
10
14
  token: string;
11
15
  track: string;
@@ -20,13 +24,16 @@ export interface ISDKOptions {
20
24
  errorCallback?: (error: {
21
25
  code: TErrorCode;
22
26
  }) => void | Promise<void>;
23
- successCallback?: TCallbackFunction;
27
+ successCallback?: <FrameType>(results: ILivenessResults<FrameType>) => void | Promise<void>;
24
28
  endCallback?: TCallbackFunction;
25
29
  customWebSocketUrl?: string;
26
30
  }
27
31
  export interface IWebSocketMessage {
28
- success?: boolean;
29
- error?: boolean;
30
32
  can_send_next_bytes?: boolean;
31
33
  next_step?: TPositions;
34
+ validation?: boolean;
35
+ error?: boolean;
36
+ success?: boolean;
37
+ livenessId: string;
32
38
  }
39
+ export {};
@@ -0,0 +1,17 @@
1
+ const config = {
2
+ testEnvironment: 'jsdom',
3
+ transform: {
4
+ '^.+\\.ts?$': 'ts-jest',
5
+ },
6
+ collectCoverageFrom: [
7
+ '<rootDir>/src/**/*.ts',
8
+ ],
9
+ coveragePathIgnorePatterns: [
10
+ 'types.ts',
11
+ ],
12
+ setupFiles: [
13
+ 'jest-canvas-mock',
14
+ ],
15
+ }
16
+
17
+ module.exports = config
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "santoid-sdk",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
5
+ "main": "dist/esm/liveness/index.js",
6
+ "types": "dist/esm/liveness/index.d.ts",
7
7
  "scripts": {
8
8
  "expose:cmd": "mv -v dist/* ./",
9
9
  "dev:webpack": "webpack --mode=development --watch --progress",
10
10
  "build:browser-node": "webpack --mode=production",
11
- "build:typescript": "rm -rf ./dist/esm && tsc",
11
+ "build:typescript": "rm -rf ./dist && tsc",
12
12
  "build": "npm run build:typescript && npm run build:browser-node",
13
13
  "test": "jest",
14
14
  "test:coverage": "jest --coverage"
@@ -1,4 +1,4 @@
1
- import { type IWebSocketMessage, type TWebSDKOptions } from './utils/types'
1
+ import { type TPositions, type IWebSocketMessage, type TWebSDKOptions } from './utils/types'
2
2
  import { SEND_FRAMES_INTERVAL_MS } from '../../utils/constants'
3
3
  import { builtInGetNextFrame, type builtInGetNextFrameReturn } from './utils/getNextFrame'
4
4
 
@@ -30,12 +30,25 @@ async function onWebSocketMessage (options: TWebSDKOptions, event: MessageEvent)
30
30
  }
31
31
 
32
32
  export async function livenessDetection (options: TWebSDKOptions) {
33
+ type FrameType = Blob | null | undefined
34
+
33
35
  let websocketClosedBySDK = false
34
36
  let builtInGetNextFrameReturn: builtInGetNextFrameReturn | undefined
35
37
  let validationSuccess = false
36
38
  let endCalled = false
37
39
  let canSendNextBytes = true
38
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
+
39
52
  async function stopLivenessDetection (options: TWebSDKOptions, websocket?: WebSocket, intervalKey?: any) {
40
53
  if (intervalKey) {
41
54
  clearInterval(intervalKey)
@@ -81,6 +94,7 @@ export async function livenessDetection (options: TWebSDKOptions) {
81
94
 
82
95
  if (bytes) {
83
96
  websocket?.send(bytes)
97
+ lastSentBytes = bytes
84
98
  canSendNextBytes = false
85
99
  }
86
100
  }
@@ -96,9 +110,19 @@ export async function livenessDetection (options: TWebSDKOptions) {
96
110
  canSendNextBytes = !!parsedMessage?.can_send_next_bytes
97
111
  }
98
112
 
113
+ if (parsedMessage?.validation && currentStep) {
114
+ successFrames[currentStep] = lastSentBytes
115
+ }
116
+
117
+ if (parsedMessage?.next_step) {
118
+ currentStep = parsedMessage.next_step
119
+ }
120
+
99
121
  if (parsedMessage?.success) {
122
+ successFrames.bottom = lastSentBytes
123
+
100
124
  validationSuccess = true
101
- await options.successCallback?.({ livenessId: parsedMessage.livenessId })
125
+ await options.successCallback?.<FrameType>({ livenessId: parsedMessage.livenessId, successFrames })
102
126
  await stopLivenessDetection(options, websocket, sendFramesIntervalKey)
103
127
  }
104
128
  })
@@ -1,6 +1,7 @@
1
1
  import type WebSocket from 'ws'
2
+ import { type Data } from 'ws'
2
3
 
3
- import { type IWebSocketMessage, type TNodeSDKOptions } from './utils/types'
4
+ import { type TPositions, type IWebSocketMessage, type TNodeSDKOptions } from './utils/types'
4
5
  import { SEND_FRAMES_INTERVAL_MS } from '../../utils/constants'
5
6
  import { connectToWebSocket, disconnectFromWebSocket } from './utils/websocket'
6
7
 
@@ -28,11 +29,24 @@ async function onWebSocketMessage (options: TNodeSDKOptions, event: { data: stri
28
29
  }
29
30
 
30
31
  export async function livenessDetection (options: TNodeSDKOptions) {
32
+ type FrameType = Data | null | undefined
33
+
31
34
  let websocketClosedBySDK = false
32
35
  let validationSuccess = false
33
36
  let endCalled = false
34
37
  let canSendNextBytes = true
35
38
 
39
+ const successFrames: Record<TPositions, FrameType> = {
40
+ front: undefined,
41
+ right: undefined,
42
+ left: undefined,
43
+ up: undefined,
44
+ bottom: undefined,
45
+ }
46
+
47
+ let lastSentBytes: FrameType
48
+ let currentStep: TPositions = 'front'
49
+
36
50
  async function stopLivenessDetection (options: TNodeSDKOptions, websocket?: WebSocket, intervalKey?: any) {
37
51
  if (intervalKey) {
38
52
  clearInterval(intervalKey)
@@ -64,6 +78,7 @@ export async function livenessDetection (options: TNodeSDKOptions) {
64
78
 
65
79
  if (bytes) {
66
80
  websocket?.send(bytes)
81
+ lastSentBytes = bytes
67
82
  canSendNextBytes = false
68
83
  }
69
84
  }
@@ -79,9 +94,19 @@ export async function livenessDetection (options: TNodeSDKOptions) {
79
94
  canSendNextBytes = !!parsedMessage?.can_send_next_bytes
80
95
  }
81
96
 
97
+ if (parsedMessage?.validation && currentStep) {
98
+ successFrames[currentStep] = lastSentBytes
99
+ }
100
+
101
+ if (parsedMessage?.next_step) {
102
+ currentStep = parsedMessage.next_step
103
+ }
104
+
82
105
  if (parsedMessage?.success) {
106
+ successFrames.bottom = lastSentBytes
107
+
83
108
  validationSuccess = true
84
- await options.successCallback?.({ livenessId: parsedMessage.livenessId })
109
+ await options.successCallback?.<FrameType>({ livenessId: parsedMessage.livenessId, successFrames })
85
110
  await stopLivenessDetection(options, websocket, sendFramesIntervalKey)
86
111
  }
87
112
  })
@@ -10,8 +10,9 @@ export interface IDecodedToken {
10
10
  }
11
11
  }
12
12
 
13
- export interface ILivenessResults {
14
- livenessId?: string
13
+ interface ILivenessResults<FrameType> {
14
+ livenessId: string
15
+ successFrames: Record<TPositions, FrameType>
15
16
  }
16
17
 
17
18
  export interface ISDKOptions {
@@ -27,7 +28,7 @@ export interface ISDKOptions {
27
28
  upCallback?: TCallbackFunction
28
29
  bottomCallback?: TCallbackFunction
29
30
  errorCallback?: (error: { code: TErrorCode }) => void | Promise<void>
30
- successCallback?: (results: ILivenessResults) => void | Promise<void>
31
+ successCallback?: <FrameType>(results: ILivenessResults<FrameType>) => void | Promise<void>
31
32
  endCallback?: TCallbackFunction
32
33
 
33
34
  customWebSocketUrl?: string
@@ -36,9 +37,10 @@ export interface ISDKOptions {
36
37
  export interface IWebSocketMessage {
37
38
  can_send_next_bytes?: boolean
38
39
  next_step?: TPositions
40
+ validation?: boolean
39
41
 
40
42
  error?: boolean
41
43
 
42
44
  success?: boolean
43
- livenessId?: string
45
+ livenessId: string
44
46
  }
package/tsconfig.json CHANGED
@@ -5,11 +5,12 @@
5
5
  "target": "es2017",
6
6
  "moduleResolution": "node",
7
7
  "outDir": "dist",
8
+ "rootDir": "src",
8
9
  "baseUrl": "./",
9
10
  "declaration": true,
10
11
  "esModuleInterop": true,
11
12
  "strictNullChecks": true,
12
13
  },
13
14
 
14
- "include": ["**/*.ts", "./.eslintrc.js", "./webpack.config.cjs"],
15
+ "include": ["src/**/*.ts", "./.eslintrc.js", "./webpack.config.cjs", "./jest.config.cjs"],
15
16
  }
@@ -1,3 +0,0 @@
1
- import type { Config } from 'jest';
2
- declare const config: Config;
3
- export default config;
@@ -1,18 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const config = {
4
- testEnvironment: 'jsdom',
5
- transform: {
6
- '^.+\\.ts?$': 'ts-jest',
7
- },
8
- collectCoverageFrom: [
9
- '<rootDir>/src/**/*.ts',
10
- ],
11
- coveragePathIgnorePatterns: [
12
- 'types.ts',
13
- ],
14
- setupFiles: [
15
- 'jest-canvas-mock',
16
- ],
17
- };
18
- exports.default = config;
File without changes
package/dist/src/index.js DELETED
File without changes
@@ -1,5 +0,0 @@
1
- export interface IInitalizeOptions {
2
- customApiUrl?: string;
3
- credentials?: string | Record<string, string>;
4
- }
5
- export declare function initialize(options?: IInitalizeOptions): Promise<string | undefined>;
@@ -1,44 +0,0 @@
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.initialize = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const constants_1 = require("../../utils/constants");
9
- async function initialize(options) {
10
- var _a;
11
- let credentialsBase64;
12
- if (options === null || options === void 0 ? void 0 : options.credentials) {
13
- if (typeof (options === null || options === void 0 ? void 0 : options.credentials) === 'string') {
14
- credentialsBase64 = options === null || options === void 0 ? void 0 : options.credentials;
15
- }
16
- else if (typeof (options === null || options === void 0 ? void 0 : options.credentials) === 'object') {
17
- credentialsBase64 = Buffer.from(JSON.stringify(options === null || options === void 0 ? void 0 : options.credentials)).toString('base64');
18
- }
19
- else {
20
- throw new Error('Invalid credentials');
21
- }
22
- }
23
- else {
24
- if (process.env.SANTOID_SDK_CREDENTIALS) {
25
- credentialsBase64 = fs_1.default.readFileSync(process.env.SANTOID_SDK_CREDENTIALS, { encoding: 'base64' });
26
- }
27
- else {
28
- throw new Error('Credentials not found');
29
- }
30
- }
31
- if (credentialsBase64) {
32
- const response = await fetch((_a = options === null || options === void 0 ? void 0 : options.customApiUrl) !== null && _a !== void 0 ? _a : constants_1.API_GATEWAY_URL, {
33
- method: 'POST',
34
- body: JSON.stringify({ service_account_base64: credentialsBase64 }),
35
- headers: { 'Content-Type': 'application/json' },
36
- });
37
- const token = await response.json();
38
- if (typeof token !== 'string') {
39
- throw new Error('Cannot generate token');
40
- }
41
- return token;
42
- }
43
- }
44
- exports.initialize = initialize;
@@ -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,98 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.livenessDetection = void 0;
4
- const constants_1 = require("../../utils/constants");
5
- const websocket_1 = require("./utils/websocket");
6
- async function onWebSocketMessage(options, event) {
7
- var _a, _b, _c;
8
- const message = event.data;
9
- let messageObj;
10
- try {
11
- messageObj = JSON.parse(message);
12
- }
13
- catch (err) {
14
- console.error('Cannot parse API message', err);
15
- }
16
- if ((messageObj === null || messageObj === void 0 ? void 0 : messageObj.next_step) && !messageObj.error) {
17
- await ((_a = options.nextStepCallback) === null || _a === void 0 ? void 0 : _a.call(options, messageObj.next_step));
18
- await ((_b = options[`${messageObj.next_step}Callback`]) === null || _b === void 0 ? void 0 : _b.call(options));
19
- }
20
- if (messageObj === null || messageObj === void 0 ? void 0 : messageObj.error) {
21
- await ((_c = options.errorCallback) === null || _c === void 0 ? void 0 : _c.call(options, { code: 'wrong-position' }));
22
- }
23
- return messageObj;
24
- }
25
- async function livenessDetection(options) {
26
- var _a, _b, _c;
27
- let websocketClosedBySDK = false;
28
- let validationSuccess = false;
29
- let endCalled = false;
30
- let canSendNextBytes = true;
31
- async function stopLivenessDetection(options, websocket, intervalKey) {
32
- var _a;
33
- if (intervalKey) {
34
- clearInterval(intervalKey);
35
- }
36
- if (websocket) {
37
- (0, websocket_1.disconnectFromWebSocket)(websocket);
38
- }
39
- !endCalled && await ((_a = options.endCallback) === null || _a === void 0 ? void 0 : _a.call(options));
40
- endCalled = true;
41
- websocketClosedBySDK = true;
42
- }
43
- let sendFramesIntervalKey;
44
- let websocket;
45
- try {
46
- websocket = await (0, websocket_1.connectToWebSocket)(options);
47
- await ((_a = options.nextStepCallback) === null || _a === void 0 ? void 0 : _a.call(options, 'front'));
48
- await ((_b = options.frontCallback) === null || _b === void 0 ? void 0 : _b.call(options));
49
- sendFramesIntervalKey = setInterval(async () => {
50
- try {
51
- if (canSendNextBytes) {
52
- const bytes = await options.getNextFrame();
53
- if (bytes) {
54
- websocket === null || websocket === void 0 ? void 0 : websocket.send(bytes);
55
- canSendNextBytes = false;
56
- }
57
- }
58
- }
59
- catch (err) {
60
- await stopLivenessDetection(options, websocket, sendFramesIntervalKey);
61
- }
62
- }, constants_1.SEND_FRAMES_INTERVAL_MS);
63
- websocket.on('message', async (data) => {
64
- var _a;
65
- const parsedMessage = await onWebSocketMessage(options, { data: Buffer.from(data).toString() });
66
- if (parsedMessage) {
67
- canSendNextBytes = !!(parsedMessage === null || parsedMessage === void 0 ? void 0 : parsedMessage.can_send_next_bytes);
68
- }
69
- if (parsedMessage === null || parsedMessage === void 0 ? void 0 : parsedMessage.success) {
70
- validationSuccess = true;
71
- await ((_a = options.successCallback) === null || _a === void 0 ? void 0 : _a.call(options, { livenessId: parsedMessage.livenessId }));
72
- await stopLivenessDetection(options, websocket, sendFramesIntervalKey);
73
- }
74
- });
75
- websocket.on('error', async (event) => {
76
- var _a;
77
- !validationSuccess && ((_a = options.errorCallback) === null || _a === void 0 ? void 0 : _a.call(options, { code: 'internal-error' }));
78
- await stopLivenessDetection(options, websocket, sendFramesIntervalKey);
79
- });
80
- websocket.on('close', async () => {
81
- var _a;
82
- if (!websocketClosedBySDK) {
83
- !validationSuccess && ((_a = options.errorCallback) === null || _a === void 0 ? void 0 : _a.call(options, { code: 'internal-error' }));
84
- await stopLivenessDetection(options, websocket, sendFramesIntervalKey);
85
- }
86
- });
87
- return {
88
- stopLivenessDetection: async () => { await stopLivenessDetection(options, websocket, sendFramesIntervalKey); },
89
- };
90
- }
91
- catch (err) {
92
- console.error('catch error', err);
93
- !validationSuccess && ((_c = options.errorCallback) === null || _c === void 0 ? void 0 : _c.call(options, { code: 'internal-error' }));
94
- await stopLivenessDetection(options, websocket, sendFramesIntervalKey);
95
- throw err;
96
- }
97
- }
98
- exports.livenessDetection = livenessDetection;
@@ -1 +0,0 @@
1
- export declare function decodeJwt(token: string): any;
@@ -1,7 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.decodeJwt = void 0;
4
- function decodeJwt(token) {
5
- return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
6
- }
7
- exports.decodeJwt = decodeJwt;
@@ -1,6 +0,0 @@
1
- import { type Data } from 'ws';
2
- import { type ISDKOptions } from '../../../utils/types';
3
- export type TNodeSDKOptions = ISDKOptions & {
4
- getNextFrame: () => Promise<Data | null>;
5
- };
6
- export * from '../../../utils/types';
@@ -1,17 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("../../../utils/types"), exports);
@@ -1,5 +0,0 @@
1
- import WebSocket from 'ws';
2
- import { type TNodeSDKOptions } from './types';
3
- export declare function waitForWebSocketConnection(websocket: WebSocket): Promise<void>;
4
- export declare function connectToWebSocket(options: TNodeSDKOptions): Promise<WebSocket>;
5
- export declare function disconnectFromWebSocket(websocket: WebSocket): void;
@@ -1,39 +0,0 @@
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.disconnectFromWebSocket = exports.connectToWebSocket = exports.waitForWebSocketConnection = void 0;
7
- const ws_1 = __importDefault(require("ws"));
8
- const _1 = require(".");
9
- const constants_1 = require("../../../utils/constants");
10
- async function waitForWebSocketConnection(websocket) {
11
- await new Promise((resolve) => {
12
- const intervalKey = setInterval(() => {
13
- if ([3, 1].includes(websocket.readyState)) {
14
- clearInterval(intervalKey);
15
- resolve();
16
- }
17
- }, 5);
18
- });
19
- if (websocket.readyState === 3) {
20
- throw new Error('Cannot connect to server');
21
- }
22
- }
23
- exports.waitForWebSocketConnection = waitForWebSocketConnection;
24
- async function connectToWebSocket(options) {
25
- var _a, _b;
26
- const decodedToken = (0, _1.decodeJwt)(options.token);
27
- const query = `?x_endpoint_api_userinfo=${options.token}&domain=${(_a = options.domain) !== null && _a !== void 0 ? _a : decodedToken.claims.domain}&track=${options.track}&customerRequestId=${options.customId}`;
28
- const websocket = new ws_1.default(`${(_b = options.customWebSocketUrl) !== null && _b !== void 0 ? _b : constants_1.WEBSOCKET_BASE_URL}${query}`);
29
- await waitForWebSocketConnection(websocket);
30
- return websocket;
31
- }
32
- exports.connectToWebSocket = connectToWebSocket;
33
- function disconnectFromWebSocket(websocket) {
34
- var _a;
35
- if (![2, 3].includes(websocket.readyState)) {
36
- (_a = websocket.close) === null || _a === void 0 ? void 0 : _a.call(websocket);
37
- }
38
- }
39
- exports.disconnectFromWebSocket = disconnectFromWebSocket;
@@ -1,3 +0,0 @@
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";
3
- export declare const SEND_FRAMES_INTERVAL_MS = 100;
@@ -1,6 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SEND_FRAMES_INTERVAL_MS = exports.API_GATEWAY_URL = exports.WEBSOCKET_BASE_URL = void 0;
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';
6
- exports.SEND_FRAMES_INTERVAL_MS = 100;
@@ -1,36 +0,0 @@
1
- export type TCallbackFunction = (...args: any[]) => void | Promise<void>;
2
- export type TPositions = 'front' | 'right' | 'left' | 'up' | 'bottom';
3
- export type TErrorCode = 'wrong-position' | 'internal-error';
4
- export interface IDecodedToken {
5
- claims: {
6
- domain: string;
7
- };
8
- }
9
- export interface ILivenessResults {
10
- livenessId?: string;
11
- }
12
- export interface ISDKOptions {
13
- token: string;
14
- track: string;
15
- customId?: string;
16
- domain?: string;
17
- nextStepCallback?: (step: TPositions) => void | Promise<void>;
18
- frontCallback?: TCallbackFunction;
19
- rightCallback?: TCallbackFunction;
20
- leftCallback?: TCallbackFunction;
21
- upCallback?: TCallbackFunction;
22
- bottomCallback?: TCallbackFunction;
23
- errorCallback?: (error: {
24
- code: TErrorCode;
25
- }) => void | Promise<void>;
26
- successCallback?: (results: ILivenessResults) => void | Promise<void>;
27
- endCallback?: TCallbackFunction;
28
- customWebSocketUrl?: string;
29
- }
30
- export interface IWebSocketMessage {
31
- can_send_next_bytes?: boolean;
32
- next_step?: TPositions;
33
- error?: boolean;
34
- success?: boolean;
35
- livenessId?: string;
36
- }
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
File without changes
File without changes
File without changes
File without changes
File without changes