@animaapp/anima-sdk 0.3.1 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +26 -5
- package/dist/index.js +3681 -3613
- package/dist/index.js.map +1 -1
- package/example.env +2 -0
- package/package.json +5 -2
- package/tsconfig.json +1 -0
- package/.turbo/turbo-build.log +0 -12
- package/.turbo/turbo-dev.log +0 -23
- package/.turbo/turbo-test.log +0 -21
- package/src/anima.ts +0 -281
- package/src/dataStream.ts +0 -129
- package/src/errors.ts +0 -47
- package/src/figma/figmaError.ts +0 -120
- package/src/figma/index.ts +0 -2
- package/src/figma/utils.ts +0 -73
- package/src/index.ts +0 -7
- package/src/settings.ts +0 -68
- package/src/types.ts +0 -106
- package/src/utils/figma.spec.ts +0 -31
- package/src/utils/figma.ts +0 -59
- package/src/utils/files.ts +0 -95
- package/src/utils/index.ts +0 -2
- package/vite.config.ts +0 -24
package/example.env
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@animaapp/anima-sdk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Anima's JavaScript utilities library",
|
|
6
6
|
"author": "Anima App, Inc.",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"build": "vite build",
|
|
23
23
|
"dev": "vite build --watch",
|
|
24
24
|
"test": "vitest --watch=false",
|
|
25
|
-
"prepack": "yarn build"
|
|
25
|
+
"prepack": "yarn build",
|
|
26
|
+
"update-design-test-snapshot": "tsx tests/design/update.ts"
|
|
26
27
|
},
|
|
27
28
|
"dependencies": {
|
|
28
29
|
"@animaapp/http-client-figma": "^1.0.2",
|
|
@@ -30,6 +31,8 @@
|
|
|
30
31
|
"zod": "^3.24.1"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
34
|
+
"dotenv": "^16.4.7",
|
|
35
|
+
"tsx": "^4.19.3",
|
|
33
36
|
"vite": "^6.0.11",
|
|
34
37
|
"vite-plugin-dts": "^4.5.0",
|
|
35
38
|
"vite-tsconfig-paths": "^5.1.4",
|
package/tsconfig.json
CHANGED
package/.turbo/turbo-build.log
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
[36mvite v6.0.11 [32mbuilding for production...[36m[39m
|
|
3
|
-
[2K[1Gtransforming (1) [2msrc/index.ts[22m[2K[1G[32m✓[39m 14 modules transformed.
|
|
4
|
-
[2K[1Grendering chunks (1)...[2K[1G[32m
|
|
5
|
-
[36m[vite:dts][32m Start generate declaration files...[39m
|
|
6
|
-
[2K[1Gcomputing gzip size (0)...[2K[1Gcomputing gzip size (1)...[2K[1G[2mdist/[22m[36mindex.cjs [39m[1m[2m88.33 kB[22m[1m[22m[2m │ gzip: 23.70 kB[22m[2m │ map: 326.39 kB[22m
|
|
7
|
-
[32m[36m[vite:dts][32m Start rollup declaration files...[39m
|
|
8
|
-
Analysis will use the bundled TypeScript version 5.7.2
|
|
9
|
-
[32m[36m[vite:dts][32m Declaration files built in 2181ms.
|
|
10
|
-
[39m
|
|
11
|
-
[2K[1Grendering chunks (1)...[2K[1G[2K[1Gcomputing gzip size (1)...[2K[1G[2mdist/[22m[36mindex.js [39m[1m[2m125.80 kB[22m[1m[22m[2m │ gzip: 28.43 kB[22m[2m │ map: 340.50 kB[22m
|
|
12
|
-
[32m✓ built in 2.49s[39m
|
package/.turbo/turbo-dev.log
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
Debugger listening on ws://127.0.0.1:55823/cf2752e1-9866-4cd5-a3d7-03e9c40ba57e
|
|
3
|
-
For help, see: https://nodejs.org/en/docs/inspector
|
|
4
|
-
Debugger attached.
|
|
5
|
-
Debugger listening on ws://127.0.0.1:55831/f454b5ca-94c5-4de9-8eef-11c7c666f990
|
|
6
|
-
For help, see: https://nodejs.org/en/docs/inspector
|
|
7
|
-
Debugger attached.
|
|
8
|
-
[36mvite v6.0.11 [32mbuilding for production...[36m[39m
|
|
9
|
-
[36m
|
|
10
|
-
watching for file changes...[39m
|
|
11
|
-
[36m
|
|
12
|
-
build started...[39m
|
|
13
|
-
[2K[1Gtransforming (1) [2msrc/index.ts[22m[2K[1G[32m✓[39m 14 modules transformed.
|
|
14
|
-
[2K[1Grendering chunks (1)...[2K[1G[32m
|
|
15
|
-
[36m[vite:dts][32m Start generate declaration files...[39m
|
|
16
|
-
[2K[1Gcomputing gzip size (0)...[2K[1Gcomputing gzip size (1)...[2K[1G[2mdist/[22m[36mindex.cjs [39m[1m[2m88.33 kB[22m[1m[22m[2m │ gzip: 23.70 kB[22m[2m │ map: 326.39 kB[22m
|
|
17
|
-
[2K[1Grendering chunks (2)...[2K[1G[32m[36m[vite:dts][32m Start rollup declaration files...[39m
|
|
18
|
-
Analysis will use the bundled TypeScript version 5.7.2
|
|
19
|
-
[2K[1Gcomputing gzip size (2)...[2K[1G[2mdist/[22m[36mindex.js [39m[1m[2m125.80 kB[22m[1m[22m[2m │ gzip: 28.43 kB[22m[2m │ map: 340.50 kB[22m
|
|
20
|
-
[32m[36m[vite:dts][32m Declaration files built in 2146ms.
|
|
21
|
-
[39m
|
|
22
|
-
[36mbuilt in 2411ms.[39m
|
|
23
|
-
Waiting for the debugger to disconnect...
|
package/.turbo/turbo-test.log
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
[?25l
|
|
3
|
-
[1m[7m[36m RUN [39m[27m[22m [36mv3.0.5 [39m[90m/Users/macabeus/ApenasMeu/anima/anima-sdk/sdk[39m
|
|
4
|
-
|
|
5
|
-
[?2026h
|
|
6
|
-
[1m[33m ❯ [39m[22msrc/utils/figma.spec.ts[2m [queued][22m
|
|
7
|
-
|
|
8
|
-
[2m Test Files [22m[1m[32m0 passed[39m[22m[90m (1)[39m
|
|
9
|
-
[2m Tests [22m[1m[32m0 passed[39m[22m[90m (0)[39m
|
|
10
|
-
[2m Start at [22m21:43:55
|
|
11
|
-
[2m Duration [22m102ms
|
|
12
|
-
[?2026l[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K [32m✓[39m src/utils/figma.spec.ts [2m([22m[2m2 tests[22m[2m)[22m[90m 2[2mms[22m[39m
|
|
13
|
-
[32m✓[39m # figma[2m > [22m.formatToFigmaLink[2m > [22mgenerates a link with file key and node id
|
|
14
|
-
[32m✓[39m # figma[2m > [22m.formatToFigmaLink[2m > [22mwhen the "duplicate" flag is enabled[2m > [22mgenerates a link including the '/duplicated' path
|
|
15
|
-
|
|
16
|
-
[2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m
|
|
17
|
-
[2m Tests [22m [1m[32m2 passed[39m[22m[90m (2)[39m
|
|
18
|
-
[2m Start at [22m 21:43:55
|
|
19
|
-
[2m Duration [22m 234ms[2m (transform 24ms, setup 0ms, collect 20ms, tests 2ms, environment 0ms, prepare 44ms)[22m
|
|
20
|
-
|
|
21
|
-
[?25h
|
package/src/anima.ts
DELETED
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
import { CodegenError } from "./errors";
|
|
2
|
-
import { validateSettings } from "./settings";
|
|
3
|
-
import {
|
|
4
|
-
AnimaSDKResult,
|
|
5
|
-
GetCodeHandler,
|
|
6
|
-
GetCodeParams,
|
|
7
|
-
SSECodgenMessage,
|
|
8
|
-
} from "./types";
|
|
9
|
-
|
|
10
|
-
export type Auth =
|
|
11
|
-
| { token: string; teamId: string } // for Anima user, it's mandatory to have an associated team
|
|
12
|
-
| { token: string; userId?: string }; // for users from a 3rd-party integrations, they may have optionally a user id
|
|
13
|
-
|
|
14
|
-
export class Anima {
|
|
15
|
-
#auth?: Auth;
|
|
16
|
-
#apiBaseAddress: string;
|
|
17
|
-
|
|
18
|
-
constructor({
|
|
19
|
-
auth,
|
|
20
|
-
apiBaseAddress = "https://public-api.animaapp.com",
|
|
21
|
-
}: {
|
|
22
|
-
auth?: Auth;
|
|
23
|
-
apiBaseAddress?: string;
|
|
24
|
-
path?: string;
|
|
25
|
-
} = {}) {
|
|
26
|
-
this.#apiBaseAddress = apiBaseAddress;
|
|
27
|
-
|
|
28
|
-
if (auth) {
|
|
29
|
-
this.auth = auth;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
protected hasAuth() {
|
|
34
|
-
return !!this.#auth;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
set auth(auth: Auth) {
|
|
38
|
-
this.#auth = auth;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
protected get headers() {
|
|
42
|
-
const headers: Record<string, string> = {
|
|
43
|
-
"Content-Type": "application/json",
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
if (this.#auth) {
|
|
47
|
-
headers["Authorization"] = `Bearer ${this.#auth.token}`;
|
|
48
|
-
|
|
49
|
-
if ("teamId" in this.#auth) {
|
|
50
|
-
headers["X-Team-Id"] = this.#auth.teamId;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return headers;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async generateCode(params: GetCodeParams, handler: GetCodeHandler = {}) {
|
|
58
|
-
if (this.hasAuth() === false) {
|
|
59
|
-
throw new Error('It needs to set "auth" before calling this method.');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const result: Partial<AnimaSDKResult> = {};
|
|
63
|
-
const settings = validateSettings(params.settings);
|
|
64
|
-
|
|
65
|
-
let tracking = params.tracking;
|
|
66
|
-
if (this.#auth && "userId" in this.#auth && this.#auth.userId) {
|
|
67
|
-
if (!tracking?.externalId) {
|
|
68
|
-
tracking = { externalId: this.#auth.userId };
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const response = await fetch(`${this.#apiBaseAddress}/v1/codegen`, {
|
|
73
|
-
method: "POST",
|
|
74
|
-
headers: {
|
|
75
|
-
...this.headers,
|
|
76
|
-
Accept: "text/event-stream",
|
|
77
|
-
},
|
|
78
|
-
body: JSON.stringify({
|
|
79
|
-
tracking,
|
|
80
|
-
fileKey: params.fileKey,
|
|
81
|
-
figmaToken: params.figmaToken,
|
|
82
|
-
nodesId: params.nodesId,
|
|
83
|
-
assetsStorage: params.assetsStorage,
|
|
84
|
-
language: settings.language,
|
|
85
|
-
model: settings.model,
|
|
86
|
-
framework: settings.framework,
|
|
87
|
-
styling: settings.styling,
|
|
88
|
-
uiLibrary: settings.uiLibrary,
|
|
89
|
-
enableTranslation: settings.enableTranslation,
|
|
90
|
-
enableUILibraryTheming: settings.enableUILibraryTheming,
|
|
91
|
-
enableCompactStructure: settings.enableCompactStructure,
|
|
92
|
-
enableAutoSplit: settings.enableAutoSplit,
|
|
93
|
-
autoSplitThreshold: settings.autoSplitThreshold,
|
|
94
|
-
disableMarkedForExport: settings.disableMarkedForExport,
|
|
95
|
-
}),
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
if (!response.ok) {
|
|
99
|
-
const errorData = await response
|
|
100
|
-
.json()
|
|
101
|
-
.catch(() => "HTTP error from Anima API");
|
|
102
|
-
|
|
103
|
-
if (typeof errorData === "string") {
|
|
104
|
-
throw new CodegenError({
|
|
105
|
-
name: errorData,
|
|
106
|
-
reason: "Unknown",
|
|
107
|
-
detail: { status: response.status },
|
|
108
|
-
status: response.status,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (typeof errorData !== "object") {
|
|
113
|
-
throw new CodegenError({
|
|
114
|
-
name: `Error "${errorData}"`,
|
|
115
|
-
reason: "Unknown",
|
|
116
|
-
detail: { status: response.status },
|
|
117
|
-
status: response.status,
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (errorData.error?.name === "ZodError") {
|
|
122
|
-
throw new CodegenError({
|
|
123
|
-
name: "HTTP error from Anima API",
|
|
124
|
-
reason: "Invalid body payload",
|
|
125
|
-
detail: errorData.error.issues,
|
|
126
|
-
status: response.status,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
throw new CodegenError({
|
|
131
|
-
name: errorData.error?.name || "HTTP error from Anima API",
|
|
132
|
-
reason: "Unknown",
|
|
133
|
-
detail: { status: response.status },
|
|
134
|
-
status: response.status,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (!response.body) {
|
|
139
|
-
throw new CodegenError({
|
|
140
|
-
name: "Stream Error",
|
|
141
|
-
reason: "Response body is null",
|
|
142
|
-
status: response.status,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const reader = response.body.getReader();
|
|
147
|
-
const decoder = new TextDecoder();
|
|
148
|
-
let buffer = "";
|
|
149
|
-
|
|
150
|
-
try {
|
|
151
|
-
while (true) {
|
|
152
|
-
const { done, value } = await reader.read();
|
|
153
|
-
if (done) {
|
|
154
|
-
break;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
buffer += decoder.decode(value, { stream: true });
|
|
158
|
-
|
|
159
|
-
const lines = buffer.split("\n");
|
|
160
|
-
|
|
161
|
-
// Process all complete lines
|
|
162
|
-
buffer = lines.pop() || ""; // Keep the last incomplete line in the buffer
|
|
163
|
-
|
|
164
|
-
for (const line of lines) {
|
|
165
|
-
if (!line.trim() || line.startsWith(":")) continue;
|
|
166
|
-
|
|
167
|
-
if (line.startsWith("data: ")) {
|
|
168
|
-
let data: SSECodgenMessage;
|
|
169
|
-
try {
|
|
170
|
-
data = JSON.parse(line.slice(6));
|
|
171
|
-
} catch {
|
|
172
|
-
// ignore malformed JSON
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
switch (data.type) {
|
|
177
|
-
case "queueing": {
|
|
178
|
-
typeof handler === "function"
|
|
179
|
-
? handler(data)
|
|
180
|
-
: handler.onQueueing?.();
|
|
181
|
-
break;
|
|
182
|
-
}
|
|
183
|
-
case "start": {
|
|
184
|
-
result.sessionId = data.sessionId;
|
|
185
|
-
typeof handler === "function"
|
|
186
|
-
? handler(data)
|
|
187
|
-
: handler.onStart?.({ sessionId: data.sessionId });
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
case "pre_codegen": {
|
|
192
|
-
typeof handler === "function"
|
|
193
|
-
? handler(data)
|
|
194
|
-
: handler.onPreCodegen?.({ message: data.message });
|
|
195
|
-
break;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
case "assets_uploaded": {
|
|
199
|
-
typeof handler === "function"
|
|
200
|
-
? handler(data)
|
|
201
|
-
: handler.onAssetsUploaded?.();
|
|
202
|
-
break;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
case "assets_list": {
|
|
206
|
-
result.assets = data.payload.assets;
|
|
207
|
-
|
|
208
|
-
typeof handler === "function"
|
|
209
|
-
? handler(data)
|
|
210
|
-
: handler.onAssetsList?.(data.payload);
|
|
211
|
-
break;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
case "figma_metadata": {
|
|
215
|
-
result.figmaFileName = data.figmaFileName;
|
|
216
|
-
result.figmaSelectedFrameName = data.figmaSelectedFrameName;
|
|
217
|
-
|
|
218
|
-
typeof handler === "function"
|
|
219
|
-
? handler(data)
|
|
220
|
-
: handler.onFigmaMetadata?.({
|
|
221
|
-
figmaFileName: data.figmaFileName,
|
|
222
|
-
figmaSelectedFrameName: data.figmaSelectedFrameName,
|
|
223
|
-
});
|
|
224
|
-
break;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
case "generating_code": {
|
|
228
|
-
if (data.payload.status === "success") {
|
|
229
|
-
result.files = data.payload.files;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
typeof handler === "function"
|
|
233
|
-
? handler(data)
|
|
234
|
-
: handler.onGeneratingCode?.({
|
|
235
|
-
status: data.payload.status,
|
|
236
|
-
progress: data.payload.progress,
|
|
237
|
-
files: data.payload.files,
|
|
238
|
-
});
|
|
239
|
-
break;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
case "codegen_completed": {
|
|
243
|
-
typeof handler === "function"
|
|
244
|
-
? handler(data)
|
|
245
|
-
: handler.onCodegenCompleted?.();
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
case "error": {
|
|
250
|
-
throw new CodegenError({
|
|
251
|
-
name: data.payload.errorName,
|
|
252
|
-
reason: data.payload.reason,
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
case "done": {
|
|
257
|
-
if (!result.files) {
|
|
258
|
-
throw new CodegenError({
|
|
259
|
-
name: "Invalid response",
|
|
260
|
-
reason: "No code generated",
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
result.tokenUsage = data.payload.tokenUsage;
|
|
265
|
-
return result as AnimaSDKResult;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
} finally {
|
|
272
|
-
reader.cancel();
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
throw new CodegenError({
|
|
276
|
-
name: "Connection",
|
|
277
|
-
reason: "Connection closed before the 'done' message",
|
|
278
|
-
status: 500,
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
}
|
package/src/dataStream.ts
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import type { Anima } from "./anima";
|
|
2
|
-
import type { CodegenErrorReason } from "./errors";
|
|
3
|
-
import type { GetCodeParams, SSECodgenMessage } from "./types";
|
|
4
|
-
|
|
5
|
-
export type StreamCodgenMessage =
|
|
6
|
-
| Exclude<SSECodgenMessage, { type: "error" }>
|
|
7
|
-
| {
|
|
8
|
-
type: "error";
|
|
9
|
-
payload: {
|
|
10
|
-
name: string;
|
|
11
|
-
message: CodegenErrorReason;
|
|
12
|
-
status?: number;
|
|
13
|
-
detail?: unknown;
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Start the code generation and creates a ReadableStream to output its result.
|
|
19
|
-
*
|
|
20
|
-
* The stream is closed when the codegen ends.
|
|
21
|
-
*
|
|
22
|
-
* @param {Anima} anima - An Anima service instance to generate the code from.
|
|
23
|
-
* @param {GetCodeParams} params - Parameters required for the code generation process.
|
|
24
|
-
* @returns {ReadableStream<StreamCodgenMessage>} - A ReadableStream that emits messages related to the code generation process.
|
|
25
|
-
*/
|
|
26
|
-
export const createCodegenStream = (
|
|
27
|
-
anima: Anima,
|
|
28
|
-
params: GetCodeParams
|
|
29
|
-
): ReadableStream<StreamCodgenMessage> => {
|
|
30
|
-
return new ReadableStream({
|
|
31
|
-
start(controller) {
|
|
32
|
-
anima
|
|
33
|
-
.generateCode(params, (message) => {
|
|
34
|
-
if (message.type === "error") {
|
|
35
|
-
// TODO: It's a dead code. It's never reached, since all errors are thrown.
|
|
36
|
-
// controller.enqueue({
|
|
37
|
-
// type: "error",
|
|
38
|
-
// payload: { message: message.payload.reason },
|
|
39
|
-
// });
|
|
40
|
-
} else {
|
|
41
|
-
controller.enqueue(message);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (message.type === "aborted" || message.type === "error") {
|
|
45
|
-
controller.close();
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
.then((_result) => {
|
|
49
|
-
controller.enqueue({
|
|
50
|
-
type: "done",
|
|
51
|
-
payload: {
|
|
52
|
-
tokenUsage: _result.tokenUsage,
|
|
53
|
-
sessionId: _result.sessionId,
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
controller.close();
|
|
57
|
-
})
|
|
58
|
-
.catch((error) => {
|
|
59
|
-
controller.enqueue({
|
|
60
|
-
type: "error",
|
|
61
|
-
payload: {
|
|
62
|
-
name: "name" in error ? error.name : "Unknown error",
|
|
63
|
-
message: "message" in error ? error.message : "Unknown",
|
|
64
|
-
status: "status" in error ? error.status : undefined,
|
|
65
|
-
detail: "detail" in error ? error.detail : undefined,
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
controller.close();
|
|
69
|
-
});
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Creates a Server-Sent Events (SSE) `Response` that forwards all messages from the code generation stream.
|
|
76
|
-
*
|
|
77
|
-
* But, if the first message indicates an error (e.g., connection failed), the function returns a 500 response with the error message.
|
|
78
|
-
*
|
|
79
|
-
* @param {Anima} anima - The Anima instance to use for creating the data stream.
|
|
80
|
-
* @param {GetCodeParams} params - The parameters for the code generation request.
|
|
81
|
-
* @returns {Promise<Response>} - A promise that resolves to an HTTP response.
|
|
82
|
-
*/
|
|
83
|
-
export const createCodegenResponseEventStream = async (
|
|
84
|
-
anima: Anima,
|
|
85
|
-
params: GetCodeParams
|
|
86
|
-
): Promise<Response> => {
|
|
87
|
-
const stream = createCodegenStream(anima, params);
|
|
88
|
-
|
|
89
|
-
const [verifyStream, consumerStream] = stream.tee();
|
|
90
|
-
const firstMessage = await verifyStream.getReader().read();
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
firstMessage.done ||
|
|
94
|
-
!firstMessage.value ||
|
|
95
|
-
(firstMessage.value?.type === "error" &&
|
|
96
|
-
firstMessage.value?.payload?.status)
|
|
97
|
-
) {
|
|
98
|
-
return new Response(JSON.stringify(firstMessage.value), {
|
|
99
|
-
status:
|
|
100
|
-
firstMessage.value?.type === "error"
|
|
101
|
-
? (firstMessage.value?.payload?.status ?? 500)
|
|
102
|
-
: 500,
|
|
103
|
-
headers: {
|
|
104
|
-
"Content-Type": "application/json",
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const encoder = new TextEncoder();
|
|
110
|
-
const seeStream = consumerStream.pipeThrough(
|
|
111
|
-
new TransformStream({
|
|
112
|
-
transform(chunk, controller) {
|
|
113
|
-
const sseString = `event: ${chunk.type}\ndata: ${JSON.stringify(
|
|
114
|
-
chunk
|
|
115
|
-
)}\n\n`;
|
|
116
|
-
controller.enqueue(encoder.encode(sseString));
|
|
117
|
-
},
|
|
118
|
-
})
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
return new Response(seeStream, {
|
|
122
|
-
status: 200,
|
|
123
|
-
headers: {
|
|
124
|
-
"Content-Type": "text/event-stream; charset=utf-8",
|
|
125
|
-
Connection: "keep-alive",
|
|
126
|
-
"Cache-Control": "no-cache",
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
};
|
package/src/errors.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Errors from Public API
|
|
3
|
-
*/
|
|
4
|
-
export type CodegenErrorReason =
|
|
5
|
-
| "Selected node type is not supported"
|
|
6
|
-
| "Invisible group nodes are unsupported"
|
|
7
|
-
| "Selected node is a page with multiple children"
|
|
8
|
-
| "There is no node with the given id"
|
|
9
|
-
| "Invalid Figma token"
|
|
10
|
-
| "Anima API connection error"
|
|
11
|
-
| "Figma token expired"
|
|
12
|
-
| "Invalid user token"
|
|
13
|
-
| "Figma file not found"
|
|
14
|
-
| "Figma rate limit exceeded"
|
|
15
|
-
| "Unknown";
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Errors from the SDK
|
|
19
|
-
*/
|
|
20
|
-
export type SDKErrorReason =
|
|
21
|
-
| "Invalid body payload"
|
|
22
|
-
| "No code generated"
|
|
23
|
-
| "Connection closed before the 'done' message"
|
|
24
|
-
| "Response body is null";
|
|
25
|
-
|
|
26
|
-
export class CodegenError extends Error {
|
|
27
|
-
status?: number;
|
|
28
|
-
detail?: unknown;
|
|
29
|
-
|
|
30
|
-
constructor({
|
|
31
|
-
name,
|
|
32
|
-
reason,
|
|
33
|
-
status,
|
|
34
|
-
detail,
|
|
35
|
-
}: {
|
|
36
|
-
name: string;
|
|
37
|
-
reason: CodegenErrorReason | SDKErrorReason;
|
|
38
|
-
status?: number;
|
|
39
|
-
detail?: unknown;
|
|
40
|
-
}) {
|
|
41
|
-
super();
|
|
42
|
-
this.name = name;
|
|
43
|
-
this.message = reason;
|
|
44
|
-
this.detail = detail;
|
|
45
|
-
this.status = status;
|
|
46
|
-
}
|
|
47
|
-
}
|
package/src/figma/figmaError.ts
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import type { CodegenErrorReason } from "../errors";
|
|
2
|
-
|
|
3
|
-
const figmaTokenIssueErrorMessage = "Figma Token Issue";
|
|
4
|
-
export class FigmaTokenIssue extends Error {
|
|
5
|
-
fileKey: string;
|
|
6
|
-
reason: string;
|
|
7
|
-
|
|
8
|
-
constructor({ fileKey, reason }: { fileKey: string; reason: string }) {
|
|
9
|
-
super(figmaTokenIssueErrorMessage);
|
|
10
|
-
|
|
11
|
-
this.fileKey = fileKey;
|
|
12
|
-
this.reason = reason;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const rateLimitExceededErrorMessage = "Rate Limit Exceeded";
|
|
17
|
-
export class RateLimitExceeded extends Error {
|
|
18
|
-
fileKey: string;
|
|
19
|
-
|
|
20
|
-
constructor({ fileKey }: { fileKey: string }) {
|
|
21
|
-
super(rateLimitExceededErrorMessage);
|
|
22
|
-
|
|
23
|
-
this.fileKey = fileKey;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Not Found
|
|
28
|
-
const notFoundErrorMessage = "Not Found";
|
|
29
|
-
export class NotFound extends Error {
|
|
30
|
-
fileKey: string;
|
|
31
|
-
|
|
32
|
-
constructor({ fileKey }: { fileKey: string }) {
|
|
33
|
-
super(notFoundErrorMessage);
|
|
34
|
-
|
|
35
|
-
this.fileKey = fileKey;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
export const isNotFound = (error: Error) => {
|
|
39
|
-
return error.message === notFoundErrorMessage;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// Unknown Exception
|
|
43
|
-
const unknownFigmaApiExceptionMessage = "Unknown Figma API Exception";
|
|
44
|
-
export class UnknownFigmaApiException extends Error {
|
|
45
|
-
fileKey: string;
|
|
46
|
-
|
|
47
|
-
constructor({ fileKey, cause }: { fileKey: string; cause: unknown }) {
|
|
48
|
-
super(unknownFigmaApiExceptionMessage);
|
|
49
|
-
|
|
50
|
-
this.name = "UnknownFigmaApiException";
|
|
51
|
-
this.fileKey = fileKey;
|
|
52
|
-
this.cause = cause;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
export const isUnknownFigmaApiException = (error: Error) => {
|
|
56
|
-
return error.message === unknownFigmaApiExceptionMessage;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export const isRateLimitExceeded = (error: Error) => {
|
|
60
|
-
return error.message === rateLimitExceededErrorMessage;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export const isFigmaTokenIssue = (error: Error) => {
|
|
64
|
-
const figmaTokenCodegenErrors: CodegenErrorReason[] = [
|
|
65
|
-
"Invalid Figma token",
|
|
66
|
-
"Figma token expired",
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
return [figmaTokenIssueErrorMessage, ...figmaTokenCodegenErrors].includes(
|
|
70
|
-
error.message
|
|
71
|
-
);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export type FigmaApiError = {
|
|
75
|
-
cause?: { body?: { status?: number; reason?: string } };
|
|
76
|
-
body?: { status?: number; reason?: string };
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export const handleFigmaApiError = (error: FigmaApiError, fileKey: string) => {
|
|
80
|
-
const err = error?.cause?.body || error.body;
|
|
81
|
-
|
|
82
|
-
if (err?.status === 403) {
|
|
83
|
-
throw new FigmaTokenIssue({
|
|
84
|
-
fileKey,
|
|
85
|
-
reason: (error?.cause?.body?.reason || error.body?.reason || "Access denied").toString(),
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (err?.status === 429) {
|
|
90
|
-
throw new RateLimitExceeded({ fileKey });
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (err?.status === 404) {
|
|
94
|
-
throw new NotFound({ fileKey });
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
throw new UnknownFigmaApiException({ fileKey, cause: error });
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
export type FigmaApiErrorType =
|
|
101
|
-
| "FigmaTokenIssue"
|
|
102
|
-
| "RateLimitExceeded"
|
|
103
|
-
| "NotFound"
|
|
104
|
-
| "UnknownFigmaApiException";
|
|
105
|
-
|
|
106
|
-
export const getFigmaApiErrorType = (error: Error): FigmaApiErrorType => {
|
|
107
|
-
if (isNotFound(error)) {
|
|
108
|
-
return "NotFound";
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (isRateLimitExceeded(error)) {
|
|
112
|
-
return "RateLimitExceeded";
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (isFigmaTokenIssue(error)) {
|
|
116
|
-
return "FigmaTokenIssue";
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return "UnknownFigmaApiException";
|
|
120
|
-
};
|
package/src/figma/index.ts
DELETED