Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
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.3",
"@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": ">=4.19.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
Loading