@longzai-intelligence-typeorm/optimistic-lock 0.0.4
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/README.md +134 -0
- package/dist/index.d.ts +199 -0
- package/dist/index.js +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# @longzai-intelligence-typeorm/optimistic-lock
|
|
2
|
+
|
|
3
|
+
TypeORM 乐观锁统一兜底能力。
|
|
4
|
+
|
|
5
|
+
为资金 / 状态机 / 库存计数等并发敏感实体提供强类型的乐观锁条件更新工具,
|
|
6
|
+
消除各域 repository 手写乐观锁导致的实现漂移与「`save()` 路径绕过乐观锁」系统性反模式。
|
|
7
|
+
|
|
8
|
+
## 背景
|
|
9
|
+
|
|
10
|
+
TypeORM 1.1.0 的 `repository.save(orm)` 路径不会在 `WHERE` 注入 `version` 条件
|
|
11
|
+
(`EntityPersistExecutor` / `SubjectExecutor` 全文零 `lockMode` 命中),
|
|
12
|
+
仅依靠 `@Column` / `@VersionColumn` 在 `SET` 中递增 `version`,无法检测并发冲突,存在丢失更新风险。
|
|
13
|
+
|
|
14
|
+
唯一生效的乐观锁是 `repository.update({ id, version: expectedVersion }, ...)`:
|
|
15
|
+
`WHERE` 带 `version`,`affectedRows = 0` 表示并发冲突。
|
|
16
|
+
|
|
17
|
+
本包将这套语义封装为强类型工具,调用方用显式字面量构造 `where` / `set`,
|
|
18
|
+
TypeScript 从字面量精确推断,**零 spread 动态合并、零类型断言**。
|
|
19
|
+
|
|
20
|
+
## 安装
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bun add @longzai-intelligence-typeorm/optimistic-lock
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`peerDependencies`:`typeorm`、`@longzai-intelligence-error/core`、`@longzai-intelligence-error/utils`、`@longzai-intelligence-shared-kernel/core`。
|
|
27
|
+
|
|
28
|
+
## 使用
|
|
29
|
+
|
|
30
|
+
### 1. 在 repository 的 conditionalSave 中调用工具
|
|
31
|
+
|
|
32
|
+
调用方用显式字面量构造 `where`(必须含 `version`)与 `set`(不含 `version`,由工具注入 `expectedVersion + 1`):
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { conditionalVersionedUpdate } from '@longzai-intelligence-typeorm/optimistic-lock';
|
|
36
|
+
|
|
37
|
+
async conditionalSave(
|
|
38
|
+
entity: WalletEntity,
|
|
39
|
+
expectedVersion: number,
|
|
40
|
+
tx?: EntityManager,
|
|
41
|
+
): Promise<Result<{ affectedRows: number }, DomainError>> {
|
|
42
|
+
const orm = WalletOrmMapper.toOrm(entity);
|
|
43
|
+
|
|
44
|
+
return conditionalVersionedUpdate(this.resolveRepository(tx), {
|
|
45
|
+
orm,
|
|
46
|
+
expectedVersion,
|
|
47
|
+
where: { id: orm.id, version: expectedVersion }, // 字面量,VersionedWhere 强制含 version
|
|
48
|
+
set: { balance: orm.balance, updatedAt: orm.updatedAt }, // 字面量,VersionedSet 排除 version
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2. 在调用方(usecase / port-adapter)处理冲突
|
|
54
|
+
|
|
55
|
+
`conditionalVersionedUpdate` 只返回 `{ affectedRows }`,不判定冲突语义。
|
|
56
|
+
用 helper 统一处理两种场景:
|
|
57
|
+
|
|
58
|
+
#### fail 模式(资金 / 库存 / 订单状态机:冲突即报错)
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import {
|
|
62
|
+
conditionalVersionedUpdate, // 在 repository 内部
|
|
63
|
+
handleConditionalSaveOnFail,
|
|
64
|
+
} from '@longzai-intelligence-typeorm/optimistic-lock';
|
|
65
|
+
|
|
66
|
+
const result = await this.walletRepo.conditionalSave(wallet, expectedVersion, tx);
|
|
67
|
+
const outcome = handleConditionalSaveOnFail(result, {
|
|
68
|
+
aggregate: 'wallet',
|
|
69
|
+
aggregateId: memberId,
|
|
70
|
+
expectedVersion,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (outcome.isFailure) {
|
|
74
|
+
return failure(outcome.error); // ConcurrencyError(HTTP OPERATION_FAILED「操作冲突,请重试」)
|
|
75
|
+
}
|
|
76
|
+
// outcome.value.kind === 'updated'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### idempotent 模式(支付回调:affectedRows=0 视为其他并发回调已处理)
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { handleConditionalSaveOnIdempotent } from '@longzai-intelligence-typeorm/optimistic-lock';
|
|
83
|
+
|
|
84
|
+
const result = await this.transactionRepo.conditionalSave(transaction, expectedState, expectedVersion);
|
|
85
|
+
const outcome = handleConditionalSaveOnIdempotent(result);
|
|
86
|
+
|
|
87
|
+
if (outcome.isFailure) {
|
|
88
|
+
return failure(outcome.error); // 仓储异常
|
|
89
|
+
}
|
|
90
|
+
if (outcome.value.kind === 'conflict') {
|
|
91
|
+
// 其他并发回调已处理,视为幂等成功
|
|
92
|
+
return success({ success: true, ... });
|
|
93
|
+
}
|
|
94
|
+
// outcome.value.kind === 'updated'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 3. 直接构造乐观锁冲突错误
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { createOptimisticLockConflictError } from '@longzai-intelligence-typeorm/optimistic-lock';
|
|
101
|
+
|
|
102
|
+
return failure(
|
|
103
|
+
createOptimisticLockConflictError({
|
|
104
|
+
aggregate: 'order',
|
|
105
|
+
aggregateId: orderId,
|
|
106
|
+
expectedVersion: 5,
|
|
107
|
+
}),
|
|
108
|
+
);
|
|
109
|
+
// → ConcurrencyError,message 含上下文:并发更新冲突:...(aggregate=order, aggregateId=..., expectedVersion=5)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 强类型契约
|
|
113
|
+
|
|
114
|
+
| 类型 | 约束 |
|
|
115
|
+
| ----------------------- | ------------------------------------------------------------------------------------------ |
|
|
116
|
+
| `VersionedWhere<TOrm>` | `FindOptionsWhere<TOrm> & { version: number }`,强制 `where` 字面量含 `version` |
|
|
117
|
+
| `VersionedSet<TOrm>` | `Omit<QueryDeepPartialEntity<TOrm>, 'version'>`,调用方 `set` 不含 `version`(由工具注入) |
|
|
118
|
+
| `ConditionalSaveResult` | `{ affectedRows: number }`,`0` 表示并发冲突 |
|
|
119
|
+
|
|
120
|
+
调用方写错(如 `where` 漏 `version`、`set` 误含 `version`)会在编译期被 TypeScript 拦截,无需运行时检查或类型断言。
|
|
121
|
+
|
|
122
|
+
## API
|
|
123
|
+
|
|
124
|
+
| 导出 | 说明 |
|
|
125
|
+
| ----------------------------------- | -------------------------------------------------------------------------------- |
|
|
126
|
+
| `conditionalVersionedUpdate` | 乐观锁条件更新工具(强类型 `VersionedWhere` / `VersionedSet` 强制 version 契约) |
|
|
127
|
+
| `handleConditionalSaveOnFail` | 冲突处理 helper(fail 模式:affectedRows=0 转 `ConcurrencyError`) |
|
|
128
|
+
| `handleConditionalSaveOnIdempotent` | 冲突处理 helper(idempotent 模式:affectedRows=0 视为幂等成功) |
|
|
129
|
+
| `createOptimisticLockConflictError` | 错误工厂,统一产出 `ConcurrencyError`(code=CONCURRENCY_ERROR) |
|
|
130
|
+
| `ConditionalSaveResult` | 条件更新返回值类型(`{ affectedRows }`) |
|
|
131
|
+
| `ConditionalVersionedUpdateParams` | `conditionalVersionedUpdate` 入参类型 |
|
|
132
|
+
| `VersionedWhere` | 带 version 强制的 WHERE 条件类型 |
|
|
133
|
+
| `VersionedSet` | 排除 version 的 SET 字段类型 |
|
|
134
|
+
| `OptimisticLockConflictContext` | 冲突错误上下文(aggregate / aggregateId / expectedVersion) |
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { ConcurrencyError, DomainError } from "@longzai-intelligence-error/core";
|
|
2
|
+
import { Result } from "@longzai-intelligence-shared-kernel/core";
|
|
3
|
+
import { EntityManager, FindOptionsWhere, ObjectLiteral, QueryDeepPartialEntity, Repository } from "typeorm";
|
|
4
|
+
//#region src/types/conditional-save.types.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* 条件更新受影响行数结果
|
|
7
|
+
*
|
|
8
|
+
* affectedRows = 0 表示 WHERE 条件(含 version [+ 业务字段])未命中,
|
|
9
|
+
* 即其他并发事务已先行更新该行,发生乐观锁冲突。
|
|
10
|
+
*/
|
|
11
|
+
type ConditionalSaveResult = {
|
|
12
|
+
/**
|
|
13
|
+
* 受影响的数据库行数
|
|
14
|
+
*
|
|
15
|
+
* - 1:条件更新成功(命中 version + 业务字段)
|
|
16
|
+
* - 0:乐观锁冲突(并发事务已更新该行,或业务字段状态已变迁)
|
|
17
|
+
*/
|
|
18
|
+
readonly affectedRows: number;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* 带版本号强制的 WHERE 条件类型
|
|
22
|
+
*
|
|
23
|
+
* FindOptionsWhere<TOrm> 与 { version: number } 的交叉类型,
|
|
24
|
+
* 强制调用方构造的 where 字面量必须包含 version 字段。
|
|
25
|
+
*
|
|
26
|
+
* 调用方用字面量构造,TypeScript 检查字面量是否同时满足 FindOptionsWhere 与含 version,
|
|
27
|
+
* 字段级类型完整保留,无 spread、无断言。
|
|
28
|
+
*
|
|
29
|
+
* @typeParam TOrm - ORM 实体类型
|
|
30
|
+
*/
|
|
31
|
+
type VersionedWhere<TOrm extends ObjectLiteral> = FindOptionsWhere<TOrm> & {
|
|
32
|
+
/**
|
|
33
|
+
* 期望版本号(查询时记录)
|
|
34
|
+
*/
|
|
35
|
+
version: number;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* 排除 version 的业务字段 SET 类型
|
|
39
|
+
*
|
|
40
|
+
* 调用方构造的 set 字面量只含业务字段(不含 version),
|
|
41
|
+
* version 由 conditionalVersionedUpdate 工具注入 expectedVersion + 1,
|
|
42
|
+
* 保证 version 递增逻辑统一、不可被调用方覆写。
|
|
43
|
+
*
|
|
44
|
+
* @typeParam TOrm - ORM 实体类型
|
|
45
|
+
*/
|
|
46
|
+
type VersionedSet<TOrm extends ObjectLiteral> = Omit<QueryDeepPartialEntity<TOrm>, 'version'>;
|
|
47
|
+
/**
|
|
48
|
+
* conditionalVersionedUpdate 入参
|
|
49
|
+
*
|
|
50
|
+
* @typeParam TOrm - ORM 实体类型,必须含 id 与 version 字段
|
|
51
|
+
*/
|
|
52
|
+
type ConditionalVersionedUpdateParams<TOrm extends ObjectLiteral & {
|
|
53
|
+
id: string | number;
|
|
54
|
+
}> = {
|
|
55
|
+
/**
|
|
56
|
+
* 已应用领域变更的 ORM 实体
|
|
57
|
+
*
|
|
58
|
+
* 工具从中读取 id 用于错误上下文(实际 WHERE 字段由 where 参数显式提供)。
|
|
59
|
+
*/
|
|
60
|
+
readonly orm: TOrm;
|
|
61
|
+
/**
|
|
62
|
+
* 查询时记录的期望版本号
|
|
63
|
+
*
|
|
64
|
+
* 工具据此构造 SET 中的 version = expectedVersion + 1;
|
|
65
|
+
* 调用方还需在 where 字面量中显式写入 version: expectedVersion(由 VersionedWhere 类型强制)。
|
|
66
|
+
*/
|
|
67
|
+
readonly expectedVersion: number;
|
|
68
|
+
/**
|
|
69
|
+
* WHERE 条件字面量(含 version + 业务字段,如 id / tradeState)
|
|
70
|
+
*
|
|
71
|
+
* 由调用方用显式字面量构造,VersionedWhere 类型强制 version 字段必填。
|
|
72
|
+
* 字段级类型完整保留,无 spread、无断言。
|
|
73
|
+
*/
|
|
74
|
+
readonly where: VersionedWhere<TOrm>;
|
|
75
|
+
/**
|
|
76
|
+
* SET 业务字段字面量(不含 version,version 由工具注入)
|
|
77
|
+
*
|
|
78
|
+
* 由调用方用显式字面量构造,只含业务字段(如 balance / status / updatedAt)。
|
|
79
|
+
*/
|
|
80
|
+
readonly set: VersionedSet<TOrm>;
|
|
81
|
+
/**
|
|
82
|
+
* 可选的事务管理器
|
|
83
|
+
*
|
|
84
|
+
* 由 PortAdapter 在 dataSource.transaction() 闭包内透传;
|
|
85
|
+
* 未提供时使用默认 repository(无事务)。
|
|
86
|
+
*/
|
|
87
|
+
readonly tx?: EntityManager;
|
|
88
|
+
};
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/utils/conditional-versioned-update.utils.d.ts
|
|
91
|
+
/**
|
|
92
|
+
* 执行乐观锁条件更新
|
|
93
|
+
*
|
|
94
|
+
* WHERE 含 version = expectedVersion(调用方字面量提供,VersionedWhere 类型强制),
|
|
95
|
+
* SET 含业务字段(调用方字面量提供)+ version = expectedVersion + 1(工具注入)。
|
|
96
|
+
* 受影响行数为 0 表示并发冲突。
|
|
97
|
+
*
|
|
98
|
+
* @typeParam TOrm - ORM 实体类型,必须含 id 与 version 字段
|
|
99
|
+
* @param repository - TypeORM Repository 实例(由调用方从 resolveRepository(tx) 获取)
|
|
100
|
+
* @param params - 条件更新入参(orm / expectedVersion / where / set / tx?)
|
|
101
|
+
* @returns 成功时返回包含受影响行数的结果;失败时返回 RepositoryError
|
|
102
|
+
*/
|
|
103
|
+
declare function conditionalVersionedUpdate<TOrm extends ObjectLiteral & {
|
|
104
|
+
id: string | number;
|
|
105
|
+
}>(repository: Repository<TOrm>, params: ConditionalVersionedUpdateParams<TOrm>): Promise<Result<ConditionalSaveResult, DomainError>>;
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/errors/optimistic-lock.errors.d.ts
|
|
108
|
+
/**
|
|
109
|
+
* 乐观锁冲突错误上下文
|
|
110
|
+
*
|
|
111
|
+
* 用于在错误消息中附加定位信息(聚合类型、聚合 ID、期望版本号),便于日志检索与排障。
|
|
112
|
+
* 字段全部可选,调用方按需提供。
|
|
113
|
+
*/
|
|
114
|
+
type OptimisticLockConflictContext = {
|
|
115
|
+
/**
|
|
116
|
+
* 聚合根类型(如 wallet / order / wechat-transaction)
|
|
117
|
+
*/
|
|
118
|
+
readonly aggregate?: string;
|
|
119
|
+
/**
|
|
120
|
+
* 聚合根 ID(如 memberId / orderId / outTradeNo)
|
|
121
|
+
*/
|
|
122
|
+
readonly aggregateId?: string;
|
|
123
|
+
/**
|
|
124
|
+
* 查询时记录的期望版本号
|
|
125
|
+
*/
|
|
126
|
+
readonly expectedVersion?: number;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* 构造乐观锁冲突错误
|
|
130
|
+
*
|
|
131
|
+
* 统一产出 ConcurrencyError,将可选的上下文信息编进错误消息便于排障。
|
|
132
|
+
* 各域 usecase 不应自行 new ConcurrencyError(...),统一走本工厂保证错误消息格式一致。
|
|
133
|
+
*
|
|
134
|
+
* @param context - 可选的错误上下文(聚合类型 / 聚合 ID / 期望版本号)
|
|
135
|
+
* @returns ConcurrencyError 实例
|
|
136
|
+
*/
|
|
137
|
+
declare function createOptimisticLockConflictError(context?: OptimisticLockConflictContext): ConcurrencyError;
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/helpers/conflict-handler.helpers.d.ts
|
|
140
|
+
/**
|
|
141
|
+
* 冲突处理策略
|
|
142
|
+
*
|
|
143
|
+
* - fail:affectedRows = 0 视为并发冲突错误(资金/库存/订单状态机等场景)
|
|
144
|
+
* - idempotent:affectedRows = 0 视为幂等成功(支付回调等重复通知场景)
|
|
145
|
+
*/
|
|
146
|
+
type ConflictStrategy = 'fail' | 'idempotent';
|
|
147
|
+
/**
|
|
148
|
+
* fail 模式的处理结果
|
|
149
|
+
*
|
|
150
|
+
* conditionalSave 成功且 affectedRows > 0 时为 updated;
|
|
151
|
+
* 失败(含 affectedRows = 0 冲突)时直接返回 failure(ConcurrencyError)。
|
|
152
|
+
*/
|
|
153
|
+
type FailOutcome = Result<{
|
|
154
|
+
kind: 'updated';
|
|
155
|
+
affectedRows: number;
|
|
156
|
+
}, DomainError>;
|
|
157
|
+
/**
|
|
158
|
+
* idempotent 模式的处理结果
|
|
159
|
+
*
|
|
160
|
+
* - updated:conditionalSave 成功且 affectedRows > 0
|
|
161
|
+
* - conflict:affectedRows = 0,调用方据此走幂等成功分支(非错误)
|
|
162
|
+
*
|
|
163
|
+
* 失败时返回 failure(仓储异常)。
|
|
164
|
+
*/
|
|
165
|
+
type IdempotentOutcome = Result<{
|
|
166
|
+
kind: 'updated';
|
|
167
|
+
affectedRows: number;
|
|
168
|
+
} | {
|
|
169
|
+
kind: 'conflict';
|
|
170
|
+
}, DomainError>;
|
|
171
|
+
/**
|
|
172
|
+
* 处理条件更新结果(fail 模式)
|
|
173
|
+
*
|
|
174
|
+
* 适用于资金/库存/订单状态机等「并发冲突必须报错」的场景。
|
|
175
|
+
*
|
|
176
|
+
* - conditionalSave 失败(仓储异常)→ 原样返回 failure
|
|
177
|
+
* - affectedRows = 0(并发冲突)→ failure(ConcurrencyError)
|
|
178
|
+
* - affectedRows > 0(更新成功)→ success({ kind: 'updated', affectedRows })
|
|
179
|
+
*
|
|
180
|
+
* @param result - OptimisticRepository.conditionalSave 的返回值
|
|
181
|
+
* @param context - 可选的冲突错误上下文(聚合类型 / 聚合 ID / 期望版本号)
|
|
182
|
+
* @returns 处理后的结果
|
|
183
|
+
*/
|
|
184
|
+
declare function handleConditionalSaveOnFail(result: Result<ConditionalSaveResult, DomainError>, context?: OptimisticLockConflictContext): FailOutcome;
|
|
185
|
+
/**
|
|
186
|
+
* 处理条件更新结果(idempotent 模式)
|
|
187
|
+
*
|
|
188
|
+
* 适用于支付回调等「affectedRows = 0 表示其他并发回调已处理,应视为幂等成功」的场景。
|
|
189
|
+
*
|
|
190
|
+
* - conditionalSave 失败(仓储异常)→ 原样返回 failure
|
|
191
|
+
* - affectedRows = 0(并发回调已处理)→ success({ kind: 'conflict' })
|
|
192
|
+
* - affectedRows > 0(本次更新成功)→ success({ kind: 'updated', affectedRows })
|
|
193
|
+
*
|
|
194
|
+
* @param result - OptimisticRepository.conditionalSave 的返回值
|
|
195
|
+
* @returns 处理后的结果
|
|
196
|
+
*/
|
|
197
|
+
declare function handleConditionalSaveOnIdempotent(result: Result<ConditionalSaveResult, DomainError>): IdempotentOutcome;
|
|
198
|
+
//#endregion
|
|
199
|
+
export { type ConditionalSaveResult, type ConditionalVersionedUpdateParams, type ConflictStrategy, type FailOutcome, type IdempotentOutcome, type OptimisticLockConflictContext, type VersionedSet, type VersionedWhere, conditionalVersionedUpdate, createOptimisticLockConflictError, handleConditionalSaveOnFail, handleConditionalSaveOnIdempotent };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ConcurrencyError as e,RepositoryError as t}from"@longzai-intelligence-error/core";import{ensureError as n}from"@longzai-intelligence-error/utils";import{failure as r,success as i}from"@longzai-intelligence-shared-kernel/core";async function a(e,a){try{let{expectedVersion:t,where:n,set:r}=a,o=t+1,s={...r,version:o};return i({affectedRows:(await e.update(n,s)).affected??0})}catch(e){return r(new t(`乐观锁条件更新失败`,n(e),{ormId:String(a.orm.id)}))}}const o=`并发更新冲突:记录已被其他事务修改,请重试`;function s(t){let n=[];return t?.aggregate!==void 0&&n.push(`aggregate=${t.aggregate}`),t?.aggregateId!==void 0&&n.push(`aggregateId=${t.aggregateId}`),t?.expectedVersion!==void 0&&n.push(`expectedVersion=${t.expectedVersion}`),new e(n.length>0?`${o}(${n.join(`, `)})`:o)}function c(e,t){if(e.isFailure)return r(e.error);let{affectedRows:n}=e.value;return n===0?r(s(t)):i({kind:`updated`,affectedRows:n})}function l(e){if(e.isFailure)return r(e.error);let{affectedRows:t}=e.value;return i(t===0?{kind:`conflict`}:{kind:`updated`,affectedRows:t})}export{a as conditionalVersionedUpdate,s as createOptimisticLockConflictError,c as handleConditionalSaveOnFail,l as handleConditionalSaveOnIdempotent};
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@longzai-intelligence-typeorm/optimistic-lock",
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"private": false,
|
|
5
|
+
"keywords": [
|
|
6
|
+
"concurrency",
|
|
7
|
+
"conditional-update",
|
|
8
|
+
"optimistic-lock",
|
|
9
|
+
"typeorm"
|
|
10
|
+
],
|
|
11
|
+
"license": "UNLICENSED",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "lzi-builder",
|
|
26
|
+
"build:prod": "NODE_ENV=production lzi-builder",
|
|
27
|
+
"prepublishOnly": "bun run build:prod",
|
|
28
|
+
"typecheck": "bun run typecheck:app && bun run typecheck:node && bun run typecheck:test",
|
|
29
|
+
"typecheck:app": "tsgo --noEmit -p tsconfig/app.json",
|
|
30
|
+
"typecheck:node": "tsgo --noEmit -p tsconfig/node.json",
|
|
31
|
+
"typecheck:test": "tsgo --noEmit -p tsconfig/test.json",
|
|
32
|
+
"lint": "oxlint && oxfmt --check",
|
|
33
|
+
"lint:fix": "oxlint --fix && oxfmt",
|
|
34
|
+
"test": "bun test",
|
|
35
|
+
"test:watch": "bun test --watch",
|
|
36
|
+
"test:coverage": "bun test --coverage",
|
|
37
|
+
"clean": "lzi-dev-cli clean"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@longzai-intelligence-error/core": "^0.1.0",
|
|
41
|
+
"@longzai-intelligence-error/utils": "^0.1.0",
|
|
42
|
+
"@longzai-intelligence-shared-kernel/core": "^0.1.0",
|
|
43
|
+
"typeorm": "^1.0.0"
|
|
44
|
+
},
|
|
45
|
+
"packageManager": "bun@1.3.14"
|
|
46
|
+
}
|