@istar-ts/core 0.13.3 → 0.13.5

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.
package/dist/index.d.cts CHANGED
@@ -715,7 +715,17 @@ export interface SourceLayout {
715
715
  readonly keys: readonly string[];
716
716
  /** `display` ids in file order. */
717
717
  readonly display: readonly string[];
718
+ /**
719
+ * Each element's, link's and hub entry's keys in file order, by id (a hub's entry under
720
+ * `hubEntryLayoutKey(id)`), and the diagram's under `DIAGRAM_LAYOUT_KEY`. Files written by
721
+ * other tools order keys differently from piStar; this keeps them as they were.
722
+ */
723
+ readonly objects?: ReadonlyMap<string, readonly string[]>;
718
724
  }
725
+ /** The `objects` key of a hub's entry (`{ hub, starts, end }`) in a `SourceLayout`. */
726
+ export declare function hubEntryLayoutKey(hubId: string): string;
727
+ /** The `objects` key of the diagram in a `SourceLayout`. */
728
+ export declare const DIAGRAM_LAYOUT_KEY = "\0diagram";
719
729
  /** The key order recorded when `model` was parsed, if it came from `parsePistar`. */
720
730
  export declare function getSourceLayout<EK extends string, LK extends string>(model: IstarModel<EK, LK>): SourceLayout | undefined;
721
731
  /**
@@ -767,8 +777,8 @@ export interface ToPistarOptions {
767
777
  metamodel?: AnyMetamodel;
768
778
  /**
769
779
  * Value for `saveDate`. Defaults to the model's own `saveDate` (so a load/save round trip is
770
- * lossless), or the current time for models that never had one. Pass `new Date()` when
771
- * saving an edited model, as piStar does.
780
+ * lossless); a parsed file that had none keeps none, and a model that was never a file gets
781
+ * the current time. Pass `new Date()` when saving an edited model, as piStar does.
772
782
  */
773
783
  saveDate?: string | Date;
774
784
  }
package/dist/index.d.ts CHANGED
@@ -715,7 +715,17 @@ export interface SourceLayout {
715
715
  readonly keys: readonly string[];
716
716
  /** `display` ids in file order. */
717
717
  readonly display: readonly string[];
718
+ /**
719
+ * Each element's, link's and hub entry's keys in file order, by id (a hub's entry under
720
+ * `hubEntryLayoutKey(id)`), and the diagram's under `DIAGRAM_LAYOUT_KEY`. Files written by
721
+ * other tools order keys differently from piStar; this keeps them as they were.
722
+ */
723
+ readonly objects?: ReadonlyMap<string, readonly string[]>;
718
724
  }
725
+ /** The `objects` key of a hub's entry (`{ hub, starts, end }`) in a `SourceLayout`. */
726
+ export declare function hubEntryLayoutKey(hubId: string): string;
727
+ /** The `objects` key of the diagram in a `SourceLayout`. */
728
+ export declare const DIAGRAM_LAYOUT_KEY = "\0diagram";
719
729
  /** The key order recorded when `model` was parsed, if it came from `parsePistar`. */
720
730
  export declare function getSourceLayout<EK extends string, LK extends string>(model: IstarModel<EK, LK>): SourceLayout | undefined;
721
731
  /**
@@ -767,8 +777,8 @@ export interface ToPistarOptions {
767
777
  metamodel?: AnyMetamodel;
768
778
  /**
769
779
  * Value for `saveDate`. Defaults to the model's own `saveDate` (so a load/save round trip is
770
- * lossless), or the current time for models that never had one. Pass `new Date()` when
771
- * saving an edited model, as piStar does.
780
+ * lossless); a parsed file that had none keeps none, and a model that was never a file gets
781
+ * the current time. Pass `new Date()` when saving an edited model, as piStar does.
772
782
  */
773
783
  saveDate?: string | Date;
774
784
  }
package/dist/index.js CHANGED
@@ -550,6 +550,26 @@ function elementBehavesLike(metamodel, kind, ancestor) {
550
550
  return false;
551
551
  }
552
552
  //#endregion
553
+ //#region src/shared.ts
554
+ /**
555
+ * Registries the library keeps beside models (their metamodel, their file's layout) are
556
+ * shared across every copy of `@istar-ts/core` loaded in a process. A bundle may load the
557
+ * package twice, once as ESM and once as CommonJS (an application parsing with the ESM build
558
+ * while a CommonJS dependency reads the model with the CJS build): a module-level WeakMap in
559
+ * each copy would make a model parsed by one copy unknown to the other, so `metamodelOf`
560
+ * would fall back to iStar 2.0 there. The registries live on `globalThis` under one symbol
561
+ * instead, keyed by name, and every copy finds the same ones.
562
+ */
563
+ const REGISTRIES = Symbol.for("@istar-ts/core/registries");
564
+ /** The WeakMap named `name`, the same object in every copy of the library in this process. */
565
+ function sharedWeakMap(name) {
566
+ const host = globalThis;
567
+ const registries = host[REGISTRIES] ??= /* @__PURE__ */ new Map();
568
+ let map = registries.get(name);
569
+ if (!map) registries.set(name, map = /* @__PURE__ */ new WeakMap());
570
+ return map;
571
+ }
572
+ //#endregion
553
573
  //#region src/model.ts
554
574
  /** Tool identifier written by piStar 2.1.0, whose file format this library emulates. */
555
575
  const PISTAR_TOOL = "pistar.2.1.0";
@@ -580,7 +600,7 @@ const DEFAULT_DIAGRAM = {
580
600
  * so a model's shape (and `JSON.stringify`, deep equality, snapshots) is the same as before
581
601
  * metamodels existed. Operations and the store carry it to every model they derive.
582
602
  */
583
- const metamodels = /* @__PURE__ */ new WeakMap();
603
+ const metamodels = sharedWeakMap("metamodels");
584
604
  /**
585
605
  * Associates `metamodel` with `model` and returns the same model, typed for it. Use when a
586
606
  * model was built without the library's operations (e.g. with an object spread), which loses
@@ -595,7 +615,7 @@ function withMetamodel(model, metamodel) {
595
615
  * For models whose file declares part of their metamodel (see `withFileMetamodel`): the
596
616
  * metamodel the host supplied, without the file's kinds.
597
617
  */
598
- const hostMetamodels = /* @__PURE__ */ new WeakMap();
618
+ const hostMetamodels = sharedWeakMap("hostMetamodels");
599
619
  /** @internal Records the host part of a model's metamodel (used by serialization). */
600
620
  function setHostMetamodel(model, metamodel) {
601
621
  if (metamodel) hostMetamodels.set(model, metamodel);
@@ -1234,7 +1254,13 @@ var PistarParseError = class extends Error {
1234
1254
  var PistarWriteError = class extends Error {
1235
1255
  name = "PistarWriteError";
1236
1256
  };
1237
- const layouts = /* @__PURE__ */ new WeakMap();
1257
+ /** The `objects` key of a hub's entry (`{ hub, starts, end }`) in a `SourceLayout`. */
1258
+ function hubEntryLayoutKey(hubId) {
1259
+ return `${hubId}\u0000entry`;
1260
+ }
1261
+ /** The `objects` key of the diagram in a `SourceLayout`. */
1262
+ const DIAGRAM_LAYOUT_KEY = "\0diagram";
1263
+ const layouts = sharedWeakMap("layouts");
1238
1264
  /** The key order recorded when `model` was parsed, if it came from `parsePistar`. */
1239
1265
  function getSourceLayout(model) {
1240
1266
  return layouts.get(model);
@@ -1341,6 +1367,7 @@ function parsePistar(input, options = {}) {
1341
1367
  usedDisplay.add(id);
1342
1368
  return entry;
1343
1369
  };
1370
+ const objects = /* @__PURE__ */ new Map();
1344
1371
  const readElement = (raw, where, known, extras) => {
1345
1372
  if (!isRecord(raw)) throw new PistarParseError(`${where}: expected an object`);
1346
1373
  const { id, type } = raw;
@@ -1360,6 +1387,7 @@ function parsePistar(input, options = {}) {
1360
1387
  ...optional("extra", extraOf(raw, known))
1361
1388
  };
1362
1389
  elements.set(id, element);
1390
+ objects.set(id, Object.keys(raw));
1363
1391
  return element;
1364
1392
  };
1365
1393
  asArray(json.actors, "actors").forEach((raw, i) => {
@@ -1404,12 +1432,14 @@ function parsePistar(input, options = {}) {
1404
1432
  ...optional("extra", extraOf(raw, LINK_KEYS))
1405
1433
  };
1406
1434
  links.set(id, link);
1435
+ objects.set(id, Object.keys(raw));
1407
1436
  return link;
1408
1437
  };
1409
1438
  const readHubEntry = (raw, where) => {
1410
1439
  const hub = readElement(raw.hub, `${where}.hub`, HUB_KEYS, {});
1411
1440
  const hubKind = metamodel.elements.get(hub.kind);
1412
1441
  if (hubKind?.role !== "hub") throw new PistarParseError(`${where}.hub (${hub.id}): ${hub.kind} is not a hub kind`);
1442
+ objects.set(hubEntryLayoutKey(hub.id), Object.keys(raw));
1413
1443
  const entryExtra = extraOf(raw, HUB_ENTRY_KEYS);
1414
1444
  if (entryExtra) elements.set(hub.id, {
1415
1445
  ...hub,
@@ -1447,6 +1477,7 @@ function parsePistar(input, options = {}) {
1447
1477
  ...isRecord(d.customProperties) ? { customProperties: d.customProperties } : {},
1448
1478
  ...optional("extra", extraOf(d, DIAGRAM_KEYS))
1449
1479
  };
1480
+ objects.set(DIAGRAM_LAYOUT_KEY, Object.keys(d));
1450
1481
  }
1451
1482
  const model = {
1452
1483
  elements,
@@ -1460,7 +1491,8 @@ function parsePistar(input, options = {}) {
1460
1491
  };
1461
1492
  layouts.set(model, {
1462
1493
  keys: Object.keys(json),
1463
- display: Object.keys(display)
1494
+ display: Object.keys(display),
1495
+ objects
1464
1496
  });
1465
1497
  setHostMetamodel(model, host);
1466
1498
  return withMetamodel(model, metamodel);
@@ -1533,6 +1565,19 @@ function pistarTypeOf(type, kind, metamodel) {
1533
1565
  if (type === void 0) throw new PistarWriteError(`kind "${kind}" is not part of ${metamodel.name}; pass its metamodel (toPistar(model, { metamodel }) or withMetamodel)`);
1534
1566
  return type;
1535
1567
  }
1568
+ /**
1569
+ * `value` with its keys in the order `keys` gives (those present), then the rest as built (piStar's
1570
+ * order): an object read from a file is written back with the file's key order, and keys the
1571
+ * file lacked follow.
1572
+ */
1573
+ function inSourceOrder(value, keys) {
1574
+ if (!keys) return value;
1575
+ const result = {};
1576
+ const record = value;
1577
+ for (const key of keys) if (key in record) result[key] = record[key];
1578
+ for (const key of Object.keys(record)) if (!(key in result)) result[key] = record[key];
1579
+ return result;
1580
+ }
1536
1581
  function elementJson(element, metamodel) {
1537
1582
  return {
1538
1583
  id: element.id,
@@ -1553,38 +1598,40 @@ function toPistarObject(input, options = {}) {
1553
1598
  const orphans = [];
1554
1599
  const dependencies = [];
1555
1600
  const children = /* @__PURE__ */ new Map();
1601
+ const layout = layouts.get(model);
1602
+ const sourceOrder = (id, value) => inSourceOrder(value, layout?.objects?.get(id));
1556
1603
  for (const element of model.elements.values()) {
1557
1604
  if (isHub(element)) continue;
1558
1605
  if (element.parent !== void 0 && !isActorKind(element.kind)) {
1559
1606
  let list = children.get(element.parent);
1560
1607
  if (!list) children.set(element.parent, list = []);
1561
- list.push({
1608
+ list.push(sourceOrder(element.id, {
1562
1609
  ...elementJson(element, metamodel),
1563
1610
  ...element.extra
1564
- });
1611
+ }));
1565
1612
  }
1566
1613
  }
1567
1614
  for (const element of model.elements.values()) {
1568
1615
  if (isHub(element)) continue;
1569
- if (isActorKind(element.kind)) actors.push({
1616
+ if (isActorKind(element.kind)) actors.push(sourceOrder(element.id, {
1570
1617
  ...elementJson(element, metamodel),
1571
1618
  nodes: children.get(element.id) ?? [],
1572
1619
  ...element.extra
1573
- });
1620
+ }));
1574
1621
  else if (element.isDependum) {
1575
1622
  const { inbound, outbound } = dependencyLinksOf(model, element.id);
1576
- dependencies.push({
1623
+ dependencies.push(sourceOrder(element.id, {
1577
1624
  ...elementJson(element, metamodel),
1578
1625
  source: inbound?.source ?? element.dependency?.source ?? "",
1579
1626
  target: outbound?.target ?? element.dependency?.target ?? "",
1580
1627
  ...element.extra
1581
- });
1582
- } else if (element.parent === void 0 || !model.elements.has(element.parent)) orphans.push({
1628
+ }));
1629
+ } else if (element.parent === void 0 || !model.elements.has(element.parent)) orphans.push(sourceOrder(element.id, {
1583
1630
  ...elementJson(element, metamodel),
1584
1631
  ...element.extra
1585
- });
1632
+ }));
1586
1633
  }
1587
- const linkJson = (link) => ({
1634
+ const linkJson = (link) => sourceOrder(link.id, {
1588
1635
  id: link.id,
1589
1636
  type: pistarTypeOf(metamodel.links.get(link.kind)?.pistarType, link.kind, metamodel),
1590
1637
  source: link.source,
@@ -1616,19 +1663,19 @@ function toPistarObject(input, options = {}) {
1616
1663
  const hubEntry = (hub) => {
1617
1664
  const parts = hubs.get(hub.id) ?? { legs: [] };
1618
1665
  const { [HUB_ENTRY_KEY]: entryExtra, ...hubExtra } = hub.extra ?? {};
1619
- return {
1620
- hub: {
1666
+ return sourceOrder(hubEntryLayoutKey(hub.id), {
1667
+ hub: sourceOrder(hub.id, {
1621
1668
  id: hub.id,
1622
1669
  type: pistarTypeOf(metamodel.elements.get(hub.kind)?.pistarType, hub.kind, metamodel),
1623
1670
  x: hub.x,
1624
1671
  y: hub.y,
1625
1672
  ...hub.customProperties ? { customProperties: { ...hub.customProperties } } : {},
1626
1673
  ...hubExtra
1627
- },
1674
+ }),
1628
1675
  starts: parts.legs.map(linkJson),
1629
1676
  ...parts.end ? { end: linkJson(parts.end) } : {},
1630
1677
  ...isRecord(entryExtra) ? entryExtra : {}
1631
- };
1678
+ });
1632
1679
  };
1633
1680
  const links = [];
1634
1681
  const written = /* @__PURE__ */ new Set();
@@ -1642,7 +1689,6 @@ function toPistarObject(input, options = {}) {
1642
1689
  }
1643
1690
  }
1644
1691
  for (const element of model.elements.values()) if (isHub(element) && !written.has(element.id)) links.push(hubEntry(element));
1645
- const layout = layouts.get(model);
1646
1692
  const display = {};
1647
1693
  const displayOf = (id) => {
1648
1694
  const owner = model.elements.get(id) ?? model.links.get(id);
@@ -1657,15 +1703,16 @@ function toPistarObject(input, options = {}) {
1657
1703
  for (const [id, entry] of Object.entries(model.extraDisplay ?? {})) if (!(id in display)) display[id] = entry;
1658
1704
  let saveDate;
1659
1705
  if (options.saveDate instanceof Date) saveDate = formatDate(options.saveDate);
1660
- else saveDate = options.saveDate ?? model.saveDate ?? formatDate(/* @__PURE__ */ new Date());
1706
+ else if (options.saveDate !== void 0 || model.saveDate !== void 0) saveDate = options.saveDate ?? model.saveDate;
1707
+ else if (!layout || layout.keys.includes("saveDate")) saveDate = formatDate(/* @__PURE__ */ new Date());
1661
1708
  const d = model.diagram;
1662
- const diagram = d && {
1709
+ const diagram = d && sourceOrder("\0diagram", {
1663
1710
  ...d.width !== void 0 ? { width: d.width } : {},
1664
1711
  ...d.height !== void 0 ? { height: d.height } : {},
1665
1712
  ...d.name !== void 0 ? { name: d.name } : {},
1666
1713
  ...d.customProperties ? { customProperties: { ...d.customProperties } } : {},
1667
1714
  ...d.extra
1668
- };
1715
+ });
1669
1716
  const values = {
1670
1717
  ...model.extra,
1671
1718
  actors,
@@ -2843,6 +2890,6 @@ function fromNodeIdDiagnostics(diagnostics) {
2843
2890
  return result;
2844
2891
  }
2845
2892
  //#endregion
2846
- export { ACTOR_KINDS, ACTOR_LINK_KINDS, CONTRIBUTION_LABELS, DEFAULT_DIAGRAM, DEFAULT_ELEMENT_SIZE, DEPENDENCY_LINK_KINDS, DIAGNOSTIC_SEVERITIES, DSM3_FILE_KEYS, DSM3_TOOL, ELEMENT_KINDS, FILE_METAMODEL_KEY, HUB_ENTRY_KEY, ISTAR_2_0, ISTAR_2_0_DECLARATION, ISTAR_PREFIX, ISTAR_VERSION, LINK_KINDS, LINK_KIND_INFO, LINK_SELECTOR, METAMODEL_VERSION, MetamodelError, ModelOperationError, NODE_KINDS, NODE_KIND_INFO, NODE_LINK_KINDS, PISTAR_TOOL, PistarParseError, PistarWriteError, addDependency, addElement, addLeg, boolean, canLink, childrenOf, compareSeverity, connect, createDiagnosticsStore, createEmptyModel, createModelStore, defaultHubPosition, defaultIdGenerator, defineMetamodel, defineMetamodelExtension, defineProperties, dependencyEnds, dependencyLinksOf, detectFramework, detectPrefix, disconnect, effectiveElementKind, effectiveLinkKind, elementBehavesLike, elementCenter, elementKindDefinition, enumOf, extendMetamodel, fileMetamodelExtension, fileMetamodelOf, fromLspDiagnostic, fromLspDiagnostics, fromNodeIdDiagnostic, fromNodeIdDiagnostics, getSourceLayout, groupDiagnostics, hostMetamodelOf, hubLinkOf, hubOfLink, inheritMetamodel, inheritSourceLayout, isActor, isActorIn, isActorKind, isActorKindIn, isActorLinkKind, isContributionLabel, isDependencyLinkKind, isElementKind, isHubElement, isLinkKind, isLinkRef, isNode, isNodeIn, isNodeKind, isNodeKindIn, isNodeLinkKind, linkBehavesLike, linkKindDefinition, linkTargetOf, linksOf, mergeDiagnostics, metamodelOf, moveElement, nestElement, number, parsePistar, prop, removeElement, removeElements, removeLeg, setHostMetamodel, shortKindName, string, targetsLinks, toPistar, toPistarObject, updateDiagram, updateElement, updateLink, validateFileMetamodel, validateModel, validateModelProperties, withFileMetamodel, withMetamodel, worstDiagnosticSeverity };
2893
+ export { ACTOR_KINDS, ACTOR_LINK_KINDS, CONTRIBUTION_LABELS, DEFAULT_DIAGRAM, DEFAULT_ELEMENT_SIZE, DEPENDENCY_LINK_KINDS, DIAGNOSTIC_SEVERITIES, DIAGRAM_LAYOUT_KEY, DSM3_FILE_KEYS, DSM3_TOOL, ELEMENT_KINDS, FILE_METAMODEL_KEY, HUB_ENTRY_KEY, ISTAR_2_0, ISTAR_2_0_DECLARATION, ISTAR_PREFIX, ISTAR_VERSION, LINK_KINDS, LINK_KIND_INFO, LINK_SELECTOR, METAMODEL_VERSION, MetamodelError, ModelOperationError, NODE_KINDS, NODE_KIND_INFO, NODE_LINK_KINDS, PISTAR_TOOL, PistarParseError, PistarWriteError, addDependency, addElement, addLeg, boolean, canLink, childrenOf, compareSeverity, connect, createDiagnosticsStore, createEmptyModel, createModelStore, defaultHubPosition, defaultIdGenerator, defineMetamodel, defineMetamodelExtension, defineProperties, dependencyEnds, dependencyLinksOf, detectFramework, detectPrefix, disconnect, effectiveElementKind, effectiveLinkKind, elementBehavesLike, elementCenter, elementKindDefinition, enumOf, extendMetamodel, fileMetamodelExtension, fileMetamodelOf, fromLspDiagnostic, fromLspDiagnostics, fromNodeIdDiagnostic, fromNodeIdDiagnostics, getSourceLayout, groupDiagnostics, hostMetamodelOf, hubEntryLayoutKey, hubLinkOf, hubOfLink, inheritMetamodel, inheritSourceLayout, isActor, isActorIn, isActorKind, isActorKindIn, isActorLinkKind, isContributionLabel, isDependencyLinkKind, isElementKind, isHubElement, isLinkKind, isLinkRef, isNode, isNodeIn, isNodeKind, isNodeKindIn, isNodeLinkKind, linkBehavesLike, linkKindDefinition, linkTargetOf, linksOf, mergeDiagnostics, metamodelOf, moveElement, nestElement, number, parsePistar, prop, removeElement, removeElements, removeLeg, setHostMetamodel, shortKindName, string, targetsLinks, toPistar, toPistarObject, updateDiagram, updateElement, updateLink, validateFileMetamodel, validateModel, validateModelProperties, withFileMetamodel, withMetamodel, worstDiagnosticSeverity };
2847
2894
 
2848
2895
  //# sourceMappingURL=index.js.map