Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"@zenstackhq/testtools": "workspace:*",
"@zenstackhq/typescript-config": "workspace:*",
"@zenstackhq/vitest-config": "workspace:*",
"better-sqlite3": "^12.2.0",
"better-sqlite3": "catalog:",
"tmp": "catalog:"
}
}
12 changes: 3 additions & 9 deletions packages/runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,9 @@
"description": "ZenStack Runtime",
"type": "module",
"scripts": {
"build": "tsc --project tsconfig.build.json --noEmit && tsup-node && pnpm test:generate",
"build": "tsc --noEmit && tsup-node",
"watch": "tsup-node --watch",
"lint": "eslint src --ext ts",
"test": "vitest run && pnpm test:typecheck",
"test:sqlite": "TEST_DB_PROVIDER=sqlite vitest run",
"test:postgresql": "TEST_DB_PROVIDER=postgresql vitest run",
"test:generate": "tsx test/scripts/generate.ts",
"test:typecheck": "tsc --project tsconfig.test.json",
"pack": "pnpm pack"
},
"keywords": [],
Expand Down Expand Up @@ -79,9 +74,9 @@
"zod-validation-error": "catalog:"
},
"peerDependencies": {
"better-sqlite3": "^12.2.0",
"better-sqlite3": "catalog:",
"kysely": "catalog:",
"pg": "^8.13.1",
"pg": "catalog:",
"zod": "catalog:"
},
"peerDependenciesMeta": {
Expand All @@ -99,7 +94,6 @@
"@zenstackhq/eslint-config": "workspace:*",
"@zenstackhq/language": "workspace:*",
"@zenstackhq/sdk": "workspace:*",
"@zenstackhq/testtools": "workspace:*",
"@zenstackhq/typescript-config": "workspace:*",
"@zenstackhq/vitest-config": "workspace:*",
"tsx": "^4.19.2",
Expand Down
3 changes: 1 addition & 2 deletions packages/runtime/src/client/crud/operations/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { match } from 'ts-pattern';
import { ulid } from 'ulid';
import * as uuid from 'uuid';
import type { ClientContract } from '../..';
import { PolicyPlugin } from '../../../plugins/policy';
import type { BuiltinType, Expression, FieldDef } from '../../../schema';
import { ExpressionUtils, type GetModels, type ModelDef, type SchemaDef } from '../../../schema';
import { clone } from '../../../utils/clone';
Expand Down Expand Up @@ -108,7 +107,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {

// TODO: this is not clean, needs a better solution
protected get hasPolicyEnabled() {
return this.options.plugins?.some((plugin) => plugin instanceof PolicyPlugin);
return this.options.plugins?.some((plugin) => plugin.constructor.name === 'PolicyPlugin');
}

protected requireModel(model: string) {
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/client/crud/operations/create.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { match } from 'ts-pattern';
import { RejectedByPolicyError, RejectedByPolicyReason } from '../../../plugins/policy/errors';
import type { GetModels, SchemaDef } from '../../../schema';
import type { CreateArgs, CreateManyAndReturnArgs, CreateManyArgs, WhereInput } from '../../crud-types';
import { RejectedByPolicyError, RejectedByPolicyReason } from '../../errors';
import { getIdValues } from '../../query-utils';
import { BaseOperationHandler } from './base';

Expand Down
3 changes: 1 addition & 2 deletions packages/runtime/src/client/crud/operations/delete.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { match } from 'ts-pattern';
import type { SchemaDef } from '../../../schema';
import type { DeleteArgs, DeleteManyArgs } from '../../crud-types';
import { NotFoundError } from '../../errors';
import { NotFoundError, RejectedByPolicyError, RejectedByPolicyReason } from '../../errors';
import { BaseOperationHandler } from './base';
import { RejectedByPolicyError, RejectedByPolicyReason } from '../../../plugins/policy';

export class DeleteOperationHandler<Schema extends SchemaDef> extends BaseOperationHandler<Schema> {
async handle(operation: 'delete' | 'deleteMany', args: unknown | undefined) {
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/client/crud/operations/update.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { match } from 'ts-pattern';
import { RejectedByPolicyError, RejectedByPolicyReason } from '../../../plugins/policy/errors';
import type { GetModels, SchemaDef } from '../../../schema';
import type { UpdateArgs, UpdateManyAndReturnArgs, UpdateManyArgs, UpsertArgs, WhereInput } from '../../crud-types';
import { RejectedByPolicyError, RejectedByPolicyReason } from '../../errors';
import { getIdValues } from '../../query-utils';
import { BaseOperationHandler } from './base';

Expand Down
35 changes: 35 additions & 0 deletions packages/runtime/src/client/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,38 @@ export class NotFoundError extends ZenStackError {
super(`Entity not found for model "${model}"${details ? `: ${details}` : ''}`);
}
}

/**
* Reason code for policy rejection.
* @todo move to policy plugin package
*/
export enum RejectedByPolicyReason {
/**
* Rejected because the operation is not allowed by policy.
*/
NO_ACCESS = 'no-access',

/**
* Rejected because the result cannot be read back after mutation due to policy.
*/
CANNOT_READ_BACK = 'cannot-read-back',

/**
* Other reasons.
*/
OTHER = 'other',
}

/**
* Error thrown when an operation is rejected by access policy.
* @todo move to policy plugin package
*/
export class RejectedByPolicyError extends ZenStackError {
constructor(
public readonly model: string | undefined,
public readonly reason: RejectedByPolicyReason = RejectedByPolicyReason.NO_ACCESS,
message?: string,
) {
super(message ?? `Operation rejected by policy${model ? ': ' + model : ''}`);
}
}
3 changes: 1 addition & 2 deletions packages/runtime/src/client/executor/name-mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ import {
type OperationNode,
} from 'kysely';
import type { FieldDef, ModelDef, SchemaDef } from '../../schema';
import { extractFieldName, extractModelName, stripAlias } from '../kysely-utils';
import { getModel, requireModel } from '../query-utils';
import { extractFieldName, extractModelName, getModel, requireModel, stripAlias } from '../query-utils';

type Scope = {
model?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import type { GetModels, SchemaDef } from '../../schema';
import { type ClientImpl } from '../client-impl';
import { TransactionIsolationLevel, type ClientContract } from '../contract';
import { InternalError, QueryError, ZenStackError } from '../errors';
import { stripAlias } from '../kysely-utils';
import type { AfterEntityMutationCallback, OnKyselyQueryCallback } from '../plugin';
import { stripAlias } from '../query-utils';
import { QueryNameMapper } from './name-mapper';
import type { ZenStackDriver } from './zenstack-driver';

Expand Down
6 changes: 4 additions & 2 deletions packages/runtime/src/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
export { sql } from 'kysely';
export { ZenStackClient } from './client-impl';
export type { ClientConstructor, ClientContract } from './contract';
export { CRUD, type ClientConstructor, type ClientContract } from './contract';
export type * from './crud-types';
export { getCrudDialect } from './crud/dialects';
export * from './errors';
export type { ClientOptions } from './options';
export { definePlugin } from './plugin';
export type { ZenStackPromise } from './promise';
export type { ToKysely } from './query-builder';
export { sql } from 'kysely';
export * as QueryUtils from './query-utils';
33 changes: 0 additions & 33 deletions packages/runtime/src/client/kysely-utils.ts

This file was deleted.

43 changes: 42 additions & 1 deletion packages/runtime/src/client/query-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { invariant } from '@zenstackhq/common-helpers';
import type { Expression, ExpressionBuilder, ExpressionWrapper } from 'kysely';
import {
AliasNode,
ColumnNode,
ReferenceNode,
TableNode,
type Expression,
type ExpressionBuilder,
type ExpressionWrapper,
type OperationNode,
} from 'kysely';
import { match } from 'ts-pattern';
import { ExpressionUtils, type FieldDef, type GetModels, type ModelDef, type SchemaDef } from '../schema';
import { extractFields } from '../utils/object-utils';
Expand Down Expand Up @@ -367,3 +376,35 @@ export function aggregate(eb: ExpressionBuilder<any, any>, expr: Expression<any>
.with('_max', () => eb.fn.max(expr))
.exhaustive();
}

/**
* Strips alias from the node if it exists.
*/
export function stripAlias(node: OperationNode) {
if (AliasNode.is(node)) {
return { alias: node.alias, node: node.node };
} else {
return { alias: undefined, node };
}
}

/**
* Extracts model name from an OperationNode.
*/
export function extractModelName(node: OperationNode) {
const { node: innerNode } = stripAlias(node);
return TableNode.is(innerNode!) ? innerNode!.table.identifier.name : undefined;
}

/**
* Extracts field name from an OperationNode.
*/
export function extractFieldName(node: OperationNode) {
if (ReferenceNode.is(node) && ColumnNode.is(node.column)) {
return node.column.column.name;
} else if (ColumnNode.is(node)) {
return node.column.name;
} else {
return undefined;
}
}
34 changes: 0 additions & 34 deletions packages/runtime/src/plugins/policy/errors.ts

This file was deleted.

Loading
Loading