@fluidframework/tree 2.31.0 → 2.32.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.
Files changed (242) hide show
  1. package/.vscode/settings.json +1 -1
  2. package/CHANGELOG.md +44 -0
  3. package/api-extractor/api-extractor.current.json +5 -1
  4. package/api-report/tree.alpha.api.md +44 -20
  5. package/dist/alpha.d.ts +6 -2
  6. package/dist/feature-libraries/flex-tree/context.d.ts +1 -1
  7. package/dist/feature-libraries/flex-tree/context.d.ts.map +1 -1
  8. package/dist/feature-libraries/flex-tree/context.js +1 -2
  9. package/dist/feature-libraries/flex-tree/context.js.map +1 -1
  10. package/dist/feature-libraries/flex-tree/index.d.ts +1 -1
  11. package/dist/feature-libraries/flex-tree/index.d.ts.map +1 -1
  12. package/dist/feature-libraries/flex-tree/index.js +1 -2
  13. package/dist/feature-libraries/flex-tree/index.js.map +1 -1
  14. package/dist/feature-libraries/flex-tree/lazyEntity.d.ts +6 -15
  15. package/dist/feature-libraries/flex-tree/lazyEntity.d.ts.map +1 -1
  16. package/dist/feature-libraries/flex-tree/lazyEntity.js +9 -18
  17. package/dist/feature-libraries/flex-tree/lazyEntity.js.map +1 -1
  18. package/dist/feature-libraries/flex-tree/lazyField.d.ts +3 -3
  19. package/dist/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
  20. package/dist/feature-libraries/flex-tree/lazyField.js +14 -14
  21. package/dist/feature-libraries/flex-tree/lazyField.js.map +1 -1
  22. package/dist/feature-libraries/flex-tree/lazyNode.d.ts +4 -4
  23. package/dist/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
  24. package/dist/feature-libraries/flex-tree/lazyNode.js +12 -12
  25. package/dist/feature-libraries/flex-tree/lazyNode.js.map +1 -1
  26. package/dist/feature-libraries/index.d.ts +1 -1
  27. package/dist/feature-libraries/index.d.ts.map +1 -1
  28. package/dist/feature-libraries/index.js +1 -2
  29. package/dist/feature-libraries/index.js.map +1 -1
  30. package/dist/index.d.ts +2 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +5 -2
  33. package/dist/index.js.map +1 -1
  34. package/dist/packageVersion.d.ts +1 -1
  35. package/dist/packageVersion.js +1 -1
  36. package/dist/packageVersion.js.map +1 -1
  37. package/dist/shared-tree/sharedTree.js +2 -2
  38. package/dist/shared-tree/sharedTree.js.map +1 -1
  39. package/dist/shared-tree/treeApiAlpha.d.ts +6 -6
  40. package/dist/shared-tree/treeApiAlpha.d.ts.map +1 -1
  41. package/dist/shared-tree/treeApiAlpha.js.map +1 -1
  42. package/dist/shared-tree-core/sharedTreeCore.d.ts +0 -6
  43. package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  44. package/dist/shared-tree-core/sharedTreeCore.js +0 -17
  45. package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
  46. package/dist/simple-tree/api/conciseTree.d.ts +2 -2
  47. package/dist/simple-tree/api/conciseTree.d.ts.map +1 -1
  48. package/dist/simple-tree/api/conciseTree.js.map +1 -1
  49. package/dist/simple-tree/api/customTree.d.ts +14 -12
  50. package/dist/simple-tree/api/customTree.d.ts.map +1 -1
  51. package/dist/simple-tree/api/customTree.js.map +1 -1
  52. package/dist/simple-tree/api/getJsonSchema.d.ts +21 -7
  53. package/dist/simple-tree/api/getJsonSchema.d.ts.map +1 -1
  54. package/dist/simple-tree/api/getJsonSchema.js +8 -16
  55. package/dist/simple-tree/api/getJsonSchema.js.map +1 -1
  56. package/dist/simple-tree/api/getSimpleSchema.d.ts +3 -10
  57. package/dist/simple-tree/api/getSimpleSchema.d.ts.map +1 -1
  58. package/dist/simple-tree/api/getSimpleSchema.js +4 -16
  59. package/dist/simple-tree/api/getSimpleSchema.js.map +1 -1
  60. package/dist/simple-tree/api/index.d.ts +4 -3
  61. package/dist/simple-tree/api/index.d.ts.map +1 -1
  62. package/dist/simple-tree/api/index.js +2 -1
  63. package/dist/simple-tree/api/index.js.map +1 -1
  64. package/dist/simple-tree/api/schemaFactoryAlpha.d.ts +17 -1
  65. package/dist/simple-tree/api/schemaFactoryAlpha.d.ts.map +1 -1
  66. package/dist/simple-tree/api/schemaFactoryAlpha.js +4 -0
  67. package/dist/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
  68. package/dist/simple-tree/api/schemaFromSimple.d.ts +5 -2
  69. package/dist/simple-tree/api/schemaFromSimple.d.ts.map +1 -1
  70. package/dist/simple-tree/api/schemaFromSimple.js +24 -8
  71. package/dist/simple-tree/api/schemaFromSimple.js.map +1 -1
  72. package/dist/simple-tree/api/simpleSchemaToJsonSchema.d.ts +11 -3
  73. package/dist/simple-tree/api/simpleSchemaToJsonSchema.d.ts.map +1 -1
  74. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js +40 -28
  75. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  76. package/dist/simple-tree/api/tree.d.ts +32 -5
  77. package/dist/simple-tree/api/tree.d.ts.map +1 -1
  78. package/dist/simple-tree/api/tree.js +17 -1
  79. package/dist/simple-tree/api/tree.js.map +1 -1
  80. package/dist/simple-tree/api/verboseTree.d.ts +4 -28
  81. package/dist/simple-tree/api/verboseTree.d.ts.map +1 -1
  82. package/dist/simple-tree/api/verboseTree.js.map +1 -1
  83. package/dist/simple-tree/api/viewSchemaToSimpleSchema.d.ts.map +1 -1
  84. package/dist/simple-tree/api/viewSchemaToSimpleSchema.js +7 -3
  85. package/dist/simple-tree/api/viewSchemaToSimpleSchema.js.map +1 -1
  86. package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  87. package/dist/simple-tree/core/treeNodeKernel.js +7 -6
  88. package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
  89. package/dist/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
  90. package/dist/simple-tree/core/unhydratedFlexTree.js +12 -1
  91. package/dist/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  92. package/dist/simple-tree/index.d.ts +1 -1
  93. package/dist/simple-tree/index.d.ts.map +1 -1
  94. package/dist/simple-tree/index.js +3 -2
  95. package/dist/simple-tree/index.js.map +1 -1
  96. package/dist/simple-tree/simpleSchema.d.ts +4 -11
  97. package/dist/simple-tree/simpleSchema.d.ts.map +1 -1
  98. package/dist/simple-tree/simpleSchema.js.map +1 -1
  99. package/dist/tableSchema.d.ts +227 -0
  100. package/dist/tableSchema.d.ts.map +1 -0
  101. package/dist/tableSchema.js +234 -0
  102. package/dist/tableSchema.js.map +1 -0
  103. package/dist/treeFactory.d.ts +4 -4
  104. package/dist/treeFactory.d.ts.map +1 -1
  105. package/dist/treeFactory.js.map +1 -1
  106. package/dist/util/breakable.d.ts.map +1 -1
  107. package/dist/util/breakable.js +9 -7
  108. package/dist/util/breakable.js.map +1 -1
  109. package/lib/alpha.d.ts +6 -2
  110. package/lib/feature-libraries/flex-tree/context.d.ts +1 -1
  111. package/lib/feature-libraries/flex-tree/context.d.ts.map +1 -1
  112. package/lib/feature-libraries/flex-tree/context.js +1 -2
  113. package/lib/feature-libraries/flex-tree/context.js.map +1 -1
  114. package/lib/feature-libraries/flex-tree/index.d.ts +1 -1
  115. package/lib/feature-libraries/flex-tree/index.d.ts.map +1 -1
  116. package/lib/feature-libraries/flex-tree/index.js +1 -1
  117. package/lib/feature-libraries/flex-tree/index.js.map +1 -1
  118. package/lib/feature-libraries/flex-tree/lazyEntity.d.ts +6 -15
  119. package/lib/feature-libraries/flex-tree/lazyEntity.d.ts.map +1 -1
  120. package/lib/feature-libraries/flex-tree/lazyEntity.js +8 -17
  121. package/lib/feature-libraries/flex-tree/lazyEntity.js.map +1 -1
  122. package/lib/feature-libraries/flex-tree/lazyField.d.ts +3 -3
  123. package/lib/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
  124. package/lib/feature-libraries/flex-tree/lazyField.js +15 -15
  125. package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
  126. package/lib/feature-libraries/flex-tree/lazyNode.d.ts +4 -4
  127. package/lib/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
  128. package/lib/feature-libraries/flex-tree/lazyNode.js +13 -13
  129. package/lib/feature-libraries/flex-tree/lazyNode.js.map +1 -1
  130. package/lib/feature-libraries/index.d.ts +1 -1
  131. package/lib/feature-libraries/index.d.ts.map +1 -1
  132. package/lib/feature-libraries/index.js +1 -1
  133. package/lib/feature-libraries/index.js.map +1 -1
  134. package/lib/index.d.ts +2 -1
  135. package/lib/index.d.ts.map +1 -1
  136. package/lib/index.js +2 -1
  137. package/lib/index.js.map +1 -1
  138. package/lib/packageVersion.d.ts +1 -1
  139. package/lib/packageVersion.js +1 -1
  140. package/lib/packageVersion.js.map +1 -1
  141. package/lib/shared-tree/sharedTree.js +2 -2
  142. package/lib/shared-tree/sharedTree.js.map +1 -1
  143. package/lib/shared-tree/treeApiAlpha.d.ts +6 -6
  144. package/lib/shared-tree/treeApiAlpha.d.ts.map +1 -1
  145. package/lib/shared-tree/treeApiAlpha.js.map +1 -1
  146. package/lib/shared-tree-core/sharedTreeCore.d.ts +0 -6
  147. package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  148. package/lib/shared-tree-core/sharedTreeCore.js +0 -17
  149. package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
  150. package/lib/simple-tree/api/conciseTree.d.ts +2 -2
  151. package/lib/simple-tree/api/conciseTree.d.ts.map +1 -1
  152. package/lib/simple-tree/api/conciseTree.js.map +1 -1
  153. package/lib/simple-tree/api/customTree.d.ts +14 -12
  154. package/lib/simple-tree/api/customTree.d.ts.map +1 -1
  155. package/lib/simple-tree/api/customTree.js.map +1 -1
  156. package/lib/simple-tree/api/getJsonSchema.d.ts +21 -7
  157. package/lib/simple-tree/api/getJsonSchema.d.ts.map +1 -1
  158. package/lib/simple-tree/api/getJsonSchema.js +8 -16
  159. package/lib/simple-tree/api/getJsonSchema.js.map +1 -1
  160. package/lib/simple-tree/api/getSimpleSchema.d.ts +3 -10
  161. package/lib/simple-tree/api/getSimpleSchema.d.ts.map +1 -1
  162. package/lib/simple-tree/api/getSimpleSchema.js +4 -16
  163. package/lib/simple-tree/api/getSimpleSchema.js.map +1 -1
  164. package/lib/simple-tree/api/index.d.ts +4 -3
  165. package/lib/simple-tree/api/index.d.ts.map +1 -1
  166. package/lib/simple-tree/api/index.js +1 -1
  167. package/lib/simple-tree/api/index.js.map +1 -1
  168. package/lib/simple-tree/api/schemaFactoryAlpha.d.ts +17 -1
  169. package/lib/simple-tree/api/schemaFactoryAlpha.d.ts.map +1 -1
  170. package/lib/simple-tree/api/schemaFactoryAlpha.js +4 -0
  171. package/lib/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
  172. package/lib/simple-tree/api/schemaFromSimple.d.ts +5 -2
  173. package/lib/simple-tree/api/schemaFromSimple.d.ts.map +1 -1
  174. package/lib/simple-tree/api/schemaFromSimple.js +24 -8
  175. package/lib/simple-tree/api/schemaFromSimple.js.map +1 -1
  176. package/lib/simple-tree/api/simpleSchemaToJsonSchema.d.ts +11 -3
  177. package/lib/simple-tree/api/simpleSchemaToJsonSchema.d.ts.map +1 -1
  178. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js +41 -29
  179. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  180. package/lib/simple-tree/api/tree.d.ts +32 -5
  181. package/lib/simple-tree/api/tree.d.ts.map +1 -1
  182. package/lib/simple-tree/api/tree.js +16 -1
  183. package/lib/simple-tree/api/tree.js.map +1 -1
  184. package/lib/simple-tree/api/verboseTree.d.ts +4 -28
  185. package/lib/simple-tree/api/verboseTree.d.ts.map +1 -1
  186. package/lib/simple-tree/api/verboseTree.js.map +1 -1
  187. package/lib/simple-tree/api/viewSchemaToSimpleSchema.d.ts.map +1 -1
  188. package/lib/simple-tree/api/viewSchemaToSimpleSchema.js +7 -3
  189. package/lib/simple-tree/api/viewSchemaToSimpleSchema.js.map +1 -1
  190. package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  191. package/lib/simple-tree/core/treeNodeKernel.js +8 -7
  192. package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
  193. package/lib/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
  194. package/lib/simple-tree/core/unhydratedFlexTree.js +12 -1
  195. package/lib/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  196. package/lib/simple-tree/index.d.ts +1 -1
  197. package/lib/simple-tree/index.d.ts.map +1 -1
  198. package/lib/simple-tree/index.js +1 -1
  199. package/lib/simple-tree/index.js.map +1 -1
  200. package/lib/simple-tree/simpleSchema.d.ts +4 -11
  201. package/lib/simple-tree/simpleSchema.d.ts.map +1 -1
  202. package/lib/simple-tree/simpleSchema.js.map +1 -1
  203. package/lib/tableSchema.d.ts +227 -0
  204. package/lib/tableSchema.d.ts.map +1 -0
  205. package/lib/tableSchema.js +231 -0
  206. package/lib/tableSchema.js.map +1 -0
  207. package/lib/treeFactory.d.ts +4 -4
  208. package/lib/treeFactory.d.ts.map +1 -1
  209. package/lib/treeFactory.js.map +1 -1
  210. package/lib/util/breakable.d.ts.map +1 -1
  211. package/lib/util/breakable.js +9 -7
  212. package/lib/util/breakable.js.map +1 -1
  213. package/package.json +25 -37
  214. package/src/feature-libraries/flex-tree/context.ts +2 -2
  215. package/src/feature-libraries/flex-tree/index.ts +0 -1
  216. package/src/feature-libraries/flex-tree/lazyEntity.ts +11 -21
  217. package/src/feature-libraries/flex-tree/lazyField.ts +17 -26
  218. package/src/feature-libraries/flex-tree/lazyNode.ts +13 -19
  219. package/src/feature-libraries/index.ts +0 -1
  220. package/src/index.ts +6 -2
  221. package/src/packageVersion.ts +1 -1
  222. package/src/shared-tree/sharedTree.ts +2 -2
  223. package/src/shared-tree/treeApiAlpha.ts +17 -15
  224. package/src/shared-tree-core/sharedTreeCore.ts +0 -23
  225. package/src/simple-tree/api/conciseTree.ts +4 -4
  226. package/src/simple-tree/api/customTree.ts +16 -14
  227. package/src/simple-tree/api/getJsonSchema.ts +25 -16
  228. package/src/simple-tree/api/getSimpleSchema.ts +4 -18
  229. package/src/simple-tree/api/index.ts +4 -2
  230. package/src/simple-tree/api/schemaFactoryAlpha.ts +18 -1
  231. package/src/simple-tree/api/schemaFromSimple.ts +45 -16
  232. package/src/simple-tree/api/simpleSchemaToJsonSchema.ts +53 -34
  233. package/src/simple-tree/api/tree.ts +51 -4
  234. package/src/simple-tree/api/verboseTree.ts +7 -32
  235. package/src/simple-tree/api/viewSchemaToSimpleSchema.ts +8 -3
  236. package/src/simple-tree/core/treeNodeKernel.ts +11 -7
  237. package/src/simple-tree/core/unhydratedFlexTree.ts +17 -4
  238. package/src/simple-tree/index.ts +4 -2
  239. package/src/simple-tree/simpleSchema.ts +4 -12
  240. package/src/tableSchema.ts +609 -0
  241. package/src/treeFactory.ts +4 -3
  242. package/src/util/breakable.ts +9 -6
@@ -0,0 +1,609 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { oob } from "@fluidframework/core-utils/internal";
7
+
8
+ import { Tree } from "./shared-tree/index.js";
9
+ import {
10
+ type ImplicitAllowedTypes,
11
+ type ImplicitFieldSchema,
12
+ type InsertableObjectFromSchemaRecord,
13
+ type InsertableTreeNodeFromImplicitAllowedTypes,
14
+ type NodeKind,
15
+ type SchemaFactoryAlpha,
16
+ type ScopedSchemaName,
17
+ TreeArrayNode,
18
+ type TreeNode,
19
+ type TreeNodeFromImplicitAllowedTypes,
20
+ type TreeNodeSchemaClass,
21
+ type WithType,
22
+ } from "./simple-tree/index.js";
23
+
24
+ // Future improvement TODOs (ideally to be done before promoting these APIs to `@alpha`):
25
+ // - Custom fields on Table/Row/Column (props pattern from Nick's demo)
26
+ // - Overloads to make Column/Row schema optional when constructing Tables
27
+ // - Record-like type parameters / input parameters?
28
+ // - Move `@system` types into separate / sub scope?
29
+
30
+ /**
31
+ * Contains types and factories for creating schema to represent dynamic tabular data.
32
+ * @privateRemarks TODO: document in more detail and add `@example`s.
33
+ * @internal
34
+ */
35
+ export namespace TableSchema {
36
+ const tableSchemaFactorySubScope = "table";
37
+
38
+ // #region Column
39
+
40
+ /**
41
+ * A column in a table.
42
+ * @remarks Implemented by the schema class returned from {@link TableSchema.createColumn}.
43
+ * @sealed @internal
44
+ */
45
+ export interface IColumn {
46
+ /**
47
+ * The unique identifier of the column.
48
+ * @remarks Uniquely identifies the node within the entire tree, not just the table.
49
+ */
50
+ readonly id: string;
51
+ }
52
+
53
+ /**
54
+ * Factory for creating new table column schema.
55
+ * @internal
56
+ */
57
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- Return type is too complex to be reasonable to specify
58
+ export function createColumn<const TInputScope extends string | undefined>(
59
+ inputSchemaFactory: SchemaFactoryAlpha<TInputScope>,
60
+ ) {
61
+ const schemaFactory = inputSchemaFactory.scopedFactory(tableSchemaFactorySubScope);
62
+ type Scope = ScopedSchemaName<TInputScope, typeof tableSchemaFactorySubScope>;
63
+
64
+ /**
65
+ * {@link Column} fields.
66
+ * @remarks Extracted for re-use in returned type signature defined later in this function.
67
+ * The implicit typing is intentional.
68
+ */
69
+ const columnFields = {
70
+ id: schemaFactory.identifier,
71
+ } as const satisfies Record<string, ImplicitFieldSchema>;
72
+
73
+ /**
74
+ * A column in a table.
75
+ */
76
+ class Column extends schemaFactory.object("Column", columnFields) implements IColumn {}
77
+
78
+ type ColumnValueType = TreeNode & IColumn & WithType<ScopedSchemaName<Scope, "Column">>;
79
+ type ColumnInsertableType = InsertableObjectFromSchemaRecord<typeof columnFields>;
80
+
81
+ // Returning SingletonSchema without a type conversion results in TypeScript generating something like `readonly "__#124291@#brand": unknown;`
82
+ // for the private brand field of TreeNode.
83
+ // This numeric id doesn't seem to be stable over incremental builds, and thus causes diffs in the API extractor reports.
84
+ // This is avoided by doing this type conversion.
85
+ // The conversion is done via assignment instead of `as` to get stronger type safety.
86
+ const ColumnSchemaType: TreeNodeSchemaClass<
87
+ /* Name */ ScopedSchemaName<Scope, "Column">,
88
+ /* Kind */ NodeKind.Object,
89
+ /* TNode */ ColumnValueType,
90
+ /* TInsertable */ object & ColumnInsertableType,
91
+ /* ImplicitlyConstructable */ true,
92
+ /* Info */ typeof columnFields
93
+ > = Column;
94
+
95
+ return ColumnSchemaType;
96
+ }
97
+
98
+ /**
99
+ * Base column schema type.
100
+ * @sealed @system @internal
101
+ */
102
+ export type ColumnSchemaBase<TScope extends string | undefined> = ReturnType<
103
+ typeof createColumn<TScope>
104
+ >;
105
+
106
+ // #endregion
107
+
108
+ // #region Row
109
+
110
+ /**
111
+ * A row in a table.
112
+ * @remarks Implemented by the schema class returned from {@link TableSchema.createRow}.
113
+ * @sealed @internal
114
+ */
115
+ export interface IRow<
116
+ TCellSchema extends ImplicitAllowedTypes,
117
+ TColumnSchema extends ImplicitAllowedTypes,
118
+ > {
119
+ /**
120
+ * The unique identifier of the row.
121
+ * @remarks Uniquely identifies the node within the entire tree, not just the table.
122
+ */
123
+ readonly id: string;
124
+
125
+ /**
126
+ * Gets the cell in the specified column
127
+ * @returns The cell if it exists, otherwise undefined.
128
+ * @privateRemarks TODO: add overload that takes column ID.
129
+ */
130
+ getCell(
131
+ column: TreeNodeFromImplicitAllowedTypes<TColumnSchema>,
132
+ ): TreeNodeFromImplicitAllowedTypes<TCellSchema> | undefined;
133
+
134
+ /**
135
+ * Sets the cell in the specified column.
136
+ * @remarks To delete a cell, call {@link TableSchema.IRow.deleteCell} instead.
137
+ * @privateRemarks TODO: add overload that takes column ID.
138
+ */
139
+ setCell(
140
+ column: TreeNodeFromImplicitAllowedTypes<TColumnSchema>,
141
+ value: InsertableTreeNodeFromImplicitAllowedTypes<TCellSchema>,
142
+ ): void;
143
+
144
+ /**
145
+ * Deletes the cell in the specified column.
146
+ * @privateRemarks TODO: add overload that takes column ID.
147
+ */
148
+ deleteCell(column: TreeNodeFromImplicitAllowedTypes<TColumnSchema>): void;
149
+ }
150
+
151
+ /**
152
+ * Factory for creating new table row schema.
153
+ * @privateRemarks TODO: add overloads to make column schema optional.
154
+ * @sealed @internal
155
+ */
156
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- Return type is too complex to be reasonable to specify
157
+ export function createRow<
158
+ const TInputScope extends string | undefined,
159
+ const TCellSchema extends ImplicitAllowedTypes,
160
+ const TColumnSchema extends ColumnSchemaBase<TInputScope> = ColumnSchemaBase<TInputScope>,
161
+ >(
162
+ inputSchemaFactory: SchemaFactoryAlpha<TInputScope>,
163
+ cellSchema: TCellSchema,
164
+ _columnSchema: TColumnSchema,
165
+ ) {
166
+ const schemaFactory = inputSchemaFactory.scopedFactory(tableSchemaFactorySubScope);
167
+ type Scope = ScopedSchemaName<TInputScope, typeof tableSchemaFactorySubScope>;
168
+
169
+ type CellValueType = TreeNodeFromImplicitAllowedTypes<TCellSchema>;
170
+ type CellInsertableType = InsertableTreeNodeFromImplicitAllowedTypes<TCellSchema>;
171
+
172
+ type ColumnValueType = TreeNodeFromImplicitAllowedTypes<TColumnSchema>;
173
+
174
+ /**
175
+ * {@link Row} fields.
176
+ * @remarks Extracted for re-use in returned type signature defined later in this function.
177
+ * The implicit typing is intentional.
178
+ */
179
+ const rowFields = {
180
+ id: schemaFactory.identifier,
181
+ cells: schemaFactory.map("Row.cells", cellSchema),
182
+ } as const satisfies Record<string, ImplicitFieldSchema>;
183
+
184
+ /**
185
+ * The Row schema - this is a map of Cells where the key is the column id
186
+ */
187
+ class Row
188
+ extends schemaFactory.object("Row", rowFields)
189
+ implements IRow<TCellSchema, TColumnSchema>
190
+ {
191
+ public getCell(column: ColumnValueType): CellValueType | undefined {
192
+ return this.cells.get(column.id) as CellValueType | undefined;
193
+ }
194
+
195
+ public setCell(column: ColumnValueType, value: CellInsertableType | undefined): void {
196
+ this.cells.set(column.id, value);
197
+ }
198
+
199
+ public deleteCell(column: ColumnValueType): void {
200
+ if (!this.cells.has(column.id)) return;
201
+ this.cells.delete(column.id);
202
+ }
203
+ }
204
+
205
+ type RowValueType = TreeNode &
206
+ IRow<TCellSchema, TColumnSchema> &
207
+ WithType<ScopedSchemaName<Scope, "Row">>;
208
+ type RowInsertableType = InsertableObjectFromSchemaRecord<typeof rowFields>;
209
+
210
+ // Returning SingletonSchema without a type conversion results in TypeScript generating something like `readonly "__#124291@#brand": unknown;`
211
+ // for the private brand field of TreeNode.
212
+ // This numeric id doesn't seem to be stable over incremental builds, and thus causes diffs in the API extractor reports.
213
+ // This is avoided by doing this type conversion.
214
+ // The conversion is done via assignment instead of `as` to get stronger type safety.
215
+ const RowSchemaType: TreeNodeSchemaClass<
216
+ /* Name */ ScopedSchemaName<Scope, "Row">,
217
+ /* Kind */ NodeKind.Object,
218
+ /* TNode */ RowValueType,
219
+ /* TInsertable */ object & RowInsertableType,
220
+ /* ImplicitlyConstructable */ true,
221
+ /* Info */ typeof rowFields
222
+ > = Row;
223
+
224
+ return RowSchemaType;
225
+ }
226
+
227
+ /**
228
+ * Base row schema type.
229
+ * @sealed @system @internal
230
+ */
231
+ export type RowSchemaBase<
232
+ TScope extends string | undefined,
233
+ TCellSchema extends ImplicitAllowedTypes,
234
+ TColumnSchema extends ColumnSchemaBase<TScope> = ColumnSchemaBase<TScope>,
235
+ > = ReturnType<typeof createRow<TScope, TCellSchema, TColumnSchema>>;
236
+
237
+ // #endregion
238
+
239
+ // #region Table
240
+
241
+ /**
242
+ * A key to uniquely identify a cell in a table.
243
+ * @sealed @internal
244
+ */
245
+ export interface CellKey {
246
+ /**
247
+ * {@link TableSchema.IColumn.id} of the containing {@link TableSchema.IColumn}.
248
+ */
249
+ readonly columnId: string;
250
+
251
+ /**
252
+ * {@link TableSchema.IRow.id} of the containing {@link TableSchema.IRow}.
253
+ */
254
+ readonly rowId: string;
255
+ }
256
+
257
+ /**
258
+ * {@link TableSchema.ITable.insertColumn} parameters.
259
+ * @sealed @internal
260
+ */
261
+ export interface InsertColumnParameters<TInsertableColumn> {
262
+ /**
263
+ * The index at which to insert the new column.
264
+ * @remarks If not provided, the column will be appended to the end of the table.
265
+ */
266
+ readonly index?: number | undefined;
267
+
268
+ /**
269
+ * The column to insert.
270
+ */
271
+ readonly column: TInsertableColumn;
272
+ }
273
+
274
+ /**
275
+ * {@link TableSchema.ITable.insertRows} parameters.
276
+ * @sealed @internal
277
+ */
278
+ export interface InsertRowsParameters<TInsertableRow> {
279
+ /**
280
+ * The index at which to insert the new rows.
281
+ * @remarks If not provided, the rows will be appended to the end of the table.
282
+ */
283
+ readonly index?: number | undefined;
284
+
285
+ /**
286
+ * The rows to insert.
287
+ */
288
+ readonly rows: TInsertableRow[];
289
+ }
290
+
291
+ /**
292
+ * {@link TableSchema.ITable.setCell} parameters.
293
+ * @sealed @internal
294
+ */
295
+ export interface SetCellParameters<TInsertableCell> {
296
+ /**
297
+ * The key to uniquely identify a cell in a table.
298
+ */
299
+ readonly key: CellKey;
300
+
301
+ /**
302
+ * The cell to set.
303
+ */
304
+ readonly cell: TInsertableCell;
305
+ }
306
+
307
+ /**
308
+ * A table.
309
+ * @sealed @internal
310
+ */
311
+ export interface ITable<
312
+ TCellSchema extends ImplicitAllowedTypes,
313
+ TColumnSchema extends ImplicitAllowedTypes,
314
+ TRowSchema extends ImplicitAllowedTypes,
315
+ > {
316
+ /**
317
+ * The table's columns.
318
+ */
319
+ readonly columns: TreeArrayNode<TColumnSchema>;
320
+
321
+ /**
322
+ * The table's rows.
323
+ */
324
+ readonly rows: TreeArrayNode<TRowSchema>;
325
+
326
+ /**
327
+ * Gets a table column by its {@link TableSchema.IRow.id}.
328
+ */
329
+ getColumn(id: string): TreeNodeFromImplicitAllowedTypes<TColumnSchema> | undefined;
330
+
331
+ /**
332
+ * Gets a table row by its {@link TableSchema.IRow.id}.
333
+ */
334
+ getRow(id: string): TreeNodeFromImplicitAllowedTypes<TRowSchema> | undefined;
335
+
336
+ /**
337
+ * Gets a cell in the table by column and row IDs.
338
+ * @param key - A key that uniquely distinguishes a cell in the table, represented as a combination of the column ID and row ID.
339
+ * @privateRemarks TODO: add overload that takes row and column nodes.
340
+ */
341
+ getCell(key: CellKey): TreeNodeFromImplicitAllowedTypes<TCellSchema> | undefined;
342
+
343
+ /**
344
+ * Inserts a column into the table.
345
+ * @throws Throws an error if the column is already in the tree, or if the specified index is out of range.
346
+ */
347
+ insertColumn(
348
+ params: InsertColumnParameters<
349
+ InsertableTreeNodeFromImplicitAllowedTypes<TColumnSchema>
350
+ >,
351
+ ): TreeNodeFromImplicitAllowedTypes<TColumnSchema>;
352
+
353
+ /**
354
+ * Inserts 0 or more rows into the table.
355
+ * @throws Throws an error if any of the rows are already in the tree, or if the specified index is out of range.
356
+ */
357
+ insertRows(
358
+ params: InsertRowsParameters<InsertableTreeNodeFromImplicitAllowedTypes<TRowSchema>>,
359
+ ): TreeNodeFromImplicitAllowedTypes<TRowSchema>[];
360
+
361
+ /**
362
+ * Sets the cell at the specified location in the table.
363
+ * @remarks To delete a cell, call {@link TableSchema.ITable.deleteCell} instead.
364
+ * @privateRemarks TODO: add overload that takes column/row nodes?
365
+ */
366
+ setCell(
367
+ params: SetCellParameters<InsertableTreeNodeFromImplicitAllowedTypes<TCellSchema>>,
368
+ ): void;
369
+
370
+ /**
371
+ * Removes the specified column from the table.
372
+ * @remarks Note: this does not remove any cells from the table's rows.
373
+ * @privateRemarks
374
+ * TODO:
375
+ * - Policy for when the column is not in the table.
376
+ * - Actually remove corresponding cells from table rows.
377
+ */
378
+ removeColumn: (column: TreeNodeFromImplicitAllowedTypes<TColumnSchema>) => void;
379
+
380
+ /**
381
+ * Deletes 0 or more rows from the table.
382
+ * @privateRemarks TODO: policy for when 1 or more rows are not in the table.
383
+ */
384
+ deleteRows: (rows: readonly TreeNodeFromImplicitAllowedTypes<TRowSchema>[]) => void;
385
+
386
+ /**
387
+ * Deletes all rows from the table.
388
+ */
389
+ deleteAllRows: () => void;
390
+
391
+ /**
392
+ * Deletes the cell at the specified location in the table.
393
+ * @privateRemarks TODO: add overload that takes column/row nodes?
394
+ */
395
+ deleteCell: (key: CellKey) => void;
396
+ }
397
+
398
+ /**
399
+ * Factory for creating new table schema.
400
+ * @privateRemarks TODO: add overloads to make column/row schema optional.
401
+ * @internal
402
+ */
403
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- Return type is too complex to be reasonable to specify
404
+ export function createTable<
405
+ const TInputScope extends string | undefined,
406
+ const TCell extends ImplicitAllowedTypes,
407
+ const TColumn extends ColumnSchemaBase<TInputScope> = ColumnSchemaBase<TInputScope>,
408
+ const TRow extends RowSchemaBase<TInputScope, TCell, TColumn> = RowSchemaBase<
409
+ TInputScope,
410
+ TCell,
411
+ TColumn
412
+ >,
413
+ >(
414
+ inputSchemaFactory: SchemaFactoryAlpha<TInputScope>,
415
+ _cellSchema: TCell,
416
+ columnSchema: TColumn,
417
+ rowSchema: TRow,
418
+ ) {
419
+ const schemaFactory = inputSchemaFactory.scopedFactory(tableSchemaFactorySubScope);
420
+ type Scope = ScopedSchemaName<TInputScope, typeof tableSchemaFactorySubScope>;
421
+
422
+ type CellValueType = TreeNodeFromImplicitAllowedTypes<TCell>;
423
+ type CellInsertableType = InsertableTreeNodeFromImplicitAllowedTypes<TCell>;
424
+
425
+ type ColumnValueType = TreeNodeFromImplicitAllowedTypes<TColumn>;
426
+ type ColumnInsertableType = InsertableTreeNodeFromImplicitAllowedTypes<TColumn>;
427
+
428
+ type RowValueType = TreeNodeFromImplicitAllowedTypes<TRow>;
429
+ type RowInsertableType = InsertableTreeNodeFromImplicitAllowedTypes<TRow>;
430
+
431
+ /**
432
+ * {@link Table} fields.
433
+ * @remarks Extracted for re-use in returned type signature defined later in this function.
434
+ * The implicit typing is intentional.
435
+ */
436
+ const tableFields = {
437
+ rows: schemaFactory.array("Table.rows", rowSchema),
438
+ columns: schemaFactory.array("Table.columns", columnSchema),
439
+ } as const satisfies Record<string, ImplicitFieldSchema>;
440
+
441
+ /**
442
+ * The Table schema
443
+ */
444
+ class Table
445
+ extends schemaFactory.object("Table", tableFields)
446
+ implements ITable<TCell, TColumn, TRow>
447
+ {
448
+ public getColumn(id: string): ColumnValueType | undefined {
449
+ // TypeScript is unable to narrow the types correctly here, hence the casts.
450
+ // See: https://github.com/microsoft/TypeScript/issues/52144
451
+ return this.columns.find((column) => (column as ColumnValueType).id === id) as
452
+ | ColumnValueType
453
+ | undefined;
454
+ }
455
+
456
+ public getRow(id: string): RowValueType | undefined {
457
+ // TypeScript is unable to narrow the types correctly here, hence the casts.
458
+ // See: https://github.com/microsoft/TypeScript/issues/52144
459
+ return this.rows.find((_row) => (_row as RowValueType).id === id) as
460
+ | RowValueType
461
+ | undefined;
462
+ }
463
+
464
+ public getCell(key: CellKey): CellValueType | undefined {
465
+ const { columnId, rowId } = key;
466
+ const row = this.getRow(rowId);
467
+ if (row !== undefined) {
468
+ const column = this.getColumn(columnId);
469
+ if (column !== undefined) {
470
+ return row.getCell(column);
471
+ }
472
+ }
473
+ // If the cell does not exist return undefined
474
+ return undefined;
475
+ }
476
+
477
+ public insertColumn({
478
+ column,
479
+ index,
480
+ }: InsertColumnParameters<ColumnInsertableType>): ColumnValueType {
481
+ if (index === undefined) {
482
+ // TypeScript is unable to narrow the types correctly here, hence the cast.
483
+ // See: https://github.com/microsoft/TypeScript/issues/52144
484
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
485
+ this.columns.insertAtEnd(column as any);
486
+ } else {
487
+ // TypeScript is unable to narrow the types correctly here, hence the cast.
488
+ // See: https://github.com/microsoft/TypeScript/issues/52144
489
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
490
+ this.columns.insertAt(index, column as any);
491
+ }
492
+
493
+ // Inserting the input node into the tree hydrates it, making it usable as a node.
494
+ return column as ColumnValueType;
495
+ }
496
+
497
+ public insertRows({
498
+ index,
499
+ rows,
500
+ }: InsertRowsParameters<RowInsertableType>): RowValueType[] {
501
+ if (index === undefined) {
502
+ // TypeScript is unable to narrow the types correctly here, hence the cast.
503
+ // See: https://github.com/microsoft/TypeScript/issues/52144
504
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
505
+ this.rows.insertAtEnd(TreeArrayNode.spread(rows) as any);
506
+ } else {
507
+ // TypeScript is unable to narrow the types correctly here, hence the cast.
508
+ // See: https://github.com/microsoft/TypeScript/issues/52144
509
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
510
+ this.rows.insertAt(index, TreeArrayNode.spread(rows) as any);
511
+ }
512
+
513
+ // Inserting the input nodes into the tree hydrates them, making them usable as nodes.
514
+ return rows as unknown as RowValueType[];
515
+ }
516
+
517
+ public setCell({ key, cell }: SetCellParameters<CellInsertableType>): void {
518
+ const { columnId, rowId } = key;
519
+ const row = this.getRow(rowId);
520
+ if (row !== undefined) {
521
+ const column = this.getColumn(columnId);
522
+ if (column !== undefined) {
523
+ row.setCell(column, cell);
524
+ }
525
+ }
526
+ }
527
+
528
+ public removeColumn(column: ColumnValueType): void {
529
+ const index = this.columns.indexOf(column);
530
+ // If the column is not in the table, do nothing
531
+ if (index === -1) return;
532
+ this.columns.removeAt(index);
533
+ }
534
+
535
+ public deleteRows(rows: readonly RowValueType[]): void {
536
+ // If there are no rows to delete, do nothing
537
+ if (rows.length === 0) {
538
+ return;
539
+ }
540
+
541
+ // If there is only one row to delete, delete it
542
+ if (rows.length === 1) {
543
+ const index = this.rows.indexOf(rows[0] ?? oob());
544
+ this.rows.removeAt(index);
545
+ return;
546
+ }
547
+ // If there are multiple rows to delete, delete them in a transaction
548
+ // This is to avoid the performance issues of deleting multiple rows at once
549
+ Tree.runTransaction(this, () => {
550
+ // Iterate over the rows and delete them
551
+ for (const row of rows) {
552
+ const index = this.rows.indexOf(row);
553
+ this.rows.removeAt(index);
554
+ }
555
+ });
556
+ }
557
+
558
+ public deleteAllRows(): void {
559
+ this.rows.removeRange();
560
+ }
561
+
562
+ public deleteCell(key: CellKey): void {
563
+ const { columnId, rowId } = key;
564
+ const row = this.getRow(rowId);
565
+ if (row !== undefined) {
566
+ const column = this.getColumn(columnId);
567
+ if (column !== undefined) {
568
+ row.deleteCell(column);
569
+ }
570
+ }
571
+ }
572
+ }
573
+
574
+ type TableValueType = TreeNode &
575
+ ITable<TCell, TColumn, TRow> &
576
+ WithType<ScopedSchemaName<Scope, "Table">>;
577
+ type TableInsertableType = InsertableObjectFromSchemaRecord<typeof tableFields>;
578
+
579
+ // Returning SingletonSchema without a type conversion results in TypeScript generating something like `readonly "__#124291@#brand": unknown;`
580
+ // for the private brand field of TreeNode.
581
+ // This numeric id doesn't seem to be stable over incremental builds, and thus causes diffs in the API extractor reports.
582
+ // This is avoided by doing this type conversion.
583
+ // The conversion is done via assignment instead of `as` to get stronger type safety.
584
+ const TableSchemaType: TreeNodeSchemaClass<
585
+ /* Name */ ScopedSchemaName<Scope, "Table">,
586
+ /* Kind */ NodeKind.Object,
587
+ /* TNode */ TableValueType,
588
+ /* TInsertable */ object & TableInsertableType,
589
+ /* ImplicitlyConstructable */ true,
590
+ /* Info */ typeof tableFields
591
+ > = Table;
592
+
593
+ // Return the table schema
594
+ return TableSchemaType;
595
+ }
596
+
597
+ /**
598
+ * Base row schema type.
599
+ * @sealed @system @internal
600
+ */
601
+ export type TableSchemaBase<
602
+ TScope extends string | undefined,
603
+ TCell extends ImplicitAllowedTypes,
604
+ TColumn extends ColumnSchemaBase<TScope> = ColumnSchemaBase<TScope>,
605
+ TRow extends RowSchemaBase<TScope, TCell, TColumn> = RowSchemaBase<TScope, TCell, TColumn>,
606
+ > = ReturnType<typeof createTable<TScope, TCell, TColumn, TRow>>;
607
+
608
+ // #endregion
609
+ }
@@ -17,6 +17,7 @@ import {
17
17
 
18
18
  import {
19
19
  SharedTree as SharedTreeImpl,
20
+ type ISharedTree,
20
21
  type SharedTreeOptions,
21
22
  type SharedTreeOptionsInternal,
22
23
  } from "./shared-tree/index.js";
@@ -27,7 +28,7 @@ import { SharedTreeFactoryType, SharedTreeAttributes } from "./sharedTreeAttribu
27
28
  /**
28
29
  * A channel factory that creates an {@link ITree}.
29
30
  */
30
- export class TreeFactory implements IChannelFactory<ITree> {
31
+ export class TreeFactory implements IChannelFactory<ISharedTree> {
31
32
  public static Type: string = SharedTreeFactoryType;
32
33
  public readonly type: string = SharedTreeFactoryType;
33
34
 
@@ -40,13 +41,13 @@ export class TreeFactory implements IChannelFactory<ITree> {
40
41
  id: string,
41
42
  services: IChannelServices,
42
43
  channelAttributes: Readonly<IChannelAttributes>,
43
- ): Promise<SharedTreeImpl> {
44
+ ): Promise<ISharedTree> {
44
45
  const tree = new SharedTreeImpl(id, runtime, channelAttributes, this.options);
45
46
  await tree.load(services);
46
47
  return tree;
47
48
  }
48
49
 
49
- public create(runtime: IFluidDataStoreRuntime, id: string): SharedTreeImpl {
50
+ public create(runtime: IFluidDataStoreRuntime, id: string): ISharedTree {
50
51
  const tree = new SharedTreeImpl(id, runtime, this.attributes, this.options);
51
52
  tree.initializeLocal();
52
53
  return tree;
@@ -183,17 +183,20 @@ export function breakingClass<Target extends abstract new (...args: any[]) => Wi
183
183
  target: Target,
184
184
  context: ClassDecoratorContext<Target>,
185
185
  ): Target {
186
- abstract class DecoratedBreakable extends target {}
186
+ // This could extend target, but doing so adds an extra step in the prototype chain and makes the instances just show up as "DecoratedBreakable" in the debugger.
187
+ const DecoratedBreakable = target;
187
188
 
188
- // Keep track of what keys we have seen,
189
- // since we visit most derived properties first and need to avoid wrapping base properties overriding more derived ones.
190
- const overriddenKeys: Set<string | symbol> = new Set();
189
+ // Keep track of what keys we have seen (and already wrapped if needed).
190
+ // Used to avoid rewrapping already wrapped properties.
191
+ // Preloaded with "constructor" to avoid wrapping the constructor as there is no need to set the broken flag when the constructor throws and does not return an object.
192
+ // Avoiding wrapping the constructor also avoids messing up the displayed name in the debugger.
193
+ const doNotWrap: Set<string | symbol> = new Set(["constructor"]);
191
194
 
192
195
  let prototype: object | null = target.prototype;
193
196
  while (prototype !== null) {
194
197
  for (const key of Reflect.ownKeys(prototype)) {
195
- if (!overriddenKeys.has(key)) {
196
- overriddenKeys.add(key);
198
+ if (!doNotWrap.has(key)) {
199
+ doNotWrap.add(key);
197
200
  const descriptor = Reflect.getOwnPropertyDescriptor(prototype, key);
198
201
  if (descriptor !== undefined) {
199
202
  // Method