rafters 0.0.69 → 0.0.70

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
@@ -12635,7 +12635,7 @@ var RegistryFileSchema = external_exports.object({
12635
12635
  devDependencies: external_exports.array(external_exports.string()).default([])
12636
12636
  // e.g., ["vitest"] - from @devDependencies JSDoc
12637
12637
  });
12638
- var RegistryItemTypeSchema = external_exports.enum(["ui", "primitive", "composite"]);
12638
+ var RegistryItemTypeSchema = external_exports.enum(["ui", "primitive", "composite", "rule"]);
12639
12639
  var RegistryItemIntelligenceSchema = external_exports.object({
12640
12640
  cognitiveLoad: external_exports.number().optional(),
12641
12641
  attentionEconomics: external_exports.string().optional(),
@@ -12668,134 +12668,94 @@ var RegistryIndexSchema = external_exports.object({
12668
12668
 
12669
12669
  // src/registry/client.ts
12670
12670
  var DEFAULT_REGISTRY_URL = "https://rafters.studio";
12671
+ var REGISTRY_SOURCE = {
12672
+ ui: { folder: "components", label: "Component" },
12673
+ primitive: { folder: "primitives", label: "Primitive" },
12674
+ composite: { folder: "composites", label: "Composite" },
12675
+ rule: { folder: "rules", label: "Rule" }
12676
+ };
12677
+ var FETCH_ORDER = Object.keys(REGISTRY_SOURCE);
12671
12678
  var RegistryClient = class {
12672
12679
  baseUrl;
12673
12680
  cache = /* @__PURE__ */ new Map();
12681
+ fetchByType;
12682
+ /** Typed fetchers -- one shared body, specialized per type. */
12683
+ fetchComponent;
12684
+ fetchPrimitive;
12685
+ fetchComposite;
12686
+ fetchRule;
12674
12687
  constructor(baseUrl = DEFAULT_REGISTRY_URL) {
12675
12688
  this.baseUrl = baseUrl.replace(/\/$/, "");
12689
+ const fetchFrom = (type2) => async (name) => {
12690
+ const { folder, label } = REGISTRY_SOURCE[type2];
12691
+ const cacheKey = `${type2}:${name}`;
12692
+ const cached2 = this.cache.get(cacheKey);
12693
+ if (cached2) return cached2;
12694
+ const response = await fetch(`${this.baseUrl}/registry/${folder}/${name}.json`);
12695
+ if (response.status === 404) {
12696
+ throw new Error(`${label} "${name}" not found`);
12697
+ }
12698
+ if (!response.ok) {
12699
+ throw new Error(
12700
+ `Failed to fetch ${label.toLowerCase()} "${name}": ${response.status} ${response.statusText}`
12701
+ );
12702
+ }
12703
+ const item = RegistryItemSchema.parse(await response.json());
12704
+ this.cache.set(cacheKey, item);
12705
+ return item;
12706
+ };
12707
+ this.fetchByType = {
12708
+ ui: fetchFrom("ui"),
12709
+ primitive: fetchFrom("primitive"),
12710
+ composite: fetchFrom("composite"),
12711
+ rule: fetchFrom("rule")
12712
+ };
12713
+ this.fetchComponent = this.fetchByType.ui;
12714
+ this.fetchPrimitive = this.fetchByType.primitive;
12715
+ this.fetchComposite = this.fetchByType.composite;
12716
+ this.fetchRule = this.fetchByType.rule;
12676
12717
  }
12677
12718
  /**
12678
- * Fetch the registry index
12719
+ * Fetch the registry index.
12679
12720
  */
12680
12721
  async fetchIndex() {
12681
- const url3 = `${this.baseUrl}/registry/index.json`;
12682
- const response = await fetch(url3);
12722
+ const response = await fetch(`${this.baseUrl}/registry/index.json`);
12683
12723
  if (!response.ok) {
12684
12724
  throw new Error(`Failed to fetch registry index: ${response.status} ${response.statusText}`);
12685
12725
  }
12686
- const data = await response.json();
12687
- return RegistryIndexSchema.parse(data);
12688
- }
12689
- /**
12690
- * Fetch a component by name
12691
- */
12692
- async fetchComponent(name) {
12693
- const cacheKey = `component:${name}`;
12694
- const cached2 = this.cache.get(cacheKey);
12695
- if (cached2) {
12696
- return cached2;
12697
- }
12698
- const url3 = `${this.baseUrl}/registry/components/${name}.json`;
12699
- const response = await fetch(url3);
12700
- if (response.status === 404) {
12701
- throw new Error(`Component "${name}" not found`);
12702
- }
12703
- if (!response.ok) {
12704
- throw new Error(
12705
- `Failed to fetch component "${name}": ${response.status} ${response.statusText}`
12706
- );
12707
- }
12708
- const data = await response.json();
12709
- const item = RegistryItemSchema.parse(data);
12710
- this.cache.set(cacheKey, item);
12711
- return item;
12726
+ return RegistryIndexSchema.parse(await response.json());
12712
12727
  }
12713
12728
  /**
12714
- * Fetch a primitive by name
12715
- */
12716
- async fetchPrimitive(name) {
12717
- const cacheKey = `primitive:${name}`;
12718
- const cached2 = this.cache.get(cacheKey);
12719
- if (cached2) {
12720
- return cached2;
12721
- }
12722
- const url3 = `${this.baseUrl}/registry/primitives/${name}.json`;
12723
- const response = await fetch(url3);
12724
- if (response.status === 404) {
12725
- throw new Error(`Primitive "${name}" not found`);
12726
- }
12727
- if (!response.ok) {
12728
- throw new Error(
12729
- `Failed to fetch primitive "${name}": ${response.status} ${response.statusText}`
12730
- );
12731
- }
12732
- const data = await response.json();
12733
- const item = RegistryItemSchema.parse(data);
12734
- this.cache.set(cacheKey, item);
12735
- return item;
12736
- }
12737
- /**
12738
- * Fetch a composite by name
12739
- */
12740
- async fetchComposite(name) {
12741
- const cacheKey = `composite:${name}`;
12742
- const cached2 = this.cache.get(cacheKey);
12743
- if (cached2) {
12744
- return cached2;
12745
- }
12746
- const url3 = `${this.baseUrl}/registry/composites/${name}.json`;
12747
- const response = await fetch(url3);
12748
- if (response.status === 404) {
12749
- throw new Error(`Composite "${name}" not found`);
12750
- }
12751
- if (!response.ok) {
12752
- throw new Error(
12753
- `Failed to fetch composite "${name}": ${response.status} ${response.statusText}`
12754
- );
12755
- }
12756
- const data = await response.json();
12757
- const item = RegistryItemSchema.parse(data);
12758
- this.cache.set(cacheKey, item);
12759
- return item;
12760
- }
12761
- /**
12762
- * Fetch a registry item by name
12763
- * Tries component, then primitive, then composite
12729
+ * Fetch a registry item by name when the type is unknown.
12730
+ * Tries each source in order; a non-"not found" error (network, 5xx) aborts.
12764
12731
  */
12765
12732
  async fetchItem(name) {
12766
- try {
12767
- return await this.fetchComponent(name);
12768
- } catch (err) {
12769
- if (err instanceof Error && err.message.includes("not found")) {
12770
- try {
12771
- return await this.fetchPrimitive(name);
12772
- } catch {
12773
- try {
12774
- return await this.fetchComposite(name);
12775
- } catch {
12776
- throw err;
12777
- }
12778
- }
12733
+ for (const type2 of FETCH_ORDER) {
12734
+ try {
12735
+ return await this.fetchByType[type2](name);
12736
+ } catch (err) {
12737
+ if (err instanceof Error && err.message.includes("not found")) continue;
12738
+ throw err;
12779
12739
  }
12780
- throw err;
12781
12740
  }
12741
+ throw new Error(`"${name}" not found in registry (component, primitive, composite, or rule)`);
12782
12742
  }
12783
12743
  /**
12784
- * List all available components
12744
+ * List all available components.
12785
12745
  */
12786
12746
  async listComponents() {
12787
12747
  const index = await this.fetchIndex();
12788
12748
  return index.components.map((name) => ({ name }));
12789
12749
  }
12790
12750
  /**
12791
- * List all available composites
12751
+ * List all available composites.
12792
12752
  */
12793
12753
  async listComposites() {
12794
12754
  const index = await this.fetchIndex();
12795
12755
  return index.composites.map((name) => ({ name }));
12796
12756
  }
12797
12757
  /**
12798
- * Check if a component exists in the registry
12758
+ * Check if a component exists in the registry.
12799
12759
  */
12800
12760
  async componentExists(name) {
12801
12761
  try {
@@ -12806,7 +12766,7 @@ var RegistryClient = class {
12806
12766
  }
12807
12767
  }
12808
12768
  /**
12809
- * Check if a primitive exists in the registry
12769
+ * Check if a primitive exists in the registry.
12810
12770
  */
12811
12771
  async primitiveExists(name) {
12812
12772
  try {
@@ -12817,20 +12777,17 @@ var RegistryClient = class {
12817
12777
  }
12818
12778
  }
12819
12779
  /**
12820
- * Resolve all dependencies for a component recursively
12821
- * Returns items in installation order (dependencies first)
12780
+ * Resolve all dependencies for an item recursively.
12781
+ * Returns items in installation order (dependencies first).
12822
12782
  */
12823
12783
  async resolveDependencies(name, resolved = /* @__PURE__ */ new Set()) {
12824
- if (resolved.has(name)) {
12825
- return [];
12826
- }
12784
+ if (resolved.has(name)) return [];
12827
12785
  const item = await this.fetchItem(name);
12828
12786
  resolved.add(name);
12829
12787
  const deps = [];
12830
12788
  for (const dep of item.primitives) {
12831
12789
  if (!resolved.has(dep)) {
12832
- const depItems = await this.resolveDependencies(dep, resolved);
12833
- deps.push(...depItems);
12790
+ deps.push(...await this.resolveDependencies(dep, resolved));
12834
12791
  }
12835
12792
  }
12836
12793
  deps.push(item);
@@ -12954,7 +12911,7 @@ var FRAMEWORK_SPECS = {
12954
12911
  components: "components/ui",
12955
12912
  primitives: "lib/primitives",
12956
12913
  composites: "composites",
12957
- rules: "rules"
12914
+ rules: "lib/rules"
12958
12915
  }
12959
12916
  },
12960
12917
  vite: {
@@ -12964,7 +12921,7 @@ var FRAMEWORK_SPECS = {
12964
12921
  components: "src/components/ui",
12965
12922
  primitives: "src/lib/primitives",
12966
12923
  composites: "src/composites",
12967
- rules: "src/rules"
12924
+ rules: "src/lib/rules"
12968
12925
  }
12969
12926
  },
12970
12927
  remix: {
@@ -12974,7 +12931,7 @@ var FRAMEWORK_SPECS = {
12974
12931
  components: "app/components/ui",
12975
12932
  primitives: "app/lib/primitives",
12976
12933
  composites: "app/composites",
12977
- rules: "app/rules"
12934
+ rules: "app/lib/rules"
12978
12935
  }
12979
12936
  },
12980
12937
  "react-router": {
@@ -12984,7 +12941,7 @@ var FRAMEWORK_SPECS = {
12984
12941
  components: "app/components/ui",
12985
12942
  primitives: "app/lib/primitives",
12986
12943
  composites: "app/composites",
12987
- rules: "app/rules"
12944
+ rules: "app/lib/rules"
12988
12945
  }
12989
12946
  },
12990
12947
  astro: {
@@ -12994,7 +12951,7 @@ var FRAMEWORK_SPECS = {
12994
12951
  components: "src/components/ui",
12995
12952
  primitives: "src/lib/primitives",
12996
12953
  composites: "src/composites",
12997
- rules: "src/rules"
12954
+ rules: "src/lib/rules"
12998
12955
  }
12999
12956
  },
13000
12957
  wc: {
@@ -13004,7 +12961,7 @@ var FRAMEWORK_SPECS = {
13004
12961
  components: "src/components/ui",
13005
12962
  primitives: "src/lib/primitives",
13006
12963
  composites: "src/composites",
13007
- rules: "src/rules"
12964
+ rules: "src/lib/rules"
13008
12965
  }
13009
12966
  },
13010
12967
  vanilla: {
@@ -13014,7 +12971,7 @@ var FRAMEWORK_SPECS = {
13014
12971
  components: "src/components/ui",
13015
12972
  primitives: "src/lib/primitives",
13016
12973
  composites: "src/composites",
13017
- rules: "src/rules"
12974
+ rules: "src/lib/rules"
13018
12975
  }
13019
12976
  },
13020
12977
  unknown: {
@@ -13023,7 +12980,7 @@ var FRAMEWORK_SPECS = {
13023
12980
  components: "components/ui",
13024
12981
  primitives: "lib/primitives",
13025
12982
  composites: "composites",
13026
- rules: "rules"
12983
+ rules: "lib/rules"
13027
12984
  }
13028
12985
  }
13029
12986
  };
@@ -13671,6 +13628,9 @@ async function loadConfig(cwd) {
13671
13628
  return null;
13672
13629
  }
13673
13630
  }
13631
+ function resolveRegistryUrl(options, config2) {
13632
+ return options.registryUrl ?? config2?.registryUrl;
13633
+ }
13674
13634
  async function saveConfig(cwd, config2) {
13675
13635
  const paths = getRaftersPaths(cwd);
13676
13636
  await writeFile(paths.config, JSON.stringify(config2, null, 2));
@@ -13709,16 +13669,26 @@ function selectFilesForFramework(files, target) {
13709
13669
  }
13710
13670
  return { files, fallback: false };
13711
13671
  }
13672
+ var TARGET_GATED_COMPOSITE_FILES = [
13673
+ { suffix: "Composite.astro", target: "astro" }
13674
+ ];
13675
+ function selectCompositeFiles(files, target) {
13676
+ return files.filter((file2) => {
13677
+ const gate = TARGET_GATED_COMPOSITE_FILES.find((g2) => file2.path.endsWith(g2.suffix));
13678
+ return gate ? gate.target === target : true;
13679
+ });
13680
+ }
13712
13681
  var FOLDER_NAMES = /* @__PURE__ */ new Set(["composites"]);
13713
13682
  function isAlreadyInstalled(config2, item) {
13714
13683
  if (!config2?.installed) return false;
13715
- if (item.type === "ui") {
13716
- return config2.installed.components.includes(item.name);
13717
- }
13718
- if (item.type === "composite") {
13719
- return (config2.installed.composites ?? []).includes(item.name);
13720
- }
13721
- return config2.installed.primitives.includes(item.name);
13684
+ const { components, primitives, composites: composites2, rules } = config2.installed;
13685
+ const bucketByType = {
13686
+ ui: components,
13687
+ primitive: primitives,
13688
+ composite: composites2 ?? [],
13689
+ rule: rules ?? []
13690
+ };
13691
+ return bucketByType[item.type].includes(item.name);
13722
13692
  }
13723
13693
  function isInstalledOnDisk(config2, item, cwd) {
13724
13694
  if (!isAlreadyInstalled(config2, item)) return false;
@@ -13735,8 +13705,14 @@ function trackInstalled(config2, items) {
13735
13705
  const installed = config2.installed;
13736
13706
  if (!installed.composites) installed.composites = [];
13737
13707
  if (!installed.rules) installed.rules = [];
13708
+ const bucketByType = {
13709
+ ui: installed.components,
13710
+ primitive: installed.primitives,
13711
+ composite: installed.composites,
13712
+ rule: installed.rules
13713
+ };
13738
13714
  for (const item of items) {
13739
- const bucket = item.type === "ui" ? installed.components : item.type === "composite" ? installed.composites : installed.primitives;
13715
+ const bucket = bucketByType[item.type];
13740
13716
  if (!bucket.includes(item.name)) bucket.push(item.name);
13741
13717
  }
13742
13718
  installed.components.sort();
@@ -13753,7 +13729,7 @@ function transformPath(registryPath, config2, cwd) {
13753
13729
  ["components/ui/", config2.componentsPath, "components/ui"],
13754
13730
  ["lib/primitives/", config2.primitivesPath, "lib/primitives"],
13755
13731
  ["composites/", config2.compositesPath, "composites"],
13756
- ["rules/", config2.rulesPath, "rules"]
13732
+ ["rules/", config2.rulesPath, "lib/rules"]
13757
13733
  ];
13758
13734
  for (const [prefix, field, fallback] of replacements) {
13759
13735
  if (registryPath.startsWith(prefix)) {
@@ -13815,6 +13791,8 @@ async function installItem(cwd, item, options, config2) {
13815
13791
  message: `No ${targetToExtension(target)} version available for ${item.name}. Installing React version.`
13816
13792
  });
13817
13793
  }
13794
+ } else if (item.type === "composite") {
13795
+ filesToInstall = selectCompositeFiles(item.files, getComponentTarget(config2));
13818
13796
  }
13819
13797
  for (const file2 of filesToInstall) {
13820
13798
  const projectPath2 = transformPath(file2.path, config2, cwd);
@@ -13846,7 +13824,9 @@ async function installItem(cwd, item, options, config2) {
13846
13824
  async function add(componentArgs, options) {
13847
13825
  setAgentMode(options.agent ?? false);
13848
13826
  let components = componentArgs;
13849
- const client = new RegistryClient(options.registryUrl);
13827
+ const cwd = process.cwd();
13828
+ const config2 = await loadConfig(cwd);
13829
+ const client = new RegistryClient(resolveRegistryUrl(options, config2));
13850
13830
  let folder;
13851
13831
  const firstArg = components[0];
13852
13832
  if (firstArg && FOLDER_NAMES.has(firstArg)) {
@@ -13876,14 +13856,12 @@ async function add(componentArgs, options) {
13876
13856
  }
13877
13857
  return;
13878
13858
  }
13879
- const cwd = process.cwd();
13880
13859
  const initialized = await isInitialized(cwd);
13881
13860
  if (!initialized) {
13882
13861
  error46("Project not initialized. Run `rafters init` first.");
13883
13862
  process.exitCode = 1;
13884
13863
  return;
13885
13864
  }
13886
- const config2 = await loadConfig(cwd);
13887
13865
  if (options.update) {
13888
13866
  options.overwrite = true;
13889
13867
  }
@@ -13988,6 +13966,7 @@ async function add(componentArgs, options) {
13988
13966
  }
13989
13967
  if (result.skipped && !result.installed) {
13990
13968
  skipped.push(item.name);
13969
+ installedItems.push(item);
13991
13970
  }
13992
13971
  } catch (err) {
13993
13972
  if (err instanceof Error) {
@@ -14034,7 +14013,7 @@ async function add(componentArgs, options) {
14034
14013
  componentsPath: "components/ui",
14035
14014
  primitivesPath: "lib/primitives",
14036
14015
  compositesPath: "composites",
14037
- rulesPath: "rules",
14016
+ rulesPath: "lib/rules",
14038
14017
  cssPath: null,
14039
14018
  shadcn: false,
14040
14019
  exports: DEFAULT_EXPORTS,
@@ -21167,12 +21146,23 @@ var SemanticSelectionError = class extends Error {
21167
21146
  this.name = "SemanticSelectionError";
21168
21147
  }
21169
21148
  };
21170
- var STATE_USES = ["hover", "active", "focus", "disabled"];
21149
+ var STATE_USES = [
21150
+ "hover",
21151
+ "active",
21152
+ "focus",
21153
+ "disabled",
21154
+ "border",
21155
+ "ring",
21156
+ "subtle"
21157
+ ];
21171
21158
  var STATE_RANK_STEP = {
21172
21159
  hover: (rank, _l, s) => rank + s,
21173
21160
  active: (rank, _l, s) => rank + 2 * s,
21174
21161
  focus: (rank, _l, s) => rank + s,
21175
- disabled: (_rank, ladder) => closestRankTo(ladder, 5)
21162
+ disabled: (_rank, ladder) => closestRankTo(ladder, 5),
21163
+ border: (rank, _l, s) => rank + 2 * s,
21164
+ ring: (rank) => rank,
21165
+ subtle: (rank, _l, s) => rank - 2 * s
21176
21166
  };
21177
21167
  function partnerForBase(pairs, base) {
21178
21168
  if (!pairs) return void 0;
@@ -22868,7 +22858,7 @@ async function registryToCompiled(registry2, options = {}) {
22868
22858
  const themeCss = registryToTailwind(registry2, { includeImport });
22869
22859
  const { execFileSync } = await import("child_process");
22870
22860
  const { mkdtempSync, writeFileSync: writeFileSync2, readFileSync: readFileSync3, rmSync } = await import("fs");
22871
- const { join: join13, dirname: dirname4 } = await import("path");
22861
+ const { join: join14, dirname: dirname4 } = await import("path");
22872
22862
  const { createRequire: createRequire2 } = await import("module");
22873
22863
  const require2 = createRequire2(import.meta.url);
22874
22864
  let pkgDir;
@@ -22878,10 +22868,10 @@ async function registryToCompiled(registry2, options = {}) {
22878
22868
  } catch {
22879
22869
  throw new Error("Failed to resolve @tailwindcss/cli");
22880
22870
  }
22881
- const binPath = join13(pkgDir, "dist", "index.mjs");
22882
- const tempDir = mkdtempSync(join13(pkgDir, ".tmp-compile-"));
22883
- const tempInput = join13(tempDir, "input.css");
22884
- const tempOutput = join13(tempDir, "output.css");
22871
+ const binPath = join14(pkgDir, "dist", "index.mjs");
22872
+ const tempDir = mkdtempSync(join14(pkgDir, ".tmp-compile-"));
22873
+ const tempInput = join14(tempDir, "input.css");
22874
+ const tempOutput = join14(tempDir, "output.css");
22885
22875
  try {
22886
22876
  writeFileSync2(tempInput, themeCss);
22887
22877
  const args = [binPath, "-i", tempInput, "-o", tempOutput];
@@ -24233,12 +24223,12 @@ var DEFAULT_SEMANTIC_COLOR_MAPPINGS = {
24233
24223
  never: ["Use for dividers within cards"]
24234
24224
  },
24235
24225
  panel: {
24236
- light: { family: "neutral", position: "100" },
24226
+ light: { family: "neutral", position: "200" },
24237
24227
  dark: { family: "neutral", position: "800" },
24238
- meaning: "Elevated panel surface -- one level above the page",
24228
+ meaning: "Persistent elevated chrome -- headers, docks, fixed toolbars",
24239
24229
  contexts: ["panels", "toolbars", "headers", "docks"],
24240
24230
  do: ["Use for persistent elevated chrome", "Pair with panel-foreground"],
24241
- never: ["Use for page-level backgrounds", "Stack directly on card without a border"]
24231
+ never: ["Use for page-level backgrounds", "Use for transient surfaces"]
24242
24232
  },
24243
24233
  "panel-foreground": {
24244
24234
  light: { family: "neutral", position: "950" },
@@ -24291,12 +24281,12 @@ var DEFAULT_SEMANTIC_COLOR_MAPPINGS = {
24291
24281
  },
24292
24282
  // Generic surface
24293
24283
  surface: {
24294
- light: { family: "neutral", position: "50" },
24284
+ light: { family: "neutral", position: "100" },
24295
24285
  dark: { family: "neutral", position: "900" },
24296
- meaning: "Elevated surface background",
24297
- contexts: ["elevated-elements"],
24298
- do: ["Use for elevated surfaces"],
24299
- never: ["Use for page background"]
24286
+ meaning: "Base chrome surface -- toolbars, app shell, UI frame",
24287
+ contexts: ["chrome", "app-shell", "toolbars"],
24288
+ do: ["Use for application chrome"],
24289
+ never: ["Use for page background", "Use for content containers"]
24300
24290
  },
24301
24291
  "surface-foreground": {
24302
24292
  light: { family: "neutral", position: "950" },
@@ -25381,9 +25371,9 @@ var DEFAULT_SEMANTIC_COLOR_MAPPINGS = {
25381
25371
  // SIDEBAR TOKENS (shadcn compatible + extended)
25382
25372
  // ============================================================================
25383
25373
  sidebar: {
25384
- light: { family: "neutral", position: "50" },
25374
+ light: { family: "neutral", position: "100" },
25385
25375
  dark: { family: "neutral", position: "900" },
25386
- meaning: "Sidebar background",
25376
+ meaning: "Sidebar background -- almost on the surface",
25387
25377
  contexts: ["navigation-sidebar", "side-panels"],
25388
25378
  do: ["Use for sidebar backgrounds"],
25389
25379
  never: ["Use for main content areas"]
@@ -27043,7 +27033,7 @@ function generateRadiusTokens(config2, radiusDefs) {
27043
27033
 
27044
27034
  // ../design-tokens/src/generators/semantic.ts
27045
27035
  var FOREGROUND_SUFFIXES = ["-foreground", "-text", "-contrast"];
27046
- var STATE_SUFFIXES = ["-hover", "-active", "-focus", "-disabled"];
27036
+ var STATE_SUFFIXES = STATE_USES.map((s) => `-${s}`);
27047
27037
  function toColorRef(mapping) {
27048
27038
  return { family: mapping.light.family, position: mapping.light.position };
27049
27039
  }
@@ -28744,7 +28734,7 @@ var scalePlugin = definePlugin({
28744
28734
  });
28745
28735
 
28746
28736
  // ../design-tokens/src/plugins/state.ts
28747
- var StateTypeSchema = external_exports.enum(["hover", "active", "focus", "disabled"]);
28737
+ var StateTypeSchema = external_exports.enum(STATE_USES);
28748
28738
  var StateInputSchema = external_exports.object({
28749
28739
  from: external_exports.string(),
28750
28740
  stateType: StateTypeSchema
@@ -29618,7 +29608,7 @@ async function init(options) {
29618
29608
 
29619
29609
  // src/commands/mcp.ts
29620
29610
  import { existsSync as existsSync6 } from "fs";
29621
- import { basename as basename2, join as join11, resolve as resolve4 } from "path";
29611
+ import { basename as basename2, join as join12, resolve as resolve4 } from "path";
29622
29612
 
29623
29613
  // src/mcp/server.ts
29624
29614
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -29626,8 +29616,8 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
29626
29616
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
29627
29617
 
29628
29618
  // src/mcp/tools.ts
29629
- import { readdir, readFile as readFile5 } from "fs/promises";
29630
- import { join as join10 } from "path";
29619
+ import { readFile as readFile6 } from "fs/promises";
29620
+ import { join as join11 } from "path";
29631
29621
 
29632
29622
  // ../composites/src/built-in-rules/email.ts
29633
29623
  var email3 = external_exports.string().email();
@@ -29656,6 +29646,10 @@ var AppliedRuleSchema = external_exports.union([
29656
29646
  var CompositeBlockSchema = external_exports.object({
29657
29647
  id: external_exports.string().min(1),
29658
29648
  type: external_exports.string().min(1),
29649
+ /** Signatures this block consumes / produces (rule names) -- the typed I/O edges.
29650
+ * Within a composite, an output named X feeds any block input named X. */
29651
+ input: external_exports.array(external_exports.string()).optional(),
29652
+ output: external_exports.array(external_exports.string()).optional(),
29659
29653
  content: external_exports.unknown().optional(),
29660
29654
  children: external_exports.array(external_exports.string()).optional(),
29661
29655
  parentId: external_exports.string().optional(),
@@ -29685,6 +29679,60 @@ var CompositeFileSchema = external_exports.object({
29685
29679
  blocks: external_exports.array(CompositeBlockSchema).min(1)
29686
29680
  });
29687
29681
 
29682
+ // ../composites/src/discovery.ts
29683
+ function discoverComposites(entries) {
29684
+ const registry2 = /* @__PURE__ */ new Map();
29685
+ const errors = [];
29686
+ for (const entry of entries) {
29687
+ let parsed;
29688
+ try {
29689
+ parsed = JSON.parse(entry.raw);
29690
+ } catch (e) {
29691
+ errors.push({
29692
+ source: entry.source,
29693
+ error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}`
29694
+ });
29695
+ continue;
29696
+ }
29697
+ const result = CompositeFileSchema.safeParse(parsed);
29698
+ if (!result.success) {
29699
+ errors.push({ source: entry.source, error: result.error.message });
29700
+ continue;
29701
+ }
29702
+ const { id } = result.data.manifest;
29703
+ if (registry2.has(id)) {
29704
+ errors.push({
29705
+ source: entry.source,
29706
+ error: `Duplicate composite id "${id}" (already registered)`
29707
+ });
29708
+ continue;
29709
+ }
29710
+ registry2.set(id, result.data);
29711
+ }
29712
+ reportUnresolvedReferences(registry2, errors);
29713
+ return { registry: registry2, errors };
29714
+ }
29715
+ var COMPOSITE_PREFIX = "composite:";
29716
+ function reportUnresolvedReferences(registry2, errors) {
29717
+ for (const [id, composite] of registry2) {
29718
+ const missing = /* @__PURE__ */ new Set();
29719
+ for (const block of composite.blocks) {
29720
+ if (block.type.startsWith(COMPOSITE_PREFIX)) {
29721
+ const refId = block.type.slice(COMPOSITE_PREFIX.length);
29722
+ if (!registry2.has(refId)) {
29723
+ missing.add(refId);
29724
+ }
29725
+ }
29726
+ }
29727
+ if (missing.size > 0) {
29728
+ errors.push({
29729
+ source: id,
29730
+ error: `Unresolved composite reference(s): ${[...missing].map((m3) => `composite:${m3}`).join(", ")}`
29731
+ });
29732
+ }
29733
+ }
29734
+ }
29735
+
29688
29736
  // ../ui/src/primitives/typeahead.ts
29689
29737
  function fuzzyScore(query, target) {
29690
29738
  if (query.length === 0) return 1;
@@ -29742,17 +29790,56 @@ function search(query) {
29742
29790
  // ../composites/src/to-mdx.ts
29743
29791
  var import_escape_html = __toESM(require_escape_html(), 1);
29744
29792
 
29793
+ // ../composites/src/discovery-node.ts
29794
+ import { readdir, readFile as readFile5 } from "fs/promises";
29795
+ import { join as join8 } from "path";
29796
+ var COMPOSITE_SUFFIX = ".composite.json";
29797
+ async function readDir(directory) {
29798
+ const entries = [];
29799
+ let dirents;
29800
+ try {
29801
+ dirents = await readdir(directory, { withFileTypes: true });
29802
+ } catch {
29803
+ return entries;
29804
+ }
29805
+ for (const dirent of dirents) {
29806
+ const fullPath = join8(directory, dirent.name);
29807
+ if (dirent.isDirectory()) {
29808
+ entries.push(...await readDir(fullPath));
29809
+ continue;
29810
+ }
29811
+ if (dirent.isFile() && dirent.name.endsWith(COMPOSITE_SUFFIX)) {
29812
+ const raw = await readFile5(fullPath, "utf-8");
29813
+ entries.push({ raw, source: fullPath });
29814
+ }
29815
+ }
29816
+ return entries;
29817
+ }
29818
+ function nodeFsAdapter(...directories) {
29819
+ return async () => {
29820
+ const entries = [];
29821
+ for (const directory of directories) {
29822
+ entries.push(...await readDir(directory));
29823
+ }
29824
+ return entries;
29825
+ };
29826
+ }
29827
+ async function discoverFromDirs(...directories) {
29828
+ const adapter = nodeFsAdapter(...directories);
29829
+ return discoverComposites(await adapter());
29830
+ }
29831
+
29745
29832
  // src/utils/workspaces.ts
29746
29833
  import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
29747
- import { basename, dirname as dirname3, join as join9, resolve as resolve3 } from "path";
29834
+ import { basename, dirname as dirname3, join as join10, resolve as resolve3 } from "path";
29748
29835
 
29749
29836
  // src/utils/discover.ts
29750
29837
  import { existsSync as existsSync4 } from "fs";
29751
- import { dirname as dirname2, join as join8, resolve as resolve2 } from "path";
29838
+ import { dirname as dirname2, join as join9, resolve as resolve2 } from "path";
29752
29839
  function discoverProjectRoot(startDir) {
29753
29840
  let current = resolve2(startDir);
29754
29841
  for (; ; ) {
29755
- const configPath = join8(current, ".rafters", "config.rafters.json");
29842
+ const configPath = join9(current, ".rafters", "config.rafters.json");
29756
29843
  if (existsSync4(configPath)) {
29757
29844
  return current;
29758
29845
  }
@@ -29769,14 +29856,14 @@ function findMonorepoRoot(startDir, boundary) {
29769
29856
  let current = resolve3(startDir);
29770
29857
  const stopAt = boundary ? resolve3(boundary) : null;
29771
29858
  for (; ; ) {
29772
- const pnpmWorkspace = join9(current, "pnpm-workspace.yaml");
29859
+ const pnpmWorkspace = join10(current, "pnpm-workspace.yaml");
29773
29860
  if (existsSync5(pnpmWorkspace)) {
29774
29861
  const patterns = parsePnpmWorkspaceYaml(readFileSync2(pnpmWorkspace, "utf-8"));
29775
29862
  if (patterns.length > 0) {
29776
29863
  return { root: current, patterns };
29777
29864
  }
29778
29865
  }
29779
- const pkgJson = join9(current, "package.json");
29866
+ const pkgJson = join10(current, "package.json");
29780
29867
  if (existsSync5(pkgJson)) {
29781
29868
  const patterns = parsePackageJsonWorkspaces(readFileSync2(pkgJson, "utf-8"));
29782
29869
  if (patterns.length > 0) {
@@ -29832,9 +29919,9 @@ function expandPattern(monorepoRoot, pattern) {
29832
29919
  const trimmed = pattern.replace(/\/+$/, "");
29833
29920
  if (trimmed.endsWith("/*")) {
29834
29921
  const parentRel = trimmed.slice(0, -2);
29835
- const parent = join9(monorepoRoot, parentRel);
29922
+ const parent = join10(monorepoRoot, parentRel);
29836
29923
  if (!existsSync5(parent)) return [];
29837
- return readdirSync2(parent).map((entry) => join9(parent, entry)).filter((path) => {
29924
+ return readdirSync2(parent).map((entry) => join10(parent, entry)).filter((path) => {
29838
29925
  try {
29839
29926
  return statSync2(path).isDirectory();
29840
29927
  } catch {
@@ -29842,7 +29929,7 @@ function expandPattern(monorepoRoot, pattern) {
29842
29929
  }
29843
29930
  });
29844
29931
  }
29845
- const literal2 = join9(monorepoRoot, trimmed);
29932
+ const literal2 = join10(monorepoRoot, trimmed);
29846
29933
  if (existsSync5(literal2)) {
29847
29934
  try {
29848
29935
  if (statSync2(literal2).isDirectory()) return [literal2];
@@ -29866,13 +29953,13 @@ function discoverWorkspaces(startDir = process.cwd(), options = {}) {
29866
29953
  for (const dir of expandPattern(layout.root, pattern)) {
29867
29954
  if (seen.has(dir)) continue;
29868
29955
  seen.add(dir);
29869
- const config2 = join9(dir, ".rafters", "config.rafters.json");
29956
+ const config2 = join10(dir, ".rafters", "config.rafters.json");
29870
29957
  if (existsSync5(config2)) {
29871
29958
  workspaces.push({ name: basename(dir), root: dir });
29872
29959
  }
29873
29960
  }
29874
29961
  }
29875
- const rootConfig = join9(layout.root, ".rafters", "config.rafters.json");
29962
+ const rootConfig = join10(layout.root, ".rafters", "config.rafters.json");
29876
29963
  if (existsSync5(rootConfig) && !seen.has(layout.root)) {
29877
29964
  workspaces.unshift({ name: basename(layout.root), root: layout.root });
29878
29965
  }
@@ -29924,29 +30011,6 @@ var TOOL_DEFINITIONS = [
29924
30011
  required: []
29925
30012
  }
29926
30013
  },
29927
- {
29928
- name: "rafters_rule",
29929
- description: "Query validation rules or create new ones. Rules are named validation patterns (required, email, password, etc.) that composites can apply.",
29930
- inputSchema: {
29931
- type: "object",
29932
- properties: {
29933
- ...WORKSPACE_PARAM,
29934
- name: { type: "string", description: "Get a specific rule by name" },
29935
- query: { type: "string", description: "Search rules by name/description" },
29936
- create: {
29937
- type: "object",
29938
- properties: {
29939
- name: { type: "string", description: "Rule name (kebab-case)" },
29940
- description: { type: "string", description: "What this rule validates" },
29941
- zodSchema: { type: "string", description: "Zod schema as a string" }
29942
- },
29943
- required: ["name", "description", "zodSchema"],
29944
- description: "Create a new rule (provide all three fields)"
29945
- }
29946
- },
29947
- required: []
29948
- }
29949
- },
29950
30014
  {
29951
30015
  name: "rafters_pattern",
29952
30016
  description: 'Get design pattern guidance by querying composites. Search by what the pattern solves (e.g., "authentication", "data entry", "navigation") to get do/never rules, cognitive load, and designer intent.',
@@ -29999,20 +30063,22 @@ var RaftersToolHandler = class {
29999
30063
  return this.handleWorkspaces();
30000
30064
  case "rafters_composite":
30001
30065
  return this.handleComposite(args);
30002
- case "rafters_rule":
30003
- return this.handleRule(args);
30004
30066
  case "rafters_pattern":
30005
30067
  return this.handlePattern(args);
30006
30068
  case "rafters_component":
30007
30069
  return this.handleComponent(args.name);
30008
- default: {
30009
- const suggestion = name === "rafters_onboard" ? "rafters_onboard was removed. Token import is now a CLI operation: run `rafters init` (auto-detects and prompts) or `rafters import` (standalone)." : "Available tools: rafters_workspaces, rafters_composite, rafters_rule, rafters_pattern, rafters_component.";
30070
+ default:
30010
30071
  return {
30011
30072
  content: [
30012
- { type: "text", text: JSON.stringify({ error: `Unknown tool: ${name}`, suggestion }) }
30073
+ {
30074
+ type: "text",
30075
+ text: JSON.stringify({
30076
+ error: `Unknown tool: ${name}`,
30077
+ suggestion: "Available tools: rafters_workspaces, rafters_composite, rafters_pattern, rafters_component."
30078
+ })
30079
+ }
30013
30080
  ]
30014
30081
  };
30015
- }
30016
30082
  }
30017
30083
  }
30018
30084
  /**
@@ -30063,42 +30129,30 @@ var RaftersToolHandler = class {
30063
30129
  ]
30064
30130
  };
30065
30131
  }
30066
- async loadCompositesFromDir(dir) {
30067
- try {
30068
- const entries = await readdir(dir);
30069
- const files = entries.filter((f) => f.endsWith(".composite.json"));
30070
- for (const file2 of files) {
30071
- try {
30072
- const { readFile: readFile6 } = await import("fs/promises");
30073
- const raw = await readFile6(join10(dir, file2), "utf-8");
30074
- const parsed = JSON.parse(raw);
30075
- const result = CompositeFileSchema.safeParse(parsed);
30076
- if (result.success) {
30077
- try {
30078
- register2(result.data);
30079
- } catch {
30080
- }
30081
- }
30082
- } catch {
30083
- }
30132
+ /**
30133
+ * Recursively discover composites under the given directories via the shared
30134
+ * discovery core (node-fs adapter), then register each into the in-memory
30135
+ * registry. Duplicate ids -- across this call or against already-registered
30136
+ * composites -- are ignored: the first registration wins.
30137
+ */
30138
+ async loadCompositesFromDirs(...dirs) {
30139
+ const { registry: registry2 } = await discoverFromDirs(...dirs);
30140
+ for (const composite of registry2.values()) {
30141
+ try {
30142
+ register2(composite);
30143
+ } catch {
30084
30144
  }
30085
- } catch {
30086
30145
  }
30087
30146
  }
30088
30147
  async ensureCompositesLoaded(workspace) {
30089
30148
  if (!this.builtInCompositesLoaded) {
30090
- const builtInDirs = ["typography"];
30091
- for (const dir of builtInDirs) {
30092
- await this.loadCompositesFromDir(
30093
- join10(process.cwd(), "node_modules/@rafters/composites/src", dir)
30094
- );
30095
- }
30149
+ await this.loadCompositesFromDirs(
30150
+ join11(process.cwd(), "node_modules/@rafters/composites/src/typography")
30151
+ );
30096
30152
  this.builtInCompositesLoaded = true;
30097
30153
  }
30098
30154
  if (workspace && !this.compositesLoadedFor.has(workspace.root)) {
30099
- for (const dir of await this.compositeReadRoots(workspace.root)) {
30100
- await this.loadCompositesFromDir(dir);
30101
- }
30155
+ await this.loadCompositesFromDirs(...await this.compositeReadRoots(workspace.root));
30102
30156
  this.compositesLoadedFor.add(workspace.root);
30103
30157
  }
30104
30158
  }
@@ -30113,11 +30167,11 @@ var RaftersToolHandler = class {
30113
30167
  const paths = getRaftersPaths(workspaceRoot);
30114
30168
  let config2 = null;
30115
30169
  try {
30116
- config2 = JSON.parse(await readFile5(paths.config, "utf-8"));
30170
+ config2 = JSON.parse(await readFile6(paths.config, "utf-8"));
30117
30171
  } catch {
30118
30172
  }
30119
30173
  if (!config2?.compositesPath) {
30120
- return [join10(paths.root, "composites")];
30174
+ return [join11(paths.root, "composites")];
30121
30175
  }
30122
30176
  return resolveReadSet(config2.compositesPath, workspaceRoot);
30123
30177
  }
@@ -30150,49 +30204,15 @@ var RaftersToolHandler = class {
30150
30204
  usagePatterns: c4.manifest.usagePatterns,
30151
30205
  input: c4.input,
30152
30206
  output: c4.output,
30153
- blockCount: c4.blocks.length
30207
+ blockCount: c4.blocks.length,
30208
+ // Surface which blocks carry which validation rules. Rules compile to
30209
+ // native HTML5 constraint attributes at build, so agents need to see
30210
+ // them to reason about a composite's behavior. Only blocks with rules
30211
+ // are listed -- the array is empty when nothing is constrained.
30212
+ blockRules: c4.blocks.filter((b2) => b2.rules && b2.rules.length > 0).map((b2) => ({ id: b2.id, type: b2.type, rules: b2.rules }))
30154
30213
  }));
30155
30214
  return { content: [{ type: "text", text: JSON.stringify({ composites: result }, null, 2) }] };
30156
30215
  }
30157
- async handleRule(args) {
30158
- const { name, query, create } = args;
30159
- const builtInRules = [
30160
- { name: "required", description: "Field must have a value", source: "registry" },
30161
- { name: "email", description: "Must be a valid email address", source: "registry" },
30162
- {
30163
- name: "password",
30164
- description: "Password strength requirements",
30165
- source: "registry"
30166
- },
30167
- { name: "url", description: "Must be a valid URL", source: "registry" },
30168
- {
30169
- name: "credentials",
30170
- description: "Combined username/password validation",
30171
- source: "registry"
30172
- }
30173
- ];
30174
- if (create) {
30175
- return {
30176
- content: [
30177
- {
30178
- type: "text",
30179
- text: JSON.stringify({
30180
- error: "Rule creation not yet implemented",
30181
- suggestion: "Rules will be written to .rafters/rules/<name>.ts"
30182
- })
30183
- }
30184
- ]
30185
- };
30186
- }
30187
- let rules = builtInRules;
30188
- if (name) {
30189
- rules = rules.filter((r) => r.name === name);
30190
- } else if (query) {
30191
- const q = query.toLowerCase();
30192
- rules = rules.filter((r) => r.name.includes(q) || r.description.toLowerCase().includes(q));
30193
- }
30194
- return { content: [{ type: "text", text: JSON.stringify({ rules }, null, 2) }] };
30195
- }
30196
30216
  async handlePattern(args) {
30197
30217
  const { solves, query, workspace } = args;
30198
30218
  const resolved = this.resolve(workspace);
@@ -30332,7 +30352,7 @@ async function mcp(options) {
30332
30352
  let defaultWorkspace;
30333
30353
  if (options.projectRoot) {
30334
30354
  const explicit = resolve4(options.projectRoot);
30335
- const configPath = join11(explicit, ".rafters", "config.rafters.json");
30355
+ const configPath = join12(explicit, ".rafters", "config.rafters.json");
30336
30356
  if (!existsSync6(configPath)) {
30337
30357
  process.stderr.write(
30338
30358
  `--project-root ${explicit} does not contain .rafters/config.rafters.json
@@ -30358,7 +30378,7 @@ import { resolve as resolve5 } from "path";
30358
30378
 
30359
30379
  // ../studio/src/api/vite-plugin.ts
30360
30380
  import { writeFile as writeFile3 } from "fs/promises";
30361
- import { join as join12 } from "path";
30381
+ import { join as join13 } from "path";
30362
30382
  var REGISTRY_PLUGINS2 = [scalePlugin, contrastPlugin, statePlugin, invertPlugin];
30363
30383
  var STUDIO_REASON_DEFAULT = "studio interactive edit";
30364
30384
  var TokenResponseSchema = external_exports.object({
@@ -30384,7 +30404,7 @@ var ColorBuildOptionsSchema = external_exports.object({
30384
30404
  states: external_exports.record(external_exports.string(), external_exports.string()).optional()
30385
30405
  });
30386
30406
  var projectPath = process.env.RAFTERS_PROJECT_PATH || process.cwd();
30387
- var outputPath = join12(projectPath, ".rafters", "output", "rafters.css");
30407
+ var outputPath = join13(projectPath, ".rafters", "output", "rafters.css");
30388
30408
  var SetTokenMessageSchema = external_exports.object({
30389
30409
  name: external_exports.string().min(1),
30390
30410
  value: external_exports.union([external_exports.string(), ColorValueSchema, ColorReferenceSchema]),
@@ -30754,7 +30774,7 @@ function studioApiPlugin() {
30754
30774
  return {
30755
30775
  name: "rafters-studio-api",
30756
30776
  async configureServer(server) {
30757
- const tokensDir = join12(projectPath, ".rafters", "tokens");
30777
+ const tokensDir = join13(projectPath, ".rafters", "tokens");
30758
30778
  try {
30759
30779
  registry2 = loadRegistryFromDir(tokensDir, REGISTRY_PLUGINS2);
30760
30780
  initialized = true;
@@ -25,6 +25,7 @@ declare const RegistryItemTypeSchema: z.ZodEnum<{
25
25
  ui: "ui";
26
26
  primitive: "primitive";
27
27
  composite: "composite";
28
+ rule: "rule";
28
29
  }>;
29
30
  type RegistryItemType = z.infer<typeof RegistryItemTypeSchema>;
30
31
  /**
@@ -57,6 +58,7 @@ declare const RegistryItemSchema: z.ZodObject<{
57
58
  ui: "ui";
58
59
  primitive: "primitive";
59
60
  composite: "composite";
61
+ rule: "rule";
60
62
  }>;
61
63
  description: z.ZodOptional<z.ZodString>;
62
64
  primitives: z.ZodArray<z.ZodString>;
@@ -8,7 +8,7 @@ var RegistryFileSchema = z.object({
8
8
  devDependencies: z.array(z.string()).default([])
9
9
  // e.g., ["vitest"] - from @devDependencies JSDoc
10
10
  });
11
- var RegistryItemTypeSchema = z.enum(["ui", "primitive", "composite"]);
11
+ var RegistryItemTypeSchema = z.enum(["ui", "primitive", "composite", "rule"]);
12
12
  var RegistryItemIntelligenceSchema = z.object({
13
13
  cognitiveLoad: z.number().optional(),
14
14
  attentionEconomics: z.string().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rafters",
3
- "version": "0.0.69",
3
+ "version": "0.0.70",
4
4
  "description": "Design Intelligence CLI. Scaffold tokens, import existing shadcn/Tailwind v4 sources, add components, and serve an MCP server so AI agents read decisions instead of guessing.",
5
5
  "homepage": "https://rafters.studio",
6
6
  "license": "MIT",