nesoi 3.0.0 → 3.0.3

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 (185) hide show
  1. package/README.md +10 -0
  2. package/lib/adapters/postgres/src/migrator/bucket.d.ts +20 -0
  3. package/lib/adapters/postgres/src/migrator/bucket.js +184 -0
  4. package/lib/adapters/postgres/src/migrator/csv.d.ts +7 -0
  5. package/lib/adapters/postgres/src/migrator/csv.js +72 -0
  6. package/lib/adapters/postgres/src/migrator/migration.d.ts +2 -18
  7. package/lib/adapters/postgres/src/migrator/migration.js +10 -158
  8. package/lib/adapters/postgres/src/migrator/migrator.js +8 -5
  9. package/lib/adapters/postgres/src/migrator/runner.d.ts +16 -6
  10. package/lib/adapters/postgres/src/migrator/runner.js +103 -34
  11. package/lib/adapters/postgres/src/postgres.bucket_adapter.d.ts +19 -22
  12. package/lib/adapters/postgres/src/postgres.bucket_adapter.js +116 -100
  13. package/lib/adapters/postgres/src/postgres.cli.d.ts +23 -3
  14. package/lib/adapters/postgres/src/postgres.cli.js +70 -10
  15. package/lib/adapters/postgres/src/postgres.config.d.ts +5 -0
  16. package/lib/adapters/postgres/src/postgres.config.js +2 -0
  17. package/lib/adapters/postgres/src/postgres.nql.d.ts +7 -3
  18. package/lib/adapters/postgres/src/postgres.nql.js +86 -32
  19. package/lib/adapters/postgres/src/postgres.provider.d.ts +18 -0
  20. package/lib/adapters/postgres/src/postgres.provider.js +77 -0
  21. package/lib/adapters/postgres/test/postgres.bucket_adapter.test.js +76 -39
  22. package/lib/compiler/apps/monolyth/monolyth_compiler.d.ts +3 -0
  23. package/lib/compiler/apps/monolyth/monolyth_compiler.js +24 -0
  24. package/lib/compiler/apps/monolyth/stages/2_build_typescript_stage.js +2 -1
  25. package/lib/compiler/apps/monolyth/stages/5_dump_cli_stage.js +1 -1
  26. package/lib/compiler/elements/bucket.element.js +26 -11
  27. package/lib/compiler/elements/constants.element.js +1 -1
  28. package/lib/compiler/elements/element.d.ts +2 -0
  29. package/lib/compiler/elements/message.element.js +4 -4
  30. package/lib/compiler/helpers/dump_helpers.js +5 -2
  31. package/lib/compiler/stages/7_dump_stage.js +2 -0
  32. package/lib/compiler/treeshake.js +9 -37
  33. package/lib/compiler/typescript/bridge/extract.js +12 -0
  34. package/lib/compiler/typescript/bridge/inject.js +3 -0
  35. package/lib/compiler/typescript/bridge/organize.js +3 -3
  36. package/lib/elements/blocks/block.builder.js +4 -2
  37. package/lib/elements/blocks/job/internal/resource_job.builder.d.ts +22 -20
  38. package/lib/elements/blocks/job/internal/resource_job.d.ts +2 -1
  39. package/lib/elements/blocks/job/internal/resource_job.js +17 -4
  40. package/lib/elements/blocks/job/job.js +3 -0
  41. package/lib/elements/blocks/job/job.types.d.ts +7 -0
  42. package/lib/elements/blocks/job/job.types.js +2 -0
  43. package/lib/elements/blocks/machine/machine.js +3 -2
  44. package/lib/elements/blocks/resource/resource.builder.js +2 -4
  45. package/lib/elements/blocks/resource/resource.d.ts +5 -3
  46. package/lib/elements/blocks/resource/resource.js +26 -17
  47. package/lib/elements/edge/controller/adapters/controller_adapter.d.ts +2 -1
  48. package/lib/elements/edge/controller/adapters/controller_adapter.js +11 -2
  49. package/lib/elements/edge/controller/controller.builder.d.ts +4 -5
  50. package/lib/elements/edge/controller/controller.builder.js +7 -7
  51. package/lib/elements/edge/controller/controller.d.ts +2 -1
  52. package/lib/elements/edge/controller/controller.js +8 -6
  53. package/lib/elements/entities/bucket/adapters/bucket_adapter.d.ts +61 -23
  54. package/lib/elements/entities/bucket/adapters/bucket_adapter.js +22 -13
  55. package/lib/elements/entities/bucket/adapters/memory.bucket_adapter.d.ts +21 -22
  56. package/lib/elements/entities/bucket/adapters/memory.bucket_adapter.js +68 -2
  57. package/lib/elements/entities/bucket/adapters/memory.nql.d.ts +10 -6
  58. package/lib/elements/entities/bucket/adapters/memory.nql.js +38 -3
  59. package/lib/elements/entities/bucket/adapters/slow_memory.bucket_adapter.d.ts +0 -20
  60. package/lib/elements/entities/bucket/adapters/slow_memory.bucket_adapter.js +46 -30
  61. package/lib/elements/entities/bucket/bucket.builder.d.ts +8 -2
  62. package/lib/elements/entities/bucket/bucket.builder.js +13 -19
  63. package/lib/elements/entities/bucket/bucket.config.d.ts +5 -1
  64. package/lib/elements/entities/bucket/bucket.d.ts +180 -19
  65. package/lib/elements/entities/bucket/bucket.js +658 -48
  66. package/lib/elements/entities/bucket/bucket.schema.d.ts +7 -1
  67. package/lib/elements/entities/bucket/bucket.schema.js +2 -1
  68. package/lib/elements/entities/bucket/bucket.types.d.ts +2 -7
  69. package/lib/elements/entities/bucket/cache/bucket_cache.d.ts +6 -2
  70. package/lib/elements/entities/bucket/cache/bucket_cache.js +12 -12
  71. package/lib/elements/entities/bucket/graph/bucket_graph.d.ts +32 -5
  72. package/lib/elements/entities/bucket/graph/bucket_graph.js +80 -111
  73. package/lib/elements/entities/bucket/graph/bucket_graph.schema.d.ts +3 -6
  74. package/lib/elements/entities/bucket/graph/bucket_graph.schema.js +1 -4
  75. package/lib/elements/entities/bucket/graph/bucket_graph_link.builder.d.ts +3 -7
  76. package/lib/elements/entities/bucket/graph/bucket_graph_link.builder.js +6 -2
  77. package/lib/elements/entities/bucket/model/bucket_model.builder.js +1 -1
  78. package/lib/elements/entities/bucket/model/bucket_model.convert.js +3 -3
  79. package/lib/elements/entities/bucket/model/bucket_model.schema.d.ts +37 -8
  80. package/lib/elements/entities/bucket/model/bucket_model.schema.js +25 -4
  81. package/lib/elements/entities/bucket/model/bucket_model_field.builder.d.ts +33 -14
  82. package/lib/elements/entities/bucket/model/bucket_model_field.builder.js +56 -13
  83. package/lib/elements/entities/bucket/query/nql.schema.d.ts +1 -0
  84. package/lib/elements/entities/bucket/query/nql_compiler.js +13 -2
  85. package/lib/elements/entities/bucket/query/nql_engine.d.ts +11 -4
  86. package/lib/elements/entities/bucket/query/nql_engine.js +20 -11
  87. package/lib/elements/entities/bucket/view/bucket_view.js +63 -35
  88. package/lib/elements/entities/bucket/view/bucket_view.schema.d.ts +5 -2
  89. package/lib/elements/entities/bucket/view/bucket_view_field.builder.d.ts +6 -2
  90. package/lib/elements/entities/bucket/view/bucket_view_field.builder.js +21 -15
  91. package/lib/elements/entities/constants/constants.schema.d.ts +1 -1
  92. package/lib/elements/entities/drive/drive_adapter.d.ts +36 -0
  93. package/lib/elements/entities/drive/drive_adapter.js +10 -0
  94. package/lib/elements/entities/drive/local.drive_adapter.d.ts +8 -0
  95. package/lib/elements/entities/drive/local.drive_adapter.js +28 -0
  96. package/lib/elements/entities/message/message.schema.d.ts +1 -0
  97. package/lib/elements/entities/message/message.schema.js +33 -0
  98. package/lib/elements/entities/message/message_parser.d.ts +5 -1
  99. package/lib/elements/entities/message/message_parser.js +56 -35
  100. package/lib/elements/entities/message/template/message_template.schema.d.ts +10 -8
  101. package/lib/elements/entities/message/template/message_template_field.builder.d.ts +16 -6
  102. package/lib/elements/entities/message/template/message_template_field.builder.js +25 -0
  103. package/lib/elements/entities/message/template/message_template_parser.js +2 -1
  104. package/lib/engine/apps/app.config.d.ts +32 -11
  105. package/lib/engine/apps/app.config.js +12 -0
  106. package/lib/engine/apps/app.d.ts +2 -0
  107. package/lib/engine/apps/app.js +3 -0
  108. package/lib/engine/apps/inline.app.d.ts +5 -3
  109. package/lib/engine/apps/inline.app.js +27 -12
  110. package/lib/engine/apps/monolyth/monolyth.app.d.ts +4 -2
  111. package/lib/engine/apps/monolyth/monolyth.app.js +22 -10
  112. package/lib/engine/auth/authn.d.ts +5 -1
  113. package/lib/engine/auth/zero.authn_provider.d.ts +4 -2
  114. package/lib/engine/auth/zero.authn_provider.js +2 -2
  115. package/lib/engine/cli/cli.d.ts +3 -1
  116. package/lib/engine/cli/cli.js +22 -3
  117. package/lib/engine/cli/cli_adapter.d.ts +2 -1
  118. package/lib/engine/cli/cli_adapter.js +2 -1
  119. package/lib/engine/cli/cli_input.d.ts +19 -0
  120. package/lib/engine/cli/cli_input.js +207 -0
  121. package/lib/engine/cli/ui.d.ts +1 -1
  122. package/lib/engine/cli/ui.js +2 -2
  123. package/lib/engine/daemon.d.ts +3 -2
  124. package/lib/engine/daemon.js +14 -2
  125. package/lib/engine/data/date.js +2 -2
  126. package/lib/engine/data/datetime.d.ts +40 -4
  127. package/lib/engine/data/datetime.js +70 -11
  128. package/lib/engine/data/decimal.d.ts +1 -1
  129. package/lib/engine/data/decimal.js +3 -3
  130. package/lib/engine/data/error.d.ts +21 -4
  131. package/lib/engine/data/error.js +23 -7
  132. package/lib/engine/data/file.d.ts +23 -0
  133. package/lib/engine/data/file.js +53 -0
  134. package/lib/engine/data/json.d.ts +6 -0
  135. package/lib/engine/data/json.js +26 -0
  136. package/lib/engine/data/obj.d.ts +1 -1
  137. package/lib/engine/data/trash.d.ts +14 -0
  138. package/lib/engine/data/trash.js +2 -0
  139. package/lib/engine/data/tree.d.ts +7 -12
  140. package/lib/engine/data/tree.js +101 -49
  141. package/lib/engine/module.d.ts +2 -1
  142. package/lib/engine/module.js +2 -5
  143. package/lib/engine/space.d.ts +1 -0
  144. package/lib/engine/space.js +6 -0
  145. package/lib/engine/transaction/nodes/bucket.trx_node.d.ts +156 -24
  146. package/lib/engine/transaction/nodes/bucket.trx_node.js +297 -467
  147. package/lib/engine/transaction/nodes/bucket_query.trx_node.d.ts +4 -2
  148. package/lib/engine/transaction/nodes/bucket_query.trx_node.js +27 -15
  149. package/lib/engine/transaction/nodes/job.trx_node.d.ts +2 -1
  150. package/lib/engine/transaction/nodes/job.trx_node.js +6 -0
  151. package/lib/engine/transaction/trx.d.ts +5 -2
  152. package/lib/engine/transaction/trx.js +2 -2
  153. package/lib/engine/transaction/trx_engine.config.d.ts +1 -3
  154. package/lib/engine/transaction/trx_engine.d.ts +2 -2
  155. package/lib/engine/transaction/trx_engine.js +14 -11
  156. package/lib/engine/transaction/trx_node.d.ts +14 -4
  157. package/lib/engine/transaction/trx_node.js +50 -8
  158. package/lib/engine/tree.d.ts +1 -1
  159. package/lib/engine/util/crypto.d.ts +50 -0
  160. package/lib/engine/util/crypto.js +89 -0
  161. package/lib/engine/util/deep.d.ts +5 -0
  162. package/lib/engine/util/deep.js +46 -0
  163. package/lib/engine/util/dotenv.d.ts +2 -8
  164. package/lib/engine/util/dotenv.js +14 -36
  165. package/lib/engine/util/hash.d.ts +3 -0
  166. package/lib/engine/util/hash.js +23 -0
  167. package/lib/engine/util/log.js +1 -1
  168. package/lib/engine/util/mime.d.ts +10 -0
  169. package/lib/engine/util/mime.js +389 -0
  170. package/lib/engine/util/parse.d.ts +6 -5
  171. package/lib/engine/util/parse.js +16 -15
  172. package/lib/engine/util/path.d.ts +3 -0
  173. package/lib/engine/util/path.js +92 -0
  174. package/lib/engine/util/rules.d.ts +4 -0
  175. package/lib/engine/util/rules.js +12 -0
  176. package/package.json +1 -1
  177. package/tools/compile.js +2 -2
  178. package/tools/dotenv.d.ts +1 -0
  179. package/tools/dotenv.js +4 -0
  180. package/tools/joaquin/job.d.ts +5 -5
  181. package/tools/joaquin/mock.d.ts +23 -2
  182. package/tools/joaquin/mock.js +127 -21
  183. package/tsconfig.build.tsbuildinfo +1 -1
  184. package/lib/adapters/postgres/test/postgres.bucket_query.test.d.ts +0 -0
  185. package/lib/adapters/postgres/test/postgres.bucket_query.test.js +0 -136
@@ -1,12 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Bucket = void 0;
3
+ exports.Bucket = exports.$id = void 0;
4
+ const trx_node_1 = require("../../../engine/transaction/trx_node");
4
5
  const error_1 = require("../../../engine/data/error");
5
6
  const bucket_view_1 = require("./view/bucket_view");
6
7
  const memory_bucket_adapter_1 = require("./adapters/memory.bucket_adapter");
7
8
  const bucket_cache_1 = require("./cache/bucket_cache");
8
9
  const log_1 = require("../../../engine/util/log");
9
10
  const bucket_graph_1 = require("./graph/bucket_graph");
11
+ const datetime_1 = require("../../../engine/data/datetime");
12
+ const tree_1 = require("../../../engine/data/tree");
13
+ const crypto_1 = require("../../../engine/util/crypto");
14
+ const bucket_model_schema_1 = require("./model/bucket_model.schema");
15
+ /**
16
+ * **This should only be used inside a `#composition` of a bucket `create`** to refer to the parent id, which doesn't exist yet.
17
+ *
18
+ * This property has no useful value outside the engine. If you try to `console.log` it, you'll find a Symbol.
19
+ * It's replaced by the bucket after creating the parent, before creating the composition.
20
+ */
21
+ exports.$id = Symbol('FUTURE ID OF CREATE');
10
22
  class Bucket {
11
23
  constructor(schema, config, providers = {}) {
12
24
  this.schema = schema;
@@ -26,100 +38,698 @@ class Bucket {
26
38
  if (this.config?.cache) {
27
39
  this.cache = new bucket_cache_1.BucketCache(this.schema.name, this.adapter, this.config.cache);
28
40
  }
41
+ // Drive
42
+ if (this.config?.drive) {
43
+ this.drive = this.config.drive(schema, providers);
44
+ }
29
45
  }
30
- // Get
31
- async readOne(trx, id) {
46
+ // Getters
47
+ getQueryMeta() {
48
+ return {
49
+ ...this.adapter.getQueryMeta(),
50
+ bucket: this.schema
51
+ };
52
+ }
53
+ /* CRUD */
54
+ /**
55
+ * Read one raw entity by `id`
56
+ *
57
+ * - Options:
58
+ * - `silent`: If not found, return `undefined` instead of throwing an exception
59
+ * - `no_tenancy`: Don't apply tenancy rules.
60
+ */
61
+ async readOne(trx, id, options) {
62
+ log_1.Log.debug('bucket', this.schema.name, `Read id=${id}`);
63
+ // Validate ID
32
64
  if (typeof id !== 'string' && typeof id !== 'number') {
33
65
  throw error_1.NesoiError.Bucket.InvalidId({ bucket: this.schema.alias, id });
34
66
  }
35
- log_1.Log.debug('bucket', this.schema.name, `Get id=${id}`);
36
- const raw = this.cache
37
- ? await this.cache.get(trx, id)
38
- : await this.adapter.get(trx, id);
39
- if (!raw)
40
- return undefined;
67
+ // Make tenancy query
68
+ const tenancy = (options?.no_tenancy)
69
+ ? undefined
70
+ : this.getTenancyQuery(trx);
71
+ let raw;
72
+ // With Tenancy
73
+ if (tenancy) {
74
+ const result = await this.adapter.query(trx, {
75
+ id,
76
+ '#and': tenancy
77
+ }, { perPage: 1 });
78
+ raw = result.data[0];
79
+ }
80
+ // Without Tenancy
81
+ else {
82
+ raw = this.cache
83
+ ? await this.cache.get(trx, id)
84
+ : await this.adapter.get(trx, id);
85
+ }
86
+ // Empty result
87
+ if (!raw) {
88
+ if (options?.silent)
89
+ return undefined;
90
+ else
91
+ throw error_1.NesoiError.Bucket.ObjNotFound({ bucket: this.schema.alias, id: id });
92
+ }
93
+ // Encryption
94
+ if (this.schema.model.hasEncryptedField) {
95
+ this.decrypt(trx, raw);
96
+ }
41
97
  return raw;
42
98
  }
43
- async readAll(trx, pagination, order) {
44
- log_1.Log.debug('bucket', this.schema.name, 'Index');
45
- const raws = this.cache
46
- ? await this.cache.index(trx)
47
- : await this.adapter.index(trx);
99
+ /**
100
+ * Read all raw entities
101
+ *
102
+ * - Options:
103
+ * - `no_tenancy`: Don't apply tenancy rules.
104
+ */
105
+ async readAll(trx, options) {
106
+ log_1.Log.debug('bucket', this.schema.name, 'Read All');
107
+ // Make tenancy query
108
+ const tenancy = (options?.no_tenancy)
109
+ ? undefined
110
+ : this.getTenancyQuery(trx);
111
+ let raws;
112
+ // With Tenancy
113
+ if (tenancy) {
114
+ const result = await this.adapter.query(trx, tenancy);
115
+ raws = result.data;
116
+ }
117
+ // Without Tenancy
118
+ else {
119
+ raws = this.cache
120
+ ? await this.cache.index(trx)
121
+ : await this.adapter.index(trx);
122
+ }
123
+ // Encryption
124
+ if (this.schema.model.hasEncryptedField) {
125
+ for (const raw of raws) {
126
+ this.decrypt(trx, raw);
127
+ }
128
+ }
48
129
  return raws;
49
130
  }
50
- async viewOne(trx, id, view) {
131
+ /**
132
+ * Read an entity's view by `id`
133
+ *
134
+ * - Options:
135
+ * - `silent`: If not found, return `undefined` instead of throwing an exception
136
+ * - `no_tenancy`: Don't apply tenancy rules.
137
+ */
138
+ async viewOne(trx, id, view, options) {
139
+ log_1.Log.debug('bucket', this.schema.name, `View id=${id}, v=${view}`);
140
+ // Read
141
+ const raw = await this.readOne(trx, id, options);
142
+ if (!raw) {
143
+ return;
144
+ }
145
+ // Build
146
+ return this.buildOne(trx, raw, view);
147
+ }
148
+ /**
149
+ * Read a view of all entities
150
+ *
151
+ * - Options:
152
+ * - `no_tenancy`: Don't apply tenancy rules.
153
+ */
154
+ async viewAll(trx, view, options) {
155
+ log_1.Log.debug('bucket', this.schema.name, `View all, v=${view}`);
156
+ // Read
157
+ const raws = await this.readAll(trx, options);
158
+ // Build
159
+ return this.buildMany(trx, raws, view);
160
+ }
161
+ // Graph
162
+ /**
163
+ * Read raw entity of a graph link
164
+ *
165
+ * - Options:
166
+ * - `silent`: If not found, return `undefined` instead of throwing an exception
167
+ * - `no_tenancy`: Don't apply tenancy rules.
168
+ */
169
+ async readLink(trx, id, link, options) {
170
+ log_1.Log.debug('bucket', this.schema.name, `Read Link, id=${id} l=${link}`);
171
+ // Validate ID
51
172
  if (typeof id !== 'string' && typeof id !== 'number') {
52
173
  throw error_1.NesoiError.Bucket.InvalidId({ bucket: this.schema.alias, id });
53
174
  }
54
- log_1.Log.debug('bucket', this.schema.name, `View id=${id}, v=${view}`);
175
+ // Read object
55
176
  const obj = await this.readOne(trx, id);
177
+ // Empty response
56
178
  if (!obj) {
57
- return;
179
+ const schema = this.schema.graph.links[link];
180
+ if (schema.many) {
181
+ return [];
182
+ }
183
+ return undefined;
58
184
  }
59
- return this.buildOne(trx, obj, view);
185
+ // Read link
186
+ const linkObj = await this.graph.readLink(trx, obj, link, options);
187
+ // Encryption
188
+ if (linkObj) {
189
+ if (this.schema.model.hasEncryptedField) {
190
+ this.decrypt(trx, linkObj);
191
+ }
192
+ }
193
+ return linkObj;
60
194
  }
61
- async viewAll(trx, view, pagination, order) {
62
- log_1.Log.debug('bucket', this.schema.name, `View all, v=${view}`);
63
- const objs = await this.readAll(trx);
64
- return this.buildAll(trx, objs, view);
195
+ /**
196
+ * Read the view of an entity of a graph link
197
+ *
198
+ * - Options:
199
+ * - `silent`: If not found, return `undefined` instead of throwing an exception
200
+ * - `no_tenancy`: Don't apply tenancy rules.
201
+ */
202
+ async viewLink(trx, id, link, view, options) {
203
+ log_1.Log.debug('bucket', this.schema.name, `View Link, id=${id} l=${link}`);
204
+ // Validate ID
205
+ if (typeof id !== 'string' && typeof id !== 'number') {
206
+ throw error_1.NesoiError.Bucket.InvalidId({ bucket: this.schema.alias, id });
207
+ }
208
+ // Read object
209
+ const obj = await this.readOne(trx, id);
210
+ // Empty response
211
+ if (!obj) {
212
+ const schema = this.schema.graph.links[link];
213
+ if (schema.many) {
214
+ return [];
215
+ }
216
+ return undefined;
217
+ }
218
+ // View link
219
+ const linkObj = await this.graph.viewLink(trx, obj, link, view, options);
220
+ // Encryption
221
+ if (linkObj) {
222
+ if (this.schema.model.hasEncryptedField) {
223
+ this.decrypt(trx, linkObj);
224
+ }
225
+ }
226
+ return linkObj;
65
227
  }
228
+ /**
229
+ * Return true if the graph link refers to at least one object
230
+ *
231
+ * - Options:
232
+ * - `no_tenancy`: Don't apply tenancy rules.
233
+ */
234
+ async hasLink(trx, id, link, options) {
235
+ log_1.Log.debug('bucket', this.schema.name, `Has Link, id=${id} l=${link}`);
236
+ // Read Object
237
+ const obj = await this.readOne(trx, id);
238
+ if (!obj) {
239
+ return undefined;
240
+ }
241
+ // Check Link
242
+ return this.graph.hasLink(trx, link, obj, options);
243
+ }
244
+ // Build
245
+ /**
246
+ * Build one object with a view
247
+ */
66
248
  async buildOne(trx, obj, view) {
67
249
  if (!(view in this.views)) {
68
250
  throw error_1.NesoiError.Bucket.ViewNotFound({ bucket: this.schema.alias, view: view });
69
251
  }
70
252
  return this.views[view].parse(trx, obj);
71
253
  }
72
- async buildAll(trx, objs, view) {
254
+ /**
255
+ * Build a list ob objects with a view
256
+ */
257
+ async buildMany(trx, objs, view) {
73
258
  return Promise.all(objs.map(obj => this.buildOne(trx, obj, view)));
74
259
  }
75
- // Put
260
+ // Create
261
+ /**
262
+ * Create an entity
263
+ */
264
+ async create(trx, obj) {
265
+ var _a, _b;
266
+ log_1.Log.debug('bucket', this.schema.name, `Create id=${obj['id'] || 'new'}`, obj);
267
+ // Separate composition
268
+ let composition = obj['#composition'];
269
+ delete obj['#composition'];
270
+ // Add meta (created_by/created_at/updated_by/updated_at)
271
+ this.addMeta(trx, obj, 'create');
272
+ // Encryption
273
+ if (this.schema.model.hasEncryptedField) {
274
+ this.encrypt(trx, obj);
275
+ }
276
+ // Drive
277
+ if (this.schema.model.hasFileField) {
278
+ await this.copyFilesToDrive(obj);
279
+ }
280
+ // Create
281
+ const input = Object.assign({}, this.schema.model.defaults, obj);
282
+ const _obj = await this.adapter.create(trx, input);
283
+ // Composition
284
+ if (composition) {
285
+ this.replaceFutureId(composition, _obj.id);
286
+ }
287
+ else {
288
+ composition = {};
289
+ }
290
+ // Create composition
291
+ for (const link of Object.values(this.schema.graph.links)) {
292
+ if (link.rel !== 'composition')
293
+ continue;
294
+ const linkObj = composition[link.name];
295
+ if (!linkObj) {
296
+ throw error_1.NesoiError.Bucket.MissingComposition({ method: 'create', bucket: this.schema.name, link: link.name });
297
+ }
298
+ if (link.many) {
299
+ if (!Array.isArray(linkObj)) {
300
+ throw error_1.NesoiError.Bucket.CompositionValueShouldBeArray({ method: 'create', bucket: this.schema.name, link: link.name });
301
+ }
302
+ _obj['#composition'] ?? (_obj['#composition'] = {});
303
+ (_a = _obj['#composition'])[_b = link.name] ?? (_a[_b] = []);
304
+ for (const linkObjItem of linkObj) {
305
+ const child = await trx.bucket(link.bucket.refName).create(linkObjItem);
306
+ _obj['#composition'][link.name].push(child);
307
+ }
308
+ }
309
+ else {
310
+ const child = await trx.bucket(link.bucket.refName).create(linkObj);
311
+ _obj['#composition'] ?? (_obj['#composition'] = {});
312
+ _obj['#composition'][link.name] = child;
313
+ }
314
+ }
315
+ return _obj;
316
+ }
317
+ /**
318
+ * Replace the `$id` symbol on an object with the proper ID value.
319
+ * This is used on composition, to access the ID of the parent.
320
+ */
321
+ replaceFutureId(composition, value) {
322
+ let poll = [composition];
323
+ while (poll.length) {
324
+ const next = [];
325
+ for (const obj of poll) {
326
+ if (Array.isArray(obj)) {
327
+ for (let i = 0; i < obj.length; i++) {
328
+ if (typeof obj[i] === 'symbol' && obj[i] == exports.$id) {
329
+ obj[i] = value;
330
+ }
331
+ else if (typeof obj[i] === 'object') {
332
+ next.push(obj[i]);
333
+ }
334
+ }
335
+ }
336
+ else {
337
+ for (const key in obj) {
338
+ if (typeof obj[key] === 'symbol' && obj[key] == exports.$id) {
339
+ obj[key] = value;
340
+ }
341
+ else if (typeof obj[key] === 'object') {
342
+ next.push(obj[key]);
343
+ }
344
+ }
345
+ }
346
+ }
347
+ poll = next;
348
+ }
349
+ }
350
+ // Update
351
+ /**
352
+ * Update an entity
353
+ *
354
+ * - Options:
355
+ * - `mode`: Type of update to perform (default: `patch`)
356
+ * - `patch`: Only modifies properties that changed
357
+ * - `replace`: Replace the whole object
358
+ * - `no_tenancy`: Don't apply tenancy rules (default: `false`)
359
+ * - `unsafe`:
360
+ * - Don't attempt to read the object before updating. This option is faster, but can throw exceptions directly from the adapter (default: `false`)
361
+ * - **WARNING** Unsafe currently avoids the tenancy check
362
+ */
363
+ async update(trx, obj, options) {
364
+ log_1.Log.debug('bucket', this.schema.name, `Update id=${obj['id']}`, obj);
365
+ // Separate composition
366
+ const composition = obj['#composition'] || {};
367
+ delete obj['#composition'];
368
+ // Tenancy
369
+ const tenancy = (options?.no_tenancy)
370
+ ? undefined
371
+ : this.getTenancyQuery(trx);
372
+ // Read old object, if safe, to check if it exists
373
+ let oldObj;
374
+ if (!options?.unsafe) {
375
+ // With Tenancy
376
+ if (tenancy) {
377
+ const result = await this.adapter.query(trx, {
378
+ id: obj.id,
379
+ '#and': tenancy
380
+ }, { perPage: 1 }, undefined, {
381
+ metadataOnly: true
382
+ });
383
+ oldObj = result.data[0];
384
+ }
385
+ // Without Tenancy
386
+ else {
387
+ oldObj = this.cache
388
+ ? await this.cache.get(trx, obj['id'])
389
+ : await this.adapter.get(trx, obj['id']);
390
+ }
391
+ // Empty response
392
+ if (!oldObj) {
393
+ throw error_1.NesoiError.Bucket.ObjNotFound({ bucket: this.schema.alias, id: obj['id'] });
394
+ }
395
+ }
396
+ // Add meta (updated_by/updated_at)
397
+ this.addMeta(trx, obj, 'update');
398
+ // Encryption
399
+ if (this.schema.model.hasEncryptedField) {
400
+ this.encrypt(trx, obj);
401
+ }
402
+ // Drive
403
+ if (this.schema.model.hasFileField) {
404
+ await this.copyFilesToDrive(obj);
405
+ }
406
+ // Patch/Replace
407
+ const mode = options?.mode || 'patch';
408
+ const _obj = await this.adapter[mode](trx, obj);
409
+ // Composition
410
+ for (const link of Object.values(this.schema.graph.links)) {
411
+ if (link.rel !== 'composition')
412
+ continue;
413
+ const linkObj = composition[link.name];
414
+ if (!linkObj) {
415
+ throw error_1.NesoiError.Bucket.MissingComposition({ method: 'patch', bucket: this.schema.name, link: link.name });
416
+ }
417
+ if (link.many) {
418
+ if (!Array.isArray(linkObj)) {
419
+ throw error_1.NesoiError.Bucket.CompositionValueShouldBeArray({ method: 'patch', bucket: this.schema.name, link: link.name });
420
+ }
421
+ for (const linkObjItem of linkObj) {
422
+ await trx.bucket(link.bucket.refName)[mode](linkObjItem);
423
+ }
424
+ }
425
+ else {
426
+ await trx.bucket(link.bucket.refName)[mode](linkObj);
427
+ }
428
+ }
429
+ return _obj;
430
+ }
431
+ /**
432
+ * Create or Replace an entity
433
+ *
434
+ * **WARNING** Tenancy not checked
435
+ */
76
436
  async put(trx, obj) {
77
- log_1.Log.debug('bucket', this.schema.name, `Put id=${obj['id'] || 'new'}`, obj);
78
- const raw = await this.adapter.put(trx, obj);
79
- return raw;
437
+ var _a, _b;
438
+ log_1.Log.debug('bucket', this.schema.name, `Put id=${obj['id']}`, obj);
439
+ // Separate composition
440
+ const composition = obj['#composition'] || {};
441
+ delete obj['#composition'];
442
+ // Add meta (updated_by/updated_at)
443
+ this.addMeta(trx, obj, 'update');
444
+ // Encryption
445
+ if (this.schema.model.hasEncryptedField) {
446
+ this.encrypt(trx, obj);
447
+ }
448
+ // Drive
449
+ if (this.schema.model.hasFileField) {
450
+ await this.copyFilesToDrive(obj);
451
+ }
452
+ // Put
453
+ const _obj = await this.adapter.put(trx, obj);
454
+ // Composition
455
+ for (const link of Object.values(this.schema.graph.links)) {
456
+ if (link.rel !== 'composition')
457
+ continue;
458
+ const linkObj = composition[link.name];
459
+ if (!linkObj) {
460
+ throw error_1.NesoiError.Bucket.MissingComposition({ method: 'replace', bucket: this.schema.name, link: link.name });
461
+ }
462
+ if (link.many) {
463
+ if (!Array.isArray(linkObj)) {
464
+ throw error_1.NesoiError.Bucket.CompositionValueShouldBeArray({ method: 'replace', bucket: this.schema.name, link: link.name });
465
+ }
466
+ _obj['#composition'] ?? (_obj['#composition'] = {});
467
+ (_a = _obj['#composition'])[_b = link.name] ?? (_a[_b] = []);
468
+ for (const linkObjItem of linkObj) {
469
+ const child = await trx.bucket(link.bucket.refName).put(linkObjItem);
470
+ _obj['#composition'][link.name].push(child);
471
+ }
472
+ }
473
+ else {
474
+ const child = await trx.bucket(link.bucket.refName).put(linkObj);
475
+ _obj['#composition'] ?? (_obj['#composition'] = {});
476
+ _obj['#composition'][link.name] = child;
477
+ }
478
+ }
479
+ return _obj;
80
480
  }
81
481
  // Delete
82
- async delete(trx, id) {
482
+ /**
483
+ * Delete an entity
484
+ *
485
+ * - Options:
486
+ * - `no_tenancy`: Don't apply tenancy rules (default: `false`)
487
+ * - `unsafe`
488
+ * - Don't attempt to read the object before updating. This option is faster, but can throw exceptions directly from the adapter (default: `false`)
489
+ * - **WARNING** Unsafe currently avoids the tenancy check
490
+ */
491
+ async delete(trx, id, options) {
492
+ log_1.Log.debug('bucket', this.schema.name, `Delete id=${id}`);
493
+ // Validate ID
83
494
  if (typeof id !== 'string' && typeof id !== 'number') {
84
495
  throw error_1.NesoiError.Bucket.InvalidId({ bucket: this.schema.alias, id });
85
496
  }
86
- log_1.Log.debug('bucket', this.schema.name, `Delete id=${id}`);
497
+ // Read object, if safe, to check if it exists
498
+ if (!options?.unsafe) {
499
+ // Tenancy
500
+ const tenancy = (options?.no_tenancy)
501
+ ? undefined
502
+ : this.getTenancyQuery(trx);
503
+ // Check if object exists
504
+ const result = await this.adapter.query(trx, {
505
+ id, '#and': tenancy
506
+ }, { perPage: 1 }, undefined, {
507
+ metadataOnly: true
508
+ });
509
+ if (!result.data.length) {
510
+ throw error_1.NesoiError.Bucket.ObjNotFound({ bucket: this.schema.alias, id });
511
+ }
512
+ }
513
+ // Delete compositions (with other key)
514
+ for (const link of Object.values(this.schema.graph.links)) {
515
+ if (link.rel !== 'composition')
516
+ continue;
517
+ if (link.keyOwner !== 'other')
518
+ continue;
519
+ const linked = await this.readLink(trx, id, link.name, {
520
+ silent: true
521
+ });
522
+ if (!linked)
523
+ continue;
524
+ if (link.many) {
525
+ for (const linkedItem of linked) {
526
+ await trx.bucket(link.bucket.refName).delete(linkedItem.id);
527
+ }
528
+ }
529
+ else {
530
+ await trx.bucket(link.bucket.refName).delete(linked.id);
531
+ }
532
+ }
533
+ // Delete the object itself
87
534
  await this.adapter.delete(trx, id);
535
+ // Composition (with self key)
536
+ for (const link of Object.values(this.schema.graph.links)) {
537
+ if (link.rel !== 'composition')
538
+ continue;
539
+ if (link.keyOwner !== 'self')
540
+ continue;
541
+ const linked = await this.readLink(trx, id, link.name, {
542
+ silent: true
543
+ });
544
+ if (!linked)
545
+ continue;
546
+ if (link.many) {
547
+ await trx.bucket(link.bucket.refName).unsafe.deleteMany(linked.map((l) => l.id));
548
+ }
549
+ else {
550
+ await trx.bucket(link.bucket.refName).delete(linked.id);
551
+ }
552
+ }
88
553
  }
89
- async deleteMany(trx, ids) {
554
+ /**
555
+ * Delete many entities
556
+ *
557
+ * - Options:
558
+ * - `no_tenancy`: Don't apply tenancy rules (default: `false`)
559
+ * - `unsafe`
560
+ * - Don't attempt to read the object before updating. This option is faster, but can throw exceptions directly from the adapter (default: `false`)
561
+ * - **WARNING** Unsafe currently avoids the tenancy check
562
+ */
563
+ async deleteMany(trx, ids, options) {
90
564
  log_1.Log.debug('bucket', this.schema.name, `Delete Many ids=${ids}`);
565
+ // Filter ids, if safe, to check if it exists
566
+ if (!options?.unsafe) {
567
+ // Tenancy
568
+ const tenancy = (options?.no_tenancy)
569
+ ? undefined
570
+ : this.getTenancyQuery(trx);
571
+ // Filter ids
572
+ const result = await this.adapter.query(trx, {
573
+ 'id in': ids,
574
+ '#and': tenancy
575
+ }, undefined, undefined, {
576
+ metadataOnly: true
577
+ });
578
+ ids = result.data.map(obj => obj.id);
579
+ }
580
+ // Composition (with other key)
581
+ for (const link of Object.values(this.schema.graph.links)) {
582
+ if (link.rel !== 'composition')
583
+ continue;
584
+ if (link.keyOwner !== 'other')
585
+ continue;
586
+ for (const id of ids) {
587
+ const linked = await this.readLink(trx, id, link.name, { silent: true });
588
+ if (!linked)
589
+ continue;
590
+ if (link.many) {
591
+ await trx.bucket(link.bucket.refName).unsafe.deleteMany(linked.map((l) => l.id));
592
+ }
593
+ else {
594
+ await trx.bucket(link.bucket.refName).unsafe.delete(linked.id);
595
+ }
596
+ }
597
+ }
91
598
  await this.adapter.deleteMany(trx, ids);
599
+ // Composition (with self key)
600
+ for (const link of Object.values(this.schema.graph.links)) {
601
+ if (link.rel !== 'composition')
602
+ continue;
603
+ if (link.keyOwner !== 'self')
604
+ continue;
605
+ for (const id of ids) {
606
+ const linked = await this.readLink(trx, id, link.name, { silent: true });
607
+ if (!linked)
608
+ continue;
609
+ if (link.many) {
610
+ await trx.bucket(link.bucket.refName).unsafe.deleteMany(linked.map((l) => l.id));
611
+ }
612
+ else {
613
+ await trx.bucket(link.bucket.refName).unsafe.delete(linked.id);
614
+ }
615
+ }
616
+ }
92
617
  }
93
618
  // Query
94
- async query(trx, query, pagination, view) {
619
+ /**
620
+ * Query entities using NQL
621
+ *
622
+ * - Options:
623
+ * - `no_tenancy`: Don't apply tenancy rules (default: `false`)
624
+ * - `params`: NQL parameters
625
+ */
626
+ async query(trx, query, pagination, view, options) {
95
627
  log_1.Log.trace('bucket', this.schema.name, 'Query', query);
96
628
  const v = (view ? this.views[view] : null) || this.views['default'];
97
629
  if (!v) {
98
630
  throw error_1.NesoiError.Bucket.Query.ViewNotFound(this.schema.name, view || 'default');
99
631
  }
100
- const raws = this.cache
101
- ? await this.cache.query(trx, v.schema, query, pagination)
102
- : await this.adapter.query(trx, query, pagination);
103
- if (!raws.length)
104
- return [];
632
+ // Tenancy
633
+ const tenancy = (options?.no_tenancy)
634
+ ? undefined
635
+ : this.getTenancyQuery(trx);
636
+ query = {
637
+ ...query,
638
+ '#and': tenancy
639
+ };
640
+ // Query
641
+ const result = await this.adapter.query(trx, query, pagination, options?.params);
642
+ if (!result.data.length)
643
+ return {
644
+ data: []
645
+ };
646
+ // Encryption
647
+ if (this.schema.model.hasEncryptedField) {
648
+ for (const obj of result.data) {
649
+ this.decrypt(trx, obj);
650
+ }
651
+ }
652
+ // Build
105
653
  if (view) {
106
- return this.buildAll(trx, raws, view);
654
+ result.data = await this.buildMany(trx, result.data, view);
107
655
  }
108
- else {
109
- return raws;
656
+ return result;
657
+ }
658
+ // Metadata
659
+ /**
660
+ * Add `created_by`, `created_at`, `updated_by` and `updated_at` fields to object
661
+ */
662
+ addMeta(trx, obj, operation) {
663
+ const match = trx_node_1.TrxNode.getFirstUserMatch(trx, this.schema.tenancy);
664
+ if (operation === 'create') {
665
+ obj[this.adapter.config.meta.created_at] = datetime_1.NesoiDatetime.now();
666
+ if (match) {
667
+ obj[this.adapter.config.meta.created_by] = match.user.id;
668
+ }
669
+ }
670
+ obj[this.adapter.config.meta.updated_at] = datetime_1.NesoiDatetime.now();
671
+ if (match) {
672
+ obj[this.adapter.config.meta.updated_by] = match.user.id;
110
673
  }
111
674
  }
112
- static getQueryMeta(bucket) {
113
- return {
114
- ...bucket.adapter.getQueryMeta(),
115
- bucket: bucket.schema
116
- };
675
+ // Tenancy
676
+ getTenancyQuery(trx) {
677
+ if (!this.schema.tenancy)
678
+ return;
679
+ const match = trx_node_1.TrxNode.getFirstUserMatch(trx, this.schema.tenancy);
680
+ return this.schema.tenancy[match.provider]?.(match.user);
117
681
  }
118
- static getQueryRunner(bucket) {
119
- return bucket.adapter.nql;
682
+ // Encryption
683
+ encrypt(trx, obj, fields = this.schema.model.fields) {
684
+ for (const key in fields) {
685
+ const field = fields[key];
686
+ if (field.crypto) {
687
+ const key = trx.value(field.crypto.key);
688
+ tree_1.Tree.set(obj, field.path, val => crypto_1.Crypto.encrypt(val, key));
689
+ }
690
+ if (field.children) {
691
+ this.encrypt(trx, obj, field.children);
692
+ }
693
+ }
120
694
  }
121
- static getAdapter(bucket) {
122
- return bucket.adapter;
695
+ decrypt(trx, obj, fields = this.schema.model.fields) {
696
+ for (const key in fields) {
697
+ const field = fields[key];
698
+ if (field.crypto) {
699
+ const key = trx.value(field.crypto.key);
700
+ tree_1.Tree.set(obj, field.path, val => crypto_1.Crypto.decrypt(val, key));
701
+ }
702
+ if (field.children) {
703
+ this.decrypt(trx, obj, field.children);
704
+ }
705
+ }
706
+ }
707
+ // Drive (Files)
708
+ /**
709
+ * Copy all files from the object to the bucket's Drive
710
+ * - Call `drive.copy` to send the files preserving the local copy
711
+ * - Replace the file on the object with a new one representing the remote
712
+ */
713
+ async copyFilesToDrive(obj) {
714
+ if (!this.drive) {
715
+ throw error_1.NesoiError.Bucket.Drive.NoAdapter({ bucket: this.schema.alias });
716
+ }
717
+ const fields = bucket_model_schema_1.$BucketModel.fieldsOfType(this.schema.model, 'file');
718
+ for (const field of fields) {
719
+ if (field.array) {
720
+ const files = tree_1.Tree.get(obj, field.path);
721
+ const remoteFiles = [];
722
+ for (const file of files) {
723
+ remoteFiles.push(await this.drive.copy(file, this.drive.dirpath));
724
+ }
725
+ tree_1.Tree.set(obj, field.path, () => remoteFiles);
726
+ }
727
+ else {
728
+ const file = tree_1.Tree.get(obj, field.path);
729
+ const remoteFile = await this.drive.copy(file, this.drive.dirpath);
730
+ tree_1.Tree.set(obj, field.path, () => remoteFile);
731
+ }
732
+ }
123
733
  }
124
734
  }
125
735
  exports.Bucket = Bucket;