jsonbadger 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +36 -18
  2. package/docs/api/connection.md +144 -0
  3. package/docs/api/delta-tracker.md +106 -0
  4. package/docs/api/document.md +77 -0
  5. package/docs/api/field-types.md +329 -0
  6. package/docs/api/index.md +35 -0
  7. package/docs/api/model.md +392 -0
  8. package/docs/api/query-builder.md +81 -0
  9. package/docs/api/schema.md +204 -0
  10. package/docs/architecture-flow.md +397 -0
  11. package/docs/examples.md +495 -218
  12. package/docs/jsonb-ops.md +171 -0
  13. package/docs/lifecycle/model-compilation.md +111 -0
  14. package/docs/lifecycle.md +146 -0
  15. package/docs/query-translation.md +11 -10
  16. package/package.json +10 -3
  17. package/src/connection/connect.js +12 -17
  18. package/src/connection/connection.js +128 -0
  19. package/src/connection/server-capabilities.js +60 -59
  20. package/src/constants/defaults.js +32 -19
  21. package/src/constants/{id-strategies.js → id-strategy.js} +28 -29
  22. package/src/constants/intake-mode.js +8 -0
  23. package/src/debug/debug-logger.js +17 -15
  24. package/src/errors/model-overwrite-error.js +25 -0
  25. package/src/errors/query-error.js +25 -23
  26. package/src/errors/validation-error.js +25 -23
  27. package/src/field-types/base-field-type.js +137 -140
  28. package/src/field-types/builtins/advanced.js +365 -365
  29. package/src/field-types/builtins/index.js +579 -585
  30. package/src/field-types/field-type-namespace.js +9 -0
  31. package/src/field-types/registry.js +149 -122
  32. package/src/index.js +26 -36
  33. package/src/migration/ensure-index.js +157 -154
  34. package/src/migration/ensure-schema.js +27 -15
  35. package/src/migration/ensure-table.js +44 -31
  36. package/src/migration/schema-indexes-resolver.js +8 -6
  37. package/src/model/document-instance.js +29 -540
  38. package/src/model/document.js +60 -0
  39. package/src/model/factory/constants.js +36 -0
  40. package/src/model/factory/index.js +58 -0
  41. package/src/model/model.js +875 -0
  42. package/src/model/operations/delete-one.js +39 -0
  43. package/src/model/operations/insert-one.js +35 -0
  44. package/src/model/operations/query-builder.js +132 -0
  45. package/src/model/operations/update-one.js +333 -0
  46. package/src/model/state.js +34 -0
  47. package/src/schema/field-definition-parser.js +213 -218
  48. package/src/schema/path-introspection.js +87 -82
  49. package/src/schema/schema-compiler.js +126 -212
  50. package/src/schema/schema.js +621 -138
  51. package/src/sql/index.js +17 -0
  52. package/src/sql/jsonb/ops.js +153 -0
  53. package/src/{query → sql/jsonb}/path-parser.js +54 -43
  54. package/src/sql/jsonb/read/elem-match.js +133 -0
  55. package/src/{query → sql/jsonb/read}/operators/contains.js +13 -7
  56. package/src/sql/jsonb/read/operators/elem-match.js +9 -0
  57. package/src/{query → sql/jsonb/read}/operators/has-all-keys.js +17 -11
  58. package/src/{query → sql/jsonb/read}/operators/has-any-keys.js +18 -11
  59. package/src/sql/jsonb/read/operators/has-key.js +12 -0
  60. package/src/{query → sql/jsonb/read}/operators/jsonpath-exists.js +22 -15
  61. package/src/{query → sql/jsonb/read}/operators/jsonpath-match.js +22 -15
  62. package/src/{query → sql/jsonb/read}/operators/size.js +23 -16
  63. package/src/sql/parameter-binder.js +18 -13
  64. package/src/sql/read/build-count-query.js +12 -0
  65. package/src/sql/read/build-find-query.js +25 -0
  66. package/src/sql/read/limit-skip.js +21 -0
  67. package/src/sql/read/sort.js +85 -0
  68. package/src/sql/read/where/base-fields.js +310 -0
  69. package/src/sql/read/where/casting.js +90 -0
  70. package/src/sql/read/where/context.js +79 -0
  71. package/src/sql/read/where/field-clause.js +58 -0
  72. package/src/sql/read/where/index.js +38 -0
  73. package/src/sql/read/where/operator-entries.js +29 -0
  74. package/src/{query → sql/read/where}/operators/all.js +16 -10
  75. package/src/sql/read/where/operators/eq.js +12 -0
  76. package/src/{query → sql/read/where}/operators/gt.js +23 -16
  77. package/src/{query → sql/read/where}/operators/gte.js +23 -16
  78. package/src/{query → sql/read/where}/operators/in.js +18 -12
  79. package/src/sql/read/where/operators/index.js +40 -0
  80. package/src/{query → sql/read/where}/operators/lt.js +23 -16
  81. package/src/{query → sql/read/where}/operators/lte.js +23 -16
  82. package/src/sql/read/where/operators/ne.js +12 -0
  83. package/src/{query → sql/read/where}/operators/nin.js +18 -12
  84. package/src/{query → sql/read/where}/operators/regex.js +14 -8
  85. package/src/sql/read/where/operators.js +126 -0
  86. package/src/sql/read/where/text-operators.js +83 -0
  87. package/src/sql/run.js +46 -0
  88. package/src/sql/write/build-delete-query.js +33 -0
  89. package/src/sql/write/build-insert-query.js +42 -0
  90. package/src/sql/write/build-update-query.js +65 -0
  91. package/src/utils/assert.js +34 -27
  92. package/src/utils/delta-tracker/.archive/1 tracker-redesign-codex-v2.md +250 -0
  93. package/src/utils/delta-tracker/.archive/1 tracker-redesign-gemini.md +101 -0
  94. package/src/utils/delta-tracker/.archive/2 evaluation by gemini.txt +65 -0
  95. package/src/utils/delta-tracker/.archive/2 evaluation by grok.txt +39 -0
  96. package/src/utils/delta-tracker/.archive/3 gemini evaluate grok.txt +37 -0
  97. package/src/utils/delta-tracker/.archive/3 grok evaluate gemini.txt +63 -0
  98. package/src/utils/delta-tracker/.archive/4 gemini veredict.txt +16 -0
  99. package/src/utils/delta-tracker/.archive/index.1.js +587 -0
  100. package/src/utils/delta-tracker/.archive/index.2.js +612 -0
  101. package/src/utils/delta-tracker/index.js +592 -0
  102. package/src/utils/dirty-tracker/inline.js +335 -0
  103. package/src/utils/dirty-tracker/instance.js +414 -0
  104. package/src/utils/dirty-tracker/static.js +343 -0
  105. package/src/utils/json-safe.js +13 -9
  106. package/src/utils/object-path.js +227 -33
  107. package/src/utils/object.js +408 -168
  108. package/src/utils/string.js +55 -0
  109. package/src/utils/value.js +169 -30
  110. package/docs/api.md +0 -152
  111. package/src/connection/disconnect.js +0 -16
  112. package/src/connection/pool-store.js +0 -46
  113. package/src/model/model-factory.js +0 -555
  114. package/src/query/limit-skip-compiler.js +0 -31
  115. package/src/query/operators/elem-match.js +0 -3
  116. package/src/query/operators/eq.js +0 -6
  117. package/src/query/operators/has-key.js +0 -6
  118. package/src/query/operators/index.js +0 -60
  119. package/src/query/operators/ne.js +0 -6
  120. package/src/query/query-builder.js +0 -93
  121. package/src/query/sort-compiler.js +0 -30
  122. package/src/query/where-compiler.js +0 -477
  123. package/src/sql/sql-runner.js +0 -31
@@ -1,585 +1,579 @@
1
- /*
2
- Assumptions and trade-offs:
3
- - Foundational FieldTypes focus on deterministic cast/validate behavior for save-path use.
4
- - Advanced type families and deep collection sub-schema behavior are deferred to later phases.
5
- */
6
- import BaseFieldType from '#src/field-types/base-field-type.js';
7
- import {
8
- decimal128_type_reference,
9
- double_type_reference,
10
- BigIntFieldType,
11
- Decimal128FieldType,
12
- DoubleFieldType,
13
- int32_type_reference,
14
- Int32FieldType,
15
- union_type_reference,
16
- UnionFieldType
17
- } from './advanced.js';
18
-
19
- const uuidv7_pattern = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
20
- const boolean_convert_to_true = new Set([true, 'true', 1, '1', 'yes']);
21
- const boolean_convert_to_false = new Set([false, 'false', 0, '0', 'no']);
22
-
23
- function uuidv7_type_reference() {
24
- return;
25
- }
26
-
27
- function StringFieldType(path_value, options) {
28
- BaseFieldType.call(this, path_value, options);
29
- this.instance = 'String';
30
-
31
- if(options && options.match instanceof RegExp) {
32
- this.regExp = options.match;
33
- this.validators.push({
34
- kind: 'match'
35
- });
36
- }
37
-
38
- if(options && Array.isArray(options.enum)) {
39
- this.enum_values = options.enum.slice();
40
- this.validators.push({
41
- kind: 'enum'
42
- });
43
- }
44
-
45
- if(options && options.minLength !== undefined) {
46
- this.validators.push({
47
- kind: 'minLength'
48
- });
49
- }
50
-
51
- if(options && options.maxLength !== undefined) {
52
- this.validators.push({
53
- kind: 'maxLength'
54
- });
55
- }
56
- }
57
-
58
- StringFieldType.prototype = Object.create(BaseFieldType.prototype);
59
- StringFieldType.prototype.constructor = StringFieldType;
60
-
61
- StringFieldType.prototype.cast = function (value) {
62
- if(value === undefined || value === null) {
63
- return value;
64
- }
65
-
66
- if(Array.isArray(value)) {
67
- throw this.create_field_error('cast_error', 'Cast to String failed for path "' + this.path + '"', value);
68
- }
69
-
70
- let casted_value = value;
71
-
72
- if(typeof casted_value !== 'string') {
73
- if(casted_value && typeof casted_value.toString === 'function' && casted_value.toString !== Object.prototype.toString) {
74
- casted_value = casted_value.toString();
75
- } else {
76
- throw this.create_field_error('cast_error', 'Cast to String failed for path "' + this.path + '"', value);
77
- }
78
- }
79
-
80
- if(this.options.trim === true) {
81
- casted_value = casted_value.trim();
82
- }
83
-
84
- if(this.options.lowercase === true) {
85
- casted_value = casted_value.toLowerCase();
86
- }
87
-
88
- if(this.options.uppercase === true) {
89
- casted_value = casted_value.toUpperCase();
90
- }
91
-
92
- return casted_value;
93
- };
94
-
95
- StringFieldType.prototype.run_type_validators = function (value) {
96
- if(this.regExp && !this.regExp.test(value)) {
97
- throw this.create_field_error('validator_error', 'Path "' + this.path + '" does not match pattern', value);
98
- }
99
-
100
- if(this.enum_values && this.enum_values.indexOf(value) === -1) {
101
- throw this.create_field_error('validator_error', 'Path "' + this.path + '" must be one of enum values', value);
102
- }
103
-
104
- if(this.options.minLength !== undefined && value.length < Number(this.options.minLength)) {
105
- throw this.create_field_error('validator_error', 'Path "' + this.path + '" is shorter than minLength', value);
106
- }
107
-
108
- if(this.options.maxLength !== undefined && value.length > Number(this.options.maxLength)) {
109
- throw this.create_field_error('validator_error', 'Path "' + this.path + '" is longer than maxLength', value);
110
- }
111
- };
112
-
113
- function NumberFieldType(path_value, options) {
114
- BaseFieldType.call(this, path_value, options);
115
- this.instance = 'Number';
116
-
117
- if(options && Array.isArray(options.enum)) {
118
- this.enum_values = options.enum.slice();
119
- this.validators.push({
120
- kind: 'enum'
121
- });
122
- }
123
-
124
- if(options && options.min !== undefined) {
125
- this.validators.push({
126
- kind: 'min'
127
- });
128
- }
129
-
130
- if(options && options.max !== undefined) {
131
- this.validators.push({
132
- kind: 'max'
133
- });
134
- }
135
- }
136
-
137
- NumberFieldType.prototype = Object.create(BaseFieldType.prototype);
138
- NumberFieldType.prototype.constructor = NumberFieldType;
139
-
140
- NumberFieldType.prototype.cast = function (value) {
141
- if(value === undefined || value === null) {
142
- return value;
143
- }
144
-
145
- if(Array.isArray(value)) {
146
- throw this.create_field_error('cast_error', 'Cast to Number failed for path "' + this.path + '"', value);
147
- }
148
-
149
- if(typeof value === 'number') {
150
- if(!Number.isFinite(value)) {
151
- throw this.create_field_error('cast_error', 'Cast to Number failed for path "' + this.path + '"', value);
152
- }
153
-
154
- return value;
155
- }
156
-
157
- if(value === true) {
158
- return 1;
159
- }
160
-
161
- if(value === false) {
162
- return 0;
163
- }
164
-
165
- if(typeof value === 'string') {
166
- if(value.trim() === '') {
167
- throw this.create_field_error('cast_error', 'Cast to Number failed for path "' + this.path + '"', value);
168
- }
169
-
170
- const string_number = Number(value);
171
-
172
- if(!Number.isFinite(string_number)) {
173
- throw this.create_field_error('cast_error', 'Cast to Number failed for path "' + this.path + '"', value);
174
- }
175
-
176
- return string_number;
177
- }
178
-
179
- if(value && typeof value.valueOf === 'function') {
180
- const value_of_result = value.valueOf();
181
-
182
- if(typeof value_of_result === 'number' && Number.isFinite(value_of_result)) {
183
- return value_of_result;
184
- }
185
- }
186
-
187
- throw this.create_field_error('cast_error', 'Cast to Number failed for path "' + this.path + '"', value);
188
- };
189
-
190
- NumberFieldType.prototype.run_type_validators = function (value) {
191
- if(this.enum_values && this.enum_values.indexOf(value) === -1) {
192
- throw this.create_field_error('validator_error', 'Path "' + this.path + '" must be one of enum values', value);
193
- }
194
-
195
- if(this.options.min !== undefined && value < Number(this.options.min)) {
196
- throw this.create_field_error('validator_error', 'Path "' + this.path + '" is lower than min', value);
197
- }
198
-
199
- if(this.options.max !== undefined && value > Number(this.options.max)) {
200
- throw this.create_field_error('validator_error', 'Path "' + this.path + '" is greater than max', value);
201
- }
202
- };
203
-
204
- function DateFieldType(path_value, options) {
205
- BaseFieldType.call(this, path_value, options);
206
- this.instance = 'Date';
207
-
208
- if(options && options.min !== undefined) {
209
- this.validators.push({
210
- kind: 'min'
211
- });
212
- }
213
-
214
- if(options && options.max !== undefined) {
215
- this.validators.push({
216
- kind: 'max'
217
- });
218
- }
219
- }
220
-
221
- DateFieldType.prototype = Object.create(BaseFieldType.prototype);
222
- DateFieldType.prototype.constructor = DateFieldType;
223
-
224
- DateFieldType.prototype.cast = function (value) {
225
- if(value === undefined || value === null) {
226
- return value;
227
- }
228
-
229
- if(value instanceof Date) {
230
- if(Number.isNaN(value.getTime())) {
231
- throw this.create_field_error('cast_error', 'Cast to Date failed for path "' + this.path + '"', value);
232
- }
233
-
234
- return value;
235
- }
236
-
237
- const casted_date = new Date(value);
238
-
239
- if(Number.isNaN(casted_date.getTime())) {
240
- throw this.create_field_error('cast_error', 'Cast to Date failed for path "' + this.path + '"', value);
241
- }
242
-
243
- return casted_date;
244
- };
245
-
246
- DateFieldType.prototype.run_type_validators = function (value) {
247
- if(!(value instanceof Date)) {
248
- throw this.create_field_error('validator_error', 'Path "' + this.path + '" must be a Date', value);
249
- }
250
-
251
- if(this.options.min !== undefined) {
252
- const min_date = this.cast(this.options.min);
253
-
254
- if(value.getTime() < min_date.getTime()) {
255
- throw this.create_field_error('validator_error', 'Path "' + this.path + '" is lower than min date', value);
256
- }
257
- }
258
-
259
- if(this.options.max !== undefined) {
260
- const max_date = this.cast(this.options.max);
261
-
262
- if(value.getTime() > max_date.getTime()) {
263
- throw this.create_field_error('validator_error', 'Path "' + this.path + '" is greater than max date', value);
264
- }
265
- }
266
- };
267
-
268
- function BooleanFieldType(path_value, options) {
269
- BaseFieldType.call(this, path_value, options);
270
- this.instance = 'Boolean';
271
- }
272
-
273
- BooleanFieldType.prototype = Object.create(BaseFieldType.prototype);
274
- BooleanFieldType.prototype.constructor = BooleanFieldType;
275
-
276
- BooleanFieldType.prototype.cast = function (value) {
277
- if(value === undefined || value === null) {
278
- return value;
279
- }
280
-
281
- if(boolean_convert_to_true.has(value)) {
282
- return true;
283
- }
284
-
285
- if(boolean_convert_to_false.has(value)) {
286
- return false;
287
- }
288
-
289
- throw this.create_field_error('cast_error', 'Cast to Boolean failed for path "' + this.path + '"', value);
290
- };
291
-
292
- function UUIDv7FieldType(path_value, options) {
293
- BaseFieldType.call(this, path_value, options);
294
- this.instance = 'UUIDv7';
295
- this.validators.push({
296
- kind: 'uuidv7'
297
- });
298
- }
299
-
300
- UUIDv7FieldType.prototype = Object.create(BaseFieldType.prototype);
301
- UUIDv7FieldType.prototype.constructor = UUIDv7FieldType;
302
-
303
- UUIDv7FieldType.prototype.cast = function (value) {
304
- if(value === undefined || value === null) {
305
- return value;
306
- }
307
-
308
- if(typeof value !== 'string') {
309
- throw this.create_field_error('cast_error', 'Cast to UUIDv7 failed for path "' + this.path + '"', value);
310
- }
311
-
312
- const normalized_value = value.toLowerCase();
313
-
314
- if(!uuidv7_pattern.test(normalized_value)) {
315
- throw this.create_field_error('cast_error', 'Cast to UUIDv7 failed for path "' + this.path + '"', value);
316
- }
317
-
318
- return normalized_value;
319
- };
320
-
321
- function BufferFieldType(path_value, options) {
322
- BaseFieldType.call(this, path_value, options);
323
- this.instance = 'Buffer';
324
- }
325
-
326
- BufferFieldType.prototype = Object.create(BaseFieldType.prototype);
327
- BufferFieldType.prototype.constructor = BufferFieldType;
328
-
329
- BufferFieldType.prototype.cast = function (value) {
330
- if(value === undefined || value === null) {
331
- return value;
332
- }
333
-
334
- if(Buffer.isBuffer(value)) {
335
- return value;
336
- }
337
-
338
- if(typeof value === 'string') {
339
- return Buffer.from(value);
340
- }
341
-
342
- if(typeof value === 'number' && Number.isFinite(value)) {
343
- return Buffer.from([value & 255]);
344
- }
345
-
346
- if(Array.isArray(value)) {
347
- try {
348
- return Buffer.from(value);
349
- } catch(error) {
350
- throw this.create_field_error('cast_error', 'Cast to Buffer failed for path "' + this.path + '"', value);
351
- }
352
- }
353
-
354
- if(value && value.type === 'Buffer' && Array.isArray(value.data)) {
355
- try {
356
- return Buffer.from(value.data);
357
- } catch(error) {
358
- throw this.create_field_error('cast_error', 'Cast to Buffer failed for path "' + this.path + '"', value);
359
- }
360
- }
361
-
362
- throw this.create_field_error('cast_error', 'Cast to Buffer failed for path "' + this.path + '"', value);
363
- };
364
-
365
- function MixedFieldType(path_value, options) {
366
- BaseFieldType.call(this, path_value, options);
367
- this.instance = 'Mixed';
368
- }
369
-
370
- MixedFieldType.prototype = Object.create(BaseFieldType.prototype);
371
- MixedFieldType.prototype.constructor = MixedFieldType;
372
-
373
- MixedFieldType.prototype.cast = function (value) {
374
- return value;
375
- };
376
-
377
- function ArrayFieldType(path_value, options) {
378
- BaseFieldType.call(this, path_value, options);
379
- this.instance = 'Array';
380
- this.of_field_type = options ? options.of_field_type || null : null;
381
- }
382
-
383
- ArrayFieldType.prototype = Object.create(BaseFieldType.prototype);
384
- ArrayFieldType.prototype.constructor = ArrayFieldType;
385
-
386
- ArrayFieldType.prototype.resolve_default = function (context_value) {
387
- if(this.options.default !== undefined) {
388
- return BaseFieldType.prototype.resolve_default.call(this, context_value);
389
- }
390
-
391
- return [];
392
- };
393
-
394
- ArrayFieldType.prototype.cast = function (value, context_value) {
395
- if(value === undefined || value === null) {
396
- return value;
397
- }
398
-
399
- if(!Array.isArray(value)) {
400
- throw this.create_field_error('cast_error', 'Cast to Array failed for path "' + this.path + '"', value);
401
- }
402
-
403
- if(!this.of_field_type) {
404
- return value;
405
- }
406
-
407
- const casted_array = [];
408
- let value_index = 0;
409
-
410
- while(value_index < value.length) {
411
- const array_value = value[value_index];
412
- const casted_item = this.of_field_type.normalize(array_value, {
413
- path: this.path + '.' + value_index,
414
- parent_path: this.path,
415
- parent_instance: this.instance,
416
- parent_context: context_value || {}
417
- });
418
-
419
- casted_array.push(casted_item);
420
- value_index += 1;
421
- }
422
-
423
- return casted_array;
424
- };
425
-
426
- function MapFieldType(path_value, options) {
427
- BaseFieldType.call(this, path_value, options);
428
- this.instance = 'Map';
429
- this.of_field_type = options ? options.of_field_type || null : null;
430
- }
431
-
432
- MapFieldType.prototype = Object.create(BaseFieldType.prototype);
433
- MapFieldType.prototype.constructor = MapFieldType;
434
-
435
- MapFieldType.prototype.cast = function (value, context_value) {
436
- if(value === undefined || value === null) {
437
- return value;
438
- }
439
-
440
- const map_output = {};
441
- let source_entries = null;
442
-
443
- if(value instanceof Map) {
444
- source_entries = Array.from(value.entries());
445
- } else if(value && typeof value === 'object' && !Array.isArray(value) && !Buffer.isBuffer(value) && !(value instanceof Date)) {
446
- source_entries = Object.entries(value);
447
- } else {
448
- throw this.create_field_error('cast_error', 'Cast to Map failed for path "' + this.path + '"', value);
449
- }
450
-
451
- let entry_index = 0;
452
-
453
- while(entry_index < source_entries.length) {
454
- const source_entry = source_entries[entry_index];
455
- const map_key = source_entry[0];
456
- const map_value = source_entry[1];
457
-
458
- if(typeof map_key !== 'string') {
459
- throw this.create_field_error('cast_error', 'Map key must be a string for path "' + this.path + '"', map_key);
460
- }
461
-
462
- if(this.of_field_type) {
463
- map_output[map_key] = this.of_field_type.normalize(map_value, {
464
- path: this.path + '.' + map_key,
465
- parent_path: this.path,
466
- parent_instance: this.instance,
467
- parent_context: context_value || {}
468
- });
469
- } else {
470
- map_output[map_key] = map_value;
471
- }
472
-
473
- entry_index += 1;
474
- }
475
-
476
- return map_output;
477
- };
478
-
479
- function get_foundational_field_types() {
480
- return {
481
- String: {
482
- constructor: StringFieldType,
483
- references: [String, 'String']
484
- },
485
-
486
- Number: {
487
- constructor: NumberFieldType,
488
- references: [Number, 'Number']
489
- },
490
-
491
- Date: {
492
- constructor: DateFieldType,
493
- references: [Date, 'Date']
494
- },
495
-
496
- Boolean: {
497
- constructor: BooleanFieldType,
498
- references: [Boolean, 'Boolean']
499
- },
500
-
501
- UUIDv7: {
502
- constructor: UUIDv7FieldType,
503
- references: [uuidv7_type_reference, 'UUIDv7']
504
- },
505
-
506
- Buffer: {
507
- constructor: BufferFieldType,
508
- references: [Buffer, 'Buffer']
509
- },
510
-
511
- Mixed: {
512
- constructor: MixedFieldType,
513
- references: ['Mixed', Object]
514
- },
515
-
516
- Array: {
517
- constructor: ArrayFieldType,
518
- references: [Array, 'Array']
519
- },
520
-
521
- Map: {
522
- constructor: MapFieldType,
523
- references: [Map, 'Map']
524
- },
525
-
526
- // Advanced FieldTypes
527
- Decimal128: {
528
- constructor: Decimal128FieldType,
529
- references: [decimal128_type_reference, 'Decimal128']
530
- },
531
-
532
- BigInt: {
533
- constructor: BigIntFieldType,
534
- references: [BigInt, 'BigInt']
535
- },
536
-
537
- Double: {
538
- constructor: DoubleFieldType,
539
- references: [double_type_reference, 'Double']
540
- },
541
-
542
- Int32: {
543
- constructor: Int32FieldType,
544
- references: [int32_type_reference, 'Int32']
545
- },
546
-
547
- Union: {
548
- constructor: UnionFieldType,
549
- references: [union_type_reference, 'Union']
550
- }
551
- };
552
- }
553
-
554
- export {
555
- // Type references
556
- decimal128_type_reference,
557
- double_type_reference,
558
- int32_type_reference,
559
- union_type_reference,
560
- uuidv7_type_reference,
561
-
562
- // FieldType constructors
563
- ArrayFieldType,
564
- BigIntFieldType,
565
- BooleanFieldType,
566
- BufferFieldType,
567
- DateFieldType,
568
- Decimal128FieldType,
569
- DoubleFieldType,
570
- Int32FieldType,
571
- MapFieldType,
572
- MixedFieldType,
573
- NumberFieldType,
574
- StringFieldType,
575
- UnionFieldType,
576
- UUIDv7FieldType,
577
-
578
- // Registry entries
579
- get_foundational_field_types,
580
-
581
- // Constants
582
- boolean_convert_to_false,
583
- boolean_convert_to_true,
584
- uuidv7_pattern
585
- };
1
+ import BaseFieldType from '#src/field-types/base-field-type.js';
2
+ import {
3
+ decimal128_type_reference,
4
+ double_type_reference,
5
+ BigIntFieldType,
6
+ Decimal128FieldType,
7
+ DoubleFieldType,
8
+ int32_type_reference,
9
+ Int32FieldType,
10
+ union_type_reference,
11
+ UnionFieldType
12
+ } from './advanced.js';
13
+ import {is_uuid_v7} from '#src/utils/value.js';
14
+
15
+ const boolean_convert_to_true = new Set([true, 'true', 1, '1', 'yes']);
16
+ const boolean_convert_to_false = new Set([false, 'false', 0, '0', 'no']);
17
+
18
+ function uuidv7_type_reference() {
19
+ return;
20
+ }
21
+
22
+ function StringFieldType(path_value, options) {
23
+ BaseFieldType.call(this, path_value, options);
24
+ this.instance = 'String';
25
+
26
+ if(options && options.match instanceof RegExp) {
27
+ this.regExp = options.match;
28
+ this.validators.push({
29
+ kind: 'match'
30
+ });
31
+ }
32
+
33
+ if(options && Array.isArray(options.enum)) {
34
+ this.enum_values = options.enum.slice();
35
+ this.validators.push({
36
+ kind: 'enum'
37
+ });
38
+ }
39
+
40
+ if(options && options.minLength !== undefined) {
41
+ this.validators.push({
42
+ kind: 'minLength'
43
+ });
44
+ }
45
+
46
+ if(options && options.maxLength !== undefined) {
47
+ this.validators.push({
48
+ kind: 'maxLength'
49
+ });
50
+ }
51
+ }
52
+
53
+ StringFieldType.prototype = Object.create(BaseFieldType.prototype);
54
+ StringFieldType.prototype.constructor = StringFieldType;
55
+
56
+ StringFieldType.prototype.cast = function (value) {
57
+ if(value === undefined || value === null) {
58
+ return value;
59
+ }
60
+
61
+ if(Array.isArray(value)) {
62
+ throw this.create_field_error('cast_error', 'Cast to String failed for path "' + this.path + '"', value);
63
+ }
64
+
65
+ let casted_value = value;
66
+
67
+ if(typeof casted_value !== 'string') {
68
+ if(casted_value && typeof casted_value.toString === 'function' && casted_value.toString !== Object.prototype.toString) {
69
+ casted_value = casted_value.toString();
70
+ } else {
71
+ throw this.create_field_error('cast_error', 'Cast to String failed for path "' + this.path + '"', value);
72
+ }
73
+ }
74
+
75
+ if(this.options.trim === true) {
76
+ casted_value = casted_value.trim();
77
+ }
78
+
79
+ if(this.options.lowercase === true) {
80
+ casted_value = casted_value.toLowerCase();
81
+ }
82
+
83
+ if(this.options.uppercase === true) {
84
+ casted_value = casted_value.toUpperCase();
85
+ }
86
+
87
+ return casted_value;
88
+ };
89
+
90
+ StringFieldType.prototype.run_type_validators = function (value) {
91
+ if(this.regExp && !this.regExp.test(value)) {
92
+ throw this.create_field_error('validator_error', 'Path "' + this.path + '" does not match pattern', value);
93
+ }
94
+
95
+ if(this.enum_values && this.enum_values.indexOf(value) === -1) {
96
+ throw this.create_field_error('validator_error', 'Path "' + this.path + '" must be one of enum values', value);
97
+ }
98
+
99
+ if(this.options.minLength !== undefined && value.length < Number(this.options.minLength)) {
100
+ throw this.create_field_error('validator_error', 'Path "' + this.path + '" is shorter than minLength', value);
101
+ }
102
+
103
+ if(this.options.maxLength !== undefined && value.length > Number(this.options.maxLength)) {
104
+ throw this.create_field_error('validator_error', 'Path "' + this.path + '" is longer than maxLength', value);
105
+ }
106
+ };
107
+
108
+ function NumberFieldType(path_value, options) {
109
+ BaseFieldType.call(this, path_value, options);
110
+ this.instance = 'Number';
111
+
112
+ if(options && Array.isArray(options.enum)) {
113
+ this.enum_values = options.enum.slice();
114
+ this.validators.push({
115
+ kind: 'enum'
116
+ });
117
+ }
118
+
119
+ if(options && options.min !== undefined) {
120
+ this.validators.push({
121
+ kind: 'min'
122
+ });
123
+ }
124
+
125
+ if(options && options.max !== undefined) {
126
+ this.validators.push({
127
+ kind: 'max'
128
+ });
129
+ }
130
+ }
131
+
132
+ NumberFieldType.prototype = Object.create(BaseFieldType.prototype);
133
+ NumberFieldType.prototype.constructor = NumberFieldType;
134
+
135
+ NumberFieldType.prototype.cast = function (value) {
136
+ if(value === undefined || value === null) {
137
+ return value;
138
+ }
139
+
140
+ if(Array.isArray(value)) {
141
+ throw this.create_field_error('cast_error', 'Cast to Number failed for path "' + this.path + '"', value);
142
+ }
143
+
144
+ if(typeof value === 'number') {
145
+ if(!Number.isFinite(value)) {
146
+ throw this.create_field_error('cast_error', 'Cast to Number failed for path "' + this.path + '"', value);
147
+ }
148
+
149
+ return value;
150
+ }
151
+
152
+ if(value === true) {
153
+ return 1;
154
+ }
155
+
156
+ if(value === false) {
157
+ return 0;
158
+ }
159
+
160
+ if(typeof value === 'string') {
161
+ if(value.trim() === '') {
162
+ throw this.create_field_error('cast_error', 'Cast to Number failed for path "' + this.path + '"', value);
163
+ }
164
+
165
+ const string_number = Number(value);
166
+
167
+ if(!Number.isFinite(string_number)) {
168
+ throw this.create_field_error('cast_error', 'Cast to Number failed for path "' + this.path + '"', value);
169
+ }
170
+
171
+ return string_number;
172
+ }
173
+
174
+ if(value && typeof value.valueOf === 'function') {
175
+ const value_of_result = value.valueOf();
176
+
177
+ if(typeof value_of_result === 'number' && Number.isFinite(value_of_result)) {
178
+ return value_of_result;
179
+ }
180
+ }
181
+
182
+ throw this.create_field_error('cast_error', 'Cast to Number failed for path "' + this.path + '"', value);
183
+ };
184
+
185
+ NumberFieldType.prototype.run_type_validators = function (value) {
186
+ if(this.enum_values && this.enum_values.indexOf(value) === -1) {
187
+ throw this.create_field_error('validator_error', 'Path "' + this.path + '" must be one of enum values', value);
188
+ }
189
+
190
+ if(this.options.min !== undefined && value < Number(this.options.min)) {
191
+ throw this.create_field_error('validator_error', 'Path "' + this.path + '" is lower than min', value);
192
+ }
193
+
194
+ if(this.options.max !== undefined && value > Number(this.options.max)) {
195
+ throw this.create_field_error('validator_error', 'Path "' + this.path + '" is greater than max', value);
196
+ }
197
+ };
198
+
199
+ function DateFieldType(path_value, options) {
200
+ BaseFieldType.call(this, path_value, options);
201
+ this.instance = 'Date';
202
+
203
+ if(options && options.min !== undefined) {
204
+ this.validators.push({
205
+ kind: 'min'
206
+ });
207
+ }
208
+
209
+ if(options && options.max !== undefined) {
210
+ this.validators.push({
211
+ kind: 'max'
212
+ });
213
+ }
214
+ }
215
+
216
+ DateFieldType.prototype = Object.create(BaseFieldType.prototype);
217
+ DateFieldType.prototype.constructor = DateFieldType;
218
+
219
+ DateFieldType.prototype.cast = function (value) {
220
+ if(value === undefined || value === null) {
221
+ return value;
222
+ }
223
+
224
+ if(value instanceof Date) {
225
+ if(Number.isNaN(value.getTime())) {
226
+ throw this.create_field_error('cast_error', 'Cast to Date failed for path "' + this.path + '"', value);
227
+ }
228
+
229
+ return value;
230
+ }
231
+
232
+ const casted_date = new Date(value);
233
+
234
+ if(Number.isNaN(casted_date.getTime())) {
235
+ throw this.create_field_error('cast_error', 'Cast to Date failed for path "' + this.path + '"', value);
236
+ }
237
+
238
+ return casted_date;
239
+ };
240
+
241
+ DateFieldType.prototype.run_type_validators = function (value) {
242
+ if(!(value instanceof Date)) {
243
+ throw this.create_field_error('validator_error', 'Path "' + this.path + '" must be a Date', value);
244
+ }
245
+
246
+ if(this.options.min !== undefined) {
247
+ const min_date = this.cast(this.options.min);
248
+
249
+ if(value.getTime() < min_date.getTime()) {
250
+ throw this.create_field_error('validator_error', 'Path "' + this.path + '" is lower than min date', value);
251
+ }
252
+ }
253
+
254
+ if(this.options.max !== undefined) {
255
+ const max_date = this.cast(this.options.max);
256
+
257
+ if(value.getTime() > max_date.getTime()) {
258
+ throw this.create_field_error('validator_error', 'Path "' + this.path + '" is greater than max date', value);
259
+ }
260
+ }
261
+ };
262
+
263
+ function BooleanFieldType(path_value, options) {
264
+ BaseFieldType.call(this, path_value, options);
265
+ this.instance = 'Boolean';
266
+ }
267
+
268
+ BooleanFieldType.prototype = Object.create(BaseFieldType.prototype);
269
+ BooleanFieldType.prototype.constructor = BooleanFieldType;
270
+
271
+ BooleanFieldType.prototype.cast = function (value) {
272
+ if(value === undefined || value === null) {
273
+ return value;
274
+ }
275
+
276
+ if(boolean_convert_to_true.has(value)) {
277
+ return true;
278
+ }
279
+
280
+ if(boolean_convert_to_false.has(value)) {
281
+ return false;
282
+ }
283
+
284
+ throw this.create_field_error('cast_error', 'Cast to Boolean failed for path "' + this.path + '"', value);
285
+ };
286
+
287
+ function UUIDv7FieldType(path_value, options) {
288
+ BaseFieldType.call(this, path_value, options);
289
+ this.instance = 'UUIDv7';
290
+ this.validators.push({
291
+ kind: 'uuidv7'
292
+ });
293
+ }
294
+
295
+ UUIDv7FieldType.prototype = Object.create(BaseFieldType.prototype);
296
+ UUIDv7FieldType.prototype.constructor = UUIDv7FieldType;
297
+
298
+ UUIDv7FieldType.prototype.cast = function (value) {
299
+ if(value === undefined || value === null) {
300
+ return value;
301
+ }
302
+
303
+ if(typeof value !== 'string') {
304
+ throw this.create_field_error('cast_error', 'Cast to UUIDv7 failed for path "' + this.path + '"', value);
305
+ }
306
+
307
+ const normalized_value = value.toLowerCase();
308
+
309
+ if(!is_uuid_v7(normalized_value)) {
310
+ throw this.create_field_error('cast_error', 'Cast to UUIDv7 failed for path "' + this.path + '"', value);
311
+ }
312
+
313
+ return normalized_value;
314
+ };
315
+
316
+ function BufferFieldType(path_value, options) {
317
+ BaseFieldType.call(this, path_value, options);
318
+ this.instance = 'Buffer';
319
+ }
320
+
321
+ BufferFieldType.prototype = Object.create(BaseFieldType.prototype);
322
+ BufferFieldType.prototype.constructor = BufferFieldType;
323
+
324
+ BufferFieldType.prototype.cast = function (value) {
325
+ if(value === undefined || value === null) {
326
+ return value;
327
+ }
328
+
329
+ if(Buffer.isBuffer(value)) {
330
+ return value;
331
+ }
332
+
333
+ if(typeof value === 'string') {
334
+ return Buffer.from(value);
335
+ }
336
+
337
+ if(typeof value === 'number' && Number.isFinite(value)) {
338
+ return Buffer.from([value & 255]);
339
+ }
340
+
341
+ if(Array.isArray(value)) {
342
+ try {
343
+ return Buffer.from(value);
344
+ } catch(error) {
345
+ throw this.create_field_error('cast_error', 'Cast to Buffer failed for path "' + this.path + '"', value);
346
+ }
347
+ }
348
+
349
+ if(value && value.type === 'Buffer' && Array.isArray(value.data)) {
350
+ try {
351
+ return Buffer.from(value.data);
352
+ } catch(error) {
353
+ throw this.create_field_error('cast_error', 'Cast to Buffer failed for path "' + this.path + '"', value);
354
+ }
355
+ }
356
+
357
+ throw this.create_field_error('cast_error', 'Cast to Buffer failed for path "' + this.path + '"', value);
358
+ };
359
+
360
+ function MixedFieldType(path_value, options) {
361
+ BaseFieldType.call(this, path_value, options);
362
+ this.instance = 'Mixed';
363
+ }
364
+
365
+ MixedFieldType.prototype = Object.create(BaseFieldType.prototype);
366
+ MixedFieldType.prototype.constructor = MixedFieldType;
367
+
368
+ MixedFieldType.prototype.cast = function (value) {
369
+ return value;
370
+ };
371
+
372
+ function ArrayFieldType(path_value, options) {
373
+ BaseFieldType.call(this, path_value, options);
374
+ this.instance = 'Array';
375
+ this.of_field_type = options ? options.of_field_type || null : null;
376
+ }
377
+
378
+ ArrayFieldType.prototype = Object.create(BaseFieldType.prototype);
379
+ ArrayFieldType.prototype.constructor = ArrayFieldType;
380
+
381
+ ArrayFieldType.prototype.resolve_default = function (context_value) {
382
+ if(this.options.default !== undefined) {
383
+ return BaseFieldType.prototype.resolve_default.call(this, context_value);
384
+ }
385
+
386
+ return null;
387
+ };
388
+
389
+ ArrayFieldType.prototype.cast = function (value, context_value) {
390
+ if(value === undefined || value === null) {
391
+ return value;
392
+ }
393
+
394
+ if(!Array.isArray(value)) {
395
+ throw this.create_field_error('cast_error', 'Cast to Array failed for path "' + this.path + '"', value);
396
+ }
397
+
398
+ if(!this.of_field_type) {
399
+ return value;
400
+ }
401
+
402
+ const casted_array = [];
403
+ let value_index = 0;
404
+
405
+ while(value_index < value.length) {
406
+ const array_value = value[value_index];
407
+ const casted_item = this.of_field_type.normalize(array_value, {
408
+ path: this.path + '.' + value_index,
409
+ parent_path: this.path,
410
+ parent_instance: this.instance,
411
+ parent_context: context_value || {}
412
+ });
413
+
414
+ casted_array.push(casted_item);
415
+ value_index += 1;
416
+ }
417
+
418
+ return casted_array;
419
+ };
420
+
421
+ function MapFieldType(path_value, options) {
422
+ BaseFieldType.call(this, path_value, options);
423
+ this.instance = 'Map';
424
+ this.of_field_type = options ? options.of_field_type || null : null;
425
+ }
426
+
427
+ MapFieldType.prototype = Object.create(BaseFieldType.prototype);
428
+ MapFieldType.prototype.constructor = MapFieldType;
429
+
430
+ MapFieldType.prototype.cast = function (value, context_value) {
431
+ if(value === undefined || value === null) {
432
+ return value;
433
+ }
434
+
435
+ const map_output = {};
436
+ let source_entries = null;
437
+
438
+ if(value instanceof Map) {
439
+ source_entries = Array.from(value.entries());
440
+ } else if(value && typeof value === 'object' && !Array.isArray(value) && !Buffer.isBuffer(value) && !(value instanceof Date)) {
441
+ source_entries = Object.entries(value);
442
+ } else {
443
+ throw this.create_field_error('cast_error', 'Cast to Map failed for path "' + this.path + '"', value);
444
+ }
445
+
446
+ let entry_index = 0;
447
+
448
+ while(entry_index < source_entries.length) {
449
+ const source_entry = source_entries[entry_index];
450
+ const map_key = source_entry[0];
451
+ const map_value = source_entry[1];
452
+
453
+ if(typeof map_key !== 'string') {
454
+ throw this.create_field_error('cast_error', 'Map key must be a string for path "' + this.path + '"', map_key);
455
+ }
456
+
457
+ if(this.of_field_type) {
458
+ map_output[map_key] = this.of_field_type.normalize(map_value, {
459
+ path: this.path + '.' + map_key,
460
+ parent_path: this.path,
461
+ parent_instance: this.instance,
462
+ parent_context: context_value || {}
463
+ });
464
+ } else {
465
+ map_output[map_key] = map_value;
466
+ }
467
+
468
+ entry_index += 1;
469
+ }
470
+
471
+ return map_output;
472
+ };
473
+
474
+ function get_foundational_field_types() {
475
+ return {
476
+ String: {
477
+ constructor: StringFieldType,
478
+ aliases: [String, 'String']
479
+ },
480
+
481
+ Number: {
482
+ constructor: NumberFieldType,
483
+ aliases: [Number, 'Number']
484
+ },
485
+
486
+ Date: {
487
+ constructor: DateFieldType,
488
+ aliases: [Date, 'Date']
489
+ },
490
+
491
+ Boolean: {
492
+ constructor: BooleanFieldType,
493
+ aliases: [Boolean, 'Boolean']
494
+ },
495
+
496
+ UUIDv7: {
497
+ constructor: UUIDv7FieldType,
498
+ aliases: [uuidv7_type_reference, 'UUIDv7']
499
+ },
500
+
501
+ Buffer: {
502
+ constructor: BufferFieldType,
503
+ aliases: [Buffer, 'Buffer']
504
+ },
505
+
506
+ Mixed: {
507
+ constructor: MixedFieldType,
508
+ aliases: ['Mixed', Object]
509
+ },
510
+
511
+ Array: {
512
+ constructor: ArrayFieldType,
513
+ aliases: [Array, 'Array']
514
+ },
515
+
516
+ Map: {
517
+ constructor: MapFieldType,
518
+ aliases: [Map, 'Map']
519
+ },
520
+
521
+ // Advanced FieldTypes
522
+ Decimal128: {
523
+ constructor: Decimal128FieldType,
524
+ aliases: [decimal128_type_reference, 'Decimal128']
525
+ },
526
+
527
+ BigInt: {
528
+ constructor: BigIntFieldType,
529
+ aliases: [BigInt, 'BigInt']
530
+ },
531
+
532
+ Double: {
533
+ constructor: DoubleFieldType,
534
+ aliases: [double_type_reference, 'Double']
535
+ },
536
+
537
+ Int32: {
538
+ constructor: Int32FieldType,
539
+ aliases: [int32_type_reference, 'Int32']
540
+ },
541
+
542
+ Union: {
543
+ constructor: UnionFieldType,
544
+ aliases: [union_type_reference, 'Union']
545
+ }
546
+ };
547
+ }
548
+
549
+ export {
550
+ // Type aliases
551
+ decimal128_type_reference,
552
+ double_type_reference,
553
+ int32_type_reference,
554
+ union_type_reference,
555
+ uuidv7_type_reference,
556
+
557
+ // FieldType constructors
558
+ ArrayFieldType,
559
+ BigIntFieldType,
560
+ BooleanFieldType,
561
+ BufferFieldType,
562
+ DateFieldType,
563
+ Decimal128FieldType,
564
+ DoubleFieldType,
565
+ Int32FieldType,
566
+ MapFieldType,
567
+ MixedFieldType,
568
+ NumberFieldType,
569
+ StringFieldType,
570
+ UnionFieldType,
571
+ UUIDv7FieldType,
572
+
573
+ // Registry entries
574
+ get_foundational_field_types,
575
+
576
+ // Constants
577
+ boolean_convert_to_false,
578
+ boolean_convert_to_true
579
+ };