nesoi 3.3.30 → 3.4.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 (47) hide show
  1. package/lib/compiler/elements/bucket.element.js +3 -3
  2. package/lib/elements/edge/controller/adapters/controller_adapter.js +1 -3
  3. package/lib/elements/entities/bucket/adapters/memory.nql.js +12 -18
  4. package/lib/elements/entities/bucket/bucket.d.ts +6 -2
  5. package/lib/elements/entities/bucket/bucket.js +4 -4
  6. package/lib/elements/entities/bucket/graph/bucket_graph.js +13 -3
  7. package/lib/elements/entities/bucket/model/bucket_model.convert.js +9 -17
  8. package/lib/elements/entities/bucket/model/bucket_model.d.ts +6 -1
  9. package/lib/elements/entities/bucket/model/bucket_model.js +182 -33
  10. package/lib/elements/entities/bucket/model/bucket_model.schema.d.ts +1 -1
  11. package/lib/elements/entities/bucket/model/bucket_model.schema.js +2 -2
  12. package/lib/elements/entities/bucket/query/nql.schema.d.ts +1 -0
  13. package/lib/elements/entities/bucket/query/nql_compiler.js +5 -4
  14. package/lib/elements/entities/bucket/query/nql_engine.js +0 -2
  15. package/lib/elements/entities/bucket/view/bucket_view.d.ts +5 -4
  16. package/lib/elements/entities/bucket/view/bucket_view.js +300 -188
  17. package/lib/elements/entities/bucket/view/bucket_view.schema.d.ts +5 -3
  18. package/lib/elements/entities/bucket/view/bucket_view.schema.js +3 -1
  19. package/lib/elements/entities/bucket/view/bucket_view_field.builder.d.ts +15 -5
  20. package/lib/elements/entities/bucket/view/bucket_view_field.builder.js +82 -28
  21. package/lib/elements/entities/message/template/message_template_parser.d.ts +5 -1
  22. package/lib/elements/entities/message/template/message_template_parser.js +13 -7
  23. package/lib/engine/daemon.d.ts +1 -11
  24. package/lib/engine/daemon.js +3 -26
  25. package/lib/engine/data/datetime.d.ts +35 -1
  26. package/lib/engine/data/datetime.js +103 -16
  27. package/lib/engine/data/error.d.ts +17 -0
  28. package/lib/engine/data/error.js +16 -0
  29. package/lib/engine/transaction/nodes/bucket.trx_node.d.ts +8 -2
  30. package/lib/engine/transaction/nodes/bucket.trx_node.js +16 -4
  31. package/lib/engine/transaction/nodes/bucket_query.trx_node.js +1 -1
  32. package/lib/engine/transaction/nodes/external.trx_node.d.ts +4 -1
  33. package/lib/engine/transaction/nodes/external.trx_node.js +19 -19
  34. package/lib/engine/transaction/nodes/job.trx_node.d.ts +4 -3
  35. package/lib/engine/transaction/nodes/job.trx_node.js +6 -2
  36. package/lib/engine/transaction/nodes/resource.trx_node.js +2 -1
  37. package/lib/engine/transaction/trx.d.ts +4 -3
  38. package/lib/engine/transaction/trx.js +15 -11
  39. package/lib/engine/transaction/trx_engine.config.d.ts +7 -3
  40. package/lib/engine/transaction/trx_engine.d.ts +7 -3
  41. package/lib/engine/transaction/trx_engine.js +99 -45
  42. package/lib/engine/transaction/trx_node.d.ts +4 -1
  43. package/lib/engine/transaction/trx_node.js +12 -9
  44. package/package.json +1 -1
  45. package/tools/joaquin/bucket.d.ts +6 -2
  46. package/tools/joaquin/bucket.js +4 -4
  47. package/tsconfig.build.tsbuildinfo +1 -1
@@ -206,9 +206,9 @@ class BucketElement extends element_1.Element {
206
206
  for (const key in fields) {
207
207
  const field = fields[key];
208
208
  if (field.scope === 'model' && 'model' in field.meta) {
209
- const modelFields = bucket_model_schema_1.$BucketModel.getField(this.schema.model, field.meta.model.path);
209
+ const modelFields = bucket_model_schema_1.$BucketModel.getFields(this.schema.model, field.meta.model.path);
210
210
  const types = [];
211
- if (!field.children || '__raw' in field.children) {
211
+ if (!field.children || '__root' in field.children) {
212
212
  types.push(dump_helpers_1.DumpHelpers.dumpUnionType(modelFields.map(f => this.buildModelFieldType(f))));
213
213
  }
214
214
  // Contains children
@@ -238,7 +238,7 @@ class BucketElement extends element_1.Element {
238
238
  return data;
239
239
  };
240
240
  const data = {};
241
- if ('__raw' in schema) {
241
+ if ('__root' in schema) {
242
242
  Object.assign(data, model);
243
243
  }
244
244
  const viewData = buildFields(schema);
@@ -22,9 +22,7 @@ class ControllerAdapter {
22
22
  const trx = this.daemon.trx(this.schema.module)
23
23
  .origin('controller:' + this.schema.name + ':' + endpoint.name)
24
24
  .auth(auth);
25
- if (endpoint.idempotent)
26
- trx.idempotent();
27
- return await trx.run(fn);
25
+ return await trx.run(fn, undefined, endpoint.idempotent);
28
26
  }
29
27
  catch (e) {
30
28
  log_1.Log.error('controller', this.schema.name, 'Unknown error', e);
@@ -172,26 +172,15 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
172
172
  };
173
173
  const _rule = (rule, objs, params, param_templates) => {
174
174
  const out = {};
175
- const combos = [];
176
- for (const param of params) {
177
- if (param_templates.length) {
178
- for (const template of param_templates) {
179
- combos.push({ params: param, param_template: template });
180
- }
181
- }
182
- else {
183
- combos.push({ params: param, param_template: {} });
184
- }
185
- }
186
- for (const combo of combos) {
187
- const match = _index(rule, objs, combo.params, combo.param_template);
175
+ for (let i = 0; i < params.length; i++) {
176
+ const match = _index(rule, objs, params[i], param_templates[i] ?? {});
188
177
  if (match) {
189
178
  Object.assign(out, match);
190
179
  continue;
191
180
  }
192
181
  for (const id in objs) {
193
182
  const obj = objs[id];
194
- let match = _obj(rule, obj, combo.params, combo.param_template);
183
+ let match = _obj(rule, obj, params[i], param_templates[i] ?? {});
195
184
  if (rule.not) {
196
185
  match = !match;
197
186
  }
@@ -235,11 +224,16 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
235
224
  }
236
225
  }
237
226
  else if ('param_with_$' in rule.value) {
238
- let path = rule.value.param_with_$;
239
- for (const key in param_template) {
240
- path = path.replace(new RegExp(key.replace('$', '\\$'), 'g'), param_template[key]);
227
+ if (Object.keys(param_template).length) {
228
+ let path = rule.value.param_with_$;
229
+ for (const key in param_template) {
230
+ path = path.replace(new RegExp(key.replace('$', '\\$'), 'g'), param_template[key]);
231
+ }
232
+ queryValue = tree_1.Tree.get(params, path);
233
+ }
234
+ else {
235
+ queryValue = undefined;
241
236
  }
242
- queryValue = tree_1.Tree.get(params, path);
243
237
  }
244
238
  else if ('static' in rule.value) {
245
239
  queryValue = rule.value.static;
@@ -133,11 +133,15 @@ export declare class Bucket<M extends $Module, $ extends $Bucket> {
133
133
  /**
134
134
  * Build one object with a view
135
135
  */
136
- buildOne<V extends ViewName<$>, Obj extends ViewObj<$, V>>(trx: AnyTrxNode, obj: $['#data'], view: V): Promise<Obj>;
136
+ buildOne<V extends ViewName<$>, Obj extends ViewObj<$, V>>(trx: AnyTrxNode, obj: $['#data'], view: V, flags?: {
137
+ serialize: boolean;
138
+ }): Promise<Obj>;
137
139
  /**
138
140
  * Build a list ob objects with a view
139
141
  */
140
- buildMany<V extends ViewName<$>, Obj extends ViewObj<$, V>>(trx: AnyTrxNode, objs: $['#data'][], view: V): Promise<Obj[]>;
142
+ buildMany<V extends ViewName<$>, Obj extends ViewObj<$, V>>(trx: AnyTrxNode, objs: $['#data'][], view: V, flags?: {
143
+ serialize: boolean;
144
+ }): Promise<Obj[]>;
141
145
  /**
142
146
  * Create an entity
143
147
  */
@@ -304,20 +304,20 @@ class Bucket {
304
304
  /**
305
305
  * Build one object with a view
306
306
  */
307
- async buildOne(trx, obj, view) {
307
+ async buildOne(trx, obj, view, flags) {
308
308
  if (!(view in this.views)) {
309
309
  throw error_1.NesoiError.Bucket.ViewNotFound({ bucket: this.schema.alias, view: view });
310
310
  }
311
- return this.views[view].parse(trx, obj);
311
+ return this.views[view].parse(trx, obj, flags);
312
312
  }
313
313
  /**
314
314
  * Build a list ob objects with a view
315
315
  */
316
- async buildMany(trx, objs, view) {
316
+ async buildMany(trx, objs, view, flags) {
317
317
  if (!(view in this.views)) {
318
318
  throw error_1.NesoiError.Bucket.ViewNotFound({ bucket: this.schema.alias, view: view });
319
319
  }
320
- return this.views[view].parseMany(trx, objs);
320
+ return this.views[view].parseMany(trx, objs, flags);
321
321
  }
322
322
  // Create
323
323
  /**
@@ -94,6 +94,14 @@ class BucketGraph {
94
94
  * - `no_tenancy`: Don't apply tenancy rules (default: `false`)
95
95
  */
96
96
  async readManyLinks(trx, objs, link, options) {
97
+ // Optimization
98
+ if (objs.length <= 1) {
99
+ const result = await this.readLink(trx, objs[0], {
100
+ name: link.name,
101
+ index: link.indexes[0] ?? []
102
+ }, options);
103
+ return [result];
104
+ }
97
105
  log_1.Log.trace('bucket', this.bucketName, `Read link ${link.name}`);
98
106
  const schema = this.schema.links[link.name];
99
107
  // 1st Query
@@ -150,17 +158,19 @@ class BucketGraph {
150
158
  }
151
159
  },
152
160
  });
153
- for (const obj of objs) {
161
+ for (let i = 0; i < objs.length; i++) {
154
162
  let result;
163
+ const param = [{ ...objs[i] }];
164
+ const param_template = param_templates ? [param_templates[i]] : [];
155
165
  if (tempAdapter instanceof bucket_cache_1.BucketCache) {
156
166
  result = await tempAdapter._queryCompiled(trx, compiled, {
157
167
  perPage: schema.many ? undefined : 1,
158
- }, [{ ...obj }], undefined);
168
+ }, param, param_template);
159
169
  }
160
170
  else {
161
171
  result = await tempAdapter._queryCompiled(trx, compiled, {
162
172
  perPage: schema.many ? undefined : 1,
163
- }, [{ ...obj }], undefined, undefined, {
173
+ }, param, param_template, undefined, {
164
174
  module: schema.bucket.module,
165
175
  buckets: {
166
176
  [schema.bucket.short]: {
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.convertToView = convertToView;
4
4
  exports.convertToMessage = convertToMessage;
5
- const bucket_view_builder_1 = require("../view/bucket_view.builder");
6
5
  const bucket_view_schema_1 = require("../view/bucket_view.schema");
7
6
  const message_template_schema_1 = require("../../message/template/message_template.schema");
8
7
  const bucket_graph_schema_1 = require("../graph/bucket_graph.schema");
@@ -13,22 +12,15 @@ const message_schema_1 = require("../../message/message.schema");
13
12
  * @subcategory Entity
14
13
  * */
15
14
  function convertToView(model, name, fields = model.fields, path, depth = 0) {
16
- const view = new bucket_view_builder_1.BucketViewBuilder(name);
17
- const convertFields = (fields) => {
18
- const viewFields = {};
19
- for (const f in fields) {
20
- const field = fields[f];
21
- const $ = new bucket_view_field_builder_1.BucketViewFieldFactory();
22
- const key = (path ? path + '.' : '')
23
- + field.name;
24
- const builder = $.model(key);
25
- const graph = new bucket_graph_schema_1.$BucketGraph();
26
- viewFields[f] = bucket_view_field_builder_1.BucketViewFieldBuilder.build(builder, model, graph, {}, field.name, depth);
27
- }
28
- return viewFields;
29
- };
30
- const v = new bucket_view_schema_1.$BucketView(name, convertFields(fields));
31
- return v;
15
+ return new bucket_view_schema_1.$BucketView(name, Object.fromEntries(Object.entries(fields).map(([model_key, model_field]) => {
16
+ const $ = new bucket_view_field_builder_1.BucketViewFieldFactory();
17
+ const key = (path ? path + '.' : '')
18
+ + model_field.name;
19
+ const builder = $.model(key);
20
+ const graph = new bucket_graph_schema_1.$BucketGraph();
21
+ const view_field = bucket_view_field_builder_1.BucketViewFieldBuilder.build(builder, model, graph, {}, model_field.name, depth);
22
+ return [model_key, view_field];
23
+ })));
32
24
  }
33
25
  /**
34
26
  * @category Elements
@@ -7,6 +7,11 @@ type BucketModelCopyCmd = {
7
7
  copy: Record<string, any>;
8
8
  path: string;
9
9
  depth: number;
10
+ modelpath?: {
11
+ i: number;
12
+ asterisk_values: string[];
13
+ };
14
+ is_union_option?: boolean | 'last';
10
15
  };
11
16
  /**
12
17
  * @category Elements
@@ -17,6 +22,6 @@ export declare class BucketModel<M extends $Module, $ extends $Bucket> {
17
22
  private alias;
18
23
  private schema;
19
24
  constructor(bucket: $Bucket, config?: BucketAdapterConfig | undefined);
20
- copy<T extends Record<string, any>>(obj: T, op: 'save' | 'load', serialize?: (cmd?: BucketModelCopyCmd) => boolean): T;
25
+ copy<T extends Record<string, any>>(obj: T, op: 'save' | 'load', serialize?: (cmd?: BucketModelCopyCmd) => boolean, modelpath?: string): T;
21
26
  }
22
27
  export {};
@@ -19,7 +19,7 @@ class BucketModel {
19
19
  this.alias = bucket.alias;
20
20
  this.schema = bucket.model;
21
21
  }
22
- copy(obj, op, serialize) {
22
+ copy(obj, op, serialize, modelpath) {
23
23
  const meta = this.config?.meta || {
24
24
  created_at: 'created_at',
25
25
  created_by: 'created_by',
@@ -27,65 +27,170 @@ class BucketModel {
27
27
  updated_by: 'updated_by',
28
28
  };
29
29
  const copy = {};
30
- let poll = Object.entries(this.schema.fields).map(([path, field]) => ({
31
- path, obj, copy, field, depth: 0
32
- }));
30
+ const modelpath_results = [];
31
+ const paths = modelpath?.split('.');
32
+ let poll = paths
33
+ // Specific modelpath requested, start with root field only
34
+ ? [{
35
+ path: paths[0],
36
+ obj,
37
+ copy,
38
+ field: this.schema.fields[paths[0]],
39
+ depth: 0,
40
+ modelpath: { i: 0, asterisk_values: [] }
41
+ }]
42
+ // No modelpath, use all fields
43
+ : Object.entries(this.schema.fields).map(([path, field]) => ({
44
+ path, obj, copy, field, depth: 0
45
+ }));
33
46
  while (poll.length) {
34
47
  const next = [];
35
48
  for (const entry of poll) {
49
+ if (entry.is_union_option) {
50
+ const alreadyParsed = entry.copy[entry.path] !== undefined;
51
+ if (alreadyParsed)
52
+ continue;
53
+ }
36
54
  const val = entry.obj[entry.path];
37
55
  if (val === undefined) {
56
+ delete entry.copy[entry.path];
38
57
  continue;
39
58
  }
40
59
  if (val === null) {
41
- entry.copy[entry.path] = null;
60
+ delete entry.copy[entry.path];
42
61
  continue;
43
62
  }
63
+ const isLeafPath = entry.modelpath
64
+ ? entry.modelpath.i === (paths?.length ?? 0) - 1
65
+ : false;
66
+ const nextPath = (!entry.modelpath || isLeafPath)
67
+ ? undefined
68
+ : paths[entry.modelpath.i + 1];
69
+ const addChildrenToQueue = !entry.modelpath
70
+ || entry.modelpath.i >= (paths.length - 1)
71
+ || nextPath === '*';
44
72
  if (entry.field.type === 'list') {
45
73
  if (!Array.isArray(val))
46
74
  continue;
47
75
  entry.copy[entry.path] = [];
48
- next.push(...val.map((_, i) => ({
49
- path: i.toString(),
50
- obj: val,
51
- copy: entry.copy[entry.path],
52
- field: entry.field.children['#'],
53
- depth: entry.depth + 1
54
- })));
76
+ // Leaf path or no modelpath = add entire list to queue
77
+ if (addChildrenToQueue) {
78
+ next.push(...val.map((_, i) => ({
79
+ path: i.toString(),
80
+ obj: val,
81
+ copy: entry.copy[entry.path],
82
+ field: entry.field.children['#'],
83
+ depth: entry.depth + 1,
84
+ modelpath: entry.modelpath ? {
85
+ i: entry.modelpath.i + 1,
86
+ asterisk_values: [...entry.modelpath.asterisk_values, ...(nextPath === '*' ? [i.toString()] : [])]
87
+ } : undefined
88
+ })));
89
+ }
90
+ // Branch path = add next path to queue
91
+ else {
92
+ next.push({
93
+ path: nextPath,
94
+ obj: val,
95
+ copy: entry.copy[entry.path],
96
+ field: entry.field.children['#'],
97
+ depth: entry.depth + 1,
98
+ modelpath: entry.modelpath ? {
99
+ i: entry.modelpath.i + 1,
100
+ asterisk_values: entry.modelpath.asterisk_values
101
+ } : undefined
102
+ });
103
+ }
55
104
  }
56
105
  else if (entry.field.type === 'dict') {
57
106
  if (typeof val !== 'object' || Array.isArray(val))
58
107
  continue;
59
108
  entry.copy[entry.path] = {};
60
- next.push(...Object.keys(val).map((path) => ({
61
- path,
62
- obj: val,
63
- copy: entry.copy[entry.path],
64
- field: entry.field.children['#'],
65
- depth: entry.depth + 1
66
- })));
109
+ // Leaf path or no modelpath = add entire dict to queue
110
+ if (addChildrenToQueue) {
111
+ next.push(...Object.keys(val).map((path) => ({
112
+ path,
113
+ obj: val,
114
+ copy: entry.copy[entry.path],
115
+ field: entry.field.children['#'],
116
+ depth: entry.depth + 1,
117
+ modelpath: entry.modelpath ? {
118
+ i: entry.modelpath.i + 1,
119
+ asterisk_values: [...entry.modelpath.asterisk_values, ...(nextPath === '*' ? [path] : [])]
120
+ } : undefined
121
+ })));
122
+ }
123
+ // Branch path = add next path to queue
124
+ else {
125
+ next.push({
126
+ path: nextPath,
127
+ obj: val,
128
+ copy: entry.copy[entry.path],
129
+ field: entry.field.children['#'],
130
+ depth: entry.depth + 1,
131
+ modelpath: entry.modelpath ? {
132
+ i: entry.modelpath.i + 1,
133
+ asterisk_values: entry.modelpath.asterisk_values
134
+ } : undefined
135
+ });
136
+ }
67
137
  }
68
138
  else if (entry.field.type === 'obj') {
69
139
  if (typeof val !== 'object' || Array.isArray(val))
70
140
  continue;
141
+ // disambiguate between dict and obj when obj comes first
142
+ // on a union
143
+ const objectHasValidFields = Object.keys(val).some(key => key in entry.field.children);
144
+ if (!objectHasValidFields)
145
+ continue;
71
146
  entry.copy[entry.path] = {};
72
- next.push(...Object.keys(entry.field.children).map(path => ({
73
- path: path,
74
- obj: val,
75
- copy: entry.copy[entry.path],
76
- field: entry.field.children[path],
77
- depth: entry.depth + 1
78
- })));
147
+ // Leaf path or no modelpath = add entire dict to queue
148
+ if (addChildrenToQueue) {
149
+ next.push(...Object.keys(entry.field.children).map(path => ({
150
+ path: path,
151
+ obj: val,
152
+ copy: entry.copy[entry.path],
153
+ field: entry.field.children[path],
154
+ depth: entry.depth + 1,
155
+ modelpath: entry.modelpath ? {
156
+ i: entry.modelpath.i + 1,
157
+ asterisk_values: [...entry.modelpath.asterisk_values, ...(nextPath === '*' ? [path] : [])]
158
+ } : undefined
159
+ })));
160
+ }
161
+ // Branch path = add next path to queue
162
+ else {
163
+ next.push({
164
+ path: nextPath,
165
+ obj: val,
166
+ copy: entry.copy[entry.path],
167
+ field: entry.field.children[nextPath],
168
+ depth: entry.depth + 1,
169
+ modelpath: entry.modelpath ? {
170
+ i: entry.modelpath.i + 1,
171
+ asterisk_values: entry.modelpath.asterisk_values
172
+ } : undefined
173
+ });
174
+ }
79
175
  }
80
176
  else if (entry.field.type === 'union') {
81
- // TODO: ??????????
82
- entry.copy[entry.path] = entry.obj[entry.path];
177
+ const options = Object.entries(entry.field.children);
178
+ // Add union options to queue with flag
179
+ // This flag avoids reparsing a value if an earlier option already did it
180
+ next.push(...options.map(([_, option], i) => ({
181
+ ...entry,
182
+ field: option,
183
+ is_union_option: i === options.length - 1 ? 'last' : true
184
+ })));
185
+ continue;
83
186
  }
84
187
  else if (entry.field.type === 'enum') {
85
188
  const v = entry.obj[entry.path];
86
189
  const options = entry.field.meta.enum.options;
87
190
  if (!(v in options)) {
88
- throw error_1.NesoiError.Bucket.Model.InvalidEnum({ bucket: this.alias, value: v, options: Object.keys(options) });
191
+ if (entry.is_union_option !== true) {
192
+ throw error_1.NesoiError.Bucket.Model.InvalidEnum({ bucket: this.alias, value: v, options: Object.keys(options) });
193
+ }
89
194
  }
90
195
  entry.copy[entry.path] = entry.obj[entry.path];
91
196
  }
@@ -97,7 +202,10 @@ class BucketModel {
97
202
  entry.copy[entry.path] = date_1.NesoiDate.fromISO(v);
98
203
  }
99
204
  catch {
100
- throw error_1.NesoiError.Bucket.Model.InvalidISODate({ bucket: this.alias, value: v });
205
+ if (entry.is_union_option !== true) {
206
+ throw error_1.NesoiError.Bucket.Model.InvalidISODate({ bucket: this.alias, value: v });
207
+ }
208
+ continue;
101
209
  }
102
210
  }
103
211
  else if (op === 'save') {
@@ -105,7 +213,10 @@ class BucketModel {
105
213
  entry.copy[entry.path] = v.toISO();
106
214
  }
107
215
  catch {
108
- throw error_1.NesoiError.Bucket.Model.InvalidNesoiDate({ bucket: this.alias, value: v });
216
+ if (entry.is_union_option !== true) {
217
+ throw error_1.NesoiError.Bucket.Model.InvalidNesoiDate({ bucket: this.alias, value: v });
218
+ }
219
+ continue;
109
220
  }
110
221
  }
111
222
  }
@@ -121,7 +232,10 @@ class BucketModel {
121
232
  entry.copy[entry.path] = datetime_1.NesoiDatetime.fromISO(v);
122
233
  }
123
234
  catch {
124
- throw error_1.NesoiError.Bucket.Model.InvalidISODatetime({ bucket: this.alias, value: v });
235
+ if (entry.is_union_option !== true) {
236
+ throw error_1.NesoiError.Bucket.Model.InvalidISODatetime({ bucket: this.alias, value: v });
237
+ }
238
+ continue;
125
239
  }
126
240
  }
127
241
  else if (op === 'save') {
@@ -129,7 +243,10 @@ class BucketModel {
129
243
  entry.copy[entry.path] = v.toISO();
130
244
  }
131
245
  catch {
132
- throw error_1.NesoiError.Bucket.Model.InvalidNesoiDatetime({ bucket: this.alias, value: v });
246
+ if (entry.is_union_option !== true) {
247
+ throw error_1.NesoiError.Bucket.Model.InvalidNesoiDatetime({ bucket: this.alias, value: v });
248
+ }
249
+ continue;
133
250
  }
134
251
  }
135
252
  }
@@ -141,30 +258,60 @@ class BucketModel {
141
258
  const meta = entry.field.meta.decimal;
142
259
  const v = entry.obj[entry.path];
143
260
  if (serialize?.(entry)) {
261
+ if ((op === 'load' && typeof v !== 'string') || (op === 'save' && !(v instanceof decimal_1.NesoiDecimal))) {
262
+ if (entry.is_union_option !== true) {
263
+ throw error_1.NesoiError.Bucket.Model.InvalidNesoiDecimal({ bucket: this.alias, value: v });
264
+ }
265
+ continue;
266
+ }
144
267
  if (op === 'load')
145
268
  entry.copy[entry.path] = new decimal_1.NesoiDecimal(v, meta?.left, meta?.right);
146
269
  if (op === 'save')
147
270
  entry.copy[entry.path] = v.toString();
148
271
  }
149
272
  else {
273
+ if (!(v instanceof decimal_1.NesoiDecimal)) {
274
+ if (entry.is_union_option !== true) {
275
+ throw error_1.NesoiError.Bucket.Model.InvalidNesoiDecimal({ bucket: this.alias, value: v });
276
+ }
277
+ continue;
278
+ }
150
279
  entry.copy[entry.path] = v;
151
280
  }
152
281
  }
153
282
  else if (entry.field.type === 'duration') {
154
283
  const v = entry.obj[entry.path];
155
284
  if (serialize?.(entry)) {
285
+ if ((op === 'load' && typeof v !== 'string') || (op === 'save' && !(v instanceof duration_1.NesoiDuration))) {
286
+ if (entry.is_union_option !== true) {
287
+ throw error_1.NesoiError.Bucket.Model.InvalidNesoiDuration({ bucket: this.alias, value: v });
288
+ }
289
+ continue;
290
+ }
156
291
  if (op === 'load')
157
292
  entry.copy[entry.path] = duration_1.NesoiDuration.fromString(v);
158
293
  if (op === 'save')
159
294
  entry.copy[entry.path] = v.toString();
160
295
  }
161
296
  else {
297
+ if (!(v instanceof duration_1.NesoiDuration)) {
298
+ if (entry.is_union_option !== true) {
299
+ throw error_1.NesoiError.Bucket.Model.InvalidNesoiDuration({ bucket: this.alias, value: v });
300
+ }
301
+ continue;
302
+ }
162
303
  entry.copy[entry.path] = v;
163
304
  }
164
305
  }
165
306
  else {
166
307
  entry.copy[entry.path] = entry.obj[entry.path];
167
308
  }
309
+ if (isLeafPath) {
310
+ modelpath_results.push({
311
+ value: entry.copy[entry.path],
312
+ index: entry.modelpath.asterisk_values
313
+ });
314
+ }
168
315
  }
169
316
  poll = next;
170
317
  }
@@ -184,6 +331,8 @@ class BucketModel {
184
331
  copy[meta.created_at] = obj[meta.created_at];
185
332
  copy[meta.updated_at] = obj[meta.updated_at];
186
333
  }
334
+ if (modelpath)
335
+ return modelpath_results;
187
336
  return copy;
188
337
  }
189
338
  }
@@ -69,7 +69,7 @@ export declare class $BucketModel {
69
69
  constructor(fields: $BucketModelFields & {
70
70
  id: $BucketModelField;
71
71
  }, defaults?: Record<string, any>, hasFileField?: boolean, hasEncryptedField?: boolean);
72
- static getField(model: $BucketModel, modelpath: string): $BucketModelField[];
72
+ static getFields(model: $BucketModel, modelpath: string): $BucketModelField[];
73
73
  static fieldsOfType(model: $BucketModel, type: $BucketModelFieldType): $BucketModelField[];
74
74
  static forEachField(model: $BucketModel, predicate: (field: $BucketModelField, path: string) => Promise<void>): Promise<void>;
75
75
  static getModelpaths(model: $BucketModel): Record<string, $BucketModelField[]>;
@@ -45,7 +45,7 @@ class $BucketModel {
45
45
  this.hasFileField = hasFileField;
46
46
  this.hasEncryptedField = hasEncryptedField;
47
47
  }
48
- static getField(model, modelpath) {
48
+ static getFields(model, modelpath) {
49
49
  const paths = modelpath.split('.');
50
50
  const results = [];
51
51
  let poll = [{ i: 0, field: { children: model.fields } }];
@@ -66,7 +66,7 @@ class $BucketModel {
66
66
  })));
67
67
  continue;
68
68
  }
69
- // If it's a list or dict, or an object
69
+ // If it's a list or dict, add the index field
70
70
  if (field.type === 'list' || field.type === 'dict') {
71
71
  next.push({
72
72
  i: item.i + 1,
@@ -53,6 +53,7 @@ export type NQL_Part = {
53
53
  many: boolean;
54
54
  union: NQL_Union;
55
55
  parent?: NQL_Part;
56
+ select?: string;
56
57
  };
57
58
  export type NQL_Node = NQL_Union | NQL_Intersection | NQL_Rule;
58
59
  /**
@@ -142,7 +142,7 @@ class NQL_RuleTree {
142
142
  }
143
143
  const is_metadata_field = Object.values(meta.meta).includes(key);
144
144
  if (!is_metadata_field) {
145
- const fields = bucket_model_schema_1.$BucketModel.getField(meta.schema.model, key);
145
+ const fields = bucket_model_schema_1.$BucketModel.getFields(meta.schema.model, key);
146
146
  if (!fields.length) {
147
147
  throw new Error(`Field '${key}' not found on bucket '${meta.schema.name}'`);
148
148
  }
@@ -191,7 +191,7 @@ class NQL_RuleTree {
191
191
  const _op = this.parseOp([{ type: 'datetime', name: fieldpath }], op);
192
192
  return { type: 'fieldpath', or: !!or, fieldpath, not: !!not, case_i: !!case_i, op: _op };
193
193
  }
194
- const fields = bucket_model_schema_1.$BucketModel.getField(meta.schema.model, fieldpath);
194
+ const fields = bucket_model_schema_1.$BucketModel.getFields(meta.schema.model, fieldpath);
195
195
  if (!fields.length) {
196
196
  throw new Error(`Field '${fieldpath}' not found on bucket '${meta.schema.name}'`);
197
197
  }
@@ -300,7 +300,7 @@ class NQL_RuleTree {
300
300
  throw new Error(`Bucket '${subMeta}' not found on module`);
301
301
  }
302
302
  subMeta.scope = this.customBuckets[tag.short]?.scope || subMeta.scope;
303
- const field = bucket_model_schema_1.$BucketModel.getField(subMeta.schema.model, fieldpath);
303
+ const field = bucket_model_schema_1.$BucketModel.getFields(subMeta.schema.model, fieldpath);
304
304
  if (!field) {
305
305
  throw new Error(`Field '${fieldpath}' not found on bucket '${subMeta.schema.name}'`);
306
306
  }
@@ -617,7 +617,8 @@ class NQL_Compiler {
617
617
  ...union,
618
618
  inters: Array.from(Array(union.inters.length), _ => ({}))
619
619
  },
620
- parent: partStack.at(-1)
620
+ parent: partStack.at(-1),
621
+ select: rule.value.subquery.select
621
622
  };
622
623
  // debugLog.push('+ [part] ' + newPart.i);
623
624
  parts.push(newPart);
@@ -26,8 +26,6 @@ class NQL_Engine {
26
26
  async run(trx, query, pagination, params = [{}], param_templates = [], view, customBuckets) {
27
27
  if (!params.length)
28
28
  params = [{}];
29
- if (!param_templates.length)
30
- param_templates = [{}];
31
29
  let result = {
32
30
  data: []
33
31
  };