@safercity/sdk-core 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,15 +10,111 @@ Core utilities for SaferCity SDK including fetch abstraction, streaming support,
10
10
  npm install @safercity/sdk-core
11
11
  ```
12
12
 
13
+ ## What's New in v0.1.0
14
+
15
+ - **Auth Mode Types** - Three authentication modes: proxy, direct, and cookie
16
+ - **Token Manager** - Server-side OAuth token management with automatic refresh and request deduplication
17
+ - **Token Storage** - Pluggable token storage interface with in-memory default
18
+
13
19
  ## Features
14
20
 
15
- - **Type definitions** - Common types for API requests/responses
16
- - **Authentication utilities** - Token management, JWT parsing
21
+ - **Type definitions** - Common types for API requests/responses and auth mode configuration
22
+ - **Authentication utilities** - Token management, JWT parsing, OAuth token lifecycle
17
23
  - **Streaming/SSE support** - Cross-platform Server-Sent Events
18
24
  - **Base HTTP client** - Fetch wrapper with timeout and error handling
19
25
 
20
26
  ## Usage
21
27
 
28
+ ### Auth Mode Types
29
+
30
+ The SDK supports three authentication modes for different deployment scenarios:
31
+
32
+ ```typescript
33
+ import type {
34
+ AuthMode,
35
+ ProxyModeConfig,
36
+ DirectModeConfig,
37
+ CookieModeConfig,
38
+ ClientModeConfig,
39
+ } from '@safercity/sdk-core';
40
+ ```
41
+
42
+ | Mode | Flow | Best For |
43
+ |------|------|----------|
44
+ | `"proxy"` | Client -> Your Backend -> SaferCity API | Production web apps (most secure) |
45
+ | `"direct"` | Client -> SaferCity API with external token | White-label apps with external auth (Clerk, Auth0, better-auth) |
46
+ | `"cookie"` | Browser with `credentials: include` | First-party web apps using session cookies |
47
+
48
+ ```typescript
49
+ // Proxy mode (default) - backend adds tenant credentials
50
+ const proxyConfig: ProxyModeConfig = {
51
+ mode: 'proxy',
52
+ proxyBaseUrl: '/api/safercity', // defaults to "/api/safercity"
53
+ };
54
+
55
+ // Direct mode - external auth provider supplies the token
56
+ const directConfig: DirectModeConfig = {
57
+ mode: 'direct',
58
+ baseUrl: 'https://api.safercity.com',
59
+ tenantId: 'tenant-123',
60
+ getAccessToken: () => session?.accessToken,
61
+ };
62
+
63
+ // Cookie mode - session cookies sent automatically
64
+ const cookieConfig: CookieModeConfig = {
65
+ mode: 'cookie',
66
+ baseUrl: 'https://api.safercity.com',
67
+ tenantId: 'tenant-123', // optional, can come from cookie
68
+ };
69
+ ```
70
+
71
+ ### Token Manager
72
+
73
+ Server-side OAuth token management with automatic refresh and concurrent request deduplication:
74
+
75
+ ```typescript
76
+ import { TokenManager, MemoryTokenStorage } from '@safercity/sdk-core';
77
+
78
+ const tokenManager = new TokenManager({
79
+ credentials: {
80
+ clientId: process.env.SAFERCITY_CLIENT_ID!,
81
+ clientSecret: process.env.SAFERCITY_CLIENT_SECRET!,
82
+ tenantId: 'tenant-123', // optional
83
+ },
84
+ baseUrl: 'https://api.safercity.com',
85
+ refreshBuffer: 60000, // refresh 1 minute before expiry (default)
86
+ storage: new MemoryTokenStorage(), // default, or provide custom
87
+ });
88
+
89
+ // Get token (auto-refreshes if expired, dedupes concurrent calls)
90
+ const token = await tokenManager.getToken();
91
+
92
+ // Force refresh (useful for error recovery)
93
+ const freshToken = await tokenManager.forceRefresh();
94
+
95
+ // Check state
96
+ tokenManager.hasToken(); // boolean
97
+ tokenManager.clear(); // remove stored tokens
98
+ ```
99
+
100
+ ### Token Storage
101
+
102
+ Implement the `TokenStorage` interface for custom persistence:
103
+
104
+ ```typescript
105
+ import type { TokenStorage, AuthTokens } from '@safercity/sdk-core';
106
+
107
+ // Built-in: MemoryTokenStorage (default, no persistence)
108
+ import { MemoryTokenStorage } from '@safercity/sdk-core';
109
+
110
+ // Custom implementation
111
+ class MyTokenStorage implements TokenStorage {
112
+ get(): AuthTokens | null { /* ... */ }
113
+ set(tokens: AuthTokens): void { /* ... */ }
114
+ clear(): void { /* ... */ }
115
+ }
116
+ ```
117
+
22
118
  ### Stream Adapters
23
119
 
24
120
  ```typescript
@@ -116,6 +212,27 @@ interface SaferCityConfig {
116
212
  }
117
213
  ```
118
214
 
215
+ ### AuthTokens
216
+
217
+ ```typescript
218
+ interface AuthTokens {
219
+ accessToken: string;
220
+ refreshToken?: string;
221
+ expiresAt?: number;
222
+ tokenType: string;
223
+ }
224
+ ```
225
+
226
+ ### OAuthCredentials
227
+
228
+ ```typescript
229
+ interface OAuthCredentials {
230
+ clientId: string;
231
+ clientSecret: string;
232
+ tenantId?: string;
233
+ }
234
+ ```
235
+
119
236
  ### ServerSentEvent
120
237
 
121
238
  ```typescript
package/dist/index.cjs CHANGED
@@ -56,6 +56,130 @@ function getJwtExpiration(token) {
56
56
  if (!payload?.exp) return null;
57
57
  return payload.exp * 1e3;
58
58
  }
59
+ var TokenManager = class {
60
+ config;
61
+ refreshPromise = null;
62
+ constructor(config) {
63
+ this.config = {
64
+ baseUrl: "https://api.safercity.com",
65
+ refreshBuffer: 6e4,
66
+ ...config,
67
+ storage: config.storage ?? new MemoryTokenStorage(),
68
+ fetch: config.fetch ?? globalThis.fetch.bind(globalThis)
69
+ };
70
+ }
71
+ /**
72
+ * Get a valid access token, refreshing if necessary
73
+ */
74
+ async getToken() {
75
+ const tokens = this.config.storage.get();
76
+ if (tokens && !isTokenExpired(tokens.expiresAt, this.config.refreshBuffer)) {
77
+ return tokens.accessToken;
78
+ }
79
+ if (tokens?.refreshToken) {
80
+ return this.refreshToken(tokens.refreshToken);
81
+ }
82
+ return this.fetchNewToken();
83
+ }
84
+ /**
85
+ * Force fetch a new token (useful for error recovery)
86
+ */
87
+ async forceRefresh() {
88
+ this.config.storage.clear();
89
+ return this.fetchNewToken();
90
+ }
91
+ /**
92
+ * Clear stored tokens
93
+ */
94
+ clear() {
95
+ this.config.storage.clear();
96
+ this.refreshPromise = null;
97
+ }
98
+ /**
99
+ * Check if we have a stored token
100
+ */
101
+ hasToken() {
102
+ return this.config.storage.get() !== null;
103
+ }
104
+ async fetchNewToken() {
105
+ if (this.refreshPromise) {
106
+ return this.refreshPromise;
107
+ }
108
+ this.refreshPromise = this.doFetchToken();
109
+ try {
110
+ return await this.refreshPromise;
111
+ } finally {
112
+ this.refreshPromise = null;
113
+ }
114
+ }
115
+ async doFetchToken() {
116
+ const { credentials, baseUrl } = this.config;
117
+ const response = await this.config.fetch(`${baseUrl}/oauth/token`, {
118
+ method: "POST",
119
+ headers: {
120
+ "Content-Type": "application/json"
121
+ },
122
+ body: JSON.stringify({
123
+ grant_type: "client_credentials",
124
+ client_id: credentials.clientId,
125
+ client_secret: credentials.clientSecret,
126
+ ...credentials.tenantId && { tenant_id: credentials.tenantId }
127
+ })
128
+ });
129
+ if (!response.ok) {
130
+ const error = await response.text();
131
+ throw new Error(`Failed to fetch token: ${response.status} ${error}`);
132
+ }
133
+ const data = await response.json();
134
+ const tokens = {
135
+ accessToken: data.access_token,
136
+ refreshToken: data.refresh_token,
137
+ expiresAt: Date.now() + data.expires_in * 1e3,
138
+ tokenType: data.token_type
139
+ };
140
+ this.config.storage.set(tokens);
141
+ return tokens.accessToken;
142
+ }
143
+ async refreshToken(refreshToken) {
144
+ if (this.refreshPromise) {
145
+ return this.refreshPromise;
146
+ }
147
+ this.refreshPromise = this.doRefreshToken(refreshToken);
148
+ try {
149
+ return await this.refreshPromise;
150
+ } finally {
151
+ this.refreshPromise = null;
152
+ }
153
+ }
154
+ async doRefreshToken(refreshToken) {
155
+ const { baseUrl } = this.config;
156
+ try {
157
+ const response = await this.config.fetch(`${baseUrl}/oauth/refresh`, {
158
+ method: "POST",
159
+ headers: {
160
+ "Content-Type": "application/json"
161
+ },
162
+ body: JSON.stringify({ refresh_token: refreshToken })
163
+ });
164
+ if (!response.ok) {
165
+ this.config.storage.clear();
166
+ return this.doFetchToken();
167
+ }
168
+ const data = await response.json();
169
+ const tokens = {
170
+ accessToken: data.access_token,
171
+ refreshToken: data.refresh_token ?? refreshToken,
172
+ expiresAt: Date.now() + data.expires_in * 1e3,
173
+ tokenType: data.token_type
174
+ };
175
+ this.config.storage.set(tokens);
176
+ return tokens.accessToken;
177
+ } catch (error) {
178
+ this.config.storage.clear();
179
+ return this.doFetchToken();
180
+ }
181
+ }
182
+ };
59
183
 
60
184
  // src/streaming.ts
61
185
  function parseSSE(buffer) {
@@ -334,7 +458,7 @@ var BaseClient = class {
334
458
  baseUrl: options.baseUrl.replace(/\/$/, "")
335
459
  // Remove trailing slash
336
460
  };
337
- this.fetchFn = options.fetch ?? fetch;
461
+ this.fetchFn = options.fetch ?? (typeof window !== "undefined" ? window.fetch.bind(window) : typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : fetch);
338
462
  }
339
463
  /**
340
464
  * Update authentication token
@@ -358,15 +482,20 @@ var BaseClient = class {
358
482
  * Build full URL from path
359
483
  */
360
484
  buildUrl(path, query) {
361
- const url = new URL(path.startsWith("/") ? path : `/${path}`, this.config.baseUrl);
485
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
486
+ const baseUrl = this.config.baseUrl.replace(/\/$/, "");
487
+ let queryString = "";
362
488
  if (query) {
489
+ const params = new URLSearchParams();
363
490
  Object.entries(query).forEach(([key, value]) => {
364
491
  if (value !== void 0) {
365
- url.searchParams.set(key, String(value));
492
+ params.set(key, String(value));
366
493
  }
367
494
  });
495
+ queryString = params.toString();
368
496
  }
369
- return url.toString();
497
+ const fullPath = baseUrl + normalizedPath;
498
+ return queryString ? `${fullPath}?${queryString}` : fullPath;
370
499
  }
371
500
  /**
372
501
  * Make HTTP request
@@ -434,6 +563,7 @@ exports.BaseClient = BaseClient;
434
563
  exports.FetchStreamAdapter = FetchStreamAdapter;
435
564
  exports.MemoryTokenStorage = MemoryTokenStorage;
436
565
  exports.SaferCityApiError = SaferCityApiError;
566
+ exports.TokenManager = TokenManager;
437
567
  exports.WebStreamAdapter = WebStreamAdapter;
438
568
  exports.createAuthHeader = createAuthHeader;
439
569
  exports.createStreamAdapter = createStreamAdapter;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/auth.ts","../src/streaming.ts","../src/client.ts"],"names":["parsed"],"mappings":";;;AAoEO,IAAM,iBAAA,GAAN,MAAM,kBAAA,SAA0B,KAAA,CAAM;AAAA,EAC3C,WAAA,CACkB,KAAA,EAChB,OAAA,EACgB,MAAA,EACA,OAAA,EAChB;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AALG,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAEA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAGhB,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAAA,EACd;AAAA,EAEA,OAAO,YAAA,CAAa,QAAA,EAAoB,IAAA,EAAkC;AACxE,IAAA,MAAM,SAAA,GAAY,IAAA;AAClB,IAAA,OAAO,IAAI,kBAAA;AAAA,MACT,UAAU,KAAA,IAAS,eAAA;AAAA,MACnB,SAAA,CAAU,OAAA,IAAW,CAAA,2BAAA,EAA8B,QAAA,CAAS,MAAM,CAAA,CAAA;AAAA,MAClE,QAAA,CAAS,MAAA;AAAA,MACT,SAAA,CAAU;AAAA,KACZ;AAAA,EACF;AACF;;;ACpEO,IAAM,qBAAN,MAAiD;AAAA,EAC9C,MAAA,GAA4B,IAAA;AAAA,EAEpC,GAAA,GAAyB;AACvB,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA,EAEA,IAAI,MAAA,EAA0B;AAC5B,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA,EAEA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,IAAA;AAAA,EAChB;AACF;AAKO,SAAS,cAAA,CAAe,SAAA,EAA+B,QAAA,GAAW,GAAA,EAAgB;AACvF,EAAA,IAAI,CAAC,WAAW,OAAO,KAAA;AACvB,EAAA,OAAO,IAAA,CAAK,GAAA,EAAI,GAAI,QAAA,IAAY,SAAA;AAClC;AAKO,SAAS,gBAAA,CAAiB,KAAA,EAAe,IAAA,GAAO,QAAA,EAAkB;AACvE,EAAA,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA;AACzB;AAMO,SAAS,gBAA6C,KAAA,EAAyB;AACpF,EAAA,IAAI;AACF,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAE/B,IAAA,MAAM,OAAA,GAAU,MAAM,CAAC,CAAA;AACvB,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAA,CAAE,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAC,CAAA;AAClE,IAAA,OAAO,IAAA,CAAK,MAAM,OAAO,CAAA;AAAA,EAC3B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAKO,SAAS,iBAAiB,KAAA,EAA8B;AAC7D,EAAA,MAAM,OAAA,GAAU,gBAAkC,KAAK,CAAA;AACvD,EAAA,IAAI,CAAC,OAAA,EAAS,GAAA,EAAK,OAAO,IAAA;AAC1B,EAAA,OAAO,QAAQ,GAAA,GAAM,GAAA;AACvB;;;AC9CO,SAAS,SAAS,MAAA,EAAkE;AACzF,EAAA,MAAM,SAA4B,EAAC;AACnC,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,CAAM,IAAI,CAAA;AAE/B,EAAA,IAAI,eAAyC,EAAC;AAC9C,EAAA,IAAI,YAAsB,EAAC;AAC3B,EAAA,IAAI,SAAA,GAAY,EAAA;AAEhB,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,IAAA,MAAM,IAAA,GAAO,MAAM,CAAC,CAAA;AAGpB,IAAA,IAAI,CAAA,KAAM,KAAA,CAAM,MAAA,GAAS,CAAA,IAAK,IAAA,KAAS,MAAM,CAAC,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA,EAAG;AACnE,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,SAAS,EAAA,EAAI;AAEf,MAAA,IAAI,UAAU,MAAA,GAAS,CAAA,IAAK,YAAA,CAAa,KAAA,IAAS,aAAa,EAAA,EAAI;AACjE,QAAA,MAAA,CAAO,IAAA,CAAK;AAAA,UACV,GAAG,YAAA;AAAA,UACH,IAAA,EAAM,SAAA,CAAU,IAAA,CAAK,IAAI;AAAA,SACP,CAAA;AAAA,MACtB;AACA,MAAA,YAAA,GAAe,EAAC;AAChB,MAAA,SAAA,GAAY,EAAC;AACb,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA;AAGnC,IAAA,IAAI,eAAe,CAAA,EAAG;AACpB,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,KAAA;AACJ,IAAA,IAAI,KAAA;AAEJ,IAAA,IAAI,eAAe,EAAA,EAAI;AACrB,MAAA,KAAA,GAAQ,IAAA;AACR,MAAA,KAAA,GAAQ,EAAA;AAAA,IACV,CAAA,MAAO;AACL,MAAA,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,UAAU,CAAA;AAEhC,MAAA,KAAA,GAAQ,KAAK,KAAA,CAAM,UAAA,GAAa,CAAC,CAAA,CAAE,OAAA,CAAQ,MAAM,EAAE,CAAA;AAAA,IACrD;AAEA,IAAA,QAAQ,KAAA;AAAO,MACb,KAAK,OAAA;AACH,QAAA,YAAA,CAAa,KAAA,GAAQ,KAAA;AACrB,QAAA;AAAA,MACF,KAAK,MAAA;AACH,QAAA,SAAA,CAAU,KAAK,KAAK,CAAA;AACpB,QAAA;AAAA,MACF,KAAK,IAAA;AACH,QAAA,YAAA,CAAa,EAAA,GAAK,KAAA;AAClB,QAAA;AAAA,MACF,KAAK,OAAA;AACH,QAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,EAAO,EAAE,CAAA;AAChC,QAAA,IAAI,CAAC,KAAA,CAAM,KAAK,CAAA,EAAG;AACjB,UAAA,YAAA,CAAa,KAAA,GAAQ,KAAA;AAAA,QACvB;AACA,QAAA;AAAA;AACJ,EACF;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,SAAA,EAAU;AACrC;AAKO,IAAM,mBAAN,MAAgD;AAAA,EACrD,iBAAA,GAA6B;AAC3B,IAAA,OAAO,OAAO,WAAA,KAAgB,WAAA;AAAA,EAChC;AAAA,EAEA,iBAAA,CAAkB,KAAa,OAAA,EAA8D;AAG3F,IAAA,OAAO;AAAA,MACL,CAAC,MAAA,CAAO,aAAa,CAAA,GAAI;AACvB,QAAA,IAAI,WAAA,GAAkC,IAAA;AACtC,QAAA,IAAI,WAAA,GAAyE,IAAA;AAC7E,QAAA,IAAI,UAAA,GAA8C,IAAA;AAClD,QAAA,MAAM,aAAgC,EAAC;AACvC,QAAA,IAAI,IAAA,GAAO,KAAA;AACX,QAAA,IAAI,KAAA,GAAsB,IAAA;AAE1B,QAAA,MAAM,UAAU,MAAM;AACpB,UAAA,IAAI,WAAA,EAAa;AACf,YAAA,WAAA,CAAY,KAAA,EAAM;AAClB,YAAA,WAAA,GAAc,IAAA;AAAA,UAChB;AACA,UAAA,IAAA,GAAO,IAAA;AAAA,QACT,CAAA;AAGA,QAAA,OAAA,EAAS,MAAA,EAAQ,gBAAA,CAAiB,OAAA,EAAS,MAAM;AAC/C,UAAA,OAAA,EAAQ;AACR,UAAA,IAAI,UAAA,EAAY;AACd,YAAA,UAAA,CAAW,IAAI,KAAA,CAAM,SAAS,CAAC,CAAA;AAAA,UACjC;AAAA,QACF,CAAC,CAAA;AAGD,QAAA,WAAA,GAAc,IAAI,YAAY,GAAG,CAAA;AAEjC,QAAA,WAAA,CAAY,SAAS,MAAM;AACzB,UAAA,OAAA,EAAS,MAAA,IAAS;AAAA,QACpB,CAAA;AAEA,QAAA,WAAA,CAAY,SAAA,GAAY,CAAC,KAAA,KAAU;AACjC,UAAA,MAAM,QAAA,GAA4B;AAAA,YAChC,EAAA,EAAI,MAAM,WAAA,IAAe,MAAA;AAAA,YACzB,MAAM,KAAA,CAAM;AAAA,WACd;AAEA,UAAA,IAAI,WAAA,EAAa;AACf,YAAA,WAAA,CAAY,EAAE,KAAA,EAAO,QAAA,EAAU,IAAA,EAAM,OAAO,CAAA;AAC5C,YAAA,WAAA,GAAc,IAAA;AACd,YAAA,UAAA,GAAa,IAAA;AAAA,UACf,CAAA,MAAO;AACL,YAAA,UAAA,CAAW,KAAK,QAAQ,CAAA;AAAA,UAC1B;AAAA,QACF,CAAA;AAEA,QAAA,WAAA,CAAY,OAAA,GAAU,CAAC,CAAA,KAAM;AAC3B,UAAA,MAAM,GAAA,GAAM,IAAI,KAAA,CAAM,mBAAmB,CAAA;AACzC,UAAA,KAAA,GAAQ,GAAA;AACR,UAAA,OAAA,EAAS,UAAU,GAAG,CAAA;AACtB,UAAA,OAAA,EAAQ;AAER,UAAA,IAAI,UAAA,EAAY;AACd,YAAA,UAAA,CAAW,GAAG,CAAA;AACd,YAAA,WAAA,GAAc,IAAA;AACd,YAAA,UAAA,GAAa,IAAA;AAAA,UACf;AAAA,QACF,CAAA;AAEA,QAAA,OAAO;AAAA,UACL,MAAM,IAAA,GAAiD;AACrD,YAAA,IAAI,KAAA,EAAO;AACT,cAAA,MAAM,KAAA;AAAA,YACR;AAEA,YAAA,IAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzB,cAAA,OAAO,EAAE,KAAA,EAAO,UAAA,CAAW,KAAA,EAAM,EAAI,MAAM,KAAA,EAAM;AAAA,YACnD;AAEA,YAAA,IAAI,IAAA,EAAM;AACR,cAAA,OAAO,EAAE,KAAA,EAAO,MAAA,EAAyC,IAAA,EAAM,IAAA,EAAK;AAAA,YACtE;AAEA,YAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,cAAA,WAAA,GAAc,OAAA;AACd,cAAA,UAAA,GAAa,MAAA;AAAA,YACf,CAAC,CAAA;AAAA,UACH,CAAA;AAAA,UAEA,MAAM,MAAA,GAAmD;AACvD,YAAA,OAAA,EAAQ;AACR,YAAA,OAAO,EAAE,KAAA,EAAO,MAAA,EAAyC,IAAA,EAAM,IAAA,EAAK;AAAA,UACtE,CAAA;AAAA,UAEA,MAAM,MAAM,CAAA,EAAoD;AAC9D,YAAA,OAAA,EAAQ;AACR,YAAA,MAAM,CAAA;AAAA,UACR;AAAA,SACF;AAAA,MACF;AAAA,KACF;AAAA,EACF;AACF;AAMO,IAAM,qBAAN,MAAkD;AAAA,EACvD,WAAA,CAAoB,UAAwB,KAAA,EAAO;AAA/B,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAAgC;AAAA,EAEpD,iBAAA,GAA6B;AAC3B,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEA,iBAAA,CAAkB,KAAa,OAAA,EAA8D;AAC3F,IAAA,MAAM,UAAU,IAAA,CAAK,OAAA;AAErB,IAAA,OAAO;AAAA,MACL,CAAC,MAAA,CAAO,aAAa,CAAA,GAAI;AACvB,QAAA,IAAI,MAAA,GAAyD,IAAA;AAC7D,QAAA,IAAI,MAAA,GAAS,EAAA;AACb,QAAA,IAAI,IAAA,GAAO,KAAA;AACX,QAAA,MAAM,aAAgC,EAAC;AAEvC,QAAA,MAAM,UAAU,MAAM;AACpB,UAAA,IAAI,MAAA,EAAQ;AACV,YAAA,MAAA,CAAO,MAAA,EAAO,CAAE,KAAA,CAAM,MAAM;AAAA,YAAC,CAAC,CAAA;AAC9B,YAAA,MAAA,GAAS,IAAA;AAAA,UACX;AACA,UAAA,IAAA,GAAO,IAAA;AAAA,QACT,CAAA;AAGA,QAAA,OAAA,EAAS,MAAA,EAAQ,gBAAA,CAAiB,OAAA,EAAS,OAAO,CAAA;AAGlD,QAAA,MAAM,YAAA,GAAe,QAAQ,GAAA,EAAK;AAAA,UAChC,MAAA,EAAQ,KAAA;AAAA,UACR,OAAA,EAAS;AAAA,YACP,QAAA,EAAU,mBAAA;AAAA,YACV,eAAA,EAAiB,UAAA;AAAA,YACjB,GAAG,OAAA,EAAS;AAAA,WACd;AAAA,UACA,QAAQ,OAAA,EAAS;AAAA,SAClB,CAAA,CAAE,IAAA,CAAK,CAAC,QAAA,KAAa;AACpB,UAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,YAAA,MAAM,IAAI,MAAM,CAAA,KAAA,EAAQ,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,QAAA,CAAS,UAAU,CAAA,CAAE,CAAA;AAAA,UACnE;AAEA,UAAA,IAAI,CAAC,SAAS,IAAA,EAAM;AAClB,YAAA,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAAA,UAClD;AAEA,UAAA,OAAA,EAAS,MAAA,IAAS;AAClB,UAAA,MAAA,GAAS,QAAA,CAAS,KAAK,SAAA,EAAU;AACjC,UAAA,OAAO,MAAA;AAAA,QACT,CAAC,CAAA,CAAE,KAAA,CAAM,CAAC,GAAA,KAAQ;AAChB,UAAA,OAAA,EAAS,UAAU,GAAG,CAAA;AACtB,UAAA,MAAM,GAAA;AAAA,QACR,CAAC,CAAA;AAED,QAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAEhC,QAAA,OAAO;AAAA,UACL,MAAM,IAAA,GAAiD;AAErD,YAAA,IAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzB,cAAA,OAAO,EAAE,KAAA,EAAO,UAAA,CAAW,KAAA,EAAM,EAAI,MAAM,KAAA,EAAM;AAAA,YACnD;AAEA,YAAA,IAAI,IAAA,EAAM;AACR,cAAA,OAAO,EAAE,KAAA,EAAO,MAAA,EAAyC,IAAA,EAAM,IAAA,EAAK;AAAA,YACtE;AAGA,YAAA,IAAI,CAAC,MAAA,EAAQ;AACX,cAAA,MAAA,GAAS,MAAM,YAAA;AAAA,YACjB;AAGA,YAAA,OAAO,UAAA,CAAW,MAAA,KAAW,CAAA,IAAK,CAAC,IAAA,EAAM;AACvC,cAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAM,YAAW,GAAI,MAAM,OAAO,IAAA,EAAK;AAEtD,cAAA,IAAI,UAAA,EAAY;AACd,gBAAA,IAAA,GAAO,IAAA;AAEP,gBAAA,IAAI,MAAA,CAAO,MAAK,EAAG;AACjB,kBAAA,MAAM,EAAE,MAAA,EAAAA,OAAAA,EAAO,GAAI,QAAA,CAAS,SAAS,MAAM,CAAA;AAC3C,kBAAA,UAAA,CAAW,IAAA,CAAK,GAAGA,OAAM,CAAA;AAAA,gBAC3B;AACA,gBAAA;AAAA,cACF;AAEA,cAAA,MAAA,IAAU,QAAQ,MAAA,CAAO,KAAA,EAAO,EAAE,MAAA,EAAQ,MAAM,CAAA;AAChD,cAAA,MAAM,EAAE,MAAA,EAAQ,SAAA,EAAU,GAAI,SAAS,MAAM,CAAA;AAC7C,cAAA,MAAA,GAAS,SAAA;AACT,cAAA,UAAA,CAAW,IAAA,CAAK,GAAG,MAAM,CAAA;AAAA,YAC3B;AAEA,YAAA,IAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzB,cAAA,OAAO,EAAE,KAAA,EAAO,UAAA,CAAW,KAAA,EAAM,EAAI,MAAM,KAAA,EAAM;AAAA,YACnD;AAEA,YAAA,OAAO,EAAE,KAAA,EAAO,MAAA,EAAyC,IAAA,EAAM,IAAA,EAAK;AAAA,UACtE,CAAA;AAAA,UAEA,MAAM,MAAA,GAAmD;AACvD,YAAA,OAAA,EAAQ;AACR,YAAA,OAAO,EAAE,KAAA,EAAO,MAAA,EAAyC,IAAA,EAAM,IAAA,EAAK;AAAA,UACtE,CAAA;AAAA,UAEA,MAAM,MAAM,CAAA,EAAoD;AAC9D,YAAA,OAAA,EAAQ;AACR,YAAA,MAAM,CAAA;AAAA,UACR;AAAA,SACF;AAAA,MACF;AAAA,KACF;AAAA,EACF;AACF;AAKO,SAAS,oBAAoB,OAAA,EAAuC;AAEzE,EAAA,IAAI,OAAO,gBAAgB,WAAA,EAAa;AACtC,IAAA,OAAO,IAAI,gBAAA,EAAiB;AAAA,EAC9B;AAGA,EAAA,OAAO,IAAI,kBAAA,CAAmB,OAAA,IAAW,KAAK,CAAA;AAChD;;;AClUA,SAAS,aAAA,CACP,QACA,OAAA,EACwB;AACxB,EAAA,MAAM,OAAA,GAAkC;AAAA,IACtC,cAAA,EAAgB,kBAAA;AAAA,IAChB,QAAA,EAAU,kBAAA;AAAA,IACV,GAAG,MAAA,CAAO,OAAA;AAAA,IACV,GAAG,OAAA,EAAS;AAAA,GACd;AAEA,EAAA,IAAI,OAAO,KAAA,EAAO;AAChB,IAAA,OAAA,CAAQ,eAAe,CAAA,GAAI,gBAAA,CAAiB,MAAA,CAAO,KAAK,CAAA;AAAA,EAC1D;AAEA,EAAA,IAAI,OAAO,QAAA,EAAU;AACnB,IAAA,OAAA,CAAQ,aAAa,IAAI,MAAA,CAAO,QAAA;AAAA,EAClC;AAEA,EAAA,OAAO,OAAA;AACT;AAKA,SAAS,mBAAA,CAAoB,SAAiB,cAAA,EAA2C;AACvF,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AAEvC,EAAA,MAAM,SAAA,GAAY,WAAW,MAAM;AACjC,IAAA,UAAA,CAAW,MAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,OAAO,IAAI,CAAC,CAAA;AAAA,EAClE,GAAG,OAAO,CAAA;AAGV,EAAA,IAAI,cAAA,EAAgB;AAClB,IAAA,cAAA,CAAe,gBAAA,CAAiB,SAAS,MAAM;AAC7C,MAAA,YAAA,CAAa,SAAS,CAAA;AACtB,MAAA,UAAA,CAAW,KAAA,CAAM,eAAe,MAAM,CAAA;AAAA,IACxC,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,UAAA,CAAW,MAAA;AACpB;AAKO,IAAM,aAAN,MAAiB;AAAA,EACZ,MAAA;AAAA,EACA,OAAA;AAAA,EAEV,YAAY,OAAA,EAA4B;AACtC,IAAA,IAAA,CAAK,MAAA,GAAS;AAAA,MACZ,OAAA,EAAS,GAAA;AAAA,MACT,GAAG,OAAA;AAAA,MACH,OAAA,EAAS,OAAA,CAAQ,OAAA,CAAQ,OAAA,CAAQ,OAAO,EAAE;AAAA;AAAA,KAC5C;AACA,IAAA,IAAA,CAAK,OAAA,GAAU,QAAQ,KAAA,IAAS,KAAA;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,KAAA,EAAiC;AACxC,IAAA,IAAA,CAAK,OAAO,KAAA,GAAQ,KAAA;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,QAAA,EAAoC;AAC9C,IAAA,IAAA,CAAK,OAAO,QAAA,GAAW,QAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,SAAA,GAAuC;AACrC,IAAA,OAAO,EAAE,GAAG,IAAA,CAAK,MAAA,EAAO;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKU,QAAA,CAAS,MAAc,KAAA,EAAuE;AACtG,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GAAI,IAAA,GAAO,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,EAAI,IAAA,CAAK,OAAO,OAAO,CAAA;AAEjF,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM;AAC9C,QAAA,IAAI,UAAU,MAAA,EAAW;AACvB,UAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,GAAA,EAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,QACzC;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAEA,IAAA,OAAO,IAAI,QAAA,EAAS;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAgB,OAAA,CACd,MAAA,EACA,IAAA,EACA,OAAA,EAIyB;AACzB,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,QAAA,CAAS,IAAA,EAAM,SAAS,KAAK,CAAA;AAC9C,IAAA,MAAM,OAAA,GAAU,aAAA,CAAc,IAAA,CAAK,MAAA,EAAQ,OAAO,CAAA;AAClD,IAAA,MAAM,OAAA,GAAU,OAAA,EAAS,OAAA,IAAW,IAAA,CAAK,MAAA,CAAO,OAAA;AAChD,IAAA,MAAM,MAAA,GAAS,mBAAA,CAAoB,OAAA,EAAS,OAAA,EAAS,MAAM,CAAA;AAE3D,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,OAAA,CAAQ,GAAA,EAAK;AAAA,MACvC,MAAA;AAAA,MACA,OAAA;AAAA,MACA,MAAM,OAAA,EAAS,IAAA,GAAO,KAAK,SAAA,CAAU,OAAA,CAAQ,IAAI,CAAA,GAAI,MAAA;AAAA,MACrD;AAAA,KACD,CAAA;AAED,IAAA,IAAI,IAAA;AACJ,IAAA,MAAM,WAAA,GAAc,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA;AAEvD,IAAA,IAAI,WAAA,EAAa,QAAA,CAAS,kBAAkB,CAAA,EAAG;AAC7C,MAAA,IAAA,GAAO,MAAM,SAAS,IAAA,EAAK;AAAA,IAC7B,CAAA,MAAO;AACL,MAAA,IAAA,GAAO,MAAM,SAAS,IAAA,EAAK;AAAA,IAC7B;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,iBAAA,CAAkB,YAAA,CAAa,QAAA,EAAU,IAAI,CAAA;AAAA,IACrD;AAEA,IAAA,OAAO;AAAA,MACL,IAAA;AAAA,MACA,QAAQ,QAAA,CAAS,MAAA;AAAA,MACjB,SAAS,QAAA,CAAS;AAAA,KACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,GAAA,CACJ,IAAA,EACA,OAAA,EACyB;AACzB,IAAA,OAAO,IAAA,CAAK,OAAA,CAAW,KAAA,EAAO,IAAA,EAAM,OAAO,CAAA;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAA,CACJ,IAAA,EACA,IAAA,EACA,OAAA,EACyB;AACzB,IAAA,OAAO,IAAA,CAAK,QAAW,MAAA,EAAQ,IAAA,EAAM,EAAE,GAAG,OAAA,EAAS,MAAM,CAAA;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,GAAA,CACJ,IAAA,EACA,IAAA,EACA,OAAA,EACyB;AACzB,IAAA,OAAO,IAAA,CAAK,QAAW,KAAA,EAAO,IAAA,EAAM,EAAE,GAAG,OAAA,EAAS,MAAM,CAAA;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAA,CACJ,IAAA,EACA,IAAA,EACA,OAAA,EACyB;AACzB,IAAA,OAAO,IAAA,CAAK,QAAW,OAAA,EAAS,IAAA,EAAM,EAAE,GAAG,OAAA,EAAS,MAAM,CAAA;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAA,CACJ,IAAA,EACA,OAAA,EACyB;AACzB,IAAA,OAAO,IAAA,CAAK,OAAA,CAAW,QAAA,EAAU,IAAA,EAAM,OAAO,CAAA;AAAA,EAChD;AACF","file":"index.cjs","sourcesContent":["/**\n * Common types for SaferCity SDK\n */\n\nexport interface SaferCityConfig {\n /**\n * Base URL for the SaferCity API\n * @example \"https://api.safercity.com\"\n */\n baseUrl: string;\n\n /**\n * Authentication token (JWT)\n */\n token?: string;\n\n /**\n * Tenant ID for multi-tenant operations\n */\n tenantId?: string;\n\n /**\n * Custom fetch implementation (useful for React Native)\n */\n fetch?: typeof fetch;\n\n /**\n * Request timeout in milliseconds\n * @default 30000\n */\n timeout?: number;\n\n /**\n * Custom headers to include in all requests\n */\n headers?: Record<string, string>;\n}\n\nexport interface RequestOptions {\n /**\n * Additional headers for this request\n */\n headers?: Record<string, string>;\n\n /**\n * Request timeout override\n */\n timeout?: number;\n\n /**\n * Abort signal for cancellation\n */\n signal?: AbortSignal;\n}\n\nexport interface ApiResponse<T> {\n data: T;\n status: number;\n headers: Headers;\n}\n\nexport interface ApiError {\n error: string;\n message: string;\n status: number;\n details?: unknown;\n}\n\nexport class SaferCityApiError extends Error {\n constructor(\n public readonly error: string,\n message: string,\n public readonly status: number,\n public readonly details?: unknown\n ) {\n super(message);\n this.name = 'SaferCityApiError';\n }\n\n static fromResponse(response: Response, body: unknown): SaferCityApiError {\n const errorBody = body as Partial<ApiError>;\n return new SaferCityApiError(\n errorBody.error ?? 'unknown_error',\n errorBody.message ?? `Request failed with status ${response.status}`,\n response.status,\n errorBody.details\n );\n }\n}\n\n/**\n * Server-Sent Event structure\n */\nexport interface ServerSentEvent {\n id?: string;\n event?: string;\n data: string;\n retry?: number;\n}\n\n/**\n * Options for SSE connections\n */\nexport interface EventSourceOptions {\n headers?: Record<string, string>;\n signal?: AbortSignal;\n onOpen?: () => void;\n onError?: (error: Error) => void;\n}\n","/**\n * Authentication utilities for SaferCity SDK\n */\n\nexport interface AuthTokens {\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number;\n tokenType: string;\n}\n\nexport interface TokenStorage {\n get(): AuthTokens | null;\n set(tokens: AuthTokens): void;\n clear(): void;\n}\n\n/**\n * In-memory token storage (default)\n */\nexport class MemoryTokenStorage implements TokenStorage {\n private tokens: AuthTokens | null = null;\n\n get(): AuthTokens | null {\n return this.tokens;\n }\n\n set(tokens: AuthTokens): void {\n this.tokens = tokens;\n }\n\n clear(): void {\n this.tokens = null;\n }\n}\n\n/**\n * Check if a token is expired (with buffer)\n */\nexport function isTokenExpired(expiresAt: number | undefined, bufferMs = 60000): boolean {\n if (!expiresAt) return false;\n return Date.now() + bufferMs >= expiresAt;\n}\n\n/**\n * Create Authorization header value\n */\nexport function createAuthHeader(token: string, type = 'Bearer'): string {\n return `${type} ${token}`;\n}\n\n/**\n * Parse JWT payload (without verification)\n * Only use for client-side display, not security decisions\n */\nexport function parseJwtPayload<T = Record<string, unknown>>(token: string): T | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n \n const payload = parts[1];\n const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));\n return JSON.parse(decoded) as T;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract expiration from JWT\n */\nexport function getJwtExpiration(token: string): number | null {\n const payload = parseJwtPayload<{ exp?: number }>(token);\n if (!payload?.exp) return null;\n return payload.exp * 1000; // Convert to milliseconds\n}\n\nexport interface SaferCityJwtPayload {\n sub?: string;\n tenantId?: string;\n environment?: string;\n scopes?: string[];\n iat?: number;\n exp?: number;\n}\n","/**\n * Cross-platform SSE/Streaming support for SaferCity SDK\n * \n * Provides adapters for:\n * - Web browsers (native EventSource)\n * - React Native (fetch-based streaming via expo-fetch or polyfill)\n * - Node.js (fetch-based streaming)\n */\n\nimport type { ServerSentEvent, EventSourceOptions } from './types';\n\n/**\n * Interface for stream adapters\n */\nexport interface StreamAdapter {\n /**\n * Create an async iterable for SSE events\n */\n createEventSource(url: string, options?: EventSourceOptions): AsyncIterable<ServerSentEvent>;\n \n /**\n * Check if native SSE is supported\n */\n supportsNativeSSE(): boolean;\n}\n\n/**\n * Parse SSE data from a text buffer\n */\nexport function parseSSE(buffer: string): { parsed: ServerSentEvent[]; remaining: string } {\n const events: ServerSentEvent[] = [];\n const lines = buffer.split('\\n');\n \n let currentEvent: Partial<ServerSentEvent> = {};\n let dataLines: string[] = [];\n let remaining = '';\n \n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n \n // Check if this might be an incomplete line at the end\n if (i === lines.length - 1 && line !== '' && !buffer.endsWith('\\n')) {\n remaining = line;\n break;\n }\n \n if (line === '') {\n // Empty line = event boundary\n if (dataLines.length > 0 || currentEvent.event || currentEvent.id) {\n events.push({\n ...currentEvent,\n data: dataLines.join('\\n'),\n } as ServerSentEvent);\n }\n currentEvent = {};\n dataLines = [];\n continue;\n }\n \n const colonIndex = line.indexOf(':');\n \n // Comment line (starts with :)\n if (colonIndex === 0) {\n continue;\n }\n \n let field: string;\n let value: string;\n \n if (colonIndex === -1) {\n field = line;\n value = '';\n } else {\n field = line.slice(0, colonIndex);\n // Skip the optional space after colon\n value = line.slice(colonIndex + 1).replace(/^ /, '');\n }\n \n switch (field) {\n case 'event':\n currentEvent.event = value;\n break;\n case 'data':\n dataLines.push(value);\n break;\n case 'id':\n currentEvent.id = value;\n break;\n case 'retry':\n const retry = parseInt(value, 10);\n if (!isNaN(retry)) {\n currentEvent.retry = retry;\n }\n break;\n }\n }\n \n return { parsed: events, remaining };\n}\n\n/**\n * Web/Browser stream adapter using native EventSource\n */\nexport class WebStreamAdapter implements StreamAdapter {\n supportsNativeSSE(): boolean {\n return typeof EventSource !== 'undefined';\n }\n \n createEventSource(url: string, options?: EventSourceOptions): AsyncIterable<ServerSentEvent> {\n const adapter = this;\n \n return {\n [Symbol.asyncIterator]() {\n let eventSource: EventSource | null = null;\n let resolveNext: ((value: IteratorResult<ServerSentEvent>) => void) | null = null;\n let rejectNext: ((error: Error) => void) | null = null;\n const eventQueue: ServerSentEvent[] = [];\n let done = false;\n let error: Error | null = null;\n \n const cleanup = () => {\n if (eventSource) {\n eventSource.close();\n eventSource = null;\n }\n done = true;\n };\n \n // Handle abort signal\n options?.signal?.addEventListener('abort', () => {\n cleanup();\n if (rejectNext) {\n rejectNext(new Error('Aborted'));\n }\n });\n \n // Initialize EventSource\n eventSource = new EventSource(url);\n \n eventSource.onopen = () => {\n options?.onOpen?.();\n };\n \n eventSource.onmessage = (event) => {\n const sseEvent: ServerSentEvent = {\n id: event.lastEventId || undefined,\n data: event.data,\n };\n \n if (resolveNext) {\n resolveNext({ value: sseEvent, done: false });\n resolveNext = null;\n rejectNext = null;\n } else {\n eventQueue.push(sseEvent);\n }\n };\n \n eventSource.onerror = (e) => {\n const err = new Error('EventSource error');\n error = err;\n options?.onError?.(err);\n cleanup();\n \n if (rejectNext) {\n rejectNext(err);\n resolveNext = null;\n rejectNext = null;\n }\n };\n \n return {\n async next(): Promise<IteratorResult<ServerSentEvent>> {\n if (error) {\n throw error;\n }\n \n if (eventQueue.length > 0) {\n return { value: eventQueue.shift()!, done: false };\n }\n \n if (done) {\n return { value: undefined as unknown as ServerSentEvent, done: true };\n }\n \n return new Promise((resolve, reject) => {\n resolveNext = resolve;\n rejectNext = reject;\n });\n },\n \n async return(): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n return { value: undefined as unknown as ServerSentEvent, done: true };\n },\n \n async throw(e: Error): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n throw e;\n },\n };\n },\n };\n }\n}\n\n/**\n * Fetch-based stream adapter for React Native and Node.js\n * Uses ReadableStream to parse SSE from fetch response\n */\nexport class FetchStreamAdapter implements StreamAdapter {\n constructor(private fetchFn: typeof fetch = fetch) {}\n \n supportsNativeSSE(): boolean {\n return false;\n }\n \n createEventSource(url: string, options?: EventSourceOptions): AsyncIterable<ServerSentEvent> {\n const fetchFn = this.fetchFn;\n \n return {\n [Symbol.asyncIterator]() {\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;\n let buffer = '';\n let done = false;\n const eventQueue: ServerSentEvent[] = [];\n \n const cleanup = () => {\n if (reader) {\n reader.cancel().catch(() => {});\n reader = null;\n }\n done = true;\n };\n \n // Handle abort signal\n options?.signal?.addEventListener('abort', cleanup);\n \n // Start fetch\n const fetchPromise = fetchFn(url, {\n method: 'GET',\n headers: {\n 'Accept': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n ...options?.headers,\n },\n signal: options?.signal,\n }).then((response) => {\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n \n if (!response.body) {\n throw new Error('Response body is not available');\n }\n \n options?.onOpen?.();\n reader = response.body.getReader();\n return reader;\n }).catch((err) => {\n options?.onError?.(err);\n throw err;\n });\n \n const decoder = new TextDecoder();\n \n return {\n async next(): Promise<IteratorResult<ServerSentEvent>> {\n // Return queued events first\n if (eventQueue.length > 0) {\n return { value: eventQueue.shift()!, done: false };\n }\n \n if (done) {\n return { value: undefined as unknown as ServerSentEvent, done: true };\n }\n \n // Ensure reader is initialized\n if (!reader) {\n reader = await fetchPromise;\n }\n \n // Read until we have at least one event\n while (eventQueue.length === 0 && !done) {\n const { value, done: readerDone } = await reader.read();\n \n if (readerDone) {\n done = true;\n // Parse any remaining buffer\n if (buffer.trim()) {\n const { parsed } = parseSSE(buffer + '\\n\\n');\n eventQueue.push(...parsed);\n }\n break;\n }\n \n buffer += decoder.decode(value, { stream: true });\n const { parsed, remaining } = parseSSE(buffer);\n buffer = remaining;\n eventQueue.push(...parsed);\n }\n \n if (eventQueue.length > 0) {\n return { value: eventQueue.shift()!, done: false };\n }\n \n return { value: undefined as unknown as ServerSentEvent, done: true };\n },\n \n async return(): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n return { value: undefined as unknown as ServerSentEvent, done: true };\n },\n \n async throw(e: Error): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n throw e;\n },\n };\n },\n };\n }\n}\n\n/**\n * Auto-detect and create the best stream adapter for the current environment\n */\nexport function createStreamAdapter(fetchFn?: typeof fetch): StreamAdapter {\n // Check for native EventSource (browser)\n if (typeof EventSource !== 'undefined') {\n return new WebStreamAdapter();\n }\n \n // Fall back to fetch-based adapter\n return new FetchStreamAdapter(fetchFn ?? fetch);\n}\n","/**\n * Base HTTP client for SaferCity SDK\n */\n\nimport type { SaferCityConfig, RequestOptions, ApiResponse } from './types';\nimport { SaferCityApiError } from './types';\nimport { createAuthHeader } from './auth';\n\nexport interface BaseClientOptions extends SaferCityConfig {}\n\n/**\n * Create base headers for requests\n */\nfunction createHeaders(\n config: SaferCityConfig,\n options?: RequestOptions\n): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n ...config.headers,\n ...options?.headers,\n };\n\n if (config.token) {\n headers['Authorization'] = createAuthHeader(config.token);\n }\n\n if (config.tenantId) {\n headers['X-Tenant-ID'] = config.tenantId;\n }\n\n return headers;\n}\n\n/**\n * Create a timeout signal\n */\nfunction createTimeoutSignal(timeout: number, existingSignal?: AbortSignal): AbortSignal {\n const controller = new AbortController();\n \n const timeoutId = setTimeout(() => {\n controller.abort(new Error(`Request timeout after ${timeout}ms`));\n }, timeout);\n \n // If there's an existing signal, abort when it aborts\n if (existingSignal) {\n existingSignal.addEventListener('abort', () => {\n clearTimeout(timeoutId);\n controller.abort(existingSignal.reason);\n });\n }\n \n return controller.signal;\n}\n\n/**\n * Base HTTP client with common functionality\n */\nexport class BaseClient {\n protected config: Required<Pick<SaferCityConfig, 'baseUrl' | 'timeout'>> & SaferCityConfig;\n protected fetchFn: typeof fetch;\n\n constructor(options: BaseClientOptions) {\n this.config = {\n timeout: 30000,\n ...options,\n baseUrl: options.baseUrl.replace(/\\/$/, ''), // Remove trailing slash\n };\n this.fetchFn = options.fetch ?? fetch;\n }\n\n /**\n * Update authentication token\n */\n setToken(token: string | undefined): void {\n this.config.token = token;\n }\n\n /**\n * Update tenant ID\n */\n setTenantId(tenantId: string | undefined): void {\n this.config.tenantId = tenantId;\n }\n\n /**\n * Get current configuration (read-only)\n */\n getConfig(): Readonly<SaferCityConfig> {\n return { ...this.config };\n }\n\n /**\n * Build full URL from path\n */\n protected buildUrl(path: string, query?: Record<string, string | number | boolean | undefined>): string {\n const url = new URL(path.startsWith('/') ? path : `/${path}`, this.config.baseUrl);\n \n if (query) {\n Object.entries(query).forEach(([key, value]) => {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n });\n }\n \n return url.toString();\n }\n\n /**\n * Make HTTP request\n */\n protected async request<T>(\n method: string,\n path: string,\n options?: RequestOptions & {\n body?: unknown;\n query?: Record<string, string | number | boolean | undefined>;\n }\n ): Promise<ApiResponse<T>> {\n const url = this.buildUrl(path, options?.query);\n const headers = createHeaders(this.config, options);\n const timeout = options?.timeout ?? this.config.timeout;\n const signal = createTimeoutSignal(timeout, options?.signal);\n\n const response = await this.fetchFn(url, {\n method,\n headers,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n signal,\n });\n\n let data: unknown;\n const contentType = response.headers.get('content-type');\n \n if (contentType?.includes('application/json')) {\n data = await response.json();\n } else {\n data = await response.text();\n }\n\n if (!response.ok) {\n throw SaferCityApiError.fromResponse(response, data);\n }\n\n return {\n data: data as T,\n status: response.status,\n headers: response.headers,\n };\n }\n\n /**\n * GET request\n */\n async get<T>(\n path: string,\n options?: RequestOptions & { query?: Record<string, string | number | boolean | undefined> }\n ): Promise<ApiResponse<T>> {\n return this.request<T>('GET', path, options);\n }\n\n /**\n * POST request\n */\n async post<T>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('POST', path, { ...options, body });\n }\n\n /**\n * PUT request\n */\n async put<T>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('PUT', path, { ...options, body });\n }\n\n /**\n * PATCH request\n */\n async patch<T>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('PATCH', path, { ...options, body });\n }\n\n /**\n * DELETE request\n */\n async delete<T>(\n path: string,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('DELETE', path, options);\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/auth.ts","../src/streaming.ts","../src/client.ts"],"names":["parsed"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Common types for SaferCity SDK\n */\n\n// ============================================================================\n// Auth Mode Types\n// ============================================================================\n\n/**\n * Authentication mode determines how the SDK authenticates with SaferCity API\n */\nexport type AuthMode = \"proxy\" | \"direct\" | \"cookie\";\n\n/**\n * Proxy Mode Configuration (DEFAULT - Most Secure)\n * \n * Client → Customer Backend → SaferCity API\n * Backend adds tenant credentials, hiding secrets from client.\n * \n * @example\n * ```tsx\n * <SaferCityProvider mode=\"proxy\" proxyBaseUrl=\"/api/safercity\">\n * <App />\n * </SaferCityProvider>\n * ```\n */\nexport interface ProxyModeConfig {\n mode?: \"proxy\";\n /**\n * Base URL for the proxy endpoint\n * @default \"/api/safercity\"\n */\n proxyBaseUrl?: string;\n}\n\n/**\n * Direct Mode Configuration\n * \n * Client → SaferCity API with external user token\n * For white-label apps using external auth (better-auth, Clerk, Auth0, etc.)\n * \n * @example\n * ```tsx\n * <SaferCityProvider\n * mode=\"direct\"\n * baseUrl=\"https://api.safercity.com\"\n * tenantId=\"tenant-123\"\n * getAccessToken={() => session?.accessToken}\n * >\n * <App />\n * </SaferCityProvider>\n * ```\n */\nexport interface DirectModeConfig {\n mode: \"direct\";\n /**\n * SaferCity API base URL\n */\n baseUrl: string;\n /**\n * Tenant ID for multi-tenant operations\n */\n tenantId: string;\n /**\n * Function to get the current access token (from external auth provider)\n */\n getAccessToken: () => Promise<string | undefined> | string | undefined;\n}\n\n/**\n * Cookie Mode Configuration\n * \n * Browser with credentials: include\n * For first-party web apps using session cookies\n * \n * @example\n * ```tsx\n * <SaferCityProvider\n * mode=\"cookie\"\n * baseUrl=\"https://api.safercity.com\"\n * >\n * <App />\n * </SaferCityProvider>\n * ```\n */\nexport interface CookieModeConfig {\n mode: \"cookie\";\n /**\n * SaferCity API base URL\n */\n baseUrl: string;\n /**\n * Tenant ID (optional, can come from cookie)\n */\n tenantId?: string;\n}\n\n/**\n * Combined client configuration supporting all auth modes\n */\nexport type ClientModeConfig = ProxyModeConfig | DirectModeConfig | CookieModeConfig;\n\n// ============================================================================\n// Base Configuration\n// ============================================================================\n\nexport interface SaferCityConfig {\n /**\n * Base URL for the SaferCity API\n * @example \"https://api.safercity.com\"\n */\n baseUrl: string;\n\n /**\n * Authentication token (JWT)\n */\n token?: string;\n\n /**\n * Tenant ID for multi-tenant operations\n */\n tenantId?: string;\n\n /**\n * Custom fetch implementation (useful for React Native)\n */\n fetch?: typeof fetch;\n\n /**\n * Request timeout in milliseconds\n * @default 30000\n */\n timeout?: number;\n\n /**\n * Custom headers to include in all requests\n */\n headers?: Record<string, string>;\n}\n\nexport interface RequestOptions {\n /**\n * Additional headers for this request\n */\n headers?: Record<string, string>;\n\n /**\n * Request timeout override\n */\n timeout?: number;\n\n /**\n * Abort signal for cancellation\n */\n signal?: AbortSignal;\n}\n\nexport interface ApiResponse<T> {\n data: T;\n status: number;\n headers: Headers;\n}\n\nexport interface ApiError {\n error: string;\n message: string;\n status: number;\n details?: unknown;\n}\n\nexport class SaferCityApiError extends Error {\n constructor(\n public readonly error: string,\n message: string,\n public readonly status: number,\n public readonly details?: unknown\n ) {\n super(message);\n this.name = 'SaferCityApiError';\n }\n\n static fromResponse(response: Response, body: unknown): SaferCityApiError {\n const errorBody = body as Partial<ApiError>;\n return new SaferCityApiError(\n errorBody.error ?? 'unknown_error',\n errorBody.message ?? `Request failed with status ${response.status}`,\n response.status,\n errorBody.details\n );\n }\n}\n\n/**\n * Server-Sent Event structure\n */\nexport interface ServerSentEvent {\n id?: string;\n event?: string;\n data: string;\n retry?: number;\n}\n\n/**\n * Options for SSE connections\n */\nexport interface EventSourceOptions {\n headers?: Record<string, string>;\n signal?: AbortSignal;\n onOpen?: () => void;\n onError?: (error: Error) => void;\n}\n","/**\n * Authentication utilities for SaferCity SDK\n */\n\nexport interface AuthTokens {\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number;\n tokenType: string;\n}\n\nexport interface TokenStorage {\n get(): AuthTokens | null;\n set(tokens: AuthTokens): void;\n clear(): void;\n}\n\n/**\n * In-memory token storage (default)\n */\nexport class MemoryTokenStorage implements TokenStorage {\n private tokens: AuthTokens | null = null;\n\n get(): AuthTokens | null {\n return this.tokens;\n }\n\n set(tokens: AuthTokens): void {\n this.tokens = tokens;\n }\n\n clear(): void {\n this.tokens = null;\n }\n}\n\n/**\n * Check if a token is expired (with buffer)\n */\nexport function isTokenExpired(expiresAt: number | undefined, bufferMs = 60000): boolean {\n if (!expiresAt) return false;\n return Date.now() + bufferMs >= expiresAt;\n}\n\n/**\n * Create Authorization header value\n */\nexport function createAuthHeader(token: string, type = 'Bearer'): string {\n return `${type} ${token}`;\n}\n\n/**\n * Parse JWT payload (without verification)\n * Only use for client-side display, not security decisions\n */\nexport function parseJwtPayload<T = Record<string, unknown>>(token: string): T | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n \n const payload = parts[1];\n const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));\n return JSON.parse(decoded) as T;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract expiration from JWT\n */\nexport function getJwtExpiration(token: string): number | null {\n const payload = parseJwtPayload<{ exp?: number }>(token);\n if (!payload?.exp) return null;\n return payload.exp * 1000; // Convert to milliseconds\n}\n\nexport interface SaferCityJwtPayload {\n sub?: string;\n tenantId?: string;\n environment?: string;\n scopes?: string[];\n iat?: number;\n exp?: number;\n}\n\n// ============================================================================\n// Token Manager for Server-Side OAuth\n// ============================================================================\n\nexport interface OAuthCredentials {\n clientId: string;\n clientSecret: string;\n tenantId?: string;\n}\n\nexport interface TokenManagerConfig {\n credentials: OAuthCredentials;\n baseUrl?: string;\n storage?: TokenStorage;\n /**\n * Buffer time before expiration to trigger refresh (ms)\n * @default 60000 (1 minute)\n */\n refreshBuffer?: number;\n /**\n * Custom fetch implementation\n */\n fetch?: typeof fetch;\n}\n\n/**\n * Token Manager for server-side OAuth token management\n * \n * Handles automatic token refresh before expiration.\n * \n * @example\n * ```typescript\n * const tokenManager = new TokenManager({\n * credentials: {\n * clientId: process.env.SAFERCITY_CLIENT_ID!,\n * clientSecret: process.env.SAFERCITY_CLIENT_SECRET!,\n * },\n * baseUrl: \"https://api.safercity.com\",\n * });\n * \n * // Get token (auto-refreshes if expired)\n * const token = await tokenManager.getToken();\n * ```\n */\nexport class TokenManager {\n private config: Required<Omit<TokenManagerConfig, 'storage' | 'fetch'>> & { \n storage: TokenStorage; \n fetch: typeof fetch;\n };\n private refreshPromise: Promise<string> | null = null;\n\n constructor(config: TokenManagerConfig) {\n this.config = {\n baseUrl: \"https://api.safercity.com\",\n refreshBuffer: 60000,\n ...config,\n storage: config.storage ?? new MemoryTokenStorage(),\n fetch: config.fetch ?? globalThis.fetch.bind(globalThis),\n };\n }\n\n /**\n * Get a valid access token, refreshing if necessary\n */\n async getToken(): Promise<string> {\n const tokens = this.config.storage.get();\n\n // If we have a valid token, return it\n if (tokens && !isTokenExpired(tokens.expiresAt, this.config.refreshBuffer)) {\n return tokens.accessToken;\n }\n\n // If we have a refresh token and access is expired, try refresh\n if (tokens?.refreshToken) {\n return this.refreshToken(tokens.refreshToken);\n }\n\n // Otherwise, get a new token\n return this.fetchNewToken();\n }\n\n /**\n * Force fetch a new token (useful for error recovery)\n */\n async forceRefresh(): Promise<string> {\n this.config.storage.clear();\n return this.fetchNewToken();\n }\n\n /**\n * Clear stored tokens\n */\n clear(): void {\n this.config.storage.clear();\n this.refreshPromise = null;\n }\n\n /**\n * Check if we have a stored token\n */\n hasToken(): boolean {\n return this.config.storage.get() !== null;\n }\n\n private async fetchNewToken(): Promise<string> {\n // Dedupe concurrent requests\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.doFetchToken();\n\n try {\n return await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async doFetchToken(): Promise<string> {\n const { credentials, baseUrl } = this.config;\n\n const response = await this.config.fetch(`${baseUrl}/oauth/token`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n grant_type: \"client_credentials\",\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n ...(credentials.tenantId && { tenant_id: credentials.tenantId }),\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to fetch token: ${response.status} ${error}`);\n }\n\n const data = await response.json() as {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n token_type: string;\n };\n\n const tokens: AuthTokens = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: Date.now() + data.expires_in * 1000,\n tokenType: data.token_type,\n };\n\n this.config.storage.set(tokens);\n return tokens.accessToken;\n }\n\n private async refreshToken(refreshToken: string): Promise<string> {\n // Dedupe concurrent requests\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.doRefreshToken(refreshToken);\n\n try {\n return await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async doRefreshToken(refreshToken: string): Promise<string> {\n const { baseUrl } = this.config;\n\n try {\n const response = await this.config.fetch(`${baseUrl}/oauth/refresh`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ refresh_token: refreshToken }),\n });\n\n if (!response.ok) {\n // Refresh failed, clear and get new token\n this.config.storage.clear();\n return this.doFetchToken();\n }\n\n const data = await response.json() as {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n token_type: string;\n };\n\n const tokens: AuthTokens = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token ?? refreshToken,\n expiresAt: Date.now() + data.expires_in * 1000,\n tokenType: data.token_type,\n };\n\n this.config.storage.set(tokens);\n return tokens.accessToken;\n } catch (error) {\n // On any error, clear and get new token\n this.config.storage.clear();\n return this.doFetchToken();\n }\n }\n}\n","/**\n * Cross-platform SSE/Streaming support for SaferCity SDK\n * \n * Provides adapters for:\n * - Web browsers (native EventSource)\n * - React Native (fetch-based streaming via expo-fetch or polyfill)\n * - Node.js (fetch-based streaming)\n */\n\nimport type { ServerSentEvent, EventSourceOptions } from './types';\n\n/**\n * Interface for stream adapters\n */\nexport interface StreamAdapter {\n /**\n * Create an async iterable for SSE events\n */\n createEventSource(url: string, options?: EventSourceOptions): AsyncIterable<ServerSentEvent>;\n \n /**\n * Check if native SSE is supported\n */\n supportsNativeSSE(): boolean;\n}\n\n/**\n * Parse SSE data from a text buffer\n */\nexport function parseSSE(buffer: string): { parsed: ServerSentEvent[]; remaining: string } {\n const events: ServerSentEvent[] = [];\n const lines = buffer.split('\\n');\n \n let currentEvent: Partial<ServerSentEvent> = {};\n let dataLines: string[] = [];\n let remaining = '';\n \n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n \n // Check if this might be an incomplete line at the end\n if (i === lines.length - 1 && line !== '' && !buffer.endsWith('\\n')) {\n remaining = line;\n break;\n }\n \n if (line === '') {\n // Empty line = event boundary\n if (dataLines.length > 0 || currentEvent.event || currentEvent.id) {\n events.push({\n ...currentEvent,\n data: dataLines.join('\\n'),\n } as ServerSentEvent);\n }\n currentEvent = {};\n dataLines = [];\n continue;\n }\n \n const colonIndex = line.indexOf(':');\n \n // Comment line (starts with :)\n if (colonIndex === 0) {\n continue;\n }\n \n let field: string;\n let value: string;\n \n if (colonIndex === -1) {\n field = line;\n value = '';\n } else {\n field = line.slice(0, colonIndex);\n // Skip the optional space after colon\n value = line.slice(colonIndex + 1).replace(/^ /, '');\n }\n \n switch (field) {\n case 'event':\n currentEvent.event = value;\n break;\n case 'data':\n dataLines.push(value);\n break;\n case 'id':\n currentEvent.id = value;\n break;\n case 'retry':\n const retry = parseInt(value, 10);\n if (!isNaN(retry)) {\n currentEvent.retry = retry;\n }\n break;\n }\n }\n \n return { parsed: events, remaining };\n}\n\n/**\n * Web/Browser stream adapter using native EventSource\n */\nexport class WebStreamAdapter implements StreamAdapter {\n supportsNativeSSE(): boolean {\n return typeof EventSource !== 'undefined';\n }\n \n createEventSource(url: string, options?: EventSourceOptions): AsyncIterable<ServerSentEvent> {\n const adapter = this;\n \n return {\n [Symbol.asyncIterator]() {\n let eventSource: EventSource | null = null;\n let resolveNext: ((value: IteratorResult<ServerSentEvent>) => void) | null = null;\n let rejectNext: ((error: Error) => void) | null = null;\n const eventQueue: ServerSentEvent[] = [];\n let done = false;\n let error: Error | null = null;\n \n const cleanup = () => {\n if (eventSource) {\n eventSource.close();\n eventSource = null;\n }\n done = true;\n };\n \n // Handle abort signal\n options?.signal?.addEventListener('abort', () => {\n cleanup();\n if (rejectNext) {\n rejectNext(new Error('Aborted'));\n }\n });\n \n // Initialize EventSource\n eventSource = new EventSource(url);\n \n eventSource.onopen = () => {\n options?.onOpen?.();\n };\n \n eventSource.onmessage = (event) => {\n const sseEvent: ServerSentEvent = {\n id: event.lastEventId || undefined,\n data: event.data,\n };\n \n if (resolveNext) {\n resolveNext({ value: sseEvent, done: false });\n resolveNext = null;\n rejectNext = null;\n } else {\n eventQueue.push(sseEvent);\n }\n };\n \n eventSource.onerror = (e) => {\n const err = new Error('EventSource error');\n error = err;\n options?.onError?.(err);\n cleanup();\n \n if (rejectNext) {\n rejectNext(err);\n resolveNext = null;\n rejectNext = null;\n }\n };\n \n return {\n async next(): Promise<IteratorResult<ServerSentEvent>> {\n if (error) {\n throw error;\n }\n \n if (eventQueue.length > 0) {\n return { value: eventQueue.shift()!, done: false };\n }\n \n if (done) {\n return { value: undefined as unknown as ServerSentEvent, done: true };\n }\n \n return new Promise((resolve, reject) => {\n resolveNext = resolve;\n rejectNext = reject;\n });\n },\n \n async return(): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n return { value: undefined as unknown as ServerSentEvent, done: true };\n },\n \n async throw(e: Error): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n throw e;\n },\n };\n },\n };\n }\n}\n\n/**\n * Fetch-based stream adapter for React Native and Node.js\n * Uses ReadableStream to parse SSE from fetch response\n */\nexport class FetchStreamAdapter implements StreamAdapter {\n constructor(private fetchFn: typeof fetch = fetch) {}\n \n supportsNativeSSE(): boolean {\n return false;\n }\n \n createEventSource(url: string, options?: EventSourceOptions): AsyncIterable<ServerSentEvent> {\n const fetchFn = this.fetchFn;\n \n return {\n [Symbol.asyncIterator]() {\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;\n let buffer = '';\n let done = false;\n const eventQueue: ServerSentEvent[] = [];\n \n const cleanup = () => {\n if (reader) {\n reader.cancel().catch(() => {});\n reader = null;\n }\n done = true;\n };\n \n // Handle abort signal\n options?.signal?.addEventListener('abort', cleanup);\n \n // Start fetch\n const fetchPromise = fetchFn(url, {\n method: 'GET',\n headers: {\n 'Accept': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n ...options?.headers,\n },\n signal: options?.signal,\n }).then((response) => {\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n \n if (!response.body) {\n throw new Error('Response body is not available');\n }\n \n options?.onOpen?.();\n reader = response.body.getReader();\n return reader;\n }).catch((err) => {\n options?.onError?.(err);\n throw err;\n });\n \n const decoder = new TextDecoder();\n \n return {\n async next(): Promise<IteratorResult<ServerSentEvent>> {\n // Return queued events first\n if (eventQueue.length > 0) {\n return { value: eventQueue.shift()!, done: false };\n }\n \n if (done) {\n return { value: undefined as unknown as ServerSentEvent, done: true };\n }\n \n // Ensure reader is initialized\n if (!reader) {\n reader = await fetchPromise;\n }\n \n // Read until we have at least one event\n while (eventQueue.length === 0 && !done) {\n const { value, done: readerDone } = await reader.read();\n \n if (readerDone) {\n done = true;\n // Parse any remaining buffer\n if (buffer.trim()) {\n const { parsed } = parseSSE(buffer + '\\n\\n');\n eventQueue.push(...parsed);\n }\n break;\n }\n \n buffer += decoder.decode(value, { stream: true });\n const { parsed, remaining } = parseSSE(buffer);\n buffer = remaining;\n eventQueue.push(...parsed);\n }\n \n if (eventQueue.length > 0) {\n return { value: eventQueue.shift()!, done: false };\n }\n \n return { value: undefined as unknown as ServerSentEvent, done: true };\n },\n \n async return(): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n return { value: undefined as unknown as ServerSentEvent, done: true };\n },\n \n async throw(e: Error): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n throw e;\n },\n };\n },\n };\n }\n}\n\n/**\n * Auto-detect and create the best stream adapter for the current environment\n */\nexport function createStreamAdapter(fetchFn?: typeof fetch): StreamAdapter {\n // Check for native EventSource (browser)\n if (typeof EventSource !== 'undefined') {\n return new WebStreamAdapter();\n }\n \n // Fall back to fetch-based adapter\n return new FetchStreamAdapter(fetchFn ?? fetch);\n}\n","/**\n * Base HTTP client for SaferCity SDK\n */\n\nimport type { SaferCityConfig, RequestOptions, ApiResponse } from './types';\nimport { SaferCityApiError } from './types';\nimport { createAuthHeader } from './auth';\n\nexport interface BaseClientOptions extends SaferCityConfig {}\n\n/**\n * Create base headers for requests\n */\nfunction createHeaders(\n config: SaferCityConfig,\n options?: RequestOptions\n): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n ...config.headers,\n ...options?.headers,\n };\n\n if (config.token) {\n headers['Authorization'] = createAuthHeader(config.token);\n }\n\n if (config.tenantId) {\n headers['X-Tenant-ID'] = config.tenantId;\n }\n\n return headers;\n}\n\n/**\n * Create a timeout signal\n */\nfunction createTimeoutSignal(timeout: number, existingSignal?: AbortSignal): AbortSignal {\n const controller = new AbortController();\n \n const timeoutId = setTimeout(() => {\n controller.abort(new Error(`Request timeout after ${timeout}ms`));\n }, timeout);\n \n // If there's an existing signal, abort when it aborts\n if (existingSignal) {\n existingSignal.addEventListener('abort', () => {\n clearTimeout(timeoutId);\n controller.abort(existingSignal.reason);\n });\n }\n \n return controller.signal;\n}\n\n/**\n * Base HTTP client with common functionality\n */\nexport class BaseClient {\n protected config: Required<Pick<SaferCityConfig, 'baseUrl' | 'timeout'>> & SaferCityConfig;\n protected fetchFn: typeof fetch;\n\n constructor(options: BaseClientOptions) {\n this.config = {\n timeout: 30000,\n ...options,\n baseUrl: options.baseUrl.replace(/\\/$/, ''), // Remove trailing slash\n };\n // Bind fetch to window/globalThis to avoid \"Illegal invocation\" error\n // when fetch is called without proper context\n this.fetchFn = options.fetch ?? (typeof window !== 'undefined' \n ? window.fetch.bind(window) \n : (typeof globalThis !== 'undefined' && globalThis.fetch \n ? globalThis.fetch.bind(globalThis) \n : fetch));\n }\n\n /**\n * Update authentication token\n */\n setToken(token: string | undefined): void {\n this.config.token = token;\n }\n\n /**\n * Update tenant ID\n */\n setTenantId(tenantId: string | undefined): void {\n this.config.tenantId = tenantId;\n }\n\n /**\n * Get current configuration (read-only)\n */\n getConfig(): Readonly<SaferCityConfig> {\n return { ...this.config };\n }\n\n /**\n * Build full URL from path\n */\n protected buildUrl(path: string, query?: Record<string, string | number | boolean | undefined>): string {\n // Normalize: ensure path starts with /\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n // Normalize: ensure baseUrl doesn't end with /\n const baseUrl = this.config.baseUrl.replace(/\\/$/, '');\n \n // Build query string\n let queryString = '';\n if (query) {\n const params = new URLSearchParams();\n Object.entries(query).forEach(([key, value]) => {\n if (value !== undefined) {\n params.set(key, String(value));\n }\n });\n queryString = params.toString();\n }\n \n // Simple concatenation: baseUrl + path + query\n // This works for both absolute URLs (https://api.example.com) and \n // relative URLs (/api/safercity) used in proxy mode\n const fullPath = baseUrl + normalizedPath;\n return queryString ? `${fullPath}?${queryString}` : fullPath;\n }\n\n /**\n * Make HTTP request\n */\n protected async request<T>(\n method: string,\n path: string,\n options?: RequestOptions & {\n body?: unknown;\n query?: Record<string, string | number | boolean | undefined>;\n }\n ): Promise<ApiResponse<T>> {\n const url = this.buildUrl(path, options?.query);\n const headers = createHeaders(this.config, options);\n const timeout = options?.timeout ?? this.config.timeout;\n const signal = createTimeoutSignal(timeout, options?.signal);\n\n const response = await this.fetchFn(url, {\n method,\n headers,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n signal,\n });\n\n let data: unknown;\n const contentType = response.headers.get('content-type');\n \n if (contentType?.includes('application/json')) {\n data = await response.json();\n } else {\n data = await response.text();\n }\n\n if (!response.ok) {\n throw SaferCityApiError.fromResponse(response, data);\n }\n\n return {\n data: data as T,\n status: response.status,\n headers: response.headers,\n };\n }\n\n /**\n * GET request\n */\n async get<T>(\n path: string,\n options?: RequestOptions & { query?: Record<string, string | number | boolean | undefined> }\n ): Promise<ApiResponse<T>> {\n return this.request<T>('GET', path, options);\n }\n\n /**\n * POST request\n */\n async post<T>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('POST', path, { ...options, body });\n }\n\n /**\n * PUT request\n */\n async put<T>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('PUT', path, { ...options, body });\n }\n\n /**\n * PATCH request\n */\n async patch<T>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('PATCH', path, { ...options, body });\n }\n\n /**\n * DELETE request\n */\n async delete<T>(\n path: string,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('DELETE', path, options);\n }\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,95 @@
1
1
  /**
2
2
  * Common types for SaferCity SDK
3
3
  */
4
+ /**
5
+ * Authentication mode determines how the SDK authenticates with SaferCity API
6
+ */
7
+ type AuthMode = "proxy" | "direct" | "cookie";
8
+ /**
9
+ * Proxy Mode Configuration (DEFAULT - Most Secure)
10
+ *
11
+ * Client → Customer Backend → SaferCity API
12
+ * Backend adds tenant credentials, hiding secrets from client.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * <SaferCityProvider mode="proxy" proxyBaseUrl="/api/safercity">
17
+ * <App />
18
+ * </SaferCityProvider>
19
+ * ```
20
+ */
21
+ interface ProxyModeConfig {
22
+ mode?: "proxy";
23
+ /**
24
+ * Base URL for the proxy endpoint
25
+ * @default "/api/safercity"
26
+ */
27
+ proxyBaseUrl?: string;
28
+ }
29
+ /**
30
+ * Direct Mode Configuration
31
+ *
32
+ * Client → SaferCity API with external user token
33
+ * For white-label apps using external auth (better-auth, Clerk, Auth0, etc.)
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * <SaferCityProvider
38
+ * mode="direct"
39
+ * baseUrl="https://api.safercity.com"
40
+ * tenantId="tenant-123"
41
+ * getAccessToken={() => session?.accessToken}
42
+ * >
43
+ * <App />
44
+ * </SaferCityProvider>
45
+ * ```
46
+ */
47
+ interface DirectModeConfig {
48
+ mode: "direct";
49
+ /**
50
+ * SaferCity API base URL
51
+ */
52
+ baseUrl: string;
53
+ /**
54
+ * Tenant ID for multi-tenant operations
55
+ */
56
+ tenantId: string;
57
+ /**
58
+ * Function to get the current access token (from external auth provider)
59
+ */
60
+ getAccessToken: () => Promise<string | undefined> | string | undefined;
61
+ }
62
+ /**
63
+ * Cookie Mode Configuration
64
+ *
65
+ * Browser with credentials: include
66
+ * For first-party web apps using session cookies
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * <SaferCityProvider
71
+ * mode="cookie"
72
+ * baseUrl="https://api.safercity.com"
73
+ * >
74
+ * <App />
75
+ * </SaferCityProvider>
76
+ * ```
77
+ */
78
+ interface CookieModeConfig {
79
+ mode: "cookie";
80
+ /**
81
+ * SaferCity API base URL
82
+ */
83
+ baseUrl: string;
84
+ /**
85
+ * Tenant ID (optional, can come from cookie)
86
+ */
87
+ tenantId?: string;
88
+ }
89
+ /**
90
+ * Combined client configuration supporting all auth modes
91
+ */
92
+ type ClientModeConfig = ProxyModeConfig | DirectModeConfig | CookieModeConfig;
4
93
  interface SaferCityConfig {
5
94
  /**
6
95
  * Base URL for the SaferCity API
@@ -128,6 +217,69 @@ interface SaferCityJwtPayload {
128
217
  iat?: number;
129
218
  exp?: number;
130
219
  }
220
+ interface OAuthCredentials {
221
+ clientId: string;
222
+ clientSecret: string;
223
+ tenantId?: string;
224
+ }
225
+ interface TokenManagerConfig {
226
+ credentials: OAuthCredentials;
227
+ baseUrl?: string;
228
+ storage?: TokenStorage;
229
+ /**
230
+ * Buffer time before expiration to trigger refresh (ms)
231
+ * @default 60000 (1 minute)
232
+ */
233
+ refreshBuffer?: number;
234
+ /**
235
+ * Custom fetch implementation
236
+ */
237
+ fetch?: typeof fetch;
238
+ }
239
+ /**
240
+ * Token Manager for server-side OAuth token management
241
+ *
242
+ * Handles automatic token refresh before expiration.
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * const tokenManager = new TokenManager({
247
+ * credentials: {
248
+ * clientId: process.env.SAFERCITY_CLIENT_ID!,
249
+ * clientSecret: process.env.SAFERCITY_CLIENT_SECRET!,
250
+ * },
251
+ * baseUrl: "https://api.safercity.com",
252
+ * });
253
+ *
254
+ * // Get token (auto-refreshes if expired)
255
+ * const token = await tokenManager.getToken();
256
+ * ```
257
+ */
258
+ declare class TokenManager {
259
+ private config;
260
+ private refreshPromise;
261
+ constructor(config: TokenManagerConfig);
262
+ /**
263
+ * Get a valid access token, refreshing if necessary
264
+ */
265
+ getToken(): Promise<string>;
266
+ /**
267
+ * Force fetch a new token (useful for error recovery)
268
+ */
269
+ forceRefresh(): Promise<string>;
270
+ /**
271
+ * Clear stored tokens
272
+ */
273
+ clear(): void;
274
+ /**
275
+ * Check if we have a stored token
276
+ */
277
+ hasToken(): boolean;
278
+ private fetchNewToken;
279
+ private doFetchToken;
280
+ private refreshToken;
281
+ private doRefreshToken;
282
+ }
131
283
 
132
284
  /**
133
285
  * Cross-platform SSE/Streaming support for SaferCity SDK
@@ -240,4 +392,4 @@ declare class BaseClient {
240
392
  delete<T>(path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
241
393
  }
242
394
 
243
- export { type ApiError, type ApiResponse, type AuthTokens, BaseClient, type BaseClientOptions, type EventSourceOptions, FetchStreamAdapter, MemoryTokenStorage, type RequestOptions, SaferCityApiError, type SaferCityConfig, type SaferCityJwtPayload, type ServerSentEvent, type StreamAdapter, type TokenStorage, WebStreamAdapter, createAuthHeader, createStreamAdapter, getJwtExpiration, isTokenExpired, parseJwtPayload, parseSSE };
395
+ export { type ApiError, type ApiResponse, type AuthMode, type AuthTokens, BaseClient, type BaseClientOptions, type ClientModeConfig, type CookieModeConfig, type DirectModeConfig, type EventSourceOptions, FetchStreamAdapter, MemoryTokenStorage, type OAuthCredentials, type ProxyModeConfig, type RequestOptions, SaferCityApiError, type SaferCityConfig, type SaferCityJwtPayload, type ServerSentEvent, type StreamAdapter, TokenManager, type TokenManagerConfig, type TokenStorage, WebStreamAdapter, createAuthHeader, createStreamAdapter, getJwtExpiration, isTokenExpired, parseJwtPayload, parseSSE };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,95 @@
1
1
  /**
2
2
  * Common types for SaferCity SDK
3
3
  */
4
+ /**
5
+ * Authentication mode determines how the SDK authenticates with SaferCity API
6
+ */
7
+ type AuthMode = "proxy" | "direct" | "cookie";
8
+ /**
9
+ * Proxy Mode Configuration (DEFAULT - Most Secure)
10
+ *
11
+ * Client → Customer Backend → SaferCity API
12
+ * Backend adds tenant credentials, hiding secrets from client.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * <SaferCityProvider mode="proxy" proxyBaseUrl="/api/safercity">
17
+ * <App />
18
+ * </SaferCityProvider>
19
+ * ```
20
+ */
21
+ interface ProxyModeConfig {
22
+ mode?: "proxy";
23
+ /**
24
+ * Base URL for the proxy endpoint
25
+ * @default "/api/safercity"
26
+ */
27
+ proxyBaseUrl?: string;
28
+ }
29
+ /**
30
+ * Direct Mode Configuration
31
+ *
32
+ * Client → SaferCity API with external user token
33
+ * For white-label apps using external auth (better-auth, Clerk, Auth0, etc.)
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * <SaferCityProvider
38
+ * mode="direct"
39
+ * baseUrl="https://api.safercity.com"
40
+ * tenantId="tenant-123"
41
+ * getAccessToken={() => session?.accessToken}
42
+ * >
43
+ * <App />
44
+ * </SaferCityProvider>
45
+ * ```
46
+ */
47
+ interface DirectModeConfig {
48
+ mode: "direct";
49
+ /**
50
+ * SaferCity API base URL
51
+ */
52
+ baseUrl: string;
53
+ /**
54
+ * Tenant ID for multi-tenant operations
55
+ */
56
+ tenantId: string;
57
+ /**
58
+ * Function to get the current access token (from external auth provider)
59
+ */
60
+ getAccessToken: () => Promise<string | undefined> | string | undefined;
61
+ }
62
+ /**
63
+ * Cookie Mode Configuration
64
+ *
65
+ * Browser with credentials: include
66
+ * For first-party web apps using session cookies
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * <SaferCityProvider
71
+ * mode="cookie"
72
+ * baseUrl="https://api.safercity.com"
73
+ * >
74
+ * <App />
75
+ * </SaferCityProvider>
76
+ * ```
77
+ */
78
+ interface CookieModeConfig {
79
+ mode: "cookie";
80
+ /**
81
+ * SaferCity API base URL
82
+ */
83
+ baseUrl: string;
84
+ /**
85
+ * Tenant ID (optional, can come from cookie)
86
+ */
87
+ tenantId?: string;
88
+ }
89
+ /**
90
+ * Combined client configuration supporting all auth modes
91
+ */
92
+ type ClientModeConfig = ProxyModeConfig | DirectModeConfig | CookieModeConfig;
4
93
  interface SaferCityConfig {
5
94
  /**
6
95
  * Base URL for the SaferCity API
@@ -128,6 +217,69 @@ interface SaferCityJwtPayload {
128
217
  iat?: number;
129
218
  exp?: number;
130
219
  }
220
+ interface OAuthCredentials {
221
+ clientId: string;
222
+ clientSecret: string;
223
+ tenantId?: string;
224
+ }
225
+ interface TokenManagerConfig {
226
+ credentials: OAuthCredentials;
227
+ baseUrl?: string;
228
+ storage?: TokenStorage;
229
+ /**
230
+ * Buffer time before expiration to trigger refresh (ms)
231
+ * @default 60000 (1 minute)
232
+ */
233
+ refreshBuffer?: number;
234
+ /**
235
+ * Custom fetch implementation
236
+ */
237
+ fetch?: typeof fetch;
238
+ }
239
+ /**
240
+ * Token Manager for server-side OAuth token management
241
+ *
242
+ * Handles automatic token refresh before expiration.
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * const tokenManager = new TokenManager({
247
+ * credentials: {
248
+ * clientId: process.env.SAFERCITY_CLIENT_ID!,
249
+ * clientSecret: process.env.SAFERCITY_CLIENT_SECRET!,
250
+ * },
251
+ * baseUrl: "https://api.safercity.com",
252
+ * });
253
+ *
254
+ * // Get token (auto-refreshes if expired)
255
+ * const token = await tokenManager.getToken();
256
+ * ```
257
+ */
258
+ declare class TokenManager {
259
+ private config;
260
+ private refreshPromise;
261
+ constructor(config: TokenManagerConfig);
262
+ /**
263
+ * Get a valid access token, refreshing if necessary
264
+ */
265
+ getToken(): Promise<string>;
266
+ /**
267
+ * Force fetch a new token (useful for error recovery)
268
+ */
269
+ forceRefresh(): Promise<string>;
270
+ /**
271
+ * Clear stored tokens
272
+ */
273
+ clear(): void;
274
+ /**
275
+ * Check if we have a stored token
276
+ */
277
+ hasToken(): boolean;
278
+ private fetchNewToken;
279
+ private doFetchToken;
280
+ private refreshToken;
281
+ private doRefreshToken;
282
+ }
131
283
 
132
284
  /**
133
285
  * Cross-platform SSE/Streaming support for SaferCity SDK
@@ -240,4 +392,4 @@ declare class BaseClient {
240
392
  delete<T>(path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
241
393
  }
242
394
 
243
- export { type ApiError, type ApiResponse, type AuthTokens, BaseClient, type BaseClientOptions, type EventSourceOptions, FetchStreamAdapter, MemoryTokenStorage, type RequestOptions, SaferCityApiError, type SaferCityConfig, type SaferCityJwtPayload, type ServerSentEvent, type StreamAdapter, type TokenStorage, WebStreamAdapter, createAuthHeader, createStreamAdapter, getJwtExpiration, isTokenExpired, parseJwtPayload, parseSSE };
395
+ export { type ApiError, type ApiResponse, type AuthMode, type AuthTokens, BaseClient, type BaseClientOptions, type ClientModeConfig, type CookieModeConfig, type DirectModeConfig, type EventSourceOptions, FetchStreamAdapter, MemoryTokenStorage, type OAuthCredentials, type ProxyModeConfig, type RequestOptions, SaferCityApiError, type SaferCityConfig, type SaferCityJwtPayload, type ServerSentEvent, type StreamAdapter, TokenManager, type TokenManagerConfig, type TokenStorage, WebStreamAdapter, createAuthHeader, createStreamAdapter, getJwtExpiration, isTokenExpired, parseJwtPayload, parseSSE };
package/dist/index.js CHANGED
@@ -54,6 +54,130 @@ function getJwtExpiration(token) {
54
54
  if (!payload?.exp) return null;
55
55
  return payload.exp * 1e3;
56
56
  }
57
+ var TokenManager = class {
58
+ config;
59
+ refreshPromise = null;
60
+ constructor(config) {
61
+ this.config = {
62
+ baseUrl: "https://api.safercity.com",
63
+ refreshBuffer: 6e4,
64
+ ...config,
65
+ storage: config.storage ?? new MemoryTokenStorage(),
66
+ fetch: config.fetch ?? globalThis.fetch.bind(globalThis)
67
+ };
68
+ }
69
+ /**
70
+ * Get a valid access token, refreshing if necessary
71
+ */
72
+ async getToken() {
73
+ const tokens = this.config.storage.get();
74
+ if (tokens && !isTokenExpired(tokens.expiresAt, this.config.refreshBuffer)) {
75
+ return tokens.accessToken;
76
+ }
77
+ if (tokens?.refreshToken) {
78
+ return this.refreshToken(tokens.refreshToken);
79
+ }
80
+ return this.fetchNewToken();
81
+ }
82
+ /**
83
+ * Force fetch a new token (useful for error recovery)
84
+ */
85
+ async forceRefresh() {
86
+ this.config.storage.clear();
87
+ return this.fetchNewToken();
88
+ }
89
+ /**
90
+ * Clear stored tokens
91
+ */
92
+ clear() {
93
+ this.config.storage.clear();
94
+ this.refreshPromise = null;
95
+ }
96
+ /**
97
+ * Check if we have a stored token
98
+ */
99
+ hasToken() {
100
+ return this.config.storage.get() !== null;
101
+ }
102
+ async fetchNewToken() {
103
+ if (this.refreshPromise) {
104
+ return this.refreshPromise;
105
+ }
106
+ this.refreshPromise = this.doFetchToken();
107
+ try {
108
+ return await this.refreshPromise;
109
+ } finally {
110
+ this.refreshPromise = null;
111
+ }
112
+ }
113
+ async doFetchToken() {
114
+ const { credentials, baseUrl } = this.config;
115
+ const response = await this.config.fetch(`${baseUrl}/oauth/token`, {
116
+ method: "POST",
117
+ headers: {
118
+ "Content-Type": "application/json"
119
+ },
120
+ body: JSON.stringify({
121
+ grant_type: "client_credentials",
122
+ client_id: credentials.clientId,
123
+ client_secret: credentials.clientSecret,
124
+ ...credentials.tenantId && { tenant_id: credentials.tenantId }
125
+ })
126
+ });
127
+ if (!response.ok) {
128
+ const error = await response.text();
129
+ throw new Error(`Failed to fetch token: ${response.status} ${error}`);
130
+ }
131
+ const data = await response.json();
132
+ const tokens = {
133
+ accessToken: data.access_token,
134
+ refreshToken: data.refresh_token,
135
+ expiresAt: Date.now() + data.expires_in * 1e3,
136
+ tokenType: data.token_type
137
+ };
138
+ this.config.storage.set(tokens);
139
+ return tokens.accessToken;
140
+ }
141
+ async refreshToken(refreshToken) {
142
+ if (this.refreshPromise) {
143
+ return this.refreshPromise;
144
+ }
145
+ this.refreshPromise = this.doRefreshToken(refreshToken);
146
+ try {
147
+ return await this.refreshPromise;
148
+ } finally {
149
+ this.refreshPromise = null;
150
+ }
151
+ }
152
+ async doRefreshToken(refreshToken) {
153
+ const { baseUrl } = this.config;
154
+ try {
155
+ const response = await this.config.fetch(`${baseUrl}/oauth/refresh`, {
156
+ method: "POST",
157
+ headers: {
158
+ "Content-Type": "application/json"
159
+ },
160
+ body: JSON.stringify({ refresh_token: refreshToken })
161
+ });
162
+ if (!response.ok) {
163
+ this.config.storage.clear();
164
+ return this.doFetchToken();
165
+ }
166
+ const data = await response.json();
167
+ const tokens = {
168
+ accessToken: data.access_token,
169
+ refreshToken: data.refresh_token ?? refreshToken,
170
+ expiresAt: Date.now() + data.expires_in * 1e3,
171
+ tokenType: data.token_type
172
+ };
173
+ this.config.storage.set(tokens);
174
+ return tokens.accessToken;
175
+ } catch (error) {
176
+ this.config.storage.clear();
177
+ return this.doFetchToken();
178
+ }
179
+ }
180
+ };
57
181
 
58
182
  // src/streaming.ts
59
183
  function parseSSE(buffer) {
@@ -332,7 +456,7 @@ var BaseClient = class {
332
456
  baseUrl: options.baseUrl.replace(/\/$/, "")
333
457
  // Remove trailing slash
334
458
  };
335
- this.fetchFn = options.fetch ?? fetch;
459
+ this.fetchFn = options.fetch ?? (typeof window !== "undefined" ? window.fetch.bind(window) : typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : fetch);
336
460
  }
337
461
  /**
338
462
  * Update authentication token
@@ -356,15 +480,20 @@ var BaseClient = class {
356
480
  * Build full URL from path
357
481
  */
358
482
  buildUrl(path, query) {
359
- const url = new URL(path.startsWith("/") ? path : `/${path}`, this.config.baseUrl);
483
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
484
+ const baseUrl = this.config.baseUrl.replace(/\/$/, "");
485
+ let queryString = "";
360
486
  if (query) {
487
+ const params = new URLSearchParams();
361
488
  Object.entries(query).forEach(([key, value]) => {
362
489
  if (value !== void 0) {
363
- url.searchParams.set(key, String(value));
490
+ params.set(key, String(value));
364
491
  }
365
492
  });
493
+ queryString = params.toString();
366
494
  }
367
- return url.toString();
495
+ const fullPath = baseUrl + normalizedPath;
496
+ return queryString ? `${fullPath}?${queryString}` : fullPath;
368
497
  }
369
498
  /**
370
499
  * Make HTTP request
@@ -428,6 +557,6 @@ var BaseClient = class {
428
557
  }
429
558
  };
430
559
 
431
- export { BaseClient, FetchStreamAdapter, MemoryTokenStorage, SaferCityApiError, WebStreamAdapter, createAuthHeader, createStreamAdapter, getJwtExpiration, isTokenExpired, parseJwtPayload, parseSSE };
560
+ export { BaseClient, FetchStreamAdapter, MemoryTokenStorage, SaferCityApiError, TokenManager, WebStreamAdapter, createAuthHeader, createStreamAdapter, getJwtExpiration, isTokenExpired, parseJwtPayload, parseSSE };
432
561
  //# sourceMappingURL=index.js.map
433
562
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/auth.ts","../src/streaming.ts","../src/client.ts"],"names":["parsed"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Common types for SaferCity SDK\n */\n\nexport interface SaferCityConfig {\n /**\n * Base URL for the SaferCity API\n * @example \"https://api.safercity.com\"\n */\n baseUrl: string;\n\n /**\n * Authentication token (JWT)\n */\n token?: string;\n\n /**\n * Tenant ID for multi-tenant operations\n */\n tenantId?: string;\n\n /**\n * Custom fetch implementation (useful for React Native)\n */\n fetch?: typeof fetch;\n\n /**\n * Request timeout in milliseconds\n * @default 30000\n */\n timeout?: number;\n\n /**\n * Custom headers to include in all requests\n */\n headers?: Record<string, string>;\n}\n\nexport interface RequestOptions {\n /**\n * Additional headers for this request\n */\n headers?: Record<string, string>;\n\n /**\n * Request timeout override\n */\n timeout?: number;\n\n /**\n * Abort signal for cancellation\n */\n signal?: AbortSignal;\n}\n\nexport interface ApiResponse<T> {\n data: T;\n status: number;\n headers: Headers;\n}\n\nexport interface ApiError {\n error: string;\n message: string;\n status: number;\n details?: unknown;\n}\n\nexport class SaferCityApiError extends Error {\n constructor(\n public readonly error: string,\n message: string,\n public readonly status: number,\n public readonly details?: unknown\n ) {\n super(message);\n this.name = 'SaferCityApiError';\n }\n\n static fromResponse(response: Response, body: unknown): SaferCityApiError {\n const errorBody = body as Partial<ApiError>;\n return new SaferCityApiError(\n errorBody.error ?? 'unknown_error',\n errorBody.message ?? `Request failed with status ${response.status}`,\n response.status,\n errorBody.details\n );\n }\n}\n\n/**\n * Server-Sent Event structure\n */\nexport interface ServerSentEvent {\n id?: string;\n event?: string;\n data: string;\n retry?: number;\n}\n\n/**\n * Options for SSE connections\n */\nexport interface EventSourceOptions {\n headers?: Record<string, string>;\n signal?: AbortSignal;\n onOpen?: () => void;\n onError?: (error: Error) => void;\n}\n","/**\n * Authentication utilities for SaferCity SDK\n */\n\nexport interface AuthTokens {\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number;\n tokenType: string;\n}\n\nexport interface TokenStorage {\n get(): AuthTokens | null;\n set(tokens: AuthTokens): void;\n clear(): void;\n}\n\n/**\n * In-memory token storage (default)\n */\nexport class MemoryTokenStorage implements TokenStorage {\n private tokens: AuthTokens | null = null;\n\n get(): AuthTokens | null {\n return this.tokens;\n }\n\n set(tokens: AuthTokens): void {\n this.tokens = tokens;\n }\n\n clear(): void {\n this.tokens = null;\n }\n}\n\n/**\n * Check if a token is expired (with buffer)\n */\nexport function isTokenExpired(expiresAt: number | undefined, bufferMs = 60000): boolean {\n if (!expiresAt) return false;\n return Date.now() + bufferMs >= expiresAt;\n}\n\n/**\n * Create Authorization header value\n */\nexport function createAuthHeader(token: string, type = 'Bearer'): string {\n return `${type} ${token}`;\n}\n\n/**\n * Parse JWT payload (without verification)\n * Only use for client-side display, not security decisions\n */\nexport function parseJwtPayload<T = Record<string, unknown>>(token: string): T | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n \n const payload = parts[1];\n const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));\n return JSON.parse(decoded) as T;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract expiration from JWT\n */\nexport function getJwtExpiration(token: string): number | null {\n const payload = parseJwtPayload<{ exp?: number }>(token);\n if (!payload?.exp) return null;\n return payload.exp * 1000; // Convert to milliseconds\n}\n\nexport interface SaferCityJwtPayload {\n sub?: string;\n tenantId?: string;\n environment?: string;\n scopes?: string[];\n iat?: number;\n exp?: number;\n}\n","/**\n * Cross-platform SSE/Streaming support for SaferCity SDK\n * \n * Provides adapters for:\n * - Web browsers (native EventSource)\n * - React Native (fetch-based streaming via expo-fetch or polyfill)\n * - Node.js (fetch-based streaming)\n */\n\nimport type { ServerSentEvent, EventSourceOptions } from './types';\n\n/**\n * Interface for stream adapters\n */\nexport interface StreamAdapter {\n /**\n * Create an async iterable for SSE events\n */\n createEventSource(url: string, options?: EventSourceOptions): AsyncIterable<ServerSentEvent>;\n \n /**\n * Check if native SSE is supported\n */\n supportsNativeSSE(): boolean;\n}\n\n/**\n * Parse SSE data from a text buffer\n */\nexport function parseSSE(buffer: string): { parsed: ServerSentEvent[]; remaining: string } {\n const events: ServerSentEvent[] = [];\n const lines = buffer.split('\\n');\n \n let currentEvent: Partial<ServerSentEvent> = {};\n let dataLines: string[] = [];\n let remaining = '';\n \n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n \n // Check if this might be an incomplete line at the end\n if (i === lines.length - 1 && line !== '' && !buffer.endsWith('\\n')) {\n remaining = line;\n break;\n }\n \n if (line === '') {\n // Empty line = event boundary\n if (dataLines.length > 0 || currentEvent.event || currentEvent.id) {\n events.push({\n ...currentEvent,\n data: dataLines.join('\\n'),\n } as ServerSentEvent);\n }\n currentEvent = {};\n dataLines = [];\n continue;\n }\n \n const colonIndex = line.indexOf(':');\n \n // Comment line (starts with :)\n if (colonIndex === 0) {\n continue;\n }\n \n let field: string;\n let value: string;\n \n if (colonIndex === -1) {\n field = line;\n value = '';\n } else {\n field = line.slice(0, colonIndex);\n // Skip the optional space after colon\n value = line.slice(colonIndex + 1).replace(/^ /, '');\n }\n \n switch (field) {\n case 'event':\n currentEvent.event = value;\n break;\n case 'data':\n dataLines.push(value);\n break;\n case 'id':\n currentEvent.id = value;\n break;\n case 'retry':\n const retry = parseInt(value, 10);\n if (!isNaN(retry)) {\n currentEvent.retry = retry;\n }\n break;\n }\n }\n \n return { parsed: events, remaining };\n}\n\n/**\n * Web/Browser stream adapter using native EventSource\n */\nexport class WebStreamAdapter implements StreamAdapter {\n supportsNativeSSE(): boolean {\n return typeof EventSource !== 'undefined';\n }\n \n createEventSource(url: string, options?: EventSourceOptions): AsyncIterable<ServerSentEvent> {\n const adapter = this;\n \n return {\n [Symbol.asyncIterator]() {\n let eventSource: EventSource | null = null;\n let resolveNext: ((value: IteratorResult<ServerSentEvent>) => void) | null = null;\n let rejectNext: ((error: Error) => void) | null = null;\n const eventQueue: ServerSentEvent[] = [];\n let done = false;\n let error: Error | null = null;\n \n const cleanup = () => {\n if (eventSource) {\n eventSource.close();\n eventSource = null;\n }\n done = true;\n };\n \n // Handle abort signal\n options?.signal?.addEventListener('abort', () => {\n cleanup();\n if (rejectNext) {\n rejectNext(new Error('Aborted'));\n }\n });\n \n // Initialize EventSource\n eventSource = new EventSource(url);\n \n eventSource.onopen = () => {\n options?.onOpen?.();\n };\n \n eventSource.onmessage = (event) => {\n const sseEvent: ServerSentEvent = {\n id: event.lastEventId || undefined,\n data: event.data,\n };\n \n if (resolveNext) {\n resolveNext({ value: sseEvent, done: false });\n resolveNext = null;\n rejectNext = null;\n } else {\n eventQueue.push(sseEvent);\n }\n };\n \n eventSource.onerror = (e) => {\n const err = new Error('EventSource error');\n error = err;\n options?.onError?.(err);\n cleanup();\n \n if (rejectNext) {\n rejectNext(err);\n resolveNext = null;\n rejectNext = null;\n }\n };\n \n return {\n async next(): Promise<IteratorResult<ServerSentEvent>> {\n if (error) {\n throw error;\n }\n \n if (eventQueue.length > 0) {\n return { value: eventQueue.shift()!, done: false };\n }\n \n if (done) {\n return { value: undefined as unknown as ServerSentEvent, done: true };\n }\n \n return new Promise((resolve, reject) => {\n resolveNext = resolve;\n rejectNext = reject;\n });\n },\n \n async return(): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n return { value: undefined as unknown as ServerSentEvent, done: true };\n },\n \n async throw(e: Error): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n throw e;\n },\n };\n },\n };\n }\n}\n\n/**\n * Fetch-based stream adapter for React Native and Node.js\n * Uses ReadableStream to parse SSE from fetch response\n */\nexport class FetchStreamAdapter implements StreamAdapter {\n constructor(private fetchFn: typeof fetch = fetch) {}\n \n supportsNativeSSE(): boolean {\n return false;\n }\n \n createEventSource(url: string, options?: EventSourceOptions): AsyncIterable<ServerSentEvent> {\n const fetchFn = this.fetchFn;\n \n return {\n [Symbol.asyncIterator]() {\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;\n let buffer = '';\n let done = false;\n const eventQueue: ServerSentEvent[] = [];\n \n const cleanup = () => {\n if (reader) {\n reader.cancel().catch(() => {});\n reader = null;\n }\n done = true;\n };\n \n // Handle abort signal\n options?.signal?.addEventListener('abort', cleanup);\n \n // Start fetch\n const fetchPromise = fetchFn(url, {\n method: 'GET',\n headers: {\n 'Accept': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n ...options?.headers,\n },\n signal: options?.signal,\n }).then((response) => {\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n \n if (!response.body) {\n throw new Error('Response body is not available');\n }\n \n options?.onOpen?.();\n reader = response.body.getReader();\n return reader;\n }).catch((err) => {\n options?.onError?.(err);\n throw err;\n });\n \n const decoder = new TextDecoder();\n \n return {\n async next(): Promise<IteratorResult<ServerSentEvent>> {\n // Return queued events first\n if (eventQueue.length > 0) {\n return { value: eventQueue.shift()!, done: false };\n }\n \n if (done) {\n return { value: undefined as unknown as ServerSentEvent, done: true };\n }\n \n // Ensure reader is initialized\n if (!reader) {\n reader = await fetchPromise;\n }\n \n // Read until we have at least one event\n while (eventQueue.length === 0 && !done) {\n const { value, done: readerDone } = await reader.read();\n \n if (readerDone) {\n done = true;\n // Parse any remaining buffer\n if (buffer.trim()) {\n const { parsed } = parseSSE(buffer + '\\n\\n');\n eventQueue.push(...parsed);\n }\n break;\n }\n \n buffer += decoder.decode(value, { stream: true });\n const { parsed, remaining } = parseSSE(buffer);\n buffer = remaining;\n eventQueue.push(...parsed);\n }\n \n if (eventQueue.length > 0) {\n return { value: eventQueue.shift()!, done: false };\n }\n \n return { value: undefined as unknown as ServerSentEvent, done: true };\n },\n \n async return(): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n return { value: undefined as unknown as ServerSentEvent, done: true };\n },\n \n async throw(e: Error): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n throw e;\n },\n };\n },\n };\n }\n}\n\n/**\n * Auto-detect and create the best stream adapter for the current environment\n */\nexport function createStreamAdapter(fetchFn?: typeof fetch): StreamAdapter {\n // Check for native EventSource (browser)\n if (typeof EventSource !== 'undefined') {\n return new WebStreamAdapter();\n }\n \n // Fall back to fetch-based adapter\n return new FetchStreamAdapter(fetchFn ?? fetch);\n}\n","/**\n * Base HTTP client for SaferCity SDK\n */\n\nimport type { SaferCityConfig, RequestOptions, ApiResponse } from './types';\nimport { SaferCityApiError } from './types';\nimport { createAuthHeader } from './auth';\n\nexport interface BaseClientOptions extends SaferCityConfig {}\n\n/**\n * Create base headers for requests\n */\nfunction createHeaders(\n config: SaferCityConfig,\n options?: RequestOptions\n): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n ...config.headers,\n ...options?.headers,\n };\n\n if (config.token) {\n headers['Authorization'] = createAuthHeader(config.token);\n }\n\n if (config.tenantId) {\n headers['X-Tenant-ID'] = config.tenantId;\n }\n\n return headers;\n}\n\n/**\n * Create a timeout signal\n */\nfunction createTimeoutSignal(timeout: number, existingSignal?: AbortSignal): AbortSignal {\n const controller = new AbortController();\n \n const timeoutId = setTimeout(() => {\n controller.abort(new Error(`Request timeout after ${timeout}ms`));\n }, timeout);\n \n // If there's an existing signal, abort when it aborts\n if (existingSignal) {\n existingSignal.addEventListener('abort', () => {\n clearTimeout(timeoutId);\n controller.abort(existingSignal.reason);\n });\n }\n \n return controller.signal;\n}\n\n/**\n * Base HTTP client with common functionality\n */\nexport class BaseClient {\n protected config: Required<Pick<SaferCityConfig, 'baseUrl' | 'timeout'>> & SaferCityConfig;\n protected fetchFn: typeof fetch;\n\n constructor(options: BaseClientOptions) {\n this.config = {\n timeout: 30000,\n ...options,\n baseUrl: options.baseUrl.replace(/\\/$/, ''), // Remove trailing slash\n };\n this.fetchFn = options.fetch ?? fetch;\n }\n\n /**\n * Update authentication token\n */\n setToken(token: string | undefined): void {\n this.config.token = token;\n }\n\n /**\n * Update tenant ID\n */\n setTenantId(tenantId: string | undefined): void {\n this.config.tenantId = tenantId;\n }\n\n /**\n * Get current configuration (read-only)\n */\n getConfig(): Readonly<SaferCityConfig> {\n return { ...this.config };\n }\n\n /**\n * Build full URL from path\n */\n protected buildUrl(path: string, query?: Record<string, string | number | boolean | undefined>): string {\n const url = new URL(path.startsWith('/') ? path : `/${path}`, this.config.baseUrl);\n \n if (query) {\n Object.entries(query).forEach(([key, value]) => {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n });\n }\n \n return url.toString();\n }\n\n /**\n * Make HTTP request\n */\n protected async request<T>(\n method: string,\n path: string,\n options?: RequestOptions & {\n body?: unknown;\n query?: Record<string, string | number | boolean | undefined>;\n }\n ): Promise<ApiResponse<T>> {\n const url = this.buildUrl(path, options?.query);\n const headers = createHeaders(this.config, options);\n const timeout = options?.timeout ?? this.config.timeout;\n const signal = createTimeoutSignal(timeout, options?.signal);\n\n const response = await this.fetchFn(url, {\n method,\n headers,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n signal,\n });\n\n let data: unknown;\n const contentType = response.headers.get('content-type');\n \n if (contentType?.includes('application/json')) {\n data = await response.json();\n } else {\n data = await response.text();\n }\n\n if (!response.ok) {\n throw SaferCityApiError.fromResponse(response, data);\n }\n\n return {\n data: data as T,\n status: response.status,\n headers: response.headers,\n };\n }\n\n /**\n * GET request\n */\n async get<T>(\n path: string,\n options?: RequestOptions & { query?: Record<string, string | number | boolean | undefined> }\n ): Promise<ApiResponse<T>> {\n return this.request<T>('GET', path, options);\n }\n\n /**\n * POST request\n */\n async post<T>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('POST', path, { ...options, body });\n }\n\n /**\n * PUT request\n */\n async put<T>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('PUT', path, { ...options, body });\n }\n\n /**\n * PATCH request\n */\n async patch<T>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('PATCH', path, { ...options, body });\n }\n\n /**\n * DELETE request\n */\n async delete<T>(\n path: string,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('DELETE', path, options);\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/auth.ts","../src/streaming.ts","../src/client.ts"],"names":["parsed"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Common types for SaferCity SDK\n */\n\n// ============================================================================\n// Auth Mode Types\n// ============================================================================\n\n/**\n * Authentication mode determines how the SDK authenticates with SaferCity API\n */\nexport type AuthMode = \"proxy\" | \"direct\" | \"cookie\";\n\n/**\n * Proxy Mode Configuration (DEFAULT - Most Secure)\n * \n * Client → Customer Backend → SaferCity API\n * Backend adds tenant credentials, hiding secrets from client.\n * \n * @example\n * ```tsx\n * <SaferCityProvider mode=\"proxy\" proxyBaseUrl=\"/api/safercity\">\n * <App />\n * </SaferCityProvider>\n * ```\n */\nexport interface ProxyModeConfig {\n mode?: \"proxy\";\n /**\n * Base URL for the proxy endpoint\n * @default \"/api/safercity\"\n */\n proxyBaseUrl?: string;\n}\n\n/**\n * Direct Mode Configuration\n * \n * Client → SaferCity API with external user token\n * For white-label apps using external auth (better-auth, Clerk, Auth0, etc.)\n * \n * @example\n * ```tsx\n * <SaferCityProvider\n * mode=\"direct\"\n * baseUrl=\"https://api.safercity.com\"\n * tenantId=\"tenant-123\"\n * getAccessToken={() => session?.accessToken}\n * >\n * <App />\n * </SaferCityProvider>\n * ```\n */\nexport interface DirectModeConfig {\n mode: \"direct\";\n /**\n * SaferCity API base URL\n */\n baseUrl: string;\n /**\n * Tenant ID for multi-tenant operations\n */\n tenantId: string;\n /**\n * Function to get the current access token (from external auth provider)\n */\n getAccessToken: () => Promise<string | undefined> | string | undefined;\n}\n\n/**\n * Cookie Mode Configuration\n * \n * Browser with credentials: include\n * For first-party web apps using session cookies\n * \n * @example\n * ```tsx\n * <SaferCityProvider\n * mode=\"cookie\"\n * baseUrl=\"https://api.safercity.com\"\n * >\n * <App />\n * </SaferCityProvider>\n * ```\n */\nexport interface CookieModeConfig {\n mode: \"cookie\";\n /**\n * SaferCity API base URL\n */\n baseUrl: string;\n /**\n * Tenant ID (optional, can come from cookie)\n */\n tenantId?: string;\n}\n\n/**\n * Combined client configuration supporting all auth modes\n */\nexport type ClientModeConfig = ProxyModeConfig | DirectModeConfig | CookieModeConfig;\n\n// ============================================================================\n// Base Configuration\n// ============================================================================\n\nexport interface SaferCityConfig {\n /**\n * Base URL for the SaferCity API\n * @example \"https://api.safercity.com\"\n */\n baseUrl: string;\n\n /**\n * Authentication token (JWT)\n */\n token?: string;\n\n /**\n * Tenant ID for multi-tenant operations\n */\n tenantId?: string;\n\n /**\n * Custom fetch implementation (useful for React Native)\n */\n fetch?: typeof fetch;\n\n /**\n * Request timeout in milliseconds\n * @default 30000\n */\n timeout?: number;\n\n /**\n * Custom headers to include in all requests\n */\n headers?: Record<string, string>;\n}\n\nexport interface RequestOptions {\n /**\n * Additional headers for this request\n */\n headers?: Record<string, string>;\n\n /**\n * Request timeout override\n */\n timeout?: number;\n\n /**\n * Abort signal for cancellation\n */\n signal?: AbortSignal;\n}\n\nexport interface ApiResponse<T> {\n data: T;\n status: number;\n headers: Headers;\n}\n\nexport interface ApiError {\n error: string;\n message: string;\n status: number;\n details?: unknown;\n}\n\nexport class SaferCityApiError extends Error {\n constructor(\n public readonly error: string,\n message: string,\n public readonly status: number,\n public readonly details?: unknown\n ) {\n super(message);\n this.name = 'SaferCityApiError';\n }\n\n static fromResponse(response: Response, body: unknown): SaferCityApiError {\n const errorBody = body as Partial<ApiError>;\n return new SaferCityApiError(\n errorBody.error ?? 'unknown_error',\n errorBody.message ?? `Request failed with status ${response.status}`,\n response.status,\n errorBody.details\n );\n }\n}\n\n/**\n * Server-Sent Event structure\n */\nexport interface ServerSentEvent {\n id?: string;\n event?: string;\n data: string;\n retry?: number;\n}\n\n/**\n * Options for SSE connections\n */\nexport interface EventSourceOptions {\n headers?: Record<string, string>;\n signal?: AbortSignal;\n onOpen?: () => void;\n onError?: (error: Error) => void;\n}\n","/**\n * Authentication utilities for SaferCity SDK\n */\n\nexport interface AuthTokens {\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number;\n tokenType: string;\n}\n\nexport interface TokenStorage {\n get(): AuthTokens | null;\n set(tokens: AuthTokens): void;\n clear(): void;\n}\n\n/**\n * In-memory token storage (default)\n */\nexport class MemoryTokenStorage implements TokenStorage {\n private tokens: AuthTokens | null = null;\n\n get(): AuthTokens | null {\n return this.tokens;\n }\n\n set(tokens: AuthTokens): void {\n this.tokens = tokens;\n }\n\n clear(): void {\n this.tokens = null;\n }\n}\n\n/**\n * Check if a token is expired (with buffer)\n */\nexport function isTokenExpired(expiresAt: number | undefined, bufferMs = 60000): boolean {\n if (!expiresAt) return false;\n return Date.now() + bufferMs >= expiresAt;\n}\n\n/**\n * Create Authorization header value\n */\nexport function createAuthHeader(token: string, type = 'Bearer'): string {\n return `${type} ${token}`;\n}\n\n/**\n * Parse JWT payload (without verification)\n * Only use for client-side display, not security decisions\n */\nexport function parseJwtPayload<T = Record<string, unknown>>(token: string): T | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n \n const payload = parts[1];\n const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));\n return JSON.parse(decoded) as T;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract expiration from JWT\n */\nexport function getJwtExpiration(token: string): number | null {\n const payload = parseJwtPayload<{ exp?: number }>(token);\n if (!payload?.exp) return null;\n return payload.exp * 1000; // Convert to milliseconds\n}\n\nexport interface SaferCityJwtPayload {\n sub?: string;\n tenantId?: string;\n environment?: string;\n scopes?: string[];\n iat?: number;\n exp?: number;\n}\n\n// ============================================================================\n// Token Manager for Server-Side OAuth\n// ============================================================================\n\nexport interface OAuthCredentials {\n clientId: string;\n clientSecret: string;\n tenantId?: string;\n}\n\nexport interface TokenManagerConfig {\n credentials: OAuthCredentials;\n baseUrl?: string;\n storage?: TokenStorage;\n /**\n * Buffer time before expiration to trigger refresh (ms)\n * @default 60000 (1 minute)\n */\n refreshBuffer?: number;\n /**\n * Custom fetch implementation\n */\n fetch?: typeof fetch;\n}\n\n/**\n * Token Manager for server-side OAuth token management\n * \n * Handles automatic token refresh before expiration.\n * \n * @example\n * ```typescript\n * const tokenManager = new TokenManager({\n * credentials: {\n * clientId: process.env.SAFERCITY_CLIENT_ID!,\n * clientSecret: process.env.SAFERCITY_CLIENT_SECRET!,\n * },\n * baseUrl: \"https://api.safercity.com\",\n * });\n * \n * // Get token (auto-refreshes if expired)\n * const token = await tokenManager.getToken();\n * ```\n */\nexport class TokenManager {\n private config: Required<Omit<TokenManagerConfig, 'storage' | 'fetch'>> & { \n storage: TokenStorage; \n fetch: typeof fetch;\n };\n private refreshPromise: Promise<string> | null = null;\n\n constructor(config: TokenManagerConfig) {\n this.config = {\n baseUrl: \"https://api.safercity.com\",\n refreshBuffer: 60000,\n ...config,\n storage: config.storage ?? new MemoryTokenStorage(),\n fetch: config.fetch ?? globalThis.fetch.bind(globalThis),\n };\n }\n\n /**\n * Get a valid access token, refreshing if necessary\n */\n async getToken(): Promise<string> {\n const tokens = this.config.storage.get();\n\n // If we have a valid token, return it\n if (tokens && !isTokenExpired(tokens.expiresAt, this.config.refreshBuffer)) {\n return tokens.accessToken;\n }\n\n // If we have a refresh token and access is expired, try refresh\n if (tokens?.refreshToken) {\n return this.refreshToken(tokens.refreshToken);\n }\n\n // Otherwise, get a new token\n return this.fetchNewToken();\n }\n\n /**\n * Force fetch a new token (useful for error recovery)\n */\n async forceRefresh(): Promise<string> {\n this.config.storage.clear();\n return this.fetchNewToken();\n }\n\n /**\n * Clear stored tokens\n */\n clear(): void {\n this.config.storage.clear();\n this.refreshPromise = null;\n }\n\n /**\n * Check if we have a stored token\n */\n hasToken(): boolean {\n return this.config.storage.get() !== null;\n }\n\n private async fetchNewToken(): Promise<string> {\n // Dedupe concurrent requests\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.doFetchToken();\n\n try {\n return await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async doFetchToken(): Promise<string> {\n const { credentials, baseUrl } = this.config;\n\n const response = await this.config.fetch(`${baseUrl}/oauth/token`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n grant_type: \"client_credentials\",\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n ...(credentials.tenantId && { tenant_id: credentials.tenantId }),\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to fetch token: ${response.status} ${error}`);\n }\n\n const data = await response.json() as {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n token_type: string;\n };\n\n const tokens: AuthTokens = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: Date.now() + data.expires_in * 1000,\n tokenType: data.token_type,\n };\n\n this.config.storage.set(tokens);\n return tokens.accessToken;\n }\n\n private async refreshToken(refreshToken: string): Promise<string> {\n // Dedupe concurrent requests\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.doRefreshToken(refreshToken);\n\n try {\n return await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async doRefreshToken(refreshToken: string): Promise<string> {\n const { baseUrl } = this.config;\n\n try {\n const response = await this.config.fetch(`${baseUrl}/oauth/refresh`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ refresh_token: refreshToken }),\n });\n\n if (!response.ok) {\n // Refresh failed, clear and get new token\n this.config.storage.clear();\n return this.doFetchToken();\n }\n\n const data = await response.json() as {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n token_type: string;\n };\n\n const tokens: AuthTokens = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token ?? refreshToken,\n expiresAt: Date.now() + data.expires_in * 1000,\n tokenType: data.token_type,\n };\n\n this.config.storage.set(tokens);\n return tokens.accessToken;\n } catch (error) {\n // On any error, clear and get new token\n this.config.storage.clear();\n return this.doFetchToken();\n }\n }\n}\n","/**\n * Cross-platform SSE/Streaming support for SaferCity SDK\n * \n * Provides adapters for:\n * - Web browsers (native EventSource)\n * - React Native (fetch-based streaming via expo-fetch or polyfill)\n * - Node.js (fetch-based streaming)\n */\n\nimport type { ServerSentEvent, EventSourceOptions } from './types';\n\n/**\n * Interface for stream adapters\n */\nexport interface StreamAdapter {\n /**\n * Create an async iterable for SSE events\n */\n createEventSource(url: string, options?: EventSourceOptions): AsyncIterable<ServerSentEvent>;\n \n /**\n * Check if native SSE is supported\n */\n supportsNativeSSE(): boolean;\n}\n\n/**\n * Parse SSE data from a text buffer\n */\nexport function parseSSE(buffer: string): { parsed: ServerSentEvent[]; remaining: string } {\n const events: ServerSentEvent[] = [];\n const lines = buffer.split('\\n');\n \n let currentEvent: Partial<ServerSentEvent> = {};\n let dataLines: string[] = [];\n let remaining = '';\n \n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n \n // Check if this might be an incomplete line at the end\n if (i === lines.length - 1 && line !== '' && !buffer.endsWith('\\n')) {\n remaining = line;\n break;\n }\n \n if (line === '') {\n // Empty line = event boundary\n if (dataLines.length > 0 || currentEvent.event || currentEvent.id) {\n events.push({\n ...currentEvent,\n data: dataLines.join('\\n'),\n } as ServerSentEvent);\n }\n currentEvent = {};\n dataLines = [];\n continue;\n }\n \n const colonIndex = line.indexOf(':');\n \n // Comment line (starts with :)\n if (colonIndex === 0) {\n continue;\n }\n \n let field: string;\n let value: string;\n \n if (colonIndex === -1) {\n field = line;\n value = '';\n } else {\n field = line.slice(0, colonIndex);\n // Skip the optional space after colon\n value = line.slice(colonIndex + 1).replace(/^ /, '');\n }\n \n switch (field) {\n case 'event':\n currentEvent.event = value;\n break;\n case 'data':\n dataLines.push(value);\n break;\n case 'id':\n currentEvent.id = value;\n break;\n case 'retry':\n const retry = parseInt(value, 10);\n if (!isNaN(retry)) {\n currentEvent.retry = retry;\n }\n break;\n }\n }\n \n return { parsed: events, remaining };\n}\n\n/**\n * Web/Browser stream adapter using native EventSource\n */\nexport class WebStreamAdapter implements StreamAdapter {\n supportsNativeSSE(): boolean {\n return typeof EventSource !== 'undefined';\n }\n \n createEventSource(url: string, options?: EventSourceOptions): AsyncIterable<ServerSentEvent> {\n const adapter = this;\n \n return {\n [Symbol.asyncIterator]() {\n let eventSource: EventSource | null = null;\n let resolveNext: ((value: IteratorResult<ServerSentEvent>) => void) | null = null;\n let rejectNext: ((error: Error) => void) | null = null;\n const eventQueue: ServerSentEvent[] = [];\n let done = false;\n let error: Error | null = null;\n \n const cleanup = () => {\n if (eventSource) {\n eventSource.close();\n eventSource = null;\n }\n done = true;\n };\n \n // Handle abort signal\n options?.signal?.addEventListener('abort', () => {\n cleanup();\n if (rejectNext) {\n rejectNext(new Error('Aborted'));\n }\n });\n \n // Initialize EventSource\n eventSource = new EventSource(url);\n \n eventSource.onopen = () => {\n options?.onOpen?.();\n };\n \n eventSource.onmessage = (event) => {\n const sseEvent: ServerSentEvent = {\n id: event.lastEventId || undefined,\n data: event.data,\n };\n \n if (resolveNext) {\n resolveNext({ value: sseEvent, done: false });\n resolveNext = null;\n rejectNext = null;\n } else {\n eventQueue.push(sseEvent);\n }\n };\n \n eventSource.onerror = (e) => {\n const err = new Error('EventSource error');\n error = err;\n options?.onError?.(err);\n cleanup();\n \n if (rejectNext) {\n rejectNext(err);\n resolveNext = null;\n rejectNext = null;\n }\n };\n \n return {\n async next(): Promise<IteratorResult<ServerSentEvent>> {\n if (error) {\n throw error;\n }\n \n if (eventQueue.length > 0) {\n return { value: eventQueue.shift()!, done: false };\n }\n \n if (done) {\n return { value: undefined as unknown as ServerSentEvent, done: true };\n }\n \n return new Promise((resolve, reject) => {\n resolveNext = resolve;\n rejectNext = reject;\n });\n },\n \n async return(): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n return { value: undefined as unknown as ServerSentEvent, done: true };\n },\n \n async throw(e: Error): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n throw e;\n },\n };\n },\n };\n }\n}\n\n/**\n * Fetch-based stream adapter for React Native and Node.js\n * Uses ReadableStream to parse SSE from fetch response\n */\nexport class FetchStreamAdapter implements StreamAdapter {\n constructor(private fetchFn: typeof fetch = fetch) {}\n \n supportsNativeSSE(): boolean {\n return false;\n }\n \n createEventSource(url: string, options?: EventSourceOptions): AsyncIterable<ServerSentEvent> {\n const fetchFn = this.fetchFn;\n \n return {\n [Symbol.asyncIterator]() {\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;\n let buffer = '';\n let done = false;\n const eventQueue: ServerSentEvent[] = [];\n \n const cleanup = () => {\n if (reader) {\n reader.cancel().catch(() => {});\n reader = null;\n }\n done = true;\n };\n \n // Handle abort signal\n options?.signal?.addEventListener('abort', cleanup);\n \n // Start fetch\n const fetchPromise = fetchFn(url, {\n method: 'GET',\n headers: {\n 'Accept': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n ...options?.headers,\n },\n signal: options?.signal,\n }).then((response) => {\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n \n if (!response.body) {\n throw new Error('Response body is not available');\n }\n \n options?.onOpen?.();\n reader = response.body.getReader();\n return reader;\n }).catch((err) => {\n options?.onError?.(err);\n throw err;\n });\n \n const decoder = new TextDecoder();\n \n return {\n async next(): Promise<IteratorResult<ServerSentEvent>> {\n // Return queued events first\n if (eventQueue.length > 0) {\n return { value: eventQueue.shift()!, done: false };\n }\n \n if (done) {\n return { value: undefined as unknown as ServerSentEvent, done: true };\n }\n \n // Ensure reader is initialized\n if (!reader) {\n reader = await fetchPromise;\n }\n \n // Read until we have at least one event\n while (eventQueue.length === 0 && !done) {\n const { value, done: readerDone } = await reader.read();\n \n if (readerDone) {\n done = true;\n // Parse any remaining buffer\n if (buffer.trim()) {\n const { parsed } = parseSSE(buffer + '\\n\\n');\n eventQueue.push(...parsed);\n }\n break;\n }\n \n buffer += decoder.decode(value, { stream: true });\n const { parsed, remaining } = parseSSE(buffer);\n buffer = remaining;\n eventQueue.push(...parsed);\n }\n \n if (eventQueue.length > 0) {\n return { value: eventQueue.shift()!, done: false };\n }\n \n return { value: undefined as unknown as ServerSentEvent, done: true };\n },\n \n async return(): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n return { value: undefined as unknown as ServerSentEvent, done: true };\n },\n \n async throw(e: Error): Promise<IteratorResult<ServerSentEvent>> {\n cleanup();\n throw e;\n },\n };\n },\n };\n }\n}\n\n/**\n * Auto-detect and create the best stream adapter for the current environment\n */\nexport function createStreamAdapter(fetchFn?: typeof fetch): StreamAdapter {\n // Check for native EventSource (browser)\n if (typeof EventSource !== 'undefined') {\n return new WebStreamAdapter();\n }\n \n // Fall back to fetch-based adapter\n return new FetchStreamAdapter(fetchFn ?? fetch);\n}\n","/**\n * Base HTTP client for SaferCity SDK\n */\n\nimport type { SaferCityConfig, RequestOptions, ApiResponse } from './types';\nimport { SaferCityApiError } from './types';\nimport { createAuthHeader } from './auth';\n\nexport interface BaseClientOptions extends SaferCityConfig {}\n\n/**\n * Create base headers for requests\n */\nfunction createHeaders(\n config: SaferCityConfig,\n options?: RequestOptions\n): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n ...config.headers,\n ...options?.headers,\n };\n\n if (config.token) {\n headers['Authorization'] = createAuthHeader(config.token);\n }\n\n if (config.tenantId) {\n headers['X-Tenant-ID'] = config.tenantId;\n }\n\n return headers;\n}\n\n/**\n * Create a timeout signal\n */\nfunction createTimeoutSignal(timeout: number, existingSignal?: AbortSignal): AbortSignal {\n const controller = new AbortController();\n \n const timeoutId = setTimeout(() => {\n controller.abort(new Error(`Request timeout after ${timeout}ms`));\n }, timeout);\n \n // If there's an existing signal, abort when it aborts\n if (existingSignal) {\n existingSignal.addEventListener('abort', () => {\n clearTimeout(timeoutId);\n controller.abort(existingSignal.reason);\n });\n }\n \n return controller.signal;\n}\n\n/**\n * Base HTTP client with common functionality\n */\nexport class BaseClient {\n protected config: Required<Pick<SaferCityConfig, 'baseUrl' | 'timeout'>> & SaferCityConfig;\n protected fetchFn: typeof fetch;\n\n constructor(options: BaseClientOptions) {\n this.config = {\n timeout: 30000,\n ...options,\n baseUrl: options.baseUrl.replace(/\\/$/, ''), // Remove trailing slash\n };\n // Bind fetch to window/globalThis to avoid \"Illegal invocation\" error\n // when fetch is called without proper context\n this.fetchFn = options.fetch ?? (typeof window !== 'undefined' \n ? window.fetch.bind(window) \n : (typeof globalThis !== 'undefined' && globalThis.fetch \n ? globalThis.fetch.bind(globalThis) \n : fetch));\n }\n\n /**\n * Update authentication token\n */\n setToken(token: string | undefined): void {\n this.config.token = token;\n }\n\n /**\n * Update tenant ID\n */\n setTenantId(tenantId: string | undefined): void {\n this.config.tenantId = tenantId;\n }\n\n /**\n * Get current configuration (read-only)\n */\n getConfig(): Readonly<SaferCityConfig> {\n return { ...this.config };\n }\n\n /**\n * Build full URL from path\n */\n protected buildUrl(path: string, query?: Record<string, string | number | boolean | undefined>): string {\n // Normalize: ensure path starts with /\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n // Normalize: ensure baseUrl doesn't end with /\n const baseUrl = this.config.baseUrl.replace(/\\/$/, '');\n \n // Build query string\n let queryString = '';\n if (query) {\n const params = new URLSearchParams();\n Object.entries(query).forEach(([key, value]) => {\n if (value !== undefined) {\n params.set(key, String(value));\n }\n });\n queryString = params.toString();\n }\n \n // Simple concatenation: baseUrl + path + query\n // This works for both absolute URLs (https://api.example.com) and \n // relative URLs (/api/safercity) used in proxy mode\n const fullPath = baseUrl + normalizedPath;\n return queryString ? `${fullPath}?${queryString}` : fullPath;\n }\n\n /**\n * Make HTTP request\n */\n protected async request<T>(\n method: string,\n path: string,\n options?: RequestOptions & {\n body?: unknown;\n query?: Record<string, string | number | boolean | undefined>;\n }\n ): Promise<ApiResponse<T>> {\n const url = this.buildUrl(path, options?.query);\n const headers = createHeaders(this.config, options);\n const timeout = options?.timeout ?? this.config.timeout;\n const signal = createTimeoutSignal(timeout, options?.signal);\n\n const response = await this.fetchFn(url, {\n method,\n headers,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n signal,\n });\n\n let data: unknown;\n const contentType = response.headers.get('content-type');\n \n if (contentType?.includes('application/json')) {\n data = await response.json();\n } else {\n data = await response.text();\n }\n\n if (!response.ok) {\n throw SaferCityApiError.fromResponse(response, data);\n }\n\n return {\n data: data as T,\n status: response.status,\n headers: response.headers,\n };\n }\n\n /**\n * GET request\n */\n async get<T>(\n path: string,\n options?: RequestOptions & { query?: Record<string, string | number | boolean | undefined> }\n ): Promise<ApiResponse<T>> {\n return this.request<T>('GET', path, options);\n }\n\n /**\n * POST request\n */\n async post<T>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('POST', path, { ...options, body });\n }\n\n /**\n * PUT request\n */\n async put<T>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('PUT', path, { ...options, body });\n }\n\n /**\n * PATCH request\n */\n async patch<T>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('PATCH', path, { ...options, body });\n }\n\n /**\n * DELETE request\n */\n async delete<T>(\n path: string,\n options?: RequestOptions\n ): Promise<ApiResponse<T>> {\n return this.request<T>('DELETE', path, options);\n }\n}\n"]}
package/package.json CHANGED
@@ -1,16 +1,11 @@
1
1
  {
2
2
  "name": "@safercity/sdk-core",
3
- "version": "0.0.1",
3
+ "version": "0.1.1",
4
4
  "description": "Core utilities for SaferCity SDK - fetch abstraction, streaming, and authentication",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "SaferCity"
8
8
  },
9
- "repository": {
10
- "type": "git",
11
- "url": "https://github.com/safercity/safercity-v2.git",
12
- "directory": "packages/sdk/core"
13
- },
14
9
  "keywords": ["safercity", "sdk", "core", "streaming", "sse"],
15
10
  "sideEffects": false,
16
11
  "type": "module",
@@ -37,6 +32,7 @@
37
32
  "build": "tsup",
38
33
  "dev": "tsup --watch",
39
34
  "check-types": "tsc --noEmit",
35
+ "test": "bun test",
40
36
  "clean": "rm -rf dist"
41
37
  },
42
38
  "devDependencies": {