@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.
- package/CHANGELOG.md +8 -0
- package/CODE_OF_CONDUCT.md +132 -0
- package/LICENSE +21 -0
- package/README.md +93 -0
- package/SECURITY.md +18 -0
- package/dist/lib/index.d.ts +4 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +20 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/types.d.ts +1 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +1 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/utils/errors.d.ts +6 -0
- package/dist/lib/utils/errors.d.ts.map +1 -0
- package/dist/lib/utils/errors.js +26 -0
- package/dist/lib/utils/errors.js.map +1 -0
- package/dist/lib/utils/index.d.ts +3 -0
- package/dist/lib/utils/index.d.ts.map +1 -0
- package/dist/lib/utils/index.js +19 -0
- package/dist/lib/utils/index.js.map +1 -0
- package/dist/lib/utils/rawbody.d.ts +2 -0
- package/dist/lib/utils/rawbody.d.ts.map +1 -0
- package/dist/lib/utils/rawbody.js +60 -0
- package/dist/lib/utils/rawbody.js.map +1 -0
- package/dist/lib/verification.d.ts +10 -0
- package/dist/lib/verification.d.ts.map +1 -0
- package/dist/lib/verification.js +84 -0
- package/dist/lib/verification.js.map +1 -0
- package/dist/react/hooks/index.d.ts +2 -0
- package/dist/react/hooks/index.d.ts.map +1 -0
- package/dist/react/hooks/index.js +18 -0
- package/dist/react/hooks/index.js.map +1 -0
- package/dist/react/hooks/useVerification.d.ts +8 -0
- package/dist/react/hooks/useVerification.d.ts.map +1 -0
- package/dist/react/hooks/useVerification.js +52 -0
- package/dist/react/hooks/useVerification.js.map +1 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +19 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/types.d.ts +1 -0
- package/dist/react/types.d.ts.map +1 -0
- package/dist/react/types.js +1 -0
- package/dist/react/types.js.map +1 -0
- package/dist/shared/WebCrypto.d.ts +55 -0
- package/dist/shared/WebCrypto.d.ts.map +1 -0
- package/dist/shared/WebCrypto.js +342 -0
- package/dist/shared/WebCrypto.js.map +1 -0
- package/dist/shared/datetime.d.ts +96 -0
- package/dist/shared/datetime.d.ts.map +1 -0
- package/dist/shared/datetime.js +295 -0
- package/dist/shared/datetime.js.map +1 -0
- package/dist/shared/display.d.ts +100 -0
- package/dist/shared/display.d.ts.map +1 -0
- package/dist/shared/display.js +481 -0
- package/dist/shared/display.js.map +1 -0
- package/dist/shared/index.d.ts +6 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +28 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/request.d.ts +16 -0
- package/dist/shared/request.d.ts.map +1 -0
- package/dist/shared/request.js +60 -0
- package/dist/shared/request.js.map +1 -0
- package/dist/shared/types.d.ts +1 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +1 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/shared/verification.d.ts +31 -0
- package/dist/shared/verification.d.ts.map +1 -0
- package/dist/shared/verification.js +91 -0
- package/dist/shared/verification.js.map +1 -0
- package/dist/umd/lib/index.js +2204 -0
- package/dist/umd/lib/index.js.map +1 -0
- package/dist/umd/lib/lib/index.d.ts +4 -0
- package/dist/umd/lib/lib/index.d.ts.map +1 -0
- package/dist/umd/lib/lib/types.d.ts +1 -0
- package/dist/umd/lib/lib/types.d.ts.map +1 -0
- package/dist/umd/lib/lib/utils/errors.d.ts +6 -0
- package/dist/umd/lib/lib/utils/errors.d.ts.map +1 -0
- package/dist/umd/lib/lib/utils/index.d.ts +3 -0
- package/dist/umd/lib/lib/utils/index.d.ts.map +1 -0
- package/dist/umd/lib/lib/utils/rawbody.d.ts +2 -0
- package/dist/umd/lib/lib/utils/rawbody.d.ts.map +1 -0
- package/dist/umd/lib/lib/verification.d.ts +10 -0
- package/dist/umd/lib/lib/verification.d.ts.map +1 -0
- package/dist/umd/lib/react/hooks/index.d.ts +2 -0
- package/dist/umd/lib/react/hooks/index.d.ts.map +1 -0
- package/dist/umd/lib/react/hooks/useVerification.d.ts +8 -0
- package/dist/umd/lib/react/hooks/useVerification.d.ts.map +1 -0
- package/dist/umd/lib/react/index.d.ts +3 -0
- package/dist/umd/lib/react/index.d.ts.map +1 -0
- package/dist/umd/lib/react/types.d.ts +1 -0
- package/dist/umd/lib/react/types.d.ts.map +1 -0
- package/dist/umd/lib/shared/WebCrypto.d.ts +55 -0
- package/dist/umd/lib/shared/WebCrypto.d.ts.map +1 -0
- package/dist/umd/lib/shared/datetime.d.ts +96 -0
- package/dist/umd/lib/shared/datetime.d.ts.map +1 -0
- package/dist/umd/lib/shared/display.d.ts +100 -0
- package/dist/umd/lib/shared/display.d.ts.map +1 -0
- package/dist/umd/lib/shared/index.d.ts +6 -0
- package/dist/umd/lib/shared/index.d.ts.map +1 -0
- package/dist/umd/lib/shared/request.d.ts +16 -0
- package/dist/umd/lib/shared/request.d.ts.map +1 -0
- package/dist/umd/lib/shared/types.d.ts +1 -0
- package/dist/umd/lib/shared/types.d.ts.map +1 -0
- package/dist/umd/lib/shared/verification.d.ts +31 -0
- package/dist/umd/lib/shared/verification.d.ts.map +1 -0
- package/node_modules/browser-monads-ts/LICENSE +21 -0
- package/node_modules/browser-monads-ts/README.md +46 -0
- package/node_modules/browser-monads-ts/dist/browser-monads-ts.cjs.js +2 -0
- package/node_modules/browser-monads-ts/dist/browser-monads-ts.cjs.js.map +1 -0
- package/node_modules/browser-monads-ts/dist/browser-monads-ts.es.js +12 -0
- package/node_modules/browser-monads-ts/dist/browser-monads-ts.es.js.map +1 -0
- package/node_modules/browser-monads-ts/dist/index.d.ts +5 -0
- package/node_modules/browser-monads-ts/dist/index.d.ts.map +1 -0
- package/node_modules/browser-monads-ts/package.json +79 -0
- package/node_modules/bytes/History.md +97 -0
- package/node_modules/bytes/LICENSE +23 -0
- package/node_modules/bytes/Readme.md +152 -0
- package/node_modules/bytes/index.js +170 -0
- package/node_modules/bytes/package.json +42 -0
- package/node_modules/depd/History.md +103 -0
- package/node_modules/depd/LICENSE +22 -0
- package/node_modules/depd/Readme.md +280 -0
- package/node_modules/depd/index.js +538 -0
- package/node_modules/depd/lib/browser/index.js +77 -0
- package/node_modules/depd/package.json +45 -0
- package/node_modules/http-errors/HISTORY.md +180 -0
- package/node_modules/http-errors/LICENSE +23 -0
- package/node_modules/http-errors/README.md +169 -0
- package/node_modules/http-errors/index.js +289 -0
- package/node_modules/http-errors/package.json +50 -0
- package/node_modules/iconv-lite/Changelog.md +162 -0
- package/node_modules/iconv-lite/LICENSE +21 -0
- package/node_modules/iconv-lite/README.md +156 -0
- package/node_modules/iconv-lite/encodings/dbcs-codec.js +555 -0
- package/node_modules/iconv-lite/encodings/dbcs-data.js +176 -0
- package/node_modules/iconv-lite/encodings/index.js +22 -0
- package/node_modules/iconv-lite/encodings/internal.js +188 -0
- package/node_modules/iconv-lite/encodings/sbcs-codec.js +72 -0
- package/node_modules/iconv-lite/encodings/sbcs-data-generated.js +451 -0
- package/node_modules/iconv-lite/encodings/sbcs-data.js +174 -0
- package/node_modules/iconv-lite/encodings/tables/big5-added.json +122 -0
- package/node_modules/iconv-lite/encodings/tables/cp936.json +264 -0
- package/node_modules/iconv-lite/encodings/tables/cp949.json +273 -0
- package/node_modules/iconv-lite/encodings/tables/cp950.json +177 -0
- package/node_modules/iconv-lite/encodings/tables/eucjp.json +182 -0
- package/node_modules/iconv-lite/encodings/tables/gb18030-ranges.json +1 -0
- package/node_modules/iconv-lite/encodings/tables/gbk-added.json +55 -0
- package/node_modules/iconv-lite/encodings/tables/shiftjis.json +125 -0
- package/node_modules/iconv-lite/encodings/utf16.js +177 -0
- package/node_modules/iconv-lite/encodings/utf7.js +290 -0
- package/node_modules/iconv-lite/lib/bom-handling.js +52 -0
- package/node_modules/iconv-lite/lib/extend-node.js +217 -0
- package/node_modules/iconv-lite/lib/index.d.ts +24 -0
- package/node_modules/iconv-lite/lib/index.js +153 -0
- package/node_modules/iconv-lite/lib/streams.js +121 -0
- package/node_modules/iconv-lite/package.json +46 -0
- package/node_modules/inherits/LICENSE +16 -0
- package/node_modules/inherits/README.md +42 -0
- package/node_modules/inherits/inherits.js +9 -0
- package/node_modules/inherits/inherits_browser.js +27 -0
- package/node_modules/inherits/package.json +29 -0
- package/node_modules/nothing-mock/LICENSE +21 -0
- package/node_modules/nothing-mock/README.md +266 -0
- package/node_modules/nothing-mock/dist/nothing-mock.es.js +2 -0
- package/node_modules/nothing-mock/dist/nothing-mock.es.js.map +1 -0
- package/node_modules/nothing-mock/dist/nothing-mock.js +2 -0
- package/node_modules/nothing-mock/dist/nothing-mock.js.map +1 -0
- package/node_modules/nothing-mock/dist/nothing-mock.modern.js +2 -0
- package/node_modules/nothing-mock/dist/nothing-mock.modern.js.map +1 -0
- package/node_modules/nothing-mock/dist/nothing-mock.umd.js +2 -0
- package/node_modules/nothing-mock/dist/nothing-mock.umd.js.map +1 -0
- package/node_modules/nothing-mock/index.d.ts +6 -0
- package/node_modules/nothing-mock/package.json +29 -0
- package/node_modules/nothing-mock/src/index.js +15 -0
- package/node_modules/raw-body/HISTORY.md +308 -0
- package/node_modules/raw-body/LICENSE +22 -0
- package/node_modules/raw-body/README.md +223 -0
- package/node_modules/raw-body/SECURITY.md +24 -0
- package/node_modules/raw-body/index.d.ts +87 -0
- package/node_modules/raw-body/index.js +336 -0
- package/node_modules/raw-body/package.json +49 -0
- package/node_modules/safer-buffer/LICENSE +21 -0
- package/node_modules/safer-buffer/Porting-Buffer.md +268 -0
- package/node_modules/safer-buffer/Readme.md +156 -0
- package/node_modules/safer-buffer/dangerous.js +58 -0
- package/node_modules/safer-buffer/package.json +34 -0
- package/node_modules/safer-buffer/safer.js +77 -0
- package/node_modules/safer-buffer/tests.js +406 -0
- package/node_modules/setprototypeof/LICENSE +13 -0
- package/node_modules/setprototypeof/README.md +31 -0
- package/node_modules/setprototypeof/index.d.ts +2 -0
- package/node_modules/setprototypeof/index.js +17 -0
- package/node_modules/setprototypeof/package.json +38 -0
- package/node_modules/setprototypeof/test/index.js +24 -0
- package/node_modules/statuses/HISTORY.md +82 -0
- package/node_modules/statuses/LICENSE +23 -0
- package/node_modules/statuses/README.md +136 -0
- package/node_modules/statuses/codes.json +65 -0
- package/node_modules/statuses/index.js +146 -0
- package/node_modules/statuses/package.json +49 -0
- package/node_modules/toidentifier/HISTORY.md +9 -0
- package/node_modules/toidentifier/LICENSE +21 -0
- package/node_modules/toidentifier/README.md +61 -0
- package/node_modules/toidentifier/index.js +32 -0
- package/node_modules/toidentifier/package.json +38 -0
- package/node_modules/unpipe/HISTORY.md +4 -0
- package/node_modules/unpipe/LICENSE +22 -0
- package/node_modules/unpipe/README.md +43 -0
- package/node_modules/unpipe/index.js +69 -0
- package/node_modules/unpipe/package.json +27 -0
- 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
|