├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── LICENSE ├── README.md ├── cjs ├── async.js ├── fn │ ├── async.js │ ├── index.js │ └── sync.js ├── index.js ├── package.json ├── solid │ ├── async.js │ ├── index.js │ └── sync.js └── sync.js ├── es.js ├── esm ├── async.js ├── fn │ ├── async.js │ ├── index.js │ └── sync.js ├── index.js ├── solid │ ├── async.js │ ├── index.js │ └── sync.js └── sync.js ├── package-lock.json ├── package.json ├── rollup └── es.config.js ├── test ├── .npmrc ├── async.js ├── benchmark.js ├── benchmark.old.png ├── benchmark.png ├── callstack.js ├── conditional.html ├── index.js ├── leak.js ├── package.json ├── preact.js ├── signal.js ├── solid-js-baseline.js ├── solid-to-usignal.js ├── solid.js ├── test.js ├── tsconfig.json ├── types.ts ├── usignal.js └── weak-effects.html ├── tsconfig.json └── types ├── async.d.ts ├── fn ├── async.d.ts ├── index.d.ts └── sync.d.ts ├── index.d.ts ├── solid ├── async.d.ts ├── index.d.ts └── sync.d.ts └── sync.d.ts /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: build 5 | 6 | on: [push, pull_request] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [22] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | cache: 'npm' 24 | - run: npm ci 25 | - run: npm run build --if-present 26 | - run: npm test 27 | - run: npm run coverage --if-present 28 | - name: Coveralls 29 | uses: coverallsapp/github-action@master 30 | with: 31 | github-token: ${{ secrets.GITHUB_TOKEN }} 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .nyc_output 3 | coverage/ 4 | node_modules/ 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .nyc_output 3 | .eslintrc.json 4 | .travis.yml 5 | .github 6 | coverage/ 7 | node_modules/ 8 | rollup/ 9 | test/ 10 | tsconfig.json 11 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=true 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2022, Andrea Giammarchi, @WebReflection 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 | PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # µsignal 2 | 3 | [![Downloads](https://img.shields.io/npm/dm/usignal.svg)](https://www.npmjs.com/package/usignal) [![Coverage Status](https://coveralls.io/repos/github/WebReflection/usignal/badge.svg?branch=main)](https://coveralls.io/github/WebReflection/usignal?branch=main) [![build status](https://github.com/WebReflection/usignal/actions/workflows/node.js.yml/badge.svg)](https://github.com/WebReflection/usignal/actions) 4 | 5 | **Social Media Photo by [Carlos Alberto Gómez Iñiguez](https://unsplash.com/@iniguez) on [Unsplash](https://unsplash.com/)** 6 | 7 | A blend of [@preact/signals-core](https://github.com/preactjs/signals) and [solid-js basic reactivity API](https://www.solidjs.com/docs/latest), with API and DX mostly identical to *@preact/signals-core* but extra goodness inspired by *solid-js*, 816 bytes minified with brotli. 8 | 9 | ```js 10 | import {signal, computed, effect, batch, Signal} from 'usignal'; 11 | // const {signal, computed, effect, batch, Signal} = require('usignal'); 12 | 13 | signal(0) instanceof Signal; // true 14 | computed(() => {}) instanceof Signal; // true 15 | 16 | effect( 17 | () => { console.log('fx') }, 18 | void 0, // optional value to pass along the callback as initial/prev value 19 | {async: true} // optionally make the effect async: false by default 20 | ); 21 | 22 | // try every example shown in 23 | // https://github.com/preactjs/signals 24 | // or see test/index.js file to see it in action 25 | ``` 26 | 27 | #### Exports 28 | 29 | This is a *dual module* so it works in either *CommonJS* or *ECMAScript* module systems. 30 | 31 | * `usignal/sync` exports with an enforced *sync* effect 32 | * `usignal/async` exports with an enforced *async* effect 33 | * `usignal` in *browsers* exports `usignal/async` and `usignal/sync` in *servers* or by *default* 34 | * `usignal/core` just exports the *effect* as callback that accepts an effect and an optionally asynchronous `true` flag, used by all other exports by default, but you decide if a specific effect should sync or async. 35 | * the [unpkg/usignal](https://unpkg.com/usignal) default export points at the pre minified [es.js](./es.js) file without any enforcement around *effect*, like `usignal/core`, so that all effects are *sync* by default but can be *async* passing `true` as second parameter 36 | 37 | Current exports are exactly these: 38 | 39 | ```js 40 | import { 41 | signal, 42 | computed, 43 | effect, 44 | batch, 45 | Signal 46 | } from 'usignal'; 47 | ``` 48 | 49 | The `Signal` export is useful only as brand check for either *computed* or *signal* references, but it cannot be used as constructor right away. 50 | 51 | 52 | #### Exports - Extra 53 | 54 | To allow developers to try and use different patterns there are a few variants of this module, still based on the very same core primitives: 55 | 56 | * `usignal/fn`, with its `*/sync` and `*/async` variants, where signals are callbacks so that `signal()` returns a its value, and `signal(value)` updates its value and return the new one, [inspired by S](https://github.com/adamhaile/S). Computeds do not update anything so `computed()` returns values. This is a variant around the `.value` accessor pattern I don't necessarily disike, specially when we'd like to *signal* that a signal is being observed: `effect(() => { mySignal(); })` 57 | * `usignal/solid`, with its `*/sync` and `*/async` variants, where the module exports [createEffect](https://www.solidjs.com/docs/latest#createeffect), [createMemo](https://www.solidjs.com/docs/latest#creatememo), and [createSignal](https://www.solidjs.com/docs/latest#createsignal), mimicking the behavior (and returned values) as [solid-js basic reactivity API](https://www.solidjs.com/docs/latest/api). This is handy to compare the two or drop-in usignal in solid-js already based code. 58 | 59 | --- 60 | 61 | ## Differently thought ... 62 | 63 | * the default comparison for equality is not based on `===` but on [Object.is](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). This might be a tiny, hopefully irrelevant, performance penalty, but I feel like guarding *NaN* cases in reactivity is a step forward to avoid infinite loops out of *NaN* poisoning some computation. *+0* and *-0* are less interesting cases to tackle, still these might be fundamental in some case, hence preserved in this moudle. 64 | 65 | * this library has lazy, non side-effecting, computed values, something [@preact/signals-core](https://github.com/preactjs/signals) recently introduced and [Solid 2.0 is planning to improve](https://twitter.com/RyanCarniato/status/1569815024964706304). 66 | 67 | * computed accepts an initial value otherwise passed as previous one by default, mimicking *solid-js* `useMemo(fn[, value[, options]])` signature. 68 | 69 | * effect passes along its initial value or the previoulsy returned one. If this is a function though, it runs it before re-executing, passing along its returned value, if any. 70 | 71 | * both `signal(value[, options])` and `computed(fn[, value[, options]])` accept an optionally *options* argument, currently implementing [equals](https://www.solidjs.com/docs/latest#options) as explained in *solid-js* documentation. 72 | 73 | * both *signal* and *computed* also have a `toJSON` and a `valueOf()` allowing to implicitly use their values, e.g. 74 | 75 | ```js 76 | const one = signal(1); 77 | const two = signal(2); 78 | const three = computed(() => one + two); 79 | 80 | three.value; // 3 indeed! 81 | ``` 82 | 83 | --- 84 | 85 | 86 | ## Benchmark 87 | 88 | The benchmark currently compares *S*, *solid*, *preact/signals*, and *cellx* against *usignal*. 89 | 90 | Please note *preact* is currently not able to solve nested effects so its logic might be simpler than other libraries. 91 | 92 | ```sh 93 | npm run benchmark 94 | ``` 95 | 96 | ![current status](./test/benchmark.png) 97 | 98 | 99 | ## Tests 100 | 101 | This module is 100% code covered, including ~~the *WeakRef*~~ possible leaks which is tested through the [test/leak.js](./test/leak.js) file, which is part of the *build* script process. 102 | 103 | To use other libraries as reference, I have also added *preact/signals-core* and *solid-js* dev-dependencies within the test folder. 104 | 105 | Please note *preact* is currently not able to solve nested effects so its logic might be simpler than other libraries. 106 | 107 | The following instructions are needed to test other libraries too: 108 | 109 | ```sh 110 | cd usignal 111 | npm i 112 | cd test 113 | npm i 114 | cd .. 115 | 116 | # normal tests 117 | npm test usignal # shows also code-coverage 118 | npm test solid 119 | npm test preact 120 | 121 | # leak test 122 | npm run leak usignal # calculate leaks via internals 123 | npm run leak solid 124 | npm run leak preact 125 | ``` 126 | 127 | #### About the leak test 128 | 129 | This file is not meant at all as meaningful benchmark against other libraries, it's simply there to allow me to spot regressions on future updates of the library: 130 | * ~~there should be zero leaks on signals when a computed reference is garbage collected~~ v0.5.0 removed the *WeakRef*, computeds go when signals go ... [but why?!](https://twitter.com/WebReflection/status/1570380914613694466) 131 | * the amount of used memory should always be lower than the initial one 132 | * the performance should be competitive compared to others 133 | 134 | ## How to integrate with Lit 135 | 136 | You create a following [mixin](https://lit.dev/docs/composition/mixins/) function. Your class inherits from Mixin. Please see the [demo](https://lit.dev/playground/#project=W3sibmFtZSI6InNpZ25hbC1leGFtcGxlLmpzIiwiY29udGVudCI6ImltcG9ydCB7IGh0bWwsIGNzcywgTGl0RWxlbWVudCB9IGZyb20gJ2xpdCc7XG5pbXBvcnQgeyBXaXRoVXNpZ25hbCB9IGZyb20gJy4vd2l0aC11c2lnbmFsLmpzJztcbmltcG9ydCB7IHNpZ25hbCB9IGZyb20gJ3VzaWduYWwnO1xuXG5jb25zdCBjb3VudGVyID0gc2lnbmFsKDEpO1xuY29uc3QgY291bnRlcjIgPSBzaWduYWwoMSk7XG5cbmNsYXNzIFNpZ25hbEV4YW1wbGUgZXh0ZW5kcyBXaXRoVXNpZ25hbChMaXRFbGVtZW50KSB7XG4gIHN0YXRpYyBzdHlsZXMgPSBjc3NgXG4gICAgOmhvc3Qge1xuICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICBib3JkZXI6IHNvbGlkIDFweCBibHVlO1xuICAgICAgcGFkZGluZzogOHB4O1xuICAgICAgbWFyZ2luOiA0cHg7XG4gICAgfVxuICBgO1xuXG4gIHN0YXRpYyBwcm9wZXJ0aWVzID0ge1xuICAgIGNvdW50OiB7IHN0YXRlOiB0cnVlIH0sXG4gICAgbmFtZToge31cbiAgfVxuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5jb3VudCA9IDE7XG4gIH1cblxuICByZW5kZXIoKSB7XG4gICAgcmV0dXJuIGh0bWxgXG4gICAgICA8aDQ-JHt0aGlzLm5hbWV9PC9oND5cbiAgICAgIDxwPlxuICAgICAgICBTaWduYWwgY291bnRlcjogJHtjb3VudGVyfVxuICAgICAgICA8YnV0dG9uIEBjbGljaz0keygpID0-IGNvdW50ZXIudmFsdWUrK30-Kys8L2J1dHRvbj5cbiAgICAgIDwvcD5cbiAgICAgIDxwPlxuICAgICAgICBSZWFjdGl2ZSBwcm9wZXJ0eSBjb3VudGVyOiAke3RoaXMuY291bnR9XG4gICAgICAgIDxidXR0b24gQGNsaWNrPSR7KCkgPT4gdGhpcy5jb3VudCsrfT4rKzwvYnV0dG9uPlxuICAgICAgPC9wPlxuICAgICAgJHsgdGhpcy5jb3VudCA-IDIgPyBodG1sYFxuICAgICAgIDxwPlxuICAgICAgICBTaWduYWwgY291bnRlcjogJHtjb3VudGVyMn1cbiAgICAgICAgPGJ1dHRvbiBAY2xpY2s9JHsoKSA9PiBjb3VudGVyMi52YWx1ZSsrfT4rKzwvYnV0dG9uPlxuICAgICAgIDwvcD5gIDogbnVsbH0gICAgICAgIFxuICAgIGA7XG4gIH1cbn1cblxuY3VzdG9tRWxlbWVudHMuZGVmaW5lKCdzaWduYWwtZXhhbXBsZScsIFNpZ25hbEV4YW1wbGUpOyJ9LHsibmFtZSI6ImluZGV4Lmh0bWwiLCJjb250ZW50IjoiPCFET0NUWVBFIGh0bWw-XG48aHRtbD5cbiAgPGhlYWQ-XG4gICAgPHNjcmlwdCB0eXBlPVwibW9kdWxlXCIgc3JjPVwiLi9zaWduYWwtZXhhbXBsZS5qc1wiPjwvc2NyaXB0PlxuICAgIDxzdHlsZT5cbiAgICAgIGJvZHkge1xuICAgICAgICBmb250LXNpemU6IDEuMjVyZW07XG4gICAgICAgIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAgICAgfVxuICAgIDwvc3R5bGU-XG4gIDwvaGVhZD5cbiAgPGJvZHk-XG4gICAgPHNpZ25hbC1leGFtcGxlIG5hbWU9XCJJbnN0YW5jZSAxXCI-PC9zaWduYWwtZXhhbXBsZT5cbiAgICA8c2lnbmFsLWV4YW1wbGUgbmFtZT1cIkluc3RhbmNlIDJcIj48L3NpZ25hbC1leGFtcGxlPlxuICA8L2JvZHk-XG48L2h0bWw-In0seyJuYW1lIjoicGFja2FnZS5qc29uIiwiY29udGVudCI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwibGl0XCI6IFwiXjIuMC4wXCIsXG4gICAgXCJAbGl0L3JlYWN0aXZlLWVsZW1lbnRcIjogXCJeMS4wLjBcIixcbiAgICBcImxpdC1lbGVtZW50XCI6IFwiXjMuMC4wXCIsXG4gICAgXCJsaXQtaHRtbFwiOiBcIl4yLjAuMFwiXG4gIH1cbn0iLCJoaWRkZW4iOnRydWV9LHsibmFtZSI6IndpdGgtdXNpZ25hbC5qcyIsImNvbnRlbnQiOiJpbXBvcnQgeyBlZmZlY3QsIHNpZ25hbCB9IGZyb20gJ3VzaWduYWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gV2l0aFVzaWduYWwoQmFzZSl7XG4gIHJldHVybiBjbGFzcyBXaXRoVXNpZ25hbCBleHRlbmRzIEJhc2Uge1xuICAgICNkaXNwb3NlRWZmZWN0XG4gICAgI3JlYWN0aXZlUHJvcFVwZGF0ZSA9IHNpZ25hbCgwKTtcblxuICAgIGRpc2Nvbm5lY3RlZENhbGxiYWNrKCkge1xuICAgICAgdGhpcy4jZGlzcG9zZUVmZmVjdD8uKCk7XG4gICAgfVxuXG4gICAgcGVyZm9ybVVwZGF0ZSgpIHtcbiAgICAgIGlmICghdGhpcy5pc1VwZGF0ZVBlbmRpbmcpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy4jZGlzcG9zZUVmZmVjdCkge1xuICAgICAgICB0aGlzLiNyZWFjdGl2ZVByb3BVcGRhdGUudmFsdWUrKztcbiAgICAgIH1cblxuICAgICAgdGhpcy4jZGlzcG9zZUVmZmVjdCA9IGVmZmVjdCgoKSA9PiB7ICAgICAgXG4gICAgICAgIHRoaXMuaXNVcGRhdGVQZW5kaW5nID0gdHJ1ZTtcbiAgICAgICAgdGhpcy4jcmVhY3RpdmVQcm9wVXBkYXRlLnZhbHVlO1xuICAgICAgICBzdXBlci5wZXJmb3JtVXBkYXRlKCk7XG4gICAgICB9KTtcbiAgICB9XG4gIH07XG59In1d) for details. 137 | 138 | ```js 139 | import { effect, signal } from 'usignal'; 140 | 141 | export function WithUsignal(Base){ 142 | return class WithUsignal extends Base { 143 | #disposeEffect 144 | #reactivePropUpdate = signal(0); 145 | 146 | disconnectedCallback() { 147 | this.#disposeEffect?.(); 148 | } 149 | 150 | performUpdate() { 151 | if (!this.isUpdatePending) { 152 | return; 153 | } 154 | 155 | if (this.#disposeEffect) { 156 | this.#reactivePropUpdate.value++; 157 | return; 158 | } 159 | 160 | this.#disposeEffect = effect(() => { 161 | this.isUpdatePending = true; 162 | this.#reactivePropUpdate.value; 163 | super.performUpdate(); 164 | }); 165 | } 166 | }; 167 | } 168 | ``` 169 | -------------------------------------------------------------------------------- /cjs/async.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | (m => Object.keys(m).map(k => k !== 'default' && (exports[k] = m[k]))) 3 | (require('./index.js')); 4 | const {effect: fx} = require('./index.js'); 5 | 6 | const options = {async: true}; 7 | 8 | /** 9 | * Invokes asynchronously a function when any of its internal signals or computed values change. 10 | * 11 | * Returns a dispose callback. 12 | * @template T 13 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 14 | */ 15 | const effect = (fn, value) => fx(fn, value, options); 16 | exports.effect = effect; 17 | -------------------------------------------------------------------------------- /cjs/fn/async.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | (m => Object.keys(m).map(k => k !== 'default' && (exports[k] = m[k]))) 3 | (require('./index.js')); 4 | const {effect: fx} = require('../index.js'); 5 | 6 | const options = {async: true}; 7 | 8 | /** 9 | * Invokes asynchronously a function when any of its internal signals or computed values change. 10 | * 11 | * Returns a dispose callback. 12 | * @template T 13 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 14 | */ 15 | const effect = (fn, value) => fx(fn, value, options); 16 | exports.effect = effect; 17 | -------------------------------------------------------------------------------- /cjs/fn/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | (m => Object.keys(m).map(k => k !== 'default' && (exports[k] = m[k]))) 3 | (require('../index.js')); 4 | const {computed: c, signal: s, Signal: S, Signal} = require('../index.js'); 5 | 6 | const {setPrototypeOf} = Object; 7 | const {prototype} = Signal; 8 | 9 | /** 10 | * Returns a callback that is invoked only when any of the internally 11 | * used signals, as in within the callback, is unknown or updated. 12 | * @template T 13 | * @type {(fn: (v: T) => T, value?: T, options?: { equals?: boolean | ((prev: T, next: T) => boolean) }) => () => T} 14 | */ 15 | const computed = (fn, value, options) => { 16 | const _ = c(fn, value, options); 17 | return setPrototypeOf( 18 | () => _.value, 19 | prototype 20 | ); 21 | }; 22 | exports.computed = computed; 23 | 24 | /** 25 | * Returns a callback that executed with no argument returns the signal value, 26 | * otherwise it updates the signal value and returns its new value. 27 | * @template T 28 | * @param {T} value the value the Signal should carry along 29 | * @param {{equals?: boolean | ((prev: T, next: T) => boolean)}} [options] signal options 30 | * @returns {(value?: T) => T} 31 | */ 32 | const signal = (value, options) => { 33 | const _ = s(value, options); 34 | return setPrototypeOf( 35 | function (value) { 36 | return arguments.length ? (_.value = value) : _.value; 37 | }, 38 | prototype 39 | ); 40 | }; 41 | exports.signal = signal; 42 | -------------------------------------------------------------------------------- /cjs/fn/sync.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | (m => Object.keys(m).map(k => k !== 'default' && (exports[k] = m[k]))) 3 | (require('./index.js')); 4 | const {effect: fx} = require('../index.js'); 5 | 6 | const options = {async: false}; 7 | 8 | /** 9 | * Invokes asynchronously a function when any of its internal signals or computed values change. 10 | * 11 | * Returns a dispose callback. 12 | * @template T 13 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 14 | */ 15 | const effect = (fn, value) => fx(fn, value, options); 16 | exports.effect = effect; 17 | -------------------------------------------------------------------------------- /cjs/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /*! (c) Andrea Giammarchi */ 3 | 4 | const {is} = Object; 5 | 6 | let batches; 7 | 8 | /** 9 | * Execute a callback that will not side-effect until its top-most batch is 10 | * completed. 11 | * @param {() => void} callback a function that batches changes to be notified 12 | * through signals. 13 | */ 14 | const batch = callback => { 15 | const prev = batches; 16 | batches = prev || []; 17 | try { 18 | callback(); 19 | if (!prev) 20 | for (const {value} of batches); 21 | } 22 | finally { batches = prev } 23 | }; 24 | exports.batch = batch; 25 | 26 | /** 27 | * A signal with a value property also exposed via toJSON, toString and valueOf. 28 | * When created via computed, the `value` property is **readonly**. 29 | * @template T 30 | */ 31 | class Signal { 32 | constructor(value) { 33 | this._ = value; 34 | } 35 | 36 | /** @returns {T} */ 37 | toJSON() { return this.value } 38 | 39 | /** @returns {string} */ 40 | toString() { return String(this.value) } 41 | 42 | /** @returns {T} */ 43 | valueOf() { return this.value } 44 | } 45 | exports.Signal = Signal 46 | 47 | let computedSignal; 48 | /** 49 | * @template T 50 | * @extends {Signal} 51 | */ 52 | class Computed extends Signal { 53 | /** 54 | * @private 55 | * @type{Reactive} 56 | */ 57 | s 58 | /** 59 | * @param {(v: T) => T} _ 60 | * @param {T} v 61 | * @param {{ equals?: Equals }} o 62 | * @param {boolean} f 63 | */ 64 | constructor(_, v, o, f) { 65 | super(_); 66 | this.f = f; // is effect? 67 | this.$ = true; // should update ("value for money") 68 | this.r = new Set; // related signals 69 | this.s = new Reactive(v, o); // signal 70 | } 71 | refresh() { 72 | if (!this.$) return 73 | 74 | const prev = computedSignal; 75 | computedSignal = this; 76 | try { this.s.value = this._(this.s._) } 77 | finally { 78 | this.$ = false; 79 | computedSignal = prev; 80 | } 81 | } 82 | peek() { 83 | this.refresh() 84 | return this.s.peek() 85 | } 86 | get value() { 87 | this.refresh() 88 | return this.s.value; 89 | } 90 | } 91 | 92 | const defaults = {async: false, equals: true}; 93 | 94 | /** 95 | * Returns a read-only Signal that is invoked only when any of the internally 96 | * used signals, as in within the callback, is unknown or updated. 97 | * @type {(fn: (v: T) => R, value?: V, options?: { equals?: Equals }) => ComputedSignal} 98 | */ 99 | const computed = (fn, value, options = defaults) => 100 | new Computed(fn, value, options, false); 101 | exports.computed = computed; 102 | 103 | let outerEffect; 104 | const empty = []; 105 | const noop = () => {}; 106 | const dispose = ({s}) => { 107 | if (typeof s._ === 'function') 108 | s._ = s._(); 109 | }; 110 | 111 | class FX extends Computed { 112 | constructor(_, v, o) { 113 | super(_, v, o, true); 114 | this.e = empty; 115 | } 116 | run() { 117 | this.$ = true; 118 | this.value; 119 | return this; 120 | } 121 | stop() { 122 | this._ = noop; 123 | this.r.clear(); 124 | this.s.c.clear(); 125 | } 126 | } 127 | exports.FX = FX 128 | 129 | class Effect extends FX { 130 | constructor(_, v, o) { 131 | super(_, v, o); 132 | this.i = 0; // index 133 | this.a = !!o.async; // async 134 | this.m = true; // microtask 135 | this.e = []; // effects 136 | // "I am effects" ^_^;; 137 | } 138 | get value() { 139 | this.a ? this.async() : this.sync(); 140 | } 141 | async() { 142 | if (this.m) { 143 | this.m = false; 144 | queueMicrotask(() => { 145 | this.m = true; 146 | this.sync(); 147 | }); 148 | } 149 | } 150 | sync() { 151 | const prev = outerEffect; 152 | (outerEffect = this).i = 0; 153 | dispose(this); 154 | super.value; 155 | outerEffect = prev; 156 | } 157 | stop() { 158 | super.stop(); 159 | dispose(this); 160 | for (const effect of this.e.splice(0)) 161 | effect.stop(); 162 | } 163 | } 164 | exports.Effect = Effect 165 | 166 | /** 167 | * Invokes a function when any of its internal signals or computed values change. 168 | * 169 | * Returns a dispose callback. 170 | * @template T 171 | * @type {(fn: (v: T) => T, value?: T, options?: { async?: boolean }) => () => void} 172 | */ 173 | const effect = (callback, value, options = defaults) => { 174 | let unique; 175 | if (outerEffect) { 176 | const {i, e} = outerEffect; 177 | const isNew = i === e.length; 178 | // bottleneck: 179 | // there's literally no way to optimize this path *unless* the callback is 180 | // already a known one. however, latter case is not really common code so 181 | // the question is: should I optimize this more than this? 'cause I don't 182 | // think the amount of code needed to understand if a callback is *likely* 183 | // the same as before makes any sense + correctness would be trashed. 184 | if (isNew || e[i]._ !== callback) { 185 | if (!isNew) e[i].stop(); 186 | e[i] = new Effect(callback, value, options).run(); 187 | } 188 | unique = e[i]; 189 | outerEffect.i++; 190 | } 191 | else 192 | unique = new Effect(callback, value, options).run(); 193 | return () => { unique.stop() }; 194 | }; 195 | exports.effect = effect; 196 | 197 | const skip = () => false; 198 | /** 199 | * @template T 200 | * @extends {Signal} 201 | */ 202 | class Reactive extends Signal { 203 | constructor(_, {equals}) { 204 | super(_) 205 | this.c = new Set; // computeds 206 | this.s = equals === true ? is : (equals || skip); // (don't) skip updates 207 | } 208 | /** 209 | * Allows to get signal.value without subscribing to updates in an effect 210 | * @returns {T} 211 | */ 212 | peek() { return this._ } 213 | /** @returns {T} */ 214 | get value() { 215 | if (computedSignal) { 216 | this.c.add(computedSignal); 217 | computedSignal.r.add(this); 218 | } 219 | return this._; 220 | } 221 | set value(_) { 222 | const prev = this._; 223 | if (!this.s((this._ = _), prev)) { 224 | if (this.c.size) { 225 | const effects = []; 226 | const stack = [this]; 227 | for (const signal of stack) { 228 | for (const computed of signal.c) { 229 | if (!computed.$ && computed.r.has(signal)) { 230 | computed.r.clear(); 231 | computed.$ = true; 232 | if (computed.f) { 233 | effects.push(computed); 234 | const stack = [computed]; 235 | for (const c of stack) { 236 | for (const effect of c.e) { 237 | effect.r.clear(); 238 | effect.$ = true; 239 | stack.push(effect); 240 | } 241 | } 242 | } 243 | else 244 | stack.push(computed.s); 245 | } 246 | } 247 | } 248 | for (const effect of effects) 249 | batches ? batches.push(effect) : effect.value; 250 | } 251 | } 252 | } 253 | } 254 | 255 | /** 256 | * Returns a writable Signal that side-effects whenever its value gets updated. 257 | * @template T 258 | * @type {(initialValue: T, options?: { equals?: Equals }) => ReactiveSignal} 259 | */ 260 | const signal = (value, options = defaults) => new Reactive(value, options); 261 | exports.signal = signal; 262 | 263 | /** 264 | * @template [T=any] 265 | * @typedef {boolean | ((prev: T, next: T) => boolean)} Equals 266 | */ 267 | 268 | /** 269 | * @public 270 | * @template T 271 | * @typedef {Omit, '_'|'s'|'c'>} ReactiveSignal 272 | */ 273 | 274 | /** 275 | * @public 276 | * @template T 277 | * @typedef {Omit, '$'|'s'|'f'|'r'|'_'>} ComputedSignal 278 | */ 279 | -------------------------------------------------------------------------------- /cjs/package.json: -------------------------------------------------------------------------------- 1 | {"type":"commonjs"} -------------------------------------------------------------------------------- /cjs/solid/async.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | (m => Object.keys(m).map(k => k !== 'default' && (exports[k] = m[k]))) 3 | (require('./index.js')); 4 | const {createEffect: fx} = require('./index.js'); 5 | 6 | const options = {async: true}; 7 | 8 | /** 9 | * asynchronous https://www.solidjs.com/docs/latest/api#createeffect 10 | * 11 | * returns a dispose callback. 12 | * @template T 13 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 14 | */ 15 | const createEffect = (fn, initialValue) => fx(fn, initialValue, options); 16 | exports.createEffect = createEffect; -------------------------------------------------------------------------------- /cjs/solid/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {computed, effect, signal} = require('../index.js'); 3 | 4 | const asValue = value => typeof value === 'function' ? value() : value; 5 | 6 | /** 7 | * https://www.solidjs.com/docs/latest/api#createeffect 8 | * 9 | * returns a dispose callback. 10 | * @template T 11 | * @type {(fn: (v: T) => T, value?: T, options?: { async?: boolean }) => () => void} 12 | */ 13 | const createEffect = effect; 14 | exports.createEffect = createEffect; 15 | 16 | /** 17 | * @template T 18 | * @type {(fn: (v: T) => T, value?: T, options?: { equals?: boolean | ((prev: T, next: T) => boolean) }) => () => T} 19 | */ 20 | const createMemo = (fn, value, options) => { 21 | const _ = computed(fn, value, options); 22 | return () => _.value; 23 | }; 24 | exports.createMemo = createMemo; 25 | 26 | /** 27 | * @template T 28 | * @type {(initialValue: T, options?: { equals?: boolean | ((prev: T, next: T) => boolean) }) => [get: () => T, set: (v: T) => T]} 29 | */ 30 | const createSignal = (initialValue, options) => { 31 | const _ = signal(asValue(initialValue), options); 32 | return [ 33 | () => _.value, 34 | value => { 35 | _.value = asValue(value); 36 | } 37 | ]; 38 | }; 39 | exports.createSignal = createSignal; 40 | -------------------------------------------------------------------------------- /cjs/solid/sync.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | (m => Object.keys(m).map(k => k !== 'default' && (exports[k] = m[k]))) 3 | (require('./index.js')); 4 | const {createEffect: fx} = require('./index.js'); 5 | 6 | const options = {async: false}; 7 | 8 | /** 9 | * synchronous https://www.solidjs.com/docs/latest/api#createeffect 10 | * 11 | * returns a dispose callback. 12 | * @template T 13 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 14 | */ 15 | const createEffect = (fn, initialValue) => fx(fn, initialValue, options); 16 | exports.createEffect = createEffect; -------------------------------------------------------------------------------- /cjs/sync.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | (m => Object.keys(m).map(k => k !== 'default' && (exports[k] = m[k]))) 3 | (require('./index.js')); 4 | const {effect: fx} = require('./index.js'); 5 | 6 | const options = {async: false}; 7 | 8 | /** 9 | * Invokes synchronously a function when any of its internal signals or computed values change. 10 | * 11 | * Returns a dispose callback. 12 | * @template T 13 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 14 | */ 15 | const effect = (fn, value) => fx(fn, value, options); 16 | exports.effect = effect; 17 | -------------------------------------------------------------------------------- /es.js: -------------------------------------------------------------------------------- 1 | /*! (c) Andrea Giammarchi */ 2 | const{is:s}=Object;let t;const e=s=>{const e=t;t=e||[];try{if(s(),!e)for(const{value:s}of t);}finally{t=e}};class i{constructor(s){this._=s}toJSON(){return this.value}toString(){return String(this.value)}valueOf(){return this.value}}let r;class n extends i{s;constructor(s,t,e,i){super(s),this.f=i,this.$=!0,this.r=new Set,this.s=new _(t,e)}refresh(){if(!this.$)return;const s=r;r=this;try{this.s.value=this._(this.s._)}finally{this.$=!1,r=s}}peek(){return this.refresh(),this.s.peek()}get value(){return this.refresh(),this.s.value}}const h={async:!1,equals:!0},o=(s,t,e=h)=>new n(s,t,e,!1);let c;const u=[],a=()=>{},l=({s:s})=>{"function"==typeof s._&&(s._=s._())};class f extends n{constructor(s,t,e){super(s,t,e,!0),this.e=u}run(){return this.$=!0,this.value,this}stop(){this._=a,this.r.clear(),this.s.c.clear()}}class p extends f{constructor(s,t,e){super(s,t,e),this.i=0,this.a=!!e.async,this.m=!0,this.e=[]}get value(){this.a?this.async():this.sync()}async(){this.m&&(this.m=!1,queueMicrotask((()=>{this.m=!0,this.sync()})))}sync(){const s=c;(c=this).i=0,l(this),super.value,c=s}stop(){super.stop(),l(this);for(const s of this.e.splice(0))s.stop()}}const v=(s,t,e=h)=>{let i;if(c){const{i:r,e:n}=c,h=r===n.length;(h||n[r]._!==s)&&(h||n[r].stop(),n[r]=new p(s,t,e).run()),i=n[r],c.i++}else i=new p(s,t,e).run();return()=>{i.stop()}},y=()=>!1;class _ extends i{constructor(t,{equals:e}){super(t),this.c=new Set,this.s=!0===e?s:e||y}peek(){return this._}get value(){return r&&(this.c.add(r),r.r.add(this)),this._}set value(s){const e=this._;if(!this.s(this._=s,e)&&this.c.size){const s=[],e=[this];for(const t of e)for(const i of t.c)if(!i.$&&i.r.has(t))if(i.r.clear(),i.$=!0,i.f){s.push(i);const t=[i];for(const s of t)for(const e of s.e)e.r.clear(),e.$=!0,t.push(e)}else e.push(i.s);for(const e of s)t?t.push(e):e.value}}}const d=(s,t=h)=>new _(s,t);export{p as Effect,f as FX,i as Signal,e as batch,o as computed,v as effect,d as signal}; 3 | -------------------------------------------------------------------------------- /esm/async.js: -------------------------------------------------------------------------------- 1 | export * from './index.js'; 2 | import {effect as fx} from './index.js'; 3 | 4 | const options = {async: true}; 5 | 6 | /** 7 | * Invokes asynchronously a function when any of its internal signals or computed values change. 8 | * 9 | * Returns a dispose callback. 10 | * @template T 11 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 12 | */ 13 | export const effect = (fn, value) => fx(fn, value, options); 14 | -------------------------------------------------------------------------------- /esm/fn/async.js: -------------------------------------------------------------------------------- 1 | export * from './index.js'; 2 | import {effect as fx} from '../index.js'; 3 | 4 | const options = {async: true}; 5 | 6 | /** 7 | * Invokes asynchronously a function when any of its internal signals or computed values change. 8 | * 9 | * Returns a dispose callback. 10 | * @template T 11 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 12 | */ 13 | export const effect = (fn, value) => fx(fn, value, options); 14 | -------------------------------------------------------------------------------- /esm/fn/index.js: -------------------------------------------------------------------------------- 1 | export * from '../index.js'; 2 | import {computed as c, signal as s, Signal as S, Signal} from '../index.js'; 3 | 4 | const {setPrototypeOf} = Object; 5 | const {prototype} = Signal; 6 | 7 | /** 8 | * Returns a callback that is invoked only when any of the internally 9 | * used signals, as in within the callback, is unknown or updated. 10 | * @template T 11 | * @type {(fn: (v: T) => T, value?: T, options?: { equals?: boolean | ((prev: T, next: T) => boolean) }) => () => T} 12 | */ 13 | export const computed = (fn, value, options) => { 14 | const _ = c(fn, value, options); 15 | return setPrototypeOf( 16 | () => _.value, 17 | prototype 18 | ); 19 | }; 20 | 21 | /** 22 | * Returns a callback that executed with no argument returns the signal value, 23 | * otherwise it updates the signal value and returns its new value. 24 | * @template T 25 | * @param {T} value the value the Signal should carry along 26 | * @param {{equals?: boolean | ((prev: T, next: T) => boolean)}} [options] signal options 27 | * @returns {(value?: T) => T} 28 | */ 29 | export const signal = (value, options) => { 30 | const _ = s(value, options); 31 | return setPrototypeOf( 32 | function (value) { 33 | return arguments.length ? (_.value = value) : _.value; 34 | }, 35 | prototype 36 | ); 37 | }; 38 | -------------------------------------------------------------------------------- /esm/fn/sync.js: -------------------------------------------------------------------------------- 1 | export * from './index.js'; 2 | import {effect as fx} from '../index.js'; 3 | 4 | const options = {async: false}; 5 | 6 | /** 7 | * Invokes asynchronously a function when any of its internal signals or computed values change. 8 | * 9 | * Returns a dispose callback. 10 | * @template T 11 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 12 | */ 13 | export const effect = (fn, value) => fx(fn, value, options); 14 | -------------------------------------------------------------------------------- /esm/index.js: -------------------------------------------------------------------------------- 1 | /*! (c) Andrea Giammarchi */ 2 | 3 | const {is} = Object; 4 | 5 | let batches; 6 | 7 | /** 8 | * Execute a callback that will not side-effect until its top-most batch is 9 | * completed. 10 | * @param {() => void} callback a function that batches changes to be notified 11 | * through signals. 12 | */ 13 | export const batch = callback => { 14 | const prev = batches; 15 | batches = prev || []; 16 | try { 17 | callback(); 18 | if (!prev) 19 | for (const {value} of batches); 20 | } 21 | finally { batches = prev } 22 | }; 23 | 24 | /** 25 | * A signal with a value property also exposed via toJSON, toString and valueOf. 26 | * When created via computed, the `value` property is **readonly**. 27 | * @template T 28 | */ 29 | export class Signal { 30 | constructor(value) { 31 | this._ = value; 32 | } 33 | 34 | /** @returns {T} */ 35 | toJSON() { return this.value } 36 | 37 | /** @returns {string} */ 38 | toString() { return String(this.value) } 39 | 40 | /** @returns {T} */ 41 | valueOf() { return this.value } 42 | } 43 | 44 | let computedSignal; 45 | /** 46 | * @template T 47 | * @extends {Signal} 48 | */ 49 | class Computed extends Signal { 50 | /** 51 | * @private 52 | * @type{Reactive} 53 | */ 54 | s 55 | /** 56 | * @param {(v: T) => T} _ 57 | * @param {T} v 58 | * @param {{ equals?: Equals }} o 59 | * @param {boolean} f 60 | */ 61 | constructor(_, v, o, f) { 62 | super(_); 63 | this.f = f; // is effect? 64 | this.$ = true; // should update ("value for money") 65 | this.r = new Set; // related signals 66 | this.s = new Reactive(v, o); // signal 67 | } 68 | refresh() { 69 | if (!this.$) return 70 | 71 | const prev = computedSignal; 72 | computedSignal = this; 73 | try { this.s.value = this._(this.s._) } 74 | finally { 75 | this.$ = false; 76 | computedSignal = prev; 77 | } 78 | } 79 | peek() { 80 | this.refresh() 81 | return this.s.peek() 82 | } 83 | get value() { 84 | this.refresh() 85 | return this.s.value; 86 | } 87 | } 88 | 89 | const defaults = {async: false, equals: true}; 90 | 91 | /** 92 | * Returns a read-only Signal that is invoked only when any of the internally 93 | * used signals, as in within the callback, is unknown or updated. 94 | * @type {(fn: (v: T) => R, value?: V, options?: { equals?: Equals }) => ComputedSignal} 95 | */ 96 | export const computed = (fn, value, options = defaults) => 97 | new Computed(fn, value, options, false); 98 | 99 | let outerEffect; 100 | const empty = []; 101 | const noop = () => {}; 102 | const dispose = ({s}) => { 103 | if (typeof s._ === 'function') 104 | s._ = s._(); 105 | }; 106 | 107 | export class FX extends Computed { 108 | constructor(_, v, o) { 109 | super(_, v, o, true); 110 | this.e = empty; 111 | } 112 | run() { 113 | this.$ = true; 114 | this.value; 115 | return this; 116 | } 117 | stop() { 118 | this._ = noop; 119 | this.r.clear(); 120 | this.s.c.clear(); 121 | } 122 | } 123 | 124 | export class Effect extends FX { 125 | constructor(_, v, o) { 126 | super(_, v, o); 127 | this.i = 0; // index 128 | this.a = !!o.async; // async 129 | this.m = true; // microtask 130 | this.e = []; // effects 131 | // "I am effects" ^_^;; 132 | } 133 | get value() { 134 | this.a ? this.async() : this.sync(); 135 | } 136 | async() { 137 | if (this.m) { 138 | this.m = false; 139 | queueMicrotask(() => { 140 | this.m = true; 141 | this.sync(); 142 | }); 143 | } 144 | } 145 | sync() { 146 | const prev = outerEffect; 147 | (outerEffect = this).i = 0; 148 | dispose(this); 149 | super.value; 150 | outerEffect = prev; 151 | } 152 | stop() { 153 | super.stop(); 154 | dispose(this); 155 | for (const effect of this.e.splice(0)) 156 | effect.stop(); 157 | } 158 | } 159 | 160 | /** 161 | * Invokes a function when any of its internal signals or computed values change. 162 | * 163 | * Returns a dispose callback. 164 | * @template T 165 | * @type {(fn: (v: T) => T, value?: T, options?: { async?: boolean }) => () => void} 166 | */ 167 | export const effect = (callback, value, options = defaults) => { 168 | let unique; 169 | if (outerEffect) { 170 | const {i, e} = outerEffect; 171 | const isNew = i === e.length; 172 | // bottleneck: 173 | // there's literally no way to optimize this path *unless* the callback is 174 | // already a known one. however, latter case is not really common code so 175 | // the question is: should I optimize this more than this? 'cause I don't 176 | // think the amount of code needed to understand if a callback is *likely* 177 | // the same as before makes any sense + correctness would be trashed. 178 | if (isNew || e[i]._ !== callback) { 179 | if (!isNew) e[i].stop(); 180 | e[i] = new Effect(callback, value, options).run(); 181 | } 182 | unique = e[i]; 183 | outerEffect.i++; 184 | } 185 | else 186 | unique = new Effect(callback, value, options).run(); 187 | return () => { unique.stop() }; 188 | }; 189 | 190 | const skip = () => false; 191 | /** 192 | * @template T 193 | * @extends {Signal} 194 | */ 195 | class Reactive extends Signal { 196 | constructor(_, {equals}) { 197 | super(_) 198 | this.c = new Set; // computeds 199 | this.s = equals === true ? is : (equals || skip); // (don't) skip updates 200 | } 201 | /** 202 | * Allows to get signal.value without subscribing to updates in an effect 203 | * @returns {T} 204 | */ 205 | peek() { return this._ } 206 | /** @returns {T} */ 207 | get value() { 208 | if (computedSignal) { 209 | this.c.add(computedSignal); 210 | computedSignal.r.add(this); 211 | } 212 | return this._; 213 | } 214 | set value(_) { 215 | const prev = this._; 216 | if (!this.s((this._ = _), prev)) { 217 | if (this.c.size) { 218 | const effects = []; 219 | const stack = [this]; 220 | for (const signal of stack) { 221 | for (const computed of signal.c) { 222 | if (!computed.$ && computed.r.has(signal)) { 223 | computed.r.clear(); 224 | computed.$ = true; 225 | if (computed.f) { 226 | effects.push(computed); 227 | const stack = [computed]; 228 | for (const c of stack) { 229 | for (const effect of c.e) { 230 | effect.r.clear(); 231 | effect.$ = true; 232 | stack.push(effect); 233 | } 234 | } 235 | } 236 | else 237 | stack.push(computed.s); 238 | } 239 | } 240 | } 241 | for (const effect of effects) 242 | batches ? batches.push(effect) : effect.value; 243 | } 244 | } 245 | } 246 | } 247 | 248 | /** 249 | * Returns a writable Signal that side-effects whenever its value gets updated. 250 | * @template T 251 | * @type {(initialValue: T, options?: { equals?: Equals }) => ReactiveSignal} 252 | */ 253 | export const signal = (value, options = defaults) => new Reactive(value, options); 254 | 255 | /** 256 | * @template [T=any] 257 | * @typedef {boolean | ((prev: T, next: T) => boolean)} Equals 258 | */ 259 | 260 | /** 261 | * @public 262 | * @template T 263 | * @typedef {Omit, '_'|'s'|'c'>} ReactiveSignal 264 | */ 265 | 266 | /** 267 | * @public 268 | * @template T 269 | * @typedef {Omit, '$'|'s'|'f'|'r'|'_'>} ComputedSignal 270 | */ 271 | -------------------------------------------------------------------------------- /esm/solid/async.js: -------------------------------------------------------------------------------- 1 | export * from './index.js'; 2 | import {createEffect as fx} from './index.js'; 3 | 4 | const options = {async: true}; 5 | 6 | /** 7 | * asynchronous https://www.solidjs.com/docs/latest/api#createeffect 8 | * 9 | * returns a dispose callback. 10 | * @template T 11 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 12 | */ 13 | export const createEffect = (fn, initialValue) => fx(fn, initialValue, options); -------------------------------------------------------------------------------- /esm/solid/index.js: -------------------------------------------------------------------------------- 1 | import {computed, effect, signal} from '../index.js'; 2 | 3 | const asValue = value => typeof value === 'function' ? value() : value; 4 | 5 | /** 6 | * https://www.solidjs.com/docs/latest/api#createeffect 7 | * 8 | * returns a dispose callback. 9 | * @template T 10 | * @type {(fn: (v: T) => T, value?: T, options?: { async?: boolean }) => () => void} 11 | */ 12 | export const createEffect = effect; 13 | 14 | /** 15 | * @template T 16 | * @type {(fn: (v: T) => T, value?: T, options?: { equals?: boolean | ((prev: T, next: T) => boolean) }) => () => T} 17 | */ 18 | export const createMemo = (fn, value, options) => { 19 | const _ = computed(fn, value, options); 20 | return () => _.value; 21 | }; 22 | 23 | /** 24 | * @template T 25 | * @type {(initialValue: T, options?: { equals?: boolean | ((prev: T, next: T) => boolean) }) => [get: () => T, set: (v: T) => T]} 26 | */ 27 | export const createSignal = (initialValue, options) => { 28 | const _ = signal(asValue(initialValue), options); 29 | return [ 30 | () => _.value, 31 | value => { 32 | _.value = asValue(value); 33 | } 34 | ]; 35 | }; 36 | -------------------------------------------------------------------------------- /esm/solid/sync.js: -------------------------------------------------------------------------------- 1 | export * from './index.js'; 2 | import {createEffect as fx} from './index.js'; 3 | 4 | const options = {async: false}; 5 | 6 | /** 7 | * synchronous https://www.solidjs.com/docs/latest/api#createeffect 8 | * 9 | * returns a dispose callback. 10 | * @template T 11 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 12 | */ 13 | export const createEffect = (fn, initialValue) => fx(fn, initialValue, options); -------------------------------------------------------------------------------- /esm/sync.js: -------------------------------------------------------------------------------- 1 | export * from './index.js'; 2 | import {effect as fx} from './index.js'; 3 | 4 | const options = {async: false}; 5 | 6 | /** 7 | * Invokes synchronously a function when any of its internal signals or computed values change. 8 | * 9 | * Returns a dispose callback. 10 | * @template T 11 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 12 | */ 13 | export const effect = (fn, value) => fx(fn, value, options); 14 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "usignal", 3 | "version": "0.10.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "usignal", 9 | "version": "0.10.0", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "@rollup/plugin-node-resolve": "^15.0.1", 13 | "@rollup/plugin-terser": "^0.4.0", 14 | "ascjs": "^5.0.1", 15 | "c8": "^7.12.0", 16 | "expect-type": "^0.15.0", 17 | "rollup": "^3.10.1", 18 | "typescript": "^4.9.4" 19 | } 20 | }, 21 | "node_modules/@babel/parser": { 22 | "version": "7.20.13", 23 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz", 24 | "integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==", 25 | "dev": true, 26 | "bin": { 27 | "parser": "bin/babel-parser.js" 28 | }, 29 | "engines": { 30 | "node": ">=6.0.0" 31 | } 32 | }, 33 | "node_modules/@bcoe/v8-coverage": { 34 | "version": "0.2.3", 35 | "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", 36 | "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", 37 | "dev": true 38 | }, 39 | "node_modules/@istanbuljs/schema": { 40 | "version": "0.1.3", 41 | "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", 42 | "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", 43 | "dev": true, 44 | "engines": { 45 | "node": ">=8" 46 | } 47 | }, 48 | "node_modules/@jridgewell/gen-mapping": { 49 | "version": "0.3.2", 50 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", 51 | "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", 52 | "dev": true, 53 | "dependencies": { 54 | "@jridgewell/set-array": "^1.0.1", 55 | "@jridgewell/sourcemap-codec": "^1.4.10", 56 | "@jridgewell/trace-mapping": "^0.3.9" 57 | }, 58 | "engines": { 59 | "node": ">=6.0.0" 60 | } 61 | }, 62 | "node_modules/@jridgewell/resolve-uri": { 63 | "version": "3.1.0", 64 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 65 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 66 | "dev": true, 67 | "engines": { 68 | "node": ">=6.0.0" 69 | } 70 | }, 71 | "node_modules/@jridgewell/set-array": { 72 | "version": "1.1.2", 73 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", 74 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", 75 | "dev": true, 76 | "engines": { 77 | "node": ">=6.0.0" 78 | } 79 | }, 80 | "node_modules/@jridgewell/source-map": { 81 | "version": "0.3.2", 82 | "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", 83 | "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", 84 | "dev": true, 85 | "dependencies": { 86 | "@jridgewell/gen-mapping": "^0.3.0", 87 | "@jridgewell/trace-mapping": "^0.3.9" 88 | } 89 | }, 90 | "node_modules/@jridgewell/sourcemap-codec": { 91 | "version": "1.4.14", 92 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 93 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 94 | "dev": true 95 | }, 96 | "node_modules/@jridgewell/trace-mapping": { 97 | "version": "0.3.17", 98 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", 99 | "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", 100 | "dev": true, 101 | "dependencies": { 102 | "@jridgewell/resolve-uri": "3.1.0", 103 | "@jridgewell/sourcemap-codec": "1.4.14" 104 | } 105 | }, 106 | "node_modules/@rollup/plugin-node-resolve": { 107 | "version": "15.0.1", 108 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz", 109 | "integrity": "sha512-ReY88T7JhJjeRVbfCyNj+NXAG3IIsVMsX9b5/9jC98dRP8/yxlZdz7mHZbHk5zHr24wZZICS5AcXsFZAXYUQEg==", 110 | "dev": true, 111 | "dependencies": { 112 | "@rollup/pluginutils": "^5.0.1", 113 | "@types/resolve": "1.20.2", 114 | "deepmerge": "^4.2.2", 115 | "is-builtin-module": "^3.2.0", 116 | "is-module": "^1.0.0", 117 | "resolve": "^1.22.1" 118 | }, 119 | "engines": { 120 | "node": ">=14.0.0" 121 | }, 122 | "peerDependencies": { 123 | "rollup": "^2.78.0||^3.0.0" 124 | }, 125 | "peerDependenciesMeta": { 126 | "rollup": { 127 | "optional": true 128 | } 129 | } 130 | }, 131 | "node_modules/@rollup/plugin-terser": { 132 | "version": "0.4.0", 133 | "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.0.tgz", 134 | "integrity": "sha512-Ipcf3LPNerey1q9ZMjiaWHlNPEHNU/B5/uh9zXLltfEQ1lVSLLeZSgAtTPWGyw8Ip1guOeq+mDtdOlEj/wNxQw==", 135 | "dev": true, 136 | "dependencies": { 137 | "serialize-javascript": "^6.0.0", 138 | "smob": "^0.0.6", 139 | "terser": "^5.15.1" 140 | }, 141 | "engines": { 142 | "node": ">=14.0.0" 143 | }, 144 | "peerDependencies": { 145 | "rollup": "^2.x || ^3.x" 146 | }, 147 | "peerDependenciesMeta": { 148 | "rollup": { 149 | "optional": true 150 | } 151 | } 152 | }, 153 | "node_modules/@rollup/pluginutils": { 154 | "version": "5.0.2", 155 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", 156 | "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", 157 | "dev": true, 158 | "dependencies": { 159 | "@types/estree": "^1.0.0", 160 | "estree-walker": "^2.0.2", 161 | "picomatch": "^2.3.1" 162 | }, 163 | "engines": { 164 | "node": ">=14.0.0" 165 | }, 166 | "peerDependencies": { 167 | "rollup": "^1.20.0||^2.0.0||^3.0.0" 168 | }, 169 | "peerDependenciesMeta": { 170 | "rollup": { 171 | "optional": true 172 | } 173 | } 174 | }, 175 | "node_modules/@types/estree": { 176 | "version": "1.0.0", 177 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", 178 | "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", 179 | "dev": true 180 | }, 181 | "node_modules/@types/istanbul-lib-coverage": { 182 | "version": "2.0.4", 183 | "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", 184 | "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", 185 | "dev": true 186 | }, 187 | "node_modules/@types/resolve": { 188 | "version": "1.20.2", 189 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", 190 | "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", 191 | "dev": true 192 | }, 193 | "node_modules/acorn": { 194 | "version": "8.8.1", 195 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", 196 | "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", 197 | "dev": true, 198 | "bin": { 199 | "acorn": "bin/acorn" 200 | }, 201 | "engines": { 202 | "node": ">=0.4.0" 203 | } 204 | }, 205 | "node_modules/ansi-regex": { 206 | "version": "5.0.1", 207 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 208 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 209 | "dev": true, 210 | "engines": { 211 | "node": ">=8" 212 | } 213 | }, 214 | "node_modules/ansi-styles": { 215 | "version": "4.3.0", 216 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 217 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 218 | "dev": true, 219 | "dependencies": { 220 | "color-convert": "^2.0.1" 221 | }, 222 | "engines": { 223 | "node": ">=8" 224 | }, 225 | "funding": { 226 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 227 | } 228 | }, 229 | "node_modules/ascjs": { 230 | "version": "5.0.1", 231 | "resolved": "https://registry.npmjs.org/ascjs/-/ascjs-5.0.1.tgz", 232 | "integrity": "sha512-1d/QdICzpywXiP53/Zz3fMdaC0/BB1ybLf+fK+QrqY8iyXNnWUHUrpmrowueXeswo+O+meJWm43TJSg2ClP3Sg==", 233 | "dev": true, 234 | "dependencies": { 235 | "@babel/parser": "^7.12.5" 236 | }, 237 | "bin": { 238 | "ascjs": "bin.js" 239 | } 240 | }, 241 | "node_modules/balanced-match": { 242 | "version": "1.0.2", 243 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 244 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 245 | "dev": true 246 | }, 247 | "node_modules/brace-expansion": { 248 | "version": "1.1.11", 249 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 250 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 251 | "dev": true, 252 | "dependencies": { 253 | "balanced-match": "^1.0.0", 254 | "concat-map": "0.0.1" 255 | } 256 | }, 257 | "node_modules/buffer-from": { 258 | "version": "1.1.2", 259 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 260 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 261 | "dev": true 262 | }, 263 | "node_modules/builtin-modules": { 264 | "version": "3.3.0", 265 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", 266 | "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", 267 | "dev": true, 268 | "engines": { 269 | "node": ">=6" 270 | }, 271 | "funding": { 272 | "url": "https://github.com/sponsors/sindresorhus" 273 | } 274 | }, 275 | "node_modules/c8": { 276 | "version": "7.12.0", 277 | "resolved": "https://registry.npmjs.org/c8/-/c8-7.12.0.tgz", 278 | "integrity": "sha512-CtgQrHOkyxr5koX1wEUmN/5cfDa2ckbHRA4Gy5LAL0zaCFtVWJS5++n+w4/sr2GWGerBxgTjpKeDclk/Qk6W/A==", 279 | "dev": true, 280 | "dependencies": { 281 | "@bcoe/v8-coverage": "^0.2.3", 282 | "@istanbuljs/schema": "^0.1.3", 283 | "find-up": "^5.0.0", 284 | "foreground-child": "^2.0.0", 285 | "istanbul-lib-coverage": "^3.2.0", 286 | "istanbul-lib-report": "^3.0.0", 287 | "istanbul-reports": "^3.1.4", 288 | "rimraf": "^3.0.2", 289 | "test-exclude": "^6.0.0", 290 | "v8-to-istanbul": "^9.0.0", 291 | "yargs": "^16.2.0", 292 | "yargs-parser": "^20.2.9" 293 | }, 294 | "bin": { 295 | "c8": "bin/c8.js" 296 | }, 297 | "engines": { 298 | "node": ">=10.12.0" 299 | } 300 | }, 301 | "node_modules/cliui": { 302 | "version": "7.0.4", 303 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 304 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 305 | "dev": true, 306 | "dependencies": { 307 | "string-width": "^4.2.0", 308 | "strip-ansi": "^6.0.0", 309 | "wrap-ansi": "^7.0.0" 310 | } 311 | }, 312 | "node_modules/color-convert": { 313 | "version": "2.0.1", 314 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 315 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 316 | "dev": true, 317 | "dependencies": { 318 | "color-name": "~1.1.4" 319 | }, 320 | "engines": { 321 | "node": ">=7.0.0" 322 | } 323 | }, 324 | "node_modules/color-name": { 325 | "version": "1.1.4", 326 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 327 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 328 | "dev": true 329 | }, 330 | "node_modules/commander": { 331 | "version": "2.20.3", 332 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 333 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 334 | "dev": true 335 | }, 336 | "node_modules/concat-map": { 337 | "version": "0.0.1", 338 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 339 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 340 | "dev": true 341 | }, 342 | "node_modules/convert-source-map": { 343 | "version": "1.9.0", 344 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", 345 | "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", 346 | "dev": true 347 | }, 348 | "node_modules/cross-spawn": { 349 | "version": "7.0.3", 350 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 351 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 352 | "dev": true, 353 | "dependencies": { 354 | "path-key": "^3.1.0", 355 | "shebang-command": "^2.0.0", 356 | "which": "^2.0.1" 357 | }, 358 | "engines": { 359 | "node": ">= 8" 360 | } 361 | }, 362 | "node_modules/deepmerge": { 363 | "version": "4.2.2", 364 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", 365 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", 366 | "dev": true, 367 | "engines": { 368 | "node": ">=0.10.0" 369 | } 370 | }, 371 | "node_modules/emoji-regex": { 372 | "version": "8.0.0", 373 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 374 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 375 | "dev": true 376 | }, 377 | "node_modules/escalade": { 378 | "version": "3.1.1", 379 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 380 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 381 | "dev": true, 382 | "engines": { 383 | "node": ">=6" 384 | } 385 | }, 386 | "node_modules/estree-walker": { 387 | "version": "2.0.2", 388 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", 389 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", 390 | "dev": true 391 | }, 392 | "node_modules/expect-type": { 393 | "version": "0.15.0", 394 | "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-0.15.0.tgz", 395 | "integrity": "sha512-yWnriYB4e8G54M5/fAFj7rCIBiKs1HAACaY13kCz6Ku0dezjS9aMcfcdVK2X8Tv2tEV1BPz/wKfQ7WA4S/d8aA==", 396 | "dev": true 397 | }, 398 | "node_modules/find-up": { 399 | "version": "5.0.0", 400 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 401 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 402 | "dev": true, 403 | "dependencies": { 404 | "locate-path": "^6.0.0", 405 | "path-exists": "^4.0.0" 406 | }, 407 | "engines": { 408 | "node": ">=10" 409 | }, 410 | "funding": { 411 | "url": "https://github.com/sponsors/sindresorhus" 412 | } 413 | }, 414 | "node_modules/foreground-child": { 415 | "version": "2.0.0", 416 | "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", 417 | "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", 418 | "dev": true, 419 | "dependencies": { 420 | "cross-spawn": "^7.0.0", 421 | "signal-exit": "^3.0.2" 422 | }, 423 | "engines": { 424 | "node": ">=8.0.0" 425 | } 426 | }, 427 | "node_modules/fs.realpath": { 428 | "version": "1.0.0", 429 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 430 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 431 | "dev": true 432 | }, 433 | "node_modules/fsevents": { 434 | "version": "2.3.2", 435 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 436 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 437 | "dev": true, 438 | "hasInstallScript": true, 439 | "optional": true, 440 | "os": [ 441 | "darwin" 442 | ], 443 | "engines": { 444 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 445 | } 446 | }, 447 | "node_modules/function-bind": { 448 | "version": "1.1.1", 449 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 450 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 451 | "dev": true 452 | }, 453 | "node_modules/get-caller-file": { 454 | "version": "2.0.5", 455 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 456 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 457 | "dev": true, 458 | "engines": { 459 | "node": "6.* || 8.* || >= 10.*" 460 | } 461 | }, 462 | "node_modules/glob": { 463 | "version": "7.2.3", 464 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 465 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 466 | "dev": true, 467 | "dependencies": { 468 | "fs.realpath": "^1.0.0", 469 | "inflight": "^1.0.4", 470 | "inherits": "2", 471 | "minimatch": "^3.1.1", 472 | "once": "^1.3.0", 473 | "path-is-absolute": "^1.0.0" 474 | }, 475 | "engines": { 476 | "node": "*" 477 | }, 478 | "funding": { 479 | "url": "https://github.com/sponsors/isaacs" 480 | } 481 | }, 482 | "node_modules/has": { 483 | "version": "1.0.3", 484 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 485 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 486 | "dev": true, 487 | "dependencies": { 488 | "function-bind": "^1.1.1" 489 | }, 490 | "engines": { 491 | "node": ">= 0.4.0" 492 | } 493 | }, 494 | "node_modules/has-flag": { 495 | "version": "4.0.0", 496 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 497 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 498 | "dev": true, 499 | "engines": { 500 | "node": ">=8" 501 | } 502 | }, 503 | "node_modules/html-escaper": { 504 | "version": "2.0.2", 505 | "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", 506 | "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", 507 | "dev": true 508 | }, 509 | "node_modules/inflight": { 510 | "version": "1.0.6", 511 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 512 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 513 | "dev": true, 514 | "dependencies": { 515 | "once": "^1.3.0", 516 | "wrappy": "1" 517 | } 518 | }, 519 | "node_modules/inherits": { 520 | "version": "2.0.4", 521 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 522 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 523 | "dev": true 524 | }, 525 | "node_modules/is-builtin-module": { 526 | "version": "3.2.0", 527 | "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz", 528 | "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==", 529 | "dev": true, 530 | "dependencies": { 531 | "builtin-modules": "^3.3.0" 532 | }, 533 | "engines": { 534 | "node": ">=6" 535 | }, 536 | "funding": { 537 | "url": "https://github.com/sponsors/sindresorhus" 538 | } 539 | }, 540 | "node_modules/is-core-module": { 541 | "version": "2.11.0", 542 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", 543 | "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", 544 | "dev": true, 545 | "dependencies": { 546 | "has": "^1.0.3" 547 | }, 548 | "funding": { 549 | "url": "https://github.com/sponsors/ljharb" 550 | } 551 | }, 552 | "node_modules/is-fullwidth-code-point": { 553 | "version": "3.0.0", 554 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 555 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 556 | "dev": true, 557 | "engines": { 558 | "node": ">=8" 559 | } 560 | }, 561 | "node_modules/is-module": { 562 | "version": "1.0.0", 563 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", 564 | "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", 565 | "dev": true 566 | }, 567 | "node_modules/isexe": { 568 | "version": "2.0.0", 569 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 570 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 571 | "dev": true 572 | }, 573 | "node_modules/istanbul-lib-coverage": { 574 | "version": "3.2.0", 575 | "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", 576 | "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", 577 | "dev": true, 578 | "engines": { 579 | "node": ">=8" 580 | } 581 | }, 582 | "node_modules/istanbul-lib-report": { 583 | "version": "3.0.0", 584 | "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", 585 | "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", 586 | "dev": true, 587 | "dependencies": { 588 | "istanbul-lib-coverage": "^3.0.0", 589 | "make-dir": "^3.0.0", 590 | "supports-color": "^7.1.0" 591 | }, 592 | "engines": { 593 | "node": ">=8" 594 | } 595 | }, 596 | "node_modules/istanbul-reports": { 597 | "version": "3.1.5", 598 | "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", 599 | "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", 600 | "dev": true, 601 | "dependencies": { 602 | "html-escaper": "^2.0.0", 603 | "istanbul-lib-report": "^3.0.0" 604 | }, 605 | "engines": { 606 | "node": ">=8" 607 | } 608 | }, 609 | "node_modules/locate-path": { 610 | "version": "6.0.0", 611 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 612 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 613 | "dev": true, 614 | "dependencies": { 615 | "p-locate": "^5.0.0" 616 | }, 617 | "engines": { 618 | "node": ">=10" 619 | }, 620 | "funding": { 621 | "url": "https://github.com/sponsors/sindresorhus" 622 | } 623 | }, 624 | "node_modules/make-dir": { 625 | "version": "3.1.0", 626 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 627 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 628 | "dev": true, 629 | "dependencies": { 630 | "semver": "^6.0.0" 631 | }, 632 | "engines": { 633 | "node": ">=8" 634 | }, 635 | "funding": { 636 | "url": "https://github.com/sponsors/sindresorhus" 637 | } 638 | }, 639 | "node_modules/minimatch": { 640 | "version": "3.1.2", 641 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 642 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 643 | "dev": true, 644 | "dependencies": { 645 | "brace-expansion": "^1.1.7" 646 | }, 647 | "engines": { 648 | "node": "*" 649 | } 650 | }, 651 | "node_modules/once": { 652 | "version": "1.4.0", 653 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 654 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 655 | "dev": true, 656 | "dependencies": { 657 | "wrappy": "1" 658 | } 659 | }, 660 | "node_modules/p-limit": { 661 | "version": "3.1.0", 662 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 663 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 664 | "dev": true, 665 | "dependencies": { 666 | "yocto-queue": "^0.1.0" 667 | }, 668 | "engines": { 669 | "node": ">=10" 670 | }, 671 | "funding": { 672 | "url": "https://github.com/sponsors/sindresorhus" 673 | } 674 | }, 675 | "node_modules/p-locate": { 676 | "version": "5.0.0", 677 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 678 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 679 | "dev": true, 680 | "dependencies": { 681 | "p-limit": "^3.0.2" 682 | }, 683 | "engines": { 684 | "node": ">=10" 685 | }, 686 | "funding": { 687 | "url": "https://github.com/sponsors/sindresorhus" 688 | } 689 | }, 690 | "node_modules/path-exists": { 691 | "version": "4.0.0", 692 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 693 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 694 | "dev": true, 695 | "engines": { 696 | "node": ">=8" 697 | } 698 | }, 699 | "node_modules/path-is-absolute": { 700 | "version": "1.0.1", 701 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 702 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 703 | "dev": true, 704 | "engines": { 705 | "node": ">=0.10.0" 706 | } 707 | }, 708 | "node_modules/path-key": { 709 | "version": "3.1.1", 710 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 711 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 712 | "dev": true, 713 | "engines": { 714 | "node": ">=8" 715 | } 716 | }, 717 | "node_modules/path-parse": { 718 | "version": "1.0.7", 719 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 720 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 721 | "dev": true 722 | }, 723 | "node_modules/picomatch": { 724 | "version": "2.3.1", 725 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 726 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 727 | "dev": true, 728 | "engines": { 729 | "node": ">=8.6" 730 | }, 731 | "funding": { 732 | "url": "https://github.com/sponsors/jonschlinkert" 733 | } 734 | }, 735 | "node_modules/randombytes": { 736 | "version": "2.1.0", 737 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 738 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 739 | "dev": true, 740 | "dependencies": { 741 | "safe-buffer": "^5.1.0" 742 | } 743 | }, 744 | "node_modules/require-directory": { 745 | "version": "2.1.1", 746 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 747 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 748 | "dev": true, 749 | "engines": { 750 | "node": ">=0.10.0" 751 | } 752 | }, 753 | "node_modules/resolve": { 754 | "version": "1.22.1", 755 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", 756 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", 757 | "dev": true, 758 | "dependencies": { 759 | "is-core-module": "^2.9.0", 760 | "path-parse": "^1.0.7", 761 | "supports-preserve-symlinks-flag": "^1.0.0" 762 | }, 763 | "bin": { 764 | "resolve": "bin/resolve" 765 | }, 766 | "funding": { 767 | "url": "https://github.com/sponsors/ljharb" 768 | } 769 | }, 770 | "node_modules/rimraf": { 771 | "version": "3.0.2", 772 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 773 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 774 | "dev": true, 775 | "dependencies": { 776 | "glob": "^7.1.3" 777 | }, 778 | "bin": { 779 | "rimraf": "bin.js" 780 | }, 781 | "funding": { 782 | "url": "https://github.com/sponsors/isaacs" 783 | } 784 | }, 785 | "node_modules/rollup": { 786 | "version": "3.10.1", 787 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.10.1.tgz", 788 | "integrity": "sha512-3Er+yel3bZbZX1g2kjVM+FW+RUWDxbG87fcqFM5/9HbPCTpbVp6JOLn7jlxnNlbu7s/N/uDA4EV/91E2gWnxzw==", 789 | "dev": true, 790 | "bin": { 791 | "rollup": "dist/bin/rollup" 792 | }, 793 | "engines": { 794 | "node": ">=14.18.0", 795 | "npm": ">=8.0.0" 796 | }, 797 | "optionalDependencies": { 798 | "fsevents": "~2.3.2" 799 | } 800 | }, 801 | "node_modules/safe-buffer": { 802 | "version": "5.2.1", 803 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 804 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 805 | "dev": true, 806 | "funding": [ 807 | { 808 | "type": "github", 809 | "url": "https://github.com/sponsors/feross" 810 | }, 811 | { 812 | "type": "patreon", 813 | "url": "https://www.patreon.com/feross" 814 | }, 815 | { 816 | "type": "consulting", 817 | "url": "https://feross.org/support" 818 | } 819 | ] 820 | }, 821 | "node_modules/semver": { 822 | "version": "6.3.0", 823 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 824 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 825 | "dev": true, 826 | "bin": { 827 | "semver": "bin/semver.js" 828 | } 829 | }, 830 | "node_modules/serialize-javascript": { 831 | "version": "6.0.1", 832 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", 833 | "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", 834 | "dev": true, 835 | "dependencies": { 836 | "randombytes": "^2.1.0" 837 | } 838 | }, 839 | "node_modules/shebang-command": { 840 | "version": "2.0.0", 841 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 842 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 843 | "dev": true, 844 | "dependencies": { 845 | "shebang-regex": "^3.0.0" 846 | }, 847 | "engines": { 848 | "node": ">=8" 849 | } 850 | }, 851 | "node_modules/shebang-regex": { 852 | "version": "3.0.0", 853 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 854 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 855 | "dev": true, 856 | "engines": { 857 | "node": ">=8" 858 | } 859 | }, 860 | "node_modules/signal-exit": { 861 | "version": "3.0.7", 862 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 863 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", 864 | "dev": true 865 | }, 866 | "node_modules/smob": { 867 | "version": "0.0.6", 868 | "resolved": "https://registry.npmjs.org/smob/-/smob-0.0.6.tgz", 869 | "integrity": "sha512-V21+XeNni+tTyiST1MHsa84AQhT1aFZipzPpOFAVB8DkHzwJyjjAmt9bgwnuZiZWnIbMo2duE29wybxv/7HWUw==", 870 | "dev": true 871 | }, 872 | "node_modules/source-map": { 873 | "version": "0.6.1", 874 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 875 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 876 | "dev": true, 877 | "engines": { 878 | "node": ">=0.10.0" 879 | } 880 | }, 881 | "node_modules/source-map-support": { 882 | "version": "0.5.21", 883 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 884 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 885 | "dev": true, 886 | "dependencies": { 887 | "buffer-from": "^1.0.0", 888 | "source-map": "^0.6.0" 889 | } 890 | }, 891 | "node_modules/string-width": { 892 | "version": "4.2.3", 893 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 894 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 895 | "dev": true, 896 | "dependencies": { 897 | "emoji-regex": "^8.0.0", 898 | "is-fullwidth-code-point": "^3.0.0", 899 | "strip-ansi": "^6.0.1" 900 | }, 901 | "engines": { 902 | "node": ">=8" 903 | } 904 | }, 905 | "node_modules/strip-ansi": { 906 | "version": "6.0.1", 907 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 908 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 909 | "dev": true, 910 | "dependencies": { 911 | "ansi-regex": "^5.0.1" 912 | }, 913 | "engines": { 914 | "node": ">=8" 915 | } 916 | }, 917 | "node_modules/supports-color": { 918 | "version": "7.2.0", 919 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 920 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 921 | "dev": true, 922 | "dependencies": { 923 | "has-flag": "^4.0.0" 924 | }, 925 | "engines": { 926 | "node": ">=8" 927 | } 928 | }, 929 | "node_modules/supports-preserve-symlinks-flag": { 930 | "version": "1.0.0", 931 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 932 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 933 | "dev": true, 934 | "engines": { 935 | "node": ">= 0.4" 936 | }, 937 | "funding": { 938 | "url": "https://github.com/sponsors/ljharb" 939 | } 940 | }, 941 | "node_modules/terser": { 942 | "version": "5.16.1", 943 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", 944 | "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", 945 | "dev": true, 946 | "dependencies": { 947 | "@jridgewell/source-map": "^0.3.2", 948 | "acorn": "^8.5.0", 949 | "commander": "^2.20.0", 950 | "source-map-support": "~0.5.20" 951 | }, 952 | "bin": { 953 | "terser": "bin/terser" 954 | }, 955 | "engines": { 956 | "node": ">=10" 957 | } 958 | }, 959 | "node_modules/test-exclude": { 960 | "version": "6.0.0", 961 | "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", 962 | "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", 963 | "dev": true, 964 | "dependencies": { 965 | "@istanbuljs/schema": "^0.1.2", 966 | "glob": "^7.1.4", 967 | "minimatch": "^3.0.4" 968 | }, 969 | "engines": { 970 | "node": ">=8" 971 | } 972 | }, 973 | "node_modules/typescript": { 974 | "version": "4.9.4", 975 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", 976 | "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", 977 | "dev": true, 978 | "bin": { 979 | "tsc": "bin/tsc", 980 | "tsserver": "bin/tsserver" 981 | }, 982 | "engines": { 983 | "node": ">=4.2.0" 984 | } 985 | }, 986 | "node_modules/v8-to-istanbul": { 987 | "version": "9.0.1", 988 | "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", 989 | "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", 990 | "dev": true, 991 | "dependencies": { 992 | "@jridgewell/trace-mapping": "^0.3.12", 993 | "@types/istanbul-lib-coverage": "^2.0.1", 994 | "convert-source-map": "^1.6.0" 995 | }, 996 | "engines": { 997 | "node": ">=10.12.0" 998 | } 999 | }, 1000 | "node_modules/which": { 1001 | "version": "2.0.2", 1002 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1003 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1004 | "dev": true, 1005 | "dependencies": { 1006 | "isexe": "^2.0.0" 1007 | }, 1008 | "bin": { 1009 | "node-which": "bin/node-which" 1010 | }, 1011 | "engines": { 1012 | "node": ">= 8" 1013 | } 1014 | }, 1015 | "node_modules/wrap-ansi": { 1016 | "version": "7.0.0", 1017 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1018 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1019 | "dev": true, 1020 | "dependencies": { 1021 | "ansi-styles": "^4.0.0", 1022 | "string-width": "^4.1.0", 1023 | "strip-ansi": "^6.0.0" 1024 | }, 1025 | "engines": { 1026 | "node": ">=10" 1027 | }, 1028 | "funding": { 1029 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1030 | } 1031 | }, 1032 | "node_modules/wrappy": { 1033 | "version": "1.0.2", 1034 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1035 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1036 | "dev": true 1037 | }, 1038 | "node_modules/y18n": { 1039 | "version": "5.0.8", 1040 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1041 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1042 | "dev": true, 1043 | "engines": { 1044 | "node": ">=10" 1045 | } 1046 | }, 1047 | "node_modules/yargs": { 1048 | "version": "16.2.0", 1049 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1050 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1051 | "dev": true, 1052 | "dependencies": { 1053 | "cliui": "^7.0.2", 1054 | "escalade": "^3.1.1", 1055 | "get-caller-file": "^2.0.5", 1056 | "require-directory": "^2.1.1", 1057 | "string-width": "^4.2.0", 1058 | "y18n": "^5.0.5", 1059 | "yargs-parser": "^20.2.2" 1060 | }, 1061 | "engines": { 1062 | "node": ">=10" 1063 | } 1064 | }, 1065 | "node_modules/yargs-parser": { 1066 | "version": "20.2.9", 1067 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", 1068 | "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", 1069 | "dev": true, 1070 | "engines": { 1071 | "node": ">=10" 1072 | } 1073 | }, 1074 | "node_modules/yocto-queue": { 1075 | "version": "0.1.0", 1076 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1077 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1078 | "dev": true, 1079 | "engines": { 1080 | "node": ">=10" 1081 | }, 1082 | "funding": { 1083 | "url": "https://github.com/sponsors/sindresorhus" 1084 | } 1085 | } 1086 | }, 1087 | "dependencies": { 1088 | "@babel/parser": { 1089 | "version": "7.20.13", 1090 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz", 1091 | "integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==", 1092 | "dev": true 1093 | }, 1094 | "@bcoe/v8-coverage": { 1095 | "version": "0.2.3", 1096 | "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", 1097 | "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", 1098 | "dev": true 1099 | }, 1100 | "@istanbuljs/schema": { 1101 | "version": "0.1.3", 1102 | "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", 1103 | "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", 1104 | "dev": true 1105 | }, 1106 | "@jridgewell/gen-mapping": { 1107 | "version": "0.3.2", 1108 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", 1109 | "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", 1110 | "dev": true, 1111 | "requires": { 1112 | "@jridgewell/set-array": "^1.0.1", 1113 | "@jridgewell/sourcemap-codec": "^1.4.10", 1114 | "@jridgewell/trace-mapping": "^0.3.9" 1115 | } 1116 | }, 1117 | "@jridgewell/resolve-uri": { 1118 | "version": "3.1.0", 1119 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 1120 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 1121 | "dev": true 1122 | }, 1123 | "@jridgewell/set-array": { 1124 | "version": "1.1.2", 1125 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", 1126 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", 1127 | "dev": true 1128 | }, 1129 | "@jridgewell/source-map": { 1130 | "version": "0.3.2", 1131 | "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", 1132 | "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", 1133 | "dev": true, 1134 | "requires": { 1135 | "@jridgewell/gen-mapping": "^0.3.0", 1136 | "@jridgewell/trace-mapping": "^0.3.9" 1137 | } 1138 | }, 1139 | "@jridgewell/sourcemap-codec": { 1140 | "version": "1.4.14", 1141 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 1142 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 1143 | "dev": true 1144 | }, 1145 | "@jridgewell/trace-mapping": { 1146 | "version": "0.3.17", 1147 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", 1148 | "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", 1149 | "dev": true, 1150 | "requires": { 1151 | "@jridgewell/resolve-uri": "3.1.0", 1152 | "@jridgewell/sourcemap-codec": "1.4.14" 1153 | } 1154 | }, 1155 | "@rollup/plugin-node-resolve": { 1156 | "version": "15.0.1", 1157 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz", 1158 | "integrity": "sha512-ReY88T7JhJjeRVbfCyNj+NXAG3IIsVMsX9b5/9jC98dRP8/yxlZdz7mHZbHk5zHr24wZZICS5AcXsFZAXYUQEg==", 1159 | "dev": true, 1160 | "requires": { 1161 | "@rollup/pluginutils": "^5.0.1", 1162 | "@types/resolve": "1.20.2", 1163 | "deepmerge": "^4.2.2", 1164 | "is-builtin-module": "^3.2.0", 1165 | "is-module": "^1.0.0", 1166 | "resolve": "^1.22.1" 1167 | } 1168 | }, 1169 | "@rollup/plugin-terser": { 1170 | "version": "0.4.0", 1171 | "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.0.tgz", 1172 | "integrity": "sha512-Ipcf3LPNerey1q9ZMjiaWHlNPEHNU/B5/uh9zXLltfEQ1lVSLLeZSgAtTPWGyw8Ip1guOeq+mDtdOlEj/wNxQw==", 1173 | "dev": true, 1174 | "requires": { 1175 | "serialize-javascript": "^6.0.0", 1176 | "smob": "^0.0.6", 1177 | "terser": "^5.15.1" 1178 | } 1179 | }, 1180 | "@rollup/pluginutils": { 1181 | "version": "5.0.2", 1182 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", 1183 | "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", 1184 | "dev": true, 1185 | "requires": { 1186 | "@types/estree": "^1.0.0", 1187 | "estree-walker": "^2.0.2", 1188 | "picomatch": "^2.3.1" 1189 | } 1190 | }, 1191 | "@types/estree": { 1192 | "version": "1.0.0", 1193 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", 1194 | "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", 1195 | "dev": true 1196 | }, 1197 | "@types/istanbul-lib-coverage": { 1198 | "version": "2.0.4", 1199 | "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", 1200 | "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", 1201 | "dev": true 1202 | }, 1203 | "@types/resolve": { 1204 | "version": "1.20.2", 1205 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", 1206 | "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", 1207 | "dev": true 1208 | }, 1209 | "acorn": { 1210 | "version": "8.8.1", 1211 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", 1212 | "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", 1213 | "dev": true 1214 | }, 1215 | "ansi-regex": { 1216 | "version": "5.0.1", 1217 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1218 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1219 | "dev": true 1220 | }, 1221 | "ansi-styles": { 1222 | "version": "4.3.0", 1223 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1224 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1225 | "dev": true, 1226 | "requires": { 1227 | "color-convert": "^2.0.1" 1228 | } 1229 | }, 1230 | "ascjs": { 1231 | "version": "5.0.1", 1232 | "resolved": "https://registry.npmjs.org/ascjs/-/ascjs-5.0.1.tgz", 1233 | "integrity": "sha512-1d/QdICzpywXiP53/Zz3fMdaC0/BB1ybLf+fK+QrqY8iyXNnWUHUrpmrowueXeswo+O+meJWm43TJSg2ClP3Sg==", 1234 | "dev": true, 1235 | "requires": { 1236 | "@babel/parser": "^7.12.5" 1237 | } 1238 | }, 1239 | "balanced-match": { 1240 | "version": "1.0.2", 1241 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1242 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1243 | "dev": true 1244 | }, 1245 | "brace-expansion": { 1246 | "version": "1.1.11", 1247 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1248 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1249 | "dev": true, 1250 | "requires": { 1251 | "balanced-match": "^1.0.0", 1252 | "concat-map": "0.0.1" 1253 | } 1254 | }, 1255 | "buffer-from": { 1256 | "version": "1.1.2", 1257 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 1258 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 1259 | "dev": true 1260 | }, 1261 | "builtin-modules": { 1262 | "version": "3.3.0", 1263 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", 1264 | "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", 1265 | "dev": true 1266 | }, 1267 | "c8": { 1268 | "version": "7.12.0", 1269 | "resolved": "https://registry.npmjs.org/c8/-/c8-7.12.0.tgz", 1270 | "integrity": "sha512-CtgQrHOkyxr5koX1wEUmN/5cfDa2ckbHRA4Gy5LAL0zaCFtVWJS5++n+w4/sr2GWGerBxgTjpKeDclk/Qk6W/A==", 1271 | "dev": true, 1272 | "requires": { 1273 | "@bcoe/v8-coverage": "^0.2.3", 1274 | "@istanbuljs/schema": "^0.1.3", 1275 | "find-up": "^5.0.0", 1276 | "foreground-child": "^2.0.0", 1277 | "istanbul-lib-coverage": "^3.2.0", 1278 | "istanbul-lib-report": "^3.0.0", 1279 | "istanbul-reports": "^3.1.4", 1280 | "rimraf": "^3.0.2", 1281 | "test-exclude": "^6.0.0", 1282 | "v8-to-istanbul": "^9.0.0", 1283 | "yargs": "^16.2.0", 1284 | "yargs-parser": "^20.2.9" 1285 | } 1286 | }, 1287 | "cliui": { 1288 | "version": "7.0.4", 1289 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 1290 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 1291 | "dev": true, 1292 | "requires": { 1293 | "string-width": "^4.2.0", 1294 | "strip-ansi": "^6.0.0", 1295 | "wrap-ansi": "^7.0.0" 1296 | } 1297 | }, 1298 | "color-convert": { 1299 | "version": "2.0.1", 1300 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1301 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1302 | "dev": true, 1303 | "requires": { 1304 | "color-name": "~1.1.4" 1305 | } 1306 | }, 1307 | "color-name": { 1308 | "version": "1.1.4", 1309 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1310 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1311 | "dev": true 1312 | }, 1313 | "commander": { 1314 | "version": "2.20.3", 1315 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 1316 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 1317 | "dev": true 1318 | }, 1319 | "concat-map": { 1320 | "version": "0.0.1", 1321 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1322 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 1323 | "dev": true 1324 | }, 1325 | "convert-source-map": { 1326 | "version": "1.9.0", 1327 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", 1328 | "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", 1329 | "dev": true 1330 | }, 1331 | "cross-spawn": { 1332 | "version": "7.0.3", 1333 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 1334 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 1335 | "dev": true, 1336 | "requires": { 1337 | "path-key": "^3.1.0", 1338 | "shebang-command": "^2.0.0", 1339 | "which": "^2.0.1" 1340 | } 1341 | }, 1342 | "deepmerge": { 1343 | "version": "4.2.2", 1344 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", 1345 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", 1346 | "dev": true 1347 | }, 1348 | "emoji-regex": { 1349 | "version": "8.0.0", 1350 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1351 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1352 | "dev": true 1353 | }, 1354 | "escalade": { 1355 | "version": "3.1.1", 1356 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 1357 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 1358 | "dev": true 1359 | }, 1360 | "estree-walker": { 1361 | "version": "2.0.2", 1362 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", 1363 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", 1364 | "dev": true 1365 | }, 1366 | "expect-type": { 1367 | "version": "0.15.0", 1368 | "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-0.15.0.tgz", 1369 | "integrity": "sha512-yWnriYB4e8G54M5/fAFj7rCIBiKs1HAACaY13kCz6Ku0dezjS9aMcfcdVK2X8Tv2tEV1BPz/wKfQ7WA4S/d8aA==", 1370 | "dev": true 1371 | }, 1372 | "find-up": { 1373 | "version": "5.0.0", 1374 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 1375 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1376 | "dev": true, 1377 | "requires": { 1378 | "locate-path": "^6.0.0", 1379 | "path-exists": "^4.0.0" 1380 | } 1381 | }, 1382 | "foreground-child": { 1383 | "version": "2.0.0", 1384 | "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", 1385 | "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", 1386 | "dev": true, 1387 | "requires": { 1388 | "cross-spawn": "^7.0.0", 1389 | "signal-exit": "^3.0.2" 1390 | } 1391 | }, 1392 | "fs.realpath": { 1393 | "version": "1.0.0", 1394 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1395 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 1396 | "dev": true 1397 | }, 1398 | "fsevents": { 1399 | "version": "2.3.2", 1400 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1401 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1402 | "dev": true, 1403 | "optional": true 1404 | }, 1405 | "function-bind": { 1406 | "version": "1.1.1", 1407 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 1408 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 1409 | "dev": true 1410 | }, 1411 | "get-caller-file": { 1412 | "version": "2.0.5", 1413 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1414 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 1415 | "dev": true 1416 | }, 1417 | "glob": { 1418 | "version": "7.2.3", 1419 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 1420 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 1421 | "dev": true, 1422 | "requires": { 1423 | "fs.realpath": "^1.0.0", 1424 | "inflight": "^1.0.4", 1425 | "inherits": "2", 1426 | "minimatch": "^3.1.1", 1427 | "once": "^1.3.0", 1428 | "path-is-absolute": "^1.0.0" 1429 | } 1430 | }, 1431 | "has": { 1432 | "version": "1.0.3", 1433 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 1434 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 1435 | "dev": true, 1436 | "requires": { 1437 | "function-bind": "^1.1.1" 1438 | } 1439 | }, 1440 | "has-flag": { 1441 | "version": "4.0.0", 1442 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1443 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1444 | "dev": true 1445 | }, 1446 | "html-escaper": { 1447 | "version": "2.0.2", 1448 | "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", 1449 | "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", 1450 | "dev": true 1451 | }, 1452 | "inflight": { 1453 | "version": "1.0.6", 1454 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1455 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 1456 | "dev": true, 1457 | "requires": { 1458 | "once": "^1.3.0", 1459 | "wrappy": "1" 1460 | } 1461 | }, 1462 | "inherits": { 1463 | "version": "2.0.4", 1464 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1465 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1466 | "dev": true 1467 | }, 1468 | "is-builtin-module": { 1469 | "version": "3.2.0", 1470 | "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz", 1471 | "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==", 1472 | "dev": true, 1473 | "requires": { 1474 | "builtin-modules": "^3.3.0" 1475 | } 1476 | }, 1477 | "is-core-module": { 1478 | "version": "2.11.0", 1479 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", 1480 | "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", 1481 | "dev": true, 1482 | "requires": { 1483 | "has": "^1.0.3" 1484 | } 1485 | }, 1486 | "is-fullwidth-code-point": { 1487 | "version": "3.0.0", 1488 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1489 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1490 | "dev": true 1491 | }, 1492 | "is-module": { 1493 | "version": "1.0.0", 1494 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", 1495 | "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", 1496 | "dev": true 1497 | }, 1498 | "isexe": { 1499 | "version": "2.0.0", 1500 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1501 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 1502 | "dev": true 1503 | }, 1504 | "istanbul-lib-coverage": { 1505 | "version": "3.2.0", 1506 | "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", 1507 | "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", 1508 | "dev": true 1509 | }, 1510 | "istanbul-lib-report": { 1511 | "version": "3.0.0", 1512 | "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", 1513 | "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", 1514 | "dev": true, 1515 | "requires": { 1516 | "istanbul-lib-coverage": "^3.0.0", 1517 | "make-dir": "^3.0.0", 1518 | "supports-color": "^7.1.0" 1519 | } 1520 | }, 1521 | "istanbul-reports": { 1522 | "version": "3.1.5", 1523 | "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", 1524 | "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", 1525 | "dev": true, 1526 | "requires": { 1527 | "html-escaper": "^2.0.0", 1528 | "istanbul-lib-report": "^3.0.0" 1529 | } 1530 | }, 1531 | "locate-path": { 1532 | "version": "6.0.0", 1533 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1534 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1535 | "dev": true, 1536 | "requires": { 1537 | "p-locate": "^5.0.0" 1538 | } 1539 | }, 1540 | "make-dir": { 1541 | "version": "3.1.0", 1542 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 1543 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 1544 | "dev": true, 1545 | "requires": { 1546 | "semver": "^6.0.0" 1547 | } 1548 | }, 1549 | "minimatch": { 1550 | "version": "3.1.2", 1551 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1552 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1553 | "dev": true, 1554 | "requires": { 1555 | "brace-expansion": "^1.1.7" 1556 | } 1557 | }, 1558 | "once": { 1559 | "version": "1.4.0", 1560 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1561 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1562 | "dev": true, 1563 | "requires": { 1564 | "wrappy": "1" 1565 | } 1566 | }, 1567 | "p-limit": { 1568 | "version": "3.1.0", 1569 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1570 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1571 | "dev": true, 1572 | "requires": { 1573 | "yocto-queue": "^0.1.0" 1574 | } 1575 | }, 1576 | "p-locate": { 1577 | "version": "5.0.0", 1578 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1579 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1580 | "dev": true, 1581 | "requires": { 1582 | "p-limit": "^3.0.2" 1583 | } 1584 | }, 1585 | "path-exists": { 1586 | "version": "4.0.0", 1587 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1588 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1589 | "dev": true 1590 | }, 1591 | "path-is-absolute": { 1592 | "version": "1.0.1", 1593 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1594 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1595 | "dev": true 1596 | }, 1597 | "path-key": { 1598 | "version": "3.1.1", 1599 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1600 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1601 | "dev": true 1602 | }, 1603 | "path-parse": { 1604 | "version": "1.0.7", 1605 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1606 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1607 | "dev": true 1608 | }, 1609 | "picomatch": { 1610 | "version": "2.3.1", 1611 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1612 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1613 | "dev": true 1614 | }, 1615 | "randombytes": { 1616 | "version": "2.1.0", 1617 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1618 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1619 | "dev": true, 1620 | "requires": { 1621 | "safe-buffer": "^5.1.0" 1622 | } 1623 | }, 1624 | "require-directory": { 1625 | "version": "2.1.1", 1626 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1627 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 1628 | "dev": true 1629 | }, 1630 | "resolve": { 1631 | "version": "1.22.1", 1632 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", 1633 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", 1634 | "dev": true, 1635 | "requires": { 1636 | "is-core-module": "^2.9.0", 1637 | "path-parse": "^1.0.7", 1638 | "supports-preserve-symlinks-flag": "^1.0.0" 1639 | } 1640 | }, 1641 | "rimraf": { 1642 | "version": "3.0.2", 1643 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1644 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1645 | "dev": true, 1646 | "requires": { 1647 | "glob": "^7.1.3" 1648 | } 1649 | }, 1650 | "rollup": { 1651 | "version": "3.10.1", 1652 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.10.1.tgz", 1653 | "integrity": "sha512-3Er+yel3bZbZX1g2kjVM+FW+RUWDxbG87fcqFM5/9HbPCTpbVp6JOLn7jlxnNlbu7s/N/uDA4EV/91E2gWnxzw==", 1654 | "dev": true, 1655 | "requires": { 1656 | "fsevents": "~2.3.2" 1657 | } 1658 | }, 1659 | "safe-buffer": { 1660 | "version": "5.2.1", 1661 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1662 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1663 | "dev": true 1664 | }, 1665 | "semver": { 1666 | "version": "6.3.0", 1667 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1668 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 1669 | "dev": true 1670 | }, 1671 | "serialize-javascript": { 1672 | "version": "6.0.1", 1673 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", 1674 | "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", 1675 | "dev": true, 1676 | "requires": { 1677 | "randombytes": "^2.1.0" 1678 | } 1679 | }, 1680 | "shebang-command": { 1681 | "version": "2.0.0", 1682 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1683 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1684 | "dev": true, 1685 | "requires": { 1686 | "shebang-regex": "^3.0.0" 1687 | } 1688 | }, 1689 | "shebang-regex": { 1690 | "version": "3.0.0", 1691 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1692 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1693 | "dev": true 1694 | }, 1695 | "signal-exit": { 1696 | "version": "3.0.7", 1697 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 1698 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", 1699 | "dev": true 1700 | }, 1701 | "smob": { 1702 | "version": "0.0.6", 1703 | "resolved": "https://registry.npmjs.org/smob/-/smob-0.0.6.tgz", 1704 | "integrity": "sha512-V21+XeNni+tTyiST1MHsa84AQhT1aFZipzPpOFAVB8DkHzwJyjjAmt9bgwnuZiZWnIbMo2duE29wybxv/7HWUw==", 1705 | "dev": true 1706 | }, 1707 | "source-map": { 1708 | "version": "0.6.1", 1709 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1710 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1711 | "dev": true 1712 | }, 1713 | "source-map-support": { 1714 | "version": "0.5.21", 1715 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 1716 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 1717 | "dev": true, 1718 | "requires": { 1719 | "buffer-from": "^1.0.0", 1720 | "source-map": "^0.6.0" 1721 | } 1722 | }, 1723 | "string-width": { 1724 | "version": "4.2.3", 1725 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1726 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1727 | "dev": true, 1728 | "requires": { 1729 | "emoji-regex": "^8.0.0", 1730 | "is-fullwidth-code-point": "^3.0.0", 1731 | "strip-ansi": "^6.0.1" 1732 | } 1733 | }, 1734 | "strip-ansi": { 1735 | "version": "6.0.1", 1736 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1737 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1738 | "dev": true, 1739 | "requires": { 1740 | "ansi-regex": "^5.0.1" 1741 | } 1742 | }, 1743 | "supports-color": { 1744 | "version": "7.2.0", 1745 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1746 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1747 | "dev": true, 1748 | "requires": { 1749 | "has-flag": "^4.0.0" 1750 | } 1751 | }, 1752 | "supports-preserve-symlinks-flag": { 1753 | "version": "1.0.0", 1754 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 1755 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 1756 | "dev": true 1757 | }, 1758 | "terser": { 1759 | "version": "5.16.1", 1760 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", 1761 | "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", 1762 | "dev": true, 1763 | "requires": { 1764 | "@jridgewell/source-map": "^0.3.2", 1765 | "acorn": "^8.5.0", 1766 | "commander": "^2.20.0", 1767 | "source-map-support": "~0.5.20" 1768 | } 1769 | }, 1770 | "test-exclude": { 1771 | "version": "6.0.0", 1772 | "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", 1773 | "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", 1774 | "dev": true, 1775 | "requires": { 1776 | "@istanbuljs/schema": "^0.1.2", 1777 | "glob": "^7.1.4", 1778 | "minimatch": "^3.0.4" 1779 | } 1780 | }, 1781 | "typescript": { 1782 | "version": "4.9.4", 1783 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", 1784 | "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", 1785 | "dev": true 1786 | }, 1787 | "v8-to-istanbul": { 1788 | "version": "9.0.1", 1789 | "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", 1790 | "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", 1791 | "dev": true, 1792 | "requires": { 1793 | "@jridgewell/trace-mapping": "^0.3.12", 1794 | "@types/istanbul-lib-coverage": "^2.0.1", 1795 | "convert-source-map": "^1.6.0" 1796 | } 1797 | }, 1798 | "which": { 1799 | "version": "2.0.2", 1800 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1801 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1802 | "dev": true, 1803 | "requires": { 1804 | "isexe": "^2.0.0" 1805 | } 1806 | }, 1807 | "wrap-ansi": { 1808 | "version": "7.0.0", 1809 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1810 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1811 | "dev": true, 1812 | "requires": { 1813 | "ansi-styles": "^4.0.0", 1814 | "string-width": "^4.1.0", 1815 | "strip-ansi": "^6.0.0" 1816 | } 1817 | }, 1818 | "wrappy": { 1819 | "version": "1.0.2", 1820 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1821 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1822 | "dev": true 1823 | }, 1824 | "y18n": { 1825 | "version": "5.0.8", 1826 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1827 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1828 | "dev": true 1829 | }, 1830 | "yargs": { 1831 | "version": "16.2.0", 1832 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1833 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1834 | "dev": true, 1835 | "requires": { 1836 | "cliui": "^7.0.2", 1837 | "escalade": "^3.1.1", 1838 | "get-caller-file": "^2.0.5", 1839 | "require-directory": "^2.1.1", 1840 | "string-width": "^4.2.0", 1841 | "y18n": "^5.0.5", 1842 | "yargs-parser": "^20.2.2" 1843 | } 1844 | }, 1845 | "yargs-parser": { 1846 | "version": "20.2.9", 1847 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", 1848 | "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", 1849 | "dev": true 1850 | }, 1851 | "yocto-queue": { 1852 | "version": "0.1.0", 1853 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1854 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1855 | "dev": true 1856 | } 1857 | } 1858 | } 1859 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "usignal", 3 | "version": "0.10.0", 4 | "main": "./cjs/index.js", 5 | "types": "./types/index.d.ts", 6 | "typesVersions": { 7 | ">=4.9": { 8 | "*": [ 9 | "types/*", 10 | "types/index.d.ts" 11 | ] 12 | } 13 | }, 14 | "scripts": { 15 | "benchmark": "node test/benchmark.js", 16 | "build": "npm run cjs && npm run rollup:es && npm run types && npm run readme-size && npm run test && npm run size", 17 | "cjs": "ascjs esm cjs", 18 | "rollup:es": "rollup --config rollup/es.config.js", 19 | "test": "tsc -p test && c8 node --expose-gc test/index.js $@", 20 | "size": "cat es.js | brotli | wc -c", 21 | "leak": "node --expose-gc ./test/leak.js $@", 22 | "types": "tsc -p . && npm run fix:types", 23 | "fix:types": "sed -i \"s/_: T;/value: T;/\" types/index.d.ts", 24 | "fix:exports": "sed -i \"s/\\.js/.d.ts/\" types/{async,sync}.d.ts; sed -i \"s/\\.js/.d.ts/\" types/solid/{async,sync}.d.ts; sed -i \"s/\\.js/.d.ts/\" types/fn/{index,async,sync}.d.ts", 25 | "readme-size": "sed -i \"s/, \\([0-9]\\+\\) bytes/, $(cat es.js | brotli | wc -c) bytes/\" README.md", 26 | "coverage": "mkdir -p ./coverage; c8 report --reporter=text-lcov > ./coverage/lcov.info" 27 | }, 28 | "keywords": [ 29 | "signal", 30 | "signals", 31 | "alternative" 32 | ], 33 | "author": "Andrea Gimmarchi", 34 | "license": "ISC", 35 | "devDependencies": { 36 | "@rollup/plugin-node-resolve": "^15.0.1", 37 | "@rollup/plugin-terser": "^0.4.0", 38 | "ascjs": "^5.0.1", 39 | "c8": "^7.12.0", 40 | "expect-type": "^0.15.0", 41 | "rollup": "^3.10.1", 42 | "typescript": "^4.9.4" 43 | }, 44 | "module": "./esm/index.js", 45 | "type": "module", 46 | "exports": { 47 | ".": { 48 | "browser": { 49 | "types": "./types/async.d.ts", 50 | "import": "./esm/async.js", 51 | "default": "./cjs/async.js" 52 | }, 53 | "default": { 54 | "types": "./types/sync.d.ts", 55 | "import": "./esm/sync.js", 56 | "default": "./cjs/sync.js" 57 | } 58 | }, 59 | "./core": { 60 | "types": "./types/index.d.ts", 61 | "import": "./esm/index.js", 62 | "default": "./cjs/index.js" 63 | }, 64 | "./sync": { 65 | "types": "./types/sync.d.ts", 66 | "import": "./esm/sync.js", 67 | "default": "./cjs/sync.js" 68 | }, 69 | "./async": { 70 | "types": "./types/async.d.ts", 71 | "import": "./esm/async.js", 72 | "default": "./cjs/async.js" 73 | }, 74 | "./fn": { 75 | "browser": { 76 | "types": "./types/fn/async.d.ts", 77 | "import": "./esm/fn/async.js", 78 | "default": "./cjs/fn/async.js" 79 | }, 80 | "default": { 81 | "types": "./types/fn/sync.d.ts", 82 | "import": "./esm/fn/sync.js", 83 | "default": "./cjs/fn/sync.js" 84 | } 85 | }, 86 | "./fn/core": { 87 | "types": "./types/fn/index.d.ts", 88 | "import": "./esm/fn/index.js", 89 | "default": "./cjs/fn/index.js" 90 | }, 91 | "./fn/async": { 92 | "types": "./types/fn/async.d.ts", 93 | "import": "./esm/fn/async.js", 94 | "default": "./cjs/fn/async.js" 95 | }, 96 | "./fn/sync": { 97 | "types": "./types/fn/sync.d.ts", 98 | "import": "./esm/fn/sync.js", 99 | "default": "./cjs/fn/sync.js" 100 | }, 101 | "./solid": { 102 | "browser": { 103 | "types": "./types/solid/async.d.ts", 104 | "import": "./esm/solid/async.js", 105 | "default": "./cjs/solid/async.js" 106 | }, 107 | "default": { 108 | "types": "./types/solid/sync.d.ts", 109 | "import": "./esm/solid/sync.js", 110 | "default": "./cjs/solid/sync.js" 111 | } 112 | }, 113 | "./solid/core": { 114 | "types": "./types/solid/index.d.ts", 115 | "import": "./esm/solid/index.js", 116 | "default": "./cjs/solid/index.js" 117 | }, 118 | "./solid/async": { 119 | "types": "./types/solid/async.d.ts", 120 | "import": "./esm/solid/async.js", 121 | "default": "./cjs/solid/async.js" 122 | }, 123 | "./solid/sync": { 124 | "types": "./types/solid/sync.d.ts", 125 | "import": "./esm/solid/sync.js", 126 | "default": "./cjs/solid/sync.js" 127 | }, 128 | "./package.json": "./package.json" 129 | }, 130 | "unpkg": "es.js", 131 | "repository": { 132 | "type": "git", 133 | "url": "git+https://github.com/WebReflection/usignal.git" 134 | }, 135 | "bugs": { 136 | "url": "https://github.com/WebReflection/usignal/issues" 137 | }, 138 | "homepage": "https://github.com/WebReflection/usignal#readme", 139 | "description": "A blend of @preact/signals-core and solid-js basic reactivity API" 140 | } 141 | -------------------------------------------------------------------------------- /rollup/es.config.js: -------------------------------------------------------------------------------- 1 | import {nodeResolve} from '@rollup/plugin-node-resolve'; 2 | import terser from '@rollup/plugin-terser'; 3 | 4 | export default { 5 | input: './esm/index.js', 6 | plugins: [ 7 | nodeResolve(), 8 | terser() 9 | ], 10 | output: { 11 | file: './es.js', 12 | format: 'module' 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /test/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /test/async.js: -------------------------------------------------------------------------------- 1 | export default (library, {signal, computed, effect}) => { 2 | 3 | console.log(''); 4 | console.log(`\x1b[1m${library} async\x1b[0m`); 5 | 6 | const assert = (what, why) => { 7 | console.assert(what); 8 | if (!what) 9 | throw new Error(`\x1b[1m${library}\x1b[0m: ${why}`); 10 | }; 11 | 12 | testEffect(); 13 | 14 | function testEffect() { 15 | const invokes = []; 16 | const name = signal('Jane'); 17 | const surname = signal('Doe'); 18 | const fullName = computed(() => name.value + ' ' + surname.value); 19 | 20 | effect( 21 | prev => { 22 | invokes.push(fullName.value + prev); 23 | }, 24 | 1, 25 | {async: true} 26 | ); 27 | 28 | assert(invokes.length === 0, 'effect should not be invoked'); 29 | 30 | name.value = 'John'; 31 | assert(invokes.length === 0, 'effect still not re-invoked'); 32 | 33 | queueMicrotask(() => { 34 | assert(invokes.length === 1, 'effect should have been invoked once'); 35 | assert(invokes.join('\n') === 'John Doe1', 'unexpected effect'); 36 | }); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /test/benchmark.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Extracted from: https://github.com/Riim/cellx#benchmark 3 | * Readapted from: https://github.com/maverick-js/observables#layers 4 | */ 5 | 6 | import kleur from 'kleur'; 7 | 8 | import * as cellx from 'cellx'; 9 | import * as Sjs from 's-js'; 10 | // @ts-expect-error 11 | import * as solid from './solid-js-baseline.js'; 12 | import * as preact from '@preact/signals-core'; 13 | import * as usignal from '../esm/index.js'; 14 | import * as signal from '@webreflection/signal'; 15 | import * as alien from 'alien-signals'; 16 | import Table from 'cli-table'; 17 | 18 | const RUNS_PER_TIER = 150; 19 | const LAYER_TIERS = [10, 100, 500, 1000, 2000, 2500]; 20 | const BATCHED = process.argv.includes('--batched'); 21 | 22 | const sum = (array) => array.reduce((a, b) => a + b, 0); 23 | const avg = (array) => sum(array) / array.length || 0; 24 | 25 | const SOLUTIONS = { 26 | 10: [2, 4, -2, -3], 27 | 100: [2, 2, 4, 2], 28 | 500: [-2, 1, -4, -4], 29 | 1000: [2, 2, 4, 2], 30 | 2000: [-2, 1, -4, -4], 31 | 2500: [2, 2, 4, 2], 32 | }; 33 | 34 | let rand = 0; 35 | 36 | /** 37 | * @param {number} layers 38 | * @param {number[]} answer 39 | */ 40 | const isSolution = (layers, answer) => answer.every((_, i) => { 41 | // if (SOLUTIONS[layers][i] !== _) console.log(layers, i, SOLUTIONS[layers][i], _); 42 | return SOLUTIONS[layers][i] === _; 43 | }); 44 | 45 | async function main() { 46 | const report = { 47 | S: { fn: runS, runs: [] }, 48 | solid: { fn: runSolid, runs: [] }, 49 | 'preact/signals': { fn: runPreact, runs: [] }, 50 | // TODO: running too slow so I need to leave it out for now - maybe something is wrong. 51 | // sinuous: { fn: runSinuous, runs: [] }, 52 | cellx: { fn: runCellx, runs: [] }, 53 | usignal: { fn: runUsignal, runs: [] }, 54 | signal: { fn: runSignal, runs: [] }, 55 | alien: { fn: runAlien, runs: [] }, 56 | }; 57 | 58 | for (const lib of Object.keys(report)) { 59 | const current = report[lib]; 60 | 61 | for (let i = 0; i < LAYER_TIERS.length; i += 1) { 62 | let layers = LAYER_TIERS[i]; 63 | const runs = []; 64 | rand++; 65 | for (let j = 0; j < RUNS_PER_TIER; j += 1) { 66 | runs.push(await start(current.fn, layers)); 67 | } 68 | // Give cellx time to release its global pendingCells array 69 | await new Promise((resolve) => setTimeout(resolve, 0)); 70 | 71 | current.runs[i] = avg(runs) * 1000; 72 | } 73 | } 74 | 75 | const table = new Table({ 76 | head: ['', ...LAYER_TIERS.map((n) => kleur.bold(kleur.cyan(n)))], 77 | }); 78 | 79 | for (let i = 0; i < LAYER_TIERS.length; i += 1) { 80 | let min = Infinity, 81 | max = -1, 82 | fastestLib, 83 | slowestLib; 84 | 85 | for (const lib of Object.keys(report)) { 86 | const time = report[lib].runs[i]; 87 | 88 | if (time < min) { 89 | min = time; 90 | fastestLib = lib; 91 | } 92 | 93 | if (time > max) { 94 | max = time; 95 | slowestLib = lib; 96 | } 97 | } 98 | 99 | report[fastestLib].runs[i] = kleur.green(report[fastestLib].runs[i].toFixed(2)); 100 | report[slowestLib].runs[i] = kleur.red(report[slowestLib].runs[i].toFixed(2)); 101 | } 102 | 103 | for (const lib of Object.keys(report)) { 104 | table.push([ 105 | kleur.magenta(lib), 106 | ...report[lib].runs.map((n) => (typeof n === 'number' ? n.toFixed(2) : n)), 107 | ]); 108 | } 109 | 110 | console.log(table.toString()); 111 | } 112 | 113 | async function start(runner, layers) { 114 | return new Promise((done) => { 115 | runner(layers, done); 116 | }).catch(() => -1); 117 | } 118 | 119 | /** 120 | * @see {@link https://github.com/adamhaile/S} 121 | */ 122 | function runS(layers, done) { 123 | const S = Sjs.default; 124 | 125 | S.root(() => { 126 | const start = { 127 | a: S.data(1), 128 | b: S.data(2), 129 | c: S.data(3), 130 | d: S.data(4), 131 | }; 132 | 133 | let layer = start; 134 | 135 | for (let i = layers; i--; ) { 136 | layer = ((m) => { 137 | const props = { 138 | a: S(() => rand % 2 ? m.b() : m.c()), 139 | b: S(() => m.a() - m.c()), 140 | c: S(() => m.b() + m.d()), 141 | d: S(() => m.c()), 142 | }; 143 | 144 | S(props.a), S(props.b), S(props.c), S(props.d); 145 | return props; 146 | })(layer); 147 | } 148 | 149 | const startTime = performance.now(); 150 | 151 | const run = BATCHED ? (fn) => fn() : (fn) => fn(); 152 | run(() => { 153 | start.a(4), start.b(3), start.c(2), start.d(1); 154 | }); 155 | 156 | const end = layer; 157 | const solution = [end.a(), end.b(), end.c(), end.d()]; 158 | const endTime = performance.now() - startTime; 159 | 160 | done(isSolution(layers, solution) ? endTime : -1); 161 | }); 162 | } 163 | 164 | /** 165 | * @see {@link https://github.com/solidjs/solid} 166 | */ 167 | function runSolid(layers, done) { 168 | solid.createRoot(async (dispose) => { 169 | const [a, setA] = solid.createSignal(1), 170 | [b, setB] = solid.createSignal(2), 171 | [c, setC] = solid.createSignal(3), 172 | [d, setD] = solid.createSignal(4); 173 | 174 | const start = { a, b, c, d }; 175 | 176 | let layer = start; 177 | 178 | for (let i = layers; i--; ) { 179 | layer = ((m) => { 180 | const props = { 181 | a: solid.createMemo(() => rand % 2 ? m.b() : m.c()), 182 | b: solid.createMemo(() => m.a() - m.c()), 183 | c: solid.createMemo(() => m.b() + m.d()), 184 | d: solid.createMemo(() => m.c()), 185 | }; 186 | 187 | return props; 188 | })(layer); 189 | } 190 | 191 | const startTime = performance.now(); 192 | 193 | const run = BATCHED ? solid.batch : (fn) => fn(); 194 | run(() => { 195 | setA(4), setB(3), setC(2), setD(1); 196 | }); 197 | 198 | const end = layer; 199 | const solution = [end.a(), end.b(), end.c(), end.d()]; 200 | const endTime = performance.now() - startTime; 201 | 202 | dispose(); 203 | done(isSolution(layers, solution) ? endTime : -1); 204 | }); 205 | } 206 | 207 | /** 208 | * @see {@link https://github.com/preactjs/signals} 209 | */ 210 | function runPreact(layers, done) { 211 | const a = preact.signal(1), 212 | b = preact.signal(2), 213 | c = preact.signal(3), 214 | d = preact.signal(4); 215 | 216 | const start = { a, b, c, d }; 217 | 218 | let layer = start; 219 | 220 | for (let i = layers; i--; ) { 221 | layer = ((m) => { 222 | const props = { 223 | a: preact.computed(() => rand % 2 ? m.b.value : m.c.value), 224 | b: preact.computed(() => m.a.value - m.c.value), 225 | c: preact.computed(() => m.b.value + m.d.value), 226 | d: preact.computed(() => m.c.value), 227 | }; 228 | 229 | return props; 230 | })(layer); 231 | } 232 | 233 | const startTime = performance.now(); 234 | 235 | const run = BATCHED ? preact.batch : (fn) => fn(); 236 | run(() => { 237 | (a.value = 4), (b.value = 3), (c.value = 2), (d.value = 1); 238 | 239 | const end = layer; 240 | const solution = [end.a.value, end.b.value, end.c.value, end.d.value]; 241 | const endTime = performance.now() - startTime; 242 | 243 | done(isSolution(layers, solution) ? endTime : -1); 244 | }); 245 | } 246 | 247 | /** 248 | * @see {@link https://github.com/Riim/cellx} 249 | */ 250 | function runCellx(layers, done) { 251 | const start = { 252 | a: new cellx.Cell(1), 253 | b: new cellx.Cell(2), 254 | c: new cellx.Cell(3), 255 | d: new cellx.Cell(4), 256 | }; 257 | 258 | let layer = start; 259 | 260 | for (let i = layers; i--; ) { 261 | layer = ((m) => { 262 | const props = { 263 | a: new cellx.Cell(() => rand % 2 ? m.b.get() : m.c.get()), 264 | b: new cellx.Cell(() => m.a.get() - m.c.get()), 265 | c: new cellx.Cell(() => m.b.get() + m.d.get()), 266 | d: new cellx.Cell(() => m.c.get()), 267 | }; 268 | 269 | props.a.on('change', function () {}); 270 | props.b.on('change', function () {}); 271 | props.c.on('change', function () {}); 272 | props.d.on('change', function () {}); 273 | 274 | return props; 275 | })(layer); 276 | } 277 | 278 | const startTime = performance.now(); 279 | 280 | start.a.set(4); 281 | start.b.set(3); 282 | start.c.set(2); 283 | start.d.set(1); 284 | 285 | const end = layer; 286 | const solution = [end.a.get(), end.b.get(), end.c.get(), end.d.get()]; 287 | const endTime = performance.now() - startTime; 288 | 289 | start.a.dispose(); 290 | start.b.dispose(); 291 | start.c.dispose(); 292 | start.d.dispose(); 293 | 294 | done(isSolution(layers, solution) ? endTime : -1); 295 | } 296 | 297 | /** 298 | * @see {@link https://github.com/WebReflection/usignal} 299 | */ 300 | function runUsignal(layers, done) { 301 | const a = usignal.signal(1), 302 | b = usignal.signal(2), 303 | c = usignal.signal(3), 304 | d = usignal.signal(4); 305 | 306 | const start = { a, b, c, d }; 307 | 308 | let layer = start; 309 | 310 | for (let i = layers; i--; ) { 311 | layer = ((m) => { 312 | const props = { 313 | a: usignal.computed(() => rand % 2 ? m.b.value : m.c.value), 314 | b: usignal.computed(() => m.a.value - m.c.value), 315 | c: usignal.computed(() => m.b.value + m.d.value), 316 | d: usignal.computed(() => m.c.value), 317 | }; 318 | 319 | return props; 320 | })(layer); 321 | } 322 | 323 | const startTime = performance.now(); 324 | 325 | const run = BATCHED ? usignal.batch : (fn) => fn(); 326 | run(() => { 327 | (a.value = 4), (b.value = 3), (c.value = 2), (d.value = 1); 328 | 329 | const end = layer; 330 | const solution = [end.a.value, end.b.value, end.c.value, end.d.value]; 331 | const endTime = performance.now() - startTime; 332 | 333 | done(isSolution(layers, solution) ? endTime : -1); 334 | }); 335 | } 336 | 337 | /** 338 | * @see {@link https://github.com/WebReflection/signal} 339 | */ 340 | function runSignal(layers, done) { 341 | const a = signal.signal(1), 342 | b = signal.signal(2), 343 | c = signal.signal(3), 344 | d = signal.signal(4); 345 | 346 | const start = { a, b, c, d }; 347 | 348 | let layer = start; 349 | 350 | for (let i = layers; i--; ) { 351 | layer = ((m) => { 352 | const props = { 353 | a: signal.computed(() => rand % 2 ? m.b.value : m.c.value), 354 | b: signal.computed(() => m.a.value - m.c.value), 355 | c: signal.computed(() => m.b.value + m.d.value), 356 | d: signal.computed(() => m.c.value), 357 | }; 358 | 359 | return props; 360 | })(layer); 361 | } 362 | 363 | const startTime = performance.now(); 364 | 365 | const run = BATCHED ? signal.batch : (fn) => fn(); 366 | run(() => { 367 | (a.value = 4), (b.value = 3), (c.value = 2), (d.value = 1); 368 | 369 | const end = layer; 370 | const solution = [end.a.value, end.b.value, end.c.value, end.d.value]; 371 | const endTime = performance.now() - startTime; 372 | 373 | done(isSolution(layers, solution) ? endTime : -1); 374 | }); 375 | } 376 | 377 | /** 378 | * @see {@link https://github.com/stackblitz/native-signals} 379 | */ 380 | function runAlien(layers, done) { 381 | const a = alien.signal(1), 382 | b = alien.signal(2), 383 | c = alien.signal(3), 384 | d = alien.signal(4); 385 | 386 | const start = { a, b, c, d }; 387 | 388 | let layer = start; 389 | 390 | for (let i = layers; i--; ) { 391 | layer = ((m) => { 392 | const props = { 393 | a: alien.computed(() => rand % 2 ? m.b.get() : m.c.get()), 394 | b: alien.computed(() => m.a.get() - m.c.get()), 395 | c: alien.computed(() => m.b.get() + m.d.get()), 396 | d: alien.computed(() => m.c.get()), 397 | }; 398 | 399 | return props; 400 | })(layer); 401 | } 402 | 403 | const startTime = performance.now(); 404 | 405 | const run = BATCHED ? (fn) => { 406 | alien.startBatch(); 407 | fn(); 408 | alien.endBatch(); 409 | } : (fn) => fn(); 410 | run(() => { 411 | (a.set(4)), (b.set(3)), (c.set(2)), (d.set(1)); 412 | 413 | const end = layer; 414 | const solution = [end.a.get(), end.b.get(), end.c.get(), end.d.get()]; 415 | const endTime = performance.now() - startTime; 416 | 417 | done(isSolution(layers, solution) ? endTime : -1); 418 | }); 419 | } 420 | 421 | main(); 422 | -------------------------------------------------------------------------------- /test/benchmark.old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/usignal/babeca11279d1da72e10f2e400f9cef8cd1b7c3a/test/benchmark.old.png -------------------------------------------------------------------------------- /test/benchmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/usignal/babeca11279d1da72e10f2e400f9cef8cd1b7c3a/test/benchmark.png -------------------------------------------------------------------------------- /test/callstack.js: -------------------------------------------------------------------------------- 1 | import * as usignal from '../esm/index.js'; 2 | 3 | const report = {}; 4 | 5 | testUsignal(report, 4829); 6 | 7 | console.table(report); 8 | 9 | function testUsignal(report, layerCount) { 10 | var start = { 11 | prop1: usignal.signal(1), 12 | prop2: usignal.signal(2), 13 | prop3: usignal.signal(3), 14 | prop4: usignal.signal(4) 15 | }; 16 | 17 | var layer = start; 18 | 19 | for (var i = layerCount; i--; ) { 20 | layer = (function (m) { 21 | var s = { 22 | prop1: usignal.computed(function () { 23 | return m.prop2.value; 24 | }), 25 | prop2: usignal.computed(function () { 26 | return m.prop1.value - m.prop3.value; 27 | }), 28 | prop3: usignal.computed(function () { 29 | return m.prop2.value + m.prop4.value; 30 | }), 31 | prop4: usignal.computed(function () { 32 | return m.prop3.value; 33 | }) 34 | }; 35 | 36 | usignal.computed(() => s.prop1.value); 37 | usignal.computed(() => s.prop2.value); 38 | usignal.computed(() => s.prop3.value); 39 | usignal.computed(() => s.prop4.value); 40 | 41 | s.prop1.value; 42 | s.prop2.value; 43 | s.prop3.value; 44 | s.prop4.value; 45 | 46 | return s; 47 | })(layer); 48 | } 49 | 50 | var end = layer; 51 | 52 | report.layers = layerCount; 53 | report.beforeChange = [end.prop1.value, end.prop2.value, end.prop3.value, end.prop4.value]; 54 | 55 | var st = performance.now(); 56 | 57 | start.prop1.value = 4; 58 | start.prop2.value = 3; 59 | start.prop3.value = 2; 60 | start.prop4.value = 1; 61 | 62 | report.afterChange = [end.prop1.value, end.prop2.value, end.prop3.value, end.prop4.value]; 63 | 64 | report.recalculationTime = performance.now() - st; 65 | } -------------------------------------------------------------------------------- /test/conditional.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | µsignal version 8 | 9 | 10 | 11 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | import {argv} from 'node:process'; 2 | 3 | const library = argv.find(arg => /^(?:preact|signal|solid|solid-js)$/.test(arg)); 4 | 5 | import( 6 | library ? 7 | (library === 'preact' ? 8 | './preact.js' : 9 | (library === 'signal' ? 10 | './signal.js' : 11 | './solid.js' 12 | ) 13 | ) : 14 | './usignal.js' 15 | ); 16 | -------------------------------------------------------------------------------- /test/leak.js: -------------------------------------------------------------------------------- 1 | import {argv, memoryUsage} from 'node:process'; 2 | 3 | const BATCHED = argv.includes('batched'); 4 | const library = argv.find(arg => /^(?:preact|signal|solid|solid-js)$/.test(arg)); 5 | 6 | console.log(''); 7 | console.log(`\x1b[1m${library || 'usignal'}\x1b[0m`, BATCHED ? 'batched' : ''); 8 | 9 | const {batch, effect, signal, computed} = await import( 10 | library ? 11 | (library === 'preact' ? 12 | '@preact/signals-core' : 13 | (library === 'signal' ? 14 | '@webreflection/signal' : 15 | './solid-to-usignal.js' 16 | ) 17 | ) : 18 | '../esm/index.js' 19 | ); 20 | 21 | const signals = []; 22 | 23 | for (let i = 65; i <= 90; i++) 24 | signals.push(signal(i)); 25 | 26 | let computeds = 0; 27 | 28 | console.time('total time'); 29 | console.time('computed value creation'); 30 | let comp = computed(() => { 31 | computeds++; 32 | const out = []; 33 | for (const s of signals) 34 | out.push(`${String.fromCharCode(s.value)} => ${s.value}`); 35 | return out.join(', '); 36 | }); 37 | console.timeEnd('computed value creation'); 38 | 39 | let compute = true; 40 | 41 | console.time('effect creation'); 42 | const dispose = effect(() => { 43 | if (compute) { 44 | compute = false; 45 | console.time('computed value retrieval'); 46 | console.log('\x1b[2m', comp.value.slice(0, 50) + '...', '\x1b[0m'); 47 | console.timeEnd('computed value retrieval'); 48 | } 49 | }); 50 | console.timeEnd('effect creation'); 51 | 52 | console.time('updating 26 signals'); 53 | 54 | const updates = () => { 55 | for (const s of signals) 56 | s.value++; 57 | }; 58 | 59 | if (BATCHED) 60 | batch(updates); 61 | else 62 | updates(); 63 | 64 | console.timeEnd('updating 26 signals'); 65 | 66 | console.log('\x1b[1mcomputed\x1b[0m', computeds); 67 | 68 | if (dispose) { 69 | console.time('effect disposal'); 70 | dispose(); 71 | console.timeEnd('effect disposal'); 72 | } 73 | 74 | console.time('computed value retrieval after update'); 75 | comp.value; 76 | console.timeEnd('computed value retrieval after update'); 77 | console.timeEnd('total time'); 78 | 79 | comp = null; 80 | signals.splice(0); 81 | 82 | setTimeout(() => { 83 | gc(); 84 | showMem('AFTER'); 85 | 86 | if (dispose) { 87 | console.time('effect disposal'); 88 | dispose(); 89 | console.timeEnd('effect disposal'); 90 | } 91 | }); 92 | 93 | const showMem = title => { 94 | console.log(`\n\x1b[1m${title}\x1b[0m`); 95 | const prev = memory; 96 | memory = memoryUsage(); 97 | if (prev && memory.heapUsed > prev.heapUsed) 98 | throw new Error(`\n\x1b[1m\x1b[7m MEMORY INCREASED \x1b[0m ${memory.heapUsed - prev.heapUsed}\n`); 99 | console.table(memory); 100 | }; 101 | 102 | let memory; 103 | showMem('BEFORE'); 104 | -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "devDependencies": { 4 | "@preact/signals-core": "latest", 5 | "@webreflection/signal": "latest", 6 | "cellx": "latest", 7 | "cli-table": "latest", 8 | "kleur": "latest", 9 | "alien-signals": "latest", 10 | "s-js": "latest", 11 | "solid-js": "latest" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/preact.js: -------------------------------------------------------------------------------- 1 | import test from './test.js'; 2 | import * as preact from '@preact/signals-core'; 3 | 4 | test('@preact/signals-core', preact); 5 | -------------------------------------------------------------------------------- /test/signal.js: -------------------------------------------------------------------------------- 1 | import test from './test.js'; 2 | import * as signal from '@webreflection/signal'; 3 | 4 | test('@webreflection/signal', signal); 5 | -------------------------------------------------------------------------------- /test/solid-js-baseline.js: -------------------------------------------------------------------------------- 1 | const equalFn = (a, b) => a === b; 2 | const signalOptions = { 3 | equals: equalFn, 4 | }; 5 | let ERROR = null; 6 | let runEffects = runQueue; 7 | const NOTPENDING = {}; 8 | const STALE = 1; 9 | const PENDING = 2; 10 | const UNOWNED = { 11 | owned: null, 12 | cleanups: null, 13 | context: null, 14 | owner: null, 15 | }; 16 | var Owner = null; 17 | let Listener = null; 18 | let Pending = null; 19 | let Updates = null; 20 | let Effects = null; 21 | let ExecCount = 0; 22 | function createRoot(fn, detachedOwner) { 23 | detachedOwner && (Owner = detachedOwner); 24 | const listener = Listener, 25 | owner = Owner, 26 | root = 27 | fn.length === 0 && !false 28 | ? UNOWNED 29 | : { 30 | owned: null, 31 | cleanups: null, 32 | context: null, 33 | owner, 34 | }; 35 | Owner = root; 36 | Listener = null; 37 | let result; 38 | try { 39 | runUpdates(() => (result = fn(() => cleanNode(root))), true); 40 | } finally { 41 | Listener = listener; 42 | Owner = owner; 43 | } 44 | return result; 45 | } 46 | function createSignal(value, options) { 47 | options = options ? Object.assign({}, signalOptions, options) : signalOptions; 48 | const s = { 49 | value, 50 | observers: null, 51 | observerSlots: null, 52 | pending: NOTPENDING, 53 | comparator: options.equals || undefined, 54 | }; 55 | return [ 56 | readSignal.bind(s), 57 | (value) => { 58 | if (typeof value === 'function') { 59 | value = value(s.pending !== NOTPENDING ? s.pending : s.value); 60 | } 61 | return writeSignal(s, value); 62 | }, 63 | ]; 64 | } 65 | function createComputed(fn, value) { 66 | updateComputation(createComputation(fn, value, true, STALE)); 67 | } 68 | function createMemo(fn, value, options) { 69 | options = options ? Object.assign({}, signalOptions, options) : signalOptions; 70 | const c = createComputation(fn, value, true, 0); 71 | c.pending = NOTPENDING; 72 | c.observers = null; 73 | c.observerSlots = null; 74 | c.comparator = options.equals || undefined; 75 | updateComputation(c); 76 | return readSignal.bind(c); 77 | } 78 | 79 | function batch(fn) { 80 | if (Pending) return fn(); 81 | let result; 82 | const q = (Pending = []); 83 | try { 84 | result = fn(); 85 | } finally { 86 | Pending = null; 87 | } 88 | runUpdates(() => { 89 | for (let i = 0; i < q.length; i += 1) { 90 | const data = q[i]; 91 | if (data.pending !== NOTPENDING) { 92 | const pending = data.pending; 93 | data.pending = NOTPENDING; 94 | writeSignal(data, pending); 95 | } 96 | } 97 | }, false); 98 | return result; 99 | } 100 | function untrack(fn) { 101 | let result, 102 | listener = Listener; 103 | Listener = null; 104 | result = fn(); 105 | Listener = listener; 106 | return result; 107 | } 108 | 109 | function readSignal() { 110 | if (this.state && this.sources) { 111 | const updates = Updates; 112 | Updates = null; 113 | this.state === STALE ? updateComputation(this) : lookDownstream(this); 114 | Updates = updates; 115 | } 116 | if (Listener) { 117 | const sSlot = this.observers ? this.observers.length : 0; 118 | if (!Listener.sources) { 119 | Listener.sources = [this]; 120 | Listener.sourceSlots = [sSlot]; 121 | } else { 122 | Listener.sources.push(this); 123 | Listener.sourceSlots.push(sSlot); 124 | } 125 | if (!this.observers) { 126 | this.observers = [Listener]; 127 | this.observerSlots = [Listener.sources.length - 1]; 128 | } else { 129 | this.observers.push(Listener); 130 | this.observerSlots.push(Listener.sources.length - 1); 131 | } 132 | } 133 | return this.value; 134 | } 135 | function writeSignal(node, value, isComp) { 136 | if (node.comparator) { 137 | if (node.comparator(node.value, value)) return value; 138 | } 139 | if (Pending) { 140 | if (node.pending === NOTPENDING) Pending.push(node); 141 | node.pending = value; 142 | return value; 143 | } 144 | node.value = value; 145 | if (node.observers && node.observers.length) { 146 | runUpdates(() => { 147 | for (let i = 0; i < node.observers.length; i += 1) { 148 | const o = node.observers[i]; 149 | if (!o.state) { 150 | if (o.pure) Updates.push(o); 151 | else Effects.push(o); 152 | if (o.observers) markUpstream(o); 153 | } 154 | o.state = STALE; 155 | } 156 | if (Updates.length > 10e5) { 157 | Updates = []; 158 | throw new Error(); 159 | } 160 | }, false); 161 | } 162 | return value; 163 | } 164 | function updateComputation(node) { 165 | if (!node.fn) return; 166 | cleanNode(node); 167 | const owner = Owner, 168 | listener = Listener, 169 | time = ExecCount; 170 | Listener = Owner = node; 171 | runComputation(node, node.value, time); 172 | Listener = listener; 173 | Owner = owner; 174 | } 175 | function runComputation(node, value, time) { 176 | let nextValue; 177 | nextValue = node.fn(value); 178 | if (!node.updatedAt || node.updatedAt <= time) { 179 | if (node.observers && node.observers.length) { 180 | writeSignal(node, nextValue, true); 181 | } else node.value = nextValue; 182 | node.updatedAt = time; 183 | } 184 | } 185 | function createComputation(fn, init, pure, state = STALE, options) { 186 | const c = { 187 | fn, 188 | state: state, 189 | updatedAt: null, 190 | owned: null, 191 | sources: null, 192 | sourceSlots: null, 193 | cleanups: null, 194 | value: init, 195 | owner: Owner, 196 | context: null, 197 | pure, 198 | }; 199 | if (Owner === null); 200 | else if (Owner !== UNOWNED) { 201 | if (!Owner.owned) Owner.owned = [c]; 202 | else Owner.owned.push(c); 203 | } 204 | return c; 205 | } 206 | function runTop(node) { 207 | if (node.state !== STALE) return lookDownstream(node); 208 | const ancestors = [node]; 209 | while ((node = node.owner) && (!node.updatedAt || node.updatedAt < ExecCount)) { 210 | if (node.state) ancestors.push(node); 211 | } 212 | for (let i = ancestors.length - 1; i >= 0; i--) { 213 | node = ancestors[i]; 214 | if (node.state === STALE) { 215 | updateComputation(node); 216 | } else if (node.state === PENDING) { 217 | const updates = Updates; 218 | Updates = null; 219 | lookDownstream(node); 220 | Updates = updates; 221 | } 222 | } 223 | } 224 | function runUpdates(fn, init) { 225 | if (Updates) return fn(); 226 | let wait = false; 227 | if (!init) Updates = []; 228 | if (Effects) wait = true; 229 | else Effects = []; 230 | ExecCount++; 231 | try { 232 | fn(); 233 | } finally { 234 | completeUpdates(wait); 235 | } 236 | } 237 | function completeUpdates(wait) { 238 | if (Updates) { 239 | runQueue(Updates); 240 | Updates = null; 241 | } 242 | if (wait) return; 243 | if (Effects.length) 244 | batch(() => { 245 | runEffects(Effects); 246 | Effects = null; 247 | }); 248 | else { 249 | Effects = null; 250 | } 251 | } 252 | function runQueue(queue) { 253 | for (let i = 0; i < queue.length; i++) runTop(queue[i]); 254 | } 255 | function lookDownstream(node) { 256 | node.state = 0; 257 | for (let i = 0; i < node.sources.length; i += 1) { 258 | const source = node.sources[i]; 259 | if (source.sources) { 260 | if (source.state === STALE) runTop(source); 261 | else if (source.state === PENDING) lookDownstream(source); 262 | } 263 | } 264 | } 265 | function markUpstream(node) { 266 | for (let i = 0; i < node.observers.length; i += 1) { 267 | const o = node.observers[i]; 268 | if (!o.state) { 269 | o.state = PENDING; 270 | if (o.pure) Updates.push(o); 271 | else Effects.push(o); 272 | o.observers && markUpstream(o); 273 | } 274 | } 275 | } 276 | function cleanNode(node) { 277 | let i; 278 | if (node.sources) { 279 | while (node.sources.length) { 280 | const source = node.sources.pop(), 281 | index = node.sourceSlots.pop(), 282 | obs = source.observers; 283 | if (obs && obs.length) { 284 | const n = obs.pop(), 285 | s = source.observerSlots.pop(); 286 | if (index < obs.length) { 287 | n.sourceSlots[s] = index; 288 | obs[index] = n; 289 | source.observerSlots[index] = s; 290 | } 291 | } 292 | } 293 | } 294 | if (node.owned) { 295 | for (i = 0; i < node.owned.length; i++) cleanNode(node.owned[i]); 296 | node.owned = null; 297 | } 298 | if (node.cleanups) { 299 | for (i = 0; i < node.cleanups.length; i++) node.cleanups[i](); 300 | node.cleanups = null; 301 | } 302 | node.state = 0; 303 | node.context = null; 304 | } 305 | export { createComputed, createMemo, createRoot, createSignal, batch }; 306 | -------------------------------------------------------------------------------- /test/solid-to-usignal.js: -------------------------------------------------------------------------------- 1 | import {batch, createEffect, createMemo, createSignal} from "./node_modules/solid-js/dist/solid.js"; 2 | 3 | class Computed { 4 | constructor(_) { 5 | this._ = createMemo(_); 6 | } 7 | get value() { 8 | return this._(); 9 | } 10 | toString() { 11 | return this.value; 12 | } 13 | } 14 | 15 | class Signal { 16 | constructor(_) { 17 | const [s, u] = createSignal(_); 18 | this.s = s; 19 | this.u = u; 20 | this._ = _; 21 | } 22 | get value() { 23 | return this.s(); 24 | } 25 | set value(_) { 26 | this._ = _; 27 | this.u(_); 28 | } 29 | peek() { 30 | return this._; 31 | } 32 | toString() { 33 | return this.value; 34 | } 35 | } 36 | 37 | const computed = _ => new Computed(_); 38 | const effect = createEffect; 39 | const signal = _ => new Signal(_); 40 | 41 | export {batch, computed, effect, signal, Signal}; 42 | -------------------------------------------------------------------------------- /test/solid.js: -------------------------------------------------------------------------------- 1 | import test from './test.js'; 2 | import * as solidJS from './solid-to-usignal.js'; 3 | 4 | test('solid-js', solidJS); 5 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | export default (library, {signal, computed, effect, batch, Signal}) => { 2 | 3 | console.log(''); 4 | console.log(`\x1b[1m${library}\x1b[0m`); 5 | 6 | const assert = (what, why) => { 7 | console.assert(what); 8 | if (!what) 9 | throw new Error(`\x1b[1m${library}\x1b[0m: ${why}`); 10 | }; 11 | 12 | if (library === 'usignal') { 13 | assert(JSON.stringify(signal(1)) === '1', 'JSON not working'); 14 | assert((signal(1) + signal(2)) === 3, 'valueOf not working'); 15 | testStoppedAsyncEffect(); 16 | // (async t => { 17 | // assert(await signal(3) === 3, 'await signal broke'); 18 | // assert(await signal(3).then(String) === '3', 'signal.then broke'); 19 | // assert(await computed(() => 4) === 4, 'thenable computed'); 20 | // clearTimeout(t); 21 | // })(setTimeout(assert, 1000, false, 'promise does not resolve')); 22 | 23 | const one = signal(1, {equals: false}); 24 | const invokes = []; 25 | const dispose = effect(() => { 26 | invokes.push(one.value); 27 | }); 28 | one.value = 1; 29 | assert(invokes.join(',') === '1,1', 'equals false not working'); 30 | } 31 | 32 | assert(signal(0) instanceof Signal, 'signals are not instances of Signal'); 33 | 34 | if (/^(?:usignal|@preact\/signals-core)$/.test(library)) { 35 | assert(computed(() => {}) instanceof Signal, 'computeds are not instances of Signal'); 36 | let calls = 0; 37 | const $ = signal(0); 38 | const dispose = effect(() => { 39 | calls++; 40 | $.value; 41 | return () => calls++; 42 | }); 43 | 44 | $.value = 1; 45 | assert(calls === 3, 'effect did not call again'); 46 | 47 | dispose(); 48 | assert(calls === 4, 'dispose did not invoke the calllback'); 49 | } 50 | 51 | testPrimitive(); 52 | testComputed(); 53 | testEffect(); 54 | testBatch(); 55 | if (!/^(?:@webreflection\/signal)$/.test(library)) 56 | testBatchMore(); 57 | nestedBatch(); 58 | readOnly(); 59 | testPeek(); 60 | testComputedPeek(); 61 | testComputedUniqueness(); 62 | if (!/^(?:@webreflection\/signal)$/.test(library)) 63 | testComputedMoar(); 64 | 65 | if (/^(?:usignal|@preact\/signals-core|@webreflection\/signal)$/.test(library)) 66 | implicitToString(); 67 | 68 | if (!/^(?:@webreflection\/signal)$/.test(library)) { 69 | testDiamond(); 70 | loopedEffects(); 71 | } 72 | 73 | nestedEffects(); 74 | nestedIndependentEffects(); 75 | testConditional(); 76 | 77 | function testPrimitive() { 78 | const str = signal('string'); 79 | assert(`a ${str}` === 'a string', 'toString not working'); 80 | } 81 | 82 | function testComputed() { 83 | const name = signal('Jane'); 84 | const surname = signal('Doe'); 85 | 86 | const fullName = computed(() => name.value + ' ' + surname.value); 87 | 88 | assert(fullName.value === 'Jane Doe', 'computed not working'); 89 | assert(`${fullName}` === 'Jane Doe', 'computed toString not working'); 90 | assert(`${fullName + ''}` === 'Jane Doe', 'computed valueOf not working'); 91 | 92 | name.value = 'John'; 93 | assert(fullName.value === 'John Doe', 'computed not updating'); 94 | } 95 | 96 | function testEffect() { 97 | const invokes = []; 98 | const name = signal('Jane'); 99 | const surname = signal('Doe'); 100 | const fullName = computed(() => name.value + ' ' + surname.value); 101 | 102 | const dispose = effect(() => { 103 | invokes.push(fullName.value); 104 | }); 105 | 106 | assert(invokes.length === 1, 'effect not invoked'); 107 | 108 | name.value = 'John'; 109 | assert(invokes.length === 2, 'effect not re-invoked'); 110 | assert(invokes.join('\n') === 'Jane Doe\nJohn Doe', 'unexpected effect'); 111 | 112 | // testing same value doesn't side-effect 113 | name.value = 'John'; 114 | assert(invokes.length === 2, 'effect side-effecting'); 115 | assert(invokes.join('\n') === 'Jane Doe\nJohn Doe', 'unexpected side-effect'); 116 | 117 | name.value = 'Jane'; 118 | surname.value = 'Deo'; 119 | assert(invokes.length === 4, 'non batched not working'); 120 | assert(invokes.join('\n') === 'Jane Doe\nJohn Doe\nJane Doe\nJane Deo', 'unexpected non batched'); 121 | 122 | if (dispose) { 123 | dispose(); 124 | name.value = 'What'; 125 | surname.value = 'Ever'; 126 | assert(invokes.length === 4, 'dispose is not working'); 127 | } 128 | } 129 | 130 | function testStoppedAsyncEffect() { 131 | const invokes = []; 132 | const dispose = effect( 133 | () => { 134 | invokes.push('this should not happen'); 135 | }, 136 | void 0, 137 | {async: true} 138 | ); 139 | dispose(); 140 | setTimeout(() => { 141 | assert(invokes.length === 0, 'async effect shold not have been triggered'); 142 | }); 143 | } 144 | 145 | function testBatch() { 146 | const invokes = []; 147 | const name = signal("Jane"); 148 | const surname = signal("Doe"); 149 | const fullName = computed(() => name.value + " " + surname.value); 150 | 151 | effect(() => { 152 | invokes.push(fullName.value); 153 | }); 154 | 155 | assert(invokes.length === 1, 'effect not working in bached'); 156 | 157 | batch(() => { 158 | name.value = "Foo"; 159 | surname.value = "Bar"; 160 | }); 161 | 162 | assert(invokes.length === 2, 'batch not working'); 163 | assert(invokes.join('\n') === 'Jane Doe\nFoo Bar', 'batch not updating'); 164 | } 165 | 166 | function testBatchMore() { 167 | const invokes = []; 168 | const counter = signal(0); 169 | const double = computed(() => counter.value * 2); 170 | const tripple = computed(() => counter.value * 3); 171 | 172 | effect(() => { 173 | invokes.push([double.value, tripple.value]); 174 | }); 175 | 176 | assert(invokes.length === 1, 'effect not working in bached more'); 177 | 178 | batch(() => { 179 | counter.value = 1; 180 | assert(double.value === 2, 'computed side-effecting within batch'); 181 | }); 182 | 183 | assert(invokes.length === 2, 'unexpected more batched result'); 184 | assert(invokes[0].join(',') === '0,0', 'unexpected batched more values'); 185 | assert(invokes[1].join(',') === '2,3'); 186 | } 187 | 188 | function nestedBatch() { 189 | const invokes = []; 190 | const counter = signal(0); 191 | 192 | effect(() => { 193 | invokes.push(counter.value); 194 | }); 195 | 196 | assert(invokes.length === 1, 'effect not working in nested batches'); 197 | 198 | batch(() => { 199 | batch(() => { 200 | counter.value = 1; 201 | }); 202 | assert(invokes.length === 1, 'nested batch not working'); 203 | assert(invokes[0] === 0, 'unexpected nested batch result'); 204 | }); 205 | 206 | assert(invokes.length === 2, 'nested batches fully not working'); 207 | assert(invokes[1] === 1, 'unexpected nested batched invokes'); 208 | } 209 | 210 | function readOnly() { 211 | const name = signal('Jane'); 212 | const surname = signal('Doe'); 213 | const fullName = computed(() => name.value + ' ' + surname.value); 214 | try { 215 | fullName.value = ""; 216 | assert(false, 'read-only not working'); 217 | } 218 | catch (expected) {} 219 | } 220 | 221 | function testPeek() { 222 | const invokes = []; 223 | const counter = signal(0); 224 | 225 | effect(() => { 226 | invokes.push(counter.peek()); 227 | }); 228 | 229 | assert(invokes.length === 1, 'effect not working in peek'); 230 | assert(invokes[0] === 0, 'peek not returning right value'); 231 | 232 | counter.value = 1; 233 | assert(invokes.length === 1, 'peek not working as expected'); 234 | } 235 | 236 | function testComputedPeek() { 237 | const invokes = []; 238 | const counter = signal(0); 239 | const doubleCounter = computed(() => counter.value * 2) 240 | doubleCounter.value 241 | 242 | effect(() => { 243 | invokes.push(doubleCounter.peek()); 244 | }); 245 | 246 | assert(invokes.length === 1, 'effect not working in computed peek'); 247 | assert(invokes[0] === 0, 'computed peek not returning right value'); 248 | 249 | counter.value = 1; 250 | assert(invokes.length === 1, 'computed peek not working as expected'); 251 | assert(doubleCounter.peek() === 2, 'computed peek not returning right value'); 252 | } 253 | 254 | function testComputedUniqueness() { 255 | let invokes = 0; 256 | const name = signal('Jane'); 257 | const surname = { 258 | get value() { 259 | invokes++; 260 | return 'Doe'; 261 | } 262 | }; 263 | const fullName = computed(() => name.value + ' ' + surname.value); 264 | assert(fullName.value === 'Jane Doe', 'computed not working'); 265 | assert(invokes === 1, 'computed value should have been invoked once'); 266 | name.value = 'John'; 267 | if (/^(?:usignal|@preact\/signals-core|@webreflection\/signal)$/.test(library)) fullName.value; 268 | assert(invokes === 2, 'computed value should have been invoked again'); 269 | name.value = 'John'; 270 | if (/^(?:usignal|@preact\/signals-core|@webreflection\/signal)$/.test(library)) fullName.value; 271 | assert(invokes === 2, 'computed value should NOT have been invoked again'); 272 | } 273 | 274 | function testComputedMoar() { 275 | let BCalc = 0; 276 | let CCalc = 0; 277 | const A = signal(0); 278 | const B = computed(()=> { 279 | BCalc++; 280 | return A.value + 1; 281 | }); 282 | const C = computed(() => { 283 | CCalc++; 284 | return A.value + B.value; 285 | }); 286 | 287 | assert( 288 | JSON.stringify({ 289 | B: [B.value, BCalc], 290 | C: [C.value, CCalc] 291 | }) === '{"B":[1,1],"C":[1,1]}', 292 | 'unexpected amount of invokes' 293 | ); 294 | 295 | A.value = 1; 296 | 297 | assert( 298 | JSON.stringify({ 299 | B: [B.value, BCalc], 300 | C: [C.value, CCalc] 301 | }) === '{"B":[2,2],"C":[3,2]}', 302 | 'unexpected amount of invokes after single change' 303 | ); 304 | } 305 | 306 | function implicitToString() { 307 | let invokes = 0; 308 | const number = signal(1); 309 | const sum = computed(() => { 310 | invokes++; 311 | return `${number} + 2 = 3`; 312 | }); 313 | assert(sum.value === '1 + 2 = 3', 'computed with toString() did not return the expected value'); 314 | number.value = 0; 315 | assert(sum.value === '0 + 2 = 3', 'computed with toString() after value did not return the expected value'); 316 | assert(invokes === 2, 'computed with toString() did not get invoked'); 317 | } 318 | 319 | function testDiamond() { 320 | let BCalc = 0; 321 | let CCalc = 0; 322 | let DCalc = 0; 323 | const a = signal(1); 324 | const b = computed(() => { 325 | BCalc++; 326 | return a.value; 327 | }); 328 | const c = computed(() => { 329 | CCalc++; 330 | return a.value; 331 | }); 332 | const d = computed(() => { 333 | DCalc++; 334 | return b.value + c.value; 335 | }); 336 | 337 | assert(d.value === 2, 'initial d value is wrong'); 338 | assert([BCalc, CCalc, DCalc].join(',') === '1,1,1', 'initial calculation is wrong'); 339 | a.value = 2; 340 | assert(d.value === 4, 'second d value is wrong'); 341 | assert([BCalc, CCalc, DCalc].join(',') === '2,2,2', 'second calculation is wrong: '); 342 | a.value = 3; 343 | assert(d.value === 6, 'third d value is wrong'); 344 | assert([BCalc, CCalc, DCalc].join(',') === '3,3,3', 'third calculation is wrong'); 345 | } 346 | 347 | function loopedEffects() { 348 | const invokes = []; 349 | const num = signal(0); 350 | let loop = 2; 351 | 352 | effect(() => { 353 | invokes.push(num.value); 354 | for (let i = 0; i < loop; i++) 355 | effect(() => { 356 | invokes.push(num.value + i); 357 | }); 358 | }); 359 | 360 | assert(invokes.length === 3, 'looped effects not working'); 361 | assert(invokes.join(',') === '0,0,1', 'looped values not matching'); 362 | 363 | invokes.splice(0); 364 | loop = 1; 365 | num.value = 1; 366 | 367 | if (!/^(?:@preact\/signals-core)$/.test(library)) { 368 | assert(invokes.length === 2, 'looped effects not working after changes'); 369 | assert(invokes.join(',') === '1,1', 'looped values not matching after changes'); 370 | } 371 | } 372 | 373 | // check different output in preact/usignal/solid 374 | // even if the logic / result is almost same output 375 | function nestedEffects() { 376 | console.log('------'); 377 | 378 | const counter = signal(1); 379 | const double = computed(() => { 380 | console.log('double'); 381 | return counter.value * 2 382 | }); 383 | const tripple = computed(() => { 384 | console.log('triple'); 385 | return counter.value * 3; 386 | }); 387 | 388 | const dispose1 = effect(() => { 389 | console.log('outer', double.value); 390 | effect(() => { 391 | console.log('nested', tripple.value); 392 | }); 393 | }); 394 | 395 | const dispose2 = effect(() => { 396 | console.log('a part', tripple.value); 397 | }); 398 | 399 | console.log('- - -'); 400 | counter.value = 20; 401 | console.log('------'); 402 | console.log(''); 403 | 404 | if (dispose1 && dispose2) { 405 | dispose1(); 406 | dispose2(); 407 | counter.value = 40; 408 | } 409 | } 410 | 411 | function nestedIndependentEffects() { 412 | console.log('------'); 413 | 414 | const a = signal(1); 415 | const b = signal(1); 416 | 417 | effect(() => { 418 | console.log('outer', a.value); 419 | effect(() => { 420 | console.log('nested', b.value); 421 | }); 422 | }); 423 | 424 | effect(() => { 425 | console.log('a part', a.value); 426 | }); 427 | 428 | console.log('- - -'); 429 | a.value = 2; 430 | console.log('- - -'); 431 | b.value = 3; 432 | console.log('- - -'); 433 | a.value = 3; 434 | console.log('------'); 435 | console.log(''); 436 | } 437 | 438 | function testConditional() { 439 | const first = signal('John'); 440 | const last = signal('Doe'); 441 | const full = computed(() => { 442 | console.log('Computing name', first.value, last.value); 443 | return `${first.value} ${last.value}`; 444 | }); 445 | const nickname = signal(undefined); 446 | 447 | effect(() => { 448 | if (nickname.value) { 449 | console.log('profile name', nickname.value); 450 | } else { 451 | console.log('profile name', full.value); 452 | } 453 | }); 454 | 455 | nickname.value = 'jdoe'; 456 | 457 | // this should _not_ execute effects, right? 458 | last.value = 'Smith'; 459 | } 460 | }; 461 | -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES2022", 4 | "target": "ES2022", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noEmit": true 8 | }, 9 | "include": [ 10 | "types.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /test/types.ts: -------------------------------------------------------------------------------- 1 | import { signal, computed, ReactiveSignal, ComputedSignal } from '..' 2 | import {expectTypeOf} from 'expect-type' 3 | 4 | const sig = signal(123) 5 | expectTypeOf(sig).toEqualTypeOf>() 6 | expectTypeOf(sig.value).toEqualTypeOf() 7 | expectTypeOf(sig.valueOf()).toEqualTypeOf() 8 | expectTypeOf(sig.toString()).toEqualTypeOf() 9 | expectTypeOf(sig.peek()).toEqualTypeOf() 10 | expectTypeOf(sig.toJSON()).toEqualTypeOf() 11 | // expectTypeOf(await sig).toEqualTypeOf() 12 | // sig.then(v => expectTypeOf(v).toEqualTypeOf()) 13 | 14 | const comp = computed(() => 213) 15 | expectTypeOf(comp).toEqualTypeOf>() 16 | expectTypeOf(comp.value).toEqualTypeOf() 17 | expectTypeOf(comp.valueOf()).toEqualTypeOf() 18 | expectTypeOf(comp.toString()).toEqualTypeOf() 19 | expectTypeOf(comp.peek()).toEqualTypeOf() 20 | expectTypeOf(comp.toJSON()).toEqualTypeOf() 21 | // expectTypeOf(await comp).toEqualTypeOf() 22 | // comp.then(v => expectTypeOf(v).toEqualTypeOf()) 23 | 24 | const compMix = computed(() => 'asd', 123) 25 | expectTypeOf(compMix).toEqualTypeOf>() 26 | expectTypeOf(compMix.value).toEqualTypeOf() 27 | expectTypeOf(compMix.valueOf()).toEqualTypeOf() 28 | expectTypeOf(compMix.toString()).toEqualTypeOf() 29 | expectTypeOf(compMix.peek()).toEqualTypeOf() 30 | expectTypeOf(compMix.toJSON()).toEqualTypeOf() 31 | // expectTypeOf(await compMix).toEqualTypeOf() 32 | // compMix.then(v => expectTypeOf(v).toEqualTypeOf()) 33 | -------------------------------------------------------------------------------- /test/usignal.js: -------------------------------------------------------------------------------- 1 | import './leak.js'; 2 | import * as usignal from '../esm/index.js'; 3 | 4 | const library = 'usignal'; 5 | 6 | const assert = (what, why) => { 7 | console.assert(what); 8 | if (!what) 9 | throw new Error(`\x1b[1m${library}\x1b[0m: ${why}`); 10 | }; 11 | 12 | let invokes = 0; 13 | const fx = new usignal.FX(() => { invokes++; }, null, {equals: true}); 14 | fx.run(); 15 | assert(invokes === 1); 16 | fx.run(); 17 | assert(invokes === 2); 18 | 19 | 20 | setTimeout( 21 | async () => { 22 | (await import('./test.js')).default('usignal', usignal); 23 | (await import('./async.js')).default('usignal', usignal); 24 | }, 25 | 250 26 | ); 27 | 28 | import {signal, computed} from '../esm/fn/index.js'; 29 | 30 | var s = signal(0); 31 | var c = computed(() => s()); 32 | 33 | assert(s() === 0 && c() === 0, 'wrong initial value'); 34 | assert(s(1) === 1 && c() === 1, 'wrong updated value'); 35 | 36 | import {createEffect, createMemo, createSignal} from '../esm/solid/index.js'; 37 | 38 | var [s, u] = createSignal(() => 0); 39 | var c = createMemo(() => s()); 40 | 41 | createEffect( 42 | prev => { 43 | assert(s() === prev && c() === prev); 44 | return s() + 1; 45 | }, 46 | 0 47 | ); 48 | 49 | assert(s() === 0); 50 | u(1); 51 | assert(s() === 1); 52 | -------------------------------------------------------------------------------- /test/weak-effects.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 23 | 24 | 25 | 26 |

27 | 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES2020", 4 | "target": "ES2020", 5 | "moduleResolution": "Classic", 6 | "allowJs": true, 7 | "declaration": true, 8 | "emitDeclarationOnly": true, 9 | "declarationDir": "types" 10 | }, 11 | "include": [ 12 | "esm/**/*.js" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /types/async.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./index.js"; 2 | /** 3 | * Invokes asynchronously a function when any of its internal signals or computed values change. 4 | * 5 | * Returns a dispose callback. 6 | * @template T 7 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 8 | */ 9 | export const effect: (fn: (v?: T) => T, value?: T) => () => void; 10 | -------------------------------------------------------------------------------- /types/fn/async.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./index.js"; 2 | /** 3 | * Invokes asynchronously a function when any of its internal signals or computed values change. 4 | * 5 | * Returns a dispose callback. 6 | * @template T 7 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 8 | */ 9 | export const effect: (fn: (v?: T) => T, value?: T) => () => void; 10 | -------------------------------------------------------------------------------- /types/fn/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from "../index.js"; 2 | /** 3 | * Returns a callback that is invoked only when any of the internally 4 | * used signals, as in within the callback, is unknown or updated. 5 | * @template T 6 | * @type {(fn: (v: T) => T, value?: T, options?: { equals?: boolean | ((prev: T, next: T) => boolean) }) => () => T} 7 | */ 8 | export const computed: (fn: (v: T) => T, value?: T, options?: { 9 | equals?: boolean | ((prev: T, next: T) => boolean); 10 | }) => () => T; 11 | export function signal(value: T, options?: { 12 | equals?: boolean | ((prev: T, next: T) => boolean); 13 | }): (value?: T) => T; 14 | -------------------------------------------------------------------------------- /types/fn/sync.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./index.js"; 2 | /** 3 | * Invokes asynchronously a function when any of its internal signals or computed values change. 4 | * 5 | * Returns a dispose callback. 6 | * @template T 7 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 8 | */ 9 | export const effect: (fn: (v?: T) => T, value?: T) => () => void; 10 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | export function batch(callback: () => void): void; 2 | /** 3 | * A signal with a value property also exposed via toJSON, toString and valueOf. 4 | * When created via computed, the `value` property is **readonly**. 5 | * @template T 6 | */ 7 | export class Signal { 8 | constructor(value: any); 9 | _: any; 10 | /** @returns {T} */ 11 | toJSON(): T; 12 | /** @returns {string} */ 13 | toString(): string; 14 | /** @returns {T} */ 15 | valueOf(): T; 16 | } 17 | /** 18 | * Returns a read-only Signal that is invoked only when any of the internally 19 | * used signals, as in within the callback, is unknown or updated. 20 | * @type {(fn: (v: T) => R, value?: V, options?: { equals?: Equals }) => ComputedSignal} 21 | */ 22 | export const computed: (fn: (v: T) => R, value?: V, options?: { 23 | equals?: Equals; 24 | }) => ComputedSignal; 25 | export class FX extends Computed { 26 | constructor(_: any, v: any, o: any); 27 | e: any[]; 28 | run(): FX; 29 | stop(): void; 30 | } 31 | export class Effect extends FX { 32 | i: number; 33 | a: boolean; 34 | m: boolean; 35 | get value(): void; 36 | async(): void; 37 | sync(): void; 38 | } 39 | /** 40 | * Invokes a function when any of its internal signals or computed values change. 41 | * 42 | * Returns a dispose callback. 43 | * @template T 44 | * @type {(fn: (v: T) => T, value?: T, options?: { async?: boolean }) => () => void} 45 | */ 46 | export const effect: (fn: (v: T) => T, value?: T, options?: { 47 | async?: boolean; 48 | }) => () => void; 49 | /** 50 | * Returns a writable Signal that side-effects whenever its value gets updated. 51 | * @template T 52 | * @type {(initialValue: T, options?: { equals?: Equals }) => ReactiveSignal} 53 | */ 54 | export const signal: (initialValue: T, options?: { 55 | equals?: Equals; 56 | }) => ReactiveSignal; 57 | export type Equals = boolean | ((prev: T, next: T) => boolean); 58 | /** 59 | * 60 | */ 61 | export type ReactiveSignal = Omit, '_' | 's' | 'c'>; 62 | /** 63 | * 64 | */ 65 | export type ComputedSignal = Omit, '$' | 's' | 'f' | 'r' | '_'>; 66 | /** 67 | * @template T 68 | * @extends {Signal} 69 | */ 70 | declare class Computed extends Signal { 71 | /** 72 | * @param {(v: T) => T} _ 73 | * @param {T} v 74 | * @param {{ equals?: Equals }} o 75 | * @param {boolean} f 76 | */ 77 | constructor(_: (v: T) => T, v: T, o: { 78 | equals?: Equals; 79 | }, f: boolean); 80 | /** 81 | * @private 82 | * @type{Reactive} 83 | */ 84 | private s; 85 | f: boolean; 86 | $: boolean; 87 | r: Set; 88 | refresh(): void; 89 | peek(): T; 90 | get value(): T; 91 | } 92 | /** 93 | * @template T 94 | * @extends {Signal} 95 | */ 96 | declare class Reactive extends Signal { 97 | constructor(_: any, { equals }: { 98 | equals: any; 99 | }); 100 | c: Set; 101 | s: any; 102 | /** 103 | * Allows to get signal.value without subscribing to updates in an effect 104 | * @returns {T} 105 | */ 106 | peek(): T; 107 | set value(arg: T); 108 | /** @returns {T} */ 109 | get value(): T; 110 | } 111 | export {}; 112 | -------------------------------------------------------------------------------- /types/solid/async.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./index.js"; 2 | /** 3 | * asynchronous https://www.solidjs.com/docs/latest/api#createeffect 4 | * 5 | * returns a dispose callback. 6 | * @template T 7 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 8 | */ 9 | export const createEffect: (fn: (v?: T) => T, value?: T) => () => void; 10 | -------------------------------------------------------------------------------- /types/solid/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * https://www.solidjs.com/docs/latest/api#createeffect 3 | * 4 | * returns a dispose callback. 5 | * @template T 6 | * @type {(fn: (v: T) => T, value?: T, options?: { async?: boolean }) => () => void} 7 | */ 8 | export const createEffect: (fn: (v: T_1) => T_1, value?: T_1, options?: { 9 | async?: boolean; 10 | }) => () => void; 11 | /** 12 | * @template T 13 | * @type {(fn: (v: T) => T, value?: T, options?: { equals?: boolean | ((prev: T, next: T) => boolean) }) => () => T} 14 | */ 15 | export const createMemo: (fn: (v: T_1) => T_1, value?: T_1, options?: { 16 | equals?: boolean | ((prev: T_1, next: T_1) => boolean); 17 | }) => () => T_1; 18 | /** 19 | * @template T 20 | * @type {(initialValue: T, options?: { equals?: boolean | ((prev: T, next: T) => boolean) }) => [get: () => T, set: (v: T) => T]} 21 | */ 22 | export const createSignal: (initialValue: T_1, options?: { 23 | equals?: boolean | ((prev: T_1, next: T_1) => boolean); 24 | }) => [get: () => T_1, set: (v: T_1) => T_1]; 25 | -------------------------------------------------------------------------------- /types/solid/sync.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./index.js"; 2 | /** 3 | * synchronous https://www.solidjs.com/docs/latest/api#createeffect 4 | * 5 | * returns a dispose callback. 6 | * @template T 7 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 8 | */ 9 | export const createEffect: (fn: (v?: T) => T, value?: T) => () => void; 10 | -------------------------------------------------------------------------------- /types/sync.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./index.js"; 2 | /** 3 | * Invokes synchronously a function when any of its internal signals or computed values change. 4 | * 5 | * Returns a dispose callback. 6 | * @template T 7 | * @type {(fn: (v?: T) => T?, value?: T) => () => void 0} 8 | */ 9 | export const effect: (fn: (v?: T) => T, value?: T) => () => void; 10 | --------------------------------------------------------------------------------