santoid-sdk 1.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 (46) hide show
  1. package/.eslintrc.js +39 -0
  2. package/LICENSE.md +177 -0
  3. package/README.md +126 -0
  4. package/dist/browser/liveness/index.js +1 -0
  5. package/dist/esm/liveness/index.d.ts +9 -0
  6. package/dist/esm/liveness/index.js +132 -0
  7. package/dist/esm/liveness/utils/getNextFrame.d.ts +6 -0
  8. package/dist/esm/liveness/utils/getNextFrame.js +55 -0
  9. package/dist/esm/liveness/utils/index.d.ts +1 -0
  10. package/dist/esm/liveness/utils/index.js +16 -0
  11. package/dist/esm/liveness/utils/types.d.ts +5 -0
  12. package/dist/esm/liveness/utils/types.js +17 -0
  13. package/dist/esm/liveness/utils/websocket.d.ts +3 -0
  14. package/dist/esm/liveness/utils/websocket.js +34 -0
  15. package/dist/index.d.ts +0 -0
  16. package/dist/index.js +0 -0
  17. package/dist/node/auth/index.d.ts +5 -0
  18. package/dist/node/auth/index.js +44 -0
  19. package/dist/node/liveness/index.d.ts +4 -0
  20. package/dist/node/liveness/index.js +98 -0
  21. package/dist/node/liveness/utils/index.d.ts +1 -0
  22. package/dist/node/liveness/utils/index.js +7 -0
  23. package/dist/node/liveness/utils/types.d.ts +6 -0
  24. package/dist/node/liveness/utils/types.js +17 -0
  25. package/dist/node/liveness/utils/websocket.d.ts +5 -0
  26. package/dist/node/liveness/utils/websocket.js +39 -0
  27. package/dist/utils/constants.d.ts +3 -0
  28. package/dist/utils/constants.js +6 -0
  29. package/dist/utils/types.d.ts +31 -0
  30. package/dist/utils/types.js +2 -0
  31. package/package.json +56 -0
  32. package/src/esm/liveness/index.ts +134 -0
  33. package/src/esm/liveness/utils/getNextFrame.ts +71 -0
  34. package/src/esm/liveness/utils/index.ts +16 -0
  35. package/src/esm/liveness/utils/types.ts +7 -0
  36. package/src/esm/liveness/utils/websocket.ts +33 -0
  37. package/src/index.ts +0 -0
  38. package/src/node/auth/index.ts +43 -0
  39. package/src/node/liveness/index.ts +112 -0
  40. package/src/node/liveness/utils/index.ts +3 -0
  41. package/src/node/liveness/utils/types.ts +8 -0
  42. package/src/node/liveness/utils/websocket.ts +35 -0
  43. package/src/utils/constants.ts +3 -0
  44. package/src/utils/types.ts +36 -0
  45. package/tsconfig.json +15 -0
  46. package/webpack.config.cjs +56 -0
@@ -0,0 +1,112 @@
1
+ import type WebSocket from 'ws'
2
+
3
+ import { type IWebSocketMessage, type TNodeSDKOptions } from './utils/types'
4
+ import { SEND_FRAMES_INTERVAL_MS } from '../../utils/constants'
5
+ import { connectToWebSocket, disconnectFromWebSocket } from './utils/websocket'
6
+
7
+ async function onWebSocketMessage (options: TNodeSDKOptions, event: { data: string }) {
8
+ const message = event.data
9
+
10
+ let messageObj: IWebSocketMessage | undefined
11
+
12
+ try {
13
+ messageObj = JSON.parse(message)
14
+ } catch (err) {
15
+ console.error('Cannot parse API message', err)
16
+ }
17
+
18
+ if (messageObj?.next_step && !messageObj.error) {
19
+ await options.nextStepCallback?.(messageObj.next_step)
20
+ await options[`${messageObj.next_step}Callback`]?.()
21
+ }
22
+
23
+ if (messageObj?.error) {
24
+ await options.errorCallback?.({ code: 'wrong-position' })
25
+ }
26
+
27
+ return messageObj
28
+ }
29
+
30
+ export async function livenessDetection (options: TNodeSDKOptions) {
31
+ let websocketClosedBySDK = false
32
+ let validationSuccess = false
33
+ let endCalled = false
34
+
35
+ async function stopLivenessDetection (options: TNodeSDKOptions, websocket?: WebSocket, intervalKey?: any) {
36
+ if (intervalKey) {
37
+ clearInterval(intervalKey)
38
+ }
39
+
40
+ if (websocket) {
41
+ disconnectFromWebSocket(websocket)
42
+ }
43
+
44
+ !endCalled && await options.endCallback?.()
45
+ endCalled = true
46
+
47
+ websocketClosedBySDK = true
48
+ }
49
+
50
+ let sendFramesIntervalKey: any
51
+ let websocket: WebSocket | undefined
52
+
53
+ try {
54
+ websocket = await connectToWebSocket(options)
55
+
56
+ await options.nextStepCallback?.('front')
57
+ await options.frontCallback?.()
58
+
59
+ let lastMessage: IWebSocketMessage | undefined
60
+
61
+ sendFramesIntervalKey = setInterval(async () => {
62
+ try {
63
+ if (lastMessage?.can_send_next_bytes) {
64
+ const bytes = await options.getNextFrame()
65
+
66
+ if (bytes) {
67
+ websocket?.send(bytes)
68
+ }
69
+ }
70
+ } catch (err) {
71
+ await stopLivenessDetection(options, websocket, sendFramesIntervalKey)
72
+ }
73
+ }, SEND_FRAMES_INTERVAL_MS)
74
+
75
+ websocket.on('message', async (data: string) => {
76
+ const parsedMessage = await onWebSocketMessage(options, { data: Buffer.from(data).toString() })
77
+
78
+ if (parsedMessage) {
79
+ lastMessage = parsedMessage
80
+ }
81
+
82
+ if (parsedMessage?.success) {
83
+ validationSuccess = true
84
+ await options.successCallback?.()
85
+ await stopLivenessDetection(options, websocket, sendFramesIntervalKey)
86
+ }
87
+ })
88
+
89
+ websocket.on('error', async (event) => {
90
+ !validationSuccess && options.errorCallback?.({ code: 'internal-error' })
91
+ await stopLivenessDetection(options, websocket, sendFramesIntervalKey)
92
+ })
93
+
94
+ websocket.on('close', async () => {
95
+ if (!websocketClosedBySDK) {
96
+ !validationSuccess && options.errorCallback?.({ code: 'internal-error' })
97
+ await stopLivenessDetection(options, websocket, sendFramesIntervalKey)
98
+ }
99
+ })
100
+
101
+ lastMessage = { can_send_next_bytes: true }
102
+
103
+ return {
104
+ stopLivenessDetection: async () => { await stopLivenessDetection(options, websocket, sendFramesIntervalKey) },
105
+ }
106
+ } catch (err) {
107
+ console.error('catch error', err)
108
+ !validationSuccess && options.errorCallback?.({ code: 'internal-error' })
109
+ await stopLivenessDetection(options, websocket, sendFramesIntervalKey)
110
+ throw err
111
+ }
112
+ }
@@ -0,0 +1,3 @@
1
+ export function decodeJwt (token: string) {
2
+ return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString())
3
+ }
@@ -0,0 +1,8 @@
1
+ import { type Data } from 'ws'
2
+ import { type ISDKOptions } from '../../../utils/types'
3
+
4
+ export type TNodeSDKOptions = ISDKOptions & {
5
+ getNextFrame: () => Promise<Data | null>
6
+ }
7
+
8
+ export * from '../../../utils/types'
@@ -0,0 +1,35 @@
1
+ import WebSocket from 'ws'
2
+
3
+ import { decodeJwt } from '.'
4
+ import { WEBSOCKET_BASE_URL } from '../../../utils/constants'
5
+ import { type IDecodedToken, type TNodeSDKOptions } from './types'
6
+
7
+ export async function waitForWebSocketConnection (websocket: WebSocket) {
8
+ await new Promise<void>((resolve) => {
9
+ const intervalKey = setInterval(() => {
10
+ if ([3, 1].includes(websocket.readyState)) {
11
+ clearInterval(intervalKey)
12
+ resolve()
13
+ }
14
+ }, 5)
15
+ })
16
+
17
+ if (websocket.readyState === 3) {
18
+ throw new Error('Cannot connect to server')
19
+ }
20
+ }
21
+
22
+ export async function connectToWebSocket (options: TNodeSDKOptions) {
23
+ const decodedToken: IDecodedToken = decodeJwt(options.token)
24
+ const query = `?x_endpoint_api_userinfo=${options.token}&domain=${decodedToken.claims.domain}&track=${options.track}&customerRequestId=${options.customId}`
25
+
26
+ const websocket = new WebSocket(`${options.customWebSocketUrl ?? WEBSOCKET_BASE_URL}${query}`)
27
+ await waitForWebSocketConnection(websocket)
28
+ return websocket
29
+ }
30
+
31
+ export function disconnectFromWebSocket (websocket: WebSocket) {
32
+ if (![2, 3].includes(websocket.readyState)) {
33
+ websocket.close?.()
34
+ }
35
+ }
@@ -0,0 +1,3 @@
1
+ export const WEBSOCKET_BASE_URL = 'ws://santoid-liveness-w4ynos5dlq-ue.a.run.app/ws'
2
+ export const API_GATEWAY_URL = 'https://api.santoid.com.br/api/v1/service-account/refresh-token'
3
+ export const SEND_FRAMES_INTERVAL_MS = 100
@@ -0,0 +1,36 @@
1
+ export type TCallbackFunction = (...args: any[]) => void | Promise<void>
2
+
3
+ export type TPositions = 'front' | 'right' | 'left' | 'up' | 'bottom'
4
+
5
+ export type TErrorCode = 'wrong-position' | 'internal-error'
6
+
7
+ export interface IDecodedToken {
8
+ claims: {
9
+ domain: string
10
+ }
11
+ }
12
+
13
+ export interface ISDKOptions {
14
+ token: string
15
+ track: string
16
+ customId?: string
17
+
18
+ nextStepCallback?: (step: TPositions) => void | Promise<void>
19
+ frontCallback?: TCallbackFunction
20
+ rightCallback?: TCallbackFunction
21
+ leftCallback?: TCallbackFunction
22
+ upCallback?: TCallbackFunction
23
+ bottomCallback?: TCallbackFunction
24
+ errorCallback?: (error: { code: TErrorCode }) => void | Promise<void>
25
+ successCallback?: TCallbackFunction
26
+ endCallback?: TCallbackFunction
27
+
28
+ customWebSocketUrl?: string
29
+ }
30
+
31
+ export interface IWebSocketMessage {
32
+ success?: boolean
33
+ error?: boolean
34
+ can_send_next_bytes?: boolean
35
+ next_step?: TPositions
36
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
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
+
14
+ "include": ["**/*.ts", "./.eslintrc.js", "./webpack.config.cjs"],
15
+ }
@@ -0,0 +1,56 @@
1
+ const path = require('path')
2
+
3
+ const { CleanWebpackPlugin } = require('clean-webpack-plugin')
4
+
5
+ const generalConfig = {
6
+ watchOptions: {
7
+ aggregateTimeout: 600,
8
+ ignored: /node_modules/,
9
+ },
10
+ plugins: [
11
+ new CleanWebpackPlugin({
12
+ cleanStaleWebpackAssets: false,
13
+ cleanOnceBeforeBuildPatterns: [
14
+ path.resolve(__dirname, './dist/browser/liveness'),
15
+ ],
16
+ }),
17
+ ],
18
+ module: {
19
+ rules: [
20
+ {
21
+ test: /\.tsx?$/,
22
+ use: 'ts-loader',
23
+ exclude: /node_modules/,
24
+ },
25
+ ],
26
+ },
27
+ resolve: {
28
+ extensions: ['.tsx', '.ts', '.js'],
29
+ },
30
+ }
31
+
32
+ const browserConfig = {
33
+ entry: './src/esm/liveness/index.ts',
34
+ target: 'web',
35
+ output: {
36
+ path: path.resolve(__dirname, './dist/browser/liveness'),
37
+ filename: 'index.js',
38
+ libraryTarget: 'umd',
39
+ globalObject: 'this',
40
+ libraryExport: 'default',
41
+ umdNamedDefine: true,
42
+ library: 'SantoiDSDK',
43
+ },
44
+ }
45
+
46
+ module.exports = (env, argv) => {
47
+ if (argv.mode === 'development') {
48
+ generalConfig.devtool = 'cheap-module-source-map'
49
+ } else if (argv.mode !== 'production') {
50
+ throw new Error('Specify env')
51
+ }
52
+
53
+ Object.assign(browserConfig, generalConfig)
54
+
55
+ return [browserConfig]
56
+ }