pax_global_header00006660000000000000000000000064145756360460014532gustar00rootroot0000000000000052 comment=3317c65e351ffeb3afc3967d48cd55e5188a749e lib0-0.2.93/000077500000000000000000000000001457563604600124535ustar00rootroot00000000000000lib0-0.2.93/.github/000077500000000000000000000000001457563604600140135ustar00rootroot00000000000000lib0-0.2.93/.github/workflows/000077500000000000000000000000001457563604600160505ustar00rootroot00000000000000lib0-0.2.93/.github/workflows/node.js.yml000066400000000000000000000012701457563604600201330ustar00rootroot00000000000000# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions name: Testing Lib0 on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: [16.x, 18.x] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - run: npm ci - run: npm test lib0-0.2.93/.gitignore000066400000000000000000000001171457563604600144420ustar00rootroot00000000000000dist .nyc_output coverage node_modules *.d.ts *.d.ts.map */*.d.ts */*.d.ts.map lib0-0.2.93/.jsdoc.json000066400000000000000000000004741457563604600145330ustar00rootroot00000000000000{ "plugins": [ "plugins/markdown", "jsdoc-plugin-typescript" ], "recurseDepth": 10, "source": { "excludePattern": "/\\.test\\.js/" }, "sourceType": "module", "tags": { "allowUnknownTags": true, "dictionaries": ["jsdoc", "closure"] }, "typescript": { "moduleRoot": "./" } }lib0-0.2.93/.npmignore000066400000000000000000000001371457563604600144530ustar00rootroot00000000000000*.test.js dist/test.* tsconfig.json rollup.config.js .nyc_output .travis.yml .git node_modules lib0-0.2.93/.travis.yml000066400000000000000000000000421457563604600145600ustar00rootroot00000000000000language: node_js node_js: - 14 lib0-0.2.93/.vscode/000077500000000000000000000000001457563604600140145ustar00rootroot00000000000000lib0-0.2.93/.vscode/launch.json000066400000000000000000000007001457563604600161560ustar00rootroot00000000000000{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Gen Docs", "program": "${workspaceFolder}/bin/gendocs.js", "skipFiles": [ "/**" ], } ] } lib0-0.2.93/LICENSE000066400000000000000000000021241457563604600134570ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2019 Kevin Jahns . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. lib0-0.2.93/README.md000066400000000000000000002231401457563604600137340ustar00rootroot00000000000000 # Lib0 [![Build Status](https://travis-ci.com/dmonad/lib0.svg?branch=main)](https://travis-ci.com/dmonad/lib0) > Monorepo of isomorphic utility functions This library is meant to replace all global JavaScript functions with isomorphic module imports. Additionally, it implements several performance-oriented utility modules. Most noteworthy are the binary encoding/decoding modules **[lib0/encoding]** / **[lib0/decoding]**, the randomized testing framework **[lib0/testing]**, the fast Pseudo Random Number Generator **[lib0/PRNG]**, the small socket.io alternative **[lib0/websocket]**, and the logging module **[lib0/logging]** that allows colorized logging in all environments. Lib0 has only one dependency, which is also from the author of lib0. If lib0 is transpiled with rollup or webpack, very little code is produced because of the way that it is written. All exports are pure and are removed by transpilers that support dead code elimination. Here is an example of how dead code elemination and mangling optimizes code from lib0: ```js // How the code is optimized by transpilers: // lib0/json.js export const stringify = JSON.stringify export const parse = JSON.parse // index.js import * as json from 'lib0/json' export const f = (arg1, arg2) => json.stringify(arg1) + json.stringify(arg2) // compiled with rollup and uglifyjs: const s=JSON.stringify,f=(a,b)=>s(a)+s(b) export {f} ``` ## Performance resources Each function in this library is tested thoroughly and is not deoptimized by v8 (except some logging and comparison functions that can't be implemented without deoptimizations). This library implements its own test suite that is designed for randomized testing and inspecting performance issues. * `node --trace-deopt` and `node --trace-opt` * https://youtu.be/IFWulQnM5E0 Good intro video * https://github.com/thlorenz/v8-perf * https://github.com/thlorenz/deoptigate - A great tool for investigating deoptimizations * https://github.com/vhf/v8-bailout-reasons - Description of some deopt messages ## Code style The code style might be a bit different from what you are used to. Stay open. Most of the design choices have been thought through. The purpose of this code style is to create code that is optimized by the compiler and that results in small code bundles when used with common module bundlers. Keep that in mind when reading the library. * No polymorphism! * Modules should only export pure functions and constants. This way the module bundler can eliminate dead code. The statement `const x = someCondition ? A : B` cannot be eleminated, because it is tied to a condition. * Use Classes for structuring data. Classes are well supported by jsdoc and are immediately optimized by the compiler. I.e. prefer `class Coord { constructor (x, y) { this.x = x; this.y = y} }` instead of `{ x: x, y: y }`, because the compiler needs to be assured that the order of properties does not change. `{ y: y, x: x }` has a different hidden class than `{ x: x, y: y }`, which will lead to code deoptimizations if their use is alternated. * The user of your module should never create data objects with the `new` keyword. Prefer exporting factory functions like `const createCoordinate = (x, y) => new Coord(x, y)`. * The use of class methods is discouraged, because method names can't be mangled or removed by dead code elimination. * The only acceptable use of methods is when two objects implement functionality differently. E.g. `class Duck { eat () { swallow() } }` and `class Cow { eat () { chew() } }` have the same signature, but implement it differently. * Prefer `const` variable declarations. Use `let` only in loops. `const` always leads to easier code. * Keep the potential execution stack small and compartmentalized. Nobody wants to debug spaghetti code. * Give proper names to your functions and ask yourself if you would know what the function does if you saw it in the execution stack. * Avoid recursion. There is a stack limit in most browsers and not every recursive function is optimized by the compiler. * Semicolons are superfluous. Lint with https://standardjs.com/ ## Using lib0 `lib0` contains isomorphic modules that work in nodejs, the browser, and other environments. It exports modules as the `commonjs` and the new `esm module` format. If possible, **ESM module** ```js import module from 'lib0/[module]' // automatically resolves to lib0/[module].js ``` **CommonJS** ```js require('lib0/[module]') // automatically resolves to lib0/dist/[module].cjs ``` **Manual** Automatically resolving to `commonjs` and `esm modules` is implemented using *conditional exports* which is available in `node>=v12`. If support for older versions is required, then it is recommended to define the location of the module manually: ```js import module from 'lib0/[module].js' // require('lib0/dist/[module].cjs') ``` ## Modules
[lib0/array] Utility module to work with Arrays.
import * as array from 'lib0/array'
array.last(arr: ArrayLike<L>): L

Return the last element of an array. The element must exist

array.create(): Array<C>
array.copy(a: Array<D>): Array<D>
array.appendTo(dest: Array<M>, src: Array<M>)

Append elements from src to dest

array.from(arraylike: ArrayLike<T>|Iterable<T>): T

Transforms something array-like to an actual Array.

array.every(arr: ARR, f: function(ITEM, number, ARR):boolean): boolean

True iff condition holds on every element in the Array.

array.some(arr: ARR, f: function(S, number, ARR):boolean): boolean

True iff condition holds on some element in the Array.

array.equalFlat(a: ArrayLike<ELEM>, b: ArrayLike<ELEM>): boolean
array.flatten(arr: Array<Array<ELEM>>): Array<ELEM>
array.isArray
array.unique(arr: Array<T>): Array<T>
array.uniqueBy(arr: ArrayLike<T>, mapper: function(T):M): Array<T>
[lib0/binary] Binary data constants.
import * as binary from 'lib0/binary'
binary.BIT1: number

n-th bit activated.

binary.BIT2
binary.BIT3
binary.BIT4
binary.BIT5
binary.BIT6
binary.BIT7
binary.BIT8
binary.BIT9
binary.BIT10
binary.BIT11
binary.BIT12
binary.BIT13
binary.BIT14
binary.BIT15
binary.BIT16
binary.BIT17
binary.BIT18
binary.BIT19
binary.BIT20
binary.BIT21
binary.BIT22
binary.BIT23
binary.BIT24
binary.BIT25
binary.BIT26
binary.BIT27
binary.BIT28
binary.BIT29
binary.BIT30
binary.BIT31
binary.BIT32
binary.BITS0: number

First n bits activated.

binary.BITS1
binary.BITS2
binary.BITS3
binary.BITS4
binary.BITS5
binary.BITS6
binary.BITS7
binary.BITS8
binary.BITS9
binary.BITS10
binary.BITS11
binary.BITS12
binary.BITS13
binary.BITS14
binary.BITS15
binary.BITS16
binary.BITS17
binary.BITS18
binary.BITS19
binary.BITS20
binary.BITS21
binary.BITS22
binary.BITS23
binary.BITS24
binary.BITS25
binary.BITS26
binary.BITS27
binary.BITS28
binary.BITS29
binary.BITS30
binary.BITS31: number
binary.BITS32: number
[lib0/broadcastchannel] Helpers for cross-tab communication using broadcastchannel with LocalStorage fallback.
import * as broadcastchannel from 'lib0/broadcastchannel'
// In browser window A:
broadcastchannel.subscribe('my events', data => console.log(data))
broadcastchannel.publish('my events', 'Hello world!') // => A: 'Hello world!' fires synchronously in same tab

// In browser window B:
broadcastchannel.publish('my events', 'hello from tab B') // => A: 'hello from tab B'
broadcastchannel.subscribe(room: string, f: function(any, any):any)

Subscribe to global publish events.

broadcastchannel.unsubscribe(room: string, f: function(any, any):any)

Unsubscribe from publish global events.

broadcastchannel.publish(room: string, data: any, origin: any)

Publish data to all subscribers (including subscribers on this tab)

[lib0/buffer] Utility functions to work with buffers (Uint8Array).
import * as buffer from 'lib0/buffer'
buffer.createUint8ArrayFromLen(len: number)
buffer.createUint8ArrayViewFromArrayBuffer(buffer: ArrayBuffer, byteOffset: number, length: number)

Create Uint8Array with initial content from buffer

buffer.createUint8ArrayFromArrayBuffer(buffer: ArrayBuffer)

Create Uint8Array with initial content from buffer

buffer.toBase64
buffer.fromBase64
buffer.copyUint8Array(uint8Array: Uint8Array): Uint8Array

Copy the content of an Uint8Array view to a new ArrayBuffer.

buffer.encodeAny(data: any): Uint8Array

Encode anything as a UInt8Array. It's a pun on typescripts's any type. See encoding.writeAny for more information.

buffer.decodeAny(buf: Uint8Array): any

Decode an any-encoded value.

[lib0/cache] An implementation of a map which has keys that expire.
import * as cache from 'lib0/cache'
new cache.Cache(timeout: number)
cache.removeStale(cache: module:cache.Cache<K, V>): number
cache.set(cache: module:cache.Cache<K, V>, key: K, value: V)
cache.get(cache: module:cache.Cache<K, V>, key: K): V | undefined
cache.refreshTimeout(cache: module:cache.Cache<K, V>, key: K)
cache.getAsync(cache: module:cache.Cache<K, V>, key: K): V | Promise<V> | undefined

Works well in conjunktion with setIfUndefined which has an async init function. Using getAsync & setIfUndefined ensures that the init function is only called once.

cache.remove(cache: module:cache.Cache<K, V>, key: K)
cache.setIfUndefined(cache: module:cache.Cache<K, V>, key: K, init: function():Promise<V>, removeNull: boolean): Promise<V> | V
cache.create(timeout: number)
[lib0/component] Web components.
import * as component from 'lib0/component'
component.registry: CustomElementRegistry
component.define(name: string, constr: any, opts: ElementDefinitionOptions)
component.whenDefined(name: string): Promise<CustomElementConstructor>
new component.Lib0Component(state: S)
component.Lib0Component#state: S|null
component.Lib0Component#setState(state: S, forceStateUpdate: boolean)
component.Lib0Component#updateState(stateUpdate: any)
component.createComponent(name: string, cnf: module:component~CONF<T>): Class<module:component.Lib0Component>
component.createComponentDefiner(definer: function)
component.defineListComponent
component.defineLazyLoadingComponent
[lib0/conditions] Often used conditions.
import * as conditions from 'lib0/conditions'
conditions.undefinedToNull
[lib0/crypto]
import * as crypto from 'lib0/crypto'
y(data: string | Uint8Array): Uint8Array
ymmetricKey(secret: string | Uint8Array, salt: string | Uint8Array, opts: Object, opts.extractable: boolean, opts.usages: Array<'sign'|'verify'|'encrypt'|'decrypt'>): PromiseLike<CryptoKey>
ymmetricKey()
eAsymmetricKey(opts: Object, opts.extractable: boolean, opts.usages: Array<'sign'|'verify'|'encrypt'|'decrypt'>)
eAsymmetricKey()
ey(key: CryptoKey)
ey()
ymmetricKey(jwk: any, opts: Object, opts.extractable: boolean, opts.usages: Array<'sign'|'verify'|'encrypt'|'decrypt'>)
ymmetricKey()
symmetricKey(jwk: any, opts: Object, opts.extractable: boolean, opts.usages: Array<'sign'|'verify'|'encrypt'|'decrypt'>)
symmetricKey()
(data: Uint8Array, key: CryptoKey): PromiseLike<Uint8Array>
()
(data: Uint8Array, key: CryptoKey): PromiseLike<Uint8Array>
()
(data: Uint8Array, privateKey: CryptoKey): PromiseLike<Uint8Array>
()
(signature: Uint8Array, data: Uint8Array, publicKey: CryptoKey): PromiseLike<boolean>
()
[lib0/decoding] Efficient schema-less binary decoding with support for variable length encoding.
import * as decoding from 'lib0/decoding'

Use [lib0/decoding] with [lib0/encoding]. Every encoding function has a corresponding decoding function.

Encodes numbers in little-endian order (least to most significant byte order) and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/) which is also used in Protocol Buffers.

// encoding step
const encoder = encoding.createEncoder()
encoding.writeVarUint(encoder, 256)
encoding.writeVarString(encoder, 'Hello world!')
const buf = encoding.toUint8Array(encoder)
// decoding step
const decoder = decoding.createDecoder(buf)
decoding.readVarUint(decoder) // => 256
decoding.readVarString(decoder) // => 'Hello world!'
decoding.hasContent(decoder) // => false - all data is read
new decoding.Decoder(uint8Array: Uint8Array)

A Decoder handles the decoding of an Uint8Array.

decoding.Decoder#arr: Uint8Array

Decoding target.

decoding.Decoder#pos: number

Current decoding position.

decoding.createDecoder(uint8Array: Uint8Array): module:decoding.Decoder
decoding.hasContent(decoder: module:decoding.Decoder): boolean
decoding.clone(decoder: module:decoding.Decoder, newPos: number): module:decoding.Decoder

Clone a decoder instance. Optionally set a new position parameter.

decoding.readUint8Array(decoder: module:decoding.Decoder, len: number): Uint8Array

Create an Uint8Array view of the next len bytes and advance the position by len.

Important: The Uint8Array still points to the underlying ArrayBuffer. Make sure to discard the result as soon as possible to prevent any memory leaks. Use buffer.copyUint8Array to copy the result into a new Uint8Array.

decoding.readVarUint8Array(decoder: module:decoding.Decoder): Uint8Array

Read variable length Uint8Array.

Important: The Uint8Array still points to the underlying ArrayBuffer. Make sure to discard the result as soon as possible to prevent any memory leaks. Use buffer.copyUint8Array to copy the result into a new Uint8Array.

decoding.readTailAsUint8Array(decoder: module:decoding.Decoder): Uint8Array

Read the rest of the content as an ArrayBuffer

decoding.skip8(decoder: module:decoding.Decoder): number

Skip one byte, jump to the next position.

decoding.readUint8(decoder: module:decoding.Decoder): number

Read one byte as unsigned integer.

decoding.readUint16(decoder: module:decoding.Decoder): number

Read 2 bytes as unsigned integer.

decoding.readUint32(decoder: module:decoding.Decoder): number

Read 4 bytes as unsigned integer.

decoding.readUint32BigEndian(decoder: module:decoding.Decoder): number

Read 4 bytes as unsigned integer in big endian order. (most significant byte first)

decoding.peekUint8(decoder: module:decoding.Decoder): number

Look ahead without incrementing the position to the next byte and read it as unsigned integer.

decoding.peekUint16(decoder: module:decoding.Decoder): number

Look ahead without incrementing the position to the next byte and read it as unsigned integer.

decoding.peekUint32(decoder: module:decoding.Decoder): number

Look ahead without incrementing the position to the next byte and read it as unsigned integer.

decoding.readVarUint(decoder: module:decoding.Decoder): number

Read unsigned integer (32bit) with variable length. 1/8th of the storage is used as encoding overhead.

  • numbers < 2^7 is stored in one bytlength
  • numbers < 2^14 is stored in two bylength
decoding.readVarInt(decoder: module:decoding.Decoder): number

Read signed integer (32bit) with variable length. 1/8th of the storage is used as encoding overhead.

  • numbers < 2^7 is stored in one bytlength
  • numbers < 2^14 is stored in two bylength
decoding.peekVarUint(decoder: module:decoding.Decoder): number

Look ahead and read varUint without incrementing position

decoding.peekVarInt(decoder: module:decoding.Decoder): number

Look ahead and read varUint without incrementing position

decoding.readVarString
decoding.peekVarString(decoder: module:decoding.Decoder): string

Look ahead and read varString without incrementing position

decoding.readFromDataView(decoder: module:decoding.Decoder, len: number): DataView
decoding.readFloat32(decoder: module:decoding.Decoder)
decoding.readFloat64(decoder: module:decoding.Decoder)
decoding.readBigInt64(decoder: module:decoding.Decoder)
decoding.readBigUint64(decoder: module:decoding.Decoder)
decoding.readAny(decoder: module:decoding.Decoder)
new decoding.RleDecoder(uint8Array: Uint8Array, reader: function(module:decoding.Decoder):T)

T must not be null.

decoding.RleDecoder#s: T|null

Current state

decoding.RleDecoder#read()
decoding.RleDecoder#s: T
new decoding.IntDiffDecoder(uint8Array: Uint8Array, start: number)
decoding.IntDiffDecoder#s: number

Current state

decoding.IntDiffDecoder#read(): number
new decoding.RleIntDiffDecoder(uint8Array: Uint8Array, start: number)
decoding.RleIntDiffDecoder#s: number

Current state

decoding.RleIntDiffDecoder#read(): number
decoding.RleIntDiffDecoder#s: number
new decoding.UintOptRleDecoder(uint8Array: Uint8Array)
decoding.UintOptRleDecoder#s: number
decoding.UintOptRleDecoder#read()
decoding.UintOptRleDecoder#s: number
new decoding.IncUintOptRleDecoder(uint8Array: Uint8Array)
decoding.IncUintOptRleDecoder#s: number
decoding.IncUintOptRleDecoder#read()
new decoding.IntDiffOptRleDecoder(uint8Array: Uint8Array)
decoding.IntDiffOptRleDecoder#s: number
decoding.IntDiffOptRleDecoder#read(): number
new decoding.StringDecoder(uint8Array: Uint8Array)
decoding.StringDecoder#spos: number
decoding.StringDecoder#read(): string
decoding.RleDecoder#arr: Uint8Array

Decoding target.

decoding.RleDecoder#pos: number

Current decoding position.

decoding.IntDiffDecoder#arr: Uint8Array

Decoding target.

decoding.IntDiffDecoder#pos: number

Current decoding position.

decoding.RleIntDiffDecoder#arr: Uint8Array

Decoding target.

decoding.RleIntDiffDecoder#pos: number

Current decoding position.

decoding.UintOptRleDecoder#arr: Uint8Array

Decoding target.

decoding.UintOptRleDecoder#pos: number

Current decoding position.

decoding.IncUintOptRleDecoder#arr: Uint8Array

Decoding target.

decoding.IncUintOptRleDecoder#pos: number

Current decoding position.

decoding.IntDiffOptRleDecoder#arr: Uint8Array

Decoding target.

decoding.IntDiffOptRleDecoder#pos: number

Current decoding position.

[lib0/diff] Efficient diffs.
import * as diff from 'lib0/diff'
diff.simpleDiffString(a: string, b: string): module:diff~SimpleDiff<string>

Create a diff between two strings. This diff implementation is highly efficient, but not very sophisticated.

diff.simpleDiff
diff.simpleDiffArray(a: Array<T>, b: Array<T>, compare: function(T, T):boolean): module:diff~SimpleDiff<Array<T>>

Create a diff between two arrays. This diff implementation is highly efficient, but not very sophisticated.

Note: This is basically the same function as above. Another function was created so that the runtime can better optimize these function calls.

diff.simpleDiffStringWithCursor(a: string, b: string, cursor: number)

Diff text and try to diff at the current cursor position.

[lib0/dom] Utility module to work with the DOM.
import * as dom from 'lib0/dom'
dom.doc: Document
dom.createElement
dom.createDocumentFragment
dom.createTextNode
dom.domParser
dom.emitCustomEvent
dom.setAttributes
dom.setAttributesMap
dom.fragment
dom.append
dom.remove
dom.addEventListener
dom.removeEventListener
dom.addEventListeners
dom.removeEventListeners
dom.element
dom.canvas
dom.text
dom.pairToStyleString
dom.pairsToStyleString
dom.mapToStyleString
dom.querySelector
dom.querySelectorAll
dom.getElementById
dom.parseFragment
dom.childNodes: any
dom.parseElement
dom.replaceWith
dom.insertBefore
dom.appendChild
dom.ELEMENT_NODE
dom.TEXT_NODE
dom.CDATA_SECTION_NODE
dom.COMMENT_NODE
dom.DOCUMENT_NODE
dom.DOCUMENT_TYPE_NODE
dom.DOCUMENT_FRAGMENT_NODE
dom.checkNodeType(node: any, type: number)
dom.isParentOf(parent: Node, child: HTMLElement)
[lib0/encoding] Efficient schema-less binary encoding with support for variable length encoding.
import * as encoding from 'lib0/encoding'

Use [lib0/encoding] with [lib0/decoding]. Every encoding function has a corresponding decoding function.

Encodes numbers in little-endian order (least to most significant byte order) and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/) which is also used in Protocol Buffers.

// encoding step
const encoder = encoding.createEncoder()
encoding.writeVarUint(encoder, 256)
encoding.writeVarString(encoder, 'Hello world!')
const buf = encoding.toUint8Array(encoder)
// decoding step
const decoder = decoding.createDecoder(buf)
decoding.readVarUint(decoder) // => 256
decoding.readVarString(decoder) // => 'Hello world!'
decoding.hasContent(decoder) // => false - all data is read
new encoding.Encoder()

A BinaryEncoder handles the encoding to an Uint8Array.

encoding.Encoder#bufs: Array<Uint8Array>
encoding.createEncoder(): module:encoding.Encoder
encoding.length(encoder: module:encoding.Encoder): number

The current length of the encoded data.

encoding.toUint8Array(encoder: module:encoding.Encoder): Uint8Array

Transform to Uint8Array.

encoding.verifyLen(encoder: module:encoding.Encoder, len: number)

Verify that it is possible to write len bytes wtihout checking. If necessary, a new Buffer with the required length is attached.

encoding.write(encoder: module:encoding.Encoder, num: number)

Write one byte to the encoder.

encoding.set(encoder: module:encoding.Encoder, pos: number, num: number)

Write one byte at a specific position. Position must already be written (i.e. encoder.length > pos)

encoding.writeUint8(encoder: module:encoding.Encoder, num: number)

Write one byte as an unsigned integer.

encoding.setUint8(encoder: module:encoding.Encoder, pos: number, num: number)

Write one byte as an unsigned Integer at a specific location.

encoding.writeUint16(encoder: module:encoding.Encoder, num: number)

Write two bytes as an unsigned integer.

encoding.setUint16(encoder: module:encoding.Encoder, pos: number, num: number)

Write two bytes as an unsigned integer at a specific location.

encoding.writeUint32(encoder: module:encoding.Encoder, num: number)

Write two bytes as an unsigned integer

encoding.writeUint32BigEndian(encoder: module:encoding.Encoder, num: number)

Write two bytes as an unsigned integer in big endian order. (most significant byte first)

encoding.setUint32(encoder: module:encoding.Encoder, pos: number, num: number)

Write two bytes as an unsigned integer at a specific location.

encoding.writeVarUint(encoder: module:encoding.Encoder, num: number)

Write a variable length unsigned integer. Max encodable integer is 2^53.

encoding.writeVarInt(encoder: module:encoding.Encoder, num: number)

Write a variable length integer.

We use the 7th bit instead for signaling that this is a negative number.

encoding.writeVarString
encoding.writeBinaryEncoder(encoder: module:encoding.Encoder, append: module:encoding.Encoder)

Write the content of another Encoder.

encoding.writeUint8Array(encoder: module:encoding.Encoder, uint8Array: Uint8Array)

Append fixed-length Uint8Array to the encoder.

encoding.writeVarUint8Array(encoder: module:encoding.Encoder, uint8Array: Uint8Array)

Append an Uint8Array to Encoder.

encoding.writeOnDataView(encoder: module:encoding.Encoder, len: number): DataView

Create an DataView of the next len bytes. Use it to write data after calling this function.

// write float32 using DataView
const dv = writeOnDataView(encoder, 4)
dv.setFloat32(0, 1.1)
// read float32 using DataView
const dv = readFromDataView(encoder, 4)
dv.getFloat32(0) // => 1.100000023841858 (leaving it to the reader to find out why this is the correct result)
encoding.writeFloat32(encoder: module:encoding.Encoder, num: number)
encoding.writeFloat64(encoder: module:encoding.Encoder, num: number)
encoding.writeBigInt64(encoder: module:encoding.Encoder, num: bigint)
encoding.writeBigUint64(encoder: module:encoding.Encoder, num: bigint)
encoding.writeAny(encoder: module:encoding.Encoder, data: undefined|null|number|bigint|boolean|string|Object<string,any>|Array<any>|Uint8Array)

Encode data with efficient binary format.

Differences to JSON: • Transforms data to a binary format (not to a string) • Encodes undefined, NaN, and ArrayBuffer (these can't be represented in JSON) • Numbers are efficiently encoded either as a variable length integer, as a 32 bit float, as a 64 bit float, or as a 64 bit bigint.

Encoding table:

Data Type Prefix Encoding Method Comment
undefined 127 Functions, symbol, and everything that cannot be identified is encoded as undefined
null 126
integer 125 writeVarInt Only encodes 32 bit signed integers
float32 124 writeFloat32
float64 123 writeFloat64
bigint 122 writeBigInt64
boolean (false) 121 True and false are different data types so we save the following byte
boolean (true) 120 - 0b01111000 so the last bit determines whether true or false
string 119 writeVarString
object<string,any> 118 custom Writes {length} then {length} key-value pairs
array 117 custom Writes {length} then {length} json values
Uint8Array 116 writeVarUint8Array We use Uint8Array for any kind of binary data

Reasons for the decreasing prefix: We need the first bit for extendability (later we may want to encode the prefix with writeVarUint). The remaining 7 bits are divided as follows: [0-30] the beginning of the data range is used for custom purposes (defined by the function that uses this library) [31-127] the end of the data range is used for data encoding by lib0/encoding.js

new encoding.RleEncoder(writer: function(module:encoding.Encoder, T):void)

Now come a few stateful encoder that have their own classes.

encoding.RleEncoder#s: T|null

Current state

encoding.RleEncoder#write(v: T)
new encoding.IntDiffEncoder(start: number)

Basic diff decoder using variable length encoding.

Encodes the values [3, 1100, 1101, 1050, 0] to [3, 1097, 1, -51, -1050] using writeVarInt.

encoding.IntDiffEncoder#s: number

Current state

encoding.IntDiffEncoder#write(v: number)
new encoding.RleIntDiffEncoder(start: number)

A combination of IntDiffEncoder and RleEncoder.

Basically first writes the IntDiffEncoder and then counts duplicate diffs using RleEncoding.

Encodes the values [1,1,1,2,3,4,5,6] as [1,1,0,2,1,5] (RLE([1,0,0,1,1,1,1,1]) ⇒ RleIntDiff[1,1,0,2,1,5])

encoding.RleIntDiffEncoder#s: number

Current state

encoding.RleIntDiffEncoder#write(v: number)
new encoding.UintOptRleEncoder()

Optimized Rle encoder that does not suffer from the mentioned problem of the basic Rle encoder.

Internally uses VarInt encoder to write unsigned integers. If the input occurs multiple times, we write write it as a negative number. The UintOptRleDecoder then understands that it needs to read a count.

Encodes [1,2,3,3,3] as [1,2,-3,3] (once 1, once 2, three times 3)

encoding.UintOptRleEncoder#s: number
encoding.UintOptRleEncoder#write(v: number)
encoding.UintOptRleEncoder#toUint8Array()
new encoding.IncUintOptRleEncoder()

Increasing Uint Optimized RLE Encoder

The RLE encoder counts the number of same occurences of the same value. The IncUintOptRle encoder counts if the value increases. I.e. 7, 8, 9, 10 will be encoded as [-7, 4]. 1, 3, 5 will be encoded as [1, 3, 5].

encoding.IncUintOptRleEncoder#s: number
encoding.IncUintOptRleEncoder#write(v: number)
encoding.IncUintOptRleEncoder#toUint8Array()
new encoding.IntDiffOptRleEncoder()

A combination of the IntDiffEncoder and the UintOptRleEncoder.

The count approach is similar to the UintDiffOptRleEncoder, but instead of using the negative bitflag, it encodes in the LSB whether a count is to be read. Therefore this Encoder only supports 31 bit integers!

Encodes [1, 2, 3, 2] as [3, 1, 6, -1] (more specifically [(1 << 1) | 1, (3 << 0) | 0, -1])

Internally uses variable length encoding. Contrary to normal UintVar encoding, the first byte contains:

  • 1 bit that denotes whether the next value is a count (LSB)
  • 1 bit that denotes whether this value is negative (MSB - 1)
  • 1 bit that denotes whether to continue reading the variable length integer (MSB)

Therefore, only five bits remain to encode diff ranges.

Use this Encoder only when appropriate. In most cases, this is probably a bad idea.

encoding.IntDiffOptRleEncoder#s: number
encoding.IntDiffOptRleEncoder#write(v: number)
encoding.IntDiffOptRleEncoder#toUint8Array()
new encoding.StringEncoder()

Optimized String Encoder.

Encoding many small strings in a simple Encoder is not very efficient. The function call to decode a string takes some time and creates references that must be eventually deleted. In practice, when decoding several million small strings, the GC will kick in more and more often to collect orphaned string objects (or maybe there is another reason?).

This string encoder solves the above problem. All strings are concatenated and written as a single string using a single encoding call.

The lengths are encoded using a UintOptRleEncoder.

encoding.StringEncoder#sarr: Array<string>
encoding.StringEncoder#write(string: string)
encoding.StringEncoder#toUint8Array()
encoding.RleEncoder#bufs: Array<Uint8Array>
encoding.IntDiffEncoder#bufs: Array<Uint8Array>
encoding.RleIntDiffEncoder#bufs: Array<Uint8Array>
[lib0/map] Isomorphic module to work access the environment (query params, env variables).
import * as map from 'lib0/environment'
map.isNode
map.isBrowser
map.isMac
map.hasParam
map.getParam
map.getVariable
map.getConf(name: string): string|null
map.hasConf
map.production
map.supportsColor
[lib0/error] Error helpers.
import * as error from 'lib0/error'
error.create(s: string): Error
error.methodUnimplemented(): never
error.unexpectedCase(): never
[lib0/eventloop] Utility module to work with EcmaScript's event loop.
import * as eventloop from 'lib0/eventloop'
eventloop.enqueue(f: function():void)
eventloop#destroy()
eventloop.timeout(timeout: number, callback: function): module:eventloop~TimeoutObject
eventloop.interval(timeout: number, callback: function): module:eventloop~TimeoutObject
eventloop.Animation
eventloop.animationFrame(cb: function(number):void): module:eventloop~TimeoutObject
eventloop.idleCallback(cb: function): module:eventloop~TimeoutObject

Note: this is experimental and is probably only useful in browsers.

eventloop.createDebouncer(timeout: number): function(function():void):void
[lib0/function] Common functions and function call helpers.
import * as function from 'lib0/function'
function.callAll(fs: Array<function>, args: Array<any>)

Calls all functions in fs with args. Only throws after all functions were called.

function.nop
function.apply(f: function():T): T
function.id(a: A): A
function.equalityStrict(a: T, b: T): boolean
function.equalityFlat(a: Array<T>|object, b: Array<T>|object): boolean
function.equalityDeep(a: any, b: any): boolean
function.isOneOf(value: V, options: Array<OPTS>)
[lib0/lib0] Experimental method to import lib0.
import * as lib0 from 'lib0/index'

Not recommended if the module bundler doesn't support dead code elimination.

[lib0/indexeddb] Helpers to work with IndexedDB.
import * as indexeddb from 'lib0/indexeddb'
indexeddb.rtop(request: IDBRequest): Promise<any>

IDB Request to Promise transformer

indexeddb.openDB(name: string, initDB: function(IDBDatabase):any): Promise<IDBDatabase>
indexeddb.deleteDB(name: string)
indexeddb.createStores(db: IDBDatabase, definitions: Array<Array<string>|Array<string|IDBObjectStoreParameters|undefined>>)
indexeddb.transact(db: IDBDatabase, stores: Array<string>, access: "readwrite"|"readonly"): Array<IDBObjectStore>
indexeddb.count(store: IDBObjectStore, range: IDBKeyRange): Promise<number>
indexeddb.get(store: IDBObjectStore, key: String | number | ArrayBuffer | Date | Array<any> ): Promise<String | number | ArrayBuffer | Date | Array<any>>
indexeddb.del(store: IDBObjectStore, key: String | number | ArrayBuffer | Date | IDBKeyRange | Array<any> )
indexeddb.put(store: IDBObjectStore, item: String | number | ArrayBuffer | Date | boolean, key: String | number | ArrayBuffer | Date | Array<any>)
indexeddb.add(store: IDBObjectStore, item: String|number|ArrayBuffer|Date|boolean, key: String|number|ArrayBuffer|Date|Array.<any>): Promise<any>
indexeddb.addAutoKey(store: IDBObjectStore, item: String|number|ArrayBuffer|Date): Promise<number>
indexeddb.getAll(store: IDBObjectStore, range: IDBKeyRange, limit: number): Promise<Array<any>>
indexeddb.getAllKeys(store: IDBObjectStore, range: IDBKeyRange, limit: number): Promise<Array<any>>
indexeddb.queryFirst(store: IDBObjectStore, query: IDBKeyRange|null, direction: 'next'|'prev'|'nextunique'|'prevunique'): Promise<any>
indexeddb.getLastKey(store: IDBObjectStore, range: IDBKeyRange?): Promise<any>
indexeddb.getFirstKey(store: IDBObjectStore, range: IDBKeyRange?): Promise<any>
indexeddb.getAllKeysValues(store: IDBObjectStore, range: IDBKeyRange, limit: number): Promise<Array<KeyValuePair>>
indexeddb.iterate(store: IDBObjectStore, keyrange: IDBKeyRange|null, f: function(any,any):void|boolean|Promise<void|boolean>, direction: 'next'|'prev'|'nextunique'|'prevunique')

Iterate on keys and values

indexeddb.iterateKeys(store: IDBObjectStore, keyrange: IDBKeyRange|null, f: function(any):void|boolean|Promise<void|boolean>, direction: 'next'|'prev'|'nextunique'|'prevunique')

Iterate on the keys (no values)

indexeddb.getStore(t: IDBTransaction, store: String)IDBObjectStore

Open store from transaction

indexeddb.createIDBKeyRangeBound(lower: any, upper: any, lowerOpen: boolean, upperOpen: boolean)
indexeddb.createIDBKeyRangeUpperBound(upper: any, upperOpen: boolean)
indexeddb.createIDBKeyRangeLowerBound(lower: any, lowerOpen: boolean)
[lib0/isomorphic] Isomorphic library exports from isomorphic.js.
import * as isomorphic from 'lib0/isomorphic'
[lib0/iterator] Utility module to create and manipulate Iterators.
import * as iterator from 'lib0/iterator'
iterator.mapIterator(iterator: Iterator<T>, f: function(T):R): IterableIterator<R>
iterator.createIterator(next: function():IteratorResult<T>): IterableIterator<T>
iterator.iteratorFilter(iterator: Iterator<T>, filter: function(T):boolean)
iterator.iteratorMap(iterator: Iterator<T>, fmap: function(T):M)
[lib0/json] JSON utility functions.
import * as json from 'lib0/json'
json.stringify(object: any): string

Transform JavaScript object to JSON.

json.parse(json: string): any

Parse JSON object.

[lib0/list]
import * as list from 'lib0/list'
new e#ListNode()
e#next: this|null
e#prev: this|null
new st()
art: N | null
d: N | null
(): module:list.List<N>
()
(queue: module:list.List<N>)
()
(queue: module:list.List<N>, node: N)

Remove a single node from the queue. Only works with Queues that operate on Doubly-linked lists of nodes.

()
ode
ode()
etween(queue: module:list.List<N>, left: N| null, right: N| null, node: N)
etween()
(queue: module:list.List<N>, node: N, newNode: N)

Remove a single node from the queue. Only works with Queues that operate on Doubly-linked lists of nodes.

()
(queue: module:list.List<N>, n: N)
()
nt(queue: module:list.List<N>, n: N)
nt()
t(list: module:list.List<N>): N| null
t()
(list: module:list.List<N>): N| null
()
(list: module:list.List<N>, f: function(N):M): Array<M>
()
(list: module:list.List<N>)
()
(list: module:list.List<N>, f: function(N):M)
()
[lib0/logging] Isomorphic logging module with support for colors!
import * as logging from 'lib0/logging'
logging.BOLD
logging.UNBOLD
logging.BLUE
logging.GREY
logging.GREEN
logging.RED
logging.PURPLE
logging.ORANGE
logging.UNCOLOR
logging.print(args: Array<string|Symbol|Object|number>)
logging.warn(args: Array<string|Symbol|Object|number>)
logging.printError(err: Error)
logging.printImg(url: string, height: number)
logging.printImgBase64(base64: string, height: number)
logging.group(args: Array<string|Symbol|Object|number>)
logging.groupCollapsed(args: Array<string|Symbol|Object|number>)
logging.groupEnd
logging.printDom(createNode: function():Node)
logging.printCanvas(canvas: HTMLCanvasElement, height: number)
logging.vconsoles
new logging.VConsole(dom: Element)
logging.VConsole#ccontainer: Element
logging.VConsole#group(args: Array<string|Symbol|Object|number>, collapsed: boolean)
logging.VConsole#groupCollapsed(args: Array<string|Symbol|Object|number>)
logging.VConsole#groupEnd()
logging.VConsole#print(args: Array<string|Symbol|Object|number>)
logging.VConsole#printError(err: Error)
logging.VConsole#printImg(url: string, height: number)
logging.VConsole#printDom(node: Node)
logging.VConsole#destroy()
logging.createVConsole(dom: Element)
logging.createModuleLogger(moduleName: string): function(...any):void
[lib0/map] Utility module to work with key-value stores.
import * as map from 'lib0/map'
map.create(): Map<any, any>

Creates a new Map instance.

map.copy(m: Map<X,Y>): Map<X,Y>

Copy a Map object into a fresh Map object.

map.setIfUndefined(map: Map<K, T>, key: K, createT: function():T): T

Get map property. Create T if property is undefined and set T on map.

const listeners = map.setIfUndefined(events, 'eventName', set.create)
listeners.add(listener)
map.map(m: Map<K,V>, f: function(V,K):R): Array<R>

Creates an Array and populates it with the content of all key-value pairs using the f(value, key) function.

map.any(m: Map<K,V>, f: function(V,K):boolean): boolean

Tests whether any key-value pairs pass the test implemented by f(value, key).

map.all(m: Map<K,V>, f: function(V,K):boolean): boolean

Tests whether all key-value pairs pass the test implemented by f(value, key).

[lib0/math] Common Math expressions.
import * as math from 'lib0/math'
math.floor
math.ceil
math.abs
math.imul
math.round
math.log10
math.log2
math.log
math.sqrt
math.add(a: number, b: number): number
math.min(a: number, b: number): number
math.max(a: number, b: number): number
math.isNaN
math.pow
math.exp10(exp: number): number

Base 10 exponential function. Returns the value of 10 raised to the power of pow.

math.sign
math.isNegativeZero(n: number): boolean
[lib0/metric] Utility module to convert metric values.
import * as metric from 'lib0/metric'
metric.yotta
metric.zetta
metric.exa
metric.peta
metric.tera
metric.giga
metric.mega
metric.kilo
metric.hecto
metric.deca
metric.deci
metric.centi
metric.milli
metric.micro
metric.nano
metric.pico
metric.femto
metric.atto
metric.zepto
metric.yocto
metric.prefix(n: number, baseMultiplier: number): {n:number,prefix:string}

Calculate the metric prefix for a number. Assumes E.g. prefix(1000) = { n: 1, prefix: 'k' }

[lib0/mutex] Mutual exclude for JavaScript.
import * as mutex from 'lib0/mutex'
mutex.createMutex(): mutex

Creates a mutual exclude function with the following property:

const mutex = createMutex()
mutex(() => {
  // This function is immediately executed
  mutex(() => {
    // This function is not executed, as the mutex is already active.
  })
})
[lib0/number]
import * as number from 'lib0/number'
number.MAX_SAFE_INTEGER
number.MIN_SAFE_INTEGER
number.LOWEST_INT32
number.HIGHEST_INT32: number
number.isInteger
number.isNaN
number.parseInt
[lib0/object] Utility functions for working with EcmaScript objects.
import * as object from 'lib0/object'
object.create(): Object<string,any>
object.assign

Object.assign

object.keys(obj: Object<string,any>)
object.forEach(obj: Object<string,any>, f: function(any,string):any)
object.map(obj: Object<string,any>, f: function(any,string):R): Array<R>
object.length(obj: Object<string,any>): number
object.some(obj: Object<string,any>, f: function(any,string):boolean): boolean
object.isEmpty(obj: Object|undefined)
object.every(obj: Object<string,any>, f: function(any,string):boolean): boolean
object.hasProperty(obj: any, key: string|symbol): boolean

Calls Object.prototype.hasOwnProperty.

object.equalFlat(a: Object<string,any>, b: Object<string,any>): boolean
[lib0/observable] Observable class prototype.
import * as observable from 'lib0/observable'
new observable.Observable()

Handles named events.

observable.Observable#on(name: N, f: function)
observable.Observable#once(name: N, f: function)
observable.Observable#off(name: N, f: function)
observable.Observable#emit(name: N, args: Array<any>)

Emit a named event. All registered event listeners that listen to the specified name will receive the event.

observable.Observable#destroy()
websocket.WebsocketClient#on(name: N, f: function)
websocket.WebsocketClient#once(name: N, f: function)
websocket.WebsocketClient#off(name: N, f: function)
websocket.WebsocketClient#emit(name: N, args: Array<any>)

Emit a named event. All registered event listeners that listen to the specified name will receive the event.

[lib0/pair] Working with value pairs.
import * as pair from 'lib0/pair'
new pair.Pair(left: L, right: R)
pair.create(left: L, right: R): module:pair.Pair<L,R>
pair.createReversed(right: R, left: L): module:pair.Pair<L,R>
pair.forEach(arr: Array<module:pair.Pair<L,R>>, f: function(L, R):any)
pair.map(arr: Array<module:pair.Pair<L,R>>, f: function(L, R):X): Array<X>
[lib0/prng] Fast Pseudo Random Number Generators.
import * as prng from 'lib0/prng'

Given a seed a PRNG generates a sequence of numbers that cannot be reasonably predicted. Two PRNGs must generate the same random sequence of numbers if given the same seed.

prng.DefaultPRNG
prng.create(seed: number): module:prng~PRNG

Create a Xoroshiro128plus Pseudo-Random-Number-Generator. This is the fastest full-period generator passing BigCrush without systematic failures. But there are more PRNGs available in ./PRNG/.

prng.bool(gen: module:prng~PRNG): Boolean

Generates a single random bool.

prng.int53(gen: module:prng~PRNG, min: Number, max: Number): Number

Generates a random integer with 53 bit resolution.

prng.uint53(gen: module:prng~PRNG, min: Number, max: Number): Number

Generates a random integer with 53 bit resolution.

prng.int32(gen: module:prng~PRNG, min: Number, max: Number): Number

Generates a random integer with 32 bit resolution.

prng.uint32(gen: module:prng~PRNG, min: Number, max: Number): Number

Generates a random integer with 53 bit resolution.

prng.int31(gen: module:prng~PRNG, min: Number, max: Number): Number
prng.real53(gen: module:prng~PRNG): Number

Generates a random real on [0, 1) with 53 bit resolution.

prng.char(gen: module:prng~PRNG): string

Generates a random character from char code 32 - 126. I.e. Characters, Numbers, special characters, and Space:

prng.letter(gen: module:prng~PRNG): string
prng.word(gen: module:prng~PRNG, minLen: number, maxLen: number): string
prng.utf16Rune(gen: module:prng~PRNG): string

TODO: this function produces invalid runes. Does not cover all of utf16!!

prng.utf16String(gen: module:prng~PRNG, maxlen: number)
prng.oneOf(gen: module:prng~PRNG, array: Array<T>): T

Returns one element of a given array.

prng.uint8Array(gen: module:prng~PRNG, len: number): Uint8Array
prng.uint16Array(gen: module:prng~PRNG, len: number): Uint16Array
prng.uint32Array(gen: module:prng~PRNG, len: number): Uint32Array
[lib0/promise] Utility helpers to work with promises.
import * as promise from 'lib0/promise'
promise.create(f: function(PromiseResolve<T>,function(Error):void):any): Promise<T>
promise.createEmpty(f: function(function():void,function(Error):void):void): Promise<void>
promise.all(arrp: Array<Promise<T>>): Promise<Array<T>>

Promise.all wait for all promises in the array to resolve and return the result

promise.reject(reason: Error): Promise<never>
promise.resolve(res: T|void): Promise<T|void>
promise.resolveWith(res: T): Promise<T>
promise.until(timeout: number, check: function():boolean, intervalResolution: number): Promise<void>
promise.wait(timeout: number): Promise<undefined>
promise.isPromise(p: any): boolean

Checks if an object is a promise using ducktyping.

Promises are often polyfilled, so it makes sense to add some additional guarantees if the user of this library has some insane environment where global Promise objects are overwritten.

[lib0/queue]
import * as queue from 'lib0/queue'
new de#QueueNode()
de#next: module:queue.QueueNode|null
new ueue()
tart: module:queue.QueueNode | null
nd: module:queue.QueueNode | null
(): module:queue.Queue
()
(queue: module:queue.Queue)
()
(queue: module:queue.Queue, n: module:queue.QueueNode)
()
(queue: module:queue.Queue): module:queue.QueueNode | null
()
[lib0/random] Isomorphic module for true random numbers / buffers / uuids.
import * as random from 'lib0/random'

Attention: falls back to Math.random if the browser does not support crypto.

random.rand
random.uint32
random.uint53
random.oneOf(arr: Array<T>): T
random.uuidv4
[lib0/set] Utility module to work with sets.
import * as set from 'lib0/set'
set.create
set.toArray(set: Set<T>): Array<T>
set.first(set: Set<T>): T
set.from(entries: Iterable<T>): Set<T>
[lib0/sort] Efficient sort implementations.
import * as sort from 'lib0/sort'

Note: These sort implementations were created to compare different sorting algorithms in JavaScript. Don't use them if you don't know what you are doing. Native Array.sort is almost always a better choice.

sort.insertionSort(arr: Array<T>, compare: function(T,T):number): void
sort.quicksort(arr: Array<T>, compare: function(T,T):number): void

This algorithm beats Array.prototype.sort in Chrome only with arrays with 10 million entries. In most cases [].sort will do just fine. Make sure to performance test your use-case before you integrate this algorithm.

Note that Chrome's sort is now a stable algorithm (Timsort). Quicksort is not stable.

[lib0/statistics] Utility helpers for generating statistics.
import * as statistics from 'lib0/statistics'
statistics.median(arr: Array<number>): number
statistics.average(arr: Array<number>): number
[lib0/storage] Isomorphic variable storage.
import * as storage from 'lib0/storage'

Uses LocalStorage in the browser and falls back to in-memory storage.

storage.varStorage

This is basically localStorage in browser, or a polyfill in nodejs

storage.onChange(eventHandler: function({ key: string, newValue: string, oldValue: string }): void)

A polyfill for addEventListener('storage', event => {..}) that does nothing if the polyfill is being used.

[lib0/string] Utility module to work with strings.
import * as string from 'lib0/string'
string.fromCharCode
string.fromCodePoint
string.trimLeft(s: string): string
string.fromCamelCase(s: string, separator: string): string
string.utf8ByteLength(str: string): number

Compute the utf8ByteLength

string.utf8TextEncoder
string.encodeUtf8
string.decodeUtf8
string.splice(str: string, index: number, remove: number, insert: string)
[lib0/symbol] Utility module to work with EcmaScript Symbols.
import * as symbol from 'lib0/symbol'
symbol.create(): Symbol

Return fresh symbol.

symbol.isSymbol(s: any): boolean
[lib0/testing] Testing framework with support for generating tests.
import * as testing from 'lib0/testing'
// test.js template for creating a test executable
import { runTests } from 'lib0/testing'
import * as log from 'lib0/logging'
import * as mod1 from './mod1.test.js'
import * as mod2 from './mod2.test.js'
import { isBrowser, isNode } from 'lib0/environment.js'

if (isBrowser) {
  // optional: if this is ran in the browser, attach a virtual console to the dom
  log.createVConsole(document.body)
}

runTests({
 mod1,
 mod2,
}).then(success => {
  if (isNode) {
    process.exit(success ? 0 : 1)
  }
})
// mod1.test.js
/**
 * runTests automatically tests all exported functions that start with "test".
 * The name of the function should be in camelCase and is used for the logging output.
 *
 * @param {t.TestCase} tc
 *\/
export const testMyFirstTest = tc => {
  t.compare({ a: 4 }, { a: 4 }, 'objects are equal')
}

Now you can simply run node test.js to run your test or run test.js in the browser.

testing.extensive
testing.envSeed
new testing.TestCase(moduleName: string, testName: string)
testing.TestCase#moduleName: string
testing.TestCase#testName: string
testing.TestCase#resetSeed()
testing.TestCase#prng: prng.PRNG

A PRNG for this test case. Use only this PRNG for randomness to make the test case reproducible.

testing.repetitionTime
testing.run(moduleName: string, name: string, f: function(module:testing.TestCase):void|Promise<any>, i: number, numberOfTests: number)
testing.describe(description: string, info: string)

Describe what you are currently testing. The message will be logged.

export const testMyFirstTest = tc => {
  t.describe('crunching numbers', 'already crunched 4 numbers!') // the optional second argument can describe the state.
}
testing.info(info: string)

Describe the state of the current computation.

export const testMyFirstTest = tc => {
  t.info(already crunched 4 numbers!') // the optional second argument can describe the state.
}
testing.printDom
testing.printCanvas
testing.group(description: string, f: function(void):void)

Group outputs in a collapsible category.

export const testMyFirstTest = tc => {
  t.group('subtest 1', () => {
    t.describe('this message is part of a collapsible section')
  })
  await t.groupAsync('subtest async 2', async () => {
    await someaction()
    t.describe('this message is part of a collapsible section')
  })
}
testing.groupAsync(description: string, f: function(void):Promise<any>)

Group outputs in a collapsible category.

export const testMyFirstTest = async tc => {
  t.group('subtest 1', () => {
    t.describe('this message is part of a collapsible section')
  })
  await t.groupAsync('subtest async 2', async () => {
    await someaction()
    t.describe('this message is part of a collapsible section')
  })
}
testing.measureTime(message: string, f: function():void): number

Measure the time that it takes to calculate something.

export const testMyFirstTest = async tc => {
  t.measureTime('measurement', () => {
    heavyCalculation()
  })
  await t.groupAsync('async measurement', async () => {
    await heavyAsyncCalculation()
  })
}
testing.measureTimeAsync(message: string, f: function():Promise<any>): Promise<number>

Measure the time that it takes to calculate something.

export const testMyFirstTest = async tc => {
  t.measureTimeAsync('measurement', async () => {
    await heavyCalculation()
  })
  await t.groupAsync('async measurement', async () => {
    await heavyAsyncCalculation()
  })
}
testing.compareArrays(as: Array<T>, bs: Array<T>, m: string): boolean
testing.compareStrings(a: string, b: string, m: string)
testing.compareObjects(a: Object<K,V>, b: Object<K,V>, m: string)
testing.compare(a: T, b: T, message: string?, customCompare: function(any,T,T,string,any):boolean)
testing.assert(condition: boolean, message: string?)
testing.promiseRejected(f: function():Promise<any>)
testing.fails(f: function():void)
testing.runTests(tests: Object<string, Object<string, function(module:testing.TestCase):void|Promise<any>>>)
testing.fail(reason: string)
testing.skip(cond: boolean)
[lib0/time] Utility module to work with time.
import * as time from 'lib0/time'
time.getDate(): Date

Return current time.

time.getUnixTime(): number

Return current unix time.

time.humanizeDuration(d: number): string

Transform time (in ms) to a human readable format. E.g. 1100 => 1.1s. 60s => 1min. .001 => 10μs.

[lib0/tree] Red-black-tree implementation.
import * as tree from 'lib0/tree'
new tree.Tree()

This is a Red Black Tree implementation

tree.Tree#findNext(id: K)
tree.Tree#findPrev(id: K)
tree.Tree#findNodeWithLowerBound(from: K)
tree.Tree#findNodeWithUpperBound(to: K)
tree.Tree#findSmallestNode(): V
tree.Tree#findWithLowerBound(from: K): V
tree.Tree#findWithUpperBound(to: K): V
tree.Tree#iterate(from: K, from: K, f: K)
tree.Tree#find(id: K): V|null
tree.Tree#findNode(id: K): module:tree~N<V>|null
tree.Tree#delete(id: K)
tree.Tree#put()
[lib0/url] Utility module to work with urls.
import * as url from 'lib0/url'
url.decodeQueryParams(url: string): Object<string,string>

Parse query parameters from an url.

url.encodeQueryParams(params: Object<string,string>): string
[lib0/webcrypto.browser]
import * as webcrypto.browser from 'lib0/webcrypto.browser'

()
omValues
omValues()
[lib0/webcrypto.node]
import * as webcrypto.node from 'lib0/webcrypto.node'

()
to.subtle: any
omValues
omValues()
[lib0/websocket] Tiny websocket connection handler.
import * as websocket from 'lib0/websocket'

Implements exponential backoff reconnects, ping/pong, and a nice event system using [lib0/observable].

new websocket.WebsocketClient(url: string, opts: object, opts.binaryType: 'arraybuffer' | 'blob' | null)
websocket.WebsocketClient#ws: WebSocket?
websocket.WebsocketClient#shouldConnect: boolean

Whether to connect to other peers or not

websocket.WebsocketClient#send(message: any)
websocket.WebsocketClient#destroy()
websocket.WebsocketClient#disconnect()
websocket.WebsocketClient#connect()
### License [The MIT License](./LICENSE) © Kevin Jahns lib0-0.2.93/array.js000066400000000000000000000072121457563604600141310ustar00rootroot00000000000000/** * Utility module to work with Arrays. * * @module array */ import * as set from './set.js' /** * Return the last element of an array. The element must exist * * @template L * @param {ArrayLike} arr * @return {L} */ export const last = arr => arr[arr.length - 1] /** * @template C * @return {Array} */ export const create = () => /** @type {Array} */ ([]) /** * @template D * @param {Array} a * @return {Array} */ export const copy = a => /** @type {Array} */ (a.slice()) /** * Append elements from src to dest * * @template M * @param {Array} dest * @param {Array} src */ export const appendTo = (dest, src) => { for (let i = 0; i < src.length; i++) { dest.push(src[i]) } } /** * Transforms something array-like to an actual Array. * * @function * @template T * @param {ArrayLike|Iterable} arraylike * @return {T} */ export const from = Array.from /** * True iff condition holds on every element in the Array. * * @function * @template ITEM * @template {ArrayLike} ARR * * @param {ARR} arr * @param {function(ITEM, number, ARR):boolean} f * @return {boolean} */ export const every = (arr, f) => { for (let i = 0; i < arr.length; i++) { if (!f(arr[i], i, arr)) { return false } } return true } /** * True iff condition holds on some element in the Array. * * @function * @template S * @template {ArrayLike} ARR * @param {ARR} arr * @param {function(S, number, ARR):boolean} f * @return {boolean} */ export const some = (arr, f) => { for (let i = 0; i < arr.length; i++) { if (f(arr[i], i, arr)) { return true } } return false } /** * @template ELEM * * @param {ArrayLike} a * @param {ArrayLike} b * @return {boolean} */ export const equalFlat = (a, b) => a.length === b.length && every(a, (item, index) => item === b[index]) /** * @template ELEM * @param {Array>} arr * @return {Array} */ export const flatten = arr => fold(arr, /** @type {Array} */ ([]), (acc, val) => acc.concat(val)) /** * @template T * @param {number} len * @param {function(number, Array):T} f * @return {Array} */ export const unfold = (len, f) => { const array = new Array(len) for (let i = 0; i < len; i++) { array[i] = f(i, array) } return array } /** * @template T * @template RESULT * @param {Array} arr * @param {RESULT} seed * @param {function(RESULT, T, number):RESULT} folder */ export const fold = (arr, seed, folder) => arr.reduce(folder, seed) export const isArray = Array.isArray /** * @template T * @param {Array} arr * @return {Array} */ export const unique = arr => from(set.from(arr)) /** * @template T * @template M * @param {ArrayLike} arr * @param {function(T):M} mapper * @return {Array} */ export const uniqueBy = (arr, mapper) => { /** * @type {Set} */ const happened = set.create() /** * @type {Array} */ const result = [] for (let i = 0; i < arr.length; i++) { const el = arr[i] const mapped = mapper(el) if (!happened.has(mapped)) { happened.add(mapped) result.push(el) } } return result } /** * @template {ArrayLike} ARR * @template {function(ARR extends ArrayLike ? T : never, number, ARR):any} MAPPER * @param {ARR} arr * @param {MAPPER} mapper * @return {Array} */ export const map = (arr, mapper) => { /** * @type {Array} */ const res = Array(arr.length) for (let i = 0; i < arr.length; i++) { res[i] = mapper(/** @type {any} */ (arr[i]), i, /** @type {any} */ (arr)) } return /** @type {any} */ (res) } lib0-0.2.93/array.test.js000066400000000000000000000053711457563604600151130ustar00rootroot00000000000000import * as array from './array.js' import * as t from './testing.js' /** * @param {t.TestCase} _tc */ export const testIsarrayPerformance = _tc => { const N = 100000 /** * @type {Array} */ const objects = [] for (let i = 0; i < N; i++) { if (i % 2 === 0) { objects.push([i]) } else { objects.push({ i }) } } const timeConstructor = t.measureTime('constructor check', () => { let collectedArrays = 0 objects.forEach(obj => { if (obj.constructor === Array) { collectedArrays++ } }) t.assert(collectedArrays === N / 2) }) const timeIsarray = t.measureTime('Array.isArray', () => { let collectedArrays = 0 objects.forEach(obj => { if (array.isArray(obj)) { collectedArrays++ } }) t.assert(collectedArrays === N / 2) }) t.assert(timeIsarray < timeConstructor * 2, 'Expecting that isArray is not much worse than a constructor check') } /** * @param {t.TestCase} _tc */ export const testAppend = _tc => { const arr = [1, 2, 3] array.appendTo(arr, array.copy(arr)) t.compareArrays(arr, [1, 2, 3, 1, 2, 3]) } /** * @param {t.TestCase} _tc */ export const testBasic = _tc => { const arr = array.create() array.appendTo(arr, array.from([1])) t.assert(array.last(arr) === 1) } /** * @param {t.TestCase} _tc */ export const testflatten = _tc => { const arr = [[1, 2, 3], [4]] t.compareArrays(array.flatten(arr), [1, 2, 3, 4]) } /** * @param {t.TestCase} _tc */ export const testFolding = _tc => { /** * @param {number} n */ const testcase = n => { // We mess with the seed (+/-1) to catch some edge cases without changing the result const result = -1 + array.fold(array.unfold(n, i => i), 1, (accumulator, item, index) => { t.assert(accumulator === index + 1) t.assert(accumulator === item + 1) return accumulator + 1 }) t.assert(result === n) } testcase(0) testcase(1) testcase(100) } /** * @param {t.TestCase} _tc */ export const testEvery = _tc => { const arr = [1, 2, 3] t.assert(array.every(arr, x => x <= 3)) t.assert(!array.every(arr, x => x < 3)) t.assert(array.some(arr, x => x === 2)) t.assert(!array.some(arr, x => x === 42)) } /** * @param {t.TestCase} _tc */ export const testIsArray = _tc => { t.assert(array.isArray([])) t.assert(array.isArray([1])) t.assert(array.isArray(Array.from(new Set([3])))) t.assert(!array.isArray(1)) t.assert(!array.isArray(0)) t.assert(!array.isArray('')) } /** * @param {t.TestCase} _tc */ export const testUnique = _tc => { t.compare([1, 2], array.unique([1, 2, 1, 2, 2, 1])) t.compare([], array.unique([])) t.compare([{ el: 1 }], array.uniqueBy([{ el: 1 }, { el: 1 }], o => o.el)) t.compare([], array.uniqueBy([], o => o)) } lib0-0.2.93/bin/000077500000000000000000000000001457563604600132235ustar00rootroot00000000000000lib0-0.2.93/bin/0ecdsa-generate-keypair.js000077500000000000000000000010511457563604600201520ustar00rootroot00000000000000#!/usr/bin/env node import * as ecdsa from 'lib0/crypto/ecdsa' import * as json from 'lib0/json' import * as env from 'lib0/environment' const prefix = env.getConf('name') const keypair = await ecdsa.generateKeyPair({ extractable: true }) const privateJwk = json.stringify(await ecdsa.exportKeyJwk(keypair.privateKey)) const publicJwk = json.stringify(await ecdsa.exportKeyJwk(keypair.publicKey)) console.log(` ${prefix ? prefix.toUpperCase() + '_' : ''}PUBLIC_KEY=${publicJwk} ${prefix ? prefix.toUpperCase() + '_' : ''}PRIVATE_KEY=${privateJwk} `) lib0-0.2.93/bin/0serve.js000077500000000000000000000051121457563604600147670ustar00rootroot00000000000000#!/usr/bin/env node import * as http from 'http' import * as path from 'path' import * as fs from 'fs' import * as env from '../environment.js' import * as number from '../number.js' import * as logging from 'lib0/logging' const host = env.getParam('--host', 'localhost') const port = number.parseInt(env.getParam('--port', '8000')) const paramOpenFile = env.getParam('-o', '') /** * @type {Object} */ const types = { html: 'text/html', css: 'text/css', js: 'application/javascript', mjs: 'application/javascript', png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', json: 'application/json', xml: 'application/xml' } const root = path.normalize(path.resolve('./')) const server = http.createServer((req, res) => { const url = (req.url || '/index.html').split('?')[0] logging.print(logging.ORANGE, logging.BOLD, req.method || '', ' ', logging.GREY, logging.UNBOLD, url) const extension = path.extname(url).slice(1) /** * @type {string} */ const type = (extension && types[extension]) || types.html const supportedExtension = Boolean(type) if (!supportedExtension) { res.writeHead(404, { 'Content-Type': 'text/html' }) res.end('404: File not found') return } let fileName = url if (url === '/') fileName = 'index.html' else if (!extension) { try { fs.accessSync(path.join(root, url + '.html'), fs.constants.F_OK) fileName = url + '.html' } catch (e) { fileName = path.join(url, 'index.html') } } const filePath = path.join(root, fileName) const isPathUnderRoot = path .normalize(path.resolve(filePath)) .startsWith(root) if (!isPathUnderRoot) { res.writeHead(404, { 'Content-Type': 'text/html' }) res.end('404: File not found') logging.print(logging.RED, logging.BOLD, 'Not Found: ', logging.GREY, logging.UNBOLD, url) return } fs.readFile(filePath, (err, data) => { if (err) { logging.print(logging.RED, logging.BOLD, 'Cannot read file: ', logging.GREY, logging.UNBOLD, url) res.writeHead(404, { 'Content-Type': 'text/html' }) res.end('404: File not found') } else { res.writeHead(200, { 'Content-Type': type }) res.end(data) } }) }) server.listen(port, host, () => { logging.print(logging.BOLD, logging.ORANGE, `Server is running on http://${host}:${port}`) if (paramOpenFile) { const start = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open' import('child_process').then(cp => { cp.exec(`${start} http://${host}:${port}/${paramOpenFile}`) }) } }) lib0-0.2.93/bin/gendocs.js000066400000000000000000000110011457563604600151740ustar00rootroot00000000000000#!/usr/bin/env node // @ts-ignore import jsdoc from 'jsdoc-api' import * as fs from 'fs' const firstTagContentRegex = /<\w>([^<]+)<\/\w>([^]*)/ const jsdocReturnRegex = /\* @return {(.*)}/ const jsdocTypeRegex = /\* @type {(.*)}/ const files = fs.readdirSync('./').filter(file => /(?/g /** * @param {string} s */ const toSafeHtml = s => s.replace(_ltregex, '<').replace(_rtregex, '>') const READMEcontent = fs.readFileSync('./README.md', 'utf8') jsdoc.explain({ files, configure: '.jsdoc.json' }).then(/** @param {Array} json */ json => { const strBuilder = [] /** * @type {Object, name: string, description: string }>} */ const modules = {} json.forEach(item => { if (item.meta && item.meta.filename) { const mod = modules[item.meta.filename] || (modules[item.meta.filename] = { items: [], name: item.meta.filename.slice(0, -3), description: '' }) if (item.kind === 'module') { mod.name = item.name mod.description = item.description || '' } else { mod.items.push(item) } } }) /** * @type {Object} */ const classDescriptions = {} for (const fileName in modules) { const mod = modules[fileName] const items = mod.items const desc = firstTagContentRegex.exec(mod.description) const descHead = desc ? desc[1] : '' const descRest = desc ? desc[2] : '' strBuilder.push(`
[lib0/${mod.name}] ${descHead}`) strBuilder.push(`
import * as ${mod.name} from 'lib0/${fileName.slice(0, -3)}'
`) if (descRest.length > 0) { strBuilder.push(descRest) } strBuilder.push('
') for (let i = 0; i < items.length; i++) { const item = items[i] if (!item.ignore && item.scope !== 'inner' && item.name[0] !== '_' && item.longname.indexOf('~') < 0) { // strBuilder.push(JSON.stringify(item)) // output json for debugging switch (item.kind) { case 'class': { if (item.params == null) { classDescriptions[item.longname] = item.classdesc break } } // eslint-disable-next-line case 'constant': { if (item.params == null && item.returns == null) { const typeEval = jsdocTypeRegex.exec(item.comment) strBuilder.push(`${item.longname.slice(7)}${typeEval ? (': ' + toSafeHtml(typeEval[1])) : ''}
`) if (item.description) { strBuilder.push(`
${item.description}
`) } break } } // eslint-disable-next-line case 'function': { /** * @param {string} name */ const getOriginalParamTypeDecl = name => { const regval = new RegExp('@param {(.*)} \\[?' + name + '\\]?[^\\w]*').exec(item.comment) return regval ? regval[1] : null } if (item.params == null && item.returns == null) { break } const paramVal = (item.params || []).map(/** @param {any} ret */ ret => `${ret.name}: ${getOriginalParamTypeDecl(ret.name) || ret.type.names.join('|')}`).join(', ') const evalReturnRegex = jsdocReturnRegex.exec(item.comment) const returnVal = evalReturnRegex ? `: ${evalReturnRegex[1]}` : (item.returns ? item.returns.map(/** @param {any} r */ r => r.type.names.join('|')).join('|') : '') strBuilder.push(`${item.kind === 'class' ? 'new ' : ''}${item.longname.slice(7)}(${toSafeHtml(paramVal)})${toSafeHtml(returnVal)}
`) const desc = item.description || item.classdesc || classDescriptions[item.longname] || null if (desc) { strBuilder.push(`
${desc}
`) } break } case 'member': { if (item.type) { strBuilder.push(`${item.longname.slice(7)}: ${toSafeHtml(/** @type {RegExpExecArray} */ (jsdocTypeRegex.exec(item.comment))[1])}
`) if (item.description) { strBuilder.push(`
${item.description}
`) } } } } } } strBuilder.push('
') strBuilder.push('
') } const replaceReadme = READMEcontent.replace(/
[^]*<\/details>/, strBuilder.join('\n')) fs.writeFileSync('./README.md', replaceReadme) }) lib0-0.2.93/bin/gentesthtml.js000077500000000000000000000035321457563604600161250ustar00rootroot00000000000000#!/usr/bin/env node import * as fs from 'fs' import * as object from '../object.js' import * as env from '../environment.js' const script = env.getParam('--script', './test.js') /** * @type {Object} */ const exports = {} /** * @type {Object>} */ const scopes = {} /** * @param {any} v * @param {string} k * @param {string} pkgName * @param {string} pathPrefix * @param {Object} importMap */ const extractModMap = (v, k, pkgName, pathPrefix, importMap) => { if (k[0] !== '.') return if (typeof v === 'object') { extractModMap(v.browser || v.module || v.default || v.import, k, pkgName, pathPrefix, importMap) } else if (v && v[0] === '.') { importMap[pkgName + k.slice(1)] = pathPrefix + v.slice(1) } } /** * @param {any} pkgJson * @param {string} pathPrefix * @param {Object} importMap */ const readPkg = (pkgJson, pathPrefix, importMap) => { object.forEach(pkgJson.exports, (v, k) => extractModMap(v, k, pkgJson.name, pathPrefix, importMap)) object.forEach(pkgJson.dependencies, (_v, depName) => { const nextImportMap = pathPrefix === '.' ? exports : (scopes[pathPrefix + '/'] = {}) const prefix = `./node_modules/${depName}` const depPkgJson = JSON.parse(fs.readFileSync(prefix + '/package.json', { encoding: 'utf8' })) readPkg(depPkgJson, prefix, nextImportMap) }) } const rootPkgJson = JSON.parse(fs.readFileSync('./package.json', { encoding: 'utf8' })) readPkg(rootPkgJson, '.', exports) const testHtml = ` Testing ${rootPkgJson.name} ` console.log(testHtml) lib0-0.2.93/binary.js000066400000000000000000000037661457563604600143110ustar00rootroot00000000000000/* eslint-env browser */ /** * Binary data constants. * * @module binary */ /** * n-th bit activated. * * @type {number} */ export const BIT1 = 1 export const BIT2 = 2 export const BIT3 = 4 export const BIT4 = 8 export const BIT5 = 16 export const BIT6 = 32 export const BIT7 = 64 export const BIT8 = 128 export const BIT9 = 256 export const BIT10 = 512 export const BIT11 = 1024 export const BIT12 = 2048 export const BIT13 = 4096 export const BIT14 = 8192 export const BIT15 = 16384 export const BIT16 = 32768 export const BIT17 = 65536 export const BIT18 = 1 << 17 export const BIT19 = 1 << 18 export const BIT20 = 1 << 19 export const BIT21 = 1 << 20 export const BIT22 = 1 << 21 export const BIT23 = 1 << 22 export const BIT24 = 1 << 23 export const BIT25 = 1 << 24 export const BIT26 = 1 << 25 export const BIT27 = 1 << 26 export const BIT28 = 1 << 27 export const BIT29 = 1 << 28 export const BIT30 = 1 << 29 export const BIT31 = 1 << 30 export const BIT32 = 1 << 31 /** * First n bits activated. * * @type {number} */ export const BITS0 = 0 export const BITS1 = 1 export const BITS2 = 3 export const BITS3 = 7 export const BITS4 = 15 export const BITS5 = 31 export const BITS6 = 63 export const BITS7 = 127 export const BITS8 = 255 export const BITS9 = 511 export const BITS10 = 1023 export const BITS11 = 2047 export const BITS12 = 4095 export const BITS13 = 8191 export const BITS14 = 16383 export const BITS15 = 32767 export const BITS16 = 65535 export const BITS17 = BIT18 - 1 export const BITS18 = BIT19 - 1 export const BITS19 = BIT20 - 1 export const BITS20 = BIT21 - 1 export const BITS21 = BIT22 - 1 export const BITS22 = BIT23 - 1 export const BITS23 = BIT24 - 1 export const BITS24 = BIT25 - 1 export const BITS25 = BIT26 - 1 export const BITS26 = BIT27 - 1 export const BITS27 = BIT28 - 1 export const BITS28 = BIT29 - 1 export const BITS29 = BIT30 - 1 export const BITS30 = BIT31 - 1 /** * @type {number} */ export const BITS31 = 0x7FFFFFFF /** * @type {number} */ export const BITS32 = 0xFFFFFFFF lib0-0.2.93/binary.test.js000066400000000000000000000011461457563604600152550ustar00rootroot00000000000000import * as binary from './binary.js' import * as t from './testing.js' /** * @param {t.TestCase} tc */ export const testBitx = tc => { for (let i = 1; i <= 32; i++) { // @ts-ignore t.assert(binary[`BIT${i}`] === (1 << (i - 1)), `BIT${i}=${1 << (i - 1)}`) } } /** * @param {t.TestCase} tc */ export const testBitsx = tc => { t.assert(binary.BITS0 === 0) for (let i = 1; i < 32; i++) { const expected = ((1 << i) - 1) >>> 0 // @ts-ignore const have = binary[`BITS${i}`] t.assert(have === expected, `BITS${i}=${have}=${expected}`) } t.assert(binary.BITS32 === 0xFFFFFFFF) } lib0-0.2.93/broadcastchannel.js000066400000000000000000000057421457563604600163140ustar00rootroot00000000000000/* eslint-env browser */ /** * Helpers for cross-tab communication using broadcastchannel with LocalStorage fallback. * * ```js * // In browser window A: * broadcastchannel.subscribe('my events', data => console.log(data)) * broadcastchannel.publish('my events', 'Hello world!') // => A: 'Hello world!' fires synchronously in same tab * * // In browser window B: * broadcastchannel.publish('my events', 'hello from tab B') // => A: 'hello from tab B' * ``` * * @module broadcastchannel */ // @todo before next major: use Uint8Array instead as buffer object import * as map from './map.js' import * as set from './set.js' import * as buffer from './buffer.js' import * as storage from './storage.js' /** * @typedef {Object} Channel * @property {Set} Channel.subs * @property {any} Channel.bc */ /** * @type {Map} */ const channels = new Map() /* c8 ignore start */ class LocalStoragePolyfill { /** * @param {string} room */ constructor (room) { this.room = room /** * @type {null|function({data:ArrayBuffer}):void} */ this.onmessage = null /** * @param {any} e */ this._onChange = e => e.key === room && this.onmessage !== null && this.onmessage({ data: buffer.fromBase64(e.newValue || '') }) storage.onChange(this._onChange) } /** * @param {ArrayBuffer} buf */ postMessage (buf) { storage.varStorage.setItem(this.room, buffer.toBase64(buffer.createUint8ArrayFromArrayBuffer(buf))) } close () { storage.offChange(this._onChange) } } /* c8 ignore stop */ // Use BroadcastChannel or Polyfill /* c8 ignore next */ const BC = typeof BroadcastChannel === 'undefined' ? LocalStoragePolyfill : BroadcastChannel /** * @param {string} room * @return {Channel} */ const getChannel = room => map.setIfUndefined(channels, room, () => { const subs = set.create() const bc = new BC(room) /** * @param {{data:ArrayBuffer}} e */ /* c8 ignore next */ bc.onmessage = e => subs.forEach(sub => sub(e.data, 'broadcastchannel')) return { bc, subs } }) /** * Subscribe to global `publish` events. * * @function * @param {string} room * @param {function(any, any):any} f */ export const subscribe = (room, f) => { getChannel(room).subs.add(f) return f } /** * Unsubscribe from `publish` global events. * * @function * @param {string} room * @param {function(any, any):any} f */ export const unsubscribe = (room, f) => { const channel = getChannel(room) const unsubscribed = channel.subs.delete(f) if (unsubscribed && channel.subs.size === 0) { channel.bc.close() channels.delete(room) } return unsubscribed } /** * Publish data to all subscribers (including subscribers on this tab) * * @function * @param {string} room * @param {any} data * @param {any} [origin] */ export const publish = (room, data, origin = null) => { const c = getChannel(room) c.bc.postMessage(data) c.subs.forEach(sub => sub(data, origin)) } lib0-0.2.93/broadcastchannel.test.js000066400000000000000000000012321457563604600172600ustar00rootroot00000000000000import * as t from './testing.js' import * as bc from './broadcastchannel.js' /** * @param {t.TestCase} tc */ export const testBroadcastChannel = tc => { bc.publish('test', 'test1', tc) /** * @type {any} */ const messages = [] const sub = bc.subscribe('test', (data, origin) => { messages.push({ data, origin }) }) t.compare(messages, []) bc.publish('test', 'test2', tc) bc.publish('test', 'test3') t.compare(messages, [{ data: 'test2', origin: tc }, { data: 'test3', origin: null }]) bc.unsubscribe('test', sub) bc.publish('test', 'test4') t.compare(messages, [{ data: 'test2', origin: tc }, { data: 'test3', origin: null }]) } lib0-0.2.93/buffer.js000066400000000000000000000100301457563604600142540ustar00rootroot00000000000000/** * Utility functions to work with buffers (Uint8Array). * * @module buffer */ import * as string from './string.js' import * as env from './environment.js' import * as array from './array.js' import * as math from './math.js' import * as encoding from './encoding.js' import * as decoding from './decoding.js' /** * @param {number} len */ export const createUint8ArrayFromLen = len => new Uint8Array(len) /** * Create Uint8Array with initial content from buffer * * @param {ArrayBuffer} buffer * @param {number} byteOffset * @param {number} length */ export const createUint8ArrayViewFromArrayBuffer = (buffer, byteOffset, length) => new Uint8Array(buffer, byteOffset, length) /** * Create Uint8Array with initial content from buffer * * @param {ArrayBuffer} buffer */ export const createUint8ArrayFromArrayBuffer = buffer => new Uint8Array(buffer) /* c8 ignore start */ /** * @param {Uint8Array} bytes * @return {string} */ const toBase64Browser = bytes => { let s = '' for (let i = 0; i < bytes.byteLength; i++) { s += string.fromCharCode(bytes[i]) } // eslint-disable-next-line no-undef return btoa(s) } /* c8 ignore stop */ /** * @param {Uint8Array} bytes * @return {string} */ const toBase64Node = bytes => Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('base64') /* c8 ignore start */ /** * @param {string} s * @return {Uint8Array} */ const fromBase64Browser = s => { // eslint-disable-next-line no-undef const a = atob(s) const bytes = createUint8ArrayFromLen(a.length) for (let i = 0; i < a.length; i++) { bytes[i] = a.charCodeAt(i) } return bytes } /* c8 ignore stop */ /** * @param {string} s */ const fromBase64Node = s => { const buf = Buffer.from(s, 'base64') return createUint8ArrayViewFromArrayBuffer(buf.buffer, buf.byteOffset, buf.byteLength) } /* c8 ignore next */ export const toBase64 = env.isBrowser ? toBase64Browser : toBase64Node /* c8 ignore next */ export const fromBase64 = env.isBrowser ? fromBase64Browser : fromBase64Node /** * Implements base64url - see https://datatracker.ietf.org/doc/html/rfc4648#section-5 * @param {Uint8Array} buf */ export const toBase64UrlEncoded = buf => toBase64(buf).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '') /** * @param {string} base64 */ export const fromBase64UrlEncoded = base64 => fromBase64(base64.replaceAll('-', '+').replaceAll('_', '/')) /** * Base64 is always a more efficient choice. This exists for utility purposes only. * * @param {Uint8Array} buf */ export const toHexString = buf => array.map(buf, b => b.toString(16).padStart(2, '0')).join('') /** * Note: This function expects that the hex doesn't start with 0x.. * * @param {string} hex */ export const fromHexString = hex => { const hlen = hex.length const buf = new Uint8Array(math.ceil(hlen / 2)) for (let i = 0; i < hlen; i += 2) { buf[buf.length - i / 2 - 1] = Number.parseInt(hex.slice(hlen - i - 2, hlen - i), 16) } return buf } /** * Copy the content of an Uint8Array view to a new ArrayBuffer. * * @param {Uint8Array} uint8Array * @return {Uint8Array} */ export const copyUint8Array = uint8Array => { const newBuf = createUint8ArrayFromLen(uint8Array.byteLength) newBuf.set(uint8Array) return newBuf } /** * Encode anything as a UInt8Array. It's a pun on typescripts's `any` type. * See encoding.writeAny for more information. * * @param {any} data * @return {Uint8Array} */ export const encodeAny = data => encoding.encode(encoder => encoding.writeAny(encoder, data)) /** * Decode an any-encoded value. * * @param {Uint8Array} buf * @return {any} */ export const decodeAny = buf => decoding.readAny(decoding.createDecoder(buf)) /** * Shift Byte Array {N} bits to the left. Does not expand byte array. * * @param {Uint8Array} bs * @param {number} N should be in the range of [0-7] */ export const shiftNBitsLeft = (bs, N) => { if (N === 0) return bs bs = new Uint8Array(bs) bs[0] <<= N for (let i = 1; i < bs.length; i++) { bs[i - 1] |= bs[i] >>> (8 - N) bs[i] <<= N } return bs } lib0-0.2.93/buffer.test.js000066400000000000000000000026131457563604600152420ustar00rootroot00000000000000import * as t from './testing.js' import * as buffer from './buffer.js' import * as prng from './prng.js' /** * @param {t.TestCase} tc * @param {function(Uint8Array):string} encoder * @param {function(string):Uint8Array} decoder */ const testEncodingHelper = (tc, encoder, decoder) => { const gen = tc.prng const barr = prng.uint8Array(gen, prng.uint32(gen, 0, 47)) const copied = buffer.copyUint8Array(barr) const encoded = encoder(barr) t.assert(encoded.constructor === String) const decoded = decoder(encoded) t.assert(decoded.constructor === Uint8Array) t.assert(decoded.byteLength === barr.byteLength) for (let i = 0; i < barr.length; i++) { t.assert(barr[i] === decoded[i]) } t.compare(copied, decoded) } /** * @param {t.TestCase} tc */ export const testRepeatBase64urlEncoding = tc => { testEncodingHelper(tc, buffer.toBase64UrlEncoded, buffer.fromBase64UrlEncoded) } /** * @param {t.TestCase} tc */ export const testRepeatBase64Encoding = tc => { testEncodingHelper(tc, buffer.toBase64, buffer.fromBase64) } /** * @param {t.TestCase} tc */ export const testRepeatHexEncoding = tc => { testEncodingHelper(tc, buffer.toHexString, buffer.fromHexString) } /** * @param {t.TestCase} _tc */ export const testAnyEncoding = _tc => { const obj = { val: 1, arr: [1, 2], str: '409231dtrnä' } const res = buffer.decodeAny(buffer.encodeAny(obj)) t.compare(obj, res) } lib0-0.2.93/cache.js000066400000000000000000000076021457563604600140610ustar00rootroot00000000000000/* eslint-env browser */ /** * An implementation of a map which has keys that expire. * * @module cache */ import * as list from './list.js' import * as map from './map.js' import * as time from './time.js' /** * @template K, V * * @implements {list.ListNode} */ class Entry { /** * @param {K} key * @param {V | Promise} val */ constructor (key, val) { /** * @type {this | null} */ this.prev = null /** * @type {this | null} */ this.next = null this.created = time.getUnixTime() this.val = val this.key = key } } /** * @template K, V */ export class Cache { /** * @param {number} timeout */ constructor (timeout) { this.timeout = timeout /** * @type list.List> */ this._q = list.create() /** * @type {Map>} */ this._map = map.create() } } /** * @template K, V * * @param {Cache} cache * @return {number} Returns the current timestamp */ export const removeStale = cache => { const now = time.getUnixTime() const q = cache._q while (q.start && now - q.start.created > cache.timeout) { cache._map.delete(q.start.key) list.popFront(q) } return now } /** * @template K, V * * @param {Cache} cache * @param {K} key * @param {V} value */ export const set = (cache, key, value) => { const now = removeStale(cache) const q = cache._q const n = cache._map.get(key) if (n) { list.removeNode(q, n) list.pushEnd(q, n) n.created = now n.val = value } else { const node = new Entry(key, value) list.pushEnd(q, node) cache._map.set(key, node) } } /** * @template K, V * * @param {Cache} cache * @param {K} key * @return {Entry | undefined} */ const getNode = (cache, key) => { removeStale(cache) const n = cache._map.get(key) if (n) { return n } } /** * @template K, V * * @param {Cache} cache * @param {K} key * @return {V | undefined} */ export const get = (cache, key) => { const n = getNode(cache, key) return n && !(n.val instanceof Promise) ? n.val : undefined } /** * @template K, V * * @param {Cache} cache * @param {K} key */ export const refreshTimeout = (cache, key) => { const now = time.getUnixTime() const q = cache._q const n = cache._map.get(key) if (n) { list.removeNode(q, n) list.pushEnd(q, n) n.created = now } } /** * Works well in conjunktion with setIfUndefined which has an async init function. * Using getAsync & setIfUndefined ensures that the init function is only called once. * * @template K, V * * @param {Cache} cache * @param {K} key * @return {V | Promise | undefined} */ export const getAsync = (cache, key) => { const n = getNode(cache, key) return n ? n.val : undefined } /** * @template K, V * * @param {Cache} cache * @param {K} key */ export const remove = (cache, key) => { const n = cache._map.get(key) if (n) { list.removeNode(cache._q, n) cache._map.delete(key) return n.val && !(n.val instanceof Promise) ? n.val : undefined } } /** * @template K, V * * @param {Cache} cache * @param {K} key * @param {function():Promise} init * @param {boolean} removeNull Optional argument that automatically removes values that resolve to null/undefined from the cache. * @return {Promise | V} */ export const setIfUndefined = (cache, key, init, removeNull = false) => { removeStale(cache) const q = cache._q const n = cache._map.get(key) if (n) { return n.val } else { const p = init() const node = new Entry(key, p) list.pushEnd(q, node) cache._map.set(key, node) p.then(v => { if (p === node.val) { node.val = v } if (removeNull && v == null) { remove(cache, key) } }) return p } } /** * @param {number} timeout */ export const create = timeout => new Cache(timeout) lib0-0.2.93/cache.test.js000066400000000000000000000053341457563604600150370ustar00rootroot00000000000000import * as t from './testing.js' import * as cache from './cache.js' import * as promise from './promise.js' /** * @param {t.TestCase} tc */ export const testCache = async tc => { /** * @type {cache.Cache} */ const c = cache.create(50) cache.set(c, 'a', '1') t.assert(cache.get(c, 'a') === '1') t.assert(await cache.getAsync(c, 'a') === '1') const p = cache.setIfUndefined(c, 'b', () => promise.resolveWith('2')) const q = cache.setIfUndefined(c, 'b', () => promise.resolveWith('3')) t.assert(p === q) t.assert(cache.get(c, 'b') == null) t.assert(cache.getAsync(c, 'b') === p) t.assert(await p === '2') t.assert(cache.get(c, 'b') === '2') t.assert(cache.getAsync(c, 'b') === '2') await promise.wait(5) // keys shouldn't be timed out yet t.assert(cache.get(c, 'a') === '1') t.assert(cache.get(c, 'b') === '2') /** * @type {any} */ const m = c._map const aTimestamp1 = m.get('a').created const bTimestamp1 = m.get('b').created // write new values and check later if the creation-timestamp was updated cache.set(c, 'a', '11') cache.set(c, 'b', '22') await promise.wait(5) // keys should be updated and not timed out. Hence the creation time should be updated t.assert(cache.get(c, 'a') === '11') t.assert(cache.get(c, 'b') === '22') cache.set(c, 'a', '11') cache.set(c, 'b', '22') // timestamps should be updated t.assert(aTimestamp1 !== m.get('a').created) t.assert(bTimestamp1 !== m.get('b').created) await promise.wait(60) // now the keys should be timed-out t.assert(cache.get(c, 'a') == null) t.assert(cache.getAsync(c, 'b') == null) t.assert(c._map.size === 0) t.assert(c._q.start === null && c._q.end === null) // test edge case of setIfUndefined const xp = cache.setIfUndefined(c, 'a', () => promise.resolve('x')) cache.set(c, 'a', 'y') await xp // we override the Entry.val property in cache when p resolves. However, we must prevent that when the value is overriden before p is resolved. t.assert(cache.get(c, 'a') === 'y') // test that we can remove properties cache.remove(c, 'a') cache.remove(c, 'does not exist') // remove a non-existent property to achieve full test-coverage t.assert(cache.get(c, 'a') === undefined) // test that the optional property in setifUndefined works const yp = cache.setIfUndefined(c, 'a', () => promise.resolveWith(null), true) t.assert(await yp === null) t.assert(cache.get(c, 'a') === undefined) // check manual updating of timeout cache.set(c, 'a', '3') const ts1 = m.get('a').created await promise.wait(30) cache.refreshTimeout(c, 'a') const ts2 = m.get('a').created t.assert(ts1 !== ts2) cache.refreshTimeout(c, 'x') // for full test coverage t.assert(m.get('x') == null) } lib0-0.2.93/component.js000066400000000000000000000310601457563604600150130ustar00rootroot00000000000000/* eslint-env browser */ /** * Web components. * * @module component */ import * as dom from './dom.js' import * as diff from './diff.js' import * as object from './object.js' import * as json from './json.js' import * as string from './string.js' import * as array from './array.js' import * as number from './number.js' import * as func from './function.js' /** * @type {CustomElementRegistry} */ export const registry = customElements /** * @param {string} name * @param {any} constr * @param {ElementDefinitionOptions} [opts] */ export const define = (name, constr, opts) => registry.define(name, constr, opts) /** * @param {string} name * @return {Promise} */ export const whenDefined = name => registry.whenDefined(name) const upgradedEventName = 'upgraded' const connectedEventName = 'connected' const disconnectedEventName = 'disconnected' /** * @template S */ export class Lib0Component extends HTMLElement { /** * @param {S} [state] */ constructor (state) { super() /** * @type {S|null} */ this.state = /** @type {any} */ (state) /** * @type {any} */ this._internal = {} } /** * @param {S} _state * @param {boolean} [_forceStateUpdate] Force that the state is rerendered even if state didn't change */ setState (_state, _forceStateUpdate = true) {} /** * @param {any} _stateUpdate */ updateState (_stateUpdate) { } } /** * @param {any} val * @param {"json"|"string"|"number"} type * @return {string} */ const encodeAttrVal = (val, type) => { if (type === 'json') { val = json.stringify(val) } return val + '' } /** * @param {any} val * @param {"json"|"string"|"number"|"bool"} type * @return {any} */ const parseAttrVal = (val, type) => { switch (type) { case 'json': return json.parse(val) case 'number': return Number.parseFloat(val) case 'string': return val case 'bool': return val != null default: return null } } /** * @typedef {Object} CONF * @property {string?} [CONF.template] Template for the shadow dom. * @property {string} [CONF.style] shadow dom style. Is only used when * `CONF.template` is defined * @property {S} [CONF.state] Initial component state. * @property {function(S,S|null,Lib0Component):void} [CONF.onStateChange] Called when * the state changes. * @property {Object} [CONF.childStates] maps from * CSS-selector to transformer function. The first element that matches the * CSS-selector receives state updates via the transformer function. * @property {Object} [CONF.attrs] * attrs-keys and state-keys should be camelCase, but the DOM uses kebap-case. I.e. * `attrs = { myAttr: 4 }` is represeted as `` in the DOM * @property {Object):boolean|void>} [CONF.listeners] Maps from dom-event-name * to event listener. * @property {function(S, S, Lib0Component):Object} [CONF.slots] Fill slots * automatically when state changes. Maps from slot-name to slot-html. * @template S */ /** * @template T * @param {string} name * @param {CONF} cnf * @return {typeof Lib0Component} */ export const createComponent = (name, { template, style = '', state: defaultState, onStateChange = () => {}, childStates = { }, attrs = {}, listeners = {}, slots = () => ({}) }) => { /** * Maps from camelCase attribute name to kebap-case attribute name. * @type {Object} */ const normalizedAttrs = {} for (const key in attrs) { normalizedAttrs[string.fromCamelCase(key, '-')] = key } const templateElement = template ? /** @type {HTMLTemplateElement} */ (dom.parseElement(` `)) : null class _Lib0Component extends HTMLElement { /** * @param {T} [state] */ constructor (state) { super() /** * @type {Array<{d:Lib0Component, s:function(any, any):Object}>} */ this._childStates = [] /** * @type {Object} */ this._slots = {} this._init = false /** * @type {any} */ this._internal = {} /** * @type {any} */ this.state = state || null this.connected = false // init shadow dom if (templateElement) { const shadow = /** @type {ShadowRoot} */ (this.attachShadow({ mode: 'open' })) shadow.appendChild(templateElement.content.cloneNode(true)) // fill child states for (const key in childStates) { this._childStates.push({ d: /** @type {Lib0Component} */ (dom.querySelector(/** @type {any} */ (shadow), key)), s: childStates[key] }) } } dom.emitCustomEvent(this, upgradedEventName, { bubbles: true }) } connectedCallback () { this.connected = true if (!this._init) { this._init = true const shadow = this.shadowRoot if (shadow) { dom.addEventListener(shadow, upgradedEventName, event => { this.setState(this.state, true) event.stopPropagation() }) } /** * @type {Object} */ const startState = this.state || object.assign({}, defaultState) if (attrs) { for (const key in attrs) { const normalizedKey = string.fromCamelCase(key, '-') const val = parseAttrVal(this.getAttribute(normalizedKey), attrs[key]) if (val) { startState[key] = val } } } // add event listeners for (const key in listeners) { dom.addEventListener(shadow || this, key, event => { if (listeners[key](/** @type {CustomEvent} */ (event), this) !== false) { event.stopPropagation() event.preventDefault() return false } }) } // first setState call this.state = null this.setState(startState) } dom.emitCustomEvent(/** @type {any} */ (this.shadowRoot || this), connectedEventName, { bubbles: true }) } disconnectedCallback () { this.connected = false dom.emitCustomEvent(/** @type {any} */ (this.shadowRoot || this), disconnectedEventName, { bubbles: true }) this.setState(null) } static get observedAttributes () { return object.keys(normalizedAttrs) } /** * @param {string} name * @param {string} oldVal * @param {string} newVal * * @private */ attributeChangedCallback (name, oldVal, newVal) { const curState = /** @type {any} */ (this.state) const camelAttrName = normalizedAttrs[name] const type = attrs[camelAttrName] const parsedVal = parseAttrVal(newVal, type) if (curState && (type !== 'json' || json.stringify(curState[camelAttrName]) !== newVal) && curState[camelAttrName] !== parsedVal && !number.isNaN(parsedVal)) { this.updateState({ [camelAttrName]: parsedVal }) } } /** * @param {any} stateUpdate */ updateState (stateUpdate) { this.setState(object.assign({}, this.state, stateUpdate)) } /** * @param {any} state */ setState (state, forceStateUpdates = false) { const prevState = this.state this.state = state if (this._init && (!func.equalityFlat(state, prevState) || forceStateUpdates)) { // fill slots if (state) { const slotElems = slots(state, prevState, this) for (const key in slotElems) { const slotContent = slotElems[key] if (this._slots[key] !== slotContent) { this._slots[key] = slotContent const currentSlots = /** @type {Array} */ (key !== 'default' ? array.from(dom.querySelectorAll(this, `[slot="${key}"]`)) : array.from(this.childNodes).filter(/** @param {any} child */ child => !dom.checkNodeType(child, dom.ELEMENT_NODE) || !child.hasAttribute('slot'))) currentSlots.slice(1).map(dom.remove) const nextSlot = dom.parseFragment(slotContent) if (key !== 'default') { array.from(nextSlot.children).forEach(c => c.setAttribute('slot', key)) } if (currentSlots.length > 0) { dom.replaceWith(currentSlots[0], nextSlot) } else { dom.appendChild(this, nextSlot) } } } } onStateChange(state, prevState, this) if (state != null) { this._childStates.forEach(cnf => { const d = cnf.d if (d.updateState) { d.updateState(cnf.s(state, this)) } }) } for (const key in attrs) { const normalizedKey = string.fromCamelCase(key, '-') if (state == null) { this.removeAttribute(normalizedKey) } else { const stateVal = state[key] const attrsType = attrs[key] if (!prevState || prevState[key] !== stateVal) { if (attrsType === 'bool') { if (stateVal) { this.setAttribute(normalizedKey, '') } else { this.removeAttribute(normalizedKey) } } else if (stateVal == null && (attrsType === 'string' || attrsType === 'number')) { this.removeAttribute(normalizedKey) } else { this.setAttribute(normalizedKey, encodeAttrVal(stateVal, attrsType)) } } } } } } } define(name, _Lib0Component) // @ts-ignore return _Lib0Component } /** * @param {function} definer function that defines a component when executed */ export const createComponentDefiner = definer => { /** * @type {any} */ let defined = null return () => { if (!defined) { defined = definer() } return defined } } export const defineListComponent = createComponentDefiner(() => { const ListItem = createComponent('lib0-list-item', { template: '', slots: state => ({ content: `
${state}
` }) }) return createComponent('lib0-list', { state: { list: /** @type {Array} */ ([]), Item: ListItem }, onStateChange: (state, prevState, component) => { if (state == null) { return } const { list = /** @type {Array} */ ([]), Item = ListItem } = state // @todo deep compare here by providing another parameter to simpleDiffArray let { index, remove, insert } = diff.simpleDiffArray(prevState ? prevState.list : [], list, func.equalityFlat) if (remove === 0 && insert.length === 0) { return } let child = /** @type {Lib0Component} */ (component.firstChild) while (index-- > 0) { child = /** @type {Lib0Component} */ (child.nextElementSibling) } let insertStart = 0 while (insertStart < insert.length && remove-- > 0) { // update existing state child.setState(insert[insertStart++]) child = /** @type {Lib0Component} */ (child.nextElementSibling) } while (remove-- > 0) { // remove remaining const prevChild = child child = /** @type {Lib0Component} */ (child.nextElementSibling) component.removeChild(prevChild) } // insert remaining component.insertBefore(dom.fragment(insert.slice(insertStart).map(/** @param {any} insState */ insState => { const el = new Item() el.setState(insState) return el })), child) } }) }) export const defineLazyLoadingComponent = createComponentDefiner(() => createComponent('lib0-lazy', { state: /** @type {{component:null|String,import:null|function():Promise,state:null|object}} */ ({ component: null, import: null, state: null }), attrs: { component: 'string' }, onStateChange: ({ component, state, import: getImport }, prevState, componentEl) => { if (component !== null) { if (getImport) { getImport() } if (!prevState || component !== prevState.component) { const el = /** @type {any} */ (dom.createElement(component)) componentEl.innerHTML = '' componentEl.insertBefore(el, null) } const el = /** @type {any} */ (componentEl.firstElementChild) // @todo generalize setting state and check if setState is defined if (el.setState) { el.setState(state) } } } })) lib0-0.2.93/conditions.js000066400000000000000000000003321457563604600151600ustar00rootroot00000000000000/** * Often used conditions. * * @module conditions */ /** * @template T * @param {T|null|undefined} v * @return {T|null} */ /* c8 ignore next */ export const undefinedToNull = v => v === undefined ? null : v lib0-0.2.93/crypto.test.js000066400000000000000000000344271457563604600153210ustar00rootroot00000000000000import * as time from './time.js' import * as jose from 'lib0/crypto/jwt' import * as aes from 'lib0/crypto/aes-gcm' import * as rsa from 'lib0/crypto/rsa-oaep' import * as ecdsa from 'lib0/crypto/ecdsa' import * as t from './testing.js' import * as prng from './prng.js' import * as json from './json.js' /** * @param {t.TestCase} _tc */ export const testJwt = async _tc => { const publicJwk = json.parse('{"key_ops":["verify"],"ext":true,"kty":"EC","x":"X7xPIgxWOHmKPv2PtrxGvaQUJ3LiUXQTVLExwPGBvanD3kAc9sEY9FwKxp8NVJ3j","y":"SIBaHLE1fvW_O-xOdzmbkU5M_M7cGHULZHrXOo_exCKBIbV2pJm3MH87gAXkZvoD","crv":"P-384"}') const privateJwk = json.parse('{"key_ops":["sign"],"ext":true,"kty":"EC","x":"X7xPIgxWOHmKPv2PtrxGvaQUJ3LiUXQTVLExwPGBvanD3kAc9sEY9FwKxp8NVJ3j","y":"SIBaHLE1fvW_O-xOdzmbkU5M_M7cGHULZHrXOo_exCKBIbV2pJm3MH87gAXkZvoD","crv":"P-384","d":"3BdPp9LSWOl36bJuwEIun14Y17dgV7AK8RKqOuTJAbG080kemtr7qmZgTiCE_K_o"}') const privateKey = await ecdsa.importKeyJwk(privateJwk) const publicKey = await ecdsa.importKeyJwk(publicJwk) const payload = { sub: '1234567890', name: 'John Doe', iat: 1516239022 } const jwt = await jose.encodeJwt(privateKey, payload) console.log('jwt: ', jwt) const verified = await jose.verifyJwt(publicKey, jwt) t.compare(verified.payload, payload) const unverified = jose.unsafeDecode(jwt) t.compare(verified, unverified) t.info('expired jwt should not parse') const payloadExpired = { sub: '1234567890', name: 'John Doe', iat: 1516239022, exp: time.getUnixTime() - 10 } const jwtExpired = await jose.encodeJwt(privateKey, payloadExpired) jose.unsafeDecode(jwtExpired) t.failsAsync(async () => { await jose.verifyJwt(publicKey, jwtExpired) }) } /** * @param {t.TestCase} tc */ export const testEncryption = async tc => { const secret = prng.word(tc.prng, 1, 30) const salt = tc.testName const data = prng.uint8Array(tc.prng, 400) await t.groupAsync('symmetric', async () => { const key = await aes.deriveKey(secret, salt) const enc = await aes.encrypt(key, data) const dec = await aes.decrypt(key, enc) t.compare(data, dec) }) await t.groupAsync('RSA', async () => { const keypair = await rsa.generateKeyPair() const enc = await rsa.encrypt(keypair.publicKey, data) const dec = await rsa.decrypt(keypair.privateKey, enc) t.compare(data, dec) }) await t.groupAsync('symmetric can fail', async () => { await t.failsAsync(async () => { const key = await aes.deriveKey(secret, salt) const key2 = await aes.deriveKey(secret + '2', salt) const enc = await aes.encrypt(key, data) const dec = await aes.decrypt(key2, enc) t.compare(data, dec) }) }) await t.groupAsync('asymmetric can fail', async () => { await t.failsAsync(async () => { const keypair = await rsa.generateKeyPair() const keypair2 = await rsa.generateKeyPair() const enc = await rsa.encrypt(keypair.privateKey, data) const dec = await rsa.decrypt(keypair2.publicKey, enc) t.compare(data, dec) }) }) } /** * @param {t.TestCase} tc */ export const testReapeatEncryption = async tc => { const secret = prng.word(tc.prng, 1, 30) const salt = prng.word(tc.prng) const data = prng.uint8Array(tc.prng, 1000000) /** * @type {any} */ let encrypted /** * @type {any} */ let decrypted /** * @type {CryptoKey} */ let key await t.measureTimeAsync('Key generation', async () => { key = await aes.deriveKey(secret, salt) }) await t.measureTimeAsync('Encryption', async () => { encrypted = await aes.encrypt(key, data) }) t.info(`Byte length: ${data.byteLength}b`) t.info(`Encrypted length: ${encrypted.length}b`) await t.measureTimeAsync('Decryption', async () => { decrypted = await aes.decrypt(key, encrypted) }) t.compare(data, decrypted) } /** * @param {t.TestCase} tc */ export const testImportExport = async tc => { const secret = prng.word(tc.prng, 1, 30) const salt = prng.word(tc.prng) await t.groupAsync('aes-gcm (jwk))', async () => { const key = await aes.deriveKey(secret, salt, { extractable: true }) const jwk = await aes.exportKeyJwk(key) const ekey = await aes.importKeyJwk(jwk, { extractable: true }) const ejwk = await aes.exportKeyJwk(ekey) t.compare(jwk, ejwk) }) await t.groupAsync('aes-gcm (raw))', async () => { const key = await aes.deriveKey(secret, salt, { extractable: true }) const raw = await aes.exportKeyRaw(key) const ekey = await aes.importKeyRaw(raw, { extractable: true }) const eraw = await aes.exportKeyRaw(ekey) t.compare(raw, eraw) }) await t.groupAsync('ecdsa (jwk))', async () => { const keypair = await ecdsa.generateKeyPair({ extractable: true }) const jwkPrivate = await ecdsa.exportKeyJwk(keypair.privateKey) const jwkPublic = await ecdsa.exportKeyJwk(keypair.publicKey) const ekeyPrivate = await ecdsa.importKeyJwk(jwkPrivate, { extractable: true }) const ekeyPublic = await ecdsa.importKeyJwk(jwkPublic, { extractable: true }) const ejwkPrivate = await ecdsa.exportKeyJwk(ekeyPrivate) const ejwkPublic = await ecdsa.exportKeyJwk(ekeyPublic) t.compare(jwkPrivate, ejwkPrivate) t.compare(jwkPublic, ejwkPublic) }) await t.groupAsync('ecdsa (raw))', async () => { const keypair = await ecdsa.generateKeyPair({ extractable: true, usages: ['sign', 'verify'] }) const rawPublic = await ecdsa.exportKeyRaw(keypair.publicKey) const ekeyPublic = await ecdsa.importKeyRaw(rawPublic, { extractable: true, usages: ['verify'] }) const erawPublic = await ecdsa.exportKeyRaw(ekeyPublic) t.compare(rawPublic, erawPublic) }) await t.groupAsync('rsa-oaep (jwk))', async () => { const keypair = await rsa.generateKeyPair({ extractable: true }) const jwkPrivate = await rsa.exportKeyJwk(keypair.privateKey) const jwkPublic = await rsa.exportKeyJwk(keypair.publicKey) const ekeyPrivate = await rsa.importKeyJwk(jwkPrivate, { extractable: true }) const ekeyPublic = await rsa.importKeyJwk(jwkPublic, { extractable: true }) const ejwkPrivate = await rsa.exportKeyJwk(ekeyPrivate) const ejwkPublic = await rsa.exportKeyJwk(ekeyPublic) t.compare(jwkPrivate, ejwkPrivate) t.compare(jwkPublic, ejwkPublic) }) } /** * @param {t.TestCase} tc */ export const testEncryptionPerformance = async tc => { const N = 1000 const BLen = 1000 const secret = prng.word(tc.prng, 1, 30) const salt = prng.word(tc.prng) /** * @type {CryptoKey} */ let key await t.measureTimeAsync('Key generation', async () => { key = await aes.deriveKey(secret, salt) }) /** * @type {Array} */ const data = [] for (let i = 0; i < N; i++) { data.push(prng.uint8Array(tc.prng, BLen)) } /** * @type {Array} */ const encryptedData = [] await t.measureTimeAsync(`Encrypt ${N / 1000}k blocks of size ${BLen}byte`, async () => { for (let i = 0; i < data.length; i++) { encryptedData.push(await aes.encrypt(key, data[i])) } }) /** * @type {Array} */ const decryptedData = [] await t.measureTimeAsync(`Decrypt ${N / 1000}k blocks of size ${BLen}byte`, async () => { for (let i = 0; i < encryptedData.length; i++) { decryptedData.push(await aes.decrypt(key, encryptedData[i])) } }) t.compare(data, decryptedData) } /** * @param {t.TestCase} _tc */ export const testConsistentKeyGeneration = async _tc => { await t.groupAsync('Key generation (AES))', async () => { const secret = 'qfycncpxhjktawlqkhc' const salt = 'my nonce' const expectedJwk = { key_ops: ['decrypt', 'encrypt'], kty: 'oct', k: 'psAqoMh9apefdr8y1tdbNMVTLxb-tFekEFipYIOX5n8', alg: 'A256GCM' } const key = await aes.deriveKey(secret, salt, { extractable: true }) const jwk = await aes.exportKeyJwk(key) delete jwk.ext jwk.key_ops?.sort() t.compare(jwk, expectedJwk) }) await t.groupAsync('key generation (ECDSA))', async () => { const jwkPublic = { key_ops: ['verify'], ext: true, kty: 'EC', x: 'zfklq8SI_XEZlBawiRmkuv1vwPqGXd456SAHvv_aH4_4v17qcnmFkChaRqCGgXKo', y: 'YAt3r7fiB6j_RVKpcnokpEXE6r7XTcOzUxb3VmvkYcC5WfqDi6S7E3HzifOjeYjI', crv: 'P-384' } const jwkPrivate = { key_ops: ['sign'], ext: true, kty: 'EC', x: 'zfklq8SI_XEZlBawiRmkuv1vwPqGXd456SAHvv_aH4_4v17qcnmFkChaRqCGgXKo', y: 'YAt3r7fiB6j_RVKpcnokpEXE6r7XTcOzUxb3VmvkYcC5WfqDi6S7E3HzifOjeYjI', crv: 'P-384', d: 'z1bahlvHj7dWLYGr_oGGSNT_o01JdmnOoG79vLEm2LCG5Arl-4UZPFKpIWhmnZZU' } const privateKey = await ecdsa.importKeyJwk(jwkPrivate, { extractable: true, usages: ['sign'] }) const publicKey = await ecdsa.importKeyJwk(jwkPublic, { extractable: true, usages: ['verify'] }) const exportedPublic = await ecdsa.exportKeyJwk(publicKey) const exportedPrivate = await ecdsa.exportKeyJwk(privateKey) delete exportedPublic.alg // for firefox compat delete exportedPrivate.alg // for firefox compat t.compare(jwkPublic, /** @type {any} */ (exportedPublic)) t.compare(jwkPrivate, /** @type {any} */ (exportedPrivate)) }) await t.groupAsync('key generation (RSA))', async () => { const jwkPublic = { key_ops: ['encrypt'], ext: true, kty: 'RSA', n: '2M17DkhswailS2qGzpuoyGFxk193-18OSgNGVYAB_rPk8gN0CdLCyW8z0Ya8LpBgLNDht7vPdsXOZOAoIvgJZvt3mSGSJQj-gFy8l9DTQrt3rtiKKCX_tGmLeN0eN-TMemIG8Wkd8Ebqpkj-mQAgIPdsukO0capWyr0RM3C7ByESb3Fk0Z9v9p06kk8BuTz-5B5yyeDI_tfALRo_ZaWAN1rCXF8Sdjoipw1OUVJjzBLDDZ_znAPkZgTl9IZJMs_l97kv4OEl4xpq4lgobBYkwa8fWmqSljC9z99hws1hcgc3OQo43vbGT_80DW5GbeAWXAdoMJjqpG9Slc-ZfRVGen9DZpTXHkFmcmI9KrNhc_HFdWQPIUc7BUkX06nW5T_-t1OcgkMzFJhtxhlh8Ah7KcOJ7f2P7kziCr8uj7MPPz05-FbFJfUJgbQgX9wo4JbwtLKtqtTqfRaI8lCCbx_k8NC_RIi4YMqtF31O1WGEL7TX4pSJyPSLnkWzniJmG_v1_CAFhenoNZPgNY_kin-5DPtFVrpEkaJygySEs1JhII33ecJXc951yAEuEiaFdqcHNVryhesDJo6P4UfvuaGW6UQW2o6g0uNXC71emvK7dWt4K1r9QYH0SslHD-amJvgbFestBxAR4M_ldeTxQMRXMnYU-iCzgwKaPvSLSSNn8kk', e: 'AQAB', alg: 'RSA-OAEP-256' } const jwkPrivate = { key_ops: ['decrypt'], ext: true, kty: 'RSA', n: '2M17DkhswailS2qGzpuoyGFxk193-18OSgNGVYAB_rPk8gN0CdLCyW8z0Ya8LpBgLNDht7vPdsXOZOAoIvgJZvt3mSGSJQj-gFy8l9DTQrt3rtiKKCX_tGmLeN0eN-TMemIG8Wkd8Ebqpkj-mQAgIPdsukO0capWyr0RM3C7ByESb3Fk0Z9v9p06kk8BuTz-5B5yyeDI_tfALRo_ZaWAN1rCXF8Sdjoipw1OUVJjzBLDDZ_znAPkZgTl9IZJMs_l97kv4OEl4xpq4lgobBYkwa8fWmqSljC9z99hws1hcgc3OQo43vbGT_80DW5GbeAWXAdoMJjqpG9Slc-ZfRVGen9DZpTXHkFmcmI9KrNhc_HFdWQPIUc7BUkX06nW5T_-t1OcgkMzFJhtxhlh8Ah7KcOJ7f2P7kziCr8uj7MPPz05-FbFJfUJgbQgX9wo4JbwtLKtqtTqfRaI8lCCbx_k8NC_RIi4YMqtF31O1WGEL7TX4pSJyPSLnkWzniJmG_v1_CAFhenoNZPgNY_kin-5DPtFVrpEkaJygySEs1JhII33ecJXc951yAEuEiaFdqcHNVryhesDJo6P4UfvuaGW6UQW2o6g0uNXC71emvK7dWt4K1r9QYH0SslHD-amJvgbFestBxAR4M_ldeTxQMRXMnYU-iCzgwKaPvSLSSNn8kk', e: 'AQAB', d: 'IXXVKtHStzDSvLOm3_b2MwxBqH_GLMLxmaACbZUMDxtZ7QrLZe4Op2LE70Q0LEZBZv6grOgM5O_dItnVrU_1Y4d8Czjl2AFuBgb0tG7uVrudhRwLy-vRbdlm793F7tUebzpMiAWz29mWGC6RMgaVmYDrrvO0GGGJyPsqecie6kLDGEq6nKpWxWEOy8tAdi5hHc6f42MQolmkt1FNvN-wzNpHv6niAjfcEUbkciNHyRSRi2YcWv_eyEAJSEFRcB4rXUhKM2g282NTC7aUxoRvDSrR_p-5b_n7JudQV0MLabs4sqej5LAsJec6nrgI9qbsdyz4JPKfhDFfiu-Mvi6tFFdv5-e15QywqJIKgzHDxREcWXA2EF5CAfeD2rt0k1EsaoBUVII3k6VXH3Ufz5-94GialafmDdWLwr_kusT43nFnNpBVLlscyi1hnLwmbyHQo7Le0bt8umFMfFRqGiFCkkOdtUecH6xt1wfvC0vqT2aSppo8DQpYzQWxfDnzhW4-8yDAjmwOkeAcH3YDpMrxmUtLm6CXGeYrcyeoPwQz39ezE8m5BqqdLTdD8IHiypon_0bdyOkvENVCcm9fcMU3isx57w-4Higrn-yvS-v-17jouPFgYmWiu9uvKQNvvszhPnxmpTcil3PXXW25JLyXPb3NEQOa51o3lFFUH-03pWE', p: '71aSuZ1fH-Ly4NSAqSWSc_gMjXVR_QbapyBlB0tr7b1SiJRwS_Yqv1jNZfFSHbx63YNaJDDi5pJaENbYYoKSHvA8--fZJ6GbVIzYSOnntW__nDz22PwpIyoJVXqXqsRygc3DX0XiFM0pQ2QaLoZzvdxv2RgBMTdMWnhelk3SA6oVe3mMarSh316PCjE5TDApWlGCFHFEHji8FDXsYKEfgXRrjSIEJW9RbufIIAdeTH2guesezguVVPoQQ9Y0KtjQ3vsCxjrHdvAzK8IqWikOEJgJZZkBNXenPFwNkZwadjz9EpR-FVwRWKRiVECjTjy-xzD_LenISpgk_SN4evfTyw', q: '5-VI90pui_C0A5eBtL8vDVLYfahw5KJxqfxeqBjEbiL_GK2B59BN4f0GbfDeLm-T8J0hYtPdR0qwLv87sT318_Y-gT2T7qrr2lEyudbQt2SgNgfWe2VwakESIN3DU7voUV-ykSXoqPqjOwunSAq-cI1fGmg29tBArciHiuLq9n-QzK3geaz7NHSz5jQ5jA71HtZdFE85VncYUFb6MZetEJ9htbpjJPvfAVagzv9dedsTV3f8gC6XH1Ck5a40SYO2suAoQoOGdJc8Yfsd5ssjdqvYrQF7M9ttqe5VFH86Jnsz1dVwq65avjgRWBmBCWgk_nX_QDzuoEcduRk4w-sXuw', dp: '3Y96QqRBlCYnCyUNeghTHFIrZKSP4rl-nqppfChA4JObnN41Wsym4_4UHuQYTXjXEMrxHoG2-xXOlLofFIqlNEjXW6dUqtB7F_lOm6kVHCxzJzJ0nYhJmMjoXR4g2zAChNFzpHXwBaurIDzB1AIZkVBIpmMHb4UuhK3bei7OVSAVxPlPmNRg6YQCzL-muDX5gifkUIJOOd_xlJAao5VkshWRHtS3m-QCMbYV2DiZ_htqN9JF8R5d_o2DkxjvsB6ItXMPLWzqi9tus3qKdG5_G7NzN3891D5RLZpV4U7uXDi3WoTmd2WElVePw0kXJG0tev6Lq_g4t31C-Kfmd4eGow', dq: '1Mjns0JxPaeZBtK3CguEOU2TqXouXR1R_xC8KrLPS-CBAzvyv6u8S2nJxIgI18M6lMcaI30Uxp4aHIXHWFPqo_mIUT8XxyC_Woy3Zx9eVWnYOLvoa0IhbN5YrB_RY7xA6KpPSDDo1GVn8n42-Twik1Slt615AfEF6HDhLugZgiZ7z9Sc7gl0WCXeDZZOV95BvhIlRsWLb3PIs6-b1HXBMEePeRmWcBFOCARdepOISpBjpxdKcrRNp0ZwiPDYubxKoMhfKOlXLxS3K5EpVuV_nR0CrX12d5cZgZxYJX649SaH4ecAhAhw66q2_4gnh2Iwz-2mUmOW8ytOctJZ7CyEkQ', qi: 'ggdoo4EYrtNl-sSNwF8PjtmJxv8GA5df__iKMnN0A5yHlbcdfBH4zCGoDQW8K4QKAsBldI7zkuYx08VCYrSQL6xkowXctjXprxkROHsWxN8cDC2kg6lUOO88GeRT9s96wgzZoHcPNxTrZiJDsAGgPr4OdiGk1feu3BpWPitSZL2TnWfEqlmIdq99-0wTCgn_-kzXrRnZEAOsEyZk2FXwBA7d22fJ5fbyvVQ98pj7ih-ieKUZWyzR5E-EEYDfQjJ8A49F1ejcNSIr2zPb7vCiEpdtLlRak9dfuetmYNfrqaVhAKLqzE2ErtZ6ZLfvJcp9d4ZHsbppR8WckKEyA-L7_A', alg: 'RSA-OAEP-256' } const privateKey = await rsa.importKeyJwk(jwkPrivate, { extractable: true, usages: ['decrypt'] }) const publicKey = await rsa.importKeyJwk(jwkPublic, { extractable: true, usages: ['encrypt'] }) const exportedPublic = await rsa.exportKeyJwk(publicKey) const exportedPrivate = await rsa.exportKeyJwk(privateKey) t.compare(jwkPublic, /** @type {any} */ (exportedPublic)) t.compare(jwkPrivate, /** @type {any} */ (exportedPrivate)) }) } /** * @param {t.TestCase} tc */ export const testSigning = async tc => { await t.measureTimeAsync('time to sign & verify 2 messages (ECDSA))', async () => { const keypair = await ecdsa.generateKeyPair({ extractable: true }) const keypair2 = await ecdsa.generateKeyPair({ extractable: true }) const data = prng.uint8Array(tc.prng, 100) const signature = await ecdsa.sign(keypair.privateKey, data) const result = await ecdsa.verify(keypair.publicKey, signature, data) const result2 = await ecdsa.verify(keypair2.publicKey, signature, data) t.assert(result, 'verification works using the correct key') t.assert(!result2, 'verification fails using the incorrect key') }) } lib0-0.2.93/crypto/000077500000000000000000000000001457563604600137735ustar00rootroot00000000000000lib0-0.2.93/crypto/aes-gcm.js000066400000000000000000000065341457563604600156550ustar00rootroot00000000000000/** * AES-GCM is a symmetric key for encryption */ import * as encoding from '../encoding.js' import * as decoding from '../decoding.js' import * as webcrypto from 'lib0/webcrypto' import * as string from '../string.js' export { exportKeyJwk, exportKeyRaw } from './common.js' /** * @typedef {Array<'encrypt'|'decrypt'>} Usages */ /** * @type {Usages} */ const defaultUsages = ['encrypt', 'decrypt'] /** * @param {CryptoKey} key * @param {Uint8Array} data */ export const encrypt = (key, data) => { const iv = webcrypto.getRandomValues(new Uint8Array(16)) // 92bit is enough. 128bit is recommended if space is not an issue. return webcrypto.subtle.encrypt( { name: 'AES-GCM', iv }, key, data ).then(cipher => { const encryptedDataEncoder = encoding.createEncoder() // iv may be sent in the clear to the other peers encoding.writeUint8Array(encryptedDataEncoder, iv) encoding.writeVarUint8Array(encryptedDataEncoder, new Uint8Array(cipher)) return encoding.toUint8Array(encryptedDataEncoder) }) } /** * @experimental The API is not final! * * Decrypt some data using AES-GCM method. * * @param {CryptoKey} key * @param {Uint8Array} data * @return {PromiseLike} decrypted buffer */ export const decrypt = (key, data) => { const dataDecoder = decoding.createDecoder(data) const iv = decoding.readUint8Array(dataDecoder, 16) const cipher = decoding.readVarUint8Array(dataDecoder) return webcrypto.subtle.decrypt( { name: 'AES-GCM', iv }, key, cipher ).then(data => new Uint8Array(data)) } const aesAlgDef = { name: 'AES-GCM', length: 256 } /** * @param {any} jwk * @param {Object} opts * @param {Usages} [opts.usages] * @param {boolean} [opts.extractable] */ export const importKeyJwk = (jwk, { usages, extractable = false } = {}) => { if (usages == null) { /* c8 ignore next */ usages = jwk.key_ops || defaultUsages } return webcrypto.subtle.importKey('jwk', jwk, 'AES-GCM', extractable, /** @type {Usages} */ (usages)) } /** * Only suited for importing public keys. * * @param {Uint8Array} raw * @param {Object} opts * @param {Usages} [opts.usages] * @param {boolean} [opts.extractable] */ export const importKeyRaw = (raw, { usages = defaultUsages, extractable = false } = {}) => webcrypto.subtle.importKey('raw', raw, aesAlgDef, extractable, /** @type {Usages} */ (usages)) /** * @param {Uint8Array | string} data */ /* c8 ignore next */ const toBinary = data => typeof data === 'string' ? string.encodeUtf8(data) : data /** * @experimental The API is not final! * * Derive an symmetric key using the Password-Based-Key-Derivation-Function-2. * * @param {Uint8Array|string} secret * @param {Uint8Array|string} salt * @param {Object} opts * @param {boolean} [opts.extractable] * @param {Usages} [opts.usages] */ export const deriveKey = (secret, salt, { extractable = false, usages = defaultUsages } = {}) => webcrypto.subtle.importKey( 'raw', toBinary(secret), 'PBKDF2', false, ['deriveKey'] ).then(keyMaterial => webcrypto.subtle.deriveKey( { name: 'PBKDF2', salt: toBinary(salt), // NIST recommends at least 64 bits iterations: 600000, // OWASP recommends 600k iterations hash: 'SHA-256' }, keyMaterial, aesAlgDef, extractable, usages ) ) lib0-0.2.93/crypto/common.js000066400000000000000000000006751457563604600156310ustar00rootroot00000000000000import * as webcrypto from 'lib0/webcrypto' /** * @param {CryptoKey} key */ export const exportKeyJwk = async key => { const jwk = await webcrypto.subtle.exportKey('jwk', key) jwk.key_ops = key.usages return jwk } /** * Only suited for exporting public keys. * * @param {CryptoKey} key * @return {Promise} */ export const exportKeyRaw = key => webcrypto.subtle.exportKey('raw', key).then(key => new Uint8Array(key)) lib0-0.2.93/crypto/ecdsa.js000066400000000000000000000041401457563604600154070ustar00rootroot00000000000000/** * ECDSA is an asymmetric key for signing */ import * as webcrypto from 'lib0/webcrypto' export { exportKeyJwk, exportKeyRaw } from './common.js' /** * @typedef {Array<'sign'|'verify'>} Usages */ /** * @type {Usages} */ const defaultUsages = ['sign', 'verify'] const defaultSignAlgorithm = { name: 'ECDSA', hash: 'SHA-384' } /** * @experimental The API is not final! * * Sign a message * * @param {CryptoKey} key * @param {Uint8Array} data * @return {PromiseLike} signature */ export const sign = (key, data) => webcrypto.subtle.sign( defaultSignAlgorithm, key, data ).then(signature => new Uint8Array(signature)) /** * @experimental The API is not final! * * Sign a message * * @param {CryptoKey} key * @param {Uint8Array} signature * @param {Uint8Array} data * @return {PromiseLike} signature */ export const verify = (key, signature, data) => webcrypto.subtle.verify( defaultSignAlgorithm, key, signature, data ) const defaultKeyAlgorithm = { name: 'ECDSA', namedCurve: 'P-384' } /* c8 ignore next */ /** * @param {Object} opts * @param {boolean} [opts.extractable] * @param {Usages} [opts.usages] */ export const generateKeyPair = ({ extractable = false, usages = defaultUsages } = {}) => webcrypto.subtle.generateKey( defaultKeyAlgorithm, extractable, usages ) /** * @param {any} jwk * @param {Object} opts * @param {boolean} [opts.extractable] * @param {Usages} [opts.usages] */ export const importKeyJwk = (jwk, { extractable = false, usages } = {}) => { if (usages == null) { /* c8 ignore next 2 */ usages = jwk.key_ops || defaultUsages } return webcrypto.subtle.importKey('jwk', jwk, defaultKeyAlgorithm, extractable, /** @type {Usages} */ (usages)) } /** * Only suited for importing public keys. * * @param {any} raw * @param {Object} opts * @param {boolean} [opts.extractable] * @param {Usages} [opts.usages] */ export const importKeyRaw = (raw, { extractable = false, usages = defaultUsages } = {}) => webcrypto.subtle.importKey('raw', raw, defaultKeyAlgorithm, extractable, usages) lib0-0.2.93/crypto/jwt.js000066400000000000000000000037541457563604600151460ustar00rootroot00000000000000import * as error from '../error.js' import * as buffer from '../buffer.js' import * as string from '../string.js' import * as json from '../json.js' import * as ecdsa from '../crypto/ecdsa.js' import * as time from '../time.js' /** * @param {Object} data */ const _stringify = data => buffer.toBase64UrlEncoded(string.encodeUtf8(json.stringify(data))) /** * @param {string} base64url */ const _parse = base64url => json.parse(string.decodeUtf8(buffer.fromBase64UrlEncoded(base64url))) /** * @param {CryptoKey} privateKey * @param {Object} payload */ export const encodeJwt = (privateKey, payload) => { const { name: algName, namedCurve: algCurve } = /** @type {any} */ (privateKey.algorithm) /* c8 ignore next 3 */ if (algName !== 'ECDSA' || algCurve !== 'P-384') { error.unexpectedCase() } const header = { alg: 'ES384', typ: 'JWT' } const jwt = _stringify(header) + '.' + _stringify(payload) return ecdsa.sign(privateKey, string.encodeUtf8(jwt)).then(signature => jwt + '.' + buffer.toBase64UrlEncoded(signature) ) } /** * @param {CryptoKey} publicKey * @param {string} jwt */ export const verifyJwt = async (publicKey, jwt) => { const [headerBase64, payloadBase64, signatureBase64] = jwt.split('.') const verified = await ecdsa.verify(publicKey, buffer.fromBase64UrlEncoded(signatureBase64), string.encodeUtf8(headerBase64 + '.' + payloadBase64)) /* c8 ignore next 3 */ if (!verified) { throw new Error('Invalid JWT') } const payload = _parse(payloadBase64) if (payload.exp != null && time.getUnixTime() > payload.exp) { throw new Error('Expired JWT') } return { header: _parse(headerBase64), payload } } /** * Decode a jwt without verifying it. Probably a bad idea to use this. Only use if you know the jwt was already verified! * * @param {string} jwt */ export const unsafeDecode = jwt => { const [headerBase64, payloadBase64] = jwt.split('.') return { header: _parse(headerBase64), payload: _parse(payloadBase64) } } lib0-0.2.93/crypto/rsa-oaep.js000066400000000000000000000034541457563604600160460ustar00rootroot00000000000000/** * RSA-OAEP is an asymmetric keypair used for encryption */ import * as webcrypto from 'lib0/webcrypto' export { exportKeyJwk } from './common.js' /** * @typedef {Array<'encrypt'|'decrypt'>} Usages */ /** * @type {Usages} */ const defaultUsages = ['encrypt', 'decrypt'] /** * Note that the max data size is limited by the size of the RSA key. * * @param {CryptoKey} key * @param {Uint8Array} data * @return {PromiseLike} */ export const encrypt = (key, data) => webcrypto.subtle.encrypt( { name: 'RSA-OAEP' }, key, data ).then(buf => new Uint8Array(buf)) /** * @experimental The API is not final! * * Decrypt some data using AES-GCM method. * * @param {CryptoKey} key * @param {Uint8Array} data * @return {PromiseLike} decrypted buffer */ export const decrypt = (key, data) => webcrypto.subtle.decrypt( { name: 'RSA-OAEP' }, key, data ).then(data => new Uint8Array(data)) /** * @param {Object} opts * @param {boolean} [opts.extractable] * @param {Usages} [opts.usages] * @return {Promise} */ export const generateKeyPair = ({ extractable = false, usages = defaultUsages } = {}) => webcrypto.subtle.generateKey( { name: 'RSA-OAEP', modulusLength: 4096, publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256' }, extractable, usages ) /** * @param {any} jwk * @param {Object} opts * @param {boolean} [opts.extractable] * @param {Usages} [opts.usages] */ export const importKeyJwk = (jwk, { extractable = false, usages } = {}) => { if (usages == null) { /* c8 ignore next */ usages = jwk.key_ops || defaultUsages } return webcrypto.subtle.importKey('jwk', jwk, { name: 'RSA-OAEP', hash: 'SHA-256' }, extractable, /** @type {Usages} */ (usages)) } lib0-0.2.93/decoding.js000066400000000000000000000423611457563604600145730ustar00rootroot00000000000000/** * Efficient schema-less binary decoding with support for variable length encoding. * * Use [lib0/decoding] with [lib0/encoding]. Every encoding function has a corresponding decoding function. * * Encodes numbers in little-endian order (least to most significant byte order) * and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/) * which is also used in Protocol Buffers. * * ```js * // encoding step * const encoder = encoding.createEncoder() * encoding.writeVarUint(encoder, 256) * encoding.writeVarString(encoder, 'Hello world!') * const buf = encoding.toUint8Array(encoder) * ``` * * ```js * // decoding step * const decoder = decoding.createDecoder(buf) * decoding.readVarUint(decoder) // => 256 * decoding.readVarString(decoder) // => 'Hello world!' * decoding.hasContent(decoder) // => false - all data is read * ``` * * @module decoding */ import * as binary from './binary.js' import * as math from './math.js' import * as number from './number.js' import * as string from './string.js' import * as error from './error.js' import * as encoding from './encoding.js' const errorUnexpectedEndOfArray = error.create('Unexpected end of array') const errorIntegerOutOfRange = error.create('Integer out of Range') /** * A Decoder handles the decoding of an Uint8Array. */ export class Decoder { /** * @param {Uint8Array} uint8Array Binary data to decode */ constructor (uint8Array) { /** * Decoding target. * * @type {Uint8Array} */ this.arr = uint8Array /** * Current decoding position. * * @type {number} */ this.pos = 0 } } /** * @function * @param {Uint8Array} uint8Array * @return {Decoder} */ export const createDecoder = uint8Array => new Decoder(uint8Array) /** * @function * @param {Decoder} decoder * @return {boolean} */ export const hasContent = decoder => decoder.pos !== decoder.arr.length /** * Clone a decoder instance. * Optionally set a new position parameter. * * @function * @param {Decoder} decoder The decoder instance * @param {number} [newPos] Defaults to current position * @return {Decoder} A clone of `decoder` */ export const clone = (decoder, newPos = decoder.pos) => { const _decoder = createDecoder(decoder.arr) _decoder.pos = newPos return _decoder } /** * Create an Uint8Array view of the next `len` bytes and advance the position by `len`. * * Important: The Uint8Array still points to the underlying ArrayBuffer. Make sure to discard the result as soon as possible to prevent any memory leaks. * Use `buffer.copyUint8Array` to copy the result into a new Uint8Array. * * @function * @param {Decoder} decoder The decoder instance * @param {number} len The length of bytes to read * @return {Uint8Array} */ export const readUint8Array = (decoder, len) => { const view = new Uint8Array(decoder.arr.buffer, decoder.pos + decoder.arr.byteOffset, len) decoder.pos += len return view } /** * Read variable length Uint8Array. * * Important: The Uint8Array still points to the underlying ArrayBuffer. Make sure to discard the result as soon as possible to prevent any memory leaks. * Use `buffer.copyUint8Array` to copy the result into a new Uint8Array. * * @function * @param {Decoder} decoder * @return {Uint8Array} */ export const readVarUint8Array = decoder => readUint8Array(decoder, readVarUint(decoder)) /** * Read the rest of the content as an ArrayBuffer * @function * @param {Decoder} decoder * @return {Uint8Array} */ export const readTailAsUint8Array = decoder => readUint8Array(decoder, decoder.arr.length - decoder.pos) /** * Skip one byte, jump to the next position. * @function * @param {Decoder} decoder The decoder instance * @return {number} The next position */ export const skip8 = decoder => decoder.pos++ /** * Read one byte as unsigned integer. * @function * @param {Decoder} decoder The decoder instance * @return {number} Unsigned 8-bit integer */ export const readUint8 = decoder => decoder.arr[decoder.pos++] /** * Read 2 bytes as unsigned integer. * * @function * @param {Decoder} decoder * @return {number} An unsigned integer. */ export const readUint16 = decoder => { const uint = decoder.arr[decoder.pos] + (decoder.arr[decoder.pos + 1] << 8) decoder.pos += 2 return uint } /** * Read 4 bytes as unsigned integer. * * @function * @param {Decoder} decoder * @return {number} An unsigned integer. */ export const readUint32 = decoder => { const uint = (decoder.arr[decoder.pos] + (decoder.arr[decoder.pos + 1] << 8) + (decoder.arr[decoder.pos + 2] << 16) + (decoder.arr[decoder.pos + 3] << 24)) >>> 0 decoder.pos += 4 return uint } /** * Read 4 bytes as unsigned integer in big endian order. * (most significant byte first) * * @function * @param {Decoder} decoder * @return {number} An unsigned integer. */ export const readUint32BigEndian = decoder => { const uint = (decoder.arr[decoder.pos + 3] + (decoder.arr[decoder.pos + 2] << 8) + (decoder.arr[decoder.pos + 1] << 16) + (decoder.arr[decoder.pos] << 24)) >>> 0 decoder.pos += 4 return uint } /** * Look ahead without incrementing the position * to the next byte and read it as unsigned integer. * * @function * @param {Decoder} decoder * @return {number} An unsigned integer. */ export const peekUint8 = decoder => decoder.arr[decoder.pos] /** * Look ahead without incrementing the position * to the next byte and read it as unsigned integer. * * @function * @param {Decoder} decoder * @return {number} An unsigned integer. */ export const peekUint16 = decoder => decoder.arr[decoder.pos] + (decoder.arr[decoder.pos + 1] << 8) /** * Look ahead without incrementing the position * to the next byte and read it as unsigned integer. * * @function * @param {Decoder} decoder * @return {number} An unsigned integer. */ export const peekUint32 = decoder => ( decoder.arr[decoder.pos] + (decoder.arr[decoder.pos + 1] << 8) + (decoder.arr[decoder.pos + 2] << 16) + (decoder.arr[decoder.pos + 3] << 24) ) >>> 0 /** * Read unsigned integer (32bit) with variable length. * 1/8th of the storage is used as encoding overhead. * * numbers < 2^7 is stored in one bytlength * * numbers < 2^14 is stored in two bylength * * @function * @param {Decoder} decoder * @return {number} An unsigned integer.length */ export const readVarUint = decoder => { let num = 0 let mult = 1 const len = decoder.arr.length while (decoder.pos < len) { const r = decoder.arr[decoder.pos++] // num = num | ((r & binary.BITS7) << len) num = num + (r & binary.BITS7) * mult // shift $r << (7*#iterations) and add it to num mult *= 128 // next iteration, shift 7 "more" to the left if (r < binary.BIT8) { return num } /* c8 ignore start */ if (num > number.MAX_SAFE_INTEGER) { throw errorIntegerOutOfRange } /* c8 ignore stop */ } throw errorUnexpectedEndOfArray } /** * Read signed integer (32bit) with variable length. * 1/8th of the storage is used as encoding overhead. * * numbers < 2^7 is stored in one bytlength * * numbers < 2^14 is stored in two bylength * @todo This should probably create the inverse ~num if number is negative - but this would be a breaking change. * * @function * @param {Decoder} decoder * @return {number} An unsigned integer.length */ export const readVarInt = decoder => { let r = decoder.arr[decoder.pos++] let num = r & binary.BITS6 let mult = 64 const sign = (r & binary.BIT7) > 0 ? -1 : 1 if ((r & binary.BIT8) === 0) { // don't continue reading return sign * num } const len = decoder.arr.length while (decoder.pos < len) { r = decoder.arr[decoder.pos++] // num = num | ((r & binary.BITS7) << len) num = num + (r & binary.BITS7) * mult mult *= 128 if (r < binary.BIT8) { return sign * num } /* c8 ignore start */ if (num > number.MAX_SAFE_INTEGER) { throw errorIntegerOutOfRange } /* c8 ignore stop */ } throw errorUnexpectedEndOfArray } /** * Look ahead and read varUint without incrementing position * * @function * @param {Decoder} decoder * @return {number} */ export const peekVarUint = decoder => { const pos = decoder.pos const s = readVarUint(decoder) decoder.pos = pos return s } /** * Look ahead and read varUint without incrementing position * * @function * @param {Decoder} decoder * @return {number} */ export const peekVarInt = decoder => { const pos = decoder.pos const s = readVarInt(decoder) decoder.pos = pos return s } /** * We don't test this function anymore as we use native decoding/encoding by default now. * Better not modify this anymore.. * * Transforming utf8 to a string is pretty expensive. The code performs 10x better * when String.fromCodePoint is fed with all characters as arguments. * But most environments have a maximum number of arguments per functions. * For effiency reasons we apply a maximum of 10000 characters at once. * * @function * @param {Decoder} decoder * @return {String} The read String. */ /* c8 ignore start */ export const _readVarStringPolyfill = decoder => { let remainingLen = readVarUint(decoder) if (remainingLen === 0) { return '' } else { let encodedString = String.fromCodePoint(readUint8(decoder)) // remember to decrease remainingLen if (--remainingLen < 100) { // do not create a Uint8Array for small strings while (remainingLen--) { encodedString += String.fromCodePoint(readUint8(decoder)) } } else { while (remainingLen > 0) { const nextLen = remainingLen < 10000 ? remainingLen : 10000 // this is dangerous, we create a fresh array view from the existing buffer const bytes = decoder.arr.subarray(decoder.pos, decoder.pos + nextLen) decoder.pos += nextLen // Starting with ES5.1 we can supply a generic array-like object as arguments encodedString += String.fromCodePoint.apply(null, /** @type {any} */ (bytes)) remainingLen -= nextLen } } return decodeURIComponent(escape(encodedString)) } } /* c8 ignore stop */ /** * @function * @param {Decoder} decoder * @return {String} The read String */ export const _readVarStringNative = decoder => /** @type any */ (string.utf8TextDecoder).decode(readVarUint8Array(decoder)) /** * Read string of variable length * * varUint is used to store the length of the string * * @function * @param {Decoder} decoder * @return {String} The read String * */ /* c8 ignore next */ export const readVarString = string.utf8TextDecoder ? _readVarStringNative : _readVarStringPolyfill /** * @param {Decoder} decoder * @return {Uint8Array} */ export const readTerminatedUint8Array = decoder => { const encoder = encoding.createEncoder() let b while (true) { b = readUint8(decoder) if (b === 0) { return encoding.toUint8Array(encoder) } if (b === 1) { b = readUint8(decoder) } encoding.write(encoder, b) } } /** * @param {Decoder} decoder * @return {string} */ export const readTerminatedString = decoder => string.decodeUtf8(readTerminatedUint8Array(decoder)) /** * Look ahead and read varString without incrementing position * * @function * @param {Decoder} decoder * @return {string} */ export const peekVarString = decoder => { const pos = decoder.pos const s = readVarString(decoder) decoder.pos = pos return s } /** * @param {Decoder} decoder * @param {number} len * @return {DataView} */ export const readFromDataView = (decoder, len) => { const dv = new DataView(decoder.arr.buffer, decoder.arr.byteOffset + decoder.pos, len) decoder.pos += len return dv } /** * @param {Decoder} decoder */ export const readFloat32 = decoder => readFromDataView(decoder, 4).getFloat32(0, false) /** * @param {Decoder} decoder */ export const readFloat64 = decoder => readFromDataView(decoder, 8).getFloat64(0, false) /** * @param {Decoder} decoder */ export const readBigInt64 = decoder => /** @type {any} */ (readFromDataView(decoder, 8)).getBigInt64(0, false) /** * @param {Decoder} decoder */ export const readBigUint64 = decoder => /** @type {any} */ (readFromDataView(decoder, 8)).getBigUint64(0, false) /** * @type {Array} */ const readAnyLookupTable = [ decoder => undefined, // CASE 127: undefined decoder => null, // CASE 126: null readVarInt, // CASE 125: integer readFloat32, // CASE 124: float32 readFloat64, // CASE 123: float64 readBigInt64, // CASE 122: bigint decoder => false, // CASE 121: boolean (false) decoder => true, // CASE 120: boolean (true) readVarString, // CASE 119: string decoder => { // CASE 118: object const len = readVarUint(decoder) /** * @type {Object} */ const obj = {} for (let i = 0; i < len; i++) { const key = readVarString(decoder) obj[key] = readAny(decoder) } return obj }, decoder => { // CASE 117: array const len = readVarUint(decoder) const arr = [] for (let i = 0; i < len; i++) { arr.push(readAny(decoder)) } return arr }, readVarUint8Array // CASE 116: Uint8Array ] /** * @param {Decoder} decoder */ export const readAny = decoder => readAnyLookupTable[127 - readUint8(decoder)](decoder) /** * T must not be null. * * @template T */ export class RleDecoder extends Decoder { /** * @param {Uint8Array} uint8Array * @param {function(Decoder):T} reader */ constructor (uint8Array, reader) { super(uint8Array) /** * The reader */ this.reader = reader /** * Current state * @type {T|null} */ this.s = null this.count = 0 } read () { if (this.count === 0) { this.s = this.reader(this) if (hasContent(this)) { this.count = readVarUint(this) + 1 // see encoder implementation for the reason why this is incremented } else { this.count = -1 // read the current value forever } } this.count-- return /** @type {T} */ (this.s) } } export class IntDiffDecoder extends Decoder { /** * @param {Uint8Array} uint8Array * @param {number} start */ constructor (uint8Array, start) { super(uint8Array) /** * Current state * @type {number} */ this.s = start } /** * @return {number} */ read () { this.s += readVarInt(this) return this.s } } export class RleIntDiffDecoder extends Decoder { /** * @param {Uint8Array} uint8Array * @param {number} start */ constructor (uint8Array, start) { super(uint8Array) /** * Current state * @type {number} */ this.s = start this.count = 0 } /** * @return {number} */ read () { if (this.count === 0) { this.s += readVarInt(this) if (hasContent(this)) { this.count = readVarUint(this) + 1 // see encoder implementation for the reason why this is incremented } else { this.count = -1 // read the current value forever } } this.count-- return /** @type {number} */ (this.s) } } export class UintOptRleDecoder extends Decoder { /** * @param {Uint8Array} uint8Array */ constructor (uint8Array) { super(uint8Array) /** * @type {number} */ this.s = 0 this.count = 0 } read () { if (this.count === 0) { this.s = readVarInt(this) // if the sign is negative, we read the count too, otherwise count is 1 const isNegative = math.isNegativeZero(this.s) this.count = 1 if (isNegative) { this.s = -this.s this.count = readVarUint(this) + 2 } } this.count-- return /** @type {number} */ (this.s) } } export class IncUintOptRleDecoder extends Decoder { /** * @param {Uint8Array} uint8Array */ constructor (uint8Array) { super(uint8Array) /** * @type {number} */ this.s = 0 this.count = 0 } read () { if (this.count === 0) { this.s = readVarInt(this) // if the sign is negative, we read the count too, otherwise count is 1 const isNegative = math.isNegativeZero(this.s) this.count = 1 if (isNegative) { this.s = -this.s this.count = readVarUint(this) + 2 } } this.count-- return /** @type {number} */ (this.s++) } } export class IntDiffOptRleDecoder extends Decoder { /** * @param {Uint8Array} uint8Array */ constructor (uint8Array) { super(uint8Array) /** * @type {number} */ this.s = 0 this.count = 0 this.diff = 0 } /** * @return {number} */ read () { if (this.count === 0) { const diff = readVarInt(this) // if the first bit is set, we read more data const hasCount = diff & 1 this.diff = math.floor(diff / 2) // shift >> 1 this.count = 1 if (hasCount) { this.count = readVarUint(this) + 2 } } this.s += this.diff this.count-- return this.s } } export class StringDecoder { /** * @param {Uint8Array} uint8Array */ constructor (uint8Array) { this.decoder = new UintOptRleDecoder(uint8Array) this.str = readVarString(this.decoder) /** * @type {number} */ this.spos = 0 } /** * @return {string} */ read () { const end = this.spos + this.decoder.read() const res = this.str.slice(this.spos, end) this.spos = end return res } } lib0-0.2.93/deno.json000066400000000000000000000006311457563604600142730ustar00rootroot00000000000000{ "imports": { "isomorphic.js": "./node_modules/isomorphic.js/node.mjs", "lib0/logging": "./logging.node.js", "lib0/performance": "./performance.node.js", "lib0/crypto/aes-gcm": "./crypto/aes-gcm.js", "lib0/crypto/rsa-oaep": "./crypto/rsa-oaep.js", "lib0/crypto/ecdsa": "./crypto/ecdsa.js", "lib0/crypto/jwt": "./crypto/jwt.js", "lib0/webcrypto": "./webcrypto.deno.js" } } lib0-0.2.93/deno.lock000066400000000000000000000404571457563604600142640ustar00rootroot00000000000000{ "version": "2", "remote": { "https://deno.land/std@0.177.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", "https://deno.land/std@0.177.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", "https://deno.land/std@0.177.0/bytes/index_of_needle.ts": "65c939607df609374c4415598fa4dad04a2f14c4d98cd15775216f0aaf597f24", "https://deno.land/std@0.177.0/crypto/_wasm/lib/deno_std_wasm_crypto.generated.mjs": "5dedb7f9aa05f0e18ed017691c58df5f4686e4cbbd70368c6f896e5cca03f2b4", "https://deno.land/std@0.177.0/crypto/_wasm/mod.ts": "e2df88236fc061eac7a89e8cb0b97843f5280b08b2a990e473b7397a3e566003", "https://deno.land/std@0.177.0/crypto/timing_safe_equal.ts": "8d69ab611c67fe51b6127d97fcfb4d8e7d0e1b6b4f3e0cc4ab86744c3691f965", "https://deno.land/std@0.177.0/encoding/base64.ts": "7de04c2f8aeeb41453b09b186480be90f2ff357613b988e99fabb91d2eeceba1", "https://deno.land/std@0.177.0/encoding/base64url.ts": "3f1178f6446834457b16bfde8b559c1cd3481727fe384d3385e4a9995dc2d851", "https://deno.land/std@0.177.0/encoding/hex.ts": "50f8c95b52eae24395d3dfcb5ec1ced37c5fe7610ef6fffdcc8b0fdc38e3b32f", "https://deno.land/std@0.177.0/flags/mod.ts": "d1cdefa18472ef69858a17df5cf7c98445ed27ac10e1460183081303b0ebc270", "https://deno.land/std@0.177.0/node/_core.ts": "9a58c0ef98ee77e9b8fcc405511d1b37a003a705eb6a9b6e95f75434d8009adc", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/asn1.js/base/buffer.js": "c9364c761681134015ec8ba6f33b39c067d6e5dd59860d55face8d5be8522744", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/asn1.js/base/node.js": "8f7f23bfa300990bbd6db7e7395e9688b54a04e3eb2fab5cab9a9a72e26c525f", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/asn1.js/base/reporter.js": "788aec7662991da549e5f7f3edbc3e3d6c6cecabc894b18d1a705b0f204e06c3", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/asn1.js/constants/der.js": "57181db0519bb3864a6cdf4e7eb9bfeb1bf5f80605187fbe80e27083b473e367", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/asn1.js/decoders/der.js": "fdc4de98c9b0b59db169a2b225895741e2ab34b00e14315ac2ff5e389d6db16e", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/asn1.js/decoders/pem.js": "fd7f0072c193c82959fec0374f4fd3adf3f4ac38594fd404d66b3e8724107151", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/asn1.js/encoders/der.js": "137bc4f8fe66b9950c743025e199789e25342f791e2d52353ceb016ad2854b42", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/asn1.js/encoders/pem.js": "e43bc706973c4c27e1e2f96262daba3d38822cb10f5b494f6944c726ee655160", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/asn1.js/mod.js": "1f88293688296be7a6c735bd8ea39425f5b274b94db1d6b7968dddfb54ac9d37", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/bn.js/bn.js": "f3f3c1dae1aa55de9e6472af1d6bec5ccda4b4890ee5c52a90961137fe99564e", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/aes.js": "698e1ed386b7dff27b2d59fa1c75f506beceec96b78670a15a734e438c08f138", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/auth_cipher.js": "5c245b5685b066356a7c9529a3a441bf5f57823a6946ce1b0ef2e1af32bb76f4", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/decrypter.js": "39152b2b3409893b8548feeab7e5997ceb1595f31df0dedaf765708be8f025c0", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/encrypter.js": "f9cc703d5a7b5255999c1a3600fbf48ff564b65f827744877526803093ceebff", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/ghash.js": "759d80b760f44cd3a454b4f161fd03a7d6c359901446f0a907a6870cb66d6767", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/incr32.js": "2bdea27b74b3990ee56807a1a5abe335f118826beabeeb905459c8768094b28f", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/mod.js": "fe4affebbd210d885b2e5135c668751f9d10bc14aa0cc3905cbfff66f04b4c58", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/modes/cbc.js": "ff24b4506522a724ba7a03c1403ad8938aba45056f9fd47c7f0b4fcb3a640adf", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/modes/cfb.js": "643720a1db969b6bcc896c95523630838a8335513d02f340514fd524bb4113cb", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/modes/cfb1.js": "01c9a46aa3affd84a54ae33652fb0fa0ff7c862be2a459d9cb188cb8e2c4b11e", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/modes/cfb8.js": "97476cee25103e02a02b196d7fe6f28a9f0f9e47ee344687d7492bc7282a59f8", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/modes/ctr.js": "1e3835adb753cfe6761e4df8c43d190e31e1ca6a586fd582747c8255c82ed78d", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/modes/ecb.js": "79677b96d4af50c49f0a4f698e5c7e5a64f1d2926b799e0d2eac2cdd5ec7488c", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/modes/mod.js": "fe3db429b867a0a8066c64d7b33b840a1f24cad9174156384a763733f68cf518", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/modes/ofb.js": "3553308f98d078e2006eac39bb6d91818f8bb376b01d962ae98eabf6ee79ad4e", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/stream_cipher.js": "70f50f37ddec530ae95911ca2f286ebd2ddbd54d914ab0be461ec1dc3c61990f", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_aes/xor.ts": "7132baacdb39ba82c3bfe325a60e68ca87469c0ed0cdd0508caf6f40bab852b8", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/browserify_rsa.js": "96e0e4fee7c2cf75ef86d958c709bfc239297a080fd17ace5ea5ab699a1b6174", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/cipher_base.js": "9ebc6ccc364cf7b23024821054d2e72a2d8da8d8a2a36cacdc5aa6cc6770ef93", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/evp_bytes_to_key.ts": "7c4c27b6e321b2d7065a6703d90264921e9a805d91d9dfdb21103393228024e2", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/parse_asn1/asn1.js": "7d99b6df508164169a33377346e8840d519fe2defccb362a023c92c5bd503433", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/parse_asn1/certificate.js": "5795348417b3ec7aafa4854ba55f364e0148eadfdd29d1566c90e617237621bb", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/parse_asn1/fix_proc.js": "858dd3e6ce264d75822cadc21bb55114f4e4867a706abde1663548aa2710fc1b", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/parse_asn1/mod.js": "ea164fbd497ce3d710426742d4b72f71da8954c4ebaeb7eadc33316c5b0060f1", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/public_encrypt/mgf.js": "dfac5008a550b3e7e6b851c4fb42e984aa9e7fae64707888f47f2aa0991c004d", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/public_encrypt/mod.js": "0704326ff3ee2bb0764a964995d1aa62b1147b714ad5465e878ba4d57731e3db", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/public_encrypt/private_decrypt.js": "8a1d11edb176d95d1e3bdf1aff5c3248a986bf9734d1a6b07508e29132d2f65c", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/public_encrypt/public_encrypt.js": "f88b0e3c228d84096fdbc03e614e86bef86e56013cb9628b2425e31b3b142b2c", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/public_encrypt/with_public.js": "752da754d253b5743d89c0f2432b6eb6f8815b80efd9ee588683e10a13d34400", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/public_encrypt/xor.js": "087ebef8f6fcb8ca4c7216cc22de728d9a61ec27b9a036b900681ff25d6409af", "https://deno.land/std@0.177.0/node/_crypto/crypto_browserify/randombytes.ts": "23bde8be640e274d7bb88cf10d1da8bba252654252dc6a877fed86a77da5952c", "https://deno.land/std@0.177.0/node/_events.d.ts": "1347437fd6b084d7c9a4e16b9fe7435f00b030970086482edeeb3b179d0775af", "https://deno.land/std@0.177.0/node/_events.mjs": "d4ba4e629abe3db9f1b14659fd5c282b7da8b2b95eaf13238eee4ebb142a2448", "https://deno.land/std@0.177.0/node/_global.d.ts": "2d88342f38b4083b858998e27c706725fb03a74aa14ef8d985dc18438b5188e4", "https://deno.land/std@0.177.0/node/_next_tick.ts": "9a3cf107d59b019a355d3cf32275b4c6157282e4b68ea85b46a799cb1d379305", "https://deno.land/std@0.177.0/node/_process/exiting.ts": "6e336180aaabd1192bf99ffeb0d14b689116a3dec1dfb34a2afbacd6766e98ab", "https://deno.land/std@0.177.0/node/_process/process.ts": "c96bb1f6253824c372f4866ee006dcefda02b7050d46759736e403f862d91051", "https://deno.land/std@0.177.0/node/_process/stdio.mjs": "cf17727eac8da3a665851df700b5aca6a12bacc3ebbf33e63e4b919f80ba44a6", "https://deno.land/std@0.177.0/node/_stream.d.ts": "112e1a0677cd6db932c3ce0e6e5bbdc7a2ac1874572f449044ecc82afcf5ee2e", "https://deno.land/std@0.177.0/node/_stream.mjs": "d6e2c86c1158ac65b4c2ca4fa019d7e84374ff12e21e2175345fe68c0823efe3", "https://deno.land/std@0.177.0/node/_utils.ts": "7fd55872a0cf9275e3c080a60e2fa6d45b8de9e956ebcde9053e72a344185884", "https://deno.land/std@0.177.0/node/buffer.ts": "85617be2063eccaf177dbb84c7580d1e32023724ed14bd9df4e453b152a26167", "https://deno.land/std@0.177.0/node/crypto.ts": "2c94fa0f76e90190fbc34df891dc5c284bddb86c932fae8ac11747de3f75293c", "https://deno.land/std@0.177.0/node/events.ts": "d2de352d509de11a375e2cb397d6b98f5fed4e562fc1d41be33214903a38e6b0", "https://deno.land/std@0.177.0/node/internal/buffer.d.ts": "bdfa991cd88cb02fd08bf8235d2618550e3e511c970b2a8f2e1a6885a2793cac", "https://deno.land/std@0.177.0/node/internal/buffer.mjs": "e92303a3cc6d9aaabcd270a937ad9319825d9ba08cb332650944df4562029b27", "https://deno.land/std@0.177.0/node/internal/crypto/_keys.ts": "8f3c3b5a141aa0331a53c205e9338655f1b3b307a08085fd6ff6dda6f7c4190b", "https://deno.land/std@0.177.0/node/internal/crypto/_randomBytes.ts": "36dd164747f73b830ba86562abb160a8ac5bea34aaeb816a67f3005a00d41177", "https://deno.land/std@0.177.0/node/internal/crypto/_randomFill.ts": "297186f290eba87a1ad7b8aa42a960ff4278a8b6b0c963fa81918c326d5c0b58", "https://deno.land/std@0.177.0/node/internal/crypto/_randomInt.ts": "6cf19da9684b67520e67a2d99f2581a3f841140842c7ce2e014d166457550fe1", "https://deno.land/std@0.177.0/node/internal/crypto/certificate.ts": "b4a6695f82e70a42e85247c74a7691ed4b3a904646451af0287e49efe1a28814", "https://deno.land/std@0.177.0/node/internal/crypto/cipher.ts": "2bae9b4d94c465e4d1c70e5a9e8fd67ce20bcc66fecd2eec6be00d35144ca4eb", "https://deno.land/std@0.177.0/node/internal/crypto/constants.ts": "544d605703053218499b08214f2e25cf4310651d535b7ab995891c4b7a217693", "https://deno.land/std@0.177.0/node/internal/crypto/diffiehellman.ts": "9cfb219c5b2936db773f559b6affe6d25b0e40531010389f05df3f05ce7eebf5", "https://deno.land/std@0.177.0/node/internal/crypto/hash.ts": "d01f5d3ad5477655b432036d2d553c7a0c31a901ac0e1e9e0d8b3975daae7624", "https://deno.land/std@0.177.0/node/internal/crypto/hkdf.ts": "5bd801234e56468fbd47466f46e88bdadc66432d625e3616abe38878d410bb66", "https://deno.land/std@0.177.0/node/internal/crypto/keygen.ts": "530cc1a00acf71a43719bb876a2dc563b6196095d080eba77c92c9f39658a5b9", "https://deno.land/std@0.177.0/node/internal/crypto/keys.ts": "c4dfa5aa3420cf700178b87203593a0989c8a93934bfef2b29adb3399d687958", "https://deno.land/std@0.177.0/node/internal/crypto/pbkdf2.ts": "0a0a3e0d3d45db0638fe75a4199c7ed7ca2164405750a520e786e4adebdb45a4", "https://deno.land/std@0.177.0/node/internal/crypto/random.ts": "85f3147e14cb45c18e016da45d319a5c663309411232a956fdc09c2317acdd9f", "https://deno.land/std@0.177.0/node/internal/crypto/scrypt.ts": "b55a0fcd12b295af4127d05b1c0bc3098b74fc0e3c62321c2a43c20f9ed18209", "https://deno.land/std@0.177.0/node/internal/crypto/sig.ts": "25819a89d49c1ebfe3baa1f9464501ec599a36cf53e9b600ec0399e568b9dccc", "https://deno.land/std@0.177.0/node/internal/crypto/types.ts": "52feb182bcbd59206f3e2f4a3cb8a5775d4452c2a8045c3e613e2178d32c2a86", "https://deno.land/std@0.177.0/node/internal/crypto/util.ts": "db282c0413aeee28bc0665fcfc1c08a65fc96dc12ed4d03282f2da4907fcf0ce", "https://deno.land/std@0.177.0/node/internal/crypto/x509.ts": "0e8a541c4f58ecb83862c373d3f7d2371aa8f5108f55bc837b190c4ab3408764", "https://deno.land/std@0.177.0/node/internal/error_codes.ts": "8495e33f448a484518d76fa3d41d34fc20fe03c14b30130ad8e936b0035d4b8b", "https://deno.land/std@0.177.0/node/internal/errors.ts": "1c699b8a3cb93174f697a348c004b1c6d576b66688eac8a48ebb78e65c720aae", "https://deno.land/std@0.177.0/node/internal/fixed_queue.ts": "62bb119afa5b5ae8fc0c7048b50502347bec82e2588017d0b250c4671d6eff8f", "https://deno.land/std@0.177.0/node/internal/hide_stack_frames.ts": "9dd1bad0a6e62a1042ce3a51eb1b1ecee2f246907bff44835f86e8f021de679a", "https://deno.land/std@0.177.0/node/internal/normalize_encoding.mjs": "fd1d9df61c44d7196432f6e8244621468715131d18cc79cd299fc78ac549f707", "https://deno.land/std@0.177.0/node/internal/options.ts": "888f267c3fe8f18dc7b2f2fbdbe7e4a0fd3302ff3e99f5d6645601e924f3e3fb", "https://deno.land/std@0.177.0/node/internal/primordials.mjs": "a72d86b5aa55d3d50b8e916b6a59b7cc0dc5a31da8937114b4a113ad5aa08c74", "https://deno.land/std@0.177.0/node/internal/streams/destroy.mjs": "b665fc71178919a34ddeac8389d162a81b4bc693ff7dc2557fa41b3a91011967", "https://deno.land/std@0.177.0/node/internal/streams/end-of-stream.mjs": "a4fb1c2e32d58dff440d4e716e2c4daaa403b3095304a028bb428575cfeed716", "https://deno.land/std@0.177.0/node/internal/streams/utils.mjs": "f2fe2e6bdc506da24c758970890cc2a21642045b129dee618bd3827c60dd9e33", "https://deno.land/std@0.177.0/node/internal/streams/writable.mjs": "775928726d0483ace8e45a35f30db2019a22dd7b9a81b67b158420e21cc692c5", "https://deno.land/std@0.177.0/node/internal/util.mjs": "f7fe2e1ca5e66f550ad0856b9f5ee4d666f0c071fe212ea7fc7f37cfa81f97a5", "https://deno.land/std@0.177.0/node/internal/util/inspect.mjs": "11d7c9cab514b8e485acc3978c74b837263ff9c08ae4537fa18ad56bae633259", "https://deno.land/std@0.177.0/node/internal/util/types.ts": "0e587b44ec5e017cf228589fc5ce9983b75beece6c39409c34170cfad49d6417", "https://deno.land/std@0.177.0/node/internal/validators.mjs": "e02f2b02dd072a5d623970292588d541204dc82207b4c58985d933a5f4b382e6", "https://deno.land/std@0.177.0/node/internal_binding/_libuv_winerror.ts": "30c9569603d4b97a1f1a034d88a3f74800d5ea1f12fcc3d225c9899d4e1a518b", "https://deno.land/std@0.177.0/node/internal_binding/_node.ts": "cb2389b0eab121df99853eb6a5e3a684e4537e065fb8bf2cca0cbf219ce4e32e", "https://deno.land/std@0.177.0/node/internal_binding/_timingSafeEqual.ts": "7d9732464d3c669ff07713868ce5d25bc974a06112edbfb5f017fc3c70c0853e", "https://deno.land/std@0.177.0/node/internal_binding/_utils.ts": "7c58a2fbb031a204dee9583ba211cf9c67922112fe77e7f0b3226112469e9fe1", "https://deno.land/std@0.177.0/node/internal_binding/_winerror.ts": "3e8cfdfe22e89f13d2b28529bab35155e6b1730c0221ec5a6fc7077dc037be13", "https://deno.land/std@0.177.0/node/internal_binding/buffer.ts": "31729e0537921d6c730ad0afea44a7e8a0a1044d070ade8368226cb6f7390c8b", "https://deno.land/std@0.177.0/node/internal_binding/constants.ts": "21ff9d1ee71d0a2086541083a7711842fc6ae25e264dbf45c73815aadce06f4c", "https://deno.land/std@0.177.0/node/internal_binding/crypto.ts": "29e8f94f283a2e7d4229d3551369c6a40c2af9737fad948cb9be56bef6c468cd", "https://deno.land/std@0.177.0/node/internal_binding/node_options.ts": "0b5cb0bf4379a39278d7b7bb6bb2c2751baf428fe437abe5ed3e8441fae1f18b", "https://deno.land/std@0.177.0/node/internal_binding/string_decoder.ts": "54c3c1cbd5a9254881be58bf22637965dc69535483014dab60487e299cb95445", "https://deno.land/std@0.177.0/node/internal_binding/types.ts": "2187595a58d2cf0134f4db6cc2a12bf777f452f52b15b6c3aed73fa072aa5fc3", "https://deno.land/std@0.177.0/node/internal_binding/util.ts": "808ff3b92740284184ab824adfc420e75398c88c8bccf5111f0c24ac18c48f10", "https://deno.land/std@0.177.0/node/internal_binding/uv.ts": "eb0048e30af4db407fb3f95563e30d70efd6187051c033713b0a5b768593a3a3", "https://deno.land/std@0.177.0/node/perf_hooks.ts": "c20e1f02f463e065e8f6c1d4fa97b0d3832dc0db5b5d8ea37f99a962ecf11f35", "https://deno.land/std@0.177.0/node/stream.ts": "09e348302af40dcc7dc58aa5e40fdff868d11d8d6b0cfb85cbb9c75b9fe450c7", "https://deno.land/std@0.177.0/node/string_decoder.ts": "1a17e3572037c512cc5fc4b29076613e90f225474362d18da908cb7e5ccb7e88" } } lib0-0.2.93/diff.js000066400000000000000000000112461457563604600137250ustar00rootroot00000000000000/** * Efficient diffs. * * @module diff */ import { equalityStrict } from './function.js' /** * A SimpleDiff describes a change on a String. * * ```js * console.log(a) // the old value * console.log(b) // the updated value * // Apply changes of diff (pseudocode) * a.remove(diff.index, diff.remove) // Remove `diff.remove` characters * a.insert(diff.index, diff.insert) // Insert `diff.insert` * a === b // values match * ``` * * @typedef {Object} SimpleDiff * @property {Number} index The index where changes were applied * @property {Number} remove The number of characters to delete starting * at `index`. * @property {T} insert The new text to insert at `index` after applying * `delete` * * @template T */ const highSurrogateRegex = /[\uD800-\uDBFF]/ const lowSurrogateRegex = /[\uDC00-\uDFFF]/ /** * Create a diff between two strings. This diff implementation is highly * efficient, but not very sophisticated. * * @function * * @param {string} a The old version of the string * @param {string} b The updated version of the string * @return {SimpleDiff} The diff description. */ export const simpleDiffString = (a, b) => { let left = 0 // number of same characters counting from left let right = 0 // number of same characters counting from right while (left < a.length && left < b.length && a[left] === b[left]) { left++ } // If the last same character is a high surrogate, we need to rollback to the previous character if (left > 0 && highSurrogateRegex.test(a[left - 1])) left-- while (right + left < a.length && right + left < b.length && a[a.length - right - 1] === b[b.length - right - 1]) { right++ } // If the last same character is a low surrogate, we need to rollback to the previous character if (right > 0 && lowSurrogateRegex.test(a[a.length - right])) right-- return { index: left, remove: a.length - left - right, insert: b.slice(left, b.length - right) } } /** * @todo Remove in favor of simpleDiffString * @deprecated */ export const simpleDiff = simpleDiffString /** * Create a diff between two arrays. This diff implementation is highly * efficient, but not very sophisticated. * * Note: This is basically the same function as above. Another function was created so that the runtime * can better optimize these function calls. * * @function * @template T * * @param {Array} a The old version of the array * @param {Array} b The updated version of the array * @param {function(T, T):boolean} [compare] * @return {SimpleDiff>} The diff description. */ export const simpleDiffArray = (a, b, compare = equalityStrict) => { let left = 0 // number of same characters counting from left let right = 0 // number of same characters counting from right while (left < a.length && left < b.length && compare(a[left], b[left])) { left++ } while (right + left < a.length && right + left < b.length && compare(a[a.length - right - 1], b[b.length - right - 1])) { right++ } return { index: left, remove: a.length - left - right, insert: b.slice(left, b.length - right) } } /** * Diff text and try to diff at the current cursor position. * * @param {string} a * @param {string} b * @param {number} cursor This should refer to the current left cursor-range position */ export const simpleDiffStringWithCursor = (a, b, cursor) => { let left = 0 // number of same characters counting from left let right = 0 // number of same characters counting from right // Iterate left to the right until we find a changed character // First iteration considers the current cursor position while ( left < a.length && left < b.length && a[left] === b[left] && left < cursor ) { left++ } // If the last same character is a high surrogate, we need to rollback to the previous character if (left > 0 && highSurrogateRegex.test(a[left - 1])) left-- // Iterate right to the left until we find a changed character while ( right + left < a.length && right + left < b.length && a[a.length - right - 1] === b[b.length - right - 1] ) { right++ } // If the last same character is a low surrogate, we need to rollback to the previous character if (right > 0 && lowSurrogateRegex.test(a[a.length - right])) right-- // Try to iterate left further to the right without caring about the current cursor position while ( right + left < a.length && right + left < b.length && a[left] === b[left] ) { left++ } if (left > 0 && highSurrogateRegex.test(a[left - 1])) left-- return { index: left, remove: a.length - left - right, insert: b.slice(left, b.length - right) } } lib0-0.2.93/diff.test.js000066400000000000000000000120271457563604600147010ustar00rootroot00000000000000import { simpleDiffString, simpleDiffArray, simpleDiffStringWithCursor } from './diff.js' import * as prng from './prng.js' import * as f from './function.js' import * as t from './testing.js' import * as str from './string.js' /** * @param {string} a * @param {string} b * @param {{index: number,remove:number,insert:string}} expected */ function runDiffTest (a, b, expected) { const result = simpleDiffString(a, b) t.compare(result, expected) t.compare(result, simpleDiffStringWithCursor(a, b, a.length)) // check that the withCursor approach returns the same result const recomposed = str.splice(a, result.index, result.remove, result.insert) t.compareStrings(recomposed, b) const arrResult = simpleDiffArray(Array.from(a), Array.from(b)) const arrRecomposed = Array.from(a) arrRecomposed.splice(arrResult.index, arrResult.remove, ...arrResult.insert) t.compareStrings(arrRecomposed.join(''), b) } /** * @param {t.TestCase} tc */ export const testDiffing = tc => { runDiffTest('abc', 'axc', { index: 1, remove: 1, insert: 'x' }) runDiffTest('bc', 'xc', { index: 0, remove: 1, insert: 'x' }) runDiffTest('ab', 'ax', { index: 1, remove: 1, insert: 'x' }) runDiffTest('b', 'x', { index: 0, remove: 1, insert: 'x' }) runDiffTest('', 'abc', { index: 0, remove: 0, insert: 'abc' }) runDiffTest('abc', 'xyz', { index: 0, remove: 3, insert: 'xyz' }) runDiffTest('axz', 'au', { index: 1, remove: 2, insert: 'u' }) runDiffTest('ax', 'axy', { index: 2, remove: 0, insert: 'y' }) // These strings share high-surrogate characters runDiffTest('\u{d83d}\u{dc77}'/* '👷' */, '\u{d83d}\u{dea7}\u{d83d}\u{dc77}'/* '🚧👷' */, { index: 0, remove: 0, insert: '🚧' }) runDiffTest('\u{d83d}\u{dea7}\u{d83d}\u{dc77}'/* '🚧👷' */, '\u{d83d}\u{dc77}'/* '👷' */, { index: 0, remove: 2, insert: '' }) // These strings share low-surrogate characters runDiffTest('\u{d83d}\u{dfe6}\u{d83d}\u{dfe6}'/* '🟦🟦' */, '\u{d83c}\u{dfe6}\u{d83d}\u{dfe6}'/* '🏦🟦' */, { index: 0, remove: 2, insert: '🏦' }) // check 4-character unicode symbols runDiffTest('🇦🇨', '🇦🇩', { index: 2, remove: 2, insert: '🇩' }) runDiffTest('a🇧🇩', '🇦🇩', { index: 0, remove: 3, insert: '🇦' }) } /** * @param {t.TestCase} tc */ export const testRepeatDiffing = tc => { const a = prng.word(tc.prng) const b = prng.word(tc.prng) const change = simpleDiffString(a, b) const recomposed = str.splice(a, change.index, change.remove, change.insert) t.compareStrings(recomposed, b) } /** * @param {t.TestCase} tc */ export const testSimpleDiffWithCursor = tc => { const initial = 'Hello WorldHello World' const expected = 'Hello World' { const change = simpleDiffStringWithCursor(initial, 'Hello World', 0) // should delete the first hello world t.compare(change, { insert: '', remove: 11, index: 0 }) const recomposed = str.splice(initial, change.index, change.remove, change.insert) t.compareStrings(expected, recomposed) } { const change = simpleDiffStringWithCursor(initial, 'Hello World', 11) // should delete the second hello world t.compare(change, { insert: '', remove: 11, index: 11 }) const recomposedSecond = str.splice(initial, change.index, change.remove, change.insert) t.compareStrings(recomposedSecond, expected) } { const change = simpleDiffStringWithCursor(initial, 'Hello World', 5) // should delete in the midst of Hello World t.compare(change, { insert: '', remove: 11, index: 5 }) const recomposed = str.splice(initial, change.index, change.remove, change.insert) t.compareStrings(expected, recomposed) } { const initial = 'Hello my World' const change = simpleDiffStringWithCursor(initial, 'Hello World', 0) // Should delete after the current cursor position t.compare(change, { insert: '', remove: 3, index: 5 }) const recomposed = str.splice(initial, change.index, change.remove, change.insert) t.compareStrings(expected, recomposed) } { const initial = '🚧🚧🚧' const change = simpleDiffStringWithCursor(initial, '🚧🚧', 2) // Should delete after the midst of 🚧 t.compare(change, { insert: '', remove: 2, index: 2 }) const recomposed = str.splice(initial, change.index, change.remove, change.insert) t.compareStrings('🚧🚧', recomposed) } { const initial = '🚧👷🚧👷' const change = simpleDiffStringWithCursor(initial, '🚧🚧', 2) // Should delete after the first 🚧 and insert 🚧 t.compare(change, { insert: '🚧', remove: 6, index: 2 }) const recomposed = str.splice(initial, change.index, change.remove, change.insert) t.compareStrings('🚧🚧', recomposed) } } /** * @param {t.TestCase} tc */ export const testArrayDiffing = tc => { const a = [[1, 2], { x: 'x' }] const b = [[1, 2], { x: 'x' }] t.compare(simpleDiffArray(a, b, f.equalityFlat), { index: 2, remove: 0, insert: [] }) t.compare(simpleDiffArray(a, b, f.equalityStrict), { index: 0, remove: 2, insert: b }) t.compare(simpleDiffArray([{ x: 'y' }, []], a, f.equalityFlat), { index: 0, remove: 2, insert: b }) } lib0-0.2.93/dom.js000066400000000000000000000142311457563604600135710ustar00rootroot00000000000000/* eslint-env browser */ /** * Utility module to work with the DOM. * * @module dom */ import * as pair from './pair.js' import * as map from './map.js' /* c8 ignore start */ /** * @type {Document} */ export const doc = /** @type {Document} */ (typeof document !== 'undefined' ? document : {}) /** * @param {string} name * @return {HTMLElement} */ export const createElement = name => doc.createElement(name) /** * @return {DocumentFragment} */ export const createDocumentFragment = () => doc.createDocumentFragment() /** * @param {string} text * @return {Text} */ export const createTextNode = text => doc.createTextNode(text) export const domParser = /** @type {DOMParser} */ (typeof DOMParser !== 'undefined' ? new DOMParser() : null) /** * @param {HTMLElement} el * @param {string} name * @param {Object} opts */ export const emitCustomEvent = (el, name, opts) => el.dispatchEvent(new CustomEvent(name, opts)) /** * @param {Element} el * @param {Array>} attrs Array of key-value pairs * @return {Element} */ export const setAttributes = (el, attrs) => { pair.forEach(attrs, (key, value) => { if (value === false) { el.removeAttribute(key) } else if (value === true) { el.setAttribute(key, '') } else { // @ts-ignore el.setAttribute(key, value) } }) return el } /** * @param {Element} el * @param {Map} attrs Array of key-value pairs * @return {Element} */ export const setAttributesMap = (el, attrs) => { attrs.forEach((value, key) => { el.setAttribute(key, value) }) return el } /** * @param {Array|HTMLCollection} children * @return {DocumentFragment} */ export const fragment = children => { const fragment = createDocumentFragment() for (let i = 0; i < children.length; i++) { appendChild(fragment, children[i]) } return fragment } /** * @param {Element} parent * @param {Array} nodes * @return {Element} */ export const append = (parent, nodes) => { appendChild(parent, fragment(nodes)) return parent } /** * @param {HTMLElement} el */ export const remove = el => el.remove() /** * @param {EventTarget} el * @param {string} name * @param {EventListener} f */ export const addEventListener = (el, name, f) => el.addEventListener(name, f) /** * @param {EventTarget} el * @param {string} name * @param {EventListener} f */ export const removeEventListener = (el, name, f) => el.removeEventListener(name, f) /** * @param {Node} node * @param {Array>} listeners * @return {Node} */ export const addEventListeners = (node, listeners) => { pair.forEach(listeners, (name, f) => addEventListener(node, name, f)) return node } /** * @param {Node} node * @param {Array>} listeners * @return {Node} */ export const removeEventListeners = (node, listeners) => { pair.forEach(listeners, (name, f) => removeEventListener(node, name, f)) return node } /** * @param {string} name * @param {Array|pair.Pair>} attrs Array of key-value pairs * @param {Array} children * @return {Element} */ export const element = (name, attrs = [], children = []) => append(setAttributes(createElement(name), attrs), children) /** * @param {number} width * @param {number} height */ export const canvas = (width, height) => { const c = /** @type {HTMLCanvasElement} */ (createElement('canvas')) c.height = height c.width = width return c } /** * @param {string} t * @return {Text} */ export const text = createTextNode /** * @param {pair.Pair} pair */ export const pairToStyleString = pair => `${pair.left}:${pair.right};` /** * @param {Array>} pairs * @return {string} */ export const pairsToStyleString = pairs => pairs.map(pairToStyleString).join('') /** * @param {Map} m * @return {string} */ export const mapToStyleString = m => map.map(m, (value, key) => `${key}:${value};`).join('') /** * @todo should always query on a dom element * * @param {HTMLElement|ShadowRoot} el * @param {string} query * @return {HTMLElement | null} */ export const querySelector = (el, query) => el.querySelector(query) /** * @param {HTMLElement|ShadowRoot} el * @param {string} query * @return {NodeListOf} */ export const querySelectorAll = (el, query) => el.querySelectorAll(query) /** * @param {string} id * @return {HTMLElement} */ export const getElementById = id => /** @type {HTMLElement} */ (doc.getElementById(id)) /** * @param {string} html * @return {HTMLElement} */ const _parse = html => domParser.parseFromString(`${html}`, 'text/html').body /** * @param {string} html * @return {DocumentFragment} */ export const parseFragment = html => fragment(/** @type {any} */ (_parse(html).childNodes)) /** * @param {string} html * @return {HTMLElement} */ export const parseElement = html => /** @type HTMLElement */ (_parse(html).firstElementChild) /** * @param {HTMLElement} oldEl * @param {HTMLElement|DocumentFragment} newEl */ export const replaceWith = (oldEl, newEl) => oldEl.replaceWith(newEl) /** * @param {HTMLElement} parent * @param {HTMLElement} el * @param {Node|null} ref * @return {HTMLElement} */ export const insertBefore = (parent, el, ref) => parent.insertBefore(el, ref) /** * @param {Node} parent * @param {Node} child * @return {Node} */ export const appendChild = (parent, child) => parent.appendChild(child) export const ELEMENT_NODE = doc.ELEMENT_NODE export const TEXT_NODE = doc.TEXT_NODE export const CDATA_SECTION_NODE = doc.CDATA_SECTION_NODE export const COMMENT_NODE = doc.COMMENT_NODE export const DOCUMENT_NODE = doc.DOCUMENT_NODE export const DOCUMENT_TYPE_NODE = doc.DOCUMENT_TYPE_NODE export const DOCUMENT_FRAGMENT_NODE = doc.DOCUMENT_FRAGMENT_NODE /** * @param {any} node * @param {number} type */ export const checkNodeType = (node, type) => node.nodeType === type /** * @param {Node} parent * @param {HTMLElement} child */ export const isParentOf = (parent, child) => { let p = child.parentNode while (p && p !== parent) { p = p.parentNode } return p === parent } /* c8 ignore stop */ lib0-0.2.93/encoding.js000066400000000000000000000634761457563604600146170ustar00rootroot00000000000000/** * Efficient schema-less binary encoding with support for variable length encoding. * * Use [lib0/encoding] with [lib0/decoding]. Every encoding function has a corresponding decoding function. * * Encodes numbers in little-endian order (least to most significant byte order) * and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/) * which is also used in Protocol Buffers. * * ```js * // encoding step * const encoder = encoding.createEncoder() * encoding.writeVarUint(encoder, 256) * encoding.writeVarString(encoder, 'Hello world!') * const buf = encoding.toUint8Array(encoder) * ``` * * ```js * // decoding step * const decoder = decoding.createDecoder(buf) * decoding.readVarUint(decoder) // => 256 * decoding.readVarString(decoder) // => 'Hello world!' * decoding.hasContent(decoder) // => false - all data is read * ``` * * @module encoding */ import * as math from './math.js' import * as number from './number.js' import * as binary from './binary.js' import * as string from './string.js' import * as array from './array.js' /** * A BinaryEncoder handles the encoding to an Uint8Array. */ export class Encoder { constructor () { this.cpos = 0 this.cbuf = new Uint8Array(100) /** * @type {Array} */ this.bufs = [] } } /** * @function * @return {Encoder} */ export const createEncoder = () => new Encoder() /** * @param {function(Encoder):void} f */ export const encode = (f) => { const encoder = createEncoder() f(encoder) return toUint8Array(encoder) } /** * The current length of the encoded data. * * @function * @param {Encoder} encoder * @return {number} */ export const length = encoder => { let len = encoder.cpos for (let i = 0; i < encoder.bufs.length; i++) { len += encoder.bufs[i].length } return len } /** * Check whether encoder is empty. * * @function * @param {Encoder} encoder * @return {boolean} */ export const hasContent = encoder => encoder.cpos > 0 || encoder.bufs.length > 0 /** * Transform to Uint8Array. * * @function * @param {Encoder} encoder * @return {Uint8Array} The created ArrayBuffer. */ export const toUint8Array = encoder => { const uint8arr = new Uint8Array(length(encoder)) let curPos = 0 for (let i = 0; i < encoder.bufs.length; i++) { const d = encoder.bufs[i] uint8arr.set(d, curPos) curPos += d.length } uint8arr.set(new Uint8Array(encoder.cbuf.buffer, 0, encoder.cpos), curPos) return uint8arr } /** * Verify that it is possible to write `len` bytes wtihout checking. If * necessary, a new Buffer with the required length is attached. * * @param {Encoder} encoder * @param {number} len */ export const verifyLen = (encoder, len) => { const bufferLen = encoder.cbuf.length if (bufferLen - encoder.cpos < len) { encoder.bufs.push(new Uint8Array(encoder.cbuf.buffer, 0, encoder.cpos)) encoder.cbuf = new Uint8Array(math.max(bufferLen, len) * 2) encoder.cpos = 0 } } /** * Write one byte to the encoder. * * @function * @param {Encoder} encoder * @param {number} num The byte that is to be encoded. */ export const write = (encoder, num) => { const bufferLen = encoder.cbuf.length if (encoder.cpos === bufferLen) { encoder.bufs.push(encoder.cbuf) encoder.cbuf = new Uint8Array(bufferLen * 2) encoder.cpos = 0 } encoder.cbuf[encoder.cpos++] = num } /** * Write one byte at a specific position. * Position must already be written (i.e. encoder.length > pos) * * @function * @param {Encoder} encoder * @param {number} pos Position to which to write data * @param {number} num Unsigned 8-bit integer */ export const set = (encoder, pos, num) => { let buffer = null // iterate all buffers and adjust position for (let i = 0; i < encoder.bufs.length && buffer === null; i++) { const b = encoder.bufs[i] if (pos < b.length) { buffer = b // found buffer } else { pos -= b.length } } if (buffer === null) { // use current buffer buffer = encoder.cbuf } buffer[pos] = num } /** * Write one byte as an unsigned integer. * * @function * @param {Encoder} encoder * @param {number} num The number that is to be encoded. */ export const writeUint8 = write /** * Write one byte as an unsigned Integer at a specific location. * * @function * @param {Encoder} encoder * @param {number} pos The location where the data will be written. * @param {number} num The number that is to be encoded. */ export const setUint8 = set /** * Write two bytes as an unsigned integer. * * @function * @param {Encoder} encoder * @param {number} num The number that is to be encoded. */ export const writeUint16 = (encoder, num) => { write(encoder, num & binary.BITS8) write(encoder, (num >>> 8) & binary.BITS8) } /** * Write two bytes as an unsigned integer at a specific location. * * @function * @param {Encoder} encoder * @param {number} pos The location where the data will be written. * @param {number} num The number that is to be encoded. */ export const setUint16 = (encoder, pos, num) => { set(encoder, pos, num & binary.BITS8) set(encoder, pos + 1, (num >>> 8) & binary.BITS8) } /** * Write two bytes as an unsigned integer * * @function * @param {Encoder} encoder * @param {number} num The number that is to be encoded. */ export const writeUint32 = (encoder, num) => { for (let i = 0; i < 4; i++) { write(encoder, num & binary.BITS8) num >>>= 8 } } /** * Write two bytes as an unsigned integer in big endian order. * (most significant byte first) * * @function * @param {Encoder} encoder * @param {number} num The number that is to be encoded. */ export const writeUint32BigEndian = (encoder, num) => { for (let i = 3; i >= 0; i--) { write(encoder, (num >>> (8 * i)) & binary.BITS8) } } /** * Write two bytes as an unsigned integer at a specific location. * * @function * @param {Encoder} encoder * @param {number} pos The location where the data will be written. * @param {number} num The number that is to be encoded. */ export const setUint32 = (encoder, pos, num) => { for (let i = 0; i < 4; i++) { set(encoder, pos + i, num & binary.BITS8) num >>>= 8 } } /** * Write a variable length unsigned integer. Max encodable integer is 2^53. * * @function * @param {Encoder} encoder * @param {number} num The number that is to be encoded. */ export const writeVarUint = (encoder, num) => { while (num > binary.BITS7) { write(encoder, binary.BIT8 | (binary.BITS7 & num)) num = math.floor(num / 128) // shift >>> 7 } write(encoder, binary.BITS7 & num) } /** * Write a variable length integer. * * We use the 7th bit instead for signaling that this is a negative number. * * @function * @param {Encoder} encoder * @param {number} num The number that is to be encoded. */ export const writeVarInt = (encoder, num) => { const isNegative = math.isNegativeZero(num) if (isNegative) { num = -num } // |- whether to continue reading |- whether is negative |- number write(encoder, (num > binary.BITS6 ? binary.BIT8 : 0) | (isNegative ? binary.BIT7 : 0) | (binary.BITS6 & num)) num = math.floor(num / 64) // shift >>> 6 // We don't need to consider the case of num === 0 so we can use a different // pattern here than above. while (num > 0) { write(encoder, (num > binary.BITS7 ? binary.BIT8 : 0) | (binary.BITS7 & num)) num = math.floor(num / 128) // shift >>> 7 } } /** * A cache to store strings temporarily */ const _strBuffer = new Uint8Array(30000) const _maxStrBSize = _strBuffer.length / 3 /** * Write a variable length string. * * @function * @param {Encoder} encoder * @param {String} str The string that is to be encoded. */ export const _writeVarStringNative = (encoder, str) => { if (str.length < _maxStrBSize) { // We can encode the string into the existing buffer /* c8 ignore next */ const written = string.utf8TextEncoder.encodeInto(str, _strBuffer).written || 0 writeVarUint(encoder, written) for (let i = 0; i < written; i++) { write(encoder, _strBuffer[i]) } } else { writeVarUint8Array(encoder, string.encodeUtf8(str)) } } /** * Write a variable length string. * * @function * @param {Encoder} encoder * @param {String} str The string that is to be encoded. */ export const _writeVarStringPolyfill = (encoder, str) => { const encodedString = unescape(encodeURIComponent(str)) const len = encodedString.length writeVarUint(encoder, len) for (let i = 0; i < len; i++) { write(encoder, /** @type {number} */ (encodedString.codePointAt(i))) } } /** * Write a variable length string. * * @function * @param {Encoder} encoder * @param {String} str The string that is to be encoded. */ /* c8 ignore next */ export const writeVarString = (string.utf8TextEncoder && /** @type {any} */ (string.utf8TextEncoder).encodeInto) ? _writeVarStringNative : _writeVarStringPolyfill /** * Write a string terminated by a special byte sequence. This is not very performant and is * generally discouraged. However, the resulting byte arrays are lexiographically ordered which * makes this a nice feature for databases. * * The string will be encoded using utf8 and then terminated and escaped using writeTerminatingUint8Array. * * @function * @param {Encoder} encoder * @param {String} str The string that is to be encoded. */ export const writeTerminatedString = (encoder, str) => writeTerminatedUint8Array(encoder, string.encodeUtf8(str)) /** * Write a terminating Uint8Array. Note that this is not performant and is generally * discouraged. There are few situations when this is needed. * * We use 0x0 as a terminating character. 0x1 serves as an escape character for 0x0 and 0x1. * * Example: [0,1,2] is encoded to [1,0,1,1,2,0]. 0x0, and 0x1 needed to be escaped using 0x1. Then * the result is terminated using the 0x0 character. * * This is basically how many systems implement null terminated strings. However, we use an escape * character 0x1 to avoid issues and potenial attacks on our database (if this is used as a key * encoder for NoSql databases). * * @function * @param {Encoder} encoder * @param {Uint8Array} buf The string that is to be encoded. */ export const writeTerminatedUint8Array = (encoder, buf) => { for (let i = 0; i < buf.length; i++) { const b = buf[i] if (b === 0 || b === 1) { write(encoder, 1) } write(encoder, buf[i]) } write(encoder, 0) } /** * Write the content of another Encoder. * * @TODO: can be improved! * - Note: Should consider that when appending a lot of small Encoders, we should rather clone than referencing the old structure. * Encoders start with a rather big initial buffer. * * @function * @param {Encoder} encoder The enUint8Arr * @param {Encoder} append The BinaryEncoder to be written. */ export const writeBinaryEncoder = (encoder, append) => writeUint8Array(encoder, toUint8Array(append)) /** * Append fixed-length Uint8Array to the encoder. * * @function * @param {Encoder} encoder * @param {Uint8Array} uint8Array */ export const writeUint8Array = (encoder, uint8Array) => { const bufferLen = encoder.cbuf.length const cpos = encoder.cpos const leftCopyLen = math.min(bufferLen - cpos, uint8Array.length) const rightCopyLen = uint8Array.length - leftCopyLen encoder.cbuf.set(uint8Array.subarray(0, leftCopyLen), cpos) encoder.cpos += leftCopyLen if (rightCopyLen > 0) { // Still something to write, write right half.. // Append new buffer encoder.bufs.push(encoder.cbuf) // must have at least size of remaining buffer encoder.cbuf = new Uint8Array(math.max(bufferLen * 2, rightCopyLen)) // copy array encoder.cbuf.set(uint8Array.subarray(leftCopyLen)) encoder.cpos = rightCopyLen } } /** * Append an Uint8Array to Encoder. * * @function * @param {Encoder} encoder * @param {Uint8Array} uint8Array */ export const writeVarUint8Array = (encoder, uint8Array) => { writeVarUint(encoder, uint8Array.byteLength) writeUint8Array(encoder, uint8Array) } /** * Create an DataView of the next `len` bytes. Use it to write data after * calling this function. * * ```js * // write float32 using DataView * const dv = writeOnDataView(encoder, 4) * dv.setFloat32(0, 1.1) * // read float32 using DataView * const dv = readFromDataView(encoder, 4) * dv.getFloat32(0) // => 1.100000023841858 (leaving it to the reader to find out why this is the correct result) * ``` * * @param {Encoder} encoder * @param {number} len * @return {DataView} */ export const writeOnDataView = (encoder, len) => { verifyLen(encoder, len) const dview = new DataView(encoder.cbuf.buffer, encoder.cpos, len) encoder.cpos += len return dview } /** * @param {Encoder} encoder * @param {number} num */ export const writeFloat32 = (encoder, num) => writeOnDataView(encoder, 4).setFloat32(0, num, false) /** * @param {Encoder} encoder * @param {number} num */ export const writeFloat64 = (encoder, num) => writeOnDataView(encoder, 8).setFloat64(0, num, false) /** * @param {Encoder} encoder * @param {bigint} num */ export const writeBigInt64 = (encoder, num) => /** @type {any} */ (writeOnDataView(encoder, 8)).setBigInt64(0, num, false) /** * @param {Encoder} encoder * @param {bigint} num */ export const writeBigUint64 = (encoder, num) => /** @type {any} */ (writeOnDataView(encoder, 8)).setBigUint64(0, num, false) const floatTestBed = new DataView(new ArrayBuffer(4)) /** * Check if a number can be encoded as a 32 bit float. * * @param {number} num * @return {boolean} */ const isFloat32 = num => { floatTestBed.setFloat32(0, num) return floatTestBed.getFloat32(0) === num } /** * Encode data with efficient binary format. * * Differences to JSON: * • Transforms data to a binary format (not to a string) * • Encodes undefined, NaN, and ArrayBuffer (these can't be represented in JSON) * • Numbers are efficiently encoded either as a variable length integer, as a * 32 bit float, as a 64 bit float, or as a 64 bit bigint. * * Encoding table: * * | Data Type | Prefix | Encoding Method | Comment | * | ------------------- | -------- | ------------------ | ------- | * | undefined | 127 | | Functions, symbol, and everything that cannot be identified is encoded as undefined | * | null | 126 | | | * | integer | 125 | writeVarInt | Only encodes 32 bit signed integers | * | float32 | 124 | writeFloat32 | | * | float64 | 123 | writeFloat64 | | * | bigint | 122 | writeBigInt64 | | * | boolean (false) | 121 | | True and false are different data types so we save the following byte | * | boolean (true) | 120 | | - 0b01111000 so the last bit determines whether true or false | * | string | 119 | writeVarString | | * | object | 118 | custom | Writes {length} then {length} key-value pairs | * | array | 117 | custom | Writes {length} then {length} json values | * | Uint8Array | 116 | writeVarUint8Array | We use Uint8Array for any kind of binary data | * * Reasons for the decreasing prefix: * We need the first bit for extendability (later we may want to encode the * prefix with writeVarUint). The remaining 7 bits are divided as follows: * [0-30] the beginning of the data range is used for custom purposes * (defined by the function that uses this library) * [31-127] the end of the data range is used for data encoding by * lib0/encoding.js * * @param {Encoder} encoder * @param {undefined|null|number|bigint|boolean|string|Object|Array|Uint8Array} data */ export const writeAny = (encoder, data) => { switch (typeof data) { case 'string': // TYPE 119: STRING write(encoder, 119) writeVarString(encoder, data) break case 'number': if (number.isInteger(data) && math.abs(data) <= binary.BITS31) { // TYPE 125: INTEGER write(encoder, 125) writeVarInt(encoder, data) } else if (isFloat32(data)) { // TYPE 124: FLOAT32 write(encoder, 124) writeFloat32(encoder, data) } else { // TYPE 123: FLOAT64 write(encoder, 123) writeFloat64(encoder, data) } break case 'bigint': // TYPE 122: BigInt write(encoder, 122) writeBigInt64(encoder, data) break case 'object': if (data === null) { // TYPE 126: null write(encoder, 126) } else if (array.isArray(data)) { // TYPE 117: Array write(encoder, 117) writeVarUint(encoder, data.length) for (let i = 0; i < data.length; i++) { writeAny(encoder, data[i]) } } else if (data instanceof Uint8Array) { // TYPE 116: ArrayBuffer write(encoder, 116) writeVarUint8Array(encoder, data) } else { // TYPE 118: Object write(encoder, 118) const keys = Object.keys(data) writeVarUint(encoder, keys.length) for (let i = 0; i < keys.length; i++) { const key = keys[i] writeVarString(encoder, key) writeAny(encoder, data[key]) } } break case 'boolean': // TYPE 120/121: boolean (true/false) write(encoder, data ? 120 : 121) break default: // TYPE 127: undefined write(encoder, 127) } } /** * Now come a few stateful encoder that have their own classes. */ /** * Basic Run Length Encoder - a basic compression implementation. * * Encodes [1,1,1,7] to [1,3,7,1] (3 times 1, 1 time 7). This encoder might do more harm than good if there are a lot of values that are not repeated. * * It was originally used for image compression. Cool .. article http://csbruce.com/cbm/transactor/pdfs/trans_v7_i06.pdf * * @note T must not be null! * * @template T */ export class RleEncoder extends Encoder { /** * @param {function(Encoder, T):void} writer */ constructor (writer) { super() /** * The writer */ this.w = writer /** * Current state * @type {T|null} */ this.s = null this.count = 0 } /** * @param {T} v */ write (v) { if (this.s === v) { this.count++ } else { if (this.count > 0) { // flush counter, unless this is the first value (count = 0) writeVarUint(this, this.count - 1) // since count is always > 0, we can decrement by one. non-standard encoding ftw } this.count = 1 // write first value this.w(this, v) this.s = v } } } /** * Basic diff decoder using variable length encoding. * * Encodes the values [3, 1100, 1101, 1050, 0] to [3, 1097, 1, -51, -1050] using writeVarInt. */ export class IntDiffEncoder extends Encoder { /** * @param {number} start */ constructor (start) { super() /** * Current state * @type {number} */ this.s = start } /** * @param {number} v */ write (v) { writeVarInt(this, v - this.s) this.s = v } } /** * A combination of IntDiffEncoder and RleEncoder. * * Basically first writes the IntDiffEncoder and then counts duplicate diffs using RleEncoding. * * Encodes the values [1,1,1,2,3,4,5,6] as [1,1,0,2,1,5] (RLE([1,0,0,1,1,1,1,1]) ⇒ RleIntDiff[1,1,0,2,1,5]) */ export class RleIntDiffEncoder extends Encoder { /** * @param {number} start */ constructor (start) { super() /** * Current state * @type {number} */ this.s = start this.count = 0 } /** * @param {number} v */ write (v) { if (this.s === v && this.count > 0) { this.count++ } else { if (this.count > 0) { // flush counter, unless this is the first value (count = 0) writeVarUint(this, this.count - 1) // since count is always > 0, we can decrement by one. non-standard encoding ftw } this.count = 1 // write first value writeVarInt(this, v - this.s) this.s = v } } } /** * @param {UintOptRleEncoder} encoder */ const flushUintOptRleEncoder = encoder => { if (encoder.count > 0) { // flush counter, unless this is the first value (count = 0) // case 1: just a single value. set sign to positive // case 2: write several values. set sign to negative to indicate that there is a length coming writeVarInt(encoder.encoder, encoder.count === 1 ? encoder.s : -encoder.s) if (encoder.count > 1) { writeVarUint(encoder.encoder, encoder.count - 2) // since count is always > 1, we can decrement by one. non-standard encoding ftw } } } /** * Optimized Rle encoder that does not suffer from the mentioned problem of the basic Rle encoder. * * Internally uses VarInt encoder to write unsigned integers. If the input occurs multiple times, we write * write it as a negative number. The UintOptRleDecoder then understands that it needs to read a count. * * Encodes [1,2,3,3,3] as [1,2,-3,3] (once 1, once 2, three times 3) */ export class UintOptRleEncoder { constructor () { this.encoder = new Encoder() /** * @type {number} */ this.s = 0 this.count = 0 } /** * @param {number} v */ write (v) { if (this.s === v) { this.count++ } else { flushUintOptRleEncoder(this) this.count = 1 this.s = v } } /** * Flush the encoded state and transform this to a Uint8Array. * * Note that this should only be called once. */ toUint8Array () { flushUintOptRleEncoder(this) return toUint8Array(this.encoder) } } /** * Increasing Uint Optimized RLE Encoder * * The RLE encoder counts the number of same occurences of the same value. * The IncUintOptRle encoder counts if the value increases. * I.e. 7, 8, 9, 10 will be encoded as [-7, 4]. 1, 3, 5 will be encoded * as [1, 3, 5]. */ export class IncUintOptRleEncoder { constructor () { this.encoder = new Encoder() /** * @type {number} */ this.s = 0 this.count = 0 } /** * @param {number} v */ write (v) { if (this.s + this.count === v) { this.count++ } else { flushUintOptRleEncoder(this) this.count = 1 this.s = v } } /** * Flush the encoded state and transform this to a Uint8Array. * * Note that this should only be called once. */ toUint8Array () { flushUintOptRleEncoder(this) return toUint8Array(this.encoder) } } /** * @param {IntDiffOptRleEncoder} encoder */ const flushIntDiffOptRleEncoder = encoder => { if (encoder.count > 0) { // 31 bit making up the diff | wether to write the counter // const encodedDiff = encoder.diff << 1 | (encoder.count === 1 ? 0 : 1) const encodedDiff = encoder.diff * 2 + (encoder.count === 1 ? 0 : 1) // flush counter, unless this is the first value (count = 0) // case 1: just a single value. set first bit to positive // case 2: write several values. set first bit to negative to indicate that there is a length coming writeVarInt(encoder.encoder, encodedDiff) if (encoder.count > 1) { writeVarUint(encoder.encoder, encoder.count - 2) // since count is always > 1, we can decrement by one. non-standard encoding ftw } } } /** * A combination of the IntDiffEncoder and the UintOptRleEncoder. * * The count approach is similar to the UintDiffOptRleEncoder, but instead of using the negative bitflag, it encodes * in the LSB whether a count is to be read. Therefore this Encoder only supports 31 bit integers! * * Encodes [1, 2, 3, 2] as [3, 1, 6, -1] (more specifically [(1 << 1) | 1, (3 << 0) | 0, -1]) * * Internally uses variable length encoding. Contrary to normal UintVar encoding, the first byte contains: * * 1 bit that denotes whether the next value is a count (LSB) * * 1 bit that denotes whether this value is negative (MSB - 1) * * 1 bit that denotes whether to continue reading the variable length integer (MSB) * * Therefore, only five bits remain to encode diff ranges. * * Use this Encoder only when appropriate. In most cases, this is probably a bad idea. */ export class IntDiffOptRleEncoder { constructor () { this.encoder = new Encoder() /** * @type {number} */ this.s = 0 this.count = 0 this.diff = 0 } /** * @param {number} v */ write (v) { if (this.diff === v - this.s) { this.s = v this.count++ } else { flushIntDiffOptRleEncoder(this) this.count = 1 this.diff = v - this.s this.s = v } } /** * Flush the encoded state and transform this to a Uint8Array. * * Note that this should only be called once. */ toUint8Array () { flushIntDiffOptRleEncoder(this) return toUint8Array(this.encoder) } } /** * Optimized String Encoder. * * Encoding many small strings in a simple Encoder is not very efficient. The function call to decode a string takes some time and creates references that must be eventually deleted. * In practice, when decoding several million small strings, the GC will kick in more and more often to collect orphaned string objects (or maybe there is another reason?). * * This string encoder solves the above problem. All strings are concatenated and written as a single string using a single encoding call. * * The lengths are encoded using a UintOptRleEncoder. */ export class StringEncoder { constructor () { /** * @type {Array} */ this.sarr = [] this.s = '' this.lensE = new UintOptRleEncoder() } /** * @param {string} string */ write (string) { this.s += string if (this.s.length > 19) { this.sarr.push(this.s) this.s = '' } this.lensE.write(string.length) } toUint8Array () { const encoder = new Encoder() this.sarr.push(this.s) this.s = '' writeVarString(encoder, this.sarr.join('')) writeUint8Array(encoder, this.lensE.toUint8Array()) return toUint8Array(encoder) } } lib0-0.2.93/encoding.test.js000066400000000000000000000723541457563604600155700ustar00rootroot00000000000000/* global BigInt */ import * as encoding from './encoding.js' import * as decoding from './decoding.js' import * as prng from './prng.js' import * as t from './testing.js' import * as string from './string.js' import * as binary from './binary.js' import * as buffer from './buffer.js' import * as number from './number.js' import * as math from './math.js' /** * @type {Array} */ let genAnyLookupTable = [ gen => BigInt(prng.int53(gen, number.MIN_SAFE_INTEGER, number.MAX_SAFE_INTEGER)), // TYPE 122 _gen => undefined, // TYPE 127 _gen => null, // TYPE 126 gen => prng.int53(gen, number.MIN_SAFE_INTEGER, number.MAX_SAFE_INTEGER), // TYPE 125 gen => prng.real53(gen), // TYPE 124 and 123 _gen => true, // TYPE 121 _gen => false, // TYPE 120 gen => prng.utf16String(gen), // TYPE 119 (gen, depth, toJsonCompatible) => ({ val: genAny(gen, depth + 1, toJsonCompatible) }), // TYPE 118 (gen, depth, toJsonCompatible) => Array.from({ length: prng.uint32(gen, 0, 20 - depth) }).map(() => genAny(gen, depth + 1, toJsonCompatible)), // TYPE 117 gen => prng.uint8Array(gen, prng.uint32(gen, 0, 50)) // TYPE 116 ] const genAnyLookupTableJsonCompatible = genAnyLookupTable.slice(1) if (typeof BigInt === 'undefined') { genAnyLookupTable = genAnyLookupTable.slice(1) } /** * @param {prng.PRNG} gen * @param {number} _depth The current call-depth */ const genAny = (gen, _depth = 0, toJsonCompatible = false) => prng.oneOf(gen, toJsonCompatible ? genAnyLookupTableJsonCompatible : genAnyLookupTable)(gen, _depth, toJsonCompatible) /** * Check if binary encoding is compatible with golang binary encoding - binary.PutVarUint. * * Result: is compatible up to 32 bit: [0, 4294967295] / [0, 0xffffffff]. (max 32 bit unsigned integer) */ export const testGolangBinaryEncodingCompatibility = () => { const tests = [ { in: 0, out: [0] }, { in: 1, out: [1] }, { in: 128, out: [128, 1] }, { in: 200, out: [200, 1] }, { in: 32, out: [32] }, { in: 500, out: [244, 3] }, { in: 256, out: [128, 2] }, { in: 700, out: [188, 5] }, { in: 1024, out: [128, 8] }, { in: 1025, out: [129, 8] }, { in: 4048, out: [208, 31] }, { in: 5050, out: [186, 39] }, { in: 1000000, out: [192, 132, 61] }, { in: 34951959, out: [151, 166, 213, 16] }, { in: 2147483646, out: [254, 255, 255, 255, 7] }, { in: 2147483647, out: [255, 255, 255, 255, 7] }, { in: 2147483648, out: [128, 128, 128, 128, 8] }, { in: 2147483700, out: [180, 128, 128, 128, 8] }, { in: 4294967294, out: [254, 255, 255, 255, 15] }, { in: 4294967295, out: [255, 255, 255, 255, 15] } ] tests.forEach(test => { const encoder = encoding.createEncoder() encoding.writeVarUint(encoder, test.in) const buffer = encoding.toUint8Array(encoder) t.assert(buffer.byteLength === test.out.length) t.assert(buffer.length > 0) t.assert(encoding.hasContent(encoder)) for (let j = 0; j < buffer.length; j++) { t.assert(buffer[j] === test.out[j]) } }) } /** * @template T * @param {string} testname * @param {function(encoding.Encoder, T):void} write * @param {function(decoding.Decoder):T} read * @param {T} val * @param {boolean} doLog */ function test (testname, write, read, val, doLog = true) { const encoder = encoding.createEncoder() write(encoder, val) const buffer = encoding.toUint8Array(encoder) t.assert((buffer.length > 0) === encoding.hasContent(encoder)) const reader = decoding.createDecoder(buffer) const result = read(reader) const utf8ByteLength = string.utf8ByteLength(val + '') const binaryByteLength = encoding.length(encoder) if (doLog) { t.describe(testname, ` utf8 encode: ${utf8ByteLength} bytes / binary encode: ${binaryByteLength} bytes`) } t.compare(val, result) return { utf8ByteLength, binaryByteLength } } /** * @param {string} s */ const testVarString = s => { const decoder = decoding.createDecoder(encoding.encode(encoder => { encoding.writeVarString(encoder, s) })) const peeked = decoding.peekVarString(decoder) const result = decoding.readVarString(decoder) t.compareStrings(s, result) t.compareStrings(s, peeked) } export const testVerifyLen = () => { const encoder = encoding.createEncoder() const vLen = encoder.cbuf.length + 1 const bufsLen = encoder.bufs.length encoding.verifyLen(encoder, vLen) t.assert(encoder.cbuf.length >= vLen) t.assert(encoder.bufs.length >= bufsLen) t.assert(encoding.hasContent(encoder)) } export const testStringEncodingPerformanceNativeVsPolyfill = () => { const largeRepetitions = 20 let bigstr = '' for (let i = 0; i < 10000; i++) { bigstr += i } const customTime = t.measureTime('large dataset: custom encoding', () => { const encoder = encoding.createEncoder() for (let i = 0; i < largeRepetitions; i++) { encoding._writeVarStringPolyfill(encoder, 'i') encoding._writeVarStringPolyfill(encoder, bigstr) } }) const nativeTime = t.measureTime('large dataset: native encoding', () => { const encoder = encoding.createEncoder() for (let i = 0; i < largeRepetitions; i++) { encoding._writeVarStringNative(encoder, 'i') encoding._writeVarStringNative(encoder, bigstr) } }) t.assert(nativeTime < customTime, 'We expect native encoding to be more performant for large data sets') const smallRepetitions = 100000 const customTimeSmall = t.measureTime('small dataset: custom encoding', () => { const encoder = encoding.createEncoder() for (let i = 0; i < smallRepetitions; i++) { encoding._writeVarStringPolyfill(encoder, 'i') encoding._writeVarStringPolyfill(encoder, 'bb') encoding._writeVarStringPolyfill(encoder, 'ccc') } }) const nativeTimeSmall = t.measureTime('small dataset: native encoding', () => { const encoder = encoding.createEncoder() for (let i = 0; i < smallRepetitions; i++) { encoding._writeVarStringNative(encoder, 'i') encoding._writeVarStringNative(encoder, 'bb') encoding._writeVarStringNative(encoder, 'ccc') } }) console.log({ nativeTimeSmall, customTimeSmall }) // @todo we should check that we use custom encoding for small datasets t.assert(nativeTimeSmall < customTimeSmall * 5, 'We expect native encoding to be not much worse than custom encoding for small data sets') } export const testDecodingPerformanceNativeVsPolyfill = () => { const iterationsSmall = 10000 const iterationsLarge = 1000 let bigstr = '' for (let i = 0; i < 10000; i++) { bigstr += i } const encoder = encoding.createEncoder() const encoderLarge = encoding.createEncoder() for (let i = 0; i < iterationsSmall; i++) { encoding.writeVarString(encoder, 'i') encoding.writeVarString(encoder, 'bb') encoding.writeVarString(encoder, 'ccc') } for (let i = 0; i < iterationsLarge; i++) { encoding.writeVarString(encoderLarge, bigstr) } const buf = encoding.toUint8Array(encoder) const bufLarge = encoding.toUint8Array(encoderLarge) const nativeTimeSmall = t.measureTime('small dataset: native encoding', () => { const decoder = decoding.createDecoder(buf) while (decoding.hasContent(decoder)) { decoding._readVarStringNative(decoder) } }) const polyfillTimeSmall = t.measureTime('small dataset: polyfill encoding', () => { const decoder = decoding.createDecoder(buf) while (decoding.hasContent(decoder)) { decoding.readVarString(decoder) } }) const nativeTimeLarge = t.measureTime('large dataset: native encoding', () => { const decoder = decoding.createDecoder(bufLarge) while (decoding.hasContent(decoder)) { decoding._readVarStringNative(decoder) } }) const polyfillTimeLarge = t.measureTime('large dataset: polyfill encoding', () => { const decoder = decoding.createDecoder(bufLarge) while (decoding.hasContent(decoder)) { decoding._readVarStringPolyfill(decoder) } }) // @todo We should switch to native decoding! console.log({ nativeTimeSmall, polyfillTimeSmall }) t.assert(nativeTimeSmall < polyfillTimeSmall * 2.0, 'Small dataset: We expect native decoding to be not much worse than') t.assert(nativeTimeLarge < polyfillTimeLarge * 1.5, 'Large dataset: We expect native decoding to be much better than polyfill decoding') } export const testStringDecodingPerformance = () => { // test if it is faster to read N single characters, or if it is faster to read N characters in one flush. // to make the comparison meaningful, we read read N characters in an Array const N = 2000000 const durationSingleElements = t.measureTime('read / write single elements', () => { const encoder = encoding.createEncoder() t.measureTime('read / write single elements - write', () => { for (let i = 0; i < N; i++) { encoding.writeVarString(encoder, 'i') } }) const decoder = decoding.createDecoder(encoding.toUint8Array(encoder)) t.measureTime('read / write single elements - read', () => { const arr = [] for (let i = 0; i < N; i++) { arr.push(decoding.readVarString(decoder)) } }) }) const durationConcatElements = t.measureTime('read / write concatenated string', () => { let stringbuf = new Uint8Array() const encoder = encoding.createEncoder() const encoderLengths = encoding.createEncoder() t.measureTime('read / write concatenated string - write', () => { let s = '' for (let i = 0; i < N; i++) { s += 'i' encoding.writeVarUint(encoderLengths, 1) // we write a single char. if (i % 20 === 0) { encoding.writeVarString(encoder, s) s = '' } } encoding.writeVarString(encoder, s) stringbuf = encoding.toUint8Array(encoder) }) const decoder = decoding.createDecoder(stringbuf) const decoderLengths = decoding.createDecoder(encoding.toUint8Array(encoderLengths)) t.measureTime('read / write concatenated string - read', () => { const arr = [] const concatS = decoding.readVarString(decoder) for (let i = 0; i < N; i++) { const len = decoding.readVarUint(decoderLengths) arr.push(concatS.slice(i, len)) // push using slice } }) }) t.assert(durationConcatElements < 2 * durationSingleElements, 'We expect that the second approach is faster. If this fails, our expectantion is not met in your javascript environment. Please report this issue.') } /** * @param {t.TestCase} _tc */ export const testAnyEncodeUnknowns = _tc => { const encoder = encoding.createEncoder() // @ts-ignore encoding.writeAny(encoder, Symbol('a')) encoding.writeAny(encoder, undefined) encoding.writeAny(encoder, () => {}) const decoder = decoding.createDecoder(encoding.toUint8Array(encoder)) t.assert(decoding.readAny(decoder) === undefined) t.assert(decoding.readAny(decoder) === undefined) t.assert(decoding.readAny(decoder) === undefined) } /** * @param {t.TestCase} _tc */ export const testAnyEncodeDate = _tc => { test('Encode current date', encoding.writeAny, decoding.readAny, new Date().getTime()) } /** * @param {t.TestCase} _tc */ export const testEncodeMax32bitUint = _tc => { test('max 32bit uint', encoding.writeVarUint, decoding.readVarUint, binary.BITS32) } /** * @param {t.TestCase} _tc */ export const testVarUintEncoding = _tc => { test('varUint 1 byte', encoding.writeVarUint, decoding.readVarUint, 42) test('varUint 2 bytes', encoding.writeVarUint, decoding.readVarUint, 1 << 9 | 3) test('varUint 3 bytes', encoding.writeVarUint, decoding.readVarUint, 1 << 17 | 1 << 9 | 3) test('varUint 4 bytes', encoding.writeVarUint, decoding.readVarUint, 1 << 25 | 1 << 17 | 1 << 9 | 3) test('varUint of 2839012934', encoding.writeVarUint, decoding.readVarUint, 2839012934) test('varUint of 2^53', encoding.writeVarUint, decoding.readVarUint, number.MAX_SAFE_INTEGER) } /** * @param {t.TestCase} _tc */ export const testVarIntEncoding = _tc => { test('varInt 1 byte', encoding.writeVarInt, decoding.readVarInt, -42) test('varInt 2 bytes', encoding.writeVarInt, decoding.readVarInt, -(1 << 9 | 3)) test('varInt 3 bytes', encoding.writeVarInt, decoding.readVarInt, -(1 << 17 | 1 << 9 | 3)) test('varInt 4 bytes', encoding.writeVarInt, decoding.readVarInt, -(1 << 25 | 1 << 17 | 1 << 9 | 3)) test('varInt of -691529286', encoding.writeVarInt, decoding.readVarInt, -(691529286)) test('varInt of 2^53', encoding.writeVarInt, decoding.readVarInt, number.MAX_SAFE_INTEGER) test('varInt of -2^53', encoding.writeVarInt, decoding.readVarInt, number.MIN_SAFE_INTEGER) } /** * @param {t.TestCase} tc */ export const testRepeatVarUintEncoding = tc => { const n = prng.uint32(tc.prng, 0, (1 << 28) - 1) test(`varUint of ${n}`, encoding.writeVarUint, decoding.readVarUint, n, false) } /** * @param {t.TestCase} tc */ export const testRepeatVarUintEncoding53bit = tc => { const n = prng.uint53(tc.prng, 0, number.MAX_SAFE_INTEGER) test(`varUint of ${n}`, encoding.writeVarUint, decoding.readVarUint, n, false) } /** * @param {t.TestCase} tc */ export const testRepeatVarIntEncoding = tc => { const n = prng.int32(tc.prng, number.LOWEST_INT32, binary.BITS32) test(`varInt of ${n}`, encoding.writeVarInt, decoding.readVarInt, n, false) } /** * @param {t.TestCase} tc */ export const testRepeatVarIntEncoding53bit = tc => { const n = prng.int32(tc.prng, number.MIN_SAFE_INTEGER, number.MAX_SAFE_INTEGER) test(`varInt of ${n}`, encoding.writeVarInt, decoding.readVarInt, n, false) } /** * @param {t.TestCase} tc */ export const testRepeanntAnyEncoding = tc => { const n = genAny(tc.prng) test('any encoding', encoding.writeAny, decoding.readAny, n, false) } /** * @param {t.TestCase} tc */ export const testRepeatPeekVarUintEncoding = tc => { const n = prng.int32(tc.prng, 0, (1 << 28) - 1) test(`varUint of ${n}`, encoding.writeVarUint, decoding.peekVarUint, n, false) } /** * @param {t.TestCase} tc */ export const testRepeatPeekVarIntEncoding = tc => { const n = prng.int53(tc.prng, number.MIN_SAFE_INTEGER, number.MAX_SAFE_INTEGER) test(`varInt of ${n}`, encoding.writeVarInt, decoding.peekVarInt, n, false) } /** * @param {t.TestCase} tc */ export const testAnyVsJsonEncoding = tc => { const n = Array.from({ length: 5000 }).map(() => genAny(tc.prng, 5, true)) t.measureTime('lib0 any encoding', () => { const encoder = encoding.createEncoder() encoding.writeAny(encoder, n) const buffer = encoding.toUint8Array(encoder) t.info('buffer length is ' + buffer.length) decoding.readAny(decoding.createDecoder(buffer)) }) t.measureTime('JSON.stringify encoding', () => { const encoder = encoding.createEncoder() encoding.writeVarString(encoder, JSON.stringify(n)) const buffer = encoding.toUint8Array(encoder) t.info('buffer length is ' + buffer.length) JSON.parse(decoding.readVarString(decoding.createDecoder(buffer))) }) } /** * @param {t.TestCase} _tc */ export const testStringEncoding = _tc => { testVarString('hello') testVarString('test!') testVarString('☺☺☺') testVarString('') testVarString('1234') testVarString('쾟') testVarString('龟') // surrogate length 3 testVarString('😝') // surrogate length 4 } /** * @param {t.TestCase} tc */ export const testRepeatStringEncoding = tc => testVarString(prng.utf16String(tc.prng)) /** * @param {t.TestCase} _tc */ export const testSetMethods = _tc => { const encoder = encoding.createEncoder() encoding.writeUint8(encoder, 1) encoding.writeUint16(encoder, 33) encoding.writeUint32(encoder, 29329) encoding.setUint8(encoder, 0, 8) encoding.setUint16(encoder, 1, 16) encoding.setUint32(encoder, 3, 32) const buf = encoding.toUint8Array(encoder) const decoder = decoding.createDecoder(buf) t.assert(decoding.peekUint8(decoder) === 8) decoding.readUint8(decoder) t.assert(decoding.peekUint16(decoder) === 16) decoding.readUint16(decoder) t.assert(decoding.peekUint32(decoder) === 32) decoding.readUint32(decoder) } const defLen = 1000 const loops = 10000 /** * @param {any} a * @param {any} b * @return {boolean} */ const strictComparison = (a, b) => a === b /** * @typedef {Object} EncodingPair * @property {function(decoding.Decoder):any} EncodingPair.read * @property {function(encoding.Encoder,any):void} EncodingPair.write * @property {function(prng.PRNG):any} EncodingPair.gen * @property {function(any,any):boolean} EncodingPair.compare * @property {string} name */ /** * @template T * @type {Array} */ const encodingPairs = [ { name: 'uint8Array', read: decoder => decoding.readUint8Array(decoder, defLen), write: encoding.writeUint8Array, gen: gen => prng.uint8Array(gen, defLen), compare: t.compare }, { name: 'varUint8Array', read: decoding.readVarUint8Array, write: encoding.writeVarUint8Array, gen: gen => prng.uint8Array(gen, prng.uint32(gen, 0, defLen)), compare: t.compare }, { name: 'uint8', read: decoding.readUint8, write: encoding.writeUint8, gen: gen => prng.uint32(gen, 0, binary.BITS8), compare: strictComparison }, { name: 'uint16', read: decoding.readUint16, write: encoding.writeUint16, gen: gen => prng.uint32(gen, 0, binary.BITS16), compare: strictComparison }, { name: 'uint32', read: decoding.readUint32, write: encoding.writeUint32, gen: gen => prng.uint32(gen, 0, binary.BITS32), compare: strictComparison }, { name: 'uint32bigEndian', read: decoding.readUint32BigEndian, write: encoding.writeUint32BigEndian, gen: gen => prng.uint32(gen, 0, binary.BITS32), compare: strictComparison }, { name: 'varString', read: decoding.readVarString, write: encoding.writeVarString, gen: gen => prng.utf16String(gen, prng.uint32(gen, 0, defLen)), compare: strictComparison }, { name: 'varUint', read: decoding.readVarUint, write: encoding.writeVarUint, gen: gen => prng.uint53(gen, 0, number.MAX_SAFE_INTEGER), compare: strictComparison }, { name: 'varInt', read: decoding.readVarInt, write: encoding.writeVarInt, gen: gen => prng.int53(gen, number.MIN_SAFE_INTEGER, number.MAX_SAFE_INTEGER), compare: strictComparison }, { name: 'Any', read: decoding.readAny, write: encoding.writeAny, gen: genAny, compare: t.compare } ] /** * @param {t.TestCase} tc */ export const testRepeatRandomWrites = tc => { t.describe(`Writing ${loops} random values`, `defLen=${defLen}`) const gen = tc.prng /** * @type {any} */ const ops = [] const encoder = encoding.createEncoder() for (let i = 0; i < 10000; i++) { const pair = prng.oneOf(gen, encodingPairs) const val = pair.gen(gen) pair.write(encoder, val) ops.push({ compare: pair.compare, read: pair.read, val, name: pair.name }) } const tailData = prng.uint8Array(gen, prng.int32(gen, 0, defLen)) encoding.writeUint8Array(encoder, tailData) const buf = encoding.toUint8Array(encoder) const decoder = decoding.createDecoder(buf) t.assert(encoding.length(encoder) === buf.byteLength) for (let i = 0; i < ops.length; i++) { const o = ops[i] const val = o.read(decoder) t.assert(o.compare(val, o.val), o.name) } t.compare(tailData, decoding.readTailAsUint8Array(decoder)) } /** * @param {t.TestCase} _tc */ export const testWriteUint8ArrayOverflow = _tc => { const encoder = encoding.createEncoder() const initialLen = encoder.cbuf.byteLength const buf = buffer.createUint8ArrayFromLen(initialLen * 4) for (let i = 0; i < buf.length; i++) { buf[i] = i } encoding.writeUint8Array(encoder, buf) encoding.write(encoder, 42) const res = encoding.toUint8Array(encoder) t.assert(res.length === initialLen * 4 + 1) for (let i = 0; i < buf.length - 1; i++) { t.assert(res[i] === (i % 256)) } t.assert(res[initialLen * 4] === 42) } /** * @param {t.TestCase} _tc */ export const testSetOnOverflow = _tc => { const encoder = encoding.createEncoder() const initialLen = encoder.cbuf.byteLength encoder.cpos = initialLen - 2 encoding.writeUint32(encoder, binary.BITS32) const buf = encoding.toUint8Array(encoder) t.assert(encoding.length(encoder) === initialLen + 2) const decoder = decoding.createDecoder(buf) const space = buffer.createUint8ArrayFromArrayBuffer(decoding.readUint8Array(decoder, initialLen - 2)) for (let i = 0; i < initialLen - 2; i++) { t.assert(space[i] === 0) } t.assert(decoding.hasContent(decoder)) t.assert(binary.BITS32 === decoding.readUint32(decoder)) t.assert(!decoding.hasContent(decoder)) encoding.setUint8(encoder, 5, binary.BITS8) encoding.setUint8(encoder, initialLen + 1, 7) const buf2 = encoding.toUint8Array(encoder) t.assert(buf2[5] === binary.BITS8) t.assert(buf[5] === 0, 'old buffer is not affected') t.assert(buf2[initialLen + 1] === 7) } /** * @param {t.TestCase} _tc */ export const testCloneDecoder = _tc => { const encoder = encoding.createEncoder() encoding.writeUint8(encoder, 12132) encoding.writeVarUint(encoder, 329840128734) encoding.writeVarString(encoder, 'dtrnuiaednudiaendturinaedt nduiaen dturinaed ') const buf = encoding.toUint8Array(encoder) const decoder = decoding.createDecoder(buf) decoding.skip8(decoder) const decoder2 = decoding.clone(decoder) const payload1 = decoding.readTailAsUint8Array(decoder) const payload2 = decoding.readTailAsUint8Array(decoder2) t.compare(payload1, payload2) } /** * @param {t.TestCase} _tc */ export const testWriteBinaryEncoder = _tc => { const encoder = encoding.createEncoder() encoding.writeUint16(encoder, 4) const encoder2 = encoding.createEncoder() encoding.writeVarUint(encoder2, 143095) encoding.writeBinaryEncoder(encoder2, encoder) const buf = encoding.toUint8Array(encoder2) const decoder = decoding.createDecoder(buf) t.assert(decoding.readVarUint(decoder) === 143095) t.assert(decoding.readUint16(decoder) === 4) } /** * @param {t.TestCase} tc */ export const testOverflowStringDecoding = tc => { const gen = tc.prng const encoder = encoding.createEncoder() let longStr = '' while (longStr.length < 11000) { longStr += prng.utf16String(gen, 100000) } encoding.writeVarString(encoder, longStr) const buf = encoding.toUint8Array(encoder) const decoder = decoding.createDecoder(buf) t.assert(longStr === decoding.readVarString(decoder)) } /** * @param {t.TestCase} _tc */ export const testRleEncoder = _tc => { const N = 100 const encoder = new encoding.RleEncoder(encoding.writeVarUint) for (let i = 0; i < N; i++) { encoder.write(i) for (let j = 0; j < i; j++) { // write additional i times encoder.write(i) } } const decoder = new decoding.RleDecoder(encoding.toUint8Array(encoder), decoding.readVarUint) for (let i = 0; i < N; i++) { t.assert(i === decoder.read()) for (let j = 0; j < i; j++) { // read additional i times t.assert(i === decoder.read()) } } } /** * @param {t.TestCase} _tc */ export const testRleIntDiffEncoder = _tc => { const N = 100 const encoder = new encoding.RleIntDiffEncoder(0) for (let i = -N; i < N; i++) { encoder.write(i) for (let j = 0; j < i; j++) { // write additional i times encoder.write(i) } } const decoder = new decoding.RleIntDiffDecoder(encoding.toUint8Array(encoder), 0) for (let i = -N; i < N; i++) { t.assert(i === decoder.read()) for (let j = 0; j < i; j++) { // read additional i times t.assert(i === decoder.read()) } } } /** * @param {t.TestCase} _tc */ export const testUintOptRleEncoder = _tc => { const N = 100 const encoder = new encoding.UintOptRleEncoder() for (let i = 0; i < N; i++) { encoder.write(i) for (let j = 0; j < i; j++) { // write additional i times encoder.write(i) } } const decoder = new decoding.UintOptRleDecoder(encoder.toUint8Array()) for (let i = 0; i < N; i++) { t.assert(i === decoder.read()) for (let j = 0; j < i; j++) { // read additional i times t.assert(i === decoder.read()) } } } /** * @param {t.TestCase} _tc */ export const testIncUintOptRleEncoder = _tc => { const N = 100 const encoder = new encoding.IncUintOptRleEncoder() for (let i = 0; i < N; i++) { encoder.write(i) for (let j = 0; j < i; j++) { // write additional i times encoder.write(i) } } const decoder = new decoding.IncUintOptRleDecoder(encoder.toUint8Array()) for (let i = 0; i < N; i++) { t.assert(i === decoder.read()) for (let j = 0; j < i; j++) { // read additional i times t.assert(i === decoder.read()) } } } /** * @param {t.TestCase} _tc */ export const testIntDiffRleEncoder = _tc => { const N = 100 const encoder = new encoding.IntDiffOptRleEncoder() for (let i = -N; i < N; i++) { encoder.write(i) for (let j = 0; j < i; j++) { // write additional i times encoder.write(i) } } const decoder = new decoding.IntDiffOptRleDecoder(encoder.toUint8Array()) for (let i = -N; i < N; i++) { t.assert(i === decoder.read()) for (let j = 0; j < i; j++) { // read additional i times t.assert(i === decoder.read()) } } } /** * @param {t.TestCase} tc */ export const testIntEncoders = tc => { const arrLen = 10000 const gen = tc.prng /** * @type {Array} */ const vals = [] for (let i = 0; i < arrLen; i++) { if (prng.bool(gen)) { vals.push(prng.int53(gen, math.floor(number.MIN_SAFE_INTEGER / 2), math.floor(number.MAX_SAFE_INTEGER / 2))) } else { vals.push(prng.int32(gen, -10, 10)) } } /** * @type {Array<{ encoder: any, read: function(any):any }>} */ const intEncoders = [ { encoder: new encoding.IntDiffOptRleEncoder(), read: encoder => new decoding.IntDiffOptRleDecoder(encoder.toUint8Array()) }, { encoder: new encoding.IntDiffEncoder(0), read: encoder => new decoding.IntDiffDecoder(encoding.toUint8Array(encoder), 0) }, { encoder: new encoding.IntDiffEncoder(42), read: encoder => new decoding.IntDiffDecoder(encoding.toUint8Array(encoder), 42) }, { encoder: new encoding.RleIntDiffEncoder(0), read: encoder => new decoding.RleIntDiffDecoder(encoding.toUint8Array(encoder), 0) } ] intEncoders.forEach(({ encoder, read }) => { vals.forEach(v => encoder.write(v)) /** * @type {Array} */ const readVals = [] const dec = read(encoder) for (let i = 0; i < arrLen; i++) { readVals.push(dec.read()) } t.compare(vals, readVals) }) } /** * @param {t.TestCase} _tc */ export const testIntDiffEncoder = _tc => { const N = 100 const encoder = new encoding.IntDiffEncoder(0) for (let i = -N; i < N; i++) { encoder.write(i) } const decoder = new decoding.IntDiffDecoder(encoding.toUint8Array(encoder), 0) for (let i = -N; i < N; i++) { t.assert(i === decoder.read()) } } /** * @param {t.TestCase} tc */ export const testStringDecoder = tc => { const gen = tc.prng const N = 1000 const words = [] for (let i = 0; i < N; i++) { words.push(prng.utf16String(gen)) if (i % 100 === 0) { const char = prng.char(gen).slice(0, 1) words.push(char) words.push(char) } if (i % 107 === 0) { words.push(prng.word(gen, 3000, 8000)) } } const encoder = new encoding.StringEncoder() for (let i = 0; i < words.length; i++) { encoder.write(words[i]) } const decoder = new decoding.StringDecoder(encoder.toUint8Array()) for (let i = 0; i < words.length; i++) { t.assert(decoder.read() === words[i]) } } /** * @param {t.TestCase} tc */ export const testLargeNumberEncoding = tc => { const encoder = encoding.createEncoder() const num1 = -2.2062063918362897e+50 const num2 = BigInt(prng.int53(tc.prng, number.MIN_SAFE_INTEGER, number.MAX_SAFE_INTEGER)) const num3 = BigInt(prng.uint53(tc.prng, 0, number.MAX_SAFE_INTEGER)) const num4 = prng.real53(tc.prng) const num5 = 0.5 encoding.writeAny(encoder, num1) encoding.writeBigInt64(encoder, num2) encoding.writeBigUint64(encoder, num3) encoding.writeFloat64(encoder, num4) encoding.writeFloat32(encoder, num5) const decoder = decoding.createDecoder(encoding.toUint8Array(encoder)) const readNum1 = decoding.readAny(decoder) t.assert(readNum1 === num1) const readNum2 = decoding.readBigInt64(decoder) t.assert(readNum2 === num2) const readNum3 = decoding.readBigUint64(decoder) t.assert(readNum3 === num3) const readNum4 = decoding.readFloat64(decoder) t.assert(readNum4 === num4) const readNum5 = decoding.readFloat32(decoder) t.assert(readNum5 === num5) } /** * @param {t.TestCase} _tc */ export const testInvalidVarIntEncoding = _tc => { const encoded = new Uint8Array(1) encoded[0] = 255 const decoder = decoding.createDecoder(encoded) t.fails(() => { decoding.readVarInt(decoder) }) decoder.pos = 0 t.fails(() => { decoding.readVarUint(decoder) }) } /** * @param {t.TestCase} _tc */ export const testTerminatedEncodering = _tc => { const str1 = 'basic test' const str2 = 'hello\0world' // can handle escaped sequences in string const buf1 = new Uint8Array([0, 1, 2, 255, 4, 5]) const buf2 = new Uint8Array([255, 255, 0, 0, 0, 1, 0, 0]) const encoder = encoding.createEncoder() encoding.writeTerminatedString(encoder, str1) encoding.writeTerminatedString(encoder, str2) encoding.writeTerminatedUint8Array(encoder, buf1) encoding.writeTerminatedUint8Array(encoder, buf2) const decoder = decoding.createDecoder(encoding.toUint8Array(encoder)) const readStr1 = decoding.readTerminatedString(decoder) const readStr2 = decoding.readTerminatedString(decoder) const readBuf1 = decoding.readTerminatedUint8Array(decoder) const readBuf2 = decoding.readTerminatedUint8Array(decoder) t.assert(readStr1 === str1) t.assert(readStr2 === str2) t.compare(readBuf1, buf1) t.compare(readBuf2, buf2) } lib0-0.2.93/environment.js000066400000000000000000000074311457563604600153620ustar00rootroot00000000000000/** * Isomorphic module to work access the environment (query params, env variables). * * @module map */ import * as map from './map.js' import * as string from './string.js' import * as conditions from './conditions.js' import * as storage from './storage.js' import * as f from './function.js' /* c8 ignore next 2 */ // @ts-ignore export const isNode = typeof process !== 'undefined' && process.release && /node|io\.js/.test(process.release.name) && Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]' /* c8 ignore next */ export const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && !isNode /* c8 ignore next 3 */ export const isMac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false /** * @type {Map} */ let params const args = [] /* c8 ignore start */ const computeParams = () => { if (params === undefined) { if (isNode) { params = map.create() const pargs = process.argv let currParamName = null for (let i = 0; i < pargs.length; i++) { const parg = pargs[i] if (parg[0] === '-') { if (currParamName !== null) { params.set(currParamName, '') } currParamName = parg } else { if (currParamName !== null) { params.set(currParamName, parg) currParamName = null } else { args.push(parg) } } } if (currParamName !== null) { params.set(currParamName, '') } // in ReactNative for example this would not be true (unless connected to the Remote Debugger) } else if (typeof location === 'object') { params = map.create(); // eslint-disable-next-line no-undef (location.search || '?').slice(1).split('&').forEach((kv) => { if (kv.length !== 0) { const [key, value] = kv.split('=') params.set(`--${string.fromCamelCase(key, '-')}`, value) params.set(`-${string.fromCamelCase(key, '-')}`, value) } }) } else { params = map.create() } } return params } /* c8 ignore stop */ /** * @param {string} name * @return {boolean} */ /* c8 ignore next */ export const hasParam = (name) => computeParams().has(name) /** * @param {string} name * @param {string} defaultVal * @return {string} */ /* c8 ignore next 2 */ export const getParam = (name, defaultVal) => computeParams().get(name) || defaultVal /** * @param {string} name * @return {string|null} */ /* c8 ignore next 4 */ export const getVariable = (name) => isNode ? conditions.undefinedToNull(process.env[name.toUpperCase().replaceAll('-', '_')]) : conditions.undefinedToNull(storage.varStorage.getItem(name)) /** * @param {string} name * @return {string|null} */ /* c8 ignore next 2 */ export const getConf = (name) => computeParams().get('--' + name) || getVariable(name) /** * @param {string} name * @return {string} */ /* c8 ignore next 5 */ export const ensureConf = (name) => { const c = getConf(name) if (c == null) throw new Error(`Expected configuration "${name.toUpperCase().replaceAll('-', '_')}"`) return c } /** * @param {string} name * @return {boolean} */ /* c8 ignore next 2 */ export const hasConf = (name) => hasParam('--' + name) || getVariable(name) !== null /* c8 ignore next */ export const production = hasConf('production') /* c8 ignore next 2 */ const forceColor = isNode && f.isOneOf(process.env.FORCE_COLOR, ['true', '1', '2']) /* c8 ignore start */ export const supportsColor = !hasParam('no-colors') && (!isNode || process.stdout.isTTY || forceColor) && ( !isNode || hasParam('color') || forceColor || getVariable('COLORTERM') !== null || (getVariable('TERM') || '').includes('color') ) /* c8 ignore stop */ lib0-0.2.93/error.js000066400000000000000000000007061457563604600141450ustar00rootroot00000000000000/** * Error helpers. * * @module error */ /** * @param {string} s * @return {Error} */ /* c8 ignore next */ export const create = s => new Error(s) /** * @throws {Error} * @return {never} */ /* c8 ignore next 3 */ export const methodUnimplemented = () => { throw create('Method unimplemented') } /** * @throws {Error} * @return {never} */ /* c8 ignore next 3 */ export const unexpectedCase = () => { throw create('Unexpected case') } lib0-0.2.93/eventloop.js000066400000000000000000000046021457563604600150260ustar00rootroot00000000000000/* global requestIdleCallback, requestAnimationFrame, cancelIdleCallback, cancelAnimationFrame */ /** * Utility module to work with EcmaScript's event loop. * * @module eventloop */ /** * @type {Array} */ let queue = [] const _runQueue = () => { for (let i = 0; i < queue.length; i++) { queue[i]() } queue = [] } /** * @param {function():void} f */ export const enqueue = f => { queue.push(f) if (queue.length === 1) { setTimeout(_runQueue, 0) } } /** * @typedef {Object} TimeoutObject * @property {function} TimeoutObject.destroy */ /** * @param {function(number):void} clearFunction */ const createTimeoutClass = clearFunction => class TT { /** * @param {number} timeoutId */ constructor (timeoutId) { this._ = timeoutId } destroy () { clearFunction(this._) } } const Timeout = createTimeoutClass(clearTimeout) /** * @param {number} timeout * @param {function} callback * @return {TimeoutObject} */ export const timeout = (timeout, callback) => new Timeout(setTimeout(callback, timeout)) const Interval = createTimeoutClass(clearInterval) /** * @param {number} timeout * @param {function} callback * @return {TimeoutObject} */ export const interval = (timeout, callback) => new Interval(setInterval(callback, timeout)) /* c8 ignore next */ export const Animation = createTimeoutClass(arg => typeof requestAnimationFrame !== 'undefined' && cancelAnimationFrame(arg)) /** * @param {function(number):void} cb * @return {TimeoutObject} */ /* c8 ignore next */ export const animationFrame = cb => typeof requestAnimationFrame === 'undefined' ? timeout(0, cb) : new Animation(requestAnimationFrame(cb)) /* c8 ignore next */ // @ts-ignore const Idle = createTimeoutClass(arg => typeof cancelIdleCallback !== 'undefined' && cancelIdleCallback(arg)) /** * Note: this is experimental and is probably only useful in browsers. * * @param {function} cb * @return {TimeoutObject} */ /* c8 ignore next 2 */ // @ts-ignore export const idleCallback = cb => typeof requestIdleCallback !== 'undefined' ? new Idle(requestIdleCallback(cb)) : timeout(1000, cb) /** * @param {number} timeout Timeout of the debounce action * @return {function(function():void):void} */ export const createDebouncer = timeout => { let timer = -1 return f => { clearTimeout(timer) if (f) { timer = /** @type {any} */ (setTimeout(f, timeout)) } } } lib0-0.2.93/eventloop.test.js000066400000000000000000000034331457563604600160050ustar00rootroot00000000000000import * as eventloop from './eventloop.js' import * as t from './testing.js' import * as promise from './promise.js' /** * @param {t.TestCase} _tc */ export const testEventloopOrder = _tc => { let currI = 0 for (let i = 0; i < 10; i++) { const bi = i eventloop.enqueue(() => { t.assert(currI++ === bi) }) } eventloop.enqueue(() => { t.assert(currI === 10) }) t.assert(currI === 0) return promise.all([ promise.createEmpty(resolve => eventloop.enqueue(resolve)), promise.until(0, () => currI === 10) ]) } /** * @param {t.TestCase} _tc */ export const testTimeout = async _tc => { let set = false const timeout = eventloop.timeout(0, () => { set = true }) timeout.destroy() await promise.create(resolve => { eventloop.timeout(10, resolve) }) t.assert(set === false) } /** * @param {t.TestCase} _tc */ export const testInterval = async _tc => { let set = false const timeout = eventloop.interval(1, () => { set = true }) timeout.destroy() let i = 0 eventloop.interval(1, () => { i++ }) await promise.until(0, () => i > 2) t.assert(set === false) t.assert(i > 1) } /** * @param {t.TestCase} _tc */ export const testAnimationFrame = async _tc => { let x = false eventloop.animationFrame(() => { x = true }) await promise.until(0, () => x) t.assert(x) } /** * @param {t.TestCase} _tc */ export const testIdleCallback = async _tc => { await promise.create(resolve => { eventloop.idleCallback(resolve) }) } /** * @param {t.TestCase} _tc */ export const testDebouncer = async _tc => { const debounce = eventloop.createDebouncer(10) let calls = 0 debounce(() => { calls++ }) debounce(() => { calls++ }) t.assert(calls === 0) await promise.wait(20) t.assert(calls === 1) } lib0-0.2.93/function.js000066400000000000000000000071701457563604600146430ustar00rootroot00000000000000/** * Common functions and function call helpers. * * @module function */ import * as array from './array.js' import * as object from './object.js' /** * Calls all functions in `fs` with args. Only throws after all functions were called. * * @param {Array} fs * @param {Array} args */ export const callAll = (fs, args, i = 0) => { try { for (; i < fs.length; i++) { fs[i](...args) } } finally { if (i < fs.length) { callAll(fs, args, i + 1) } } } export const nop = () => {} /** * @template T * @param {function():T} f * @return {T} */ export const apply = f => f() /** * @template A * * @param {A} a * @return {A} */ export const id = a => a /** * @template T * * @param {T} a * @param {T} b * @return {boolean} */ export const equalityStrict = (a, b) => a === b /** * @template T * * @param {Array|object} a * @param {Array|object} b * @return {boolean} */ export const equalityFlat = (a, b) => a === b || (a != null && b != null && a.constructor === b.constructor && ((array.isArray(a) && array.equalFlat(a, /** @type {Array} */ (b))) || (typeof a === 'object' && object.equalFlat(a, b)))) /* c8 ignore start */ /** * @param {any} a * @param {any} b * @return {boolean} */ export const equalityDeep = (a, b) => { if (a == null || b == null) { return equalityStrict(a, b) } if (a.constructor !== b.constructor) { return false } if (a === b) { return true } switch (a.constructor) { case ArrayBuffer: a = new Uint8Array(a) b = new Uint8Array(b) // eslint-disable-next-line no-fallthrough case Uint8Array: { if (a.byteLength !== b.byteLength) { return false } for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) { return false } } break } case Set: { if (a.size !== b.size) { return false } for (const value of a) { if (!b.has(value)) { return false } } break } case Map: { if (a.size !== b.size) { return false } for (const key of a.keys()) { if (!b.has(key) || !equalityDeep(a.get(key), b.get(key))) { return false } } break } case Object: if (object.length(a) !== object.length(b)) { return false } for (const key in a) { if (!object.hasProperty(a, key) || !equalityDeep(a[key], b[key])) { return false } } break case Array: if (a.length !== b.length) { return false } for (let i = 0; i < a.length; i++) { if (!equalityDeep(a[i], b[i])) { return false } } break default: return false } return true } /** * @template V * @template {V} OPTS * * @param {V} value * @param {Array} options */ // @ts-ignore export const isOneOf = (value, options) => options.includes(value) /* c8 ignore stop */ export const isArray = array.isArray /** * @param {any} s * @return {s is String} */ export const isString = (s) => s && s.constructor === String /** * @param {any} n * @return {n is Number} */ export const isNumber = n => n != null && n.constructor === Number /** * @template {abstract new (...args: any) => any} TYPE * @param {any} n * @param {TYPE} T * @return {n is InstanceType} */ export const is = (n, T) => n && n.constructor === T /** * @template {abstract new (...args: any) => any} TYPE * @param {TYPE} T */ export const isTemplate = (T) => /** * @param {any} n * @return {n is InstanceType} **/ n => n && n.constructor === T lib0-0.2.93/function.test.js000066400000000000000000000066451457563604600156270ustar00rootroot00000000000000import * as f from './function.js' import * as t from './testing.js' /** * @param {t.TestCase} _tc */ export const testBasics = _tc => { let calls = 0 f.apply(() => calls++) t.assert(calls === 1) t.assert(f.isOneOf(1, [3, 2, 1])) t.assert(!f.isOneOf(0, [3, 2, 1])) // test is* const arr = [1, 'two', [1], { one: 1 }] arr.forEach(val => { if (f.isArray(val)) { /** * @type {Array} */ const yy = val t.assert(yy) } if (f.isString(val)) { /** * @type {string} */ const yy = val t.assert(yy) } if (f.isNumber(val)) { /** * @type {number} */ const yy = val t.assert(yy) } if (f.is(val, String)) { /** * @type {string} */ const yy = val t.assert(yy) } if (f.isTemplate(Number)(val)) { /** * @type {number} */ const yy = val t.assert(yy) } }) } /** * @param {t.TestCase} _tc */ export const testCallAll = _tc => { const err = new Error() let calls = 0 try { f.callAll([ () => { calls++ }, () => { throw err }, f.nop, () => { calls++ } ], []) } catch (e) { t.assert(calls === 2) return } t.fail('Expected callAll to throw error') } /** * @param {t.TestCase} _tc */ export const testDeepEquality = _tc => { t.assert(f.equalityDeep(1, 1)) t.assert(!f.equalityDeep(1, 2)) t.assert(!f.equalityDeep(1, '1')) t.assert(!f.equalityDeep(1, null)) const obj = { b: 5 } const map1 = new Map() const map2 = new Map() const map3 = new Map() const map4 = new Map() map1.set('a', obj) map2.set('a', { b: 5 }) map3.set('b', obj) map4.set('a', obj) map4.set('b', obj) t.assert(f.equalityDeep({ a: 4 }, { a: 4 })) t.assert(f.equalityDeep({ a: 4, obj: { b: 5 } }, { a: 4, obj })) t.assert(!f.equalityDeep({ a: 4 }, { a: 4, obj })) t.assert(f.equalityDeep({ a: [], obj }, { a: [], obj })) t.assert(!f.equalityDeep({ a: [], obj }, { a: [], obj: undefined })) t.assert(f.equalityDeep({}, {})) t.assert(!f.equalityDeep({}, { a: 4 })) t.assert(f.equalityDeep([{ a: 4 }, 1], [{ a: 4 }, 1])) t.assert(!f.equalityDeep([{ a: 4 }, 1], [{ a: 4 }, 2])) t.assert(!f.equalityDeep([{ a: 4 }, 1], [{ a: 4 }, 1, 3])) t.assert(f.equalityDeep([], [])) t.assert(!f.equalityDeep([1], [])) t.assert(f.equalityDeep(map1, map2)) t.assert(!f.equalityDeep(map1, map3)) t.assert(!f.equalityDeep(map1, map4)) const set1 = new Set([1]) const set2 = new Set([true]) const set3 = new Set([1, true]) const set4 = new Set([true]) t.assert(f.equalityDeep(set2, set4)) t.assert(!f.equalityDeep(set1, set2)) t.assert(!f.equalityDeep(set1, set3)) t.assert(!f.equalityDeep(set1, set4)) t.assert(!f.equalityDeep(set2, set3)) t.assert(f.equalityDeep(set2, set4)) const buf1 = Uint8Array.from([1, 2]) const buf2 = Uint8Array.from([1, 3]) const buf3 = Uint8Array.from([1, 2, 3]) const buf4 = Uint8Array.from([1, 2]) t.assert(!f.equalityDeep(buf1, buf2)) t.assert(!f.equalityDeep(buf2, buf3)) t.assert(!f.equalityDeep(buf3, buf4)) t.assert(f.equalityDeep(buf4, buf1)) t.assert(!f.equalityDeep(buf1.buffer, buf2.buffer)) t.assert(!f.equalityDeep(buf2.buffer, buf3.buffer)) t.assert(!f.equalityDeep(buf3.buffer, buf4.buffer)) t.assert(f.equalityDeep(buf4.buffer, buf1.buffer)) t.assert(!f.equalityDeep(buf1, buf4.buffer)) } lib0-0.2.93/hash/000077500000000000000000000000001457563604600133765ustar00rootroot00000000000000lib0-0.2.93/hash/rabin-gf2-polynomial.js000066400000000000000000000212701457563604600176660ustar00rootroot00000000000000/** * The idea of the Rabin fingerprint algorithm is to represent the binary as a polynomial in a * finite field (Galois Field G(2)). The polynomial will then be taken "modulo" by an irreducible * polynomial of the desired size. * * This implementation is inefficient and is solely used to verify the actually performant * implementation in `./rabin.js`. * * @module rabin-gf2-polynomial */ import * as math from '../math.js' import * as webcrypto from 'lib0/webcrypto' import * as array from '../array.js' import * as buffer from '../buffer.js' /** * @param {number} degree */ const _degreeToMinByteLength = degree => math.floor(degree / 8) + 1 /** * This is a GF2 Polynomial abstraction that is not meant for production! * * It is easy to understand and it's correctness is as obvious as possible. It can be used to verify * efficient implementations of algorithms on GF2. */ export class GF2Polynomial { constructor () { /** * @type {Set} */ this.degrees = new Set() } } /** * From Uint8Array (MSB). * * @param {Uint8Array} bytes */ export const createFromBytes = bytes => { const p = new GF2Polynomial() for (let bsi = bytes.length - 1, currDegree = 0; bsi >= 0; bsi--) { const currByte = bytes[bsi] for (let i = 0; i < 8; i++) { if (((currByte >>> i) & 1) === 1) { p.degrees.add(currDegree) } currDegree++ } } return p } /** * Transform to Uint8Array (MSB). * * @param {GF2Polynomial} p * @param {number} byteLength */ export const toUint8Array = (p, byteLength = _degreeToMinByteLength(getHighestDegree(p))) => { const buf = buffer.createUint8ArrayFromLen(byteLength) /** * @param {number} i */ const setBit = i => { const bi = math.floor(i / 8) buf[buf.length - 1 - bi] |= (1 << (i % 8)) } p.degrees.forEach(setBit) return buf } /** * Create from unsigned integer (max 32bit uint) - read most-significant-byte first. * * @param {number} uint */ export const createFromUint = uint => { const buf = new Uint8Array(4) for (let i = 0; i < 4; i++) { buf[i] = uint >>> 8 * (3 - i) } return createFromBytes(buf) } /** * Create a random polynomial of a specified degree. * * @param {number} degree */ export const createRandom = degree => { const bs = new Uint8Array(_degreeToMinByteLength(degree)) webcrypto.getRandomValues(bs) // Get first byte and explicitly set the bit of "degree" to 1 (the result must have the specified // degree). const firstByte = bs[0] | 1 << (degree % 8) // Find out how many bits of the first byte need to be filled with zeros because they are >degree. const zeros = 7 - (degree % 8) bs[0] = ((firstByte << zeros) & 0xff) >>> zeros return createFromBytes(bs) } /** * @param {GF2Polynomial} p * @return number */ export const getHighestDegree = p => array.fold(array.from(p.degrees), 0, math.max) /** * Add (+) p2 int the p1 polynomial. * * Addition is defined as xor in F2. Substraction is equivalent to addition in F2. * * @param {GF2Polynomial} p1 * @param {GF2Polynomial} p2 */ export const addInto = (p1, p2) => { p2.degrees.forEach(degree => { if (p1.degrees.has(degree)) { p1.degrees.delete(degree) } else { p1.degrees.add(degree) } }) } /** * Or (|) p2 into the p1 polynomial. * * Addition is defined as xor in F2. Substraction is equivalent to addition in F2. * * @param {GF2Polynomial} p1 * @param {GF2Polynomial} p2 */ export const orInto = (p1, p2) => { p2.degrees.forEach(degree => { p1.degrees.add(degree) }) } /** * Add (+) p2 to the p1 polynomial. * * Addition is defined as xor in F2. Substraction is equivalent to addition in F2. * * @param {GF2Polynomial} p1 * @param {GF2Polynomial} p2 */ export const add = (p1, p2) => { const result = new GF2Polynomial() p2.degrees.forEach(degree => { if (!p1.degrees.has(degree)) { result.degrees.add(degree) } }) p1.degrees.forEach(degree => { if (!p2.degrees.has(degree)) { result.degrees.add(degree) } }) return result } /** * Add (+) p2 to the p1 polynomial. * * Addition is defined as xor in F2. Substraction is equivalent to addition in F2. * * @param {GF2Polynomial} p */ export const clone = (p) => { const result = new GF2Polynomial() p.degrees.forEach(d => result.degrees.add(d)) return result } /** * Add (+) p2 to the p1 polynomial. * * Addition is defined as xor in F2. Substraction is equivalent to addition in F2. * * @param {GF2Polynomial} p * @param {number} degree */ export const addDegreeInto = (p, degree) => { if (p.degrees.has(degree)) { p.degrees.delete(degree) } else { p.degrees.add(degree) } } /** * Multiply (•) p1 with p2 and store the result in p1. * * @param {GF2Polynomial} p1 * @param {GF2Polynomial} p2 */ export const multiply = (p1, p2) => { const result = new GF2Polynomial() p1.degrees.forEach(degree1 => { p2.degrees.forEach(degree2 => { addDegreeInto(result, degree1 + degree2) }) }) return result } /** * Multiply (•) p1 with p2 and store the result in p1. * * @param {GF2Polynomial} p * @param {number} shift */ export const shiftLeft = (p, shift) => { const result = new GF2Polynomial() p.degrees.forEach(degree => { const r = degree + shift r >= 0 && result.degrees.add(r) }) return result } /** * Computes p1 % p2. I.e. the remainder of p1/p2. * * @param {GF2Polynomial} p1 * @param {GF2Polynomial} p2 */ export const mod = (p1, p2) => { const maxDeg1 = getHighestDegree(p1) const maxDeg2 = getHighestDegree(p2) const result = clone(p1) for (let i = maxDeg1 - maxDeg2; i >= 0; i--) { if (result.degrees.has(maxDeg2 + i)) { const shifted = shiftLeft(p2, i) addInto(result, shifted) } } return result } /** * Computes (p^e mod m). * * http://en.wikipedia.org/wiki/Modular_exponentiation * * @param {GF2Polynomial} p * @param {number} e * @param {GF2Polynomial} m */ export const modPow = (p, e, m) => { let result = ONE while (true) { if ((e & 1) === 1) { result = mod(multiply(result, p), m) } e >>>= 1 if (e === 0) { return result } p = mod(multiply(p, p), m) } } /** * Find the greatest common divisor using Euclid's Algorithm. * * @param {GF2Polynomial} p1 * @param {GF2Polynomial} p2 */ export const gcd = (p1, p2) => { while (p2.degrees.size > 0) { const modded = mod(p1, p2) p1 = p2 p2 = modded } return p1 } /** * true iff p1 equals p2 * * @param {GF2Polynomial} p1 * @param {GF2Polynomial} p2 */ export const equals = (p1, p2) => { if (p1.degrees.size !== p2.degrees.size) return false for (const d of p1.degrees) { if (!p2.degrees.has(d)) return false } return true } const X = createFromBytes(new Uint8Array([2])) const ONE = createFromBytes(new Uint8Array([1])) /** * Computes ( x^(2^p) - x ) mod f * * (shamelessly copied from * https://github.com/opendedup/rabinfingerprint/blob/master/src/org/rabinfingerprint/polynomial/Polynomial.java) * * @param {GF2Polynomial} f * @param {number} p */ const reduceExponent = (f, p) => { // compute (x^q^p mod f) const q2p = math.pow(2, p) const x2q2p = modPow(X, q2p, f) // subtract (x mod f) return mod(add(x2q2p, X), f) } /** * BenOr Reducibility Test * * Tests and Constructions of Irreducible Polynomials over Finite Fields * (1997) Shuhong Gao, Daniel Panario * * http://citeseer.ist.psu.edu/cache/papers/cs/27167/http:zSzzSzwww.math.clemson.eduzSzfacultyzSzGaozSzpaperszSzGP97a.pdf/gao97tests.pdf * * @param {GF2Polynomial} p */ export const isIrreducibleBenOr = p => { const degree = getHighestDegree(p) for (let i = 1; i < degree / 2; i++) { const b = reduceExponent(p, i) const g = gcd(p, b) if (!equals(g, ONE)) { return false } } return true } /** * @param {number} degree */ export const createIrreducible = degree => { while (true) { const p = createRandom(degree) if (isIrreducibleBenOr(p)) return p } } /** * Create a fingerprint of buf using the irreducible polynomial m. * * @param {Uint8Array} buf * @param {GF2Polynomial} m */ export const fingerprint = (buf, m) => toUint8Array(mod(createFromBytes(buf), m), _degreeToMinByteLength(getHighestDegree(m) - 1)) export class RabinPolynomialEncoder { /** * @param {GF2Polynomial} m The irreducible polynomial */ constructor (m) { this.fingerprint = new GF2Polynomial() this.m = m } /** * @param {number} b */ write (b) { const bp = createFromBytes(new Uint8Array([b])) const fingerprint = shiftLeft(this.fingerprint, 8) orInto(fingerprint, bp) this.fingerprint = mod(fingerprint, this.m) } getFingerprint () { return toUint8Array(this.fingerprint, _degreeToMinByteLength(getHighestDegree(this.m) - 1)) } } lib0-0.2.93/hash/rabin-uncached.js000066400000000000000000000033541457563604600166040ustar00rootroot00000000000000/** * It is not recommended to use this package. This is the uncached implementation of the rabin * fingerprint algorithm. However, it can be used to verify the `rabin.js` implementation. * * @module rabin-uncached */ import * as math from '../math.js' import * as buffer from '../buffer.js' export class RabinUncachedEncoder { /** * @param {Uint8Array} m assert(m[0] === 1) */ constructor (m) { this.m = m this.blen = m.byteLength this.bs = new Uint8Array(this.blen) /** * This describes the position of the most significant byte (starts with 0 and increases with * shift) */ this.bpos = 0 } /** * Add/Xor/Substract bytes. * * Discards bytes that are out of range. * @todo put this in function or inline * * @param {Uint8Array} cs */ add (cs) { const copyLen = math.min(this.blen, cs.byteLength) // copy from right to left until max is reached for (let i = 0; i < copyLen; i++) { this.bs[(this.bpos + this.blen - i - 1) % this.blen] ^= cs[cs.byteLength - i - 1] } } /** * @param {number} byte */ write (byte) { // [0,m1,m2,b] // x <- bpos // Shift one byte to the left, add b this.bs[this.bpos] = byte this.bpos = (this.bpos + 1) % this.blen // mod for (let i = 7; i >= 0; i--) { if (((this.bs[this.bpos] >>> i) & 1) === 1) { this.add(buffer.shiftNBitsLeft(this.m, i)) } } // if (this.bs[this.bpos] !== 0) { error.unexpectedCase() } // assert(this.bs[this.bpos] === 0) } getFingerprint () { const result = new Uint8Array(this.blen - 1) for (let i = 0; i < result.byteLength; i++) { result[i] = this.bs[(this.bpos + i + 1) % this.blen] } return result } } lib0-0.2.93/hash/rabin.js000066400000000000000000000057551457563604600150430ustar00rootroot00000000000000/** * @module rabin * * Very efficient & versatile fingerprint/hashing algorithm. However, it is not cryptographically * secure. Well suited for fingerprinting. */ import * as buffer from '../buffer.js' import * as map from '../map.js' export const StandardIrreducible8 = new Uint8Array([1, 221]) export const StandardIrreducible16 = new Uint8Array([1, 244, 157]) export const StandardIrreducible32 = new Uint8Array([1, 149, 183, 205, 191]) export const StandardIrreducible64 = new Uint8Array([1, 133, 250, 114, 193, 250, 28, 193, 231]) export const StandardIrreducible128 = new Uint8Array([1, 94, 109, 166, 228, 6, 222, 102, 239, 27, 128, 184, 13, 50, 112, 169, 199]) /** * Maps from a modulo to the precomputed values. * * @type {Map} */ const _precomputedFingerprintCache = new Map() /** * @param {Uint8Array} m */ const ensureCache = m => map.setIfUndefined(_precomputedFingerprintCache, buffer.toBase64(m), () => { const byteLen = m.byteLength const cache = new Uint8Array(256 * byteLen) // Use dynamic computing to compute the cached results. // Starting values: cache(0) = 0; cache(1) = m cache.set(m, byteLen) for (let bit = 1; bit < 8; bit++) { const mBitShifted = buffer.shiftNBitsLeft(m, bit) const bitShifted = 1 << bit for (let j = 0; j < bitShifted; j++) { // apply the shifted result (reducing the degree of the polynomial) const msb = bitShifted | j const rest = msb ^ mBitShifted[0] for (let i = 0; i < byteLen; i++) { // rest is already precomputed in the cache cache[msb * byteLen + i] = cache[rest * byteLen + i] ^ mBitShifted[i] } // if (cache[(bitShifted | j) * byteLen] !== (bitShifted | j)) { error.unexpectedCase() } } } return cache }) export class RabinEncoder { /** * @param {Uint8Array} m assert(m[0] === 1) */ constructor (m) { this.m = m this.blen = m.byteLength this.bs = new Uint8Array(this.blen) this.cache = ensureCache(m) /** * This describes the position of the most significant byte (starts with 0 and increases with * shift) */ this.bpos = 0 } /** * @param {number} byte */ write (byte) { // assert(this.bs[0] === 0) // Shift one byte to the left, add b this.bs[this.bpos] = byte this.bpos = (this.bpos + 1) % this.blen const msb = this.bs[this.bpos] for (let i = 0; i < this.blen; i++) { this.bs[(this.bpos + i) % this.blen] ^= this.cache[msb * this.blen + i] } // assert(this.bs[this.bpos] === 0) } getFingerprint () { const result = new Uint8Array(this.blen - 1) for (let i = 0; i < result.byteLength; i++) { result[i] = this.bs[(this.bpos + i + 1) % this.blen] } return result } } /** * @param {Uint8Array} irreducible * @param {Uint8Array} data */ export const fingerprint = (irreducible, data) => { const encoder = new RabinEncoder(irreducible) for (let i = 0; i < data.length; i++) { encoder.write(data[i]) } return encoder.getFingerprint() } lib0-0.2.93/hash/rabin.test.js000066400000000000000000000152451457563604600160140ustar00rootroot00000000000000import * as t from '../testing.js' import * as gf2 from './rabin-gf2-polynomial.js' import { RabinUncachedEncoder } from './rabin-uncached.js' import * as rabin from './rabin.js' import * as math from '../math.js' import * as array from '../array.js' import * as prng from '../prng.js' import * as buffer from '../buffer.js' import * as map from '../map.js' /** * @param {t.TestCase} _tc */ export const testPolynomialBasics = _tc => { const bs = new Uint8Array([1, 11]) const p = gf2.createFromBytes(bs) t.assert(p.degrees.has(3)) t.assert(p.degrees.has(1)) t.assert(p.degrees.has(0)) t.assert(p.degrees.has(8)) } /** * @param {t.TestCase} _tc */ export const testIrreducibleInput = _tc => { const pa = gf2.createFromUint(0x53) const pb = gf2.createFromUint(0xCA) const pm = gf2.createFromUint(0x11B) const px = gf2.multiply(pa, pb) t.compare(new Uint8Array([0x53]), gf2.toUint8Array(pa)) t.compare(new Uint8Array([0xCA]), gf2.toUint8Array(pb)) t.assert(gf2.equals(gf2.createFromUint(0x3F7E), px)) t.compare(new Uint8Array([0x3F, 0x7E]), gf2.toUint8Array(px)) const pabm = gf2.mod(px, pm) t.compare(new Uint8Array([0x1]), gf2.toUint8Array(pabm)) } /** * @param {t.TestCase} _tc */ export const testIrreducibleSpread = _tc => { const degree = 32 const N = 1000 const avgSpread = getSpreadAverage(degree, N) const diffSpread = math.abs(avgSpread - degree) t.info(`Average spread for degree ${degree} at ${N} repetitions: ${avgSpread}`) t.assert(diffSpread < 4, 'Spread of irreducible polynomials is within expected range') } /** * @param {number} degree * @param {number} tests */ const getSpreadAverage = (degree, tests) => { const spreads = [] for (let i = 0, test = 0, lastI = 0; test < tests; i++) { const f = gf2.createRandom(degree) t.assert(gf2.getHighestDegree(f) === degree) if (gf2.isIrreducibleBenOr(f)) { const spread = i - lastI spreads.push(spread) lastI = i test++ } } return array.fold(spreads, 0, math.add) / tests } /** * @param {t.TestCase} _tc */ export const testGenerateIrreducibles = _tc => { /** * @param {number} byteLen */ const testIrreducibleGen = byteLen => { const K = byteLen * 8 const irr = gf2.createIrreducible(K) t.assert(gf2.getHighestDegree(irr) === K, 'degree equals K') const irrBs = gf2.toUint8Array(irr) console.log(`K = ${K}`, irrBs) t.assert(irrBs[0] === 1) t.assert(irrBs.byteLength === byteLen + 1) } testIrreducibleGen(1) testIrreducibleGen(2) testIrreducibleGen(4) testIrreducibleGen(8) testIrreducibleGen(16) gf2.isIrreducibleBenOr(gf2.createFromBytes(rabin.StandardIrreducible8)) gf2.isIrreducibleBenOr(gf2.createFromBytes(rabin.StandardIrreducible16)) gf2.isIrreducibleBenOr(gf2.createFromBytes(rabin.StandardIrreducible32)) gf2.isIrreducibleBenOr(gf2.createFromBytes(rabin.StandardIrreducible64)) gf2.isIrreducibleBenOr(gf2.createFromBytes(rabin.StandardIrreducible128)) } /** * @param {t.TestCase} tc * @param {number} K */ const _testFingerprintCompatiblityK = (tc, K) => { /** * @type {Array} */ const dataObjects = [] const N = 300 const MSIZE = 130 t.info(`N=${N} K=${K} MSIZE=${MSIZE}`) /** * @type {gf2.GF2Polynomial} */ let irreducible /** * @type {Uint8Array} */ let irreducibleBuffer t.measureTime(`find irreducible of ${K}`, () => { irreducible = gf2.createIrreducible(K) irreducibleBuffer = gf2.toUint8Array(irreducible) }) for (let i = 0; i < N; i++) { dataObjects.push(prng.uint8Array(tc.prng, MSIZE)) } /** * @type {Array} */ let fingerprints1 = [] t.measureTime('polynomial direct', () => { fingerprints1 = dataObjects.map((o, _index) => gf2.fingerprint(o, irreducible)) }) const testSet = new Set(fingerprints1.map(buffer.toBase64)) t.assert(K < 32 || testSet.size === N) /** * @type {Array} */ let fingerprints2 = [] t.measureTime('polynomial incremental', () => { fingerprints2 = dataObjects.map((o, _index) => { const encoder = new gf2.RabinPolynomialEncoder(irreducible) for (let i = 0; i < o.byteLength; i++) { encoder.write(o[i]) } return encoder.getFingerprint() }) }) t.compare(fingerprints1, fingerprints2) /** * @type {Array} */ let fingerprints3 = [] t.measureTime('polynomial incremental (efficent))', () => { fingerprints3 = dataObjects.map((o, _index) => { const encoder = new RabinUncachedEncoder(irreducibleBuffer) for (let i = 0; i < o.byteLength; i++) { encoder.write(o[i]) } return encoder.getFingerprint() }) }) t.compare(fingerprints1, fingerprints3) // ensuring that the cache is already populated // @ts-ignore // eslint-disable-next-line new rabin.RabinEncoder(irreducibleBuffer) /** * @type {Array} */ let fingerprints4 = [] t.measureTime('polynomial incremental (efficent & cached)) using encoder', () => { fingerprints4 = dataObjects.map((o, _index) => { const encoder = new rabin.RabinEncoder(irreducibleBuffer) for (let i = 0; i < o.byteLength; i++) { encoder.write(o[i]) } return encoder.getFingerprint() }) }) t.compare(fingerprints1, fingerprints4) /** * @type {Array} */ let fingerprints5 = [] t.measureTime('polynomial incremental (efficent & cached))', () => { fingerprints5 = dataObjects.map((o, _index) => { return rabin.fingerprint(irreducibleBuffer, o) }) }) t.compare(fingerprints1, fingerprints5) } /** * @param {t.TestCase} tc */ export const testFingerprintCompatiblity = tc => { _testFingerprintCompatiblityK(tc, 8) _testFingerprintCompatiblityK(tc, 16) _testFingerprintCompatiblityK(tc, 32) _testFingerprintCompatiblityK(tc, 64) _testFingerprintCompatiblityK(tc, 128) } /** * @param {t.TestCase} tc */ export const testConflicts = tc => { /** * @type {Array} */ const data = [] const N = 100 const Irr = rabin.StandardIrreducible8 t.measureTime(`generate ${N} items`, () => { for (let i = 0; i < N; i++) { data.push(prng.uint8Array(tc.prng, prng.uint32(tc.prng, 5, 50))) } }) /** * @type {Map>} */ const results = new Map() t.measureTime(`fingerprint ${N} items`, () => { data.forEach(d => { const f = buffer.toBase64(rabin.fingerprint(Irr, d)) map.setIfUndefined(results, f, () => new Set()).add(buffer.toBase64(d)) }) }) const conflicts = array.fold(map.map(results, (ds) => ds.size - 1), 0, math.add) const usedFields = results.size const unusedFieds = math.pow(2, (Irr.length - 1) * 8) - results.size console.log({ conflicts, usedFields, unusedFieds }) } lib0-0.2.93/hash/sha256.js000066400000000000000000000121721457563604600147470ustar00rootroot00000000000000/** * @module sha256 * Spec: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf * Resources: * - https://web.archive.org/web/20150315061807/http://csrc.nist.gov/groups/STM/cavp/documents/shs/sha256-384-512.pdf */ import * as binary from '../binary.js' /** * @param {number} w - a 32bit uint * @param {number} shift */ const rotr = (w, shift) => (w >>> shift) | (w << (32 - shift)) /** * Helper for SHA-224 & SHA-256. See 4.1.2. * @param {number} x */ const sum0to256 = x => rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22) /** * Helper for SHA-224 & SHA-256. See 4.1.2. * @param {number} x */ const sum1to256 = x => rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25) /** * Helper for SHA-224 & SHA-256. See 4.1.2. * @param {number} x */ const sigma0to256 = x => rotr(x, 7) ^ rotr(x, 18) ^ x >>> 3 /** * Helper for SHA-224 & SHA-256. See 4.1.2. * @param {number} x */ const sigma1to256 = x => rotr(x, 17) ^ rotr(x, 19) ^ x >>> 10 // @todo don't init these variables globally /** * See 4.2.2: Constant for sha256 & sha224 * These words represent the first thirty-two bits of the fractional parts of * the cube roots of the first sixty-four prime numbers. In hex, these constant words are (from left to * right) */ const K = new Uint32Array([ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 ]) /** * See 5.3.3. Initial hash value. * * These words were obtained by taking the first thirty-two bits of the fractional parts of the * square roots of the first eight prime numbers. * * @todo shouldn't be a global variable */ const HINIT = new Uint32Array([ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ]) // time to beat: (large value < 4.35s) class Hasher { constructor () { const buf = new ArrayBuffer(64 + 64 * 4) // Init working variables using a single arraybuffer this._H = new Uint32Array(buf, 0, 8) this._H.set(HINIT) // "Message schedule" - a working variable this._W = new Uint32Array(buf, 64, 64) } _updateHash () { const H = this._H const W = this._W for (let t = 16; t < 64; t++) { W[t] = sigma1to256(W[t - 2]) + W[t - 7] + sigma0to256(W[t - 15]) + W[t - 16] } let a = H[0] let b = H[1] let c = H[2] let d = H[3] let e = H[4] let f = H[5] let g = H[6] let h = H[7] for (let tt = 0, T1, T2; tt < 64; tt++) { T1 = (h + sum1to256(e) + ((e & f) ^ (~e & g)) + K[tt] + W[tt]) >>> 0 T2 = (sum0to256(a) + ((a & b) ^ (a & c) ^ (b & c))) >>> 0 h = g g = f f = e e = (d + T1) >>> 0 d = c c = b b = a a = (T1 + T2) >>> 0 } H[0] += a H[1] += b H[2] += c H[3] += d H[4] += e H[5] += f H[6] += g H[7] += h } /** * @param {Uint8Array} data */ digest (data) { let i = 0 for (; i + 56 <= data.length;) { // write data in big endianess let j = 0 for (; j < 16 && i + 3 < data.length; j++) { this._W[j] = data[i++] << 24 | data[i++] << 16 | data[i++] << 8 | data[i++] } if (i % 64 !== 0) { // there is still room to write partial content and the ending bit. this._W.fill(0, j, 16) while (i < data.length) { this._W[j] |= data[i] << ((3 - (i % 4)) * 8) i++ } this._W[j] |= binary.BIT8 << ((3 - (i % 4)) * 8) } this._updateHash() } // same check as earlier - the ending bit has been written const isPaddedWith1 = i % 64 !== 0 this._W.fill(0, 0, 16) let j = 0 for (; i < data.length; j++) { for (let ci = 3; ci >= 0 && i < data.length; ci--) { this._W[j] |= data[i++] << (ci * 8) } } // Write padding of the message. See 5.1.2. if (!isPaddedWith1) { this._W[j - (i % 4 === 0 ? 0 : 1)] |= binary.BIT8 << ((3 - (i % 4)) * 8) } // write length of message (size in bits) as 64 bit uint // @todo test that this works correctly this._W[14] = data.byteLength / binary.BIT30 // same as data.byteLength >>> 30 - but works on floats this._W[15] = data.byteLength * 8 this._updateHash() // correct H endianness to use big endiannes and return a Uint8Array const dv = new Uint8Array(32) for (let i = 0; i < this._H.length; i++) { for (let ci = 0; ci < 4; ci++) { dv[i * 4 + ci] = this._H[i] >>> (3 - ci) * 8 } } return dv } } /** * @param {Uint8Array} data */ export const digest = data => new Hasher().digest(data) lib0-0.2.93/hash/sha256.node.js000066400000000000000000000003051457563604600156660ustar00rootroot00000000000000import { createHash } from 'node:crypto' /** * @param {Uint8Array} data */ export const digest = data => { const hasher = createHash('sha256') hasher.update(data) return hasher.digest() } lib0-0.2.93/hash/sha256.test.js000066400000000000000000000133371457563604600157310ustar00rootroot00000000000000import * as t from '../testing.js' import * as sha256 from './sha256.js' import * as buffer from '../buffer.js' import * as string from '../string.js' import * as prng from '../prng.js' import * as webcrypto from 'lib0/webcrypto' import * as promise from '../promise.js' import * as env from '../environment.js' import * as array from '../array.js' import * as f from '../function.js' /** * @param {t.TestCase} _tc */ export const testSelfReferencingHash = _tc => { const hash = sha256.digest(string.encodeUtf8('The SHA256 for this sentence begins with: one, eight, two, a, seven, c and nine.')) t.assert(buffer.toHexString(hash).startsWith('182a7c9')) } /** * @param {t.TestCase} _tc */ export const testSha256Basics = async _tc => { /** * @param {string | Uint8Array} data input data (buffer or hex encoded) * @param {string} result Expected result (hex encoded) */ const test = async (data, result) => { data = typeof data === 'string' ? buffer.fromHexString(data) : data const res = sha256.digest(data) const resHex = buffer.toHexString(res) t.assert(resHex === result) const resWebcrypto = new Uint8Array(await webcrypto.subtle.digest('SHA-256', data)) const resWebcryptoHex = buffer.toHexString(resWebcrypto) t.assert(resWebcryptoHex === result) } // const newInput = 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopno' // const xx = new Uint8Array(await webcrypto.subtle.digest('SHA-256', string.encodeUtf8(newInput))) // console.log(buffer.toHexString(xx), ' dtrndtndtn', newInput.length) await test('', 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855') await test(string.encodeUtf8('ab'), 'fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603') await test(string.encodeUtf8('abc'), 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad') await test(string.encodeUtf8('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopno'), '71806f0af18dbbe905f8fcaa576b8e687859163cf68b38bc26f32e5120522cc1') await test(string.encodeUtf8('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'), '248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1') await test(string.encodeUtf8('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopqqqq0001'), 'e54ad86cad2d9d7c03506d6a67ed03fab5fbb8d34012143e9c0eb88ace56ca59') await test(string.encodeUtf8('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopqqqq00012'), '7a1e062405a534817dcb89fa2416a69e4fbe75fabece33d528b82d1b71d3e418') await test(string.encodeUtf8('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopqqqq000123'), '51691fe639226a9df2c0e4637918990291c73cdbb58665a7c729bb4e8d67784c') } /** * Test if implementation is correct when length (in bits) exceeds uint32. * * @param {t.TestCase} _tc */ export const testLargeValue = async _tc => { t.skip(!t.extensive) const BS = 100 * 1000 * 1000 const data = prng.uint8Array(prng.create(42), BS) let resNode = buffer.fromBase64('81m7UtkH2s9J3E33Bw5kRMuC5zvktgZ64SfzenbV5Lw=') let resLib0 let resWebcrypto if (env.isNode) { const sha256Node = await import('./sha256.node.js') t.measureTime(`[node] Hash message of size ${BS}`, () => { const res = new Uint8Array(sha256Node.digest(data)) if (!f.equalityDeep(res, resNode)) { console.warn(`Precomputed result should be the same! New result: ${buffer.toBase64(res)}`) } resNode = res t.compare(res, resNode, 'Precomputed result should be the same') }) } t.measureTime(`[lib0] Hash message of size ${BS}`, () => { resLib0 = sha256.digest(data) }) await t.measureTimeAsync(`[webcrypto] Hash message of size ${BS}`, async () => { resWebcrypto = new Uint8Array(await webcrypto.subtle.digest('SHA-256', data)) }) t.compare(resLib0, resNode) t.compare(resLib0, resWebcrypto) } /** * @param {t.TestCase} tc */ export const testRepeatSha256Hashing = async tc => { const LEN = prng.bool(tc.prng) ? prng.uint32(tc.prng, 0, 512) : prng.uint32(tc.prng, 0, 3003030) const data = prng.uint8Array(tc.prng, LEN) const hashedCustom = sha256.digest(data) const hashedWebcrypto = new Uint8Array(await webcrypto.subtle.digest('SHA-256', data)) t.compare(hashedCustom, hashedWebcrypto) } /** * @param {t.TestCase} _tc */ export const testBenchmarkSha256 = async _tc => { /** * @param {number} N * @param {number} BS */ const bench = (N, BS) => t.groupAsync(`Hash ${N} random values of size ${BS}`, async () => { const gen = prng.create(42) const datas = array.unfold(N, () => prng.uint8Array(gen, BS)) t.measureTime('lib0 (fallback))', () => { for (let i = 0; i < N; i++) { const x = sha256.digest(datas[i]) if (x === null) throw new Error() } }) if (env.isNode) { const nodeSha = await import('./sha256.node.js') t.measureTime('lib0 (node))', () => { for (let i = 0; i < N; i++) { const x = nodeSha.digest(datas[i]) if (x === null) throw new Error() } }) } await t.measureTimeAsync('webcrypto sequentially', async () => { for (let i = 0; i < N; i++) { const x = await webcrypto.subtle.digest('SHA-256', datas[i]) if (x === null) throw new Error() } }) await t.measureTimeAsync('webcrypto concurrent', async () => { /** * @type {Array>} */ const ps = [] for (let i = 0; i < N; i++) { ps.push(webcrypto.subtle.digest('SHA-256', datas[i])) } const x = await promise.all(ps) if (x === null) throw new Error() }) }) await bench(10 * 1000, 10) await bench(10 * 1000, 50) t.skip(!t.extensive) await bench(10 * 1000, 100) await bench(10 * 1000, 500) await bench(10 * 1000, 1000) await bench(10 * 1000, 4098) await bench(10, 5 * 1000 * 1000) } lib0-0.2.93/index.js000066400000000000000000000035771457563604600141340ustar00rootroot00000000000000/** * Experimental method to import lib0. * * Not recommended if the module bundler doesn't support dead code elimination. * * @module lib0 */ import * as array from './array.js' import * as binary from './binary.js' import * as broadcastchannel from './broadcastchannel.js' import * as buffer from './buffer.js' import * as conditions from './conditions.js' import * as decoding from './decoding.js' import * as diff from './diff.js' import * as dom from './dom.js' import * as encoding from './encoding.js' import * as environment from './environment.js' import * as error from './error.js' import * as eventloop from './eventloop.js' // @todo rename file to func import * as func from './function.js' import * as indexeddb from './indexeddb.js' import * as iterator from './iterator.js' import * as json from './json.js' import * as logging from 'lib0/logging' import * as map from './map.js' import * as math from './math.js' import * as mutex from './mutex.js' import * as number from './number.js' import * as object from './object.js' import * as pair from './pair.js' import * as prng from './prng.js' import * as promise from './promise.js' // import * as random from './random.js' import * as set from './set.js' import * as sort from './sort.js' import * as statistics from './statistics.js' import * as string from './string.js' import * as symbol from './symbol.js' // import * as testing from './testing.js' import * as time from './time.js' import * as tree from './tree.js' import * as websocket from './websocket.js' export { array, binary, broadcastchannel, buffer, conditions, decoding, diff, dom, encoding, environment, error, eventloop, func, indexeddb, iterator, json, logging, map, math, mutex, number, object, pair, prng, promise, // random, set, sort, statistics, string, symbol, // testing, time, tree, websocket } lib0-0.2.93/indexeddb.js000066400000000000000000000155431457563604600147470ustar00rootroot00000000000000/* eslint-env browser */ /** * Helpers to work with IndexedDB. * * @module indexeddb */ import * as promise from './promise.js' import * as error from './error.js' /* c8 ignore start */ /** * IDB Request to Promise transformer * * @param {IDBRequest} request * @return {Promise} */ export const rtop = request => promise.create((resolve, reject) => { // @ts-ignore request.onerror = event => reject(new Error(event.target.error)) // @ts-ignore request.onsuccess = event => resolve(event.target.result) }) /** * @param {string} name * @param {function(IDBDatabase):any} initDB Called when the database is first created * @return {Promise} */ export const openDB = (name, initDB) => promise.create((resolve, reject) => { const request = indexedDB.open(name) /** * @param {any} event */ request.onupgradeneeded = event => initDB(event.target.result) /** * @param {any} event */ request.onerror = event => reject(error.create(event.target.error)) /** * @param {any} event */ request.onsuccess = event => { /** * @type {IDBDatabase} */ const db = event.target.result db.onversionchange = () => { db.close() } resolve(db) } }) /** * @param {string} name */ export const deleteDB = name => rtop(indexedDB.deleteDatabase(name)) /** * @param {IDBDatabase} db * @param {Array|Array>} definitions */ export const createStores = (db, definitions) => definitions.forEach(d => // @ts-ignore db.createObjectStore.apply(db, d) ) /** * @param {IDBDatabase} db * @param {Array} stores * @param {"readwrite"|"readonly"} [access] * @return {Array} */ export const transact = (db, stores, access = 'readwrite') => { const transaction = db.transaction(stores, access) return stores.map(store => getStore(transaction, store)) } /** * @param {IDBObjectStore} store * @param {IDBKeyRange} [range] * @return {Promise} */ export const count = (store, range) => rtop(store.count(range)) /** * @param {IDBObjectStore} store * @param {String | number | ArrayBuffer | Date | Array } key * @return {Promise>} */ export const get = (store, key) => rtop(store.get(key)) /** * @param {IDBObjectStore} store * @param {String | number | ArrayBuffer | Date | IDBKeyRange | Array } key */ export const del = (store, key) => rtop(store.delete(key)) /** * @param {IDBObjectStore} store * @param {String | number | ArrayBuffer | Date | boolean} item * @param {String | number | ArrayBuffer | Date | Array} [key] */ export const put = (store, item, key) => rtop(store.put(item, key)) /** * @param {IDBObjectStore} store * @param {String | number | ArrayBuffer | Date | boolean} item * @param {String | number | ArrayBuffer | Date | Array} key * @return {Promise} */ export const add = (store, item, key) => rtop(store.add(item, key)) /** * @param {IDBObjectStore} store * @param {String | number | ArrayBuffer | Date} item * @return {Promise} Returns the generated key */ export const addAutoKey = (store, item) => rtop(store.add(item)) /** * @param {IDBObjectStore} store * @param {IDBKeyRange} [range] * @param {number} [limit] * @return {Promise>} */ export const getAll = (store, range, limit) => rtop(store.getAll(range, limit)) /** * @param {IDBObjectStore} store * @param {IDBKeyRange} [range] * @param {number} [limit] * @return {Promise>} */ export const getAllKeys = (store, range, limit) => rtop(store.getAllKeys(range, limit)) /** * @param {IDBObjectStore} store * @param {IDBKeyRange|null} query * @param {'next'|'prev'|'nextunique'|'prevunique'} direction * @return {Promise} */ export const queryFirst = (store, query, direction) => { /** * @type {any} */ let first = null return iterateKeys(store, query, key => { first = key return false }, direction).then(() => first) } /** * @param {IDBObjectStore} store * @param {IDBKeyRange?} [range] * @return {Promise} */ export const getLastKey = (store, range = null) => queryFirst(store, range, 'prev') /** * @param {IDBObjectStore} store * @param {IDBKeyRange?} [range] * @return {Promise} */ export const getFirstKey = (store, range = null) => queryFirst(store, range, 'next') /** * @typedef KeyValuePair * @type {Object} * @property {any} k key * @property {any} v Value */ /** * @param {IDBObjectStore} store * @param {IDBKeyRange} [range] * @param {number} [limit] * @return {Promise>} */ export const getAllKeysValues = (store, range, limit) => // @ts-ignore promise.all([getAllKeys(store, range, limit), getAll(store, range, limit)]).then(([ks, vs]) => ks.map((k, i) => ({ k, v: vs[i] }))) /** * @param {any} request * @param {function(IDBCursorWithValue):void|boolean|Promise} f * @return {Promise} */ const iterateOnRequest = (request, f) => promise.create((resolve, reject) => { request.onerror = reject /** * @param {any} event */ request.onsuccess = async event => { const cursor = event.target.result if (cursor === null || (await f(cursor)) === false) { return resolve() } cursor.continue() } }) /** * Iterate on keys and values * @param {IDBObjectStore} store * @param {IDBKeyRange|null} keyrange * @param {function(any,any):void|boolean|Promise} f Callback that receives (value, key) * @param {'next'|'prev'|'nextunique'|'prevunique'} direction */ export const iterate = (store, keyrange, f, direction = 'next') => iterateOnRequest(store.openCursor(keyrange, direction), cursor => f(cursor.value, cursor.key)) /** * Iterate on the keys (no values) * * @param {IDBObjectStore} store * @param {IDBKeyRange|null} keyrange * @param {function(any):void|boolean|Promise} f callback that receives the key * @param {'next'|'prev'|'nextunique'|'prevunique'} direction */ export const iterateKeys = (store, keyrange, f, direction = 'next') => iterateOnRequest(store.openKeyCursor(keyrange, direction), cursor => f(cursor.key)) /** * Open store from transaction * @param {IDBTransaction} t * @param {String} store * @returns {IDBObjectStore} */ export const getStore = (t, store) => t.objectStore(store) /** * @param {any} lower * @param {any} upper * @param {boolean} lowerOpen * @param {boolean} upperOpen */ export const createIDBKeyRangeBound = (lower, upper, lowerOpen, upperOpen) => IDBKeyRange.bound(lower, upper, lowerOpen, upperOpen) /** * @param {any} upper * @param {boolean} upperOpen */ export const createIDBKeyRangeUpperBound = (upper, upperOpen) => IDBKeyRange.upperBound(upper, upperOpen) /** * @param {any} lower * @param {boolean} lowerOpen */ export const createIDBKeyRangeLowerBound = (lower, lowerOpen) => IDBKeyRange.lowerBound(lower, lowerOpen) /* c8 ignore stop */ lib0-0.2.93/indexeddb.test.js000066400000000000000000000063701457563604600157230ustar00rootroot00000000000000import * as t from './testing.js' import * as idb from './indexeddb.js' import { isBrowser } from './environment.js' /* c8 ignore next */ /** * @param {IDBDatabase} db */ const initTestDB = db => idb.createStores(db, [['test', { autoIncrement: true }]]) const testDBName = 'idb-test' /* c8 ignore next */ /** * @param {IDBDatabase} db */ const createTransaction = db => db.transaction(['test'], 'readwrite') /* c8 ignore next */ /** * @param {IDBTransaction} t * @return {IDBObjectStore} */ const getStore = t => idb.getStore(t, 'test') /* c8 ignore next */ export const testRetrieveElements = async () => { t.skip(!isBrowser) t.describe('create, then iterate some keys') await idb.deleteDB(testDBName) const db = await idb.openDB(testDBName, initTestDB) const transaction = createTransaction(db) const store = getStore(transaction) await idb.put(store, 0, ['t', 1]) await idb.put(store, 1, ['t', 2]) const expectedKeys = [['t', 1], ['t', 2]] const expectedVals = [0, 1] const expectedKeysVals = [{ v: 0, k: ['t', 1] }, { v: 1, k: ['t', 2] }] t.describe('idb.getAll') const valsGetAll = await idb.getAll(store) t.compare(valsGetAll, expectedVals) t.describe('idb.getAllKeys') const valsGetAllKeys = await idb.getAllKeys(store) t.compare(valsGetAllKeys, expectedKeys) t.describe('idb.getAllKeysVals') const valsGetAllKeysVals = await idb.getAllKeysValues(store) t.compare(valsGetAllKeysVals, expectedKeysVals) /** * @param {string} desc * @param {IDBKeyRange?} keyrange */ const iterateTests = async (desc, keyrange) => { t.describe(`idb.iterate (${desc})`) /** * @type {Array<{v:any,k:any}>} */ const valsIterate = [] await idb.iterate(store, keyrange, (v, k) => { valsIterate.push({ v, k }) }) t.compare(valsIterate, expectedKeysVals) t.describe(`idb.iterateKeys (${desc})`) /** * @type {Array} */ const keysIterate = [] await idb.iterateKeys(store, keyrange, key => { keysIterate.push(key) }) t.compare(keysIterate, expectedKeys) } await iterateTests('range=null', null) const range = idb.createIDBKeyRangeBound(['t', 1], ['t', 2], false, false) // adding more items that should not be touched by iteration with above range await idb.put(store, 2, ['t', 3]) await idb.put(store, 2, ['t', 0]) await iterateTests('range!=null', range) t.describe('idb.get') const getV = await idb.get(store, ['t', 1]) t.assert(getV === 0) t.describe('idb.del') await idb.del(store, ['t', 0]) const getVDel = await idb.get(store, ['t', 0]) t.assert(getVDel === undefined) t.describe('idb.add') await idb.add(store, 99, 42) const idbVAdd = await idb.get(store, 42) t.assert(idbVAdd === 99) t.describe('idb.addAutoKey') const key = await idb.addAutoKey(store, 1234) const retrieved = await idb.get(store, key) t.assert(retrieved === 1234) } /* c8 ignore next */ export const testBlocked = async () => { t.skip(!isBrowser) t.describe('ignore blocked event') await idb.deleteDB(testDBName) const db = await idb.openDB(testDBName, initTestDB) const transaction = createTransaction(db) const store = getStore(transaction) await idb.put(store, 0, ['t', 1]) await idb.put(store, 1, ['t', 2]) db.close() idb.deleteDB(testDBName) } lib0-0.2.93/isomorphic.js000066400000000000000000000003551457563604600151700ustar00rootroot00000000000000/** * Isomorphic library exports from isomorphic.js. * * @todo remove this module * @deprecated * * @module isomorphic */ // @todo remove this module // @ts-ignore export { performance, cryptoRandomBuffer } from 'isomorphic.js' lib0-0.2.93/iterator.js000066400000000000000000000023161457563604600146440ustar00rootroot00000000000000/** * Utility module to create and manipulate Iterators. * * @module iterator */ /** * @template T,R * @param {Iterator} iterator * @param {function(T):R} f * @return {IterableIterator} */ export const mapIterator = (iterator, f) => ({ [Symbol.iterator] () { return this }, // @ts-ignore next () { const r = iterator.next() return { value: r.done ? undefined : f(r.value), done: r.done } } }) /** * @template T * @param {function():IteratorResult} next * @return {IterableIterator} */ export const createIterator = next => ({ /** * @return {IterableIterator} */ [Symbol.iterator] () { return this }, // @ts-ignore next }) /** * @template T * @param {Iterator} iterator * @param {function(T):boolean} filter */ export const iteratorFilter = (iterator, filter) => createIterator(() => { let res do { res = iterator.next() } while (!res.done && !filter(res.value)) return res }) /** * @template T,M * @param {Iterator} iterator * @param {function(T):M} fmap */ export const iteratorMap = (iterator, fmap) => createIterator(() => { const { done, value } = iterator.next() return { done, value: done ? undefined : fmap(value) } }) lib0-0.2.93/json.js000066400000000000000000000004501457563604600137610ustar00rootroot00000000000000/** * JSON utility functions. * * @module json */ /** * Transform JavaScript object to JSON. * * @param {any} object * @return {string} */ export const stringify = JSON.stringify /** * Parse JSON object. * * @param {string} json * @return {any} */ export const parse = JSON.parse lib0-0.2.93/list.js000066400000000000000000000066211457563604600137710ustar00rootroot00000000000000import { id } from './function.js' import * as error from './error.js' export class ListNode { constructor () { /** * @type {this|null} */ this.next = null /** * @type {this|null} */ this.prev = null } } /** * @template {ListNode} N */ export class List { constructor () { /** * @type {N | null} */ this.start = null /** * @type {N | null} */ this.end = null this.len = 0 } } /** * @note The queue implementation is experimental and unfinished. * Don't use this in production yet. * * @template {ListNode} N * * @return {List} */ export const create = () => new List() /** * @template {ListNode} N * * @param {List} queue */ export const isEmpty = queue => queue.start === null /** * Remove a single node from the queue. Only works with Queues that operate on Doubly-linked lists of nodes. * * @template {ListNode} N * * @param {List} queue * @param {N} node */ export const remove = (queue, node) => { const prev = node.prev const next = node.next if (prev) { prev.next = next } else { queue.start = next } if (next) { next.prev = prev } else { queue.end = prev } queue.len-- return node } /** * @deprecated @todo remove in next major release */ export const removeNode = remove /** * @template {ListNode} N * * @param {List} queue * @param {N| null} left * @param {N| null} right * @param {N} node */ export const insertBetween = (queue, left, right, node) => { /* c8 ignore start */ if (left != null && left.next !== right) { throw error.unexpectedCase() } /* c8 ignore stop */ if (left) { left.next = node } else { queue.start = node } if (right) { right.prev = node } else { queue.end = node } node.prev = left node.next = right queue.len++ } /** * Remove a single node from the queue. Only works with Queues that operate on Doubly-linked lists of nodes. * * @template {ListNode} N * * @param {List} queue * @param {N} node * @param {N} newNode */ export const replace = (queue, node, newNode) => { insertBetween(queue, node, node.next, newNode) remove(queue, node) } /** * @template {ListNode} N * * @param {List} queue * @param {N} n */ export const pushEnd = (queue, n) => insertBetween(queue, queue.end, null, n) /** * @template {ListNode} N * * @param {List} queue * @param {N} n */ export const pushFront = (queue, n) => insertBetween(queue, null, queue.start, n) /** * @template {ListNode} N * * @param {List} list * @return {N| null} */ export const popFront = list => list.start ? removeNode(list, list.start) : null /** * @template {ListNode} N * * @param {List} list * @return {N| null} */ export const popEnd = list => list.end ? removeNode(list, list.end) : null /** * @template {ListNode} N * @template M * * @param {List} list * @param {function(N):M} f * @return {Array} */ export const map = (list, f) => { /** * @type {Array} */ const arr = [] let n = list.start while (n) { arr.push(f(n)) n = n.next } return arr } /** * @template {ListNode} N * * @param {List} list */ export const toArray = list => map(list, id) /** * @template {ListNode} N * @template M * * @param {List} list * @param {function(N):M} f */ export const forEach = (list, f) => { let n = list.start while (n) { f(n) n = n.next } } lib0-0.2.93/list.test.js000066400000000000000000000037131457563604600147460ustar00rootroot00000000000000import * as t from './testing.js' import * as list from './list.js' class QueueItem extends list.ListNode { /** * @param {number} v */ constructor (v) { super() this.v = v } } /** * @param {t.TestCase} _tc */ export const testEnqueueDequeue = _tc => { const N = 30 /** * @type {list.List} */ const q = list.create() t.assert(list.isEmpty(q)) t.assert(list.popFront(q) === null) for (let i = 0; i < N; i++) { list.pushEnd(q, new QueueItem(i)) t.assert(!list.isEmpty(q)) } for (let i = 0; i < N; i++) { const item = /** @type {QueueItem} */ (list.popFront(q)) t.assert(item !== null && item.v === i) } t.assert(list.isEmpty(q)) t.assert(list.popFront(q) === null) for (let i = 0; i < N; i++) { list.pushEnd(q, new QueueItem(i)) t.assert(!list.isEmpty(q)) } for (let i = 0; i < N; i++) { const item = /** @type {QueueItem} */ (list.popFront(q)) t.assert(item !== null && item.v === i) } t.assert(list.isEmpty(q)) t.assert(list.popFront(q) === null) } /** * @param {t.TestCase} _tc */ export const testSelectivePop = _tc => { /** * @type {list.List} */ const l = list.create() list.pushFront(l, new QueueItem(1)) const q3 = new QueueItem(3) list.pushEnd(l, q3) const middleNode = new QueueItem(2) list.insertBetween(l, l.start, l.end, middleNode) list.replace(l, q3, new QueueItem(4)) t.compare(list.map(l, n => n.v), [1, 2, 4]) t.compare(list.toArray(l).map(n => n.v), [1, 2, 4]) { let cnt = 0 list.forEach(l, () => cnt++) t.assert(cnt === l.len) } t.assert(l.len === 3) t.assert(list.remove(l, middleNode) === middleNode) t.assert(l.len === 2) t.compare(/** @type {QueueItem} */ (list.popEnd(l)).v, 4) t.assert(l.len === 1) t.compare(/** @type {QueueItem} */ (list.popEnd(l)).v, 1) t.assert(l.len === 0) t.compare(list.popEnd(l), null) t.assert(l.start === null) t.assert(l.end === null) t.assert(l.len === 0) } lib0-0.2.93/logging.common.js000066400000000000000000000054451457563604600157360ustar00rootroot00000000000000import * as symbol from './symbol.js' import * as time from './time.js' import * as env from './environment.js' import * as func from './function.js' import * as json from './json.js' export const BOLD = symbol.create() export const UNBOLD = symbol.create() export const BLUE = symbol.create() export const GREY = symbol.create() export const GREEN = symbol.create() export const RED = symbol.create() export const PURPLE = symbol.create() export const ORANGE = symbol.create() export const UNCOLOR = symbol.create() /* c8 ignore start */ /** * @param {Array} args * @return {Array} */ export const computeNoColorLoggingArgs = args => { if (args.length === 1 && args[0]?.constructor === Function) { args = /** @type {Array} */ (/** @type {[function]} */ (args)[0]()) } const strBuilder = [] const logArgs = [] // try with formatting until we find something unsupported let i = 0 for (; i < args.length; i++) { const arg = args[i] if (arg === undefined) { strBuilder.push('undefined') } else if (arg.constructor === String || arg.constructor === Number) { strBuilder.push(arg) } else if (arg.constructor === Object) { logArgs.push(JSON.stringify(arg)) } } return logArgs } /* c8 ignore stop */ const loggingColors = [GREEN, PURPLE, ORANGE, BLUE] let nextColor = 0 let lastLoggingTime = time.getUnixTime() /* c8 ignore start */ /** * @param {function(...any):void} _print * @param {string} moduleName * @return {function(...any):void} */ export const createModuleLogger = (_print, moduleName) => { const color = loggingColors[nextColor] const debugRegexVar = env.getVariable('log') const doLogging = debugRegexVar !== null && (debugRegexVar === '*' || debugRegexVar === 'true' || new RegExp(debugRegexVar, 'gi').test(moduleName)) nextColor = (nextColor + 1) % loggingColors.length moduleName += ': ' return !doLogging ? func.nop : (...args) => { if (args.length === 1 && args[0]?.constructor === Function) { args = args[0]() } const timeNow = time.getUnixTime() const timeDiff = timeNow - lastLoggingTime lastLoggingTime = timeNow _print( color, moduleName, UNCOLOR, ...args.map((arg) => { if (arg != null && arg.constructor === Uint8Array) { arg = Array.from(arg) } const t = typeof arg switch (t) { case 'string': case 'symbol': return arg default: { return json.stringify(arg) } } }), color, ' +' + timeDiff + 'ms' ) } } /* c8 ignore stop */ lib0-0.2.93/logging.js000066400000000000000000000231411457563604600144400ustar00rootroot00000000000000/** * Isomorphic logging module with support for colors! * * @module logging */ import * as env from './environment.js' import * as set from './set.js' import * as pair from './pair.js' import * as dom from './dom.js' import * as json from './json.js' import * as map from './map.js' import * as eventloop from './eventloop.js' import * as math from './math.js' import * as common from './logging.common.js' export { BOLD, UNBOLD, BLUE, GREY, GREEN, RED, PURPLE, ORANGE, UNCOLOR } from './logging.common.js' /** * @type {Object>} */ const _browserStyleMap = { [common.BOLD]: pair.create('font-weight', 'bold'), [common.UNBOLD]: pair.create('font-weight', 'normal'), [common.BLUE]: pair.create('color', 'blue'), [common.GREEN]: pair.create('color', 'green'), [common.GREY]: pair.create('color', 'grey'), [common.RED]: pair.create('color', 'red'), [common.PURPLE]: pair.create('color', 'purple'), [common.ORANGE]: pair.create('color', 'orange'), // not well supported in chrome when debugging node with inspector - TODO: deprecate [common.UNCOLOR]: pair.create('color', 'black') } /** * @param {Array} args * @return {Array} */ /* c8 ignore start */ const computeBrowserLoggingArgs = (args) => { if (args.length === 1 && args[0]?.constructor === Function) { args = /** @type {Array} */ (/** @type {[function]} */ (args)[0]()) } const strBuilder = [] const styles = [] const currentStyle = map.create() /** * @type {Array} */ let logArgs = [] // try with formatting until we find something unsupported let i = 0 for (; i < args.length; i++) { const arg = args[i] // @ts-ignore const style = _browserStyleMap[arg] if (style !== undefined) { currentStyle.set(style.left, style.right) } else { if (arg === undefined) { break } if (arg.constructor === String || arg.constructor === Number) { const style = dom.mapToStyleString(currentStyle) if (i > 0 || style.length > 0) { strBuilder.push('%c' + arg) styles.push(style) } else { strBuilder.push(arg) } } else { break } } } if (i > 0) { // create logArgs with what we have so far logArgs = styles logArgs.unshift(strBuilder.join('')) } // append the rest for (; i < args.length; i++) { const arg = args[i] if (!(arg instanceof Symbol)) { logArgs.push(arg) } } return logArgs } /* c8 ignore stop */ /* c8 ignore start */ const computeLoggingArgs = env.supportsColor ? computeBrowserLoggingArgs : common.computeNoColorLoggingArgs /* c8 ignore stop */ /** * @param {Array} args */ export const print = (...args) => { console.log(...computeLoggingArgs(args)) /* c8 ignore next */ vconsoles.forEach((vc) => vc.print(args)) } /* c8 ignore start */ /** * @param {Array} args */ export const warn = (...args) => { console.warn(...computeLoggingArgs(args)) args.unshift(common.ORANGE) vconsoles.forEach((vc) => vc.print(args)) } /* c8 ignore stop */ /** * @param {Error} err */ /* c8 ignore start */ export const printError = (err) => { console.error(err) vconsoles.forEach((vc) => vc.printError(err)) } /* c8 ignore stop */ /** * @param {string} url image location * @param {number} height height of the image in pixel */ /* c8 ignore start */ export const printImg = (url, height) => { if (env.isBrowser) { console.log( '%c ', `font-size: ${height}px; background-size: contain; background-repeat: no-repeat; background-image: url(${url})` ) // console.log('%c ', `font-size: ${height}x; background: url(${url}) no-repeat;`) } vconsoles.forEach((vc) => vc.printImg(url, height)) } /* c8 ignore stop */ /** * @param {string} base64 * @param {number} height */ /* c8 ignore next 2 */ export const printImgBase64 = (base64, height) => printImg(`data:image/gif;base64,${base64}`, height) /** * @param {Array} args */ export const group = (...args) => { console.group(...computeLoggingArgs(args)) /* c8 ignore next */ vconsoles.forEach((vc) => vc.group(args)) } /** * @param {Array} args */ export const groupCollapsed = (...args) => { console.groupCollapsed(...computeLoggingArgs(args)) /* c8 ignore next */ vconsoles.forEach((vc) => vc.groupCollapsed(args)) } export const groupEnd = () => { console.groupEnd() /* c8 ignore next */ vconsoles.forEach((vc) => vc.groupEnd()) } /** * @param {function():Node} createNode */ /* c8 ignore next 2 */ export const printDom = (createNode) => vconsoles.forEach((vc) => vc.printDom(createNode())) /** * @param {HTMLCanvasElement} canvas * @param {number} height */ /* c8 ignore next 2 */ export const printCanvas = (canvas, height) => printImg(canvas.toDataURL(), height) export const vconsoles = set.create() /** * @param {Array} args * @return {Array} */ /* c8 ignore start */ const _computeLineSpans = (args) => { const spans = [] const currentStyle = new Map() // try with formatting until we find something unsupported let i = 0 for (; i < args.length; i++) { let arg = args[i] // @ts-ignore const style = _browserStyleMap[arg] if (style !== undefined) { currentStyle.set(style.left, style.right) } else { if (arg === undefined) { arg = 'undefined ' } if (arg.constructor === String || arg.constructor === Number) { // @ts-ignore const span = dom.element('span', [ pair.create('style', dom.mapToStyleString(currentStyle)) ], [dom.text(arg.toString())]) if (span.innerHTML === '') { span.innerHTML = ' ' } spans.push(span) } else { break } } } // append the rest for (; i < args.length; i++) { let content = args[i] if (!(content instanceof Symbol)) { if (content.constructor !== String && content.constructor !== Number) { content = ' ' + json.stringify(content) + ' ' } spans.push( dom.element('span', [], [dom.text(/** @type {string} */ (content))]) ) } } return spans } /* c8 ignore stop */ const lineStyle = 'font-family:monospace;border-bottom:1px solid #e2e2e2;padding:2px;' /* c8 ignore start */ export class VConsole { /** * @param {Element} dom */ constructor (dom) { this.dom = dom /** * @type {Element} */ this.ccontainer = this.dom this.depth = 0 vconsoles.add(this) } /** * @param {Array} args * @param {boolean} collapsed */ group (args, collapsed = false) { eventloop.enqueue(() => { const triangleDown = dom.element('span', [ pair.create('hidden', collapsed), pair.create('style', 'color:grey;font-size:120%;') ], [dom.text('▼')]) const triangleRight = dom.element('span', [ pair.create('hidden', !collapsed), pair.create('style', 'color:grey;font-size:125%;') ], [dom.text('▶')]) const content = dom.element( 'div', [pair.create( 'style', `${lineStyle};padding-left:${this.depth * 10}px` )], [triangleDown, triangleRight, dom.text(' ')].concat( _computeLineSpans(args) ) ) const nextContainer = dom.element('div', [ pair.create('hidden', collapsed) ]) const nextLine = dom.element('div', [], [content, nextContainer]) dom.append(this.ccontainer, [nextLine]) this.ccontainer = nextContainer this.depth++ // when header is clicked, collapse/uncollapse container dom.addEventListener(content, 'click', (_event) => { nextContainer.toggleAttribute('hidden') triangleDown.toggleAttribute('hidden') triangleRight.toggleAttribute('hidden') }) }) } /** * @param {Array} args */ groupCollapsed (args) { this.group(args, true) } groupEnd () { eventloop.enqueue(() => { if (this.depth > 0) { this.depth-- // @ts-ignore this.ccontainer = this.ccontainer.parentElement.parentElement } }) } /** * @param {Array} args */ print (args) { eventloop.enqueue(() => { dom.append(this.ccontainer, [ dom.element('div', [ pair.create( 'style', `${lineStyle};padding-left:${this.depth * 10}px` ) ], _computeLineSpans(args)) ]) }) } /** * @param {Error} err */ printError (err) { this.print([common.RED, common.BOLD, err.toString()]) } /** * @param {string} url * @param {number} height */ printImg (url, height) { eventloop.enqueue(() => { dom.append(this.ccontainer, [ dom.element('img', [ pair.create('src', url), pair.create('height', `${math.round(height * 1.5)}px`) ]) ]) }) } /** * @param {Node} node */ printDom (node) { eventloop.enqueue(() => { dom.append(this.ccontainer, [node]) }) } destroy () { eventloop.enqueue(() => { vconsoles.delete(this) }) } } /* c8 ignore stop */ /** * @param {Element} dom */ /* c8 ignore next */ export const createVConsole = (dom) => new VConsole(dom) /** * @param {string} moduleName * @return {function(...any):void} */ export const createModuleLogger = (moduleName) => common.createModuleLogger(print, moduleName) lib0-0.2.93/logging.node.js000066400000000000000000000074751457563604600154000ustar00rootroot00000000000000/** * Isomorphic logging module with support for colors! * * @module logging */ import * as env from './environment.js' import * as common from './logging.common.js' export { BOLD, UNBOLD, BLUE, GREY, GREEN, RED, PURPLE, ORANGE, UNCOLOR } from './logging.common.js' const _nodeStyleMap = { [common.BOLD]: '\u001b[1m', [common.UNBOLD]: '\u001b[2m', [common.BLUE]: '\x1b[34m', [common.GREEN]: '\x1b[32m', [common.GREY]: '\u001b[37m', [common.RED]: '\x1b[31m', [common.PURPLE]: '\x1b[35m', [common.ORANGE]: '\x1b[38;5;208m', [common.UNCOLOR]: '\x1b[0m' } /* c8 ignore start */ /** * @param {Array>} args * @return {Array} */ const computeNodeLoggingArgs = (args) => { if (args.length === 1 && args[0]?.constructor === Function) { args = /** @type {Array} */ (/** @type {[function]} */ (args)[0]()) } const strBuilder = [] const logArgs = [] // try with formatting until we find something unsupported let i = 0 for (; i < args.length; i++) { const arg = args[i] // @ts-ignore const style = _nodeStyleMap[arg] if (style !== undefined) { strBuilder.push(style) } else { if (arg === undefined) { break } else if (arg.constructor === String || arg.constructor === Number) { strBuilder.push(arg) } else { break } } } if (i > 0) { // create logArgs with what we have so far strBuilder.push('\x1b[0m') logArgs.push(strBuilder.join('')) } // append the rest for (; i < args.length; i++) { const arg = args[i] if (!(arg instanceof Symbol)) { logArgs.push(arg) } } return logArgs } /* c8 ignore stop */ /* c8 ignore start */ const computeLoggingArgs = env.supportsColor ? computeNodeLoggingArgs : common.computeNoColorLoggingArgs /* c8 ignore stop */ /** * @param {Array} args */ export const print = (...args) => { console.log(...computeLoggingArgs(args)) } /* c8 ignore start */ /** * @param {Array} args */ export const warn = (...args) => { console.warn(...computeLoggingArgs(args)) } /* c8 ignore stop */ /** * @param {Error} err */ /* c8 ignore start */ export const printError = (err) => { console.error(err) } /* c8 ignore stop */ /** * @param {string} _url image location * @param {number} _height height of the image in pixel */ /* c8 ignore start */ export const printImg = (_url, _height) => { // console.log('%c ', `font-size: ${height}x; background: url(${url}) no-repeat;`) } /* c8 ignore stop */ /** * @param {string} base64 * @param {number} height */ /* c8 ignore next 2 */ export const printImgBase64 = (base64, height) => printImg(`data:image/gif;base64,${base64}`, height) /** * @param {Array} args */ /* c8 ignore next 3 */ export const group = (...args) => { console.group(...computeLoggingArgs(args)) } /** * @param {Array} args */ /* c8 ignore next 3 */ export const groupCollapsed = (...args) => { console.groupCollapsed(...computeLoggingArgs(args)) } /* c8 ignore next 3 */ export const groupEnd = () => { console.groupEnd() } /** * @param {function():Node} _createNode */ /* c8 ignore next 2 */ export const printDom = (_createNode) => {} /** * @param {HTMLCanvasElement} canvas * @param {number} height */ /* c8 ignore next 2 */ export const printCanvas = (canvas, height) => printImg(canvas.toDataURL(), height) /** * @param {Element} _dom */ /* c8 ignore next */ export const createVConsole = (_dom) => {} /** * @param {string} moduleName * @return {function(...any):void} */ /* c8 ignore next */ export const createModuleLogger = (moduleName) => common.createModuleLogger(print, moduleName) lib0-0.2.93/logging.test.js000066400000000000000000000023001457563604600154100ustar00rootroot00000000000000import * as log from 'lib0/logging' export const testLogging = () => { log.print(log.BLUE, 'blue ') log.print(log.BLUE, 'blue ', log.BOLD, 'blue,bold') log.print(log.GREEN, log.RED, 'red ', 'red') log.print(log.ORANGE, 'orange') log.print(log.BOLD, 'bold ', log.UNBOLD, 'nobold') log.print(log.GREEN, 'green ', log.UNCOLOR, 'nocolor') log.print('expecting objects from now on!') log.print({ 'my-object': 'isLogged' }) log.print(log.GREEN, 'green ', { 'my-object': 'isLogged' }) log.print(log.GREEN, 'green ', { 'my-object': 'isLogged' }, 'unformatted') log.print(log.BLUE, log.BOLD, 'number', 1) log.print(log.BLUE, log.BOLD, 'number', 1, {}, 's', 2) log.print({}, 'dtrn') log.print(() => [log.GREEN, 'can lazyprint stuff ', log.RED, 'with formatting']) log.print(undefined, 'supports undefined') } export const testModuleLogger = () => { // if you want to see the messages, enable logging: LOG=* npm run test --filter logging const mlog = log.createModuleLogger('testing') mlog('can print ', log.GREEN, 'with colors') mlog(() => ['can lazyprint ', log.GREEN, 'with colors']) mlog(undefined, 'supports undefined') mlog(() => [undefined, 'supports lazyprint undefined']) } lib0-0.2.93/map.js000066400000000000000000000042431457563604600135710ustar00rootroot00000000000000/** * Utility module to work with key-value stores. * * @module map */ /** * Creates a new Map instance. * * @function * @return {Map} * * @function */ export const create = () => new Map() /** * Copy a Map object into a fresh Map object. * * @function * @template K,V * @param {Map} m * @return {Map} */ export const copy = m => { const r = create() m.forEach((v, k) => { r.set(k, v) }) return r } /** * Get map property. Create T if property is undefined and set T on map. * * ```js * const listeners = map.setIfUndefined(events, 'eventName', set.create) * listeners.add(listener) * ``` * * @function * @template {Map} MAP * @template {MAP extends Map ? function():V : unknown} CF * @param {MAP} map * @param {MAP extends Map ? K : unknown} key * @param {CF} createT * @return {ReturnType} */ export const setIfUndefined = (map, key, createT) => { let set = map.get(key) if (set === undefined) { map.set(key, set = createT()) } return set } /** * Creates an Array and populates it with the content of all key-value pairs using the `f(value, key)` function. * * @function * @template K * @template V * @template R * @param {Map} m * @param {function(V,K):R} f * @return {Array} */ export const map = (m, f) => { const res = [] for (const [key, value] of m) { res.push(f(value, key)) } return res } /** * Tests whether any key-value pairs pass the test implemented by `f(value, key)`. * * @todo should rename to some - similarly to Array.some * * @function * @template K * @template V * @param {Map} m * @param {function(V,K):boolean} f * @return {boolean} */ export const any = (m, f) => { for (const [key, value] of m) { if (f(value, key)) { return true } } return false } /** * Tests whether all key-value pairs pass the test implemented by `f(value, key)`. * * @function * @template K * @template V * @param {Map} m * @param {function(V,K):boolean} f * @return {boolean} */ export const all = (m, f) => { for (const [key, value] of m) { if (!f(value, key)) { return false } } return true } lib0-0.2.93/map.test.js000066400000000000000000000024711457563604600145500ustar00rootroot00000000000000import * as map from './map.js' import * as math from './math.js' import * as t from './testing.js' /** * @param {t.TestCase} _tc */ export const testMap = _tc => { /** * @type {Map} */ const m = map.create() m.set(1, 2) m.set(2, 3) t.assert(map.map(m, (value, key) => value * 2 + key).reduce(math.add) === 13) let numberOfWrites = 0 const createT = () => ++numberOfWrites map.setIfUndefined(m, 3, createT) map.setIfUndefined(m, 3, createT) map.setIfUndefined(m, 3, createT) t.compare(map.copy(m), m) t.assert(numberOfWrites === 1) t.assert(map.any(m, (v, k) => k === 1 && v === 2)) t.assert(map.any(m, (v, k) => k === 2 && v === 3)) t.assert(!map.any(m, () => false)) t.assert(!map.all(m, (v, k) => k === 1 && v === 2)) t.assert(map.all(m, (v) => v === 2 || v === 3 || v === numberOfWrites)) } /** * @param {t.TestCase} _tc */ export const testTypeDefinitions = _tc => { // setIfUndefined supports inheritance properly: See https://github.com/dmonad/lib0/issues/82 class A { constructor () { this.a = 4 } } class B extends A { constructor () { super() this.b = 4 } } /** * @type {Map} */ const m = map.create() /** * @type {B} */ const b = map.setIfUndefined(m, 0, () => new B()) console.log(b) } lib0-0.2.93/math.js000066400000000000000000000023411457563604600137420ustar00rootroot00000000000000/** * Common Math expressions. * * @module math */ export const floor = Math.floor export const ceil = Math.ceil export const abs = Math.abs export const imul = Math.imul export const round = Math.round export const log10 = Math.log10 export const log2 = Math.log2 export const log = Math.log export const sqrt = Math.sqrt /** * @function * @param {number} a * @param {number} b * @return {number} The sum of a and b */ export const add = (a, b) => a + b /** * @function * @param {number} a * @param {number} b * @return {number} The smaller element of a and b */ export const min = (a, b) => a < b ? a : b /** * @function * @param {number} a * @param {number} b * @return {number} The bigger element of a and b */ export const max = (a, b) => a > b ? a : b export const isNaN = Number.isNaN export const pow = Math.pow /** * Base 10 exponential function. Returns the value of 10 raised to the power of pow. * * @param {number} exp * @return {number} */ export const exp10 = exp => Math.pow(10, exp) export const sign = Math.sign /** * @param {number} n * @return {boolean} Wether n is negative. This function also differentiates between -0 and +0 */ export const isNegativeZero = n => n !== 0 ? n < 0 : 1 / n < 0 lib0-0.2.93/math.test.js000066400000000000000000000020231457563604600147150ustar00rootroot00000000000000import * as t from './testing.js' import * as math from './math.js' import * as array from './array.js' /** * @param {t.TestCase} tc */ export const testMath = tc => { t.describe('math.abs') t.assert(math.abs(-1) === 1) t.assert(math.abs(Number.MIN_SAFE_INTEGER) === Number.MAX_SAFE_INTEGER) t.assert(math.abs(Number.MAX_SAFE_INTEGER) === Number.MAX_SAFE_INTEGER) t.describe('math.add') t.assert(array.fold([1, 2, 3, 4, 5], 0, math.add) === 15) t.describe('math.ceil') t.assert(math.ceil(1.5) === 2) t.assert(math.ceil(-1.5) === -1) t.describe('math.floor') t.assert(math.floor(1.5) === 1) t.assert(math.floor(-1.5) === -2) t.describe('math.isNaN') t.assert(math.isNaN(NaN)) // @ts-ignore t.assert(!math.isNaN(null)) t.describe('math.max') t.assert([1, 3, 65, 1, 314, 25, 3475, 2, 1].reduce(math.max) === 3475) t.describe('math.min') t.assert([1, 3, 65, 1, 314, 25, 3475, 2, 1].reduce(math.min) === 1) t.describe('math.round') t.assert(math.round(0.5) === 1) t.assert(math.round(-0.5) === 0) } lib0-0.2.93/metric.js000066400000000000000000000030341457563604600142740ustar00rootroot00000000000000/** * Utility module to convert metric values. * * @module metric */ import * as math from './math.js' export const yotta = 1e24 export const zetta = 1e21 export const exa = 1e18 export const peta = 1e15 export const tera = 1e12 export const giga = 1e9 export const mega = 1e6 export const kilo = 1e3 export const hecto = 1e2 export const deca = 10 export const deci = 0.1 export const centi = 0.01 export const milli = 1e-3 export const micro = 1e-6 export const nano = 1e-9 export const pico = 1e-12 export const femto = 1e-15 export const atto = 1e-18 export const zepto = 1e-21 export const yocto = 1e-24 const prefixUp = ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] const prefixDown = ['', 'm', 'μ', 'n', 'p', 'f', 'a', 'z', 'y'] /** * Calculate the metric prefix for a number. Assumes E.g. `prefix(1000) = { n: 1, prefix: 'k' }` * * @param {number} n * @param {number} [baseMultiplier] Multiplier of the base (10^(3*baseMultiplier)). E.g. `convert(time, -3)` if time is already in milli seconds * @return {{n:number,prefix:string}} */ export const prefix = (n, baseMultiplier = 0) => { const nPow = n === 0 ? 0 : math.log10(n) let mult = 0 while (nPow < mult * 3 && baseMultiplier > -8) { baseMultiplier-- mult-- } while (nPow >= 3 + mult * 3 && baseMultiplier < 8) { baseMultiplier++ mult++ } const prefix = baseMultiplier < 0 ? prefixDown[-baseMultiplier] : prefixUp[baseMultiplier] return { n: math.round((mult > 0 ? n / math.exp10(mult * 3) : n * math.exp10(mult * -3)) * 1e12) / 1e12, prefix } } lib0-0.2.93/metric.test.js000066400000000000000000000031341457563604600152530ustar00rootroot00000000000000import * as t from './testing.js' import * as metric from './metric.js' /** * @param {t.TestCase} tc */ export const testMetricPrefix = tc => { t.compare(metric.prefix(0), { n: 0, prefix: '' }) t.compare(metric.prefix(1, -1), { n: 1, prefix: 'm' }) t.compare(metric.prefix(1.5), { n: 1.5, prefix: '' }) t.compare(metric.prefix(100.5), { n: 100.5, prefix: '' }) t.compare(metric.prefix(1000.5), { n: 1.0005, prefix: 'k' }) t.compare(metric.prefix(0.3), { n: 300, prefix: 'm' }) t.compare(metric.prefix(0.001), { n: 1, prefix: 'm' }) // up t.compare(metric.prefix(10000), { n: 10, prefix: 'k' }) t.compare(metric.prefix(1e7), { n: 10, prefix: 'M' }) t.compare(metric.prefix(1e11), { n: 100, prefix: 'G' }) t.compare(metric.prefix(1e12 + 3), { n: (1e12 + 3) / 1e12, prefix: 'T' }) t.compare(metric.prefix(1e15), { n: 1, prefix: 'P' }) t.compare(metric.prefix(1e20), { n: 100, prefix: 'E' }) t.compare(metric.prefix(1e22), { n: 10, prefix: 'Z' }) t.compare(metric.prefix(1e24), { n: 1, prefix: 'Y' }) t.compare(metric.prefix(1e28), { n: 10000, prefix: 'Y' }) // down t.compare(metric.prefix(0.01), { n: 10, prefix: 'm' }) t.compare(metric.prefix(1e-4), { n: 100, prefix: 'μ' }) t.compare(metric.prefix(1e-9), { n: 1, prefix: 'n' }) t.compare(metric.prefix(1e-12), { n: 1, prefix: 'p' }) t.compare(metric.prefix(1e-14), { n: 10, prefix: 'f' }) t.compare(metric.prefix(1e-18), { n: 1, prefix: 'a' }) t.compare(metric.prefix(1e-21), { n: 1, prefix: 'z' }) t.compare(metric.prefix(1e-22), { n: 100, prefix: 'y' }) t.compare(metric.prefix(1e-30), { n: 0.000001, prefix: 'y' }) } lib0-0.2.93/mutex.js000066400000000000000000000015311457563604600141530ustar00rootroot00000000000000/** * Mutual exclude for JavaScript. * * @module mutex */ /** * @callback mutex * @param {function():void} cb Only executed when this mutex is not in the current stack * @param {function():void} [elseCb] Executed when this mutex is in the current stack */ /** * Creates a mutual exclude function with the following property: * * ```js * const mutex = createMutex() * mutex(() => { * // This function is immediately executed * mutex(() => { * // This function is not executed, as the mutex is already active. * }) * }) * ``` * * @return {mutex} A mutual exclude function * @public */ export const createMutex = () => { let token = true return (f, g) => { if (token) { token = false try { f() } finally { token = true } } else if (g !== undefined) { g() } } } lib0-0.2.93/number.js000066400000000000000000000015641457563604600143070ustar00rootroot00000000000000/** * Utility helpers for working with numbers. * * @module number */ import * as math from './math.js' import * as binary from './binary.js' export const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER export const MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER export const LOWEST_INT32 = 1 << 31 export const HIGHEST_INT32 = binary.BITS31 export const HIGHEST_UINT32 = binary.BITS32 /* c8 ignore next */ export const isInteger = Number.isInteger || (num => typeof num === 'number' && isFinite(num) && math.floor(num) === num) export const isNaN = Number.isNaN export const parseInt = Number.parseInt /** * Count the number of "1" bits in an unsigned 32bit number. * * Super fun bitcount algorithm by Brian Kernighan. * * @param {number} n */ export const countBits = n => { n &= binary.BITS32 let count = 0 while (n) { n &= (n - 1) count++ } return count } lib0-0.2.93/number.test.js000066400000000000000000000036651457563604600152710ustar00rootroot00000000000000import * as t from './testing.js' import * as number from './number.js' import * as random from './random.js' import * as math from './math.js' /** * @param {t.TestCase} _tc */ export const testNumber = _tc => { t.describe('isNaN') t.assert(number.isNaN(NaN)) t.assert(!number.isNaN(1 / 0)) // @ts-ignore t.assert(number.isNaN('a' / 0)) t.assert(!number.isNaN(0)) t.describe('isInteger') t.assert(!number.isInteger(1 / 0)) t.assert(!number.isInteger(NaN)) t.assert(number.isInteger(0)) t.assert(number.isInteger(-1)) t.assert(number.countBits(1) === 1) t.assert(number.countBits(3) === 2) t.assert(number.countBits(128 + 3) === 3) } /** * This benchmark confirms performance of division vs shifting numbers. * * @param {t.TestCase} tc */ export const testShiftVsDivision = tc => { /** * @type {Array} */ const numbers = [] for (let i = 0; i < 10000; i++) { numbers.push(random.uint32()) } t.measureTime('comparison', () => { for (let i = 0; i < numbers.length; i++) { let n = numbers[i] while (n > 0) { const ns = n >>> 7 const nd = math.floor(n / 128) t.assert(ns === nd) n = nd } } }) t.measureTime('shift', () => { let x = 0 for (let i = 0; i < numbers.length; i++) { x = numbers[i] >>> 7 } t.info('' + x) }) t.measureTime('division', () => { for (let i = 0; i < numbers.length; i++) { math.floor(numbers[i] / 128) } }) { /** * @type {Array} */ const divided = [] /** * @type {Array} */ const shifted = [] t.measureTime('division', () => { for (let i = 0; i < numbers.length; i++) { divided.push(math.floor(numbers[i] / 128)) } }) t.measureTime('shift', () => { for (let i = 0; i < numbers.length; i++) { shifted.push(numbers[i] >>> 7) } }) t.compareArrays(shifted, divided) } } lib0-0.2.93/object.js000066400000000000000000000037571457563604600142730ustar00rootroot00000000000000/** * Utility functions for working with EcmaScript objects. * * @module object */ /** * @return {Object} obj */ export const create = () => Object.create(null) /** * Object.assign */ export const assign = Object.assign /** * @param {Object} obj */ export const keys = Object.keys /** * @template V * @param {{[k:string]:V}} obj * @param {function(V,string):any} f */ export const forEach = (obj, f) => { for (const key in obj) { f(obj[key], key) } } /** * @todo implement mapToArray & map * * @template R * @param {Object} obj * @param {function(any,string):R} f * @return {Array} */ export const map = (obj, f) => { const results = [] for (const key in obj) { results.push(f(obj[key], key)) } return results } /** * @param {Object} obj * @return {number} */ export const length = obj => keys(obj).length /** * @param {Object} obj * @param {function(any,string):boolean} f * @return {boolean} */ export const some = (obj, f) => { for (const key in obj) { if (f(obj[key], key)) { return true } } return false } /** * @param {Object|undefined} obj */ export const isEmpty = obj => { // eslint-disable-next-line for (const _k in obj) { return false } return true } /** * @param {Object} obj * @param {function(any,string):boolean} f * @return {boolean} */ export const every = (obj, f) => { for (const key in obj) { if (!f(obj[key], key)) { return false } } return true } /** * Calls `Object.prototype.hasOwnProperty`. * * @param {any} obj * @param {string|symbol} key * @return {boolean} */ export const hasProperty = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key) /** * @param {Object} a * @param {Object} b * @return {boolean} */ export const equalFlat = (a, b) => a === b || (length(a) === length(b) && every(a, (val, key) => (val !== undefined || hasProperty(b, key)) && b[key] === val)) lib0-0.2.93/object.test.js000066400000000000000000000033021457563604600152330ustar00rootroot00000000000000import * as t from './testing.js' import * as object from './object.js' import * as math from './math.js' /** * @param {t.TestCase} _tc */ export const testObject = _tc => { t.assert(object.create().constructor === undefined, 'object.create creates an empty object without constructor') t.describe('object.equalFlat') t.assert(object.equalFlat({}, {}), 'comparing equal objects') t.assert(object.equalFlat({ x: 1 }, { x: 1 }), 'comparing equal objects') t.assert(object.equalFlat({ x: 'dtrn' }, { x: 'dtrn' }), 'comparing equal objects') t.assert(!object.equalFlat({ x: {} }, { x: {} }), 'flatEqual does not dive deep') t.assert(object.equalFlat({ x: undefined }, { x: undefined }), 'flatEqual handles undefined') t.assert(!object.equalFlat({ x: undefined }, { y: {} }), 'flatEqual handles undefined') t.describe('object.every') t.assert(object.every({ a: 1, b: 3 }, (v, k) => (v % 2) === 1 && k !== 'c')) t.assert(!object.every({ a: 1, b: 3, c: 5 }, (v, k) => (v % 2) === 1 && k !== 'c')) t.describe('object.some') t.assert(object.some({ a: 1, b: 3 }, (v, k) => v === 3 && k === 'b')) t.assert(!object.some({ a: 1, b: 5 }, (v, _k) => v === 3)) t.assert(object.some({ a: 1, b: 5 }, () => true)) t.assert(!object.some({ a: 1, b: 5 }, (_v, _k) => false)) t.describe('object.forEach') let forEachSum = 0 const r = { x: 1, y: 3 } object.forEach(r, (v, _k) => { forEachSum += v }) t.assert(forEachSum === 4) t.describe('object.map') t.assert(object.map({ x: 1, z: 5 }, (v, _k) => v).reduce(math.add) === 6) t.describe('object.length') t.assert(object.length({}) === 0) t.assert(object.length({ x: 1 }) === 1) t.assert(object.isEmpty({})) t.assert(!object.isEmpty({ a: 3 })) } lib0-0.2.93/observable.js000066400000000000000000000071401457563604600151370ustar00rootroot00000000000000/** * Observable class prototype. * * @module observable */ import * as map from './map.js' import * as set from './set.js' import * as array from './array.js' /** * Handles named events. * @experimental * * This is basically a (better typed) duplicate of Observable, which will replace Observable in the * next release. * * @template {{[key in keyof EVENTS]: function(...any):void}} EVENTS */ export class ObservableV2 { constructor () { /** * Some desc. * @type {Map>} */ this._observers = map.create() } /** * @template {keyof EVENTS & string} NAME * @param {NAME} name * @param {EVENTS[NAME]} f */ on (name, f) { map.setIfUndefined(this._observers, /** @type {string} */ (name), set.create).add(f) return f } /** * @template {keyof EVENTS & string} NAME * @param {NAME} name * @param {EVENTS[NAME]} f */ once (name, f) { /** * @param {...any} args */ const _f = (...args) => { this.off(name, /** @type {any} */ (_f)) f(...args) } this.on(name, /** @type {any} */ (_f)) } /** * @template {keyof EVENTS & string} NAME * @param {NAME} name * @param {EVENTS[NAME]} f */ off (name, f) { const observers = this._observers.get(name) if (observers !== undefined) { observers.delete(f) if (observers.size === 0) { this._observers.delete(name) } } } /** * Emit a named event. All registered event listeners that listen to the * specified name will receive the event. * * @todo This should catch exceptions * * @template {keyof EVENTS & string} NAME * @param {NAME} name The event name. * @param {Parameters} args The arguments that are applied to the event listener. */ emit (name, args) { // copy all listeners to an array first to make sure that no event is emitted to listeners that are subscribed while the event handler is called. return array.from((this._observers.get(name) || map.create()).values()).forEach(f => f(...args)) } destroy () { this._observers = map.create() } } /* c8 ignore start */ /** * Handles named events. * * @deprecated * @template N */ export class Observable { constructor () { /** * Some desc. * @type {Map} */ this._observers = map.create() } /** * @param {N} name * @param {function} f */ on (name, f) { map.setIfUndefined(this._observers, name, set.create).add(f) } /** * @param {N} name * @param {function} f */ once (name, f) { /** * @param {...any} args */ const _f = (...args) => { this.off(name, _f) f(...args) } this.on(name, _f) } /** * @param {N} name * @param {function} f */ off (name, f) { const observers = this._observers.get(name) if (observers !== undefined) { observers.delete(f) if (observers.size === 0) { this._observers.delete(name) } } } /** * Emit a named event. All registered event listeners that listen to the * specified name will receive the event. * * @todo This should catch exceptions * * @param {N} name The event name. * @param {Array} args The arguments that are applied to the event listener. */ emit (name, args) { // copy all listeners to an array first to make sure that no event is emitted to listeners that are subscribed while the event handler is called. return array.from((this._observers.get(name) || map.create()).values()).forEach(f => f(...args)) } destroy () { this._observers = map.create() } } /* c8 ignore end */ lib0-0.2.93/observable.test.js000066400000000000000000000023161457563604600161150ustar00rootroot00000000000000import * as t from './testing.js' import { ObservableV2 } from './observable.js' /** * @param {t.TestCase} _tc */ export const testTypedObservable = _tc => { /** * @type {ObservableV2<{ "hey": function(number, string):any, listen: function(string):any }>} */ const o = new ObservableV2() let calls = 0 /** * Test "hey" */ /** * @param {number} n * @param {string} s */ const listener = (n, s) => { t.assert(typeof n === 'number') t.assert(typeof s === 'string') calls++ } o.on('hey', listener) o.on('hey', (arg1) => t.assert(typeof arg1 === 'number')) // o.emit('hey', ['four']) // should emit type error // o.emit('hey', [4]) // should emit type error o.emit('hey', [4, 'four']) t.assert(calls === 1) o.emit('hey', [5, 'five']) t.assert(calls === 2) o.off('hey', listener) o.emit('hey', [6, 'six']) t.assert(calls === 2) /** * Test "listen" */ o.once('listen', n => { t.assert(typeof n === 'string') calls++ }) // o.emit('listen', [4]) // should emit type error o.emit('listen', ['four']) o.emit('listen', ['five']) // shouldn't trigger t.assert(calls === 3) o.destroy() o.emit('hey', [7, 'seven']) t.assert(calls === 3) } lib0-0.2.93/package-lock.json000066400000000000000000003737171457563604600157110ustar00rootroot00000000000000{ "name": "lib0", "version": "0.2.93", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "lib0", "version": "0.2.93", "license": "MIT", "dependencies": { "isomorphic.js": "^0.2.4" }, "bin": { "0gentesthtml": "bin/gentesthtml.js", "0serve": "bin/0serve.js" }, "devDependencies": { "@types/node": "^18.14.0", "c8": "^7.13.0", "jsdoc-api": "^8.0.0", "jsdoc-plugin-typescript": "^2.2.1", "rollup": "^2.42.1", "standard": "^17.1.0", "typescript": "^5.0.2" }, "engines": { "node": ">=16" }, "funding": { "type": "GitHub Sponsors ❤", "url": "https://github.com/sponsors/dmonad" } }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/@babel/parser": { "version": "7.21.3", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "dependencies": { "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/js": { "version": "8.46.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "engines": { "node": ">=12.22" }, "funding": { "type": "github", "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.17", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" } }, "node_modules/@jsdoc/salty": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==", "dev": true, "dependencies": { "lodash": "^4.17.21" }, "engines": { "node": ">=v12.0.0" } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" }, "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" }, "engines": { "node": ">= 8" } }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", "dev": true }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "node_modules/@types/linkify-it": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", "dev": true }, "node_modules/@types/markdown-it": { "version": "12.2.3", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", "dev": true, "dependencies": { "@types/linkify-it": "*", "@types/mdurl": "*" } }, "node_modules/@types/mdurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", "dev": true }, "node_modules/@types/node": { "version": "18.15.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz", "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==", "dev": true }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true, "bin": { "acorn": "bin/acorn" }, "engines": { "node": ">=0.4.0" } }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "node_modules/array-back": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", "dev": true, "engines": { "node": ">=12.17" } }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "is-array-buffer": "^3.0.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/array-includes": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "es-abstract": "^1.20.4", "get-intrinsic": "^1.1.3", "is-string": "^1.0.7" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/array.prototype.findlastindex": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz", "integrity": "sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "es-abstract": "^1.20.4", "es-shim-unscopables": "^1.0.0", "get-intrinsic": "^1.1.3" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/array.prototype.flat": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "es-abstract": "^1.20.4", "es-shim-unscopables": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/array.prototype.flatmap": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "es-abstract": "^1.20.4", "es-shim-unscopables": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/array.prototype.tosorted": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "es-abstract": "^1.20.4", "es-shim-unscopables": "^1.0.0", "get-intrinsic": "^1.1.3" } }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "dev": true, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/builtins": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", "dev": true, "dependencies": { "semver": "^7.0.0" } }, "node_modules/builtins/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/c8": { "version": "7.13.0", "resolved": "https://registry.npmjs.org/c8/-/c8-7.13.0.tgz", "integrity": "sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@istanbuljs/schema": "^0.1.3", "find-up": "^5.0.0", "foreground-child": "^2.0.0", "istanbul-lib-coverage": "^3.2.0", "istanbul-lib-report": "^3.0.0", "istanbul-reports": "^3.1.4", "rimraf": "^3.0.2", "test-exclude": "^6.0.0", "v8-to-istanbul": "^9.0.0", "yargs": "^16.2.0", "yargs-parser": "^20.2.9" }, "bin": { "c8": "bin/c8.js" }, "engines": { "node": ">=10.12.0" } }, "node_modules/cache-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-2.0.0.tgz", "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", "dev": true, "dependencies": { "array-back": "^4.0.1", "fs-then-native": "^2.0.0", "mkdirp2": "^1.0.4" }, "engines": { "node": ">=8" } }, "node_modules/cache-point/node_modules/array-back": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "dev": true, "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/catharsis": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", "dev": true, "dependencies": { "lodash": "^4.17.15" }, "engines": { "node": ">= 10" } }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "node_modules/collect-all": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", "integrity": "sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==", "dev": true, "dependencies": { "stream-connect": "^1.0.2", "stream-via": "^1.0.4" }, "engines": { "node": ">=0.10.0" } }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, "node_modules/define-properties": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "dev": true, "dependencies": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "dependencies": { "esutils": "^2.0.2" }, "engines": { "node": ">=6.0.0" } }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "node_modules/entities": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", "dev": true, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } }, "node_modules/es-abstract": { "version": "1.21.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.5", "get-intrinsic": "^1.2.0", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "internal-slot": "^1.0.5", "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", "object-inspect": "^1.12.3", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.4.3", "safe-regex-test": "^1.0.0", "string.prototype.trim": "^1.2.7", "string.prototype.trimend": "^1.0.6", "string.prototype.trimstart": "^1.0.6", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", "which-typed-array": "^1.1.9" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/es-set-tostringtag": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", "dev": true, "dependencies": { "get-intrinsic": "^1.1.3", "has": "^1.0.3", "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-shim-unscopables": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", "dev": true, "dependencies": { "has": "^1.0.3" } }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", "is-symbol": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/eslint": { "version": "8.46.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.1", "@eslint/js": "^8.46.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.2", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-config-standard": { "version": "17.1.0", "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ], "engines": { "node": ">=12.0.0" }, "peerDependencies": { "eslint": "^8.0.1", "eslint-plugin-import": "^2.25.2", "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", "eslint-plugin-promise": "^6.0.0" } }, "node_modules/eslint-config-standard-jsx": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-11.0.0.tgz", "integrity": "sha512-+1EV/R0JxEK1L0NGolAr8Iktm3Rgotx3BKwgaX+eAuSX8D952LULKtjgZD3F+e6SvibONnhLwoTi9DPxN5LvvQ==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ], "peerDependencies": { "eslint": "^8.8.0", "eslint-plugin-react": "^7.28.0" } }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "node_modules/eslint-import-resolver-node/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-module-utils": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", "dev": true, "dependencies": { "debug": "^3.2.7" }, "engines": { "node": ">=4" }, "peerDependenciesMeta": { "eslint": { "optional": true } } }, "node_modules/eslint-module-utils/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-plugin-es": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", "dev": true, "dependencies": { "eslint-utils": "^2.0.0", "regexpp": "^3.0.0" }, "engines": { "node": ">=8.10.0" }, "funding": { "url": "https://github.com/sponsors/mysticatea" }, "peerDependencies": { "eslint": ">=4.19.1" } }, "node_modules/eslint-plugin-es/node_modules/eslint-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, "dependencies": { "eslint-visitor-keys": "^1.1.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/mysticatea" } }, "node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/eslint-plugin-import": { "version": "2.28.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz", "integrity": "sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==", "dev": true, "dependencies": { "array-includes": "^3.1.6", "array.prototype.findlastindex": "^1.2.2", "array.prototype.flat": "^1.3.1", "array.prototype.flatmap": "^1.3.1", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.7", "eslint-module-utils": "^2.8.0", "has": "^1.0.3", "is-core-module": "^2.12.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.6", "object.groupby": "^1.0.0", "object.values": "^1.1.6", "resolve": "^1.22.3", "semver": "^6.3.1", "tsconfig-paths": "^3.14.2" }, "engines": { "node": ">=4" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-plugin-import/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "dependencies": { "esutils": "^2.0.2" }, "engines": { "node": ">=0.10.0" } }, "node_modules/eslint-plugin-n": { "version": "15.7.0", "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz", "integrity": "sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==", "dev": true, "dependencies": { "builtins": "^5.0.1", "eslint-plugin-es": "^4.1.0", "eslint-utils": "^3.0.0", "ignore": "^5.1.1", "is-core-module": "^2.11.0", "minimatch": "^3.1.2", "resolve": "^1.22.1", "semver": "^7.3.8" }, "engines": { "node": ">=12.22.0" }, "funding": { "url": "https://github.com/sponsors/mysticatea" }, "peerDependencies": { "eslint": ">=7.0.0" } }, "node_modules/eslint-plugin-n/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/eslint-plugin-promise": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/eslint-plugin-react": { "version": "7.33.1", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.1.tgz", "integrity": "sha512-L093k0WAMvr6VhNwReB8VgOq5s2LesZmrpPdKz/kZElQDzqS7G7+DnKoqT+w4JwuiGeAhAvHO0fvy0Eyk4ejDA==", "dev": true, "dependencies": { "array-includes": "^3.1.6", "array.prototype.flatmap": "^1.3.1", "array.prototype.tosorted": "^1.1.1", "doctrine": "^2.1.0", "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.6", "object.fromentries": "^2.0.6", "object.hasown": "^1.1.2", "object.values": "^1.1.6", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.4", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.8" }, "engines": { "node": ">=4" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "dependencies": { "esutils": "^2.0.2" }, "engines": { "node": ">=0.10.0" } }, "node_modules/eslint-plugin-react/node_modules/resolve": { "version": "2.0.0-next.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", "dev": true, "dependencies": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-utils": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "dependencies": { "eslint-visitor-keys": "^2.0.0" }, "engines": { "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" }, "funding": { "url": "https://github.com/sponsors/mysticatea" }, "peerDependencies": { "eslint": ">=5" } }, "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true, "engines": { "node": ">=10" } }, "node_modules/eslint-visitor-keys": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" }, "engines": { "node": ">=0.10" } }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "dependencies": { "estraverse": "^5.2.0" }, "engines": { "node": ">=4.0" } }, "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" } }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { "reusify": "^1.0.4" } }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "dependencies": { "flat-cache": "^3.0.4" }, "engines": { "node": "^10.12.0 || >=12.0.0" } }, "node_modules/file-set": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/file-set/-/file-set-4.0.2.tgz", "integrity": "sha512-fuxEgzk4L8waGXaAkd8cMr73Pm0FxOVkn8hztzUW7BAHhOGH90viQNXbiOsnecCWmfInqU6YmAMwxRMdKETceQ==", "dev": true, "dependencies": { "array-back": "^5.0.0", "glob": "^7.1.6" }, "engines": { "node": ">=10" } }, "node_modules/file-set/node_modules/array-back": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", "dev": true, "engines": { "node": ">=10" } }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "dependencies": { "flatted": "^3.1.0", "rimraf": "^3.0.2" }, "engines": { "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, "dependencies": { "is-callable": "^1.1.3" } }, "node_modules/foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^3.0.2" }, "engines": { "node": ">=8.0.0" } }, "node_modules/fs-then-native": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", "dev": true, "engines": { "node": ">=4.0.0" } }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, "optional": true, "os": [ "darwin" ], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "node_modules/function.prototype.name": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", "es-abstract": "^1.19.0", "functions-have-names": "^1.2.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", "has-proto": "^1.0.1", "has-symbols": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, "engines": { "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { "is-glob": "^4.0.3" }, "engines": { "node": ">=10.13.0" } }, "node_modules/globals": { "version": "13.20.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globalthis": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, "dependencies": { "define-properties": "^1.1.3" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, "dependencies": { "get-intrinsic": "^1.1.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "dependencies": { "function-bind": "^1.1.1" }, "engines": { "node": ">= 0.4.0" } }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/has-property-descriptors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", "dev": true, "dependencies": { "get-intrinsic": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", "dev": true, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-tostringtag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" } }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" } }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "node_modules/internal-slot": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dev": true, "dependencies": { "get-intrinsic": "^1.2.0", "has": "^1.0.3", "side-channel": "^1.0.4" }, "engines": { "node": ">= 0.4" } }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.0", "is-typed-array": "^1.1.10" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, "dependencies": { "has-bigints": "^1.0.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-core-module": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dev": true, "dependencies": { "has": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-date-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-number-object": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-shared-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, "dependencies": { "call-bind": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-symbol": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-typed-array": { "version": "1.1.10", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/isomorphic.js": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", "funding": { "type": "GitHub Sponsors ❤", "url": "https://github.com/sponsors/dmonad" } }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^3.0.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=8" } }, "node_modules/istanbul-reports": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "node_modules/js2xmlparser": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", "dev": true, "dependencies": { "xmlcreate": "^2.0.4" } }, "node_modules/jsdoc": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", "dev": true, "dependencies": { "@babel/parser": "^7.20.15", "@jsdoc/salty": "^0.2.1", "@types/markdown-it": "^12.2.3", "bluebird": "^3.7.2", "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.2", "klaw": "^3.0.0", "markdown-it": "^12.3.2", "markdown-it-anchor": "^8.4.1", "marked": "^4.0.10", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", "underscore": "~1.13.2" }, "bin": { "jsdoc": "jsdoc.js" }, "engines": { "node": ">=12.0.0" } }, "node_modules/jsdoc-api": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-8.0.0.tgz", "integrity": "sha512-Rnhor0suB1Ds1abjmFkFfKeD+kSMRN9oHMTMZoJVUrmtCGDwXty+sWMA9sa4xbe4UyxuPjhC7tavZ40mDKK6QQ==", "dev": true, "dependencies": { "array-back": "^6.2.2", "cache-point": "^2.0.0", "collect-all": "^1.0.4", "file-set": "^4.0.2", "fs-then-native": "^2.0.0", "jsdoc": "^4.0.0", "object-to-spawn-args": "^2.0.1", "temp-path": "^1.0.0", "walk-back": "^5.1.0" }, "engines": { "node": ">=12.17" } }, "node_modules/jsdoc-plugin-typescript": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/jsdoc-plugin-typescript/-/jsdoc-plugin-typescript-2.2.1.tgz", "integrity": "sha512-xxuiqJ1O5+KIoOd8G8+iIZ89ns/ZvuzerrhCQhLPmeSIVsv5Ra42D9/YOHPi2DndUJbbkEpJCB95i7EXIOmhnA==", "dev": true, "dependencies": { "string.prototype.matchall": "^4.0.0" } }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "node_modules/json5": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" }, "engines": { "node": ">=4.0" } }, "node_modules/klaw": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", "dev": true, "dependencies": { "graceful-fs": "^4.1.9" } }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/linkify-it": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", "dev": true, "dependencies": { "uc.micro": "^1.0.1" } }, "node_modules/load-json-file": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", "dev": true, "dependencies": { "graceful-fs": "^4.1.15", "parse-json": "^4.0.0", "pify": "^4.0.1", "strip-bom": "^3.0.0", "type-fest": "^0.3.0" }, "engines": { "node": ">=6" } }, "node_modules/load-json-file/node_modules/type-fest": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { "p-locate": "^5.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "dependencies": { "semver": "^6.0.0" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/markdown-it": { "version": "12.3.2", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dev": true, "dependencies": { "argparse": "^2.0.1", "entities": "~2.1.0", "linkify-it": "^3.0.1", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" }, "bin": { "markdown-it": "bin/markdown-it.js" } }, "node_modules/markdown-it-anchor": { "version": "8.6.7", "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", "dev": true, "peerDependencies": { "@types/markdown-it": "*", "markdown-it": "*" } }, "node_modules/marked": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", "dev": true, "bin": { "marked": "bin/marked.js" }, "engines": { "node": ">= 12" } }, "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, "bin": { "mkdirp": "bin/cmd.js" }, "engines": { "node": ">=10" } }, "node_modules/mkdirp2": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.5.tgz", "integrity": "sha512-xOE9xbICroUDmG1ye2h4bZ8WBie9EGmACaco8K8cx6RlkJJrxGIqjGqztAI+NMhexXBcdGbSEzI6N3EJPevxZw==", "dev": true }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "engines": { "node": ">= 0.4" } }, "node_modules/object-to-spawn-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-2.0.1.tgz", "integrity": "sha512-6FuKFQ39cOID+BMZ3QaphcC8Y4cw6LXBLyIgPU+OhIYwviJamPAn+4mITapnSBQrejB+NNp+FMskhD8Cq+Ys3w==", "dev": true, "engines": { "node": ">=8.0.0" } }, "node_modules/object.assign": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.entries": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "es-abstract": "^1.20.4" }, "engines": { "node": ">= 0.4" } }, "node_modules/object.fromentries": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "es-abstract": "^1.20.4" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.groupby": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.0.tgz", "integrity": "sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", "es-abstract": "^1.21.2", "get-intrinsic": "^1.2.1" } }, "node_modules/object.hasown": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", "dev": true, "dependencies": { "define-properties": "^1.1.4", "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.values": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "es-abstract": "^1.20.4" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" } }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { "p-limit": "^3.0.2" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "dependencies": { "callsites": "^3.0.0" }, "engines": { "node": ">=6" } }, "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" }, "engines": { "node": ">=4" } }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "node_modules/pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/pkg-conf": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", "dev": true, "dependencies": { "find-up": "^3.0.0", "load-json-file": "^5.2.0" }, "engines": { "node": ">=6" } }, "node_modules/pkg-conf/node_modules/find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "dependencies": { "locate-path": "^3.0.0" }, "engines": { "node": ">=6" } }, "node_modules/pkg-conf/node_modules/locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" }, "engines": { "node": ">=6" } }, "node_modules/pkg-conf/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { "p-try": "^2.0.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/pkg-conf/node_modules/p-locate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "dependencies": { "p-limit": "^2.0.0" }, "engines": { "node": ">=6" } }, "node_modules/pkg-conf/node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "engines": { "node": ">= 0.8.0" } }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ] }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", "functions-have-names": "^1.2.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/mysticatea" } }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/requizzle": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", "dev": true, "dependencies": { "lodash": "^4.17.21" } }, "node_modules/resolve": { "version": "1.22.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rollup": { "version": "2.79.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "dev": true, "bin": { "rollup": "dist/bin/rollup" }, "engines": { "node": ">=10.0.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ], "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/safe-regex-test": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.3", "is-regex": "^1.1.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dev": true, "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", "object-inspect": "^1.9.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "node_modules/standard": { "version": "17.1.0", "resolved": "https://registry.npmjs.org/standard/-/standard-17.1.0.tgz", "integrity": "sha512-jaDqlNSzLtWYW4lvQmU0EnxWMUGQiwHasZl5ZEIwx3S/ijZDjZOzs1y1QqKwKs5vqnFpGtizo4NOYX2s0Voq/g==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ], "dependencies": { "eslint": "^8.41.0", "eslint-config-standard": "17.1.0", "eslint-config-standard-jsx": "^11.0.0", "eslint-plugin-import": "^2.27.5", "eslint-plugin-n": "^15.7.0", "eslint-plugin-promise": "^6.1.1", "eslint-plugin-react": "^7.32.2", "standard-engine": "^15.0.0", "version-guard": "^1.1.1" }, "bin": { "standard": "bin/cmd.cjs" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/standard-engine": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-15.1.0.tgz", "integrity": "sha512-VHysfoyxFu/ukT+9v49d4BRXIokFRZuH3z1VRxzFArZdjSCFpro6rEIU3ji7e4AoAtuSfKBkiOmsrDqKW5ZSRw==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ], "dependencies": { "get-stdin": "^8.0.0", "minimist": "^1.2.6", "pkg-conf": "^3.1.0", "xdg-basedir": "^4.0.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/stream-connect": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", "dev": true, "dependencies": { "array-back": "^1.0.2" }, "engines": { "node": ">=0.10.0" } }, "node_modules/stream-connect/node_modules/array-back": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "dependencies": { "typical": "^2.6.0" }, "engines": { "node": ">=0.12.0" } }, "node_modules/stream-via": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/stream-via/-/stream-via-1.0.4.tgz", "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/string.prototype.matchall": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "es-abstract": "^1.20.4", "get-intrinsic": "^1.1.3", "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", "regexp.prototype.flags": "^1.4.3", "side-channel": "^1.0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trim": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "es-abstract": "^1.20.4" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimend": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/temp-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", "dev": true }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" }, "engines": { "node": ">=8" } }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "dependencies": { "prelude-ls": "^1.2.1" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typed-array-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "for-each": "^0.3.3", "is-typed-array": "^1.1.9" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/typescript": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { "node": ">=12.20" } }, "node_modules/typical": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", "dev": true }, "node_modules/uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/underscore": { "version": "1.13.6", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "dependencies": { "punycode": "^2.1.0" } }, "node_modules/v8-to-istanbul": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", "convert-source-map": "^1.6.0" }, "engines": { "node": ">=10.12.0" } }, "node_modules/version-guard": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/version-guard/-/version-guard-1.1.1.tgz", "integrity": "sha512-MGQLX89UxmYHgDvcXyjBI0cbmoW+t/dANDppNPrno64rYr8nH4SHSuElQuSYdXGEs0mUzdQe1BY+FhVPNsAmJQ==", "dev": true, "engines": { "node": ">=0.10.48" } }, "node_modules/walk-back": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.0.tgz", "integrity": "sha512-Uhxps5yZcVNbLEAnb+xaEEMdgTXl9qAQDzKYejG2AZ7qPwRQ81lozY9ECDbjLPNWm7YsO1IK5rsP1KoQzXAcGA==", "dev": true, "engines": { "node": ">=12.17" } }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" }, "engines": { "node": ">= 8" } }, "node_modules/which-boxed-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", "is-number-object": "^1.0.4", "is-string": "^1.0.5", "is-symbol": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-typed-array": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.0", "is-typed-array": "^1.1.10" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "node_modules/xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/xmlcreate": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", "dev": true }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "engines": { "node": ">=10" } }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" }, "engines": { "node": ">=10" } }, "node_modules/yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "engines": { "node": ">=10" } }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } } } } lib0-0.2.93/package.json000066400000000000000000000373511457563604600147520ustar00rootroot00000000000000{ "name": "lib0", "version": "0.2.93", "description": "", "sideEffects": false, "type": "module", "main": "./dist/index.cjs", "module": "./index.js", "types": "./index.d.ts", "funding": { "type": "GitHub Sponsors ❤", "url": "https://github.com/sponsors/dmonad" }, "bin": { "0gentesthtml": "./bin/gentesthtml.js", "0serve": "./bin/0serve.js", "0ecdsa-generate-keypair": "./bin/0ecdsa-generate-keypair.js" }, "exports": { "./package.json": "./package.json", ".": { "types": "./index.d.ts", "module": "./index.js", "import": "./index.js", "require": "./dist/index.cjs" }, "./array.js": "./array.js", "./dist/array.cjs": "./dist/array.cjs", "./array": { "types": "./array.d.ts", "module": "./array.js", "import": "./array.js", "require": "./dist/array.cjs" }, "./binary.js": "./binary.js", "./dist/binary.cjs": "./dist/binary.cjs", "./binary": { "types": "./binary.d.ts", "module": "./binary.js", "import": "./binary.js", "require": "./dist/binary.cjs" }, "./broadcastchannel.js": "./broadcastchannel.js", "./dist/broadcastchannel.cjs": "./dist/broadcastchannel.cjs", "./broadcastchannel": { "types": "./broadcastchannel.d.ts", "module": "./broadcastchannel.js", "import": "./broadcastchannel.js", "require": "./dist/broadcastchannel.cjs" }, "./buffer.js": "./buffer.js", "./dist/buffer.cjs": "./dist/buffer.cjs", "./buffer": { "types": "./buffer.d.ts", "module": "./buffer.js", "import": "./buffer.js", "require": "./dist/buffer.cjs" }, "./cache.js": "./cache.js", "./dist/cache.cjs": "./dist/cache.cjs", "./cache": { "types": "./cache.d.ts", "module": "./cache.js", "import": "./cache.js", "require": "./dist/cache.cjs" }, "./component.js": "./component.js", "./dist/component.cjs": "./dist/component.cjs", "./component": { "types": "./component.d.ts", "module": "./component.js", "import": "./component.js", "require": "./dist/component.cjs" }, "./conditions.js": "./conditions.js", "./dist/conditions.cjs": "./dist/conditions.cjs", "./conditions": { "types": "./condititons.d.ts", "module": "./condititons.js", "import": "./condititons.js", "require": "./dist/conditions.cjs" }, "./crypto/jwt": { "types": "./crypto/jwt.d.ts", "module": "./crypto/jwt.js", "import": "./crypto/jwt.js", "require": "./dist/jwt.cjs" }, "./crypto/aes-gcm": { "types": "./crypto/aes-gcm.d.ts", "module": "./crypto/aes-gcm.js", "import": "./crypto/aes-gcm.js", "require": "./dist/aes-gcm.cjs" }, "./crypto/ecdsa": { "types": "./crypto/ecdsa.d.ts", "module": "./crypto/ecdsa.js", "import": "./crypto/ecdsa.js", "require": "./dist/ecdsa.cjs" }, "./crypto/rsa-oaep": { "types": "./crypto/rsa-oaep.d.ts", "module": "./crypto/rsa-oaep.js", "import": "./crypto/rsa-oaep.js", "require": "./dist/rsa-oaep.cjs" }, "./hash/rabin": { "types": "./hash/rabin.d.ts", "module": "./hash/rabin.js", "import": "./hash/rabin.js", "require": "./dist/rabin.cjs" }, "./hash/sha256": { "types": "./hash/sha256.d.ts", "browser": { "module": "./hash/sha256.js", "require": "./dist/sha256.cjs", "default": "./hash/sha256.js" }, "node": { "require": "./dist/sha256.node.cjs", "default": "./hash/sha256.node.js" }, "default": { "module": "./hash/sha256.js", "require": "./dist/sha256.cjs", "default": "./hash/sha256.js" } }, "./decoding.js": "./decoding.js", "./dist/decoding.cjs": "./dist/decoding.cjs", "./decoding": { "types": "./decoding.d.ts", "module": "./decoding.js", "import": "./decoding.js", "require": "./dist/decoding.cjs" }, "./diff.js": "./diff.js", "./dist/diff.cjs": "./dist/diff.cjs", "./diff": { "types": "./diff.d.ts", "module": "./diff.js", "import": "./diff.js", "require": "./dist/diff.cjs" }, "./dom.js": "./dom.js", "./dist/dom.cjs": "./dist/dom.cjs", "./dom": { "types": "./dom.d.ts", "module": "./dom.js", "import": "./dom.js", "require": "./dist/dom.cjs" }, "./encoding.js": "./encoding.js", "./dist/encoding.cjs": "./dist/encoding.cjs", "./encoding": { "types": "./encoding.d.ts", "module": "./encoding.js", "import": "./encoding.js", "require": "./dist/encoding.cjs" }, "./environment.js": "./environment.js", "./dist/environment.cjs": "./dist/environment.cjs", "./environment": { "types": "./environment.d.ts", "module": "./environment.js", "import": "./environment.js", "require": "./dist/environment.cjs" }, "./error.js": "./error.js", "./dist/error.cjs": "./dist/error.cjs", "./error": { "types": "./error.d.ts", "module": "./error.js", "import": "./error.js", "require": "./dist/error.cjs" }, "./eventloop.js": "./eventloop.js", "./dist/eventloop.cjs": "./dist/eventloop.cjs", "./eventloop": { "types": "./eventloop.d.ts", "module": "./eventloop.js", "import": "./eventloop.js", "require": "./dist/eventloop.cjs" }, "./function.js": "./function.js", "./dist/function.cjs": "./dist/function.cjs", "./function": { "types": "./function.d.ts", "module": "./function.js", "import": "./function.js", "require": "./dist/function.cjs" }, "./indexeddb.js": "./indexeddb.js", "./dist/indexeddb.cjs": "./dist/indexeddb.cjs", "./indexeddb": { "types": "./indexeddb.d.ts", "module": "./indexeddb.js", "import": "./indexeddb.js", "require": "./dist/indexeddb.cjs" }, "./isomorphic.js": "./isomorphic.js", "./dist/isomorphic.cjs": "./dist/isomorphic.cjs", "./isomorphic": { "types": "./isomorphic.d.ts", "module": "./isomorphic.js", "import": "./isomorphic.js", "require": "./dist/isomorphic.cjs" }, "./iterator.js": "./iterator.js", "./dist/iterator.cjs": "./dist/iterator.cjs", "./iterator": { "types": "./iterator.d.ts", "module": "./iterator.js", "import": "./iterator.js", "require": "./dist/iterator.cjs" }, "./json.js": "./json.js", "./dist/json.cjs": "./dist/json.cjs", "./json": { "types": "./json.d.ts", "module": "./json.js", "import": "./json.js", "require": "./dist/json.cjs" }, "./list.js": "./list.js", "./dist/list.cjs": "./dist/list.cjs", "./list": { "types": "./list.d.ts", "module": "./list.js", "import": "./list.js", "require": "./dist/list.cjs" }, "./logging.js": "./logging.js", "./dist/logging.cjs": "./dist/logging.node.cjs", "./logging": { "types": "./logging.node.d.ts", "deno": "./logging.node.js", "bun": "./logging.js", "browser": { "module": "./logging.js", "require": "./dist/logging.cjs", "default": "./logging.js" }, "node": { "module": "./logging.node.js", "require": "./dist/logging.node.cjs", "default": "./logging.node.js" }, "default": { "module": "./logging.js", "require": "./dist/logging.cjs", "default": "./logging.js" } }, "./map.js": "./map.js", "./dist/map.cjs": "./dist/map.cjs", "./map": { "types": "./map.d.ts", "module": "./map.js", "import": "./map.js", "require": "./dist/map.cjs" }, "./math.js": "./math.js", "./dist/math.cjs": "./dist/math.cjs", "./math": { "types": "./math.d.ts", "module": "./math.js", "import": "./math.js", "require": "./dist/math.cjs" }, "./metric.js": "./metric.js", "./dist/metric.cjs": "./dist/metric.cjs", "./metric": { "types": "./metric.d.ts", "module": "./metric.js", "import": "./metric.js", "require": "./dist/metric.cjs" }, "./mutex.js": "./mutex.js", "./dist/mutex.cjs": "./dist/mutex.cjs", "./mutex": { "types": "./mutex.d.ts", "module": "./mutex.js", "import": "./mutex.js", "require": "./dist/mutex.cjs" }, "./number.js": "./number.js", "./dist/number.cjs": "./dist/number.cjs", "./number": { "types": "./number.d.ts", "module": "./number.js", "import": "./number.js", "require": "./dist/number.cjs" }, "./object.js": "./object.js", "./dist/object.cjs": "./dist/object.cjs", "./object": { "types": "./object.d.ts", "module": "./object.js", "import": "./object.js", "require": "./dist/object.cjs" }, "./observable.js": "./observable.js", "./dist/observable.cjs": "./dist/observable.cjs", "./observable": { "types": "./observable.d.ts", "module": "./observable.js", "import": "./observable.js", "require": "./dist/observable.cjs" }, "./pair.js": "./pair.js", "./dist/pair.cjs": "./dist/pair.cjs", "./pair": { "types": "./pair.d.ts", "module": "./pair.js", "import": "./pair.js", "require": "./dist/pair.cjs" }, "./prng.js": "./prng.js", "./dist/prng.cjs": "./dist/prng.cjs", "./prng": { "types": "./prng.d.ts", "module": "./prng.js", "import": "./prng.js", "require": "./dist/prng.cjs" }, "./promise.js": "./promise.js", "./dist/promise.cjs": "./dist/promise.cjs", "./promise": { "types": "./promise.d.ts", "module": "./promise.js", "import": "./promise.js", "require": "./dist/promise.cjs" }, "./queue.js": "./queue.js", "./dist/queue.cjs": "./dist/queue.cjs", "./queue": { "types": "./queue.d.ts", "module": "./queue.js", "import": "./queue.js", "require": "./dist/queue.cjs" }, "./random.js": "./random.js", "./dist/random.cjs": "./dist/random.cjs", "./random": { "types": "./random.d.ts", "module": "./random.js", "import": "./random.js", "require": "./dist/random.cjs" }, "./set.js": "./set.js", "./dist/set.cjs": "./dist/set.cjs", "./set": { "types": "./set.d.ts", "module": "./set.js", "import": "./set.js", "require": "./dist/set.cjs" }, "./sort.js": "./sort.js", "./dist/sort.cjs": "./dist/sort.cjs", "./sort": { "types": "./sort.d.ts", "module": "./sort.js", "import": "./sort.js", "require": "./dist/sort.cjs" }, "./statistics.js": "./statistics.js", "./dist/statistics.cjs": "./dist/statistics.cjs", "./statistics": { "types": "./statistics.d.ts", "module": "./statistics.js", "import": "./statistics.js", "require": "./dist/statistics.cjs" }, "./storage.js": "./storage.js", "./dist/storage.cjs": "./dist/storage.cjs", "./storage": { "types": "./storage.d.ts", "module": "./storage.js", "import": "./storage.js", "require": "./dist/storage.cjs" }, "./string.js": "./string.js", "./dist/string.cjs": "./dist/string.cjs", "./string": { "types": "./string.d.ts", "module": "./string.js", "import": "./string.js", "require": "./dist/string.cjs" }, "./symbol.js": "./symbol.js", "./dist/symbol.cjs": "./dist/symbol.cjs", "./symbol": { "types": "./symbol.d.ts", "module": "./symbol.js", "import": "./symbol.js", "require": "./dist/symbol.cjs" }, "./testing.js": "./testing.js", "./dist/testing.cjs": "./dist/testing.cjs", "./testing": { "types": "./testing.d.ts", "module": "./testing.js", "import": "./testing.js", "require": "./dist/testing.cjs" }, "./time.js": "./time.js", "./dist/time.cjs": "./dist/time.cjs", "./time": { "types": "./time.d.ts", "module": "./time.js", "import": "./time.js", "require": "./dist/time.cjs" }, "./tree.js": "./tree.js", "./dist/tree.cjs": "./dist/tree.cjs", "./tree": { "types": "./tree.d.ts", "module": "./tree.js", "import": "./tree.js", "require": "./dist/tree.cjs" }, "./url.js": "./url.js", "./dist/url.cjs": "./dist/url.cjs", "./url": { "types": "./url.d.ts", "module": "./url.js", "import": "./url.js", "require": "./dist/url.cjs" }, "./websocket.js": "./websocket.js", "./dist/websocket.cjs": "./dist/websocket.cjs", "./websocket": { "types": "./websocket.d.ts", "module": "./websocket.js", "import": "./websocket.js", "require": "./dist/websocket.cjs" }, "./webcrypto": { "types": "./webcrypto.d.ts", "deno": "./webcrypto.deno.js", "bun": "./webcrypto.js", "browser": { "module": "./webcrypto.js", "require": "./dist/webcrypto.cjs", "default": "./webcrypto.js" }, "node": { "module": "./webcrypto.node.js", "require": "./dist/webcrypto.node.cjs", "default": "./webcrypto.node.js" }, "default": { "module": "./webcrypto.js", "require": "./dist/webcrypto.cjs", "default": "./webcrypto.js" } }, "./performance.js": "./performance.js", "./dist/performance.cjs": "./dist/performance.node.cjs", "./performance": { "types": "./performance.d.ts", "deno": "./performance.node.js", "bun": "./performance.node.js", "browser": { "module": "./performance.js", "require": "./dist/performance.cjs", "default": "./performance.js" }, "node": { "module": "./performance.node.js", "require": "./dist/performance.node.cjs", "default": "./performance.node.js" }, "default": { "module": "./performance.js", "require": "./dist/performance.cjs", "default": "./performance.js" } } }, "dependencies": { "isomorphic.js": "^0.2.4" }, "devDependencies": { "@types/node": "^18.14.0", "c8": "^7.13.0", "jsdoc-api": "^8.0.0", "jsdoc-plugin-typescript": "^2.2.1", "rollup": "^2.42.1", "standard": "^17.1.0", "typescript": "^5.0.2" }, "scripts": { "clean": "rm -rf dist *.d.ts */*.d.ts *.d.ts.map */*.d.ts.map", "types": "tsc --outDir .", "dist": "rollup -c", "debug": "npm run gentesthtml && node ./bin/0serve.js -o test.html", "test": "c8 --check-coverage --lines 100 --branches 100 --functions 100 --statements 100 node --unhandled-rejections=strict ./test.js --repetition-time 50 --production", "test-inspect": "node --inspect-brk --unhandled-rejections=strict ./test.js --repetition-time 50 --production", "test-extensive": "c8 --check-coverage --lines 100 --branches 100 --functions 100 --statements 100 node test.js --repetition-time 30000 --extensive --production", "trace-deopt": "clear && rollup -c && node --trace-deopt dist/test.cjs", "trace-opt": "clear && rollup -c && node --trace-opt dist/test.cjs", "lint": "standard && tsc", "gendocs": "node ./bin/gendocs.js", "preversion": "npm run clean && npm run lint && npm run test && npm run types && npm run dist && git add README.md", "postpublish": "npm run clean", "gentesthtml": "node ./bin/gentesthtml.js --script test.js > test.html" }, "repository": { "type": "git", "url": "git+https://github.com/dmonad/lib0.git" }, "author": "Kevin Jahns ", "license": "MIT", "bugs": { "url": "https://github.com/dmonad/lib0/issues" }, "homepage": "https://github.com/dmonad/lib0#readme", "standard": { "ignore": [ "/dist", "/node_modules", "/docs" ] }, "engines": { "node": ">=16" } } lib0-0.2.93/pair.js000066400000000000000000000015651457563604600137530ustar00rootroot00000000000000/** * Working with value pairs. * * @module pair */ /** * @template L,R */ export class Pair { /** * @param {L} left * @param {R} right */ constructor (left, right) { this.left = left this.right = right } } /** * @template L,R * @param {L} left * @param {R} right * @return {Pair} */ export const create = (left, right) => new Pair(left, right) /** * @template L,R * @param {R} right * @param {L} left * @return {Pair} */ export const createReversed = (right, left) => new Pair(left, right) /** * @template L,R * @param {Array>} arr * @param {function(L, R):any} f */ export const forEach = (arr, f) => arr.forEach(p => f(p.left, p.right)) /** * @template L,R,X * @param {Array>} arr * @param {function(L, R):X} f * @return {Array} */ export const map = (arr, f) => arr.map(p => f(p.left, p.right)) lib0-0.2.93/pair.test.js000066400000000000000000000011741457563604600147250ustar00rootroot00000000000000import * as t from './testing.js' import * as pair from './pair.js' import * as math from './math.js' /** * @param {t.TestCase} tc */ export const testPair = tc => { const ps = [pair.create(1, 2), pair.create(3, 4), pair.createReversed(6, 5)] t.describe('Counting elements in pair list') let countLeft = 0 let countRight = 0 pair.forEach(ps, (left, right) => { countLeft += left countRight += right }) t.assert(countLeft === 9) t.assert(countRight === 12) t.assert(countLeft === pair.map(ps, left => left).reduce(math.add)) t.assert(countRight === pair.map(ps, (left, right) => right).reduce(math.add)) } lib0-0.2.93/performance.js000066400000000000000000000003031457563604600153060ustar00rootroot00000000000000/* eslint-env browser */ export const measure = performance.measure.bind(performance) export const now = performance.now.bind(performance) export const mark = performance.mark.bind(performance) lib0-0.2.93/performance.node.js000066400000000000000000000011461457563604600162400ustar00rootroot00000000000000import { performance } from 'node:perf_hooks' import { nop } from './function.js' import * as time from './time.js' /** * @type {typeof performance.measure} */ /* c8 ignore next */ export const measure = performance.measure ? performance.measure.bind(performance) : /** @type {any} */ (nop) /** * @type {typeof performance.now} */ /* c8 ignore next */ export const now = performance.now ? performance.now.bind(performance) : time.getUnixTime /** * @type {typeof performance.mark} */ /* c8 ignore next */ export const mark = performance.mark ? performance.mark.bind(performance) : /** @type {any} */ (nop) lib0-0.2.93/prng.js000066400000000000000000000136771457563604600137750ustar00rootroot00000000000000/** * Fast Pseudo Random Number Generators. * * Given a seed a PRNG generates a sequence of numbers that cannot be reasonably predicted. * Two PRNGs must generate the same random sequence of numbers if given the same seed. * * @module prng */ import * as binary from './binary.js' import { fromCharCode, fromCodePoint } from './string.js' import * as math from './math.js' import { Xoroshiro128plus } from './prng/Xoroshiro128plus.js' import * as buffer from './buffer.js' /** * Description of the function * @callback generatorNext * @return {number} A random float in the cange of [0,1) */ /** * A random type generator. * * @typedef {Object} PRNG * @property {generatorNext} next Generate new number */ export const DefaultPRNG = Xoroshiro128plus /** * Create a Xoroshiro128plus Pseudo-Random-Number-Generator. * This is the fastest full-period generator passing BigCrush without systematic failures. * But there are more PRNGs available in ./PRNG/. * * @param {number} seed A positive 32bit integer. Do not use negative numbers. * @return {PRNG} */ export const create = seed => new DefaultPRNG(seed) /** * Generates a single random bool. * * @param {PRNG} gen A random number generator. * @return {Boolean} A random boolean */ export const bool = gen => (gen.next() >= 0.5) /** * Generates a random integer with 53 bit resolution. * * @param {PRNG} gen A random number generator. * @param {Number} min The lower bound of the allowed return values (inclusive). * @param {Number} max The upper bound of the allowed return values (inclusive). * @return {Number} A random integer on [min, max] */ export const int53 = (gen, min, max) => math.floor(gen.next() * (max + 1 - min) + min) /** * Generates a random integer with 53 bit resolution. * * @param {PRNG} gen A random number generator. * @param {Number} min The lower bound of the allowed return values (inclusive). * @param {Number} max The upper bound of the allowed return values (inclusive). * @return {Number} A random integer on [min, max] */ export const uint53 = (gen, min, max) => math.abs(int53(gen, min, max)) /** * Generates a random integer with 32 bit resolution. * * @param {PRNG} gen A random number generator. * @param {Number} min The lower bound of the allowed return values (inclusive). * @param {Number} max The upper bound of the allowed return values (inclusive). * @return {Number} A random integer on [min, max] */ export const int32 = (gen, min, max) => math.floor(gen.next() * (max + 1 - min) + min) /** * Generates a random integer with 53 bit resolution. * * @param {PRNG} gen A random number generator. * @param {Number} min The lower bound of the allowed return values (inclusive). * @param {Number} max The upper bound of the allowed return values (inclusive). * @return {Number} A random integer on [min, max] */ export const uint32 = (gen, min, max) => int32(gen, min, max) >>> 0 /** * @deprecated * Optimized version of prng.int32. It has the same precision as prng.int32, but should be preferred when * openaring on smaller ranges. * * @param {PRNG} gen A random number generator. * @param {Number} min The lower bound of the allowed return values (inclusive). * @param {Number} max The upper bound of the allowed return values (inclusive). The max inclusive number is `binary.BITS31-1` * @return {Number} A random integer on [min, max] */ export const int31 = (gen, min, max) => int32(gen, min, max) /** * Generates a random real on [0, 1) with 53 bit resolution. * * @param {PRNG} gen A random number generator. * @return {Number} A random real number on [0, 1). */ export const real53 = gen => gen.next() // (((gen.next() >>> 5) * binary.BIT26) + (gen.next() >>> 6)) / MAX_SAFE_INTEGER /** * Generates a random character from char code 32 - 126. I.e. Characters, Numbers, special characters, and Space: * * @param {PRNG} gen A random number generator. * @return {string} * * (Space)!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefghijklmnopqrstuvwxyz{|}~ */ export const char = gen => fromCharCode(int31(gen, 32, 126)) /** * @param {PRNG} gen * @return {string} A single letter (a-z) */ export const letter = gen => fromCharCode(int31(gen, 97, 122)) /** * @param {PRNG} gen * @param {number} [minLen=0] * @param {number} [maxLen=20] * @return {string} A random word (0-20 characters) without spaces consisting of letters (a-z) */ export const word = (gen, minLen = 0, maxLen = 20) => { const len = int31(gen, minLen, maxLen) let str = '' for (let i = 0; i < len; i++) { str += letter(gen) } return str } /** * TODO: this function produces invalid runes. Does not cover all of utf16!! * * @param {PRNG} gen * @return {string} */ export const utf16Rune = gen => { const codepoint = int31(gen, 0, 256) return fromCodePoint(codepoint) } /** * @param {PRNG} gen * @param {number} [maxlen = 20] */ export const utf16String = (gen, maxlen = 20) => { const len = int31(gen, 0, maxlen) let str = '' for (let i = 0; i < len; i++) { str += utf16Rune(gen) } return str } /** * Returns one element of a given array. * * @param {PRNG} gen A random number generator. * @param {Array} array Non empty Array of possible values. * @return {T} One of the values of the supplied Array. * @template T */ export const oneOf = (gen, array) => array[int31(gen, 0, array.length - 1)] /** * @param {PRNG} gen * @param {number} len * @return {Uint8Array} */ export const uint8Array = (gen, len) => { const buf = buffer.createUint8ArrayFromLen(len) for (let i = 0; i < buf.length; i++) { buf[i] = int32(gen, 0, binary.BITS8) } return buf } /* c8 ignore start */ /** * @param {PRNG} gen * @param {number} len * @return {Uint16Array} */ export const uint16Array = (gen, len) => new Uint16Array(uint8Array(gen, len * 2).buffer) /** * @param {PRNG} gen * @param {number} len * @return {Uint32Array} */ export const uint32Array = (gen, len) => new Uint32Array(uint8Array(gen, len * 4).buffer) /* c8 ignore stop */ lib0-0.2.93/prng.test.js000066400000000000000000000160141457563604600147370ustar00rootroot00000000000000import { Xoroshiro128plus } from './prng/Xoroshiro128plus.js' import * as prng from './prng.js' import { MAX_SAFE_INTEGER } from './number.js' import * as binary from './binary.js' import * as t from './testing.js' import { Xorshift32 } from './prng/Xorshift32.js' import { Mt19937 } from './prng/Mt19937.js' import * as dom from './dom.js' import { isBrowser, production } from './environment.js' import * as math from './math.js' const genTestData = 5000 /** * @param {t.TestCase} tc * @param {prng.PRNG} gen */ const runGenTest = (tc, gen) => { t.group('next - average distribution', () => { let sum = 0 for (let i = 0; i < genTestData; i++) { const next = gen.next() if (next >= 1) { t.fail('unexpected prng result') } sum += next } const avg = sum / genTestData t.assert(avg >= 0.45) t.assert(avg <= 0.55) }) t.group('bool - bool distribution is fair', () => { let head = 0 let tail = 0 let b let i for (i = 0; i < genTestData; i++) { b = prng.bool(gen) if (b) { head++ } else { tail++ } } t.info(`Generated ${head} heads and ${tail} tails.`) t.assert(tail >= math.floor(genTestData * 0.45), 'Generated enough tails.') t.assert(head >= math.floor(genTestData * 0.45), 'Generated enough heads.') }) t.group('int31 - integers average correctly', () => { let count = 0 let i for (i = 0; i < genTestData; i++) { count += prng.uint32(gen, 0, 100) } const average = count / genTestData const expectedAverage = 100 / 2 t.info(`Average is: ${average}. Expected average is ${expectedAverage}.`) t.assert(math.abs(average - expectedAverage) <= 2, 'Expected average is at most 1 off.') }) t.group('int32 - generates integer with 32 bits', () => { let largest = 0 let smallest = 0 let i let newNum for (i = 0; i < genTestData; i++) { newNum = prng.int32(gen, -binary.BITS31, binary.BITS31) if (newNum > largest) { largest = newNum } if (newNum < smallest) { smallest = newNum } } t.assert(smallest < -1000, 'Smallest number is negative') t.assert(largest > 1000, 'Largest number is positive') t.info(`Largest number generated is ${largest} (0x${largest.toString(16)})`) t.info(`Smallest number generated is ${smallest} (0x${smallest.toString(16)})`) t.assert((smallest & binary.BIT32) !== 0, 'Largest number is 32 bits long') // largest.. assuming we convert int to uint }) t.group('uint32 - generates unsigned integer with 32 bits', () => { let num = 0 let i let newNum for (i = 0; i < genTestData; i++) { newNum = prng.uint32(gen, 0, binary.BITS32) if (newNum > num) { num = newNum } } t.info(`Largest number generated is ${num} (0x${num.toString(16)})`) t.assert((num & binary.BIT32) !== 0, 'Largest number is 32 bits long.') }) t.group('int53 - generates integer exceeding 32 bits', () => { let largest = 0 let smallest = 0 let i let newNum for (i = 0; i < genTestData; i++) { newNum = prng.int53(gen, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER) if (newNum > largest) { largest = newNum } if (newNum < smallest) { smallest = newNum } } t.assert(smallest < -1000, 'Smallest number is negative') t.assert(largest > 1000, 'Largest number is positive') t.info(`Largest number generated is ${largest}`) t.info(`Smallest number generated is ${smallest}`) t.assert(largest > (binary.BITS32 >>> 0), 'Largest number exceeds BITS32') t.assert(smallest < binary.BITS32, 'Smallest Number is smaller than BITS32 (negative)') }) t.group('uint53 - generates integer exceeding 32 bits', () => { let largest = 0 let smallest = 10000 let i let newNum for (i = 0; i < genTestData; i++) { newNum = prng.uint53(gen, 0, Number.MAX_SAFE_INTEGER) if (newNum > largest) { largest = newNum } /* c8 ignore next */ if (newNum < smallest) { smallest = newNum } } t.assert(smallest >= 0, 'Smallest number is not negative') t.assert(largest > 1000, 'Largest number is positive') t.info(`Largest number generated is ${largest}`) t.info(`Smallest number generated is ${smallest}`) t.assert(largest > (binary.BITS32 >>> 0), 'Largest number exceeds BITS32') }) t.group('int31 - generates integer with 31 bits', () => { let num = 0 let i let newNum for (i = 0; i < genTestData; i++) { newNum = prng.uint32(gen, 0, binary.BITS31) if (newNum > num) { num = newNum } } t.info(`Largest number generated is ${num} (0x${num.toString(16)})`) t.assert((num & binary.BIT31) !== 0, 'Largest number is 31 bits long.') }) t.group('real - has 53 bit resolution', () => { let num = 0 let i let newNum for (i = 0; i < genTestData; i++) { newNum = prng.real53(gen) * MAX_SAFE_INTEGER if (newNum > num) { num = newNum } } t.info(`Largest number generated is ${num}.`) t.assert((MAX_SAFE_INTEGER - num) / MAX_SAFE_INTEGER < 0.01, 'Largest number is close to MAX_SAFE_INTEGER (at most 1% off).') }) t.group('char - generates all ascii characters', () => { const charSet = new Set() const chars = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefghijklmnopqrstuvwxyz{|}~"' for (let i = chars.length - 1; i >= 0; i--) { charSet.add(chars[i]) } for (let i = 0; i < genTestData; i++) { const char = prng.char(gen) charSet.delete(char) } t.info(`Charactes missing: ${charSet.size} - generating all of "${chars}"`) t.assert(charSet.size === 0, 'Generated all documented characters.') }) } /** * @param {t.TestCase} tc */ export const testGeneratorXoroshiro128plus = tc => runGenTest(tc, new Xoroshiro128plus(tc.seed)) /** * @param {t.TestCase} tc */ export const testGeneratorXorshift32 = tc => { t.skip(!production) runGenTest(tc, new Xorshift32(tc.seed)) } /** * @param {t.TestCase} tc */ export const testGeneratorMt19937 = tc => { t.skip(!production) runGenTest(tc, new Mt19937(tc.seed)) } /* c8 ignore next */ /** * @param {prng.PRNG} gen * @param {t.TestCase} tc */ const printDistribution = (gen, tc) => { const DIAMETER = genTestData / 50 const canvas = dom.canvas(DIAMETER * 3, DIAMETER) const ctx = canvas.getContext('2d') if (ctx == null) { return t.skip() } ctx.fillStyle = 'blue' for (let i = 0; i < genTestData; i++) { const x = prng.int32(gen, 0, DIAMETER * 3) const y = prng.int32(gen, 0, DIAMETER) ctx.fillRect(x, y, 1, 2) } t.printCanvas(canvas, DIAMETER) } /* c8 ignore next */ /** * @param {t.TestCase} tc */ export const testNumberDistributions = tc => { t.skip(!isBrowser) t.group('Xoroshiro128plus', () => printDistribution(new Xoroshiro128plus(tc.seed), tc)) t.group('Xorshift32', () => printDistribution(new Xorshift32(tc.seed), tc)) t.group('MT19937', () => printDistribution(new Mt19937(tc.seed), tc)) } lib0-0.2.93/prng/000077500000000000000000000000001457563604600134215ustar00rootroot00000000000000lib0-0.2.93/prng/Mt19937.js000066400000000000000000000037271457563604600147650ustar00rootroot00000000000000import * as binary from '../binary.js' import * as math from '../math.js' /** * @module prng */ const N = 624 const M = 397 /** * @param {number} u * @param {number} v */ const twist = (u, v) => ((((u & 0x80000000) | (v & 0x7fffffff)) >>> 1) ^ ((v & 1) ? 0x9908b0df : 0)) /** * @param {Uint32Array} state */ const nextState = state => { let p = 0 let j for (j = N - M + 1; --j; p++) { state[p] = state[p + M] ^ twist(state[p], state[p + 1]) } for (j = M; --j; p++) { state[p] = state[p + M - N] ^ twist(state[p], state[p + 1]) } state[p] = state[p + M - N] ^ twist(state[p], state[0]) } /** * This is a port of Shawn Cokus's implementation of the original Mersenne Twister algorithm (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/MTARCOK/mt19937ar-cok.c). * MT has a very high period of 2^19937. Though the authors of xorshift describe that a high period is not * very relevant (http://vigna.di.unimi.it/xorshift/). It is four times slower than xoroshiro128plus and * needs to recompute its state after generating 624 numbers. * * ```js * const gen = new Mt19937(new Date().getTime()) * console.log(gen.next()) * ``` * * @public */ export class Mt19937 { /** * @param {number} seed Unsigned 32 bit number */ constructor (seed) { this.seed = seed const state = new Uint32Array(N) state[0] = seed for (let i = 1; i < N; i++) { state[i] = (math.imul(1812433253, (state[i - 1] ^ (state[i - 1] >>> 30))) + i) & binary.BITS32 } this._state = state this._i = 0 nextState(this._state) } /** * Generate a random signed integer. * * @return {Number} A 32 bit signed integer. */ next () { if (this._i === N) { // need to compute a new state nextState(this._state) this._i = 0 } let y = this._state[this._i++] y ^= (y >>> 11) y ^= (y << 7) & 0x9d2c5680 y ^= (y << 15) & 0xefc60000 y ^= (y >>> 18) return (y >>> 0) / (binary.BITS32 + 1) } } lib0-0.2.93/prng/Xoroshiro128plus.js000066400000000000000000000065201457563604600171150ustar00rootroot00000000000000/** * @module prng */ import { Xorshift32 } from './Xorshift32.js' import * as binary from '../binary.js' /** * This is a variant of xoroshiro128plus - the fastest full-period generator passing BigCrush without systematic failures. * * This implementation follows the idea of the original xoroshiro128plus implementation, * but is optimized for the JavaScript runtime. I.e. * * The operations are performed on 32bit integers (the original implementation works with 64bit values). * * The initial 128bit state is computed based on a 32bit seed and Xorshift32. * * This implementation returns two 32bit values based on the 64bit value that is computed by xoroshiro128plus. * Caution: The last addition step works slightly different than in the original implementation - the add carry of the * first 32bit addition is not carried over to the last 32bit. * * [Reference implementation](http://vigna.di.unimi.it/xorshift/xoroshiro128plus.c) */ export class Xoroshiro128plus { /** * @param {number} seed Unsigned 32 bit number */ constructor (seed) { this.seed = seed // This is a variant of Xoroshiro128plus to fill the initial state const xorshift32 = new Xorshift32(seed) this.state = new Uint32Array(4) for (let i = 0; i < 4; i++) { this.state[i] = xorshift32.next() * binary.BITS32 } this._fresh = true } /** * @return {number} Float/Double in [0,1) */ next () { const state = this.state if (this._fresh) { this._fresh = false return ((state[0] + state[2]) >>> 0) / (binary.BITS32 + 1) } else { this._fresh = true const s0 = state[0] const s1 = state[1] const s2 = state[2] ^ s0 const s3 = state[3] ^ s1 // function js_rotl (x, k) { // k = k - 32 // const x1 = x[0] // const x2 = x[1] // x[0] = x2 << k | x1 >>> (32 - k) // x[1] = x1 << k | x2 >>> (32 - k) // } // rotl(s0, 55) // k = 23 = 55 - 32; j = 9 = 32 - 23 state[0] = (s1 << 23 | s0 >>> 9) ^ s2 ^ (s2 << 14 | s3 >>> 18) state[1] = (s0 << 23 | s1 >>> 9) ^ s3 ^ (s3 << 14) // rol(s1, 36) // k = 4 = 36 - 32; j = 23 = 32 - 9 state[2] = s3 << 4 | s2 >>> 28 state[3] = s2 << 4 | s3 >>> 28 return (((state[1] + state[3]) >>> 0) / (binary.BITS32 + 1)) } } } /* // Reference implementation // Source: http://vigna.di.unimi.it/xorshift/xoroshiro128plus.c // By David Blackman and Sebastiano Vigna // Who published the reference implementation under Public Domain (CC0) #include #include uint64_t s[2]; static inline uint64_t rotl(const uint64_t x, int k) { return (x << k) | (x >> (64 - k)); } uint64_t next(void) { const uint64_t s0 = s[0]; uint64_t s1 = s[1]; s1 ^= s0; s[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); // a, b s[1] = rotl(s1, 36); // c return (s[0] + s[1]) & 0xFFFFFFFF; } int main(void) { int i; s[0] = 1111 | (1337ul << 32); s[1] = 1234 | (9999ul << 32); printf("1000 outputs of genrand_int31()\n"); for (i=0; i<100; i++) { printf("%10lu ", i); printf("%10lu ", next()); printf("- %10lu ", s[0] >> 32); printf("%10lu ", (s[0] << 32) >> 32); printf("%10lu ", s[1] >> 32); printf("%10lu ", (s[1] << 32) >> 32); printf("\n"); // if (i%5==4) printf("\n"); } return 0; } */ lib0-0.2.93/prng/Xorshift32.js000066400000000000000000000011331457563604600157300ustar00rootroot00000000000000/** * @module prng */ import * as binary from '../binary.js' /** * Xorshift32 is a very simple but elegang PRNG with a period of `2^32-1`. */ export class Xorshift32 { /** * @param {number} seed Unsigned 32 bit number */ constructor (seed) { this.seed = seed /** * @type {number} */ this._state = seed } /** * Generate a random signed integer. * * @return {Number} A 32 bit signed integer. */ next () { let x = this._state x ^= x << 13 x ^= x >> 17 x ^= x << 5 this._state = x return (x >>> 0) / (binary.BITS32 + 1) } } lib0-0.2.93/promise.js000066400000000000000000000046611457563604600144760ustar00rootroot00000000000000/** * Utility helpers to work with promises. * * @module promise */ import * as time from './time.js' /** * @template T * @callback PromiseResolve * @param {T|PromiseLike} [result] */ /** * @template T * @param {function(PromiseResolve,function(Error):void):any} f * @return {Promise} */ export const create = f => /** @type {Promise} */ (new Promise(f)) /** * @param {function(function():void,function(Error):void):void} f * @return {Promise} */ export const createEmpty = f => new Promise(f) /** * `Promise.all` wait for all promises in the array to resolve and return the result * @template {unknown[] | []} PS * * @param {PS} ps * @return {Promise<{ -readonly [P in keyof PS]: Awaited }>} */ export const all = Promise.all.bind(Promise) /** * @param {Error} [reason] * @return {Promise} */ export const reject = reason => Promise.reject(reason) /** * @template T * @param {T|void} res * @return {Promise} */ export const resolve = res => Promise.resolve(res) /** * @template T * @param {T} res * @return {Promise} */ export const resolveWith = res => Promise.resolve(res) /** * @todo Next version, reorder parameters: check, [timeout, [intervalResolution]] * * @param {number} timeout * @param {function():boolean} check * @param {number} [intervalResolution] * @return {Promise} */ export const until = (timeout, check, intervalResolution = 10) => create((resolve, reject) => { const startTime = time.getUnixTime() const hasTimeout = timeout > 0 const untilInterval = () => { if (check()) { clearInterval(intervalHandle) resolve() } else if (hasTimeout) { /* c8 ignore else */ if (time.getUnixTime() - startTime > timeout) { clearInterval(intervalHandle) reject(new Error('Timeout')) } } } const intervalHandle = setInterval(untilInterval, intervalResolution) }) /** * @param {number} timeout * @return {Promise} */ export const wait = timeout => create((resolve, reject) => setTimeout(resolve, timeout)) /** * Checks if an object is a promise using ducktyping. * * Promises are often polyfilled, so it makes sense to add some additional guarantees if the user of this * library has some insane environment where global Promise objects are overwritten. * * @param {any} p * @return {boolean} */ export const isPromise = p => p instanceof Promise || (p && p.then && p.catch && p.finally) lib0-0.2.93/promise.test.js000066400000000000000000000044241457563604600154510ustar00rootroot00000000000000import * as promise from './promise.js' import * as t from './testing.js' import * as time from './time.js' import * as error from './error.js' /** * @param {Promise} p * @param {number} min * @param {number} max */ const measureP = (p, min, max) => { const start = time.getUnixTime() return p.then(() => { const duration = time.getUnixTime() - start t.assert(duration <= max, 'Expected promise to take less time') t.assert(duration >= min, 'Expected promise to take more time') }) } /** * @template T * @param {Promise} p * @return {Promise} */ const failsP = p => promise.create((resolve, reject) => p.then(() => reject(error.create('Promise should fail')), resolve)) /** * @param {t.TestCase} _tc */ export const testRepeatPromise = async _tc => { t.assert(promise.createEmpty(r => r()).constructor === Promise, 'p.create() creates a Promise') t.assert(promise.resolve().constructor === Promise, 'p.reject() creates a Promise') const rejectedP = promise.reject() t.assert(rejectedP.constructor === Promise, 'p.reject() creates a Promise') rejectedP.catch(() => {}) await promise.createEmpty(r => r()) await failsP(promise.reject()) await promise.resolve() await measureP(promise.wait(10), 7, 1000) await measureP(failsP(promise.until(15, () => false)), 15, 1000) const startTime = time.getUnixTime() await measureP(promise.until(0, () => (time.getUnixTime() - startTime) > 100), 100, 1000) await promise.all([promise.wait(5), promise.wait(10)]) } /** * @param {t.TestCase} _tc */ export const testispromise = _tc => { t.assert(promise.isPromise(new Promise(() => {}))) t.assert(promise.isPromise(promise.create(() => {}))) const rej = promise.reject() t.assert(promise.isPromise(rej)) rej.catch(() => {}) t.assert(promise.isPromise(promise.resolve())) t.assert(promise.isPromise({ then: () => {}, catch: () => {}, finally: () => {} })) t.fails(() => { t.assert(promise.isPromise({ then: () => {}, catch: () => {} })) }) } /** * @param {t.TestCase} _tc */ export const testTypings = async _tc => { const ps = await promise.all([promise.resolveWith(4), 'string']) /** * @type {number} */ const a = ps[0] /** * @type {string} */ const b = ps[1] t.assert(typeof a === 'number' && typeof b === 'string') } lib0-0.2.93/queue.js000066400000000000000000000024721457563604600141420ustar00rootroot00000000000000export class QueueNode { constructor () { /** * @type {QueueNode|null} */ this.next = null } } /** * @template V */ export class QueueValue extends QueueNode { /** * @param {V} v */ constructor (v) { super() this.v = v } } /** * @template {QueueNode} N */ export class Queue { constructor () { /** * @type {N | null} */ this.start = null /** * @type {N | null} */ this.end = null } } /** * @note The queue implementation is experimental and unfinished. * Don't use this in production yet. * * @template {QueueNode} N * @return {Queue} */ export const create = () => new Queue() /** * @param {Queue} queue */ export const isEmpty = queue => queue.start === null /** * @template {Queue} Q * @param {Q} queue * @param {Q extends Queue ? N : never} n */ export const enqueue = (queue, n) => { if (queue.end !== null) { queue.end.next = n queue.end = n } else { queue.end = n queue.start = n } } /** * @template {QueueNode} N * @param {Queue} queue * @return {N | null} */ export const dequeue = queue => { const n = queue.start if (n !== null) { // @ts-ignore queue.start = n.next if (queue.start === null) { queue.end = null } return n } return null } lib0-0.2.93/queue.test.js000066400000000000000000000016211457563604600151130ustar00rootroot00000000000000import * as t from './testing.js' import * as queue from './queue.js' /** * @param {t.TestCase} _tc */ export const testEnqueueDequeue = _tc => { const N = 30 /** * @type {queue.Queue>} */ const q = queue.create() t.assert(queue.isEmpty(q)) t.assert(queue.dequeue(q) === null) for (let i = 0; i < N; i++) { queue.enqueue(q, new queue.QueueValue(i)) t.assert(!queue.isEmpty(q)) } for (let i = 0; i < N; i++) { const item = queue.dequeue(q) t.assert(item !== null && item.v === i) } t.assert(queue.isEmpty(q)) t.assert(queue.dequeue(q) === null) for (let i = 0; i < N; i++) { queue.enqueue(q, new queue.QueueValue(i)) t.assert(!queue.isEmpty(q)) } for (let i = 0; i < N; i++) { const item = queue.dequeue(q) t.assert(item !== null && item.v === i) } t.assert(queue.isEmpty(q)) t.assert(queue.dequeue(q) === null) } lib0-0.2.93/random.js000066400000000000000000000016331457563604600142740ustar00rootroot00000000000000/** * Isomorphic module for true random numbers / buffers / uuids. * * Attention: falls back to Math.random if the browser does not support crypto. * * @module random */ import * as math from './math.js' import * as binary from './binary.js' import { getRandomValues } from 'lib0/webcrypto' export const rand = Math.random export const uint32 = () => getRandomValues(new Uint32Array(1))[0] export const uint53 = () => { const arr = getRandomValues(new Uint32Array(8)) return (arr[0] & binary.BITS21) * (binary.BITS32 + 1) + (arr[1] >>> 0) } /** * @template T * @param {Array} arr * @return {T} */ export const oneOf = arr => arr[math.floor(rand() * arr.length)] // @ts-ignore const uuidv4Template = [1e7] + -1e3 + -4e3 + -8e3 + -1e11 /** * @return {string} */ export const uuidv4 = () => uuidv4Template.replace(/[018]/g, /** @param {number} c */ c => (c ^ uint32() & 15 >> c / 4).toString(16) ) lib0-0.2.93/random.test.js000066400000000000000000000053371457563604600152570ustar00rootroot00000000000000import * as random from './random.js' import * as t from './testing.js' import * as binary from './binary.js' import * as math from './math.js' import * as number from './number.js' /** * @param {t.TestCase} tc */ export const testRandom = tc => { const res = random.oneOf([1, 2, 3]) t.assert(res > 0) } /** * @param {t.TestCase} tc */ export const testUint32 = tc => { const iterations = 10000 let largest = 0 let smallest = number.HIGHEST_INT32 let newNum = 0 let lenSum = 0 let ones = 0 for (let i = 0; i < iterations; i++) { newNum = random.uint32() lenSum += newNum.toString().length ones += newNum.toString(2).split('').filter(x => x === '1').length if (newNum > largest) { largest = newNum } if (newNum < smallest) { smallest = newNum } } t.info(`Largest number generated is ${largest} (0x${largest.toString(16)})`) t.info(`Smallest number generated is ${smallest} (0x${smallest.toString(16)})`) t.info(`Average decimal length of number is ${lenSum / iterations}`) t.info(`Average number of 1s in number is ${ones / iterations} (expecting ~16)`) t.assert(((largest & binary.BITS32) >>> 0) === largest, 'Largest number is 32 bits long.') t.assert(((smallest & binary.BITS32) >>> 0) === smallest, 'Smallest number is 32 bits long.') } /** * @param {t.TestCase} tc */ export const testUint53 = tc => { const iterations = 10000 let largest = 0 let smallest = number.MAX_SAFE_INTEGER let newNum = 0 let lenSum = 0 let ones = 0 for (let i = 0; i < iterations; i++) { newNum = random.uint53() lenSum += newNum.toString().length ones += newNum.toString(2).split('').filter(x => x === '1').length if (newNum > largest) { largest = newNum } if (newNum < smallest) { smallest = newNum } } t.info(`Largest number generated is ${largest}`) t.info(`Smallest number generated is ${smallest}`) t.info(`Average decimal length of number is ${lenSum / iterations}`) t.info(`Average number of 1s in number is ${ones / iterations} (expecting ~26.5)`) t.assert(largest > number.MAX_SAFE_INTEGER * 0.9) } /** * @param {t.TestCase} tc */ export const testUuidv4 = tc => { t.info(`Generated a UUIDv4: ${random.uuidv4()}`) } /** * @param {t.TestCase} tc */ export const testUuidv4Overlaps = tc => { t.skip(!t.production) const iterations = t.extensive ? 1000000 : 10000 const uuids = new Set() for (let i = 0; i < iterations; i++) { const uuid = random.uuidv4() if (uuids.has(uuid)) { t.fail('uuid already exists') } else { uuids.add(uuid) } if (uuids.size % (iterations / 20) === 0) { t.info(`${math.round(uuids.size * 100 / iterations)}% complete`) } } t.assert(uuids.size === iterations) } lib0-0.2.93/rollup.config.js000066400000000000000000000007751457563604600156030ustar00rootroot00000000000000import * as fs from 'fs' const roots = ['./', './crypto/', './hash/'] const files = roots.map(root => fs.readdirSync(root).map(f => root + f)).flat().filter(file => /(? new Set() /** * @template T * @param {Set} set * @return {Array} */ export const toArray = set => Array.from(set) /** * @template T * @param {Set} set * @return {T} */ export const first = set => set.values().next().value || undefined /** * @template T * @param {Iterable} entries * @return {Set} */ export const from = entries => new Set(entries) lib0-0.2.93/set.test.js000066400000000000000000000005741457563604600145700ustar00rootroot00000000000000import * as t from './testing.js' import * as set from './set.js' /** * @param {t.TestCase} _tc */ export const testFirst = _tc => { const two = set.from(['a', 'b']) const one = set.from(['b']) const zero = set.create() t.assert(set.first(two) === 'a') t.assert(set.first(one) === 'b') t.assert(set.first(zero) === undefined) t.compare(set.toArray(one), ['b']) } lib0-0.2.93/sort.js000066400000000000000000000041751457563604600140070ustar00rootroot00000000000000/** * Efficient sort implementations. * * Note: These sort implementations were created to compare different sorting algorithms in JavaScript. * Don't use them if you don't know what you are doing. Native Array.sort is almost always a better choice. * * @module sort */ import * as math from './math.js' /** * @template T * @param {Array} arr * @param {number} lo * @param {number} hi * @param {function(T,T):number} compare */ export const _insertionSort = (arr, lo, hi, compare) => { for (let i = lo + 1; i <= hi; i++) { for (let j = i; j > 0 && compare(arr[j - 1], arr[j]) > 0; j--) { const tmp = arr[j] arr[j] = arr[j - 1] arr[j - 1] = tmp } } } /** * @template T * @param {Array} arr * @param {function(T,T):number} compare * @return {void} */ export const insertionSort = (arr, compare) => { _insertionSort(arr, 0, arr.length - 1, compare) } /** * @template T * @param {Array} arr * @param {number} lo * @param {number} hi * @param {function(T,T):number} compare */ const _quickSort = (arr, lo, hi, compare) => { if (hi - lo < 42) { _insertionSort(arr, lo, hi, compare) } else { const pivot = arr[math.floor((lo + hi) / 2)] let i = lo let j = hi while (true) { while (compare(pivot, arr[i]) > 0) { i++ } while (compare(arr[j], pivot) > 0) { j-- } if (i >= j) { break } // swap arr[i] with arr[j] // and increment i and j const arri = arr[i] arr[i++] = arr[j] arr[j--] = arri } _quickSort(arr, lo, j, compare) _quickSort(arr, j + 1, hi, compare) } } /** * This algorithm beats Array.prototype.sort in Chrome only with arrays with 10 million entries. * In most cases [].sort will do just fine. Make sure to performance test your use-case before you * integrate this algorithm. * * Note that Chrome's sort is now a stable algorithm (Timsort). Quicksort is not stable. * * @template T * @param {Array} arr * @param {function(T,T):number} compare * @return {void} */ export const quicksort = (arr, compare) => { _quickSort(arr, 0, arr.length - 1, compare) } lib0-0.2.93/sort.test.js000066400000000000000000000114431457563604600147610ustar00rootroot00000000000000import * as prng from './prng.js' import * as t from './testing.js' import * as sort from './sort.js' /** * @template T * @param {t.TestCase} tc * @param {Array} arr * @param {function(T,T):number} compare * @param {function(T):number} getVal */ const runSortTest = (tc, arr, compare, getVal) => { const arrSort = arr const arrQuicksort = arr.slice() const arrInsertionsort = arr.slice() t.measureTime('Array.constructor.sort', () => { arrSort.sort(compare) }) if (arrInsertionsort.length <= 10000) { t.measureTime('Insertionsort', () => { sort.insertionSort(arrInsertionsort, compare) }) t.compareArrays(arrSort, arrInsertionsort, 'compare Insertionsort with expected result') } t.measureTime('Quicksort', () => { sort.quicksort(arrQuicksort, compare) }) // quickSort is not stable t.compareArrays(arrSort.map(getVal), arrQuicksort.map(getVal), 'compare Quicksort with expected result') } /** * @template T * @param {t.TestCase} tc * @param {function(number):Array} createArray * @param {function(T,T):number} compare 0 if equal, 1 if a { t.describe('sort 10 elements') runSortTest(tc, createArray(10), compare, getVal) t.describe('sort 10 elements') runSortTest(tc, createArray(10), compare, getVal) t.describe('sort 10 elements') runSortTest(tc, createArray(10), compare, getVal) t.describe('sort 50 elements') runSortTest(tc, createArray(50), compare, getVal) t.describe('sort 100 elements') runSortTest(tc, createArray(100), compare, getVal) t.describe('sort 500 elements') runSortTest(tc, createArray(500), compare, getVal) t.describe('sort 1k elements') runSortTest(tc, createArray(1000), compare, getVal) t.describe('sort 10k elements') runSortTest(tc, createArray(10000), compare, getVal) t.describe('sort 100k elements') runSortTest(tc, createArray(100000), compare, getVal) if (t.extensive) { t.describe('sort 1M elements') runSortTest(tc, createArray(1000000), compare, getVal) t.describe('sort 10M elements') runSortTest(tc, createArray(10000000), compare, getVal) } } /** * @param {t.TestCase} tc */ export const testSortUint8 = tc => { t.skip(!t.production) /** * @param {number} i * @return {number} */ const getVal = i => i /** * @param {number} a * @param {number} b * @return {number} */ const compare = (a, b) => a - b /** * @param {number} len * @return {Array} */ const createArray = len => Array.from(prng.uint8Array(tc.prng, len * 2)) createSortTest(tc, createArray, compare, getVal) } /** * @param {t.TestCase} tc */ export const testSortUint32 = tc => { t.skip(!t.production) /** * @param {number} i * @return {number} */ const getVal = i => i /** * @param {number} a * @param {number} b * @return {number} */ const compare = (a, b) => a - b /** * @param {number} len * @return {Array} */ const createArray = len => Array.from(prng.uint32Array(tc.prng, len)) createSortTest(tc, createArray, compare, getVal) } /** * @param {t.TestCase} tc */ export const testSortUint16 = tc => { t.skip(!t.production) /** * @param {number} i * @return {number} */ const getVal = i => i /** * @param {number} a * @param {number} b * @return {number} */ const compare = (a, b) => a - b /** * @param {number} len * @return {Array} */ const createArray = len => Array.from(prng.uint16Array(tc.prng, len)) createSortTest(tc, createArray, compare, getVal) } /** * @param {t.TestCase} tc */ export const testSortObjectUint32 = tc => { /** * @param {{index:number}} obj * @return {number} */ const getVal = obj => obj.index /** * @param {{index:number}} a * @param {{index:number}} b * @return {number} */ const compare = (a, b) => a.index - b.index /** * @param {number} len * @return {Array<{index:number}>} */ const createArray = len => Array.from(prng.uint32Array(tc.prng, len)).map(index => ({ index })) createSortTest(tc, createArray, compare, getVal) } /** * @param {t.TestCase} tc */ export const testListVsArrayPerformance = tc => { /** * @typedef {{ val: number }} Val * @typedef {{ val: Val, next: item }|null} item */ const len = 100000 t.measureTime('array creation', () => { /** * @type {Array} */ const array = new Array(len) for (let i = 0; i < len; i++) { array[i] = { val: i } } }) t.measureTime('list creation', () => { /** * @type {item} */ const listStart = { val: { val: 0 }, next: null } for (let i = 1, n = listStart; i < len; i++) { const next = { val: { val: i }, next: null } n.next = next n = next } }) } lib0-0.2.93/statistics.js000066400000000000000000000010341457563604600152010ustar00rootroot00000000000000/** * Utility helpers for generating statistics. * * @module statistics */ import * as math from './math.js' /** * @param {Array} arr Array of values * @return {number} Returns null if the array is empty */ export const median = arr => arr.length === 0 ? NaN : (arr.length % 2 === 1 ? arr[(arr.length - 1) / 2] : (arr[math.floor((arr.length - 1) / 2)] + arr[math.ceil((arr.length - 1) / 2)]) / 2) /** * @param {Array} arr * @return {number} */ export const average = arr => arr.reduce(math.add, 0) / arr.length lib0-0.2.93/statistics.test.js000066400000000000000000000012151457563604600161600ustar00rootroot00000000000000import * as statistics from './statistics.js' import * as t from './testing.js' import * as math from './math.js' /** * @param {t.TestCase} tc */ export const testMedian = tc => { t.assert(math.isNaN(statistics.median([])), 'median([]) = NaN') t.assert(statistics.median([1]) === 1, 'median([x]) = x') t.assert(statistics.median([1, 2, 3]) === 2, 'median([a,b,c]) = b') t.assert(statistics.median([1, 2, 3, 4]) === (2 + 3) / 2, 'median([a,b,c,d]) = (b+c)/2') t.assert(statistics.median([1, 2, 3, 4, 5]) === 3, 'median([a,b,c,d,e]) = c') t.assert(statistics.median([1, 2, 3, 4, 5, 6]) === (3 + 4) / 2, 'median([a,b,c,d,e,f]) = (c+d)/2') } lib0-0.2.93/storage.js000066400000000000000000000033331457563604600144570ustar00rootroot00000000000000/* eslint-env browser */ /** * Isomorphic variable storage. * * Uses LocalStorage in the browser and falls back to in-memory storage. * * @module storage */ /* c8 ignore start */ class VarStoragePolyfill { constructor () { this.map = new Map() } /** * @param {string} key * @param {any} newValue */ setItem (key, newValue) { this.map.set(key, newValue) } /** * @param {string} key */ getItem (key) { return this.map.get(key) } } /* c8 ignore stop */ /** * @type {any} */ let _localStorage = new VarStoragePolyfill() let usePolyfill = true /* c8 ignore start */ try { // if the same-origin rule is violated, accessing localStorage might thrown an error if (typeof localStorage !== 'undefined' && localStorage) { _localStorage = localStorage usePolyfill = false } } catch (e) { } /* c8 ignore stop */ /** * This is basically localStorage in browser, or a polyfill in nodejs */ /* c8 ignore next */ export const varStorage = _localStorage /** * A polyfill for `addEventListener('storage', event => {..})` that does nothing if the polyfill is being used. * * @param {function({ key: string, newValue: string, oldValue: string }): void} eventHandler * @function */ /* c8 ignore next */ export const onChange = eventHandler => usePolyfill || addEventListener('storage', /** @type {any} */ (eventHandler)) /** * A polyfill for `removeEventListener('storage', event => {..})` that does nothing if the polyfill is being used. * * @param {function({ key: string, newValue: string, oldValue: string }): void} eventHandler * @function */ /* c8 ignore next */ export const offChange = eventHandler => usePolyfill || removeEventListener('storage', /** @type {any} */ (eventHandler)) lib0-0.2.93/storage.test.js000066400000000000000000000005371457563604600154400ustar00rootroot00000000000000import * as storage from './storage.js' import * as t from './testing.js' /** * @param {t.TestCase} tc */ export const testStorageModule = tc => { const s = storage.varStorage /** * @type {any} */ let lastEvent = null storage.onChange(event => { lastEvent = event }) s.setItem('key', 'value') t.assert(lastEvent === null) } lib0-0.2.93/string.js000066400000000000000000000073701457563604600143260ustar00rootroot00000000000000import * as array from './array.js' /** * Utility module to work with strings. * * @module string */ export const fromCharCode = String.fromCharCode export const fromCodePoint = String.fromCodePoint /** * The largest utf16 character. * Corresponds to Uint8Array([255, 255]) or charcodeof(2x2^8) */ export const MAX_UTF16_CHARACTER = fromCharCode(65535) /** * @param {string} s * @return {string} */ const toLowerCase = s => s.toLowerCase() const trimLeftRegex = /^\s*/g /** * @param {string} s * @return {string} */ export const trimLeft = s => s.replace(trimLeftRegex, '') const fromCamelCaseRegex = /([A-Z])/g /** * @param {string} s * @param {string} separator * @return {string} */ export const fromCamelCase = (s, separator) => trimLeft(s.replace(fromCamelCaseRegex, match => `${separator}${toLowerCase(match)}`)) /** * Compute the utf8ByteLength * @param {string} str * @return {number} */ export const utf8ByteLength = str => unescape(encodeURIComponent(str)).length /** * @param {string} str * @return {Uint8Array} */ export const _encodeUtf8Polyfill = str => { const encodedString = unescape(encodeURIComponent(str)) const len = encodedString.length const buf = new Uint8Array(len) for (let i = 0; i < len; i++) { buf[i] = /** @type {number} */ (encodedString.codePointAt(i)) } return buf } /* c8 ignore next */ export const utf8TextEncoder = /** @type {TextEncoder} */ (typeof TextEncoder !== 'undefined' ? new TextEncoder() : null) /** * @param {string} str * @return {Uint8Array} */ export const _encodeUtf8Native = str => utf8TextEncoder.encode(str) /** * @param {string} str * @return {Uint8Array} */ /* c8 ignore next */ export const encodeUtf8 = utf8TextEncoder ? _encodeUtf8Native : _encodeUtf8Polyfill /** * @param {Uint8Array} buf * @return {string} */ export const _decodeUtf8Polyfill = buf => { let remainingLen = buf.length let encodedString = '' let bufPos = 0 while (remainingLen > 0) { const nextLen = remainingLen < 10000 ? remainingLen : 10000 const bytes = buf.subarray(bufPos, bufPos + nextLen) bufPos += nextLen // Starting with ES5.1 we can supply a generic array-like object as arguments encodedString += String.fromCodePoint.apply(null, /** @type {any} */ (bytes)) remainingLen -= nextLen } return decodeURIComponent(escape(encodedString)) } /* c8 ignore next */ export let utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }) /* c8 ignore start */ if (utf8TextDecoder && utf8TextDecoder.decode(new Uint8Array()).length === 1) { // Safari doesn't handle BOM correctly. // This fixes a bug in Safari 13.0.5 where it produces a BOM the first time it is called. // utf8TextDecoder.decode(new Uint8Array()).length === 1 on the first call and // utf8TextDecoder.decode(new Uint8Array()).length === 1 on the second call // Another issue is that from then on no BOM chars are recognized anymore /* c8 ignore next */ utf8TextDecoder = null } /* c8 ignore stop */ /** * @param {Uint8Array} buf * @return {string} */ export const _decodeUtf8Native = buf => /** @type {TextDecoder} */ (utf8TextDecoder).decode(buf) /** * @param {Uint8Array} buf * @return {string} */ /* c8 ignore next */ export const decodeUtf8 = utf8TextDecoder ? _decodeUtf8Native : _decodeUtf8Polyfill /** * @param {string} str The initial string * @param {number} index Starting position * @param {number} remove Number of characters to remove * @param {string} insert New content to insert */ export const splice = (str, index, remove, insert = '') => str.slice(0, index) + insert + str.slice(index + remove) /** * @param {string} source * @param {number} n */ export const repeat = (source, n) => array.unfold(n, () => source).join('') lib0-0.2.93/string.test.js000066400000000000000000000045441457563604600153040ustar00rootroot00000000000000import * as prng from './prng.js' import * as string from './string.js' import * as t from './testing.js' /** * @param {t.TestCase} tc */ export const testUtilities = tc => { t.assert(string.repeat('1', 3) === '111') t.assert(string.repeat('1', 0) === '') t.assert(string.repeat('1', 1) === '1') } /** * @param {t.TestCase} tc */ export const testLowercaseTransformation = tc => { t.compareStrings(string.fromCamelCase('ThisIsATest', ' '), 'this is a test') t.compareStrings(string.fromCamelCase('Testing', ' '), 'testing') t.compareStrings(string.fromCamelCase('testingThis', ' '), 'testing this') t.compareStrings(string.fromCamelCase('testYAY', ' '), 'test y a y') } /** * @param {t.TestCase} tc */ export const testRepeatStringUtf8Encoding = tc => { t.skip(!string.utf8TextDecoder) const str = prng.utf16String(tc.prng, 1000000) let nativeResult, polyfilledResult t.measureTime('TextEncoder utf8 encoding', () => { nativeResult = string._encodeUtf8Native(str) }) t.measureTime('Polyfilled utf8 encoding', () => { polyfilledResult = string._encodeUtf8Polyfill(str) }) t.compare(nativeResult, polyfilledResult, 'Encoded utf8 buffers match') } /** * @param {t.TestCase} tc */ export const testRepeatStringUtf8Decoding = tc => { t.skip(!string.utf8TextDecoder) const buf = string.encodeUtf8(prng.utf16String(tc.prng, 1000000)) let nativeResult, polyfilledResult t.measureTime('TextEncoder utf8 decoding', () => { nativeResult = string._decodeUtf8Native(buf) }) t.measureTime('Polyfilled utf8 decoding', () => { polyfilledResult = string._decodeUtf8Polyfill(buf) }) t.compare(nativeResult, polyfilledResult, 'Decoded utf8 buffers match') } /** * @param {t.TestCase} tc */ export const testBomEncodingDecoding = tc => { const bomStr = 'bom' t.assert(bomStr.length === 4) const polyfilledResult = string._decodeUtf8Polyfill(string._encodeUtf8Polyfill(bomStr)) t.assert(polyfilledResult.length === 4) t.assert(polyfilledResult === bomStr) if (string.utf8TextDecoder) { const nativeResult = string._decodeUtf8Native(string._encodeUtf8Native(bomStr)) t.assert(nativeResult === polyfilledResult) } } /** * @param {t.TestCase} tc */ export const testSplice = tc => { const initial = 'xyz' t.compareStrings(string.splice(initial, 0, 2), 'z') t.compareStrings(string.splice(initial, 0, 2, 'u'), 'uz') } lib0-0.2.93/symbol.js000066400000000000000000000004101457563604600143110ustar00rootroot00000000000000/** * Utility module to work with EcmaScript Symbols. * * @module symbol */ /** * Return fresh symbol. * * @return {Symbol} */ export const create = Symbol /** * @param {any} s * @return {boolean} */ export const isSymbol = s => typeof s === 'symbol' lib0-0.2.93/symbol.test.js000066400000000000000000000004731457563604600153000ustar00rootroot00000000000000import * as t from './testing.js' import * as symbol from './symbol.js' /** * @param {t.TestCase} _tc */ export const testBasicSymbolFeatures = _tc => { const s1 = symbol.create() const s2 = symbol.create() t.assert(s1 !== s2) t.assert(s1 === s1) // eslint-disable-line t.assert(symbol.isSymbol(s1)) } lib0-0.2.93/test.html000066400000000000000000000136171457563604600143300ustar00rootroot00000000000000 Testing lib0 lib0-0.2.93/test.js000066400000000000000000000042171457563604600137740ustar00rootroot00000000000000import { runTests } from './testing.js' import * as array from './array.test.js' import * as broadcastchannel from './broadcastchannel.test.js' import * as crypto from './crypto.test.js' import * as rabin from './hash/rabin.test.js' import * as sha256 from './hash/sha256.test.js' import * as logging from './logging.test.js' import * as string from './string.test.js' import * as encoding from './encoding.test.js' import * as diff from './diff.test.js' import * as testing from './testing.test.js' import * as indexeddb from './indexeddb.test.js' import * as prng from './prng.test.js' import * as log from 'lib0/logging' import * as statistics from './statistics.test.js' import * as binary from './binary.test.js' import * as random from './random.test.js' import * as promise from './promise.test.js' import * as queue from './queue.test.js' import * as map from './map.test.js' import * as eventloop from './eventloop.test.js' import * as time from './time.test.js' import * as pair from './pair.test.js' import * as object from './object.test.js' import * as observable from './observable.test.js' import * as math from './math.test.js' import * as number from './number.test.js' import * as buffer from './buffer.test.js' import * as set from './set.test.js' import * as sort from './sort.test.js' import * as url from './url.test.js' import * as metric from './metric.test.js' import * as func from './function.test.js' import * as storage from './storage.test.js' import * as list from './list.test.js' import * as cache from './cache.test.js' import * as symbol from './symbol.test.js' import { isBrowser, isNode } from './environment.js' /* c8 ignore next */ if (isBrowser) { log.createVConsole(document.body) } runTests({ array, broadcastchannel, crypto, rabin, sha256, logging, string, encoding, diff, testing, indexeddb, prng, statistics, binary, random, promise, queue, map, eventloop, time, pair, object, observable, math, number, buffer, set, sort, url, metric, func, storage, list, cache, symbol }).then(success => { /* c8 ignore next */ if (isNode) { process.exit(success ? 0 : 1) } }) lib0-0.2.93/testing.js000066400000000000000000000643371457563604600145030ustar00rootroot00000000000000/** * Testing framework with support for generating tests. * * ```js * // test.js template for creating a test executable * import { runTests } from 'lib0/testing' * import * as log from 'lib0/logging' * import * as mod1 from './mod1.test.js' * import * as mod2 from './mod2.test.js' * import { isBrowser, isNode } from 'lib0/environment.js' * * if (isBrowser) { * // optional: if this is ran in the browser, attach a virtual console to the dom * log.createVConsole(document.body) * } * * runTests({ * mod1, * mod2, * }).then(success => { * if (isNode) { * process.exit(success ? 0 : 1) * } * }) * ``` * * ```js * // mod1.test.js * /** * * runTests automatically tests all exported functions that start with "test". * * The name of the function should be in camelCase and is used for the logging output. * * * * @param {t.TestCase} tc * *\/ * export const testMyFirstTest = tc => { * t.compare({ a: 4 }, { a: 4 }, 'objects are equal') * } * ``` * * Now you can simply run `node test.js` to run your test or run test.js in the browser. * * @module testing */ import * as log from 'lib0/logging' import { simpleDiffString } from './diff.js' import * as object from './object.js' import * as string from './string.js' import * as math from './math.js' import * as random from './random.js' import * as prng from './prng.js' import * as statistics from './statistics.js' import * as array from './array.js' import * as env from './environment.js' import * as json from './json.js' import * as time from './time.js' import * as promise from './promise.js' import * as performance from 'lib0/performance' export { production } from './environment.js' export const extensive = env.hasConf('extensive') /* c8 ignore next */ export const envSeed = env.hasParam('--seed') ? Number.parseInt(env.getParam('--seed', '0')) : null export class TestCase { /** * @param {string} moduleName * @param {string} testName */ constructor (moduleName, testName) { /** * @type {string} */ this.moduleName = moduleName /** * @type {string} */ this.testName = testName /** * This type can store custom information related to the TestCase * * @type {Map} */ this.meta = new Map() this._seed = null this._prng = null } resetSeed () { this._seed = null this._prng = null } /** * @type {number} */ /* c8 ignore next */ get seed () { /* c8 ignore else */ if (this._seed === null) { /* c8 ignore next */ this._seed = envSeed === null ? random.uint32() : envSeed } return this._seed } /** * A PRNG for this test case. Use only this PRNG for randomness to make the test case reproducible. * * @type {prng.PRNG} */ get prng () { /* c8 ignore else */ if (this._prng === null) { this._prng = prng.create(this.seed) } return this._prng } } export const repetitionTime = Number(env.getParam('--repetition-time', '50')) /* c8 ignore next */ const testFilter = env.hasParam('--filter') ? env.getParam('--filter', '') : null /* c8 ignore next */ const testFilterRegExp = testFilter !== null ? new RegExp(testFilter) : /.*/ const repeatTestRegex = /^(repeat|repeating)\s/ /** * @param {string} moduleName * @param {string} name * @param {function(TestCase):void|Promise} f * @param {number} i * @param {number} numberOfTests */ export const run = async (moduleName, name, f, i, numberOfTests) => { const uncamelized = string.fromCamelCase(name.slice(4), ' ') const filtered = !testFilterRegExp.test(`[${i + 1}/${numberOfTests}] ${moduleName}: ${uncamelized}`) /* c8 ignore next 3 */ if (filtered) { return true } const tc = new TestCase(moduleName, name) const repeat = repeatTestRegex.test(uncamelized) const groupArgs = [log.GREY, `[${i + 1}/${numberOfTests}] `, log.PURPLE, `${moduleName}: `, log.BLUE, uncamelized] /* c8 ignore next 5 */ if (testFilter === null) { log.groupCollapsed(...groupArgs) } else { log.group(...groupArgs) } const times = [] const start = performance.now() let lastTime = start /** * @type {any} */ let err = null performance.mark(`${name}-start`) do { try { const p = f(tc) if (promise.isPromise(p)) { await p } } catch (_err) { err = _err } const currTime = performance.now() times.push(currTime - lastTime) lastTime = currTime if (repeat && err === null && (lastTime - start) < repetitionTime) { tc.resetSeed() } else { break } } while (err === null && (lastTime - start) < repetitionTime) performance.mark(`${name}-end`) /* c8 ignore next 3 */ if (err !== null && err.constructor !== SkipError) { log.printError(err) } performance.measure(name, `${name}-start`, `${name}-end`) log.groupEnd() const duration = lastTime - start let success = true times.sort((a, b) => a - b) /* c8 ignore next 3 */ const againMessage = env.isBrowser ? ` - ${window.location.host + window.location.pathname}?filter=\\[${i + 1}/${tc._seed === null ? '' : `&seed=${tc._seed}`}` : `\nrepeat: npm run test -- --filter "\\[${i + 1}/" ${tc._seed === null ? '' : `--seed ${tc._seed}`}` const timeInfo = (repeat && err === null) ? ` - ${times.length} repetitions in ${time.humanizeDuration(duration)} (best: ${time.humanizeDuration(times[0])}, worst: ${time.humanizeDuration(array.last(times))}, median: ${time.humanizeDuration(statistics.median(times))}, average: ${time.humanizeDuration(statistics.average(times))})` : ` in ${time.humanizeDuration(duration)}` if (err !== null) { /* c8 ignore start */ if (err.constructor === SkipError) { log.print(log.GREY, log.BOLD, 'Skipped: ', log.UNBOLD, uncamelized) } else { success = false log.print(log.RED, log.BOLD, 'Failure: ', log.UNBOLD, log.UNCOLOR, uncamelized, log.GREY, timeInfo, againMessage) } /* c8 ignore stop */ } else { log.print(log.GREEN, log.BOLD, 'Success: ', log.UNBOLD, log.UNCOLOR, uncamelized, log.GREY, timeInfo, againMessage) } return success } /** * Describe what you are currently testing. The message will be logged. * * ```js * export const testMyFirstTest = tc => { * t.describe('crunching numbers', 'already crunched 4 numbers!') // the optional second argument can describe the state. * } * ``` * * @param {string} description * @param {string} info */ export const describe = (description, info = '') => log.print(log.BLUE, description, ' ', log.GREY, info) /** * Describe the state of the current computation. * ```js * export const testMyFirstTest = tc => { * t.info(already crunched 4 numbers!') // the optional second argument can describe the state. * } * ``` * * @param {string} info */ export const info = info => describe('', info) export const printDom = log.printDom export const printCanvas = log.printCanvas /** * Group outputs in a collapsible category. * * ```js * export const testMyFirstTest = tc => { * t.group('subtest 1', () => { * t.describe('this message is part of a collapsible section') * }) * await t.groupAsync('subtest async 2', async () => { * await someaction() * t.describe('this message is part of a collapsible section') * }) * } * ``` * * @param {string} description * @param {function(...any):void} f */ export const group = (description, f) => { log.group(log.BLUE, description) try { f() } finally { log.groupEnd() } } /** * Group outputs in a collapsible category. * * ```js * export const testMyFirstTest = async tc => { * t.group('subtest 1', () => { * t.describe('this message is part of a collapsible section') * }) * await t.groupAsync('subtest async 2', async () => { * await someaction() * t.describe('this message is part of a collapsible section') * }) * } * ``` * * @param {string} description * @param {function(...any):Promise} f */ export const groupAsync = async (description, f) => { log.group(log.BLUE, description) try { await f() } finally { log.groupEnd() } } /** * Measure the time that it takes to calculate something. * * ```js * export const testMyFirstTest = async tc => { * t.measureTime('measurement', () => { * heavyCalculation() * }) * await t.groupAsync('async measurement', async () => { * await heavyAsyncCalculation() * }) * } * ``` * * @param {string} message * @param {function(...any):void} f * @return {number} Returns a promise that resolves the measured duration to apply f */ export const measureTime = (message, f) => { let duration const start = performance.now() try { f() } finally { duration = performance.now() - start log.print(log.PURPLE, message, log.GREY, ` ${time.humanizeDuration(duration)}`) } return duration } /** * Measure the time that it takes to calculate something. * * ```js * export const testMyFirstTest = async tc => { * t.measureTimeAsync('measurement', async () => { * await heavyCalculation() * }) * await t.groupAsync('async measurement', async () => { * await heavyAsyncCalculation() * }) * } * ``` * * @param {string} message * @param {function(...any):Promise} f * @return {Promise} Returns a promise that resolves the measured duration to apply f */ export const measureTimeAsync = async (message, f) => { let duration const start = performance.now() try { await f() } finally { duration = performance.now() - start log.print(log.PURPLE, message, log.GREY, ` ${time.humanizeDuration(duration)}`) } return duration } /** * @template T * @param {Array} as * @param {Array} bs * @param {string} [m] * @return {boolean} */ export const compareArrays = (as, bs, m = 'Arrays match') => { if (as.length !== bs.length) { fail(m) } for (let i = 0; i < as.length; i++) { if (as[i] !== bs[i]) { fail(m) } } return true } /** * @param {string} a * @param {string} b * @param {string} [m] * @throws {TestError} Throws if tests fails */ export const compareStrings = (a, b, m = 'Strings match') => { if (a !== b) { const diff = simpleDiffString(a, b) log.print(log.GREY, a.slice(0, diff.index), log.RED, a.slice(diff.index, diff.remove), log.GREEN, diff.insert, log.GREY, a.slice(diff.index + diff.remove)) fail(m) } } /** * @template K,V * @param {Object} a * @param {Object} b * @param {string} [m] * @throws {TestError} Throws if test fails */ export const compareObjects = (a, b, m = 'Objects match') => { object.equalFlat(a, b) || fail(m) } /** * @param {any} _constructor * @param {any} a * @param {any} b * @param {string} path * @throws {TestError} */ const compareValues = (_constructor, a, b, path) => { if (a !== b) { fail(`Values ${json.stringify(a)} and ${json.stringify(b)} don't match (${path})`) } return true } /** * @param {string?} message * @param {string} reason * @param {string} path * @throws {TestError} */ const _failMessage = (message, reason, path) => fail( message === null ? `${reason} ${path}` : `${message} (${reason}) ${path}` ) /** * @param {any} a * @param {any} b * @param {string} path * @param {string?} message * @param {function(any,any,any,string,any):boolean} customCompare */ const _compare = (a, b, path, message, customCompare) => { // we don't use assert here because we want to test all branches (istanbul errors if one branch is not tested) if (a == null || b == null) { return compareValues(null, a, b, path) } if (a.constructor !== b.constructor) { _failMessage(message, 'Constructors don\'t match', path) } let success = true switch (a.constructor) { case ArrayBuffer: a = new Uint8Array(a) b = new Uint8Array(b) // eslint-disable-next-line no-fallthrough case Uint8Array: { if (a.byteLength !== b.byteLength) { _failMessage(message, 'ArrayBuffer lengths match', path) } for (let i = 0; success && i < a.length; i++) { success = success && a[i] === b[i] } break } case Set: { if (a.size !== b.size) { _failMessage(message, 'Sets have different number of attributes', path) } // @ts-ignore a.forEach(value => { if (!b.has(value)) { _failMessage(message, `b.${path} does have ${value}`, path) } }) break } case Map: { if (a.size !== b.size) { _failMessage(message, 'Maps have different number of attributes', path) } // @ts-ignore a.forEach((value, key) => { if (!b.has(key)) { _failMessage(message, `Property ${path}["${key}"] does not exist on second argument`, path) } _compare(value, b.get(key), `${path}["${key}"]`, message, customCompare) }) break } case Object: if (object.length(a) !== object.length(b)) { _failMessage(message, 'Objects have a different number of attributes', path) } object.forEach(a, (value, key) => { if (!object.hasProperty(b, key)) { _failMessage(message, `Property ${path} does not exist on second argument`, path) } _compare(value, b[key], `${path}["${key}"]`, message, customCompare) }) break case Array: if (a.length !== b.length) { _failMessage(message, 'Arrays have a different number of attributes', path) } // @ts-ignore a.forEach((value, i) => _compare(value, b[i], `${path}[${i}]`, message, customCompare)) break /* c8 ignore next 4 */ default: if (!customCompare(a.constructor, a, b, path, compareValues)) { _failMessage(message, `Values ${json.stringify(a)} and ${json.stringify(b)} don't match`, path) } } assert(success, message) return true } /** * @template T * @param {T} a * @param {T} b * @param {string?} [message] * @param {function(any,T,T,string,any):boolean} [customCompare] */ export const compare = (a, b, message = null, customCompare = compareValues) => _compare(a, b, 'obj', message, customCompare) /** * @template T * @param {T} property * @param {string?} [message] * @return {asserts property is NonNullable} * @throws {TestError} */ /* c8 ignore next */ export const assert = (property, message = null) => { property || fail(`Assertion failed${message !== null ? `: ${message}` : ''}`) } /** * @param {function(...any):Promise} f */ export const promiseRejected = async f => { try { await f() } catch (err) { return } fail('Expected promise to fail') } /** * @param {function(...any):void} f * @throws {TestError} */ export const fails = f => { try { f() } catch (_err) { log.print(log.GREEN, '⇖ This Error was expected') return } fail('Expected this to fail') } /** * @param {function(...any):Promise} f * @throws {TestError} */ export const failsAsync = async f => { try { await f() } catch (_err) { log.print(log.GREEN, '⇖ This Error was expected') return } fail('Expected this to fail') } /** * @param {Object>>} tests */ export const runTests = async tests => { /** * @param {string} testname */ const filterTest = testname => testname.startsWith('test') || testname.startsWith('benchmark') const numberOfTests = object.map(tests, mod => object.map(mod, (f, fname) => /* c8 ignore next */ f && filterTest(fname) ? 1 : 0).reduce(math.add, 0)).reduce(math.add, 0) let successfulTests = 0 let testnumber = 0 const start = performance.now() for (const modName in tests) { const mod = tests[modName] for (const fname in mod) { const f = mod[fname] /* c8 ignore else */ if (f && filterTest(fname)) { const repeatEachTest = 1 let success = true for (let i = 0; success && i < repeatEachTest; i++) { success = await run(modName, fname, f, testnumber, numberOfTests) } testnumber++ /* c8 ignore else */ if (success) { successfulTests++ } } } } const end = performance.now() log.print('') const success = successfulTests === numberOfTests /* c8 ignore start */ if (success) { log.print(log.GREEN, log.BOLD, 'All tests successful!', log.GREY, log.UNBOLD, ` in ${time.humanizeDuration(end - start)}`) log.printImgBase64(nyanCatImage, 50) } else { const failedTests = numberOfTests - successfulTests log.print(log.RED, log.BOLD, `> ${failedTests} test${failedTests > 1 ? 's' : ''} failed`) } /* c8 ignore stop */ return success } class TestError extends Error {} /** * @param {string} reason * @throws {TestError} */ export const fail = reason => { log.print(log.RED, log.BOLD, 'X ', log.UNBOLD, reason) throw new TestError('Test Failed') } class SkipError extends Error {} /** * @param {boolean} cond If true, this tests will be skipped * @throws {SkipError} */ export const skip = (cond = true) => { if (cond) { throw new SkipError('skipping..') } } // eslint-disable-next-line const nyanCatImage = 'R0lGODlhjABMAPcAAMiSE0xMTEzMzUKJzjQ0NFsoKPc7//FM/9mH/z9x0HIiIoKCgmBHN+frGSkZLdDQ0LCwsDk71g0KCUzDdrQQEOFz/8yYdelmBdTiHFxcXDU2erR/mLrTHCgoKK5szBQUFNgSCTk6ymfpCB9VZS2Bl+cGBt2N8kWm0uDcGXhZRUvGq94NCFPhDiwsLGVlZTgqIPMDA1g3aEzS5D6xAURERDtG9JmBjJsZGWs2AD1W6Hp6eswyDeJ4CFNTU1LcEoJRmTMzSd14CTg5ser2GmDzBd17/xkZGUzMvoSMDiEhIfKruCwNAJaWlvRzA8kNDXDrCfi0pe1U/+GS6SZrAB4eHpZwVhoabsx9oiYmJt/TGHFxcYyMjOid0+Zl/0rF6j09PeRr/0zU9DxO6j+z0lXtBtp8qJhMAEssLGhoaPL/GVn/AAsWJ/9/AE3Z/zs9/3cAAOlf/+aa2RIyADo85uhh/0i84WtrazQ0UyMlmDMzPwUFBe16BTMmHau0E03X+g8pMEAoS1MBAf++kkzO8pBaqSZoe9uB/zE0BUQ3Sv///4WFheuiyzo880gzNDIyNissBNqF/8RiAOF2qG5ubj0vL1z6Avl5ASsgGkgUSy8vL/8n/z4zJy8lOv96uEssV1csAN5ZCDQ0Wz1a3tbEGHLeDdYKCg4PATE7PiMVFSoqU83eHEi43gUPAOZ8reGogeKU5dBBC8faHEez2lHYF4bQFMukFtl4CzY3kkzBVJfMGZkAAMfSFf27mP0t//g4/9R6Dfsy/1DRIUnSAPRD/0fMAFQ0Q+l7rnbaD0vEntCDD6rSGtO8GNpUCU/MK07LPNEfC7RaABUWWkgtOst+71v9AfD7GfDw8P19ATtA/NJpAONgB9yL+fm6jzIxMdnNGJxht1/2A9x//9jHGOSX3+5tBP27l35+fk5OTvZ9AhYgTjo0PUhGSDs9+LZjCFf2Aw0IDwcVAA8PD5lwg9+Q7YaChC0kJP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpGNEM2MUEyMzE0QTRFMTExOUQzRkE3QTBCRDNBMjdBQyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpERjQ0NEY0QkI2MTcxMUUxOUJEQkUzNUNGQTkwRTU2MiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpERjQ0NEY0QUI2MTcxMUUxOUJEQkUzNUNGQTkwRTU2MiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1OEE3RTIwRjcyQTlFMTExOTQ1QkY2QTU5QzVCQjJBOSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGNEM2MUEyMzE0QTRFMTExOUQzRkE3QTBCRDNBMjdBQyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAkKABEAIf4jUmVzaXplZCBvbiBodHRwczovL2V6Z2lmLmNvbS9yZXNpemUALAAAAACMAEwAAAj/ACMIHEiwoMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOqXLkxEcuXMAm6jElTZaKZNXOOvOnyps6fInECHdpRKNGjSJMqXZrSKNOnC51CnUq1qtWrWLNC9GmQq9avYMOKHUs2aFmmUs8SlcC2rdu3cNWeTEG3rt27eBnIHflBj6C/gAMLHpxCz16QElJw+7tom+PHkCOP+8utiuHDHRP/5WICgefPkIYV8RAjxudtkwVZjqCnNeaMmheZqADm8+coHn5kyPBt2udFvKrc+7A7gITXFzV77hLF9ucYGRaYo+FhWhHPUKokobFgQYbjyCsq/3fuHHr3BV88HMBeZd357+HFpxBEvnz0961b3+8OP37DtgON5xxznpl3ng5aJKiFDud5B55/Ct3TQwY93COQgLZV0AUC39ihRYMggjhJDw9CeNA9kyygxT2G6TGfcxUY8pkeH3YHgTkMNrgFBJOYs8Akl5l4Yoor3mPki6BpUsGMNS6QiA772WjNPR8CSRAjWBI0B5ZYikGQGFwyMseVYWoZppcDhSkmmVyaySWaAqk5pkBbljnQlnNYEZ05fGaAJGieVQAMjd2ZY+R+X2Rgh5FVBhmBG5BGKumklFZq6aWYZqrpppTOIQQNNPjoJ31RbGibIRXQuIExrSSY4wI66P9gToJlGHOFo374MQg2vGLjRa65etErNoMA68ew2Bi7a6+/Aitsr8UCi6yywzYb7LDR5jotsMvyau0qJJCwGw0vdrEkeTRe0UknC7hQYwYMQrmAMZ2U4WgY+Lahbxt+4Ovvvm34i68fAAscBsD9+kvwvgYDHLDACAu8sL4NFwzxvgkP3EYhhYzw52dFhOPZD5Ns0Iok6PUwyaIuTJLBBwuUIckG8RCkhhrUHKHzEUTcfLM7Ox/hjs9qBH0E0ZUE3bPPQO9cCdFGIx300EwH/bTPUfuc9M5U30zEzhN87NkwcDyXgY/oxaP22vFQIR2JBT3xBDhEUyO33FffXMndT1D/QzTfdPts9915qwEO3377DHjdfBd++N2J47y44Ij7PMN85UgBxzCeQQKJbd9wFyKI6jgqUBqoD6G66qinvvoQ1bSexutDyF4N7bLTHnvruLd+++u5v76766vb3jvxM0wxnyBQxHEued8Y8cX01Fc/fQcHZaG97A1or30DsqPgfRbDpzF+FtyPD37r4ns/fDXnp+/9+qif//74KMj/fRp9TEIDAxb4ixIWQcACFrAMFkigAhPIAAmwyHQDYYMEJ0jBClrwghjMoAY3yMEOYhAdQaCBFtBAAD244oQoTKEKV5iCbizEHjCkoCVgCENLULAJNLTHNSZ4jRzaQ4Y5tOEE+X24Qwn2MIdApKEQJUhEHvowiTBkhh7QVqT8GOmKWHwgFiWghR5AkCA+DKMYx0jGMprxjGhMYw5XMEXvGAZF5piEhQyih1CZ4wt6kIARfORFhjwDBoCEQQkIUoJAwmAFBDEkDAhSCkMOciCFDCQiB6JIgoDAkYQ0JAgSaUhLYnIgFLjH9AggkHsQYHo1oyMVptcCgUjvCx34opAWkp/L1BIhtxxILmfJy17KxJcrSQswhykWYRLzI8Y8pjKXycxfNvOZMEkmNC0izWlSpJrWlAg2s8kQnkRgJt7kpja92ZNwivOcNdkmOqOyzoyos50IeSc850nPegIzIAAh+QQJCgARACwAAAAAjABMAAAI/wAjCBxIsKDBgwgTKlzIsKHDhxAjSpxIsaLFixgzatzIsaPHjyBDihxJcmKikihTZkx0UqXLlw5ZwpxJ02DLmjhz6twJkqVMnz55Ch1KtGhCmUaTYkSqtKnJm05rMl0aVefUqlhtFryatavXr2DDHoRKkKzYs2jTqpW61exani3jun0rlCvdrhLy6t3Lt+9dlykCCx5MuDCDvyU/6BHEuLHjx5BT6EEsUkIKbowXbdvMubPncYy5VZlM+aNlxlxMIFjNGtKwIggqDGO9DbSg0aVNpxC0yEQFMKxZRwmHoEiU4AgW8cKdu+Pp1V2OI6c9bdq2cLARQGEeIV7zjM+nT//3oEfPNDiztTOXoMf7d4vhxbP+ts6cORrfIK3efq+8FnN2kPbeRPEFF918NCywgBZafLNfFffEM4k5C0wi4IARFchaBV0gqGCFDX6zQQqZZPChhRgSuBtyFRiC3DcJfqgFDTTSYOKJF6boUIGQaFLBizF+KOSQKA7EyJEEzXHkkWIQJMaSjMxBEJSMJAllk0ZCKWWWS1q5JJYCUbllBEpC6SWTEehxzz0rBqdfbL1AEsONQ9b5oQ73DOTGnnz26eefgAYq6KCEFmoooCHccosdk5yzYhQdBmfIj3N++AAEdCqoiDU62LGAOXkK5Icfg2BjKjZejDqqF6diM4iqfrT/ig2spZ6aqqqsnvqqqrLS2uqtq7a666i9qlqrqbeeQEIGN2awYhc/ilepghAssM6JaCwAQQ8ufBpqBGGE28a4bfgR7rnktnFuuH6ku24Y6Zp7brvkvpuuuuvGuy6949rrbr7kmltHIS6Yw6AWjgoyXRHErTYnPRtskMEXdLrQgzlffKHDBjZ8q4Ya1Bwh8hFEfPyxOyMf4Y7JaqR8BMuVpFyyySiPXAnLLsOc8so0p3yzyTmbHPPIK8sxyYJr9tdmcMPAwdqcG3TSyQZ2fniF1N8+8QQ4LFOjtdY/f1zJ109QwzLZXJvs9ddhqwEO2WabjHbXZLf99tdxgzy32k8Y/70gK+5UMsNu5UiB3mqQvIkA1FJLfO0CFH8ajxZXd/JtGpgPobnmmGe++RDVdJ7G50OIXg3popMeeueod37656l/vrrnm5uOOgZIfJECBpr3sZsgUMQRLXLTEJJBxPRkkETGRmSS8T1a2CCPZANlYb3oDVhvfQOio6B9FrOn8X0W2H/Pfefeaz97NeOXr/35mI+//vcouJ9MO7V03gcDFjCmxCIADGAAr1CFG2mBWQhEoA600IMLseGBEIygBCdIwQpa8IIYzKAGMcgDaGTMFSAMoQhDaAE9HOyEKOyBewZijxZG0BItbKElItiEGNrjGhC8hg3t8UIbzhCCO8ThA+Z1aMMexvCHDwxiDndoRBk+8A03Slp/1CTFKpaHiv3JS9IMssMuevGLYAyjGMdIxjJ6EYoK0oNivmCfL+RIINAD0GT0YCI8rdAgz4CBHmFQAoKUYI8wWAFBAAkDgpQCkH0cyB/3KMiBEJIgIECkHwEJgkECEpKSVKQe39CCjH0gTUbIWAsQcg8CZMw78TDlF76lowxdUSBXfONArrhC9pSnlbjMpS7rssuZzKWXPQHKL4HZEWESMyXDPKZHkqnMZjrzLnZ5pjSnSc1qWmQuzLSmQrCpzW5685vfjCY4x0nOcprznB4JCAAh+QQJCgBIACwAAAAAjABMAAAI/wCRCBxIsKDBgwgTKlzIsKHDhxAjSpxIsaLFixgzatzIsaPHjyBDihxJcmGiRCVTqsyIcqXLlzBjypxJs6bNmzgPtjR4MqfPn0CDCh1KtKjNnkaTPtyptKlToEyfShUYderTqlaNnkSJNGvTrl6dYg1bdCzZs2jTqvUpoa3bt3DjrnWZoq7du3jzMphb8oMeQYADCx5MOIUeviIlpOAGeNG2x5AjSx4HmFuVw4g/KgbMxQSCz6AhDSuCoMIw0NsoC7qcWXMKQYtMVAADGnSUcAiKRKmNYBEv1q07bv7cZTfvz9OSfw5HGgEU1vHiBdc4/Djvb3refY5y2jlrPeCnY/+sbv1zjAzmzFGZBgnS5+f3PqTvIUG8RfK1i5vPsGDBpB8egPbcF5P0l0F99jV0z4ILCoQfaBV0sV9/C7jwwzcYblAFGhQemGBDX9BAAwH3HKbHa7xVYEht51FYoYgictghgh8iZMQ95vSnBYP3oBiaJhWwyJ+LRLrooUGlwKCkkgSVsCQMKxD0JAwEgfBkCU0+GeVAUxK0wpVZLrmlQF0O9OWSTpRY4ALp0dCjILy5Vxow72hR5J0U2oGZQPb06eefgAYq6KCEFmrooYj6CQMIICgAIw0unINiFBLWZkgFetjZnzU62EEkEw/QoIN/eyLh5zWoXmPJn5akek0TrLr/Cqirq/rZaqqw2ppqrX02QWusuAKr6p++7trnDtAka8o5NKDYRZDHZUohBBkMWaEWTEBwj52TlMrGt+CGK+645JZr7rnopquuuejU9YmPtRWBGwKZ2rCBDV98IeMCPaChRb7ybCBPqVkUnMbBaTRQcMENIJwGCgtnUY3DEWfhsMILN4wwxAtPfHA1EaNwccQaH8xxwR6nAfLCIiOMMcMI9wEvaMPA8VmmV3TSCZ4UGtNJGaV+PMTQQztMNNFGH+1wNUcPkbTSCDe9tNRRH51yGlQLDfXBR8ssSDlSwNFdezdrkfPOX7jAZjzcUrGAz0ATBA44lahhtxrUzD133XdX/6I3ONTcrcbf4Aiet96B9/134nb/zbfdh8/NuBp+I3535HQbvrjdM0zxmiBQxAFtbR74u8EGC3yRSb73qPMFAR8sYIM8KdCIBORH5H4EGYITofsR7gj++xGCV/I773f7rnvwdw9f/O9E9P7742o4f7c70AtOxhEzuEADAxYApsQi5JdPvgUb9udCteyzX2EAtiMRxvxt1N+GH/PP74f9beRPP//+CwP/8Je//dkvgPzrn/8G6D8D1g+BAFyg/QiYv1XQQAtoIIAeXMHBDnqQg1VQhxZGSMISjlCDBvGDHwaBjRZiwwsqVKEXXIiNQcTQDzWg4Q1Z6EIYxnCGLrRhDP9z6MId0tCHMqShEFVIxBYasYc3PIEecrSAHZUIPDzK4hV5pAcJ6IFBCHGDGMdIxjKa8YxoTKMa18jGNqJxDlNcQAYOc49JmGMS9ziIHr6Qni+Axwg56kGpDMKIQhIkAoUs5BwIIoZEMiICBHGkGAgyB0cuciCNTGRBJElJSzLSkZtM5CQHUslECuEe+SKAQO5BgHxJxyB6oEK+WiAQI+SrA4Os0UPAEx4k8DKXAvklQXQwR2DqMiVgOeZLkqnMlTCzmdCcy1aQwJVpRjMk06zmM6/pEbNwEyTb/OZHwinOjpCznNREJzaj4k11TiSZ7XSnPHESz3lW5JnntKc+94kTFnjyUyP1/OdSBErQghr0oB0JCAAh+QQFCgAjACwAAAAAjABMAAAI/wBHCBxIsKDBgwgTKlzIsKHDhxAjSpxIsaLFixgzatzIsaPHjyBDihxJkmCikihTWjw5giVLlTBjHkz0UmBNmThz6tzJs6fPkTRn3vxJtKjRo0iTbgxqUqlTiC5tPt05dOXUnkyval2YdatXg12/ih07lmZQs2bJql27NSzbqW7fOo0rN2nViBLy6t3Lt29dmfGqCB5MuLBhBvH+pmSQQpAgKJAjS54M2XEVBopLSmjseBGCz6BDi37lWFAVPZlHbnb8SvRnSL0qIKjQK/Q2y6hTh1z9ahuYKK4rGEJgSHboV1BO697d+HOFLq4/e/j2zTmYz8lR37u3vOPq6KGnEf/68mXaNjrAEWT/QL5b943fwX+OkWGBOT3TQie/92HBggwSvCeRHgQSKFB8osExzHz12UdDddhVQYM5/gEoYET3ZDBJBveghmBoRRhHn38LaKHFDyimYIcWJFp44UP39KCFDhno0WFzocERTmgjkrhhBkCy2GKALzq03Tk6LEADFffg+NowshU3jR1okGjllf658EWRMN7zhX80NCkIeLTpISSWaC4wSW4ElQLDm28SVAKcMKxAEJ0wEAQCnSXISaedA+FJ0Ap8+gknoAIJOhChcPYpUCAdUphBc8PAEZ2ZJCZC45UQWIPpmgTZI+qopJZq6qmopqrqqqy2eioMTtz/QwMNmTRXQRGXnqnIFw0u0EOVC9zDIqgDjXrNsddYQqolyF7TxLLNltqssqMyi+yz1SJLrahNTAvttd8mS2q32pJ6ATTQfCKma10YZ+YGV1wRJIkuzAgkvPKwOQIb/Pbr778AByzwwAQXbPDBBZvxSWNSbBMOrghEAR0CZl7RSSclJlkiheawaEwnZeibxchplJxGAyOP3IDJaaCQchbVsPxyFiyjnPLKJruccswlV/MyCjW/jHPJOo/Mcxo+pwy0yTarbHIfnL2ioGvvaGExxrzaJ+wCdvT3ccgE9TzE2GOzTDbZZp/NcjVnD5G22ia3vbbccZ99dBp0iw13yWdD/10aF5BERx899CzwhQTxxHMP4hL0R08GlxQEDjiVqGG5GtRMPnnll1eiOTjUXK7G5+CInrnmoXf+eeqWf8655adPzroanqN+eeyUm7665TNMsQlnUCgh/PDCu1JFD/6ZqPzyvhJgEOxHRH8EGaITIf0R7oh+/RGiV3I99ZdbL332l2/f/fVEVH/962qYf7k76ItOxhEzuABkBhbkr//++aeQyf0ADKDzDBKGArbhgG3wQwEL6AcEtmGBBnQgBMPgQAUusIEInKADHwjBCkIQgwfUoAQ7iEALMtAPa5iEfbTQIT0YgTxGKJAMvfSFDhDoHgT4AgE6hBA/+GEQ2AgiNvy84EMfekGI2BhEEf1QAyQuEYhCJGIRjyhEJRaxiUJ8IhKlaEQkWtGHWAyiFqO4RC/UIIUl2s4H9PAlw+lrBPHQQ4UCtDU7vJEgbsijHvfIxz768Y+ADKQgB0lIQGJjDdvZjkBstJ3EHCSRRLLRHQnCiEoSJAKVrOQcCCKGTDIiApTMpBgIMgdPbnIgncxkQTw5yoGUMpOnFEgqLRnKSrZSIK/U5Ag+kLjEDaSXCQGmQHzJpWIasyV3OaYyl8nMZi7nLsl0ZkagKc1qWvOa2JxLNLPJzW6+ZZvevAhdwrkStJCTI2gZ5zknos51shOc7oynPOdJz3ra857hDAgAOw==' lib0-0.2.93/testing.test.js000066400000000000000000000112221457563604600154420ustar00rootroot00000000000000import * as t from './testing.js' import * as math from './math.js' import * as buffer from './buffer.js' import * as map from './map.js' import * as promise from './promise.js' import * as error from './error.js' /* c8 ignore next */ export const nottestingNotTested = () => { t.assert(false, 'This test should not be executed because the name doesnt start with "test"') } export const testAssertTyping = () => { const q = Math.random() const x = q === 0.3 ? null : { a: 4 } // this should always be an object // t.assert(x.a === 4) - this will give a type error because the type is uncertain t.assert(x) t.assert(x.a === 4) // this works because x is asserted } /** * @param {t.TestCase} _tc */ export const testComparing = _tc => { t.compare({}, {}) t.compare({ a: 4 }, { a: 4 }, 'simple compare (object)') t.compare([1, 2], [1, 2], 'simple compare (array)') t.compare({ a: [1, 2] }, { a: [1, 2] }, 'simple compare nested') t.compare(new Set(['3', 1234]), new Set(['3', 1234]), 'compare Sets') const map1 = map.create() map1.set(1, 2) map1.set('x', {}) map1.set(98, 'tst') const map2 = new Map() map2.set(1, 2) map2.set('x', {}) map2.set(98, 'tst') t.compare(map1, map2, 'compare Maps') t.describe('The following errors are expected!') t.fails(() => { t.compare({ a: 4 }, { b: 5 }, 'childs are not equal') }) t.fails(() => { t.compare({ a: 4 }, { a: 5 }, 'childs are not equal') }) t.fails(() => { t.compare({ a: 4 }, null, 'childs are not equal') }) t.fails(() => { // @ts-ignore t.compare({ a: 4 }, [4], 'childs have different types') }) t.fails(() => { t.compare({ a: 4 }, { a: 4, b: 5 }, 'childs have different length (object)') }) t.fails(() => { t.compare([1], [1, 2]) // childs have different length (array) -- no message }) t.fails(() => { t.compare(buffer.createUint8ArrayFromLen(1), buffer.createUint8ArrayFromLen(2), 'Uint8Arrays have different length') }) t.fails(() => { t.compare(buffer.createUint8ArrayFromLen(1).buffer, buffer.createUint8ArrayFromLen(2).buffer, 'ArrayBuffer have different length') }) t.fails(() => { t.compareStrings('str1', 'str2', 'Strings comparison can fail') }) t.compareArrays([], [], 'Comparing empty arrays') t.fails(() => { t.compareArrays([1], [1, 2], 'Compare arrays with different length') }) t.fails(() => { t.compareArrays([1], [2]) // Compare different arrays -- no message }) t.compareObjects({ x: 1 }, { x: 1 }, 'comparing objects') t.fails(() => { t.compareObjects({}, { x: 1 }, 'compareObjects can fail') }) t.fails(() => { t.compareObjects({ x: 3 }, { x: 1 }) // Compare different objects -- no message }) t.fails(() => { t.compare({ x: undefined }, { y: 1 }, 'compare correctly handles undefined') }) t.fails(() => { t.compareObjects({ x: undefined }, { y: 1 }, 'compare correctly handles undefined') }) t.describe('Map fails') t.fails(() => { const m1 = new Map() m1.set(1, 2) const m2 = new Map() m2.set(1, 3) t.compare(m1, m2) // childs have different length (array) -- no message }) t.fails(() => { const m1 = new Map() m1.set(2, 2) const m2 = new Map() m2.set(1, 2) t.compare(m1, m2) // childs have different length (array) -- no message }) t.fails(() => { const m1 = new Map() m1.set(1, 2) const m2 = new Map() t.compare(m1, m2) // childs have different length (array) -- no message }) t.describe('Set fails') t.fails(() => { t.compare(new Set([1]), new Set([1, 2])) // childs have different length (array) -- no message }) t.fails(() => { t.compare(new Set([1]), new Set([2])) // childs have different length (array) -- no message }) } export const testFailing = async () => { t.fails(() => { t.fail('This fail is expected') }) await t.promiseRejected(() => promise.reject(error.create('should be rejected'))) t.fails(() => { t.fails(() => {}) }) await t.failsAsync(async () => { await t.failsAsync(async () => { }) }) await t.promiseRejected(() => t.promiseRejected(() => promise.resolve()) ) } export const testSkipping = () => { t.skip(false) t.assert(true) t.skip() /* c8 ignore next */ t.fail('should have skipped') } export const testAsync = async () => { await t.measureTimeAsync('time', () => promise.create(r => setTimeout(r))) await t.groupAsync('some description', () => promise.wait(1)) await t.promiseRejected(() => promise.reject(error.create('should be rejected'))) } export const testRepeatRepetition = () => { const arr = [] const n = 100 for (let i = 1; i <= n; i++) { arr.push(i) } t.assert(arr.reduce(math.add, 0) === (n + 1) * n / 2) } lib0-0.2.93/time.js000066400000000000000000000023161457563604600137510ustar00rootroot00000000000000/** * Utility module to work with time. * * @module time */ import * as metric from './metric.js' import * as math from './math.js' /** * Return current time. * * @return {Date} */ export const getDate = () => new Date() /** * Return current unix time. * * @return {number} */ export const getUnixTime = Date.now /** * Transform time (in ms) to a human readable format. E.g. 1100 => 1.1s. 60s => 1min. .001 => 10μs. * * @param {number} d duration in milliseconds * @return {string} humanized approximation of time */ export const humanizeDuration = d => { if (d < 60000) { const p = metric.prefix(d, -1) return math.round(p.n * 100) / 100 + p.prefix + 's' } d = math.floor(d / 1000) const seconds = d % 60 const minutes = math.floor(d / 60) % 60 const hours = math.floor(d / 3600) % 24 const days = math.floor(d / 86400) if (days > 0) { return days + 'd' + ((hours > 0 || minutes > 30) ? ' ' + (minutes > 30 ? hours + 1 : hours) + 'h' : '') } if (hours > 0) { /* c8 ignore next */ return hours + 'h' + ((minutes > 0 || seconds > 30) ? ' ' + (seconds > 30 ? minutes + 1 : minutes) + 'min' : '') } return minutes + 'min' + (seconds > 0 ? ' ' + seconds + 's' : '') } lib0-0.2.93/time.test.js000066400000000000000000000024151457563604600147270ustar00rootroot00000000000000import * as time from './time.js' import * as t from './testing.js' import * as math from './math.js' /** * @param {t.TestCase} tc */ export const testTime = tc => { const l = time.getDate().getTime() const r = time.getUnixTime() t.assert(math.abs(l - r) < 10, 'Times generated are roughly the same') } /** * @param {t.TestCase} tc */ export const testHumanDuration = tc => { t.assert(time.humanizeDuration(10) === '10ms') t.assert(time.humanizeDuration(0.1) === '100μs') t.assert(time.humanizeDuration(61030) === '1min 1s') t.assert(time.humanizeDuration(60030) === '1min') t.assert(time.humanizeDuration(3600000) === '1h') t.assert(time.humanizeDuration(3640000) === '1h 1min') t.assert(time.humanizeDuration(3700000) === '1h 2min') t.assert(time.humanizeDuration(60 * 60 * 1000 + 29000) === '1h') t.assert(time.humanizeDuration(60 * 60 * 1000 + 31000) === '1h 1min') t.assert(time.humanizeDuration(60 * 60 * 1000 + 31000 * 3) === '1h 2min') t.assert(time.humanizeDuration(3600000 * 25) === '1d 1h') t.assert(time.humanizeDuration(3600000 * 24.6) === '1d 1h') t.assert(time.humanizeDuration(3600000 * 25.6) === '1d 2h') t.assert(time.humanizeDuration(3600000 * 24 * 400) === '400d') // test round t.assert(time.humanizeDuration(6001) === '6s') } lib0-0.2.93/tree.js000066400000000000000000000276261457563604600137650ustar00rootroot00000000000000/** * Red-black-tree implementation. * * @module tree */ // @ts-nocheck TODO: remove or refactor this file const rotate = (tree, parent, newParent, n) => { if (parent === null) { tree.root = newParent newParent._parent = null } else if (parent.left === n) { parent.left = newParent } else if (parent.right === n) { parent.right = newParent } else { throw new Error('The elements are wrongly connected!') } } /** * @template V */ class N { /** * A created node is always red! * * @param {V} val */ constructor (val) { this.val = val this.color = true this._left = null this._right = null this._parent = null } isRed () { return this.color } isBlack () { return !this.color } redden () { this.color = true; return this } blacken () { this.color = false; return this } get grandparent () { return this.parent.parent } get parent () { return this._parent } get sibling () { return (this === this.parent.left) ? this.parent.right : this.parent.left } get left () { return this._left } get right () { return this._right } set left (n) { if (n !== null) { n._parent = this } this._left = n } set right (n) { if (n !== null) { n._parent = this } this._right = n } rotateLeft (tree) { const parent = this.parent const newParent = this.right const newRight = this.right.left newParent.left = this this.right = newRight rotate(tree, parent, newParent, this) } next () { if (this.right !== null) { // search the most left node in the right tree let o = this.right while (o.left !== null) { o = o.left } return o } else { let p = this while (p.parent !== null && p !== p.parent.left) { p = p.parent } return p.parent } } prev () { if (this.left !== null) { // search the most right node in the left tree let o = this.left while (o.right !== null) { o = o.right } return o } else { let p = this while (p.parent !== null && p !== p.parent.right) { p = p.parent } return p.parent } } rotateRight (tree) { const parent = this.parent const newParent = this.left const newLeft = this.left.right newParent.right = this this.left = newLeft rotate(tree, parent, newParent, this) } getUncle () { // we can assume that grandparent exists when this is called! if (this.parent === this.parent.parent.left) { return this.parent.parent.right } else { return this.parent.parent.left } } } const isBlack = node => node !== null ? node.isBlack() : true const isRed = (node) => node !== null ? node.isRed() : false /** * This is a Red Black Tree implementation * * @template K,V */ export class Tree { constructor () { this.root = null this.length = 0 } /** * @param {K} id */ findNext (id) { const nextID = id.clone() nextID.clock += 1 return this.findWithLowerBound(nextID) } /** * @param {K} id */ findPrev (id) { const prevID = id.clone() prevID.clock -= 1 return this.findWithUpperBound(prevID) } /** * @param {K} from */ findNodeWithLowerBound (from) { let o = this.root if (o === null) { return null } else { while (true) { if (from === null || (from.lessThan(o.val._id) && o.left !== null)) { // o is included in the bound // try to find an element that is closer to the bound o = o.left } else if (from !== null && o.val._id.lessThan(from)) { // o is not within the bound, maybe one of the right elements is.. if (o.right !== null) { o = o.right } else { // there is no right element. Search for the next bigger element, // this should be within the bounds return o.next() } } else { return o } } } } /** * @param {K} to */ findNodeWithUpperBound (to) { if (to === undefined) { throw new Error('You must define from!') } let o = this.root if (o === null) { return null } else { while (true) { if ((to === null || o.val._id.lessThan(to)) && o.right !== null) { // o is included in the bound // try to find an element that is closer to the bound o = o.right } else if (to !== null && to.lessThan(o.val._id)) { // o is not within the bound, maybe one of the left elements is.. if (o.left !== null) { o = o.left } else { // there is no left element. Search for the prev smaller element, // this should be within the bounds return o.prev() } } else { return o } } } } /** * @return {V} */ findSmallestNode () { let o = this.root while (o != null && o.left != null) { o = o.left } return o } /** * @param {K} from * @return {V} */ findWithLowerBound (from) { const n = this.findNodeWithLowerBound(from) return n == null ? null : n.val } /** * @param {K} to * @return {V} */ findWithUpperBound (to) { const n = this.findNodeWithUpperBound(to) return n == null ? null : n.val } /** * @param {K} from * @param {V} from * @param {function(V):void} f */ iterate (from, to, f) { let o if (from === null) { o = this.findSmallestNode() } else { o = this.findNodeWithLowerBound(from) } while ( o !== null && ( to === null || // eslint-disable-line no-unmodified-loop-condition o.val._id.lessThan(to) || o.val._id.equals(to) ) ) { f(o.val) o = o.next() } } /** * @param {K} id * @return {V|null} */ find (id) { const n = this.findNode(id) if (n !== null) { return n.val } else { return null } } /** * @param {K} id * @return {N|null} */ findNode (id) { let o = this.root if (o === null) { return null } else { while (true) { if (o === null) { return null } if (id.lessThan(o.val._id)) { o = o.left } else if (o.val._id.lessThan(id)) { o = o.right } else { return o } } } } /** * @param {K} id */ delete (id) { let d = this.findNode(id) if (d == null) { // throw new Error('Element does not exist!') return } this.length-- if (d.left !== null && d.right !== null) { // switch d with the greates element in the left subtree. // o should have at most one child. let o = d.left // find while (o.right !== null) { o = o.right } // switch d.val = o.val d = o } // d has at most one child // let n be the node that replaces d let isFakeChild let child = d.left || d.right if (child === null) { isFakeChild = true child = new N(null) child.blacken() d.right = child } else { isFakeChild = false } if (d.parent === null) { if (!isFakeChild) { this.root = child child.blacken() child._parent = null } else { this.root = null } return } else if (d.parent.left === d) { d.parent.left = child } else if (d.parent.right === d) { d.parent.right = child } else { throw new Error('Impossible!') } if (d.isBlack()) { if (child.isRed()) { child.blacken() } else { this._fixDelete(child) } } this.root.blacken() if (isFakeChild) { if (child.parent.left === child) { child.parent.left = null } else if (child.parent.right === child) { child.parent.right = null } else { throw new Error('Impossible #3') } } } _fixDelete (n) { if (n.parent === null) { // this can only be called after the first iteration of fixDelete. return } // d was already replaced by the child // d is not the root // d and child are black let sibling = n.sibling if (isRed(sibling)) { // make sibling the grandfather n.parent.redden() sibling.blacken() if (n === n.parent.left) { n.parent.rotateLeft(this) } else if (n === n.parent.right) { n.parent.rotateRight(this) } else { throw new Error('Impossible #2') } sibling = n.sibling } // parent, sibling, and children of n are black if (n.parent.isBlack() && sibling.isBlack() && isBlack(sibling.left) && isBlack(sibling.right) ) { sibling.redden() this._fixDelete(n.parent) } else if (n.parent.isRed() && sibling.isBlack() && isBlack(sibling.left) && isBlack(sibling.right) ) { sibling.redden() n.parent.blacken() } else { if (n === n.parent.left && sibling.isBlack() && isRed(sibling.left) && isBlack(sibling.right) ) { sibling.redden() sibling.left.blacken() sibling.rotateRight(this) sibling = n.sibling } else if (n === n.parent.right && sibling.isBlack() && isRed(sibling.right) && isBlack(sibling.left) ) { sibling.redden() sibling.right.blacken() sibling.rotateLeft(this) sibling = n.sibling } sibling.color = n.parent.color n.parent.blacken() if (n === n.parent.left) { sibling.right.blacken() n.parent.rotateLeft(this) } else { sibling.left.blacken() n.parent.rotateRight(this) } } } put (v) { const node = new N(v) if (this.root !== null) { let p = this.root // p abbrev. parent while (true) { if (node.val._id.lessThan(p.val._id)) { if (p.left === null) { p.left = node break } else { p = p.left } } else if (p.val._id.lessThan(node.val._id)) { if (p.right === null) { p.right = node break } else { p = p.right } } else { p.val = node.val return p } } this._fixInsert(node) } else { this.root = node } this.length++ this.root.blacken() return node } _fixInsert (n) { if (n.parent === null) { n.blacken() return } else if (n.parent.isBlack()) { return } const uncle = n.getUncle() if (uncle !== null && uncle.isRed()) { // Note: parent: red, uncle: red n.parent.blacken() uncle.blacken() n.grandparent.redden() this._fixInsert(n.grandparent) } else { // Note: parent: red, uncle: black or null // Now we transform the tree in such a way that // either of these holds: // 1) grandparent.left.isRed // and grandparent.left.left.isRed // 2) grandparent.right.isRed // and grandparent.right.right.isRed if (n === n.parent.right && n.parent === n.grandparent.left) { n.parent.rotateLeft(this) // Since we rotated and want to use the previous // cases, we need to set n in such a way that // n.parent.isRed again n = n.left } else if (n === n.parent.left && n.parent === n.grandparent.right) { n.parent.rotateRight(this) // see above n = n.right } // Case 1) or 2) hold from here on. // Now traverse grandparent, make parent a black node // on the highest level which holds two red nodes. n.parent.blacken() n.grandparent.redden() if (n === n.parent.left) { // Case 1 n.grandparent.rotateRight(this) } else { // Case 2 n.grandparent.rotateLeft(this) } } } } lib0-0.2.93/tree.test.js000066400000000000000000000126571457563604600147410ustar00rootroot00000000000000/* import { Tree as RedBlackTree } from 'lib0/Tree.js' import * as ID from '../utils/ID.js' import { test, proxyConsole } from 'cutest' import * as random from 'lib0/prng.js' proxyConsole() var numberOfRBTreeTests = 10000 const checkRedNodesDoNotHaveBlackChildren = (t, tree) => { let correct = true const traverse = n => { if (n == null) { return } if (n.isRed()) { if (n.left != null) { correct = correct && !n.left.isRed() } if (n.right != null) { correct = correct && !n.right.isRed() } } traverse(n.left) traverse(n.right) } traverse(tree.root) t.assert(correct, 'Red nodes do not have black children') } const checkBlackHeightOfSubTreesAreEqual = (t, tree) => { let correct = true const traverse = n => { if (n == null) { return 0 } var sub1 = traverse(n.left) var sub2 = traverse(n.right) if (sub1 !== sub2) { correct = false } if (n.isRed()) { return sub1 } else { return sub1 + 1 } } traverse(tree.root) t.assert(correct, 'Black-height of sub-trees are equal') } const checkRootNodeIsBlack = (t, tree) => { t.assert(tree.root == null || tree.root.isBlack(), 'root node is black') } test('RedBlack Tree', async function redBlackTree (t) { let tree = new RedBlackTree() tree.put({_id: ID.createID(8433, 0)}) tree.put({_id: ID.createID(12844, 0)}) tree.put({_id: ID.createID(1795, 0)}) tree.put({_id: ID.createID(30302, 0)}) tree.put({_id: ID.createID(64287)}) tree.delete(ID.createID(8433, 0)) tree.put({_id: ID.createID(28996)}) tree.delete(ID.createID(64287)) tree.put({_id: ID.createID(22721)}) checkRootNodeIsBlack(t, tree) checkBlackHeightOfSubTreesAreEqual(t, tree) checkRedNodesDoNotHaveBlackChildren(t, tree) }) test(`random tests (${numberOfRBTreeTests})`, async function randomRBTree (t) { let prng = random.createPRNG(t.getSeed() * 1000000000) let tree = new RedBlackTree() let elements = [] for (var i = 0; i < numberOfRBTreeTests; i++) { if (random.int31(prng, 0, 100) < 80) { // 80% chance to insert an element let obj = ID.createID(random.int31(prng, 0, numberOfRBTreeTests), random.int31(prng, 0, 1)) let nodeExists = tree.find(obj) if (nodeExists === null) { if (elements.some(e => e.equals(obj))) { t.assert(false, 'tree and elements contain different results') } elements.push(obj) tree.put({_id: obj}) } } else if (elements.length > 0) { // ~20% chance to delete an element var elem = random.oneOf(prng, elements) elements = elements.filter(e => { return !e.equals(elem) }) tree.delete(elem) } } checkRootNodeIsBlack(t, tree) checkBlackHeightOfSubTreesAreEqual(t, tree) checkRedNodesDoNotHaveBlackChildren(t, tree) // TEST if all nodes exist let allNodesExist = true for (let id of elements) { let node = tree.find(id) if (!node._id.equals(id)) { allNodesExist = false } } t.assert(allNodesExist, 'All inserted nodes exist') // TEST lower bound search let findAllNodesWithLowerBoundSerach = true for (let id of elements) { let node = tree.findWithLowerBound(id) if (!node._id.equals(id)) { findAllNodesWithLowerBoundSerach = false } } t.assert( findAllNodesWithLowerBoundSerach, 'Find every object with lower bound search' ) // TEST iteration (with lower bound search) let lowerBound = random.oneOf(prng, elements) let expectedResults = elements.filter((e, pos) => (lowerBound.lessThan(e) || e.equals(lowerBound)) && elements.indexOf(e) === pos ).length let actualResults = 0 tree.iterate(lowerBound, null, val => { if (val == null) { t.assert(false, 'val is undefined!') } actualResults++ }) t.assert( expectedResults === actualResults, 'Iterating over a tree with lower bound yields the right amount of results' ) expectedResults = elements.filter((e, pos) => elements.indexOf(e) === pos ).length actualResults = 0 tree.iterate(null, null, val => { if (val == null) { t.assert(false, 'val is undefined!') } actualResults++ }) t.assert( expectedResults === actualResults, 'iterating over a tree without bounds yields the right amount of results' ) let upperBound = random.oneOf(prng, elements) expectedResults = elements.filter((e, pos) => (e.lessThan(upperBound) || e.equals(upperBound)) && elements.indexOf(e) === pos ).length actualResults = 0 tree.iterate(null, upperBound, val => { if (val == null) { t.assert(false, 'val is undefined!') } actualResults++ }) t.assert( expectedResults === actualResults, 'iterating over a tree with upper bound yields the right amount of results' ) upperBound = random.oneOf(prng, elements) lowerBound = random.oneOf(prng, elements) if (upperBound.lessThan(lowerBound)) { [lowerBound, upperBound] = [upperBound, lowerBound] } expectedResults = elements.filter((e, pos) => (lowerBound.lessThan(e) || e.equals(lowerBound)) && (e.lessThan(upperBound) || e.equals(upperBound)) && elements.indexOf(e) === pos ).length actualResults = 0 tree.iterate(lowerBound, upperBound, val => { if (val == null) { t.assert(false, 'val is undefined!') } actualResults++ }) t.assert( expectedResults === actualResults, 'iterating over a tree with upper bound yields the right amount of results' ) }) */ lib0-0.2.93/tsconfig.json000066400000000000000000000012421457563604600151610ustar00rootroot00000000000000{ "compilerOptions": { "target": "ES2022", "lib": ["ES2022", "dom"], "module": "ES2022", "allowJs": true, "checkJs": true, "declaration": true, "declarationMap": true, "outDir": "./dist", "baseUrl": "./", "rootDir": "./", "emitDeclarationOnly": true, "strict": true, "noImplicitAny": true, "moduleResolution": "nodenext", "paths": { "lib0/*": ["./*"], "lib0/webcrypto": ["./webcrypto.js"], "lib0/performance": ["./performance.node.js"], "lib0/logging": ["./logging.node.js"] } }, "include": ["./*.js", "./hash/*.js", "./crypto/*.js", "./bin/*.js"], "exclude": ["./dist/**/*"] } lib0-0.2.93/url.js000066400000000000000000000015531457563604600136170ustar00rootroot00000000000000/** * Utility module to work with urls. * * @module url */ import * as object from './object.js' /** * Parse query parameters from an url. * * @param {string} url * @return {Object} */ export const decodeQueryParams = url => { /** * @type {Object} */ const query = {} const urlQuerySplit = url.split('?') const pairs = urlQuerySplit[urlQuerySplit.length - 1].split('&') for (let i = 0; i < pairs.length; i++) { const item = pairs[i] if (item.length > 0) { const pair = item.split('=') query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '') } } return query } /** * @param {Object} params * @return {string} */ export const encodeQueryParams = params => object.map(params, (val, key) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&') lib0-0.2.93/url.test.js000066400000000000000000000016721457563604600145770ustar00rootroot00000000000000import * as t from './testing.js' import * as url from './url.js' /** * @param {Object} params */ const paramTest = params => { const out = url.decodeQueryParams(url.encodeQueryParams(params)) t.compareObjects(params, out, 'Compare params') } /** * @param {t.TestCase} tc */ export const testUrlParamQuery = tc => { paramTest({}) paramTest({ a: '4' }) paramTest({ a: 'dtrn', b: '0x0' }) t.compareObjects({ }, url.decodeQueryParams('http://localhost:8080/dtrn?')) t.compareObjects({ a: 'ay' }, url.decodeQueryParams('http://localhost:8080/dtrn?a=ay')) t.compareObjects({ a: '' }, url.decodeQueryParams('http://localhost:8080/dtrn?a=')) t.compareObjects({ a: '' }, url.decodeQueryParams('http://localhost:8080/dtrn?a')) t.compareObjects({ a: 'ay' }, url.decodeQueryParams('http://localhost:8080/dtrn?a=ay&')) t.compareObjects({ a: 'ay', b: 'bey' }, url.decodeQueryParams('http://localhost:8080/dtrn?a=ay&b=bey')) } lib0-0.2.93/webcrypto.deno.js000066400000000000000000000003111457563604600157460ustar00rootroot00000000000000// eslint-disable-next-line export const subtle = /** @type {any} */ (crypto).subtle // eslint-disable-next-line export const getRandomValues = /** @type {any} */ (crypto).getRandomValues.bind(crypto) lib0-0.2.93/webcrypto.js000066400000000000000000000002011457563604600150200ustar00rootroot00000000000000/* eslint-env browser */ export const subtle = crypto.subtle export const getRandomValues = crypto.getRandomValues.bind(crypto) lib0-0.2.93/webcrypto.node.js000066400000000000000000000003031457563604600157470ustar00rootroot00000000000000import { webcrypto } from 'node:crypto' export const subtle = /** @type {any} */ (webcrypto).subtle export const getRandomValues = /** @type {any} */ (webcrypto).getRandomValues.bind(webcrypto) lib0-0.2.93/websocket.js000066400000000000000000000103011457563604600147720ustar00rootroot00000000000000/* eslint-env browser */ /** * Tiny websocket connection handler. * * Implements exponential backoff reconnects, ping/pong, and a nice event system using [lib0/observable]. * * @module websocket */ import { Observable } from './observable.js' import * as time from './time.js' import * as math from './math.js' const reconnectTimeoutBase = 1200 const maxReconnectTimeout = 2500 // @todo - this should depend on awareness.outdatedTime const messageReconnectTimeout = 30000 /** * @param {WebsocketClient} wsclient */ const setupWS = (wsclient) => { if (wsclient.shouldConnect && wsclient.ws === null) { const websocket = new WebSocket(wsclient.url) const binaryType = wsclient.binaryType /** * @type {any} */ let pingTimeout = null if (binaryType) { websocket.binaryType = binaryType } wsclient.ws = websocket wsclient.connecting = true wsclient.connected = false websocket.onmessage = event => { wsclient.lastMessageReceived = time.getUnixTime() const data = event.data const message = typeof data === 'string' ? JSON.parse(data) : data if (message && message.type === 'pong') { clearTimeout(pingTimeout) pingTimeout = setTimeout(sendPing, messageReconnectTimeout / 2) } wsclient.emit('message', [message, wsclient]) } /** * @param {any} error */ const onclose = error => { if (wsclient.ws !== null) { wsclient.ws = null wsclient.connecting = false if (wsclient.connected) { wsclient.connected = false wsclient.emit('disconnect', [{ type: 'disconnect', error }, wsclient]) } else { wsclient.unsuccessfulReconnects++ } // Start with no reconnect timeout and increase timeout by // log10(wsUnsuccessfulReconnects). // The idea is to increase reconnect timeout slowly and have no reconnect // timeout at the beginning (log(1) = 0) setTimeout(setupWS, math.min(math.log10(wsclient.unsuccessfulReconnects + 1) * reconnectTimeoutBase, maxReconnectTimeout), wsclient) } clearTimeout(pingTimeout) } const sendPing = () => { if (wsclient.ws === websocket) { wsclient.send({ type: 'ping' }) } } websocket.onclose = () => onclose(null) websocket.onerror = error => onclose(error) websocket.onopen = () => { wsclient.lastMessageReceived = time.getUnixTime() wsclient.connecting = false wsclient.connected = true wsclient.unsuccessfulReconnects = 0 wsclient.emit('connect', [{ type: 'connect' }, wsclient]) // set ping pingTimeout = setTimeout(sendPing, messageReconnectTimeout / 2) } } } /** * @deprecated * @extends Observable */ export class WebsocketClient extends Observable { /** * @param {string} url * @param {object} opts * @param {'arraybuffer' | 'blob' | null} [opts.binaryType] Set `ws.binaryType` */ constructor (url, { binaryType } = {}) { super() this.url = url /** * @type {WebSocket?} */ this.ws = null this.binaryType = binaryType || null this.connected = false this.connecting = false this.unsuccessfulReconnects = 0 this.lastMessageReceived = 0 /** * Whether to connect to other peers or not * @type {boolean} */ this.shouldConnect = true this._checkInterval = setInterval(() => { if (this.connected && messageReconnectTimeout < time.getUnixTime() - this.lastMessageReceived) { // no message received in a long time - not even your own awareness // updates (which are updated every 15 seconds) /** @type {WebSocket} */ (this.ws).close() } }, messageReconnectTimeout / 2) setupWS(this) } /** * @param {any} message */ send (message) { if (this.ws) { this.ws.send(JSON.stringify(message)) } } destroy () { clearInterval(this._checkInterval) this.disconnect() super.destroy() } disconnect () { this.shouldConnect = false if (this.ws !== null) { this.ws.close() } } connect () { this.shouldConnect = true if (!this.connected && this.ws === null) { setupWS(this) } } }