├── .editorconfig ├── .gitignore ├── .prettierrc.cjs ├── .scripts └── bundle.js ├── LICENSE ├── README.md ├── package.json ├── pnpm-lock.yaml ├── src └── index.ts ├── tests └── scheduler.test.ts ├── tsconfig-build.json ├── tsconfig.json ├── types.json └── vite.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | types/ 3 | index.d.ts 4 | .tsbuildinfo 5 | 6 | *~ 7 | *.sw[mnpcod] 8 | *.log 9 | *.lock* 10 | *.tmp 11 | *.tmp.* 12 | *.sln 13 | *.suo 14 | *.ntvs* 15 | *.njsproj 16 | log.txt 17 | *.sublime-project 18 | *.sublime-workspace 19 | .env 20 | .env.* 21 | debug.log 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | pnpm-debug.log* 26 | /libpeerconnection.log 27 | chrome-profiler-events*.json 28 | speed-measure-plugin*.json 29 | 30 | /.link 31 | /tmp 32 | /out-tsc 33 | /bazel-out 34 | /.pnp 35 | .temp/ 36 | .pnp.js 37 | .eslintcache 38 | .cspellcache 39 | .nyc_output/ 40 | .idea/ 41 | .versions/ 42 | node_modules/ 43 | web_modules/ 44 | coverage/ 45 | cypress-coverage/ 46 | $RECYCLE.BIN/ 47 | 48 | .DS_Store 49 | Thumbs.db 50 | UserInterfaceState.xcuserstate 51 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | useTabs: false, 3 | singleQuote: true, 4 | trailingComma: 'all', 5 | printWidth: 100, 6 | tabWidth: 2, 7 | overrides: [], 8 | }; 9 | -------------------------------------------------------------------------------- /.scripts/bundle.js: -------------------------------------------------------------------------------- 1 | import { build } from 'esbuild'; 2 | 3 | async function main() { 4 | await build({ 5 | entryPoints: ['src/index.ts'], 6 | outfile: `dist/index.js`, 7 | treeShaking: true, 8 | format: 'esm', 9 | bundle: true, 10 | platform: 'browser', 11 | target: 'es2019', 12 | write: true, 13 | watch: hasArg('-w'), 14 | minify: true, 15 | }); 16 | } 17 | 18 | function hasArg(arg) { 19 | return process.argv.includes(arg); 20 | } 21 | 22 | main().catch((e) => { 23 | console.error(e); 24 | process.exit(1); 25 | }); 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Rahim Alwer 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 | # Scheduler 2 | 3 | [![package-badge]][package] 4 | [![license-badge]][license] 5 | [![size-badge]][bundlephobia] 6 | 7 | This is a tiny (~250B minzipped) scheduler which batches and runs tasks off the microtask queue. 8 | 9 | ## Installation 10 | 11 | ```bash 12 | $: npm i @maverick-js/scheduler 13 | 14 | $: pnpm i @maverick-js/scheduler 15 | 16 | $: yarn add @maverick-js/scheduler 17 | ``` 18 | 19 | ## Usage 20 | 21 | ```js 22 | import { createScheduler } from '@maverick-js/scheduler'; 23 | 24 | const scheduler = createScheduler(); 25 | 26 | const taskA = () => {}; 27 | const taskB = () => {}; 28 | 29 | // Queue tasks. 30 | scheduler.enqueue(taskA); 31 | scheduler.enqueue(taskB); 32 | 33 | // Be notified of a flush. 34 | const stop = scheduler.onFlush(() => { 35 | console.log('Flushed!'); 36 | }); 37 | 38 | stop(); // unsubscribe 39 | 40 | // Schedule a flush - can be invoked more than once. 41 | scheduler.flush(); 42 | 43 | // Wait for flush to complete. 44 | await scheduler.tick; 45 | 46 | // Synchronously flush the queue whenever desired. 47 | scheduler.flushSync(); 48 | ``` 49 | 50 | Extra reading: 51 | 52 | - The [source file](./src/index.ts) is only ~80 LOC so feel free to dig through. 53 | - You can read more about microtasks on [MDN][mdn-microtasks]. 54 | 55 | ## Inspiration 56 | 57 | `@maverick-js/scheduler` was made possible based on my learnings from: 58 | 59 | - [Svelte Scheduler][svelte-scheduler] 60 | 61 | [package]: https://www.npmjs.com/package/@maverick-js/scheduler 62 | [package-badge]: https://img.shields.io/npm/v/@maverick-js/scheduler/latest 63 | [license]: https://github.com/maverick-js/scheduler/blob/main/LICENSE 64 | [license-badge]: https://img.shields.io/github/license/maverick-js/scheduler 65 | [size-badge]: https://img.shields.io/bundlephobia/minzip/@maverick-js/scheduler@^1.0.0 66 | [svelte-scheduler]: https://github.com/sveltejs/svelte/blob/master/src/runtime/internal/scheduler.ts 67 | [mdn-microtasks]: https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide 68 | [bundlephobia]: https://bundlephobia.com/package/@maverick-js/scheduler@^1.0.0 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@maverick-js/scheduler", 3 | "description": "A tiny (~250B) microtask scheduler.", 4 | "license": "MIT", 5 | "version": "2.1.0", 6 | "type": "module", 7 | "module": "dist/index.js", 8 | "sideEffects": false, 9 | "exports": { 10 | ".": "./dist/index.js", 11 | "./package.json": "./package.json" 12 | }, 13 | "files": [ 14 | "dist/", 15 | "index.d.ts" 16 | ], 17 | "scripts": { 18 | "bundle": "rimraf dist && node .scripts/bundle.js", 19 | "types": "tsc -p tsconfig-build.json && api-extractor run -c types.json && rimraf dist/*.d.ts", 20 | "test": "vitest --run", 21 | "format": "prettier src --write --loglevel warn", 22 | "test:watch": "vitest --watch", 23 | "validate": "pnpm test && pnpm bundle && pnpm types", 24 | "release": "pnpm validate && npm publish" 25 | }, 26 | "contributors": [ 27 | "Rahim Alwer " 28 | ], 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/maverick-js/scheduler.git" 32 | }, 33 | "bugs": { 34 | "url": "https://github.com/maverick-js/scheduler/issues" 35 | }, 36 | "devDependencies": { 37 | "@microsoft/api-extractor": "^7.25.3", 38 | "@types/node": "^18.0.0", 39 | "esbuild": "^0.14.47", 40 | "prettier": "^2.7.1", 41 | "rimraf": "^3.0.2", 42 | "typescript": "^4.7.4", 43 | "vite": "^2.9.12", 44 | "vitest": "^0.15.2" 45 | }, 46 | "publishConfig": { 47 | "access": "public" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.4 2 | 3 | specifiers: 4 | '@microsoft/api-extractor': ^7.25.3 5 | '@types/node': ^18.0.0 6 | esbuild: ^0.14.47 7 | prettier: ^2.7.1 8 | rimraf: ^3.0.2 9 | typescript: ^4.7.4 10 | vite: ^2.9.12 11 | vitest: ^0.15.2 12 | 13 | devDependencies: 14 | '@microsoft/api-extractor': 7.28.3 15 | '@types/node': 18.0.3 16 | esbuild: 0.14.48 17 | prettier: 2.7.1 18 | rimraf: 3.0.2 19 | typescript: 4.7.4 20 | vite: 2.9.13 21 | vitest: 0.15.2 22 | 23 | packages: 24 | 25 | /@microsoft/api-extractor-model/7.21.0: 26 | resolution: {integrity: sha512-NN4mXzoQWTuzznIcnLWeV6tGyn6Os9frDK6M/mmTXZ73vUYOvSWoKQ5SYzyzP7HF3YtvTmr1Rs+DsBb0HRx7WQ==} 27 | dependencies: 28 | '@microsoft/tsdoc': 0.14.1 29 | '@microsoft/tsdoc-config': 0.16.1 30 | '@rushstack/node-core-library': 3.49.0 31 | dev: true 32 | 33 | /@microsoft/api-extractor/7.28.3: 34 | resolution: {integrity: sha512-lkDHPyln8MNEy1QHjmGwedRquclGKU0qL0gHplfnHuSTXSoNQ86UYaPmhG77/GiNehXzGNKMYSIfTsuoQb69jA==} 35 | hasBin: true 36 | dependencies: 37 | '@microsoft/api-extractor-model': 7.21.0 38 | '@microsoft/tsdoc': 0.14.1 39 | '@microsoft/tsdoc-config': 0.16.1 40 | '@rushstack/node-core-library': 3.49.0 41 | '@rushstack/rig-package': 0.3.13 42 | '@rushstack/ts-command-line': 4.12.1 43 | colors: 1.2.5 44 | lodash: 4.17.21 45 | resolve: 1.17.0 46 | semver: 7.3.7 47 | source-map: 0.6.1 48 | typescript: 4.6.4 49 | dev: true 50 | 51 | /@microsoft/tsdoc-config/0.16.1: 52 | resolution: {integrity: sha512-2RqkwiD4uN6MLnHFljqBlZIXlt/SaUT6cuogU1w2ARw4nKuuppSmR0+s+NC+7kXBQykd9zzu0P4HtBpZT5zBpQ==} 53 | dependencies: 54 | '@microsoft/tsdoc': 0.14.1 55 | ajv: 6.12.6 56 | jju: 1.4.0 57 | resolve: 1.19.0 58 | dev: true 59 | 60 | /@microsoft/tsdoc/0.14.1: 61 | resolution: {integrity: sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==} 62 | dev: true 63 | 64 | /@rushstack/node-core-library/3.49.0: 65 | resolution: {integrity: sha512-yBJRzGgUNFwulVrwwBARhbGaHsxVMjsZ9JwU1uSBbqPYCdac+t2HYdzi4f4q/Zpgb0eNbwYj2yxgHYpJORNEaw==} 66 | dependencies: 67 | '@types/node': 12.20.24 68 | colors: 1.2.5 69 | fs-extra: 7.0.1 70 | import-lazy: 4.0.0 71 | jju: 1.4.0 72 | resolve: 1.17.0 73 | semver: 7.3.7 74 | timsort: 0.3.0 75 | z-schema: 5.0.3 76 | dev: true 77 | 78 | /@rushstack/rig-package/0.3.13: 79 | resolution: {integrity: sha512-4/2+yyA/uDl7LQvtYtFs1AkhSWuaIGEKhP9/KK2nNARqOVc5eCXmu1vyOqr5mPvNq7sHoIR+sG84vFbaKYGaDA==} 80 | dependencies: 81 | resolve: 1.17.0 82 | strip-json-comments: 3.1.1 83 | dev: true 84 | 85 | /@rushstack/ts-command-line/4.12.1: 86 | resolution: {integrity: sha512-S1Nev6h/kNnamhHeGdp30WgxZTA+B76SJ/P721ctP7DrnC+rrjAc6h/R80I4V0cA2QuEEcMdVOQCtK2BTjsOiQ==} 87 | dependencies: 88 | '@types/argparse': 1.0.38 89 | argparse: 1.0.10 90 | colors: 1.2.5 91 | string-argv: 0.3.1 92 | dev: true 93 | 94 | /@types/argparse/1.0.38: 95 | resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} 96 | dev: true 97 | 98 | /@types/chai-subset/1.3.3: 99 | resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} 100 | dependencies: 101 | '@types/chai': 4.3.1 102 | dev: true 103 | 104 | /@types/chai/4.3.1: 105 | resolution: {integrity: sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==} 106 | dev: true 107 | 108 | /@types/node/12.20.24: 109 | resolution: {integrity: sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==} 110 | dev: true 111 | 112 | /@types/node/18.0.3: 113 | resolution: {integrity: sha512-HzNRZtp4eepNitP+BD6k2L6DROIDG4Q0fm4x+dwfsr6LGmROENnok75VGw40628xf+iR24WeMFcHuuBDUAzzsQ==} 114 | dev: true 115 | 116 | /ajv/6.12.6: 117 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 118 | dependencies: 119 | fast-deep-equal: 3.1.3 120 | fast-json-stable-stringify: 2.1.0 121 | json-schema-traverse: 0.4.1 122 | uri-js: 4.4.1 123 | dev: true 124 | 125 | /argparse/1.0.10: 126 | resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} 127 | dependencies: 128 | sprintf-js: 1.0.3 129 | dev: true 130 | 131 | /assertion-error/1.1.0: 132 | resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} 133 | dev: true 134 | 135 | /balanced-match/1.0.2: 136 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 137 | dev: true 138 | 139 | /brace-expansion/1.1.11: 140 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 141 | dependencies: 142 | balanced-match: 1.0.2 143 | concat-map: 0.0.1 144 | dev: true 145 | 146 | /chai/4.3.6: 147 | resolution: {integrity: sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==} 148 | engines: {node: '>=4'} 149 | dependencies: 150 | assertion-error: 1.1.0 151 | check-error: 1.0.2 152 | deep-eql: 3.0.1 153 | get-func-name: 2.0.0 154 | loupe: 2.3.4 155 | pathval: 1.1.1 156 | type-detect: 4.0.8 157 | dev: true 158 | 159 | /check-error/1.0.2: 160 | resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} 161 | dev: true 162 | 163 | /colors/1.2.5: 164 | resolution: {integrity: sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==} 165 | engines: {node: '>=0.1.90'} 166 | dev: true 167 | 168 | /commander/2.20.3: 169 | resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} 170 | requiresBuild: true 171 | dev: true 172 | optional: true 173 | 174 | /concat-map/0.0.1: 175 | resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} 176 | dev: true 177 | 178 | /debug/4.3.4: 179 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 180 | engines: {node: '>=6.0'} 181 | peerDependencies: 182 | supports-color: '*' 183 | peerDependenciesMeta: 184 | supports-color: 185 | optional: true 186 | dependencies: 187 | ms: 2.1.2 188 | dev: true 189 | 190 | /deep-eql/3.0.1: 191 | resolution: {integrity: sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==} 192 | engines: {node: '>=0.12'} 193 | dependencies: 194 | type-detect: 4.0.8 195 | dev: true 196 | 197 | /esbuild-android-64/0.14.48: 198 | resolution: {integrity: sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg==} 199 | engines: {node: '>=12'} 200 | cpu: [x64] 201 | os: [android] 202 | requiresBuild: true 203 | dev: true 204 | optional: true 205 | 206 | /esbuild-android-arm64/0.14.48: 207 | resolution: {integrity: sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g==} 208 | engines: {node: '>=12'} 209 | cpu: [arm64] 210 | os: [android] 211 | requiresBuild: true 212 | dev: true 213 | optional: true 214 | 215 | /esbuild-darwin-64/0.14.48: 216 | resolution: {integrity: sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA==} 217 | engines: {node: '>=12'} 218 | cpu: [x64] 219 | os: [darwin] 220 | requiresBuild: true 221 | dev: true 222 | optional: true 223 | 224 | /esbuild-darwin-arm64/0.14.48: 225 | resolution: {integrity: sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA==} 226 | engines: {node: '>=12'} 227 | cpu: [arm64] 228 | os: [darwin] 229 | requiresBuild: true 230 | dev: true 231 | optional: true 232 | 233 | /esbuild-freebsd-64/0.14.48: 234 | resolution: {integrity: sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA==} 235 | engines: {node: '>=12'} 236 | cpu: [x64] 237 | os: [freebsd] 238 | requiresBuild: true 239 | dev: true 240 | optional: true 241 | 242 | /esbuild-freebsd-arm64/0.14.48: 243 | resolution: {integrity: sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw==} 244 | engines: {node: '>=12'} 245 | cpu: [arm64] 246 | os: [freebsd] 247 | requiresBuild: true 248 | dev: true 249 | optional: true 250 | 251 | /esbuild-linux-32/0.14.48: 252 | resolution: {integrity: sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ==} 253 | engines: {node: '>=12'} 254 | cpu: [ia32] 255 | os: [linux] 256 | requiresBuild: true 257 | dev: true 258 | optional: true 259 | 260 | /esbuild-linux-64/0.14.48: 261 | resolution: {integrity: sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug==} 262 | engines: {node: '>=12'} 263 | cpu: [x64] 264 | os: [linux] 265 | requiresBuild: true 266 | dev: true 267 | optional: true 268 | 269 | /esbuild-linux-arm/0.14.48: 270 | resolution: {integrity: sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw==} 271 | engines: {node: '>=12'} 272 | cpu: [arm] 273 | os: [linux] 274 | requiresBuild: true 275 | dev: true 276 | optional: true 277 | 278 | /esbuild-linux-arm64/0.14.48: 279 | resolution: {integrity: sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw==} 280 | engines: {node: '>=12'} 281 | cpu: [arm64] 282 | os: [linux] 283 | requiresBuild: true 284 | dev: true 285 | optional: true 286 | 287 | /esbuild-linux-mips64le/0.14.48: 288 | resolution: {integrity: sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA==} 289 | engines: {node: '>=12'} 290 | cpu: [mips64el] 291 | os: [linux] 292 | requiresBuild: true 293 | dev: true 294 | optional: true 295 | 296 | /esbuild-linux-ppc64le/0.14.48: 297 | resolution: {integrity: sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ==} 298 | engines: {node: '>=12'} 299 | cpu: [ppc64] 300 | os: [linux] 301 | requiresBuild: true 302 | dev: true 303 | optional: true 304 | 305 | /esbuild-linux-riscv64/0.14.48: 306 | resolution: {integrity: sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA==} 307 | engines: {node: '>=12'} 308 | cpu: [riscv64] 309 | os: [linux] 310 | requiresBuild: true 311 | dev: true 312 | optional: true 313 | 314 | /esbuild-linux-s390x/0.14.48: 315 | resolution: {integrity: sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g==} 316 | engines: {node: '>=12'} 317 | cpu: [s390x] 318 | os: [linux] 319 | requiresBuild: true 320 | dev: true 321 | optional: true 322 | 323 | /esbuild-netbsd-64/0.14.48: 324 | resolution: {integrity: sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw==} 325 | engines: {node: '>=12'} 326 | cpu: [x64] 327 | os: [netbsd] 328 | requiresBuild: true 329 | dev: true 330 | optional: true 331 | 332 | /esbuild-openbsd-64/0.14.48: 333 | resolution: {integrity: sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA==} 334 | engines: {node: '>=12'} 335 | cpu: [x64] 336 | os: [openbsd] 337 | requiresBuild: true 338 | dev: true 339 | optional: true 340 | 341 | /esbuild-sunos-64/0.14.48: 342 | resolution: {integrity: sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA==} 343 | engines: {node: '>=12'} 344 | cpu: [x64] 345 | os: [sunos] 346 | requiresBuild: true 347 | dev: true 348 | optional: true 349 | 350 | /esbuild-windows-32/0.14.48: 351 | resolution: {integrity: sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg==} 352 | engines: {node: '>=12'} 353 | cpu: [ia32] 354 | os: [win32] 355 | requiresBuild: true 356 | dev: true 357 | optional: true 358 | 359 | /esbuild-windows-64/0.14.48: 360 | resolution: {integrity: sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA==} 361 | engines: {node: '>=12'} 362 | cpu: [x64] 363 | os: [win32] 364 | requiresBuild: true 365 | dev: true 366 | optional: true 367 | 368 | /esbuild-windows-arm64/0.14.48: 369 | resolution: {integrity: sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g==} 370 | engines: {node: '>=12'} 371 | cpu: [arm64] 372 | os: [win32] 373 | requiresBuild: true 374 | dev: true 375 | optional: true 376 | 377 | /esbuild/0.14.48: 378 | resolution: {integrity: sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA==} 379 | engines: {node: '>=12'} 380 | hasBin: true 381 | requiresBuild: true 382 | optionalDependencies: 383 | esbuild-android-64: 0.14.48 384 | esbuild-android-arm64: 0.14.48 385 | esbuild-darwin-64: 0.14.48 386 | esbuild-darwin-arm64: 0.14.48 387 | esbuild-freebsd-64: 0.14.48 388 | esbuild-freebsd-arm64: 0.14.48 389 | esbuild-linux-32: 0.14.48 390 | esbuild-linux-64: 0.14.48 391 | esbuild-linux-arm: 0.14.48 392 | esbuild-linux-arm64: 0.14.48 393 | esbuild-linux-mips64le: 0.14.48 394 | esbuild-linux-ppc64le: 0.14.48 395 | esbuild-linux-riscv64: 0.14.48 396 | esbuild-linux-s390x: 0.14.48 397 | esbuild-netbsd-64: 0.14.48 398 | esbuild-openbsd-64: 0.14.48 399 | esbuild-sunos-64: 0.14.48 400 | esbuild-windows-32: 0.14.48 401 | esbuild-windows-64: 0.14.48 402 | esbuild-windows-arm64: 0.14.48 403 | dev: true 404 | 405 | /fast-deep-equal/3.1.3: 406 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 407 | dev: true 408 | 409 | /fast-json-stable-stringify/2.1.0: 410 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 411 | dev: true 412 | 413 | /fs-extra/7.0.1: 414 | resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} 415 | engines: {node: '>=6 <7 || >=8'} 416 | dependencies: 417 | graceful-fs: 4.2.10 418 | jsonfile: 4.0.0 419 | universalify: 0.1.2 420 | dev: true 421 | 422 | /fs.realpath/1.0.0: 423 | resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} 424 | dev: true 425 | 426 | /fsevents/2.3.2: 427 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 428 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 429 | os: [darwin] 430 | requiresBuild: true 431 | dev: true 432 | optional: true 433 | 434 | /function-bind/1.1.1: 435 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} 436 | dev: true 437 | 438 | /get-func-name/2.0.0: 439 | resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} 440 | dev: true 441 | 442 | /glob/7.2.3: 443 | resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} 444 | dependencies: 445 | fs.realpath: 1.0.0 446 | inflight: 1.0.6 447 | inherits: 2.0.4 448 | minimatch: 3.1.2 449 | once: 1.4.0 450 | path-is-absolute: 1.0.1 451 | dev: true 452 | 453 | /graceful-fs/4.2.10: 454 | resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} 455 | dev: true 456 | 457 | /has/1.0.3: 458 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} 459 | engines: {node: '>= 0.4.0'} 460 | dependencies: 461 | function-bind: 1.1.1 462 | dev: true 463 | 464 | /import-lazy/4.0.0: 465 | resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} 466 | engines: {node: '>=8'} 467 | dev: true 468 | 469 | /inflight/1.0.6: 470 | resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} 471 | dependencies: 472 | once: 1.4.0 473 | wrappy: 1.0.2 474 | dev: true 475 | 476 | /inherits/2.0.4: 477 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 478 | dev: true 479 | 480 | /is-core-module/2.9.0: 481 | resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==} 482 | dependencies: 483 | has: 1.0.3 484 | dev: true 485 | 486 | /jju/1.4.0: 487 | resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} 488 | dev: true 489 | 490 | /json-schema-traverse/0.4.1: 491 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 492 | dev: true 493 | 494 | /jsonfile/4.0.0: 495 | resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} 496 | optionalDependencies: 497 | graceful-fs: 4.2.10 498 | dev: true 499 | 500 | /local-pkg/0.4.1: 501 | resolution: {integrity: sha512-lL87ytIGP2FU5PWwNDo0w3WhIo2gopIAxPg9RxDYF7m4rr5ahuZxP22xnJHIvaLTe4Z9P6uKKY2UHiwyB4pcrw==} 502 | engines: {node: '>=14'} 503 | dev: true 504 | 505 | /lodash.get/4.4.2: 506 | resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} 507 | dev: true 508 | 509 | /lodash.isequal/4.5.0: 510 | resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} 511 | dev: true 512 | 513 | /lodash/4.17.21: 514 | resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} 515 | dev: true 516 | 517 | /loupe/2.3.4: 518 | resolution: {integrity: sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==} 519 | dependencies: 520 | get-func-name: 2.0.0 521 | dev: true 522 | 523 | /lru-cache/6.0.0: 524 | resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} 525 | engines: {node: '>=10'} 526 | dependencies: 527 | yallist: 4.0.0 528 | dev: true 529 | 530 | /minimatch/3.1.2: 531 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 532 | dependencies: 533 | brace-expansion: 1.1.11 534 | dev: true 535 | 536 | /ms/2.1.2: 537 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 538 | dev: true 539 | 540 | /nanoid/3.3.4: 541 | resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} 542 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 543 | hasBin: true 544 | dev: true 545 | 546 | /once/1.4.0: 547 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 548 | dependencies: 549 | wrappy: 1.0.2 550 | dev: true 551 | 552 | /path-is-absolute/1.0.1: 553 | resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} 554 | engines: {node: '>=0.10.0'} 555 | dev: true 556 | 557 | /path-parse/1.0.7: 558 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 559 | dev: true 560 | 561 | /pathval/1.1.1: 562 | resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} 563 | dev: true 564 | 565 | /picocolors/1.0.0: 566 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 567 | dev: true 568 | 569 | /postcss/8.4.14: 570 | resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==} 571 | engines: {node: ^10 || ^12 || >=14} 572 | dependencies: 573 | nanoid: 3.3.4 574 | picocolors: 1.0.0 575 | source-map-js: 1.0.2 576 | dev: true 577 | 578 | /prettier/2.7.1: 579 | resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==} 580 | engines: {node: '>=10.13.0'} 581 | hasBin: true 582 | dev: true 583 | 584 | /punycode/2.1.1: 585 | resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} 586 | engines: {node: '>=6'} 587 | dev: true 588 | 589 | /resolve/1.17.0: 590 | resolution: {integrity: sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==} 591 | dependencies: 592 | path-parse: 1.0.7 593 | dev: true 594 | 595 | /resolve/1.19.0: 596 | resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} 597 | dependencies: 598 | is-core-module: 2.9.0 599 | path-parse: 1.0.7 600 | dev: true 601 | 602 | /resolve/1.22.1: 603 | resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} 604 | hasBin: true 605 | dependencies: 606 | is-core-module: 2.9.0 607 | path-parse: 1.0.7 608 | supports-preserve-symlinks-flag: 1.0.0 609 | dev: true 610 | 611 | /rimraf/3.0.2: 612 | resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} 613 | hasBin: true 614 | dependencies: 615 | glob: 7.2.3 616 | dev: true 617 | 618 | /rollup/2.75.7: 619 | resolution: {integrity: sha512-VSE1iy0eaAYNCxEXaleThdFXqZJ42qDBatAwrfnPlENEZ8erQ+0LYX4JXOLPceWfZpV1VtZwZ3dFCuOZiSyFtQ==} 620 | engines: {node: '>=10.0.0'} 621 | hasBin: true 622 | optionalDependencies: 623 | fsevents: 2.3.2 624 | dev: true 625 | 626 | /semver/7.3.7: 627 | resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} 628 | engines: {node: '>=10'} 629 | hasBin: true 630 | dependencies: 631 | lru-cache: 6.0.0 632 | dev: true 633 | 634 | /source-map-js/1.0.2: 635 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 636 | engines: {node: '>=0.10.0'} 637 | dev: true 638 | 639 | /source-map/0.6.1: 640 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 641 | engines: {node: '>=0.10.0'} 642 | dev: true 643 | 644 | /sprintf-js/1.0.3: 645 | resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} 646 | dev: true 647 | 648 | /string-argv/0.3.1: 649 | resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==} 650 | engines: {node: '>=0.6.19'} 651 | dev: true 652 | 653 | /strip-json-comments/3.1.1: 654 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 655 | engines: {node: '>=8'} 656 | dev: true 657 | 658 | /supports-preserve-symlinks-flag/1.0.0: 659 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 660 | engines: {node: '>= 0.4'} 661 | dev: true 662 | 663 | /timsort/0.3.0: 664 | resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==} 665 | dev: true 666 | 667 | /tinypool/0.1.3: 668 | resolution: {integrity: sha512-2IfcQh7CP46XGWGGbdyO4pjcKqsmVqFAPcXfPxcPXmOWt9cYkTP9HcDmGgsfijYoAEc4z9qcpM/BaBz46Y9/CQ==} 669 | engines: {node: '>=14.0.0'} 670 | dev: true 671 | 672 | /tinyspy/0.3.3: 673 | resolution: {integrity: sha512-gRiUR8fuhUf0W9lzojPf1N1euJYA30ISebSfgca8z76FOvXtVXqd5ojEIaKLWbDQhAaC3ibxZIjqbyi4ybjcTw==} 674 | engines: {node: '>=14.0.0'} 675 | dev: true 676 | 677 | /type-detect/4.0.8: 678 | resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} 679 | engines: {node: '>=4'} 680 | dev: true 681 | 682 | /typescript/4.6.4: 683 | resolution: {integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==} 684 | engines: {node: '>=4.2.0'} 685 | hasBin: true 686 | dev: true 687 | 688 | /typescript/4.7.4: 689 | resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} 690 | engines: {node: '>=4.2.0'} 691 | hasBin: true 692 | dev: true 693 | 694 | /universalify/0.1.2: 695 | resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} 696 | engines: {node: '>= 4.0.0'} 697 | dev: true 698 | 699 | /uri-js/4.4.1: 700 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 701 | dependencies: 702 | punycode: 2.1.1 703 | dev: true 704 | 705 | /validator/13.7.0: 706 | resolution: {integrity: sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==} 707 | engines: {node: '>= 0.10'} 708 | dev: true 709 | 710 | /vite/2.9.13: 711 | resolution: {integrity: sha512-AsOBAaT0AD7Mhe8DuK+/kE4aWYFMx/i0ZNi98hJclxb4e0OhQcZYUrvLjIaQ8e59Ui7txcvKMiJC1yftqpQoDw==} 712 | engines: {node: '>=12.2.0'} 713 | hasBin: true 714 | peerDependencies: 715 | less: '*' 716 | sass: '*' 717 | stylus: '*' 718 | peerDependenciesMeta: 719 | less: 720 | optional: true 721 | sass: 722 | optional: true 723 | stylus: 724 | optional: true 725 | dependencies: 726 | esbuild: 0.14.48 727 | postcss: 8.4.14 728 | resolve: 1.22.1 729 | rollup: 2.75.7 730 | optionalDependencies: 731 | fsevents: 2.3.2 732 | dev: true 733 | 734 | /vitest/0.15.2: 735 | resolution: {integrity: sha512-cMabuUqu+nNHafkdN7H8Z20+UZTrrUfqjGwAoLwUwrqFGWBz3gXwxndjbLf6mgSFs9lF/JWjKeNM1CXKwtk26w==} 736 | engines: {node: '>=v14.16.0'} 737 | hasBin: true 738 | peerDependencies: 739 | '@vitest/ui': '*' 740 | c8: '*' 741 | happy-dom: '*' 742 | jsdom: '*' 743 | peerDependenciesMeta: 744 | '@vitest/ui': 745 | optional: true 746 | c8: 747 | optional: true 748 | happy-dom: 749 | optional: true 750 | jsdom: 751 | optional: true 752 | dependencies: 753 | '@types/chai': 4.3.1 754 | '@types/chai-subset': 1.3.3 755 | '@types/node': 18.0.3 756 | chai: 4.3.6 757 | debug: 4.3.4 758 | local-pkg: 0.4.1 759 | tinypool: 0.1.3 760 | tinyspy: 0.3.3 761 | vite: 2.9.13 762 | transitivePeerDependencies: 763 | - less 764 | - sass 765 | - stylus 766 | - supports-color 767 | dev: true 768 | 769 | /wrappy/1.0.2: 770 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 771 | dev: true 772 | 773 | /yallist/4.0.0: 774 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} 775 | dev: true 776 | 777 | /z-schema/5.0.3: 778 | resolution: {integrity: sha512-sGvEcBOTNum68x9jCpCVGPFJ6mWnkD0YxOcddDlJHRx3tKdB2q8pCHExMVZo/AV/6geuVJXG7hljDaWG8+5GDw==} 779 | engines: {node: '>=8.0.0'} 780 | hasBin: true 781 | dependencies: 782 | lodash.get: 4.4.2 783 | lodash.isequal: 4.5.0 784 | validator: 13.7.0 785 | optionalDependencies: 786 | commander: 2.20.3 787 | dev: true 788 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export type ScheduledTask = () => void; 2 | export type StopFlushUpdates = () => void; 3 | 4 | export type Scheduler = { 5 | tick: Promise; 6 | enqueue: (task: ScheduledTask) => void; 7 | flush: () => void; 8 | flushSync: () => void; 9 | onBeforeFlush: (callback: () => void) => StopFlushUpdates; 10 | onFlush: (callback: () => void) => StopFlushUpdates; 11 | }; 12 | 13 | /** 14 | * Creates a scheduler which batches tasks and runs them in the microtask queue. 15 | * 16 | * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide} 17 | * @example 18 | * ```ts 19 | * const scheduler = createScheduler(); 20 | * 21 | * // Queue tasks. 22 | * scheduler.enqueue(() => {}); 23 | * scheduler.enqueue(() => {}); 24 | * 25 | * // Schedule a flush - can be invoked more than once. 26 | * scheduler.flush(); 27 | * 28 | * // Wait for flush to complete. 29 | * await scheduler.tick; 30 | * ``` 31 | */ 32 | export function createScheduler(): Scheduler { 33 | const queue = new Set(); 34 | const microtask = Promise.resolve(); 35 | const beforeCallbacks = new Set<() => void>(); 36 | const afterCallbacks = new Set<() => void>(); 37 | const queueTask = typeof queueMicrotask !== 'undefined' ? queueMicrotask : microtask.then; 38 | 39 | const enqueue = (task: ScheduledTask) => { 40 | queue.add(task); 41 | scheduleFlush(); 42 | }; 43 | 44 | let flushing = false; 45 | const scheduleFlush = () => { 46 | if (!flushing) { 47 | flushing = true; 48 | queueTask(flush); 49 | } 50 | }; 51 | 52 | const flush = () => { 53 | runAll(beforeCallbacks); 54 | 55 | for (const task of queue) { 56 | task(); 57 | queue.delete(task); 58 | } 59 | 60 | flushing = false; 61 | runAll(afterCallbacks); 62 | }; 63 | 64 | return { 65 | tick: microtask, 66 | enqueue, 67 | flush: scheduleFlush, 68 | flushSync: flush, 69 | onBeforeFlush: hook(beforeCallbacks), 70 | onFlush: hook(afterCallbacks), 71 | }; 72 | } 73 | 74 | function hook(callbacks: Set<() => void>) { 75 | return (callback: () => void) => { 76 | callbacks.add(callback); 77 | return () => callbacks.delete(callback); 78 | }; 79 | } 80 | 81 | function runAll(callbacks: Set<() => void>) { 82 | for (const callback of callbacks) callback(); 83 | } 84 | -------------------------------------------------------------------------------- /tests/scheduler.test.ts: -------------------------------------------------------------------------------- 1 | import { createScheduler } from '../src'; 2 | 3 | it('should batch tasks', async () => { 4 | const scheduler = createScheduler(); 5 | 6 | let a = 0; 7 | 8 | scheduler.enqueue(() => { 9 | a = 2; 10 | }); 11 | 12 | scheduler.enqueue(() => { 13 | a = 3; 14 | }); 15 | 16 | scheduler.enqueue(() => { 17 | a = 4; 18 | }); 19 | 20 | expect(a).toBe(0); 21 | 22 | scheduler.flush(); 23 | expect(a).toBe(0); 24 | 25 | await scheduler.tick; 26 | expect(a).toBe(4); 27 | }); 28 | 29 | it('should flush queue synchronously', async () => { 30 | const scheduler = createScheduler(); 31 | 32 | let a = 0; 33 | 34 | scheduler.enqueue(() => { 35 | a = 2; 36 | }); 37 | 38 | scheduler.enqueue(() => { 39 | a = 3; 40 | }); 41 | 42 | scheduler.flushSync(); 43 | expect(a).toBe(3); 44 | }); 45 | 46 | it('should call `onBeforeFlush` callbacks', () => { 47 | const scheduler = createScheduler(); 48 | 49 | const callbackA = vi.fn(); 50 | const callbackB = vi.fn(); 51 | 52 | const removeA = scheduler.onBeforeFlush(callbackA); 53 | const removeB = scheduler.onBeforeFlush(callbackB); 54 | 55 | scheduler.flushSync(); 56 | 57 | expect(callbackA).toHaveBeenCalledTimes(1); 58 | expect(callbackB).toHaveBeenCalledTimes(1); 59 | 60 | removeA(); 61 | removeB(); 62 | 63 | scheduler.flushSync(); 64 | 65 | expect(callbackA).toHaveBeenCalledTimes(1); 66 | expect(callbackB).toHaveBeenCalledTimes(1); 67 | }); 68 | 69 | it('should call `onFlush` callbacks', () => { 70 | const scheduler = createScheduler(); 71 | 72 | const callbackA = vi.fn(); 73 | const callbackB = vi.fn(); 74 | 75 | const removeA = scheduler.onFlush(callbackA); 76 | const removeB = scheduler.onFlush(callbackB); 77 | 78 | scheduler.flushSync(); 79 | 80 | expect(callbackA).toHaveBeenCalledTimes(1); 81 | expect(callbackB).toHaveBeenCalledTimes(1); 82 | 83 | removeA(); 84 | removeB(); 85 | 86 | scheduler.flushSync(); 87 | 88 | expect(callbackA).toHaveBeenCalledTimes(1); 89 | expect(callbackB).toHaveBeenCalledTimes(1); 90 | }); 91 | -------------------------------------------------------------------------------- /tsconfig-build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "allowUnreachableCode": false, 5 | "alwaysStrict": true, 6 | "declaration": true, 7 | "declarationDir": "types", 8 | "declarationMap": false, 9 | "emitDeclarationOnly": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "importHelpers": true, 12 | "incremental": true, 13 | "lib": ["es2020"], 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "noImplicitAny": false, 17 | "noImplicitOverride": true, 18 | "noImplicitReturns": true, 19 | "noUnusedParameters": false, 20 | "preserveWatchOutput": true, 21 | "skipLibCheck": true, 22 | "sourceMap": false, 23 | "strict": true, 24 | "strictNullChecks": true, 25 | "target": "es2020", 26 | "tsBuildInfoFile": ".tsbuildinfo", 27 | "types": ["@types/node", "vitest/globals"] 28 | }, 29 | "include": ["src", "tests"], 30 | "exclude": ["dist", "node_modules"] 31 | } 32 | -------------------------------------------------------------------------------- /types.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", 3 | 4 | "projectFolder": ".", 5 | 6 | "mainEntryPointFilePath": "./types/index.d.ts", 7 | 8 | "dtsRollup": { 9 | "enabled": true, 10 | "publicTrimmedFilePath": "./index.d.ts" 11 | }, 12 | 13 | "apiReport": { 14 | "enabled": false 15 | }, 16 | 17 | "docModel": { 18 | "enabled": false 19 | }, 20 | 21 | "tsdocMetadata": { 22 | "enabled": false 23 | }, 24 | 25 | "messages": { 26 | "compilerMessageReporting": { 27 | "default": { 28 | "logLevel": "warning" 29 | } 30 | }, 31 | 32 | "extractorMessageReporting": { 33 | "default": { 34 | "logLevel": "warning", 35 | "addToApiReportFile": true 36 | }, 37 | "ae-missing-release-tag": { 38 | "logLevel": "none" 39 | } 40 | }, 41 | 42 | "tsdocMessageReporting": { 43 | "default": { 44 | "logLevel": "warning" 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { defineConfig } from 'vite'; 4 | 5 | export default defineConfig({ 6 | // https://vitest.dev/config 7 | test: { 8 | include: ['tests/**/*.test.ts'], 9 | globals: true, 10 | }, 11 | }); 12 | --------------------------------------------------------------------------------