@aephia/atlas-kit 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +124 -0
  3. package/dist/bindings.d.ts +11 -0
  4. package/dist/bindings.d.ts.map +1 -0
  5. package/dist/bindings.js +11 -0
  6. package/dist/bindings.js.map +1 -0
  7. package/dist/cargo/actions/index.d.ts +122 -0
  8. package/dist/cargo/actions/index.d.ts.map +1 -0
  9. package/dist/cargo/actions/index.js +526 -0
  10. package/dist/cargo/actions/index.js.map +1 -0
  11. package/dist/cargo/index.d.ts +77 -0
  12. package/dist/cargo/index.d.ts.map +1 -0
  13. package/dist/cargo/index.js +102 -0
  14. package/dist/cargo/index.js.map +1 -0
  15. package/dist/claim-stakes/index.d.ts +64 -0
  16. package/dist/claim-stakes/index.d.ts.map +1 -0
  17. package/dist/claim-stakes/index.js +97 -0
  18. package/dist/claim-stakes/index.js.map +1 -0
  19. package/dist/client/context.d.ts +21 -0
  20. package/dist/client/context.d.ts.map +1 -0
  21. package/dist/client/context.js +1374 -0
  22. package/dist/client/context.js.map +1 -0
  23. package/dist/client/contracts.d.ts +671 -0
  24. package/dist/client/contracts.d.ts.map +1 -0
  25. package/dist/client/contracts.js +2 -0
  26. package/dist/client/contracts.js.map +1 -0
  27. package/dist/client/deployments.d.ts +3 -0
  28. package/dist/client/deployments.d.ts.map +1 -0
  29. package/dist/client/deployments.js +2 -0
  30. package/dist/client/deployments.js.map +1 -0
  31. package/dist/client/errors.d.ts +227 -0
  32. package/dist/client/errors.d.ts.map +1 -0
  33. package/dist/client/errors.js +288 -0
  34. package/dist/client/errors.js.map +1 -0
  35. package/dist/client/index.d.ts +7 -0
  36. package/dist/client/index.d.ts.map +1 -0
  37. package/dist/client/index.js +5 -0
  38. package/dist/client/index.js.map +1 -0
  39. package/dist/client/lifecycle.d.ts +4 -0
  40. package/dist/client/lifecycle.d.ts.map +1 -0
  41. package/dist/client/lifecycle.js +10 -0
  42. package/dist/client/lifecycle.js.map +1 -0
  43. package/dist/client/provider-errors.d.ts +9 -0
  44. package/dist/client/provider-errors.d.ts.map +1 -0
  45. package/dist/client/provider-errors.js +12 -0
  46. package/dist/client/provider-errors.js.map +1 -0
  47. package/dist/client/reconciliation.d.ts +10 -0
  48. package/dist/client/reconciliation.d.ts.map +1 -0
  49. package/dist/client/reconciliation.js +9 -0
  50. package/dist/client/reconciliation.js.map +1 -0
  51. package/dist/client/refs.d.ts +23 -0
  52. package/dist/client/refs.d.ts.map +1 -0
  53. package/dist/client/refs.js +12 -0
  54. package/dist/client/refs.js.map +1 -0
  55. package/dist/combat/index.d.ts +256 -0
  56. package/dist/combat/index.d.ts.map +1 -0
  57. package/dist/combat/index.js +222 -0
  58. package/dist/combat/index.js.map +1 -0
  59. package/dist/crafting/index.d.ts +308 -0
  60. package/dist/crafting/index.d.ts.map +1 -0
  61. package/dist/crafting/index.js +400 -0
  62. package/dist/crafting/index.js.map +1 -0
  63. package/dist/factions/index.d.ts +292 -0
  64. package/dist/factions/index.d.ts.map +1 -0
  65. package/dist/factions/index.js +334 -0
  66. package/dist/factions/index.js.map +1 -0
  67. package/dist/fleets/actions/index.d.ts +200 -0
  68. package/dist/fleets/actions/index.d.ts.map +1 -0
  69. package/dist/fleets/actions/index.js +854 -0
  70. package/dist/fleets/actions/index.js.map +1 -0
  71. package/dist/fleets/index.d.ts +100 -0
  72. package/dist/fleets/index.d.ts.map +1 -0
  73. package/dist/fleets/index.js +124 -0
  74. package/dist/fleets/index.js.map +1 -0
  75. package/dist/identity/index.d.ts +225 -0
  76. package/dist/identity/index.d.ts.map +1 -0
  77. package/dist/identity/index.js +341 -0
  78. package/dist/identity/index.js.map +1 -0
  79. package/dist/index.d.ts +401 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +519 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/internal/c4/adapter.d.ts +6 -0
  84. package/dist/internal/c4/adapter.d.ts.map +1 -0
  85. package/dist/internal/c4/adapter.js +2 -0
  86. package/dist/internal/c4/adapter.js.map +1 -0
  87. package/dist/internal/c4/bindings.d.ts +4 -0
  88. package/dist/internal/c4/bindings.d.ts.map +1 -0
  89. package/dist/internal/c4/bindings.js +4 -0
  90. package/dist/internal/c4/bindings.js.map +1 -0
  91. package/dist/internal/c4/character-modifier.d.ts +5 -0
  92. package/dist/internal/c4/character-modifier.d.ts.map +1 -0
  93. package/dist/internal/c4/character-modifier.js +100 -0
  94. package/dist/internal/c4/character-modifier.js.map +1 -0
  95. package/dist/internal/c4/claim-stake.d.ts +140 -0
  96. package/dist/internal/c4/claim-stake.d.ts.map +1 -0
  97. package/dist/internal/c4/claim-stake.js +205 -0
  98. package/dist/internal/c4/claim-stake.js.map +1 -0
  99. package/dist/internal/c4/combat.d.ts +98 -0
  100. package/dist/internal/c4/combat.d.ts.map +1 -0
  101. package/dist/internal/c4/combat.js +232 -0
  102. package/dist/internal/c4/combat.js.map +1 -0
  103. package/dist/internal/c4/crafting.d.ts +170 -0
  104. package/dist/internal/c4/crafting.d.ts.map +1 -0
  105. package/dist/internal/c4/crafting.js +387 -0
  106. package/dist/internal/c4/crafting.js.map +1 -0
  107. package/dist/internal/c4/factions.d.ts +248 -0
  108. package/dist/internal/c4/factions.d.ts.map +1 -0
  109. package/dist/internal/c4/factions.js +589 -0
  110. package/dist/internal/c4/factions.js.map +1 -0
  111. package/dist/internal/c4/fleet-crew-binding.d.ts +12 -0
  112. package/dist/internal/c4/fleet-crew-binding.d.ts.map +1 -0
  113. package/dist/internal/c4/fleet-crew-binding.js +51 -0
  114. package/dist/internal/c4/fleet-crew-binding.js.map +1 -0
  115. package/dist/internal/c4/fleet.d.ts +310 -0
  116. package/dist/internal/c4/fleet.d.ts.map +1 -0
  117. package/dist/internal/c4/fleet.js +393 -0
  118. package/dist/internal/c4/fleet.js.map +1 -0
  119. package/dist/internal/c4/game.d.ts +192 -0
  120. package/dist/internal/c4/game.d.ts.map +1 -0
  121. package/dist/internal/c4/game.js +396 -0
  122. package/dist/internal/c4/game.js.map +1 -0
  123. package/dist/internal/c4/identity.d.ts +193 -0
  124. package/dist/internal/c4/identity.d.ts.map +1 -0
  125. package/dist/internal/c4/identity.js +180 -0
  126. package/dist/internal/c4/identity.js.map +1 -0
  127. package/dist/internal/c4/loot-layout.d.ts +20 -0
  128. package/dist/internal/c4/loot-layout.d.ts.map +1 -0
  129. package/dist/internal/c4/loot-layout.js +29 -0
  130. package/dist/internal/c4/loot-layout.js.map +1 -0
  131. package/dist/internal/c4/loyalty.d.ts +102 -0
  132. package/dist/internal/c4/loyalty.d.ts.map +1 -0
  133. package/dist/internal/c4/loyalty.js +318 -0
  134. package/dist/internal/c4/loyalty.js.map +1 -0
  135. package/dist/internal/c4/market.d.ts +112 -0
  136. package/dist/internal/c4/market.d.ts.map +1 -0
  137. package/dist/internal/c4/market.js +247 -0
  138. package/dist/internal/c4/market.js.map +1 -0
  139. package/dist/internal/c4/pda.d.ts +6 -0
  140. package/dist/internal/c4/pda.d.ts.map +1 -0
  141. package/dist/internal/c4/pda.js +45 -0
  142. package/dist/internal/c4/pda.js.map +1 -0
  143. package/dist/internal/c4/profile-faction.d.ts +14 -0
  144. package/dist/internal/c4/profile-faction.d.ts.map +1 -0
  145. package/dist/internal/c4/profile-faction.js +69 -0
  146. package/dist/internal/c4/profile-faction.js.map +1 -0
  147. package/dist/internal/c4/rewards.d.ts +135 -0
  148. package/dist/internal/c4/rewards.d.ts.map +1 -0
  149. package/dist/internal/c4/rewards.js +305 -0
  150. package/dist/internal/c4/rewards.js.map +1 -0
  151. package/dist/internal/c4/scanning.d.ts +85 -0
  152. package/dist/internal/c4/scanning.d.ts.map +1 -0
  153. package/dist/internal/c4/scanning.js +180 -0
  154. package/dist/internal/c4/scanning.js.map +1 -0
  155. package/dist/internal/c4/starbase.d.ts +186 -0
  156. package/dist/internal/c4/starbase.d.ts.map +1 -0
  157. package/dist/internal/c4/starbase.js +259 -0
  158. package/dist/internal/c4/starbase.js.map +1 -0
  159. package/dist/internal/c4/world.d.ts +294 -0
  160. package/dist/internal/c4/world.d.ts.map +1 -0
  161. package/dist/internal/c4/world.js +417 -0
  162. package/dist/internal/c4/world.js.map +1 -0
  163. package/dist/internal/cargo.d.ts +65 -0
  164. package/dist/internal/cargo.d.ts.map +1 -0
  165. package/dist/internal/cargo.js +46 -0
  166. package/dist/internal/cargo.js.map +1 -0
  167. package/dist/internal/claim-stake.d.ts +31 -0
  168. package/dist/internal/claim-stake.d.ts.map +1 -0
  169. package/dist/internal/claim-stake.js +20 -0
  170. package/dist/internal/claim-stake.js.map +1 -0
  171. package/dist/internal/fleet.d.ts +165 -0
  172. package/dist/internal/fleet.d.ts.map +1 -0
  173. package/dist/internal/fleet.js +179 -0
  174. package/dist/internal/fleet.js.map +1 -0
  175. package/dist/internal/game-context.d.ts +4 -0
  176. package/dist/internal/game-context.d.ts.map +1 -0
  177. package/dist/internal/game-context.js +9 -0
  178. package/dist/internal/game-context.js.map +1 -0
  179. package/dist/internal/immutable-tree.d.ts +5 -0
  180. package/dist/internal/immutable-tree.d.ts.map +1 -0
  181. package/dist/internal/immutable-tree.js +47 -0
  182. package/dist/internal/immutable-tree.js.map +1 -0
  183. package/dist/internal/json.d.ts +7 -0
  184. package/dist/internal/json.d.ts.map +1 -0
  185. package/dist/internal/json.js +33 -0
  186. package/dist/internal/json.js.map +1 -0
  187. package/dist/internal/planning/authorization-adapter.d.ts +63 -0
  188. package/dist/internal/planning/authorization-adapter.d.ts.map +1 -0
  189. package/dist/internal/planning/authorization-adapter.js +222 -0
  190. package/dist/internal/planning/authorization-adapter.js.map +1 -0
  191. package/dist/internal/read-meta.d.ts +84 -0
  192. package/dist/internal/read-meta.d.ts.map +1 -0
  193. package/dist/internal/read-meta.js +28 -0
  194. package/dist/internal/read-meta.js.map +1 -0
  195. package/dist/internal/resolved-snapshot.d.ts +3 -0
  196. package/dist/internal/resolved-snapshot.d.ts.map +1 -0
  197. package/dist/internal/resolved-snapshot.js +11 -0
  198. package/dist/internal/resolved-snapshot.js.map +1 -0
  199. package/dist/internal/zink/provider.d.ts +9 -0
  200. package/dist/internal/zink/provider.d.ts.map +1 -0
  201. package/dist/internal/zink/provider.js +213 -0
  202. package/dist/internal/zink/provider.js.map +1 -0
  203. package/dist/loyalty/index.d.ts +195 -0
  204. package/dist/loyalty/index.d.ts.map +1 -0
  205. package/dist/loyalty/index.js +289 -0
  206. package/dist/loyalty/index.js.map +1 -0
  207. package/dist/markets/index.d.ts +195 -0
  208. package/dist/markets/index.d.ts.map +1 -0
  209. package/dist/markets/index.js +232 -0
  210. package/dist/markets/index.js.map +1 -0
  211. package/dist/mining/index.d.ts +156 -0
  212. package/dist/mining/index.d.ts.map +1 -0
  213. package/dist/mining/index.js +236 -0
  214. package/dist/mining/index.js.map +1 -0
  215. package/dist/planning/assembly.d.ts +42 -0
  216. package/dist/planning/assembly.d.ts.map +1 -0
  217. package/dist/planning/assembly.js +141 -0
  218. package/dist/planning/assembly.js.map +1 -0
  219. package/dist/planning/composition.d.ts +4 -0
  220. package/dist/planning/composition.d.ts.map +1 -0
  221. package/dist/planning/composition.js +76 -0
  222. package/dist/planning/composition.js.map +1 -0
  223. package/dist/planning/execution.d.ts +93 -0
  224. package/dist/planning/execution.d.ts.map +1 -0
  225. package/dist/planning/execution.js +549 -0
  226. package/dist/planning/execution.js.map +1 -0
  227. package/dist/planning/index.d.ts +219 -0
  228. package/dist/planning/index.d.ts.map +1 -0
  229. package/dist/planning/index.js +375 -0
  230. package/dist/planning/index.js.map +1 -0
  231. package/dist/planning/plan-instances.d.ts +3 -0
  232. package/dist/planning/plan-instances.d.ts.map +1 -0
  233. package/dist/planning/plan-instances.js +8 -0
  234. package/dist/planning/plan-instances.js.map +1 -0
  235. package/dist/planning/plan-sequence-checkpoint.d.ts +154 -0
  236. package/dist/planning/plan-sequence-checkpoint.d.ts.map +1 -0
  237. package/dist/planning/plan-sequence-checkpoint.js +449 -0
  238. package/dist/planning/plan-sequence-checkpoint.js.map +1 -0
  239. package/dist/planning/plan-sequence-execution.d.ts +72 -0
  240. package/dist/planning/plan-sequence-execution.d.ts.map +1 -0
  241. package/dist/planning/plan-sequence-execution.js +410 -0
  242. package/dist/planning/plan-sequence-execution.js.map +1 -0
  243. package/dist/planning/plan-sequence.d.ts +244 -0
  244. package/dist/planning/plan-sequence.d.ts.map +1 -0
  245. package/dist/planning/plan-sequence.js +264 -0
  246. package/dist/planning/plan-sequence.js.map +1 -0
  247. package/dist/planning/simulation.d.ts +50 -0
  248. package/dist/planning/simulation.d.ts.map +1 -0
  249. package/dist/planning/simulation.js +69 -0
  250. package/dist/planning/simulation.js.map +1 -0
  251. package/dist/registry/combat.d.ts +5 -0
  252. package/dist/registry/combat.d.ts.map +1 -0
  253. package/dist/registry/combat.js +7 -0
  254. package/dist/registry/combat.js.map +1 -0
  255. package/dist/registry/index.d.ts +138 -0
  256. package/dist/registry/index.d.ts.map +1 -0
  257. package/dist/registry/index.js +397 -0
  258. package/dist/registry/index.js.map +1 -0
  259. package/dist/rewards/index.d.ts +202 -0
  260. package/dist/rewards/index.d.ts.map +1 -0
  261. package/dist/rewards/index.js +290 -0
  262. package/dist/rewards/index.js.map +1 -0
  263. package/dist/scanning/index.d.ts +185 -0
  264. package/dist/scanning/index.d.ts.map +1 -0
  265. package/dist/scanning/index.js +207 -0
  266. package/dist/scanning/index.js.map +1 -0
  267. package/dist/starbases/actions/index.d.ts +60 -0
  268. package/dist/starbases/actions/index.d.ts.map +1 -0
  269. package/dist/starbases/actions/index.js +245 -0
  270. package/dist/starbases/actions/index.js.map +1 -0
  271. package/dist/starbases/index.d.ts +165 -0
  272. package/dist/starbases/index.d.ts.map +1 -0
  273. package/dist/starbases/index.js +213 -0
  274. package/dist/starbases/index.js.map +1 -0
  275. package/dist/world/index.d.ts +180 -0
  276. package/dist/world/index.d.ts.map +1 -0
  277. package/dist/world/index.js +265 -0
  278. package/dist/world/index.js.map +1 -0
  279. package/package.json +140 -0
@@ -0,0 +1,854 @@
1
+ import { getAddressEncoder, getBytesEncoder, getProgramDerivedAddress, isAddress, } from '@solana/kit';
2
+ import { InvalidActionInputError, SageSdkError } from '../../client/errors.js';
3
+ import { readContextRawAccounts } from '../../client/reconciliation.js';
4
+ import { deriveFleetAddress, fleetAccountDefinition, } from '../../internal/c4/fleet.js';
5
+ import { deriveFleetCrewBindingAddress, fleetCrewBindingAccountDefinition, } from '../../internal/c4/fleet-crew-binding.js';
6
+ import { deriveFactionRelationTrackerAddress, deriveRegionTrackerAddress, factionRelationTrackerAccountDefinition, regionTrackerAccountDefinition, } from '../../internal/c4/factions.js';
7
+ import { sage } from '../../internal/c4/bindings.js';
8
+ import { characterAccountDefinition, findCharacterPda, } from '../../internal/c4/identity.js';
9
+ import { deriveStarbasePlayerAddress, starbasePlayerAccountDefinition, } from '../../internal/c4/starbase.js';
10
+ import { buildStarSystemDiscoveryFilters, deriveStarSystemAddress, starSystemAccountDefinition, } from '../../internal/c4/world.js';
11
+ import { requireGameAddress } from '../../internal/game-context.js';
12
+ import { adaptDockedToIdleInstruction, adaptFleetSettleArrivalInstruction, adaptIdleToDockedInstruction, adaptStartSubwarpInstruction, adaptStopSubwarpInstruction, adaptWarpLaneInstruction, adaptWarpToCoordinateInstruction, validatePlanAuthorization, } from '../../internal/planning/authorization-adapter.js';
13
+ import { createPlan, } from '../../planning/index.js';
14
+ const FLEET_STATE_IDLE = 'fleet.state.idle';
15
+ const FLEET_STATE_MOVEMENT_STARTED = 'fleet.state.movement-started';
16
+ function movementStartSemantics(fleet) {
17
+ return [
18
+ {
19
+ fact: FLEET_STATE_IDLE,
20
+ kind: 'requires',
21
+ resource: fleet,
22
+ },
23
+ {
24
+ from: FLEET_STATE_IDLE,
25
+ kind: 'transition',
26
+ resource: fleet,
27
+ to: FLEET_STATE_MOVEMENT_STARTED,
28
+ },
29
+ ];
30
+ }
31
+ function fail(message, address) {
32
+ throw new SageSdkError('ACTION_PRECONDITION_FAILED', message, { address });
33
+ }
34
+ function validateSettleArrivalOptions(value) {
35
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
36
+ throw new InvalidActionInputError('options', value, 'an object containing only funder', 'Pass { funder } with one valid z.ink/Solana address before retrying.');
37
+ }
38
+ const record = value;
39
+ if (Object.keys(record).length !== 1 ||
40
+ !Object.hasOwn(record, 'funder') ||
41
+ typeof record.funder !== 'string' ||
42
+ !isAddress(record.funder)) {
43
+ throw new InvalidActionInputError('options.funder', record.funder, 'one valid z.ink/Solana account address and no other option fields', 'Set options.funder to the address that will fund Fleet arrival settlement before retrying.');
44
+ }
45
+ return { funder: record.funder };
46
+ }
47
+ function requireSettleArrivalState(fleet) {
48
+ switch (fleet.state.kind) {
49
+ case 'subwarp':
50
+ case 'warp':
51
+ return fleet.state.kind;
52
+ case 'idle':
53
+ return fail(`Fleet ${fleet.name} is idle and has no movement arrival to settle. Start a warp or subwarp movement, refresh the Fleet, and retry after or during that movement.`, fleet.address);
54
+ case 'docked':
55
+ return fail(`Fleet ${fleet.name} is docked and has no movement arrival to settle. Undock the Fleet, start a warp or subwarp movement, refresh it, and retry.`, fleet.address);
56
+ case 'mining':
57
+ return fail(`Fleet ${fleet.name} is mining, not moving toward an arrival. Stop mining through an appropriate game client, refresh the Fleet, and retry only after starting warp or subwarp movement.`, fleet.address);
58
+ case 'respawn':
59
+ return fail(`Fleet ${fleet.name} is in respawn, not warp or subwarp movement. Wait for the Fleet to finish respawning or advance it through an appropriate game client, refresh it, and retry only after starting movement.`, fleet.address);
60
+ case 'destroyed':
61
+ return fail(`Fleet ${fleet.name} is destroyed and cannot have a movement arrival settled. Respawn the Fleet through an appropriate game client, refresh it, and retry only after starting warp or subwarp movement.`, fleet.address);
62
+ case 'claimStakeTransfer':
63
+ return fail(`Fleet ${fleet.name} is in a claimStakeTransfer state, not movement. Finish the Claim Stake transfer, refresh the Fleet, and retry only after starting warp or subwarp movement.`, fleet.address);
64
+ }
65
+ }
66
+ const STOP_SUBWARP_REMEDIES = {
67
+ claimStakeTransfer: 'Finish the Claim Stake transfer',
68
+ destroyed: 'Respawn it',
69
+ docked: 'Undock it',
70
+ mining: 'Stop mining',
71
+ respawn: 'Wait for it to finish respawning',
72
+ warp: 'Let warp finish and settle its arrival',
73
+ };
74
+ function requireStopSubwarpState(fleet) {
75
+ const state = fleet.state.kind;
76
+ if (state === 'subwarp' || state === 'idle')
77
+ return;
78
+ fail(`Fleet ${fleet.name} is ${state} and cannot stop subwarp. ${STOP_SUBWARP_REMEDIES[state]}, start subwarp, refresh it, and retry.`, fleet.address);
79
+ }
80
+ function assertFleetOwner(fleet, authorization) {
81
+ if (fleet.ownerProfile.address !== authorization.profile) {
82
+ fail(`Fleet ${fleet.name} belongs to Profile ${fleet.ownerProfile.address}, not authorized Profile ${authorization.profile}. Use the owning Profile in PlanAuthorization, refresh the Fleet, and retry.`, fleet.address);
83
+ }
84
+ }
85
+ function requireDockedState(fleet) {
86
+ if (fleet.state.kind !== 'docked') {
87
+ fail(`Fleet ${fleet.name} is ${fleet.state.kind}, so it cannot undock; the Fleet must be docked at a Starbase. Dock the Fleet at a Starbase, refresh it, and retry.`, fleet.address);
88
+ }
89
+ return fleet.state;
90
+ }
91
+ function requireDockIdleState(fleet) {
92
+ if (fleet.state.kind !== 'idle') {
93
+ fail(`Fleet ${fleet.name} is ${fleet.state.kind}, so it cannot dock; the Fleet must be idle at a Starbase System coordinate. Finish or cancel the current Fleet action, refresh the Fleet, and retry docking.`, fleet.address);
94
+ }
95
+ }
96
+ function requireRawAccount(account, message, address) {
97
+ return account ?? fail(message, address);
98
+ }
99
+ function validateFleetRead(account, fleet, game, system, systemSequenceId) {
100
+ const data = fleetAccountDefinition.decode(account);
101
+ if (account.address !== fleet.address ||
102
+ data.game !== game ||
103
+ data.ownerProfile !== fleet.ownerProfile.address ||
104
+ data.state.kind !== 'docked' ||
105
+ data.state.system !== system ||
106
+ data.state.systemSeqId !== systemSequenceId) {
107
+ fail(`Fleet ${fleet.name} changed or no longer matches its configured Game, owner, address, or docked Star System. Refresh the Fleet and create a new undock Plan before retrying.`, fleet.address);
108
+ }
109
+ return data;
110
+ }
111
+ function validateCharacterRead(account, fleet, profile, game) {
112
+ const data = characterAccountDefinition.decode(account);
113
+ if (data.playerProfile !== profile || data.game !== game) {
114
+ fail(`Fleet ${fleet.name} cannot undock because the canonical Character does not match Profile ${profile} and the configured Game ${game}. Refresh or repair the Character registration for this Profile and Game before retrying.`, account.address);
115
+ }
116
+ return data;
117
+ }
118
+ async function validateSystemRead(account, fleet, game, systemSequenceId) {
119
+ const data = starSystemAccountDefinition.decode(account);
120
+ const canonicalAddress = await deriveStarSystemAddress(game, data.systemId);
121
+ if (data.game !== game ||
122
+ canonicalAddress !== account.address ||
123
+ data.sequenceId !== systemSequenceId) {
124
+ fail(`Fleet ${fleet.name} cannot undock because Star System ${account.address} does not match its configured Game, PDA, or docked sequence. Refresh the Fleet and System state before retrying.`, account.address);
125
+ }
126
+ return data;
127
+ }
128
+ function validateStarbasePlayerRead(account, fleet, profile, character, system, canonicalAddress, action = 'undock') {
129
+ const data = starbasePlayerAccountDefinition.decode(account);
130
+ if (account.address !== canonicalAddress ||
131
+ data.playerProfile !== profile ||
132
+ data.character !== character ||
133
+ data.system !== system) {
134
+ fail(`Fleet ${fleet.name} cannot ${action} because its Starbase Player does not match the Profile, Character, System, or canonical PDA. Refresh or repair the Starbase registration before retrying.`, account.address);
135
+ }
136
+ return data;
137
+ }
138
+ async function validateDockSystemRead(account, fleet, game, canonicalAddress, exactLocation) {
139
+ const data = starSystemAccountDefinition.decode(account);
140
+ const derivedAddress = await deriveStarSystemAddress(game, data.systemId);
141
+ if (account.address !== canonicalAddress ||
142
+ account.address !== derivedAddress ||
143
+ data.game !== game ||
144
+ !hasExactSystemLocation(account, exactLocation)) {
145
+ fail(`Fleet ${fleet.name} cannot dock because Star System ${canonicalAddress} does not match the configured Game, Fleet coordinate, or canonical PDA. Refresh the Fleet and galaxy before retrying.`, canonicalAddress);
146
+ }
147
+ return data;
148
+ }
149
+ function failNoDockableStarbase(fleet) {
150
+ return fail(`Fleet ${fleet.name} coordinate (${String(fleet.location.x)}, ${String(fleet.location.y)}) has no Starbase to dock at. Move the Fleet to a Starbase system first, refresh the Fleet and galaxy, and retry.`, fleet.address);
151
+ }
152
+ async function hashAccountState(account) {
153
+ const owner = getAddressEncoder().encode(account.owner);
154
+ const canonical = new Uint8Array(owner.length + account.data.length);
155
+ canonical.set(owner);
156
+ canonical.set(account.data, owner.length);
157
+ const digest = new Uint8Array(await globalThis.crypto.subtle.digest('SHA-256', canonical));
158
+ return [...digest].map((byte) => byte.toString(16).padStart(2, '0')).join('');
159
+ }
160
+ async function precondition(account, describes) {
161
+ return Object.freeze({
162
+ address: account.address,
163
+ dataHash: await hashAccountState(account),
164
+ describes,
165
+ kind: 'account-state',
166
+ });
167
+ }
168
+ function requireIdleState(fleet) {
169
+ if (fleet.state.kind !== 'idle') {
170
+ fail(`Fleet ${fleet.name} is ${fleet.state.kind}, so it cannot start coordinate movement; the Fleet must be idle at a coordinate. Finish or cancel the current Fleet action, refresh the Fleet, and retry.`, fleet.address);
171
+ }
172
+ }
173
+ function requireIdleLaneState(fleet) {
174
+ if (fleet.state.kind !== 'idle') {
175
+ fail(`Fleet ${fleet.name} is ${fleet.state.kind}, so it cannot warp through a lane; the Fleet must be idle at a Star System coordinate. Finish or cancel the current Fleet action, refresh the Fleet, and retry.`, fleet.address);
176
+ }
177
+ }
178
+ function validateCoordinateComponent(field, value) {
179
+ if (!Number.isFinite(value)) {
180
+ throw new InvalidActionInputError(field, value, 'a finite SAGE coordinate', `Set ${field} to a finite number before planning coordinate movement.`);
181
+ }
182
+ if (value < -128 || value >= 128) {
183
+ throw new InvalidActionInputError(field, value, 'an I8F56-encodable SAGE coordinate from -128 inclusive to 128 exclusive', `Set ${field} within the pinned movement instruction codec range before retrying.`);
184
+ }
185
+ }
186
+ function validateDestination(fleet, destination) {
187
+ validateCoordinateComponent('destination.x', destination.x);
188
+ validateCoordinateComponent('destination.y', destination.y);
189
+ if (destination.x === fleet.location.x &&
190
+ destination.y === fleet.location.y) {
191
+ failSameCoordinate(fleet, destination);
192
+ }
193
+ }
194
+ function failSameCoordinate(fleet, destination) {
195
+ return fail(`Fleet ${fleet.name} destination (${String(destination.x)}, ${String(destination.y)}) is its current coordinate. Choose a different coordinate and retry.`, fleet.address);
196
+ }
197
+ function validateCharacterForCoordinateMovement(account, fleet, profile, game, canonicalAddress, action = 'move') {
198
+ const data = characterAccountDefinition.decode(account);
199
+ if (account.address !== canonicalAddress ||
200
+ data.playerProfile !== profile ||
201
+ data.game !== game) {
202
+ fail(`Fleet ${fleet.name} cannot ${action} because the canonical Character does not match Profile ${profile} and the configured Game ${game}. Refresh or repair the Character registration for this Profile and Game before retrying.`, account.address);
203
+ }
204
+ return data;
205
+ }
206
+ async function validateIdleFleetRead(account, fleet, game, planName = 'coordinate movement') {
207
+ const data = fleetAccountDefinition.decode(account);
208
+ const canonicalAddress = await deriveFleetAddress(data);
209
+ if (account.address !== fleet.address ||
210
+ canonicalAddress !== fleet.address ||
211
+ data.game !== game ||
212
+ data.ownerProfile !== fleet.ownerProfile.address ||
213
+ data.state.kind !== 'idle' ||
214
+ data.location.x !== fleet.location.x ||
215
+ data.location.y !== fleet.location.y) {
216
+ fail(`Fleet ${fleet.name} changed or no longer matches its configured Game, owner, address, PDA, or idle coordinate. Refresh the Fleet and create a new ${planName} Plan before retrying.`, fleet.address);
217
+ }
218
+ const exact = sage.getFleetDecoder().decode(account.data);
219
+ return {
220
+ location: {
221
+ x: exact.location[0].raw,
222
+ y: exact.location[1].raw,
223
+ },
224
+ };
225
+ }
226
+ function validateRegionTrackerRead(account, fleet, game, canonicalAddress, action = 'move') {
227
+ const data = regionTrackerAccountDefinition.decode(account);
228
+ if (account.address !== canonicalAddress || data.game !== game) {
229
+ fail(`Fleet ${fleet.name} cannot ${action} because RegionTracker does not match the configured Game or canonical PDA: ${account.address}; refresh the world region data for the configured Game and retry.`, account.address);
230
+ }
231
+ return sage.getRegionTrackerDecoder().decode(account.data);
232
+ }
233
+ function validateCrewBindingRead(account, fleet, canonicalAddress) {
234
+ const data = fleetCrewBindingAccountDefinition.decode(account);
235
+ if (account.address !== canonicalAddress ||
236
+ data.fleet !== fleet.address ||
237
+ data.ownerProfile !== fleet.ownerProfile.address) {
238
+ fail(`FleetCrewBinding does not match Fleet ${fleet.address}, its owner Profile, or its canonical PDA: ${account.address}. Refresh the Fleet crew registration or remove the invalid binding before retrying.`, account.address);
239
+ }
240
+ return data;
241
+ }
242
+ function pointIsOnSegment(point, start, end) {
243
+ const cross = (point.y - start.y) * (end.x - start.x) -
244
+ (point.x - start.x) * (end.y - start.y);
245
+ return (cross === 0n &&
246
+ point.x >= (start.x < end.x ? start.x : end.x) &&
247
+ point.x <= (start.x > end.x ? start.x : end.x) &&
248
+ point.y >= (start.y < end.y ? start.y : end.y) &&
249
+ point.y <= (start.y > end.y ? start.y : end.y));
250
+ }
251
+ function regionContainsCoordinate(region, coordinate) {
252
+ const boundingBox = {
253
+ maxX: region.boundingBox.maxX.raw,
254
+ maxY: region.boundingBox.maxY.raw,
255
+ minX: region.boundingBox.minX.raw,
256
+ minY: region.boundingBox.minY.raw,
257
+ };
258
+ const border = region.border.map(([x, y]) => ({ x: x.raw, y: y.raw }));
259
+ if (border.length < 3 ||
260
+ coordinate.x < boundingBox.minX ||
261
+ coordinate.x > boundingBox.maxX ||
262
+ coordinate.y < boundingBox.minY ||
263
+ coordinate.y > boundingBox.maxY) {
264
+ return false;
265
+ }
266
+ let inside = false;
267
+ for (let index = 0; index < border.length; index += 1) {
268
+ const start = border[index];
269
+ const end = border[(index + 1) % border.length];
270
+ if (pointIsOnSegment(coordinate, start, end))
271
+ return true;
272
+ if (start.y > coordinate.y !== end.y > coordinate.y) {
273
+ const deltaY = end.y - start.y;
274
+ const pointSide = (coordinate.x - start.x) * deltaY;
275
+ const edgeSide = (coordinate.y - start.y) * (end.x - start.x);
276
+ if ((deltaY > 0n && pointSide < edgeSide) ||
277
+ (deltaY < 0n && pointSide > edgeSide)) {
278
+ inside = !inside;
279
+ }
280
+ }
281
+ }
282
+ return inside;
283
+ }
284
+ function resolveRegionId(tracker, coordinate, displayCoordinate, label, fleet, trackerAddress) {
285
+ const region = tracker.regions.unsizedList.find((candidate) => regionContainsCoordinate(candidate, coordinate));
286
+ if (region === undefined) {
287
+ fail(`Fleet ${fleet.name} ${label} coordinate (${String(displayCoordinate.x)}, ${String(displayCoordinate.y)}) cannot be mapped by RegionTracker ${trackerAddress}. Refresh the world region data or choose a coordinate inside a configured Region before retrying.`, trackerAddress);
288
+ }
289
+ return region.id;
290
+ }
291
+ function exactCoordinate(coordinate) {
292
+ const spec = { fractionalBits: 56, signed: true, totalBits: 64 };
293
+ return {
294
+ x: sage.FixedPoint.fromNumber(coordinate.x, spec).raw,
295
+ y: sage.FixedPoint.fromNumber(coordinate.y, spec).raw,
296
+ };
297
+ }
298
+ async function resolveCoordinateMovement(ctx, fleet, authorization, destination) {
299
+ const game = requireGameAddress(ctx);
300
+ const [character, regionTracker, crewBinding] = await Promise.all([
301
+ findCharacterPda(authorization.profile, game),
302
+ deriveRegionTrackerAddress(game),
303
+ deriveFleetCrewBindingAddress(fleet.address),
304
+ ]);
305
+ const [fleetCandidate, characterCandidate, regionCandidate, crewCandidate] = await readContextRawAccounts(ctx, [
306
+ fleet.address,
307
+ character,
308
+ regionTracker,
309
+ crewBinding,
310
+ ]);
311
+ const fleetAccount = requireRawAccount(fleetCandidate, `Fleet ${fleet.name} is no longer available at ${fleet.address}. Refresh the Fleet and retry planning coordinate movement.`, fleet.address);
312
+ const characterAccount = requireRawAccount(characterCandidate, `Fleet ${fleet.name} cannot move because its Profile has no canonical Character at ${character}. Create the Character for Profile ${authorization.profile} in the configured Game, refresh the Fleet, and retry.`, character);
313
+ const regionTrackerAccount = requireRawAccount(regionCandidate, `Fleet ${fleet.name} cannot move because the canonical RegionTracker ${regionTracker} is missing. Verify the configured Game and cluster, refresh the world region data, and retry.`, regionTracker);
314
+ const validatedFleet = await validateIdleFleetRead(fleetAccount, fleet, game);
315
+ const exactDestination = exactCoordinate(destination);
316
+ if (exactDestination.x === validatedFleet.location.x &&
317
+ exactDestination.y === validatedFleet.location.y) {
318
+ failSameCoordinate(fleet, destination);
319
+ }
320
+ validateCharacterForCoordinateMovement(characterAccount, fleet, authorization.profile, game, character);
321
+ const tracker = validateRegionTrackerRead(regionTrackerAccount, fleet, game, regionTracker);
322
+ if (crewCandidate !== undefined) {
323
+ validateCrewBindingRead(crewCandidate, fleet, crewBinding);
324
+ }
325
+ return {
326
+ character,
327
+ characterAccount,
328
+ ...(crewCandidate === undefined
329
+ ? {}
330
+ : { crewBinding, crewBindingAccount: crewCandidate }),
331
+ destinationRegionId: resolveRegionId(tracker, exactDestination, destination, 'destination', fleet, regionTracker),
332
+ fleetAccount,
333
+ regionTracker,
334
+ regionTrackerAccount,
335
+ startRegionId: resolveRegionId(tracker, validatedFleet.location, fleet.location, 'current', fleet, regionTracker),
336
+ };
337
+ }
338
+ async function planCoordinateMovement(mode, ctx, fleet, options) {
339
+ validatePlanAuthorization(options.authorization);
340
+ assertFleetOwner(fleet, options.authorization);
341
+ requireIdleState(fleet);
342
+ validateDestination(fleet, options.destination);
343
+ const game = requireGameAddress(ctx);
344
+ const resolved = await resolveCoordinateMovement(ctx, fleet, options.authorization, options.destination);
345
+ const common = {
346
+ authorization: options.authorization,
347
+ character: resolved.character,
348
+ ...(resolved.crewBinding === undefined
349
+ ? {}
350
+ : { crewBinding: resolved.crewBinding }),
351
+ destRegionId: resolved.destinationRegionId,
352
+ fleet: fleet.address,
353
+ game,
354
+ regionTracker: resolved.regionTracker,
355
+ startRegionId: resolved.startRegionId,
356
+ };
357
+ const instruction = mode === 'subwarp'
358
+ ? adaptStartSubwarpInstruction({
359
+ ...common,
360
+ toCoordinate: [options.destination.x, options.destination.y],
361
+ })
362
+ : adaptWarpToCoordinateInstruction({
363
+ ...common,
364
+ destination: [options.destination.x, options.destination.y],
365
+ });
366
+ const verb = mode === 'subwarp' ? 'Subwarp' : 'Warp';
367
+ const description = `${verb} fleet ${fleet.name} to coordinate (${String(options.destination.x)}, ${String(options.destination.y)}).`;
368
+ const accounts = [
369
+ await precondition(resolved.fleetAccount, `Fleet ${fleet.name} remains idle at coordinate (${String(fleet.location.x)}, ${String(fleet.location.y)}).`),
370
+ await precondition(resolved.characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
371
+ await precondition(resolved.regionTrackerAccount, `The configured RegionTracker continues to map Fleet ${fleet.name}'s current and destination coordinates.`),
372
+ ...(resolved.crewBindingAccount === undefined
373
+ ? []
374
+ : [
375
+ await precondition(resolved.crewBindingAccount, `Fleet ${fleet.name}'s optional FleetCrewBinding remains assigned to the Fleet and owner Profile.`),
376
+ ]),
377
+ ];
378
+ return createPlan({
379
+ kind: mode === 'subwarp' ? 'fleet.subwarp' : 'fleet.warp-to-coordinate',
380
+ preconditions: accounts,
381
+ steps: [
382
+ {
383
+ describes: description,
384
+ instruction,
385
+ semantics: movementStartSemantics(fleet.address),
386
+ signers: [options.authorization.authority],
387
+ },
388
+ ],
389
+ summary: description,
390
+ });
391
+ }
392
+ async function deriveCurrencyCacheAddress(game) {
393
+ const [address] = await getProgramDerivedAddress({
394
+ programAddress: sage.SAGE_PROGRAM_ADDRESS,
395
+ seeds: [
396
+ getBytesEncoder().encode(new TextEncoder().encode('CurrencyConfig')),
397
+ getAddressEncoder().encode(game),
398
+ ],
399
+ });
400
+ return address;
401
+ }
402
+ function validateCurrencyCacheRead(account, fleet, game, canonicalAddress) {
403
+ const decoder = sage.getCurrencyConfigCacheDecoder();
404
+ const discriminator = sage.CURRENCY_CONFIG_CACHE_DISCRIMINATOR;
405
+ const validDiscriminator = discriminator.every((byte, index) => account.data[index] === byte);
406
+ if (account.address !== canonicalAddress ||
407
+ account.owner !== sage.SAGE_PROGRAM_ADDRESS ||
408
+ account.data.length !== decoder.fixedSize ||
409
+ !validDiscriminator) {
410
+ fail(`Fleet ${fleet.name} cannot use a warp lane because CurrencyCache ${account.address} does not match the configured Game, canonical PDA, owner, or pinned account layout. Refresh the Game currency configuration and retry.`, account.address);
411
+ }
412
+ const decoded = decoder.decode(account.data);
413
+ if (decoded.gameId !== game) {
414
+ fail(`Fleet ${fleet.name} cannot use a warp lane because CurrencyCache ${account.address} does not match the configured Game or canonical PDA. Refresh the Game currency configuration and retry.`, account.address);
415
+ }
416
+ }
417
+ async function validateLaneSystemRead(account, fleet, game, label) {
418
+ const data = starSystemAccountDefinition.decode(account);
419
+ const canonicalAddress = await deriveStarSystemAddress(game, data.systemId);
420
+ if (data.game !== game || account.address !== canonicalAddress) {
421
+ fail(`Fleet ${fleet.name} cannot use a warp lane because the ${label} Star System ${account.address} does not match the configured Game or canonical PDA. Refresh the galaxy and retry with a Star System from the configured Game.`, account.address);
422
+ }
423
+ return data;
424
+ }
425
+ function hasExactSystemLocation(account, location) {
426
+ starSystemAccountDefinition.decode(account);
427
+ const exact = sage.getStarSystemDecoder().decode(account.data);
428
+ return (exact.coordinates[0].raw === location.x &&
429
+ exact.coordinates[1].raw === location.y);
430
+ }
431
+ function validateFactionRelationTrackerRead(account, fleet, game, canonicalAddress) {
432
+ const data = factionRelationTrackerAccountDefinition.decode(account);
433
+ if (account.address !== canonicalAddress || data.game !== game) {
434
+ fail(`Fleet ${fleet.name} cannot use a warp lane because FactionRelationTracker ${account.address} does not match the configured Game or canonical PDA. Refresh the faction relation data and retry.`, account.address);
435
+ }
436
+ }
437
+ async function resolveWarpLane(ctx, fleet, options) {
438
+ const game = requireGameAddress(ctx);
439
+ const systems = await ctx.data.discover(starSystemAccountDefinition, {
440
+ filters: buildStarSystemDiscoveryFilters(game),
441
+ strategy: 'targeted-program-accounts',
442
+ });
443
+ const sourceCandidates = systems.filter((system) => system.data.coordinates.x === fleet.location.x &&
444
+ system.data.coordinates.y === fleet.location.y);
445
+ if (sourceCandidates.length === 0) {
446
+ fail(`Fleet ${fleet.name} idle coordinate (${String(fleet.location.x)}, ${String(fleet.location.y)}) does not match a Star System in the configured Game. Refresh the galaxy or move the Fleet to a Star System before retrying.`, fleet.address);
447
+ }
448
+ const [character, currencyCache, factionRelationTracker, regionTracker] = await Promise.all([
449
+ findCharacterPda(options.authorization.profile, game),
450
+ deriveCurrencyCacheAddress(game),
451
+ deriveFactionRelationTrackerAddress(game),
452
+ deriveRegionTrackerAddress(game),
453
+ ]);
454
+ const sourceOffset = 2;
455
+ const destinationOffset = sourceOffset + sourceCandidates.length;
456
+ const reads = await readContextRawAccounts(ctx, [
457
+ fleet.address,
458
+ character,
459
+ ...sourceCandidates.map((candidate) => candidate.address),
460
+ options.destinationSystem,
461
+ currencyCache,
462
+ factionRelationTracker,
463
+ regionTracker,
464
+ ]);
465
+ const fleetCandidate = reads[0];
466
+ const characterCandidate = reads[1];
467
+ const destinationCandidate = reads[destinationOffset];
468
+ const currencyCandidate = reads[destinationOffset + 1];
469
+ const factionCandidate = reads[destinationOffset + 2];
470
+ const regionCandidate = reads[destinationOffset + 3];
471
+ const fleetAccount = requireRawAccount(fleetCandidate, `Fleet ${fleet.name} is no longer available at ${fleet.address}. Refresh the Fleet and retry planning the warp lane.`, fleet.address);
472
+ const validatedFleet = await validateIdleFleetRead(fleetAccount, fleet, game);
473
+ const exactSourceCandidates = sourceCandidates.flatMap((candidate, index) => {
474
+ const account = reads[sourceOffset + index];
475
+ return account !== undefined &&
476
+ hasExactSystemLocation(account, validatedFleet.location)
477
+ ? [{ account, address: candidate.address }]
478
+ : [];
479
+ });
480
+ if (exactSourceCandidates.length !== 1) {
481
+ if (sourceCandidates.length === 1) {
482
+ const sourceSystem = sourceCandidates[0].address;
483
+ const sourceAccount = requireRawAccount(reads[sourceOffset], `Fleet ${fleet.name} cannot use a warp lane because source Star System ${sourceSystem} is missing. Refresh the Fleet and galaxy, then retry.`, sourceSystem);
484
+ const source = starSystemAccountDefinition.decode(sourceAccount);
485
+ fail(`Fleet ${fleet.name} idle coordinate (${String(fleet.location.x)}, ${String(fleet.location.y)}) no longer matches source Star System ${source.name}. Refresh the Fleet and galaxy before retrying.`, sourceSystem);
486
+ }
487
+ fail(`Fleet ${fleet.name} idle coordinate (${String(fleet.location.x)}, ${String(fleet.location.y)}) matches multiple Star Systems in the configured Game. Refresh the galaxy and repair the contradictory System data before retrying.`, fleet.address);
488
+ }
489
+ const { account: sourceAccount, address: sourceSystem } = exactSourceCandidates[0];
490
+ const characterAccount = requireRawAccount(characterCandidate, `Fleet ${fleet.name} cannot use a warp lane because its Profile has no canonical Character at ${character}. Create the Character for Profile ${options.authorization.profile} in the configured Game, refresh the Fleet, and retry.`, character);
491
+ const destinationAccount = requireRawAccount(destinationCandidate, `Fleet ${fleet.name} cannot use a warp lane because destination Star System ${options.destinationSystem} is missing. Choose a Star System in the configured Game, refresh the galaxy, and retry.`, options.destinationSystem);
492
+ const currencyCacheAccount = requireRawAccount(currencyCandidate, `Fleet ${fleet.name} cannot use a warp lane because canonical CurrencyCache ${currencyCache} is missing. Verify the configured Game and cluster, refresh the Game currency configuration, and retry.`, currencyCache);
493
+ const factionRelationTrackerAccount = requireRawAccount(factionCandidate, `Fleet ${fleet.name} cannot use a warp lane because canonical FactionRelationTracker ${factionRelationTracker} is missing. Verify the configured Game and cluster, refresh the faction relation data, and retry.`, factionRelationTracker);
494
+ const regionTrackerAccount = requireRawAccount(regionCandidate, `Fleet ${fleet.name} cannot use a warp lane because canonical RegionTracker ${regionTracker} is missing. Verify the configured Game and cluster, refresh the world region data, and retry.`, regionTracker);
495
+ validateCharacterForCoordinateMovement(characterAccount, fleet, options.authorization.profile, game, character);
496
+ const source = await validateLaneSystemRead(sourceAccount, fleet, game, 'source');
497
+ const destination = await validateLaneSystemRead(destinationAccount, fleet, game, 'destination');
498
+ validateCurrencyCacheRead(currencyCacheAccount, fleet, game, currencyCache);
499
+ validateFactionRelationTrackerRead(factionRelationTrackerAccount, fleet, game, factionRelationTracker);
500
+ validateRegionTrackerRead(regionTrackerAccount, fleet, game, regionTracker);
501
+ if (sourceSystem === options.destinationSystem) {
502
+ fail(`Fleet ${fleet.name} destination Star System ${source.name} is its current Star System. Choose a different connected Star System and retry.`, options.destinationSystem);
503
+ }
504
+ if (!source.connections.some((connection) => connection.systemId === destination.systemId)) {
505
+ fail(`Star System ${source.name} does not have a warp lane to Star System ${destination.name}. Choose a connected Star System from the source System's decoded connections and retry.`, options.destinationSystem);
506
+ }
507
+ return {
508
+ character,
509
+ characterAccount,
510
+ currencyCache,
511
+ currencyCacheAccount,
512
+ destination,
513
+ destinationAccount,
514
+ factionRelationTracker,
515
+ factionRelationTrackerAccount,
516
+ fleetAccount,
517
+ regionTracker,
518
+ regionTrackerAccount,
519
+ source,
520
+ sourceAccount,
521
+ sourceSystem,
522
+ };
523
+ }
524
+ /** Plans one explicit connected-system Fleet warp without local economics estimates.
525
+ *
526
+ * The planner resolves the source Star System from the idle Fleet coordinate,
527
+ * proves connectivity from that System's decoded connections, and resolves all
528
+ * generated instruction accounts internally.
529
+ *
530
+ * @example
531
+ * ```ts
532
+ * declare const ctx: SageContext;
533
+ * declare const fleet: FleetSnapshot;
534
+ * declare const destinationSystem: Address;
535
+ * declare const authorization: PlanAuthorization;
536
+ * const plan = await planFleetWarpLane(ctx, fleet, {
537
+ * authorization,
538
+ * destinationSystem,
539
+ * });
540
+ * ```
541
+ */
542
+ export async function planFleetWarpLane(ctx, fleet, options) {
543
+ validatePlanAuthorization(options.authorization);
544
+ assertFleetOwner(fleet, options.authorization);
545
+ requireIdleLaneState(fleet);
546
+ const game = requireGameAddress(ctx);
547
+ const resolved = await resolveWarpLane(ctx, fleet, options);
548
+ const description = `Warp fleet ${fleet.name} from Star System ${resolved.source.name} to Star System ${resolved.destination.name} via warp lane.`;
549
+ return createPlan({
550
+ kind: 'fleet.warp-lane',
551
+ preconditions: await Promise.all([
552
+ precondition(resolved.fleetAccount, `Fleet ${fleet.name} remains idle at source Star System ${resolved.source.name}.`),
553
+ precondition(resolved.characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
554
+ precondition(resolved.sourceAccount, `Source Star System ${resolved.source.name} remains at Fleet ${fleet.name}'s coordinate with a connection to system id ${String(resolved.destination.systemId)}.`),
555
+ precondition(resolved.destinationAccount, `Destination Star System ${resolved.destination.name} remains system id ${String(resolved.destination.systemId)} in the configured Game.`),
556
+ precondition(resolved.currencyCacheAccount, 'The canonical CurrencyCache remains assigned to the configured Game.'),
557
+ precondition(resolved.factionRelationTrackerAccount, 'The canonical FactionRelationTracker remains assigned to the configured Game.'),
558
+ precondition(resolved.regionTrackerAccount, 'The canonical RegionTracker remains assigned to the configured Game.'),
559
+ ]),
560
+ steps: [
561
+ {
562
+ describes: description,
563
+ instruction: adaptWarpLaneInstruction({
564
+ authorization: options.authorization,
565
+ character: resolved.character,
566
+ currencyCache: resolved.currencyCache,
567
+ factionRelationTracker: resolved.factionRelationTracker,
568
+ fleet: fleet.address,
569
+ fromSystem: resolved.sourceSystem,
570
+ game,
571
+ regionTracker: resolved.regionTracker,
572
+ toSystem: options.destinationSystem,
573
+ }),
574
+ semantics: movementStartSemantics(fleet.address),
575
+ signers: [options.authorization.authority],
576
+ },
577
+ ],
578
+ summary: description,
579
+ });
580
+ }
581
+ /** Plans one explicit Fleet subwarp to a coordinate without movement estimates.
582
+ *
583
+ * @example
584
+ * ```ts
585
+ * declare const ctx: SageContext;
586
+ * declare const fleet: FleetSnapshot;
587
+ * declare const authorization: PlanAuthorization;
588
+ * const plan = await planFleetSubwarp(ctx, fleet, {
589
+ * authorization,
590
+ * destination: { x: 12, y: -7 },
591
+ * });
592
+ * ```
593
+ */
594
+ export function planFleetSubwarp(ctx, fleet, options) {
595
+ return planCoordinateMovement('subwarp', ctx, fleet, options);
596
+ }
597
+ /** Plans one explicit Fleet coordinate warp without movement estimates.
598
+ *
599
+ * @example
600
+ * ```ts
601
+ * declare const ctx: SageContext;
602
+ * declare const fleet: FleetSnapshot;
603
+ * declare const authorization: PlanAuthorization;
604
+ * const plan = await planFleetWarpToCoordinate(ctx, fleet, {
605
+ * authorization,
606
+ * destination: { x: 12, y: -7 },
607
+ * });
608
+ * ```
609
+ */
610
+ export function planFleetWarpToCoordinate(ctx, fleet, options) {
611
+ return planCoordinateMovement('warp', ctx, fleet, options);
612
+ }
613
+ /** Plans settlement of one Fleet warp or subwarp arrival.
614
+ *
615
+ * Settlement is distinct from stopping subwarp early: the program completes an
616
+ * elapsed movement, while an in-transit call is a measured safe no-op. Subwarp
617
+ * settlement charges the fuel recorded when movement started; warp fuel was
618
+ * already charged up front. Settlement does not clear the independent warp
619
+ * cooldown, so an arrived Fleet can be idle but still unable to warp again.
620
+ *
621
+ * @example
622
+ * ```ts
623
+ * declare const ctx: SageContext;
624
+ * declare const fleet: FleetSnapshot;
625
+ * declare const funder: Address;
626
+ * const plan = await planFleetSettleArrival(ctx, fleet, { funder });
627
+ * console.log(plan.describe());
628
+ * ```
629
+ */
630
+ export async function planFleetSettleArrival(ctx, fleet, input) {
631
+ const { funder } = validateSettleArrivalOptions(input);
632
+ const movement = requireSettleArrivalState(fleet);
633
+ const game = requireGameAddress(ctx);
634
+ const character = await findCharacterPda(fleet.ownerProfile.address, game);
635
+ const description = movement === 'subwarp'
636
+ ? `Settle fleet ${fleet.name} after subwarp arrival and charge the fuel recorded when subwarp started.`
637
+ : `Settle fleet ${fleet.name} after warp arrival; warp cooldown remains independent.`;
638
+ return createPlan({
639
+ kind: 'fleet.settle-arrival',
640
+ preconditions: [],
641
+ steps: [
642
+ {
643
+ describes: description,
644
+ instruction: adaptFleetSettleArrivalInstruction({
645
+ character,
646
+ fleet: fleet.address,
647
+ funder,
648
+ game,
649
+ }),
650
+ signers: [funder],
651
+ },
652
+ ],
653
+ summary: `Settle arrival for fleet ${fleet.name}.`,
654
+ });
655
+ }
656
+ /** Plans an early Fleet subwarp stop without signing, simulation, or write RPC.
657
+ *
658
+ * This operation stops an in-progress subwarp before arrival. Use
659
+ * {@link planFleetSettleArrival} for a trip already known to have completed;
660
+ * callers should not deliberately stop a completed trip. An idle Fleet is
661
+ * still accepted so the program's benign no-op handles the normal race where
662
+ * subwarp completes between planning and execution.
663
+ *
664
+ * @example
665
+ * ```ts
666
+ * declare const ctx: SageContext;
667
+ * declare const fleet: FleetSnapshot;
668
+ * declare const authorization: PlanAuthorization;
669
+ * const plan = await planFleetStopSubwarp(ctx, fleet, { authorization });
670
+ * console.log(plan.describe());
671
+ * ```
672
+ */
673
+ export async function planFleetStopSubwarp(ctx, fleet, options) {
674
+ validatePlanAuthorization(options.authorization);
675
+ assertFleetOwner(fleet, options.authorization);
676
+ requireStopSubwarpState(fleet);
677
+ const game = requireGameAddress(ctx);
678
+ const character = await findCharacterPda(options.authorization.profile, game);
679
+ const regionTracker = await deriveRegionTrackerAddress(game);
680
+ const [regionCandidate] = await readContextRawAccounts(ctx, [regionTracker]);
681
+ const regionTrackerAccount = requireRawAccount(regionCandidate, `Canonical RegionTracker ${regionTracker} is missing. Verify the configured Game and cluster, refresh world region data, and retry stopping Fleet ${fleet.name}.`, regionTracker);
682
+ validateRegionTrackerRead(regionTrackerAccount, fleet, game, regionTracker, 'stop subwarp');
683
+ const description = `Stop subwarp early for fleet ${fleet.name}; leave it idle if the trip already completed.`;
684
+ return createPlan({
685
+ kind: 'fleet.stop-subwarp',
686
+ preconditions: [],
687
+ steps: [
688
+ {
689
+ describes: description,
690
+ instruction: adaptStopSubwarpInstruction({
691
+ authorization: options.authorization,
692
+ character,
693
+ fleet: fleet.address,
694
+ game,
695
+ regionTracker,
696
+ }),
697
+ signers: [options.authorization.authority],
698
+ },
699
+ ],
700
+ summary: `Stop subwarp for fleet ${fleet.name}.`,
701
+ });
702
+ }
703
+ /** Plans one explicit Fleet undock without signing, simulation, or write RPC.
704
+ *
705
+ * The planner accepts a loaded Fleet snapshot, resolves its canonical Character,
706
+ * docked Star System, and Starbase Player internally, and returns one inspectable
707
+ * docked-to-idle Plan.
708
+ *
709
+ * @example
710
+ * ```ts
711
+ * declare const ctx: SageContext;
712
+ * declare const fleet: FleetSnapshot;
713
+ * declare const authorization: PlanAuthorization;
714
+ * const plan = await planFleetUndock(ctx, fleet, { authorization });
715
+ * console.log(plan.describe());
716
+ * ```
717
+ */
718
+ export async function planFleetUndock(ctx, fleet, options) {
719
+ validatePlanAuthorization(options.authorization);
720
+ assertFleetOwner(fleet, options.authorization);
721
+ const docked = requireDockedState(fleet);
722
+ const game = requireGameAddress(ctx);
723
+ const characterAddress = await findCharacterPda(options.authorization.profile, game);
724
+ const starbasePlayerAddress = await deriveStarbasePlayerAddress(docked.system.address, characterAddress);
725
+ const [fleetCandidate, characterCandidate, systemCandidate, playerCandidate] = await readContextRawAccounts(ctx, [
726
+ fleet.address,
727
+ characterAddress,
728
+ docked.system.address,
729
+ starbasePlayerAddress,
730
+ ]);
731
+ const fleetAccount = requireRawAccount(fleetCandidate, `Fleet ${fleet.name} is no longer available at ${fleet.address}. Refresh the Fleet and retry planning the undock action.`, fleet.address);
732
+ const characterAccount = requireRawAccount(characterCandidate, `Fleet ${fleet.name} cannot undock because its Profile has no canonical Character at ${characterAddress}. Create the Character for Profile ${options.authorization.profile} in the configured Game, refresh the Fleet, and retry.`, characterAddress);
733
+ const systemAccount = requireRawAccount(systemCandidate, `Fleet ${fleet.name} is docked at Star System ${docked.system.address}, but that System account is missing. Verify the configured Game and cluster, refresh the Fleet, and retry.`, docked.system.address);
734
+ const playerAccount = requireRawAccount(playerCandidate, `Fleet ${fleet.name} cannot undock because its Starbase Player registration is missing at ${starbasePlayerAddress}. Register the Character at this Starbase, refresh the Fleet, and retry.`, starbasePlayerAddress);
735
+ validateFleetRead(fleetAccount, fleet, game, docked.system.address, docked.systemSeqId);
736
+ validateCharacterRead(characterAccount, fleet, options.authorization.profile, game);
737
+ const system = await validateSystemRead(systemAccount, fleet, game, docked.systemSeqId);
738
+ validateStarbasePlayerRead(playerAccount, fleet, options.authorization.profile, characterAddress, docked.system.address, starbasePlayerAddress);
739
+ const instruction = adaptDockedToIdleInstruction({
740
+ authorization: options.authorization,
741
+ fleet: fleet.address,
742
+ game,
743
+ systemAndStarbasePlayerStarbasePlayer: starbasePlayerAddress,
744
+ systemAndStarbasePlayerSystem: docked.system.address,
745
+ });
746
+ const description = `Undock fleet ${fleet.name} from the Starbase in Star System ${system.name}.`;
747
+ return createPlan({
748
+ kind: 'fleet.undock',
749
+ preconditions: await Promise.all([
750
+ precondition(fleetAccount, `Fleet ${fleet.name} remains docked at Star System ${system.name}.`),
751
+ precondition(characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
752
+ precondition(systemAccount, `Star System ${system.name} remains the docked System for Fleet ${fleet.name}.`),
753
+ precondition(playerAccount, `The Starbase Player remains registered for Fleet ${fleet.name} at Star System ${system.name}.`),
754
+ ]),
755
+ steps: [
756
+ {
757
+ describes: description,
758
+ instruction,
759
+ signers: [options.authorization.authority],
760
+ },
761
+ ],
762
+ summary: `Undock fleet ${fleet.name} from Star System ${system.name}.`,
763
+ });
764
+ }
765
+ /** Plans one explicit Fleet dock without signing, simulation, or write RPC.
766
+ *
767
+ * The planner accepts a loaded idle Fleet snapshot, resolves the only canonical
768
+ * Character, Star System at its current coordinate, and Starbase Player
769
+ * internally, and returns one inspectable idle-to-docked Plan.
770
+ *
771
+ * @example
772
+ * ```ts
773
+ * declare const ctx: SageContext;
774
+ * declare const fleet: FleetSnapshot;
775
+ * declare const authorization: PlanAuthorization;
776
+ * const plan = await planFleetDock(ctx, fleet, { authorization });
777
+ * console.log(plan.describe());
778
+ * ```
779
+ */
780
+ export async function planFleetDock(ctx, fleet, options) {
781
+ validatePlanAuthorization(options.authorization);
782
+ assertFleetOwner(fleet, options.authorization);
783
+ requireDockIdleState(fleet);
784
+ const game = requireGameAddress(ctx);
785
+ const [fleetCandidate] = await readContextRawAccounts(ctx, [fleet.address]);
786
+ const fleetAccount = requireRawAccount(fleetCandidate, `Fleet ${fleet.name} is no longer available at ${fleet.address}. Refresh the Fleet and retry planning the dock action.`, fleet.address);
787
+ const validatedFleet = await validateIdleFleetRead(fleetAccount, fleet, game, 'dock');
788
+ const characterAddress = await findCharacterPda(options.authorization.profile, game);
789
+ const [characterCandidate] = await readContextRawAccounts(ctx, [
790
+ characterAddress,
791
+ ]);
792
+ const characterAccount = requireRawAccount(characterCandidate, `Fleet ${fleet.name} cannot dock because its Profile has no canonical Character at ${characterAddress}. Create the Character for Profile ${options.authorization.profile} in the configured Game, refresh the Fleet, and retry.`, characterAddress);
793
+ validateCharacterForCoordinateMovement(characterAccount, fleet, options.authorization.profile, game, characterAddress, 'dock');
794
+ const discoveredSystems = await ctx.data.discover(starSystemAccountDefinition, {
795
+ filters: buildStarSystemDiscoveryFilters(game),
796
+ strategy: 'targeted-program-accounts',
797
+ });
798
+ const matchingSystems = discoveredSystems.filter((system) => system.data.coordinates.x === fleet.location.x &&
799
+ system.data.coordinates.y === fleet.location.y);
800
+ if (matchingSystems.length === 0)
801
+ failNoDockableStarbase(fleet);
802
+ const systemCandidates = await readContextRawAccounts(ctx, matchingSystems.map((candidate) => candidate.address));
803
+ const exactSystems = matchingSystems.flatMap((candidate, index) => {
804
+ const account = systemCandidates[index];
805
+ return account !== undefined &&
806
+ hasExactSystemLocation(account, validatedFleet.location)
807
+ ? [{ account, address: candidate.address }]
808
+ : [];
809
+ });
810
+ if (exactSystems.length !== 1) {
811
+ if (matchingSystems.length === 1) {
812
+ const systemAddress = matchingSystems[0].address;
813
+ const systemAccount = systemCandidates[0] ?? failNoDockableStarbase(fleet);
814
+ await validateDockSystemRead(systemAccount, fleet, game, systemAddress, validatedFleet.location);
815
+ }
816
+ fail(`Fleet ${fleet.name} coordinate (${String(fleet.location.x)}, ${String(fleet.location.y)}) matches multiple Star Systems in the configured Game, so there is no canonical Starbase to dock at. Refresh the galaxy and repair the contradictory System data before retrying.`, fleet.address);
817
+ }
818
+ const { account: systemAccount, address: systemAddress } = exactSystems[0];
819
+ const system = await validateDockSystemRead(systemAccount, fleet, game, systemAddress, validatedFleet.location);
820
+ if (system.starbase === undefined)
821
+ failNoDockableStarbase(fleet);
822
+ const starbasePlayerAddress = await deriveStarbasePlayerAddress(systemAddress, characterAddress);
823
+ const [playerCandidate] = await readContextRawAccounts(ctx, [
824
+ starbasePlayerAddress,
825
+ ]);
826
+ const playerAccount = requireRawAccount(playerCandidate, `Fleet ${fleet.name} cannot dock because Character ${characterAddress} is not registered at Starbase ${system.name} in Star System ${systemAddress}. Register the Character at this Starbase before docking, refresh the Fleet, and retry.`, starbasePlayerAddress);
827
+ validateStarbasePlayerRead(playerAccount, fleet, options.authorization.profile, characterAddress, systemAddress, starbasePlayerAddress, 'dock');
828
+ const instruction = adaptIdleToDockedInstruction({
829
+ authorization: options.authorization,
830
+ fleet: fleet.address,
831
+ game,
832
+ systemAndStarbasePlayerStarbasePlayer: starbasePlayerAddress,
833
+ systemAndStarbasePlayerSystem: systemAddress,
834
+ });
835
+ const description = `Dock fleet ${fleet.name} at the Starbase in Star System ${system.name}.`;
836
+ return createPlan({
837
+ kind: 'fleet.dock',
838
+ preconditions: await Promise.all([
839
+ precondition(fleetAccount, `Fleet ${fleet.name} remains idle at Starbase System ${system.name} coordinate (${String(fleet.location.x)}, ${String(fleet.location.y)}).`),
840
+ precondition(characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
841
+ precondition(systemAccount, `Star System ${system.name} remains the Starbase System at Fleet ${fleet.name} coordinate (${String(fleet.location.x)}, ${String(fleet.location.y)}).`),
842
+ precondition(playerAccount, `The Starbase Player remains registered for Fleet ${fleet.name} at Starbase ${system.name}.`),
843
+ ]),
844
+ steps: [
845
+ {
846
+ describes: description,
847
+ instruction,
848
+ signers: [options.authorization.authority],
849
+ },
850
+ ],
851
+ summary: `Dock fleet ${fleet.name} at Starbase ${system.name}.`,
852
+ });
853
+ }
854
+ //# sourceMappingURL=index.js.map