@tinycloud/sdk-services 2.1.0 → 2.2.0-beta.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/kv/index.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/kv/index.ts
21
21
  var kv_exports = {};
22
22
  __export(kv_exports, {
23
+ DEFAULT_SIGNED_READ_URL_EXPIRY_MS: () => DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
23
24
  KVAction: () => KVAction,
24
25
  KVService: () => KVService,
25
26
  PrefixedKVService: () => PrefixedKVService
@@ -414,6 +415,13 @@ var PrefixedKVService = class _PrefixedKVService {
414
415
  const fullKey = this.getFullKey(key);
415
416
  return this._kv.head(fullKey, { ...options, prefix: "" });
416
417
  }
418
+ /**
419
+ * Create a short-lived signed URL for reading a KV object.
420
+ */
421
+ async createSignedReadUrl(key, options) {
422
+ const fullKey = this.getFullKey(key);
423
+ return this._kv.createSignedReadUrl(fullKey, { ...options, prefix: "" });
424
+ }
417
425
  /**
418
426
  * Create a nested prefix-scoped view.
419
427
  */
@@ -425,6 +433,7 @@ var PrefixedKVService = class _PrefixedKVService {
425
433
  };
426
434
 
427
435
  // src/kv/types.ts
436
+ var DEFAULT_SIGNED_READ_URL_EXPIRY_MS = 5 * 60 * 1e3;
428
437
  var KVAction = {
429
438
  GET: "tinycloud.kv/get",
430
439
  PUT: "tinycloud.kv/put",
@@ -509,6 +518,15 @@ var KVService = class extends BaseService {
509
518
  get host() {
510
519
  return this.context.hosts[0];
511
520
  }
521
+ withJsonContentType(headers) {
522
+ if (Array.isArray(headers)) {
523
+ return [...headers, ["content-type", "application/json"]];
524
+ }
525
+ return {
526
+ ...headers,
527
+ "content-type": "application/json"
528
+ };
529
+ }
512
530
  /**
513
531
  * Execute an invoke operation.
514
532
  *
@@ -578,6 +596,48 @@ var KVService = class extends BaseService {
578
596
  return text;
579
597
  }
580
598
  }
599
+ async createSignedReadUrlError(response, key) {
600
+ let errorText = response.statusText;
601
+ try {
602
+ const text = await response.text();
603
+ if (text) {
604
+ errorText = text;
605
+ }
606
+ } catch {
607
+ }
608
+ if (response.status === 401 || response.status === 403) {
609
+ const { resource, action } = parseAuthError(errorText);
610
+ return err(authUnauthorizedError("kv", errorText, {
611
+ status: response.status,
612
+ ...action && { requiredAction: action },
613
+ ...resource && { resource }
614
+ }));
615
+ }
616
+ const code = response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;
617
+ return err(
618
+ serviceError(
619
+ code,
620
+ `Failed to create signed read URL for key "${key}": ${response.status} - ${errorText}`,
621
+ "kv",
622
+ { meta: { status: response.status, statusText: response.statusText } }
623
+ )
624
+ );
625
+ }
626
+ normalizeSignedReadUrlResponse(data) {
627
+ if (!data || typeof data !== "object") {
628
+ return void 0;
629
+ }
630
+ const response = data;
631
+ if (typeof response.url !== "string" || typeof response.ticketId !== "string" || typeof response.expiresAt !== "string") {
632
+ return void 0;
633
+ }
634
+ return {
635
+ url: new URL(response.url, this.host).toString(),
636
+ relativeUrl: response.url,
637
+ ticketId: response.ticketId,
638
+ expiresAt: response.expiresAt
639
+ };
640
+ }
581
641
  /**
582
642
  * Get a value by key.
583
643
  */
@@ -850,6 +910,61 @@ var KVService = class extends BaseService {
850
910
  }
851
911
  });
852
912
  }
913
+ /**
914
+ * Create a short-lived signed URL for reading a KV object.
915
+ */
916
+ async createSignedReadUrl(key, options) {
917
+ return this.withTelemetry("createSignedReadUrl", key, async () => {
918
+ if (!this.requireAuth()) {
919
+ return err(authRequiredError("kv"));
920
+ }
921
+ const path = this.getFullPath(key, options?.prefix);
922
+ const session = this.context.session;
923
+ const headers = this.context.invoke(
924
+ session,
925
+ "kv",
926
+ path,
927
+ KVAction.GET
928
+ );
929
+ const body = {
930
+ space: session.spaceId,
931
+ path,
932
+ ttl_seconds: options?.expiresInSeconds ?? Math.ceil(DEFAULT_SIGNED_READ_URL_EXPIRY_MS / 1e3)
933
+ };
934
+ if (options?.contentHash !== void 0) {
935
+ body.content_hash = options.contentHash;
936
+ }
937
+ if (options?.etag !== void 0) {
938
+ body.etag = options.etag;
939
+ }
940
+ try {
941
+ const response = await this.context.fetch(`${this.host}/signed/kv`, {
942
+ method: "POST",
943
+ headers: this.withJsonContentType(headers),
944
+ body: JSON.stringify(body),
945
+ signal: this.combineSignals(options?.signal)
946
+ });
947
+ if (!response.ok) {
948
+ return this.createSignedReadUrlError(response, key);
949
+ }
950
+ const signedUrl = this.normalizeSignedReadUrlResponse(
951
+ await response.json()
952
+ );
953
+ if (!signedUrl) {
954
+ return err(
955
+ serviceError(
956
+ ErrorCodes.NETWORK_ERROR,
957
+ "Signed read URL response did not include url, ticketId, and expiresAt",
958
+ "kv"
959
+ )
960
+ );
961
+ }
962
+ return ok(signedUrl);
963
+ } catch (error) {
964
+ return err(wrapError("kv", error));
965
+ }
966
+ });
967
+ }
853
968
  /**
854
969
  * Create a prefix-scoped view of this KV service.
855
970
  *
@@ -902,6 +1017,7 @@ var KVService = class extends BaseService {
902
1017
  KVService.serviceName = "kv";
903
1018
  // Annotate the CommonJS export names for ESM import in node:
904
1019
  0 && (module.exports = {
1020
+ DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
905
1021
  KVAction,
906
1022
  KVService,
907
1023
  PrefixedKVService
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/kv/index.ts","../../src/types.ts","../../src/errors.ts","../../src/base/BaseService.ts","../../src/kv/PrefixedKVService.ts","../../src/kv/types.ts","../../src/kv/KVService.ts"],"sourcesContent":["/**\n * KV Service Exports\n *\n * Key-Value storage service for TinyCloud SDK.\n */\n\n// Service implementation\nexport { KVService } from \"./KVService\";\n\n// Prefixed service implementation\nexport { PrefixedKVService, IPrefixedKVService } from \"./PrefixedKVService\";\n\n// Interface\nexport { IKVService } from \"./IKVService\";\n\n// Types\nexport {\n KVServiceConfig,\n KVGetOptions,\n KVPutOptions,\n KVListOptions,\n KVDeleteOptions,\n KVHeadOptions,\n KVResponse,\n KVListResponse,\n KVResponseHeaders,\n KVAction,\n KVActionType,\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 * 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\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 spaceId: string;\n service: string;\n path: string;\n action: string;\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 | string;\n signal?: AbortSignal;\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 * 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 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 ok: boolean;\n duration: number;\n status?: number;\n}\n\n/**\n * Event emitted on service error.\n */\nexport interface ServiceErrorEvent {\n service: 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 * Telemetry event names.\n */\nexport const TelemetryEvents = {\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 { ServiceError, ErrorCodes, err, serviceError } from \"./types\";\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 * 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/**\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.abortController.signal;\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 this.emit(TelemetryEvents.SERVICE_REQUEST, {\n service: this.getServiceName(),\n 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 this.emit(TelemetryEvents.SERVICE_RESPONSE, {\n service: this.getServiceName(),\n action,\n ok,\n duration: Date.now() - startTime,\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): void {\n this.emit(TelemetryEvents.SERVICE_ERROR, {\n service: this.getServiceName(),\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 * 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 = [this.abortSignal, ...signals.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);\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);\n return err(serviceError);\n }\n }\n}\n","/**\n * PrefixedKVService - A prefix-scoped view of KVService.\n *\n * Provides key-value operations scoped to a specific prefix.\n * All operations automatically prefix keys, enabling app data isolation\n * within a shared space.\n *\n * @example\n * ```typescript\n * const space = sdk.space('default');\n *\n * // Create prefix-scoped views\n * const myApp = space.kv.withPrefix('/app.myapp.com');\n * const sharedPhotos = space.kv.withPrefix('/photos');\n *\n * // Operations are automatically prefixed\n * await myApp.put('settings.json', { theme: 'dark' });\n * // -> Actually writes to: /app.myapp.com/settings.json\n *\n * await myApp.get('settings.json');\n * // -> Actually reads from: /app.myapp.com/settings.json\n *\n * await sharedPhotos.list();\n * // -> Lists: /photos/*\n *\n * // Nested prefixes\n * const settings = myApp.withPrefix('/settings');\n * await settings.get('theme.json'); // -> /app.myapp.com/settings/theme.json\n * ```\n */\n\nimport { Result } from \"../types\";\nimport {\n KVGetOptions,\n KVPutOptions,\n KVListOptions,\n KVDeleteOptions,\n KVHeadOptions,\n KVResponse,\n KVListResponse,\n} from \"./types\";\n\n/**\n * Interface for prefixed KV operations.\n *\n * Provides the same operations as IKVService but scoped to a prefix.\n * Supports nested prefixes via withPrefix().\n */\nexport interface IPrefixedKVService {\n /**\n * The current prefix for this scoped view.\n */\n readonly prefix: string;\n\n /**\n * Get a value by key.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to retrieve (will be prefixed)\n * @param options - Optional get configuration\n * @returns Result with the stored value and headers\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * const result = await myApp.get('settings.json');\n * // -> Reads from: /app.myapp.com/settings.json\n * ```\n */\n get<T = unknown>(\n key: string,\n options?: Omit<KVGetOptions, 'prefix'>\n ): Promise<Result<KVResponse<T>>>;\n\n /**\n * Store a value at a key.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to store under (will be prefixed)\n * @param value - The value to store\n * @param options - Optional put configuration\n * @returns Result indicating success/failure\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * await myApp.put('settings.json', { theme: 'dark' });\n * // -> Stores at: /app.myapp.com/settings.json\n * ```\n */\n put(\n key: string,\n value: unknown,\n options?: Omit<KVPutOptions, 'prefix'>\n ): Promise<Result<KVResponse<void>>>;\n\n /**\n * List keys within this prefix.\n *\n * Returns keys that match the prefix, with keys returned relative\n * to the prefix when removePrefix is true (default for prefixed service).\n *\n * @param options - Optional list configuration\n * @returns Result with array of matching keys\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * const result = await myApp.list();\n * // -> Lists keys under: /app.myapp.com/*\n * // Returns: ['settings.json', 'data/user.json', ...]\n * ```\n */\n list(options?: Omit<KVListOptions, 'prefix'>): Promise<Result<KVListResponse>>;\n\n /**\n * Delete a key.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to delete (will be prefixed)\n * @param options - Optional delete configuration\n * @returns Result indicating success/failure\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * await myApp.delete('old-settings.json');\n * // -> Deletes: /app.myapp.com/old-settings.json\n * ```\n */\n delete(key: string, options?: Omit<KVDeleteOptions, 'prefix'>): Promise<Result<void>>;\n\n /**\n * Get metadata for a key without retrieving the value.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to check (will be prefixed)\n * @param options - Optional head configuration\n * @returns Result with headers only\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * const result = await myApp.head('large-file.bin');\n * // -> Gets metadata for: /app.myapp.com/large-file.bin\n * ```\n */\n head(key: string, options?: Omit<KVHeadOptions, 'prefix'>): Promise<Result<KVResponse<void>>>;\n\n /**\n * Create a nested prefix-scoped view.\n *\n * The subPrefix is appended to the current prefix.\n *\n * @param subPrefix - The sub-prefix to append\n * @returns A new PrefixedKVService with the combined prefix\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * const settings = myApp.withPrefix('/settings');\n * await settings.get('theme.json');\n * // -> Reads from: /app.myapp.com/settings/theme.json\n * ```\n */\n withPrefix(subPrefix: string): IPrefixedKVService;\n}\n\n/**\n * Interface for a KV service that supports prefix delegation.\n *\n * This is the subset of IKVService methods needed by PrefixedKVService.\n */\ninterface IKVServiceLike {\n get<T = unknown>(\n key: string,\n options?: KVGetOptions\n ): Promise<Result<KVResponse<T>>>;\n\n put(\n key: string,\n value: unknown,\n options?: KVPutOptions\n ): Promise<Result<KVResponse<void>>>;\n\n list(options?: KVListOptions): Promise<Result<KVListResponse>>;\n\n delete(key: string, options?: KVDeleteOptions): Promise<Result<void>>;\n\n head(key: string, options?: KVHeadOptions): Promise<Result<KVResponse<void>>>;\n}\n\n/**\n * PrefixedKVService - Implementation of prefix-scoped KV operations.\n *\n * This class wraps a KVService (or another PrefixedKVService) and\n * automatically prefixes all key operations with the configured prefix.\n *\n * ## Prefix Convention\n *\n * | Pattern | Use Case | Example |\n * | -- | -- | -- |\n * | `/app.{domain}/` | App-private data | `/app.photos.xyz/settings.json` |\n * | `/{type}/` | Shared data type | `/photos/vacation.jpg` |\n * | `/.{name}/` | Hidden/system data | `/.cache/thumbnails/` |\n * | `/public/` | Explicitly shareable | `/public/profile.json` |\n *\n * @example\n * ```typescript\n * // Create from KVService\n * const prefixed = new PrefixedKVService(kvService, '/app.myapp.com');\n *\n * // Or use the withPrefix factory method on KVService\n * const prefixed = kvService.withPrefix('/app.myapp.com');\n *\n * // All operations are automatically prefixed\n * await prefixed.put('settings.json', { theme: 'dark' });\n * await prefixed.get('settings.json');\n *\n * // Nested prefixes\n * const nested = prefixed.withPrefix('/settings');\n * await nested.get('theme.json'); // -> /app.myapp.com/settings/theme.json\n * ```\n */\nexport class PrefixedKVService implements IPrefixedKVService {\n /**\n * The underlying KV service.\n */\n private readonly _kv: IKVServiceLike;\n\n /**\n * The prefix for this scoped view.\n */\n private readonly _prefix: string;\n\n /**\n * Create a new PrefixedKVService.\n *\n * @param kv - The underlying KV service to delegate to\n * @param prefix - The prefix to apply to all operations\n */\n constructor(kv: IKVServiceLike, prefix: string) {\n this._kv = kv;\n // Normalize prefix: ensure it doesn't end with slash\n this._prefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n }\n\n /**\n * The current prefix for this scoped view.\n */\n get prefix(): string {\n return this._prefix;\n }\n\n /**\n * Compute the full key path by combining prefix and key.\n *\n * @param key - The key to prefix\n * @returns The full path including prefix\n */\n private getFullKey(key: string): string {\n // Handle keys that start with slash\n const normalizedKey = key.startsWith('/') ? key : `/${key}`;\n return `${this._prefix}${normalizedKey}`;\n }\n\n /**\n * Get a value by key.\n */\n async get<T = unknown>(\n key: string,\n options?: Omit<KVGetOptions, 'prefix'>\n ): Promise<Result<KVResponse<T>>> {\n const fullKey = this.getFullKey(key);\n // Use empty prefix override to use the full key as-is\n return this._kv.get<T>(fullKey, { ...options, prefix: '' });\n }\n\n /**\n * Store a value at a key.\n */\n async put(\n key: string,\n value: unknown,\n options?: Omit<KVPutOptions, 'prefix'>\n ): Promise<Result<KVResponse<void>>> {\n const fullKey = this.getFullKey(key);\n return this._kv.put(fullKey, value, { ...options, prefix: '' });\n }\n\n /**\n * List keys within this prefix.\n */\n async list(options?: Omit<KVListOptions, 'prefix'>): Promise<Result<KVListResponse>> {\n // List uses the prefix directly, and by default removes the prefix from results\n const removePrefix = options?.removePrefix ?? true;\n return this._kv.list({\n ...options,\n prefix: this._prefix,\n removePrefix,\n });\n }\n\n /**\n * Delete a key.\n */\n async delete(key: string, options?: Omit<KVDeleteOptions, 'prefix'>): Promise<Result<void>> {\n const fullKey = this.getFullKey(key);\n return this._kv.delete(fullKey, { ...options, prefix: '' });\n }\n\n /**\n * Get metadata for a key without retrieving the value.\n */\n async head(\n key: string,\n options?: Omit<KVHeadOptions, 'prefix'>\n ): Promise<Result<KVResponse<void>>> {\n const fullKey = this.getFullKey(key);\n return this._kv.head(fullKey, { ...options, prefix: '' });\n }\n\n /**\n * Create a nested prefix-scoped view.\n */\n withPrefix(subPrefix: string): IPrefixedKVService {\n // Normalize subPrefix\n const normalizedSubPrefix = subPrefix.startsWith('/')\n ? subPrefix\n : `/${subPrefix}`;\n const combinedPrefix = `${this._prefix}${normalizedSubPrefix}`;\n return new PrefixedKVService(this._kv, combinedPrefix);\n }\n}\n","/**\n * KV Service Types\n *\n * Type definitions for the KV (Key-Value) service operations.\n */\n\n/**\n * Configuration for KVService.\n */\nexport interface KVServiceConfig {\n /**\n * Default prefix for all keys.\n * Useful for namespacing data within a space.\n *\n * @example\n * ```typescript\n * const kv = new KVService({ prefix: 'myapp/settings' });\n * await kv.put('theme', 'dark'); // Stores at 'myapp/settings/theme'\n * ```\n */\n prefix?: string;\n\n /**\n * Default timeout in milliseconds for KV operations.\n * Overrides the context-level timeout if set.\n */\n timeout?: number;\n\n /** Allow additional config properties */\n [key: string]: unknown;\n}\n\n/**\n * Options for KV get operations.\n */\nexport interface KVGetOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Return raw response instead of parsed JSON.\n * When true, data will be the raw response text.\n */\n raw?: boolean;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for KV put operations.\n */\nexport interface KVPutOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Content type for the value.\n * Defaults to 'application/json' for objects.\n */\n contentType?: string;\n\n /**\n * Custom metadata headers to store with the value.\n */\n metadata?: Record<string, string>;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for KV list operations.\n */\nexport interface KVListOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Additional path to append to the prefix.\n */\n path?: string;\n\n /**\n * Whether to remove the prefix from returned keys.\n * When true, keys are returned relative to the prefix.\n */\n removePrefix?: boolean;\n\n /**\n * Return raw response instead of parsed JSON.\n */\n raw?: boolean;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for KV delete operations.\n */\nexport interface KVDeleteOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for KV head (metadata) operations.\n */\nexport interface KVHeadOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Response headers from KV operations.\n */\nexport interface KVResponseHeaders {\n /**\n * ETag for conditional requests.\n */\n etag?: string;\n\n /**\n * Content type of the stored value.\n */\n contentType?: string;\n\n /**\n * Last modification timestamp.\n */\n lastModified?: string;\n\n /**\n * Content length in bytes.\n */\n contentLength?: number;\n\n /**\n * Get a header value by name.\n * @param name - Header name (case-insensitive)\n */\n get(name: string): string | null;\n}\n\n/**\n * Response from KV get/put operations.\n *\n * @template T - Type of the data payload\n */\nexport interface KVResponse<T = unknown> {\n /**\n * The data payload.\n * For get: the stored value.\n * For put: undefined.\n */\n data: T;\n\n /**\n * Response headers with metadata.\n */\n headers: KVResponseHeaders;\n}\n\n/**\n * Response from KV list operations.\n */\nexport interface KVListResponse {\n /**\n * Array of keys matching the list criteria.\n */\n keys: string[];\n}\n\n/**\n * KV service action types.\n */\nexport const KVAction = {\n GET: \"tinycloud.kv/get\",\n PUT: \"tinycloud.kv/put\",\n LIST: \"tinycloud.kv/list\",\n DELETE: \"tinycloud.kv/del\",\n HEAD: \"tinycloud.kv/metadata\",\n} as const;\n\nexport type KVActionType = (typeof KVAction)[keyof typeof KVAction];\n","/**\n * KVService - Key-Value storage service implementation.\n *\n * Platform-agnostic KV 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 FetchResponse,\n} from \"../types\";\nimport {\n authRequiredError,\n wrapError,\n storageQuotaExceededError,\n storageLimitReachedError,\n parseAuthError,\n authUnauthorizedError,\n} from \"../errors\";\nimport { IKVService } from \"./IKVService\";\nimport { PrefixedKVService, IPrefixedKVService } from \"./PrefixedKVService\";\nimport {\n KVServiceConfig,\n KVGetOptions,\n KVPutOptions,\n KVListOptions,\n KVDeleteOptions,\n KVHeadOptions,\n KVResponse,\n KVListResponse,\n KVResponseHeaders,\n KVAction,\n} from \"./types\";\n\n/**\n * KV service implementation.\n *\n * Provides key-value storage operations using TinyCloud's KV API.\n * Uses the Result type pattern for explicit error handling.\n *\n * @example\n * ```typescript\n * // Register with SDK\n * const sdk = new TinyCloud({\n * services: { kv: KVService },\n * serviceConfigs: { kv: { prefix: 'myapp' } },\n * });\n *\n * // Use the service\n * const result = await sdk.kv.get('settings');\n * if (result.ok) {\n * console.log(result.data.data);\n * }\n * ```\n */\nexport class KVService extends BaseService implements IKVService {\n /**\n * Service identifier for registration.\n */\n static readonly serviceName = \"kv\";\n\n /**\n * Service configuration.\n */\n declare protected _config: KVServiceConfig;\n\n /**\n * Create a new KVService instance.\n *\n * @param config - Service configuration\n */\n constructor(config: KVServiceConfig = {}) {\n super();\n this._config = config;\n }\n\n /**\n * Get the service configuration.\n */\n get config(): KVServiceConfig {\n return this._config;\n }\n\n // Parses \"Used: X bytes, Limit: Y bytes\" from tinycloud-node error responses\n private parseQuotaInfo(\n errorText: string\n ): { usedBytes: number; limitBytes: number } | undefined {\n const match = errorText.match(\n /Used:\\s*(\\d+)\\s*bytes,\\s*Limit:\\s*(\\d+)\\s*bytes/i\n );\n if (match) {\n return {\n usedBytes: parseInt(match[1], 10),\n limitBytes: parseInt(match[2], 10),\n };\n }\n return undefined;\n }\n\n private handleQuotaErrorResponse(\n response: FetchResponse,\n errorText: string,\n key: string\n ): Result<never> | undefined {\n if (response.status === 402) {\n const quotaInfo = this.parseQuotaInfo(errorText);\n return err(\n storageQuotaExceededError(\n \"kv\",\n `Storage quota exceeded for key \"${key}\": ${errorText}`,\n {\n status: response.status,\n ...(quotaInfo\n ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes }\n : {}),\n }\n )\n );\n }\n\n if (response.status === 413) {\n const quotaInfo = this.parseQuotaInfo(errorText);\n return err(\n storageLimitReachedError(\n \"kv\",\n `Storage limit reached for key \"${key}\": ${errorText}`,\n {\n status: response.status,\n ...(quotaInfo\n ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes }\n : {}),\n }\n )\n );\n }\n\n return undefined;\n }\n\n /**\n * Get the full path with optional prefix.\n *\n * @param key - The key\n * @param prefixOverride - Optional prefix override\n * @returns The full path\n */\n private getFullPath(key: string, prefixOverride?: string): string {\n const prefix = prefixOverride ?? this._config.prefix ?? \"\";\n return prefix ? `${prefix}/${key}` : key;\n }\n\n /**\n * Get the host URL.\n */\n private get host(): string {\n return this.context.hosts[0];\n }\n\n /**\n * Execute an invoke operation.\n *\n * @param path - Resource path\n * @param action - KV action\n * @param body - Optional request body\n * @param signal - Optional abort signal\n * @returns Fetch response\n */\n private async invokeOperation(\n path: string,\n action: string,\n body?: Blob | string,\n signal?: AbortSignal\n ): Promise<FetchResponse> {\n const session = this.context.session!;\n const headers = this.context.invoke(\n session,\n \"kv\",\n path,\n action\n );\n\n return this.context.fetch(`${this.host}/invoke`, {\n method: \"POST\",\n headers,\n body,\n signal: this.combineSignals(signal),\n });\n }\n\n /**\n * Create KVResponseHeaders from fetch response headers.\n *\n * @param headers - Fetch response headers\n * @returns KVResponseHeaders object\n */\n private createResponseHeaders(headers: {\n get(name: string): string | null;\n }): KVResponseHeaders {\n return {\n etag: headers.get(\"etag\") ?? undefined,\n contentType: headers.get(\"content-type\") ?? undefined,\n lastModified: headers.get(\"last-modified\") ?? undefined,\n contentLength: headers.get(\"content-length\")\n ? parseInt(headers.get(\"content-length\")!, 10)\n : undefined,\n get: (name: string) => headers.get(name),\n };\n }\n\n /**\n * Parse response body based on content type.\n *\n * @param response - Fetch response\n * @param raw - Whether to return raw text\n * @returns Parsed data\n */\n private async parseResponse<T>(\n response: FetchResponse,\n raw: boolean = false\n ): Promise<T | undefined> {\n if (!response.ok) {\n return undefined;\n }\n\n if (raw) {\n return (await response.text()) as unknown as T;\n }\n\n const contentType = response.headers.get(\"content-type\");\n if (contentType?.includes(\"application/json\")) {\n return (await response.json()) as T;\n } else if (contentType?.startsWith(\"text/\")) {\n return (await response.text()) as unknown as T;\n }\n\n // No content-type header - try to parse as JSON, fall back to text\n const text = await response.text();\n if (!text) {\n return undefined;\n }\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n }\n\n /**\n * Get a value by key.\n */\n async get<T = unknown>(\n key: string,\n options?: KVGetOptions\n ): Promise<Result<KVResponse<T>>> {\n return this.withTelemetry(\"get\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.GET,\n undefined,\n options?.signal\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n const errorText = await response.text();\n const { resource, action } = parseAuthError(errorText);\n return err(authUnauthorizedError(\"kv\", errorText, {\n status: response.status,\n ...(action && { requiredAction: action }),\n ...(resource && { resource }),\n }));\n }\n\n if (response.status === 404) {\n return err(\n serviceError(\n ErrorCodes.KV_NOT_FOUND,\n `Key not found: ${key}`,\n \"kv\"\n )\n );\n }\n\n const errorText = await response.text();\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to get key \"${key}\": ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n const data = await this.parseResponse<T>(response, options?.raw);\n return ok({\n data: data as T,\n headers: this.createResponseHeaders(response.headers),\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * Store a value at a key.\n */\n async put(\n key: string,\n value: unknown,\n options?: KVPutOptions\n ): Promise<Result<KVResponse<void>>> {\n return this.withTelemetry(\"put\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n // Serialize value to string\n let body: string;\n if (typeof value === \"string\") {\n body = value;\n } else {\n body = JSON.stringify(value);\n }\n\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.PUT,\n body,\n options?.signal\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n const errorText = await response.text();\n const { resource, action } = parseAuthError(errorText);\n return err(authUnauthorizedError(\"kv\", errorText, {\n status: response.status,\n ...(action && { requiredAction: action }),\n ...(resource && { resource }),\n }));\n }\n\n const errorText = await response.text();\n\n // Check for storage quota errors (402, 413)\n const quotaError = this.handleQuotaErrorResponse(\n response,\n errorText,\n key\n );\n if (quotaError) {\n return quotaError;\n }\n\n return err(\n serviceError(\n ErrorCodes.KV_WRITE_FAILED,\n `Failed to put key \"${key}\": ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n return ok({\n data: undefined as void,\n headers: this.createResponseHeaders(response.headers),\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * List keys with optional prefix filtering.\n */\n async list(options?: KVListOptions): Promise<Result<KVListResponse>> {\n return this.withTelemetry(\"list\", options?.prefix, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n // Build the path from prefix and optional path\n let listPath = options?.prefix ?? this._config.prefix ?? \"\";\n if (options?.path) {\n listPath = listPath ? `${listPath}/${options.path}` : options.path;\n }\n\n try {\n const response = await this.invokeOperation(\n listPath,\n KVAction.LIST,\n undefined,\n options?.signal\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n const errorText = await response.text();\n const { resource, action } = parseAuthError(errorText);\n return err(authUnauthorizedError(\"kv\", errorText, {\n status: response.status,\n ...(action && { requiredAction: action }),\n ...(resource && { resource }),\n }));\n }\n\n const errorText = await response.text();\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to list keys: ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n let keys = await this.parseResponse<string[]>(response, options?.raw);\n keys = keys ?? [];\n\n // Optionally remove prefix from keys\n if (options?.removePrefix && listPath) {\n const prefixWithSlash = listPath.endsWith(\"/\")\n ? listPath\n : `${listPath}/`;\n keys = keys.map((key) =>\n key.startsWith(prefixWithSlash)\n ? key.slice(prefixWithSlash.length)\n : key\n );\n }\n\n return ok({ keys });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * Delete a key.\n */\n async delete(key: string, options?: KVDeleteOptions): Promise<Result<void>> {\n return this.withTelemetry(\"delete\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.DELETE,\n undefined,\n options?.signal\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n const errorText = await response.text();\n const { resource, action } = parseAuthError(errorText);\n return err(authUnauthorizedError(\"kv\", errorText, {\n status: response.status,\n ...(action && { requiredAction: action }),\n ...(resource && { resource }),\n }));\n }\n\n if (response.status === 404) {\n return err(\n serviceError(\n ErrorCodes.KV_NOT_FOUND,\n `Key not found: ${key}`,\n \"kv\"\n )\n );\n }\n\n const errorText = await response.text();\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to delete key \"${key}\": ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n return ok(undefined);\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * Get metadata for a key without retrieving the value.\n */\n async head(\n key: string,\n options?: KVHeadOptions\n ): Promise<Result<KVResponse<void>>> {\n return this.withTelemetry(\"head\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.HEAD,\n undefined,\n options?.signal\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n const errorText = await response.text();\n const { resource, action } = parseAuthError(errorText);\n return err(authUnauthorizedError(\"kv\", errorText, {\n status: response.status,\n ...(action && { requiredAction: action }),\n ...(resource && { resource }),\n }));\n }\n\n if (response.status === 404) {\n return err(\n serviceError(\n ErrorCodes.KV_NOT_FOUND,\n `Key not found: ${key}`,\n \"kv\"\n )\n );\n }\n\n const errorText = await response.text();\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to get metadata for key \"${key}\": ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n return ok({\n data: undefined as void,\n headers: this.createResponseHeaders(response.headers),\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * Create a prefix-scoped view of this KV service.\n *\n * Returns a PrefixedKVService that automatically prefixes all\n * key operations with the specified prefix. This enables apps\n * to isolate their data within a shared space.\n *\n * @param prefix - The prefix to apply to all operations\n * @returns A PrefixedKVService scoped to the prefix\n *\n * ## Prefix Conventions\n *\n * | Pattern | Use Case | Example |\n * | -- | -- | -- |\n * | `/app.{domain}/` | App-private data | `/app.photos.xyz/settings.json` |\n * | `/{type}/` | Shared data type | `/photos/vacation.jpg` |\n * | `/.{name}/` | Hidden/system data | `/.cache/thumbnails/` |\n * | `/public/` | Explicitly shareable | `/public/profile.json` |\n *\n * @example\n * ```typescript\n * const space = sdk.space('default');\n *\n * // Create prefix-scoped views\n * const myApp = space.kv.withPrefix('/app.myapp.com');\n * const sharedPhotos = space.kv.withPrefix('/photos');\n *\n * // Operations are automatically prefixed\n * await myApp.put('settings.json', { theme: 'dark' });\n * // -> Actually writes to: /app.myapp.com/settings.json\n *\n * await myApp.get('settings.json');\n * // -> Actually reads from: /app.myapp.com/settings.json\n *\n * await sharedPhotos.list();\n * // -> Lists: /photos/*\n *\n * // Nested prefixes\n * const settings = myApp.withPrefix('/settings');\n * await settings.get('theme.json'); // -> /app.myapp.com/settings/theme.json\n * ```\n */\n withPrefix(prefix: string): IPrefixedKVService {\n return new PrefixedKVService(this, prefix);\n }\n}\n"],"mappings":"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","names":["ok","serviceError","errorText"]}
1
+ {"version":3,"sources":["../../src/kv/index.ts","../../src/types.ts","../../src/errors.ts","../../src/base/BaseService.ts","../../src/kv/PrefixedKVService.ts","../../src/kv/types.ts","../../src/kv/KVService.ts"],"sourcesContent":["/**\n * KV Service Exports\n *\n * Key-Value storage service for TinyCloud SDK.\n */\n\n// Service implementation\nexport { KVService } from \"./KVService\";\n\n// Prefixed service implementation\nexport { PrefixedKVService, IPrefixedKVService } from \"./PrefixedKVService\";\n\n// Interface\nexport { IKVService } from \"./IKVService\";\n\n// Types\nexport {\n DEFAULT_SIGNED_READ_URL_EXPIRY_MS,\n KVServiceConfig,\n KVGetOptions,\n KVPutOptions,\n KVListOptions,\n KVDeleteOptions,\n KVHeadOptions,\n KVCreateSignedReadUrlOptions,\n KVResponse,\n KVListResponse,\n KVSignedReadUrlResponse,\n KVResponseHeaders,\n KVAction,\n KVActionType,\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 * 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\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 spaceId: string;\n service: string;\n path: string;\n action: string;\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 | string;\n signal?: AbortSignal;\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 * 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 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 ok: boolean;\n duration: number;\n status?: number;\n}\n\n/**\n * Event emitted on service error.\n */\nexport interface ServiceErrorEvent {\n service: 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 * Telemetry event names.\n */\nexport const TelemetryEvents = {\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 { ServiceError, ErrorCodes, err, serviceError } from \"./types\";\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 * 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/**\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.abortController.signal;\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 this.emit(TelemetryEvents.SERVICE_REQUEST, {\n service: this.getServiceName(),\n 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 this.emit(TelemetryEvents.SERVICE_RESPONSE, {\n service: this.getServiceName(),\n action,\n ok,\n duration: Date.now() - startTime,\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): void {\n this.emit(TelemetryEvents.SERVICE_ERROR, {\n service: this.getServiceName(),\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 * 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 = [this.abortSignal, ...signals.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);\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);\n return err(serviceError);\n }\n }\n}\n","/**\n * PrefixedKVService - A prefix-scoped view of KVService.\n *\n * Provides key-value operations scoped to a specific prefix.\n * All operations automatically prefix keys, enabling app data isolation\n * within a shared space.\n *\n * @example\n * ```typescript\n * const space = sdk.space('default');\n *\n * // Create prefix-scoped views\n * const myApp = space.kv.withPrefix('/app.myapp.com');\n * const sharedPhotos = space.kv.withPrefix('/photos');\n *\n * // Operations are automatically prefixed\n * await myApp.put('settings.json', { theme: 'dark' });\n * // -> Actually writes to: /app.myapp.com/settings.json\n *\n * await myApp.get('settings.json');\n * // -> Actually reads from: /app.myapp.com/settings.json\n *\n * await sharedPhotos.list();\n * // -> Lists: /photos/*\n *\n * // Nested prefixes\n * const settings = myApp.withPrefix('/settings');\n * await settings.get('theme.json'); // -> /app.myapp.com/settings/theme.json\n * ```\n */\n\nimport { Result } from \"../types\";\nimport {\n KVGetOptions,\n KVPutOptions,\n KVListOptions,\n KVDeleteOptions,\n KVHeadOptions,\n KVCreateSignedReadUrlOptions,\n KVResponse,\n KVListResponse,\n KVSignedReadUrlResponse,\n} from \"./types\";\n\n/**\n * Interface for prefixed KV operations.\n *\n * Provides the same operations as IKVService but scoped to a prefix.\n * Supports nested prefixes via withPrefix().\n */\nexport interface IPrefixedKVService {\n /**\n * The current prefix for this scoped view.\n */\n readonly prefix: string;\n\n /**\n * Get a value by key.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to retrieve (will be prefixed)\n * @param options - Optional get configuration\n * @returns Result with the stored value and headers\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * const result = await myApp.get('settings.json');\n * // -> Reads from: /app.myapp.com/settings.json\n * ```\n */\n get<T = unknown>(\n key: string,\n options?: Omit<KVGetOptions, 'prefix'>\n ): Promise<Result<KVResponse<T>>>;\n\n /**\n * Store a value at a key.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to store under (will be prefixed)\n * @param value - The value to store\n * @param options - Optional put configuration\n * @returns Result indicating success/failure\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * await myApp.put('settings.json', { theme: 'dark' });\n * // -> Stores at: /app.myapp.com/settings.json\n * ```\n */\n put(\n key: string,\n value: unknown,\n options?: Omit<KVPutOptions, 'prefix'>\n ): Promise<Result<KVResponse<void>>>;\n\n /**\n * List keys within this prefix.\n *\n * Returns keys that match the prefix, with keys returned relative\n * to the prefix when removePrefix is true (default for prefixed service).\n *\n * @param options - Optional list configuration\n * @returns Result with array of matching keys\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * const result = await myApp.list();\n * // -> Lists keys under: /app.myapp.com/*\n * // Returns: ['settings.json', 'data/user.json', ...]\n * ```\n */\n list(options?: Omit<KVListOptions, 'prefix'>): Promise<Result<KVListResponse>>;\n\n /**\n * Delete a key.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to delete (will be prefixed)\n * @param options - Optional delete configuration\n * @returns Result indicating success/failure\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * await myApp.delete('old-settings.json');\n * // -> Deletes: /app.myapp.com/old-settings.json\n * ```\n */\n delete(key: string, options?: Omit<KVDeleteOptions, 'prefix'>): Promise<Result<void>>;\n\n /**\n * Get metadata for a key without retrieving the value.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to check (will be prefixed)\n * @param options - Optional head configuration\n * @returns Result with headers only\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * const result = await myApp.head('large-file.bin');\n * // -> Gets metadata for: /app.myapp.com/large-file.bin\n * ```\n */\n head(key: string, options?: Omit<KVHeadOptions, 'prefix'>): Promise<Result<KVResponse<void>>>;\n\n /**\n * Create a short-lived signed URL for reading a KV object.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to expose via a signed read URL (will be prefixed)\n * @param options - Optional signed URL configuration\n * @returns Result with URL and expiry metadata\n */\n createSignedReadUrl(\n key: string,\n options?: Omit<KVCreateSignedReadUrlOptions, 'prefix'>\n ): Promise<Result<KVSignedReadUrlResponse>>;\n\n /**\n * Create a nested prefix-scoped view.\n *\n * The subPrefix is appended to the current prefix.\n *\n * @param subPrefix - The sub-prefix to append\n * @returns A new PrefixedKVService with the combined prefix\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * const settings = myApp.withPrefix('/settings');\n * await settings.get('theme.json');\n * // -> Reads from: /app.myapp.com/settings/theme.json\n * ```\n */\n withPrefix(subPrefix: string): IPrefixedKVService;\n}\n\n/**\n * Interface for a KV service that supports prefix delegation.\n *\n * This is the subset of IKVService methods needed by PrefixedKVService.\n */\ninterface IKVServiceLike {\n get<T = unknown>(\n key: string,\n options?: KVGetOptions\n ): Promise<Result<KVResponse<T>>>;\n\n put(\n key: string,\n value: unknown,\n options?: KVPutOptions\n ): Promise<Result<KVResponse<void>>>;\n\n list(options?: KVListOptions): Promise<Result<KVListResponse>>;\n\n delete(key: string, options?: KVDeleteOptions): Promise<Result<void>>;\n\n head(key: string, options?: KVHeadOptions): Promise<Result<KVResponse<void>>>;\n\n createSignedReadUrl(\n key: string,\n options?: KVCreateSignedReadUrlOptions\n ): Promise<Result<KVSignedReadUrlResponse>>;\n}\n\n/**\n * PrefixedKVService - Implementation of prefix-scoped KV operations.\n *\n * This class wraps a KVService (or another PrefixedKVService) and\n * automatically prefixes all key operations with the configured prefix.\n *\n * ## Prefix Convention\n *\n * | Pattern | Use Case | Example |\n * | -- | -- | -- |\n * | `/app.{domain}/` | App-private data | `/app.photos.xyz/settings.json` |\n * | `/{type}/` | Shared data type | `/photos/vacation.jpg` |\n * | `/.{name}/` | Hidden/system data | `/.cache/thumbnails/` |\n * | `/public/` | Explicitly shareable | `/public/profile.json` |\n *\n * @example\n * ```typescript\n * // Create from KVService\n * const prefixed = new PrefixedKVService(kvService, '/app.myapp.com');\n *\n * // Or use the withPrefix factory method on KVService\n * const prefixed = kvService.withPrefix('/app.myapp.com');\n *\n * // All operations are automatically prefixed\n * await prefixed.put('settings.json', { theme: 'dark' });\n * await prefixed.get('settings.json');\n *\n * // Nested prefixes\n * const nested = prefixed.withPrefix('/settings');\n * await nested.get('theme.json'); // -> /app.myapp.com/settings/theme.json\n * ```\n */\nexport class PrefixedKVService implements IPrefixedKVService {\n /**\n * The underlying KV service.\n */\n private readonly _kv: IKVServiceLike;\n\n /**\n * The prefix for this scoped view.\n */\n private readonly _prefix: string;\n\n /**\n * Create a new PrefixedKVService.\n *\n * @param kv - The underlying KV service to delegate to\n * @param prefix - The prefix to apply to all operations\n */\n constructor(kv: IKVServiceLike, prefix: string) {\n this._kv = kv;\n // Normalize prefix: ensure it doesn't end with slash\n this._prefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n }\n\n /**\n * The current prefix for this scoped view.\n */\n get prefix(): string {\n return this._prefix;\n }\n\n /**\n * Compute the full key path by combining prefix and key.\n *\n * @param key - The key to prefix\n * @returns The full path including prefix\n */\n private getFullKey(key: string): string {\n // Handle keys that start with slash\n const normalizedKey = key.startsWith('/') ? key : `/${key}`;\n return `${this._prefix}${normalizedKey}`;\n }\n\n /**\n * Get a value by key.\n */\n async get<T = unknown>(\n key: string,\n options?: Omit<KVGetOptions, 'prefix'>\n ): Promise<Result<KVResponse<T>>> {\n const fullKey = this.getFullKey(key);\n // Use empty prefix override to use the full key as-is\n return this._kv.get<T>(fullKey, { ...options, prefix: '' });\n }\n\n /**\n * Store a value at a key.\n */\n async put(\n key: string,\n value: unknown,\n options?: Omit<KVPutOptions, 'prefix'>\n ): Promise<Result<KVResponse<void>>> {\n const fullKey = this.getFullKey(key);\n return this._kv.put(fullKey, value, { ...options, prefix: '' });\n }\n\n /**\n * List keys within this prefix.\n */\n async list(options?: Omit<KVListOptions, 'prefix'>): Promise<Result<KVListResponse>> {\n // List uses the prefix directly, and by default removes the prefix from results\n const removePrefix = options?.removePrefix ?? true;\n return this._kv.list({\n ...options,\n prefix: this._prefix,\n removePrefix,\n });\n }\n\n /**\n * Delete a key.\n */\n async delete(key: string, options?: Omit<KVDeleteOptions, 'prefix'>): Promise<Result<void>> {\n const fullKey = this.getFullKey(key);\n return this._kv.delete(fullKey, { ...options, prefix: '' });\n }\n\n /**\n * Get metadata for a key without retrieving the value.\n */\n async head(\n key: string,\n options?: Omit<KVHeadOptions, 'prefix'>\n ): Promise<Result<KVResponse<void>>> {\n const fullKey = this.getFullKey(key);\n return this._kv.head(fullKey, { ...options, prefix: '' });\n }\n\n /**\n * Create a short-lived signed URL for reading a KV object.\n */\n async createSignedReadUrl(\n key: string,\n options?: Omit<KVCreateSignedReadUrlOptions, 'prefix'>\n ): Promise<Result<KVSignedReadUrlResponse>> {\n const fullKey = this.getFullKey(key);\n return this._kv.createSignedReadUrl(fullKey, { ...options, prefix: '' });\n }\n\n /**\n * Create a nested prefix-scoped view.\n */\n withPrefix(subPrefix: string): IPrefixedKVService {\n // Normalize subPrefix\n const normalizedSubPrefix = subPrefix.startsWith('/')\n ? subPrefix\n : `/${subPrefix}`;\n const combinedPrefix = `${this._prefix}${normalizedSubPrefix}`;\n return new PrefixedKVService(this._kv, combinedPrefix);\n }\n}\n","/**\n * KV Service Types\n *\n * Type definitions for the KV (Key-Value) service operations.\n */\n\n/**\n * Configuration for KVService.\n */\nexport interface KVServiceConfig {\n /**\n * Default prefix for all keys.\n * Useful for namespacing data within a space.\n *\n * @example\n * ```typescript\n * const kv = new KVService({ prefix: 'myapp/settings' });\n * await kv.put('theme', 'dark'); // Stores at 'myapp/settings/theme'\n * ```\n */\n prefix?: string;\n\n /**\n * Default timeout in milliseconds for KV operations.\n * Overrides the context-level timeout if set.\n */\n timeout?: number;\n\n /** Allow additional config properties */\n [key: string]: unknown;\n}\n\n/**\n * Options for KV get operations.\n */\nexport interface KVGetOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Return raw response instead of parsed JSON.\n * When true, data will be the raw response text.\n */\n raw?: boolean;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for KV put operations.\n */\nexport interface KVPutOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Content type for the value.\n * Defaults to 'application/json' for objects.\n */\n contentType?: string;\n\n /**\n * Custom metadata headers to store with the value.\n */\n metadata?: Record<string, string>;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for KV list operations.\n */\nexport interface KVListOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Additional path to append to the prefix.\n */\n path?: string;\n\n /**\n * Whether to remove the prefix from returned keys.\n * When true, keys are returned relative to the prefix.\n */\n removePrefix?: boolean;\n\n /**\n * Return raw response instead of parsed JSON.\n */\n raw?: boolean;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for KV delete operations.\n */\nexport interface KVDeleteOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for KV head (metadata) operations.\n */\nexport interface KVHeadOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Default lifetime for signed KV read URLs when a caller omits expiresInSeconds.\n * SDK duration defaults are stored in milliseconds; createSignedReadUrl converts\n * this to the node endpoint's ttl_seconds field.\n *\n * Keep this in sync with EXPIRY.SIGNED_READ_URL_MS in @tinycloud/sdk-core.\n * sdk-services cannot import sdk-core because sdk-core depends on sdk-services.\n */\nexport const DEFAULT_SIGNED_READ_URL_EXPIRY_MS = 5 * 60 * 1000;\n\n/**\n * Options for creating a signed KV read URL.\n */\nexport interface KVCreateSignedReadUrlOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Requested URL lifetime in seconds.\n * Defaults to {@link DEFAULT_SIGNED_READ_URL_EXPIRY_MS} converted to seconds.\n * The node may cap this by its configured maximum, the invocation expiry,\n * or the parent delegation expiry.\n */\n expiresInSeconds?: number;\n\n /**\n * Optional blake3 content hash to bind the signed URL to a specific object.\n */\n contentHash?: string;\n\n /**\n * Optional ETag to bind the signed URL to a specific object version.\n */\n etag?: string;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Response headers from KV operations.\n */\nexport interface KVResponseHeaders {\n /**\n * ETag for conditional requests.\n */\n etag?: string;\n\n /**\n * Content type of the stored value.\n */\n contentType?: string;\n\n /**\n * Last modification timestamp.\n */\n lastModified?: string;\n\n /**\n * Content length in bytes.\n */\n contentLength?: number;\n\n /**\n * Get a header value by name.\n * @param name - Header name (case-insensitive)\n */\n get(name: string): string | null;\n}\n\n/**\n * Response from KV get/put operations.\n *\n * @template T - Type of the data payload\n */\nexport interface KVResponse<T = unknown> {\n /**\n * The data payload.\n * For get: the stored value.\n * For put: undefined.\n */\n data: T;\n\n /**\n * Response headers with metadata.\n */\n headers: KVResponseHeaders;\n}\n\n/**\n * Response from KV list operations.\n */\nexport interface KVListResponse {\n /**\n * Array of keys matching the list criteria.\n */\n keys: string[];\n}\n\n/**\n * Response from signed KV read URL creation.\n */\nexport interface KVSignedReadUrlResponse {\n /**\n * Absolute URL suitable for passing to external readers.\n */\n url: string;\n\n /**\n * Opaque URL returned by tinycloud-node, usually relative to the node host.\n */\n relativeUrl: string;\n\n /**\n * Opaque signed KV ticket identifier.\n */\n ticketId: string;\n\n /**\n * Expiry timestamp as returned by tinycloud-node.\n */\n expiresAt: string;\n}\n\n/**\n * KV service action types.\n */\nexport const KVAction = {\n GET: \"tinycloud.kv/get\",\n PUT: \"tinycloud.kv/put\",\n LIST: \"tinycloud.kv/list\",\n DELETE: \"tinycloud.kv/del\",\n HEAD: \"tinycloud.kv/metadata\",\n} as const;\n\nexport type KVActionType = (typeof KVAction)[keyof typeof KVAction];\n","/**\n * KVService - Key-Value storage service implementation.\n *\n * Platform-agnostic KV 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 FetchResponse,\n ServiceHeaders,\n} from \"../types\";\nimport {\n authRequiredError,\n wrapError,\n storageQuotaExceededError,\n storageLimitReachedError,\n parseAuthError,\n authUnauthorizedError,\n} from \"../errors\";\nimport { IKVService } from \"./IKVService\";\nimport { PrefixedKVService, IPrefixedKVService } from \"./PrefixedKVService\";\nimport {\n DEFAULT_SIGNED_READ_URL_EXPIRY_MS,\n KVServiceConfig,\n KVGetOptions,\n KVPutOptions,\n KVListOptions,\n KVDeleteOptions,\n KVHeadOptions,\n KVCreateSignedReadUrlOptions,\n KVResponse,\n KVListResponse,\n KVResponseHeaders,\n KVSignedReadUrlResponse,\n KVAction,\n} from \"./types\";\n\ninterface SignedKvUrlNodeResponse {\n url: string;\n ticketId: string;\n expiresAt: string;\n}\n\n/**\n * KV service implementation.\n *\n * Provides key-value storage operations using TinyCloud's KV API.\n * Uses the Result type pattern for explicit error handling.\n *\n * @example\n * ```typescript\n * // Register with SDK\n * const sdk = new TinyCloud({\n * services: { kv: KVService },\n * serviceConfigs: { kv: { prefix: 'myapp' } },\n * });\n *\n * // Use the service\n * const result = await sdk.kv.get('settings');\n * if (result.ok) {\n * console.log(result.data.data);\n * }\n * ```\n */\nexport class KVService extends BaseService implements IKVService {\n /**\n * Service identifier for registration.\n */\n static readonly serviceName = \"kv\";\n\n /**\n * Service configuration.\n */\n declare protected _config: KVServiceConfig;\n\n /**\n * Create a new KVService instance.\n *\n * @param config - Service configuration\n */\n constructor(config: KVServiceConfig = {}) {\n super();\n this._config = config;\n }\n\n /**\n * Get the service configuration.\n */\n get config(): KVServiceConfig {\n return this._config;\n }\n\n // Parses \"Used: X bytes, Limit: Y bytes\" from tinycloud-node error responses\n private parseQuotaInfo(\n errorText: string\n ): { usedBytes: number; limitBytes: number } | undefined {\n const match = errorText.match(\n /Used:\\s*(\\d+)\\s*bytes,\\s*Limit:\\s*(\\d+)\\s*bytes/i\n );\n if (match) {\n return {\n usedBytes: parseInt(match[1], 10),\n limitBytes: parseInt(match[2], 10),\n };\n }\n return undefined;\n }\n\n private handleQuotaErrorResponse(\n response: FetchResponse,\n errorText: string,\n key: string\n ): Result<never> | undefined {\n if (response.status === 402) {\n const quotaInfo = this.parseQuotaInfo(errorText);\n return err(\n storageQuotaExceededError(\n \"kv\",\n `Storage quota exceeded for key \"${key}\": ${errorText}`,\n {\n status: response.status,\n ...(quotaInfo\n ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes }\n : {}),\n }\n )\n );\n }\n\n if (response.status === 413) {\n const quotaInfo = this.parseQuotaInfo(errorText);\n return err(\n storageLimitReachedError(\n \"kv\",\n `Storage limit reached for key \"${key}\": ${errorText}`,\n {\n status: response.status,\n ...(quotaInfo\n ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes }\n : {}),\n }\n )\n );\n }\n\n return undefined;\n }\n\n /**\n * Get the full path with optional prefix.\n *\n * @param key - The key\n * @param prefixOverride - Optional prefix override\n * @returns The full path\n */\n private getFullPath(key: string, prefixOverride?: string): string {\n const prefix = prefixOverride ?? this._config.prefix ?? \"\";\n return prefix ? `${prefix}/${key}` : key;\n }\n\n /**\n * Get the host URL.\n */\n private get host(): string {\n return this.context.hosts[0];\n }\n\n private withJsonContentType(headers: ServiceHeaders): ServiceHeaders {\n if (Array.isArray(headers)) {\n return [...headers, [\"content-type\", \"application/json\"]];\n }\n\n return {\n ...headers,\n \"content-type\": \"application/json\",\n };\n }\n\n /**\n * Execute an invoke operation.\n *\n * @param path - Resource path\n * @param action - KV action\n * @param body - Optional request body\n * @param signal - Optional abort signal\n * @returns Fetch response\n */\n private async invokeOperation(\n path: string,\n action: string,\n body?: Blob | string,\n signal?: AbortSignal\n ): Promise<FetchResponse> {\n const session = this.context.session!;\n const headers = this.context.invoke(\n session,\n \"kv\",\n path,\n action\n );\n\n return this.context.fetch(`${this.host}/invoke`, {\n method: \"POST\",\n headers,\n body,\n signal: this.combineSignals(signal),\n });\n }\n\n /**\n * Create KVResponseHeaders from fetch response headers.\n *\n * @param headers - Fetch response headers\n * @returns KVResponseHeaders object\n */\n private createResponseHeaders(headers: {\n get(name: string): string | null;\n }): KVResponseHeaders {\n return {\n etag: headers.get(\"etag\") ?? undefined,\n contentType: headers.get(\"content-type\") ?? undefined,\n lastModified: headers.get(\"last-modified\") ?? undefined,\n contentLength: headers.get(\"content-length\")\n ? parseInt(headers.get(\"content-length\")!, 10)\n : undefined,\n get: (name: string) => headers.get(name),\n };\n }\n\n /**\n * Parse response body based on content type.\n *\n * @param response - Fetch response\n * @param raw - Whether to return raw text\n * @returns Parsed data\n */\n private async parseResponse<T>(\n response: FetchResponse,\n raw: boolean = false\n ): Promise<T | undefined> {\n if (!response.ok) {\n return undefined;\n }\n\n if (raw) {\n return (await response.text()) as unknown as T;\n }\n\n const contentType = response.headers.get(\"content-type\");\n if (contentType?.includes(\"application/json\")) {\n return (await response.json()) as T;\n } else if (contentType?.startsWith(\"text/\")) {\n return (await response.text()) as unknown as T;\n }\n\n // No content-type header - try to parse as JSON, fall back to text\n const text = await response.text();\n if (!text) {\n return undefined;\n }\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n }\n\n private async createSignedReadUrlError(\n response: FetchResponse,\n key: string\n ): Promise<Result<never>> {\n let errorText = response.statusText;\n try {\n const text = await response.text();\n if (text) {\n errorText = text;\n }\n } catch {\n // Ignore secondary body read failure.\n }\n\n if (response.status === 401 || response.status === 403) {\n const { resource, action } = parseAuthError(errorText);\n return err(authUnauthorizedError(\"kv\", errorText, {\n status: response.status,\n ...(action && { requiredAction: action }),\n ...(resource && { resource }),\n }));\n }\n\n const code =\n response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;\n return err(\n serviceError(\n code,\n `Failed to create signed read URL for key \"${key}\": ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n private normalizeSignedReadUrlResponse(\n data: unknown\n ): KVSignedReadUrlResponse | undefined {\n if (!data || typeof data !== \"object\") {\n return undefined;\n }\n\n const response = data as Partial<SignedKvUrlNodeResponse>;\n if (\n typeof response.url !== \"string\" ||\n typeof response.ticketId !== \"string\" ||\n typeof response.expiresAt !== \"string\"\n ) {\n return undefined;\n }\n\n return {\n url: new URL(response.url, this.host).toString(),\n relativeUrl: response.url,\n ticketId: response.ticketId,\n expiresAt: response.expiresAt,\n };\n }\n\n /**\n * Get a value by key.\n */\n async get<T = unknown>(\n key: string,\n options?: KVGetOptions\n ): Promise<Result<KVResponse<T>>> {\n return this.withTelemetry(\"get\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.GET,\n undefined,\n options?.signal\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n const errorText = await response.text();\n const { resource, action } = parseAuthError(errorText);\n return err(authUnauthorizedError(\"kv\", errorText, {\n status: response.status,\n ...(action && { requiredAction: action }),\n ...(resource && { resource }),\n }));\n }\n\n if (response.status === 404) {\n return err(\n serviceError(\n ErrorCodes.KV_NOT_FOUND,\n `Key not found: ${key}`,\n \"kv\"\n )\n );\n }\n\n const errorText = await response.text();\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to get key \"${key}\": ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n const data = await this.parseResponse<T>(response, options?.raw);\n return ok({\n data: data as T,\n headers: this.createResponseHeaders(response.headers),\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * Store a value at a key.\n */\n async put(\n key: string,\n value: unknown,\n options?: KVPutOptions\n ): Promise<Result<KVResponse<void>>> {\n return this.withTelemetry(\"put\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n // Serialize value to string\n let body: string;\n if (typeof value === \"string\") {\n body = value;\n } else {\n body = JSON.stringify(value);\n }\n\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.PUT,\n body,\n options?.signal\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n const errorText = await response.text();\n const { resource, action } = parseAuthError(errorText);\n return err(authUnauthorizedError(\"kv\", errorText, {\n status: response.status,\n ...(action && { requiredAction: action }),\n ...(resource && { resource }),\n }));\n }\n\n const errorText = await response.text();\n\n // Check for storage quota errors (402, 413)\n const quotaError = this.handleQuotaErrorResponse(\n response,\n errorText,\n key\n );\n if (quotaError) {\n return quotaError;\n }\n\n return err(\n serviceError(\n ErrorCodes.KV_WRITE_FAILED,\n `Failed to put key \"${key}\": ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n return ok({\n data: undefined as void,\n headers: this.createResponseHeaders(response.headers),\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * List keys with optional prefix filtering.\n */\n async list(options?: KVListOptions): Promise<Result<KVListResponse>> {\n return this.withTelemetry(\"list\", options?.prefix, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n // Build the path from prefix and optional path\n let listPath = options?.prefix ?? this._config.prefix ?? \"\";\n if (options?.path) {\n listPath = listPath ? `${listPath}/${options.path}` : options.path;\n }\n\n try {\n const response = await this.invokeOperation(\n listPath,\n KVAction.LIST,\n undefined,\n options?.signal\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n const errorText = await response.text();\n const { resource, action } = parseAuthError(errorText);\n return err(authUnauthorizedError(\"kv\", errorText, {\n status: response.status,\n ...(action && { requiredAction: action }),\n ...(resource && { resource }),\n }));\n }\n\n const errorText = await response.text();\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to list keys: ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n let keys = await this.parseResponse<string[]>(response, options?.raw);\n keys = keys ?? [];\n\n // Optionally remove prefix from keys\n if (options?.removePrefix && listPath) {\n const prefixWithSlash = listPath.endsWith(\"/\")\n ? listPath\n : `${listPath}/`;\n keys = keys.map((key) =>\n key.startsWith(prefixWithSlash)\n ? key.slice(prefixWithSlash.length)\n : key\n );\n }\n\n return ok({ keys });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * Delete a key.\n */\n async delete(key: string, options?: KVDeleteOptions): Promise<Result<void>> {\n return this.withTelemetry(\"delete\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.DELETE,\n undefined,\n options?.signal\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n const errorText = await response.text();\n const { resource, action } = parseAuthError(errorText);\n return err(authUnauthorizedError(\"kv\", errorText, {\n status: response.status,\n ...(action && { requiredAction: action }),\n ...(resource && { resource }),\n }));\n }\n\n if (response.status === 404) {\n return err(\n serviceError(\n ErrorCodes.KV_NOT_FOUND,\n `Key not found: ${key}`,\n \"kv\"\n )\n );\n }\n\n const errorText = await response.text();\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to delete key \"${key}\": ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n return ok(undefined);\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * Get metadata for a key without retrieving the value.\n */\n async head(\n key: string,\n options?: KVHeadOptions\n ): Promise<Result<KVResponse<void>>> {\n return this.withTelemetry(\"head\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.HEAD,\n undefined,\n options?.signal\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n const errorText = await response.text();\n const { resource, action } = parseAuthError(errorText);\n return err(authUnauthorizedError(\"kv\", errorText, {\n status: response.status,\n ...(action && { requiredAction: action }),\n ...(resource && { resource }),\n }));\n }\n\n if (response.status === 404) {\n return err(\n serviceError(\n ErrorCodes.KV_NOT_FOUND,\n `Key not found: ${key}`,\n \"kv\"\n )\n );\n }\n\n const errorText = await response.text();\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to get metadata for key \"${key}\": ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n return ok({\n data: undefined as void,\n headers: this.createResponseHeaders(response.headers),\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * Create a short-lived signed URL for reading a KV object.\n */\n async createSignedReadUrl(\n key: string,\n options?: KVCreateSignedReadUrlOptions\n ): Promise<Result<KVSignedReadUrlResponse>> {\n return this.withTelemetry(\"createSignedReadUrl\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n const session = this.context.session!;\n const headers = this.context.invoke(\n session,\n \"kv\",\n path,\n KVAction.GET\n );\n\n const body: {\n space: string;\n path: string;\n ttl_seconds: number;\n content_hash?: string;\n etag?: string;\n } = {\n space: session.spaceId,\n path,\n ttl_seconds:\n options?.expiresInSeconds ??\n Math.ceil(DEFAULT_SIGNED_READ_URL_EXPIRY_MS / 1000),\n };\n\n if (options?.contentHash !== undefined) {\n body.content_hash = options.contentHash;\n }\n if (options?.etag !== undefined) {\n body.etag = options.etag;\n }\n\n try {\n const response = await this.context.fetch(`${this.host}/signed/kv`, {\n method: \"POST\",\n headers: this.withJsonContentType(headers),\n body: JSON.stringify(body),\n signal: this.combineSignals(options?.signal),\n });\n\n if (!response.ok) {\n return this.createSignedReadUrlError(response, key);\n }\n\n const signedUrl = this.normalizeSignedReadUrlResponse(\n await response.json()\n );\n if (!signedUrl) {\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n \"Signed read URL response did not include url, ticketId, and expiresAt\",\n \"kv\"\n )\n );\n }\n\n return ok(signedUrl);\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * Create a prefix-scoped view of this KV service.\n *\n * Returns a PrefixedKVService that automatically prefixes all\n * key operations with the specified prefix. This enables apps\n * to isolate their data within a shared space.\n *\n * @param prefix - The prefix to apply to all operations\n * @returns A PrefixedKVService scoped to the prefix\n *\n * ## Prefix Conventions\n *\n * | Pattern | Use Case | Example |\n * | -- | -- | -- |\n * | `/app.{domain}/` | App-private data | `/app.photos.xyz/settings.json` |\n * | `/{type}/` | Shared data type | `/photos/vacation.jpg` |\n * | `/.{name}/` | Hidden/system data | `/.cache/thumbnails/` |\n * | `/public/` | Explicitly shareable | `/public/profile.json` |\n *\n * @example\n * ```typescript\n * const space = sdk.space('default');\n *\n * // Create prefix-scoped views\n * const myApp = space.kv.withPrefix('/app.myapp.com');\n * const sharedPhotos = space.kv.withPrefix('/photos');\n *\n * // Operations are automatically prefixed\n * await myApp.put('settings.json', { theme: 'dark' });\n * // -> Actually writes to: /app.myapp.com/settings.json\n *\n * await myApp.get('settings.json');\n * // -> Actually reads from: /app.myapp.com/settings.json\n *\n * await sharedPhotos.list();\n * // -> Lists: /photos/*\n *\n * // Nested prefixes\n * const settings = myApp.withPrefix('/settings');\n * await settings.get('theme.json'); // -> /app.myapp.com/settings/theme.json\n * ```\n */\n withPrefix(prefix: string): IPrefixedKVService {\n return new PrefixedKVService(this, prefix);\n }\n}\n"],"mappings":"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","names":["ok","serviceError","errorText"]}