@trops/dash-core 0.1.431 → 0.1.433

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/dist/index.js CHANGED
@@ -18054,7 +18054,6 @@ var EnhancedWidgetDropdown = function EnhancedWidgetDropdown(_ref) {
18054
18054
  padding: true,
18055
18055
  height: "h-full",
18056
18056
  children: filteredWidgets.map(function (widget) {
18057
- var _widget$providers;
18058
18057
  return /*#__PURE__*/jsxRuntime.jsx(DashReact.MenuItem3, {
18059
18058
  onClick: function onClick() {
18060
18059
  return handleWidgetSelect(widget);
@@ -18074,15 +18073,22 @@ var EnhancedWidgetDropdown = function EnhancedWidgetDropdown(_ref) {
18074
18073
  }), widget.description && /*#__PURE__*/jsxRuntime.jsx("div", {
18075
18074
  className: "text-xs opacity-50 truncate",
18076
18075
  children: widget.description
18077
- }), ((_widget$providers = widget.providers) === null || _widget$providers === void 0 ? void 0 : _widget$providers.length) > 0 && /*#__PURE__*/jsxRuntime.jsx("div", {
18078
- className: "flex flex-wrap gap-1 mt-0.5",
18079
- children: widget.providers.map(function (p) {
18080
- return /*#__PURE__*/jsxRuntime.jsx("span", {
18081
- className: "text-[10px] px-1.5 py-0.5 rounded bg-blue-500/20 text-blue-300",
18082
- children: p.type
18083
- }, p.type);
18084
- })
18085
- })]
18076
+ }), function () {
18077
+ // Same defensive null-safe iteration as WidgetSidebar:
18078
+ // a sparse `providers` array (null entries from a
18079
+ // malformed `.dash.js`) used to crash the chip render.
18080
+ var provs = getUserConfigurableProviders(widget.providers);
18081
+ if (provs.length === 0) return null;
18082
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
18083
+ className: "flex flex-wrap gap-1 mt-0.5",
18084
+ children: provs.map(function (p) {
18085
+ return /*#__PURE__*/jsxRuntime.jsx("span", {
18086
+ className: "text-[10px] px-1.5 py-0.5 rounded bg-blue-500/20 text-blue-300",
18087
+ children: p.type
18088
+ }, p.type);
18089
+ })
18090
+ });
18091
+ }()]
18086
18092
  }), /*#__PURE__*/jsxRuntime.jsxs("div", {
18087
18093
  className: "flex items-center gap-1.5 flex-shrink-0",
18088
18094
  children: [installedPackageNames.has(widget.packageName) && /*#__PURE__*/jsxRuntime.jsx("span", {
@@ -26686,7 +26692,11 @@ function renderComponent(component, id) {
26686
26692
  // Check if widget exists before attempting render —
26687
26693
  // WidgetFactory.render() always returns a React element (truthy),
26688
26694
  // so the old ternary fallback could never fire.
26689
- if (!isWidgetResolvable(component)) {
26695
+ // Pass `params` (the layout item) so `packageId` /
26696
+ // `_sourcePackage` can disambiguate when a legacy layout
26697
+ // references a bare component name and multiple installed
26698
+ // packages register a widget with the same trailing name.
26699
+ if (!isWidgetResolvable(component, params)) {
26690
26700
  return /*#__PURE__*/jsxRuntime.jsx(WidgetNotFound, {
26691
26701
  component: component
26692
26702
  });
@@ -27491,20 +27501,25 @@ function addChildToLayoutItem(childComponent, layoutItem, workspace) {
27491
27501
  }
27492
27502
 
27493
27503
  /**
27494
- * Check if a widget component key is resolvable in ComponentManager
27504
+ * Check if a widget component key is resolvable in ComponentManager.
27505
+ * Routes through `ComponentManager.resolve` so a legacy bare component
27506
+ * key (e.g. `"ProspectWorkspace"`) finds its registered scoped form
27507
+ * (`"ai-built.pipeline.ProspectWorkspace"`). Without this routing the
27508
+ * function would return false for every legacy-layout widget after the
27509
+ * scoped-IDs migration, and the renderer would show `WidgetNotFound`
27510
+ * for working dashboards.
27511
+ *
27495
27512
  * @param {string} componentKey the component key to check
27513
+ * @param {object} [data] the layout item — used for `packageId`
27514
+ * disambiguation when multiple registered
27515
+ * widgets share the same bare name
27496
27516
  * @returns {boolean} true if the widget can be rendered
27497
27517
  */
27498
- function isWidgetResolvable(componentKey) {
27518
+ function isWidgetResolvable(componentKey, data) {
27499
27519
  // Layout containers are always resolvable (handled specially by WidgetFactory)
27500
27520
  if (ComponentManager.isLayoutContainer(componentKey)) return true;
27501
- var m = ComponentManager.componentMap();
27502
- if (!m) return false;
27503
- // Exact key match (scoped id)
27504
- if (m[componentKey] && typeof m[componentKey].component === "function") {
27505
- return true;
27506
- }
27507
- return false;
27521
+ var config = ComponentManager.resolve(componentKey, data);
27522
+ return !!(config && typeof config.component === "function");
27508
27523
  }
27509
27524
 
27510
27525
  // export {
@@ -27686,6 +27701,16 @@ var LayoutModel = function LayoutModel(layoutItem, workspaceLayout, dashboardId)
27686
27701
  if (Array.isArray(widgetConfig.eventHandlers)) {
27687
27702
  layout.eventHandlers = widgetConfig.eventHandlers;
27688
27703
  }
27704
+ // Migrate legacy bare `component` references to the canonical
27705
+ // scoped form (`scope.package.Component`). ComponentManager.config
27706
+ // returns the resolved scoped key on `widgetConfig.component`, so
27707
+ // we just lift it. Idempotent: items already scoped pass through
27708
+ // unchanged because resolveComponentKey returns the input verbatim
27709
+ // when it's already in the map. Persisted on next save — no
27710
+ // separate migration step needed.
27711
+ if (typeof widgetConfig.component === "string" && widgetConfig.component && widgetConfig.component !== layout.component) {
27712
+ layout.component = widgetConfig.component;
27713
+ }
27689
27714
  }
27690
27715
 
27691
27716
  // Merge user-entered config values (from EnhancedWidgetDropdown) into userPrefs
@@ -31371,6 +31396,120 @@ var ContextModel = /*#__PURE__*/function () {
31371
31396
  }]);
31372
31397
  }();
31373
31398
 
31399
+ /**
31400
+ * scopedComponentId.js
31401
+ *
31402
+ * Single source of truth for the canonical scoped component id used by
31403
+ * ComponentManager registration, layout items, and publish-time scope
31404
+ * remap. Format: `scope.packageName.ComponentName`.
31405
+ *
31406
+ * Why a 3-part dotted form (and not `@scope/pkg.Component`)?
31407
+ * - Matches existing `config.id` shape that `.dash.js` files already
31408
+ * set when authored explicitly (see ComponentManager.registerWidget:
31409
+ * `const registrationKey = config.id || widgetKey;`).
31410
+ * - Trivial to parse (`split(".")` — three parts, ordered).
31411
+ * - Avoids slashes inside object keys, which some downstream serializers
31412
+ * (older dash-registry indexers) historically choked on.
31413
+ *
31414
+ * The upstream package id may arrive in either of two shapes:
31415
+ * - "@scope/pkg" (npm-style)
31416
+ * - "scope/pkg" (bare scope)
31417
+ * Both produce the same scoped id.
31418
+ */
31419
+
31420
+ /**
31421
+ * Build the canonical scoped component id from a package name and a
31422
+ * bare component name.
31423
+ *
31424
+ * @param {string} packageName e.g. "@ai-built/pipeline" or "ai-built/pipeline"
31425
+ * @param {string} componentName e.g. "ProspectListColumn"
31426
+ * @returns {string} e.g. "ai-built.pipeline.ProspectListColumn"
31427
+ */
31428
+ function makeScopedComponentId(packageName, componentName) {
31429
+ if (!componentName) return "";
31430
+ if (!packageName) return componentName;
31431
+ var cleaned = String(packageName).replace(/^@/, "").replace(/\//g, ".");
31432
+ return "".concat(cleaned, ".").concat(componentName);
31433
+ }
31434
+
31435
+ /**
31436
+ * Parse a scoped component id into its three parts. Returns null when
31437
+ * the input isn't a 3-part dotted id (e.g. legacy bare names).
31438
+ *
31439
+ * @param {string} scopedId
31440
+ * @returns {{scope: string, packageName: string, componentName: string} | null}
31441
+ */
31442
+ function parseScopedComponentId(scopedId) {
31443
+ if (typeof scopedId !== "string") return null;
31444
+ var parts = scopedId.split(".");
31445
+ if (parts.length !== 3) return null;
31446
+ return {
31447
+ scope: parts[0],
31448
+ packageName: parts[1],
31449
+ componentName: parts[2]
31450
+ };
31451
+ }
31452
+
31453
+ /**
31454
+ * Pull the bare component name from a scoped or unscoped id.
31455
+ *
31456
+ * @param {string} idOrName
31457
+ * @returns {string}
31458
+ */
31459
+ function bareComponentName(idOrName) {
31460
+ if (typeof idOrName !== "string") return "";
31461
+ var parts = idOrName.split(".");
31462
+ return parts[parts.length - 1] || "";
31463
+ }
31464
+
31465
+ /**
31466
+ * Resolve a component name to a registry key. Returns null if no
31467
+ * match exists.
31468
+ *
31469
+ * Pure function — depends only on the passed-in `componentMap` and
31470
+ * `data`. Lives outside ComponentManager so it can be tested without
31471
+ * pulling in the full Component/Layout import chain (and so other
31472
+ * call sites can route through it without circular deps).
31473
+ *
31474
+ * Lookup order (the LAYOUT ITEM is the source of truth):
31475
+ * 1. EXACT match on `component` — covers the new scoped form
31476
+ * (`scope.package.X`) and any legacy `.dash.js` that already
31477
+ * set `config.id` to a scoped value.
31478
+ * 2. If `component` is bare (no dots) AND we have a packageId hint
31479
+ * on the layout item, build the scoped id and try that.
31480
+ * 3. Bare-name fallback: scan the map for any key ending in
31481
+ * `.${component}`. If exactly one matches, use it. If multiple
31482
+ * match (the collision case), prefer the one matching the
31483
+ * layout item's `packageId` / `_sourcePackage`; otherwise fall
31484
+ * through to the first match (deterministic, but also logs a
31485
+ * warning so callers can spot the ambiguity).
31486
+ *
31487
+ * Step (3) is the back-compat path for layouts authored before
31488
+ * scoped registration landed. New layouts ALWAYS resolve via step (1).
31489
+ */
31490
+ function resolveComponentKey(componentMap, component, data) {
31491
+ if (!componentMap || !component) return null;
31492
+ if (component in componentMap) return component;
31493
+ if (typeof component !== "string") return null;
31494
+ if (component.includes(".")) return null;
31495
+ var packageId = (data === null || data === void 0 ? void 0 : data.packageId) || (data === null || data === void 0 ? void 0 : data._sourcePackage) || (data === null || data === void 0 ? void 0 : data.packageName) || null;
31496
+ if (packageId) {
31497
+ var scoped = makeScopedComponentId(packageId, component);
31498
+ if (scoped in componentMap) return scoped;
31499
+ }
31500
+ var suffix = ".".concat(component);
31501
+ var matches = Object.keys(componentMap).filter(function (k) {
31502
+ return k.endsWith(suffix);
31503
+ });
31504
+ if (matches.length === 0) return null;
31505
+ if (matches.length === 1) return matches[0];
31506
+ if (packageId) {
31507
+ var target = makeScopedComponentId(packageId, component);
31508
+ if (matches.includes(target)) return target;
31509
+ }
31510
+ return matches[0];
31511
+ }
31512
+
31374
31513
  var _componentMap = {};
31375
31514
  var _containerComponent = null;
31376
31515
  var _gridContainerComponent = null;
@@ -31407,6 +31546,25 @@ var ComponentManager = {
31407
31546
  componentMap: function componentMap() {
31408
31547
  return _componentMap;
31409
31548
  },
31549
+ /**
31550
+ * Resolve a component name to its registered config — single source
31551
+ * of truth for every render-path lookup. Routes through
31552
+ * `resolveComponentKey` so legacy bare names (`"ProspectWorkspace"`)
31553
+ * still find their registered scoped counterpart
31554
+ * (`"ai-built.pipeline.ProspectWorkspace"`). Returns null when the
31555
+ * widget isn't registered. Pass the layout item as `data` to use
31556
+ * its `packageId` / `_sourcePackage` for disambiguation.
31557
+ *
31558
+ * @param {string} component
31559
+ * @param {object} [data]
31560
+ * @returns {object|null} the live registered config, or null
31561
+ */
31562
+ resolve: function resolve(component, data) {
31563
+ var m = _componentMap;
31564
+ if (!m) return null;
31565
+ var key = resolveComponentKey(m, component, data);
31566
+ return key ? m[key] || null : null;
31567
+ },
31410
31568
  /**
31411
31569
  * The method for registering the widget into the Dashboard application
31412
31570
  * This is a requirement for the widget to be included into the Dash
@@ -31475,15 +31633,18 @@ var ComponentManager = {
31475
31633
  * @returns {Widget} the Widget in the component map
31476
31634
  */
31477
31635
  getComponent: function getComponent(component) {
31636
+ var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
31478
31637
  try {
31479
31638
  // console.log("get component");
31480
31639
  if (component && this.componentMap()) {
31481
31640
  if (ComponentManager.isLayoutContainer(component) === false) {
31482
31641
  var m = this.componentMap();
31483
- // Try exact match first (works for both scoped ids and legacy names)
31484
- var cmp = component in m ? m[component] : null;
31485
- if (cmp !== null) {
31486
- cmp["componentName"] = component;
31642
+ // Resolve through the scoped/bare lookup pipeline so layouts
31643
+ // authored under either format land at the same registry key.
31644
+ var resolvedKey = resolveComponentKey(m, component, data);
31645
+ var cmp = resolvedKey ? m[resolvedKey] : null;
31646
+ if (cmp !== null && cmp !== undefined) {
31647
+ cmp["componentName"] = resolvedKey;
31487
31648
  return cmp;
31488
31649
  }
31489
31650
  } else {
@@ -31661,17 +31822,20 @@ var ComponentManager = {
31661
31822
 
31662
31823
  // get the component configuration from the map
31663
31824
  var components = this.map();
31664
- if (component in components) {
31825
+ var resolvedKey = resolveComponentKey(components, component, data);
31826
+ if (resolvedKey && resolvedKey in components) {
31665
31827
  // let c = deepCopy(components['component']);
31666
31828
 
31667
31829
  // we have to make sure that we remove the component if this is a context
31668
31830
 
31669
- var tempComponent = components[component];
31831
+ var tempComponent = components[resolvedKey];
31670
31832
  delete tempComponent["component"];
31671
31833
  var c = JSON.parse(JSON.stringify(tempComponent));
31672
31834
 
31673
- // tack on the component name
31674
- c["component"] = component;
31835
+ // Carry the canonical scoped id forward so callers (LayoutModel,
31836
+ // ComponentManager.getComponent) can rewrite layout items'
31837
+ // `component` to the scoped form on first load.
31838
+ c["component"] = resolvedKey;
31675
31839
 
31676
31840
  // if no userConfig key. let's add it for the next step
31677
31841
  if ("userConfig" in c === false) {
@@ -51307,14 +51471,17 @@ var NotificationsSection = function NotificationsSection(_ref) {
51307
51471
  });
51308
51472
  }, []);
51309
51473
 
51310
- // Collect all widget instances with notifications from workspaces
51474
+ // Collect all widget instances with notifications from workspaces.
51475
+ // Route through `ComponentManager.resolve` so a legacy layout
51476
+ // referencing a bare component name still finds its registered
51477
+ // scoped form (post-v0.1.432). Direct `componentMap[item.component]`
51478
+ // returns undefined for bare names after the migration.
51311
51479
  var widgetInstances = [];
51312
- var componentMap = ComponentManager.componentMap() || {};
51313
51480
  workspaces.forEach(function (ws) {
51314
51481
  var items = flattenLayout(ws.layout);
51315
51482
  items.forEach(function (item) {
51316
51483
  var _config$notifications;
51317
- var config = componentMap[item.component];
51484
+ var config = ComponentManager.resolve(item.component, item);
51318
51485
  if ((config === null || config === void 0 || (_config$notifications = config.notifications) === null || _config$notifications === void 0 ? void 0 : _config$notifications.length) > 0) {
51319
51486
  var _item$userPrefs;
51320
51487
  widgetInstances.push({
@@ -53531,7 +53698,13 @@ var DraggableWidgetItem = function DraggableWidgetItem(_ref) {
53531
53698
  _useDrag2 = _slicedToArray(_useDrag, 2),
53532
53699
  isDragging = _useDrag2[0].isDragging,
53533
53700
  drag = _useDrag2[1];
53534
- var providerTypes = (widget.providers || []).map(function (p) {
53701
+
53702
+ // Route through getUserConfigurableProviders — drops the `api`
53703
+ // class AND null/undefined entries that occasionally appear in
53704
+ // sparse `widget.providers` arrays. Without this, a single null
53705
+ // slot crashes the whole sidebar with `Cannot read properties of
53706
+ // null (reading 'type')`.
53707
+ var providerTypes = getUserConfigurableProviders(widget.providers).map(function (p) {
53535
53708
  return p.type;
53536
53709
  });
53537
53710
  var eventCount = (widget.events || []).length;
@@ -54105,12 +54278,18 @@ var WidgetSidebar = function WidgetSidebar(_ref4) {
54105
54278
  }))).sort();
54106
54279
  }, [allWidgets]);
54107
54280
 
54108
- // Derive unique provider types for dropdown
54281
+ // Derive unique provider types for the filter dropdown. Route
54282
+ // through getUserConfigurableProviders so a single null entry in
54283
+ // any widget's `providers` array doesn't crash the whole sidebar
54284
+ // (it filters non-objects AND drops the `api` provider class that
54285
+ // isn't user-configurable). Observed prod crash:
54286
+ // `TypeError: Cannot read properties of null (reading 'type')` →
54287
+ // unmounted WidgetSidebar via React error boundary.
54109
54288
  var uniqueProviders = React.useMemo(function () {
54110
54289
  var types = new Set();
54111
54290
  allWidgets.forEach(function (_ref7) {
54112
54291
  var widget = _ref7.widget;
54113
- return (widget.providers || []).forEach(function (p) {
54292
+ return getUserConfigurableProviders(widget === null || widget === void 0 ? void 0 : widget.providers).forEach(function (p) {
54114
54293
  return types.add(p.type);
54115
54294
  });
54116
54295
  });
@@ -54132,12 +54311,13 @@ var WidgetSidebar = function WidgetSidebar(_ref4) {
54132
54311
  if (filterAuthor !== "all") {
54133
54312
  if ((widget["package"] || widget.author || "Other") !== filterAuthor) return false;
54134
54313
  }
54135
- // Provider filter
54314
+ // Provider filter — same null-safety as `uniqueProviders` above.
54136
54315
  if (filterProvider !== "all") {
54316
+ var configurable = getUserConfigurableProviders(widget === null || widget === void 0 ? void 0 : widget.providers);
54137
54317
  if (filterProvider === "none") {
54138
- if (widget.providers && widget.providers.length > 0) return false;
54318
+ if (configurable.length > 0) return false;
54139
54319
  } else {
54140
- if (!widget.providers || !widget.providers.some(function (p) {
54320
+ if (!configurable.some(function (p) {
54141
54321
  return p.type === filterProvider;
54142
54322
  })) return false;
54143
54323
  }
@@ -57715,9 +57895,13 @@ var DashboardStageInner = function DashboardStageInner(_ref3) {
57715
57895
  return getUnresolvedProviders({
57716
57896
  workspace: workspaceSelected,
57717
57897
  appProviders: (appContext === null || appContext === void 0 ? void 0 : appContext.providers) || {},
57898
+ // Use ComponentManager.config so a legacy layout referencing
57899
+ // a bare component name (`"PipelineKanban"`) still resolves
57900
+ // to its scoped registration. Direct `componentMap[name]`
57901
+ // returns undefined after the v0.1.432 scoped-IDs migration.
57718
57902
  getWidgetRequirements: function getWidgetRequirements(name) {
57719
- var _ComponentManager$com;
57720
- return name && ((_ComponentManager$com = ComponentManager.componentMap()[name]) === null || _ComponentManager$com === void 0 ? void 0 : _ComponentManager$com.providers) || [];
57903
+ var _ComponentManager$con;
57904
+ return name && ((_ComponentManager$con = ComponentManager.config(name)) === null || _ComponentManager$con === void 0 ? void 0 : _ComponentManager$con.providers) || [];
57721
57905
  }
57722
57906
  });
57723
57907
  }, [workspaceSelected, appContext === null || appContext === void 0 ? void 0 : appContext.providers]);
@@ -58719,13 +58903,18 @@ var DashboardStageInner = function DashboardStageInner(_ref3) {
58719
58903
  isOpen: isConfigModalOpen,
58720
58904
  setIsOpen: setIsConfigModalOpen,
58721
58905
  workspace: workspaceSelected,
58722
- appProviders: (appContext === null || appContext === void 0 ? void 0 : appContext.providers) || {},
58906
+ appProviders: (appContext === null || appContext === void 0 ? void 0 : appContext.providers) || {}
58907
+ // Use ComponentManager.config so legacy bare component
58908
+ // refs in `workspaceSelected.layout` still resolve to
58909
+ // their registered scoped form (post-v0.1.432). Direct
58910
+ // `componentMap[name]` returns undefined for those.
58911
+ ,
58723
58912
  getWidgetRequirements: function getWidgetRequirements(name) {
58724
- var _ComponentManager$com2;
58725
- return name && ((_ComponentManager$com2 = ComponentManager.componentMap()[name]) === null || _ComponentManager$com2 === void 0 ? void 0 : _ComponentManager$com2.providers) || [];
58913
+ var _ComponentManager$con2;
58914
+ return name && ((_ComponentManager$con2 = ComponentManager.config(name)) === null || _ComponentManager$con2 === void 0 ? void 0 : _ComponentManager$con2.providers) || [];
58726
58915
  },
58727
58916
  getWidgetConfig: function getWidgetConfig(name) {
58728
- return name && ComponentManager.componentMap()[name] || null;
58917
+ return name && ComponentManager.config(name) || null;
58729
58918
  },
58730
58919
  onSaveBindings: handleBulkProviderBindings,
58731
58920
  onSaveListeners: handleBulkListenerBindings,
@@ -62322,6 +62511,7 @@ exports.WorkspaceModel = WorkspaceModel;
62322
62511
  exports.WorkspaceScopeContext = WorkspaceScopeContext;
62323
62512
  exports.addChildToLayoutItem = addChildToLayoutItem;
62324
62513
  exports.addItemToItemLayout = addItemToItemLayout;
62514
+ exports.bareComponentName = bareComponentName;
62325
62515
  exports.buildMcpConfigFromOverrides = buildMcpConfigFromOverrides;
62326
62516
  exports.canHaveChildren = canHaveChildren;
62327
62517
  exports.changeDirectionForLayoutItem = changeDirectionForLayoutItem;
@@ -62367,10 +62557,12 @@ exports.isWidgetResolvable = isWidgetResolvable;
62367
62557
  exports.isWorkspace = isWorkspace;
62368
62558
  exports.layoutItemHasWorkspaceAsChild = layoutItemHasWorkspaceAsChild;
62369
62559
  exports.loadWidgetBundle = loadWidgetBundle;
62560
+ exports.makeScopedComponentId = makeScopedComponentId;
62370
62561
  exports.mcpJsonToFormState = mcpJsonToFormState;
62371
62562
  exports.moveWidgetAcrossContainers = moveWidgetAcrossContainers;
62372
62563
  exports.numChildrenForLayout = numChildrenForLayout;
62373
62564
  exports.parse = parse;
62565
+ exports.parseScopedComponentId = parseScopedComponentId;
62374
62566
  exports.removeItemFromLayout = removeItemFromLayout;
62375
62567
  exports.renderComponent = renderComponent;
62376
62568
  exports.renderGridLayout = renderGridLayout;