@agentplat/mesh 0.3.0-alpha.1 → 0.3.0-alpha.3
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/README.md +247 -2
- package/dist/coordination-allocation-contracts.d.ts +598 -0
- package/dist/coordination-allocation-contracts.d.ts.map +1 -0
- package/dist/coordination-allocation-contracts.js +2 -0
- package/dist/coordination-allocation-contracts.js.map +1 -0
- package/dist/coordination-allocation-state.d.ts +19 -0
- package/dist/coordination-allocation-state.d.ts.map +1 -0
- package/dist/coordination-allocation-state.js +5496 -0
- package/dist/coordination-allocation-state.js.map +1 -0
- package/dist/coordination-allocation.d.ts +10 -0
- package/dist/coordination-allocation.d.ts.map +1 -0
- package/dist/coordination-allocation.js +2743 -0
- package/dist/coordination-allocation.js.map +1 -0
- package/dist/coordination-contracts.d.ts +78 -0
- package/dist/coordination-contracts.d.ts.map +1 -0
- package/dist/coordination-contracts.js +2 -0
- package/dist/coordination-contracts.js.map +1 -0
- package/dist/coordination-discovery-contracts.d.ts +150 -0
- package/dist/coordination-discovery-contracts.d.ts.map +1 -0
- package/dist/coordination-discovery-contracts.js +2 -0
- package/dist/coordination-discovery-contracts.js.map +1 -0
- package/dist/coordination-discovery-state.d.ts +12 -0
- package/dist/coordination-discovery-state.d.ts.map +1 -0
- package/dist/coordination-discovery-state.js +542 -0
- package/dist/coordination-discovery-state.js.map +1 -0
- package/dist/coordination-discovery.d.ts +19 -0
- package/dist/coordination-discovery.d.ts.map +1 -0
- package/dist/coordination-discovery.js +850 -0
- package/dist/coordination-discovery.js.map +1 -0
- package/dist/coordination-execution.d.ts +6 -0
- package/dist/coordination-execution.d.ts.map +1 -0
- package/dist/coordination-execution.js +854 -0
- package/dist/coordination-execution.js.map +1 -0
- package/dist/coordination-inbound-contracts.d.ts +150 -0
- package/dist/coordination-inbound-contracts.d.ts.map +1 -0
- package/dist/coordination-inbound-contracts.js +2 -0
- package/dist/coordination-inbound-contracts.js.map +1 -0
- package/dist/coordination-inbound-state.d.ts +26 -0
- package/dist/coordination-inbound-state.d.ts.map +1 -0
- package/dist/coordination-inbound-state.js +331 -0
- package/dist/coordination-inbound-state.js.map +1 -0
- package/dist/coordination-inbound.d.ts +16 -0
- package/dist/coordination-inbound.d.ts.map +1 -0
- package/dist/coordination-inbound.js +944 -0
- package/dist/coordination-inbound.js.map +1 -0
- package/dist/coordination-lease-renewal.d.ts +8 -0
- package/dist/coordination-lease-renewal.d.ts.map +1 -0
- package/dist/coordination-lease-renewal.js +518 -0
- package/dist/coordination-lease-renewal.js.map +1 -0
- package/dist/coordination-objective-topic-contracts.d.ts +70 -0
- package/dist/coordination-objective-topic-contracts.d.ts.map +1 -0
- package/dist/coordination-objective-topic-contracts.js +2 -0
- package/dist/coordination-objective-topic-contracts.js.map +1 -0
- package/dist/coordination-objective-topic.d.ts +6 -0
- package/dist/coordination-objective-topic.d.ts.map +1 -0
- package/dist/coordination-objective-topic.js +561 -0
- package/dist/coordination-objective-topic.js.map +1 -0
- package/dist/coordination-objective-work-contracts.d.ts +212 -0
- package/dist/coordination-objective-work-contracts.d.ts.map +1 -0
- package/dist/coordination-objective-work-contracts.js +2 -0
- package/dist/coordination-objective-work-contracts.js.map +1 -0
- package/dist/coordination-objective-work-state.d.ts +10 -0
- package/dist/coordination-objective-work-state.d.ts.map +1 -0
- package/dist/coordination-objective-work-state.js +1075 -0
- package/dist/coordination-objective-work-state.js.map +1 -0
- package/dist/coordination-objective-work-time.d.ts +9 -0
- package/dist/coordination-objective-work-time.d.ts.map +1 -0
- package/dist/coordination-objective-work-time.js +48 -0
- package/dist/coordination-objective-work-time.js.map +1 -0
- package/dist/coordination-objective-work.d.ts +11 -0
- package/dist/coordination-objective-work.d.ts.map +1 -0
- package/dist/coordination-objective-work.js +1090 -0
- package/dist/coordination-objective-work.js.map +1 -0
- package/dist/coordination-recovery.d.ts +13 -0
- package/dist/coordination-recovery.d.ts.map +1 -0
- package/dist/coordination-recovery.js +951 -0
- package/dist/coordination-recovery.js.map +1 -0
- package/dist/coordination-reducer.d.ts +8 -0
- package/dist/coordination-reducer.d.ts.map +1 -0
- package/dist/coordination-reducer.js +81 -0
- package/dist/coordination-reducer.js.map +1 -0
- package/dist/coordination-state.d.ts +13 -0
- package/dist/coordination-state.d.ts.map +1 -0
- package/dist/coordination-state.js +335 -0
- package/dist/coordination-state.js.map +1 -0
- package/dist/coordination-topic-contracts.d.ts +82 -0
- package/dist/coordination-topic-contracts.d.ts.map +1 -0
- package/dist/coordination-topic-contracts.js +2 -0
- package/dist/coordination-topic-contracts.js.map +1 -0
- package/dist/coordination-topic.d.ts +6 -0
- package/dist/coordination-topic.d.ts.map +1 -0
- package/dist/coordination-topic.js +561 -0
- package/dist/coordination-topic.js.map +1 -0
- package/dist/coordination.d.ts +21 -0
- package/dist/coordination.d.ts.map +1 -0
- package/dist/coordination.js +21 -0
- package/dist/coordination.js.map +1 -0
- package/dist/sha256.d.ts +3 -0
- package/dist/sha256.d.ts.map +1 -0
- package/dist/sha256.js +117 -0
- package/dist/sha256.js.map +1 -0
- package/package.json +7 -3
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { SignedMeshEnvelope } from '@agentplat/mesh-protocol';
|
|
2
|
+
import type { MeshCoordinationTopicAddress, MeshCoordinationTopicLimits, MeshCoordinationTopicTime } from './coordination-topic-contracts.js';
|
|
3
|
+
import type { MeshObjectiveInboundProcessor, MeshObjectiveInboundRuntimeState } from './coordination-inbound-contracts.js';
|
|
4
|
+
import type { MeshObjectivePayload } from './coordination-objective-work-contracts.js';
|
|
5
|
+
/** An Objective topic route is always an exact process-local peer instance. */
|
|
6
|
+
export type MeshCoordinationObjectiveTopicAddress = MeshCoordinationTopicAddress;
|
|
7
|
+
/** One trusted receiver-time sample supplied outside the Objective topic driver. */
|
|
8
|
+
export type MeshCoordinationObjectiveTopicTime = MeshCoordinationTopicTime;
|
|
9
|
+
/** Hard ceilings retained by one independently scoped Objective driver. */
|
|
10
|
+
export interface MeshCoordinationObjectiveTopicLimits extends MeshCoordinationTopicLimits {
|
|
11
|
+
}
|
|
12
|
+
/** Address-aware trusted clock used for Objective selection and delivery. */
|
|
13
|
+
export interface MeshCoordinationObjectiveTopicClock {
|
|
14
|
+
now(address: MeshCoordinationObjectiveTopicAddress): MeshCoordinationObjectiveTopicTime;
|
|
15
|
+
}
|
|
16
|
+
/** Frozen construction snapshot safe to expose for local inspection. */
|
|
17
|
+
export interface MeshCoordinationObjectiveTopicConfiguration extends MeshCoordinationObjectiveTopicLimits {
|
|
18
|
+
readonly tenantId: string;
|
|
19
|
+
readonly meshId: string;
|
|
20
|
+
}
|
|
21
|
+
/** Redacted local-only delivery detail; callback failures are ignored. */
|
|
22
|
+
export interface MeshCoordinationObjectiveTopicDiagnostic {
|
|
23
|
+
readonly status: 'rejected' | 'unavailable';
|
|
24
|
+
readonly code: string;
|
|
25
|
+
readonly messageId: string;
|
|
26
|
+
readonly target: MeshCoordinationObjectiveTopicAddress;
|
|
27
|
+
}
|
|
28
|
+
/** Construction options for one explicit, process-local Objective topic driver. */
|
|
29
|
+
export interface MeshCoordinationObjectiveTopicDriverOptions {
|
|
30
|
+
readonly tenantId: string;
|
|
31
|
+
readonly meshId: string;
|
|
32
|
+
readonly clock: MeshCoordinationObjectiveTopicClock;
|
|
33
|
+
readonly maximumEndpoints?: number;
|
|
34
|
+
readonly maximumQueueDepth?: number;
|
|
35
|
+
readonly maximumQueuedBytes?: number;
|
|
36
|
+
readonly maximumDeliveriesPerPublish?: number;
|
|
37
|
+
readonly maximumInternalStepsPerDrain?: number;
|
|
38
|
+
readonly onDiagnostic?: (diagnostic: MeshCoordinationObjectiveTopicDiagnostic) => void;
|
|
39
|
+
}
|
|
40
|
+
/** State and authenticated Objective processor installed at one exact endpoint. */
|
|
41
|
+
export interface MeshCoordinationObjectiveTopicRegistration {
|
|
42
|
+
readonly state: MeshObjectiveInboundRuntimeState;
|
|
43
|
+
readonly processor: MeshObjectiveInboundProcessor;
|
|
44
|
+
}
|
|
45
|
+
/** One already-signed Objective publication and optional bounded sender fanout. */
|
|
46
|
+
export interface MeshCoordinationObjectiveTopicPublishInput {
|
|
47
|
+
readonly envelope: SignedMeshEnvelope<MeshObjectivePayload>;
|
|
48
|
+
readonly fanout?: number;
|
|
49
|
+
}
|
|
50
|
+
/** Public delivery outcome, deliberately excluding exact rejection codes. */
|
|
51
|
+
export interface MeshCoordinationObjectiveTopicReceipt {
|
|
52
|
+
readonly status: 'accepted' | 'rejected' | 'unavailable';
|
|
53
|
+
readonly messageId: string;
|
|
54
|
+
readonly target: MeshCoordinationObjectiveTopicAddress;
|
|
55
|
+
}
|
|
56
|
+
/** Mutable driver-owned endpoint around immutable inbound snapshots. */
|
|
57
|
+
export interface MeshCoordinationObjectiveTopicPeer {
|
|
58
|
+
readonly address: MeshCoordinationObjectiveTopicAddress;
|
|
59
|
+
getState(): MeshObjectiveInboundRuntimeState;
|
|
60
|
+
publish(input: MeshCoordinationObjectiveTopicPublishInput): Promise<readonly MeshCoordinationObjectiveTopicReceipt[]>;
|
|
61
|
+
unregister(): void;
|
|
62
|
+
}
|
|
63
|
+
/** Bounded sender-view Objective delivery with no process-global routing. */
|
|
64
|
+
export interface MeshCoordinationObjectiveTopicDriver {
|
|
65
|
+
readonly configuration: MeshCoordinationObjectiveTopicConfiguration;
|
|
66
|
+
register(registration: MeshCoordinationObjectiveTopicRegistration): MeshCoordinationObjectiveTopicPeer;
|
|
67
|
+
idle(): Promise<void>;
|
|
68
|
+
close(): Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=coordination-objective-topic-contracts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-objective-topic-contracts.d.ts","sourceRoot":"","sources":["../src/coordination-objective-topic-contracts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-objective-topic-contracts.js","sourceRoot":"","sources":["../src/coordination-objective-topic-contracts.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MeshCoordinationObjectiveTopicDriver, MeshCoordinationObjectiveTopicDriverOptions, MeshCoordinationObjectiveTopicLimits } from './coordination-objective-topic-contracts.js';
|
|
2
|
+
/** Conservative defaults that may only be reduced per driver instance. */
|
|
3
|
+
export declare const DEFAULT_MESH_COORDINATION_OBJECTIVE_TOPIC_LIMITS: Readonly<MeshCoordinationObjectiveTopicLimits>;
|
|
4
|
+
/** Creates one isolated Objective topic driver with construction-bound trust inputs. */
|
|
5
|
+
export declare function createMeshCoordinationObjectiveTopicDriver(options: MeshCoordinationObjectiveTopicDriverOptions): MeshCoordinationObjectiveTopicDriver;
|
|
6
|
+
//# sourceMappingURL=coordination-objective-topic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-objective-topic.d.ts","sourceRoot":"","sources":["../src/coordination-objective-topic.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAIV,oCAAoC,EACpC,2CAA2C,EAC3C,oCAAoC,EAMrC,MAAM,6CAA6C,CAAC;AAMrD,0EAA0E;AAC1E,eAAO,MAAM,gDAAgD,EAAE,QAAQ,CAAC,oCAAoC,CAOxG,CAAC;AAoBL,wFAAwF;AACxF,wBAAgB,0CAA0C,CACxD,OAAO,EAAE,2CAA2C,GACnD,oCAAoC,CAQtC"}
|
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
import { DEFAULT_MESH_PROTOCOL_LIMITS, canonicalizeMeshJsonBytes, compareMeshTimestamps, validateSignedMeshEnvelope, } from '@agentplat/mesh-protocol';
|
|
2
|
+
import { isTrustedMeshObjectiveInboundDecision } from './coordination-inbound.js';
|
|
3
|
+
import { createMeshObjectiveInboundRuntimeState } from './coordination-inbound-state.js';
|
|
4
|
+
import { assertMeshLogicalTime } from './state.js';
|
|
5
|
+
const identifierPattern = /^[A-Za-z0-9][A-Za-z0-9._:@-]*$/;
|
|
6
|
+
const utf8Encoder = new TextEncoder();
|
|
7
|
+
/** Conservative defaults that may only be reduced per driver instance. */
|
|
8
|
+
export const DEFAULT_MESH_COORDINATION_OBJECTIVE_TOPIC_LIMITS = Object.freeze({
|
|
9
|
+
maximumEndpoints: 256,
|
|
10
|
+
maximumQueueDepth: 1_024,
|
|
11
|
+
maximumQueuedBytes: 16 * 1_024 * 1_024,
|
|
12
|
+
maximumDeliveriesPerPublish: 32,
|
|
13
|
+
maximumInternalStepsPerDrain: 1_024,
|
|
14
|
+
});
|
|
15
|
+
/** Creates one isolated Objective topic driver with construction-bound trust inputs. */
|
|
16
|
+
export function createMeshCoordinationObjectiveTopicDriver(options) {
|
|
17
|
+
const driver = new InMemoryMeshCoordinationObjectiveTopicDriver(options);
|
|
18
|
+
return Object.freeze({
|
|
19
|
+
configuration: driver.configuration,
|
|
20
|
+
register: driver.register.bind(driver),
|
|
21
|
+
idle: driver.idle.bind(driver),
|
|
22
|
+
close: driver.close.bind(driver),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
class InMemoryMeshCoordinationObjectiveTopicDriver {
|
|
26
|
+
configuration;
|
|
27
|
+
#now;
|
|
28
|
+
#onDiagnostic;
|
|
29
|
+
#endpoints = new Map();
|
|
30
|
+
#queue = [];
|
|
31
|
+
#insertionSequence = 0;
|
|
32
|
+
#queuedBytes = 0;
|
|
33
|
+
#inFlight = 0;
|
|
34
|
+
#inFlightBytes = 0;
|
|
35
|
+
#draining = false;
|
|
36
|
+
#closed = false;
|
|
37
|
+
#idlePromise;
|
|
38
|
+
#resolveIdle;
|
|
39
|
+
#closePromise;
|
|
40
|
+
#resolveClose;
|
|
41
|
+
constructor(options) {
|
|
42
|
+
assertOptions(options);
|
|
43
|
+
const limits = resolveLimits(options);
|
|
44
|
+
this.configuration = Object.freeze({
|
|
45
|
+
tenantId: options.tenantId,
|
|
46
|
+
meshId: options.meshId,
|
|
47
|
+
...limits,
|
|
48
|
+
});
|
|
49
|
+
this.#now = options.clock.now.bind(options.clock);
|
|
50
|
+
this.#onDiagnostic =
|
|
51
|
+
options.onDiagnostic === undefined
|
|
52
|
+
? undefined
|
|
53
|
+
: options.onDiagnostic.bind(undefined);
|
|
54
|
+
}
|
|
55
|
+
register(registration) {
|
|
56
|
+
if (this.#closed) {
|
|
57
|
+
throw new Error('Mesh coordination Objective topic driver is closed');
|
|
58
|
+
}
|
|
59
|
+
assertRegistration(registration);
|
|
60
|
+
if (this.#endpoints.size >= this.configuration.maximumEndpoints) {
|
|
61
|
+
throw new RangeError('Mesh coordination Objective topic endpoint capacity exceeded');
|
|
62
|
+
}
|
|
63
|
+
assertRuntimeState(registration.state);
|
|
64
|
+
const identity = registration.state.discovery.identity;
|
|
65
|
+
if (identity.tenantId !== this.configuration.tenantId ||
|
|
66
|
+
identity.meshId !== this.configuration.meshId) {
|
|
67
|
+
throw new TypeError('Mesh coordination Objective topic endpoint scope mismatch');
|
|
68
|
+
}
|
|
69
|
+
const address = freezeAddress(identity);
|
|
70
|
+
const key = addressKey(address);
|
|
71
|
+
if (this.#endpoints.has(key)) {
|
|
72
|
+
throw new TypeError('Duplicate Mesh coordination Objective topic endpoint');
|
|
73
|
+
}
|
|
74
|
+
const endpoint = new RegisteredObjectiveTopicEndpoint(this, address, registration.state, registration.processor.process.bind(registration.processor));
|
|
75
|
+
this.#endpoints.set(key, endpoint);
|
|
76
|
+
return endpoint.handle;
|
|
77
|
+
}
|
|
78
|
+
idle() {
|
|
79
|
+
if (this.#isQuiescent())
|
|
80
|
+
return Promise.resolve();
|
|
81
|
+
if (!this.#idlePromise) {
|
|
82
|
+
this.#idlePromise = new Promise((resolve) => {
|
|
83
|
+
this.#resolveIdle = resolve;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return this.#idlePromise;
|
|
87
|
+
}
|
|
88
|
+
close() {
|
|
89
|
+
if (this.#closePromise)
|
|
90
|
+
return this.#closePromise;
|
|
91
|
+
this.#closed = true;
|
|
92
|
+
this.#closePromise = new Promise((resolve) => {
|
|
93
|
+
this.#resolveClose = resolve;
|
|
94
|
+
}).then(() => {
|
|
95
|
+
this.#endpoints.clear();
|
|
96
|
+
});
|
|
97
|
+
this.#resolveQuiescence();
|
|
98
|
+
return this.#closePromise;
|
|
99
|
+
}
|
|
100
|
+
unregister(endpoint) {
|
|
101
|
+
const key = addressKey(endpoint.address);
|
|
102
|
+
if (this.#endpoints.get(key) === endpoint)
|
|
103
|
+
this.#endpoints.delete(key);
|
|
104
|
+
}
|
|
105
|
+
publishFrom(sender, input) {
|
|
106
|
+
if (this.#closed) {
|
|
107
|
+
throw new Error('Mesh coordination Objective topic driver is closed');
|
|
108
|
+
}
|
|
109
|
+
if (this.#endpoints.get(addressKey(sender.address)) !== sender) {
|
|
110
|
+
throw new Error('Mesh coordination Objective topic endpoint is unregistered');
|
|
111
|
+
}
|
|
112
|
+
assertPublishInput(input);
|
|
113
|
+
const structural = validateSignedMeshEnvelope(input.envelope);
|
|
114
|
+
if (!structural.ok) {
|
|
115
|
+
throw new TypeError('Invalid signed Mesh Objective envelope');
|
|
116
|
+
}
|
|
117
|
+
const envelope = structural.value;
|
|
118
|
+
assertPublicationIdentity(sender, envelope, this.configuration);
|
|
119
|
+
assertObjectiveTopic(envelope);
|
|
120
|
+
const time = this.#sampleTime(sender.address);
|
|
121
|
+
const state = sender.getState();
|
|
122
|
+
const lastLogicalTime = Math.max(state.coordination.lastLogicalTime, state.discovery.lastLogicalTime, state.objectives.lastLogicalTime, state.inbound.lastLogicalTime);
|
|
123
|
+
if (time.receivedAt < lastLogicalTime) {
|
|
124
|
+
throw new RangeError('Mesh coordination Objective topic logical time cannot move backwards');
|
|
125
|
+
}
|
|
126
|
+
const maximumFanout = Math.min(this.configuration.maximumDeliveriesPerPublish, state.discovery.limits.maximumFanout);
|
|
127
|
+
const fanout = input.fanout ?? maximumFanout;
|
|
128
|
+
if (!Number.isSafeInteger(fanout) || fanout < 1 || fanout > maximumFanout) {
|
|
129
|
+
throw new RangeError('Invalid Mesh coordination Objective topic fanout');
|
|
130
|
+
}
|
|
131
|
+
// Selection begins solely with the sender's own active view. The process
|
|
132
|
+
// registry only pins an already-selected exact instance; it never expands
|
|
133
|
+
// the reachable recipient set.
|
|
134
|
+
const targets = selectTopicTargets(state, sender.address, time, fanout);
|
|
135
|
+
if (targets.length === 0)
|
|
136
|
+
return Promise.resolve(Object.freeze([]));
|
|
137
|
+
const canonical = canonicalizeMeshJsonBytes(envelope);
|
|
138
|
+
if (!canonical.ok) {
|
|
139
|
+
throw new TypeError('Invalid canonical Mesh Objective envelope');
|
|
140
|
+
}
|
|
141
|
+
const batchBytes = checkedProduct(canonical.value.byteLength, targets.length);
|
|
142
|
+
if (batchBytes === undefined ||
|
|
143
|
+
exceedsSum(this.#insertionSequence, targets.length, Number.MAX_SAFE_INTEGER) ||
|
|
144
|
+
exceedsSum(this.#queue.length + this.#inFlight, targets.length, this.configuration.maximumQueueDepth) ||
|
|
145
|
+
exceedsSum(this.#queuedBytes + this.#inFlightBytes, batchBytes, this.configuration.maximumQueuedBytes)) {
|
|
146
|
+
return Promise.resolve(this.#rejectBatch(envelope.messageId, targets, 'queue_capacity_exceeded'));
|
|
147
|
+
}
|
|
148
|
+
const promises = targets.map((target) => new Promise((resolve) => {
|
|
149
|
+
this.#insertionSequence += 1;
|
|
150
|
+
this.#queue.push({
|
|
151
|
+
insertionSequence: this.#insertionSequence,
|
|
152
|
+
envelope,
|
|
153
|
+
canonicalEnvelope: canonical.value,
|
|
154
|
+
target,
|
|
155
|
+
selectedEndpoint: this.#endpoints.get(addressKey(target)),
|
|
156
|
+
byteLength: canonical.value.byteLength,
|
|
157
|
+
resolve,
|
|
158
|
+
});
|
|
159
|
+
}));
|
|
160
|
+
this.#queuedBytes += batchBytes;
|
|
161
|
+
this.#scheduleDrain();
|
|
162
|
+
return Promise.all(promises).then((receipts) => Object.freeze(receipts));
|
|
163
|
+
}
|
|
164
|
+
#rejectBatch(messageId, targets, code) {
|
|
165
|
+
return Object.freeze(targets.map((target) => {
|
|
166
|
+
this.#diagnose('rejected', code, messageId, target);
|
|
167
|
+
return freezeReceipt('rejected', messageId, target);
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
#sampleTime(address) {
|
|
171
|
+
return freezeTime(this.#now(address));
|
|
172
|
+
}
|
|
173
|
+
#scheduleDrain() {
|
|
174
|
+
if (this.#draining)
|
|
175
|
+
return;
|
|
176
|
+
this.#draining = true;
|
|
177
|
+
void Promise.resolve().then(() => this.#drain());
|
|
178
|
+
}
|
|
179
|
+
async #drain() {
|
|
180
|
+
let steps = 0;
|
|
181
|
+
try {
|
|
182
|
+
while (this.#queue.length > 0 &&
|
|
183
|
+
steps < this.configuration.maximumInternalStepsPerDrain) {
|
|
184
|
+
const job = this.#queue.shift();
|
|
185
|
+
if (!job)
|
|
186
|
+
break;
|
|
187
|
+
steps += 1;
|
|
188
|
+
this.#queuedBytes -= job.byteLength;
|
|
189
|
+
this.#inFlight = 1;
|
|
190
|
+
this.#inFlightBytes = job.byteLength;
|
|
191
|
+
const endpoint = this.#endpoints.get(addressKey(job.target));
|
|
192
|
+
if (!endpoint || endpoint !== job.selectedEndpoint) {
|
|
193
|
+
this.#finishJob(job, freezeReceipt('unavailable', job.envelope.messageId, job.target), 'endpoint_unavailable');
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
let receipt;
|
|
197
|
+
let diagnosticCode;
|
|
198
|
+
try {
|
|
199
|
+
const time = this.#sampleTime(job.target);
|
|
200
|
+
const priorState = endpoint.getState();
|
|
201
|
+
const request = Object.freeze({
|
|
202
|
+
envelope: job.envelope,
|
|
203
|
+
verifiedAt: time.verifiedAt,
|
|
204
|
+
receivedAt: time.receivedAt,
|
|
205
|
+
});
|
|
206
|
+
const decision = await endpoint.process(priorState, request);
|
|
207
|
+
assertProcessorDecision(decision, priorState, request, job.envelope, job.canonicalEnvelope);
|
|
208
|
+
endpoint.setState(decision.state);
|
|
209
|
+
if (decision.accepted) {
|
|
210
|
+
receipt = freezeReceipt('accepted', job.envelope.messageId, job.target);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
diagnosticCode = decision.code;
|
|
214
|
+
receipt = freezeReceipt('rejected', job.envelope.messageId, job.target);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
diagnosticCode = 'endpoint_failed';
|
|
219
|
+
receipt = freezeReceipt('rejected', job.envelope.messageId, job.target);
|
|
220
|
+
}
|
|
221
|
+
this.#finishJob(job, receipt, diagnosticCode);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
this.#draining = false;
|
|
226
|
+
if (this.#queue.length > 0) {
|
|
227
|
+
this.#scheduleDrain();
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
this.#resolveQuiescence();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
#finishJob(job, receipt, diagnosticCode) {
|
|
235
|
+
this.#inFlight = 0;
|
|
236
|
+
this.#inFlightBytes = 0;
|
|
237
|
+
if (diagnosticCode !== undefined && receipt.status !== 'accepted') {
|
|
238
|
+
this.#diagnose(receipt.status, diagnosticCode, job.envelope.messageId, job.target);
|
|
239
|
+
}
|
|
240
|
+
job.resolve(receipt);
|
|
241
|
+
}
|
|
242
|
+
#diagnose(status, code, messageId, target) {
|
|
243
|
+
try {
|
|
244
|
+
this.#onDiagnostic?.(Object.freeze({ status, code, messageId, target }));
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// Local diagnostics cannot change delivery or committed endpoint state.
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
#isQuiescent() {
|
|
251
|
+
return !this.#draining && this.#inFlight === 0 && this.#queue.length === 0;
|
|
252
|
+
}
|
|
253
|
+
#resolveQuiescence() {
|
|
254
|
+
if (!this.#isQuiescent())
|
|
255
|
+
return;
|
|
256
|
+
const resolveIdle = this.#resolveIdle;
|
|
257
|
+
this.#resolveIdle = undefined;
|
|
258
|
+
this.#idlePromise = undefined;
|
|
259
|
+
resolveIdle?.();
|
|
260
|
+
if (this.#closed) {
|
|
261
|
+
this.#resolveClose?.();
|
|
262
|
+
this.#resolveClose = undefined;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
class RegisteredObjectiveTopicEndpoint {
|
|
267
|
+
address;
|
|
268
|
+
handle;
|
|
269
|
+
process;
|
|
270
|
+
#driver;
|
|
271
|
+
#state;
|
|
272
|
+
constructor(driver, address, state, process) {
|
|
273
|
+
this.#driver = driver;
|
|
274
|
+
this.address = address;
|
|
275
|
+
this.#state = state;
|
|
276
|
+
this.process = process;
|
|
277
|
+
this.handle = Object.freeze({
|
|
278
|
+
address,
|
|
279
|
+
getState: this.getState.bind(this),
|
|
280
|
+
publish: this.publish.bind(this),
|
|
281
|
+
unregister: this.unregister.bind(this),
|
|
282
|
+
});
|
|
283
|
+
Object.freeze(this);
|
|
284
|
+
}
|
|
285
|
+
getState() {
|
|
286
|
+
return this.#state;
|
|
287
|
+
}
|
|
288
|
+
setState(state) {
|
|
289
|
+
this.#state = state;
|
|
290
|
+
}
|
|
291
|
+
publish(input) {
|
|
292
|
+
return this.#driver.publishFrom(this, input);
|
|
293
|
+
}
|
|
294
|
+
unregister() {
|
|
295
|
+
this.#driver.unregister(this);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function selectTopicTargets(state, sender, time, fanout) {
|
|
299
|
+
const targets = [];
|
|
300
|
+
for (const [peerId, view] of Object.entries(state.discovery.peerViews)) {
|
|
301
|
+
const card = state.discovery.peerCards[peerId];
|
|
302
|
+
const admission = state.discovery.admittedPeers[peerId];
|
|
303
|
+
if (!card ||
|
|
304
|
+
!admission ||
|
|
305
|
+
card.status !== 'active' ||
|
|
306
|
+
view.expiresAt <= time.receivedAt ||
|
|
307
|
+
card.expiresAt <= time.receivedAt ||
|
|
308
|
+
view.peerCardId !== card.peerCardId ||
|
|
309
|
+
view.cardRevision !== card.cardRevision ||
|
|
310
|
+
!admission.instanceIds.includes(card.instanceId) ||
|
|
311
|
+
!timestampIsBefore(time.verifiedAt, admission.validUntil) ||
|
|
312
|
+
(peerId === sender.peerId && card.instanceId === sender.instanceId)) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
targets.push(Object.freeze({
|
|
316
|
+
tenantId: sender.tenantId,
|
|
317
|
+
meshId: sender.meshId,
|
|
318
|
+
peerId,
|
|
319
|
+
instanceId: card.instanceId,
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
322
|
+
targets.sort(compareAddresses);
|
|
323
|
+
return Object.freeze(targets.slice(0, fanout));
|
|
324
|
+
}
|
|
325
|
+
function assertPublicationIdentity(sender, envelope, configuration) {
|
|
326
|
+
const identity = sender.getState().discovery.identity;
|
|
327
|
+
if (envelope.tenantId !== configuration.tenantId ||
|
|
328
|
+
envelope.meshId !== configuration.meshId ||
|
|
329
|
+
envelope.tenantId !== sender.address.tenantId ||
|
|
330
|
+
envelope.meshId !== sender.address.meshId ||
|
|
331
|
+
envelope.sender.peerId !== sender.address.peerId ||
|
|
332
|
+
envelope.sender.instanceId !== sender.address.instanceId ||
|
|
333
|
+
envelope.proof.keyId !== identity.keyId) {
|
|
334
|
+
throw new TypeError('Mesh coordination Objective topic sender identity mismatch');
|
|
335
|
+
}
|
|
336
|
+
if (envelope.audience.kind !== 'mesh') {
|
|
337
|
+
throw new TypeError('Mesh coordination Objective topic audience is required');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function assertObjectiveTopic(envelope) {
|
|
341
|
+
if (envelope.audience.kind !== 'mesh' ||
|
|
342
|
+
envelope.audience.topic !== 'objective' ||
|
|
343
|
+
(envelope.payload.type !== 'objective.announce' &&
|
|
344
|
+
envelope.payload.type !== 'objective.revise' &&
|
|
345
|
+
envelope.payload.type !== 'objective.cancel')) {
|
|
346
|
+
throw new TypeError('Mesh coordination Objective message family/topic mismatch');
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function assertProcessorDecision(decision, priorState, request, envelope, canonicalEnvelope) {
|
|
350
|
+
if (!isPlainDataRecord(decision) ||
|
|
351
|
+
!Object.isFrozen(decision) ||
|
|
352
|
+
typeof decision.accepted !== 'boolean' ||
|
|
353
|
+
!isTrustedMeshObjectiveInboundDecision(decision, priorState, request)) {
|
|
354
|
+
throw new TypeError('Invalid Mesh coordination Objective topic processor result');
|
|
355
|
+
}
|
|
356
|
+
const expectedKeys = decision.accepted
|
|
357
|
+
? ['accepted', 'duplicate', 'envelope', 'state']
|
|
358
|
+
: ['accepted', 'code', 'state'];
|
|
359
|
+
if (!hasExactDataKeys(decision, expectedKeys, expectedKeys)) {
|
|
360
|
+
throw new TypeError('Invalid Mesh coordination Objective topic processor result');
|
|
361
|
+
}
|
|
362
|
+
assertRuntimeState(decision.state);
|
|
363
|
+
const expectedIdentity = priorState.discovery.identity;
|
|
364
|
+
const identity = decision.state.discovery.identity;
|
|
365
|
+
if (identity.tenantId !== expectedIdentity.tenantId ||
|
|
366
|
+
identity.meshId !== expectedIdentity.meshId ||
|
|
367
|
+
identity.peerId !== expectedIdentity.peerId ||
|
|
368
|
+
identity.instanceId !== expectedIdentity.instanceId ||
|
|
369
|
+
identity.keyId !== expectedIdentity.keyId) {
|
|
370
|
+
throw new TypeError('Mesh coordination Objective topic processor changed identity');
|
|
371
|
+
}
|
|
372
|
+
if (decision.accepted) {
|
|
373
|
+
const acceptedEnvelope = validateSignedMeshEnvelope(decision.envelope);
|
|
374
|
+
const acceptedCanonical = acceptedEnvelope.ok
|
|
375
|
+
? canonicalizeMeshJsonBytes(acceptedEnvelope.value)
|
|
376
|
+
: undefined;
|
|
377
|
+
if (typeof decision.duplicate !== 'boolean' ||
|
|
378
|
+
!Object.isFrozen(decision.envelope) ||
|
|
379
|
+
decision.envelope.messageId !== envelope.messageId ||
|
|
380
|
+
!acceptedCanonical ||
|
|
381
|
+
!acceptedCanonical.ok ||
|
|
382
|
+
!sameBytes(acceptedCanonical.value, canonicalEnvelope)) {
|
|
383
|
+
throw new TypeError('Invalid Mesh coordination Objective topic acceptance');
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
else if (typeof decision.code !== 'string' || decision.code.length < 1) {
|
|
387
|
+
throw new TypeError('Invalid Mesh coordination Objective topic rejection');
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function assertRuntimeState(state) {
|
|
391
|
+
if (!isPlainDataRecord(state) ||
|
|
392
|
+
!Object.isFrozen(state) ||
|
|
393
|
+
!hasExactDataKeys(state, ['coordination', 'discovery', 'inbound', 'objectives'], ['coordination', 'discovery', 'inbound', 'objectives'])) {
|
|
394
|
+
throw new TypeError('Mesh coordination Objective topic state must be an immutable snapshot');
|
|
395
|
+
}
|
|
396
|
+
createMeshObjectiveInboundRuntimeState(state.coordination, state.discovery, state.objectives, state.inbound);
|
|
397
|
+
}
|
|
398
|
+
function assertOptions(options) {
|
|
399
|
+
if (!isPlainDataRecord(options) ||
|
|
400
|
+
!hasExactDataKeys(options, [
|
|
401
|
+
'clock',
|
|
402
|
+
'maximumDeliveriesPerPublish',
|
|
403
|
+
'maximumEndpoints',
|
|
404
|
+
'maximumInternalStepsPerDrain',
|
|
405
|
+
'maximumQueueDepth',
|
|
406
|
+
'maximumQueuedBytes',
|
|
407
|
+
'meshId',
|
|
408
|
+
'onDiagnostic',
|
|
409
|
+
'tenantId',
|
|
410
|
+
], ['clock', 'meshId', 'tenantId'])) {
|
|
411
|
+
throw new TypeError('Invalid Mesh coordination Objective topic driver options');
|
|
412
|
+
}
|
|
413
|
+
assertIdentifier(options.tenantId, 'tenantId');
|
|
414
|
+
assertIdentifier(options.meshId, 'meshId');
|
|
415
|
+
if (!options.clock ||
|
|
416
|
+
typeof options.clock !== 'object' ||
|
|
417
|
+
typeof options.clock.now !== 'function' ||
|
|
418
|
+
(options.onDiagnostic !== undefined &&
|
|
419
|
+
typeof options.onDiagnostic !== 'function')) {
|
|
420
|
+
throw new TypeError('Invalid Mesh coordination Objective topic trusted dependency');
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
function assertRegistration(registration) {
|
|
424
|
+
if (!isPlainDataRecord(registration) ||
|
|
425
|
+
!hasExactDataKeys(registration, ['processor', 'state'], ['processor', 'state']) ||
|
|
426
|
+
!registration.processor ||
|
|
427
|
+
typeof registration.processor !== 'object' ||
|
|
428
|
+
typeof registration.processor.process !== 'function') {
|
|
429
|
+
throw new TypeError('Invalid Mesh coordination Objective topic registration');
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function assertPublishInput(input) {
|
|
433
|
+
if (!isPlainDataRecord(input) ||
|
|
434
|
+
!hasExactDataKeys(input, ['envelope', 'fanout'], ['envelope'])) {
|
|
435
|
+
throw new TypeError('Invalid Mesh coordination Objective topic publish input');
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
function resolveLimits(options) {
|
|
439
|
+
const limits = {
|
|
440
|
+
maximumEndpoints: options.maximumEndpoints ??
|
|
441
|
+
DEFAULT_MESH_COORDINATION_OBJECTIVE_TOPIC_LIMITS.maximumEndpoints,
|
|
442
|
+
maximumQueueDepth: options.maximumQueueDepth ??
|
|
443
|
+
DEFAULT_MESH_COORDINATION_OBJECTIVE_TOPIC_LIMITS.maximumQueueDepth,
|
|
444
|
+
maximumQueuedBytes: options.maximumQueuedBytes ??
|
|
445
|
+
DEFAULT_MESH_COORDINATION_OBJECTIVE_TOPIC_LIMITS.maximumQueuedBytes,
|
|
446
|
+
maximumDeliveriesPerPublish: options.maximumDeliveriesPerPublish ??
|
|
447
|
+
DEFAULT_MESH_COORDINATION_OBJECTIVE_TOPIC_LIMITS.maximumDeliveriesPerPublish,
|
|
448
|
+
maximumInternalStepsPerDrain: options.maximumInternalStepsPerDrain ??
|
|
449
|
+
DEFAULT_MESH_COORDINATION_OBJECTIVE_TOPIC_LIMITS.maximumInternalStepsPerDrain,
|
|
450
|
+
};
|
|
451
|
+
for (const [name, value] of Object.entries(limits)) {
|
|
452
|
+
const ceiling = DEFAULT_MESH_COORDINATION_OBJECTIVE_TOPIC_LIMITS[name];
|
|
453
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > ceiling) {
|
|
454
|
+
throw new RangeError(`Mesh coordination Objective topic ${name} must be a positive safe integer no greater than its default`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return Object.freeze(limits);
|
|
458
|
+
}
|
|
459
|
+
function freezeAddress(value) {
|
|
460
|
+
assertIdentifier(value.tenantId, 'tenantId');
|
|
461
|
+
assertIdentifier(value.meshId, 'meshId');
|
|
462
|
+
assertIdentifier(value.peerId, 'peerId');
|
|
463
|
+
assertIdentifier(value.instanceId, 'instanceId');
|
|
464
|
+
return Object.freeze({
|
|
465
|
+
tenantId: value.tenantId,
|
|
466
|
+
meshId: value.meshId,
|
|
467
|
+
peerId: value.peerId,
|
|
468
|
+
instanceId: value.instanceId,
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
function freezeTime(value) {
|
|
472
|
+
if (!isPlainDataRecord(value) ||
|
|
473
|
+
!hasExactDataKeys(value, ['receivedAt', 'verifiedAt'], ['receivedAt', 'verifiedAt'])) {
|
|
474
|
+
throw new TypeError('Invalid Mesh coordination Objective topic time sample');
|
|
475
|
+
}
|
|
476
|
+
assertMeshLogicalTime(value.receivedAt);
|
|
477
|
+
if (!compareMeshTimestamps(value.verifiedAt, value.verifiedAt).ok) {
|
|
478
|
+
throw new TypeError('Invalid Mesh coordination Objective topic verification time');
|
|
479
|
+
}
|
|
480
|
+
return Object.freeze({
|
|
481
|
+
verifiedAt: value.verifiedAt,
|
|
482
|
+
receivedAt: value.receivedAt,
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
function freezeReceipt(status, messageId, target) {
|
|
486
|
+
return Object.freeze({ status, messageId, target });
|
|
487
|
+
}
|
|
488
|
+
function timestampIsBefore(left, right) {
|
|
489
|
+
const compared = compareMeshTimestamps(left, right);
|
|
490
|
+
return compared.ok && compared.value < 0;
|
|
491
|
+
}
|
|
492
|
+
function checkedProduct(left, right) {
|
|
493
|
+
if (!Number.isSafeInteger(left) ||
|
|
494
|
+
left < 0 ||
|
|
495
|
+
!Number.isSafeInteger(right) ||
|
|
496
|
+
right < 0 ||
|
|
497
|
+
(right !== 0 && left > Math.floor(Number.MAX_SAFE_INTEGER / right))) {
|
|
498
|
+
return undefined;
|
|
499
|
+
}
|
|
500
|
+
return left * right;
|
|
501
|
+
}
|
|
502
|
+
function sameBytes(left, right) {
|
|
503
|
+
if (left.byteLength !== right.byteLength)
|
|
504
|
+
return false;
|
|
505
|
+
for (let index = 0; index < left.byteLength; index += 1) {
|
|
506
|
+
if (left[index] !== right[index])
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
function exceedsSum(left, right, maximum) {
|
|
512
|
+
return (!Number.isSafeInteger(left) ||
|
|
513
|
+
left < 0 ||
|
|
514
|
+
!Number.isSafeInteger(right) ||
|
|
515
|
+
right < 0 ||
|
|
516
|
+
left > maximum ||
|
|
517
|
+
right > maximum - left);
|
|
518
|
+
}
|
|
519
|
+
function compareAddresses(left, right) {
|
|
520
|
+
if (left.peerId !== right.peerId)
|
|
521
|
+
return left.peerId < right.peerId ? -1 : 1;
|
|
522
|
+
if (left.instanceId !== right.instanceId) {
|
|
523
|
+
return left.instanceId < right.instanceId ? -1 : 1;
|
|
524
|
+
}
|
|
525
|
+
return 0;
|
|
526
|
+
}
|
|
527
|
+
function addressKey(address) {
|
|
528
|
+
return JSON.stringify([
|
|
529
|
+
address.tenantId,
|
|
530
|
+
address.meshId,
|
|
531
|
+
address.peerId,
|
|
532
|
+
address.instanceId,
|
|
533
|
+
]);
|
|
534
|
+
}
|
|
535
|
+
function assertIdentifier(value, name) {
|
|
536
|
+
if (typeof value !== 'string' ||
|
|
537
|
+
!identifierPattern.test(value) ||
|
|
538
|
+
utf8Encoder.encode(value).byteLength >
|
|
539
|
+
DEFAULT_MESH_PROTOCOL_LIMITS.maximumIdBytes) {
|
|
540
|
+
throw new TypeError(`Invalid Mesh coordination Objective topic ${name}`);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
function isPlainDataRecord(value) {
|
|
544
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
545
|
+
return false;
|
|
546
|
+
const prototype = Object.getPrototypeOf(value);
|
|
547
|
+
return ((prototype === null || prototype === Object.prototype) &&
|
|
548
|
+
Object.getOwnPropertySymbols(value).length === 0 &&
|
|
549
|
+
Object.getOwnPropertyNames(value).every((key) => {
|
|
550
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
551
|
+
return (descriptor !== undefined &&
|
|
552
|
+
'value' in descriptor &&
|
|
553
|
+
descriptor.enumerable === true);
|
|
554
|
+
}));
|
|
555
|
+
}
|
|
556
|
+
function hasExactDataKeys(value, supportedKeys, requiredKeys) {
|
|
557
|
+
const supported = new Set(supportedKeys);
|
|
558
|
+
return (Object.getOwnPropertyNames(value).every((key) => supported.has(key)) &&
|
|
559
|
+
requiredKeys.every((key) => Object.hasOwn(value, key)));
|
|
560
|
+
}
|
|
561
|
+
//# sourceMappingURL=coordination-objective-topic.js.map
|