Skip to content

Repository files navigation

@flyrank/dna

@flyrank/dna is a code-first visual intelligence package for extracting and operating on website design systems.

Use it to:

  • extract design language (colors, type, spacing, motion, components, voice, intent, stack intel),
  • return focused outputs by mode (overview, stack, dna, voice, design-language),
  • compare brands, benchmark a category, export competitive packs, diff designs, drift-check tokens, lint token files,
  • generate prompt packs for Claude, Cursor, Lovable, v0, and Codex.

Install

npm i @flyrank/dna

or

bun add @flyrank/dna

Runtime Requirements

  • Node.js runtime (server-side only).
  • Playwright is used internally for crawling/screenshot extraction.
  • Not intended for browser/client-side execution.

For Next.js, call it in:

  • route handlers (app/api/.../route.ts)
  • server actions
  • backend jobs/workers

Quick Start

import { init } from "@flyrank/dna";

const dl = init({
  cache: true,
  screenshots: true,
  outputDir: ".flydesign",
  emitFiles: true,
  pages: 5,
  responsive: true,
  interact: true,
  platforms: ["web", "ios"],
});

const design = await dl.extract("https://stripe.com");

if (design.ok) {
  console.log(design.data);
}

const overview = await dl.extract("https://stripe.com", { mode: "overview" });
const dna = await dl.extract("https://stripe.com", { mode: "dna" });
const layout = await dl.extract("https://stripe.com", {
  mode: "layout",
  aiInsights: true,
});

All public SDK methods now return a standardized envelope:

type SdkResponse<T> =
  | { ok: true; data: T; error: null; meta: { requestId: string; durationMs: number; version: string; source: string } }
  | { ok: false; data: null; error: { code: string; message: string; source: string; retryable: boolean }; meta: { requestId: string; durationMs: number; version: string; source: string } };

Init Options

interface FlyDesignInitOptions {
  cache?: boolean;          // default: true
  screenshots?: boolean;    // capture component screenshots
  screenshot?: boolean;     // alias of screenshots
  outputDir?: string;       // default: ".flydesign"
  emitFiles?: boolean;      // default: true (write md/json artifacts)
  artifactMode?: "disk" | "memory" | "both";
  pages?: number;           // crawl N internal pages for reconciliation
  responsive?: boolean;     // capture multiple breakpoints
  interact?: boolean;       // auto-interaction pass
  deepInteract?: boolean;   // alias of interact
  platforms?: ("web" | "ios" | "android" | "flutter" | "wordpress" | "all")[];
  mode?: ExtractMode;       // default extract mode
  profile?: "default" | "local" | "serverless" | "remote" | string;
  wsEndpoint?: string;      // connect to a remote Playwright/CDP browser
  browserArgs?: string[];   // extra Chromium args for local launch
  serverless?: boolean;     // prefer bundled Sparticuz Chromium on Linux/serverless
  timeoutMs?: number;       // browser launch/connect timeout, default: 30000
  features?: {
    responsive?: boolean;
    dark?: boolean;
    screenshots?: boolean;
    assetHeavy?: boolean;
  };
  aiInsights?: boolean | {
    areas?: ("layout" | "palette" | "typography" | "voice" | "brand-scaling")[];
    ai?: {
      mode?: "inline" | "deferred" | "off";
      fetch?: typeof fetch;
      maxRetries?: number;
    };
  };
  ignore?: string[];
}

Per-call overrides are supported:

const result = await dl.extract("https://flyrank.com", {
  mode: "design-language",
  pages: 6,
  responsive: true,
  interact: true,
  screenshot: true,
  platforms: ["all"],
  outputDir: ".flydesign",
  emitFiles: true,
});

Advanced init() configuration also supports grouped config:

const dl = init({
  profile: "serverless",
  crawl: {
    pages: 2,
    responsive: false,
  },
  artifacts: {
    outputDir: ".flydesign",
    artifactMode: "memory",
    emitFiles: false,
  },
  profiles: {
    serverless: {
      timeoutMs: 20_000,
      features: {
        assetHeavy: false,
      },
    },
  },
  ai: {
    enabled: true,
    provider: "openai",
    apiKey: process.env.OPENAI_API_KEY,
    mode: "deferred",
    maxRetries: 2,
  },
});

Runtime Profiles

Profiles let you set browser/runtime defaults once and reuse them across calls.

Built-in profiles:

Profile Built-in defaults Typical use
default no extra overrides existing behavior
local serverless: false local development with Playwright-managed Chromium
serverless serverless: true, emitFiles: false Lambda/serverless workers
remote emitFiles: false Browserless/remote Playwright connections

Precedence is deterministic:

  1. profile defaults
  2. init(...) values, including crawl / artifacts
  3. per-call extract(url, options)

You can also add custom named profiles:

const dl = init({
  profile: "ci",
  profiles: {
    ci: {
      timeoutMs: 45_000,
      browserArgs: ["--disable-dev-shm-usage"],
      artifactMode: "memory",
      features: {
        screenshots: false,
        assetHeavy: false,
      },
    },
  },
});

Browser Runtime Options

These options are available both at init(...) time and per extract(...) call:

Option What it does
profile selects a built-in or custom runtime profile
wsEndpoint switches execution to a remote Playwright/CDP browser
browserArgs passes extra launch args to local Chromium runs
serverless prefers bundled Sparticuz Chromium on Linux/serverless targets
timeoutMs controls browser launch/connect timeout
artifactMode controls disk vs in-memory artifact delivery
features.responsive enables or skips responsive capture work
features.dark enables or skips dark-mode crawl work
features.screenshots enables or skips screenshot-heavy work
features.assetHeavy enables or skips expensive asset-heavy passes

Runtime details are exposed on successful responses in result.meta.runtime, and mode/design results also preserve runtime metadata in result.data.meta.runtime.

Notes:

  • wsEndpoint changes browser mode to remote and uses browserTarget: "remote-cdp".
  • browserArgs only affect local launches.
  • serverless: true is mainly for Linux/serverless deployments. On macOS local development, the SDK falls back to the Playwright-managed browser because the bundled Sparticuz binary is Linux-only.

Runtime Examples

Local Chromium

const dl = init({
  profile: "local",
  browserArgs: ["--disable-dev-shm-usage"],
  timeoutMs: 45_000,
});

const result = await dl.extract("https://stripe.com", {
  mode: "overview",
});

Bundled Serverless Chromium

const dl = init({
  profile: "serverless",
  artifactMode: "memory",
  emitFiles: false,
  timeoutMs: 20_000,
  features: {
    responsive: false,
    dark: false,
    screenshots: false,
    assetHeavy: false,
  },
});

const result = await dl.extract("https://stripe.com", {
  mode: "design-language",
});

if (result.ok) {
  console.log(result.meta.runtime);
  console.log(result.data.artifacts?.files.length);
}

Remote Browser Execution

const dl = init({
  profile: "remote",
  wsEndpoint: process.env.PLAYWRIGHT_WS_ENDPOINT,
  artifactMode: "both",
  outputDir: ".flydesign/remote",
  timeoutMs: 25_000,
});

const result = await dl.extract("https://stripe.com", {
  mode: "stack",
});

if (result.ok) {
  console.log(result.meta.runtime?.browserMode);   // "remote"
  console.log(result.meta.runtime?.browserTarget); // "remote-cdp"
}

Extract Modes

Pass mode on extract(url, { mode }) to get focused payloads.

  • standard: full design extraction object
  • overview: concise DESIGN-style summary (intent/material/library/tokens/score)
  • stack: CMS/analytics/experimentation intel
  • dna: material language + imagery style + background patterns
  • voice: tone/pronoun/CTA verbs/button patterns/headings
  • layout: layout pattern, hero treatment, grid structure, rhythm, and overall feel
  • palette: palette-only response with brand palette score
  • fonts: font-family response with usage/source/provider links
  • assets: logo/font/image asset links with metadata
  • design-language: rich structured design-language response for deep UI/documentation use

Example:

const stack = await dl.extract("https://flyrank.com", { mode: "stack" });
const voice = await dl.extract("https://flyrank.com", { mode: "voice" });
const layout = await dl.extract("https://flyrank.com", { mode: "layout" });
const deep = await dl.extract("https://flyrank.com", { mode: "design-language" });

AI Insights In Extract

AI insights are optional and additive. When enabled, extract() uses the deterministic extraction as the baseline and appends an aiInsights field.

const result = await dl.extract("https://stripe.com", {
  mode: "design-language",
  aiInsights: true,
});

You can also scope AI enrichment to specific areas:

const result = await dl.extract("https://stripe.com", {
  aiInsights: {
    areas: ["layout", "palette", "typography", "brand-scaling"],
  },
});

Output Artifacts

Artifact delivery keeps the legacy file-writing behavior by default:

  • default: emitFiles: true with no artifactMode writes artifacts to disk only
  • artifactMode: "disk" always writes files to disk
  • artifactMode: "memory" returns artifact contents in the extraction response
  • artifactMode: "both" writes files to disk and returns in-memory artifact contents

Effective behavior:

artifactMode emitFiles Result
omitted true / omitted disk only
omitted false no artifacts written or returned
"disk" any disk only
"memory" false / omitted memory only
"memory" true promoted to "both"
"both" any disk + memory

When disk writing is enabled, extraction writes files into outputDir:

  • <site>-design-language.md
  • <site>-DESIGN.md
  • <site>-design-language.json
  • <site>-stack-intel.json
  • <site>-visual-dna.json
  • <site>-voice.json
  • <site>-library.json

When pages > 0, it also emits route reconciliation artifacts:

  • <site>-tokens-shared.json
  • <site>-tokens-routes/<slug>.json
  • <site>-routes-report.md

Disable file emission:

const dl = init({ emitFiles: false });
const inMemory = await dl.extract("https://stripe.com", { emitFiles: false });

Return artifacts in memory instead:

const dl = init({
  artifactMode: "memory",
  emitFiles: false,
});

const result = await dl.extract("https://stripe.com");

if (result.ok) {
  console.log(result.data.artifacts?.mode); // "memory"
  console.log(result.data.artifacts?.files[0]?.relativePath);
  console.log(result.meta.artifacts);
}

Write to disk and keep a copy in memory:

const result = await init({
  artifactMode: "both",
  outputDir: ".flydesign/artifacts",
}).extract("https://stripe.com");

Core API

Create a client:

import { init } from "@flyrank/dna";
const dl = init();

Methods:

  • extract(url, options?)
  • analyze(input)
  • grade(input)
  • remix(input, options?)
  • clone(input, options?)
  • apply(input, options?)
  • brands(urls, options?)
  • benchmark(urls, options?)
  • packCategory(urls, options?)
  • analyzeBrandIdentity(input)
  • analyzeDesignConsistency(input)
  • analyzeVisualSystem(input)
  • suggestEnhancements(input, options?)
  • generateBrandContent(input, options?)
  • analyzeAssets(input)
  • extractDesignPalette(input, options?)
  • extractFontFamilies(input, options?)
  • extractAssets(input, options?)
  • extractBrandVoice(input, options?)
  • extractTechStack(input, options?)
  • extractAiInsights(input, options?)
  • suggestBrandScaling(input, options?)
  • suggestPaletteEvolution(input, options?)
  • suggestTypographySystem(input, options?)
  • suggestLayoutDirections(input, options?)
  • runCombinedAiMethods(input, options?)
  • extractScreenshots(input, options?)
  • createSnapshot(input)
  • compareSnapshots(current, previous)
  • monitorBrand(input, previous?)
  • drift(url, { tokens, tolerance?, options? })
  • lint(file)
  • diff(left, right, options?)
  • visualDiff(beforeUrl, afterUrl, options?)
  • makePrompt(input)

input can be:

  • a URL string, or
  • a previously extracted standard design object.

Prompt Generation

Generate cloning/build prompts for multiple coding agents:

const prompts = await dl.makePrompt("https://apple.com");

if (prompts.ok) {
  console.log(prompts.data.claude);
  console.log(prompts.data.cursor);
  console.log(prompts.data.lovable);
  console.log(prompts.data.v0);
  console.log(prompts.data.codex);
}

Top-level helper is also exported:

import { makePrompt } from "@flyrank/dna";

const prompts = await makePrompt("https://linear.app", {
  cache: true,
  screenshots: true,
});

Modular Mini Methods

Use focused methods when you do not need the full design dossier:

const palette = await dl.extractDesignPalette("https://stripe.com");
const fonts = await dl.extractFontFamilies("https://stripe.com");
const assets = await dl.extractAssets("https://stripe.com");
const voice = await dl.extractBrandVoice("https://stripe.com");
const stack = await dl.extractTechStack("https://stripe.com");
const ai = await dl.extractAiInsights("https://stripe.com", { ai: true });

AI-focused mini methods:

const scaling = await dl.suggestBrandScaling("https://stripe.com", { ai: true });
const paletteEvolution = await dl.suggestPaletteEvolution("https://stripe.com", { ai: true });
const typographySystem = await dl.suggestTypographySystem("https://stripe.com", { ai: true });
const layoutDirections = await dl.suggestLayoutDirections("https://stripe.com", { ai: true });
const aiBundle = await dl.runCombinedAiMethods("https://stripe.com", { ai: true });

runCombinedAiMethods(...) performs a single AI insights generation pass and returns merged method-shaped results for:

  • suggestEnhancements
  • extractAiInsights
  • suggestPaletteEvolution
  • suggestBrandScaling
  • suggestTypographySystem

Screenshot Extraction

Capture reusable screenshots for downstream prompting, auditing, or cloning:

const shots = await dl.extractScreenshots("https://stripe.com", {
  components: true,
  fullPage: true,
  responsive: true,
  includeDark: true,
  outputDir: ".flydesign",
});

The screenshot response includes:

  • component screenshots with cluster labels and size metadata
  • full-page screenshots
  • responsive screenshots by breakpoint and color scheme

Next.js Example (Route Handler)

// app/api/design/route.ts
import { init } from "@flyrank/dna";

export const runtime = "nodejs";

export async function POST(req: Request) {
  const { url, mode = "overview" } = await req.json();

  const dl = init({
    cache: true,
    screenshot: true,
    outputDir: ".flydesign",
    emitFiles: true,
  });

  const result = await dl.extract(url, {
    mode,
    pages: 5,
    responsive: true,
    interact: true,
    platforms: ["web", "ios", "android"],
  });
  const prompts = await dl.makePrompt(url);

  return Response.json({ result, prompts });
}

Common Workflows

1) Grade one site

const dl = init();
const grade = await dl.grade("https://stripe.com");

2) Compare two sites

const dl = init();
const diff = await dl.diff("https://apple.com", "https://linear.app");

3) Multi-brand matrix

const dl = init();
const brands = await dl.brands([
  "https://stripe.com",
  "https://apple.com",
  "https://linear.app",
]);

4) Competitive benchmark

const dl = init({ outputDir: ".flydesign" });
const benchmark = await dl.benchmark([
  "https://stripe.com",
  "https://linear.app",
  "https://vercel.com",
  "https://supabase.com",
]);

if (benchmark.ok) {
  console.log(benchmark.data.baseline);
  console.log(benchmark.data.whitespace);
  console.log(benchmark.data.topUniqueSignals);
}

5) Brand audit

const audit = await dl.analyzeBrandIdentity("https://stripe.com");

if (audit.ok) {
  console.log(audit.data.summary);
  console.log(audit.data.strengths);
  console.log(audit.data.risks);
}

6) AI suggestions with optional OpenAI adapter

The core package keeps AI optional. Configure it only when needed:

const dl = init({
  ai: {
    enabled: true,
    provider: "openai",
    apiKey: process.env.OPENAI_API_KEY,
    model: "gpt-5-mini",
  },
});

const suggestions = await dl.suggestEnhancements("https://stripe.com", {
  ai: true,
});

Without an AI provider, the SDK still returns deterministic non-AI suggestions.

Runtime AI controls:

  • ai.mode: "inline" calls the configured provider immediately
  • ai.mode: "deferred" keeps the API deterministic and skips provider execution
  • ai.mode: "off" disables provider execution and returns deterministic fallbacks
  • ai.fetch injects a custom fetch implementation
  • ai.maxRetries controls adapter retry behavior

Configure them globally:

const dl = init({
  ai: {
    enabled: true,
    provider: "openai",
    apiKey: process.env.OPENAI_API_KEY,
    model: "gpt-5-mini",
    mode: "inline",
    fetch: fetch,
    maxRetries: 3,
  },
});

Or override per call:

const insights = await dl.extractAiInsights("https://stripe.com", {
  ai: {
    mode: "deferred",
    maxRetries: 1,
  },
});

const enrichedExtract = await dl.extract("https://stripe.com", {
  aiInsights: {
    areas: ["layout", "palette"],
    ai: {
      mode: "inline",
      maxRetries: 2,
    },
  },
});

7) Asset analysis

const assets = await dl.analyzeAssets("https://linear.app");

if (assets.ok) {
  console.log(assets.data.findings);
  console.log(assets.data.optimizations);
}

8) Scheduled monitoring snapshot flow

const previous = await dl.createSnapshot("https://stripe.com");
const current = await dl.monitorBrand("https://stripe.com", previous.ok ? previous.data : undefined);

if (current.ok) {
  console.log(current.data.diff);
  console.log(current.data.alerts);
}

This monitoring model is intended for cron jobs, workers, or route-triggered backend tasks, not a persistent in-process service.

The benchmark result includes:

  • brands: per-brand summaries, archetypes, positioning, fingerprints, distinctiveness
  • similarityMatrix: pairwise similarity scores
  • baseline: category dominant patterns and crowded lanes
  • whitespace: underused lanes and differentiation opportunities
  • topSharedPatterns: patterns common across the category
  • topUniqueSignals: strongest brand-specific signals

9) Export a category pack

const dl = init({ outputDir: ".flydesign" });
const pack = await dl.packCategory(
  [
    "https://stripe.com",
    "https://linear.app",
    "https://vercel.com",
  ],
  { outDir: ".flydesign/category-benchmark" },
);

console.log(pack.dir);
console.log(pack.files);

This writes:

  • benchmark-overview.json
  • similarity-matrix.json
  • category-baseline.json
  • whitespace-opportunities.json
  • brand-positioning-map.json
  • competitive-summary.md
  • competitive-summary.html
  • brand-cards/<brand>.json

6) Token drift check

const dl = init();
const drift = await dl.drift("https://flyrank.com", {
  tokens: "./tokens.json",
  tolerance: 8,
});

Build, Lint, Test (Contributors)

bun install
bun run lint
bun run build
bun run test

Open Source

  • License: MIT (see LICENSE)
  • Contribution guide: CONTRIBUTING.md
  • Code of Conduct: CODE_OF_CONDUCT.md
  • Security policy: SECURITY.md
  • Support guide: SUPPORT.md

Stack Intel

stack mode now surfaces broader deterministic detection categories including:

  • CMS
  • analytics
  • experimentation
  • hosting/deployment
  • frontend/runtime
  • design system hints
  • commerce tooling
  • observability
  • support/chat tooling
  • evidence snippets from scripts, metas, and class samples

Notes

  • Keep calls on backend/server runtime.
  • If using mode outputs, prefer passing URLs into downstream operations (grade, diff, clone) for best compatibility.
  • Use outputDir to isolate artifacts per project/environment.
  • Competitive benchmark flows are package-first and build on the same extraction pipeline as extract(), so they inherit pages, responsive, interact, and screenshot options.
  • OpenAI integration is optional by design; standard extraction and analysis workflows do not require it.

Releases

Packages

Contributors

Languages