rafters 0.4.1 → 0.4.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
@@ -79,14 +79,6 @@ Start the MCP server for AI agent access via stdio transport.
79
79
  pnpm dlx rafters mcp
80
80
  ```
81
81
 
82
- ### `rafters studio`
83
-
84
- Launch Studio for visual token editing.
85
-
86
- ```bash
87
- pnpm dlx rafters studio
88
- ```
89
-
90
82
  ## MCP tools
91
83
 
92
84
  Four tools for agent ASSEMBLY (not design). Token design lives in Studio; import lives in `rafters init`. Agents read pre-made decisions; they do not author them.
package/dist/index.js CHANGED
@@ -16836,12 +16836,6 @@ function assembleWcagAccessibility(scale2, reference) {
16836
16836
  }
16837
16837
  };
16838
16838
  }
16839
- function rebakeAccessibility(value) {
16840
- if (!("scale" in value) || !Array.isArray(value.scale)) return value;
16841
- const reference = value.scale[5] ?? value.scale[0];
16842
- if (!reference) return value;
16843
- return { ...value, accessibility: assembleWcagAccessibility(value.scale, reference) };
16844
- }
16845
16839
 
16846
16840
  // ../color-utils/src/internal/math.ts
16847
16841
  function clamp2(value, min, max2) {
@@ -21059,7 +21053,7 @@ ${sourceDirectives}
21059
21053
  ${themeBody}`;
21060
21054
  const { execFileSync } = await import("child_process");
21061
21055
  const { mkdtempSync, writeFileSync: writeFileSync2, readFileSync: readFileSync5, rmSync } = await import("fs");
21062
- const { join: join16, dirname: dirname4 } = await import("path");
21056
+ const { join: join15, dirname: dirname4 } = await import("path");
21063
21057
  const { createRequire: createRequire2 } = await import("module");
21064
21058
  const require2 = createRequire2(import.meta.url);
21065
21059
  let pkgDir;
@@ -21069,10 +21063,10 @@ ${themeBody}`;
21069
21063
  } catch {
21070
21064
  throw new Error("Failed to resolve @tailwindcss/cli");
21071
21065
  }
21072
- const binPath = join16(pkgDir, "dist", "index.mjs");
21073
- const tempDir = mkdtempSync(join16(pkgDir, ".tmp-compile-"));
21074
- const tempInput = join16(tempDir, "input.css");
21075
- const tempOutput = join16(tempDir, "output.css");
21066
+ const binPath = join15(pkgDir, "dist", "index.mjs");
21067
+ const tempDir = mkdtempSync(join15(pkgDir, ".tmp-compile-"));
21068
+ const tempInput = join15(tempDir, "input.css");
21069
+ const tempOutput = join15(tempDir, "output.css");
21076
21070
  try {
21077
21071
  writeFileSync2(tempInput, input);
21078
21072
  const args = [binPath, "-i", tempInput, "-o", tempOutput];
@@ -21256,7 +21250,7 @@ async function registryToDocumentation(registry2, options = {}) {
21256
21250
  const themeBody = registryToTailwind(registry2, { includeImport: false });
21257
21251
  const candidates = deriveCandidates(themeBody);
21258
21252
  const { mkdtempSync, writeFileSync: writeFileSync2, rmSync } = await import("fs");
21259
- const { join: join16, dirname: dirname4 } = await import("path");
21253
+ const { join: join15, dirname: dirname4 } = await import("path");
21260
21254
  const { createRequire: createRequire2 } = await import("module");
21261
21255
  const require2 = createRequire2(import.meta.url);
21262
21256
  let pkgDir;
@@ -21268,8 +21262,8 @@ async function registryToDocumentation(registry2, options = {}) {
21268
21262
  "Failed to resolve @tailwindcss/cli -- install it to generate documentation CSS"
21269
21263
  );
21270
21264
  }
21271
- const tempDir = mkdtempSync(join16(pkgDir, ".tmp-doc-compile-"));
21272
- const candidateFile = join16(tempDir, "candidates.txt");
21265
+ const tempDir = mkdtempSync(join15(pkgDir, ".tmp-doc-compile-"));
21266
+ const candidateFile = join15(tempDir, "candidates.txt");
21273
21267
  writeFileSync2(candidateFile, candidates.join("\n"));
21274
21268
  const sourceDirectives = [
21275
21269
  `@source "${candidateFile}";`,
@@ -21278,12 +21272,12 @@ async function registryToDocumentation(registry2, options = {}) {
21278
21272
  const input = `@import "tailwindcss" source(none);
21279
21273
  ${sourceDirectives}
21280
21274
  ${themeBody}`;
21281
- const tempInput = join16(tempDir, "input.css");
21282
- const tempOutput = join16(tempDir, "output.css");
21275
+ const tempInput = join15(tempDir, "input.css");
21276
+ const tempOutput = join15(tempDir, "output.css");
21283
21277
  try {
21284
21278
  writeFileSync2(tempInput, input);
21285
21279
  const { execFileSync } = await import("child_process");
21286
- const binPath = join16(pkgDir, "dist", "index.mjs");
21280
+ const binPath = join15(pkgDir, "dist", "index.mjs");
21287
21281
  const args = [binPath, "-i", tempInput, "-o", tempOutput];
21288
21282
  if (minify) args.push("--minify");
21289
21283
  execFileSync("node", args, { stdio: "pipe", timeout: 6e4, cwd: pkgDir });
@@ -28324,43 +28318,43 @@ function resolveContentSources(cwd, fields) {
28324
28318
  }
28325
28319
  async function regenerateOutputs(registry2, input, hooks2 = {}) {
28326
28320
  const {
28327
- outputDir: outputDir2,
28321
+ outputDir,
28328
28322
  exports,
28329
28323
  contentSources = [],
28330
28324
  darkMode = "class",
28331
28325
  includeImport = true
28332
28326
  } = input;
28333
- await mkdir(outputDir2, { recursive: true });
28327
+ await mkdir(outputDir, { recursive: true });
28334
28328
  const written = [];
28335
28329
  if (exports.tailwind) {
28336
28330
  const css = registryToTailwind(registry2, { includeImport, darkMode });
28337
- await writeFile(join(outputDir2, "rafters.css"), css);
28331
+ await writeFile(join(outputDir, "rafters.css"), css);
28338
28332
  written.push("rafters.css");
28339
28333
  }
28340
28334
  if (exports.typescript) {
28341
28335
  const ts = registryToTypeScript(registry2, { includeJSDoc: true });
28342
- await writeFile(join(outputDir2, "rafters.ts"), ts);
28336
+ await writeFile(join(outputDir, "rafters.ts"), ts);
28343
28337
  written.push("rafters.ts");
28344
28338
  }
28345
28339
  if (exports.dtcg) {
28346
28340
  const dtcg = toDTCG([...registry2.list()]);
28347
- await writeFile(join(outputDir2, "rafters.json"), JSON.stringify(dtcg, null, 2));
28341
+ await writeFile(join(outputDir, "rafters.json"), JSON.stringify(dtcg, null, 2));
28348
28342
  written.push("rafters.json");
28349
28343
  }
28350
28344
  if (exports.compiled) {
28351
28345
  const compiled = await registryToCompiled(registry2, { contentSources });
28352
- await writeFile(join(outputDir2, "rafters.standalone.css"), compiled);
28346
+ await writeFile(join(outputDir, "rafters.standalone.css"), compiled);
28353
28347
  written.push("rafters.standalone.css");
28354
28348
  }
28355
28349
  if (exports.documentation) {
28356
28350
  const doc = await registryToDocumentation(registry2, { contentSources });
28357
- await writeFile(join(outputDir2, "rafters.documentation.css"), doc);
28351
+ await writeFile(join(outputDir, "rafters.documentation.css"), doc);
28358
28352
  written.push("rafters.documentation.css");
28359
28353
  }
28360
28354
  const writtenSet = new Set(written);
28361
28355
  for (const filename of KNOWN_OUTPUT_FILENAMES) {
28362
28356
  if (writtenSet.has(filename)) continue;
28363
- const filePath = join(outputDir2, filename);
28357
+ const filePath = join(outputDir, filename);
28364
28358
  if (!existsSync(filePath)) continue;
28365
28359
  try {
28366
28360
  await unlink(filePath);
@@ -28829,13 +28823,7 @@ var RegistryItemSchema = external_exports.object({
28829
28823
  primitives: external_exports.array(external_exports.string()),
28830
28824
  files: external_exports.array(RegistryFileSchema),
28831
28825
  rules: external_exports.array(external_exports.string()).default([]),
28832
- composites: external_exports.array(external_exports.string()).default([]),
28833
- intelligence: RegistryItemIntelligenceSchema.optional(),
28834
- // Per-target facets. zod v4's `z.record(enum, ...)` demands EVERY enum key be
28835
- // present; a component built for only some targets must parse, so this is a
28836
- // partial record (only the built targets appear).
28837
- facets: external_exports.partialRecord(ComponentTargetSchema, FacetSchema).default({}),
28838
- parent: external_exports.string().optional()
28826
+ intelligence: RegistryItemIntelligenceSchema.optional()
28839
28827
  });
28840
28828
  var RegistryIndexSchema = external_exports.object({
28841
28829
  name: external_exports.string(),
@@ -30332,14 +30320,14 @@ async function installItem(cwd, item, options, config2, substrateKinds = []) {
30332
30320
  filesToInstall = selectCompositeFiles(item.files, getComponentTarget(config2));
30333
30321
  }
30334
30322
  for (const file2 of filesToInstall) {
30335
- const projectPath2 = item.type === "substrate" ? substrateProjectPath(file2.path, config2, cwd) : transformPath(file2.path, config2, cwd);
30336
- const targetPath = join7(cwd, projectPath2);
30337
- if (fileExists(cwd, projectPath2)) {
30323
+ const projectPath = item.type === "substrate" ? substrateProjectPath(file2.path, config2, cwd) : transformPath(file2.path, config2, cwd);
30324
+ const targetPath = join7(cwd, projectPath);
30325
+ if (fileExists(cwd, projectPath)) {
30338
30326
  if (!options.overwrite) {
30339
30327
  log({
30340
30328
  event: "add:skip",
30341
30329
  component: item.name,
30342
- file: projectPath2,
30330
+ file: projectPath,
30343
30331
  reason: "exists"
30344
30332
  });
30345
30333
  skipped = true;
@@ -30353,7 +30341,7 @@ async function installItem(cwd, item, options, config2, substrateKinds = []) {
30353
30341
  installPath: file2.path
30354
30342
  });
30355
30343
  await writeFile2(targetPath, transformedContent, "utf-8");
30356
- installedFiles.push(projectPath2);
30344
+ installedFiles.push(projectPath);
30357
30345
  }
30358
30346
  return {
30359
30347
  installed: installedFiles.length > 0,
@@ -30845,8 +30833,8 @@ import { dirname as dirname2, join as join9, resolve as resolve3 } from "path";
30845
30833
  function discoverProjectRoot(startDir) {
30846
30834
  let current = resolve3(startDir);
30847
30835
  for (; ; ) {
30848
- const configPath2 = join9(current, ".rafters", "config.rafters.json");
30849
- if (existsSync4(configPath2)) {
30836
+ const configPath = join9(current, ".rafters", "config.rafters.json");
30837
+ if (existsSync4(configPath)) {
30850
30838
  return current;
30851
30839
  }
30852
30840
  const parent = dirname2(current);
@@ -31004,15 +30992,16 @@ function assembleGraph(items) {
31004
30992
  if (src?.accessibility !== void 0) intel.accessibility = src.accessibility;
31005
30993
  if (src?.attentionEconomics !== void 0) intel.attentionEconomics = src.attentionEconomics;
31006
30994
  if (src?.trustBuilding !== void 0) intel.trustBuilding = src.trustBuilding;
30995
+ const raw = item;
31007
30996
  const gn = {
31008
30997
  id: item.name,
31009
30998
  kind,
31010
30999
  intel,
31011
- facets: item.facets ?? {},
31012
- composesWith: item.composites,
31000
+ facets: raw["facets"] ?? {},
31001
+ composesWith: raw["composites"] ?? [],
31013
31002
  parts: []
31014
31003
  };
31015
- if (item.parent !== void 0) gn.parent = item.parent;
31004
+ if (typeof raw["parent"] === "string") gn.parent = raw["parent"];
31016
31005
  nodes.set(item.name, gn);
31017
31006
  }
31018
31007
  for (const node of nodes.values()) {
@@ -31817,9 +31806,9 @@ var RaftersToolHandler = class {
31817
31806
  if (!parsedPaths.success) {
31818
31807
  const issue2 = parsedPaths.error.issues[0];
31819
31808
  const field = issue2 && issue2.path.length > 0 ? issue2.path.join(".") : "a path field";
31820
- const configPath2 = workspace ? getRaftersPaths(workspace.root).config : "config.rafters.json";
31809
+ const configPath = workspace ? getRaftersPaths(workspace.root).config : "config.rafters.json";
31821
31810
  return {
31822
- configError: `malformed config at ${configPath2}: ${field} ${issue2?.message ?? "is invalid"}`
31811
+ configError: `malformed config at ${configPath}: ${field} ${issue2?.message ?? "is invalid"}`
31823
31812
  };
31824
31813
  }
31825
31814
  const parsedTarget = ComponentTargetSchema.safeParse(config2?.componentTarget);
@@ -33173,8 +33162,8 @@ async function mcp(options) {
33173
33162
  let defaultWorkspace;
33174
33163
  if (options.projectRoot) {
33175
33164
  const explicit = resolve5(options.projectRoot);
33176
- const configPath2 = join14(explicit, ".rafters", "config.rafters.json");
33177
- if (!existsSync8(configPath2)) {
33165
+ const configPath = join14(explicit, ".rafters", "config.rafters.json");
33166
+ if (!existsSync8(configPath)) {
33178
33167
  process.stderr.write(
33179
33168
  `--project-root ${explicit} does not contain .rafters/config.rafters.json
33180
33169
  `
@@ -33193,844 +33182,6 @@ async function mcp(options) {
33193
33182
  await startMcpServer(workspaces, defaultWorkspace);
33194
33183
  }
33195
33184
 
33196
- // src/commands/studio.ts
33197
- import { existsSync as existsSync9 } from "fs";
33198
- import { resolve as resolve6 } from "path";
33199
-
33200
- // ../studio/src/api/vite-plugin.ts
33201
- import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
33202
- import { isAbsolute as isAbsolute4, join as join15 } from "path";
33203
- var REGISTRY_PLUGINS3 = [scalePlugin, contrastPlugin, statePlugin, invertPlugin];
33204
- var STUDIO_REASON_DEFAULT = "studio interactive edit";
33205
- var TokenResponseSchema = external_exports.object({
33206
- ok: external_exports.literal(true),
33207
- token: TokenSchema
33208
- });
33209
- var TokensResponseSchema = external_exports.object({
33210
- tokens: external_exports.array(TokenSchema),
33211
- initialized: external_exports.boolean()
33212
- });
33213
- var ErrorResponseSchema = external_exports.object({
33214
- ok: external_exports.literal(false),
33215
- error: external_exports.string()
33216
- });
33217
- var ColorBuildResponseSchema = external_exports.object({
33218
- ok: external_exports.literal(true),
33219
- colorValue: ColorValueSchema
33220
- });
33221
- var ColorBuildOptionsSchema = external_exports.object({
33222
- token: external_exports.string().optional(),
33223
- value: external_exports.string().optional(),
33224
- use: external_exports.string().optional(),
33225
- states: external_exports.record(external_exports.string(), external_exports.string()).optional()
33226
- });
33227
- var projectPath = process.env.RAFTERS_PROJECT_PATH || process.cwd();
33228
- var outputDir = join15(projectPath, ".rafters", "output");
33229
- var configPath = join15(projectPath, ".rafters", "config.rafters.json");
33230
- var FontsConfigSchema = external_exports.object({
33231
- path: external_exports.union([external_exports.string(), external_exports.null()]).optional(),
33232
- imports: external_exports.array(external_exports.string()).optional()
33233
- });
33234
- var RaftersConfigSchema = external_exports.object({
33235
- intent: external_exports.string().optional(),
33236
- fonts: FontsConfigSchema.optional(),
33237
- source: external_exports.string().optional(),
33238
- darkMode: external_exports.enum(["class", "media"]).optional(),
33239
- framework: external_exports.string().optional(),
33240
- cssPath: external_exports.union([external_exports.string(), external_exports.null()]).optional(),
33241
- exports: external_exports.record(external_exports.string(), external_exports.boolean()).optional()
33242
- }).passthrough();
33243
- var KNOWN_INTENTS = ["efficient"];
33244
- async function readRaftersConfig() {
33245
- try {
33246
- const raw = JSON.parse(await readFile8(configPath, "utf8"));
33247
- if ("shadcn" in raw && !("source" in raw)) {
33248
- if (raw.shadcn === true) raw.source = "shadcn";
33249
- delete raw.shadcn;
33250
- }
33251
- const result = RaftersConfigSchema.safeParse(raw);
33252
- if (!result.success) {
33253
- console.log(`[rafters] Config validation failed: ${result.error.message}`);
33254
- return null;
33255
- }
33256
- return result.data;
33257
- } catch {
33258
- return null;
33259
- }
33260
- }
33261
- function validateFontsPath(path) {
33262
- if (path === null || path === void 0) return null;
33263
- if (isAbsolute4(path)) return "fonts path must be relative";
33264
- return null;
33265
- }
33266
- function validateIntent(intent) {
33267
- if (!KNOWN_INTENTS.includes(intent)) {
33268
- return `unknown intent "${intent}". Known: ${KNOWN_INTENTS.join(", ")}`;
33269
- }
33270
- return null;
33271
- }
33272
- async function loadStudioConfig() {
33273
- const raw = await readRaftersConfig();
33274
- return raw;
33275
- }
33276
- var SetTokenMessageSchema = external_exports.object({
33277
- name: external_exports.string().min(1),
33278
- value: external_exports.union([external_exports.string(), ColorValueSchema, ColorReferenceSchema]),
33279
- persist: external_exports.boolean().optional(),
33280
- reason: external_exports.string().optional()
33281
- });
33282
- var ConfigPatchSchema = external_exports.object({
33283
- intent: external_exports.string().min(1).optional(),
33284
- darkMode: external_exports.enum(["class", "media"]).optional(),
33285
- fonts: FontsConfigSchema.optional()
33286
- }).refine((patch) => Object.keys(patch).length > 0, {
33287
- message: "patch must set at least one of: intent, darkMode, fonts"
33288
- });
33289
- function ridOf(rawData) {
33290
- const rid = rawData?.__rid;
33291
- return typeof rid === "string" ? rid : void 0;
33292
- }
33293
- var TokenPatchSchema = TokenSchema.pick({
33294
- value: true,
33295
- trustLevel: true,
33296
- elevationLevel: true,
33297
- motionIntent: true,
33298
- accessibilityLevel: true,
33299
- description: true
33300
- }).extend({
33301
- userOverride: TokenSchema.shape.userOverride.optional()
33302
- });
33303
- var ColorNamespacePatchSchema = external_exports.object({
33304
- value: external_exports.union([
33305
- external_exports.string().regex(/^oklch\(/, "Color value must be oklch() format"),
33306
- ColorValueSchema
33307
- ]),
33308
- scalePosition: external_exports.number().min(0).max(10).optional(),
33309
- description: external_exports.string().optional(),
33310
- userOverride: TokenSchema.shape.userOverride.optional()
33311
- });
33312
- var SemanticNamespacePatchSchema = external_exports.object({
33313
- value: ColorReferenceSchema,
33314
- trustLevel: external_exports.enum(["low", "medium", "high", "critical"]).optional(),
33315
- consequence: external_exports.enum(["reversible", "significant", "permanent", "destructive"]).optional(),
33316
- description: external_exports.string().optional(),
33317
- userOverride: TokenSchema.shape.userOverride.optional()
33318
- });
33319
- var SpacingNamespacePatchSchema = external_exports.object({
33320
- value: external_exports.string().regex(/^-?\d+(\.\d+)?rem$/, 'Spacing value must be rem (e.g., "1rem", "0.25rem")'),
33321
- scalePosition: external_exports.number().min(0).max(12).optional(),
33322
- description: external_exports.string().optional(),
33323
- userOverride: TokenSchema.shape.userOverride.optional()
33324
- });
33325
- var TypographyNamespacePatchSchema = external_exports.object({
33326
- value: external_exports.string().min(1),
33327
- lineHeight: external_exports.string().optional(),
33328
- description: external_exports.string().optional(),
33329
- userOverride: TokenSchema.shape.userOverride.optional()
33330
- });
33331
- var BreakpointNamespacePatchSchema = external_exports.object({
33332
- value: external_exports.string().regex(/^\d+(\.\d+)?(px|rem)$/, "Breakpoint must be px or rem value"),
33333
- viewportAware: external_exports.boolean().optional(),
33334
- containerQueryAware: external_exports.boolean().optional(),
33335
- description: external_exports.string().optional(),
33336
- userOverride: TokenSchema.shape.userOverride.optional()
33337
- });
33338
- var RadiusNamespacePatchSchema = external_exports.object({
33339
- value: external_exports.union([
33340
- external_exports.literal("0"),
33341
- external_exports.literal("9999px"),
33342
- external_exports.string().regex(/^\d+(\.\d+)?rem$/, 'Radius must be rem value, "0", or "9999px"')
33343
- ]),
33344
- scalePosition: external_exports.number().optional(),
33345
- description: external_exports.string().optional(),
33346
- userOverride: TokenSchema.shape.userOverride.optional()
33347
- });
33348
- var ShadowNamespacePatchSchema = external_exports.object({
33349
- value: external_exports.string().min(1),
33350
- shadowToken: external_exports.string().optional(),
33351
- description: external_exports.string().optional(),
33352
- userOverride: TokenSchema.shape.userOverride.optional()
33353
- });
33354
- var DepthNamespacePatchSchema = external_exports.object({
33355
- value: external_exports.string().regex(/^-?\d+$/, "Depth value must be numeric z-index"),
33356
- elevationLevel: external_exports.enum(["surface", "raised", "overlay", "sticky", "modal", "popover", "tooltip"]).optional(),
33357
- description: external_exports.string().optional(),
33358
- userOverride: TokenSchema.shape.userOverride.optional()
33359
- });
33360
- var MotionNamespacePatchSchema = external_exports.object({
33361
- value: external_exports.union([
33362
- external_exports.string().regex(/^\d+ms$/, "Duration must be in ms"),
33363
- external_exports.string().regex(/^cubic-bezier\(/, "Easing must be cubic-bezier()")
33364
- ]),
33365
- motionIntent: external_exports.enum(["enter", "exit", "emphasis", "transition"]).optional(),
33366
- easingName: TokenSchema.shape.easingName,
33367
- reducedMotionAware: external_exports.boolean().optional(),
33368
- description: external_exports.string().optional(),
33369
- userOverride: TokenSchema.shape.userOverride.optional()
33370
- });
33371
- var ElevationNamespacePatchSchema = external_exports.object({
33372
- value: external_exports.string(),
33373
- elevationLevel: external_exports.enum(["surface", "raised", "overlay", "sticky", "modal", "popover", "tooltip"]).optional(),
33374
- shadowToken: external_exports.string().optional(),
33375
- description: external_exports.string().optional(),
33376
- userOverride: TokenSchema.shape.userOverride.optional()
33377
- });
33378
- var FocusNamespacePatchSchema = external_exports.object({
33379
- value: external_exports.string().min(1),
33380
- focusRingWidth: external_exports.string().optional(),
33381
- focusRingColor: external_exports.string().optional(),
33382
- focusRingOffset: external_exports.string().optional(),
33383
- focusRingStyle: external_exports.string().optional(),
33384
- accessibilityLevel: external_exports.enum(["AA", "AAA"]).optional(),
33385
- description: external_exports.string().optional(),
33386
- userOverride: TokenSchema.shape.userOverride.optional()
33387
- });
33388
- var namespacePatchSchemas = {
33389
- color: ColorNamespacePatchSchema,
33390
- semantic: SemanticNamespacePatchSchema,
33391
- spacing: SpacingNamespacePatchSchema,
33392
- typography: TypographyNamespacePatchSchema,
33393
- breakpoint: BreakpointNamespacePatchSchema,
33394
- radius: RadiusNamespacePatchSchema,
33395
- shadow: ShadowNamespacePatchSchema,
33396
- depth: DepthNamespacePatchSchema,
33397
- motion: MotionNamespacePatchSchema,
33398
- elevation: ElevationNamespacePatchSchema,
33399
- focus: FocusNamespacePatchSchema
33400
- };
33401
- function getNamespacePatchSchema(namespace) {
33402
- return namespacePatchSchemas[namespace] ?? TokenPatchSchema;
33403
- }
33404
- var MAX_BODY_SIZE = 1024 * 1024;
33405
- function readJsonBody(req) {
33406
- return new Promise((resolve7, reject) => {
33407
- let body = "";
33408
- let size = 0;
33409
- req.on("data", (chunk) => {
33410
- size += chunk.length;
33411
- if (size > MAX_BODY_SIZE) {
33412
- reject(new Error("Request body too large"));
33413
- return;
33414
- }
33415
- body += chunk.toString();
33416
- });
33417
- req.on("end", () => {
33418
- try {
33419
- resolve7(body ? JSON.parse(body) : {});
33420
- } catch {
33421
- reject(new Error("Invalid JSON body"));
33422
- }
33423
- });
33424
- req.on("error", reject);
33425
- });
33426
- }
33427
- async function handleBuildColor(req, res) {
33428
- let body;
33429
- try {
33430
- body = await readJsonBody(req);
33431
- } catch (error47) {
33432
- res.statusCode = 400;
33433
- const message = error47 instanceof Error ? error47.message : "Invalid JSON body";
33434
- res.end(JSON.stringify({ ok: false, error: message }));
33435
- return;
33436
- }
33437
- const inputSchema = external_exports.object({
33438
- oklch: OKLCHSchema,
33439
- options: ColorBuildOptionsSchema.optional()
33440
- });
33441
- const inputResult = inputSchema.safeParse(body);
33442
- if (!inputResult.success) {
33443
- res.statusCode = 400;
33444
- const issues = inputResult.error.issues;
33445
- const message = issues[0] ? `${issues[0].path.join(".") || "oklch"}: ${issues[0].message}` : inputResult.error.message;
33446
- res.end(JSON.stringify({ ok: false, error: message }));
33447
- return;
33448
- }
33449
- const { oklch: oklch2, options } = inputResult.data;
33450
- try {
33451
- const cleanOptions = {};
33452
- if (options) {
33453
- for (const [k, v] of Object.entries(options)) {
33454
- if (v !== void 0) cleanOptions[k] = v;
33455
- }
33456
- }
33457
- const colorValue = buildColorValue(
33458
- oklch2,
33459
- cleanOptions
33460
- );
33461
- const outputResult = ColorValueSchema.safeParse(colorValue);
33462
- if (!outputResult.success) {
33463
- console.log(`[rafters] ColorValue validation failed: ${outputResult.error.message}`);
33464
- res.statusCode = 500;
33465
- res.end(JSON.stringify({ ok: false, error: "ColorValue generation failed validation" }));
33466
- return;
33467
- }
33468
- const response = ColorBuildResponseSchema.parse({ ok: true, colorValue: outputResult.data });
33469
- res.end(JSON.stringify(response));
33470
- } catch (error47) {
33471
- console.log(`[rafters] Color build failed: ${error47}`);
33472
- res.statusCode = 500;
33473
- res.end(JSON.stringify({ ok: false, error: String(error47) }));
33474
- }
33475
- }
33476
- async function handlePostToken(req, res, name, registry2) {
33477
- let body;
33478
- try {
33479
- body = await readJsonBody(req);
33480
- } catch (error47) {
33481
- res.statusCode = 400;
33482
- const message = error47 instanceof Error ? error47.message : "Invalid JSON body";
33483
- res.end(JSON.stringify({ ok: false, error: message }));
33484
- return;
33485
- }
33486
- const existingToken = registry2.get(name);
33487
- if (!existingToken) {
33488
- const createSchema = external_exports.object({
33489
- namespace: external_exports.string().min(1),
33490
- category: external_exports.string().min(1),
33491
- value: external_exports.union([external_exports.string(), ColorValueSchema, ColorReferenceSchema]),
33492
- userOverride: external_exports.object({
33493
- previousValue: external_exports.union([external_exports.string(), ColorValueSchema, ColorReferenceSchema]),
33494
- reason: external_exports.string().min(1, "Reason is required. Every token needs a why."),
33495
- context: external_exports.string().optional()
33496
- }),
33497
- description: external_exports.string().optional()
33498
- });
33499
- const createResult = createSchema.safeParse(body);
33500
- if (!createResult.success) {
33501
- res.statusCode = 400;
33502
- const issues = createResult.error.issues;
33503
- const message = issues[0] ? `${issues[0].path.join(".") || "body"}: ${issues[0].message}` : createResult.error.message;
33504
- res.end(JSON.stringify({ ok: false, error: message }));
33505
- return;
33506
- }
33507
- const newToken = {
33508
- name,
33509
- namespace: createResult.data.namespace,
33510
- category: createResult.data.category,
33511
- value: createResult.data.value,
33512
- userOverride: createResult.data.userOverride,
33513
- containerQueryAware: true,
33514
- description: createResult.data.description
33515
- };
33516
- const tokenResult2 = TokenSchema.safeParse(newToken);
33517
- if (!tokenResult2.success) {
33518
- res.statusCode = 400;
33519
- res.end(JSON.stringify({ ok: false, error: tokenResult2.error.message }));
33520
- return;
33521
- }
33522
- try {
33523
- registry2.define(tokenResult2.data);
33524
- const response = TokenResponseSchema.parse({ ok: true, token: registry2.get(name) });
33525
- res.statusCode = 201;
33526
- res.end(JSON.stringify(response));
33527
- } catch (error47) {
33528
- res.statusCode = 500;
33529
- res.end(JSON.stringify({ ok: false, error: String(error47) }));
33530
- }
33531
- return;
33532
- }
33533
- const namespaceSchema = getNamespacePatchSchema(existingToken.namespace);
33534
- const patchResult = namespaceSchema.safeParse(body);
33535
- if (!patchResult.success) {
33536
- res.statusCode = 400;
33537
- const issues = patchResult.error.issues;
33538
- const message = issues[0] ? `${issues[0].path.join(".") || "value"}: ${issues[0].message}` : patchResult.error.message;
33539
- res.end(JSON.stringify({ ok: false, error: message }));
33540
- return;
33541
- }
33542
- const mergedToken = {
33543
- ...existingToken,
33544
- ...patchResult.data
33545
- };
33546
- if (mergedToken.value && typeof mergedToken.value === "object" && "scale" in mergedToken.value) {
33547
- mergedToken.value = rebakeAccessibility(mergedToken.value);
33548
- }
33549
- const tokenResult = TokenSchema.safeParse(mergedToken);
33550
- if (!tokenResult.success) {
33551
- res.statusCode = 400;
33552
- const issues = tokenResult.error.issues;
33553
- const message = issues[0] ? `${issues[0].path.join(".") || "token"}: ${issues[0].message}` : tokenResult.error.message;
33554
- res.end(JSON.stringify({ ok: false, error: message }));
33555
- return;
33556
- }
33557
- try {
33558
- const merged = tokenResult.data;
33559
- const patchOverride = patchResult.data.userOverride;
33560
- const reason = patchOverride?.reason ?? STUDIO_REASON_DEFAULT;
33561
- registry2.define(merged);
33562
- registry2.set(merged.name, merged.value, { reason });
33563
- const updatedToken = registry2.get(name);
33564
- const response = TokenResponseSchema.parse({ ok: true, token: updatedToken });
33565
- res.end(JSON.stringify(response));
33566
- } catch (error47) {
33567
- console.log(`[rafters] Token update failed for "${name}": ${error47}`);
33568
- res.statusCode = 500;
33569
- res.end(JSON.stringify({ ok: false, error: String(error47) }));
33570
- }
33571
- }
33572
- var TokenArraySchema = external_exports.array(TokenSchema);
33573
- var NamespaceParamSchema = external_exports.string().min(1).optional();
33574
- function handleGetTokens(url3, res, registry2, initialized) {
33575
- try {
33576
- const parsedUrl = new URL(url3, "http://localhost");
33577
- const namespaceParam = parsedUrl.searchParams.get("namespace");
33578
- const namespaceResult = NamespaceParamSchema.safeParse(namespaceParam ?? void 0);
33579
- if (namespaceParam !== null && !namespaceResult.success) {
33580
- res.statusCode = 400;
33581
- res.end(JSON.stringify({ ok: false, error: "Invalid namespace parameter" }));
33582
- return;
33583
- }
33584
- let tokens = registry2.list();
33585
- if (namespaceParam && namespaceResult.success && namespaceResult.data) {
33586
- tokens = tokens.filter((t) => t.namespace === namespaceResult.data);
33587
- }
33588
- const tokensResult = external_exports.array(TokenSchema).safeParse(tokens);
33589
- if (!tokensResult.success) {
33590
- console.log(`[rafters] Tokens list failed validation: ${tokensResult.error.message}`);
33591
- res.statusCode = 500;
33592
- res.end(JSON.stringify({ ok: false, error: "Token validation failed" }));
33593
- return;
33594
- }
33595
- const response = TokensResponseSchema.parse({
33596
- tokens: tokensResult.data,
33597
- initialized
33598
- });
33599
- res.end(JSON.stringify(response));
33600
- } catch (error47) {
33601
- console.log(`[rafters] Failed to list tokens: ${error47}`);
33602
- res.statusCode = 500;
33603
- res.end(JSON.stringify({ ok: false, error: "Failed to retrieve tokens" }));
33604
- }
33605
- }
33606
- async function handlePostTokens(req, res, registry2) {
33607
- let body;
33608
- try {
33609
- body = await readJsonBody(req);
33610
- } catch (error47) {
33611
- res.statusCode = 400;
33612
- const message = error47 instanceof Error ? error47.message : "Invalid JSON body";
33613
- res.end(JSON.stringify({ ok: false, error: message }));
33614
- return;
33615
- }
33616
- const tokensResult = TokenArraySchema.safeParse(body);
33617
- if (!tokensResult.success) {
33618
- res.statusCode = 400;
33619
- const issues = tokensResult.error.issues;
33620
- const message = issues[0] ? `${issues[0].path.join(".") || "tokens"}: ${issues[0].message}` : tokensResult.error.message;
33621
- res.end(JSON.stringify({ ok: false, error: message }));
33622
- return;
33623
- }
33624
- const tokens = tokensResult.data;
33625
- for (const token of tokens) {
33626
- if (!registry2.has(token.name)) {
33627
- res.statusCode = 404;
33628
- res.end(JSON.stringify({ ok: false, error: `Token "${token.name}" not found` }));
33629
- return;
33630
- }
33631
- }
33632
- try {
33633
- for (const token of tokens) {
33634
- const reason = token.userOverride?.reason ?? STUDIO_REASON_DEFAULT;
33635
- registry2.define(token);
33636
- registry2.set(token.name, token.value, { reason });
33637
- }
33638
- const updatedTokens = tokens.map((t) => registry2.get(t.name)).filter(Boolean);
33639
- const response = TokensResponseSchema.parse({
33640
- tokens: updatedTokens,
33641
- initialized: true
33642
- });
33643
- res.end(JSON.stringify(response));
33644
- } catch (error47) {
33645
- console.log(`[rafters] Batch token update failed: ${error47}`);
33646
- res.statusCode = 500;
33647
- res.end(JSON.stringify({ ok: false, error: String(error47) }));
33648
- }
33649
- }
33650
- function studioApiPlugin() {
33651
- let registry2;
33652
- let initialized = false;
33653
- return {
33654
- name: "rafters-studio-api",
33655
- async configureServer(server) {
33656
- const tokensDir = join15(projectPath, ".rafters", "tokens");
33657
- try {
33658
- registry2 = loadRegistryFromDir(tokensDir, REGISTRY_PLUGINS3);
33659
- initialized = true;
33660
- } catch (error47) {
33661
- const message = error47 instanceof Error ? error47.message : String(error47);
33662
- console.log(`[rafters] Failed to initialize: ${message}`);
33663
- if (message.includes("ENOENT")) {
33664
- console.log(`[rafters] No project found at ${projectPath}. Run 'rafters init' first.`);
33665
- }
33666
- registry2 = new TokenRegistry([], REGISTRY_PLUGINS3);
33667
- initialized = false;
33668
- }
33669
- const regenerate = async () => {
33670
- const config2 = await loadStudioConfig();
33671
- const exports = config2?.exports ?? {
33672
- tailwind: true,
33673
- typescript: true,
33674
- dtcg: false,
33675
- compiled: false,
33676
- documentation: false
33677
- };
33678
- await regenerateOutputs(
33679
- registry2,
33680
- {
33681
- outputDir,
33682
- exports,
33683
- contentSources: config2 ? resolveContentSources(projectPath, config2) : [],
33684
- darkMode: config2?.darkMode ?? "class",
33685
- includeImport: config2?.source !== "shadcn"
33686
- },
33687
- { notify: () => server.ws.send({ type: "custom", event: "rafters:css-updated" }) }
33688
- );
33689
- };
33690
- const persistAndNotify = async () => {
33691
- try {
33692
- if (initialized) {
33693
- saveRegistryToDir(tokensDir, registry2);
33694
- }
33695
- await regenerate();
33696
- } catch (error47) {
33697
- console.log(`[rafters] CSS regeneration failed: ${error47}`);
33698
- }
33699
- };
33700
- const reloadAndRegenerate = async () => {
33701
- try {
33702
- registry2 = loadRegistryFromDir(tokensDir, REGISTRY_PLUGINS3);
33703
- initialized = true;
33704
- } catch {
33705
- }
33706
- try {
33707
- await regenerate();
33708
- } catch (error47) {
33709
- console.log(`[rafters] CSS regeneration failed: ${error47}`);
33710
- }
33711
- };
33712
- {
33713
- const trackedClassDirs = /* @__PURE__ */ new Set();
33714
- const syncWatchTargets = async () => {
33715
- const watchConfig = await loadStudioConfig();
33716
- const classDirs = watchConfig ? resolveContentSources(projectPath, watchConfig) : [];
33717
- const newDirs = classDirs.filter((d2) => !trackedClassDirs.has(d2));
33718
- if (newDirs.length > 0) {
33719
- server.watcher.add(newDirs.map((dir) => join15(dir, "**/*.classes.ts")));
33720
- for (const d2 of newDirs) trackedClassDirs.add(d2);
33721
- }
33722
- };
33723
- server.watcher.add([join15(tokensDir, "*.rafters.json"), configPath]);
33724
- await syncWatchTargets();
33725
- let debounce = null;
33726
- const onChange = (changed) => {
33727
- const watched = changed.endsWith(".classes.ts") || changed.endsWith(".rafters.json") || changed === configPath;
33728
- if (!watched) return;
33729
- if (debounce) clearTimeout(debounce);
33730
- debounce = setTimeout(() => {
33731
- const isConfigChange = changed === configPath;
33732
- const run = isConfigChange ? syncWatchTargets().then(reloadAndRegenerate) : reloadAndRegenerate();
33733
- void run;
33734
- }, 150);
33735
- };
33736
- server.watcher.on("change", onChange);
33737
- server.watcher.on("add", onChange);
33738
- server.watcher.on("unlink", onChange);
33739
- }
33740
- server.ws.on("rafters:set-token", async (rawData, client) => {
33741
- const parsed = SetTokenMessageSchema.safeParse(rawData);
33742
- if (!parsed.success) {
33743
- client.send("rafters:token-updated", {
33744
- ok: false,
33745
- error: `Invalid message: ${parsed.error.message}`
33746
- });
33747
- return;
33748
- }
33749
- const data = parsed.data;
33750
- const shouldPersist = data.persist !== false;
33751
- const existingToken = registry2.get(data.name);
33752
- const isColorFamily = existingToken?.namespace === "color" && typeof data.value === "object" && data.value !== null && "scale" in data.value;
33753
- let enrichmentPromise = null;
33754
- if (isColorFamily) {
33755
- const colorValue = data.value;
33756
- const base = colorValue.scale?.[5];
33757
- if (base) {
33758
- const l = base.l.toFixed(3);
33759
- const c4 = base.c.toFixed(3);
33760
- const h = Math.round(base.h);
33761
- enrichmentPromise = fetch(`https://api.rafters.studio/color/${l}-${c4}-${h}?sync=true`).then((r) => r.ok ? r.json() : null).catch(() => null);
33762
- }
33763
- }
33764
- const reason = data.reason ?? STUDIO_REASON_DEFAULT;
33765
- try {
33766
- registry2.set(data.name, data.value, { reason });
33767
- if (shouldPersist) {
33768
- await persistAndNotify();
33769
- }
33770
- client.send("rafters:token-updated", {
33771
- ok: true,
33772
- name: data.name,
33773
- persisted: shouldPersist
33774
- });
33775
- if (enrichmentPromise) {
33776
- const enrichment = await enrichmentPromise;
33777
- if (enrichment && typeof enrichment === "object" && "color" in enrichment) {
33778
- const apiColor = enrichment.color;
33779
- if (apiColor?.intelligence) {
33780
- const current = registry2.get(data.name);
33781
- if (current && typeof current.value === "object" && current.value !== null) {
33782
- const enrichedValue = {
33783
- ...current.value,
33784
- intelligence: apiColor.intelligence
33785
- };
33786
- registry2.set(data.name, enrichedValue, {
33787
- reason: `${reason} (enriched by api.rafters.studio)`
33788
- });
33789
- await persistAndNotify();
33790
- client.send("rafters:color-enriched", {
33791
- name: data.name,
33792
- intelligence: apiColor.intelligence
33793
- });
33794
- }
33795
- }
33796
- }
33797
- }
33798
- } catch (error47) {
33799
- console.log(`[rafters] Token update failed for "${data.name}": ${error47}`);
33800
- client.send("rafters:token-updated", { ok: false, error: String(error47) });
33801
- }
33802
- });
33803
- server.ws.on("rafters:get-config", async (rawData, client) => {
33804
- const __rid = ridOf(rawData);
33805
- const reply = (msg) => client.send("rafters:config", { ...msg, __rid });
33806
- const config2 = await readRaftersConfig();
33807
- if (!config2) {
33808
- reply({ ok: false, error: `config not found at ${configPath}` });
33809
- return;
33810
- }
33811
- reply({ ok: true, config: config2 });
33812
- });
33813
- server.ws.on("rafters:set-config", async (rawData, client) => {
33814
- const __rid = ridOf(rawData);
33815
- const reply = (msg) => client.send("rafters:config", { ...msg, __rid });
33816
- const parsed = ConfigPatchSchema.safeParse(rawData);
33817
- if (!parsed.success) {
33818
- reply({ ok: false, error: `Invalid patch: ${parsed.error.message}` });
33819
- return;
33820
- }
33821
- const patch = parsed.data;
33822
- if (patch.intent !== void 0) {
33823
- const intentError = validateIntent(patch.intent);
33824
- if (intentError) {
33825
- reply({ ok: false, error: intentError });
33826
- return;
33827
- }
33828
- }
33829
- if (patch.fonts?.path !== void 0) {
33830
- const pathError = validateFontsPath(patch.fonts.path);
33831
- if (pathError) {
33832
- reply({ ok: false, error: pathError });
33833
- return;
33834
- }
33835
- }
33836
- const config2 = await readRaftersConfig();
33837
- if (!config2) {
33838
- reply({ ok: false, error: `config not found at ${configPath}` });
33839
- return;
33840
- }
33841
- const updated = { ...config2 };
33842
- if (patch.intent !== void 0) updated.intent = patch.intent;
33843
- if (patch.darkMode !== void 0) updated.darkMode = patch.darkMode;
33844
- if (patch.fonts !== void 0) {
33845
- const merged = { ...config2.fonts };
33846
- if (patch.fonts.path !== void 0) merged.path = patch.fonts.path;
33847
- if (patch.fonts.imports !== void 0) merged.imports = patch.fonts.imports;
33848
- updated.fonts = merged;
33849
- }
33850
- try {
33851
- await writeFile6(configPath, `${JSON.stringify(updated, null, 2)}
33852
- `);
33853
- reply({ ok: true, config: updated });
33854
- } catch (error47) {
33855
- console.log(`[rafters] Config update failed: ${error47}`);
33856
- reply({ ok: false, error: String(error47) });
33857
- }
33858
- });
33859
- server.middlewares.use(async (req, res, next) => {
33860
- let pathname;
33861
- try {
33862
- pathname = new URL(req.url ?? "", "http://localhost").pathname;
33863
- } catch {
33864
- next();
33865
- return;
33866
- }
33867
- if ((pathname === "/api/" || pathname === "/api") && req.method === "GET") {
33868
- res.setHeader("Content-Type", "application/json");
33869
- res.end(
33870
- JSON.stringify({
33871
- name: "Rafters Studio API",
33872
- initialized,
33873
- tokenCount: registry2.list().length,
33874
- rules: {
33875
- whyGate: "Every POST to /api/tokens/:name requires userOverride.reason.",
33876
- gets: "Returns full Token with all intelligence metadata.",
33877
- sets: "Value + reason in. API fills the Token shape. CSS updates via HMR.",
33878
- colors: "POST /api/color/build with OKLCH to get a ColorValue."
33879
- },
33880
- endpoints: {
33881
- "GET /api/tokens": "All tokens (optional ?namespace= filter)",
33882
- "GET /api/tokens/:name": "One token with full data",
33883
- "POST /api/tokens/:name": "Update token (namespace-validated patch)",
33884
- "POST /api/tokens": "Batch update tokens",
33885
- "POST /api/color/build": "OKLCH -> full ColorValue",
33886
- "POST /api/shutdown": "Gracefully stop the studio"
33887
- }
33888
- })
33889
- );
33890
- return;
33891
- }
33892
- if (pathname === "/api/shutdown" && req.method === "POST") {
33893
- res.setHeader("Content-Type", "application/json");
33894
- res.end(JSON.stringify({ ok: true, message: "Shutting down" }));
33895
- console.log("[rafters] Studio shutting down...");
33896
- setTimeout(() => server.close(), 100);
33897
- return;
33898
- }
33899
- if (pathname === "/api/color/build") {
33900
- res.setHeader("Content-Type", "application/json");
33901
- if (req.method !== "POST") {
33902
- res.statusCode = 405;
33903
- res.setHeader("Allow", "POST");
33904
- res.end(JSON.stringify({ ok: false, error: "Method not allowed" }));
33905
- return;
33906
- }
33907
- handleBuildColor(req, res).catch((error47) => {
33908
- console.log(`[rafters] Unhandled error in POST /api/color/build: ${error47}`);
33909
- if (!res.headersSent) {
33910
- res.statusCode = 500;
33911
- res.end(JSON.stringify({ ok: false, error: "Internal server error" }));
33912
- }
33913
- });
33914
- return;
33915
- }
33916
- const tokenMatch = pathname.match(/^\/api\/tokens\/(.+)$/);
33917
- if (tokenMatch) {
33918
- let name;
33919
- try {
33920
- name = decodeURIComponent(tokenMatch[1] ?? "");
33921
- } catch {
33922
- res.statusCode = 400;
33923
- res.setHeader("Content-Type", "application/json");
33924
- res.end(JSON.stringify({ ok: false, error: "Invalid token name encoding" }));
33925
- return;
33926
- }
33927
- res.setHeader("Content-Type", "application/json");
33928
- if (req.method !== "GET" && req.method !== "POST") {
33929
- res.statusCode = 405;
33930
- res.setHeader("Allow", "GET, POST");
33931
- res.end(JSON.stringify({ ok: false, error: "Method not allowed" }));
33932
- return;
33933
- }
33934
- if (req.method === "POST") {
33935
- try {
33936
- await handlePostToken(req, res, name, registry2);
33937
- if (res.statusCode < 400) await persistAndNotify();
33938
- } catch (error47) {
33939
- console.log(`[rafters] Unhandled error in POST /api/tokens/${name}: ${error47}`);
33940
- if (!res.headersSent) {
33941
- res.statusCode = 500;
33942
- res.end(JSON.stringify({ ok: false, error: "Internal server error" }));
33943
- }
33944
- }
33945
- return;
33946
- }
33947
- const token = registry2.get(name);
33948
- if (!token) {
33949
- const errorResponse = ErrorResponseSchema.parse({
33950
- ok: false,
33951
- error: `Token "${name}" not found`
33952
- });
33953
- res.statusCode = 404;
33954
- res.end(JSON.stringify(errorResponse));
33955
- return;
33956
- }
33957
- const tokenResult = TokenSchema.safeParse(token);
33958
- if (!tokenResult.success) {
33959
- console.log(
33960
- `[rafters] Token "${name}" failed validation: ${tokenResult.error.message}`
33961
- );
33962
- const errorResponse = ErrorResponseSchema.parse({
33963
- ok: false,
33964
- error: `Token "${name}" has invalid structure`
33965
- });
33966
- res.statusCode = 500;
33967
- res.end(JSON.stringify(errorResponse));
33968
- return;
33969
- }
33970
- const response = TokenResponseSchema.parse({ ok: true, token: tokenResult.data });
33971
- res.end(JSON.stringify(response));
33972
- return;
33973
- }
33974
- if (pathname === "/api/tokens") {
33975
- res.setHeader("Content-Type", "application/json");
33976
- if (req.method !== "GET" && req.method !== "POST") {
33977
- res.statusCode = 405;
33978
- res.setHeader("Allow", "GET, POST");
33979
- res.end(JSON.stringify({ ok: false, error: "Method not allowed" }));
33980
- return;
33981
- }
33982
- if (req.method === "POST") {
33983
- try {
33984
- await handlePostTokens(req, res, registry2);
33985
- if (res.statusCode < 400) await persistAndNotify();
33986
- } catch (error47) {
33987
- console.log(`[rafters] Unhandled error in POST /api/tokens: ${error47}`);
33988
- if (!res.headersSent) {
33989
- res.statusCode = 500;
33990
- res.end(JSON.stringify({ ok: false, error: "Internal server error" }));
33991
- }
33992
- }
33993
- return;
33994
- }
33995
- handleGetTokens(req.url ?? "", res, registry2, initialized);
33996
- return;
33997
- }
33998
- next();
33999
- });
34000
- }
34001
- };
34002
- }
34003
-
34004
- // src/commands/studio.ts
34005
- import { createServer } from "vite";
34006
- async function studio() {
34007
- const cwd = process.cwd();
34008
- const paths = getRaftersPaths(cwd);
34009
- if (!existsSync9(paths.root)) {
34010
- console.error('No .rafters/ directory found. Run "rafters init" first.');
34011
- process.exit(1);
34012
- }
34013
- console.log("Starting Rafters Studio...");
34014
- console.log(`Project: ${cwd}`);
34015
- console.log(`Tokens: ${paths.tokens}`);
34016
- console.log("");
34017
- process.env.RAFTERS_PROJECT_PATH = cwd;
34018
- process.env.RAFTERS_TOKENS_PATH = paths.tokens;
34019
- const server = await createServer({
34020
- plugins: [studioApiPlugin()],
34021
- server: {
34022
- port: 7777
34023
- },
34024
- resolve: {
34025
- alias: {
34026
- "@rafters-output": resolve6(cwd, ".rafters", "output")
34027
- }
34028
- }
34029
- });
34030
- await server.listen();
34031
- server.printUrls();
34032
- }
34033
-
34034
33185
  // src/index.ts
34035
33186
  var program = new Command();
34036
33187
  program.name("rafters").description("Design system CLI - scaffold tokens and serve MCP").version(VERSION);
@@ -34040,7 +33191,6 @@ program.command("init").description("Initialize .rafters/ with default tokens an
34040
33191
  ).option("--agent", "Output JSON for machine consumption").action(withErrorHandler(init));
34041
33192
  program.command("add").description("Add rafters components to the project").argument("[components...]", "Component names to add").option("--list", "List available components").option("--overwrite", "Overwrite existing component files").option("--update", "Re-fetch named components from registry").option("--update-all", "Re-fetch all installed components from registry").option("--registry-url <url>", "Custom registry URL").option("--agent", "Output JSON for machine consumption").action(withErrorHandler(add));
34042
33193
  program.command("mcp").description("Start MCP server for AI agent access (stdio)").option("--project-root <path>", "Explicit project root (skips .rafters/ discovery)").action(mcp);
34043
- program.command("studio").description("Open Studio UI for visual token editing").action(studio);
34044
33194
  program.command("agents").description(
34045
33195
  "Write or update the rafters contract block in AGENTS.md for non-Claude-Code agent hosts"
34046
33196
  ).action(withErrorHandler(agents));
@@ -186,7 +186,6 @@ declare const RegistryItemSchema: z.ZodObject<{
186
186
  devDependencies: z.ZodDefault<z.ZodArray<z.ZodString>>;
187
187
  }, z.core.$strip>>;
188
188
  rules: z.ZodDefault<z.ZodArray<z.ZodString>>;
189
- composites: z.ZodDefault<z.ZodArray<z.ZodString>>;
190
189
  intelligence: z.ZodOptional<z.ZodObject<{
191
190
  cognitiveLoad: z.ZodOptional<z.ZodNumber>;
192
191
  attentionEconomics: z.ZodOptional<z.ZodString>;
@@ -198,54 +197,6 @@ declare const RegistryItemSchema: z.ZodObject<{
198
197
  nevers: z.ZodDefault<z.ZodArray<z.ZodString>>;
199
198
  }, z.core.$strip>>;
200
199
  }, z.core.$strip>>;
201
- facets: z.ZodDefault<z.ZodRecord<z.ZodEnum<{
202
- react: "react";
203
- astro: "astro";
204
- vue: "vue";
205
- svelte: "svelte";
206
- wc: "wc";
207
- }> & z.core.$partial, z.ZodObject<{
208
- props: z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
209
- type: z.ZodLiteral<"enum">;
210
- values: z.ZodArray<z.ZodString>;
211
- default: z.ZodOptional<z.ZodString>;
212
- required: z.ZodOptional<z.ZodBoolean>;
213
- constraint: z.ZodOptional<z.ZodObject<{
214
- when: z.ZodObject<{
215
- prop: z.ZodString;
216
- matches: z.ZodString;
217
- }, z.core.$strip>;
218
- requires: z.ZodObject<{
219
- prop: z.ZodString;
220
- }, z.core.$strip>;
221
- }, z.core.$strip>>;
222
- }, z.core.$strip>, z.ZodObject<{
223
- type: z.ZodLiteral<"boolean">;
224
- default: z.ZodOptional<z.ZodBoolean>;
225
- required: z.ZodOptional<z.ZodBoolean>;
226
- }, z.core.$strip>, z.ZodObject<{
227
- type: z.ZodLiteral<"string">;
228
- default: z.ZodOptional<z.ZodString>;
229
- required: z.ZodOptional<z.ZodBoolean>;
230
- }, z.core.$strip>, z.ZodObject<{
231
- type: z.ZodLiteral<"number">;
232
- default: z.ZodOptional<z.ZodNumber>;
233
- required: z.ZodOptional<z.ZodBoolean>;
234
- }, z.core.$strip>, z.ZodObject<{
235
- type: z.ZodLiteral<"grammar">;
236
- grammar: z.ZodArray<z.ZodString>;
237
- vocab: z.ZodString;
238
- onInvalid: z.ZodLiteral<"silent-noop">;
239
- default: z.ZodOptional<z.ZodString>;
240
- }, z.core.$strip>, z.ZodObject<{
241
- type: z.ZodLiteral<"deprecated">;
242
- deprecatedFor: z.ZodString;
243
- }, z.core.$strip>], "type">>;
244
- slots: z.ZodOptional<z.ZodArray<z.ZodString>>;
245
- events: z.ZodOptional<z.ZodArray<z.ZodString>>;
246
- snippet: z.ZodString;
247
- }, z.core.$strip>>>;
248
- parent: z.ZodOptional<z.ZodString>;
249
200
  }, z.core.$strip>;
250
201
  type RegistryItem = z.infer<typeof RegistryItemSchema>;
251
202
  /**
@@ -77,13 +77,7 @@ var RegistryItemSchema = z.object({
77
77
  primitives: z.array(z.string()),
78
78
  files: z.array(RegistryFileSchema),
79
79
  rules: z.array(z.string()).default([]),
80
- composites: z.array(z.string()).default([]),
81
- intelligence: RegistryItemIntelligenceSchema.optional(),
82
- // Per-target facets. zod v4's `z.record(enum, ...)` demands EVERY enum key be
83
- // present; a component built for only some targets must parse, so this is a
84
- // partial record (only the built targets appear).
85
- facets: z.partialRecord(ComponentTargetSchema, FacetSchema).default({}),
86
- parent: z.string().optional()
80
+ intelligence: RegistryItemIntelligenceSchema.optional()
87
81
  });
88
82
  var RegistryIndexSchema = z.object({
89
83
  name: z.string(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rafters",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
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",
@@ -46,8 +46,7 @@
46
46
  "commander": "^13.0.0",
47
47
  "css-tree": "^3.2.1",
48
48
  "execa": "^9.6.1",
49
- "ora": "^9.0.0",
50
- "vite": "^8.0.0"
49
+ "ora": "^9.0.0"
51
50
  },
52
51
  "devDependencies": {
53
52
  "@playwright/test": "catalog:",
@@ -56,7 +55,6 @@
56
55
  "@rafters/design-tokens": "workspace:*",
57
56
  "@rafters/math-utils": "workspace:*",
58
57
  "@rafters/shared": "workspace:*",
59
- "@rafters/studio": "workspace:*",
60
58
  "@types/css-tree": "^2.3.11",
61
59
  "@types/node": "catalog:",
62
60
  "playwright-bdd": "catalog:",