@aiao/rxdb-plugin-tree 0.0.0-stage → 0.0.28

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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -2
  3. package/dist/constants-DRludxr-.js +41 -0
  4. package/dist/constants.d.ts +49 -0
  5. package/dist/constants.d.ts.map +1 -0
  6. package/dist/entity/tree-entity-base.d.ts +44 -0
  7. package/dist/entity/tree-entity-base.d.ts.map +1 -0
  8. package/dist/entity/tree-entity.decorator.d.ts +7 -0
  9. package/dist/entity/tree-entity.decorator.d.ts.map +1 -0
  10. package/dist/entity/tree-entity.interface.d.ts +36 -0
  11. package/dist/entity/tree-entity.interface.d.ts.map +1 -0
  12. package/dist/entity/tree-metadata.interface.d.ts +34 -0
  13. package/dist/entity/tree-metadata.interface.d.ts.map +1 -0
  14. package/dist/generator/TreeRepositoryGenerator.d.ts +37 -0
  15. package/dist/generator/TreeRepositoryGenerator.d.ts.map +1 -0
  16. package/dist/generator/index.d.ts +17 -0
  17. package/dist/generator/index.d.ts.map +1 -0
  18. package/dist/generator.js +87 -0
  19. package/dist/index.d.ts +23 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +564 -0
  22. package/dist/plugin.d.ts +84 -0
  23. package/dist/plugin.d.ts.map +1 -0
  24. package/dist/query/merge-update-tree.d.ts +80 -0
  25. package/dist/query/merge-update-tree.d.ts.map +1 -0
  26. package/dist/query/merge_create.d.ts +9 -0
  27. package/dist/query/merge_create.d.ts.map +1 -0
  28. package/dist/query/merge_remove.d.ts +9 -0
  29. package/dist/query/merge_remove.d.ts.map +1 -0
  30. package/dist/query/merge_update.d.ts +9 -0
  31. package/dist/query/merge_update.d.ts.map +1 -0
  32. package/dist/query/query-tree.utils.d.ts +87 -0
  33. package/dist/query/query-tree.utils.d.ts.map +1 -0
  34. package/dist/query/tree-helper.d.ts +143 -0
  35. package/dist/query/tree-helper.d.ts.map +1 -0
  36. package/dist/query/tree-query.interface.d.ts +53 -0
  37. package/dist/query/tree-query.interface.d.ts.map +1 -0
  38. package/dist/repository/TreeRepository.d.ts +56 -0
  39. package/dist/repository/TreeRepository.d.ts.map +1 -0
  40. package/dist/repository/tree-level.utils.d.ts +9 -0
  41. package/dist/repository/tree-level.utils.d.ts.map +1 -0
  42. package/dist/repository/tree-repository.interface.d.ts +70 -0
  43. package/dist/repository/tree-repository.interface.d.ts.map +1 -0
  44. package/package.json +63 -3
  45. package/src/constants.ts +97 -0
  46. package/src/entity/tree-entity-base.ts +100 -0
  47. package/src/entity/tree-entity.decorator.ts +37 -0
  48. package/src/entity/tree-entity.interface.ts +43 -0
  49. package/src/entity/tree-metadata.interface.ts +35 -0
  50. package/src/generator/TreeRepositoryGenerator.ts +153 -0
  51. package/src/generator/index.ts +17 -0
  52. package/src/index.ts +23 -0
  53. package/src/plugin.ts +111 -0
  54. package/src/query/merge-update-tree.ts +558 -0
  55. package/src/query/merge_create.ts +125 -0
  56. package/src/query/merge_remove.ts +164 -0
  57. package/src/query/merge_update.ts +132 -0
  58. package/src/query/query-tree.utils.ts +204 -0
  59. package/src/query/tree-helper.ts +342 -0
  60. package/src/query/tree-query.interface.ts +66 -0
  61. package/src/repository/TreeRepository.ts +133 -0
  62. package/src/repository/tree-level.utils.ts +14 -0
  63. package/src/repository/tree-repository.interface.ts +76 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Aiao Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,112 @@
1
- # Temporary Holding Version
1
+ # @aiao/rxdb-plugin-tree
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ > Implements: [US-025 核心插件化拆分](https://github.com/aiao-io/rxdb/blob/main/requirements/stories/core/US-025-core-plugin-extraction.md)
4
+
5
+ RxDB 树形结构插件:邻接表模型的实体基类、装饰器、`TreeRepository`,以及四个树查询的**真增量 merge**。
6
+
7
+ 这套能力曾经内置在 `@aiao/rxdb`。核心里躺着 4 个树 task 类型、3 个 merge switch 的树分支和约 1100 行树专属合并逻辑——它们从永远加载的 switch 里可达,tree-shaking 甩不掉,不用树的应用照样付这份体积。外移之后,装插件才有树。
8
+
9
+ ## 安装
10
+
11
+ ```bash
12
+ pnpm add @aiao/rxdb @aiao/rxdb-adapter-wa-sqlite @aiao/rxdb-plugin-tree
13
+ ```
14
+
15
+ ## 使用
16
+
17
+ ```typescript
18
+ import { PropertyType, RxDB, SyncType } from '@aiao/rxdb';
19
+ import { RxDBAdapterWaSqlite } from '@aiao/rxdb-adapter-wa-sqlite';
20
+ import { TreeAdjacencyListEntityBase, TreeEntity, rxDBPluginTree } from '@aiao/rxdb-plugin-tree';
21
+
22
+ @TreeEntity({
23
+ name: 'Category',
24
+ properties: [{ name: 'name', type: PropertyType.string }],
25
+ features: { tree: { hasChildren: true } }
26
+ })
27
+ class Category extends TreeAdjacencyListEntityBase {
28
+ name!: string;
29
+ }
30
+
31
+ const rxdb = new RxDB({
32
+ dbName: 'catalog',
33
+ entities: [Category],
34
+ sync: { type: SyncType.None, local: { adapter: 'wa-sqlite' } }
35
+ });
36
+
37
+ // 必须在 init() 之前 use():仓储注册发生在 init() 内部,早于实体元数据校验。
38
+ rxdb.use(rxDBPluginTree).adapter('wa-sqlite', db => new RxDBAdapterWaSqlite(db, { vfs: 'MemoryAsyncVFS' }));
39
+
40
+ await rxdb.connect('wa-sqlite');
41
+ rxdb.init();
42
+
43
+ Category.findDescendants({ entityId: root.id, level: 2 }).subscribe(nodes => {
44
+ console.log(nodes.length);
45
+ });
46
+ ```
47
+
48
+ 不装插件而实体声明了 `repository: 'TreeRepository'`(`@TreeEntity` 会自动写上),`init()` 直接抛错并列出当前已注册的仓储名。
49
+
50
+ ## 公开面
51
+
52
+ | 分类 | 符号 |
53
+ | ---- | -------------------------------------------------------------------------------------------- |
54
+ | 插件 | `RxDBPluginTree`、`rxDBPluginTree` |
55
+ | 实体 | `TreeAdjacencyListEntityBase`、`TREE_ADJACENCY_LIST_ENTITY_BASE_OPTIONS`、`@TreeEntity` |
56
+ | 类型 | `ITreeEntity`、`ISortableTreeEntity`、`TreeEntityType`、`EntityMetadataTreeFeatures` |
57
+ | 仓储 | `TreeRepository`、`ITreeRepository`、`FindTreeOptions` |
58
+ | 查询 | `FindDescendantsQuery`、`FindAncestorsQuery`、`CountDescendantsQuery`、`CountAncestorsQuery` |
59
+
60
+ 四个查询 task 类型经 `RepositoryQueryExtensions` 模块增强挂进核心的 `QueryOptions` 联合——这是插件给核心闭合联合加分支的唯一缝。
61
+
62
+ ## 查询语义
63
+
64
+ `level` 包含当前节点本身:`level: 0` 只有当前节点,`level: 1` 是当前节点 + 直接子节点。数字以外的值(字符串、小数、NaN)在适配器层直接抛 `RxDBError`,没有兜底。
65
+
66
+ - `findDescendants` / `countDescendants`:指定 `entityId` 时**不含**当前节点;不指定时覆盖所有根节点及其后代。
67
+ - `findAncestors` / `countAncestors`:指定 `entityId` 时**含**当前节点。
68
+
69
+ ## 增量 merge
70
+
71
+ 树查询与图查询不同:图查询一律回 SQL `refresh()`,树查询走真增量。`TreeRepository` 构造时按 task 类型注册 `merge_create` / `merge_update` / `merge_remove`,三道前置守卫之后才进增量路径:
72
+
73
+ 1. `task.result === undefined` → refresh;
74
+ 2. 事件里有过期实体(`isStaleEntityEvent`)→ refresh;
75
+ 3. `findAncestors` 且 `parentId` 变过 → refresh(祖先链已被改写,增量无从谈起)。
76
+
77
+ `find*` 走 recalculate,`count*` 走 refresh。用到的 merge 原语(`prepareIncrementalUpdate` / `applyExternalEntityUpdate` / `getEntityId` 等)由 `@aiao/rxdb` 公开导出,进基线后受兼容承诺约束;`classifyUpdates` 与 `invalidateEntityFingerprint` 是核心内部装配细节,不在公开面上。
78
+
79
+ ## 连接纪元
80
+
81
+ 插件声明 `lifecycle: 'scoped'`,唯一的宿主改动——注册 `TreeRepository`——登记在 `install(scope)` 收到的作用域上,`disconnectAll()` 时随作用域一起撤销。重新 `connect()` 会重新注册。
82
+
83
+ ## 框架绑定
84
+
85
+ 四个树查询 hook 在各自的框架包里,三端同名同形:
86
+
87
+ - Angular:[`@aiao/rxdb-plugin-tree-angular`](../rxdb-plugin-tree-angular)
88
+ - React:[`@aiao/rxdb-plugin-tree-react`](../rxdb-plugin-tree-react)
89
+ - Vue:[`@aiao/rxdb-plugin-tree-vue`](../rxdb-plugin-tree-vue)
90
+
91
+ ## 适配器
92
+
93
+ `TreeRepository` 的名字没变,适配器仍按字符串 `'TreeRepository'` 分发,公开 API 一字未动。PGlite / SQLite / Supabase 三家的 `*TreeRepository` 只把类型来源改到了本包。
94
+
95
+ ## 迁移
96
+
97
+ 从内置树升级见 [tree-split 迁移说明](https://github.com/aiao-io/rxdb/blob/main/website/docs/migration/tree-split.md)。
98
+
99
+ ## 开发命令
100
+
101
+ ```bash
102
+ pnpm nx test rxdb-plugin-tree # node 环境,只跑构建期生成器 spec
103
+ pnpm nx run rxdb-plugin-tree:test-browser # 先跑 node 趟(依赖 coverage target),再在真实 chromium 里跑运行时 spec
104
+ ```
105
+
106
+ `vite.config.mts` 按 `VITEST_BROWSER` 环境变量把测试拆成两趟:不设该变量时(`nx test` 走的路径)排除全部 `*.browser.spec.ts`,只剩 `src/__tests__/generator/` 下的构建期生成器 spec;查询 / 合并 / 仓储等运行时 spec 都以 `.browser.spec.ts` 结尾,只有 `VITEST_BROWSER=true`(即 `test-browser`)才会执行。
107
+
108
+ 所以 `pnpm nx test rxdb-plugin-tree --watch` 做 TDD 会**静默**跳过全部运行时 spec——改了查询 / 合并 / 仓储代码,红没红只有 `test-browser` 看得到。
109
+
110
+ ## License
111
+
112
+ [MIT](https://github.com/aiao-io/rxdb/blob/main/LICENSE)
@@ -0,0 +1,41 @@
1
+ import { OnDeleteAction as e, PropertyType as t, RelationKind as n } from "@aiao/rxdb";
2
+ //#region src/constants.ts
3
+ var r = [
4
+ "findDescendants",
5
+ "findAncestors",
6
+ "countDescendants",
7
+ "countAncestors"
8
+ ], i = new Set(r), a = {
9
+ name: "TreeAdjacencyListEntityBase",
10
+ abstract: !0,
11
+ repository: "TreeRepository",
12
+ computedProperties: [{
13
+ name: "hasChildren",
14
+ displayName: "是否有子节点",
15
+ type: t.boolean,
16
+ nullable: !0,
17
+ readonly: !0
18
+ }],
19
+ relations: [{
20
+ name: "children",
21
+ displayName: "子节点",
22
+ kind: n.ONE_TO_MANY,
23
+ mappedEntity: "TreeAdjacencyListEntityBase",
24
+ mappedProperty: "parent"
25
+ }, {
26
+ name: "parent",
27
+ columnName: "parentId",
28
+ displayName: "父节点",
29
+ kind: n.MANY_TO_ONE,
30
+ mappedEntity: "TreeAdjacencyListEntityBase",
31
+ mappedProperty: "children",
32
+ nullable: !0,
33
+ onDelete: e.CASCADE
34
+ }],
35
+ features: { tree: {
36
+ type: "adjacency-list",
37
+ hasChildren: !0
38
+ } }
39
+ };
40
+ //#endregion
41
+ export { i as n, r, a as t };
@@ -0,0 +1,49 @@
1
+ import { EntityMetadataOptions } from '@aiao/rxdb';
2
+ /**
3
+ * 树查询任务类型的**单一来源**。
4
+ *
5
+ * @remarks
6
+ * 这四个名字要被三处消费:`*Query` 接口的 `type` 字段(类型侧)、
7
+ * {@link TREE_QUERY_TYPES}(三个 merge 的首道过滤)、`TreeRepository._STATIC_METHODS`
8
+ * (挂到实体上的静态方法名)。它们此前各写一份,加第五种树查询时漏掉其中一处
9
+ * **不会报错**,只会让新任务静默落回核心默认 merge、或者压根不挂到实体上。
10
+ *
11
+ * 现在后两者从这里派生,接口侧用 `Extract<TreeQueryType, …>` 与它绑定,
12
+ * 双向一致性由 `__tests__/contracts/tree-query-type-parity.spec.ts` 在编译期钉死。
13
+ */
14
+ export declare const TREE_QUERY_TYPE_LIST: readonly ["findDescendants", "findAncestors", "countDescendants", "countAncestors"];
15
+ /**
16
+ * 树查询任务类型的联合,由 {@link TREE_QUERY_TYPE_LIST} 派生。
17
+ */
18
+ export type TreeQueryType = (typeof TREE_QUERY_TYPE_LIST)[number];
19
+ /**
20
+ * 树查询任务类型集合
21
+ *
22
+ * @remarks
23
+ * 三个 merge 函数用它做首道过滤:`TreeRepository` 把自己注册到 `QueryManager` 时是
24
+ * 按 task 类型逐个登记的,但同一个 Repository 上也跑着 `findAll` / `count` 这类通用查询,
25
+ * 它们必须原样落回核心的默认 merge。没有这一句,树的增量逻辑会去接管非树任务。
26
+ *
27
+ * 元素类型声明成 `string` 而不是 {@link TreeQueryType}:调用方传进来的是
28
+ * `QueryTask.type`(`string`),收窄成联合反而让 `has()` 在调用点编译失败。
29
+ */
30
+ export declare const TREE_QUERY_TYPES: ReadonlySet<string>;
31
+ /**
32
+ * `TreeAdjacencyListEntityBase` 的 `@Entity` 元数据。
33
+ *
34
+ * @remarks
35
+ * 邻接表模型的那一份声明:`parentId` 外键、`children` 一对多自关联、
36
+ * `hasChildren` 计算属性,以及 `repository: 'TreeRepository'`。子类沿原型链继承整份,
37
+ * 所以 `@Entity({ name: 'Category' })` 不必重复声明任何一项就有树能力。
38
+ *
39
+ * 之所以导出而不是内联进装饰器:`@TreeEntity` 装饰器要拿它去合并用户自己的选项,
40
+ * 手写实体(不继承基类、自己实现 {@link ITreeEntity})也要靠它对齐字段,
41
+ * 两条路径必须用同一份声明,否则「继承来的树」和「手写的树」在 schema 上会分叉。
42
+ *
43
+ * 放在这个不含装饰器与 rxjs 的模块里,`/generator` 子路径才能读到它而不必导入
44
+ * {@link TreeAdjacencyListEntityBase} —— 生成器只在构建期跑。
45
+ *
46
+ * @see {@link TreeAdjacencyListEntityBase}
47
+ */
48
+ export declare const TREE_ADJACENCY_LIST_ENTITY_BASE_OPTIONS: EntityMetadataOptions;
49
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,qBAAqB,EAA8C,MAAM,YAAY,CAAC;AAE/F;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,qFAAsF,CAAC;AAExH;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAAyC,CAAC;AAE3F;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,uCAAuC,EAAE,qBAyC5C,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { EntityBase, RxDBEntityId, UUID } from '@aiao/rxdb';
2
+ import { Observable } from 'rxjs';
3
+ import { FindTreeOptions } from '../repository/tree-repository.interface.js';
4
+ import { ITreeEntity } from './tree-entity.interface.js';
5
+ /**
6
+ * 树形实体装饰器配置
7
+ * 定义了树形结构所需的父子关系
8
+ */
9
+ export declare abstract class TreeAdjacencyListEntityBase<Id extends RxDBEntityId = UUID> extends EntityBase<Id> implements ITreeEntity {
10
+ /**
11
+ * 父节点ID
12
+ */
13
+ parentId?: Id | null;
14
+ /**
15
+ * 是否有子节点,由数据库计算属性自动填充。
16
+ * `declare` 因为它是 metadata 注入的计算属性,class 不需要初始化。
17
+ */
18
+ hasChildren?: boolean | null;
19
+ /**
20
+ * 查询所有子孙节点
21
+ * @param options - 查询选项,包含实体ID和层级深度
22
+ * @returns Observable 包装的子孙节点数组
23
+ */
24
+ static findDescendants: <T extends TreeAdjacencyListEntityBase<RxDBEntityId>>(this: new () => T, options: FindTreeOptions<new () => T>) => Observable<T[]>;
25
+ /**
26
+ * 统计子孙节点数量
27
+ * @param options - 查询选项,包含实体ID和层级深度
28
+ * @returns Observable 包装的子孙节点数量
29
+ */
30
+ static countDescendants: <T extends TreeAdjacencyListEntityBase<RxDBEntityId>>(this: new () => T, options: FindTreeOptions<new () => T>) => Observable<number>;
31
+ /**
32
+ * 查询所有祖先节点
33
+ * @param options - 查询选项,包含实体ID和层级深度
34
+ * @returns Observable 包装的祖先节点数组
35
+ */
36
+ static findAncestors: <T extends TreeAdjacencyListEntityBase<RxDBEntityId>>(this: new () => T, options: FindTreeOptions<new () => T>) => Observable<T[]>;
37
+ /**
38
+ * 统计祖先节点数量
39
+ * @param options - 查询选项,包含实体ID和层级深度
40
+ * @returns Observable 包装的祖先节点数量
41
+ */
42
+ static countAncestors: <T extends TreeAdjacencyListEntityBase<RxDBEntityId>>(this: new () => T, options: FindTreeOptions<new () => T>) => Observable<number>;
43
+ }
44
+ //# sourceMappingURL=tree-entity-base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-entity-base.d.ts","sourceRoot":"","sources":["../../src/entity/tree-entity-base.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAU,UAAU,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAElC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD;;;GAGG;AACH,8BA8BsB,2BAA2B,CAAC,EAAE,SAAS,YAAY,GAAG,IAAI,CAC9E,SAAQ,UAAU,CAAC,EAAE,CACrB,YAAW,WAAW;IAEtB;;OAEG;IACH,QAAQ,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC;IAErB;;;OAGG;IACK,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAErC;;;;OAIG;IACH,OAAe,eAAe,EAAE,CAAC,CAAC,SAAS,2BAA2B,CAAC,YAAY,CAAC,EAClF,IAAI,EAAE,UAAU,CAAC,EACjB,OAAO,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC,KAClC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC;IAErB;;;;OAIG;IACH,OAAe,gBAAgB,EAAE,CAAC,CAAC,SAAS,2BAA2B,CAAC,YAAY,CAAC,EACnF,IAAI,EAAE,UAAU,CAAC,EACjB,OAAO,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC,KAClC,UAAU,CAAC,MAAM,CAAC,CAAC;IAExB;;;;OAIG;IACH,OAAe,aAAa,EAAE,CAAC,CAAC,SAAS,2BAA2B,CAAC,YAAY,CAAC,EAChF,IAAI,EAAE,UAAU,CAAC,EACjB,OAAO,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC,KAClC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC;IAErB;;;;OAIG;IACH,OAAe,cAAc,EAAE,CAAC,CAAC,SAAS,2BAA2B,CAAC,YAAY,CAAC,EACjF,IAAI,EAAE,UAAU,CAAC,EACjB,OAAO,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC,KAClC,UAAU,CAAC,MAAM,CAAC,CAAC;CACzB"}
@@ -0,0 +1,7 @@
1
+ import { Entity } from '@aiao/rxdb';
2
+ /**
3
+ * 树形实体装饰器
4
+ * 用于将类标记为树形结构实体,并处理树形特定的元数据
5
+ */
6
+ export declare const TreeEntity: typeof Entity;
7
+ //# sourceMappingURL=tree-entity.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-entity.decorator.d.ts","sourceRoot":"","sources":["../../src/entity/tree-entity.decorator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,MAAM,EAAyB,MAAM,YAAY,CAAC;AAE3D;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,OAAO,MAc/B,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { IEntity, IEntityStaticType, RxDBEntityId, SortOrderKey } from '@aiao/rxdb';
2
+ export interface ITreeEntity extends IEntity {
3
+ /**
4
+ * 父节点主键。与 {@link IEntity.id} 同属 {@link RxDBEntityId} 域 ——
5
+ * 一棵树的 `parentId` 指向的正是另一个节点的 `id`,两者必须允许同样的类型
6
+ * (`string | number | bigint`),否则数值主键的树在类型层根本无法表达。
7
+ */
8
+ parentId?: RxDBEntityId | null;
9
+ /**
10
+ * 是否有子节点。由 `TreeAdjacencyListEntityBase` 的计算属性自动填充,应用层只读。
11
+ * 未启用 `features.tree.hasChildren` 的实体此字段为 `null`。
12
+ */
13
+ hasChildren?: boolean | null;
14
+ }
15
+ /**
16
+ * 支持手动排序的树形实体接口。
17
+ *
18
+ * 在 {@link ITreeEntity} 基础上要求实体声明 `sortOrder` 字段(通常用 fractional indexing
19
+ * 如 `generateKeyBetween` 产生)。拖放、手动排序等场景的算法约束类型。
20
+ *
21
+ * @remarks
22
+ * 键类型取自核心排序模块的 {@link SortOrderKey}(唯一来源),树节点的 `sortOrder` 仍可空可缺省;
23
+ * 组合的是键类型而不是非空的 `ISortableEntity`。只是类型:实现它不会让实体获得 `manualOrder` 声明。
24
+ */
25
+ export interface ISortableTreeEntity extends ITreeEntity {
26
+ sortOrder?: SortOrderKey | null;
27
+ }
28
+ /**
29
+ * 树形实体类型
30
+ *
31
+ * 继承了 {@link IEntity} 的 `id` / `createdAt` 等字段,再加上
32
+ * `parentId` 与 `hasChildren` 这类树形独有属性。
33
+ * {@link TreeRepository} 据此提供 `findDescendants` / `countAncestors` 等方法。
34
+ */
35
+ export type TreeEntityType = IEntityStaticType & (new (...args: never[]) => ITreeEntity);
36
+ //# sourceMappingURL=tree-entity.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-entity.interface.d.ts","sourceRoot":"","sources":["../../src/entity/tree-entity.interface.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEpF,MAAM,WAAW,WAAY,SAAQ,OAAO;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/B;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,SAAS,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CACjC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,iBAAiB,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,WAAW,CAAC,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @fileoverview 树形实体的元数据特性
3
+ *
4
+ * 经 `plugin.ts` 的 `declare module '@aiao/rxdb'` 挂到 `EntityMetadataFeatures.tree` 上。
5
+ */
6
+ type TreeType = 'adjacency-list';
7
+ /**
8
+ * 实体元数据树形结构特性接口
9
+ */
10
+ export interface EntityMetadataTreeFeatures {
11
+ /**
12
+ * 树形结构类型
13
+ *
14
+ * 目前仅支持 adjacency-list (邻接表模型),未来可能支持其他模型:
15
+ * - 'closure-table': 闭包表模型
16
+ * - 'nested-set': 嵌套集模型
17
+ * - 'materialized-path': 物化路径模型
18
+ *
19
+ * 参考资料:
20
+ * - https://www.slideshare.net/slideshow/models-for-hierarchical-data/4179181
21
+ * - https://schinckel.net/2014/09/13/long-live-adjacency-lists/
22
+ *
23
+ * @default 'adjacency-list'
24
+ */
25
+ type?: TreeType;
26
+ /**
27
+ * 是否有子节点
28
+ * 在树结构里使用,表示当前节点是否有子节点
29
+ * @default false
30
+ */
31
+ hasChildren?: boolean;
32
+ }
33
+ export {};
34
+ //# sourceMappingURL=tree-metadata.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-metadata.interface.d.ts","sourceRoot":"","sources":["../../src/entity/tree-metadata.interface.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,KAAK,QAAQ,GAAG,gBAAgB,CAAC;AAEjC;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;;;;;;;;;;;OAaG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;IAEhB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB"}
@@ -0,0 +1,37 @@
1
+ import { EntityMetadata, EntityMetadataOptions } from '@aiao/rxdb';
2
+ import { GeneratorContext, RepositoryGeneratorSymbols, RepositoryMethodsGenerator } from '@aiao/rxdb-client-generator';
3
+ /**
4
+ * 树形结构 Repository 方法生成器
5
+ * 继承 Repository 基类方法,新增 TreeRepository 特有的查询方法
6
+ */
7
+ export declare class TreeRepositoryGenerator extends RepositoryMethodsGenerator {
8
+ readonly name = "TreeRepository";
9
+ /**
10
+ * 树实体的基类与配套类型都在 `@aiao/rxdb-plugin-tree`(US-025 阶段 E 起核心不再内置树)。
11
+ *
12
+ * @remarks
13
+ * 这个值同时决定三处产物:`.js` 里 `TreeAdjacencyListEntityBase` 的 import 来源、
14
+ * `.d.ts` 里 `ITreeEntity` 的 import 来源、以及基类签名带出的 `FindTreeOptions`。
15
+ */
16
+ readonly entityBaseModuleSpecifier = "@aiao/rxdb-plugin-tree";
17
+ /**
18
+ * `TreeAdjacencyListEntityBase` 的装饰器实参是常量标识符,CLI 静态求值取不到它的值。
19
+ *
20
+ * @remarks
21
+ * 生成器随身带上这份元数据,CLI 才能在分析 `extends TreeAdjacencyListEntityBase` 的实体时
22
+ * 把它回填;否则生成器包得反向依赖本插件才拿得到(RV-015)。顺序为「自身 → 祖先」。
23
+ *
24
+ * `TreeEntityBase` 是 `@TreeEntity` 装饰器给手写实体用的别名,共用同一份声明。
25
+ */
26
+ readonly abstractEntityMetadata: ReadonlyMap<string, EntityMetadataOptions[]>;
27
+ /**
28
+ * @inheritDoc
29
+ *
30
+ * @remarks
31
+ * 不合并父类结果:`generateMethods` 没有调用 `super`,基类那一份符号由
32
+ * `entity-definition` 单独跑的基类生成轮次自报。
33
+ */
34
+ declareSymbols(metadata: EntityMetadata): RepositoryGeneratorSymbols;
35
+ protected generateMethods(context: GeneratorContext): void;
36
+ }
37
+ //# sourceMappingURL=TreeRepositoryGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TreeRepositoryGenerator.d.ts","sourceRoot":"","sources":["../../src/generator/TreeRepositoryGenerator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAgC,KAAK,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAC3G,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,0BAA0B,EAC/B,0BAA0B,EAC3B,MAAM,6BAA6B,CAAC;AAGrC;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,0BAA0B;IACrE,SAAkB,IAAI,oBAAoB;IAE1C;;;;;;OAMG;IACH,SAAkB,yBAAyB,4BAA4B;IAEvE;;;;;;;;OAQG;IACH,SAAkB,sBAAsB,EAAE,WAAW,CAAC,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAGnF;IAEH;;;;;;OAMG;IACM,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,0BAA0B;cAQ1D,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;CA0FpE"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Tree 插件的构建期生成器入口
4
+ *
5
+ * @remarks
6
+ * 供 `rxdb-client-generator` 的 CLI 通过 `repositoryGenerators` 装载:
7
+ *
8
+ * ```jsonc
9
+ * { "repositoryGenerators": ["@aiao/rxdb-plugin-tree/generator#TreeRepositoryGenerator"] }
10
+ * ```
11
+ *
12
+ * 与运行时入口分开:本入口只在构建期跑,不应把装饰器与 rxjs 拉进来。
13
+ *
14
+ * @module rxdb-plugin-tree/generator
15
+ */
16
+ export { TreeRepositoryGenerator } from './TreeRepositoryGenerator.js';
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generator/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC"}
@@ -0,0 +1,87 @@
1
+ import { t as e } from "./constants-DRludxr-.js";
2
+ import { ENTITY_BASE_METADATA_OPTIONS as t } from "@aiao/rxdb";
3
+ import { RepositoryMethodsGenerator as n, buildRules as r, generateEntityRules as i } from "@aiao/rxdb-client-generator";
4
+ //#region src/generator/TreeRepositoryGenerator.ts
5
+ var a = class extends n {
6
+ name = "TreeRepository";
7
+ entityBaseModuleSpecifier = "@aiao/rxdb-plugin-tree";
8
+ abstractEntityMetadata = /* @__PURE__ */ new Map([["TreeAdjacencyListEntityBase", [e, t]], ["TreeEntityBase", [e, t]]]);
9
+ declareSymbols(e) {
10
+ return {
11
+ entityInterfaces: e.extends[0]?.includes("TreeAdjacencyListEntityBase") ? ["ITreeEntity"] : [],
12
+ types: [{ name: `${e.name}TreeRule` }, {
13
+ exported: !0,
14
+ name: `${e.name}TreeRuleGroup`
15
+ }]
16
+ };
17
+ }
18
+ generateMethods(e) {
19
+ let { metadata: t, rxdbNamedImports: n, siblingNamedImports: a, generator: o, file: s, staticTypesInterface: c } = e, { name: l } = t, u = i(o, t).filter((e) => e.key.startsWith("children.") && e.key !== "children.id");
20
+ if (u.length === 0) throw Error(`${l} TreeRepository requires children query rules`);
21
+ let d = r(u, n, a), f = u.map((e) => `'${e.key}'`).join(" | ");
22
+ s.addTypeAlias({
23
+ name: `${l}TreeRule`,
24
+ type: Array.from(new Set(d)).join("\n| "),
25
+ hasDeclareKeyword: !0,
26
+ docs: ["TreeRule"]
27
+ }), s.addTypeAlias({
28
+ name: `${l}TreeRuleGroup`,
29
+ type: `RuleGroupBase<typeof ${l}, ${f}, ${l}TreeRule>`,
30
+ hasDeclareKeyword: !0,
31
+ docs: ["TreeRuleGroup"],
32
+ isExported: !0
33
+ }), c.addProperty({
34
+ name: "entity",
35
+ type: `${l}`,
36
+ docs: ["查询的实体"]
37
+ });
38
+ let p = `FindTreeOptions<typeof ${l},${l}TreeRuleGroup>`;
39
+ this.addStaticMethod(e, {
40
+ method: "findDescendants",
41
+ options: p,
42
+ returnType: `${l}[]`,
43
+ metHodDoc: "查询子孙实体(包含自身)",
44
+ example: `// 查询某节点的所有后代\n${l}.findDescendants({ entityId: root.id }).subscribe(list => console.log(list));\n\n// 仅查询直接子节点(level 1)\n${l}.findDescendants({ entityId: root.id, level: 1 }).subscribe(children => console.log(children));`,
45
+ baseSignature: {
46
+ entityBase: "TreeAdjacencyListEntityBase",
47
+ options: "FindTreeOptions<new () => T>",
48
+ returnType: "T[]"
49
+ }
50
+ }), this.addStaticMethod(e, {
51
+ method: "countDescendants",
52
+ options: p,
53
+ returnType: "number",
54
+ metHodDoc: "统计子孙实体数量(不包含自身)",
55
+ example: `// 统计某节点下的后代数量\n${l}.countDescendants({ entityId: root.id }).subscribe(count => console.log(count));`,
56
+ baseSignature: {
57
+ entityBase: "TreeAdjacencyListEntityBase",
58
+ options: "FindTreeOptions<new () => T>",
59
+ returnType: "number"
60
+ }
61
+ }), this.addStaticMethod(e, {
62
+ method: "findAncestors",
63
+ options: p,
64
+ returnType: `${l}[]`,
65
+ metHodDoc: "查询祖先实体(包含自身)",
66
+ example: `// 查询某节点的所有祖先(面包屑导航)\n${l}.findAncestors({ entityId: grand.id }).subscribe(ancestors => console.log(ancestors));\n\n// 仅查询直接父节点(level 1)\n${l}.findAncestors({ entityId: grand.id, level: 1 }).subscribe(parents => console.log(parents));`,
67
+ baseSignature: {
68
+ entityBase: "TreeAdjacencyListEntityBase",
69
+ options: "FindTreeOptions<new () => T>",
70
+ returnType: "T[]"
71
+ }
72
+ }), this.addStaticMethod(e, {
73
+ method: "countAncestors",
74
+ options: p,
75
+ returnType: "number",
76
+ metHodDoc: "统计祖先实体数量(不包含自身)",
77
+ example: `// 统计某节点的祖先层级深度\n${l}.countAncestors({ entityId: grand.id }).subscribe(depth => console.log(depth));`,
78
+ baseSignature: {
79
+ entityBase: "TreeAdjacencyListEntityBase",
80
+ options: "FindTreeOptions<new () => T>",
81
+ returnType: "number"
82
+ }
83
+ });
84
+ }
85
+ };
86
+ //#endregion
87
+ export { a as TreeRepositoryGenerator };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * RxDB Tree 插件
4
+ * 树形结构插件,提供邻接表模型的实体基类、装饰器、仓储与增量查询合并
5
+ *
6
+ * 主要功能:
7
+ * - 树形实体基类与装饰器(`TreeAdjacencyListEntityBase` / `@TreeEntity`)
8
+ * - 四个树查询(`findDescendants` / `countDescendants` / `findAncestors` / `countAncestors`)
9
+ * - 树查询的真增量 merge(不像图查询一律回 SQL 刷新)
10
+ *
11
+ * @module rxdb-plugin-tree
12
+ */
13
+ export * from './constants.js';
14
+ export * from './entity/tree-entity-base.js';
15
+ export * from './entity/tree-entity.decorator.js';
16
+ export * from './entity/tree-entity.interface.js';
17
+ export * from './entity/tree-metadata.interface.js';
18
+ export * from './plugin.js';
19
+ export * from './query/tree-query.interface.js';
20
+ export * from './repository/tree-level.utils.js';
21
+ export * from './repository/tree-repository.interface.js';
22
+ export * from './repository/TreeRepository.js';
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC;AAClD,cAAc,qCAAqC,CAAC;AACpD,cAAc,aAAa,CAAC;AAC5B,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,gCAAgC,CAAC"}