Skip to content

Commit 88583a6

Browse files
authored
Merge pull request #352 from enormora/zod
Migrate effect/Schema to zod
2 parents 0a136ab + f18caca commit 88583a6

36 files changed

+287
-1136
lines changed

package-lock.json

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

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
},
4242
"dependencies": {
4343
"@effect/schema": "0.60.4",
44+
"@schema-hub/zod-error-formatter": "0.0.10",
4445
"@topcli/spinner": "3.0.0",
4546
"@ts-morph/common": "0.28.0",
4647
"cmd-ts": "0.14.2",
@@ -55,7 +56,8 @@
5556
"true-myth": "8.6.0",
5657
"ts-morph": "24.0.0",
5758
"tslib": "2.8.1",
58-
"unix-permissions": "6.0.1"
59+
"unix-permissions": "6.0.1",
60+
"zod": "4.1.11"
5961
},
6062
"devDependencies": {
6163
"@ava/typescript": "6.0.0",

source/bundle-emitter/registry-client.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type _npmFetch from 'npm-registry-fetch';
22
import type { publish as _publish } from 'libnpmpublish';
33
import { Maybe } from 'true-myth';
4-
import { struct, string, optional, record, is, type Schema } from '@effect/schema/Schema';
4+
import { z } from 'zod/mini';
5+
import { safeParse } from '@schema-hub/zod-error-formatter';
56
import type { RegistrySettings } from '../config/registry-settings.js';
67
import type { BundlePackageJson } from '../version-manager/manifest/builder.js';
78

@@ -30,21 +31,21 @@ function encodePackageName(name: string): string {
3031
return name.replace('/', '%2F');
3132
}
3233

33-
const distTagsSchema = struct({
34-
latest: optional(string, { exact: true })
34+
const distTagsSchema = z.object({
35+
latest: z.optional(z.string())
3536
});
3637

37-
const versionDataSchema = struct({
38-
dist: struct({ shasum: string, tarball: string })
38+
const versionDataSchema = z.object({
39+
dist: z.object({ shasum: z.string(), tarball: z.string() })
3940
});
4041

41-
const abbreviatedPackageResponseSchema = struct({
42-
name: string,
42+
const abbreviatedPackageResponseSchema = z.object({
43+
name: z.string(),
4344
'dist-tags': distTagsSchema,
44-
versions: record(string, versionDataSchema)
45+
versions: z.record(z.string(), versionDataSchema)
4546
});
4647

47-
type AbbreviatedPackageResponse = Schema.To<typeof abbreviatedPackageResponseSchema>;
48+
type AbbreviatedPackageResponse = z.infer<typeof abbreviatedPackageResponseSchema>;
4849

4950
const httpStatusCode = {
5051
notFound: 404,
@@ -86,10 +87,12 @@ export function createRegistryClient(dependencies: Readonly<RegistryClientDepend
8687
const endpointUri = `/${encodePackageName(packageName)}`;
8788
try {
8889
const response = await fetchRegistryEndpoint(endpointUri, registrySettings);
89-
if (!is(abbreviatedPackageResponseSchema)(response)) {
90+
const result = safeParse(abbreviatedPackageResponseSchema, response);
91+
92+
if (!result.success) {
9093
throw new Error('Got an invalid response from registry API');
9194
}
92-
return Maybe.just(response);
95+
return Maybe.just(result.data);
9396
} catch (error: unknown) {
9497
if (
9598
isFetchError(error) &&

source/config/additional-files.test.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,53 +13,59 @@ test('validation succeeds when valid data is given', checkValidationSuccess, {
1313
test('validation fails when a non-object is given', checkValidationFailure, {
1414
schema: additionalFileDescriptionSchema,
1515
data: true,
16-
expectedMessages: ['Expected object; but got boolean']
16+
expectedMessages: ['expected object, but got boolean']
1717
});
1818

1919
test('validation fails when the an empty object is given', checkValidationFailure, {
2020
schema: additionalFileDescriptionSchema,
2121
data: {},
22-
expectedMessages: ['At sourceFilePath: missing key or index', 'At targetFilePath: missing key or index']
22+
expectedMessages: ['at sourceFilePath: missing property', 'at targetFilePath: missing property']
2323
});
2424

2525
test('validation fails when sourceFilePath is missing', checkValidationFailure, {
2626
schema: additionalFileDescriptionSchema,
2727
data: { targetFilePath: 'foo' },
28-
expectedMessages: ['At sourceFilePath: missing key or index']
28+
expectedMessages: ['at sourceFilePath: missing property']
2929
});
3030

3131
test('validation fails when sourceFilePath is not a string', checkValidationFailure, {
3232
schema: additionalFileDescriptionSchema,
3333
data: { sourceFilePath: [], targetFilePath: 'foo' },
34-
expectedMessages: ['At sourceFilePath: expected string; but got array']
34+
expectedMessages: [
35+
'at sourceFilePath: expected string, but got array',
36+
'at sourceFilePath: array must contain at least 1 element'
37+
]
3538
});
3639

3740
test('validation fails when sourceFilePath is an empty string', checkValidationFailure, {
3841
schema: additionalFileDescriptionSchema,
3942
data: { sourceFilePath: '', targetFilePath: 'foo' },
40-
expectedMessages: ['At sourceFilePath: expected a non empty string; but got string']
43+
expectedMessages: ['at sourceFilePath: string must contain at least 1 character']
4144
});
4245

4346
test('validation fails when targetFilePath is missing', checkValidationFailure, {
4447
schema: additionalFileDescriptionSchema,
4548
data: { sourceFilePath: 'foo' },
46-
expectedMessages: ['At targetFilePath: missing key or index']
49+
expectedMessages: ['at targetFilePath: missing property']
4750
});
4851

4952
test('validation fails when targetFilePath is not a string', checkValidationFailure, {
5053
schema: additionalFileDescriptionSchema,
5154
data: { targetFilePath: [], sourceFilePath: 'foo' },
52-
expectedMessages: ['At targetFilePath: expected string; but got array']
55+
expectedMessages: [
56+
'at targetFilePath: expected string, but got array',
57+
'at targetFilePath: array must contain at least 1 element'
58+
]
5359
});
5460

5561
test('validation fails when targetFilePath is an empty string', checkValidationFailure, {
5662
schema: additionalFileDescriptionSchema,
5763
data: { targetFilePath: '', sourceFilePath: 'foo' },
58-
expectedMessages: ['At targetFilePath: expected a non empty string; but got string']
64+
expectedMessages: ['at targetFilePath: string must contain at least 1 character']
5965
});
6066

6167
test('validation fails when an additional unknown property is given', checkValidationFailure, {
6268
schema: additionalFileDescriptionSchema,
6369
data: { targetFilePath: 'bar', sourceFilePath: 'foo', something: 'else' },
64-
expectedMessages: ['At something: unexpected extra key or index']
70+
expectedMessages: ['unexpected additional property: "something"']
6571
});

source/config/additional-files.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { type Schema, struct } from '@effect/schema/Schema';
2-
import { type NoExpand, nonEmptyStringSchema } from './base-validations.js';
1+
import { z } from 'zod/mini';
2+
import { nonEmptyStringSchema } from './base-validations.js';
33

4-
const $additionalFileDescriptionSchema = struct({
5-
sourceFilePath: nonEmptyStringSchema,
6-
targetFilePath: nonEmptyStringSchema
7-
});
4+
export const additionalFileDescriptionSchema = z.readonly(
5+
z.strictObject({
6+
sourceFilePath: nonEmptyStringSchema,
7+
targetFilePath: nonEmptyStringSchema
8+
})
9+
);
810

9-
export type AdditionalFileDescription = NoExpand<Schema.To<typeof $additionalFileDescriptionSchema>>;
10-
11-
export const additionalFileDescriptionSchema: Schema<AdditionalFileDescription> = $additionalFileDescriptionSchema;
11+
export type AdditionalFileDescription = z.infer<typeof additionalFileDescriptionSchema>;

source/config/base-validations.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { string, nonEmpty } from '@effect/schema/Schema';
1+
import { z } from 'zod/mini';
22

3-
export const nonEmptyStringSchema = string.pipe(nonEmpty());
4-
5-
export type NoExpand<T> = T & { readonly _?: never };
3+
export const nonEmptyStringSchema = z.string().check(z.minLength(1));

0 commit comments

Comments
 (0)