swcombine-sdk 1.7.0 → 2.1.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.
- package/README.md +82 -215
- package/dist/cjs/SWCombine.js +49 -13
- package/dist/cjs/SWCombine.js.map +1 -1
- package/dist/cjs/Timestamp.js +427 -0
- package/dist/cjs/Timestamp.js.map +1 -0
- package/dist/cjs/auth/TokenManager.js +7 -2
- package/dist/cjs/auth/TokenManager.js.map +1 -1
- package/dist/cjs/http/HttpClient.js +3 -0
- package/dist/cjs/http/HttpClient.js.map +1 -1
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/resources/ApiResource.js +2 -0
- package/dist/cjs/resources/ApiResource.js.map +1 -1
- package/dist/cjs/resources/CharacterResource.js +76 -4
- package/dist/cjs/resources/CharacterResource.js.map +1 -1
- package/dist/cjs/resources/DatacardResource.js +2 -0
- package/dist/cjs/resources/DatacardResource.js.map +1 -1
- package/dist/cjs/resources/EventsResource.js +2 -0
- package/dist/cjs/resources/EventsResource.js.map +1 -1
- package/dist/cjs/resources/FactionResource.js +12 -0
- package/dist/cjs/resources/FactionResource.js.map +1 -1
- package/dist/cjs/resources/GalaxyResource.js +12 -0
- package/dist/cjs/resources/GalaxyResource.js.map +1 -1
- package/dist/cjs/resources/InventoryResource.js +4 -0
- package/dist/cjs/resources/InventoryResource.js.map +1 -1
- package/dist/cjs/resources/LocationResource.js +2 -0
- package/dist/cjs/resources/LocationResource.js.map +1 -1
- package/dist/cjs/resources/MarketResource.js +4 -0
- package/dist/cjs/resources/MarketResource.js.map +1 -1
- package/dist/cjs/resources/NewsResource.js +6 -0
- package/dist/cjs/resources/NewsResource.js.map +1 -1
- package/dist/cjs/resources/TypesResource.js +171 -19
- package/dist/cjs/resources/TypesResource.js.map +1 -1
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/esm/SWCombine.js +49 -13
- package/dist/esm/SWCombine.js.map +1 -1
- package/dist/esm/Timestamp.js +423 -0
- package/dist/esm/Timestamp.js.map +1 -0
- package/dist/esm/auth/TokenManager.js +7 -2
- package/dist/esm/auth/TokenManager.js.map +1 -1
- package/dist/esm/http/HttpClient.js +3 -0
- package/dist/esm/http/HttpClient.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/resources/ApiResource.js +2 -0
- package/dist/esm/resources/ApiResource.js.map +1 -1
- package/dist/esm/resources/CharacterResource.js +76 -4
- package/dist/esm/resources/CharacterResource.js.map +1 -1
- package/dist/esm/resources/DatacardResource.js +2 -0
- package/dist/esm/resources/DatacardResource.js.map +1 -1
- package/dist/esm/resources/EventsResource.js +2 -0
- package/dist/esm/resources/EventsResource.js.map +1 -1
- package/dist/esm/resources/FactionResource.js +12 -0
- package/dist/esm/resources/FactionResource.js.map +1 -1
- package/dist/esm/resources/GalaxyResource.js +12 -0
- package/dist/esm/resources/GalaxyResource.js.map +1 -1
- package/dist/esm/resources/InventoryResource.js +4 -0
- package/dist/esm/resources/InventoryResource.js.map +1 -1
- package/dist/esm/resources/LocationResource.js +2 -0
- package/dist/esm/resources/LocationResource.js.map +1 -1
- package/dist/esm/resources/MarketResource.js +4 -0
- package/dist/esm/resources/MarketResource.js.map +1 -1
- package/dist/esm/resources/NewsResource.js +6 -0
- package/dist/esm/resources/NewsResource.js.map +1 -1
- package/dist/esm/resources/TypesResource.js +171 -19
- package/dist/esm/resources/TypesResource.js.map +1 -1
- package/dist/esm/types/index.js.map +1 -1
- package/dist/types/SWCombine.d.ts +11 -2
- package/dist/types/SWCombine.d.ts.map +1 -1
- package/dist/types/Timestamp.d.ts +166 -0
- package/dist/types/Timestamp.d.ts.map +1 -0
- package/dist/types/auth/TokenManager.d.ts.map +1 -1
- package/dist/types/http/HttpClient.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/resources/ApiResource.d.ts +2 -0
- package/dist/types/resources/ApiResource.d.ts.map +1 -1
- package/dist/types/resources/CharacterResource.d.ts +53 -5
- package/dist/types/resources/CharacterResource.d.ts.map +1 -1
- package/dist/types/resources/DatacardResource.d.ts +2 -0
- package/dist/types/resources/DatacardResource.d.ts.map +1 -1
- package/dist/types/resources/EventsResource.d.ts +2 -0
- package/dist/types/resources/EventsResource.d.ts.map +1 -1
- package/dist/types/resources/FactionResource.d.ts +12 -0
- package/dist/types/resources/FactionResource.d.ts.map +1 -1
- package/dist/types/resources/GalaxyResource.d.ts +12 -0
- package/dist/types/resources/GalaxyResource.d.ts.map +1 -1
- package/dist/types/resources/InventoryResource.d.ts +4 -0
- package/dist/types/resources/InventoryResource.d.ts.map +1 -1
- package/dist/types/resources/LocationResource.d.ts +2 -0
- package/dist/types/resources/LocationResource.d.ts.map +1 -1
- package/dist/types/resources/MarketResource.d.ts +4 -0
- package/dist/types/resources/MarketResource.d.ts.map +1 -1
- package/dist/types/resources/NewsResource.d.ts +6 -0
- package/dist/types/resources/NewsResource.d.ts.map +1 -1
- package/dist/types/resources/TypesResource.d.ts +41 -16
- package/dist/types/resources/TypesResource.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +1039 -9
- package/dist/types/types/index.d.ts.map +1 -1
- package/package.json +19 -4
package/README.md
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
**Comprehensive TypeScript SDK for the Star Wars Combine API v2.0**
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/swcombine-sdk)
|
|
8
|
-
[](https://www.typescriptlang.org/)
|
|
9
9
|
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
[](https://jonmarkgo.github.io/swcombine-sdk-nodejs/)
|
|
10
11
|
|
|
11
12
|
[Features](#features) •
|
|
12
13
|
[Installation](#installation) •
|
|
@@ -93,6 +94,24 @@ const messages = await authenticatedClient.character.messages.list({
|
|
|
93
94
|
mode: 'received',
|
|
94
95
|
});
|
|
95
96
|
|
|
97
|
+
// List returns metadata items (MessageListItem[])
|
|
98
|
+
const firstMessageId = messages[0]?.attributes.uid;
|
|
99
|
+
if (firstMessageId) {
|
|
100
|
+
const fullMessage = await authenticatedClient.character.messages.get({
|
|
101
|
+
uid: '1:12345',
|
|
102
|
+
messageId: firstMessageId,
|
|
103
|
+
});
|
|
104
|
+
console.log(fullMessage.communication);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Send a message
|
|
108
|
+
// IMPORTANT: use receiver handle(s), not UID(s), for `receivers`
|
|
109
|
+
await authenticatedClient.character.messages.create({
|
|
110
|
+
uid: '1:12345',
|
|
111
|
+
receivers: 'recipient_handle',
|
|
112
|
+
communication: 'Test message',
|
|
113
|
+
});
|
|
114
|
+
|
|
96
115
|
// Get faction information
|
|
97
116
|
const faction = await authenticatedClient.faction.get({
|
|
98
117
|
uid: '20:123',
|
|
@@ -186,214 +205,25 @@ See [OAuth Scopes Guide](docs/SCOPES.md) for all 170+ available scopes.
|
|
|
186
205
|
|
|
187
206
|
## API Resources
|
|
188
207
|
|
|
189
|
-
The SDK provides access to all SW Combine API resources:
|
|
190
|
-
|
|
191
|
-
### API Utilities
|
|
192
|
-
|
|
193
|
-
```typescript
|
|
194
|
-
// Get list of available API resources
|
|
195
|
-
await client.api.getResources();
|
|
196
|
-
|
|
197
|
-
// Test connectivity
|
|
198
|
-
await client.api.helloWorld();
|
|
199
|
-
await client.api.helloAuth(); // Requires authentication
|
|
200
|
-
|
|
201
|
-
// Get available permissions and rate limits
|
|
202
|
-
await client.api.permissions();
|
|
203
|
-
await client.api.rateLimits();
|
|
204
|
-
|
|
205
|
-
// Time conversion (CGT = Combine Galactic Time)
|
|
206
|
-
await client.api.time(); // Current time: { years, days, hours, mins, secs }
|
|
207
|
-
await client.api.time({ cgt: 'Y26D100' }); // CGT to Unix timestamp
|
|
208
|
-
await client.api.time({ time: 1701432000 }); // Unix timestamp to CGT
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Timestamp Utility (CGT)
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
import { Timestamp } from 'swcombine-sdk';
|
|
215
|
-
|
|
216
|
-
// Get current CGT
|
|
217
|
-
const now = Timestamp.now();
|
|
218
|
-
|
|
219
|
-
// Create from Unix timestamp (seconds or milliseconds)
|
|
220
|
-
const fromUnix = Timestamp.fromUnixTimestamp(1701432000);
|
|
221
|
-
|
|
222
|
-
// Create from Date
|
|
223
|
-
const fromDate = Timestamp.fromDate(new Date('2025-01-01T00:00:00Z'));
|
|
224
|
-
|
|
225
|
-
// Add or subtract CGT duration
|
|
226
|
-
const plus = fromUnix.add({ days: 2, hours: 3 });
|
|
227
|
-
const minus = fromUnix.subtract({ minutes: 30 });
|
|
228
|
-
|
|
229
|
-
// Format output
|
|
230
|
-
now.toString('full'); // "Year 27 Day 134, 8:45:23"
|
|
231
|
-
now.toString('{hms} on Day {d} of Year {y}'); // "08:45:23 on Day 134 of Year 27"
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
### Characters
|
|
235
|
-
|
|
236
|
-
```typescript
|
|
237
|
-
// Public endpoints
|
|
238
|
-
await client.character.getByHandle({ handle: 'character-handle' });
|
|
239
|
-
|
|
240
|
-
// Authenticated endpoints
|
|
241
|
-
await client.character.me(); // Get authenticated user's character
|
|
242
|
-
await client.character.get({ uid: '1:12345' });
|
|
243
|
-
await client.character.skills.list({ uid: '1:12345' });
|
|
244
|
-
await client.character.privileges.list({ uid: '1:12345' });
|
|
245
|
-
await client.character.privileges.get({ uid: '1:12345', privilegeGroup: 'group', privilege: 'name' });
|
|
246
|
-
await client.character.credits.get({ uid: '1:12345' }); // Returns credit balance as number
|
|
247
|
-
await client.character.credits.transfer({ uid: '1:12345', amount: 1000, recipient: '1:67890' });
|
|
248
|
-
await client.character.creditlog.list({ uid: '1:12345' });
|
|
249
|
-
await client.character.permissions.list({ uid: '1:12345' });
|
|
250
|
-
await client.character.hasPermission({ uid: '1:12345', permission: 'CHARACTER_READ' });
|
|
251
|
-
|
|
252
|
-
// Messages
|
|
253
|
-
await client.character.messages.list({ uid: '1:12345' }); // All messages (sent + received)
|
|
254
|
-
await client.character.messages.list({ uid: '1:12345', mode: 'received' }); // Only received
|
|
255
|
-
await client.character.messages.list({ uid: '1:12345', mode: 'sent' }); // Only sent
|
|
256
|
-
await client.character.messages.get({ uid: '1:12345', messageId: 'msg-123' });
|
|
257
|
-
await client.character.messages.create({ uid: '1:12345', receivers: 'recipient1;recipient2', communication: 'Hello!' });
|
|
258
|
-
await client.character.messages.delete({ uid: '1:12345', messageId: 'msg-123' });
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
### Factions
|
|
262
|
-
|
|
263
|
-
```typescript
|
|
264
|
-
await client.faction.list();
|
|
265
|
-
await client.faction.get({ uid: '20:123' });
|
|
266
|
-
await client.faction.members.list({ factionId: '20:123' });
|
|
267
|
-
await client.faction.budgets.list({ factionId: '20:123' });
|
|
268
|
-
await client.faction.budgets.get({ factionId: '20:123', budgetId: 'budget-uid' });
|
|
269
|
-
await client.faction.stockholders.list({ factionId: '20:123' });
|
|
270
|
-
await client.faction.credits.get({ factionId: '20:123' });
|
|
271
|
-
await client.faction.credits.update({ factionId: '20:123', amount: 1000, recipient: '1:12345' });
|
|
272
|
-
await client.faction.creditlog.list({ factionId: '20:123' });
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### Inventory
|
|
276
|
-
|
|
277
|
-
```typescript
|
|
278
|
-
await client.inventory.get({ uid: '1:12345' });
|
|
279
|
-
await client.inventory.entities.list({
|
|
280
|
-
uid: '1:12345',
|
|
281
|
-
entityType: 'ships', // ships, vehicles, stations, cities, facilities, planets, items, npcs, droids, creatures, materials
|
|
282
|
-
assignType: 'owner', // owner, commander, pilot
|
|
283
|
-
});
|
|
284
|
-
await client.inventory.entities.get({ entityType: 'ships', uid: '5:12345' });
|
|
285
|
-
|
|
286
|
-
// Entity management
|
|
287
|
-
await client.inventory.entities.updateProperty({
|
|
288
|
-
entityType: 'ships',
|
|
289
|
-
uid: '5:12345',
|
|
290
|
-
property: 'name', // name, owner, commander, pilot, infotext, etc.
|
|
291
|
-
new_value: 'New Ship Name',
|
|
292
|
-
});
|
|
293
|
-
await client.inventory.entities.addTag({ entityType: 'ships', uid: '5:12345', tag: 'favorite' });
|
|
294
|
-
await client.inventory.entities.removeTag({ entityType: 'ships', uid: '5:12345', tag: 'favorite' });
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
### Galaxy
|
|
208
|
+
The SDK provides access to all SW Combine API v2.0 resources through a fluent, type-safe interface:
|
|
298
209
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
await client.galaxy.sectors.get({ uid: 'seswenna' }); // Use lowercase sector name
|
|
313
|
-
|
|
314
|
-
// Stations and cities
|
|
315
|
-
await client.galaxy.stations.list();
|
|
316
|
-
await client.galaxy.stations.get({ uid: '6:789' });
|
|
317
|
-
await client.galaxy.cities.list();
|
|
318
|
-
await client.galaxy.cities.get({ uid: '22:101' });
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### Events
|
|
322
|
-
|
|
323
|
-
```typescript
|
|
324
|
-
// Note: Events uses 0-based indexing unlike other endpoints
|
|
325
|
-
await client.events.list({ eventMode: 'personal' }); // personal, faction, inventory, combat
|
|
326
|
-
await client.events.list({ eventMode: 'personal', start_index: 0, item_count: 100 }); // omit eventType to get all
|
|
327
|
-
await client.events.list({ eventMode: 'personal', eventType: 'xp' }); // filter by specific type
|
|
328
|
-
await client.events.get({ uid: 'event-uid' });
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
### Location
|
|
210
|
+
| Resource | Access | Description |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| [`client.api`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_ApiResource.ApiResource.html) | Utilities | Hello world, permissions, rate limits, time conversion |
|
|
213
|
+
| [`client.character`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_CharacterResource.CharacterResource.html) | Characters | Profile, [messages](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_CharacterResource.CharacterMessagesResource.html), [skills](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_CharacterResource.CharacterSkillsResource.html), [privileges](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_CharacterResource.CharacterPrivilegesResource.html), [credits](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_CharacterResource.CharacterCreditsResource.html), [credit log](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_CharacterResource.CharacterCreditlogResource.html) |
|
|
214
|
+
| [`client.faction`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_FactionResource.FactionResource.html) | Factions | Info, [members](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_FactionResource.FactionMembersResource.html), [budgets](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_FactionResource.FactionBudgetsResource.html), [stockholders](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_FactionResource.FactionStockholdersResource.html), [credits](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_FactionResource.FactionCreditsResource.html), [credit log](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_FactionResource.FactionCreditlogResource.html) |
|
|
215
|
+
| [`client.galaxy`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_GalaxyResource.GalaxyResource.html) | Galaxy | [Systems](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_GalaxyResource.GalaxySystemsResource.html), [sectors](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_GalaxyResource.GalaxySectorsResource.html), [planets](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_GalaxyResource.GalaxyPlanetsResource.html), [stations](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_GalaxyResource.GalaxyStationsResource.html), [cities](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_GalaxyResource.GalaxyCitiesResource.html) |
|
|
216
|
+
| [`client.inventory`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_InventoryResource.InventoryResource.html) | Inventory | [Entity listing](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_InventoryResource.InventoryEntitiesResource.html), management, tagging |
|
|
217
|
+
| [`client.market`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_MarketResource.MarketResource.html) | Market | [Vendor listings](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_MarketResource.MarketVendorsResource.html) |
|
|
218
|
+
| [`client.news`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_NewsResource.NewsResource.html) | News | [GNS](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_NewsResource.GNSResource.html) and [Sim News](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_NewsResource.SimNewsResource.html) feeds |
|
|
219
|
+
| [`client.types`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_TypesResource.TypesResource.html) | Types | [Entity types](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_TypesResource.TypesEntitiesResource.html), [classes](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_TypesResource.TypesClassesResource.html), and detailed type info |
|
|
220
|
+
| [`client.events`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_EventsResource.EventsResource.html) | Events | Personal, faction, inventory, and combat events |
|
|
221
|
+
| [`client.location`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_LocationResource.LocationResource.html) | Location | Entity location lookups |
|
|
222
|
+
| [`client.datacard`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_DatacardResource.DatacardResource.html) | Datacards | Datacard management and assignment |
|
|
332
223
|
|
|
333
|
-
|
|
334
|
-
await client.location.get({ entityType: 'characters', uid: '1:12345' });
|
|
335
|
-
await client.location.get({ entityType: 'ships', uid: '5:12345' });
|
|
336
|
-
```
|
|
224
|
+
Also includes a [`Timestamp`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/Timestamp.Timestamp.html) utility for Combine Galactic Time (CGT) conversion and formatting.
|
|
337
225
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
```typescript
|
|
341
|
-
await client.datacard.list({ factionId: '20:123' });
|
|
342
|
-
await client.datacard.get({ uid: 'datacard-uid' });
|
|
343
|
-
await client.datacard.create({ uid: 'datacard-uid', production_entity_uid: '6:789', uses: 10 });
|
|
344
|
-
await client.datacard.delete({ uid: 'datacard-uid', production_entity_uid: '6:789' });
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
### Market
|
|
348
|
-
|
|
349
|
-
```typescript
|
|
350
|
-
await client.market.vendors.list();
|
|
351
|
-
await client.market.vendors.get({ uid: 'vendor-uid' });
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
### News
|
|
355
|
-
|
|
356
|
-
```typescript
|
|
357
|
-
// Galactic News Service (GNS)
|
|
358
|
-
await client.news.gns.list();
|
|
359
|
-
await client.news.gns.list({ category: 'economy', search: 'battle', author: 'John Doe' });
|
|
360
|
-
await client.news.gns.get({ id: 'news-id' });
|
|
361
|
-
|
|
362
|
-
// Sim News
|
|
363
|
-
await client.news.simNews.list();
|
|
364
|
-
await client.news.simNews.list({ category: 'player' });
|
|
365
|
-
await client.news.simNews.get({ id: 'news-id' });
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
### Types
|
|
369
|
-
|
|
370
|
-
```typescript
|
|
371
|
-
// List all entity types
|
|
372
|
-
await client.types.listEntityTypes();
|
|
373
|
-
|
|
374
|
-
// Get entity classes for a specific type
|
|
375
|
-
await client.types.classes.list({ entityType: 'vehicles' });
|
|
376
|
-
await client.types.classes.list({ entityType: 'ships' });
|
|
377
|
-
|
|
378
|
-
// Get entities by type
|
|
379
|
-
await client.types.entities.list({ entityType: 'ships' });
|
|
380
|
-
await client.types.entities.list({ entityType: 'ships', class: 'fighter' });
|
|
381
|
-
await client.types.entities.get({ entityType: 'ships', uid: 'type-uid' });
|
|
382
|
-
|
|
383
|
-
// Get entities by type with pagination metadata
|
|
384
|
-
const rawVehicles = await client.types.entities.listRaw({
|
|
385
|
-
entityType: 'vehicles',
|
|
386
|
-
start_index: 1,
|
|
387
|
-
item_count: 50,
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
// listRaw() is normalized for all entity types:
|
|
391
|
-
// { attributes?: { start, total, count }, items: TypesEntityListItem[] }
|
|
392
|
-
console.log(rawVehicles.attributes?.start, rawVehicles.attributes?.count, rawVehicles.attributes?.total);
|
|
393
|
-
console.log(rawVehicles.items[0]?.attributes.uid, rawVehicles.items[0]?.value);
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
See [API Documentation](docs/API.md) for complete reference.
|
|
226
|
+
For complete method signatures, parameters, and examples, see the **[API Reference Documentation](https://jonmarkgo.github.io/swcombine-sdk-nodejs/)**.
|
|
397
227
|
|
|
398
228
|
## Rate Limiting
|
|
399
229
|
|
|
@@ -442,16 +272,36 @@ try {
|
|
|
442
272
|
Full TypeScript support with intelligent type inference:
|
|
443
273
|
|
|
444
274
|
```typescript
|
|
275
|
+
import { Message, MessageListItem } from 'swcombine-sdk';
|
|
276
|
+
|
|
445
277
|
// Types are automatically inferred
|
|
446
278
|
const character = await client.character.get({ uid: '1:12345' });
|
|
447
279
|
// character: Character
|
|
448
280
|
|
|
449
281
|
// Request parameters are typed
|
|
450
|
-
await client.character.messages.list({
|
|
282
|
+
const listedMessages = await client.character.messages.list({
|
|
451
283
|
uid: '1:12345',
|
|
452
284
|
mode: 'received', // Optional - TypeScript knows valid values: 'sent' | 'received'
|
|
453
285
|
// mode: 'invalid', // TypeScript error
|
|
454
286
|
});
|
|
287
|
+
|
|
288
|
+
const messageListItem: MessageListItem | undefined = listedMessages[0];
|
|
289
|
+
const messageId = messageListItem?.attributes.uid;
|
|
290
|
+
|
|
291
|
+
if (messageId) {
|
|
292
|
+
const messageDetail: Message = await client.character.messages.get({
|
|
293
|
+
uid: '1:12345',
|
|
294
|
+
messageId,
|
|
295
|
+
});
|
|
296
|
+
console.log(messageDetail.communication);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Send message: receivers must be handle(s), not UID(s)
|
|
300
|
+
await client.character.messages.create({
|
|
301
|
+
uid: '1:12345',
|
|
302
|
+
receivers: 'recipient_handle_1;recipient_handle_2',
|
|
303
|
+
communication: 'Hello there',
|
|
304
|
+
});
|
|
455
305
|
```
|
|
456
306
|
|
|
457
307
|
## Configuration Options
|
|
@@ -489,7 +339,10 @@ interface OAuthToken {
|
|
|
489
339
|
|
|
490
340
|
See the [examples](examples/) directory for complete working examples:
|
|
491
341
|
|
|
492
|
-
- **[
|
|
342
|
+
- **[Basic Usage](examples/basic-usage.ts)** - Getting started with the SDK
|
|
343
|
+
- **[OAuth Flow](examples/oauth-flow.ts)** - Complete OAuth 2.0 authentication flow
|
|
344
|
+
- **[OAuth Scopes](examples/oauth-scopes-example.ts)** - Scope usage examples
|
|
345
|
+
- **[Error Handling](examples/error-handling.ts)** - Error handling patterns
|
|
493
346
|
|
|
494
347
|
### Basic Usage
|
|
495
348
|
|
|
@@ -532,10 +385,13 @@ npm run test:integration
|
|
|
532
385
|
|
|
533
386
|
## Documentation
|
|
534
387
|
|
|
388
|
+
- **[API Reference](https://jonmarkgo.github.io/swcombine-sdk-nodejs/)** - Full API reference with all methods, parameters, and examples
|
|
535
389
|
- **[Getting Started Guide](docs/GETTING_STARTED.md)** - Detailed setup and usage
|
|
536
390
|
- **[Authentication Guide](docs/AUTHENTICATION.md)** - OAuth 2.0 setup and token management
|
|
537
391
|
- **[OAuth Scopes Reference](docs/SCOPES.md)** - Complete scope documentation
|
|
538
|
-
- **[
|
|
392
|
+
- **[Getting an OAuth Token](docs/getting-oauth-token.md)** - Step-by-step token guide
|
|
393
|
+
- **[Local Development](docs/LOCAL_DEVELOPMENT.md)** - Development environment setup
|
|
394
|
+
- **[Publishing](docs/PUBLISHING.md)** - NPM publishing guide
|
|
539
395
|
- **[Examples](examples/)** - Working code examples
|
|
540
396
|
|
|
541
397
|
## Development
|
|
@@ -547,12 +403,22 @@ npm install
|
|
|
547
403
|
# Build
|
|
548
404
|
npm run build
|
|
549
405
|
|
|
550
|
-
# Run tests
|
|
406
|
+
# Run unit tests (fast, no API calls)
|
|
551
407
|
npm test
|
|
552
408
|
|
|
553
|
-
# Run
|
|
409
|
+
# Run unit tests in watch mode
|
|
410
|
+
npm run test:watch
|
|
411
|
+
|
|
412
|
+
# Run all integration tests (requires .env with API credentials)
|
|
554
413
|
npm run test:integration
|
|
555
414
|
|
|
415
|
+
# Run integration tests for a specific resource
|
|
416
|
+
npm run test:integration:character
|
|
417
|
+
npm run test:integration:galaxy
|
|
418
|
+
npm run test:integration:faction
|
|
419
|
+
# Also: test:integration:api, test:integration:market, test:integration:news,
|
|
420
|
+
# test:integration:types, test:integration:misc
|
|
421
|
+
|
|
556
422
|
# Lint
|
|
557
423
|
npm run lint
|
|
558
424
|
|
|
@@ -563,13 +429,14 @@ npm run format
|
|
|
563
429
|
## Requirements
|
|
564
430
|
|
|
565
431
|
- Node.js 18 or higher
|
|
566
|
-
- TypeScript 5.
|
|
432
|
+
- TypeScript 5.5 or higher (for TypeScript projects)
|
|
567
433
|
|
|
568
434
|
## Links
|
|
569
435
|
|
|
570
|
-
- [
|
|
436
|
+
- [SDK API Reference](https://jonmarkgo.github.io/swcombine-sdk-nodejs/) - Full TypeDoc-generated documentation
|
|
437
|
+
- [SW Combine API Documentation](https://www.swcombine.com/ws/developers/) - Official API docs
|
|
571
438
|
- [npm Package](https://www.npmjs.com/package/swcombine-sdk)
|
|
572
|
-
- [GitHub Repository](https://github.com/
|
|
439
|
+
- [GitHub Repository](https://github.com/jonmarkgo/swcombine-sdk-nodejs)
|
|
573
440
|
|
|
574
441
|
## License
|
|
575
442
|
|
|
@@ -588,7 +455,7 @@ Contributions are welcome! Please:
|
|
|
588
455
|
## Support
|
|
589
456
|
|
|
590
457
|
- Check the [documentation](docs/)
|
|
591
|
-
- Submit issues on [GitHub](https://github.com/
|
|
458
|
+
- Submit issues on [GitHub](https://github.com/jonmarkgo/swcombine-sdk-nodejs/issues)
|
|
592
459
|
- Contact support
|
|
593
460
|
|
|
594
461
|
---
|
package/dist/cjs/SWCombine.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.SWCombine = void 0;
|
|
|
7
7
|
const HttpClient_js_1 = require("./http/HttpClient.js");
|
|
8
8
|
const OAuthClient_js_1 = require("./auth/OAuthClient.js");
|
|
9
9
|
const TokenManager_js_1 = require("./auth/TokenManager.js");
|
|
10
|
+
const errors_js_1 = require("./http/errors.js");
|
|
10
11
|
// Import all resource classes
|
|
11
12
|
const ApiResource_js_1 = require("./resources/ApiResource.js");
|
|
12
13
|
const CharacterResource_js_1 = require("./resources/CharacterResource.js");
|
|
@@ -21,26 +22,43 @@ const LocationResource_js_1 = require("./resources/LocationResource.js");
|
|
|
21
22
|
const DatacardResource_js_1 = require("./resources/DatacardResource.js");
|
|
22
23
|
/**
|
|
23
24
|
* Main SW Combine SDK client
|
|
25
|
+
*
|
|
26
|
+
* **Warning:** Integration tests (`npm run test:integration`) make real API calls
|
|
27
|
+
* to the SW Combine servers. Run `npm test` for unit tests instead.
|
|
28
|
+
*
|
|
29
|
+
* @see https://www.swcombine.com/ws/v2.0/developers/index.php SW Combine API Documentation
|
|
24
30
|
*/
|
|
25
31
|
class SWCombine {
|
|
26
|
-
constructor(config) {
|
|
32
|
+
constructor(config = {}) {
|
|
27
33
|
this.config = config;
|
|
34
|
+
const hasClientId = !!config.clientId?.trim();
|
|
35
|
+
const hasClientSecret = !!config.clientSecret?.trim();
|
|
36
|
+
if (hasClientId !== hasClientSecret) {
|
|
37
|
+
throw new errors_js_1.SWCError('Provide both clientId and clientSecret together, or neither.', {
|
|
38
|
+
type: 'auth',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
28
41
|
// Initialize token manager
|
|
29
42
|
this.tokenManager = new TokenManager_js_1.TokenManager(config.token);
|
|
30
|
-
// Initialize OAuth client
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
// Initialize OAuth client when full OAuth credentials are provided
|
|
44
|
+
if (hasClientId && hasClientSecret) {
|
|
45
|
+
this.oauthClient = new OAuthClient_js_1.OAuthClient({
|
|
46
|
+
clientId: config.clientId,
|
|
47
|
+
clientSecret: config.clientSecret,
|
|
48
|
+
redirectUri: config.redirectUri,
|
|
49
|
+
accessType: config.accessType,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
37
52
|
// Set up token refresh callback
|
|
38
53
|
this.tokenManager.setRefreshCallback(async () => {
|
|
54
|
+
const oauthClient = this.requireOAuthCredentials('refresh access tokens');
|
|
39
55
|
const refreshToken = this.tokenManager.getRefreshToken();
|
|
40
56
|
if (!refreshToken) {
|
|
41
|
-
throw new
|
|
57
|
+
throw new errors_js_1.SWCError('Cannot refresh access token: no refresh token is available.', {
|
|
58
|
+
type: 'auth',
|
|
59
|
+
});
|
|
42
60
|
}
|
|
43
|
-
return
|
|
61
|
+
return oauthClient.refreshToken(refreshToken);
|
|
44
62
|
});
|
|
45
63
|
// Initialize HTTP client
|
|
46
64
|
this.http = new HttpClient_js_1.HttpClient({
|
|
@@ -65,17 +83,20 @@ class SWCombine {
|
|
|
65
83
|
// Set up auth operations
|
|
66
84
|
this.auth = {
|
|
67
85
|
getAuthorizationUrl: (options) => {
|
|
68
|
-
|
|
86
|
+
const oauthClient = this.requireOAuthCredentials('generate an authorization URL');
|
|
87
|
+
return oauthClient.getAuthorizationUrl(options);
|
|
69
88
|
},
|
|
70
89
|
handleCallback: async (query) => {
|
|
71
|
-
const
|
|
90
|
+
const oauthClient = this.requireOAuthCredentials('handle OAuth callbacks');
|
|
91
|
+
const result = await oauthClient.handleCallback(query);
|
|
72
92
|
if (result.success && result.token) {
|
|
73
93
|
this.tokenManager.setToken(result.token);
|
|
74
94
|
}
|
|
75
95
|
return result;
|
|
76
96
|
},
|
|
77
97
|
revokeToken: async (refreshToken) => {
|
|
78
|
-
|
|
98
|
+
const oauthClient = this.requireOAuthCredentials('revoke tokens');
|
|
99
|
+
return oauthClient.revokeToken(refreshToken);
|
|
79
100
|
},
|
|
80
101
|
};
|
|
81
102
|
}
|
|
@@ -101,6 +122,12 @@ class SWCombine {
|
|
|
101
122
|
* Manually refresh the token
|
|
102
123
|
*/
|
|
103
124
|
async refreshToken() {
|
|
125
|
+
this.requireOAuthCredentials('refresh access tokens');
|
|
126
|
+
if (!this.tokenManager.hasRefreshToken()) {
|
|
127
|
+
throw new errors_js_1.SWCError('Cannot refresh access token: no refresh token is available.', {
|
|
128
|
+
type: 'auth',
|
|
129
|
+
});
|
|
130
|
+
}
|
|
104
131
|
await this.tokenManager.refreshToken();
|
|
105
132
|
}
|
|
106
133
|
/**
|
|
@@ -150,6 +177,15 @@ class SWCombine {
|
|
|
150
177
|
onRateLimitUpdate(callback) {
|
|
151
178
|
this.http.setRateLimitCallback(callback);
|
|
152
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Ensure OAuth credentials are configured before running OAuth-only operations.
|
|
182
|
+
*/
|
|
183
|
+
requireOAuthCredentials(operation) {
|
|
184
|
+
if (!this.oauthClient) {
|
|
185
|
+
throw new errors_js_1.SWCError(`Cannot ${operation} without OAuth credentials. Initialize SWCombine with both clientId and clientSecret.`, { type: 'auth' });
|
|
186
|
+
}
|
|
187
|
+
return this.oauthClient;
|
|
188
|
+
}
|
|
153
189
|
}
|
|
154
190
|
exports.SWCombine = SWCombine;
|
|
155
191
|
//# sourceMappingURL=SWCombine.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SWCombine.js","sourceRoot":"","sources":["../../src/SWCombine.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,wDAAkD;AAClD,0DAAoD;AACpD,4DAAsD;
|
|
1
|
+
{"version":3,"file":"SWCombine.js","sourceRoot":"","sources":["../../src/SWCombine.ts"],"names":[],"mappings":"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"}
|