cascivo 0.5.0 → 0.5.2

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/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  [![license](https://img.shields.io/npm/l/cascivo?style=flat-square&color=0079bf)](https://github.com/cascivo/cascivo/blob/main/LICENSE)
11
11
  ![types](https://img.shields.io/badge/types-included-0079bf?style=flat-square&logo=typescript&logoColor=white)
12
12
 
13
- [npm](https://www.npmjs.com/package/cascivo) · [cascivo.com](https://cascivo.com) · [Docs](https://docs.cascivo.com) · [Storybook](https://storybook.cascivo.com) · [GitHub](https://github.com/cascivo/cascivo)
13
+ [npm](https://www.npmjs.com/package/cascivo) · [cascivo.com](https://cascivo.com) · [Docs](https://cascivo.com/docs) · [Storybook](https://storybook.cascivo.com) · [GitHub](https://github.com/cascivo/cascivo)
14
14
 
15
15
  </div>
16
16
 
@@ -80,6 +80,6 @@ pnpm add cascivo
80
80
 
81
81
  ---
82
82
 
83
- [cascivo.com](https://cascivo.com) · [Docs](https://docs.cascivo.com) · [Storybook](https://storybook.cascivo.com) · [GitHub](https://github.com/cascivo/cascivo) · AI agents: read [`llms.txt`](https://cascivo.com/llms.txt) (install steps + component index, plain text) or use [`@cascivo/mcp`](https://github.com/cascivo/cascivo/tree/main/packages/mcp) and [`registry.json`](https://github.com/cascivo/cascivo/blob/main/registry.json) · MIT
83
+ [cascivo.com](https://cascivo.com) · [Docs](https://cascivo.com/docs) · [Storybook](https://storybook.cascivo.com) · [GitHub](https://github.com/cascivo/cascivo) · AI agents: read [`llms.txt`](https://cascivo.com/llms.txt) (install steps + component index, plain text) or use [`@cascivo/mcp`](https://github.com/cascivo/cascivo/tree/main/packages/mcp) and [`registry.json`](https://github.com/cascivo/cascivo/blob/main/registry.json) · MIT
84
84
 
85
85
  <div align="center"><a href="https://cascivo.com"><img src="https://cascivo.com/favicon.svg" width="28" height="28" alt="cascivo"></a></div>
@@ -28,9 +28,9 @@ var config_exports = /* @__PURE__ */ __exportAll({
28
28
  resolveConfig: () => resolveConfig
29
29
  });
30
30
  /**
31
- * Canonical host for hosted cascivo artifacts (registry.json, per-item
32
- * r/<name>.json, marketplace.json). docs.cascivo.com mirrors the same tree.
33
- * Keep in sync with CASCIVO_HOST in packages/mcp.
31
+ * Canonical (and only) host for hosted cascivo artifacts (registry.json, per-item
32
+ * r/<name>.json, marketplace.json, /llms/*, /context/*). The legacy docs.cascivo.com
33
+ * subdomain is retired and 301s here. Keep in sync with CASCIVO_HOST in packages/mcp.
34
34
  */
35
35
  const CASCIVO_HOST = "https://cascivo.com";
36
36
  /** All first-party themes shipped by @cascivo/themes (selectable via data-theme). */
@@ -168,4 +168,4 @@ function installHint(pm, packages, opts = {}) {
168
168
  //#endregion
169
169
  export { detectPackageManager as a, isPackageManager as c, config_exports as i, loadConfig as l, DEFAULT_CONFIG as n, installCommand as o, THEMES as r, installHint as s, CASCIVO_HOST as t, __exportAll as u };
170
170
 
171
- //# sourceMappingURL=config-CYlTGdmE.mjs.map
171
+ //# sourceMappingURL=config-C6GdrbvF.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-C6GdrbvF.mjs","names":[],"sources":["../src/utils/config.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\n/**\n * Canonical (and only) host for hosted cascivo artifacts (registry.json, per-item\n * r/<name>.json, marketplace.json, /llms/*, /context/*). The legacy docs.cascivo.com\n * subdomain is retired and 301s here. Keep in sync with CASCIVO_HOST in packages/mcp.\n */\nexport const CASCIVO_HOST = 'https://cascivo.com'\n\n/** All first-party themes shipped by @cascivo/themes (selectable via data-theme). */\nexport const THEMES = [\n 'light',\n 'dark',\n 'warm',\n 'flat',\n 'minimal',\n 'midnight',\n 'pastel',\n 'brutalist',\n 'corporate',\n 'terminal',\n 'cyberpunk',\n 'arcade',\n] as const\n\nexport type ThemeName = (typeof THEMES)[number]\n\nexport type RegistryNamespaceConfig =\n | string\n | { url: string; headers?: Record<string, string>; params?: Record<string, string> }\n\nexport interface CascadeConfig {\n /** URL of the registry.json index. */\n registry: string\n /** Directory (relative to project root) where components are written. */\n outputDir: string\n /** Default theme imported by `cascade init`. */\n theme: ThemeName\n /** Namespace → registry URL template (with {name} placeholder) or auth config. */\n registries?: Record<string, RegistryNamespaceConfig>\n /** Whether to copy test files (*.contract.test.tsx) when adding components. */\n tests?: boolean\n}\n\nexport const DEFAULT_CONFIG: CascadeConfig = {\n // Canonical hosted registry index (served from the landing site, documented in\n // llms.txt). Prefer this over a branch's GitHub raw URL, which 404s for\n // unauthenticated/private-repo requests and breaks `cascivo list`/`add`.\n registry: `${CASCIVO_HOST}/registry.json`,\n outputDir: 'src/components/ui',\n theme: 'light',\n}\n\nconst CONFIG_FILES = ['cascivo.config.ts', 'cascivo.config.js', 'cascivo.config.mjs']\n\n/** Apply defaults over a (possibly partial) user config object. */\nexport function resolveConfig(partial: Partial<CascadeConfig> | null | undefined): CascadeConfig {\n return { ...DEFAULT_CONFIG, ...partial }\n}\n\n/** Let env vars override config — used by the MCP server to pass `outputDir`. */\nexport function applyEnvOverrides(\n config: CascadeConfig,\n env: NodeJS.ProcessEnv = process.env,\n): CascadeConfig {\n return {\n ...config,\n ...(env.CASCIVO_REGISTRY ? { registry: env.CASCIVO_REGISTRY } : {}),\n ...(env.CASCIVO_OUTPUT_DIR ? { outputDir: env.CASCIVO_OUTPUT_DIR } : {}),\n }\n}\n\n/**\n * Locate and load `cascivo.config.{ts,js,mjs}` from `cwd`. Falls back to the\n * default config when no file is found or the file cannot be loaded. Env vars\n * (`CASCIVO_REGISTRY`, `CASCIVO_OUTPUT_DIR`) take precedence.\n */\nexport async function loadConfig(cwd: string = process.cwd()): Promise<CascadeConfig> {\n for (const file of CONFIG_FILES) {\n const path = join(cwd, file)\n if (!existsSync(path)) continue\n try {\n const mod = (await import(pathToFileURL(path).href)) as {\n default?: Partial<CascadeConfig>\n }\n return applyEnvOverrides(resolveConfig(mod.default ?? (mod as Partial<CascadeConfig>)))\n } catch {\n // Unloadable config (e.g. unsupported TS syntax) — fall back to defaults.\n return applyEnvOverrides(resolveConfig(null))\n }\n }\n return applyEnvOverrides(resolveConfig(null))\n}\n\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm' | 'bun'\n\n/** Narrow an arbitrary string to a known PackageManager. */\nexport function isPackageManager(value: string | undefined): value is PackageManager {\n return value === 'pnpm' || value === 'yarn' || value === 'npm' || value === 'bun'\n}\n\n/**\n * Lock files in probe order. `bun.lock` (bun ≥ 1.2 text lockfile) sits beside the\n * legacy binary `bun.lockb`; `package-lock.json` is npm's marker so a walk that\n * reaches an npm workspace root still resolves to npm rather than the default.\n */\nconst PM_LOCKFILES: readonly [string, PackageManager][] = [\n ['pnpm-lock.yaml', 'pnpm'],\n ['yarn.lock', 'yarn'],\n ['bun.lockb', 'bun'],\n ['bun.lock', 'bun'],\n ['package-lock.json', 'npm'],\n]\n\n/** The package manager that invoked this process, from `npm_config_user_agent`. */\nfunction pmFromUserAgent(ua: string | undefined): PackageManager | undefined {\n if (!ua) return undefined\n const name = ua.split('/')[0]\n return isPackageManager(name) ? name : undefined\n}\n\n/** The `packageManager` corepack field of a package.json, if it names a known PM. */\nfunction pmFromPackageJson(dir: string): PackageManager | undefined {\n try {\n const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as {\n packageManager?: string\n }\n const name = pkg.packageManager?.split('@')[0]\n return isPackageManager(name) ? name : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Detect the package manager in use. Precedence, highest first:\n * 1. explicit override (the `--package-manager`/`--pm` flag)\n * 2. `CASCIVO_PACKAGE_MANAGER` env var\n * 3. `npm_config_user_agent` (the PM that spawned the CLI, e.g. `pnpm dlx`)\n * 4. an upward walk from `cwd` for a lock file or `packageManager` field —\n * this is what makes detection work inside a workspace, where the lock\n * file lives at the repo root, not in the app subdirectory the user runs\n * the CLI from. The walk stops after a directory containing `.git`.\n * 5. default `npm`.\n */\nexport function detectPackageManager(\n cwd: string = process.cwd(),\n opts: { override?: string; env?: NodeJS.ProcessEnv } = {},\n): PackageManager {\n const env = opts.env ?? process.env\n\n if (isPackageManager(opts.override)) return opts.override\n\n const envPm = env.CASCIVO_PACKAGE_MANAGER\n if (isPackageManager(envPm)) return envPm\n\n const uaPm = pmFromUserAgent(env.npm_config_user_agent)\n if (uaPm) return uaPm\n\n let current = cwd\n for (;;) {\n for (const [file, pm] of PM_LOCKFILES) {\n if (existsSync(join(current, file))) return pm\n }\n const fromField = pmFromPackageJson(current)\n if (fromField) return fromField\n // A `.git` directory marks the repo root — do not walk past it.\n if (existsSync(join(current, '.git'))) break\n const parent = dirname(current)\n if (parent === current) break\n current = parent\n }\n\n return 'npm'\n}\n\n/** The install subcommand each package manager uses to add dependencies. */\nexport function installCommand(\n pm: PackageManager,\n packages: string[],\n opts: { dev?: boolean } = {},\n): [string, string[]] {\n const verb = pm === 'npm' ? 'install' : 'add'\n const devFlag = opts.dev ? [pm === 'npm' ? '--save-dev' : '-D'] : []\n return [pm, [verb, ...devFlag, ...packages]]\n}\n\n/** Human-readable install command a user can copy-paste, e.g. `pnpm add -D cascivo`. */\nexport function installHint(\n pm: PackageManager,\n packages: string[],\n opts: { dev?: boolean } = {},\n): string {\n const [cmd, args] = installCommand(pm, packages, opts)\n return `${cmd} ${args.join(' ')}`\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { t as readFileSafe } from "./fs-m7ZvuBBm.mjs";
2
- import { i as sha256, l as findComponent, r as readLock, s as fetchRegistry, t as checkPeerVersions } from "./peer-versions-B2bCgko-.mjs";
2
+ import { c as findComponent, n as readLock, o as fetchRegistry, r as sha256 } from "./lock-CW8UuEPJ.mjs";
3
+ import { t as checkPeerVersions } from "./peer-versions-Cep8Brn3.mjs";
3
4
  //#region src/commands/drift.ts
4
5
  /**
5
6
  * `cascivo doctor --drift` — compares installed components against the
@@ -53,4 +54,4 @@ async function runDoctorDrift(config, cwd = process.cwd()) {
53
54
  //#endregion
54
55
  export { runDoctorDrift };
55
56
 
56
- //# sourceMappingURL=drift-Ciw5qeEt.mjs.map
57
+ //# sourceMappingURL=drift-qTzJ75Ds.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"drift-Ciw5qeEt.mjs","names":[],"sources":["../src/commands/drift.ts"],"sourcesContent":["import type { CascadeConfig } from '../utils/config.js'\nimport { readFileSafe } from '../utils/fs.js'\nimport { readLock, sha256 } from '../utils/lock.js'\nimport { checkPeerVersions } from '../utils/peer-versions.js'\nimport { fetchRegistry, findComponent } from '../utils/registry.js'\n\n/**\n * `cascivo doctor --drift` — compares installed components against the\n * registry. Two drift classes:\n *\n * 1. Local-edit drift: an installed file's content no longer matches what\n * was copied at install time (hand edits, or deleted after install).\n * 2. Peer-version drift: the currently-registered component source needs a\n * newer `@cascivo/*` peer package (per `peerVersions`) than what's\n * actually installed in node_modules — the dashboard-feedback failure\n * mode (DataTable referencing an i18n builtin key an older published\n * @cascivo/i18n build doesn't have).\n */\nexport async function runDoctorDrift(\n config: CascadeConfig,\n cwd: string = process.cwd(),\n): Promise<void> {\n const lock = await readLock(cwd)\n if (!lock || Object.keys(lock.items).length === 0) {\n console.log('No installed components found in cascivo.lock.')\n return\n }\n\n const registry = await fetchRegistry(config.registry)\n let driftCount = 0\n\n for (const [name, entry] of Object.entries(lock.items)) {\n const current = findComponent(registry, name)\n if (!current) continue\n\n for (const [path, lockedHash] of Object.entries(entry.files)) {\n const content = await readFileSafe(path)\n if (content === null) {\n console.log(`${name}: ${path} is missing (installed, then deleted)`)\n driftCount++\n continue\n }\n if (sha256(content) !== lockedHash) {\n console.log(`${name}: ${path} has local edits (differs from the version installed)`)\n driftCount++\n }\n }\n\n if (current.peerVersions) {\n const violations = await checkPeerVersions(cwd, current.peerVersions)\n for (const v of violations) {\n const installedDesc = v.installed ? `${v.installed} is installed` : 'it is not installed'\n console.log(`${name}: needs ${v.pkg} ${v.required}, but ${installedDesc}.`)\n driftCount++\n }\n }\n }\n\n if (driftCount > 0) {\n console.log(`\\n${driftCount} drift issue(s) found.`)\n process.exitCode = 1\n } else {\n console.log('No drift detected — installed components match the registry.')\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,eAAsB,eACpB,QACA,MAAc,QAAQ,IAAI,GACX;CACf,MAAM,OAAO,MAAM,SAAS,GAAG;CAC/B,IAAI,CAAC,QAAQ,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC,WAAW,GAAG;EACjD,QAAQ,IAAI,gDAAgD;EAC5D;CACF;CAEA,MAAM,WAAW,MAAM,cAAc,OAAO,QAAQ;CACpD,IAAI,aAAa;CAEjB,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,KAAK,KAAK,GAAG;EACtD,MAAM,UAAU,cAAc,UAAU,IAAI;EAC5C,IAAI,CAAC,SAAS;EAEd,KAAK,MAAM,CAAC,MAAM,eAAe,OAAO,QAAQ,MAAM,KAAK,GAAG;GAC5D,MAAM,UAAU,MAAM,aAAa,IAAI;GACvC,IAAI,YAAY,MAAM;IACpB,QAAQ,IAAI,GAAG,KAAK,IAAI,KAAK,sCAAsC;IACnE;IACA;GACF;GACA,IAAI,OAAO,OAAO,MAAM,YAAY;IAClC,QAAQ,IAAI,GAAG,KAAK,IAAI,KAAK,sDAAsD;IACnF;GACF;EACF;EAEA,IAAI,QAAQ,cAAc;GACxB,MAAM,aAAa,MAAM,kBAAkB,KAAK,QAAQ,YAAY;GACpE,KAAK,MAAM,KAAK,YAAY;IAC1B,MAAM,gBAAgB,EAAE,YAAY,GAAG,EAAE,UAAU,iBAAiB;IACpE,QAAQ,IAAI,GAAG,KAAK,UAAU,EAAE,IAAI,GAAG,EAAE,SAAS,QAAQ,cAAc,EAAE;IAC1E;GACF;EACF;CACF;CAEA,IAAI,aAAa,GAAG;EAClB,QAAQ,IAAI,KAAK,WAAW,uBAAuB;EACnD,QAAQ,WAAW;CACrB,OACE,QAAQ,IAAI,8DAA8D;AAE9E"}
1
+ {"version":3,"file":"drift-qTzJ75Ds.mjs","names":[],"sources":["../src/commands/drift.ts"],"sourcesContent":["import type { CascadeConfig } from '../utils/config.js'\nimport { readFileSafe } from '../utils/fs.js'\nimport { readLock, sha256 } from '../utils/lock.js'\nimport { checkPeerVersions } from '../utils/peer-versions.js'\nimport { fetchRegistry, findComponent } from '../utils/registry.js'\n\n/**\n * `cascivo doctor --drift` — compares installed components against the\n * registry. Two drift classes:\n *\n * 1. Local-edit drift: an installed file's content no longer matches what\n * was copied at install time (hand edits, or deleted after install).\n * 2. Peer-version drift: the currently-registered component source needs a\n * newer `@cascivo/*` peer package (per `peerVersions`) than what's\n * actually installed in node_modules — the dashboard-feedback failure\n * mode (DataTable referencing an i18n builtin key an older published\n * @cascivo/i18n build doesn't have).\n */\nexport async function runDoctorDrift(\n config: CascadeConfig,\n cwd: string = process.cwd(),\n): Promise<void> {\n const lock = await readLock(cwd)\n if (!lock || Object.keys(lock.items).length === 0) {\n console.log('No installed components found in cascivo.lock.')\n return\n }\n\n const registry = await fetchRegistry(config.registry)\n let driftCount = 0\n\n for (const [name, entry] of Object.entries(lock.items)) {\n const current = findComponent(registry, name)\n if (!current) continue\n\n for (const [path, lockedHash] of Object.entries(entry.files)) {\n const content = await readFileSafe(path)\n if (content === null) {\n console.log(`${name}: ${path} is missing (installed, then deleted)`)\n driftCount++\n continue\n }\n if (sha256(content) !== lockedHash) {\n console.log(`${name}: ${path} has local edits (differs from the version installed)`)\n driftCount++\n }\n }\n\n if (current.peerVersions) {\n const violations = await checkPeerVersions(cwd, current.peerVersions)\n for (const v of violations) {\n const installedDesc = v.installed ? `${v.installed} is installed` : 'it is not installed'\n console.log(`${name}: needs ${v.pkg} ${v.required}, but ${installedDesc}.`)\n driftCount++\n }\n }\n }\n\n if (driftCount > 0) {\n console.log(`\\n${driftCount} drift issue(s) found.`)\n process.exitCode = 1\n } else {\n console.log('No drift detected — installed components match the registry.')\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAkBA,eAAsB,eACpB,QACA,MAAc,QAAQ,IAAI,GACX;CACf,MAAM,OAAO,MAAM,SAAS,GAAG;CAC/B,IAAI,CAAC,QAAQ,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC,WAAW,GAAG;EACjD,QAAQ,IAAI,gDAAgD;EAC5D;CACF;CAEA,MAAM,WAAW,MAAM,cAAc,OAAO,QAAQ;CACpD,IAAI,aAAa;CAEjB,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,KAAK,KAAK,GAAG;EACtD,MAAM,UAAU,cAAc,UAAU,IAAI;EAC5C,IAAI,CAAC,SAAS;EAEd,KAAK,MAAM,CAAC,MAAM,eAAe,OAAO,QAAQ,MAAM,KAAK,GAAG;GAC5D,MAAM,UAAU,MAAM,aAAa,IAAI;GACvC,IAAI,YAAY,MAAM;IACpB,QAAQ,IAAI,GAAG,KAAK,IAAI,KAAK,sCAAsC;IACnE;IACA;GACF;GACA,IAAI,OAAO,OAAO,MAAM,YAAY;IAClC,QAAQ,IAAI,GAAG,KAAK,IAAI,KAAK,sDAAsD;IACnF;GACF;EACF;EAEA,IAAI,QAAQ,cAAc;GACxB,MAAM,aAAa,MAAM,kBAAkB,KAAK,QAAQ,YAAY;GACpE,KAAK,MAAM,KAAK,YAAY;IAC1B,MAAM,gBAAgB,EAAE,YAAY,GAAG,EAAE,UAAU,iBAAiB;IACpE,QAAQ,IAAI,GAAG,KAAK,UAAU,EAAE,IAAI,GAAG,EAAE,SAAS,QAAQ,cAAc,EAAE;IAC1E;GACF;EACF;CACF;CAEA,IAAI,aAAa,GAAG;EAClB,QAAQ,IAAI,KAAK,WAAW,uBAAuB;EACnD,QAAQ,WAAW;CACrB,OACE,QAAQ,IAAI,8DAA8D;AAE9E"}
package/dist/index.mjs CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { a as detectPackageManager, c as isPackageManager, l as loadConfig, n as DEFAULT_CONFIG$1, o as installCommand, r as THEMES, s as installHint, t as CASCIVO_HOST, u as __exportAll } from "./config-CYlTGdmE.mjs";
2
+ import { a as detectPackageManager, c as isPackageManager, l as loadConfig, n as DEFAULT_CONFIG$1, o as installCommand, r as THEMES, s as installHint, t as CASCIVO_HOST, u as __exportAll } from "./config-C6GdrbvF.mjs";
3
3
  import { n as resolveOutputPath, r as writeFileSafe, t as readFileSafe } from "./fs-m7ZvuBBm.mjs";
4
4
  import { r as fetchTextRetry, t as fetchJson } from "./http-CJZ5W0Fa.mjs";
5
- import { a as updateLockEntry, c as fileName, i as sha256, l as findComponent, n as createLock, o as writeLock, r as readLock, s as fetchRegistry, t as checkPeerVersions } from "./peer-versions-B2bCgko-.mjs";
5
+ import { a as writeLock, c as findComponent, i as updateLockEntry, n as readLock, o as fetchRegistry, r as sha256, s as fileName, t as createLock } from "./lock-CW8UuEPJ.mjs";
6
+ import { n as readInstalledPackageVersion, r as compareVersions, t as checkPeerVersions } from "./peer-versions-Cep8Brn3.mjs";
6
7
  import { createRequire } from "node:module";
7
8
  import { argv, stdin, stdout } from "node:process";
8
9
  import { pathToFileURL } from "node:url";
@@ -937,7 +938,7 @@ async function create(args, cwd = process.cwd()) {
937
938
  const templateSpec = flagValue(args, "template");
938
939
  if (templateSpec) {
939
940
  const { add } = await Promise.resolve().then(() => add_exports);
940
- const { loadConfig } = await import("./config-CYlTGdmE.mjs").then((n) => n.i);
941
+ const { loadConfig } = await import("./config-C6GdrbvF.mjs").then((n) => n.i);
941
942
  console.log(`\nInstalling template "${templateSpec}"…`);
942
943
  await add([templateSpec], await loadConfig(), {
943
944
  cwd: targetDir,
@@ -956,6 +957,8 @@ async function create(args, cwd = process.cwd()) {
956
957
  //#region src/commands/doctor.ts
957
958
  var doctor_exports = /* @__PURE__ */ __exportAll({
958
959
  checkProjectDependencies: () => checkProjectDependencies,
960
+ checkSignalsCompat: () => checkSignalsCompat,
961
+ checkSsrConfig: () => checkSsrConfig,
959
962
  isAdopterProject: () => isAdopterProject,
960
963
  runDoctor: () => runDoctor,
961
964
  stripCommentsAndStrings: () => stripCommentsAndStrings
@@ -1011,6 +1014,85 @@ function checkProjectDependencies(cwd) {
1011
1014
  });
1012
1015
  return findings;
1013
1016
  }
1017
+ /**
1018
+ * Checks the installed `@preact/signals-react` against the installed React.
1019
+ * React 19 removed the `__SECRET_INTERNALS…` export that signals-react 2.x
1020
+ * imports, so a 2.x runtime on React 19 dies with a `SyntaxError` at module
1021
+ * evaluation (the 2026-07-20 report's blocker #2). cascivo peer-depends on
1022
+ * `>=3.0.0`, but a lockfile carried over from an earlier install can still pin
1023
+ * 2.x — this turns that into a diagnosed condition with a fix. Returns null when
1024
+ * either package is absent/unreadable (nothing reliable to advise on) or the
1025
+ * pairing is fine.
1026
+ */
1027
+ async function checkSignalsCompat(cwd) {
1028
+ const [signals, react] = await Promise.all([readInstalledPackageVersion(cwd, "@preact/signals-react"), readInstalledPackageVersion(cwd, "react")]);
1029
+ if (signals === null) return null;
1030
+ let signalsBelow3;
1031
+ try {
1032
+ signalsBelow3 = compareVersions(signals, "3.0.0") < 0;
1033
+ } catch {
1034
+ return null;
1035
+ }
1036
+ if (!signalsBelow3) return null;
1037
+ const hint = installHint(detectPackageManager(cwd), ["@preact/signals-react@^3"]);
1038
+ let reactMajor = null;
1039
+ if (react !== null) {
1040
+ const m = /^(\d+)\./.exec(react.trim());
1041
+ reactMajor = m ? Number(m[1]) : null;
1042
+ }
1043
+ if (reactMajor !== null && reactMajor >= 19) return {
1044
+ severity: "error",
1045
+ detail: `@preact/signals-react ${signals} cannot run on React ${react} — React 19 removed an internal signals 2.x imports, so it fails at module load. Upgrade to signals-react 3.x.`,
1046
+ hint
1047
+ };
1048
+ return {
1049
+ severity: "warning",
1050
+ detail: `@preact/signals-react ${signals} is below the required 3.x floor. It works on React 18 today but breaks the moment you move to React 19; upgrade now.`,
1051
+ hint
1052
+ };
1053
+ }
1054
+ /** Vite SSR frameworks whose default setup needs `ssr.noExternal` for cascivo. */
1055
+ const VITE_SSR_MARKERS = [
1056
+ "@tanstack/react-start",
1057
+ "vite-ssr",
1058
+ "@remix-run/dev"
1059
+ ];
1060
+ const VITE_CONFIG_FILES = [
1061
+ "vite.config.ts",
1062
+ "vite.config.js",
1063
+ "vite.config.mjs",
1064
+ "vite.config.mts"
1065
+ ];
1066
+ /**
1067
+ * Advisory: on a Vite SSR framework, cascivo's per-component `.css` side-effect
1068
+ * imports crash a bare server-side ESM loader unless the packages are marked
1069
+ * `ssr.noExternal` (or the `cascivoSsr()` plugin is used). This warns when a known
1070
+ * Vite SSR framework is present but no vite config mentions either — the exact
1071
+ * cliff the 2026-07-20 report hit (blocker #1). A text match, not a gate. Returns
1072
+ * null when there's no Vite SSR framework or the config already handles it.
1073
+ */
1074
+ function checkSsrConfig(cwd) {
1075
+ let deps = {};
1076
+ try {
1077
+ const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
1078
+ deps = {
1079
+ ...pkg.dependencies,
1080
+ ...pkg.devDependencies
1081
+ };
1082
+ } catch {
1083
+ return null;
1084
+ }
1085
+ const framework = VITE_SSR_MARKERS.find((m) => deps[m] !== void 0);
1086
+ if (framework === void 0) return null;
1087
+ for (const file of VITE_CONFIG_FILES) {
1088
+ const path = join(cwd, file);
1089
+ if (!existsSync(path)) continue;
1090
+ const config = readFileSync(path, "utf8");
1091
+ if (/noExternal/.test(config) && /@cascivo/.test(config)) return null;
1092
+ if (/cascivoSsr/.test(config)) return null;
1093
+ }
1094
+ return `${framework} is a Vite SSR framework, but no vite config marks the cascivo packages ssr.noExternal — an unconfigured SSR build crashes with \`Unknown file extension ".css"\`. Add \`ssr: { noExternal: [/^@cascivo\\//] }\` (or the cascivoSsr() plugin from @cascivo/vite-plugin). Recipe: https://cascivo.com/docs/using-with-vite-ssr.md`;
1095
+ }
1014
1096
  const BANNED_HOOKS = [
1015
1097
  "useState",
1016
1098
  "useEffect",
@@ -2436,20 +2518,26 @@ async function run(args) {
2436
2518
  case "doctor": {
2437
2519
  const ci = rest.includes("--ci");
2438
2520
  if (rest.includes("--drift")) {
2439
- const { runDoctorDrift } = await import("./drift-Ciw5qeEt.mjs");
2521
+ const { runDoctorDrift } = await import("./drift-qTzJ75Ds.mjs");
2440
2522
  await runDoctorDrift(await loadConfig());
2441
2523
  } else {
2442
2524
  const cwd = process.cwd();
2443
2525
  const result = await runDoctor(cwd);
2444
- const { checkProjectDependencies, isAdopterProject } = await Promise.resolve().then(() => doctor_exports);
2445
- const deps = isAdopterProject(cwd) ? checkProjectDependencies(cwd) : [];
2526
+ const { checkProjectDependencies, checkSignalsCompat, checkSsrConfig, isAdopterProject } = await Promise.resolve().then(() => doctor_exports);
2527
+ const adopter = isAdopterProject(cwd);
2528
+ const deps = adopter ? checkProjectDependencies(cwd) : [];
2446
2529
  const missingRequired = deps.filter((d) => d.required);
2447
- if (result.passed && deps.length === 0) console.log("No violations found.");
2530
+ const signalsCompat = adopter ? await checkSignalsCompat(cwd) : null;
2531
+ const signalsError = signalsCompat?.severity === "error";
2532
+ const ssrHint = adopter ? checkSsrConfig(cwd) : null;
2533
+ if (result.passed && deps.length === 0 && signalsCompat === null && ssrHint === null) console.log("No violations found.");
2448
2534
  else {
2449
2535
  for (const v of result.violations) console.error(`[${v.rule}] ${v.detail}\n ${v.file}`);
2450
2536
  for (const d of missingRequired) console.error(`[missing-dependency] ${d.package} is not in package.json — copied cascivo source needs it. Install: ${d.hint}`);
2537
+ if (signalsCompat) (signalsError ? console.error : console.log)(`[${signalsError ? "signals-incompatible" : "signals-outdated"}] ${signalsCompat.detail} Upgrade: ${signalsCompat.hint}`);
2538
+ if (ssrHint) console.log(`[ssr-config] ${ssrHint}`);
2451
2539
  for (const d of deps.filter((x) => !x.required)) console.log(`[optional] ${d.package} is not installed; add it when a component or chart needs it: ${d.hint}`);
2452
- if (ci && (result.violations.length > 0 || missingRequired.length > 0)) process.exitCode = 1;
2540
+ if (ci && (result.violations.length > 0 || missingRequired.length > 0 || signalsError)) process.exitCode = 1;
2453
2541
  }
2454
2542
  }
2455
2543
  break;