@aeolus-fleet/core 0.20.3 → 0.20.5
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 +1 -1
- package/dist/adapters/http/server.d.ts +0 -2
- package/dist/adapters/http/server.js +4 -12
- package/dist/adapters/http/server.js.map +1 -1
- package/dist/adapters/prisma/client.d.ts +0 -2
- package/dist/adapters/prisma/client.js +0 -9
- package/dist/adapters/prisma/client.js.map +1 -1
- package/dist/adapters/prisma/generated/browser.d.ts +18 -0
- package/dist/adapters/prisma/generated/client.d.ts +18 -0
- package/dist/adapters/prisma/generated/internal/class.d.ts +22 -0
- package/dist/adapters/prisma/generated/internal/class.js +4 -4
- package/dist/adapters/prisma/generated/internal/class.js.map +1 -1
- package/dist/adapters/prisma/generated/internal/prismaNamespace.d.ts +174 -1
- package/dist/adapters/prisma/generated/internal/prismaNamespace.js +22 -1
- package/dist/adapters/prisma/generated/internal/prismaNamespace.js.map +1 -1
- package/dist/adapters/prisma/generated/internal/prismaNamespaceBrowser.d.ts +23 -0
- package/dist/adapters/prisma/generated/internal/prismaNamespaceBrowser.js +22 -1
- package/dist/adapters/prisma/generated/internal/prismaNamespaceBrowser.js.map +1 -1
- package/dist/adapters/prisma/generated/models/CrewRequest.d.ts +53 -1
- package/dist/adapters/prisma/generated/models/Delivery.d.ts +68 -1
- package/dist/adapters/prisma/generated/models/Fleet.d.ts +481 -0
- package/dist/adapters/prisma/generated/models/NetworkSettings.d.ts +1207 -0
- package/dist/adapters/prisma/generated/models/NetworkSettings.js +2 -0
- package/dist/adapters/prisma/generated/models/NetworkSettings.js.map +1 -0
- package/dist/adapters/prisma/generated/models/ReachRefusal.d.ts +1278 -0
- package/dist/adapters/prisma/generated/models/ReachRefusal.js +2 -0
- package/dist/adapters/prisma/generated/models/ReachRefusal.js.map +1 -0
- package/dist/adapters/prisma/generated/models.d.ts +2 -0
- package/dist/adapters/prisma/messaging.js +16 -2
- package/dist/adapters/prisma/messaging.js.map +1 -1
- package/dist/adapters/prisma/network.d.ts +4 -0
- package/dist/adapters/prisma/network.js +89 -0
- package/dist/adapters/prisma/network.js.map +1 -0
- package/dist/adapters/prisma/registry.js +16 -6
- package/dist/adapters/prisma/registry.js.map +1 -1
- package/dist/adapters/prisma/rows.d.ts +2 -0
- package/dist/adapters/prisma/rows.js +8 -1
- package/dist/adapters/prisma/rows.js.map +1 -1
- package/dist/adapters/prisma/unit-of-work.d.ts +3 -1
- package/dist/adapters/prisma/unit-of-work.js +3 -0
- package/dist/adapters/prisma/unit-of-work.js.map +1 -1
- package/dist/adapters/trpc/console.d.ts +1 -1
- package/dist/adapters/trpc/context.d.ts +8 -0
- package/dist/adapters/trpc/fleet.d.ts +88 -6
- package/dist/adapters/trpc/fleet.js +49 -1
- package/dist/adapters/trpc/fleet.js.map +1 -1
- package/dist/adapters/trpc/router.d.ts +85 -7
- package/dist/adapters/trpc/ship-contract.js +3 -0
- package/dist/adapters/trpc/ship-contract.js.map +1 -1
- package/dist/adapters/trpc/ship-protocol.js +9 -8
- package/dist/adapters/trpc/ship-protocol.js.map +1 -1
- package/dist/adapters/trpc/trpc.js +4 -0
- package/dist/adapters/trpc/trpc.js.map +1 -1
- package/dist/app.js +1 -2
- package/dist/app.js.map +1 -1
- package/dist/domain/messaging/answer.d.ts +17 -0
- package/dist/domain/messaging/answer.js +16 -0
- package/dist/domain/messaging/answer.js.map +1 -0
- package/dist/domain/messaging/message.d.ts +10 -1
- package/dist/domain/messaging/message.js +4 -2
- package/dist/domain/messaging/message.js.map +1 -1
- package/dist/domain/messaging/ports.d.ts +2 -0
- package/dist/domain/messaging/resend-delivery.d.ts +11 -4
- package/dist/domain/messaging/resend-delivery.js +87 -62
- package/dist/domain/messaging/resend-delivery.js.map +1 -1
- package/dist/domain/messaging/send-message.d.ts +11 -4
- package/dist/domain/messaging/send-message.js +32 -6
- package/dist/domain/messaging/send-message.js.map +1 -1
- package/dist/domain/registry/check-reach.d.ts +48 -0
- package/dist/domain/registry/check-reach.js +89 -0
- package/dist/domain/registry/check-reach.js.map +1 -0
- package/dist/domain/registry/crew-request.d.ts +9 -3
- package/dist/domain/registry/crew-request.js +18 -6
- package/dist/domain/registry/crew-request.js.map +1 -1
- package/dist/domain/registry/get-ship.d.ts +3 -3
- package/dist/domain/registry/get-ship.js +2 -1
- package/dist/domain/registry/get-ship.js.map +1 -1
- package/dist/domain/registry/network-settings.d.ts +122 -0
- package/dist/domain/registry/network-settings.js +174 -0
- package/dist/domain/registry/network-settings.js.map +1 -0
- package/dist/domain/registry/ports.d.ts +28 -0
- package/dist/domain/registry/public.d.ts +1 -0
- package/dist/domain/registry/public.js +1 -0
- package/dist/domain/registry/public.js.map +1 -1
- package/dist/domain/registry/reach-refusal.d.ts +32 -0
- package/dist/domain/registry/reach-refusal.js +2 -0
- package/dist/domain/registry/reach-refusal.js.map +1 -0
- package/dist/domain/registry/read-reach-refusals.d.ts +14 -0
- package/dist/domain/registry/read-reach-refusals.js +17 -0
- package/dist/domain/registry/read-reach-refusals.js.map +1 -0
- package/dist/domain/registry/register-network-plugin.d.ts +28 -0
- package/dist/domain/registry/register-network-plugin.js +26 -0
- package/dist/domain/registry/register-network-plugin.js.map +1 -0
- package/dist/domain/registry/retire-ship.d.ts +7 -4
- package/dist/domain/registry/retire-ship.js +14 -3
- package/dist/domain/registry/retire-ship.js.map +1 -1
- package/dist/domain/registry/set-network-rules.d.ts +29 -0
- package/dist/domain/registry/set-network-rules.js +25 -0
- package/dist/domain/registry/set-network-rules.js.map +1 -0
- package/dist/domain/registry/ship-report.d.ts +5 -0
- package/dist/domain/registry/ship-report.js +3 -0
- package/dist/domain/registry/ship-report.js.map +1 -1
- package/dist/domain/registry/unregister-network-plugin.d.ts +27 -0
- package/dist/domain/registry/unregister-network-plugin.js +25 -0
- package/dist/domain/registry/unregister-network-plugin.js.map +1 -0
- package/dist/domain/shared/errors.d.ts +1 -1
- package/dist/domain/shared/errors.js.map +1 -1
- package/dist/domain/shared/history.d.ts +17 -0
- package/dist/domain/shared/history.js +4 -1
- package/dist/domain/shared/history.js.map +1 -1
- package/dist/domain/shared/read-needs-attention.d.ts +4 -4
- package/dist/domain/shared/read-needs-attention.js +4 -3
- package/dist/domain/shared/read-needs-attention.js.map +1 -1
- package/dist/domain/shared/read-ship-timeline.d.ts +5 -4
- package/dist/domain/shared/read-ship-timeline.js +7 -3
- package/dist/domain/shared/read-ship-timeline.js.map +1 -1
- package/dist/wiring.d.ts +8 -0
- package/dist/wiring.js +9 -0
- package/dist/wiring.js.map +1 -1
- package/package.json +2 -2
- package/src/adapters/prisma/migrations/20261009200000_network_scope/migration.sql +8 -0
- package/src/adapters/prisma/migrations/20261009210000_network_rules/migration.sql +31 -0
- package/src/adapters/prisma/migrations/20261009220000_type_delivery_reachable_ships/migration.sql +5 -0
- package/src/adapters/prisma/migrations/20261010070000_crew_request_is_final/migration.sql +7 -0
- package/src/adapters/prisma/migrations/20261010080000_network_plugin/migration.sql +21 -0
- package/src/adapters/prisma/schema.prisma +48 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { FleetId, IdGenerator, ShipId } from '@aeolus-fleet/common';
|
|
2
|
+
import { type DomainError } from '../shared/errors.js';
|
|
3
|
+
import { type Result } from '../shared/result.js';
|
|
4
|
+
import type { Recipient } from '../shared/selector.js';
|
|
5
|
+
import type { LabelRepository, LeaseRepository, NetworkSettingsRepository, ReachRefusalRepository, ShipRepository } from './ports.js';
|
|
6
|
+
/** The ports a send's reach check reads and writes, inside the send's unit of work. */
|
|
7
|
+
export interface CheckReachTx {
|
|
8
|
+
networkSettings: Pick<NetworkSettingsRepository, 'findForShare'>;
|
|
9
|
+
ships: Pick<ShipRepository, 'find' | 'listActiveOfType'>;
|
|
10
|
+
labels: Pick<LabelRepository, 'carriedBy' | 'find'>;
|
|
11
|
+
reachRefusals: ReachRefusalRepository;
|
|
12
|
+
leases: Pick<LeaseRepository, 'findLastSeen'>;
|
|
13
|
+
}
|
|
14
|
+
/** One fixed message, so a refused sender learns nothing about the rules (decision 0034). */
|
|
15
|
+
export type NotReachable = DomainError<'NOT_REACHABLE'>;
|
|
16
|
+
/** What a send may go on with: for a type, the ships of it the sender may reach, or none for any ship of it. */
|
|
17
|
+
export interface Reach {
|
|
18
|
+
reachableShipIds?: readonly ShipId[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Whether the fleet's network rules let the sender reach the recipient a
|
|
22
|
+
* selector resolved to, at send time: Messaging's question to Registry before
|
|
23
|
+
* it stores a message (decision 0034). The settings stay held shared until
|
|
24
|
+
* the unit of work ends, so a change of rules waits for the send, and a send
|
|
25
|
+
* that comes after it is checked against the new version.
|
|
26
|
+
*
|
|
27
|
+
* For a type, the ships of it the sender may reach now are the ones that may
|
|
28
|
+
* claim the delivery, fixed here; none of them refuses the send. With no
|
|
29
|
+
* rules, or from argo, who reaches every ship, any ship of the type may.
|
|
30
|
+
*
|
|
31
|
+
* A refusal is recorded here, in the send's unit of work, with the ships and
|
|
32
|
+
* the label values they carry now and the settings version: the send stores
|
|
33
|
+
* nothing else, so its caller commits only the record. `isAnswerToSender` says
|
|
34
|
+
* the send answers the sender of a message the sending ship received.
|
|
35
|
+
*
|
|
36
|
+
* While the fleet's networking plugin is not responding, what it declared
|
|
37
|
+
* applies instead of its rules (decision 0035), and a refusal says so.
|
|
38
|
+
*/
|
|
39
|
+
export declare function checkReach(deps: {
|
|
40
|
+
tx: CheckReachTx;
|
|
41
|
+
ids: IdGenerator;
|
|
42
|
+
}, send: {
|
|
43
|
+
fleetId: FleetId;
|
|
44
|
+
senderShipId: ShipId;
|
|
45
|
+
recipient: Recipient;
|
|
46
|
+
isAnswerToSender: boolean;
|
|
47
|
+
at: Date;
|
|
48
|
+
}): Promise<Result<Reach, NotReachable>>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { refuse } from '../shared/errors.js';
|
|
2
|
+
import { ok } from '../shared/result.js';
|
|
3
|
+
import { allowsReach, isNetworkPluginResponding, rulesInForce } from './network-settings.js';
|
|
4
|
+
/**
|
|
5
|
+
* Whether the fleet's network rules let the sender reach the recipient a
|
|
6
|
+
* selector resolved to, at send time: Messaging's question to Registry before
|
|
7
|
+
* it stores a message (decision 0034). The settings stay held shared until
|
|
8
|
+
* the unit of work ends, so a change of rules waits for the send, and a send
|
|
9
|
+
* that comes after it is checked against the new version.
|
|
10
|
+
*
|
|
11
|
+
* For a type, the ships of it the sender may reach now are the ones that may
|
|
12
|
+
* claim the delivery, fixed here; none of them refuses the send. With no
|
|
13
|
+
* rules, or from argo, who reaches every ship, any ship of the type may.
|
|
14
|
+
*
|
|
15
|
+
* A refusal is recorded here, in the send's unit of work, with the ships and
|
|
16
|
+
* the label values they carry now and the settings version: the send stores
|
|
17
|
+
* nothing else, so its caller commits only the record. `isAnswerToSender` says
|
|
18
|
+
* the send answers the sender of a message the sending ship received.
|
|
19
|
+
*
|
|
20
|
+
* While the fleet's networking plugin is not responding, what it declared
|
|
21
|
+
* applies instead of its rules (decision 0035), and a refusal says so.
|
|
22
|
+
*/
|
|
23
|
+
export async function checkReach(deps, send) {
|
|
24
|
+
const { tx, ids } = deps;
|
|
25
|
+
const { fleetId, recipient } = send;
|
|
26
|
+
const settings = await tx.networkSettings.findForShare(fleetId);
|
|
27
|
+
const inForce = await rulesInForceAt(tx, { settings, now: send.at });
|
|
28
|
+
const senderShip = await tx.ships.find(fleetId, send.senderShipId);
|
|
29
|
+
if (inForce.rules === null || !senderShip || senderShip.kind === 'operator') {
|
|
30
|
+
return ok({});
|
|
31
|
+
}
|
|
32
|
+
const sender = await shipAsItIs(tx, senderShip);
|
|
33
|
+
const allows = (to) => allowsReach(inForce, { sender: reaching(sender), recipient: reaching(to), isAnswerToSender: send.isAnswerToSender });
|
|
34
|
+
const refusal = { fleetId, at: send.at, sender: refused(sender), settingsVersion: settings.version, whilePluginUnavailable: inForce.whilePluginUnavailable };
|
|
35
|
+
if (recipient.kind === 'type') {
|
|
36
|
+
const ships = [];
|
|
37
|
+
for (const ship of await tx.ships.listActiveOfType(fleetId, recipient.type)) {
|
|
38
|
+
ships.push(await shipAsItIs(tx, ship));
|
|
39
|
+
}
|
|
40
|
+
const reachable = ships.filter(allows);
|
|
41
|
+
if (reachable.length > 0) {
|
|
42
|
+
return ok({ reachableShipIds: reachable.map(({ ship }) => ship.id) });
|
|
43
|
+
}
|
|
44
|
+
await tx.reachRefusals.record({ ...refusal, id: ids('reachRefusal'), recipient: { kind: 'type', type: recipient.type, ships: ships.map(refused) } });
|
|
45
|
+
return refuse('NOT_REACHABLE', NOT_REACHABLE_MESSAGE);
|
|
46
|
+
}
|
|
47
|
+
const reachedShip = await tx.ships.find(fleetId, recipient.shipId);
|
|
48
|
+
if (!reachedShip) {
|
|
49
|
+
// resolveSelector found it in this unit of work: a ship is never deleted, only retired.
|
|
50
|
+
return ok({});
|
|
51
|
+
}
|
|
52
|
+
const reached = await shipAsItIs(tx, reachedShip);
|
|
53
|
+
if (allows(reached)) {
|
|
54
|
+
return ok({});
|
|
55
|
+
}
|
|
56
|
+
await tx.reachRefusals.record({ ...refusal, id: ids('reachRefusal'), recipient: { kind: 'ship', ship: refused(reached) } });
|
|
57
|
+
return refuse('NOT_REACHABLE', NOT_REACHABLE_MESSAGE);
|
|
58
|
+
}
|
|
59
|
+
const NOT_REACHABLE_MESSAGE = 'The network rules do not allow this send';
|
|
60
|
+
/** The rules in force at the send: the plugin's crew last seen, read without a lock, says whether it responds. */
|
|
61
|
+
async function rulesInForceAt(tx, at) {
|
|
62
|
+
const { settings, now } = at;
|
|
63
|
+
const { plugin } = settings;
|
|
64
|
+
if (plugin === null) {
|
|
65
|
+
return rulesInForce(settings, false);
|
|
66
|
+
}
|
|
67
|
+
const lastSeenAt = await tx.leases.findLastSeen(settings.fleetId, plugin.shipId);
|
|
68
|
+
return rulesInForce(settings, isNetworkPluginResponding(plugin, { lastSeenAt, now }));
|
|
69
|
+
}
|
|
70
|
+
/** The ship with the label values it carries now, by key then value, with their texts. */
|
|
71
|
+
async function shipAsItIs(tx, ship) {
|
|
72
|
+
const labels = [];
|
|
73
|
+
for (const carried of await tx.labels.carriedBy(ship.fleetId, ship.id)) {
|
|
74
|
+
const label = await tx.labels.find(ship.fleetId, carried.labelId);
|
|
75
|
+
const value = label?.values.find((held) => held.id === carried.valueId);
|
|
76
|
+
if (label && value) {
|
|
77
|
+
labels.push({ labelId: label.id, key: label.key, valueId: value.id, value: value.value });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
labels.sort((first, second) => first.key.localeCompare(second.key) || first.value.localeCompare(second.value));
|
|
81
|
+
return { ship, labels };
|
|
82
|
+
}
|
|
83
|
+
function reaching({ ship, labels }) {
|
|
84
|
+
return { kind: ship.kind, labels: labels.map((label) => label.valueId) };
|
|
85
|
+
}
|
|
86
|
+
function refused({ ship, labels }) {
|
|
87
|
+
return { id: ship.id, name: ship.name, labels };
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=check-reach.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-reach.js","sourceRoot":"","sources":["../../../src/domain/registry/check-reach.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,6 +32,8 @@ export interface CrewRequest {
|
|
|
32
32
|
attempt: number;
|
|
33
33
|
/** When the session its trierarch runs now started, written with the status; null while none runs. */
|
|
34
34
|
sessionStartedAt: Date | null;
|
|
35
|
+
/** Whether its crew is final: set by its trierarch's first running, restarting or crashed, kept whatever status follows, cleared on every new settings version (#382, #472). */
|
|
36
|
+
isFinal: boolean;
|
|
35
37
|
/** The trierarchs that gave it back before their crew was final, oldest first (#382); cleared on every new settings version. */
|
|
36
38
|
givenBack: GiveBack[];
|
|
37
39
|
}
|
|
@@ -49,7 +51,8 @@ export type RequestCrewRefusal = CrewRequestShipRefusal | Releasing | DomainErro
|
|
|
49
51
|
/**
|
|
50
52
|
* A requester asks that the ship be kept crewed, with these settings: the
|
|
51
53
|
* request it holds now, replacing the settings of any it held and keeping its
|
|
52
|
-
* assignment and status,
|
|
54
|
+
* assignment and status, its crew not final until the new settings run, and
|
|
55
|
+
* CrewRequested with the settings version, never
|
|
53
56
|
* the settings. Never argo, the viewer ship or a retired ship; settings of at
|
|
54
57
|
* most 16 KB; never while the request is releasing.
|
|
55
58
|
*/
|
|
@@ -121,6 +124,8 @@ export declare function explainCrewRequest({ ship, current }: {
|
|
|
121
124
|
* The assigned trierarch writes how its crew of the ship stands, with the
|
|
122
125
|
* restart attempt and when the session started (#332), and CrewStatusChanged
|
|
123
126
|
* when any of them changes. Once the request is releasing, only releasing.
|
|
127
|
+
* Its first running, restarting or crashed makes the crew final, and no later
|
|
128
|
+
* status undoes it (#472).
|
|
124
129
|
*/
|
|
125
130
|
export declare function reportCrewStatus({ ship, current, trierarchShipId }: {
|
|
126
131
|
ship: Ship;
|
|
@@ -153,8 +158,9 @@ export declare function confirmCrewRelease({ ship, current, trierarchShipId }: {
|
|
|
153
158
|
export type GiveBackCrewRequestRefusal = NotFound | NotTheCallers | Releasing | DomainError<'CREW_REQUEST_FINAL' | 'CREW_REQUEST_SETTINGS_CHANGED' | 'INVALID_CREW_REQUEST_REASON'>;
|
|
154
159
|
/**
|
|
155
160
|
* The assigned trierarch gives back a request it tried to fulfil and failed,
|
|
156
|
-
* with a reason (#382): only while its crew is not final,
|
|
157
|
-
*
|
|
161
|
+
* with a reason (#382): only while its crew is not final, never once its
|
|
162
|
+
* trierarch reported it running, restarting or crashed, whatever it reported
|
|
163
|
+
* since (#472), and only the settings version it tried. The request is unassigned
|
|
158
164
|
* again, its status cleared, and the trierarch recorded, with
|
|
159
165
|
* CrewRequestGivenBack, so placement leaves it out. A repeat by the same
|
|
160
166
|
* trierarch for the same version, after a lost answer, is an OK with no
|
|
@@ -15,7 +15,8 @@ function event(ship, change) {
|
|
|
15
15
|
/**
|
|
16
16
|
* A requester asks that the ship be kept crewed, with these settings: the
|
|
17
17
|
* request it holds now, replacing the settings of any it held and keeping its
|
|
18
|
-
* assignment and status,
|
|
18
|
+
* assignment and status, its crew not final until the new settings run, and
|
|
19
|
+
* CrewRequested with the settings version, never
|
|
19
20
|
* the settings. Never argo, the viewer ship or a retired ship; settings of at
|
|
20
21
|
* most 16 KB; never while the request is releasing.
|
|
21
22
|
*/
|
|
@@ -42,6 +43,8 @@ export function requestCrew({ ship, current }, input) {
|
|
|
42
43
|
reason: current?.reason ?? null,
|
|
43
44
|
attempt: current?.attempt ?? 0,
|
|
44
45
|
sessionStartedAt: current?.sessionStartedAt ?? null,
|
|
46
|
+
// New settings are a new request: not final until they run.
|
|
47
|
+
isFinal: false,
|
|
45
48
|
// New settings may fit the trierarchs that gave the old ones back.
|
|
46
49
|
givenBack: [],
|
|
47
50
|
};
|
|
@@ -128,6 +131,8 @@ function checkAssignedTo({ ship, current, trierarchShipId }) {
|
|
|
128
131
|
* The assigned trierarch writes how its crew of the ship stands, with the
|
|
129
132
|
* restart attempt and when the session started (#332), and CrewStatusChanged
|
|
130
133
|
* when any of them changes. Once the request is releasing, only releasing.
|
|
134
|
+
* Its first running, restarting or crashed makes the crew final, and no later
|
|
135
|
+
* status undoes it (#472).
|
|
131
136
|
*/
|
|
132
137
|
export function reportCrewStatus({ ship, current, trierarchShipId }, input) {
|
|
133
138
|
if (!current) {
|
|
@@ -145,7 +150,13 @@ export function reportCrewStatus({ ship, current, trierarchShipId }, input) {
|
|
|
145
150
|
return ok({ request: current, events: [] });
|
|
146
151
|
}
|
|
147
152
|
return ok({
|
|
148
|
-
request: {
|
|
153
|
+
request: {
|
|
154
|
+
...current,
|
|
155
|
+
status: input.status,
|
|
156
|
+
attempt: input.attempt,
|
|
157
|
+
sessionStartedAt: input.sessionStartedAt,
|
|
158
|
+
isFinal: current.isFinal || FINAL_STATUSES.has(input.status),
|
|
159
|
+
},
|
|
149
160
|
events: [event(ship, { at: input.at, actor: input.actor, type: 'CrewStatusChanged', details: { status: input.status, attempt: input.attempt } })],
|
|
150
161
|
});
|
|
151
162
|
}
|
|
@@ -168,8 +179,9 @@ export function confirmCrewRelease({ ship, current, trierarchShipId }, input) {
|
|
|
168
179
|
}
|
|
169
180
|
/**
|
|
170
181
|
* The assigned trierarch gives back a request it tried to fulfil and failed,
|
|
171
|
-
* with a reason (#382): only while its crew is not final,
|
|
172
|
-
*
|
|
182
|
+
* with a reason (#382): only while its crew is not final, never once its
|
|
183
|
+
* trierarch reported it running, restarting or crashed, whatever it reported
|
|
184
|
+
* since (#472), and only the settings version it tried. The request is unassigned
|
|
173
185
|
* again, its status cleared, and the trierarch recorded, with
|
|
174
186
|
* CrewRequestGivenBack, so placement leaves it out. A repeat by the same
|
|
175
187
|
* trierarch for the same version, after a lost answer, is an OK with no
|
|
@@ -190,8 +202,8 @@ export function giveBackCrewRequest({ ship, current, trierarchShipId }, input) {
|
|
|
190
202
|
if (current.status === 'releasing') {
|
|
191
203
|
return refuse('CREW_REQUEST_RELEASING', `${ship.name}'s crew request is releasing: stop its session, end its lease and confirm`);
|
|
192
204
|
}
|
|
193
|
-
if (current.
|
|
194
|
-
return refuse('CREW_REQUEST_FINAL', `${ship.name}'s crew is final
|
|
205
|
+
if (current.isFinal) {
|
|
206
|
+
return refuse('CREW_REQUEST_FINAL', `${ship.name}'s crew is final: a request is given back only before its crew works`);
|
|
195
207
|
}
|
|
196
208
|
if (input.settingsVersion !== current.settingsVersion) {
|
|
197
209
|
return refuse('CREW_REQUEST_SETTINGS_CHANGED', `${ship.name}'s crew request is at settings version ${String(current.settingsVersion)}, not ${String(input.settingsVersion)}: crew the new settings`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crew-request.js","sourceRoot":"","sources":["../../../src/domain/registry/crew-request.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,+BAA+B,EAC/B,SAAS,EACT,wBAAwB,GAKzB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,QAAQ,EAAe,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAoB,MAAM,qBAAqB,CAAC;AAE/D,OAAO,EAAE,EAAE,EAAe,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,uBAAuB,EAA0C,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"crew-request.js","sourceRoot":"","sources":["../../../src/domain/registry/crew-request.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,13 +5,13 @@ import { type Result } from '../shared/result.js';
|
|
|
5
5
|
import { type ListedShip } from './list-fleet.js';
|
|
6
6
|
import { type CrewRequest } from './crew-request.js';
|
|
7
7
|
import type { FleetListing } from './ports.js';
|
|
8
|
-
import type
|
|
8
|
+
import { type ShownReport } from './ship-report.js';
|
|
9
9
|
/** One ship for its page: as the fleet lists it, with its crew's report whole, when it was commissioned and since when it is crewed. */
|
|
10
10
|
export interface ShipDetail extends Omit<ListedShip, 'report' | 'crewRequest'> {
|
|
11
11
|
/** The ship's crew request, settings included; null when it holds none. */
|
|
12
12
|
crewRequest: (NonNullable<ListedShip['crewRequest']> & Pick<CrewRequest, 'settings'>) | null;
|
|
13
|
-
/** The crew's last report, details included; null until it reports, and while no session crews the ship. */
|
|
14
|
-
report:
|
|
13
|
+
/** The crew's last report, details and their size included; null until it reports, and while no session crews the ship. */
|
|
14
|
+
report: ShownReport | null;
|
|
15
15
|
commissionedAt: Date;
|
|
16
16
|
/** Since when the session crewing it has held it; null while no session does. */
|
|
17
17
|
crewedSince: Date | null;
|
|
@@ -2,6 +2,7 @@ import { refuse } from '../shared/errors.js';
|
|
|
2
2
|
import { ok } from '../shared/result.js';
|
|
3
3
|
import { listedShipOf } from './list-fleet.js';
|
|
4
4
|
import { checkReaches } from './crew-request.js';
|
|
5
|
+
import { shownReportOf } from './ship-report.js';
|
|
5
6
|
/** The scopes that read every ship; crew:run reads only the ships assigned to the caller's ship. */
|
|
6
7
|
const READ_SCOPES = ['fleet:read'];
|
|
7
8
|
/**
|
|
@@ -24,7 +25,7 @@ export function createGetShip(deps) {
|
|
|
24
25
|
const counts = await deps.listing.deliveryCounts(caller.fleetId, shipId);
|
|
25
26
|
return ok({
|
|
26
27
|
...listed,
|
|
27
|
-
report: facts.openLease?.report
|
|
28
|
+
report: facts.openLease?.report ? shownReportOf(facts.openLease.report) : null,
|
|
28
29
|
crewRequest: listed.crewRequest && facts.crewRequest && { ...listed.crewRequest, settings: facts.crewRequest.settings },
|
|
29
30
|
inFlightDeliveries: counts.inFlight,
|
|
30
31
|
openDeliveries: counts.open,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-ship.js","sourceRoot":"","sources":["../../../src/domain/registry/get-ship.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAoB,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,EAAE,EAAe,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAmB,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAoB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"get-ship.js","sourceRoot":"","sources":["../../../src/domain/registry/get-ship.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAoB,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,EAAE,EAAe,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAmB,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAoB,MAAM,mBAAmB,CAAC;AAEnE,OAAO,EAAE,aAAa,EAAoB,MAAM,kBAAkB,CAAC;AAsBnE,oGAAoG;AACpG,MAAM,WAAW,GAAG,CAAC,YAAY,CAAU,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAA+B;IAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QAClC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC9D,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,MAAM,CAAC,gBAAgB,EAAE,WAAW,MAAM,gBAAgB,CAAC,CAAC;QACrE,CAAC;QACD,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAC;QACjH,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAClB,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACzE,OAAO,EAAE,CAAC;YACR,GAAG,MAAM;YACT,MAAM,EAAE,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI;YAC9E,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,IAAI,EAAE,GAAG,MAAM,CAAC,WAAW,EAAE,QAAQ,EAAE,KAAK,CAAC,WAAW,CAAC,QAAQ,EAAE;YACvH,kBAAkB,EAAE,MAAM,CAAC,QAAQ;YACnC,cAAc,EAAE,MAAM,CAAC,IAAI;YAC3B,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,SAAS;YACpC,WAAW,EAAE,KAAK,CAAC,SAAS,EAAE,SAAS,IAAI,IAAI;SAChD,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fleet's network settings (decision 0034): which ships may send to
|
|
3
|
+
* which. A rule says that the ships matching one label selector may send to
|
|
4
|
+
* the ships matching another; allow-only, no deny rules, no precedence. A
|
|
5
|
+
* fleet holds its rules, or none (all-to-all), with a version that moves on
|
|
6
|
+
* every set, so a refusal names the settings that refused it.
|
|
7
|
+
*/
|
|
8
|
+
import { type FleetId, type LabelValueId, type NetworkPluginDeclaration, type ShipId, type ShipKind, type WhileUnavailable } from '@aeolus-fleet/common';
|
|
9
|
+
import { type DomainError } from '../shared/errors.js';
|
|
10
|
+
import type { Actor, NewEvent } from '../shared/events.js';
|
|
11
|
+
import { type Result } from '../shared/result.js';
|
|
12
|
+
/** The ships carrying every one of these label values may send to the ships carrying every one of those. */
|
|
13
|
+
export interface NetworkRule {
|
|
14
|
+
from: readonly LabelValueId[];
|
|
15
|
+
to: readonly LabelValueId[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The fleet's networking plugin (decision 0035): the ship that alone sets the
|
|
19
|
+
* rules while registered, with what it declared for while it is unavailable.
|
|
20
|
+
*/
|
|
21
|
+
export interface NetworkPlugin extends NetworkPluginDeclaration {
|
|
22
|
+
shipId: ShipId;
|
|
23
|
+
}
|
|
24
|
+
export interface NetworkSettings {
|
|
25
|
+
fleetId: FleetId;
|
|
26
|
+
/** The rules every send is checked against; none is all-to-all, an empty list allows only the fixed exceptions. */
|
|
27
|
+
rules: readonly NetworkRule[] | null;
|
|
28
|
+
/** 0 until the fleet first changes its settings; one more on every change. */
|
|
29
|
+
version: number;
|
|
30
|
+
/** The fleet's networking plugin, or none: then anyone with fleet:network sets the rules. */
|
|
31
|
+
plugin: NetworkPlugin | null;
|
|
32
|
+
}
|
|
33
|
+
/** A fleet that never set rules: none, at version 0, without a plugin. */
|
|
34
|
+
export declare function noNetworkSettings(fleetId: FleetId): NetworkSettings;
|
|
35
|
+
export type NotTheNetworkPlugin = DomainError<'NOT_THE_NETWORK_PLUGIN'>;
|
|
36
|
+
export type SetNetworkRulesRefusal = DomainError<'INVALID_NETWORK_RULES'> | NotTheNetworkPlugin;
|
|
37
|
+
export type RegisterNetworkPluginRefusal = DomainError<'NETWORK_PLUGIN_REGISTERED'>;
|
|
38
|
+
/**
|
|
39
|
+
* The ship becomes the fleet's networking plugin, with what it declared, at
|
|
40
|
+
* the next version, with NetworkPluginRegistered. The rules stay until it
|
|
41
|
+
* supplies its own. The plugin may register again, replacing what it
|
|
42
|
+
* declared; another ship is refused until it unregisters: one owner of the
|
|
43
|
+
* rules, as a label has one.
|
|
44
|
+
*/
|
|
45
|
+
export declare function registerNetworkPlugin(current: NetworkSettings, change: {
|
|
46
|
+
shipId: ShipId;
|
|
47
|
+
declaration: NetworkPluginDeclaration;
|
|
48
|
+
at: Date;
|
|
49
|
+
actor: Actor;
|
|
50
|
+
}): Result<{
|
|
51
|
+
settings: NetworkSettings;
|
|
52
|
+
events: NewEvent[];
|
|
53
|
+
}, RegisterNetworkPluginRefusal>;
|
|
54
|
+
/**
|
|
55
|
+
* The fleet's plugin goes, and its rules with it: no plugin is all-to-all
|
|
56
|
+
* (decision 0035), at the next version, with NetworkPluginUnregistered. Only
|
|
57
|
+
* the plugin's own ship unregisters it; its retirement does too.
|
|
58
|
+
*/
|
|
59
|
+
export declare function unregisterNetworkPlugin(current: NetworkSettings, change: {
|
|
60
|
+
shipId: ShipId;
|
|
61
|
+
at: Date;
|
|
62
|
+
actor: Actor;
|
|
63
|
+
}): Result<{
|
|
64
|
+
settings: NetworkSettings;
|
|
65
|
+
events: NewEvent[];
|
|
66
|
+
}, NotTheNetworkPlugin>;
|
|
67
|
+
/**
|
|
68
|
+
* The fleet's rules from now on, replacing the ones it had, at the next
|
|
69
|
+
* version, with NetworkRulesSet. The rules are within the decision's limits:
|
|
70
|
+
* at most 200, each selector at most 20 label values, as a ship carries no
|
|
71
|
+
* more, each once. A value id need not exist: an unknown one matches no ship,
|
|
72
|
+
* as in label selection. While a networking plugin is registered, only its
|
|
73
|
+
* ship sets them (decision 0035).
|
|
74
|
+
*/
|
|
75
|
+
export declare function setNetworkRules(current: NetworkSettings, change: {
|
|
76
|
+
rules: readonly NetworkRule[] | null;
|
|
77
|
+
shipId: ShipId;
|
|
78
|
+
at: Date;
|
|
79
|
+
actor: Actor;
|
|
80
|
+
}): Result<{
|
|
81
|
+
settings: NetworkSettings;
|
|
82
|
+
events: NewEvent[];
|
|
83
|
+
}, SetNetworkRulesRefusal>;
|
|
84
|
+
/**
|
|
85
|
+
* Whether the plugin responds at `now` (decision 0035): its ship holds a
|
|
86
|
+
* lease whose crew called the fleet no longer ago than it declared. Its last
|
|
87
|
+
* call is the lease's last seen; nothing else acts on it, and nothing here
|
|
88
|
+
* ends the lease.
|
|
89
|
+
*/
|
|
90
|
+
export declare function isNetworkPluginResponding(plugin: NetworkPlugin, crew: {
|
|
91
|
+
lastSeenAt: Date | undefined;
|
|
92
|
+
now: Date;
|
|
93
|
+
}): boolean;
|
|
94
|
+
/** The rules a send is checked against, and what the plugin declared when it is the reason. */
|
|
95
|
+
export interface RulesInForce {
|
|
96
|
+
rules: readonly NetworkRule[] | null;
|
|
97
|
+
whilePluginUnavailable: Exclude<WhileUnavailable, 'open-all'> | null;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The rules in force: the settings' own, unless the fleet's networking plugin
|
|
101
|
+
* is not responding. Then what it declared applies: block-all leaves only
|
|
102
|
+
* the fixed exceptions, open-all none (all-to-all), keep-latest the rules it
|
|
103
|
+
* supplied last.
|
|
104
|
+
*/
|
|
105
|
+
export declare function rulesInForce(settings: NetworkSettings, isPluginResponding: boolean): RulesInForce;
|
|
106
|
+
/** A ship as a send's check sees it: its kind, for the argo exception, and the label values it carries. */
|
|
107
|
+
export interface ReachingShip {
|
|
108
|
+
kind: ShipKind;
|
|
109
|
+
labels: readonly LabelValueId[];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Whether the settings let the sender reach the recipient. With no rules,
|
|
113
|
+
* every ship reaches every ship. The fixed exceptions hold whatever the
|
|
114
|
+
* rules: argo reaches every ship and every ship reaches argo, and a ship
|
|
115
|
+
* answers the sender of a message it received. Otherwise a rule must match
|
|
116
|
+
* the sender with its `from` and the recipient with its `to`.
|
|
117
|
+
*/
|
|
118
|
+
export declare function allowsReach(settings: Pick<NetworkSettings, 'rules'>, send: {
|
|
119
|
+
sender: ReachingShip;
|
|
120
|
+
recipient: ReachingShip;
|
|
121
|
+
isAnswerToSender: boolean;
|
|
122
|
+
}): boolean;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fleet's network settings (decision 0034): which ships may send to
|
|
3
|
+
* which. A rule says that the ships matching one label selector may send to
|
|
4
|
+
* the ships matching another; allow-only, no deny rules, no precedence. A
|
|
5
|
+
* fleet holds its rules, or none (all-to-all), with a version that moves on
|
|
6
|
+
* every set, so a refusal names the settings that refused it.
|
|
7
|
+
*/
|
|
8
|
+
import { NETWORK_RULES_MAX, SHIP_LABELS_MAX } from '@aeolus-fleet/common';
|
|
9
|
+
import { refuse } from '../shared/errors.js';
|
|
10
|
+
import { ok } from '../shared/result.js';
|
|
11
|
+
/** A fleet that never set rules: none, at version 0, without a plugin. */
|
|
12
|
+
export function noNetworkSettings(fleetId) {
|
|
13
|
+
return { fleetId, rules: null, version: 0, plugin: null };
|
|
14
|
+
}
|
|
15
|
+
function refuseNotThePlugin() {
|
|
16
|
+
return refuse('NOT_THE_NETWORK_PLUGIN', "Only the fleet's networking plugin does this (decision 0035)");
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The ship becomes the fleet's networking plugin, with what it declared, at
|
|
20
|
+
* the next version, with NetworkPluginRegistered. The rules stay until it
|
|
21
|
+
* supplies its own. The plugin may register again, replacing what it
|
|
22
|
+
* declared; another ship is refused until it unregisters: one owner of the
|
|
23
|
+
* rules, as a label has one.
|
|
24
|
+
*/
|
|
25
|
+
export function registerNetworkPlugin(current, change) {
|
|
26
|
+
const { shipId, declaration } = change;
|
|
27
|
+
if (current.plugin !== null && current.plugin.shipId !== shipId) {
|
|
28
|
+
return refuse('NETWORK_PLUGIN_REGISTERED', 'The fleet already has a networking plugin; it unregisters first (decision 0035)');
|
|
29
|
+
}
|
|
30
|
+
const settings = {
|
|
31
|
+
...current,
|
|
32
|
+
version: current.version + 1,
|
|
33
|
+
plugin: { shipId, whileUnavailable: declaration.whileUnavailable, notRespondingAfterSeconds: declaration.notRespondingAfterSeconds },
|
|
34
|
+
};
|
|
35
|
+
return ok({
|
|
36
|
+
settings,
|
|
37
|
+
events: [
|
|
38
|
+
{
|
|
39
|
+
type: 'NetworkPluginRegistered',
|
|
40
|
+
fleetId: current.fleetId,
|
|
41
|
+
occurredAt: change.at,
|
|
42
|
+
actor: change.actor,
|
|
43
|
+
shipId,
|
|
44
|
+
details: { version: settings.version, whileUnavailable: declaration.whileUnavailable, notRespondingAfterSeconds: declaration.notRespondingAfterSeconds },
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The fleet's plugin goes, and its rules with it: no plugin is all-to-all
|
|
51
|
+
* (decision 0035), at the next version, with NetworkPluginUnregistered. Only
|
|
52
|
+
* the plugin's own ship unregisters it; its retirement does too.
|
|
53
|
+
*/
|
|
54
|
+
export function unregisterNetworkPlugin(current, change) {
|
|
55
|
+
if (current.plugin?.shipId !== change.shipId) {
|
|
56
|
+
return refuseNotThePlugin();
|
|
57
|
+
}
|
|
58
|
+
const settings = { fleetId: current.fleetId, rules: null, version: current.version + 1, plugin: null };
|
|
59
|
+
return ok({
|
|
60
|
+
settings,
|
|
61
|
+
events: [
|
|
62
|
+
{
|
|
63
|
+
type: 'NetworkPluginUnregistered',
|
|
64
|
+
fleetId: current.fleetId,
|
|
65
|
+
occurredAt: change.at,
|
|
66
|
+
actor: change.actor,
|
|
67
|
+
shipId: change.shipId,
|
|
68
|
+
details: { version: settings.version },
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The fleet's rules from now on, replacing the ones it had, at the next
|
|
75
|
+
* version, with NetworkRulesSet. The rules are within the decision's limits:
|
|
76
|
+
* at most 200, each selector at most 20 label values, as a ship carries no
|
|
77
|
+
* more, each once. A value id need not exist: an unknown one matches no ship,
|
|
78
|
+
* as in label selection. While a networking plugin is registered, only its
|
|
79
|
+
* ship sets them (decision 0035).
|
|
80
|
+
*/
|
|
81
|
+
export function setNetworkRules(current, change) {
|
|
82
|
+
const { rules } = change;
|
|
83
|
+
if (current.plugin !== null && current.plugin.shipId !== change.shipId) {
|
|
84
|
+
return refuseNotThePlugin();
|
|
85
|
+
}
|
|
86
|
+
if (rules !== null) {
|
|
87
|
+
const invalid = invalidRules(rules);
|
|
88
|
+
if (invalid !== undefined) {
|
|
89
|
+
return refuse('INVALID_NETWORK_RULES', `${invalid} (decision 0034)`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const settings = { ...current, rules: rules?.map(copyOfRule) ?? null, version: current.version + 1 };
|
|
93
|
+
return ok({
|
|
94
|
+
settings,
|
|
95
|
+
events: [
|
|
96
|
+
{
|
|
97
|
+
type: 'NetworkRulesSet',
|
|
98
|
+
fleetId: current.fleetId,
|
|
99
|
+
occurredAt: change.at,
|
|
100
|
+
actor: change.actor,
|
|
101
|
+
details: { version: settings.version, rules: rules?.length ?? null },
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/** What breaks the limits, said for the caller, or undefined when the rules keep them. */
|
|
107
|
+
function invalidRules(rules) {
|
|
108
|
+
if (rules.length > NETWORK_RULES_MAX) {
|
|
109
|
+
return `A fleet holds at most ${String(NETWORK_RULES_MAX)} network rules`;
|
|
110
|
+
}
|
|
111
|
+
for (const selector of rules.flatMap((rule) => [rule.from, rule.to])) {
|
|
112
|
+
if (selector.length > SHIP_LABELS_MAX) {
|
|
113
|
+
return `A selector holds at most ${String(SHIP_LABELS_MAX)} label values, as a ship carries no more`;
|
|
114
|
+
}
|
|
115
|
+
const twice = selector.find((valueId, index) => selector.indexOf(valueId) !== index);
|
|
116
|
+
if (twice !== undefined) {
|
|
117
|
+
return `A selector names each label value once: ${twice} twice`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
function copyOfRule(rule) {
|
|
123
|
+
return { from: [...rule.from], to: [...rule.to] };
|
|
124
|
+
}
|
|
125
|
+
const MILLISECONDS_PER_SECOND = 1000;
|
|
126
|
+
/**
|
|
127
|
+
* Whether the plugin responds at `now` (decision 0035): its ship holds a
|
|
128
|
+
* lease whose crew called the fleet no longer ago than it declared. Its last
|
|
129
|
+
* call is the lease's last seen; nothing else acts on it, and nothing here
|
|
130
|
+
* ends the lease.
|
|
131
|
+
*/
|
|
132
|
+
export function isNetworkPluginResponding(plugin, crew) {
|
|
133
|
+
const { lastSeenAt, now } = crew;
|
|
134
|
+
return lastSeenAt !== undefined && now.getTime() - lastSeenAt.getTime() <= plugin.notRespondingAfterSeconds * MILLISECONDS_PER_SECOND;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The rules in force: the settings' own, unless the fleet's networking plugin
|
|
138
|
+
* is not responding. Then what it declared applies: block-all leaves only
|
|
139
|
+
* the fixed exceptions, open-all none (all-to-all), keep-latest the rules it
|
|
140
|
+
* supplied last.
|
|
141
|
+
*/
|
|
142
|
+
export function rulesInForce(settings, isPluginResponding) {
|
|
143
|
+
const { plugin, rules } = settings;
|
|
144
|
+
if (plugin === null || isPluginResponding) {
|
|
145
|
+
return { rules, whilePluginUnavailable: null };
|
|
146
|
+
}
|
|
147
|
+
switch (plugin.whileUnavailable) {
|
|
148
|
+
case 'block-all':
|
|
149
|
+
return { rules: [], whilePluginUnavailable: 'block-all' };
|
|
150
|
+
case 'open-all':
|
|
151
|
+
return { rules: null, whilePluginUnavailable: null };
|
|
152
|
+
case 'keep-latest':
|
|
153
|
+
return { rules, whilePluginUnavailable: 'keep-latest' };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Whether the settings let the sender reach the recipient. With no rules,
|
|
158
|
+
* every ship reaches every ship. The fixed exceptions hold whatever the
|
|
159
|
+
* rules: argo reaches every ship and every ship reaches argo, and a ship
|
|
160
|
+
* answers the sender of a message it received. Otherwise a rule must match
|
|
161
|
+
* the sender with its `from` and the recipient with its `to`.
|
|
162
|
+
*/
|
|
163
|
+
export function allowsReach(settings, send) {
|
|
164
|
+
const { sender, recipient, isAnswerToSender } = send;
|
|
165
|
+
if (settings.rules === null || sender.kind === 'operator' || recipient.kind === 'operator' || isAnswerToSender) {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
return settings.rules.some((rule) => matches(rule.from, sender) && matches(rule.to, recipient));
|
|
169
|
+
}
|
|
170
|
+
/** A ship matches a selector when it carries every value in it: exact matches, combined with AND. */
|
|
171
|
+
function matches(selector, ship) {
|
|
172
|
+
return selector.every((valueId) => ship.labels.includes(valueId));
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=network-settings.js.map
|