@saltcorn/builder 0.9.4-beta.1 → 0.9.4-beta.11

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.
@@ -17,13 +17,18 @@ import {
17
17
  buildOptions,
18
18
  HelpTopicLink,
19
19
  initialRelation,
20
- prepCacheAndFinder,
21
- updateRelationsCache,
20
+ buildLayers,
22
21
  } from "./utils";
23
22
 
24
23
  import { RelationBadges } from "./RelationBadges";
25
24
  import { RelationOnDemandPicker } from "./RelationOnDemandPicker";
26
25
 
26
+ import {
27
+ RelationsFinder,
28
+ Relation,
29
+ buildTableCaches,
30
+ } from "@saltcorn/common-code";
31
+
27
32
  export /**
28
33
  * @param {object} props
29
34
  * @param {*} props.name
@@ -48,7 +53,6 @@ const View = ({ name, view, configuration, state }) => {
48
53
  if (rest.startsWith(".")) viewname = prefix;
49
54
  else viewname = rest;
50
55
  }
51
-
52
56
  const theview = options.views.find((v) => v.name === viewname);
53
57
  const label = theview ? theview.label : view;
54
58
  const { previews, setPreviews } = React.useContext(previewCtx);
@@ -109,10 +113,23 @@ const ViewSettings = () => {
109
113
  extra_state_fml,
110
114
  } = node;
111
115
  const options = React.useContext(optionsCtx);
112
- const { caches, finder } = useMemo(
113
- () => prepCacheAndFinder(options),
114
- [undefined]
115
- );
116
+ const {
117
+ tables,
118
+ views,
119
+ max_relations_layer_depth,
120
+ tableName,
121
+ excluded_subview_templates,
122
+ } = options;
123
+ // not needed in page editor
124
+ let finder = null;
125
+ let tableCaches = null;
126
+ if (tables && views) {
127
+ finder = useMemo(
128
+ () => new RelationsFinder(tables, views, max_relations_layer_depth),
129
+ [undefined]
130
+ );
131
+ tableCaches = useMemo(() => buildTableCaches(tables), [undefined]);
132
+ }
116
133
  const fixed_state_fields =
117
134
  options.fixed_state_fields && options.fixed_state_fields[view];
118
135
  const { setPreviews } = React.useContext(previewCtx);
@@ -135,27 +152,48 @@ const ViewSettings = () => {
135
152
  else viewname = rest;
136
153
  }
137
154
  if (viewname.includes(".")) viewname = viewname.split(".")[0];
138
- if (finder)
139
- updateRelationsCache(
140
- relationsCache,
141
- setRelationsCache,
142
- options,
143
- finder,
144
- viewname
155
+
156
+ if (
157
+ finder &&
158
+ !(relationsCache[tableName] && relationsCache[tableName][viewname])
159
+ ) {
160
+ const relations = finder.findRelations(
161
+ tableName,
162
+ viewname,
163
+ excluded_subview_templates
164
+ );
165
+ const layers = buildLayers(
166
+ relations,
167
+ tableName,
168
+ tableCaches.tableNameCache
145
169
  );
146
- const [relations, setRelations] = finder
147
- ? React.useState(relationsCache[options.tableName][viewname])
170
+ relationsCache[tableName] = relationsCache[tableName] || {};
171
+ relationsCache[tableName][viewname] = { relations, layers };
172
+ setRelationsCache({ ...relationsCache });
173
+ }
174
+ const [relationsData, setRelationsData] = finder
175
+ ? React.useState(relationsCache[tableName][viewname])
148
176
  : [undefined, undefined];
149
- let safeRelation = relation;
177
+ let safeRelation = null;
178
+ const subView = views.find((view) => view.name === viewname);
179
+ if (relation) {
180
+ const subTbl = tables.find((tbl) => tbl.id === subView.table_id);
181
+ safeRelation = new Relation(
182
+ relation,
183
+ subTbl ? subTbl.name : "",
184
+ subView.display_type
185
+ );
186
+ }
150
187
  if (
151
188
  options.mode !== "filter" &&
189
+ subView.table_id &&
152
190
  !safeRelation &&
153
191
  !hasLegacyRelation &&
154
- relations?.paths.length > 0
192
+ relationsData?.relations.length > 0
155
193
  ) {
156
- safeRelation = initialRelation(relations.paths, options.tableName);
194
+ safeRelation = initialRelation(relationsData.relations);
157
195
  setProp((prop) => {
158
- prop.relation = safeRelation;
196
+ prop.relation = safeRelation.relationString;
159
197
  });
160
198
  }
161
199
  const helpContext = { view_name: viewname };
@@ -169,24 +207,32 @@ const ViewSettings = () => {
169
207
  prop.view = target_value;
170
208
  });
171
209
  } else {
172
- updateRelationsCache(
173
- relationsCache,
174
- setRelationsCache,
175
- options,
176
- finder,
177
- target_value
210
+ const newRelations = finder.findRelations(
211
+ tableName,
212
+ target_value,
213
+ excluded_subview_templates
178
214
  );
179
- const newRelations = relationsCache[options.tableName][target_value];
180
- if (newRelations.paths.length > 0) {
215
+ const layers = buildLayers(
216
+ newRelations,
217
+ tableName,
218
+ tableCaches.tableNameCache
219
+ );
220
+ relationsCache[tableName] = relationsCache[tableName] || {};
221
+ relationsCache[tableName][target_value] = {
222
+ relations: newRelations,
223
+ layers,
224
+ };
225
+ if (newRelations.length > 0) {
181
226
  setProp((prop) => {
182
227
  prop.view = target_value;
183
- prop.relation = initialRelation(
184
- newRelations.paths,
185
- options.tableName
186
- );
228
+ prop.relation = initialRelation(newRelations).relationString;
229
+ });
230
+ setRelationsData({ relations: newRelations, layers });
231
+ } else
232
+ window.notifyAlert({
233
+ type: "warning",
234
+ text: `${target_value} has no relations`,
187
235
  });
188
- setRelations(newRelations);
189
- }
190
236
  }
191
237
  }
192
238
  }
@@ -194,7 +240,7 @@ const ViewSettings = () => {
194
240
 
195
241
  return (
196
242
  <div>
197
- {relations ? (
243
+ {relationsData ? (
198
244
  <Fragment>
199
245
  <div>
200
246
  <label>View to {options.mode === "show" ? "embed" : "show"}</label>
@@ -214,7 +260,7 @@ const ViewSettings = () => {
214
260
  {options.mode !== "filter" && (
215
261
  <div>
216
262
  <RelationOnDemandPicker
217
- relations={relations.layers}
263
+ relations={relationsData.layers}
218
264
  update={(relPath) => {
219
265
  if (relPath.startsWith(".")) {
220
266
  setProp((prop) => {
@@ -232,8 +278,8 @@ const ViewSettings = () => {
232
278
  <RelationBadges
233
279
  view={view}
234
280
  relation={safeRelation}
235
- parentTbl={options.tableName}
236
- tableNameCache={caches.tableNameCache}
281
+ parentTbl={tableName}
282
+ caches={tableCaches}
237
283
  />
238
284
  </div>
239
285
  )}
@@ -293,7 +339,7 @@ const ViewSettings = () => {
293
339
  )}
294
340
  </Fragment>
295
341
  )}
296
- {(state === "shared" || options.mode === "page") && (
342
+ {
297
343
  <Fragment>
298
344
  <label>
299
345
  Extra state Formula
@@ -311,7 +357,7 @@ const ViewSettings = () => {
311
357
  </small>
312
358
  ) : null}
313
359
  </Fragment>
314
- )}
360
+ }
315
361
  {view ? (
316
362
  <a
317
363
  className="d-block mt-2"
@@ -18,14 +18,19 @@ import {
18
18
  setAPropGen,
19
19
  FormulaTooltip,
20
20
  HelpTopicLink,
21
- prepCacheAndFinder,
22
21
  initialRelation,
23
- updateRelationsCache,
22
+ buildLayers,
24
23
  } from "./utils";
25
24
 
26
25
  import { RelationBadges } from "./RelationBadges";
27
26
  import { RelationOnDemandPicker } from "./RelationOnDemandPicker";
28
27
 
28
+ import {
29
+ RelationsFinder,
30
+ Relation,
31
+ buildTableCaches,
32
+ } from "@saltcorn/common-code";
33
+
29
34
  export /**
30
35
  * @param {object} props
31
36
  * @param {string} props.name
@@ -127,10 +132,18 @@ const ViewLinkSettings = () => {
127
132
  link_target_blank,
128
133
  } = node;
129
134
  const options = React.useContext(optionsCtx);
130
- const { caches, finder } = useMemo(
131
- () => prepCacheAndFinder(options),
135
+ const {
136
+ tables,
137
+ views,
138
+ tableName,
139
+ excluded_subview_templates,
140
+ max_relations_layer_depth,
141
+ } = options;
142
+ const finder = useMemo(
143
+ () => new RelationsFinder(tables, views, max_relations_layer_depth),
132
144
  [undefined]
133
145
  );
146
+ const tableCaches = useMemo(() => buildTableCaches(tables), [undefined]);
134
147
  const { relationsCache, setRelationsCache } = React.useContext(relationsCtx);
135
148
  let errorString = false;
136
149
  try {
@@ -147,50 +160,82 @@ const ViewLinkSettings = () => {
147
160
  const safeViewName = use_view_name?.includes(".")
148
161
  ? use_view_name.split(".")[0]
149
162
  : use_view_name;
150
- updateRelationsCache(
151
- relationsCache,
152
- setRelationsCache,
153
- options,
154
- finder,
155
- safeViewName
156
- );
157
- const [relations, setRelations] = React.useState(
163
+ const subView = views.find((view) => view.name === safeViewName);
164
+ const hasTableId = subView?.table_id !== undefined;
165
+ if (!(relationsCache[tableName] && relationsCache[tableName][safeViewName])) {
166
+ const relations = finder.findRelations(
167
+ tableName,
168
+ safeViewName,
169
+ excluded_subview_templates
170
+ );
171
+ const layers = buildLayers(
172
+ relations,
173
+ tableName,
174
+ tableCaches.tableNameCache
175
+ );
176
+ relationsCache[tableName] = relationsCache[tableName] || {};
177
+ relationsCache[tableName][safeViewName] = { relations, layers };
178
+ setRelationsCache({ ...relationsCache });
179
+ }
180
+ const [relationsData, setRelationsData] = React.useState(
158
181
  relationsCache[options.tableName][safeViewName]
159
182
  );
160
- let safeRelation = relation;
161
- if (!safeRelation && !hasLegacyRelation && relations?.paths.length > 0) {
162
- safeRelation = initialRelation(relations.paths, options.tableName);
183
+ let safeRelation = null;
184
+ if (relation) {
185
+ const subView = views.find((view) => view.name === safeViewName);
186
+ const subTbl = tables.find((tbl) => tbl.id === subView.table_id);
187
+ safeRelation = new Relation(
188
+ relation,
189
+ subTbl ? subTbl.name : "",
190
+ subView.display_type
191
+ );
192
+ }
193
+ if (
194
+ !safeRelation &&
195
+ !hasLegacyRelation &&
196
+ relationsData?.relations.length > 0
197
+ ) {
198
+ safeRelation = initialRelation(relationsData.relations);
163
199
  setProp((prop) => {
164
- prop.relation = safeRelation;
200
+ prop.relation = safeRelation.relationString;
165
201
  });
166
202
  }
167
203
  const set_view_name = (e) => {
168
204
  if (e.target) {
169
205
  const target_value = e.target.value;
170
206
  if (target_value !== use_view_name) {
171
- updateRelationsCache(
172
- relationsCache,
173
- setRelationsCache,
174
- options,
175
- finder,
176
- target_value
207
+ const newRelations = finder.findRelations(
208
+ tableName,
209
+ target_value,
210
+ excluded_subview_templates
211
+ );
212
+ const layers = buildLayers(
213
+ newRelations,
214
+ tableName,
215
+ tableCaches.tableNameCache
177
216
  );
178
- const newRelations = relationsCache[options.tableName][target_value];
179
- if (newRelations.paths.length > 0) {
217
+
218
+ relationsCache[tableName] = relationsCache[tableName] || {};
219
+ relationsCache[tableName][target_value] = {
220
+ relations: newRelations,
221
+ layers,
222
+ };
223
+ if (newRelations.length > 0) {
180
224
  setProp((prop) => {
181
225
  prop.name = target_value;
182
- prop.relation = initialRelation(
183
- newRelations.paths,
184
- options.tableName
185
- );
226
+ prop.relation = initialRelation(newRelations).relationString;
227
+ });
228
+ setRelationsData({ relations: newRelations, layers });
229
+ } else
230
+ window.notifyAlert({
231
+ type: "warning",
232
+ text: `${target_value} has no relations`,
186
233
  });
187
- setRelations(newRelations);
188
- }
189
234
  }
190
235
  }
191
236
  };
192
237
  const helpContext = { view_name: use_view_name };
193
- if (options.tableName) helpContext.srcTable = options.tableName;
238
+ if (tableName) helpContext.srcTable = tableName;
194
239
  return (
195
240
  <div>
196
241
  <table className="w-100">
@@ -215,7 +260,7 @@ const ViewLinkSettings = () => {
215
260
  <tr>
216
261
  <td colSpan="2">
217
262
  <RelationOnDemandPicker
218
- relations={relations.layers}
263
+ relations={relationsData.layers}
219
264
  update={(relPath) => {
220
265
  if (relPath.startsWith(".")) {
221
266
  setProp((prop) => {
@@ -233,8 +278,8 @@ const ViewLinkSettings = () => {
233
278
  <RelationBadges
234
279
  view={name}
235
280
  relation={safeRelation}
236
- parentTbl={options.tableName}
237
- tableNameCache={caches.tableNameCache}
281
+ parentTbl={tableName}
282
+ caches={tableCaches}
238
283
  />
239
284
  </td>
240
285
  </tr>
@@ -278,6 +323,7 @@ const ViewLinkSettings = () => {
278
323
  values={node}
279
324
  linkFirst={true}
280
325
  linkIsBlank={true}
326
+ allowRunOnLoad={false}
281
327
  />
282
328
  </tbody>
283
329
  </table>
@@ -18,6 +18,7 @@ import FontIconPicker from "@fonticonpicker/react-fonticonpicker";
18
18
  import faIcons from "./faicons";
19
19
  import { Columns, ntimes } from "./Columns";
20
20
  import Tippy from "@tippyjs/react";
21
+ import { RelationType } from "@saltcorn/common-code";
21
22
 
22
23
  export const DynamicFontAwesomeIcon = ({ icon, className }) => {
23
24
  if (!icon) return null;
@@ -445,6 +446,10 @@ const fetchPreview = ({ url, body, options, setPreviews, node_id, isView }) => {
445
446
  }
446
447
  const newHtml = $(".preview-scratchpad").html();
447
448
  setPreviews((prevState) => ({ ...prevState, [node_id]: newHtml }));
449
+ })
450
+ .catch((e) => {
451
+ console.log("Unable to fetch the preview:");
452
+ console.log(e);
448
453
  });
449
454
  };
450
455
 
@@ -1245,19 +1250,27 @@ const ButtonOrLinkSettingsRows = ({
1245
1250
  <option value={addBtnClass("btn-secondary")}>Secondary button</option>
1246
1251
  <option value={addBtnClass("btn-success")}>Success button</option>
1247
1252
  <option value={addBtnClass("btn-danger")}>Danger button</option>
1253
+ <option value={addBtnClass("btn-warning")}>Warning button</option>
1254
+ <option value={addBtnClass("btn-info")}>Info button</option>
1248
1255
  <option value={addBtnClass("btn-outline-primary")}>
1249
1256
  Primary outline button
1250
1257
  </option>
1251
1258
  <option value={addBtnClass("btn-outline-secondary")}>
1252
1259
  Secondary outline button
1253
1260
  </option>
1261
+ <option value={addBtnClass("btn-outline-warning")}>
1262
+ Warning outline button
1263
+ </option>{" "}
1264
+ <option value={addBtnClass("btn-outline-info")}>
1265
+ Info outline button
1266
+ </option>
1254
1267
  <option value={addBtnClass("btn-custom-color")}>
1255
1268
  Button custom color
1256
1269
  </option>
1257
1270
  {!linkFirst ? (
1258
1271
  <option value={addBtnClass("btn-link")}>Link</option>
1259
1272
  ) : null}
1260
- {!linkFirst ? (
1273
+ {!linkFirst && allowRunOnLoad ? (
1261
1274
  <option value="on_page_load">Run on Page Load</option>
1262
1275
  ) : null}
1263
1276
  </select>
@@ -1439,22 +1452,6 @@ const Tooltip = ({ children }) => {
1439
1452
  );
1440
1453
  };
1441
1454
 
1442
- export const buildTableCaches = (allTables) => {
1443
- const tableIdCache = {};
1444
- const tableNameCache = {};
1445
- const fieldCache = {};
1446
- for (const table of allTables) {
1447
- tableIdCache[table.id] = table;
1448
- tableNameCache[table.name] = table;
1449
- for (const field of table.foreign_keys) {
1450
- if (!fieldCache[field.reftable_name])
1451
- fieldCache[field.reftable_name] = [];
1452
- fieldCache[field.reftable_name].push(field);
1453
- }
1454
- }
1455
- return { tableIdCache, tableNameCache, fieldCache };
1456
- };
1457
-
1458
1455
  export const removeWhitespaces = (str) => {
1459
1456
  return str.replace(/\s/g, "X");
1460
1457
  };
@@ -1497,79 +1494,145 @@ export const buildBootstrapOptions = (values) => {
1497
1494
  ));
1498
1495
  };
1499
1496
 
1500
- export const prepCacheAndFinder = ({
1501
- tables,
1502
- views,
1503
- max_relations_layer_depth,
1504
- }) => {
1505
- if (tables && views) {
1506
- const caches = buildTableCaches(tables);
1507
- const finder = new relationHelpers.RelationsFinder(
1508
- caches,
1509
- views,
1510
- max_relations_layer_depth || 6
1511
- );
1512
- return { caches, finder };
1513
- } else return { caches: null, finder: null };
1497
+ export const arrayChunks = (xs, n) => {
1498
+ const arrayOfArrays = [];
1499
+ for (var i = 0; i < bigarray.length; i += n) {
1500
+ arrayOfArrays.push(bigarray.slice(i, i + n));
1501
+ }
1502
+ return arrayOfArrays;
1514
1503
  };
1515
1504
 
1516
1505
  /**
1517
- * @param {string[]} paths
1506
+ * @param {string[]} relations
1518
1507
  * @param {string} sourceTbl name of the topview table
1519
1508
  * @returns either a same table relation, a parent relation, a child relation, or the first relation
1520
1509
  */
1521
- export const initialRelation = (paths, sourceTbl) => {
1510
+ export const initialRelation = (relations) => {
1522
1511
  let sameTblRel = null;
1523
1512
  let parentRel = null;
1524
1513
  let childRel = null;
1525
- for (const path of paths) {
1526
- if (!sameTblRel && path === `.${sourceTbl}`) sameTblRel = path;
1527
- else {
1528
- const tokens = path.split(".");
1529
- if (
1530
- !parentRel &&
1531
- tokens.length === 3 &&
1532
- tokens[1] === sourceTbl &&
1533
- tokens[2].indexOf("$") === -1
1534
- )
1535
- parentRel = path;
1536
- else {
1537
- const lastToken = tokens[tokens.length - 1];
1538
- if (
1539
- lastToken.indexOf("$") > 0 &&
1540
- (!childRel || childRel.split(".").length > tokens.length)
1541
- )
1542
- childRel = path;
1543
- }
1514
+ for (const relation of relations) {
1515
+ switch (relation.type) {
1516
+ case RelationType.OWN:
1517
+ sameTblRel = relation;
1518
+ break;
1519
+ case RelationType.PARENT_SHOW:
1520
+ parentRel = relation;
1521
+ break;
1522
+ case RelationType.CHILD_LIST:
1523
+ case RelationType.ONE_TO_ONE_SHOW:
1524
+ childRel = relation;
1525
+ break;
1544
1526
  }
1545
1527
  }
1546
- return sameTblRel || parentRel || childRel || paths[0];
1528
+ return sameTblRel || parentRel || childRel || relations[0];
1547
1529
  };
1548
1530
 
1549
1531
  /**
1550
- * update the builder wide relations cache relations cache
1551
- * if there is no entry for the given tableName and viewname
1552
- * @param {any} relationsCache cache from the context
1553
- * @param {Function} setRelationsCache set cache in context
1554
- * @param {any} options builder options
1555
- * @param {RelationsFinder} finder
1556
- * @param {string} viewname subview name
1532
+ * builder intern path method
1533
+ * @param path
1534
+ * @param tableNameCache
1535
+ * @returns
1557
1536
  */
1558
- export const updateRelationsCache = (
1559
- relationsCache,
1560
- setRelationsCache,
1561
- options,
1562
- finder,
1563
- viewname
1564
- ) => {
1565
- if (!relationsCache[options.tableName])
1566
- relationsCache[options.tableName] = {};
1567
- if (!relationsCache[options.tableName][viewname]) {
1568
- relationsCache[options.tableName][viewname] = finder.findRelations(
1569
- options.tableName,
1570
- viewname,
1571
- options.excluded_subview_templates
1572
- );
1573
- setRelationsCache({ ...relationsCache });
1537
+ export const buildRelationArray = (path, tableNameCache) => {
1538
+ if (path === ".")
1539
+ return [{ type: "Independent", table: "None (no relation)" }];
1540
+ const tokens = path.split(".");
1541
+ if (tokens.length === 2)
1542
+ return [{ type: "Own", table: `${tokens[1]} (same table)` }];
1543
+ else if (tokens.length >= 3) {
1544
+ const result = [];
1545
+ let currentTbl = tokens[1];
1546
+ for (const relation of tokens.slice(2)) {
1547
+ if (relation.indexOf("$") > 0) {
1548
+ const [inboundTbl, inboundKey] = relation.split("$");
1549
+ result.push({ type: "Inbound", table: inboundTbl, key: inboundKey });
1550
+ currentTbl = inboundTbl;
1551
+ } else {
1552
+ const srcTbl = tableNameCache[currentTbl];
1553
+ const fk = srcTbl.foreign_keys.find((fk) => fk.name === relation);
1554
+ if (fk) {
1555
+ const targetTbl = tableNameCache[fk.reftable_name];
1556
+ result.push({
1557
+ type: "Foreign",
1558
+ table: targetTbl.name,
1559
+ key: relation,
1560
+ });
1561
+ currentTbl = targetTbl.name;
1562
+ }
1563
+ }
1564
+ }
1565
+ return result;
1566
+ }
1567
+ };
1568
+
1569
+ export const buildLayers = (relations, tableName, tableNameCache) => {
1570
+ const result = { table: tableName, inboundKeys: [], fkeys: [] };
1571
+ for (const relation of relations) {
1572
+ const relType = relation.type;
1573
+ let currentLevel = result;
1574
+ if (relType === RelationType.INDEPENDENT) {
1575
+ currentLevel.fkeys.push({
1576
+ name: "none (no relation)",
1577
+ table: "",
1578
+ inboundKeys: [],
1579
+ fkeys: [],
1580
+ relPath: relation.relationString,
1581
+ });
1582
+ } else if (relType === RelationType.OWN) {
1583
+ currentLevel.fkeys.push({
1584
+ name: "same table",
1585
+ table: relation.targetTblName,
1586
+ inboundKeys: [],
1587
+ fkeys: [],
1588
+ relPath: relation.relationString,
1589
+ });
1590
+ } else {
1591
+ let currentTbl = relation.sourceTblName;
1592
+ for (const pathElement of relation.path) {
1593
+ if (pathElement.inboundKey) {
1594
+ currentTbl = pathElement.table;
1595
+ const existing = currentLevel.inboundKeys.find(
1596
+ (key) =>
1597
+ key.name === pathElement.inboundKey && key.table === currentTbl
1598
+ );
1599
+ if (existing) {
1600
+ currentLevel = existing;
1601
+ } else {
1602
+ const nextLevel = {
1603
+ name: pathElement.inboundKey,
1604
+ table: currentTbl,
1605
+ inboundKeys: [],
1606
+ fkeys: [],
1607
+ };
1608
+ currentLevel.inboundKeys.push(nextLevel);
1609
+ currentLevel = nextLevel;
1610
+ }
1611
+ } else if (pathElement.fkey) {
1612
+ const tblObj = tableNameCache[currentTbl];
1613
+ const fkey = tblObj.foreign_keys.find(
1614
+ (key) => key.name === pathElement.fkey
1615
+ );
1616
+ currentTbl = fkey.reftable_name;
1617
+ const existing = currentLevel.fkeys.find(
1618
+ (key) => key.name === pathElement.fkey
1619
+ );
1620
+ if (existing) {
1621
+ currentLevel = existing;
1622
+ } else {
1623
+ const nextLevel = {
1624
+ name: pathElement.fkey,
1625
+ table: currentTbl,
1626
+ inboundKeys: [],
1627
+ fkeys: [],
1628
+ };
1629
+ currentLevel.fkeys.push(nextLevel);
1630
+ currentLevel = nextLevel;
1631
+ }
1632
+ }
1633
+ }
1634
+ }
1635
+ currentLevel.relPath = relation.relationString;
1574
1636
  }
1637
+ return result;
1575
1638
  };