@growthub/cli 0.9.13 → 0.9.14
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/README.md +27 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integration-entities/route.js +41 -9
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/list-entities/route.js +67 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/refresh-source/route.js +124 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/refresh-sources/route.js +127 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/register-resolver/route.js +119 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/resolvers/route.js +41 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/test-api-record/route.js +126 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/test-source/route.js +130 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/page.jsx +692 -223
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +996 -4
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +1539 -433
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/data-sources-api-registry.md +139 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/resolver-loader.js +57 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/resolvers/README.md +133 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/resolvers/google-analytics.js +160 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/source-resolver-registry.js +85 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-config.js +79 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +104 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +23 -6
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package.json +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +7 -0
- package/dist/index.js +1764 -40677
- package/package.json +1 -1
|
@@ -171,9 +171,12 @@ function deriveManualObjectTable(object) {
|
|
|
171
171
|
id: `manual-object:${object.id || source}`,
|
|
172
172
|
label: object.label || object.name || source,
|
|
173
173
|
source,
|
|
174
|
+
objectType: object.objectType || "custom",
|
|
175
|
+
icon: object.icon || null,
|
|
174
176
|
columns,
|
|
175
177
|
rows,
|
|
176
178
|
binding: object.binding || { mode: "manual", source: "Data Model" },
|
|
179
|
+
relations: Array.isArray(object.relations) ? object.relations : [],
|
|
177
180
|
mutable: true,
|
|
178
181
|
storage: "manual-object",
|
|
179
182
|
objectId: object.id,
|
|
@@ -313,6 +316,105 @@ function uniqueObjectId(workspaceConfig, name) {
|
|
|
313
316
|
return `${base}-${index}`;
|
|
314
317
|
}
|
|
315
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Top-level object type presets.
|
|
321
|
+
* Each entry defines: label, icon (Lucide name), description, default columns, and
|
|
322
|
+
* any built-in relations. These are the five first-class types the UI offers when
|
|
323
|
+
* a user clicks "New object" — they act like schema templates, not hard constraints.
|
|
324
|
+
*
|
|
325
|
+
* Relation shape:
|
|
326
|
+
* {
|
|
327
|
+
* id: string, // stable slug within this object
|
|
328
|
+
* name: string, // display label
|
|
329
|
+
* field: string, // FK column on THIS object
|
|
330
|
+
* targetObjectType:string, // objectType of the referenced object
|
|
331
|
+
* type: "belongs-to" | "has-many",
|
|
332
|
+
* description: string
|
|
333
|
+
* }
|
|
334
|
+
*/
|
|
335
|
+
const OBJECT_TYPE_PRESETS = {
|
|
336
|
+
"data-source": {
|
|
337
|
+
label: "Data Source",
|
|
338
|
+
icon: "Globe",
|
|
339
|
+
description: "Custom API, webhook, or external feed. References an API Registry record while credentials stay in workspace settings.",
|
|
340
|
+
columns: ["Name", "registryId", "endpoint", "authRef", "baseUrl", "status", "lastTested", "lastResponse"],
|
|
341
|
+
relations: [
|
|
342
|
+
{
|
|
343
|
+
id: "resolver-binding",
|
|
344
|
+
name: "Resolver",
|
|
345
|
+
field: "registryId",
|
|
346
|
+
targetObjectType: "api-registry",
|
|
347
|
+
type: "belongs-to",
|
|
348
|
+
description: "The API Registry entry whose fetchRecords function resolves this source. Set registryId to match the resolver integrationId."
|
|
349
|
+
}
|
|
350
|
+
]
|
|
351
|
+
},
|
|
352
|
+
"api-registry": {
|
|
353
|
+
label: "API Registry",
|
|
354
|
+
icon: "Code2",
|
|
355
|
+
description: "HTTP API records with endpoint config, auth references, connection status, and stored test output.",
|
|
356
|
+
columns: ["integrationId", "authRef", "baseUrl", "endpoint", "method", "status", "lastTested", "lastResponse", "entityTypes", "description"],
|
|
357
|
+
relations: []
|
|
358
|
+
},
|
|
359
|
+
"people": {
|
|
360
|
+
label: "People",
|
|
361
|
+
icon: "Users",
|
|
362
|
+
description: "Contacts, leads, or team members with standard CRM fields.",
|
|
363
|
+
columns: ["Name", "Email", "Phone", "Company", "Status", "Role"],
|
|
364
|
+
relations: []
|
|
365
|
+
},
|
|
366
|
+
"tasks": {
|
|
367
|
+
label: "Tasks",
|
|
368
|
+
icon: "CheckSquare",
|
|
369
|
+
description: "Action items, to-dos, or work items.",
|
|
370
|
+
columns: ["Name", "Status", "DueDate", "Assignee", "Priority"],
|
|
371
|
+
relations: []
|
|
372
|
+
},
|
|
373
|
+
"custom": {
|
|
374
|
+
label: "Custom",
|
|
375
|
+
icon: "Plus",
|
|
376
|
+
description: "Start with a blank table — define your own fields and records.",
|
|
377
|
+
columns: ["Name"],
|
|
378
|
+
relations: []
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Create a typed business object from a preset template.
|
|
384
|
+
* Accepts objectType (one of the OBJECT_TYPE_PRESETS keys) and an optional icon override.
|
|
385
|
+
* The object is stored in dataModel.objects[] alongside manual objects.
|
|
386
|
+
*/
|
|
387
|
+
function createTypedBusinessObject(workspaceConfig, { name, objectType = "custom", icon } = {}) {
|
|
388
|
+
const label = String(name || "").trim();
|
|
389
|
+
if (!label) return workspaceConfig;
|
|
390
|
+
const preset = OBJECT_TYPE_PRESETS[objectType] || OBJECT_TYPE_PRESETS.custom;
|
|
391
|
+
const columns = [...preset.columns];
|
|
392
|
+
const dataModel =
|
|
393
|
+
workspaceConfig.dataModel && typeof workspaceConfig.dataModel === "object" && !Array.isArray(workspaceConfig.dataModel)
|
|
394
|
+
? workspaceConfig.dataModel
|
|
395
|
+
: {};
|
|
396
|
+
const id = uniqueObjectId(workspaceConfig, label);
|
|
397
|
+
const object = {
|
|
398
|
+
id,
|
|
399
|
+
label,
|
|
400
|
+
source: label,
|
|
401
|
+
objectType,
|
|
402
|
+
icon: icon || preset.icon,
|
|
403
|
+
columns,
|
|
404
|
+
rows: [],
|
|
405
|
+
binding: { mode: "manual", source: "Data Model" },
|
|
406
|
+
relations: preset.relations ? preset.relations.map((r) => ({ ...r })) : [],
|
|
407
|
+
fieldSettings: { hidden: [], order: columns }
|
|
408
|
+
};
|
|
409
|
+
return {
|
|
410
|
+
...workspaceConfig,
|
|
411
|
+
dataModel: {
|
|
412
|
+
...dataModel,
|
|
413
|
+
objects: [...normalizeManualObjects(workspaceConfig), object]
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
316
418
|
function createManualBusinessObject(workspaceConfig, { name, fields } = {}) {
|
|
317
419
|
const label = String(name || "").trim();
|
|
318
420
|
const columns = Array.from(new Set((Array.isArray(fields) ? fields : String(fields || "").split(","))
|
|
@@ -417,10 +519,12 @@ function describeBindingMode(binding) {
|
|
|
417
519
|
}
|
|
418
520
|
|
|
419
521
|
export {
|
|
522
|
+
OBJECT_TYPE_PRESETS,
|
|
420
523
|
addTableField,
|
|
421
524
|
addTableRow,
|
|
422
525
|
appendRowsToTable,
|
|
423
526
|
createManualBusinessObject,
|
|
527
|
+
createTypedBusinessObject,
|
|
424
528
|
deleteTableRow,
|
|
425
529
|
describeBindingLane,
|
|
426
530
|
describeBindingMode,
|
|
@@ -152,7 +152,9 @@ const WIDGET_SCHEMA_CONTRACTS = {
|
|
|
152
152
|
lane: "string optional (when mode === 'integration')",
|
|
153
153
|
entityId: "string optional — stable source object ID (never a token or credential)",
|
|
154
154
|
entityType: "string optional — adapter-provided object type",
|
|
155
|
-
entityLabel: "string optional — display-only resolved label, not authoritative"
|
|
155
|
+
entityLabel: "string optional — display-only resolved label, not authoritative",
|
|
156
|
+
sourceStorage: "'workspace-source-records' optional — marks this binding as live-backed; records are written by POST /api/workspace/refresh-sources and keyed by dataModel.objects[].sourceId",
|
|
157
|
+
sourceId: "string optional — stable key in growthub.source-records.json; required when sourceStorage === 'workspace-source-records'"
|
|
156
158
|
},
|
|
157
159
|
NormalizedIntegrationEntity: {
|
|
158
160
|
id: "non-empty string — stable source object ID",
|
|
@@ -200,19 +202,19 @@ const SAMPLE_DATA_BINDINGS = {
|
|
|
200
202
|
function defaultConfigFor(kind) {
|
|
201
203
|
switch (kind) {
|
|
202
204
|
case "chart":
|
|
203
|
-
return { values: [
|
|
205
|
+
return { values: [], binding: { mode: "manual", source: "", rows: [] } };
|
|
204
206
|
case "view":
|
|
205
207
|
return {
|
|
206
208
|
source: "",
|
|
207
209
|
layout: "Table",
|
|
208
210
|
columns: [],
|
|
209
211
|
rows: [],
|
|
210
|
-
binding: { mode: "manual", source: "
|
|
212
|
+
binding: { mode: "manual", source: "", rows: [] }
|
|
211
213
|
};
|
|
212
214
|
case "iframe":
|
|
213
215
|
return { url: "" };
|
|
214
216
|
case "rich-text":
|
|
215
|
-
return { text: "", binding: { mode: "manual", source: "
|
|
217
|
+
return { text: "", binding: { mode: "manual", source: "", rows: [] } };
|
|
216
218
|
default:
|
|
217
219
|
return {};
|
|
218
220
|
}
|
|
@@ -396,8 +398,11 @@ function validateStaticDataBinding(binding, path, errors) {
|
|
|
396
398
|
if (typeof binding.integrationId !== "string" || !binding.integrationId.trim()) {
|
|
397
399
|
errors.push(`${path}.integrationId is required when mode is integration`);
|
|
398
400
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
+
// lane is not required when sourceStorage delegates routing to a registry resolver
|
|
402
|
+
if (binding.sourceStorage !== "workspace-source-records") {
|
|
403
|
+
if (typeof binding.lane !== "string" || !binding.lane.trim()) {
|
|
404
|
+
errors.push(`${path}.lane is required when mode is integration`);
|
|
405
|
+
}
|
|
401
406
|
}
|
|
402
407
|
}
|
|
403
408
|
if (binding.source !== undefined && typeof binding.source !== "string") {
|
|
@@ -439,6 +444,14 @@ function validateStaticDataBinding(binding, path, errors) {
|
|
|
439
444
|
if (binding.entityLabel !== undefined && typeof binding.entityLabel !== "string") {
|
|
440
445
|
errors.push(`${path}.entityLabel must be a string`);
|
|
441
446
|
}
|
|
447
|
+
if (binding.sourceStorage !== undefined) {
|
|
448
|
+
if (binding.sourceStorage !== "workspace-source-records") {
|
|
449
|
+
errors.push(`${path}.sourceStorage must be "workspace-source-records" when present`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (binding.sourceId !== undefined && typeof binding.sourceId !== "string") {
|
|
453
|
+
errors.push(`${path}.sourceId must be a string`);
|
|
454
|
+
}
|
|
442
455
|
}
|
|
443
456
|
|
|
444
457
|
function validateFieldSettings(fieldSettings, path, errors) {
|
|
@@ -805,6 +818,7 @@ function validateDataModelConfig(dataModel, errors) {
|
|
|
805
818
|
}
|
|
806
819
|
if (typeof object.label !== "string" || !object.label.trim()) errors.push(`${prefix}.label must be a non-empty string`);
|
|
807
820
|
if (object.source !== undefined && typeof object.source !== "string") errors.push(`${prefix}.source must be a string`);
|
|
821
|
+
if (object.sourceId !== undefined && typeof object.sourceId !== "string") errors.push(`${prefix}.sourceId must be a string`);
|
|
808
822
|
validateStringArray(object.columns, `${prefix}.columns`, errors);
|
|
809
823
|
if (!Array.isArray(object.rows)) {
|
|
810
824
|
errors.push(`${prefix}.rows must be an array`);
|
|
@@ -814,6 +828,9 @@ function validateDataModelConfig(dataModel, errors) {
|
|
|
814
828
|
});
|
|
815
829
|
}
|
|
816
830
|
validateStaticDataBinding(object.binding, `${prefix}.binding`, errors);
|
|
831
|
+
if (object.binding?.sourceStorage === "workspace-source-records" && typeof object.sourceId !== "string") {
|
|
832
|
+
errors.push(`${prefix}.sourceId is required when binding.sourceStorage is "workspace-source-records"`);
|
|
833
|
+
}
|
|
817
834
|
validateFieldSettings(object.fieldSettings, `${prefix}.fieldSettings`, errors);
|
|
818
835
|
});
|
|
819
836
|
}
|
|
@@ -76,6 +76,10 @@
|
|
|
76
76
|
"apps/workspace/app/workspace-builder.jsx",
|
|
77
77
|
"apps/workspace/app/settings/integrations/page.jsx",
|
|
78
78
|
"apps/workspace/app/api/workspace/route.js",
|
|
79
|
+
"apps/workspace/app/api/workspace/refresh-sources/route.js",
|
|
80
|
+
"apps/workspace/app/api/workspace/test-source/route.js",
|
|
81
|
+
"apps/workspace/app/api/workspace/register-resolver/route.js",
|
|
82
|
+
"apps/workspace/app/api/workspace/resolvers/route.js",
|
|
79
83
|
"apps/workspace/app/api/settings/integrations/route.js",
|
|
80
84
|
"apps/workspace/lib/workspace-schema.js",
|
|
81
85
|
"apps/workspace/lib/workspace-config.js",
|
|
@@ -85,6 +89,9 @@
|
|
|
85
89
|
"apps/workspace/lib/adapters/auth/index.js",
|
|
86
90
|
"apps/workspace/lib/adapters/integrations/index.js",
|
|
87
91
|
"apps/workspace/lib/adapters/integrations/growthub-connection-normalizer.js",
|
|
92
|
+
"apps/workspace/lib/adapters/integrations/source-resolver-registry.js",
|
|
93
|
+
"apps/workspace/lib/adapters/integrations/resolver-loader.js",
|
|
94
|
+
"apps/workspace/lib/adapters/integrations/resolvers/README.md",
|
|
88
95
|
"apps/workspace/lib/adapters/payments/index.js",
|
|
89
96
|
"apps/workspace/lib/adapters/persistence/index.js",
|
|
90
97
|
"apps/workspace/lib/adapters/persistence/postgres.js",
|