@anjianshi/utils 2.7.0 → 2.8.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.
- package/env-browser/device.d.ts +24 -0
- package/env-browser/device.js +50 -0
- package/env-browser/global.d.ts +10 -0
- package/env-browser/global.js +15 -0
- package/env-browser/load-script.d.ts +5 -0
- package/env-browser/load-script.js +13 -0
- package/env-browser/logging.d.ts +18 -0
- package/env-browser/logging.js +49 -0
- package/env-browser/manage-vconsole.d.ts +16 -0
- package/env-browser/manage-vconsole.js +38 -0
- package/env-node/crypto-random.d.ts +13 -0
- package/env-node/crypto-random.js +28 -0
- package/env-node/fs.d.ts +19 -0
- package/env-node/fs.js +48 -0
- package/env-node/index.d.ts +6 -0
- package/env-node/index.js +6 -0
- package/env-node/logging/handlers.d.ts +58 -0
- package/env-node/logging/handlers.js +154 -0
- package/env-node/logging/index.d.ts +11 -0
- package/env-node/logging/index.js +14 -0
- package/env-node/safe-request.d.ts +26 -0
- package/env-node/safe-request.js +40 -0
- package/env-react/emotion.d.ts +20 -0
- package/env-react/emotion.jsx +34 -0
- package/env-service/controllers.d.ts +30 -0
- package/env-service/controllers.js +41 -0
- package/env-service/env-reader.d.ts +55 -0
- package/env-service/env-reader.js +79 -0
- package/env-service/index.d.ts +6 -0
- package/env-service/index.js +6 -0
- package/env-service/prisma/adapt-logging.d.ts +21 -0
- package/env-service/prisma/adapt-logging.js +30 -0
- package/env-service/prisma/extensions/exist.d.ts +10 -0
- package/env-service/prisma/extensions/exist.js +16 -0
- package/env-service/prisma/extensions/find-and-count.d.ts +7 -0
- package/env-service/prisma/extensions/find-and-count.js +19 -0
- package/env-service/prisma/extensions/soft-delete.d.ts +52 -0
- package/env-service/prisma/extensions/soft-delete.js +123 -0
- package/env-service/prisma/extensions/with-transaction.d.ts +9 -0
- package/env-service/prisma/extensions/with-transaction.js +54 -0
- package/env-service/prisma/index.d.ts +6 -0
- package/env-service/prisma/index.js +6 -0
- package/env-service/prisma/transaction-contexted.d.ts +11 -0
- package/env-service/prisma/transaction-contexted.js +52 -0
- package/env-service/redis-cache.d.ts +39 -0
- package/env-service/redis-cache.js +116 -0
- package/env-service/tasks.d.ts +12 -0
- package/env-service/tasks.js +37 -0
- package/index.d.ts +3 -0
- package/index.js +3 -0
- package/init-dayjs.d.ts +2 -0
- package/init-dayjs.js +7 -0
- package/lang/async.d.ts +19 -0
- package/lang/async.js +34 -0
- package/lang/color.d.ts +37 -0
- package/lang/color.js +111 -0
- package/lang/index.d.ts +7 -0
- package/lang/index.js +7 -0
- package/lang/may-success.d.ts +40 -0
- package/lang/may-success.js +27 -0
- package/lang/object.d.ts +12 -0
- package/lang/object.js +41 -0
- package/lang/random.d.ts +13 -0
- package/lang/random.js +24 -0
- package/lang/string.d.ts +29 -0
- package/lang/string.js +92 -0
- package/lang/time.d.ts +10 -0
- package/lang/time.js +18 -0
- package/{src/lang/types.ts → lang/types.d.ts} +23 -43
- package/lang/types.js +28 -0
- package/logging/adapt.d.ts +10 -0
- package/logging/adapt.js +43 -0
- package/logging/formatters.d.ts +10 -0
- package/logging/formatters.js +22 -0
- package/logging/index.d.ts +45 -0
- package/logging/index.js +90 -0
- package/md5.d.ts +30 -0
- package/md5.js +308 -0
- package/package.json +14 -20
- package/url.d.ts +77 -0
- package/url.js +149 -0
- package/validators/array.d.ts +30 -0
- package/validators/array.js +47 -0
- package/validators/base.d.ts +82 -0
- package/validators/base.js +42 -0
- package/validators/boolean.d.ts +3 -0
- package/validators/boolean.js +22 -0
- package/validators/datetime.d.ts +12 -0
- package/validators/datetime.js +30 -0
- package/validators/factory.d.ts +70 -0
- package/validators/factory.js +121 -0
- package/validators/index.d.ts +9 -0
- package/validators/index.js +9 -0
- package/validators/number.d.ts +19 -0
- package/validators/number.js +26 -0
- package/validators/object.d.ts +28 -0
- package/validators/object.js +49 -0
- package/validators/one-of.d.ts +10 -0
- package/validators/one-of.js +15 -0
- package/validators/string.d.ts +22 -0
- package/validators/string.js +35 -0
- package/README.md +0 -10
- package/eslint.config.cjs +0 -33
- package/publish-prepare.cjs +0 -16
- package/src/env-browser/device.ts +0 -62
- package/src/env-browser/global.ts +0 -21
- package/src/env-browser/load-script.ts +0 -13
- package/src/env-browser/logging.ts +0 -58
- package/src/env-browser/manage-vconsole.ts +0 -54
- package/src/env-node/crypto-random.ts +0 -30
- package/src/env-node/fs.ts +0 -50
- package/src/env-node/index.ts +0 -6
- package/src/env-node/logging/handlers.ts +0 -190
- package/src/env-node/logging/index.ts +0 -16
- package/src/env-node/safe-request.ts +0 -66
- package/src/env-react/emotion.tsx +0 -42
- package/src/env-service/controllers.ts +0 -93
- package/src/env-service/env-reader.ts +0 -141
- package/src/env-service/index.ts +0 -6
- package/src/env-service/prisma/adapt-logging.ts +0 -39
- package/src/env-service/prisma/extensions/exist.ts +0 -21
- package/src/env-service/prisma/extensions/find-and-count.ts +0 -24
- package/src/env-service/prisma/extensions/soft-delete.ts +0 -162
- package/src/env-service/prisma/extensions/with-transaction.ts +0 -65
- package/src/env-service/prisma/index.ts +0 -6
- package/src/env-service/prisma/transaction-contexted.ts +0 -80
- package/src/env-service/redis-cache.ts +0 -142
- package/src/env-service/tasks.ts +0 -45
- package/src/index.ts +0 -3
- package/src/init-dayjs.ts +0 -8
- package/src/lang/async.ts +0 -47
- package/src/lang/color.ts +0 -119
- package/src/lang/index.ts +0 -7
- package/src/lang/may-success.ts +0 -57
- package/src/lang/object.ts +0 -39
- package/src/lang/random.ts +0 -25
- package/src/lang/string.ts +0 -95
- package/src/lang/time.ts +0 -19
- package/src/logging/adapt.ts +0 -49
- package/src/logging/formatters.ts +0 -23
- package/src/logging/index.ts +0 -106
- package/src/md5.ts +0 -318
- package/src/url.ts +0 -185
- package/src/validators/array.ts +0 -97
- package/src/validators/base.ts +0 -145
- package/src/validators/boolean.ts +0 -21
- package/src/validators/datetime.ts +0 -39
- package/src/validators/factory.ts +0 -244
- package/src/validators/index.ts +0 -9
- package/src/validators/number.ts +0 -54
- package/src/validators/object.ts +0 -101
- package/src/validators/one-of.ts +0 -33
- package/src/validators/string.ts +0 -72
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { success, failed } from '../lang/may-success.js';
|
|
2
|
+
function defaultHandleRequestError(error) {
|
|
3
|
+
return failed('Request Error', undefined, error);
|
|
4
|
+
}
|
|
5
|
+
async function defaultHandleResponseError(response) {
|
|
6
|
+
return failed(`Response Error:${response.status}`, undefined, await response.text());
|
|
7
|
+
}
|
|
8
|
+
function defaultHandleParseFailed(error) {
|
|
9
|
+
return failed('Response Parse Failed', undefined, error);
|
|
10
|
+
}
|
|
11
|
+
async function safeRequest(options) {
|
|
12
|
+
const { url, method = 'GET', headers = {}, body = null, responseType = 'json' } = options;
|
|
13
|
+
try {
|
|
14
|
+
const response = await fetch(url, {
|
|
15
|
+
method,
|
|
16
|
+
headers,
|
|
17
|
+
body,
|
|
18
|
+
});
|
|
19
|
+
try {
|
|
20
|
+
if (response.status !== 200 && (response.status !== 204 || responseType !== 'text')) {
|
|
21
|
+
return await (options.handleResponseError ?? defaultHandleResponseError)(response);
|
|
22
|
+
}
|
|
23
|
+
let responseBody;
|
|
24
|
+
if (responseType === 'json')
|
|
25
|
+
responseBody = await response.json();
|
|
26
|
+
else if (responseType === 'binary')
|
|
27
|
+
responseBody = Buffer.from(await response.arrayBuffer());
|
|
28
|
+
else
|
|
29
|
+
responseBody = await response.text();
|
|
30
|
+
return success(responseBody);
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
return (options.handleParseFailed ?? defaultHandleParseFailed)(e, response);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
return (options.handleRequestError ?? defaultHandleRequestError)(e);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export { safeRequest };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通过 React Hook 把 emotion css 转换成 className
|
|
3
|
+
* (不再需要 <ClassName>)
|
|
4
|
+
*
|
|
5
|
+
* 使用前提:
|
|
6
|
+
* 1. 只支持浏览器渲染
|
|
7
|
+
* 2. 用 EmotionCacheProvider 包裹 App 根元素
|
|
8
|
+
*
|
|
9
|
+
* 来自:
|
|
10
|
+
* https://github.com/emotion-js/emotion/issues/1853#issuecomment-623349622
|
|
11
|
+
*/
|
|
12
|
+
import { type EmotionCache } from '@emotion/react';
|
|
13
|
+
import { type CSSInterpolation } from '@emotion/serialize';
|
|
14
|
+
export declare const useEmotionCache: () => EmotionCache | undefined;
|
|
15
|
+
export declare const EmotionCacheProvider: import("react").FC<{
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
} & import("react").RefAttributes<any>> | import("react").ForwardRefExoticComponent<{
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
} & import("react").RefAttributes<any>>;
|
|
20
|
+
export declare function useEmotionClassName(): (...args: CSSInterpolation[]) => string;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通过 React Hook 把 emotion css 转换成 className
|
|
3
|
+
* (不再需要 <ClassName>)
|
|
4
|
+
*
|
|
5
|
+
* 使用前提:
|
|
6
|
+
* 1. 只支持浏览器渲染
|
|
7
|
+
* 2. 用 EmotionCacheProvider 包裹 App 根元素
|
|
8
|
+
*
|
|
9
|
+
* 来自:
|
|
10
|
+
* https://github.com/emotion-js/emotion/issues/1853#issuecomment-623349622
|
|
11
|
+
*/
|
|
12
|
+
import { withEmotionCache } from '@emotion/react';
|
|
13
|
+
import { serializeStyles } from '@emotion/serialize';
|
|
14
|
+
import { insertStyles } from '@emotion/utils';
|
|
15
|
+
import { createContext, useContext, useCallback } from 'react';
|
|
16
|
+
const CacheContext = createContext(undefined);
|
|
17
|
+
export const useEmotionCache = () => useContext(CacheContext);
|
|
18
|
+
export const EmotionCacheProvider = withEmotionCache(({ children }, cache) => {
|
|
19
|
+
return <CacheContext.Provider value={cache}>{children}</CacheContext.Provider>;
|
|
20
|
+
});
|
|
21
|
+
export function useEmotionClassName() {
|
|
22
|
+
const cache = useEmotionCache();
|
|
23
|
+
return useCallback((...args) => {
|
|
24
|
+
if (!cache) {
|
|
25
|
+
if (process.env.NODE_ENV === 'production') {
|
|
26
|
+
return 'emotion-cache-missing';
|
|
27
|
+
}
|
|
28
|
+
throw new Error('No emotion cache found!');
|
|
29
|
+
}
|
|
30
|
+
const serialized = serializeStyles(args, cache.registered);
|
|
31
|
+
insertStyles(cache, serialized, false);
|
|
32
|
+
return cache.key + '-' + serialized.name;
|
|
33
|
+
}, [cache]);
|
|
34
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 把业务功能整理成各个 Controller,
|
|
3
|
+
* 并整合成一个 controllers 对象方便外部引用和 Controller 之间互相引用。
|
|
4
|
+
*
|
|
5
|
+
* 支持自定义 Controller 类,例如把 context 中的内容定义成属性。
|
|
6
|
+
*/
|
|
7
|
+
export type AnyObject = Record<string, unknown>;
|
|
8
|
+
export type AnyController<Context> = Controller<Context, any>;
|
|
9
|
+
type AnyControllerClass<Context> = typeof Controller<Context, any>;
|
|
10
|
+
export type ControllersFrom<Context, T extends AnyObject> = {
|
|
11
|
+
[K in keyof T]: T[K] extends AnyControllerClass<Context> ? InstanceType<T[K]> : never;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Controller 基类
|
|
15
|
+
*/
|
|
16
|
+
export declare class Controller<Context, AllControllers extends Record<string, AnyController<Context>>> {
|
|
17
|
+
/** 调用其他 controllers */
|
|
18
|
+
protected readonly controllers: AllControllers;
|
|
19
|
+
protected readonly context: Context;
|
|
20
|
+
protected readonly name: string;
|
|
21
|
+
constructor(
|
|
22
|
+
/** 调用其他 controllers */
|
|
23
|
+
controllers: AllControllers, context: Context, name?: string);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 传入 Controller 类列表,返回 controller 实例集合。
|
|
27
|
+
* 为优化性能,每个 controller 只有在被使用到时才会实例化。
|
|
28
|
+
*/
|
|
29
|
+
export declare function initializeControllers<Context, T extends AnyObject>(controllerClasses: T, context: Context): ControllersFrom<Context, T>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 把业务功能整理成各个 Controller,
|
|
3
|
+
* 并整合成一个 controllers 对象方便外部引用和 Controller 之间互相引用。
|
|
4
|
+
*
|
|
5
|
+
* 支持自定义 Controller 类,例如把 context 中的内容定义成属性。
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Controller 基类
|
|
9
|
+
*/
|
|
10
|
+
export class Controller {
|
|
11
|
+
controllers;
|
|
12
|
+
context;
|
|
13
|
+
name;
|
|
14
|
+
constructor(
|
|
15
|
+
/** 调用其他 controllers */
|
|
16
|
+
controllers, context, name = this.constructor.name) {
|
|
17
|
+
this.controllers = controllers;
|
|
18
|
+
this.context = context;
|
|
19
|
+
this.name = name;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 传入 Controller 类列表,返回 controller 实例集合。
|
|
24
|
+
* 为优化性能,每个 controller 只有在被使用到时才会实例化。
|
|
25
|
+
*/
|
|
26
|
+
export function initializeControllers(controllerClasses, context) {
|
|
27
|
+
const proxy = new Proxy({}, {
|
|
28
|
+
get(controllers, prop) {
|
|
29
|
+
if (typeof prop !== 'string')
|
|
30
|
+
return;
|
|
31
|
+
if (prop in controllers)
|
|
32
|
+
return controllers[prop];
|
|
33
|
+
if (prop in controllerClasses) {
|
|
34
|
+
const Class = controllerClasses[prop];
|
|
35
|
+
controllers[prop] = new Class(proxy, context, prop);
|
|
36
|
+
return controllers[prop];
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
return proxy;
|
|
41
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as dotenv from 'dotenv';
|
|
2
|
+
type EnvValue = string | number | boolean | unknown[] | Record<string, unknown>;
|
|
3
|
+
type TypeDef = 'string' | 'number' | 'boolean' | unknown[] | Record<string, unknown>;
|
|
4
|
+
type TypeFrom<D extends TypeDef> = D extends 'string' ? string : D extends 'number' ? number : D extends 'boolean' ? boolean : D;
|
|
5
|
+
type ResultFrom<Defs extends Record<string, TypeDef>> = {
|
|
6
|
+
[K in keyof Defs]: TypeFrom<Defs[K]>;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* 读取 .env 文件,并获取格式化后的数据
|
|
10
|
+
* 注意:依赖 dotenv 包
|
|
11
|
+
*/
|
|
12
|
+
export declare class EnvReader {
|
|
13
|
+
protected loadedEnvs: Record<string, string>;
|
|
14
|
+
constructor(options?: dotenv.DotenvConfigOptions);
|
|
15
|
+
protected toNumber(raw: string): number | undefined;
|
|
16
|
+
protected toBoolean(raw: string): boolean | undefined;
|
|
17
|
+
getRaw(key: string): string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* 获取指定 env 的值,并转换成与 defaults 匹配的类型。
|
|
20
|
+
* 若值不存在,返回 defaults。
|
|
21
|
+
*/
|
|
22
|
+
get(key: string, defaults: string): string;
|
|
23
|
+
get(key: string, defaults: number): number;
|
|
24
|
+
get(key: string, defaults: boolean): boolean;
|
|
25
|
+
get<T extends unknown[] | Record<string, unknown>>(key: string, defaults: T): T;
|
|
26
|
+
/**
|
|
27
|
+
* 获取指定 env 的值,并转换成指定类型,无需提供默认值。
|
|
28
|
+
* 值不存在或转换失败时,返回 undefined。
|
|
29
|
+
*/
|
|
30
|
+
getByType(key: string, type?: 'string'): string | undefined;
|
|
31
|
+
getByType(key: string, type: 'number'): number | undefined;
|
|
32
|
+
getByType(key: string, type: 'boolean'): boolean | undefined;
|
|
33
|
+
getByType<T extends unknown[] | Record<string, unknown>>(key: string, type: 'json'): T | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* 同 envReader.get(),只不过是通过对象指定各 env 的默认值来批量获取
|
|
36
|
+
* envReader.batchGet({ port: 8000, debug: false, mobiles: ['123', '456'] }
|
|
37
|
+
*/
|
|
38
|
+
batchGet<Defs extends Record<string, EnvValue>>(definitions: Defs): { [K in keyof Defs]: Defs[K] extends string ? string : Defs[K] extends number ? number : Defs[K] extends boolean ? boolean : Defs[K]; };
|
|
39
|
+
/**
|
|
40
|
+
* 同 envReader.getByType(),只不过是通过对象指定各 env 的类型来批量获取。
|
|
41
|
+
*
|
|
42
|
+
* - required=false(默认)时,不存在或值为 undefined 的 env 不会出现在返回对象里,以保证 { ...defaults, ...envReader.batchGetByType(...) } 的用法能正常保留默认值。
|
|
43
|
+
* - required=true 时要求所有 env 都必须有值,否则会抛出异常
|
|
44
|
+
*
|
|
45
|
+
* envReader.batchGetByType({
|
|
46
|
+
* port: 'number',
|
|
47
|
+
* debug: 'boolean',
|
|
48
|
+
* mobiles: [] as string[], // 用此格式定义内容是数组的 JSON 值
|
|
49
|
+
* obj: {} as { a: number, b: string } // 用此格式定义内容是对象的 JSON 值
|
|
50
|
+
* })
|
|
51
|
+
*/
|
|
52
|
+
batchGetByType<Defs extends Record<string, TypeDef>>(definitions: Defs, required?: false): Partial<ResultFrom<Defs>>;
|
|
53
|
+
batchGetByType<Defs extends Record<string, TypeDef>>(definitions: Defs, required: true): ResultFrom<Defs>;
|
|
54
|
+
}
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as dotenv from 'dotenv';
|
|
2
|
+
import { safeParseJSON } from '../lang/string.js';
|
|
3
|
+
/**
|
|
4
|
+
* 读取 .env 文件,并获取格式化后的数据
|
|
5
|
+
* 注意:依赖 dotenv 包
|
|
6
|
+
*/
|
|
7
|
+
export class EnvReader {
|
|
8
|
+
loadedEnvs = {};
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
dotenv.config({
|
|
11
|
+
...options,
|
|
12
|
+
// 把从 .env 文件读到的内容写入此变量,而不是 process.env,以避免污染 process.env。
|
|
13
|
+
processEnv: this.loadedEnvs,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
toNumber(raw) {
|
|
17
|
+
const num = parseInt(raw, 10);
|
|
18
|
+
return isFinite(num) ? num : undefined;
|
|
19
|
+
}
|
|
20
|
+
toBoolean(raw) {
|
|
21
|
+
const formatted = raw.toLowerCase().trim();
|
|
22
|
+
if (['1', 'true', 'on'].includes(formatted))
|
|
23
|
+
return true;
|
|
24
|
+
if (['0', 'false', 'off'].includes(formatted))
|
|
25
|
+
return false;
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
getRaw(key) {
|
|
29
|
+
return this.loadedEnvs[key] ?? process.env[key];
|
|
30
|
+
}
|
|
31
|
+
get(key, defaults) {
|
|
32
|
+
const raw = this.getRaw(key);
|
|
33
|
+
if (raw === undefined)
|
|
34
|
+
return defaults;
|
|
35
|
+
if (typeof defaults === 'number')
|
|
36
|
+
return this.toNumber(raw) ?? defaults;
|
|
37
|
+
else if (typeof defaults === 'boolean')
|
|
38
|
+
return this.toBoolean(raw) ?? defaults;
|
|
39
|
+
else if (Array.isArray(defaults) || typeof defaults === 'object') {
|
|
40
|
+
return safeParseJSON(raw) ?? defaults;
|
|
41
|
+
}
|
|
42
|
+
return raw;
|
|
43
|
+
}
|
|
44
|
+
getByType(key, type = 'string') {
|
|
45
|
+
const raw = this.getRaw(key);
|
|
46
|
+
if (raw === undefined)
|
|
47
|
+
return raw;
|
|
48
|
+
if (type === 'number')
|
|
49
|
+
return this.toNumber(raw);
|
|
50
|
+
if (type === 'boolean')
|
|
51
|
+
return this.toBoolean(raw);
|
|
52
|
+
if (type === 'json')
|
|
53
|
+
return safeParseJSON(raw);
|
|
54
|
+
return raw;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 同 envReader.get(),只不过是通过对象指定各 env 的默认值来批量获取
|
|
58
|
+
* envReader.batchGet({ port: 8000, debug: false, mobiles: ['123', '456'] }
|
|
59
|
+
*/
|
|
60
|
+
batchGet(definitions) {
|
|
61
|
+
const result = {};
|
|
62
|
+
for (const [key, defaults] of Object.entries(definitions)) {
|
|
63
|
+
result[key] = this.get(key, defaults);
|
|
64
|
+
}
|
|
65
|
+
// 保证返回的值类型是“通用化”的,例如不是 `false` 而是 `boolean`
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
batchGetByType(definitions, required = false) {
|
|
69
|
+
const result = {};
|
|
70
|
+
for (const [key, def] of Object.entries(definitions)) {
|
|
71
|
+
const value = typeof def === 'string' ? this.getByType(key, def) : this.getByType(key, 'json');
|
|
72
|
+
if (value !== undefined)
|
|
73
|
+
result[key] = value;
|
|
74
|
+
else if (required)
|
|
75
|
+
throw new Error(`env ${key} needs a value`);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { getPrismaClient } from '@prisma/client/runtime/library.js';
|
|
2
|
+
import { type Logger } from '../../logging/index.js';
|
|
3
|
+
type PrismalClient = ReturnType<typeof getPrismaClient> extends new () => infer T ? T : never;
|
|
4
|
+
export declare function getPrismaLoggingOptions(debug: boolean): {
|
|
5
|
+
errorFormat: "pretty";
|
|
6
|
+
log: ({
|
|
7
|
+
readonly emit: "event";
|
|
8
|
+
readonly level: "query";
|
|
9
|
+
} | {
|
|
10
|
+
emit: "event";
|
|
11
|
+
level: "info";
|
|
12
|
+
} | {
|
|
13
|
+
emit: "event";
|
|
14
|
+
level: "warn";
|
|
15
|
+
} | {
|
|
16
|
+
emit: "event";
|
|
17
|
+
level: "error";
|
|
18
|
+
})[];
|
|
19
|
+
};
|
|
20
|
+
export declare function adaptPrismaLogging<T extends Pick<PrismalClient, '$on'>>(prisma: T, baseLogger: Logger): void;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 对接 Prisma 的日志记录
|
|
3
|
+
*
|
|
4
|
+
* 注意:Prisma 的 debugging 日志是直接输出到 console 的,没有提供处理渠道,所以无法记录进日志文件。
|
|
5
|
+
* 理论上可以重写 console.log/debug... 等方法来实现捕获,但这牵扯面太广,暂不这样做。
|
|
6
|
+
*/
|
|
7
|
+
import nodeUtil from 'node:util';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
export function getPrismaLoggingOptions(debug) {
|
|
10
|
+
return {
|
|
11
|
+
errorFormat: 'pretty',
|
|
12
|
+
log: [
|
|
13
|
+
...(debug ? [{ emit: 'event', level: 'query' }] : []),
|
|
14
|
+
{ emit: 'event', level: 'info' },
|
|
15
|
+
{ emit: 'event', level: 'warn' },
|
|
16
|
+
{ emit: 'event', level: 'error' },
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
|
|
21
|
+
export function adaptPrismaLogging(prisma, baseLogger) {
|
|
22
|
+
// 记录 Prisma 相关日志
|
|
23
|
+
const queryLogger = baseLogger.getChild('query');
|
|
24
|
+
prisma.$on('query', e => {
|
|
25
|
+
queryLogger.debug(e.query, chalk.green(nodeUtil.format(e.params) + ` +${e.duration}ms`));
|
|
26
|
+
});
|
|
27
|
+
prisma.$on('info', e => baseLogger.info(e.message));
|
|
28
|
+
prisma.$on('warn', e => baseLogger.warn(e.message));
|
|
29
|
+
prisma.$on('error', e => baseLogger.error(e.message));
|
|
30
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Prisma } from '@prisma/client/extension';
|
|
2
|
+
/**
|
|
3
|
+
* 快速检查指定条件的数据是否存在
|
|
4
|
+
* const exists = await prisma.xxx.exists({ id: '1' })
|
|
5
|
+
*/
|
|
6
|
+
export declare const exists: (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/library").InternalArgs<{}, {
|
|
7
|
+
$allModels: {
|
|
8
|
+
exists<T>(this: T, where: Prisma.Args<T, "count">["where"], withDeleted?: boolean): Promise<boolean>;
|
|
9
|
+
};
|
|
10
|
+
}, {}, {}>>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Prisma } from '@prisma/client/extension';
|
|
2
|
+
/**
|
|
3
|
+
* 快速检查指定条件的数据是否存在
|
|
4
|
+
* const exists = await prisma.xxx.exists({ id: '1' })
|
|
5
|
+
*/
|
|
6
|
+
export const exists = Prisma.defineExtension({
|
|
7
|
+
name: 'exists',
|
|
8
|
+
model: {
|
|
9
|
+
$allModels: {
|
|
10
|
+
async exists(where, withDeleted = false) {
|
|
11
|
+
const context = Prisma.getExtensionContext(this);
|
|
12
|
+
return !!(await context.count({ where, withDeleted }));
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Prisma } from '@prisma/client/extension';
|
|
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<{}, {
|
|
4
|
+
$allModels: {
|
|
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
|
+
};
|
|
7
|
+
}, {}, {}>>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Prisma } from '@prisma/client/extension';
|
|
2
|
+
export const findAndCount = Prisma.defineExtension({
|
|
3
|
+
name: 'findAndCount',
|
|
4
|
+
model: {
|
|
5
|
+
$allModels: {
|
|
6
|
+
findAndCount(rawArgs) {
|
|
7
|
+
const context = Prisma.getExtensionContext(this);
|
|
8
|
+
const args = rawArgs;
|
|
9
|
+
return Promise.all([
|
|
10
|
+
context.findMany(args),
|
|
11
|
+
context.count({
|
|
12
|
+
where: args.where,
|
|
13
|
+
withDeleted: args.withDeleted,
|
|
14
|
+
}),
|
|
15
|
+
]);
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 扩展 Prisma 实现软删除
|
|
3
|
+
*
|
|
4
|
+
* 1. 有 deleteTime 字段的 model 支持软删除。
|
|
5
|
+
* 2. 执行 delete() 和 deleteMany() 时默认是进行软删除;可指定 soft 为 false 来彻底删除;执行软删除时可指定要额外更新的 data。
|
|
6
|
+
* 2. 查询时会忽略被软删除的记录;可指定 withDeleted 为 true 来包含它们。
|
|
7
|
+
* 4. 可通过 restore() 和 restoreMany() 恢复软删除的记录。
|
|
8
|
+
*
|
|
9
|
+
* 扩展实现方式参考:
|
|
10
|
+
* https://www.prisma.io/docs/orm/prisma-client/client-extensions/type-utilities#add-a-custom-property-to-a-method
|
|
11
|
+
* https://www.npmjs.com/package/@prisma/extension-accelerate?activeTab=code => @prisma/extension-accelerate/dist/esm/extension.js
|
|
12
|
+
*
|
|
13
|
+
* 此扩展修改了 Prisma 的原生方法。
|
|
14
|
+
* 为保证其他扩展也应用到修改过的这些方法,此扩展应尽可能放在最前面。
|
|
15
|
+
*/
|
|
16
|
+
import { Prisma } from '@prisma/client/extension';
|
|
17
|
+
import type { Operation } from '@prisma/client/runtime/library.js';
|
|
18
|
+
import { type OptionalFields } from '../../../index.js';
|
|
19
|
+
type DeleteArgs<T> = Prisma.Args<T, 'delete'> & {
|
|
20
|
+
soft?: boolean;
|
|
21
|
+
data?: Prisma.Args<T, 'update'>['data'];
|
|
22
|
+
};
|
|
23
|
+
type DeleteReturn<T, A> = Promise<Prisma.Result<T, A, 'delete'>>;
|
|
24
|
+
type DeleteManyArgs<T> = Prisma.Args<T, 'deleteMany'> & {
|
|
25
|
+
soft?: boolean;
|
|
26
|
+
data?: Prisma.Args<T, 'updateMany'>['data'];
|
|
27
|
+
};
|
|
28
|
+
type DeleteManyReturn<T, A> = Promise<Prisma.Result<T, A, 'deleteMany'>>;
|
|
29
|
+
type RestoreArgs<T> = OptionalFields<Prisma.Args<T, 'update'>, 'data'>;
|
|
30
|
+
type RestoreManyArgs<T> = OptionalFields<Prisma.Args<T, 'updateMany'>, 'data'>;
|
|
31
|
+
interface QueryExtraArgs {
|
|
32
|
+
withDeleted?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export type { QueryExtraArgs as SoftDeleteQueryArgs };
|
|
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<{}, {
|
|
37
|
+
$allModels: {
|
|
38
|
+
withSoftDeleteExtension: boolean;
|
|
39
|
+
delete<T, A>(this: T, rawArgs: Prisma.Exact<A, DeleteArgs<T>>): DeleteReturn<T, A>;
|
|
40
|
+
deleteMany<T, A>(this: T, rawArgs: Prisma.Exact<A, DeleteManyArgs<T>>): DeleteManyReturn<T, A>;
|
|
41
|
+
restore<T, A>(this: T, rawArgs: Prisma.Exact<A, RestoreArgs<T>>): Promise<Prisma.Result<T, A, "update">>;
|
|
42
|
+
restoreMany<T, A>(this: T, rawArgs: Prisma.Exact<A, RestoreManyArgs<T>>): Promise<Prisma.Result<T, A, "updateMany">>;
|
|
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">>;
|
|
48
|
+
findUnique<T, A>(this: T, inputArgs: QueryInputArgs<T, A, "findUnique">): Promise<Prisma.Result<T, A, "findUnique">>;
|
|
49
|
+
findUniqueOrThrow<T, A>(this: T, inputArgs: QueryInputArgs<T, A, "findUniqueOrThrow">): Promise<Prisma.Result<T, A, "findUniqueOrThrow">>;
|
|
50
|
+
groupBy<T, A>(this: T, inputArgs: QueryInputArgs<T, A, "groupBy">): Promise<Prisma.Result<T, A, "groupBy">>;
|
|
51
|
+
};
|
|
52
|
+
}, {}, {}>>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 扩展 Prisma 实现软删除
|
|
3
|
+
*
|
|
4
|
+
* 1. 有 deleteTime 字段的 model 支持软删除。
|
|
5
|
+
* 2. 执行 delete() 和 deleteMany() 时默认是进行软删除;可指定 soft 为 false 来彻底删除;执行软删除时可指定要额外更新的 data。
|
|
6
|
+
* 2. 查询时会忽略被软删除的记录;可指定 withDeleted 为 true 来包含它们。
|
|
7
|
+
* 4. 可通过 restore() 和 restoreMany() 恢复软删除的记录。
|
|
8
|
+
*
|
|
9
|
+
* 扩展实现方式参考:
|
|
10
|
+
* https://www.prisma.io/docs/orm/prisma-client/client-extensions/type-utilities#add-a-custom-property-to-a-method
|
|
11
|
+
* https://www.npmjs.com/package/@prisma/extension-accelerate?activeTab=code => @prisma/extension-accelerate/dist/esm/extension.js
|
|
12
|
+
*
|
|
13
|
+
* 此扩展修改了 Prisma 的原生方法。
|
|
14
|
+
* 为保证其他扩展也应用到修改过的这些方法,此扩展应尽可能放在最前面。
|
|
15
|
+
*/
|
|
16
|
+
import { Prisma } from '@prisma/client/extension';
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
|
|
18
|
+
function getModel(that) {
|
|
19
|
+
const context = Prisma.getExtensionContext(that);
|
|
20
|
+
// 1. 此扩展修改了 Prisma 原生的方法,所以要通过 context.$parent[context.$name] 获取上一层的 model,不然会自己调用自己导致死循环。
|
|
21
|
+
// 2. 如果此扩展后面还应用了其他扩展,那么仅仅一层 $parent 取得的 model 还是这个扩展修改过的版本而不是原生的。
|
|
22
|
+
// 此时需要递归向上,直到取得未经此扩展修改过的 model。不然此扩展的业务逻辑会被重复执行,
|
|
23
|
+
// 而因为第一次执行时已经把定制参数消解掉了,第二次执行时会误以为没有传入定制参数,最终导致定制参数失效。
|
|
24
|
+
let model = context;
|
|
25
|
+
do {
|
|
26
|
+
model = model.$parent[context.$name];
|
|
27
|
+
} while ('withSoftDeleteExtension' in model);
|
|
28
|
+
const supportSoftDelete = 'deleteTime' in model.fields;
|
|
29
|
+
return { model, supportSoftDelete };
|
|
30
|
+
}
|
|
31
|
+
function query(that, inputArgs, method) {
|
|
32
|
+
const { model, supportSoftDelete } = getModel(that);
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
|
|
34
|
+
const { withDeleted = false, ...args } = inputArgs;
|
|
35
|
+
return model[method]({
|
|
36
|
+
...args,
|
|
37
|
+
where: !supportSoftDelete || withDeleted ? args.where : { ...args.where, deleteTime: null },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
export const softDelete = Prisma.defineExtension({
|
|
41
|
+
name: 'softDeleted',
|
|
42
|
+
model: {
|
|
43
|
+
$allModels: {
|
|
44
|
+
withSoftDeleteExtension: true,
|
|
45
|
+
// -----------------------------
|
|
46
|
+
// 操作
|
|
47
|
+
// -----------------------------
|
|
48
|
+
delete(rawArgs) {
|
|
49
|
+
const { model, supportSoftDelete } = getModel(this);
|
|
50
|
+
const { soft = true, data, ...args } = rawArgs;
|
|
51
|
+
if (supportSoftDelete && soft) {
|
|
52
|
+
return model.update({
|
|
53
|
+
...args, // .delete() 的参数 .update() 也都支持
|
|
54
|
+
data: { ...(data ?? {}), deleteTime: new Date() },
|
|
55
|
+
}); // .update() 的返回值和 .delete() 一样
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
return model.delete(args);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
deleteMany(rawArgs) {
|
|
62
|
+
const { model, supportSoftDelete } = getModel(this);
|
|
63
|
+
const { soft = true, data, ...args } = rawArgs;
|
|
64
|
+
if (supportSoftDelete && soft) {
|
|
65
|
+
return model.updateMany({
|
|
66
|
+
...args, // .deleteMany() 的参数 .updateMany() 也都支持
|
|
67
|
+
data: { ...(data ?? {}), deleteTime: new Date() },
|
|
68
|
+
}); // .updateMany() 的返回值和 .deleteMany() 一样
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
return model.deleteMany(args);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
restore(rawArgs) {
|
|
75
|
+
const { data, ...args } = rawArgs;
|
|
76
|
+
const { model, supportSoftDelete } = getModel(this);
|
|
77
|
+
if (!supportSoftDelete)
|
|
78
|
+
throw new Error('当前模型不支持软删除,不能执行恢复');
|
|
79
|
+
return model.update({
|
|
80
|
+
...args,
|
|
81
|
+
data: { ...(data ?? {}), deleteTime: null },
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
restoreMany(rawArgs) {
|
|
85
|
+
const { data, ...args } = rawArgs;
|
|
86
|
+
const { model, supportSoftDelete } = getModel(this);
|
|
87
|
+
if (!supportSoftDelete)
|
|
88
|
+
throw new Error('当前模型不支持软删除,不能执行恢复');
|
|
89
|
+
return model.updateMany({
|
|
90
|
+
...args,
|
|
91
|
+
data: { ...(data ?? {}), deleteTime: new Date() },
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
// -----------------------------
|
|
95
|
+
// 查询
|
|
96
|
+
// -----------------------------
|
|
97
|
+
aggregate(inputArgs) {
|
|
98
|
+
return query(this, inputArgs, 'aggregate');
|
|
99
|
+
},
|
|
100
|
+
count(inputArgs) {
|
|
101
|
+
return query(this, inputArgs, 'count');
|
|
102
|
+
},
|
|
103
|
+
findFirst(inputArgs) {
|
|
104
|
+
return query(this, inputArgs, 'findFirst');
|
|
105
|
+
},
|
|
106
|
+
findFirstOrThrow(inputArgs) {
|
|
107
|
+
return query(this, inputArgs, 'findFirstOrThrow');
|
|
108
|
+
},
|
|
109
|
+
findMany(inputArgs) {
|
|
110
|
+
return query(this, inputArgs, 'findMany');
|
|
111
|
+
},
|
|
112
|
+
findUnique(inputArgs) {
|
|
113
|
+
return query(this, inputArgs, 'findUnique');
|
|
114
|
+
},
|
|
115
|
+
findUniqueOrThrow(inputArgs) {
|
|
116
|
+
return query(this, inputArgs, 'findUniqueOrThrow');
|
|
117
|
+
},
|
|
118
|
+
groupBy(inputArgs) {
|
|
119
|
+
return query(this, inputArgs, 'groupBy');
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ITXClientDenyList } from '@prisma/client/runtime/library.js';
|
|
2
|
+
import type { MaySuccess, Failed } from '../../../index.js';
|
|
3
|
+
export declare const withTransaction: (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/library").InternalArgs<{}, {}, {}, {
|
|
4
|
+
$withTransaction: typeof $withTransaction;
|
|
5
|
+
}>>;
|
|
6
|
+
export type GetPrismaClientInTransaction<PrismaClient> = Omit<PrismaClient, ITXClientDenyList>;
|
|
7
|
+
export type WithTransactionMethod = typeof $withTransaction;
|
|
8
|
+
declare function $withTransaction<That extends object, R extends MaySuccess<unknown, unknown>>(this: That, callback: (dbInTransaction: GetPrismaClientInTransaction<That>) => Promise<R>): Promise<Failed<any> | R>;
|
|
9
|
+
export {};
|