@easyflow/javascript-sdk 2.1.7

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 (93) hide show
  1. package/.babelrc +5 -0
  2. package/.github/workflows/deploy-sdk-cf.yml +49 -0
  3. package/.github/workflows/release-sdk-cdn.yml +144 -0
  4. package/.github/workflows/release-sdk.yml +112 -0
  5. package/.prettierrc +6 -0
  6. package/CDN-DEPLOYMENT.md +175 -0
  7. package/DEMO.md +258 -0
  8. package/DEPLOYMENT.md +224 -0
  9. package/INTEGRATION-GUIDE.md +521 -0
  10. package/README.md +1013 -0
  11. package/coverage/base.css +224 -0
  12. package/coverage/block-navigation.js +87 -0
  13. package/coverage/easyflow-javascript-sdk/index.html +116 -0
  14. package/coverage/easyflow-javascript-sdk/libs/constants.mjs.html +268 -0
  15. package/coverage/easyflow-javascript-sdk/libs/errors.mjs.html +271 -0
  16. package/coverage/easyflow-javascript-sdk/libs/exception-handler.mjs.html +148 -0
  17. package/coverage/easyflow-javascript-sdk/libs/fingerprint.mjs.html +895 -0
  18. package/coverage/easyflow-javascript-sdk/libs/http.mjs.html +502 -0
  19. package/coverage/easyflow-javascript-sdk/libs/index.html +266 -0
  20. package/coverage/easyflow-javascript-sdk/libs/logger.mjs.html +568 -0
  21. package/coverage/easyflow-javascript-sdk/libs/sanitizer.mjs.html +1099 -0
  22. package/coverage/easyflow-javascript-sdk/libs/security.mjs.html +733 -0
  23. package/coverage/easyflow-javascript-sdk/libs/types.mjs.html +508 -0
  24. package/coverage/easyflow-javascript-sdk/libs/utils.mjs.html +379 -0
  25. package/coverage/easyflow-javascript-sdk/libs/validator.mjs.html +2623 -0
  26. package/coverage/easyflow-javascript-sdk/sdk.mjs.html +2434 -0
  27. package/coverage/favicon.png +0 -0
  28. package/coverage/index.html +131 -0
  29. package/coverage/lcov-report/base.css +224 -0
  30. package/coverage/lcov-report/block-navigation.js +87 -0
  31. package/coverage/lcov-report/easyflow-javascript-sdk/index.html +116 -0
  32. package/coverage/lcov-report/easyflow-javascript-sdk/libs/constants.mjs.html +268 -0
  33. package/coverage/lcov-report/easyflow-javascript-sdk/libs/errors.mjs.html +271 -0
  34. package/coverage/lcov-report/easyflow-javascript-sdk/libs/exception-handler.mjs.html +148 -0
  35. package/coverage/lcov-report/easyflow-javascript-sdk/libs/fingerprint.mjs.html +895 -0
  36. package/coverage/lcov-report/easyflow-javascript-sdk/libs/http.mjs.html +502 -0
  37. package/coverage/lcov-report/easyflow-javascript-sdk/libs/index.html +266 -0
  38. package/coverage/lcov-report/easyflow-javascript-sdk/libs/logger.mjs.html +568 -0
  39. package/coverage/lcov-report/easyflow-javascript-sdk/libs/sanitizer.mjs.html +1099 -0
  40. package/coverage/lcov-report/easyflow-javascript-sdk/libs/security.mjs.html +733 -0
  41. package/coverage/lcov-report/easyflow-javascript-sdk/libs/types.mjs.html +508 -0
  42. package/coverage/lcov-report/easyflow-javascript-sdk/libs/utils.mjs.html +379 -0
  43. package/coverage/lcov-report/easyflow-javascript-sdk/libs/validator.mjs.html +2623 -0
  44. package/coverage/lcov-report/easyflow-javascript-sdk/sdk.mjs.html +2434 -0
  45. package/coverage/lcov-report/favicon.png +0 -0
  46. package/coverage/lcov-report/index.html +131 -0
  47. package/coverage/lcov-report/prettify.css +1 -0
  48. package/coverage/lcov-report/prettify.js +2 -0
  49. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  50. package/coverage/lcov-report/sorter.js +196 -0
  51. package/coverage/lcov.info +1429 -0
  52. package/coverage/prettify.css +1 -0
  53. package/coverage/prettify.js +2 -0
  54. package/coverage/sort-arrow-sprite.png +0 -0
  55. package/coverage/sorter.js +196 -0
  56. package/dist/435.easyflow-sdk.min.js +1 -0
  57. package/dist/easyflow-sdk.min.js +1 -0
  58. package/dist/easyflow-sdk.min.js.LICENSE.txt +1 -0
  59. package/dist/index.html +756 -0
  60. package/docs/index.html +775 -0
  61. package/examples/lovable-integration.html +410 -0
  62. package/index.html +981 -0
  63. package/jest.config.js +37 -0
  64. package/jsdoc.json +42 -0
  65. package/libs/auto-integration.mjs +333 -0
  66. package/libs/constants.mjs +61 -0
  67. package/libs/constants.spec.js +198 -0
  68. package/libs/errors.mjs +62 -0
  69. package/libs/errors.spec.js +178 -0
  70. package/libs/exception-handler.mjs +21 -0
  71. package/libs/exception-handler.spec.js +237 -0
  72. package/libs/fingerprint.mjs +270 -0
  73. package/libs/http.mjs +163 -0
  74. package/libs/http.spec.js +427 -0
  75. package/libs/integration-wrapper.mjs +285 -0
  76. package/libs/logger.mjs +161 -0
  77. package/libs/logger.spec.js +389 -0
  78. package/libs/sanitizer.mjs +340 -0
  79. package/libs/sanitizer.spec.js +583 -0
  80. package/libs/security.mjs +217 -0
  81. package/libs/types.mjs +141 -0
  82. package/libs/utils.mjs +368 -0
  83. package/libs/utils.spec.js +231 -0
  84. package/libs/validator.mjs +952 -0
  85. package/libs/validator.spec.js +615 -0
  86. package/mocks/offer.mock.js +77 -0
  87. package/package.json +72 -0
  88. package/scripts/publish-npm.sh +82 -0
  89. package/sdk.mjs +945 -0
  90. package/sdk.spec.js +796 -0
  91. package/test-setup.cjs +211 -0
  92. package/test.html +154 -0
  93. package/webpack.config.cjs +41 -0
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Browser fingerprinting utilities for Easyflow SDK
3
+ *
4
+ * This module provides functions to generate unique browser fingerprints
5
+ * for security and fraud detection purposes.
6
+ */
7
+
8
+ /**
9
+ * Generates a unique browser fingerprint based on hardware and software characteristics
10
+ *
11
+ * @param {Object} [options={}] - Fingerprinting options
12
+ * @param {boolean} [options.hardwareOnly=false] - Only include hardware-based characteristics
13
+ * @param {boolean} [options.enableWebgl=false] - Include WebGL fingerprinting
14
+ * @param {boolean} [options.debug=false] - Enable debug logging
15
+ * @returns {number} Unique fingerprint hash
16
+ *
17
+ * @example
18
+ * ```javascript
19
+ * const fingerprint = getBrowserFingerprint({ hardwareOnly: true });
20
+ * console.log('Browser fingerprint:', fingerprint);
21
+ * ```
22
+ */
23
+ const getBrowserFingerprint = ({
24
+ hardwareOnly: e = !1,
25
+ enableWebgl: t = !1,
26
+ debug: r = !1,
27
+ } = {}) => {
28
+ const {
29
+ cookieEnabled: a,
30
+ deviceMemory: o,
31
+ doNotTrack: n,
32
+ hardwareConcurrency: i,
33
+ language: c,
34
+ languages: l,
35
+ maxTouchPoints: h,
36
+ platform: g,
37
+ userAgent: d,
38
+ vendor: s,
39
+ } = window.navigator
40
+ let {
41
+ width: u,
42
+ height: m,
43
+ colorDepth: f,
44
+ pixelDepth: A,
45
+ } = window.screen
46
+ ;(u = 1e3), (m = 1e3)
47
+ const v = new Date().getTimezoneOffset(),
48
+ S = Intl.DateTimeFormat().resolvedOptions().timeZone,
49
+ p = 'ontouchstart' in window,
50
+ x = window.devicePixelRatio,
51
+ E = null,
52
+ w = t ? getWebglID(r) : void 0,
53
+ R = t ? getWebglInfo(r) : void 0,
54
+ b = e
55
+ ? JSON.stringify({
56
+ canvas: E,
57
+ colorDepth: f,
58
+ deviceMemory: o,
59
+ devicePixelRatio: x,
60
+ hardwareConcurrency: i,
61
+ height: m,
62
+ maxTouchPoints: h,
63
+ pixelDepth: A,
64
+ platform: g,
65
+ touchSupport: p,
66
+ webgl: w,
67
+ webglInfo: R,
68
+ width: u,
69
+ })
70
+ : JSON.stringify({
71
+ canvas: E,
72
+ colorDepth: f,
73
+ cookieEnabled: a,
74
+ deviceMemory: o,
75
+ devicePixelRatio: x,
76
+ doNotTrack: n,
77
+ hardwareConcurrency: i,
78
+ height: m,
79
+ language: c,
80
+ languages: l,
81
+ maxTouchPoints: h,
82
+ pixelDepth: A,
83
+ platform: g,
84
+ timezone: S,
85
+ timezoneOffset: v,
86
+ touchSupport: p,
87
+ userAgent: d,
88
+ vendor: s,
89
+ webgl: w,
90
+ webglInfo: R,
91
+ width: u,
92
+ }),
93
+ y = JSON.stringify(b, null, 4)
94
+ return r && console.log('fingerprint data', y), murmurhash3_32_gc(y)
95
+ },
96
+ getCanvasID = (e) => {
97
+ try {
98
+ const t = document.createElement('canvas'),
99
+ r = t.getContext('2d'),
100
+ a =
101
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\`~1!2@3#4$5%6^7&8*9(0)-_=+[{]}|;:',<.>/?"
102
+ ;(r.textBaseline = 'top'),
103
+ (r.font = "14px 'Arial'"),
104
+ (r.textBaseline = 'alphabetic'),
105
+ (r.fillStyle = '#f60'),
106
+ r.fillRect(125, 1, 62, 20),
107
+ (r.fillStyle = '#069'),
108
+ r.fillText(a, 2, 15),
109
+ (r.fillStyle = 'rgba(102, 204, 0, 0.7)'),
110
+ r.fillText(a, 4, 17)
111
+ const o = t.toDataURL()
112
+ return (
113
+ e
114
+ ? document.body.appendChild(t)
115
+ : r.clearRect(0, 0, t.width, t.height),
116
+ murmurhash3_32_gc(o)
117
+ )
118
+ } catch {
119
+ return null
120
+ }
121
+ },
122
+ getWebglID = (e) => {
123
+ try {
124
+ const t = document.createElement('canvas'),
125
+ r = t.getContext('webgl')
126
+ ;(t.width = 256), (t.height = 128)
127
+ const a =
128
+ 'attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}',
129
+ o =
130
+ 'precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}',
131
+ n = r.createBuffer()
132
+ r.bindBuffer(r.ARRAY_BUFFER, n)
133
+ const i = new Float32Array([
134
+ -0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.7321, 0,
135
+ ])
136
+ r.bufferData(r.ARRAY_BUFFER, i, r.STATIC_DRAW),
137
+ (n.itemSize = 3),
138
+ (n.numItems = 3)
139
+ const c = r.createProgram(),
140
+ l = r.createShader(r.VERTEX_SHADER)
141
+ r.shaderSource(l, a), r.compileShader(l)
142
+ const h = r.createShader(r.FRAGMENT_SHADER)
143
+ r.shaderSource(h, o),
144
+ r.compileShader(h),
145
+ r.attachShader(c, l),
146
+ r.attachShader(c, h),
147
+ r.linkProgram(c),
148
+ r.useProgram(c),
149
+ (c.vertexPosAttrib = r.getAttribLocation(c, 'attrVertex')),
150
+ (c.offsetUniform = r.getUniformLocation(c, 'uniformOffset')),
151
+ r.enableVertexAttribArray(c.vertexPosArray),
152
+ r.vertexAttribPointer(
153
+ c.vertexPosAttrib,
154
+ n.itemSize,
155
+ r.FLOAT,
156
+ !1,
157
+ 0,
158
+ 0
159
+ ),
160
+ r.uniform2f(c.offsetUniform, 1, 1),
161
+ r.drawArrays(r.TRIANGLE_STRIP, 0, n.numItems)
162
+ const g = new Uint8Array(t.width * t.height * 4)
163
+ r.readPixels(0, 0, t.width, t.height, r.RGBA, r.UNSIGNED_BYTE, g)
164
+ const d = JSON.stringify(g).replace(/,?"[0-9]+":/g, '')
165
+ return (
166
+ e
167
+ ? document.body.appendChild(t)
168
+ : r.clear(
169
+ r.COLOR_BUFFER_BIT |
170
+ r.DEPTH_BUFFER_BIT |
171
+ r.STENCIL_BUFFER_BIT
172
+ ),
173
+ murmurhash3_32_gc(d)
174
+ )
175
+ } catch {
176
+ return null
177
+ }
178
+ },
179
+ getWebglInfo = () => {
180
+ try {
181
+ const e = document.createElement('canvas').getContext('webgl')
182
+ return {
183
+ VERSION: e.getParameter(e.VERSION),
184
+ SHADING_LANGUAGE_VERSION: e.getParameter(
185
+ e.SHADING_LANGUAGE_VERSION
186
+ ),
187
+ VENDOR: e.getParameter(e.VENDOR),
188
+ SUPORTED_EXTENSIONS: e.getSupportedExtensions(),
189
+ }
190
+ } catch {
191
+ return null
192
+ }
193
+ },
194
+ murmurhash3_32_gc = (e) => {
195
+ const t = 3 & e.length,
196
+ r = e.length - t,
197
+ a = 3432918353,
198
+ o = 461845907
199
+ let n, i, c
200
+ for (let t = 0; t < r; t++)
201
+ (c =
202
+ (255 & e.charCodeAt(t)) |
203
+ ((255 & e.charCodeAt(++t)) << 8) |
204
+ ((255 & e.charCodeAt(++t)) << 16) |
205
+ ((255 & e.charCodeAt(++t)) << 24)),
206
+ ++t,
207
+ (c =
208
+ ((65535 & c) * a + ((((c >>> 16) * a) & 65535) << 16)) &
209
+ 4294967295),
210
+ (c = (c << 15) | (c >>> 17)),
211
+ (c =
212
+ ((65535 & c) * o + ((((c >>> 16) * o) & 65535) << 16)) &
213
+ 4294967295),
214
+ (n ^= c),
215
+ (n = (n << 13) | (n >>> 19)),
216
+ (i =
217
+ (5 * (65535 & n) + (((5 * (n >>> 16)) & 65535) << 16)) &
218
+ 4294967295),
219
+ (n =
220
+ 27492 +
221
+ (65535 & i) +
222
+ (((58964 + (i >>> 16)) & 65535) << 16))
223
+ const l = r - 1
224
+ switch (((c = 0), t)) {
225
+ case 3:
226
+ c ^= (255 & e.charCodeAt(l + 2)) << 16
227
+ break
228
+ case 2:
229
+ c ^= (255 & e.charCodeAt(l + 1)) << 8
230
+ break
231
+ case 1:
232
+ c ^= 255 & e.charCodeAt(l)
233
+ }
234
+ return (
235
+ (c =
236
+ ((65535 & c) * a + ((((c >>> 16) * a) & 65535) << 16)) &
237
+ 4294967295),
238
+ (c = (c << 15) | (c >>> 17)),
239
+ (c =
240
+ ((65535 & c) * o + ((((c >>> 16) * o) & 65535) << 16)) &
241
+ 4294967295),
242
+ (n ^= c),
243
+ (n ^= e.length),
244
+ (n ^= n >>> 16),
245
+ (n =
246
+ (2246822507 * (65535 & n) +
247
+ (((2246822507 * (n >>> 16)) & 65535) << 16)) &
248
+ 4294967295),
249
+ (n ^= n >>> 13),
250
+ (n =
251
+ (3266489909 * (65535 & n) +
252
+ (((3266489909 * (n >>> 16)) & 65535) << 16)) &
253
+ 4294967295),
254
+ (n ^= n >>> 16),
255
+ n >>> 0
256
+ )
257
+ }
258
+
259
+ const makeFingerprint = () => {
260
+ try {
261
+ const fingerprint = getBrowserFingerprint()
262
+ console.log('Fingerprint:', fingerprint)
263
+ return fingerprint
264
+ } catch (e) {
265
+ console.log('Error generating fingerprint', e)
266
+ return null
267
+ }
268
+ }
269
+
270
+ export { makeFingerprint }
package/libs/http.mjs ADDED
@@ -0,0 +1,163 @@
1
+ import { buildApiUrl, getBrowserFingerprint } from './utils.mjs'
2
+ import { DEFAULT_CONFIG, HTTP_REQUEST_METHODS, TARGETS } from './constants.mjs'
3
+ import { throwsError } from './exception-handler.mjs'
4
+ import { SecureFetch } from './security.mjs'
5
+ import { NetworkError, SecurityError, ValidationError } from './errors.mjs'
6
+
7
+ /**
8
+ * Checks if an error is a known SDK error type
9
+ *
10
+ * @param {Error} error - Error to check
11
+ * @returns {boolean} True if error is a known SDK error type
12
+ *
13
+ * @private
14
+ */
15
+ function isKnownError(error) {
16
+ return (
17
+ error instanceof SecurityError ||
18
+ error instanceof ValidationError ||
19
+ error instanceof NetworkError
20
+ )
21
+ }
22
+
23
+ /**
24
+ * Ensures fingerprint header is present, generating one if missing
25
+ *
26
+ * @param {Object} headers - Request headers
27
+ * @returns {Object} Headers with fingerprint included
28
+ *
29
+ * @private
30
+ */
31
+ function ensureFingerprintHeader(headers = {}) {
32
+ if (!headers['x-fingerprint-id']) {
33
+ try {
34
+ const fingerprint = getBrowserFingerprint({ hardwareOnly: true })
35
+ if (fingerprint) {
36
+ headers['x-fingerprint-id'] = fingerprint
37
+ }
38
+ } catch (error) {
39
+ console.warn('Failed to generate fingerprint:', error.message)
40
+ }
41
+ }
42
+ return headers
43
+ }
44
+
45
+ /**
46
+ * Makes a secure HTTP request and returns JSON response
47
+ *
48
+ * @param {string} url - URL to request
49
+ * @param {Object} options - Request options
50
+ * @returns {Promise<Object>} Promise that resolves to JSON response
51
+ *
52
+ * @private
53
+ * @throws {NetworkError} When request fails
54
+ */
55
+ async function request(url, options) {
56
+ const response = await SecureFetch.request(url, options)
57
+ return await response.json()
58
+ }
59
+
60
+ /**
61
+ * Makes a secure API call to the Easyflow backend
62
+ *
63
+ * This function handles different types of API calls (GET vs POST) based on the target.
64
+ * GET requests (like getOffer and getOrder) use query parameters, while POST requests
65
+ * send data in the request body.
66
+ *
67
+ * @param {string} target - API target from TARGETS constants
68
+ * @param {Object} bodyOrQueryParams - Data to send (body for POST, query params for GET)
69
+ * @param {Object} [headers={}] - Additional HTTP headers
70
+ * @returns {Promise<Object>} Promise that resolves to API response
71
+ *
72
+ * @throws {NetworkError} When network request fails
73
+ * @throws {SecurityError} When security validation fails
74
+ * @throws {ValidationError} When input validation fails
75
+ *
76
+ * @example
77
+ * ```javascript
78
+ * // GET request
79
+ * const offer = await callSecureApi(TARGETS.GET_OFFER, { offerId: '123' });
80
+ *
81
+ * // POST request
82
+ * const orderId = await callSecureApi(TARGETS.PLACE_ORDER, {
83
+ * businessId: 'business-123',
84
+ * payments: [...]
85
+ * });
86
+ * ```
87
+ */
88
+ async function callSecureApi(target, bodyOrQueryParams, headers = {}) {
89
+ try {
90
+ const secureHeaders = ensureFingerprintHeader(headers)
91
+
92
+ const requestOptions = {
93
+ method: HTTP_REQUEST_METHODS.POST,
94
+ headers: secureHeaders,
95
+ }
96
+ if (target === TARGETS.GET_OFFER) {
97
+ const { offerId } = bodyOrQueryParams
98
+ const url = buildApiUrl(DEFAULT_CONFIG.baseUrl, target, { offerId })
99
+ return await request(url, requestOptions)
100
+ } else if (target === TARGETS.GET_ORDER) {
101
+ const { orderId } = bodyOrQueryParams
102
+ const url = buildApiUrl(DEFAULT_CONFIG.baseUrl, target, { orderId })
103
+ return await request(url, requestOptions)
104
+ } else if (target === TARGETS.ADD_CREDIT_CARD) {
105
+ const { customerId, ...bodyData } = bodyOrQueryParams
106
+ const url = buildApiUrl(DEFAULT_CONFIG.baseUrl, target, {
107
+ customerId,
108
+ })
109
+ return await request(url, {
110
+ ...requestOptions,
111
+ body: JSON.stringify(bodyData),
112
+ })
113
+ } else if (target === TARGETS.REMOVE_CREDIT_CARD) {
114
+ const { customerId, creditCardId, ...bodyData } = bodyOrQueryParams
115
+ const url = buildApiUrl(DEFAULT_CONFIG.baseUrl, target, {
116
+ customerId,
117
+ creditCardId,
118
+ })
119
+ return await request(url, {
120
+ ...requestOptions,
121
+ body: JSON.stringify(bodyData),
122
+ })
123
+ } else if (target === TARGETS.GET_CREDIT_CARD) {
124
+ const { customerId, creditCardId, ...bodyData } = bodyOrQueryParams
125
+ const url = buildApiUrl(DEFAULT_CONFIG.baseUrl, target, {
126
+ customerId,
127
+ creditCardId,
128
+ })
129
+ return await request(url, {
130
+ ...requestOptions,
131
+ body: JSON.stringify(bodyData),
132
+ })
133
+ } else if (target === TARGETS.GET_CUSTOMER) {
134
+ const { customerId, ...bodyData } = bodyOrQueryParams
135
+ const url = buildApiUrl(DEFAULT_CONFIG.baseUrl, target, {
136
+ customerId,
137
+ })
138
+ return await request(url, {
139
+ ...requestOptions,
140
+ body: JSON.stringify(bodyData),
141
+ })
142
+ } else if (target === TARGETS.UPDATE_CUSTOMER) {
143
+ const { customerId, ...bodyData } = bodyOrQueryParams
144
+ const url = buildApiUrl(DEFAULT_CONFIG.baseUrl, target, {
145
+ customerId,
146
+ })
147
+ return await request(url, {
148
+ ...requestOptions,
149
+ body: JSON.stringify(bodyData),
150
+ })
151
+ }
152
+ const url = buildApiUrl(DEFAULT_CONFIG.baseUrl, target)
153
+ return await request(url, {
154
+ ...requestOptions,
155
+ body: JSON.stringify(bodyOrQueryParams),
156
+ })
157
+ } catch (error) {
158
+ if (isKnownError(error)) throwsError(error)
159
+ throwsError(new NetworkError(`Network error: ${error.message}`))
160
+ }
161
+ }
162
+
163
+ export { callSecureApi }