@trops/dash-core 0.1.446 → 0.1.447

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.
@@ -74145,6 +74145,99 @@ function findWidget(registry, packageId) {
74145
74145
  return null;
74146
74146
  }
74147
74147
 
74148
+ /**
74149
+ * Dedup duplicate `{type: "..."}` entries inside a `providers: [...]`
74150
+ * array literal in a .dash.js source string. Mirrors the regex used at
74151
+ * AI-build write time in dash-electron's WidgetBuilderModal so old
74152
+ * AI-generated widgets get healed before publish (the runtime parse
74153
+ * dedup keeps consumers correct, but the raw .dash.js text on disk
74154
+ * stays dirty unless we rewrite it).
74155
+ *
74156
+ * Conservative: only handles a single-level array of object literals.
74157
+ * More exotic forms fall through unchanged and the runtime dedup picks
74158
+ * up the slack.
74159
+ *
74160
+ * @param {string} source
74161
+ * @returns {{ source: string, dropped: number }}
74162
+ */
74163
+ function dedupProvidersInDashSource(source) {
74164
+ if (!source) return { source, dropped: 0 };
74165
+ let totalDropped = 0;
74166
+ const cleaned = source.replace(
74167
+ /(providers\s*:\s*\[)([^[\]]*?)(\])/,
74168
+ (match, head, body, tail) => {
74169
+ const chunks = body
74170
+ .split(/(\{[^{}]*\})/)
74171
+ .filter((s) => s && /\S/.test(s));
74172
+ const seenTypes = new Set();
74173
+ const kept = [];
74174
+ let dropped = 0;
74175
+ for (const chunk of chunks) {
74176
+ if (!chunk.startsWith("{")) continue;
74177
+ const typeMatch = chunk.match(/type\s*:\s*["']([^"']+)["']/);
74178
+ if (!typeMatch) {
74179
+ kept.push(chunk.trim());
74180
+ continue;
74181
+ }
74182
+ const t = typeMatch[1];
74183
+ if (seenTypes.has(t)) {
74184
+ dropped++;
74185
+ continue;
74186
+ }
74187
+ seenTypes.add(t);
74188
+ kept.push(chunk.trim());
74189
+ }
74190
+ if (dropped === 0) return match;
74191
+ totalDropped += dropped;
74192
+ return `${head}${kept.join(", ")}${tail}`;
74193
+ },
74194
+ );
74195
+ return { source: cleaned, dropped: totalDropped };
74196
+ }
74197
+
74198
+ /**
74199
+ * Walk a widget package's `.dash.js` files and rewrite any with
74200
+ * duplicate provider-type entries. Returns counts so the publish
74201
+ * caller can log what was healed. Errors are non-fatal — a single
74202
+ * unparseable .dash.js shouldn't block the whole publish.
74203
+ */
74204
+ function cleanupProvidersInWidgetPackage(widgetPath) {
74205
+ const summary = { filesScanned: 0, filesRewritten: 0, totalDropped: 0 };
74206
+ try {
74207
+ const widgetsDir =
74208
+ findWidgetsDir(widgetPath) || path.join(widgetPath, "widgets");
74209
+ if (!fs.existsSync(widgetsDir)) return summary;
74210
+ for (const file of fs.readdirSync(widgetsDir)) {
74211
+ if (!file.endsWith(".dash.js")) continue;
74212
+ const filePath = path.join(widgetsDir, file);
74213
+ try {
74214
+ const original = fs.readFileSync(filePath, "utf8");
74215
+ summary.filesScanned++;
74216
+ const { source: deduped, dropped } =
74217
+ dedupProvidersInDashSource(original);
74218
+ if (dropped > 0 && deduped !== original) {
74219
+ fs.writeFileSync(filePath, deduped, "utf8");
74220
+ summary.filesRewritten++;
74221
+ summary.totalDropped += dropped;
74222
+ console.log(
74223
+ `[widgetRegistry] Cleaned ${dropped} duplicate provider(s) from ${file}`,
74224
+ );
74225
+ }
74226
+ } catch (err) {
74227
+ console.warn(
74228
+ `[widgetRegistry] cleanupProviders skip ${file}: ${err.message}`,
74229
+ );
74230
+ }
74231
+ }
74232
+ } catch (err) {
74233
+ console.warn(
74234
+ "[widgetRegistry] cleanupProvidersInWidgetPackage failed:",
74235
+ err.message,
74236
+ );
74237
+ }
74238
+ return summary;
74239
+ }
74240
+
74148
74241
  /**
74149
74242
  * Scan a widget package directory for `.dash.js` component configs and
74150
74243
  * return the parsed configs. Used when the widget registry's cached
@@ -74487,6 +74580,21 @@ async function prepareWidgetForPublish$1(appId, packageId, options = {}) {
74487
74580
  }
74488
74581
  }
74489
74582
 
74583
+ // 5b. Heal `.dash.js` source files that have duplicate
74584
+ // provider-type entries before we read configs / build the
74585
+ // manifest / zip. AI-generated configs occasionally double a
74586
+ // `{type:"..."}` entry; the runtime dedup makes it invisible
74587
+ // on the publisher's machine, but we don't want the dirty raw
74588
+ // text shipping to the registry. Mirrors the write-time dedup
74589
+ // in dash-electron's WidgetBuilderModal so older widgets
74590
+ // authored before that fix landed get cleaned at publish.
74591
+ const providerCleanupSummary = cleanupProvidersInWidgetPackage(widget.path);
74592
+ if (providerCleanupSummary.filesRewritten > 0) {
74593
+ console.log(
74594
+ `[widgetRegistry] Provider cleanup: rewrote ${providerCleanupSummary.filesRewritten} file(s), removed ${providerCleanupSummary.totalDropped} duplicate(s)`,
74595
+ );
74596
+ }
74597
+
74490
74598
  // 6. Build manifest using the widget's component configs. The
74491
74599
  // registry cache may be missing widgets (orphaned / locally-
74492
74600
  // registered packages), so fall back to scanning the package's