@likec4/core 1.18.0 → 1.19.1

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 (177) hide show
  1. package/LICENSE +1 -1
  2. package/dist/builder/index.d.mts +502 -0
  3. package/dist/builder/index.d.ts +502 -0
  4. package/dist/builder/index.js +871 -0
  5. package/dist/compute-view/index.d.mts +9 -78
  6. package/dist/compute-view/index.d.ts +9 -78
  7. package/dist/compute-view/index.js +78 -0
  8. package/dist/index.d.mts +9 -730
  9. package/dist/index.d.ts +9 -730
  10. package/dist/{index.mjs → index.js} +7 -784
  11. package/dist/model/index.d.mts +218 -606
  12. package/dist/model/index.d.ts +218 -606
  13. package/dist/model/index.js +1 -0
  14. package/dist/shared/chunk-RAAYCPUM.M-JWF7SS.js +21 -0
  15. package/dist/shared/core.BIfgabEw.d.ts +719 -0
  16. package/dist/shared/core.BZ9Msq7w.d.mts +719 -0
  17. package/dist/shared/{core.DMYY7C8s.d.mts → core.C05RDLhi.d.ts} +179 -230
  18. package/dist/shared/{core.DMYY7C8s.d.ts → core.CmYMqgha.d.mts} +179 -230
  19. package/dist/shared/core.D6-fWbM3.d.ts +127 -0
  20. package/dist/shared/core.D74xkKkG.d.mts +148 -0
  21. package/dist/shared/core.D74xkKkG.d.ts +148 -0
  22. package/dist/shared/core.sLaWHBjR.d.mts +127 -0
  23. package/dist/shared/index.DafVOuVd.js +7443 -0
  24. package/dist/shared/object_hash.CE_oF3I3.js +1222 -0
  25. package/dist/shared/view.CyZrG8BJ.js +354 -0
  26. package/dist/types/index.d.mts +4 -2
  27. package/dist/types/index.d.ts +4 -2
  28. package/dist/types/index.js +20 -0
  29. package/dist/utils/index.d.mts +419 -0
  30. package/dist/utils/index.d.ts +419 -0
  31. package/dist/utils/index.js +347 -0
  32. package/package.json +47 -33
  33. package/src/builder/Builder-style1.test-d.ts +190 -0
  34. package/src/builder/Builder-style2.test-d.ts +175 -0
  35. package/src/builder/Builder.deployment.ts +25 -20
  36. package/src/builder/Builder.deploymentModel.ts +80 -55
  37. package/src/builder/Builder.element.ts +2 -0
  38. package/src/builder/Builder.model.ts +57 -43
  39. package/src/builder/Builder.ts +412 -431
  40. package/src/builder/Builder.view-common.ts +234 -0
  41. package/src/builder/Builder.view-deployment.ts +135 -0
  42. package/src/builder/Builder.view-element.ts +189 -0
  43. package/src/builder/Builder.views.ts +109 -33
  44. package/src/builder/Builder.with.ts +475 -0
  45. package/src/builder/__snapshots__/Builder-style1.spec.ts.snap +374 -0
  46. package/src/builder/__snapshots__/Builder-style2.spec.ts.snap +183 -0
  47. package/src/builder/__snapshots__/Builder.spec.ts.snap +374 -0
  48. package/src/builder/_types.ts +28 -5
  49. package/src/builder/index.ts +10 -4
  50. package/src/colors/index.ts +2 -2
  51. package/src/compute-view/compute-view.ts +45 -54
  52. package/src/compute-view/deployment-view/__test__/TestHelper.ts +236 -0
  53. package/src/compute-view/deployment-view/__test__/fixture.ts +50 -62
  54. package/src/compute-view/deployment-view/_types.ts +26 -18
  55. package/src/compute-view/deployment-view/clean-connections.ts +173 -164
  56. package/src/compute-view/deployment-view/compute.ts +51 -37
  57. package/src/compute-view/deployment-view/memory/index.ts +9 -0
  58. package/src/compute-view/deployment-view/memory/memory.ts +97 -0
  59. package/src/compute-view/deployment-view/predicates/elements.ts +144 -0
  60. package/src/compute-view/deployment-view/predicates/relation-direct.ts +183 -76
  61. package/src/compute-view/deployment-view/predicates/relation-in-out.ts +38 -20
  62. package/src/compute-view/deployment-view/predicates/relation-incoming.ts +72 -23
  63. package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +61 -22
  64. package/src/compute-view/deployment-view/predicates/wildcard.ts +15 -15
  65. package/src/compute-view/deployment-view/stages/stage-exclude.ts +5 -0
  66. package/src/compute-view/deployment-view/stages/stage-final.ts +245 -0
  67. package/src/compute-view/deployment-view/stages/stage-include.ts +95 -0
  68. package/src/compute-view/deployment-view/utils.ts +95 -31
  69. package/src/compute-view/dynamic-view/__test__/fixture.ts +4 -4
  70. package/src/compute-view/dynamic-view/compute.ts +59 -45
  71. package/src/compute-view/element-view/__test__/TestHelper.ts +258 -0
  72. package/src/compute-view/element-view/__test__/__snapshots__/legacy.spec.ts.snap +64 -62
  73. package/src/compute-view/element-view/__test__/fixture.ts +147 -117
  74. package/src/compute-view/element-view/_types.ts +48 -0
  75. package/src/compute-view/element-view/clean-connections.ts +113 -0
  76. package/src/compute-view/element-view/compute.ts +264 -683
  77. package/src/compute-view/element-view/memory/NodeGroup.ts +31 -0
  78. package/src/compute-view/element-view/memory/index.ts +10 -0
  79. package/src/compute-view/element-view/memory/memory.ts +177 -0
  80. package/src/compute-view/element-view/memory/stage-exclude.ts +59 -0
  81. package/src/compute-view/element-view/memory/stage-final.ts +129 -0
  82. package/src/compute-view/element-view/memory/stage-include.ts +128 -0
  83. package/src/compute-view/element-view/predicates/_utils.ts +86 -0
  84. package/src/compute-view/element-view/predicates/element-expand.ts +33 -0
  85. package/src/compute-view/element-view/predicates/element-kind-tag.ts +26 -0
  86. package/src/compute-view/element-view/predicates/element-ref.ts +37 -0
  87. package/src/compute-view/element-view/predicates/relation-direct.ts +309 -0
  88. package/src/compute-view/element-view/predicates/relation-in-out.ts +66 -0
  89. package/src/compute-view/element-view/predicates/relation-in.ts +118 -0
  90. package/src/compute-view/element-view/predicates/relation-out.ts +116 -0
  91. package/src/compute-view/element-view/predicates/wildcard.ts +88 -0
  92. package/src/compute-view/element-view/utils.ts +59 -0
  93. package/src/compute-view/index.ts +3 -4
  94. package/src/compute-view/memory/AbstractMemory.ts +45 -0
  95. package/src/compute-view/memory/AbstractStageExclude.ts +216 -0
  96. package/src/compute-view/memory/AbstractStageInclude.ts +200 -0
  97. package/src/compute-view/memory/_types.ts +147 -0
  98. package/src/compute-view/memory/index.ts +5 -0
  99. package/src/compute-view/memory/ops.ts +43 -0
  100. package/src/compute-view/utils/buildComputedNodes.ts +4 -4
  101. package/src/compute-view/utils/elementExpressionToPredicate.ts +1 -3
  102. package/src/compute-view/utils/link-nodes-with-edges.ts +3 -7
  103. package/src/compute-view/utils/{derive-edge-props-from-relationships.ts → merge-props-from-relationships.ts} +54 -23
  104. package/src/compute-view/utils/relationExpressionToPredicates.ts +2 -2
  105. package/src/compute-view/utils/resolve-extended-views.ts +44 -40
  106. package/src/compute-view/utils/resolve-global-rules.ts +6 -6
  107. package/src/compute-view/utils/topological-sort.ts +167 -71
  108. package/src/compute-view/utils/with-readable-edges.ts +14 -9
  109. package/src/index.ts +22 -16
  110. package/src/model/DeploymentElementModel.ts +150 -85
  111. package/src/model/DeploymentModel.ts +27 -21
  112. package/src/model/ElementModel.ts +51 -25
  113. package/src/model/LikeC4Model.test-d.ts +4 -3
  114. package/src/model/LikeC4Model.ts +98 -42
  115. package/src/model/RelationModel.ts +5 -4
  116. package/src/model/__test__/fixture.ts +5 -9
  117. package/src/model/connection/Connection.ts +72 -0
  118. package/src/model/connection/deployment/DeploymentConnectionModel.ts +178 -0
  119. package/src/model/connection/{deployment.ts → deployment/find.ts} +14 -18
  120. package/src/model/connection/deployment/index.ts +8 -0
  121. package/src/model/connection/index.ts +22 -0
  122. package/src/model/connection/model/ConnectionModel.ts +157 -0
  123. package/src/model/connection/{model.ts → model/find.ts} +43 -49
  124. package/src/model/connection/model/index.ts +8 -0
  125. package/src/model/connection/ops.ts +274 -0
  126. package/src/model/guards.ts +9 -0
  127. package/src/model/index.ts +45 -3
  128. package/src/model/types.ts +35 -18
  129. package/src/model/view/EdgeModel.ts +6 -6
  130. package/src/model/view/LikeC4ViewModel.ts +14 -8
  131. package/src/model/view/NodeModel.ts +6 -5
  132. package/src/types/_common.ts +53 -1
  133. package/src/types/deployments.ts +88 -156
  134. package/src/types/element.ts +2 -2
  135. package/src/types/expression-v2.ts +172 -0
  136. package/src/types/expression.ts +9 -5
  137. package/src/types/global.ts +3 -3
  138. package/src/types/index.ts +220 -13
  139. package/src/types/model.ts +28 -24
  140. package/src/types/operators.ts +1 -1
  141. package/src/types/relation.ts +14 -10
  142. package/src/types/theme.ts +0 -1
  143. package/src/types/view.ts +20 -18
  144. package/src/utils/const.ts +1 -0
  145. package/src/utils/fqn.ts +140 -49
  146. package/src/utils/getOrCreate.ts +1 -1
  147. package/src/utils/index.ts +75 -9
  148. package/src/utils/iterable/_types.ts +5 -0
  149. package/src/utils/iterable/filter.ts +55 -0
  150. package/src/utils/iterable/find.ts +36 -0
  151. package/src/utils/iterable/flat.ts +17 -0
  152. package/src/utils/iterable/index.ts +13 -0
  153. package/src/utils/iterable/map.ts +40 -0
  154. package/src/utils/iterable/reduce.ts +24 -0
  155. package/src/utils/iterable/some.ts +36 -0
  156. package/src/utils/iterable/to.ts +19 -0
  157. package/src/utils/iterable/unique.ts +29 -0
  158. package/src/utils/relations.ts +3 -64
  159. package/src/utils/set.ts +11 -9
  160. package/src/utils/string-hash.ts +1 -1
  161. package/dist/compute-view/index.mjs +0 -79
  162. package/dist/model/index.mjs +0 -2
  163. package/dist/shared/core.BBgiAAv1.mjs +0 -144
  164. package/dist/shared/core.BPyJvuMU.mjs +0 -2420
  165. package/dist/shared/core.BQ4AbfvB.mjs +0 -9228
  166. package/dist/shared/core.k12s_keD.mjs +0 -203
  167. package/dist/types/index.mjs +0 -21
  168. package/src/builder/Builder.test-d.ts +0 -124
  169. package/src/builder/Builder.view.ts +0 -543
  170. package/src/compute-view/LikeC4ModelGraph.ts +0 -330
  171. package/src/compute-view/deployment-view/Memory.ts +0 -108
  172. package/src/compute-view/deployment-view/Stage.ts +0 -145
  173. package/src/compute-view/deployment-view/predicates/deployment-ref.ts +0 -146
  174. package/src/compute-view/element-view/predicates.ts +0 -517
  175. package/src/compute-view/utils/sortNodes.ts +0 -101
  176. package/src/model/connection/ConnectionModel.ts +0 -94
  177. package/src/model/connection/DeploymentConnectionModel.ts +0 -99
package/src/utils/fqn.ts CHANGED
@@ -1,8 +1,8 @@
1
- import type { Element, Fqn } from '../types'
1
+ import type { Fqn } from '../types'
2
2
  import { compareNatural } from './compare-natural'
3
3
  import { isString } from './guards'
4
4
 
5
- type Predicate<T> = (x: T) => boolean
5
+ export type Predicate<T> = (x: T) => boolean
6
6
 
7
7
  export function parentFqn<E extends string>(fqn: E): E | null {
8
8
  const lastDot = fqn.lastIndexOf('.')
@@ -17,7 +17,7 @@ export function parentFqnPredicate<T extends { parent: Fqn | null }>(parent: Fqn
17
17
  return (e: T) => !!e.parent && (e.parent === parent || e.parent.startsWith(prefix))
18
18
  }
19
19
 
20
- export function nameFromFqn<E extends string>(fqn: E) {
20
+ export function nameFromFqn<E extends string>(fqn: E): string {
21
21
  const lastDot = fqn.lastIndexOf('.')
22
22
  if (lastDot > 0) {
23
23
  return fqn.slice(lastDot + 1)
@@ -26,35 +26,66 @@ export function nameFromFqn<E extends string>(fqn: E) {
26
26
  }
27
27
  }
28
28
 
29
- export function isAncestor<E extends { id: string }>(
30
- ...args: [ancestor: string, another: string] | [ancestor: E, another: E]
29
+ const asString: <E extends string | { id: string }>(e: E) => string = e => (isString(e) ? e : e.id)
30
+
31
+ /**
32
+ * Check if one element is an ancestor of another
33
+ * Composable version
34
+ * @signature
35
+ * isAncestor(another)(ancestor)
36
+ */
37
+ export function isAncestor<A extends string | { id: string }>(another: NoInfer<A>): (ancestor: A) => boolean
38
+ /**
39
+ * Check if one element is an ancestor of another
40
+ * @signature
41
+ * isAncestor(ancestor, another)
42
+ */
43
+ export function isAncestor<A extends string | { id: string }>(ancestor: A, another: A): boolean
44
+
45
+ export function isAncestor<T extends string>(
46
+ arg1: T | WithId<T>,
47
+ arg2?: NoInfer<T> | WithId<NoInfer<T>>,
31
48
  ) {
32
- const ancestor = isString(args[0]) ? args[0] : args[0].id
33
- const another = isString(args[1]) ? args[1] : args[1].id
34
- return another.startsWith(ancestor + '.')
49
+ const arg1Id = asString(arg1)
50
+ if (arg2) {
51
+ const arg2Id = asString(arg2)
52
+ return arg2Id.startsWith(arg1Id + '.')
53
+ }
54
+ return (ancestor: T | WithId<T>) => {
55
+ const ancestorId = asString(ancestor)
56
+ return arg1Id.startsWith(ancestorId + '.')
57
+ }
35
58
  }
36
59
 
37
- export function isSameHierarchy<E extends string | { id: Fqn }>(one: E, another: E) {
38
- const first = isString(one) ? one : one.id
39
- const second = isString(another) ? another : another.id
60
+ export function isSameHierarchy<T extends string>(
61
+ one: NoInfer<T> | WithId<NoInfer<T>>,
62
+ ): (another: T | WithId<T>) => boolean
63
+ export function isSameHierarchy<T extends string>(one: T | WithId<T>, another: T | WithId<T>): boolean
64
+ export function isSameHierarchy<T extends string>(one: T | WithId<T>, another?: T | WithId<T>) {
65
+ if (!another) {
66
+ return (b: T | WithId<T>) => isSameHierarchy(one, b)
67
+ }
68
+ const first = asString(one)
69
+ const second = asString(another)
40
70
  return first === second || second.startsWith(first + '.') || first.startsWith(second + '.')
41
71
  }
42
72
 
43
- export function isDescendantOf<E extends { id: Fqn }>(ancestors: E[]): (e: E) => boolean {
44
- const predicates = ancestors.flatMap(a => [(e: E) => e.id === a.id, (e: E) => isAncestor(a, e)])
45
- return (e: E) => predicates.some(p => p(e))
46
- }
73
+ type WithId<T> = { id: T }
47
74
 
48
- export function notDescendantOf(ancestors: Element[]): (e: Element) => boolean {
49
- const isDescendant = isDescendantOf(ancestors)
50
- return (e: Element) => !isDescendant(e)
75
+ export function isDescendantOf<T extends string>(ancestor: WithId<T>): (descedant: WithId<T>) => boolean
76
+ export function isDescendantOf<T extends string>(descedant: WithId<T>, ancestor: WithId<T>): boolean
77
+ export function isDescendantOf<T extends string>(descedant: WithId<T>, ancestor?: WithId<T>) {
78
+ if (!ancestor) {
79
+ return (d: WithId<T>) => isAncestor(descedant, d)
80
+ }
81
+ return isAncestor(ancestor, descedant)
51
82
  }
52
83
 
53
84
  /**
54
85
  * How deep in the hierarchy the element is.
55
86
  * Root element has depth 1
56
87
  */
57
- export function hierarchyDepth<E extends string | { id: Fqn }>(elementOfFqn: E): number {
88
+ export function hierarchyLevel<E extends string | { id: Fqn }>(elementOfFqn: E): number {
58
89
  const first = isString(elementOfFqn) ? elementOfFqn : elementOfFqn.id
59
90
  return first.split('.').length
60
91
  }
@@ -66,7 +97,7 @@ export function hierarchyDepth<E extends string | { id: Fqn }>(elementOfFqn: E):
66
97
  *
67
98
  * Can be used for hierarchical clustering
68
99
  */
69
- export function hierarchyDistance<E extends string | { id: Fqn }>(one: E, another: E) {
100
+ export function hierarchyDistance<E extends string | { id: Fqn }>(one: E, another: E): number {
70
101
  const first = isString(one) ? one as Fqn : one.id
71
102
  const second = isString(another) ? another as Fqn : another.id
72
103
 
@@ -74,39 +105,36 @@ export function hierarchyDistance<E extends string | { id: Fqn }>(one: E, anothe
74
105
  return 0
75
106
  }
76
107
 
77
- const firstDepth = hierarchyDepth(first)
78
- const secondDepth = hierarchyDepth(second)
108
+ const firstDepth = hierarchyLevel(first)
109
+ const secondDepth = hierarchyLevel(second)
79
110
 
80
111
  if (isSameHierarchy(first, second)) {
81
112
  return Math.abs(firstDepth - secondDepth)
82
113
  }
83
114
 
84
115
  const ancestor = commonAncestor(first as Fqn, second as Fqn)
85
- const ancestorDepth = ancestor ? hierarchyDepth(ancestor) : 0
116
+ const ancestorDepth = ancestor ? hierarchyLevel(ancestor) : 0
86
117
 
87
118
  return firstDepth + secondDepth - (2 * ancestorDepth + 1)
88
119
  }
89
120
 
90
- export function commonAncestor<E extends string>(first: E, second: E) {
91
- const parentA = parentFqn(first)
92
- const parentB = parentFqn(second)
93
- if (parentA === parentB) {
94
- return parentA
95
- }
96
- if (!parentA || !parentB) {
121
+ export function commonAncestor<E extends string>(first: E, second: E): E | null {
122
+ const a = first.split('.')
123
+ if (a.length < 2) {
97
124
  return null
98
125
  }
99
-
100
- const a = first.split('.')
101
126
  const b = second.split('.')
102
- let ancestor: E | null = null
103
-
104
- while (a.length > 1 && b.length > 1 && !!a[0] && a[0] === b[0]) {
105
- ancestor = (ancestor ? `${ancestor}.${a[0]}` : a[0]) as E
106
- a.shift()
107
- b.shift()
127
+ if (b.length < 2) {
128
+ return null
129
+ }
130
+ let ancestor = [] as string[]
131
+ for (let i = 0; i < Math.min(a.length, b.length) - 1 && a[i] === b[i]; i++) {
132
+ ancestor.push(a[i]!)
133
+ }
134
+ if (ancestor.length === 0) {
135
+ return null
108
136
  }
109
- return ancestor
137
+ return ancestor.join('.') as E
110
138
  }
111
139
 
112
140
  /**
@@ -155,7 +183,7 @@ export function compareFqnHierarchically<T extends string = string>(a: T, b: T):
155
183
  }
156
184
  }
157
185
 
158
- export function compareByFqnHierarchically<T extends { id: string }>(a: T, b: T) {
186
+ export function compareByFqnHierarchically<T extends { id: string }>(a: T, b: T): -1 | 0 | 1 {
159
187
  return compareFqnHierarchically(a.id, b.id)
160
188
  }
161
189
 
@@ -164,8 +192,33 @@ export type ReorderedArray<T extends IterableContainer> = {
164
192
  -readonly [P in keyof T]: T[number]
165
193
  }
166
194
 
195
+ /**
196
+ * Sorts an array of objects hierarchically based on their fully qualified names (FQN).
197
+ * Objects are sorted by the number of segments in their FQN (defined by dot-separated ID).
198
+ *
199
+ * @typeParam T - Object type that contains an 'id' string property
200
+ * @typeParam A - Type extending IterableContainer of T
201
+ *
202
+ * @param array - Array of objects to be sorted
203
+ * @returns A new array with items sorted by their FQN hierarchy depth (number of segments)
204
+ *
205
+ * @example
206
+ * ```ts
207
+ * const items = [
208
+ * { id: "a.b.c" },
209
+ * { id: "a" },
210
+ * { id: "a.b" }
211
+ * ];
212
+ * sortByFqnHierarchically(items);
213
+ * // Result: [
214
+ * // { id: "a" },
215
+ * // { id: "a.b" },
216
+ * // { id: "a.b.c" }
217
+ * // ]
218
+ * ```
219
+ */
167
220
  export function sortByFqnHierarchically<T extends { id: string }, A extends IterableContainer<T>>(
168
- array: A
221
+ array: A,
169
222
  ): ReorderedArray<A> {
170
223
  return array
171
224
  .map(item => ({ item, fqn: item.id.split('.') }))
@@ -175,34 +228,72 @@ export function sortByFqnHierarchically<T extends { id: string }, A extends Iter
175
228
  .map(({ item }) => item) as ReorderedArray<A>
176
229
  }
177
230
 
231
+ function findTopAncestor<T extends { id: string }>(items: T[], item: T): T | null {
232
+ let parent = item
233
+ for (const e of items) {
234
+ if (isAncestor(e, parent)) {
235
+ parent = e
236
+ }
237
+ }
238
+ return parent !== item ? parent : null
239
+ }
240
+
178
241
  /**
179
242
  * Keeps initial order of the elements, but ensures that parents are before children
180
243
  */
181
244
  export function sortParentsFirst<T extends { id: string }, A extends IterableContainer<T>>(
182
- array: A
245
+ array: A,
183
246
  ): ReorderedArray<A> {
184
247
  const result = [] as T[]
185
248
  const items = [...array]
186
249
  let item
187
250
  while ((item = items.shift())) {
188
- const itemId = item.id
189
- let parentIndx
190
- while ((parentIndx = items.findIndex(parent => isAncestor(parent.id, itemId))) !== -1) {
191
- result.push(...items.splice(parentIndx, 1))
251
+ let parent
252
+ while ((parent = findTopAncestor(items, item))) {
253
+ result.push(items.splice(items.indexOf(parent), 1)[0]!)
192
254
  }
193
255
  result.push(item)
194
256
  }
195
257
  return result as ReorderedArray<A>
196
258
  }
197
259
 
260
+ /**
261
+ * Sorts an array of objects naturally by their fully qualified name (FQN) identifier.
262
+ *
263
+ * @template T - Type of objects containing an 'id' string property
264
+ * @template A - Type extending IterableContainer of T
265
+ *
266
+ * @param first - Optional. Either the array to sort or the sort direction ('asc'|'desc')
267
+ * @param sort - Optional. The sort direction ('asc'|'desc'). Defaults to 'asc' if not specified
268
+ *
269
+ * @example
270
+ * // As a function that returns a sorting function
271
+ * const sorted = sortNaturalByFqn('desc')(myArray);
272
+ *
273
+ * // Direct array sorting
274
+ * const sorted = sortNaturalByFqn(myArray, 'desc');
275
+ */
276
+ export function sortNaturalByFqn(
277
+ sort?: 'asc' | 'desc',
278
+ ): <T extends { id: string }, A extends IterableContainer<T>>(array: A) => ReorderedArray<A>
198
279
  export function sortNaturalByFqn<T extends { id: string }, A extends IterableContainer<T>>(
199
- array: A
200
- ): ReorderedArray<A> {
280
+ array: A,
281
+ sort?: 'asc' | 'desc',
282
+ ): ReorderedArray<A>
283
+ export function sortNaturalByFqn<T extends { id: string }, A extends IterableContainer<T>>(
284
+ array?: A | 'asc' | 'desc',
285
+ sort?: 'asc' | 'desc',
286
+ ) {
287
+ if (!array || isString(array)) {
288
+ const dir = array ?? 'asc'
289
+ return (arr: A) => sortNaturalByFqn(arr, dir)
290
+ }
291
+ const dir = sort === 'desc' ? -1 : 1
201
292
  return array
202
293
  .map(item => ({ item, fqn: item.id.split('.') }))
203
294
  .sort((a, b) => {
204
295
  if (a.fqn.length !== b.fqn.length) {
205
- return a.fqn.length - b.fqn.length
296
+ return (a.fqn.length - b.fqn.length) * dir
206
297
  }
207
298
  for (let i = 0; i < a.fqn.length; i++) {
208
299
  const compare = compareNatural(a.fqn[i], b.fqn[i])
@@ -4,7 +4,7 @@
4
4
  // }
5
5
  // export function getOrCreate<K, V>(map: Map<K, V>, key: K, create: (key: K) => V): V
6
6
  // export function getOrCreate<K, V>(map: Map<K, V>, key?: K, create?: (key: K) => V) {
7
- export function getOrCreate<K, V>(map: Map<K, V>, key: K, create: (key: K) => V) {
7
+ export function getOrCreate<K, V>(map: Map<K, V>, key: K, create: (key: K) => V): V {
8
8
  let entry = map.get(key)
9
9
  if (!entry) {
10
10
  entry = create(key)
@@ -1,9 +1,75 @@
1
- export * from './common-head'
2
- export * from './compare-natural'
3
- export * from './fqn'
4
- export * from './getOrCreate'
5
- export * from './guards'
6
- export * from './promises'
7
- export * from './relations'
8
- export * from './set'
9
- export * from './string-hash'
1
+ import DefaultMap from 'mnemonist/default-map'
2
+ import LinkedList from 'mnemonist/linked-list'
3
+
4
+ export { DefaultMap, LinkedList }
5
+
6
+ export {
7
+ commonHead,
8
+ } from './common-head'
9
+
10
+ export {
11
+ compareNatural,
12
+ } from './compare-natural'
13
+ export {
14
+ ancestorsFqn,
15
+ commonAncestor,
16
+ compareByFqnHierarchically,
17
+ compareFqnHierarchically,
18
+ hierarchyDistance,
19
+ hierarchyLevel,
20
+ isAncestor,
21
+ isDescendantOf,
22
+ isSameHierarchy,
23
+ type IterableContainer,
24
+ nameFromFqn,
25
+ parentFqn,
26
+ type ReorderedArray,
27
+ sortByFqnHierarchically,
28
+ sortNaturalByFqn,
29
+ sortParentsFirst,
30
+ } from './fqn'
31
+
32
+ export {
33
+ getOrCreate,
34
+ } from './getOrCreate'
35
+
36
+ export {
37
+ hasAtLeast,
38
+ isNonEmptyArray,
39
+ isString,
40
+ } from './guards'
41
+ export {
42
+ ifilter,
43
+ ifind,
44
+ iflat,
45
+ imap,
46
+ ireduce,
47
+ isIterable,
48
+ isome,
49
+ iunique,
50
+ toArray,
51
+ toSet,
52
+ } from './iterable'
53
+ export {
54
+ delay,
55
+ } from './promises'
56
+
57
+ export {
58
+ compareRelations,
59
+ } from './relations'
60
+
61
+ export {
62
+ difference,
63
+ equals as equalsSet,
64
+ intersection,
65
+ symmetricDifference,
66
+ union,
67
+ } from './set'
68
+
69
+ export {
70
+ stringHash,
71
+ } from './string-hash'
72
+
73
+ export {
74
+ objectHash,
75
+ } from './object-hash'
@@ -0,0 +1,5 @@
1
+ import { isNonNullish } from 'remeda'
2
+
3
+ export function isIterable<T, I extends Iterable<T>>(something: I | unknown): something is Iterable<T> {
4
+ return isNonNullish(something) && typeof something === 'object' && Symbol.iterator in something
5
+ }
@@ -0,0 +1,55 @@
1
+ import { isFunction } from 'remeda'
2
+ import { invariant } from '../../errors'
3
+ import type { IteratorLike } from '../../types'
4
+
5
+ /**
6
+ * Filters an iterable based on a predicate.
7
+ * Composabel first version of `ifilter`.
8
+ * @signature
9
+ * ifilter(predicate)(data)
10
+ */
11
+ export function ifilter<T, S extends T>(
12
+ predicate: (v: T) => v is S,
13
+ ): (iterable: Iterable<T>) => IteratorLike<S>
14
+ export function ifilter<T>(
15
+ predicate: (v: T) => boolean,
16
+ ): (iterable: Iterable<T>) => IteratorLike<T>
17
+
18
+ /**
19
+ * Filters an iterable based on a predicate.
20
+ * Data first version of `ifilter`.
21
+ * @signature
22
+ * ifilter(data, predicate)
23
+ * @example
24
+ * ifilter(new Set([1, 2, 3]), x => x % 2 === 1) // => Iterable<[1, 3]>
25
+ */
26
+ export function ifilter<T, S extends T>(
27
+ iterable: Iterable<T>,
28
+ predicate: (v: T) => v is S,
29
+ ): IteratorLike<S>
30
+ export function ifilter<T>(
31
+ iterable: Iterable<T>,
32
+ predicate: (v: T) => boolean,
33
+ ): IteratorLike<T>
34
+
35
+ export function ifilter(
36
+ arg1: unknown,
37
+ arg2?: unknown,
38
+ ): IteratorLike<unknown> | ((it: Iterable<unknown>) => IteratorLike<unknown>) {
39
+ const pred = (arg2 ?? arg1) as (v: any) => boolean
40
+ invariant(isFunction(pred))
41
+
42
+ function* _filter(iter: Iterable<unknown>): IteratorLike<unknown> {
43
+ for (const value of iter) {
44
+ if (pred(value)) {
45
+ yield value
46
+ }
47
+ }
48
+ return
49
+ }
50
+
51
+ if (!arg2) {
52
+ return _filter
53
+ }
54
+ return _filter(arg1 as Iterable<unknown>)
55
+ }
@@ -0,0 +1,36 @@
1
+ import { isFunction } from 'remeda'
2
+ import { invariant } from '../../errors'
3
+
4
+ /**
5
+ * Finds the first element in the iterable that satisfies the predicate.
6
+ * Composable first version of `find`.
7
+ * @signature
8
+ * ifind(predicate)(data)
9
+ */
10
+ export function ifind<T>(predicate: (item: T) => boolean): (iterable: Iterable<T>) => T | undefined
11
+ /**
12
+ * Finds the first element in the iterable that satisfies the predicate.
13
+ * Data first version of `find`.
14
+ * @signature
15
+ * ifind(data, predicate)
16
+ */
17
+ export function ifind<T>(iterable: Iterable<T>, predicate: (item: T) => boolean): T | undefined
18
+
19
+ export function ifind(arg1: unknown, arg2?: unknown) {
20
+ const pred = (arg2 ?? arg1) as (v: any) => boolean
21
+ invariant(isFunction(pred))
22
+
23
+ function _find(iter: Iterable<unknown>) {
24
+ for (const value of iter) {
25
+ if (pred(value)) {
26
+ return value
27
+ }
28
+ }
29
+ return
30
+ }
31
+
32
+ if (!arg2) {
33
+ return _find
34
+ }
35
+ return _find(arg1 as Iterable<unknown>)
36
+ }
@@ -0,0 +1,17 @@
1
+ import type { IteratorLike } from '../../types'
2
+
3
+ export function iflat(): <T>(iterable: Iterable<IteratorLike<T>>) => IteratorLike<T>
4
+ export function iflat<T>(iterable: Iterable<IteratorLike<T>>): IteratorLike<T>
5
+
6
+ export function iflat<T>(
7
+ iterable?: Iterable<IteratorLike<T>>,
8
+ ): IteratorLike<T> | ((iterable: Iterable<IteratorLike<T>>) => IteratorLike<T>) {
9
+ return iterable ? _iflat(iterable) : _iflat
10
+ }
11
+
12
+ function* _iflat<T>(iterable: Iterable<IteratorLike<T>>): IteratorLike<T> {
13
+ for (const inner of iterable) {
14
+ yield* inner
15
+ }
16
+ return
17
+ }
@@ -0,0 +1,13 @@
1
+ export { isIterable } from './_types'
2
+
3
+ /**
4
+ * operators
5
+ */
6
+ export { ifilter } from './filter'
7
+ export { ifind } from './find'
8
+ export { iflat } from './flat'
9
+ export { imap } from './map'
10
+ export { ireduce } from './reduce'
11
+ export { isome } from './some'
12
+ export { toArray, toSet } from './to'
13
+ export { iunique } from './unique'
@@ -0,0 +1,40 @@
1
+ import { isFunction } from 'remeda'
2
+ import { invariant } from '../../errors'
3
+ import type { IteratorLike } from '../../types'
4
+
5
+ type MapFunction<T, S> = (item: T) => S
6
+
7
+ /**
8
+ * Maps an iterable using a mapper function.
9
+ * Composable first version of `imap`.
10
+ * @signature
11
+ * imap(mapper)(data)
12
+ */
13
+ export function imap<T, S>(mapper: MapFunction<T, S>): (iterable: Iterable<T>) => IteratorLike<S>
14
+ /**
15
+ * Maps an iterable using a mapper function.
16
+ * Data first version of `imap`.
17
+ * @signature
18
+ * imap(data, mapper)
19
+ */
20
+ export function imap<T, S>(iterable: Iterable<T>, mapper: MapFunction<T, S>): IteratorLike<S>
21
+
22
+ export function imap(
23
+ arg1: unknown,
24
+ arg2?: unknown,
25
+ ): IteratorLike<unknown> | ((it: Iterable<unknown>) => IteratorLike<unknown>) {
26
+ const mapper = (arg2 ?? arg1) as MapFunction<any, any>
27
+ invariant(isFunction(mapper))
28
+
29
+ function* _map(iter: Iterable<unknown>): IteratorLike<unknown> {
30
+ for (const value of iter) {
31
+ yield mapper(value)
32
+ }
33
+ return
34
+ }
35
+
36
+ if (!arg2) {
37
+ return _map
38
+ }
39
+ return _map(arg1 as Iterable<unknown>)
40
+ }
@@ -0,0 +1,24 @@
1
+ import { isDefined, isFunction } from 'remeda'
2
+ import { invariant } from '../../errors'
3
+
4
+ export function ireduce<T, R>(reducer: (acc: R, item: T) => R, initialValue: R): (iterable: Iterable<T>) => R
5
+ export function ireduce<T, R>(iterable: Iterable<T>, reducer: (acc: R, item: T) => R, initialValue: R): R
6
+ export function ireduce<T, R>(
7
+ arg1: unknown,
8
+ arg2: unknown,
9
+ arg3?: unknown,
10
+ ): R | ((iterable: Iterable<T>) => R) {
11
+ const reducer = (isDefined(arg3) ? arg2 : arg1) as (acc: R, item: T) => R
12
+ const initialValue = arg3 ?? arg2
13
+ invariant(isFunction(reducer))
14
+
15
+ function _reduce(iter: Iterable<T>): R {
16
+ let acc = initialValue as R
17
+ for (const value of iter) {
18
+ acc = reducer(acc, value)
19
+ }
20
+ return acc
21
+ }
22
+
23
+ return isDefined(arg3) ? _reduce(arg1 as Iterable<T>) : _reduce
24
+ }
@@ -0,0 +1,36 @@
1
+ import { isFunction } from 'remeda'
2
+ import { invariant } from '../../errors'
3
+
4
+ /**
5
+ * Checks if at least one element in the iterable satisfies the predicate.
6
+ * Composable first version of `some`.
7
+ * @signature
8
+ * isome(predicate)(data)
9
+ */
10
+ export function isome<T>(predicate: (item: T) => boolean): (iterable: Iterable<T>) => boolean
11
+ /**
12
+ * Checks if at least one element in the iterable satisfies the predicate.
13
+ * Data first version of `some`.
14
+ * @signature
15
+ * isome(data, predicate)
16
+ */
17
+ export function isome<T>(iterable: Iterable<T>, predicate: (item: T) => boolean): boolean
18
+
19
+ export function isome<T>(arg1: unknown, arg2?: unknown): boolean | ((iterable: Iterable<T>) => boolean) {
20
+ const pred = (arg2 ?? arg1) as (v: any) => boolean
21
+ invariant(isFunction(pred))
22
+
23
+ function _some(iter: Iterable<unknown>): boolean {
24
+ for (const value of iter) {
25
+ if (pred(value)) {
26
+ return true
27
+ }
28
+ }
29
+ return false
30
+ }
31
+
32
+ if (!arg2) {
33
+ return _some
34
+ }
35
+ return _some(arg1 as Iterable<unknown>)
36
+ }
@@ -0,0 +1,19 @@
1
+ export function toArray(): <T>(iterable: Iterable<T> | ArrayLike<T>) => T[]
2
+ export function toArray<T>(iterable: Iterable<T> | ArrayLike<T>): T[]
3
+ export function toArray<T>(
4
+ iterable?: Iterable<T> | ArrayLike<T>
5
+ ): T[] | (<T>(iterable: Iterable<T> | ArrayLike<T>) => T[]) {
6
+ if (iterable) {
7
+ return Array.from(iterable)
8
+ }
9
+ return (it) => Array.from(it)
10
+ }
11
+
12
+ export function toSet(): <T>(iterable: Iterable<T>) => Set<T>
13
+ export function toSet<T>(iterable: Iterable<T>): Set<T>
14
+ export function toSet<T>(iterable?: Iterable<T>): Set<T> | ((iterable: Iterable<T>) => Set<T>) {
15
+ if (iterable) {
16
+ return new Set(iterable)
17
+ }
18
+ return (it) => new Set(it)
19
+ }
@@ -0,0 +1,29 @@
1
+ import type { IteratorLike } from '../../types'
2
+
3
+ /**
4
+ * Returns an iterable that yields only unique values.
5
+ * It uses a Set to keep track of the values.
6
+ */
7
+ export function iunique(): <T>(iterable: Iterable<T>) => IteratorLike<T>
8
+ export function iunique<T>(iterable: Iterable<T>): IteratorLike<T>
9
+
10
+ export function iunique<T>(iterable?: Iterable<T>): IteratorLike<T> | ((iterable: Iterable<T>) => IteratorLike<T>) {
11
+ if (iterable) {
12
+ return (function*(): IteratorLike<T> {
13
+ yield* _iunique(iterable)
14
+ return
15
+ })()
16
+ }
17
+ return _iunique
18
+ }
19
+
20
+ function* _iunique<T>(iterable: Iterable<T>): IteratorLike<T> {
21
+ const seen = new Set<T>()
22
+ for (const item of iterable) {
23
+ if (!seen.has(item)) {
24
+ seen.add(item)
25
+ yield item
26
+ }
27
+ }
28
+ return
29
+ }