Rename optional and undefinable functions for clarity; update related schemas to use optionalOrNull

This commit is contained in:
Mario Campos
2026-06-19 13:55:56 -05:00
parent c8e32e423d
commit defcf1bff6
5 changed files with 53 additions and 53 deletions

View File

@@ -240,8 +240,8 @@ export function isVersionInfo(x: unknown): x is VersionInfo {
json.validateSchema(
{
version: json.string,
features: json.undefinable(json.object),
overlayVersion: json.undefinable(json.number),
features: json.optional(json.object),
overlayVersion: json.optional(json.number),
},
x,
)

View File

@@ -10,8 +10,8 @@ const testSchema = {
requiredKey: json.string,
};
const optionalSchema = {
optionalKey: json.optional(json.string),
const optionalOrNullSchema = {
optionalKey: json.optionalOrNull(json.string),
};
test("validateSchema - required properties are required", async (t) => {
@@ -30,11 +30,34 @@ test("validateSchema - required properties are required", async (t) => {
test("validateSchema - optional properties are optional", async (t) => {
// Optional fields may be absent
t.true(json.validateSchema(optionalSchema, {}));
t.true(json.validateSchema(optionalSchema, { optionalKey: undefined }));
t.true(json.validateSchema(optionalSchema, { optionalKey: null }));
t.true(json.validateSchema(optionalOrNullSchema, {}));
t.true(json.validateSchema(optionalOrNullSchema, { optionalKey: undefined }));
t.true(json.validateSchema(optionalOrNullSchema, { optionalKey: null }));
// But, if present, should have the expected type
t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: 0 }));
t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: 123 }));
t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: false }));
t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: true }));
t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: [] }));
t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: {} }));
t.true(json.validateSchema(optionalOrNullSchema, { optionalKey: "" }));
t.true(json.validateSchema(optionalOrNullSchema, { optionalKey: "foo" }));
});
const optionalSchema = {
optionalKey: json.optional(json.string),
};
test("validateSchema - optional properties may be absent or undefined, but reject null", async (t) => {
// Optional fields may be absent or explicitly undefined
t.true(json.validateSchema(optionalSchema, {}));
t.true(json.validateSchema(optionalSchema, { optionalKey: undefined }));
// But should reject null
t.false(json.validateSchema(optionalSchema, { optionalKey: null }));
// And, if present, should have the expected type
t.false(json.validateSchema(optionalSchema, { optionalKey: 0 }));
t.false(json.validateSchema(optionalSchema, { optionalKey: 123 }));
t.false(json.validateSchema(optionalSchema, { optionalKey: false }));
@@ -44,26 +67,3 @@ test("validateSchema - optional properties are optional", async (t) => {
t.true(json.validateSchema(optionalSchema, { optionalKey: "" }));
t.true(json.validateSchema(optionalSchema, { optionalKey: "foo" }));
});
const undefinableSchema = {
optionalKey: json.undefinable(json.string),
};
test("validateSchema - undefinable properties are optional but reject null", async (t) => {
// Optional fields may be absent or explicitly undefined
t.true(json.validateSchema(undefinableSchema, {}));
t.true(json.validateSchema(undefinableSchema, { optionalKey: undefined }));
// But, unlike `optional`, `null` is rejected
t.false(json.validateSchema(undefinableSchema, { optionalKey: null }));
// And, if present, should have the expected type
t.false(json.validateSchema(undefinableSchema, { optionalKey: 0 }));
t.false(json.validateSchema(undefinableSchema, { optionalKey: 123 }));
t.false(json.validateSchema(undefinableSchema, { optionalKey: false }));
t.false(json.validateSchema(undefinableSchema, { optionalKey: true }));
t.false(json.validateSchema(undefinableSchema, { optionalKey: [] }));
t.false(json.validateSchema(undefinableSchema, { optionalKey: {} }));
t.true(json.validateSchema(undefinableSchema, { optionalKey: "" }));
t.true(json.validateSchema(undefinableSchema, { optionalKey: "foo" }));
});

View File

@@ -76,7 +76,7 @@ export const object = {
* Transforms a validator to be optional, accepting `undefined` or `null` for an
* absent value.
*/
export function optional<T>(validator: Validator<T>) {
export function optionalOrNull<T>(validator: Validator<T>) {
return {
validate: (val: unknown) => {
return val === undefined || val === null || validator.validate(val);
@@ -87,9 +87,9 @@ export function optional<T>(validator: Validator<T>) {
/**
* Transforms a validator to be optional, accepting `undefined` for an absent
* value but, unlike `optional`, rejecting `null`.
* value but, unlike `optionalOrNull`, rejecting `null`.
*/
export function undefinable<T>(validator: Validator<T>) {
export function optional<T>(validator: Validator<T>) {
return {
validate: (val: unknown): val is T | undefined => {
return val === undefined || validator.validate(val);

View File

@@ -12,7 +12,7 @@ export type RawCredential = UnvalidatedObject<Credential>;
/** A schema for credential objects with a username. */
export const usernameSchema = {
/** The username needed to authenticate to the package registry, if any. */
username: json.optional(json.string),
username: json.optionalOrNull(json.string),
} as const satisfies json.Schema;
/** Usernames may be present for both authentication with tokens or passwords. */
@@ -29,7 +29,7 @@ export function hasUsername(config: AuthConfig): config is Username {
/** A schema for credential objects with a username and password. */
export const usernamePasswordSchema = {
/** The password needed to authenticate to the package registry, if any. */
password: json.optional(json.string),
password: json.optionalOrNull(json.string),
...usernameSchema,
} as const satisfies json.Schema;
@@ -52,7 +52,7 @@ export function hasUsernameAndPassword(
/** A schema for credential objects for token-based authentication. */
export const tokenSchema = {
/** The token needed to authenticate to the package registry, if any. */
token: json.optional(json.string),
token: json.optionalOrNull(json.string),
...usernameSchema,
} as const satisfies json.Schema;
@@ -100,7 +100,7 @@ export const awsConfigSchema = {
"role-name": json.string,
domain: json.string,
"domain-owner": json.string,
audience: json.optional(json.string),
audience: json.optionalOrNull(json.string),
} as const satisfies json.Schema;
/** Configuration for AWS OIDC. */
@@ -116,8 +116,8 @@ export function isAWSConfig(
/** A schema for JFrog OIDC configurations. */
export const jfrogConfigSchema = {
"jfrog-oidc-provider-name": json.string,
audience: json.optional(json.string),
"identity-mapping-name": json.optional(json.string),
audience: json.optionalOrNull(json.string),
"identity-mapping-name": json.optionalOrNull(json.string),
} as const satisfies json.Schema;
/** Configuration for JFrog OIDC. */
@@ -150,8 +150,8 @@ export function isCloudsmithConfig(
/** A schema for GCP OIDC configurations. */
export const gcpConfigSchema = {
"workload-identity-provider": json.string,
"service-account": json.optional(json.string),
audience: json.optional(json.string),
"service-account": json.optionalOrNull(json.string),
audience: json.optionalOrNull(json.string),
} as const satisfies json.Schema;
/** Configuration for GCP OIDC. */