@growthub/cli 0.9.9 → 0.9.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.
- package/README.md +1 -1
- package/assets/worker-kits/creative-strategist-v1/kit.json +5 -2
- package/assets/worker-kits/growthub-agency-portal-starter-v1/kit.json +4 -1
- package/assets/worker-kits/growthub-ai-website-cloner-v1/kit.json +6 -3
- package/assets/worker-kits/growthub-creative-video-pipeline-v1/kit.json +4 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/README.md +4 -4
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integration-entities/route.js +50 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +980 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/page.jsx +5 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/integrations/page.jsx +4 -5
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +1686 -68
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/growthub-connection-normalizer.js +12 -16
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/index.js +61 -11
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/domain/integrations.js +31 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +236 -9
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package-lock.json +10 -64
- 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 +5 -2
- package/assets/worker-kits/growthub-email-marketing-v1/kit.json +5 -2
- package/assets/worker-kits/growthub-geo-seo-v1/kit.json +5 -2
- package/assets/worker-kits/growthub-hyperframes-studio-v1/kit.json +5 -2
- package/assets/worker-kits/growthub-marketing-skills-v1/kit.json +6 -3
- package/assets/worker-kits/growthub-open-higgsfield-studio-v1/kit.json +5 -2
- package/assets/worker-kits/growthub-open-montage-studio-v1/kit.json +6 -3
- package/assets/worker-kits/growthub-postiz-social-v1/kit.json +5 -2
- package/assets/worker-kits/growthub-twenty-crm-v1/kit.json +6 -3
- package/assets/worker-kits/growthub-video-use-studio-v1/kit.json +5 -2
- package/assets/worker-kits/growthub-zernio-social-v1/kit.json +5 -2
- package/dist/index.js +1750 -433
- package/package.json +1 -1
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
const providerAliases = {
|
|
2
|
-
ga4: "google-analytics",
|
|
3
|
-
google_analytics: "google-analytics",
|
|
4
|
-
google_drive: "google-drive",
|
|
5
|
-
ghl: "go-high-level",
|
|
6
|
-
gohighlevel: "go-high-level",
|
|
7
|
-
meta: "meta-ads",
|
|
8
|
-
meta_ads: "meta-ads"
|
|
9
|
-
};
|
|
10
1
|
function normalizeProviderId(provider) {
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
return provider.trim().toLowerCase().replaceAll("_", "-");
|
|
3
|
+
}
|
|
4
|
+
function providerLabel(provider) {
|
|
5
|
+
return normalizeProviderId(provider)
|
|
6
|
+
.split("-")
|
|
7
|
+
.filter(Boolean)
|
|
8
|
+
.map((part) => part.slice(0, 1).toUpperCase() + part.slice(1))
|
|
9
|
+
.join(" ");
|
|
13
10
|
}
|
|
14
11
|
function isHostedRecord(row) {
|
|
15
12
|
return "provider" in row && ("ready" in row || "connectedAt" in row || "scopes" in row || "handle" in row);
|
|
@@ -41,10 +38,10 @@ function normalizeMcpAccount(account) {
|
|
|
41
38
|
const isVerified = account.isVerified === true;
|
|
42
39
|
const isConnected = isActive;
|
|
43
40
|
return {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
id: provider,
|
|
42
|
+
provider,
|
|
43
|
+
label: providerLabel(provider),
|
|
44
|
+
name: providerLabel(provider),
|
|
48
45
|
authType: normalizeConnectionType(account.connectionType),
|
|
49
46
|
status: isConnected ? "connected" : "needs-connection",
|
|
50
47
|
isConnected,
|
|
@@ -54,7 +51,6 @@ function normalizeMcpAccount(account) {
|
|
|
54
51
|
setupMode: "hosted-authority",
|
|
55
52
|
connectionMetadata: {
|
|
56
53
|
source: "growthub-mcp-accounts",
|
|
57
|
-
accountId: account.id,
|
|
58
54
|
connectionName: account.connectionName,
|
|
59
55
|
connectionType: account.connectionType,
|
|
60
56
|
isVerified,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readAdapterConfig } from "@/lib/adapters/env";
|
|
2
2
|
import {
|
|
3
|
-
governedWorkspaceIntegrationCatalog
|
|
3
|
+
governedWorkspaceIntegrationCatalog,
|
|
4
|
+
normalizeIntegrationEntities
|
|
4
5
|
} from "@/lib/domain/integrations";
|
|
5
6
|
import {
|
|
6
7
|
normalizeGrowthubBridgePayload
|
|
@@ -136,10 +137,10 @@ function mergeBridgeRows(rows) {
|
|
|
136
137
|
});
|
|
137
138
|
if (!row) return catalogItem;
|
|
138
139
|
seenProviders.add(row.provider || row.id || catalogItem.provider);
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
return {
|
|
141
|
+
...catalogItem,
|
|
142
|
+
label: catalogItem.label,
|
|
143
|
+
name: catalogItem.name,
|
|
143
144
|
icon: row.icon || catalogItem.icon,
|
|
144
145
|
description: row.description || catalogItem.description,
|
|
145
146
|
category: row.category || catalogItem.category,
|
|
@@ -150,7 +151,6 @@ function mergeBridgeRows(rows) {
|
|
|
150
151
|
setupMode: row.setupMode || catalogItem.setupMode,
|
|
151
152
|
status: row.status || (row.isConnected || row.isActive ? "connected" : catalogItem.status),
|
|
152
153
|
connectionId: row.connectionId,
|
|
153
|
-
accountId: row.accountId,
|
|
154
154
|
secretEnvName: row.secretEnvName,
|
|
155
155
|
connectionMetadata: row.connectionMetadata || row.metadata,
|
|
156
156
|
metadata: row.metadata || row.connectionMetadata
|
|
@@ -167,8 +167,9 @@ function mergeBridgeRows(rows) {
|
|
|
167
167
|
function toDiscoveredIntegration(row) {
|
|
168
168
|
const provider = row.provider || row.id || "unknown-provider";
|
|
169
169
|
const label = row.label || row.name || provider;
|
|
170
|
-
const isDataPipeline = ["windsor-ai", "google-sheets", "google-analytics", "shopify", "meta-ads"].includes(provider);
|
|
171
170
|
const isConnected = row.isConnected ?? row.status === "connected";
|
|
171
|
+
const lane = typeof row.lane === "string" && row.lane ? row.lane : "workspace-integration";
|
|
172
|
+
const objectType = typeof row.objectType === "string" && row.objectType ? row.objectType : "mcp-connection";
|
|
172
173
|
return {
|
|
173
174
|
id: row.id || provider,
|
|
174
175
|
label,
|
|
@@ -180,19 +181,68 @@ function toDiscoveredIntegration(row) {
|
|
|
180
181
|
authType: row.authType || "oauth_first_party",
|
|
181
182
|
isConnected,
|
|
182
183
|
isActive: row.isActive ?? isConnected,
|
|
183
|
-
lane
|
|
184
|
-
objectType
|
|
184
|
+
lane,
|
|
185
|
+
objectType,
|
|
185
186
|
status: row.status || (isConnected ? "connected" : "needs-connection"),
|
|
186
187
|
authPath: row.authPath || "growthub-mcp-bridge",
|
|
187
188
|
setupMode: row.setupMode || "hosted-authority",
|
|
188
189
|
connectionId: row.connectionId,
|
|
189
|
-
accountId: row.accountId,
|
|
190
190
|
secretEnvName: row.secretEnvName,
|
|
191
191
|
connectionMetadata: row.connectionMetadata || row.metadata,
|
|
192
192
|
metadata: row.metadata || row.connectionMetadata
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Governed Integration Reference Binding — entity metadata resolution.
|
|
197
|
+
*
|
|
198
|
+
* Returns NormalizedIntegrationEntity[] for the requested integration when a
|
|
199
|
+
* server-side object resolver is available. Bridge connection discovery alone
|
|
200
|
+
* does not fabricate provider objects.
|
|
201
|
+
*
|
|
202
|
+
* Authority invariant: this function runs server-side only (API route).
|
|
203
|
+
* The browser NEVER calls provider APIs, holds tokens, or resolves entities.
|
|
204
|
+
*/
|
|
205
|
+
async function listEntityMetadataForIntegration(integrationId) {
|
|
206
|
+
if (!integrationId) return [];
|
|
207
|
+
const config = readAdapterConfig();
|
|
208
|
+
|
|
209
|
+
if (config.integrationAdapter === "growthub-bridge" &&
|
|
210
|
+
config.growthubBridge?.baseUrl &&
|
|
211
|
+
process.env.GROWTHUB_BRIDGE_ACCESS_TOKEN) {
|
|
212
|
+
try {
|
|
213
|
+
const baseUrl = config.growthubBridge.baseUrl;
|
|
214
|
+
const entitiesPath = `/api/integrations/${encodeURIComponent(integrationId)}/entities`;
|
|
215
|
+
const url = new URL(entitiesPath, baseUrl);
|
|
216
|
+
const headers = {
|
|
217
|
+
accept: "application/json",
|
|
218
|
+
authorization: `Bearer ${process.env.GROWTHUB_BRIDGE_ACCESS_TOKEN}`
|
|
219
|
+
};
|
|
220
|
+
if (config.growthubBridge.userId) {
|
|
221
|
+
headers["x-user-id"] = config.growthubBridge.userId;
|
|
222
|
+
}
|
|
223
|
+
const response = await fetch(url, {
|
|
224
|
+
headers,
|
|
225
|
+
next: { revalidate: 30 }
|
|
226
|
+
});
|
|
227
|
+
if (response.ok) {
|
|
228
|
+
const payload = await response.json();
|
|
229
|
+
const entities = Array.isArray(payload.entities) ? payload.entities :
|
|
230
|
+
Array.isArray(payload.objects) ? payload.objects :
|
|
231
|
+
Array.isArray(payload.data) ? payload.data :
|
|
232
|
+
Array.isArray(payload) ? payload : [];
|
|
233
|
+
const normalized = normalizeIntegrationEntities(entities);
|
|
234
|
+
if (normalized.length) return normalized;
|
|
235
|
+
}
|
|
236
|
+
} catch {
|
|
237
|
+
// No fallback object data. The UI must surface the missing resolver.
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return [];
|
|
242
|
+
}
|
|
243
|
+
|
|
195
244
|
export {
|
|
196
245
|
describeIntegrationAdapter,
|
|
197
|
-
listGovernedWorkspaceIntegrations
|
|
246
|
+
listGovernedWorkspaceIntegrations,
|
|
247
|
+
listEntityMetadataForIntegration
|
|
198
248
|
};
|
|
@@ -173,13 +173,43 @@ const workspaceIntegrations = [
|
|
|
173
173
|
}
|
|
174
174
|
];
|
|
175
175
|
const governedWorkspaceIntegrationCatalog = [...dataSources, ...workspaceIntegrations];
|
|
176
|
+
|
|
176
177
|
function groupIntegrationsByLane(integrations) {
|
|
177
178
|
return {
|
|
178
179
|
dataSources: integrations.filter((item) => item.lane === "data-source"),
|
|
179
180
|
workspaceIntegrations: integrations.filter((item) => item.lane === "workspace-integration")
|
|
180
181
|
};
|
|
181
182
|
}
|
|
183
|
+
|
|
184
|
+
function normalizeIntegrationEntity(entity) {
|
|
185
|
+
if (!entity || typeof entity !== "object" || Array.isArray(entity)) return null;
|
|
186
|
+
const id = typeof entity.id === "string" ? entity.id.trim() : "";
|
|
187
|
+
const label = typeof entity.label === "string" && entity.label.trim()
|
|
188
|
+
? entity.label.trim()
|
|
189
|
+
: id;
|
|
190
|
+
if (!id || !label) return null;
|
|
191
|
+
const normalized = {
|
|
192
|
+
id,
|
|
193
|
+
label,
|
|
194
|
+
secondaryLabel: typeof entity.secondaryLabel === "string" ? entity.secondaryLabel : id,
|
|
195
|
+
entityType: typeof entity.entityType === "string" ? entity.entityType : undefined,
|
|
196
|
+
provider: typeof entity.provider === "string" ? entity.provider : undefined,
|
|
197
|
+
lane: typeof entity.lane === "string" ? entity.lane : undefined,
|
|
198
|
+
status: typeof entity.status === "string" ? entity.status : undefined,
|
|
199
|
+
metadata: entity.metadata && typeof entity.metadata === "object" && !Array.isArray(entity.metadata)
|
|
200
|
+
? entity.metadata
|
|
201
|
+
: undefined
|
|
202
|
+
};
|
|
203
|
+
return Object.fromEntries(Object.entries(normalized).filter(([, value]) => value !== undefined));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function normalizeIntegrationEntities(entities) {
|
|
207
|
+
if (!Array.isArray(entities)) return [];
|
|
208
|
+
return entities.map(normalizeIntegrationEntity).filter(Boolean);
|
|
209
|
+
}
|
|
210
|
+
|
|
182
211
|
export {
|
|
183
212
|
governedWorkspaceIntegrationCatalog,
|
|
184
|
-
groupIntegrationsByLane
|
|
213
|
+
groupIntegrationsByLane,
|
|
214
|
+
normalizeIntegrationEntities
|
|
185
215
|
};
|
|
@@ -40,7 +40,14 @@ const GRID_COLUMNS = 12;
|
|
|
40
40
|
const GRID_ROWS = 16;
|
|
41
41
|
const KNOWN_WIDGET_KINDS = ["chart", "view", "iframe", "rich-text"];
|
|
42
42
|
const KNOWN_FIELDS = ["dashboards", "widgetTypes", "canvas"];
|
|
43
|
-
const KNOWN_DATA_BINDING_MODES = ["manual", "json", "csv"];
|
|
43
|
+
const KNOWN_DATA_BINDING_MODES = ["manual", "json", "csv", "integration"];
|
|
44
|
+
const KNOWN_CHART_TYPES = ["bar-vertical", "bar-horizontal", "line", "pie", "sum", "gauge"];
|
|
45
|
+
const KNOWN_FILTER_OPERATORS = ["eq", "ne", "contains", "gt", "lt", "isEmpty", "isNotEmpty"];
|
|
46
|
+
const KNOWN_FILTER_CONJUNCTIONS = ["and", "or"];
|
|
47
|
+
const KNOWN_SORT_DIRECTIONS = ["asc", "desc"];
|
|
48
|
+
const KNOWN_AGGREGATIONS = ["sum", "avg", "count", "min", "max"];
|
|
49
|
+
|
|
50
|
+
const NORMALIZED_OBJECT_FIELD_IDS = ["id", "label", "secondaryLabel", "entityType", "provider", "lane", "status"];
|
|
44
51
|
const WORKSPACE_TEMPLATE_KIND = "growthub-workspace-template";
|
|
45
52
|
const WORKSPACE_TEMPLATE_VERSION = 1;
|
|
46
53
|
const WORKSPACE_TEMPLATE_SOURCE = "growthub-custom-workspace-starter-v1";
|
|
@@ -61,7 +68,12 @@ const WIDGET_SCHEMA_CONTRACTS = {
|
|
|
61
68
|
config: "kind-specific config object"
|
|
62
69
|
},
|
|
63
70
|
ChartWidgetConfig: {
|
|
64
|
-
values: "number[]",
|
|
71
|
+
values: "number[] (legacy preserved)",
|
|
72
|
+
chartType: `${KNOWN_CHART_TYPES.join(" | ")} optional, defaults to bar-vertical`,
|
|
73
|
+
xAxis: "ChartAxisConfig optional",
|
|
74
|
+
yAxis: "ChartAxisConfig optional",
|
|
75
|
+
style: "ChartStyleConfig optional",
|
|
76
|
+
filter: "FilterConfig optional",
|
|
65
77
|
binding: "StaticDataBinding optional"
|
|
66
78
|
},
|
|
67
79
|
ViewWidgetConfig: {
|
|
@@ -69,8 +81,42 @@ const WIDGET_SCHEMA_CONTRACTS = {
|
|
|
69
81
|
layout: "Table",
|
|
70
82
|
columns: "string[]",
|
|
71
83
|
rows: "record[]",
|
|
84
|
+
fieldSettings: "FieldSettingsConfig optional (hidden[], order[])",
|
|
85
|
+
sort: "SortClause[] optional ({ fieldId, direction })",
|
|
86
|
+
filter: "FilterConfig optional ({ op, clauses[] })",
|
|
72
87
|
binding: "StaticDataBinding optional"
|
|
73
88
|
},
|
|
89
|
+
ChartAxisConfig: {
|
|
90
|
+
field: "string optional",
|
|
91
|
+
sort: "string optional (asc | desc | position)",
|
|
92
|
+
aggregation: `${KNOWN_AGGREGATIONS.join(" | ")} optional`,
|
|
93
|
+
groupBy: "string optional",
|
|
94
|
+
omitZero: "boolean optional",
|
|
95
|
+
min: "string | number optional",
|
|
96
|
+
max: "string | number optional"
|
|
97
|
+
},
|
|
98
|
+
ChartStyleConfig: {
|
|
99
|
+
colors: "string optional (auto | manual swatch label)",
|
|
100
|
+
axisName: "string optional",
|
|
101
|
+
dataLabels: "boolean optional"
|
|
102
|
+
},
|
|
103
|
+
FieldSettingsConfig: {
|
|
104
|
+
hidden: "string[] of column names hidden from preview",
|
|
105
|
+
order: "string[] of column names defining custom order"
|
|
106
|
+
},
|
|
107
|
+
SortClause: {
|
|
108
|
+
fieldId: "non-empty string (column name)",
|
|
109
|
+
direction: KNOWN_SORT_DIRECTIONS.join(" | ")
|
|
110
|
+
},
|
|
111
|
+
FilterConfig: {
|
|
112
|
+
op: KNOWN_FILTER_CONJUNCTIONS.join(" | "),
|
|
113
|
+
clauses: "FilterClause[]"
|
|
114
|
+
},
|
|
115
|
+
FilterClause: {
|
|
116
|
+
fieldId: "non-empty string (column name)",
|
|
117
|
+
operator: KNOWN_FILTER_OPERATORS.join(" | "),
|
|
118
|
+
value: "string | number | boolean optional"
|
|
119
|
+
},
|
|
74
120
|
IframeWidgetConfig: {
|
|
75
121
|
url: "string"
|
|
76
122
|
},
|
|
@@ -97,17 +143,35 @@ const WIDGET_SCHEMA_CONTRACTS = {
|
|
|
97
143
|
source: "string",
|
|
98
144
|
rows: "manual record[] optional",
|
|
99
145
|
json: "JSON string optional",
|
|
100
|
-
csv: "CSV string optional"
|
|
146
|
+
csv: "CSV string optional",
|
|
147
|
+
sourceType: "managed-integrations | custom-api-webhooks optional",
|
|
148
|
+
sourceAuthority: "string optional — adapter authority label, never a secret",
|
|
149
|
+
endpointRef: "string optional — stable custom API/webhook reference, never a token",
|
|
150
|
+
integrationId: "string optional (when mode === 'integration')",
|
|
151
|
+
lane: "string optional (when mode === 'integration')",
|
|
152
|
+
entityId: "string optional — stable source object ID (never a token or credential)",
|
|
153
|
+
entityType: "string optional — adapter-provided object type",
|
|
154
|
+
entityLabel: "string optional — display-only resolved label, not authoritative"
|
|
155
|
+
},
|
|
156
|
+
NormalizedIntegrationEntity: {
|
|
157
|
+
id: "non-empty string — stable source object ID",
|
|
158
|
+
label: "non-empty string — primary display name",
|
|
159
|
+
secondaryLabel: "string optional — muted subtitle (ID, domain, or type hint)",
|
|
160
|
+
entityType: "string optional — adapter-provided object type",
|
|
161
|
+
provider: "string optional — adapter/provider slug",
|
|
162
|
+
lane: "string optional — adapter-provided lane",
|
|
163
|
+
status: "string optional — adapter-provided status",
|
|
164
|
+
metadata: "record optional — additional adapter metadata"
|
|
101
165
|
}
|
|
102
166
|
};
|
|
103
167
|
|
|
104
168
|
const SAMPLE_VIEW_ROWS = [
|
|
105
|
-
{ Name: "
|
|
106
|
-
{ Name: "
|
|
107
|
-
{ Name: "
|
|
108
|
-
{ Name: "
|
|
109
|
-
{ Name: "
|
|
110
|
-
{ Name: "
|
|
169
|
+
{ Name: "Example Company A", "Domain Name": "example-a.test" },
|
|
170
|
+
{ Name: "Example Company B", "Domain Name": "example-b.test" },
|
|
171
|
+
{ Name: "Example Company C", "Domain Name": "example-c.test" },
|
|
172
|
+
{ Name: "Example Company D", "Domain Name": "example-d.test" },
|
|
173
|
+
{ Name: "Example Company E", "Domain Name": "example-e.test" },
|
|
174
|
+
{ Name: "Example Company F", "Domain Name": "example-f.test" }
|
|
111
175
|
];
|
|
112
176
|
|
|
113
177
|
const SAMPLE_DATA_BINDINGS = {
|
|
@@ -321,6 +385,14 @@ function validateStaticDataBinding(binding, path, errors) {
|
|
|
321
385
|
if (!KNOWN_DATA_BINDING_MODES.includes(binding.mode)) {
|
|
322
386
|
errors.push(`${path}.mode must be one of ${KNOWN_DATA_BINDING_MODES.join(", ")}`);
|
|
323
387
|
}
|
|
388
|
+
if (binding.mode === "integration") {
|
|
389
|
+
if (typeof binding.integrationId !== "string" || !binding.integrationId.trim()) {
|
|
390
|
+
errors.push(`${path}.integrationId is required when mode is integration`);
|
|
391
|
+
}
|
|
392
|
+
if (typeof binding.lane !== "string" || !binding.lane.trim()) {
|
|
393
|
+
errors.push(`${path}.lane is required when mode is integration`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
324
396
|
if (binding.source !== undefined && typeof binding.source !== "string") {
|
|
325
397
|
errors.push(`${path}.source must be a string`);
|
|
326
398
|
}
|
|
@@ -333,6 +405,145 @@ function validateStaticDataBinding(binding, path, errors) {
|
|
|
333
405
|
if (binding.csv !== undefined && typeof binding.csv !== "string") {
|
|
334
406
|
errors.push(`${path}.csv must be a string`);
|
|
335
407
|
}
|
|
408
|
+
if (binding.sourceType !== undefined && typeof binding.sourceType !== "string") {
|
|
409
|
+
errors.push(`${path}.sourceType must be a string`);
|
|
410
|
+
}
|
|
411
|
+
if (binding.sourceAuthority !== undefined && typeof binding.sourceAuthority !== "string") {
|
|
412
|
+
errors.push(`${path}.sourceAuthority must be a string`);
|
|
413
|
+
}
|
|
414
|
+
if (binding.endpointRef !== undefined && typeof binding.endpointRef !== "string") {
|
|
415
|
+
errors.push(`${path}.endpointRef must be a string`);
|
|
416
|
+
}
|
|
417
|
+
if (binding.integrationId !== undefined && typeof binding.integrationId !== "string") {
|
|
418
|
+
errors.push(`${path}.integrationId must be a string`);
|
|
419
|
+
}
|
|
420
|
+
if (binding.lane !== undefined && typeof binding.lane !== "string") {
|
|
421
|
+
errors.push(`${path}.lane must be a string`);
|
|
422
|
+
}
|
|
423
|
+
if (binding.entityId !== undefined && typeof binding.entityId !== "string") {
|
|
424
|
+
errors.push(`${path}.entityId must be a string`);
|
|
425
|
+
}
|
|
426
|
+
if (binding.entityType !== undefined && typeof binding.entityType !== "string") {
|
|
427
|
+
errors.push(`${path}.entityType must be a string`);
|
|
428
|
+
}
|
|
429
|
+
if (binding.entityLabel !== undefined && typeof binding.entityLabel !== "string") {
|
|
430
|
+
errors.push(`${path}.entityLabel must be a string`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function validateFieldSettings(fieldSettings, path, errors) {
|
|
435
|
+
if (fieldSettings === undefined) return;
|
|
436
|
+
if (!isPlainObject(fieldSettings)) {
|
|
437
|
+
errors.push(`${path} must be a plain object`);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
if (fieldSettings.hidden !== undefined) validateStringArray(fieldSettings.hidden, `${path}.hidden`, errors);
|
|
441
|
+
if (fieldSettings.order !== undefined) validateStringArray(fieldSettings.order, `${path}.order`, errors);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function validateSortClauses(sort, path, errors) {
|
|
445
|
+
if (sort === undefined) return;
|
|
446
|
+
if (!Array.isArray(sort)) {
|
|
447
|
+
errors.push(`${path} must be an array`);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
sort.forEach((clause, index) => {
|
|
451
|
+
const prefix = `${path}[${index}]`;
|
|
452
|
+
if (!isPlainObject(clause)) {
|
|
453
|
+
errors.push(`${prefix} must be a plain object`);
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
if (typeof clause.fieldId !== "string" || !clause.fieldId) {
|
|
457
|
+
errors.push(`${prefix}.fieldId must be a non-empty string`);
|
|
458
|
+
}
|
|
459
|
+
if (clause.direction !== undefined && !KNOWN_SORT_DIRECTIONS.includes(clause.direction)) {
|
|
460
|
+
errors.push(`${prefix}.direction must be one of ${KNOWN_SORT_DIRECTIONS.join(", ")}`);
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function validateFilterClauses(filter, path, errors) {
|
|
466
|
+
if (filter === undefined) return;
|
|
467
|
+
if (!isPlainObject(filter)) {
|
|
468
|
+
errors.push(`${path} must be a plain object`);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (filter.op !== undefined && !KNOWN_FILTER_CONJUNCTIONS.includes(filter.op)) {
|
|
472
|
+
errors.push(`${path}.op must be one of ${KNOWN_FILTER_CONJUNCTIONS.join(", ")}`);
|
|
473
|
+
}
|
|
474
|
+
if (filter.clauses !== undefined) {
|
|
475
|
+
if (!Array.isArray(filter.clauses)) {
|
|
476
|
+
errors.push(`${path}.clauses must be an array`);
|
|
477
|
+
} else {
|
|
478
|
+
filter.clauses.forEach((clause, index) => {
|
|
479
|
+
const prefix = `${path}.clauses[${index}]`;
|
|
480
|
+
if (!isPlainObject(clause)) {
|
|
481
|
+
errors.push(`${prefix} must be a plain object`);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
if (typeof clause.fieldId !== "string" || !clause.fieldId) {
|
|
485
|
+
errors.push(`${prefix}.fieldId must be a non-empty string`);
|
|
486
|
+
}
|
|
487
|
+
if (clause.operator !== undefined && !KNOWN_FILTER_OPERATORS.includes(clause.operator)) {
|
|
488
|
+
errors.push(`${prefix}.operator must be one of ${KNOWN_FILTER_OPERATORS.join(", ")}`);
|
|
489
|
+
}
|
|
490
|
+
if (
|
|
491
|
+
clause.value !== undefined &&
|
|
492
|
+
typeof clause.value !== "string" &&
|
|
493
|
+
typeof clause.value !== "number" &&
|
|
494
|
+
typeof clause.value !== "boolean"
|
|
495
|
+
) {
|
|
496
|
+
errors.push(`${prefix}.value must be a string, number, or boolean`);
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function validateChartAxis(axis, path, errors) {
|
|
504
|
+
if (axis === undefined) return;
|
|
505
|
+
if (!isPlainObject(axis)) {
|
|
506
|
+
errors.push(`${path} must be a plain object`);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
if (axis.field !== undefined && typeof axis.field !== "string") {
|
|
510
|
+
errors.push(`${path}.field must be a string`);
|
|
511
|
+
}
|
|
512
|
+
if (axis.sort !== undefined && typeof axis.sort !== "string") {
|
|
513
|
+
errors.push(`${path}.sort must be a string`);
|
|
514
|
+
}
|
|
515
|
+
if (axis.aggregation !== undefined && !KNOWN_AGGREGATIONS.includes(axis.aggregation)) {
|
|
516
|
+
errors.push(`${path}.aggregation must be one of ${KNOWN_AGGREGATIONS.join(", ")}`);
|
|
517
|
+
}
|
|
518
|
+
if (axis.groupBy !== undefined && typeof axis.groupBy !== "string") {
|
|
519
|
+
errors.push(`${path}.groupBy must be a string`);
|
|
520
|
+
}
|
|
521
|
+
if (axis.omitZero !== undefined && typeof axis.omitZero !== "boolean") {
|
|
522
|
+
errors.push(`${path}.omitZero must be a boolean`);
|
|
523
|
+
}
|
|
524
|
+
if (axis.min !== undefined && typeof axis.min !== "string" && typeof axis.min !== "number") {
|
|
525
|
+
errors.push(`${path}.min must be a string or number`);
|
|
526
|
+
}
|
|
527
|
+
if (axis.max !== undefined && typeof axis.max !== "string" && typeof axis.max !== "number") {
|
|
528
|
+
errors.push(`${path}.max must be a string or number`);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function validateChartStyle(style, path, errors) {
|
|
533
|
+
if (style === undefined) return;
|
|
534
|
+
if (!isPlainObject(style)) {
|
|
535
|
+
errors.push(`${path} must be a plain object`);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (style.colors !== undefined && typeof style.colors !== "string") {
|
|
539
|
+
errors.push(`${path}.colors must be a string`);
|
|
540
|
+
}
|
|
541
|
+
if (style.axisName !== undefined && typeof style.axisName !== "string") {
|
|
542
|
+
errors.push(`${path}.axisName must be a string`);
|
|
543
|
+
}
|
|
544
|
+
if (style.dataLabels !== undefined && typeof style.dataLabels !== "boolean") {
|
|
545
|
+
errors.push(`${path}.dataLabels must be a boolean`);
|
|
546
|
+
}
|
|
336
547
|
}
|
|
337
548
|
|
|
338
549
|
function validateWidgetConfig(kind, config, path, errors) {
|
|
@@ -353,6 +564,13 @@ function validateWidgetConfig(kind, config, path, errors) {
|
|
|
353
564
|
});
|
|
354
565
|
}
|
|
355
566
|
}
|
|
567
|
+
if (config.chartType !== undefined && !KNOWN_CHART_TYPES.includes(config.chartType)) {
|
|
568
|
+
errors.push(`${path}.chartType must be one of ${KNOWN_CHART_TYPES.join(", ")}`);
|
|
569
|
+
}
|
|
570
|
+
validateChartAxis(config.xAxis, `${path}.xAxis`, errors);
|
|
571
|
+
validateChartAxis(config.yAxis, `${path}.yAxis`, errors);
|
|
572
|
+
validateChartStyle(config.style, `${path}.style`, errors);
|
|
573
|
+
validateFilterClauses(config.filter, `${path}.filter`, errors);
|
|
356
574
|
validateStaticDataBinding(config.binding, `${path}.binding`, errors);
|
|
357
575
|
}
|
|
358
576
|
if (kind === "view") {
|
|
@@ -360,6 +578,9 @@ function validateWidgetConfig(kind, config, path, errors) {
|
|
|
360
578
|
if (config.layout !== undefined && config.layout !== "Table") errors.push(`${path}.layout must be Table`);
|
|
361
579
|
if (config.columns !== undefined) validateStringArray(config.columns, `${path}.columns`, errors);
|
|
362
580
|
if (config.rows !== undefined && !Array.isArray(config.rows)) errors.push(`${path}.rows must be an array`);
|
|
581
|
+
validateFieldSettings(config.fieldSettings, `${path}.fieldSettings`, errors);
|
|
582
|
+
validateSortClauses(config.sort, `${path}.sort`, errors);
|
|
583
|
+
validateFilterClauses(config.filter, `${path}.filter`, errors);
|
|
363
584
|
validateStaticDataBinding(config.binding, `${path}.binding`, errors);
|
|
364
585
|
}
|
|
365
586
|
if (kind === "iframe" && config.url !== undefined && typeof config.url !== "string") {
|
|
@@ -787,9 +1008,15 @@ export {
|
|
|
787
1008
|
DASHBOARD_TEMPLATES,
|
|
788
1009
|
GRID_COLUMNS,
|
|
789
1010
|
GRID_ROWS,
|
|
1011
|
+
KNOWN_AGGREGATIONS,
|
|
1012
|
+
KNOWN_CHART_TYPES,
|
|
790
1013
|
KNOWN_DATA_BINDING_MODES,
|
|
791
1014
|
KNOWN_FIELDS,
|
|
1015
|
+
KNOWN_FILTER_CONJUNCTIONS,
|
|
1016
|
+
KNOWN_FILTER_OPERATORS,
|
|
1017
|
+
KNOWN_SORT_DIRECTIONS,
|
|
792
1018
|
KNOWN_WIDGET_KINDS,
|
|
1019
|
+
NORMALIZED_OBJECT_FIELD_IDS,
|
|
793
1020
|
SAMPLE_DATA_BINDINGS,
|
|
794
1021
|
SAMPLE_VIEW_ROWS,
|
|
795
1022
|
WIDGET_SCHEMA_CONTRACTS,
|
package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package-lock.json
CHANGED
|
@@ -8,15 +8,10 @@
|
|
|
8
8
|
"name": "growthub-workspace-app",
|
|
9
9
|
"version": "1.0.0",
|
|
10
10
|
"dependencies": {
|
|
11
|
+
"lucide-react": "^0.468.0",
|
|
11
12
|
"next": "16.2.4",
|
|
12
13
|
"react": "19.2.4",
|
|
13
14
|
"react-dom": "19.2.4"
|
|
14
|
-
},
|
|
15
|
-
"devDependencies": {
|
|
16
|
-
"@types/node": "^20",
|
|
17
|
-
"@types/react": "^19",
|
|
18
|
-
"@types/react-dom": "^19",
|
|
19
|
-
"typescript": "^5"
|
|
20
15
|
}
|
|
21
16
|
},
|
|
22
17
|
"node_modules/@emnapi/runtime": {
|
|
@@ -638,36 +633,6 @@
|
|
|
638
633
|
"tslib": "^2.8.0"
|
|
639
634
|
}
|
|
640
635
|
},
|
|
641
|
-
"node_modules/@types/node": {
|
|
642
|
-
"version": "20.19.39",
|
|
643
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz",
|
|
644
|
-
"integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==",
|
|
645
|
-
"dev": true,
|
|
646
|
-
"license": "MIT",
|
|
647
|
-
"dependencies": {
|
|
648
|
-
"undici-types": "~6.21.0"
|
|
649
|
-
}
|
|
650
|
-
},
|
|
651
|
-
"node_modules/@types/react": {
|
|
652
|
-
"version": "19.2.14",
|
|
653
|
-
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
|
654
|
-
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
|
655
|
-
"dev": true,
|
|
656
|
-
"license": "MIT",
|
|
657
|
-
"dependencies": {
|
|
658
|
-
"csstype": "^3.2.2"
|
|
659
|
-
}
|
|
660
|
-
},
|
|
661
|
-
"node_modules/@types/react-dom": {
|
|
662
|
-
"version": "19.2.3",
|
|
663
|
-
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
|
|
664
|
-
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
|
665
|
-
"dev": true,
|
|
666
|
-
"license": "MIT",
|
|
667
|
-
"peerDependencies": {
|
|
668
|
-
"@types/react": "^19.2.0"
|
|
669
|
-
}
|
|
670
|
-
},
|
|
671
636
|
"node_modules/baseline-browser-mapping": {
|
|
672
637
|
"version": "2.10.21",
|
|
673
638
|
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.21.tgz",
|
|
@@ -706,13 +671,6 @@
|
|
|
706
671
|
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
|
707
672
|
"license": "MIT"
|
|
708
673
|
},
|
|
709
|
-
"node_modules/csstype": {
|
|
710
|
-
"version": "3.2.3",
|
|
711
|
-
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
|
712
|
-
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
|
713
|
-
"dev": true,
|
|
714
|
-
"license": "MIT"
|
|
715
|
-
},
|
|
716
674
|
"node_modules/detect-libc": {
|
|
717
675
|
"version": "2.1.2",
|
|
718
676
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
|
@@ -723,6 +681,15 @@
|
|
|
723
681
|
"node": ">=8"
|
|
724
682
|
}
|
|
725
683
|
},
|
|
684
|
+
"node_modules/lucide-react": {
|
|
685
|
+
"version": "0.468.0",
|
|
686
|
+
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz",
|
|
687
|
+
"integrity": "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==",
|
|
688
|
+
"license": "ISC",
|
|
689
|
+
"peerDependencies": {
|
|
690
|
+
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
|
|
691
|
+
}
|
|
692
|
+
},
|
|
726
693
|
"node_modules/nanoid": {
|
|
727
694
|
"version": "3.3.11",
|
|
728
695
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
|
@@ -950,27 +917,6 @@
|
|
|
950
917
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
951
918
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
|
952
919
|
"license": "0BSD"
|
|
953
|
-
},
|
|
954
|
-
"node_modules/typescript": {
|
|
955
|
-
"version": "5.9.3",
|
|
956
|
-
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
|
957
|
-
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
|
958
|
-
"dev": true,
|
|
959
|
-
"license": "Apache-2.0",
|
|
960
|
-
"bin": {
|
|
961
|
-
"tsc": "bin/tsc",
|
|
962
|
-
"tsserver": "bin/tsserver"
|
|
963
|
-
},
|
|
964
|
-
"engines": {
|
|
965
|
-
"node": ">=14.17"
|
|
966
|
-
}
|
|
967
|
-
},
|
|
968
|
-
"node_modules/undici-types": {
|
|
969
|
-
"version": "6.21.0",
|
|
970
|
-
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
|
971
|
-
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
|
972
|
-
"dev": true,
|
|
973
|
-
"license": "MIT"
|
|
974
920
|
}
|
|
975
921
|
}
|
|
976
922
|
}
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
},
|
|
102
102
|
"outputStandard": {
|
|
103
103
|
"type": "working-directory",
|
|
104
|
-
"description": "Fork the starter kit directly with `growthub starter init`
|
|
104
|
+
"description": "Fork the starter kit directly with `growthub starter init` \u2014 the CLI materializes this bundled asset tree to the user's chosen path, auto-registers the destination as a kit-fork (canonical state under `<forkPath>/.growthub-fork/fork.json`), optionally binds it to a GitHub remote, and hands off the forkId. Every downstream customization flows through the v1 Self-Healing Fork Sync Agent.",
|
|
105
105
|
"requiredPaths": [
|
|
106
106
|
"QUICKSTART.md",
|
|
107
107
|
"kit.json",
|
|
@@ -159,5 +159,8 @@
|
|
|
159
159
|
"provenance": {
|
|
160
160
|
"sourceRepo": "growthub-local",
|
|
161
161
|
"frozenAt": "2026-04-16T00:00:00.000Z"
|
|
162
|
-
}
|
|
162
|
+
},
|
|
163
|
+
"workspaceVisibility": "official",
|
|
164
|
+
"workspaceCompatible": true,
|
|
165
|
+
"workspaceCategory": "workspace-starter"
|
|
163
166
|
}
|