swcombine-sdk 1.7.0 → 2.0.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.
Files changed (100) hide show
  1. package/README.md +43 -214
  2. package/dist/cjs/SWCombine.js +49 -13
  3. package/dist/cjs/SWCombine.js.map +1 -1
  4. package/dist/cjs/Timestamp.js +427 -0
  5. package/dist/cjs/Timestamp.js.map +1 -0
  6. package/dist/cjs/auth/TokenManager.js +7 -2
  7. package/dist/cjs/auth/TokenManager.js.map +1 -1
  8. package/dist/cjs/http/HttpClient.js +3 -0
  9. package/dist/cjs/http/HttpClient.js.map +1 -1
  10. package/dist/cjs/index.js +4 -1
  11. package/dist/cjs/index.js.map +1 -1
  12. package/dist/cjs/resources/ApiResource.js +2 -0
  13. package/dist/cjs/resources/ApiResource.js.map +1 -1
  14. package/dist/cjs/resources/CharacterResource.js +14 -0
  15. package/dist/cjs/resources/CharacterResource.js.map +1 -1
  16. package/dist/cjs/resources/DatacardResource.js +2 -0
  17. package/dist/cjs/resources/DatacardResource.js.map +1 -1
  18. package/dist/cjs/resources/EventsResource.js +2 -0
  19. package/dist/cjs/resources/EventsResource.js.map +1 -1
  20. package/dist/cjs/resources/FactionResource.js +12 -0
  21. package/dist/cjs/resources/FactionResource.js.map +1 -1
  22. package/dist/cjs/resources/GalaxyResource.js +12 -0
  23. package/dist/cjs/resources/GalaxyResource.js.map +1 -1
  24. package/dist/cjs/resources/InventoryResource.js +4 -0
  25. package/dist/cjs/resources/InventoryResource.js.map +1 -1
  26. package/dist/cjs/resources/LocationResource.js +2 -0
  27. package/dist/cjs/resources/LocationResource.js.map +1 -1
  28. package/dist/cjs/resources/MarketResource.js +4 -0
  29. package/dist/cjs/resources/MarketResource.js.map +1 -1
  30. package/dist/cjs/resources/NewsResource.js +6 -0
  31. package/dist/cjs/resources/NewsResource.js.map +1 -1
  32. package/dist/cjs/resources/TypesResource.js +147 -19
  33. package/dist/cjs/resources/TypesResource.js.map +1 -1
  34. package/dist/cjs/types/index.js.map +1 -1
  35. package/dist/esm/SWCombine.js +49 -13
  36. package/dist/esm/SWCombine.js.map +1 -1
  37. package/dist/esm/Timestamp.js +423 -0
  38. package/dist/esm/Timestamp.js.map +1 -0
  39. package/dist/esm/auth/TokenManager.js +7 -2
  40. package/dist/esm/auth/TokenManager.js.map +1 -1
  41. package/dist/esm/http/HttpClient.js +3 -0
  42. package/dist/esm/http/HttpClient.js.map +1 -1
  43. package/dist/esm/index.js +2 -0
  44. package/dist/esm/index.js.map +1 -1
  45. package/dist/esm/resources/ApiResource.js +2 -0
  46. package/dist/esm/resources/ApiResource.js.map +1 -1
  47. package/dist/esm/resources/CharacterResource.js +14 -0
  48. package/dist/esm/resources/CharacterResource.js.map +1 -1
  49. package/dist/esm/resources/DatacardResource.js +2 -0
  50. package/dist/esm/resources/DatacardResource.js.map +1 -1
  51. package/dist/esm/resources/EventsResource.js +2 -0
  52. package/dist/esm/resources/EventsResource.js.map +1 -1
  53. package/dist/esm/resources/FactionResource.js +12 -0
  54. package/dist/esm/resources/FactionResource.js.map +1 -1
  55. package/dist/esm/resources/GalaxyResource.js +12 -0
  56. package/dist/esm/resources/GalaxyResource.js.map +1 -1
  57. package/dist/esm/resources/InventoryResource.js +4 -0
  58. package/dist/esm/resources/InventoryResource.js.map +1 -1
  59. package/dist/esm/resources/LocationResource.js +2 -0
  60. package/dist/esm/resources/LocationResource.js.map +1 -1
  61. package/dist/esm/resources/MarketResource.js +4 -0
  62. package/dist/esm/resources/MarketResource.js.map +1 -1
  63. package/dist/esm/resources/NewsResource.js +6 -0
  64. package/dist/esm/resources/NewsResource.js.map +1 -1
  65. package/dist/esm/resources/TypesResource.js +147 -19
  66. package/dist/esm/resources/TypesResource.js.map +1 -1
  67. package/dist/esm/types/index.js.map +1 -1
  68. package/dist/types/SWCombine.d.ts +11 -2
  69. package/dist/types/SWCombine.d.ts.map +1 -1
  70. package/dist/types/Timestamp.d.ts +166 -0
  71. package/dist/types/Timestamp.d.ts.map +1 -0
  72. package/dist/types/auth/TokenManager.d.ts.map +1 -1
  73. package/dist/types/http/HttpClient.d.ts.map +1 -1
  74. package/dist/types/index.d.ts +1 -0
  75. package/dist/types/index.d.ts.map +1 -1
  76. package/dist/types/resources/ApiResource.d.ts +2 -0
  77. package/dist/types/resources/ApiResource.d.ts.map +1 -1
  78. package/dist/types/resources/CharacterResource.d.ts +16 -2
  79. package/dist/types/resources/CharacterResource.d.ts.map +1 -1
  80. package/dist/types/resources/DatacardResource.d.ts +2 -0
  81. package/dist/types/resources/DatacardResource.d.ts.map +1 -1
  82. package/dist/types/resources/EventsResource.d.ts +2 -0
  83. package/dist/types/resources/EventsResource.d.ts.map +1 -1
  84. package/dist/types/resources/FactionResource.d.ts +12 -0
  85. package/dist/types/resources/FactionResource.d.ts.map +1 -1
  86. package/dist/types/resources/GalaxyResource.d.ts +12 -0
  87. package/dist/types/resources/GalaxyResource.d.ts.map +1 -1
  88. package/dist/types/resources/InventoryResource.d.ts +4 -0
  89. package/dist/types/resources/InventoryResource.d.ts.map +1 -1
  90. package/dist/types/resources/LocationResource.d.ts +2 -0
  91. package/dist/types/resources/LocationResource.d.ts.map +1 -1
  92. package/dist/types/resources/MarketResource.d.ts +4 -0
  93. package/dist/types/resources/MarketResource.d.ts.map +1 -1
  94. package/dist/types/resources/NewsResource.d.ts +6 -0
  95. package/dist/types/resources/NewsResource.d.ts.map +1 -1
  96. package/dist/types/resources/TypesResource.d.ts +17 -16
  97. package/dist/types/resources/TypesResource.d.ts.map +1 -1
  98. package/dist/types/types/index.d.ts +944 -2
  99. package/dist/types/types/index.d.ts.map +1 -1
  100. package/package.json +18 -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
  [![npm version](https://img.shields.io/npm/v/swcombine-sdk.svg)](https://www.npmjs.com/package/swcombine-sdk)
8
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue.svg)](https://www.typescriptlang.org/)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.5+-blue.svg)](https://www.typescriptlang.org/)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+ [![API Docs](https://img.shields.io/badge/API_Docs-TypeDoc-blue.svg)](https://jonmarkgo.github.io/swcombine-sdk-nodejs/)
10
11
 
11
12
  [Features](#features) •
12
13
  [Installation](#installation) •
@@ -186,214 +187,25 @@ See [OAuth Scopes Guide](docs/SCOPES.md) for all 170+ available scopes.
186
187
 
187
188
  ## API Resources
188
189
 
189
- The SDK provides access to all SW Combine API resources:
190
+ The SDK provides access to all SW Combine API v2.0 resources through a fluent, type-safe interface:
190
191
 
191
- ### API Utilities
192
+ | Resource | Access | Description |
193
+ |---|---|---|
194
+ | [`client.api`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_ApiResource.ApiResource.html) | Utilities | Hello world, permissions, rate limits, time conversion |
195
+ | [`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) |
196
+ | [`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) |
197
+ | [`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) |
198
+ | [`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 |
199
+ | [`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) |
200
+ | [`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 |
201
+ | [`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 |
202
+ | [`client.events`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_EventsResource.EventsResource.html) | Events | Personal, faction, inventory, and combat events |
203
+ | [`client.location`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_LocationResource.LocationResource.html) | Location | Entity location lookups |
204
+ | [`client.datacard`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/resources_DatacardResource.DatacardResource.html) | Datacards | Datacard management and assignment |
192
205
 
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
298
-
299
- ```typescript
300
- // Systems, planets, sectors
301
- const systems = await client.galaxy.systems.list();
302
- if (systems.length > 0) {
303
- console.log(systems[0].attributes.uid, systems[0].attributes.name);
304
- }
305
- const rawSectors = await client.galaxy.sectors.listRaw({ start_index: 1, item_count: 10 });
306
- console.log(rawSectors.attributes?.start, rawSectors.attributes?.count, rawSectors.attributes?.total);
307
- console.log(rawSectors.sector?.[0]?.attributes.name);
308
- await client.galaxy.systems.get({ uid: '24:123' });
309
- await client.galaxy.planets.list();
310
- await client.galaxy.planets.get({ uid: '23:456' });
311
- await client.galaxy.sectors.list();
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
206
+ Also includes a [`Timestamp`](https://jonmarkgo.github.io/swcombine-sdk-nodejs/classes/Timestamp.Timestamp.html) utility for Combine Galactic Time (CGT) conversion and formatting.
332
207
 
333
- ```typescript
334
- await client.location.get({ entityType: 'characters', uid: '1:12345' });
335
- await client.location.get({ entityType: 'ships', uid: '5:12345' });
336
- ```
337
-
338
- ### Datacards
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.
208
+ For complete method signatures, parameters, and examples, see the **[API Reference Documentation](https://jonmarkgo.github.io/swcombine-sdk-nodejs/)**.
397
209
 
398
210
  ## Rate Limiting
399
211
 
@@ -489,7 +301,10 @@ interface OAuthToken {
489
301
 
490
302
  See the [examples](examples/) directory for complete working examples:
491
303
 
492
- - **[OAuth Scopes](examples/oauth-scopes-example.ts)** - 10 examples of scope usage
304
+ - **[Basic Usage](examples/basic-usage.ts)** - Getting started with the SDK
305
+ - **[OAuth Flow](examples/oauth-flow.ts)** - Complete OAuth 2.0 authentication flow
306
+ - **[OAuth Scopes](examples/oauth-scopes-example.ts)** - Scope usage examples
307
+ - **[Error Handling](examples/error-handling.ts)** - Error handling patterns
493
308
 
494
309
  ### Basic Usage
495
310
 
@@ -532,10 +347,13 @@ npm run test:integration
532
347
 
533
348
  ## Documentation
534
349
 
350
+ - **[API Reference](https://jonmarkgo.github.io/swcombine-sdk-nodejs/)** - Full API reference with all methods, parameters, and examples
535
351
  - **[Getting Started Guide](docs/GETTING_STARTED.md)** - Detailed setup and usage
536
352
  - **[Authentication Guide](docs/AUTHENTICATION.md)** - OAuth 2.0 setup and token management
537
353
  - **[OAuth Scopes Reference](docs/SCOPES.md)** - Complete scope documentation
538
- - **[API Reference](docs/API.md)** - Detailed API endpoint documentation
354
+ - **[Getting an OAuth Token](docs/getting-oauth-token.md)** - Step-by-step token guide
355
+ - **[Local Development](docs/LOCAL_DEVELOPMENT.md)** - Development environment setup
356
+ - **[Publishing](docs/PUBLISHING.md)** - NPM publishing guide
539
357
  - **[Examples](examples/)** - Working code examples
540
358
 
541
359
  ## Development
@@ -547,12 +365,22 @@ npm install
547
365
  # Build
548
366
  npm run build
549
367
 
550
- # Run tests
368
+ # Run unit tests (fast, no API calls)
551
369
  npm test
552
370
 
553
- # Run integration tests
371
+ # Run unit tests in watch mode
372
+ npm run test:watch
373
+
374
+ # Run all integration tests (requires .env with API credentials)
554
375
  npm run test:integration
555
376
 
377
+ # Run integration tests for a specific resource
378
+ npm run test:integration:character
379
+ npm run test:integration:galaxy
380
+ npm run test:integration:faction
381
+ # Also: test:integration:api, test:integration:market, test:integration:news,
382
+ # test:integration:types, test:integration:misc
383
+
556
384
  # Lint
557
385
  npm run lint
558
386
 
@@ -563,13 +391,14 @@ npm run format
563
391
  ## Requirements
564
392
 
565
393
  - Node.js 18 or higher
566
- - TypeScript 5.0 or higher (for TypeScript projects)
394
+ - TypeScript 5.5 or higher (for TypeScript projects)
567
395
 
568
396
  ## Links
569
397
 
570
- - [SW Combine API Documentation](https://www.swcombine.com/ws/developers/)
398
+ - [SDK API Reference](https://jonmarkgo.github.io/swcombine-sdk-nodejs/) - Full TypeDoc-generated documentation
399
+ - [SW Combine API Documentation](https://www.swcombine.com/ws/developers/) - Official API docs
571
400
  - [npm Package](https://www.npmjs.com/package/swcombine-sdk)
572
- - [GitHub Repository](https://github.com/yourusername/swcombine-sdk-nodejs)
401
+ - [GitHub Repository](https://github.com/jonmarkgo/swcombine-sdk-nodejs)
573
402
 
574
403
  ## License
575
404
 
@@ -588,7 +417,7 @@ Contributions are welcome! Please:
588
417
  ## Support
589
418
 
590
419
  - Check the [documentation](docs/)
591
- - Submit issues on [GitHub](https://github.com/yourusername/swcombine-sdk-nodejs/issues)
420
+ - Submit issues on [GitHub](https://github.com/jonmarkgo/swcombine-sdk-nodejs/issues)
592
421
  - Contact support
593
422
 
594
423
  ---
@@ -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
- this.oauthClient = new OAuthClient_js_1.OAuthClient({
32
- clientId: config.clientId,
33
- clientSecret: config.clientSecret,
34
- redirectUri: config.redirectUri,
35
- accessType: config.accessType,
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 Error('No refresh token available');
57
+ throw new errors_js_1.SWCError('Cannot refresh access token: no refresh token is available.', {
58
+ type: 'auth',
59
+ });
42
60
  }
43
- return this.oauthClient.refreshToken(refreshToken);
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
- return this.oauthClient.getAuthorizationUrl(options);
86
+ const oauthClient = this.requireOAuthCredentials('generate an authorization URL');
87
+ return oauthClient.getAuthorizationUrl(options);
69
88
  },
70
89
  handleCallback: async (query) => {
71
- const result = await this.oauthClient.handleCallback(query);
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
- return this.oauthClient.revokeToken(refreshToken);
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":"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"}
1
+ {"version":3,"file":"SWCombine.js","sourceRoot":"","sources":["../../src/SWCombine.ts"],"names":[],"mappings":"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"}