@augment-vir/assert 31.71.3 → 31.72.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.
@@ -1,12 +1,38 @@
1
- import { type MaybePromise, type NarrowToExpected, type RequiredKeysOf } from '@augment-vir/core';
1
+ import { type AnyObject, type MaybePromise, type NarrowToExpected, type RequiredKeysOf, type SetRequiredAndNotNull } from '@augment-vir/core';
2
2
  import { type SetRequired } from 'type-fest';
3
3
  import { type WaitUntilOptions } from '../guard-types/wait-until-function.js';
4
- /** Helper type for `hasKey`. */
5
- type ExtractValue<Key extends PropertyKey, Parent> = Key extends keyof Parent ? Key extends keyof SetRequired<Parent, Key> ? SetRequired<Parent, Key>[Key] : never : Key extends keyof Extract<Parent, Record<Key, any>> ? Key extends keyof SetRequired<Extract<Parent, Record<Key, any>>, Key> ? SetRequired<Extract<Parent, Record<Key, any>>, Key>[Key] : never : never;
6
- /** Helper type for `hasKey`. */
7
- type CombinedParentValue<Key extends PropertyKey, Parent> = ExtractValue<Key, Parent> extends never ? unknown : ExtractValue<Key, Parent>;
8
- /** Helper type for `hasKey`. */
9
- type CombineTypeWithKey<Key extends PropertyKey, Parent> = Parent & Record<Key, CombinedParentValue<Key, Parent>>;
4
+ /**
5
+ * Helper type for `hasKey`.
6
+ *
7
+ * @category Assert : Util
8
+ * @category Package : @augment-vir/assert
9
+ * @package [`@augment-vir/assert`](https://www.npmjs.com/package/@augment-vir/assert)
10
+ */
11
+ export type ExtractValue<Key extends PropertyKey, Parent> = Key extends keyof Parent ? Key extends keyof SetRequired<Parent, Key> ? SetRequired<Parent, Key>[Key] : never : Key extends keyof Extract<Parent, Record<Key, any>> ? Key extends keyof SetRequired<Extract<Parent, Record<Key, any>>, Key> ? SetRequired<Extract<Parent, Record<Key, any>>, Key>[Key] : never : never;
12
+ /**
13
+ * Helper type for `hasKey`.
14
+ *
15
+ * @category Assert : Util
16
+ * @category Package : @augment-vir/assert
17
+ * @package [`@augment-vir/assert`](https://www.npmjs.com/package/@augment-vir/assert)
18
+ */
19
+ export type CombinedParentValue<Key extends PropertyKey, Parent> = ExtractValue<Key, Parent> extends never ? unknown : ExtractValue<Key, Parent>;
20
+ /**
21
+ * Helper type for `hasKey`.
22
+ *
23
+ * @category Assert : Util
24
+ * @category Package : @augment-vir/assert
25
+ * @package [`@augment-vir/assert`](https://www.npmjs.com/package/@augment-vir/assert)
26
+ */
27
+ export type CombineTypeWithKey<Key extends PropertyKey, Parent> = Parent & Record<Key, CombinedParentValue<Key, Parent>>;
28
+ /**
29
+ * Helper type for `hasDefinedProperty` and `hasDefinedProperties`.
30
+ *
31
+ * @category Assert : Util
32
+ * @category Package : @augment-vir/assert
33
+ * @package [`@augment-vir/assert`](https://www.npmjs.com/package/@augment-vir/assert)
34
+ */
35
+ export type WithDefinedProperties<Parent extends AnyObject, Keys extends keyof Parent> = Parent & SetRequiredAndNotNull<Parent, Keys>;
10
36
  declare function hasKey<const Key extends PropertyKey, const Parent>(this: void, parent: Parent, key: Key): parent is CombineTypeWithKey<Key, Parent>;
11
37
  export declare const keyGuards: {
12
38
  assert: {
@@ -140,6 +166,53 @@ export declare const keyGuards: {
140
166
  * - {@link assert.lacksKey} : the single-key assertion.
141
167
  */
142
168
  lacksKeys<const Parent, const Key extends PropertyKey>(this: void, parent: Parent, keys: ReadonlyArray<Key>, failureMessage?: string | undefined): asserts parent is Exclude<Parent, Partial<Record<Key, any>>>;
169
+ /**
170
+ * Asserts that a parent object has the given property and that its value is defined (not `null`
171
+ * and not `undefined`).
172
+ *
173
+ * Type guards the parent value.
174
+ *
175
+ * @example
176
+ *
177
+ * ```ts
178
+ * import {assert} from '@augment-vir/assert';
179
+ *
180
+ * assert.hasDefinedProperty({a: 0, b: 1}, 'a'); // passes
181
+ * assert.hasDefinedProperty({a: undefined, b: 1}, 'a'); // fails
182
+ * assert.hasDefinedProperty({a: 0, b: 1}, 'c'); // fails
183
+ * ```
184
+ *
185
+ * @throws {@link AssertionError} If the property is missing or not defined.
186
+ * @see
187
+ * - {@link assert.hasDefinedProperties} : the multi-property assertion.
188
+ */
189
+ hasDefinedProperty<const Parent extends AnyObject, const Key extends keyof Parent>(this: void, parent: Parent, key: Key, failureMessage?: string | undefined): asserts parent is WithDefinedProperties<Parent, Key>;
190
+ /**
191
+ * Asserts that a parent object has all the given properties and that each of their values is
192
+ * defined (not `null` and not `undefined`).
193
+ *
194
+ * Type guards the parent value.
195
+ *
196
+ * @example
197
+ *
198
+ * ```ts
199
+ * import {assert} from '@augment-vir/assert';
200
+ *
201
+ * assert.hasDefinedProperties({a: 0, b: 1}, [
202
+ * 'a',
203
+ * 'b',
204
+ * ]); // passes
205
+ * assert.hasDefinedProperties({a: 0, b: undefined}, [
206
+ * 'a',
207
+ * 'b',
208
+ * ]); // fails
209
+ * ```
210
+ *
211
+ * @throws {@link AssertionError} If any of the properties are missing or not defined.
212
+ * @see
213
+ * - {@link assert.hasDefinedProperty} : the single-property assertion.
214
+ */
215
+ hasDefinedProperties<const Parent extends AnyObject, const Keys extends keyof Parent>(this: void, parent: Parent, keys: ReadonlyArray<Keys>, failureMessage?: string | undefined): asserts parent is WithDefinedProperties<Parent, Keys>;
143
216
  };
144
217
  check: {
145
218
  /**
@@ -266,6 +339,51 @@ export declare const keyGuards: {
266
339
  * - {@link check.lacksKey} : the single-key check.
267
340
  */
268
341
  lacksKeys<const Parent, const Key extends PropertyKey>(this: void, parent: Parent, keys: ReadonlyArray<Key>): parent is Exclude<Parent, Partial<Record<Key, any>>>;
342
+ /**
343
+ * Checks that a parent object has the given property and that its value is defined (not
344
+ * `null` and not `undefined`).
345
+ *
346
+ * Type guards the parent value.
347
+ *
348
+ * @example
349
+ *
350
+ * ```ts
351
+ * import {check} from '@augment-vir/assert';
352
+ *
353
+ * check.hasDefinedProperty({a: 0, b: 1}, 'a'); // returns `true`
354
+ * check.hasDefinedProperty({a: undefined, b: 1}, 'a'); // returns `false`
355
+ * check.hasDefinedProperty({a: 0, b: 1}, 'c'); // returns `false`
356
+ * ```
357
+ *
358
+ * @see
359
+ * - {@link check.hasDefinedProperties} : the multi-property check.
360
+ */
361
+ hasDefinedProperty<const Parent extends AnyObject, const Key extends keyof Parent>(this: void, parent: Parent, key: Key): parent is WithDefinedProperties<Parent, Key>;
362
+ /**
363
+ * Checks that a parent object has all the given properties and that each of their values is
364
+ * defined (not `null` and not `undefined`).
365
+ *
366
+ * Type guards the parent value.
367
+ *
368
+ * @example
369
+ *
370
+ * ```ts
371
+ * import {check} from '@augment-vir/assert';
372
+ *
373
+ * check.hasDefinedProperties({a: 0, b: 1}, [
374
+ * 'a',
375
+ * 'b',
376
+ * ]); // returns `true`
377
+ * check.hasDefinedProperties({a: 0, b: undefined}, [
378
+ * 'a',
379
+ * 'b',
380
+ * ]); // returns `false`
381
+ * ```
382
+ *
383
+ * @see
384
+ * - {@link check.hasDefinedProperty} : the single-property check.
385
+ */
386
+ hasDefinedProperties<const Parent extends AnyObject, const Keys extends keyof Parent>(this: void, parent: Parent, keys: ReadonlyArray<Keys>): parent is WithDefinedProperties<Parent, Keys>;
269
387
  };
270
388
  assertWrap: {
271
389
  /**
@@ -407,6 +525,54 @@ export declare const keyGuards: {
407
525
  * - {@link assertWrap.lacksKey} : the single-key assertion.
408
526
  */
409
527
  lacksKeys<const Parent, const Key extends PropertyKey>(this: void, parent: Parent, keys: ReadonlyArray<Key>, failureMessage?: string | undefined): Exclude<Parent, Partial<Record<Key, any>>>;
528
+ /**
529
+ * Asserts that a parent object has the given property and that its value is defined (not
530
+ * `null` and not `undefined`). Returns the parent if the assertion passes.
531
+ *
532
+ * Type guards the parent value.
533
+ *
534
+ * @example
535
+ *
536
+ * ```ts
537
+ * import {assertWrap} from '@augment-vir/assert';
538
+ *
539
+ * assertWrap.hasDefinedProperty({a: 0, b: 1}, 'a'); // returns `{a: 0, b: 1}`
540
+ * assertWrap.hasDefinedProperty({a: undefined, b: 1}, 'a'); // throws an error
541
+ * ```
542
+ *
543
+ * @returns The parent if the assertion passes.
544
+ * @throws {@link AssertionError} If the property is missing or not defined.
545
+ * @see
546
+ * - {@link assertWrap.hasDefinedProperties} : the multi-property assertion.
547
+ */
548
+ hasDefinedProperty<const Parent extends AnyObject, const Key extends keyof Parent>(this: void, parent: Parent, key: Key, failureMessage?: string | undefined): WithDefinedProperties<Parent, Key>;
549
+ /**
550
+ * Asserts that a parent object has all the given properties and that each of their values
551
+ * is defined (not `null` and not `undefined`). Returns the parent if the assertion passes.
552
+ *
553
+ * Type guards the parent value.
554
+ *
555
+ * @example
556
+ *
557
+ * ```ts
558
+ * import {assertWrap} from '@augment-vir/assert';
559
+ *
560
+ * assertWrap.hasDefinedProperties({a: 0, b: 1}, [
561
+ * 'a',
562
+ * 'b',
563
+ * ]); // returns `{a: 0, b: 1}`
564
+ * assertWrap.hasDefinedProperties({a: 0, b: undefined}, [
565
+ * 'a',
566
+ * 'b',
567
+ * ]); // throws an error
568
+ * ```
569
+ *
570
+ * @returns The parent if the assertion passes.
571
+ * @throws {@link AssertionError} If any of the properties are missing or not defined.
572
+ * @see
573
+ * - {@link assertWrap.hasDefinedProperty} : the single-property assertion.
574
+ */
575
+ hasDefinedProperties<const Parent extends AnyObject, const Keys extends keyof Parent>(this: void, parent: Parent, keys: ReadonlyArray<Keys>, failureMessage?: string | undefined): WithDefinedProperties<Parent, Keys>;
410
576
  };
411
577
  checkWrap: {
412
578
  /**
@@ -545,6 +711,54 @@ export declare const keyGuards: {
545
711
  * - {@link checkWrap.lacksKey} : the single-key check.
546
712
  */
547
713
  lacksKeys<const Parent, const Key extends PropertyKey>(this: void, parent: Parent, keys: ReadonlyArray<Key>): Exclude<Parent, Partial<Record<Key, any>>> | undefined;
714
+ /**
715
+ * Checks that a parent object has the given property and that its value is defined (not
716
+ * `null` and not `undefined`). Returns the parent value if the check passes, otherwise
717
+ * `undefined`.
718
+ *
719
+ * Type guards the parent value.
720
+ *
721
+ * @example
722
+ *
723
+ * ```ts
724
+ * import {checkWrap} from '@augment-vir/assert';
725
+ *
726
+ * checkWrap.hasDefinedProperty({a: 0, b: 1}, 'a'); // returns `{a: 0, b: 1}`
727
+ * checkWrap.hasDefinedProperty({a: undefined, b: 1}, 'a'); // returns `undefined`
728
+ * ```
729
+ *
730
+ * @returns The parent value if the check passes, otherwise `undefined`.
731
+ * @see
732
+ * - {@link checkWrap.hasDefinedProperties} : the multi-property check.
733
+ */
734
+ hasDefinedProperty<const Parent extends AnyObject, const Key extends keyof Parent>(this: void, parent: Parent, key: Key): WithDefinedProperties<Parent, Key> | undefined;
735
+ /**
736
+ * Checks that a parent object has all the given properties and that each of their values is
737
+ * defined (not `null` and not `undefined`). Returns the parent value if the check passes,
738
+ * otherwise `undefined`.
739
+ *
740
+ * Type guards the parent value.
741
+ *
742
+ * @example
743
+ *
744
+ * ```ts
745
+ * import {checkWrap} from '@augment-vir/assert';
746
+ *
747
+ * checkWrap.hasDefinedProperties({a: 0, b: 1}, [
748
+ * 'a',
749
+ * 'b',
750
+ * ]); // returns `{a: 0, b: 1}`
751
+ * checkWrap.hasDefinedProperties({a: 0, b: undefined}, [
752
+ * 'a',
753
+ * 'b',
754
+ * ]); // returns `undefined`
755
+ * ```
756
+ *
757
+ * @returns The parent value if the check passes, otherwise `undefined`.
758
+ * @see
759
+ * - {@link checkWrap.hasDefinedProperty} : the single-property check.
760
+ */
761
+ hasDefinedProperties<const Parent extends AnyObject, const Keys extends keyof Parent>(this: void, parent: Parent, keys: ReadonlyArray<Keys>): WithDefinedProperties<Parent, Keys> | undefined;
548
762
  };
549
763
  waitUntil: {
550
764
  /**
@@ -723,6 +937,70 @@ export declare const keyGuards: {
723
937
  * - {@link waitUntil.lacksKey} : the single-key assertion.
724
938
  */
725
939
  lacksKeys: <const Parent, const Keys extends PropertyKey>(this: void, keys: ReadonlyArray<Keys>, callback: () => MaybePromise<Parent>, options?: WaitUntilOptions | undefined, failureMessage?: string | undefined) => Promise<Exclude<Parent, Partial<Record<Keys, any>>>>;
940
+ /**
941
+ * Repeatedly calls a callback until its output is a parent object that has the first, key
942
+ * input defined (not `null` and not `undefined`). Once the callback output passes, it is
943
+ * returned. If the attempts time out, an error is thrown.
944
+ *
945
+ * Type guards the parent value.
946
+ *
947
+ * @example
948
+ *
949
+ * ```ts
950
+ * import {waitUntil} from '@augment-vir/assert';
951
+ *
952
+ * await waitUntil.hasDefinedProperty('a', () => {
953
+ * return {a: 0, b: 1};
954
+ * }); // returns `{a: 0, b: 1}`
955
+ * await waitUntil.hasDefinedProperty('a', () => {
956
+ * return {a: undefined, b: 1};
957
+ * }); // throws an error
958
+ * ```
959
+ *
960
+ * @returns The callback output once it passes.
961
+ * @throws {@link AssertionError} On timeout.
962
+ * @see
963
+ * - {@link waitUntil.hasDefinedProperties} : the multi-property assertion.
964
+ */
965
+ hasDefinedProperty: <const Parent extends AnyObject, const Key extends keyof Parent>(this: void, key: Key, callback: () => MaybePromise<Parent>, options?: WaitUntilOptions | undefined, failureMessage?: string | undefined) => Promise<WithDefinedProperties<Parent, Key>>;
966
+ /**
967
+ * Repeatedly calls a callback until its output is a parent object that has all of the
968
+ * first, keys input defined (not `null` and not `undefined`). Once the callback output
969
+ * passes, it is returned. If the attempts time out, an error is thrown.
970
+ *
971
+ * Type guards the parent value.
972
+ *
973
+ * @example
974
+ *
975
+ * ```ts
976
+ * import {waitUntil} from '@augment-vir/assert';
977
+ *
978
+ * await waitUntil.hasDefinedProperties(
979
+ * [
980
+ * 'a',
981
+ * 'b',
982
+ * ],
983
+ * () => {
984
+ * return {a: 0, b: 1};
985
+ * },
986
+ * ); // returns `{a: 0, b: 1}`
987
+ * await waitUntil.hasDefinedProperties(
988
+ * [
989
+ * 'a',
990
+ * 'b',
991
+ * ],
992
+ * () => {
993
+ * return {a: 0, b: undefined};
994
+ * },
995
+ * ); // throws an error
996
+ * ```
997
+ *
998
+ * @returns The callback output once it passes.
999
+ * @throws {@link AssertionError} On timeout.
1000
+ * @see
1001
+ * - {@link waitUntil.hasDefinedProperty} : the single-property assertion.
1002
+ */
1003
+ hasDefinedProperties: <const Parent extends AnyObject, const Keys extends keyof Parent>(this: void, keys: ReadonlyArray<Keys>, callback: () => MaybePromise<Parent>, options?: WaitUntilOptions | undefined, failureMessage?: string | undefined) => Promise<WithDefinedProperties<Parent, Keys>>;
726
1004
  };
727
1005
  };
728
1006
  export {};
@@ -177,6 +177,62 @@ const assertions = {
177
177
  throw new AssertionError(`'${stringify(parent)}' does not lack keys '${existingKeys.join(',')}'.`, failureMessage);
178
178
  }
179
179
  },
180
+ /**
181
+ * Asserts that a parent object has the given property and that its value is defined (not `null`
182
+ * and not `undefined`).
183
+ *
184
+ * Type guards the parent value.
185
+ *
186
+ * @example
187
+ *
188
+ * ```ts
189
+ * import {assert} from '@augment-vir/assert';
190
+ *
191
+ * assert.hasDefinedProperty({a: 0, b: 1}, 'a'); // passes
192
+ * assert.hasDefinedProperty({a: undefined, b: 1}, 'a'); // fails
193
+ * assert.hasDefinedProperty({a: 0, b: 1}, 'c'); // fails
194
+ * ```
195
+ *
196
+ * @throws {@link AssertionError} If the property is missing or not defined.
197
+ * @see
198
+ * - {@link assert.hasDefinedProperties} : the multi-property assertion.
199
+ */
200
+ hasDefinedProperty(parent, key, failureMessage) {
201
+ if (parent[key] == undefined) {
202
+ throw new AssertionError(`'${stringify(parent)}' does not have a defined property '${String(key)}'.`, failureMessage);
203
+ }
204
+ },
205
+ /**
206
+ * Asserts that a parent object has all the given properties and that each of their values is
207
+ * defined (not `null` and not `undefined`).
208
+ *
209
+ * Type guards the parent value.
210
+ *
211
+ * @example
212
+ *
213
+ * ```ts
214
+ * import {assert} from '@augment-vir/assert';
215
+ *
216
+ * assert.hasDefinedProperties({a: 0, b: 1}, [
217
+ * 'a',
218
+ * 'b',
219
+ * ]); // passes
220
+ * assert.hasDefinedProperties({a: 0, b: undefined}, [
221
+ * 'a',
222
+ * 'b',
223
+ * ]); // fails
224
+ * ```
225
+ *
226
+ * @throws {@link AssertionError} If any of the properties are missing or not defined.
227
+ * @see
228
+ * - {@link assert.hasDefinedProperty} : the single-property assertion.
229
+ */
230
+ hasDefinedProperties(parent, keys, failureMessage) {
231
+ const undefinedKeys = keys.filter((key) => parent[key] == undefined);
232
+ if (undefinedKeys.length) {
233
+ throw new AssertionError(`'${stringify(parent)}' does not have defined properties '${undefinedKeys.join(',')}'.`, failureMessage);
234
+ }
235
+ },
180
236
  };
181
237
  export const keyGuards = {
182
238
  assert: assertions,
@@ -315,6 +371,55 @@ export const keyGuards = {
315
371
  lacksKeys(parent, keys) {
316
372
  return keys.every((key) => !hasKey(parent, key));
317
373
  },
374
+ /**
375
+ * Checks that a parent object has the given property and that its value is defined (not
376
+ * `null` and not `undefined`).
377
+ *
378
+ * Type guards the parent value.
379
+ *
380
+ * @example
381
+ *
382
+ * ```ts
383
+ * import {check} from '@augment-vir/assert';
384
+ *
385
+ * check.hasDefinedProperty({a: 0, b: 1}, 'a'); // returns `true`
386
+ * check.hasDefinedProperty({a: undefined, b: 1}, 'a'); // returns `false`
387
+ * check.hasDefinedProperty({a: 0, b: 1}, 'c'); // returns `false`
388
+ * ```
389
+ *
390
+ * @see
391
+ * - {@link check.hasDefinedProperties} : the multi-property check.
392
+ */
393
+ hasDefinedProperty(parent, key) {
394
+ return parent[key] != undefined;
395
+ },
396
+ /**
397
+ * Checks that a parent object has all the given properties and that each of their values is
398
+ * defined (not `null` and not `undefined`).
399
+ *
400
+ * Type guards the parent value.
401
+ *
402
+ * @example
403
+ *
404
+ * ```ts
405
+ * import {check} from '@augment-vir/assert';
406
+ *
407
+ * check.hasDefinedProperties({a: 0, b: 1}, [
408
+ * 'a',
409
+ * 'b',
410
+ * ]); // returns `true`
411
+ * check.hasDefinedProperties({a: 0, b: undefined}, [
412
+ * 'a',
413
+ * 'b',
414
+ * ]); // returns `false`
415
+ * ```
416
+ *
417
+ * @see
418
+ * - {@link check.hasDefinedProperty} : the single-property check.
419
+ */
420
+ hasDefinedProperties(parent, keys) {
421
+ return keys.every((key) => parent[key] != undefined);
422
+ },
318
423
  },
319
424
  assertWrap: {
320
425
  /**
@@ -488,6 +593,65 @@ export const keyGuards = {
488
593
  }
489
594
  return parent;
490
595
  },
596
+ /**
597
+ * Asserts that a parent object has the given property and that its value is defined (not
598
+ * `null` and not `undefined`). Returns the parent if the assertion passes.
599
+ *
600
+ * Type guards the parent value.
601
+ *
602
+ * @example
603
+ *
604
+ * ```ts
605
+ * import {assertWrap} from '@augment-vir/assert';
606
+ *
607
+ * assertWrap.hasDefinedProperty({a: 0, b: 1}, 'a'); // returns `{a: 0, b: 1}`
608
+ * assertWrap.hasDefinedProperty({a: undefined, b: 1}, 'a'); // throws an error
609
+ * ```
610
+ *
611
+ * @returns The parent if the assertion passes.
612
+ * @throws {@link AssertionError} If the property is missing or not defined.
613
+ * @see
614
+ * - {@link assertWrap.hasDefinedProperties} : the multi-property assertion.
615
+ */
616
+ hasDefinedProperty(parent, key, failureMessage) {
617
+ if (parent[key] == undefined) {
618
+ throw new AssertionError(`'${stringify(parent)}' does not have a defined property '${String(key)}'.`, failureMessage);
619
+ }
620
+ return parent;
621
+ },
622
+ /**
623
+ * Asserts that a parent object has all the given properties and that each of their values
624
+ * is defined (not `null` and not `undefined`). Returns the parent if the assertion passes.
625
+ *
626
+ * Type guards the parent value.
627
+ *
628
+ * @example
629
+ *
630
+ * ```ts
631
+ * import {assertWrap} from '@augment-vir/assert';
632
+ *
633
+ * assertWrap.hasDefinedProperties({a: 0, b: 1}, [
634
+ * 'a',
635
+ * 'b',
636
+ * ]); // returns `{a: 0, b: 1}`
637
+ * assertWrap.hasDefinedProperties({a: 0, b: undefined}, [
638
+ * 'a',
639
+ * 'b',
640
+ * ]); // throws an error
641
+ * ```
642
+ *
643
+ * @returns The parent if the assertion passes.
644
+ * @throws {@link AssertionError} If any of the properties are missing or not defined.
645
+ * @see
646
+ * - {@link assertWrap.hasDefinedProperty} : the single-property assertion.
647
+ */
648
+ hasDefinedProperties(parent, keys, failureMessage) {
649
+ const undefinedKeys = keys.filter((key) => parent[key] == undefined);
650
+ if (undefinedKeys.length) {
651
+ throw new AssertionError(`'${stringify(parent)}' does not have defined properties '${undefinedKeys.join(',')}'.`, failureMessage);
652
+ }
653
+ return parent;
654
+ },
491
655
  },
492
656
  checkWrap: {
493
657
  /**
@@ -668,6 +832,68 @@ export const keyGuards = {
668
832
  return undefined;
669
833
  }
670
834
  },
835
+ /**
836
+ * Checks that a parent object has the given property and that its value is defined (not
837
+ * `null` and not `undefined`). Returns the parent value if the check passes, otherwise
838
+ * `undefined`.
839
+ *
840
+ * Type guards the parent value.
841
+ *
842
+ * @example
843
+ *
844
+ * ```ts
845
+ * import {checkWrap} from '@augment-vir/assert';
846
+ *
847
+ * checkWrap.hasDefinedProperty({a: 0, b: 1}, 'a'); // returns `{a: 0, b: 1}`
848
+ * checkWrap.hasDefinedProperty({a: undefined, b: 1}, 'a'); // returns `undefined`
849
+ * ```
850
+ *
851
+ * @returns The parent value if the check passes, otherwise `undefined`.
852
+ * @see
853
+ * - {@link checkWrap.hasDefinedProperties} : the multi-property check.
854
+ */
855
+ hasDefinedProperty(parent, key) {
856
+ if (parent[key] == undefined) {
857
+ return undefined;
858
+ }
859
+ else {
860
+ return parent;
861
+ }
862
+ },
863
+ /**
864
+ * Checks that a parent object has all the given properties and that each of their values is
865
+ * defined (not `null` and not `undefined`). Returns the parent value if the check passes,
866
+ * otherwise `undefined`.
867
+ *
868
+ * Type guards the parent value.
869
+ *
870
+ * @example
871
+ *
872
+ * ```ts
873
+ * import {checkWrap} from '@augment-vir/assert';
874
+ *
875
+ * checkWrap.hasDefinedProperties({a: 0, b: 1}, [
876
+ * 'a',
877
+ * 'b',
878
+ * ]); // returns `{a: 0, b: 1}`
879
+ * checkWrap.hasDefinedProperties({a: 0, b: undefined}, [
880
+ * 'a',
881
+ * 'b',
882
+ * ]); // returns `undefined`
883
+ * ```
884
+ *
885
+ * @returns The parent value if the check passes, otherwise `undefined`.
886
+ * @see
887
+ * - {@link checkWrap.hasDefinedProperty} : the single-property check.
888
+ */
889
+ hasDefinedProperties(parent, keys) {
890
+ if (keys.every((key) => parent[key] != undefined)) {
891
+ return parent;
892
+ }
893
+ else {
894
+ return undefined;
895
+ }
896
+ },
671
897
  },
672
898
  waitUntil: {
673
899
  /**
@@ -846,5 +1072,69 @@ export const keyGuards = {
846
1072
  * - {@link waitUntil.lacksKey} : the single-key assertion.
847
1073
  */
848
1074
  lacksKeys: createWaitUntil(assertions.lacksKeys),
1075
+ /**
1076
+ * Repeatedly calls a callback until its output is a parent object that has the first, key
1077
+ * input defined (not `null` and not `undefined`). Once the callback output passes, it is
1078
+ * returned. If the attempts time out, an error is thrown.
1079
+ *
1080
+ * Type guards the parent value.
1081
+ *
1082
+ * @example
1083
+ *
1084
+ * ```ts
1085
+ * import {waitUntil} from '@augment-vir/assert';
1086
+ *
1087
+ * await waitUntil.hasDefinedProperty('a', () => {
1088
+ * return {a: 0, b: 1};
1089
+ * }); // returns `{a: 0, b: 1}`
1090
+ * await waitUntil.hasDefinedProperty('a', () => {
1091
+ * return {a: undefined, b: 1};
1092
+ * }); // throws an error
1093
+ * ```
1094
+ *
1095
+ * @returns The callback output once it passes.
1096
+ * @throws {@link AssertionError} On timeout.
1097
+ * @see
1098
+ * - {@link waitUntil.hasDefinedProperties} : the multi-property assertion.
1099
+ */
1100
+ hasDefinedProperty: createWaitUntil(assertions.hasDefinedProperty),
1101
+ /**
1102
+ * Repeatedly calls a callback until its output is a parent object that has all of the
1103
+ * first, keys input defined (not `null` and not `undefined`). Once the callback output
1104
+ * passes, it is returned. If the attempts time out, an error is thrown.
1105
+ *
1106
+ * Type guards the parent value.
1107
+ *
1108
+ * @example
1109
+ *
1110
+ * ```ts
1111
+ * import {waitUntil} from '@augment-vir/assert';
1112
+ *
1113
+ * await waitUntil.hasDefinedProperties(
1114
+ * [
1115
+ * 'a',
1116
+ * 'b',
1117
+ * ],
1118
+ * () => {
1119
+ * return {a: 0, b: 1};
1120
+ * },
1121
+ * ); // returns `{a: 0, b: 1}`
1122
+ * await waitUntil.hasDefinedProperties(
1123
+ * [
1124
+ * 'a',
1125
+ * 'b',
1126
+ * ],
1127
+ * () => {
1128
+ * return {a: 0, b: undefined};
1129
+ * },
1130
+ * ); // throws an error
1131
+ * ```
1132
+ *
1133
+ * @returns The callback output once it passes.
1134
+ * @throws {@link AssertionError} On timeout.
1135
+ * @see
1136
+ * - {@link waitUntil.hasDefinedProperty} : the single-property assertion.
1137
+ */
1138
+ hasDefinedProperties: createWaitUntil(assertions.hasDefinedProperties),
849
1139
  },
850
1140
  };
@@ -1,4 +1,4 @@
1
- import { stringify } from '@augment-vir/core';
1
+ import { isPrimitive, stringify } from '@augment-vir/core';
2
2
  import { AssertionError } from '../augments/assertion.error.js';
3
3
  import { createWaitUntil } from '../guard-types/wait-until-function.js';
4
4
  const assertions = {
@@ -77,11 +77,7 @@ const assertions = {
77
77
  * - {@link assert.isNotPrimitive} : the opposite assertion.
78
78
  */
79
79
  isPrimitive(actual, failureMessage) {
80
- /**
81
- * `null` is a primitive but `typeof null` gives `'object'` so we have to special case
82
- * `null` here.
83
- */
84
- if (actual !== null && (typeof actual === 'object' || typeof actual === 'function')) {
80
+ if (!isPrimitive(actual)) {
85
81
  throw new AssertionError(`'${stringify(actual)}' is not a Primitive.`, failureMessage);
86
82
  }
87
83
  },
@@ -106,11 +102,7 @@ const assertions = {
106
102
  * - {@link assert.isPrimitive} : the opposite assertion.
107
103
  */
108
104
  isNotPrimitive(actual, failureMessage) {
109
- /**
110
- * `null` is a primitive but `typeof null` gives `'object'` so we have to special case
111
- * `null` here.
112
- */
113
- if (actual === null || (typeof actual !== 'object' && typeof actual !== 'function')) {
105
+ if (isPrimitive(actual)) {
114
106
  throw new AssertionError(`'${stringify(actual)}' is not a Primitive.`, failureMessage);
115
107
  }
116
108
  },
@@ -138,11 +130,7 @@ export const primitiveGuards = {
138
130
  * - {@link check.isPrimitive} : the opposite check.
139
131
  */
140
132
  isNotPrimitive(actual) {
141
- /**
142
- * `null` is a primitive but `typeof null` gives `'object'` so we have to special case
143
- * `null` here.
144
- */
145
- return actual !== null && (typeof actual === 'object' || typeof actual === 'function');
133
+ return !isPrimitive(actual);
146
134
  },
147
135
  /**
148
136
  * Checks that a value is _not_ a valid `PropertyKey`. `PropertyKey` is a built-in
@@ -188,11 +176,7 @@ export const primitiveGuards = {
188
176
  * - {@link check.isNotPrimitive} : the opposite check.
189
177
  */
190
178
  isPrimitive(actual) {
191
- /**
192
- * `null` is a primitive but `typeof null` gives `'object'` so we have to special case
193
- * `null` here.
194
- */
195
- return actual === null || (typeof actual !== 'object' && typeof actual !== 'function');
179
+ return isPrimitive(actual);
196
180
  },
197
181
  /**
198
182
  * Checks that a value is _not_ a JavaScript
@@ -243,11 +227,7 @@ export const primitiveGuards = {
243
227
  * - {@link assertWrap.isNotPropertyKey} : the opposite assertion.
244
228
  */
245
229
  isNotPrimitive(actual, failureMessage) {
246
- /**
247
- * `null` is a primitive but `typeof null` gives `'object'` so we have to special case
248
- * `null` here.
249
- */
250
- if (actual === null || (typeof actual !== 'object' && typeof actual !== 'function')) {
230
+ if (isPrimitive(actual)) {
251
231
  throw new AssertionError(`'${stringify(actual)}' is not a Primitive.`, failureMessage);
252
232
  }
253
233
  return actual;
@@ -305,11 +285,7 @@ export const primitiveGuards = {
305
285
  * - {@link assertWrap.isNotPrimitive} : the opposite assertion.
306
286
  */
307
287
  isPrimitive(actual, failureMessage) {
308
- /**
309
- * `null` is a primitive but `typeof null` gives `'object'` so we have to special case
310
- * `null` here.
311
- */
312
- if (actual !== null && (typeof actual === 'object' || typeof actual === 'function')) {
288
+ if (!isPrimitive(actual)) {
313
289
  throw new AssertionError(`'${stringify(actual)}' is not a Primitive.`, failureMessage);
314
290
  }
315
291
  return actual;
@@ -368,15 +344,11 @@ export const primitiveGuards = {
368
344
  * - {@link checkWrap.isPrimitive} : the opposite check.
369
345
  */
370
346
  isNotPrimitive(actual) {
371
- /**
372
- * `null` is a primitive but `typeof null` gives `'object'` so we have to special case
373
- * `null` here.
374
- */
375
- if (actual !== null && (typeof actual === 'object' || typeof actual === 'function')) {
376
- return actual;
347
+ if (isPrimitive(actual)) {
348
+ return undefined;
377
349
  }
378
350
  else {
379
- return undefined;
351
+ return actual;
380
352
  }
381
353
  },
382
354
  /**
@@ -432,11 +404,7 @@ export const primitiveGuards = {
432
404
  * - {@link checkWrap.isNotPrimitive} : the opposite check.
433
405
  */
434
406
  isPrimitive(actual) {
435
- /**
436
- * `null` is a primitive but `typeof null` gives `'object'` so we have to special case
437
- * `null` here.
438
- */
439
- if (actual === null || (typeof actual !== 'object' && typeof actual !== 'function')) {
407
+ if (isPrimitive(actual)) {
440
408
  return actual;
441
409
  }
442
410
  else {
@@ -1,4 +1,5 @@
1
1
  export type { Falsy, FalsyValue, Truthy } from '../assertions/boolean.js';
2
+ export type { CombineTypeWithKey, CombinedParentValue, ExtractValue, WithDefinedProperties, } from '../assertions/keys.js';
2
3
  export type { CustomOutputAsserter } from '../assertions/output.js';
3
4
  export type { ErrorMatchOptions } from '../assertions/throws.js';
4
5
  export type { CanBeEmpty, Empty } from '../assertions/values.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@augment-vir/assert",
3
- "version": "31.71.3",
3
+ "version": "31.72.0",
4
4
  "description": "A collection of assertions for test and production code alike.",
5
5
  "keywords": [
6
6
  "augment",
@@ -24,7 +24,7 @@
24
24
  "type": "git",
25
25
  "url": "git+https://github.com/electrovir/augment-vir.git"
26
26
  },
27
- "license": "(MIT or CC0 1.0)",
27
+ "license": "(MIT OR CC0-1.0)",
28
28
  "author": {
29
29
  "name": "electrovir",
30
30
  "url": "https://github.com/electrovir"
@@ -42,7 +42,7 @@
42
42
  "test:update": "npm test"
43
43
  },
44
44
  "dependencies": {
45
- "@augment-vir/core": "^31.71.3",
45
+ "@augment-vir/core": "^31.72.0",
46
46
  "@date-vir/duration": "^8.3.2",
47
47
  "deep-eql": "^5.0.2",
48
48
  "expect-type": "^1.3.0",