velocious 1.0.619 → 1.0.621

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/build/background-jobs/store.js +1 -1
  3. package/build/configuration.js +1 -1
  4. package/build/environment-handlers/base.js +8 -0
  5. package/build/environment-handlers/node.js +12 -0
  6. package/build/src/background-jobs/store.js +1 -1
  7. package/build/src/configuration.js +2 -2
  8. package/build/src/environment-handlers/base.d.ts +7 -0
  9. package/build/src/environment-handlers/base.d.ts.map +1 -1
  10. package/build/src/environment-handlers/base.js +8 -1
  11. package/build/src/environment-handlers/node.d.ts +7 -0
  12. package/build/src/environment-handlers/node.d.ts.map +1 -1
  13. package/build/src/environment-handlers/node.js +12 -1
  14. package/build/src/testing/shared-transaction-connection-coordinator.d.ts +5 -1
  15. package/build/src/testing/shared-transaction-connection-coordinator.d.ts.map +1 -1
  16. package/build/src/testing/shared-transaction-connection-coordinator.js +81 -14
  17. package/build/src/testing/test.d.ts +1 -1
  18. package/build/src/testing/test.d.ts.map +1 -1
  19. package/build/src/testing/test.js +2 -2
  20. package/build/testing/shared-transaction-connection-coordinator.js +93 -12
  21. package/build/testing/test.js +1 -1
  22. package/build/tsconfig.tsbuildinfo +1 -1
  23. package/package.json +2 -1
  24. package/src/background-jobs/store.js +1 -1
  25. package/src/configuration.js +1 -1
  26. package/src/environment-handlers/base.js +8 -0
  27. package/src/environment-handlers/node.js +12 -0
  28. package/src/testing/shared-transaction-connection-coordinator.js +93 -12
  29. package/src/testing/test.js +1 -1
  30. package/build/src/testing/wait-for-event.d.ts +0 -46
  31. package/build/src/testing/wait-for-event.d.ts.map +0 -1
  32. package/build/src/testing/wait-for-event.js +0 -64
  33. package/build/testing/wait-for-event.js +0 -72
  34. package/src/testing/wait-for-event.js +0 -72
@@ -1,64 +0,0 @@
1
- // @ts-check
2
- /**
3
- * @typedef {object} WaitForEventOptions
4
- * @property {number} [timeoutMs] Timeout in milliseconds (default: 5000).
5
- * @property {(...args: Array<ReturnType<typeof JSON.parse>>) => boolean} [filter] Only resolve when this predicate returns true for the emitted arguments.
6
- */
7
- /**
8
- * @typedef {object} EventEmitterLike
9
- * @property {(event: string, listener: (...args: Array<ReturnType<typeof JSON.parse>>) => void) => void} on Registers a listener.
10
- * @property {(event: string, listener: (...args: Array<ReturnType<typeof JSON.parse>>) => void) => void} off Removes a listener.
11
- */
12
- /**
13
- * Resolves the moment `eventName` fires on `emitter` — optionally only when `filter`
14
- * matches the emitted arguments — instead of sleeping a fixed duration. Rejects with a
15
- * timeout error if the event does not fire within `timeoutMs`. The listener is always
16
- * removed (on resolve and on timeout).
17
- *
18
- * Use this to await a real signal (a background job finishing, a model update, a
19
- * websocket message) rather than guessing a delay. To poll an arbitrary condition
20
- * instead of a discrete event, use awaitery's `waitFor`.
21
- * @param {EventEmitterLike} emitter - Event emitter exposing `on`/`off` (Node EventEmitter, eventemitter3, velocious `testEvents`, ...).
22
- * @param {string} eventName - The event to wait for.
23
- * @param {WaitForEventOptions} [options] - Options.
24
- * @returns {Promise<ReturnType<typeof JSON.parse>>} - Resolves with the single emitted argument, an array when the event emits multiple, or undefined when it emits none.
25
- */
26
- export default function waitForEvent(emitter, eventName, options = {}) {
27
- const { timeoutMs = 5000, filter } = options;
28
- return new Promise((resolve, reject) => {
29
- /** @type {ReturnType<typeof setTimeout>} */
30
- let timer;
31
- /**
32
- * Resolves the wait when a matching event fires, removing itself first. A filter
33
- * that throws (e.g. it assumes an event shape an intermediate emission doesn't
34
- * have) rejects immediately rather than leaving the waiter pending until timeout.
35
- * @param {...ReturnType<typeof JSON.parse>} args - Emitted arguments.
36
- * @returns {void}
37
- */
38
- const listener = (...args) => {
39
- if (filter) {
40
- let matched;
41
- try {
42
- matched = filter(...args);
43
- }
44
- catch (error) {
45
- clearTimeout(timer);
46
- emitter.off(eventName, listener);
47
- reject(error);
48
- return;
49
- }
50
- if (!matched)
51
- return;
52
- }
53
- clearTimeout(timer);
54
- emitter.off(eventName, listener);
55
- resolve(args.length > 1 ? args : args[0]);
56
- };
57
- timer = setTimeout(() => {
58
- emitter.off(eventName, listener);
59
- reject(new Error(`Timed out after ${timeoutMs}ms waiting for event ${JSON.stringify(eventName)}`));
60
- }, timeoutMs);
61
- emitter.on(eventName, listener);
62
- });
63
- }
64
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2FpdC1mb3ItZXZlbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvdGVzdGluZy93YWl0LWZvci1ldmVudC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVo7Ozs7R0FJRztBQUVIOzs7O0dBSUc7QUFFSDs7Ozs7Ozs7Ozs7OztHQWFHO0FBQ0gsTUFBTSxDQUFDLE9BQU8sVUFBVSxZQUFZLENBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxPQUFPLEdBQUcsRUFBRTtJQUNuRSxNQUFNLEVBQUMsU0FBUyxHQUFHLElBQUksRUFBRSxNQUFNLEVBQUMsR0FBRyxPQUFPLENBQUE7SUFFMUMsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtRQUNyQyw0Q0FBNEM7UUFDNUMsSUFBSSxLQUFLLENBQUE7UUFFVDs7Ozs7O1dBTUc7UUFDSCxNQUFNLFFBQVEsR0FBRyxDQUFDLEdBQUcsSUFBSSxFQUFFLEVBQUU7WUFDM0IsSUFBSSxNQUFNLEVBQUUsQ0FBQztnQkFDWCxJQUFJLE9BQU8sQ0FBQTtnQkFFWCxJQUFJLENBQUM7b0JBQ0gsT0FBTyxHQUFHLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFBO2dCQUMzQixDQUFDO2dCQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7b0JBQ2YsWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFBO29CQUNuQixPQUFPLENBQUMsR0FBRyxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQTtvQkFDaEMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFBO29CQUViLE9BQU07Z0JBQ1IsQ0FBQztnQkFFRCxJQUFJLENBQUMsT0FBTztvQkFBRSxPQUFNO1lBQ3RCLENBQUM7WUFFRCxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUE7WUFDbkIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUE7WUFDaEMsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFBO1FBQzNDLENBQUMsQ0FBQTtRQUVELEtBQUssR0FBRyxVQUFVLENBQUMsR0FBRyxFQUFFO1lBQ3RCLE9BQU8sQ0FBQyxHQUFHLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFBO1lBQ2hDLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyxtQkFBbUIsU0FBUyx3QkFBd0IsSUFBSSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQTtRQUNwRyxDQUFDLEVBQUUsU0FBUyxDQUFDLENBQUE7UUFFYixPQUFPLENBQUMsRUFBRSxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQTtJQUNqQyxDQUFDLENBQUMsQ0FBQTtBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuLyoqXG4gKiBAdHlwZWRlZiB7b2JqZWN0fSBXYWl0Rm9yRXZlbnRPcHRpb25zXG4gKiBAcHJvcGVydHkge251bWJlcn0gW3RpbWVvdXRNc10gVGltZW91dCBpbiBtaWxsaXNlY29uZHMgKGRlZmF1bHQ6IDUwMDApLlxuICogQHByb3BlcnR5IHsoLi4uYXJnczogQXJyYXk8UmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT4+KSA9PiBib29sZWFufSBbZmlsdGVyXSBPbmx5IHJlc29sdmUgd2hlbiB0aGlzIHByZWRpY2F0ZSByZXR1cm5zIHRydWUgZm9yIHRoZSBlbWl0dGVkIGFyZ3VtZW50cy5cbiAqL1xuXG4vKipcbiAqIEB0eXBlZGVmIHtvYmplY3R9IEV2ZW50RW1pdHRlckxpa2VcbiAqIEBwcm9wZXJ0eSB7KGV2ZW50OiBzdHJpbmcsIGxpc3RlbmVyOiAoLi4uYXJnczogQXJyYXk8UmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT4+KSA9PiB2b2lkKSA9PiB2b2lkfSBvbiBSZWdpc3RlcnMgYSBsaXN0ZW5lci5cbiAqIEBwcm9wZXJ0eSB7KGV2ZW50OiBzdHJpbmcsIGxpc3RlbmVyOiAoLi4uYXJnczogQXJyYXk8UmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT4+KSA9PiB2b2lkKSA9PiB2b2lkfSBvZmYgUmVtb3ZlcyBhIGxpc3RlbmVyLlxuICovXG5cbi8qKlxuICogUmVzb2x2ZXMgdGhlIG1vbWVudCBgZXZlbnROYW1lYCBmaXJlcyBvbiBgZW1pdHRlcmAg4oCUIG9wdGlvbmFsbHkgb25seSB3aGVuIGBmaWx0ZXJgXG4gKiBtYXRjaGVzIHRoZSBlbWl0dGVkIGFyZ3VtZW50cyDigJQgaW5zdGVhZCBvZiBzbGVlcGluZyBhIGZpeGVkIGR1cmF0aW9uLiBSZWplY3RzIHdpdGggYVxuICogdGltZW91dCBlcnJvciBpZiB0aGUgZXZlbnQgZG9lcyBub3QgZmlyZSB3aXRoaW4gYHRpbWVvdXRNc2AuIFRoZSBsaXN0ZW5lciBpcyBhbHdheXNcbiAqIHJlbW92ZWQgKG9uIHJlc29sdmUgYW5kIG9uIHRpbWVvdXQpLlxuICpcbiAqIFVzZSB0aGlzIHRvIGF3YWl0IGEgcmVhbCBzaWduYWwgKGEgYmFja2dyb3VuZCBqb2IgZmluaXNoaW5nLCBhIG1vZGVsIHVwZGF0ZSwgYVxuICogd2Vic29ja2V0IG1lc3NhZ2UpIHJhdGhlciB0aGFuIGd1ZXNzaW5nIGEgZGVsYXkuIFRvIHBvbGwgYW4gYXJiaXRyYXJ5IGNvbmRpdGlvblxuICogaW5zdGVhZCBvZiBhIGRpc2NyZXRlIGV2ZW50LCB1c2UgYXdhaXRlcnkncyBgd2FpdEZvcmAuXG4gKiBAcGFyYW0ge0V2ZW50RW1pdHRlckxpa2V9IGVtaXR0ZXIgLSBFdmVudCBlbWl0dGVyIGV4cG9zaW5nIGBvbmAvYG9mZmAgKE5vZGUgRXZlbnRFbWl0dGVyLCBldmVudGVtaXR0ZXIzLCB2ZWxvY2lvdXMgYHRlc3RFdmVudHNgLCAuLi4pLlxuICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50TmFtZSAtIFRoZSBldmVudCB0byB3YWl0IGZvci5cbiAqIEBwYXJhbSB7V2FpdEZvckV2ZW50T3B0aW9uc30gW29wdGlvbnNdIC0gT3B0aW9ucy5cbiAqIEByZXR1cm5zIHtQcm9taXNlPFJldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+Pn0gLSBSZXNvbHZlcyB3aXRoIHRoZSBzaW5nbGUgZW1pdHRlZCBhcmd1bWVudCwgYW4gYXJyYXkgd2hlbiB0aGUgZXZlbnQgZW1pdHMgbXVsdGlwbGUsIG9yIHVuZGVmaW5lZCB3aGVuIGl0IGVtaXRzIG5vbmUuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHdhaXRGb3JFdmVudChlbWl0dGVyLCBldmVudE5hbWUsIG9wdGlvbnMgPSB7fSkge1xuICBjb25zdCB7dGltZW91dE1zID0gNTAwMCwgZmlsdGVyfSA9IG9wdGlvbnNcblxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIC8qKiBAdHlwZSB7UmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD59ICovXG4gICAgbGV0IHRpbWVyXG5cbiAgICAvKipcbiAgICAgKiBSZXNvbHZlcyB0aGUgd2FpdCB3aGVuIGEgbWF0Y2hpbmcgZXZlbnQgZmlyZXMsIHJlbW92aW5nIGl0c2VsZiBmaXJzdC4gQSBmaWx0ZXJcbiAgICAgKiB0aGF0IHRocm93cyAoZS5nLiBpdCBhc3N1bWVzIGFuIGV2ZW50IHNoYXBlIGFuIGludGVybWVkaWF0ZSBlbWlzc2lvbiBkb2Vzbid0XG4gICAgICogaGF2ZSkgcmVqZWN0cyBpbW1lZGlhdGVseSByYXRoZXIgdGhhbiBsZWF2aW5nIHRoZSB3YWl0ZXIgcGVuZGluZyB1bnRpbCB0aW1lb3V0LlxuICAgICAqIEBwYXJhbSB7Li4uUmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT59IGFyZ3MgLSBFbWl0dGVkIGFyZ3VtZW50cy5cbiAgICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICAgKi9cbiAgICBjb25zdCBsaXN0ZW5lciA9ICguLi5hcmdzKSA9PiB7XG4gICAgICBpZiAoZmlsdGVyKSB7XG4gICAgICAgIGxldCBtYXRjaGVkXG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBtYXRjaGVkID0gZmlsdGVyKC4uLmFyZ3MpXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVyKVxuICAgICAgICAgIGVtaXR0ZXIub2ZmKGV2ZW50TmFtZSwgbGlzdGVuZXIpXG4gICAgICAgICAgcmVqZWN0KGVycm9yKVxuXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIW1hdGNoZWQpIHJldHVyblxuICAgICAgfVxuXG4gICAgICBjbGVhclRpbWVvdXQodGltZXIpXG4gICAgICBlbWl0dGVyLm9mZihldmVudE5hbWUsIGxpc3RlbmVyKVxuICAgICAgcmVzb2x2ZShhcmdzLmxlbmd0aCA+IDEgPyBhcmdzIDogYXJnc1swXSlcbiAgICB9XG5cbiAgICB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgZW1pdHRlci5vZmYoZXZlbnROYW1lLCBsaXN0ZW5lcilcbiAgICAgIHJlamVjdChuZXcgRXJyb3IoYFRpbWVkIG91dCBhZnRlciAke3RpbWVvdXRNc31tcyB3YWl0aW5nIGZvciBldmVudCAke0pTT04uc3RyaW5naWZ5KGV2ZW50TmFtZSl9YCkpXG4gICAgfSwgdGltZW91dE1zKVxuXG4gICAgZW1pdHRlci5vbihldmVudE5hbWUsIGxpc3RlbmVyKVxuICB9KVxufVxuIl19
@@ -1,72 +0,0 @@
1
- // @ts-check
2
-
3
- /**
4
- * @typedef {object} WaitForEventOptions
5
- * @property {number} [timeoutMs] Timeout in milliseconds (default: 5000).
6
- * @property {(...args: Array<ReturnType<typeof JSON.parse>>) => boolean} [filter] Only resolve when this predicate returns true for the emitted arguments.
7
- */
8
-
9
- /**
10
- * @typedef {object} EventEmitterLike
11
- * @property {(event: string, listener: (...args: Array<ReturnType<typeof JSON.parse>>) => void) => void} on Registers a listener.
12
- * @property {(event: string, listener: (...args: Array<ReturnType<typeof JSON.parse>>) => void) => void} off Removes a listener.
13
- */
14
-
15
- /**
16
- * Resolves the moment `eventName` fires on `emitter` — optionally only when `filter`
17
- * matches the emitted arguments — instead of sleeping a fixed duration. Rejects with a
18
- * timeout error if the event does not fire within `timeoutMs`. The listener is always
19
- * removed (on resolve and on timeout).
20
- *
21
- * Use this to await a real signal (a background job finishing, a model update, a
22
- * websocket message) rather than guessing a delay. To poll an arbitrary condition
23
- * instead of a discrete event, use awaitery's `waitFor`.
24
- * @param {EventEmitterLike} emitter - Event emitter exposing `on`/`off` (Node EventEmitter, eventemitter3, velocious `testEvents`, ...).
25
- * @param {string} eventName - The event to wait for.
26
- * @param {WaitForEventOptions} [options] - Options.
27
- * @returns {Promise<ReturnType<typeof JSON.parse>>} - Resolves with the single emitted argument, an array when the event emits multiple, or undefined when it emits none.
28
- */
29
- export default function waitForEvent(emitter, eventName, options = {}) {
30
- const {timeoutMs = 5000, filter} = options
31
-
32
- return new Promise((resolve, reject) => {
33
- /** @type {ReturnType<typeof setTimeout>} */
34
- let timer
35
-
36
- /**
37
- * Resolves the wait when a matching event fires, removing itself first. A filter
38
- * that throws (e.g. it assumes an event shape an intermediate emission doesn't
39
- * have) rejects immediately rather than leaving the waiter pending until timeout.
40
- * @param {...ReturnType<typeof JSON.parse>} args - Emitted arguments.
41
- * @returns {void}
42
- */
43
- const listener = (...args) => {
44
- if (filter) {
45
- let matched
46
-
47
- try {
48
- matched = filter(...args)
49
- } catch (error) {
50
- clearTimeout(timer)
51
- emitter.off(eventName, listener)
52
- reject(error)
53
-
54
- return
55
- }
56
-
57
- if (!matched) return
58
- }
59
-
60
- clearTimeout(timer)
61
- emitter.off(eventName, listener)
62
- resolve(args.length > 1 ? args : args[0])
63
- }
64
-
65
- timer = setTimeout(() => {
66
- emitter.off(eventName, listener)
67
- reject(new Error(`Timed out after ${timeoutMs}ms waiting for event ${JSON.stringify(eventName)}`))
68
- }, timeoutMs)
69
-
70
- emitter.on(eventName, listener)
71
- })
72
- }
@@ -1,72 +0,0 @@
1
- // @ts-check
2
-
3
- /**
4
- * @typedef {object} WaitForEventOptions
5
- * @property {number} [timeoutMs] Timeout in milliseconds (default: 5000).
6
- * @property {(...args: Array<ReturnType<typeof JSON.parse>>) => boolean} [filter] Only resolve when this predicate returns true for the emitted arguments.
7
- */
8
-
9
- /**
10
- * @typedef {object} EventEmitterLike
11
- * @property {(event: string, listener: (...args: Array<ReturnType<typeof JSON.parse>>) => void) => void} on Registers a listener.
12
- * @property {(event: string, listener: (...args: Array<ReturnType<typeof JSON.parse>>) => void) => void} off Removes a listener.
13
- */
14
-
15
- /**
16
- * Resolves the moment `eventName` fires on `emitter` — optionally only when `filter`
17
- * matches the emitted arguments — instead of sleeping a fixed duration. Rejects with a
18
- * timeout error if the event does not fire within `timeoutMs`. The listener is always
19
- * removed (on resolve and on timeout).
20
- *
21
- * Use this to await a real signal (a background job finishing, a model update, a
22
- * websocket message) rather than guessing a delay. To poll an arbitrary condition
23
- * instead of a discrete event, use awaitery's `waitFor`.
24
- * @param {EventEmitterLike} emitter - Event emitter exposing `on`/`off` (Node EventEmitter, eventemitter3, velocious `testEvents`, ...).
25
- * @param {string} eventName - The event to wait for.
26
- * @param {WaitForEventOptions} [options] - Options.
27
- * @returns {Promise<ReturnType<typeof JSON.parse>>} - Resolves with the single emitted argument, an array when the event emits multiple, or undefined when it emits none.
28
- */
29
- export default function waitForEvent(emitter, eventName, options = {}) {
30
- const {timeoutMs = 5000, filter} = options
31
-
32
- return new Promise((resolve, reject) => {
33
- /** @type {ReturnType<typeof setTimeout>} */
34
- let timer
35
-
36
- /**
37
- * Resolves the wait when a matching event fires, removing itself first. A filter
38
- * that throws (e.g. it assumes an event shape an intermediate emission doesn't
39
- * have) rejects immediately rather than leaving the waiter pending until timeout.
40
- * @param {...ReturnType<typeof JSON.parse>} args - Emitted arguments.
41
- * @returns {void}
42
- */
43
- const listener = (...args) => {
44
- if (filter) {
45
- let matched
46
-
47
- try {
48
- matched = filter(...args)
49
- } catch (error) {
50
- clearTimeout(timer)
51
- emitter.off(eventName, listener)
52
- reject(error)
53
-
54
- return
55
- }
56
-
57
- if (!matched) return
58
- }
59
-
60
- clearTimeout(timer)
61
- emitter.off(eventName, listener)
62
- resolve(args.length > 1 ? args : args[0])
63
- }
64
-
65
- timer = setTimeout(() => {
66
- emitter.off(eventName, listener)
67
- reject(new Error(`Timed out after ${timeoutMs}ms waiting for event ${JSON.stringify(eventName)}`))
68
- }, timeoutMs)
69
-
70
- emitter.on(eventName, listener)
71
- })
72
- }