From a8602a0ec143731325b20ca644708510ba5a5acc Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 29 Jul 2025 18:31:26 +0800 Subject: [PATCH] perf: improve type checking performance (magically...) --- packages/runtime/src/client/client-impl.ts | 22 ++++++++++++++----- .../executor/zenstack-query-executor.ts | 13 ++++++----- packages/runtime/src/client/plugin.ts | 6 ++++- .../runtime/test/client-api/client-specs.ts | 16 +++++++------- packages/runtime/test/policy/utils.ts | 2 +- packages/runtime/test/utils.ts | 4 ++-- 6 files changed, 40 insertions(+), 23 deletions(-) diff --git a/packages/runtime/src/client/client-impl.ts b/packages/runtime/src/client/client-impl.ts index d2c5bfba..616cdd48 100644 --- a/packages/runtime/src/client/client-impl.ts +++ b/packages/runtime/src/client/client-impl.ts @@ -255,23 +255,33 @@ export class ClientImpl { } $use(plugin: RuntimePlugin) { - const newOptions = { + // tsc perf + const newPlugins: RuntimePlugin[] = [...(this.$options.plugins ?? []), plugin]; + const newOptions: ClientOptions = { ...this.options, - plugins: [...(this.options.plugins ?? []), plugin], + plugins: newPlugins, }; return new ClientImpl(this.schema, newOptions, this); } $unuse(pluginId: string) { - const newOptions = { + // tsc perf + const newPlugins: RuntimePlugin[] = []; + for (const plugin of this.options.plugins ?? []) { + if (plugin.id !== pluginId) { + newPlugins.push(plugin); + } + } + const newOptions: ClientOptions = { ...this.options, - plugins: this.options.plugins?.filter((p) => p.id !== pluginId), + plugins: newPlugins, }; return new ClientImpl(this.schema, newOptions, this); } $unuseAll() { - const newOptions = { + // tsc perf + const newOptions: ClientOptions = { ...this.options, plugins: [] as RuntimePlugin[], }; @@ -388,7 +398,7 @@ function createModelCrudHandler(plugin.onQuery)) { if (_model === lowerCaseFirst(model) || _model === '$allModels') { if (modelHooks && typeof modelHooks === 'object') { // for each operation key or "$allOperations" diff --git a/packages/runtime/src/client/executor/zenstack-query-executor.ts b/packages/runtime/src/client/executor/zenstack-query-executor.ts index 440b7e1f..97cdd9cb 100644 --- a/packages/runtime/src/client/executor/zenstack-query-executor.ts +++ b/packages/runtime/src/client/executor/zenstack-query-executor.ts @@ -25,7 +25,7 @@ import type { GetModels, SchemaDef } from '../../schema'; import { type ClientImpl } from '../client-impl'; import type { ClientContract } from '../contract'; import { InternalError, QueryError } from '../errors'; -import type { MutationInterceptionFilterResult } from '../plugin'; +import type { MutationInterceptionFilterResult, OnKyselyQueryCallback } from '../plugin'; import { QueryNameMapper } from './name-mapper'; import type { ZenStackDriver } from './zenstack-driver'; @@ -113,10 +113,13 @@ export class ZenStackQueryExecutor extends DefaultQuer // return this.executeWithTransaction(() => callback(p)); // }; - const hooks = - this.options.plugins - ?.filter((plugin) => typeof plugin.onKyselyQuery === 'function') - .map((plugin) => plugin.onKyselyQuery!.bind(plugin)) ?? []; + const hooks: OnKyselyQueryCallback[] = []; + // tsc perf + for (const plugin of this.client.$options.plugins ?? []) { + if (plugin.onKyselyQuery) { + hooks.push(plugin.onKyselyQuery.bind(plugin)); + } + } for (const hook of hooks) { const _proceed = proceed; diff --git a/packages/runtime/src/client/plugin.ts b/packages/runtime/src/client/plugin.ts index 58881af4..04320935 100644 --- a/packages/runtime/src/client/plugin.ts +++ b/packages/runtime/src/client/plugin.ts @@ -96,6 +96,10 @@ export type OnKyselyQueryArgs = { export type ProceedKyselyQueryFunction = (query: RootOperationNode) => Promise>; +export type OnKyselyQueryCallback = ( + args: OnKyselyQueryArgs, +) => Promise>; + /** * ZenStack runtime plugin. */ @@ -123,7 +127,7 @@ export interface RuntimePlugin { /** * Intercepts a Kysely query. */ - onKyselyQuery?: (args: OnKyselyQueryArgs) => Promise>; + onKyselyQuery?: OnKyselyQueryCallback; /** * This callback determines whether a mutation should be intercepted, and if so, diff --git a/packages/runtime/test/client-api/client-specs.ts b/packages/runtime/test/client-api/client-specs.ts index fef380b9..59e50d5d 100644 --- a/packages/runtime/test/client-api/client-specs.ts +++ b/packages/runtime/test/client-api/client-specs.ts @@ -15,11 +15,11 @@ export function createClientSpecs(dbName: string, logQueries = false, providers: { provider: 'sqlite' as const, schema: getSchema('sqlite'), - createClient: async () => { - const client = await makeSqliteClient(getSchema('sqlite'), { + createClient: async (): Promise> => { + // tsc perf + return makeSqliteClient(getSchema('sqlite'), { log: logQueries ? logger('sqlite') : undefined, - }); - return client as ClientContract; + }) as unknown as ClientContract; }, }, ] @@ -29,11 +29,11 @@ export function createClientSpecs(dbName: string, logQueries = false, providers: { provider: 'postgresql' as const, schema: getSchema('postgresql'), - createClient: async () => { - const client = await makePostgresClient(getSchema('postgresql'), dbName, { + createClient: async (): Promise> => { + // tsc perf + return makePostgresClient(getSchema('postgresql'), dbName, { log: logQueries ? logger('postgresql') : undefined, - }); - return client as unknown as ClientContract; + }) as unknown as ClientContract; }, }, ] diff --git a/packages/runtime/test/policy/utils.ts b/packages/runtime/test/policy/utils.ts index 2fecc72b..30b99577 100644 --- a/packages/runtime/test/policy/utils.ts +++ b/packages/runtime/test/policy/utils.ts @@ -20,6 +20,6 @@ export async function createPolicyTestClient( { ...options, plugins: [new PolicyPlugin()], - } as CreateTestClientOptions, + } as any, ); } diff --git a/packages/runtime/test/utils.ts b/packages/runtime/test/utils.ts index e7387e85..449e82a5 100644 --- a/packages/runtime/test/utils.ts +++ b/packages/runtime/test/utils.ts @@ -17,7 +17,7 @@ type PostgresSchema = SchemaDef & { provider: { type: 'postgresql' } }; export async function makeSqliteClient( schema: Schema, extraOptions?: Partial>, -) { +): Promise> { const client = new ZenStackClient(schema, { ...extraOptions, dialectConfig: { database: new SQLite(':memory:') }, @@ -37,7 +37,7 @@ export async function makePostgresClient( schema: Schema, dbName: string, extraOptions?: Partial>, -) { +): Promise> { invariant(dbName, 'dbName is required'); const pgClient = new PGClient(TEST_PG_CONFIG); await pgClient.connect();