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
6 changes: 3 additions & 3 deletions BREAKINGCHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
1. `auth()` cannot be directly compared with a relation anymore
2. `update` and `delete` policy rejection throws `NotFoundError`
3. non-optional to-one relation doesn't automatically filter parent read when evaluating access policies
1. `update` and `delete` policy rejection throws `NotFoundError`
1. `check()` ORM api has been removed
1. non-optional to-one relation doesn't automatically filter parent read when evaluating access policies
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
- [ ] DbNull vs JsonNull
- [ ] Migrate to tsdown
- [x] @default validation
- [ ] Benchmark
- [x] Benchmark
- [x] Plugin
- [x] Post-mutation hooks should be called after transaction is committed
- [x] TypeDef and mixin
Expand Down
1 change: 1 addition & 0 deletions packages/common-helpers/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './is-plain-object';
export * from './lower-case-first';
export * from './param-case';
export * from './safe-json-stringify';
export * from './sleep';
export * from './tiny-invariant';
export * from './upper-case-first';
Expand Down
12 changes: 12 additions & 0 deletions packages/common-helpers/src/safe-json-stringify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* A safe JSON stringify that handles bigint values.
*/
export function safeJSONStringify(value: unknown) {
return JSON.stringify(value, (_, v) => {
if (typeof v === 'bigint') {
return v.toString();
} else {
return v;
}
});
}
1 change: 1 addition & 0 deletions packages/language/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"build": "pnpm langium:generate && tsc --noEmit && tsup-node",
"watch": "tsup-node --watch",
"lint": "eslint src --ext ts",
"test": "vitest run",
"langium:generate": "langium generate",
"langium:generate:production": "langium generate --mode=production",
"pack": "pnpm pack"
Expand Down
2 changes: 1 addition & 1 deletion packages/language/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Supported Prisma db providers
* Supported db providers
*/
export const SUPPORTED_PROVIDERS = [
'sqlite',
Expand Down
4 changes: 2 additions & 2 deletions packages/plugins/policy/src/policy-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export class PolicyHandler<Schema extends SchemaDef> extends OperationNodeTransf
if (constCondition === true) {
needCheckPreCreate = false;
} else if (constCondition === false) {
throw new RejectedByPolicyError(mutationModel);
throw new RejectedByPolicyError(mutationModel, RejectedByPolicyReason.NO_ACCESS);
}
}

Expand Down Expand Up @@ -621,7 +621,7 @@ export class PolicyHandler<Schema extends SchemaDef> extends OperationNodeTransf

const result = await proceed(preCreateCheck);
if (!result.rows[0]?.$condition) {
throw new RejectedByPolicyError(model);
throw new RejectedByPolicyError(model, RejectedByPolicyReason.NO_ACCESS);
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"dependencies": {
"@paralleldrive/cuid2": "^2.2.2",
"@zenstackhq/common-helpers": "workspace:*",
"decimal.js": "^10.4.3",
"decimal.js": "catalog:",
"json-stable-stringify": "^1.3.0",
"nanoid": "^5.0.9",
"toposort": "^2.0.2",
Expand Down
7 changes: 6 additions & 1 deletion packages/runtime/src/client/crud/dialects/postgresql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,12 @@ export class PostgresCrudDialect<Schema extends SchemaDef> extends BaseCrudDiale
}

private transformOutputBytes(value: unknown) {
return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
return Buffer.isBuffer(value)
? Uint8Array.from(value)
: // node-pg encode bytea as hex string prefixed with \x when embedded in JSON
typeof value === 'string' && value.startsWith('\\x')
? Uint8Array.from(Buffer.from(value.slice(2), 'hex'))
: value;
}

override buildRelationSelection(
Expand Down
1 change: 1 addition & 0 deletions packages/runtime/src/client/crud/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ export class InputValidator<Schema extends SchemaDef> {
const { error, data } = schema.safeParse(args);
if (error) {
throw new InputValidationError(
model,
`Invalid ${operation} args for model "${model}": ${formatError(error)}`,
error,
);
Expand Down
11 changes: 9 additions & 2 deletions packages/runtime/src/client/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ export class ZenStackError extends Error {}
* Error thrown when input validation fails.
*/
export class InputValidationError extends ZenStackError {
constructor(message: string, cause?: unknown) {
constructor(
public readonly model: string,
message: string,
cause?: unknown,
) {
super(message, { cause });
}
}
Expand All @@ -30,7 +34,10 @@ export class InternalError extends ZenStackError {}
* Error thrown when an entity is not found.
*/
export class NotFoundError extends ZenStackError {
constructor(model: string, details?: string) {
constructor(
public readonly model: string,
details?: string,
) {
super(`Entity not found for model "${model}"${details ? `: ${details}` : ''}`);
}
}
Expand Down
7 changes: 7 additions & 0 deletions packages/runtime/src/client/helpers/schema-db-pusher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,16 @@ export class SchemaDbPusher<Schema extends SchemaDef> {
(cb) => {
if (fieldDef.relation?.onDelete) {
cb = cb.onDelete(this.mapCascadeAction(fieldDef.relation.onDelete));
} else if (fieldDef.optional) {
cb = cb.onDelete('set null');
} else {
cb = cb.onDelete('restrict');
}

if (fieldDef.relation?.onUpdate) {
cb = cb.onUpdate(this.mapCascadeAction(fieldDef.relation.onUpdate));
} else {
cb = cb.onUpdate('cascade');
}
return cb;
},
Expand Down
10 changes: 0 additions & 10 deletions packages/runtime/src/client/query-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,16 +315,6 @@ export function ensureArray<T>(value: T | T[]): T[] {
}
}

export function safeJSONStringify(value: unknown) {
return JSON.stringify(value, (_, v) => {
if (typeof v === 'bigint') {
return v.toString();
} else {
return v;
}
});
}

export function extractIdFields(entity: any, schema: SchemaDef, model: string) {
const idFields = requireIdFields(schema, model);
return extractFields(entity, idFields);
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"devDependencies": {
"@zenstackhq/eslint-config": "workspace:*",
"@zenstackhq/typescript-config": "workspace:*",
"decimal.js": "^10.4.3",
"decimal.js": "catalog:",
"kysely": "catalog:"
}
}
4 changes: 4 additions & 0 deletions packages/server/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import config from '@zenstackhq/eslint-config/base.js';

/** @type {import("eslint").Linter.Config} */
export default config;
79 changes: 79 additions & 0 deletions packages/server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"name": "@zenstackhq/server",
"version": "3.0.0-beta.12",
"description": "ZenStack automatic CRUD API handlers and server adapters",
"type": "module",
"scripts": {
"build": "tsc --noEmit && tsup-node",
"watch": "tsup-node --watch",
"lint": "eslint src --ext ts",
"test": "vitest run",
"pack": "pnpm pack"
},
"keywords": [
"fastify",
"express",
"nextjs",
"sveltekit",
"nuxtjs",
"elysia",
"tanstack-start"
],
"author": "ZenStack Team",
"license": "MIT",
"files": [
"dist"
],
"exports": {
"./package.json": {
"import": "./package.json",
"require": "./package.json"
},
"./api": {
"import": {
"types": "./dist/api.d.ts",
"default": "./dist/api.js"
},
"require": {
"types": "./dist/api.d.cts",
"default": "./dist/api.cjs"
}
},
"./express": {
"import": {
"types": "./dist/express.d.ts",
"default": "./dist/express.js"
},
"require": {
"types": "./dist/express.d.cts",
"default": "./dist/express.cjs"
}
}
},
"dependencies": {
"@zenstackhq/common-helpers": "workspace:*",
"@zenstackhq/runtime": "workspace:*",
"decimal.js": "catalog:",
"superjson": "^2.2.3",
"ts-pattern": "catalog:"
},
"devDependencies": {
"@types/body-parser": "^1.19.6",
"@types/express": "^5.0.0",
"@types/supertest": "^6.0.3",
"@zenstackhq/eslint-config": "workspace:*",
"@zenstackhq/testtools": "workspace:*",
"@zenstackhq/typescript-config": "workspace:*",
"@zenstackhq/vitest-config": "workspace:*",
"body-parser": "^2.2.0",
"supertest": "^7.1.4"
},
"peerDependencies": {
"express": "^5.0.0"
},
"peerDependenciesMeta": {
"express": {
"optional": true
}
}
}
1 change: 1 addition & 0 deletions packages/server/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { RPCApiHandler } from './rpc';
Loading