@githolon/dsl 0.113.4 → 0.115.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.
Files changed (103) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/compile_package.mjs +23 -3
  3. package/compile_proof.d.mts +7 -0
  4. package/compile_proof.mjs +38 -3
  5. package/dart/CHANGELOG.md +60 -0
  6. package/dart/lib/src/application_framework_calls.dart +16 -6
  7. package/dart/lib/src/console_enrolment.dart +7 -0
  8. package/dart/lib/src/console_http_io.dart +22 -15
  9. package/dart/lib/src/dev_cloud.dart +62 -0
  10. package/dart/lib/src/generated_call.dart +14 -3
  11. package/dart/lib/src/nms_generated_runtime.dart +7 -0
  12. package/dart/lib/src/node_runner_transport.dart +77 -8
  13. package/dart/lib/src/nomos_trace.dart +107 -0
  14. package/dart/lib/src/operation_receipts.dart +74 -7
  15. package/dart/lib/src/reception/dart_reception.dart +174 -17
  16. package/dart/lib/src/rows_watch.dart +390 -0
  17. package/dart/lib/src/runner/node-entry.mjs +7678 -2860
  18. package/dart/lib/src/runtime_v2_transport.dart +3 -0
  19. package/dart/lib/src/sync_status_projection.dart +94 -1
  20. package/dart/lib/src/sync_status_reducer.dart +5 -2
  21. package/dart/lib/src/test_harness.dart +107 -41
  22. package/dart/lib/src/testing/test_peer.dart +107 -99
  23. package/dart/lib/src/testing/test_peer_host.dart +305 -0
  24. package/dart/lib/src/typed_watch_registry.dart +26 -3
  25. package/dart/pubspec.lock +1 -1
  26. package/dart/pubspec.yaml +2 -2
  27. package/dart/test/background_model_adoption_test.dart +1 -0
  28. package/dart/test/console_enrolment_test.dart +47 -0
  29. package/dart/test/custodian_warming_test.dart +105 -0
  30. package/dart/test/fixtures/offer_watch_order_peer.dart +74 -0
  31. package/dart/test/kernel_author_after_read_test.dart +6 -3
  32. package/dart/test/kernel_fence_test.dart +4 -1
  33. package/dart/test/model_adoption_test.dart +66 -0
  34. package/dart/test/nomos_trace_test.dart +91 -0
  35. package/dart/test/own_offer_watch_order_test.dart +90 -0
  36. package/dart/test/receiving_group_open_test.dart +4 -8
  37. package/dart/test/replica_readiness_test.dart +14 -4
  38. package/dart/test/rows_watch_test.dart +331 -0
  39. package/dart/test/runtime_projections_test.dart +41 -0
  40. package/dart/test/test_peer_host_selection_test.dart +49 -0
  41. package/dart_types/CHANGELOG.md +39 -0
  42. package/dart_types/lib/src/approved_model.dart +100 -6
  43. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +1063 -3
  44. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +89 -0
  45. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +14 -0
  46. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +2272 -130
  47. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +65 -0
  48. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +118 -2
  49. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +278 -9
  50. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +7 -85
  51. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +16 -0
  52. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +35 -0
  53. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +43 -2
  54. package/dart_types/pubspec.yaml +1 -1
  55. package/package.json +6 -2
  56. package/src/aggregate.ts +93 -0
  57. package/src/anonymous_value_interning.ts +205 -0
  58. package/src/baseline.ts +27 -0
  59. package/src/baseline_export.ts +432 -0
  60. package/src/baseline_import.ts +312 -0
  61. package/src/baseline_usda.ts +186 -0
  62. package/src/birth_key_rule.ts +472 -0
  63. package/src/births.ts +207 -24
  64. package/src/build_provenance.ts +17 -2
  65. package/src/business_birth.ts +50 -4
  66. package/src/check_package_main.ts +50 -6
  67. package/src/codegen_call_proto.ts +5 -18
  68. package/src/codegen_dart.ts +95 -3
  69. package/src/codegen_dart_client.ts +73 -15
  70. package/src/codegen_dart_facade.ts +58 -35
  71. package/src/codegen_dart_framework_calls.ts +32 -10
  72. package/src/codegen_dart_scenario.ts +7 -0
  73. package/src/codegen_proof.ts +5 -2
  74. package/src/codegen_result_proto.ts +8 -6
  75. package/src/codegen_result_usda.ts +4 -0
  76. package/src/codegen_ts.ts +135 -34
  77. package/src/compatibility_graph.ts +41 -4
  78. package/src/compatibility_usda.ts +11 -1
  79. package/src/compile_package_main.ts +272 -105
  80. package/src/composed_wire_identity.ts +24 -0
  81. package/src/directive.ts +6 -0
  82. package/src/domain_requirements.ts +75 -0
  83. package/src/engine_entry.ts +4 -48
  84. package/src/engine_javascript.ts +12 -6
  85. package/src/fields.ts +109 -0
  86. package/src/identity_usda.ts +77 -51
  87. package/src/index.ts +25 -1
  88. package/src/intent.ts +5 -0
  89. package/src/law_graph.ts +142 -1
  90. package/src/law_graph_usda.ts +25 -141
  91. package/src/law_layer_prims.ts +199 -0
  92. package/src/law_unit_selection.ts +45 -0
  93. package/src/law_unit_set_identity.ts +44 -0
  94. package/src/law_version.ts +107 -0
  95. package/src/package_config.ts +44 -0
  96. package/src/package_dependencies.ts +8 -0
  97. package/src/query/compile.ts +0 -0
  98. package/src/release_inspection.ts +24 -0
  99. package/src/release_publication.ts +298 -0
  100. package/src/value_object.ts +6 -3
  101. package/src/wire.ts +2 -0
  102. package/src/wire_paths.ts +17 -0
  103. package/src/workspace_routing.ts +1 -1
@@ -0,0 +1,390 @@
1
+ import 'dart:async';
2
+ import 'dart:collection';
3
+ import 'dart:convert' show utf8;
4
+ import 'dart:typed_data';
5
+
6
+ import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
7
+ import 'package:nomos_types/src/proto/google/protobuf/any.pb.dart'
8
+ as google_protobuf;
9
+
10
+ import 'generated_call.dart'
11
+ show NmsGeneratedResult, NmsInternalGeneratedResults;
12
+
13
+ /// A row delta did not start where this watch's rows are (CAP-731).
14
+ ///
15
+ /// The runtime composes every delta onto the position it last delivered, so a
16
+ /// mismatch is a protocol defect, never a stale read to paper over. The watch
17
+ /// ends with this error instead of guessing which rows it holds.
18
+ final class NomosWatchPositionMismatch implements Exception {
19
+ const NomosWatchPositionMismatch({required this.held, required this.base});
20
+
21
+ /// The position of the rows this watch holds (`null`: no complete result yet).
22
+ final String? held;
23
+
24
+ /// The position the delta was computed from.
25
+ final String base;
26
+
27
+ @override
28
+ String toString() => 'NomosWatchPositionMismatch: the watch holds rows at '
29
+ '${held ?? '<no complete result>'} but was sent a delta from $base';
30
+ }
31
+
32
+ /// One emission of a rows watch: the rows in kernel order, plus what changed
33
+ /// since this watch's previous emission (CAP-751).
34
+ ///
35
+ /// It is the `List<T>` a rows watch has always emitted (read-only), so existing
36
+ /// listeners are unaffected. A listener that keeps per-row work can use
37
+ /// [changedIds] and [removedIds] to touch only those rows, or rely on identity:
38
+ /// a row in neither set is the identical object it received last time.
39
+ final class NomosRows<T> extends UnmodifiableListView<T> {
40
+ NomosRows._(
41
+ List<T> rows, {
42
+ required this.changedIds,
43
+ required this.removedIds,
44
+ required this.complete,
45
+ }) : super(rows);
46
+
47
+ /// Ids of the rows that are new, or whose object was replaced, since the
48
+ /// previous emission. Every id on the first emission.
49
+ final Set<String> changedIds;
50
+
51
+ /// Ids the previous emission held that this one does not.
52
+ final Set<String> removedIds;
53
+
54
+ /// Whether this emission came from a complete result (the first one, or the
55
+ /// first after a delivered failure) rather than from a row delta. The sets
56
+ /// are exact either way; a complete result usually replaces every row.
57
+ final bool complete;
58
+ }
59
+
60
+ /// The rows of one declared query, keyed by id and patched by kernel deltas.
61
+ ///
62
+ /// Rows are kept in the kernel's order (ascending id by UTF-8 bytes, i.e. code
63
+ /// points), so a merged result lists exactly what a complete read would. A row
64
+ /// the update did not change keeps its identity, so a consumer can skip work on
65
+ /// `identical(old, new)`.
66
+ final class NmsRowsMerge<T> {
67
+ NmsRowsMerge({
68
+ required List<T> Function(NmsGeneratedResult result) decode,
69
+ required String Function(T row) idOf,
70
+ }) : _decode = decode,
71
+ _idOf = idOf;
72
+
73
+ final List<T> Function(NmsGeneratedResult result) _decode;
74
+ final String Function(T row) _idOf;
75
+ SplayTreeMap<String, T>? _rows;
76
+ String? _position;
77
+
78
+ /// The rows after [changed], or `null` when nothing this watch shows changed.
79
+ NomosRows<T>? apply(nomos_protocol.WatchChanged changed) {
80
+ if (changed.hasApplicationDelta()) {
81
+ return _applyDelta(changed.applicationDelta);
82
+ }
83
+ if (!changed.hasApplicationResult()) {
84
+ throw StateError('Generated watch returned no typed application result');
85
+ }
86
+ final previous = _rows;
87
+ final next = SplayTreeMap<String, T>(compareCodePoints);
88
+ final changedIds = <String>{};
89
+ for (final row in _decode(_result(changed.applicationResult))) {
90
+ final id = _idOf(row);
91
+ final held = previous?[id];
92
+ if (held != null && held == row) {
93
+ next[id] = held;
94
+ } else {
95
+ next[id] = row;
96
+ changedIds.add(id);
97
+ }
98
+ }
99
+ final removedIds = <String>{
100
+ if (previous != null)
101
+ for (final id in previous.keys)
102
+ if (!next.containsKey(id)) id,
103
+ };
104
+ _rows = next;
105
+ _position = changed.hasResultFrontier() ? changed.resultFrontier : null;
106
+ if (previous != null && changedIds.isEmpty && removedIds.isEmpty) {
107
+ return null;
108
+ }
109
+ return NomosRows<T>._(
110
+ List<T>.of(next.values, growable: false),
111
+ changedIds: changedIds,
112
+ removedIds: removedIds,
113
+ complete: true,
114
+ );
115
+ }
116
+
117
+ /// A failure delivered to the watch: whatever comes next must be complete.
118
+ void reset() {
119
+ _rows = null;
120
+ _position = null;
121
+ }
122
+
123
+ NomosRows<T>? _applyDelta(nomos_protocol.ApplicationResultDelta delta) {
124
+ final rows = _rows;
125
+ if (rows == null || _position == null || delta.baseFrontier != _position) {
126
+ throw NomosWatchPositionMismatch(
127
+ held: _position, base: delta.baseFrontier);
128
+ }
129
+ final removedIds = <String>{};
130
+ for (final id in delta.removedIds) {
131
+ if (rows.remove(id) != null) removedIds.add(id);
132
+ }
133
+ final changedIds = <String>{};
134
+ for (final row in _decode(_result(delta.changedRows))) {
135
+ final id = _idOf(row);
136
+ final held = rows[id];
137
+ if (held != null && held == row) continue;
138
+ rows[id] = row;
139
+ changedIds.add(id);
140
+ // Removed and re-added within one delta is a replacement, not a removal.
141
+ removedIds.remove(id);
142
+ }
143
+ _position = delta.frontier;
144
+ if (changedIds.isEmpty && removedIds.isEmpty) return null;
145
+ return NomosRows<T>._(
146
+ List<T>.of(rows.values, growable: false),
147
+ changedIds: changedIds,
148
+ removedIds: removedIds,
149
+ complete: false,
150
+ );
151
+ }
152
+
153
+ static NmsGeneratedResult _result(google_protobuf.Any any) =>
154
+ NmsInternalGeneratedResults.create(
155
+ protobufType: any.typeUrl,
156
+ protobufPayload: Uint8List.fromList(any.value),
157
+ );
158
+ }
159
+
160
+ /// Projects the rows of a rows watch into view models, calling the projection
161
+ /// only for rows it has not seen (CAP-751).
162
+ ///
163
+ /// A rows watch keeps every row the change did not touch as the identical
164
+ /// object, so a row seen on the previous [apply] reuses its projection: one
165
+ /// moved asset in a thousand costs one projection, not a thousand. A projection
166
+ /// returning `null` leaves the row out. With an [order], the output is sorted by
167
+ /// it, merging the few new projections into the previous order instead of
168
+ /// sorting everything again; rows that compare equal have no defined order, so
169
+ /// give a total order (for example by name, then id).
170
+ ///
171
+ /// The projection must depend only on the row. When it also reads something
172
+ /// else that changed (a lookup table, a setting), call [invalidate] so the next
173
+ /// [apply] projects every row again.
174
+ final class NomosRowProjection<T extends Object, R extends Object> {
175
+ NomosRowProjection(R? Function(T row) project, {Comparator<R>? order})
176
+ : _project = project,
177
+ _order = order;
178
+
179
+ final R? Function(T row) _project;
180
+ final Comparator<R>? _order;
181
+ HashMap<T, _Projected<R>> _seen = HashMap<T, _Projected<R>>.identity();
182
+ List<T>? _sources;
183
+ List<R>? _output;
184
+
185
+ /// [rows] projected. Returns the identical previous list when the output did
186
+ /// not change (every row was seen and none was added or left out anew).
187
+ List<R> apply(Iterable<T> rows) {
188
+ final seen = _seen;
189
+ final next = HashMap<T, _Projected<R>>.identity();
190
+ final sources = <T>[];
191
+ final output = <R>[];
192
+ final freshSources = <T>[];
193
+ final freshOutput = <R>[];
194
+ for (final row in rows) {
195
+ var projected = seen[row];
196
+ final fresh = projected == null;
197
+ projected ??= _Projected<R>(_project(row));
198
+ next[row] = projected;
199
+ final value = projected.value;
200
+ if (value == null) continue;
201
+ if (fresh) {
202
+ freshSources.add(row);
203
+ freshOutput.add(value);
204
+ } else {
205
+ sources.add(row);
206
+ output.add(value);
207
+ }
208
+ }
209
+ _seen = next;
210
+
211
+ final previousSources = _sources;
212
+ final previousOutput = _output;
213
+ final order = _order;
214
+ if (order == null) {
215
+ // Input order: rebuild in one pass when anything is new.
216
+ if (freshOutput.isEmpty &&
217
+ previousOutput != null &&
218
+ output.length == previousOutput.length) {
219
+ return previousOutput;
220
+ }
221
+ final all = <T>[];
222
+ final values = <R>[];
223
+ for (final row in rows) {
224
+ final value = next[row]!.value;
225
+ if (value == null) continue;
226
+ all.add(row);
227
+ values.add(value);
228
+ }
229
+ return _publish(all, values);
230
+ }
231
+
232
+ if (previousSources == null || previousOutput == null) {
233
+ final pairs = [
234
+ for (var i = 0; i < freshSources.length; i += 1)
235
+ (freshSources[i], freshOutput[i]),
236
+ ]..sort((a, b) => order(a.$2, b.$2));
237
+ return _publish([for (final p in pairs) p.$1], [for (final p in pairs) p.$2]);
238
+ }
239
+ // The kept rows are already in order: drop the ones that left, then merge
240
+ // in the new projections, sorted among themselves.
241
+ final keptSources = <T>[];
242
+ final keptOutput = <R>[];
243
+ for (var i = 0; i < previousSources.length; i += 1) {
244
+ final source = previousSources[i];
245
+ if (next.containsKey(source)) {
246
+ keptSources.add(source);
247
+ keptOutput.add(previousOutput[i]);
248
+ }
249
+ }
250
+ if (freshOutput.isEmpty && keptOutput.length == previousOutput.length) {
251
+ return previousOutput;
252
+ }
253
+ final fresh = [
254
+ for (var i = 0; i < freshSources.length; i += 1)
255
+ (freshSources[i], freshOutput[i]),
256
+ ]..sort((a, b) => order(a.$2, b.$2));
257
+ final mergedSources = <T>[];
258
+ final mergedOutput = <R>[];
259
+ var k = 0;
260
+ var f = 0;
261
+ while (k < keptOutput.length || f < fresh.length) {
262
+ final takeKept = f >= fresh.length ||
263
+ (k < keptOutput.length && order(keptOutput[k], fresh[f].$2) <= 0);
264
+ if (takeKept) {
265
+ mergedSources.add(keptSources[k]);
266
+ mergedOutput.add(keptOutput[k]);
267
+ k += 1;
268
+ } else {
269
+ mergedSources.add(fresh[f].$1);
270
+ mergedOutput.add(fresh[f].$2);
271
+ f += 1;
272
+ }
273
+ }
274
+ return _publish(mergedSources, mergedOutput);
275
+ }
276
+
277
+ /// Forget every projection: the next [apply] projects every row again.
278
+ void invalidate() {
279
+ _seen = HashMap<T, _Projected<R>>.identity();
280
+ _sources = null;
281
+ _output = null;
282
+ }
283
+
284
+ List<R> _publish(List<T> sources, List<R> output) {
285
+ _sources = sources;
286
+ return _output = List<R>.unmodifiable(output);
287
+ }
288
+ }
289
+
290
+ final class _Projected<R> {
291
+ _Projected(this.value);
292
+ final R? value;
293
+ }
294
+
295
+ /// Row projection for a stream of row lists, such as a generated rows watch
296
+ /// (CAP-751). See [NomosRowProjection].
297
+ extension NomosRowsProjectionStream<T extends Object> on Stream<List<T>> {
298
+ /// Each emission's rows projected with [project] (a `null` result leaves the
299
+ /// row out), optionally sorted by [order]. A row the watch did not change
300
+ /// reuses its earlier projection, and an emission whose output is unchanged
301
+ /// is not forwarded. Each listener keeps its own projections.
302
+ Stream<List<R>> projectRows<R extends Object>(
303
+ R? Function(T row) project, {
304
+ Comparator<R>? order,
305
+ }) {
306
+ final source = this;
307
+ return Stream<List<R>>.multi((controller) {
308
+ final projection = NomosRowProjection<T, R>(project, order: order);
309
+ List<R>? last;
310
+ final subscription = source.listen(
311
+ (rows) {
312
+ final List<R> output;
313
+ try {
314
+ output = projection.apply(rows);
315
+ } catch (error, stack) {
316
+ projection.invalidate();
317
+ controller.addErrorSync(error, stack);
318
+ return;
319
+ }
320
+ if (identical(output, last)) return;
321
+ last = output;
322
+ controller.addSync(output);
323
+ },
324
+ onError: controller.addErrorSync,
325
+ onDone: controller.closeSync,
326
+ );
327
+ controller
328
+ ..onPause = subscription.pause
329
+ ..onResume = subscription.resume
330
+ ..onCancel = subscription.cancel;
331
+ }, isBroadcast: source.isBroadcast);
332
+ }
333
+ }
334
+
335
+ /// Kernel row order: ascending UTF-8 bytes, which is ascending code points.
336
+ /// Dart's [String.compareTo] orders UTF-16 code units and differs above the
337
+ /// Basic Multilingual Plane.
338
+ int compareCodePoints(String left, String right) {
339
+ if (left == right) return 0;
340
+ final a = utf8.encode(left);
341
+ final b = utf8.encode(right);
342
+ final length = a.length < b.length ? a.length : b.length;
343
+ for (var index = 0; index < length; index += 1) {
344
+ final order = a[index] - b[index];
345
+ if (order != 0) return order;
346
+ }
347
+ return a.length - b.length;
348
+ }
349
+
350
+ /// Merge a watch's complete results and deltas into row lists. Emits only when
351
+ /// the rows changed. A delivered watch failure passes through and resets the
352
+ /// rows; a position mismatch ends the stream with [NomosWatchPositionMismatch].
353
+ Stream<List<T>> nmsMergeRows<T>(
354
+ Stream<nomos_protocol.WatchChanged> source,
355
+ NmsRowsMerge<T> merge,
356
+ ) {
357
+ late final StreamController<List<T>> controller;
358
+ StreamSubscription<nomos_protocol.WatchChanged>? upstream;
359
+ controller = StreamController<List<T>>(
360
+ onListen: () {
361
+ upstream = source.listen(
362
+ (changed) {
363
+ final List<T>? rows;
364
+ try {
365
+ rows = merge.apply(changed);
366
+ } on NomosWatchPositionMismatch catch (mismatch, stack) {
367
+ controller.addError(mismatch, stack);
368
+ unawaited(upstream?.cancel());
369
+ unawaited(controller.close());
370
+ return;
371
+ } catch (error, stack) {
372
+ merge.reset();
373
+ controller.addError(error, stack);
374
+ return;
375
+ }
376
+ if (rows != null) controller.add(rows);
377
+ },
378
+ onError: (Object error, StackTrace stack) {
379
+ merge.reset();
380
+ controller.addError(error, stack);
381
+ },
382
+ onDone: () => unawaited(controller.close()),
383
+ );
384
+ },
385
+ onPause: () => upstream?.pause(),
386
+ onResume: () => upstream?.resume(),
387
+ onCancel: () => upstream?.cancel(),
388
+ );
389
+ return controller.stream;
390
+ }