lib0 1.0.0-rc.21 → 1.0.0-rc.23

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 (62) hide show
  1. package/dist/array.d.ts +4 -4
  2. package/dist/broadcastchannel.d.ts +3 -3
  3. package/dist/decoding.d.ts +3 -3
  4. package/dist/delta/delta.d.ts +0 -8
  5. package/dist/delta/rdt/dom.d.ts +19 -25
  6. package/dist/delta/rdt.d.ts +24 -0
  7. package/dist/diff.d.ts +1 -1
  8. package/dist/encoding.d.ts +4 -4
  9. package/dist/environment.common.d.ts +21 -0
  10. package/dist/environment.d.ts +2 -8
  11. package/dist/error.d.ts +21 -3
  12. package/dist/eventloop.d.ts +1 -1
  13. package/dist/indexeddb.d.ts +3 -3
  14. package/dist/indexeddbV2.d.ts +3 -3
  15. package/dist/iterator.d.ts +3 -3
  16. package/dist/list.d.ts +6 -6
  17. package/dist/logging.common.d.ts +1 -1
  18. package/dist/logging.d.ts +1 -1
  19. package/dist/logging.node.d.ts +1 -1
  20. package/dist/map.d.ts +4 -4
  21. package/dist/object.d.ts +2 -2
  22. package/dist/observable.d.ts +2 -2
  23. package/dist/pair.d.ts +2 -2
  24. package/dist/pledge.d.ts +10 -10
  25. package/dist/promise.d.ts +2 -2
  26. package/dist/schema.d.ts +19 -1
  27. package/dist/sort.d.ts +3 -3
  28. package/dist/storage.d.ts +2 -2
  29. package/dist/testing.d.ts +18 -11
  30. package/package.json +3 -3
  31. package/src/array.js +5 -5
  32. package/src/broadcastchannel.js +4 -4
  33. package/src/cache.js +1 -1
  34. package/src/decoding.js +2 -2
  35. package/src/delta/delta.js +13 -26
  36. package/src/delta/rdt/dom.js +176 -63
  37. package/src/delta/rdt.js +39 -10
  38. package/src/diff.js +1 -1
  39. package/src/encoding.js +2 -2
  40. package/src/environment.common.js +41 -0
  41. package/src/environment.js +2 -21
  42. package/src/error.js +10 -4
  43. package/src/eventloop.js +3 -3
  44. package/src/function.js +1 -1
  45. package/src/indexeddb.js +4 -4
  46. package/src/indexeddbV2.js +4 -4
  47. package/src/iterator.js +4 -4
  48. package/src/list.js +4 -4
  49. package/src/logging.common.js +3 -3
  50. package/src/logging.js +3 -3
  51. package/src/logging.node.js +3 -3
  52. package/src/map.js +4 -4
  53. package/src/mutex.js +2 -2
  54. package/src/object.js +2 -2
  55. package/src/observable.js +1 -1
  56. package/src/pair.js +2 -2
  57. package/src/pledge.js +6 -6
  58. package/src/promise.js +3 -3
  59. package/src/schema.js +316 -3
  60. package/src/sort.js +4 -4
  61. package/src/storage.js +8 -4
  62. package/src/testing.js +15 -15
package/src/promise.js CHANGED
@@ -14,13 +14,13 @@ import * as time from './time.js'
14
14
 
15
15
  /**
16
16
  * @template T
17
- * @param {function(PromiseResolve<T>,function(Error):void):any} f
17
+ * @param {(resolve: PromiseResolve<T>, reject: (err: Error) => void) => any} f
18
18
  * @return {Promise<T>}
19
19
  */
20
20
  export const create = f => /** @type {Promise<T>} */ (new Promise(f))
21
21
 
22
22
  /**
23
- * @param {function(function():void,function(Error):void):void} f
23
+ * @param {(resolve: () => void, reject: (err: Error) => void) => void} f
24
24
  * @return {Promise<void>}
25
25
  */
26
26
  export const createEmpty = f => new Promise(f)
@@ -59,7 +59,7 @@ export const resolveWith = res => Promise.resolve(res)
59
59
  * @deprecated use untilAsync instead
60
60
  *
61
61
  * @param {number} timeout
62
- * @param {function():boolean} check
62
+ * @param {() => boolean} check
63
63
  * @param {number} [intervalResolution]
64
64
  * @return {Promise<void>}
65
65
  */
package/src/schema.js CHANGED
@@ -228,7 +228,7 @@ export class Schema {
228
228
  * Can be useful when defining lambdas: `s.lambda(s.$number, s.$number).expect((n) => n + 1)`
229
229
  *
230
230
  * @param {T} o
231
- * @return {o extends T ? T : never}
231
+ * @return {T extends T ? T : never}
232
232
  */
233
233
  expect (o) {
234
234
  assert(o, this)
@@ -1058,8 +1058,8 @@ export const $primitive = $union($number, $string, $null, $undefined, $bigint, $
1058
1058
  * @type {Schema<null|number|string|boolean|JSON[]|{[key:string]:JSON}>}
1059
1059
  */
1060
1060
  export const $json = /* @__PURE__ */(() => {
1061
- const $jsonArr = /** @type {$Array<$any>} */ ($array($any))
1062
- const $jsonRecord = /** @type {$Record<$string,$any>} */ ($record($string, $any))
1061
+ const $jsonArr = /** @type {$Array<typeof $any>} */ ($array($any))
1062
+ const $jsonRecord = /** @type {$Record<typeof $string,typeof $any>} */ ($record($string, $any))
1063
1063
  const $json = $union($number, $string, $null, $boolean, $jsonArr, $jsonRecord)
1064
1064
  $jsonArr.shape = $json
1065
1065
  $jsonRecord.shape.values = $json
@@ -1241,3 +1241,316 @@ const _random = /* @__PURE__ */ (() => match({ gen: /** @type {Schema<prng.PRNG>
1241
1241
  */
1242
1242
  /* @__NO_SIDE_EFFECTS__ */
1243
1243
  export const random = (gen, schema, fallback) => /* @__PURE__ */_random($(schema), { gen, fallback })
1244
+
1245
+ /**
1246
+ * Carries the reason of a failed coercion out of the recursion.
1247
+ *
1248
+ * @typedef {{ err: string }} _CoerceCtx
1249
+ */
1250
+
1251
+ /**
1252
+ * A compiled coercer. Returns the coerced value, or `_failed` after writing the reason to
1253
+ * `ctx.err`.
1254
+ *
1255
+ * @typedef {(o:any, path:string, ctx:_CoerceCtx) => any} _Coercer
1256
+ */
1257
+
1258
+ const _failed = Symbol('schema:coercion failed')
1259
+
1260
+ const _bigintRegex = /^[+-]?\d+$/
1261
+
1262
+ /**
1263
+ * Render a value for an error message. Objects are only rendered by kind - printing them adds
1264
+ * noise (and `String` throws on null-prototype objects).
1265
+ *
1266
+ * @param {any} o
1267
+ * @return {string}
1268
+ */
1269
+ /* @__NO_SIDE_EFFECTS__ */
1270
+ const _show = o => typeof o === 'string'
1271
+ ? JSON.stringify(o)
1272
+ : (o !== null && object.isObject(o) ? (arr.isArray(o) ? 'array' : 'object') : String(o))
1273
+
1274
+ /**
1275
+ * @param {_CoerceCtx} ctx
1276
+ * @param {string} path
1277
+ * @param {any} o
1278
+ * @param {string} expected
1279
+ * @return {typeof _failed}
1280
+ */
1281
+ const _fail = (ctx, path, o, expected) => {
1282
+ ctx.err = `${path === '' ? '' : `[${path}] `}${_show(o)} doesn't match ${expected}`
1283
+ return _failed
1284
+ }
1285
+
1286
+ /**
1287
+ * @param {string} path
1288
+ * @param {string} key
1289
+ */
1290
+ /* @__NO_SIDE_EFFECTS__ */
1291
+ const _prop = (path, key) => path === '' ? key : `${path}.${key}`
1292
+
1293
+ /**
1294
+ * @param {string} path
1295
+ * @param {number} i
1296
+ */
1297
+ /* @__NO_SIDE_EFFECTS__ */
1298
+ const _idx = (path, i) => path === '' ? `${i}` : `${path}[${i}]`
1299
+
1300
+ /**
1301
+ * `$$x.check` is a type predicate. Dispatching through this indirection keeps `$s` from being
1302
+ * narrowed to `never` in the negative branches of the coercer dispatch.
1303
+ *
1304
+ * @param {Schema<any>} $meta
1305
+ * @param {Schema<any>} $s
1306
+ * @return {boolean}
1307
+ */
1308
+ const _isMeta = ($meta, $s) => $meta.check($s)
1309
+
1310
+ /**
1311
+ * A short, human readable name for a schema - used in coercion error messages.
1312
+ *
1313
+ * Only `$union` & `$optional` are descended into (`$union` flattens nested unions, so this
1314
+ * terminates). Container shapes are *not* descended into - that's what makes this safe on
1315
+ * self-referential schemas like `$json`.
1316
+ *
1317
+ * @param {Schema<any>} $s
1318
+ * @return {string}
1319
+ */
1320
+ const _nameOf = $s => {
1321
+ const shape = /** @type {any} */ ($s).shape
1322
+ if (_isMeta($$any, $s)) return 'any'
1323
+ if (_isMeta($$string, $s)) return 'string'
1324
+ if (_isMeta($$number, $s)) return 'number'
1325
+ if (_isMeta($$bigint, $s)) return 'bigint'
1326
+ if (_isMeta($$boolean, $s)) return 'boolean'
1327
+ if (_isMeta($$symbol, $s)) return 'symbol'
1328
+ if (_isMeta($$literal, $s) || _isMeta($$null, $s) || _isMeta($$undefined, $s)) {
1329
+ return /** @type {Array<Primitive>} */ (shape).map(l => String(l)).join(' | ')
1330
+ }
1331
+ if (_isMeta($$optional, $s)) return `${_nameOf(shape)} | undefined`
1332
+ if (_isMeta($$union, $s)) return /** @type {Array<Schema<any>>} */ (shape).map(_nameOf).join(' | ')
1333
+ if (_isMeta($$object, $s)) return 'object'
1334
+ if (_isMeta($$record, $s)) return 'record'
1335
+ if (_isMeta($$tuple, $s)) return 'tuple'
1336
+ if (_isMeta($$array, $s)) return 'array'
1337
+ // `instanceof` instead of the meta schemas: `$uint8Array` & `$promise` overwrite their inherited
1338
+ // `$type` with a more specific one, so `_isMeta($$constructedBy, ..)` doesn't hold for them
1339
+ if ($s instanceof $ConstructedBy || $s instanceof $InstanceOf) return shape.name
1340
+ return $s.constructor.name
1341
+ }
1342
+
1343
+ /**
1344
+ * Compile a coercer for `$s`. Dispatches on the meta schemas (`$$string`, `$$object`, ..) which are
1345
+ * an exact identity check. The order matters: `$string` & friends are `$Custom` instances and
1346
+ * `$null` & `$undefined` are `$Literal` instances that are only distinguishable by their `$type`.
1347
+ *
1348
+ * @param {Schema<any>} $s
1349
+ * @param {Map<Schema<any>,_Coercer>} cache
1350
+ * @return {_Coercer}
1351
+ */
1352
+ const _createCoercer = ($s, cache) => {
1353
+ // `shape` is only defined on the subclasses - see the comment on `Schema`
1354
+ const shape = /** @type {any} */ ($s).shape
1355
+ const expected = _nameOf($s)
1356
+ if (_isMeta($$any, $s)) {
1357
+ return o => o
1358
+ }
1359
+ if (_isMeta($$string, $s)) {
1360
+ return (o, path, ctx) => {
1361
+ const t = typeof o
1362
+ return t === 'string' ? o : (t === 'number' || t === 'bigint' || t === 'boolean' ? o + '' : _fail(ctx, path, o, expected))
1363
+ }
1364
+ }
1365
+ if (_isMeta($$number, $s)) {
1366
+ return (o, path, ctx) => {
1367
+ const t = typeof o
1368
+ if (t === 'number') return o
1369
+ if (t === 'boolean' || t === 'bigint' || (t === 'string' && o.trim() !== '')) {
1370
+ const n = Number(o)
1371
+ if (!number.isNaN(n)) return n
1372
+ }
1373
+ return _fail(ctx, path, o, expected)
1374
+ }
1375
+ }
1376
+ if (_isMeta($$bigint, $s)) {
1377
+ return (o, path, ctx) => {
1378
+ const t = typeof o
1379
+ return t === 'bigint'
1380
+ ? o
1381
+ : ((t === 'number' && number.isInteger(o)) || (t === 'string' && _bigintRegex.test(o))
1382
+ ? BigInt(o)
1383
+ : _fail(ctx, path, o, expected))
1384
+ }
1385
+ }
1386
+ if (_isMeta($$boolean, $s)) {
1387
+ return (o, path, ctx) => typeof o === 'boolean'
1388
+ ? o
1389
+ : (o === 'true' ? true : (o === 'false' ? false : _fail(ctx, path, o, expected)))
1390
+ }
1391
+ if (_isMeta($$literal, $s) || _isMeta($$null, $s) || _isMeta($$undefined, $s)) {
1392
+ const literals = /** @type {Array<Primitive>} */ (shape)
1393
+ return (o, path, ctx) => {
1394
+ if (arr.some(literals, l => l === o)) return o
1395
+ if (typeof o === 'string') {
1396
+ const i = literals.findIndex(l => String(l) === o)
1397
+ if (i >= 0) return literals[i]
1398
+ }
1399
+ return _fail(ctx, path, o, expected)
1400
+ }
1401
+ }
1402
+ if (_isMeta($$optional, $s)) {
1403
+ const c = _buildCoercer(shape, cache)
1404
+ return (o, path, ctx) => o === undefined ? undefined : c(o, path, ctx)
1405
+ }
1406
+ if (_isMeta($$union, $s)) {
1407
+ const members = /** @type {Array<Schema<any>>} */ (shape)
1408
+ const cs = members.map($m => _buildCoercer($m, cache))
1409
+ return (o, path, ctx) => {
1410
+ // a value that already matches one of the members is never converted
1411
+ if (arr.some(members, $m => $m.check(o))) return o
1412
+ for (let i = 0; i < cs.length; i++) {
1413
+ const v = cs[i](o, path, ctx)
1414
+ if (v !== _failed) return v
1415
+ }
1416
+ return _fail(ctx, path, o, expected)
1417
+ }
1418
+ }
1419
+ if (_isMeta($$object, $s)) {
1420
+ const isPartial = /** @type {any} */ ($s)._isPartial
1421
+ /**
1422
+ * @type {Array<{ key: string, skippable: boolean, c: _Coercer }>}
1423
+ */
1424
+ const props = []
1425
+ for (const key in shape) {
1426
+ props.push({ key, skippable: isPartial || $$optional.check(shape[key]), c: _buildCoercer(shape[key], cache) })
1427
+ }
1428
+ return (o, path, ctx) => {
1429
+ if (o === null || !object.isObject(o)) return _fail(ctx, path, o, expected)
1430
+ let changed = false
1431
+ /**
1432
+ * @type {any}
1433
+ */
1434
+ const res = {}
1435
+ for (let i = 0; i < props.length; i++) {
1436
+ const { key, skippable, c } = props[i]
1437
+ if (skippable && !object.hasProperty(o, key)) continue
1438
+ const v = c(o[key], _prop(path, key), ctx)
1439
+ if (v === _failed) return _failed
1440
+ changed = changed || v !== o[key]
1441
+ res[key] = v
1442
+ }
1443
+ // unknown props are preserved - `$Object` accepts them as well
1444
+ return changed ? object.assign({}, o, res) : o
1445
+ }
1446
+ }
1447
+ if (_isMeta($$record, $s)) {
1448
+ const ckey = _buildCoercer(shape.keys, cache)
1449
+ const cval = _buildCoercer(shape.values, cache)
1450
+ return (o, path, ctx) => {
1451
+ if (o === null || !object.isObject(o)) return _fail(ctx, path, o, expected)
1452
+ let changed = false
1453
+ /**
1454
+ * @type {any}
1455
+ */
1456
+ const res = {}
1457
+ for (const key in o) {
1458
+ const p = _prop(path, key)
1459
+ // keys are coerced only to validate them - the property name stays a string either way
1460
+ if (ckey(key, p, ctx) === _failed) return _failed
1461
+ const v = cval(o[key], p, ctx)
1462
+ if (v === _failed) return _failed
1463
+ changed = changed || v !== o[key]
1464
+ res[key] = v
1465
+ }
1466
+ return changed ? res : o
1467
+ }
1468
+ }
1469
+ if (_isMeta($$tuple, $s)) {
1470
+ const cs = /** @type {Array<Schema<any>>} */ (shape).map($m => _buildCoercer($m, cache))
1471
+ return (o, path, ctx) => {
1472
+ if (!arr.isArray(o)) return _fail(ctx, path, o, expected)
1473
+ let changed = false
1474
+ // `$Tuple` doesn't constrain the length - additional items are preserved
1475
+ const res = o.slice()
1476
+ for (let i = 0; i < cs.length; i++) {
1477
+ const v = cs[i](o[i], _idx(path, i), ctx)
1478
+ if (v === _failed) return _failed
1479
+ changed = changed || v !== o[i]
1480
+ res[i] = v
1481
+ }
1482
+ return changed ? res : o
1483
+ }
1484
+ }
1485
+ if (_isMeta($$array, $s)) {
1486
+ const c = _buildCoercer(shape, cache)
1487
+ return (o, path, ctx) => {
1488
+ if (!arr.isArray(o)) return _fail(ctx, path, o, expected)
1489
+ let changed = false
1490
+ const res = new Array(o.length)
1491
+ for (let i = 0; i < o.length; i++) {
1492
+ const v = c(o[i], _idx(path, i), ctx)
1493
+ if (v === _failed) return _failed
1494
+ changed = changed || v !== o[i]
1495
+ res[i] = v
1496
+ }
1497
+ return changed ? res : o
1498
+ }
1499
+ }
1500
+ // everything else ($instanceOf, $constructedBy, $custom, $lambda, $intersect, $stringTemplate,
1501
+ // $type, ..) has no meaningful conversion - accept the value iff it already matches
1502
+ return (o, path, ctx) => $s.check(o) ? o : _fail(ctx, path, o, expected)
1503
+ }
1504
+
1505
+ /**
1506
+ * Compile with memoization. The coercer is registered *before* recursing so that self-referential
1507
+ * schemas (e.g. `$json`) terminate.
1508
+ *
1509
+ * @param {Schema<any>} $s
1510
+ * @param {Map<Schema<any>,_Coercer>} cache
1511
+ * @return {_Coercer}
1512
+ */
1513
+ const _buildCoercer = ($s, cache) => {
1514
+ const cached = cache.get($s)
1515
+ if (cached != null) return cached
1516
+ // `self` is a box because the registered forwarder must close over a coercer that doesn't exist
1517
+ // yet
1518
+ /**
1519
+ * @type {Array<_Coercer>}
1520
+ */
1521
+ const self = []
1522
+ cache.set($s, (o, path, ctx) => self[0](o, path, ctx))
1523
+ self.push(_createCoercer($s, cache))
1524
+ return self[0]
1525
+ }
1526
+
1527
+ /**
1528
+ * Compile a coercion function for `schema`.
1529
+ *
1530
+ * Unlike `check` (which only validates) a coercion converts values that don't match yet - `'42'`
1531
+ * becomes `42` for `$number`, `'true'` becomes `true` for `$boolean`. Values that already match
1532
+ * are returned untouched (including object identity). Containers (`$object`, `$array`, `$record`,
1533
+ * `$tuple`, `$union`, `$optional`) are coerced recursively. Schemas without a meaningful
1534
+ * conversion (`$instanceOf`, `$custom`, ..) simply have to match.
1535
+ *
1536
+ * Failures are returned instead of thrown - this is the only api in lib0 that reports errors as a
1537
+ * value.
1538
+ *
1539
+ * @example
1540
+ * const readConfig = coerce($object({ port: $number, dev: $boolean }))
1541
+ * readConfig({ port: '8080', dev: 'true' }) // => { err: null, result: { port: 8080, dev: true } }
1542
+ * readConfig({ port: 'x', dev: 'true' }) // => { err: '[port] "x" doesn\'t match number', result: null }
1543
+ *
1544
+ * @template {Schema<any>} S
1545
+ * @param {S} schema
1546
+ * @return {(o:any) => { err: string, result: null } | { err: null, result: Unwrap<S> }}
1547
+ */
1548
+ /* @__NO_SIDE_EFFECTS__ */
1549
+ export const coerce = schema => {
1550
+ const c = _buildCoercer(schema, new Map())
1551
+ return o => {
1552
+ const ctx = { err: '' }
1553
+ const res = c(o, '', ctx)
1554
+ return res === _failed ? { err: ctx.err, result: null } : { err: null, result: res }
1555
+ }
1556
+ }
package/src/sort.js CHANGED
@@ -14,7 +14,7 @@ import * as math from './math.js'
14
14
  * @param {Array<T>} arr
15
15
  * @param {number} lo
16
16
  * @param {number} hi
17
- * @param {function(T,T):number} compare
17
+ * @param {(a: T, b: T) => number} compare
18
18
  */
19
19
  export const _insertionSort = (arr, lo, hi, compare) => {
20
20
  for (let i = lo + 1; i <= hi; i++) {
@@ -29,7 +29,7 @@ export const _insertionSort = (arr, lo, hi, compare) => {
29
29
  /**
30
30
  * @template T
31
31
  * @param {Array<T>} arr
32
- * @param {function(T,T):number} compare
32
+ * @param {(a: T, b: T) => number} compare
33
33
  * @return {void}
34
34
  */
35
35
  export const insertionSort = (arr, compare) => {
@@ -41,7 +41,7 @@ export const insertionSort = (arr, compare) => {
41
41
  * @param {Array<T>} arr
42
42
  * @param {number} lo
43
43
  * @param {number} hi
44
- * @param {function(T,T):number} compare
44
+ * @param {(a: T, b: T) => number} compare
45
45
  */
46
46
  const _quickSort = (arr, lo, hi, compare) => {
47
47
  if (hi - lo < 42) {
@@ -80,7 +80,7 @@ const _quickSort = (arr, lo, hi, compare) => {
80
80
  *
81
81
  * @template T
82
82
  * @param {Array<T>} arr
83
- * @param {function(T,T):number} compare
83
+ * @param {(a: T, b: T) => number} compare
84
84
  * @return {void}
85
85
  */
86
86
  export const quicksort = (arr, compare) => {
package/src/storage.js CHANGED
@@ -8,6 +8,8 @@
8
8
  * @module storage
9
9
  */
10
10
 
11
+ import { isBrowser } from './environment.common.js'
12
+
11
13
  /* c8 ignore start */
12
14
  class VarStoragePolyfill {
13
15
  constructor () {
@@ -39,8 +41,10 @@ let usePolyfill = true
39
41
 
40
42
  /* c8 ignore start */
41
43
  try {
42
- // if the same-origin rule is violated, accessing localStorage might thrown an error
43
- if (typeof localStorage !== 'undefined' && localStorage) {
44
+ // Only use localStorage in the browser — node & deno also define a localStorage global, but
45
+ // with different semantics (in node it is non-functional unless `--localstorage-file` is set).
46
+ // If the same-origin rule is violated, accessing localStorage might throw an error.
47
+ if (isBrowser && typeof localStorage !== 'undefined' && localStorage) {
44
48
  _localStorage = localStorage
45
49
  usePolyfill = false
46
50
  }
@@ -56,7 +60,7 @@ export const varStorage = _localStorage
56
60
  /**
57
61
  * A polyfill for `addEventListener('storage', event => {..})` that does nothing if the polyfill is being used.
58
62
  *
59
- * @param {function({ key: string, newValue: string, oldValue: string }): void} eventHandler
63
+ * @param {(e: { key: string, newValue: string, oldValue: string }) => void} eventHandler
60
64
  * @function
61
65
  */
62
66
  /* c8 ignore next */
@@ -65,7 +69,7 @@ export const onChange = eventHandler => usePolyfill || addEventListener('storage
65
69
  /**
66
70
  * A polyfill for `removeEventListener('storage', event => {..})` that does nothing if the polyfill is being used.
67
71
  *
68
- * @param {function({ key: string, newValue: string, oldValue: string }): void} eventHandler
72
+ * @param {(e: { key: string, newValue: string, oldValue: string }) => void} eventHandler
69
73
  * @function
70
74
  */
71
75
  /* c8 ignore next */
package/src/testing.js CHANGED
@@ -133,7 +133,7 @@ const repeatTestRegex = /^(repeat|repeating)\s/
133
133
  /**
134
134
  * @param {string} moduleName
135
135
  * @param {string} name
136
- * @param {function(TestCase):void|Promise<any>} f
136
+ * @param {(tc: TestCase) => void|Promise<any>} f
137
137
  * @param {number} i
138
138
  * @param {number} numberOfTests
139
139
  */
@@ -257,7 +257,7 @@ export const printCanvas = log.printCanvas
257
257
  * ```
258
258
  *
259
259
  * @param {string} description
260
- * @param {function(...any):void} f
260
+ * @param {(...args: Array<any>) => void} f
261
261
  */
262
262
  export const group = (description, f) => {
263
263
  log.group(log.BLUE, description)
@@ -284,7 +284,7 @@ export const group = (description, f) => {
284
284
  * ```
285
285
  *
286
286
  * @param {string} description
287
- * @param {function(...any):Promise<any>} f
287
+ * @param {(...args: Array<any>) => Promise<any>} f
288
288
  */
289
289
  export const groupAsync = async (description, f) => {
290
290
  log.group(log.BLUE, description)
@@ -310,7 +310,7 @@ export const groupAsync = async (description, f) => {
310
310
  * ```
311
311
  *
312
312
  * @param {string} message
313
- * @param {function(...any):void} f
313
+ * @param {(...args: Array<any>) => void} f
314
314
  * @return {number} Returns a promise that resolves the measured duration to apply f
315
315
  */
316
316
  export const measureTime = (message, f) => {
@@ -340,7 +340,7 @@ export const measureTime = (message, f) => {
340
340
  * ```
341
341
  *
342
342
  * @param {string} message
343
- * @param {function(...any):Promise<any>} f
343
+ * @param {(...args: Array<any>) => Promise<any>} f
344
344
  * @return {Promise<number>} Returns a promise that resolves the measured duration to apply f
345
345
  */
346
346
  export const measureTimeAsync = async (message, f) => {
@@ -428,7 +428,7 @@ const _failMessage = (message, reason, path) => fail(
428
428
  * @param {any} b
429
429
  * @param {string} path
430
430
  * @param {string?} message
431
- * @param {function(any,any,any,string,any):boolean} customCompare
431
+ * @param {(constructor: any, a: any, b: any, path: string, compareValues: any) => boolean} customCompare
432
432
  */
433
433
  const _compare = (a, b, path, message, customCompare) => {
434
434
  // we don't use assert here because we want to test all branches (istanbul errors if one branch is not tested)
@@ -519,22 +519,22 @@ const _compare = (a, b, path, message, customCompare) => {
519
519
  * @param {T} a
520
520
  * @param {T} b
521
521
  * @param {string?} [message]
522
- * @param {function(any,T,T,string,any):boolean} [customCompare]
522
+ * @param {(constructor: any, a: T, b: T, path: string, compareValues: any) => boolean} [customCompare]
523
523
  */
524
524
  export const compare = (a, b, message = null, customCompare = compareValues) => _compare(a, b, 'obj', message, customCompare)
525
525
 
526
526
  /**
527
- * @template T
528
- * @param {T} property
529
- * @param {string?} [message]
530
- * @return {asserts property is NonNullable<T>}
527
+ * `@type` (not `@param`/`@return`) is required — an assertion signature only narrows if the
528
+ * callee is declared with an explicit type annotation.
529
+ *
531
530
  * @throws {TestError}
531
+ * @type {<T>(property: T, message?: string|null) => asserts property is NonNullable<T>}
532
532
  */
533
533
  /* c8 ignore next */
534
534
  export const assert = (property, message = null) => { property || fail(`Assertion failed${message !== null ? `: ${message}` : ''}`) }
535
535
 
536
536
  /**
537
- * @param {function(...any):Promise<any>} f
537
+ * @param {(...args: Array<any>) => Promise<any>} f
538
538
  */
539
539
  export const promiseRejected = async f => {
540
540
  try {
@@ -546,7 +546,7 @@ export const promiseRejected = async f => {
546
546
  }
547
547
 
548
548
  /**
549
- * @param {function(...any):void} f
549
+ * @param {(...args: Array<any>) => void} f
550
550
  * @throws {TestError}
551
551
  */
552
552
  export const fails = f => {
@@ -560,7 +560,7 @@ export const fails = f => {
560
560
  }
561
561
 
562
562
  /**
563
- * @param {function(...any):Promise<any>} f
563
+ * @param {(...args: Array<any>) => Promise<any>} f
564
564
  * @throws {TestError}
565
565
  */
566
566
  export const failsAsync = async f => {
@@ -574,7 +574,7 @@ export const failsAsync = async f => {
574
574
  }
575
575
 
576
576
  /**
577
- * @param {Object<string, Object<string, function(TestCase):any|Promise<any>>>} tests
577
+ * @param {Object<string, Object<string, (tc: TestCase) => any|Promise<any>>>} tests
578
578
  */
579
579
  export const runTests = async tests => {
580
580
  /**