@safercity/sdk-core
v0.4.0
Published
Core utilities for SaferCity SDK - fetch abstraction, streaming, and authentication
Maintainers
Readme
@safercity/sdk-core
Core utilities for SaferCity SDK including fetch abstraction, streaming support, and authentication helpers.
Note: Most users should use
@safercity/sdkinstead. This package contains low-level utilities used internally.
What's New in v0.4.0
- No breaking changes - Internal package only. Bumped for consistency with SDK v0.4.0.
What's New in v0.3.2
- ProxyModeConfig Enhanced - Added
tenantId,userId, andheaderssupport toProxyModeConfigfor white-label apps using proxy mode.
What's New in v0.3.1
- No breaking changes - Internal package only. Bumped for consistency with SDK client v0.3.1.
What's New in v0.3.0
- Foundation for Typed SDK - Core types and utilities updated to support the auto-generated OpenAPI SDK.
Installation
npm install @safercity/sdk-coreWhat's New in v0.2.0
- User Scoping - Added
userIdtoDirectModeConfigandCookieModeConfigfor automatic request scoping
What's New in v0.1.3
- OAuth endpoint path fix - Token and refresh endpoints now correctly use
/v1/oauth/*prefix
What's New in v0.1.0
- Auth Mode Types - Three authentication modes: proxy, direct, and cookie
- Token Manager - Server-side OAuth token management with automatic refresh and request deduplication
- Token Storage - Pluggable token storage interface with in-memory default
Features
- Type definitions - Common types for API requests/responses and auth mode configuration
- Authentication utilities - Token management, JWT parsing, OAuth token lifecycle
- Streaming/SSE support - Cross-platform Server-Sent Events
- Base HTTP client - Fetch wrapper with timeout and error handling
Usage
Auth Mode Types
The SDK supports three authentication modes for different deployment scenarios:
import type {
AuthMode,
ProxyModeConfig,
DirectModeConfig,
CookieModeConfig,
ClientModeConfig,
} from '@safercity/sdk-core';| Mode | Flow | Best For |
|------|------|----------|
| "proxy" | Client -> Your Backend -> SaferCity API | Production web apps (most secure) |
| "direct" | Client -> SaferCity API with external token | White-label apps with external auth (Clerk, Auth0, better-auth) |
| "cookie" | Browser with credentials: include | First-party web apps using session cookies |
// Proxy mode (default) - backend adds tenant credentials
const proxyConfig: ProxyModeConfig = {
mode: 'proxy',
proxyBaseUrl: '/api/safercity', // defaults to "/api/safercity"
};
// Direct mode - external auth provider supplies the token
const directConfig: DirectModeConfig = {
mode: 'direct',
baseUrl: 'https://api.safercity.com',
tenantId: 'tenant-123',
userId: 'user-123', // optional, for user-scoped operations
getAccessToken: () => session?.accessToken,
};
// Cookie mode - session cookies sent automatically
const cookieConfig: CookieModeConfig = {
mode: 'cookie',
baseUrl: 'https://api.safercity.com',
tenantId: 'tenant-123', // optional, can come from cookie
userId: 'user-123', // optional, for user-scoped operations
};Token Manager
Server-side OAuth token management with automatic refresh and concurrent request deduplication:
import { TokenManager, MemoryTokenStorage } from '@safercity/sdk-core';
const tokenManager = new TokenManager({
credentials: {
clientId: process.env.SAFERCITY_CLIENT_ID!,
clientSecret: process.env.SAFERCITY_CLIENT_SECRET!,
tenantId: 'tenant-123', // optional
},
baseUrl: 'https://api.safercity.com',
refreshBuffer: 60000, // refresh 1 minute before expiry (default)
storage: new MemoryTokenStorage(), // default, or provide custom
});
// Get token (auto-refreshes if expired, dedupes concurrent calls)
const token = await tokenManager.getToken();
// Force refresh (useful for error recovery)
const freshToken = await tokenManager.forceRefresh();
// Check state
tokenManager.hasToken(); // boolean
tokenManager.clear(); // remove stored tokensToken Storage
Implement the TokenStorage interface for custom persistence:
import type { TokenStorage, AuthTokens } from '@safercity/sdk-core';
// Built-in: MemoryTokenStorage (default, no persistence)
import { MemoryTokenStorage } from '@safercity/sdk-core';
// Custom implementation
class MyTokenStorage implements TokenStorage {
get(): AuthTokens | null { /* ... */ }
set(tokens: AuthTokens): void { /* ... */ }
clear(): void { /* ... */ }
}Stream Adapters
import {
WebStreamAdapter,
FetchStreamAdapter,
createStreamAdapter
} from '@safercity/sdk-core';
// Auto-detect best adapter for environment
const adapter = createStreamAdapter();
// Or use specific adapter
const webAdapter = new WebStreamAdapter(); // Browser with EventSource
const fetchAdapter = new FetchStreamAdapter(fetch); // Node.js / React Native
// Stream events
const stream = adapter.createEventSource('https://api.example.com/stream', {
headers: { Authorization: 'Bearer token' },
});
for await (const event of stream) {
console.log(event.data);
}Authentication Helpers
import {
createAuthHeader,
parseJwtPayload,
getJwtExpiration,
isTokenExpired,
} from '@safercity/sdk-core';
// Create auth header
const header = createAuthHeader('my-token'); // "Bearer my-token"
// Parse JWT (without verification)
const payload = parseJwtPayload(token);
console.log(payload?.sub); // User ID
// Check expiration
const expiresAt = getJwtExpiration(token);
if (isTokenExpired(expiresAt)) {
// Token expired, refresh it
}Base Client
import { BaseClient } from '@safercity/sdk-core';
const client = new BaseClient({
baseUrl: 'https://api.example.com',
token: 'my-token',
timeout: 30000,
});
// Make requests
const response = await client.get('/users');
const created = await client.post('/users', { name: 'John' });Error Handling
import { SaferCityApiError } from '@safercity/sdk-core';
try {
await client.get('/not-found');
} catch (error) {
if (error instanceof SaferCityApiError) {
console.log(error.error); // "not_found"
console.log(error.message); // "Resource not found"
console.log(error.status); // 404
}
}Types
SaferCityConfig
interface SaferCityConfig {
baseUrl: string;
token?: string;
tenantId?: string;
fetch?: typeof fetch;
timeout?: number;
headers?: Record<string, string>;
}AuthTokens
interface AuthTokens {
accessToken: string;
refreshToken?: string;
expiresAt?: number;
tokenType: string;
}OAuthCredentials
interface OAuthCredentials {
clientId: string;
clientSecret: string;
tenantId?: string;
}ServerSentEvent
interface ServerSentEvent {
id?: string;
event?: string;
data: string;
retry?: number;
}License
MIT
