diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml
index 712831013..004d67b9a 100644
--- a/.github/workflows/publish-release.yml
+++ b/.github/workflows/publish-release.yml
@@ -11,6 +11,7 @@ env:
permissions:
contents: write
+ id-token: write
jobs:
publish-and-release:
@@ -28,7 +29,7 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
- node-version: 22.x
+ node-version: 24.x
cache: 'pnpm'
registry-url: 'https://registry.npmjs.org'
@@ -55,7 +56,7 @@ jobs:
- name: Publish packages
run: pnpm run publish-all
env:
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+ NPM_CONFIG_PROVENANCE: 'true'
- name: Generate changelog
id: changelog
diff --git a/package.json b/package.json
index a8076383a..8456e81df 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "zenstack-v3",
"displayName": "ZenStack",
"description": "ZenStack",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/auth-adapters/better-auth/package.json b/packages/auth-adapters/better-auth/package.json
index ad40b27ee..a97ef4a75 100644
--- a/packages/auth-adapters/better-auth/package.json
+++ b/packages/auth-adapters/better-auth/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/better-auth",
"displayName": "ZenStack Better Auth Adapter",
"description": "ZenStack Better Auth Adapter. This adapter is modified from better-auth's Prisma adapter.",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 931b71ca3..5ce77f6cb 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/cli",
"displayName": "ZenStack CLI",
"description": "FullStack database toolkit with built-in access control and automatic API generation.",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/cli/test/db/pull.test.ts b/packages/cli/test/db/pull.test.ts
index 811c20ccf..58649e3da 100644
--- a/packages/cli/test/db/pull.test.ts
+++ b/packages/cli/test/db/pull.test.ts
@@ -621,6 +621,7 @@ enum Status {
`model User {
id Int @id @default(autoincrement())
email String @unique @email
+ phone String @phone
name String @length(min: 2, max: 100)
website String? @url
code String? @regex('^[A-Z]+$')
diff --git a/packages/clients/client-helpers/README.md b/packages/clients/client-helpers/README.md
new file mode 100644
index 000000000..5fc4a8416
--- /dev/null
+++ b/packages/clients/client-helpers/README.md
@@ -0,0 +1,3 @@
+# @zenstackhq/client-helpers
+
+Shared building blocks for implementing clients that consume ZenStack's CRUD service. Used internally by [`@zenstackhq/fetch-client`](../fetch-client) and [`@zenstackhq/tanstack-query`](../tanstack-query), and useful when building your own custom client.
diff --git a/packages/clients/client-helpers/package.json b/packages/clients/client-helpers/package.json
index 83fc10fba..40f5b6fbc 100644
--- a/packages/clients/client-helpers/package.json
+++ b/packages/clients/client-helpers/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/client-helpers",
"displayName": "ZenStack Client Helpers",
"description": "Helpers for implementing clients that consume ZenStack's CRUD service",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/clients/fetch-client/README.md b/packages/clients/fetch-client/README.md
new file mode 100644
index 000000000..1afb9986c
--- /dev/null
+++ b/packages/clients/fetch-client/README.md
@@ -0,0 +1,32 @@
+# @zenstackhq/fetch-client
+
+A lightweight, type-safe fetch-based client for consuming ZenStack's RPC-style auto CRUD API. Provides the same model and operation surface as the server-side `ZenStackClient`, but issues HTTP requests instead of running queries locally.
+
+## Installation
+
+```bash
+npm install @zenstackhq/fetch-client
+```
+
+## Usage
+
+```typescript
+import { createClient } from '@zenstackhq/fetch-client';
+import { schema } from './schema';
+
+const client = createClient(schema, {
+ endpoint: 'https://example.com/api/model',
+});
+
+const users = await client.user.findMany({ where: { active: true } });
+const post = await client.post.create({ data: { title: 'Hello' } });
+
+const [user, newPost] = await client.$transaction([
+ { model: 'User', op: 'create', args: { data: { email: 'alice@example.com' } } },
+ { model: 'Post', op: 'create', args: { data: { title: 'Hello' } } },
+]);
+```
+
+## Learn More
+
+- [ZenStack Documentation](https://zenstack.dev/docs/service/client-sdk/fetch-client)
diff --git a/packages/clients/fetch-client/package.json b/packages/clients/fetch-client/package.json
index 3ffc77c2b..6836ffe1c 100644
--- a/packages/clients/fetch-client/package.json
+++ b/packages/clients/fetch-client/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/fetch-client",
"displayName": "ZenStack Fetch Client",
"description": "Simple fetch-based client for consuming ZenStack's RPC-style CRUD API",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/clients/tanstack-query/README.md b/packages/clients/tanstack-query/README.md
new file mode 100644
index 000000000..0f62ff0a1
--- /dev/null
+++ b/packages/clients/tanstack-query/README.md
@@ -0,0 +1,41 @@
+# @zenstackhq/tanstack-query
+
+[TanStack Query](https://tanstack.com/query) integration for ZenStack. Generates fully typed query and mutation hooks from your ZModel schema for React, Vue, and Svelte, with built-in cache invalidation and optimistic update helpers.
+
+## Supported Frameworks
+
+- **React** — `@zenstackhq/tanstack-query/react`
+- **Vue** — `@zenstackhq/tanstack-query/vue`
+- **Svelte** — `@zenstackhq/tanstack-query/svelte`
+
+## Installation
+
+```bash
+npm install @zenstackhq/tanstack-query @tanstack/react-query
+```
+
+Replace `@tanstack/react-query` with `@tanstack/vue-query` or `@tanstack/svelte-query` as needed.
+
+## Usage (React example)
+
+```tsx
+import { useClientQueries } from '@zenstackhq/tanstack-query/react';
+import { schema } from './schema';
+
+function PostList() {
+ const client = useClientQueries(schema);
+
+ const { data: posts } = client.post.useQuery({
+ where: { published: true },
+ include: { author: true },
+ });
+
+ const { mutate: createPost } = client.post.useMutation();
+
+ return
{posts?.map((p) => - {p.title}
)}
;
+}
+```
+
+## Learn More
+
+- [ZenStack Documentation](https://zenstack.dev/docs/service/client-sdk/tanstack-query/)
diff --git a/packages/clients/tanstack-query/package.json b/packages/clients/tanstack-query/package.json
index 54172d272..90d4add21 100644
--- a/packages/clients/tanstack-query/package.json
+++ b/packages/clients/tanstack-query/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/tanstack-query",
"displayName": "ZenStack TanStack Query Integration",
"description": "TanStack Query Client for consuming ZenStack v3's CRUD service",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/common-helpers/package.json b/packages/common-helpers/package.json
index 7ce8e86de..c570e0e58 100644
--- a/packages/common-helpers/package.json
+++ b/packages/common-helpers/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/common-helpers",
"displayName": "ZenStack Common Helpers",
"description": "ZenStack Common Helpers",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/config/eslint-config/package.json b/packages/config/eslint-config/package.json
index 8651d6432..560889bd7 100644
--- a/packages/config/eslint-config/package.json
+++ b/packages/config/eslint-config/package.json
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/eslint-config",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"private": true,
"license": "MIT"
diff --git a/packages/config/tsdown-config/package.json b/packages/config/tsdown-config/package.json
index 5203ce764..477d67c02 100644
--- a/packages/config/tsdown-config/package.json
+++ b/packages/config/tsdown-config/package.json
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/tsdown-config",
- "version": "3.7.1",
+ "version": "3.7.2",
"private": true,
"type": "module",
"license": "MIT",
diff --git a/packages/config/typescript-config/package.json b/packages/config/typescript-config/package.json
index 673bb69f5..3f7337055 100644
--- a/packages/config/typescript-config/package.json
+++ b/packages/config/typescript-config/package.json
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/typescript-config",
- "version": "3.7.1",
+ "version": "3.7.2",
"private": true,
"license": "MIT"
}
diff --git a/packages/config/vitest-config/package.json b/packages/config/vitest-config/package.json
index 713cabb63..058d5ce67 100644
--- a/packages/config/vitest-config/package.json
+++ b/packages/config/vitest-config/package.json
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/vitest-config",
"type": "module",
- "version": "3.7.1",
+ "version": "3.7.2",
"private": true,
"license": "MIT",
"exports": {
diff --git a/packages/create-zenstack/package.json b/packages/create-zenstack/package.json
index 8515e7629..f20cfed63 100644
--- a/packages/create-zenstack/package.json
+++ b/packages/create-zenstack/package.json
@@ -2,7 +2,7 @@
"name": "create-zenstack",
"displayName": "Create ZenStack",
"description": "Create a new ZenStack project",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/ide/vscode/package.json b/packages/ide/vscode/package.json
index c6f85e050..7931002c9 100644
--- a/packages/ide/vscode/package.json
+++ b/packages/ide/vscode/package.json
@@ -1,7 +1,7 @@
{
"name": "zenstack-v3",
"publisher": "zenstack",
- "version": "3.7.1",
+ "version": "3.7.2",
"displayName": "ZenStack V3 Language Tools",
"description": "VSCode extension for ZenStack (v3) ZModel language",
"private": true,
diff --git a/packages/language/package.json b/packages/language/package.json
index 75a47aa41..97d5f2805 100644
--- a/packages/language/package.json
+++ b/packages/language/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/language",
"displayName": "ZenStack Language Tooling",
"description": "ZenStack ZModel language specification",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/language/res/stdlib.zmodel b/packages/language/res/stdlib.zmodel
index 99dafdbd5..4561da14a 100644
--- a/packages/language/res/stdlib.zmodel
+++ b/packages/language/res/stdlib.zmodel
@@ -551,6 +551,11 @@ attribute @datetime(_ message: String?) @@@targetField([StringField]) @@@validat
*/
attribute @url(_ message: String?) @@@targetField([StringField]) @@@validation
+/**
+ * Validates a string field value is a valid E.164 phone number.
+ */
+attribute @phone(_ message: String?) @@@targetField([StringField]) @@@validation
+
/**
* Trims whitespaces from the start and end of the string.
*/
@@ -622,6 +627,12 @@ function isDateTime(field: String): Boolean {
function isUrl(field: String): Boolean {
} @@@expressionContext([ValidationRule])
+/**
+ * Validates a string field value is a valid E.164 phone number.
+ */
+function isPhone(field: String): Boolean {
+} @@@expressionContext([ValidationRule])
+
//////////////////////////////////////////////
// End validation attributes and functions
//////////////////////////////////////////////
diff --git a/packages/orm/package.json b/packages/orm/package.json
index a72106534..ce62edc31 100644
--- a/packages/orm/package.json
+++ b/packages/orm/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/orm",
"displayName": "ZenStack ORM",
"description": "ZenStack ORM",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/plugins/policy/package.json b/packages/plugins/policy/package.json
index fbea698c0..9cbb28126 100644
--- a/packages/plugins/policy/package.json
+++ b/packages/plugins/policy/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/plugin-policy",
"displayName": "ZenStack Access Policy Plugin",
"description": "ZenStack plugin that enforces access control policies defined in the schema",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/schema/package.json b/packages/schema/package.json
index efd85e628..a5e42c1fb 100644
--- a/packages/schema/package.json
+++ b/packages/schema/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/schema",
"displayName": "ZenStack Schema Object Model",
"description": "TypeScript representation of ZModel schema",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/sdk/package.json b/packages/sdk/package.json
index 54a23b2bf..d886d8765 100644
--- a/packages/sdk/package.json
+++ b/packages/sdk/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/sdk",
"displayName": "ZenStack SDK",
"description": "Utilities for building ZenStack plugins",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/server/package.json b/packages/server/package.json
index 1f9cf3349..f71f11d6e 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/server",
"displayName": "ZenStack Automatic CRUD Server",
"description": "ZenStack automatic CRUD API handlers and server adapters for popular frameworks",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/testtools/package.json b/packages/testtools/package.json
index d26f90f1b..5e9cb76e7 100644
--- a/packages/testtools/package.json
+++ b/packages/testtools/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/testtools",
"displayName": "ZenStack Test Tools",
"description": "ZenStack Test Tools",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/zod/package.json b/packages/zod/package.json
index 3cae1587d..4161686f8 100644
--- a/packages/zod/package.json
+++ b/packages/zod/package.json
@@ -2,7 +2,7 @@
"name": "@zenstackhq/zod",
"displayName": "ZenStack Zod Integration",
"description": "Automatically deriving Zod schemas from ZModel schemas",
- "version": "3.7.1",
+ "version": "3.7.2",
"type": "module",
"author": {
"name": "ZenStack Team",
diff --git a/packages/zod/src/utils.ts b/packages/zod/src/utils.ts
index 9081c1c86..f21d9196f 100644
--- a/packages/zod/src/utils.ts
+++ b/packages/zod/src/utils.ts
@@ -72,6 +72,9 @@ export function addStringValidation(
case '@email':
result = result.email();
break;
+ case '@phone':
+ result = result.e164();
+ break;
case '@datetime':
result = result.datetime();
break;
@@ -533,12 +536,19 @@ function evalCall(data: any, expr: CallExpression) {
}
case 'isEmail':
case 'isUrl':
+ case 'isPhone':
case 'isDateTime': {
if (fieldArg === undefined || fieldArg === null || fieldArg === ABSENT) {
return false;
}
invariant(typeof fieldArg === 'string', `"${f}" first argument must be a string`);
- const fn = f === 'isEmail' ? ('email' as const) : f === 'isUrl' ? ('url' as const) : ('datetime' as const);
+ const fn = f === 'isEmail'
+ ? ('email' as const)
+ : f === 'isUrl'
+ ? ('url' as const)
+ : f === 'isPhone'
+ ? ('e164' as const)
+ : ('datetime' as const);
return z.string()[fn]().safeParse(fieldArg).success;
}
// list functions
diff --git a/packages/zod/test/factory.test.ts b/packages/zod/test/factory.test.ts
index 0dd616c2d..f221d2b64 100644
--- a/packages/zod/test/factory.test.ts
+++ b/packages/zod/test/factory.test.ts
@@ -11,6 +11,7 @@ const factory = createSchemaFactory(schema);
const validUser = {
id: 'user123',
email: 'test@example.com',
+ phone: '+15555555555',
username: 'johndoe',
website: null,
code: 'USR001',
@@ -44,6 +45,7 @@ describe('SchemaFactory - makeModelSchema', () => {
// required string fields
expectTypeOf().toEqualTypeOf();
expectTypeOf().toEqualTypeOf();
+ expectTypeOf().toEqualTypeOf();
expectTypeOf().toEqualTypeOf();
expectTypeOf().toEqualTypeOf();
// optional string field (nullable + optional)
@@ -259,6 +261,18 @@ describe('SchemaFactory - makeModelSchema', () => {
expect(result.success).toBe(true);
});
+ it('rejects invalid phone number for @phone field', () => {
+ const userSchema = factory.makeModelSchema('User');
+ const result = userSchema.safeParse({ ...validUser, phone: 'not-a-phone' });
+ expect(result.success).toBe(false);
+ });
+
+ it('accepts valid phone number for @phone field', () => {
+ const userSchema = factory.makeModelSchema('User');
+ const result = userSchema.safeParse({ ...validUser, phone: '+15555555555' });
+ expect(result.success).toBe(true);
+ });
+
it('rejects code that does not start with "USR" for @startsWith', () => {
const userSchema = factory.makeModelSchema('User');
const result = userSchema.safeParse({ ...validUser, code: 'ABC001' });
@@ -576,6 +590,7 @@ describe('SchemaFactory - makeTypeSchema', () => {
const validUser = {
id: 'u1',
email: 'a@b.com',
+ phone: '+15555555555',
username: 'alice',
website: null,
code: 'USR01',
@@ -934,7 +949,9 @@ describe('SchemaFactory - makeModelSchema with options', () => {
expectTypeOf().toHaveProperty('id');
expectTypeOf().toEqualTypeOf();
expectTypeOf().toHaveProperty('email');
+ expectTypeOf().toHaveProperty('phone');
expectTypeOf().toEqualTypeOf();
+ expectTypeOf().toEqualTypeOf();
});
it('omit: {} (empty) keeps all scalar fields', () => {
@@ -954,6 +971,7 @@ describe('SchemaFactory - makeModelSchema with options', () => {
expectTypeOf().not.toHaveProperty('username');
expectTypeOf().not.toHaveProperty('avatar');
expectTypeOf().toHaveProperty('email');
+ expectTypeOf().toHaveProperty('phone');
});
});
@@ -985,6 +1003,7 @@ describe('SchemaFactory - makeModelSchema with options', () => {
type Result = z.infer;
expectTypeOf().toEqualTypeOf();
expectTypeOf().toEqualTypeOf();
+ expectTypeOf().toEqualTypeOf();
expectTypeOf().toEqualTypeOf();
});
@@ -1039,6 +1058,7 @@ describe('SchemaFactory - makeModelSchema with options', () => {
type Result = z.infer;
expectTypeOf().not.toHaveProperty('username');
expectTypeOf().toHaveProperty('email');
+ expectTypeOf().toHaveProperty('phone');
expectTypeOf().toHaveProperty('posts');
});
});
@@ -1069,6 +1089,7 @@ describe('SchemaFactory - makeModelSchema with options', () => {
expectTypeOf().toEqualTypeOf();
expectTypeOf().not.toHaveProperty('username');
expectTypeOf().not.toHaveProperty('posts');
+ expectTypeOf().not.toHaveProperty('phone');
});
it('select with a relation field (true) includes the relation', () => {
@@ -1084,6 +1105,7 @@ describe('SchemaFactory - makeModelSchema with options', () => {
expectTypeOf().toHaveProperty('id');
expectTypeOf().toHaveProperty('posts');
expectTypeOf().not.toHaveProperty('email');
+ expectTypeOf().not.toHaveProperty('phone');
});
it('select with nested options on a relation', () => {
@@ -1214,6 +1236,7 @@ describe('SchemaFactory - makeModelSchema with options', () => {
type Result = z.infer;
expectTypeOf().toEqualTypeOf();
expectTypeOf().toEqualTypeOf();
+ expectTypeOf().toEqualTypeOf();
expectTypeOf().toEqualTypeOf();
expectTypeOf().toEqualTypeOf();
expectTypeOf().toEqualTypeOf();
@@ -1342,6 +1365,7 @@ describe('SchemaFactory - makeModelSchema with options', () => {
const _schema = factory.makeModelSchema('User', { optionality: 'all' });
type Result = z.infer;
expectTypeOf().toEqualTypeOf();
+ expectTypeOf().toEqualTypeOf();
expectTypeOf().toEqualTypeOf();
expectTypeOf().toEqualTypeOf();
// already-optional nullable field
@@ -1356,6 +1380,7 @@ describe('SchemaFactory - makeModelSchema with options', () => {
type Result = z.infer;
expectTypeOf().not.toHaveProperty('username');
expectTypeOf().toEqualTypeOf();
+ expectTypeOf().toEqualTypeOf();
});
it('infers selected fields as optional when optionality is all', () => {
diff --git a/packages/zod/test/schema/schema.ts b/packages/zod/test/schema/schema.ts
index e0dff2a49..3b8dd6eb2 100644
--- a/packages/zod/test/schema/schema.ts
+++ b/packages/zod/test/schema/schema.ts
@@ -26,6 +26,11 @@ export class SchemaType implements SchemaDef {
type: "String",
attributes: [{ name: "@email" }, { name: "@meta", args: [{ name: "name", value: ExpressionUtils.literal("description") }, { name: "value", value: ExpressionUtils.literal("The user's email address") }] }] as readonly AttributeApplication[]
},
+ phone: {
+ name: "phone",
+ type: "String",
+ attributes: [{ name: "@phone" }] as readonly AttributeApplication[]
+ },
username: {
name: "username",
type: "String",
diff --git a/packages/zod/test/schema/schema.zmodel b/packages/zod/test/schema/schema.zmodel
index 9e0d5716a..0fc3aec88 100644
--- a/packages/zod/test/schema/schema.zmodel
+++ b/packages/zod/test/schema/schema.zmodel
@@ -23,6 +23,7 @@ type Address {
model User {
id String @id @default(cuid())
email String @email @meta("description", "The user's email address")
+ phone String @phone
username String @length(3, 50)
website String? @url
code String @startsWith("USR")
diff --git a/samples/orm/package.json b/samples/orm/package.json
index c8df85405..0368c8902 100644
--- a/samples/orm/package.json
+++ b/samples/orm/package.json
@@ -1,6 +1,6 @@
{
"name": "sample-orm",
- "version": "3.7.1",
+ "version": "3.7.2",
"description": "",
"main": "index.js",
"private": true,
diff --git a/tests/e2e/orm/validation/custom-validation.test.ts b/tests/e2e/orm/validation/custom-validation.test.ts
index 35df71b60..905c99c92 100644
--- a/tests/e2e/orm/validation/custom-validation.test.ts
+++ b/tests/e2e/orm/validation/custom-validation.test.ts
@@ -12,6 +12,7 @@ describe('Custom validation tests', () => {
str3 String?
str4 String?
str5 String?
+ str6 String?
int1 Int?
list1 Int[]
list2 Int[]
@@ -32,6 +33,8 @@ describe('Custom validation tests', () => {
@@validate(str5 == null || isDateTime(str5), 'invalid str5')
+ @@validate(str6 == null || isPhone(str6), 'invalid str6')
+
@@validate(list1 == null || (has(list1, 1) && hasSome(list1, [2, 3]) && hasEvery(list1, [4, 5])), 'invalid list1')
@@validate(list2 == null || isEmpty(list2), 'invalid list2', ['x', 'y'])
@@ -77,6 +80,9 @@ describe('Custom validation tests', () => {
// violates datetime
await expect(_t({ str5: 'not-an-datetime' })).toBeRejectedByValidation(['invalid str5']);
+ // violates phone
+ await expect(_t({ str6: 'not-a-phone' })).toBeRejectedByValidation(['invalid str6']);
+
// violates has
await expect(_t({ list1: [2, 3, 4, 5] })).toBeRejectedByValidation(['invalid list1']);
@@ -107,6 +113,7 @@ describe('Custom validation tests', () => {
str3: 'ab@c.com',
str4: 'http://a.b.c',
str5: new Date().toISOString(),
+ str6: '+15555555555',
int1: 2,
list1: [1, 2, 4, 5],
list2: [],
diff --git a/tests/e2e/orm/validation/toplevel.test.ts b/tests/e2e/orm/validation/toplevel.test.ts
index fab16d636..65927e192 100644
--- a/tests/e2e/orm/validation/toplevel.test.ts
+++ b/tests/e2e/orm/validation/toplevel.test.ts
@@ -14,6 +14,7 @@ describe('Toplevel field validation tests', () => {
str4 String? @url
str5 String? @trim @lower
str6 String? @upper
+ str7 String? @phone
}
`,
);
@@ -83,6 +84,12 @@ describe('Toplevel field validation tests', () => {
} else {
await expect(_t({ str6: 'aBc' })).resolves.toMatchObject({ count: 1 });
}
+
+ // violates @phone
+ await expect(_t({ str7: 'not-a-phone' })).toBeRejectedByValidation(['Invalid E.164']);
+
+ // satisfies @phone
+ await expect(_t({ str7: '+15555555555' })).toResolveTruthy();
}
});
diff --git a/tests/e2e/package.json b/tests/e2e/package.json
index 228ebee18..49f8aca55 100644
--- a/tests/e2e/package.json
+++ b/tests/e2e/package.json
@@ -1,6 +1,6 @@
{
"name": "e2e",
- "version": "3.7.1",
+ "version": "3.7.2",
"private": true,
"type": "module",
"scripts": {
diff --git a/tests/regression/package.json b/tests/regression/package.json
index 6b1f35c80..6f7b4945f 100644
--- a/tests/regression/package.json
+++ b/tests/regression/package.json
@@ -1,6 +1,6 @@
{
"name": "regression",
- "version": "3.7.1",
+ "version": "3.7.2",
"private": true,
"type": "module",
"scripts": {
diff --git a/tests/runtimes/bun/package.json b/tests/runtimes/bun/package.json
index bd6407caf..a99454a00 100644
--- a/tests/runtimes/bun/package.json
+++ b/tests/runtimes/bun/package.json
@@ -1,6 +1,6 @@
{
"name": "bun-e2e",
- "version": "3.7.1",
+ "version": "3.7.2",
"private": true,
"type": "module",
"scripts": {
diff --git a/tests/runtimes/edge-runtime/package.json b/tests/runtimes/edge-runtime/package.json
index 32c5d6a2a..ba2e31069 100644
--- a/tests/runtimes/edge-runtime/package.json
+++ b/tests/runtimes/edge-runtime/package.json
@@ -1,6 +1,6 @@
{
"name": "edge-runtime-e2e",
- "version": "3.7.1",
+ "version": "3.7.2",
"private": true,
"type": "module",
"scripts": {