Skip to content

Commit 9939a54

Browse files
committed
Change optional to include undefined
The previous implementation of `optional` is now `optionalOrNull`.
1 parent 4e30aa9 commit 9939a54

4 files changed

Lines changed: 69 additions & 30 deletions

File tree

lib/entry-points.js

Lines changed: 16 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/json/index.test.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ const testSchema = {
1010
requiredKey: json.string,
1111
};
1212

13-
const optionalSchema = {
14-
optionalKey: json.optional(json.string),
13+
const optionalOrNullSchema = {
14+
optionalKey: json.optionalOrNull(json.string),
1515
};
1616

1717
test("validateSchema - required properties are required", async (t) => {
@@ -30,11 +30,34 @@ test("validateSchema - required properties are required", async (t) => {
3030

3131
test("validateSchema - optional properties are optional", async (t) => {
3232
// Optional fields may be absent
33+
t.true(json.validateSchema(optionalOrNullSchema, {}));
34+
t.true(json.validateSchema(optionalOrNullSchema, { optionalKey: undefined }));
35+
t.true(json.validateSchema(optionalOrNullSchema, { optionalKey: null }));
36+
37+
// But, if present, should have the expected type
38+
t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: 0 }));
39+
t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: 123 }));
40+
t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: false }));
41+
t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: true }));
42+
t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: [] }));
43+
t.false(json.validateSchema(optionalOrNullSchema, { optionalKey: {} }));
44+
t.true(json.validateSchema(optionalOrNullSchema, { optionalKey: "" }));
45+
t.true(json.validateSchema(optionalOrNullSchema, { optionalKey: "foo" }));
46+
});
47+
48+
const optionalSchema = {
49+
optionalKey: json.optional(json.string),
50+
};
51+
52+
test("validateSchema - optional properties may be absent or undefined, but reject null", async (t) => {
53+
// Optional fields may be absent or explicitly undefined
3354
t.true(json.validateSchema(optionalSchema, {}));
3455
t.true(json.validateSchema(optionalSchema, { optionalKey: undefined }));
35-
t.true(json.validateSchema(optionalSchema, { optionalKey: null }));
3656

37-
// But, if present, should have the expected type
57+
// But should reject null
58+
t.false(json.validateSchema(optionalSchema, { optionalKey: null }));
59+
60+
// And, if present, should have the expected type
3861
t.false(json.validateSchema(optionalSchema, { optionalKey: 0 }));
3962
t.false(json.validateSchema(optionalSchema, { optionalKey: 123 }));
4063
t.false(json.validateSchema(optionalSchema, { optionalKey: false }));

src/json/index.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,11 @@ export const object = {
7272
required: true,
7373
} as const satisfies Validator<UnvalidatedObject<unknown>>;
7474

75-
/** Transforms a validator to be optional. */
76-
export function optional<T>(validator: Validator<T>) {
75+
/**
76+
* Transforms a validator to be optional, accepting `undefined` or `null` for an
77+
* absent value.
78+
*/
79+
export function optionalOrNull<T>(validator: Validator<T>) {
7780
return {
7881
validate: (val: unknown) => {
7982
return val === undefined || val === null || validator.validate(val);
@@ -82,6 +85,19 @@ export function optional<T>(validator: Validator<T>) {
8285
} as const satisfies Validator<T | undefined | null>;
8386
}
8487

88+
/**
89+
* Transforms a validator to be optional, accepting `undefined` for an absent
90+
* value but, unlike `optionalOrNull`, rejecting `null`.
91+
*/
92+
export function optional<T>(validator: Validator<T>) {
93+
return {
94+
validate: (val: unknown): val is T | undefined => {
95+
return val === undefined || validator.validate(val);
96+
},
97+
required: false,
98+
} as const satisfies Validator<T | undefined>;
99+
}
100+
85101
/** Represents an arbitrary object schema. */
86102
export type Schema = Record<string, Validator<any>>;
87103

src/start-proxy/types.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export type RawCredential = UnvalidatedObject<Credential>;
1212
/** A schema for credential objects with a username. */
1313
export const usernameSchema = {
1414
/** The username needed to authenticate to the package registry, if any. */
15-
username: json.optional(json.string),
15+
username: json.optionalOrNull(json.string),
1616
} as const satisfies json.Schema;
1717

1818
/** Usernames may be present for both authentication with tokens or passwords. */
@@ -29,7 +29,7 @@ export function hasUsername(config: AuthConfig): config is Username {
2929
/** A schema for credential objects with a username and password. */
3030
export const usernamePasswordSchema = {
3131
/** The password needed to authenticate to the package registry, if any. */
32-
password: json.optional(json.string),
32+
password: json.optionalOrNull(json.string),
3333
...usernameSchema,
3434
} as const satisfies json.Schema;
3535

@@ -52,7 +52,7 @@ export function hasUsernameAndPassword(
5252
/** A schema for credential objects for token-based authentication. */
5353
export const tokenSchema = {
5454
/** The token needed to authenticate to the package registry, if any. */
55-
token: json.optional(json.string),
55+
token: json.optionalOrNull(json.string),
5656
...usernameSchema,
5757
} as const satisfies json.Schema;
5858

@@ -100,7 +100,7 @@ export const awsConfigSchema = {
100100
"role-name": json.string,
101101
domain: json.string,
102102
"domain-owner": json.string,
103-
audience: json.optional(json.string),
103+
audience: json.optionalOrNull(json.string),
104104
} as const satisfies json.Schema;
105105

106106
/** Configuration for AWS OIDC. */
@@ -116,8 +116,8 @@ export function isAWSConfig(
116116
/** A schema for JFrog OIDC configurations. */
117117
export const jfrogConfigSchema = {
118118
"jfrog-oidc-provider-name": json.string,
119-
audience: json.optional(json.string),
120-
"identity-mapping-name": json.optional(json.string),
119+
audience: json.optionalOrNull(json.string),
120+
"identity-mapping-name": json.optionalOrNull(json.string),
121121
} as const satisfies json.Schema;
122122

123123
/** Configuration for JFrog OIDC. */
@@ -150,8 +150,8 @@ export function isCloudsmithConfig(
150150
/** A schema for GCP OIDC configurations. */
151151
export const gcpConfigSchema = {
152152
"workload-identity-provider": json.string,
153-
"service-account": json.optional(json.string),
154-
audience: json.optional(json.string),
153+
"service-account": json.optionalOrNull(json.string),
154+
audience: json.optionalOrNull(json.string),
155155
} as const satisfies json.Schema;
156156

157157
/** Configuration for GCP OIDC. */

0 commit comments

Comments
 (0)