@nxtedition/lib 25.1.7 → 26.0.1
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 +2 -3
- package/http.js +102 -1
- package/package.json +1 -7
- package/rxjs/combineMap.test.js +199 -0
- package/util/compare-rev.test.js +113 -0
- package/util/template/index.test.js +29 -0
- package/util/template/nextpressions.test.js +165 -0
package/app.js
CHANGED
|
@@ -29,8 +29,7 @@ import rx from 'rxjs/operators'
|
|
|
29
29
|
import { performance } from 'perf_hooks'
|
|
30
30
|
import hashString from './hash.js'
|
|
31
31
|
import { makeTrace } from './trace.js'
|
|
32
|
-
import compose from '
|
|
33
|
-
import { createServer } from './http.js'
|
|
32
|
+
import { compose, createServer } from './http.js'
|
|
34
33
|
import { json } from 'node:stream/consumers'
|
|
35
34
|
import { monitorEventLoopDelay } from 'node:perf_hooks'
|
|
36
35
|
import xuid from 'xuid'
|
|
@@ -113,7 +112,7 @@ export function makeApp(appConfig, onTerminate) {
|
|
|
113
112
|
}
|
|
114
113
|
} else {
|
|
115
114
|
config = {
|
|
116
|
-
isProduction
|
|
115
|
+
isProduction,
|
|
117
116
|
...cleanAppConfig(appConfig),
|
|
118
117
|
...appConfig,
|
|
119
118
|
}
|
package/http.js
CHANGED
|
@@ -372,13 +372,24 @@ export class IncomingMessage extends http.IncomingMessage {
|
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
export class ServerResponse extends http.ServerResponse {
|
|
375
|
-
#created = 0
|
|
376
375
|
#bytesWritten = 0
|
|
376
|
+
|
|
377
|
+
#created = -1
|
|
377
378
|
#connect = -1
|
|
378
379
|
#headers = -1
|
|
379
380
|
#data = -1
|
|
380
381
|
#end = -1
|
|
381
382
|
|
|
383
|
+
#headersObj = undefined
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* @returns {{
|
|
387
|
+
* connect: number,
|
|
388
|
+
* headers: number,
|
|
389
|
+
* data: number,
|
|
390
|
+
* end: number
|
|
391
|
+
* }}
|
|
392
|
+
*/
|
|
382
393
|
get timing() {
|
|
383
394
|
return {
|
|
384
395
|
connect: this.#connect,
|
|
@@ -388,16 +399,101 @@ export class ServerResponse extends http.ServerResponse {
|
|
|
388
399
|
}
|
|
389
400
|
}
|
|
390
401
|
|
|
402
|
+
/**
|
|
403
|
+
* @returns {number}
|
|
404
|
+
*/
|
|
391
405
|
get bytesWritten() {
|
|
392
406
|
return this.#bytesWritten
|
|
393
407
|
}
|
|
394
408
|
|
|
409
|
+
/**
|
|
410
|
+
* @param {http.IncomingMessage} req
|
|
411
|
+
*/
|
|
395
412
|
constructor(req) {
|
|
396
413
|
super(req)
|
|
397
414
|
|
|
398
415
|
this.#created = performance.now()
|
|
399
416
|
}
|
|
400
417
|
|
|
418
|
+
setHeaders() {
|
|
419
|
+
throw new Error('not supported')
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
appendHeader() {
|
|
423
|
+
throw new Error('not supported')
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* @param {string} key
|
|
428
|
+
* @param {string} val
|
|
429
|
+
*/
|
|
430
|
+
setHeader(key, val) {
|
|
431
|
+
if (this.#headersObj === null) {
|
|
432
|
+
throw new Error('headers already sent')
|
|
433
|
+
} else if (this.#headersObj === undefined) {
|
|
434
|
+
this.#headersObj = { key: val }
|
|
435
|
+
} else {
|
|
436
|
+
this.#headersObj[key] = val
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* @param {string} key
|
|
442
|
+
* @param {string} val
|
|
443
|
+
*/
|
|
444
|
+
removeHeader(key, val) {
|
|
445
|
+
if (this.#headersObj === null) {
|
|
446
|
+
throw new Error('headers already sent')
|
|
447
|
+
} else if (this.#headersObj === undefined) {
|
|
448
|
+
// Do nothing...
|
|
449
|
+
} else {
|
|
450
|
+
delete this.#headersObj[key]
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* @returns {string[]}
|
|
456
|
+
*/
|
|
457
|
+
getHeaderNames() {
|
|
458
|
+
if (this.#headersObj === null) {
|
|
459
|
+
throw new Error('headers already sent')
|
|
460
|
+
} else if (this.#headersObj === undefined) {
|
|
461
|
+
return []
|
|
462
|
+
} else {
|
|
463
|
+
return Object.keys(this.#headersObj)
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* @returns {boolean}
|
|
469
|
+
*/
|
|
470
|
+
get headersSent() {
|
|
471
|
+
return this.#headersObj === null
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* @param {number} statusCode
|
|
476
|
+
* @param {Record<string, string>} [headers]
|
|
477
|
+
* @returns
|
|
478
|
+
*/
|
|
479
|
+
writeHead(statusCode, headers) {
|
|
480
|
+
if (this.#headersObj === null) {
|
|
481
|
+
throw new Error('headers already sent')
|
|
482
|
+
} else if (this.#headersObj === undefined) {
|
|
483
|
+
// Do nothing...
|
|
484
|
+
} else {
|
|
485
|
+
headers = headers ? Object.assign(this.#headersObj, headers) : this.#headersObj
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
this.#headersObj = null
|
|
489
|
+
|
|
490
|
+
return super.writeHead(statusCode, headers)
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* @param {net.Socket} socket
|
|
495
|
+
* @returns
|
|
496
|
+
*/
|
|
401
497
|
assignSocket(socket) {
|
|
402
498
|
if (!this.destroyed) {
|
|
403
499
|
if (this.#connect === -1) {
|
|
@@ -462,6 +558,11 @@ export class ServerResponse extends http.ServerResponse {
|
|
|
462
558
|
return super.end(chunk, encoding, callback)
|
|
463
559
|
}
|
|
464
560
|
|
|
561
|
+
/**
|
|
562
|
+
*
|
|
563
|
+
* @param {Error} [err]
|
|
564
|
+
* @returns
|
|
565
|
+
*/
|
|
465
566
|
destroy(err) {
|
|
466
567
|
if (!this.destroyed) {
|
|
467
568
|
if (this.#end === -1) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/lib",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "26.0.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Robert Nagy <robert.nagy@boffins.se>",
|
|
6
6
|
"type": "module",
|
|
@@ -48,8 +48,6 @@
|
|
|
48
48
|
"yield.js"
|
|
49
49
|
],
|
|
50
50
|
"scripts": {
|
|
51
|
-
"prepublishOnly": "pinst --disable",
|
|
52
|
-
"postpublish": "pinst --enable",
|
|
53
51
|
"test": "node --test-timeout 60000 --test",
|
|
54
52
|
"prepare": "husky"
|
|
55
53
|
},
|
|
@@ -84,7 +82,6 @@
|
|
|
84
82
|
"mitata": "^1.0.34",
|
|
85
83
|
"moment-timezone": "^0.5.48",
|
|
86
84
|
"nconf": "^0.13.0",
|
|
87
|
-
"nested-error-stacks": "^2.1.1",
|
|
88
85
|
"object-hash": "^3.0.0",
|
|
89
86
|
"p-queue": "^8.0.1",
|
|
90
87
|
"pino": "^9.9.0",
|
|
@@ -110,11 +107,8 @@
|
|
|
110
107
|
"eslint-plugin-promise": "^7.2.1",
|
|
111
108
|
"husky": "^9.1.7",
|
|
112
109
|
"lint-staged": "^16.1.5",
|
|
113
|
-
"pinst": "^3.0.0",
|
|
114
110
|
"prettier": "^3.6.2",
|
|
115
111
|
"rxjs": "^7.8.2",
|
|
116
|
-
"send": "^1.1.0",
|
|
117
|
-
"tap": "^21.1.0",
|
|
118
112
|
"typescript-eslint": "^8.40.0"
|
|
119
113
|
},
|
|
120
114
|
"peerDependencies": {
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import combineMap from './combineMap.js'
|
|
3
|
+
import * as rxjs from 'rxjs'
|
|
4
|
+
|
|
5
|
+
test('combineMap sync', (t) => {
|
|
6
|
+
t.plan(1, { wait: true })
|
|
7
|
+
rxjs
|
|
8
|
+
.of([1, 2, 3])
|
|
9
|
+
.pipe(combineMap((val) => rxjs.of(val * 2)))
|
|
10
|
+
.subscribe((val) => {
|
|
11
|
+
t.assert.deepStrictEqual(val, [2, 4, 6])
|
|
12
|
+
})
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('combineMap async', (t) => {
|
|
16
|
+
t.plan(1, { wait: true })
|
|
17
|
+
rxjs
|
|
18
|
+
.of([1, 2, 3])
|
|
19
|
+
.pipe(combineMap(async (val) => val * 2))
|
|
20
|
+
.subscribe((val) => {
|
|
21
|
+
t.assert.deepStrictEqual(val, [2, 4, 6])
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('combineMap empty', (t) => {
|
|
26
|
+
t.plan(1, { wait: true })
|
|
27
|
+
rxjs
|
|
28
|
+
.of([])
|
|
29
|
+
.pipe(combineMap((val) => rxjs.of(val * 2)))
|
|
30
|
+
.subscribe((val) => {
|
|
31
|
+
t.assert.deepStrictEqual(val, [])
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('combineMap throw in resolver', (t) => {
|
|
36
|
+
t.plan(1, { wait: true })
|
|
37
|
+
const _err = new Error('asd')
|
|
38
|
+
rxjs
|
|
39
|
+
.of([1, 2, 3])
|
|
40
|
+
.pipe(
|
|
41
|
+
combineMap((val) => {
|
|
42
|
+
throw _err
|
|
43
|
+
}),
|
|
44
|
+
)
|
|
45
|
+
.subscribe({
|
|
46
|
+
error: (err) => {
|
|
47
|
+
t.assert.strictEqual(err, _err)
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('combineMap throw in source', (t) => {
|
|
53
|
+
t.plan(1, { wait: true })
|
|
54
|
+
const _err = new Error('asd')
|
|
55
|
+
rxjs
|
|
56
|
+
.throwError(() => _err)
|
|
57
|
+
.pipe(combineMap((val) => rxjs.of(val)))
|
|
58
|
+
.subscribe({
|
|
59
|
+
error: (err) => {
|
|
60
|
+
t.assert.strictEqual(err, _err)
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('combineMap bad resolve', (t) => {
|
|
66
|
+
t.plan(1, { wait: true })
|
|
67
|
+
rxjs
|
|
68
|
+
.of([1])
|
|
69
|
+
.pipe(combineMap((val) => val))
|
|
70
|
+
.subscribe({
|
|
71
|
+
error: () => {
|
|
72
|
+
t.assert.ok(true)
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('combineMap no change no tick', (t) => {
|
|
78
|
+
t.plan(1, { wait: true })
|
|
79
|
+
rxjs
|
|
80
|
+
.concat(
|
|
81
|
+
rxjs.timer(10).pipe(rxjs.map(() => [1, 2, 3])),
|
|
82
|
+
rxjs.timer(10).pipe(rxjs.map(() => [1, 2, 3])),
|
|
83
|
+
)
|
|
84
|
+
.pipe(combineMap((val) => rxjs.of(val * 2)))
|
|
85
|
+
.subscribe(() => {
|
|
86
|
+
t.assert.ok(true)
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('combineMap combine in single tick', (t) => {
|
|
91
|
+
t.plan(2, { wait: true })
|
|
92
|
+
rxjs
|
|
93
|
+
.concat(
|
|
94
|
+
rxjs.timer(10).pipe(rxjs.map(() => [1, 2, 3])),
|
|
95
|
+
rxjs.timer(10).pipe(rxjs.map(() => [4, 5, 6])),
|
|
96
|
+
)
|
|
97
|
+
.pipe(combineMap((val) => rxjs.from([val * 2, val * 2])))
|
|
98
|
+
.subscribe(() => {
|
|
99
|
+
t.assert.ok(true)
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('combineLatest completion', (t) => {
|
|
104
|
+
t.plan(1, { wait: true })
|
|
105
|
+
rxjs.combineLatest([1, 2, 3].map((x) => rxjs.of(x))).subscribe({
|
|
106
|
+
complete: () => {
|
|
107
|
+
t.assert.ok(true)
|
|
108
|
+
},
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test('combineMap completion', (t) => {
|
|
113
|
+
t.plan(1, { wait: true })
|
|
114
|
+
rxjs
|
|
115
|
+
.of([1, 2, 3])
|
|
116
|
+
.pipe(combineMap((x) => rxjs.of(x)))
|
|
117
|
+
.subscribe({
|
|
118
|
+
complete: () => {
|
|
119
|
+
t.assert.ok(true)
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('combineLatest no completion', (t) => {
|
|
125
|
+
t.plan(1, { wait: true })
|
|
126
|
+
const subscription = rxjs
|
|
127
|
+
.combineLatest([1, 2, 3].map((x) => rxjs.timer(0, 1e3).pipe(rxjs.map(() => x))))
|
|
128
|
+
.subscribe({
|
|
129
|
+
next: () => {
|
|
130
|
+
t.assert.ok(true)
|
|
131
|
+
},
|
|
132
|
+
complete: () => {
|
|
133
|
+
t.assert.fail()
|
|
134
|
+
},
|
|
135
|
+
})
|
|
136
|
+
t.after(() => subscription.unsubscribe())
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('combineMap no completion', (t) => {
|
|
140
|
+
t.plan(1, { wait: true })
|
|
141
|
+
const subscription = rxjs
|
|
142
|
+
.of([1, 2, 3])
|
|
143
|
+
.pipe(combineMap((x) => rxjs.timer(0, 1e3).pipe(rxjs.map(() => x))))
|
|
144
|
+
.subscribe({
|
|
145
|
+
next: () => {
|
|
146
|
+
t.assert.ok(true)
|
|
147
|
+
},
|
|
148
|
+
complete: () => {
|
|
149
|
+
t.assert.fail()
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
t.after(() => subscription.unsubscribe())
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('combineLatest no value', (t) => {
|
|
156
|
+
t.plan(1, { wait: true })
|
|
157
|
+
rxjs.combineLatest([1, 2, 3].map((x) => rxjs.EMPTY)).subscribe({
|
|
158
|
+
complete: () => {
|
|
159
|
+
t.assert.ok(true)
|
|
160
|
+
},
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
test('combineMap no value', (t) => {
|
|
165
|
+
t.plan(1, { wait: true })
|
|
166
|
+
rxjs
|
|
167
|
+
.of([1, 2, 3])
|
|
168
|
+
.pipe(combineMap((x) => rxjs.EMPTY))
|
|
169
|
+
.subscribe({
|
|
170
|
+
complete: () => {
|
|
171
|
+
t.assert.ok(true)
|
|
172
|
+
},
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
test('combineMap object keys removed', (t) => {
|
|
177
|
+
t.plan(3, { wait: true })
|
|
178
|
+
const a = {}
|
|
179
|
+
const b = {}
|
|
180
|
+
const c = {}
|
|
181
|
+
|
|
182
|
+
let i = 0
|
|
183
|
+
rxjs
|
|
184
|
+
.concat(rxjs.of([a, b, c]), rxjs.of([a, b]).pipe(rxjs.delay(10)))
|
|
185
|
+
.pipe(combineMap((x) => rxjs.of(x)))
|
|
186
|
+
.subscribe({
|
|
187
|
+
next: (value) => {
|
|
188
|
+
if (i === 0) {
|
|
189
|
+
t.assert.deepStrictEqual(value, [a, b, c])
|
|
190
|
+
} else if (i === 1) {
|
|
191
|
+
t.assert.deepStrictEqual(value, [a, b])
|
|
192
|
+
}
|
|
193
|
+
i++
|
|
194
|
+
},
|
|
195
|
+
complete: () => {
|
|
196
|
+
t.assert.ok(true)
|
|
197
|
+
},
|
|
198
|
+
})
|
|
199
|
+
})
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import compareRev from './compare-rev.js'
|
|
4
|
+
|
|
5
|
+
const cases = [
|
|
6
|
+
[null, null, 0],
|
|
7
|
+
[null, '1-00000000000000', -1],
|
|
8
|
+
['1-00000000000000', null, 1],
|
|
9
|
+
['INF-00000000000000', '1-00000000000000', 1],
|
|
10
|
+
['1-00000000000000', 'INF-00000000000000', -1],
|
|
11
|
+
['INF-00000000000000', 'INF-00000000000000', 0],
|
|
12
|
+
['INF-00000000000001', 'INF-00000000000000', 1],
|
|
13
|
+
['INF-00000000000001', 'INF-00000000000002', -1],
|
|
14
|
+
['1-00000000000000', '1-00000000000000', 0],
|
|
15
|
+
['1-00000000000000', '1-00000000000001', -1],
|
|
16
|
+
['1-00000000000001', '1-00000000000000', 1],
|
|
17
|
+
['1-00000000000000', '2-00000000000000', -1],
|
|
18
|
+
['2-00000000000000', '1-00000000000000', 1],
|
|
19
|
+
['1-00000000000000', '02-00000000000000', -1],
|
|
20
|
+
['02-00000000000000', '1-00000000000000', 1],
|
|
21
|
+
['01-00000000000000', '02-00000000000000', -1],
|
|
22
|
+
['02-00000000000000', '01-00000000000000', 1],
|
|
23
|
+
['11-00000000000000', '2-00000000000000', 1],
|
|
24
|
+
['2-00000000000000', '11-00000000000000', -1],
|
|
25
|
+
['837-N', '5763-/h', -1],
|
|
26
|
+
['000000837-N', '5763-/h', -1],
|
|
27
|
+
['1-A', '1-AA', -1],
|
|
28
|
+
['4-UI7WqKrsEZJdA1-_asset', '6-UI7YZdKsENJfhH-_asset', -1],
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
for (const [a, b, r] of cases) {
|
|
32
|
+
await test(`${a} ${b} ${r}`, async (t) => {
|
|
33
|
+
const expected = _compareRev(a, b)
|
|
34
|
+
if (r != null) {
|
|
35
|
+
assert.equal(expected, r) // sanity check
|
|
36
|
+
assert.equal(Math.sign(compareRev(a, b)), r)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
assert.equal(Math.sign(compareRev(a && Buffer.from(a), b)), expected)
|
|
40
|
+
assert.equal(Math.sign(compareRev(a, b && Buffer.from(b))), expected)
|
|
41
|
+
assert.equal(Math.sign(compareRev(a && Buffer.from(a), b && Buffer.from(b))), expected)
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const ascii = Array.from({ length: 128 })
|
|
46
|
+
.map((_, index) => (index > 27 && index < 127 ? String.fromCharCode(index) : null))
|
|
47
|
+
.filter(Boolean)
|
|
48
|
+
.join('')
|
|
49
|
+
const rand = (n) => Math.floor(Math.random() * n)
|
|
50
|
+
const randChar = () => ascii[rand(ascii.length)]
|
|
51
|
+
const randId = () => Array.from({ length: rand(63) + 1 }, randChar).join('')
|
|
52
|
+
const randRev = (other) => {
|
|
53
|
+
const num = other && !rand(3) ? parseInt(other) : rand(10e3)
|
|
54
|
+
let id
|
|
55
|
+
if (other && !rand(3)) {
|
|
56
|
+
id = other.slice(other.indexOf('-') + 1)
|
|
57
|
+
if (rand(3)) {
|
|
58
|
+
id = id.slice(0, rand(id.length - 1) + 1)
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
id = randId()
|
|
62
|
+
}
|
|
63
|
+
return `${String(num).padStart(rand(10), '0')}-${id}`
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
await test('fuzz', async (t) => {
|
|
67
|
+
const N = 1e4
|
|
68
|
+
for (let i = 0; i < N; i++) {
|
|
69
|
+
let a = randRev()
|
|
70
|
+
let b = randRev(a)
|
|
71
|
+
if (rand(2)) {
|
|
72
|
+
;[a, b] = [b, a]
|
|
73
|
+
}
|
|
74
|
+
const r = _compareRev(a, b)
|
|
75
|
+
|
|
76
|
+
await t.test(`${a} ${b} ${r}`, (t) => {
|
|
77
|
+
assert.equal(Math.sign(compareRev(a, b)), r)
|
|
78
|
+
assert.equal(Math.sign(compareRev(a && Buffer.from(a), b)), r)
|
|
79
|
+
assert.equal(Math.sign(compareRev(a, b && Buffer.from(b))), r)
|
|
80
|
+
assert.equal(Math.sign(compareRev(a && Buffer.from(a), b && Buffer.from(b))), r)
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
function _compareRev(a, b) {
|
|
86
|
+
if (!a) {
|
|
87
|
+
return b ? -1 : 0
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!b) {
|
|
91
|
+
return a ? 1 : 0
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (a === b) {
|
|
95
|
+
return 0
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const av = a[0] === 'I' ? Infinity : parseInt(a)
|
|
99
|
+
const bv = b[0] === 'I' ? Infinity : parseInt(b)
|
|
100
|
+
|
|
101
|
+
if (av !== bv) {
|
|
102
|
+
return av > bv ? 1 : -1
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const ar = a.slice(a.indexOf('-') + 1)
|
|
106
|
+
const br = b.slice(b.indexOf('-') + 1)
|
|
107
|
+
|
|
108
|
+
if (ar !== br) {
|
|
109
|
+
return ar > br ? 1 : -1
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return 0
|
|
113
|
+
}
|
|
@@ -50,3 +50,32 @@ test('return function', async () => {
|
|
|
50
50
|
'BARBAZ',
|
|
51
51
|
)
|
|
52
52
|
})
|
|
53
|
+
|
|
54
|
+
test('noop', async (t) => {
|
|
55
|
+
const { resolveTemplate } = makeTemplateCompiler({})
|
|
56
|
+
const x = { foo: 1, bar: {} }
|
|
57
|
+
const y = await resolveTemplate(x)
|
|
58
|
+
assert.strictEqual(x, y)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('simple', async (t) => {
|
|
62
|
+
const { resolveTemplate } = makeTemplateCompiler({})
|
|
63
|
+
const x = { bar: '{{#js $.test }}' }
|
|
64
|
+
const y = await resolveTemplate(x, { test: 1 })
|
|
65
|
+
assert.deepStrictEqual(y, { bar: 1 })
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
// test('cache', async (t) => {
|
|
69
|
+
// const x = '{{#js $.test }}'
|
|
70
|
+
// t.equal(compileTemplate(x), compileTemplate(x))
|
|
71
|
+
// t.end()
|
|
72
|
+
// })
|
|
73
|
+
|
|
74
|
+
test('string concat', async (t) => {
|
|
75
|
+
const { resolveTemplate } = makeTemplateCompiler({})
|
|
76
|
+
const x = '{{#js $.pre }} {{#js $.body}} {{#js $.post}}'
|
|
77
|
+
assert.strictEqual(
|
|
78
|
+
await resolveTemplate(x, { pre: 'pre', body: 'body', post: 'post' }),
|
|
79
|
+
'pre body post',
|
|
80
|
+
)
|
|
81
|
+
})
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { makeTemplateCompiler } from './index.js'
|
|
4
|
+
import * as rxjs from 'rxjs'
|
|
5
|
+
|
|
6
|
+
const { resolveTemplate } = makeTemplateCompiler({
|
|
7
|
+
ds: {
|
|
8
|
+
record: {
|
|
9
|
+
observe: () => rxjs.of({ foo: 'bar' }),
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('hash to int', async () => {
|
|
15
|
+
const val = await resolveTemplate('{{test | hashaint() | mod(128)}}', { test: { foo: '11d1' } })
|
|
16
|
+
assert.strictEqual(Number.isFinite(val), true)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('baseValue', async () => {
|
|
20
|
+
assert.strictEqual(await resolveTemplate('{{test}}', { test: '11d1' }), '11d1')
|
|
21
|
+
assert.strictEqual(await resolveTemplate('{{test}}', { test: rxjs.of('11d1') }), '11d1')
|
|
22
|
+
assert.strictEqual(
|
|
23
|
+
await resolveTemplate('{{test.asd}}', { test: rxjs.of({ asd: '11d1' }) }),
|
|
24
|
+
'11d1',
|
|
25
|
+
)
|
|
26
|
+
assert.strictEqual(
|
|
27
|
+
await resolveTemplate('{{test.asd}}', { test: { asd: rxjs.of('11d1') } }),
|
|
28
|
+
'11d1',
|
|
29
|
+
)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('integer ops', async () => {
|
|
33
|
+
assert.strictEqual(await resolveTemplate('{{test | div(2)}}', { test: '8' }), 4)
|
|
34
|
+
assert.strictEqual(await resolveTemplate('{{test | div(2)}}', { test: 8 }), 4)
|
|
35
|
+
assert.strictEqual(await resolveTemplate('{{test | mul(2)}}', { test: '8' }), 16)
|
|
36
|
+
assert.strictEqual(await resolveTemplate('{{test | mul(2)}}', { test: 8 }), 16)
|
|
37
|
+
assert.strictEqual(await resolveTemplate('{{test | add(2)}}', { test: '10' }), 12)
|
|
38
|
+
assert.strictEqual(await resolveTemplate('{{test | add(2)}}', { test: 10 }), 12)
|
|
39
|
+
assert.strictEqual(await resolveTemplate('{{test | sub(2)}}', { test: '10' }), 8)
|
|
40
|
+
assert.strictEqual(await resolveTemplate('{{test | sub(2)}}', { test: 10 }), 8)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('null undefined args', async () => {
|
|
44
|
+
assert.strictEqual(await resolveTemplate('{{asd | default(null, true)}}', {}), null)
|
|
45
|
+
assert.strictEqual(await resolveTemplate('{{asd | default(undefined, true)}}', {}), undefined)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('path var', async () => {
|
|
49
|
+
assert.strictEqual(await resolveTemplate('{{test.foo}}', { test: { foo: '111' } }), '111')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('replaces strings', async () => {
|
|
53
|
+
assert.strictEqual(await resolveTemplate('{{test}}', { test: '111' }), '111')
|
|
54
|
+
assert.strictEqual(await resolveTemplate('pre{{test}}post', { test: '111' }), 'pre111post')
|
|
55
|
+
assert.strictEqual(
|
|
56
|
+
await resolveTemplate('123{{test}}456{{test}}{{test}}', { test: 'body' }),
|
|
57
|
+
'123body456bodybody',
|
|
58
|
+
)
|
|
59
|
+
assert.strictEqual(
|
|
60
|
+
await resolveTemplate('test{{test.foo}}test{{test.bar.baz}}test', {
|
|
61
|
+
test: { foo: '111', bar: { baz: '222' } },
|
|
62
|
+
}),
|
|
63
|
+
'test111test222test',
|
|
64
|
+
)
|
|
65
|
+
assert.strictEqual(await resolveTemplate('{{ asd | default("te | st")}}', {}), 'te | st')
|
|
66
|
+
assert.strictEqual(await resolveTemplate('{{ asd | default("test\n") }}', {}), 'test\n')
|
|
67
|
+
assert.strictEqual(await resolveTemplate('{{ asd | default("test\n\n") }}', {}), 'test\n\n')
|
|
68
|
+
assert.strictEqual(await resolveTemplate('{{ asd | default("test\r\n") }}', {}), 'test\r\n')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('nested', async () => {
|
|
72
|
+
assert.strictEqual(
|
|
73
|
+
await resolveTemplate('{{ asd | default("{{foo}}") }}', { foo: '"test"' }),
|
|
74
|
+
'"test"',
|
|
75
|
+
)
|
|
76
|
+
assert.strictEqual(await resolveTemplate('{{{{foo}}}}', { test: '111', foo: 'test' }), '111')
|
|
77
|
+
assert.strictEqual(
|
|
78
|
+
await resolveTemplate('f{{oo}}', { test: '111', foo: 'test', oo: 'oo' }),
|
|
79
|
+
'foo',
|
|
80
|
+
)
|
|
81
|
+
assert.strictEqual(
|
|
82
|
+
await resolveTemplate('{{f{{oo}}}}', { test: '111', foo: 'test', oo: 'oo' }),
|
|
83
|
+
'test',
|
|
84
|
+
)
|
|
85
|
+
assert.strictEqual(await resolveTemplate('{{{{foo}}}}', { test: '111', foo: 'test' }), '111')
|
|
86
|
+
assert.strictEqual(
|
|
87
|
+
await resolveTemplate('{{{{f{{o}}o}}}}', { test: '111', foo: 'test', o: 'o' }),
|
|
88
|
+
'111',
|
|
89
|
+
)
|
|
90
|
+
assert.strictEqual(
|
|
91
|
+
await resolveTemplate('{{ asd | default("{{test}}")}}', { test: '111', foo: 'test' }),
|
|
92
|
+
'111',
|
|
93
|
+
)
|
|
94
|
+
assert.strictEqual(
|
|
95
|
+
await resolveTemplate('{{ asd | default("{{t{{es}}t}}")}}', {
|
|
96
|
+
test: '111',
|
|
97
|
+
foo: 'test',
|
|
98
|
+
es: 'es',
|
|
99
|
+
}),
|
|
100
|
+
'111',
|
|
101
|
+
)
|
|
102
|
+
assert.strictEqual(
|
|
103
|
+
await resolveTemplate('{{ asd | default("{{test | default("test\n")}}")}}', {}),
|
|
104
|
+
'test\n',
|
|
105
|
+
)
|
|
106
|
+
assert.strictEqual(
|
|
107
|
+
await resolveTemplate('{{ asd | default("{{test | default("test\n\n")}}")}}', {}),
|
|
108
|
+
'test\n\n',
|
|
109
|
+
)
|
|
110
|
+
assert.strictEqual(
|
|
111
|
+
await resolveTemplate('{{ asd | default("{{test | default("test\r\n")}}")}}', {}),
|
|
112
|
+
'test\r\n',
|
|
113
|
+
)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('append', async () => {
|
|
117
|
+
assert.strictEqual(await resolveTemplate("{{test | append('1')}}", { test: '111' }), '1111')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test('object', async () => {
|
|
121
|
+
const obj = { foo: 1 }
|
|
122
|
+
assert.strictEqual(await resolveTemplate('{{test}}', { test: obj }), obj)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test('ds', async () => {
|
|
126
|
+
assert.strictEqual(
|
|
127
|
+
await resolveTemplate("{{test | ds() | pluck('foo')}}", { test: 'foo' }),
|
|
128
|
+
'bar',
|
|
129
|
+
)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('replace array', async () => {
|
|
133
|
+
assert.deepStrictEqual(
|
|
134
|
+
await resolveTemplate('{{test | join("#") | replace("foo", "bar") | split("#")}}', {
|
|
135
|
+
test: ['foo', 'bar'],
|
|
136
|
+
}),
|
|
137
|
+
['bar', 'bar'],
|
|
138
|
+
)
|
|
139
|
+
assert.deepStrictEqual(
|
|
140
|
+
await resolveTemplate('{{test | join(",") | replace("foo", "bar") | split(",")}}', {
|
|
141
|
+
test: ['foo', 'bar'],
|
|
142
|
+
}),
|
|
143
|
+
['bar', 'bar'],
|
|
144
|
+
)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
test('You Do Not Know Me', async () => {
|
|
148
|
+
assert.deepStrictEqual(
|
|
149
|
+
await resolveTemplate('{{id | default("You Do Not Know", true)}} -', {}),
|
|
150
|
+
'You Do Not Know -',
|
|
151
|
+
)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('object 1', async () => {
|
|
155
|
+
assert.deepStrictEqual(await resolveTemplate({ asd: ['{{foo}}'] }, { foo: 'bar' }), {
|
|
156
|
+
asd: ['bar'],
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
test('empty arg', async () => {
|
|
161
|
+
assert.deepStrictEqual(
|
|
162
|
+
await resolveTemplate('{{source.value | includes("salami") | ternary([], )}}', {}),
|
|
163
|
+
undefined,
|
|
164
|
+
)
|
|
165
|
+
})
|