@anjianshi/utils 3.6.1 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,14 @@
1
- import type { getPrismaClient } from '@prisma/client/runtime/library.js';
1
+ import type { getPrismaClient } from '@prisma/client/runtime/client.js';
2
2
  import { type Logger } from '../../logging/index.js';
3
- type PrismalClient = ReturnType<typeof getPrismaClient> extends new () => infer T ? T : never;
3
+ type PrismalClient = InstanceType<ReturnType<typeof getPrismaClient>>;
4
+ /**
5
+ * 生成 Prisma 日志配置项
6
+ *
7
+ * * 使用方法:
8
+ * 1. 初始化 PrismaClient 时传入 getPrismaLoggingOptions() 的返回值,来开启日志:
9
+ * new PrismaClient({ xxx, ...getPrismaLoggingOptions('xxx') })
10
+ * 2. client 初始化完成后,传给 adaptPrismaLogging() 完整与 logger 的对接。
11
+ */
4
12
  export declare function getPrismaLoggingOptions(level: 'debug' | 'info' | 'warn' | 'error'): {
5
13
  errorFormat: "pretty";
6
14
  log: ({
@@ -17,5 +25,8 @@ export declare function getPrismaLoggingOptions(level: 'debug' | 'info' | 'warn'
17
25
  level: "error";
18
26
  })[];
19
27
  };
20
- export declare function adaptPrismaLogging<T extends Pick<PrismalClient, '$on'>>(prisma: T, baseLogger: Logger): void;
28
+ /** Prisma 日志重定向到 logger */
29
+ export declare function adaptPrismaLogging(prisma: Pick<PrismalClient, '$on'>, logger: Logger, enableDebugLog?: boolean): void;
30
+ /** 开启调试日志,并改为通过 logger 记录日志内容 */
31
+ export declare function adaptPrismaDebugLogging(logger: Logger): void;
21
32
  export {};
@@ -1,14 +1,42 @@
1
1
  /**
2
2
  * 对接 Prisma 的日志记录
3
3
  *
4
- * 注意:Prisma 的 debugging 日志是直接输出到 console 的,没有提供处理渠道,所以无法记录进日志文件。
5
- * 理论上可以重写 console.log/debug... 等方法来实现捕获,但这牵扯面太广,暂不这样做。
6
- *
7
4
  * 使用前提:
8
5
  * - 安装 chalk 依赖
6
+ *
7
+ * Prisma 输出的日志分两部分:
8
+ * 1. 日常运行日志,可通过 PrismaClient 的 log 选项控制。
9
+ * 2. 调试日志,不由 log 选项控制,通过 @prisma/debug 包输出(与 debug 包类似)。
10
+ *
11
+ * [@prisma/debug 包的存在形式]
12
+ * prisma 直接把此包打进了其代码里,不通过依赖的方式引入,无法通过引用相同的依赖来对其自定义。
13
+ *
14
+ * [调试日志的开关]
15
+ * - @prisma/debug 包会读取 DEBUG 环境变量即 `process.env.DEBUG` 的值写入到 `globalThis.DEBUG` 中,作为全局的开关配置。
16
+ * 此配置也可调用 enable() / disable() 方法来修改,不过因为我们引用不到这个模块,所以没法调用。
17
+ * - 每个 prisma 模块还会生成一个 debug 子实例,它会根据当前的全局配置来决定自身是否开启,但生成后就不再跟随全局配置的变化而修改。
18
+ * - 最终输出日志时,只要全局配置和子实例中有任意一个是开启的,就会输出日志。
19
+ * 因为这一特性,不建议在涉及 Prisma 的项目里事先指定 DEBUG 环境变量。
20
+ * 最好引入完 Prisma 代码后,再在代码里修改 process.env.DEBUG,以避免调试日志被意外开启。
21
+ *
22
+ * [调试日志的输出方式]
23
+ * 在 @prisma/debug 代码里,它所有输出行为都是统一调用 log() 方法来完成。
24
+ * 而这个方法底层调用的是 console.warn() 和 console.log()。
25
+ * 我们引用不到 prisma 使用的 @prisma/debug 库,无法修改器 log() 方法,但可以修改全局的 console.warn() 来实现自定义。
26
+ *
27
+ * [相关代码]
28
+ * https://github.com/prisma/prisma/ => `packages/debug/src/index.ts`
9
29
  */
10
30
  import nodeUtil from 'node:util';
11
31
  import chalk from 'chalk';
32
+ /**
33
+ * 生成 Prisma 日志配置项
34
+ *
35
+ * * 使用方法:
36
+ * 1. 初始化 PrismaClient 时传入 getPrismaLoggingOptions() 的返回值,来开启日志:
37
+ * new PrismaClient({ xxx, ...getPrismaLoggingOptions('xxx') })
38
+ * 2. client 初始化完成后,传给 adaptPrismaLogging() 完整与 logger 的对接。
39
+ */
12
40
  export function getPrismaLoggingOptions(level) {
13
41
  return {
14
42
  errorFormat: 'pretty',
@@ -22,14 +50,44 @@ export function getPrismaLoggingOptions(level) {
22
50
  ],
23
51
  };
24
52
  }
25
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
26
- export function adaptPrismaLogging(prisma, baseLogger) {
27
- // 记录 Prisma 相关日志
28
- const queryLogger = baseLogger.getChild('query');
53
+ /** Prisma 日志重定向到 logger 中 */
54
+ export function adaptPrismaLogging(prisma, logger, enableDebugLog = false) {
55
+ const queryLogger = logger.getChild('query');
29
56
  prisma.$on('query', e => {
30
57
  queryLogger.debug(e.query, chalk.green(nodeUtil.format(e.params) + ` +${e.duration}ms`));
31
58
  });
32
- prisma.$on('info', e => baseLogger.info(e.message));
33
- prisma.$on('warn', e => baseLogger.warn(e.message));
34
- prisma.$on('error', e => baseLogger.error(e.message));
59
+ prisma.$on('info', e => logger.info(e.message));
60
+ prisma.$on('warn', e => logger.warn(e.message));
61
+ prisma.$on('error', e => logger.error(e.message));
62
+ if (enableDebugLog)
63
+ adaptPrismaDebugLogging(logger);
64
+ }
65
+ /** 开启调试日志,并改为通过 logger 记录日志内容 */
66
+ export function adaptPrismaDebugLogging(logger) {
67
+ ;
68
+ globalThis.DEBUG = '*';
69
+ const loggers = new Map();
70
+ function fixedConsoleWarn(...args) {
71
+ const [initialArg, ...restArgs] = args;
72
+ if (typeof initialArg === 'string') {
73
+ const pattern = /(?<=^(?:\x1b\[\d+m){2})(prisma(?::[\w-]+)+)\x1b\[22m\x1b\[39m ([\s\S]*)/;
74
+ const match = pattern.exec(initialArg);
75
+ if (match) {
76
+ const namespace = match[1];
77
+ const format = match[2] ?? '';
78
+ if (!loggers.has(namespace)) {
79
+ loggers.set(namespace, logger.getChild(namespace.replace(/^prisma:/, '').replaceAll(':', '/')));
80
+ }
81
+ const childLogger = loggers.get(namespace);
82
+ if (format)
83
+ childLogger.debug(nodeUtil.format(format, ...restArgs));
84
+ else
85
+ childLogger.debug(...restArgs);
86
+ return;
87
+ }
88
+ }
89
+ originalConsoleWarn(...args);
90
+ }
91
+ console.warn = fixedConsoleWarn;
35
92
  }
93
+ const originalConsoleWarn = console.warn.bind(console);
@@ -2,8 +2,11 @@ import { Prisma } from '@prisma/client/extension.js';
2
2
  /**
3
3
  * 快速检查指定条件的数据是否存在
4
4
  * const exists = await prisma.xxx.exists({ id: '1' })
5
+ *
6
+ * 注意:
7
+ * 此扩展使用了 soft-delete 扩展定义的 withDeleted 选项,因此必须与 soft-delete 扩展一起使用。
5
8
  */
6
- export declare const exists: (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/library").InternalArgs<{}, {
9
+ export declare const exists: (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/client").InternalArgs<{}, {
7
10
  $allModels: {
8
11
  exists<T>(this: T, where: Prisma.Args<T, "count">["where"], withDeleted?: boolean): Promise<boolean>;
9
12
  };
@@ -2,6 +2,9 @@ import { Prisma } from '@prisma/client/extension.js';
2
2
  /**
3
3
  * 快速检查指定条件的数据是否存在
4
4
  * const exists = await prisma.xxx.exists({ id: '1' })
5
+ *
6
+ * 注意:
7
+ * 此扩展使用了 soft-delete 扩展定义的 withDeleted 选项,因此必须与 soft-delete 扩展一起使用。
5
8
  */
6
9
  export const exists = Prisma.defineExtension({
7
10
  name: 'exists',
@@ -1,6 +1,6 @@
1
1
  import { Prisma } from '@prisma/client/extension.js';
2
2
  import { type SoftDeleteQueryArgs } from './soft-delete.js';
3
- export declare const findAndCount: (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/library").InternalArgs<{}, {
3
+ export declare const findAndCount: (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/client").InternalArgs<{}, {
4
4
  $allModels: {
5
5
  findAndCount<T, A>(this: T, rawArgs: Prisma.Exact<A, Prisma.Args<T, "findMany"> & SoftDeleteQueryArgs>): Promise<[Awaited<Prisma.Result<T, A, "findMany">>, Awaited<Prisma.Result<T, A, "count">>]>;
6
6
  };
@@ -14,7 +14,7 @@
14
14
  * 为保证其他扩展也应用到修改过的这些方法,此扩展应尽可能放在最前面。
15
15
  */
16
16
  import { Prisma } from '@prisma/client/extension.js';
17
- import type { Operation } from '@prisma/client/runtime/library.js';
17
+ import type { Operation } from '@prisma/client/runtime/client.js';
18
18
  import { type OptionalFields } from '../../../index.js';
19
19
  type DeleteArgs<T> = Prisma.Args<T, 'delete'> & {
20
20
  soft?: boolean;
@@ -33,7 +33,7 @@ interface QueryExtraArgs {
33
33
  }
34
34
  export type { QueryExtraArgs as SoftDeleteQueryArgs };
35
35
  type QueryInputArgs<T, A, K extends Operation> = Prisma.Exact<A, Prisma.Args<T, K> & QueryExtraArgs>;
36
- export declare const softDelete: (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/library").InternalArgs<{}, {
36
+ export declare const softDelete: (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/client").InternalArgs<{}, {
37
37
  $allModels: {
38
38
  withSoftDeleteExtension: boolean;
39
39
  delete<T, A>(this: T, rawArgs: Prisma.Exact<A, DeleteArgs<T>>): DeleteReturn<T, A>;
@@ -41,10 +41,10 @@ export declare const softDelete: (client: any) => import("@prisma/client/extensi
41
41
  restore<T, A>(this: T, rawArgs: Prisma.Exact<A, RestoreArgs<T>>): Promise<Prisma.Result<T, A, "update">>;
42
42
  restoreMany<T, A>(this: T, rawArgs: Prisma.Exact<A, RestoreManyArgs<T>>): Promise<Prisma.Result<T, A, "updateMany">>;
43
43
  aggregate<T, A>(this: T, inputArgs: QueryInputArgs<T, A, "aggregate">): Promise<Prisma.Result<T, A, "aggregate">>;
44
- count<T, A>(this: T, inputArgs: QueryInputArgs<T, A, "count">): Promise<Prisma.Result<T, A, "count">>;
45
- findFirst<T, A>(this: T, inputArgs: QueryInputArgs<T, A, "findFirst">): Promise<Prisma.Result<T, A, "findFirst">>;
46
- findFirstOrThrow<T, A>(this: T, inputArgs: QueryInputArgs<T, A, "findFirstOrThrow">): Promise<Prisma.Result<T, A, "findFirstOrThrow">>;
47
- findMany<T, A>(this: T, inputArgs: QueryInputArgs<T, A, "findMany">): Promise<Prisma.Result<T, A, "findMany">>;
44
+ count<T, A>(this: T, inputArgs?: QueryInputArgs<T, A, "count">): Promise<Prisma.Result<T, A, "count">>;
45
+ findFirst<T, A>(this: T, inputArgs?: QueryInputArgs<T, A, "findFirst">): Promise<Prisma.Result<T, A, "findFirst">>;
46
+ findFirstOrThrow<T, A>(this: T, inputArgs?: QueryInputArgs<T, A, "findFirstOrThrow">): Promise<Prisma.Result<T, A, "findFirstOrThrow">>;
47
+ findMany<T, A>(this: T, inputArgs?: QueryInputArgs<T, A, "findMany">): Promise<Prisma.Result<T, A, "findMany">>;
48
48
  findUnique<T, A>(this: T, inputArgs: QueryInputArgs<T, A, "findUnique">): Promise<Prisma.Result<T, A, "findUnique">>;
49
49
  findUniqueOrThrow<T, A>(this: T, inputArgs: QueryInputArgs<T, A, "findUniqueOrThrow">): Promise<Prisma.Result<T, A, "findUniqueOrThrow">>;
50
50
  groupBy<T, A>(this: T, inputArgs: QueryInputArgs<T, A, "groupBy">): Promise<Prisma.Result<T, A, "groupBy">>;
@@ -31,7 +31,7 @@ function getModel(that) {
31
31
  function query(that, inputArgs, method) {
32
32
  const { model, supportSoftDelete } = getModel(that);
33
33
  // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
34
- const { withDeleted = false, ...args } = inputArgs;
34
+ const { withDeleted = false, ...args } = (inputArgs ?? {});
35
35
  return model[method]({
36
36
  ...args,
37
37
  where: !supportSoftDelete || withDeleted ? args.where : { ...args.where, deleteTime: null },
@@ -1,9 +1,13 @@
1
- import { type ITXClientDenyList } from '@prisma/client/runtime/library.js';
1
+ import { type ITXClientDenyList } from '@prisma/client/runtime/client.js';
2
2
  import type { Result, Failed } from '../../../index.js';
3
- export declare const withTransaction: (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/library").InternalArgs<{}, {}, {}, {
3
+ export declare const withTransaction: (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/client").InternalArgs<{}, {}, {}, {
4
4
  $withTransaction: typeof $withTransaction;
5
5
  }>>;
6
6
  export type GetPrismaClientInTransaction<PrismaClient> = Omit<PrismaClient, ITXClientDenyList>;
7
7
  export type WithTransactionMethod = typeof $withTransaction;
8
- declare function $withTransaction<That extends object, R extends Result>(this: That, callback: (dbInTransaction: GetPrismaClientInTransaction<That>) => Promise<R>): Promise<R | Failed<unknown, any>>;
8
+ export type TransactionIsolationLevel = 'ReadUncommitted' | 'ReadCommitted' | 'RepeatableRead' | 'Serializable';
9
+ export interface TransactionOptions {
10
+ isolationLevel?: TransactionIsolationLevel;
11
+ }
12
+ declare function $withTransaction<That extends object, R extends Result>(this: That, callback: (dbInTransaction: GetPrismaClientInTransaction<That>) => Promise<R>, options?: TransactionOptions): Promise<Failed<any, any> | R>;
9
13
  export {};
@@ -1,5 +1,6 @@
1
1
  /**
2
- * 在事务中执行回调。与 $transaction 有几点不同:
2
+ * 在事务中执行操作(回调函数)。
3
+ * 与 prisma.$transaction 有几点不同:
3
4
  * 1. 回调必须返回 Result 值
4
5
  * 2. 回调返回 Failed 值或抛出异常都会触发回滚。
5
6
  * 如果是返回 Failed,会作为此方法的返回值;如果是抛出异常,则异常会继续向上传递,直到被捕获或触发请求失败。
@@ -30,7 +31,7 @@ class FailedInTransaction extends Error {
30
31
  // 这是有意为之的,`Failed<unknown, any>` 并不多余。
31
32
  // 因为有时 callback() 只会返回 success 结果,此时 R=Success<xxx>,
32
33
  // 但是 $withTransaction 整体的返回值仍有可能有 Failed<unknown, any>,所以不能用 R 作为整体返回值。
33
- async function $withTransaction(callback) {
34
+ async function $withTransaction(callback, options) {
34
35
  const executeCallback = async (dbInTransaction) => {
35
36
  const result = await callback(dbInTransaction);
36
37
  if (result.success)
@@ -41,7 +42,7 @@ async function $withTransaction(callback) {
41
42
  if ('$transaction' in this && this.$transaction) {
42
43
  // 如果当前不在事务中,开启新事务并执行回调
43
44
  try {
44
- return await this.$transaction(async (dbInTransaction) => executeCallback(dbInTransaction));
45
+ return await this.$transaction(async (dbInTransaction) => executeCallback(dbInTransaction), options);
45
46
  }
46
47
  catch (e) {
47
48
  if (e instanceof FailedInTransaction)
@@ -1,7 +1,7 @@
1
1
  import type { WithTransactionMethod } from './extensions/with-transaction.js';
2
2
  /**
3
3
  * 返回一个可以在事务内和事务外通用的 PrismaClient 代理对象。
4
- * 当前在事务内时,调用它是调用事务内的 client;当前不在事务内时,调用它是调用全局 client。
4
+ * 在事务内时,对应事务内的 client;不在事务内时,对应全局 client。
5
5
  * 这样当一个事务涉及多个函数调用时,就不用把事务内 client 传递来传递去了。
6
6
  * 注意:应给每个线性流程(例如一个请求)单独生成一个此对象,不能作为全局对象使用,不然可能出现事务冲突。
7
7
  */
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * 返回一个可以在事务内和事务外通用的 PrismaClient 代理对象。
3
- * 当前在事务内时,调用它是调用事务内的 client;当前不在事务内时,调用它是调用全局 client。
3
+ * 在事务内时,对应事务内的 client;不在事务内时,对应全局 client。
4
4
  * 这样当一个事务涉及多个函数调用时,就不用把事务内 client 传递来传递去了。
5
5
  * 注意:应给每个线性流程(例如一个请求)单独生成一个此对象,不能作为全局对象使用,不然可能出现事务冲突。
6
6
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anjianshi/utils",
3
- "version": "3.6.1",
3
+ "version": "3.7.0",
4
4
  "description": "Common JavaScript Utils",
5
5
  "homepage": "https://github.com/anjianshi/js-packages/utils",
6
6
  "bugs": {
@@ -26,7 +26,7 @@
26
26
  "@emotion/react": "^11.14.0",
27
27
  "@emotion/serialize": "^1.3.3",
28
28
  "@emotion/utils": "^1.4.2",
29
- "@prisma/client": "^6.8.2",
29
+ "@prisma/client": "^7.4.0",
30
30
  "@types/lodash": "^4.17.20",
31
31
  "@types/node": "^22.16.0",
32
32
  "@types/react": "^19.1.8",
@@ -38,12 +38,12 @@
38
38
  "redis": "^5.5.6",
39
39
  "typescript": "^5.8.3",
40
40
  "vconsole": "^3.15.1",
41
- "@anjianshi/presets-eslint-base": "6.1.0",
42
- "@anjianshi/presets-eslint-node": "6.1.0",
43
- "@anjianshi/presets-eslint-react": "6.1.1",
44
- "@anjianshi/presets-eslint-typescript": "6.1.0",
45
- "@anjianshi/presets-typescript": "3.2.5",
46
- "@anjianshi/presets-prettier": "3.2.0"
41
+ "@anjianshi/presets-eslint-base": "6.1.2",
42
+ "@anjianshi/presets-eslint-node": "6.1.2",
43
+ "@anjianshi/presets-eslint-react": "6.1.3",
44
+ "@anjianshi/presets-eslint-typescript": "6.1.2",
45
+ "@anjianshi/presets-prettier": "3.2.0",
46
+ "@anjianshi/presets-typescript": "3.2.5"
47
47
  },
48
48
  "prettier": "@anjianshi/presets-prettier/prettierrc"
49
49
  }