@tinycloud/sdk-services 3.1.0-beta.2 → 3.1.0-beta.7

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/sql/index.ts","../../src/types.ts","../../src/errors.ts","../../src/base/BaseService.ts","../../src/responseErrors.ts","../../src/sql/SQLMigrations.ts","../../src/sql/DatabaseHandle.ts","../../src/sql/types.ts","../../src/sql/SQLService.ts"],"sourcesContent":["/**\n * SQL Service Module\n *\n * Provides SQL database operations for TinyCloud.\n */\n\nexport { SQLService } from \"./SQLService\";\nexport { DatabaseHandle } from \"./DatabaseHandle\";\nexport { SQLMigrations } from \"./SQLMigrations\";\nexport type { ISQLService, IDatabaseHandle, ISQLMigrations } from \"./ISQLService\";\nexport {\n SQLAction,\n type SQLActionType,\n type SQLServiceConfig,\n type QueryOptions,\n type ExecuteOptions,\n type BatchOptions,\n type SqlValue,\n type SqlStatement,\n type QueryResponse,\n type ExecuteResponse,\n type BatchResponse,\n type SqlMigration,\n type SqlMigrationApplyOptions,\n type SqlMigrationApplyResponse,\n} from \"./types\";\n","/**\n * SDK Services - Core Types\n *\n * These types define the service architecture for TinyCloud SDK.\n * Services use dependency injection via IServiceContext for platform independence.\n */\n\n// =============================================================================\n// Result Type Pattern\n// =============================================================================\n\n/**\n * Result type for service operations.\n * Services return Result instead of throwing, making error handling explicit.\n *\n * @template T - The success data type\n * @template E - The error type (defaults to ServiceError)\n *\n * @example\n * ```typescript\n * const result = await kv.get('key');\n * if (result.ok) {\n * console.log(result.data);\n * } else {\n * console.error(result.error.code);\n * }\n * ```\n */\nexport type Result<T, E = ServiceError> =\n | { ok: true; data: T }\n | { ok: false; error: E };\n\n/**\n * Service error with structured information.\n */\nexport interface ServiceError {\n /** Error code for programmatic handling (e.g., 'KV_NOT_FOUND', 'AUTH_EXPIRED') */\n code: string;\n /** Human-readable error message */\n message: string;\n /** Service that produced the error (e.g., 'kv', 'sql') */\n service: string;\n /** Original error if this wraps another error */\n cause?: Error;\n /** Additional metadata about the error */\n meta?: Record<string, unknown>;\n}\n\n/**\n * A node-supplied permission hint after strict SDK validation. This is\n * deliberately narrower than a general capability: no wildcards, caveats,\n * descriptions, or transport metadata can cross the service boundary.\n */\nexport interface PermissionHint {\n readonly service: \"tinycloud.kv\" | \"tinycloud.encryption\";\n readonly space?: string;\n readonly path: string;\n readonly actions: readonly string[];\n}\n\n/**\n * Storage quota information returned with quota-related errors.\n */\nexport interface StorageQuotaInfo {\n usedBytes: number;\n limitBytes: number;\n service: string;\n}\n\n/**\n * Standard error codes used across services.\n */\nexport const ErrorCodes = {\n // Common errors\n NOT_FOUND: \"NOT_FOUND\",\n AUTH_EXPIRED: \"AUTH_EXPIRED\",\n AUTH_REQUIRED: \"AUTH_REQUIRED\",\n AUTH_UNAUTHORIZED: \"AUTH_UNAUTHORIZED\",\n NETWORK_ERROR: \"NETWORK_ERROR\",\n TIMEOUT: \"TIMEOUT\",\n ABORTED: \"ABORTED\",\n INVALID_INPUT: \"INVALID_INPUT\",\n PERMISSION_DENIED: \"PERMISSION_DENIED\",\n\n // KV-specific errors\n KV_NOT_FOUND: \"KV_NOT_FOUND\",\n KV_WRITE_FAILED: \"KV_WRITE_FAILED\",\n KV_PRECONDITION_FAILED: \"KV_PRECONDITION_FAILED\",\n KV_CONFLICT: \"KV_CONFLICT\",\n KV_RESPONSE_TOO_LARGE: \"KV_RESPONSE_TOO_LARGE\",\n\n // SQL-specific errors\n SQL_ERROR: \"SQL_ERROR\",\n SQL_PERMISSION_DENIED: \"SQL_PERMISSION_DENIED\",\n SQL_DATABASE_NOT_FOUND: \"SQL_DATABASE_NOT_FOUND\",\n SQL_RESPONSE_TOO_LARGE: \"SQL_RESPONSE_TOO_LARGE\",\n SQL_QUOTA_EXCEEDED: \"SQL_QUOTA_EXCEEDED\",\n SQL_INVALID_STATEMENT: \"SQL_INVALID_STATEMENT\",\n SQL_SCHEMA_ERROR: \"SQL_SCHEMA_ERROR\",\n SQL_READONLY_VIOLATION: \"SQL_READONLY_VIOLATION\",\n\n // Storage quota errors\n STORAGE_QUOTA_EXCEEDED: \"STORAGE_QUOTA_EXCEEDED\",\n STORAGE_LIMIT_REACHED: \"STORAGE_LIMIT_REACHED\",\n\n // DuckDB-specific errors\n DUCKDB_ERROR: \"DUCKDB_ERROR\",\n DUCKDB_PERMISSION_DENIED: \"DUCKDB_PERMISSION_DENIED\",\n DUCKDB_DATABASE_NOT_FOUND: \"DUCKDB_DATABASE_NOT_FOUND\",\n DUCKDB_RESPONSE_TOO_LARGE: \"DUCKDB_RESPONSE_TOO_LARGE\",\n DUCKDB_QUOTA_EXCEEDED: \"DUCKDB_QUOTA_EXCEEDED\",\n DUCKDB_INVALID_STATEMENT: \"DUCKDB_INVALID_STATEMENT\",\n DUCKDB_SCHEMA_ERROR: \"DUCKDB_SCHEMA_ERROR\",\n DUCKDB_READONLY_VIOLATION: \"DUCKDB_READONLY_VIOLATION\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\n// =============================================================================\n// Service Session\n// =============================================================================\n\n/**\n * Session data required for authenticated service operations.\n * Both TinyCloudSession and web-sdk Session can be cast to this interface.\n */\nexport interface ServiceSession {\n /** The delegation header containing the UCAN */\n delegationHeader: { Authorization: string };\n /** The delegation CID */\n delegationCid: string;\n /** The space ID for this session */\n spaceId: string;\n /** The verification method DID */\n verificationMethod: string;\n /** The session key JWK (required for invoke) */\n jwk: object;\n}\n\n// =============================================================================\n// Platform Dependencies (Injected)\n// =============================================================================\n\n/**\n * Headers type - compatible with both browser and Node.js.\n */\nexport type ServiceHeaders = Record<string, string> | [string, string][];\n\n/**\n * A single fact object to include in the UCAN invocation.\n * Facts are key-value objects that the server reads from the UCAN facts field.\n */\nexport interface InvocationFact {\n [key: string]: unknown;\n}\n\n/**\n * Facts to include in the UCAN invocation.\n * This is an array of fact objects per the UCAN spec.\n * Used to pass additional parameters that the server reads from the UCAN facts field.\n */\nexport type InvocationFacts = InvocationFact[];\n\n/**\n * Invoke function signature - platform-specific implementation injected via DI.\n * Both node-sdk-wasm and web-sdk-wasm export this with identical signature.\n *\n * @param session - The service session with delegation data\n * @param service - Service name (e.g., \"kv\")\n * @param path - Resource path or key\n * @param action - Action to perform (e.g., \"tinycloud.kv/get\")\n * @param facts - Optional facts to include in the UCAN (e.g., for capabilities/read params)\n * @returns Headers to include in the request\n */\nexport type InvokeFunction = (\n session: ServiceSession,\n service: string,\n path: string,\n action: string,\n facts?: InvocationFacts,\n) => ServiceHeaders;\n\n/**\n * Multi-resource invocation entry.\n */\nexport interface InvokeAnyEntry {\n /**\n * Legacy space-scoped resource. Optional when `resource` is provided.\n */\n spaceId?: string;\n service: string;\n path: string;\n action: string;\n /** Optional raw resource URI. When set, WASM signs this URI directly. */\n resource?: string;\n /** Capability caveats that must remain on this invocation attenuation. */\n caveats?: Record<string, unknown>[];\n}\n\n/**\n * Invoke function for minting a single authorization header that covers\n * multiple capabilities across one effective invoker.\n */\nexport type InvokeAnyFunction = (\n session: ServiceSession,\n entries: InvokeAnyEntry[],\n facts?: InvocationFacts,\n) => ServiceHeaders;\n\n/**\n * Fetch request options - compatible with standard fetch API.\n */\nexport interface FetchRequestInit {\n method?: string;\n headers?: ServiceHeaders;\n body?: Blob | FormData | string;\n signal?: AbortSignal;\n redirect?: \"error\";\n}\n\n/**\n * Fetch response interface - compatible with standard Response.\n */\nexport interface FetchResponse {\n ok: boolean;\n status: number;\n statusText: string;\n body?: unknown;\n headers: {\n get(name: string): string | null;\n };\n json(): Promise<unknown>;\n text(): Promise<string>;\n arrayBuffer(): Promise<ArrayBuffer>;\n blob(): Promise<Blob>;\n}\n\n/**\n * Fetch function signature - allows for custom fetch implementations.\n * Compatible with both browser fetch and Node.js fetch.\n */\nexport type FetchFunction = (\n url: string,\n init?: FetchRequestInit,\n) => Promise<FetchResponse>;\n\n// =============================================================================\n// Retry Policy\n// =============================================================================\n\n/**\n * Configuration for automatic retry of failed requests.\n */\nexport interface RetryPolicy {\n /** Maximum number of attempts (including initial) */\n maxAttempts: number;\n /** Backoff strategy between retries */\n backoff: \"none\" | \"linear\" | \"exponential\";\n /** Base delay in milliseconds for backoff calculation */\n baseDelayMs: number;\n /** Maximum delay in milliseconds between retries */\n maxDelayMs: number;\n /** Error codes that should trigger a retry */\n retryableErrors: string[];\n}\n\n/**\n * Default retry policy.\n */\nexport const defaultRetryPolicy: RetryPolicy = {\n maxAttempts: 3,\n backoff: \"exponential\",\n baseDelayMs: 1000,\n maxDelayMs: 10000,\n retryableErrors: [ErrorCodes.NETWORK_ERROR, ErrorCodes.TIMEOUT],\n};\n\n// =============================================================================\n// Service Context\n// =============================================================================\n\n/**\n * Event handler function type.\n */\nexport type EventHandler = (data: unknown) => void;\n\n/**\n * SDK telemetry event handler.\n */\nexport type TelemetryEventHandler = (event: string, data: unknown) => void;\n\n/**\n * Default-off telemetry configuration.\n */\nexport type TelemetryConfig =\n | boolean\n | {\n enabled?: boolean;\n onEvent?: TelemetryEventHandler;\n };\n\n/**\n * Service interface - base contract for all services.\n */\nexport interface IService {\n /** Initialize service with context */\n initialize(context: IServiceContext): void;\n\n /** Called when session changes (sign-in, sign-out, refresh) */\n onSessionChange(session: ServiceSession | null): void;\n\n /** Called when SDK signs out - should abort pending operations */\n onSignOut(): void;\n\n /** Service-specific configuration */\n readonly config: Record<string, unknown>;\n}\n\n/**\n * Context provided to services for accessing platform dependencies.\n * The SDK creates this context and passes it to services during initialization.\n */\nexport interface IServiceContext {\n // Session management\n /** Current active session, or null if not authenticated */\n readonly session: ServiceSession | null;\n /** Whether there is an active authenticated session */\n readonly isAuthenticated: boolean;\n\n // Platform dependencies (injected by SDK)\n /** Platform-specific invoke function from WASM binding */\n readonly invoke: InvokeFunction;\n /** Optional multi-resource invoke function */\n readonly invokeAny?: InvokeAnyFunction;\n /** Fetch function (defaults to globalThis.fetch) */\n readonly fetch: FetchFunction;\n /** Available TinyCloud host URLs */\n readonly hosts: string[];\n\n // Cross-service access\n /** Get another registered service by name */\n getService<T extends IService>(name: string): T | undefined;\n\n // Telemetry/Events\n /** Emit a telemetry event */\n emit(event: string, data: unknown): void;\n /** Subscribe to events */\n on(event: string, handler: EventHandler): () => void;\n\n // Lifecycle\n /** Abort signal that fires when SDK signs out */\n readonly abortSignal: AbortSignal;\n\n // Retry policy\n /** Retry policy for failed requests */\n readonly retryPolicy: RetryPolicy;\n}\n\n// =============================================================================\n// Telemetry Events\n// =============================================================================\n\n/**\n * Event emitted before a service request.\n */\nexport interface ServiceRequestEvent {\n service: string;\n action: string;\n span?: string;\n key?: string;\n timestamp: number;\n}\n\n/**\n * Event emitted after a service response.\n */\nexport interface ServiceResponseEvent {\n service: string;\n action: string;\n span?: string;\n ok: boolean;\n duration: number;\n durationMs?: number;\n status?: number;\n}\n\n/**\n * Event emitted on service error.\n */\nexport interface ServiceErrorEvent {\n service: string;\n span?: string;\n error: ServiceError;\n}\n\n/**\n * Event emitted on retry attempt.\n */\nexport interface ServiceRetryEvent {\n service: string;\n attempt: number;\n maxAttempts: number;\n error: ServiceError;\n}\n\n/**\n * Generic named span event for aggregating operation timings.\n */\nexport interface TelemetrySpanEvent {\n span: string;\n ok: boolean;\n durationMs: number;\n service?: string;\n action?: string;\n status?: number;\n error?: ServiceError;\n}\n\n/**\n * Telemetry event names.\n */\nexport const TelemetryEvents = {\n SPAN: \"telemetry.span\",\n SERVICE_REQUEST: \"service.request\",\n SERVICE_RESPONSE: \"service.response\",\n SERVICE_ERROR: \"service.error\",\n SERVICE_RETRY: \"service.retry\",\n SESSION_CHANGED: \"session.changed\",\n SESSION_EXPIRED: \"session.expired\",\n} as const;\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Create a success result.\n */\nexport function ok<T>(data: T): Result<T> {\n return { ok: true, data };\n}\n\n/**\n * Create an error result.\n */\nexport function err<E = ServiceError>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n\n/**\n * Create a ServiceError.\n */\nexport function serviceError(\n code: string,\n message: string,\n service: string,\n options?: { cause?: Error; meta?: Record<string, unknown> },\n): ServiceError {\n return {\n code,\n message,\n service,\n cause: options?.cause,\n meta: options?.meta,\n };\n}\n","/**\n * SDK Services - Error Utilities\n *\n * Utilities for creating and handling service errors.\n */\n\nimport {\n ServiceError,\n ErrorCodes,\n err,\n serviceError,\n type PermissionHint,\n} from \"./types\";\n\n/** Validate one structured node permission hint without accepting raw errors. */\nexport function parsePermissionHint(value: unknown): PermissionHint | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n const candidate = value as Record<string, unknown>;\n const keys = Object.keys(candidate).sort();\n if (keys.some((key) => ![\"actions\", \"path\", \"service\", \"space\"].includes(key))) return undefined;\n if (candidate.service !== \"tinycloud.kv\" && candidate.service !== \"tinycloud.encryption\") return undefined;\n if (typeof candidate.path !== \"string\" || candidate.path.length === 0 || candidate.path.includes(\"*\")) return undefined;\n if (candidate.service === \"tinycloud.kv\" &&\n (typeof candidate.space !== \"string\" || candidate.space.length === 0 || !candidate.path.startsWith(\"vault/\") || candidate.path.endsWith(\"/\"))) {\n return undefined;\n }\n if (candidate.service === \"tinycloud.encryption\" &&\n (candidate.space !== undefined || !candidate.path.startsWith(\"urn:tinycloud:encryption:\") || candidate.path.endsWith(\":\"))) {\n return undefined;\n }\n if (!Array.isArray(candidate.actions) || candidate.actions.length !== 1 ||\n typeof candidate.actions[0] !== \"string\" || candidate.actions[0].includes(\"*\")) return undefined;\n const expectedAction = candidate.service === \"tinycloud.kv\"\n ? \"tinycloud.kv/get\"\n : \"tinycloud.encryption/decrypt\";\n if (candidate.actions[0] !== expectedAction) return undefined;\n const space = typeof candidate.space === \"string\" ? candidate.space : undefined;\n return {\n service: candidate.service,\n ...(space === undefined ? {} : { space }),\n path: candidate.path,\n actions: [candidate.actions[0]],\n };\n}\n\n/** Extract only a validated structured hint from an SDK-owned error body. */\nexport function parsePermissionHintFromErrorText(text: string): PermissionHint | undefined {\n try {\n const parsed: unknown = JSON.parse(text);\n if (typeof parsed !== \"object\" || parsed === null) return undefined;\n const root = parsed as Record<string, unknown>;\n const nested = root.error;\n const nestedRecord = typeof nested === \"object\" && nested !== null\n ? nested as Record<string, unknown>\n : undefined;\n return parsePermissionHint(root.permissionHint) ??\n parsePermissionHint(nestedRecord?.permissionHint);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Create a service error for authentication required.\n */\nexport function authRequiredError(service: string): ServiceError {\n return {\n code: ErrorCodes.AUTH_REQUIRED,\n message: \"Authentication required. Please sign in first.\",\n service,\n };\n}\n\n/**\n * Create a service error for expired authentication.\n */\nexport function authExpiredError(service: string): ServiceError {\n return {\n code: ErrorCodes.AUTH_EXPIRED,\n message: \"Session has expired. Please sign in again.\",\n service,\n };\n}\n\n/**\n * Create a service error for network issues.\n */\nexport function networkError(\n service: string,\n message: string,\n cause?: Error\n): ServiceError {\n return {\n code: ErrorCodes.NETWORK_ERROR,\n message,\n service,\n cause,\n };\n}\n\n/**\n * Create a service error for timeouts.\n */\nexport function timeoutError(service: string): ServiceError {\n return {\n code: ErrorCodes.TIMEOUT,\n message: \"Request timed out.\",\n service,\n };\n}\n\n/**\n * Create a service error for aborted requests.\n */\nexport function abortedError(service: string): ServiceError {\n return {\n code: ErrorCodes.ABORTED,\n message: \"Request was aborted.\",\n service,\n };\n}\n\n/**\n * Create a service error for not found resources.\n */\nexport function notFoundError(\n service: string,\n resource: string\n): ServiceError {\n return {\n code: ErrorCodes.NOT_FOUND,\n message: `Resource not found: ${resource}`,\n service,\n };\n}\n\n/**\n * Create a service error for permission denied.\n */\nexport function permissionDeniedError(\n service: string,\n action: string\n): ServiceError {\n return {\n code: ErrorCodes.PERMISSION_DENIED,\n message: `Permission denied for action: ${action}`,\n service,\n };\n}\n\n/**\n * Parse the server's \"Unauthorized Action: {resource} / {ability}\" pattern.\n */\nexport function parseAuthError(responseText: string): { resource?: string; action?: string } {\n const match = responseText.match(/^Unauthorized Action:\\s*(.+?)\\s*\\/\\s*(tinycloud\\.\\S+)$/m);\n if (match) {\n return { resource: match[1].trim(), action: match[2].trim() };\n }\n return {};\n}\n\n/**\n * Create a service error for unauthorized action (missing capability).\n */\nexport function authUnauthorizedError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return serviceError(ErrorCodes.AUTH_UNAUTHORIZED, message, service, { meta });\n}\n\n/**\n * Authorization verdict read from an error's structure, never its message:\n * - `\"unauthenticated\"`: HTTP 401 — the session is missing or stale.\n * - `\"forbidden\"`: HTTP 403, or `AUTH_UNAUTHORIZED` without a status — the\n * session is valid but lacks the capability.\n * - `\"other\"`: a non-authorization HTTP error status (4xx/5xx).\n */\nexport type AuthorizationVerdict = \"unauthenticated\" | \"forbidden\" | \"other\";\n\nconst MAX_CAUSE_DEPTH = 8;\n\n/** The first 4xx/5xx among `status`, `statusCode`, `meta.status`. Success and\n * informational statuses (e.g. a 200 `Response` kept as `cause`) are not\n * failures, so they are skipped rather than ending the search. */\nfunction errorStatusOf(node: Record<string, unknown>): number | undefined {\n const meta = node.meta;\n const candidates = [\n node.status,\n node.statusCode,\n typeof meta === \"object\" && meta !== null\n ? (meta as Record<string, unknown>).status\n : undefined,\n ];\n for (const candidate of candidates) {\n if (\n typeof candidate === \"number\" &&\n Number.isInteger(candidate) &&\n candidate >= 400 &&\n candidate <= 599\n ) {\n return candidate;\n }\n }\n return undefined;\n}\n\n/**\n * Classify an error from its typed HTTP error status (`status`, `statusCode`,\n * or `meta.status`, 4xx/5xx only) and `AUTH_UNAUTHORIZED` code, following the\n * `cause` chain outer-first (at most {@link MAX_CAUSE_DEPTH} links, cycles\n * stop the walk) so wrappers that rethrow with `cause` keep the verdict. The\n * outermost error status decides, so an outer 5xx keeps its retry budget even\n * over an inner 401. Returns `undefined` for untyped errors; only then may\n * callers fall back to message heuristics.\n */\nexport function authorizationVerdictOf(\n error: unknown,\n): AuthorizationVerdict | undefined {\n const seen = new Set<unknown>();\n let sawUnauthorizedCode = false;\n let node: unknown = error;\n for (\n let depth = 0;\n depth < MAX_CAUSE_DEPTH && typeof node === \"object\" && node !== null && !seen.has(node);\n depth += 1\n ) {\n seen.add(node);\n const record = node as Record<string, unknown>;\n const status = errorStatusOf(record);\n if (status === 401) return \"unauthenticated\";\n if (status === 403) return \"forbidden\";\n if (status !== undefined) return \"other\";\n if (record.code === ErrorCodes.AUTH_UNAUTHORIZED) sawUnauthorizedCode = true;\n node = record.cause;\n }\n return sawUnauthorizedCode ? \"forbidden\" : undefined;\n}\n\n/**\n * Create a service error for storage quota exceeded (402 Payment Required).\n */\nexport function storageQuotaExceededError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return {\n code: ErrorCodes.STORAGE_QUOTA_EXCEEDED,\n message,\n service,\n meta,\n };\n}\n\n/**\n * Create a service error for storage limit reached (413 Payload Too Large).\n */\nexport function storageLimitReachedError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return {\n code: ErrorCodes.STORAGE_LIMIT_REACHED,\n message,\n service,\n meta,\n };\n}\n\n/** Shown when a write is rejected because the owner's storage is full. */\nexport const STORAGE_FULL_MESSAGE =\n \"TinyCloud storage is full, so this change was not saved. Reading still works. Free up space or upgrade your plan to save again.\";\n\n/** Shown when a write is larger than the storage the owner has left. */\nexport const STORAGE_WRITE_TOO_LARGE_MESSAGE =\n \"This change is larger than the TinyCloud storage you have left, so it was not saved. Reading still works. Free up space or upgrade your plan to save it.\";\n\n/**\n * Parse the byte counts from a node storage rejection\n * (\"... Used: 155744 bytes, Limit: 0 bytes\"). The limit is the share of the\n * owner's account-wide budget left for this space, so it is 0 when the\n * owner's other spaces already use the whole budget.\n */\nexport function parseStorageQuotaBytes(\n responseText: string\n): { usedBytes: number; limitBytes: number } | undefined {\n const match = responseText.match(/Used:\\s*(\\d+)\\s*bytes,\\s*Limit:\\s*(\\d+)\\s*bytes/i);\n if (!match) return undefined;\n return {\n usedBytes: parseInt(match[1], 10),\n limitBytes: parseInt(match[2], 10),\n };\n}\n\n/**\n * Build the error for a write the node rejected for storage: 402 when storage\n * is already full, 413 when this write is larger than what is left. KV, SQL\n * and DuckDB all report it with the same codes and message so an app can\n * detect \"storage full\" in one place with {@link isStorageFullError}.\n */\nexport function storageRejectionError(\n service: string,\n status: 402 | 413,\n meta: Record<string, unknown>,\n responseText: string\n): ServiceError {\n const quotaMeta = { ...meta, ...parseStorageQuotaBytes(responseText) };\n return status === 402\n ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta)\n : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);\n}\n\n/**\n * True when a write failed for storage: the owner's TinyCloud storage is full\n * (`STORAGE_QUOTA_EXCEEDED`), or this write is larger than what is left\n * (`STORAGE_LIMIT_REACHED`). Reads keep working in both cases, so apps should\n * stay usable read-only and point the user at freeing up space or upgrading.\n */\nexport function isStorageFullError(error: { code?: string } | null | undefined): boolean {\n return (\n error?.code === ErrorCodes.STORAGE_QUOTA_EXCEEDED ||\n error?.code === ErrorCodes.STORAGE_LIMIT_REACHED\n );\n}\n\n/**\n * Wrap an unknown error in a ServiceError.\n */\nexport function wrapError(\n service: string,\n error: unknown,\n defaultCode: string = ErrorCodes.NETWORK_ERROR\n): ServiceError {\n if (error instanceof Error) {\n // Check for abort errors\n if (error.name === \"AbortError\") {\n return abortedError(service);\n }\n\n // Check for timeout errors (varies by platform)\n if (\n error.name === \"TimeoutError\" ||\n error.message.toLowerCase().includes(\"timeout\")\n ) {\n return timeoutError(service);\n }\n\n return {\n code: defaultCode,\n message: error.message,\n service,\n cause: error,\n };\n }\n\n return {\n code: defaultCode,\n message: String(error),\n service,\n };\n}\n\n/**\n * Create an error Result from a ServiceError.\n */\nexport function errorResult(error: ServiceError) {\n return err(error);\n}\n","/**\n * BaseService - Abstract base class for all TinyCloud services.\n *\n * Provides common functionality:\n * - Context management\n * - Session lifecycle hooks\n * - Abort signal handling\n * - Telemetry emission\n */\n\nimport {\n IService,\n IServiceContext,\n ServiceSession,\n ServiceError,\n TelemetryEvents,\n err,\n Result,\n} from \"../types\";\nimport { authRequiredError, wrapError } from \"../errors\";\n\n/**\n * Abstract base class for TinyCloud services.\n *\n * Services extend this class to get common functionality like\n * context management, session lifecycle, and abort handling.\n *\n * @example\n * ```typescript\n * class MyService extends BaseService implements IMyService {\n * static readonly serviceName = 'myservice';\n *\n * constructor(config: MyServiceConfig = {}) {\n * super();\n * this._config = config;\n * }\n *\n * async doSomething(): Promise<Result<Data>> {\n * if (!this.requireAuth()) {\n * return err(authRequiredError('myservice'));\n * }\n * // ... implementation\n * }\n * }\n * ```\n */\nexport abstract class BaseService implements IService {\n /**\n * Service identifier used for registration.\n * Must be overridden by subclasses.\n */\n static readonly serviceName: string;\n\n /**\n * Service context providing access to platform dependencies.\n * Set during initialize().\n */\n protected context!: IServiceContext;\n\n /**\n * Abort controller for this service's operations.\n * Reset on sign-out.\n */\n protected abortController: AbortController = new AbortController();\n\n /**\n * Service-specific configuration.\n */\n protected _config: Record<string, unknown> = {};\n\n /**\n * Get the service configuration.\n */\n get config(): Record<string, unknown> {\n return this._config;\n }\n\n /**\n * Initialize the service with context.\n * Called by the SDK after instantiation.\n *\n * @param context - The service context\n */\n initialize(context: IServiceContext): void {\n this.context = context;\n }\n\n /**\n * Called when session changes (sign-in, sign-out, refresh).\n * Override in subclasses to handle session changes.\n *\n * @param session - The new session, or null if signed out\n */\n onSessionChange(session: ServiceSession | null): void {\n // Override in subclass if needed\n }\n\n /**\n * Called when SDK signs out.\n * Aborts all pending operations.\n */\n onSignOut(): void {\n this.abortController.abort();\n this.abortController = new AbortController();\n }\n\n /**\n * Get the abort signal for this service.\n * Combines the service-level abort with context-level abort.\n */\n protected get abortSignal(): AbortSignal {\n return this.combineSignals();\n }\n\n /**\n * Check if the service is authenticated.\n */\n protected get isAuthenticated(): boolean {\n return this.context?.isAuthenticated ?? false;\n }\n\n /**\n * Get the current session.\n * Throws if not authenticated.\n */\n protected get session(): ServiceSession {\n if (!this.context?.session) {\n throw new Error(\"Not authenticated\");\n }\n return this.context.session;\n }\n\n /**\n * Check authentication and return error result if not authenticated.\n * Use this at the start of methods that require authentication.\n *\n * @returns true if authenticated, false otherwise\n */\n protected requireAuth(): boolean {\n return this.isAuthenticated;\n }\n\n /**\n * Emit a telemetry event.\n *\n * @param event - Event name\n * @param data - Event data\n */\n protected emit(event: string, data: unknown): void {\n this.context?.emit(event, data);\n }\n\n /**\n * Emit a service request event.\n *\n * @param action - The action being performed\n * @param key - Optional key/path being accessed\n */\n protected emitRequest(action: string, key?: string): void {\n const service = this.getServiceName();\n this.emit(TelemetryEvents.SERVICE_REQUEST, {\n service,\n action,\n span: this.spanName(action),\n key,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Emit a service response event.\n *\n * @param action - The action that was performed\n * @param ok - Whether the request was successful\n * @param startTime - Start time for duration calculation\n * @param status - Optional HTTP status code\n */\n protected emitResponse(\n action: string,\n ok: boolean,\n startTime: number,\n status?: number\n ): void {\n const service = this.getServiceName();\n const durationMs = Date.now() - startTime;\n const span = this.spanName(action);\n this.emit(TelemetryEvents.SERVICE_RESPONSE, {\n service,\n action,\n span,\n ok,\n duration: durationMs,\n durationMs,\n status,\n });\n this.emit(TelemetryEvents.SPAN, {\n span,\n service,\n action,\n ok,\n durationMs,\n status,\n });\n }\n\n /**\n * Emit a service error event.\n *\n * @param error - The service error\n */\n protected emitError(error: ServiceError, action?: string): void {\n const span = action ? this.spanName(action) : undefined;\n this.emit(TelemetryEvents.SERVICE_ERROR, {\n service: this.getServiceName(),\n ...(span ? { span } : {}),\n error,\n });\n }\n\n /**\n * Get the service name from the static property.\n * Subclasses must define static serviceName.\n */\n protected getServiceName(): string {\n return (this.constructor as typeof BaseService).serviceName;\n }\n\n /**\n * Stable span name used by SDK telemetry sinks.\n */\n protected spanName(action: string): string {\n return `sdk.${this.getServiceName()}.${action}`;\n }\n\n /**\n * Create a combined abort signal from multiple sources.\n *\n * @param signals - Additional abort signals to combine\n * @returns A combined abort signal\n */\n protected combineSignals(...signals: (AbortSignal | undefined)[]): AbortSignal {\n const controller = new AbortController();\n const allSignals = [\n this.abortController.signal,\n this.context?.abortSignal,\n ...signals.filter(Boolean),\n ].filter(Boolean) as AbortSignal[];\n\n for (const signal of allSignals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n signal.addEventListener(\"abort\", () => controller.abort(signal.reason), {\n once: true,\n });\n }\n\n return controller.signal;\n }\n\n /**\n * Wrap an operation with error handling and telemetry.\n *\n * @param action - The action name for telemetry\n * @param key - Optional key for telemetry\n * @param operation - The operation to execute\n * @returns Result of the operation\n */\n protected async withTelemetry<T>(\n action: string,\n key: string | undefined,\n operation: () => Promise<Result<T>>\n ): Promise<Result<T>> {\n const startTime = Date.now();\n this.emitRequest(action, key);\n\n try {\n const result = await operation();\n\n if (result.ok) {\n this.emitResponse(action, true, startTime);\n } else {\n this.emitResponse(action, false, startTime);\n this.emitError(result.error, action);\n }\n\n return result;\n } catch (error) {\n const serviceError = wrapError(this.getServiceName(), error);\n this.emitResponse(action, false, startTime);\n this.emitError(serviceError, action);\n return err(serviceError);\n }\n }\n}\n","export interface ParsedServiceErrorBody {\n error?: string;\n message?: string;\n code?: string;\n}\n\nexport function parseServiceErrorBody(errorText: string): ParsedServiceErrorBody {\n try {\n return JSON.parse(errorText) as ParsedServiceErrorBody;\n } catch {\n return {};\n }\n}\n\nexport function formatServiceResponseError(\n serviceLabel: string,\n operation: string,\n status: number,\n errorText: string,\n parsed: ParsedServiceErrorBody,\n): string {\n if (parsed.message) {\n return compactMessage(parsed.message);\n }\n\n if (looksLikeHtml(errorText)) {\n if (status === 524 || /524\\s*[:-]/i.test(errorText) || /a timeout occurred/i.test(errorText)) {\n return `${serviceLabel} ${operation} failed: upstream request timed out. Please retry.`;\n }\n return `${serviceLabel} ${operation} failed: upstream service returned an HTML error page (${status}).`;\n }\n\n return `${serviceLabel} ${operation} failed: ${status} - ${compactMessage(errorText)}`;\n}\n\nexport function responseErrorMeta(\n status: number,\n statusText: string,\n errorText: string,\n): Record<string, unknown> {\n const meta: Record<string, unknown> = { status, statusText };\n const snippet = compactMessage(errorText);\n if (snippet) {\n meta.responseSnippet = snippet.slice(0, 300);\n }\n return meta;\n}\n\nfunction looksLikeHtml(text: string): boolean {\n return /<!doctype html/i.test(text) || /<html[\\s>]/i.test(text);\n}\n\nfunction compactMessage(message: string): string {\n const compact = message.replace(/\\s+/g, \" \").trim();\n return compact.length > 500 ? `${compact.slice(0, 500)}...` : compact;\n}\n","import type { Result } from \"../types\";\nimport type { SQLService } from \"./SQLService\";\nimport type { ISQLMigrations } from \"./ISQLService\";\nimport type {\n SqlMigrationApplyOptions,\n SqlMigrationApplyResponse,\n} from \"./types\";\n\nexport class SQLMigrations implements ISQLMigrations {\n constructor(\n private readonly service: SQLService,\n private readonly dbName: string,\n ) {}\n\n apply(options: SqlMigrationApplyOptions): Promise<Result<SqlMigrationApplyResponse>> {\n return this.service.applyMigrationsOnDb(this.dbName, options);\n }\n}\n","/**\n * DatabaseHandle - Handle for operations on a specific named database.\n *\n * Delegates all operations to the parent SQLService with the database name.\n */\n\nimport type { Result } from \"../types\";\nimport type { IDatabaseHandle, ISQLMigrations } from \"./ISQLService\";\nimport type { SQLService } from \"./SQLService\";\nimport { SQLMigrations } from \"./SQLMigrations\";\nimport type {\n SqlValue,\n SqlStatement,\n QueryResponse,\n ExecuteResponse,\n BatchResponse,\n QueryOptions,\n ExecuteOptions,\n BatchOptions,\n} from \"./types\";\n\nexport class DatabaseHandle implements IDatabaseHandle {\n private service: SQLService;\n public readonly name: string;\n public readonly migrations: ISQLMigrations;\n\n constructor(service: SQLService, name: string) {\n this.service = service;\n this.name = name;\n this.migrations = new SQLMigrations(service, name);\n }\n\n async query<T = Record<string, unknown>>(\n sql: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse<T>>> {\n return this.service.queryOnDb<T>(this.name, sql, params, options);\n }\n\n async execute(\n sql: string,\n params?: SqlValue[],\n options?: ExecuteOptions\n ): Promise<Result<ExecuteResponse>> {\n return this.service.executeOnDb(this.name, sql, params, options);\n }\n\n async batch(\n statements: SqlStatement[],\n options?: BatchOptions\n ): Promise<Result<BatchResponse>> {\n return this.service.batchOnDb(this.name, statements, options);\n }\n\n async executeStatement(\n name: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse | ExecuteResponse>> {\n return this.service.executeStatementOnDb(this.name, name, params, options);\n }\n\n async export(options?: QueryOptions): Promise<Result<Blob>> {\n return this.service.exportDb(this.name, options);\n }\n}\n","/**\n * SQL Service Types\n *\n * Type definitions for the SQL service operations.\n */\n\nimport { SQL } from \"@tinycloud/bootstrap\";\n\n/**\n * Configuration for SQLService.\n */\nexport interface SQLServiceConfig {\n /**\n * Default database name.\n * If not set, operations default to \"default\".\n */\n defaultDatabase?: string;\n\n /**\n * Default timeout in milliseconds for SQL operations.\n */\n timeout?: number;\n\n /** Allow additional config properties */\n [key: string]: unknown;\n}\n\n/**\n * Options for SQL query operations.\n */\nexport interface QueryOptions {\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n\n /** Maximum rows the node may return. Nodes without limit support ignore it. */\n maxRows?: number;\n\n /** Maximum serialized result bytes the node may return. */\n maxBytes?: number;\n}\n\n/**\n * Options for SQL execute operations.\n */\nexport interface ExecuteOptions {\n /**\n * Schema initialization statements (CREATE TABLE IF NOT EXISTS ...).\n * Executed before the main statement on first write.\n */\n schema?: string[];\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for SQL batch operations.\n */\nexport interface BatchOptions {\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * A SQL value: null, number, string, or binary data.\n */\nexport type SqlValue = null | number | string | Uint8Array;\n\n/**\n * A SQL statement with optional parameters.\n */\nexport interface SqlStatement {\n sql: string;\n params?: SqlValue[];\n}\n\n/**\n * Response from SQL query operations.\n */\nexport interface QueryResponse<T = Record<string, unknown>> {\n columns: string[];\n rows: T[][];\n rowCount: number;\n}\n\n/**\n * Response from SQL execute operations.\n */\nexport interface ExecuteResponse {\n changes: number;\n lastInsertRowId: number | null;\n}\n\n/**\n * Response from SQL batch operations.\n */\nexport interface BatchResponse {\n results: ExecuteResponse[];\n}\n\n/**\n * A versioned SQL migration owned by an application or SDK subsystem.\n */\nexport interface SqlMigration {\n /**\n * Stable migration id within the namespace, e.g. \"001_initial\".\n */\n id: string;\n\n /**\n * SQL statements to apply when this migration has not been recorded.\n * Statements should be idempotent where SQLite supports it.\n */\n sql: Array<string | SqlStatement>;\n}\n\n/**\n * Options for applying SQL migrations.\n */\nexport interface SqlMigrationApplyOptions {\n /**\n * Namespace for this migration set, usually an app id or SDK subsystem.\n */\n namespace: string;\n\n /**\n * Ordered migration list.\n */\n migrations: SqlMigration[];\n\n /**\n * Custom abort signal for migration operations.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Result from applying SQL migrations.\n */\nexport interface SqlMigrationApplyResponse {\n database: string;\n namespace: string;\n status: \"already_current\" | \"applied\";\n applied: string[];\n skipped: string[];\n}\n\n/**\n * SQL service action types.\n *\n * URNs derive from the canonical capability registry in `@tinycloud/bootstrap`\n * (TC-112 single source of truth, vendored from tinycloud-node). `insert`,\n * `update`, and `delete` were dropped — zero client refs and never accepted\n * node-side. `select` is a deprecated alias of `read`. `execute`/`export` are\n * request-kind artifacts, not grantable capabilities: the node routes them by\n * request-body kind, so they are absent from the registry but kept here as the\n * invocation ability the client sends today (wire alignment tracked in TC-114).\n */\nexport const SQLAction = {\n READ: SQL.READ,\n WRITE: SQL.WRITE,\n SCHEMA: SQL.SCHEMA,\n ADMIN: SQL.ADMIN,\n SELECT: SQL.SELECT,\n EXECUTE: SQL.EXECUTE,\n EXPORT: SQL.EXPORT,\n ALL: SQL.ALL,\n} as const;\n\nexport type SQLActionType = (typeof SQLAction)[keyof typeof SQLAction];\n","/**\n * SQLService - SQL database service implementation.\n *\n * Platform-agnostic SQL service that works with both web-sdk and node-sdk.\n * Uses dependency injection via IServiceContext for platform dependencies.\n */\n\nimport { BaseService } from \"../base/BaseService\";\nimport {\n Result,\n ok,\n err,\n ErrorCodes,\n serviceError,\n type FetchResponse,\n type ServiceHeaders,\n type ServiceSession,\n} from \"../types\";\nimport {\n authRequiredError,\n wrapError,\n parseAuthError,\n storageRejectionError,\n} from \"../errors\";\nimport {\n formatServiceResponseError,\n parseServiceErrorBody,\n responseErrorMeta,\n} from \"../responseErrors\";\nimport type { ISQLService } from \"./ISQLService\";\nimport type { IDatabaseHandle } from \"./ISQLService\";\nimport { DatabaseHandle } from \"./DatabaseHandle\";\nimport {\n type SQLServiceConfig,\n type QueryOptions,\n type ExecuteOptions,\n type BatchOptions,\n type SqlValue,\n type SqlStatement,\n type QueryResponse,\n type ExecuteResponse,\n type BatchResponse,\n type SqlMigration,\n type SqlMigrationApplyOptions,\n type SqlMigrationApplyResponse,\n SQLAction,\n} from \"./types\";\n\nconst DDL_TOKENS = new Set([\"alter\", \"create\", \"drop\"]);\nconst MIGRATIONS_TABLE = \"__tinycloud_sql_migrations\";\nconst MIGRATIONS_SCHEMA = `CREATE TABLE IF NOT EXISTS ${MIGRATIONS_TABLE} (\n key TEXT PRIMARY KEY,\n namespace TEXT NOT NULL,\n id TEXT NOT NULL,\n applied_at TEXT NOT NULL,\n statement_count INTEGER NOT NULL\n)`;\nconst MIGRATIONS_META_NAMESPACE = \"tinycloud.sql.migrations\";\nconst MIGRATIONS_META_ID = \"000_metadata\";\n\nexport class SQLService extends BaseService implements ISQLService {\n static readonly serviceName = \"sql\";\n\n declare protected _config: SQLServiceConfig;\n private readonly migrationLocks = new Map<string, Promise<Result<SqlMigrationApplyResponse>>>();\n\n constructor(config: SQLServiceConfig = {}) {\n super();\n this._config = config;\n }\n\n get config(): SQLServiceConfig {\n return this._config;\n }\n\n private get defaultDbName(): string {\n return this._config.defaultDatabase ?? \"default\";\n }\n\n private get host(): string {\n return this.context.hosts[0];\n }\n\n /**\n * Get a handle to a named database.\n */\n db(name?: string): IDatabaseHandle {\n return new DatabaseHandle(this, name ?? this.defaultDbName);\n }\n\n /**\n * Shortcut: query the default database.\n */\n async query<T = Record<string, unknown>>(\n sql: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse<T>>> {\n return this.queryOnDb<T>(this.defaultDbName, sql, params, options);\n }\n\n /**\n * Shortcut: execute on the default database.\n */\n async execute(\n sql: string,\n params?: SqlValue[],\n options?: ExecuteOptions\n ): Promise<Result<ExecuteResponse>> {\n return this.executeOnDb(this.defaultDbName, sql, params, options);\n }\n\n /**\n * Shortcut: batch on the default database.\n */\n async batch(\n statements: SqlStatement[],\n options?: BatchOptions\n ): Promise<Result<BatchResponse>> {\n return this.batchOnDb(this.defaultDbName, statements, options);\n }\n\n async applyMigrationsOnDb(\n dbName: string,\n options: SqlMigrationApplyOptions,\n ): Promise<Result<SqlMigrationApplyResponse>> {\n const validationError = validateMigrationOptions(options);\n if (validationError) {\n return err(serviceError(ErrorCodes.INVALID_INPUT, validationError, \"sql\"));\n }\n\n const lockKey = `${dbName}\\0${options.namespace}`;\n const existing = this.migrationLocks.get(lockKey);\n if (existing) {\n return existing;\n }\n\n const promise = this.applyMigrationsOnDbUnlocked(dbName, options);\n this.migrationLocks.set(lockKey, promise);\n try {\n return await promise;\n } finally {\n this.migrationLocks.delete(lockKey);\n }\n }\n\n // === Internal methods called by DatabaseHandle ===\n\n async queryOnDb<T = Record<string, unknown>>(\n dbName: string,\n sql: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse<T>>> {\n return this.withTelemetry(\"query\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n try {\n const body: Record<string, unknown> = {\n action: \"query\",\n sql,\n params: serializeSqlValues(params ?? []),\n };\n if (options?.maxRows !== undefined) body.maxRows = options.maxRows;\n if (options?.maxBytes !== undefined) body.maxBytes = options.maxBytes;\n\n const response = await this.invokeSQL(\n dbName,\n this.actionForSql(sql, SQLAction.READ),\n body,\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"query\");\n }\n\n const data = (await response.json()) as QueryResponse<T>;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n }\n });\n }\n\n async executeOnDb(\n dbName: string,\n sql: string,\n params?: SqlValue[],\n options?: ExecuteOptions\n ): Promise<Result<ExecuteResponse>> {\n return this.withTelemetry(\"execute\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n try {\n const body: Record<string, unknown> = {\n action: \"execute\",\n sql,\n params: serializeSqlValues(params ?? []),\n };\n if (options?.schema) {\n body.schema = options.schema;\n }\n\n const actions = [\n this.actionForSql(sql, SQLAction.WRITE),\n ...(options?.schema ?? []).map((statement) =>\n this.actionForSql(statement, SQLAction.SCHEMA),\n ),\n ];\n const response = await this.invokeSQL(\n dbName,\n this.dedupeActions(actions),\n body,\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"execute\");\n }\n\n const data = (await response.json()) as ExecuteResponse;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n }\n });\n }\n\n async batchOnDb(\n dbName: string,\n statements: SqlStatement[],\n options?: BatchOptions\n ): Promise<Result<BatchResponse>> {\n return this.withTelemetry(\"batch\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n try {\n const response = await this.invokeSQL(\n dbName,\n this.actionsForSqlBatch(statements),\n { action: \"batch\", statements },\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"batch\");\n }\n\n const data = (await response.json()) as BatchResponse;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n }\n });\n }\n\n async executeStatementOnDb(\n dbName: string,\n name: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse | ExecuteResponse>> {\n return this.withTelemetry(\"executeStatement\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n try {\n // TC-114: mint the dispatchable ability, not the literal method name.\n // The node has no `tinycloud.sql/execute` capability — it routes an\n // `execute_statement` request by body kind and classifies write-class\n // from the RESOLVED named statement's SQL\n // (tinycloud-node-server/src/routes/mod.rs:1168-1171). A named statement\n // may mutate, and the SQL parser rejects writes under a read/select\n // ability (tinycloud-core/src/sql/parser.rs:135) while permitting reads\n // under write (write ⊇ read), so `write` is the ability that works for\n // both read-only and mutating statements and is contained in a normal\n // read+write delegation.\n const response = await this.invokeSQL(\n dbName,\n SQLAction.WRITE,\n { action: \"execute_statement\", name, params: params ?? [] },\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"executeStatement\");\n }\n\n const data = (await response.json()) as\n | QueryResponse\n | ExecuteResponse;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n }\n });\n }\n\n async exportDb(\n dbName: string,\n options?: QueryOptions\n ): Promise<Result<Blob>> {\n return this.withTelemetry(\"export\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n try {\n // TC-114: mint the dispatchable ability, not the literal method name.\n // The node has no `tinycloud.sql/export` capability — it routes an\n // `export` request by body kind and authorizes it as a READ\n // (`SqlRequest::Export` is non-write:\n // tinycloud-node-server/src/routes/mod.rs:1172, see comment :1155).\n const response = await this.invokeSQL(\n dbName,\n SQLAction.READ,\n { action: \"export\" },\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"export\");\n }\n\n // FetchResponse doesn't expose blob(), so access it from the\n // underlying response which is a standard fetch Response at runtime\n const resp = response as any;\n if (typeof resp.blob === \"function\") {\n const blob = await resp.blob();\n return ok(blob as Blob);\n }\n // If blob() is not available, return the raw text as a Blob-like\n const text = await response.text();\n return ok(text as unknown as Blob);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n }\n });\n }\n\n private async applyMigrationsOnDbUnlocked(\n dbName: string,\n options: SqlMigrationApplyOptions,\n ): Promise<Result<SqlMigrationApplyResponse>> {\n return this.withTelemetry(\"migrations.apply\", dbName, async () => {\n // Read before writing: an up-to-date database must open with a single\n // read, because writes fail once the owner's storage is full (402) and\n // read-only sessions cannot write at all.\n const listed = await this.queryOnDb<{ id: string }>(\n dbName,\n `SELECT id FROM ${MIGRATIONS_TABLE} WHERE namespace = ? ORDER BY applied_at, id`,\n [options.namespace],\n { signal: options.signal },\n );\n // A node may answer for a database that was never created with either\n // \"no such table\" or \"database not found\"; both mean nothing is applied.\n const tableMissing =\n !listed.ok &&\n (listed.error.code === ErrorCodes.SQL_DATABASE_NOT_FOUND ||\n isMissingTableError(listed.error.message));\n if (!listed.ok && !tableMissing) return listed;\n\n const appliedIds = new Set(\n (listed.ok ? listed.data.rows : [])\n .map((row) => rowValue(row, 0))\n .filter((id): id is string => typeof id === \"string\"),\n );\n const skipped = options.migrations\n .filter((migration) => appliedIds.has(migration.id))\n .map((migration) => migration.id);\n const pending = options.migrations.filter((migration) => !appliedIds.has(migration.id));\n const applied: string[] = [];\n\n if (pending.length > 0 && tableMissing) {\n const created = await this.ensureMigrationsTable(dbName, options.signal);\n if (!created.ok) return created;\n }\n\n for (const migration of pending) {\n const result = await this.applyOneMigration(dbName, options.namespace, migration, options.signal);\n if (!result.ok) return result;\n applied.push(migration.id);\n }\n\n return ok({\n database: dbName,\n namespace: options.namespace,\n status: applied.length > 0 ? \"applied\" : \"already_current\",\n applied,\n skipped,\n });\n });\n }\n\n private async applyOneMigration(\n dbName: string,\n namespace: string,\n migration: SqlMigration,\n signal?: AbortSignal,\n ): Promise<Result<void>> {\n const statements = [\n ...migration.sql.map((statement) =>\n typeof statement === \"string\" ? { sql: statement } : statement,\n ),\n {\n sql: `INSERT OR REPLACE INTO ${MIGRATIONS_TABLE} (key, namespace, id, applied_at, statement_count) VALUES (?, ?, ?, ?, ?)`,\n params: [\n migrationKey(namespace, migration.id),\n namespace,\n migration.id,\n new Date().toISOString(),\n migration.sql.length,\n ],\n },\n ];\n\n const result = await this.batchOnDb(dbName, statements, { signal });\n if (!result.ok) return result;\n return ok(undefined);\n }\n\n private async ensureMigrationsTable(\n dbName: string,\n signal?: AbortSignal,\n ): Promise<Result<void>> {\n const result = await this.batchOnDb(\n dbName,\n [\n { sql: MIGRATIONS_SCHEMA },\n {\n sql: `INSERT OR REPLACE INTO ${MIGRATIONS_TABLE} (key, namespace, id, applied_at, statement_count) VALUES (?, ?, ?, ?, ?)`,\n params: [\n migrationKey(MIGRATIONS_META_NAMESPACE, MIGRATIONS_META_ID),\n MIGRATIONS_META_NAMESPACE,\n MIGRATIONS_META_ID,\n new Date().toISOString(),\n 1,\n ],\n },\n ],\n { signal },\n );\n if (!result.ok) return result;\n return ok(undefined);\n }\n\n // === Private helpers ===\n\n private async invokeSQL(\n dbName: string,\n actions: string | string[],\n body: Record<string, unknown>,\n signal?: AbortSignal\n ): Promise<FetchResponse> {\n const session = this.context.session!;\n const actionList = Array.isArray(actions) ? actions : [actions];\n const headers =\n actionList.length === 1\n ? this.context.invoke(session, \"sql\", dbName, actionList[0]!)\n : this.invokeSQLAny(session, dbName, actionList);\n\n return this.context.fetch(`${this.host}/invoke`, {\n method: \"POST\",\n headers: {\n ...(headers as Record<string, string>),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body) as any,\n signal: this.combineSignals(signal),\n });\n }\n\n private actionForSql(sql: string, fallback: string): string {\n const token = firstSqlToken(sql);\n if (token === \"pragma\") return SQLAction.ADMIN;\n if (token !== undefined && DDL_TOKENS.has(token)) return SQLAction.SCHEMA;\n return fallback;\n }\n\n private actionsForSqlBatch(statements: SqlStatement[]): string[] {\n return this.dedupeActions(\n statements.map((statement) => this.actionForSql(statement.sql, SQLAction.WRITE)),\n );\n }\n\n private dedupeActions(actions: string[]): string[] {\n return [...new Set(actions)];\n }\n\n private invokeSQLAny(\n session: ServiceSession,\n dbName: string,\n actions: string[],\n ): ServiceHeaders {\n if (!this.context.invokeAny) {\n throw new Error(\n `SQL operation requires multiple permissions (${actions.join(\", \")}) but this SDK runtime does not support multi-resource invocations`,\n );\n }\n\n return this.context.invokeAny(\n session,\n actions.map((action) => ({\n spaceId: session.spaceId,\n service: \"sql\",\n path: dbName,\n action,\n })),\n );\n }\n\n private async handleErrorResponse(\n response: FetchResponse,\n operation: string\n ): Promise<Result<never>> {\n const errorText = await response.text();\n const meta = responseErrorMeta(response.status, response.statusText, errorText);\n\n // The node answers a write-class request on a full space with 402.\n if (response.status === 402) {\n return err(storageRejectionError(\"sql\", 402, meta, errorText));\n }\n\n const errorBody = parseServiceErrorBody(errorText);\n\n const errorCode = this.mapHttpStatusToErrorCode(\n response.status,\n errorBody.error\n );\n const message = formatServiceResponseError(\n \"SQL\",\n operation,\n response.status,\n errorText,\n errorBody,\n );\n\n if (response.status === 401) {\n const { resource, action } = parseAuthError(errorText);\n if (action) meta.requiredAction = action;\n if (resource) meta.resource = resource;\n }\n\n return err(\n serviceError(errorCode, message, \"sql\", { meta })\n );\n }\n\n private mapHttpStatusToErrorCode(\n status: number,\n serverError?: string\n ): string {\n switch (status) {\n case 400:\n return ErrorCodes.SQL_ERROR;\n case 401:\n return ErrorCodes.AUTH_UNAUTHORIZED;\n case 403:\n if (serverError === \"sql_readonly_violation\") {\n return ErrorCodes.SQL_READONLY_VIOLATION;\n }\n return ErrorCodes.SQL_PERMISSION_DENIED;\n case 404:\n return ErrorCodes.SQL_DATABASE_NOT_FOUND;\n case 413:\n return ErrorCodes.SQL_RESPONSE_TOO_LARGE;\n case 429:\n return ErrorCodes.SQL_QUOTA_EXCEEDED;\n default:\n return ErrorCodes.NETWORK_ERROR;\n }\n }\n}\n\nfunction firstSqlToken(sql: string): string | undefined {\n let index = 0;\n\n while (index < sql.length) {\n while (index < sql.length && /\\s/.test(sql[index])) {\n index++;\n }\n\n if (sql.startsWith(\"--\", index)) {\n const newline = sql.indexOf(\"\\n\", index + 2);\n if (newline === -1) {\n return undefined;\n }\n index = newline + 1;\n continue;\n }\n\n if (sql.startsWith(\"/*\", index)) {\n const end = sql.indexOf(\"*/\", index + 2);\n if (end === -1) {\n return undefined;\n }\n index = end + 2;\n continue;\n }\n\n break;\n }\n\n const match = /^[A-Za-z_]+/.exec(sql.slice(index));\n return match?.[0].toLowerCase();\n}\n\nfunction validateMigrationOptions(options: SqlMigrationApplyOptions): string | null {\n if (!options || typeof options !== \"object\") {\n return \"SQL migrations require options\";\n }\n if (!options.namespace || typeof options.namespace !== \"string\") {\n return \"SQL migrations require a non-empty namespace\";\n }\n if (!Array.isArray(options.migrations)) {\n return \"SQL migrations require an ordered migrations array\";\n }\n\n const ids = new Set<string>();\n for (const migration of options.migrations) {\n if (!migration.id || typeof migration.id !== \"string\") {\n return \"SQL migrations require every migration to have a non-empty id\";\n }\n if (ids.has(migration.id)) {\n return `Duplicate SQL migration id: ${migration.id}`;\n }\n ids.add(migration.id);\n\n if (!Array.isArray(migration.sql)) {\n return `SQL migration ${migration.id} requires a sql array`;\n }\n for (const statement of migration.sql) {\n if (typeof statement === \"string\") {\n if (statement.trim() === \"\") {\n return `SQL migration ${migration.id} contains an empty statement`;\n }\n continue;\n }\n if (!statement || typeof statement.sql !== \"string\" || statement.sql.trim() === \"\") {\n return `SQL migration ${migration.id} contains an invalid statement`;\n }\n }\n }\n\n return null;\n}\n\nfunction serializeSqlValues(values: SqlValue[]): Array<null | number | string | number[]> {\n return values.map((value) =>\n value instanceof Uint8Array ? Array.from(value) : value,\n );\n}\n\nfunction isMissingTableError(message: string): boolean {\n return /no such table/i.test(message);\n}\n\nfunction migrationKey(namespace: string, id: string): string {\n return `${encodeURIComponent(namespace)}:${encodeURIComponent(id)}`;\n}\n\nfunction rowValue(row: unknown, index: number): unknown {\n if (Array.isArray(row)) return row[index];\n if (row && typeof row === \"object\") {\n const values = Object.values(row as Record<string, unknown>);\n return values[index];\n }\n return undefined;\n}\n"],"mappings":"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","names":["ok","serviceError"]}
1
+ {"version":3,"sources":["../../src/sql/index.ts","../../src/types.ts","../../src/errors.ts","../../src/base/BaseService.ts","../../src/responseErrors.ts","../../src/sql/SQLMigrations.ts","../../src/sql/DatabaseHandle.ts","../../src/sql/types.ts","../../src/sql/SQLService.ts"],"sourcesContent":["/**\n * SQL Service Module\n *\n * Provides SQL database operations for TinyCloud.\n */\n\nexport { SQLService } from \"./SQLService\";\nexport { DatabaseHandle } from \"./DatabaseHandle\";\nexport { SQLMigrations } from \"./SQLMigrations\";\nexport type { ISQLService, IDatabaseHandle, ISQLMigrations } from \"./ISQLService\";\nexport {\n SQLAction,\n type SQLActionType,\n type SQLServiceConfig,\n type QueryOptions,\n type ExecuteOptions,\n type BatchOptions,\n type SqlValue,\n type SqlStatement,\n type QueryResponse,\n type ExecuteResponse,\n type BatchResponse,\n type SqlMigration,\n type SqlMigrationApplyOptions,\n type SqlMigrationApplyResponse,\n} from \"./types\";\n","/**\n * SDK Services - Core Types\n *\n * These types define the service architecture for TinyCloud SDK.\n * Services use dependency injection via IServiceContext for platform independence.\n */\n\n// =============================================================================\n// Result Type Pattern\n// =============================================================================\n\n/**\n * Result type for service operations.\n * Services return Result instead of throwing, making error handling explicit.\n *\n * @template T - The success data type\n * @template E - The error type (defaults to ServiceError)\n *\n * @example\n * ```typescript\n * const result = await kv.get('key');\n * if (result.ok) {\n * console.log(result.data);\n * } else {\n * console.error(result.error.code);\n * }\n * ```\n */\nexport type Result<T, E = ServiceError> =\n | { ok: true; data: T }\n | { ok: false; error: E };\n\n/**\n * Service error with structured information.\n */\nexport interface ServiceError {\n /** Error code for programmatic handling (e.g., 'KV_NOT_FOUND', 'AUTH_EXPIRED') */\n code: string;\n /** Human-readable error message */\n message: string;\n /** Service that produced the error (e.g., 'kv', 'sql') */\n service: string;\n /** Original error if this wraps another error */\n cause?: Error;\n /** Additional metadata about the error */\n meta?: Record<string, unknown>;\n}\n\n/**\n * A node-supplied permission hint after strict SDK validation. This is\n * deliberately narrower than a general capability: no wildcards, caveats,\n * descriptions, or transport metadata can cross the service boundary.\n */\nexport interface PermissionHint {\n readonly service: \"tinycloud.kv\" | \"tinycloud.encryption\";\n readonly space?: string;\n readonly path: string;\n readonly actions: readonly string[];\n}\n\n/**\n * Storage quota information returned with quota-related errors.\n */\nexport interface StorageQuotaInfo {\n usedBytes: number;\n limitBytes: number;\n service: string;\n}\n\n/**\n * Standard error codes used across services.\n */\nexport const ErrorCodes = {\n // Common errors\n NOT_FOUND: \"NOT_FOUND\",\n AUTH_EXPIRED: \"AUTH_EXPIRED\",\n AUTH_REQUIRED: \"AUTH_REQUIRED\",\n AUTH_UNAUTHORIZED: \"AUTH_UNAUTHORIZED\",\n NETWORK_ERROR: \"NETWORK_ERROR\",\n TIMEOUT: \"TIMEOUT\",\n ABORTED: \"ABORTED\",\n INVALID_INPUT: \"INVALID_INPUT\",\n PERMISSION_DENIED: \"PERMISSION_DENIED\",\n\n // KV-specific errors\n KV_NOT_FOUND: \"KV_NOT_FOUND\",\n KV_WRITE_FAILED: \"KV_WRITE_FAILED\",\n KV_PRECONDITION_FAILED: \"KV_PRECONDITION_FAILED\",\n KV_CONFLICT: \"KV_CONFLICT\",\n KV_RESPONSE_TOO_LARGE: \"KV_RESPONSE_TOO_LARGE\",\n\n // SQL-specific errors\n SQL_ERROR: \"SQL_ERROR\",\n SQL_PERMISSION_DENIED: \"SQL_PERMISSION_DENIED\",\n SQL_DATABASE_NOT_FOUND: \"SQL_DATABASE_NOT_FOUND\",\n SQL_RESPONSE_TOO_LARGE: \"SQL_RESPONSE_TOO_LARGE\",\n SQL_QUOTA_EXCEEDED: \"SQL_QUOTA_EXCEEDED\",\n SQL_INVALID_STATEMENT: \"SQL_INVALID_STATEMENT\",\n SQL_SCHEMA_ERROR: \"SQL_SCHEMA_ERROR\",\n SQL_READONLY_VIOLATION: \"SQL_READONLY_VIOLATION\",\n\n // Storage quota errors\n STORAGE_QUOTA_EXCEEDED: \"STORAGE_QUOTA_EXCEEDED\",\n STORAGE_LIMIT_REACHED: \"STORAGE_LIMIT_REACHED\",\n\n // DuckDB-specific errors\n DUCKDB_ERROR: \"DUCKDB_ERROR\",\n DUCKDB_PERMISSION_DENIED: \"DUCKDB_PERMISSION_DENIED\",\n DUCKDB_DATABASE_NOT_FOUND: \"DUCKDB_DATABASE_NOT_FOUND\",\n DUCKDB_RESPONSE_TOO_LARGE: \"DUCKDB_RESPONSE_TOO_LARGE\",\n DUCKDB_QUOTA_EXCEEDED: \"DUCKDB_QUOTA_EXCEEDED\",\n DUCKDB_INVALID_STATEMENT: \"DUCKDB_INVALID_STATEMENT\",\n DUCKDB_SCHEMA_ERROR: \"DUCKDB_SCHEMA_ERROR\",\n DUCKDB_READONLY_VIOLATION: \"DUCKDB_READONLY_VIOLATION\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\n// =============================================================================\n// Service Session\n// =============================================================================\n\n/**\n * Session data required for authenticated service operations.\n * Both TinyCloudSession and web-sdk Session can be cast to this interface.\n */\nexport interface ServiceSession {\n /** The delegation header containing the UCAN */\n delegationHeader: { Authorization: string };\n /** The delegation CID */\n delegationCid: string;\n /** The space ID for this session */\n spaceId: string;\n /** The verification method DID */\n verificationMethod: string;\n /** The session key JWK (required for invoke) */\n jwk: object;\n}\n\n// =============================================================================\n// Platform Dependencies (Injected)\n// =============================================================================\n\n/**\n * Headers type - compatible with both browser and Node.js.\n */\nexport type ServiceHeaders = Record<string, string> | [string, string][];\n\n/**\n * A single fact object to include in the UCAN invocation.\n * Facts are key-value objects that the server reads from the UCAN facts field.\n */\nexport interface InvocationFact {\n [key: string]: unknown;\n}\n\n/**\n * Facts to include in the UCAN invocation.\n * This is an array of fact objects per the UCAN spec.\n * Used to pass additional parameters that the server reads from the UCAN facts field.\n */\nexport type InvocationFacts = InvocationFact[];\n\n/**\n * Invoke function signature - platform-specific implementation injected via DI.\n * Both node-sdk-wasm and web-sdk-wasm export this with identical signature.\n *\n * @param session - The service session with delegation data\n * @param service - Service name (e.g., \"kv\")\n * @param path - Resource path or key\n * @param action - Action to perform (e.g., \"tinycloud.kv/get\")\n * @param facts - Optional facts to include in the UCAN (e.g., for capabilities/read params)\n * @returns Headers to include in the request\n */\nexport type InvokeFunction = (\n session: ServiceSession,\n service: string,\n path: string,\n action: string,\n facts?: InvocationFacts,\n) => ServiceHeaders;\n\n/**\n * Multi-resource invocation entry.\n */\nexport interface InvokeAnyEntry {\n /**\n * Legacy space-scoped resource. Optional when `resource` is provided.\n */\n spaceId?: string;\n service: string;\n path: string;\n action: string;\n /** Optional raw resource URI. When set, WASM signs this URI directly. */\n resource?: string;\n /** Capability caveats that must remain on this invocation attenuation. */\n caveats?: Record<string, unknown>[];\n}\n\n/**\n * Invoke function for minting a single authorization header that covers\n * multiple capabilities across one effective invoker.\n */\nexport type InvokeAnyFunction = (\n session: ServiceSession,\n entries: InvokeAnyEntry[],\n facts?: InvocationFacts,\n) => ServiceHeaders;\n\n/**\n * Fetch request options - compatible with standard fetch API.\n */\nexport interface FetchRequestInit {\n method?: string;\n headers?: ServiceHeaders;\n body?: Blob | FormData | string;\n signal?: AbortSignal;\n redirect?: \"error\";\n}\n\n/**\n * Fetch response interface - compatible with standard Response.\n */\nexport interface FetchResponse {\n ok: boolean;\n status: number;\n statusText: string;\n body?: unknown;\n headers: {\n get(name: string): string | null;\n };\n json(): Promise<unknown>;\n text(): Promise<string>;\n arrayBuffer(): Promise<ArrayBuffer>;\n blob(): Promise<Blob>;\n}\n\n/**\n * Fetch function signature - allows for custom fetch implementations.\n * Compatible with both browser fetch and Node.js fetch.\n */\nexport type FetchFunction = (\n url: string,\n init?: FetchRequestInit,\n) => Promise<FetchResponse>;\n\n// =============================================================================\n// Retry Policy\n// =============================================================================\n\n/**\n * Configuration for automatic retry of failed requests.\n */\nexport interface RetryPolicy {\n /** Maximum number of attempts (including initial) */\n maxAttempts: number;\n /** Backoff strategy between retries */\n backoff: \"none\" | \"linear\" | \"exponential\";\n /** Base delay in milliseconds for backoff calculation */\n baseDelayMs: number;\n /** Maximum delay in milliseconds between retries */\n maxDelayMs: number;\n /** Error codes that should trigger a retry */\n retryableErrors: string[];\n}\n\n/**\n * Default retry policy.\n */\nexport const defaultRetryPolicy: RetryPolicy = {\n maxAttempts: 3,\n backoff: \"exponential\",\n baseDelayMs: 1000,\n maxDelayMs: 10000,\n retryableErrors: [ErrorCodes.NETWORK_ERROR, ErrorCodes.TIMEOUT],\n};\n\n// =============================================================================\n// Service Context\n// =============================================================================\n\n/**\n * Event handler function type.\n */\nexport type EventHandler = (data: unknown) => void;\n\n/**\n * SDK telemetry event handler.\n */\nexport type TelemetryEventHandler = (event: string, data: unknown) => void;\n\n/**\n * Default-off telemetry configuration.\n */\nexport type TelemetryConfig =\n | boolean\n | {\n enabled?: boolean;\n onEvent?: TelemetryEventHandler;\n };\n\n/**\n * Service interface - base contract for all services.\n */\nexport interface IService {\n /** Initialize service with context */\n initialize(context: IServiceContext): void;\n\n /** Called when session changes (sign-in, sign-out, refresh) */\n onSessionChange(session: ServiceSession | null): void;\n\n /** Called when SDK signs out - should abort pending operations */\n onSignOut(): void;\n\n /** Service-specific configuration */\n readonly config: Record<string, unknown>;\n}\n\n/**\n * Context provided to services for accessing platform dependencies.\n * The SDK creates this context and passes it to services during initialization.\n */\nexport interface IServiceContext {\n // Session management\n /** Current active session, or null if not authenticated */\n readonly session: ServiceSession | null;\n /** Whether there is an active authenticated session */\n readonly isAuthenticated: boolean;\n\n // Platform dependencies (injected by SDK)\n /** Platform-specific invoke function from WASM binding */\n readonly invoke: InvokeFunction;\n /** Optional multi-resource invoke function */\n readonly invokeAny?: InvokeAnyFunction;\n /** Fetch function (defaults to globalThis.fetch) */\n readonly fetch: FetchFunction;\n /** Available TinyCloud host URLs */\n readonly hosts: string[];\n\n // Cross-service access\n /** Get another registered service by name */\n getService<T extends IService>(name: string): T | undefined;\n\n // Telemetry/Events\n /** Emit a telemetry event */\n emit(event: string, data: unknown): void;\n /** Subscribe to events */\n on(event: string, handler: EventHandler): () => void;\n\n // Lifecycle\n /** Abort signal that fires when SDK signs out */\n readonly abortSignal: AbortSignal;\n\n // Retry policy\n /** Retry policy for failed requests */\n readonly retryPolicy: RetryPolicy;\n}\n\n// =============================================================================\n// Telemetry Events\n// =============================================================================\n\n/**\n * Event emitted before a service request.\n */\nexport interface ServiceRequestEvent {\n service: string;\n action: string;\n span?: string;\n key?: string;\n timestamp: number;\n}\n\n/**\n * Event emitted after a service response.\n */\nexport interface ServiceResponseEvent {\n service: string;\n action: string;\n span?: string;\n ok: boolean;\n duration: number;\n durationMs?: number;\n status?: number;\n}\n\n/**\n * Event emitted on service error.\n */\nexport interface ServiceErrorEvent {\n service: string;\n span?: string;\n error: ServiceError;\n}\n\n/**\n * Event emitted on retry attempt.\n */\nexport interface ServiceRetryEvent {\n service: string;\n attempt: number;\n maxAttempts: number;\n error: ServiceError;\n}\n\n/**\n * Generic named span event for aggregating operation timings.\n */\nexport interface TelemetrySpanEvent {\n span: string;\n ok: boolean;\n durationMs: number;\n service?: string;\n action?: string;\n status?: number;\n error?: ServiceError;\n}\n\n/**\n * Telemetry event names.\n */\nexport const TelemetryEvents = {\n SPAN: \"telemetry.span\",\n SERVICE_REQUEST: \"service.request\",\n SERVICE_RESPONSE: \"service.response\",\n SERVICE_ERROR: \"service.error\",\n SERVICE_RETRY: \"service.retry\",\n SESSION_CHANGED: \"session.changed\",\n SESSION_EXPIRED: \"session.expired\",\n} as const;\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Create a success result.\n */\nexport function ok<T>(data: T): Result<T> {\n return { ok: true, data };\n}\n\n/**\n * Create an error result.\n */\nexport function err<E = ServiceError>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n\n/**\n * Create a ServiceError.\n */\nexport function serviceError(\n code: string,\n message: string,\n service: string,\n options?: { cause?: Error; meta?: Record<string, unknown> },\n): ServiceError {\n return {\n code,\n message,\n service,\n cause: options?.cause,\n meta: options?.meta,\n };\n}\n","/**\n * SDK Services - Error Utilities\n *\n * Utilities for creating and handling service errors.\n */\n\nimport { CAPABILITY_REGISTRY } from \"@tinycloud/bootstrap\";\nimport { parseNetworkId } from \"./encryption/networkId\";\n\nimport {\n type ServiceError,\n ErrorCodes,\n err,\n serviceError,\n type PermissionHint,\n} from \"./types\";\n\n/** Validate one structured node permission hint without accepting raw errors. */\nexport function parsePermissionHint(value: unknown): PermissionHint | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n const candidate = value as Record<string, unknown>;\n const keys = Object.keys(candidate).sort();\n if (keys.some((key) => ![\"actions\", \"path\", \"service\", \"space\"].includes(key))) return undefined;\n if (candidate.service !== \"tinycloud.kv\" && candidate.service !== \"tinycloud.encryption\") return undefined;\n if (typeof candidate.path !== \"string\" || candidate.path.length === 0 || candidate.path.includes(\"*\")) return undefined;\n if (candidate.service === \"tinycloud.kv\" &&\n (typeof candidate.space !== \"string\" || candidate.space.length === 0 || !candidate.path.startsWith(\"vault/\") || candidate.path.endsWith(\"/\"))) {\n return undefined;\n }\n if (candidate.service === \"tinycloud.encryption\" &&\n (candidate.space !== undefined || !candidate.path.startsWith(\"urn:tinycloud:encryption:\") || candidate.path.endsWith(\":\"))) {\n return undefined;\n }\n if (!Array.isArray(candidate.actions) || candidate.actions.length !== 1 ||\n typeof candidate.actions[0] !== \"string\" || candidate.actions[0].includes(\"*\")) return undefined;\n const expectedAction = candidate.service === \"tinycloud.kv\"\n ? \"tinycloud.kv/get\"\n : \"tinycloud.encryption/decrypt\";\n if (candidate.actions[0] !== expectedAction) return undefined;\n const space = typeof candidate.space === \"string\" ? candidate.space : undefined;\n return {\n service: candidate.service,\n ...(space === undefined ? {} : { space }),\n path: candidate.path,\n actions: [candidate.actions[0]],\n };\n}\n\n/** Extract only a validated structured hint from an SDK-owned error body. */\nexport function parsePermissionHintFromErrorText(text: string): PermissionHint | undefined {\n try {\n const parsed: unknown = JSON.parse(text);\n if (typeof parsed !== \"object\" || parsed === null) return undefined;\n const root = parsed as Record<string, unknown>;\n const nested = root.error;\n const nestedRecord = typeof nested === \"object\" && nested !== null\n ? nested as Record<string, unknown>\n : undefined;\n return parsePermissionHint(root.permissionHint) ??\n parsePermissionHint(nestedRecord?.permissionHint);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Create a service error for authentication required.\n */\nexport function authRequiredError(service: string): ServiceError {\n return {\n code: ErrorCodes.AUTH_REQUIRED,\n message: \"Authentication required. Please sign in first.\",\n service,\n };\n}\n\n/**\n * Create a service error for expired authentication.\n */\nexport function authExpiredError(service: string): ServiceError {\n return {\n code: ErrorCodes.AUTH_EXPIRED,\n message: \"Session has expired. Please sign in again.\",\n service,\n };\n}\n\n/**\n * Create a service error for network issues.\n */\nexport function networkError(\n service: string,\n message: string,\n cause?: Error\n): ServiceError {\n return {\n code: ErrorCodes.NETWORK_ERROR,\n message,\n service,\n cause,\n };\n}\n\n/**\n * Abort reason for a service request that exceeded its configured timeout.\n *\n * Named `TimeoutError` (like the reason of the platform's\n * `AbortSignal.timeout()`), so it maps to `ErrorCodes.TIMEOUT` and never to\n * `ErrorCodes.ABORTED`, which is reserved for caller and sign-out aborts.\n */\nexport class RequestTimeoutError extends Error {\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms.`);\n this.name = \"TimeoutError\";\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * Whether an error is a {@link RequestTimeoutError}. Also matches by shape so\n * that a copy of the class from another bundle (ESM/CJS entrypoint) is\n * recognized.\n */\nexport function isRequestTimeoutError(error: unknown): error is RequestTimeoutError {\n return (\n error instanceof RequestTimeoutError ||\n (error instanceof Error &&\n error.name === \"TimeoutError\" &&\n typeof (error as { timeoutMs?: unknown }).timeoutMs === \"number\")\n );\n}\n\n/**\n * Create a service error for timeouts.\n *\n * @param timeoutMs - The configured timeout that elapsed, when known\n * @param cause - The underlying timeout error, when known\n */\nexport function timeoutError(\n service: string,\n timeoutMs?: number,\n cause?: Error\n): ServiceError {\n if (timeoutMs === undefined) {\n return {\n code: ErrorCodes.TIMEOUT,\n message: \"Request timed out.\",\n service,\n ...(cause === undefined ? {} : { cause }),\n };\n }\n return {\n code: ErrorCodes.TIMEOUT,\n message: `Request timed out after ${timeoutMs}ms.`,\n service,\n ...(cause === undefined ? {} : { cause }),\n meta: { timeoutMs },\n };\n}\n\n/**\n * Create a service error for aborted requests.\n */\nexport function abortedError(service: string): ServiceError {\n return {\n code: ErrorCodes.ABORTED,\n message: \"Request was aborted.\",\n service,\n };\n}\n\n/**\n * Create a service error for not found resources.\n */\nexport function notFoundError(\n service: string,\n resource: string\n): ServiceError {\n return {\n code: ErrorCodes.NOT_FOUND,\n message: `Resource not found: ${resource}`,\n service,\n };\n}\n\n/**\n * Create a service error for permission denied.\n */\nexport function permissionDeniedError(\n service: string,\n action: string\n): ServiceError {\n return {\n code: ErrorCodes.PERMISSION_DENIED,\n message: `Permission denied for action: ${action}`,\n service,\n };\n}\n\n/**\n * Parse the server's \"Unauthorized Action: {resource} / {ability}\" pattern.\n */\nexport function parseAuthError(responseText: string): { resource?: string; action?: string } {\n const match = responseText.match(/^Unauthorized Action:\\s*(.+?)\\s*\\/\\s*(tinycloud\\.\\S+)$/m);\n if (match) {\n return { resource: match[1].trim(), action: match[2].trim() };\n }\n return {};\n}\n\n/**\n * Create a service error for unauthorized action (missing capability).\n */\nexport function authUnauthorizedError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return serviceError(ErrorCodes.AUTH_UNAUTHORIZED, message, service, { meta });\n}\n\n/**\n * Authorization verdict read from an error's structure, never its message:\n * - `\"unauthenticated\"`: HTTP 401 — the session is missing or stale.\n * - `\"forbidden\"`: HTTP 403, or `AUTH_UNAUTHORIZED` without a status — the\n * session is valid but lacks the capability.\n * - `\"other\"`: a non-authorization HTTP error status (4xx/5xx).\n */\nexport type AuthorizationVerdict = \"unauthenticated\" | \"forbidden\" | \"other\";\n\nconst grantableActions = new Set(\n CAPABILITY_REGISTRY.map(({ urn }) => urn).filter((urn) => !urn.endsWith(\"/*\"))\n);\n\nconst capabilityServices = new Set(\n CAPABILITY_REGISTRY.map(({ service }) => service.slice(\"tinycloud.\".length))\n);\n\n/**\n * Parse a canonical TinyCloud resource, keeping nested space names intact.\n * The first registered service segment is authoritative: a later matching\n * segment in the path cannot turn a different service into grant advice.\n */\nexport function parseCapabilityResource(\n resource: string,\n service: string\n): { space: string; path: string } | undefined {\n if (\n resource.length === 0 ||\n resource.length > 1024 ||\n !capabilityServices.has(service) ||\n /[\\s\\p{C}*#]/u.test(resource) ||\n resource.includes(\"://\")\n ) return undefined;\n\n const segments = resource.split(\"/\");\n if (!/^tinycloud:[A-Za-z0-9][A-Za-z0-9:._-]*$/.test(segments[0])) return undefined;\n const serviceIndex = segments.findIndex((segment, index) =>\n index > 0 && capabilityServices.has(segment)\n );\n if (serviceIndex < 1 || segments[serviceIndex] !== service) return undefined;\n if (segments.slice(1, serviceIndex).some((segment) =>\n segment.length === 0 || segment === \".\" || segment === \"..\"\n )) return undefined;\n const pathSegments = segments.slice(serviceIndex + 1);\n if (pathSegments.some((segment) => segment === \".\" || segment === \"..\")) return undefined;\n if (service !== \"kv\" && (pathSegments.length === 0 || pathSegments[pathSegments.length - 1] === \"\")) return undefined;\n\n return {\n space: segments.slice(0, serviceIndex).join(\"/\"),\n path: pathSegments.join(\"/\"),\n };\n}\n\n/** A capability resource is a canonical TinyCloud resource or encryption network, never a URL or expression. */\nfunction safeCapabilityResource(value: unknown, service: string, action: string): value is string {\n if (typeof value !== \"string\" || value.length === 0 || value.length > 1024) return false;\n if (service === \"encryption\" && value.startsWith(\"urn:tinycloud:encryption:\")) {\n if (\n !/^[A-Za-z0-9_][A-Za-z0-9_.:/-]*$/.test(value) ||\n value.includes(\"//\") ||\n value.split(\"/\").some((segment) => segment === \".\" || segment === \"..\")\n ) return false;\n try {\n parseNetworkId(value);\n return true;\n } catch {\n return false;\n }\n }\n const parsed = parseCapabilityResource(value, service);\n return parsed !== undefined &&\n (!parsed.path.endsWith(\"/\") || (service === \"kv\" && action === \"tinycloud.kv/list\")) &&\n (parsed.path !== \"\" || (service === \"kv\" && action === \"tinycloud.kv/list\"));\n}\n\n/**\n * Return a grantable missing capability only when typed authorization evidence\n * and both safe, service-matched capability fields are present. Status alone,\n * message text, wildcard actions and unrecognized actions cannot grant scope.\n */\nexport function validatedCapabilityOf(\n error: unknown\n): { resource: string; requiredAction: string } | undefined {\n const verdict = authorizationVerdictOf(error);\n if (verdict !== \"unauthenticated\" && verdict !== \"forbidden\") return undefined;\n\n const seen = new Set<unknown>();\n let node: unknown = error;\n for (\n let depth = 0;\n depth < MAX_CAUSE_DEPTH && typeof node === \"object\" && node !== null && !seen.has(node);\n depth += 1\n ) {\n seen.add(node);\n const record = node as Record<string, unknown>;\n const status = errorStatusOf(record);\n const meta = record.meta;\n if (\n (status === 401 || status === 403 || (status === undefined && record.code === ErrorCodes.AUTH_UNAUTHORIZED)) &&\n typeof meta === \"object\" && meta !== null && !Array.isArray(meta)\n ) {\n const metadata = meta as Record<string, unknown>;\n const { resource, requiredAction } = metadata;\n if (typeof requiredAction === \"string\" && grantableActions.has(requiredAction)) {\n const actionService = requiredAction.slice(\"tinycloud.\".length, requiredAction.indexOf(\"/\"));\n const explicitService = record.service === undefined ? metadata.service : record.service;\n if (\n safeCapabilityResource(resource, actionService, requiredAction) &&\n (explicitService === undefined ||\n explicitService === actionService ||\n explicitService === `tinycloud.${actionService}`)\n ) {\n return { resource, requiredAction };\n }\n }\n }\n node = record.cause;\n }\n return undefined;\n}\n\nconst MAX_CAUSE_DEPTH = 8;\n\n/** The first 4xx/5xx among `status`, `statusCode`, `meta.status`. Success and\n * informational statuses (e.g. a 200 `Response` kept as `cause`) are not\n * failures, so they are skipped rather than ending the search. */\nfunction errorStatusOf(node: Record<string, unknown>): number | undefined {\n const meta = node.meta;\n const candidates = [\n node.status,\n node.statusCode,\n typeof meta === \"object\" && meta !== null\n ? (meta as Record<string, unknown>).status\n : undefined,\n ];\n for (const candidate of candidates) {\n if (\n typeof candidate === \"number\" &&\n Number.isInteger(candidate) &&\n candidate >= 400 &&\n candidate <= 599\n ) {\n return candidate;\n }\n }\n return undefined;\n}\n\n/**\n * Classify an error from its typed HTTP error status (`status`, `statusCode`,\n * or `meta.status`, 4xx/5xx only) and `AUTH_UNAUTHORIZED` code, following the\n * `cause` chain outer-first (at most {@link MAX_CAUSE_DEPTH} links, cycles\n * stop the walk) so wrappers that rethrow with `cause` keep the verdict. The\n * outermost error status decides, so an outer 5xx keeps its retry budget even\n * over an inner 401. Returns `undefined` for untyped errors; only then may\n * callers fall back to message heuristics.\n */\nexport function authorizationVerdictOf(\n error: unknown,\n): AuthorizationVerdict | undefined {\n const seen = new Set<unknown>();\n let sawUnauthorizedCode = false;\n let node: unknown = error;\n for (\n let depth = 0;\n depth < MAX_CAUSE_DEPTH && typeof node === \"object\" && node !== null && !seen.has(node);\n depth += 1\n ) {\n seen.add(node);\n const record = node as Record<string, unknown>;\n const status = errorStatusOf(record);\n if (status === 401) return \"unauthenticated\";\n if (status === 403) return \"forbidden\";\n if (status !== undefined) return \"other\";\n if (record.code === ErrorCodes.AUTH_UNAUTHORIZED) sawUnauthorizedCode = true;\n node = record.cause;\n }\n return sawUnauthorizedCode ? \"forbidden\" : undefined;\n}\n\n/**\n * Create a service error for storage quota exceeded (402 Payment Required).\n */\nexport function storageQuotaExceededError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return {\n code: ErrorCodes.STORAGE_QUOTA_EXCEEDED,\n message,\n service,\n meta,\n };\n}\n\n/**\n * Create a service error for storage limit reached (413 Payload Too Large).\n */\nexport function storageLimitReachedError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return {\n code: ErrorCodes.STORAGE_LIMIT_REACHED,\n message,\n service,\n meta,\n };\n}\n\n/** Shown when a write is rejected because the owner's storage is full. */\nexport const STORAGE_FULL_MESSAGE =\n \"TinyCloud storage is full, so this change was not saved. Reading still works. Free up space or upgrade your plan to save again.\";\n\n/** Shown when a write is larger than the storage the owner has left. */\nexport const STORAGE_WRITE_TOO_LARGE_MESSAGE =\n \"This change is larger than the TinyCloud storage you have left, so it was not saved. Reading still works. Free up space or upgrade your plan to save it.\";\n\n/**\n * Parse the byte counts from a node storage rejection\n * (\"... Used: 155744 bytes, Limit: 0 bytes\"). The limit is the share of the\n * owner's account-wide budget left for this space, so it is 0 when the\n * owner's other spaces already use the whole budget.\n */\nexport function parseStorageQuotaBytes(\n responseText: string\n): { usedBytes: number; limitBytes: number } | undefined {\n const match = responseText.match(/Used:\\s*(\\d+)\\s*bytes,\\s*Limit:\\s*(\\d+)\\s*bytes/i);\n if (!match) return undefined;\n return {\n usedBytes: parseInt(match[1], 10),\n limitBytes: parseInt(match[2], 10),\n };\n}\n\n/**\n * Build the error for a write the node rejected for storage: 402 when storage\n * is already full, 413 when this write is larger than what is left. KV, SQL\n * and DuckDB all report it with the same codes and message so an app can\n * detect \"storage full\" in one place with {@link isStorageFullError}.\n */\nexport function storageRejectionError(\n service: string,\n status: 402 | 413,\n meta: Record<string, unknown>,\n responseText: string\n): ServiceError {\n const quotaMeta = { ...meta, ...parseStorageQuotaBytes(responseText) };\n return status === 402\n ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta)\n : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);\n}\n\n/**\n * True when a write failed for storage: the owner's TinyCloud storage is full\n * (`STORAGE_QUOTA_EXCEEDED`), or this write is larger than what is left\n * (`STORAGE_LIMIT_REACHED`). Reads keep working in both cases, so apps should\n * stay usable read-only and point the user at freeing up space or upgrading.\n */\nexport function isStorageFullError(error: { code?: string } | null | undefined): boolean {\n return (\n error?.code === ErrorCodes.STORAGE_QUOTA_EXCEEDED ||\n error?.code === ErrorCodes.STORAGE_LIMIT_REACHED\n );\n}\n\n/**\n * Wrap an unknown error in a ServiceError.\n */\nexport function wrapError(\n service: string,\n error: unknown,\n defaultCode: string = ErrorCodes.NETWORK_ERROR\n): ServiceError {\n if (error instanceof Error) {\n // A configured request timeout elapsed (see BaseService.createRequestSignal)\n if (isRequestTimeoutError(error)) {\n return timeoutError(service, error.timeoutMs, error);\n }\n\n // Check for abort errors\n if (error.name === \"AbortError\") {\n return abortedError(service);\n }\n\n // Check for timeout errors (varies by platform)\n if (\n error.name === \"TimeoutError\" ||\n error.message.toLowerCase().includes(\"timeout\")\n ) {\n return timeoutError(service);\n }\n\n return {\n code: defaultCode,\n message: error.message,\n service,\n cause: error,\n };\n }\n\n return {\n code: defaultCode,\n message: String(error),\n service,\n };\n}\n\n/**\n * Create an error Result from a ServiceError.\n */\nexport function errorResult(error: ServiceError) {\n return err(error);\n}\n","/**\n * BaseService - Abstract base class for all TinyCloud services.\n *\n * Provides common functionality:\n * - Context management\n * - Session lifecycle hooks\n * - Abort signal handling\n * - Telemetry emission\n */\n\nimport {\n IService,\n IServiceContext,\n ServiceSession,\n ServiceError,\n TelemetryEvents,\n err,\n Result,\n} from \"../types\";\nimport { authRequiredError, wrapError, RequestTimeoutError } from \"../errors\";\nimport type { RequestSignal } from \"./types\";\n\n/** setTimeout's delay is a signed 32-bit integer; larger values fire at once. */\nconst MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/**\n * Abstract base class for TinyCloud services.\n *\n * Services extend this class to get common functionality like\n * context management, session lifecycle, and abort handling.\n *\n * @example\n * ```typescript\n * class MyService extends BaseService implements IMyService {\n * static readonly serviceName = 'myservice';\n *\n * constructor(config: MyServiceConfig = {}) {\n * super();\n * this._config = config;\n * }\n *\n * async doSomething(): Promise<Result<Data>> {\n * if (!this.requireAuth()) {\n * return err(authRequiredError('myservice'));\n * }\n * // ... implementation\n * }\n * }\n * ```\n */\nexport abstract class BaseService implements IService {\n /**\n * Service identifier used for registration.\n * Must be overridden by subclasses.\n */\n static readonly serviceName: string;\n\n /**\n * Service context providing access to platform dependencies.\n * Set during initialize().\n */\n protected context!: IServiceContext;\n\n /**\n * Abort controller for this service's operations.\n * Reset on sign-out.\n */\n protected abortController: AbortController = new AbortController();\n\n /**\n * Service-specific configuration.\n */\n protected _config: Record<string, unknown> = {};\n\n /**\n * Get the service configuration.\n */\n get config(): Record<string, unknown> {\n return this._config;\n }\n\n /**\n * Initialize the service with context.\n * Called by the SDK after instantiation.\n *\n * @param context - The service context\n */\n initialize(context: IServiceContext): void {\n this.context = context;\n }\n\n /**\n * Called when session changes (sign-in, sign-out, refresh).\n * Override in subclasses to handle session changes.\n *\n * @param session - The new session, or null if signed out\n */\n onSessionChange(session: ServiceSession | null): void {\n // Override in subclass if needed\n }\n\n /**\n * Called when SDK signs out.\n * Aborts all pending operations.\n */\n onSignOut(): void {\n this.abortController.abort();\n this.abortController = new AbortController();\n }\n\n /**\n * Get the abort signal for this service.\n * Combines the service-level abort with context-level abort.\n */\n protected get abortSignal(): AbortSignal {\n return this.combineSignals();\n }\n\n /**\n * Check if the service is authenticated.\n */\n protected get isAuthenticated(): boolean {\n return this.context?.isAuthenticated ?? false;\n }\n\n /**\n * Get the current session.\n * Throws if not authenticated.\n */\n protected get session(): ServiceSession {\n if (!this.context?.session) {\n throw new Error(\"Not authenticated\");\n }\n return this.context.session;\n }\n\n /**\n * Check authentication and return error result if not authenticated.\n * Use this at the start of methods that require authentication.\n *\n * @returns true if authenticated, false otherwise\n */\n protected requireAuth(): boolean {\n return this.isAuthenticated;\n }\n\n /**\n * Emit a telemetry event.\n *\n * @param event - Event name\n * @param data - Event data\n */\n protected emit(event: string, data: unknown): void {\n this.context?.emit(event, data);\n }\n\n /**\n * Emit a service request event.\n *\n * @param action - The action being performed\n * @param key - Optional key/path being accessed\n */\n protected emitRequest(action: string, key?: string): void {\n const service = this.getServiceName();\n this.emit(TelemetryEvents.SERVICE_REQUEST, {\n service,\n action,\n span: this.spanName(action),\n key,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Emit a service response event.\n *\n * @param action - The action that was performed\n * @param ok - Whether the request was successful\n * @param startTime - Start time for duration calculation\n * @param status - Optional HTTP status code\n */\n protected emitResponse(\n action: string,\n ok: boolean,\n startTime: number,\n status?: number\n ): void {\n const service = this.getServiceName();\n const durationMs = Date.now() - startTime;\n const span = this.spanName(action);\n this.emit(TelemetryEvents.SERVICE_RESPONSE, {\n service,\n action,\n span,\n ok,\n duration: durationMs,\n durationMs,\n status,\n });\n this.emit(TelemetryEvents.SPAN, {\n span,\n service,\n action,\n ok,\n durationMs,\n status,\n });\n }\n\n /**\n * Emit a service error event.\n *\n * @param error - The service error\n */\n protected emitError(error: ServiceError, action?: string): void {\n const span = action ? this.spanName(action) : undefined;\n this.emit(TelemetryEvents.SERVICE_ERROR, {\n service: this.getServiceName(),\n ...(span ? { span } : {}),\n error,\n });\n }\n\n /**\n * Get the service name from the static property.\n * Subclasses must define static serviceName.\n */\n protected getServiceName(): string {\n return (this.constructor as typeof BaseService).serviceName;\n }\n\n /**\n * Stable span name used by SDK telemetry sinks.\n */\n protected spanName(action: string): string {\n return `sdk.${this.getServiceName()}.${action}`;\n }\n\n /**\n * Create a combined abort signal from multiple sources.\n *\n * @param signals - Additional abort signals to combine\n * @returns A combined abort signal\n */\n protected combineSignals(...signals: (AbortSignal | undefined)[]): AbortSignal {\n const controller = new AbortController();\n const allSignals = [\n this.abortController.signal,\n this.context?.abortSignal,\n ...signals.filter(Boolean),\n ].filter(Boolean) as AbortSignal[];\n\n for (const signal of allSignals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n signal.addEventListener(\"abort\", () => controller.abort(signal.reason), {\n once: true,\n });\n }\n\n return controller.signal;\n }\n\n /**\n * Create the abort signal for one request, bounded by the request timeout.\n *\n * The signal aborts when the service signs out, the context aborts, the\n * caller's `signal` aborts, or the timeout elapses, whichever comes first.\n * A timeout aborts with a {@link RequestTimeoutError} (`TimeoutError`), which\n * `wrapError` maps to `ErrorCodes.TIMEOUT`; the other sources keep their own\n * reason (normally `AbortError`, mapped to `ErrorCodes.ABORTED`).\n *\n * The timeout is `timeoutMs` when given, otherwise `config.timeout`. Only a\n * positive, finite value (at most 2^31-1 ms) applies; `undefined`, `0`, and\n * `Infinity` mean no timeout, so a per-call `0` opts out of a configured one.\n *\n * Callers must call `dispose()` once the response body has been consumed,\n * which clears the timer and removes the listeners this adds to the\n * long-lived service and context signals.\n *\n * @param signal - The caller's abort signal for this request\n * @param timeoutMs - Per-request timeout override in milliseconds\n */\n protected createRequestSignal(\n signal?: AbortSignal,\n timeoutMs?: number\n ): RequestSignal {\n const controller = new AbortController();\n const detachers: Array<() => void> = [];\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const dispose = (): void => {\n if (timer !== undefined) {\n clearTimeout(timer);\n timer = undefined;\n }\n for (const detach of detachers.splice(0)) {\n detach();\n }\n };\n const abort = (reason: unknown): void => {\n dispose();\n controller.abort(reason);\n };\n const requestSignal: RequestSignal = { signal: controller.signal, dispose };\n\n const parents = [\n this.abortController.signal,\n this.context?.abortSignal,\n signal,\n ].filter((parent): parent is AbortSignal => parent !== undefined);\n for (const parent of parents) {\n if (parent.aborted) {\n abort(parent.reason);\n return requestSignal;\n }\n const onAbort = (): void => abort(parent.reason);\n parent.addEventListener(\"abort\", onAbort, { once: true });\n detachers.push(() => parent.removeEventListener(\"abort\", onAbort));\n }\n\n const timeout = timeoutMs ?? this._config.timeout;\n if (\n typeof timeout === \"number\" &&\n timeout > 0 &&\n timeout <= MAX_TIMER_DELAY_MS\n ) {\n timer = setTimeout(() => abort(new RequestTimeoutError(timeout)), timeout);\n }\n\n return requestSignal;\n }\n\n /**\n * Wrap an operation with error handling and telemetry.\n *\n * @param action - The action name for telemetry\n * @param key - Optional key for telemetry\n * @param operation - The operation to execute\n * @returns Result of the operation\n */\n protected async withTelemetry<T>(\n action: string,\n key: string | undefined,\n operation: () => Promise<Result<T>>\n ): Promise<Result<T>> {\n const startTime = Date.now();\n this.emitRequest(action, key);\n\n try {\n const result = await operation();\n\n if (result.ok) {\n this.emitResponse(action, true, startTime);\n } else {\n this.emitResponse(action, false, startTime);\n this.emitError(result.error, action);\n }\n\n return result;\n } catch (error) {\n const serviceError = wrapError(this.getServiceName(), error);\n this.emitResponse(action, false, startTime);\n this.emitError(serviceError, action);\n return err(serviceError);\n }\n }\n}\n","export interface ParsedServiceErrorBody {\n error?: string;\n message?: string;\n code?: string;\n}\n\nexport function parseServiceErrorBody(errorText: string): ParsedServiceErrorBody {\n try {\n return JSON.parse(errorText) as ParsedServiceErrorBody;\n } catch {\n return {};\n }\n}\n\nexport function formatServiceResponseError(\n serviceLabel: string,\n operation: string,\n status: number,\n errorText: string,\n parsed: ParsedServiceErrorBody,\n): string {\n if (parsed.message) {\n return compactMessage(parsed.message);\n }\n\n if (looksLikeHtml(errorText)) {\n if (status === 524 || /524\\s*[:-]/i.test(errorText) || /a timeout occurred/i.test(errorText)) {\n return `${serviceLabel} ${operation} failed: upstream request timed out. Please retry.`;\n }\n return `${serviceLabel} ${operation} failed: upstream service returned an HTML error page (${status}).`;\n }\n\n return `${serviceLabel} ${operation} failed: ${status} - ${compactMessage(errorText)}`;\n}\n\nexport function responseErrorMeta(\n status: number,\n statusText: string,\n errorText: string,\n): Record<string, unknown> {\n const meta: Record<string, unknown> = { status, statusText };\n const snippet = compactMessage(errorText);\n if (snippet) {\n meta.responseSnippet = snippet.slice(0, 300);\n }\n return meta;\n}\n\nfunction looksLikeHtml(text: string): boolean {\n return /<!doctype html/i.test(text) || /<html[\\s>]/i.test(text);\n}\n\nfunction compactMessage(message: string): string {\n const compact = message.replace(/\\s+/g, \" \").trim();\n return compact.length > 500 ? `${compact.slice(0, 500)}...` : compact;\n}\n","import type { Result } from \"../types\";\nimport type { SQLService } from \"./SQLService\";\nimport type { ISQLMigrations } from \"./ISQLService\";\nimport type {\n SqlMigrationApplyOptions,\n SqlMigrationApplyResponse,\n} from \"./types\";\n\nexport class SQLMigrations implements ISQLMigrations {\n constructor(\n private readonly service: SQLService,\n private readonly dbName: string,\n ) {}\n\n apply(options: SqlMigrationApplyOptions): Promise<Result<SqlMigrationApplyResponse>> {\n return this.service.applyMigrationsOnDb(this.dbName, options);\n }\n}\n","/**\n * DatabaseHandle - Handle for operations on a specific named database.\n *\n * Delegates all operations to the parent SQLService with the database name.\n */\n\nimport type { Result } from \"../types\";\nimport type { IDatabaseHandle, ISQLMigrations } from \"./ISQLService\";\nimport type { SQLService } from \"./SQLService\";\nimport { SQLMigrations } from \"./SQLMigrations\";\nimport type {\n SqlValue,\n SqlStatement,\n QueryResponse,\n ExecuteResponse,\n BatchResponse,\n QueryOptions,\n ExecuteOptions,\n BatchOptions,\n} from \"./types\";\n\nexport class DatabaseHandle implements IDatabaseHandle {\n private service: SQLService;\n public readonly name: string;\n public readonly migrations: ISQLMigrations;\n\n constructor(service: SQLService, name: string) {\n this.service = service;\n this.name = name;\n this.migrations = new SQLMigrations(service, name);\n }\n\n async query<T = Record<string, unknown>>(\n sql: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse<T>>> {\n return this.service.queryOnDb<T>(this.name, sql, params, options);\n }\n\n async execute(\n sql: string,\n params?: SqlValue[],\n options?: ExecuteOptions\n ): Promise<Result<ExecuteResponse>> {\n return this.service.executeOnDb(this.name, sql, params, options);\n }\n\n async batch(\n statements: SqlStatement[],\n options?: BatchOptions\n ): Promise<Result<BatchResponse>> {\n return this.service.batchOnDb(this.name, statements, options);\n }\n\n async executeStatement(\n name: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse | ExecuteResponse>> {\n return this.service.executeStatementOnDb(this.name, name, params, options);\n }\n\n async export(options?: QueryOptions): Promise<Result<Blob>> {\n return this.service.exportDb(this.name, options);\n }\n}\n","/**\n * SQL Service Types\n *\n * Type definitions for the SQL service operations.\n */\n\nimport { SQL } from \"@tinycloud/bootstrap\";\n\n/**\n * Configuration for SQLService.\n */\nexport interface SQLServiceConfig {\n /**\n * Default database name.\n * If not set, operations default to \"default\".\n */\n defaultDatabase?: string;\n\n /**\n * Timeout in milliseconds for each SQL request, including reading the\n * response body. When it elapses the request is aborted and the operation\n * returns an `ErrorCodes.TIMEOUT` error. Unset (the default), `0`, or\n * `Infinity` means no timeout.\n */\n timeout?: number;\n\n /** Allow additional config properties */\n [key: string]: unknown;\n}\n\n/**\n * Options for SQL query operations.\n */\nexport interface QueryOptions {\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n\n /** Maximum rows the node may return. Nodes without limit support ignore it. */\n maxRows?: number;\n\n /** Maximum serialized result bytes the node may return. */\n maxBytes?: number;\n}\n\n/**\n * Options for SQL execute operations.\n */\nexport interface ExecuteOptions {\n /**\n * Schema initialization statements (CREATE TABLE IF NOT EXISTS ...).\n * Executed before the main statement on first write.\n */\n schema?: string[];\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for SQL batch operations.\n */\nexport interface BatchOptions {\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * A SQL value: null, number, string, or binary data.\n */\nexport type SqlValue = null | number | string | Uint8Array;\n\n/**\n * A SQL statement with optional parameters.\n */\nexport interface SqlStatement {\n sql: string;\n params?: SqlValue[];\n}\n\n/**\n * Response from SQL query operations.\n */\nexport interface QueryResponse<T = Record<string, unknown>> {\n columns: string[];\n rows: T[][];\n rowCount: number;\n}\n\n/**\n * Response from SQL execute operations.\n */\nexport interface ExecuteResponse {\n changes: number;\n lastInsertRowId: number | null;\n}\n\n/**\n * Response from SQL batch operations.\n */\nexport interface BatchResponse {\n results: ExecuteResponse[];\n}\n\n/**\n * A versioned SQL migration owned by an application or SDK subsystem.\n */\nexport interface SqlMigration {\n /**\n * Stable migration id within the namespace, e.g. \"001_initial\".\n */\n id: string;\n\n /**\n * SQL statements to apply when this migration has not been recorded.\n * Statements should be idempotent where SQLite supports it.\n */\n sql: Array<string | SqlStatement>;\n}\n\n/**\n * Options for applying SQL migrations.\n */\nexport interface SqlMigrationApplyOptions {\n /**\n * Namespace for this migration set, usually an app id or SDK subsystem.\n */\n namespace: string;\n\n /**\n * Ordered migration list.\n */\n migrations: SqlMigration[];\n\n /**\n * Custom abort signal for migration operations.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Result from applying SQL migrations.\n */\nexport interface SqlMigrationApplyResponse {\n database: string;\n namespace: string;\n status: \"already_current\" | \"applied\";\n applied: string[];\n skipped: string[];\n}\n\n/**\n * SQL service action types.\n *\n * URNs derive from the canonical capability registry in `@tinycloud/bootstrap`\n * (TC-112 single source of truth, vendored from tinycloud-node). `insert`,\n * `update`, and `delete` were dropped — zero client refs and never accepted\n * node-side. `select` is a deprecated alias of `read`. `execute`/`export` are\n * request-kind artifacts, not grantable capabilities: the node routes them by\n * request-body kind, so they are absent from the registry but kept here as the\n * invocation ability the client sends today (wire alignment tracked in TC-114).\n */\nexport const SQLAction = {\n READ: SQL.READ,\n WRITE: SQL.WRITE,\n SCHEMA: SQL.SCHEMA,\n ADMIN: SQL.ADMIN,\n SELECT: SQL.SELECT,\n EXECUTE: SQL.EXECUTE,\n EXPORT: SQL.EXPORT,\n ALL: SQL.ALL,\n} as const;\n\nexport type SQLActionType = (typeof SQLAction)[keyof typeof SQLAction];\n","/**\n * SQLService - SQL database service implementation.\n *\n * Platform-agnostic SQL service that works with both web-sdk and node-sdk.\n * Uses dependency injection via IServiceContext for platform dependencies.\n */\n\nimport { BaseService } from \"../base/BaseService\";\nimport {\n Result,\n ok,\n err,\n ErrorCodes,\n serviceError,\n type FetchResponse,\n type ServiceHeaders,\n type ServiceSession,\n} from \"../types\";\nimport {\n authRequiredError,\n wrapError,\n parseAuthError,\n storageRejectionError,\n} from \"../errors\";\nimport {\n formatServiceResponseError,\n parseServiceErrorBody,\n responseErrorMeta,\n} from \"../responseErrors\";\nimport type { ISQLService } from \"./ISQLService\";\nimport type { IDatabaseHandle } from \"./ISQLService\";\nimport { DatabaseHandle } from \"./DatabaseHandle\";\nimport {\n type SQLServiceConfig,\n type QueryOptions,\n type ExecuteOptions,\n type BatchOptions,\n type SqlValue,\n type SqlStatement,\n type QueryResponse,\n type ExecuteResponse,\n type BatchResponse,\n type SqlMigration,\n type SqlMigrationApplyOptions,\n type SqlMigrationApplyResponse,\n SQLAction,\n} from \"./types\";\n\nconst DDL_TOKENS = new Set([\"alter\", \"create\", \"drop\"]);\nconst MIGRATIONS_TABLE = \"__tinycloud_sql_migrations\";\nconst MIGRATIONS_SCHEMA = `CREATE TABLE IF NOT EXISTS ${MIGRATIONS_TABLE} (\n key TEXT PRIMARY KEY,\n namespace TEXT NOT NULL,\n id TEXT NOT NULL,\n applied_at TEXT NOT NULL,\n statement_count INTEGER NOT NULL\n)`;\nconst MIGRATIONS_META_NAMESPACE = \"tinycloud.sql.migrations\";\nconst MIGRATIONS_META_ID = \"000_metadata\";\n\nexport class SQLService extends BaseService implements ISQLService {\n static readonly serviceName = \"sql\";\n\n declare protected _config: SQLServiceConfig;\n private readonly migrationLocks = new Map<string, Promise<Result<SqlMigrationApplyResponse>>>();\n\n constructor(config: SQLServiceConfig = {}) {\n super();\n this._config = config;\n }\n\n get config(): SQLServiceConfig {\n return this._config;\n }\n\n private get defaultDbName(): string {\n return this._config.defaultDatabase ?? \"default\";\n }\n\n private get host(): string {\n return this.context.hosts[0];\n }\n\n /**\n * Get a handle to a named database.\n */\n db(name?: string): IDatabaseHandle {\n return new DatabaseHandle(this, name ?? this.defaultDbName);\n }\n\n /**\n * Shortcut: query the default database.\n */\n async query<T = Record<string, unknown>>(\n sql: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse<T>>> {\n return this.queryOnDb<T>(this.defaultDbName, sql, params, options);\n }\n\n /**\n * Shortcut: execute on the default database.\n */\n async execute(\n sql: string,\n params?: SqlValue[],\n options?: ExecuteOptions\n ): Promise<Result<ExecuteResponse>> {\n return this.executeOnDb(this.defaultDbName, sql, params, options);\n }\n\n /**\n * Shortcut: batch on the default database.\n */\n async batch(\n statements: SqlStatement[],\n options?: BatchOptions\n ): Promise<Result<BatchResponse>> {\n return this.batchOnDb(this.defaultDbName, statements, options);\n }\n\n async applyMigrationsOnDb(\n dbName: string,\n options: SqlMigrationApplyOptions,\n ): Promise<Result<SqlMigrationApplyResponse>> {\n const validationError = validateMigrationOptions(options);\n if (validationError) {\n return err(serviceError(ErrorCodes.INVALID_INPUT, validationError, \"sql\"));\n }\n\n const lockKey = `${dbName}\\0${options.namespace}`;\n const existing = this.migrationLocks.get(lockKey);\n if (existing) {\n return existing;\n }\n\n const promise = this.applyMigrationsOnDbUnlocked(dbName, options);\n this.migrationLocks.set(lockKey, promise);\n try {\n return await promise;\n } finally {\n this.migrationLocks.delete(lockKey);\n }\n }\n\n // === Internal methods called by DatabaseHandle ===\n\n async queryOnDb<T = Record<string, unknown>>(\n dbName: string,\n sql: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse<T>>> {\n return this.withTelemetry(\"query\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n const request = this.createRequestSignal(options?.signal);\n try {\n const body: Record<string, unknown> = {\n action: \"query\",\n sql,\n params: serializeSqlValues(params ?? []),\n };\n if (options?.maxRows !== undefined) body.maxRows = options.maxRows;\n if (options?.maxBytes !== undefined) body.maxBytes = options.maxBytes;\n\n const action = this.actionForSql(sql, SQLAction.READ);\n const response = await this.invokeSQL(\n dbName,\n action,\n body,\n request.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"query\", dbName, [action]);\n }\n\n const data = (await response.json()) as QueryResponse<T>;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n } finally {\n request.dispose();\n }\n });\n }\n\n async executeOnDb(\n dbName: string,\n sql: string,\n params?: SqlValue[],\n options?: ExecuteOptions\n ): Promise<Result<ExecuteResponse>> {\n return this.withTelemetry(\"execute\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n const request = this.createRequestSignal(options?.signal);\n try {\n const body: Record<string, unknown> = {\n action: \"execute\",\n sql,\n params: serializeSqlValues(params ?? []),\n };\n if (options?.schema) {\n body.schema = options.schema;\n }\n\n const actions = [\n this.actionForSql(sql, SQLAction.WRITE),\n ...(options?.schema ?? []).map((statement) =>\n this.actionForSql(statement, SQLAction.SCHEMA),\n ),\n ];\n const requestedActions = this.dedupeActions(actions);\n const response = await this.invokeSQL(\n dbName,\n requestedActions,\n body,\n request.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"execute\", dbName, requestedActions);\n }\n\n const data = (await response.json()) as ExecuteResponse;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n } finally {\n request.dispose();\n }\n });\n }\n\n async batchOnDb(\n dbName: string,\n statements: SqlStatement[],\n options?: BatchOptions\n ): Promise<Result<BatchResponse>> {\n return this.withTelemetry(\"batch\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n const request = this.createRequestSignal(options?.signal);\n try {\n const requestedActions = this.actionsForSqlBatch(statements);\n const response = await this.invokeSQL(\n dbName,\n requestedActions,\n { action: \"batch\", statements },\n request.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"batch\", dbName, requestedActions);\n }\n\n const data = (await response.json()) as BatchResponse;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n } finally {\n request.dispose();\n }\n });\n }\n\n async executeStatementOnDb(\n dbName: string,\n name: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse | ExecuteResponse>> {\n return this.withTelemetry(\"executeStatement\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n const request = this.createRequestSignal(options?.signal);\n try {\n // TC-114: mint the dispatchable ability, not the literal method name.\n // The node has no `tinycloud.sql/execute` capability — it routes an\n // `execute_statement` request by body kind and classifies write-class\n // from the RESOLVED named statement's SQL\n // (tinycloud-node-server/src/routes/mod.rs:1168-1171). A named statement\n // may mutate, and the SQL parser rejects writes under a read/select\n // ability (tinycloud-core/src/sql/parser.rs:135) while permitting reads\n // under write (write ⊇ read), so `write` is the ability that works for\n // both read-only and mutating statements and is contained in a normal\n // read+write delegation.\n const response = await this.invokeSQL(\n dbName,\n SQLAction.WRITE,\n { action: \"execute_statement\", name, params: params ?? [] },\n request.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"executeStatement\", dbName, [SQLAction.WRITE]);\n }\n\n const data = (await response.json()) as\n | QueryResponse\n | ExecuteResponse;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n } finally {\n request.dispose();\n }\n });\n }\n\n async exportDb(\n dbName: string,\n options?: QueryOptions\n ): Promise<Result<Blob>> {\n return this.withTelemetry(\"export\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n const request = this.createRequestSignal(options?.signal);\n try {\n // TC-114: mint the dispatchable ability, not the literal method name.\n // The node has no `tinycloud.sql/export` capability — it routes an\n // `export` request by body kind and authorizes it as a READ\n // (`SqlRequest::Export` is non-write:\n // tinycloud-node-server/src/routes/mod.rs:1172, see comment :1155).\n const response = await this.invokeSQL(\n dbName,\n SQLAction.READ,\n { action: \"export\" },\n request.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"export\", dbName, [SQLAction.READ]);\n }\n\n // FetchResponse doesn't expose blob(), so access it from the\n // underlying response which is a standard fetch Response at runtime\n const resp = response as any;\n if (typeof resp.blob === \"function\") {\n const blob = await resp.blob();\n return ok(blob as Blob);\n }\n // If blob() is not available, return the raw text as a Blob-like\n const text = await response.text();\n return ok(text as unknown as Blob);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n } finally {\n request.dispose();\n }\n });\n }\n\n private async applyMigrationsOnDbUnlocked(\n dbName: string,\n options: SqlMigrationApplyOptions,\n ): Promise<Result<SqlMigrationApplyResponse>> {\n return this.withTelemetry(\"migrations.apply\", dbName, async () => {\n // Read before writing: an up-to-date database must open with a single\n // read, because writes fail once the owner's storage is full (402) and\n // read-only sessions cannot write at all.\n const listed = await this.queryOnDb<{ id: string }>(\n dbName,\n `SELECT id FROM ${MIGRATIONS_TABLE} WHERE namespace = ? ORDER BY applied_at, id`,\n [options.namespace],\n { signal: options.signal },\n );\n // A node may answer for a database that was never created with either\n // \"no such table\" or \"database not found\"; both mean nothing is applied.\n const tableMissing =\n !listed.ok &&\n (listed.error.code === ErrorCodes.SQL_DATABASE_NOT_FOUND ||\n isMissingTableError(listed.error.message));\n if (!listed.ok && !tableMissing) return listed;\n\n const appliedIds = new Set(\n (listed.ok ? listed.data.rows : [])\n .map((row) => rowValue(row, 0))\n .filter((id): id is string => typeof id === \"string\"),\n );\n const skipped = options.migrations\n .filter((migration) => appliedIds.has(migration.id))\n .map((migration) => migration.id);\n const pending = options.migrations.filter((migration) => !appliedIds.has(migration.id));\n const applied: string[] = [];\n\n if (pending.length > 0 && tableMissing) {\n const created = await this.ensureMigrationsTable(dbName, options.signal);\n if (!created.ok) return created;\n }\n\n for (const migration of pending) {\n const result = await this.applyOneMigration(dbName, options.namespace, migration, options.signal);\n if (!result.ok) return result;\n applied.push(migration.id);\n }\n\n return ok({\n database: dbName,\n namespace: options.namespace,\n status: applied.length > 0 ? \"applied\" : \"already_current\",\n applied,\n skipped,\n });\n });\n }\n\n private async applyOneMigration(\n dbName: string,\n namespace: string,\n migration: SqlMigration,\n signal?: AbortSignal,\n ): Promise<Result<void>> {\n const statements = [\n ...migration.sql.map((statement) =>\n typeof statement === \"string\" ? { sql: statement } : statement,\n ),\n {\n sql: `INSERT OR REPLACE INTO ${MIGRATIONS_TABLE} (key, namespace, id, applied_at, statement_count) VALUES (?, ?, ?, ?, ?)`,\n params: [\n migrationKey(namespace, migration.id),\n namespace,\n migration.id,\n new Date().toISOString(),\n migration.sql.length,\n ],\n },\n ];\n\n const result = await this.batchOnDb(dbName, statements, { signal });\n if (!result.ok) return result;\n return ok(undefined);\n }\n\n private async ensureMigrationsTable(\n dbName: string,\n signal?: AbortSignal,\n ): Promise<Result<void>> {\n const result = await this.batchOnDb(\n dbName,\n [\n { sql: MIGRATIONS_SCHEMA },\n {\n sql: `INSERT OR REPLACE INTO ${MIGRATIONS_TABLE} (key, namespace, id, applied_at, statement_count) VALUES (?, ?, ?, ?, ?)`,\n params: [\n migrationKey(MIGRATIONS_META_NAMESPACE, MIGRATIONS_META_ID),\n MIGRATIONS_META_NAMESPACE,\n MIGRATIONS_META_ID,\n new Date().toISOString(),\n 1,\n ],\n },\n ],\n { signal },\n );\n if (!result.ok) return result;\n return ok(undefined);\n }\n\n // === Private helpers ===\n\n private async invokeSQL(\n dbName: string,\n actions: string | string[],\n body: Record<string, unknown>,\n signal: AbortSignal\n ): Promise<FetchResponse> {\n const session = this.context.session!;\n const actionList = Array.isArray(actions) ? actions : [actions];\n const headers =\n actionList.length === 1\n ? this.context.invoke(session, \"sql\", dbName, actionList[0]!)\n : this.invokeSQLAny(session, dbName, actionList);\n\n return this.context.fetch(`${this.host}/invoke`, {\n method: \"POST\",\n headers: {\n ...(headers as Record<string, string>),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body) as any,\n signal,\n });\n }\n\n private actionForSql(sql: string, fallback: string): string {\n const token = firstSqlToken(sql);\n if (token === \"pragma\") return SQLAction.ADMIN;\n if (token !== undefined && DDL_TOKENS.has(token)) return SQLAction.SCHEMA;\n return fallback;\n }\n\n private actionsForSqlBatch(statements: SqlStatement[]): string[] {\n return this.dedupeActions(\n statements.map((statement) => this.actionForSql(statement.sql, SQLAction.WRITE)),\n );\n }\n\n private dedupeActions(actions: string[]): string[] {\n return [...new Set(actions)];\n }\n\n private invokeSQLAny(\n session: ServiceSession,\n dbName: string,\n actions: string[],\n ): ServiceHeaders {\n if (!this.context.invokeAny) {\n throw new Error(\n `SQL operation requires multiple permissions (${actions.join(\", \")}) but this SDK runtime does not support multi-resource invocations`,\n );\n }\n\n return this.context.invokeAny(\n session,\n actions.map((action) => ({\n spaceId: session.spaceId,\n service: \"sql\",\n path: dbName,\n action,\n })),\n );\n }\n\n private async handleErrorResponse(\n response: FetchResponse,\n operation: string,\n dbName: string,\n requestedActions: readonly string[],\n ): Promise<Result<never>> {\n const errorText = await response.text();\n const meta = responseErrorMeta(response.status, response.statusText, errorText);\n\n // The node answers a write-class request on a full space with 402.\n if (response.status === 402) {\n return err(storageRejectionError(\"sql\", 402, meta, errorText));\n }\n\n const errorBody = parseServiceErrorBody(errorText);\n\n const errorCode = this.mapHttpStatusToErrorCode(\n response.status,\n errorBody.error\n );\n const message = formatServiceResponseError(\n \"SQL\",\n operation,\n response.status,\n errorText,\n errorBody,\n );\n\n if (response.status === 401) {\n const { action } = parseAuthError(errorText);\n if (action && requestedActions.includes(action)) {\n meta.requiredAction = action;\n meta.resource = `${this.context.session!.spaceId}/sql/${dbName}`;\n }\n }\n\n return err(\n serviceError(errorCode, message, \"sql\", { meta })\n );\n }\n\n private mapHttpStatusToErrorCode(\n status: number,\n serverError?: string\n ): string {\n switch (status) {\n case 400:\n return ErrorCodes.SQL_ERROR;\n case 401:\n return ErrorCodes.AUTH_UNAUTHORIZED;\n case 403:\n if (serverError === \"sql_readonly_violation\") {\n return ErrorCodes.SQL_READONLY_VIOLATION;\n }\n return ErrorCodes.SQL_PERMISSION_DENIED;\n case 404:\n return ErrorCodes.SQL_DATABASE_NOT_FOUND;\n case 413:\n return ErrorCodes.SQL_RESPONSE_TOO_LARGE;\n case 429:\n return ErrorCodes.SQL_QUOTA_EXCEEDED;\n default:\n return ErrorCodes.NETWORK_ERROR;\n }\n }\n}\n\nfunction firstSqlToken(sql: string): string | undefined {\n let index = 0;\n\n while (index < sql.length) {\n while (index < sql.length && /\\s/.test(sql[index])) {\n index++;\n }\n\n if (sql.startsWith(\"--\", index)) {\n const newline = sql.indexOf(\"\\n\", index + 2);\n if (newline === -1) {\n return undefined;\n }\n index = newline + 1;\n continue;\n }\n\n if (sql.startsWith(\"/*\", index)) {\n const end = sql.indexOf(\"*/\", index + 2);\n if (end === -1) {\n return undefined;\n }\n index = end + 2;\n continue;\n }\n\n break;\n }\n\n const match = /^[A-Za-z_]+/.exec(sql.slice(index));\n return match?.[0].toLowerCase();\n}\n\nfunction validateMigrationOptions(options: SqlMigrationApplyOptions): string | null {\n if (!options || typeof options !== \"object\") {\n return \"SQL migrations require options\";\n }\n if (!options.namespace || typeof options.namespace !== \"string\") {\n return \"SQL migrations require a non-empty namespace\";\n }\n if (!Array.isArray(options.migrations)) {\n return \"SQL migrations require an ordered migrations array\";\n }\n\n const ids = new Set<string>();\n for (const migration of options.migrations) {\n if (!migration.id || typeof migration.id !== \"string\") {\n return \"SQL migrations require every migration to have a non-empty id\";\n }\n if (ids.has(migration.id)) {\n return `Duplicate SQL migration id: ${migration.id}`;\n }\n ids.add(migration.id);\n\n if (!Array.isArray(migration.sql)) {\n return `SQL migration ${migration.id} requires a sql array`;\n }\n for (const statement of migration.sql) {\n if (typeof statement === \"string\") {\n if (statement.trim() === \"\") {\n return `SQL migration ${migration.id} contains an empty statement`;\n }\n continue;\n }\n if (!statement || typeof statement.sql !== \"string\" || statement.sql.trim() === \"\") {\n return `SQL migration ${migration.id} contains an invalid statement`;\n }\n }\n }\n\n return null;\n}\n\nfunction serializeSqlValues(values: SqlValue[]): Array<null | number | string | number[]> {\n return values.map((value) =>\n value instanceof Uint8Array ? Array.from(value) : value,\n );\n}\n\nfunction isMissingTableError(message: string): boolean {\n return /no such table/i.test(message);\n}\n\nfunction migrationKey(namespace: string, id: string): string {\n return `${encodeURIComponent(namespace)}:${encodeURIComponent(id)}`;\n}\n\nfunction rowValue(row: unknown, index: number): unknown {\n if (Array.isArray(row)) return row[index];\n if (row && typeof row === \"object\") {\n const values = Object.values(row as Record<string, unknown>);\n return values[index];\n }\n return undefined;\n}\n"],"mappings":"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","names":["ok","serviceError","import_bootstrap"]}
@@ -1,4 +1,4 @@
1
- import { B as BaseService } from '../BaseService-BhpVpA9b.cjs';
1
+ import { B as BaseService } from '../BaseService-C8zRy0ww.cjs';
2
2
  import { R as Result, I as IService } from '../types-qwMmB7qB.cjs';
3
3
 
4
4
  /**
@@ -16,7 +16,10 @@ interface SQLServiceConfig {
16
16
  */
17
17
  defaultDatabase?: string;
18
18
  /**
19
- * Default timeout in milliseconds for SQL operations.
19
+ * Timeout in milliseconds for each SQL request, including reading the
20
+ * response body. When it elapses the request is aborted and the operation
21
+ * returns an `ErrorCodes.TIMEOUT` error. Unset (the default), `0`, or
22
+ * `Infinity` means no timeout.
20
23
  */
21
24
  timeout?: number;
22
25
  /** Allow additional config properties */
@@ -1,4 +1,4 @@
1
- import { B as BaseService } from '../BaseService-CuV-ZVPs.js';
1
+ import { B as BaseService } from '../BaseService-DU-lE-Qx.js';
2
2
  import { R as Result, I as IService } from '../types-qwMmB7qB.js';
3
3
 
4
4
  /**
@@ -16,7 +16,10 @@ interface SQLServiceConfig {
16
16
  */
17
17
  defaultDatabase?: string;
18
18
  /**
19
- * Default timeout in milliseconds for SQL operations.
19
+ * Timeout in milliseconds for each SQL request, including reading the
20
+ * response body. When it elapses the request is aborted and the operation
21
+ * returns an `ErrorCodes.TIMEOUT` error. Unset (the default), `0`, or
22
+ * `Infinity` means no timeout.
20
23
  */
21
24
  timeout?: number;
22
25
  /** Allow additional config properties */