├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .simple-git-hooks.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── layout.png ├── package.json └── yarn.lock /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | 10 | 11 | **Describe the bug** 12 | A clear and concise description of what the bug is. 13 | 14 | **To Reproduce** 15 | Steps to reproduce the behavior: 16 | 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Screenshots** 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: enhancement 6 | assignees: "" 7 | --- 8 | 9 | 10 | 11 | **Is your feature request related to a problem? Please describe.** 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 13 | 14 | **Describe the solution you'd like** 15 | A clear and concise description of what you want to happen. Does the solution involve a change to functionality or additional scripts/tests/documentation? 16 | 17 | **Describe alternatives you've considered** 18 | A clear and concise description of any alternative solutions or features you've considered. 19 | 20 | **Additional context** 21 | Add any other context or screenshots about the feature request here. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /.simple-git-hooks.json: -------------------------------------------------------------------------------- 1 | { 2 | "pre-commit": "yarn pretty-quick --staged" 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## Unreleased 4 | 5 | - 6 | 7 | ## v0.1.8 8 | 9 | - Make loadFigmassets properly async 10 | - Let loadFigmassets optionally replace icons 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Stamen Design 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Figmasset 2 | 3 | Figmasset is a tool to facilitate bulk-loading assets from Figma into a JavaScript application. This makes it easy to load the latest version of assets under active development, including map marker icons, mockup UI overlays, legend images etc. Which means designers can iterate on assets, without needing developers to keep saving the latest copy of assets and redeploying. 4 | 5 | Read more on our blog: "[Figmasset: reducing design friction while making map markers](https://hi.stamen.com/figmasset-reducing-design-friction-while-making-map-markers-ea2d90cb061d)" and check out our more recent presentation at NACIS: "[Figmasset: A Missing Map Tool](https://www.youtube.com/watch?v=cxEquZj9T4Q)" 6 | 7 | Figmasset supports loading assets from several different frames in the same file. Assets loaded later will override earlier assets if they share a name. 8 | 9 | This has two main uses: 10 | 11 | 1. Loading different groups of assets worked on by different people 12 | 2. Loading a "base" set of icons and an "override" set, so you can iterate on designs of a few icons without having to duplicate all the base icons. 13 | 14 | Once your designs have stabilised, it is easy to export all assets into your web app. See "Moving to production". 15 | 16 | Figmasset can be used in Node by importing the `node-fetch` library and passing it as the `fetchFunc` parameter. 17 | 18 | **Note**: This library is still in early days and its interface is not stable. 19 | 20 | ### Setting up your assets in Figma 21 | 22 | In Figma, arrange your assets as top-level children under one or more frames. That is, your Layers sidebar should look something like this: 23 | 24 | ![](layout.png) 25 | 26 | ``` 27 | # pins 28 | # pin 29 | # pin-cluster 30 | # pin-selected 31 | # pin-cluster-selected 32 | # pins-alt-clustering 33 | # pin-cluster 34 | # pin-cluster-selected 35 | ``` 36 | 37 | (Some of the assets are frames, some are groups - this is fine.) 38 | 39 | Naming: 40 | 41 | - Top-level frame names should not contain spaces or commas. 42 | - Top-level frame names are _not_ case-sensitive. 43 | - Asset names _are_ case-sensitive. We recommend lower-case. 44 | 45 | Get the file key from the URL. Given a URL like https://www.figma.com/file/ABC123/Untitled?node-id=0%3A1, "ABC123" is your file key. 46 | 47 | ### Loading assets into Mapbox GL JS or Maplibre GL JS 48 | 49 | To retrieve your assets as icons and load them into a map, use `loadFigmassets()`, like this: 50 | 51 | ```js 52 | import { loadFigmassets } from "figmasset"; 53 | 54 | //... 55 | 56 | loadFigmassets({ 57 | map, // Mapbox GL JS or Maplibre GL JS object 58 | frameNames: ["pins", "pins-alt-clustering"], 59 | fileKey: "ABC123", 60 | personalAccessToken: "snt34h5sn24h5", // get this from your user > Settings page. Be careful who you expose this to, it provides unrestricted access to your account 61 | scales: [2], // pixel ratios. [1,2] fetches both @1x and @2x versions of each asset. 62 | }); 63 | 64 | // you can now access your icon like this: 65 | map.addLayer({ 66 | id: "mypin", 67 | type: "symbol", 68 | source: "pointsource", 69 | layout: { "icon-image": "pin" }, 70 | }); 71 | ``` 72 | 73 | `loadFigmassets()` is async, returning after icons have been generated by Figma, and are being loaded into your map. There is generally no need to `await` it, although you may get warnings in your console if you create map layers that reference icons that haven't been loaded yet. 74 | 75 | ### Loading your assets outside a map 76 | 77 | You can use Figmasset outside of a map (for instance, to overlay static images). It returns an object of all the 78 | 79 | ```js 80 | import { getFigmassets } from 'figmasset'; 81 | 82 | loadFigmassets({ 83 | frameNames = ['pins', 'pins-alt-clustering'], 84 | fileKey: 'ABC123', 85 | personalAccessToken: 'snt34h5sn24h5', // get this from your user > Settings page. Be careful who you expose this to, it provides unrestricted access to your account 86 | scales: [1, 2], // pixel ratios. [1,2] fetches both @1x and @2x versions of each asset. 87 | 88 | // other options: 89 | // frameIds = [], // you can specify frames by their node ID (in the URL) instead of frameNames 90 | // format: 'png', // svg is also supported 91 | // fetchFunc: nodeFetch // in the browser, window.fetch is used. If using in Node, pass in the node-fetch library. 92 | }); 93 | } 94 | ``` 95 | 96 | The Figma API can take a few seconds to generate PNG versions of assets. Once it returns, you will have an object of this form: 97 | 98 | ```js 99 | { 100 | pin: { 101 | id: '2:8', // the node ID 102 | '@1x': 'https://s3-us-west-2.amazonaws.com/figma-alpha-api/img/5b83/a061/e42384a5bc5a5ac5cabcb5a5cabcb5c5, 103 | '@2x': 'https://s3-us-west-2.amazonaws.com/figma-alpha-api/img/af20/18b77f7fe7ee57f5efe5ef5ee7eaa2f32aea0' 104 | }, 105 | 'pin-cluster': { ... }, 106 | 'pin-selected': { ... }, 107 | 'pin-cluster-selected': { ... }, 108 | } 109 | ``` 110 | 111 | Note that the assets named `pin-cluster` and `pin-cluster-selected` in the `pins-alt-clustering` frame will be used, rather than those in `pins`. That's because the names match, and `pins-alt-clustering` was specified after `pins`. 112 | 113 | ### Moving to production 114 | 115 | #### loadStoredFigmassets 116 | 117 | Once your asset designs have stabilised you will want to move to production without loading them from Figma dynamically. You can use [`figmasset-export`](https://www.npmjs.com/package/figmasset-export) for this. The process is: 118 | 119 | 1. Run figmasset-export to download assets and generate an asset metadata file, in a directory inside your web app. (Say its URL path is `static`, so you now have `static/assets@2x/assets.json` and so on). 120 | 121 | For instance: 122 | 123 | ``` 124 | cd static 125 | npx figmasset-export --file ABC123 --token snt34h5sn24h5 --frame 'pins,pins-alt-clustering' 126 | ``` 127 | 128 | 2. Switch out the `loadFigmassets()` call above with `loadStoredFigmassets()` like this: 129 | 130 | ```js 131 | loadStoredFigmassets({ map, path: "static/assets@2x" }); 132 | ``` 133 | 134 | #### Generate a spritesheet 135 | 136 | For better performance, you may want to generate a spritesheet instead of loading the icons one at a time, using `figmasset-export` and [`mbsprite`](https://www.npmjs.com/package/mbsprite) along these lines: 137 | 138 | ``` 139 | figmasset-export --file ABC123 --frame myframe myotherframe --frame pins --scale 1 2 --out assets 140 | npx mbsprite bundle sprite assets@1x assets@2x 141 | ``` 142 | 143 | See the `mbsprite` documentation for more details, including how to extract icons from an existing spritesheet to merge them. 144 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | function figmaApi({ personalAccessToken, fetchFunc }) { 2 | this._personalAccessToken = personalAccessToken; 3 | this._fetchFunc = fetchFunc; 4 | } 5 | figmaApi.prototype.get = async function (api, params) { 6 | const search = params ? "?" + new URLSearchParams(params).toString() : ""; 7 | const response = await this._fetchFunc( 8 | `https://api.figma.com/v1/${api}${search}`, 9 | { 10 | headers: { "X-Figma-Token": this._personalAccessToken }, 11 | }, 12 | ); 13 | if (response.status >= 400) { 14 | let extra = ""; 15 | try { 16 | extra += (await response.json()).err; 17 | } finally { 18 | throw new Error( 19 | `HTTP error ${response.status} accessing Figma. ${extra}`, 20 | ); 21 | } 22 | } 23 | return response.json(); 24 | }; 25 | 26 | // For each nodeId, find its corresponding node by searching the document. 27 | // Note this is pretty inefficient, we can end up traversing the entire document quite a few times. Likely insignificant on smallish documents. 28 | const findNodesById = function ({ file, nodeIds }) { 29 | function findNode(nodeId, searchNode) { 30 | if (nodeId === searchNode.id) { 31 | return searchNode; 32 | } else if (searchNode.children) { 33 | return searchNode.children.map((n) => findNode(nodeId, n)).find(Boolean); 34 | } else { 35 | return null; 36 | } 37 | } 38 | return nodeIds.map((nodeId) => findNode(nodeId, file.document)); 39 | }; 40 | 41 | const findNodeIdsForNames = function ({ file, names }) { 42 | // find node IDs for names with a depth-first search 43 | function findNameInNode(name, node) { 44 | if (name === node.name && node.type === "FRAME") { 45 | return node.id; 46 | } else if (node.children) { 47 | return node.children.map((n) => findNameInNode(name, n)).find(Boolean); 48 | } else { 49 | return null; 50 | } 51 | } 52 | 53 | return names.map((name) => findNameInNode(name, file.document)); 54 | }; 55 | 56 | /* Request images for some assets, and combine multiple scales into one object per asset */ 57 | async function getAssetImages({ 58 | api, 59 | fileKey, 60 | assetList, 61 | scales = [1], 62 | format = "png", 63 | }) { 64 | async function getImageList(scale) { 65 | return api.get(`images/${fileKey}`, { 66 | ids: Object.values(assetList), 67 | format, 68 | scale, 69 | }); 70 | } 71 | 72 | const assetsWithUrls = {}; 73 | const imageLists = await Promise.all( 74 | scales.map((scale) => getImageList(scale)), 75 | ); 76 | scales.forEach((scale, scaleIndex) => { 77 | for (const name of Object.keys(assetList)) { 78 | const nodeId = assetList[name]; 79 | assetsWithUrls[name] = assetsWithUrls[name] || { id: nodeId }; 80 | assetsWithUrls[name][`@${scale}x`] = 81 | imageLists[scaleIndex].images[nodeId]; 82 | } 83 | }); 84 | return assetsWithUrls; 85 | } 86 | 87 | // Given a number of frames, compiles a set of named top-level nodes within them. Later nodes of the same name replace earlier ones. 88 | function makeAssetList(frames) { 89 | const assetList = {}; 90 | for (const frame of frames.filter(Boolean)) { 91 | for (const node of frame.children) { 92 | assetList[node.name] = node.id; 93 | } 94 | } 95 | return assetList; 96 | } 97 | 98 | /* 99 | Returns URLs of rasterisations of all objects within one or more frames specified by ID or name, for one or more scales. 100 | Return format: 101 | { 102 | 'asset-name': { 103 | id: '102:5', 104 | '@1x': 'https://s3-us-west-2.amazonaws 105 | '@2x': '...' 106 | }, 107 | ... 108 | } 109 | */ 110 | async function getFigmassets({ 111 | frameIds = [], 112 | frameNames = [], 113 | fileKey, 114 | personalAccessToken, 115 | scales = [1], 116 | format = "png", 117 | fetchFunc = (...args) => window.fetch(...args), 118 | }) { 119 | const api = new figmaApi({ personalAccessToken, fetchFunc }); 120 | const file = await api.get(`files/${fileKey}`); 121 | if (frameNames.length) { 122 | frameIds = [ 123 | ...frameIds, 124 | ...findNodeIdsForNames({ file, names: frameNames }), 125 | ]; 126 | } 127 | const frames = findNodesById({ file, nodeIds: frameIds }).filter(Boolean); 128 | if (!frames.length) { 129 | throw new Error(`No matching frames for ${frameIds}, ${frameNames}`); 130 | } 131 | 132 | const assetList = makeAssetList(frames); 133 | 134 | return await getAssetImages({ api, fileKey, assetList, scales, format }); 135 | } 136 | 137 | function getScale(asset) { 138 | return Math.max( 139 | ...Object.keys(asset) 140 | .filter((k) => k[0] === "@") 141 | .map((k) => +k.replace(/[^0-9.]/g, "")), 142 | ); 143 | } 144 | 145 | async function addAssetToMap(map, iconId, asset, options = {}) { 146 | const { replace } = options; 147 | const scale = getScale(asset); 148 | 149 | if (replace && map.hasImage(iconId)) { 150 | map.removeImage(iconId); 151 | } 152 | 153 | return new Promise((resolve, reject) => { 154 | map.loadImage(asset[`@${scale}x`], (error, image) => { 155 | if (error) return reject(error); 156 | map.addImage(iconId, image, { 157 | pixelRatio: scale, 158 | }); 159 | resolve(); 160 | }); 161 | }); 162 | } 163 | 164 | async function addAssetsToMap(map, assets, options = {}) { 165 | return Promise.all( 166 | Object.entries(assets).map(([iconId, asset]) => 167 | addAssetToMap(map, iconId, asset, options), 168 | ), 169 | ); 170 | } 171 | 172 | async function loadFigmassets({ map, replace = false, ...otherArgs }) { 173 | if (map && !otherArgs.scales) { 174 | // makes sense to load map assets at 2x 175 | otherArgs.scales = [2]; 176 | } 177 | const assets = await getFigmassets(otherArgs); 178 | if (map) { 179 | await addAssetsToMap(map, assets, { replace }); 180 | } 181 | return assets; 182 | } 183 | 184 | // path is URL path to directory containing assets.json and every referenced image 185 | async function loadStoredFigmassets({ map, path = "" }) { 186 | if (path) { 187 | path = path.replace(/([^/])$/, "$1/"); 188 | } 189 | const assets = await fetch(`${path}assets.json`).then((r) => r.json()); 190 | for (const asset of assets) { 191 | map.loadImage(`${path}${asset.fileName}`, (error, image) => { 192 | map.addImage(asset.id, image, { pixelRatio: asset.scale }); 193 | }); 194 | } 195 | } 196 | 197 | export { 198 | getFigmassets as getFigmaIconsByFrames, 199 | getFigmassets, 200 | addAssetsToMap, 201 | loadFigmassets, 202 | loadStoredFigmassets, 203 | }; 204 | -------------------------------------------------------------------------------- /layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stamen/figmasset/3a47dbd07aaef2984ebf1efbb43256451c9988d4/layout.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "figmasset", 3 | "description": "Easily fetch a bunch of assets from Figma for possible use in a Mapbox GL JS map.", 4 | "author": { 5 | "name": "Stamen Design", 6 | "email": "info@stamen.com", 7 | "url": "https://stamen.com" 8 | }, 9 | "license": "MIT", 10 | "module": "./index.js", 11 | "exports": { 12 | ".": "./index.js" 13 | }, 14 | "keywords": [ 15 | "figma", 16 | "mapbox", 17 | "icon", 18 | "asset", 19 | "mapbox-gl-js", 20 | "maplibre-gl-js", 21 | "design" 22 | ], 23 | "version": "0.1.8", 24 | "main": "index.js", 25 | "files": [ 26 | "index.js", 27 | "layout.png" 28 | ], 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/stamen/figmasset" 32 | }, 33 | "dependencies": { 34 | "prettier": "^3.5.3" 35 | }, 36 | "devDependencies": { 37 | "lint-staged": "^15.5.1", 38 | "pretty-quick": "^4.1.1", 39 | "simple-git-hooks": "^2.12.1" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | ansi-escapes@^7.0.0: 6 | version "7.0.0" 7 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.0.0.tgz#00fc19f491bbb18e1d481b97868204f92109bfe7" 8 | integrity sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw== 9 | dependencies: 10 | environment "^1.0.0" 11 | 12 | ansi-regex@^6.0.1: 13 | version "6.1.0" 14 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" 15 | integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== 16 | 17 | ansi-styles@^6.0.0, ansi-styles@^6.2.1: 18 | version "6.2.1" 19 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" 20 | integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== 21 | 22 | braces@^3.0.3: 23 | version "3.0.3" 24 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" 25 | integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== 26 | dependencies: 27 | fill-range "^7.1.1" 28 | 29 | chalk@^5.4.1: 30 | version "5.4.1" 31 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8" 32 | integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== 33 | 34 | cli-cursor@^5.0.0: 35 | version "5.0.0" 36 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-5.0.0.tgz#24a4831ecf5a6b01ddeb32fb71a4b2088b0dce38" 37 | integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== 38 | dependencies: 39 | restore-cursor "^5.0.0" 40 | 41 | cli-truncate@^4.0.0: 42 | version "4.0.0" 43 | resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-4.0.0.tgz#6cc28a2924fee9e25ce91e973db56c7066e6172a" 44 | integrity sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA== 45 | dependencies: 46 | slice-ansi "^5.0.0" 47 | string-width "^7.0.0" 48 | 49 | colorette@^2.0.20: 50 | version "2.0.20" 51 | resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" 52 | integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== 53 | 54 | commander@^13.1.0: 55 | version "13.1.0" 56 | resolved "https://registry.yarnpkg.com/commander/-/commander-13.1.0.tgz#776167db68c78f38dcce1f9b8d7b8b9a488abf46" 57 | integrity sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw== 58 | 59 | cross-spawn@^7.0.3: 60 | version "7.0.6" 61 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" 62 | integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== 63 | dependencies: 64 | path-key "^3.1.0" 65 | shebang-command "^2.0.0" 66 | which "^2.0.1" 67 | 68 | debug@^4.4.0: 69 | version "4.4.0" 70 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" 71 | integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== 72 | dependencies: 73 | ms "^2.1.3" 74 | 75 | emoji-regex@^10.3.0: 76 | version "10.4.0" 77 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.4.0.tgz#03553afea80b3975749cfcb36f776ca268e413d4" 78 | integrity sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw== 79 | 80 | environment@^1.0.0: 81 | version "1.1.0" 82 | resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" 83 | integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== 84 | 85 | eventemitter3@^5.0.1: 86 | version "5.0.1" 87 | resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" 88 | integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== 89 | 90 | execa@^8.0.1: 91 | version "8.0.1" 92 | resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" 93 | integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== 94 | dependencies: 95 | cross-spawn "^7.0.3" 96 | get-stream "^8.0.1" 97 | human-signals "^5.0.0" 98 | is-stream "^3.0.0" 99 | merge-stream "^2.0.0" 100 | npm-run-path "^5.1.0" 101 | onetime "^6.0.0" 102 | signal-exit "^4.1.0" 103 | strip-final-newline "^3.0.0" 104 | 105 | fill-range@^7.1.1: 106 | version "7.1.1" 107 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" 108 | integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== 109 | dependencies: 110 | to-regex-range "^5.0.1" 111 | 112 | find-up@^5.0.0: 113 | version "5.0.0" 114 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" 115 | integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== 116 | dependencies: 117 | locate-path "^6.0.0" 118 | path-exists "^4.0.0" 119 | 120 | get-east-asian-width@^1.0.0: 121 | version "1.3.0" 122 | resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz#21b4071ee58ed04ee0db653371b55b4299875389" 123 | integrity sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ== 124 | 125 | get-stream@^8.0.1: 126 | version "8.0.1" 127 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" 128 | integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== 129 | 130 | human-signals@^5.0.0: 131 | version "5.0.0" 132 | resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" 133 | integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== 134 | 135 | ignore@^7.0.3: 136 | version "7.0.3" 137 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.3.tgz#397ef9315dfe0595671eefe8b633fec6943ab733" 138 | integrity sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA== 139 | 140 | is-fullwidth-code-point@^4.0.0: 141 | version "4.0.0" 142 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" 143 | integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== 144 | 145 | is-fullwidth-code-point@^5.0.0: 146 | version "5.0.0" 147 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz#9609efced7c2f97da7b60145ef481c787c7ba704" 148 | integrity sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA== 149 | dependencies: 150 | get-east-asian-width "^1.0.0" 151 | 152 | is-number@^7.0.0: 153 | version "7.0.0" 154 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 155 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 156 | 157 | is-stream@^3.0.0: 158 | version "3.0.0" 159 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" 160 | integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== 161 | 162 | isexe@^2.0.0: 163 | version "2.0.0" 164 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 165 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== 166 | 167 | lilconfig@^3.1.3: 168 | version "3.1.3" 169 | resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" 170 | integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== 171 | 172 | lint-staged@^15.5.1: 173 | version "15.5.1" 174 | resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.5.1.tgz#6de35298964641b8b6e060d3db0fb6ac866c6e24" 175 | integrity sha512-6m7u8mue4Xn6wK6gZvSCQwBvMBR36xfY24nF5bMTf2MHDYG6S3yhJuOgdYVw99hsjyDt2d4z168b3naI8+NWtQ== 176 | dependencies: 177 | chalk "^5.4.1" 178 | commander "^13.1.0" 179 | debug "^4.4.0" 180 | execa "^8.0.1" 181 | lilconfig "^3.1.3" 182 | listr2 "^8.2.5" 183 | micromatch "^4.0.8" 184 | pidtree "^0.6.0" 185 | string-argv "^0.3.2" 186 | yaml "^2.7.0" 187 | 188 | listr2@^8.2.5: 189 | version "8.3.2" 190 | resolved "https://registry.yarnpkg.com/listr2/-/listr2-8.3.2.tgz#c252ec9a3334950bfca9238457d0ad2c1a5cc867" 191 | integrity sha512-vsBzcU4oE+v0lj4FhVLzr9dBTv4/fHIa57l+GCwovP8MoFNZJTOhGU8PXd4v2VJCbECAaijBiHntiekFMLvo0g== 192 | dependencies: 193 | cli-truncate "^4.0.0" 194 | colorette "^2.0.20" 195 | eventemitter3 "^5.0.1" 196 | log-update "^6.1.0" 197 | rfdc "^1.4.1" 198 | wrap-ansi "^9.0.0" 199 | 200 | locate-path@^6.0.0: 201 | version "6.0.0" 202 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" 203 | integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== 204 | dependencies: 205 | p-locate "^5.0.0" 206 | 207 | log-update@^6.1.0: 208 | version "6.1.0" 209 | resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.1.0.tgz#1a04ff38166f94647ae1af562f4bd6a15b1b7cd4" 210 | integrity sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== 211 | dependencies: 212 | ansi-escapes "^7.0.0" 213 | cli-cursor "^5.0.0" 214 | slice-ansi "^7.1.0" 215 | strip-ansi "^7.1.0" 216 | wrap-ansi "^9.0.0" 217 | 218 | merge-stream@^2.0.0: 219 | version "2.0.0" 220 | resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" 221 | integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== 222 | 223 | micromatch@^4.0.8: 224 | version "4.0.8" 225 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" 226 | integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== 227 | dependencies: 228 | braces "^3.0.3" 229 | picomatch "^2.3.1" 230 | 231 | mimic-fn@^4.0.0: 232 | version "4.0.0" 233 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" 234 | integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== 235 | 236 | mimic-function@^5.0.0: 237 | version "5.0.1" 238 | resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" 239 | integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== 240 | 241 | mri@^1.2.0: 242 | version "1.2.0" 243 | resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" 244 | integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== 245 | 246 | ms@^2.1.3: 247 | version "2.1.3" 248 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 249 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 250 | 251 | npm-run-path@^5.1.0: 252 | version "5.3.0" 253 | resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" 254 | integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== 255 | dependencies: 256 | path-key "^4.0.0" 257 | 258 | onetime@^6.0.0: 259 | version "6.0.0" 260 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" 261 | integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== 262 | dependencies: 263 | mimic-fn "^4.0.0" 264 | 265 | onetime@^7.0.0: 266 | version "7.0.0" 267 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" 268 | integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== 269 | dependencies: 270 | mimic-function "^5.0.0" 271 | 272 | p-limit@^3.0.2: 273 | version "3.1.0" 274 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" 275 | integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== 276 | dependencies: 277 | yocto-queue "^0.1.0" 278 | 279 | p-locate@^5.0.0: 280 | version "5.0.0" 281 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" 282 | integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== 283 | dependencies: 284 | p-limit "^3.0.2" 285 | 286 | path-exists@^4.0.0: 287 | version "4.0.0" 288 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" 289 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== 290 | 291 | path-key@^3.1.0: 292 | version "3.1.1" 293 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" 294 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 295 | 296 | path-key@^4.0.0: 297 | version "4.0.0" 298 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" 299 | integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== 300 | 301 | picocolors@^1.1.1: 302 | version "1.1.1" 303 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" 304 | integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== 305 | 306 | picomatch@^2.3.1: 307 | version "2.3.1" 308 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 309 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 310 | 311 | picomatch@^4.0.2: 312 | version "4.0.2" 313 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" 314 | integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== 315 | 316 | pidtree@^0.6.0: 317 | version "0.6.0" 318 | resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" 319 | integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== 320 | 321 | prettier@^3.5.3: 322 | version "3.5.3" 323 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5" 324 | integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw== 325 | 326 | pretty-quick@^4.1.1: 327 | version "4.1.1" 328 | resolved "https://registry.yarnpkg.com/pretty-quick/-/pretty-quick-4.1.1.tgz#8a0c883e4beeb0eddbe7eda50d7b1251d4f72968" 329 | integrity sha512-9Ud0l/CspNTmyIdYac9X7Inb3o8fuUsw+1zJFvCGn+at0t1UwUcUdo2RSZ41gcmfLv1fxgWQxWEfItR7CBwugg== 330 | dependencies: 331 | find-up "^5.0.0" 332 | ignore "^7.0.3" 333 | mri "^1.2.0" 334 | picocolors "^1.1.1" 335 | picomatch "^4.0.2" 336 | tinyexec "^0.3.2" 337 | tslib "^2.8.1" 338 | 339 | restore-cursor@^5.0.0: 340 | version "5.1.0" 341 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-5.1.0.tgz#0766d95699efacb14150993f55baf0953ea1ebe7" 342 | integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== 343 | dependencies: 344 | onetime "^7.0.0" 345 | signal-exit "^4.1.0" 346 | 347 | rfdc@^1.4.1: 348 | version "1.4.1" 349 | resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" 350 | integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== 351 | 352 | shebang-command@^2.0.0: 353 | version "2.0.0" 354 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" 355 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 356 | dependencies: 357 | shebang-regex "^3.0.0" 358 | 359 | shebang-regex@^3.0.0: 360 | version "3.0.0" 361 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 362 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 363 | 364 | signal-exit@^4.1.0: 365 | version "4.1.0" 366 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" 367 | integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== 368 | 369 | simple-git-hooks@^2.12.1: 370 | version "2.12.1" 371 | resolved "https://registry.yarnpkg.com/simple-git-hooks/-/simple-git-hooks-2.12.1.tgz#5b688423b00bc1c4b6e4da42fdcce4c2d283d33b" 372 | integrity sha512-NB3V4XyCOrWTIhjh85DyEoVlM3adHWwqQXKYHmuegy/108bJPP6YxuPGm4ZKBq1+GVKRbKJuzNY//09cMJYp+A== 373 | 374 | slice-ansi@^5.0.0: 375 | version "5.0.0" 376 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" 377 | integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== 378 | dependencies: 379 | ansi-styles "^6.0.0" 380 | is-fullwidth-code-point "^4.0.0" 381 | 382 | slice-ansi@^7.1.0: 383 | version "7.1.0" 384 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.0.tgz#cd6b4655e298a8d1bdeb04250a433094b347b9a9" 385 | integrity sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg== 386 | dependencies: 387 | ansi-styles "^6.2.1" 388 | is-fullwidth-code-point "^5.0.0" 389 | 390 | string-argv@^0.3.2: 391 | version "0.3.2" 392 | resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" 393 | integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== 394 | 395 | string-width@^7.0.0: 396 | version "7.2.0" 397 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" 398 | integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== 399 | dependencies: 400 | emoji-regex "^10.3.0" 401 | get-east-asian-width "^1.0.0" 402 | strip-ansi "^7.1.0" 403 | 404 | strip-ansi@^7.1.0: 405 | version "7.1.0" 406 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" 407 | integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== 408 | dependencies: 409 | ansi-regex "^6.0.1" 410 | 411 | strip-final-newline@^3.0.0: 412 | version "3.0.0" 413 | resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" 414 | integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== 415 | 416 | tinyexec@^0.3.2: 417 | version "0.3.2" 418 | resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" 419 | integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== 420 | 421 | to-regex-range@^5.0.1: 422 | version "5.0.1" 423 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 424 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 425 | dependencies: 426 | is-number "^7.0.0" 427 | 428 | tslib@^2.8.1: 429 | version "2.8.1" 430 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" 431 | integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== 432 | 433 | which@^2.0.1: 434 | version "2.0.2" 435 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 436 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 437 | dependencies: 438 | isexe "^2.0.0" 439 | 440 | wrap-ansi@^9.0.0: 441 | version "9.0.0" 442 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.0.tgz#1a3dc8b70d85eeb8398ddfb1e4a02cd186e58b3e" 443 | integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== 444 | dependencies: 445 | ansi-styles "^6.2.1" 446 | string-width "^7.0.0" 447 | strip-ansi "^7.1.0" 448 | 449 | yaml@^2.7.0: 450 | version "2.7.1" 451 | resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.1.tgz#44a247d1b88523855679ac7fa7cda6ed7e135cf6" 452 | integrity sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ== 453 | 454 | yocto-queue@^0.1.0: 455 | version "0.1.0" 456 | resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" 457 | integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== 458 | --------------------------------------------------------------------------------