@nxtedition/lib 26.8.8 → 27.0.0
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.
- package/app.js +39 -8
- package/couch.d.ts +1 -0
- package/couch.js +1 -1
- package/numa.js +22 -13
- package/package.json +10 -48
- package/rxjs/auditMap.js +0 -111
- package/rxjs/auditMap.test.js +0 -64
- package/rxjs/combineMap.d.ts +0 -6
- package/rxjs/combineMap.js +0 -152
- package/rxjs/combineMap.test.js +0 -199
- package/rxjs/firstValueFrom.d.ts +0 -12
- package/rxjs/firstValueFrom.js +0 -18
- package/rxjs/lastValueFrom.js +0 -14
- package/rxjs/retry.js +0 -112
- package/rxjs/withAbortSignal.js +0 -31
- package/s3.js +0 -339
- package/timeline.js +0 -115
- package/util/template/index-common.d.ts +0 -67
- package/util/template/index-common.js +0 -226
- package/util/template/index-web.d.ts +0 -1
- package/util/template/index-web.js +0 -28
- package/util/template/index.d.ts +0 -1
- package/util/template/index.js +0 -26
- package/util/template/index.test.js +0 -94
- package/util/template/javascript.js +0 -692
- package/util/template/nextpressions.js +0 -600
- package/util/template/nextpressions.test.js +0 -165
- package/util/template/transform.js +0 -22
- package/util/template/transform.test.js +0 -16
- package/weakCache.d.ts +0 -4
- package/weakCache.js +0 -25
- package/yield.js +0 -103
package/app.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
1
2
|
import * as inspector from 'node:inspector'
|
|
2
3
|
import fs from 'node:fs'
|
|
3
4
|
import os from 'node:os'
|
|
@@ -7,9 +8,9 @@ import assert from 'node:assert'
|
|
|
7
8
|
import cluster from 'node:cluster'
|
|
8
9
|
import stream from 'node:stream'
|
|
9
10
|
import { Buffer } from 'node:buffer'
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
11
|
+
import v8 from 'node:v8'
|
|
12
|
+
import { isPrimary } from 'node:cluster'
|
|
13
|
+
import { monitorEventLoopDelay } from 'node:perf_hooks'
|
|
13
14
|
import {
|
|
14
15
|
isMainThread,
|
|
15
16
|
parentPort,
|
|
@@ -17,13 +18,16 @@ import {
|
|
|
17
18
|
BroadcastChannel,
|
|
18
19
|
resourceLimits,
|
|
19
20
|
} from 'node:worker_threads'
|
|
21
|
+
|
|
22
|
+
import { getDockerSecretsSync } from './docker-secrets.js'
|
|
23
|
+
import { getUTCRangeForLocalTime } from './time.js'
|
|
24
|
+
import fp from 'lodash/fp.js'
|
|
20
25
|
import deepstream from '@nxtedition/deepstream.io-client-js'
|
|
21
26
|
import { createLogger } from './logger.js'
|
|
22
27
|
import nconf from 'nconf'
|
|
23
28
|
import { makeCouch } from './couch.js'
|
|
24
|
-
import { makeTemplateCompiler } from '
|
|
29
|
+
import { makeTemplateCompiler } from '@nxtedition/template'
|
|
25
30
|
import { makeDeepstream } from './deepstream.js'
|
|
26
|
-
import v8 from 'v8'
|
|
27
31
|
import * as rxjs from 'rxjs'
|
|
28
32
|
import rx from 'rxjs/operators'
|
|
29
33
|
import { performance } from 'perf_hooks'
|
|
@@ -31,15 +35,18 @@ import hashString from './hash.js'
|
|
|
31
35
|
import { makeTrace } from './trace.js'
|
|
32
36
|
import { compose, createServer } from './http.js'
|
|
33
37
|
import { json } from 'node:stream/consumers'
|
|
34
|
-
import { monitorEventLoopDelay } from 'node:perf_hooks'
|
|
35
38
|
import xuid from 'xuid'
|
|
36
39
|
import { isTimeBetween } from './time.js'
|
|
37
40
|
import makeUnderPressure from './under-pressure.js'
|
|
38
|
-
import { isPrimary } from 'node:cluster'
|
|
39
41
|
import { nice } from '@nxtedition/sched'
|
|
40
42
|
import { setAffinity } from './numa.js'
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
/**
|
|
45
|
+
* @param {object} appConfig
|
|
46
|
+
* @param {object} [meta]
|
|
47
|
+
* @returns {object}
|
|
48
|
+
*/
|
|
49
|
+
export function makeApp(appConfig, onTerminateOrMeta, metaOrNull) {
|
|
43
50
|
let ds
|
|
44
51
|
let nxt
|
|
45
52
|
let couch
|
|
@@ -49,6 +56,27 @@ export function makeApp(appConfig, onTerminate) {
|
|
|
49
56
|
let logger
|
|
50
57
|
let trace
|
|
51
58
|
|
|
59
|
+
let onTerminate
|
|
60
|
+
let meta
|
|
61
|
+
|
|
62
|
+
if (typeof onTerminateOrMeta === 'function') {
|
|
63
|
+
meta = metaOrNull
|
|
64
|
+
} else {
|
|
65
|
+
meta = onTerminateOrMeta ?? metaOrNull
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (onTerminate != null && typeof onTerminateOrMeta !== 'function') {
|
|
69
|
+
throw new Error('onTerminate must be a function or null')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (meta != null && typeof meta !== 'object') {
|
|
73
|
+
throw new Error('meta must be an object or null')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const pkg = meta?.url
|
|
77
|
+
? JSON.parse(readFileSync(new URL('../package.json', meta.url).pathname).toString())
|
|
78
|
+
: null
|
|
79
|
+
|
|
52
80
|
/** @type {Array<rxjs.Subscription|Function|AsyncFunction|Disposable|AsyncDisposable>} */
|
|
53
81
|
const destroyers = []
|
|
54
82
|
|
|
@@ -119,6 +147,9 @@ export function makeApp(appConfig, onTerminate) {
|
|
|
119
147
|
}
|
|
120
148
|
}
|
|
121
149
|
|
|
150
|
+
appConfig.name ||= pkg?.name ?? null
|
|
151
|
+
appConfig.version ||= pkg?.version ?? null
|
|
152
|
+
|
|
122
153
|
assert(appConfig.name, 'name is required')
|
|
123
154
|
|
|
124
155
|
const appDestroyers = []
|
package/couch.d.ts
CHANGED
package/couch.js
CHANGED
|
@@ -2,7 +2,7 @@ import assert from 'node:assert'
|
|
|
2
2
|
import stream from 'node:stream'
|
|
3
3
|
import querystring from 'node:querystring'
|
|
4
4
|
import createError from 'http-errors'
|
|
5
|
-
import { makeWeakCache } from '
|
|
5
|
+
import { makeWeakCache } from '@nxtedition/weak-cache'
|
|
6
6
|
import { defaultDelay as delay } from './http.js'
|
|
7
7
|
import urljoin from 'url-join'
|
|
8
8
|
import { AbortError } from './errors.js'
|
package/numa.js
CHANGED
|
@@ -3,6 +3,21 @@ import path from 'node:path'
|
|
|
3
3
|
|
|
4
4
|
import { sched_setaffinity } from '@nxtedition/sched'
|
|
5
5
|
|
|
6
|
+
function parseRange(value) {
|
|
7
|
+
const range = []
|
|
8
|
+
for (const part of value.split(',')) {
|
|
9
|
+
if (part.includes('-')) {
|
|
10
|
+
const [start, end] = part.split('-').map(Number)
|
|
11
|
+
for (let i = start; i <= end; i++) {
|
|
12
|
+
range.push(i)
|
|
13
|
+
}
|
|
14
|
+
} else {
|
|
15
|
+
range.push(Number(part))
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return range
|
|
19
|
+
}
|
|
20
|
+
|
|
6
21
|
/**
|
|
7
22
|
*
|
|
8
23
|
* @param {number|number[]} numa
|
|
@@ -14,6 +29,8 @@ export function setAffinity(numa) {
|
|
|
14
29
|
throw new Error('NUMA node must be a non-negative integer')
|
|
15
30
|
}
|
|
16
31
|
|
|
32
|
+
const isolated = parseRange(fs.readFileSync('/sys/devices/system/cpu/isolated', 'utf8').trim())
|
|
33
|
+
|
|
17
34
|
const allNodes = []
|
|
18
35
|
for (const entry of fs.readdirSync('/sys/devices/system/node')) {
|
|
19
36
|
if (!entry.startsWith('node')) {
|
|
@@ -23,25 +40,17 @@ export function setAffinity(numa) {
|
|
|
23
40
|
const cpulist = fs
|
|
24
41
|
.readFileSync(path.join('/sys/devices/system/node', entry, 'cpulist'), 'utf8')
|
|
25
42
|
.trim()
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (part.includes('-')) {
|
|
29
|
-
const [start, end] = part.split('-').map(Number)
|
|
30
|
-
for (let i = start; i <= end; i++) {
|
|
31
|
-
cpus.push(i)
|
|
32
|
-
}
|
|
33
|
-
} else {
|
|
34
|
-
cpus.push(Number(part))
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
allNodes.push(cpus)
|
|
43
|
+
|
|
44
|
+
allNodes.push(parseRange(cpulist))
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
if (allNodes.length === 0) {
|
|
41
48
|
throw new Error('No NUMA nodes found')
|
|
42
49
|
}
|
|
43
50
|
|
|
44
|
-
const affinity = indices
|
|
51
|
+
const affinity = indices
|
|
52
|
+
.flatMap((i) => allNodes[i % allNodes.length] ?? [])
|
|
53
|
+
.filter((cpu) => !isolated.includes(cpu))
|
|
45
54
|
sched_setaffinity(0, affinity)
|
|
46
55
|
globalThis.__nxt_sched_affinity = {
|
|
47
56
|
cpulist: affinity,
|
package/package.json
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/lib",
|
|
3
|
-
"version": "
|
|
4
|
-
"license": "
|
|
3
|
+
"version": "27.0.0",
|
|
4
|
+
"license": "UNLICENSED",
|
|
5
5
|
"author": "Robert Nagy <robert.nagy@boffins.se>",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
8
8
|
"index.d.ts",
|
|
9
9
|
"hash.js",
|
|
10
10
|
"ass.js",
|
|
11
|
-
"rxjs/*",
|
|
12
11
|
"util/*",
|
|
13
12
|
"cache.js",
|
|
14
13
|
"fixed-queue.js",
|
|
15
|
-
"http-client.js",
|
|
16
14
|
"subtract-ranges.js",
|
|
17
15
|
"serializers.js",
|
|
18
16
|
"platform.js",
|
|
19
17
|
"elasticsearch.js",
|
|
20
18
|
"merge-ranges.js",
|
|
21
19
|
"http.js",
|
|
22
|
-
"s3.js",
|
|
23
20
|
"time.js",
|
|
24
21
|
"mutex.js",
|
|
25
22
|
"deepstream.js",
|
|
@@ -33,8 +30,6 @@
|
|
|
33
30
|
"proxy.js",
|
|
34
31
|
"timers.js",
|
|
35
32
|
"trace.js",
|
|
36
|
-
"weakCache.js",
|
|
37
|
-
"weakCache.d.ts",
|
|
38
33
|
"couch.js",
|
|
39
34
|
"couch.d.ts",
|
|
40
35
|
"app.js",
|
|
@@ -43,57 +38,34 @@
|
|
|
43
38
|
"errors.d.ts",
|
|
44
39
|
"worker.js",
|
|
45
40
|
"stream.js",
|
|
46
|
-
"timeline.js",
|
|
47
41
|
"transcript.js",
|
|
48
42
|
"docker-secrets.js",
|
|
49
43
|
"wordwrap.js",
|
|
50
|
-
"under-pressure.js"
|
|
51
|
-
"yield.js"
|
|
44
|
+
"under-pressure.js"
|
|
52
45
|
],
|
|
53
46
|
"scripts": {
|
|
54
47
|
"test": "node --test-timeout 60000 --test",
|
|
55
|
-
"test:types": "tsd"
|
|
56
|
-
"prepare": "husky"
|
|
57
|
-
},
|
|
58
|
-
"lint-staged": {
|
|
59
|
-
"*.{js,jsx,md,ts}": [
|
|
60
|
-
"eslint",
|
|
61
|
-
"prettier --write"
|
|
62
|
-
]
|
|
63
|
-
},
|
|
64
|
-
"prettier": {
|
|
65
|
-
"printWidth": 100,
|
|
66
|
-
"semi": false,
|
|
67
|
-
"singleQuote": true
|
|
48
|
+
"test:types": "tsd"
|
|
68
49
|
},
|
|
69
50
|
"dependencies": {
|
|
70
|
-
"@aws-sdk/client-s3": "3.918.0",
|
|
71
51
|
"@elastic/elasticsearch": "^8.17.1",
|
|
72
52
|
"@elastic/transport": "^8.9.3",
|
|
73
53
|
"@nxtedition/nxt-undici": "^6.4.17",
|
|
74
54
|
"@nxtedition/sched": "^1.0.2",
|
|
75
|
-
"@
|
|
76
|
-
"
|
|
77
|
-
"astring": "^1.9.0",
|
|
78
|
-
"date-fns": "^4.1.0",
|
|
55
|
+
"@nxtedition/template": "^1.0.0-alpha.0",
|
|
56
|
+
"@nxtedition/weak-cache": "^1.0.0-alpha.0",
|
|
79
57
|
"diff": "5.2.0",
|
|
80
58
|
"fast-querystring": "^1.1.2",
|
|
81
|
-
"hasha": "^7.0.0",
|
|
82
59
|
"http-errors": "^2.0.0",
|
|
83
|
-
"json5": "^2.2.3",
|
|
84
60
|
"lodash": "^4.17.21",
|
|
85
61
|
"lru-cache": "^11.2.2",
|
|
86
62
|
"mime": "^4.0.7",
|
|
87
63
|
"mitata": "^1.0.34",
|
|
88
|
-
"moment-timezone": "^0.5.48",
|
|
89
64
|
"nconf": "^0.13.0",
|
|
90
65
|
"object-hash": "^3.0.0",
|
|
91
|
-
"p-queue": "^9.0.0",
|
|
92
66
|
"pino": "^10.1.0",
|
|
93
67
|
"qs": "^6.14.0",
|
|
94
68
|
"request-target": "^1.0.2",
|
|
95
|
-
"smpte-timecode": "^1.3.6",
|
|
96
|
-
"split-string": "^6.0.0",
|
|
97
69
|
"url-join": "^5.0.0",
|
|
98
70
|
"xuid": "^4.1.5",
|
|
99
71
|
"yocto-queue": "^1.2.1"
|
|
@@ -103,20 +75,9 @@
|
|
|
103
75
|
"@types/lodash": "^4.17.20",
|
|
104
76
|
"@types/node": "^24.9.1",
|
|
105
77
|
"canvas": "^3.1.0",
|
|
106
|
-
"eslint": "^9.38.0",
|
|
107
|
-
"eslint-config-prettier": "^10.1.8",
|
|
108
|
-
"eslint-config-standard": "^17.0.0",
|
|
109
|
-
"eslint-plugin-import": "^2.32.0",
|
|
110
|
-
"eslint-plugin-n": "^17.23.1",
|
|
111
|
-
"eslint-plugin-node": "^11.1.0",
|
|
112
|
-
"eslint-plugin-promise": "^7.2.1",
|
|
113
|
-
"husky": "^9.1.7",
|
|
114
|
-
"lint-staged": "^16.2.6",
|
|
115
|
-
"prettier": "^3.6.2",
|
|
116
78
|
"rxjs": "^7.8.2",
|
|
117
79
|
"tsd": "^0.33.0",
|
|
118
|
-
"typescript": "^5.9.3"
|
|
119
|
-
"typescript-eslint": "^8.46.2"
|
|
80
|
+
"typescript": "^5.9.3"
|
|
120
81
|
},
|
|
121
82
|
"peerDependencies": {
|
|
122
83
|
"@elastic/elasticsearch": "^8.6.0",
|
|
@@ -124,6 +85,7 @@
|
|
|
124
85
|
"@nxtedition/deepstream.io-client-js": ">=14.1.0",
|
|
125
86
|
"canvas": "^3.1.0",
|
|
126
87
|
"pino": ">=7.0.0",
|
|
127
|
-
"rxjs": "
|
|
128
|
-
}
|
|
88
|
+
"rxjs": "^7.0.0"
|
|
89
|
+
},
|
|
90
|
+
"gitHead": "8c7a9f6b7dbf185d39f5927c779265415fb29bfb"
|
|
129
91
|
}
|
package/rxjs/auditMap.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { Observable, from } from 'rxjs'
|
|
2
|
-
|
|
3
|
-
function auditMapImpl(project) {
|
|
4
|
-
return new Observable((o) => {
|
|
5
|
-
let pendingValue = null
|
|
6
|
-
let hasPendingValue = false
|
|
7
|
-
let isComplete = false
|
|
8
|
-
let abortController = null
|
|
9
|
-
|
|
10
|
-
let innerSubscription = null
|
|
11
|
-
let outerSubscription = null
|
|
12
|
-
|
|
13
|
-
function _error(err) {
|
|
14
|
-
o.error(err)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function _innerComplete() {
|
|
18
|
-
innerSubscription = null
|
|
19
|
-
abortController = null
|
|
20
|
-
|
|
21
|
-
if (hasPendingValue) {
|
|
22
|
-
const value = pendingValue
|
|
23
|
-
pendingValue = null
|
|
24
|
-
hasPendingValue = false
|
|
25
|
-
_tryNext(value)
|
|
26
|
-
} else if (isComplete) {
|
|
27
|
-
o.complete()
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function _innerNext(val) {
|
|
32
|
-
o.next(val)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function _drain() {
|
|
36
|
-
if (!hasPendingValue) {
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
const value = pendingValue
|
|
40
|
-
pendingValue = null
|
|
41
|
-
hasPendingValue = false
|
|
42
|
-
return { value }
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function _tryNext(value) {
|
|
46
|
-
try {
|
|
47
|
-
const result = project(value, {
|
|
48
|
-
get signal() {
|
|
49
|
-
if (!abortController) {
|
|
50
|
-
abortController = new AbortController()
|
|
51
|
-
if (hasPendingValue) {
|
|
52
|
-
abortController.abort()
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return abortController.signal
|
|
56
|
-
},
|
|
57
|
-
_drain,
|
|
58
|
-
})
|
|
59
|
-
const observable = typeof result.then === 'function' ? from(result) : result
|
|
60
|
-
innerSubscription = observable.subscribe({
|
|
61
|
-
next: _innerNext,
|
|
62
|
-
error: _error,
|
|
63
|
-
complete: _innerComplete,
|
|
64
|
-
})
|
|
65
|
-
if (innerSubscription && innerSubscription.closed) {
|
|
66
|
-
innerSubscription = null
|
|
67
|
-
}
|
|
68
|
-
} catch (err) {
|
|
69
|
-
o.error(err)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function _next(value) {
|
|
74
|
-
if (innerSubscription) {
|
|
75
|
-
pendingValue = value
|
|
76
|
-
hasPendingValue = true
|
|
77
|
-
abortController?.abort()
|
|
78
|
-
} else {
|
|
79
|
-
_tryNext(value)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function _complete() {
|
|
84
|
-
isComplete = true
|
|
85
|
-
if (!innerSubscription) {
|
|
86
|
-
o.complete()
|
|
87
|
-
} else {
|
|
88
|
-
abortController?.abort()
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
outerSubscription = this.subscribe({
|
|
93
|
-
next: _next,
|
|
94
|
-
error: _error,
|
|
95
|
-
complete: _complete,
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
return () => {
|
|
99
|
-
if (innerSubscription) {
|
|
100
|
-
innerSubscription.unsubscribe()
|
|
101
|
-
}
|
|
102
|
-
outerSubscription.unsubscribe()
|
|
103
|
-
}
|
|
104
|
-
})
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
Observable.prototype.auditMap = auditMapImpl
|
|
108
|
-
|
|
109
|
-
export default function auditMap(project) {
|
|
110
|
-
return (o) => auditMapImpl.call(o, project)
|
|
111
|
-
}
|
package/rxjs/auditMap.test.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { test } from 'node:test'
|
|
2
|
-
import assert from 'node:assert'
|
|
3
|
-
import auditMap from './auditMap.js'
|
|
4
|
-
import * as rxjs from 'rxjs'
|
|
5
|
-
import tp from 'node:timers/promises'
|
|
6
|
-
|
|
7
|
-
test('auditMap sync', (t) => {
|
|
8
|
-
t.plan(1, { wait: true })
|
|
9
|
-
rxjs
|
|
10
|
-
.of(1, 2, 3)
|
|
11
|
-
.pipe(
|
|
12
|
-
auditMap((val) => rxjs.of(val * 2)),
|
|
13
|
-
rxjs.toArray(),
|
|
14
|
-
)
|
|
15
|
-
.subscribe((val) => {
|
|
16
|
-
t.assert.deepStrictEqual(val, [2, 4, 6])
|
|
17
|
-
})
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
test('auditMap async', (t) => {
|
|
21
|
-
t.plan(1, { wait: true })
|
|
22
|
-
rxjs
|
|
23
|
-
.of(1, 2, 3)
|
|
24
|
-
.pipe(
|
|
25
|
-
auditMap(async (val) => val * 2),
|
|
26
|
-
rxjs.toArray(),
|
|
27
|
-
)
|
|
28
|
-
.subscribe((val) => {
|
|
29
|
-
t.assert.deepStrictEqual(val, [2, 6])
|
|
30
|
-
})
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
test('auditMap drain', (t) => {
|
|
34
|
-
t.plan(1, { wait: true })
|
|
35
|
-
rxjs
|
|
36
|
-
.of(1, 2, 3)
|
|
37
|
-
.pipe(
|
|
38
|
-
auditMap(async (val, { _drain }) => {
|
|
39
|
-
await tp.setTimeout(1)
|
|
40
|
-
return _drain()?.value ?? val
|
|
41
|
-
}),
|
|
42
|
-
rxjs.toArray(),
|
|
43
|
-
)
|
|
44
|
-
.subscribe((val) => {
|
|
45
|
-
t.assert.deepStrictEqual(val, [3])
|
|
46
|
-
})
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
test('auditMap signal', (t) => {
|
|
50
|
-
t.plan(1, { wait: true })
|
|
51
|
-
rxjs
|
|
52
|
-
.of(1, 2, 3)
|
|
53
|
-
.pipe(
|
|
54
|
-
auditMap(async (val, { signal }) => {
|
|
55
|
-
await tp.setTimeout(1)
|
|
56
|
-
assert.strictEqual(signal.aborted, val === 1)
|
|
57
|
-
return val
|
|
58
|
-
}),
|
|
59
|
-
rxjs.toArray(),
|
|
60
|
-
)
|
|
61
|
-
.subscribe((val) => {
|
|
62
|
-
t.assert.deepStrictEqual(val, [1, 3])
|
|
63
|
-
})
|
|
64
|
-
})
|
package/rxjs/combineMap.d.ts
DELETED
package/rxjs/combineMap.js
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { Observable, from, throwError } from 'rxjs'
|
|
2
|
-
|
|
3
|
-
const EMPTY = Object.freeze([])
|
|
4
|
-
|
|
5
|
-
function combineMapImpl(project, equals = (a, b) => a === b) {
|
|
6
|
-
return new Observable((o) => {
|
|
7
|
-
let curr = EMPTY
|
|
8
|
-
let scheduled = false
|
|
9
|
-
let disposed = false
|
|
10
|
-
let dirty = false
|
|
11
|
-
let active = 0
|
|
12
|
-
let empty = 0
|
|
13
|
-
|
|
14
|
-
const _error = (err) => o.error(err)
|
|
15
|
-
|
|
16
|
-
function _update() {
|
|
17
|
-
scheduled = false
|
|
18
|
-
|
|
19
|
-
if (empty > 0) {
|
|
20
|
-
return
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (dirty) {
|
|
24
|
-
dirty = false
|
|
25
|
-
o.next(curr.map(({ value }) => value))
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (active === 0) {
|
|
29
|
-
o.complete()
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function update() {
|
|
34
|
-
if (!scheduled && !disposed) {
|
|
35
|
-
scheduled = true
|
|
36
|
-
queueMicrotask(_update)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
active += 1
|
|
41
|
-
const subscription = this.subscribe({
|
|
42
|
-
next(keys) {
|
|
43
|
-
keys = Array.isArray(keys) ? keys : EMPTY
|
|
44
|
-
|
|
45
|
-
// TODO (perf): Avoid array allocation & copy if nothing has updated.
|
|
46
|
-
const prev = curr
|
|
47
|
-
curr = new Array(keys.length)
|
|
48
|
-
|
|
49
|
-
const prevLen = prev.length
|
|
50
|
-
const currLen = curr.length
|
|
51
|
-
|
|
52
|
-
if (currLen !== prevLen || prev === EMPTY) {
|
|
53
|
-
dirty = true
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
for (let n = 0; n < currLen; ++n) {
|
|
57
|
-
const key = keys[n]
|
|
58
|
-
|
|
59
|
-
if (n < prevLen && prev[n] && equals(prev[n].key, key)) {
|
|
60
|
-
curr[n] = prev[n]
|
|
61
|
-
prev[n] = null
|
|
62
|
-
continue
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
dirty = true
|
|
66
|
-
|
|
67
|
-
// TODO (perf): Guess start index based on n, e.g. n - 1 and n + 1 to check if
|
|
68
|
-
// a key has simply been added or removed.
|
|
69
|
-
const i = prev.findIndex((entry) => entry && equals(entry.key, key))
|
|
70
|
-
|
|
71
|
-
if (i !== -1) {
|
|
72
|
-
curr[n] = prev[i]
|
|
73
|
-
prev[i] = null
|
|
74
|
-
} else {
|
|
75
|
-
let observable
|
|
76
|
-
try {
|
|
77
|
-
observable = from(project(keys[n]))
|
|
78
|
-
} catch (err) {
|
|
79
|
-
observable = throwError(() => err)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const entry = {
|
|
83
|
-
key,
|
|
84
|
-
value: EMPTY,
|
|
85
|
-
subscription: null,
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
empty += 1
|
|
89
|
-
entry.subscription = observable.subscribe({
|
|
90
|
-
next(value) {
|
|
91
|
-
if (entry.value === EMPTY) {
|
|
92
|
-
empty -= 1
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
entry.value = value
|
|
96
|
-
dirty = true
|
|
97
|
-
|
|
98
|
-
update()
|
|
99
|
-
},
|
|
100
|
-
error: _error,
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
active += 1
|
|
104
|
-
entry.subscription.add(() => {
|
|
105
|
-
active -= 1
|
|
106
|
-
|
|
107
|
-
if (entry.value === EMPTY) {
|
|
108
|
-
empty -= 1
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
update()
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
if (disposed) {
|
|
115
|
-
entry.subscription.unsubscribe()
|
|
116
|
-
} else {
|
|
117
|
-
curr[n] = entry
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// TODO (perf): start from index where prev[n] is not null.
|
|
123
|
-
for (let n = 0; n < prevLen; n++) {
|
|
124
|
-
prev[n]?.subscription?.unsubscribe()
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
update()
|
|
128
|
-
},
|
|
129
|
-
complete: () => {
|
|
130
|
-
active -= 1
|
|
131
|
-
update()
|
|
132
|
-
},
|
|
133
|
-
error: _error,
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
return () => {
|
|
137
|
-
disposed = true
|
|
138
|
-
|
|
139
|
-
for (const entry of curr) {
|
|
140
|
-
entry?.subscription?.unsubscribe()
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
subscription.unsubscribe()
|
|
144
|
-
}
|
|
145
|
-
})
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
Observable.prototype.combineMap = combineMapImpl
|
|
149
|
-
|
|
150
|
-
export default function combineMap(project, equals) {
|
|
151
|
-
return (o) => combineMapImpl.call(o, project, equals)
|
|
152
|
-
}
|