@mitway/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,432 @@
1
+ import * as _supabase_postgrest_js from '@supabase/postgrest-js';
2
+
3
+ /**
4
+ * User shape returned by the MITWAY-BaaS auth endpoints.
5
+ *
6
+ * This is a 1:1 inline copy of the `User` zod schema from the BaaS workspace
7
+ * package `@mitway-baas/shared-schemas` (`src/auth.schema.ts`). It is
8
+ * inlined here so this SDK has zero internal MITWAY-BaaS workspace
9
+ * dependencies and can be published to npm standalone.
10
+ *
11
+ * If the backend ever changes the User shape, update this type to match.
12
+ * The fields below should always be a strict subset/superset of what
13
+ * `POST /api/auth/login` and `POST /api/auth/register` return on the
14
+ * `user` field of their JSON response.
15
+ */
16
+ interface User {
17
+ id: string;
18
+ email: string;
19
+ /** Hashed password — usually omitted from API responses but included in the type for shape parity. */
20
+ password: string | null;
21
+ is_project_admin: boolean;
22
+ profile: Record<string, unknown>;
23
+ metadata: Record<string, unknown>;
24
+ /** ISO 8601 timestamp when the user was banned, or null. */
25
+ banned_until: string | null;
26
+ /** ISO 8601 timestamp. */
27
+ created_at: string;
28
+ /** ISO 8601 timestamp. */
29
+ updated_at: string;
30
+ }
31
+
32
+ /**
33
+ * MITWAY-BaaS SDK types — only SDK-specific shapes live here.
34
+ * The `User` shape is inlined in `./lib/user` so this package has zero
35
+ * MITWAY-BaaS workspace dependencies.
36
+ */
37
+
38
+ interface MitwayBaasConfig {
39
+ /**
40
+ * Base URL of the MITWAY-BaaS backend (auth, metadata, etc).
41
+ * @default "http://localhost:7130"
42
+ */
43
+ baseUrl?: string;
44
+ /**
45
+ * Base URL of the per-cliente PostgREST instance. Used by the database
46
+ * module. If omitted, calling `client.database.from(...)` will throw at
47
+ * call time. The PostgREST URL is separate from `baseUrl` because the two
48
+ * services are deployed as independent Pods in the per-cliente namespace.
49
+ */
50
+ postgrestUrl?: string;
51
+ /**
52
+ * Anonymous JWT (signed by the tenant's JWT_SECRET, role = "anon").
53
+ * Used as a fallback Bearer token for unauthenticated requests when no
54
+ * user session is active.
55
+ */
56
+ anonKey?: string;
57
+ /**
58
+ * Custom fetch implementation. Useful in Node < 18 or test environments.
59
+ * Defaults to `globalThis.fetch`.
60
+ */
61
+ fetch?: typeof fetch;
62
+ /**
63
+ * Custom default headers included on every request.
64
+ */
65
+ headers?: Record<string, string>;
66
+ /**
67
+ * Enable debug logging. `true` logs to console; pass a function to receive
68
+ * log lines instead.
69
+ */
70
+ debug?: boolean | ((message: string, ...args: any[]) => void);
71
+ /**
72
+ * Per-request timeout in milliseconds. 0 disables the timeout.
73
+ * @default 30000
74
+ */
75
+ timeout?: number;
76
+ /**
77
+ * Number of retry attempts on network errors and 5xx responses. Client
78
+ * errors (4xx) are never retried. 0 disables retries.
79
+ * @default 3
80
+ */
81
+ retryCount?: number;
82
+ /**
83
+ * Initial delay before the first retry, in ms. Doubles each attempt with
84
+ * ±15% jitter.
85
+ * @default 500
86
+ */
87
+ retryDelay?: number;
88
+ /**
89
+ * Automatically refresh the access token on 401 INVALID_TOKEN responses
90
+ * and retry the original request.
91
+ * @default true
92
+ */
93
+ autoRefreshToken?: boolean;
94
+ }
95
+ /**
96
+ * Active user session in memory. Mirrors what the auth endpoints return.
97
+ */
98
+ interface AuthSession {
99
+ user: User;
100
+ accessToken: string;
101
+ expiresAt?: Date;
102
+ }
103
+ /**
104
+ * Minimal payload that auth refresh endpoints emit. The SDK uses this to
105
+ * refresh the in-memory session.
106
+ */
107
+ interface AuthRefreshResponse {
108
+ user: User;
109
+ accessToken: string;
110
+ refreshToken?: string;
111
+ csrfToken?: string;
112
+ }
113
+ /**
114
+ * The `{ data, error }` envelope used by the MITWAY-BaaS backend on every
115
+ * response. The SDK unwraps `data` for happy-path returns and converts
116
+ * `error` into a `MitwayBaasError`.
117
+ */
118
+ interface ApiError {
119
+ error: string;
120
+ message: string;
121
+ statusCode: number;
122
+ nextActions?: string;
123
+ }
124
+ declare class MitwayBaasError extends Error {
125
+ statusCode: number;
126
+ error: string;
127
+ nextActions?: string;
128
+ constructor(message: string, statusCode: number, error: string, nextActions?: string);
129
+ static fromApiError(apiError: ApiError): MitwayBaasError;
130
+ }
131
+
132
+ /**
133
+ * Debug logger for the MITWAY-BaaS SDK.
134
+ *
135
+ * Logs HTTP request/response details with automatic redaction of sensitive
136
+ * headers and body fields. Disabled by default; pass `debug: true` (or a
137
+ * custom log function) on the SDK config to enable.
138
+ */
139
+ type LogFunction = (message: string, ...args: any[]) => void;
140
+ declare class Logger {
141
+ enabled: boolean;
142
+ private customLog;
143
+ constructor(debug?: boolean | LogFunction);
144
+ log(message: string, ...args: any[]): void;
145
+ warn(message: string, ...args: any[]): void;
146
+ error(message: string, ...args: any[]): void;
147
+ logRequest(method: string, url: string, headers?: Record<string, string>, body?: any): void;
148
+ logResponse(method: string, url: string, status: number, durationMs: number, body?: any): void;
149
+ }
150
+
151
+ /**
152
+ * Token Manager for the MITWAY-BaaS SDK.
153
+ *
154
+ * In-memory storage for the access token + user. Browser CSRF token lives
155
+ * in a cookie so the cookie-based refresh flow works across page reloads.
156
+ */
157
+
158
+ declare class TokenManager {
159
+ private accessToken;
160
+ private user;
161
+ /** Fired when the access token changes (used by long-lived consumers). */
162
+ onTokenChange: (() => void) | null;
163
+ saveSession(session: AuthSession): void;
164
+ getSession(): AuthSession | null;
165
+ getAccessToken(): string | null;
166
+ setAccessToken(token: string): void;
167
+ getUser(): User | null;
168
+ setUser(user: User): void;
169
+ clearSession(): void;
170
+ }
171
+
172
+ /**
173
+ * HttpClient with retry, timeout, abort signal composition, and automatic
174
+ * token refresh on 401 INVALID_TOKEN responses.
175
+ *
176
+ * Ported from the InsForge SDK with rebranding plus one route change:
177
+ * the refresh endpoint is `/api/auth/refresh` (MITWAY-BaaS) instead of
178
+ * `/api/auth/sessions/current` (InsForge).
179
+ */
180
+
181
+ type JsonRequestBody = Record<string, unknown> | unknown[] | null;
182
+ interface RequestOptions extends Omit<RequestInit, 'body'> {
183
+ params?: Record<string, string>;
184
+ body?: RequestInit['body'] | JsonRequestBody;
185
+ /**
186
+ * Allow retrying non-idempotent requests (POST, PATCH). Off by default to
187
+ * prevent duplicate writes on transient server errors.
188
+ */
189
+ idempotent?: boolean;
190
+ }
191
+ declare class HttpClient {
192
+ readonly baseUrl: string;
193
+ readonly fetch: typeof fetch;
194
+ private defaultHeaders;
195
+ private anonKey;
196
+ private userToken;
197
+ private logger;
198
+ private autoRefreshToken;
199
+ private isRefreshing;
200
+ private refreshPromise;
201
+ private tokenManager;
202
+ private refreshToken;
203
+ private timeout;
204
+ private retryCount;
205
+ private retryDelay;
206
+ constructor(config: MitwayBaasConfig, tokenManager?: TokenManager, logger?: Logger);
207
+ private buildUrl;
208
+ private isRetryableStatus;
209
+ private computeRetryDelay;
210
+ private handleRequest;
211
+ request<T>(method: string, path: string, options?: RequestOptions): Promise<T>;
212
+ get<T>(path: string, options?: RequestOptions): Promise<T>;
213
+ post<T>(path: string, body?: any, options?: RequestOptions): Promise<T>;
214
+ put<T>(path: string, body?: any, options?: RequestOptions): Promise<T>;
215
+ patch<T>(path: string, body?: any, options?: RequestOptions): Promise<T>;
216
+ delete<T>(path: string, options?: RequestOptions): Promise<T>;
217
+ setAuthToken(token: string | null): void;
218
+ setRefreshToken(token: string | null): void;
219
+ getHeaders(): Record<string, string>;
220
+ /**
221
+ * Refresh the current session by calling the MITWAY-BaaS refresh endpoint.
222
+ * Note: the route is `/api/auth/refresh` (POST), not the InsForge
223
+ * `/api/auth/sessions/current` route. Returns the new access token + user.
224
+ */
225
+ handleTokenRefresh(): Promise<AuthRefreshResponse>;
226
+ }
227
+
228
+ /**
229
+ * Auth module — MITWAY-BaaS specific.
230
+ *
231
+ * Targets the routes that the MITWAY-BaaS backend currently exposes:
232
+ * POST /api/auth/register → signUp
233
+ * POST /api/auth/login → signInWithPassword
234
+ * POST /api/auth/logout → signOut
235
+ * POST /api/auth/refresh → refreshSession (also auto-called by HttpClient)
236
+ *
237
+ * OAuth, email verification, password reset, and the InsForge-only
238
+ * profile/sessions admin endpoints are NOT included — the backend does not
239
+ * implement them yet. When it does, port the corresponding methods from the
240
+ * upstream InsForge SDK (`InsForge/InsForge-sdk-js/src/modules/auth/auth.ts`).
241
+ */
242
+
243
+ interface SignUpRequest {
244
+ email: string;
245
+ password: string;
246
+ name?: string;
247
+ }
248
+ interface SignInRequest {
249
+ email: string;
250
+ password: string;
251
+ }
252
+ /**
253
+ * The shape the backend returns from /register, /login, and /refresh. Mirrors
254
+ * what the existing backend handlers send: `{ user, accessToken, refreshToken,
255
+ * csrfToken? }` wrapped in the standard `{ data, error }` envelope. The SDK
256
+ * unwraps the envelope before reaching this type, so consumers see the raw
257
+ * shape.
258
+ */
259
+ interface AuthResponse {
260
+ user: User;
261
+ accessToken: string;
262
+ refreshToken?: string;
263
+ csrfToken?: string;
264
+ }
265
+ type AuthResult<T> = {
266
+ data: T | null;
267
+ error: MitwayBaasError | null;
268
+ };
269
+ declare class Auth {
270
+ private http;
271
+ private tokenManager;
272
+ constructor(http: HttpClient, tokenManager: TokenManager);
273
+ /**
274
+ * Persist the session in memory + HttpClient defaults so subsequent
275
+ * requests carry the new bearer token automatically.
276
+ */
277
+ private saveSessionFromResponse;
278
+ /**
279
+ * Create a new user account and start a session.
280
+ *
281
+ * @example
282
+ * const { data, error } = await client.auth.signUp({
283
+ * email: 'a@b.com',
284
+ * password: 'a-strong-password',
285
+ * name: 'Alice'
286
+ * });
287
+ */
288
+ signUp(request: SignUpRequest): Promise<AuthResult<AuthResponse>>;
289
+ /**
290
+ * Sign in with email + password and start a session.
291
+ */
292
+ signInWithPassword(request: SignInRequest): Promise<AuthResult<AuthResponse>>;
293
+ /**
294
+ * End the current session. Clears in-memory state even if the backend
295
+ * call fails (network/offline).
296
+ */
297
+ signOut(): Promise<{
298
+ error: MitwayBaasError | null;
299
+ }>;
300
+ /**
301
+ * Manually refresh the current session. The HttpClient will call this
302
+ * automatically on 401 INVALID_TOKEN responses; consumers usually do
303
+ * not need to call it directly.
304
+ */
305
+ refreshSession(): Promise<AuthResult<AuthResponse>>;
306
+ /**
307
+ * Get the current in-memory session, or null if the user is not signed in.
308
+ * Synchronous — does not hit the network.
309
+ */
310
+ getSession(): AuthSession | null;
311
+ /**
312
+ * Get the current in-memory user, or null if not signed in.
313
+ */
314
+ getUser(): User | null;
315
+ }
316
+
317
+ declare class Database {
318
+ private postgrest;
319
+ private postgrestUrl;
320
+ constructor(tokenManager: TokenManager, postgrestUrl: string | undefined, anonKey: string | undefined);
321
+ private requireClient;
322
+ /**
323
+ * Build a PostgREST query against a table.
324
+ *
325
+ * @example
326
+ * const { data, error } = await client.database
327
+ * .from('posts')
328
+ * .select('*')
329
+ * .eq('user_id', userId)
330
+ * .order('created_at', { ascending: false })
331
+ * .limit(10);
332
+ *
333
+ * @example
334
+ * const { data, error } = await client.database
335
+ * .from('posts')
336
+ * .insert({ title: 'Hello', content: 'World' })
337
+ * .select()
338
+ * .single();
339
+ */
340
+ from(table: string): _supabase_postgrest_js.PostgrestQueryBuilder<any, any, any, string, unknown>;
341
+ /**
342
+ * Call a PostgreSQL stored function (RPC).
343
+ *
344
+ * @example
345
+ * const { data, error } = await client.database
346
+ * .rpc('get_user_stats', { user_id: 123 });
347
+ */
348
+ rpc(fn: string, args?: Record<string, unknown>, options?: {
349
+ head?: boolean;
350
+ get?: boolean;
351
+ count?: 'exact' | 'planned' | 'estimated';
352
+ }): _supabase_postgrest_js.PostgrestFilterBuilder<any, any, any, any, string, null, "RPC">;
353
+ /**
354
+ * The PostgREST URL the database client is talking to, or undefined if
355
+ * the database module is not configured.
356
+ */
357
+ getUrl(): string | undefined;
358
+ }
359
+
360
+ /**
361
+ * MITWAY-BaaS SDK client.
362
+ *
363
+ * @example
364
+ * ```typescript
365
+ * import { createClient } from '@mitway-baas/sdk';
366
+ *
367
+ * const client = createClient({
368
+ * baseUrl: 'https://acme.api.dev.nttmitway.com', // backend (auth)
369
+ * postgrestUrl: 'https://acme.db.dev.nttmitway.com', // postgrest (db)
370
+ * anonKey: 'eyJhbGciOiJIUzI1NiIs...', // optional
371
+ * });
372
+ *
373
+ * // Auth
374
+ * const { data: session, error } = await client.auth.signInWithPassword({
375
+ * email: 'a@b.com',
376
+ * password: 'pw',
377
+ * });
378
+ *
379
+ * // Database
380
+ * const { data, error: dbError } = await client.database
381
+ * .from('posts')
382
+ * .select('*')
383
+ * .eq('user_id', session.user.id)
384
+ * .order('created_at', { ascending: false });
385
+ * ```
386
+ */
387
+ declare class MitwayBaasClient {
388
+ private http;
389
+ private tokenManager;
390
+ readonly auth: Auth;
391
+ readonly database: Database;
392
+ constructor(config?: MitwayBaasConfig);
393
+ /**
394
+ * Escape hatch for callers that need to make custom requests against the
395
+ * backend without going through `auth` or `database`.
396
+ */
397
+ getHttpClient(): HttpClient;
398
+ }
399
+
400
+ /**
401
+ * @mitway-baas/sdk — TypeScript SDK for the MITWAY-BaaS backend.
402
+ *
403
+ * Currently ships:
404
+ * - auth (signUp, signInWithPassword, signOut, refreshSession, getSession, getUser)
405
+ * - database (PostgREST-backed query builder via @supabase/postgrest-js)
406
+ *
407
+ * Not yet included (no backend support):
408
+ * - storage
409
+ * - functions
410
+ * - email
411
+ * - ai
412
+ * - realtime
413
+ *
414
+ * @packageDocumentation
415
+ */
416
+
417
+ /**
418
+ * Factory function for creating SDK clients (Supabase-style).
419
+ *
420
+ * @example
421
+ * ```typescript
422
+ * import { createClient } from '@mitway-baas/sdk';
423
+ *
424
+ * const client = createClient({
425
+ * baseUrl: 'https://acme.api.dev.nttmitway.com',
426
+ * postgrestUrl: 'https://acme.db.dev.nttmitway.com',
427
+ * });
428
+ * ```
429
+ */
430
+ declare function createClient(config: MitwayBaasConfig): MitwayBaasClient;
431
+
432
+ export { type ApiError, Auth, type AuthRefreshResponse, type AuthResponse, type AuthResult, type AuthSession, Database, HttpClient, Logger, MitwayBaasClient, type MitwayBaasConfig, MitwayBaasError, type SignInRequest, type SignUpRequest, TokenManager, type User, createClient, MitwayBaasClient as default };