@shipload/sdk 1.0.0-next.42 → 1.0.0-next.44

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 (42) hide show
  1. package/lib/shipload.d.ts +222 -172
  2. package/lib/shipload.js +658 -421
  3. package/lib/shipload.js.map +1 -1
  4. package/lib/shipload.m.js +644 -423
  5. package/lib/shipload.m.js.map +1 -1
  6. package/lib/testing.d.ts +50 -67
  7. package/lib/testing.js +168 -208
  8. package/lib/testing.js.map +1 -1
  9. package/lib/testing.m.js +169 -209
  10. package/lib/testing.m.js.map +1 -1
  11. package/package.json +1 -1
  12. package/src/contracts/server.ts +126 -214
  13. package/src/data/capabilities.ts +22 -14
  14. package/src/data/capability-formulas.ts +7 -7
  15. package/src/data/entities.json +4 -0
  16. package/src/data/item-ids.ts +1 -0
  17. package/src/data/items.json +6 -0
  18. package/src/data/kind-registry.json +18 -6
  19. package/src/data/kind-registry.ts +7 -0
  20. package/src/data/metadata.ts +22 -15
  21. package/src/data/recipes.json +147 -138
  22. package/src/derivation/build-methods.test.ts +13 -0
  23. package/src/derivation/build-methods.ts +5 -2
  24. package/src/derivation/capabilities.ts +8 -8
  25. package/src/derivation/capability-mappings.ts +49 -4
  26. package/src/index-module.ts +21 -1
  27. package/src/managers/actions.ts +42 -8
  28. package/src/managers/cluster.test.ts +39 -0
  29. package/src/managers/cluster.ts +53 -0
  30. package/src/managers/context.ts +9 -0
  31. package/src/managers/index.ts +2 -0
  32. package/src/nft/buildImmutableData.ts +15 -17
  33. package/src/nft/description.ts +8 -8
  34. package/src/resolution/describe-module.ts +6 -6
  35. package/src/resolution/resolve-item.ts +3 -3
  36. package/src/scheduling/unwrap.test.ts +60 -0
  37. package/src/scheduling/unwrap.ts +187 -0
  38. package/src/shipload.ts +5 -0
  39. package/src/subscriptions/manager.cluster.test.ts +46 -0
  40. package/src/subscriptions/manager.ts +22 -0
  41. package/src/subscriptions/types.ts +16 -0
  42. package/src/types.ts +7 -0
@@ -24,6 +24,14 @@
24
24
  "statIndex": 2
25
25
  }
26
26
  ]
27
+ },
28
+ {
29
+ "sources": [
30
+ {
31
+ "inputIndex": 0,
32
+ "statIndex": 1
33
+ }
34
+ ]
27
35
  }
28
36
  ],
29
37
  "blendWeights": []
@@ -57,6 +65,22 @@
57
65
  "statIndex": 0
58
66
  }
59
67
  ]
68
+ },
69
+ {
70
+ "sources": [
71
+ {
72
+ "inputIndex": 0,
73
+ "statIndex": 2
74
+ }
75
+ ]
76
+ },
77
+ {
78
+ "sources": [
79
+ {
80
+ "inputIndex": 1,
81
+ "statIndex": 2
82
+ }
83
+ ]
60
84
  }
61
85
  ],
62
86
  "blendWeights": []
@@ -86,6 +110,14 @@
86
110
  "statIndex": 2
87
111
  }
88
112
  ]
113
+ },
114
+ {
115
+ "sources": [
116
+ {
117
+ "inputIndex": 0,
118
+ "statIndex": 1
119
+ }
120
+ ]
89
121
  }
90
122
  ],
91
123
  "blendWeights": []
@@ -119,6 +151,14 @@
119
151
  "statIndex": 2
120
152
  }
121
153
  ]
154
+ },
155
+ {
156
+ "sources": [
157
+ {
158
+ "inputIndex": 0,
159
+ "statIndex": 0
160
+ }
161
+ ]
122
162
  }
123
163
  ],
124
164
  "blendWeights": []
@@ -152,6 +192,14 @@
152
192
  "statIndex": 1
153
193
  }
154
194
  ]
195
+ },
196
+ {
197
+ "sources": [
198
+ {
199
+ "inputIndex": 0,
200
+ "statIndex": 2
201
+ }
202
+ ]
155
203
  }
156
204
  ],
157
205
  "blendWeights": []
@@ -181,6 +229,14 @@
181
229
  "statIndex": 2
182
230
  }
183
231
  ]
232
+ },
233
+ {
234
+ "sources": [
235
+ {
236
+ "inputIndex": 0,
237
+ "statIndex": 1
238
+ }
239
+ ]
184
240
  }
185
241
  ],
186
242
  "blendWeights": []
@@ -210,6 +266,14 @@
210
266
  "statIndex": 1
211
267
  }
212
268
  ]
269
+ },
270
+ {
271
+ "sources": [
272
+ {
273
+ "inputIndex": 0,
274
+ "statIndex": 2
275
+ }
276
+ ]
213
277
  }
214
278
  ],
215
279
  "blendWeights": []
@@ -272,6 +336,22 @@
272
336
  "statIndex": 2
273
337
  }
274
338
  ]
339
+ },
340
+ {
341
+ "sources": [
342
+ {
343
+ "inputIndex": 1,
344
+ "statIndex": 2
345
+ }
346
+ ]
347
+ },
348
+ {
349
+ "sources": [
350
+ {
351
+ "inputIndex": 1,
352
+ "statIndex": 0
353
+ }
354
+ ]
275
355
  }
276
356
  ],
277
357
  "blendWeights": []
@@ -305,6 +385,14 @@
305
385
  "statIndex": 0
306
386
  }
307
387
  ]
388
+ },
389
+ {
390
+ "sources": [
391
+ {
392
+ "inputIndex": 0,
393
+ "statIndex": 1
394
+ }
395
+ ]
308
396
  }
309
397
  ],
310
398
  "blendWeights": []
@@ -324,6 +412,10 @@
324
412
  {
325
413
  "inputIndex": 0,
326
414
  "statIndex": 0
415
+ },
416
+ {
417
+ "inputIndex": 0,
418
+ "statIndex": 2
327
419
  }
328
420
  ]
329
421
  },
@@ -336,7 +428,9 @@
336
428
  ]
337
429
  }
338
430
  ],
339
- "blendWeights": []
431
+ "blendWeights": [
432
+ 1
433
+ ]
340
434
  },
341
435
  {
342
436
  "outputItemId": 10101,
@@ -353,6 +447,10 @@
353
447
  {
354
448
  "inputIndex": 0,
355
449
  "statIndex": 0
450
+ },
451
+ {
452
+ "inputIndex": 0,
453
+ "statIndex": 2
356
454
  }
357
455
  ]
358
456
  },
@@ -365,7 +463,9 @@
365
463
  ]
366
464
  }
367
465
  ],
368
- "blendWeights": []
466
+ "blendWeights": [
467
+ 1
468
+ ]
369
469
  },
370
470
  {
371
471
  "outputItemId": 10102,
@@ -376,7 +476,7 @@
376
476
  "quantity": 300
377
477
  },
378
478
  {
379
- "itemId": 10010,
479
+ "itemId": 10002,
380
480
  "quantity": 300
381
481
  }
382
482
  ],
@@ -386,14 +486,10 @@
386
486
  {
387
487
  "inputIndex": 0,
388
488
  "statIndex": 0
389
- }
390
- ]
391
- },
392
- {
393
- "sources": [
489
+ },
394
490
  {
395
491
  "inputIndex": 0,
396
- "statIndex": 1
492
+ "statIndex": 2
397
493
  }
398
494
  ]
399
495
  },
@@ -402,6 +498,10 @@
402
498
  {
403
499
  "inputIndex": 1,
404
500
  "statIndex": 0
501
+ },
502
+ {
503
+ "inputIndex": 0,
504
+ "statIndex": 1
405
505
  }
406
506
  ]
407
507
  },
@@ -409,12 +509,15 @@
409
509
  "sources": [
410
510
  {
411
511
  "inputIndex": 1,
412
- "statIndex": 1
512
+ "statIndex": 3
413
513
  }
414
514
  ]
415
515
  }
416
516
  ],
417
- "blendWeights": []
517
+ "blendWeights": [
518
+ 1,
519
+ 1
520
+ ]
418
521
  },
419
522
  {
420
523
  "outputItemId": 10103,
@@ -439,11 +542,17 @@
439
542
  {
440
543
  "inputIndex": 0,
441
544
  "statIndex": 0
545
+ },
546
+ {
547
+ "inputIndex": 0,
548
+ "statIndex": 2
442
549
  }
443
550
  ]
444
551
  }
445
552
  ],
446
- "blendWeights": []
553
+ "blendWeights": [
554
+ 1
555
+ ]
447
556
  },
448
557
  {
449
558
  "outputItemId": 10104,
@@ -463,7 +572,11 @@
463
572
  "sources": [
464
573
  {
465
574
  "inputIndex": 0,
466
- "statIndex": 0
575
+ "statIndex": 2
576
+ },
577
+ {
578
+ "inputIndex": 0,
579
+ "statIndex": 3
467
580
  }
468
581
  ]
469
582
  },
@@ -476,7 +589,10 @@
476
589
  ]
477
590
  }
478
591
  ],
479
- "blendWeights": []
592
+ "blendWeights": [
593
+ 1,
594
+ 1
595
+ ]
480
596
  },
481
597
  {
482
598
  "outputItemId": 10105,
@@ -546,6 +662,10 @@
546
662
  {
547
663
  "inputIndex": 0,
548
664
  "statIndex": 0
665
+ },
666
+ {
667
+ "inputIndex": 0,
668
+ "statIndex": 1
549
669
  }
550
670
  ]
551
671
  },
@@ -561,12 +681,15 @@
561
681
  "sources": [
562
682
  {
563
683
  "inputIndex": 0,
564
- "statIndex": 1
684
+ "statIndex": 2
565
685
  }
566
686
  ]
567
687
  }
568
688
  ],
569
- "blendWeights": []
689
+ "blendWeights": [
690
+ 1,
691
+ 1
692
+ ]
570
693
  },
571
694
  {
572
695
  "outputItemId": 10107,
@@ -586,11 +709,11 @@
586
709
  "sources": [
587
710
  {
588
711
  "inputIndex": 0,
589
- "statIndex": 0
712
+ "statIndex": 1
590
713
  },
591
714
  {
592
- "inputIndex": 1,
593
- "statIndex": 1
715
+ "inputIndex": 0,
716
+ "statIndex": 2
594
717
  }
595
718
  ]
596
719
  }
@@ -877,135 +1000,21 @@
877
1000
  "blendWeights": []
878
1001
  },
879
1002
  {
880
- "outputItemId": 20001,
881
- "outputMass": 500,
882
- "inputs": [
883
- {
884
- "itemId": 10001,
885
- "quantity": 200
886
- },
887
- {
888
- "itemId": 102,
889
- "quantity": 15
890
- }
891
- ],
892
- "statSlots": [
893
- {
894
- "sources": [
895
- {
896
- "inputIndex": 0,
897
- "statIndex": 0
898
- },
899
- {
900
- "inputIndex": 1,
901
- "statIndex": 0
902
- }
903
- ]
904
- },
905
- {
906
- "sources": [
907
- {
908
- "inputIndex": 0,
909
- "statIndex": 1
910
- },
911
- {
912
- "inputIndex": 1,
913
- "statIndex": 2
914
- }
915
- ]
916
- }
917
- ],
918
- "blendWeights": [
919
- 1,
920
- 1
921
- ]
922
- },
923
- {
924
- "outputItemId": 20002,
925
- "outputMass": 300,
1003
+ "outputItemId": 10207,
1004
+ "outputMass": 2400000,
926
1005
  "inputs": [
927
1006
  {
928
- "itemId": 10002,
1007
+ "itemId": 10005,
929
1008
  "quantity": 200
930
1009
  },
931
1010
  {
932
- "itemId": 402,
933
- "quantity": 10
934
- },
935
- {
936
- "itemId": 502,
937
- "quantity": 20
938
- }
939
- ],
940
- "statSlots": [
941
- {
942
- "sources": [
943
- {
944
- "inputIndex": 0,
945
- "statIndex": 0
946
- },
947
- {
948
- "inputIndex": 1,
949
- "statIndex": 1
950
- }
951
- ]
952
- },
953
- {
954
- "sources": [
955
- {
956
- "inputIndex": 0,
957
- "statIndex": 1
958
- },
959
- {
960
- "inputIndex": 2,
961
- "statIndex": 2
962
- }
963
- ]
964
- }
965
- ],
966
- "blendWeights": [
967
- 1,
968
- 1,
969
- 1
970
- ]
971
- },
972
- {
973
- "outputItemId": 20200,
974
- "outputMass": 80000,
975
- "inputs": [
976
- {
977
- "itemId": 20001,
978
- "quantity": 600
979
- },
980
- {
981
- "itemId": 20002,
1011
+ "itemId": 10010,
982
1012
  "quantity": 200
983
1013
  }
984
1014
  ],
985
1015
  "statSlots": [
986
1016
  {
987
- "sources": [
988
- {
989
- "inputIndex": 0,
990
- "statIndex": 0
991
- }
992
- ]
993
- },
994
- {
995
- "sources": [
996
- {
997
- "inputIndex": 0,
998
- "statIndex": 1
999
- }
1000
- ]
1001
- },
1002
- {
1003
- "sources": [
1004
- {
1005
- "inputIndex": 1,
1006
- "statIndex": 0
1007
- }
1008
- ]
1017
+ "sources": []
1009
1018
  },
1010
1019
  {
1011
1020
  "sources": [
@@ -0,0 +1,13 @@
1
+ import {describe, expect, test} from 'bun:test'
2
+ import {availableBuildMethods} from './build-methods'
3
+ import {ITEM_HUB_T1_PACKED, ITEM_WAREHOUSE_T1_PACKED} from '../data/item-ids'
4
+
5
+ describe('availableBuildMethods', () => {
6
+ test('orbital structures build via craft+deploy or plot', () => {
7
+ expect(availableBuildMethods(ITEM_WAREHOUSE_T1_PACKED)).toEqual(['craft+deploy', 'plot'])
8
+ })
9
+
10
+ test('hub is craft+deploy only — excluded from the plot path', () => {
11
+ expect(availableBuildMethods(ITEM_HUB_T1_PACKED)).toEqual(['craft+deploy'])
12
+ })
13
+ })
@@ -1,4 +1,4 @@
1
- import {EntityClass, getKindMeta, getTemplateMeta} from '../data/kind-registry'
1
+ import {ENTITY_HUB, EntityClass, getKindMeta, getTemplateMeta} from '../data/kind-registry'
2
2
  import {getRecipe} from '../data/recipes-runtime'
3
3
  import {getItems} from '../data/catalog'
4
4
  import type {Item} from '../types'
@@ -15,7 +15,10 @@ export function availableBuildMethods(itemId: number): BuildMethod[] {
15
15
  const kindMeta = getKindMeta(template.kind)
16
16
  if (!kindMeta) return ['craft+deploy']
17
17
 
18
- if (kindMeta.classification === EntityClass.PlanetaryStructure) {
18
+ if (
19
+ kindMeta.classification === EntityClass.OrbitalStructure &&
20
+ !kindMeta.kind.equals(ENTITY_HUB)
21
+ ) {
19
22
  return ['craft+deploy', 'plot']
20
23
  }
21
24
  return ['craft+deploy']
@@ -78,12 +78,12 @@ export function computeGathererCapabilities(
78
78
  } {
79
79
  const str = stats.strength
80
80
  const con = stats.saturation
81
- const tol = stats.tolerance
81
+ const hrd = stats.hardness
82
82
 
83
83
  return {
84
84
  yield: 200 + str,
85
85
  drain: 2 * Math.max(250, 1250 - Math.floor((con * 25) / 20)),
86
- depth: gathererDepthForTier(tol, tier),
86
+ depth: gathererDepthForTier(hrd, tier),
87
87
  }
88
88
  }
89
89
 
@@ -106,11 +106,11 @@ export function computeCrafterCapabilities(stats: Record<string, number>): {
106
106
  speed: number
107
107
  drain: number
108
108
  } {
109
- const rea = stats.reactivity
109
+ const fin = stats.fineness
110
110
  const con = stats.conductivity
111
111
 
112
112
  return {
113
- speed: 100 + Math.floor((rea * 4) / 5),
113
+ speed: 100 + Math.floor((fin * 4) / 5),
114
114
  drain: Math.max(5, 30 - Math.floor(con / 33)),
115
115
  }
116
116
  }
@@ -122,12 +122,12 @@ export function computeHaulerCapabilities(stats: Record<string, number>): {
122
122
  } {
123
123
  const resonance = stats.resonance
124
124
  const plasticity = stats.plasticity
125
- const reflectivity = stats.reflectivity
125
+ const conductivity = stats.conductivity
126
126
 
127
127
  return {
128
128
  capacity: Math.max(1, 1 + Math.floor(resonance / 400)),
129
129
  efficiency: 2000 + plasticity * 6,
130
- drain: Math.max(3, 15 - Math.floor(reflectivity / 80)),
130
+ drain: Math.max(3, 15 - Math.floor(conductivity / 80)),
131
131
  }
132
132
  }
133
133
 
@@ -224,8 +224,8 @@ export function computeBaseCapacity(itemId: number, stats: Record<string, number
224
224
  export function computeWarpCapabilities(stats: Record<string, number>): {
225
225
  range: number
226
226
  } {
227
- const resonance = stats.resonance
228
- return {range: 100 + resonance * 3}
227
+ const reflectivity = stats.reflectivity
228
+ return {range: 100 + reflectivity * 3}
229
229
  }
230
230
 
231
231
  export function computeWarehouseHullCapabilities(stats: Record<string, number>): {
@@ -18,7 +18,8 @@ import {
18
18
  ITEM_WAREHOUSE_T1_PACKED,
19
19
  ITEM_CONTAINER_T2_PACKED,
20
20
  } from '../data/item-ids'
21
- import type {StatMapping} from '../data/capabilities'
21
+ import type {StatMapping, CapabilityAttributeRow} from '../data/capabilities'
22
+ import {capabilityAttributes} from '../data/capabilities'
22
23
 
23
24
  export const KIND_TO_ITEM_ID: Record<SlotConsumerKind, number> = {
24
25
  engine: ITEM_ENGINE_T1,
@@ -69,6 +70,12 @@ function traceToRawCategoryStat(
69
70
  return traceToRawCategoryStat(subRecipe, subSource, nextVisited)
70
71
  }
71
72
 
73
+ // Producing role for a capability·attribute: entity hull slots all roll up to "Hull"; modules use their own name.
74
+ export function sourceLabelForOutput(itemId: number): string {
75
+ const item = getItem(itemId)
76
+ return item.type === 'entity' ? 'Hull' : item.name
77
+ }
78
+
72
79
  let cached: StatMapping[] | undefined
73
80
 
74
81
  export function deriveStatMappings(): StatMapping[] {
@@ -82,20 +89,22 @@ export function deriveStatMappings(): StatMapping[] {
82
89
  const itemId = KIND_TO_ITEM_ID[kind]
83
90
  const recipe = getRecipe(itemId)
84
91
  if (!recipe) continue
92
+ const source = sourceLabelForOutput(itemId)
85
93
  for (const [slotIdxStr, consumer] of Object.entries(slots)) {
86
94
  const slotIdx = Number(slotIdxStr)
87
95
  const slot = recipe.statSlots[slotIdx]
88
96
  if (!slot) continue
89
- for (const source of slot.sources) {
90
- const stat = traceToRawCategoryStat(recipe, source)
97
+ for (const src of slot.sources) {
98
+ const stat = traceToRawCategoryStat(recipe, src)
91
99
  if (!stat) continue
92
- const key = `${stat.label}|${consumer.capability}|${consumer.attribute}`
100
+ const key = `${stat.label}|${consumer.capability}|${consumer.attribute}|${source}`
93
101
  if (seen.has(key)) continue
94
102
  seen.add(key)
95
103
  out.push({
96
104
  stat: stat.label,
97
105
  capability: consumer.capability,
98
106
  attribute: consumer.attribute,
107
+ source,
99
108
  })
100
109
  }
101
110
  }
@@ -115,3 +124,39 @@ export function getStatMappingsForStat(stat: string): StatMapping[] {
115
124
  export function getStatMappingsForCapability(capability: string): StatMapping[] {
116
125
  return deriveStatMappings().filter((m) => m.capability === capability)
117
126
  }
127
+
128
+ export function getProducersForAttribute(capability: string, attribute: string): string[] {
129
+ const seen = new Set<string>()
130
+ const out: string[] = []
131
+ for (const m of deriveStatMappings()) {
132
+ if (m.capability !== capability || m.attribute !== attribute) continue
133
+ if (seen.has(m.source)) continue
134
+ seen.add(m.source)
135
+ out.push(m.source)
136
+ }
137
+ return out
138
+ }
139
+
140
+ export function getCapabilityAttributeRows(): CapabilityAttributeRow[] {
141
+ const rows: CapabilityAttributeRow[] = []
142
+ for (const ca of capabilityAttributes) {
143
+ const producers = getProducersForAttribute(ca.capability, ca.attribute)
144
+ if (producers.length === 0) {
145
+ rows.push({
146
+ capability: ca.capability,
147
+ attribute: ca.attribute,
148
+ description: ca.description,
149
+ })
150
+ continue
151
+ }
152
+ for (const source of producers) {
153
+ rows.push({
154
+ capability: ca.capability,
155
+ attribute: ca.attribute,
156
+ description: ca.description,
157
+ source,
158
+ })
159
+ }
160
+ }
161
+ return rows
162
+ }
@@ -46,9 +46,12 @@ export {
46
46
  LocationsManager,
47
47
  EpochsManager,
48
48
  ActionsManager,
49
+ ClusterManager,
50
+ computeFreeCells,
49
51
  NftManager,
50
52
  ConstructionManager,
51
53
  } from './managers'
54
+ export type {GridCell, ClusterCell, Cluster} from './managers'
52
55
  export type {
53
56
  PlayerRosterEntry,
54
57
  LocationStratum,
@@ -283,6 +286,17 @@ export type {
283
286
  CancelEligibilityInput,
284
287
  } from './scheduling/cancel'
285
288
 
289
+ export {
290
+ derivedLoaders,
291
+ estimateUnwrapDuration,
292
+ incomingHoldMass,
293
+ projectedPeakCargomass,
294
+ receiveFits,
295
+ unwrapLoadDuration,
296
+ unwrapTransitDuration,
297
+ } from './scheduling/unwrap'
298
+ export type {DerivedLoaders, UnwrapDestination, UnwrapItem} from './scheduling/unwrap'
299
+
286
300
  export {
287
301
  projectedCargoAvailableAt,
288
302
  availableForItem,
@@ -304,6 +318,7 @@ export {
304
318
  ENTITY_FACTORY,
305
319
  ENTITY_CONTAINER,
306
320
  ENTITY_NEXUS,
321
+ ENTITY_HUB,
307
322
  getEntityClass,
308
323
  getPackedEntityType,
309
324
  getKindMeta,
@@ -321,6 +336,7 @@ export {
321
336
  isContainer,
322
337
  isNexus,
323
338
  isPlot,
339
+ isHub,
324
340
  } from './data/kind-registry'
325
341
  export type {EntityTypeName, KindMeta, TemplateMeta} from './data/kind-registry'
326
342
  export * from './capabilities'
@@ -348,13 +364,16 @@ export {
348
364
  isInvertedAttribute,
349
365
  getCapabilityAttributes,
350
366
  } from './data/capabilities'
351
- export type {CapabilityAttribute, StatMapping} from './data/capabilities'
367
+ export type {CapabilityAttribute, StatMapping, CapabilityAttributeRow} from './data/capabilities'
352
368
 
353
369
  export {
354
370
  deriveStatMappings,
355
371
  getStatMappings,
356
372
  getStatMappingsForStat,
357
373
  getStatMappingsForCapability,
374
+ getProducersForAttribute,
375
+ getCapabilityAttributeRows,
376
+ sourceLabelForOutput,
358
377
  } from './derivation/capability-mappings'
359
378
  export {SLOT_FORMULAS} from './data/capability-formulas'
360
379
  export type {SlotConsumer, SlotConsumerKind} from './data/capability-formulas'
@@ -422,6 +441,7 @@ export {
422
441
  feistelInv,
423
442
  regionOf,
424
443
  partnerRegion,
444
+ nearbyWormholes,
425
445
  wormholeAt,
426
446
  wormholeAtRegionEndpoint,
427
447
  isValidWormholePair,