@blitznocode/blitz-orm 0.0.42 → 0.0.43

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/dist/index.js ADDED
@@ -0,0 +1,1775 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
25
+
26
+ // src/index.ts
27
+ var src_exports = {};
28
+ __export(src_exports, {
29
+ default: () => src_default
30
+ });
31
+ module.exports = __toCommonJS(src_exports);
32
+ var import_radash9 = require("radash");
33
+ var import_typedb_client3 = require("typedb-client");
34
+
35
+ // src/default.config.ts
36
+ var defaultConfig = {
37
+ query: {
38
+ simplifiedLinks: true
39
+ },
40
+ mutation: {}
41
+ };
42
+
43
+ // src/helpers.ts
44
+ var import_immer = __toESM(require("immer"));
45
+ var import_object_traversal = require("object-traversal");
46
+ var import_radash = require("radash");
47
+ var oFind = (obj, fn) => Object.values(
48
+ Object.fromEntries(Object.entries(obj).filter(([k, v]) => fn(k, v)))
49
+ )[0];
50
+ var oFilter = (obj, fn) => Object.fromEntries(Object.entries(obj).filter(([k, v]) => fn(k, v)));
51
+ var enrichSchema = (schema) => {
52
+ const allLinkedFields = [];
53
+ const withExtensionsSchema = (0, import_immer.default)(
54
+ schema,
55
+ (draft) => (0, import_object_traversal.traverse)(
56
+ draft,
57
+ ({ value, meta }) => {
58
+ if (meta.depth === 2 && value.extends) {
59
+ const extendedSchema = draft.entities[value.extends] || draft.relations[value.extends];
60
+ value;
61
+ value.idFields = extendedSchema.idFields ? (value.idFields || []).concat(extendedSchema.idFields) : value.idFields;
62
+ value.dataFields = extendedSchema.dataFields ? (value.dataFields || []).concat(extendedSchema.dataFields) : value.dataFields;
63
+ value.linkFields = extendedSchema.linkFields ? (value.linkFields || []).concat(extendedSchema.linkFields) : value.linkFields;
64
+ if ("roles" in extendedSchema) {
65
+ const val = value;
66
+ const extendedRelationSchema = extendedSchema;
67
+ val.roles = val.roles || {};
68
+ val.roles = {
69
+ ...val.roles,
70
+ ...extendedRelationSchema.roles
71
+ };
72
+ if (Object.keys(val.roles).length === 0) {
73
+ val.roles = {};
74
+ }
75
+ }
76
+ }
77
+ },
78
+ { traversalType: "breadth-first" }
79
+ )
80
+ );
81
+ (0, import_object_traversal.traverse)(schema, ({ key, value, meta }) => {
82
+ if (key === "linkFields") {
83
+ const getThingTypes = () => {
84
+ if (!meta.nodePath) {
85
+ throw new Error("No path");
86
+ }
87
+ const pathArray = meta.nodePath.split(".");
88
+ const thingPath = pathArray[0];
89
+ const thing = pathArray[1];
90
+ const thingType = thingPath === "entities" ? "entity" : thingPath === "relations" ? "relation" : "";
91
+ return {
92
+ thing,
93
+ thingType
94
+ };
95
+ };
96
+ const thingTypes = getThingTypes();
97
+ const withThing = !Array.isArray(value) ? [
98
+ {
99
+ ...value,
100
+ ...thingTypes
101
+ }
102
+ ] : value.map((x) => ({ ...x, ...thingTypes }));
103
+ allLinkedFields.push(...withThing);
104
+ }
105
+ });
106
+ const enrichedSchema = (0, import_immer.default)(
107
+ withExtensionsSchema,
108
+ (draft) => (0, import_object_traversal.traverse)(draft, ({ value, key, meta }) => {
109
+ if (meta.depth === 2 && value.idFields && !value.id) {
110
+ value.name = key;
111
+ const thingType = () => {
112
+ if (meta.nodePath?.split(".")[0] === "entities")
113
+ return "entity";
114
+ if (meta.nodePath?.split(".")[0] === "relations")
115
+ return "relation";
116
+ throw new Error("Unsupported node attributes");
117
+ };
118
+ value.thingType = thingType();
119
+ value.computedFields = [];
120
+ if ("roles" in value) {
121
+ const val = value;
122
+ Object.entries(val.roles).forEach(([roleKey, role]) => {
123
+ role.playedBy = allLinkedFields.filter(
124
+ (x) => x.relation === key && x.plays === roleKey
125
+ ) || [];
126
+ });
127
+ }
128
+ if ("linkFields" in value && value.linkFields) {
129
+ const val = value;
130
+ val.linkFields?.forEach((linkField) => {
131
+ if (linkField.target === "relation") {
132
+ linkField.oppositeLinkFieldsPlayedBy = [
133
+ {
134
+ plays: linkField.path,
135
+ thing: linkField.relation,
136
+ thingType: "relation"
137
+ }
138
+ ];
139
+ return;
140
+ }
141
+ const allOppositeLinkFields = allLinkedFields.filter(
142
+ (x) => x.relation === linkField.relation && x.plays !== linkField.plays
143
+ ) || [];
144
+ linkField.oppositeLinkFieldsPlayedBy = allOppositeLinkFields;
145
+ const { filter } = linkField;
146
+ linkField.oppositeLinkFieldsPlayedBy = linkField.oppositeLinkFieldsPlayedBy.filter(
147
+ (x) => x.target === "role"
148
+ );
149
+ if (filter && Array.isArray(filter)) {
150
+ linkField.oppositeLinkFieldsPlayedBy = linkField.oppositeLinkFieldsPlayedBy.filter(
151
+ (lf) => filter.some((ft) => lf.thing === ft.$role)
152
+ );
153
+ linkField.oppositeLinkFieldsPlayedBy = linkField.oppositeLinkFieldsPlayedBy.filter(
154
+ (lf) => filter.some((ft) => lf.thing === ft.$thing)
155
+ );
156
+ }
157
+ if (filter && !Array.isArray(filter)) {
158
+ linkField.oppositeLinkFieldsPlayedBy = linkField.oppositeLinkFieldsPlayedBy.filter(
159
+ (lf) => lf.$role === filter.$role
160
+ );
161
+ linkField.oppositeLinkFieldsPlayedBy = linkField.oppositeLinkFieldsPlayedBy.filter(
162
+ (lf) => lf.thing === filter.$thing
163
+ );
164
+ }
165
+ });
166
+ }
167
+ }
168
+ if (typeof value === "object" && "playedBy" in value) {
169
+ if ([
170
+ ...new Set(
171
+ value.playedBy?.map((x) => x.thing)
172
+ )
173
+ ].length > 1) {
174
+ throw new Error(
175
+ `Unsupported: roleFields can be only played by one thing. Role: ${key} path:${meta.nodePath}`
176
+ );
177
+ }
178
+ if (value.playedBy.length === 0) {
179
+ throw new Error(
180
+ `Unsupported: roleFields should be played at least by one thing. Role: ${key}, path:${meta.nodePath}`
181
+ );
182
+ }
183
+ }
184
+ if (meta.depth === 4 && (value.default || value.computedValue)) {
185
+ const [type, thingId] = meta.nodePath?.split(".") || [];
186
+ draft[type][thingId].computedFields.push(value.path);
187
+ }
188
+ })
189
+ );
190
+ return enrichedSchema;
191
+ };
192
+ var getCurrentSchema = (schema, node) => {
193
+ if (node.$entity) {
194
+ if (!(node.$entity in schema.entities)) {
195
+ throw new Error(`Missing entity '${node.$entity}' in the schema`);
196
+ }
197
+ return schema.entities[node.$entity];
198
+ }
199
+ if (node.$relation) {
200
+ if (!(node.$relation in schema.relations)) {
201
+ throw new Error(`Missing relation '${node.$relation}' in the schema`);
202
+ }
203
+ return schema.relations[node.$relation];
204
+ }
205
+ throw new Error(`Wrong schema or query for ${JSON.stringify(node)}`);
206
+ };
207
+ var getCurrentFields = (currentSchema, node) => {
208
+ const availableDataFields = currentSchema.dataFields?.map((x) => x.path);
209
+ const availableLinkFields = currentSchema.linkFields?.map((x) => x.path) || [];
210
+ const availableRoleFields = "roles" in currentSchema ? (0, import_radash.listify)(currentSchema.roles, (k) => k) : [];
211
+ const availableFields = [
212
+ ...availableDataFields || [],
213
+ ...availableLinkFields || [],
214
+ ...availableRoleFields || []
215
+ ];
216
+ const reservedRootFields = [
217
+ "$entity",
218
+ "$op",
219
+ "$id",
220
+ "$tempId",
221
+ "$filer",
222
+ "$relation",
223
+ "$parentKey",
224
+ "$filter",
225
+ "$fields"
226
+ ];
227
+ const allowedFields = [...reservedRootFields, ...availableFields];
228
+ if (!node) {
229
+ return {
230
+ fields: availableFields,
231
+ dataFields: availableDataFields,
232
+ roleFields: availableRoleFields,
233
+ linkFields: availableLinkFields
234
+ };
235
+ }
236
+ const usedFields = node.$fields ? node.$fields.map((x) => {
237
+ if (typeof x === "string")
238
+ return x;
239
+ if ("$path" in x)
240
+ return x.$path;
241
+ throw new Error(" Wrongly structured query");
242
+ }) : (0, import_radash.listify)(node, (k) => k);
243
+ const localFilterFields = !node.$filter ? [] : (0, import_radash.listify)(
244
+ node.$filter,
245
+ (k) => k.toString().startsWith("$") ? void 0 : k.toString()
246
+ ).filter((x) => x && availableDataFields?.includes(x));
247
+ const nestedFilterFields = !node.$filter ? [] : (0, import_radash.listify)(
248
+ node.$filter,
249
+ (k) => k.toString().startsWith("$") ? void 0 : k.toString()
250
+ ).filter(
251
+ (x) => x && [
252
+ ...availableRoleFields || [],
253
+ ...availableLinkFields || []
254
+ ]?.includes(x)
255
+ );
256
+ const unidentifiedFields = [...usedFields, ...localFilterFields].filter(
257
+ (x) => !allowedFields.includes(x)
258
+ );
259
+ const localFilters = !node.$filter ? {} : oFilter(node.$filter, (k, _v) => localFilterFields.includes(k));
260
+ const nestedFilters = !node.$filter ? {} : oFilter(node.$filter, (k, _v) => nestedFilterFields.includes(k));
261
+ return {
262
+ fields: availableFields,
263
+ dataFields: availableDataFields,
264
+ roleFields: availableRoleFields,
265
+ linkFields: availableLinkFields,
266
+ unidentifiedFields,
267
+ ...localFilterFields.length ? { localFilters } : {},
268
+ ...nestedFilterFields.length ? { nestedFilters } : {}
269
+ };
270
+ };
271
+ var getLocalFilters = (currentSchema, node) => {
272
+ const thingPath = currentSchema.defaultDBConnector.path || currentSchema.name;
273
+ const localFilters = node.$localFilters && (0, import_radash.listify)(node.$localFilters, (k, v) => {
274
+ const currentDataField = currentSchema.dataFields?.find(
275
+ (x) => x.path === k
276
+ );
277
+ const dbField = currentDataField?.shared ? k : `${thingPath}\xB7${k}`;
278
+ return `has ${dbField} '${v}'`;
279
+ });
280
+ const localFiltersTql = localFilters?.length ? `, ${localFilters.join(",")}` : "";
281
+ return localFiltersTql;
282
+ };
283
+
284
+ // src/pipeline/control/dispatchPipeline.ts
285
+ var import_radash6 = require("radash");
286
+
287
+ // src/pipeline/postprocess/parseTQLRes.ts
288
+ var import_radash2 = require("radash");
289
+ var extractEntities = (conceptMapGroups, schema) => {
290
+ const bormEntities = conceptMapGroups.map((conceptMapGroup) => {
291
+ const conceptMaps = [...conceptMapGroup.conceptMaps];
292
+ const typeDBEntity = conceptMapGroup.owner.asThing();
293
+ const thingName = typeDBEntity.type.label.name;
294
+ const currentSchema = schema.entities[thingName] ? schema.entities[thingName] : schema.relations[thingName];
295
+ if (!currentSchema.idFields) {
296
+ throw new Error(`No idFields defined for ${thingName}`);
297
+ }
298
+ const thingType = schema.entities[thingName] ? "entity" : "relation";
299
+ const entityAttributes = conceptMaps.map((conceptMap) => {
300
+ const attribute = conceptMap.get("attribute")?.asAttribute();
301
+ if (!attribute) {
302
+ return [];
303
+ }
304
+ const nameParts = attribute.type.label.name.split("\xB7");
305
+ const attributeName = nameParts[nameParts.length - 1];
306
+ return [attributeName, attribute.value];
307
+ });
308
+ const entity = Object.fromEntries(entityAttributes);
309
+ return {
310
+ ...entity,
311
+ [`$${thingType}`]: thingName,
312
+ $id: entity[currentSchema.idFields[0]]
313
+ };
314
+ });
315
+ return bormEntities;
316
+ };
317
+ var extractRelations = (conceptMapGroups, entityNames) => {
318
+ const relations = conceptMapGroups.flatMap((conceptMapGroup) => {
319
+ const relationsInGroup = conceptMapGroup.conceptMaps.map((conceptMap) => {
320
+ const link = /* @__PURE__ */ new Map();
321
+ entityNames.forEach((entityName) => {
322
+ const id = conceptMap.get(`${entityName}_id`)?.asAttribute().value.toString();
323
+ if (id)
324
+ link.set(entityName, id);
325
+ });
326
+ return link;
327
+ });
328
+ return relationsInGroup;
329
+ });
330
+ return relations;
331
+ };
332
+ var extractRoles = (conceptMapGroups, ownerPath, rolePath) => {
333
+ const roles = conceptMapGroups.flatMap((conceptMapGroup) => {
334
+ const rolesInGroup = conceptMapGroup.conceptMaps.map((conceptMap) => {
335
+ const ownerId = conceptMap.get(`${ownerPath}_id`)?.asAttribute().value.toString();
336
+ const roleId = conceptMap.get(`${rolePath}_id`)?.asAttribute().value.toString();
337
+ return {
338
+ ownerId,
339
+ path: rolePath,
340
+ roleId
341
+ };
342
+ });
343
+ return rolesInGroup;
344
+ });
345
+ return roles;
346
+ };
347
+ var parseTQLRes = async (req, res) => {
348
+ const { schema, bqlRequest, config, tqlRequest } = req;
349
+ const { rawTqlRes } = res;
350
+ if (!bqlRequest) {
351
+ throw new Error("BQL request not parsed");
352
+ } else if (!rawTqlRes) {
353
+ throw new Error("TQL query not executed");
354
+ }
355
+ const { query } = bqlRequest;
356
+ if (!query) {
357
+ if (rawTqlRes.insertions?.length === 0 && !tqlRequest?.deletions) {
358
+ throw new Error(
359
+ "Nothing has changed in DB, probably one of the ids specified in the mutation does not exist"
360
+ );
361
+ }
362
+ const { mutation } = bqlRequest;
363
+ if (!mutation) {
364
+ throw new Error("TQL mutation not executed");
365
+ }
366
+ const expected = [...mutation.things, ...mutation.edges];
367
+ const result = expected.map((x) => {
368
+ const currentNode = rawTqlRes.insertions?.[0].get(
369
+ `${x.$tempId || x.$id}`
370
+ );
371
+ if (x.$op === "create" || x.$op === "update" || x.$op === "link") {
372
+ if (!currentNode?.asThing().iid) {
373
+ throw new Error(
374
+ `Thing not received on mutation: ${JSON.stringify(x)}`
375
+ );
376
+ }
377
+ const dbIdd = currentNode?.asThing().iid;
378
+ if (config.mutation?.noMetadata) {
379
+ return (0, import_radash2.mapEntries)(x, (k, v) => [
380
+ k.toString().startsWith("$") ? Symbol.for(k) : k,
381
+ v
382
+ ]);
383
+ }
384
+ return { $dbId: dbIdd, ...x };
385
+ }
386
+ if (x.$op === "delete" || x.$op === "unlink") {
387
+ return x;
388
+ }
389
+ if (x.$op === "noop") {
390
+ return void 0;
391
+ }
392
+ throw new Error(`Unsupported op ${x.$op}`);
393
+ }).filter((x) => x);
394
+ res.bqlRes = result.length > 1 ? result : result[0];
395
+ return;
396
+ }
397
+ if (!rawTqlRes.entity) {
398
+ throw new Error("TQL query not executed");
399
+ }
400
+ const entities = extractEntities(rawTqlRes.entity, schema);
401
+ const relations = rawTqlRes.relations?.map((relation) => {
402
+ const currentRelSchema = schema.relations[relation.relation];
403
+ const currentRelroles = (0, import_radash2.listify)(
404
+ currentRelSchema.roles,
405
+ (_k, v) => {
406
+ if ([...new Set(v.playedBy?.map((x) => x.thing))].length !== 1) {
407
+ throw new Error(
408
+ "a role can be played by two entities throws the same relation"
409
+ );
410
+ }
411
+ if (!v.playedBy)
412
+ throw new Error("Role not being played by nobody");
413
+ return v.playedBy[0].thing;
414
+ }
415
+ );
416
+ const links = extractRelations(relation.conceptMapGroups, [
417
+ ...currentRelroles,
418
+ currentRelSchema.name
419
+ ]);
420
+ return {
421
+ name: relation.relation,
422
+ links
423
+ };
424
+ });
425
+ const roles = rawTqlRes.roles?.map((role) => {
426
+ return {
427
+ name: role.ownerPath,
428
+ links: extractRoles(role.conceptMapGroups, role.ownerPath, role.path)
429
+ };
430
+ });
431
+ const cache = res.cache || {
432
+ entities: /* @__PURE__ */ new Map(),
433
+ relations: /* @__PURE__ */ new Map(),
434
+ roleLinks: /* @__PURE__ */ new Map()
435
+ };
436
+ entities.forEach((entity) => {
437
+ const entityName = entity.$entity || entity.$relation;
438
+ const entityId = entity.$id;
439
+ const entityCache = cache.entities.get(entityName) || /* @__PURE__ */ new Map();
440
+ entityCache.set(entityId, { ...entity, $show: true });
441
+ cache.entities.set(entityName, entityCache);
442
+ });
443
+ relations?.forEach((relation) => {
444
+ const relationName = relation.name;
445
+ const relationCache = cache.relations.get(relationName) || [];
446
+ relationCache.push(...relation.links);
447
+ cache.relations.set(relationName, relationCache);
448
+ relation.links.forEach((link) => {
449
+ [...link.entries()].forEach(([entityName, entityId]) => {
450
+ const entityCache = cache.entities.get(entityName) || /* @__PURE__ */ new Map();
451
+ const getEntityThingType = () => {
452
+ if (schema.entities[entityName])
453
+ return "entity";
454
+ if (schema.relations[entityName])
455
+ return "relation";
456
+ throw new Error("Entity or relation not found");
457
+ };
458
+ const entityThingType = getEntityThingType();
459
+ const entity = {
460
+ [entityThingType]: entityName,
461
+ $id: entityId,
462
+ ...entityCache.get(entityId)
463
+ };
464
+ entityCache.set(entityId, entity);
465
+ cache.entities.set(entityName, entityCache);
466
+ });
467
+ });
468
+ });
469
+ roles?.forEach((role) => {
470
+ const ownerSchema = schema.relations[role.name] || schema.entities[role.name];
471
+ role.links.forEach((link) => {
472
+ const cachedLinks = cache.roleLinks.get(link.ownerId) || {};
473
+ let cachedLinkID = cachedLinks[link.path];
474
+ if (cachedLinkID) {
475
+ if ((0, import_radash2.isArray)(cachedLinkID)) {
476
+ cachedLinkID.push(link.roleId);
477
+ } else if ((0, import_radash2.isString)(cachedLinkID) && cachedLinkID !== link.roleId) {
478
+ cachedLinkID = [cachedLinkID, link.roleId];
479
+ }
480
+ } else {
481
+ cachedLinkID = link.roleId;
482
+ }
483
+ cachedLinks[link.path] = cachedLinkID;
484
+ cache.roleLinks.set(link.ownerId, cachedLinks);
485
+ ownerSchema.roles[link.path].playedBy?.forEach((roleSchema) => {
486
+ const entityCache = cache.entities.get(roleSchema.thing) || /* @__PURE__ */ new Map();
487
+ const entity = {
488
+ $entity: roleSchema.thing,
489
+ $id: link.roleId,
490
+ ...entityCache.get(link.roleId)
491
+ };
492
+ entityCache.set(link.roleId, entity);
493
+ cache.entities.set(roleSchema.thing, entityCache);
494
+ });
495
+ });
496
+ });
497
+ res.cache = cache;
498
+ };
499
+
500
+ // src/pipeline/postprocess/fieldsOperator.ts
501
+ var import_radash3 = require("radash");
502
+
503
+ // src/pipeline/postprocess/buildBQLTree.ts
504
+ var import_immer2 = __toESM(require("immer"));
505
+ var import_object_traversal2 = require("object-traversal");
506
+ var import_radash4 = require("radash");
507
+ var cleanOutput = (obj, config) => (0, import_immer2.default)(
508
+ obj,
509
+ (draft) => (0, import_object_traversal2.traverse)(
510
+ draft,
511
+ ({ value }) => {
512
+ if (Array.isArray(value) || !(typeof value === "object"))
513
+ return;
514
+ if (value.$fields) {
515
+ delete value.$fields;
516
+ }
517
+ if (value.$filter) {
518
+ delete value.$filter;
519
+ }
520
+ if (value.$show) {
521
+ delete value.$show;
522
+ }
523
+ if (config.query?.noMetadata && (value.$entity || value.$relation)) {
524
+ delete value.$entity;
525
+ delete value.$relation;
526
+ delete value.$id;
527
+ }
528
+ const symbols = Object.getOwnPropertySymbols(obj);
529
+ symbols.forEach((symbol) => {
530
+ delete value[symbol];
531
+ });
532
+ }
533
+ )
534
+ );
535
+ var filterChildrenEntities = (things, ids, node, path) => things.map(([id, entity]) => {
536
+ if (!ids)
537
+ return null;
538
+ if (!ids.includes(id))
539
+ return null;
540
+ if (!node.$fields)
541
+ return id;
542
+ if (node.$fields.includes(path))
543
+ return id;
544
+ const currentFieldConf = node.$fields.find(
545
+ (f) => (0, import_radash4.isObject)(f) && f.$path === path
546
+ );
547
+ if (currentFieldConf) {
548
+ const onlyMetadataEntity = {
549
+ ...oFilter(entity, (k, _v) => k.startsWith("$"))
550
+ };
551
+ const withFieldsEntity = currentFieldConf.$fields ? {
552
+ ...onlyMetadataEntity,
553
+ $fields: currentFieldConf.$fields
554
+ } : onlyMetadataEntity;
555
+ if (currentFieldConf.$id) {
556
+ if (Array.isArray(currentFieldConf.$id)) {
557
+ if (currentFieldConf.$id.includes(id))
558
+ return withFieldsEntity;
559
+ return null;
560
+ }
561
+ if (currentFieldConf.$id === id)
562
+ return withFieldsEntity;
563
+ }
564
+ return withFieldsEntity;
565
+ }
566
+ return null;
567
+ }).filter((x) => x);
568
+ var buildBQLTree = async (req, res) => {
569
+ const { bqlRequest, config, schema } = req;
570
+ const { cache } = res;
571
+ if (!bqlRequest) {
572
+ throw new Error("BQL request not parsed");
573
+ }
574
+ const { query } = bqlRequest;
575
+ if (!query) {
576
+ res.bqlRes = cleanOutput(res.bqlRes, config);
577
+ return;
578
+ }
579
+ if (!cache) {
580
+ return;
581
+ }
582
+ const thingSchema = "$entity" in query ? query.$entity : query.$relation;
583
+ const entityMap = cache.entities.get(thingSchema.name);
584
+ if (!entityMap) {
585
+ res.bqlRes = null;
586
+ return;
587
+ }
588
+ const filterFields = (0, import_radash4.listify)(query.$filter, (x) => x);
589
+ const atLeastOneUnique = filterFields.some(
590
+ (x) => thingSchema.dataFields?.find((y) => y.path === x)?.validations?.unique
591
+ );
592
+ const monoOutput = !Array.isArray(bqlRequest.query) && (bqlRequest.query?.$id && !Array.isArray(bqlRequest.query?.$id) || atLeastOneUnique);
593
+ if (Array.isArray(req.rawBqlRequest)) {
594
+ throw new Error("Query arrays not implemented yet");
595
+ }
596
+ const rootThings = entityMap;
597
+ const structuredAnswer = [...rootThings].length ? [...rootThings].map(([id, _entity]) => ({
598
+ ...req.rawBqlRequest,
599
+ $id: id
600
+ })) : req.rawBqlRequest;
601
+ const bqlTree = (0, import_immer2.default)(
602
+ structuredAnswer,
603
+ (draft) => (0, import_object_traversal2.traverse)(draft, ({ value: val }) => {
604
+ const value = val;
605
+ if (!value?.$entity && !value?.$relation)
606
+ return;
607
+ const thingName = "$entity" in value ? value.$entity : value.$relation;
608
+ if (thingName) {
609
+ const currentIds = Array.isArray(value.$id) ? value.$id : [value.$id];
610
+ const currentSchema = "$relation" in value ? schema.relations[value.$relation] : schema.entities[value.$entity];
611
+ const { dataFields, roleFields } = getCurrentFields(currentSchema);
612
+ const currentEntities = cache.entities.get(thingName);
613
+ if (!currentEntities)
614
+ return;
615
+ [...currentEntities].forEach(([id, entity]) => {
616
+ if (currentIds.includes(id)) {
617
+ const queriedDataFields = value.$fields ? value.$fields : dataFields;
618
+ (0, import_radash4.listify)(entity, (k, v) => {
619
+ if (k.startsWith("$"))
620
+ return;
621
+ if (!queriedDataFields?.includes(k))
622
+ return;
623
+ value[k] = v;
624
+ });
625
+ const links = cache.roleLinks.get(id);
626
+ const flatRoleFields = value.$fields ? value.$fields.filter((x) => typeof x === "string") : roleFields;
627
+ const embeddedRoleFields = value.$fields?.filter((x) => typeof x === "object")?.map((y) => y.$path) || [];
628
+ Object.entries(links || {}).forEach(([rolePath, linkedIds]) => {
629
+ if (![...flatRoleFields, ...embeddedRoleFields].includes(rolePath)) {
630
+ return;
631
+ }
632
+ if (!("roles" in currentSchema))
633
+ throw new Error("No roles in schema");
634
+ const uniqueLinkedIds = !Array.isArray(linkedIds) ? [linkedIds] : [...new Set(linkedIds)];
635
+ const { cardinality, playedBy } = currentSchema.roles[rolePath];
636
+ const thingNames = [...new Set(playedBy?.map((x) => x.thing))];
637
+ const children = thingNames?.flatMap((x) => {
638
+ const thingEntities = cache.entities.get(x);
639
+ if (!thingEntities)
640
+ return [];
641
+ return filterChildrenEntities(
642
+ [...thingEntities],
643
+ uniqueLinkedIds,
644
+ value,
645
+ rolePath
646
+ );
647
+ });
648
+ if (children?.length) {
649
+ if (children.length === 1 && children[0] === value.$id) {
650
+ return;
651
+ }
652
+ if (cardinality === "ONE") {
653
+ value[rolePath] = children[0];
654
+ return;
655
+ }
656
+ value[rolePath] = children.filter(
657
+ (x) => typeof x === "string" || typeof x === "object" && x?.$show
658
+ );
659
+ }
660
+ });
661
+ }
662
+ });
663
+ const currentLinkFields = currentSchema.linkFields;
664
+ if (currentLinkFields) {
665
+ currentLinkFields.forEach((linkField) => {
666
+ const currentRelation = cache.relations.get(linkField.relation);
667
+ const tunnel = linkField.oppositeLinkFieldsPlayedBy;
668
+ if (linkField.target === "relation") {
669
+ const targetRelation = tunnel[0];
670
+ const targetRelationThings = cache.relations.get(
671
+ linkField.relation
672
+ );
673
+ const matchedLinks = !targetRelationThings ? [] : [...targetRelationThings].filter((link) => {
674
+ return currentIds.includes(link.get(thingName));
675
+ }).map((x) => x.get(targetRelation.thing));
676
+ const targetRelationEntities = cache.entities.get(
677
+ targetRelation.thing
678
+ );
679
+ if (!targetRelationEntities)
680
+ return null;
681
+ const children = filterChildrenEntities(
682
+ [...targetRelationEntities],
683
+ matchedLinks,
684
+ value,
685
+ linkField.path
686
+ );
687
+ if (children.length) {
688
+ if (children.length === 1 && children[0] === value.$id) {
689
+ return null;
690
+ }
691
+ if (linkField.cardinality === "ONE") {
692
+ value[linkField.path] = children[0];
693
+ return null;
694
+ }
695
+ value[linkField.path] = children.filter(
696
+ (x) => typeof x === "string" || typeof x === "object" && x?.$show
697
+ );
698
+ }
699
+ return null;
700
+ }
701
+ if (linkField.target === "role") {
702
+ const linkFieldPlayers = tunnel.flatMap((t) => {
703
+ const allCurrentLinkFieldThings = cache.entities.get(t.thing);
704
+ const linkedIds = currentRelation ? [...currentRelation].filter((rel) => rel.get(thingName) === currentIds[0]).map((x) => x.get(t.thing)) : [];
705
+ const uniqueLinkedIds = [...new Set(linkedIds)];
706
+ if (!allCurrentLinkFieldThings)
707
+ return null;
708
+ const children = filterChildrenEntities(
709
+ [...allCurrentLinkFieldThings],
710
+ uniqueLinkedIds,
711
+ value,
712
+ linkField.path
713
+ );
714
+ if (children.length) {
715
+ return children.filter(
716
+ (x) => typeof x === "string" || typeof x === "object" && x?.$show
717
+ );
718
+ }
719
+ return null;
720
+ }).filter((x) => x);
721
+ if (linkFieldPlayers && linkFieldPlayers.length) {
722
+ if (linkField.cardinality === "ONE") {
723
+ value[linkField.path] = linkFieldPlayers[0];
724
+ return null;
725
+ }
726
+ value[linkField.path] = linkFieldPlayers;
727
+ }
728
+ return null;
729
+ }
730
+ return null;
731
+ });
732
+ }
733
+ }
734
+ })
735
+ );
736
+ const withoutFieldFilters = cleanOutput(bqlTree, config);
737
+ res.bqlRes = monoOutput ? withoutFieldFilters[0] : withoutFieldFilters;
738
+ };
739
+
740
+ // src/pipeline/preprocess/buildTQLQuery.ts
741
+ var import_radash5 = require("radash");
742
+ var buildTQLQuery = async (req) => {
743
+ const { schema, bqlRequest } = req;
744
+ if (!bqlRequest?.query) {
745
+ throw new Error("BQL query not parsed");
746
+ }
747
+ const { query } = bqlRequest;
748
+ const currentThingSchema = "$entity" in query ? query.$entity : query.$relation;
749
+ const thingPath = currentThingSchema.defaultDBConnector.path || currentThingSchema.name;
750
+ if (!currentThingSchema.idFields) {
751
+ throw new Error("No id fields");
752
+ }
753
+ const [idField] = currentThingSchema.idFields;
754
+ const idParam = `$${thingPath}_id`;
755
+ let idFilter = `, has ${idField} ${idParam};`;
756
+ if (query.$id) {
757
+ if (Array.isArray(query.$id)) {
758
+ idFilter += ` ${idParam} like "${query.$id.join("|")}";`;
759
+ } else {
760
+ idFilter += ` ${idParam} "${query.$id}";`;
761
+ }
762
+ }
763
+ const localFiltersTql = getLocalFilters(currentThingSchema, query);
764
+ const allRoles = "roles" in currentThingSchema ? (0, import_radash5.listify)(currentThingSchema.roles, (k, v) => ({
765
+ path: k,
766
+ var: `$${k}`,
767
+ schema: v
768
+ })) : [];
769
+ const queryStr = `match $${thingPath} isa ${thingPath}, has attribute $attribute ${localFiltersTql} ${idFilter} group $${thingPath};`;
770
+ const rolesObj = allRoles.map((role) => {
771
+ if (!role.schema.playedBy || [...new Set(role.schema.playedBy?.map((x) => x.thing))].length !== 1) {
772
+ throw new Error(
773
+ "Unsupported: Role played by multiple linkfields or none"
774
+ );
775
+ }
776
+ const roleThingName = role.schema.playedBy[0].thing;
777
+ return {
778
+ path: role.path,
779
+ owner: thingPath,
780
+ request: `match $${thingPath} (${role.path}: ${role.var} ) isa ${thingPath} ${idFilter} ${role.var} isa ${roleThingName}, has id ${role.var}_id; group $${thingPath};`
781
+ };
782
+ });
783
+ const relations = currentThingSchema.linkFields?.flatMap((linkField) => {
784
+ const entityMatch = `match $${thingPath} isa ${thingPath}${localFiltersTql} ${idFilter}`;
785
+ const dirRel = linkField.target === "relation";
786
+ const tarRel = linkField.relation;
787
+ const relVar = `$${tarRel}`;
788
+ const relationMatchStart = `${dirRel ? relVar : ""} (${linkField.plays}: $${thingPath}`;
789
+ const relationMatchOpposite = linkField.oppositeLinkFieldsPlayedBy.map(
790
+ (link) => !dirRel ? `${link.plays}: $${link.thing}` : null
791
+ );
792
+ const roles = [relationMatchStart, ...relationMatchOpposite].filter((x) => x).join(",");
793
+ const relationPath = schema.relations[linkField.relation].defaultDBConnector.path || linkField.relation;
794
+ const relationMatchEnd = `) isa ${relationPath};`;
795
+ const relationIdFilters = linkField.oppositeLinkFieldsPlayedBy.map(
796
+ (link) => `$${link.thing} isa ${link.thing}, has id $${link.thing}_id;`
797
+ ).join(", ");
798
+ const group = `group $${thingPath};`;
799
+ const request = `${entityMatch} ${roles} ${relationMatchEnd} ${relationIdFilters} ${group}`;
800
+ return { relation: relationPath, entity: thingPath, request };
801
+ });
802
+ req.tqlRequest = {
803
+ entity: queryStr,
804
+ ...rolesObj?.length ? { roles: rolesObj } : {},
805
+ ...relations?.length ? { relations } : {}
806
+ };
807
+ };
808
+
809
+ // src/pipeline/preprocess/parseBQLQuery.ts
810
+ var parseBQLQuery = async (req) => {
811
+ const { rawBqlRequest: rawBqlQuery, schema } = req;
812
+ if (!("$entity" in rawBqlQuery) && !("$relation" in rawBqlQuery)) {
813
+ throw new Error("No entity specified in query");
814
+ }
815
+ const currentSchema = getCurrentSchema(schema, rawBqlQuery);
816
+ if (!currentSchema) {
817
+ throw new Error(`Thing '${rawBqlQuery}' not found in schema`);
818
+ }
819
+ const { unidentifiedFields, localFilters, nestedFilters } = getCurrentFields(
820
+ currentSchema,
821
+ rawBqlQuery
822
+ );
823
+ if (unidentifiedFields && unidentifiedFields.length > 0) {
824
+ throw new Error(
825
+ `Unknown fields: [${unidentifiedFields.join(",")}] in ${JSON.stringify(
826
+ rawBqlQuery
827
+ )}`
828
+ );
829
+ }
830
+ req.bqlRequest = {
831
+ query: {
832
+ ...req.rawBqlRequest,
833
+ ...currentSchema.thingType === "entity" ? { $entity: currentSchema } : {},
834
+ ...currentSchema.thingType === "relation" ? { $relation: currentSchema } : {},
835
+ ...localFilters ? { $localFilters: localFilters } : {},
836
+ ...nestedFilters ? { $nestedFilters: nestedFilters } : {}
837
+ }
838
+ };
839
+ };
840
+
841
+ // src/pipeline/transaction/runTQLQuery.ts
842
+ var import_typedb_client = require("typedb-client");
843
+ var runTQLQuery = async (req, res) => {
844
+ const { dbHandles, bqlRequest, tqlRequest, config } = req;
845
+ if (!bqlRequest) {
846
+ throw new Error("BQL request not parsed");
847
+ }
848
+ if (!tqlRequest) {
849
+ throw new Error("TQL request not built");
850
+ }
851
+ if (!tqlRequest.entity) {
852
+ throw new Error("BQL request error, no entities");
853
+ }
854
+ const { query } = bqlRequest;
855
+ if (!query) {
856
+ throw new Error("BQL request is not a query");
857
+ }
858
+ const singleHandlerV0 = config.dbConnectors[0].id;
859
+ const session = dbHandles.typeDB.get(singleHandlerV0)?.session;
860
+ if (!session?.isOpen()) {
861
+ throw new Error("Session is closed");
862
+ }
863
+ const transaction = await session.transaction(import_typedb_client.TransactionType.READ);
864
+ if (!transaction) {
865
+ throw new Error("Can't create transaction");
866
+ }
867
+ const entityStream = transaction.query.matchGroup(tqlRequest.entity);
868
+ const rolesStreams = tqlRequest.roles?.map((role) => ({
869
+ ...role,
870
+ stream: transaction.query.matchGroup(role.request)
871
+ }));
872
+ const relationStreams = tqlRequest.relations?.map((relation) => ({
873
+ ...relation,
874
+ stream: transaction.query.matchGroup(relation.request)
875
+ }));
876
+ const entityConceptMapGroups = await entityStream.collect();
877
+ const rolesConceptMapGroups = await Promise.all(
878
+ rolesStreams?.map(async (role) => ({
879
+ path: role.path,
880
+ ownerPath: role.owner,
881
+ conceptMapGroups: await role.stream.collect()
882
+ })) || []
883
+ );
884
+ const relationConceptMapGroups = await Promise.all(
885
+ relationStreams?.map(async (relation) => ({
886
+ relation: relation.relation,
887
+ entity: relation.entity,
888
+ conceptMapGroups: await relation.stream.collect()
889
+ })) || []
890
+ );
891
+ await transaction.close();
892
+ res.rawTqlRes = {
893
+ entity: entityConceptMapGroups,
894
+ ...rolesConceptMapGroups?.length && { roles: rolesConceptMapGroups },
895
+ ...relationConceptMapGroups?.length && {
896
+ relations: relationConceptMapGroups
897
+ }
898
+ };
899
+ };
900
+
901
+ // src/pipeline/control/dispatchPipeline.ts
902
+ var dispatchPipeline = async (req, res) => {
903
+ const { bqlRequest, schema } = req;
904
+ const { cache } = res;
905
+ if (!bqlRequest) {
906
+ throw new Error("BQL request not parsed");
907
+ }
908
+ if (!cache) {
909
+ throw new Error("Cache not initialized");
910
+ }
911
+ const { query } = bqlRequest;
912
+ if (!query) {
913
+ return;
914
+ }
915
+ const { $fields } = query;
916
+ if (!("$entity" in query) && !("$relation" in query)) {
917
+ throw new Error("Node attributes not supported");
918
+ }
919
+ const $thing = "$entity" in query ? query.$entity : query.$relation;
920
+ if (!$fields || !Array.isArray($fields)) {
921
+ return;
922
+ }
923
+ const expandedLinkAndRoleFields = $fields.filter(
924
+ (f) => typeof f !== "string" && f.$path
925
+ );
926
+ const nestedThingsByLF = $thing.linkFields?.filter(
927
+ (linkField) => expandedLinkAndRoleFields.findIndex(
928
+ (expanded) => expanded.$path === linkField.path
929
+ ) !== -1
930
+ ).flatMap((linkField) => linkField.oppositeLinkFieldsPlayedBy) || [];
931
+ const nestedThingsByRF = "roles" in $thing ? (0, import_radash6.listify)($thing.roles, (k, v) => {
932
+ if (expandedLinkAndRoleFields.findIndex(
933
+ (expanded) => expanded.$path === k
934
+ ) !== -1) {
935
+ return v;
936
+ }
937
+ return null;
938
+ }).flatMap((role) => role?.playedBy).filter((x) => x) : [];
939
+ const nestedThings = [...nestedThingsByLF, ...nestedThingsByRF];
940
+ const nextOps = nestedThings?.map((linkField) => {
941
+ if (!linkField)
942
+ return null;
943
+ const { thing } = linkField;
944
+ const result = cache.entities.get(thing);
945
+ const resultIds = [...result?.keys() || []];
946
+ const currentSchema = schema.entities[thing] ? { ...schema.entities[thing], thingType: "entity" } : { ...schema.relations[thing], thingType: "relation" };
947
+ const $FieldsObj = $fields.find(
948
+ (x) => typeof x === "object" && x.$path === linkField.plays
949
+ );
950
+ const localIdsTemp = $FieldsObj?.$id;
951
+ const localIds = !localIdsTemp ? [] : Array.isArray(localIdsTemp) ? localIdsTemp : [localIdsTemp];
952
+ const localFilters = $FieldsObj?.$filter;
953
+ const commonIds = !localIds.length ? resultIds : localIds.filter((id) => resultIds.includes(id));
954
+ const newBqlRequest = {
955
+ query: {
956
+ $id: commonIds,
957
+ $fields: $FieldsObj?.$fields,
958
+ ...currentSchema.thingType === "entity" ? { $entity: currentSchema } : {},
959
+ ...currentSchema.thingType === "relation" ? { $relation: currentSchema } : {},
960
+ ...localFilters ? { $localFilters: localFilters } : {}
961
+ }
962
+ };
963
+ return {
964
+ req: {
965
+ ...req,
966
+ bqlRequest: newBqlRequest
967
+ },
968
+ res,
969
+ pipeline: [buildTQLQuery, runTQLQuery, parseTQLRes, dispatchPipeline]
970
+ };
971
+ }).filter((x) => x);
972
+ if (nextOps?.length) {
973
+ return nextOps;
974
+ }
975
+ };
976
+
977
+ // src/pipeline/preprocess/buildTQLMutation.ts
978
+ var import_radash7 = require("radash");
979
+ var buildTQLMutation = async (req) => {
980
+ const { bqlRequest, schema } = req;
981
+ if (!bqlRequest) {
982
+ throw new Error("BQL request not parsed");
983
+ }
984
+ const { mutation } = bqlRequest;
985
+ if (!mutation) {
986
+ throw new Error("BQL request is not a mutation");
987
+ }
988
+ const nodeToTypeQL = (node) => {
989
+ const op = node.$op;
990
+ const id = node.$tempId || node.$id;
991
+ const currentSchema = getCurrentSchema(schema, node);
992
+ const thingDbPath = currentSchema.defaultDBConnector?.path || node.$entity || node.$relation;
993
+ const { idFields } = currentSchema;
994
+ if (!idFields)
995
+ throw new Error("no idFields");
996
+ const idField = idFields[0];
997
+ const attributes = (0, import_radash7.listify)(node, (k, v) => {
998
+ if (k.startsWith("$") || k === idField || !v)
999
+ return "";
1000
+ const currentDataField = currentSchema.dataFields?.find(
1001
+ (x) => x.path === k
1002
+ );
1003
+ const fieldDbPath = currentDataField?.path;
1004
+ if (!fieldDbPath) {
1005
+ return ``;
1006
+ }
1007
+ const dbField = currentDataField.shared ? fieldDbPath : `${thingDbPath}\xB7${fieldDbPath}`;
1008
+ if (["TEXT", "ID", "EMAIL"].includes(currentDataField.contentType)) {
1009
+ return `has ${dbField} '${v}'`;
1010
+ }
1011
+ if (["NUMBER"].includes(currentDataField.contentType)) {
1012
+ return `has ${dbField} ${v}`;
1013
+ }
1014
+ if (currentDataField.contentType === "DATE") {
1015
+ if (Number.isNaN(v.valueOf())) {
1016
+ throw new Error("Invalid format, Nan Date");
1017
+ }
1018
+ if (v instanceof Date) {
1019
+ return `has ${dbField} ${v.toISOString().replace("Z", "")}`;
1020
+ }
1021
+ return `has ${dbField} ${new Date(v).toISOString().replace("Z", "")}`;
1022
+ }
1023
+ throw new Error(
1024
+ `Unsupported contentType ${currentDataField.contentType}`
1025
+ );
1026
+ }).filter((x) => x);
1027
+ const attributesVar = `$${id}-atts`;
1028
+ const matchAttributes = (0, import_radash7.listify)(node, (k, v) => {
1029
+ if (k.startsWith("$") || k === idField || !v)
1030
+ return "";
1031
+ const currentDataField = currentSchema.dataFields?.find(
1032
+ (x) => x.path === k
1033
+ );
1034
+ const fieldDbPath = currentDataField?.path;
1035
+ if (!fieldDbPath) {
1036
+ return ``;
1037
+ }
1038
+ const dbField = currentDataField.shared ? fieldDbPath : `${thingDbPath}\xB7${fieldDbPath}`;
1039
+ return `{${attributesVar} isa ${dbField};}`;
1040
+ }).filter((x) => x);
1041
+ const idFieldValue = node[idField] || node.$id;
1042
+ const idDataField = currentSchema.dataFields?.find(
1043
+ (x) => x.path === idField
1044
+ );
1045
+ const idDefaultValue = node.$op === "create" ? idDataField?.default?.value() : null;
1046
+ const idValue = idFieldValue || idDefaultValue;
1047
+ const idAttributes = idValue ? [`has ${idField} '${idValue}'`] : [];
1048
+ const allAttributes = [...idAttributes, ...attributes].filter((x) => x).join(",");
1049
+ const getDeletionMatch = () => {
1050
+ if (node.$tempId)
1051
+ return "";
1052
+ if (op === "delete" || op === "unlink" || op === "noop") {
1053
+ return `$${id} isa ${[thingDbPath, ...idAttributes].filter((x) => x).join(",")};`;
1054
+ }
1055
+ if (op === "update") {
1056
+ if (!matchAttributes.length)
1057
+ throw new Error("update without attributes");
1058
+ return `$${id} isa ${thingDbPath}, ${idAttributes[0]}, has ${attributesVar};
1059
+ ${matchAttributes.join(" or ")};
1060
+ `;
1061
+ }
1062
+ return "";
1063
+ };
1064
+ const getInsertionMatch = () => {
1065
+ if (node.$tempId)
1066
+ return "";
1067
+ if (op === "update" || op === "link" || op === "noop") {
1068
+ return `$${id} isa ${[thingDbPath, ...idAttributes].filter((x) => x).join(",")};`;
1069
+ }
1070
+ return "";
1071
+ };
1072
+ if (node.$entity || node.$relation) {
1073
+ return {
1074
+ op,
1075
+ deletionMatch: getDeletionMatch(),
1076
+ insertionMatch: getInsertionMatch(),
1077
+ insertion: op === "create" ? `$${id} isa ${[thingDbPath, allAttributes].filter((x) => x).join(",")};` : op === "update" && attributes.length ? `$${id} ${attributes.join(",")};` : "",
1078
+ deletion: op === "delete" ? `$${id} isa ${thingDbPath};` : op === "update" && matchAttributes.length ? `$${id} has ${attributesVar};` : ""
1079
+ };
1080
+ }
1081
+ throw new Error("in attributes");
1082
+ };
1083
+ const edgeToTypeQL = (node) => {
1084
+ const op = node.$op;
1085
+ const id = node.$tempId || node.$id;
1086
+ const currentSchema = getCurrentSchema(schema, node);
1087
+ const relationDbPath = currentSchema.defaultDBConnector?.path || node.$relation;
1088
+ const roleFields = "roles" in currentSchema ? (0, import_radash7.listify)(currentSchema.roles, (k) => k) : [];
1089
+ const roleDbPaths = node.$relation && "roles" in currentSchema && (0, import_radash7.mapEntries)(currentSchema.roles, (k, v) => [k, v.dbConnector?.path || k]);
1090
+ const fromRoleFields = (0, import_radash7.listify)(node, (k, v) => {
1091
+ if (!roleFields.includes(k))
1092
+ return null;
1093
+ if (!("roles" in currentSchema)) {
1094
+ throw new Error("This should have roles! ");
1095
+ }
1096
+ const roleDbPath = roleDbPaths[k];
1097
+ if (Array.isArray(v)) {
1098
+ return v.map((x) => ({ path: roleDbPath, id: x }));
1099
+ }
1100
+ return { path: roleDbPath, id: v };
1101
+ }).filter((x) => x).flat();
1102
+ const fromRoleFieldsTql = fromRoleFields.map(
1103
+ (x) => x ? `${x.path}: $${x.id}` : ""
1104
+ );
1105
+ const roles = fromRoleFields.length > 0 ? `( ${fromRoleFieldsTql.join(" , ")} )` : "";
1106
+ const relationTql = !roles ? "" : `$${id} ${roles} ${node[Symbol.for("edgeType")] === "linkField" ? `isa ${relationDbPath}` : ""}`;
1107
+ const getInsertions = () => {
1108
+ if (!relationTql)
1109
+ return "";
1110
+ if (op === "link")
1111
+ return `${relationTql};`;
1112
+ if (op === "create")
1113
+ return `${relationTql}, has id '${id}';`;
1114
+ return "";
1115
+ };
1116
+ const getDeletions = () => {
1117
+ if (!relationTql)
1118
+ return "";
1119
+ if (op === "unlink")
1120
+ return `${relationTql};`;
1121
+ return "";
1122
+ };
1123
+ return {
1124
+ deletionMatch: "",
1125
+ insertionMatch: "",
1126
+ deletion: getDeletions(),
1127
+ insertion: getInsertions(),
1128
+ op: ""
1129
+ };
1130
+ };
1131
+ const toTypeQL = (nodes, mode) => {
1132
+ const typeQL = mode === "edges" ? edgeToTypeQL : nodeToTypeQL;
1133
+ if (Array.isArray(nodes)) {
1134
+ return nodes.map((x) => {
1135
+ const { insertionMatch: insertionMatch2, deletionMatch: deletionMatch2, insertion: insertion2, deletion: deletion2 } = typeQL(x);
1136
+ return (0, import_radash7.shake)(
1137
+ { insertionMatch: insertionMatch2, deletionMatch: deletionMatch2, insertion: insertion2, deletion: deletion2 },
1138
+ (z) => !z
1139
+ );
1140
+ }).filter((y) => y);
1141
+ }
1142
+ const { insertionMatch, deletionMatch, insertion, deletion } = typeQL(nodes);
1143
+ return (0, import_radash7.shake)(
1144
+ { insertionMatch, deletionMatch, insertion, deletion },
1145
+ (z) => !z
1146
+ );
1147
+ };
1148
+ const nodeOperations = toTypeQL(mutation.things);
1149
+ const arrayNodeOperations = Array.isArray(nodeOperations) ? nodeOperations : [nodeOperations];
1150
+ const edgeOperations = toTypeQL(mutation.edges, "edges");
1151
+ const arrayEdgeOperations = Array.isArray(edgeOperations) ? edgeOperations : [edgeOperations];
1152
+ const allOperations = [...arrayNodeOperations, ...arrayEdgeOperations];
1153
+ const tqlRequest = (0, import_radash7.shake)(
1154
+ {
1155
+ insertionMatches: allOperations.map((x) => x.insertionMatch).join(" ").trim(),
1156
+ deletionMatches: allOperations.map((x) => x.deletionMatch).join(" ").trim(),
1157
+ insertions: allOperations.map((x) => x.insertion).join(" ").trim(),
1158
+ deletions: allOperations.map((x) => x.deletion).join(" ").trim()
1159
+ },
1160
+ (x) => !x
1161
+ );
1162
+ req.tqlRequest = tqlRequest;
1163
+ };
1164
+
1165
+ // src/pipeline/preprocess/parseBQLMutation.ts
1166
+ var import_immer3 = __toESM(require("immer"));
1167
+ var import_object_traversal3 = require("object-traversal");
1168
+ var import_radash8 = require("radash");
1169
+ var import_uuid = require("uuid");
1170
+ var parseBQLMutation = async (req) => {
1171
+ const { rawBqlRequest, schema } = req;
1172
+ const stringToObjects = (blocks) => {
1173
+ return (0, import_immer3.default)(
1174
+ blocks,
1175
+ (draft) => (0, import_object_traversal3.traverse)(draft, ({ value: val, meta }) => {
1176
+ if ((0, import_radash8.isObject)(val)) {
1177
+ if (val.$arrayOp) {
1178
+ throw new Error("Array op not supported yet");
1179
+ }
1180
+ const value = val;
1181
+ const currentSchema = getCurrentSchema(schema, val);
1182
+ if (!currentSchema) {
1183
+ throw new Error(
1184
+ `Schema not found for ${val.$entity || val.$relation}`
1185
+ );
1186
+ }
1187
+ value[Symbol.for("thingType")] = currentSchema.thingType;
1188
+ value[Symbol.for("schema")] = currentSchema;
1189
+ value[Symbol.for("dbId")] = currentSchema.defaultDBConnector.id;
1190
+ const { linkFields, roleFields } = getCurrentFields(currentSchema);
1191
+ const currentPath = meta.nodePath;
1192
+ [
1193
+ ...linkFields.map((x) => ({ fieldType: "linkField", path: x })),
1194
+ ...roleFields.map((x) => ({ fieldType: "roleField", path: x }))
1195
+ ]?.forEach((currentField) => {
1196
+ const currentLinkFieldSchema = currentSchema.linkFields?.find(
1197
+ (x) => x.path === currentField.path
1198
+ );
1199
+ const currentValue = value[currentField.path];
1200
+ if (currentValue === void 0)
1201
+ return;
1202
+ const currentRoleFieldSchema = "roles" in currentSchema ? oFind(
1203
+ currentSchema.roles,
1204
+ (k) => k === currentField.path
1205
+ ) : null;
1206
+ const currentFieldSchema = currentLinkFieldSchema || currentRoleFieldSchema;
1207
+ if (currentRoleFieldSchema && [...new Set(currentRoleFieldSchema.playedBy?.map((x) => x.thing))].length !== 1) {
1208
+ throw new Error(
1209
+ `Field: ${currentField.path} - If a role can be played by multiple things, you must specify the thing in the mutation: ${JSON.stringify(
1210
+ currentRoleFieldSchema.playedBy
1211
+ )}. Schema: ${JSON.stringify(currentFieldSchema)}`
1212
+ );
1213
+ }
1214
+ const currentEdgeSchema = currentRoleFieldSchema?.playedBy ? currentRoleFieldSchema?.playedBy[0] : currentLinkFieldSchema;
1215
+ const getCurrentRelation = () => {
1216
+ if (currentFieldSchema && "relation" in currentFieldSchema && currentEdgeSchema?.relation === value.$relation) {
1217
+ return "$self";
1218
+ }
1219
+ if (currentEdgeSchema?.relation) {
1220
+ return currentEdgeSchema?.relation;
1221
+ }
1222
+ return "$self";
1223
+ };
1224
+ const relation = getCurrentRelation();
1225
+ const relationSchema = relation === "$self" ? currentSchema : schema.relations[relation];
1226
+ const currentFieldRole = oFind(
1227
+ relationSchema.roles,
1228
+ (k, _v) => k === currentField.path
1229
+ );
1230
+ if (currentFieldRole?.playedBy?.length === 0)
1231
+ throw new Error(
1232
+ `unused role: ${currentPath}.${currentField.path}`
1233
+ );
1234
+ if (!currentFieldSchema) {
1235
+ throw new Error(`Field ${currentField.path} not found in schema`);
1236
+ }
1237
+ const oppositeFields = currentLinkFieldSchema?.oppositeLinkFieldsPlayedBy || currentRoleFieldSchema?.playedBy;
1238
+ if (!oppositeFields) {
1239
+ throw new Error(
1240
+ `No opposite fields found for ${JSON.stringify(
1241
+ currentFieldSchema
1242
+ )}`
1243
+ );
1244
+ }
1245
+ if ([...new Set(oppositeFields?.map((x) => x.thing))].length > 1)
1246
+ throw new Error(
1247
+ `Field: ${currentField.path} - If a role can be played by multiple things, you must specify the thing in the mutation: ${JSON.stringify(
1248
+ oppositeFields
1249
+ )}. Schema: ${JSON.stringify(currentFieldSchema)}`
1250
+ );
1251
+ if (currentValue === null) {
1252
+ value[currentField.path] = { $op: "unlink" };
1253
+ }
1254
+ if (currentFieldSchema.cardinality === "ONE" && Array.isArray(currentValue)) {
1255
+ throw new Error(
1256
+ `Can't have an array in a cardinality === ONE link field`
1257
+ );
1258
+ }
1259
+ if (currentValue.$entity || currentValue.$relation)
1260
+ return;
1261
+ const childrenLinkField = oppositeFields[0];
1262
+ const childrenThingObj = {
1263
+ [`$${childrenLinkField.thingType}`]: childrenLinkField.thing,
1264
+ [Symbol.for("relation")]: relation,
1265
+ [Symbol.for("edgeType")]: "plays" in currentFieldSchema ? "linkField" : "roleField",
1266
+ [Symbol.for("parent")]: {
1267
+ path: currentPath,
1268
+ id: value.$id || value.$tempId,
1269
+ links: oppositeFields
1270
+ },
1271
+ [Symbol.for("role")]: childrenLinkField.plays,
1272
+ [Symbol.for("oppositeRole")]: "plays" in currentFieldSchema ? currentFieldSchema.plays : void 0
1273
+ };
1274
+ if ((0, import_radash8.isObject)(currentValue)) {
1275
+ if (currentSchema.thingType === "relation" && currentValue.$tempId) {
1276
+ value[currentField.path] = currentValue.$tempId;
1277
+ } else {
1278
+ value[currentField.path] = {
1279
+ ...childrenThingObj,
1280
+ ...currentValue
1281
+ };
1282
+ }
1283
+ }
1284
+ if (Array.isArray(currentValue) && currentValue.every((x) => (0, import_radash8.isObject)(x))) {
1285
+ value[currentField.path] = currentValue.map((y) => ({
1286
+ ...childrenThingObj,
1287
+ ...y
1288
+ }));
1289
+ }
1290
+ if (typeof currentValue === "string") {
1291
+ value[currentField.path] = {
1292
+ ...childrenThingObj,
1293
+ $op: "link",
1294
+ $id: currentValue
1295
+ };
1296
+ }
1297
+ if (Array.isArray(currentValue) && currentValue.every((x) => typeof x === "string")) {
1298
+ value[currentField.path] = currentValue.map((y) => ({
1299
+ ...childrenThingObj,
1300
+ $op: "link",
1301
+ $id: y
1302
+ }));
1303
+ }
1304
+ });
1305
+ const nodePathArray = meta.nodePath?.split(".");
1306
+ const notRoot = nodePathArray?.filter((x) => Number.isNaN(parseInt(x, 10))).join(".");
1307
+ if (!notRoot && !value.$entity && !value.$relation) {
1308
+ throw new Error("Root things must specify $entity or $relation");
1309
+ }
1310
+ if (!notRoot) {
1311
+ }
1312
+ }
1313
+ })
1314
+ );
1315
+ };
1316
+ const withObjects = stringToObjects(rawBqlRequest);
1317
+ const fillBlocks = (blocks) => {
1318
+ return (0, import_immer3.default)(
1319
+ blocks,
1320
+ (draft) => (0, import_object_traversal3.traverse)(
1321
+ draft,
1322
+ ({ parent, key, value: val, meta }) => {
1323
+ if ((0, import_radash8.isObject)(val)) {
1324
+ if (Object.keys(val).length === 0) {
1325
+ throw new Error("Empty object!");
1326
+ }
1327
+ const value = val;
1328
+ const nodePathArray = meta.nodePath?.split(".");
1329
+ const notRoot = nodePathArray?.filter((x) => Number.isNaN(parseInt(x, 10))).join(".");
1330
+ const currentSchema = getCurrentSchema(schema, value);
1331
+ const { unidentifiedFields, dataFields, roleFields, linkFields } = getCurrentFields(currentSchema, value);
1332
+ const hasUpdatedDataFields = Object.keys(value).some(
1333
+ (x) => dataFields?.includes(x)
1334
+ );
1335
+ const hasUpdatedChildren = Object.keys(value).some(
1336
+ (x) => [...roleFields, ...linkFields]?.includes(x)
1337
+ );
1338
+ const getOp = () => {
1339
+ if (value.$op)
1340
+ return value.$op;
1341
+ if ((value.$id || value.$filter) && hasUpdatedDataFields)
1342
+ return "update";
1343
+ if ((value.$id || value.$filter) && notRoot && !hasUpdatedDataFields && !hasUpdatedChildren)
1344
+ return "link";
1345
+ if (!value.$filter && !value.$id)
1346
+ return "create";
1347
+ if ((value.$id || value.$filter) && !hasUpdatedDataFields && hasUpdatedChildren)
1348
+ return "noop";
1349
+ throw new Error("Wrong op");
1350
+ };
1351
+ if (!value.$op)
1352
+ value.$op = getOp();
1353
+ if (!parent)
1354
+ value.$parentKey = "";
1355
+ if (!(value.$id || value.$tempId || value.$filter) && ["delete", "link", "update", "unlink"].includes(value.$op)) {
1356
+ throw new Error(
1357
+ "Targeted operations (update, delete, link & unlink) require an $id or a $filter"
1358
+ );
1359
+ }
1360
+ if (typeof parent === "object") {
1361
+ const ArParent = Array.isArray(parent);
1362
+ if (ArParent)
1363
+ value[Symbol.for("index")] = key;
1364
+ value[Symbol.for("path")] = meta.nodePath;
1365
+ value[Symbol.for("isRoot")] = !notRoot;
1366
+ value[Symbol.for("depth")] = notRoot?.split(".").length;
1367
+ }
1368
+ if (!value.$entity && !value.$relation) {
1369
+ throw new Error(
1370
+ `Node ${JSON.stringify(value)} without $entity/$relation`
1371
+ );
1372
+ }
1373
+ const { idFields, computedFields } = currentSchema;
1374
+ if (!idFields)
1375
+ throw new Error("No idFields found");
1376
+ const idField = idFields[0];
1377
+ if (value[idField] && !value.$id) {
1378
+ value.$id = value[idField];
1379
+ }
1380
+ const filledFields = (0, import_radash8.listify)(
1381
+ value,
1382
+ (attKey, v) => v ? attKey : void 0
1383
+ );
1384
+ const missingComputedFields = computedFields.filter(
1385
+ (x) => !filledFields.includes(x)
1386
+ );
1387
+ missingComputedFields.forEach((fieldPath) => {
1388
+ const currentFieldDef = currentSchema.dataFields?.find(
1389
+ (x) => x.path === fieldPath
1390
+ );
1391
+ const currentLinkDef = currentSchema.linkFields?.find(
1392
+ (x) => x.path === fieldPath
1393
+ );
1394
+ const currentLinkedDef = currentLinkDef?.oppositeLinkFieldsPlayedBy[0];
1395
+ const currentRoleDef = "roles" in currentSchema ? oFind(currentSchema.roles, (k, _v) => k === fieldPath) : void 0;
1396
+ const currentDef = currentFieldDef || currentLinkedDef || currentRoleDef;
1397
+ if (!currentDef) {
1398
+ throw new Error(`no field Def for ${fieldPath}`);
1399
+ }
1400
+ if (fieldPath === idField && value.$op === "create" && !value[fieldPath]) {
1401
+ const defaultValue = "default" in currentDef ? currentDef.default?.value() : void 0;
1402
+ if (!defaultValue) {
1403
+ throw new Error(`No default value for ${fieldPath}`);
1404
+ }
1405
+ value[fieldPath] = defaultValue;
1406
+ value.$id = defaultValue;
1407
+ }
1408
+ });
1409
+ if (unidentifiedFields.length > 0) {
1410
+ throw new Error(
1411
+ `Unknown fields: [${unidentifiedFields.join(
1412
+ ","
1413
+ )}] in ${JSON.stringify(value)}`
1414
+ );
1415
+ }
1416
+ }
1417
+ }
1418
+ )
1419
+ );
1420
+ };
1421
+ const filledBQLMutation = fillBlocks(withObjects);
1422
+ const listNodes = (blocks) => {
1423
+ const nodes = [];
1424
+ const edges = [];
1425
+ const listOp = ({ value }) => {
1426
+ if (value.$entity || value.$relation) {
1427
+ if (!value.$id && !["link", "unlink"].includes(value.$op)) {
1428
+ throw new Error(
1429
+ "An id must be specified either in the mutation or has tu have a default value in the schema"
1430
+ );
1431
+ }
1432
+ const currentThingSchema = getCurrentSchema(schema, value);
1433
+ const { dataFields: dataFieldPaths, roleFields: roleFieldPaths } = getCurrentFields(currentThingSchema);
1434
+ const dataObj = {
1435
+ ...value.$entity && { $entity: value.$entity },
1436
+ ...value.$relation && { $relation: value.$relation },
1437
+ ...value.$id && { $id: value.$id },
1438
+ ...value.$tempId && { $tempId: value.$tempId },
1439
+ ...(0, import_radash8.shake)((0, import_radash8.pick)(value, dataFieldPaths || [""])),
1440
+ $op: value.$op,
1441
+ [Symbol.for("dbId")]: currentThingSchema.defaultDBConnector.id,
1442
+ [Symbol.for("isRoot")]: value[Symbol.for("isRoot")]
1443
+ };
1444
+ if (value.$op === "create" || value.$op === "update" || value.$op === "delete") {
1445
+ nodes.push(dataObj);
1446
+ } else {
1447
+ nodes.push({ ...dataObj, $op: "noop" });
1448
+ }
1449
+ if (value[Symbol.for("relation")] && value[Symbol.for("edgeType")] === "linkField") {
1450
+ if (value.$op === "link" || value.$op === "unlink") {
1451
+ if (value.$id || value.$filter) {
1452
+ if (value.$tempId) {
1453
+ throw new Error(
1454
+ "can't specify a existing and a new element at once. Use an id/filter or a tempId"
1455
+ );
1456
+ }
1457
+ nodes.push({ ...value, $op: "noop" });
1458
+ }
1459
+ }
1460
+ const ownRelation = value[Symbol.for("relation")] === value.$relation;
1461
+ if (ownRelation && !(value.$id || value.$tempId)) {
1462
+ throw new Error("No id or tempId found for complex link");
1463
+ }
1464
+ const linkTempId = ownRelation ? value.$id || value.$tempId : (0, import_uuid.v4)();
1465
+ const parentMeta = value[Symbol.for("parent")];
1466
+ const parentPath = parentMeta.path;
1467
+ const parentNode = !parentPath ? blocks : (0, import_object_traversal3.getNodeByPath)(blocks, parentPath);
1468
+ const parentId = parentNode.$id || parentNode.$tempId;
1469
+ if (!parentId)
1470
+ throw new Error("No parent id found");
1471
+ if (value[Symbol.for("relation")] === "$self")
1472
+ return;
1473
+ const getLinkObjOp = () => {
1474
+ if (value.$op === "unlink" || value.$op === "delete") {
1475
+ if (ownRelation)
1476
+ return "unlink";
1477
+ return "delete";
1478
+ }
1479
+ if (value.$op === "link" || value.$op === "create") {
1480
+ if (ownRelation)
1481
+ return "link";
1482
+ return "create";
1483
+ }
1484
+ return "noop";
1485
+ };
1486
+ const linkObj = {
1487
+ $relation: value[Symbol.for("relation")],
1488
+ $op: getLinkObjOp(),
1489
+ ...value.$op === "unlink" ? { $tempId: linkTempId } : { $id: linkTempId },
1490
+ [value[Symbol.for("role")]]: value.$tempId || value.$id,
1491
+ [value[Symbol.for("oppositeRole")]]: parentId,
1492
+ [Symbol.for("isRoot")]: false,
1493
+ [Symbol.for("dbId")]: schema.relations[value[Symbol.for("relation")]].defaultDBConnector.id,
1494
+ [Symbol.for("edgeType")]: "linkField"
1495
+ };
1496
+ edges.push(linkObj);
1497
+ }
1498
+ if (value.$relation) {
1499
+ const val = value;
1500
+ if (!val.$id && !["link", "unlink"].includes(value.$op)) {
1501
+ throw new Error(
1502
+ "An id must be specified either in the mutation or has tu have a default value in the schema"
1503
+ );
1504
+ }
1505
+ const rolesObjFiltered = oFilter(
1506
+ val,
1507
+ (k, _v) => roleFieldPaths.includes(k)
1508
+ );
1509
+ const rolesObjOnlyIds = (0, import_radash8.mapEntries)(rolesObjFiltered, (k, v) => {
1510
+ return [k, v.$id || v];
1511
+ });
1512
+ const objWithMetaDataOnly = oFilter(val, (k, _v) => {
1513
+ return k.startsWith("$") || k.startsWith("Symbol");
1514
+ });
1515
+ if (Object.keys(rolesObjFiltered).filter((x) => !x.startsWith("$")).length > 0) {
1516
+ if (val.$op === "create" || val.$op === "delete") {
1517
+ const getEdgeOp = () => {
1518
+ if (val.$op === "create")
1519
+ return "link";
1520
+ if (val.$op === "delete")
1521
+ return "unlink";
1522
+ throw new Error("Unsupported parent of edge op");
1523
+ };
1524
+ edges.push({
1525
+ ...objWithMetaDataOnly,
1526
+ $relation: val.$relation,
1527
+ $op: getEdgeOp(),
1528
+ ...rolesObjOnlyIds,
1529
+ [Symbol.for("dbId")]: currentThingSchema.defaultDBConnector.id,
1530
+ [Symbol.for("info")]: "coming from created or deleted relation"
1531
+ });
1532
+ return;
1533
+ }
1534
+ if (val.$op === "noop") {
1535
+ const rolesWithLinks = oFilter(
1536
+ rolesObjOnlyIds,
1537
+ (_k, v) => v.some(
1538
+ (x) => x.$op === "link" || x.$op === "create"
1539
+ )
1540
+ );
1541
+ const rolesWithLinksFiltered = (0, import_radash8.mapEntries)(
1542
+ rolesWithLinks,
1543
+ (k, v) => [
1544
+ k,
1545
+ v.filter((x) => x.$op === "link" || x.$op === "create").map((y) => y.$id)
1546
+ ]
1547
+ );
1548
+ const rolesWithUnlinks = oFilter(
1549
+ rolesObjOnlyIds,
1550
+ (_k, v) => v.some(
1551
+ (x) => x.$op === "unlink" || x.$op === "delete"
1552
+ )
1553
+ );
1554
+ const rolesWithUnlinksFiltered = (0, import_radash8.mapEntries)(
1555
+ rolesWithUnlinks,
1556
+ (k, v) => [
1557
+ k,
1558
+ v.filter((x) => x.$op === "unlink" || x.$op === "delete").map((y) => y.$id)
1559
+ ]
1560
+ );
1561
+ const rolesWithReplaces = {};
1562
+ [
1563
+ { op: "link", obj: rolesWithLinksFiltered },
1564
+ { op: "unlink", obj: rolesWithUnlinksFiltered },
1565
+ { op: "replace", obj: rolesWithReplaces }
1566
+ ].forEach((x) => {
1567
+ if (Object.keys(x.obj).length) {
1568
+ edges.push({
1569
+ ...objWithMetaDataOnly,
1570
+ $relation: val.$relation,
1571
+ $op: x.op,
1572
+ ...x.obj,
1573
+ [Symbol.for("dbId")]: currentThingSchema.defaultDBConnector.id,
1574
+ [Symbol.for("info")]: "updating roleFields"
1575
+ });
1576
+ }
1577
+ });
1578
+ }
1579
+ }
1580
+ }
1581
+ }
1582
+ };
1583
+ (0, import_object_traversal3.traverse)(blocks, listOp);
1584
+ return [nodes, edges];
1585
+ };
1586
+ const [parsedThings, parsedEdges] = listNodes(filledBQLMutation);
1587
+ const mergedEdges = parsedEdges.reduce((acc, curr) => {
1588
+ const existingEdge = acc.find(
1589
+ (r) => r.$id === curr.$id && r.$relation === curr.$relation
1590
+ );
1591
+ if (existingEdge) {
1592
+ const newRelation = {
1593
+ ...existingEdge,
1594
+ ...curr
1595
+ };
1596
+ const newAcc = acc.filter(
1597
+ (r) => r.$id !== curr.$id || r.$relation !== curr.$relation
1598
+ );
1599
+ return [...newAcc, newRelation];
1600
+ }
1601
+ return [...acc, curr];
1602
+ }, []);
1603
+ req.bqlRequest = {
1604
+ mutation: {
1605
+ things: parsedThings,
1606
+ edges: mergedEdges
1607
+ }
1608
+ };
1609
+ };
1610
+
1611
+ // src/pipeline/transaction/runTQLMutation.ts
1612
+ var import_typedb_client2 = require("typedb-client");
1613
+ var runTQLMutation = async (req, res) => {
1614
+ const { dbHandles, tqlRequest, bqlRequest, config } = req;
1615
+ if (!tqlRequest) {
1616
+ throw new Error("TQL request not built");
1617
+ }
1618
+ if (!(tqlRequest.deletions && tqlRequest.deletionMatches || tqlRequest.insertions)) {
1619
+ throw new Error("TQL request error, no things");
1620
+ }
1621
+ if (!bqlRequest?.mutation) {
1622
+ throw new Error("BQL mutation not parsed");
1623
+ }
1624
+ const singleHandlerV0 = config.dbConnectors[0].id;
1625
+ const session = dbHandles.typeDB.get(singleHandlerV0)?.session;
1626
+ if (!session?.isOpen()) {
1627
+ throw new Error("Session is closed");
1628
+ }
1629
+ const mutateTransaction = await session.transaction(import_typedb_client2.TransactionType.WRITE);
1630
+ if (!mutateTransaction) {
1631
+ throw new Error("Can't create transaction");
1632
+ }
1633
+ const tqlDeletion = tqlRequest.deletionMatches && tqlRequest.deletions && `match ${tqlRequest.deletionMatches} delete ${tqlRequest.deletions}`;
1634
+ const tqlInsertion = tqlRequest.insertions && `${tqlRequest.insertionMatches ? `match ${tqlRequest.insertionMatches}` : ""} insert ${tqlRequest.insertions}`;
1635
+ if (tqlDeletion)
1636
+ mutateTransaction.query.delete(tqlDeletion);
1637
+ const insertionsStream = tqlInsertion && mutateTransaction.query.insert(tqlInsertion);
1638
+ const insertionsRes = insertionsStream ? await insertionsStream.collect() : void 0;
1639
+ await mutateTransaction.commit();
1640
+ await mutateTransaction.close();
1641
+ res.rawTqlRes = { insertions: insertionsRes };
1642
+ };
1643
+
1644
+ // src/pipeline/pipeline.ts
1645
+ var Pipelines = {
1646
+ query: [
1647
+ parseBQLQuery,
1648
+ buildTQLQuery,
1649
+ runTQLQuery,
1650
+ parseTQLRes,
1651
+ dispatchPipeline
1652
+ ],
1653
+ mutation: [parseBQLMutation, buildTQLMutation, runTQLMutation, parseTQLRes]
1654
+ };
1655
+ var finalPipeline = [buildBQLTree];
1656
+ var runPipeline = async (pipeline, req, res = {}, root = true) => {
1657
+ for (const operation of pipeline) {
1658
+ const next = await operation(req, res);
1659
+ if (next && Array.isArray(next)) {
1660
+ for (const nextPipeline of next) {
1661
+ await runPipeline(
1662
+ nextPipeline.pipeline,
1663
+ nextPipeline.req,
1664
+ nextPipeline.res,
1665
+ false
1666
+ );
1667
+ }
1668
+ }
1669
+ }
1670
+ if (root) {
1671
+ await runPipeline(finalPipeline, req, res, false);
1672
+ return res.bqlRes;
1673
+ }
1674
+ };
1675
+ var queryPipeline = (bqlRequest, bormConfig, bormSchema, dbHandles) => runPipeline(
1676
+ Pipelines.query,
1677
+ {
1678
+ config: bormConfig,
1679
+ schema: bormSchema,
1680
+ rawBqlRequest: bqlRequest,
1681
+ dbHandles
1682
+ },
1683
+ {}
1684
+ );
1685
+ var mutationPipeline = (bqlRequest, bormConfig, bormSchema, dbHandles) => runPipeline(
1686
+ Pipelines.mutation,
1687
+ {
1688
+ config: bormConfig,
1689
+ schema: bormSchema,
1690
+ rawBqlRequest: bqlRequest,
1691
+ dbHandles
1692
+ },
1693
+ {}
1694
+ );
1695
+
1696
+ // src/index.ts
1697
+ var BormClient = class {
1698
+ schema;
1699
+ config;
1700
+ dbHandles;
1701
+ constructor({ schema, config }) {
1702
+ this.schema = schema;
1703
+ this.config = config;
1704
+ }
1705
+ init = async () => {
1706
+ const dbHandles = { typeDB: /* @__PURE__ */ new Map() };
1707
+ const enrichedSchema = enrichSchema(this.schema);
1708
+ await Promise.all(
1709
+ this.config.dbConnectors.map(async (dbc) => {
1710
+ if (dbc.provider === "typeDB" && dbc.dbName) {
1711
+ const [clientErr, client] = await (0, import_radash9.tryit)(import_typedb_client3.TypeDB.coreClient)(dbc.url);
1712
+ if (clientErr) {
1713
+ const message = `[BORM:${dbc.provider}:${dbc.dbName}] ${clientErr.message ?? "Can't create TypeDB Client"}`;
1714
+ throw new Error(message);
1715
+ }
1716
+ try {
1717
+ const session = await client.session(dbc.dbName, import_typedb_client3.SessionType.DATA);
1718
+ dbHandles.typeDB.set(dbc.id, { client, session });
1719
+ } catch (sessionErr) {
1720
+ const message = `[BORM:${dbc.provider}:${dbc.dbName}] ${(sessionErr.messageTemplate?._messageBody() || sessionErr.message) ?? "Can't create TypeDB Session"}`;
1721
+ throw new Error(message);
1722
+ }
1723
+ }
1724
+ })
1725
+ );
1726
+ this.schema = enrichedSchema;
1727
+ this.dbHandles = dbHandles;
1728
+ };
1729
+ #enforceConnection = async () => {
1730
+ if (!this.dbHandles) {
1731
+ await this.init();
1732
+ if (!this.dbHandles) {
1733
+ throw new Error("Can't init BormClient");
1734
+ }
1735
+ }
1736
+ };
1737
+ introspect = async () => {
1738
+ await this.#enforceConnection();
1739
+ return this.schema;
1740
+ };
1741
+ query = async (query, queryConfig) => {
1742
+ await this.#enforceConnection();
1743
+ const qConfig = {
1744
+ ...this.config,
1745
+ query: { ...defaultConfig.query, ...this.config.query, ...queryConfig }
1746
+ };
1747
+ return queryPipeline(query, qConfig, this.schema, this.dbHandles);
1748
+ };
1749
+ mutate = async (mutation, mutationConfig) => {
1750
+ await this.#enforceConnection();
1751
+ const mConfig = {
1752
+ ...this.config,
1753
+ mutation: {
1754
+ ...defaultConfig.mutation,
1755
+ ...this.config.mutation,
1756
+ ...mutationConfig
1757
+ }
1758
+ };
1759
+ return mutationPipeline(mutation, mConfig, this.schema, this.dbHandles);
1760
+ };
1761
+ close = async () => {
1762
+ if (!this.dbHandles) {
1763
+ return;
1764
+ }
1765
+ this.dbHandles.typeDB.forEach(async ({ client, session }) => {
1766
+ console.log("Closing session");
1767
+ await session.close();
1768
+ console.log("Closing client");
1769
+ await client.close();
1770
+ });
1771
+ };
1772
+ };
1773
+ var src_default = BormClient;
1774
+ // Annotate the CommonJS export names for ESM import in node:
1775
+ 0 && (module.exports = {});