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.
- package/README.md +36 -18
- package/docs/api/connection.md +144 -0
- package/docs/api/delta-tracker.md +106 -0
- package/docs/api/document.md +77 -0
- package/docs/api/field-types.md +329 -0
- package/docs/api/index.md +35 -0
- package/docs/api/model.md +392 -0
- package/docs/api/query-builder.md +81 -0
- package/docs/api/schema.md +204 -0
- package/docs/architecture-flow.md +397 -0
- package/docs/examples.md +495 -218
- package/docs/jsonb-ops.md +171 -0
- package/docs/lifecycle/model-compilation.md +111 -0
- package/docs/lifecycle.md +146 -0
- package/docs/query-translation.md +11 -10
- package/package.json +10 -3
- package/src/connection/connect.js +12 -17
- package/src/connection/connection.js +128 -0
- package/src/connection/server-capabilities.js +60 -59
- package/src/constants/defaults.js +32 -19
- package/src/constants/{id-strategies.js → id-strategy.js} +28 -29
- package/src/constants/intake-mode.js +8 -0
- package/src/debug/debug-logger.js +17 -15
- package/src/errors/model-overwrite-error.js +25 -0
- package/src/errors/query-error.js +25 -23
- package/src/errors/validation-error.js +25 -23
- package/src/field-types/base-field-type.js +137 -140
- package/src/field-types/builtins/advanced.js +365 -365
- package/src/field-types/builtins/index.js +579 -585
- package/src/field-types/field-type-namespace.js +9 -0
- package/src/field-types/registry.js +149 -122
- package/src/index.js +26 -36
- package/src/migration/ensure-index.js +157 -154
- package/src/migration/ensure-schema.js +27 -15
- package/src/migration/ensure-table.js +44 -31
- package/src/migration/schema-indexes-resolver.js +8 -6
- package/src/model/document-instance.js +29 -540
- package/src/model/document.js +60 -0
- package/src/model/factory/constants.js +36 -0
- package/src/model/factory/index.js +58 -0
- package/src/model/model.js +875 -0
- package/src/model/operations/delete-one.js +39 -0
- package/src/model/operations/insert-one.js +35 -0
- package/src/model/operations/query-builder.js +132 -0
- package/src/model/operations/update-one.js +333 -0
- package/src/model/state.js +34 -0
- package/src/schema/field-definition-parser.js +213 -218
- package/src/schema/path-introspection.js +87 -82
- package/src/schema/schema-compiler.js +126 -212
- package/src/schema/schema.js +621 -138
- package/src/sql/index.js +17 -0
- package/src/sql/jsonb/ops.js +153 -0
- package/src/{query → sql/jsonb}/path-parser.js +54 -43
- package/src/sql/jsonb/read/elem-match.js +133 -0
- package/src/{query → sql/jsonb/read}/operators/contains.js +13 -7
- package/src/sql/jsonb/read/operators/elem-match.js +9 -0
- package/src/{query → sql/jsonb/read}/operators/has-all-keys.js +17 -11
- package/src/{query → sql/jsonb/read}/operators/has-any-keys.js +18 -11
- package/src/sql/jsonb/read/operators/has-key.js +12 -0
- package/src/{query → sql/jsonb/read}/operators/jsonpath-exists.js +22 -15
- package/src/{query → sql/jsonb/read}/operators/jsonpath-match.js +22 -15
- package/src/{query → sql/jsonb/read}/operators/size.js +23 -16
- package/src/sql/parameter-binder.js +18 -13
- package/src/sql/read/build-count-query.js +12 -0
- package/src/sql/read/build-find-query.js +25 -0
- package/src/sql/read/limit-skip.js +21 -0
- package/src/sql/read/sort.js +85 -0
- package/src/sql/read/where/base-fields.js +310 -0
- package/src/sql/read/where/casting.js +90 -0
- package/src/sql/read/where/context.js +79 -0
- package/src/sql/read/where/field-clause.js +58 -0
- package/src/sql/read/where/index.js +38 -0
- package/src/sql/read/where/operator-entries.js +29 -0
- package/src/{query → sql/read/where}/operators/all.js +16 -10
- package/src/sql/read/where/operators/eq.js +12 -0
- package/src/{query → sql/read/where}/operators/gt.js +23 -16
- package/src/{query → sql/read/where}/operators/gte.js +23 -16
- package/src/{query → sql/read/where}/operators/in.js +18 -12
- package/src/sql/read/where/operators/index.js +40 -0
- package/src/{query → sql/read/where}/operators/lt.js +23 -16
- package/src/{query → sql/read/where}/operators/lte.js +23 -16
- package/src/sql/read/where/operators/ne.js +12 -0
- package/src/{query → sql/read/where}/operators/nin.js +18 -12
- package/src/{query → sql/read/where}/operators/regex.js +14 -8
- package/src/sql/read/where/operators.js +126 -0
- package/src/sql/read/where/text-operators.js +83 -0
- package/src/sql/run.js +46 -0
- package/src/sql/write/build-delete-query.js +33 -0
- package/src/sql/write/build-insert-query.js +42 -0
- package/src/sql/write/build-update-query.js +65 -0
- package/src/utils/assert.js +34 -27
- package/src/utils/delta-tracker/.archive/1 tracker-redesign-codex-v2.md +250 -0
- package/src/utils/delta-tracker/.archive/1 tracker-redesign-gemini.md +101 -0
- package/src/utils/delta-tracker/.archive/2 evaluation by gemini.txt +65 -0
- package/src/utils/delta-tracker/.archive/2 evaluation by grok.txt +39 -0
- package/src/utils/delta-tracker/.archive/3 gemini evaluate grok.txt +37 -0
- package/src/utils/delta-tracker/.archive/3 grok evaluate gemini.txt +63 -0
- package/src/utils/delta-tracker/.archive/4 gemini veredict.txt +16 -0
- package/src/utils/delta-tracker/.archive/index.1.js +587 -0
- package/src/utils/delta-tracker/.archive/index.2.js +612 -0
- package/src/utils/delta-tracker/index.js +592 -0
- package/src/utils/dirty-tracker/inline.js +335 -0
- package/src/utils/dirty-tracker/instance.js +414 -0
- package/src/utils/dirty-tracker/static.js +343 -0
- package/src/utils/json-safe.js +13 -9
- package/src/utils/object-path.js +227 -33
- package/src/utils/object.js +408 -168
- package/src/utils/string.js +55 -0
- package/src/utils/value.js +169 -30
- package/docs/api.md +0 -152
- package/src/connection/disconnect.js +0 -16
- package/src/connection/pool-store.js +0 -46
- package/src/model/model-factory.js +0 -555
- package/src/query/limit-skip-compiler.js +0 -31
- package/src/query/operators/elem-match.js +0 -3
- package/src/query/operators/eq.js +0 -6
- package/src/query/operators/has-key.js +0 -6
- package/src/query/operators/index.js +0 -60
- package/src/query/operators/ne.js +0 -6
- package/src/query/query-builder.js +0 -93
- package/src/query/sort-compiler.js +0 -30
- package/src/query/where-compiler.js +0 -477
- package/src/sql/sql-runner.js +0 -31
|
@@ -1,540 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const field_type = resolve_schema_field_type(schema_instance, resolved_path);
|
|
32
|
-
|
|
33
|
-
if(call_options.getters === false) {
|
|
34
|
-
return path_state.value;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if(!field_type || !field_type.options || typeof field_type.options.get !== 'function') {
|
|
38
|
-
return path_state.value;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return field_type.apply_get(path_state.value, {
|
|
42
|
-
path: resolved_path,
|
|
43
|
-
mode: 'get'
|
|
44
|
-
});
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
model_constructor.prototype.set = function (path_name, next_value, runtime_options) {
|
|
48
|
-
ensure_runtime_state(this);
|
|
49
|
-
|
|
50
|
-
const call_options = is_object(runtime_options) ? runtime_options : {};
|
|
51
|
-
const resolved_path = resolve_alias_path(alias_path_map, path_name);
|
|
52
|
-
const path_segments = split_dot_path(resolved_path);
|
|
53
|
-
const field_type = resolve_schema_field_type(schema_instance, resolved_path);
|
|
54
|
-
const current_path_state = read_existing_path(this.data, path_segments);
|
|
55
|
-
let assigned_value = next_value;
|
|
56
|
-
|
|
57
|
-
if(field_type) {
|
|
58
|
-
if(call_options.setters !== false) {
|
|
59
|
-
assigned_value = field_type.apply_set(assigned_value, {
|
|
60
|
-
path: resolved_path,
|
|
61
|
-
mode: 'set'
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if(call_options.cast !== false && typeof field_type.cast === 'function') {
|
|
66
|
-
assigned_value = field_type.cast(assigned_value, {
|
|
67
|
-
path: resolved_path,
|
|
68
|
-
mode: 'set'
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
assert_immutable_write_allowed(this, field_type, resolved_path, current_path_state, assigned_value);
|
|
74
|
-
|
|
75
|
-
if(current_path_state.exists && Object.is(current_path_state.value, assigned_value)) {
|
|
76
|
-
return this;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if(!is_object(this.data)) {
|
|
80
|
-
this.data = {};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
write_path(this.data, path_segments, assigned_value);
|
|
84
|
-
mark_document_path_modified(this, resolved_path);
|
|
85
|
-
return this;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
model_constructor.prototype.mark_modified = function (path_name) {
|
|
89
|
-
ensure_runtime_state(this);
|
|
90
|
-
|
|
91
|
-
const resolved_path = resolve_alias_path(alias_path_map, path_name);
|
|
92
|
-
split_dot_path(resolved_path);
|
|
93
|
-
mark_document_path_modified(this, resolved_path);
|
|
94
|
-
return this;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
model_constructor.prototype.is_modified = function (path_name) {
|
|
98
|
-
ensure_runtime_state(this);
|
|
99
|
-
|
|
100
|
-
const runtime_state = this[runtime_state_symbol];
|
|
101
|
-
|
|
102
|
-
if(path_name === undefined) {
|
|
103
|
-
return runtime_state.modified_paths.size > 0;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const resolved_path = resolve_alias_path(alias_path_map, path_name);
|
|
107
|
-
split_dot_path(resolved_path);
|
|
108
|
-
return is_path_marked_modified(runtime_state.modified_paths, resolved_path);
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
model_constructor.prototype.clear_modified = function () {
|
|
112
|
-
ensure_runtime_state(this);
|
|
113
|
-
clear_document_modified_paths(this);
|
|
114
|
-
return this;
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
define_alias_properties(model_constructor, alias_path_map);
|
|
118
|
-
|
|
119
|
-
model_constructor.prototype.to_object = function (serialization_options) {
|
|
120
|
-
return serialize_document(this, model_constructor, 'to_object', serialization_options);
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
model_constructor.prototype.to_json = function (serialization_options) {
|
|
124
|
-
return serialize_document(this, model_constructor, 'to_json', serialization_options);
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
model_constructor.prototype.toJSON = function () {
|
|
128
|
-
return this.to_json();
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
model_constructor.prototype.save = async function () {
|
|
132
|
-
ensure_runtime_state(this);
|
|
133
|
-
|
|
134
|
-
const model_options = model_constructor.model_options;
|
|
135
|
-
const table_name = model_options.table_name;
|
|
136
|
-
const data_column = model_options.data_column;
|
|
137
|
-
const table_identifier = quote_identifier(table_name);
|
|
138
|
-
const data_identifier = quote_identifier(data_column);
|
|
139
|
-
const validated_payload = this.validate();
|
|
140
|
-
|
|
141
|
-
await model_constructor.ensure_table();
|
|
142
|
-
|
|
143
|
-
const insert_statement = build_insert_statement(table_identifier, data_identifier, validated_payload);
|
|
144
|
-
const query_result = await sql_runner(insert_statement.sql_text, insert_statement.sql_params);
|
|
145
|
-
const saved_data = query_result.rows[0].data;
|
|
146
|
-
|
|
147
|
-
this.data = saved_data;
|
|
148
|
-
this.is_new = false;
|
|
149
|
-
clear_document_modified_paths(this);
|
|
150
|
-
return saved_data;
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function build_insert_statement(table_identifier, data_identifier, validated_payload) {
|
|
155
|
-
const serialized_payload = jsonb_stringify(validated_payload);
|
|
156
|
-
const sql_text = 'INSERT INTO ' + table_identifier + ' (' + data_identifier + ') VALUES ($1::jsonb) RETURNING ' + data_identifier + ' AS data';
|
|
157
|
-
const sql_params = [serialized_payload];
|
|
158
|
-
|
|
159
|
-
return {
|
|
160
|
-
sql_text: sql_text,
|
|
161
|
-
sql_params: sql_params
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function serialize_document(document_instance, model_constructor, mode_value, serialization_options) {
|
|
166
|
-
const schema_instance = model_constructor.schema_instance;
|
|
167
|
-
const call_options = is_object(serialization_options) ? serialization_options : {};
|
|
168
|
-
const schema_serialization_options = resolve_schema_serialization_options(schema_instance, mode_value);
|
|
169
|
-
const apply_getters = resolve_getters_option(call_options, schema_serialization_options);
|
|
170
|
-
let serialized_value = deep_clone(document_instance.data);
|
|
171
|
-
|
|
172
|
-
if(apply_getters) {
|
|
173
|
-
serialized_value = apply_schema_getters(serialized_value, schema_instance, mode_value);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const transform_function = resolve_transform_function(call_options, schema_serialization_options);
|
|
177
|
-
|
|
178
|
-
if(typeof transform_function !== 'function') {
|
|
179
|
-
return serialized_value;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const transform_result = transform_function.call(null, document_instance, serialized_value, {
|
|
183
|
-
mode: mode_value
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
if(transform_result === undefined) {
|
|
187
|
-
return serialized_value;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return transform_result;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function resolve_schema_serialization_options(schema_instance, mode_value) {
|
|
194
|
-
if(!schema_instance || !is_object(schema_instance.options)) {
|
|
195
|
-
return null;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const option_key = mode_value === 'to_json' ? 'to_json' : 'to_object';
|
|
199
|
-
const option_value = schema_instance.options[option_key];
|
|
200
|
-
|
|
201
|
-
if(!is_object(option_value)) {
|
|
202
|
-
return null;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return option_value;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function resolve_getters_option(call_options, schema_serialization_options) {
|
|
209
|
-
if(call_options.getters !== undefined) {
|
|
210
|
-
return call_options.getters === true;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if(schema_serialization_options && schema_serialization_options.getters !== undefined) {
|
|
214
|
-
return schema_serialization_options.getters === true;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return true;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function resolve_transform_function(call_options, schema_serialization_options) {
|
|
221
|
-
if(call_options.transform === false) {
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if(typeof call_options.transform === 'function') {
|
|
226
|
-
return call_options.transform;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if(schema_serialization_options && typeof schema_serialization_options.transform === 'function') {
|
|
230
|
-
return schema_serialization_options.transform;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function apply_schema_getters(serialized_value, schema_instance, mode_value) {
|
|
237
|
-
if(!schema_instance || typeof schema_instance.path !== 'function') {
|
|
238
|
-
return serialized_value;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if(is_not_object(serialized_value)) {
|
|
242
|
-
return serialized_value;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const path_names = resolve_schema_paths(schema_instance);
|
|
246
|
-
let path_index = 0;
|
|
247
|
-
|
|
248
|
-
while(path_index < path_names.length) {
|
|
249
|
-
const path_name = path_names[path_index];
|
|
250
|
-
const field_type = schema_instance.path(path_name);
|
|
251
|
-
|
|
252
|
-
if(!field_type || !field_type.options || typeof field_type.options.get !== 'function') {
|
|
253
|
-
path_index += 1;
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const path_segments = path_name.split('.');
|
|
258
|
-
const apply_getter = (current_path_value) => {
|
|
259
|
-
return field_type.apply_get(current_path_value, {
|
|
260
|
-
path: path_name,
|
|
261
|
-
mode: mode_value
|
|
262
|
-
});
|
|
263
|
-
};
|
|
264
|
-
const update_result = update_existing_path(serialized_value, path_segments, apply_getter);
|
|
265
|
-
|
|
266
|
-
if(!update_result.exists) {
|
|
267
|
-
path_index += 1;
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
path_index += 1;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return serialized_value;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function resolve_schema_paths(schema_instance) {
|
|
278
|
-
const schema_description = schema_instance.schema_description;
|
|
279
|
-
|
|
280
|
-
if(!schema_description || !is_array(schema_description.paths)) {
|
|
281
|
-
return [];
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const path_entries = [];
|
|
285
|
-
let path_index = 0;
|
|
286
|
-
|
|
287
|
-
while(path_index < schema_description.paths.length) {
|
|
288
|
-
const path_name = schema_description.paths[path_index];
|
|
289
|
-
|
|
290
|
-
path_entries.push({
|
|
291
|
-
path_name: path_name,
|
|
292
|
-
depth: path_name.split('.').length
|
|
293
|
-
});
|
|
294
|
-
path_index += 1;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
path_entries.sort(sort_paths_by_getter_order);
|
|
298
|
-
|
|
299
|
-
const sorted_paths = [];
|
|
300
|
-
path_index = 0;
|
|
301
|
-
|
|
302
|
-
while(path_index < path_entries.length) {
|
|
303
|
-
sorted_paths.push(path_entries[path_index].path_name);
|
|
304
|
-
path_index += 1;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return sorted_paths;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function sort_paths_by_getter_order(left_path_entry, right_path_entry) {
|
|
311
|
-
const left_depth = left_path_entry.depth;
|
|
312
|
-
const right_depth = right_path_entry.depth;
|
|
313
|
-
|
|
314
|
-
if(left_depth !== right_depth) {
|
|
315
|
-
return right_depth - left_depth;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
return left_path_entry.path_name.localeCompare(right_path_entry.path_name);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function build_alias_path_map(schema_instance) {
|
|
322
|
-
const alias_path_map = Object.create(null);
|
|
323
|
-
const schema_description = schema_instance && schema_instance.schema_description;
|
|
324
|
-
|
|
325
|
-
if(!schema_description || !is_array(schema_description.paths) || typeof schema_instance.path !== 'function') {
|
|
326
|
-
return alias_path_map;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
let path_index = 0;
|
|
330
|
-
|
|
331
|
-
while(path_index < schema_description.paths.length) {
|
|
332
|
-
const path_name = schema_description.paths[path_index];
|
|
333
|
-
const field_type = schema_instance.path(path_name);
|
|
334
|
-
const alias_value = field_type && field_type.options ? field_type.options.alias : undefined;
|
|
335
|
-
|
|
336
|
-
if(!is_string(alias_value) || alias_value.length === 0) {
|
|
337
|
-
path_index += 1;
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if(has_own(alias_path_map, alias_value) && alias_path_map[alias_value] !== path_name) {
|
|
342
|
-
throw new Error('Duplicate alias "' + alias_value + '" for paths "' + alias_path_map[alias_value] + '" and "' + path_name + '"');
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
alias_path_map[alias_value] = path_name;
|
|
346
|
-
path_index += 1;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return alias_path_map;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
function define_alias_properties(model_constructor, alias_path_map) {
|
|
353
|
-
const alias_names = Object.keys(alias_path_map);
|
|
354
|
-
let alias_index = 0;
|
|
355
|
-
|
|
356
|
-
while(alias_index < alias_names.length) {
|
|
357
|
-
const alias_name = alias_names[alias_index];
|
|
358
|
-
|
|
359
|
-
if(alias_name.indexOf('.') !== -1) {
|
|
360
|
-
alias_index += 1;
|
|
361
|
-
continue;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
if(alias_name in model_constructor.prototype) {
|
|
365
|
-
throw new Error('Alias "' + alias_name + '" conflicts with an existing document property');
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
Object.defineProperty(model_constructor.prototype, alias_name, {
|
|
369
|
-
configurable: true,
|
|
370
|
-
enumerable: false,
|
|
371
|
-
get: function () {
|
|
372
|
-
return this.get(alias_name);
|
|
373
|
-
},
|
|
374
|
-
set: function (next_value) {
|
|
375
|
-
this.set(alias_name, next_value);
|
|
376
|
-
}
|
|
377
|
-
});
|
|
378
|
-
alias_index += 1;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function resolve_alias_path(alias_path_map, path_name) {
|
|
383
|
-
if(has_own(alias_path_map, path_name)) {
|
|
384
|
-
return alias_path_map[path_name];
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
return path_name;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
function resolve_schema_field_type(schema_instance, path_name) {
|
|
391
|
-
if(!schema_instance || typeof schema_instance.path !== 'function') {
|
|
392
|
-
return null;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
return schema_instance.path(path_name);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
function ensure_runtime_state(document_value) {
|
|
399
|
-
if(typeof document_value.is_new !== 'boolean') {
|
|
400
|
-
document_value.is_new = true;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
if(!document_value[runtime_state_symbol]) {
|
|
404
|
-
document_value[runtime_state_symbol] = {
|
|
405
|
-
modified_paths: new Set()
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
return document_value[runtime_state_symbol];
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
function assert_immutable_write_allowed(document_value, field_type, path_name, current_path_state, assigned_value) {
|
|
413
|
-
if(!field_type || !field_type.options || field_type.options.immutable !== true) {
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
if(document_value.is_new === true) {
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
const current_value = current_path_state.exists ? current_path_state.value : undefined;
|
|
422
|
-
|
|
423
|
-
if(Object.is(current_value, assigned_value)) {
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
throw new Error('Path "' + path_name + '" is immutable');
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
function mark_document_path_modified(document_value, path_name) {
|
|
431
|
-
const runtime_state = ensure_runtime_state(document_value);
|
|
432
|
-
runtime_state.modified_paths.add(path_name);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
function clear_document_modified_paths(document_value) {
|
|
436
|
-
const runtime_state = ensure_runtime_state(document_value);
|
|
437
|
-
runtime_state.modified_paths.clear();
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function is_path_marked_modified(modified_paths, path_name) {
|
|
441
|
-
if(modified_paths.has(path_name)) {
|
|
442
|
-
return true;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const modified_path_values = Array.from(modified_paths);
|
|
446
|
-
let path_index = 0;
|
|
447
|
-
|
|
448
|
-
while(path_index < modified_path_values.length) {
|
|
449
|
-
const modified_path = modified_path_values[path_index];
|
|
450
|
-
|
|
451
|
-
if(is_parent_or_child_path(modified_path, path_name)) {
|
|
452
|
-
return true;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
path_index += 1;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
return false;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
function is_parent_or_child_path(left_path, right_path) {
|
|
462
|
-
return left_path.indexOf(right_path + '.') === 0 || right_path.indexOf(left_path + '.') === 0;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
function read_existing_path(root_object, path_segments) {
|
|
466
|
-
let current_value = root_object;
|
|
467
|
-
let segment_index = 0;
|
|
468
|
-
|
|
469
|
-
while(segment_index < path_segments.length) {
|
|
470
|
-
const segment_value = path_segments[segment_index];
|
|
471
|
-
|
|
472
|
-
if(is_not_object(current_value) || !has_own(current_value, segment_value)) {
|
|
473
|
-
return {
|
|
474
|
-
exists: false,
|
|
475
|
-
value: undefined
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
current_value = current_value[segment_value];
|
|
480
|
-
segment_index += 1;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
return {
|
|
484
|
-
exists: true,
|
|
485
|
-
value: current_value
|
|
486
|
-
};
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
function write_path(root_object, path_segments, next_value) {
|
|
490
|
-
let current_value = root_object;
|
|
491
|
-
let segment_index = 0;
|
|
492
|
-
|
|
493
|
-
while(segment_index < path_segments.length) {
|
|
494
|
-
const segment_value = path_segments[segment_index];
|
|
495
|
-
const is_leaf = segment_index === path_segments.length - 1;
|
|
496
|
-
|
|
497
|
-
if(is_leaf) {
|
|
498
|
-
current_value[segment_value] = next_value;
|
|
499
|
-
return;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
const has_segment = has_own(current_value, segment_value);
|
|
503
|
-
const segment_value_ref = has_segment ? current_value[segment_value] : undefined;
|
|
504
|
-
const should_create_container = !has_segment || is_not_object(segment_value_ref);
|
|
505
|
-
|
|
506
|
-
if(should_create_container) {
|
|
507
|
-
current_value[segment_value] = {};
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
current_value = current_value[segment_value];
|
|
511
|
-
segment_index += 1;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
function update_existing_path(root_object, path_segments, update_value) {
|
|
516
|
-
let current_value = root_object;
|
|
517
|
-
let segment_index = 0;
|
|
518
|
-
|
|
519
|
-
while(segment_index < path_segments.length) {
|
|
520
|
-
const segment_value = path_segments[segment_index];
|
|
521
|
-
const is_leaf = segment_index === path_segments.length - 1;
|
|
522
|
-
|
|
523
|
-
if(is_not_object(current_value) || !has_own(current_value, segment_value)) {
|
|
524
|
-
return {
|
|
525
|
-
exists: false
|
|
526
|
-
};
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
if(is_leaf) {
|
|
530
|
-
current_value[segment_value] = update_value(current_value[segment_value]);
|
|
531
|
-
return {
|
|
532
|
-
exists: true
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
current_value = current_value[segment_value];
|
|
537
|
-
segment_index += 1;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Deprecated placeholder for the legacy document-instance runtime.
|
|
3
|
+
*
|
|
4
|
+
* Production code no longer uses this module. The remaining items below are
|
|
5
|
+
* the feature candidates that have not been ported into `document.js` /
|
|
6
|
+
* `model.js` yet.
|
|
7
|
+
*/
|
|
8
|
+
const pending_document_instance_features = Object.freeze([
|
|
9
|
+
'document serialization helpers: $serialize, to_json, toJSON',
|
|
10
|
+
'alias and schema-root property proxies',
|
|
11
|
+
'schema-defined prototype method installation',
|
|
12
|
+
'path-oriented dirty APIs: mark_modified, is_modified, clear_modified',
|
|
13
|
+
'instance delete convenience wrapper'
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Return the remaining legacy feature backlog for document-instance parity.
|
|
18
|
+
*
|
|
19
|
+
* @returns {string[]}
|
|
20
|
+
*/
|
|
21
|
+
function document_instance() {
|
|
22
|
+
return pending_document_instance_features;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
pending_document_instance_features
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default document_instance;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {read_nested_path, split_dot_path, write_nested_path} from '#src/utils/object-path.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create one document instance from normalized state.
|
|
5
|
+
*
|
|
6
|
+
* @param {object} [data]
|
|
7
|
+
* @returns {Document}
|
|
8
|
+
*/
|
|
9
|
+
function Document(data = {}) {
|
|
10
|
+
Object.assign(this, data);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
Document.prototype.id = null;
|
|
14
|
+
Document.prototype.created_at = null;
|
|
15
|
+
Document.prototype.updated_at = null;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Apply one state object onto this document.
|
|
19
|
+
*
|
|
20
|
+
* @param {object} [data]
|
|
21
|
+
* @returns {Document}
|
|
22
|
+
*/
|
|
23
|
+
Document.prototype.init = function (data = {}) {
|
|
24
|
+
Object.assign(this, data);
|
|
25
|
+
|
|
26
|
+
return this;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Read one document value by path.
|
|
31
|
+
*
|
|
32
|
+
* @param {string} path_name
|
|
33
|
+
* @returns {*}
|
|
34
|
+
*/
|
|
35
|
+
Document.prototype.get = function (path_name) {
|
|
36
|
+
const path_segments = split_dot_path(path_name);
|
|
37
|
+
const path_state = read_nested_path(this, path_segments);
|
|
38
|
+
|
|
39
|
+
if(!path_state.exists) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return path_state.value;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Set one document value by path.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} path
|
|
50
|
+
* @param {*} value
|
|
51
|
+
* @returns {Document}
|
|
52
|
+
*/
|
|
53
|
+
Document.prototype.set = function (path, value) {
|
|
54
|
+
const path_segments = split_dot_path(path);
|
|
55
|
+
write_nested_path(this, path_segments, value);
|
|
56
|
+
|
|
57
|
+
return this;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default Document;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const update_path_root_segment_pattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
2
|
+
const update_path_nested_segment_pattern = /^(?:[a-zA-Z_][a-zA-Z0-9_]*|[0-9]+)$/;
|
|
3
|
+
|
|
4
|
+
const base_field_keys = new Set(['id', 'created_at', 'updated_at']);
|
|
5
|
+
const timestamp_fields = new Set(['created_at', 'updated_at']);
|
|
6
|
+
const unsafe_from_keys = new Set(['__proto__', 'constructor', 'prototype']);
|
|
7
|
+
|
|
8
|
+
const update_operator_names = Object.freeze({
|
|
9
|
+
set: '$set',
|
|
10
|
+
insert: '$insert',
|
|
11
|
+
set_lax: '$set_lax'
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const update_operator_order = Object.freeze([
|
|
15
|
+
update_operator_names.set,
|
|
16
|
+
update_operator_names.insert,
|
|
17
|
+
update_operator_names.set_lax
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
const set_lax_null_treatments = Object.freeze({
|
|
21
|
+
raise_exception: 'raise_exception',
|
|
22
|
+
use_json_null: 'use_json_null',
|
|
23
|
+
delete_key: 'delete_key',
|
|
24
|
+
return_target: 'return_target'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
base_field_keys,
|
|
29
|
+
set_lax_null_treatments,
|
|
30
|
+
timestamp_fields,
|
|
31
|
+
unsafe_from_keys,
|
|
32
|
+
update_operator_names,
|
|
33
|
+
update_operator_order,
|
|
34
|
+
update_path_root_segment_pattern,
|
|
35
|
+
update_path_nested_segment_pattern
|
|
36
|
+
};
|