Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ jobs:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'

- name: Use Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Get pnpm store directory
id: pnpm-cache
shell: bash
Expand Down
2 changes: 1 addition & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp

// List of extensions which should be recommended for users of this workspace.
"recommendations": ["langium.langium-vscode", "kingwl.vscode-vitest-runner"]
"recommendations": ["langium.langium-vscode", "vitest.explorer"]
}
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
{}
{
"vitest.maximumConfigs": 20
}
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"name": "zenstack-v3",
"version": "3.0.0-beta.26",
"version": "3.0.0-beta.27",
"description": "ZenStack",
"packageManager": "[email protected]",
"type": "module",
"scripts": {
"build": "turbo run build",
"watch": "turbo run watch build",
"lint": "turbo run lint",
"test": "vitest run",
"test": "turbo run test",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"pr": "gh pr create --fill-first --base dev",
"merge-main": "gh pr create --title \"merge dev to main\" --body \"\" --base main --head dev",
Expand All @@ -31,7 +32,7 @@
"turbo": "^2.5.4",
"typescript": "catalog:",
"typescript-eslint": "^8.34.1",
"vitest": "^3.2.4",
"vitest": "^4.0.14",
"yaml": "^2.8.0"
},
"pnpm": {
Expand Down
2 changes: 1 addition & 1 deletion packages/auth-adapters/better-auth/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/better-auth",
"version": "3.0.0-beta.26",
"version": "3.0.0-beta.27",
"description": "ZenStack Better Auth Adapter. This adapter is modified from better-auth's Prisma adapter.",
"type": "module",
"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.26",
"version": "3.0.0-beta.27",
"type": "module",
"author": {
"name": "ZenStack Team"
Expand Down
12 changes: 10 additions & 2 deletions packages/cli/src/utils/exec-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,19 @@ export function execPrisma(args: string, options?: Omit<ExecSyncOptions, 'env'>
// ignore and fallback
}

const _options = {
...options,
env: {
...options?.env,
PRISMA_HIDE_UPDATE_MESSAGE: '1',
},
};

if (!prismaPath) {
// fallback to npx/bunx execute
execPackage(`prisma ${args}`, options);
execPackage(`prisma ${args}`, _options);
return;
}

execSync(`node ${prismaPath} ${args}`, options);
execSync(`node ${prismaPath} ${args}`, _options);
}
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.26",
"version": "3.0.0-beta.27",
"description": "TanStack Query Client for consuming ZenStack v3's CRUD service",
"main": "index.js",
"type": "module",
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.26",
"version": "3.0.0-beta.27",
"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.26",
"version": "3.0.0-beta.27",
"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.26",
"version": "3.0.0-beta.27",
"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.26",
"version": "3.0.0-beta.27",
"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.26",
"version": "3.0.0-beta.27",
"description": "Create a new ZenStack project",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/ide/vscode/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "zenstack-v3",
"publisher": "zenstack",
"version": "3.0.13",
"version": "3.0.14",
"displayName": "ZenStack V3 Language Tools",
"description": "VSCode extension for ZenStack (v3) ZModel language",
"private": true,
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.26",
"version": "3.0.0-beta.27",
"license": "MIT",
"author": "ZenStack Team",
"files": [
Expand Down
6 changes: 6 additions & 0 deletions packages/language/src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
Enum,
Expression,
FunctionDecl,
GeneratorDecl,
InvocationExpr,
Model,
TypeDef,
Expand All @@ -31,6 +32,7 @@ export function registerValidationChecks(services: ZModelServices) {
const checks: ValidationChecks<ZModelAstType> = {
Model: validator.checkModel,
DataSource: validator.checkDataSource,
GeneratorDecl: validator.checkGenerator,
DataModel: validator.checkDataModel,
TypeDef: validator.checkTypeDef,
Enum: validator.checkEnum,
Expand All @@ -56,6 +58,10 @@ export class ZModelValidator {
new DataSourceValidator().validate(node, accept);
}

checkGenerator(node: GeneratorDecl, accept: ValidationAcceptor): void {
accept('warning', '"generator" is not used by ZenStack and should be removed.', { node });
}

checkDataModel(node: DataModel, accept: ValidationAcceptor): void {
new DataModelValidator().validate(node, accept);
}
Expand Down
25 changes: 25 additions & 0 deletions packages/language/test/attribute-application.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,29 @@ describe('Attribute application validation tests', () => {
`"before()" is only allowed in "post-update" policy rules`,
);
});

it('requires relation and fk to have consistent optionality', async () => {
await loadSchemaWithError(
`
datasource db {
provider = 'sqlite'
url = 'file:./dev.db'
}

model Foo {
id Int @id @default(autoincrement())
bar Bar @relation(fields: [barId], references: [id])
barId Int?
@@allow('all', true)
}

model Bar {
id Int @id @default(autoincrement())
foos Foo[]
@@allow('all', true)
}
`,
/relation "bar" is not optional/,
);
});
});
71 changes: 71 additions & 0 deletions packages/language/test/this-resolution.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { describe, expect, it } from 'vitest';
import { loadSchema, loadSchemaWithError } from './utils';

describe('This keyword resolution tests', () => {
it('always resolves to the containing model', async () => {
await loadSchemaWithError(
`
datasource db {
provider = 'sqlite'
url = 'file:./dev.db'
}

model A {
id Int @id @default(autoincrement())
av Int
b B[]

@@allow('read', b?[c?[cv == this.cv]])
}

model B {
id Int @id @default(autoincrement())
bv Int
aId Int
a A @relation(fields: [aId], references: [id])
c C[]
}

model C {
id Int @id @default(autoincrement())
cv Int
bId Int
b B @relation(fields: [bId], references: [id])
}
`,
/MemberAccessTarget named 'cv'/,
);

await expect(
loadSchema(`
datasource db {
provider = 'sqlite'
url = 'file:./dev.db'
}

model A {
id Int @id @default(autoincrement())
av Int
b B[]

@@allow('read', b?[c?[cv == this.av]])
}

model B {
id Int @id @default(autoincrement())
bv Int
aId Int
a A @relation(fields: [aId], references: [id])
c C[]
}

model C {
id Int @id @default(autoincrement())
cv Int
bId Int
b B @relation(fields: [bId], references: [id])
}
`),
).resolves.toBeTruthy();
});
});
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.26",
"version": "3.0.0-beta.27",
"description": "ZenStack ORM",
"type": "module",
"scripts": {
Expand Down
14 changes: 13 additions & 1 deletion packages/orm/src/client/crud/dialects/postgresql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
type SelectQueryBuilder,
} from 'kysely';
import { match } from 'ts-pattern';
import z from 'zod';
import type { BuiltinType, FieldDef, GetModels, SchemaDef } from '../../../schema';
import { DELEGATE_JOINED_FIELD_PREFIX } from '../../constants';
import type { FindArgs } from '../../crud-types';
Expand All @@ -26,6 +27,8 @@ import {
import { BaseCrudDialect } from './base-dialect';

export class PostgresCrudDialect<Schema extends SchemaDef> extends BaseCrudDialect<Schema> {
private isoDateSchema = z.iso.datetime({ local: true, offset: true });

constructor(schema: Schema, options: ClientOptions<Schema>) {
super(schema, options);
}
Expand Down Expand Up @@ -106,7 +109,16 @@ export class PostgresCrudDialect<Schema extends SchemaDef> extends BaseCrudDiale

private transformOutputDate(value: unknown) {
if (typeof value === 'string') {
return new Date(value);
// PostgreSQL's jsonb_build_object serializes timestamp as ISO 8601 strings
// without timezone, (e.g., "2023-01-01T12:00:00.123456"). Since Date is always
// stored as UTC `timestamp` type, we add 'Z' to explicitly mark them as UTC for
// correct Date object creation.
if (this.isoDateSchema.safeParse(value).success) {
const hasOffset = value.endsWith('Z') || /[+-]\d{2}:\d{2}$/.test(value);
return new Date(hasOffset ? value : `${value}Z`);
} else {
return value;
}
} else if (value instanceof Date && this.options.fixPostgresTimezone !== false) {
// SPECIAL NOTES:
// node-pg has a terrible quirk that it returns the date value in local timezone
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.26",
"version": "3.0.0-beta.27",
"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.26",
"version": "3.0.0-beta.27",
"description": "ZenStack Runtime Schema",
"type": "module",
"scripts": {
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.26",
"version": "3.0.0-beta.27",
"description": "ZenStack SDK",
"type": "module",
"scripts": {
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.26",
"version": "3.0.0-beta.27",
"description": "ZenStack automatic CRUD API handlers and server adapters",
"type": "module",
"scripts": {
Expand Down
3 changes: 1 addition & 2 deletions packages/server/src/adapter/elysia/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export interface ElysiaOptions<Schema extends SchemaDef> extends CommonAdapterOp
export function createElysiaHandler<Schema extends SchemaDef>(options: ElysiaOptions<Schema>) {
return async (app: Elysia) => {
app.all('/*', async (ctx: ElysiaContext) => {
const { request, body, set } = ctx;
const { query, body, set, request } = ctx;
const client = await options.getClient(ctx);
if (!client) {
set.status = 500;
Expand All @@ -35,7 +35,6 @@ export function createElysiaHandler<Schema extends SchemaDef>(options: ElysiaOpt
}

const url = new URL(request.url);
const query = Object.fromEntries(url.searchParams);
let path = url.pathname;

if (options.basePath && path.startsWith(options.basePath)) {
Expand Down
11 changes: 9 additions & 2 deletions packages/server/src/adapter/nuxt/handler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import type { ClientContract } from '@zenstackhq/orm';
import type { SchemaDef } from '@zenstackhq/orm/schema';
import { H3Event, defineEventHandler, getQuery, getRouterParams, readBody, type EventHandlerRequest } from 'h3';
import { setResponseStatus } from 'nuxt/app';
import {
defineEventHandler,
getQuery,
getRouterParams,
readBody,
setResponseStatus,
type H3Event,
type EventHandlerRequest,
} from 'h3';
import { logInternalError, type CommonAdapterOptions } from '../common';

/**
Expand Down
Loading