claw-dashboard 1.9.0 → 2.0.0
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/.c8rc.json +38 -0
- package/.dockerignore +68 -0
- package/.github/dependabot.yml +45 -0
- package/.github/pull_request_template.md +39 -0
- package/.github/workflows/ci.yml +147 -0
- package/.github/workflows/release.yml +40 -0
- package/.github/workflows/security.yml +84 -0
- package/.husky/pre-commit +1 -0
- package/.planning/codebase/ARCHITECTURE.md +206 -0
- package/.planning/codebase/INTEGRATIONS.md +150 -0
- package/.planning/codebase/STACK.md +122 -0
- package/.planning/codebase/STRUCTURE.md +201 -0
- package/CHANGELOG.md +293 -0
- package/CONTRIBUTING.md +378 -0
- package/Dockerfile +54 -0
- package/FEATURES.md +195 -21
- package/README.md +83 -6
- package/TODO.md +28 -0
- package/build-cjs.js +127 -0
- package/cjs-shim.js +13 -0
- package/dist/clawdash +1729 -0
- package/dist/clawdash.meta.json +2236 -0
- package/dist/widgets.cjs +6511 -0
- package/docker-compose.yml +67 -0
- package/docs/API.md +1050 -0
- package/docs/PLUGINS.md +1504 -0
- package/esbuild.config.js +158 -0
- package/eslint.config.js +56 -0
- package/examples/plugins/README.md +122 -0
- package/examples/plugins/api-status/index.js +294 -0
- package/examples/plugins/api-status/plugin.json +19 -0
- package/examples/plugins/hello-world/index.js +104 -0
- package/examples/plugins/hello-world/plugin.json +15 -0
- package/examples/plugins/system-metrics-chart/index.js +339 -0
- package/examples/plugins/system-metrics-chart/plugin.json +18 -0
- package/examples/plugins/weather-widget/index.js +136 -0
- package/examples/plugins/weather-widget/plugin.json +19 -0
- package/index.cjs +23005 -0
- package/index.js +5236 -566
- package/jest.config.js +11 -0
- package/man/clawdash.1 +276 -0
- package/package.json +54 -5
- package/schemas/plugin-manifest.json +128 -0
- package/scripts/release.js +595 -0
- package/src/alerts.js +693 -0
- package/src/auto-save.js +584 -0
- package/src/cache.js +390 -0
- package/src/checksum.js +146 -0
- package/src/cli/args.js +110 -0
- package/src/cli/export-schedule.js +423 -0
- package/src/cli/export-snapshot.js +138 -0
- package/src/cli/help.js +69 -0
- package/src/cli/import-snapshot.js +230 -0
- package/src/cli/index.js +14 -0
- package/src/cli/list-templates.js +69 -0
- package/src/cli/validate-config.js +76 -0
- package/src/cli/validate-plugin.js +187 -0
- package/src/cli/version.js +15 -0
- package/src/config-validator.js +586 -0
- package/src/config-watcher.js +401 -0
- package/src/config.js +684 -0
- package/src/container-detector.js +499 -0
- package/src/database.js +734 -0
- package/src/differential-render.js +327 -0
- package/src/errors.js +169 -0
- package/src/export-scheduler.js +581 -0
- package/src/gateway-manager.js +685 -0
- package/src/hints.js +371 -0
- package/src/loading-states.js +509 -0
- package/src/logger.js +185 -0
- package/src/memory-pressure.js +467 -0
- package/src/performance-monitor.js +374 -0
- package/src/plugin-errors.js +586 -0
- package/src/plugin-manifest-validator.js +219 -0
- package/src/plugin-reload.js +481 -0
- package/src/plugin-scaffold.js +1941 -0
- package/src/plugin-validator.js +284 -0
- package/src/retry.js +218 -0
- package/src/security.js +860 -0
- package/src/snapshot.js +372 -0
- package/src/splash.js +115 -0
- package/src/theme-selector.js +411 -0
- package/src/themes.js +874 -0
- package/src/transitions.js +534 -0
- package/src/types.d.ts +174 -0
- package/src/validation.js +926 -0
- package/src/web-server.js +885 -0
- package/src/widgets/builtin-widgets.js +1057 -0
- package/src/widgets/config-processor.js +401 -0
- package/src/widgets/dependency-resolver.js +596 -0
- package/src/widgets/index.js +79 -0
- package/src/widgets/plugin-api.js +845 -0
- package/src/widgets/widget-error-boundary.js +773 -0
- package/src/widgets/widget-error-isolation.js +551 -0
- package/src/widgets/widget-loader.js +1437 -0
- package/src/workers/system-worker.js +130 -0
- package/src/workers/worker-pool.js +633 -0
- package/tests/alerts.test.js +437 -0
- package/tests/auto-save.test.js +529 -0
- package/tests/cache.test.js +317 -0
- package/tests/cli.test.js +351 -0
- package/tests/command-palette.test.js +320 -0
- package/tests/config-processor.test.js +452 -0
- package/tests/config-validator.test.js +710 -0
- package/tests/config-watcher.test.js +594 -0
- package/tests/config.test.js +755 -0
- package/tests/database.test.js +438 -0
- package/tests/errors.test.js +189 -0
- package/tests/example-plugins.test.js +624 -0
- package/tests/integration.test.js +1321 -0
- package/tests/loading-states.test.js +300 -0
- package/tests/manifest-validation-on-load.test.js +671 -0
- package/tests/performance-monitor.test.js +190 -0
- package/tests/plugin-api-rate-limit.test.js +302 -0
- package/tests/plugin-errors.test.js +311 -0
- package/tests/plugin-lifecycle-e2e.test.js +1036 -0
- package/tests/plugin-reload.test.js +764 -0
- package/tests/rate-limiter.test.js +539 -0
- package/tests/retry.test.js +308 -0
- package/tests/security.test.js +411 -0
- package/tests/settings-modal.test.js +226 -0
- package/tests/theme-selector.test.js +57 -0
- package/tests/utils.js +242 -0
- package/tests/utils.test.js +317 -0
- package/tests/validate-plugin-cli.test.js +359 -0
- package/tests/validation.test.js +837 -0
- package/tests/web-server.test.js +646 -0
- package/tests/widget-arrange-mode.test.js +183 -0
- package/tests/widget-config-hot-reload.test.js +465 -0
- package/tests/widget-dependency.test.js +591 -0
- package/tests/widget-error-boundary.test.js +749 -0
- package/tests/widget-error-isolation.test.js +469 -0
- package/tests/widget-integration.test.js +1147 -0
- package/tests/widget-refresh-intervals.test.js +284 -0
- package/tests/worker-pool.test.js +522 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget Configuration Processor
|
|
3
|
+
* Handles environment variable interpolation, config versioning, and migration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import logger from '../logger.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Default configuration processing options
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_PROCESSING_OPTIONS = {
|
|
12
|
+
interpolateEnv: true,
|
|
13
|
+
supportLegacy: true,
|
|
14
|
+
validateVersion: true,
|
|
15
|
+
throwOnError: false,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Interpolate environment variables in config values
|
|
20
|
+
* Supports ${ENV_VAR} and ${ENV_VAR:-default} syntax
|
|
21
|
+
*
|
|
22
|
+
* @param {string} value - String value to interpolate
|
|
23
|
+
* @param {Object} env - Environment variables object (defaults to process.env)
|
|
24
|
+
* @returns {string} Interpolated value
|
|
25
|
+
*/
|
|
26
|
+
export function interpolateEnvVars(value, env = process.env) {
|
|
27
|
+
if (typeof value !== 'string') {
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Match ${VAR} or ${VAR:-default} patterns
|
|
32
|
+
const pattern = /\$\{([^}]+)\}/g;
|
|
33
|
+
|
|
34
|
+
return value.replace(pattern, (match, content) => {
|
|
35
|
+
// Check for default value syntax: VAR:-default
|
|
36
|
+
const colonIndex = content.indexOf(':-');
|
|
37
|
+
|
|
38
|
+
if (colonIndex !== -1) {
|
|
39
|
+
const varName = content.substring(0, colonIndex);
|
|
40
|
+
const defaultValue = content.substring(colonIndex + 2);
|
|
41
|
+
return env[varName] !== undefined ? env[varName] : defaultValue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Simple variable substitution
|
|
45
|
+
return env[content] !== undefined ? env[content] : match;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Deep process config object, interpolating env vars in all string values
|
|
51
|
+
*
|
|
52
|
+
* @param {*} config - Config value (any type)
|
|
53
|
+
* @param {Object} env - Environment variables
|
|
54
|
+
* @param {Set} visited - Track visited objects to prevent circular reference issues
|
|
55
|
+
* @returns {*} Processed config
|
|
56
|
+
*/
|
|
57
|
+
export function processConfigValues(config, env = process.env, visited = new Set()) {
|
|
58
|
+
// Handle null/undefined
|
|
59
|
+
if (config === null || config === undefined) {
|
|
60
|
+
return config;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Handle strings - interpolate env vars
|
|
64
|
+
if (typeof config === 'string') {
|
|
65
|
+
return interpolateEnvVars(config, env);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Handle arrays
|
|
69
|
+
if (Array.isArray(config)) {
|
|
70
|
+
return config.map(item => processConfigValues(item, env, visited));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Handle objects (but not dates, regexps, etc)
|
|
74
|
+
if (typeof config === 'object' && config.constructor === Object) {
|
|
75
|
+
// Prevent circular references
|
|
76
|
+
if (visited.has(config)) {
|
|
77
|
+
logger.warn('Circular reference detected in config, skipping');
|
|
78
|
+
return config;
|
|
79
|
+
}
|
|
80
|
+
visited.add(config);
|
|
81
|
+
|
|
82
|
+
const result = {};
|
|
83
|
+
for (const [key, value] of Object.entries(config)) {
|
|
84
|
+
result[key] = processConfigValues(value, env, visited);
|
|
85
|
+
}
|
|
86
|
+
visited.delete(config);
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Return primitives as-is
|
|
91
|
+
return config;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Config version information
|
|
96
|
+
*/
|
|
97
|
+
export const CONFIG_VERSION = {
|
|
98
|
+
CURRENT: '1.0.0',
|
|
99
|
+
MIN_SUPPORTED: '1.0.0',
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Parse version string to comparable array
|
|
104
|
+
* @param {string} version - Semver version string
|
|
105
|
+
* @returns {number[]} [major, minor, patch]
|
|
106
|
+
*/
|
|
107
|
+
function parseVersion(version) {
|
|
108
|
+
const parts = version.split('.').map(Number);
|
|
109
|
+
return [parts[0] || 0, parts[1] || 0, parts[2] || 0];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Compare two versions
|
|
114
|
+
* @param {string} v1 - First version
|
|
115
|
+
* @param {string} v2 - Second version
|
|
116
|
+
* @returns {number} -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
117
|
+
*/
|
|
118
|
+
export function compareVersions(v1, v2) {
|
|
119
|
+
const a = parseVersion(v1);
|
|
120
|
+
const b = parseVersion(v2);
|
|
121
|
+
|
|
122
|
+
for (let i = 0; i < 3; i++) {
|
|
123
|
+
if (a[i] < b[i]) return -1;
|
|
124
|
+
if (a[i] > b[i]) return 1;
|
|
125
|
+
}
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Config migration registry
|
|
131
|
+
* Maps version ranges to migration functions
|
|
132
|
+
*/
|
|
133
|
+
const migrations = new Map();
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Register a config migration
|
|
137
|
+
* @param {string} fromVersion - Source version (exact match or 'x.x.x' for any)
|
|
138
|
+
* @param {string} toVersion - Target version
|
|
139
|
+
* @param {Function} migrateFn - Migration function: (config) => migratedConfig
|
|
140
|
+
*/
|
|
141
|
+
export function registerMigration(fromVersion, toVersion, migrateFn) {
|
|
142
|
+
const key = `${fromVersion}→${toVersion}`;
|
|
143
|
+
migrations.set(key, {
|
|
144
|
+
fromVersion,
|
|
145
|
+
toVersion,
|
|
146
|
+
migrate: migrateFn,
|
|
147
|
+
});
|
|
148
|
+
logger.debug(`Registered config migration: ${key}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Find migration path from source to target version
|
|
153
|
+
* Uses simple greedy algorithm - for complex cases, explicit migration chains should be registered
|
|
154
|
+
*
|
|
155
|
+
* @param {string} fromVersion - Starting version
|
|
156
|
+
* @param {string} toVersion - Target version
|
|
157
|
+
* @returns {Array} Array of migration steps
|
|
158
|
+
*/
|
|
159
|
+
function findMigrationPath(fromVersion, toVersion) {
|
|
160
|
+
// Same version - no migration needed
|
|
161
|
+
if (fromVersion === toVersion) {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Look for direct migration
|
|
166
|
+
const directKey = `${fromVersion}→${toVersion}`;
|
|
167
|
+
if (migrations.has(directKey)) {
|
|
168
|
+
return [migrations.get(directKey)];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Look for wildcard migration from this version
|
|
172
|
+
for (const [key, migration] of migrations) {
|
|
173
|
+
if (migration.fromVersion === fromVersion) {
|
|
174
|
+
// Found a step, recursively find rest of path
|
|
175
|
+
const remainingPath = findMigrationPath(migration.toVersion, toVersion);
|
|
176
|
+
if (remainingPath !== null) {
|
|
177
|
+
return [migration, ...remainingPath];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// No migration path found
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Validate config version against supported range
|
|
188
|
+
* @param {Object} config - Widget configuration
|
|
189
|
+
* @returns {Object} Validation result { valid: boolean, error?: string, migrated?: boolean }
|
|
190
|
+
*/
|
|
191
|
+
export function validateConfigVersion(config) {
|
|
192
|
+
const configVersion = config?.__version || '1.0.0';
|
|
193
|
+
|
|
194
|
+
// Check minimum supported version
|
|
195
|
+
if (compareVersions(configVersion, CONFIG_VERSION.MIN_SUPPORTED) < 0) {
|
|
196
|
+
return {
|
|
197
|
+
valid: false,
|
|
198
|
+
error: `Config version ${configVersion} is below minimum supported ${CONFIG_VERSION.MIN_SUPPORTED}`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Check maximum (future) version
|
|
203
|
+
if (compareVersions(configVersion, CONFIG_VERSION.CURRENT) > 0) {
|
|
204
|
+
return {
|
|
205
|
+
valid: false,
|
|
206
|
+
error: `Config version ${configVersion} is newer than current ${CONFIG_VERSION.CURRENT}. Please upgrade the dashboard.`,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return { valid: true, version: configVersion };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Migrate config to current version
|
|
215
|
+
* @param {Object} config - Widget configuration
|
|
216
|
+
* @param {string} targetVersion - Target version (defaults to CURRENT)
|
|
217
|
+
* @returns {Object} Migration result { success: boolean, config?: Object, error?: string, path?: string[] }
|
|
218
|
+
*/
|
|
219
|
+
export function migrateConfig(config, targetVersion = CONFIG_VERSION.CURRENT) {
|
|
220
|
+
if (!config || typeof config !== 'object') {
|
|
221
|
+
return { success: false, error: 'Invalid config object' };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const sourceVersion = config.__version || '1.0.0';
|
|
225
|
+
|
|
226
|
+
// No migration needed
|
|
227
|
+
if (sourceVersion === targetVersion) {
|
|
228
|
+
return { success: true, config, path: [] };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Find migration path
|
|
232
|
+
const migrationPath = findMigrationPath(sourceVersion, targetVersion);
|
|
233
|
+
|
|
234
|
+
if (migrationPath === null) {
|
|
235
|
+
return {
|
|
236
|
+
success: false,
|
|
237
|
+
error: `No migration path from ${sourceVersion} to ${targetVersion}`,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Apply migrations
|
|
242
|
+
let migratedConfig = { ...config };
|
|
243
|
+
const path = [];
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
for (const migration of migrationPath) {
|
|
247
|
+
migratedConfig = migration.migrate(migratedConfig);
|
|
248
|
+
migratedConfig.__version = migration.toVersion;
|
|
249
|
+
path.push(`${migration.fromVersion}→${migration.toVersion}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
success: true,
|
|
254
|
+
config: migratedConfig,
|
|
255
|
+
path,
|
|
256
|
+
};
|
|
257
|
+
} catch (err) {
|
|
258
|
+
return {
|
|
259
|
+
success: false,
|
|
260
|
+
error: `Migration failed: ${err.message}`,
|
|
261
|
+
path,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Process widget configuration
|
|
268
|
+
* Applies env interpolation, version validation, and migration
|
|
269
|
+
*
|
|
270
|
+
* @param {Object} config - Raw widget configuration
|
|
271
|
+
* @param {Object} options - Processing options
|
|
272
|
+
* @returns {Object} Processing result { success: boolean, config?: Object, error?: string, warnings?: string[] }
|
|
273
|
+
*/
|
|
274
|
+
export function processWidgetConfig(config, options = {}) {
|
|
275
|
+
const opts = { ...DEFAULT_PROCESSING_OPTIONS, ...options };
|
|
276
|
+
const warnings = [];
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
let processedConfig = config;
|
|
280
|
+
|
|
281
|
+
// Validate and migrate version
|
|
282
|
+
if (opts.validateVersion) {
|
|
283
|
+
const validation = validateConfigVersion(processedConfig);
|
|
284
|
+
if (!validation.valid) {
|
|
285
|
+
// Try to migrate if version is old
|
|
286
|
+
if (validation.error?.includes('below minimum')) {
|
|
287
|
+
if (opts.throwOnError) {
|
|
288
|
+
throw new Error(validation.error);
|
|
289
|
+
}
|
|
290
|
+
return { success: false, error: validation.error };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Version is newer than current - can't migrate forward
|
|
294
|
+
if (opts.throwOnError) {
|
|
295
|
+
throw new Error(validation.error);
|
|
296
|
+
}
|
|
297
|
+
return { success: false, error: validation.error };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Migrate if needed
|
|
301
|
+
if (validation.version !== CONFIG_VERSION.CURRENT) {
|
|
302
|
+
const migration = migrateConfig(processedConfig);
|
|
303
|
+
if (!migration.success) {
|
|
304
|
+
if (opts.throwOnError) {
|
|
305
|
+
throw new Error(migration.error);
|
|
306
|
+
}
|
|
307
|
+
warnings.push(`Config migration failed: ${migration.error}`);
|
|
308
|
+
} else {
|
|
309
|
+
processedConfig = migration.config;
|
|
310
|
+
if (migration.path?.length > 0) {
|
|
311
|
+
warnings.push(`Migrated config: ${migration.path.join(', ')}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Interpolate environment variables
|
|
318
|
+
if (opts.interpolateEnv) {
|
|
319
|
+
processedConfig = processConfigValues(processedConfig);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
success: true,
|
|
324
|
+
config: processedConfig,
|
|
325
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
326
|
+
};
|
|
327
|
+
} catch (err) {
|
|
328
|
+
const error = `Config processing failed: ${err.message}`;
|
|
329
|
+
if (opts.throwOnError) {
|
|
330
|
+
throw err;
|
|
331
|
+
}
|
|
332
|
+
return { success: false, error };
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Extract environment variable requirements from config
|
|
338
|
+
* Useful for documentation and validation
|
|
339
|
+
*
|
|
340
|
+
* @param {*} config - Config value
|
|
341
|
+
* @param {Set} found - Set to collect found variables
|
|
342
|
+
* @returns {Array} Array of { name, hasDefault, defaultValue } objects
|
|
343
|
+
*/
|
|
344
|
+
export function extractEnvRequirements(config, found = new Set()) {
|
|
345
|
+
const requirements = [];
|
|
346
|
+
|
|
347
|
+
function extract(value) {
|
|
348
|
+
if (typeof value === 'string') {
|
|
349
|
+
const pattern = /\$\{([^}]+)\}/g;
|
|
350
|
+
let match;
|
|
351
|
+
while ((match = pattern.exec(value)) !== null) {
|
|
352
|
+
const content = match[1];
|
|
353
|
+
const colonIndex = content.indexOf(':-');
|
|
354
|
+
|
|
355
|
+
if (colonIndex !== -1) {
|
|
356
|
+
const varName = content.substring(0, colonIndex);
|
|
357
|
+
const defaultValue = content.substring(colonIndex + 2);
|
|
358
|
+
if (!found.has(varName)) {
|
|
359
|
+
found.add(varName);
|
|
360
|
+
requirements.push({ name: varName, hasDefault: true, defaultValue });
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
if (!found.has(content)) {
|
|
364
|
+
found.add(content);
|
|
365
|
+
requirements.push({ name: content, hasDefault: false });
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
} else if (Array.isArray(value)) {
|
|
370
|
+
value.forEach(extract);
|
|
371
|
+
} else if (value && typeof value === 'object' && value.constructor === Object) {
|
|
372
|
+
Object.values(value).forEach(extract);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
extract(config);
|
|
377
|
+
return requirements;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Create a config preprocessor for widget loader integration
|
|
382
|
+
* @param {Object} options - Processing options
|
|
383
|
+
* @returns {Function} Preprocessor function
|
|
384
|
+
*/
|
|
385
|
+
export function createConfigPreprocessor(options = {}) {
|
|
386
|
+
return (config) => processWidgetConfig(config, options);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export default {
|
|
390
|
+
interpolateEnvVars,
|
|
391
|
+
processConfigValues,
|
|
392
|
+
processWidgetConfig,
|
|
393
|
+
validateConfigVersion,
|
|
394
|
+
migrateConfig,
|
|
395
|
+
registerMigration,
|
|
396
|
+
compareVersions,
|
|
397
|
+
extractEnvRequirements,
|
|
398
|
+
createConfigPreprocessor,
|
|
399
|
+
CONFIG_VERSION,
|
|
400
|
+
DEFAULT_PROCESSING_OPTIONS,
|
|
401
|
+
};
|