@trops/dash-core 0.1.87 → 0.1.89

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.
@@ -589,11 +589,15 @@ var clientCacheEvents$1 = {
589
589
  const DASHBOARD_CONFIG_EXPORT$1 = "dashboard-config-export";
590
590
  const DASHBOARD_CONFIG_IMPORT$1 = "dashboard-config-import";
591
591
  const DASHBOARD_CONFIG_INSTALL$1 = "dashboard-config-install";
592
+ const DASHBOARD_CONFIG_COMPATIBILITY$1 = "dashboard-config-compatibility";
593
+ const DASHBOARD_CONFIG_PUBLISH$1 = "dashboard-config-publish";
592
594
 
593
595
  var dashboardConfigEvents$1 = {
594
596
  DASHBOARD_CONFIG_EXPORT: DASHBOARD_CONFIG_EXPORT$1,
595
597
  DASHBOARD_CONFIG_IMPORT: DASHBOARD_CONFIG_IMPORT$1,
596
598
  DASHBOARD_CONFIG_INSTALL: DASHBOARD_CONFIG_INSTALL$1,
599
+ DASHBOARD_CONFIG_COMPATIBILITY: DASHBOARD_CONFIG_COMPATIBILITY$1,
600
+ DASHBOARD_CONFIG_PUBLISH: DASHBOARD_CONFIG_PUBLISH$1,
597
601
  };
598
602
 
599
603
  /**
@@ -8356,12 +8360,151 @@ function applyEventWiringToLayout$1(layout, eventWiring) {
8356
8360
  return layout;
8357
8361
  }
8358
8362
 
8363
+ /**
8364
+ * Check compatibility of a dashboard config against installed widgets.
8365
+ * Returns a per-widget status report indicating what's installed,
8366
+ * what needs to be installed, and what's unavailable.
8367
+ *
8368
+ * @param {Array} dashboardWidgets - Widget deps from dashboard config (widgets array)
8369
+ * @param {Array} installedWidgets - Currently installed widget metadata (from widgetRegistry.getWidgets())
8370
+ * @param {Array} registryPackages - Available packages from registry index (optional)
8371
+ * @returns {Object} Compatibility report
8372
+ */
8373
+ function checkDashboardCompatibility(
8374
+ dashboardWidgets = [],
8375
+ installedWidgets = [],
8376
+ registryPackages = [],
8377
+ ) {
8378
+ const installedByName = new Map();
8379
+ for (const w of installedWidgets) {
8380
+ if (w.name) {
8381
+ installedByName.set(w.name, w);
8382
+ }
8383
+ }
8384
+
8385
+ const registryByName = new Map();
8386
+ for (const p of registryPackages) {
8387
+ if (p.name) {
8388
+ registryByName.set(p.name, p);
8389
+ }
8390
+ }
8391
+
8392
+ const widgets = [];
8393
+ let installedCount = 0;
8394
+ let toInstallCount = 0;
8395
+ let unavailableCount = 0;
8396
+
8397
+ for (const dep of dashboardWidgets) {
8398
+ const packageName = dep.package;
8399
+ const required = dep.required !== false;
8400
+ const installed = installedByName.get(packageName);
8401
+
8402
+ if (installed) {
8403
+ installedCount++;
8404
+ widgets.push({
8405
+ package: packageName,
8406
+ required,
8407
+ status: "installed",
8408
+ installedVersion: installed.version || null,
8409
+ requiredVersion: dep.version || "*",
8410
+ });
8411
+ } else if (registryByName.has(packageName)) {
8412
+ toInstallCount++;
8413
+ const registryPkg = registryByName.get(packageName);
8414
+ widgets.push({
8415
+ package: packageName,
8416
+ required,
8417
+ status: "to-install",
8418
+ availableVersion: registryPkg.version || null,
8419
+ requiredVersion: dep.version || "*",
8420
+ });
8421
+ } else {
8422
+ unavailableCount++;
8423
+ widgets.push({
8424
+ package: packageName,
8425
+ required,
8426
+ status: "unavailable",
8427
+ requiredVersion: dep.version || "*",
8428
+ });
8429
+ }
8430
+ }
8431
+
8432
+ const hasUnavailableRequired = widgets.some(
8433
+ (w) => w.status === "unavailable" && w.required,
8434
+ );
8435
+
8436
+ return {
8437
+ compatible: !hasUnavailableRequired,
8438
+ summary: {
8439
+ total: dashboardWidgets.length,
8440
+ installed: installedCount,
8441
+ toInstall: toInstallCount,
8442
+ unavailable: unavailableCount,
8443
+ },
8444
+ widgets,
8445
+ };
8446
+ }
8447
+
8448
+ /**
8449
+ * Generate a registry manifest from a dashboard config.
8450
+ * Converts the internal .dashboard.json format into the registry
8451
+ * manifest.json format used by dash-registry.
8452
+ *
8453
+ * @param {Object} dashboardConfig - Validated dashboard config object
8454
+ * @param {Object} options - Publishing options
8455
+ * @param {string} options.githubUser - GitHub username / org for the package scope
8456
+ * @param {string} options.category - Registry category (default: "general")
8457
+ * @param {string} options.repository - Repository URL (optional)
8458
+ * @returns {Object} Registry manifest object
8459
+ */
8460
+ function generateRegistryManifest(dashboardConfig, options = {}) {
8461
+ const name = (dashboardConfig.name || "dashboard")
8462
+ .replace(/[^a-zA-Z0-9-_ ]/g, "")
8463
+ .replace(/\s+/g, "-")
8464
+ .toLowerCase();
8465
+
8466
+ const githubUser = options.githubUser || "";
8467
+ const version = dashboardConfig.workspace?.version
8468
+ ? `1.0.${dashboardConfig.workspace.version}`
8469
+ : "1.0.0";
8470
+
8471
+ const manifest = {
8472
+ githubUser,
8473
+ name,
8474
+ displayName: dashboardConfig.name || "Dashboard",
8475
+ author: dashboardConfig.author?.name || "",
8476
+ description: dashboardConfig.description || "",
8477
+ version,
8478
+ type: "dashboard",
8479
+ category: options.category || "general",
8480
+ tags: dashboardConfig.tags || [],
8481
+ icon: dashboardConfig.icon || "grip",
8482
+ downloadUrl: `https://github.com/${githubUser}/dash-registry/releases/download/${githubUser}--${name}--v{version}/${name}-v{version}.zip`,
8483
+ repository: options.repository || "",
8484
+ publishedAt: new Date().toISOString(),
8485
+ widgets: (dashboardConfig.widgets || []).map((w) => ({
8486
+ id: w.id,
8487
+ name: w.id ? w.id.split(".").pop() : w.package,
8488
+ package: w.package,
8489
+ version: w.version || "*",
8490
+ required: w.required !== false,
8491
+ author: w.author || "",
8492
+ })),
8493
+ providers: dashboardConfig.providers || [],
8494
+ eventWiring: dashboardConfig.eventWiring || [],
8495
+ };
8496
+
8497
+ return manifest;
8498
+ }
8499
+
8359
8500
  var dashboardConfigUtils$1 = {
8360
8501
  collectComponentNames: collectComponentNames$1,
8361
8502
  extractEventWiring: extractEventWiring$1,
8362
8503
  buildWidgetDependencies: buildWidgetDependencies$1,
8363
8504
  buildProviderRequirements: buildProviderRequirements$1,
8364
8505
  applyEventWiringToLayout: applyEventWiringToLayout$1,
8506
+ checkDashboardCompatibility,
8507
+ generateRegistryManifest,
8365
8508
  };
8366
8509
 
8367
8510
  var widgetRegistry$1 = {exports: {}};
@@ -10271,10 +10414,223 @@ async function installDashboardFromRegistry$1(
10271
10414
  }
10272
10415
  }
10273
10416
 
10417
+ /**
10418
+ * Check compatibility of a dashboard's widget dependencies against
10419
+ * installed widgets and registry availability.
10420
+ *
10421
+ * @param {Array} dashboardWidgets - Widget deps from dashboard config
10422
+ * @param {Object} widgetRegistry - WidgetRegistry instance (needs getWidgets())
10423
+ * @returns {Promise<Object>} Compatibility report
10424
+ */
10425
+ async function checkCompatibility$1(dashboardWidgets, widgetRegistry = null) {
10426
+ const { checkDashboardCompatibility } = dashboardConfigUtils$1;
10427
+ const { fetchRegistryIndex } = registryController$1;
10428
+
10429
+ const installedWidgets = widgetRegistry ? widgetRegistry.getWidgets() : [];
10430
+
10431
+ let registryPackages = [];
10432
+ try {
10433
+ const index = await fetchRegistryIndex();
10434
+ registryPackages = index.packages || [];
10435
+ } catch (err) {
10436
+ console.warn(
10437
+ "[DashboardConfigController] Could not fetch registry index for compatibility check:",
10438
+ err.message,
10439
+ );
10440
+ }
10441
+
10442
+ return checkDashboardCompatibility(
10443
+ dashboardWidgets,
10444
+ installedWidgets,
10445
+ registryPackages,
10446
+ );
10447
+ }
10448
+
10449
+ /**
10450
+ * Prepare a dashboard for publishing to the registry.
10451
+ *
10452
+ * Validates that the workspace is shareable, builds the dashboard config,
10453
+ * checks that all widgets exist in the registry, generates a registry
10454
+ * manifest, and creates a ZIP containing both the manifest and
10455
+ * .dashboard.json config.
10456
+ *
10457
+ * @param {BrowserWindow} win - The main window (for save dialog)
10458
+ * @param {string} appId - Application identifier
10459
+ * @param {number|string} workspaceId - ID of the workspace to publish
10460
+ * @param {Object} options - Publishing options
10461
+ * @param {string} options.authorName - Author name
10462
+ * @param {string} options.authorId - Author ID
10463
+ * @param {string} options.description - Dashboard description
10464
+ * @param {string[]} options.tags - Tags
10465
+ * @param {string} options.icon - Icon name
10466
+ * @param {string} options.githubUser - GitHub user/org for registry scope
10467
+ * @param {string} options.category - Registry category
10468
+ * @param {Object} widgetRegistry - WidgetRegistry instance
10469
+ * @returns {Promise<Object>} Result with success, manifest, and filePath
10470
+ */
10471
+ async function prepareDashboardForPublish$1(
10472
+ win,
10473
+ appId,
10474
+ workspaceId,
10475
+ options = {},
10476
+ widgetRegistry = null,
10477
+ ) {
10478
+ try {
10479
+ const { generateRegistryManifest } = dashboardConfigUtils$1;
10480
+
10481
+ // 1. Read workspace
10482
+ const filename = path.join(
10483
+ app.getPath("userData"),
10484
+ appName,
10485
+ appId,
10486
+ configFilename,
10487
+ );
10488
+ const workspacesArray = getFileContents(filename);
10489
+ const workspace = workspacesArray.find(
10490
+ (w) => w.id === workspaceId || w.id === Number(workspaceId),
10491
+ );
10492
+
10493
+ if (!workspace) {
10494
+ return {
10495
+ success: false,
10496
+ error: `Workspace not found: ${workspaceId}`,
10497
+ };
10498
+ }
10499
+
10500
+ // 2. Check shareable flag — imported dashboards cannot be published
10501
+ if (workspace._dashboardConfig && workspace._dashboardConfig.shareable === false) {
10502
+ return {
10503
+ success: false,
10504
+ error: "This dashboard was imported and cannot be published. Only dashboards you created can be shared.",
10505
+ };
10506
+ }
10507
+
10508
+ const layout = workspace.layout || [];
10509
+
10510
+ // 3. Build the dashboard config (reuse export logic)
10511
+ const componentNames = collectComponentNames(layout);
10512
+ const eventWiring = extractEventWiring(layout);
10513
+ const widgets = buildWidgetDependencies(componentNames, widgetRegistry);
10514
+ const providers = buildProviderRequirements(componentNames, widgetRegistry);
10515
+
10516
+ const dashboardConfig = applyDefaults({
10517
+ schemaVersion: CURRENT_SCHEMA_VERSION,
10518
+ name: workspace.name || workspace.label || "Dashboard",
10519
+ description: options.description || "",
10520
+ author: {
10521
+ name: options.authorName || "",
10522
+ id: options.authorId || "",
10523
+ },
10524
+ shareable: true,
10525
+ tags: options.tags || [],
10526
+ icon: options.icon || "grip",
10527
+ workspace: {
10528
+ id: workspace.id,
10529
+ name: workspace.name,
10530
+ type: workspace.type || "workspace",
10531
+ label: workspace.label || workspace.name,
10532
+ version: workspace.version || 1,
10533
+ layout,
10534
+ menuId: workspace.menuId || 1,
10535
+ },
10536
+ widgets,
10537
+ providers,
10538
+ eventWiring,
10539
+ });
10540
+
10541
+ // 4. Validate the config
10542
+ const validation = validateDashboardConfig(dashboardConfig);
10543
+ if (!validation.valid) {
10544
+ return {
10545
+ success: false,
10546
+ error: `Generated config is invalid: ${validation.errors.join(", ")}`,
10547
+ };
10548
+ }
10549
+
10550
+ // 5. Verify all widgets exist in the registry
10551
+ const { fetchRegistryIndex } = registryController$1;
10552
+ let registryPackages = [];
10553
+ try {
10554
+ const index = await fetchRegistryIndex();
10555
+ registryPackages = index.packages || [];
10556
+ } catch (err) {
10557
+ return {
10558
+ success: false,
10559
+ error: `Cannot verify widgets in registry: ${err.message}`,
10560
+ };
10561
+ }
10562
+
10563
+ const registryNames = new Set(registryPackages.map((p) => p.name));
10564
+ const missingFromRegistry = widgets
10565
+ .filter((w) => w.required !== false && !registryNames.has(w.package))
10566
+ .map((w) => w.package);
10567
+
10568
+ if (missingFromRegistry.length > 0) {
10569
+ return {
10570
+ success: false,
10571
+ error: `Required widgets not found in registry: ${missingFromRegistry.join(", ")}. Publish them first.`,
10572
+ };
10573
+ }
10574
+
10575
+ // 6. Generate registry manifest
10576
+ const manifest = generateRegistryManifest(dashboardConfig, {
10577
+ githubUser: options.githubUser || options.authorId || "",
10578
+ category: options.category || "general",
10579
+ repository: options.repository || "",
10580
+ });
10581
+
10582
+ // 7. Show save dialog for the publish package
10583
+ const sanitizedName = manifest.name;
10584
+ const { canceled, filePath } = await dialog.showSaveDialog(win, {
10585
+ title: "Save Dashboard Package for Registry",
10586
+ defaultPath: path.join(
10587
+ app.getPath("desktop"),
10588
+ `${sanitizedName}-v${manifest.version}.zip`,
10589
+ ),
10590
+ filters: [{ name: "ZIP Archive", extensions: ["zip"] }],
10591
+ });
10592
+
10593
+ if (canceled || !filePath) {
10594
+ return { success: false, canceled: true };
10595
+ }
10596
+
10597
+ // 8. Create ZIP with manifest and dashboard config
10598
+ const zip = new AdmZip();
10599
+ zip.addFile("manifest.json", Buffer.from(JSON.stringify(manifest, null, 2), "utf-8"));
10600
+ zip.addFile(
10601
+ `${sanitizedName}.dashboard.json`,
10602
+ Buffer.from(JSON.stringify(dashboardConfig, null, 2), "utf-8"),
10603
+ );
10604
+ zip.writeZip(filePath);
10605
+
10606
+ console.log(
10607
+ `[DashboardConfigController] Prepared publish package: ${filePath}`,
10608
+ );
10609
+
10610
+ return {
10611
+ success: true,
10612
+ filePath,
10613
+ manifest,
10614
+ config: dashboardConfig,
10615
+ };
10616
+ } catch (error) {
10617
+ console.error(
10618
+ "[DashboardConfigController] Error preparing dashboard for publish:",
10619
+ error,
10620
+ );
10621
+ return {
10622
+ success: false,
10623
+ error: error.message,
10624
+ };
10625
+ }
10626
+ }
10627
+
10274
10628
  var dashboardConfigController$1 = {
10275
10629
  exportDashboardConfig: exportDashboardConfig$1,
10276
10630
  importDashboardConfig: importDashboardConfig$1,
10277
10631
  installDashboardFromRegistry: installDashboardFromRegistry$1,
10632
+ checkCompatibility: checkCompatibility$1,
10633
+ prepareDashboardForPublish: prepareDashboardForPublish$1,
10278
10634
  };
10279
10635
 
10280
10636
  /**
@@ -10367,6 +10723,8 @@ const {
10367
10723
  exportDashboardConfig,
10368
10724
  importDashboardConfig,
10369
10725
  installDashboardFromRegistry,
10726
+ checkCompatibility,
10727
+ prepareDashboardForPublish,
10370
10728
  } = dashboardConfigController$1;
10371
10729
 
10372
10730
  var controller = {
@@ -10413,6 +10771,8 @@ var controller = {
10413
10771
  exportDashboardConfig,
10414
10772
  importDashboardConfig,
10415
10773
  installDashboardFromRegistry,
10774
+ checkCompatibility,
10775
+ prepareDashboardForPublish,
10416
10776
  };
10417
10777
 
10418
10778
  const { ipcRenderer: ipcRenderer$i } = require$$0$1;
@@ -11792,6 +12152,8 @@ const {
11792
12152
  DASHBOARD_CONFIG_EXPORT,
11793
12153
  DASHBOARD_CONFIG_IMPORT,
11794
12154
  DASHBOARD_CONFIG_INSTALL,
12155
+ DASHBOARD_CONFIG_COMPATIBILITY,
12156
+ DASHBOARD_CONFIG_PUBLISH,
11795
12157
  } = events$8;
11796
12158
 
11797
12159
  const dashboardConfigApi$2 = {
@@ -11835,6 +12197,36 @@ const dashboardConfigApi$2 = {
11835
12197
  appId,
11836
12198
  packageName,
11837
12199
  }),
12200
+
12201
+ /**
12202
+ * Check compatibility of a dashboard config against installed widgets.
12203
+ *
12204
+ * @param {string} appId - Application identifier
12205
+ * @param {Array} dashboardWidgets - Widget dependencies from dashboard config
12206
+ * @returns {Promise<Object>} Compatibility report with per-widget status
12207
+ */
12208
+ checkDashboardCompatibility: (appId, dashboardWidgets) =>
12209
+ ipcRenderer$1.invoke(DASHBOARD_CONFIG_COMPATIBILITY, {
12210
+ appId,
12211
+ dashboardWidgets,
12212
+ }),
12213
+
12214
+ /**
12215
+ * Prepare a dashboard for publishing to the registry.
12216
+ * Validates shareable status, checks widgets exist in registry,
12217
+ * generates manifest, and saves a publish-ready ZIP.
12218
+ *
12219
+ * @param {string} appId - Application identifier
12220
+ * @param {number|string} workspaceId - Workspace to publish
12221
+ * @param {Object} options - Publishing options (authorName, authorId, description, tags, icon, githubUser, category)
12222
+ * @returns {Promise<Object>} Result with success, manifest, filePath
12223
+ */
12224
+ prepareDashboardForPublish: (appId, workspaceId, options = {}) =>
12225
+ ipcRenderer$1.invoke(DASHBOARD_CONFIG_PUBLISH, {
12226
+ appId,
12227
+ workspaceId,
12228
+ options,
12229
+ }),
11838
12230
  };
11839
12231
 
11840
12232
  var dashboardConfigApi_1 = dashboardConfigApi$2;