@wy-ai-labs/figma-tables-core
v0.1.0
Published
Figma Auto Layout frames → table model → Markdown / JSON / XLSX (zero-dependency OOXML writer), a Figma link parser and a REST client with injectable fetch
Downloads
110
Maintainers
Readme
@wy-ai-labs/figma-tables-core
Figma Auto Layout frames → rectangular table model → Markdown / JSON / XLSX (zero-dependency OOXML writer), plus a Figma link parser and a REST client that never logs the token and always carries a timeout.
Part of the wy-ai-labs parts monorepo (packages/figma-tables-core) · contract v1 · no Python twin
Figma has no native table element: designers build tables by nesting Auto Layout frames (rows inside a vertical stack, cells inside each row — or columns inside a horizontal stack). This part reconstructs rows, columns and cells from that nesting, exactly the way the source products proved on real files, and exports the result.
Install
npm install @wy-ai-labs/figma-tables-core # Node ≥22.11, ESM only (import — no require)30-second usage
Offline — any Figma REST / plugin node tree you already have (no token, no network):
import { extractTables, tablesToMarkdown, tablesToJson, tablesToXlsx, CONTRACT_VERSION } from '@wy-ai-labs/figma-tables-core';
import { writeFile } from 'node:fs/promises';
const cell = (id, t) => ({ id, name: t, type: 'FRAME', layoutMode: 'HORIZONTAL', children: [{ id: `${id}t`, name: t, type: 'TEXT', characters: t }] });
const row = (id, ...cells) => ({ id, name: 'Row', type: 'FRAME', layoutMode: 'HORIZONTAL', children: cells.map((t, i) => cell(`${id}c${i}`, t)) });
const people = { id: '1:2', name: 'People', type: 'FRAME', layoutMode: 'VERTICAL', children: [row('r1', 'Name', 'Age'), row('r2', 'Alice', '30'), row('r3', 'Bob', '25')] };
const tables = extractTables(people); // [{ name: 'People', nodeId: '1:2', header: ['Name','Age'], rows: [['Alice','30'],['Bob','25']], columns: 2, source }]
console.log(CONTRACT_VERSION, tablesToMarkdown(tables));
console.log(tablesToJson(tables)); // [{ name, nodeId, columns, header, rows, records: [{ Name: 'Alice', Age: '30' }, …], source }]
await writeFile('tables.xlsx', tablesToXlsx(tables)); // one worksheet per table, bold header, zero dependenciesOnline — from a "Copy link" URL (the token comes from FIGMA_TOKEN or the option; it is sent only as X-Figma-Token):
import { createFigmaClient, extractFromFigma, tablesToMarkdown } from '@wy-ai-labs/figma-tables-core';
const client = createFigmaClient({ token: process.env.FIGMA_TOKEN }); // apiBase defaults to https://api.figma.com/v1
const { tables, file } = await extractFromFigma(client, 'https://www.figma.com/design/<fileKey>/Title?node-id=12-34');
console.log(file.name, file.lastModified, tablesToMarkdown(tables));A node link fetches just that node (GET /files/:key/nodes?ids=…); a whole-file link fetches the document (GET /files/:key) — link a node for large files. Every request carries a 120 000 ms budget (timeoutMs) and honours an AbortSignal.
API at a glance
| Area | Symbols |
|---|---|
| Links | parseFigmaLink(url) → { fileKey, nodeId, kind, branchOf } · normalizeNodeId('12-34') → '12:34' |
| Extraction | extractTables(nodeOrDocument, { firstRowHeader, minRows, minCols, includeHidden, nameFilter }) · tableFromNode · collectText · tableMatrix |
| Export | tableToMarkdown · tablesToMarkdown · tableToRecords · tablesToJson · tablesToXlsx(tables, { sheetPerTable, sheetNameMax }) · sanitizeSheetName |
| Spec workbook | buildSpecDocument(tables, { schema, title, generatedAt }) · tablesToSpecJson · buildSpecLayout · buildSpecWorkbook (sheets "Spec" + "Tables") · resolveAncestorTags · normalizeListKey · safeFileName |
| REST | createFigmaClient({ token, apiBase, fetch, timeoutMs, dispatcher, log }) → getFile / getNodes / health · extractFromFigma(client, link, options) |
| Errors | FigmaTablesError { code, status, retryable, upstreamText, cause } — codes bad_link · invalid_argument · auth · not_found · rate_limited · network · timeout · aborted · upstream |
Behind an HTTP proxy (enterprise networks)
Node's global fetch ignores HTTP(S)_PROXY. Inject a proxy-aware fetch instead of patching the part — for example with undici:
import { EnvHttpProxyAgent, fetch as proxyFetch } from 'undici';
import { createFigmaClient } from '@wy-ai-labs/figma-tables-core';
const client = createFigmaClient({
token: process.env.FIGMA_TOKEN,
fetch: proxyFetch,
dispatcher: new EnvHttpProxyAgent(), // reads HTTP_PROXY / HTTPS_PROXY / NO_PROXY
apiBase: process.env.FIGMA_API_BASE, // optional: an API gateway in front of api.figma.com
});
console.log(await client.health()); // { ok, apiBase, tokenConfigured, customFetch, dispatcherConfigured, proxyEnv: ['HTTPS_PROXY'], … }health() is offline diagnostics: it names which proxy variables are set, never their values.
Spec workbook
buildSpecWorkbook ports the source plugin's structured export: many tables → one record set. Rows are read through header aliases, classification values come from key=value tags in ancestor node names (category=Living; group=Oven on a page, section or frame — the nearest ancestor wins), rows sharing the key fields merge into one record (comma lists compare as sets), conflicting values are joined with line breaks, and every degradation is a warning (MISSING_HEADER, DUPLICATE_HEADER, MERGED_VALUE_CONFLICT, ANCESTOR_TAG_CONFLICT, INVALID_FLAG, FLAG_CONFLICT, MISSING_GROUP_KEY, MISSING_NODE_TAG, EMPTY_TABLE). Without a schema the columns are derived from the tags and headers found; with one you control keys, aliases, required headers, O/X flag columns, widths, tones and vertical merging — see CONTRACT.md → Configuration.
Contract
Public API, invariants and error model are fixed in CONTRACT.md; the tests in test/ are the contract suite (test/contract.test.mjs: one test per numbered invariant). Three invariants to know before depending on this part:
- Pure by default — parsing, extraction, export and the spec builder touch neither network nor filesystem; identical input gives identical output, byte-for-byte for XLSX.
- Never logs or echoes the token — it travels only in the
X-Figma-Tokenheader; log lines, error messages andupstreamTextnever contain it, and errors keep the upstream body (≥ 500 chars) with the status. - XLSX is a valid OOXML package with zero dependencies — STORE zip with verified CRC-32s, inline strings only, formula-looking text stays text, unique sheet names ≤ 31 chars.
An incompatible change bumps CONTRACT_VERSION and the major version together (CONTRACT.md → Compatibility).
Mined from
Extracted from a private source repository (figma2Excel, modules server/src/tableExtractor.ts, server/src/figmaClient.ts, server/src/figmaTypes.ts, server/src/index.ts, plugin/src/tableModel.ts, plugin/src/didExport.ts, plugin/src/workbookLayout.ts, plugin/src/workbookExport.ts, web/src/exporters.ts, developed 2026-07-10 – 2026-07-21), generalized and re-tested for publication. What was kept, generalized and stripped: PROVENANCE.md.
Used by
| Client | Role of this part there | |---|---| | (none yet — wave 0) | first candidates: a CLI and a local web app mirroring the source product (link → preview → download) |
Dependencies & budget
- Runtime dependencies: 0 (budget 0) — enforced by
scripts/check-budget.mjs. The XLSX writer, the zip packager and the REST client are built on Node's standard library only. - Allowed: other
@wy-ai-labs/*parts, pinned as caret ranges from the registry. Never a client, neverfile:/link:/ git URLs /../. - No private host or key as a default: the API base is the public
https://api.figma.com/v1(override viaapiBase/FIGMA_API_BASE), the token comes from the caller orFIGMA_TOKEN/FIGMA_ACCESS_TOKEN.
Gates
| Command | What | When |
|---|---|---|
| npm test | contract suite (node:test), offline, seconds | every save |
| npm run gates:core | dependency budget + independence + engines.node + tests | before every commit / PR |
| npm run gates:full | everything above | nightly / before release |
CI only calls these scripts (wy-ai-labs/.github → node-gates.yml). Releases: Keep a Changelog + npm publish --provenance.
License
MIT © 2026 waneekim
한국어 요약
- @wy-ai-labs/figma-tables-core — Figma 오토레이아웃 프레임을 표 모델로 복원해 Markdown · JSON · XLSX(의존성 0의 OOXML 작성기)로 내보내고, Figma 링크 파서와 토큰을 절대 로그에 남기지 않는 REST 클라이언트를 제공합니다. 공개 API·불변식·오류 모델은 CONTRACT.md에 고정되어 있고, 테스트가 곧 계약 스위트입니다.
- 설치
npm install @wy-ai-labs/figma-tables-core(Node ≥22, ESM). 런타임 의존성 0, 다른 part에만 핀 버전으로 의존, 사설 호스트 기본값 없음(기본 API는 공개https://api.figma.com/v1, 토큰은FIGMA_TOKEN). - 프록시 환경에서는 프록시 인식
fetch/dispatcher를 주입하세요(README → Behind an HTTP proxy). 비공개 저장소figma2Excel에서 추출·일반화했습니다(PROVENANCE.md). 커밋 전npm run gates:core.
