@knotx/data 0.4.12 → 0.4.14

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.
Files changed (3) hide show
  1. package/README.en.md +490 -0
  2. package/README.md +490 -0
  3. package/package.json +4 -4
package/README.en.md ADDED
@@ -0,0 +1,490 @@
1
+ # @knotx/data
2
+
3
+ A reactive data management library based on RxJS, providing powerful data operations and bidirectional relationship management capabilities.
4
+
5
+ ## 📦 Installation
6
+
7
+ ```bash
8
+ npm install @knotx/data
9
+ ```
10
+
11
+ ```bash
12
+ yarn add @knotx/data
13
+ ```
14
+
15
+ ```bash
16
+ pnpm add @knotx/data
17
+ ```
18
+
19
+ ## 🔧 Overview
20
+
21
+ `@knotx/data` is the core data management package of the Knotx ecosystem, providing two main data management tools:
22
+
23
+ - **DataManager**: Manages CRUD operations for data with advanced features like draft mode, batch operations, and version control
24
+ - **DualRelation**: Manages bidirectional relationships between parent and child nodes with level management and circular reference detection
25
+
26
+ ## 🚀 Quick Start
27
+
28
+ ```typescript
29
+ import { DataManager, DualRelation } from '@knotx/data'
30
+
31
+ // Create data manager
32
+ const dataManager = new DataManager('my-data')
33
+
34
+ // Create dual relation manager
35
+ const relation = new DualRelation<string, string>()
36
+ ```
37
+
38
+ ## 📚 API Documentation
39
+
40
+ ### DataManager
41
+
42
+ #### Type Definitions
43
+
44
+ ```typescript
45
+ interface IData {
46
+ id: string
47
+ }
48
+
49
+ // DataManager class type definition
50
+ interface DataManager<TData extends IData = IData, TTag extends string = any> {
51
+ // Data manager methods and properties
52
+ }
53
+ ```
54
+
55
+ #### Constructor
56
+
57
+ ```typescript
58
+ const dataManager = new DataManager<MyData>('my-tag')
59
+ ```
60
+
61
+ **Parameters:**
62
+ - `tag`: Identifier for the data manager
63
+
64
+ #### Core Methods
65
+
66
+ ##### `init(initialDataList?: TData[]): void`
67
+
68
+ Initialize the data manager with an initial data list.
69
+
70
+ ```typescript
71
+ interface User {
72
+ id: string
73
+ name: string
74
+ age: number
75
+ }
76
+
77
+ const userManager = new DataManager<User>('users')
78
+ userManager.init([
79
+ { id: '1', name: 'Alice', age: 25 },
80
+ { id: '2', name: 'Bob', age: 30 }
81
+ ])
82
+ ```
83
+
84
+ ##### `getDataList$(): Observable<TData[]>`
85
+
86
+ Get reactive stream of data list.
87
+
88
+ ```typescript
89
+ userManager.getDataList$().subscribe((users) => {
90
+ console.log('User list updated:', users)
91
+ })
92
+ ```
93
+
94
+ ##### `getDataList(): TData[]`
95
+
96
+ Get current data list.
97
+
98
+ ```typescript
99
+ const users = userManager.getDataList()
100
+ console.log('Current users:', users)
101
+ ```
102
+
103
+ ##### `getData$(id: string, equal?: (a?: TData, b?: TData) => boolean): Observable<TData | undefined>`
104
+
105
+ Get reactive stream of specific data by ID.
106
+
107
+ ```typescript
108
+ userManager.getData$('1').subscribe((user) => {
109
+ console.log('User 1 data:', user)
110
+ })
111
+ ```
112
+
113
+ ##### `getData(id: string): TData | undefined`
114
+
115
+ Get data by ID.
116
+
117
+ ```typescript
118
+ const user = userManager.getData('1')
119
+ console.log('User 1:', user)
120
+ ```
121
+
122
+ ##### `dispatch(operation: DataOperation<TData>): void`
123
+
124
+ Dispatch data operations.
125
+
126
+ ```typescript
127
+ // Add user
128
+ userManager.dispatch({
129
+ type: 'add',
130
+ data: { id: '3', name: 'Charlie', age: 28 }
131
+ })
132
+
133
+ // Update user
134
+ userManager.dispatch({
135
+ type: 'update',
136
+ id: '1',
137
+ data: { age: 26 }
138
+ })
139
+
140
+ // Remove user
141
+ userManager.dispatch({
142
+ type: 'remove',
143
+ id: '2'
144
+ })
145
+
146
+ // Batch operations
147
+ userManager.dispatch({
148
+ type: 'batch',
149
+ operations: [
150
+ { type: 'add', data: { id: '4', name: 'David', age: 35 } },
151
+ { type: 'update', id: '3', data: { age: 29 } }
152
+ ]
153
+ })
154
+ ```
155
+
156
+ #### Draft Mode
157
+
158
+ Draft mode allows you to perform data operations without affecting the main data.
159
+
160
+ ##### `dispatch` Draft Operations
161
+
162
+ ```typescript
163
+ // Start draft
164
+ userManager.dispatch({
165
+ type: 'startDraft',
166
+ draftId: 'my-draft'
167
+ })
168
+
169
+ // Operate in draft
170
+ userManager.dispatch({
171
+ type: 'draftOperation',
172
+ draftId: 'my-draft',
173
+ operation: {
174
+ type: 'add',
175
+ data: { id: '5', name: 'Eve', age: 24 }
176
+ }
177
+ })
178
+
179
+ // Commit draft
180
+ userManager.dispatch({
181
+ type: 'commitDraft',
182
+ draftId: 'my-draft'
183
+ })
184
+
185
+ // Or discard draft
186
+ userManager.dispatch({
187
+ type: 'discardDraft',
188
+ draftId: 'my-draft'
189
+ })
190
+ ```
191
+
192
+ ##### `getDraftDataList(draftId: string): TData[] | undefined`
193
+
194
+ Get data list in draft.
195
+
196
+ ```typescript
197
+ const draftUsers = userManager.getDraftDataList('my-draft')
198
+ ```
199
+
200
+ ##### `getDraftData(draftId: string, dataId: string): TData | undefined`
201
+
202
+ Get specific data in draft.
203
+
204
+ ```typescript
205
+ const draftUser = userManager.getDraftData('my-draft', '5')
206
+ ```
207
+
208
+ ##### `dryRun(operation: DataOperation<TData>): Map<string, TData>`
209
+
210
+ Simulate operation execution, returns result without actually modifying data.
211
+
212
+ ```typescript
213
+ const result = userManager.dryRun({
214
+ type: 'commitDraft',
215
+ draftId: 'my-draft'
216
+ })
217
+ ```
218
+
219
+ #### Version Control
220
+
221
+ DataManager provides version control functionality to track data changes.
222
+
223
+ ```typescript
224
+ // Listen to version changes
225
+ userManager.patchVersion$.subscribe((version) => {
226
+ console.log('Patch version:', version)
227
+ })
228
+
229
+ userManager.mapVersion$.subscribe((version) => {
230
+ console.log('Map version:', version)
231
+ })
232
+
233
+ // Get current version
234
+ console.log('Current version:', userManager.version)
235
+ ```
236
+
237
+ #### Operation Pipes
238
+
239
+ Operation pipes allow you to insert custom logic at different stages of data operations.
240
+
241
+ ```typescript
242
+ interface DataOperationPipe<T extends IData = IData> {
243
+ transform?: (operations$: Subject<DataOperation<T>>) => OperatorFunction<DataOperation<T>, DataOperation<T>>
244
+ preOperation?: (operations$: Subject<DataOperation<T>>) => OperatorFunction<DataOperation<T>, DataOperation<T>>
245
+ postOperation?: (operations$: Subject<DataOperation<T>>) => OperatorFunction<
246
+ { dataMap: Map<string, T>, operations: DataOperation<T>[] },
247
+ { dataMap: Map<string, T>, operations: DataOperation<T>[] }
248
+ >
249
+ }
250
+
251
+ // Add operation pipe
252
+ const removePipe = userManager.addDataOperationPipe({
253
+ preOperation: () => tap((operation) => {
254
+ console.log('Before operation:', operation)
255
+ }),
256
+ postOperation: () => tap(({ dataMap, operations }) => {
257
+ console.log('After operation:', { dataMap, operations })
258
+ })
259
+ })
260
+
261
+ // Remove pipe
262
+ removePipe()
263
+ ```
264
+
265
+ ### DualRelation
266
+
267
+ #### Type Definitions
268
+
269
+ ```typescript
270
+ // DualRelation class type definition
271
+ interface DualRelation<P, C> {
272
+ // Dual relation methods and properties
273
+ }
274
+ ```
275
+
276
+ **Generic Parameters:**
277
+ - `P`: Parent node type
278
+ - `C`: Child node type
279
+
280
+ #### Constructor
281
+
282
+ ```typescript
283
+ interface DualRelationOptions {
284
+ allowEmptyParent?: boolean // Whether to allow parent nodes without children
285
+ historyDepth?: number // Maximum number of change history records
286
+ }
287
+
288
+ const relation = new DualRelation<string, string>({
289
+ allowEmptyParent: true,
290
+ historyDepth: 50
291
+ })
292
+ ```
293
+
294
+ #### Core Methods
295
+
296
+ ##### `add(parent: P, child: C): void`
297
+
298
+ Add parent-child relationship.
299
+
300
+ ```typescript
301
+ // Create file system relationship
302
+ const fileSystem = new DualRelation<string, string>()
303
+
304
+ fileSystem.add('root', 'folder1')
305
+ fileSystem.add('root', 'folder2')
306
+ fileSystem.add('folder1', 'file1.txt')
307
+ fileSystem.add('folder1', 'file2.txt')
308
+ fileSystem.add('folder2', 'file3.txt')
309
+ ```
310
+
311
+ ##### `removeChild(child: C): boolean`
312
+
313
+ Remove child node and its parent relationship.
314
+
315
+ ```typescript
316
+ const removed = fileSystem.removeChild('file1.txt')
317
+ console.log('Successfully removed:', removed)
318
+ ```
319
+
320
+ ##### `removeParent(parent: P): boolean`
321
+
322
+ Remove parent node and all its child relationships.
323
+
324
+ ```typescript
325
+ const removed = fileSystem.removeParent('folder1')
326
+ console.log('Successfully removed:', removed)
327
+ ```
328
+
329
+ ##### `getChildren(parent: P): Set<C>`
330
+
331
+ Get all children of parent node.
332
+
333
+ ```typescript
334
+ const children = fileSystem.getChildren('root')
335
+ console.log('Root children:', Array.from(children))
336
+ ```
337
+
338
+ ##### `getParent(child: C): P | undefined`
339
+
340
+ Get parent of child node.
341
+
342
+ ```typescript
343
+ const parent = fileSystem.getParent('file1.txt')
344
+ console.log('Parent of file1.txt:', parent)
345
+ ```
346
+
347
+ ##### `getRootParent(node: C): P | C | null`
348
+
349
+ Get root parent of node.
350
+
351
+ ```typescript
352
+ const rootParent = fileSystem.getRootParent('file1.txt')
353
+ console.log('Root parent of file1.txt:', rootParent)
354
+ ```
355
+
356
+ ##### `hasRelation(parent: P, child: C): boolean`
357
+
358
+ Check if parent-child relationship exists.
359
+
360
+ ```typescript
361
+ const exists = fileSystem.hasRelation('folder1', 'file1.txt')
362
+ console.log('Relationship exists:', exists)
363
+ ```
364
+
365
+ ##### `hasCircularReference(): boolean`
366
+
367
+ Check if circular reference exists.
368
+
369
+ ```typescript
370
+ const hasCircular = fileSystem.hasCircularReference()
371
+ console.log('Has circular reference:', hasCircular)
372
+ ```
373
+
374
+ ##### `addSafe(parent: P, child: C): boolean`
375
+
376
+ Safely add relationship with circular reference check.
377
+
378
+ ```typescript
379
+ const added = fileSystem.addSafe('file1.txt', 'root') // Will fail due to circular reference
380
+ console.log('Successfully added:', added)
381
+ ```
382
+
383
+ #### Level Management
384
+
385
+ ##### `getNodeLevel(node: P | C): number`
386
+
387
+ Get node level.
388
+
389
+ ```typescript
390
+ const level = fileSystem.getNodeLevel('file1.txt')
391
+ console.log('Level of file1.txt:', level)
392
+ ```
393
+
394
+ ##### `getDescendants(parent: P, includeSelf?: boolean): Set<C>`
395
+
396
+ Get all descendants of parent.
397
+
398
+ ```typescript
399
+ const descendants = fileSystem.getDescendants('root', true)
400
+ console.log('All descendants of root:', Array.from(descendants))
401
+ ```
402
+
403
+ #### Batch Operations
404
+
405
+ ```typescript
406
+ // Begin batch operation
407
+ fileSystem.beginBatch()
408
+
409
+ // Execute multiple operations
410
+ fileSystem.add('root', 'folder3')
411
+ fileSystem.add('folder3', 'file4.txt')
412
+ fileSystem.add('folder3', 'file5.txt')
413
+
414
+ // Commit batch operation
415
+ const hasChanges = fileSystem.commitBatch()
416
+ console.log('Has changes:', hasChanges)
417
+
418
+ // Or use convenience method
419
+ const hasChanges2 = fileSystem.batch(() => {
420
+ fileSystem.add('root', 'folder4')
421
+ fileSystem.add('folder4', 'file6.txt')
422
+ })
423
+ ```
424
+
425
+ #### Change History
426
+
427
+ ```typescript
428
+ // Listen to version changes
429
+ fileSystem.version.subscribe((version) => {
430
+ console.log('Version:', version)
431
+ })
432
+
433
+ // Get change history
434
+ const history = fileSystem.changeHistory
435
+ console.log('Change history:', history)
436
+ ```
437
+
438
+ #### Utility Methods
439
+
440
+ ```typescript
441
+ // Get all parents
442
+ const allParents = fileSystem.getAllParents()
443
+
444
+ // Get all children
445
+ const allChildren = fileSystem.getAllChildren()
446
+
447
+ // Get relationship count
448
+ const count = fileSystem.size()
449
+
450
+ // Clear all relationships
451
+ fileSystem.clear()
452
+ ```
453
+
454
+ ## 📁 Directory Structure
455
+
456
+ ```
457
+ packages/data/
458
+ ├── src/
459
+ │ ├── index.ts # Main export file
460
+ │ ├── data-manager.ts # DataManager class and related interfaces
461
+ │ └── dual-relation.ts # DualRelation class and related interfaces
462
+ ├── dist/ # Compiled output directory
463
+ ├── package.json # Package configuration
464
+ ├── README.md # Chinese documentation
465
+ ├── README.en.md # English documentation
466
+ ├── CHANGELOG.md # Change log
467
+ ├── build.config.ts # Build configuration
468
+ ├── eslint.config.mjs # ESLint configuration
469
+ └── tsconfig.json # TypeScript configuration
470
+ ```
471
+
472
+ ### Source Code Structure
473
+
474
+ - **`index.ts`**: Main export file, exports DataManager and DualRelation
475
+ - **`data-manager.ts`**: Complete implementation of DataManager class providing data management functionality
476
+ - **`dual-relation.ts`**: Complete implementation of DualRelation class providing bidirectional relationship management
477
+
478
+ ## 🔗 Related Links
479
+
480
+ - [GitHub Repository](https://github.com/boenfu/knotx)
481
+ - [Issue Tracker](https://github.com/boenfu/knotx/issues)
482
+ - [Contributing Guide](https://github.com/boenfu/knotx/blob/main/CONTRIBUTING.md)
483
+
484
+ ## 📄 License
485
+
486
+ MIT License - See [LICENSE](../../LICENSE) file for details.
487
+
488
+ ## 🤝 Contributing
489
+
490
+ Contributions are welcome! Please see [CONTRIBUTING.md](../../CONTRIBUTING.md) for detailed information.
package/README.md ADDED
@@ -0,0 +1,490 @@
1
+ # @knotx/data
2
+
3
+ 一个基于 RxJS 的响应式数据管理库,提供强大的数据操作和双向关系管理功能。
4
+
5
+ ## 📦 安装
6
+
7
+ ```bash
8
+ npm install @knotx/data
9
+ ```
10
+
11
+ ```bash
12
+ yarn add @knotx/data
13
+ ```
14
+
15
+ ```bash
16
+ pnpm add @knotx/data
17
+ ```
18
+
19
+ ## 🔧 基本概述
20
+
21
+ `@knotx/data` 是 Knotx 生态系统的核心数据管理包,提供两个主要的数据管理工具:
22
+
23
+ - **DataManager**: 用于管理数据的增删改查操作,支持草稿模式、批量操作、版本控制等高级功能
24
+ - **DualRelation**: 用于管理父子节点之间的双向关系,支持层级管理、循环引用检测等功能
25
+
26
+ ## 🚀 快速开始
27
+
28
+ ```typescript
29
+ import { DataManager, DualRelation } from '@knotx/data'
30
+
31
+ // 创建数据管理器
32
+ const dataManager = new DataManager('my-data')
33
+
34
+ // 创建双向关系管理器
35
+ const relation = new DualRelation<string, string>()
36
+ ```
37
+
38
+ ## 📚 API 文档
39
+
40
+ ### DataManager
41
+
42
+ #### 类型定义
43
+
44
+ ```typescript
45
+ interface IData {
46
+ id: string
47
+ }
48
+
49
+ // DataManager 类的类型定义
50
+ interface DataManager<TData extends IData = IData, TTag extends string = any> {
51
+ // 数据管理器的方法和属性
52
+ }
53
+ ```
54
+
55
+ #### 构造函数
56
+
57
+ ```typescript
58
+ const dataManager = new DataManager<MyData>('my-tag')
59
+ ```
60
+
61
+ **参数:**
62
+ - `tag`: 数据管理器的标识符
63
+
64
+ #### 核心方法
65
+
66
+ ##### `init(initialDataList?: TData[]): void`
67
+
68
+ 初始化数据管理器,设置初始数据列表。
69
+
70
+ ```typescript
71
+ interface User {
72
+ id: string
73
+ name: string
74
+ age: number
75
+ }
76
+
77
+ const userManager = new DataManager<User>('users')
78
+ userManager.init([
79
+ { id: '1', name: 'Alice', age: 25 },
80
+ { id: '2', name: 'Bob', age: 30 }
81
+ ])
82
+ ```
83
+
84
+ ##### `getDataList$(): Observable<TData[]>`
85
+
86
+ 获取数据列表的响应式流。
87
+
88
+ ```typescript
89
+ userManager.getDataList$().subscribe((users) => {
90
+ console.log('用户列表更新:', users)
91
+ })
92
+ ```
93
+
94
+ ##### `getDataList(): TData[]`
95
+
96
+ 获取当前数据列表。
97
+
98
+ ```typescript
99
+ const users = userManager.getDataList()
100
+ console.log('当前用户:', users)
101
+ ```
102
+
103
+ ##### `getData$(id: string, equal?: (a?: TData, b?: TData) => boolean): Observable<TData | undefined>`
104
+
105
+ 获取指定ID数据的响应式流。
106
+
107
+ ```typescript
108
+ userManager.getData$('1').subscribe((user) => {
109
+ console.log('用户1数据:', user)
110
+ })
111
+ ```
112
+
113
+ ##### `getData(id: string): TData | undefined`
114
+
115
+ 获取指定ID的数据。
116
+
117
+ ```typescript
118
+ const user = userManager.getData('1')
119
+ console.log('用户1:', user)
120
+ ```
121
+
122
+ ##### `dispatch(operation: DataOperation<TData>): void`
123
+
124
+ 分发数据操作。
125
+
126
+ ```typescript
127
+ // 添加用户
128
+ userManager.dispatch({
129
+ type: 'add',
130
+ data: { id: '3', name: 'Charlie', age: 28 }
131
+ })
132
+
133
+ // 更新用户
134
+ userManager.dispatch({
135
+ type: 'update',
136
+ id: '1',
137
+ data: { age: 26 }
138
+ })
139
+
140
+ // 删除用户
141
+ userManager.dispatch({
142
+ type: 'remove',
143
+ id: '2'
144
+ })
145
+
146
+ // 批量操作
147
+ userManager.dispatch({
148
+ type: 'batch',
149
+ operations: [
150
+ { type: 'add', data: { id: '4', name: 'David', age: 35 } },
151
+ { type: 'update', id: '3', data: { age: 29 } }
152
+ ]
153
+ })
154
+ ```
155
+
156
+ #### 草稿模式
157
+
158
+ 草稿模式允许你在不影响主数据的情况下进行数据操作。
159
+
160
+ ##### `dispatch` 草稿操作
161
+
162
+ ```typescript
163
+ // 开始草稿
164
+ userManager.dispatch({
165
+ type: 'startDraft',
166
+ draftId: 'my-draft'
167
+ })
168
+
169
+ // 在草稿中操作
170
+ userManager.dispatch({
171
+ type: 'draftOperation',
172
+ draftId: 'my-draft',
173
+ operation: {
174
+ type: 'add',
175
+ data: { id: '5', name: 'Eve', age: 24 }
176
+ }
177
+ })
178
+
179
+ // 提交草稿
180
+ userManager.dispatch({
181
+ type: 'commitDraft',
182
+ draftId: 'my-draft'
183
+ })
184
+
185
+ // 或者丢弃草稿
186
+ userManager.dispatch({
187
+ type: 'discardDraft',
188
+ draftId: 'my-draft'
189
+ })
190
+ ```
191
+
192
+ ##### `getDraftDataList(draftId: string): TData[] | undefined`
193
+
194
+ 获取草稿中的数据列表。
195
+
196
+ ```typescript
197
+ const draftUsers = userManager.getDraftDataList('my-draft')
198
+ ```
199
+
200
+ ##### `getDraftData(draftId: string, dataId: string): TData | undefined`
201
+
202
+ 获取草稿中的特定数据。
203
+
204
+ ```typescript
205
+ const draftUser = userManager.getDraftData('my-draft', '5')
206
+ ```
207
+
208
+ ##### `dryRun(operation: DataOperation<TData>): Map<string, TData>`
209
+
210
+ 模拟执行操作,返回结果但不实际修改数据。
211
+
212
+ ```typescript
213
+ const result = userManager.dryRun({
214
+ type: 'commitDraft',
215
+ draftId: 'my-draft'
216
+ })
217
+ ```
218
+
219
+ #### 版本控制
220
+
221
+ DataManager 提供版本控制功能,跟踪数据变更。
222
+
223
+ ```typescript
224
+ // 监听版本变更
225
+ userManager.patchVersion$.subscribe((version) => {
226
+ console.log('补丁版本:', version)
227
+ })
228
+
229
+ userManager.mapVersion$.subscribe((version) => {
230
+ console.log('映射版本:', version)
231
+ })
232
+
233
+ // 获取当前版本
234
+ console.log('当前版本:', userManager.version)
235
+ ```
236
+
237
+ #### 操作管道
238
+
239
+ 操作管道允许你在数据操作的不同阶段插入自定义逻辑。
240
+
241
+ ```typescript
242
+ interface DataOperationPipe<T extends IData = IData> {
243
+ transform?: (operations$: Subject<DataOperation<T>>) => OperatorFunction<DataOperation<T>, DataOperation<T>>
244
+ preOperation?: (operations$: Subject<DataOperation<T>>) => OperatorFunction<DataOperation<T>, DataOperation<T>>
245
+ postOperation?: (operations$: Subject<DataOperation<T>>) => OperatorFunction<
246
+ { dataMap: Map<string, T>, operations: DataOperation<T>[] },
247
+ { dataMap: Map<string, T>, operations: DataOperation<T>[] }
248
+ >
249
+ }
250
+
251
+ // 添加操作管道
252
+ const removePipe = userManager.addDataOperationPipe({
253
+ preOperation: () => tap((operation) => {
254
+ console.log('执行操作前:', operation)
255
+ }),
256
+ postOperation: () => tap(({ dataMap, operations }) => {
257
+ console.log('执行操作后:', { dataMap, operations })
258
+ })
259
+ })
260
+
261
+ // 移除管道
262
+ removePipe()
263
+ ```
264
+
265
+ ### DualRelation
266
+
267
+ #### 类型定义
268
+
269
+ ```typescript
270
+ // DualRelation 类的类型定义
271
+ interface DualRelation<P, C> {
272
+ // 双向关系的方法和属性
273
+ }
274
+ ```
275
+
276
+ **泛型参数:**
277
+ - `P`: 父节点类型
278
+ - `C`: 子节点类型
279
+
280
+ #### 构造函数
281
+
282
+ ```typescript
283
+ interface DualRelationOptions {
284
+ allowEmptyParent?: boolean // 是否允许父节点没有子节点
285
+ historyDepth?: number // 历史变更记录的最大数量
286
+ }
287
+
288
+ const relation = new DualRelation<string, string>({
289
+ allowEmptyParent: true,
290
+ historyDepth: 50
291
+ })
292
+ ```
293
+
294
+ #### 核心方法
295
+
296
+ ##### `add(parent: P, child: C): void`
297
+
298
+ 添加父子关系。
299
+
300
+ ```typescript
301
+ // 创建文件系统关系
302
+ const fileSystem = new DualRelation<string, string>()
303
+
304
+ fileSystem.add('root', 'folder1')
305
+ fileSystem.add('root', 'folder2')
306
+ fileSystem.add('folder1', 'file1.txt')
307
+ fileSystem.add('folder1', 'file2.txt')
308
+ fileSystem.add('folder2', 'file3.txt')
309
+ ```
310
+
311
+ ##### `removeChild(child: C): boolean`
312
+
313
+ 移除子节点及其父关系。
314
+
315
+ ```typescript
316
+ const removed = fileSystem.removeChild('file1.txt')
317
+ console.log('是否成功移除:', removed)
318
+ ```
319
+
320
+ ##### `removeParent(parent: P): boolean`
321
+
322
+ 移除父节点及其所有子关系。
323
+
324
+ ```typescript
325
+ const removed = fileSystem.removeParent('folder1')
326
+ console.log('是否成功移除:', removed)
327
+ ```
328
+
329
+ ##### `getChildren(parent: P): Set<C>`
330
+
331
+ 获取父节点的所有子节点。
332
+
333
+ ```typescript
334
+ const children = fileSystem.getChildren('root')
335
+ console.log('root的子节点:', Array.from(children))
336
+ ```
337
+
338
+ ##### `getParent(child: C): P | undefined`
339
+
340
+ 获取子节点的父节点。
341
+
342
+ ```typescript
343
+ const parent = fileSystem.getParent('file1.txt')
344
+ console.log('file1.txt的父节点:', parent)
345
+ ```
346
+
347
+ ##### `getRootParent(node: C): P | C | null`
348
+
349
+ 获取节点的根父节点。
350
+
351
+ ```typescript
352
+ const rootParent = fileSystem.getRootParent('file1.txt')
353
+ console.log('file1.txt的根父节点:', rootParent)
354
+ ```
355
+
356
+ ##### `hasRelation(parent: P, child: C): boolean`
357
+
358
+ 检查是否存在父子关系。
359
+
360
+ ```typescript
361
+ const exists = fileSystem.hasRelation('folder1', 'file1.txt')
362
+ console.log('关系是否存在:', exists)
363
+ ```
364
+
365
+ ##### `hasCircularReference(): boolean`
366
+
367
+ 检查是否存在循环引用。
368
+
369
+ ```typescript
370
+ const hasCircular = fileSystem.hasCircularReference()
371
+ console.log('是否存在循环引用:', hasCircular)
372
+ ```
373
+
374
+ ##### `addSafe(parent: P, child: C): boolean`
375
+
376
+ 安全地添加关系,会检查循环引用。
377
+
378
+ ```typescript
379
+ const added = fileSystem.addSafe('file1.txt', 'root') // 会失败,因为会产生循环引用
380
+ console.log('是否成功添加:', added)
381
+ ```
382
+
383
+ #### 层级管理
384
+
385
+ ##### `getNodeLevel(node: P | C): number`
386
+
387
+ 获取节点的层级。
388
+
389
+ ```typescript
390
+ const level = fileSystem.getNodeLevel('file1.txt')
391
+ console.log('file1.txt的层级:', level)
392
+ ```
393
+
394
+ ##### `getDescendants(parent: P, includeSelf?: boolean): Set<C>`
395
+
396
+ 获取父节点的所有后代。
397
+
398
+ ```typescript
399
+ const descendants = fileSystem.getDescendants('root', true)
400
+ console.log('root的所有后代:', Array.from(descendants))
401
+ ```
402
+
403
+ #### 批量操作
404
+
405
+ ```typescript
406
+ // 开始批量操作
407
+ fileSystem.beginBatch()
408
+
409
+ // 执行多个操作
410
+ fileSystem.add('root', 'folder3')
411
+ fileSystem.add('folder3', 'file4.txt')
412
+ fileSystem.add('folder3', 'file5.txt')
413
+
414
+ // 提交批量操作
415
+ const hasChanges = fileSystem.commitBatch()
416
+ console.log('是否有变更:', hasChanges)
417
+
418
+ // 或者使用便捷方法
419
+ const hasChanges2 = fileSystem.batch(() => {
420
+ fileSystem.add('root', 'folder4')
421
+ fileSystem.add('folder4', 'file6.txt')
422
+ })
423
+ ```
424
+
425
+ #### 变更历史
426
+
427
+ ```typescript
428
+ // 监听版本变更
429
+ fileSystem.version.subscribe((version) => {
430
+ console.log('版本:', version)
431
+ })
432
+
433
+ // 获取变更历史
434
+ const history = fileSystem.changeHistory
435
+ console.log('变更历史:', history)
436
+ ```
437
+
438
+ #### 工具方法
439
+
440
+ ```typescript
441
+ // 获取所有父节点
442
+ const allParents = fileSystem.getAllParents()
443
+
444
+ // 获取所有子节点
445
+ const allChildren = fileSystem.getAllChildren()
446
+
447
+ // 获取关系数量
448
+ const count = fileSystem.size()
449
+
450
+ // 清空所有关系
451
+ fileSystem.clear()
452
+ ```
453
+
454
+ ## 📁 目录结构
455
+
456
+ ```
457
+ packages/data/
458
+ ├── src/
459
+ │ ├── index.ts # 主要导出文件
460
+ │ ├── data-manager.ts # DataManager 类和相关接口
461
+ │ └── dual-relation.ts # DualRelation 类和相关接口
462
+ ├── dist/ # 编译输出目录
463
+ ├── package.json # 包配置文件
464
+ ├── README.md # 中文文档
465
+ ├── README.en.md # 英文文档
466
+ ├── CHANGELOG.md # 变更日志
467
+ ├── build.config.ts # 构建配置
468
+ ├── eslint.config.mjs # ESLint 配置
469
+ └── tsconfig.json # TypeScript 配置
470
+ ```
471
+
472
+ ### 源代码结构
473
+
474
+ - **`index.ts`**: 主要的导出文件,导出 DataManager 和 DualRelation
475
+ - **`data-manager.ts`**: 包含 DataManager 类的完整实现,提供数据管理功能
476
+ - **`dual-relation.ts`**: 包含 DualRelation 类的完整实现,提供双向关系管理功能
477
+
478
+ ## 🔗 相关链接
479
+
480
+ - [GitHub 仓库](https://github.com/boenfu/knotx)
481
+ - [问题反馈](https://github.com/boenfu/knotx/issues)
482
+ - [贡献指南](https://github.com/boenfu/knotx/blob/main/CONTRIBUTING.md)
483
+
484
+ ## 📄 许可证
485
+
486
+ MIT License - 详见 [LICENSE](../../LICENSE) 文件。
487
+
488
+ ## 🤝 贡献
489
+
490
+ 欢迎贡献代码!请查看 [CONTRIBUTING.md](../../CONTRIBUTING.md) 了解详细信息。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knotx/data",
3
- "version": "0.4.12",
3
+ "version": "0.4.14",
4
4
  "description": "Data for Knotx",
5
5
  "author": "boenfu",
6
6
  "license": "MIT",
@@ -33,9 +33,9 @@
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/lodash-es": "^4.17.12",
36
- "@knotx/build-config": "0.4.12",
37
- "@knotx/eslint-config": "0.4.12",
38
- "@knotx/typescript-config": "0.4.12"
36
+ "@knotx/build-config": "0.4.14",
37
+ "@knotx/eslint-config": "0.4.14",
38
+ "@knotx/typescript-config": "0.4.14"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "unbuild",