@likec4/core 0.25.0 → 0.26.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.
@@ -1,6 +1,6 @@
1
1
  import { anyPass, filter, head, uniq, isNil } from 'rambdax';
2
2
  import { DefaultThemeColor, DefaultElementShape, } from '../types';
3
- import * as Expression from '../types/expression';
3
+ import * as Expr from '../types/expression';
4
4
  import { isViewRuleAutoLayout, isViewRuleExpression, isViewRuleStyle } from '../types/view';
5
5
  import { compareByFqnHierarchically, failExpectedNever, ignoreNeverInRuntime, isAncestor, isSameHierarchy, parentFqn, Relations } from '../utils';
6
6
  import { hasRelation, isAnyInOut, isBetween, isIncoming, isInside, isOutgoing } from '../utils/relations';
@@ -9,7 +9,7 @@ import { sortNodes } from './utils/sortNodes';
9
9
  import { ComputeCtx } from './compute-ctx';
10
10
  import { anyPossibleRelations } from './utils/anyPossibleRelations';
11
11
  import invariant from 'tiny-invariant';
12
- function transformToNodes(elementsIterator, index, currentViewid) {
12
+ function transformToNodes(elementsIterator) {
13
13
  return Array.from(elementsIterator)
14
14
  .sort(compareByFqnHierarchically)
15
15
  .reduce((map, { id, kind, title, color, shape, description }) => {
@@ -20,7 +20,6 @@ function transformToNodes(elementsIterator, index, currentViewid) {
20
20
  }
21
21
  parent = parentFqn(parent);
22
22
  }
23
- const navigateTo = head(index.defaultViewOf(id).filter(v => v !== currentViewid));
24
23
  if (parent) {
25
24
  const parentNd = map.get(parent);
26
25
  invariant(parentNd, `parent node ${parent} not found`);
@@ -35,7 +34,6 @@ function transformToNodes(elementsIterator, index, currentViewid) {
35
34
  shape: shape ?? DefaultElementShape,
36
35
  children: [],
37
36
  ...(description ? { description } : {}),
38
- ...(navigateTo ? { navigateTo } : {})
39
37
  };
40
38
  map.set(id, node);
41
39
  return map;
@@ -49,21 +47,21 @@ function applyViewRuleStyles(rules, nodes) {
49
47
  continue;
50
48
  }
51
49
  for (const target of rule.targets) {
52
- if (Expression.isWildcard(target)) {
50
+ if (Expr.isWildcard(target)) {
53
51
  predicates.push(() => true);
54
52
  break;
55
53
  }
56
- if (Expression.isElementKindExpr(target)) {
54
+ if (Expr.isElementKindExpr(target)) {
57
55
  predicates.push(target.isEqual ? n => n.kind === target.elementKind : n => n.kind !== target.elementKind);
58
56
  continue;
59
57
  }
60
- if (Expression.isElementTagExpr(target)) {
58
+ if (Expr.isElementTagExpr(target)) {
61
59
  predicates.push(target.isEqual
62
60
  ? ({ tags }) => !!tags && tags.includes(target.elementTag)
63
61
  : ({ tags }) => isNil(tags) || !tags.includes(target.elementTag));
64
62
  continue;
65
63
  }
66
- if (Expression.isElementRef(target)) {
64
+ if (Expr.isElementRef(target)) {
67
65
  const { element, isDescedants } = target;
68
66
  predicates.push(isDescedants ? n => n.id.startsWith(element + '.') : n => n.id === element);
69
67
  continue;
@@ -113,7 +111,7 @@ const excludeElementRef = (ctx, expr) => {
113
111
  };
114
112
  const asElementPredicate = (expr) => {
115
113
  if (expr.isEqual) {
116
- if (Expression.isElementKindExpr(expr)) {
114
+ if (Expr.isElementKindExpr(expr)) {
117
115
  return e => e.kind === expr.elementKind;
118
116
  }
119
117
  else {
@@ -121,7 +119,7 @@ const asElementPredicate = (expr) => {
121
119
  }
122
120
  }
123
121
  else {
124
- if (Expression.isElementKindExpr(expr)) {
122
+ if (Expr.isElementKindExpr(expr)) {
125
123
  return e => e.kind !== expr.elementKind;
126
124
  }
127
125
  else {
@@ -196,7 +194,7 @@ const excludeWildcardRef = (ctx, _expr) => {
196
194
  }
197
195
  };
198
196
  const resolveElements = (ctx, expr) => {
199
- if (Expression.isWildcard(expr)) {
197
+ if (Expr.isWildcard(expr)) {
200
198
  if (ctx.root) {
201
199
  return [ctx.index.find(ctx.root)];
202
200
  }
@@ -204,7 +202,7 @@ const resolveElements = (ctx, expr) => {
204
202
  return ctx.index.rootElements();
205
203
  }
206
204
  }
207
- if (Expression.isElementKindExpr(expr)) {
205
+ if (Expr.isElementKindExpr(expr)) {
208
206
  return ctx.index.elements.filter(el => {
209
207
  if (expr.isEqual) {
210
208
  return el.kind === expr.elementKind;
@@ -212,7 +210,7 @@ const resolveElements = (ctx, expr) => {
212
210
  return el.kind !== expr.elementKind;
213
211
  });
214
212
  }
215
- if (Expression.isElementTagExpr(expr)) {
213
+ if (Expr.isElementTagExpr(expr)) {
216
214
  return ctx.index.elements.filter(el => {
217
215
  const tags = el.tags;
218
216
  if (expr.isEqual) {
@@ -229,7 +227,7 @@ const resolveElements = (ctx, expr) => {
229
227
  }
230
228
  };
231
229
  const includeIncomingExpr = (ctx, expr) => {
232
- if (Expression.isWildcard(expr.incoming) && !ctx.root) {
230
+ if (Expr.isWildcard(expr.incoming) && !ctx.root) {
233
231
  return ctx;
234
232
  }
235
233
  const elements = resolveElements(ctx, expr.incoming);
@@ -251,7 +249,7 @@ const includeIncomingExpr = (ctx, expr) => {
251
249
  });
252
250
  };
253
251
  const excludeIncomingExpr = (ctx, expr) => {
254
- if (Expression.isWildcard(expr.incoming) && !ctx.root) {
252
+ if (Expr.isWildcard(expr.incoming) && !ctx.root) {
255
253
  return ctx;
256
254
  }
257
255
  const elements = resolveElements(ctx, expr.incoming);
@@ -268,7 +266,7 @@ const excludeIncomingExpr = (ctx, expr) => {
268
266
  return ctx;
269
267
  };
270
268
  const includeOutgoingExpr = (ctx, expr) => {
271
- if (Expression.isWildcard(expr.outgoing) && !ctx.root) {
269
+ if (Expr.isWildcard(expr.outgoing) && !ctx.root) {
272
270
  return ctx;
273
271
  }
274
272
  const elements = resolveElements(ctx, expr.outgoing);
@@ -290,7 +288,7 @@ const includeOutgoingExpr = (ctx, expr) => {
290
288
  });
291
289
  };
292
290
  const excludeOutgoingExpr = (ctx, expr) => {
293
- if (Expression.isWildcard(expr.outgoing) && !ctx.root) {
291
+ if (Expr.isWildcard(expr.outgoing) && !ctx.root) {
294
292
  return ctx;
295
293
  }
296
294
  const elements = resolveElements(ctx, expr.outgoing);
@@ -307,7 +305,7 @@ const excludeOutgoingExpr = (ctx, expr) => {
307
305
  return ctx;
308
306
  };
309
307
  const includeInOutExpr = (ctx, expr) => {
310
- if (Expression.isWildcard(expr.inout) && !ctx.root) {
308
+ if (Expr.isWildcard(expr.inout) && !ctx.root) {
311
309
  return ctx;
312
310
  }
313
311
  const targets = resolveElements(ctx, expr.inout);
@@ -329,7 +327,7 @@ const includeInOutExpr = (ctx, expr) => {
329
327
  });
330
328
  };
331
329
  const excludeInOutExpr = (ctx, expr) => {
332
- if (Expression.isWildcard(expr.inout) && !ctx.root) {
330
+ if (Expr.isWildcard(expr.inout) && !ctx.root) {
333
331
  return ctx;
334
332
  }
335
333
  const targets = resolveElements(ctx, expr.inout);
@@ -403,31 +401,31 @@ export function computeElementView(view, index) {
403
401
  }
404
402
  for (const { isInclude, exprs } of rulesInclude) {
405
403
  for (const expr of exprs) {
406
- if (Expression.isElementKindExpr(expr) || Expression.isElementTagExpr(expr)) {
404
+ if (Expr.isElementKindExpr(expr) || Expr.isElementTagExpr(expr)) {
407
405
  ctx = isInclude ? includeElementKindOrTag(ctx, expr) : excludeElementKindOrTag(ctx, expr);
408
406
  continue;
409
407
  }
410
- if (Expression.isElementRef(expr)) {
408
+ if (Expr.isElementRef(expr)) {
411
409
  ctx = isInclude ? includeElementRef(ctx, expr) : excludeElementRef(ctx, expr);
412
410
  continue;
413
411
  }
414
- if (Expression.isWildcard(expr)) {
412
+ if (Expr.isWildcard(expr)) {
415
413
  ctx = isInclude ? includeWildcardRef(ctx, expr) : excludeWildcardRef(ctx, expr);
416
414
  continue;
417
415
  }
418
- if (Expression.isIncoming(expr)) {
416
+ if (Expr.isIncoming(expr)) {
419
417
  ctx = isInclude ? includeIncomingExpr(ctx, expr) : excludeIncomingExpr(ctx, expr);
420
418
  continue;
421
419
  }
422
- if (Expression.isOutgoing(expr)) {
420
+ if (Expr.isOutgoing(expr)) {
423
421
  ctx = isInclude ? includeOutgoingExpr(ctx, expr) : excludeOutgoingExpr(ctx, expr);
424
422
  continue;
425
423
  }
426
- if (Expression.isInOut(expr)) {
424
+ if (Expr.isInOut(expr)) {
427
425
  ctx = isInclude ? includeInOutExpr(ctx, expr) : excludeInOutExpr(ctx, expr);
428
426
  continue;
429
427
  }
430
- if (Expression.isRelation(expr)) {
428
+ if (Expr.isRelation(expr)) {
431
429
  ctx = isInclude ? includeRelationExpr(ctx, expr) : excludeRelationExpr(ctx, expr);
432
430
  continue;
433
431
  }
@@ -453,7 +451,7 @@ export function computeElementView(view, index) {
453
451
  idx = resolvedRelations.findIndex(findPredicate);
454
452
  }
455
453
  }
456
- const nodesreg = transformToNodes(elements, index, view.id);
454
+ const nodesreg = transformToNodes(elements);
457
455
  const edges = edgeBuilder.build().map(edge => {
458
456
  while (edge.parent) {
459
457
  if (nodesreg.has(edge.parent)) {
@@ -1,10 +1,10 @@
1
1
  import { ModelIndex } from '../model-index';
2
- import type { Element, ElementView, Fqn, Relation, RelationID, ViewID, ComputeResult } from '../types';
3
- export declare function computeView<V extends ElementView>(view: V, index: ModelIndex): ComputeResult<V>;
2
+ import type { ComputeResult, Element, ElementView, Fqn, Relation, RelationID, ViewID } from '../types';
3
+ export declare function computeView<V extends ElementView>(view: V, index: ModelIndex): ComputeResult<V> | null;
4
4
  type InputModel<V extends ElementView> = {
5
5
  elements: Record<Fqn, Element>;
6
6
  relations: Record<RelationID, Relation>;
7
- views: Record<ViewID, V>;
7
+ views: V[];
8
8
  };
9
9
  type OutputModel<V extends ElementView> = {
10
10
  elements: Record<Fqn, Element>;
@@ -12,5 +12,6 @@ type OutputModel<V extends ElementView> = {
12
12
  views: Record<ViewID, ComputeResult<V>>;
13
13
  };
14
14
  export declare function computeViews<V extends ElementView>(model: InputModel<V>): OutputModel<V>;
15
+ export declare function assignNavigateTo<V extends ElementView, R extends ComputeResult<V>[]>(views: R): R;
15
16
  export {};
16
17
  //# sourceMappingURL=compute.d.ts.map
@@ -1,15 +1,43 @@
1
- import { map } from 'rambdax';
1
+ import { compact, find, map, mapToObj } from 'remeda';
2
2
  import { ModelIndex } from '../model-index';
3
3
  import { computeElementView } from './compute-element-view';
4
4
  export function computeView(view, index) {
5
- return computeElementView(view, index);
5
+ try {
6
+ return computeElementView(view, index);
7
+ }
8
+ catch (e) {
9
+ console.error(e);
10
+ return null;
11
+ }
6
12
  }
7
13
  export function computeViews(model) {
8
14
  const index = ModelIndex.from(model);
15
+ const computedViews = compact(map(model.views, view => computeView(view, index)));
9
16
  return {
10
17
  elements: model.elements,
11
18
  relations: model.relations,
12
- views: map(v => computeElementView(v, index), model.views)
19
+ views: mapToObj(computedViews, view => [view.id, view])
13
20
  };
14
21
  }
22
+ export function assignNavigateTo(views) {
23
+ const allElementViews = new Map();
24
+ for (const { id, viewOf } of views) {
25
+ if (viewOf) {
26
+ const viewsOf = allElementViews.get(viewOf) ?? [];
27
+ viewsOf.push(id);
28
+ allElementViews.set(viewOf, viewsOf);
29
+ }
30
+ }
31
+ // set default navigateTo
32
+ for (const { id, nodes } of views) {
33
+ for (const node of nodes) {
34
+ // find first element view that is not the current one
35
+ const navigateTo = find(allElementViews.get(node.id) ?? [], v => v !== id);
36
+ if (navigateTo) {
37
+ node.navigateTo = navigateTo;
38
+ }
39
+ }
40
+ }
41
+ return views;
42
+ }
15
43
  //# sourceMappingURL=compute.js.map
@@ -1,3 +1,3 @@
1
- export { computeView, computeViews } from './compute';
1
+ export { computeView, computeViews, assignNavigateTo } from './compute';
2
2
  export type * from '../types/computed-view';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,2 @@
1
- export { computeView, computeViews } from './compute';
1
+ export { computeView, computeViews, assignNavigateTo } from './compute';
2
2
  //# sourceMappingURL=index.js.map
@@ -1,18 +1,15 @@
1
- import type { Predicate } from 'rambdax';
2
- import type { Element, ElementView, Fqn, Relation, RelationID, ViewID } from '../types';
1
+ import type { Element, Fqn, Relation, RelationID } from '../types';
3
2
  type ModelInput = {
4
3
  elements: Record<Fqn, Element>;
5
4
  relations: Record<RelationID, Relation>;
6
- views: Record<ViewID, ElementView>;
7
5
  };
8
- export declare class ModelIndex {
6
+ export default class ModelIndex {
9
7
  private root;
10
8
  private _elements;
11
9
  private _relations;
12
- private _defaultElementView;
13
10
  get relations(): Relation[];
14
- filterRelations: (predicate: Predicate<Relation>) => Relation[];
15
- static from({ elements, relations, views }: ModelInput): ModelIndex;
11
+ filterRelations: (predicate: (r: Relation) => boolean) => Relation[];
12
+ static from({ elements, relations }: ModelInput): ModelIndex;
16
13
  addElement(el: Element): void;
17
14
  private locateTrie;
18
15
  find: (id: Fqn) => Element;
@@ -25,7 +22,6 @@ export declare class ModelIndex {
25
22
  rootElements(): Element[];
26
23
  get elements(): Element[];
27
24
  addRelation(rel: Relation): void;
28
- defaultViewOf: (id: Fqn) => ViewID[];
29
25
  }
30
26
  export {};
31
27
  //# sourceMappingURL=ModelIndex.d.ts.map
@@ -1,4 +1,4 @@
1
- import { values } from 'rambdax';
1
+ import { values } from 'remeda';
2
2
  import invariant from 'tiny-invariant';
3
3
  import { parentFqn } from '../utils/fqn';
4
4
  function childrenOf(trie) {
@@ -13,39 +13,34 @@ function childrenOf(trie) {
13
13
  function asPath(id) {
14
14
  return id.split('.');
15
15
  }
16
- export class ModelIndex {
16
+ export default class ModelIndex {
17
17
  root = {
18
18
  children: {}
19
19
  };
20
- _elements = new Set();
20
+ _elements = new Map();
21
21
  _relations = new Map();
22
- _defaultElementView = new Map();
23
22
  // private _taggedElements = new Map<Tag, Set<Element>>()
24
23
  // private _taggedRelations = new Map<Tag, Set<Relation>>()
25
24
  get relations() {
26
- return [...this._relations.values()];
25
+ return Array.from(this._relations.values());
27
26
  }
28
27
  filterRelations = (predicate) => {
29
- return [...this._relations.values()].filter(predicate);
28
+ return this.relations.filter(predicate);
30
29
  };
31
- static from({ elements, relations, views }) {
30
+ static from({ elements, relations }) {
32
31
  const index = new ModelIndex();
33
- for (const el of Object.values(elements)) {
32
+ for (const el of values(elements)) {
34
33
  index.addElement(el);
35
34
  }
36
- for (const rel of Object.values(relations)) {
35
+ for (const rel of values(relations)) {
37
36
  index.addRelation(rel);
38
37
  }
39
- for (const { id, viewOf } of Object.values(views)) {
40
- if (viewOf) {
41
- const viewsOf = index._defaultElementView.get(viewOf) ?? [];
42
- viewsOf.push(id);
43
- index._defaultElementView.set(viewOf, viewsOf);
44
- }
45
- }
46
38
  return index;
47
39
  }
48
40
  addElement(el) {
41
+ if (this._elements.has(el.id)) {
42
+ throw new Error(`Element already exists with id ${el.id}`);
43
+ }
49
44
  const path = asPath(el.id);
50
45
  let scope = this.root;
51
46
  for (const name of path) {
@@ -56,7 +51,7 @@ export class ModelIndex {
56
51
  scope = next;
57
52
  }
58
53
  scope.el = el;
59
- this._elements.add(el);
54
+ this._elements.set(el.id, el);
60
55
  }
61
56
  locateTrie = (id) => {
62
57
  let scope = this.root;
@@ -68,11 +63,11 @@ export class ModelIndex {
68
63
  return scope;
69
64
  };
70
65
  find = (id) => {
71
- const trie = this.locateTrie(id);
72
- if (!trie.el) {
73
- throw new Error(`Invalid index, element not found at path ${id}`);
66
+ const el = this._elements.get(id);
67
+ if (!el) {
68
+ throw new Error(`Element not found ${id}`);
74
69
  }
75
- return trie.el;
70
+ return el;
76
71
  };
77
72
  children = (id) => {
78
73
  return childrenOf(this.locateTrie(id));
@@ -98,7 +93,9 @@ export class ModelIndex {
98
93
  let trie = this.root;
99
94
  while (name) {
100
95
  const next = trie.children[name];
101
- invariant(next, `Invalid index, Element not found at path ${name} of ${id}`);
96
+ if (!next) {
97
+ throw new Error(`Invalid index, Element not found at path ${name} of ${id}`);
98
+ }
102
99
  trie = next;
103
100
  if (!trie.el) {
104
101
  throw new Error(`invalid index, no element ${name} found in ${id}`);
@@ -121,15 +118,19 @@ export class ModelIndex {
121
118
  return childrenOf(this.root);
122
119
  }
123
120
  get elements() {
124
- return [...this._elements];
121
+ return Array.from(this._elements.values());
125
122
  }
126
123
  // hasElement(fqn: Fqn): boolean {
127
124
  // return fqn in this._elements
128
125
  // }
129
126
  addRelation(rel) {
130
127
  // Validate source and target
131
- this.locateTrie(rel.source);
132
- this.locateTrie(rel.target);
128
+ if (!this._elements.has(rel.source)) {
129
+ throw new Error(`Invalid index, source of relation not found ${rel.source}`);
130
+ }
131
+ if (!this._elements.has(rel.target)) {
132
+ throw new Error(`Invalid index, target of relation not found ${rel.target}`);
133
+ }
133
134
  this._relations.set(rel.id, rel);
134
135
  // for (const tag of rel.tags) {
135
136
  // const tagged = this._taggedRelations.get(tag) ?? new Set()
@@ -137,8 +138,5 @@ export class ModelIndex {
137
138
  // this._taggedRelations.set(tag, tagged)
138
139
  // }
139
140
  }
140
- defaultViewOf = (id) => {
141
- return this._defaultElementView.get(id) ?? [];
142
- };
143
141
  }
144
142
  //# sourceMappingURL=ModelIndex.js.map
@@ -1,2 +1,2 @@
1
- export * from './ModelIndex';
1
+ export { default as ModelIndex } from './ModelIndex';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,2 @@
1
- export * from './ModelIndex';
1
+ export { default as ModelIndex } from './ModelIndex';
2
2
  //# sourceMappingURL=index.js.map
package/dist/utils/fqn.js CHANGED
@@ -1,4 +1,4 @@
1
- import { anyPass } from 'rambdax';
1
+ import { anyPass } from 'remeda';
2
2
  import { isString } from './guards';
3
3
  export function nameFromFqn(fqn) {
4
4
  const lastDot = fqn.lastIndexOf('.');
@@ -3,16 +3,17 @@ type Relation = {
3
3
  source: Fqn;
4
4
  target: Fqn;
5
5
  };
6
+ type RelationPredicate = (rel: Relation) => boolean;
6
7
  export declare const compareRelations: <T extends {
7
8
  source: Fqn;
8
9
  target: Fqn;
9
10
  }>(a: T, b: T) => number;
10
- export declare const isInside: (parent: Fqn) => (rel: Relation) => boolean;
11
- export declare const isBetween: (source: Fqn, target: Fqn) => (rel: Relation) => boolean;
12
- export declare const isAnyBetween: (source: Fqn, target: Fqn) => import("rambdax").Predicate<Relation>;
13
- export declare const isIncoming: (target: Fqn) => (rel: Relation) => boolean;
14
- export declare const isOutgoing: (source: Fqn) => (rel: Relation) => boolean;
15
- export declare const isAnyInOut: (source: Fqn) => import("rambdax").Predicate<Relation>;
11
+ export declare const isInside: (parent: Fqn) => RelationPredicate;
12
+ export declare const isBetween: (source: Fqn, target: Fqn) => RelationPredicate;
13
+ export declare const isAnyBetween: (source: Fqn, target: Fqn) => RelationPredicate;
14
+ export declare const isIncoming: (target: Fqn) => RelationPredicate;
15
+ export declare const isOutgoing: (source: Fqn) => RelationPredicate;
16
+ export declare const isAnyInOut: (source: Fqn) => RelationPredicate;
16
17
  export declare const hasRelation: (rel: Relation) => (element: Element) => boolean;
17
18
  export {};
18
19
  //# sourceMappingURL=relations.d.ts.map
@@ -1,4 +1,4 @@
1
- import { either } from 'rambdax';
1
+ import { anyPass } from 'remeda';
2
2
  import { compareFqnHierarchically, isAncestor } from './fqn';
3
3
  export const compareRelations = (a, b) => {
4
4
  return (compareFqnHierarchically(a.source, b.source) || compareFqnHierarchically(a.target, b.target));
@@ -18,7 +18,7 @@ export const isBetween = (source, target) => {
18
18
  };
19
19
  };
20
20
  export const isAnyBetween = (source, target) => {
21
- return either(isBetween(source, target), isBetween(target, source));
21
+ return anyPass([isBetween(source, target), isBetween(target, source)]);
22
22
  };
23
23
  export const isIncoming = (target) => {
24
24
  const targetPrefix = target + '.';
@@ -33,7 +33,7 @@ export const isOutgoing = (source) => {
33
33
  };
34
34
  };
35
35
  export const isAnyInOut = (source) => {
36
- return either(isIncoming(source), isOutgoing(source));
36
+ return anyPass([isIncoming(source), isOutgoing(source)]);
37
37
  };
38
38
  export const hasRelation = (rel) => {
39
39
  return (element) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@likec4/core",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
4
4
  "license": "MIT",
5
5
  "bugs": "https://github.com/likec4/likec4/issues",
6
6
  "homepage": "https://likec4.dev",
@@ -92,6 +92,7 @@
92
92
  "dependencies": {
93
93
  "@dagrejs/graphlib": "^2.1.13",
94
94
  "rambdax": "^9.1.1",
95
+ "remeda": "^1.23.0",
95
96
  "tiny-invariant": "^1.3.1"
96
97
  },
97
98
  "devDependencies": {