object-shape-tester
Advanced tools
Comparing version 3.0.0 to 3.1.0
@@ -22,6 +22,7 @@ import {baseTypedocConfig} from '@virmator/docs/configs/typedoc.config.base'; | ||
'unknownSymbol', | ||
'numericRangeSymbol', | ||
'optionalSymbol', | ||
'BaseParts', | ||
'ExpandParts', | ||
'MaybeRequired', | ||
'OptionallyReadonly', | ||
@@ -28,0 +29,0 @@ 'ShapeSpecifierType', |
import { ArrayElement, AtLeastTuple, type AnyFunction } from '@augment-vir/common'; | ||
import { LiteralToPrimitive, Primitive, UnionToIntersection, WritableDeep } from 'type-fest'; | ||
import { LiteralToPrimitive, Primitive, UnionToIntersection, WritableDeep, type Simplify } from 'type-fest'; | ||
/** | ||
@@ -53,2 +53,4 @@ * ======================================== | ||
declare const unknownSymbol: unique symbol; | ||
declare const numericRangeSymbol: unique symbol; | ||
declare const optionalSymbol: unique symbol; | ||
/** | ||
@@ -59,3 +61,3 @@ * Symbols used to mark the outputs of each sub-shape function (like {@link or}). | ||
*/ | ||
export declare const shapeSpecifiersTypes: readonly [typeof andSymbol, typeof enumSymbol, typeof exactSymbol, typeof indexedKeysSymbol, typeof classSymbol, typeof orSymbol, typeof unknownSymbol]; | ||
export declare const shapeSpecifiersTypes: readonly [typeof andSymbol, typeof enumSymbol, typeof exactSymbol, typeof indexedKeysSymbol, typeof classSymbol, typeof orSymbol, typeof unknownSymbol, typeof numericRangeSymbol, typeof optionalSymbol]; | ||
type BaseParts = AtLeastTuple<unknown, 0>; | ||
@@ -155,2 +157,17 @@ /** | ||
/** | ||
* {@link ShapeSpecifier} for {@link numericRange}. | ||
* | ||
* @category Internal | ||
*/ | ||
export type ShapeNumericRange<T extends number = number> = ShapeSpecifier<[ | ||
T, | ||
T | ||
], typeof numericRangeSymbol>; | ||
/** | ||
* {@link ShapeSpecifier} for {@link optional}. | ||
* | ||
* @category Internal | ||
*/ | ||
export type ShapeOptional<T = unknown> = ShapeSpecifier<[T], typeof optionalSymbol>; | ||
/** | ||
* ======================================== | ||
@@ -288,3 +305,3 @@ * | ||
* | ||
* // `myShape.runtimeType` is `{a: unknown` | ||
* // `myShape.runtimeType` is `{a: unknown}` | ||
* ``` | ||
@@ -294,2 +311,42 @@ */ | ||
/** | ||
* Define a shape part that requires numbers to be within a specific range, inclusive. | ||
* | ||
* @category Shape Part | ||
* @example | ||
* | ||
* ```ts | ||
* import {numericRange, defineShape} from 'object-shape-tester'; | ||
* | ||
* const myShape = defineShape({ | ||
* // This will simply produce a type of `number` but will validate runtime values against the range. | ||
* a: numericRange(1, 10), | ||
* }); | ||
* // `myShape.runtimeType` is just `{a: number}` | ||
* | ||
* const myShape2 = defineShape({ | ||
* // If you want type safety, you must specify the allowed numbers manually | ||
* a: numericRange<1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10>(1, 10), | ||
* }); | ||
* // `myShape2.runtimeType` is `{a: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10}` | ||
* ``` | ||
*/ | ||
export declare function numericRange<T extends number = number>(min: NoInfer<T>, max: NoInfer<T>): ShapeNumericRange<T>; | ||
/** | ||
* Define a shape part that is optional. This only makes sense as a property in an object. | ||
* | ||
* @category Shape Part | ||
* @example | ||
* | ||
* ```ts | ||
* import {optional, defineShape} from 'object-shape-tester'; | ||
* | ||
* const myShape = defineShape({ | ||
* a: optional(-1), | ||
* }); | ||
* | ||
* // `myShape.runtimeType` is `{a?: number}` | ||
* ``` | ||
*/ | ||
export declare function optional<T>(part: T): ShapeOptional<T>; | ||
/** | ||
* ======================================== | ||
@@ -302,3 +359,3 @@ * | ||
/** | ||
* Checks if the input is an `and` shape specifier for internal type guarding purposes. | ||
* Checks if the input is an {@link and} shape specifier for internal type guarding purposes. | ||
* | ||
@@ -309,3 +366,3 @@ * @category Internal | ||
/** | ||
* Checks if the input is a `classShape` shape specifier for internal type guarding purposes. | ||
* Checks if the input is a {@link classShape} shape specifier for internal type guarding purposes. | ||
* | ||
@@ -316,3 +373,3 @@ * @category Internal | ||
/** | ||
* Checks if the input is an `enumShape` shape specifier for internal type guarding purposes. | ||
* Checks if the input is an {@link enumShape} shape specifier for internal type guarding purposes. | ||
* | ||
@@ -323,3 +380,3 @@ * @category Internal | ||
/** | ||
* Checks if the input is an `exact` shape specifier for internal type guarding purposes. | ||
* Checks if the input is an {@link exact} shape specifier for internal type guarding purposes. | ||
* | ||
@@ -330,3 +387,3 @@ * @category Internal | ||
/** | ||
* Checks if the input is an `indexedKeys` shape specifier for internal type guarding purposes. | ||
* Checks if the input is an {@link indexedKeys} shape specifier for internal type guarding purposes. | ||
* | ||
@@ -337,3 +394,3 @@ * @category Internal | ||
/** | ||
* Checks if the input is an `or` shape specifier for internal type guarding purposes. | ||
* Checks if the input is an {@link or} shape specifier for internal type guarding purposes. | ||
* | ||
@@ -344,3 +401,4 @@ * @category Internal | ||
/** | ||
* Checks if the input is an `unknown` shape specifier for internal type guarding purposes. | ||
* Checks if the input is an {@link unknownShape} shape specifier for internal type guarding | ||
* purposes. | ||
* | ||
@@ -351,2 +409,14 @@ * @category Internal | ||
/** | ||
* Checks if the input is a {@link numericRange} shape specifier for internal type guarding purposes. | ||
* | ||
* @category Internal | ||
*/ | ||
export declare function isNumericRangeShapeSpecifier(maybeSpecifier: unknown): maybeSpecifier is ShapeNumericRange; | ||
/** | ||
* Checks if the input is a {@link optional} shape specifier for internal type guarding purposes. | ||
* | ||
* @category Internal | ||
*/ | ||
export declare function isOptionalShapeSpecifier(maybeSpecifier: unknown): maybeSpecifier is ShapeOptional; | ||
/** | ||
* ======================================== | ||
@@ -365,7 +435,9 @@ * | ||
*/ | ||
export type SpecifierToRuntimeType<PossiblySpecifier, IsExact extends boolean, IsReadonly extends boolean> = PossiblySpecifier extends ShapeSpecifier<infer Parts, infer Type> ? Type extends typeof andSymbol ? OptionallyReadonly<IsReadonly, UnionToIntersection<ExpandParts<Parts, IsExact, IsReadonly>>> : Type extends typeof classSymbol ? Parts[0] extends AnyConstructor ? OptionallyReadonly<IsReadonly, InstanceType<Parts[0]>> : 'TypeError: classShape input must be a constructor.' : Type extends typeof orSymbol ? OptionallyReadonly<IsReadonly, ExpandParts<Parts, IsExact, IsReadonly>> : Type extends typeof exactSymbol ? OptionallyReadonly<IsReadonly, WritableDeep<ExpandParts<Parts, true, IsReadonly>>> : Type extends typeof enumSymbol ? OptionallyReadonly<IsReadonly, Parts[0][keyof Parts[0]]> : Type extends typeof indexedKeysSymbol ? Parts[0] extends { | ||
export type SpecifierToRuntimeType<PossiblySpecifier, IsExact extends boolean, IsReadonly extends boolean> = PossiblySpecifier extends ShapeSpecifier<infer Parts, infer Type> ? Type extends typeof numericRangeSymbol ? Parts[0] : Type extends typeof andSymbol ? OptionallyReadonly<IsReadonly, UnionToIntersection<ExpandParts<Parts, IsExact, IsReadonly>>> : Type extends typeof classSymbol ? Parts[0] extends AnyConstructor ? OptionallyReadonly<IsReadonly, InstanceType<Parts[0]>> : 'TypeError: classShape input must be a constructor.' : Type extends typeof orSymbol ? OptionallyReadonly<IsReadonly, ExpandParts<Parts, IsExact, IsReadonly>> : Type extends typeof exactSymbol ? OptionallyReadonly<IsReadonly, WritableDeep<ExpandParts<Parts, true, IsReadonly>>> : Type extends typeof enumSymbol ? OptionallyReadonly<IsReadonly, Parts[0][keyof Parts[0]]> : Type extends typeof indexedKeysSymbol ? Parts[0] extends { | ||
keys: unknown; | ||
values: unknown; | ||
required: boolean; | ||
} ? ExpandParts<[Parts[0]['keys']], IsExact, IsReadonly> extends PropertyKey ? OptionallyReadonly<IsReadonly, MaybeRequired<Record<ExpandParts<[Parts[0]['keys']], IsExact, IsReadonly>, ExpandParts<[Parts[0]['values']], IsExact, IsReadonly>>, Parts[0]['required']>> : 'TypeError: indexedKeys keys be a subset of PropertyKey.' : 'TypeError: indexedKeys input is invalid.' : Type extends typeof unknownSymbol ? unknown : 'TypeError: found not match for shape specifier type.' : PossiblySpecifier extends Primitive ? IsExact extends true ? PossiblySpecifier : LiteralToPrimitive<PossiblySpecifier> : PossiblySpecifier extends object ? PossiblySpecifier extends ShapeDefinition<any, any> ? PossiblySpecifier['runtimeType'] : OptionallyReadonly<IsReadonly, { | ||
} ? ExpandParts<[ | ||
Parts[0]['keys'] | ||
], IsExact, IsReadonly> extends PropertyKey ? OptionallyReadonly<IsReadonly, MaybeRequired<Record<ExpandParts<[Parts[0]['keys']], IsExact, IsReadonly>, ExpandParts<[Parts[0]['values']], IsExact, IsReadonly>>, Parts[0]['required']>> : 'TypeError: indexedKeys keys be a subset of PropertyKey.' : 'TypeError: indexedKeys input is invalid.' : Type extends typeof unknownSymbol ? unknown : Type extends typeof optionalSymbol ? Parts[0] : 'TypeError: found no match for shape specifier type.' : PossiblySpecifier extends Primitive ? IsExact extends true ? PossiblySpecifier : LiteralToPrimitive<PossiblySpecifier> : PossiblySpecifier extends object ? PossiblySpecifier extends ShapeDefinition<any, any> ? PossiblySpecifier['runtimeType'] : OptionallyReadonly<IsReadonly, { | ||
[Prop in keyof PossiblySpecifier]: SpecifierToRuntimeType<PossiblySpecifier[Prop], IsExact, IsReadonly>; | ||
@@ -379,5 +451,9 @@ }> : PossiblySpecifier; | ||
*/ | ||
export type ShapeToRuntimeType<Shape, IsExact extends boolean, IsReadonly extends boolean> = Shape extends AnyFunction ? Shape : Shape extends object ? Shape extends ShapeDefinition<infer InnerShape, any> ? ShapeToRuntimeType<InnerShape, IsExact, IsReadonly> : Shape extends ShapeSpecifier<any, any> ? Shape extends ShapeSpecifier<any, typeof exactSymbol> ? SpecifierToRuntimeType<Shape, true, IsReadonly> : SpecifierToRuntimeType<Shape, IsExact, IsReadonly> : OptionallyReadonly<IsReadonly, { | ||
[PropName in keyof Shape]: Shape[PropName] extends ShapeSpecifier<any, typeof exactSymbol> ? ShapeToRuntimeType<Shape[PropName], true, IsReadonly> : ShapeToRuntimeType<Shape[PropName], IsExact, IsReadonly>; | ||
}> : Shape; | ||
export type ShapeToRuntimeType<Shape, IsExact extends boolean, IsReadonly extends boolean> = Shape extends AnyFunction ? Shape : Shape extends object ? Shape extends ShapeDefinition<infer InnerShape, any> ? ShapeToRuntimeType<InnerShape, IsExact, IsReadonly> : Shape extends ShapeSpecifier<any, any> ? Shape extends ShapeSpecifier<any, typeof exactSymbol> ? SpecifierToRuntimeType<Shape, true, IsReadonly> : SpecifierToRuntimeType<Shape, IsExact, IsReadonly> : Shape extends Array<any> ? OptionallyReadonly<IsReadonly, { | ||
[Prop in keyof Shape]: Shape[Prop] extends ShapeSpecifier<any, typeof exactSymbol> ? ShapeToRuntimeType<Shape[Prop], true, IsReadonly> : ShapeToRuntimeType<Shape[Prop], IsExact, IsReadonly>; | ||
}> : OptionallyReadonly<IsReadonly, Simplify<{ | ||
[Prop in keyof Shape as Shape[Prop] extends ShapeOptional<any> ? never : Prop]: Shape[Prop] extends ShapeOptional<any> ? never : Shape[Prop] extends ShapeSpecifier<any, typeof exactSymbol> ? ShapeToRuntimeType<Shape[Prop], true, IsReadonly> : ShapeToRuntimeType<Shape[Prop], IsExact, IsReadonly>; | ||
} & { | ||
[Prop in keyof Shape as Shape[Prop] extends ShapeOptional<any> ? Prop : never]?: Shape[Prop] extends ShapeOptional<any> ? Shape[Prop] extends ShapeSpecifier<any, typeof exactSymbol> ? ShapeToRuntimeType<Shape[Prop], true, IsReadonly> : ShapeToRuntimeType<Shape[Prop], IsExact, IsReadonly> : never; | ||
}>> : Shape; | ||
/** | ||
@@ -384,0 +460,0 @@ * Checks if the given `subject` matches the given `shape`. |
@@ -43,2 +43,4 @@ import { check } from '@augment-vir/assert'; | ||
const unknownSymbol = Symbol('unknown'); | ||
const numericRangeSymbol = Symbol('numeric-range'); | ||
const optionalSymbol = Symbol('optional'); | ||
/** | ||
@@ -57,2 +59,4 @@ * Symbols used to mark the outputs of each sub-shape function (like {@link or}). | ||
unknownSymbol, | ||
numericRangeSymbol, | ||
optionalSymbol, | ||
]; | ||
@@ -214,3 +218,3 @@ /** | ||
* | ||
* // `myShape.runtimeType` is `{a: unknown` | ||
* // `myShape.runtimeType` is `{a: unknown}` | ||
* ``` | ||
@@ -222,2 +226,49 @@ */ | ||
/** | ||
* Define a shape part that requires numbers to be within a specific range, inclusive. | ||
* | ||
* @category Shape Part | ||
* @example | ||
* | ||
* ```ts | ||
* import {numericRange, defineShape} from 'object-shape-tester'; | ||
* | ||
* const myShape = defineShape({ | ||
* // This will simply produce a type of `number` but will validate runtime values against the range. | ||
* a: numericRange(1, 10), | ||
* }); | ||
* // `myShape.runtimeType` is just `{a: number}` | ||
* | ||
* const myShape2 = defineShape({ | ||
* // If you want type safety, you must specify the allowed numbers manually | ||
* a: numericRange<1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10>(1, 10), | ||
* }); | ||
* // `myShape2.runtimeType` is `{a: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10}` | ||
* ``` | ||
*/ | ||
export function numericRange(min, max) { | ||
return specifier([ | ||
min, | ||
max, | ||
], numericRangeSymbol); | ||
} | ||
/** | ||
* Define a shape part that is optional. This only makes sense as a property in an object. | ||
* | ||
* @category Shape Part | ||
* @example | ||
* | ||
* ```ts | ||
* import {optional, defineShape} from 'object-shape-tester'; | ||
* | ||
* const myShape = defineShape({ | ||
* a: optional(-1), | ||
* }); | ||
* | ||
* // `myShape.runtimeType` is `{a?: number}` | ||
* ``` | ||
*/ | ||
export function optional(part) { | ||
return specifier([part], optionalSymbol); | ||
} | ||
/** | ||
* ======================================== | ||
@@ -230,3 +281,3 @@ * | ||
/** | ||
* Checks if the input is an `and` shape specifier for internal type guarding purposes. | ||
* Checks if the input is an {@link and} shape specifier for internal type guarding purposes. | ||
* | ||
@@ -239,3 +290,3 @@ * @category Internal | ||
/** | ||
* Checks if the input is a `classShape` shape specifier for internal type guarding purposes. | ||
* Checks if the input is a {@link classShape} shape specifier for internal type guarding purposes. | ||
* | ||
@@ -248,3 +299,3 @@ * @category Internal | ||
/** | ||
* Checks if the input is an `enumShape` shape specifier for internal type guarding purposes. | ||
* Checks if the input is an {@link enumShape} shape specifier for internal type guarding purposes. | ||
* | ||
@@ -257,3 +308,3 @@ * @category Internal | ||
/** | ||
* Checks if the input is an `exact` shape specifier for internal type guarding purposes. | ||
* Checks if the input is an {@link exact} shape specifier for internal type guarding purposes. | ||
* | ||
@@ -266,3 +317,3 @@ * @category Internal | ||
/** | ||
* Checks if the input is an `indexedKeys` shape specifier for internal type guarding purposes. | ||
* Checks if the input is an {@link indexedKeys} shape specifier for internal type guarding purposes. | ||
* | ||
@@ -275,3 +326,3 @@ * @category Internal | ||
/** | ||
* Checks if the input is an `or` shape specifier for internal type guarding purposes. | ||
* Checks if the input is an {@link or} shape specifier for internal type guarding purposes. | ||
* | ||
@@ -284,3 +335,4 @@ * @category Internal | ||
/** | ||
* Checks if the input is an `unknown` shape specifier for internal type guarding purposes. | ||
* Checks if the input is an {@link unknownShape} shape specifier for internal type guarding | ||
* purposes. | ||
* | ||
@@ -293,2 +345,18 @@ * @category Internal | ||
/** | ||
* Checks if the input is a {@link numericRange} shape specifier for internal type guarding purposes. | ||
* | ||
* @category Internal | ||
*/ | ||
export function isNumericRangeShapeSpecifier(maybeSpecifier) { | ||
return specifierHasSymbol(maybeSpecifier, numericRangeSymbol); | ||
} | ||
/** | ||
* Checks if the input is a {@link optional} shape specifier for internal type guarding purposes. | ||
* | ||
* @category Internal | ||
*/ | ||
export function isOptionalShapeSpecifier(maybeSpecifier) { | ||
return specifierHasSymbol(maybeSpecifier, optionalSymbol); | ||
} | ||
/** | ||
* ======================================== | ||
@@ -319,3 +387,9 @@ * | ||
if (specifier) { | ||
if (isClassShapeSpecifier(specifier)) { | ||
if (isNumericRangeShapeSpecifier(specifier)) { | ||
if (!check.isNumber(subject)) { | ||
return false; | ||
} | ||
return subject >= specifier.parts[0] && subject <= specifier.parts[1]; | ||
} | ||
else if (isClassShapeSpecifier(specifier)) { | ||
return subject instanceof specifier.parts[0]; | ||
@@ -322,0 +396,0 @@ } |
import { check } from '@augment-vir/assert'; | ||
import { extractErrorMessage, mapObjectValues } from '@augment-vir/common'; | ||
import { DefaultValueConstructionError } from '../errors/default-value-construction.error.js'; | ||
import { expandIndexedKeysKeys, getShapeSpecifier, isAndShapeSpecifier, isClassShapeSpecifier, isEnumShapeSpecifier, isExactShapeSpecifier, isIndexedKeysSpecifier, isOrShapeSpecifier, isShapeDefinition, isUnknownShapeSpecifier, } from './shape-specifiers.js'; | ||
import { expandIndexedKeysKeys, getShapeSpecifier, isAndShapeSpecifier, isClassShapeSpecifier, isEnumShapeSpecifier, isExactShapeSpecifier, isIndexedKeysSpecifier, isNumericRangeShapeSpecifier, isOptionalShapeSpecifier, isOrShapeSpecifier, isShapeDefinition, isUnknownShapeSpecifier, } from './shape-specifiers.js'; | ||
export function shapeToDefaultValue(shape) { | ||
@@ -11,3 +11,9 @@ return innerShapeToDefaultValue(shape); | ||
if (specifier) { | ||
if (isClassShapeSpecifier(specifier)) { | ||
if (isOptionalShapeSpecifier(specifier)) { | ||
return innerShapeToDefaultValue(specifier.parts[0]); | ||
} | ||
else if (isNumericRangeShapeSpecifier(specifier)) { | ||
return specifier.parts[0]; | ||
} | ||
else if (isClassShapeSpecifier(specifier)) { | ||
const classConstructor = specifier.parts[0]; | ||
@@ -14,0 +20,0 @@ try { |
import { check } from '@augment-vir/assert'; | ||
import { combineErrorMessages, ensureErrorAndPrependMessage, getObjectTypedKeys, mapObjectValues, } from '@augment-vir/common'; | ||
import { getShapeSpecifier, isAndShapeSpecifier, isClassShapeSpecifier, isEnumShapeSpecifier, isExactShapeSpecifier, isIndexedKeysSpecifier, isOrShapeSpecifier, isShapeDefinition, isUnknownShapeSpecifier, matchesShape, } from '../define-shape/shape-specifiers.js'; | ||
import { getShapeSpecifier, isAndShapeSpecifier, isClassShapeSpecifier, isEnumShapeSpecifier, isExactShapeSpecifier, isIndexedKeysSpecifier, isOptionalShapeSpecifier, isOrShapeSpecifier, isShapeDefinition, isUnknownShapeSpecifier, matchesShape, } from '../define-shape/shape-specifiers.js'; | ||
import { ShapeMismatchError } from '../errors/shape-mismatch.error.js'; | ||
@@ -94,2 +94,14 @@ /** | ||
} | ||
else if (isOptionalShapeSpecifier(shape)) { | ||
/** | ||
* The optional specifier does not add any extra restrictions when the subject actually | ||
* exists. Thus, we'll just compare the subject to the optional shape's input. | ||
*/ | ||
return internalAssertValidShape({ | ||
keys, | ||
options, | ||
shape: shape.parts[0], | ||
subject, | ||
}); | ||
} | ||
else if (!matchesShape(subject, shape, !options.ignoreExtraKeys)) { | ||
@@ -122,5 +134,3 @@ throw new ShapeMismatchError(`Subject does not match shape definition at key ${keysString}`); | ||
keys, | ||
options: { | ||
...options, | ||
}, | ||
options, | ||
}); | ||
@@ -254,3 +264,3 @@ Object.assign(keysPassed, newKeysPassed); | ||
else { | ||
// if we have no specifier, pass in the whole shape itself | ||
/** If we have no specifier, check the whole object. */ | ||
const newKeysPassed = isValidRawObjectShape({ | ||
@@ -303,4 +313,7 @@ keys, | ||
shapeKeys.forEach((shapeKey) => { | ||
// try to account for non-enumerable keys | ||
if (shapeKey in subject) { | ||
if ( | ||
/** Account for non-enumerable keys. */ | ||
shapeKey in subject || | ||
/** Account for optional keys. */ | ||
isOptionalShapeSpecifier(shape[shapeKey])) { | ||
subjectKeys.add(shapeKey); | ||
@@ -327,2 +340,7 @@ } | ||
subjectKeys.forEach((key) => { | ||
/** If the key doesn't exist and it's optional, mark it as passed. */ | ||
if (!(key in subject) && isOptionalShapeSpecifier(shape[key])) { | ||
keysPassed[key] = true; | ||
return; | ||
} | ||
const subjectChild = subject[key]; | ||
@@ -347,5 +365,5 @@ if (options.ignoreExtraKeys && !shapeKeys.has(key)) { | ||
else { | ||
throw new ShapeMismatchError(`shape definition at ${keysString} was not an object.`); | ||
throw new ShapeMismatchError(`Shape definition at ${keysString} was not an object.`); | ||
} | ||
return keysPassed; | ||
} |
@@ -5,1 +5,2 @@ /** | ||
*/ | ||
export {}; |
@@ -1,6 +0,1 @@ | ||
"use strict"; | ||
/* eslint-disable unicorn/no-empty-file */ | ||
/** | ||
* This file is helpful for easy in-browser debugging. Simply run `npm start` to start a web-server | ||
* that hosts it. | ||
*/ | ||
export {}; |
{ | ||
"name": "object-shape-tester", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"description": "Test object properties and value types.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
77446
1803
0