@trops/dash-core 0.1.445 → 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.
@@ -27191,6 +27191,10 @@ var widgetRegistry$1 = {exports: {}};
27191
27191
 
27192
27192
  var dynamicWidgetLoader$3 = {exports: {}};
27193
27193
 
27194
+ function commonjsRequire(path) {
27195
+ throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
27196
+ }
27197
+
27194
27198
  /**
27195
27199
  * Widget Compiler
27196
27200
  *
@@ -27204,6 +27208,90 @@ var dynamicWidgetLoader$3 = {exports: {}};
27204
27208
  const fs$7 = require$$0$2;
27205
27209
  const path$b = require$$1$2;
27206
27210
 
27211
+ /**
27212
+ * Structured error thrown by compileWidget() when the underlying
27213
+ * esbuild spawn fails (typically ENOENT — the native helper binary is
27214
+ * missing on this arch in a packaged build). The renderer surfaces
27215
+ * `.code` + `.diagnostics` to give the user something actionable
27216
+ * instead of a raw "spawn ENOENT".
27217
+ */
27218
+ class WidgetCompileError extends Error {
27219
+ constructor(message, code, diagnostics) {
27220
+ super(message);
27221
+ this.name = "WidgetCompileError";
27222
+ this.code = code;
27223
+ this.diagnostics = diagnostics;
27224
+ }
27225
+ }
27226
+
27227
+ /**
27228
+ * Probe the on-disk state of esbuild + its arch-specific native helper.
27229
+ * Returns a flat object suitable for logging/UI display. Never throws.
27230
+ */
27231
+ function getEsbuildDiagnostics() {
27232
+ const diagnostics = {
27233
+ platform: process.platform,
27234
+ arch: process.arch,
27235
+ esbuildVersion: null,
27236
+ esbuildPackageDir: null,
27237
+ archPackage: `@esbuild/${process.platform}-${process.arch}`,
27238
+ nativeBinaryPath: null,
27239
+ nativeBinaryExists: false,
27240
+ };
27241
+
27242
+ try {
27243
+ const pkgJsonPath = require.resolve("esbuild/package.json");
27244
+ diagnostics.esbuildPackageDir = path$b.dirname(pkgJsonPath);
27245
+ diagnostics.esbuildVersion = commonjsRequire(pkgJsonPath).version;
27246
+ } catch (err) {
27247
+ diagnostics.esbuildResolveError = err.message;
27248
+ }
27249
+
27250
+ try {
27251
+ const archPkgJson = require.resolve(
27252
+ `${diagnostics.archPackage}/package.json`,
27253
+ );
27254
+ const archDir = path$b.dirname(archPkgJson);
27255
+ // esbuild's native binary on macOS/Linux is bin/esbuild;
27256
+ // on Windows it's esbuild.exe at the package root.
27257
+ const candidate =
27258
+ process.platform === "win32"
27259
+ ? path$b.join(archDir, "esbuild.exe")
27260
+ : path$b.join(archDir, "bin", "esbuild");
27261
+ diagnostics.nativeBinaryPath = candidate;
27262
+ diagnostics.nativeBinaryExists = fs$7.existsSync(candidate);
27263
+ } catch (err) {
27264
+ diagnostics.archResolveError = err.message;
27265
+ }
27266
+
27267
+ return diagnostics;
27268
+ }
27269
+
27270
+ /**
27271
+ * Quick liveness probe for the widget compiler. Runs a no-op
27272
+ * `esbuild.transform("")` so any missing-native-binary failure surfaces
27273
+ * before the user tries to compile a real widget. Returns
27274
+ * `{ ok, error?, code?, diagnostics }` — never throws.
27275
+ */
27276
+ async function healthCheck() {
27277
+ const diagnostics = getEsbuildDiagnostics();
27278
+ try {
27279
+ const esbuild = require("esbuild");
27280
+ await esbuild.transform("", { loader: "js" });
27281
+ return { ok: true, diagnostics };
27282
+ } catch (err) {
27283
+ return {
27284
+ ok: false,
27285
+ error: err.message,
27286
+ code:
27287
+ err.code === "ENOENT" || /spawn|ENOENT/i.test(err.message || "")
27288
+ ? "ESBUILD_SPAWN_FAILED"
27289
+ : "ESBUILD_UNAVAILABLE",
27290
+ diagnostics,
27291
+ };
27292
+ }
27293
+ }
27294
+
27207
27295
  /**
27208
27296
  * Find the widgets/ directory, handling nested ZIP extraction.
27209
27297
  *
@@ -27385,6 +27473,18 @@ async function compileWidget$1(widgetPath) {
27385
27473
  `[WidgetCompiler] Compilation failed for ${widgetPath}:`,
27386
27474
  error,
27387
27475
  );
27476
+ // ENOENT on the esbuild path means the native helper binary
27477
+ // wasn't found — usually a packaging issue (wrong arch in the
27478
+ // universal asar, asar-unpacked glob missing the arch package,
27479
+ // dev install never ran for the runtime arch). Wrap with
27480
+ // diagnostics so the UI can show something useful.
27481
+ if (error.code === "ENOENT" || /spawn|ENOENT/i.test(error.message || "")) {
27482
+ throw new WidgetCompileError(
27483
+ `Widget compiler unavailable: ${error.message}`,
27484
+ "ESBUILD_SPAWN_FAILED",
27485
+ getEsbuildDiagnostics(),
27486
+ );
27487
+ }
27388
27488
  throw error;
27389
27489
  } finally {
27390
27490
  // Clean up temporary entry file
@@ -27402,7 +27502,13 @@ async function compileWidget$1(widgetPath) {
27402
27502
  }
27403
27503
  }
27404
27504
 
27405
- var widgetCompiler$1 = { compileWidget: compileWidget$1, findWidgetsDir: findWidgetsDir$2 };
27505
+ var widgetCompiler$1 = {
27506
+ compileWidget: compileWidget$1,
27507
+ findWidgetsDir: findWidgetsDir$2,
27508
+ healthCheck,
27509
+ getEsbuildDiagnostics,
27510
+ WidgetCompileError,
27511
+ };
27406
27512
 
27407
27513
  /**
27408
27514
  * Dynamic Widget Loader
@@ -74039,6 +74145,99 @@ function findWidget(registry, packageId) {
74039
74145
  return null;
74040
74146
  }
74041
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
+
74042
74241
  /**
74043
74242
  * Scan a widget package directory for `.dash.js` component configs and
74044
74243
  * return the parsed configs. Used when the widget registry's cached
@@ -74381,6 +74580,21 @@ async function prepareWidgetForPublish$1(appId, packageId, options = {}) {
74381
74580
  }
74382
74581
  }
74383
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
+
74384
74598
  // 6. Build manifest using the widget's component configs. The
74385
74599
  // registry cache may be missing widgets (orphaned / locally-
74386
74600
  // registered packages), so fall back to scanning the package's