@almadar/runtime 6.8.0 → 6.9.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,4 +1,4 @@
1
- export { InMemoryPersistence, OrbitalServerRuntime, collectDeclaredConfigDefaults, createOrbitalServerRuntime } from './chunk-WOXI5KCA.js';
1
+ export { InMemoryPersistence, OrbitalServerRuntime, collectDeclaredConfigDefaults, createOrbitalServerRuntime } from './chunk-TNK77RLG.js';
2
2
  import './chunk-PZ5AY32C.js';
3
3
  //# sourceMappingURL=OrbitalServerRuntime.js.map
4
4
  //# sourceMappingURL=OrbitalServerRuntime.js.map
@@ -1703,7 +1703,7 @@ function picsumUrl(entityName, fieldName, width = 400, height = 400) {
1703
1703
  return `https://picsum.photos/seed/${encodeURIComponent(seed)}/${width}/${height}`;
1704
1704
  }
1705
1705
  var SEED_REFERENCE_TIMESTAMP = "2024-01-01T00:00:00.000Z";
1706
- var MockPersistenceAdapter = class {
1706
+ var MockPersistenceAdapter = class _MockPersistenceAdapter {
1707
1707
  stores = /* @__PURE__ */ new Map();
1708
1708
  schemas = /* @__PURE__ */ new Map();
1709
1709
  idCounters = /* @__PURE__ */ new Map();
@@ -1764,6 +1764,51 @@ var MockPersistenceAdapter = class {
1764
1764
  const count = seedCount ?? this.config.defaultSeedCount ?? 6;
1765
1765
  this.seed(schema.name, schema.fields, count);
1766
1766
  }
1767
+ this.linkRelationFields();
1768
+ }
1769
+ /**
1770
+ * Walk every row of every registered entity and fill in `type: "relation"`
1771
+ * fields with real IDs from the target entity's store. Without this pass,
1772
+ * relation fields stay as placeholder `[]` / `""` and `populateRelations`
1773
+ * in OrbitalServerRuntime has nothing to hydrate — catalog/preview demos
1774
+ * of nested-tree atoms (e.g. std-thread-comments-linear with ThreadPost.
1775
+ * replies → [ThreadPost]) render empty reply cards.
1776
+ *
1777
+ * For self-referential relations, each row gets 2–4 sibling IDs (excluding
1778
+ * self). For cross-entity relations, IDs are picked from the target store.
1779
+ * The runtime caps recursion at depth=2 in `populateRelations`, so
1780
+ * grandparent-of-self cycles render two levels deep then stop.
1781
+ */
1782
+ linkRelationFields() {
1783
+ for (const [normalizedName, schema] of this.schemas) {
1784
+ const store = this.stores.get(normalizedName);
1785
+ if (!store) continue;
1786
+ const relationFields = schema.fields.filter(
1787
+ (f) => f.type === "relation"
1788
+ );
1789
+ if (relationFields.length === 0) continue;
1790
+ for (const row of store.values()) {
1791
+ for (const field of relationFields) {
1792
+ const targetStore = this.stores.get(field.relation.entity.toLowerCase());
1793
+ if (!targetStore || targetStore.size === 0) continue;
1794
+ const selfId = row["id"];
1795
+ const sameStore = targetStore === store;
1796
+ const eligible = [];
1797
+ for (const id of targetStore.keys()) {
1798
+ if (sameStore && id === selfId) continue;
1799
+ eligible.push(id);
1800
+ }
1801
+ if (eligible.length === 0) continue;
1802
+ const cardinality = field.relation.cardinality ?? "many";
1803
+ if (cardinality === "one" || cardinality === "many-to-one") {
1804
+ row[field.name] = faker.helpers.arrayElement(eligible);
1805
+ } else {
1806
+ const pickCount = Math.min(eligible.length, faker.number.int({ min: 2, max: 4 }));
1807
+ row[field.name] = faker.helpers.shuffle(eligible.slice()).slice(0, pickCount);
1808
+ }
1809
+ }
1810
+ }
1811
+ }
1767
1812
  }
1768
1813
  /**
1769
1814
  * Seed an entity with pre-authored instance data.
@@ -1815,6 +1860,7 @@ var MockPersistenceAdapter = class {
1815
1860
  updatedAt: SEED_REFERENCE_TIMESTAMP
1816
1861
  };
1817
1862
  for (const field of fields) {
1863
+ if (!field.name) continue;
1818
1864
  if (field.name === "id" || field.name === "createdAt" || field.name === "updatedAt") {
1819
1865
  continue;
1820
1866
  }
@@ -1822,18 +1868,26 @@ var MockPersistenceAdapter = class {
1822
1868
  }
1823
1869
  return item;
1824
1870
  }
1871
+ /** Max nesting depth for recursive array/object schemas (e.g. a Comment
1872
+ * entity whose `replies: [Comment]` field references itself). Without
1873
+ * this guard, generateArrayValue → generateObjectValue → generateArray…
1874
+ * recurses until stack overflow on every recursive type. Three levels
1875
+ * is enough to render a useful thread depth (parent → reply → sub-reply)
1876
+ * in catalog/preview without blowing the fixture. */
1877
+ static MAX_NESTED_DEPTH = 3;
1825
1878
  /**
1826
1879
  * Generate a mock value for a field based on its schema.
1827
1880
  */
1828
- generateFieldValue(entityName, field, index) {
1881
+ generateFieldValue(entityName, field, index, depth = 0) {
1829
1882
  const fieldTypeLc = field.type.toLowerCase();
1883
+ const values = "values" in field ? field.values : void 0;
1830
1884
  mockLog.debug("field:generate", {
1831
1885
  entityName,
1832
1886
  fieldName: field.name,
1833
1887
  fieldType: fieldTypeLc,
1834
- hasValues: !!field.values?.length,
1835
- valuesCount: field.values?.length ?? 0,
1836
- values: field.values?.length ? field.values.join(",") : null,
1888
+ hasValues: !!values?.length,
1889
+ valuesCount: values?.length ?? 0,
1890
+ values: values?.length ? values.join(",") : null,
1837
1891
  format: field.format ?? null,
1838
1892
  hasDefault: field.default !== void 0
1839
1893
  });
@@ -1841,7 +1895,7 @@ var MockPersistenceAdapter = class {
1841
1895
  if (isNumeric && field.default !== void 0) {
1842
1896
  return field.default;
1843
1897
  }
1844
- switch (fieldTypeLc) {
1898
+ switch (field.type) {
1845
1899
  case "string":
1846
1900
  return this.generateStringValue(entityName, field, index);
1847
1901
  case "number":
@@ -1858,12 +1912,11 @@ var MockPersistenceAdapter = class {
1858
1912
  }
1859
1913
  return null;
1860
1914
  case "relation":
1861
- return null;
1862
- // Relations need special handling
1915
+ return field.relation?.cardinality === "one" ? "" : [];
1863
1916
  case "array":
1864
- return this.generateArrayValue(entityName, field, index);
1917
+ return this.generateArrayValue(entityName, field, index, depth);
1865
1918
  case "object":
1866
- return this.generateObjectValue(entityName, field, index);
1919
+ return this.generateObjectValue(entityName, field, index, depth);
1867
1920
  default:
1868
1921
  return this.generateStringValue(entityName, field, index);
1869
1922
  }
@@ -1877,16 +1930,18 @@ var MockPersistenceAdapter = class {
1877
1930
  * with no element schema), falls back to an empty array — the historical
1878
1931
  * behavior.
1879
1932
  */
1880
- generateArrayValue(entityName, field, index) {
1881
- if (!field.items) return [];
1933
+ generateArrayValue(entityName, field, index, depth = 0) {
1934
+ if (field.type !== "array" || !field.items) return [];
1935
+ if (depth >= _MockPersistenceAdapter.MAX_NESTED_DEPTH) return [];
1882
1936
  const count = faker.number.int({ min: 3, max: 5 });
1883
1937
  const out = [];
1938
+ const elementName = field.name ?? "item";
1884
1939
  for (let i = 0; i < count; i++) {
1885
1940
  const elementField = {
1886
1941
  ...field.items,
1887
- name: `${field.name}[${i}]`
1942
+ name: `${elementName}[${i}]`
1888
1943
  };
1889
- out.push(this.generateFieldValue(entityName, elementField, index * 10 + i));
1944
+ out.push(this.generateFieldValue(entityName, elementField, index * 10 + i, depth + 1));
1890
1945
  }
1891
1946
  return out;
1892
1947
  }
@@ -1896,12 +1951,13 @@ var MockPersistenceAdapter = class {
1896
1951
  * `generateFieldValue` per property so nested objects-of-arrays-of-objects
1897
1952
  * compose correctly.
1898
1953
  */
1899
- generateObjectValue(entityName, field, index) {
1954
+ generateObjectValue(entityName, field, index, depth = 0) {
1900
1955
  if (!field.properties) return null;
1956
+ if (depth >= _MockPersistenceAdapter.MAX_NESTED_DEPTH) return null;
1901
1957
  const out = {};
1902
1958
  for (const [propName, propField] of Object.entries(field.properties)) {
1903
1959
  const childField = { ...propField, name: propName };
1904
- out[propName] = this.generateFieldValue(entityName, childField, index);
1960
+ out[propName] = this.generateFieldValue(entityName, childField, index, depth + 1);
1905
1961
  }
1906
1962
  return out;
1907
1963
  }
@@ -1913,9 +1969,11 @@ var MockPersistenceAdapter = class {
1913
1969
  * declare `format: "email"`; if they need an enum, they declare `values: [...]`.
1914
1970
  */
1915
1971
  generateStringValue(entityName, field, _index) {
1916
- if (field.values && field.values.length > 0) {
1917
- return faker.helpers.arrayElement(field.values);
1972
+ const values = "values" in field ? field.values : void 0;
1973
+ if (values && values.length > 0) {
1974
+ return faker.helpers.arrayElement(values);
1918
1975
  }
1976
+ const fieldName = field.name ?? "field";
1919
1977
  switch (field.format) {
1920
1978
  case "email":
1921
1979
  return faker.internet.email();
@@ -1932,11 +1990,11 @@ var MockPersistenceAdapter = class {
1932
1990
  case "image":
1933
1991
  case "avatar":
1934
1992
  case "thumbnail":
1935
- return picsumUrl(entityName, field.name);
1993
+ return picsumUrl(entityName, fieldName);
1936
1994
  }
1937
- const lname = field.name.toLowerCase();
1995
+ const lname = fieldName.toLowerCase();
1938
1996
  if (lname === "image" || lname === "imageurl" || lname === "image_url" || lname === "photo" || lname === "photourl" || lname === "photo_url" || lname === "avatar" || lname === "avatarurl" || lname === "avatar_url" || lname === "thumbnail" || lname === "thumbnailurl" || lname === "thumbnail_url" || lname === "picture" || lname === "pictureurl" || lname === "cover" || lname === "coverurl" || lname === "banner" || lname === "bannerurl") {
1939
- return picsumUrl(entityName, field.name);
1997
+ return picsumUrl(entityName, fieldName);
1940
1998
  }
1941
1999
  const value = faker.lorem.words(2);
1942
2000
  mockLog.debug("field:fallback-lorem", () => ({
@@ -3626,32 +3684,6 @@ function needsPreprocessing(schema) {
3626
3684
  }
3627
3685
  return false;
3628
3686
  }
3629
- function mapFieldForMock(f) {
3630
- const rec = f;
3631
- const out = {
3632
- name: f.name,
3633
- type: rec["type"]
3634
- };
3635
- if (typeof rec["required"] === "boolean") out.required = rec["required"];
3636
- if (Array.isArray(rec["values"])) out.values = rec["values"];
3637
- if (rec["default"] !== void 0) out.default = rec["default"];
3638
- if (typeof rec["format"] === "string") out.format = rec["format"];
3639
- const items = rec["items"];
3640
- if (items && typeof items === "object" && "type" in items) {
3641
- out.items = mapFieldForMock({ name: "", ...items });
3642
- }
3643
- const properties = rec["properties"];
3644
- if (properties && typeof properties === "object" && !Array.isArray(properties)) {
3645
- const propsOut = {};
3646
- for (const [k, v] of Object.entries(properties)) {
3647
- if (v && typeof v === "object" && "type" in v) {
3648
- propsOut[k] = mapFieldForMock({ name: k, ...v });
3649
- }
3650
- }
3651
- if (Object.keys(propsOut).length > 0) out.properties = propsOut;
3652
- }
3653
- return out;
3654
- }
3655
3687
  var OrbitalServerRuntime = class {
3656
3688
  orbitals = /* @__PURE__ */ new Map();
3657
3689
  eventBus;
@@ -4068,7 +4100,7 @@ var OrbitalServerRuntime = class {
4068
4100
  if (entity?.name && entity.fields) {
4069
4101
  const fields = entity.fields.filter(
4070
4102
  (f) => typeof f.name === "string" && f.name.length > 0
4071
- ).map((f) => mapFieldForMock(f));
4103
+ );
4072
4104
  this.persistence.registerEntity({ name: entity.name, fields });
4073
4105
  if (this.config.debug) {
4074
4106
  persistLog.debug("mock:seeded", { entity: entity.name, count: this.persistence.count(entity.name) });
@@ -4083,7 +4115,7 @@ var OrbitalServerRuntime = class {
4083
4115
  if (!auxEntity.name || !auxEntity.fields) continue;
4084
4116
  const auxFields = auxEntity.fields.filter(
4085
4117
  (f) => typeof f.name === "string" && f.name.length > 0
4086
- ).map((f) => mapFieldForMock(f));
4118
+ );
4087
4119
  this.persistence.registerEntity({ name: auxEntity.name, fields: auxFields });
4088
4120
  if (this.config.debug) {
4089
4121
  persistLog.debug("mock:seeded-auxiliary", {
@@ -4365,7 +4397,7 @@ var OrbitalServerRuntime = class {
4365
4397
  if (entity?.name && entity.fields) {
4366
4398
  const fields = entity.fields.filter(
4367
4399
  (f) => typeof f.name === "string" && f.name.length > 0
4368
- ).map((f) => mapFieldForMock(f));
4400
+ );
4369
4401
  this.persistence.registerEntity({ name: entity.name, fields });
4370
4402
  }
4371
4403
  }
@@ -5443,5 +5475,5 @@ function buildMatcher(src, listenerOrbital) {
5443
5475
  }
5444
5476
 
5445
5477
  export { EffectExecutor, EventBus, HANDLER_MANIFEST, InMemoryPersistence, MockPersistenceAdapter, OrbitalServerRuntime, StateMachineManager, buildEmitsFromTraits, collectDeclaredConfigDefaults, containsBindings, createContextFromBindings, createInitialTraitState, createMockPersistence, createOrbitalServerRuntime, createTestExecutor, createUnifiedLoader, extractBindings, findInitialState, findTransition, formatPayloadValidationError, getIsolatedCollectionName, getNamespacedEvent, interpolateProps, interpolateValue, isBrowser, isElectron, isNamespacedEvent, isNode, normalizeEventKey, parseNamespacedEvent, preprocessSchema, processEvent, validateEventPayload, validatePayloadShapes };
5446
- //# sourceMappingURL=chunk-WOXI5KCA.js.map
5447
- //# sourceMappingURL=chunk-WOXI5KCA.js.map
5478
+ //# sourceMappingURL=chunk-TNK77RLG.js.map
5479
+ //# sourceMappingURL=chunk-TNK77RLG.js.map