figma-metadata-extractor 1.0.13 → 1.0.14

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.
@@ -0,0 +1 @@
1
+ "use strict";
package/dist/index.cjs CHANGED
@@ -265,6 +265,122 @@ function formatHeadersForCurl(headers) {
265
265
  }
266
266
  return headerArgs;
267
267
  }
268
+ async function downloadFigmaImage(fileName, localPath, imageUrl, returnBuffer = false) {
269
+ const { pipeline } = await Promise.resolve().then(() => require("./__vite-browser-external-Dyvby5gX.cjs"));
270
+ const { Readable } = await Promise.resolve().then(() => require("./__vite-browser-external-Dyvby5gX.cjs"));
271
+ try {
272
+ const response = await fetch(imageUrl, {
273
+ method: "GET"
274
+ });
275
+ if (!response.ok) {
276
+ throw new Error(
277
+ `Failed to download image: ${response.status} ${response.statusText}`
278
+ );
279
+ }
280
+ if (!response.body) {
281
+ throw new Error("Response body is empty");
282
+ }
283
+ if (returnBuffer) {
284
+ const arrayBuffer = await response.arrayBuffer();
285
+ if (arrayBuffer.byteLength === 0) {
286
+ throw new Error("Downloaded image buffer is empty");
287
+ }
288
+ return arrayBuffer;
289
+ }
290
+ if (!fs.existsSync(localPath)) {
291
+ fs.mkdirSync(localPath, { recursive: true });
292
+ }
293
+ const fullPath = path.join(localPath, fileName);
294
+ const fileStream = fs.createWriteStream(fullPath);
295
+ await pipeline(Readable.fromWeb(response.body), fileStream);
296
+ const stats = fs.statSync(fullPath);
297
+ if (stats.size === 0) {
298
+ fs.unlinkSync(fullPath);
299
+ throw new Error("Downloaded file is empty (0 bytes)");
300
+ }
301
+ return fullPath;
302
+ } catch (error) {
303
+ const errorMessage = error instanceof Error ? error.message : String(error);
304
+ throw new Error(`Error downloading image: ${errorMessage}`);
305
+ }
306
+ }
307
+ function generateVarId(prefix = "var") {
308
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
309
+ let result = "";
310
+ for (let i = 0; i < 6; i++) {
311
+ const randomIndex = Math.floor(Math.random() * chars.length);
312
+ result += chars[randomIndex];
313
+ }
314
+ return `${prefix}_${result}`;
315
+ }
316
+ function generateCSSShorthand(values, {
317
+ ignoreZero = true,
318
+ suffix = "px"
319
+ } = {}) {
320
+ const { top, right, bottom, left } = values;
321
+ if (ignoreZero && top === 0 && right === 0 && bottom === 0 && left === 0) {
322
+ return void 0;
323
+ }
324
+ if (top === right && right === bottom && bottom === left) {
325
+ return `${top}${suffix}`;
326
+ }
327
+ if (right === left) {
328
+ if (top === bottom) {
329
+ return `${top}${suffix} ${right}${suffix}`;
330
+ }
331
+ return `${top}${suffix} ${right}${suffix} ${bottom}${suffix}`;
332
+ }
333
+ return `${top}${suffix} ${right}${suffix} ${bottom}${suffix} ${left}${suffix}`;
334
+ }
335
+ function isVisible(element) {
336
+ return element.visible ?? true;
337
+ }
338
+ function pixelRound(num) {
339
+ if (isNaN(num)) {
340
+ throw new TypeError(`Input must be a valid number`);
341
+ }
342
+ return Number(Number(num).toFixed(2));
343
+ }
344
+ async function runWithConcurrency(tasks, limit) {
345
+ const results = new Array(tasks.length);
346
+ return new Promise((resolve, reject) => {
347
+ if (tasks.length === 0) {
348
+ resolve([]);
349
+ return;
350
+ }
351
+ let completed = 0;
352
+ let launched = 0;
353
+ let failed = false;
354
+ const next = () => {
355
+ if (failed) return;
356
+ if (completed === tasks.length) {
357
+ resolve(results);
358
+ return;
359
+ }
360
+ while (launched < tasks.length && launched - completed < limit) {
361
+ const index = launched++;
362
+ tasks[index]().then((result) => {
363
+ results[index] = result;
364
+ completed++;
365
+ next();
366
+ }).catch((err) => {
367
+ failed = true;
368
+ reject(err);
369
+ });
370
+ }
371
+ };
372
+ next();
373
+ });
374
+ }
375
+ const common = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
376
+ __proto__: null,
377
+ downloadFigmaImage,
378
+ generateCSSShorthand,
379
+ generateVarId,
380
+ isVisible,
381
+ pixelRound,
382
+ runWithConcurrency
383
+ }, Symbol.toStringTag, { value: "Module" }));
268
384
  class FigmaService {
269
385
  apiKey;
270
386
  oauthToken;
@@ -372,6 +488,7 @@ class FigmaService {
372
488
  async downloadImages(fileKey, localPath, items, options = {}) {
373
489
  if (items.length === 0) return [];
374
490
  const { pngScale = 2, svgOptions, returnBuffer = false } = options;
491
+ const CONCURRENCY_LIMIT = 10;
375
492
  let resolvedPath = "";
376
493
  if (!returnBuffer) {
377
494
  const sanitizedPath = path.normalize(localPath).replace(/^(\.\.(\/|\\|$))+/, "");
@@ -391,7 +508,7 @@ class FigmaService {
391
508
  );
392
509
  if (imageFills.length > 0) {
393
510
  const fillUrls = await this.getImageFillUrls(fileKey);
394
- const fillDownloads = imageFills.map(
511
+ const fillTasks = imageFills.map(
395
512
  ({
396
513
  imageRef,
397
514
  fileName,
@@ -406,7 +523,7 @@ class FigmaService {
406
523
  );
407
524
  return null;
408
525
  }
409
- return downloadAndProcessImage(
526
+ return () => downloadAndProcessImage(
410
527
  fileName,
411
528
  resolvedPath,
412
529
  imageUrl,
@@ -417,10 +534,10 @@ class FigmaService {
417
534
  );
418
535
  }
419
536
  ).filter(
420
- (promise) => promise !== null
537
+ (task) => task !== null
421
538
  );
422
- if (fillDownloads.length > 0) {
423
- downloadPromises.push(Promise.all(fillDownloads));
539
+ if (fillTasks.length > 0) {
540
+ downloadPromises.push(runWithConcurrency(fillTasks, CONCURRENCY_LIMIT));
424
541
  }
425
542
  }
426
543
  if (renderNodes.length > 0) {
@@ -437,7 +554,7 @@ class FigmaService {
437
554
  "png",
438
555
  { pngScale }
439
556
  );
440
- const pngDownloads = pngNodes.map(
557
+ const pngTasks = pngNodes.map(
441
558
  ({
442
559
  nodeId,
443
560
  fileName,
@@ -452,7 +569,7 @@ class FigmaService {
452
569
  );
453
570
  return null;
454
571
  }
455
- return downloadAndProcessImage(
572
+ return () => downloadAndProcessImage(
456
573
  fileName,
457
574
  resolvedPath,
458
575
  imageUrl,
@@ -465,10 +582,12 @@ class FigmaService {
465
582
  );
466
583
  }
467
584
  ).filter(
468
- (promise) => promise !== null
585
+ (task) => task !== null
469
586
  );
470
- if (pngDownloads.length > 0) {
471
- downloadPromises.push(Promise.all(pngDownloads));
587
+ if (pngTasks.length > 0) {
588
+ downloadPromises.push(
589
+ runWithConcurrency(pngTasks, CONCURRENCY_LIMIT)
590
+ );
472
591
  }
473
592
  }
474
593
  if (svgNodes.length > 0) {
@@ -478,7 +597,7 @@ class FigmaService {
478
597
  "svg",
479
598
  { svgOptions }
480
599
  );
481
- const svgDownloads = svgNodes.map(
600
+ const svgTasks = svgNodes.map(
482
601
  ({
483
602
  nodeId,
484
603
  fileName,
@@ -493,7 +612,7 @@ class FigmaService {
493
612
  );
494
613
  return null;
495
614
  }
496
- return downloadAndProcessImage(
615
+ return () => downloadAndProcessImage(
497
616
  fileName,
498
617
  resolvedPath,
499
618
  imageUrl,
@@ -506,10 +625,12 @@ class FigmaService {
506
625
  );
507
626
  }
508
627
  ).filter(
509
- (promise) => promise !== null
628
+ (task) => task !== null
510
629
  );
511
- if (svgDownloads.length > 0) {
512
- downloadPromises.push(Promise.all(svgDownloads));
630
+ if (svgTasks.length > 0) {
631
+ downloadPromises.push(
632
+ runWithConcurrency(svgTasks, CONCURRENCY_LIMIT)
633
+ );
513
634
  }
514
635
  }
515
636
  }
@@ -541,106 +662,6 @@ class FigmaService {
541
662
  return response;
542
663
  }
543
664
  }
544
- async function downloadFigmaImage(fileName, localPath, imageUrl, returnBuffer = false) {
545
- try {
546
- const response = await fetch(imageUrl, {
547
- method: "GET"
548
- });
549
- if (!response.ok) {
550
- throw new Error(`Failed to download image: ${response.statusText}`);
551
- }
552
- if (returnBuffer) {
553
- const arrayBuffer = await response.arrayBuffer();
554
- return arrayBuffer;
555
- }
556
- if (!fs.existsSync(localPath)) {
557
- fs.mkdirSync(localPath, { recursive: true });
558
- }
559
- const fullPath = path.join(localPath, fileName);
560
- const reader = response.body?.getReader();
561
- if (!reader) {
562
- throw new Error("Failed to get response body");
563
- }
564
- const writer = fs.createWriteStream(fullPath);
565
- return new Promise((resolve, reject) => {
566
- const processStream = async () => {
567
- try {
568
- while (true) {
569
- const { done, value } = await reader.read();
570
- if (done) {
571
- writer.end();
572
- break;
573
- }
574
- writer.write(value);
575
- }
576
- } catch (err) {
577
- writer.end();
578
- fs.unlink(fullPath, () => {
579
- });
580
- reject(err);
581
- }
582
- };
583
- writer.on("finish", () => {
584
- resolve(fullPath);
585
- });
586
- writer.on("error", (err) => {
587
- reader.cancel();
588
- fs.unlink(fullPath, () => {
589
- });
590
- reject(new Error(`Failed to write image: ${err.message}`));
591
- });
592
- processStream();
593
- });
594
- } catch (error) {
595
- const errorMessage = error instanceof Error ? error.message : String(error);
596
- throw new Error(`Error downloading image: ${errorMessage}`);
597
- }
598
- }
599
- function generateVarId(prefix = "var") {
600
- const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
601
- let result = "";
602
- for (let i = 0; i < 6; i++) {
603
- const randomIndex = Math.floor(Math.random() * chars.length);
604
- result += chars[randomIndex];
605
- }
606
- return `${prefix}_${result}`;
607
- }
608
- function generateCSSShorthand(values, {
609
- ignoreZero = true,
610
- suffix = "px"
611
- } = {}) {
612
- const { top, right, bottom, left } = values;
613
- if (ignoreZero && top === 0 && right === 0 && bottom === 0 && left === 0) {
614
- return void 0;
615
- }
616
- if (top === right && right === bottom && bottom === left) {
617
- return `${top}${suffix}`;
618
- }
619
- if (right === left) {
620
- if (top === bottom) {
621
- return `${top}${suffix} ${right}${suffix}`;
622
- }
623
- return `${top}${suffix} ${right}${suffix} ${bottom}${suffix}`;
624
- }
625
- return `${top}${suffix} ${right}${suffix} ${bottom}${suffix} ${left}${suffix}`;
626
- }
627
- function isVisible(element) {
628
- return element.visible ?? true;
629
- }
630
- function pixelRound(num) {
631
- if (isNaN(num)) {
632
- throw new TypeError(`Input must be a valid number`);
633
- }
634
- return Number(Number(num).toFixed(2));
635
- }
636
- const common = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
637
- __proto__: null,
638
- downloadFigmaImage,
639
- generateCSSShorthand,
640
- generateVarId,
641
- isVisible,
642
- pixelRound
643
- }, Symbol.toStringTag, { value: "Module" }));
644
665
  function hasValue(key, obj, typeGuard) {
645
666
  const isObject = typeof obj === "object" && obj !== null;
646
667
  if (!isObject || !(key in obj)) return false;
package/dist/index.js CHANGED
@@ -263,6 +263,122 @@ function formatHeadersForCurl(headers) {
263
263
  }
264
264
  return headerArgs;
265
265
  }
266
+ async function downloadFigmaImage(fileName, localPath, imageUrl, returnBuffer = false) {
267
+ const { pipeline } = await import("./__vite-browser-external-l0sNRNKZ.js");
268
+ const { Readable } = await import("./__vite-browser-external-l0sNRNKZ.js");
269
+ try {
270
+ const response = await fetch(imageUrl, {
271
+ method: "GET"
272
+ });
273
+ if (!response.ok) {
274
+ throw new Error(
275
+ `Failed to download image: ${response.status} ${response.statusText}`
276
+ );
277
+ }
278
+ if (!response.body) {
279
+ throw new Error("Response body is empty");
280
+ }
281
+ if (returnBuffer) {
282
+ const arrayBuffer = await response.arrayBuffer();
283
+ if (arrayBuffer.byteLength === 0) {
284
+ throw new Error("Downloaded image buffer is empty");
285
+ }
286
+ return arrayBuffer;
287
+ }
288
+ if (!fs.existsSync(localPath)) {
289
+ fs.mkdirSync(localPath, { recursive: true });
290
+ }
291
+ const fullPath = path.join(localPath, fileName);
292
+ const fileStream = fs.createWriteStream(fullPath);
293
+ await pipeline(Readable.fromWeb(response.body), fileStream);
294
+ const stats = fs.statSync(fullPath);
295
+ if (stats.size === 0) {
296
+ fs.unlinkSync(fullPath);
297
+ throw new Error("Downloaded file is empty (0 bytes)");
298
+ }
299
+ return fullPath;
300
+ } catch (error) {
301
+ const errorMessage = error instanceof Error ? error.message : String(error);
302
+ throw new Error(`Error downloading image: ${errorMessage}`);
303
+ }
304
+ }
305
+ function generateVarId(prefix = "var") {
306
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
307
+ let result = "";
308
+ for (let i = 0; i < 6; i++) {
309
+ const randomIndex = Math.floor(Math.random() * chars.length);
310
+ result += chars[randomIndex];
311
+ }
312
+ return `${prefix}_${result}`;
313
+ }
314
+ function generateCSSShorthand(values, {
315
+ ignoreZero = true,
316
+ suffix = "px"
317
+ } = {}) {
318
+ const { top, right, bottom, left } = values;
319
+ if (ignoreZero && top === 0 && right === 0 && bottom === 0 && left === 0) {
320
+ return void 0;
321
+ }
322
+ if (top === right && right === bottom && bottom === left) {
323
+ return `${top}${suffix}`;
324
+ }
325
+ if (right === left) {
326
+ if (top === bottom) {
327
+ return `${top}${suffix} ${right}${suffix}`;
328
+ }
329
+ return `${top}${suffix} ${right}${suffix} ${bottom}${suffix}`;
330
+ }
331
+ return `${top}${suffix} ${right}${suffix} ${bottom}${suffix} ${left}${suffix}`;
332
+ }
333
+ function isVisible(element) {
334
+ return element.visible ?? true;
335
+ }
336
+ function pixelRound(num) {
337
+ if (isNaN(num)) {
338
+ throw new TypeError(`Input must be a valid number`);
339
+ }
340
+ return Number(Number(num).toFixed(2));
341
+ }
342
+ async function runWithConcurrency(tasks, limit) {
343
+ const results = new Array(tasks.length);
344
+ return new Promise((resolve, reject) => {
345
+ if (tasks.length === 0) {
346
+ resolve([]);
347
+ return;
348
+ }
349
+ let completed = 0;
350
+ let launched = 0;
351
+ let failed = false;
352
+ const next = () => {
353
+ if (failed) return;
354
+ if (completed === tasks.length) {
355
+ resolve(results);
356
+ return;
357
+ }
358
+ while (launched < tasks.length && launched - completed < limit) {
359
+ const index = launched++;
360
+ tasks[index]().then((result) => {
361
+ results[index] = result;
362
+ completed++;
363
+ next();
364
+ }).catch((err) => {
365
+ failed = true;
366
+ reject(err);
367
+ });
368
+ }
369
+ };
370
+ next();
371
+ });
372
+ }
373
+ const common = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
374
+ __proto__: null,
375
+ downloadFigmaImage,
376
+ generateCSSShorthand,
377
+ generateVarId,
378
+ isVisible,
379
+ pixelRound,
380
+ runWithConcurrency
381
+ }, Symbol.toStringTag, { value: "Module" }));
266
382
  class FigmaService {
267
383
  apiKey;
268
384
  oauthToken;
@@ -370,6 +486,7 @@ class FigmaService {
370
486
  async downloadImages(fileKey, localPath, items, options = {}) {
371
487
  if (items.length === 0) return [];
372
488
  const { pngScale = 2, svgOptions, returnBuffer = false } = options;
489
+ const CONCURRENCY_LIMIT = 10;
373
490
  let resolvedPath = "";
374
491
  if (!returnBuffer) {
375
492
  const sanitizedPath = path.normalize(localPath).replace(/^(\.\.(\/|\\|$))+/, "");
@@ -389,7 +506,7 @@ class FigmaService {
389
506
  );
390
507
  if (imageFills.length > 0) {
391
508
  const fillUrls = await this.getImageFillUrls(fileKey);
392
- const fillDownloads = imageFills.map(
509
+ const fillTasks = imageFills.map(
393
510
  ({
394
511
  imageRef,
395
512
  fileName,
@@ -404,7 +521,7 @@ class FigmaService {
404
521
  );
405
522
  return null;
406
523
  }
407
- return downloadAndProcessImage(
524
+ return () => downloadAndProcessImage(
408
525
  fileName,
409
526
  resolvedPath,
410
527
  imageUrl,
@@ -415,10 +532,10 @@ class FigmaService {
415
532
  );
416
533
  }
417
534
  ).filter(
418
- (promise) => promise !== null
535
+ (task) => task !== null
419
536
  );
420
- if (fillDownloads.length > 0) {
421
- downloadPromises.push(Promise.all(fillDownloads));
537
+ if (fillTasks.length > 0) {
538
+ downloadPromises.push(runWithConcurrency(fillTasks, CONCURRENCY_LIMIT));
422
539
  }
423
540
  }
424
541
  if (renderNodes.length > 0) {
@@ -435,7 +552,7 @@ class FigmaService {
435
552
  "png",
436
553
  { pngScale }
437
554
  );
438
- const pngDownloads = pngNodes.map(
555
+ const pngTasks = pngNodes.map(
439
556
  ({
440
557
  nodeId,
441
558
  fileName,
@@ -450,7 +567,7 @@ class FigmaService {
450
567
  );
451
568
  return null;
452
569
  }
453
- return downloadAndProcessImage(
570
+ return () => downloadAndProcessImage(
454
571
  fileName,
455
572
  resolvedPath,
456
573
  imageUrl,
@@ -463,10 +580,12 @@ class FigmaService {
463
580
  );
464
581
  }
465
582
  ).filter(
466
- (promise) => promise !== null
583
+ (task) => task !== null
467
584
  );
468
- if (pngDownloads.length > 0) {
469
- downloadPromises.push(Promise.all(pngDownloads));
585
+ if (pngTasks.length > 0) {
586
+ downloadPromises.push(
587
+ runWithConcurrency(pngTasks, CONCURRENCY_LIMIT)
588
+ );
470
589
  }
471
590
  }
472
591
  if (svgNodes.length > 0) {
@@ -476,7 +595,7 @@ class FigmaService {
476
595
  "svg",
477
596
  { svgOptions }
478
597
  );
479
- const svgDownloads = svgNodes.map(
598
+ const svgTasks = svgNodes.map(
480
599
  ({
481
600
  nodeId,
482
601
  fileName,
@@ -491,7 +610,7 @@ class FigmaService {
491
610
  );
492
611
  return null;
493
612
  }
494
- return downloadAndProcessImage(
613
+ return () => downloadAndProcessImage(
495
614
  fileName,
496
615
  resolvedPath,
497
616
  imageUrl,
@@ -504,10 +623,12 @@ class FigmaService {
504
623
  );
505
624
  }
506
625
  ).filter(
507
- (promise) => promise !== null
626
+ (task) => task !== null
508
627
  );
509
- if (svgDownloads.length > 0) {
510
- downloadPromises.push(Promise.all(svgDownloads));
628
+ if (svgTasks.length > 0) {
629
+ downloadPromises.push(
630
+ runWithConcurrency(svgTasks, CONCURRENCY_LIMIT)
631
+ );
511
632
  }
512
633
  }
513
634
  }
@@ -539,106 +660,6 @@ class FigmaService {
539
660
  return response;
540
661
  }
541
662
  }
542
- async function downloadFigmaImage(fileName, localPath, imageUrl, returnBuffer = false) {
543
- try {
544
- const response = await fetch(imageUrl, {
545
- method: "GET"
546
- });
547
- if (!response.ok) {
548
- throw new Error(`Failed to download image: ${response.statusText}`);
549
- }
550
- if (returnBuffer) {
551
- const arrayBuffer = await response.arrayBuffer();
552
- return arrayBuffer;
553
- }
554
- if (!fs.existsSync(localPath)) {
555
- fs.mkdirSync(localPath, { recursive: true });
556
- }
557
- const fullPath = path.join(localPath, fileName);
558
- const reader = response.body?.getReader();
559
- if (!reader) {
560
- throw new Error("Failed to get response body");
561
- }
562
- const writer = fs.createWriteStream(fullPath);
563
- return new Promise((resolve, reject) => {
564
- const processStream = async () => {
565
- try {
566
- while (true) {
567
- const { done, value } = await reader.read();
568
- if (done) {
569
- writer.end();
570
- break;
571
- }
572
- writer.write(value);
573
- }
574
- } catch (err) {
575
- writer.end();
576
- fs.unlink(fullPath, () => {
577
- });
578
- reject(err);
579
- }
580
- };
581
- writer.on("finish", () => {
582
- resolve(fullPath);
583
- });
584
- writer.on("error", (err) => {
585
- reader.cancel();
586
- fs.unlink(fullPath, () => {
587
- });
588
- reject(new Error(`Failed to write image: ${err.message}`));
589
- });
590
- processStream();
591
- });
592
- } catch (error) {
593
- const errorMessage = error instanceof Error ? error.message : String(error);
594
- throw new Error(`Error downloading image: ${errorMessage}`);
595
- }
596
- }
597
- function generateVarId(prefix = "var") {
598
- const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
599
- let result = "";
600
- for (let i = 0; i < 6; i++) {
601
- const randomIndex = Math.floor(Math.random() * chars.length);
602
- result += chars[randomIndex];
603
- }
604
- return `${prefix}_${result}`;
605
- }
606
- function generateCSSShorthand(values, {
607
- ignoreZero = true,
608
- suffix = "px"
609
- } = {}) {
610
- const { top, right, bottom, left } = values;
611
- if (ignoreZero && top === 0 && right === 0 && bottom === 0 && left === 0) {
612
- return void 0;
613
- }
614
- if (top === right && right === bottom && bottom === left) {
615
- return `${top}${suffix}`;
616
- }
617
- if (right === left) {
618
- if (top === bottom) {
619
- return `${top}${suffix} ${right}${suffix}`;
620
- }
621
- return `${top}${suffix} ${right}${suffix} ${bottom}${suffix}`;
622
- }
623
- return `${top}${suffix} ${right}${suffix} ${bottom}${suffix} ${left}${suffix}`;
624
- }
625
- function isVisible(element) {
626
- return element.visible ?? true;
627
- }
628
- function pixelRound(num) {
629
- if (isNaN(num)) {
630
- throw new TypeError(`Input must be a valid number`);
631
- }
632
- return Number(Number(num).toFixed(2));
633
- }
634
- const common = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
635
- __proto__: null,
636
- downloadFigmaImage,
637
- generateCSSShorthand,
638
- generateVarId,
639
- isVisible,
640
- pixelRound
641
- }, Symbol.toStringTag, { value: "Module" }));
642
663
  function hasValue(key, obj, typeGuard) {
643
664
  const isObject = typeof obj === "object" && obj !== null;
644
665
  if (!isObject || !(key in obj)) return false;
@@ -68,3 +68,10 @@ export declare function isVisible(element: {
68
68
  * @throws TypeError If the input is not a valid number
69
69
  */
70
70
  export declare function pixelRound(num: number): number;
71
+ /**
72
+ * Run a list of async tasks with a concurrency limit
73
+ * @param tasks - Array of functions that return a Promise
74
+ * @param limit - Maximum number of concurrent tasks
75
+ * @returns Promise resolving to array of results
76
+ */
77
+ export declare function runWithConcurrency<T>(tasks: Array<() => Promise<T>>, limit: number): Promise<T[]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "figma-metadata-extractor",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "Extract metadata and download images from Figma files. A standalone library for accessing Figma design data and downloading frame images programmatically.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",