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: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
"test-ci": "ZENSTACK_TEST=1 pnpm -r run test --silent --forceExit",
"publish-all": "pnpm --filter \"./packages/**\" -r publish --access public",
"publish-preview": "pnpm --filter \"./packages/**\" -r publish --force --registry https://preview.registry.zenstack.dev/",
"unpublish-preview": "pnpm --recursive --shell-mode exec -- npm unpublish -f --registry https://preview.registry.zenstack.dev/ \"\\$PNPM_PACKAGE_NAME\""
"unpublish-preview": "pnpm --recursive --shell-mode exec -- npm unpublish -f --registry https://preview.registry.zenstack.dev/ \"\\$PNPM_PACKAGE_NAME\"",
"publish-next": "pnpm --filter \"./packages/**\" -r publish --access public --tag next",
"publish-preview-next": "pnpm --filter \"./packages/**\" -r publish --force --registry https://preview.registry.zenstack.dev/ --tag next",
"unpublish-preview-next": "pnpm --recursive --shell-mode exec -- npm unpublish -f --registry https://preview.registry.zenstack.dev/ --tag next \"\\$PNPM_PACKAGE_NAME\""
},
"keywords": [],
"author": "",
Expand Down
56 changes: 31 additions & 25 deletions packages/runtime/src/enhancements/create-enhancement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,60 +32,64 @@ export type TransactionIsolationLevel =
| 'Snapshot'
| 'Serializable';

/**
* Options for {@link createEnhancement}
*/
export type EnhancementOptions = {
/**
* Policy definition
* The kinds of enhancements to apply. By default all enhancements are applied.
*/
policy: PolicyDef;
kinds?: EnhancementKind[];

/**
* Model metadata
* Whether to log Prisma query
*/
modelMeta: ModelMeta;
logPrismaQuery?: boolean;

/**
* Zod schemas for validation
* Hook for transforming errors before they are thrown to the caller.
*/
zodSchemas?: ZodSchemas;
errorTransformer?: ErrorTransformer;

/**
* Whether to log Prisma query
* The `maxWait` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack.
*/
logPrismaQuery?: boolean;
transactionMaxWait?: number;

/**
* The Node module that contains PrismaClient
* The `timeout` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
prismaModule: any;
transactionTimeout?: number;

/**
* The kinds of enhancements to apply. By default all enhancements are applied.
* The `isolationLevel` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack.
*/
kinds?: EnhancementKind[];
transactionIsolationLevel?: TransactionIsolationLevel;
};

/**
* Options for {@link createEnhancement}
*
* @private
*/
export type InternalEnhancementOptions = EnhancementOptions & {
/**
* Hook for transforming errors before they are thrown to the caller.
* Policy definition
*/
errorTransformer?: ErrorTransformer;
policy: PolicyDef;

/**
* The `maxWait` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack.
* Model metadata
*/
transactionMaxWait?: number;
modelMeta: ModelMeta;

/**
* The `timeout` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack.
* Zod schemas for validation
*/
transactionTimeout?: number;
zodSchemas?: ZodSchemas;

/**
* The `isolationLevel` option passed to `prisma.$transaction()` call for transactions initiated by ZenStack.
* The Node module that contains PrismaClient
*/
transactionIsolationLevel?: TransactionIsolationLevel;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
prismaModule: any;
};

/**
Expand All @@ -103,13 +107,15 @@ let hasDefaultAuth: boolean | undefined = undefined;
* Gets a Prisma client enhanced with all enhancement behaviors, including access
* policy, field validation, field omission and password hashing.
*
* @private
*
* @param prisma The Prisma client to enhance.
* @param context Context.
* @param options Options.
*/
export function createEnhancement<DbClient extends object>(
prisma: DbClient,
options: EnhancementOptions,
options: InternalEnhancementOptions,
context?: EnhancementContext
) {
if (!prisma) {
Expand Down
6 changes: 3 additions & 3 deletions packages/runtime/src/enhancements/default-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import deepcopy from 'deepcopy';
import { FieldInfo, NestedWriteVisitor, PrismaWriteActionType, enumerate, getFields } from '../cross';
import { DbClientContract } from '../types';
import { EnhancementContext, EnhancementOptions } from './create-enhancement';
import { EnhancementContext, InternalEnhancementOptions } from './create-enhancement';
import { DefaultPrismaProxyHandler, PrismaProxyActions, makeProxy } from './proxy';

/**
Expand All @@ -14,7 +14,7 @@ import { DefaultPrismaProxyHandler, PrismaProxyActions, makeProxy } from './prox
*/
export function withDefaultAuth<DbClient extends object>(
prisma: DbClient,
options: EnhancementOptions,
options: InternalEnhancementOptions,
context?: EnhancementContext
): DbClient {
return makeProxy(
Expand All @@ -31,7 +31,7 @@ class DefaultAuthHandler extends DefaultPrismaProxyHandler {
constructor(
prisma: DbClientContract,
model: string,
options: EnhancementOptions,
options: InternalEnhancementOptions,
private readonly context?: EnhancementContext
) {
super(prisma, model, options);
Expand Down
6 changes: 3 additions & 3 deletions packages/runtime/src/enhancements/delegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import {
resolveField,
} from '../cross';
import type { CrudContract, DbClientContract } from '../types';
import type { EnhancementOptions } from './create-enhancement';
import type { InternalEnhancementOptions } from './create-enhancement';
import { Logger } from './logger';
import { DefaultPrismaProxyHandler, makeProxy } from './proxy';
import { QueryUtils } from './query-utils';
import { formatObject, prismaClientValidationError } from './utils';

export function withDelegate<DbClient extends object>(prisma: DbClient, options: EnhancementOptions): DbClient {
export function withDelegate<DbClient extends object>(prisma: DbClient, options: InternalEnhancementOptions): DbClient {
return makeProxy(
prisma,
options.modelMeta,
Expand All @@ -34,7 +34,7 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
private readonly logger: Logger;
private readonly queryUtils: QueryUtils;

constructor(prisma: DbClientContract, model: string, options: EnhancementOptions) {
constructor(prisma: DbClientContract, model: string, options: InternalEnhancementOptions) {
super(prisma, model, options);
this.logger = new Logger(prisma);
this.queryUtils = new QueryUtils(prisma, this.options);
Expand Down
8 changes: 4 additions & 4 deletions packages/runtime/src/enhancements/omit.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-explicit-any */

import { enumerate, getModelFields, resolveField, type ModelMeta } from '../cross';
import { enumerate, getModelFields, resolveField } from '../cross';
import { DbClientContract } from '../types';
import { EnhancementOptions } from './create-enhancement';
import { InternalEnhancementOptions } from './create-enhancement';
import { DefaultPrismaProxyHandler, makeProxy } from './proxy';

/**
* Gets an enhanced Prisma client that supports `@omit` attribute.
*
* @private
*/
export function withOmit<DbClient extends object>(prisma: DbClient, options: EnhancementOptions): DbClient {
export function withOmit<DbClient extends object>(prisma: DbClient, options: InternalEnhancementOptions): DbClient {
return makeProxy(
prisma,
options.modelMeta,
Expand All @@ -21,7 +21,7 @@ export function withOmit<DbClient extends object>(prisma: DbClient, options: Enh
}

class OmitHandler extends DefaultPrismaProxyHandler {
constructor(prisma: DbClientContract, model: string, options: EnhancementOptions) {
constructor(prisma: DbClientContract, model: string, options: InternalEnhancementOptions) {
super(prisma, model, options);
}

Expand Down
11 changes: 7 additions & 4 deletions packages/runtime/src/enhancements/password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@

import { hash } from 'bcryptjs';
import { DEFAULT_PASSWORD_SALT_LENGTH } from '../constants';
import { NestedWriteVisitor, type ModelMeta, type PrismaWriteActionType } from '../cross';
import { NestedWriteVisitor, type PrismaWriteActionType } from '../cross';
import { DbClientContract } from '../types';
import { EnhancementOptions } from './create-enhancement';
import { InternalEnhancementOptions } from './create-enhancement';
import { DefaultPrismaProxyHandler, PrismaProxyActions, makeProxy } from './proxy';

/**
* Gets an enhanced Prisma client that supports `@password` attribute.
*
* @private
*/
export function withPassword<DbClient extends object = any>(prisma: DbClient, options: EnhancementOptions): DbClient {
export function withPassword<DbClient extends object = any>(
prisma: DbClient,
options: InternalEnhancementOptions
): DbClient {
return makeProxy(
prisma,
options.modelMeta,
Expand All @@ -23,7 +26,7 @@ export function withPassword<DbClient extends object = any>(prisma: DbClient, op
}

class PasswordHandler extends DefaultPrismaProxyHandler {
constructor(prisma: DbClientContract, model: string, options: EnhancementOptions) {
constructor(prisma: DbClientContract, model: string, options: InternalEnhancementOptions) {
super(prisma, model, options);
}

Expand Down
6 changes: 3 additions & 3 deletions packages/runtime/src/enhancements/policy/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import {
type FieldInfo,
type ModelMeta,
} from '../../cross';
import { type CrudContract, type DbClientContract, PolicyOperationKind } from '../../types';
import type { EnhancementContext, EnhancementOptions } from '../create-enhancement';
import { PolicyOperationKind, type CrudContract, type DbClientContract } from '../../types';
import type { EnhancementContext, InternalEnhancementOptions } from '../create-enhancement';
import { Logger } from '../logger';
import { PrismaProxyHandler } from '../proxy';
import { QueryUtils } from '../query-utils';
Expand Down Expand Up @@ -49,7 +49,7 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
constructor(
private readonly prisma: DbClient,
model: string,
private readonly options: EnhancementOptions,
private readonly options: InternalEnhancementOptions,
private readonly context?: EnhancementContext
) {
this.logger = new Logger(prisma);
Expand Down
4 changes: 2 additions & 2 deletions packages/runtime/src/enhancements/policy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { getIdFields } from '../../cross';
import { DbClientContract } from '../../types';
import { hasAllFields } from '../../validation';
import type { EnhancementContext, EnhancementOptions } from '../create-enhancement';
import type { EnhancementContext, InternalEnhancementOptions } from '../create-enhancement';
import { makeProxy } from '../proxy';
import { PolicyProxyHandler } from './handler';

Expand All @@ -19,7 +19,7 @@ import { PolicyProxyHandler } from './handler';
*/
export function withPolicy<DbClient extends object>(
prisma: DbClient,
options: EnhancementOptions,
options: InternalEnhancementOptions,
context?: EnhancementContext
): DbClient {
const { modelMeta, policy } = options;
Expand Down
4 changes: 2 additions & 2 deletions packages/runtime/src/enhancements/policy/policy-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
import { enumerate, getFields, getModelFields, resolveField, zip, type FieldInfo, type ModelMeta } from '../../cross';
import { AuthUser, CrudContract, DbClientContract, PolicyOperationKind } from '../../types';
import { getVersion } from '../../version';
import type { EnhancementContext, EnhancementOptions } from '../create-enhancement';
import type { EnhancementContext, InternalEnhancementOptions } from '../create-enhancement';
import { Logger } from '../logger';
import { QueryUtils } from '../query-utils';
import type { InputCheckFunc, PolicyDef, ReadFieldCheckFunc, ZodSchemas } from '../types';
Expand All @@ -38,7 +38,7 @@ export class PolicyUtil extends QueryUtils {

constructor(
private readonly db: DbClientContract,
options: EnhancementOptions,
options: InternalEnhancementOptions,
context?: EnhancementContext,
private readonly shouldLogQuery = false
) {
Expand Down
13 changes: 7 additions & 6 deletions packages/runtime/src/enhancements/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { PRISMA_PROXY_ENHANCER } from '../constants';
import type { ModelMeta } from '../cross';
import type { DbClientContract } from '../types';
import { EnhancementOptions } from './create-enhancement';
import { InternalEnhancementOptions } from './create-enhancement';
import { createDeferredPromise } from './policy/promise';

/**
Expand Down Expand Up @@ -67,7 +67,7 @@ export class DefaultPrismaProxyHandler implements PrismaProxyHandler {
constructor(
protected readonly prisma: DbClientContract,
protected readonly model: string,
protected readonly options: EnhancementOptions
protected readonly options: InternalEnhancementOptions
) {}

async findUnique(args: any): Promise<unknown> {
Expand Down Expand Up @@ -241,7 +241,7 @@ export function makeProxy<T extends PrismaProxyHandler>(
return propVal;
}

return createHandlerProxy(makeHandler(target, prop), propVal, errorTransformer);
return createHandlerProxy(makeHandler(target, prop), propVal, prop, errorTransformer);
},
});

Expand All @@ -252,6 +252,7 @@ export function makeProxy<T extends PrismaProxyHandler>(
function createHandlerProxy<T extends PrismaProxyHandler>(
handler: T,
origTarget: any,
model: string,
errorTransformer?: ErrorTransformer
): T {
return new Proxy(handler, {
Expand Down Expand Up @@ -282,7 +283,7 @@ function createHandlerProxy<T extends PrismaProxyHandler>(
if (capture.stack && err instanceof Error) {
// save the original stack and replace it with a clean one
(err as any).internalStack = err.stack;
err.stack = cleanCallStack(capture.stack, propKey.toString(), err.message);
err.stack = cleanCallStack(capture.stack, model, propKey.toString(), err.message);
}

if (errorTransformer) {
Expand All @@ -308,9 +309,9 @@ function createHandlerProxy<T extends PrismaProxyHandler>(
}

// Filter out @zenstackhq/runtime stack (generated by proxy) from stack trace
function cleanCallStack(stack: string, method: string, message: string) {
function cleanCallStack(stack: string, model: string, method: string, message: string) {
// message line
let resultStack = `Error calling enhanced Prisma method \`${method}\`: ${message}`;
let resultStack = `Error calling enhanced Prisma method \`${model}.${method}\`: ${message}`;

const lines = stack.split('\n');
let foundMarker = false;
Expand Down
4 changes: 2 additions & 2 deletions packages/runtime/src/enhancements/query-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import {
} from '../cross';
import { CrudContract, DbClientContract } from '../types';
import { getVersion } from '../version';
import { EnhancementOptions } from './create-enhancement';
import { InternalEnhancementOptions } from './create-enhancement';
import { prismaClientUnknownRequestError, prismaClientValidationError } from './utils';

export class QueryUtils {
constructor(private readonly prisma: DbClientContract, private readonly options: EnhancementOptions) {}
constructor(private readonly prisma: DbClientContract, private readonly options: InternalEnhancementOptions) {}

getIdFields(model: string) {
return getIdFields(this.options.modelMeta, model, true);
Expand Down
Loading