@rwillians/qx 0.1.20 → 0.2.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.
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ // // // // // // // // // // // // // // // // // // // // // // // //
3
+ // STANDARD SCHEMA SPEC //
4
+ // // // // // // // // // // // // // // // // // // // // // // // //
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.string = exports.strictObject = exports.number = exports.nullable = exports.integer = exports.instanceOf = exports.date = exports.boolean = exports.array = exports.parse = void 0;
7
+ /**
8
+ * @public Use any standard schema to parse a value.
9
+ * @since 0.1.0
10
+ * @version 1
11
+ */
12
+ const parse = (schema, value) => {
13
+ const parsed = schema['~standard'].validate(value);
14
+ if (parsed instanceof Promise) {
15
+ throw new Error('async standard schema validators are not supported');
16
+ }
17
+ return parsed;
18
+ };
19
+ exports.parse = parse;
20
+ // // // // // // // // // // // // // // // // // // // // // // // //
21
+ // UTILS //
22
+ // // // // // // // // // // // // // // // // // // // // // // // //
23
+ const prependPath = (path) => (issue) => ({
24
+ ...issue,
25
+ path: [path, ...(issue.path ?? [])],
26
+ });
27
+ /**
28
+ * @public Defines an array of a given standard schema.
29
+ * @since 0.1.0
30
+ * @version 1
31
+ */
32
+ const array = (schema) => ({
33
+ '~standard': {
34
+ version: 1,
35
+ vendor: 'qx',
36
+ validate: (input) => {
37
+ if (!Array.isArray(input)) {
38
+ return { issues: [{ message: 'must be an array' }] };
39
+ }
40
+ const issues = [];
41
+ const value = [];
42
+ for (let i = 0; i < input.length; i++) {
43
+ const parsed = (0, exports.parse)(schema, input[i]);
44
+ parsed.issues
45
+ ? issues.push(...parsed.issues.map(prependPath(i)))
46
+ : value.push(parsed.value);
47
+ }
48
+ return issues.length > 0
49
+ ? { issues }
50
+ : { value: value };
51
+ },
52
+ },
53
+ });
54
+ exports.array = array;
55
+ /**
56
+ * @public Defines a standard schema for boolean values.
57
+ * @since 0.1.0
58
+ * @version 1
59
+ */
60
+ const boolean = () => ({
61
+ '~standard': {
62
+ version: 1,
63
+ vendor: 'qx',
64
+ validate: (input) => typeof input !== 'boolean'
65
+ ? { issues: [{ message: 'must be a boolean' }] }
66
+ : { value: input },
67
+ },
68
+ });
69
+ exports.boolean = boolean;
70
+ /**
71
+ * @public Defines a standard schema for Date values that can be
72
+ * coerced from ISO 8601 strings or epoch timestamps in
73
+ * milliseconds.
74
+ * @since 0.1.0
75
+ * @version 1
76
+ */
77
+ const date = () => ({
78
+ '~standard': {
79
+ version: 1,
80
+ vendor: 'qx',
81
+ validate: (input) => {
82
+ if (input instanceof Date)
83
+ return isNaN(input.getTime())
84
+ ? { issues: [{ message: 'must be a valid date' }] }
85
+ : { value: input };
86
+ if (typeof input !== 'string' &&
87
+ typeof input !== 'number')
88
+ return { issues: [{ message: 'must be a valid ISO 8601 string or epoch timestamp in milliseconds' }] };
89
+ const date = new Date(input);
90
+ if (isNaN(date.getTime()))
91
+ return { issues: [{ message: 'must be a valid ISO 8601 string or epoch timestamp in milliseconds' }] };
92
+ return { value: date };
93
+ },
94
+ },
95
+ });
96
+ exports.date = date;
97
+ /**
98
+ * @public Defines a standard schema that validates instances of a
99
+ * given class.
100
+ * @since 0.1.0
101
+ * @version 1
102
+ */
103
+ const instanceOf = (ctor) => ({
104
+ '~standard': {
105
+ version: 1,
106
+ vendor: 'qx',
107
+ validate: (input) => !(input instanceof ctor)
108
+ ? { issues: [{ message: `must be an instance of ${ctor.name}` }] }
109
+ : { value: input },
110
+ },
111
+ });
112
+ exports.instanceOf = instanceOf;
113
+ /**
114
+ * @public Defines a standard schema for integers with optional min and max constraints.
115
+ * @since 0.1.0
116
+ * @version 1
117
+ */
118
+ const integer = ({ min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER } = {}) => ({
119
+ '~standard': {
120
+ version: 1,
121
+ vendor: 'qx',
122
+ validate: (input) => {
123
+ if (typeof input !== 'number')
124
+ return { issues: [{ message: 'must be a integer' }] };
125
+ if (Number.isNaN(input) || !Number.isFinite(input))
126
+ return { issues: [{ message: 'must be a integer' }] };
127
+ if (!Number.isInteger(input))
128
+ return { issues: [{ message: 'must be a integer' }] };
129
+ if (input < min)
130
+ return { issues: [{ message: `must be greater than or equal to ${min}` }] };
131
+ if (input > max)
132
+ return { issues: [{ message: `must be less than or equal to ${max}` }] };
133
+ return { value: input };
134
+ },
135
+ },
136
+ });
137
+ exports.integer = integer;
138
+ /**
139
+ * @public Makes any standard schema accepts `null` as a valid value.
140
+ * @since 0.1.0
141
+ * @version 1
142
+ */
143
+ const nullable = (schema) => ({
144
+ '~standard': {
145
+ version: 1,
146
+ vendor: 'qx',
147
+ validate: (value) => value === null
148
+ ? { value }
149
+ : (0, exports.parse)(schema, value),
150
+ },
151
+ });
152
+ exports.nullable = nullable;
153
+ /**
154
+ * @public Defines a standard schema for numbers with optional min
155
+ * and max constraints.
156
+ * @since 0.1.0
157
+ * @version 1
158
+ */
159
+ const number = ({ min = Number.MIN_VALUE, max = Number.MAX_VALUE } = {}) => ({
160
+ '~standard': {
161
+ version: 1,
162
+ vendor: 'qx',
163
+ validate: (input) => {
164
+ if (typeof input !== 'number')
165
+ return { issues: [{ message: 'must be a number' }] };
166
+ if (Number.isNaN(input) || !Number.isFinite(input))
167
+ return { issues: [{ message: 'must be a number' }] };
168
+ if (input < min)
169
+ return { issues: [{ message: `must be greater than or equal to ${min}` }] };
170
+ if (input > max)
171
+ return { issues: [{ message: `must be less than or equal to ${max}` }] };
172
+ return { value: input };
173
+ },
174
+ },
175
+ });
176
+ exports.number = number;
177
+ /**
178
+ * @public Defines an object schema that does not allow extra fields.
179
+ * @since 0.1.0
180
+ * @version 1
181
+ */
182
+ const strictObject = (shape) => ({
183
+ '~standard': {
184
+ version: 1,
185
+ vendor: 'qx',
186
+ validate: (input) => {
187
+ if (typeof input !== 'object' || input === null) {
188
+ return { issues: [{ message: 'must be an object' }] };
189
+ }
190
+ const issues = [];
191
+ const inputKeys = new Set(Object.keys(input));
192
+ const shapeKeys = new Set(Object.keys(shape));
193
+ // one issue for each key of `input` that doesn't exist in `shape`
194
+ for (const key of inputKeys.difference(shapeKeys)) {
195
+ issues.push({ path: [key], message: 'unknown field' });
196
+ }
197
+ // one issue for each key of `shape` that doesn't exist in `input`
198
+ for (const key of shapeKeys.difference(inputKeys)) {
199
+ issues.push({ path: [key], message: 'is required' });
200
+ }
201
+ const record = {};
202
+ for (const [key, value] of Object.entries(input)) {
203
+ const parsed = shape[key]['~standard'].validate(value);
204
+ if (parsed instanceof Promise) {
205
+ throw new Error('async validators are not supported');
206
+ }
207
+ parsed.issues
208
+ ? issues.push(...parsed.issues.map(prependPath(key)))
209
+ : record[key] = parsed.value;
210
+ }
211
+ return issues.length > 0
212
+ ? { issues }
213
+ : { value: record };
214
+ },
215
+ },
216
+ });
217
+ exports.strictObject = strictObject;
218
+ /**
219
+ * @public Defines a standard schema for strings with optional min
220
+ * and max length constraints.
221
+ * @since 0.1.0
222
+ * @version 1
223
+ */
224
+ const string = ({ min, max } = {}) => ({
225
+ '~standard': {
226
+ version: 1,
227
+ vendor: 'qx',
228
+ validate: (input) => {
229
+ if (typeof input !== 'string')
230
+ return { issues: [{ message: 'must be a string' }] };
231
+ if (min !== undefined && input.length < min)
232
+ return { issues: [{ message: `must be at least ${min} characters long` }] };
233
+ if (max !== undefined && input.length > max)
234
+ return { issues: [{ message: `must be at most ${max} characters long` }] };
235
+ return { value: input };
236
+ },
237
+ },
238
+ });
239
+ exports.string = string;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.wrap = exports.snakeCase = exports.resolve = exports.mapValues = exports.mapKeys = exports.lte = exports.keys = exports.isPlainObject = exports.entries = exports.camelCase = void 0;
4
+ /**
5
+ * @private Converts a snake_case string to camelCase.
6
+ * @since 0.1.0
7
+ * @version 2
8
+ */
9
+ const camelCase = (str) => str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
10
+ exports.camelCase = camelCase;
11
+ /**
12
+ * @private Same as {@link Object.prototype.entries} but with better
13
+ * types.
14
+ * @since 0.1.17
15
+ * @version 1
16
+ */
17
+ const entries = (obj) => Object.entries(obj);
18
+ exports.entries = entries;
19
+ /**
20
+ * @private Simplified check for plain objects.
21
+ * @since 0.1.0
22
+ * @version 1
23
+ */
24
+ const isPlainObject = (value) => {
25
+ if (typeof value !== 'object' || value === null)
26
+ return false;
27
+ let proto = Object.getPrototypeOf(value);
28
+ if (proto === null)
29
+ return true;
30
+ return proto === Object.prototype;
31
+ };
32
+ exports.isPlainObject = isPlainObject;
33
+ /**
34
+ * @private Same as {@link Object.prototype.keys} but with better
35
+ * types.
36
+ * @since 0.1.17
37
+ * @version 1
38
+ */
39
+ const keys = (obj) => Object.keys(obj);
40
+ exports.keys = keys;
41
+ /**
42
+ * @private Throws an error if the given number is greater than the
43
+ * specified maximum.
44
+ * @since 0.1.0
45
+ * @version 1
46
+ */
47
+ const lte = (n, max) => {
48
+ if (n > max)
49
+ throw new Error(`Must be at most ${max}, got ${n}`);
50
+ return n;
51
+ };
52
+ exports.lte = lte;
53
+ /**
54
+ * @public Maps over the keys of an object.
55
+ * @since 0.1.0
56
+ * @version 1
57
+ */
58
+ const mapKeys = (obj, fn) => Object.fromEntries((0, exports.entries)(obj).map(([key, value]) => [fn(key), value]));
59
+ exports.mapKeys = mapKeys;
60
+ /**
61
+ * @private Maps over the values of an object.
62
+ * @since 0.1.0
63
+ * @version 1
64
+ */
65
+ const mapValues = (obj, fn) => Object.fromEntries((0, exports.entries)(obj).map(([key, value]) => [key, fn(value, key)]));
66
+ exports.mapValues = mapValues;
67
+ /**
68
+ * @private Resolves the given value or function to a value.
69
+ * @since 0.1.0
70
+ * @version 1
71
+ */
72
+ const resolve = (value) => typeof value === 'function'
73
+ ? value()
74
+ : value;
75
+ exports.resolve = resolve;
76
+ /**
77
+ * @private Converts a camelCase string to snake_case.
78
+ * @since 0.1.0
79
+ * @version 1
80
+ */
81
+ const snakeCase = (str) => str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
82
+ exports.snakeCase = snakeCase;
83
+ /**
84
+ * @public Wraps the given value in an array, unless it's already an
85
+ * array.
86
+ * @since 0.1.0
87
+ * @version 1
88
+ */
89
+ const wrap = (value) => Array.isArray(value)
90
+ ? value
91
+ : [value];
92
+ exports.wrap = wrap;
@@ -0,0 +1,23 @@
1
+ import {} from './index';
2
+ import * as u from './utils';
3
+ /**
4
+ * @public Wraps a function call that executes DDL with logging
5
+ * capabilities.
6
+ *
7
+ * A `debug` log is emitted before the function is executed,
8
+ * and an `error` log if the function throws an error. After
9
+ * logging, the error is re-thrown.
10
+ * @since 0.1.17
11
+ * @version 1
12
+ */
13
+ export const withLoggedQuery = async (logger, data, fn) => {
14
+ const loggers = u.wrap(logger);
15
+ const { sql, params } = data;
16
+ loggers.forEach(logger => logger.debug(sql, params));
17
+ return Promise.resolve().then(() => fn(sql, params)).catch(error => {
18
+ loggers.forEach(logger => logger.error(sql, params, error));
19
+ return Promise.reject(error); // propagate the error without
20
+ // appending stack traces
21
+ });
22
+ };
23
+ export { is, } from './index';