swcombine.js 0.0.11 → 0.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.
Files changed (51) hide show
  1. package/LICENSE +5 -17
  2. package/README.md +668 -97
  3. package/dist/index.d.ts +5 -522
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +3 -0
  6. package/dist/index.js.map +23 -0
  7. package/dist/src/auth/index.d.ts +8 -0
  8. package/dist/src/auth/index.d.ts.map +1 -0
  9. package/dist/src/auth/oauth-manager.d.ts +168 -0
  10. package/dist/src/auth/oauth-manager.d.ts.map +1 -0
  11. package/dist/src/auth/oauth.d.ts +101 -0
  12. package/dist/src/auth/oauth.d.ts.map +1 -0
  13. package/dist/src/auth/scopes.d.ts +61 -0
  14. package/dist/src/auth/scopes.d.ts.map +1 -0
  15. package/dist/src/auth/types.d.ts +118 -0
  16. package/dist/src/auth/types.d.ts.map +1 -0
  17. package/dist/src/client/base-resource.d.ts +33 -0
  18. package/dist/src/client/base-resource.d.ts.map +1 -0
  19. package/dist/src/client/client.d.ts +85 -0
  20. package/dist/src/client/client.d.ts.map +1 -0
  21. package/dist/src/client/errors.d.ts +63 -0
  22. package/dist/src/client/errors.d.ts.map +1 -0
  23. package/dist/src/client/http-client.d.ts +35 -0
  24. package/dist/src/client/http-client.d.ts.map +1 -0
  25. package/dist/src/client/index.d.ts +15 -0
  26. package/dist/src/client/index.d.ts.map +1 -0
  27. package/dist/src/client/rate-limit.d.ts +12 -0
  28. package/dist/src/client/rate-limit.d.ts.map +1 -0
  29. package/dist/src/client/resources/api.d.ts +42 -0
  30. package/dist/src/client/resources/api.d.ts.map +1 -0
  31. package/dist/src/client/resources/character.d.ts +98 -0
  32. package/dist/src/client/resources/character.d.ts.map +1 -0
  33. package/dist/src/client/resources/faction.d.ts +70 -0
  34. package/dist/src/client/resources/faction.d.ts.map +1 -0
  35. package/dist/src/client/resources/index.d.ts +8 -0
  36. package/dist/src/client/resources/index.d.ts.map +1 -0
  37. package/dist/src/client/resources/inventory.d.ts +205 -0
  38. package/dist/src/client/resources/inventory.d.ts.map +1 -0
  39. package/dist/src/client/types.d.ts +78 -0
  40. package/dist/src/client/types.d.ts.map +1 -0
  41. package/dist/src/index.d.ts +11 -0
  42. package/dist/src/index.d.ts.map +1 -0
  43. package/dist/src/utils/index.d.ts +6 -0
  44. package/dist/src/utils/index.d.ts.map +1 -0
  45. package/dist/src/utils/timestamp.d.ts +188 -0
  46. package/dist/src/utils/timestamp.d.ts.map +1 -0
  47. package/dist/src/utils/types.d.ts +42 -0
  48. package/dist/src/utils/types.d.ts.map +1 -0
  49. package/package.json +35 -55
  50. package/dist/index.cjs.js +0 -772
  51. package/dist/index.esm.js +0 -772
package/README.md CHANGED
@@ -1,98 +1,669 @@
1
- # Star Wars Combine Web Services 2.0 JS/TS SDK
2
-
3
- Typescript and Javascript SDK for the Star Wars Combine 2.0 Web Services.
4
- The SDK is currently a WIP. Status is as follows:
5
-
6
- ## API SDK Status
7
-
8
- - :x: = Not yet implemented
9
- - :construction: = Partially implemented
10
- - :heavy_check_mark: = Implemented
11
- - :white_check_mark: = Implemented and covered by tests
12
-
13
- | Endpoint | Status | Availability | OAuth scope required (if applicable) |
14
- |--------------------------------------------|--------------------|------------------|------------------------------------------------------------------------------------------------------------|
15
- | **<h3>OAuth</h3>** | :x: | Public | |
16
- | Get token using server flow (node.js only) | :x: | Public | |
17
- | Get token using client flow (browser only) | :x: | Public | |
18
- | Get granted permissions | :x: | OAuth :lock: | |
19
- | **<h3>Permissions</h3>** | :x: | | |
20
- | List permissions | :heavy_check_mark: | Public | |
21
- | **<h3>Rate Limits</h3>** | :x: | | |
22
- | List rate limits | :x: | Public | |
23
- | **<h3>Time</h3>** | :white_check_mark: | | |
24
- | Get current SWC time | :white_check_mark: | Public/on device | |
25
- | Convert between SWC and Unix time | :white_check_mark: | Public/on device | |
26
- | Convert between SWC time and Date objects | :white_check_mark: | Public/on device | |
27
- | **<h3>Character</h3>** | :x: | | |
28
- | Get character info | :x: | OAuth :lock: | character_read |
29
- | Get character's credits | :x: | OAuth :lock: | character_credits |
30
- | Get character's creditlog | :x: | OAuth :lock: | character_credits |
31
- | Get UID by handle(handlecheck) | :x: | Public | |
32
- | List received messages | :x: | OAuth :lock: | messages_read |
33
- | List sent messages | :x: | OAuth :lock: | messages_read |
34
- | Send message from character | :x: | OAuth :lock: | messages_send |
35
- | Get message by id | :x: | OAUth :lock: | messages_read |
36
- | Delete message by id | :x: | OAuth :lock: | messages_delete |
37
- | Get character's skills | :x: | OAuth :lock: | character_skills |
38
- | List character's privileges | :x: | OAuth :lock: | character_privileges |
39
- | Check if character has privilege | :x: | OAuth :lock: | character_privileges, and must be logged in as someone that can view the privileges of others. |
40
- | Grant privilege to a character | :x: | OAuth :lock: | character_privileges, and must be logged in as someone that has the ability to grant privileges to others. |
41
- | Revoke privilege from character | :x: | OAuth :lock: | character_privileges, and must be logged in as someone that can grant/revoke privileges of others. |
42
- | **<h3>Datacard</h3>** | :x: | | |
43
- | List datacards | :x: | OAuth :lock: | faction_datacards_read |
44
- | Get datacard | :x: | OAuth :lock: | faction_datacards_read |
45
- | Assign datacard | :x: | OAuth :lock: | faction_datacards_write |
46
- | Delete datacard assignment | :x: | OAuth :lock: | faction_datacards_write |
47
- | **<h3>Events</h3>** | :x: | | |
48
- | List events for character | :x: | OAuth :lock: | character_events |
49
- | Get event by uid | :x: | OAuth :lock: | character_events |
50
- | **<h3>Factions</h3>** | :x: | | |
51
- | List factions | :x: | Public | |
52
- | Get faction by uid | :x: | Public | |
53
- | Get faction credits | :x: | OAuth :lock: | faction_credits_read |
54
- | Get faction's creditlog | :x: | OAuth :lock: | faction_credits_read |
55
- | Send faction credits | :x: | OAuth :lock: | faction_credits_write |
56
- | List faction's budgets | :x: | OAuth :lock: | faction_budgets_read |
57
- | Get faction budget by uid | :x: | OAuth :lock: | faction_budgets_read |
58
- | List faction members | :x: | OAuth :lock: | faction_members |
59
- | Update faction member's infofields | :x: | OAuth :lock: | faction_members, and must be logged in as someone that can update infofields |
60
- | List faction's stock holders | :x: | OAuth :lock: | faction_stocks |
61
- | **<h3>Galaxy</h3>** | :x: | | |
62
- | List sectors | :x: | Public | |
63
- | Get sector by uid | :x: | Public | |
64
- | List systems | :x: | Public | |
65
- | Get system by uid | :x: | Public | |
66
- | List planets | :x: | Public | |
67
- | Get planet by uid | :x: | Public | |
68
- | List stations | :x: | Public | |
69
- | Get station by uid | :x: | Public | |
70
- | List cities | :x: | Public | |
71
- | Get city by uid | :x: | Public | |
72
- | **<h3>Inventory</h3>** | :x: | | |
73
- | List inventories | :x: | OAuth :lock: | [personal/faction]_inv_overview |
74
- | List entities in inventory | :x: | OAuth :lock: | [personal/faction]\_inv_[inventory]_read |
75
- | Get one entity in inventory | :x: | OAuth :lock: | [personal/faction]\_inv_[inventory]_read |
76
- | Modify entity info | :x: | OAuth :lock: | [personal/faction]\_inv_[inventory]_assign |
77
- | Rename entity | :x: | OAuth :lock: | [personal/faction]\_inv_[inventory]_rename |
78
- | Makeover entity | :x: | OAuth :lock: | [personal/faction]\_inv_[inventory]_makeover |
79
- | Apply tag to entity | :x: | OAuth :lock: | [personal/faction]\_inv_[inventory]_tags_write |
80
- | Remove tag from entity | :x: | OAuth :lock: | [personal/faction]\_inv_[inventory]_tags_write |
81
- | Clear all tags from entity | :x: | OAuth :lock: | [personal/faction]\_inv_[inventory]_tags_write |
82
- | **<h3>Location</h3>** | :x: | | |
83
- | Get location for specified entity | :x: | OAuth :lock: | character_location |
84
- | **<h3>Market</h3>** | :x: | |
85
- | List all public market vendors | :x: | Public |
86
- | Get vendor info | :x: | Public |
87
- | **<h3>News</h3>** | :x: | | |
88
- | List GNS news | :x: | Public | |
89
- | Get one GNS Item | :x: | Public | |
90
- | List Sim news | :x: | Public | |
91
- | Get one Sim news item | :x: | Public | |
92
- | **<h3>Types</h3>** | :x: | | |
93
- | List classes of type | :x: | Public | |
94
- | List entities of type | :x: | Public | |
95
- | List entities by class and type | :x: | Public | |
96
- | Get type for existing entity | :x: | Public | |
97
- | List all entity types | :x: | Public | |
1
+ # swcombine.js
98
2
 
3
+ TypeScript SDK for the [Star Wars Combine API](https://www.swcombine.com/ws)
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun install
9
+ ```
10
+
11
+ ## Features
12
+
13
+ ### ✅ Phase 1: OAuth 2.0 Authentication (Complete)
14
+
15
+ - **Three flexible authentication modes:**
16
+ - Full OAuth mode with automatic token refresh and storage
17
+ - Direct access token mode (for pre-obtained tokens)
18
+ - Utility OAuth mode (URL generation and manual token refresh without storage)
19
+ - Full OAuth 2.0 flow implementation
20
+ - Authorization URL generation
21
+ - Token exchange (authorization code → access token)
22
+ - Automatic token refresh (when using storage)
23
+ - Manual token refresh utility method
24
+ - Token revocation
25
+ - Custom token storage support
26
+ - Built-in memory storage
27
+ - **Strongly-typed OAuth scopes** (100+ scopes with descriptions)
28
+
29
+ ### ✅ Phase 2: HTTP Client (Complete)
30
+
31
+ - Resource-based API client (`client.character.get()`)
32
+ - Automatic authentication with OAuth tokens
33
+ - Comprehensive error handling (typed error classes)
34
+ - Rate limit tracking and RateLimitError
35
+ - Support for 4 core resources: `api`, `character`, `faction`, `inventory`
36
+ - Inventory resource with 11 entity types (ships, vehicles, stations, cities, facilities, planets, items, npcs, droids, creatures, materials)
37
+ - Advanced filtering with InventoryFilters interface
38
+ - Entity-specific operations (properties and tags management)
39
+ - Custom request options (headers, timeout, abort signals)
40
+
41
+ ### 🚧 Phase 3: API Resources (Coming Soon)
42
+
43
+ - Additional API resources (events, market, etc.)
44
+ - Type-safe response types from API spec
45
+ - Auto-generated resource methods
46
+ - Full TypeScript support for all endpoints
47
+
48
+ ## Quick Start
49
+
50
+ ### Authentication
51
+
52
+ The SDK supports three authentication modes:
53
+
54
+ #### 1. Full OAuth Mode (Recommended for Server-Side)
55
+
56
+ Complete OAuth flow with automatic token management and refresh:
57
+
58
+ ```typescript
59
+ import { OAuthManager, SWCClient, MemoryTokenStorage } from "swcombine.js";
60
+
61
+ // Initialize OAuth manager with storage for token persistence
62
+ const storage = new MemoryTokenStorage(); // Or implement custom TokenStorage
63
+ const oauth = new OAuthManager(
64
+ {
65
+ clientId: "your-client-id",
66
+ clientSecret: "your-client-secret",
67
+ redirectUri: "https://example.com/callback",
68
+ defaultScopes: ['character_read', 'faction_members'],
69
+ },
70
+ storage
71
+ );
72
+
73
+ // Step 1: Generate authorization URL
74
+ const authUrl = oauth.getAuthorizationUrl({
75
+ state: "random-csrf-token",
76
+ accessType: "offline", // Request refresh token
77
+ });
78
+
79
+ // Redirect user to authUrl...
80
+
81
+ // Step 2: Handle callback (after user authorizes)
82
+ const tokens = await oauth.handleCallback(authorizationCode);
83
+
84
+ // Step 3: Create API client (tokens auto-refresh)
85
+ const client = new SWCClient({ auth: oauth });
86
+ const character = await client.character.get();
87
+
88
+ // Step 4: Revoke when done
89
+ await oauth.revoke();
90
+ ```
91
+
92
+ #### 2. Direct Access Token Mode
93
+
94
+ Use when you already have an access token (no auto-refresh):
95
+
96
+ ```typescript
97
+ import { SWCClient } from "swcombine.js";
98
+
99
+ // Create client with direct access token
100
+ const client = new SWCClient({ auth: "your-access-token" });
101
+
102
+ // Make API calls (token won't auto-refresh)
103
+ const character = await client.character.get();
104
+ ```
105
+
106
+ #### 3. Utility OAuth Mode
107
+
108
+ Use OAuthManager as a utility without storage (for URL generation or manual token refresh):
109
+
110
+ ```typescript
111
+ import { OAuthManager } from "swcombine.js";
112
+
113
+ // Initialize without storage
114
+ const oauth = new OAuthManager({
115
+ clientId: "your-client-id",
116
+ clientSecret: "your-client-secret",
117
+ redirectUri: "https://example.com/callback",
118
+ });
119
+
120
+ // Generate authorization URL
121
+ const authUrl = oauth.getAuthorizationUrl({
122
+ scopes: ['character_read'],
123
+ });
124
+
125
+ // Manually refresh a token (without storage)
126
+ const newTokens = await oauth.refreshAccessToken(refreshToken);
127
+ // Use newTokens.access_token with direct access token mode
128
+ ```
129
+
130
+ ### Using the HTTP Client
131
+
132
+ Once authenticated, use the `SWCClient` to interact with the API:
133
+
134
+ ```typescript
135
+ import { SWCClient } from "swcombine.js";
136
+
137
+ // Create client (with OAuth manager or direct token)
138
+ const client = new SWCClient({ auth: oauth }); // or { auth: "token" }
139
+
140
+ // Character endpoints
141
+ const character = await client.character.get();
142
+ const skills = await client.character.skills.get();
143
+ const credits = await client.character.credits.get();
144
+ const creditlog = await client.character.creditlog.get();
145
+ const messages = await client.character.messages.get();
146
+ const message = await client.character.messages.get("123"); // Specific message
147
+
148
+ // Transfer character credits
149
+ await client.character.credits.post({
150
+ recipient: "Character Name",
151
+ amount: 50000,
152
+ reason: "Payment for services", // optional
153
+ });
154
+
155
+ // Send a message
156
+ await client.character.messages.put({
157
+ receivers: "Character1;Character2;Character3",
158
+ communication: "Hello everyone!",
159
+ });
160
+
161
+ // Delete a message
162
+ await client.character.messages.delete("message-uid");
163
+
164
+ // Grant or revoke a privilege
165
+ await client.character.privilege.post("privilegegroup/privilege", {
166
+ revoke: "1", // optional - set to revoke instead of grant
167
+ });
168
+
169
+ // Get character UID by handle
170
+ const characterInfo = await client.character.handlecheck.get("CharacterName");
171
+
172
+ // Faction endpoints
173
+ const myFaction = await client.faction.get();
174
+ const specificFaction = await client.faction.get("123");
175
+ const members = await client.faction.members.get();
176
+ const budgets = await client.faction.budgets.get();
177
+ const stockholders = await client.faction.stockholders.get();
178
+
179
+ // Transfer faction credits
180
+ await client.faction.credits.post({
181
+ recipient: "Character Name",
182
+ amount: 100000,
183
+ budget: "budget-uid", // optional
184
+ reason: "Payment for services", // optional
185
+ });
186
+
187
+ // Inventory endpoints
188
+ const inventory = await client.inventory.get("character-uid");
189
+
190
+ // List entity types (ships, vehicles, stations, etc.)
191
+ const ships = await client.inventory.ships.get("character-uid");
192
+ const ownedShips = await client.inventory.ships.get("character-uid", "owner");
193
+
194
+ // List with filters
195
+ const filteredShips = await client.inventory.ships.get("character-uid", "owner", {
196
+ filters: {
197
+ filter_type: ["tags"],
198
+ filter_value: { tags: ["combat", "transport"] },
199
+ filter_inclusion: { tags: "includes" },
200
+ },
201
+ });
202
+
203
+ // Get specific entity
204
+ const ship = await client.inventory.entity.ships.get("ship-uid");
205
+
206
+ // Manage entity properties
207
+ const shipProps = await client.inventory.entity.ships.properties.get("ship-uid");
208
+ await client.inventory.entity.ships.properties.post("ship-uid", {
209
+ property_name: "custom_name",
210
+ property_value: "My Ship",
211
+ });
212
+
213
+ // Manage entity tags
214
+ const shipTags = await client.inventory.entity.ships.tags.get("ship-uid");
215
+ await client.inventory.entity.ships.tags.post("ship-uid", { tags: ["combat"] });
216
+ await client.inventory.entity.ships.tags.delete("ship-uid", { tags: ["old-tag"] });
217
+
218
+ // API utility endpoints
219
+ const rateLimits = await client.api.rateLimits();
220
+ const permissions = await client.api.permissions();
221
+ const serverTime = await client.api.time();
222
+ ```
223
+
224
+ ### Error Handling
225
+
226
+ The client throws typed errors for different failure scenarios:
227
+
228
+ ```typescript
229
+ import {
230
+ SWCClient,
231
+ RateLimitError,
232
+ AuthenticationError,
233
+ ValidationError,
234
+ NotFoundError,
235
+ ServerError,
236
+ NetworkError,
237
+ } from "swcombine.js";
238
+
239
+ try {
240
+ const character = await client.character.get();
241
+ console.log(character);
242
+ } catch (error) {
243
+ if (error instanceof RateLimitError) {
244
+ // Rate limit exceeded
245
+ console.log(`Rate limited! Reset at ${error.rateLimit.reset}`);
246
+ console.log(`Remaining: ${error.rateLimit.remaining}/${error.rateLimit.limit}`);
247
+ } else if (error instanceof AuthenticationError) {
248
+ // 401 - Need to re-authenticate
249
+ console.log("Authentication failed");
250
+ } else if (error instanceof ValidationError) {
251
+ // 400 - Bad request
252
+ console.log("Invalid request:", error.response);
253
+ } else if (error instanceof NotFoundError) {
254
+ // 404 - Resource not found
255
+ console.log("Resource not found");
256
+ } else if (error instanceof ServerError) {
257
+ // 5xx - Server error
258
+ console.log(`Server error (${error.statusCode}):`, error.message);
259
+ } else if (error instanceof NetworkError) {
260
+ // Network/fetch failure
261
+ console.log("Network error:", error.message);
262
+ }
263
+ }
264
+ ```
265
+
266
+ ### Advanced Options
267
+
268
+ Customize individual requests with options:
269
+
270
+ ```typescript
271
+ // Custom headers
272
+ const character = await client.character.get({
273
+ headers: {
274
+ "X-Custom-Header": "value",
275
+ },
276
+ });
277
+
278
+ // Request timeout (in milliseconds)
279
+ const skills = await client.character.skills.get({
280
+ timeout: 5000, // 5 second timeout
281
+ });
282
+
283
+ // Abort signal for cancellation
284
+ const controller = new AbortController();
285
+ const promise = client.faction.members.get({
286
+ signal: controller.signal,
287
+ });
288
+
289
+ // Cancel the request
290
+ controller.abort();
291
+ ```
292
+
293
+ ### Timestamp Utility - Combine Galactic Time (CGT)
294
+
295
+ Convert between Unix timestamps, JavaScript Dates, and Star Wars Combine Galactic Time:
296
+
297
+ ```typescript
298
+ import { Timestamp } from "swcombine.js";
299
+
300
+ // Get current CGT
301
+ const now = Timestamp.now();
302
+ console.log(now.toString()); // "Year 27 Day 134, 8:45:23"
303
+
304
+ // Convert from Unix timestamp
305
+ const timestamp = Timestamp.fromUnixTimestamp(1735920000);
306
+ console.log(timestamp.toString("day")); // "Year 27 Day 12"
307
+
308
+ // Convert from Date
309
+ const date = new Date();
310
+ const cgt = Timestamp.fromDate(date);
311
+
312
+ // Create specific CGT moment
313
+ const moment = new Timestamp({
314
+ year: 25,
315
+ day: 60,
316
+ hour: 12,
317
+ minute: 30,
318
+ });
319
+
320
+ // Access components
321
+ console.log(moment.getYear()); // 25
322
+ console.log(moment.getDay()); // 60
323
+ console.log(moment.getHour()); // 12
324
+ console.log(moment.getMinute()); // 30
325
+
326
+ // Time arithmetic
327
+ const future = moment.add({ days: 5, hours: 3 });
328
+ const past = moment.subtract({ years: 1 });
329
+
330
+ // Get duration between timestamps
331
+ const duration = moment.getDurationTo(future);
332
+ console.log(duration); // { years: 0, days: 5, hours: 3, minutes: 0, seconds: 0 }
333
+
334
+ // Formatting options
335
+ now.toString("full"); // "Year 27 Day 134, 8:45:23"
336
+ now.toString("minute"); // "Year 27 Day 134, 8:45"
337
+ now.toString("day"); // "Year 27 Day 134"
338
+ now.toString("shortFull"); // "Y27 D134, 8:45:23"
339
+ now.toString("shortDay"); // "Y27 D134"
340
+
341
+ // Custom formatting with tags
342
+ now.toString("Day {d} of Year {y} at {hms}");
343
+ // "Day 134 of Year 27 at 08:45:23"
344
+
345
+ // Convert back to Unix/Date
346
+ const unixSeconds = moment.toUnixTimestamp("sec");
347
+ const unixMs = moment.toUnixTimestamp("ms");
348
+ const dateObj = moment.toDate();
349
+ ```
350
+
351
+ ### Custom Token Storage
352
+
353
+ By default, tokens are stored in memory. Implement custom storage for persistence:
354
+
355
+ ```typescript
356
+ import { TokenStorage, StoredTokens } from "swcombine.js";
357
+
358
+ class FileTokenStorage implements TokenStorage {
359
+ async get(): Promise<StoredTokens | null> {
360
+ // Read from file, database, etc.
361
+ }
362
+
363
+ async set(tokens: StoredTokens): Promise<void> {
364
+ // Save to file, database, etc.
365
+ }
366
+
367
+ async clear(): Promise<void> {
368
+ // Clear stored tokens
369
+ }
370
+ }
371
+
372
+ const oauth = new OAuthManager(config, new FileTokenStorage());
373
+ ```
374
+
375
+ ### Working with Scopes
376
+
377
+ All OAuth scopes are strongly typed using string literal union types. Get full IntelliSense and type safety with simple strings!
378
+
379
+ ```typescript
380
+ import { Scopes } from "swcombine.js";
381
+ import type { ScopeKey } from "swcombine.js";
382
+
383
+ // Use string literals with full type safety and IntelliSense
384
+ const myScopes: ScopeKey[] = [
385
+ 'character_read',
386
+ 'faction_members',
387
+ 'messages_read',
388
+ ];
389
+
390
+ // Get scope information programmatically
391
+ const scope = Scopes['character_read'];
392
+ console.log(scope.name); // "character_read"
393
+ console.log(scope.description); // "Read basic character information..."
394
+ console.log(scope.inherits); // [] (array of inherited scopes)
395
+
396
+ // Check what a scope inherits
397
+ const allScope = Scopes['character_all'];
398
+ console.log(allScope.inherits); // ['character_credits_write', ...]
399
+ ```
400
+
401
+ **Available Scope Categories:**
402
+ - Character (read, stats, skills, credits, location, events, etc.)
403
+ - Messages (read, send, delete)
404
+ - Personal Inventory (ships, vehicles, stations, cities, facilities, planets, items, NPCs, droids, materials, creatures)
405
+ - Faction (read, members, stocks, credits, budgets, datacards)
406
+ - Faction Inventory (ships, vehicles, stations, cities, facilities, planets, items, NPCs, droids, materials, creatures)
407
+
408
+ See `src/auth/scopes.ts` for the complete list of 100+ available scopes.
409
+
410
+ ## API Reference
411
+
412
+ ### SWCClient
413
+
414
+ Main client for interacting with the Star Wars Combine API.
415
+
416
+ #### Constructor
417
+
418
+ ```typescript
419
+ new SWCClient(oauth: OAuthManager, baseUrl?: string)
420
+ new SWCClient(config: { oauth: OAuthManager, baseUrl?: string })
421
+ ```
422
+
423
+ #### Resources
424
+
425
+ - `client.api` - API utility endpoints
426
+ - `helloWorld()` - Test endpoint (no auth required)
427
+ - `helloAuth()` - Test authenticated endpoint
428
+ - `permissions()` - List available OAuth permissions
429
+ - `rateLimits()` - Get current rate limit status
430
+ - `time()` - Get server time
431
+
432
+ - `client.character` - Character data
433
+ - `get()` - Get basic character info
434
+ - `creditlog.get()` - Get credit transaction history
435
+ - `permissions.get()` - Get OAuth permissions
436
+ - `skills.get()` - Get character skills
437
+ - `privileges.get()` - Get all privileges
438
+ - `privilege.get(id)` - Get specific privilege
439
+ - `privilege.post(id, data)` - Grant or revoke a privilege (requires `character_privileges`)
440
+ - `credits.get()` - Get current credits
441
+ - `credits.post(data)` - Transfer character credits (requires `character_credits_write`)
442
+ - `messages.get(id?)` - Get messages or specific message
443
+ - `messages.put(data)` - Send a message (requires `messages_send`)
444
+ - `messages.delete(id)` - Delete a message (requires `messages_delete`)
445
+ - `handlecheck.get(handle)` - Get character UID by handle (no authentication required)
446
+
447
+ - `client.faction` - Faction data
448
+ - `get(id?)` - Get faction info (your faction or specific ID)
449
+ - `budgets.get()` - Get all budgets
450
+ - `budget.get(id)` - Get specific budget
451
+ - `creditlog.get()` - Get credit transaction history
452
+ - `members.get()` - Get faction members
453
+ - `stockholders.get()` - Get stockholders
454
+ - `credits.get()` - Get faction credits
455
+ - `credits.post(data)` - Transfer faction credits (requires `faction_credits_write` permission)
456
+
457
+ - `client.inventory` - Inventory data
458
+ - `get(uid)` - Get inventory overview for character or faction
459
+ - Entity type resources (ships, vehicles, stations, cities, facilities, planets, items, npcs, droids, creatures, materials):
460
+ - `<entityType>.get(uid, assignType?, options?)` - List entities with optional filters
461
+ - Entity-specific resources:
462
+ - `entity.<entityType>.get(entityUid)` - Get specific entity details
463
+ - `entity.<entityType>.properties.get(entityUid)` - Get entity properties
464
+ - `entity.<entityType>.properties.post(entityUid, data)` - Set entity properties
465
+ - `entity.<entityType>.tags.get(entityUid)` - Get entity tags
466
+ - `entity.<entityType>.tags.post(entityUid, data)` - Add entity tags
467
+ - `entity.<entityType>.tags.delete(entityUid, data)` - Remove entity tags
468
+
469
+ ### OAuthManager
470
+
471
+ Main class for handling OAuth flows.
472
+
473
+ #### Constructor
474
+
475
+ ```typescript
476
+ new OAuthManager(config: OAuthConfig, storage?: TokenStorage)
477
+ ```
478
+
479
+ #### Methods
480
+
481
+ - `getAuthorizationUrl(options?)` - Generate authorization URL
482
+ - `handleCallback(code)` - Exchange authorization code for tokens
483
+ - `getAccessToken()` - Get valid access token (auto-refreshes)
484
+ - `isAuthenticated()` - Check if user is authenticated
485
+ - `getStoredTokens()` - Get stored tokens
486
+ - `setTokens(tokens)` - Manually set tokens
487
+ - `revoke()` - Revoke refresh token and clear storage
488
+ - `logout()` - Clear stored tokens without revoking
489
+
490
+ ### Low-Level Functions
491
+
492
+ For manual control:
493
+
494
+ ```typescript
495
+ import {
496
+ generateAuthorizationUrl,
497
+ exchangeCodeForToken,
498
+ refreshAccessToken,
499
+ revokeToken,
500
+ } from "swcombine.js";
501
+
502
+ const url = generateAuthorizationUrl({
503
+ clientId: "...",
504
+ redirectUri: "...",
505
+ scopes: ['character_read', 'faction_members'],
506
+ });
507
+
508
+ const tokens = await exchangeCodeForToken({
509
+ code: "...",
510
+ clientId: "...",
511
+ clientSecret: "...",
512
+ redirectUri: "...",
513
+ });
514
+
515
+ const newTokens = await refreshAccessToken({
516
+ refreshToken: "...",
517
+ clientId: "...",
518
+ clientSecret: "...",
519
+ });
520
+
521
+ await revokeToken({
522
+ token: "...",
523
+ clientId: "...",
524
+ });
525
+ ```
526
+
527
+ ## Examples
528
+
529
+ Run the OAuth example:
530
+
531
+ ```bash
532
+ bun run example:oauth
533
+ ```
534
+
535
+ ## Testing
536
+
537
+ ```bash
538
+ bun test
539
+ ```
540
+
541
+ ## Important Notes
542
+
543
+ ### SWC Combine OAuth Quirks
544
+
545
+ The SWC Combine API has some non-standard OAuth behavior:
546
+
547
+ 1. **Authorization header**: Uses `Authorization: OAuth TOKEN` instead of `Bearer`
548
+ 2. **Refresh tokens**: Only returned on **first** token exchange when `access_type=offline`
549
+ 3. **Consent persistence**: Subsequent requests with same scopes won't re-prompt users
550
+
551
+ ## API Endpoint Implementation Status
552
+
553
+ This section tracks which API endpoints have been implemented in the SDK.
554
+
555
+ ### API Resource (5/6 implemented - 83%)
556
+ - [x] `GET /api/helloworld` - `client.api.helloWorld()`
557
+ - [x] `GET /api/helloauth` - `client.api.helloAuth()`
558
+ - [x] `GET /api/permissions` - `client.api.permissions()`
559
+ - [x] `GET /api/ratelimits` - `client.api.rateLimits()`
560
+ - [x] `GET /api/time` - `client.api.time()`
561
+ - [ ] `POST /api/time` - Convert CGT/timestamps
562
+
563
+ ### Character Resource (14/14 implemented - 100%) ✅
564
+ - [x] `GET /character` - `client.character.get()`
565
+ - [x] `GET /character/{uid}/creditlog` - `client.character.creditlog.get()`
566
+ - [x] `GET /character/{uid}/permissions` - `client.character.permissions.get()`
567
+ - [x] `GET /character/{uid}/skills` - `client.character.skills.get()`
568
+ - [x] `GET /character/{uid}/privileges` - `client.character.privileges.get()`
569
+ - [x] `GET /character/{uid}/privileges/{group}/{priv}` - `client.character.privilege.get(id)`
570
+ - [x] `POST /character/{uid}/privileges/{group}/{priv}` - `client.character.privilege.post(id, data)`
571
+ - [x] `GET /character/{uid}/credits` - `client.character.credits.get()`
572
+ - [x] `POST /character/{uid}/credits` - `client.character.credits.post(data)`
573
+ - [x] `GET /character/{uid}/messages` - `client.character.messages.get()`
574
+ - [x] `PUT /character/{uid}/messages` - `client.character.messages.put(data)`
575
+ - [x] `GET /character/{uid}/messages/{msguid}` - `client.character.messages.get(id)`
576
+ - [x] `DELETE /character/{uid}/messages/{msguid}` - `client.character.messages.delete(id)`
577
+ - [x] `GET /character/handlecheck/{handle}` - `client.character.handlecheck.get(handle)`
578
+
579
+ ### Faction Resource (9/11 implemented - 82%)
580
+ - [x] `GET /faction` - `client.faction.get()`
581
+ - [x] `GET /faction/{uid}` - `client.faction.get(id)`
582
+ - [x] `GET /faction/{uid}/budgets` - `client.faction.budgets.get()`
583
+ - [x] `GET /faction/{uid}/budget/{budgetuid}` - `client.faction.budget.get(id)`
584
+ - [x] `GET /faction/{uid}/creditlog` - `client.faction.creditlog.get()`
585
+ - [x] `GET /faction/{uid}/members` - `client.faction.members.get()`
586
+ - [ ] `POST /faction/{uid}/members` - Update member info fields
587
+ - [x] `GET /faction/{uid}/stockholders` - `client.faction.stockholders.get()`
588
+ - [x] `GET /faction/{uid}/credits` - `client.faction.credits.get()`
589
+ - [x] `POST /faction/{uid}/credits` - `client.faction.credits.post(data)`
590
+ - [ ] `GET /factions` - List all factions
591
+
592
+ ### Inventory Resource (3/11 implemented - 27%)
593
+ ⚠️ **Note**: Current implementation uses different structure than API spec
594
+
595
+ - [x] `GET /inventory/{uid}` - `client.inventory.get(uid)`
596
+ - [x] `GET /inventory/{uid}/{entity_type}/{assign_type}` - `client.inventory.{entityType}.get(uid, assignType, options)`
597
+ - [x] `GET /inventory/{entity_type}/{uid}` - `client.inventory.entity.{entityType}.get(entityUid)`
598
+ - [ ] `GET /inventory/{entity_type}/{uid}/properties` - Get entity properties
599
+ - [ ] `POST /inventory/{entity_type}/{uid}/{property}` - Update specific property
600
+ - [ ] `GET /inventory/{entity_type}/{uid}/tags` - Get entity tags
601
+ - [ ] `PUT /inventory/{entity_type}/{uid}/tag/{tag}` - Add/modify tag
602
+ - [ ] `DELETE /inventory/{entity_type}/{uid}/tag/{tag}` - Remove tag
603
+
604
+ ### Datacard Resource (0/4 implemented - 0%)
605
+ - [ ] `GET /datacard/{uid}` - Get datacard details
606
+ - [ ] `POST /datacard/{uid}` - Assign datacard
607
+ - [ ] `DELETE /datacard/{uid}` - Revoke datacard
608
+ - [ ] `GET /datacards/{uid}` - List faction datacards
609
+
610
+ ### Events Resource (0/2 implemented - 0%)
611
+ - [ ] `GET /events/{event_mode}/{event_type?}` - Get events collection
612
+ - [ ] `GET /event/{uid}` - Get specific event
613
+
614
+ ### Galaxy Resource (0/10 implemented - 0%)
615
+ - [ ] `GET /galaxy/cities` - List all cities
616
+ - [ ] `GET /galaxy/cities/{uid}` - Get city details
617
+ - [ ] `GET /galaxy/planets` - List all planets
618
+ - [ ] `GET /galaxy/planets/{uid}` - Get planet details
619
+ - [ ] `GET /galaxy/sectors` - List all sectors
620
+ - [ ] `GET /galaxy/sectors/{uid}` - Get sector details
621
+ - [ ] `GET /galaxy/stations` - List all stations
622
+ - [ ] `GET /galaxy/stations/{uid}` - Get station details
623
+ - [ ] `GET /galaxy/systems` - List all systems
624
+ - [ ] `GET /galaxy/systems/{uid}` - Get system details
625
+
626
+ ### Implementation Summary
627
+ - **API Resource**: 5/6 endpoints (83%)
628
+ - **Character Resource**: 14/14 endpoints (100%) ✅
629
+ - **Faction Resource**: 9/11 endpoints (82%)
630
+ - **Inventory Resource**: 3/11 endpoints (27% - needs refactoring)
631
+ - **Datacard Resource**: 0/4 endpoints (0%)
632
+ - **Events Resource**: 0/2 endpoints (0%)
633
+ - **Galaxy Resource**: 0/10 endpoints (0%)
634
+
635
+ **Overall**: 31/58 endpoints implemented (53%)
636
+
637
+ ## Development Roadmap
638
+
639
+ - [x] Phase 1: OAuth 2.0 Authentication
640
+ - [x] URL generation
641
+ - [x] Token exchange
642
+ - [x] Token refresh
643
+ - [x] Token revocation
644
+ - [x] Token management
645
+ - [x] Tests
646
+ - [x] Phase 2: HTTP Client
647
+ - [x] Resource-based client architecture
648
+ - [x] Automatic authentication
649
+ - [x] Comprehensive error handling
650
+ - [x] Rate limit tracking
651
+ - [x] Core resources (api, character, faction)
652
+ - [x] Inventory resource with 11 entity types
653
+ - [x] Advanced filtering with InventoryFilters
654
+ - [x] Entity-specific operations (properties, tags)
655
+ - [x] Tests
656
+ - [ ] Phase 3: API Resources & Types
657
+ - [ ] Additional resources (events, market, etc.)
658
+ - [ ] Parse response specs
659
+ - [ ] Type-safe response types
660
+ - [ ] Auto-generate from API spec
661
+
662
+ ## License
663
+
664
+ MIT
665
+
666
+ ## Links
667
+
668
+ - [SWC Combine API Documentation](https://www.swcombine.com/ws)
669
+ - [OAuth 2.0 Flow Documentation](https://www.swcombine.com/ws/developers/oauth2/flows/web-server/)