@likec4/core
Advanced tools
Comparing version 0.29.0 to 0.30.0
@@ -15,3 +15,3 @@ import { anyPass, filter, uniq, isNil } from 'rambdax'; | ||
.sort(compareByFqnHierarchically) | ||
.reduce((map, { id, kind, title, color, shape, description }) => { | ||
.reduce((map, { id, color, shape, tags, ...el }) => { | ||
let parent = parentFqn(id); | ||
@@ -30,10 +30,9 @@ while (parent) { | ||
const node = { | ||
...el, | ||
id, | ||
kind, | ||
parent, | ||
title, | ||
color: color ?? DefaultThemeColor, | ||
shape: shape ?? DefaultElementShape, | ||
children: [], | ||
...(description ? { description } : {}) | ||
tags: [...tags] | ||
}; | ||
@@ -40,0 +39,0 @@ map.set(id, node); |
export * from './_base'; | ||
export * from './invariant'; | ||
export * from './model-index'; | ||
//# sourceMappingURL=index.d.ts.map |
export * from './_base'; | ||
export * from './invariant'; | ||
export * from './model-index'; | ||
//# sourceMappingURL=index.js.map |
export type * from './types'; | ||
export * from './types'; | ||
export * from './compute-view'; | ||
@@ -3,0 +4,0 @@ export * from './utils'; |
@@ -0,1 +1,2 @@ | ||
export * from './types'; | ||
export * from './compute-view'; | ||
@@ -2,0 +3,0 @@ export * from './utils'; |
@@ -11,3 +11,3 @@ import type { Element, Fqn, Relation, RelationID } from '../types'; | ||
get relations(): Relation[]; | ||
filterRelations: (predicate: (r: Relation) => boolean) => Relation[]; | ||
filterRelations(predicate: (r: Relation) => boolean): Relation[]; | ||
static from({ elements, relations }: ModelInput): ModelIndex; | ||
@@ -22,5 +22,6 @@ addElement(el: Element): void; | ||
*/ | ||
ancestors: (id: Fqn) => Element[]; | ||
ancestors(id: Fqn): Element[]; | ||
rootElements(): Element[]; | ||
get elements(): Element[]; | ||
hasElement(fqn: Fqn): boolean; | ||
addRelation(rel: Relation): void; | ||
@@ -27,0 +28,0 @@ } |
@@ -1,4 +0,4 @@ | ||
import { values } from 'remeda'; | ||
import { invariant } from '../errors'; | ||
import { parentFqn } from '../utils/fqn'; | ||
import { pipe, sort, values } from 'remeda'; | ||
import { InvalidModelError, ensureModel } from '../errors'; | ||
import { compareByFqnHierarchically, parentFqn } from '../utils/fqn'; | ||
function childrenOf(trie) { | ||
@@ -27,8 +27,9 @@ const children = []; | ||
} | ||
filterRelations = (predicate) => { | ||
filterRelations(predicate) { | ||
return this.relations.filter(predicate); | ||
}; | ||
} | ||
static from({ elements, relations }) { | ||
const index = new ModelIndex(); | ||
for (const el of values(elements)) { | ||
const sortedElements = pipe(values(elements), sort(compareByFqnHierarchically)); | ||
for (const el of sortedElements) { | ||
index.addElement(el); | ||
@@ -42,4 +43,4 @@ } | ||
addElement(el) { | ||
if (this._elements.has(el.id)) { | ||
throw new Error(`Element already exists with id ${el.id}`); | ||
if (this.hasElement(el.id)) { | ||
throw new InvalidModelError(`Element already exists with id ${el.id}`); | ||
} | ||
@@ -62,3 +63,3 @@ const path = asPath(el.id); | ||
const next = scope.children[name]; | ||
invariant(next, `Invalid index, Element not found at path ${name} of ${id}`); | ||
ensureModel(next, `Invalid index, Element not found at path ${name} of ${id}`); | ||
scope = next; | ||
@@ -71,3 +72,3 @@ } | ||
if (!el) { | ||
throw new Error(`Element not found ${id}`); | ||
throw new InvalidModelError(`Element not found ${id}`); | ||
} | ||
@@ -87,3 +88,3 @@ return el; | ||
*/ | ||
ancestors = (id) => { | ||
ancestors(id) { | ||
const path = asPath(id); | ||
@@ -101,9 +102,5 @@ const ancestors = []; | ||
const next = trie.children[name]; | ||
if (!next) { | ||
throw new Error(`Invalid index, Element not found at path ${name} of ${id}`); | ||
} | ||
ensureModel(next, `Invalid index, Element not found at path ${name} of ${id}`); | ||
trie = next; | ||
if (!trie.el) { | ||
throw new Error(`invalid index, no element ${name} found in ${id}`); | ||
} | ||
ensureModel(trie.el, `invalid index, no element ${name} found in ${id}`); | ||
ancestors.unshift(trie.el); | ||
@@ -113,3 +110,3 @@ name = path.shift(); | ||
return ancestors; | ||
}; | ||
} | ||
// tagged = (tag?: Tag): TaggedResult => { | ||
@@ -130,12 +127,12 @@ // return tag ? { | ||
} | ||
// hasElement(fqn: Fqn): boolean { | ||
// return fqn in this._elements | ||
// } | ||
hasElement(fqn) { | ||
return this._elements.has(fqn); | ||
} | ||
addRelation(rel) { | ||
// Validate source and target | ||
if (!this._elements.has(rel.source)) { | ||
throw new Error(`Invalid index, source of relation not found ${rel.source}`); | ||
if (!this.hasElement(rel.source)) { | ||
throw new InvalidModelError(`Source of relation not found ${rel.source}`); | ||
} | ||
if (!this._elements.has(rel.target)) { | ||
throw new Error(`Invalid index, target of relation not found ${rel.target}`); | ||
if (!this.hasElement(rel.target)) { | ||
throw new InvalidModelError(`Target of relation not found ${rel.target}`); | ||
} | ||
@@ -142,0 +139,0 @@ this._relations.set(rel.id, rel); |
@@ -12,5 +12,5 @@ import type { Opaque } from './opaque'; | ||
title: string; | ||
description?: string; | ||
technology?: string; | ||
tags?: Tag[]; | ||
description: string | null; | ||
technology: string | null; | ||
tags: Tag[]; | ||
children: NodeId[]; | ||
@@ -17,0 +17,0 @@ shape: ElementShape; |
import type { Opaque } from './opaque'; | ||
export type Fqn = Opaque<string, 'Fqn'>; | ||
export declare function Fqn(name: string, parent?: Fqn | null): Fqn; | ||
export declare function AsFqn(name: string, parent?: Fqn | null): Fqn; | ||
export type ElementKind = Opaque<string, 'ElementKind'>; | ||
/** | ||
* TailwindCSS based color palette | ||
*/ | ||
export type ThemeColor = 'amber' | 'blue' | 'gray' | 'slate' | 'green' | 'indigo' | 'muted' | 'primary' | 'red' | 'secondary' | 'sky'; | ||
@@ -21,5 +24,5 @@ export type ElementShape = 'rectangle' | 'person' | 'browser' | 'mobile' | 'cylinder' | 'storage' | 'queue'; | ||
readonly title: string; | ||
readonly description?: string; | ||
readonly technology?: string; | ||
readonly tags?: Tag[]; | ||
readonly description: string | null; | ||
readonly technology: string | null; | ||
readonly tags: ReadonlyArray<Tag>; | ||
readonly shape?: ElementShape; | ||
@@ -26,0 +29,0 @@ readonly color?: ThemeColor; |
@@ -1,2 +0,2 @@ | ||
export function Fqn(name, parent) { | ||
export function AsFqn(name, parent) { | ||
return (parent ? parent + '.' + name : name); | ||
@@ -3,0 +3,0 @@ } |
import type { ElementKind, Fqn, Tag } from './element'; | ||
interface BaseExr { | ||
interface BaseExpr { | ||
element?: never; | ||
@@ -15,3 +15,3 @@ elementKind?: never; | ||
} | ||
export interface ElementRefExpr extends Omit<BaseExr, 'element' | 'isDescedants'> { | ||
export interface ElementRefExpr extends Omit<BaseExpr, 'element' | 'isDescedants'> { | ||
element: Fqn; | ||
@@ -21,7 +21,7 @@ isDescedants: boolean; | ||
export declare function isElementRef(expr: Expression): expr is ElementRefExpr; | ||
export interface WildcardExpr extends Omit<BaseExr, 'wildcard'> { | ||
export interface WildcardExpr extends Omit<BaseExpr, 'wildcard'> { | ||
wildcard: true; | ||
} | ||
export declare function isWildcard(expr: Expression): expr is WildcardExpr; | ||
export interface ElementKindExpr extends Omit<BaseExr, 'elementKind' | 'isEqual'> { | ||
export interface ElementKindExpr extends Omit<BaseExpr, 'elementKind' | 'isEqual'> { | ||
elementKind: ElementKind; | ||
@@ -31,3 +31,3 @@ isEqual: boolean; | ||
export declare function isElementKindExpr(expr: Expression): expr is ElementKindExpr; | ||
export interface ElementTagExpr extends Omit<BaseExr, 'elementTag' | 'isEqual'> { | ||
export interface ElementTagExpr extends Omit<BaseExpr, 'elementTag' | 'isEqual'> { | ||
elementTag: Tag; | ||
@@ -39,3 +39,3 @@ isEqual: boolean; | ||
export declare function isElement(expr: Expression): expr is ElementExpression; | ||
export interface RelationExpr extends Omit<BaseExr, 'source' | 'target'> { | ||
export interface RelationExpr extends Omit<BaseExpr, 'source' | 'target'> { | ||
source: ElementExpression; | ||
@@ -45,11 +45,11 @@ target: ElementExpression; | ||
export declare function isRelation(expr: Expression): expr is RelationExpr; | ||
export interface InOutExpr extends Omit<BaseExr, 'inout'> { | ||
export interface InOutExpr extends Omit<BaseExpr, 'inout'> { | ||
inout: ElementExpression; | ||
} | ||
export declare function isInOut(expr: Expression): expr is InOutExpr; | ||
export interface IncomingExpr extends Omit<BaseExr, 'incoming'> { | ||
export interface IncomingExpr extends Omit<BaseExpr, 'incoming'> { | ||
incoming: ElementExpression; | ||
} | ||
export declare function isIncoming(expr: Expression): expr is IncomingExpr; | ||
export interface OutgoingExpr extends Omit<BaseExr, 'outgoing'> { | ||
export interface OutgoingExpr extends Omit<BaseExpr, 'outgoing'> { | ||
outgoing: ElementExpression; | ||
@@ -56,0 +56,0 @@ } |
@@ -1,3 +0,4 @@ | ||
export { Fqn, DefaultThemeColor, DefaultElementShape } from './element'; | ||
export { isViewRuleExpression, isViewRuleStyle } from './view'; | ||
export { AsFqn, DefaultThemeColor, DefaultElementShape } from './element'; | ||
export { isViewRuleExpression, isViewRuleAutoLayout, isViewRuleStyle } from './view'; | ||
export * as Expr from './expression'; | ||
export type * from './opaque'; | ||
@@ -4,0 +5,0 @@ export type * from './element'; |
@@ -1,3 +0,4 @@ | ||
export { Fqn, DefaultThemeColor, DefaultElementShape } from './element'; | ||
export { isViewRuleExpression, isViewRuleStyle } from './view'; | ||
export { AsFqn, DefaultThemeColor, DefaultElementShape } from './element'; | ||
export { isViewRuleExpression, isViewRuleAutoLayout, isViewRuleStyle } from './view'; | ||
export * as Expr from './expression'; | ||
//# sourceMappingURL=index.js.map |
@@ -9,2 +9,12 @@ import type { Element, Fqn } from '../types'; | ||
export declare function parentFqn(fqn: Fqn): Fqn | null; | ||
/** | ||
* Compares two fully qualified names (fqns) hierarchically based on their depth. | ||
* From parent nodes to leaves | ||
* | ||
* @param {string} a - The first fqn to compare. | ||
* @param {string} b - The second fqn to compare. | ||
* @returns {number} - 0 if the fqns have the same depth. | ||
* - Positive number if a is deeper than b. | ||
* - Negative number if b is deeper than a. | ||
*/ | ||
export declare const compareFqnHierarchically: (a: string, b: string) => number; | ||
@@ -11,0 +21,0 @@ export declare const compareByFqnHierarchically: <T extends { |
@@ -59,2 +59,12 @@ import { anyPass } from 'remeda'; | ||
} | ||
/** | ||
* Compares two fully qualified names (fqns) hierarchically based on their depth. | ||
* From parent nodes to leaves | ||
* | ||
* @param {string} a - The first fqn to compare. | ||
* @param {string} b - The second fqn to compare. | ||
* @returns {number} - 0 if the fqns have the same depth. | ||
* - Positive number if a is deeper than b. | ||
* - Negative number if b is deeper than a. | ||
*/ | ||
export const compareFqnHierarchically = (a, b) => { | ||
@@ -61,0 +71,0 @@ const depthA = a.split('.').length; |
{ | ||
"name": "@likec4/core", | ||
"version": "0.29.0", | ||
"version": "0.30.0", | ||
"license": "MIT", | ||
"bugs": "https://github.com/likec4/likec4/issues", | ||
"homepage": "https://likec4.dev", | ||
"author": "Denis Davydkov <denis@davydkov.com>", | ||
"files": [ | ||
"dist", | ||
"!dist/__test__", | ||
"!**/*.spec.*", | ||
"!**/*.map" | ||
], | ||
"repository": { | ||
@@ -19,2 +12,3 @@ "type": "git", | ||
}, | ||
"bugs": "https://github.com/likec4/likec4/issues", | ||
"scripts": { | ||
@@ -29,2 +23,8 @@ "compile": "tsc --noEmit", | ||
}, | ||
"files": [ | ||
"dist", | ||
"!dist/__test__", | ||
"!**/*.spec.*", | ||
"!**/*.map" | ||
], | ||
"module": "./dist/index.js", | ||
@@ -31,0 +31,0 @@ "types": "./dist/index.d.ts", |
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
64044
60
1649
0
4