@palbase/core 0.3.0 → 0.5.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.
package/dist/index.cjs CHANGED
@@ -82,15 +82,51 @@ function parseProjectRef(apiKey) {
82
82
  }
83
83
  var MAX_RETRIES = 3;
84
84
  var INITIAL_BACKOFF_MS = 200;
85
- var HttpClient = class {
85
+ var HttpClient = class _HttpClient {
86
86
  apiKey;
87
87
  options;
88
88
  tokenManager = null;
89
+ /**
90
+ * Admin JWT used for platform admin endpoints (/admin/*).
91
+ * When set, takes precedence over serviceRoleKey and tokenManager access
92
+ * token in the Authorization header.
93
+ */
94
+ adminToken = null;
89
95
  interceptors = [];
90
96
  constructor(apiKey, options) {
91
97
  this.apiKey = apiKey;
92
98
  this.options = options;
93
99
  }
100
+ /** Set (or clear) the admin JWT used on admin endpoints. */
101
+ setAdminToken(token) {
102
+ this.adminToken = token;
103
+ }
104
+ /**
105
+ * Create a scoped HttpClient that adds the given extra headers to every
106
+ * request. The returned client shares the admin token and token manager
107
+ * with the parent at runtime — later changes on the parent propagate to
108
+ * the scope and vice versa.
109
+ *
110
+ * Typical use: tagging admin calls with `x-palbase-project: <ref>` so the
111
+ * gateway can route them to the correct project's data plane.
112
+ */
113
+ withHeaders(extra) {
114
+ const mergedHeaders = { ...this.options?.headers ?? {}, ...extra };
115
+ const parent = this;
116
+ const scoped = new _HttpClient(this.apiKey, {
117
+ ...this.options,
118
+ headers: mergedHeaders
119
+ });
120
+ scoped.tokenManager = this.tokenManager;
121
+ Object.defineProperty(scoped, "adminToken", {
122
+ get: () => parent.adminToken,
123
+ set: (v) => {
124
+ parent.adminToken = v;
125
+ },
126
+ configurable: true
127
+ });
128
+ return scoped;
129
+ }
94
130
  /** Add a request interceptor. Runs before every request. */
95
131
  addInterceptor(interceptor) {
96
132
  this.interceptors.push(interceptor);
@@ -117,9 +153,11 @@ var HttpClient = class {
117
153
  }
118
154
  buildHeaders(options) {
119
155
  const headers = {
120
- "apikey": this.apiKey,
121
156
  "Content-Type": "application/json"
122
157
  };
158
+ if (this.apiKey) {
159
+ headers["apikey"] = this.apiKey;
160
+ }
123
161
  const token = this.tokenManager?.getAccessToken();
124
162
  if (token) {
125
163
  headers["Authorization"] = `Bearer ${token}`;
@@ -127,6 +165,9 @@ var HttpClient = class {
127
165
  if (this.options?.serviceRoleKey) {
128
166
  headers["Authorization"] = `Bearer ${this.options.serviceRoleKey}`;
129
167
  }
168
+ if (this.adminToken) {
169
+ headers["Authorization"] = `Bearer ${this.adminToken}`;
170
+ }
130
171
  if (this.options?.headers) {
131
172
  Object.assign(headers, this.options.headers);
132
173
  }
@@ -176,7 +217,7 @@ var HttpClient = class {
176
217
  let data = null;
177
218
  let errorBody;
178
219
  const contentType = response.headers.get("Content-Type");
179
- if (contentType?.includes("json")) {
220
+ if (method !== "HEAD" && contentType?.includes("json")) {
180
221
  const body = await response.json();
181
222
  if (response.ok) {
182
223
  data = body;
@@ -196,7 +237,26 @@ var HttpClient = class {
196
237
  status: response.status
197
238
  };
198
239
  }
199
- return { data, error: null, status: response.status };
240
+ const contentRange = response.headers.get("Content-Range");
241
+ let count;
242
+ if (contentRange) {
243
+ const slash = contentRange.lastIndexOf("/");
244
+ if (slash >= 0) {
245
+ const totalPart = contentRange.slice(slash + 1);
246
+ if (totalPart !== "*") {
247
+ const parsed = Number.parseInt(totalPart, 10);
248
+ if (!Number.isNaN(parsed)) {
249
+ count = parsed;
250
+ }
251
+ }
252
+ }
253
+ }
254
+ return {
255
+ data,
256
+ error: null,
257
+ status: response.status,
258
+ ...count !== void 0 ? { count } : {}
259
+ };
200
260
  }
201
261
  delay(ms) {
202
262
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/errors.ts","../src/http.ts","../src/platform.ts","../src/token.ts"],"sourcesContent":["export { ConfigFetcher } from './config.js';\nexport { PalbaseError } from './errors.js';\nexport { HttpClient } from './http.js';\nexport type { RequestInterceptor } from './http.js';\nexport type { Platform } from './platform.js';\nexport { detectPlatform } from './platform.js';\nexport { TokenManager } from './token.js';\nexport type {\n AuthStateCallback,\n AuthStateEvent,\n HttpClientOptions,\n PalbaseConfig,\n PalbaseResponse,\n ProjectConfig,\n RequestOptions,\n Session,\n Unsubscribe,\n} from './types.js';\n","import type { HttpClient } from './http.js';\nimport type { ProjectConfig } from './types.js';\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\nexport class ConfigFetcher {\n protected readonly httpClient: HttpClient;\n private cachedConfig: ProjectConfig | null = null;\n private cacheTimestamp = 0;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n async getConfig(): Promise<ProjectConfig | null> {\n const now = Date.now();\n\n if (this.cachedConfig && now - this.cacheTimestamp < CACHE_TTL_MS) {\n return this.cachedConfig;\n }\n\n try {\n const response = await this.httpClient.request<ProjectConfig>('GET', '/v1/config');\n\n if (response.error || !response.data) {\n return null;\n }\n\n this.cachedConfig = response.data;\n this.cacheTimestamp = now;\n\n return this.cachedConfig;\n } catch {\n return null;\n }\n }\n}\n","export class PalbaseError extends Error {\n readonly code: string;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(code: string, message: string, status: number, details?: unknown) {\n super(message);\n this.name = 'PalbaseError';\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n","import { PalbaseError } from './errors.js';\nimport type { TokenManager } from './token.js';\nimport type { HttpClientOptions, PalbaseResponse, RequestOptions } from './types.js';\n\nconst PALBASE_DOMAIN = 'palbase.studio';\n\n/**\n * Parse project ref from an API key.\n * Format: pb_{ref}_{random}\n * Example: pb_abc12345_xxxxxxxxxxxxxxxxxxxxxxxx\n * Returns the ref segment or null if the key doesn't match.\n */\nfunction parseProjectRef(apiKey: string): string | null {\n const parts = apiKey.split('_');\n // Format: [\"pb\", ref, random]\n const ref = parts[1];\n if (parts.length >= 3 && parts[0] === 'pb' && ref) {\n return ref;\n }\n return null;\n}\nconst MAX_RETRIES = 3;\nconst INITIAL_BACKOFF_MS = 200;\n\n/**\n * Request interceptor. Runs before every HTTP request.\n * Can modify headers, body, or reject the request.\n */\nexport interface RequestInterceptor {\n (request: { headers: Record<string, string>; method: string; path: string }): void | Promise<void>;\n}\n\nexport class HttpClient {\n protected readonly apiKey: string;\n protected readonly options?: HttpClientOptions;\n\n tokenManager: TokenManager | null = null;\n\n private readonly interceptors: RequestInterceptor[] = [];\n\n constructor(apiKey: string, options?: HttpClientOptions) {\n this.apiKey = apiKey;\n this.options = options;\n }\n\n /** Add a request interceptor. Runs before every request. */\n addInterceptor(interceptor: RequestInterceptor): void {\n this.interceptors.push(interceptor);\n }\n\n async request<T>(\n method: string,\n path: string,\n options?: RequestOptions,\n ): Promise<PalbaseResponse<T>> {\n // If token is expired and refresh is available, refresh before making the request\n if (this.tokenManager?.isExpired() && this.tokenManager.getRefreshToken() && this.tokenManager.refreshFunction) {\n await this.tokenManager.refreshSession();\n }\n\n return this.executeWithRetry<T>(method, path, options, 0);\n }\n\n private getBaseUrl(): string {\n // Explicit URL always wins (local dev, testing)\n if (this.options?.url) {\n return this.options.url;\n }\n\n // Resolve from API key (format: pb_{ref}_{random})\n const ref = parseProjectRef(this.apiKey);\n if (ref) {\n return `https://${ref}.${PALBASE_DOMAIN}`;\n }\n\n throw new PalbaseError(\n 'invalid_api_key',\n 'Invalid API key format. Expected: pb_{ref}_{random}. Provide an explicit url option for custom endpoints.',\n 0,\n );\n }\n\n private buildHeaders(options?: RequestOptions): Record<string, string> {\n const headers: Record<string, string> = {\n 'apikey': this.apiKey,\n 'Content-Type': 'application/json',\n };\n\n // Add token if available\n const token = this.tokenManager?.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n // serviceRoleKey overrides token-based Authorization\n if (this.options?.serviceRoleKey) {\n headers['Authorization'] = `Bearer ${this.options.serviceRoleKey}`;\n }\n\n // Merge global custom headers\n if (this.options?.headers) {\n Object.assign(headers, this.options.headers);\n }\n\n // Merge per-request headers\n if (options?.headers) {\n Object.assign(headers, options.headers);\n }\n\n return headers;\n }\n\n private async executeWithRetry<T>(\n method: string,\n path: string,\n options: RequestOptions | undefined,\n attempt: number,\n ): Promise<PalbaseResponse<T>> {\n const url = `${this.getBaseUrl()}${path}`;\n const headers = this.buildHeaders(options);\n\n // Run interceptors\n for (const interceptor of this.interceptors) {\n await interceptor({ headers, method, path });\n }\n\n const fetchOptions: RequestInit = {\n method,\n headers,\n signal: options?.signal,\n };\n\n if (options?.body !== undefined) {\n fetchOptions.body = JSON.stringify(options.body);\n }\n\n let response: Response;\n try {\n response = await fetch(url, fetchOptions);\n } catch (error) {\n // Network error — retry with backoff\n if (attempt < MAX_RETRIES - 1) {\n const backoff = INITIAL_BACKOFF_MS * 2 ** attempt;\n await this.delay(backoff);\n return this.executeWithRetry<T>(method, path, options, attempt + 1);\n }\n\n // All retries exhausted — throw PalbaseError\n throw new PalbaseError(\n 'network_error',\n error instanceof Error ? error.message : 'Network request failed',\n 0,\n );\n }\n\n // Handle 429 Too Many Requests — retry with Retry-After or backoff;\n // if retries exhausted, fall through to normal error response handling below\n if (response.status === 429) {\n if (attempt < MAX_RETRIES - 1) {\n const retryAfter = response.headers.get('Retry-After');\n const parsed = retryAfter ? Number.parseInt(retryAfter, 10) : Number.NaN;\n const delayMs = Number.isNaN(parsed) ? INITIAL_BACKOFF_MS * 2 ** attempt : parsed * 1000;\n await this.delay(delayMs);\n return this.executeWithRetry<T>(method, path, options, attempt + 1);\n }\n }\n\n // Parse response body\n let data: T | null = null;\n let errorBody: { error?: string; error_description?: string; status?: number } | undefined;\n\n const contentType = response.headers.get('Content-Type');\n if (contentType?.includes('json')) {\n const body = await response.json() as Record<string, unknown>;\n if (response.ok) {\n data = body as T;\n } else {\n errorBody = body as typeof errorBody;\n }\n }\n\n if (!response.ok) {\n return {\n data: null,\n error: new PalbaseError(\n errorBody?.error ?? 'unknown_error',\n errorBody?.error_description ?? response.statusText,\n response.status,\n errorBody,\n ),\n status: response.status,\n };\n }\n\n return { data, error: null, status: response.status };\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","export type Platform = 'browser' | 'node' | 'react-native' | 'deno' | 'bun';\n\ndeclare const Deno: unknown;\ndeclare const process: { versions: Record<string, string> } | undefined;\n\nexport function detectPlatform(): Platform {\n if (typeof Deno !== 'undefined') {\n return 'deno';\n }\n\n if (process?.versions) {\n if ('bun' in process.versions) {\n return 'bun';\n }\n if ('node' in process.versions) {\n return 'node';\n }\n }\n\n if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {\n return 'react-native';\n }\n\n return 'browser';\n}\n","import type { AuthStateCallback, Session, Unsubscribe } from './types.js';\n\nexport class TokenManager {\n private session: Session | null = null;\n private listeners: Set<AuthStateCallback> = new Set();\n private refreshPromise: Promise<void> | null = null;\n\n refreshFunction: ((refreshToken: string) => Promise<Session>) | null = null;\n\n setSession(session: Session): void {\n this.session = session;\n this.notify('SESSION_SET', session);\n }\n\n getAccessToken(): string | null {\n return this.session?.accessToken ?? null;\n }\n\n getRefreshToken(): string | null {\n return this.session?.refreshToken ?? null;\n }\n\n clearSession(): void {\n this.session = null;\n this.notify('SESSION_CLEARED', null);\n }\n\n isExpired(): boolean {\n if (!this.session) return true;\n return Date.now() >= this.session.expiresAt;\n }\n\n async refreshSession(): Promise<void> {\n if (!this.session?.refreshToken || !this.refreshFunction) {\n return;\n }\n\n // Collapse concurrent refresh calls into a single request\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.executeRefresh(this.session.refreshToken);\n\n try {\n await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n onAuthStateChange(callback: AuthStateCallback): Unsubscribe {\n this.listeners.add(callback);\n return () => {\n this.listeners.delete(callback);\n };\n }\n\n private async executeRefresh(refreshToken: string): Promise<void> {\n if (!this.refreshFunction) return;\n const newSession = await this.refreshFunction(refreshToken);\n this.setSession(newSession);\n }\n\n private notify(event: 'SESSION_SET' | 'SESSION_CLEARED', session: Session | null): void {\n for (const listener of this.listeners) {\n listener(event, session);\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/errors.ts","../src/http.ts","../src/platform.ts","../src/token.ts"],"sourcesContent":["export { ConfigFetcher } from './config.js';\nexport { PalbaseError } from './errors.js';\nexport { HttpClient } from './http.js';\nexport type { RequestInterceptor } from './http.js';\nexport type { Platform } from './platform.js';\nexport { detectPlatform } from './platform.js';\nexport { TokenManager } from './token.js';\nexport type {\n AuthStateCallback,\n AuthStateEvent,\n HttpClientOptions,\n PalbaseConfig,\n PalbaseResponse,\n ProjectConfig,\n RequestOptions,\n Session,\n Unsubscribe,\n} from './types.js';\n","import type { HttpClient } from './http.js';\nimport type { ProjectConfig } from './types.js';\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\nexport class ConfigFetcher {\n protected readonly httpClient: HttpClient;\n private cachedConfig: ProjectConfig | null = null;\n private cacheTimestamp = 0;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n async getConfig(): Promise<ProjectConfig | null> {\n const now = Date.now();\n\n if (this.cachedConfig && now - this.cacheTimestamp < CACHE_TTL_MS) {\n return this.cachedConfig;\n }\n\n try {\n const response = await this.httpClient.request<ProjectConfig>('GET', '/v1/config');\n\n if (response.error || !response.data) {\n return null;\n }\n\n this.cachedConfig = response.data;\n this.cacheTimestamp = now;\n\n return this.cachedConfig;\n } catch {\n return null;\n }\n }\n}\n","export class PalbaseError extends Error {\n readonly code: string;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(code: string, message: string, status: number, details?: unknown) {\n super(message);\n this.name = 'PalbaseError';\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n","import { PalbaseError } from './errors.js';\nimport type { TokenManager } from './token.js';\nimport type { HttpClientOptions, PalbaseResponse, RequestOptions } from './types.js';\n\nconst PALBASE_DOMAIN = 'palbase.studio';\n\n/**\n * Parse project ref from an API key.\n * Format: pb_{ref}_{random}\n * Example: pb_abc12345_xxxxxxxxxxxxxxxxxxxxxxxx\n * Returns the ref segment or null if the key doesn't match.\n */\nfunction parseProjectRef(apiKey: string): string | null {\n const parts = apiKey.split('_');\n // Format: [\"pb\", ref, random]\n const ref = parts[1];\n if (parts.length >= 3 && parts[0] === 'pb' && ref) {\n return ref;\n }\n return null;\n}\nconst MAX_RETRIES = 3;\nconst INITIAL_BACKOFF_MS = 200;\n\n/**\n * Request interceptor. Runs before every HTTP request.\n * Can modify headers, body, or reject the request.\n */\nexport interface RequestInterceptor {\n (request: { headers: Record<string, string>; method: string; path: string }): void | Promise<void>;\n}\n\nexport class HttpClient {\n protected readonly apiKey: string;\n protected readonly options?: HttpClientOptions;\n\n tokenManager: TokenManager | null = null;\n\n /**\n * Admin JWT used for platform admin endpoints (/admin/*).\n * When set, takes precedence over serviceRoleKey and tokenManager access\n * token in the Authorization header.\n */\n adminToken: string | null = null;\n\n private readonly interceptors: RequestInterceptor[] = [];\n\n constructor(apiKey: string, options?: HttpClientOptions) {\n this.apiKey = apiKey;\n this.options = options;\n }\n\n /** Set (or clear) the admin JWT used on admin endpoints. */\n setAdminToken(token: string | null): void {\n this.adminToken = token;\n }\n\n /**\n * Create a scoped HttpClient that adds the given extra headers to every\n * request. The returned client shares the admin token and token manager\n * with the parent at runtime — later changes on the parent propagate to\n * the scope and vice versa.\n *\n * Typical use: tagging admin calls with `x-palbase-project: <ref>` so the\n * gateway can route them to the correct project's data plane.\n */\n withHeaders(extra: Record<string, string>): HttpClient {\n const mergedHeaders = { ...(this.options?.headers ?? {}), ...extra };\n const parent = this;\n const scoped: HttpClient = new HttpClient(this.apiKey, {\n ...this.options,\n headers: mergedHeaders,\n });\n scoped.tokenManager = this.tokenManager;\n // Delegate adminToken reads + writes to the parent so the scope always\n // sees the latest token, and setAdminToken on the scope affects the parent.\n Object.defineProperty(scoped, 'adminToken', {\n get: () => parent.adminToken,\n set: (v: string | null) => {\n parent.adminToken = v;\n },\n configurable: true,\n });\n return scoped;\n }\n\n /** Add a request interceptor. Runs before every request. */\n addInterceptor(interceptor: RequestInterceptor): void {\n this.interceptors.push(interceptor);\n }\n\n async request<T>(\n method: string,\n path: string,\n options?: RequestOptions,\n ): Promise<PalbaseResponse<T>> {\n // If token is expired and refresh is available, refresh before making the request\n if (this.tokenManager?.isExpired() && this.tokenManager.getRefreshToken() && this.tokenManager.refreshFunction) {\n await this.tokenManager.refreshSession();\n }\n\n return this.executeWithRetry<T>(method, path, options, 0);\n }\n\n private getBaseUrl(): string {\n // Explicit URL always wins (local dev, testing)\n if (this.options?.url) {\n return this.options.url;\n }\n\n // Resolve from API key (format: pb_{ref}_{random})\n const ref = parseProjectRef(this.apiKey);\n if (ref) {\n return `https://${ref}.${PALBASE_DOMAIN}`;\n }\n\n throw new PalbaseError(\n 'invalid_api_key',\n 'Invalid API key format. Expected: pb_{ref}_{random}. Provide an explicit url option for custom endpoints.',\n 0,\n );\n }\n\n private buildHeaders(options?: RequestOptions): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n // apikey header only when an API key is configured (platform admin flows\n // may omit it — e.g. setup/login before any project exists).\n if (this.apiKey) {\n headers['apikey'] = this.apiKey;\n }\n\n // Add token if available\n const token = this.tokenManager?.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n // serviceRoleKey overrides token-based Authorization\n if (this.options?.serviceRoleKey) {\n headers['Authorization'] = `Bearer ${this.options.serviceRoleKey}`;\n }\n\n // adminToken (platform admin JWT) takes precedence over everything else\n if (this.adminToken) {\n headers['Authorization'] = `Bearer ${this.adminToken}`;\n }\n\n // Merge global custom headers\n if (this.options?.headers) {\n Object.assign(headers, this.options.headers);\n }\n\n // Merge per-request headers\n if (options?.headers) {\n Object.assign(headers, options.headers);\n }\n\n return headers;\n }\n\n private async executeWithRetry<T>(\n method: string,\n path: string,\n options: RequestOptions | undefined,\n attempt: number,\n ): Promise<PalbaseResponse<T>> {\n const url = `${this.getBaseUrl()}${path}`;\n const headers = this.buildHeaders(options);\n\n // Run interceptors\n for (const interceptor of this.interceptors) {\n await interceptor({ headers, method, path });\n }\n\n const fetchOptions: RequestInit = {\n method,\n headers,\n signal: options?.signal,\n };\n\n if (options?.body !== undefined) {\n fetchOptions.body = JSON.stringify(options.body);\n }\n\n let response: Response;\n try {\n response = await fetch(url, fetchOptions);\n } catch (error) {\n // Network error — retry with backoff\n if (attempt < MAX_RETRIES - 1) {\n const backoff = INITIAL_BACKOFF_MS * 2 ** attempt;\n await this.delay(backoff);\n return this.executeWithRetry<T>(method, path, options, attempt + 1);\n }\n\n // All retries exhausted — throw PalbaseError\n throw new PalbaseError(\n 'network_error',\n error instanceof Error ? error.message : 'Network request failed',\n 0,\n );\n }\n\n // Handle 429 Too Many Requests — retry with Retry-After or backoff;\n // if retries exhausted, fall through to normal error response handling below\n if (response.status === 429) {\n if (attempt < MAX_RETRIES - 1) {\n const retryAfter = response.headers.get('Retry-After');\n const parsed = retryAfter ? Number.parseInt(retryAfter, 10) : Number.NaN;\n const delayMs = Number.isNaN(parsed) ? INITIAL_BACKOFF_MS * 2 ** attempt : parsed * 1000;\n await this.delay(delayMs);\n return this.executeWithRetry<T>(method, path, options, attempt + 1);\n }\n }\n\n // Parse response body\n let data: T | null = null;\n let errorBody: { error?: string; error_description?: string; status?: number } | undefined;\n\n // HEAD responses have no body by spec — skip parsing.\n const contentType = response.headers.get('Content-Type');\n if (method !== 'HEAD' && contentType?.includes('json')) {\n const body = await response.json() as Record<string, unknown>;\n if (response.ok) {\n data = body as T;\n } else {\n errorBody = body as typeof errorBody;\n }\n }\n\n if (!response.ok) {\n return {\n data: null,\n error: new PalbaseError(\n errorBody?.error ?? 'unknown_error',\n errorBody?.error_description ?? response.statusText,\n response.status,\n errorBody,\n ),\n status: response.status,\n };\n }\n\n // Parse PostgREST Content-Range for count queries (e.g. \"0-9/42\" or \"*/42\").\n const contentRange = response.headers.get('Content-Range');\n let count: number | undefined;\n if (contentRange) {\n const slash = contentRange.lastIndexOf('/');\n if (slash >= 0) {\n const totalPart = contentRange.slice(slash + 1);\n if (totalPart !== '*') {\n const parsed = Number.parseInt(totalPart, 10);\n if (!Number.isNaN(parsed)) {\n count = parsed;\n }\n }\n }\n }\n\n return {\n data,\n error: null,\n status: response.status,\n ...(count !== undefined ? { count } : {}),\n };\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","export type Platform = 'browser' | 'node' | 'react-native' | 'deno' | 'bun';\n\ndeclare const Deno: unknown;\ndeclare const process: { versions: Record<string, string> } | undefined;\n\nexport function detectPlatform(): Platform {\n if (typeof Deno !== 'undefined') {\n return 'deno';\n }\n\n if (process?.versions) {\n if ('bun' in process.versions) {\n return 'bun';\n }\n if ('node' in process.versions) {\n return 'node';\n }\n }\n\n if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {\n return 'react-native';\n }\n\n return 'browser';\n}\n","import type { AuthStateCallback, Session, Unsubscribe } from './types.js';\n\nexport class TokenManager {\n private session: Session | null = null;\n private listeners: Set<AuthStateCallback> = new Set();\n private refreshPromise: Promise<void> | null = null;\n\n refreshFunction: ((refreshToken: string) => Promise<Session>) | null = null;\n\n setSession(session: Session): void {\n this.session = session;\n this.notify('SESSION_SET', session);\n }\n\n getAccessToken(): string | null {\n return this.session?.accessToken ?? null;\n }\n\n getRefreshToken(): string | null {\n return this.session?.refreshToken ?? null;\n }\n\n clearSession(): void {\n this.session = null;\n this.notify('SESSION_CLEARED', null);\n }\n\n isExpired(): boolean {\n if (!this.session) return true;\n return Date.now() >= this.session.expiresAt;\n }\n\n async refreshSession(): Promise<void> {\n if (!this.session?.refreshToken || !this.refreshFunction) {\n return;\n }\n\n // Collapse concurrent refresh calls into a single request\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.executeRefresh(this.session.refreshToken);\n\n try {\n await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n onAuthStateChange(callback: AuthStateCallback): Unsubscribe {\n this.listeners.add(callback);\n return () => {\n this.listeners.delete(callback);\n };\n }\n\n private async executeRefresh(refreshToken: string): Promise<void> {\n if (!this.refreshFunction) return;\n const newSession = await this.refreshFunction(refreshToken);\n this.setSession(newSession);\n }\n\n private notify(event: 'SESSION_SET' | 'SESSION_CLEARED', session: Session | null): void {\n for (const listener of this.listeners) {\n listener(event, session);\n }\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -75,8 +75,26 @@ declare class HttpClient {
75
75
  protected readonly apiKey: string;
76
76
  protected readonly options?: HttpClientOptions;
77
77
  tokenManager: TokenManager | null;
78
+ /**
79
+ * Admin JWT used for platform admin endpoints (/admin/*).
80
+ * When set, takes precedence over serviceRoleKey and tokenManager access
81
+ * token in the Authorization header.
82
+ */
83
+ adminToken: string | null;
78
84
  private readonly interceptors;
79
85
  constructor(apiKey: string, options?: HttpClientOptions);
86
+ /** Set (or clear) the admin JWT used on admin endpoints. */
87
+ setAdminToken(token: string | null): void;
88
+ /**
89
+ * Create a scoped HttpClient that adds the given extra headers to every
90
+ * request. The returned client shares the admin token and token manager
91
+ * with the parent at runtime — later changes on the parent propagate to
92
+ * the scope and vice versa.
93
+ *
94
+ * Typical use: tagging admin calls with `x-palbase-project: <ref>` so the
95
+ * gateway can route them to the correct project's data plane.
96
+ */
97
+ withHeaders(extra: Record<string, string>): HttpClient;
80
98
  /** Add a request interceptor. Runs before every request. */
81
99
  addInterceptor(interceptor: RequestInterceptor): void;
82
100
  request<T>(method: string, path: string, options?: RequestOptions): Promise<PalbaseResponse<T>>;
package/dist/index.d.ts CHANGED
@@ -75,8 +75,26 @@ declare class HttpClient {
75
75
  protected readonly apiKey: string;
76
76
  protected readonly options?: HttpClientOptions;
77
77
  tokenManager: TokenManager | null;
78
+ /**
79
+ * Admin JWT used for platform admin endpoints (/admin/*).
80
+ * When set, takes precedence over serviceRoleKey and tokenManager access
81
+ * token in the Authorization header.
82
+ */
83
+ adminToken: string | null;
78
84
  private readonly interceptors;
79
85
  constructor(apiKey: string, options?: HttpClientOptions);
86
+ /** Set (or clear) the admin JWT used on admin endpoints. */
87
+ setAdminToken(token: string | null): void;
88
+ /**
89
+ * Create a scoped HttpClient that adds the given extra headers to every
90
+ * request. The returned client shares the admin token and token manager
91
+ * with the parent at runtime — later changes on the parent propagate to
92
+ * the scope and vice versa.
93
+ *
94
+ * Typical use: tagging admin calls with `x-palbase-project: <ref>` so the
95
+ * gateway can route them to the correct project's data plane.
96
+ */
97
+ withHeaders(extra: Record<string, string>): HttpClient;
80
98
  /** Add a request interceptor. Runs before every request. */
81
99
  addInterceptor(interceptor: RequestInterceptor): void;
82
100
  request<T>(method: string, path: string, options?: RequestOptions): Promise<PalbaseResponse<T>>;
package/dist/index.js CHANGED
@@ -52,15 +52,51 @@ function parseProjectRef(apiKey) {
52
52
  }
53
53
  var MAX_RETRIES = 3;
54
54
  var INITIAL_BACKOFF_MS = 200;
55
- var HttpClient = class {
55
+ var HttpClient = class _HttpClient {
56
56
  apiKey;
57
57
  options;
58
58
  tokenManager = null;
59
+ /**
60
+ * Admin JWT used for platform admin endpoints (/admin/*).
61
+ * When set, takes precedence over serviceRoleKey and tokenManager access
62
+ * token in the Authorization header.
63
+ */
64
+ adminToken = null;
59
65
  interceptors = [];
60
66
  constructor(apiKey, options) {
61
67
  this.apiKey = apiKey;
62
68
  this.options = options;
63
69
  }
70
+ /** Set (or clear) the admin JWT used on admin endpoints. */
71
+ setAdminToken(token) {
72
+ this.adminToken = token;
73
+ }
74
+ /**
75
+ * Create a scoped HttpClient that adds the given extra headers to every
76
+ * request. The returned client shares the admin token and token manager
77
+ * with the parent at runtime — later changes on the parent propagate to
78
+ * the scope and vice versa.
79
+ *
80
+ * Typical use: tagging admin calls with `x-palbase-project: <ref>` so the
81
+ * gateway can route them to the correct project's data plane.
82
+ */
83
+ withHeaders(extra) {
84
+ const mergedHeaders = { ...this.options?.headers ?? {}, ...extra };
85
+ const parent = this;
86
+ const scoped = new _HttpClient(this.apiKey, {
87
+ ...this.options,
88
+ headers: mergedHeaders
89
+ });
90
+ scoped.tokenManager = this.tokenManager;
91
+ Object.defineProperty(scoped, "adminToken", {
92
+ get: () => parent.adminToken,
93
+ set: (v) => {
94
+ parent.adminToken = v;
95
+ },
96
+ configurable: true
97
+ });
98
+ return scoped;
99
+ }
64
100
  /** Add a request interceptor. Runs before every request. */
65
101
  addInterceptor(interceptor) {
66
102
  this.interceptors.push(interceptor);
@@ -87,9 +123,11 @@ var HttpClient = class {
87
123
  }
88
124
  buildHeaders(options) {
89
125
  const headers = {
90
- "apikey": this.apiKey,
91
126
  "Content-Type": "application/json"
92
127
  };
128
+ if (this.apiKey) {
129
+ headers["apikey"] = this.apiKey;
130
+ }
93
131
  const token = this.tokenManager?.getAccessToken();
94
132
  if (token) {
95
133
  headers["Authorization"] = `Bearer ${token}`;
@@ -97,6 +135,9 @@ var HttpClient = class {
97
135
  if (this.options?.serviceRoleKey) {
98
136
  headers["Authorization"] = `Bearer ${this.options.serviceRoleKey}`;
99
137
  }
138
+ if (this.adminToken) {
139
+ headers["Authorization"] = `Bearer ${this.adminToken}`;
140
+ }
100
141
  if (this.options?.headers) {
101
142
  Object.assign(headers, this.options.headers);
102
143
  }
@@ -146,7 +187,7 @@ var HttpClient = class {
146
187
  let data = null;
147
188
  let errorBody;
148
189
  const contentType = response.headers.get("Content-Type");
149
- if (contentType?.includes("json")) {
190
+ if (method !== "HEAD" && contentType?.includes("json")) {
150
191
  const body = await response.json();
151
192
  if (response.ok) {
152
193
  data = body;
@@ -166,7 +207,26 @@ var HttpClient = class {
166
207
  status: response.status
167
208
  };
168
209
  }
169
- return { data, error: null, status: response.status };
210
+ const contentRange = response.headers.get("Content-Range");
211
+ let count;
212
+ if (contentRange) {
213
+ const slash = contentRange.lastIndexOf("/");
214
+ if (slash >= 0) {
215
+ const totalPart = contentRange.slice(slash + 1);
216
+ if (totalPart !== "*") {
217
+ const parsed = Number.parseInt(totalPart, 10);
218
+ if (!Number.isNaN(parsed)) {
219
+ count = parsed;
220
+ }
221
+ }
222
+ }
223
+ }
224
+ return {
225
+ data,
226
+ error: null,
227
+ status: response.status,
228
+ ...count !== void 0 ? { count } : {}
229
+ };
170
230
  }
171
231
  delay(ms) {
172
232
  return new Promise((resolve) => setTimeout(resolve, ms));
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config.ts","../src/errors.ts","../src/http.ts","../src/platform.ts","../src/token.ts"],"sourcesContent":["import type { HttpClient } from './http.js';\nimport type { ProjectConfig } from './types.js';\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\nexport class ConfigFetcher {\n protected readonly httpClient: HttpClient;\n private cachedConfig: ProjectConfig | null = null;\n private cacheTimestamp = 0;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n async getConfig(): Promise<ProjectConfig | null> {\n const now = Date.now();\n\n if (this.cachedConfig && now - this.cacheTimestamp < CACHE_TTL_MS) {\n return this.cachedConfig;\n }\n\n try {\n const response = await this.httpClient.request<ProjectConfig>('GET', '/v1/config');\n\n if (response.error || !response.data) {\n return null;\n }\n\n this.cachedConfig = response.data;\n this.cacheTimestamp = now;\n\n return this.cachedConfig;\n } catch {\n return null;\n }\n }\n}\n","export class PalbaseError extends Error {\n readonly code: string;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(code: string, message: string, status: number, details?: unknown) {\n super(message);\n this.name = 'PalbaseError';\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n","import { PalbaseError } from './errors.js';\nimport type { TokenManager } from './token.js';\nimport type { HttpClientOptions, PalbaseResponse, RequestOptions } from './types.js';\n\nconst PALBASE_DOMAIN = 'palbase.studio';\n\n/**\n * Parse project ref from an API key.\n * Format: pb_{ref}_{random}\n * Example: pb_abc12345_xxxxxxxxxxxxxxxxxxxxxxxx\n * Returns the ref segment or null if the key doesn't match.\n */\nfunction parseProjectRef(apiKey: string): string | null {\n const parts = apiKey.split('_');\n // Format: [\"pb\", ref, random]\n const ref = parts[1];\n if (parts.length >= 3 && parts[0] === 'pb' && ref) {\n return ref;\n }\n return null;\n}\nconst MAX_RETRIES = 3;\nconst INITIAL_BACKOFF_MS = 200;\n\n/**\n * Request interceptor. Runs before every HTTP request.\n * Can modify headers, body, or reject the request.\n */\nexport interface RequestInterceptor {\n (request: { headers: Record<string, string>; method: string; path: string }): void | Promise<void>;\n}\n\nexport class HttpClient {\n protected readonly apiKey: string;\n protected readonly options?: HttpClientOptions;\n\n tokenManager: TokenManager | null = null;\n\n private readonly interceptors: RequestInterceptor[] = [];\n\n constructor(apiKey: string, options?: HttpClientOptions) {\n this.apiKey = apiKey;\n this.options = options;\n }\n\n /** Add a request interceptor. Runs before every request. */\n addInterceptor(interceptor: RequestInterceptor): void {\n this.interceptors.push(interceptor);\n }\n\n async request<T>(\n method: string,\n path: string,\n options?: RequestOptions,\n ): Promise<PalbaseResponse<T>> {\n // If token is expired and refresh is available, refresh before making the request\n if (this.tokenManager?.isExpired() && this.tokenManager.getRefreshToken() && this.tokenManager.refreshFunction) {\n await this.tokenManager.refreshSession();\n }\n\n return this.executeWithRetry<T>(method, path, options, 0);\n }\n\n private getBaseUrl(): string {\n // Explicit URL always wins (local dev, testing)\n if (this.options?.url) {\n return this.options.url;\n }\n\n // Resolve from API key (format: pb_{ref}_{random})\n const ref = parseProjectRef(this.apiKey);\n if (ref) {\n return `https://${ref}.${PALBASE_DOMAIN}`;\n }\n\n throw new PalbaseError(\n 'invalid_api_key',\n 'Invalid API key format. Expected: pb_{ref}_{random}. Provide an explicit url option for custom endpoints.',\n 0,\n );\n }\n\n private buildHeaders(options?: RequestOptions): Record<string, string> {\n const headers: Record<string, string> = {\n 'apikey': this.apiKey,\n 'Content-Type': 'application/json',\n };\n\n // Add token if available\n const token = this.tokenManager?.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n // serviceRoleKey overrides token-based Authorization\n if (this.options?.serviceRoleKey) {\n headers['Authorization'] = `Bearer ${this.options.serviceRoleKey}`;\n }\n\n // Merge global custom headers\n if (this.options?.headers) {\n Object.assign(headers, this.options.headers);\n }\n\n // Merge per-request headers\n if (options?.headers) {\n Object.assign(headers, options.headers);\n }\n\n return headers;\n }\n\n private async executeWithRetry<T>(\n method: string,\n path: string,\n options: RequestOptions | undefined,\n attempt: number,\n ): Promise<PalbaseResponse<T>> {\n const url = `${this.getBaseUrl()}${path}`;\n const headers = this.buildHeaders(options);\n\n // Run interceptors\n for (const interceptor of this.interceptors) {\n await interceptor({ headers, method, path });\n }\n\n const fetchOptions: RequestInit = {\n method,\n headers,\n signal: options?.signal,\n };\n\n if (options?.body !== undefined) {\n fetchOptions.body = JSON.stringify(options.body);\n }\n\n let response: Response;\n try {\n response = await fetch(url, fetchOptions);\n } catch (error) {\n // Network error — retry with backoff\n if (attempt < MAX_RETRIES - 1) {\n const backoff = INITIAL_BACKOFF_MS * 2 ** attempt;\n await this.delay(backoff);\n return this.executeWithRetry<T>(method, path, options, attempt + 1);\n }\n\n // All retries exhausted — throw PalbaseError\n throw new PalbaseError(\n 'network_error',\n error instanceof Error ? error.message : 'Network request failed',\n 0,\n );\n }\n\n // Handle 429 Too Many Requests — retry with Retry-After or backoff;\n // if retries exhausted, fall through to normal error response handling below\n if (response.status === 429) {\n if (attempt < MAX_RETRIES - 1) {\n const retryAfter = response.headers.get('Retry-After');\n const parsed = retryAfter ? Number.parseInt(retryAfter, 10) : Number.NaN;\n const delayMs = Number.isNaN(parsed) ? INITIAL_BACKOFF_MS * 2 ** attempt : parsed * 1000;\n await this.delay(delayMs);\n return this.executeWithRetry<T>(method, path, options, attempt + 1);\n }\n }\n\n // Parse response body\n let data: T | null = null;\n let errorBody: { error?: string; error_description?: string; status?: number } | undefined;\n\n const contentType = response.headers.get('Content-Type');\n if (contentType?.includes('json')) {\n const body = await response.json() as Record<string, unknown>;\n if (response.ok) {\n data = body as T;\n } else {\n errorBody = body as typeof errorBody;\n }\n }\n\n if (!response.ok) {\n return {\n data: null,\n error: new PalbaseError(\n errorBody?.error ?? 'unknown_error',\n errorBody?.error_description ?? response.statusText,\n response.status,\n errorBody,\n ),\n status: response.status,\n };\n }\n\n return { data, error: null, status: response.status };\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","export type Platform = 'browser' | 'node' | 'react-native' | 'deno' | 'bun';\n\ndeclare const Deno: unknown;\ndeclare const process: { versions: Record<string, string> } | undefined;\n\nexport function detectPlatform(): Platform {\n if (typeof Deno !== 'undefined') {\n return 'deno';\n }\n\n if (process?.versions) {\n if ('bun' in process.versions) {\n return 'bun';\n }\n if ('node' in process.versions) {\n return 'node';\n }\n }\n\n if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {\n return 'react-native';\n }\n\n return 'browser';\n}\n","import type { AuthStateCallback, Session, Unsubscribe } from './types.js';\n\nexport class TokenManager {\n private session: Session | null = null;\n private listeners: Set<AuthStateCallback> = new Set();\n private refreshPromise: Promise<void> | null = null;\n\n refreshFunction: ((refreshToken: string) => Promise<Session>) | null = null;\n\n setSession(session: Session): void {\n this.session = session;\n this.notify('SESSION_SET', session);\n }\n\n getAccessToken(): string | null {\n return this.session?.accessToken ?? null;\n }\n\n getRefreshToken(): string | null {\n return this.session?.refreshToken ?? null;\n }\n\n clearSession(): void {\n this.session = null;\n this.notify('SESSION_CLEARED', null);\n }\n\n isExpired(): boolean {\n if (!this.session) return true;\n return Date.now() >= this.session.expiresAt;\n }\n\n async refreshSession(): Promise<void> {\n if (!this.session?.refreshToken || !this.refreshFunction) {\n return;\n }\n\n // Collapse concurrent refresh calls into a single request\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.executeRefresh(this.session.refreshToken);\n\n try {\n await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n onAuthStateChange(callback: AuthStateCallback): Unsubscribe {\n this.listeners.add(callback);\n return () => {\n this.listeners.delete(callback);\n };\n }\n\n private async executeRefresh(refreshToken: string): Promise<void> {\n if (!this.refreshFunction) return;\n const newSession = await this.refreshFunction(refreshToken);\n this.setSession(newSession);\n }\n\n private notify(event: 'SESSION_SET' | 'SESSION_CLEARED', session: Session | null): void {\n for (const listener of this.listeners) {\n listener(event, session);\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/config.ts","../src/errors.ts","../src/http.ts","../src/platform.ts","../src/token.ts"],"sourcesContent":["import type { HttpClient } from './http.js';\nimport type { ProjectConfig } from './types.js';\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\nexport class ConfigFetcher {\n protected readonly httpClient: HttpClient;\n private cachedConfig: ProjectConfig | null = null;\n private cacheTimestamp = 0;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n async getConfig(): Promise<ProjectConfig | null> {\n const now = Date.now();\n\n if (this.cachedConfig && now - this.cacheTimestamp < CACHE_TTL_MS) {\n return this.cachedConfig;\n }\n\n try {\n const response = await this.httpClient.request<ProjectConfig>('GET', '/v1/config');\n\n if (response.error || !response.data) {\n return null;\n }\n\n this.cachedConfig = response.data;\n this.cacheTimestamp = now;\n\n return this.cachedConfig;\n } catch {\n return null;\n }\n }\n}\n","export class PalbaseError extends Error {\n readonly code: string;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(code: string, message: string, status: number, details?: unknown) {\n super(message);\n this.name = 'PalbaseError';\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n","import { PalbaseError } from './errors.js';\nimport type { TokenManager } from './token.js';\nimport type { HttpClientOptions, PalbaseResponse, RequestOptions } from './types.js';\n\nconst PALBASE_DOMAIN = 'palbase.studio';\n\n/**\n * Parse project ref from an API key.\n * Format: pb_{ref}_{random}\n * Example: pb_abc12345_xxxxxxxxxxxxxxxxxxxxxxxx\n * Returns the ref segment or null if the key doesn't match.\n */\nfunction parseProjectRef(apiKey: string): string | null {\n const parts = apiKey.split('_');\n // Format: [\"pb\", ref, random]\n const ref = parts[1];\n if (parts.length >= 3 && parts[0] === 'pb' && ref) {\n return ref;\n }\n return null;\n}\nconst MAX_RETRIES = 3;\nconst INITIAL_BACKOFF_MS = 200;\n\n/**\n * Request interceptor. Runs before every HTTP request.\n * Can modify headers, body, or reject the request.\n */\nexport interface RequestInterceptor {\n (request: { headers: Record<string, string>; method: string; path: string }): void | Promise<void>;\n}\n\nexport class HttpClient {\n protected readonly apiKey: string;\n protected readonly options?: HttpClientOptions;\n\n tokenManager: TokenManager | null = null;\n\n /**\n * Admin JWT used for platform admin endpoints (/admin/*).\n * When set, takes precedence over serviceRoleKey and tokenManager access\n * token in the Authorization header.\n */\n adminToken: string | null = null;\n\n private readonly interceptors: RequestInterceptor[] = [];\n\n constructor(apiKey: string, options?: HttpClientOptions) {\n this.apiKey = apiKey;\n this.options = options;\n }\n\n /** Set (or clear) the admin JWT used on admin endpoints. */\n setAdminToken(token: string | null): void {\n this.adminToken = token;\n }\n\n /**\n * Create a scoped HttpClient that adds the given extra headers to every\n * request. The returned client shares the admin token and token manager\n * with the parent at runtime — later changes on the parent propagate to\n * the scope and vice versa.\n *\n * Typical use: tagging admin calls with `x-palbase-project: <ref>` so the\n * gateway can route them to the correct project's data plane.\n */\n withHeaders(extra: Record<string, string>): HttpClient {\n const mergedHeaders = { ...(this.options?.headers ?? {}), ...extra };\n const parent = this;\n const scoped: HttpClient = new HttpClient(this.apiKey, {\n ...this.options,\n headers: mergedHeaders,\n });\n scoped.tokenManager = this.tokenManager;\n // Delegate adminToken reads + writes to the parent so the scope always\n // sees the latest token, and setAdminToken on the scope affects the parent.\n Object.defineProperty(scoped, 'adminToken', {\n get: () => parent.adminToken,\n set: (v: string | null) => {\n parent.adminToken = v;\n },\n configurable: true,\n });\n return scoped;\n }\n\n /** Add a request interceptor. Runs before every request. */\n addInterceptor(interceptor: RequestInterceptor): void {\n this.interceptors.push(interceptor);\n }\n\n async request<T>(\n method: string,\n path: string,\n options?: RequestOptions,\n ): Promise<PalbaseResponse<T>> {\n // If token is expired and refresh is available, refresh before making the request\n if (this.tokenManager?.isExpired() && this.tokenManager.getRefreshToken() && this.tokenManager.refreshFunction) {\n await this.tokenManager.refreshSession();\n }\n\n return this.executeWithRetry<T>(method, path, options, 0);\n }\n\n private getBaseUrl(): string {\n // Explicit URL always wins (local dev, testing)\n if (this.options?.url) {\n return this.options.url;\n }\n\n // Resolve from API key (format: pb_{ref}_{random})\n const ref = parseProjectRef(this.apiKey);\n if (ref) {\n return `https://${ref}.${PALBASE_DOMAIN}`;\n }\n\n throw new PalbaseError(\n 'invalid_api_key',\n 'Invalid API key format. Expected: pb_{ref}_{random}. Provide an explicit url option for custom endpoints.',\n 0,\n );\n }\n\n private buildHeaders(options?: RequestOptions): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n // apikey header only when an API key is configured (platform admin flows\n // may omit it — e.g. setup/login before any project exists).\n if (this.apiKey) {\n headers['apikey'] = this.apiKey;\n }\n\n // Add token if available\n const token = this.tokenManager?.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n // serviceRoleKey overrides token-based Authorization\n if (this.options?.serviceRoleKey) {\n headers['Authorization'] = `Bearer ${this.options.serviceRoleKey}`;\n }\n\n // adminToken (platform admin JWT) takes precedence over everything else\n if (this.adminToken) {\n headers['Authorization'] = `Bearer ${this.adminToken}`;\n }\n\n // Merge global custom headers\n if (this.options?.headers) {\n Object.assign(headers, this.options.headers);\n }\n\n // Merge per-request headers\n if (options?.headers) {\n Object.assign(headers, options.headers);\n }\n\n return headers;\n }\n\n private async executeWithRetry<T>(\n method: string,\n path: string,\n options: RequestOptions | undefined,\n attempt: number,\n ): Promise<PalbaseResponse<T>> {\n const url = `${this.getBaseUrl()}${path}`;\n const headers = this.buildHeaders(options);\n\n // Run interceptors\n for (const interceptor of this.interceptors) {\n await interceptor({ headers, method, path });\n }\n\n const fetchOptions: RequestInit = {\n method,\n headers,\n signal: options?.signal,\n };\n\n if (options?.body !== undefined) {\n fetchOptions.body = JSON.stringify(options.body);\n }\n\n let response: Response;\n try {\n response = await fetch(url, fetchOptions);\n } catch (error) {\n // Network error — retry with backoff\n if (attempt < MAX_RETRIES - 1) {\n const backoff = INITIAL_BACKOFF_MS * 2 ** attempt;\n await this.delay(backoff);\n return this.executeWithRetry<T>(method, path, options, attempt + 1);\n }\n\n // All retries exhausted — throw PalbaseError\n throw new PalbaseError(\n 'network_error',\n error instanceof Error ? error.message : 'Network request failed',\n 0,\n );\n }\n\n // Handle 429 Too Many Requests — retry with Retry-After or backoff;\n // if retries exhausted, fall through to normal error response handling below\n if (response.status === 429) {\n if (attempt < MAX_RETRIES - 1) {\n const retryAfter = response.headers.get('Retry-After');\n const parsed = retryAfter ? Number.parseInt(retryAfter, 10) : Number.NaN;\n const delayMs = Number.isNaN(parsed) ? INITIAL_BACKOFF_MS * 2 ** attempt : parsed * 1000;\n await this.delay(delayMs);\n return this.executeWithRetry<T>(method, path, options, attempt + 1);\n }\n }\n\n // Parse response body\n let data: T | null = null;\n let errorBody: { error?: string; error_description?: string; status?: number } | undefined;\n\n // HEAD responses have no body by spec — skip parsing.\n const contentType = response.headers.get('Content-Type');\n if (method !== 'HEAD' && contentType?.includes('json')) {\n const body = await response.json() as Record<string, unknown>;\n if (response.ok) {\n data = body as T;\n } else {\n errorBody = body as typeof errorBody;\n }\n }\n\n if (!response.ok) {\n return {\n data: null,\n error: new PalbaseError(\n errorBody?.error ?? 'unknown_error',\n errorBody?.error_description ?? response.statusText,\n response.status,\n errorBody,\n ),\n status: response.status,\n };\n }\n\n // Parse PostgREST Content-Range for count queries (e.g. \"0-9/42\" or \"*/42\").\n const contentRange = response.headers.get('Content-Range');\n let count: number | undefined;\n if (contentRange) {\n const slash = contentRange.lastIndexOf('/');\n if (slash >= 0) {\n const totalPart = contentRange.slice(slash + 1);\n if (totalPart !== '*') {\n const parsed = Number.parseInt(totalPart, 10);\n if (!Number.isNaN(parsed)) {\n count = parsed;\n }\n }\n }\n }\n\n return {\n data,\n error: null,\n status: response.status,\n ...(count !== undefined ? { count } : {}),\n };\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","export type Platform = 'browser' | 'node' | 'react-native' | 'deno' | 'bun';\n\ndeclare const Deno: unknown;\ndeclare const process: { versions: Record<string, string> } | undefined;\n\nexport function detectPlatform(): Platform {\n if (typeof Deno !== 'undefined') {\n return 'deno';\n }\n\n if (process?.versions) {\n if ('bun' in process.versions) {\n return 'bun';\n }\n if ('node' in process.versions) {\n return 'node';\n }\n }\n\n if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {\n return 'react-native';\n }\n\n return 'browser';\n}\n","import type { AuthStateCallback, Session, Unsubscribe } from './types.js';\n\nexport class TokenManager {\n private session: Session | null = null;\n private listeners: Set<AuthStateCallback> = new Set();\n private refreshPromise: Promise<void> | null = null;\n\n refreshFunction: ((refreshToken: string) => Promise<Session>) | null = null;\n\n setSession(session: Session): void {\n this.session = session;\n this.notify('SESSION_SET', session);\n }\n\n getAccessToken(): string | null {\n return this.session?.accessToken ?? null;\n }\n\n getRefreshToken(): string | null {\n return this.session?.refreshToken ?? null;\n }\n\n clearSession(): void {\n this.session = null;\n this.notify('SESSION_CLEARED', null);\n }\n\n isExpired(): boolean {\n if (!this.session) return true;\n return Date.now() >= this.session.expiresAt;\n }\n\n async refreshSession(): Promise<void> {\n if (!this.session?.refreshToken || !this.refreshFunction) {\n return;\n }\n\n // Collapse concurrent refresh calls into a single request\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.executeRefresh(this.session.refreshToken);\n\n try {\n await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n onAuthStateChange(callback: AuthStateCallback): Unsubscribe {\n this.listeners.add(callback);\n return () => {\n this.listeners.delete(callback);\n };\n }\n\n private async executeRefresh(refreshToken: string): Promise<void> {\n if (!this.refreshFunction) return;\n const newSession = await this.refreshFunction(refreshToken);\n this.setSession(newSession);\n }\n\n private notify(event: 'SESSION_SET' | 'SESSION_CLEARED', session: Session | null): void {\n for (const listener of this.listeners) {\n listener(event, session);\n }\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@palbase/core",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Shared core for Palbase SDK — HTTP client, error types, token management",
5
5
  "license": "MIT",
6
6
  "repository": {