@aephia/atlas-kit 0.6.0-next.101 → 0.6.0-next.103
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/dist/internal/planning/arrival-guards.d.ts +0 -1
- package/dist/internal/planning/arrival-guards.d.ts.map +1 -1
- package/dist/internal/planning/arrival-guards.js +0 -16
- package/dist/internal/planning/arrival-guards.js.map +1 -1
- package/dist/internal/planning/fleet-dock-funding-guard.d.ts +3 -3
- package/dist/internal/planning/fleet-dock-funding-guard.d.ts.map +1 -1
- package/dist/internal/planning/fleet-dock-funding-guard.js +76 -93
- package/dist/internal/planning/fleet-dock-funding-guard.js.map +1 -1
- package/dist/internal/planning/local-market-order.d.ts +4 -4
- package/dist/internal/planning/local-market-order.d.ts.map +1 -1
- package/dist/internal/planning/local-market-order.js +80 -108
- package/dist/internal/planning/local-market-order.js.map +1 -1
- package/dist/internal/planning/plan-fields.d.ts +59 -0
- package/dist/internal/planning/plan-fields.d.ts.map +1 -0
- package/dist/internal/planning/plan-fields.js +153 -0
- package/dist/internal/planning/plan-fields.js.map +1 -0
- package/dist/internal/planning/plan-guards.d.ts +4 -0
- package/dist/internal/planning/plan-guards.d.ts.map +1 -0
- package/dist/internal/planning/plan-guards.js +168 -0
- package/dist/internal/planning/plan-guards.js.map +1 -0
- package/dist/internal/planning/runtime-guards.d.ts +0 -2
- package/dist/internal/planning/runtime-guards.d.ts.map +1 -1
- package/dist/internal/planning/runtime-guards.js +0 -3
- package/dist/internal/planning/runtime-guards.js.map +1 -1
- package/dist/planning/assembly.d.ts.map +1 -1
- package/dist/planning/assembly.js +2 -26
- package/dist/planning/assembly.js.map +1 -1
- package/dist/planning/index.d.ts +3 -3
- package/dist/planning/index.d.ts.map +1 -1
- package/dist/planning/index.js +30 -1067
- package/dist/planning/index.js.map +1 -1
- package/dist/planning/plan-validation.d.ts +14 -0
- package/dist/planning/plan-validation.d.ts.map +1 -0
- package/dist/planning/plan-validation.js +124 -0
- package/dist/planning/plan-validation.js.map +1 -0
- package/package.json +1 -1
package/dist/planning/index.js
CHANGED
|
@@ -1,51 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { validateMovementXpInstructions } from '../internal/planning/movement-xp-guard.js';
|
|
11
|
-
import { validateFleetDockingGuards } from '../internal/planning/fleet-docking-guard.js';
|
|
12
|
-
import { validateStarbaseSystemInstructions, validateStarbaseSystemGuards, } from '../internal/planning/starbase-system-guard.js';
|
|
13
|
-
import { validateCharacterRegistrationInstructions } from '../internal/planning/character-registration-guard.js';
|
|
14
|
-
import { normalizeArrivalGuard, arrivalGuardToJSON, validateArrivalInstructions, } from '../internal/planning/arrival-guards.js';
|
|
15
|
-
import { isRuntimeGuard, normalizeRuntimeGuard, runtimeGuardToJSON, validateRuntimeInstructions, } from '../internal/planning/runtime-guards.js';
|
|
16
|
-
import { isTradeBuyGuard, isTradeSellGuard, isTradeMeterGuard, isFactionTradeGuard, } from '../internal/planning/faction-trade-guards.js';
|
|
17
|
-
import { AccountRole, getBase64Decoder, getBase64Encoder, isAddress, isWritableRole, } from '@solana/kit';
|
|
18
|
-
import { InvalidActionInputError, SageSdkError } from '../client/errors.js';
|
|
1
|
+
import {} from '../internal/planning/fleet-dock-funding-guard.js';
|
|
2
|
+
import {} from '../internal/planning/local-market-order.js';
|
|
3
|
+
import {} from '../internal/planning/scan-completion-guard.js';
|
|
4
|
+
import {} from '../internal/planning/arrival-guards.js';
|
|
5
|
+
import {} from '../internal/planning/runtime-guards.js';
|
|
6
|
+
import { assertKeys, guardToWire, invalid, readAddress, readRecord, readString, } from '../internal/planning/plan-fields.js';
|
|
7
|
+
import { normalizePrecondition } from '../internal/planning/plan-guards.js';
|
|
8
|
+
import { AccountRole, getBase64Decoder, getBase64Encoder, isWritableRole, } from '@solana/kit';
|
|
9
|
+
import { InvalidActionInputError } from '../client/errors.js';
|
|
19
10
|
import { deepFreeze } from '../internal/immutable-tree.js';
|
|
20
|
-
import { validateMiningStopInstructions } from '../internal/planning/mining-stop.js';
|
|
21
|
-
import { validatePlanComposition } from './composition.js';
|
|
22
11
|
import { isPlanInstance, registerPlanInstance } from './plan-instances.js';
|
|
12
|
+
import { validateCreationBindings, validateHydrationBindings, validatePlanBindings, } from './plan-validation.js';
|
|
23
13
|
const BASE64_PATTERN = /^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$/u;
|
|
24
|
-
const DATA_HASH_PATTERN = /^[\da-f]{64}$/u;
|
|
25
14
|
const ACCOUNT_ROLES = new Set([
|
|
26
15
|
AccountRole.READONLY,
|
|
27
16
|
AccountRole.WRITABLE,
|
|
28
17
|
AccountRole.READONLY_SIGNER,
|
|
29
18
|
AccountRole.WRITABLE_SIGNER,
|
|
30
19
|
]);
|
|
31
|
-
function invalid(field, value, acceptedRange) {
|
|
32
|
-
throw new InvalidActionInputError(field, value, acceptedRange, 'Correct this Plan field and recreate or hydrate the Plan before retrying.');
|
|
33
|
-
}
|
|
34
|
-
function readRecord(value, field) {
|
|
35
|
-
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
36
|
-
return invalid(field, value, 'a plain object');
|
|
37
|
-
}
|
|
38
|
-
return value;
|
|
39
|
-
}
|
|
40
|
-
function assertKeys(value, field, required, optional = []) {
|
|
41
|
-
const keys = Object.keys(value);
|
|
42
|
-
const accepted = new Set([...required, ...optional]);
|
|
43
|
-
const missing = required.find((key) => !Object.hasOwn(value, key));
|
|
44
|
-
const unexpected = keys.find((key) => !accepted.has(key));
|
|
45
|
-
if (missing !== undefined || unexpected !== undefined) {
|
|
46
|
-
invalid(field, missing === undefined ? unexpected : `missing ${missing}`, `exactly the fields ${[...required, ...optional].join(', ')}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
20
|
function readArray(value, field) {
|
|
50
21
|
if (!Array.isArray(value)) {
|
|
51
22
|
return invalid(field, value, 'an ordered array');
|
|
@@ -59,29 +30,16 @@ function readArray(value, field) {
|
|
|
59
30
|
}
|
|
60
31
|
return normalized;
|
|
61
32
|
}
|
|
62
|
-
function readString(value, field) {
|
|
63
|
-
return typeof value === 'string' ? value : invalid(field, value, 'a string');
|
|
64
|
-
}
|
|
65
33
|
function readToken(value, field) {
|
|
66
34
|
return typeof value === 'string' && value.length > 0
|
|
67
35
|
? value
|
|
68
36
|
: invalid(field, value, 'a non-empty string');
|
|
69
37
|
}
|
|
70
|
-
function readAddress(value, field) {
|
|
71
|
-
return typeof value === 'string' && isAddress(value)
|
|
72
|
-
? value
|
|
73
|
-
: invalid(field, value, 'a valid base58 Solana address');
|
|
74
|
-
}
|
|
75
38
|
function readRole(value, field) {
|
|
76
39
|
return typeof value === 'number' && ACCOUNT_ROLES.has(value)
|
|
77
40
|
? value
|
|
78
41
|
: invalid(field, value, 'an AccountRole integer from 0 through 3');
|
|
79
42
|
}
|
|
80
|
-
function readDataHash(value, field) {
|
|
81
|
-
return typeof value === 'string' && DATA_HASH_PATTERN.test(value)
|
|
82
|
-
? value
|
|
83
|
-
: invalid(field, value, 'a lowercase 64-character SHA-256 hex digest');
|
|
84
|
-
}
|
|
85
43
|
function decodeBase64(value, field) {
|
|
86
44
|
if (typeof value !== 'string' || !BASE64_PATTERN.test(value)) {
|
|
87
45
|
return invalid(field, value, 'canonical base64-encoded instruction bytes');
|
|
@@ -175,764 +133,6 @@ function normalizeSemanticFact(value, field) {
|
|
|
175
133
|
}
|
|
176
134
|
return invalid(`${field}.kind`, semantic.kind, 'one of exclusive, requires, invalidates, or transition');
|
|
177
135
|
}
|
|
178
|
-
function normalizePrecondition(value, field) {
|
|
179
|
-
const precondition = readRecord(value, field);
|
|
180
|
-
if (precondition.kind === 'fleet-arrival')
|
|
181
|
-
return normalizeArrivalGuard(precondition, field);
|
|
182
|
-
if (isRuntimeGuard(precondition))
|
|
183
|
-
return normalizeRuntimeGuard(precondition, field);
|
|
184
|
-
if (precondition.kind === 'character-registration') {
|
|
185
|
-
assertKeys(precondition, field, [
|
|
186
|
-
'kind',
|
|
187
|
-
'address',
|
|
188
|
-
'game',
|
|
189
|
-
'profile',
|
|
190
|
-
'profileDataHash',
|
|
191
|
-
'funder',
|
|
192
|
-
'xpBudget',
|
|
193
|
-
'xpBudgetConfig',
|
|
194
|
-
'maximumFunderDebit',
|
|
195
|
-
'characterSize',
|
|
196
|
-
'initializationHash',
|
|
197
|
-
'characterLamports',
|
|
198
|
-
'budgetLamports',
|
|
199
|
-
'describes',
|
|
200
|
-
]);
|
|
201
|
-
if (typeof precondition.characterSize !== 'number' ||
|
|
202
|
-
!Number.isSafeInteger(precondition.characterSize) ||
|
|
203
|
-
precondition.characterSize < 423)
|
|
204
|
-
invalid(`${field}.characterSize`, precondition.characterSize, 'a qualified Character allocation of at least 423 bytes');
|
|
205
|
-
for (const key of ['characterLamports', 'budgetLamports'])
|
|
206
|
-
if (typeof precondition[key] !== 'bigint' || precondition[key] < 0n)
|
|
207
|
-
invalid(`${field}.${key}`, precondition[key], 'a nonnegative bigint');
|
|
208
|
-
const maximumFunderDebit = precondition.maximumFunderDebit;
|
|
209
|
-
if (typeof maximumFunderDebit !== 'bigint' || maximumFunderDebit < 0n)
|
|
210
|
-
invalid(`${field}.maximumFunderDebit`, maximumFunderDebit, 'a nonnegative bigint');
|
|
211
|
-
return Object.freeze({
|
|
212
|
-
kind: 'character-registration',
|
|
213
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
214
|
-
game: readAddress(precondition.game, `${field}.game`),
|
|
215
|
-
profile: readAddress(precondition.profile, `${field}.profile`),
|
|
216
|
-
profileDataHash: readDataHash(precondition.profileDataHash, `${field}.profileDataHash`),
|
|
217
|
-
funder: readAddress(precondition.funder, `${field}.funder`),
|
|
218
|
-
xpBudget: readAddress(precondition.xpBudget, `${field}.xpBudget`),
|
|
219
|
-
xpBudgetConfig: readAddress(precondition.xpBudgetConfig, `${field}.xpBudgetConfig`),
|
|
220
|
-
maximumFunderDebit,
|
|
221
|
-
characterLamports: precondition.characterLamports,
|
|
222
|
-
budgetLamports: precondition.budgetLamports,
|
|
223
|
-
characterSize: precondition.characterSize,
|
|
224
|
-
initializationHash: readDataHash(precondition.initializationHash, `${field}.initializationHash`),
|
|
225
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
if (precondition.kind === 'ship-import-funding') {
|
|
229
|
-
assertKeys(precondition, field, [
|
|
230
|
-
'kind',
|
|
231
|
-
'address',
|
|
232
|
-
'game',
|
|
233
|
-
'profile',
|
|
234
|
-
'system',
|
|
235
|
-
'shipId',
|
|
236
|
-
'maximumProfileDebit',
|
|
237
|
-
'describes',
|
|
238
|
-
]);
|
|
239
|
-
const shipId = precondition.shipId;
|
|
240
|
-
if (typeof shipId !== 'number' ||
|
|
241
|
-
!Number.isInteger(shipId) ||
|
|
242
|
-
shipId < 0 ||
|
|
243
|
-
shipId > 65535)
|
|
244
|
-
invalid(`${field}.shipId`, shipId, 'an unsigned u16 ship configuration ID');
|
|
245
|
-
const maximumProfileDebit = precondition.maximumProfileDebit;
|
|
246
|
-
if (typeof maximumProfileDebit !== 'bigint' || maximumProfileDebit < 0n)
|
|
247
|
-
invalid(`${field}.maximumProfileDebit`, maximumProfileDebit, 'a nonnegative bigint');
|
|
248
|
-
return Object.freeze({
|
|
249
|
-
kind: 'ship-import-funding',
|
|
250
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
251
|
-
game: readAddress(precondition.game, `${field}.game`),
|
|
252
|
-
profile: readAddress(precondition.profile, `${field}.profile`),
|
|
253
|
-
system: readAddress(precondition.system, `${field}.system`),
|
|
254
|
-
shipId,
|
|
255
|
-
maximumProfileDebit,
|
|
256
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
if (precondition.kind === 'fleet-attack') {
|
|
260
|
-
assertKeys(precondition, field, [
|
|
261
|
-
'kind',
|
|
262
|
-
'address',
|
|
263
|
-
'defender',
|
|
264
|
-
'game',
|
|
265
|
-
'profile',
|
|
266
|
-
'defenderProfile',
|
|
267
|
-
'instructionKey',
|
|
268
|
-
'describes',
|
|
269
|
-
]);
|
|
270
|
-
return Object.freeze({
|
|
271
|
-
kind: 'fleet-attack',
|
|
272
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
273
|
-
defender: readAddress(precondition.defender, `${field}.defender`),
|
|
274
|
-
game: readAddress(precondition.game, `${field}.game`),
|
|
275
|
-
profile: readAddress(precondition.profile, `${field}.profile`),
|
|
276
|
-
defenderProfile: readAddress(precondition.defenderProfile, `${field}.defenderProfile`),
|
|
277
|
-
instructionKey: readString(precondition.instructionKey, `${field}.instructionKey`),
|
|
278
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
if (precondition.kind === 'currency-cache') {
|
|
282
|
-
assertKeys(precondition, field, ['kind', 'address', 'game', 'describes']);
|
|
283
|
-
return Object.freeze({
|
|
284
|
-
kind: 'currency-cache',
|
|
285
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
286
|
-
game: readAddress(precondition.game, `${field}.game`),
|
|
287
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
if (precondition.kind === 'movement-character') {
|
|
291
|
-
assertKeys(precondition, field, [
|
|
292
|
-
'kind',
|
|
293
|
-
'address',
|
|
294
|
-
'game',
|
|
295
|
-
'profile',
|
|
296
|
-
'stateHash',
|
|
297
|
-
'describes',
|
|
298
|
-
]);
|
|
299
|
-
return Object.freeze({
|
|
300
|
-
kind: 'movement-character',
|
|
301
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
302
|
-
game: readAddress(precondition.game, `${field}.game`),
|
|
303
|
-
profile: readAddress(precondition.profile, `${field}.profile`),
|
|
304
|
-
stateHash: readDataHash(precondition.stateHash, `${field}.stateHash`),
|
|
305
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
if (precondition.kind === 'fleet-docking-policy') {
|
|
309
|
-
assertKeys(precondition, field, [
|
|
310
|
-
'kind',
|
|
311
|
-
'address',
|
|
312
|
-
'game',
|
|
313
|
-
'system',
|
|
314
|
-
'ownership',
|
|
315
|
-
'describes',
|
|
316
|
-
]);
|
|
317
|
-
return Object.freeze({
|
|
318
|
-
kind: 'fleet-docking-policy',
|
|
319
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
320
|
-
game: readAddress(precondition.game, `${field}.game`),
|
|
321
|
-
system: readAddress(precondition.system, `${field}.system`),
|
|
322
|
-
ownership: readAddress(precondition.ownership, `${field}.ownership`),
|
|
323
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
if (precondition.kind === 'starbase-system') {
|
|
327
|
-
assertKeys(precondition, field, ['kind', 'address', 'game', 'starbaseSequenceId', 'describes'], ['requireCss']);
|
|
328
|
-
if (precondition.requireCss !== undefined &&
|
|
329
|
-
precondition.requireCss !== true)
|
|
330
|
-
invalid(`${field}.requireCss`, precondition.requireCss, 'true when CSS access is required, otherwise omitted');
|
|
331
|
-
const sequence = precondition.starbaseSequenceId;
|
|
332
|
-
if (typeof sequence !== 'bigint' ||
|
|
333
|
-
sequence < 0n ||
|
|
334
|
-
sequence > 18446744073709551615n)
|
|
335
|
-
invalid(`${field}.starbaseSequenceId`, sequence, 'an unsigned u64 Starbase sequence');
|
|
336
|
-
return Object.freeze({
|
|
337
|
-
kind: 'starbase-system',
|
|
338
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
339
|
-
game: readAddress(precondition.game, `${field}.game`),
|
|
340
|
-
starbaseSequenceId: sequence,
|
|
341
|
-
...(precondition.requireCss === undefined
|
|
342
|
-
? {}
|
|
343
|
-
: { requireCss: true }),
|
|
344
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
if (precondition.kind === 'fleet-mining') {
|
|
348
|
-
assertKeys(precondition, field, [
|
|
349
|
-
'kind',
|
|
350
|
-
'address',
|
|
351
|
-
'game',
|
|
352
|
-
'profile',
|
|
353
|
-
'asteroid',
|
|
354
|
-
'describes',
|
|
355
|
-
]);
|
|
356
|
-
return Object.freeze({
|
|
357
|
-
kind: 'fleet-mining',
|
|
358
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
359
|
-
game: readAddress(precondition.game, `${field}.game`),
|
|
360
|
-
profile: readAddress(precondition.profile, `${field}.profile`),
|
|
361
|
-
asteroid: readAddress(precondition.asteroid, `${field}.asteroid`),
|
|
362
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
if (isFactionTradeGuard(precondition)) {
|
|
366
|
-
const kind = precondition.kind;
|
|
367
|
-
assertKeys(precondition, field, [
|
|
368
|
-
'kind',
|
|
369
|
-
'address',
|
|
370
|
-
'game',
|
|
371
|
-
'factionId',
|
|
372
|
-
'describes',
|
|
373
|
-
'schemaVersion',
|
|
374
|
-
...(isTradeBuyGuard(precondition)
|
|
375
|
-
? ['cargoId', 'floorRaw']
|
|
376
|
-
: isTradeSellGuard(precondition)
|
|
377
|
-
? ['atlasOutCapPerEpochRaw', 'epochLengthSeconds']
|
|
378
|
-
: []),
|
|
379
|
-
]);
|
|
380
|
-
const unsigned = (key, max = 65535) => {
|
|
381
|
-
const n = precondition[key];
|
|
382
|
-
if (typeof n !== 'number' || !Number.isInteger(n) || n < 0 || n > max)
|
|
383
|
-
invalid(`${field}.${key}`, n, `an unsigned ${max === 255 ? 'u8' : 'u16'} integer`);
|
|
384
|
-
return n;
|
|
385
|
-
};
|
|
386
|
-
const integer = (key, signed = false) => {
|
|
387
|
-
const n = precondition[key];
|
|
388
|
-
const min = signed ? -(1n << 63n) : 0n;
|
|
389
|
-
const max = signed ? (1n << 63n) - 1n : (1n << 64n) - 1n;
|
|
390
|
-
if (typeof n !== 'bigint' || n < min || n > max)
|
|
391
|
-
invalid(`${field}.${key}`, n, signed ? 'an i64 bigint' : 'a u64 bigint');
|
|
392
|
-
return n;
|
|
393
|
-
};
|
|
394
|
-
const common = {
|
|
395
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
396
|
-
game: readAddress(precondition.game, `${field}.game`),
|
|
397
|
-
factionId: unsigned('factionId'),
|
|
398
|
-
schemaVersion: unsigned('schemaVersion', 255),
|
|
399
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
400
|
-
};
|
|
401
|
-
if (isTradeBuyGuard(precondition))
|
|
402
|
-
return Object.freeze({
|
|
403
|
-
kind,
|
|
404
|
-
...common,
|
|
405
|
-
cargoId: unsigned('cargoId'),
|
|
406
|
-
floorRaw: integer('floorRaw'),
|
|
407
|
-
});
|
|
408
|
-
if (isTradeSellGuard(precondition))
|
|
409
|
-
return Object.freeze({
|
|
410
|
-
kind,
|
|
411
|
-
...common,
|
|
412
|
-
atlasOutCapPerEpochRaw: integer('atlasOutCapPerEpochRaw'),
|
|
413
|
-
epochLengthSeconds: integer('epochLengthSeconds', true),
|
|
414
|
-
});
|
|
415
|
-
return Object.freeze({ kind, ...common });
|
|
416
|
-
}
|
|
417
|
-
if (precondition.kind === 'fleet-add-ship') {
|
|
418
|
-
assertKeys(precondition, field, [
|
|
419
|
-
'kind',
|
|
420
|
-
'address',
|
|
421
|
-
'game',
|
|
422
|
-
'profile',
|
|
423
|
-
'authority',
|
|
424
|
-
'system',
|
|
425
|
-
'shipId',
|
|
426
|
-
'amount',
|
|
427
|
-
'keyIndex',
|
|
428
|
-
'configHash',
|
|
429
|
-
'maximumProfileDebit',
|
|
430
|
-
'describes',
|
|
431
|
-
]);
|
|
432
|
-
const keyIndex = precondition.keyIndex;
|
|
433
|
-
if (typeof keyIndex !== 'number' ||
|
|
434
|
-
!Number.isInteger(keyIndex) ||
|
|
435
|
-
keyIndex < 0 ||
|
|
436
|
-
keyIndex > 65535)
|
|
437
|
-
invalid(`${field}.keyIndex`, keyIndex, 'an integer from 0 through 65535');
|
|
438
|
-
const shipId = precondition.shipId;
|
|
439
|
-
if (typeof shipId !== 'number' ||
|
|
440
|
-
!Number.isInteger(shipId) ||
|
|
441
|
-
shipId < 0 ||
|
|
442
|
-
shipId > 65535)
|
|
443
|
-
invalid(`${field}.shipId`, shipId, 'a u16 ship configuration ID');
|
|
444
|
-
const amount = precondition.amount;
|
|
445
|
-
if (typeof amount !== 'bigint' || amount < 1n || amount > 255n)
|
|
446
|
-
invalid(`${field}.amount`, amount, 'a whole ship quantity from 1n through 255n');
|
|
447
|
-
const maximumProfileDebit = precondition.maximumProfileDebit;
|
|
448
|
-
if (typeof maximumProfileDebit !== 'bigint' || maximumProfileDebit < 0n)
|
|
449
|
-
invalid(`${field}.maximumProfileDebit`, maximumProfileDebit, 'a nonnegative bigint');
|
|
450
|
-
return Object.freeze({
|
|
451
|
-
kind: 'fleet-add-ship',
|
|
452
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
453
|
-
game: readAddress(precondition.game, `${field}.game`),
|
|
454
|
-
profile: readAddress(precondition.profile, `${field}.profile`),
|
|
455
|
-
authority: readAddress(precondition.authority, `${field}.authority`),
|
|
456
|
-
system: readAddress(precondition.system, `${field}.system`),
|
|
457
|
-
keyIndex,
|
|
458
|
-
shipId,
|
|
459
|
-
amount,
|
|
460
|
-
maximumProfileDebit,
|
|
461
|
-
configHash: readDataHash(precondition.configHash, `${field}.configHash`),
|
|
462
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
if (precondition.kind === 'scan-completion') {
|
|
466
|
-
const addresses = [
|
|
467
|
-
'address',
|
|
468
|
-
'game',
|
|
469
|
-
'signal',
|
|
470
|
-
'profile',
|
|
471
|
-
'character',
|
|
472
|
-
'authority',
|
|
473
|
-
'policy',
|
|
474
|
-
'loot',
|
|
475
|
-
'xpBudget',
|
|
476
|
-
'xpConfig',
|
|
477
|
-
];
|
|
478
|
-
assertKeys(precondition, field, [
|
|
479
|
-
'kind',
|
|
480
|
-
'operation',
|
|
481
|
-
...addresses,
|
|
482
|
-
'keyIndex',
|
|
483
|
-
'status',
|
|
484
|
-
'expectedSequence',
|
|
485
|
-
'configHash',
|
|
486
|
-
'describes',
|
|
487
|
-
]);
|
|
488
|
-
if (typeof precondition.operation !== 'string' ||
|
|
489
|
-
!Object.hasOwn(scanCompletionDiscriminators, precondition.operation))
|
|
490
|
-
invalid(field, precondition.operation, 'a supported scan lifecycle operation');
|
|
491
|
-
const keyIndex = precondition.keyIndex, status = precondition.status, sequence = precondition.expectedSequence;
|
|
492
|
-
if (typeof keyIndex !== 'number' ||
|
|
493
|
-
!Number.isInteger(keyIndex) ||
|
|
494
|
-
keyIndex < 0 ||
|
|
495
|
-
keyIndex > 65535)
|
|
496
|
-
invalid(field, keyIndex, 'a u16 key index');
|
|
497
|
-
if (typeof status !== 'number' ||
|
|
498
|
-
!Number.isInteger(status) ||
|
|
499
|
-
status < 1 ||
|
|
500
|
-
status > 6)
|
|
501
|
-
invalid(field, status, 'a current nonempty signal state');
|
|
502
|
-
if (typeof sequence !== 'bigint' ||
|
|
503
|
-
sequence < 0n ||
|
|
504
|
-
sequence > 18446744073709551615n)
|
|
505
|
-
invalid(field, sequence, 'a u64 sequence');
|
|
506
|
-
const parsed = Object.fromEntries(addresses.map((k) => [k, readAddress(precondition[k], `${field}.${k}`)]));
|
|
507
|
-
return Object.freeze({
|
|
508
|
-
kind: 'scan-completion',
|
|
509
|
-
operation: precondition.operation,
|
|
510
|
-
...parsed,
|
|
511
|
-
keyIndex,
|
|
512
|
-
status,
|
|
513
|
-
expectedSequence: sequence,
|
|
514
|
-
configHash: readDataHash(precondition.configHash, field),
|
|
515
|
-
describes: readString(precondition.describes, field),
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
if (precondition.kind === 'scan-detect') {
|
|
519
|
-
assertKeys(precondition, field, [
|
|
520
|
-
'kind',
|
|
521
|
-
'address',
|
|
522
|
-
'game',
|
|
523
|
-
'profile',
|
|
524
|
-
'authority',
|
|
525
|
-
'character',
|
|
526
|
-
'signal',
|
|
527
|
-
'pattern',
|
|
528
|
-
'policy',
|
|
529
|
-
'loot',
|
|
530
|
-
'tracker',
|
|
531
|
-
'keyIndex',
|
|
532
|
-
'patternId',
|
|
533
|
-
'focus',
|
|
534
|
-
'expectedSequence',
|
|
535
|
-
'policyRevision',
|
|
536
|
-
'profileRevision',
|
|
537
|
-
'configHash',
|
|
538
|
-
'describes',
|
|
539
|
-
]);
|
|
540
|
-
const numbers = {};
|
|
541
|
-
for (const [key, max] of [
|
|
542
|
-
['keyIndex', 65535],
|
|
543
|
-
['patternId', 65535],
|
|
544
|
-
['focus', 4],
|
|
545
|
-
['policyRevision', 4294967295],
|
|
546
|
-
['profileRevision', 4294967295],
|
|
547
|
-
]) {
|
|
548
|
-
const value = precondition[key];
|
|
549
|
-
if (typeof value !== 'number' ||
|
|
550
|
-
!Number.isInteger(value) ||
|
|
551
|
-
value < 0 ||
|
|
552
|
-
value > max)
|
|
553
|
-
invalid(`${field}.${key}`, value, `an integer from zero through ${max}`);
|
|
554
|
-
numbers[key] = value;
|
|
555
|
-
}
|
|
556
|
-
const sequence = precondition.expectedSequence;
|
|
557
|
-
if (typeof sequence !== 'bigint' ||
|
|
558
|
-
sequence < 0n ||
|
|
559
|
-
sequence > 18446744073709551615n)
|
|
560
|
-
invalid(`${field}.expectedSequence`, sequence, 'a u64 bigint');
|
|
561
|
-
return Object.freeze({
|
|
562
|
-
kind: 'scan-detect',
|
|
563
|
-
address: readAddress(precondition.address, field),
|
|
564
|
-
game: readAddress(precondition.game, field),
|
|
565
|
-
profile: readAddress(precondition.profile, field),
|
|
566
|
-
authority: readAddress(precondition.authority, field),
|
|
567
|
-
character: readAddress(precondition.character, field),
|
|
568
|
-
signal: readAddress(precondition.signal, field),
|
|
569
|
-
pattern: readAddress(precondition.pattern, field),
|
|
570
|
-
policy: readAddress(precondition.policy, field),
|
|
571
|
-
loot: readAddress(precondition.loot, field),
|
|
572
|
-
tracker: readAddress(precondition.tracker, field),
|
|
573
|
-
keyIndex: numbers.keyIndex,
|
|
574
|
-
patternId: numbers.patternId,
|
|
575
|
-
focus: numbers.focus,
|
|
576
|
-
policyRevision: numbers.policyRevision,
|
|
577
|
-
profileRevision: numbers.profileRevision,
|
|
578
|
-
expectedSequence: sequence,
|
|
579
|
-
configHash: readDataHash(precondition.configHash, field),
|
|
580
|
-
describes: readString(precondition.describes, field),
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
if (precondition.kind === 'fleet-crew-transfer') {
|
|
584
|
-
assertKeys(precondition, field, [
|
|
585
|
-
'kind',
|
|
586
|
-
'direction',
|
|
587
|
-
'address',
|
|
588
|
-
'game',
|
|
589
|
-
'profile',
|
|
590
|
-
'authority',
|
|
591
|
-
'system',
|
|
592
|
-
'starbasePlayer',
|
|
593
|
-
'keyIndex',
|
|
594
|
-
'count',
|
|
595
|
-
'systemSequenceId',
|
|
596
|
-
'describes',
|
|
597
|
-
]);
|
|
598
|
-
for (const key of ['count', 'keyIndex']) {
|
|
599
|
-
const n = precondition[key];
|
|
600
|
-
if (typeof n !== 'number' ||
|
|
601
|
-
!Number.isInteger(n) ||
|
|
602
|
-
n < (key === 'count' ? 1 : 0) ||
|
|
603
|
-
n > 65535)
|
|
604
|
-
invalid(field, n, 'a positive u16 count and u16 key index');
|
|
605
|
-
}
|
|
606
|
-
const direction = precondition.direction, sequence = precondition.systemSequenceId;
|
|
607
|
-
if (direction !== 'load' && direction !== 'unload')
|
|
608
|
-
invalid(field, direction, 'load or unload');
|
|
609
|
-
if (typeof sequence !== 'bigint' ||
|
|
610
|
-
sequence < 0n ||
|
|
611
|
-
sequence > (1n << 64n) - 1n)
|
|
612
|
-
invalid(field, sequence, 'a u64 System generation');
|
|
613
|
-
return Object.freeze({
|
|
614
|
-
kind: 'fleet-crew-transfer',
|
|
615
|
-
direction,
|
|
616
|
-
address: readAddress(precondition.address, field),
|
|
617
|
-
game: readAddress(precondition.game, field),
|
|
618
|
-
profile: readAddress(precondition.profile, field),
|
|
619
|
-
authority: readAddress(precondition.authority, field),
|
|
620
|
-
system: readAddress(precondition.system, field),
|
|
621
|
-
starbasePlayer: readAddress(precondition.starbasePlayer, field),
|
|
622
|
-
count: precondition.count,
|
|
623
|
-
keyIndex: precondition.keyIndex,
|
|
624
|
-
systemSequenceId: sequence,
|
|
625
|
-
describes: readString(precondition.describes, field),
|
|
626
|
-
});
|
|
627
|
-
}
|
|
628
|
-
if (precondition.kind === 'fleet-respawn') {
|
|
629
|
-
assertKeys(precondition, field, [
|
|
630
|
-
'kind',
|
|
631
|
-
'address',
|
|
632
|
-
'game',
|
|
633
|
-
'profile',
|
|
634
|
-
'authority',
|
|
635
|
-
'system',
|
|
636
|
-
'keyIndex',
|
|
637
|
-
'phase',
|
|
638
|
-
'fleetHash',
|
|
639
|
-
'maximumRecoveryFee',
|
|
640
|
-
'maximumProfileDebit',
|
|
641
|
-
'describes',
|
|
642
|
-
]);
|
|
643
|
-
const keyIndex = precondition.keyIndex;
|
|
644
|
-
if (typeof keyIndex !== 'number' ||
|
|
645
|
-
!Number.isInteger(keyIndex) ||
|
|
646
|
-
keyIndex < 0 ||
|
|
647
|
-
keyIndex > 65535)
|
|
648
|
-
invalid(`${field}.keyIndex`, keyIndex, 'a u16 key index');
|
|
649
|
-
const phase = precondition.phase;
|
|
650
|
-
if (phase !== 'start' && phase !== 'complete')
|
|
651
|
-
invalid(`${field}.phase`, phase, 'start or complete');
|
|
652
|
-
const maximumRecoveryFee = precondition.maximumRecoveryFee, maximumProfileDebit = precondition.maximumProfileDebit;
|
|
653
|
-
for (const value of [maximumRecoveryFee, maximumProfileDebit])
|
|
654
|
-
if (typeof value !== 'bigint' || value < 0n || value > (1n << 64n) - 1n)
|
|
655
|
-
invalid(field, value, 'a nonnegative u64 funding ceiling');
|
|
656
|
-
return Object.freeze({
|
|
657
|
-
kind: 'fleet-respawn',
|
|
658
|
-
address: readAddress(precondition.address, field),
|
|
659
|
-
game: readAddress(precondition.game, field),
|
|
660
|
-
profile: readAddress(precondition.profile, field),
|
|
661
|
-
authority: readAddress(precondition.authority, field),
|
|
662
|
-
system: readAddress(precondition.system, field),
|
|
663
|
-
keyIndex,
|
|
664
|
-
phase,
|
|
665
|
-
fleetHash: readDataHash(precondition.fleetHash, field),
|
|
666
|
-
maximumRecoveryFee: maximumRecoveryFee,
|
|
667
|
-
maximumProfileDebit: maximumProfileDebit,
|
|
668
|
-
describes: readString(precondition.describes, field),
|
|
669
|
-
});
|
|
670
|
-
}
|
|
671
|
-
if (precondition.kind === 'fleet-dock-funding')
|
|
672
|
-
return normalizeFleetDockFundingGuard(precondition);
|
|
673
|
-
if (precondition.kind === 'local-market-order')
|
|
674
|
-
return normalizeLocalMarketOrderGuard(precondition);
|
|
675
|
-
if (precondition.kind === 'scan-signal-open') {
|
|
676
|
-
assertKeys(precondition, field, [
|
|
677
|
-
'kind',
|
|
678
|
-
'address',
|
|
679
|
-
'game',
|
|
680
|
-
'fleet',
|
|
681
|
-
'profile',
|
|
682
|
-
'authority',
|
|
683
|
-
'character',
|
|
684
|
-
'keyIndex',
|
|
685
|
-
'authorityHash',
|
|
686
|
-
'maximumProfileDebit',
|
|
687
|
-
'describes',
|
|
688
|
-
]);
|
|
689
|
-
const keyIndex = precondition.keyIndex;
|
|
690
|
-
if (typeof keyIndex !== 'number' ||
|
|
691
|
-
!Number.isInteger(keyIndex) ||
|
|
692
|
-
keyIndex < 0 ||
|
|
693
|
-
keyIndex > 65535)
|
|
694
|
-
invalid(field, keyIndex, 'a u16 key index');
|
|
695
|
-
const maximumProfileDebit = precondition.maximumProfileDebit;
|
|
696
|
-
if (typeof maximumProfileDebit !== 'bigint' ||
|
|
697
|
-
maximumProfileDebit < 0n ||
|
|
698
|
-
maximumProfileDebit > 0xffffffffffffffffn)
|
|
699
|
-
invalid(field, maximumProfileDebit, 'a nonnegative u64 funding ceiling');
|
|
700
|
-
return Object.freeze({
|
|
701
|
-
kind: 'scan-signal-open',
|
|
702
|
-
authorityHash: readDataHash(precondition.authorityHash, field),
|
|
703
|
-
address: readAddress(precondition.address, field),
|
|
704
|
-
game: readAddress(precondition.game, field),
|
|
705
|
-
fleet: readAddress(precondition.fleet, field),
|
|
706
|
-
profile: readAddress(precondition.profile, field),
|
|
707
|
-
authority: readAddress(precondition.authority, field),
|
|
708
|
-
character: readAddress(precondition.character, field),
|
|
709
|
-
keyIndex,
|
|
710
|
-
maximumProfileDebit,
|
|
711
|
-
describes: readString(precondition.describes, field),
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
if (precondition.kind === 'fleet-create') {
|
|
715
|
-
assertKeys(precondition, field, [
|
|
716
|
-
'kind',
|
|
717
|
-
'address',
|
|
718
|
-
'game',
|
|
719
|
-
'profile',
|
|
720
|
-
'authority',
|
|
721
|
-
'system',
|
|
722
|
-
'keyIndex',
|
|
723
|
-
'label',
|
|
724
|
-
'maximumProfileDebit',
|
|
725
|
-
'describes',
|
|
726
|
-
]);
|
|
727
|
-
const keyIndex = precondition.keyIndex;
|
|
728
|
-
if (typeof keyIndex !== 'number' ||
|
|
729
|
-
!Number.isSafeInteger(keyIndex) ||
|
|
730
|
-
keyIndex < 0 ||
|
|
731
|
-
keyIndex > 65_535)
|
|
732
|
-
invalid(`${field}.keyIndex`, keyIndex, 'an integer from 0 through 65535');
|
|
733
|
-
const label = readString(precondition.label, `${field}.label`);
|
|
734
|
-
const bytes = new TextEncoder().encode(label);
|
|
735
|
-
if (label.trim().length === 0 ||
|
|
736
|
-
label.includes('\0') ||
|
|
737
|
-
bytes.length > 64 ||
|
|
738
|
-
new TextDecoder('utf-8', { ignoreBOM: true }).decode(bytes) !== label)
|
|
739
|
-
invalid(`${field}.label`, label, 'a nonblank, NUL-free Unicode label of at most 64 UTF-8 bytes');
|
|
740
|
-
const maximumProfileDebit = precondition.maximumProfileDebit;
|
|
741
|
-
if (typeof maximumProfileDebit !== 'bigint' || maximumProfileDebit < 0n)
|
|
742
|
-
invalid(`${field}.maximumProfileDebit`, maximumProfileDebit, 'a nonnegative bigint');
|
|
743
|
-
return Object.freeze({
|
|
744
|
-
kind: 'fleet-create',
|
|
745
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
746
|
-
game: readAddress(precondition.game, `${field}.game`),
|
|
747
|
-
profile: readAddress(precondition.profile, `${field}.profile`),
|
|
748
|
-
authority: readAddress(precondition.authority, `${field}.authority`),
|
|
749
|
-
system: readAddress(precondition.system, `${field}.system`),
|
|
750
|
-
keyIndex,
|
|
751
|
-
label,
|
|
752
|
-
maximumProfileDebit,
|
|
753
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
754
|
-
});
|
|
755
|
-
}
|
|
756
|
-
if (precondition.kind === 'system-account-or-absent') {
|
|
757
|
-
assertKeys(precondition, field, ['kind', 'address', 'describes']);
|
|
758
|
-
return Object.freeze({
|
|
759
|
-
kind: 'system-account-or-absent',
|
|
760
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
761
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
762
|
-
});
|
|
763
|
-
}
|
|
764
|
-
if (precondition.kind === 'recipe-state') {
|
|
765
|
-
assertKeys(precondition, field, [
|
|
766
|
-
'kind',
|
|
767
|
-
'address',
|
|
768
|
-
'configHash',
|
|
769
|
-
'requiredUses',
|
|
770
|
-
'describes',
|
|
771
|
-
]);
|
|
772
|
-
const requiredUses = precondition.requiredUses;
|
|
773
|
-
if (typeof requiredUses !== 'bigint' ||
|
|
774
|
-
requiredUses < 0n ||
|
|
775
|
-
requiredUses > (1n << 64n) - 1n)
|
|
776
|
-
invalid(`${field}.requiredUses`, requiredUses, 'an unsigned u64 bigint');
|
|
777
|
-
const configHash = readDataHash(precondition.configHash, `${field}.configHash`);
|
|
778
|
-
return Object.freeze({
|
|
779
|
-
kind: 'recipe-state',
|
|
780
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
781
|
-
configHash,
|
|
782
|
-
requiredUses,
|
|
783
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
784
|
-
});
|
|
785
|
-
}
|
|
786
|
-
if (precondition.kind === 'token-mint') {
|
|
787
|
-
assertKeys(precondition, field, [
|
|
788
|
-
'kind',
|
|
789
|
-
'address',
|
|
790
|
-
'decimals',
|
|
791
|
-
'describes',
|
|
792
|
-
]);
|
|
793
|
-
const decimals = precondition.decimals;
|
|
794
|
-
if (typeof decimals !== 'number' ||
|
|
795
|
-
!Number.isInteger(decimals) ||
|
|
796
|
-
decimals < 0 ||
|
|
797
|
-
decimals > 255)
|
|
798
|
-
invalid(`${field}.decimals`, decimals, 'an unsigned u8 mint decimals value');
|
|
799
|
-
return Object.freeze({
|
|
800
|
-
kind: 'token-mint',
|
|
801
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
802
|
-
decimals,
|
|
803
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
|
-
if (precondition.kind === 'game-ship-config') {
|
|
807
|
-
assertKeys(precondition, field, [
|
|
808
|
-
'kind',
|
|
809
|
-
'address',
|
|
810
|
-
'shipId',
|
|
811
|
-
'configHash',
|
|
812
|
-
'describes',
|
|
813
|
-
]);
|
|
814
|
-
const id = precondition.shipId;
|
|
815
|
-
if (typeof id !== 'number' || !Number.isInteger(id) || id < 0 || id > 65535)
|
|
816
|
-
invalid(`${field}.shipId`, id, 'a u16 ship configuration ID');
|
|
817
|
-
return Object.freeze({
|
|
818
|
-
kind: 'game-ship-config',
|
|
819
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
820
|
-
shipId: id,
|
|
821
|
-
configHash: readDataHash(precondition.configHash, `${field}.configHash`),
|
|
822
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
823
|
-
});
|
|
824
|
-
}
|
|
825
|
-
if (precondition.kind === 'game-account') {
|
|
826
|
-
assertKeys(precondition, field, ['kind', 'address', 'describes']);
|
|
827
|
-
return Object.freeze({
|
|
828
|
-
kind: 'game-account',
|
|
829
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
830
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
831
|
-
});
|
|
832
|
-
}
|
|
833
|
-
if (['game-cargo-bytes-config', 'token-account', 'cargo-cache'].includes(String(precondition.kind))) {
|
|
834
|
-
const base = {
|
|
835
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
836
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
837
|
-
};
|
|
838
|
-
if (precondition.kind === 'token-account') {
|
|
839
|
-
assertKeys(precondition, field, [
|
|
840
|
-
'kind',
|
|
841
|
-
'address',
|
|
842
|
-
'mint',
|
|
843
|
-
'owner',
|
|
844
|
-
'minimumBalance',
|
|
845
|
-
'describes',
|
|
846
|
-
]);
|
|
847
|
-
const amount = precondition.minimumBalance;
|
|
848
|
-
if (typeof amount !== 'bigint' ||
|
|
849
|
-
amount < 0n ||
|
|
850
|
-
amount > (1n << 64n) - 1n)
|
|
851
|
-
invalid(`${field}.minimumBalance`, amount, 'an unsigned u64 bigint');
|
|
852
|
-
return Object.freeze({
|
|
853
|
-
...base,
|
|
854
|
-
kind: 'token-account',
|
|
855
|
-
mint: readAddress(precondition.mint, `${field}.mint`),
|
|
856
|
-
owner: readAddress(precondition.owner, `${field}.owner`),
|
|
857
|
-
minimumBalance: amount,
|
|
858
|
-
});
|
|
859
|
-
}
|
|
860
|
-
if (precondition.kind === 'game-cargo-bytes-config') {
|
|
861
|
-
assertKeys(precondition, field, [
|
|
862
|
-
'kind',
|
|
863
|
-
'address',
|
|
864
|
-
'cargoId',
|
|
865
|
-
'configHash',
|
|
866
|
-
'describes',
|
|
867
|
-
]);
|
|
868
|
-
const id = precondition.cargoId;
|
|
869
|
-
if (typeof id !== 'number' ||
|
|
870
|
-
!Number.isInteger(id) ||
|
|
871
|
-
id < 0 ||
|
|
872
|
-
id > 65535)
|
|
873
|
-
invalid(`${field}.cargoId`, id, 'a u16 cargo ID');
|
|
874
|
-
return Object.freeze({
|
|
875
|
-
...base,
|
|
876
|
-
kind: precondition.kind,
|
|
877
|
-
cargoId: id,
|
|
878
|
-
configHash: readDataHash(precondition.configHash, `${field}.configHash`),
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
assertKeys(precondition, field, [
|
|
882
|
-
'kind',
|
|
883
|
-
'address',
|
|
884
|
-
'game',
|
|
885
|
-
'bump',
|
|
886
|
-
'describes',
|
|
887
|
-
]);
|
|
888
|
-
const bump = precondition.bump;
|
|
889
|
-
if (typeof bump !== 'number' ||
|
|
890
|
-
!Number.isInteger(bump) ||
|
|
891
|
-
bump < 0 ||
|
|
892
|
-
bump > 255)
|
|
893
|
-
invalid(`${field}.bump`, bump, 'a u8 PDA bump');
|
|
894
|
-
return Object.freeze({
|
|
895
|
-
...base,
|
|
896
|
-
kind: 'cargo-cache',
|
|
897
|
-
game: readAddress(precondition.game, `${field}.game`),
|
|
898
|
-
bump,
|
|
899
|
-
});
|
|
900
|
-
}
|
|
901
|
-
if (precondition.kind === 'token-account-or-absent') {
|
|
902
|
-
assertKeys(precondition, field, [
|
|
903
|
-
'kind',
|
|
904
|
-
'address',
|
|
905
|
-
'mint',
|
|
906
|
-
'owner',
|
|
907
|
-
'describes',
|
|
908
|
-
]);
|
|
909
|
-
return Object.freeze({
|
|
910
|
-
kind: 'token-account-or-absent',
|
|
911
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
912
|
-
mint: readAddress(precondition.mint, `${field}.mint`),
|
|
913
|
-
owner: readAddress(precondition.owner, `${field}.owner`),
|
|
914
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
915
|
-
});
|
|
916
|
-
}
|
|
917
|
-
if (precondition.kind === 'account-absent') {
|
|
918
|
-
assertKeys(precondition, field, ['kind', 'address', 'describes']);
|
|
919
|
-
return Object.freeze({
|
|
920
|
-
kind: 'account-absent',
|
|
921
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
922
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
923
|
-
});
|
|
924
|
-
}
|
|
925
|
-
if (precondition.kind !== 'account-state') {
|
|
926
|
-
invalid(`${field}.kind`, precondition.kind, 'a supported current Plan guard; recreate the Plan using the current capability planner');
|
|
927
|
-
}
|
|
928
|
-
assertKeys(precondition, field, ['kind', 'address', 'dataHash', 'describes']);
|
|
929
|
-
return Object.freeze({
|
|
930
|
-
kind: 'account-state',
|
|
931
|
-
address: readAddress(precondition.address, `${field}.address`),
|
|
932
|
-
dataHash: readDataHash(precondition.dataHash, `${field}.dataHash`),
|
|
933
|
-
describes: readString(precondition.describes, `${field}.describes`),
|
|
934
|
-
});
|
|
935
|
-
}
|
|
936
136
|
function getAffected(steps) {
|
|
937
137
|
return uniqueAddresses(steps.flatMap((step) => (step.instruction.accounts ?? [])
|
|
938
138
|
.filter((account) => isWritableRole(account.role))
|
|
@@ -973,64 +173,7 @@ function planToJSON(plan) {
|
|
|
973
173
|
: {}),
|
|
974
174
|
signers: [...step.signers],
|
|
975
175
|
})),
|
|
976
|
-
preconditions: plan.preconditions.map(
|
|
977
|
-
if (guard.kind === 'recipe-state')
|
|
978
|
-
return { ...guard, requiredUses: String(guard.requiredUses) };
|
|
979
|
-
if (guard.kind === 'scan-detect' || guard.kind === 'scan-completion')
|
|
980
|
-
return { ...guard, expectedSequence: String(guard.expectedSequence) };
|
|
981
|
-
if (guard.kind === 'fleet-dock-funding')
|
|
982
|
-
return dockFundingGuardToJSON(guard);
|
|
983
|
-
if (guard.kind === 'fleet-arrival')
|
|
984
|
-
return arrivalGuardToJSON(guard);
|
|
985
|
-
if (guard.kind === 'fleet-crew-transfer')
|
|
986
|
-
return { ...guard, systemSequenceId: String(guard.systemSequenceId) };
|
|
987
|
-
if (guard.kind === 'fleet-respawn')
|
|
988
|
-
return {
|
|
989
|
-
...guard,
|
|
990
|
-
maximumRecoveryFee: String(guard.maximumRecoveryFee),
|
|
991
|
-
maximumProfileDebit: String(guard.maximumProfileDebit),
|
|
992
|
-
};
|
|
993
|
-
if (isRuntimeGuard(guard))
|
|
994
|
-
return runtimeGuardToJSON(guard);
|
|
995
|
-
if (isTradeBuyGuard(guard))
|
|
996
|
-
return { ...guard, floorRaw: String(guard.floorRaw) };
|
|
997
|
-
if (isTradeSellGuard(guard))
|
|
998
|
-
return {
|
|
999
|
-
...guard,
|
|
1000
|
-
atlasOutCapPerEpochRaw: String(guard.atlasOutCapPerEpochRaw),
|
|
1001
|
-
epochLengthSeconds: String(guard.epochLengthSeconds),
|
|
1002
|
-
};
|
|
1003
|
-
if (guard.kind === 'fleet-add-ship')
|
|
1004
|
-
return {
|
|
1005
|
-
...guard,
|
|
1006
|
-
amount: String(guard.amount),
|
|
1007
|
-
maximumProfileDebit: String(guard.maximumProfileDebit),
|
|
1008
|
-
};
|
|
1009
|
-
if (guard.kind === 'character-registration')
|
|
1010
|
-
return {
|
|
1011
|
-
...guard,
|
|
1012
|
-
maximumFunderDebit: String(guard.maximumFunderDebit),
|
|
1013
|
-
characterLamports: String(guard.characterLamports),
|
|
1014
|
-
budgetLamports: String(guard.budgetLamports),
|
|
1015
|
-
};
|
|
1016
|
-
if (guard.kind === 'scan-signal-open' ||
|
|
1017
|
-
guard.kind === 'fleet-create' ||
|
|
1018
|
-
guard.kind === 'ship-import-funding')
|
|
1019
|
-
return {
|
|
1020
|
-
...guard,
|
|
1021
|
-
maximumProfileDebit: String(guard.maximumProfileDebit),
|
|
1022
|
-
};
|
|
1023
|
-
if (guard.kind === 'starbase-system')
|
|
1024
|
-
return {
|
|
1025
|
-
...guard,
|
|
1026
|
-
starbaseSequenceId: String(guard.starbaseSequenceId),
|
|
1027
|
-
};
|
|
1028
|
-
if (guard.kind === 'local-market-order')
|
|
1029
|
-
return localMarketOrderGuardToJSON(guard);
|
|
1030
|
-
if (guard.kind === 'token-account')
|
|
1031
|
-
return { ...guard, minimumBalance: String(guard.minimumBalance) };
|
|
1032
|
-
return { ...guard };
|
|
1033
|
-
}),
|
|
176
|
+
preconditions: plan.preconditions.map(guardToWire),
|
|
1034
177
|
});
|
|
1035
178
|
}
|
|
1036
179
|
/** Creates an inert branded Plan while deriving every aggregate field.
|
|
@@ -1052,21 +195,15 @@ export function createPlan(input) {
|
|
|
1052
195
|
assertKeys(value, 'plan', ['kind', 'summary', 'steps', 'preconditions']);
|
|
1053
196
|
const steps = Object.freeze(readArray(value.steps, 'plan.steps').map((step, index) => normalizeStep(step, `plan.steps[${String(index)}]`)));
|
|
1054
197
|
const preconditions = Object.freeze(readArray(value.preconditions, 'plan.preconditions').map((precondition, index) => normalizePrecondition(precondition, `plan.preconditions[${String(index)}]`)));
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
validateDetectInstructions(steps, preconditions);
|
|
1061
|
-
validateCompletionInstructions(steps, preconditions);
|
|
1062
|
-
validateRespawnInstructions(steps, preconditions);
|
|
1063
|
-
validateCrewTransferInstructions(steps, preconditions);
|
|
1064
|
-
validateOpenSignalInstructions(steps, preconditions);
|
|
1065
|
-
validateLocalMarketOrderInstructions(steps, preconditions);
|
|
198
|
+
validateCreationBindings(steps, preconditions);
|
|
199
|
+
return brandPlan(readString(value.kind, 'plan.kind'), readString(value.summary, 'plan.summary'), steps, preconditions);
|
|
200
|
+
}
|
|
201
|
+
/** Derives aggregates and brands already normalized, validated contents. */
|
|
202
|
+
function brandPlan(kind, summary, steps, preconditions) {
|
|
1066
203
|
const descriptions = Object.freeze(steps.map((step) => step.describes));
|
|
1067
204
|
const plan = {
|
|
1068
|
-
kind
|
|
1069
|
-
summary
|
|
205
|
+
kind,
|
|
206
|
+
summary,
|
|
1070
207
|
steps,
|
|
1071
208
|
preconditions,
|
|
1072
209
|
affected: getAffected(steps),
|
|
@@ -1127,111 +264,6 @@ export function planFromJSON(json) {
|
|
|
1127
264
|
if (value.v !== 27) {
|
|
1128
265
|
throw new InvalidActionInputError('planJson.v', value.v, 'the current Plan wire version 27', 'Recreate the Plan using the current capability planner; do not change its saved version or migrate its guards.');
|
|
1129
266
|
}
|
|
1130
|
-
value.preconditions = readArray(value.preconditions, 'planJson.preconditions').map((entry) => {
|
|
1131
|
-
const guard = readRecord(entry, 'precondition');
|
|
1132
|
-
if (guard.kind === 'recipe-state') {
|
|
1133
|
-
if (typeof guard.requiredUses !== 'string' ||
|
|
1134
|
-
!/^(0|[1-9][0-9]*)$/.test(guard.requiredUses))
|
|
1135
|
-
invalid('requiredUses', guard.requiredUses, 'a canonical unsigned decimal string');
|
|
1136
|
-
return { ...guard, requiredUses: BigInt(guard.requiredUses) };
|
|
1137
|
-
}
|
|
1138
|
-
if (guard.kind === 'fleet-dock-funding')
|
|
1139
|
-
return normalizeFleetDockFundingGuard(guard, true);
|
|
1140
|
-
if (guard.kind === 'local-market-order')
|
|
1141
|
-
return normalizeLocalMarketOrderGuard(guard, true);
|
|
1142
|
-
if (guard.kind === 'scan-detect' || guard.kind === 'scan-completion') {
|
|
1143
|
-
if (typeof guard.expectedSequence !== 'string' ||
|
|
1144
|
-
!/^(0|[1-9][0-9]*)$/.test(guard.expectedSequence))
|
|
1145
|
-
invalid('expectedSequence', guard.expectedSequence, 'a canonical unsigned decimal string');
|
|
1146
|
-
return { ...guard, expectedSequence: BigInt(guard.expectedSequence) };
|
|
1147
|
-
}
|
|
1148
|
-
if (guard.kind === 'fleet-arrival')
|
|
1149
|
-
return normalizeArrivalGuard(guard, 'precondition', true);
|
|
1150
|
-
if (isRuntimeGuard(guard))
|
|
1151
|
-
return normalizeRuntimeGuard(guard, 'precondition', true);
|
|
1152
|
-
if (isTradeBuyGuard(guard) || isTradeSellGuard(guard)) {
|
|
1153
|
-
const keys = isTradeBuyGuard(guard)
|
|
1154
|
-
? ['floorRaw']
|
|
1155
|
-
: ['atlasOutCapPerEpochRaw', 'epochLengthSeconds'];
|
|
1156
|
-
const decoded = { ...guard };
|
|
1157
|
-
for (const key of keys) {
|
|
1158
|
-
const raw = guard[key];
|
|
1159
|
-
const pattern = key === 'epochLengthSeconds'
|
|
1160
|
-
? /^(0|[1-9][0-9]*|-[1-9][0-9]*)$/
|
|
1161
|
-
: /^(0|[1-9][0-9]*)$/;
|
|
1162
|
-
if (typeof raw !== 'string' || !pattern.test(raw))
|
|
1163
|
-
invalid(key, raw, 'a canonical decimal string');
|
|
1164
|
-
decoded[key] = BigInt(raw);
|
|
1165
|
-
}
|
|
1166
|
-
return decoded;
|
|
1167
|
-
}
|
|
1168
|
-
if (guard.kind === 'fleet-add-ship') {
|
|
1169
|
-
for (const field of ['amount', 'maximumProfileDebit'])
|
|
1170
|
-
if (typeof guard[field] !== 'string' ||
|
|
1171
|
-
!/^(0|[1-9][0-9]*)$/.test(guard[field]))
|
|
1172
|
-
invalid(field, guard[field], 'a canonical unsigned decimal string');
|
|
1173
|
-
return {
|
|
1174
|
-
...guard,
|
|
1175
|
-
amount: BigInt(guard.amount),
|
|
1176
|
-
maximumProfileDebit: BigInt(guard.maximumProfileDebit),
|
|
1177
|
-
};
|
|
1178
|
-
}
|
|
1179
|
-
if (guard.kind === 'character-registration') {
|
|
1180
|
-
for (const key of ['characterLamports', 'budgetLamports'])
|
|
1181
|
-
if (typeof guard[key] !== 'string' ||
|
|
1182
|
-
!/^(0|[1-9][0-9]*)$/.test(guard[key]))
|
|
1183
|
-
invalid(key, guard[key], 'a canonical unsigned decimal string');
|
|
1184
|
-
if (typeof guard.maximumFunderDebit !== 'string' ||
|
|
1185
|
-
!/^(0|[1-9][0-9]*)$/.test(guard.maximumFunderDebit))
|
|
1186
|
-
invalid('maximumFunderDebit', guard.maximumFunderDebit, 'a canonical unsigned decimal string');
|
|
1187
|
-
return {
|
|
1188
|
-
...guard,
|
|
1189
|
-
maximumFunderDebit: BigInt(guard.maximumFunderDebit),
|
|
1190
|
-
characterLamports: BigInt(guard.characterLamports),
|
|
1191
|
-
budgetLamports: BigInt(guard.budgetLamports),
|
|
1192
|
-
};
|
|
1193
|
-
}
|
|
1194
|
-
if (guard.kind === 'fleet-crew-transfer') {
|
|
1195
|
-
if (typeof guard.systemSequenceId !== 'string' ||
|
|
1196
|
-
!/^(0|[1-9][0-9]*)$/.test(guard.systemSequenceId))
|
|
1197
|
-
invalid('systemSequenceId', guard.systemSequenceId, 'a canonical unsigned decimal string');
|
|
1198
|
-
return { ...guard, systemSequenceId: BigInt(guard.systemSequenceId) };
|
|
1199
|
-
}
|
|
1200
|
-
if (guard.kind === 'fleet-respawn') {
|
|
1201
|
-
for (const key of ['maximumRecoveryFee', 'maximumProfileDebit'])
|
|
1202
|
-
if (typeof guard[key] !== 'string' ||
|
|
1203
|
-
!/^(0|[1-9][0-9]*)$/.test(guard[key]))
|
|
1204
|
-
invalid(key, guard[key], 'a canonical unsigned decimal string');
|
|
1205
|
-
return {
|
|
1206
|
-
...guard,
|
|
1207
|
-
maximumRecoveryFee: BigInt(guard.maximumRecoveryFee),
|
|
1208
|
-
maximumProfileDebit: BigInt(guard.maximumProfileDebit),
|
|
1209
|
-
};
|
|
1210
|
-
}
|
|
1211
|
-
if (guard.kind === 'scan-signal-open' ||
|
|
1212
|
-
guard.kind === 'fleet-create' ||
|
|
1213
|
-
guard.kind === 'ship-import-funding') {
|
|
1214
|
-
if (typeof guard.maximumProfileDebit !== 'string' ||
|
|
1215
|
-
!/^(0|[1-9][0-9]*)$/.test(guard.maximumProfileDebit))
|
|
1216
|
-
invalid('maximumProfileDebit', guard.maximumProfileDebit, 'a canonical unsigned decimal string');
|
|
1217
|
-
return {
|
|
1218
|
-
...guard,
|
|
1219
|
-
maximumProfileDebit: BigInt(guard.maximumProfileDebit),
|
|
1220
|
-
};
|
|
1221
|
-
}
|
|
1222
|
-
if (guard.kind === 'starbase-system') {
|
|
1223
|
-
if (typeof guard.starbaseSequenceId !== 'string' ||
|
|
1224
|
-
!/^(0|[1-9][0-9]*)$/.test(guard.starbaseSequenceId))
|
|
1225
|
-
invalid('starbaseSequenceId', guard.starbaseSequenceId, 'a canonical unsigned decimal string');
|
|
1226
|
-
return { ...guard, starbaseSequenceId: BigInt(guard.starbaseSequenceId) };
|
|
1227
|
-
}
|
|
1228
|
-
if (guard.kind !== 'token-account')
|
|
1229
|
-
return guard;
|
|
1230
|
-
if (typeof guard.minimumBalance !== 'string' ||
|
|
1231
|
-
!/^(0|[1-9][0-9]*)$/.test(guard.minimumBalance))
|
|
1232
|
-
invalid('minimumBalance', guard.minimumBalance, 'a canonical unsigned decimal string');
|
|
1233
|
-
return { ...guard, minimumBalance: BigInt(guard.minimumBalance) };
|
|
1234
|
-
});
|
|
1235
267
|
const steps = readArray(value.steps, 'planJson.steps').map((candidate, index) => {
|
|
1236
268
|
const field = `planJson.steps[${String(index)}]`;
|
|
1237
269
|
const step = readRecord(candidate, field);
|
|
@@ -1247,21 +279,15 @@ export function planFromJSON(json) {
|
|
|
1247
279
|
signers: readArray(step.signers, `${field}.signers`).map((signer, signerIndex) => readAddress(signer, `${field}.signers[${String(signerIndex)}]`)),
|
|
1248
280
|
};
|
|
1249
281
|
});
|
|
1250
|
-
const preconditions = readArray(value.preconditions, 'planJson.preconditions').map((candidate, index) => normalizePrecondition(candidate, `planJson.preconditions[${String(index)}]
|
|
282
|
+
const preconditions = readArray(value.preconditions, 'planJson.preconditions').map((candidate, index) => normalizePrecondition(candidate, `planJson.preconditions[${String(index)}]`, true));
|
|
1251
283
|
const affected = readArray(value.affected, 'planJson.affected').map((candidate, index) => readAddress(candidate, `planJson.affected[${String(index)}]`));
|
|
1252
284
|
const requiredSigners = readArray(value.requiredSigners, 'planJson.requiredSigners').map((candidate, index) => readAddress(candidate, `planJson.requiredSigners[${String(index)}]`));
|
|
1253
|
-
const
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
validateStarbaseSystemInstructions(plan.steps, plan.preconditions);
|
|
1260
|
-
validateFleetDockingGuards(plan.steps, plan.preconditions);
|
|
1261
|
-
validateFleetDockFundingGuards(plan.steps, plan.preconditions);
|
|
1262
|
-
validateMovementXpInstructions(plan.steps, plan.preconditions);
|
|
1263
|
-
validateFleetAttackInstructions(plan.steps, plan.preconditions);
|
|
1264
|
-
validateCharacterRegistrationInstructions(plan.steps, plan.preconditions);
|
|
285
|
+
const kind = readString(value.kind, 'planJson.kind');
|
|
286
|
+
const summary = readString(value.summary, 'planJson.summary');
|
|
287
|
+
const normalizedSteps = Object.freeze(steps.map((step, index) => normalizeStep(step, `planJson.steps[${String(index)}]`)));
|
|
288
|
+
const normalizedPreconditions = Object.freeze(preconditions);
|
|
289
|
+
validateHydrationBindings(normalizedSteps, normalizedPreconditions);
|
|
290
|
+
const plan = brandPlan(kind, summary, normalizedSteps, normalizedPreconditions);
|
|
1265
291
|
if (!sameAddresses(plan.affected, affected)) {
|
|
1266
292
|
invalid('planJson.affected', value.affected, 'the first-seen union of writable instruction account addresses');
|
|
1267
293
|
}
|
|
@@ -1278,49 +304,6 @@ function assertPlan(value, index) {
|
|
|
1278
304
|
function combinedSummary(steps) {
|
|
1279
305
|
return steps.map((step) => step.describes).join(' ');
|
|
1280
306
|
}
|
|
1281
|
-
function validateShipImportFundingComposition(guards) {
|
|
1282
|
-
const targets = new Map();
|
|
1283
|
-
for (const guard of guards) {
|
|
1284
|
-
if (guard.kind !== 'ship-import-funding')
|
|
1285
|
-
continue;
|
|
1286
|
-
const identity = `${guard.game}:${guard.profile}:${guard.system}:${String(guard.shipId)}`;
|
|
1287
|
-
const previous = targets.get(guard.address);
|
|
1288
|
-
if (previous !== undefined && previous !== identity)
|
|
1289
|
-
throw new SageSdkError('INCOMPATIBLE_PLAN_COMPOSITION', 'Ship imports retain conflicting identities or different configurations for one StarbasePlayer. Use separate confirmed imports, then refresh inventory and replan; prefunding cannot be shared across independently estimated storage growth.');
|
|
1290
|
-
targets.set(guard.address, identity);
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
function validateFactionGuardComposition(guards) {
|
|
1294
|
-
const retained = new Map();
|
|
1295
|
-
for (const guard of guards) {
|
|
1296
|
-
if (!isFactionTradeGuard(guard))
|
|
1297
|
-
continue;
|
|
1298
|
-
const constraints = [
|
|
1299
|
-
[
|
|
1300
|
-
`${isTradeMeterGuard(guard) ? 'meter' : 'treasury'}:${guard.address}:identity`,
|
|
1301
|
-
`${guard.game}:${String(guard.factionId)}`,
|
|
1302
|
-
],
|
|
1303
|
-
];
|
|
1304
|
-
if (!isTradeMeterGuard(guard)) {
|
|
1305
|
-
constraints.push(isTradeBuyGuard(guard)
|
|
1306
|
-
? [
|
|
1307
|
-
`${guard.address}:buy:${String(guard.cargoId)}`,
|
|
1308
|
-
String(guard.floorRaw),
|
|
1309
|
-
]
|
|
1310
|
-
: [
|
|
1311
|
-
`${guard.address}:sell`,
|
|
1312
|
-
`${String(guard.atlasOutCapPerEpochRaw)}:${String(guard.epochLengthSeconds)}`,
|
|
1313
|
-
]);
|
|
1314
|
-
}
|
|
1315
|
-
constraints.push([`${guard.address}:schema`, String(guard.schemaVersion)]);
|
|
1316
|
-
for (const [key, value] of constraints) {
|
|
1317
|
-
const previous = retained.get(key);
|
|
1318
|
-
if (previous !== undefined && previous !== value)
|
|
1319
|
-
throw new SageSdkError('INCOMPATIBLE_PLAN_COMPOSITION', 'Faction Market Plans retain incompatible Treasury policy or shared account identity. Refresh the affected Markets and prepare compatible Plans, or use separate confirmed transactions.');
|
|
1320
|
-
retained.set(key, value);
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
307
|
/** Combines one or more Plans with deterministic associative flattening.
|
|
1325
308
|
*
|
|
1326
309
|
* Semantic validation is eager. Conflict indices are relative to the flattened
|
|
@@ -1339,31 +322,11 @@ export function combinePlans(...plans) {
|
|
|
1339
322
|
throw new InvalidActionInputError('plans', plans.length, 'one or more Plan values', 'Pass at least one Plan to combinePlans before retrying.');
|
|
1340
323
|
}
|
|
1341
324
|
plans.forEach(assertPlan);
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
const preconditions = plans.flatMap((plan) => plan.preconditions);
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
validateStarbaseSystemInstructions(steps, preconditions);
|
|
1348
|
-
validateFleetDockingGuards(steps, preconditions);
|
|
1349
|
-
validateFleetDockFundingGuards(steps, preconditions);
|
|
1350
|
-
validateCharacterRegistrationInstructions(steps, preconditions);
|
|
1351
|
-
validateArrivalInstructions(steps, preconditions);
|
|
1352
|
-
validateDetectInstructions(steps, preconditions);
|
|
1353
|
-
validateCompletionInstructions(steps, preconditions);
|
|
1354
|
-
validateRespawnInstructions(steps, preconditions);
|
|
1355
|
-
validateCrewTransferInstructions(steps, preconditions);
|
|
1356
|
-
validateOpenSignalInstructions(steps, preconditions);
|
|
1357
|
-
validateLocalMarketOrderInstructions(steps, preconditions);
|
|
1358
|
-
validateRuntimeInstructions(steps, preconditions);
|
|
1359
|
-
validateMovementXpInstructions(steps, preconditions);
|
|
1360
|
-
validateFleetAttackInstructions(steps, preconditions);
|
|
1361
|
-
return createPlan({
|
|
1362
|
-
kind: 'combined',
|
|
1363
|
-
summary: combinedSummary(steps),
|
|
1364
|
-
steps,
|
|
1365
|
-
preconditions,
|
|
1366
|
-
});
|
|
325
|
+
// Branded Plans hold normalized, frozen contents; validate the union once.
|
|
326
|
+
const steps = Object.freeze(plans.flatMap((plan) => plan.steps));
|
|
327
|
+
const preconditions = Object.freeze(plans.flatMap((plan) => plan.preconditions));
|
|
328
|
+
validatePlanBindings(steps, preconditions);
|
|
329
|
+
return brandPlan('combined', combinedSummary(steps), steps, preconditions);
|
|
1367
330
|
}
|
|
1368
331
|
export { assemblePlan } from './assembly.js';
|
|
1369
332
|
export { executePlan, } from './execution.js';
|