@likec4/core 1.1.1 → 1.2.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 (56) hide show
  1. package/dist/colors/element.d.ts +1 -0
  2. package/dist/colors/index.d.ts +1 -0
  3. package/dist/colors/relationships.d.ts +1 -0
  4. package/dist/errors/errors.spec.d.ts +2 -0
  5. package/dist/errors/errors.spec.js +69 -0
  6. package/dist/errors/index.d.ts +1 -0
  7. package/dist/errors/index.js +7 -1
  8. package/dist/index.d.ts +1 -0
  9. package/dist/types/_common.d.ts +1 -0
  10. package/dist/types/element.d.ts +1 -0
  11. package/dist/types/expression.d.ts +1 -0
  12. package/dist/types/index.d.ts +1 -2
  13. package/dist/types/index.js +2 -3
  14. package/dist/types/model.d.ts +3 -3
  15. package/dist/types/opaque.d.ts +1 -0
  16. package/dist/types/relation.d.ts +1 -0
  17. package/dist/types/theme.d.ts +1 -0
  18. package/dist/types/view.d.ts +133 -8
  19. package/dist/types/view.js +31 -3
  20. package/dist/utils/fqn.d.ts +1 -0
  21. package/dist/utils/fqn.spec.d.ts +2 -0
  22. package/dist/utils/fqn.spec.js +82 -0
  23. package/dist/utils/guards.d.ts +1 -0
  24. package/dist/utils/index.d.ts +1 -0
  25. package/dist/utils/promises.d.ts +1 -0
  26. package/dist/utils/relations.d.ts +1 -0
  27. package/dist/utils/relations.spec.d.ts +2 -0
  28. package/dist/utils/relations.spec.js +210 -0
  29. package/package.json +6 -6
  30. package/src/colors/element.ts +80 -0
  31. package/src/colors/index.ts +12 -0
  32. package/src/colors/relationships.ts +54 -0
  33. package/src/errors/errors.spec.ts +88 -0
  34. package/src/errors/index.ts +120 -0
  35. package/src/index.ts +9 -0
  36. package/src/reset.d.ts +2 -0
  37. package/src/types/_common.ts +5 -0
  38. package/src/types/element.ts +56 -0
  39. package/src/types/expression.ts +140 -0
  40. package/src/types/index.ts +10 -0
  41. package/src/types/model.ts +15 -0
  42. package/src/types/opaque.ts +108 -0
  43. package/src/types/relation.ts +38 -0
  44. package/src/types/theme.ts +46 -0
  45. package/src/types/view.ts +263 -0
  46. package/src/utils/fqn.spec.ts +105 -0
  47. package/src/utils/fqn.ts +112 -0
  48. package/src/utils/guards.ts +10 -0
  49. package/src/utils/index.ts +4 -0
  50. package/src/utils/promises.ts +9 -0
  51. package/src/utils/relations.spec.ts +268 -0
  52. package/src/utils/relations.ts +104 -0
  53. package/dist/types/computed-view.d.ts +0 -52
  54. package/dist/types/computed-view.js +0 -1
  55. package/dist/types/diagram.d.ts +0 -40
  56. package/dist/types/diagram.js +0 -1
@@ -66,3 +66,4 @@ export declare const ElementColors: {
66
66
  readonly loContrast: "#c7d2fe";
67
67
  };
68
68
  };
69
+ //# sourceMappingURL=element.d.ts.map
@@ -130,3 +130,4 @@ export declare const defaultTheme: {
130
130
  shadow: "#0a0a0a";
131
131
  };
132
132
  export { ElementColors, RelationshipColors };
133
+ //# sourceMappingURL=index.d.ts.map
@@ -55,3 +55,4 @@ export declare const RelationshipColors: {
55
55
  labelColor: "#cbd5e1";
56
56
  };
57
57
  };
58
+ //# sourceMappingURL=relationships.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=errors.spec.d.ts.map
@@ -0,0 +1,69 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { BaseError, InvalidArgError, InvalidModelError, invariant, InvariantError, NonExhaustiveError, normalizeError, NullableError, RelationRefError, UnknownError } from './index';
3
+ describe('errors', () => {
4
+ describe('normalizeError', () => {
5
+ it.each([
6
+ { error: new BaseError('BaseError') },
7
+ { error: new RelationRefError('RelationRefError') },
8
+ { error: new UnknownError('UnknownError') },
9
+ { error: new InvariantError('InvariantError') },
10
+ { error: new NonExhaustiveError('NonExhaustiveError') },
11
+ { error: new InvalidModelError('InvalidModelError') },
12
+ { error: new NullableError('NullableError') },
13
+ { error: new InvalidArgError('InvalidArgError') }
14
+ ])('should return $error.name as-is', ({ error }) => {
15
+ expect(normalizeError(error)).toBe(error);
16
+ });
17
+ it.skip('should wrap an error', () => {
18
+ const cause = new Error('original test');
19
+ const error = normalizeError(cause);
20
+ expect(error).toBeInstanceOf(UnknownError);
21
+ expect(error).to.include({
22
+ name: 'UnknownError',
23
+ message: 'original test'
24
+ });
25
+ expect(error).to.have.property('cause').that.eq(cause);
26
+ expect(error)
27
+ .to.have.property('stack')
28
+ .that.is.a('string')
29
+ .and.includes('UnknownError: original test');
30
+ });
31
+ it('should wrap a string', () => {
32
+ const cause = 'error string';
33
+ const error = normalizeError(cause);
34
+ expect(error).toBeInstanceOf(UnknownError);
35
+ expect(error).to.include({
36
+ name: 'UnknownError',
37
+ message: cause
38
+ });
39
+ expect(error).not.to.have.property('cause');
40
+ expect(error).to.have.property('stack').that.is.a('string');
41
+ });
42
+ // Fails on serializiation of AST
43
+ it.skip('should wrap an object', () => {
44
+ const cause = { foo: 'bar' };
45
+ const error = normalizeError(cause);
46
+ expect(error).toBeInstanceOf(UnknownError);
47
+ expect(error).to.include({
48
+ name: 'UnknownError',
49
+ message: '{"foo":"bar"}'
50
+ });
51
+ expect(error).not.to.have.property('cause');
52
+ expect(error).to.have.property('stack').that.is.a('string');
53
+ });
54
+ });
55
+ });
56
+ describe('invariant', () => {
57
+ it('should throw an error if the condition fails', () => {
58
+ expect(() => invariant(false)).toThrow('Invariant failed');
59
+ });
60
+ it('should throw an error if the condition is null', () => {
61
+ expect(() => invariant(null)).toThrow(InvariantError);
62
+ });
63
+ it('should not throw an error if the condition passes', () => {
64
+ expect(() => invariant(true)).not.toThrow();
65
+ });
66
+ it('should throw an error with the provided message if the condition fails', () => {
67
+ expect(() => invariant(false, 'test')).toThrow('test');
68
+ });
69
+ });
@@ -36,3 +36,4 @@ export declare function nonexhaustive(value: never): never;
36
36
  export declare class InvalidModelError extends BaseError {
37
37
  constructor(message: string, options?: BaseErrorOptions);
38
38
  }
39
+ //# sourceMappingURL=index.d.ts.map
@@ -63,7 +63,13 @@ export function normalizeError(e) {
63
63
  }
64
64
  const message = isString(e) ? e : String(e);
65
65
  const error = new UnknownError(message);
66
- Error.captureStackTrace(error, normalizeError);
66
+ try {
67
+ // @ts-ignore
68
+ Error.captureStackTrace(error, normalizeError);
69
+ }
70
+ catch {
71
+ // Ignore
72
+ }
67
73
  return error;
68
74
  }
69
75
  // Throw an error if the condition fails
package/dist/index.d.ts CHANGED
@@ -4,3 +4,4 @@ export * from './types';
4
4
  export * from './utils';
5
5
  export type * as likec4 from './types';
6
6
  export type * as c4 from './types';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,4 @@
1
1
  import type { Opaque } from './opaque';
2
2
  export type NonEmptyArray<T> = [T, ...T[]] | [...T[], T];
3
3
  export type IconUrl = Opaque<string, 'IconUrl'>;
4
+ //# sourceMappingURL=_common.d.ts.map
@@ -32,3 +32,4 @@ export interface Element {
32
32
  readonly color?: ThemeColor;
33
33
  readonly style?: ElementStyle;
34
34
  }
35
+ //# sourceMappingURL=element.d.ts.map
@@ -79,3 +79,4 @@ export type AnyRelationExpression = RelationExpr | InOutExpr | IncomingExpr | Ou
79
79
  export declare function isAnyRelation(expr: Expression): expr is AnyRelationExpression;
80
80
  export type Expression = ElementExpression | CustomElementExpr | AnyRelationExpression;
81
81
  export {};
82
+ //# sourceMappingURL=expression.d.ts.map
@@ -1,6 +1,4 @@
1
1
  export * from './_common';
2
- export * from './computed-view';
3
- export * from './diagram';
4
2
  export * from './element';
5
3
  export * from './expression';
6
4
  export * from './model';
@@ -9,3 +7,4 @@ export * from './relation';
9
7
  export * from './theme';
10
8
  export * from './view';
11
9
  export * as Expr from './expression';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -1,6 +1,4 @@
1
1
  export * from './_common';
2
- export * from './computed-view';
3
- export * from './diagram';
4
2
  export * from './element';
5
3
  export * from './expression';
6
4
  export * from './model';
@@ -8,4 +6,5 @@ export * from './opaque';
8
6
  export * from './relation';
9
7
  export * from './theme';
10
8
  export * from './view';
11
- export * as Expr from './expression';
9
+ import * as Expr_1 from './expression';
10
+ export { Expr_1 as Expr };
@@ -1,14 +1,14 @@
1
- import type { ComputedView } from './computed-view';
2
1
  import type { Element, Fqn } from './element';
3
2
  import type { Relation, RelationID } from './relation';
4
- import type { ElementView, ViewID } from './view';
3
+ import type { ComputedView, View, ViewID } from './view';
5
4
  export interface LikeC4Model {
6
5
  elements: Record<Fqn, Element>;
7
6
  relations: Record<RelationID, Relation>;
8
- views: Record<ViewID, ElementView>;
7
+ views: Record<ViewID, View>;
9
8
  }
10
9
  export interface LikeC4ComputedModel {
11
10
  elements: Record<Fqn, Element>;
12
11
  relations: Record<RelationID, Relation>;
13
12
  views: Record<ViewID, ComputedView>;
14
13
  }
14
+ //# sourceMappingURL=model.d.ts.map
@@ -100,3 +100,4 @@ type WontWork = UnwrapOpaque<string>;
100
100
  */
101
101
  export type UnwrapOpaque<OpaqueType extends Tagged<unknown>> = OpaqueType extends Opaque<infer Type, OpaqueType[typeof tag]> ? Type : OpaqueType;
102
102
  export {};
103
+ //# sourceMappingURL=opaque.d.ts.map
@@ -22,3 +22,4 @@ export interface Relation {
22
22
  readonly tail?: RelationshipArrowType;
23
23
  readonly links?: NonEmptyArray<string>;
24
24
  }
25
+ //# sourceMappingURL=relation.d.ts.map
@@ -24,3 +24,4 @@ export interface LikeC4Theme {
24
24
  relationships: RelationshipThemeColors;
25
25
  elements: ElementThemeColors;
26
26
  }
27
+ //# sourceMappingURL=theme.d.ts.map
@@ -1,8 +1,9 @@
1
1
  import type { IconUrl, NonEmptyArray } from './_common';
2
- import type { ElementShape, ElementStyle, Fqn, Tag } from './element';
3
- import type { ElementExpression, Expression } from './expression';
2
+ import type { ElementKind, ElementShape, ElementStyle, Fqn, Tag } from './element';
3
+ import type { CustomElementExpr, ElementExpression, Expression } from './expression';
4
4
  import type { Opaque } from './opaque';
5
- import type { ThemeColor } from './theme';
5
+ import type { RelationID, RelationshipArrowType, RelationshipLineType } from './relation';
6
+ import type { ColorLiteral, ThemeColor } from './theme';
6
7
  export type ViewID = Opaque<string, 'ViewID'>;
7
8
  export type ViewRuleExpression = {
8
9
  include: Expression[];
@@ -27,14 +28,13 @@ export interface ViewRuleAutoLayout {
27
28
  }
28
29
  export declare function isViewRuleAutoLayout(rule: ViewRule): rule is ViewRuleAutoLayout;
29
30
  export type ViewRule = ViewRuleExpression | ViewRuleStyle | ViewRuleAutoLayout;
30
- export interface BasicElementView {
31
+ export interface BasicView<ViewType extends 'element' | 'dynamic' = 'element' | 'dynamic'> {
32
+ readonly __?: ViewType;
31
33
  readonly id: ViewID;
32
- readonly viewOf?: Fqn;
33
34
  readonly title: string | null;
34
35
  readonly description: string | null;
35
36
  readonly tags: NonEmptyArray<Tag> | null;
36
37
  readonly links: NonEmptyArray<string> | null;
37
- readonly rules: ViewRule[];
38
38
  /**
39
39
  * URI to the source file of this view.
40
40
  * Undefined if the view is auto-generated.
@@ -51,12 +51,137 @@ export interface BasicElementView {
51
51
  */
52
52
  readonly relativePath?: string;
53
53
  }
54
+ export interface BasicElementView extends BasicView<'element'> {
55
+ readonly viewOf?: Fqn;
56
+ readonly rules: ViewRule[];
57
+ }
54
58
  export interface StrictElementView extends BasicElementView {
55
59
  readonly viewOf: Fqn;
56
60
  }
57
- export declare function isStrictElementView(view: ElementView): view is StrictElementView;
58
61
  export interface ExtendsElementView extends BasicElementView {
59
62
  readonly extends: ViewID;
60
63
  }
61
- export declare function isExtendsElementView(view: ElementView): view is ExtendsElementView;
62
64
  export type ElementView = StrictElementView | ExtendsElementView | BasicElementView;
65
+ export interface DynamicViewStep {
66
+ readonly source: Fqn;
67
+ readonly target: Fqn;
68
+ readonly title: string | null;
69
+ readonly isBackward?: boolean;
70
+ }
71
+ export type DynamicViewIncludeRule = {
72
+ include: (ElementExpression | CustomElementExpr)[];
73
+ };
74
+ export declare function isDynamicViewIncludeRule(rule: DynamicViewRule): rule is DynamicViewIncludeRule;
75
+ export type DynamicViewRule = DynamicViewIncludeRule | ViewRuleStyle | ViewRuleAutoLayout;
76
+ export interface DynamicView extends BasicView<'dynamic'> {
77
+ readonly __: 'dynamic';
78
+ readonly steps: DynamicViewStep[];
79
+ readonly rules: DynamicViewRule[];
80
+ }
81
+ export type View = ElementView | DynamicView;
82
+ export declare function isDynamicView(view: View): view is DynamicView;
83
+ export declare function isElementView(view: View): view is ElementView;
84
+ export declare function isExtendsElementView(view: View): view is ExtendsElementView;
85
+ export declare function isStrictElementView(view: View): view is StrictElementView;
86
+ export type NodeId = Fqn;
87
+ export type EdgeId = Opaque<string, 'EdgeId'>;
88
+ export type StepEdgeIdLiteral = `step-${number}`;
89
+ export type StepEdgeId = Opaque<StepEdgeIdLiteral, 'EdgeId'>;
90
+ export declare function StepEdgeId(step: number): StepEdgeId;
91
+ export declare function isStepEdgeId(id: string): id is StepEdgeId;
92
+ export declare function extractStep(id: EdgeId): number;
93
+ export interface ComputedNode {
94
+ id: NodeId;
95
+ kind: ElementKind;
96
+ parent: NodeId | null;
97
+ title: string;
98
+ description: string | null;
99
+ technology: string | null;
100
+ tags: NonEmptyArray<Tag> | null;
101
+ links: NonEmptyArray<string> | null;
102
+ children: NodeId[];
103
+ inEdges: EdgeId[];
104
+ outEdges: EdgeId[];
105
+ shape: ElementShape;
106
+ /**
107
+ * @deprecated Use `style` instead
108
+ */
109
+ color: ThemeColor;
110
+ /**
111
+ * @deprecated Use `style` instead
112
+ */
113
+ icon?: IconUrl;
114
+ style: ElementStyle;
115
+ navigateTo?: ViewID;
116
+ level: number;
117
+ depth?: number;
118
+ }
119
+ export interface ComputedEdge {
120
+ id: EdgeId;
121
+ parent: NodeId | null;
122
+ source: NodeId;
123
+ target: NodeId;
124
+ label: string | null;
125
+ relations: RelationID[];
126
+ color?: ThemeColor;
127
+ line?: RelationshipLineType;
128
+ head?: RelationshipArrowType;
129
+ tail?: RelationshipArrowType;
130
+ /**
131
+ * For layouting purposes
132
+ * @default 'forward'
133
+ */
134
+ dir?: 'forward' | 'back';
135
+ }
136
+ export interface ComputedElementView extends Omit<ElementView, 'rules'> {
137
+ readonly extends?: ViewID;
138
+ readonly autoLayout: ViewRuleAutoLayout['autoLayout'];
139
+ readonly nodes: ComputedNode[];
140
+ readonly edges: ComputedEdge[];
141
+ }
142
+ export interface ComputedDynamicView extends Omit<DynamicView, 'rules' | 'steps'> {
143
+ readonly autoLayout: ViewRuleAutoLayout['autoLayout'];
144
+ readonly nodes: ComputedNode[];
145
+ readonly edges: ComputedEdge[];
146
+ }
147
+ export declare function isComputedDynamicView(view: ComputedView): view is ComputedDynamicView;
148
+ export type ComputedView = ComputedElementView | ComputedDynamicView;
149
+ export declare function isComputedElementView(view: ComputedView): view is ComputedElementView;
150
+ export type Point = readonly [x: number, y: number];
151
+ export type BBox = {
152
+ x: number;
153
+ y: number;
154
+ width: number;
155
+ height: number;
156
+ };
157
+ export interface DiagramLabel {
158
+ align: 'left' | 'right' | 'center';
159
+ fontStyle?: 'bold' | 'normal';
160
+ color?: ColorLiteral;
161
+ fontSize: number;
162
+ pt: Point;
163
+ width: number;
164
+ text: string;
165
+ }
166
+ export interface DiagramNode extends ComputedNode {
167
+ width: number;
168
+ height: number;
169
+ position: Point;
170
+ labels: DiagramLabel[];
171
+ }
172
+ export interface DiagramEdge extends ComputedEdge {
173
+ points: NonEmptyArray<Point>;
174
+ headArrow?: NonEmptyArray<Point>;
175
+ headArrowPoint?: Point;
176
+ tailArrow?: NonEmptyArray<Point>;
177
+ tailArrowPoint?: Point;
178
+ labels?: NonEmptyArray<DiagramLabel>;
179
+ labelBBox?: BBox;
180
+ }
181
+ export interface DiagramView extends Omit<ComputedView, 'nodes' | 'edges'> {
182
+ readonly nodes: DiagramNode[];
183
+ readonly edges: DiagramEdge[];
184
+ readonly width: number;
185
+ readonly height: number;
186
+ }
187
+ //# sourceMappingURL=view.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { isNullish } from 'remeda';
1
2
  export function isViewRuleExpression(rule) {
2
3
  return (('include' in rule && Array.isArray(rule.include))
3
4
  || ('exclude' in rule && Array.isArray(rule.exclude)));
@@ -8,9 +9,36 @@ export function isViewRuleStyle(rule) {
8
9
  export function isViewRuleAutoLayout(rule) {
9
10
  return 'autoLayout' in rule;
10
11
  }
11
- export function isStrictElementView(view) {
12
- return 'viewOf' in view;
12
+ export function isDynamicViewIncludeRule(rule) {
13
+ return 'include' in rule && Array.isArray(rule.include);
14
+ }
15
+ export function isDynamicView(view) {
16
+ return view.__ === 'dynamic';
17
+ }
18
+ export function isElementView(view) {
19
+ return isNullish(view.__) || view.__ === 'element';
13
20
  }
14
21
  export function isExtendsElementView(view) {
15
- return 'extends' in view;
22
+ return isElementView(view) && 'extends' in view;
23
+ }
24
+ export function isStrictElementView(view) {
25
+ return isElementView(view) && 'viewOf' in view;
26
+ }
27
+ export function StepEdgeId(step) {
28
+ return `step-${String(step).padStart(3, '0')}`;
29
+ }
30
+ export function isStepEdgeId(id) {
31
+ return id.startsWith('step-');
32
+ }
33
+ export function extractStep(id) {
34
+ if (!isStepEdgeId(id)) {
35
+ throw new Error(`Invalid step edge id: ${id}`);
36
+ }
37
+ return Number(id.slice('step-'.length));
38
+ }
39
+ export function isComputedDynamicView(view) {
40
+ return view.__ === 'dynamic';
41
+ }
42
+ export function isComputedElementView(view) {
43
+ return isNullish(view.__) || view.__ === 'element';
16
44
  }
@@ -31,3 +31,4 @@ export declare function compareFqnHierarchically<T extends string = string>(a: T
31
31
  export declare function compareByFqnHierarchically<T extends {
32
32
  id: string;
33
33
  }>(a: T, b: T): number;
34
+ //# sourceMappingURL=fqn.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fqn.spec.d.ts.map
@@ -0,0 +1,82 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { ancestorsFqn, commonAncestor, compareFqnHierarchically, isAncestor, isDescendantOf, notDescendantOf, parentFqn } from './fqn';
3
+ const el = (id) => ({ id });
4
+ describe('parentFqn', () => {
5
+ it('should return null if no parent', () => {
6
+ expect(parentFqn('a')).toBeNull();
7
+ });
8
+ it('should return parent', () => {
9
+ expect(parentFqn('a.b')).toBe('a');
10
+ expect(parentFqn('a.b.c')).toBe('a.b');
11
+ });
12
+ });
13
+ describe('ancestorsFqn', () => {
14
+ it('should return empty array if no parent', () => {
15
+ expect(ancestorsFqn('a')).toEqual([]);
16
+ });
17
+ it('should return ancestors', () => {
18
+ expect(ancestorsFqn('a.b.c.d.e')).toEqual(['a.b.c.d', 'a.b.c', 'a.b', 'a']);
19
+ });
20
+ });
21
+ describe('commonAncestor', () => {
22
+ it('should return null if no common ancestor', () => {
23
+ expect(commonAncestor('a', 'b')).toBeNull();
24
+ expect(commonAncestor('a.b', 'c.d')).toBeNull();
25
+ });
26
+ it('should return common ancestor', () => {
27
+ expect(commonAncestor('a.b', 'a.c')).toBe('a');
28
+ expect(commonAncestor('a.b.c', 'a.b.e')).toBe('a.b');
29
+ expect(commonAncestor('a.b.c.d.e', 'a.b.c.d')).toBe('a.b.c');
30
+ });
31
+ });
32
+ describe('isAncestor', () => {
33
+ it('should return true if ancestor', () => {
34
+ expect(isAncestor('a', 'a.b')).toBe(true);
35
+ expect(isAncestor('a.b', 'a.b.c')).toBe(true);
36
+ });
37
+ it('should return false if not ancestor', () => {
38
+ expect(isAncestor('a', 'b')).toBe(false);
39
+ expect(isAncestor('a.b', 'a')).toBe(false);
40
+ expect(isAncestor('a.b', 'b.a')).toBe(false);
41
+ });
42
+ });
43
+ describe('isDescendantOf', () => {
44
+ const predicate = isDescendantOf(['a', 'b', 'a.b', 'a.b.c'].map(el));
45
+ it('should return true if isDescendantOf', () => {
46
+ expect(predicate(el('a'))).toBe(true);
47
+ expect(predicate(el('b.c'))).toBe(true);
48
+ expect(predicate(el('a.b.c.d.e'))).toBe(true);
49
+ });
50
+ it('should return false if not descendantOf', () => {
51
+ expect(predicate(el('c'))).toBe(false);
52
+ expect(predicate(el('ac'))).toBe(false);
53
+ expect(predicate(el('d.a.c'))).toBe(false);
54
+ });
55
+ });
56
+ describe('notDescendantOf', () => {
57
+ const predicate = notDescendantOf(['a', 'b', 'a.b', 'a.b.c'].map(el));
58
+ it('should return true if notDescendantOf', () => {
59
+ expect(predicate(el('c'))).toBe(true);
60
+ expect(predicate(el('ac'))).toBe(true);
61
+ expect(predicate(el('d.a.c'))).toBe(true);
62
+ });
63
+ it('should return false if descendantOf', () => {
64
+ expect(predicate(el('a'))).toBe(false);
65
+ expect(predicate(el('b.c'))).toBe(false);
66
+ expect(predicate(el('a.b.c.d.e'))).toBe(false);
67
+ });
68
+ });
69
+ describe('compareFqnHierarchically', () => {
70
+ it('should compare hierarchically', () => {
71
+ expect(['a', 'b', 'a.b', 'a.b.c', 'a.c.c'].sort(compareFqnHierarchically)).toEqual([
72
+ 'a',
73
+ 'b',
74
+ 'a.b',
75
+ 'a.b.c',
76
+ 'a.c.c'
77
+ ]);
78
+ });
79
+ it('should preserve initial order', () => {
80
+ expect(['aaa', 'aa', 'a', 'aaa.c', 'aa.b', 'a.c', 'a.b'].sort(compareFqnHierarchically)).toEqual(['aaa', 'aa', 'a', 'aaa.c', 'aa.b', 'a.c', 'a.b']);
81
+ });
82
+ });
@@ -2,3 +2,4 @@ import type { NonEmptyArray } from '../types';
2
2
  export { hasAtLeast } from 'remeda';
3
3
  export declare function isString(value: unknown): value is string;
4
4
  export declare function isNonEmptyArray<A>(arr: ArrayLike<A> | undefined): arr is NonEmptyArray<A>;
5
+ //# sourceMappingURL=guards.d.ts.map
@@ -2,3 +2,4 @@ export * from './fqn';
2
2
  export * from './guards';
3
3
  export * from './promises';
4
4
  export * from './relations';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -1 +1,2 @@
1
1
  export declare function delay(ms?: number): Promise<unknown>;
2
+ //# sourceMappingURL=promises.d.ts.map
@@ -27,3 +27,4 @@ export declare const Relations: {
27
27
  }>(element: E) => boolean;
28
28
  };
29
29
  export {};
30
+ //# sourceMappingURL=relations.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=relations.spec.d.ts.map