@agnostack/verifyd 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 (215) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/CODE_OF_CONDUCT.md +132 -0
  3. package/LICENSE +21 -0
  4. package/README.md +93 -0
  5. package/SECURITY.md +18 -0
  6. package/dist/lib/index.d.ts +4 -0
  7. package/dist/lib/index.d.ts.map +1 -0
  8. package/dist/lib/index.js +20 -0
  9. package/dist/lib/index.js.map +1 -0
  10. package/dist/lib/types.d.ts +1 -0
  11. package/dist/lib/types.d.ts.map +1 -0
  12. package/dist/lib/types.js +1 -0
  13. package/dist/lib/types.js.map +1 -0
  14. package/dist/lib/utils/errors.d.ts +6 -0
  15. package/dist/lib/utils/errors.d.ts.map +1 -0
  16. package/dist/lib/utils/errors.js +26 -0
  17. package/dist/lib/utils/errors.js.map +1 -0
  18. package/dist/lib/utils/index.d.ts +3 -0
  19. package/dist/lib/utils/index.d.ts.map +1 -0
  20. package/dist/lib/utils/index.js +19 -0
  21. package/dist/lib/utils/index.js.map +1 -0
  22. package/dist/lib/utils/rawbody.d.ts +2 -0
  23. package/dist/lib/utils/rawbody.d.ts.map +1 -0
  24. package/dist/lib/utils/rawbody.js +60 -0
  25. package/dist/lib/utils/rawbody.js.map +1 -0
  26. package/dist/lib/verification.d.ts +10 -0
  27. package/dist/lib/verification.d.ts.map +1 -0
  28. package/dist/lib/verification.js +84 -0
  29. package/dist/lib/verification.js.map +1 -0
  30. package/dist/react/hooks/index.d.ts +2 -0
  31. package/dist/react/hooks/index.d.ts.map +1 -0
  32. package/dist/react/hooks/index.js +18 -0
  33. package/dist/react/hooks/index.js.map +1 -0
  34. package/dist/react/hooks/useVerification.d.ts +8 -0
  35. package/dist/react/hooks/useVerification.d.ts.map +1 -0
  36. package/dist/react/hooks/useVerification.js +52 -0
  37. package/dist/react/hooks/useVerification.js.map +1 -0
  38. package/dist/react/index.d.ts +3 -0
  39. package/dist/react/index.d.ts.map +1 -0
  40. package/dist/react/index.js +19 -0
  41. package/dist/react/index.js.map +1 -0
  42. package/dist/react/types.d.ts +1 -0
  43. package/dist/react/types.d.ts.map +1 -0
  44. package/dist/react/types.js +1 -0
  45. package/dist/react/types.js.map +1 -0
  46. package/dist/shared/WebCrypto.d.ts +55 -0
  47. package/dist/shared/WebCrypto.d.ts.map +1 -0
  48. package/dist/shared/WebCrypto.js +342 -0
  49. package/dist/shared/WebCrypto.js.map +1 -0
  50. package/dist/shared/datetime.d.ts +96 -0
  51. package/dist/shared/datetime.d.ts.map +1 -0
  52. package/dist/shared/datetime.js +295 -0
  53. package/dist/shared/datetime.js.map +1 -0
  54. package/dist/shared/display.d.ts +100 -0
  55. package/dist/shared/display.d.ts.map +1 -0
  56. package/dist/shared/display.js +481 -0
  57. package/dist/shared/display.js.map +1 -0
  58. package/dist/shared/index.d.ts +6 -0
  59. package/dist/shared/index.d.ts.map +1 -0
  60. package/dist/shared/index.js +28 -0
  61. package/dist/shared/index.js.map +1 -0
  62. package/dist/shared/request.d.ts +16 -0
  63. package/dist/shared/request.d.ts.map +1 -0
  64. package/dist/shared/request.js +60 -0
  65. package/dist/shared/request.js.map +1 -0
  66. package/dist/shared/types.d.ts +1 -0
  67. package/dist/shared/types.d.ts.map +1 -0
  68. package/dist/shared/types.js +1 -0
  69. package/dist/shared/types.js.map +1 -0
  70. package/dist/shared/verification.d.ts +31 -0
  71. package/dist/shared/verification.d.ts.map +1 -0
  72. package/dist/shared/verification.js +91 -0
  73. package/dist/shared/verification.js.map +1 -0
  74. package/dist/umd/lib/index.js +2204 -0
  75. package/dist/umd/lib/index.js.map +1 -0
  76. package/dist/umd/lib/lib/index.d.ts +4 -0
  77. package/dist/umd/lib/lib/index.d.ts.map +1 -0
  78. package/dist/umd/lib/lib/types.d.ts +1 -0
  79. package/dist/umd/lib/lib/types.d.ts.map +1 -0
  80. package/dist/umd/lib/lib/utils/errors.d.ts +6 -0
  81. package/dist/umd/lib/lib/utils/errors.d.ts.map +1 -0
  82. package/dist/umd/lib/lib/utils/index.d.ts +3 -0
  83. package/dist/umd/lib/lib/utils/index.d.ts.map +1 -0
  84. package/dist/umd/lib/lib/utils/rawbody.d.ts +2 -0
  85. package/dist/umd/lib/lib/utils/rawbody.d.ts.map +1 -0
  86. package/dist/umd/lib/lib/verification.d.ts +10 -0
  87. package/dist/umd/lib/lib/verification.d.ts.map +1 -0
  88. package/dist/umd/lib/react/hooks/index.d.ts +2 -0
  89. package/dist/umd/lib/react/hooks/index.d.ts.map +1 -0
  90. package/dist/umd/lib/react/hooks/useVerification.d.ts +8 -0
  91. package/dist/umd/lib/react/hooks/useVerification.d.ts.map +1 -0
  92. package/dist/umd/lib/react/index.d.ts +3 -0
  93. package/dist/umd/lib/react/index.d.ts.map +1 -0
  94. package/dist/umd/lib/react/types.d.ts +1 -0
  95. package/dist/umd/lib/react/types.d.ts.map +1 -0
  96. package/dist/umd/lib/shared/WebCrypto.d.ts +55 -0
  97. package/dist/umd/lib/shared/WebCrypto.d.ts.map +1 -0
  98. package/dist/umd/lib/shared/datetime.d.ts +96 -0
  99. package/dist/umd/lib/shared/datetime.d.ts.map +1 -0
  100. package/dist/umd/lib/shared/display.d.ts +100 -0
  101. package/dist/umd/lib/shared/display.d.ts.map +1 -0
  102. package/dist/umd/lib/shared/index.d.ts +6 -0
  103. package/dist/umd/lib/shared/index.d.ts.map +1 -0
  104. package/dist/umd/lib/shared/request.d.ts +16 -0
  105. package/dist/umd/lib/shared/request.d.ts.map +1 -0
  106. package/dist/umd/lib/shared/types.d.ts +1 -0
  107. package/dist/umd/lib/shared/types.d.ts.map +1 -0
  108. package/dist/umd/lib/shared/verification.d.ts +31 -0
  109. package/dist/umd/lib/shared/verification.d.ts.map +1 -0
  110. package/node_modules/browser-monads-ts/LICENSE +21 -0
  111. package/node_modules/browser-monads-ts/README.md +46 -0
  112. package/node_modules/browser-monads-ts/dist/browser-monads-ts.cjs.js +2 -0
  113. package/node_modules/browser-monads-ts/dist/browser-monads-ts.cjs.js.map +1 -0
  114. package/node_modules/browser-monads-ts/dist/browser-monads-ts.es.js +12 -0
  115. package/node_modules/browser-monads-ts/dist/browser-monads-ts.es.js.map +1 -0
  116. package/node_modules/browser-monads-ts/dist/index.d.ts +5 -0
  117. package/node_modules/browser-monads-ts/dist/index.d.ts.map +1 -0
  118. package/node_modules/browser-monads-ts/package.json +79 -0
  119. package/node_modules/bytes/History.md +97 -0
  120. package/node_modules/bytes/LICENSE +23 -0
  121. package/node_modules/bytes/Readme.md +152 -0
  122. package/node_modules/bytes/index.js +170 -0
  123. package/node_modules/bytes/package.json +42 -0
  124. package/node_modules/depd/History.md +103 -0
  125. package/node_modules/depd/LICENSE +22 -0
  126. package/node_modules/depd/Readme.md +280 -0
  127. package/node_modules/depd/index.js +538 -0
  128. package/node_modules/depd/lib/browser/index.js +77 -0
  129. package/node_modules/depd/package.json +45 -0
  130. package/node_modules/http-errors/HISTORY.md +180 -0
  131. package/node_modules/http-errors/LICENSE +23 -0
  132. package/node_modules/http-errors/README.md +169 -0
  133. package/node_modules/http-errors/index.js +289 -0
  134. package/node_modules/http-errors/package.json +50 -0
  135. package/node_modules/iconv-lite/Changelog.md +162 -0
  136. package/node_modules/iconv-lite/LICENSE +21 -0
  137. package/node_modules/iconv-lite/README.md +156 -0
  138. package/node_modules/iconv-lite/encodings/dbcs-codec.js +555 -0
  139. package/node_modules/iconv-lite/encodings/dbcs-data.js +176 -0
  140. package/node_modules/iconv-lite/encodings/index.js +22 -0
  141. package/node_modules/iconv-lite/encodings/internal.js +188 -0
  142. package/node_modules/iconv-lite/encodings/sbcs-codec.js +72 -0
  143. package/node_modules/iconv-lite/encodings/sbcs-data-generated.js +451 -0
  144. package/node_modules/iconv-lite/encodings/sbcs-data.js +174 -0
  145. package/node_modules/iconv-lite/encodings/tables/big5-added.json +122 -0
  146. package/node_modules/iconv-lite/encodings/tables/cp936.json +264 -0
  147. package/node_modules/iconv-lite/encodings/tables/cp949.json +273 -0
  148. package/node_modules/iconv-lite/encodings/tables/cp950.json +177 -0
  149. package/node_modules/iconv-lite/encodings/tables/eucjp.json +182 -0
  150. package/node_modules/iconv-lite/encodings/tables/gb18030-ranges.json +1 -0
  151. package/node_modules/iconv-lite/encodings/tables/gbk-added.json +55 -0
  152. package/node_modules/iconv-lite/encodings/tables/shiftjis.json +125 -0
  153. package/node_modules/iconv-lite/encodings/utf16.js +177 -0
  154. package/node_modules/iconv-lite/encodings/utf7.js +290 -0
  155. package/node_modules/iconv-lite/lib/bom-handling.js +52 -0
  156. package/node_modules/iconv-lite/lib/extend-node.js +217 -0
  157. package/node_modules/iconv-lite/lib/index.d.ts +24 -0
  158. package/node_modules/iconv-lite/lib/index.js +153 -0
  159. package/node_modules/iconv-lite/lib/streams.js +121 -0
  160. package/node_modules/iconv-lite/package.json +46 -0
  161. package/node_modules/inherits/LICENSE +16 -0
  162. package/node_modules/inherits/README.md +42 -0
  163. package/node_modules/inherits/inherits.js +9 -0
  164. package/node_modules/inherits/inherits_browser.js +27 -0
  165. package/node_modules/inherits/package.json +29 -0
  166. package/node_modules/nothing-mock/LICENSE +21 -0
  167. package/node_modules/nothing-mock/README.md +266 -0
  168. package/node_modules/nothing-mock/dist/nothing-mock.es.js +2 -0
  169. package/node_modules/nothing-mock/dist/nothing-mock.es.js.map +1 -0
  170. package/node_modules/nothing-mock/dist/nothing-mock.js +2 -0
  171. package/node_modules/nothing-mock/dist/nothing-mock.js.map +1 -0
  172. package/node_modules/nothing-mock/dist/nothing-mock.modern.js +2 -0
  173. package/node_modules/nothing-mock/dist/nothing-mock.modern.js.map +1 -0
  174. package/node_modules/nothing-mock/dist/nothing-mock.umd.js +2 -0
  175. package/node_modules/nothing-mock/dist/nothing-mock.umd.js.map +1 -0
  176. package/node_modules/nothing-mock/index.d.ts +6 -0
  177. package/node_modules/nothing-mock/package.json +29 -0
  178. package/node_modules/nothing-mock/src/index.js +15 -0
  179. package/node_modules/raw-body/HISTORY.md +308 -0
  180. package/node_modules/raw-body/LICENSE +22 -0
  181. package/node_modules/raw-body/README.md +223 -0
  182. package/node_modules/raw-body/SECURITY.md +24 -0
  183. package/node_modules/raw-body/index.d.ts +87 -0
  184. package/node_modules/raw-body/index.js +336 -0
  185. package/node_modules/raw-body/package.json +49 -0
  186. package/node_modules/safer-buffer/LICENSE +21 -0
  187. package/node_modules/safer-buffer/Porting-Buffer.md +268 -0
  188. package/node_modules/safer-buffer/Readme.md +156 -0
  189. package/node_modules/safer-buffer/dangerous.js +58 -0
  190. package/node_modules/safer-buffer/package.json +34 -0
  191. package/node_modules/safer-buffer/safer.js +77 -0
  192. package/node_modules/safer-buffer/tests.js +406 -0
  193. package/node_modules/setprototypeof/LICENSE +13 -0
  194. package/node_modules/setprototypeof/README.md +31 -0
  195. package/node_modules/setprototypeof/index.d.ts +2 -0
  196. package/node_modules/setprototypeof/index.js +17 -0
  197. package/node_modules/setprototypeof/package.json +38 -0
  198. package/node_modules/setprototypeof/test/index.js +24 -0
  199. package/node_modules/statuses/HISTORY.md +82 -0
  200. package/node_modules/statuses/LICENSE +23 -0
  201. package/node_modules/statuses/README.md +136 -0
  202. package/node_modules/statuses/codes.json +65 -0
  203. package/node_modules/statuses/index.js +146 -0
  204. package/node_modules/statuses/package.json +49 -0
  205. package/node_modules/toidentifier/HISTORY.md +9 -0
  206. package/node_modules/toidentifier/LICENSE +21 -0
  207. package/node_modules/toidentifier/README.md +61 -0
  208. package/node_modules/toidentifier/index.js +32 -0
  209. package/node_modules/toidentifier/package.json +38 -0
  210. package/node_modules/unpipe/HISTORY.md +4 -0
  211. package/node_modules/unpipe/LICENSE +22 -0
  212. package/node_modules/unpipe/README.md +43 -0
  213. package/node_modules/unpipe/index.js +69 -0
  214. package/node_modules/unpipe/package.json +27 -0
  215. package/package.json +127 -0
@@ -0,0 +1,2204 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('luxon')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'luxon'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@agnostack/verifyd/lib"] = {}, global.luxon));
5
+ })(this, (function (exports, luxon) { 'use strict';
6
+
7
+ /* eslint-disable no-use-before-define */
8
+
9
+ // #region lib-core
10
+ // TODO!!!: keep in sync between verifyd and lib-core (and lib-utils-js at bottom)
11
+ const normalizeShopifyId = (shopifyData) => {
12
+ const isString = isType(shopifyData, 'string');
13
+ if (isString && !shopifyData.startsWith('gid:')) {
14
+ if (!isNumericOnly(shopifyData)) {
15
+ return {}
16
+ }
17
+
18
+ return {
19
+ id: `${shopifyData}`,
20
+ }
21
+ }
22
+
23
+ const { id: _entityId, legacyResourceId } = isString
24
+ ? { id: shopifyData }
25
+ : ensureObject(shopifyData);
26
+
27
+ if (stringEmpty(_entityId) && stringEmpty(legacyResourceId)) {
28
+ return {}
29
+ }
30
+
31
+ const entity_id = ensureString(_entityId);
32
+ const { entity_type, parsedId } = entity_id.match(/^gid:\/\/shopify\/(?<entity_type>.*)\/(?<parsedId>[0-9]+).*$/)?.groups ?? {};
33
+ const id = ensureString(parsedId || legacyResourceId);
34
+
35
+ const hasEntityId = stringNotEmpty(entity_id);
36
+ const hasEntityType = stringNotEmpty(entity_type);
37
+
38
+ return {
39
+ ...stringNotEmpty(id) && { id },
40
+ ...hasEntityId && { entity_id },
41
+ ...hasEntityType && { entity_type },
42
+ ...(hasEntityId && hasEntityType) && {
43
+ gid: `gid://shopify/${entity_type}/${entity_id}`,
44
+ },
45
+ }
46
+ };
47
+
48
+ const random = (max = 100, min = 1) => {
49
+ const floor = Math.min(max, min);
50
+ return Math.floor(Math.random() * (max - floor + 1)) + floor
51
+ };
52
+
53
+ const nextRandom = (max = 100, min = 1) => {
54
+ const maximum = Math.max(max, 0);
55
+ const minimum = Math.max(min, 0);
56
+
57
+ let randomNumber = maximum;
58
+
59
+ if (maximum > minimum) {
60
+ do {
61
+ randomNumber = random(maximum, minimum);
62
+ } while (randomNumber === nextRandom.last)
63
+
64
+ nextRandom.last = randomNumber;
65
+ }
66
+
67
+ return randomNumber
68
+ };
69
+
70
+ const arrayRandom = (_values, _max = 1) => {
71
+ const values = ensureArray(_values);
72
+ const valuesLength = values.length;
73
+ const maxLength = Math.min(_max, valuesLength);
74
+
75
+ if (valuesLength <= maxLength) {
76
+ return values
77
+ }
78
+
79
+ const arrayLength = valuesLength - 1;
80
+ const randomValues = [];
81
+ do {
82
+ const newVal = values[nextRandom(arrayLength, 0)];
83
+ if (!randomValues.includes(newVal)) {
84
+ randomValues.push(newVal);
85
+ }
86
+ } while (randomValues.length < maxLength)
87
+
88
+ return randomValues
89
+ };
90
+
91
+ const arrayRandomItem = (_values) => (
92
+ arrayRandom(_values)[0]
93
+ );
94
+
95
+ const isArray = (value) => (
96
+ // eslint-disable-next-line eqeqeq
97
+ (value != undefined) && Array.isArray(value)
98
+ );
99
+
100
+ const isSet = (value) => (
101
+ // eslint-disable-next-line eqeqeq
102
+ (value != undefined) && (value instanceof Set)
103
+ );
104
+
105
+ const isType = (value, type) => (
106
+ // eslint-disable-next-line eqeqeq, valid-typeof
107
+ (value != undefined) && (typeof value === type)
108
+ );
109
+
110
+ const isTypeEnhanced = (value, type) => {
111
+ switch (true) {
112
+ case (type === 'set'): {
113
+ return isSet(value)
114
+ }
115
+
116
+ case (type === 'array'): {
117
+ return (
118
+ isArray(value) &&
119
+ !isSet(value)
120
+ )
121
+ }
122
+
123
+ case (type === 'object'): {
124
+ return (
125
+ isType(value, type) &&
126
+ !isArray(value)
127
+ )
128
+ }
129
+
130
+ default: {
131
+ return isType(value, type)
132
+ }
133
+ }
134
+ };
135
+
136
+ const safeTrim = (value) => {
137
+ if (isType(value, 'string')) {
138
+ // eslint-disable-next-line no-param-reassign
139
+ value = value.trim();
140
+ }
141
+
142
+ return value
143
+ };
144
+
145
+ const safeParse = (value, trim) => {
146
+ if (isType(value, 'string')) {
147
+ if (trim) {
148
+ // eslint-disable-next-line no-param-reassign
149
+ value = safeTrim(value);
150
+ }
151
+
152
+ if (value.startsWith('{') || value.startsWith('[') || value.startsWith('"')) {
153
+ // eslint-disable-next-line no-param-reassign
154
+ value = JSON.parse(value);
155
+ }
156
+ }
157
+
158
+ // TODO: should this be value ?? {}
159
+ return value
160
+ };
161
+
162
+ const ensureString = (string) => (
163
+ string ? `${string}` : ''
164
+ );
165
+
166
+ // HMMM: what if 'string' is an object?
167
+ const ensureStringOnly = (string) => (
168
+ // eslint-disable-next-line eqeqeq
169
+ (string != undefined) ? `${string}` : ''
170
+ );
171
+
172
+ const ensureStringAscii = (string) => (
173
+ // eslint-disable-next-line no-control-regex
174
+ ensureStringOnly(string).replace(/[^\x00-\x7F]/g, '')
175
+ );
176
+
177
+ const ensureStringClean = (string) => (
178
+ // eslint-disable-next-line no-control-regex
179
+ ensureStringOnly(string).replace(/[^a-z0-9_-]/gi, '')
180
+ );
181
+
182
+ const ensureAlphaNumeric = (string) => (
183
+ // eslint-disable-next-line no-control-regex
184
+ ensureStringOnly(string).replace(/[^a-z0-9]/gi, '')
185
+ );
186
+
187
+ const isNumericOnly = (value) => {
188
+ const [matched] = `${value}`.match(/^([0-9]+)$/) ?? [];
189
+ // eslint-disable-next-line eqeqeq
190
+ return (matched != undefined)
191
+ };
192
+
193
+ const isNumericNegatable = (value) => {
194
+ const [matched] = `${value}`.match(/^(-?[0-9]+(\.[0-9]+)?)?$/) ?? [];
195
+ // eslint-disable-next-line eqeqeq
196
+ return (matched != undefined)
197
+ };
198
+
199
+ // TODO: explore places using ensureNumeric to move to isNumericNegatable
200
+ const ensureNumeric = (string) => (
201
+ Number(ensureString(string).replace(/[^0-9.]/gi, ''))
202
+ );
203
+
204
+ const ensureNumericOnly = (string) => (
205
+ Number(ensureString(string).replace(/[^0-9]/gi, ''))
206
+ );
207
+
208
+ // TODO: update regex to handle negative number returns negative. Something like (?!\d\.)(-?\d+(\.\d)?)
209
+ const ensureNumericNegatable = (string) => (
210
+ Number(ensureString(string).replace(/[^0-9.-]/gi, ''))
211
+ );
212
+
213
+ const ensureNumericConstrained = (value, { min: _min = 0, max: _max = 100 } = {}) => {
214
+ const min = ensureNumericNegatable(_min);
215
+ const max = ensureNumericNegatable(_max);
216
+
217
+ return Math.max(
218
+ min,
219
+ Math.min(ensureNumeric(value), max)
220
+ )
221
+ };
222
+
223
+ const ensureArray = (array = []) => (
224
+ // eslint-disable-next-line no-nested-ternary
225
+ !array ? [] : Array.isArray(array) ? array : [array]
226
+ );
227
+
228
+ const ensureArraySet = (arrayOrSet) => (
229
+ ensureArray(isSet(arrayOrSet) ? [...arrayOrSet] : arrayOrSet)
230
+ );
231
+
232
+ const arrayEmpty = (array, disableEmptyString = false) => (
233
+ // eslint-disable-next-line eqeqeq
234
+ !array || !array.length || (array.length === 1 && (array[0] == undefined || (disableEmptyString && stringEmpty(array[0]))))
235
+ );
236
+
237
+ const arrayNotEmpty = (array, disableEmptyString = false) => (
238
+ // eslint-disable-next-line eqeqeq
239
+ !arrayEmpty(array) && array[0] != undefined && (!disableEmptyString || stringNotEmpty(array[0]))
240
+ );
241
+
242
+ const findLastMatch = (array, filterCallback = (value) => (value)) => (
243
+ ensureArray(array).filter(filterCallback).slice(-1)[0]
244
+ );
245
+
246
+ const ensureObject = (object) => (
247
+ object ?? {}
248
+ );
249
+
250
+ // NOTE: this does not ensure !isType(string)
251
+ const objectEmpty = (object) => (
252
+ !object || !Object.keys(object).length
253
+ );
254
+
255
+ const objectNotEmpty = (object) => (
256
+ !objectEmpty(object)
257
+ );
258
+
259
+ const nullable = (object) => (
260
+ (!object || (object === 'null') || (object === undefined) || (object === null))
261
+ ? null // TODO: explore undefined here??
262
+ : object
263
+ );
264
+
265
+ const objectContainsAnyValue = (object) => (
266
+ Object.values(ensureObject(object)).some((value) => nullable(value))
267
+ );
268
+
269
+ const isUndefined = (value) => (
270
+ // eslint-disable-next-line eqeqeq
271
+ value == undefined
272
+ );
273
+
274
+ const cleanObject = (object, allowEmptyLeafs, isEmptyCallback) => (
275
+ Object.entries(ensureObject(object)).reduce((
276
+ _cleanedObject,
277
+ [key, _value]
278
+ ) => {
279
+ if (!allowEmptyLeafs && (isEmptyCallback?.(_value) ?? isUndefined(_value))) {
280
+ return _cleanedObject // skip key if null or undefined
281
+ }
282
+
283
+ const value = !Array.isArray(_value)
284
+ ? cleanObjectValue(_value, allowEmptyLeafs, isEmptyCallback)
285
+ : _value.reduce((_data, __value) => ([
286
+ ..._data,
287
+ cleanObjectValue(__value, allowEmptyLeafs, isEmptyCallback)
288
+ ]), []);
289
+
290
+ return {
291
+ ..._cleanedObject,
292
+ [key]: value,
293
+ }
294
+ }, {})
295
+ );
296
+
297
+ const cleanObjectValue = (value, allowEmptyLeafs, isEmptyCallback) => (
298
+ isType(value, 'object') ? cleanObject(value, allowEmptyLeafs, isEmptyCallback) : value
299
+ );
300
+
301
+ const stringNotEmptyOnly = (stringable) => {
302
+ const string = ensureStringOnly(stringable);
303
+ return ((string.length > 0) && !['null', 'undefined'].includes(string))
304
+ };
305
+
306
+ const stringEmptyOnly = (stringable) => (
307
+ !stringNotEmptyOnly(stringable)
308
+ );
309
+
310
+ const stringNotEmpty = (stringable) => {
311
+ const string = ensureString(stringable);
312
+ return ((string.length > 0) && !['null', 'undefined'].includes(string))
313
+ };
314
+
315
+ const stringEmpty = (stringable) => (
316
+ !stringNotEmpty(stringable)
317
+ );
318
+
319
+ const splitString = (splitting, index = splitting.length) => {
320
+ const string = ensureString(splitting);
321
+ return [string.slice(0, index), string.slice(index)]
322
+ };
323
+
324
+ const compareNumber = (number1 = 0, number2 = 0) => (
325
+ number1 - number2
326
+ );
327
+
328
+ const compareString = (string1, string2) => {
329
+ if (stringEmpty(string1)) {
330
+ return 1
331
+ }
332
+
333
+ if (stringEmpty(string2)) {
334
+ return -1
335
+ }
336
+
337
+ return ensureString(string1).localeCompare(string2)
338
+ };
339
+
340
+ const compareEntryKeys = ([string1], [string2]) => (
341
+ compareString(string1, string2)
342
+ );
343
+
344
+ const objectToSortedString = (object, separator = '') => (
345
+ Object.entries(ensureObject(object))
346
+ .sort(compareEntryKeys)
347
+ .map(([key, value]) => `${key}=${value}`)
348
+ .join(separator)
349
+ );
350
+
351
+ const isHTML = (string) => (
352
+ (string?.startsWith?.('<') || string?.startsWith?.('\n<')) ?? false
353
+ );
354
+
355
+ const freeze = (logMessage, object, type = 'log') => {
356
+ console[type](logMessage, !object ? object : JSON.parse(JSON.stringify(ensureObject(object))));
357
+ };
358
+
359
+ const replaceSpaces = (string, replacement = '') => (
360
+ !isType(string, 'string')
361
+ ? ''
362
+ : string.replace(/\s/g, replacement)
363
+ );
364
+
365
+ const recase = (string, replacement = '') => (
366
+ !isType(string, 'string')
367
+ ? ''
368
+ : string
369
+ .replace(/[^a-zA-Z0-9]+/g, replacement)
370
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
371
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
372
+ .replace(/([0-9])([^0-9])/g, '$1-$2')
373
+ .replace(/([^0-9])([0-9])/g, '$1-$2')
374
+ .replace(/[-_]+/g, replacement)
375
+ .replace(new RegExp(`${replacement}$`), '')
376
+ .toLowerCase()
377
+ );
378
+
379
+ const zencase = (string, replacement = '-') => (
380
+ !isType(string, 'string')
381
+ ? ''
382
+ : replaceSpaces(string, replacement)
383
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
384
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
385
+ .replace(new RegExp(`${replacement}$`), '')
386
+ .toLowerCase()
387
+ );
388
+
389
+ const dashcase = (string) => (
390
+ recase(string, '-')
391
+ );
392
+
393
+ const undashcase = (string, replacement = ' ') => (
394
+ ensureString(string).replace(/-/g, replacement).trim()
395
+ );
396
+
397
+ const snakecase = (string) => (
398
+ recase(string, '_')
399
+ );
400
+
401
+ const unsnakecase = (string, replacement = ' ') => (
402
+ ensureString(string).replace(/_/g, replacement).trim()
403
+ );
404
+
405
+ const uppercase = (string) => (
406
+ ensureString(string).toUpperCase()
407
+ );
408
+
409
+ const lowercase = (string, defaultValue = '') => (
410
+ !stringNotEmpty(string)
411
+ ? defaultValue
412
+ : ensureString(string).toLowerCase()
413
+ );
414
+
415
+ const slugify = (string) => (
416
+ lowercase(dashcase(string))
417
+ );
418
+
419
+ const capitalize = (string) => {
420
+ const parts = splitString(string, 1);
421
+ return `${uppercase(parts[0])}${parts[1]}`
422
+ };
423
+
424
+ const camelCase = (string) => (
425
+ stringNotEmpty(string)
426
+ ? unsnakecase(undashcase(string)).replace(/\w\S*/g, (word, charIndex) => {
427
+ if (charIndex === 0) {
428
+ return lowercase(word)
429
+ }
430
+ return `${uppercase(word.charAt(0))}${lowercase(word.substr(1))}`
431
+ }).split(' ').join('')
432
+ : ''
433
+ );
434
+
435
+ const titleCase = (string, _overrides) => {
436
+ if (!stringNotEmpty(string)) {
437
+ return ''
438
+ }
439
+
440
+ const overrides = {
441
+ 'add-ons': 'Add-Ons',
442
+ agnostack: 'agnoStack',
443
+ ai: 'AI',
444
+ b2b: 'B2B',
445
+ b2c: 'B2C',
446
+ cartcollab: 'CartCollab',
447
+ 'cartcollab(sm)': 'CartCollab(SM)',
448
+ chatgpt: 'ChatGPT',
449
+ covid: 'COVID',
450
+ covid19: 'COVID-19',
451
+ 'covid-19': 'COVID-19',
452
+ crm: 'CRM',
453
+ elasticpath: 'Elastic Path',
454
+ ecommerce: 'eCommerce',
455
+ 'e-commerce': 'eCommerce',
456
+ faqs: 'FAQs',
457
+ gpt: 'GPT',
458
+ 'smile.io': 'Smile.io',
459
+ 'stamped.io': 'Stamped.io',
460
+ 'judge.me': 'Judge.me',
461
+ 'influence.io': 'Influence.io',
462
+ keepsmallstrong: 'KeepSmallStrong',
463
+ loyaltylion: 'LoyaltyLion',
464
+ openai: 'OpenAI',
465
+ paypal: 'PayPal',
466
+ 'postscript.io': 'Postscript.io',
467
+ recharge: 'ReCharge',
468
+ 'stay.ai': 'Stay.ai',
469
+ 'stay ai': 'Stay Ai',
470
+ shipengine: 'ShipEngine',
471
+ shipperhq: 'ShipperHQ',
472
+ shipstation: 'ShipStation',
473
+ taxjar: 'TaxJar',
474
+ yotpo: 'YotPo',
475
+ ..._overrides,
476
+ };
477
+
478
+ const stringParts = lowercase(string)
479
+ .split(' ')
480
+ .reduce((
481
+ _stringParts,
482
+ _stringPart
483
+ ) => {
484
+ const stringPart = overrides[_stringPart] ?? _stringPart
485
+ .replace(/([A-Z]+)/g, ' $1')
486
+ .replace(/\s\s+/g, ' ')
487
+ .replace(/(\b[a-z](?!\s)*)/g, (firstChar) => uppercase(firstChar));
488
+
489
+ return [
490
+ ..._stringParts,
491
+ ...stringNotEmpty(stringPart) ? [stringPart] : []
492
+ ]
493
+ }, []);
494
+
495
+ return stringParts.join(' ')
496
+ };
497
+
498
+ const removeLeadingSlash = (string) => (
499
+ ensureString(string).replace(/^\//, '')
500
+ );
501
+
502
+ const removeLeadingTrailingQuotes = (string) => (
503
+ ensureString(string).replace(/^"|"$/g, '')
504
+ );
505
+
506
+ // TODO: not sure if this should remove multipel at end or just one (as it does now)?
507
+ const removeTrailingSlash = (string) => (
508
+ ensureString(string).replace(/\/$/, '')
509
+ );
510
+
511
+ const removeLeadingTrailingSlash = (string) => (
512
+ removeTrailingSlash(removeLeadingSlash(string))
513
+ );
514
+
515
+ const ensureLeadingSlash = (string) => (
516
+ `/${removeLeadingSlash(string)}`
517
+ );
518
+
519
+ const ensureTrailingSlash = (string) => (
520
+ `${removeTrailingSlash(string)}/`
521
+ );
522
+
523
+ const ensureExtension = (string, extension) => {
524
+ if (stringEmpty(extension)) {
525
+ return string
526
+ }
527
+
528
+ const filePath = string.match(/(.*)\..+$/)?.[1] ?? string;
529
+
530
+ return `${filePath}.${extension}`
531
+ };
532
+
533
+ const wrappedEncode = (basePath, _wrappingPath) => {
534
+ const wrappingPath = ensureString(_wrappingPath);
535
+ if (stringEmpty(basePath)) {
536
+ return wrappingPath
537
+ }
538
+
539
+ return `${basePath}${encodeURIComponent(wrappingPath)}`
540
+ };
541
+
542
+ const splitCommas = (value) => (
543
+ ensureString(value)
544
+ .replace(/, /g, ',')
545
+ .split(',')
546
+ .reduce((_values, _value) => {
547
+ const __value = safeTrim(_value);
548
+ return [
549
+ ..._values,
550
+ ...stringNotEmpty(__value) ? [__value] : [] // TODO: explore stringEmptyOnly?
551
+ ]
552
+ }, [])
553
+ );
554
+
555
+ const combineCommas = (values) => (
556
+ [...new Set(
557
+ ensureArray(values).reduce((
558
+ _combined,
559
+ value
560
+ ) => ([
561
+ ..._combined,
562
+ ...splitCommas(value)
563
+ ]), [])
564
+ )]
565
+ );
566
+
567
+ const normalizeArray = (input, separator = ' ') => {
568
+ const inputArray = ensureArray(input);
569
+
570
+ return inputArray.reduce((normalized, _ignore, index) => ([
571
+ ...normalized,
572
+ inputArray.slice(0, index + 1).join(separator)
573
+ ]), []).reverse()
574
+ };
575
+
576
+ const arrayToObject = (arrayable, key) => (
577
+ ensureArray(arrayable).reduce((_object, item) => ({
578
+ ..._object,
579
+ [item?.[key]]: item,
580
+ }), {})
581
+ );
582
+
583
+ const isTrue = (value, falsy) => {
584
+ const string = ensureString(value);
585
+ return ![...ensureArray(falsy), '', 'false'].includes(string)
586
+ };
587
+
588
+ // TODO: move all uses of parseKeywords to parseKeywordGroups
589
+ const parseKeywords = (keywordGroup, previous) => (
590
+ [...new Set(ensureArray(keywordGroup).reduce((_parsedKeywords, keyword) => {
591
+ const keywords = splitCommas(keyword);
592
+
593
+ return [
594
+ ..._parsedKeywords,
595
+ ...keywords.filter((_keyword) => stringNotEmpty(_keyword))
596
+ ]
597
+ }, ensureArray(previous)))].join(', ')
598
+ );
599
+
600
+ const parseKeywordGroups = (keywordGroups) => {
601
+ const parsedKeywords = ensureArray(keywordGroups).reduce((_parsedKeywords, keywordGroup) => ([
602
+ ..._parsedKeywords,
603
+ ...ensureArray(keywordGroup).reduce((_parsedGroupKeywords, keyword) => {
604
+ const keywords = splitCommas(keyword);
605
+
606
+ return [
607
+ ..._parsedGroupKeywords,
608
+ ...keywords.filter((_keyword) => stringNotEmpty(_keyword))
609
+ ]
610
+ }, [])
611
+ ]), []);
612
+
613
+ return [...new Set(parsedKeywords)].join(', ')
614
+ };
615
+
616
+ const parseProjectName = (_name) => {
617
+ const name = ensureString(_name);
618
+ const [, organization, ...parts] = /(@.*)([\\]+|\/)+(.*)/.exec(name) ?? [];
619
+ const projectName = organization ? name.replace(organization, '').replace('/', '') : name;
620
+
621
+ return {
622
+ name,
623
+ parts,
624
+ organization,
625
+ projectName,
626
+ }
627
+ };
628
+
629
+ const parseProject = (packageInfo) => {
630
+ const { shortName, owner: _appOwner, name: _name, appName: _appName, keywords, siteName: _siteName } = packageInfo ?? {};
631
+ const { name, organization, projectName } = parseProjectName(_name);
632
+
633
+ const appName = _appName ?? projectName?.slice(projectName?.indexOf('-') + 1);
634
+ const siteName = _siteName ?? projectName?.slice(projectName?.indexOf('-') + 1);
635
+ const companyName = titleCase(organization?.replace('@', ''));
636
+ const appOwner = _appOwner ?? companyName;
637
+
638
+ return [{
639
+ keywords,
640
+ appOwner,
641
+ appName,
642
+ siteName,
643
+ shortName,
644
+ companyName,
645
+ projectName,
646
+ }, organization, name]
647
+ };
648
+
649
+ // #endregion lib-core
650
+
651
+ // #region lib-utils-js
652
+
653
+ const querystringToObject = (queryString) => {
654
+ if (stringEmpty(queryString)) {
655
+ return {}
656
+ }
657
+
658
+ const urlParams = new URLSearchParams(queryString);
659
+ return Object.fromEntries(urlParams)
660
+ };
661
+
662
+ const arrayIncludesAll = (_values, comparison) => {
663
+ const values = ensureArray(_values);
664
+ return ensureArray(comparison).every((value) => (
665
+ stringNotEmpty(value) && values.includes(value)
666
+ ))
667
+ };
668
+
669
+ const arraysMatch = (array1, array2) => (
670
+ arrayIncludesAll(array1, array2) && arrayIncludesAll(array2, array1)
671
+ );
672
+
673
+ const appendQuery = (_basePath, queryString) => {
674
+ const basePath = ensureString(_basePath);
675
+ if (stringEmpty(queryString)) {
676
+ return basePath
677
+ }
678
+ return `${basePath}${basePath.includes('?') ? '&' : '?'}${queryString}`
679
+ };
680
+
681
+ /* eslint-disable no-use-before-define */
682
+
683
+ // TODO!!!: keep in sync between verifyd and lib-core
684
+ // TODO: consolidate these to export as COMMON_FORMATS
685
+ const TIME_FORMAT_LONG = 'HH:mm';
686
+ const TIME_FORMAT_AMPM = 'h:mm a';
687
+
688
+ const DATE_FORMAT_MED = 'M/d/yyyy';
689
+ const DATE_FORMAT_LONG = 'MM/dd/yyyy';
690
+ const DATE_FORMAT_TRACKING = 'MMddyyyy';
691
+ const DATE_FORMAT_ORGANIZED = 'yyyy-MM-dd';
692
+
693
+ const {
694
+ DATE_SHORT,
695
+ DATE_MED,
696
+ DATE_FULL,
697
+ DATE_HUGE,
698
+ DATETIME_SHORT,
699
+ DATETIME_MED,
700
+ DATETIME_MED_WITH_SECONDS,
701
+ DATETIME_FULL,
702
+ DATETIME_HUGE,
703
+ TIME_SIMPLE,
704
+ } = luxon.DateTime;
705
+
706
+ const DATE_MINI = {
707
+ ...DATE_SHORT,
708
+ year: '2-digit',
709
+ };
710
+
711
+ const DATETIME_MINI = {
712
+ ...DATETIME_SHORT,
713
+ year: '2-digit',
714
+ };
715
+
716
+ const {
717
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
718
+ timeZoneName: _timeZoneNameF,
719
+ ...DATETIME_FULL_NO_ZONE
720
+ } = DATETIME_FULL;
721
+
722
+ const {
723
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
724
+ timeZoneName: _timeZoneNameH,
725
+ ...DATETIME_HUGE_NO_ZONE
726
+ } = DATETIME_HUGE;
727
+
728
+ const CUSTOM_FORMATS = {
729
+ UTC: 'UTC',
730
+ ISO: 'ISO',
731
+ UNIX: 'UNIX',
732
+ };
733
+
734
+ const LOCALE_FORMATS = {
735
+ DATE_MINI,
736
+ DATE_SHORT,
737
+ DATE_MED,
738
+ DATE_FULL,
739
+ DATE_HUGE,
740
+ DATETIME_MINI,
741
+ DATETIME_SHORT,
742
+ DATETIME_MED,
743
+ DATETIME_MED_WITH_SECONDS,
744
+ DATETIME_FULL_NO_ZONE,
745
+ DATETIME_HUGE_NO_ZONE,
746
+ TIME_SIMPLE,
747
+ };
748
+
749
+ const DATE_PARTS = {
750
+ day: 'dd',
751
+ month: 'MM',
752
+ year: 'yyyy',
753
+ };
754
+
755
+ const validLocaleFormats = Object.values(LOCALE_FORMATS);
756
+
757
+ const hasSomeNumbers = (value) => /\d/.test(value);
758
+ const hasOnlyValidCharacters = (value) => /^[\w-+:,\\/\s.]+$/.test(value); // TODO: confirm if these are all needed?
759
+
760
+ const getDaysInMonth = (year, month) => (
761
+ new Date(year, month, 0).getDate()
762
+ );
763
+
764
+ const setDateTimeLocale = (locale) => {
765
+ if (stringNotEmpty(locale)) {
766
+ luxon.Settings.defaultLocale = locale;
767
+ }
768
+ };
769
+
770
+ const setDateTimeZone = (zone) => {
771
+ if (stringNotEmpty(zone)) {
772
+ luxon.Settings.defaultZone = zone;
773
+ }
774
+ };
775
+
776
+ const getDateTimeFormat = ({ locale, mappings = DATE_PARTS } = {}) => {
777
+ const localeParts = getCurrentDateTime().toLocaleParts({ locale });
778
+
779
+ return localeParts.map(({ type, value }) => {
780
+ if (type === 'literal') {
781
+ return value
782
+ }
783
+
784
+ return mappings[type]
785
+ }).filter(stringNotEmpty).join('')
786
+ };
787
+
788
+ const getDateTimeFromString = ({ format, value, ...options } = {}) => {
789
+ if (
790
+ !hasSomeNumbers(value) ||
791
+ !hasOnlyValidCharacters(value) ||
792
+ (isType(value, 'string') && stringEmpty(value))
793
+ ) {
794
+ return undefined
795
+ }
796
+
797
+ if (stringNotEmpty(format)) {
798
+ return luxon.DateTime.fromFormat(value, format, options)
799
+ }
800
+
801
+ if (isNumericNegatable(value)) {
802
+ return luxon.DateTime.fromSeconds(ensureNumericNegatable(value))
803
+ }
804
+
805
+ let dateTime = luxon.DateTime.fromISO(value, options);
806
+
807
+ if (!dateTime?.isValid) {
808
+ dateTime = luxon.DateTime.fromSQL(value, options);
809
+ }
810
+
811
+ if (!dateTime?.isValid) {
812
+ dateTime = luxon.DateTime.fromRFC2822(value, options);
813
+ }
814
+
815
+ return dateTime
816
+ };
817
+
818
+ const convertDateTime = (value, { shouldThrow = true, shouldLog = shouldThrow, ...options } = {}) => {
819
+ let dateTime;
820
+ try {
821
+ switch (true) {
822
+ // eslint-disable-next-line eqeqeq
823
+ case (value == undefined): {
824
+ break
825
+ }
826
+
827
+ case luxon.DateTime.isDateTime(value): {
828
+ dateTime = value;
829
+ break
830
+ }
831
+
832
+ case (value instanceof Date): {
833
+ dateTime = luxon.DateTime.fromJSDate(value, options);
834
+ break
835
+ }
836
+
837
+ case isType(value, 'object'): {
838
+ dateTime = luxon.DateTime.fromObject(value, options);
839
+ break
840
+ }
841
+
842
+ default: {
843
+ dateTime = getDateTimeFromString({ value, ...options });
844
+ break
845
+ }
846
+ }
847
+
848
+ if (dateTime && !dateTime.isValid) {
849
+ if (shouldLog) {
850
+ console.info('Issue converting timestamp', { value, dateTime });
851
+ }
852
+ throw new Error(['Error converting timestamp', dateTime?.invalidExplanation].filter(stringNotEmpty).join('. '))
853
+ }
854
+ } catch (error) {
855
+ if (shouldThrow) {
856
+ throw error
857
+ }
858
+ dateTime = undefined;
859
+ }
860
+
861
+ return dateTime
862
+ };
863
+
864
+ const convertDuration = (value, { shouldThrow = true, ...options } = {}) => {
865
+ let duration;
866
+ try {
867
+ switch (true) {
868
+ // eslint-disable-next-line eqeqeq
869
+ case (value == undefined): {
870
+ break
871
+ }
872
+
873
+ case luxon.Duration.isDuration(value): {
874
+ duration = value;
875
+ break
876
+ }
877
+
878
+ case isType(value, 'object'): {
879
+ duration = luxon.Duration.fromObject(value, options);
880
+ break
881
+ }
882
+
883
+ case isNumericNegatable(value): {
884
+ duration = luxon.Duration.fromMillis(value, options);
885
+ break
886
+ }
887
+
888
+ case (
889
+ isType(value, 'string') &&
890
+ stringNotEmpty(value) &&
891
+ hasSomeNumbers(value) &&
892
+ hasOnlyValidCharacters(value)
893
+ ): {
894
+ duration = luxon.Duration.fromISOTime(value, options);
895
+
896
+ if (!duration?.isValid) {
897
+ duration = luxon.Duration.fromISO(value, options);
898
+ }
899
+
900
+ break
901
+ }
902
+
903
+ default: {
904
+ break
905
+ }
906
+ }
907
+
908
+ if (duration && !duration.isValid) {
909
+ console.error('Error converting duration', { value, duration });
910
+ throw new Error(['Error converting duration', duration?.invalidExplanation].filter(stringNotEmpty).join('. '))
911
+ }
912
+ } catch (error) {
913
+ if (shouldThrow) {
914
+ throw error
915
+ }
916
+ duration = undefined;
917
+ }
918
+
919
+ return duration
920
+ };
921
+
922
+ const getDuration = (dt1, dt2) => (
923
+ luxon.Interval.fromDateTimes(convertDateTime(dt1), convertDateTime(dt2)).toDuration()
924
+ );
925
+
926
+ const getCurrentDateTime = () => (
927
+ luxon.DateTime.now()
928
+ );
929
+
930
+ const getDateTimeDifference = (dt1, dt2 = getCurrentDateTime(), unit) => (
931
+ dt2.diff(ensureDateTime(dt1), unit)?.[unit]
932
+ );
933
+
934
+ const getDaysBetween = (dt1, dt2) => (
935
+ Math.abs(getDateTimeDifference(dt1, dt2, 'days'))
936
+ );
937
+
938
+ const getWeeksBetween = (dt1, dt2) => (
939
+ Math.abs(getDateTimeDifference(dt1, dt2, 'weeks'))
940
+ );
941
+
942
+ const isSameDateTime = (dt1, dt2 = getCurrentDateTime(), unit) => (
943
+ // eslint-disable-next-line eqeqeq
944
+ dt1.startOf(unit).ts == dt2.startOf(unit).ts
945
+ );
946
+
947
+ const isSameHour = (dt1, dt2) => (
948
+ isSameDateTime(dt1, dt2, 'hour')
949
+ );
950
+
951
+ const isSameDay = (dt1, dt2) => (
952
+ isSameDateTime(dt1, dt2, 'day')
953
+ );
954
+
955
+ const ensureDateTime = (value, options) => (
956
+ convertDateTime(value, options) ?? getCurrentDateTime()
957
+ );
958
+
959
+ const formatTimestamps = (timestamps, defaultValue) => (
960
+ Object.entries(ensureObject(timestamps)).reduce((_timestamps, [name, value]) => ({
961
+ ..._timestamps,
962
+ [name]: value ? toUnixString(value) : defaultValue,
963
+ }), {})
964
+ );
965
+
966
+ const formatRequiredTimestamps = ({ created_at, updated_at, ...timestamps } = {}) => {
967
+ const _createdTimestamp = toUnixString(created_at);
968
+ const updatedTimestamp = updated_at ? toUnixString(updated_at) : _createdTimestamp;
969
+ const createdTimestamp = `${Math.min(_createdTimestamp, updatedTimestamp)}`; // HMMM: why doing math on strings??
970
+
971
+ const otherTimestamps = formatTimestamps(timestamps, createdTimestamp);
972
+ return { created_at: createdTimestamp, updated_at: updatedTimestamp, ...otherTimestamps }
973
+ };
974
+
975
+ const toJSDate = (value, options) => (
976
+ ensureDateTime(value, options).toJSDate()
977
+ );
978
+
979
+ const toUnixInteger = (value, options) => (
980
+ ensureDateTime(value, options).toUnixInteger()
981
+ );
982
+
983
+ // consolidate w/ toFormatted({ value, CUSTOM_FORMATS.UNIX })
984
+ const toUnixString = (value, options) => (
985
+ `${toUnixInteger(convertDateTime(value, options), options)}`
986
+ );
987
+
988
+ // consolidate w/ toFormatted({ value, format: CUSTOM_FORMATS.ISO })
989
+ const toISOString = (value, options) => (
990
+ ensureDateTime(value, options).toISO()
991
+ );
992
+
993
+ // consolidate w/ toFormatted({ value, format: CUSTOM_FORMATS.UTC })
994
+ const toUTCString = (value, options) => (
995
+ ensureDateTime(value, options).toUTC().toString()
996
+ );
997
+
998
+ // HMMM: return toString if no format (or similar)??
999
+ const toFormatted = ({ format, value, zone, ...options } = {}) => {
1000
+ // eslint-disable-next-line eqeqeq
1001
+ if (format == undefined) {
1002
+ throw new Error('Please supply a format')
1003
+ }
1004
+
1005
+ let datetime = ensureDateTime(value, options);
1006
+
1007
+ if (stringNotEmpty(zone)) {
1008
+ datetime = datetime.setZone(zone);
1009
+ }
1010
+
1011
+ switch (true) {
1012
+ case (format === CUSTOM_FORMATS.UTC): {
1013
+ return datetime.toUTC().toString()
1014
+ }
1015
+
1016
+ case (format === CUSTOM_FORMATS.ISO): {
1017
+ return datetime.toISO()
1018
+ }
1019
+
1020
+ case (format === CUSTOM_FORMATS.UNIX): {
1021
+ return `${toUnixInteger(datetime)}`
1022
+ }
1023
+
1024
+ case (isType(format, 'string')): {
1025
+ return datetime.toFormat(format, options)
1026
+ }
1027
+
1028
+ default: {
1029
+ if (!validLocaleFormats.includes(format)) {
1030
+ console.warn('Please consider using a format from LOCALE_FORMATS');
1031
+ }
1032
+
1033
+ return datetime.toLocaleString(format, options)
1034
+ }
1035
+ }
1036
+ };
1037
+
1038
+ const convertFormatted = ({
1039
+ value,
1040
+ to: { format: toFormat, ...toOptions } = {},
1041
+ from: { format: fromFormat, ...fromOptions } = {},
1042
+ } = {}) => {
1043
+ if (stringEmpty(toFormat) || stringEmpty(fromFormat)) {
1044
+ throw new Error('Please supply to/from formats')
1045
+ }
1046
+
1047
+ if (stringEmpty(value)) {
1048
+ return undefined
1049
+ }
1050
+
1051
+ return toFormatted({
1052
+ format: toFormat,
1053
+ value: getDateTimeFromString({ value, format: fromFormat, ...fromOptions }),
1054
+ ...toOptions,
1055
+ })
1056
+ };
1057
+
1058
+ const compareDate = (date1, date2) => (
1059
+ -convertDateTime(date1).diff(convertDateTime(date2)).as('milliseconds')
1060
+ );
1061
+
1062
+ const TEMP_HOSTNAME = 'xyz.com';
1063
+ const REMOVABLE_KEYS = ['shop', 'host'];
1064
+
1065
+ const HEADER_CONTENT_TYPE = 'Content-Type';
1066
+ const CONTENT_TYPES = {
1067
+ APPLICATION_JSON: 'application/json',
1068
+ };
1069
+
1070
+ const getRequestMethod = (body, _method) => {
1071
+ const method = _method ?? (body ? 'POST' : 'GET');
1072
+
1073
+ return uppercase(method)
1074
+ };
1075
+
1076
+ const prepareRequestOptions = ({ method: _method, body: _body, headers: _header, ...requestOptions } = {}) => {
1077
+ const method = getRequestMethod(_body, _method);
1078
+
1079
+ const headers = {
1080
+ [HEADER_CONTENT_TYPE]: CONTENT_TYPES.APPLICATION_JSON,
1081
+ ..._header,
1082
+ };
1083
+
1084
+ let body = _body;
1085
+ if (body && headers[HEADER_CONTENT_TYPE].startsWith(CONTENT_TYPES.APPLICATION_JSON)) {
1086
+ body = JSON.stringify(safeParse(body));
1087
+ }
1088
+
1089
+ return {
1090
+ method,
1091
+ headers,
1092
+ ...(body && (method !== 'GET')) && { body },
1093
+ ...requestOptions,
1094
+ }
1095
+ };
1096
+
1097
+ const convertToURL = (uri) => {
1098
+ if (stringEmpty(uri)) {
1099
+ return undefined
1100
+ }
1101
+
1102
+ const protocolRegex = /^(https?:\/\/).+/i;
1103
+
1104
+ if (!protocolRegex.test(uri)) {
1105
+ return new URL(`https://${TEMP_HOSTNAME}${ensureLeadingSlash(uri)}`)
1106
+ }
1107
+
1108
+ return new URL(uri)
1109
+ };
1110
+
1111
+ const normalizeURIParts = (uri) => {
1112
+ const urlObject = convertToURL(uri);
1113
+ if (!urlObject) {
1114
+ return undefined
1115
+ }
1116
+
1117
+ REMOVABLE_KEYS.forEach((key) => urlObject.searchParams.delete(key));
1118
+
1119
+ return {
1120
+ ...(urlObject.hostname !== TEMP_HOSTNAME) && {
1121
+ origin: urlObject.origin,
1122
+ },
1123
+ pathname: urlObject.pathname,
1124
+ search: urlObject.search,
1125
+ }
1126
+ };
1127
+
1128
+ var n,r=((n=function(){return r}).toString=n.toLocaleString=n[Symbol.toPrimitive]=function(){return ""},n.valueOf=function(){return !1},new Proxy(Object.freeze(n),{get:function(n,t){return n.hasOwnProperty(t)?n[t]:r}})),u=function(n){return n===r};
1129
+
1130
+ let win = global.window;
1131
+ let exists = (variable) => !u(variable);
1132
+ let window = typeof win !== "undefined" ? win : r;
1133
+
1134
+ class WebCrypto {
1135
+ constructor({ crypto: _crypto, util: _util } = {}) {
1136
+ this._crypto = _crypto ?? {};
1137
+ this._util = _util ?? {};
1138
+ }
1139
+
1140
+ get subtle() {
1141
+ return this._crypto?.subtle
1142
+ }
1143
+
1144
+ async getWebCrypto() {
1145
+ if (!this._crypto?.subtle) {
1146
+ try {
1147
+ this._crypto = (await import('isomorphic-webcrypto')).default;
1148
+ } catch (_ignore) {
1149
+ console.info('Failed to import isomorphic-webcrypto, retrying w/ node crypto');
1150
+ try {
1151
+ this._crypto = (await import('crypto')).default;
1152
+ } catch (error) {
1153
+ // eslint-disable-next-line max-len
1154
+ console.error(`Failed to import node crypto, ensure 'isomorphic-webcrypto' (or node 'crypto') is installed and/or pass in implementation via 'new WebCrypto({ crypto })'`);
1155
+ throw error
1156
+ }
1157
+ }
1158
+ }
1159
+
1160
+ if (!this._crypto?.subtle) {
1161
+ throw new Error('Invalid crypto, missing subtle')
1162
+ }
1163
+
1164
+ return this._crypto
1165
+ }
1166
+
1167
+ async getTextDecoder() {
1168
+ if (this._util?.TextDecoder) {
1169
+ return this._util.TextDecoder
1170
+ }
1171
+
1172
+ if (exists(window) && typeof window?.TextDecoder === 'function') {
1173
+ return window.TextDecoder
1174
+ }
1175
+
1176
+ try {
1177
+ const TextDecoder = (await import('util')).TextDecoder;
1178
+ this._util.TextDecoder = TextDecoder;
1179
+
1180
+ return TextDecoder
1181
+ } catch (error) {
1182
+ console.error(`Failed to import 'utils.TextDecoder', ensure 'util' is available and/or pass in implementation via 'new WebCrypto({ util })'`);
1183
+ throw error
1184
+ }
1185
+ }
1186
+
1187
+ async getTextEncoder() {
1188
+ if (this._util?.TextEncoder) {
1189
+ return this._util.TextEncoder
1190
+ }
1191
+
1192
+ if (exists(window) && typeof window?.TextEncoder === 'function') {
1193
+ return window.TextEncoder
1194
+ }
1195
+
1196
+ try {
1197
+ const TextEncoder = (await import('util')).TextEncoder;
1198
+ this._util.TextEncoder = TextEncoder;
1199
+
1200
+ return TextEncoder
1201
+ } catch (error) {
1202
+ console.error(`Failed to import 'utils.TextEncoder', ensure 'util' is available and/or pass in implementation via 'new WebCrypto({ util })'`);
1203
+ throw error
1204
+ }
1205
+ }
1206
+
1207
+ timingSafeEqual(value1, value2) {
1208
+ if (
1209
+ (value1 == undefined) ||
1210
+ (value2 == undefined) ||
1211
+ (value1.length !== value2.length)
1212
+ ) {
1213
+ return false
1214
+ }
1215
+
1216
+ let result = 0;
1217
+ // eslint-disable-next-line no-plusplus
1218
+ for (let i = 0; i < value1.length; i++) {
1219
+ // eslint-disable-next-line no-bitwise
1220
+ result |= value1[i] ^ value2[i];
1221
+ }
1222
+
1223
+ return (result === 0)
1224
+ }
1225
+
1226
+ stringToHex(stringValue) {
1227
+ return (
1228
+ Array.from(ensureString(stringValue), (char) => (
1229
+ char.charCodeAt(0).toString(16).padStart(2, '0')
1230
+ )).join('')
1231
+ )
1232
+ }
1233
+
1234
+ hexToString(hexValue) {
1235
+ return (
1236
+ ensureArray(
1237
+ ensureString(hexValue).match(/.{1,2}/g)
1238
+ )
1239
+ .map((byte) => String.fromCharCode(parseInt(byte, 16)))
1240
+ .join('')
1241
+ )
1242
+ }
1243
+
1244
+ async arrayBufferToString(arrayBuffer) {
1245
+ const uint8Array = new Uint8Array(arrayBuffer);
1246
+ const Decoder = await this.getTextDecoder();
1247
+ return new Decoder().decode(uint8Array)
1248
+ }
1249
+
1250
+ arrayToArrayBuffer(array) {
1251
+ return (
1252
+ (ArrayBuffer.from != undefined)
1253
+ ? ArrayBuffer.from(array)
1254
+ : new Uint8Array(array).buffer
1255
+ )
1256
+ }
1257
+
1258
+ ensureArrayBuffer(arrayOrArrayBuffer) {
1259
+ return (
1260
+ (arrayOrArrayBuffer instanceof ArrayBuffer)
1261
+ ? arrayOrArrayBuffer
1262
+ : this.arrayToArrayBuffer(arrayOrArrayBuffer)
1263
+ )
1264
+ }
1265
+
1266
+ async generateKeyPair() {
1267
+ const crypto = await this.getWebCrypto();
1268
+ const keyPair = await crypto.subtle.generateKey(
1269
+ {
1270
+ name: 'ECDH',
1271
+ namedCurve: 'P-256',
1272
+ },
1273
+ true,
1274
+ ['deriveKey']
1275
+ );
1276
+
1277
+ return keyPair
1278
+ }
1279
+
1280
+ getKeyOperations(keyType) {
1281
+ switch (keyType) {
1282
+ case 'private':
1283
+ case 'privateKey': {
1284
+ return ['deriveKey']
1285
+ }
1286
+
1287
+ case 'secret':
1288
+ case 'secretKey':
1289
+ case 'sharedSecret': {
1290
+ return ['encrypt', 'decrypt']
1291
+ }
1292
+
1293
+ default: {
1294
+ return []
1295
+ }
1296
+ }
1297
+ }
1298
+
1299
+ getKeyAlgorythm(keyType) {
1300
+ switch (keyType) {
1301
+ case 'derivedKey':
1302
+ case 'derived':
1303
+ case 'secret':
1304
+ case 'secretKey':
1305
+ case 'sharedSecret': {
1306
+ return {
1307
+ name: 'AES-GCM',
1308
+ }
1309
+ }
1310
+
1311
+ default: {
1312
+ return {
1313
+ name: 'ECDH',
1314
+ namedCurve: 'P-256',
1315
+ }
1316
+ }
1317
+ }
1318
+ }
1319
+
1320
+ async generateHMAC(message, derivedKey) {
1321
+ if (!message || !derivedKey) {
1322
+ return undefined
1323
+ }
1324
+
1325
+ const crypto = await this.getWebCrypto();
1326
+ const Encoder = await this.getTextEncoder();
1327
+
1328
+ const signature = await crypto.subtle.sign(
1329
+ 'HMAC',
1330
+ derivedKey,
1331
+ new Encoder().encode(message)
1332
+ );
1333
+
1334
+ return this.stringToHex(
1335
+ this.arrayBufferToString(signature)
1336
+ )
1337
+ }
1338
+
1339
+ async verifyHMAC(message, derivedKey, verifiableHMAC) {
1340
+ const calculatedHMAC = await this.generateHMAC(message, derivedKey);
1341
+
1342
+ return this.timingSafeEqual(calculatedHMAC, verifiableHMAC)
1343
+ }
1344
+
1345
+ async getStorableKey(key) {
1346
+ const crypto = await this.getWebCrypto();
1347
+
1348
+ const exportedJWK = await crypto.subtle.exportKey('jwk', key);
1349
+ return this.stringToHex(JSON.stringify(exportedJWK))
1350
+ }
1351
+
1352
+ async restoreStorableKey(keyType, storableHex) {
1353
+ if (!storableHex) {
1354
+ return undefined
1355
+ }
1356
+ const crypto = await this.getWebCrypto();
1357
+
1358
+ const exportedJWK = JSON.parse(this.hexToString(storableHex) || '{}');
1359
+ if (objectEmpty(exportedJWK)) {
1360
+ return undefined
1361
+ }
1362
+
1363
+ return crypto.subtle.importKey(
1364
+ 'jwk',
1365
+ exportedJWK,
1366
+ this.getKeyAlgorythm(keyType),
1367
+ true,
1368
+ this.getKeyOperations(keyType)
1369
+ )
1370
+ }
1371
+
1372
+ async getStorableKeyPair(keyPair) {
1373
+ const storableKeys = {};
1374
+
1375
+ // eslint-disable-next-line no-restricted-syntax
1376
+ for (const [keyType, key] of Object.entries(keyPair)) {
1377
+ // eslint-disable-next-line no-await-in-loop
1378
+ storableKeys[keyType] = await this.getStorableKey(key);
1379
+ }
1380
+
1381
+ return storableKeys
1382
+ }
1383
+
1384
+ async restoreStorableKeyPair(keyPair) {
1385
+ const restoredKeys = {};
1386
+
1387
+ // eslint-disable-next-line no-restricted-syntax
1388
+ for (const [keyType, key] of Object.entries(keyPair)) {
1389
+ // eslint-disable-next-line no-await-in-loop
1390
+ restoredKeys[keyType] = await this.restoreStorableKey(keyType, key);
1391
+ }
1392
+
1393
+ return restoredKeys
1394
+ }
1395
+
1396
+ async deriveSharedKey({ publicKey, privateKey }) {
1397
+ if (!publicKey || !privateKey) {
1398
+ return undefined
1399
+ }
1400
+
1401
+ const crypto = await this.getWebCrypto();
1402
+ const derivedKey = await crypto.subtle.deriveKey(
1403
+ {
1404
+ name: 'ECDH',
1405
+ public: publicKey,
1406
+ },
1407
+ privateKey,
1408
+ {
1409
+ name: 'AES-GCM',
1410
+ length: 256,
1411
+ },
1412
+ true,
1413
+ ['encrypt', 'decrypt']
1414
+ );
1415
+ return derivedKey
1416
+ }
1417
+
1418
+ async deriveHMACKey({ publicKey, privateKey }) {
1419
+ if (!publicKey || !privateKey) {
1420
+ return undefined
1421
+ }
1422
+
1423
+ const crypto = await this.getWebCrypto();
1424
+ const derivedKey = await crypto.subtle.deriveKey(
1425
+ {
1426
+ name: 'ECDH',
1427
+ public: publicKey,
1428
+ },
1429
+ privateKey,
1430
+ {
1431
+ name: 'HMAC',
1432
+ hash: { name: 'SHA-256' },
1433
+ length: 256, // Adjusted key length, e.g., 128 bits
1434
+ },
1435
+ true,
1436
+ ['sign', 'verify']
1437
+ );
1438
+ return derivedKey
1439
+ }
1440
+
1441
+ async getVerificationKeys({ publicKey, privateKey }) {
1442
+ if (!publicKey || !privateKey) {
1443
+ return {}
1444
+ }
1445
+
1446
+ const sharedKeyPair = await this.restoreStorableKeyPair({ publicKey, privateKey });
1447
+ const derivedHMACKey = await this.deriveHMACKey(sharedKeyPair);
1448
+ const derivedSecretKey = await this.deriveSharedKey(sharedKeyPair);
1449
+
1450
+ return {
1451
+ derivedSecretKey,
1452
+ derivedHMACKey,
1453
+ }
1454
+ }
1455
+
1456
+ async encryptMessage(decryptedMessage, derivedKey) {
1457
+ if (!decryptedMessage || !derivedKey) {
1458
+ return undefined
1459
+ }
1460
+
1461
+ const crypto = await this.getWebCrypto();
1462
+ const iv = crypto.getRandomValues(new Uint8Array(12));
1463
+ const Encoder = await this.getTextEncoder();
1464
+ const encodedMessage = new Encoder().encode(decryptedMessage);
1465
+ const ciphertext = await crypto.subtle.encrypt(
1466
+ {
1467
+ name: 'AES-GCM',
1468
+ iv,
1469
+ },
1470
+ derivedKey,
1471
+ encodedMessage
1472
+ );
1473
+
1474
+ const encryptedMessage = new Uint8Array([
1475
+ ...iv,
1476
+ ...new Uint8Array(ciphertext)
1477
+ ]);
1478
+ return Array.from(encryptedMessage)
1479
+ }
1480
+
1481
+ async decryptMessage(encryptedMessage, derivedKey) {
1482
+ if (!encryptedMessage || !derivedKey) {
1483
+ return undefined
1484
+ }
1485
+
1486
+ const crypto = await this.getWebCrypto();
1487
+ // NOTE: this presumed an array or arrayBuffer coming in as encryptedMessage (will fail w/ IV error if its a string)
1488
+ const encryptedArrayBuffer = this.ensureArrayBuffer(encryptedMessage);
1489
+ const iv = encryptedArrayBuffer.slice(0, 12);
1490
+ const ciphertext = encryptedArrayBuffer.slice(12);
1491
+
1492
+ const decryptedArrayBuffer = await crypto.subtle.decrypt(
1493
+ {
1494
+ name: 'AES-GCM',
1495
+ iv,
1496
+ },
1497
+ derivedKey,
1498
+ ciphertext
1499
+ );
1500
+
1501
+ const Decoder = await this.getTextDecoder();
1502
+ const decryptedMessage = new Decoder().decode(decryptedArrayBuffer);
1503
+ return decryptedMessage
1504
+ }
1505
+ }
1506
+
1507
+ const getKeysData = async (publicKey, { crypto: _crypto, util: _util } = {}) => {
1508
+ const webCrypto = new WebCrypto({ crypto: _crypto, util: _util });
1509
+
1510
+ const _ephemeralStoreableKeyPair = await webCrypto.getStorableKeyPair(
1511
+ await webCrypto.generateKeyPair(publicKey)
1512
+ );
1513
+
1514
+ const _verificationKeyPair = await webCrypto.getVerificationKeys({
1515
+ publicKey,
1516
+ privateKey: _ephemeralStoreableKeyPair.privateKey,
1517
+ });
1518
+
1519
+ return {
1520
+ publicKey,
1521
+ ephemeral: _ephemeralStoreableKeyPair,
1522
+ verification: _verificationKeyPair,
1523
+ }
1524
+ };
1525
+
1526
+ // eslint-disable-next-line arrow-body-style
1527
+ const prepareVerificationRequest = ({ keysData: _keysData, disableRecryption: _disableRecryption, crypto: _crypto, util: _util } = {}) => {
1528
+ const webCrypto = new WebCrypto({ crypto: _crypto, util: _util });
1529
+ const disableRecryption = isTrue(_disableRecryption);
1530
+
1531
+ return async (requestPath, { method: rawMethod, body: rawBody, headers: rawHeaders, ...requestOptions } = {}) => {
1532
+ let parsedBody = safeParse(rawBody);
1533
+ const method = getRequestMethod(parsedBody, rawMethod);
1534
+
1535
+ if (disableRecryption || stringEmpty(_keysData?.publicKey)) {
1536
+ return [
1537
+ requestPath,
1538
+ prepareRequestOptions({
1539
+ method,
1540
+ body: parsedBody,
1541
+ headers: rawHeaders,
1542
+ ...requestOptions,
1543
+ })
1544
+ ]
1545
+ }
1546
+
1547
+ const {
1548
+ verification: {
1549
+ derivedHMACKey,
1550
+ derivedSecretKey,
1551
+ } = {},
1552
+ ephemeral: {
1553
+ publicKey: ephemeralPublicKey,
1554
+ } = {},
1555
+ } = _keysData ?? {};
1556
+
1557
+ if (!derivedHMACKey || !ephemeralPublicKey) {
1558
+ return undefined
1559
+ }
1560
+
1561
+ if (parsedBody && derivedSecretKey) {
1562
+ parsedBody = await webCrypto.encryptMessage(JSON.stringify(parsedBody), derivedSecretKey);
1563
+ }
1564
+
1565
+ const timestamp = toUnixString();
1566
+ const computedHMAC = await webCrypto.generateHMAC(
1567
+ objectToSortedString({
1568
+ body: parsedBody,
1569
+ method,
1570
+ timestamp,
1571
+ ...normalizeURIParts(requestPath),
1572
+ }),
1573
+ derivedHMACKey
1574
+ );
1575
+
1576
+ return [
1577
+ requestPath,
1578
+ prepareRequestOptions({
1579
+ method,
1580
+ body: parsedBody,
1581
+ headers: {
1582
+ 'X-Authorization': `HMAC-SHA256 ${computedHMAC}`,
1583
+ 'X-Authorization-Timestamp': timestamp,
1584
+ 'X-Ephemeral-Key': ephemeralPublicKey,
1585
+ 'X-Public-Key': _keysData.publicKey,
1586
+ ...rawHeaders,
1587
+ },
1588
+ ...requestOptions,
1589
+ }),
1590
+ derivedSecretKey
1591
+ ]
1592
+ }
1593
+ };
1594
+
1595
+ const processVerificationResponse = ({ keysData, disableRecryption: _disableRecryption, crypto: _crypto, util: _util } = {}) => {
1596
+ const webCrypto = new WebCrypto({ crypto: _crypto, util: _util });
1597
+ const disableRecryption = isTrue(_disableRecryption);
1598
+
1599
+ return async (encryptedResponse, _derivedSecretKey) => {
1600
+ const derivedSecretKey = _derivedSecretKey ?? keysData.verification?.derivedSecretKey;
1601
+ if (disableRecryption || !encryptedResponse || !derivedSecretKey) {
1602
+ return encryptedResponse
1603
+ }
1604
+
1605
+ const decryptedMessage = await webCrypto.decryptMessage(encryptedResponse, derivedSecretKey);
1606
+ return safeParse(decryptedMessage)
1607
+ }
1608
+ };
1609
+
1610
+ class VerificationError extends Error {
1611
+ constructor(message, _data) {
1612
+ super(message);
1613
+ const { code = 500, ...data } = _data ?? {};
1614
+ this.code = code;
1615
+ this.data = data;
1616
+ this.name = 'VerificationError';
1617
+ Object.setPrototypeOf(this, VerificationError.prototype);
1618
+ }
1619
+ }
1620
+
1621
+ const getChunkedRawBody = async (readable) => {
1622
+ if (readable?.rawBody) {
1623
+ return readable.rawBody
1624
+ }
1625
+
1626
+ // TODO: move to req.text() after next 13.5: https://github.com/vercel/next.js/discussions/13405
1627
+ try {
1628
+ const getRawBody = (await Promise.resolve().then(function () { return index; })).default;
1629
+ return getRawBody(readable)
1630
+ } catch (error) {
1631
+ console.error(`Failed to import 'raw-body', please ensure the dependency is installed`);
1632
+ throw error
1633
+ }
1634
+ };
1635
+
1636
+ // TODO: explore returning mutated request object adding on req.rawBody??
1637
+ const ensureRawBody = async (req) => (
1638
+ getChunkedRawBody(req)
1639
+ .then((_rawBody) => _rawBody.toString())
1640
+ .catch((error) => {
1641
+ console.error(`Error getting raw body for '${req?.url}'`, error);
1642
+ throw error
1643
+ })
1644
+ );
1645
+
1646
+ const getVerificationHelpers = ({ keyPairs, crypto: _crypto, util: _util } = {}) => {
1647
+ const webCrypto = new WebCrypto({ crypto: _crypto, util: _util });
1648
+
1649
+ return async (req, params) => {
1650
+ const {
1651
+ 'x-public-key': apiKey,
1652
+ 'x-ephemeral-key': ephemeralPublicKey,
1653
+ 'x-authorization-timestamp': customAuthTimestamp,
1654
+ 'x-authorization': customAuth,
1655
+ } = req.headers ?? {};
1656
+
1657
+ const { uri: _uri, disableRecryption: _disableRecryption } = params ?? {};
1658
+ const uri = _uri ?? req.url;
1659
+ const disableRecryption = isTrue(_disableRecryption);
1660
+
1661
+ let isVerifiable;
1662
+ try {
1663
+ const [authProtocol, authSignature] = ensureString(customAuth).split(' ');
1664
+ isVerifiable = isTrue(
1665
+ apiKey &&
1666
+ ephemeralPublicKey &&
1667
+ keyPairs?.shared &&
1668
+ customAuthTimestamp &&
1669
+ authSignature &&
1670
+ (authProtocol === 'HMAC-SHA256')
1671
+ );
1672
+
1673
+ let verificationKeys;
1674
+ const rawBody = await ensureRawBody(req);
1675
+
1676
+ // NOTE: requestBody should be wind up decrypted when isVerifiable (unless disableRecryption, then will pass through)
1677
+ let requestBody = safeParse(rawBody);
1678
+
1679
+ // TEMP!!! remove isVerifiable check once webwidget moved to react
1680
+ if (isVerifiable) {
1681
+ if (
1682
+ !apiKey ||
1683
+ !ephemeralPublicKey ||
1684
+ !customAuthTimestamp ||
1685
+ !authSignature ||
1686
+ !keyPairs?.shared ||
1687
+ (apiKey !== keyPairs.shared.publicKey) ||
1688
+ (authProtocol !== 'HMAC-SHA256')
1689
+ ) {
1690
+ throw new VerificationError('Invalid or missing authorization', { code: 401 })
1691
+ }
1692
+
1693
+ verificationKeys = await webCrypto.getVerificationKeys({
1694
+ publicKey: ephemeralPublicKey,
1695
+ privateKey: keyPairs.shared.privateKey,
1696
+ });
1697
+
1698
+ if (!verificationKeys) {
1699
+ throw new VerificationError('Invalid or missing verification', { code: 412 })
1700
+ }
1701
+
1702
+ const verificationPayload = objectToSortedString({
1703
+ method: getRequestMethod(rawBody, req.method),
1704
+ timestamp: customAuthTimestamp,
1705
+ body: requestBody, // NOTE: requestBody should be encrypted when isVerifiable
1706
+ ...normalizeURIParts(uri),
1707
+ });
1708
+
1709
+ const isValid = await webCrypto.verifyHMAC(
1710
+ verificationPayload,
1711
+ verificationKeys.derivedHMACKey,
1712
+ authSignature
1713
+ );
1714
+
1715
+ if (!isValid) {
1716
+ throw new VerificationError('Invalid or missing verification', { code: 412 })
1717
+ }
1718
+
1719
+ if (!disableRecryption && requestBody) {
1720
+ const decryptedMessage = await webCrypto.decryptMessage(requestBody, verificationKeys.derivedSecretKey);
1721
+ requestBody = safeParse(decryptedMessage);
1722
+ }
1723
+ }
1724
+
1725
+ const processResponse = async (response) => {
1726
+ if (!response || disableRecryption || !isVerifiable || !verificationKeys?.derivedSecretKey) {
1727
+ return response
1728
+ }
1729
+
1730
+ return webCrypto.encryptMessage(JSON.stringify(response), verificationKeys.derivedSecretKey)
1731
+ };
1732
+
1733
+ return { rawBody, requestBody, processResponse }
1734
+ } catch (error) {
1735
+ console.error(`Error handling request verification for '${uri}'`, { error, isVerifiable, disableRecryption });
1736
+ throw error
1737
+ }
1738
+ }
1739
+ };
1740
+
1741
+ /*!
1742
+ * raw-body
1743
+ * Copyright(c) 2013-2014 Jonathan Ong
1744
+ * Copyright(c) 2014-2022 Douglas Christopher Wilson
1745
+ * MIT Licensed
1746
+ */
1747
+
1748
+ /**
1749
+ * Module dependencies.
1750
+ * @private
1751
+ */
1752
+
1753
+ var asyncHooks = tryRequireAsyncHooks();
1754
+ var bytes = require('bytes');
1755
+ var createError = require('http-errors');
1756
+ var iconv = require('iconv-lite');
1757
+ var unpipe = require('unpipe');
1758
+
1759
+ /**
1760
+ * Module exports.
1761
+ * @public
1762
+ */
1763
+
1764
+ module.exports = getRawBody;
1765
+
1766
+ /**
1767
+ * Module variables.
1768
+ * @private
1769
+ */
1770
+
1771
+ var ICONV_ENCODING_MESSAGE_REGEXP = /^Encoding not recognized: /;
1772
+
1773
+ /**
1774
+ * Get the decoder for a given encoding.
1775
+ *
1776
+ * @param {string} encoding
1777
+ * @private
1778
+ */
1779
+
1780
+ function getDecoder (encoding) {
1781
+ if (!encoding) return null
1782
+
1783
+ try {
1784
+ return iconv.getDecoder(encoding)
1785
+ } catch (e) {
1786
+ // error getting decoder
1787
+ if (!ICONV_ENCODING_MESSAGE_REGEXP.test(e.message)) throw e
1788
+
1789
+ // the encoding was not found
1790
+ throw createError(415, 'specified encoding unsupported', {
1791
+ encoding: encoding,
1792
+ type: 'encoding.unsupported'
1793
+ })
1794
+ }
1795
+ }
1796
+
1797
+ /**
1798
+ * Get the raw body of a stream (typically HTTP).
1799
+ *
1800
+ * @param {object} stream
1801
+ * @param {object|string|function} [options]
1802
+ * @param {function} [callback]
1803
+ * @public
1804
+ */
1805
+
1806
+ function getRawBody (stream, options, callback) {
1807
+ var done = callback;
1808
+ var opts = options || {};
1809
+
1810
+ // light validation
1811
+ if (stream === undefined) {
1812
+ throw new TypeError('argument stream is required')
1813
+ } else if (typeof stream !== 'object' || stream === null || typeof stream.on !== 'function') {
1814
+ throw new TypeError('argument stream must be a stream')
1815
+ }
1816
+
1817
+ if (options === true || typeof options === 'string') {
1818
+ // short cut for encoding
1819
+ opts = {
1820
+ encoding: options
1821
+ };
1822
+ }
1823
+
1824
+ if (typeof options === 'function') {
1825
+ done = options;
1826
+ opts = {};
1827
+ }
1828
+
1829
+ // validate callback is a function, if provided
1830
+ if (done !== undefined && typeof done !== 'function') {
1831
+ throw new TypeError('argument callback must be a function')
1832
+ }
1833
+
1834
+ // require the callback without promises
1835
+ if (!done && !global.Promise) {
1836
+ throw new TypeError('argument callback is required')
1837
+ }
1838
+
1839
+ // get encoding
1840
+ var encoding = opts.encoding !== true
1841
+ ? opts.encoding
1842
+ : 'utf-8';
1843
+
1844
+ // convert the limit to an integer
1845
+ var limit = bytes.parse(opts.limit);
1846
+
1847
+ // convert the expected length to an integer
1848
+ var length = opts.length != null && !isNaN(opts.length)
1849
+ ? parseInt(opts.length, 10)
1850
+ : null;
1851
+
1852
+ if (done) {
1853
+ // classic callback style
1854
+ return readStream(stream, encoding, length, limit, wrap(done))
1855
+ }
1856
+
1857
+ return new Promise(function executor (resolve, reject) {
1858
+ readStream(stream, encoding, length, limit, function onRead (err, buf) {
1859
+ if (err) return reject(err)
1860
+ resolve(buf);
1861
+ });
1862
+ })
1863
+ }
1864
+
1865
+ /**
1866
+ * Halt a stream.
1867
+ *
1868
+ * @param {Object} stream
1869
+ * @private
1870
+ */
1871
+
1872
+ function halt (stream) {
1873
+ // unpipe everything from the stream
1874
+ unpipe(stream);
1875
+
1876
+ // pause stream
1877
+ if (typeof stream.pause === 'function') {
1878
+ stream.pause();
1879
+ }
1880
+ }
1881
+
1882
+ /**
1883
+ * Read the data from the stream.
1884
+ *
1885
+ * @param {object} stream
1886
+ * @param {string} encoding
1887
+ * @param {number} length
1888
+ * @param {number} limit
1889
+ * @param {function} callback
1890
+ * @public
1891
+ */
1892
+
1893
+ function readStream (stream, encoding, length, limit, callback) {
1894
+ var complete = false;
1895
+ var sync = true;
1896
+
1897
+ // check the length and limit options.
1898
+ // note: we intentionally leave the stream paused,
1899
+ // so users should handle the stream themselves.
1900
+ if (limit !== null && length !== null && length > limit) {
1901
+ return done(createError(413, 'request entity too large', {
1902
+ expected: length,
1903
+ length: length,
1904
+ limit: limit,
1905
+ type: 'entity.too.large'
1906
+ }))
1907
+ }
1908
+
1909
+ // streams1: assert request encoding is buffer.
1910
+ // streams2+: assert the stream encoding is buffer.
1911
+ // stream._decoder: streams1
1912
+ // state.encoding: streams2
1913
+ // state.decoder: streams2, specifically < 0.10.6
1914
+ var state = stream._readableState;
1915
+ if (stream._decoder || (state && (state.encoding || state.decoder))) {
1916
+ // developer error
1917
+ return done(createError(500, 'stream encoding should not be set', {
1918
+ type: 'stream.encoding.set'
1919
+ }))
1920
+ }
1921
+
1922
+ if (typeof stream.readable !== 'undefined' && !stream.readable) {
1923
+ return done(createError(500, 'stream is not readable', {
1924
+ type: 'stream.not.readable'
1925
+ }))
1926
+ }
1927
+
1928
+ var received = 0;
1929
+ var decoder;
1930
+
1931
+ try {
1932
+ decoder = getDecoder(encoding);
1933
+ } catch (err) {
1934
+ return done(err)
1935
+ }
1936
+
1937
+ var buffer = decoder
1938
+ ? ''
1939
+ : [];
1940
+
1941
+ // attach listeners
1942
+ stream.on('aborted', onAborted);
1943
+ stream.on('close', cleanup);
1944
+ stream.on('data', onData);
1945
+ stream.on('end', onEnd);
1946
+ stream.on('error', onEnd);
1947
+
1948
+ // mark sync section complete
1949
+ sync = false;
1950
+
1951
+ function done () {
1952
+ var args = new Array(arguments.length);
1953
+
1954
+ // copy arguments
1955
+ for (var i = 0; i < args.length; i++) {
1956
+ args[i] = arguments[i];
1957
+ }
1958
+
1959
+ // mark complete
1960
+ complete = true;
1961
+
1962
+ if (sync) {
1963
+ process.nextTick(invokeCallback);
1964
+ } else {
1965
+ invokeCallback();
1966
+ }
1967
+
1968
+ function invokeCallback () {
1969
+ cleanup();
1970
+
1971
+ if (args[0]) {
1972
+ // halt the stream on error
1973
+ halt(stream);
1974
+ }
1975
+
1976
+ callback.apply(null, args);
1977
+ }
1978
+ }
1979
+
1980
+ function onAborted () {
1981
+ if (complete) return
1982
+
1983
+ done(createError(400, 'request aborted', {
1984
+ code: 'ECONNABORTED',
1985
+ expected: length,
1986
+ length: length,
1987
+ received: received,
1988
+ type: 'request.aborted'
1989
+ }));
1990
+ }
1991
+
1992
+ function onData (chunk) {
1993
+ if (complete) return
1994
+
1995
+ received += chunk.length;
1996
+
1997
+ if (limit !== null && received > limit) {
1998
+ done(createError(413, 'request entity too large', {
1999
+ limit: limit,
2000
+ received: received,
2001
+ type: 'entity.too.large'
2002
+ }));
2003
+ } else if (decoder) {
2004
+ buffer += decoder.write(chunk);
2005
+ } else {
2006
+ buffer.push(chunk);
2007
+ }
2008
+ }
2009
+
2010
+ function onEnd (err) {
2011
+ if (complete) return
2012
+ if (err) return done(err)
2013
+
2014
+ if (length !== null && received !== length) {
2015
+ done(createError(400, 'request size did not match content length', {
2016
+ expected: length,
2017
+ length: length,
2018
+ received: received,
2019
+ type: 'request.size.invalid'
2020
+ }));
2021
+ } else {
2022
+ var string = decoder
2023
+ ? buffer + (decoder.end() || '')
2024
+ : Buffer.concat(buffer);
2025
+ done(null, string);
2026
+ }
2027
+ }
2028
+
2029
+ function cleanup () {
2030
+ buffer = null;
2031
+
2032
+ stream.removeListener('aborted', onAborted);
2033
+ stream.removeListener('data', onData);
2034
+ stream.removeListener('end', onEnd);
2035
+ stream.removeListener('error', onEnd);
2036
+ stream.removeListener('close', cleanup);
2037
+ }
2038
+ }
2039
+
2040
+ /**
2041
+ * Try to require async_hooks
2042
+ * @private
2043
+ */
2044
+
2045
+ function tryRequireAsyncHooks () {
2046
+ try {
2047
+ return require('async_hooks')
2048
+ } catch (e) {
2049
+ return {}
2050
+ }
2051
+ }
2052
+
2053
+ /**
2054
+ * Wrap function with async resource, if possible.
2055
+ * AsyncResource.bind static method backported.
2056
+ * @private
2057
+ */
2058
+
2059
+ function wrap (fn) {
2060
+ var res;
2061
+
2062
+ // create anonymous resource
2063
+ if (asyncHooks.AsyncResource) {
2064
+ res = new asyncHooks.AsyncResource(fn.name || 'bound-anonymous-fn');
2065
+ }
2066
+
2067
+ // incompatible node.js
2068
+ if (!res || !res.runInAsyncScope) {
2069
+ return fn
2070
+ }
2071
+
2072
+ // return bound function
2073
+ return res.runInAsyncScope.bind(res, fn, null)
2074
+ }
2075
+
2076
+ var index = /*#__PURE__*/Object.freeze({
2077
+ __proto__: null
2078
+ });
2079
+
2080
+ exports.CONTENT_TYPES = CONTENT_TYPES;
2081
+ exports.CUSTOM_FORMATS = CUSTOM_FORMATS;
2082
+ exports.DATE_FORMAT_LONG = DATE_FORMAT_LONG;
2083
+ exports.DATE_FORMAT_MED = DATE_FORMAT_MED;
2084
+ exports.DATE_FORMAT_ORGANIZED = DATE_FORMAT_ORGANIZED;
2085
+ exports.DATE_FORMAT_TRACKING = DATE_FORMAT_TRACKING;
2086
+ exports.HEADER_CONTENT_TYPE = HEADER_CONTENT_TYPE;
2087
+ exports.LOCALE_FORMATS = LOCALE_FORMATS;
2088
+ exports.TIME_FORMAT_AMPM = TIME_FORMAT_AMPM;
2089
+ exports.TIME_FORMAT_LONG = TIME_FORMAT_LONG;
2090
+ exports.VerificationError = VerificationError;
2091
+ exports.WebCrypto = WebCrypto;
2092
+ exports.appendQuery = appendQuery;
2093
+ exports.arrayEmpty = arrayEmpty;
2094
+ exports.arrayIncludesAll = arrayIncludesAll;
2095
+ exports.arrayNotEmpty = arrayNotEmpty;
2096
+ exports.arrayRandom = arrayRandom;
2097
+ exports.arrayRandomItem = arrayRandomItem;
2098
+ exports.arrayToObject = arrayToObject;
2099
+ exports.arraysMatch = arraysMatch;
2100
+ exports.camelCase = camelCase;
2101
+ exports.capitalize = capitalize;
2102
+ exports.cleanObject = cleanObject;
2103
+ exports.combineCommas = combineCommas;
2104
+ exports.compareDate = compareDate;
2105
+ exports.compareEntryKeys = compareEntryKeys;
2106
+ exports.compareNumber = compareNumber;
2107
+ exports.compareString = compareString;
2108
+ exports.convertDateTime = convertDateTime;
2109
+ exports.convertDuration = convertDuration;
2110
+ exports.convertFormatted = convertFormatted;
2111
+ exports.dashcase = dashcase;
2112
+ exports.ensureAlphaNumeric = ensureAlphaNumeric;
2113
+ exports.ensureArray = ensureArray;
2114
+ exports.ensureArraySet = ensureArraySet;
2115
+ exports.ensureDateTime = ensureDateTime;
2116
+ exports.ensureExtension = ensureExtension;
2117
+ exports.ensureLeadingSlash = ensureLeadingSlash;
2118
+ exports.ensureNumeric = ensureNumeric;
2119
+ exports.ensureNumericConstrained = ensureNumericConstrained;
2120
+ exports.ensureNumericNegatable = ensureNumericNegatable;
2121
+ exports.ensureNumericOnly = ensureNumericOnly;
2122
+ exports.ensureObject = ensureObject;
2123
+ exports.ensureRawBody = ensureRawBody;
2124
+ exports.ensureString = ensureString;
2125
+ exports.ensureStringAscii = ensureStringAscii;
2126
+ exports.ensureStringClean = ensureStringClean;
2127
+ exports.ensureStringOnly = ensureStringOnly;
2128
+ exports.ensureTrailingSlash = ensureTrailingSlash;
2129
+ exports.findLastMatch = findLastMatch;
2130
+ exports.formatRequiredTimestamps = formatRequiredTimestamps;
2131
+ exports.formatTimestamps = formatTimestamps;
2132
+ exports.freeze = freeze;
2133
+ exports.getCurrentDateTime = getCurrentDateTime;
2134
+ exports.getDateTimeFormat = getDateTimeFormat;
2135
+ exports.getDateTimeFromString = getDateTimeFromString;
2136
+ exports.getDaysBetween = getDaysBetween;
2137
+ exports.getDaysInMonth = getDaysInMonth;
2138
+ exports.getDuration = getDuration;
2139
+ exports.getKeysData = getKeysData;
2140
+ exports.getRequestMethod = getRequestMethod;
2141
+ exports.getVerificationHelpers = getVerificationHelpers;
2142
+ exports.getWeeksBetween = getWeeksBetween;
2143
+ exports.isArray = isArray;
2144
+ exports.isHTML = isHTML;
2145
+ exports.isNumericNegatable = isNumericNegatable;
2146
+ exports.isNumericOnly = isNumericOnly;
2147
+ exports.isSameDay = isSameDay;
2148
+ exports.isSameHour = isSameHour;
2149
+ exports.isSet = isSet;
2150
+ exports.isTrue = isTrue;
2151
+ exports.isType = isType;
2152
+ exports.isTypeEnhanced = isTypeEnhanced;
2153
+ exports.lowercase = lowercase;
2154
+ exports.nextRandom = nextRandom;
2155
+ exports.normalizeArray = normalizeArray;
2156
+ exports.normalizeShopifyId = normalizeShopifyId;
2157
+ exports.normalizeURIParts = normalizeURIParts;
2158
+ exports.nullable = nullable;
2159
+ exports.objectContainsAnyValue = objectContainsAnyValue;
2160
+ exports.objectEmpty = objectEmpty;
2161
+ exports.objectNotEmpty = objectNotEmpty;
2162
+ exports.objectToSortedString = objectToSortedString;
2163
+ exports.parseKeywordGroups = parseKeywordGroups;
2164
+ exports.parseKeywords = parseKeywords;
2165
+ exports.parseProject = parseProject;
2166
+ exports.parseProjectName = parseProjectName;
2167
+ exports.prepareRequestOptions = prepareRequestOptions;
2168
+ exports.prepareVerificationRequest = prepareVerificationRequest;
2169
+ exports.processVerificationResponse = processVerificationResponse;
2170
+ exports.querystringToObject = querystringToObject;
2171
+ exports.random = random;
2172
+ exports.removeLeadingSlash = removeLeadingSlash;
2173
+ exports.removeLeadingTrailingQuotes = removeLeadingTrailingQuotes;
2174
+ exports.removeLeadingTrailingSlash = removeLeadingTrailingSlash;
2175
+ exports.removeTrailingSlash = removeTrailingSlash;
2176
+ exports.replaceSpaces = replaceSpaces;
2177
+ exports.safeParse = safeParse;
2178
+ exports.safeTrim = safeTrim;
2179
+ exports.setDateTimeLocale = setDateTimeLocale;
2180
+ exports.setDateTimeZone = setDateTimeZone;
2181
+ exports.slugify = slugify;
2182
+ exports.snakecase = snakecase;
2183
+ exports.splitCommas = splitCommas;
2184
+ exports.stringEmpty = stringEmpty;
2185
+ exports.stringEmptyOnly = stringEmptyOnly;
2186
+ exports.stringNotEmpty = stringNotEmpty;
2187
+ exports.stringNotEmptyOnly = stringNotEmptyOnly;
2188
+ exports.titleCase = titleCase;
2189
+ exports.toFormatted = toFormatted;
2190
+ exports.toISOString = toISOString;
2191
+ exports.toJSDate = toJSDate;
2192
+ exports.toUTCString = toUTCString;
2193
+ exports.toUnixInteger = toUnixInteger;
2194
+ exports.toUnixString = toUnixString;
2195
+ exports.undashcase = undashcase;
2196
+ exports.unsnakecase = unsnakecase;
2197
+ exports.uppercase = uppercase;
2198
+ exports.wrappedEncode = wrappedEncode;
2199
+ exports.zencase = zencase;
2200
+
2201
+ Object.defineProperty(exports, '__esModule', { value: true });
2202
+
2203
+ }));
2204
+ //# sourceMappingURL=index.js.map