@growthub/cli 0.9.5 → 0.9.7
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/globals.css +284 -3
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +819 -61
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-config.js +26 -146
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +770 -0
- package/dist/index.js +1824 -1061
- package/package.json +3 -2
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { readAdapterConfig } from "@/lib/adapters/env";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
import {
|
|
5
|
+
GRID_COLUMNS,
|
|
6
|
+
GRID_ROWS,
|
|
7
|
+
KNOWN_WIDGET_KINDS,
|
|
8
|
+
validateWorkspaceConfig
|
|
9
|
+
} from "@/lib/workspace-schema";
|
|
9
10
|
|
|
10
11
|
function resolveWorkspaceConfigPath() {
|
|
11
12
|
return path.resolve(/*turbopackIgnore: true*/ process.cwd(), "growthub.config.json");
|
|
@@ -36,156 +37,35 @@ function describePersistenceMode() {
|
|
|
36
37
|
return { mode: "filesystem", reason: "Local development" };
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
function isFiniteInt(value) {
|
|
40
|
-
return typeof value === "number" && Number.isFinite(value) && Math.floor(value) === value;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function validateWidgetArray(widgets, contextPath, errors, seenIds) {
|
|
44
|
-
if (!Array.isArray(widgets)) {
|
|
45
|
-
errors.push(`${contextPath} must be an array`);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
const occupied = new Map();
|
|
49
|
-
widgets.forEach((widget, index) => {
|
|
50
|
-
const prefix = `${contextPath}[${index}]`;
|
|
51
|
-
if (!widget || typeof widget !== "object" || Array.isArray(widget)) {
|
|
52
|
-
errors.push(`${prefix} must be an object`);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
if (typeof widget.id !== "string" || !widget.id) {
|
|
56
|
-
errors.push(`${prefix}.id must be a non-empty string`);
|
|
57
|
-
} else if (seenIds.has(widget.id)) {
|
|
58
|
-
errors.push(`${prefix}.id duplicates an earlier widget id`);
|
|
59
|
-
} else {
|
|
60
|
-
seenIds.add(widget.id);
|
|
61
|
-
}
|
|
62
|
-
if (!KNOWN_WIDGET_KINDS.includes(widget.kind)) {
|
|
63
|
-
errors.push(`${prefix}.kind must be one of ${KNOWN_WIDGET_KINDS.join(", ")}`);
|
|
64
|
-
}
|
|
65
|
-
if (!widget.position || typeof widget.position !== "object" || Array.isArray(widget.position)) {
|
|
66
|
-
errors.push(`${prefix}.position must be an object`);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
for (const k of ["x", "y", "w", "h"]) {
|
|
70
|
-
if (!isFiniteInt(widget.position[k])) {
|
|
71
|
-
errors.push(`${prefix}.position.${k} must be a finite integer`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (
|
|
75
|
-
isFiniteInt(widget.position.x) &&
|
|
76
|
-
isFiniteInt(widget.position.w) &&
|
|
77
|
-
(widget.position.x < 0 || widget.position.w < 1 || widget.position.x + widget.position.w > GRID_COLUMNS)
|
|
78
|
-
) {
|
|
79
|
-
errors.push(`${prefix} x/w out of [0..${GRID_COLUMNS}] grid`);
|
|
80
|
-
}
|
|
81
|
-
if (
|
|
82
|
-
isFiniteInt(widget.position.y) &&
|
|
83
|
-
isFiniteInt(widget.position.h) &&
|
|
84
|
-
(widget.position.y < 0 || widget.position.h < 1 || widget.position.y + widget.position.h > GRID_ROWS)
|
|
85
|
-
) {
|
|
86
|
-
errors.push(`${prefix} y/h out of [0..${GRID_ROWS}] grid`);
|
|
87
|
-
}
|
|
88
|
-
if (
|
|
89
|
-
isFiniteInt(widget.position.x) &&
|
|
90
|
-
isFiniteInt(widget.position.y) &&
|
|
91
|
-
isFiniteInt(widget.position.w) &&
|
|
92
|
-
isFiniteInt(widget.position.h)
|
|
93
|
-
) {
|
|
94
|
-
for (let dx = 0; dx < widget.position.w; dx += 1) {
|
|
95
|
-
for (let dy = 0; dy < widget.position.h; dy += 1) {
|
|
96
|
-
const cell = `${widget.position.x + dx}:${widget.position.y + dy}`;
|
|
97
|
-
const previous = occupied.get(cell);
|
|
98
|
-
if (previous) {
|
|
99
|
-
errors.push(`${prefix} overlaps ${previous} at grid cell ${cell}`);
|
|
100
|
-
} else {
|
|
101
|
-
occupied.set(cell, `${prefix}.position`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function validateWorkspaceConfig(nextConfig) {
|
|
110
|
-
if (!nextConfig || typeof nextConfig !== "object" || Array.isArray(nextConfig)) {
|
|
111
|
-
const error = new Error("workspace config must be a plain object");
|
|
112
|
-
error.code = "INVALID_WORKSPACE_CONFIG";
|
|
113
|
-
error.details = ["root must be a plain object"];
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
|
-
const errors = [];
|
|
117
|
-
for (const key of Object.keys(nextConfig)) {
|
|
118
|
-
if (!KNOWN_FIELDS.includes(key)) {
|
|
119
|
-
errors.push(`unknown top-level field: ${key}`);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (nextConfig.dashboards !== undefined && !Array.isArray(nextConfig.dashboards)) {
|
|
123
|
-
errors.push("dashboards must be an array");
|
|
124
|
-
}
|
|
125
|
-
if (nextConfig.widgetTypes !== undefined && !Array.isArray(nextConfig.widgetTypes)) {
|
|
126
|
-
errors.push("widgetTypes must be an array");
|
|
127
|
-
}
|
|
128
|
-
if (nextConfig.canvas !== undefined) {
|
|
129
|
-
if (typeof nextConfig.canvas !== "object" || Array.isArray(nextConfig.canvas) || nextConfig.canvas === null) {
|
|
130
|
-
errors.push("canvas must be a plain object");
|
|
131
|
-
} else {
|
|
132
|
-
const seenWidgetIds = new Set();
|
|
133
|
-
if (nextConfig.canvas.widgets !== undefined) {
|
|
134
|
-
validateWidgetArray(nextConfig.canvas.widgets, "canvas.widgets", errors, seenWidgetIds);
|
|
135
|
-
}
|
|
136
|
-
if (nextConfig.canvas.tabs !== undefined) {
|
|
137
|
-
if (!Array.isArray(nextConfig.canvas.tabs)) {
|
|
138
|
-
errors.push("canvas.tabs must be an array");
|
|
139
|
-
} else {
|
|
140
|
-
const seenTabIds = new Set();
|
|
141
|
-
nextConfig.canvas.tabs.forEach((tab, index) => {
|
|
142
|
-
const tabPrefix = `canvas.tabs[${index}]`;
|
|
143
|
-
if (!tab || typeof tab !== "object" || Array.isArray(tab)) {
|
|
144
|
-
errors.push(`${tabPrefix} must be an object`);
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
if (typeof tab.id !== "string" || !tab.id) {
|
|
148
|
-
errors.push(`${tabPrefix}.id must be a non-empty string`);
|
|
149
|
-
} else if (seenTabIds.has(tab.id)) {
|
|
150
|
-
errors.push(`${tabPrefix}.id duplicates an earlier tab id`);
|
|
151
|
-
} else {
|
|
152
|
-
seenTabIds.add(tab.id);
|
|
153
|
-
}
|
|
154
|
-
if (typeof tab.name !== "string" || !tab.name) {
|
|
155
|
-
errors.push(`${tabPrefix}.name must be a non-empty string`);
|
|
156
|
-
}
|
|
157
|
-
if (tab.widgets !== undefined) {
|
|
158
|
-
validateWidgetArray(tab.widgets, `${tabPrefix}.widgets`, errors, seenWidgetIds);
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
if (nextConfig.canvas.activeTabId !== undefined && typeof nextConfig.canvas.activeTabId !== "string") {
|
|
164
|
-
errors.push("canvas.activeTabId must be a string");
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
if (errors.length) {
|
|
169
|
-
const error = new Error(`invalid workspace config: ${errors.join("; ")}`);
|
|
170
|
-
error.code = "INVALID_WORKSPACE_CONFIG";
|
|
171
|
-
error.details = errors;
|
|
172
|
-
throw error;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
40
|
function applyPatch(currentConfig, patch) {
|
|
177
41
|
const next = { ...currentConfig };
|
|
178
42
|
if (patch.dashboards !== undefined) next.dashboards = patch.dashboards;
|
|
179
43
|
if (patch.widgetTypes !== undefined) next.widgetTypes = patch.widgetTypes;
|
|
180
44
|
if (patch.canvas !== undefined && patch.canvas !== null) {
|
|
45
|
+
const patchCanvas = { ...patch.canvas };
|
|
46
|
+
if (Array.isArray(patchCanvas.tabs)) {
|
|
47
|
+
delete patchCanvas.widgets;
|
|
48
|
+
delete patchCanvas.name;
|
|
49
|
+
} else if (Array.isArray(patchCanvas.widgets)) {
|
|
50
|
+
delete patchCanvas.tabs;
|
|
51
|
+
delete patchCanvas.activeTabId;
|
|
52
|
+
}
|
|
181
53
|
next.canvas = {
|
|
182
54
|
...currentConfig.canvas,
|
|
183
|
-
...
|
|
184
|
-
layout: { ...(currentConfig.canvas?.layout || {}), ...(
|
|
185
|
-
bindings: { ...(currentConfig.canvas?.bindings || {}), ...(
|
|
55
|
+
...patchCanvas,
|
|
56
|
+
layout: { ...(currentConfig.canvas?.layout || {}), ...(patchCanvas.layout || {}) },
|
|
57
|
+
bindings: { ...(currentConfig.canvas?.bindings || {}), ...(patchCanvas.bindings || {}) }
|
|
186
58
|
};
|
|
59
|
+
if (Array.isArray(patch.canvas.tabs)) {
|
|
60
|
+
delete next.canvas.widgets;
|
|
61
|
+
delete next.canvas.name;
|
|
62
|
+
}
|
|
63
|
+
if (Array.isArray(patch.canvas.widgets)) {
|
|
64
|
+
delete next.canvas.tabs;
|
|
65
|
+
delete next.canvas.activeTabId;
|
|
66
|
+
}
|
|
187
67
|
for (const key of ["widgets", "tabs", "activeTabId", "name"]) {
|
|
188
|
-
if (Object.prototype.hasOwnProperty.call(
|
|
68
|
+
if (Object.prototype.hasOwnProperty.call(patchCanvas, key) && patchCanvas[key] === null) {
|
|
189
69
|
delete next.canvas[key];
|
|
190
70
|
}
|
|
191
71
|
}
|