@githolon/dsl 0.103.0 → 0.103.1

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/dart/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 0.64.3
2
+
3
+ - Single-flight generated child-workspace acquisition and continuous sync so concurrent references share one
4
+ open, one lifecycle result and one error rather than cloning or registering the same workspace twice.
5
+ - Generate separate shared-workspace reads so applications can render denormalized owned-workspace cards
6
+ without eagerly opening every founded child merely to discover genuinely shared lives.
7
+
1
8
  ## 0.64.2
2
9
 
3
10
  - Execute generated applications through the typed USDA-native kernel boundary, including native read
@@ -16,6 +16,43 @@ var RUNNER_BUNDLE_VERSION = typeof __NOMOS_BUNDLE_VERSION__ !== "undefined" ? __
16
16
  var plainObject = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : null;
17
17
  var errorMessage = (e, fallback = "nomos runtime error") => String(e?.message ?? e ?? fallback);
18
18
  var compact = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0 && v !== null && v !== ""));
19
+ var PERF_COMPONENT_LIMIT = 12;
20
+ var PERF_PHASE_LIMIT = 16;
21
+ var PERF_NOTE_LIMIT = 12;
22
+ var numericEntries = (value, limit) => {
23
+ const entries = Object.entries(plainObject(value) || {}).filter(([, v]) => typeof v === "number" && Number.isFinite(v) && v !== 0).map(([name, n]) => [name, n]).sort((a, b) => Math.abs(b[1]) - Math.abs(a[1]));
24
+ return {
25
+ values: Object.fromEntries(entries.slice(0, limit)),
26
+ omitted: Math.max(0, entries.length - limit)
27
+ };
28
+ };
29
+ function boundedKernelSpans(value) {
30
+ const root = plainObject(value);
31
+ if (!root) return value;
32
+ const ranked = Object.entries(root).map(([name, raw]) => {
33
+ const row = plainObject(raw) || {};
34
+ const phases = numericEntries(row.phases, PERF_PHASE_LIMIT);
35
+ const notes = numericEntries(row.notes, PERF_NOTE_LIMIT);
36
+ const flat = numericEntries(row, PERF_PHASE_LIMIT);
37
+ const totalUs = typeof row.totalUs === "number" && Number.isFinite(row.totalUs) ? row.totalUs : Object.values(flat.values).reduce((sum, n) => sum + Number(n), 0);
38
+ const shaped = plainObject(row.phases) || plainObject(row.notes) || row.totalUs !== void 0 ? compact({
39
+ totalUs,
40
+ phases: Object.keys(phases.values).length ? phases.values : void 0,
41
+ notes: Object.keys(notes.values).length ? notes.values : void 0,
42
+ omittedPhases: phases.omitted || void 0,
43
+ omittedNotes: notes.omitted || void 0
44
+ }) : compact({
45
+ ...flat.values,
46
+ omittedMetrics: flat.omitted || void 0
47
+ });
48
+ return { name, totalUs, shaped };
49
+ }).sort((a, b) => b.totalUs - a.totalUs);
50
+ const kept = ranked.slice(0, PERF_COMPONENT_LIMIT);
51
+ return {
52
+ ...Object.fromEntries(kept.map(({ name, shaped }) => [name, shaped])),
53
+ ...ranked.length > PERF_COMPONENT_LIMIT ? { _summary: { omittedComponents: ranked.length - PERF_COMPONENT_LIMIT } } : {}
54
+ };
55
+ }
19
56
  var errorInfo = (e, req = {}, component = "nomos.webview.runner") => {
20
57
  const err = e;
21
58
  const existing = plainObject(err?.errorInfo) || plainObject(err?.info);
@@ -99,6 +136,7 @@ function createWebviewRunner({ post: post2, openRealm: openRealm2 }) {
99
136
  const spanStarts = /* @__PURE__ */ new Map();
100
137
  const forwardSpan = (session, raw) => {
101
138
  const s = plainObject(raw) ?? {};
139
+ const presented = s.kernelSpans === void 0 ? s : { ...s, kernelSpans: boundedKernelSpans(s.kernelSpans) };
102
140
  const id = String(s.id ?? "");
103
141
  if (s.phase === "begin" && id) {
104
142
  spanStarts.set(id, s);
@@ -115,13 +153,13 @@ function createWebviewRunner({ post: post2, openRealm: openRealm2 }) {
115
153
  elapsedMs,
116
154
  domain: s.domain,
117
155
  directiveId: s.directiveId,
118
- kernelSpans: s.kernelSpans,
156
+ kernelSpans: presented.kernelSpans,
119
157
  born: s.born,
120
158
  birthOutcomes: s.birthOutcomes
121
159
  }))}`);
122
160
  }
123
161
  }
124
- post2({ v: WEBVIEW_BRIDGE_VERSION, event: "span", session, span: raw });
162
+ post2({ v: WEBVIEW_BRIDGE_VERSION, event: "span", session, span: presented });
125
163
  };
126
164
  const pendingCustodyAsks = /* @__PURE__ */ new Map();
127
165
  let connectGate = Promise.resolve();
@@ -566,7 +604,12 @@ function createWebviewRunner({ post: post2, openRealm: openRealm2 }) {
566
604
  return await (await need(a)).__nomosPrepareOwnedRecordAction({ action: a.action, scope: a.scope, record: a.record, peer: a.peer });
567
605
  },
568
606
  async cryptoOwnedBirthScopeKey(a) {
569
- return await (await need(a)).__nomosOwnedBirthScopeKey({ scope: a.scope, namespace: a.namespace, key: a.key, record: a.record });
607
+ return await (await need(a)).__nomosOwnedBirthScopeKey({
608
+ scope: a.scope,
609
+ namespace: a.namespace,
610
+ key: a.key,
611
+ ...a.record !== void 0 ? { record: a.record } : {}
612
+ });
570
613
  },
571
614
  async cryptoOwnedDeviceRequest(a) {
572
615
  return { request: await (await need(a)).__nomosOwnedDeviceRequest({ scope: a.scope }) };
@@ -1039,6 +1039,11 @@ class NomosBridge {
1039
1039
  // [acquireSession]'s per-workspace single-flight: workspace → the in-flight open every concurrent
1040
1040
  // acquirer joins (removed when it completes; a live session then hits the fast path instead).
1041
1041
  final Map<String, Future<NomosSession>> _acquiring = {};
1042
+ // Every workspace opened as part of one stateful application participates in continuous custody exchange.
1043
+ // Starting that lifecycle is itself single-flight: generated navigation may discover the same child through
1044
+ // several references, but the runner gets one autoSync registration for that session.
1045
+ final Set<String> _autoSyncSessions = {};
1046
+ final Map<String, Future<void>> _autoSyncStarting = {};
1042
1047
  int _seq = 0;
1043
1048
  final String _traceId = _newTraceId();
1044
1049
  late final StreamSubscription<Map<String, Object?>> _sub;
@@ -1875,14 +1880,34 @@ class NomosBridge {
1875
1880
  plane._sessionPrincipals[workspace] = sourcePrincipal;
1876
1881
  }
1877
1882
 
1883
+ Future<NomosSession> ready(NomosSession opened) async {
1884
+ inheritPrincipal();
1885
+ if (!plane._autoSyncSessions.contains(workspace)) {
1886
+ final start = plane._autoSyncStarting.putIfAbsent(workspace, () async {
1887
+ await opened.autoSync();
1888
+ plane._autoSyncSessions.add(workspace);
1889
+ });
1890
+ try {
1891
+ await start;
1892
+ } finally {
1893
+ if (identical(plane._autoSyncStarting[workspace], start)) {
1894
+ plane._autoSyncStarting.remove(workspace);
1895
+ }
1896
+ }
1897
+ }
1898
+ return opened;
1899
+ }
1900
+
1878
1901
  if (plane._sessionNames.contains(workspace)) {
1879
1902
  inheritPrincipal();
1880
- return Future.value(session(workspace));
1903
+ return ready(session(workspace));
1881
1904
  }
1882
1905
  // SINGLE-FLIGHT: one in-flight open per workspace per plane; concurrent callers join it.
1883
1906
  return plane._acquiring.putIfAbsent(workspace, () async {
1884
1907
  try {
1885
- if (plane._sessionNames.contains(workspace)) return session(workspace);
1908
+ if (plane._sessionNames.contains(workspace)) {
1909
+ return ready(session(workspace));
1910
+ }
1886
1911
  // Consult the plane's TRUTHFUL registry (mounted ∪ parked ∪ resident custody — the runner's
1887
1912
  // realm.list()): anything the device already holds opens locally, no cloud, no credentials.
1888
1913
  var resident = false;
@@ -1898,8 +1923,7 @@ class NomosBridge {
1898
1923
  session: workspace,
1899
1924
  allowUnborn: true,
1900
1925
  authorSecret: authorSecret);
1901
- inheritPrincipal();
1902
- return session(workspace);
1926
+ return ready(session(workspace));
1903
1927
  }
1904
1928
  if (ifAbsentCredentials == null) {
1905
1929
  throw StateError(
@@ -1916,8 +1940,7 @@ class NomosBridge {
1916
1940
  authorSecret: c.authorSecret ?? authorSecret,
1917
1941
  allowUnborn: c.allowUnborn,
1918
1942
  );
1919
- inheritPrincipal();
1920
- return session(workspace);
1943
+ return ready(session(workspace));
1921
1944
  } finally {
1922
1945
  // completed (either way) — the next acquire re-evaluates fresh (a live session hits the fast path).
1923
1946
  plane._acquiring.remove(workspace);
@@ -1935,11 +1958,12 @@ class NomosBridge {
1935
1958
  /// @internal The immutable genesis of a sibling custody chain. This is observational metadata used to
1936
1959
  /// compose an ordinary source-enrolment offer; the kernel still reopens and proves every foreign read.
1937
1960
  Future<String> nomosWorkspaceGenesis(String workspace,
1938
- {String? session}) async =>
1961
+ {String? session}) async =>
1939
1962
  (_asMap(await _call('nomosWorkspaceGenesis', {
1940
- 'workspace': workspace,
1941
- if (session != null) 'session': session,
1942
- }))['genesis'] ?? '')
1963
+ 'workspace': workspace,
1964
+ if (session != null) 'session': session,
1965
+ }))['genesis'] ??
1966
+ '')
1943
1967
  .toString();
1944
1968
 
1945
1969
  /// UNDO the most-recent action via the kernel STRIKE primitive (the fold drops the struck intent by parity).
@@ -2452,7 +2476,8 @@ class NomosBridge {
2452
2476
  /// Compare an immutable publication pin with the stable series' one compiled
2453
2477
  /// current pointer. This is an O(current-state) check: it neither resolves the
2454
2478
  /// pinned publication nor reconstructs aggregate history.
2455
- Future<ReferenceFreshness<TVersion>> checkReferenceFreshness<TVersion, TSeries>(
2479
+ Future<ReferenceFreshness<TVersion>>
2480
+ checkReferenceFreshness<TVersion, TSeries>(
2456
2481
  VersionedNomosRef<TVersion, TSeries> reference,
2457
2482
  ) async {
2458
2483
  final result = _asMap(await _call('checkRefFreshness', {
@@ -2460,8 +2485,7 @@ class NomosBridge {
2460
2485
  {
2461
2486
  'pinned': NmsInternalNomosRefs.toJson(reference.pinned),
2462
2487
  'series': NmsInternalNomosRefs.toJson(reference.series),
2463
- 'currentField':
2464
- NmsInternalVersionedNomosRefs.currentField(reference),
2488
+ 'currentField': NmsInternalVersionedNomosRefs.currentField(reference),
2465
2489
  },
2466
2490
  ],
2467
2491
  }));
@@ -2666,8 +2690,8 @@ class NomosBridge {
2666
2690
  }));
2667
2691
 
2668
2692
  /// FRAMEWORK-INTERNAL business birth: derive the unborn child's owned key from its deterministic address.
2669
- Future<NomosScopeKey> nomosOwnedBirthScopeKey(
2670
- String scope, String namespace, Object? key, String recordType, String recordId) async {
2693
+ Future<NomosScopeKey> nomosOwnedBirthScopeKey(String scope, String namespace,
2694
+ Object? key, String recordType, String recordId) async {
2671
2695
  final out = _asMap(await _call('cryptoOwnedBirthScopeKey', {
2672
2696
  'scope': scope,
2673
2697
  'namespace': namespace,
@@ -2799,14 +2823,20 @@ class NomosBridge {
2799
2823
  /// Each round arrives on [syncEvents] as `{ event:'sync', reason, result }`; socket state as `{event:'link'}`.
2800
2824
  /// Call once after [connect] — then the app never polls.
2801
2825
  Future<void> autoSync(
2802
- {int? debounceMs, int? fallbackMs, String? signalId}) async =>
2803
- await _call('autoSync', {
2804
- if (debounceMs != null) 'debounceMs': debounceMs,
2805
- if (fallbackMs != null) 'fallbackMs': fallbackMs,
2806
- if (signalId != null) 'signalId': signalId
2807
- });
2826
+ {int? debounceMs, int? fallbackMs, String? signalId}) async {
2827
+ await _call('autoSync', {
2828
+ if (debounceMs != null) 'debounceMs': debounceMs,
2829
+ if (fallbackMs != null) 'fallbackMs': fallbackMs,
2830
+ if (signalId != null) 'signalId': signalId
2831
+ });
2832
+ final workspace = _sessionName;
2833
+ if (workspace != null) _eventPlane._autoSyncSessions.add(workspace);
2834
+ }
2835
+
2808
2836
  Future<void> stopAutoSync() async {
2809
2837
  await _call('stopAutoSync');
2838
+ final workspace = _sessionName;
2839
+ if (workspace != null) _eventPlane._autoSyncSessions.remove(workspace);
2810
2840
  }
2811
2841
 
2812
2842
  /// Background-sync telemetry: each autoSync round (`event:'sync'`) and doorbell socket state (`event:'link'`).
package/dart/pubspec.yaml CHANGED
@@ -8,7 +8,7 @@ description: >-
8
8
  Nomos 2 Dart frontend types — the hand-written kernel wire types plus the
9
9
  code-generated per-domain aggregate + directive-payload types. (De)serializes
10
10
  the EXACT serde JSON the Rust kernel + projection produce/consume.
11
- version: 0.64.2
11
+ version: 0.64.3
12
12
 
13
13
  environment:
14
14
  sdk: ^3.5.0
@@ -25,6 +25,11 @@ class FakeRunner implements BridgeTransport {
25
25
  }
26
26
 
27
27
  int get connectCount => sent.where((m) => m['op'] == 'connect').length;
28
+ int autoSyncCount(String workspace) => sent
29
+ .where((m) =>
30
+ m['op'] == 'autoSync' &&
31
+ ((m['args'] as Map)['session']).toString() == workspace)
32
+ .length;
28
33
  Map<String, Object?> connectArgs(int i) =>
29
34
  (sent.where((m) => m['op'] == 'connect').elementAt(i)['args'] as Map)
30
35
  .cast<String, Object?>();
@@ -89,6 +94,8 @@ void main() {
89
94
  fail('credentials must not be requested for a mounted session'));
90
95
  expect(identical(s, bridge.session('home')), isTrue);
91
96
  expect(runner.connectCount, 1, reason: 'no re-connect for a live session');
97
+ expect(runner.autoSyncCount('home'), 1,
98
+ reason: 'an acquired application workspace continuously syncs');
92
99
  await bridge.dispose();
93
100
  });
94
101
 
@@ -140,6 +147,8 @@ void main() {
140
147
  reason: 'ONE connect for three concurrent acquires');
141
148
  expect(identical(results[0], results[1]), isTrue);
142
149
  expect(identical(results[1], results[2]), isTrue);
150
+ expect(runner.autoSyncCount('cloudy'), 1,
151
+ reason: 'the workspace lifecycle is single-flight too');
143
152
  final args = runner.connectArgs(0);
144
153
  expect(args['cloud'], 'https://cloud.example');
145
154
  expect(args['authToken'], 'tok');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.103.0",
3
+ "version": "0.103.1",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
@@ -1168,6 +1168,18 @@ export function generateDartApplication(
1168
1168
  ` for (final record in records) {`,
1169
1169
  ` seen.add(NmsInternalRecordOrigins.require(record).workspace);`,
1170
1170
  ` }`,
1171
+ ` for (final record in await shared()) {`,
1172
+ ` final workspace = NmsInternalRecordOrigins.require(record).workspace;`,
1173
+ ` if (seen.add(workspace)) records.add(record);`,
1174
+ ` }`,
1175
+ ` return records;`,
1176
+ ` }`,
1177
+ ``,
1178
+ ` /// The ${birth.aggregateId} lives shared with the signed-in person.`,
1179
+ ` /// Unlike [available], this does not open lives founded by that person.`,
1180
+ ` Future<List<${rootModel}>> shared() async {`,
1181
+ ` final records = <${rootModel}>[];`,
1182
+ ` final seen = <String>{};`,
1171
1183
  ` final shares = await _client.bridge.query('sharedByObject', {'object': 'workspace:self'}, null, _client.actor);`,
1172
1184
  ` for (final row in shares) {`,
1173
1185
  ` final raw = row['data'];`,
@@ -1191,6 +1203,12 @@ export function generateDartApplication(
1191
1203
  ` return records;`,
1192
1204
  ` }`,
1193
1205
  ``,
1206
+ ` /// React when this person's shared ${birth.aggregateId} lives change.`,
1207
+ ` Stream<List<${rootModel}>> watchShared() =>`,
1208
+ ` _client.bridge`,
1209
+ ` .watch('sharedByObject', {'object': 'workspace:self'}, null, _client.actor, 60000)`,
1210
+ ` .asyncMap((_) => shared());`,
1211
+ ``,
1194
1212
  ` /// React when this person's available ${birth.aggregateId} lives change.`,
1195
1213
  ` Stream<List<${rootModel}>> watchAvailable() =>`,
1196
1214
  ` Stream<List<${rootModel}>>.multi((events) {`,
@@ -1603,8 +1603,8 @@ const NOMOS_TYPES_DEP = "^0.8.0";
1603
1603
  // 0.48.x: session-bound clients (XClient.bind/NomosClientInterface/DomainClientSessionMismatch/
1604
1604
  // DomainNotInstalledForSession, engine.follow, pendingConflicts lanes) — the generated client
1605
1605
  // REQUIRES this surface. 0.48.1 fixes NomosSession.watchPendingConflicts delegation.
1606
- const NOMOS_CLIENT_DEP = "^0.64.1";
1607
- const NOMOS_FLUTTER_DEP = "^0.63.1";
1606
+ const NOMOS_CLIENT_DEP = "^0.64.3";
1607
+ const NOMOS_FLUTTER_DEP = "^0.63.4";
1608
1608
  // DRIFT TRIPWIRE: pubspec_pins.test.ts asserts these equal the CURRENT versions in dsl/dart/pubspec.yaml
1609
1609
  // and dsl/dart_types/pubspec.yaml — a Dart release without this bump fails the suite, so tenants never
1610
1610
  // land back in dependency_overrides (the exact co2 report this closes, third occurrence).