From f86343155094855b4e3d7e51f6654635d0aa09ee Mon Sep 17 00:00:00 2001 From: Kacper Kula Date: Sat, 26 Mar 2022 13:11:04 +0000 Subject: [PATCH] release 0.0.16: MIDI Clock messages. --- README.md | 12 ++- package.json | 14 +-- src/MIDIValInput.ts | 161 +++++++++++++++++++++++--------- src/MIDIValOutput.ts | 24 +++++ src/errors.ts | 13 +++ src/index.ts | 9 +- src/types/messages.ts | 46 +++++++++ src/utils/MIDIMessageConvert.ts | 6 +- src/utils/clock.test.ts | 25 +++++ src/utils/clock.ts | 27 ++++++ src/utils/midiCommands.ts | 6 ++ yarn.lock | 100 ++++++++++---------- 12 files changed, 333 insertions(+), 110 deletions(-) create mode 100644 src/errors.ts create mode 100644 src/types/messages.ts create mode 100644 src/utils/clock.test.ts create mode 100644 src/utils/clock.ts diff --git a/README.md b/README.md index b860e0c..dbdce6d 100644 --- a/README.md +++ b/README.md @@ -181,13 +181,17 @@ You can listen to all notes off input.onAllNotesOff(() => { }); ``` +### MIDI Clock +You can listen to MIDI Clock messages: +- `onClockStart` +- `onClockStop` +- `onClockContinue` +- `onClockPulse` - sent 24 times every quarternote. + ### To Be Added The following features are planned to be added to MIDI Input: -- Omni Mode On -- Omni Mode Off -- Mode Mode On -- Poly On - Better support for sysex +- Better documentation ### Disconnect diff --git a/package.json b/package.json index ccad6c2..593e2bf 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,18 @@ { "name": "@midival/core", - "version": "0.0.15", + "version": "0.0.16", "main": "dist/index.js", "types": "dist/index.d.ts", "license": "MIT", "devDependencies": { - "@types/jest": "^27.4.0", + "@types/jest": "^27.4.1", "@types/webmidi": "^2.0.6", "gh-pages": "^3.2.3", "jest": "^27.5.1", - "prettier": "^2.5.1", - "ts-jest": "^27.1.3", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "prettier": "^2.6.1", + "ts-jest": "^27.1.4", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" }, "scripts": { "compile": "tsc", @@ -23,6 +23,6 @@ "deploy": "gh-pages -d docs" }, "dependencies": { - "@hypersphere/omnibus": "^0.0.2" + "@hypersphere/omnibus": "^0.0.6" } } diff --git a/src/MIDIValInput.ts b/src/MIDIValInput.ts index d155ff0..7878c41 100644 --- a/src/MIDIValInput.ts +++ b/src/MIDIValInput.ts @@ -10,39 +10,10 @@ import {IMIDIAccess} from "./wrappers/access/IMIDIAccess"; import { splitValueIntoFraction } from "./utils/pitchBend"; import { MidiCommand } from "./utils/midiCommands"; import { MidiControlChange } from "./utils/midiControlChanges"; +import { ticksToBPM } from "./utils/clock"; +import { MIDIValConfigurationError, MIDIValError } from "./errors"; +import { ControlChangeMessage, NoteMessage, ProgramChangeMessage, toControlChangeMessage, toNoteMessage, toProgramMessage } from "./types/messages"; -interface NoteMessage extends MidiMessage { - note: number; - velocity: number; -} - -const toNoteMessage = (m: MidiMessage): NoteMessage => ({ - ...m, - note: m.data1, - velocity: m.data2, -}); - -interface ControlChangeMessage extends MidiMessage { - control: number, - value: number, -} - -const toControlChangeMessage = (m: MidiMessage): ControlChangeMessage => ({ - ...m, - control: m.data1, - value: m.data2, -}); - -interface ProgramMessage extends MidiMessage { - program: number, - value: number, -} - -const toProgramMessage = (m: MidiMessage): ProgramMessage => ({ - ...m, - program: m.data1, - value: m.data2, -}); interface EventDefinitions { 'pitchBend': [number], @@ -51,42 +22,72 @@ interface EventDefinitions { 'noteOn': [NoteMessage], 'noteOff': [NoteMessage], 'controlChange': [ControlChangeMessage], - 'programChange': [ProgramMessage], + 'programChange': [ProgramChangeMessage], 'polyKeyPressure': [MidiMessage], + + 'clockPulse': [], + 'clockStart': [], + 'clockStop': [], + 'clockContinue': [], +} + +const TEMPO_SAMPLES_LIMIT = 20; + +/** + * MIDIVal Input Configuration Options + */ +export interface MIDIValInputOptions { + computeClockTempo: boolean, } +const DefaultOptions: MIDIValInputOptions = { + computeClockTempo: false, +}; + export class MIDIValInput { private unregisterInput: UnregisterCallback; private omnibus: Omnibus; private midiInput: IMIDIInput; - constructor(input: IMIDIInput) { + private tempoSamples: number[]; + private options: MIDIValInputOptions + + constructor(input: IMIDIInput, options: MIDIValInputOptions = DefaultOptions) { this.omnibus = new Omnibus(); + this.tempoSamples = []; this.registerInput(input); + this.options = options; } /** * Returns new MIDIValInput object based on the interface id. * @param interfaceId id of the interface from the MIDIAcces object. + * @throws MIDIValError when interface id is not found. * @returns Promise resolving to MIDIValInput. */ - static async fromInterfaceId(interfaceId: string): Promise { + static async fromInterfaceId(interfaceId: string, options?: MIDIValInputOptions): Promise { const midiAccess = await this.getMidiAccess(); const input = midiAccess.inputs.find(({ id }) => id === interfaceId); if (!input) { - throw new Error(`${interfaceId} not found`); + throw new MIDIValError(`${interfaceId} not found`); } - return new MIDIValInput(input); + return new MIDIValInput(input, options); } - - static async fromInterfaceName(interfaceName: string): Promise { +/** + * Finds first interface matching the name + * @param interfaceName interface Name + * @param options input configuration options + * @throws MIDIValError when no interface with that name is found + * @returns MIDIValInput object + */ + static async fromInterfaceName(interfaceName: string, options?: MIDIValInputOptions): Promise { const midiAccess = await this.getMidiAccess(); const input = midiAccess.inputs.find(({ name }) => name === interfaceName); if (!input) { - throw new Error(`${interfaceName} not found`); + throw new MIDIValError(`${interfaceName} not found`); } - return new MIDIValInput(input); + return new MIDIValInput(input, options); } private static async getMidiAccess(): Promise { @@ -94,6 +95,18 @@ export class MIDIValInput { return midiAccess; } + /** + * Current MIDI Clock tempo + * @throws MIDIValConfigurationError when computeClockTempo is not on. + * @returns current tempo in BPM. + */ + public get tempo(): number { + if (!this.options.computeClockTempo) { + throw new MIDIValConfigurationError("To use MIDIValInput.tempo you need to enable computeClockTempo option."); + } + return ticksToBPM(this.tempoSamples); + } + private async registerInput(input: IMIDIInput): Promise { this.midiInput = input; this.unregisterInput = await input.onMessage( @@ -103,6 +116,9 @@ export class MIDIValInput { this.omnibus.trigger('sysex', e.data); return; } + if (this.isClockCommand(e)) { + return; + } const midiMessage = toMidiMessage(e.data); switch (midiMessage.command) { case MidiCommand.NoteOn: @@ -125,14 +141,55 @@ export class MIDIValInput { break; default: // TODO: Unknown message. + console.log('unknown msg', midiMessage); break; } } ); + + if (this.options.computeClockTempo) { + this.onClockPulse(() => { + // compute time + this.tempoSamples.push(performance.now()); + if (this.tempoSamples.length > TEMPO_SAMPLES_LIMIT) { + this.tempoSamples.shift(); + } + }); + + const resetSamples = () => { + this.tempoSamples = []; + }; + + this.onClockContinue(resetSamples); + this.onClockStart(resetSamples); + } + } + + private isClockCommand(e: WebMidi.MIDIMessageEvent): boolean { + switch (e.data[0]) { + case MidiCommand.Clock.Pulse: + this.omnibus.trigger('clockPulse'); + return true; + case MidiCommand.Clock.Start: + this.omnibus.trigger('clockStart'); + return true; + case MidiCommand.Clock.Continue: + this.omnibus.trigger('clockContinue'); + return true; + case MidiCommand.Clock.Stop: + this.omnibus.trigger('clockStop'); + return true; + default: + return false; + } } private onBusKeyValue(event: K, key: keyof EventDefinitions[K][0], value: EventDefinitions[K][0][keyof EventDefinitions[K][0]], callback: (obj: EventDefinitions[K][0]) => void) { - return this.omnibus.on(event, (obj) => { + return this.omnibus.on(event, (...args) => { + if (!args.length) { + return; + } + const obj: EventDefinitions[K][0] = args[0]; // FIXME: how to do it so we have multiple args? if (obj[key] === value) { callback(obj); @@ -237,7 +294,7 @@ export class MIDIValInput { * @param callback Callback to be called * @returns Unregister function. */ - onAllProgramChange(callback: CallbackType<[ProgramMessage]>): UnregisterCallback { + onAllProgramChange(callback: CallbackType<[ProgramChangeMessage]>): UnregisterCallback { return this.omnibus.on('programChange', callback); } @@ -247,7 +304,7 @@ export class MIDIValInput { * @param callback Callback to be called * @returns Unregister function */ - onProgramChange(program: number, callback: CallbackType<[ProgramMessage]>): UnregisterCallback { + onProgramChange(program: number, callback: CallbackType<[ProgramChangeMessage]>): UnregisterCallback { return this.onBusKeyValue('programChange', 'program', program, callback); } @@ -332,4 +389,20 @@ export class MIDIValInput { onPolyModeOn(callback: CallbackType<[MidiMessage]>): UnregisterCallback { return this.onBusKeyValue('controlChange', 'control', MidiControlChange.PolyModeOn, callback); } + + onClockPulse(callback: CallbackType<[]>): UnregisterCallback { + return this.omnibus.on('clockPulse', callback); + } + + onClockStart(callback: CallbackType<[]>): UnregisterCallback { + return this.omnibus.on('clockStart', callback); + } + + onClockStop(callback: CallbackType<[]>): UnregisterCallback { + return this.omnibus.on('clockStop', callback); + } + + onClockContinue(callback: CallbackType<[]>): UnregisterCallback { + return this.omnibus.on('clockContinue', callback); + } } diff --git a/src/MIDIValOutput.ts b/src/MIDIValOutput.ts index cc8524c..9ee590c 100644 --- a/src/MIDIValOutput.ts +++ b/src/MIDIValOutput.ts @@ -178,4 +178,28 @@ export class MIDIValOutput { 0, ]); } + + sendClockStart(): void { + return this.send([ + MidiCommand.Clock.Start, + ]); + } + + sendClockStop(): void { + return this.send([ + MidiCommand.Clock.Stop, + ]) + } + + sendClockContinue(): void { + return this.send([ + MidiCommand.Clock.Continue, + ]); + } + + sendClockPulse(): void { + return this.send([ + MidiCommand.Clock.Pulse, + ]); + } } diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 0000000..06ffe49 --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,13 @@ +export class MIDIValError extends Error { + constructor(message: string) { + super(message); + } + + get name() { + return this.constructor.name; + } +} + +export class MIDIValConfigurationError extends MIDIValError { + +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index a6e9dca..aa8917b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,11 @@ import {MIDIVal} from "./MIDIval"; -import {MIDIValInput} from "./MIDIValInput"; +import {MIDIValInput, MIDIValInputOptions} from "./MIDIValInput"; import {MIDIValOutput} from "./MIDIValOutput"; import {IMIDIInput} from "./wrappers/inputs/IMIDIInput"; import {IMIDIOutput} from "./wrappers/outputs/IMIDIOutput"; import {IMIDIAccess} from "./wrappers/access/IMIDIAccess"; +import { ControlChangeMessage, NoteMessage, ProgramChangeMessage } from "./types/messages"; +import { MidiMessage } from "./utils/MIDIMessageConvert"; import { CallbackType, UnregisterCallback } from "@hypersphere/omnibus"; export { @@ -15,4 +17,9 @@ export { IMIDIAccess, CallbackType as Callback, UnregisterCallback, + MIDIValInputOptions, + ControlChangeMessage, + NoteMessage, + ProgramChangeMessage, + MidiMessage, }; \ No newline at end of file diff --git a/src/types/messages.ts b/src/types/messages.ts new file mode 100644 index 0000000..3c589fa --- /dev/null +++ b/src/types/messages.ts @@ -0,0 +1,46 @@ +import { MidiMessage } from "../utils/MIDIMessageConvert"; + +/** + * Message used in Note On / Note Off Callbacks. + * @category MIDI Messages + */ + export interface NoteMessage extends MidiMessage { + note: number; + velocity: number; + } + + export const toNoteMessage = (m: MidiMessage): NoteMessage => ({ + ...m, + note: m.data1, + velocity: m.data2, + }); + +/** + * Message used in Control Change Callbacks. + * @category MIDI Messages + */ + export interface ControlChangeMessage extends MidiMessage { + control: number, + value: number, + } + + export const toControlChangeMessage = (m: MidiMessage): ControlChangeMessage => ({ + ...m, + control: m.data1, + value: m.data2, + }); + +/** + * Message used in Program Change Callbacks. + * @category MIDI Messages + */ + export interface ProgramChangeMessage extends MidiMessage { + program: number, + value: number, + } + + export const toProgramMessage = (m: MidiMessage): ProgramChangeMessage => ({ + ...m, + program: m.data1, + value: m.data2, + }); \ No newline at end of file diff --git a/src/utils/MIDIMessageConvert.ts b/src/utils/MIDIMessageConvert.ts index 4700778..9c56224 100644 --- a/src/utils/MIDIMessageConvert.ts +++ b/src/utils/MIDIMessageConvert.ts @@ -1,4 +1,8 @@ -export type MidiMessage = { +/** + * Generic message interface. + * @category MIDI Messages + */ +export interface MidiMessage { channel: number; command: number; data1: number; diff --git a/src/utils/clock.test.ts b/src/utils/clock.test.ts new file mode 100644 index 0000000..bf157ff --- /dev/null +++ b/src/utils/clock.test.ts @@ -0,0 +1,25 @@ +import { quarternoteToBPM, ticksToBPM } from "./clock"; + +describe("quarternoteToBPM", () => { + it("should properly compute BPM = 60", () => { + expect(quarternoteToBPM(1000 / 24)).toEqual(60); + }); + + it("should properly compute BPM = 100", () => { + expect(quarternoteToBPM(600 / 24)).toEqual(100); + }); +}); + +describe("ticksToBPM", () => { + it("should properly compute BPM = 60 (every second)", () => { + expect(ticksToBPM([1000, 1041.6666666667, 1000+2*(41.6666666667)])).toBeCloseTo(60); + }); + + it("should properly compute BPM = 100 (every 600ms)", () => { + expect(ticksToBPM([600, 625, 650, 675, 700])).toBeCloseTo(100); + }); + + it("should properly return 0 when array is empty", () => { + expect(ticksToBPM([])).toBeCloseTo(0); + }) +}); \ No newline at end of file diff --git a/src/utils/clock.ts b/src/utils/clock.ts new file mode 100644 index 0000000..0f589b0 --- /dev/null +++ b/src/utils/clock.ts @@ -0,0 +1,27 @@ +const sum = (a: number, b: number): number => a + b; + +export const computeIntervalsInMs = (ticks: number[]): number[] => { + const results = []; + for(let i = 1;i { + if (!differences || !differences.length) { + return 0; + } + return differences.reduce(sum) / differences.length; +} + +export const quarternoteToBPM = (interval: number): number => { + return 60000 / (interval * 24); +} + +export const ticksToBPM = (ticks: number[]): number => { + if (!ticks || ticks.length < 2) { + return 0; + } + return quarternoteToBPM(averageIntervals(computeIntervalsInMs(ticks))); +} \ No newline at end of file diff --git a/src/utils/midiCommands.ts b/src/utils/midiCommands.ts index 622ba9b..0c03893 100644 --- a/src/utils/midiCommands.ts +++ b/src/utils/midiCommands.ts @@ -7,4 +7,10 @@ export const MidiCommand = { ChannelPressure: 0b1101 << 4, PitchBend: 0b1110 << 4, Sysex: 0b1111 << 4, + Clock: { + Start: 0xFA, + Continue: 0xFB, + Stop: 0xFC, + Pulse: 0xF8, + } }; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index a7f8b82..24512d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -633,10 +633,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@hypersphere/omnibus@^0.0.2": - version "0.0.2" - resolved "https://registry.yarnpkg.com/@hypersphere/omnibus/-/omnibus-0.0.2.tgz#84a3b7d00905d0ce9cbd367b837e77e304bed585" - integrity sha512-KcK2dvEMSO13SqNxiG5G1wZuJzTHhvB6U3PgKe1WZBbibSR8FeJ90j8olf2q8cCRYwgmVwrGwBDwjqOW4BYA8Q== +"@hypersphere/omnibus@^0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@hypersphere/omnibus/-/omnibus-0.0.6.tgz#07a3c415e60d9597878d83595b9e26d54093e9cd" + integrity sha512-FoBtsV9mLHVqQWaqu/p+A26Y0nUEmK1wCuPjfNzey02fzdHOCbFNlaxX0mETql3I3G6xA3/u1H90OC2FdyOyVA== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -942,12 +942,12 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^27.4.0": - version "27.4.0" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.0.tgz#037ab8b872067cae842a320841693080f9cb84ed" - integrity sha512-gHl8XuC1RZ8H2j5sHv/JqsaxXkDDM9iDOgu0Wp8sjs4u/snb2PVehyWXJPr+ORA0RPpgw231mnutWI1+0hgjIQ== +"@types/jest@^27.4.1": + version "27.4.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d" + integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw== dependencies: - jest-diff "^27.0.0" + jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" "@types/node@*": @@ -1166,6 +1166,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -1444,11 +1451,6 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -diff-sequences@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723" - integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ== - diff-sequences@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" @@ -2020,16 +2022,6 @@ jest-config@^27.5.1: slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^27.0.0: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.0.6.tgz#4a7a19ee6f04ad70e0e3388f35829394a44c7b5e" - integrity sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg== - dependencies: - chalk "^4.0.0" - diff-sequences "^27.0.6" - jest-get-type "^27.0.6" - pretty-format "^27.0.6" - jest-diff@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" @@ -2083,11 +2075,6 @@ jest-environment-node@^27.5.1: jest-mock "^27.5.1" jest-util "^27.5.1" -jest-get-type@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe" - integrity sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg== - jest-get-type@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" @@ -2144,7 +2131,7 @@ jest-leak-detector@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-matcher-utils@^27.5.1: +jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== @@ -2530,7 +2517,7 @@ makeerror@1.0.x: dependencies: tmpl "1.0.x" -marked@^4.0.10: +marked@^4.0.12: version "4.0.12" resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.12.tgz#2262a4e6fd1afd2f13557726238b69a48b982f7d" integrity sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ== @@ -2572,6 +2559,13 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -2748,12 +2742,12 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prettier@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" - integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== +prettier@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.1.tgz#d472797e0d7461605c1609808e27b80c0f9cfe17" + integrity sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A== -pretty-format@^27.0.0, pretty-format@^27.0.6: +pretty-format@^27.0.0: version "27.0.6" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.6.tgz#ab770c47b2c6f893a21aefc57b75da63ef49a11f" integrity sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ== @@ -2885,7 +2879,7 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shiki@^0.10.0: +shiki@^0.10.1: version "0.10.1" resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.10.1.tgz#6f9a16205a823b56c072d0f1a0bcd0f2646bef14" integrity sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng== @@ -3091,10 +3085,10 @@ trim-repeated@^1.0.0: dependencies: escape-string-regexp "^1.0.2" -ts-jest@^27.1.3: - version "27.1.3" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.3.tgz#1f723e7e74027c4da92c0ffbd73287e8af2b2957" - integrity sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA== +ts-jest@^27.1.4: + version "27.1.4" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.4.tgz#84d42cf0f4e7157a52e7c64b1492c46330943e00" + integrity sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" @@ -3129,21 +3123,21 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typedoc@^0.22.11: - version "0.22.11" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.22.11.tgz#a3d7f4577eef9fc82dd2e8f4e2915e69f884c250" - integrity sha512-pVr3hh6dkS3lPPaZz1fNpvcrqLdtEvXmXayN55czlamSgvEjh+57GUqfhAI1Xsuu/hNHUT1KNSx8LH2wBP/7SA== +typedoc@^0.22.13: + version "0.22.13" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.22.13.tgz#d061f8f0fb7c9d686e48814f245bddeea4564e66" + integrity sha512-NHNI7Dr6JHa/I3+c62gdRNXBIyX7P33O9TafGLd07ur3MqzcKgwTvpg18EtvCLHJyfeSthAtCLpM7WkStUmDuQ== dependencies: glob "^7.2.0" lunr "^2.3.9" - marked "^4.0.10" - minimatch "^3.0.4" - shiki "^0.10.0" - -typescript@^4.5.5: - version "4.5.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3" - integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA== + marked "^4.0.12" + minimatch "^5.0.1" + shiki "^0.10.1" + +typescript@^4.6.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c" + integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw== universalify@^0.1.0, universalify@^0.1.2: version "0.1.2"