@growthub/cli 0.9.18 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/reference-options/route.js +62 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/refresh-sources/route.js +13 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/resolver-templates/route.js +23 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +35 -5
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/test-source/route.js +15 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +2277 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataTable.jsx +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/FieldEditor.jsx +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/FieldManager.jsx +9 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ObjectSidebar.jsx +41 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/RecordDrawer.jsx +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ReferencePicker.jsx +244 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxRunPanel.jsx +21 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SourceTestPanel.jsx +15 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/StatusPill.jsx +13 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ToggleField.jsx +41 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/dm-shared.jsx +99 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/page.jsx +2 -1528
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +99 -6
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/connector-template-authoring.md +8 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/data-model-reference-fields.md +15 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/mcp-chrome-tool-connectors.md +12 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/resolver-template-library.md +17 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/source-resolver-registry.js +13 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/README.md +12 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/chrome-bridge.js +22 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/custom-http.js +23 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/generic-commerce.js +22 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/generic-crm.js +23 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/generic-project-management.js +22 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/generic-spreadsheet.js +22 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/mcp-tool.js +22 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/template-registry.js +50 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/webhook.js +22 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/collect-reference-options.js +133 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/reference-resolver-registry.js +17 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/resolver-loader.js +6 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/resolvers/README.md +8 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/resolvers/local-data-model.js +11 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/resolvers/source-records.js +34 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/adapters/README.md +5 -3
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/default-local-intelligence.js +203 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/index.js +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/field-contracts.js +81 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/reference-option-schema.js +59 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/reference-options.js +29 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +534 -23
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +131 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/export-training-traces.mjs +144 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/grade-raw-pairs.mjs +279 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/harvest-cursor-traces.mjs +288 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/upload-graded-traces.mjs +128 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +10 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/seeded-configs/alignment-loop.config.json +264 -0
- package/dist/index.js +486 -1
- package/package.json +1 -1
|
@@ -163,6 +163,108 @@ function normalizeManualObjects(workspaceConfig) {
|
|
|
163
163
|
return Array.isArray(workspaceConfig?.dataModel?.objects) ? workspaceConfig.dataModel.objects : [];
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
function normalizeStringList(values) {
|
|
167
|
+
return Array.from(new Set((Array.isArray(values) ? values : [])
|
|
168
|
+
.map((value) => String(value || "").trim())
|
|
169
|
+
.filter(Boolean)));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function normalizeSortClauses(sort, columns) {
|
|
173
|
+
const allowed = new Set(columns);
|
|
174
|
+
return (Array.isArray(sort) ? sort : []).flatMap((clause) => {
|
|
175
|
+
if (!clause || typeof clause !== "object" || Array.isArray(clause)) return [];
|
|
176
|
+
const fieldId = String(clause.fieldId || "").trim();
|
|
177
|
+
const direction = String(clause.direction || "asc").trim().toLowerCase() === "desc" ? "desc" : "asc";
|
|
178
|
+
return allowed.has(fieldId) ? [{ fieldId, direction }] : [];
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function normalizeFilterConfig(filter, columns) {
|
|
183
|
+
if (!filter || typeof filter !== "object" || Array.isArray(filter)) return undefined;
|
|
184
|
+
const allowed = new Set(columns);
|
|
185
|
+
const op = String(filter.op || "and").trim().toLowerCase() === "or" ? "or" : "and";
|
|
186
|
+
const clauses = (Array.isArray(filter.clauses) ? filter.clauses : []).flatMap((clause) => {
|
|
187
|
+
if (!clause || typeof clause !== "object" || Array.isArray(clause)) return [];
|
|
188
|
+
const fieldId = String(clause.fieldId || "").trim();
|
|
189
|
+
const operator = String(clause.operator || "eq").trim();
|
|
190
|
+
if (!allowed.has(fieldId)) return [];
|
|
191
|
+
if (["isEmpty", "isNotEmpty"].includes(operator)) return [{ fieldId, operator }];
|
|
192
|
+
const value = clause.value;
|
|
193
|
+
if (value === undefined || value === null || value === "") return [];
|
|
194
|
+
return [{ fieldId, operator, value }];
|
|
195
|
+
});
|
|
196
|
+
return clauses.length ? { op, clauses } : undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function normalizeFieldTypes(types, columns) {
|
|
200
|
+
const result = {};
|
|
201
|
+
const source = types && typeof types === "object" && !Array.isArray(types) ? types : {};
|
|
202
|
+
columns.forEach((column) => {
|
|
203
|
+
const value = String(source[column] || "").trim();
|
|
204
|
+
if (value) result[column] = value;
|
|
205
|
+
});
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function snapshotTableViewState(settings) {
|
|
210
|
+
return {
|
|
211
|
+
hidden: normalizeStringList(settings?.hidden),
|
|
212
|
+
order: normalizeStringList(settings?.order),
|
|
213
|
+
sort: Array.isArray(settings?.sort) ? settings.sort.map((clause) => ({ ...clause })) : [],
|
|
214
|
+
filter: settings?.filter ? {
|
|
215
|
+
op: settings.filter.op,
|
|
216
|
+
clauses: (settings.filter.clauses || []).map((clause) => ({ ...clause }))
|
|
217
|
+
} : undefined
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function normalizeSavedViews(views, columns) {
|
|
222
|
+
return (Array.isArray(views) ? views : []).flatMap((view, index) => {
|
|
223
|
+
if (!view || typeof view !== "object" || Array.isArray(view)) return [];
|
|
224
|
+
const id = String(view.id || "").trim() || `view-${index + 1}`;
|
|
225
|
+
const name = String(view.name || "").trim();
|
|
226
|
+
if (!name) return [];
|
|
227
|
+
const state = normalizeFieldSettings({
|
|
228
|
+
hidden: view.hidden,
|
|
229
|
+
order: view.order,
|
|
230
|
+
sort: view.sort,
|
|
231
|
+
filter: view.filter,
|
|
232
|
+
}, columns);
|
|
233
|
+
return [{
|
|
234
|
+
id,
|
|
235
|
+
name,
|
|
236
|
+
favorite: Boolean(view.favorite),
|
|
237
|
+
locked: Boolean(view.locked),
|
|
238
|
+
hidden: state.hidden,
|
|
239
|
+
order: state.order,
|
|
240
|
+
sort: state.sort,
|
|
241
|
+
filter: state.filter
|
|
242
|
+
}];
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function normalizeFieldSettings(fieldSettings, columns) {
|
|
247
|
+
const order = normalizeStringList([
|
|
248
|
+
...(Array.isArray(fieldSettings?.order) ? fieldSettings.order : []),
|
|
249
|
+
...columns
|
|
250
|
+
]).filter((column) => columns.includes(column));
|
|
251
|
+
const hidden = normalizeStringList(fieldSettings?.hidden).filter((column) => columns.includes(column));
|
|
252
|
+
const sort = normalizeSortClauses(fieldSettings?.sort, columns);
|
|
253
|
+
const filter = normalizeFilterConfig(fieldSettings?.filter, columns);
|
|
254
|
+
const views = normalizeSavedViews(fieldSettings?.views, columns);
|
|
255
|
+
const activeViewId = String(fieldSettings?.activeViewId || "").trim();
|
|
256
|
+
return {
|
|
257
|
+
hidden,
|
|
258
|
+
order,
|
|
259
|
+
sort,
|
|
260
|
+
filter,
|
|
261
|
+
types: normalizeFieldTypes(fieldSettings?.types, columns),
|
|
262
|
+
views,
|
|
263
|
+
activeViewId: views.some((view) => view.id === activeViewId) ? activeViewId : "",
|
|
264
|
+
favorite: Boolean(fieldSettings?.favorite)
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
166
268
|
function deriveManualObjectTable(object) {
|
|
167
269
|
const columns = Array.isArray(object.columns) ? object.columns.filter(Boolean) : [];
|
|
168
270
|
const rows = Array.isArray(object.rows) ? object.rows.filter((row) => row && typeof row === "object" && !Array.isArray(row)) : [];
|
|
@@ -173,6 +275,7 @@ function deriveManualObjectTable(object) {
|
|
|
173
275
|
source,
|
|
174
276
|
objectType: object.objectType || "custom",
|
|
175
277
|
icon: object.icon || null,
|
|
278
|
+
pickerHidden: Boolean(object.pickerHidden),
|
|
176
279
|
columns,
|
|
177
280
|
rows,
|
|
178
281
|
binding: object.binding || { mode: "manual", source: "Data Model" },
|
|
@@ -181,10 +284,7 @@ function deriveManualObjectTable(object) {
|
|
|
181
284
|
storage: "manual-object",
|
|
182
285
|
objectId: object.id,
|
|
183
286
|
widgetRefs: [],
|
|
184
|
-
fieldSettings:
|
|
185
|
-
hidden: Array.isArray(object.fieldSettings?.hidden) ? object.fieldSettings.hidden : [],
|
|
186
|
-
order: Array.isArray(object.fieldSettings?.order) ? object.fieldSettings.order : columns
|
|
187
|
-
}
|
|
287
|
+
fieldSettings: normalizeFieldSettings(object.fieldSettings, columns)
|
|
188
288
|
};
|
|
189
289
|
}
|
|
190
290
|
|
|
@@ -224,10 +324,11 @@ function listWorkspaceDataModelTables(workspaceConfig) {
|
|
|
224
324
|
widgetTitle: widget.title,
|
|
225
325
|
widgetKind: widget.kind
|
|
226
326
|
}],
|
|
227
|
-
fieldSettings: {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
327
|
+
fieldSettings: normalizeFieldSettings({
|
|
328
|
+
...(widget.config?.fieldSettings || {}),
|
|
329
|
+
sort: widget.config?.sort,
|
|
330
|
+
filter: widget.config?.filter
|
|
331
|
+
}, table.columns)
|
|
231
332
|
};
|
|
232
333
|
})
|
|
233
334
|
.filter(Boolean);
|
|
@@ -235,25 +336,194 @@ function listWorkspaceDataModelTables(workspaceConfig) {
|
|
|
235
336
|
}
|
|
236
337
|
|
|
237
338
|
function writeTableConfig(config, storage, columns, rows) {
|
|
339
|
+
const fieldSettings = normalizeFieldSettings({
|
|
340
|
+
...(config.fieldSettings || {}),
|
|
341
|
+
sort: config.sort,
|
|
342
|
+
filter: config.filter
|
|
343
|
+
}, columns);
|
|
238
344
|
if (storage === "view") {
|
|
239
345
|
const binding = config.binding?.mode === "manual" ? { ...config.binding, rows } : config.binding;
|
|
240
|
-
return { ...config, columns, rows, binding, fieldSettings
|
|
346
|
+
return { ...config, columns, rows, binding, fieldSettings, sort: fieldSettings.sort, filter: fieldSettings.filter };
|
|
241
347
|
}
|
|
242
348
|
if (storage === "json") {
|
|
243
|
-
return { ...config, binding: { ...config.binding, json: JSON.stringify(rows, null, 2) } };
|
|
349
|
+
return { ...config, binding: { ...config.binding, json: JSON.stringify(rows, null, 2) }, fieldSettings, sort: fieldSettings.sort, filter: fieldSettings.filter };
|
|
244
350
|
}
|
|
245
351
|
if (storage === "csv") {
|
|
246
|
-
return { ...config, binding: { ...config.binding, csv: toCsv(columns, rows) } };
|
|
352
|
+
return { ...config, binding: { ...config.binding, csv: toCsv(columns, rows) }, fieldSettings, sort: fieldSettings.sort, filter: fieldSettings.filter };
|
|
247
353
|
}
|
|
248
354
|
if (storage === "manual-binding") {
|
|
249
|
-
return { ...config, binding: { ...config.binding, rows } };
|
|
355
|
+
return { ...config, binding: { ...config.binding, rows }, fieldSettings, sort: fieldSettings.sort, filter: fieldSettings.filter };
|
|
250
356
|
}
|
|
251
357
|
if (storage === "chart-values") {
|
|
252
|
-
return { ...config, values: rows.map((row) => Number(row.Value)).filter((value) => Number.isFinite(value)) };
|
|
358
|
+
return { ...config, values: rows.map((row) => Number(row.Value)).filter((value) => Number.isFinite(value)), fieldSettings, sort: fieldSettings.sort, filter: fieldSettings.filter };
|
|
253
359
|
}
|
|
254
360
|
return config;
|
|
255
361
|
}
|
|
256
362
|
|
|
363
|
+
function updateTableFieldSettings(workspaceConfig, table, updater) {
|
|
364
|
+
const nextSettings = normalizeFieldSettings(
|
|
365
|
+
updater(normalizeFieldSettings(table.fieldSettings, table.columns)),
|
|
366
|
+
table.columns
|
|
367
|
+
);
|
|
368
|
+
if (table.storage === "manual-object") {
|
|
369
|
+
const objects = normalizeManualObjects(workspaceConfig);
|
|
370
|
+
const dataModel = workspaceConfig.dataModel && typeof workspaceConfig.dataModel === "object" && !Array.isArray(workspaceConfig.dataModel)
|
|
371
|
+
? workspaceConfig.dataModel
|
|
372
|
+
: {};
|
|
373
|
+
return {
|
|
374
|
+
...workspaceConfig,
|
|
375
|
+
dataModel: {
|
|
376
|
+
...dataModel,
|
|
377
|
+
objects: objects.map((object) => object.id === table.objectId
|
|
378
|
+
? { ...object, fieldSettings: nextSettings }
|
|
379
|
+
: object)
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const ids = new Set((table.widgetRefs || []).map((ref) => ref.widgetId));
|
|
385
|
+
const mutateWidgets = (widgets) => (widgets || []).map((widget) => {
|
|
386
|
+
if (!ids.has(widget.id)) return widget;
|
|
387
|
+
const current = deriveWidgetTable(widget, { widgetId: widget.id });
|
|
388
|
+
if (!current?.mutable) return widget;
|
|
389
|
+
return {
|
|
390
|
+
...widget,
|
|
391
|
+
config: {
|
|
392
|
+
...(widget.config || {}),
|
|
393
|
+
fieldSettings: nextSettings,
|
|
394
|
+
sort: nextSettings.sort,
|
|
395
|
+
filter: nextSettings.filter
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
const dashboards = (workspaceConfig.dashboards || []).map((dashboard) => ({
|
|
401
|
+
...dashboard,
|
|
402
|
+
tabs: (dashboard.tabs || []).map((tab) => ({ ...tab, widgets: mutateWidgets(tab.widgets) }))
|
|
403
|
+
}));
|
|
404
|
+
let canvas = workspaceConfig.canvas ? { ...workspaceConfig.canvas } : {};
|
|
405
|
+
if (Array.isArray(canvas.widgets)) canvas = { ...canvas, widgets: mutateWidgets(canvas.widgets) };
|
|
406
|
+
if (Array.isArray(canvas.tabs)) canvas = { ...canvas, tabs: canvas.tabs.map((tab) => ({ ...tab, widgets: mutateWidgets(tab.widgets) })) };
|
|
407
|
+
return { ...workspaceConfig, dashboards, canvas };
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function remapFieldName(name, renameMap = {}) {
|
|
411
|
+
return renameMap[name] || name;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function remapFieldSettings(fieldSettings, columns, renameMap = {}) {
|
|
415
|
+
const mapList = (values) => normalizeStringList((values || []).map((value) => remapFieldName(value, renameMap))).filter((value) => columns.includes(value));
|
|
416
|
+
const mapSort = (sort) => normalizeSortClauses((sort || []).map((clause) => ({
|
|
417
|
+
...clause,
|
|
418
|
+
fieldId: remapFieldName(clause.fieldId, renameMap)
|
|
419
|
+
})), columns);
|
|
420
|
+
const mapFilter = (filter) => normalizeFilterConfig(filter ? {
|
|
421
|
+
...filter,
|
|
422
|
+
clauses: (filter.clauses || []).map((clause) => ({
|
|
423
|
+
...clause,
|
|
424
|
+
fieldId: remapFieldName(clause.fieldId, renameMap)
|
|
425
|
+
}))
|
|
426
|
+
} : null, columns);
|
|
427
|
+
const sourceTypes = fieldSettings?.types && typeof fieldSettings.types === "object" && !Array.isArray(fieldSettings.types)
|
|
428
|
+
? fieldSettings.types
|
|
429
|
+
: {};
|
|
430
|
+
const types = columns.reduce((acc, column) => {
|
|
431
|
+
const previousKey = Object.keys(renameMap).find((key) => renameMap[key] === column) || column;
|
|
432
|
+
const typeValue = sourceTypes[column] || sourceTypes[previousKey];
|
|
433
|
+
if (typeValue) acc[column] = typeValue;
|
|
434
|
+
return acc;
|
|
435
|
+
}, {});
|
|
436
|
+
const views = normalizeSavedViews((fieldSettings?.views || []).map((view) => ({
|
|
437
|
+
...view,
|
|
438
|
+
hidden: mapList(view.hidden || []),
|
|
439
|
+
order: mapList(view.order || []),
|
|
440
|
+
sort: mapSort(view.sort || []),
|
|
441
|
+
filter: mapFilter(view.filter)
|
|
442
|
+
})), columns);
|
|
443
|
+
const activeViewId = String(fieldSettings?.activeViewId || "").trim();
|
|
444
|
+
return {
|
|
445
|
+
hidden: mapList(fieldSettings?.hidden || []),
|
|
446
|
+
order: mapList(fieldSettings?.order || columns),
|
|
447
|
+
sort: mapSort(fieldSettings?.sort || []),
|
|
448
|
+
filter: mapFilter(fieldSettings?.filter),
|
|
449
|
+
types,
|
|
450
|
+
views,
|
|
451
|
+
activeViewId: views.some((view) => view.id === activeViewId) ? activeViewId : "",
|
|
452
|
+
favorite: Boolean(fieldSettings?.favorite)
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function renameRowFields(row, nextColumns, renameMap = {}) {
|
|
457
|
+
const reverseMap = Object.entries(renameMap).reduce((acc, [previous, next]) => {
|
|
458
|
+
acc[next] = previous;
|
|
459
|
+
return acc;
|
|
460
|
+
}, {});
|
|
461
|
+
return nextColumns.reduce((acc, column) => {
|
|
462
|
+
const previousKey = reverseMap[column] || column;
|
|
463
|
+
acc[column] = row?.[previousKey] ?? row?.[column] ?? "";
|
|
464
|
+
return acc;
|
|
465
|
+
}, {});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function transformTableSchema(workspaceConfig, table, { columns, renameMap = {} }) {
|
|
469
|
+
const nextColumns = Array.isArray(columns) ? columns.filter(Boolean) : table.columns;
|
|
470
|
+
if (!nextColumns.length) return workspaceConfig;
|
|
471
|
+
|
|
472
|
+
if (table.storage === "manual-object") {
|
|
473
|
+
const objects = normalizeManualObjects(workspaceConfig);
|
|
474
|
+
const dataModel = workspaceConfig.dataModel && typeof workspaceConfig.dataModel === "object" && !Array.isArray(workspaceConfig.dataModel)
|
|
475
|
+
? workspaceConfig.dataModel
|
|
476
|
+
: {};
|
|
477
|
+
return {
|
|
478
|
+
...workspaceConfig,
|
|
479
|
+
dataModel: {
|
|
480
|
+
...dataModel,
|
|
481
|
+
objects: objects.map((object) => {
|
|
482
|
+
if (object.id !== table.objectId) return object;
|
|
483
|
+
const rows = (Array.isArray(object.rows) ? object.rows : []).map((row) => renameRowFields(row, nextColumns, renameMap));
|
|
484
|
+
return {
|
|
485
|
+
...object,
|
|
486
|
+
columns: nextColumns,
|
|
487
|
+
rows,
|
|
488
|
+
fieldSettings: remapFieldSettings(object.fieldSettings || {}, nextColumns, renameMap)
|
|
489
|
+
};
|
|
490
|
+
})
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const ids = new Set((table.widgetRefs || []).map((ref) => ref.widgetId));
|
|
496
|
+
const mutateWidgets = (widgets) => (widgets || []).map((widget) => {
|
|
497
|
+
if (!ids.has(widget.id)) return widget;
|
|
498
|
+
const current = deriveWidgetTable(widget, { widgetId: widget.id });
|
|
499
|
+
if (!current?.mutable) return widget;
|
|
500
|
+
const rows = (current.rows || []).map((row) => renameRowFields(row, nextColumns, renameMap));
|
|
501
|
+
const nextFieldSettings = remapFieldSettings({
|
|
502
|
+
...(widget.config?.fieldSettings || {}),
|
|
503
|
+
sort: widget.config?.sort,
|
|
504
|
+
filter: widget.config?.filter
|
|
505
|
+
}, nextColumns, renameMap);
|
|
506
|
+
return {
|
|
507
|
+
...widget,
|
|
508
|
+
config: {
|
|
509
|
+
...writeTableConfig(widget.config || {}, current.storage, nextColumns, rows),
|
|
510
|
+
fieldSettings: nextFieldSettings,
|
|
511
|
+
sort: nextFieldSettings.sort,
|
|
512
|
+
filter: nextFieldSettings.filter
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
const dashboards = (workspaceConfig.dashboards || []).map((dashboard) => ({
|
|
518
|
+
...dashboard,
|
|
519
|
+
tabs: (dashboard.tabs || []).map((tab) => ({ ...tab, widgets: mutateWidgets(tab.widgets) }))
|
|
520
|
+
}));
|
|
521
|
+
let canvas = workspaceConfig.canvas ? { ...workspaceConfig.canvas } : {};
|
|
522
|
+
if (Array.isArray(canvas.widgets)) canvas = { ...canvas, widgets: mutateWidgets(canvas.widgets) };
|
|
523
|
+
if (Array.isArray(canvas.tabs)) canvas = { ...canvas, tabs: canvas.tabs.map((tab) => ({ ...tab, widgets: mutateWidgets(tab.widgets) })) };
|
|
524
|
+
return { ...workspaceConfig, dashboards, canvas };
|
|
525
|
+
}
|
|
526
|
+
|
|
257
527
|
function applyTableMutation(workspaceConfig, table, mutate) {
|
|
258
528
|
if (table.storage === "manual-object") {
|
|
259
529
|
const objects = normalizeManualObjects(workspaceConfig);
|
|
@@ -330,6 +600,16 @@ function uniqueObjectId(workspaceConfig, name) {
|
|
|
330
600
|
* targetObjectType:string, // objectType of the referenced object
|
|
331
601
|
* type: "belongs-to" | "has-many",
|
|
332
602
|
* description: string
|
|
603
|
+
* valueField?: string, // column on TARGET row used as stored FK value (default integrationId)
|
|
604
|
+
* labelField?: string, // primary label column on target (default Name)
|
|
605
|
+
* secondaryLabelField?: string,
|
|
606
|
+
* statusField?: string, // default status
|
|
607
|
+
* statusAllowlist?: string[] // when set, only rows whose status matches (case-insensitive) appear
|
|
608
|
+
* searchable?: boolean,
|
|
609
|
+
* pageSize?: number,
|
|
610
|
+
* resolver?: { integrationId: string } // optional listEntities-backed option source
|
|
611
|
+
* referenceSource?: "workspace-rows" | "source-records" // default workspace-rows
|
|
612
|
+
* sidecarSourceId?: string // when referenceSource is source-records
|
|
333
613
|
* }
|
|
334
614
|
*/
|
|
335
615
|
const OBJECT_TYPE_PRESETS = {
|
|
@@ -337,7 +617,20 @@ const OBJECT_TYPE_PRESETS = {
|
|
|
337
617
|
label: "Data Source",
|
|
338
618
|
icon: "Globe",
|
|
339
619
|
description: "Custom API, webhook, or external feed. References an API Registry record while credentials stay in workspace settings.",
|
|
340
|
-
columns: [
|
|
620
|
+
columns: [
|
|
621
|
+
"Name",
|
|
622
|
+
"registryId",
|
|
623
|
+
"endpoint",
|
|
624
|
+
"authRef",
|
|
625
|
+
"baseUrl",
|
|
626
|
+
"status",
|
|
627
|
+
"lastTested",
|
|
628
|
+
"lastResponse",
|
|
629
|
+
"entityType",
|
|
630
|
+
"sourceId",
|
|
631
|
+
"sourceStorage",
|
|
632
|
+
"resolverTemplateId"
|
|
633
|
+
],
|
|
341
634
|
relations: [
|
|
342
635
|
{
|
|
343
636
|
id: "resolver-binding",
|
|
@@ -345,7 +638,14 @@ const OBJECT_TYPE_PRESETS = {
|
|
|
345
638
|
field: "registryId",
|
|
346
639
|
targetObjectType: "api-registry",
|
|
347
640
|
type: "belongs-to",
|
|
348
|
-
description: "The API Registry entry whose fetchRecords function resolves this source. Set registryId to match the resolver integrationId."
|
|
641
|
+
description: "The API Registry entry whose fetchRecords function resolves this source. Set registryId to match the resolver integrationId.",
|
|
642
|
+
valueField: "integrationId",
|
|
643
|
+
labelField: "Name",
|
|
644
|
+
secondaryLabelField: "endpoint",
|
|
645
|
+
statusField: "status",
|
|
646
|
+
statusAllowlist: null,
|
|
647
|
+
searchable: true,
|
|
648
|
+
pageSize: 25
|
|
349
649
|
}
|
|
350
650
|
]
|
|
351
651
|
},
|
|
@@ -353,7 +653,23 @@ const OBJECT_TYPE_PRESETS = {
|
|
|
353
653
|
label: "API Registry",
|
|
354
654
|
icon: "Code2",
|
|
355
655
|
description: "HTTP API records with endpoint config, auth references, connection status, and stored test output.",
|
|
356
|
-
columns: [
|
|
656
|
+
columns: [
|
|
657
|
+
"integrationId",
|
|
658
|
+
"authRef",
|
|
659
|
+
"baseUrl",
|
|
660
|
+
"endpoint",
|
|
661
|
+
"method",
|
|
662
|
+
"status",
|
|
663
|
+
"lastTested",
|
|
664
|
+
"lastResponse",
|
|
665
|
+
"entityTypes",
|
|
666
|
+
"description",
|
|
667
|
+
"connectorKind",
|
|
668
|
+
"resolverTemplateId",
|
|
669
|
+
"schemaVersion",
|
|
670
|
+
"capabilities",
|
|
671
|
+
"executionLane"
|
|
672
|
+
],
|
|
357
673
|
relations: []
|
|
358
674
|
},
|
|
359
675
|
"people": {
|
|
@@ -383,6 +699,9 @@ const OBJECT_TYPE_PRESETS = {
|
|
|
383
699
|
"runtime",
|
|
384
700
|
"adapter",
|
|
385
701
|
"agentHost",
|
|
702
|
+
"localModel",
|
|
703
|
+
"localEndpoint",
|
|
704
|
+
"intelligenceAdapterMode",
|
|
386
705
|
"envRefs",
|
|
387
706
|
"networkAllow",
|
|
388
707
|
"allowList",
|
|
@@ -393,7 +712,10 @@ const OBJECT_TYPE_PRESETS = {
|
|
|
393
712
|
"lastTested",
|
|
394
713
|
"lastRunId",
|
|
395
714
|
"lastSourceId",
|
|
396
|
-
"lastResponse"
|
|
715
|
+
"lastResponse",
|
|
716
|
+
"resolverTemplateId",
|
|
717
|
+
"connectorKind",
|
|
718
|
+
"executionLane"
|
|
397
719
|
],
|
|
398
720
|
relations: [
|
|
399
721
|
{
|
|
@@ -402,7 +724,14 @@ const OBJECT_TYPE_PRESETS = {
|
|
|
402
724
|
field: "schedulerRegistryId",
|
|
403
725
|
targetObjectType: "api-registry",
|
|
404
726
|
type: "belongs-to",
|
|
405
|
-
description: "When runLocality is serverless, POST /api/workspace/sandbox-run sends growthub-sandbox-run-v1 to this API Registry record (METHOD, baseUrl, endpoint, authRef resolved server-side). Use for Supabase Edge URL, QStash forwarder, Vercel-exposed webhook, cron targets, etc."
|
|
727
|
+
description: "When runLocality is serverless, POST /api/workspace/sandbox-run sends growthub-sandbox-run-v1 to this API Registry record (METHOD, baseUrl, endpoint, authRef resolved server-side). Use for Supabase Edge URL, QStash forwarder, Vercel-exposed webhook, cron targets, etc.",
|
|
728
|
+
valueField: "integrationId",
|
|
729
|
+
labelField: "Name",
|
|
730
|
+
secondaryLabelField: "endpoint",
|
|
731
|
+
statusField: "status",
|
|
732
|
+
statusAllowlist: ["connected", "approved", "ok", "success"],
|
|
733
|
+
searchable: true,
|
|
734
|
+
pageSize: 25
|
|
406
735
|
}
|
|
407
736
|
]
|
|
408
737
|
},
|
|
@@ -440,7 +769,7 @@ function createTypedBusinessObject(workspaceConfig, { name, objectType = "custom
|
|
|
440
769
|
rows: [],
|
|
441
770
|
binding: { mode: "manual", source: "Data Model" },
|
|
442
771
|
relations: preset.relations ? preset.relations.map((r) => ({ ...r })) : [],
|
|
443
|
-
fieldSettings: {
|
|
772
|
+
fieldSettings: normalizeFieldSettings({}, columns)
|
|
444
773
|
};
|
|
445
774
|
return {
|
|
446
775
|
...workspaceConfig,
|
|
@@ -468,7 +797,7 @@ function createManualBusinessObject(workspaceConfig, { name, fields } = {}) {
|
|
|
468
797
|
columns,
|
|
469
798
|
rows: [],
|
|
470
799
|
binding: { mode: "manual", source: "Data Model" },
|
|
471
|
-
fieldSettings: {
|
|
800
|
+
fieldSettings: normalizeFieldSettings({}, columns)
|
|
472
801
|
};
|
|
473
802
|
return {
|
|
474
803
|
...workspaceConfig,
|
|
@@ -479,13 +808,22 @@ function createManualBusinessObject(workspaceConfig, { name, fields } = {}) {
|
|
|
479
808
|
};
|
|
480
809
|
}
|
|
481
810
|
|
|
482
|
-
function addTableField(workspaceConfig, table,
|
|
483
|
-
const
|
|
811
|
+
function addTableField(workspaceConfig, table, fieldSpec) {
|
|
812
|
+
const spec = fieldSpec && typeof fieldSpec === "object" && !Array.isArray(fieldSpec)
|
|
813
|
+
? fieldSpec
|
|
814
|
+
: { name: fieldSpec };
|
|
815
|
+
const name = String(spec.name || "").trim();
|
|
816
|
+
const fieldType = String(spec.type || "").trim();
|
|
484
817
|
if (!name || !table.mutable) return workspaceConfig;
|
|
485
|
-
|
|
818
|
+
const nextConfig = applyTableMutation(workspaceConfig, table, ({ columns, rows }) => {
|
|
486
819
|
if (columns.includes(name)) return { columns, rows };
|
|
487
820
|
return { columns: [...columns, name], rows: rows.map((row) => ({ ...row, [name]: "" })) };
|
|
488
821
|
});
|
|
822
|
+
return updateTableFieldSettings(nextConfig, { ...table, columns: table.columns.includes(name) ? table.columns : [...table.columns, name] }, (settings) => ({
|
|
823
|
+
...settings,
|
|
824
|
+
order: settings.order.includes(name) ? settings.order : [...settings.order, name],
|
|
825
|
+
types: fieldType ? { ...(settings.types || {}), [name]: fieldType } : settings.types
|
|
826
|
+
}));
|
|
489
827
|
}
|
|
490
828
|
|
|
491
829
|
function addTableRow(workspaceConfig, table) {
|
|
@@ -621,6 +959,169 @@ function describeBindingMode(binding) {
|
|
|
621
959
|
return { label: "Manual local table", description: "Rows and fields live in the existing widget config and travel with workspace export/import." };
|
|
622
960
|
}
|
|
623
961
|
|
|
962
|
+
/**
|
|
963
|
+
* Normalize a reference option for API/UI interchange.
|
|
964
|
+
*/
|
|
965
|
+
function normalizeReferenceOption(option) {
|
|
966
|
+
if (!option || typeof option !== "object") return null;
|
|
967
|
+
const value = String(option.value ?? "").trim();
|
|
968
|
+
if (!value) return null;
|
|
969
|
+
const source = ["workspace-config", "source-records", "resolver"].includes(option.source)
|
|
970
|
+
? option.source
|
|
971
|
+
: "workspace-config";
|
|
972
|
+
const out = {
|
|
973
|
+
value,
|
|
974
|
+
label: String(option.label ?? value).trim() || value,
|
|
975
|
+
source,
|
|
976
|
+
objectType: typeof option.objectType === "string" && option.objectType.trim() ? option.objectType.trim() : undefined,
|
|
977
|
+
provider: typeof option.provider === "string" && option.provider.trim() ? option.provider.trim() : undefined,
|
|
978
|
+
status: typeof option.status === "string" && option.status.trim() ? option.status.trim() : undefined
|
|
979
|
+
};
|
|
980
|
+
if (option.secondaryLabel !== undefined && option.secondaryLabel !== null && String(option.secondaryLabel).trim()) {
|
|
981
|
+
out.secondaryLabel = String(option.secondaryLabel).trim();
|
|
982
|
+
}
|
|
983
|
+
if (option.metadata && typeof option.metadata === "object" && !Array.isArray(option.metadata)) {
|
|
984
|
+
out.metadata = option.metadata;
|
|
985
|
+
}
|
|
986
|
+
return out;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Merge preset relation defaults with stored `object.relations[]` so older rows
|
|
991
|
+
* pick up new optional metadata (valueField, statusAllowlist, …).
|
|
992
|
+
*/
|
|
993
|
+
function effectiveRelations(object) {
|
|
994
|
+
const stored = Array.isArray(object?.relations) ? object.relations : [];
|
|
995
|
+
const presets =
|
|
996
|
+
object?.objectType && OBJECT_TYPE_PRESETS[object.objectType]?.relations
|
|
997
|
+
? OBJECT_TYPE_PRESETS[object.objectType].relations
|
|
998
|
+
: [];
|
|
999
|
+
const presetFields = new Set(presets.map((p) => p.field).filter(Boolean));
|
|
1000
|
+
const mergedByField = new Map();
|
|
1001
|
+
for (const preset of presets) {
|
|
1002
|
+
if (!preset?.field) continue;
|
|
1003
|
+
const storedMatch = stored.find((s) => s?.field === preset.field);
|
|
1004
|
+
mergedByField.set(preset.field, { ...preset, ...(storedMatch || {}) });
|
|
1005
|
+
}
|
|
1006
|
+
const extras = stored.filter((s) => s?.field && !presetFields.has(s.field));
|
|
1007
|
+
return [...Array.from(mergedByField.values()), ...extras];
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
function findRelationForField(object, field) {
|
|
1011
|
+
if (!field) return null;
|
|
1012
|
+
return effectiveRelations(object).find((r) => r.field === field) || null;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
function listReferenceFields(object) {
|
|
1016
|
+
return effectiveRelations(object).map((r) => r.field).filter(Boolean);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
function decodeRefCursor(cursor) {
|
|
1020
|
+
if (typeof cursor !== "string" || !cursor.startsWith("o:")) return 0;
|
|
1021
|
+
const n = Number(cursor.slice(2));
|
|
1022
|
+
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
function encodeRefCursor(offset) {
|
|
1026
|
+
return `o:${offset}`;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Resolve reference options from local `dataModel.objects[]` rows (workspace-config source).
|
|
1031
|
+
*/
|
|
1032
|
+
function resolveLocalReferenceOptions(workspaceConfig, {
|
|
1033
|
+
objectId,
|
|
1034
|
+
field,
|
|
1035
|
+
query = "",
|
|
1036
|
+
cursor = null,
|
|
1037
|
+
limit = 25,
|
|
1038
|
+
relation: relationOverride = null
|
|
1039
|
+
} = {}) {
|
|
1040
|
+
const objects = normalizeManualObjects(workspaceConfig);
|
|
1041
|
+
const objectItem = objects.find((o) => o.id === objectId) || null;
|
|
1042
|
+
const relation = relationOverride || (objectItem && field ? findRelationForField(objectItem, field) : null);
|
|
1043
|
+
if (!relation || !relation.targetObjectType) {
|
|
1044
|
+
return { options: [], nextCursor: null, reason: objectItem ? "unknown-field" : "unknown-object" };
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
const valueField = typeof relation.valueField === "string" && relation.valueField.trim()
|
|
1048
|
+
? relation.valueField.trim()
|
|
1049
|
+
: "integrationId";
|
|
1050
|
+
const labelField = typeof relation.labelField === "string" && relation.labelField.trim()
|
|
1051
|
+
? relation.labelField.trim()
|
|
1052
|
+
: "Name";
|
|
1053
|
+
const secondaryLabelField =
|
|
1054
|
+
typeof relation.secondaryLabelField === "string" && relation.secondaryLabelField.trim()
|
|
1055
|
+
? relation.secondaryLabelField.trim()
|
|
1056
|
+
: "";
|
|
1057
|
+
const statusField =
|
|
1058
|
+
typeof relation.statusField === "string" && relation.statusField.trim()
|
|
1059
|
+
? relation.statusField.trim()
|
|
1060
|
+
: "status";
|
|
1061
|
+
const allowlist = Array.isArray(relation.statusAllowlist)
|
|
1062
|
+
? relation.statusAllowlist.map((s) => String(s).toLowerCase())
|
|
1063
|
+
: null;
|
|
1064
|
+
|
|
1065
|
+
const pageSize = Math.min(100, Math.max(1, Number(relation.pageSize) || Number(limit) || 25));
|
|
1066
|
+
const offset = decodeRefCursor(cursor);
|
|
1067
|
+
|
|
1068
|
+
const targetObjectId = typeof relation.targetObjectId === "string" && relation.targetObjectId.trim()
|
|
1069
|
+
? relation.targetObjectId.trim()
|
|
1070
|
+
: "";
|
|
1071
|
+
const targets = objects.filter((o) => (
|
|
1072
|
+
o.objectType === relation.targetObjectType
|
|
1073
|
+
&& (!targetObjectId || o.id === targetObjectId)
|
|
1074
|
+
));
|
|
1075
|
+
const needle = String(query || "").trim().toLowerCase();
|
|
1076
|
+
|
|
1077
|
+
const candidates = [];
|
|
1078
|
+
for (const target of targets) {
|
|
1079
|
+
const rows = Array.isArray(target.rows) ? target.rows : [];
|
|
1080
|
+
rows.forEach((row, index) => {
|
|
1081
|
+
if (!row || typeof row !== "object") return;
|
|
1082
|
+
const rawValue =
|
|
1083
|
+
row[valueField] ??
|
|
1084
|
+
row.integrationId ??
|
|
1085
|
+
row.id ??
|
|
1086
|
+
row.Name ??
|
|
1087
|
+
`${target.id}:${index}`;
|
|
1088
|
+
const value = String(rawValue ?? "").trim();
|
|
1089
|
+
if (!value) return;
|
|
1090
|
+
const label = String(row[labelField] ?? row.Name ?? row.integrationId ?? value).trim() || value;
|
|
1091
|
+
const secondaryLabel = secondaryLabelField
|
|
1092
|
+
? String(row[secondaryLabelField] ?? "").trim()
|
|
1093
|
+
: "";
|
|
1094
|
+
const status = String(row[statusField] ?? "").trim();
|
|
1095
|
+
if (allowlist && allowlist.length) {
|
|
1096
|
+
const st = status.toLowerCase();
|
|
1097
|
+
if (!st || !allowlist.includes(st)) return;
|
|
1098
|
+
}
|
|
1099
|
+
if (needle) {
|
|
1100
|
+
const hay = `${value} ${label} ${secondaryLabel} ${status}`.toLowerCase();
|
|
1101
|
+
if (!hay.includes(needle)) return;
|
|
1102
|
+
}
|
|
1103
|
+
candidates.push(
|
|
1104
|
+
normalizeReferenceOption({
|
|
1105
|
+
value,
|
|
1106
|
+
label,
|
|
1107
|
+
secondaryLabel: secondaryLabel || undefined,
|
|
1108
|
+
source: "workspace-config",
|
|
1109
|
+
objectType: relation.targetObjectType,
|
|
1110
|
+
status: status || undefined,
|
|
1111
|
+
metadata: { objectLabel: target.label || target.source }
|
|
1112
|
+
})
|
|
1113
|
+
);
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
const filtered = candidates.filter(Boolean);
|
|
1118
|
+
const page = filtered.slice(offset, offset + pageSize);
|
|
1119
|
+
const nextOffset = offset + page.length;
|
|
1120
|
+
const nextCursor = nextOffset < filtered.length ? encodeRefCursor(nextOffset) : null;
|
|
1121
|
+
|
|
1122
|
+
return { options: page, nextCursor, reason: null, total: filtered.length };
|
|
1123
|
+
}
|
|
1124
|
+
|
|
624
1125
|
export {
|
|
625
1126
|
OBJECT_TYPE_PRESETS,
|
|
626
1127
|
addTableField,
|
|
@@ -632,13 +1133,23 @@ export {
|
|
|
632
1133
|
describeBindingLane,
|
|
633
1134
|
describeBindingMode,
|
|
634
1135
|
duplicateTableRow,
|
|
1136
|
+
effectiveRelations,
|
|
635
1137
|
exportTableAsCsv,
|
|
1138
|
+
findRelationForField,
|
|
636
1139
|
importTableFromCsv,
|
|
1140
|
+
listReferenceFields,
|
|
637
1141
|
listSavedEnvRefs,
|
|
638
1142
|
listWorkspaceDataModelTables,
|
|
1143
|
+
normalizeManualObjects,
|
|
1144
|
+
normalizeReferenceOption,
|
|
639
1145
|
parseSandboxAllowList,
|
|
640
1146
|
parseSandboxEnvRefs,
|
|
641
1147
|
replaceTableContent,
|
|
1148
|
+
resolveLocalReferenceOptions,
|
|
642
1149
|
sandboxRunSourceId,
|
|
1150
|
+
snapshotTableViewState,
|
|
1151
|
+
transformTableSchema,
|
|
1152
|
+
normalizeFieldSettings,
|
|
1153
|
+
updateTableFieldSettings,
|
|
643
1154
|
updateTableCell
|
|
644
1155
|
};
|