@istar-ts/core 0.13.3 → 0.13.4

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
@@ -1234,6 +1234,12 @@ var PistarParseError = class extends Error {
1234
1234
  var PistarWriteError = class extends Error {
1235
1235
  name = "PistarWriteError";
1236
1236
  };
1237
+ /** The `objects` key of a hub's entry (`{ hub, starts, end }`) in a `SourceLayout`. */
1238
+ function hubEntryLayoutKey(hubId) {
1239
+ return `${hubId}\u0000entry`;
1240
+ }
1241
+ /** The `objects` key of the diagram in a `SourceLayout`. */
1242
+ const DIAGRAM_LAYOUT_KEY = "\0diagram";
1237
1243
  const layouts = /* @__PURE__ */ new WeakMap();
1238
1244
  /** The key order recorded when `model` was parsed, if it came from `parsePistar`. */
1239
1245
  function getSourceLayout(model) {
@@ -1341,6 +1347,7 @@ function parsePistar(input, options = {}) {
1341
1347
  usedDisplay.add(id);
1342
1348
  return entry;
1343
1349
  };
1350
+ const objects = /* @__PURE__ */ new Map();
1344
1351
  const readElement = (raw, where, known, extras) => {
1345
1352
  if (!isRecord(raw)) throw new PistarParseError(`${where}: expected an object`);
1346
1353
  const { id, type } = raw;
@@ -1360,6 +1367,7 @@ function parsePistar(input, options = {}) {
1360
1367
  ...optional("extra", extraOf(raw, known))
1361
1368
  };
1362
1369
  elements.set(id, element);
1370
+ objects.set(id, Object.keys(raw));
1363
1371
  return element;
1364
1372
  };
1365
1373
  asArray(json.actors, "actors").forEach((raw, i) => {
@@ -1404,12 +1412,14 @@ function parsePistar(input, options = {}) {
1404
1412
  ...optional("extra", extraOf(raw, LINK_KEYS))
1405
1413
  };
1406
1414
  links.set(id, link);
1415
+ objects.set(id, Object.keys(raw));
1407
1416
  return link;
1408
1417
  };
1409
1418
  const readHubEntry = (raw, where) => {
1410
1419
  const hub = readElement(raw.hub, `${where}.hub`, HUB_KEYS, {});
1411
1420
  const hubKind = metamodel.elements.get(hub.kind);
1412
1421
  if (hubKind?.role !== "hub") throw new PistarParseError(`${where}.hub (${hub.id}): ${hub.kind} is not a hub kind`);
1422
+ objects.set(hubEntryLayoutKey(hub.id), Object.keys(raw));
1413
1423
  const entryExtra = extraOf(raw, HUB_ENTRY_KEYS);
1414
1424
  if (entryExtra) elements.set(hub.id, {
1415
1425
  ...hub,
@@ -1447,6 +1457,7 @@ function parsePistar(input, options = {}) {
1447
1457
  ...isRecord(d.customProperties) ? { customProperties: d.customProperties } : {},
1448
1458
  ...optional("extra", extraOf(d, DIAGRAM_KEYS))
1449
1459
  };
1460
+ objects.set(DIAGRAM_LAYOUT_KEY, Object.keys(d));
1450
1461
  }
1451
1462
  const model = {
1452
1463
  elements,
@@ -1460,7 +1471,8 @@ function parsePistar(input, options = {}) {
1460
1471
  };
1461
1472
  layouts.set(model, {
1462
1473
  keys: Object.keys(json),
1463
- display: Object.keys(display)
1474
+ display: Object.keys(display),
1475
+ objects
1464
1476
  });
1465
1477
  setHostMetamodel(model, host);
1466
1478
  return withMetamodel(model, metamodel);
@@ -1533,6 +1545,19 @@ function pistarTypeOf(type, kind, metamodel) {
1533
1545
  if (type === void 0) throw new PistarWriteError(`kind "${kind}" is not part of ${metamodel.name}; pass its metamodel (toPistar(model, { metamodel }) or withMetamodel)`);
1534
1546
  return type;
1535
1547
  }
1548
+ /**
1549
+ * `value` with its keys in the order `keys` gives (those present), then the rest as built (piStar's
1550
+ * order): an object read from a file is written back with the file's key order, and keys the
1551
+ * file lacked follow.
1552
+ */
1553
+ function inSourceOrder(value, keys) {
1554
+ if (!keys) return value;
1555
+ const result = {};
1556
+ const record = value;
1557
+ for (const key of keys) if (key in record) result[key] = record[key];
1558
+ for (const key of Object.keys(record)) if (!(key in result)) result[key] = record[key];
1559
+ return result;
1560
+ }
1536
1561
  function elementJson(element, metamodel) {
1537
1562
  return {
1538
1563
  id: element.id,
@@ -1553,38 +1578,40 @@ function toPistarObject(input, options = {}) {
1553
1578
  const orphans = [];
1554
1579
  const dependencies = [];
1555
1580
  const children = /* @__PURE__ */ new Map();
1581
+ const layout = layouts.get(model);
1582
+ const sourceOrder = (id, value) => inSourceOrder(value, layout?.objects?.get(id));
1556
1583
  for (const element of model.elements.values()) {
1557
1584
  if (isHub(element)) continue;
1558
1585
  if (element.parent !== void 0 && !isActorKind(element.kind)) {
1559
1586
  let list = children.get(element.parent);
1560
1587
  if (!list) children.set(element.parent, list = []);
1561
- list.push({
1588
+ list.push(sourceOrder(element.id, {
1562
1589
  ...elementJson(element, metamodel),
1563
1590
  ...element.extra
1564
- });
1591
+ }));
1565
1592
  }
1566
1593
  }
1567
1594
  for (const element of model.elements.values()) {
1568
1595
  if (isHub(element)) continue;
1569
- if (isActorKind(element.kind)) actors.push({
1596
+ if (isActorKind(element.kind)) actors.push(sourceOrder(element.id, {
1570
1597
  ...elementJson(element, metamodel),
1571
1598
  nodes: children.get(element.id) ?? [],
1572
1599
  ...element.extra
1573
- });
1600
+ }));
1574
1601
  else if (element.isDependum) {
1575
1602
  const { inbound, outbound } = dependencyLinksOf(model, element.id);
1576
- dependencies.push({
1603
+ dependencies.push(sourceOrder(element.id, {
1577
1604
  ...elementJson(element, metamodel),
1578
1605
  source: inbound?.source ?? element.dependency?.source ?? "",
1579
1606
  target: outbound?.target ?? element.dependency?.target ?? "",
1580
1607
  ...element.extra
1581
- });
1582
- } else if (element.parent === void 0 || !model.elements.has(element.parent)) orphans.push({
1608
+ }));
1609
+ } else if (element.parent === void 0 || !model.elements.has(element.parent)) orphans.push(sourceOrder(element.id, {
1583
1610
  ...elementJson(element, metamodel),
1584
1611
  ...element.extra
1585
- });
1612
+ }));
1586
1613
  }
1587
- const linkJson = (link) => ({
1614
+ const linkJson = (link) => sourceOrder(link.id, {
1588
1615
  id: link.id,
1589
1616
  type: pistarTypeOf(metamodel.links.get(link.kind)?.pistarType, link.kind, metamodel),
1590
1617
  source: link.source,
@@ -1616,19 +1643,19 @@ function toPistarObject(input, options = {}) {
1616
1643
  const hubEntry = (hub) => {
1617
1644
  const parts = hubs.get(hub.id) ?? { legs: [] };
1618
1645
  const { [HUB_ENTRY_KEY]: entryExtra, ...hubExtra } = hub.extra ?? {};
1619
- return {
1620
- hub: {
1646
+ return sourceOrder(hubEntryLayoutKey(hub.id), {
1647
+ hub: sourceOrder(hub.id, {
1621
1648
  id: hub.id,
1622
1649
  type: pistarTypeOf(metamodel.elements.get(hub.kind)?.pistarType, hub.kind, metamodel),
1623
1650
  x: hub.x,
1624
1651
  y: hub.y,
1625
1652
  ...hub.customProperties ? { customProperties: { ...hub.customProperties } } : {},
1626
1653
  ...hubExtra
1627
- },
1654
+ }),
1628
1655
  starts: parts.legs.map(linkJson),
1629
1656
  ...parts.end ? { end: linkJson(parts.end) } : {},
1630
1657
  ...isRecord(entryExtra) ? entryExtra : {}
1631
- };
1658
+ });
1632
1659
  };
1633
1660
  const links = [];
1634
1661
  const written = /* @__PURE__ */ new Set();
@@ -1642,7 +1669,6 @@ function toPistarObject(input, options = {}) {
1642
1669
  }
1643
1670
  }
1644
1671
  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
1672
  const display = {};
1647
1673
  const displayOf = (id) => {
1648
1674
  const owner = model.elements.get(id) ?? model.links.get(id);
@@ -1657,15 +1683,16 @@ function toPistarObject(input, options = {}) {
1657
1683
  for (const [id, entry] of Object.entries(model.extraDisplay ?? {})) if (!(id in display)) display[id] = entry;
1658
1684
  let saveDate;
1659
1685
  if (options.saveDate instanceof Date) saveDate = formatDate(options.saveDate);
1660
- else saveDate = options.saveDate ?? model.saveDate ?? formatDate(/* @__PURE__ */ new Date());
1686
+ else if (options.saveDate !== void 0 || model.saveDate !== void 0) saveDate = options.saveDate ?? model.saveDate;
1687
+ else if (!layout || layout.keys.includes("saveDate")) saveDate = formatDate(/* @__PURE__ */ new Date());
1661
1688
  const d = model.diagram;
1662
- const diagram = d && {
1689
+ const diagram = d && sourceOrder("\0diagram", {
1663
1690
  ...d.width !== void 0 ? { width: d.width } : {},
1664
1691
  ...d.height !== void 0 ? { height: d.height } : {},
1665
1692
  ...d.name !== void 0 ? { name: d.name } : {},
1666
1693
  ...d.customProperties ? { customProperties: { ...d.customProperties } } : {},
1667
1694
  ...d.extra
1668
- };
1695
+ });
1669
1696
  const values = {
1670
1697
  ...model.extra,
1671
1698
  actors,
@@ -2843,6 +2870,6 @@ function fromNodeIdDiagnostics(diagnostics) {
2843
2870
  return result;
2844
2871
  }
2845
2872
  //#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 };
2873
+ 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
2874
 
2848
2875
  //# sourceMappingURL=index.js.map