Skip to content
This repository was archived by the owner on Mar 1, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zenstack-v3",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"description": "ZenStack",
"packageManager": "pnpm@10.20.0",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"publisher": "zenstack",
"displayName": "ZenStack CLI",
"description": "FullStack database toolkit with built-in access control and automatic API generation.",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"type": "module",
"author": {
"name": "ZenStack Team"
Expand Down
2 changes: 1 addition & 1 deletion packages/clients/tanstack-query/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/tanstack-query",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"description": "TanStack Query Client for consuming ZenStack v3's CRUD service",
"main": "index.js",
"type": "module",
Expand Down
8 changes: 5 additions & 3 deletions packages/clients/tanstack-query/src/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import {
type ExtraMutationOptions,
type ExtraQueryOptions,
} from './utils/common';
import type { TrimDelegateModelOperations } from './utils/types';
import type { TrimDelegateModelOperations, WithOptimistic } from './utils/types';

export type { FetchFn } from './utils/common';

Expand Down Expand Up @@ -93,12 +93,14 @@ function useHooksContext() {

export type ModelQueryOptions<T> = Omit<UseQueryOptions<T, DefaultError>, 'queryKey'> & ExtraQueryOptions;

export type ModelQueryResult<T> = UseQueryResult<T, DefaultError> & { queryKey: QueryKey };
export type ModelQueryResult<T> = UseQueryResult<WithOptimistic<T>, DefaultError> & { queryKey: QueryKey };

export type ModelSuspenseQueryOptions<T> = Omit<UseSuspenseQueryOptions<T, DefaultError>, 'queryKey'> &
ExtraQueryOptions;

export type ModelSuspenseQueryResult<T> = UseSuspenseQueryResult<T, DefaultError> & { queryKey: QueryKey };
export type ModelSuspenseQueryResult<T> = UseSuspenseQueryResult<WithOptimistic<T>, DefaultError> & {
queryKey: QueryKey;
};

export type ModelInfiniteQueryOptions<T> = Omit<
UseInfiniteQueryOptions<T, DefaultError, InfiniteData<T>>,
Expand Down
6 changes: 4 additions & 2 deletions packages/clients/tanstack-query/src/svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import {
type ExtraMutationOptions,
type ExtraQueryOptions,
} from './utils/common';
import type { TrimDelegateModelOperations } from './utils/types';
import type { TrimDelegateModelOperations, WithOptimistic } from './utils/types';

export type { FetchFn } from './utils/common';

Expand Down Expand Up @@ -91,7 +91,9 @@ function getQuerySettings() {

export type ModelQueryOptions<T> = Omit<CreateQueryOptions<T, DefaultError>, 'queryKey'> & ExtraQueryOptions;

export type ModelQueryResult<T> = Readable<UnwrapStore<CreateQueryResult<T, DefaultError>> & { queryKey: QueryKey }>;
export type ModelQueryResult<T> = Readable<
UnwrapStore<CreateQueryResult<WithOptimistic<T>, DefaultError>> & { queryKey: QueryKey }
>;

export type ModelInfiniteQueryOptions<T> = Omit<
CreateInfiniteQueryOptions<T, DefaultError, InfiniteData<T>>,
Expand Down
17 changes: 17 additions & 0 deletions packages/clients/tanstack-query/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,20 @@ export type TrimDelegateModelOperations<
Model extends GetModels<Schema>,
T extends Record<string, unknown>,
> = IsDelegateModel<Schema, Model> extends true ? Omit<T, HooksOperationsIneligibleForDelegateModels> : T;

export type WithOptimistic<T> =
T extends Array<infer U>
? Array<
U & {
/**
* Indicates if the item is in an optimistic update state
*/
$optimistic?: boolean;
}
>
: T & {
/**
* Indicates if the item is in an optimistic update state
*/
$optimistic?: boolean;
};
Comment thread
ymc9 marked this conversation as resolved.
Outdated
4 changes: 2 additions & 2 deletions packages/clients/tanstack-query/src/vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import {
type ExtraMutationOptions,
type ExtraQueryOptions,
} from './utils/common';
import type { TrimDelegateModelOperations } from './utils/types';
import type { TrimDelegateModelOperations, WithOptimistic } from './utils/types';

export type { FetchFn } from './utils/common';
export const VueQueryContextKey = 'zenstack-vue-query-context';
Expand Down Expand Up @@ -86,7 +86,7 @@ export type ModelQueryOptions<T> = MaybeRefOrGetter<
Omit<UnwrapRef<UseQueryOptions<T, DefaultError>>, 'queryKey'> & ExtraQueryOptions
>;

export type ModelQueryResult<T> = UseQueryReturnType<T, DefaultError> & { queryKey: QueryKey };
export type ModelQueryResult<T> = UseQueryReturnType<WithOptimistic<T>, DefaultError> & { queryKey: QueryKey };

export type ModelInfiniteQueryOptions<T> = MaybeRefOrGetter<
Omit<UnwrapRef<UseInfiniteQueryOptions<T, DefaultError, InfiniteData<T>>>, 'queryKey' | 'initialPageParam'>
Expand Down
4 changes: 4 additions & 0 deletions packages/clients/tanstack-query/test/react-typing-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ check(client.user.useFindUnique({ select: { email: true } }).data.name);
check(client.user.useFindUnique({ where: { id: '1' }, include: { posts: true } }).data?.posts[0]?.title);

check(client.user.useFindFirst().data?.email);
check(client.user.useFindFirst().data?.$optimistic);

check(client.user.useFindMany().data?.[0]?.email);
check(client.user.useFindMany().data?.[0]?.$optimistic);

check(client.user.useInfiniteFindMany().data?.pages[0]?.[0]?.email);
check(
Expand All @@ -28,6 +30,8 @@ check(
},
).data?.pages[1]?.[0]?.email,
);
// @ts-expect-error
check(client.user.useInfiniteFindMany().data?.pages[0]?.[0]?.$optimistic);

check(client.user.useSuspenseFindMany().data[0]?.email);
check(client.user.useSuspenseInfiniteFindMany().data.pages[0]?.[0]?.email);
Expand Down
5 changes: 5 additions & 0 deletions packages/clients/tanstack-query/test/svelte-typing-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ check(get(client.user.useFindUnique({ select: { email: true } })).data.name);
check(get(client.user.useFindUnique({ where: { id: '1' }, include: { posts: true } })).data?.posts[0]?.title);

check(get(client.user.useFindFirst()).data?.email);
check(get(client.user.useFindFirst()).data?.$optimistic);

check(get(client.user.useFindMany()).data?.[0]?.email);
check(get(client.user.useFindMany()).data?.[0]?.$optimistic);

check(get(client.user.useInfiniteFindMany()).data?.pages[0]?.[0]?.email);
check(
get(
Expand All @@ -30,6 +33,8 @@ check(
),
).data?.pages[1]?.[0]?.email,
);
// @ts-expect-error
check(get(client.user.useInfiniteFindMany()).data?.pages[0]?.[0]?.$optimistic);

check(get(client.user.useCount()).data?.toFixed(2));
check(get(client.user.useCount({ select: { email: true } })).data?.email.toFixed(2));
Expand Down
4 changes: 4 additions & 0 deletions packages/clients/tanstack-query/test/vue-typing-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ check(client.user.useFindUnique({ select: { email: true } }).data.name);
check(client.user.useFindUnique({ where: { id: '1' }, include: { posts: true } }).data.value?.posts[0]?.title);

check(client.user.useFindFirst().data.value?.email);
check(client.user.useFindFirst().data.value?.$optimistic);

check(client.user.useFindMany().data.value?.[0]?.email);
check(client.user.useFindMany().data.value?.[0]?.$optimistic);

check(client.user.useInfiniteFindMany().data.value?.pages[0]?.[0]?.email);
check(
Expand All @@ -28,6 +30,8 @@ check(
},
).data.value?.pages[1]?.[0]?.email,
);
// @ts-expect-error
check(client.user.useInfiniteFindMany().data.value?.pages[0]?.[0]?.$optimistic);

check(client.user.useCount().data.value?.toFixed(2));
check(client.user.useCount({ select: { email: true } }).data.value?.email.toFixed(2));
Expand Down
2 changes: 1 addition & 1 deletion packages/common-helpers/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/common-helpers",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"description": "ZenStack Common Helpers",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/config/eslint-config/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/eslint-config",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"type": "module",
"private": true,
"license": "MIT"
Expand Down
2 changes: 1 addition & 1 deletion packages/config/typescript-config/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/typescript-config",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"private": true,
"license": "MIT"
}
2 changes: 1 addition & 1 deletion packages/config/vitest-config/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/vitest-config",
"type": "module",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"private": true,
"license": "MIT",
"exports": {
Expand Down
2 changes: 1 addition & 1 deletion packages/create-zenstack/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-zenstack",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"description": "Create a new ZenStack project",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/dialects/sql.js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/kysely-sql-js",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"description": "Kysely dialect for sql.js",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/language/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/language",
"description": "ZenStack ZModel language specification",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"license": "MIT",
"author": "ZenStack Team",
"files": [
Expand Down
2 changes: 1 addition & 1 deletion packages/orm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/orm",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"description": "ZenStack ORM",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/policy/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/plugin-policy",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"description": "ZenStack Policy Plugin",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/schema",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"description": "ZenStack Runtime Schema",
"type": "module",
"scripts": {
Expand Down
5 changes: 4 additions & 1 deletion packages/schema/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export type RelationInfo = {
name?: string;
fields?: string[];
references?: string[];
hasDefault?: boolean;
opposite?: string;
onDelete?: CascadeAction;
onUpdate?: CascadeAction;
Expand Down Expand Up @@ -253,7 +254,9 @@ export type FieldHasDefault<
? true
: GetModelField<Schema, Model, Field>['updatedAt'] extends true
? true
: false;
: GetModelField<Schema, Model, Field>['relation'] extends { hasDefault: true }
? true
: false;

export type FieldIsRelationArray<
Schema extends SchemaDef,
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/sdk",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"description": "ZenStack SDK",
"type": "module",
"scripts": {
Expand Down
15 changes: 15 additions & 0 deletions packages/sdk/src/ts-schema-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -707,12 +707,16 @@ export class TsSchemaGenerator {
}

const relation = getAttribute(field, '@relation');
const fkFields: string[] = [];
if (relation) {
for (const arg of relation.args) {
const param = arg.$resolvedParam.name;
if (param === 'fields' || param === 'references') {
const fieldNames = this.getReferenceNames(arg.value);
if (fieldNames) {
if (param === 'fields') {
fkFields.push(...fieldNames);
}
relationFields.push(
ts.factory.createPropertyAssignment(
param,
Expand All @@ -733,6 +737,17 @@ export class TsSchemaGenerator {
}
}

// check if all fk fields have default values
if (fkFields.length > 0) {
const allHaveDefault = fkFields.every((fieldName) => {
const fieldDef = field.$container.fields.find((f) => f.name === fieldName);
return fieldDef && hasAttribute(fieldDef, '@default');
});
if (allHaveDefault) {
relationFields.push(ts.factory.createPropertyAssignment('hasDefault', ts.factory.createTrue()));
}
}

return ts.factory.createObjectLiteralExpression(relationFields);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/server",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"description": "ZenStack automatic CRUD API handlers and server adapters",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/testtools/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/testtools",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"description": "ZenStack Test Tools",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/zod/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/zod",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"description": "",
"type": "module",
"main": "index.js",
Expand Down
2 changes: 1 addition & 1 deletion samples/next.js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "next.js",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"private": true,
"scripts": {
"generate": "zen generate --lite",
Expand Down
2 changes: 1 addition & 1 deletion samples/orm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sample-blog",
"version": "3.0.0-beta.18",
"version": "3.0.0-beta.19",
"description": "",
"main": "index.js",
"private": true,
Expand Down
21 changes: 21 additions & 0 deletions tests/e2e/orm/client-api/default-auth.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createTestClient } from '@zenstackhq/testtools';
import { describe, expect, it } from 'vitest';
import { schema } from '../schemas/default-auth/schema';

describe('Auth as default value tests', () => {
it('should create without requiring the default auth field', async () => {
const db = await createTestClient(schema);
const user1 = await db.user.create({ data: {} });
await expect(db.$setAuth(user1).profile.create({ data: { bio: 'My bio' } })).resolves.toMatchObject({
userId: user1.id,
});

const address = await db.address.create({ data: { city: 'Seattle ' } });
const user2 = await db.user.create({ data: {} });
await expect(
db.$setAuth(user2).profile.create({ data: { bio: 'My bio', address: { connect: { id: address.id } } } }),
).resolves.toMatchObject({
userId: user2.id,
});
});
});
Loading