@aephia/atlas-kit 0.4.0 → 0.6.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -29
- package/dist/cargo/index.d.ts +1 -0
- package/dist/cargo/index.d.ts.map +1 -1
- package/dist/cargo/index.js +1 -0
- package/dist/cargo/index.js.map +1 -1
- package/dist/claim-stakes/actions/index.d.ts +393 -0
- package/dist/claim-stakes/actions/index.d.ts.map +1 -0
- package/dist/claim-stakes/actions/index.js +1747 -0
- package/dist/claim-stakes/actions/index.js.map +1 -0
- package/dist/claim-stakes/index.d.ts +64 -0
- package/dist/claim-stakes/index.d.ts.map +1 -1
- package/dist/claim-stakes/index.js +49 -0
- package/dist/claim-stakes/index.js.map +1 -1
- package/dist/client/context.d.ts.map +1 -1
- package/dist/client/context.js +2 -1
- package/dist/client/context.js.map +1 -1
- package/dist/client/errors.d.ts +12 -3
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +11 -0
- package/dist/client/errors.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/lifecycle.d.ts +2 -0
- package/dist/client/lifecycle.d.ts.map +1 -1
- package/dist/client/lifecycle.js +31 -0
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/combat/actions/index.d.ts +239 -0
- package/dist/combat/actions/index.d.ts.map +1 -0
- package/dist/combat/actions/index.js +1146 -0
- package/dist/combat/actions/index.js.map +1 -0
- package/dist/crafting/actions/index.d.ts +140 -0
- package/dist/crafting/actions/index.d.ts.map +1 -0
- package/dist/crafting/actions/index.js +728 -0
- package/dist/crafting/actions/index.js.map +1 -0
- package/dist/crafting/index.d.ts +74 -8
- package/dist/crafting/index.d.ts.map +1 -1
- package/dist/crafting/index.js +55 -9
- package/dist/crafting/index.js.map +1 -1
- package/dist/fleets/actions/index.d.ts +5 -4
- package/dist/fleets/actions/index.d.ts.map +1 -1
- package/dist/fleets/actions/index.js +85 -58
- package/dist/fleets/actions/index.js.map +1 -1
- package/dist/fleets/index.d.ts +1 -0
- package/dist/fleets/index.d.ts.map +1 -1
- package/dist/fleets/index.js +1 -0
- package/dist/fleets/index.js.map +1 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/c4/claim-stake-building-definitions.d.ts +50 -0
- package/dist/internal/c4/claim-stake-building-definitions.d.ts.map +1 -0
- package/dist/internal/c4/claim-stake-building-definitions.js +56 -0
- package/dist/internal/c4/claim-stake-building-definitions.js.map +1 -0
- package/dist/internal/c4/claim-stake-configuration.d.ts +17 -0
- package/dist/internal/c4/claim-stake-configuration.d.ts.map +1 -0
- package/dist/internal/c4/claim-stake-configuration.js +47 -0
- package/dist/internal/c4/claim-stake-configuration.js.map +1 -0
- package/dist/internal/c4/claim-stake-definitions.d.ts +37 -0
- package/dist/internal/c4/claim-stake-definitions.d.ts.map +1 -0
- package/dist/internal/c4/claim-stake-definitions.js +77 -0
- package/dist/internal/c4/claim-stake-definitions.js.map +1 -0
- package/dist/internal/c4/fleet.d.ts.map +1 -1
- package/dist/internal/c4/fleet.js +2 -102
- package/dist/internal/c4/fleet.js.map +1 -1
- package/dist/internal/c4/game-definition-layout.d.ts +11 -0
- package/dist/internal/c4/game-definition-layout.d.ts.map +1 -0
- package/dist/internal/c4/game-definition-layout.js +32 -0
- package/dist/internal/c4/game-definition-layout.js.map +1 -0
- package/dist/internal/c4/game.d.ts +13 -1
- package/dist/internal/c4/game.d.ts.map +1 -1
- package/dist/internal/c4/game.js +19 -37
- package/dist/internal/c4/game.js.map +1 -1
- package/dist/internal/c4/profile-faction.d.ts +6 -0
- package/dist/internal/c4/profile-faction.d.ts.map +1 -1
- package/dist/internal/c4/profile-faction.js +22 -1
- package/dist/internal/c4/profile-faction.js.map +1 -1
- package/dist/internal/c4/ship-stats.d.ts +5 -0
- package/dist/internal/c4/ship-stats.d.ts.map +1 -0
- package/dist/internal/c4/ship-stats.js +106 -0
- package/dist/internal/c4/ship-stats.js.map +1 -0
- package/dist/internal/c4/world.d.ts +23 -1
- package/dist/internal/c4/world.d.ts.map +1 -1
- package/dist/internal/c4/world.js +53 -1
- package/dist/internal/c4/world.js.map +1 -1
- package/dist/internal/crafting-process-state.d.ts +15 -0
- package/dist/internal/crafting-process-state.d.ts.map +1 -0
- package/dist/internal/crafting-process-state.js +12 -0
- package/dist/internal/crafting-process-state.js.map +1 -0
- package/dist/internal/planning/authorization-adapter.d.ts +74 -0
- package/dist/internal/planning/authorization-adapter.d.ts.map +1 -1
- package/dist/internal/planning/authorization-adapter.js +382 -0
- package/dist/internal/planning/authorization-adapter.js.map +1 -1
- package/dist/internal/read-meta.d.ts +39 -3
- package/dist/internal/read-meta.d.ts.map +1 -1
- package/dist/internal/read-meta.js +0 -10
- package/dist/internal/read-meta.js.map +1 -1
- package/dist/loyalty/actions/index.d.ts +40 -0
- package/dist/loyalty/actions/index.d.ts.map +1 -0
- package/dist/loyalty/actions/index.js +159 -0
- package/dist/loyalty/actions/index.js.map +1 -0
- package/dist/markets/actions/index.d.ts +100 -0
- package/dist/markets/actions/index.d.ts.map +1 -0
- package/dist/markets/actions/index.js +417 -0
- package/dist/markets/actions/index.js.map +1 -0
- package/dist/planning/assembly.d.ts +4 -0
- package/dist/planning/assembly.d.ts.map +1 -1
- package/dist/planning/assembly.js +13 -0
- package/dist/planning/assembly.js.map +1 -1
- package/dist/planning/composition.d.ts.map +1 -1
- package/dist/planning/composition.js +21 -2
- package/dist/planning/composition.js.map +1 -1
- package/dist/planning/execution.d.ts.map +1 -1
- package/dist/planning/execution.js +7 -4
- package/dist/planning/execution.js.map +1 -1
- package/dist/planning/index.d.ts +27 -8
- package/dist/planning/index.d.ts.map +1 -1
- package/dist/planning/index.js +57 -13
- package/dist/planning/index.js.map +1 -1
- package/dist/planning/plan-sequence-execution.d.ts +26 -4
- package/dist/planning/plan-sequence-execution.d.ts.map +1 -1
- package/dist/planning/plan-sequence-execution.js +26 -4
- package/dist/planning/plan-sequence-execution.js.map +1 -1
- package/dist/planning/plan-sequence.d.ts +20 -3
- package/dist/planning/plan-sequence.d.ts.map +1 -1
- package/dist/planning/plan-sequence.js +20 -3
- package/dist/planning/plan-sequence.js.map +1 -1
- package/dist/planning/simulation.d.ts.map +1 -1
- package/dist/planning/simulation.js +2 -1
- package/dist/planning/simulation.js.map +1 -1
- package/dist/registry/cargo.d.ts +10 -0
- package/dist/registry/cargo.d.ts.map +1 -0
- package/dist/registry/cargo.js +15 -0
- package/dist/registry/cargo.js.map +1 -0
- package/dist/registry/claim-stakes.d.ts +11 -0
- package/dist/registry/claim-stakes.d.ts.map +1 -0
- package/dist/registry/claim-stakes.js +15 -0
- package/dist/registry/claim-stakes.js.map +1 -0
- package/dist/registry/definition-section-snapshot.d.ts +42 -0
- package/dist/registry/definition-section-snapshot.d.ts.map +1 -0
- package/dist/registry/definition-section-snapshot.js +214 -0
- package/dist/registry/definition-section-snapshot.js.map +1 -0
- package/dist/registry/index.d.ts +114 -7
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +372 -101
- package/dist/registry/index.js.map +1 -1
- package/dist/registry/publication.d.ts +8 -0
- package/dist/registry/publication.d.ts.map +1 -0
- package/dist/registry/publication.js +10 -0
- package/dist/registry/publication.js.map +1 -0
- package/dist/registry/ship-stats-snapshot.d.ts +8 -0
- package/dist/registry/ship-stats-snapshot.d.ts.map +1 -0
- package/dist/registry/ship-stats-snapshot.js +154 -0
- package/dist/registry/ship-stats-snapshot.js.map +1 -0
- package/dist/world/index.d.ts +22 -1
- package/dist/world/index.d.ts.map +1 -1
- package/dist/world/index.js +24 -1
- package/dist/world/index.js.map +1 -1
- package/package.json +21 -1
package/dist/registry/index.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import { encodeDefinitionSectionSnapshot, hydrateDefinitionSectionSnapshot, } from './definition-section-snapshot.js';
|
|
2
|
+
import { captureSectionRevisions, publishIfCurrent } from './publication.js';
|
|
3
|
+
import { attachReadMeta, } from '../internal/read-meta.js';
|
|
4
|
+
import { attachJsonSerialization, toJsonSafeValue } from '../internal/json.js';
|
|
5
|
+
import { hydrateShipStats } from './ship-stats-snapshot.js';
|
|
1
6
|
import { isAddress } from '@solana/kit';
|
|
2
|
-
import { AccountNotFoundError, MissingGameContextError, ProviderError, RegistryOutOfSyncError, } from '../client/errors.js';
|
|
3
|
-
import { assertContextActive } from '../client/lifecycle.js';
|
|
7
|
+
import { AccountNotFoundError, MissingGameContextError, InvalidDefinitionSectionError, ProviderError, RegistryOutOfSyncError, } from '../client/errors.js';
|
|
8
|
+
import { assertContextActive, waitForRegistry } from '../client/lifecycle.js';
|
|
4
9
|
import { deepFreeze, validateImmutableTree, } from '../internal/immutable-tree.js';
|
|
5
10
|
import { decodeGameRegistrySections, } from '../internal/c4/game.js';
|
|
6
11
|
const SEQUENCE_MISMATCH_RETRY_DELAY_MS = 1_000;
|
|
7
|
-
//
|
|
12
|
+
// D029/D055 tolerate newer ship definitions for older callers; the resolved
|
|
13
|
+
// definitionSequenceId exposes that skew. Every other section remains strict.
|
|
8
14
|
const SECTIONS_TOLERATING_OLDER_CALLERS = new Set(['ships']);
|
|
9
15
|
const registryStates = new WeakMap();
|
|
10
16
|
function stateFor(context) {
|
|
@@ -15,7 +21,7 @@ function stateFor(context) {
|
|
|
15
21
|
confirmedSequenceMismatches: {},
|
|
16
22
|
confirmingMissing: new Set(),
|
|
17
23
|
confirmingSequenceIds: {},
|
|
18
|
-
|
|
24
|
+
revisions: {},
|
|
19
25
|
refresh: new Set(),
|
|
20
26
|
sections: {},
|
|
21
27
|
};
|
|
@@ -26,7 +32,33 @@ function stateFor(context) {
|
|
|
26
32
|
function freezeDefinitions(definitions) {
|
|
27
33
|
return Object.freeze(definitions.map((definition) => deepFreeze({ ...definition })));
|
|
28
34
|
}
|
|
35
|
+
function freezeShips(definitions, sequenceId) {
|
|
36
|
+
return Object.freeze(definitions.map((definition) => {
|
|
37
|
+
const value = deepFreeze({
|
|
38
|
+
...definition,
|
|
39
|
+
definitionSequenceId: sequenceId,
|
|
40
|
+
});
|
|
41
|
+
return Object.freeze(attachJsonSerialization({ ...value }));
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
function isPersistedDefinitionSection(section) {
|
|
45
|
+
return (section === 'claimStakes' ||
|
|
46
|
+
section === 'buildings' ||
|
|
47
|
+
section === 'craftingHabs' ||
|
|
48
|
+
section === 'habBuildings');
|
|
49
|
+
}
|
|
50
|
+
function freezeCatalog(data) {
|
|
51
|
+
const catalog = Object.freeze(data.definitions.map((definition) => Object.freeze(attachJsonSerialization({ ...deepFreeze(definition) }))));
|
|
52
|
+
return {
|
|
53
|
+
catalog,
|
|
54
|
+
definitions: new Map(catalog.map((definition) => [definition.id, definition])),
|
|
55
|
+
sequenceId: data.sequenceId,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
29
58
|
function sectionFromGame(game, section) {
|
|
59
|
+
if (isPersistedDefinitionSection(section)) {
|
|
60
|
+
return freezeCatalog(game);
|
|
61
|
+
}
|
|
30
62
|
if (section === 'xp') {
|
|
31
63
|
const definitions = freezeDefinitions([
|
|
32
64
|
game,
|
|
@@ -46,11 +78,13 @@ function sectionFromGame(game, section) {
|
|
|
46
78
|
}
|
|
47
79
|
if (section === 'ships') {
|
|
48
80
|
const ships = game;
|
|
49
|
-
const definitions =
|
|
50
|
-
|
|
81
|
+
const definitions = freezeShips(ships.definitions, ships.sequenceId);
|
|
82
|
+
const loaded = {
|
|
83
|
+
configurations: definitions,
|
|
51
84
|
definitions: new Map(definitions.map((definition) => [definition.id, definition])),
|
|
52
85
|
sequenceId: ships.sequenceId,
|
|
53
86
|
};
|
|
87
|
+
return loaded;
|
|
54
88
|
}
|
|
55
89
|
const values = game;
|
|
56
90
|
const definitions = freezeDefinitions(values.definitions);
|
|
@@ -84,8 +118,9 @@ function hasUniqueDefinitionIds(definitions) {
|
|
|
84
118
|
}
|
|
85
119
|
function isShipDefinition(value) {
|
|
86
120
|
return (isRecord(value) &&
|
|
87
|
-
hasOwnKeyCount(value,
|
|
121
|
+
hasOwnKeyCount(value, 5) &&
|
|
88
122
|
isNonnegativeInteger(value.id) &&
|
|
123
|
+
value.id <= 65535 &&
|
|
89
124
|
typeof value.mint === 'string' &&
|
|
90
125
|
isAddress(value.mint) &&
|
|
91
126
|
typeof value.name === 'string' &&
|
|
@@ -110,10 +145,11 @@ function isCompatibleSnapshot(snapshot, game, section) {
|
|
|
110
145
|
}
|
|
111
146
|
if (!isRecord(snapshot) ||
|
|
112
147
|
!hasOwnKeyCount(snapshot, 5) ||
|
|
113
|
-
snapshot.schemaVersion !== 1 ||
|
|
148
|
+
snapshot.schemaVersion !== (section === 'ships' ? 2 : 1) ||
|
|
114
149
|
snapshot.game !== game ||
|
|
115
150
|
snapshot.section !== section ||
|
|
116
151
|
!isNonnegativeInteger(snapshot.sequenceId) ||
|
|
152
|
+
(section === 'ships' && snapshot.sequenceId > 4294967295) ||
|
|
117
153
|
!Array.isArray(snapshot.definitions)) {
|
|
118
154
|
return false;
|
|
119
155
|
}
|
|
@@ -125,18 +161,43 @@ function isCompatibleSnapshot(snapshot, game, section) {
|
|
|
125
161
|
hasUniqueDefinitionIds(snapshot.definitions));
|
|
126
162
|
}
|
|
127
163
|
function snapshotFor(game, section, loaded) {
|
|
164
|
+
if (section === 'ships') {
|
|
165
|
+
return {
|
|
166
|
+
definitions: [...loaded.definitions.values()].map(({ id, mint, name, sizeClass, stats }) => ({
|
|
167
|
+
id,
|
|
168
|
+
mint,
|
|
169
|
+
name,
|
|
170
|
+
sizeClass,
|
|
171
|
+
stats: toJsonSafeValue(stats),
|
|
172
|
+
})),
|
|
173
|
+
game,
|
|
174
|
+
schemaVersion: 2,
|
|
175
|
+
section,
|
|
176
|
+
sequenceId: loaded.sequenceId,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
128
179
|
return {
|
|
129
|
-
definitions: [
|
|
180
|
+
definitions: [
|
|
181
|
+
...loaded.definitions.values(),
|
|
182
|
+
],
|
|
130
183
|
game,
|
|
131
184
|
schemaVersion: 1,
|
|
132
|
-
section,
|
|
185
|
+
section: 'cargo',
|
|
133
186
|
sequenceId: loaded.sequenceId,
|
|
134
187
|
};
|
|
135
188
|
}
|
|
136
189
|
function sectionFromSnapshot(snapshot) {
|
|
137
190
|
if (snapshot.section === 'ships') {
|
|
138
|
-
const
|
|
191
|
+
const rows = [];
|
|
192
|
+
for (const definition of snapshot.definitions) {
|
|
193
|
+
const stats = hydrateShipStats(definition.stats);
|
|
194
|
+
if (stats === undefined)
|
|
195
|
+
return undefined;
|
|
196
|
+
rows.push({ ...definition, stats });
|
|
197
|
+
}
|
|
198
|
+
const definitions = freezeShips(rows, snapshot.sequenceId);
|
|
139
199
|
return {
|
|
200
|
+
configurations: definitions,
|
|
140
201
|
definitions: new Map(definitions.map((definition) => [definition.id, definition])),
|
|
141
202
|
sequenceId: snapshot.sequenceId,
|
|
142
203
|
};
|
|
@@ -148,35 +209,49 @@ function sectionFromSnapshot(snapshot) {
|
|
|
148
209
|
};
|
|
149
210
|
}
|
|
150
211
|
async function loadStoredSection(context, game, section) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (section !== 'ships' && section !== 'cargo') {
|
|
212
|
+
assertContextActive(context);
|
|
213
|
+
if (section === 'xp' || section === 'research')
|
|
154
214
|
return undefined;
|
|
155
|
-
}
|
|
156
215
|
const store = context.registrySnapshotStore;
|
|
157
216
|
if (store === undefined)
|
|
158
217
|
return undefined;
|
|
218
|
+
let snapshot;
|
|
159
219
|
try {
|
|
160
|
-
|
|
161
|
-
if (!isCompatibleSnapshot(snapshot, game, section)) {
|
|
162
|
-
return undefined;
|
|
163
|
-
}
|
|
164
|
-
return sectionFromSnapshot(snapshot);
|
|
220
|
+
snapshot = await store.load(game, section);
|
|
165
221
|
}
|
|
166
222
|
catch {
|
|
167
223
|
throw new ProviderError(`The application registry snapshot store could not load the ${section} definitions. Repair or disable the snapshot store, then retry.`, { accountType: 'game', address: game });
|
|
168
224
|
}
|
|
225
|
+
if (isPersistedDefinitionSection(section)) {
|
|
226
|
+
const hydrated = hydrateDefinitionSectionSnapshot(snapshot, game, section);
|
|
227
|
+
return hydrated === undefined
|
|
228
|
+
? undefined
|
|
229
|
+
: freezeCatalog(hydrated);
|
|
230
|
+
}
|
|
231
|
+
if (!isCompatibleSnapshot(snapshot, game, section))
|
|
232
|
+
return undefined;
|
|
233
|
+
return sectionFromSnapshot(snapshot);
|
|
169
234
|
}
|
|
170
235
|
async function saveSection(context, game, section, loaded) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (section !== 'ships' && section !== 'cargo')
|
|
236
|
+
assertContextActive(context);
|
|
237
|
+
if (section === 'xp' || section === 'research')
|
|
174
238
|
return;
|
|
175
239
|
const store = context.registrySnapshotStore;
|
|
176
240
|
if (store === undefined)
|
|
177
241
|
return;
|
|
178
242
|
try {
|
|
179
|
-
if (section
|
|
243
|
+
if (isPersistedDefinitionSection(section)) {
|
|
244
|
+
const definitions = loaded.definitions;
|
|
245
|
+
const snapshot = encodeDefinitionSectionSnapshot(game, section, {
|
|
246
|
+
definitions: [...definitions.values()],
|
|
247
|
+
sequenceId: loaded.sequenceId,
|
|
248
|
+
});
|
|
249
|
+
// Loaded Game projections have already passed the pinned section validator.
|
|
250
|
+
/* v8 ignore next -- the encoder also guards its typed boundary; incompatible inputs are covered in its dedicated tests. */
|
|
251
|
+
if (snapshot !== undefined)
|
|
252
|
+
await store.save(snapshot);
|
|
253
|
+
}
|
|
254
|
+
else if (section === 'ships') {
|
|
180
255
|
await store.save(snapshotFor(game, section, loaded));
|
|
181
256
|
}
|
|
182
257
|
else {
|
|
@@ -188,122 +263,209 @@ async function saveSection(context, game, section, loaded) {
|
|
|
188
263
|
// read remains authoritative when application-owned storage is unavailable.
|
|
189
264
|
}
|
|
190
265
|
}
|
|
266
|
+
function publishSection(state, section, loaded) {
|
|
267
|
+
// Every caller pairs a candidate with the section that translated it.
|
|
268
|
+
state.sections[section] = loaded;
|
|
269
|
+
state.revisions[section] = (state.revisions[section] ?? 0) + 1;
|
|
270
|
+
}
|
|
271
|
+
function invalidateSection(state, section) {
|
|
272
|
+
// Resolver invalidation follows a successful publication, which initializes this revision.
|
|
273
|
+
state.revisions[section] = state.revisions[section] + 1;
|
|
274
|
+
state.refresh.add(section);
|
|
275
|
+
}
|
|
191
276
|
async function populateFromGame(context, game, sectionsToPopulate, state) {
|
|
192
|
-
const
|
|
277
|
+
const revisions = captureSectionRevisions(state.revisions, sectionsToPopulate);
|
|
278
|
+
const account = await waitForRegistry(context, context.data.readRaw(game));
|
|
279
|
+
assertContextActive(context);
|
|
193
280
|
if (account === undefined)
|
|
194
281
|
throw new AccountNotFoundError('game', game);
|
|
195
282
|
const decoded = decodeGameRegistrySections(account, sectionsToPopulate);
|
|
196
283
|
const populated = sectionsToPopulate.map((populatedSection) => {
|
|
197
284
|
const loaded = (() => {
|
|
285
|
+
if (populatedSection === 'buildings') {
|
|
286
|
+
const buildings = sectionFromGame(decoded.buildings, 'buildings');
|
|
287
|
+
return buildings;
|
|
288
|
+
}
|
|
289
|
+
if (populatedSection === 'claimStakes') {
|
|
290
|
+
const claimStakes = sectionFromGame(decoded.claimStakes, 'claimStakes');
|
|
291
|
+
return claimStakes;
|
|
292
|
+
}
|
|
198
293
|
if (populatedSection === 'ships') {
|
|
199
294
|
const ships = sectionFromGame(decoded.ships, 'ships');
|
|
200
|
-
state.sections.ships = ships;
|
|
201
295
|
return ships;
|
|
202
296
|
}
|
|
203
297
|
if (populatedSection === 'cargo') {
|
|
204
298
|
const cargo = sectionFromGame(decoded.cargo, 'cargo');
|
|
205
|
-
state.sections.cargo = cargo;
|
|
206
299
|
return cargo;
|
|
207
300
|
}
|
|
208
301
|
if (populatedSection === 'craftingHabs') {
|
|
209
302
|
const craftingHabs = sectionFromGame(decoded.craftingHabs, 'craftingHabs');
|
|
210
|
-
state.sections.craftingHabs = craftingHabs;
|
|
211
303
|
return craftingHabs;
|
|
212
304
|
}
|
|
213
305
|
if (populatedSection === 'habBuildings') {
|
|
214
306
|
const habBuildings = sectionFromGame(decoded.habBuildings, 'habBuildings');
|
|
215
|
-
state.sections.habBuildings = habBuildings;
|
|
216
307
|
return habBuildings;
|
|
217
308
|
}
|
|
218
309
|
if (populatedSection === 'xp') {
|
|
219
310
|
const xp = sectionFromGame(decoded.xp, 'xp');
|
|
220
|
-
state.sections.xp = xp;
|
|
221
311
|
return xp;
|
|
222
312
|
}
|
|
223
313
|
const research = sectionFromGame(decoded.research, 'research');
|
|
224
|
-
state.sections.research = research;
|
|
225
314
|
return research;
|
|
226
315
|
})();
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
state
|
|
316
|
+
return {
|
|
317
|
+
loaded: {
|
|
318
|
+
...loaded,
|
|
319
|
+
observation: Object.freeze({
|
|
320
|
+
slot: account.slot,
|
|
321
|
+
commitment: account.commitment,
|
|
322
|
+
}),
|
|
323
|
+
},
|
|
324
|
+
section: populatedSection,
|
|
325
|
+
};
|
|
326
|
+
});
|
|
327
|
+
assertContextActive(context);
|
|
328
|
+
const saves = [];
|
|
329
|
+
publishIfCurrent(state.revisions, revisions, () => {
|
|
330
|
+
for (const { loaded, section: populatedSection } of populated) {
|
|
331
|
+
publishSection(state, populatedSection, loaded);
|
|
332
|
+
const confirmingSequenceId = state.confirmingSequenceIds[populatedSection];
|
|
333
|
+
if (confirmingSequenceId !== undefined) {
|
|
334
|
+
state.confirmedSequenceMismatches[populatedSection] = {
|
|
335
|
+
actualSequenceId: loaded.sequenceId,
|
|
336
|
+
confirmedAtMonotonicMs: performance.now(),
|
|
337
|
+
expectedSequenceId: confirmingSequenceId,
|
|
338
|
+
};
|
|
339
|
+
delete state.confirmingSequenceIds[populatedSection];
|
|
340
|
+
}
|
|
341
|
+
else if (state.confirmedSequenceMismatches[populatedSection]
|
|
342
|
+
?.actualSequenceId !== loaded.sequenceId) {
|
|
343
|
+
delete state.confirmedSequenceMismatches[populatedSection];
|
|
344
|
+
}
|
|
345
|
+
if (state.confirmingMissing.has(populatedSection)) {
|
|
346
|
+
state.confirmedMissingSequenceIds[populatedSection] = loaded.sequenceId;
|
|
347
|
+
state.confirmingMissing.delete(populatedSection);
|
|
348
|
+
}
|
|
349
|
+
state.refresh.delete(populatedSection);
|
|
243
350
|
}
|
|
244
|
-
|
|
245
|
-
|
|
351
|
+
for (const { loaded, section } of populated)
|
|
352
|
+
saves.push(saveSection(context, game, section, loaded));
|
|
246
353
|
});
|
|
247
|
-
await Promise.all(
|
|
248
|
-
}
|
|
249
|
-
async function
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
354
|
+
await waitForRegistry(context, Promise.all(saves));
|
|
355
|
+
}
|
|
356
|
+
async function populateSections(context, game, sections, state, fetchedTargets) {
|
|
357
|
+
const revisions = captureSectionRevisions(state.revisions, sections);
|
|
358
|
+
const stored = await waitForRegistry(context, Promise.all(sections.map(async (section) => state.refresh.has(section)
|
|
359
|
+
? undefined
|
|
360
|
+
: loadStoredSection(context, game, section))));
|
|
361
|
+
assertContextActive(context);
|
|
362
|
+
for (const [index, section] of sections.entries()) {
|
|
363
|
+
const candidate = stored[index];
|
|
364
|
+
if (candidate !== undefined)
|
|
365
|
+
publishIfCurrent(state.revisions, [revisions[index]], () => publishSection(state, section, candidate));
|
|
366
|
+
}
|
|
367
|
+
const missing = sections.filter((section) => state.sections[section] === undefined || state.refresh.has(section));
|
|
368
|
+
if (missing.length > 0)
|
|
369
|
+
await populateFromGame(context, game, fetchedTargets.filter((section) => state.sections[section] === undefined || state.refresh.has(section)), state);
|
|
370
|
+
}
|
|
371
|
+
async function ensureSections(context, sections, lazyPair = false) {
|
|
372
|
+
const state = stateFor(context);
|
|
373
|
+
for (;;) {
|
|
374
|
+
assertContextActive(context);
|
|
375
|
+
const game = context.game;
|
|
376
|
+
if (game === undefined)
|
|
377
|
+
throw new MissingGameContextError();
|
|
378
|
+
const missing = sections.filter((section) => state.sections[section] === undefined || state.refresh.has(section));
|
|
379
|
+
if (missing.length === 0)
|
|
257
380
|
return;
|
|
381
|
+
let group = state.gameLoading;
|
|
382
|
+
if (group === undefined) {
|
|
383
|
+
const targets = lazyPair &&
|
|
384
|
+
(missing[0] === 'ships' || missing[0] === 'cargo') &&
|
|
385
|
+
((state.refresh.has('ships') && state.refresh.has('cargo')) ||
|
|
386
|
+
(state.sections.ships === undefined &&
|
|
387
|
+
state.sections.cargo === undefined))
|
|
388
|
+
? ['ships', 'cargo']
|
|
389
|
+
: missing;
|
|
390
|
+
const promise = Promise.resolve()
|
|
391
|
+
.then(() => populateSections(context, game, missing, state, targets))
|
|
392
|
+
.finally(() => {
|
|
393
|
+
/* v8 ignore next -- serialization prevents replacement before settlement; retain identity-checked cleanup so a late completion can never clear newer work. */
|
|
394
|
+
if (state.gameLoading?.[0] === promise)
|
|
395
|
+
delete state.gameLoading;
|
|
396
|
+
});
|
|
397
|
+
group = [promise, targets];
|
|
398
|
+
state.gameLoading = group;
|
|
258
399
|
}
|
|
259
|
-
}
|
|
260
|
-
let gameLoading = state.gameLoading;
|
|
261
|
-
if (gameLoading === undefined) {
|
|
262
|
-
const sections = (section === 'ships' || section === 'cargo') &&
|
|
263
|
-
((state.refresh.has('ships') && state.refresh.has('cargo')) ||
|
|
264
|
-
(state.sections.ships === undefined &&
|
|
265
|
-
state.sections.cargo === undefined))
|
|
266
|
-
? ['ships', 'cargo']
|
|
267
|
-
: [section];
|
|
268
|
-
const promise = populateFromGame(context, game, sections, state).finally(() => {
|
|
269
|
-
delete state.gameLoading;
|
|
270
|
-
});
|
|
271
|
-
gameLoading = [promise, sections];
|
|
272
|
-
state.gameLoading = gameLoading;
|
|
273
|
-
}
|
|
274
|
-
else if (!gameLoading[1].includes(section)) {
|
|
275
400
|
try {
|
|
276
|
-
await
|
|
401
|
+
await waitForRegistry(context, group[0]);
|
|
277
402
|
}
|
|
278
|
-
catch {
|
|
279
|
-
//
|
|
280
|
-
|
|
403
|
+
catch (error) {
|
|
404
|
+
// Only unrelated work can fail independently of this request.
|
|
405
|
+
if (missing.some((section) => group[1].includes(section)))
|
|
406
|
+
throw error;
|
|
281
407
|
}
|
|
282
|
-
return populateSection(context, game, section, state);
|
|
283
|
-
}
|
|
284
|
-
await gameLoading[0];
|
|
285
|
-
if (state.sections[section] === undefined || state.refresh.has(section)) {
|
|
286
|
-
return populateSection(context, game, section, state);
|
|
287
408
|
}
|
|
288
409
|
}
|
|
289
410
|
async function ensureSection(context, section) {
|
|
411
|
+
await ensureSections(context, [section], true);
|
|
412
|
+
return stateFor(context).sections[section];
|
|
413
|
+
}
|
|
414
|
+
/** Warm selected definition catalogs, retaining each section's own observation.
|
|
415
|
+
* Already usable sections are reused; this is not a freshness refresh.
|
|
416
|
+
* @example
|
|
417
|
+
* ```ts
|
|
418
|
+
* declare const ctx: SageContext;
|
|
419
|
+
* const loaded = await preloadDefinitions(ctx, ['ships', 'claimStakes']);
|
|
420
|
+
* const provenance = readMeta(loaded);
|
|
421
|
+
* ```
|
|
422
|
+
*/
|
|
423
|
+
export async function preloadDefinitions(context, sections, options = {}) {
|
|
290
424
|
assertContextActive(context);
|
|
291
|
-
|
|
292
|
-
if (
|
|
293
|
-
throw new
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
425
|
+
options.signal?.throwIfAborted();
|
|
426
|
+
if (!Array.isArray(sections))
|
|
427
|
+
throw new InvalidDefinitionSectionError();
|
|
428
|
+
const targets = [];
|
|
429
|
+
for (let index = 0; index < sections.length; index += 1) {
|
|
430
|
+
const descriptor = Object.getOwnPropertyDescriptor(sections, index);
|
|
431
|
+
const section = descriptor !== undefined && 'value' in descriptor
|
|
432
|
+
? descriptor.value
|
|
433
|
+
: undefined;
|
|
434
|
+
if (typeof section !== 'string' ||
|
|
435
|
+
![
|
|
436
|
+
'ships',
|
|
437
|
+
'cargo',
|
|
438
|
+
'buildings',
|
|
439
|
+
'claimStakes',
|
|
440
|
+
'craftingHabs',
|
|
441
|
+
'habBuildings',
|
|
442
|
+
'xp',
|
|
443
|
+
'research',
|
|
444
|
+
].includes(section))
|
|
445
|
+
throw new InvalidDefinitionSectionError();
|
|
446
|
+
const supported = section;
|
|
447
|
+
if (!targets.includes(supported))
|
|
448
|
+
targets.push(supported);
|
|
304
449
|
}
|
|
305
|
-
|
|
306
|
-
|
|
450
|
+
if (targets.length > 0)
|
|
451
|
+
await waitForRegistry(context, ensureSections(context, targets), options.signal);
|
|
452
|
+
assertContextActive(context);
|
|
453
|
+
const state = stateFor(context);
|
|
454
|
+
const metadata = {
|
|
455
|
+
kind: 'definition-preload',
|
|
456
|
+
sections: targets.map((section) => {
|
|
457
|
+
const loaded = state.sections[section];
|
|
458
|
+
return {
|
|
459
|
+
section,
|
|
460
|
+
sequenceId: loaded.sequenceId,
|
|
461
|
+
observation: loaded.observation === undefined
|
|
462
|
+
? { availability: 'unavailable' }
|
|
463
|
+
: { availability: 'available', ...loaded.observation },
|
|
464
|
+
};
|
|
465
|
+
}),
|
|
466
|
+
};
|
|
467
|
+
deepFreeze(metadata);
|
|
468
|
+
return Object.freeze(attachReadMeta(targets, metadata));
|
|
307
469
|
}
|
|
308
470
|
async function resolveDefinition(context, section, id, options) {
|
|
309
471
|
const loaded = await ensureSection(context, section);
|
|
@@ -321,7 +483,7 @@ async function resolveDefinition(context, section, id, options) {
|
|
|
321
483
|
performance.now() - confirmed.confirmedAtMonotonicMs >=
|
|
322
484
|
SEQUENCE_MISMATCH_RETRY_DELAY_MS) {
|
|
323
485
|
state.confirmingSequenceIds[section] = expected;
|
|
324
|
-
state
|
|
486
|
+
invalidateSection(state, section);
|
|
325
487
|
}
|
|
326
488
|
}
|
|
327
489
|
throw new RegistryOutOfSyncError(section, expected, loaded.sequenceId, id);
|
|
@@ -330,7 +492,7 @@ async function resolveDefinition(context, section, id, options) {
|
|
|
330
492
|
const definitionMissingAfterRefresh = state.confirmedMissingSequenceIds[section] === loaded.sequenceId;
|
|
331
493
|
if (!definitionMissingAfterRefresh && !toleratesOlderCaller) {
|
|
332
494
|
state.confirmingMissing.add(section);
|
|
333
|
-
state
|
|
495
|
+
invalidateSection(state, section);
|
|
334
496
|
}
|
|
335
497
|
throw new RegistryOutOfSyncError(section, expected, loaded.sequenceId, id, definitionMissingAfterRefresh);
|
|
336
498
|
}
|
|
@@ -348,6 +510,71 @@ async function resolveDefinition(context, section, id, options) {
|
|
|
348
510
|
export function resolveShip(context, shipId, options = {}) {
|
|
349
511
|
return resolveDefinition(context, 'ships', shipId, options);
|
|
350
512
|
}
|
|
513
|
+
/** Lists every ship configuration in the context's loaded Game definitions section.
|
|
514
|
+
*
|
|
515
|
+
* Entries and the readonly array are retained for the section generation. Each
|
|
516
|
+
* entry is the same immutable object returned by `resolveShip`; loading either
|
|
517
|
+
* route serves the other without another Game read. A section refresh replaces
|
|
518
|
+
* the array and its entries together. Existing returned arrays remain immutable
|
|
519
|
+
* snapshots of their prior generation.
|
|
520
|
+
*
|
|
521
|
+
* `definitionSequenceId` identifies the loaded section, not proven-current chain
|
|
522
|
+
* state. This enumerates existing configurations without grouping by model or
|
|
523
|
+
* filling unallocated ids. The result has no guaranteed sorting order.
|
|
524
|
+
*
|
|
525
|
+
* @example
|
|
526
|
+
* ```ts
|
|
527
|
+
* declare const ctx: SageContext;
|
|
528
|
+
* const configurations = await listShipConfigurations(ctx);
|
|
529
|
+
* for (const ship of configurations) {
|
|
530
|
+
* console.log(ship.id, ship.name, ship.stats.cargo.cargoCapacity);
|
|
531
|
+
* }
|
|
532
|
+
* ```
|
|
533
|
+
*/
|
|
534
|
+
export async function listShipConfigurations(context) {
|
|
535
|
+
const loaded = (await ensureSection(context, 'ships'));
|
|
536
|
+
return loaded.configurations;
|
|
537
|
+
}
|
|
538
|
+
/** Lists current ship configurations associated with a mint in this context's Game.
|
|
539
|
+
*
|
|
540
|
+
* Groups by each configuration's on-chain `mint`, never display names, id ranges,
|
|
541
|
+
* or historical `mintToId` entries. Use a mint from the selected deployment;
|
|
542
|
+
* this does not map production mints to PTR or provide make/model labels or a
|
|
543
|
+
* default configuration. See `docs/research/ship-model-identity.md` (#395/#397).
|
|
544
|
+
*
|
|
545
|
+
* The frozen result shares objects with `resolveShip` and `listShipConfigurations`.
|
|
546
|
+
* A lazy index is retained with the ships section and replaced on section refresh;
|
|
547
|
+
* building it adds no RPC calls or metadata fetches. Unknown mints return a retained
|
|
548
|
+
* frozen empty array. Results have no guaranteed order or configuration count.
|
|
549
|
+
* `definitionSequenceId` denotes the loaded generation, not proven-current state.
|
|
550
|
+
*
|
|
551
|
+
* @example
|
|
552
|
+
* ```ts
|
|
553
|
+
* declare const ctx: SageContext;
|
|
554
|
+
* declare const shipMint: Address;
|
|
555
|
+
* const configurations = await listShipConfigurationsByMint(ctx, shipMint);
|
|
556
|
+
* for (const ship of configurations) {
|
|
557
|
+
* console.log(ship.id, ship.stats.cargo.cargoCapacity);
|
|
558
|
+
* }
|
|
559
|
+
* ```
|
|
560
|
+
*/
|
|
561
|
+
export async function listShipConfigurationsByMint(context, mint) {
|
|
562
|
+
const loaded = (await ensureSection(context, 'ships'));
|
|
563
|
+
if (loaded.mintIndex === undefined) {
|
|
564
|
+
const groups = new Map();
|
|
565
|
+
for (const ship of loaded.configurations) {
|
|
566
|
+
const group = groups.get(ship.mint);
|
|
567
|
+
if (group === undefined)
|
|
568
|
+
groups.set(ship.mint, [ship]);
|
|
569
|
+
else
|
|
570
|
+
group.push(ship);
|
|
571
|
+
}
|
|
572
|
+
for (const group of groups.values())
|
|
573
|
+
Object.freeze(group);
|
|
574
|
+
loaded.mintIndex = { groups, empty: Object.freeze([]) };
|
|
575
|
+
}
|
|
576
|
+
return loaded.mintIndex.groups.get(mint) ?? loaded.mintIndex.empty;
|
|
577
|
+
}
|
|
351
578
|
/** Resolves one cargo id to an immutable SDK-owned definition summary.
|
|
352
579
|
*
|
|
353
580
|
* @example
|
|
@@ -394,4 +621,48 @@ export async function listResearchDefinitions(context) {
|
|
|
394
621
|
});
|
|
395
622
|
}
|
|
396
623
|
export { RegistryOutOfSyncError } from '../client/errors.js';
|
|
624
|
+
/** Internal Claim Stake lookup using the shared section lifecycle. */
|
|
625
|
+
export function resolveClaimStakeDefinition(context, id) {
|
|
626
|
+
return resolveDefinition(context, 'claimStakes', id, {});
|
|
627
|
+
}
|
|
628
|
+
/** Internal canonical Claim Stake catalog. */
|
|
629
|
+
export async function listClaimStakeDefinitions(context) {
|
|
630
|
+
return (await ensureSection(context, 'claimStakes')).catalog;
|
|
631
|
+
}
|
|
632
|
+
/** Internal canonical Crafting Hab container catalog. */
|
|
633
|
+
export async function listCraftingHabDefinitions(context) {
|
|
634
|
+
return (await ensureSection(context, 'craftingHabs')).catalog;
|
|
635
|
+
}
|
|
636
|
+
/** Internal Building definition lookup using the shared section lifecycle. */
|
|
637
|
+
export function resolveClaimStakeBuildingDefinition(context, id) {
|
|
638
|
+
return resolveDefinition(context, 'buildings', id, {});
|
|
639
|
+
}
|
|
640
|
+
/** Internal canonical Claim Stake building catalog. */
|
|
641
|
+
export async function listClaimStakeBuildingDefinitions(context) {
|
|
642
|
+
return (await ensureSection(context, 'buildings'))
|
|
643
|
+
.catalog;
|
|
644
|
+
}
|
|
645
|
+
/** Internal canonical Hab building catalog. */
|
|
646
|
+
export async function listHabBuildingDefinitions(context) {
|
|
647
|
+
return (await ensureSection(context, 'habBuildings')).catalog;
|
|
648
|
+
}
|
|
649
|
+
/** Lists all cargo definitions from the context's retained Game section.
|
|
650
|
+
*
|
|
651
|
+
* The readonly array and its definitions are shared with by-id lookups for one
|
|
652
|
+
* section generation. A refresh replaces the array and id map together; old
|
|
653
|
+
* arrays remain immutable snapshots. Results have no guaranteed sorting order.
|
|
654
|
+
* Loading is lazy and may use a compatible cargo schema-v1 stored snapshot.
|
|
655
|
+
* The retained generation does not prove latest-chain freshness.
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* ```ts
|
|
659
|
+
* declare const ctx: SageContext;
|
|
660
|
+
* const definitions = await listCargoDefinitions(ctx);
|
|
661
|
+
* for (const cargo of definitions) console.log(cargo.id, cargo.name);
|
|
662
|
+
* ```
|
|
663
|
+
*/
|
|
664
|
+
export async function listCargoDefinitions(context) {
|
|
665
|
+
const loaded = (await ensureSection(context, 'cargo'));
|
|
666
|
+
return (loaded.catalog ??= Object.freeze([...loaded.definitions.values()]));
|
|
667
|
+
}
|
|
397
668
|
//# sourceMappingURL=index.js.map
|