1.32.1 • Published 10 months ago

@likec4/core v1.32.1

Weekly downloads
-
License
MIT
Repository
github
Last release
10 months ago

@likec4/core

NPM Version NPM Downloads

A core package for LikeC4, containing types, api, utilities and guards.

!NOTE This package is a low level API for working with LikeC4.\ It is exposed mostly through likec4, that generates models from DSL.

Types

Includes a set of types that are used across the project

import type {
  DiagramView,
  Element,
  ElementViewPredicate,
  Fqn,
  // ....
} from '@likec4/core/types'

There is some concept that allows to build models in a type-safe way.\ Consider the following example:

import type { aux } from '@likec4/core/types'

interface Element<A extends aux.Any = aux.Any> {
  id: aux.Fqn<A>
  kind: aux.ElementKind<A>
  title: string
  tags: aux.Tags<A>
}

Aux is a registry that keeps track of the specification (kinds, tags), all the elements and views. Thus, it is used to generate types for the identifiers (FQNs, view predicates, etc.).

When you generate the model from DSL or use builder, Aux is generated automatically.

You can use aux.Any as the default type, that infers to string.

Model API

This is the main API to work with LikeC4 model.\ It provides methods to query and traverse the model (get element, children, incoming relationships, etc.)\ Model is built from ModelData, and has three stages:

  • parsed\ Sourced from ParsedLikeC4ModelData - represents model parsed from DSL or generated using builder.\ All the elements and relations are available, but views are not processed yet, and have rules[] property (that are predicates to compute)\ These views are available as ParsedView, or ParsedDynamicView as a type for specific view.

  • computed\ Model is computed from parsed model (source type ComputedLikeC4ModelData)\ Now views are available as ComputedView (or ComputedDynamicView), and have nodes and edges.\ Also, elements have additional traversal methods, like views() - to get views where element is included.

  • layouted\ Model is layouted from computed model.\ Now every view (and its nodes and edges) has layout data (dimensions, positions).\ Source type is LayoutedLikeC4ModelData. Views are available as LayoutedView (or DiagramView)

Example:

const parsedData: ParsedLikeC4ModelData<Aux1> = {}
const computedData: ComputedLikeC4ModelData<Aux2> = {}
const layoutedData: LayoutedLikeC4ModelData<Aux3> = {}

const m1 = LikeC4Model.create(parsedData)
// m1 is LikeC4Model.Parsed<Aux1>
// m1.stage === 'parsed'

const m2 = LikeC4Model.create(computedData)
// m2 is LikeC4Model.Computed<Aux2>
// m2.stage === 'computed'

const m3 = LikeC4Model.create(layoutedData)
// m3 is LikeC4Model.Layouted<Aux3>
// m3.stage === 'layouted'

LikeC4Model.create expects Aux to be provided, or fallback to aux.Any.

There is also LikeC4Model.fromDump method, that infers Aux from the code (if constant is given).

!TIP Try npx likec4 codegen model to play with your model.

Usage

If Model has Aux with literals, every method is type-safe:

// Compiler error here, if there is no element with such FQN
model.element('non.existing.element')

// Workaround:
model.findElement('non.existing.element') // returns ElementModel | null

Other examples:

// Get model source
const source = model.$data

// Get elements of some kind
const elements = model.elementsOfKind('kind1')

// Use where operator to filter elements:
//  kind is 'kind1' and (tag is 'tag2' or tag is not 'tag3')
const elements = model.elementsWhere({
  and: [
    { kind: 'kind1' },
    {
      or: [
        { tag: 'tag2' },
        {
          tag: {
            neq: 'tag3',
          },
        },
      ],
    },
  ],
})

// Find all views, tagged with "tag1" and scoped for elements nested in "parent"
const parent = model.element('some.parent')
for (const v of model.findByTag('tag1', 'views')) {
  if (v.isScopedElementView() && v.viewOf.isDescendantOf(parent)) {
    //
  }
}

// Deployment model
for (const node of model.deployment.nodes()) {
  if (node.isDeployedInstance()) {
    // node is DeploymentNodeModel
  }
}

!NOTE Most methods return lazy evaluated iterators, use for...of or spread operator to retrieve all values (or methods like toArray())

Models have guards to check their type, like in example above we check if view is a scoped element view.

Generic guards are also available in @likec4/core/model:

import {
  isElementModel,,
  isDeployedInstanceModel,
  isLikeC4ViewModel,
  // ... other guards
} from '@likec4/core/model'

Connections

Connections can be used to find all relationships between elements (direct or derived).

import { modelConnection } from '@likec4/core/model'

const source = model.element('some.source')
const target = model.element('some.target')

const connection = modelConnection.findConnection(source, target)

Check sources for available methods.

Compute Model

You rarely need to compute model yourself, but in case you need to:

import { computeLikeC4Model } from '@likec4/core/compute-view'

const parsedData: ParsedLikeC4ModelData<Aux1> = {}
const model = computeLikeC4Model(parsedData)

Model Builder

We use it internally a lot for testing, but it is also available for you to build models in a type-safe way.

Two styles are available. You can mix both styles, depending on your preference and use cases.

via chain

import { Builder } from '@likec4/core/builder'

const m = Builder
  .specification({
    elements: {
      actor: {
        style: {
          shape: 'person',
        },
      },
      system: {},
      component: {},
    },
    relationships: {
      likes: {},
    },
    tags: ['tag1', 'tag2', 'tag1'],
  })
  .model(({ actor, system, component, relTo, rel }, _) =>
    _(
      actor('alice'),
      actor('bob'),
      rel('alice', 'bob', {
        tags: ['tag1'], // you get code completion for tags
        kind: 'likes', // code completion for kind
      }),
      system('cloud', { tags: ['tag1', 'tag2'] }).with(
        component('backend').with(
          component('api'),
          component('db'),
          // code completion for relationships
          rel('cloud.backend.api', 'cloud.backend.db'),
        ),
        component('frontend').with(
          relTo('cloud.backend.api'),
        ),
      ),
    )
  )
  .views(({ view, viewOf, $include, $style }, _) =>
    _(
      view('index', 'Index').with(
        // code completion for predicates
        $include('cloud.*'),
      ),
      viewOf('ui', 'cloud.ui').with(
        $include('* -> cloud.**'),
        $style('cloud.ui', { color: 'red' }),
      ),
    )
  )
  .toLikeC4Model()

Builder has two methods:

  • build(): returns ParsedLikeC4ModelData
  • toLikeC4Model(): returns already computed LikeC4Model

via composition

import { Builder } from '@likec4/core/builder'

// Get composition functions for given specification
const {
  model: {
    model,
    actor,
    system,
    component,
    rel,
    relTo,
  },
  views: {
    view,
    viewOf,
    views,
    $include,
    $style,
  },
  builder,
} = Builder.forSpecification({
  elements: {
    actor: {
      style: {
        shape: 'person',
      },
    },
    system: {},
    component: {},
  },
  relationships: {
    likes: {},
  },
  tags: ['tag1', 'tag2', 'tag1'],
})

const b1 = builder.with(
  model(
    actor('alice'),
    actor('bob'),
    rel('alice', 'bob', {
      tags: ['tag1'],
      kind: 'likes',
    }),
    system('cloud', { tags: ['tag1', 'tag2'] }).with(
      component('backend').with(
        component('api'),
        component('db'),
        rel('cloud.backend.api', 'cloud.backend.db'),
      ),
      component('frontend').with(
        relTo('cloud.backend.api'),
      ),
    ),
  ),
)

const b2 = b1.with(
  views(
    view('index', 'Index').with(
      $include('cloud.*'),
    ),
    viewOf('ui', 'cloud.ui').with(
      $include('* -> cloud.**'),
      $style('cloud.ui', { color: 'red' }),
    ),
  ),
)
  .toLikeC4Model()

!TIP You can also use specification from existing model:

const b = Builder.specification(existingModel.specification)

Utils

Project uses remeda internally, and exports some model-related utilities.\ Check sources for available functions.

Full Qualified Name (FQN) utils

There are various functions to work with Full Qualified Name (FQN) of elements, like isAncestor, commonAncestor, compareFqnHierarchically, etc.

import { isAncestor, isDescendantOf, sortNaturalByFqn } from '@likec4/core/utils'

if (isAncestor('parent', 'child')) {
  // do something
}

// filter elements that are descendants of parent
elements.filter(isDescendantOf(parent))

// Sort elements by FQN, like
//  a
//  a.b2
//  a.b10
//  a.b.c
sortNaturalByFqn(elements)

Iterables

Functions like filter, find, flat, map, reduce, some, unique, but for iterables, are available as i* functions, and can be used in pipelines.

import { isElementModel } from '@likec4/core/model'
import { ifilter, imap } from '@likec4/core/utils'
import { isTruthy, pipe, prop } from 'remeda'

pipe(
  model.findByTag('tag1'),
  ifilter(isElementModel),
  imap(prop('defaultView')),
  ifilter(isTruthy),
)

This composes to single iterator, avoiding intermediate arrays.

Getting help

We are always happy to help you get started:

Contributors

Become a contributor

Support development

LikeC4 is a MIT-licensed open source project with its ongoing development made possible entirely by your support.\ If you like the project, please consider contributing financially to help grow and improve it.\ You can support us via OpenCollective or GitHub Sponsors.

License

This project is released under the MIT License

1.18.0

1 year ago

1.21.0

1 year ago

1.21.1

1 year ago

1.25.0

1 year ago

1.25.1

1 year ago

1.29.0

12 months ago

1.29.1

11 months ago

1.32.0

10 months ago

1.32.1

10 months ago

1.19.0

1 year ago

1.19.2

1 year ago

1.19.1

1 year ago

1.22.0

1 year ago

1.26.0

1 year ago

1.22.1

1 year ago

1.26.1

1 year ago

1.26.2

1 year ago

1.16.0

1 year ago

1.23.0

1 year ago

1.23.1

1 year ago

1.27.2

12 months ago

1.27.3

12 months ago

1.27.0

1 year ago

1.27.1

1 year ago

1.30.0

11 months ago

1.17.1

1 year ago

1.17.0

1 year ago

1.20.1

1 year ago

1.20.2

1 year ago

1.20.0

1 year ago

1.24.1

1 year ago

1.20.3

1 year ago

1.24.0

1 year ago

1.28.1

12 months ago

1.28.0

12 months ago

1.31.0

10 months ago

1.15.0

1 year ago

1.15.1

1 year ago

1.14.0

1 year ago

1.13.0

1 year ago

1.12.2

1 year ago

1.12.1

1 year ago

1.12.0

1 year ago

1.11.0

2 years ago

1.10.1

2 years ago

1.10.0

2 years ago

1.9.0

2 years ago

1.8.1

2 years ago

1.8.0

2 years ago

1.2.0

2 years ago

1.6.1

2 years ago

1.6.0

2 years ago

1.2.2

2 years ago

1.2.1

2 years ago

1.7.4

2 years ago

1.1.1

2 years ago

1.1.0

2 years ago

1.5.0

2 years ago

1.0.2

2 years ago

1.4.0

2 years ago

1.7.3

2 years ago

1.7.2

2 years ago

1.7.1

2 years ago

1.7.0

2 years ago

1.3.0

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago

1.0.0-rc.1

2 years ago

1.0.0-next.14

2 years ago

0.60.4

2 years ago

1.0.0-next.11

2 years ago

1.0.0-next.10

2 years ago

1.0.0-next.2

2 years ago

1.0.0-next.0

2 years ago

1.0.0-next.1

2 years ago

0.60.3

2 years ago

0.60.2

2 years ago

0.60.1

2 years ago

0.60.0

2 years ago

0.58.0

2 years ago

0.57.0

2 years ago

0.57.1

2 years ago

0.56.0

2 years ago

0.54.0

2 years ago

0.53.0

2 years ago

0.52.0

2 years ago

0.51.0

2 years ago

0.43.0

2 years ago

0.41.0

2 years ago

0.36.0

3 years ago

0.34.0

3 years ago

0.32.0

3 years ago

0.30.0

3 years ago

0.29.0

3 years ago

0.48.0

2 years ago

0.27.0

3 years ago

0.46.0

2 years ago

0.25.0

3 years ago

0.46.1

2 years ago

0.44.0

2 years ago

0.42.2

2 years ago

0.23.0

3 years ago

0.44.1

2 years ago

0.42.0

2 years ago

0.42.1

2 years ago

0.40.0

2 years ago

0.37.1

3 years ago

0.37.0

3 years ago

0.35.0

3 years ago

0.33.1

3 years ago

0.33.0

3 years ago

0.31.0

3 years ago

0.50.0

2 years ago

0.28.1

3 years ago

0.28.0

3 years ago

0.49.0

2 years ago

0.26.0

3 years ago

0.47.0

2 years ago

0.24.0

3 years ago

0.43.1

2 years ago

0.45.0

2 years ago

0.28.3

3 years ago

0.28.2

3 years ago

0.21.0

3 years ago

0.20.1

3 years ago

0.20.11

3 years ago

0.20.10

3 years ago

0.20.9

3 years ago

0.20.8

3 years ago

0.20.7

3 years ago

0.20.6

3 years ago

0.20.5

3 years ago

0.20.4

3 years ago

0.22.0

3 years ago

0.20.2

3 years ago

0.20.0

3 years ago

0.19.0

3 years ago

0.18.1

3 years ago

0.18.0

3 years ago

0.7.0

3 years ago

0.6.3

3 years ago

0.6.2

3 years ago

0.6.1

3 years ago

0.6.0

3 years ago

0.5.0

3 years ago

0.4.0

3 years ago

0.2.2

3 years ago

0.2.1

3 years ago

0.2.0

3 years ago

0.1.0

3 years ago