jsonbadger 0.5.0 → 0.6.1

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
@@ -0,0 +1,335 @@
1
+ import {deep_clone, is_function, is_not_object, is_object} from '#src/utils/value.js';
2
+
3
+ // dirty-tracker: inline
4
+
5
+ /*
6
+ * MAIN API
7
+ */
8
+
9
+ /**
10
+ * Wrap this object with dirty tracking and optional set interception.
11
+ *
12
+ * @param {object} target
13
+ * @param {object} [options]
14
+ * @param {object} [options.watch]
15
+ * @param {function} [options.intercept_set]
16
+ * @returns {Proxy}
17
+ */
18
+ function track_changes(target, options = {}) {
19
+ const store = {
20
+ base_state: deep_clone(target),
21
+ dirty_keys: new Set(),
22
+ watchers: []
23
+ };
24
+
25
+ let root_proxy;
26
+ const get_root = () => root_proxy;
27
+
28
+ // Build the intercepting proxy
29
+ root_proxy = build_proxy(target, target, store, get_root, options);
30
+
31
+ // Parse and bind Watchers
32
+ if(is_object(options.watch)) {
33
+ const watch_keys = Object.keys(options.watch);
34
+ let key_index = 0;
35
+
36
+ while(key_index < watch_keys.length) {
37
+ const watch_path = watch_keys[key_index];
38
+ add_watcher(store, target, root_proxy, watch_path, options.watch[watch_path]);
39
+ key_index += 1;
40
+ }
41
+ }
42
+
43
+ return root_proxy;
44
+ }
45
+
46
+ /*
47
+ * LAZY PROXY BUILDER
48
+ */
49
+
50
+ function build_proxy(target_object, root_object, store, get_root, options = {}) {
51
+ const base_path = options.base_path || '';
52
+ let intercept_set = (path, next_value) => next_value;
53
+
54
+ if(is_function(options.intercept_set)) {
55
+ intercept_set = options.intercept_set;
56
+ }
57
+
58
+ const internal_methods = Object.assign(Object.create(null), {
59
+ $has_dirty_fields: () => {
60
+ return has_dirty_fields(store);
61
+ },
62
+ $get_dirty_fields: () => {
63
+ return get_dirty_fields(store);
64
+ },
65
+ $reset_dirty_fields: () => {
66
+ return reset_dirty_fields(store, get_root());
67
+ },
68
+ $rebase_dirty_fields: () => {
69
+ return rebase_dirty_fields(store, root_object);
70
+ },
71
+ $watch: (path, watch_options) => {
72
+ return add_watcher(store, root_object, get_root(), path, watch_options);
73
+ }
74
+ });
75
+
76
+ return new Proxy(target_object, {
77
+ get(target, prop) {
78
+ // Do not proxy internal JS symbols
79
+ if(typeof prop === 'symbol') {
80
+ return target[prop];
81
+ }
82
+
83
+ // Root level: allow access to tracker methods
84
+ if(base_path === '') {
85
+ const internal_method = internal_methods[prop];
86
+
87
+ if(internal_method) {
88
+ return internal_method;
89
+ }
90
+
91
+ if(prop in target && is_function(target[prop])) {
92
+ return target[prop];
93
+ }
94
+ }
95
+
96
+ const value = target[prop];
97
+
98
+ // Lazy-proxy nested objects on access
99
+ if(is_object(value)) {
100
+ const next_path = base_path === '' ? prop : `${base_path}.${prop}`;
101
+ const next_options = {...options, base_path: next_path};
102
+ return build_proxy(value, root_object, store, get_root, next_options);
103
+ }
104
+
105
+ return value;
106
+ },
107
+
108
+ set(target, prop, value) {
109
+ if(typeof prop === 'symbol') {
110
+ target[prop] = value;
111
+ return true;
112
+ }
113
+
114
+ // Do not track mutations to functions/methods
115
+ if(base_path === '' && is_function(target[prop])) {
116
+ target[prop] = value;
117
+ return true;
118
+ }
119
+
120
+ const full_path = base_path === '' ? prop : `${base_path}.${prop}`;
121
+ const next_value = intercept_set(full_path, value);
122
+
123
+ const old_value = target[prop];
124
+ const original_value = read_path(store.base_state, full_path);
125
+
126
+ // Apply the mutation
127
+ target[prop] = next_value;
128
+
129
+ // Track Dirty State
130
+ if(original_value !== next_value) {
131
+ store.dirty_keys.add(full_path);
132
+ } else {
133
+ store.dirty_keys.delete(full_path);
134
+ }
135
+
136
+ // Trigger Watchers
137
+ if(old_value !== next_value) {
138
+ check_watchers(store, full_path, old_value, root_object, get_root());
139
+ }
140
+
141
+ return true;
142
+ }
143
+ });
144
+ }
145
+
146
+ /*
147
+ * STATE MANAGEMENT HELPERS
148
+ */
149
+
150
+ function has_dirty_fields(store) {
151
+ return store ? store.dirty_keys.size > 0 : false;
152
+ }
153
+
154
+ function get_dirty_fields(store) {
155
+ return store ? Array.from(store.dirty_keys) : [];
156
+ }
157
+
158
+ function reset_dirty_fields(store, proxy) {
159
+ const original = store.base_state;
160
+ const original_keys = Object.keys(original);
161
+ let key_index = 0;
162
+
163
+ // Resetting through the proxy triggers setters naturally
164
+ while(key_index < original_keys.length) {
165
+ const key = original_keys[key_index];
166
+
167
+ if(!is_function(original[key])) {
168
+ proxy[key] = deep_clone(original[key]);
169
+ }
170
+
171
+ key_index += 1;
172
+ }
173
+
174
+ store.dirty_keys.clear();
175
+ }
176
+
177
+ function rebase_dirty_fields(store, root_object) {
178
+ store.base_state = deep_clone(root_object);
179
+ store.dirty_keys.clear();
180
+ }
181
+
182
+ /*
183
+ * WATCHER HELPERS
184
+ */
185
+
186
+ function add_watcher(store, root_object, proxy, path, options) {
187
+ const handler = is_function(options) ? options : () => {};
188
+ const config = is_object(options) ? options : {handler};
189
+
190
+ const watcher = {
191
+ path,
192
+ handler: config.handler,
193
+ deep: config.deep === true,
194
+ once: config.once === true,
195
+ active: true
196
+ };
197
+
198
+ store.watchers.push(watcher);
199
+
200
+ if(config.immediate) {
201
+ const initial_value = read_path(root_object, path);
202
+ watcher.handler.call(proxy, initial_value, undefined);
203
+
204
+ if(watcher.once) {
205
+ watcher.active = false;
206
+ }
207
+ }
208
+
209
+ // Return the closure to unwatch
210
+ return () => {
211
+ watcher.active = false;
212
+ const index = store.watchers.indexOf(watcher);
213
+
214
+ if(index !== -1) {
215
+ store.watchers.splice(index, 1);
216
+ }
217
+ };
218
+ }
219
+
220
+ const pending_watchers = new Map();
221
+ let is_flushing = false;
222
+
223
+ function check_watchers(store, mutated_path, old_value, root_object, root_proxy) {
224
+ const watchers = store.watchers;
225
+ let watcher_index = 0;
226
+
227
+ while(watcher_index < watchers.length) {
228
+ const watcher = watchers[watcher_index];
229
+ if(!watcher.active) {
230
+ watcher_index += 1;
231
+ continue;
232
+ }
233
+
234
+ let should_trigger = false;
235
+ let handler_new_value = undefined;
236
+ let handler_old_value = old_value;
237
+
238
+ // Exact path match
239
+ if(watcher.path === mutated_path) {
240
+ should_trigger = true;
241
+ handler_new_value = read_path(root_object, mutated_path);
242
+ }
243
+ // Deep mutation (e.g., watching 'user', mutated 'user.name')
244
+ else if(watcher.deep && mutated_path.startsWith(watcher.path + '.')) {
245
+ should_trigger = true;
246
+ // In deep mutations, new and old values are identical references to the same parent object
247
+ handler_new_value = read_path(root_object, watcher.path);
248
+ handler_old_value = handler_new_value;
249
+ }
250
+ // Parent replacement (e.g., watching 'user.name', mutated 'user')
251
+ else if(watcher.path.startsWith(mutated_path + '.')) {
252
+ should_trigger = true;
253
+ handler_new_value = read_path(root_object, watcher.path);
254
+
255
+ // Attempt to extract the old nested value from the replaced parent object
256
+ const nested_path = watcher.path.substring(mutated_path.length + 1);
257
+ handler_old_value = read_path(old_value, nested_path);
258
+ }
259
+
260
+ if(should_trigger) {
261
+ queue_watcher(watcher, handler_new_value, handler_old_value, root_proxy);
262
+ }
263
+
264
+ watcher_index += 1;
265
+ }
266
+ }
267
+
268
+ function queue_watcher(watcher, new_value, old_value, context) {
269
+ // Deduplicate watchers in the same tick.
270
+ // If it exists, we update to the latest new_value, but keep the initial old_value of this tick.
271
+ if(pending_watchers.has(watcher)) {
272
+ pending_watchers.get(watcher).new_value = new_value;
273
+ } else {
274
+ pending_watchers.set(watcher, {new_value, old_value, context});
275
+ }
276
+
277
+ if(is_flushing) {
278
+ return;
279
+ }
280
+
281
+ is_flushing = true;
282
+
283
+ // Flush asynchronously on the next microtask (after synchronous code finishes)
284
+ Promise.resolve().then(() => {
285
+ const jobs = Array.from(pending_watchers.entries());
286
+ pending_watchers.clear();
287
+ is_flushing = false;
288
+
289
+ let job_index = 0;
290
+ while(job_index < jobs.length) {
291
+ const job_watcher = jobs[job_index][0];
292
+ const job_args = jobs[job_index][1];
293
+
294
+ if(job_watcher.active) {
295
+ job_watcher.handler.call(
296
+ job_args.context,
297
+ job_args.new_value,
298
+ job_args.old_value
299
+ );
300
+
301
+ if(job_watcher.once) {
302
+ job_watcher.active = false; // Mark inactive after first run
303
+ }
304
+ }
305
+ job_index += 1;
306
+ }
307
+ });
308
+ }
309
+
310
+ /*
311
+ * PATH HELPERS
312
+ */
313
+
314
+ function read_path(root_object, dot_path) {
315
+ if(!dot_path) {
316
+ return root_object;
317
+ }
318
+
319
+ const segments = dot_path.split('.');
320
+ let current_value = root_object;
321
+ let segment_index = 0;
322
+
323
+ while(segment_index < segments.length) {
324
+ if(is_not_object(current_value)) {
325
+ return undefined;
326
+ }
327
+
328
+ current_value = current_value[segments[segment_index]];
329
+ segment_index += 1;
330
+ }
331
+
332
+ return current_value;
333
+ }
334
+
335
+ export default track_changes;