@githolon/dsl 0.110.0 → 0.111.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.
- package/assets/operation-contract.wasm +0 -0
- package/dart/CHANGELOG.md +9 -0
- package/dart/lib/src/reception/event_desk.dart +11 -8
- package/dart/lib/src/reception/progress_desk.dart +4 -3
- package/dart/lib/src/runner/node-entry.mjs +146 -123
- package/dart/lib/src/sync_status_projection.dart +3 -1
- package/dart/pubspec.yaml +1 -1
- package/dart/test/reception_event_desk_close_test.dart +16 -0
- package/package.json +2 -2
- package/src/codegen_call_proto.ts +4 -2
- package/src/codegen_dart.ts +4 -0
- package/src/compile_package_main.ts +29 -12
|
Binary file
|
package/dart/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## 0.66.7
|
|
2
|
+
|
|
3
|
+
- Recover sync after loss of the upstream bookmark by fetching provider history and allowing the kernel to verify a shared ancestor. Preserve pending local work and reject unrelated history through the existing admission gate.
|
|
4
|
+
- Wait for a sealed initial sync base before sending a first suffix, and expose provider/local heads in sync diagnostics.
|
|
5
|
+
|
|
6
|
+
## 0.66.6
|
|
7
|
+
|
|
8
|
+
- Let reception close promptly when presentation stream listeners are paused.
|
|
9
|
+
|
|
1
10
|
## 0.66.5
|
|
2
11
|
|
|
3
12
|
- Resolve the Node runtime runner through `@githolon/runtime`, the public name for the application runtime package.
|
|
@@ -194,17 +194,20 @@ final class ReceptionEventDesk {
|
|
|
194
194
|
|
|
195
195
|
Future<void> close() async {
|
|
196
196
|
await _syncStatusChanges.cancel();
|
|
197
|
-
|
|
197
|
+
// Public broadcast streams can have paused UI listeners. Their close()
|
|
198
|
+
// futures wait for those listeners to resume, so signal done without
|
|
199
|
+
// letting an external subscriber hold reception teardown open.
|
|
200
|
+
if (!_copyChanges.isClosed) unawaited(_copyChanges.close());
|
|
198
201
|
await typedWatches.close();
|
|
199
|
-
if (!_sync.isClosed)
|
|
200
|
-
if (!_custody.isClosed)
|
|
201
|
-
if (!_localWorkRejected.isClosed)
|
|
202
|
+
if (!_sync.isClosed) unawaited(_sync.close());
|
|
203
|
+
if (!_custody.isClosed) unawaited(_custody.close());
|
|
204
|
+
if (!_localWorkRejected.isClosed) unawaited(_localWorkRejected.close());
|
|
202
205
|
if (!_receivingGroupConvergence.isClosed) {
|
|
203
|
-
|
|
206
|
+
unawaited(_receivingGroupConvergence.close());
|
|
204
207
|
}
|
|
205
|
-
if (!_modelAdoption.isClosed)
|
|
206
|
-
if (!_preserved.isClosed)
|
|
207
|
-
if (!_foreignPlacements.isClosed)
|
|
208
|
+
if (!_modelAdoption.isClosed) unawaited(_modelAdoption.close());
|
|
209
|
+
if (!_preserved.isClosed) unawaited(_preserved.close());
|
|
210
|
+
if (!_foreignPlacements.isClosed) unawaited(_foreignPlacements.close());
|
|
208
211
|
await progress.close();
|
|
209
212
|
await syncStatuses.close();
|
|
210
213
|
}
|
|
@@ -260,8 +260,9 @@ final class ReceptionProgressDesk {
|
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
Future<void> close() async {
|
|
263
|
-
|
|
264
|
-
if (!
|
|
265
|
-
if (!
|
|
263
|
+
// Observers may be paused while their widget tree is being replaced.
|
|
264
|
+
if (!_spans.isClosed) unawaited(_spans.close());
|
|
265
|
+
if (!_transfers.isClosed) unawaited(_transfers.close());
|
|
266
|
+
if (!_kernel.isClosed) unawaited(_kernel.close());
|
|
266
267
|
}
|
|
267
268
|
}
|
|
@@ -21835,6 +21835,15 @@ var ReplicaFollowController = class {
|
|
|
21835
21835
|
}
|
|
21836
21836
|
if (result.stillAhead || result.pushFailed || result.directOfferDeferred || result.reconnectRequired) {
|
|
21837
21837
|
if (result.directOfferDeferred) {
|
|
21838
|
+
if (!result.reconnectRequired) {
|
|
21839
|
+
this.#desk.transition(result.workspace, {
|
|
21840
|
+
case: ReplicaFollowEventKind.Failed,
|
|
21841
|
+
nowUnixMillis,
|
|
21842
|
+
failure: "remote workspace opening; retrying",
|
|
21843
|
+
retryWithinMillis: 5e3
|
|
21844
|
+
});
|
|
21845
|
+
return delayUntilDue(this.#desk.get(result.workspace), nowUnixMillis);
|
|
21846
|
+
}
|
|
21838
21847
|
this.#desk.transition(result.workspace, {
|
|
21839
21848
|
case: ReplicaFollowEventKind.DependencyDeferred,
|
|
21840
21849
|
nowUnixMillis,
|
|
@@ -23740,6 +23749,34 @@ function resolveCustodyDisagreement(kernel, mount, requestId, policy, verdict) {
|
|
|
23740
23749
|
init_define_NOMOS_KERNEL_RELEASE();
|
|
23741
23750
|
var CUSTODY_OFFER_MEDIA_TYPE = "application/vnd.nomos.custody-offer-batch+protobuf";
|
|
23742
23751
|
var CUSTODY_OFFER_RESULT_MEDIA_TYPE = "application/vnd.nomos.custody-offer-result+protobuf";
|
|
23752
|
+
function settleCustodyOfferExchange(awaiting, response) {
|
|
23753
|
+
if (response.status === 202 && response.contentType?.includes("application/json")) {
|
|
23754
|
+
try {
|
|
23755
|
+
const pending = JSON.parse(new TextDecoder().decode(response.body));
|
|
23756
|
+
if (pending?.retryable === true && typeof pending.preparing === "string") {
|
|
23757
|
+
return { case: "maintenance", kind: pending.preparing };
|
|
23758
|
+
}
|
|
23759
|
+
} catch {
|
|
23760
|
+
}
|
|
23761
|
+
}
|
|
23762
|
+
if (!response.contentType?.includes(CUSTODY_OFFER_RESULT_MEDIA_TYPE)) {
|
|
23763
|
+
return {
|
|
23764
|
+
case: "unexpected",
|
|
23765
|
+
status: response.status,
|
|
23766
|
+
...response.contentType === void 0 ? {} : { contentType: response.contentType }
|
|
23767
|
+
};
|
|
23768
|
+
}
|
|
23769
|
+
const verdict = decodeCustodyOffer(response.body, awaiting.requestId);
|
|
23770
|
+
const receipt = custodyOfferReceipt(awaiting.requestId, verdict);
|
|
23771
|
+
switch (verdict.case) {
|
|
23772
|
+
case "admission":
|
|
23773
|
+
return { case: "admission", value: verdict.value, receipt, status: response.status };
|
|
23774
|
+
case "failure":
|
|
23775
|
+
return { case: "failure", value: verdict.value, receipt, status: response.status };
|
|
23776
|
+
case "blocked":
|
|
23777
|
+
return { case: "blocked", value: verdict.value, receipt, status: response.status };
|
|
23778
|
+
}
|
|
23779
|
+
}
|
|
23743
23780
|
function encodeInlineCustodyOffer(args) {
|
|
23744
23781
|
return toBinary(CustodyOfferBatchRequestSchema, create(CustodyOfferBatchRequestSchema, {
|
|
23745
23782
|
protocolVersion: 2,
|
|
@@ -23866,6 +23903,7 @@ function summarizeCustodyAdmission(admission) {
|
|
|
23866
23903
|
var PLACEMENT_REF3 = "refs/nomos/placement";
|
|
23867
23904
|
var LAW_PRODUCTS_MEDIA_TYPE = "application/vnd.nomos.export-law-products-response+protobuf";
|
|
23868
23905
|
var WorkspaceSyncPhase = Object.freeze({
|
|
23906
|
+
Idle: "idle",
|
|
23869
23907
|
Probe: "probe",
|
|
23870
23908
|
Offer: "offer",
|
|
23871
23909
|
Lease: "lease",
|
|
@@ -23875,7 +23913,6 @@ var WorkspaceSyncPhase = Object.freeze({
|
|
|
23875
23913
|
Done: "done"
|
|
23876
23914
|
});
|
|
23877
23915
|
var trackedMain = "refs/remotes/nomos/main";
|
|
23878
|
-
var NO_COMMON_BASE = "this replica holds local history with no frontier its upstream ever confirmed; convergence needs a common admitted base \u2014 drop the local copy to refetch it, or re-discover a provider holding this lineage";
|
|
23879
23916
|
var deferredRemote = "refs/remotes/nomos/deferred";
|
|
23880
23917
|
var deferredLocal = "refs/nomos/sync-deferred-local";
|
|
23881
23918
|
var judgedThisLifetime = /* @__PURE__ */ new Map();
|
|
@@ -23899,7 +23936,6 @@ var WorkspaceSyncWork = class {
|
|
|
23899
23936
|
#rediscover;
|
|
23900
23937
|
/** The route the directory's answer yields, recomputed once that answer is observed. */
|
|
23901
23938
|
#reroute;
|
|
23902
|
-
#placementFetch;
|
|
23903
23939
|
#discoveredFrontier;
|
|
23904
23940
|
#observePlacement;
|
|
23905
23941
|
/** Ask the directory for the provider's current placement frontier this round although a
|
|
@@ -23908,19 +23944,14 @@ var WorkspaceSyncWork = class {
|
|
|
23908
23944
|
#directoryAnswered;
|
|
23909
23945
|
#placementFetched;
|
|
23910
23946
|
/** A round which converged without leasing, finishing once placement alone is fetched. */
|
|
23911
|
-
#afterPlacement;
|
|
23912
23947
|
#leased = false;
|
|
23913
|
-
#lease;
|
|
23914
23948
|
#command;
|
|
23915
23949
|
#requestId;
|
|
23916
23950
|
#started;
|
|
23917
|
-
#
|
|
23951
|
+
#state = { phase: WorkspaceSyncPhase.Idle };
|
|
23918
23952
|
#localHead;
|
|
23919
23953
|
#offerHead;
|
|
23920
23954
|
#previousHead;
|
|
23921
|
-
#fetchStarted;
|
|
23922
|
-
#offerStarted;
|
|
23923
|
-
#offerRequestId;
|
|
23924
23955
|
#pushMillis = 0;
|
|
23925
23956
|
#pushBytes = 0n;
|
|
23926
23957
|
#desiredHead;
|
|
@@ -24015,7 +24046,7 @@ var WorkspaceSyncWork = class {
|
|
|
24015
24046
|
throw new Error(`replica '${this.#binding.replicaId}' has no custody remote`);
|
|
24016
24047
|
}
|
|
24017
24048
|
if (this.#rediscover || this.#recheckPlacement || !this.#route?.placementObserved && !this.#route?.placementFrontier && !this.#kernel.resolveRef(this.#mount, this.#requestId, PLACEMENT_REF3)) {
|
|
24018
|
-
this.#
|
|
24049
|
+
this.#state = { phase: WorkspaceSyncPhase.Discover };
|
|
24019
24050
|
return this.#remember({
|
|
24020
24051
|
case: "pending",
|
|
24021
24052
|
value: create(CorrespondencePendingSchema, {
|
|
@@ -24035,13 +24066,13 @@ var WorkspaceSyncWork = class {
|
|
|
24035
24066
|
#openRound() {
|
|
24036
24067
|
if (this.#receiveState || this.#replaceLocalHead !== void 0) return this.#leaseEffect();
|
|
24037
24068
|
if (!this.#localHead) return this.#leaseEffect();
|
|
24038
|
-
|
|
24069
|
+
const requestId = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}:frontier`;
|
|
24039
24070
|
return this.#beginOffer(encodeInlineCustodyOffer({
|
|
24040
24071
|
session: this.#binding.replicaId,
|
|
24041
|
-
requestId
|
|
24072
|
+
requestId,
|
|
24042
24073
|
observedHead: this.#localHead,
|
|
24043
24074
|
intents: []
|
|
24044
|
-
}), true);
|
|
24075
|
+
}), { case: "awaiting", requestId }, true);
|
|
24045
24076
|
}
|
|
24046
24077
|
#offerOrReconcile() {
|
|
24047
24078
|
if (this.#command.admit && this.#localHead && this.#localHead !== this.#previousHead) {
|
|
@@ -24062,13 +24093,13 @@ var WorkspaceSyncWork = class {
|
|
|
24062
24093
|
throw new Error(`kernel did not classify transport for intent '${unspecified.intentId}'`);
|
|
24063
24094
|
}
|
|
24064
24095
|
if (suffix.length > 0) {
|
|
24065
|
-
|
|
24066
|
-
|
|
24096
|
+
const requestId2 = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}`;
|
|
24097
|
+
const startedAt = this.#clock.monotonicNanoseconds();
|
|
24067
24098
|
this.#offerAfterConverge = true;
|
|
24068
24099
|
this.#rebaseFrom = this.#previousHead;
|
|
24069
24100
|
return this.#beginOffer(encodeInlineCustodyOffer({
|
|
24070
24101
|
session: this.#binding.replicaId,
|
|
24071
|
-
requestId:
|
|
24102
|
+
requestId: requestId2,
|
|
24072
24103
|
...this.#previousHead === void 0 ? {} : { baseHead: this.#previousHead },
|
|
24073
24104
|
observedHead: this.#localHead,
|
|
24074
24105
|
intents: suffix,
|
|
@@ -24079,51 +24110,26 @@ var WorkspaceSyncWork = class {
|
|
|
24079
24110
|
this.#previousHead
|
|
24080
24111
|
) : void 0,
|
|
24081
24112
|
fastForwardOnly: true
|
|
24082
|
-
}));
|
|
24113
|
+
}), { case: "awaiting", requestId: requestId2, startedAt });
|
|
24083
24114
|
}
|
|
24084
24115
|
}
|
|
24085
|
-
|
|
24116
|
+
const requestId = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}:reconcile`;
|
|
24086
24117
|
return this.#beginOffer(encodeInlineCustodyOffer({
|
|
24087
24118
|
session: this.#binding.replicaId,
|
|
24088
|
-
requestId
|
|
24119
|
+
requestId,
|
|
24089
24120
|
baseHead: this.#previousHead,
|
|
24090
24121
|
observedHead: this.#localHead,
|
|
24091
24122
|
intents: []
|
|
24092
|
-
}));
|
|
24123
|
+
}), { case: "awaiting", requestId });
|
|
24093
24124
|
}
|
|
24094
24125
|
/**
|
|
24095
|
-
*
|
|
24096
|
-
*
|
|
24097
|
-
*
|
|
24098
|
-
*
|
|
24099
|
-
* state ("a non-empty live ledger requires a common admitted base"), so every step after the
|
|
24100
|
-
* O(1) frontier probe is wasted work — and it is not cheap waste: the fetch negotiates with
|
|
24101
|
-
* no `have` (a full-history pack), and the dependency planner decodes EVERY intent from
|
|
24102
|
-
* genesis and inspects every change, once per round. That is O(history) per follow round,
|
|
24103
|
-
* repeated at every retry, for a round that can only end in a refusal.
|
|
24104
|
-
*
|
|
24105
|
-
* The one base this round can establish costs nothing and is established before this is
|
|
24106
|
-
* asked: an upstream holding exactly this replica's head (the probe's answer) converges it
|
|
24107
|
-
* and records the tracking frontier. Anything else needs the local copy to be dropped and
|
|
24108
|
-
* refetched (an application decision, which preserves the copy), or a re-discovery that
|
|
24109
|
-
* names a provider holding this lineage. Both are reported, at O(1), as this failure.
|
|
24110
|
-
*/
|
|
24111
|
-
/**
|
|
24112
|
-
* Establish this round's base when none is tracked, without walking any history it can avoid.
|
|
24113
|
-
*
|
|
24114
|
-
* The upstream just named its own frontier. If that commit is in THIS replica's lineage, it is
|
|
24115
|
-
* a common admitted base by definition — both sides hold it — and adopting it turns the round
|
|
24116
|
-
* into an ordinary fast-forward: the offer carries only the suffix above it, the fetch can name
|
|
24117
|
-
* it as a truthful `have`, and convergence has the base the kernel requires. One commit walk
|
|
24118
|
-
* (no change decoding, no pack, no network) decides it.
|
|
24119
|
-
*
|
|
24120
|
-
* If it is not in this replica's lineage, no base exists to establish and the round is refused
|
|
24121
|
-
* (#noCommonBase) before it leases, fetches or plans anything.
|
|
24122
|
-
*
|
|
24123
|
-
* A kernel without the commit walk cannot be asked, so it keeps the previous behaviour.
|
|
24126
|
+
* Establish a cheap base when the provider's current tip is in our local chain.
|
|
24127
|
+
* A negative answer means the base is unknown: both sides may have advanced from
|
|
24128
|
+
* an earlier shared commit. Acquire the candidate and let the kernel discover
|
|
24129
|
+
* and judge that history. A tracking bookmark is not an admission prerequisite.
|
|
24124
24130
|
*/
|
|
24125
24131
|
#establishBase(upstreamHead) {
|
|
24126
|
-
if (!this.#
|
|
24132
|
+
if (!this.#missingTrackedBase() || !this.#kernel.historySummary) return true;
|
|
24127
24133
|
const summary = this.#kernel.historySummary(this.#mount, this.#requestId, upstreamHead, this.#localHead);
|
|
24128
24134
|
if (!summary.reachedBase) return false;
|
|
24129
24135
|
this.#previousHead = upstreamHead;
|
|
@@ -24138,26 +24144,9 @@ var WorkspaceSyncWork = class {
|
|
|
24138
24144
|
get establishedBase() {
|
|
24139
24145
|
return this.#establishedBase;
|
|
24140
24146
|
}
|
|
24141
|
-
#
|
|
24147
|
+
#missingTrackedBase() {
|
|
24142
24148
|
return this.#previousHead === void 0 && this.#localHead !== void 0 && this.#replaceLocalHead === void 0;
|
|
24143
24149
|
}
|
|
24144
|
-
#noCommonBaseResult() {
|
|
24145
|
-
return this.#finish(create(SyncWorkspaceResponseSchema, {
|
|
24146
|
-
workspace: this.#mount.workspace,
|
|
24147
|
-
...this.#localHead === void 0 ? {} : { currentHead: this.#localHead },
|
|
24148
|
-
stillAhead: true,
|
|
24149
|
-
...this.#offerReceipt === void 0 ? {} : { offerReceipt: this.#offerReceipt },
|
|
24150
|
-
pushError: create(RuntimeTransportFailureSchema, {
|
|
24151
|
-
endpoint: this.#offerEndpoint(),
|
|
24152
|
-
status: 409,
|
|
24153
|
-
refused: true,
|
|
24154
|
-
message: NO_COMMON_BASE
|
|
24155
|
-
}),
|
|
24156
|
-
timings: create(SyncPhaseTimingsSchema, {
|
|
24157
|
-
convergeMillis: elapsedMillis(this.#clock, this.#started)
|
|
24158
|
-
})
|
|
24159
|
-
}));
|
|
24160
|
-
}
|
|
24161
24150
|
#holdsLocalWork() {
|
|
24162
24151
|
return this.#command.admit && !!this.#localHead && this.#localHead !== this.#previousHead;
|
|
24163
24152
|
}
|
|
@@ -24165,18 +24154,18 @@ var WorkspaceSyncWork = class {
|
|
|
24165
24154
|
#pullBeforeOffer() {
|
|
24166
24155
|
this.#offerAfterConverge = true;
|
|
24167
24156
|
this.#rebaseFrom = this.#previousHead;
|
|
24168
|
-
|
|
24157
|
+
const requestId = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}:pull-${this.#rebases}`;
|
|
24169
24158
|
return this.#beginOffer(encodeInlineCustodyOffer({
|
|
24170
24159
|
session: this.#binding.replicaId,
|
|
24171
|
-
requestId
|
|
24160
|
+
requestId,
|
|
24172
24161
|
...this.#previousHead === void 0 ? {} : { baseHead: this.#previousHead },
|
|
24173
24162
|
observedHead: this.#localHead,
|
|
24174
24163
|
intents: []
|
|
24175
|
-
}));
|
|
24164
|
+
}), { case: "awaiting", requestId });
|
|
24176
24165
|
}
|
|
24177
|
-
#leaseEffect() {
|
|
24166
|
+
#leaseEffect(afterPlacement) {
|
|
24178
24167
|
const token = this.#authToken();
|
|
24179
|
-
this.#
|
|
24168
|
+
this.#state = { phase: WorkspaceSyncPhase.Lease, ...afterPlacement ? { afterPlacement } : {} };
|
|
24180
24169
|
return this.#remember({
|
|
24181
24170
|
case: "pending",
|
|
24182
24171
|
value: create(CorrespondencePendingSchema, {
|
|
@@ -24205,35 +24194,38 @@ var WorkspaceSyncWork = class {
|
|
|
24205
24194
|
`workspace sync '${this.workId}' expected effect '${expected}', received '${receipt.effectId}'`
|
|
24206
24195
|
);
|
|
24207
24196
|
}
|
|
24197
|
+
const state = this.#state;
|
|
24208
24198
|
const previousReceipt = this.#lastReceiptEffectId;
|
|
24209
24199
|
this.#lastReceiptEffectId = receipt.effectId;
|
|
24210
24200
|
try {
|
|
24211
24201
|
if (receipt.outcome.case === "failure") {
|
|
24212
|
-
if (
|
|
24202
|
+
if (state.phase === WorkspaceSyncPhase.Discover) return this.#openRound();
|
|
24213
24203
|
return this.#transportFailure(
|
|
24214
24204
|
receipt.outcome.value.message,
|
|
24215
24205
|
receipt.outcome.value.reason === PeerEffectFailureReason.OFFLINE
|
|
24216
24206
|
);
|
|
24217
24207
|
}
|
|
24218
|
-
if (
|
|
24219
|
-
|
|
24220
|
-
|
|
24221
|
-
if (
|
|
24222
|
-
|
|
24223
|
-
|
|
24208
|
+
if (state.phase === WorkspaceSyncPhase.Offer || state.phase === WorkspaceSyncPhase.Probe) {
|
|
24209
|
+
return this.#acceptOffer(receipt, state);
|
|
24210
|
+
}
|
|
24211
|
+
if (state.phase === WorkspaceSyncPhase.Lease) return this.#acceptLease(receipt, state);
|
|
24212
|
+
if (state.phase === WorkspaceSyncPhase.Discover) return this.#acceptDiscovery(receipt);
|
|
24213
|
+
if (state.phase === WorkspaceSyncPhase.Placement) {
|
|
24214
|
+
state.transfer.accept(receipt);
|
|
24224
24215
|
this.#placementFetched?.();
|
|
24225
|
-
if (
|
|
24226
|
-
return this.#beginMainFetch(
|
|
24216
|
+
if (state.afterPlacement) return this.#finish(state.afterPlacement);
|
|
24217
|
+
return this.#beginMainFetch(state.lease);
|
|
24227
24218
|
}
|
|
24228
|
-
if (
|
|
24219
|
+
if (state.phase === WorkspaceSyncPhase.Fetch) return this.#acceptFetch(receipt, state);
|
|
24229
24220
|
throw new Error(`workspace sync '${this.workId}' is already complete`);
|
|
24230
24221
|
} catch (error) {
|
|
24231
24222
|
this.#lastReceiptEffectId = previousReceipt;
|
|
24232
24223
|
throw error;
|
|
24233
24224
|
}
|
|
24234
24225
|
}
|
|
24235
|
-
#beginOffer(body, probe = false) {
|
|
24236
|
-
|
|
24226
|
+
#beginOffer(body, exchange, probe = false) {
|
|
24227
|
+
const phase = probe ? WorkspaceSyncPhase.Probe : WorkspaceSyncPhase.Offer;
|
|
24228
|
+
this.#state = { phase, exchange };
|
|
24237
24229
|
const token = this.#authToken();
|
|
24238
24230
|
this.#pushBytes += BigInt(body.byteLength);
|
|
24239
24231
|
return this.#remember({
|
|
@@ -24243,7 +24235,7 @@ var WorkspaceSyncWork = class {
|
|
|
24243
24235
|
effect: {
|
|
24244
24236
|
case: "http",
|
|
24245
24237
|
value: create(PeerHttpEffectSchema, {
|
|
24246
|
-
effectId: `${this.workId}:${
|
|
24238
|
+
effectId: `${this.workId}:${phase}`,
|
|
24247
24239
|
method: PeerHttpMethod.POST,
|
|
24248
24240
|
url: this.#offerEndpoint(),
|
|
24249
24241
|
contentType: CUSTODY_OFFER_MEDIA_TYPE,
|
|
@@ -24260,28 +24252,30 @@ var WorkspaceSyncWork = class {
|
|
|
24260
24252
|
})
|
|
24261
24253
|
});
|
|
24262
24254
|
}
|
|
24263
|
-
#acceptOffer(receipt) {
|
|
24264
|
-
|
|
24255
|
+
#acceptOffer(receipt, state) {
|
|
24256
|
+
const exchange = state.exchange;
|
|
24257
|
+
this.#pushMillis = exchange.startedAt === void 0 ? 0 : elapsedMillis(this.#clock, exchange.startedAt);
|
|
24265
24258
|
if (receipt.outcome.case !== "response") {
|
|
24266
24259
|
throw new Error("platform returned the wrong receipt for a custody offer");
|
|
24267
24260
|
}
|
|
24268
|
-
const
|
|
24269
|
-
if (
|
|
24261
|
+
const outcome = settleCustodyOfferExchange(exchange, receipt.outcome.value);
|
|
24262
|
+
if (outcome.case === "maintenance") {
|
|
24263
|
+
return this.#finishOfferOutcome(true, false);
|
|
24264
|
+
}
|
|
24265
|
+
if (outcome.case === "unexpected") {
|
|
24270
24266
|
this.#pushError = create(RuntimeTransportFailureSchema, {
|
|
24271
24267
|
endpoint: this.#offerEndpoint(),
|
|
24272
|
-
status:
|
|
24268
|
+
status: outcome.status,
|
|
24273
24269
|
refused: false,
|
|
24274
|
-
message: `Sync received an unexpected server response (HTTP ${
|
|
24270
|
+
message: `Sync received an unexpected server response (HTTP ${outcome.status}; ${outcome.contentType || "no content type"}). No custody receipt was returned.`
|
|
24275
24271
|
});
|
|
24276
24272
|
return this.#finishOfferOutcome(false);
|
|
24277
24273
|
}
|
|
24278
|
-
|
|
24279
|
-
const outcome = decodeCustodyOffer(response.body, this.#offerRequestId);
|
|
24280
|
-
this.#offerReceipt = custodyOfferReceipt(this.#offerRequestId, outcome);
|
|
24274
|
+
this.#offerReceipt = outcome.receipt;
|
|
24281
24275
|
if (outcome.case === "failure") {
|
|
24282
24276
|
this.#pushError = create(RuntimeTransportFailureSchema, {
|
|
24283
24277
|
endpoint: this.#offerEndpoint(),
|
|
24284
|
-
status:
|
|
24278
|
+
status: outcome.status,
|
|
24285
24279
|
refused: false,
|
|
24286
24280
|
message: `${outcome.value.code}: ${outcome.value.message}`
|
|
24287
24281
|
});
|
|
@@ -24290,12 +24284,12 @@ var WorkspaceSyncWork = class {
|
|
|
24290
24284
|
if (outcome.case === "blocked") {
|
|
24291
24285
|
return this.#finishOfferOutcome(true);
|
|
24292
24286
|
}
|
|
24293
|
-
if (
|
|
24287
|
+
if (outcome.status < 200 || outcome.status >= 300) {
|
|
24294
24288
|
this.#pushError = create(RuntimeTransportFailureSchema, {
|
|
24295
24289
|
endpoint: this.#offerEndpoint(),
|
|
24296
|
-
status:
|
|
24297
|
-
refused:
|
|
24298
|
-
message: `custody offer returned an admission verdict with HTTP ${
|
|
24290
|
+
status: outcome.status,
|
|
24291
|
+
refused: outcome.status === 422,
|
|
24292
|
+
message: `custody offer returned an admission verdict with HTTP ${outcome.status}`
|
|
24299
24293
|
});
|
|
24300
24294
|
return this.#finishOfferOutcome(false);
|
|
24301
24295
|
}
|
|
@@ -24303,8 +24297,7 @@ var WorkspaceSyncWork = class {
|
|
|
24303
24297
|
this.#desiredHead = admission.main || void 0;
|
|
24304
24298
|
if (!this.#desiredHead) return this.#transportFailure("resident peer returned no admitted frontier", false);
|
|
24305
24299
|
this.#candidateDurable = admission.sealedMain === this.#desiredHead;
|
|
24306
|
-
if (
|
|
24307
|
-
if (!this.#establishBase(this.#desiredHead)) return this.#noCommonBaseResult();
|
|
24300
|
+
if (state.phase === WorkspaceSyncPhase.Probe && this.#desiredHead !== this.#localHead) {
|
|
24308
24301
|
if (this.#alreadyJudged(this.#desiredHead)) {
|
|
24309
24302
|
return this.#finish(create(SyncWorkspaceResponseSchema, {
|
|
24310
24303
|
workspace: this.#mount.workspace,
|
|
@@ -24316,6 +24309,22 @@ var WorkspaceSyncWork = class {
|
|
|
24316
24309
|
offerReceipt: this.#offerReceipt
|
|
24317
24310
|
}));
|
|
24318
24311
|
}
|
|
24312
|
+
if (!this.#establishBase(this.#desiredHead)) {
|
|
24313
|
+
this.#offerAfterConverge = this.#command.admit;
|
|
24314
|
+
this.#rebaseFrom = void 0;
|
|
24315
|
+
return this.#leaseEffect();
|
|
24316
|
+
}
|
|
24317
|
+
if (this.#establishedBase && !this.#candidateDurable && this.#kernel.resolveRef(this.#mount, this.#requestId, trackedMain) === void 0) {
|
|
24318
|
+
return this.#finish(create(SyncWorkspaceResponseSchema, {
|
|
24319
|
+
workspace: this.#mount.workspace,
|
|
24320
|
+
currentHead: this.#localHead,
|
|
24321
|
+
previousHead: this.#previousHead,
|
|
24322
|
+
stillAhead: true,
|
|
24323
|
+
directOfferDeferred: true,
|
|
24324
|
+
reconnectRequired: false,
|
|
24325
|
+
offerReceipt: this.#offerReceipt
|
|
24326
|
+
}));
|
|
24327
|
+
}
|
|
24319
24328
|
if (this.#probeOnly) {
|
|
24320
24329
|
this.#needsReconciliation = true;
|
|
24321
24330
|
return this.#finish(create(SyncWorkspaceResponseSchema, {
|
|
@@ -24361,11 +24370,11 @@ var WorkspaceSyncWork = class {
|
|
|
24361
24370
|
return this.#converge(this.#desiredHead, admission.resultPack.byteLength, 0);
|
|
24362
24371
|
}
|
|
24363
24372
|
if (this.#desiredHead === this.#previousHead) return this.#converge(this.#desiredHead, 0, 0);
|
|
24364
|
-
|
|
24373
|
+
this.#establishBase(this.#desiredHead);
|
|
24365
24374
|
if (admission.recoveryRequired !== false) return this.#leaseEffect();
|
|
24366
24375
|
return this.#transportFailure("resident peer supplied neither the missing delta nor a history gap", false);
|
|
24367
24376
|
}
|
|
24368
|
-
#acceptLease(receipt) {
|
|
24377
|
+
#acceptLease(receipt, state) {
|
|
24369
24378
|
if (receipt.outcome.case !== "response") {
|
|
24370
24379
|
throw new Error("platform returned the wrong receipt for a Git read lease");
|
|
24371
24380
|
}
|
|
@@ -24381,21 +24390,25 @@ var WorkspaceSyncWork = class {
|
|
|
24381
24390
|
this.#leased = true;
|
|
24382
24391
|
const frontier = this.#placementFrontier();
|
|
24383
24392
|
if (frontier) {
|
|
24384
|
-
|
|
24393
|
+
const transfer = new PlacementFetch(
|
|
24385
24394
|
this.#kernel,
|
|
24386
24395
|
this.#mount,
|
|
24387
24396
|
this.#requestId,
|
|
24388
24397
|
this.workId,
|
|
24389
24398
|
frontier
|
|
24390
24399
|
);
|
|
24391
|
-
const pending =
|
|
24400
|
+
const pending = transfer.start(lease);
|
|
24392
24401
|
if (pending) {
|
|
24393
|
-
this.#
|
|
24394
|
-
|
|
24402
|
+
this.#state = {
|
|
24403
|
+
phase: WorkspaceSyncPhase.Placement,
|
|
24404
|
+
transfer,
|
|
24405
|
+
lease,
|
|
24406
|
+
...state.afterPlacement ? { afterPlacement: state.afterPlacement } : {}
|
|
24407
|
+
};
|
|
24395
24408
|
return this.#remember({ case: "pending", value: pending });
|
|
24396
24409
|
}
|
|
24397
24410
|
}
|
|
24398
|
-
if (
|
|
24411
|
+
if (state.afterPlacement) return this.#finish(state.afterPlacement);
|
|
24399
24412
|
return this.#beginMainFetch(lease);
|
|
24400
24413
|
}
|
|
24401
24414
|
/** The provider's placement frontier as this round knows it: what the directory named in this
|
|
@@ -24415,8 +24428,7 @@ var WorkspaceSyncWork = class {
|
|
|
24415
24428
|
if (!new PlacementFetch(this.#kernel, this.#mount, this.#requestId, this.workId, frontier).wanted()) {
|
|
24416
24429
|
return void 0;
|
|
24417
24430
|
}
|
|
24418
|
-
this.#
|
|
24419
|
-
return this.#leaseEffect();
|
|
24431
|
+
return this.#leaseEffect(presented);
|
|
24420
24432
|
}
|
|
24421
24433
|
#acceptDiscovery(receipt) {
|
|
24422
24434
|
this.#directoryAnswered?.();
|
|
@@ -24440,9 +24452,11 @@ var WorkspaceSyncWork = class {
|
|
|
24440
24452
|
return this.#replaceLocalHead !== void 0 || this.#localHead === void 0 && this.#previousHead === void 0;
|
|
24441
24453
|
}
|
|
24442
24454
|
#beginMainFetch(lease) {
|
|
24443
|
-
this.#phase = WorkspaceSyncPhase.Fetch;
|
|
24444
24455
|
const lawProducts = this.#lawProductsAlongside();
|
|
24445
|
-
this.#
|
|
24456
|
+
this.#state = {
|
|
24457
|
+
phase: WorkspaceSyncPhase.Fetch,
|
|
24458
|
+
startedAt: this.#clock.monotonicNanoseconds()
|
|
24459
|
+
};
|
|
24446
24460
|
const haves = this.#replaceLocalHead !== void 0 ? this.#repairMissingCommit ? [] : [this.#kernel.resolveRef(this.#mount, this.#requestId, deferredRemote)].filter(
|
|
24447
24461
|
(head) => !!head
|
|
24448
24462
|
) : [...new Set([
|
|
@@ -24473,7 +24487,7 @@ var WorkspaceSyncWork = class {
|
|
|
24473
24487
|
})
|
|
24474
24488
|
});
|
|
24475
24489
|
}
|
|
24476
|
-
#acceptFetch(receipt) {
|
|
24490
|
+
#acceptFetch(receipt, state) {
|
|
24477
24491
|
if (receipt.outcome.case !== "gitFetch") {
|
|
24478
24492
|
throw new Error("platform returned the wrong receipt for a Git object fetch");
|
|
24479
24493
|
}
|
|
@@ -24483,7 +24497,7 @@ var WorkspaceSyncWork = class {
|
|
|
24483
24497
|
if (fetched.pack.byteLength > 0) {
|
|
24484
24498
|
this.#kernel.applyPack(this.#mount, this.#requestId, fetched.pack, fetched.head, fetched.shallow);
|
|
24485
24499
|
}
|
|
24486
|
-
const prefetchMillis =
|
|
24500
|
+
const prefetchMillis = elapsedMillis(this.#clock, state.startedAt);
|
|
24487
24501
|
const candidateHead = this.#receiveState ? this.#kernel.resolveStatePosition?.(this.#mount, this.#requestId, fetched.head, this.#receiveState) : fetched.head;
|
|
24488
24502
|
if (!candidateHead) {
|
|
24489
24503
|
return this.#finish(create(SyncWorkspaceResponseSchema, {
|
|
@@ -24579,7 +24593,7 @@ var WorkspaceSyncWork = class {
|
|
|
24579
24593
|
}
|
|
24580
24594
|
}
|
|
24581
24595
|
#converge(candidateHead, receivedBytes, prefetchMillis) {
|
|
24582
|
-
|
|
24596
|
+
this.#establishBase(candidateHead);
|
|
24583
24597
|
this.#beforeConverge?.(candidateHead, this.#previousHead);
|
|
24584
24598
|
const convergeStarted = this.#clock.monotonicNanoseconds();
|
|
24585
24599
|
let judged;
|
|
@@ -24834,11 +24848,11 @@ var WorkspaceSyncWork = class {
|
|
|
24834
24848
|
pushBytes: this.#pushBytes,
|
|
24835
24849
|
pullBytes: BigInt(receivedBytes),
|
|
24836
24850
|
serverMillis: this.#admissionSummary?.serverMillis ?? 0,
|
|
24837
|
-
offerInPush: this.#
|
|
24851
|
+
offerInPush: this.#pushBytes > 0n
|
|
24838
24852
|
})
|
|
24839
24853
|
});
|
|
24840
24854
|
}
|
|
24841
|
-
#finishOfferOutcome(blocked) {
|
|
24855
|
+
#finishOfferOutcome(blocked, reconnectRequired = true) {
|
|
24842
24856
|
return this.#finish(create(SyncWorkspaceResponseSchema, {
|
|
24843
24857
|
workspace: this.#mount.workspace,
|
|
24844
24858
|
previousHead: this.#previousHead,
|
|
@@ -24846,7 +24860,7 @@ var WorkspaceSyncWork = class {
|
|
|
24846
24860
|
admitted: BigInt(this.#admissionSummary?.admittedIntentIds.length ?? 0),
|
|
24847
24861
|
refused: BigInt((this.#admissionSummary?.refusedMessages.length ?? 0) + this.#parkedLocal),
|
|
24848
24862
|
deferred: BigInt(this.#admissionSummary ? this.#admissionSummary.blockedMessages.length + this.#admissionSummary.deadLetteredMessages.length + this.#admissionSummary.quarantinedMessages.length + this.#admissionSummary.deferredMessages.length : blocked ? 1 : 0),
|
|
24849
|
-
reconnectRequired
|
|
24863
|
+
reconnectRequired,
|
|
24850
24864
|
stillAhead: this.#localHead !== this.#previousHead,
|
|
24851
24865
|
directOfferDeferred: blocked,
|
|
24852
24866
|
offerReceipt: this.#offerReceipt,
|
|
@@ -24860,15 +24874,16 @@ var WorkspaceSyncWork = class {
|
|
|
24860
24874
|
}));
|
|
24861
24875
|
}
|
|
24862
24876
|
#transportFailure(message, offline, status = -1) {
|
|
24863
|
-
const
|
|
24877
|
+
const state = this.#state;
|
|
24878
|
+
const pushFailed = state.phase === WorkspaceSyncPhase.Offer;
|
|
24864
24879
|
const failedEndpoint = this.#offerEndpoint();
|
|
24865
24880
|
const failure = create(RuntimeTransportFailureSchema, {
|
|
24866
24881
|
endpoint: pushFailed ? failedEndpoint : this.#binding.cloud ?? "",
|
|
24867
24882
|
status,
|
|
24868
24883
|
message
|
|
24869
24884
|
});
|
|
24870
|
-
if (
|
|
24871
|
-
this.#pushMillis =
|
|
24885
|
+
if (state.phase === WorkspaceSyncPhase.Offer) {
|
|
24886
|
+
this.#pushMillis = state.exchange.startedAt === void 0 ? 0 : elapsedMillis(this.#clock, state.exchange.startedAt);
|
|
24872
24887
|
this.#pushError = failure;
|
|
24873
24888
|
}
|
|
24874
24889
|
return this.#finish(create(SyncWorkspaceResponseSchema, {
|
|
@@ -24885,7 +24900,7 @@ var WorkspaceSyncWork = class {
|
|
|
24885
24900
|
pushMillis: this.#pushMillis,
|
|
24886
24901
|
pushBytes: this.#pushBytes,
|
|
24887
24902
|
prefetchMillis: elapsedMillis(this.#clock, this.#started),
|
|
24888
|
-
offerInPush: this.#
|
|
24903
|
+
offerInPush: this.#pushBytes > 0n
|
|
24889
24904
|
})
|
|
24890
24905
|
}));
|
|
24891
24906
|
}
|
|
@@ -24895,7 +24910,7 @@ var WorkspaceSyncWork = class {
|
|
|
24895
24910
|
return step;
|
|
24896
24911
|
}
|
|
24897
24912
|
#finish(value) {
|
|
24898
|
-
this.#
|
|
24913
|
+
this.#state = { phase: WorkspaceSyncPhase.Done };
|
|
24899
24914
|
this.#current = void 0;
|
|
24900
24915
|
return this.#remember({ case: "synced", value });
|
|
24901
24916
|
}
|
|
@@ -27033,13 +27048,21 @@ var CorrespondenceDesk = class {
|
|
|
27033
27048
|
}
|
|
27034
27049
|
#observeSyncResult(requestId, workId2, result) {
|
|
27035
27050
|
const timings = result.timings;
|
|
27051
|
+
const receipt = result.offerReceipt?.outcome;
|
|
27052
|
+
const admission = receipt?.case === "admission" ? receipt.value : void 0;
|
|
27036
27053
|
this.#events.instant("peer.follow.sync-result", {
|
|
27037
27054
|
request: create(RuntimeRequestSchema, { protocolVersion: 2, requestId }),
|
|
27038
27055
|
workspace: result.workspace
|
|
27039
27056
|
}, {
|
|
27040
27057
|
"sync.workId": workId2,
|
|
27041
27058
|
...result.currentHead ? { "sync.head": result.currentHead } : {},
|
|
27059
|
+
...result.previousHead ? { "sync.previousHead": result.previousHead } : {},
|
|
27060
|
+
...admission?.main ? { "sync.providerHead": admission.main } : {},
|
|
27061
|
+
...admission?.sealedMain ? { "sync.sealedHead": admission.sealedMain } : {},
|
|
27042
27062
|
"sync.stillAhead": result.stillAhead,
|
|
27063
|
+
"sync.reconnectRequired": result.reconnectRequired,
|
|
27064
|
+
"sync.refused": result.refused,
|
|
27065
|
+
"sync.deferred": result.deferred,
|
|
27043
27066
|
"sync.directOfferDeferred": result.directOfferDeferred,
|
|
27044
27067
|
"sync.offline": result.offline,
|
|
27045
27068
|
...timings ? {
|
|
@@ -543,6 +543,8 @@ final class NmsSyncStatusProjection {
|
|
|
543
543
|
for (final stream in streams) {
|
|
544
544
|
if (!stream.isClosed) unawaited(stream.close());
|
|
545
545
|
}
|
|
546
|
-
|
|
546
|
+
// A paused UI subscriber may not process the done event until it resumes.
|
|
547
|
+
// Closing this broadcast stream must not hold reception teardown open.
|
|
548
|
+
if (!_application.isClosed) unawaited(_application.close());
|
|
547
549
|
}
|
|
548
550
|
}
|
package/dart/pubspec.yaml
CHANGED
|
@@ -7,7 +7,7 @@ repository: https://github.com/Captain-App/nomos2
|
|
|
7
7
|
description: >-
|
|
8
8
|
Nomos 2 Dart client — generated domain values, intent payloads and application
|
|
9
9
|
lifecycle types over the typed Nomos kernel protocol and USDA custody model.
|
|
10
|
-
version: 0.66.
|
|
10
|
+
version: 0.66.7
|
|
11
11
|
|
|
12
12
|
environment:
|
|
13
13
|
sdk: ^3.5.0
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import 'package:nomos_client/src/reception/event_desk.dart';
|
|
2
|
+
import 'package:test/test.dart';
|
|
3
|
+
|
|
4
|
+
void main() {
|
|
5
|
+
test('reception closes while presentation observers are paused', () async {
|
|
6
|
+
final desk = ReceptionEventDesk();
|
|
7
|
+
final copies = desk.copyChanges.listen((_) {})..pause();
|
|
8
|
+
final application = desk.syncStatuses.applicationStream.listen((_) {})
|
|
9
|
+
..pause();
|
|
10
|
+
|
|
11
|
+
await desk.close().timeout(const Duration(seconds: 1));
|
|
12
|
+
|
|
13
|
+
await copies.cancel();
|
|
14
|
+
await application.cancel();
|
|
15
|
+
});
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@githolon/dsl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.111.0",
|
|
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.",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"test": "vitest run"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@githolon/runtime": "^0.
|
|
75
|
+
"@githolon/runtime": "^0.111.0",
|
|
76
76
|
"@bufbuild/protobuf": "^2.13.0",
|
|
77
77
|
"@bufbuild/protoc-gen-es": "^2.13.0",
|
|
78
78
|
"esbuild": "0.24.2",
|
|
@@ -1237,7 +1237,7 @@ export function emitDartCallProtoEncoder(
|
|
|
1237
1237
|
for (const field of fields) {
|
|
1238
1238
|
const fieldAccess = access.get(field.name);
|
|
1239
1239
|
if (fieldAccess?.omit === true || fieldAccess?.present === "false") continue;
|
|
1240
|
-
const raw = fieldAccess?.slot === true ? `${fieldAccess.value}.
|
|
1240
|
+
const raw = fieldAccess?.slot === true ? `${fieldAccess.value}.valueOrNull` : fieldAccess?.value ?? `payload.${dartMemberName(field.name)}`;
|
|
1241
1241
|
const fieldSemantic = typeNode(spec.nodes, field.value.id).expression.semantic;
|
|
1242
1242
|
const fieldValueIsCanonical = fieldSemantic?.kind === "id-reference" ||
|
|
1243
1243
|
fieldSemantic?.kind === "address-reference" ||
|
|
@@ -1252,7 +1252,9 @@ export function emitDartCallProtoEncoder(
|
|
|
1252
1252
|
emitValue(body, raw, field.value.id, field.name, (expression) => {
|
|
1253
1253
|
body.push(`${member} = ${expression};`);
|
|
1254
1254
|
}, fieldValueIsCanonical);
|
|
1255
|
-
if (
|
|
1255
|
+
if (fieldAccess?.slot === true) {
|
|
1256
|
+
lines.push(` if (!${fieldAccess.value}.isOmitted) {`, ...body.map((line) => ` ${line}`), ` }`);
|
|
1257
|
+
} else if (field.required) lines.push(...body.map((line) => ` ${line}`));
|
|
1256
1258
|
else lines.push(` if (${fieldAccess?.present ?? `${raw} != null`}) {`, ...body.map((line) => ` ${line}`), ` }`);
|
|
1257
1259
|
}
|
|
1258
1260
|
lines.push(
|
package/src/codegen_dart.ts
CHANGED
|
@@ -5479,6 +5479,10 @@ export function generateDartDomain(mod: DomainModule, opts: GenerateDartDomainOp
|
|
|
5479
5479
|
* Every tuple and subject is nominally typed from the same DSL graph; there is no JSON leaf or map-shaped
|
|
5480
5480
|
* boundary. INFORMATIONAL only (the one wasm gate is still authoritative). */
|
|
5481
5481
|
export function dartRoleHelpers(mod: DomainModule): string {
|
|
5482
|
+
// Some packages have role policy declarations but do not project the framework
|
|
5483
|
+
// RelationTuple aggregate into this domain. In that case there is no typed
|
|
5484
|
+
// read model for a client-side hint to consume.
|
|
5485
|
+
if (!mod.aggregates.some((aggregate) => aggregate.id === "RelationTuple")) return "";
|
|
5482
5486
|
const editRels = new Map<string, Set<string>>();
|
|
5483
5487
|
const viewRels = new Map<string, Set<string>>();
|
|
5484
5488
|
for (const r of mod.roles ?? []) {
|
|
@@ -2544,9 +2544,10 @@ const NOMOS_TYPES_DEP = "^0.11.2";
|
|
|
2544
2544
|
// 0.48.x: session-bound clients (XClient.bind/NomosClientInterface/DomainClientSessionMismatch/
|
|
2545
2545
|
// DomainNotInstalledForSession, engine.follow, pendingConflicts lanes) — the generated client
|
|
2546
2546
|
// REQUIRES this surface. 0.48.1 fixes NomosSession.watchPendingConflicts delegation.
|
|
2547
|
-
const NOMOS_CLIENT_DEP = "^0.66.
|
|
2548
|
-
|
|
2549
|
-
//
|
|
2547
|
+
const NOMOS_CLIENT_DEP = "^0.66.7";
|
|
2548
|
+
const NOMOS_FLUTTER_DEP = "^0.65.9";
|
|
2549
|
+
// DRIFT TRIPWIRE: pubspec_pins.test.ts asserts these equal the CURRENT versions in the Dart and Flutter
|
|
2550
|
+
// package pubspecs — a release without this bump fails the suite, so tenants never
|
|
2550
2551
|
// land back in dependency_overrides (the exact co2 report this closes, third occurrence).
|
|
2551
2552
|
|
|
2552
2553
|
const isPubPackageName = (s: string): boolean => /^[a-z][a-z0-9_]*$/.test(s);
|
|
@@ -3166,10 +3167,13 @@ function reconcilePubspec(
|
|
|
3166
3167
|
index < dependenciesEnd &&
|
|
3167
3168
|
line.startsWith(prefix)
|
|
3168
3169
|
);
|
|
3169
|
-
const replacement =
|
|
3170
|
+
const replacement =
|
|
3171
|
+
name === "flutter" && constraint === "sdk:flutter"
|
|
3172
|
+
? [" flutter:", " sdk: flutter"]
|
|
3173
|
+
: [` ${name}: ${constraint}`];
|
|
3170
3174
|
if (existing < 0) {
|
|
3171
|
-
lines.splice(dependenciesEnd, 0, replacement);
|
|
3172
|
-
dependenciesEnd +=
|
|
3175
|
+
lines.splice(dependenciesEnd, 0, ...replacement);
|
|
3176
|
+
dependenciesEnd += replacement.length;
|
|
3173
3177
|
continue;
|
|
3174
3178
|
}
|
|
3175
3179
|
let nestedEnd = existing + 1;
|
|
@@ -3178,8 +3182,8 @@ function reconcilePubspec(
|
|
|
3178
3182
|
/^(?: {4,}|\t)/.test(lines[nestedEnd]!)
|
|
3179
3183
|
)
|
|
3180
3184
|
nestedEnd += 1;
|
|
3181
|
-
lines.splice(existing, nestedEnd - existing, replacement);
|
|
3182
|
-
dependenciesEnd
|
|
3185
|
+
lines.splice(existing, nestedEnd - existing, ...replacement);
|
|
3186
|
+
dependenciesEnd += replacement.length - (nestedEnd - existing);
|
|
3183
3187
|
}
|
|
3184
3188
|
|
|
3185
3189
|
const next = `${lines.join("\n")}${trailingNewline ? "\n" : ""}`;
|
|
@@ -4045,6 +4049,7 @@ function emitDartPackages(
|
|
|
4045
4049
|
` flutter:`,
|
|
4046
4050
|
` sdk: flutter`,
|
|
4047
4051
|
` nomos_client: ${NOMOS_CLIENT_DEP}`,
|
|
4052
|
+
` nomos_flutter: ${NOMOS_FLUTTER_DEP}`,
|
|
4048
4053
|
` ${packages.client}:`,
|
|
4049
4054
|
` path: ../${packages.client} # co-emitted sibling; replace with a hosted constraint when publishing`,
|
|
4050
4055
|
`dev_dependencies:`,
|
|
@@ -4054,7 +4059,11 @@ function emitDartPackages(
|
|
|
4054
4059
|
` - lib/${genRel}/${embeddedLawName}`,
|
|
4055
4060
|
``,
|
|
4056
4061
|
].join("\n"),
|
|
4057
|
-
{
|
|
4062
|
+
{
|
|
4063
|
+
flutter: "sdk:flutter",
|
|
4064
|
+
nomos_client: NOMOS_CLIENT_DEP,
|
|
4065
|
+
nomos_flutter: NOMOS_FLUTTER_DEP,
|
|
4066
|
+
}
|
|
4058
4067
|
);
|
|
4059
4068
|
reconcileGeneratedLawAsset(
|
|
4060
4069
|
pkgDir(packages.test),
|
|
@@ -8918,12 +8927,14 @@ async function main(): Promise<void> {
|
|
|
8918
8927
|
const client = path.resolve(selfDir, "..", "dart");
|
|
8919
8928
|
const types = path.resolve(selfDir, "..", "dart_types");
|
|
8920
8929
|
const peerHost = path.resolve(selfDir, "..", "..", "nomos_peer_host");
|
|
8930
|
+
const flutter = path.resolve(selfDir, "..", "..", "nomos_flutter");
|
|
8921
8931
|
if (
|
|
8922
8932
|
existsSync(path.join(client, "pubspec.yaml")) &&
|
|
8923
8933
|
existsSync(path.join(types, "pubspec.yaml")) &&
|
|
8924
|
-
existsSync(path.join(peerHost, "pubspec.yaml"))
|
|
8934
|
+
existsSync(path.join(peerHost, "pubspec.yaml")) &&
|
|
8935
|
+
existsSync(path.join(flutter, "pubspec.yaml"))
|
|
8925
8936
|
) {
|
|
8926
|
-
return { client, types, peerHost };
|
|
8937
|
+
return { client, types, peerHost, flutter };
|
|
8927
8938
|
}
|
|
8928
8939
|
return undefined;
|
|
8929
8940
|
})();
|
|
@@ -8961,6 +8972,7 @@ async function main(): Promise<void> {
|
|
|
8961
8972
|
const lines = ["dependency_overrides:"];
|
|
8962
8973
|
const usesClient = /(^|\n)\s+nomos_client\s*:/.test(spec);
|
|
8963
8974
|
const usesTypes = /(^|\n)\s+nomos_types\s*:/.test(spec);
|
|
8975
|
+
const usesFlutter = /(^|\n)\s+nomos_flutter\s*:/.test(spec);
|
|
8964
8976
|
if (usesClient)
|
|
8965
8977
|
lines.push(` nomos_client:`, ` path: ${localDart.client}`);
|
|
8966
8978
|
// A generated CLIENT or TEST package may only mention nomos_client directly, but that local
|
|
@@ -8978,6 +8990,8 @@ async function main(): Promise<void> {
|
|
|
8978
8990
|
` nomos_peer_host:`,
|
|
8979
8991
|
` path: ${localDart.peerHost}`
|
|
8980
8992
|
);
|
|
8993
|
+
if (usesFlutter)
|
|
8994
|
+
lines.push(` nomos_flutter:`, ` path: ${localDart.flutter}`);
|
|
8981
8995
|
if (lines.length > 1) {
|
|
8982
8996
|
writeTextIfChanged(overrideFile, lines.join("\n") + "\n");
|
|
8983
8997
|
}
|
|
@@ -9039,7 +9053,10 @@ async function main(): Promise<void> {
|
|
|
9039
9053
|
for (const pkg of pkgDirs) {
|
|
9040
9054
|
if (dartPackageResolutionCurrent(pkg)) continue;
|
|
9041
9055
|
try {
|
|
9042
|
-
|
|
9056
|
+
const isFlutterPackage = /^\s{2}flutter:\s*\n\s{4}sdk:\s*flutter\s*$/m.test(
|
|
9057
|
+
readFileSync(path.join(pkg, "pubspec.yaml"), "utf8")
|
|
9058
|
+
);
|
|
9059
|
+
execFileSync(isFlutterPackage ? "flutter" : "dart", ["pub", "get"], {
|
|
9043
9060
|
cwd: pkg,
|
|
9044
9061
|
stdio: "pipe",
|
|
9045
9062
|
encoding: "utf8",
|