@hyperframes/core 0.7.28 → 0.7.30
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/figma/assetSnippet.d.ts +3 -0
- package/dist/figma/assetSnippet.d.ts.map +1 -0
- package/dist/figma/assetSnippet.js +18 -0
- package/dist/figma/assetSnippet.js.map +1 -0
- package/dist/figma/bindings.d.ts +38 -0
- package/dist/figma/bindings.d.ts.map +1 -0
- package/dist/figma/bindings.js +84 -0
- package/dist/figma/bindings.js.map +1 -0
- package/dist/figma/client.d.ts +67 -0
- package/dist/figma/client.d.ts.map +1 -0
- package/dist/figma/client.js +134 -0
- package/dist/figma/client.js.map +1 -0
- package/dist/figma/color.d.ts +3 -0
- package/dist/figma/color.d.ts.map +1 -0
- package/dist/figma/color.js +23 -0
- package/dist/figma/color.js.map +1 -0
- package/dist/figma/emitTimelineScript.d.ts +3 -0
- package/dist/figma/emitTimelineScript.d.ts.map +1 -0
- package/dist/figma/emitTimelineScript.js +42 -0
- package/dist/figma/emitTimelineScript.js.map +1 -0
- package/dist/figma/freeze.d.ts +16 -0
- package/dist/figma/freeze.d.ts.map +1 -0
- package/dist/figma/freeze.js +67 -0
- package/dist/figma/freeze.js.map +1 -0
- package/dist/figma/index.d.ts +21 -0
- package/dist/figma/index.d.ts.map +1 -0
- package/dist/figma/index.js +15 -0
- package/dist/figma/index.js.map +1 -0
- package/dist/figma/jsonl.d.ts +3 -0
- package/dist/figma/jsonl.d.ts.map +1 -0
- package/dist/figma/jsonl.js +22 -0
- package/dist/figma/jsonl.js.map +1 -0
- package/dist/figma/manifest.d.ts +17 -0
- package/dist/figma/manifest.d.ts.map +1 -0
- package/dist/figma/manifest.js +96 -0
- package/dist/figma/manifest.js.map +1 -0
- package/dist/figma/motionEase.d.ts +3 -0
- package/dist/figma/motionEase.d.ts.map +1 -0
- package/dist/figma/motionEase.js +45 -0
- package/dist/figma/motionEase.js.map +1 -0
- package/dist/figma/motionToGsap.d.ts +3 -0
- package/dist/figma/motionToGsap.d.ts.map +1 -0
- package/dist/figma/motionToGsap.js +61 -0
- package/dist/figma/motionToGsap.js.map +1 -0
- package/dist/figma/nodeDocument.d.ts +4 -0
- package/dist/figma/nodeDocument.d.ts.map +1 -0
- package/dist/figma/nodeDocument.js +26 -0
- package/dist/figma/nodeDocument.js.map +1 -0
- package/dist/figma/nodeToHtml.d.ts +31 -0
- package/dist/figma/nodeToHtml.d.ts.map +1 -0
- package/dist/figma/nodeToHtml.js +244 -0
- package/dist/figma/nodeToHtml.js.map +1 -0
- package/dist/figma/parseFigmaRef.d.ts +3 -0
- package/dist/figma/parseFigmaRef.d.ts.map +1 -0
- package/dist/figma/parseFigmaRef.js +31 -0
- package/dist/figma/parseFigmaRef.js.map +1 -0
- package/dist/figma/resolveBindings.d.ts +31 -0
- package/dist/figma/resolveBindings.d.ts.map +1 -0
- package/dist/figma/resolveBindings.js +77 -0
- package/dist/figma/resolveBindings.js.map +1 -0
- package/dist/figma/sanitizeSvg.d.ts +18 -0
- package/dist/figma/sanitizeSvg.d.ts.map +1 -0
- package/dist/figma/sanitizeSvg.js +52 -0
- package/dist/figma/sanitizeSvg.js.map +1 -0
- package/dist/figma/tokensToVariables.d.ts +40 -0
- package/dist/figma/tokensToVariables.d.ts.map +1 -0
- package/dist/figma/tokensToVariables.js +118 -0
- package/dist/figma/tokensToVariables.js.map +1 -0
- package/dist/figma/types.d.ts +81 -0
- package/dist/figma/types.d.ts.map +1 -0
- package/dist/figma/types.js +2 -0
- package/dist/figma/types.js.map +1 -0
- package/dist/generated/runtime-inline.js +1 -1
- package/dist/generated/runtime-inline.js.map +1 -1
- package/dist/hyperframe.runtime.iife.js +24 -24
- package/dist/runtime/analytics.d.ts +19 -0
- package/dist/runtime/analytics.d.ts.map +1 -0
- package/dist/runtime/analytics.js +31 -0
- package/dist/runtime/analytics.js.map +1 -0
- package/dist/runtime/diagnostics.d.ts +2 -0
- package/dist/runtime/diagnostics.d.ts.map +1 -0
- package/dist/runtime/diagnostics.js +53 -0
- package/dist/runtime/diagnostics.js.map +1 -0
- package/dist/runtime/globals.d.ts +12 -0
- package/dist/runtime/globals.d.ts.map +1 -0
- package/dist/runtime/globals.js +4 -0
- package/dist/runtime/globals.js.map +1 -0
- package/dist/runtime/positionEdits.d.ts +47 -0
- package/dist/runtime/positionEdits.d.ts.map +1 -0
- package/dist/runtime/positionEdits.js +166 -0
- package/dist/runtime/positionEdits.js.map +1 -0
- package/package.json +12 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assetSnippet.d.ts","sourceRoot":"","sources":["../../src/figma/assetSnippet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAUjE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,mBAAmB,GAAG,YAAY,CAS3E"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function escapeAttr(value) {
|
|
2
|
+
return value
|
|
3
|
+
.replace(/&/g, "&")
|
|
4
|
+
.replace(/</g, "<")
|
|
5
|
+
.replace(/>/g, ">")
|
|
6
|
+
.replace(/"/g, """);
|
|
7
|
+
}
|
|
8
|
+
export function buildAssetSnippet(record) {
|
|
9
|
+
// Every interpolated attribute is escaped — path and nodeId are
|
|
10
|
+
// system-generated today, but defense-in-depth is free here.
|
|
11
|
+
const alt = escapeAttr(record.description ?? record.id);
|
|
12
|
+
const src = escapeAttr(record.path);
|
|
13
|
+
const w = record.width !== undefined ? ` width="${record.width}"` : "";
|
|
14
|
+
const h = record.height !== undefined ? ` height="${record.height}"` : "";
|
|
15
|
+
const html = `<img src="${src}" alt="${alt}"${w}${h} data-figma-id="${escapeAttr(record.provenance.nodeId)}" />`;
|
|
16
|
+
return { path: record.path, html };
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=assetSnippet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assetSnippet.js","sourceRoot":"","sources":["../../src/figma/assetSnippet.ts"],"names":[],"mappings":"AAEA,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAA2B;IAC3D,gEAAgE;IAChE,6DAA6D;IAC7D,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACvE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,MAAM,IAAI,GAAG,aAAa,GAAG,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,mBAAmB,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC;IACjH,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binding index — the machine-readable join between figma variable/style
|
|
3
|
+
* identities and composition variables (design spec §7.1).
|
|
4
|
+
*
|
|
5
|
+
* Lives at .media/figma-bindings.jsonl, next to (but separate from) the
|
|
6
|
+
* human-readable figma-tokens.json sidecar. Resolution is exact-ID only:
|
|
7
|
+
* a missed link bakes a correct literal; a wrong link silently changes
|
|
8
|
+
* color at the next brand refresh. Never match by value or name.
|
|
9
|
+
*/
|
|
10
|
+
export interface FigmaBindingRecord {
|
|
11
|
+
kind: "binding";
|
|
12
|
+
/** the figma variable/style id as it appears in node data (exact match key) */
|
|
13
|
+
figmaId: string;
|
|
14
|
+
/** stable cross-file identity, when known ("id and key are stable over the lifetime") */
|
|
15
|
+
key?: string;
|
|
16
|
+
sourceFileKey: string;
|
|
17
|
+
/** semantic→primitive alias chain, directly-bound id first */
|
|
18
|
+
aliasChain?: string[];
|
|
19
|
+
compositionVariableId: string;
|
|
20
|
+
brandRole?: string;
|
|
21
|
+
/** figma file version at import time — staleness check */
|
|
22
|
+
version: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function readBindings(projectDir: string): FigmaBindingRecord[];
|
|
25
|
+
export declare function appendBinding(projectDir: string, record: FigmaBindingRecord): void;
|
|
26
|
+
/**
|
|
27
|
+
* Upsert by figmaId: re-running a tokens import must REPLACE that file's
|
|
28
|
+
* stale binding rows, not append duplicates — `findBindingByFigmaId`
|
|
29
|
+
* returns the first match, so appended duplicates would pin lookups to the
|
|
30
|
+
* stale record forever. Library rows and other files' bindings survive.
|
|
31
|
+
*/
|
|
32
|
+
export declare function upsertBindings(projectDir: string, records: FigmaBindingRecord[]): void;
|
|
33
|
+
/** Exact-ID lookup, checking alias chains too. Never value/name matching. */
|
|
34
|
+
export declare function findBindingByFigmaId(projectDir: string, figmaId: string): FigmaBindingRecord | null;
|
|
35
|
+
/** Answered "which file is this library?" mappings — asked once per project. */
|
|
36
|
+
export declare function readLibraryMap(projectDir: string): Record<string, string>;
|
|
37
|
+
export declare function recordLibraryFile(projectDir: string, libraryKey: string, fileKey: string): void;
|
|
38
|
+
//# sourceMappingURL=bindings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindings.d.ts","sourceRoot":"","sources":["../../src/figma/bindings.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,SAAS,CAAC;IAChB,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAC;IAChB,yFAAyF;IACzF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;CACjB;AA6CD,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAErE;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAElF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAQtF;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,kBAAkB,GAAG,IAAI,CAM3B;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMzE;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAG/F"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binding index — the machine-readable join between figma variable/style
|
|
3
|
+
* identities and composition variables (design spec §7.1).
|
|
4
|
+
*
|
|
5
|
+
* Lives at .media/figma-bindings.jsonl, next to (but separate from) the
|
|
6
|
+
* human-readable figma-tokens.json sidecar. Resolution is exact-ID only:
|
|
7
|
+
* a missed link bakes a correct literal; a wrong link silently changes
|
|
8
|
+
* color at the next brand refresh. Never match by value or name.
|
|
9
|
+
*/
|
|
10
|
+
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { readJsonlValues } from "./jsonl.js";
|
|
13
|
+
import { mediaDir } from "./manifest.js";
|
|
14
|
+
const BINDINGS_FILE = "figma-bindings.jsonl";
|
|
15
|
+
function bindingsPath(projectDir) {
|
|
16
|
+
return join(mediaDir(projectDir), BINDINGS_FILE);
|
|
17
|
+
}
|
|
18
|
+
function isRecord(value) {
|
|
19
|
+
return typeof value === "object" && value !== null;
|
|
20
|
+
}
|
|
21
|
+
function isBindingRecord(value) {
|
|
22
|
+
return (isRecord(value) &&
|
|
23
|
+
value.kind === "binding" &&
|
|
24
|
+
typeof value.figmaId === "string" &&
|
|
25
|
+
typeof value.sourceFileKey === "string" &&
|
|
26
|
+
typeof value.compositionVariableId === "string" &&
|
|
27
|
+
typeof value.version === "string");
|
|
28
|
+
}
|
|
29
|
+
function isLibraryRecord(value) {
|
|
30
|
+
return (isRecord(value) &&
|
|
31
|
+
value.kind === "library" &&
|
|
32
|
+
typeof value.libraryKey === "string" &&
|
|
33
|
+
typeof value.fileKey === "string");
|
|
34
|
+
}
|
|
35
|
+
function readLines(projectDir) {
|
|
36
|
+
return readJsonlValues(bindingsPath(projectDir));
|
|
37
|
+
}
|
|
38
|
+
function appendLine(projectDir, record) {
|
|
39
|
+
mkdirSync(mediaDir(projectDir), { recursive: true });
|
|
40
|
+
appendFileSync(bindingsPath(projectDir), JSON.stringify(record) + "\n");
|
|
41
|
+
}
|
|
42
|
+
export function readBindings(projectDir) {
|
|
43
|
+
return readLines(projectDir).filter(isBindingRecord);
|
|
44
|
+
}
|
|
45
|
+
export function appendBinding(projectDir, record) {
|
|
46
|
+
appendLine(projectDir, record);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Upsert by figmaId: re-running a tokens import must REPLACE that file's
|
|
50
|
+
* stale binding rows, not append duplicates — `findBindingByFigmaId`
|
|
51
|
+
* returns the first match, so appended duplicates would pin lookups to the
|
|
52
|
+
* stale record forever. Library rows and other files' bindings survive.
|
|
53
|
+
*/
|
|
54
|
+
export function upsertBindings(projectDir, records) {
|
|
55
|
+
const incoming = new Set(records.map((r) => r.figmaId));
|
|
56
|
+
const survivors = readLines(projectDir).filter((line) => !(isBindingRecord(line) && incoming.has(line.figmaId)));
|
|
57
|
+
mkdirSync(mediaDir(projectDir), { recursive: true });
|
|
58
|
+
const lines = [...survivors, ...records].map((r) => JSON.stringify(r)).join("\n");
|
|
59
|
+
writeFileSync(bindingsPath(projectDir), lines.length > 0 ? lines + "\n" : "");
|
|
60
|
+
}
|
|
61
|
+
/** Exact-ID lookup, checking alias chains too. Never value/name matching. */
|
|
62
|
+
export function findBindingByFigmaId(projectDir, figmaId) {
|
|
63
|
+
for (const b of readBindings(projectDir)) {
|
|
64
|
+
if (b.figmaId === figmaId)
|
|
65
|
+
return b;
|
|
66
|
+
if (b.aliasChain?.includes(figmaId))
|
|
67
|
+
return b;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
/** Answered "which file is this library?" mappings — asked once per project. */
|
|
72
|
+
export function readLibraryMap(projectDir) {
|
|
73
|
+
const map = {};
|
|
74
|
+
for (const r of readLines(projectDir)) {
|
|
75
|
+
if (isLibraryRecord(r))
|
|
76
|
+
map[r.libraryKey] = r.fileKey;
|
|
77
|
+
}
|
|
78
|
+
return map;
|
|
79
|
+
}
|
|
80
|
+
export function recordLibraryFile(projectDir, libraryKey, fileKey) {
|
|
81
|
+
const record = { kind: "library", libraryKey, fileKey };
|
|
82
|
+
appendLine(projectDir, record);
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=bindings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindings.js","sourceRoot":"","sources":["../../src/figma/bindings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { FigmaAssetFormat, FigmaRef } from "./types";
|
|
2
|
+
/** Typed capability/transport failures per design spec §4.4. */
|
|
3
|
+
export type FigmaClientErrorCode = "NO_TOKEN" | "BAD_TOKEN" | "FORBIDDEN" | "REQUIRES_ENTERPRISE" | "RATE_LIMITED" | "RENDER_FAILED" | "NODE_NOT_FOUND" | "HTTP_ERROR";
|
|
4
|
+
export declare class FigmaClientError extends Error {
|
|
5
|
+
readonly code: FigmaClientErrorCode;
|
|
6
|
+
readonly status?: number;
|
|
7
|
+
constructor(code: FigmaClientErrorCode, message: string, status?: number);
|
|
8
|
+
}
|
|
9
|
+
/** Injectable fetch so tests never touch the network. */
|
|
10
|
+
export type FigmaFetch = (url: string, init?: {
|
|
11
|
+
headers?: Record<string, string>;
|
|
12
|
+
}) => Promise<Response>;
|
|
13
|
+
export interface RenderNodeOptions {
|
|
14
|
+
format: FigmaAssetFormat;
|
|
15
|
+
scale?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface RenderedNode {
|
|
18
|
+
/** short-lived figma CDN url — freeze it immediately */
|
|
19
|
+
url: string;
|
|
20
|
+
ext: FigmaAssetFormat;
|
|
21
|
+
}
|
|
22
|
+
export interface FigmaVariablePayload {
|
|
23
|
+
name: string;
|
|
24
|
+
key?: string;
|
|
25
|
+
resolvedType?: string;
|
|
26
|
+
valuesByMode?: Record<string, unknown>;
|
|
27
|
+
variableCollectionId?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface FigmaVariablesResult {
|
|
30
|
+
variables: Record<string, FigmaVariablePayload>;
|
|
31
|
+
variableCollections: Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
export interface FigmaStyleMeta {
|
|
34
|
+
key: string;
|
|
35
|
+
name: string;
|
|
36
|
+
style_type: string;
|
|
37
|
+
node_id?: string;
|
|
38
|
+
description?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Raw figma node document from GET /v1/files/:key/nodes. Field-level shape
|
|
41
|
+
* is consumed by nodeToHtml; kept loose here on purpose — consumers narrow
|
|
42
|
+
* children/fills/etc themselves. */
|
|
43
|
+
export interface FigmaNodeDocument {
|
|
44
|
+
id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
type: string;
|
|
47
|
+
[field: string]: unknown;
|
|
48
|
+
}
|
|
49
|
+
export interface FigmaFileVersion {
|
|
50
|
+
version: string;
|
|
51
|
+
lastModified: string;
|
|
52
|
+
}
|
|
53
|
+
export interface FigmaClient {
|
|
54
|
+
renderNode(ref: FigmaRef, opts: RenderNodeOptions): Promise<RenderedNode>;
|
|
55
|
+
imageFills(fileKey: string): Promise<Map<string, string>>;
|
|
56
|
+
variables(fileKey: string): Promise<FigmaVariablesResult>;
|
|
57
|
+
styles(fileKey: string): Promise<FigmaStyleMeta[]>;
|
|
58
|
+
nodeTree(ref: FigmaRef): Promise<FigmaNodeDocument>;
|
|
59
|
+
fileVersion(fileKey: string): Promise<FigmaFileVersion>;
|
|
60
|
+
}
|
|
61
|
+
export interface FigmaClientOptions {
|
|
62
|
+
token: string;
|
|
63
|
+
fetch?: FigmaFetch;
|
|
64
|
+
baseUrl?: string;
|
|
65
|
+
}
|
|
66
|
+
export declare function createFigmaClient(options: FigmaClientOptions): FigmaClient;
|
|
67
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/figma/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
export class FigmaClientError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
status;
|
|
4
|
+
constructor(code, message, status) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "FigmaClientError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.status = status;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function requireNodeId(ref) {
|
|
12
|
+
if (!ref.nodeId)
|
|
13
|
+
throw new Error(`figma ref ${ref.fileKey} has no nodeId`);
|
|
14
|
+
return ref.nodeId;
|
|
15
|
+
}
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return typeof value === "object" && value !== null;
|
|
18
|
+
}
|
|
19
|
+
function optionalString(value) {
|
|
20
|
+
return typeof value === "string" ? value : undefined;
|
|
21
|
+
}
|
|
22
|
+
function toVariablePayload(payload) {
|
|
23
|
+
if (!isRecord(payload) || typeof payload.name !== "string")
|
|
24
|
+
return null;
|
|
25
|
+
return {
|
|
26
|
+
name: payload.name,
|
|
27
|
+
key: optionalString(payload.key),
|
|
28
|
+
resolvedType: optionalString(payload.resolvedType),
|
|
29
|
+
valuesByMode: isRecord(payload.valuesByMode) ? payload.valuesByMode : undefined,
|
|
30
|
+
variableCollectionId: optionalString(payload.variableCollectionId),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function createFigmaClient(options) {
|
|
34
|
+
const token = options.token.trim();
|
|
35
|
+
if (token === "") {
|
|
36
|
+
throw new FigmaClientError("NO_TOKEN", [
|
|
37
|
+
"FIGMA_TOKEN is missing. One-time setup:",
|
|
38
|
+
" 1. figma.com/settings → Security → Personal access tokens → Generate new token",
|
|
39
|
+
" 2. Scopes (read-only is all this integration ever needs — it never writes to figma):",
|
|
40
|
+
" File content: Read-only · File metadata: Read-only",
|
|
41
|
+
" Variables: Read-only (optional — brand variables, requires figma Enterprise;",
|
|
42
|
+
" without it `tokens` falls back to published styles)",
|
|
43
|
+
' 3. export FIGMA_TOKEN="figd_…" — add it to your shell profile or the project .env',
|
|
44
|
+
" so future sessions skip this step",
|
|
45
|
+
"Then re-run this command.",
|
|
46
|
+
].join("\n"));
|
|
47
|
+
}
|
|
48
|
+
const doFetch = options.fetch ?? ((url, init) => fetch(url, init));
|
|
49
|
+
const base = options.baseUrl ?? "https://api.figma.com";
|
|
50
|
+
async function get(path, enterpriseGated = false) {
|
|
51
|
+
const res = await doFetch(`${base}${path}`, {
|
|
52
|
+
headers: { "X-Figma-Token": token },
|
|
53
|
+
});
|
|
54
|
+
if (res.status === 401)
|
|
55
|
+
throw new FigmaClientError("BAD_TOKEN", "figma rejected the token (401) — it is expired or revoked. Re-mint at figma.com/settings → Security, then update FIGMA_TOKEN.", 401);
|
|
56
|
+
if (res.status === 403 && enterpriseGated)
|
|
57
|
+
throw new FigmaClientError("REQUIRES_ENTERPRISE", "figma variables require an Enterprise plan (403) — fall back to styles", 403);
|
|
58
|
+
if (res.status === 403)
|
|
59
|
+
throw new FigmaClientError("FORBIDDEN", "figma denied access (403) — the token is missing a read scope, or your account can't view this file. Check the token has File content: Read-only + File metadata: Read-only (figma.com/settings → Security) and that the file is visible to your account.", 403);
|
|
60
|
+
if (res.status === 429)
|
|
61
|
+
throw new FigmaClientError("RATE_LIMITED", "figma rate limit hit (429) — back off and retry", 429);
|
|
62
|
+
if (!res.ok)
|
|
63
|
+
throw new FigmaClientError("HTTP_ERROR", `figma request failed: HTTP ${res.status} ${path}`, res.status);
|
|
64
|
+
return res.json();
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
async renderNode(ref, opts) {
|
|
68
|
+
const nodeId = requireNodeId(ref);
|
|
69
|
+
const params = new URLSearchParams({ ids: nodeId, format: opts.format });
|
|
70
|
+
if (opts.scale !== undefined)
|
|
71
|
+
params.set("scale", String(opts.scale));
|
|
72
|
+
const body = await get(`/v1/images/${ref.fileKey}?${params}`);
|
|
73
|
+
const images = isRecord(body) && isRecord(body.images) ? body.images : {};
|
|
74
|
+
const url = images[nodeId];
|
|
75
|
+
if (typeof url !== "string" || url === "")
|
|
76
|
+
throw new FigmaClientError("RENDER_FAILED", `figma could not render node ${nodeId} as ${opts.format}`);
|
|
77
|
+
return { url, ext: opts.format };
|
|
78
|
+
},
|
|
79
|
+
async imageFills(fileKey) {
|
|
80
|
+
const body = await get(`/v1/files/${fileKey}/images`);
|
|
81
|
+
const meta = isRecord(body) && isRecord(body.meta) ? body.meta : {};
|
|
82
|
+
const images = isRecord(meta.images) ? meta.images : {};
|
|
83
|
+
const out = new Map();
|
|
84
|
+
for (const [ref, url] of Object.entries(images)) {
|
|
85
|
+
if (typeof url === "string")
|
|
86
|
+
out.set(ref, url);
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
},
|
|
90
|
+
async variables(fileKey) {
|
|
91
|
+
const body = await get(`/v1/files/${fileKey}/variables/local`, true);
|
|
92
|
+
const meta = isRecord(body) && isRecord(body.meta) ? body.meta : {};
|
|
93
|
+
const variables = isRecord(meta.variables) ? meta.variables : {};
|
|
94
|
+
const collections = isRecord(meta.variableCollections) ? meta.variableCollections : {};
|
|
95
|
+
const typed = {};
|
|
96
|
+
for (const [id, payload] of Object.entries(variables)) {
|
|
97
|
+
const v = toVariablePayload(payload);
|
|
98
|
+
if (v)
|
|
99
|
+
typed[id] = v;
|
|
100
|
+
}
|
|
101
|
+
return { variables: typed, variableCollections: collections };
|
|
102
|
+
},
|
|
103
|
+
async styles(fileKey) {
|
|
104
|
+
const body = await get(`/v1/files/${fileKey}/styles`);
|
|
105
|
+
const meta = isRecord(body) && isRecord(body.meta) ? body.meta : {};
|
|
106
|
+
const styles = Array.isArray(meta.styles) ? meta.styles : [];
|
|
107
|
+
return styles.filter((s) => isRecord(s) &&
|
|
108
|
+
typeof s.key === "string" &&
|
|
109
|
+
typeof s.name === "string" &&
|
|
110
|
+
typeof s.style_type === "string");
|
|
111
|
+
},
|
|
112
|
+
async nodeTree(ref) {
|
|
113
|
+
const nodeId = requireNodeId(ref);
|
|
114
|
+
const params = new URLSearchParams({ ids: nodeId, geometry: "paths" });
|
|
115
|
+
const body = await get(`/v1/files/${ref.fileKey}/nodes?${params}`);
|
|
116
|
+
const nodes = isRecord(body) && isRecord(body.nodes) ? body.nodes : {};
|
|
117
|
+
const entry = nodes[nodeId];
|
|
118
|
+
const doc = isRecord(entry) ? entry.document : undefined;
|
|
119
|
+
if (!isRecord(doc) ||
|
|
120
|
+
typeof doc.id !== "string" ||
|
|
121
|
+
typeof doc.name !== "string" ||
|
|
122
|
+
typeof doc.type !== "string")
|
|
123
|
+
throw new FigmaClientError("NODE_NOT_FOUND", `node ${nodeId} not found in ${ref.fileKey}`);
|
|
124
|
+
return { ...doc, id: doc.id, name: doc.name, type: doc.type };
|
|
125
|
+
},
|
|
126
|
+
async fileVersion(fileKey) {
|
|
127
|
+
const body = await get(`/v1/files/${fileKey}?depth=1`);
|
|
128
|
+
const version = isRecord(body) && typeof body.version === "string" ? body.version : "";
|
|
129
|
+
const lastModified = isRecord(body) && typeof body.lastModified === "string" ? body.lastModified : "";
|
|
130
|
+
return { version, lastModified };
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/figma/client.ts"],"names":[],"mappings":"AAaA,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,IAAI,CAAuB;IAC3B,MAAM,CAAU;IAEzB,YAAY,IAA0B,EAAE,OAAe,EAAE,MAAe;QACtE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAsED,SAAS,aAAa,CAAC,GAAa;IAClC,IAAI,CAAC,GAAG,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC;IAC3E,OAAO,GAAG,CAAC,MAAM,CAAC;AACpB,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAgB;IACzC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACxE,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,GAAG,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC;QAChC,YAAY,EAAE,cAAc,CAAC,OAAO,CAAC,YAAY,CAAC;QAClD,YAAY,EAAE,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS;QAC/E,oBAAoB,EAAE,cAAc,CAAC,OAAO,CAAC,oBAAoB,CAAC;KACnE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,OAA2B;IAC3D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IACnC,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,gBAAgB,CACxB,UAAU,EACV;YACE,yCAAyC;YACzC,kFAAkF;YAClF,wFAAwF;YACxF,+DAA+D;YAC/D,0FAA0F;YAC1F,uFAAuF;YACvF,sFAAsF;YACtF,wCAAwC;YACxC,2BAA2B;SAC5B,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAe,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/E,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,IAAI,uBAAuB,CAAC;IAExD,KAAK,UAAU,GAAG,CAAC,IAAY,EAAE,eAAe,GAAG,KAAK;QACtD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE;YAC1C,OAAO,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE;SACpC,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YACpB,MAAM,IAAI,gBAAgB,CACxB,WAAW,EACX,+HAA+H,EAC/H,GAAG,CACJ,CAAC;QACJ,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,eAAe;YACvC,MAAM,IAAI,gBAAgB,CACxB,qBAAqB,EACrB,wEAAwE,EACxE,GAAG,CACJ,CAAC;QACJ,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YACpB,MAAM,IAAI,gBAAgB,CACxB,WAAW,EACX,2PAA2P,EAC3P,GAAG,CACJ,CAAC;QACJ,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YACpB,MAAM,IAAI,gBAAgB,CACxB,cAAc,EACd,iDAAiD,EACjD,GAAG,CACJ,CAAC;QACJ,IAAI,CAAC,GAAG,CAAC,EAAE;YACT,MAAM,IAAI,gBAAgB,CACxB,YAAY,EACZ,8BAA8B,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,EAClD,GAAG,CAAC,MAAM,CACX,CAAC;QACJ,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;IACpB,CAAC;IAED,OAAO;QACL,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI;YACxB,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACzE,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;gBAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACtE,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,cAAc,GAAG,CAAC,OAAO,IAAI,MAAM,EAAE,CAAC,CAAC;YAC9D,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1E,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YAC3B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,EAAE;gBACvC,MAAM,IAAI,gBAAgB,CACxB,eAAe,EACf,+BAA+B,MAAM,OAAO,IAAI,CAAC,MAAM,EAAE,CAC1D,CAAC;YACJ,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;QACnC,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,OAAO;YACtB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,aAAa,OAAO,SAAS,CAAC,CAAC;YACtD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACpE,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YACxD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;YACtC,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAChD,IAAI,OAAO,GAAG,KAAK,QAAQ;oBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACjD,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;QAED,KAAK,CAAC,SAAS,CAAC,OAAO;YACrB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,aAAa,OAAO,kBAAkB,EAAE,IAAI,CAAC,CAAC;YACrE,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACpE,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;YACjE,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC;YACvF,MAAM,KAAK,GAAyC,EAAE,CAAC;YACvD,KAAK,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;gBACtD,MAAM,CAAC,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;gBACrC,IAAI,CAAC;oBAAE,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;YACvB,CAAC;YACD,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,CAAC;QAChE,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,OAAO;YAClB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,aAAa,OAAO,SAAS,CAAC,CAAC;YACtD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACpE,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7D,OAAO,MAAM,CAAC,MAAM,CAClB,CAAC,CAAC,EAAuB,EAAE,CACzB,QAAQ,CAAC,CAAC,CAAC;gBACX,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ;gBACzB,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;gBAC1B,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ,CACnC,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,GAAG;YAChB,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;YACvE,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,aAAa,GAAG,CAAC,OAAO,UAAU,MAAM,EAAE,CAAC,CAAC;YACnE,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;YAC5B,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;YACzD,IACE,CAAC,QAAQ,CAAC,GAAG,CAAC;gBACd,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ;gBAC1B,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAC5B,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAE5B,MAAM,IAAI,gBAAgB,CAAC,gBAAgB,EAAE,QAAQ,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAC7F,OAAO,EAAE,GAAG,GAAG,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;QAChE,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,OAAO;YACvB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,aAAa,OAAO,UAAU,CAAC,CAAC;YACvD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YACvF,MAAM,YAAY,GAChB,QAAQ,CAAC,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;YACnF,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;QACnC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../src/figma/color.ts"],"names":[],"mappings":"AAWA,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,SAAI,GAAG,MAAM,GAAG,IAAI,CAQ/E"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
function isRecord(value) {
|
|
2
|
+
return typeof value === "object" && value !== null;
|
|
3
|
+
}
|
|
4
|
+
function toHexByte(channel) {
|
|
5
|
+
return Math.round(channel * 255)
|
|
6
|
+
.toString(16)
|
|
7
|
+
.padStart(2, "0")
|
|
8
|
+
.toUpperCase();
|
|
9
|
+
}
|
|
10
|
+
/** figma {r,g,b,a?} floats (0..1) → #RRGGBB, or rgba() when translucent. */
|
|
11
|
+
export function figmaColorToCss(value, extraOpacity = 1) {
|
|
12
|
+
if (!isRecord(value))
|
|
13
|
+
return null;
|
|
14
|
+
const { r, g, b, a } = value;
|
|
15
|
+
if (typeof r !== "number" || typeof g !== "number" || typeof b !== "number")
|
|
16
|
+
return null;
|
|
17
|
+
const alpha = (typeof a === "number" ? a : 1) * extraOpacity;
|
|
18
|
+
if (alpha >= 1)
|
|
19
|
+
return `#${toHexByte(r)}${toHexByte(g)}${toHexByte(b)}`;
|
|
20
|
+
const c = (n) => Math.round(n * 255);
|
|
21
|
+
return `rgba(${c(r)}, ${c(g)}, ${c(b)}, ${Number(alpha.toFixed(4))})`;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=color.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.js","sourceRoot":"","sources":["../../src/figma/color.ts"],"names":[],"mappings":"AAAA,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED,SAAS,SAAS,CAAC,OAAe;IAChC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC;SAC7B,QAAQ,CAAC,EAAE,CAAC;SACZ,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;SAChB,WAAW,EAAE,CAAC;AACnB,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAAC,KAAc,EAAE,YAAY,GAAG,CAAC;IAC9D,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC;IAC7B,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzF,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC;IAC7D,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IACxE,MAAM,CAAC,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;IAC7C,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AACxE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitTimelineScript.d.ts","sourceRoot":"","sources":["../../src/figma/emitTimelineScript.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,YAAY,EAAE,MAAM,SAAS,CAAC;AA0BvD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CA0B7D"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function lit(value) {
|
|
2
|
+
return JSON.stringify(value);
|
|
3
|
+
}
|
|
4
|
+
function num(value) {
|
|
5
|
+
return Math.round(value * 1e6) / 1e6;
|
|
6
|
+
}
|
|
7
|
+
function val(value) {
|
|
8
|
+
return typeof value === "number" ? String(num(value)) : JSON.stringify(value);
|
|
9
|
+
}
|
|
10
|
+
function emitTween(t) {
|
|
11
|
+
const set = `tl.set(${lit(t.selector)}, { ${t.property}: ${val(t.initial)} }, 0);`;
|
|
12
|
+
const kf = t.steps
|
|
13
|
+
.map((s) => `{ ${t.property}: ${val(s.value)}, duration: ${num(s.duration)}, ease: ${lit(s.ease)} }`)
|
|
14
|
+
.join(", ");
|
|
15
|
+
const repeat = t.repeat > 0 ? `, repeat: ${t.repeat}` : "";
|
|
16
|
+
return [set, `tl.to(${lit(t.selector)}, { keyframes: [${kf}]${repeat} }, 0);`];
|
|
17
|
+
}
|
|
18
|
+
export function emitTimelineScript(spec) {
|
|
19
|
+
const lines = [];
|
|
20
|
+
// Guard the whole script: if the composition author forgot the GSAP or
|
|
21
|
+
// CustomEase CDN tag, warn loudly instead of throwing mid-script and
|
|
22
|
+
// silently never registering the timeline.
|
|
23
|
+
lines.push("(function () {");
|
|
24
|
+
const needsCustomEase = spec.customEases.length > 0;
|
|
25
|
+
const missing = needsCustomEase
|
|
26
|
+
? 'typeof gsap === "undefined" || typeof CustomEase === "undefined"'
|
|
27
|
+
: 'typeof gsap === "undefined"';
|
|
28
|
+
const libs = needsCustomEase ? "gsap + CustomEase" : "gsap";
|
|
29
|
+
lines.push(`if (${missing}) { console.warn(${lit(`figma timeline ${spec.timelineId}: ${libs} not loaded — add the CDN <script> tags before this one`)}); return; }`);
|
|
30
|
+
for (const ce of spec.customEases) {
|
|
31
|
+
const [x1, y1, x2, y2] = ce.bezier;
|
|
32
|
+
lines.push(`CustomEase.create(${lit(ce.name)}, "M0,0 C${num(x1)},${num(y1)} ${num(x2)},${num(y2)} 1,1");`);
|
|
33
|
+
}
|
|
34
|
+
lines.push("const tl = gsap.timeline({ paused: true });");
|
|
35
|
+
for (const t of spec.tweens)
|
|
36
|
+
lines.push(...emitTween(t));
|
|
37
|
+
lines.push("window.__timelines = window.__timelines || {};");
|
|
38
|
+
lines.push(`window.__timelines[${lit(spec.timelineId)}] = tl;`);
|
|
39
|
+
lines.push("})();");
|
|
40
|
+
return lines.join("\n");
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=emitTimelineScript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitTimelineScript.js","sourceRoot":"","sources":["../../src/figma/emitTimelineScript.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Freeze" = write asset bytes to local disk permanently so renders never
|
|
3
|
+
* re-fetch from figma (design spec §5) — not Object.freeze.
|
|
4
|
+
*/
|
|
5
|
+
export declare const MAX_FREEZE_BYTES: number;
|
|
6
|
+
export declare function exceedsFreezeCap(byteLength: number): boolean;
|
|
7
|
+
export declare function freezeBytes(bytes: Uint8Array, destPath: string): number;
|
|
8
|
+
/**
|
|
9
|
+
* Only figma-owned hosts may be frozen from a URL — render/CDN responses
|
|
10
|
+
* come from figma.com subdomains or figma's S3 buckets. Blocks SSRF via a
|
|
11
|
+
* crafted manifest/config URL (metadata endpoints, internal services).
|
|
12
|
+
*/
|
|
13
|
+
export declare function isAllowedFreezeUrl(url: string): boolean;
|
|
14
|
+
export declare function freezeUrl(url: string, destPath: string): Promise<number>;
|
|
15
|
+
export declare function freezeLocalFile(srcPath: string, destPath: string): void;
|
|
16
|
+
//# sourceMappingURL=freeze.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"freeze.d.ts","sourceRoot":"","sources":["../../src/figma/freeze.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,eAAO,MAAM,gBAAgB,QAAoB,CAAC;AAElD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAevE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAUvD;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS9E;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMvE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Freeze" = write asset bytes to local disk permanently so renders never
|
|
3
|
+
* re-fetch from figma (design spec §5) — not Object.freeze.
|
|
4
|
+
*/
|
|
5
|
+
import { copyFileSync, mkdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { dirname } from "node:path";
|
|
7
|
+
// ponytail: bound the write so a hostile/runaway source can't fill the disk.
|
|
8
|
+
export const MAX_FREEZE_BYTES = 256 * 1024 * 1024;
|
|
9
|
+
export function exceedsFreezeCap(byteLength) {
|
|
10
|
+
return byteLength > MAX_FREEZE_BYTES;
|
|
11
|
+
}
|
|
12
|
+
export function freezeBytes(bytes, destPath) {
|
|
13
|
+
if (bytes.length === 0)
|
|
14
|
+
throw new Error("freeze failed: empty bytes");
|
|
15
|
+
if (exceedsFreezeCap(bytes.length))
|
|
16
|
+
throw new Error(`freeze failed: ${bytes.length} bytes exceeds ${MAX_FREEZE_BYTES} cap`);
|
|
17
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
18
|
+
// Exclusive create; on EEXIST remove and retry — never write through an
|
|
19
|
+
// existing file or planted symlink (CodeQL js/insecure-temporary-file).
|
|
20
|
+
try {
|
|
21
|
+
writeFileSync(destPath, bytes, { flag: "wx" });
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
if (err.code !== "EEXIST")
|
|
25
|
+
throw err;
|
|
26
|
+
rmSync(destPath);
|
|
27
|
+
writeFileSync(destPath, bytes, { flag: "wx" });
|
|
28
|
+
}
|
|
29
|
+
return bytes.length;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Only figma-owned hosts may be frozen from a URL — render/CDN responses
|
|
33
|
+
* come from figma.com subdomains or figma's S3 buckets. Blocks SSRF via a
|
|
34
|
+
* crafted manifest/config URL (metadata endpoints, internal services).
|
|
35
|
+
*/
|
|
36
|
+
export function isAllowedFreezeUrl(url) {
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = new URL(url);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (parsed.protocol !== "https:")
|
|
45
|
+
return false;
|
|
46
|
+
const host = parsed.hostname;
|
|
47
|
+
return host === "figma.com" || host.endsWith(".figma.com") || host.endsWith(".amazonaws.com");
|
|
48
|
+
}
|
|
49
|
+
export async function freezeUrl(url, destPath) {
|
|
50
|
+
if (!isAllowedFreezeUrl(url))
|
|
51
|
+
throw new Error(`freeze failed: refusing non-figma url ${url} (https + figma hosts only)`);
|
|
52
|
+
const res = await fetch(url);
|
|
53
|
+
if (!res.ok)
|
|
54
|
+
throw new Error(`freeze failed: HTTP ${res.status}`);
|
|
55
|
+
const declared = Number(res.headers.get("content-length") ?? 0);
|
|
56
|
+
if (exceedsFreezeCap(declared))
|
|
57
|
+
throw new Error(`freeze failed: content-length ${declared} exceeds ${MAX_FREEZE_BYTES} cap`);
|
|
58
|
+
return freezeBytes(new Uint8Array(await res.arrayBuffer()), destPath);
|
|
59
|
+
}
|
|
60
|
+
export function freezeLocalFile(srcPath, destPath) {
|
|
61
|
+
const size = statSync(srcPath).size;
|
|
62
|
+
if (exceedsFreezeCap(size))
|
|
63
|
+
throw new Error(`freeze failed: ${size} bytes exceeds ${MAX_FREEZE_BYTES} cap`);
|
|
64
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
65
|
+
copyFileSync(srcPath, destPath);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=freeze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"freeze.js","sourceRoot":"","sources":["../../src/figma/freeze.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type * from "./types";
|
|
2
|
+
export { createFigmaClient, FigmaClientError } from "./client";
|
|
3
|
+
export type { FigmaClient, FigmaClientErrorCode, FigmaClientOptions, FigmaFetch, FigmaFileVersion, FigmaNodeDocument, FigmaStyleMeta, FigmaVariablePayload, FigmaVariablesResult, RenderedNode, RenderNodeOptions, } from "./client";
|
|
4
|
+
export { parseFigmaRef } from "./parseFigmaRef";
|
|
5
|
+
export { MAX_FREEZE_BYTES, exceedsFreezeCap, freezeBytes, freezeUrl, freezeLocalFile, } from "./freeze";
|
|
6
|
+
export { mediaDir, manifestPath, typeDirPath, isFigmaManifestRecord, readManifest, appendRecord, findByFigmaNode, nextId, } from "./manifest";
|
|
7
|
+
export { buildAssetSnippet } from "./assetSnippet";
|
|
8
|
+
export { sanitizeSvg } from "./sanitizeSvg";
|
|
9
|
+
export { appendBinding, upsertBindings, findBindingByFigmaId, readBindings, readLibraryMap, recordLibraryFile, } from "./bindings";
|
|
10
|
+
export type { FigmaBindingRecord } from "./bindings";
|
|
11
|
+
export { figmaColorToCss } from "./color";
|
|
12
|
+
export { resolveBindings } from "./resolveBindings";
|
|
13
|
+
export type { BindingSite, ResolvedBindingSite, ResolveBindingsResult } from "./resolveBindings";
|
|
14
|
+
export { nodeToHtml, slugify } from "./nodeToHtml";
|
|
15
|
+
export type { NodeToHtmlResult, RasterizeRequest } from "./nodeToHtml";
|
|
16
|
+
export { tokensToVariables } from "./tokensToVariables";
|
|
17
|
+
export type { CompositionVariableEntry, FigmaTokenSidecarEntry, FigmaTokensSidecar, TokenSource, TokensToVariablesResult, } from "./tokensToVariables";
|
|
18
|
+
export { mapEase } from "./motionEase";
|
|
19
|
+
export { motionToGsap } from "./motionToGsap";
|
|
20
|
+
export { emitTimelineScript } from "./emitTimelineScript";
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/figma/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,SAAS,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC/D,YAAY,EACV,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,GAClB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,eAAe,GAChB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,MAAM,GACP,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EACL,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EACV,wBAAwB,EACxB,sBAAsB,EACtB,kBAAkB,EAClB,WAAW,EACX,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { createFigmaClient, FigmaClientError } from "./client.js";
|
|
2
|
+
export { parseFigmaRef } from "./parseFigmaRef.js";
|
|
3
|
+
export { MAX_FREEZE_BYTES, exceedsFreezeCap, freezeBytes, freezeUrl, freezeLocalFile, } from "./freeze.js";
|
|
4
|
+
export { mediaDir, manifestPath, typeDirPath, isFigmaManifestRecord, readManifest, appendRecord, findByFigmaNode, nextId, } from "./manifest.js";
|
|
5
|
+
export { buildAssetSnippet } from "./assetSnippet.js";
|
|
6
|
+
export { sanitizeSvg } from "./sanitizeSvg.js";
|
|
7
|
+
export { appendBinding, upsertBindings, findBindingByFigmaId, readBindings, readLibraryMap, recordLibraryFile, } from "./bindings.js";
|
|
8
|
+
export { figmaColorToCss } from "./color.js";
|
|
9
|
+
export { resolveBindings } from "./resolveBindings.js";
|
|
10
|
+
export { nodeToHtml, slugify } from "./nodeToHtml.js";
|
|
11
|
+
export { tokensToVariables } from "./tokensToVariables.js";
|
|
12
|
+
export { mapEase } from "./motionEase.js";
|
|
13
|
+
export { motionToGsap } from "./motionToGsap.js";
|
|
14
|
+
export { emitTimelineScript } from "./emitTimelineScript.js";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|