@tinycloud/sdk-services 3.2.6-beta.4 → 3.3.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/{BaseService-1Ug6y7DC.d.cts → BaseService-Cc6Ueicm.d.cts} +1 -1
  2. package/dist/{BaseService-Bn07Vudy.d.ts → BaseService-DOdXd7sr.d.ts} +1 -1
  3. package/dist/encryption/index.cjs +8 -0
  4. package/dist/encryption/index.cjs.map +1 -1
  5. package/dist/encryption/index.d.cts +2 -2
  6. package/dist/encryption/index.d.ts +2 -2
  7. package/dist/encryption/index.js +8 -0
  8. package/dist/encryption/index.js.map +1 -1
  9. package/dist/index.cjs +94 -57
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +29 -6
  12. package/dist/index.d.ts +29 -6
  13. package/dist/index.js +93 -57
  14. package/dist/index.js.map +1 -1
  15. package/dist/internal/decrypt-transport-response-error.d.cts +1 -1
  16. package/dist/internal/decrypt-transport-response-error.d.ts +1 -1
  17. package/dist/kv/index.cjs +66 -21
  18. package/dist/kv/index.cjs.map +1 -1
  19. package/dist/kv/index.d.cts +4 -4
  20. package/dist/kv/index.d.ts +4 -4
  21. package/dist/kv/index.js +66 -21
  22. package/dist/kv/index.js.map +1 -1
  23. package/dist/kv/replication/grantPolicy.cjs +82 -0
  24. package/dist/kv/replication/grantPolicy.cjs.map +1 -0
  25. package/dist/kv/replication/grantPolicy.d.cts +19 -0
  26. package/dist/kv/replication/grantPolicy.d.ts +19 -0
  27. package/dist/kv/replication/grantPolicy.js +50 -0
  28. package/dist/kv/replication/grantPolicy.js.map +1 -0
  29. package/dist/kv/replication/index.cjs +571 -128
  30. package/dist/kv/replication/index.cjs.map +1 -1
  31. package/dist/kv/replication/index.d.cts +3 -3
  32. package/dist/kv/replication/index.d.ts +3 -3
  33. package/dist/kv/replication/index.js +571 -128
  34. package/dist/kv/replication/index.js.map +1 -1
  35. package/dist/{scope-BnyVPZ5E.d.ts → scope-B-m6cKH-.d.ts} +310 -7
  36. package/dist/{scope-CQ5kmHlt.d.cts → scope-B7oeKdLM.d.cts} +310 -7
  37. package/dist/sql/index.cjs +70 -36
  38. package/dist/sql/index.cjs.map +1 -1
  39. package/dist/sql/index.d.cts +2 -2
  40. package/dist/sql/index.d.ts +2 -2
  41. package/dist/sql/index.js +70 -36
  42. package/dist/sql/index.js.map +1 -1
  43. package/dist/{types-gmdUiorz.d.cts → types-BjH0ZtGy.d.cts} +6 -0
  44. package/dist/{types-gmdUiorz.d.ts → types-BjH0ZtGy.d.ts} +6 -0
  45. package/package.json +16 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types.ts","../../src/errors.ts","../../src/encryption/networkId.ts","../../src/base/BaseService.ts","../../src/kv/PrefixedKVService.ts","../../src/kv/types.ts","../../src/kv/KVService.ts","../../src/kv/replication/constants.ts","../../src/kv/replication/identity.ts","../../src/kv/replication/prefix.ts","../../src/kv/replication/scope.ts"],"sourcesContent":["/**\n * SDK Services - Core Types\n *\n * These types define the service architecture for TinyCloud SDK.\n * Services use dependency injection via IServiceContext for platform independence.\n */\n\n// =============================================================================\n// Result Type Pattern\n// =============================================================================\n\n/**\n * Result type for service operations.\n * Services return Result instead of throwing, making error handling explicit.\n *\n * @template T - The success data type\n * @template E - The error type (defaults to ServiceError)\n *\n * @example\n * ```typescript\n * const result = await kv.get('key');\n * if (result.ok) {\n * console.log(result.data);\n * } else {\n * console.error(result.error.code);\n * }\n * ```\n */\nexport type Result<T, E = ServiceError> =\n | { ok: true; data: T }\n | { ok: false; error: E };\n\n/**\n * Service error with structured information.\n */\nexport interface ServiceError {\n /** Error code for programmatic handling (e.g., 'KV_NOT_FOUND', 'AUTH_EXPIRED') */\n code: string;\n /** Human-readable error message */\n message: string;\n /** Service that produced the error (e.g., 'kv', 'sql') */\n service: string;\n /** Original error if this wraps another error */\n cause?: Error;\n /** Additional metadata about the error */\n meta?: Record<string, unknown>;\n}\n\n/**\n * A node-supplied permission hint after strict SDK validation. This is\n * deliberately narrower than a general capability: no wildcards, caveats,\n * descriptions, or transport metadata can cross the service boundary.\n */\nexport interface PermissionHint {\n readonly service: \"tinycloud.kv\" | \"tinycloud.encryption\";\n readonly space?: string;\n readonly path: string;\n readonly actions: readonly string[];\n}\n\n/**\n * Storage quota information returned with quota-related errors.\n * @deprecated Read `usedBytes`, `limitBytes` and `account` from the error's\n * `meta`, or use `StorageFullEvent` from `tc.on(\"storage.full\", handler)`.\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 /** HTTP 401: the delegation the invocation cites was revoked. */\n AUTH_DELEGATION_REVOKED: \"AUTH_DELEGATION_REVOKED\",\n /** HTTP 401: a delegation the cited one rests on was revoked. */\n AUTH_DELEGATION_ANCESTOR_REVOKED: \"AUTH_DELEGATION_ANCESTOR_REVOKED\",\n NETWORK_ERROR: \"NETWORK_ERROR\",\n TIMEOUT: \"TIMEOUT\",\n ABORTED: \"ABORTED\",\n INVALID_INPUT: \"INVALID_INPUT\",\n PERMISSION_DENIED: \"PERMISSION_DENIED\",\n\n // KV-specific errors\n KV_NOT_FOUND: \"KV_NOT_FOUND\",\n KV_WRITE_FAILED: \"KV_WRITE_FAILED\",\n KV_PRECONDITION_FAILED: \"KV_PRECONDITION_FAILED\",\n KV_CONFLICT: \"KV_CONFLICT\",\n KV_RESPONSE_TOO_LARGE: \"KV_RESPONSE_TOO_LARGE\",\n /** HTTP 410 from the change feed: discard sync state and restart without a cursor; `meta.reason` says why. */\n KV_SYNC_RESET_REQUIRED: \"KV_SYNC_RESET_REQUIRED\",\n /** HTTP 403 from the change feed: the retention grant was refused; `meta.reason` says why. */\n KV_RETENTION_GRANT_REFUSED: \"KV_RETENTION_GRANT_REFUSED\",\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 /** The node answered the usage read without usage numbers (it predates the read). */\n STORAGE_STATUS_UNAVAILABLE: \"STORAGE_STATUS_UNAVAILABLE\",\n\n // DuckDB-specific errors\n DUCKDB_ERROR: \"DUCKDB_ERROR\",\n DUCKDB_PERMISSION_DENIED: \"DUCKDB_PERMISSION_DENIED\",\n DUCKDB_DATABASE_NOT_FOUND: \"DUCKDB_DATABASE_NOT_FOUND\",\n DUCKDB_RESPONSE_TOO_LARGE: \"DUCKDB_RESPONSE_TOO_LARGE\",\n DUCKDB_QUOTA_EXCEEDED: \"DUCKDB_QUOTA_EXCEEDED\",\n DUCKDB_INVALID_STATEMENT: \"DUCKDB_INVALID_STATEMENT\",\n DUCKDB_SCHEMA_ERROR: \"DUCKDB_SCHEMA_ERROR\",\n DUCKDB_READONLY_VIOLATION: \"DUCKDB_READONLY_VIOLATION\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\n// =============================================================================\n// Service Session\n// =============================================================================\n\n/**\n * Session data required for authenticated service operations.\n * Both TinyCloudSession and web-sdk Session can be cast to this interface.\n */\nexport interface ServiceSession {\n /** The delegation header containing the UCAN */\n delegationHeader: { Authorization: string };\n /** The delegation CID */\n delegationCid: string;\n /** The space ID for this session */\n spaceId: string;\n /** The verification method DID */\n verificationMethod: string;\n /** The session key JWK (required for invoke) */\n jwk: object;\n}\n\n// =============================================================================\n// Platform Dependencies (Injected)\n// =============================================================================\n\n/**\n * Headers type - compatible with both browser and Node.js.\n */\nexport type ServiceHeaders = Record<string, string> | [string, string][];\n\n/**\n * A single fact object to include in the UCAN invocation.\n * Facts are key-value objects that the server reads from the UCAN facts field.\n */\nexport interface InvocationFact {\n [key: string]: unknown;\n}\n\n/**\n * Facts to include in the UCAN invocation.\n * This is an array of fact objects per the UCAN spec.\n * Used to pass additional parameters that the server reads from the UCAN facts field.\n */\nexport type InvocationFacts = InvocationFact[];\n\n/**\n * Invoke function signature - platform-specific implementation injected via DI.\n * Both node-sdk-wasm and web-sdk-wasm export this with identical signature.\n *\n * @param session - The service session with delegation data\n * @param service - Service name (e.g., \"kv\")\n * @param path - Resource path or key\n * @param action - Action to perform (e.g., \"tinycloud.kv/get\")\n * @param facts - Optional facts to include in the UCAN (e.g., for capabilities/read params)\n * @returns Headers to include in the request\n */\nexport type InvokeFunction = (\n session: ServiceSession,\n service: string,\n path: string,\n action: string,\n facts?: InvocationFacts,\n) => ServiceHeaders;\n\n/**\n * Multi-resource invocation entry.\n */\nexport interface InvokeAnyEntry {\n /**\n * Legacy space-scoped resource. Optional when `resource` is provided.\n */\n spaceId?: string;\n service: string;\n path: string;\n action: string;\n /** Optional raw resource URI. When set, WASM signs this URI directly. */\n resource?: string;\n /** Capability caveats that must remain on this invocation attenuation. */\n caveats?: Record<string, unknown>[];\n}\n\n/**\n * Invoke function for minting a single authorization header that covers\n * multiple capabilities across one effective invoker.\n */\nexport type InvokeAnyFunction = (\n session: ServiceSession,\n entries: InvokeAnyEntry[],\n facts?: InvocationFacts,\n) => ServiceHeaders;\n\n/**\n * Fetch request options - compatible with standard fetch API.\n */\nexport interface FetchRequestInit {\n method?: string;\n headers?: ServiceHeaders;\n body?: Blob | FormData | string;\n signal?: AbortSignal;\n redirect?: \"error\";\n}\n\n/**\n * Fetch response interface - compatible with standard Response.\n */\nexport interface FetchResponse {\n ok: boolean;\n status: number;\n statusText: string;\n body?: unknown;\n headers: {\n get(name: string): string | null;\n };\n json(): Promise<unknown>;\n text(): Promise<string>;\n arrayBuffer(): Promise<ArrayBuffer>;\n blob(): Promise<Blob>;\n}\n\n/**\n * Fetch function signature - allows for custom fetch implementations.\n * Compatible with both browser fetch and Node.js fetch.\n */\nexport type FetchFunction = (\n url: string,\n init?: FetchRequestInit,\n) => Promise<FetchResponse>;\n\n// =============================================================================\n// Retry Policy\n// =============================================================================\n\n/**\n * Configuration for automatic retry of failed requests.\n */\nexport interface RetryPolicy {\n /** Maximum number of attempts (including initial) */\n maxAttempts: number;\n /** Backoff strategy between retries */\n backoff: \"none\" | \"linear\" | \"exponential\";\n /** Base delay in milliseconds for backoff calculation */\n baseDelayMs: number;\n /** Maximum delay in milliseconds between retries */\n maxDelayMs: number;\n /** Error codes that should trigger a retry */\n retryableErrors: string[];\n}\n\n/**\n * Default retry policy.\n */\nexport const defaultRetryPolicy: RetryPolicy = {\n maxAttempts: 3,\n backoff: \"exponential\",\n baseDelayMs: 1000,\n maxDelayMs: 10000,\n retryableErrors: [ErrorCodes.NETWORK_ERROR, ErrorCodes.TIMEOUT],\n};\n\n// =============================================================================\n// Service Context\n// =============================================================================\n\n/**\n * Event handler function type.\n */\nexport type EventHandler = (data: unknown) => void;\n\n/**\n * SDK telemetry event handler.\n */\nexport type TelemetryEventHandler = (event: string, data: unknown) => void;\n\n/**\n * Default-off telemetry configuration.\n */\nexport type TelemetryConfig =\n | boolean\n | {\n enabled?: boolean;\n onEvent?: TelemetryEventHandler;\n };\n\n/**\n * Service interface - base contract for all services.\n */\nexport interface IService {\n /** Initialize service with context */\n initialize(context: IServiceContext): void;\n\n /** Called when session changes (sign-in, sign-out, refresh) */\n onSessionChange(session: ServiceSession | null): void;\n\n /** Called when SDK signs out - should abort pending operations */\n onSignOut(): void;\n\n /** Service-specific configuration */\n readonly config: Record<string, unknown>;\n}\n\n/**\n * Context provided to services for accessing platform dependencies.\n * The SDK creates this context and passes it to services during initialization.\n */\nexport interface IServiceContext {\n // Session management\n /** Current active session, or null if not authenticated */\n readonly session: ServiceSession | null;\n /** Whether there is an active authenticated session */\n readonly isAuthenticated: boolean;\n\n // Platform dependencies (injected by SDK)\n /** Platform-specific invoke function from WASM binding */\n readonly invoke: InvokeFunction;\n /** Optional multi-resource invoke function */\n readonly invokeAny?: InvokeAnyFunction;\n /** Fetch function (defaults to globalThis.fetch) */\n readonly fetch: FetchFunction;\n /** Available TinyCloud host URLs */\n readonly hosts: string[];\n\n // Cross-service access\n /** Get another registered service by name */\n getService<T extends IService>(name: string): T | undefined;\n\n // Telemetry/Events\n /** Emit a telemetry event */\n emit(event: string, data: unknown): void;\n /** Subscribe to events */\n on(event: string, handler: EventHandler): () => void;\n\n // Lifecycle\n /** Abort signal that fires when SDK signs out. */\n readonly abortSignal: AbortSignal;\n /** Additional abort signal for a scoped operation, when active. */\n readonly operationAbortSignal?: AbortSignal;\n /** Install an additional signal for the duration of a scoped operation. */\n setOperationAbortSignal?(signal: AbortSignal | undefined): void;\n // Retry policy\n /** Retry policy for failed requests */\n readonly retryPolicy: RetryPolicy;\n}\n\n// =============================================================================\n// Telemetry Events\n// =============================================================================\n\n/**\n * Event emitted before a service request.\n */\nexport interface ServiceRequestEvent {\n service: string;\n action: string;\n span?: string;\n key?: string;\n timestamp: number;\n}\n\n/**\n * Event emitted after a service response.\n */\nexport interface ServiceResponseEvent {\n service: string;\n action: string;\n span?: string;\n ok: boolean;\n duration: number;\n durationMs?: number;\n status?: number;\n}\n\n/**\n * Event emitted on service error.\n */\nexport interface ServiceErrorEvent {\n service: string;\n span?: string;\n error: ServiceError;\n}\n\n/**\n * Event emitted on retry attempt.\n */\nexport interface ServiceRetryEvent {\n service: string;\n attempt: number;\n maxAttempts: number;\n error: ServiceError;\n}\n\n/**\n * Generic named span event for aggregating operation timings.\n */\nexport interface TelemetrySpanEvent {\n span: string;\n ok: boolean;\n durationMs: number;\n service?: string;\n action?: string;\n status?: number;\n error?: ServiceError;\n}\n\n/**\n * Telemetry event names.\n */\nexport const TelemetryEvents = {\n SPAN: \"telemetry.span\",\n SERVICE_REQUEST: \"service.request\",\n SERVICE_RESPONSE: \"service.response\",\n SERVICE_ERROR: \"service.error\",\n SERVICE_RETRY: \"service.retry\",\n SESSION_CHANGED: \"session.changed\",\n SESSION_EXPIRED: \"session.expired\",\n REPLICATION_READ: \"replication.read\",\n REPLICATION_WRITE: \"replication.write\",\n REPLICATION_SYNC: \"replication.sync\",\n REPLICATION_STATE: \"replication.state\",\n REPLICATION_DIVERGENCE: \"replication.divergence\",\n REPLICATION_GRANT: \"replication.grant\",\n REPLICATION_LIFECYCLE: \"replication.lifecycle\",\n /** Emitted once, on the first write rejected because storage is full. */\n STORAGE_FULL: \"storage.full\",\n} as const;\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Create a success result.\n */\nexport function ok<T>(data: T): Result<T> {\n return { ok: true, data };\n}\n\n/**\n * Create an error result.\n */\nexport function err<E = ServiceError>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n\n/**\n * Create a ServiceError.\n */\nexport function serviceError(\n code: string,\n message: string,\n service: string,\n options?: { cause?: Error; meta?: Record<string, unknown> },\n): ServiceError {\n return {\n code,\n message,\n service,\n cause: options?.cause,\n meta: options?.meta,\n };\n}\n","/**\n * SDK Services - Error Utilities\n *\n * Utilities for creating and handling service errors.\n */\n\nimport { CAPABILITY_REGISTRY } from \"@tinycloud/bootstrap\";\nimport { parseNetworkId } from \"./encryption/networkId\";\n\nimport {\n type ServiceError,\n ErrorCodes,\n err,\n serviceError,\n type PermissionHint,\n} from \"./types\";\n\n/** Validate one structured node permission hint without accepting raw errors. */\nexport function parsePermissionHint(value: unknown): PermissionHint | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n const candidate = value as Record<string, unknown>;\n const keys = Object.keys(candidate).sort();\n if (keys.some((key) => ![\"actions\", \"path\", \"service\", \"space\"].includes(key))) return undefined;\n if (candidate.service !== \"tinycloud.kv\" && candidate.service !== \"tinycloud.encryption\") return undefined;\n if (typeof candidate.path !== \"string\" || candidate.path.length === 0 || candidate.path.includes(\"*\")) return undefined;\n if (candidate.service === \"tinycloud.kv\" &&\n (typeof candidate.space !== \"string\" || candidate.space.length === 0 || !candidate.path.startsWith(\"vault/\") || candidate.path.endsWith(\"/\"))) {\n return undefined;\n }\n if (candidate.service === \"tinycloud.encryption\" &&\n (candidate.space !== undefined || !candidate.path.startsWith(\"urn:tinycloud:encryption:\") || candidate.path.endsWith(\":\"))) {\n return undefined;\n }\n if (!Array.isArray(candidate.actions) || candidate.actions.length !== 1 ||\n typeof candidate.actions[0] !== \"string\" || candidate.actions[0].includes(\"*\")) return undefined;\n const expectedAction = candidate.service === \"tinycloud.kv\"\n ? \"tinycloud.kv/get\"\n : \"tinycloud.encryption/decrypt\";\n if (candidate.actions[0] !== expectedAction) return undefined;\n const space = typeof candidate.space === \"string\" ? candidate.space : undefined;\n return {\n service: candidate.service,\n ...(space === undefined ? {} : { space }),\n path: candidate.path,\n actions: [candidate.actions[0]],\n };\n}\n\n/** Extract only a validated structured hint from an SDK-owned error body. */\nexport function parsePermissionHintFromErrorText(text: string): PermissionHint | undefined {\n try {\n const parsed: unknown = JSON.parse(text);\n if (typeof parsed !== \"object\" || parsed === null) return undefined;\n const root = parsed as Record<string, unknown>;\n const nested = root.error;\n const nestedRecord = typeof nested === \"object\" && nested !== null\n ? nested as Record<string, unknown>\n : undefined;\n return parsePermissionHint(root.permissionHint) ??\n parsePermissionHint(nestedRecord?.permissionHint);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Create a service error for authentication required.\n */\nexport function authRequiredError(service: string): ServiceError {\n return {\n code: ErrorCodes.AUTH_REQUIRED,\n message: \"Authentication required. Please sign in first.\",\n service,\n };\n}\n\n/**\n * Create a service error for expired authentication.\n */\nexport function authExpiredError(service: string): ServiceError {\n return {\n code: ErrorCodes.AUTH_EXPIRED,\n message: \"Session has expired. Please sign in again.\",\n service,\n };\n}\n\n/**\n * Create a service error for network issues.\n */\nexport function networkError(\n service: string,\n message: string,\n cause?: Error\n): ServiceError {\n return {\n code: ErrorCodes.NETWORK_ERROR,\n message,\n service,\n cause,\n };\n}\n\n/**\n * Abort reason for a service request that exceeded its configured timeout.\n *\n * Named `TimeoutError` (like the reason of the platform's\n * `AbortSignal.timeout()`), so it maps to `ErrorCodes.TIMEOUT` and never to\n * `ErrorCodes.ABORTED`, which is reserved for caller and sign-out aborts.\n */\nexport class RequestTimeoutError extends Error {\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms.`);\n this.name = \"TimeoutError\";\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * Whether an error is a {@link RequestTimeoutError}. Also matches by shape so\n * that a copy of the class from another bundle (ESM/CJS entrypoint) is\n * recognized.\n */\nexport function isRequestTimeoutError(error: unknown): error is RequestTimeoutError {\n return (\n error instanceof RequestTimeoutError ||\n (error instanceof Error &&\n error.name === \"TimeoutError\" &&\n typeof (error as { timeoutMs?: unknown }).timeoutMs === \"number\")\n );\n}\n\n/**\n * Create a service error for timeouts.\n *\n * @param timeoutMs - The configured timeout that elapsed, when known\n * @param cause - The underlying timeout error, when known\n */\nexport function timeoutError(\n service: string,\n timeoutMs?: number,\n cause?: Error\n): ServiceError {\n if (timeoutMs === undefined) {\n return {\n code: ErrorCodes.TIMEOUT,\n message: \"Request timed out.\",\n service,\n ...(cause === undefined ? {} : { cause }),\n };\n }\n return {\n code: ErrorCodes.TIMEOUT,\n message: `Request timed out after ${timeoutMs}ms.`,\n service,\n ...(cause === undefined ? {} : { cause }),\n meta: { timeoutMs },\n };\n}\n\n/**\n * Create a service error for aborted requests.\n */\nexport function abortedError(service: string): ServiceError {\n return {\n code: ErrorCodes.ABORTED,\n message: \"Request was aborted.\",\n service,\n };\n}\n\n/**\n * Create a service error for not found resources.\n */\nexport function notFoundError(\n service: string,\n resource: string\n): ServiceError {\n return {\n code: ErrorCodes.NOT_FOUND,\n message: `Resource not found: ${resource}`,\n service,\n };\n}\n\n/**\n * Create a service error for permission denied.\n */\nexport function permissionDeniedError(\n service: string,\n action: string\n): ServiceError {\n return {\n code: ErrorCodes.PERMISSION_DENIED,\n message: `Permission denied for action: ${action}`,\n service,\n };\n}\n\n/**\n * Parse the server's \"Unauthorized Action: {resource} / {ability}\" pattern.\n */\nexport function parseAuthError(responseText: string): { resource?: string; action?: string } {\n const match = responseText.match(/^Unauthorized Action:\\s*(.+?)\\s*\\/\\s*(tinycloud\\.\\S+)$/m);\n if (match) {\n return { resource: match[1].trim(), action: match[2].trim() };\n }\n return {};\n}\n\n/**\n * Create a service error for unauthorized action (missing capability).\n */\nexport function authUnauthorizedError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return serviceError(ErrorCodes.AUTH_UNAUTHORIZED, message, service, { meta });\n}\n\n/**\n * Authorization verdict read from an error's structure, never its message:\n * - `\"unauthenticated\"`: HTTP 401 — the session is missing or stale.\n * - `\"forbidden\"`: HTTP 403, or `AUTH_UNAUTHORIZED` without a status — the\n * session is valid but lacks the capability.\n * - `\"other\"`: a non-authorization HTTP error status (4xx/5xx).\n */\nexport type AuthorizationVerdict = \"unauthenticated\" | \"forbidden\" | \"other\";\n\nconst grantableActions = new Set(\n CAPABILITY_REGISTRY.map(({ urn }) => urn).filter((urn) => !urn.endsWith(\"/*\"))\n);\n\nconst capabilityServices = new Set(\n CAPABILITY_REGISTRY.map(({ service }) => service.slice(\"tinycloud.\".length))\n);\n\n/**\n * Parse a canonical TinyCloud resource, keeping nested space names intact.\n * The first registered service segment is authoritative: a later matching\n * segment in the path cannot turn a different service into grant advice.\n */\nexport function parseCapabilityResource(\n resource: string,\n service: string\n): { space: string; path: string } | undefined {\n if (\n resource.length === 0 ||\n resource.length > 1024 ||\n !capabilityServices.has(service) ||\n /[\\s\\p{C}*#]/u.test(resource) ||\n resource.includes(\"://\")\n ) return undefined;\n\n const segments = resource.split(\"/\");\n if (!/^tinycloud:[A-Za-z0-9][A-Za-z0-9:._-]*$/.test(segments[0])) return undefined;\n const serviceIndex = segments.findIndex((segment, index) =>\n index > 0 && capabilityServices.has(segment)\n );\n if (serviceIndex < 1 || segments[serviceIndex] !== service) return undefined;\n if (segments.slice(1, serviceIndex).some((segment) =>\n segment.length === 0 || segment === \".\" || segment === \"..\"\n )) return undefined;\n const pathSegments = segments.slice(serviceIndex + 1);\n if (pathSegments.some((segment) => segment === \".\" || segment === \"..\")) return undefined;\n if (service !== \"kv\" && (pathSegments.length === 0 || pathSegments[pathSegments.length - 1] === \"\")) return undefined;\n\n return {\n space: segments.slice(0, serviceIndex).join(\"/\"),\n path: pathSegments.join(\"/\"),\n };\n}\n\n/** A capability resource is a canonical TinyCloud resource or encryption network, never a URL or expression. */\nfunction safeCapabilityResource(value: unknown, service: string, action: string): value is string {\n if (typeof value !== \"string\" || value.length === 0 || value.length > 1024) return false;\n if (service === \"encryption\" && value.startsWith(\"urn:tinycloud:encryption:\")) {\n if (\n !/^[A-Za-z0-9_][A-Za-z0-9_.:/-]*$/.test(value) ||\n value.includes(\"//\") ||\n value.split(\"/\").some((segment) => segment === \".\" || segment === \"..\")\n ) return false;\n try {\n parseNetworkId(value);\n return true;\n } catch {\n return false;\n }\n }\n const parsed = parseCapabilityResource(value, service);\n return parsed !== undefined &&\n (!parsed.path.endsWith(\"/\") || (service === \"kv\" && action === \"tinycloud.kv/list\")) &&\n (parsed.path !== \"\" || (service === \"kv\" && action === \"tinycloud.kv/list\"));\n}\n\n/**\n * Return a grantable missing capability only when typed authorization evidence\n * and both safe, service-matched capability fields are present. Status alone,\n * message text, wildcard actions and unrecognized actions cannot grant scope.\n */\nexport function validatedCapabilityOf(\n error: unknown\n): { resource: string; requiredAction: string } | undefined {\n const verdict = authorizationVerdictOf(error);\n if (verdict !== \"unauthenticated\" && verdict !== \"forbidden\") return undefined;\n\n const seen = new Set<unknown>();\n let node: unknown = error;\n for (\n let depth = 0;\n depth < MAX_CAUSE_DEPTH && typeof node === \"object\" && node !== null && !seen.has(node);\n depth += 1\n ) {\n seen.add(node);\n const record = node as Record<string, unknown>;\n const status = errorStatusOf(record);\n const meta = record.meta;\n if (\n (status === 401 || status === 403 || (status === undefined && record.code === ErrorCodes.AUTH_UNAUTHORIZED)) &&\n typeof meta === \"object\" && meta !== null && !Array.isArray(meta)\n ) {\n const metadata = meta as Record<string, unknown>;\n const { resource, requiredAction } = metadata;\n if (typeof requiredAction === \"string\" && grantableActions.has(requiredAction)) {\n const actionService = requiredAction.slice(\"tinycloud.\".length, requiredAction.indexOf(\"/\"));\n const explicitService = record.service === undefined ? metadata.service : record.service;\n if (\n safeCapabilityResource(resource, actionService, requiredAction) &&\n (explicitService === undefined ||\n explicitService === actionService ||\n explicitService === `tinycloud.${actionService}`)\n ) {\n return { resource, requiredAction };\n }\n }\n }\n node = record.cause;\n }\n return undefined;\n}\n\nconst MAX_CAUSE_DEPTH = 8;\n\n/** The first 4xx/5xx among `status`, `statusCode`, `meta.status`. Success and\n * informational statuses (e.g. a 200 `Response` kept as `cause`) are not\n * failures, so they are skipped rather than ending the search. */\nfunction errorStatusOf(node: Record<string, unknown>): number | undefined {\n const meta = node.meta;\n const candidates = [\n node.status,\n node.statusCode,\n typeof meta === \"object\" && meta !== null\n ? (meta as Record<string, unknown>).status\n : undefined,\n ];\n for (const candidate of candidates) {\n if (\n typeof candidate === \"number\" &&\n Number.isInteger(candidate) &&\n candidate >= 400 &&\n candidate <= 599\n ) {\n return candidate;\n }\n }\n return undefined;\n}\n\n/**\n * Classify an error from its typed HTTP error status (`status`, `statusCode`,\n * or `meta.status`, 4xx/5xx only) and `AUTH_UNAUTHORIZED` code, following the\n * `cause` chain outer-first (at most {@link MAX_CAUSE_DEPTH} links, cycles\n * stop the walk) so wrappers that rethrow with `cause` keep the verdict. The\n * outermost error status decides, so an outer 5xx keeps its retry budget even\n * over an inner 401. Returns `undefined` for untyped errors; only then may\n * callers fall back to message heuristics.\n */\nexport function authorizationVerdictOf(\n error: unknown,\n): AuthorizationVerdict | undefined {\n const seen = new Set<unknown>();\n let sawUnauthorizedCode = false;\n let node: unknown = error;\n for (\n let depth = 0;\n depth < MAX_CAUSE_DEPTH && typeof node === \"object\" && node !== null && !seen.has(node);\n depth += 1\n ) {\n seen.add(node);\n const record = node as Record<string, unknown>;\n const status = errorStatusOf(record);\n if (status === 401) return \"unauthenticated\";\n if (status === 403) return \"forbidden\";\n if (status !== undefined) return \"other\";\n if (record.code === ErrorCodes.AUTH_UNAUTHORIZED) sawUnauthorizedCode = true;\n node = record.cause;\n }\n return sawUnauthorizedCode ? \"forbidden\" : undefined;\n}\n\n/**\n * Create a service error for storage quota exceeded (402 Payment Required).\n */\nexport function storageQuotaExceededError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return {\n code: ErrorCodes.STORAGE_QUOTA_EXCEEDED,\n message,\n service,\n meta,\n };\n}\n\n/**\n * Create a service error for storage limit reached (413 Payload Too Large).\n */\nexport function storageLimitReachedError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return {\n code: ErrorCodes.STORAGE_LIMIT_REACHED,\n message,\n service,\n meta,\n };\n}\n\n/** Shown when a write is rejected because the owner's storage is full. */\nexport const STORAGE_FULL_MESSAGE =\n \"TinyCloud storage is full, so this change was not saved. Reading still works. Free up space or upgrade your plan to save again.\";\n\n/** Shown when a write is larger than the storage the owner has left. */\nexport const STORAGE_WRITE_TOO_LARGE_MESSAGE =\n \"This change is larger than the TinyCloud storage you have left, so it was not saved. Reading still works. Free up space or upgrade your plan to save it.\";\n\n/**\n * Parse the byte counts from a node storage rejection\n * (\"... Used: 155744 bytes, Limit: 0 bytes\"). The limit is the share of the\n * owner's account-wide budget left for this space, so it is 0 when the\n * owner's other spaces already use the whole budget.\n */\nexport function parseStorageQuotaBytes(\n responseText: string\n): { usedBytes: number; limitBytes: number } | undefined {\n const match = responseText.match(/Used:\\s*(\\d+)\\s*bytes,\\s*Limit:\\s*(\\d+)\\s*bytes/i);\n if (!match) return undefined;\n return {\n usedBytes: parseInt(match[1], 10),\n limitBytes: parseInt(match[2], 10),\n };\n}\n\n/**\n * Account-wide storage totals the node attaches to a storage rejection when\n * billing supplied them. `limitBytes` is the plan budget shared by all the\n * owner's spaces, so this is the number to show the user, not the space's.\n */\nexport interface StorageAccountUsage {\n usedBytes: number;\n limitBytes: number;\n /** Plan tier id, e.g. `free` or `paid`. */\n plan?: string;\n}\n\n/** What a node storage rejection says about the space and the account. */\nexport interface StorageRejectionDetails {\n usedBytes?: number;\n limitBytes?: number;\n account?: StorageAccountUsage;\n}\n\nconst STORAGE_REJECTION_BODY_ERRORS: Record<string, true> = {\n storage_quota_exceeded: true,\n storage_limit_reached: true,\n};\n\nfunction isByteCount(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction byteCountsOf(value: unknown): { usedBytes: number; limitBytes: number } | undefined {\n if (typeof value !== \"object\" || value === null) return undefined;\n const { usedBytes, limitBytes } = value as Record<string, unknown>;\n return isByteCount(usedBytes) && isByteCount(limitBytes) ? { usedBytes, limitBytes } : undefined;\n}\n\n/**\n * Parse a node storage rejection. The node sends a JSON body\n * (`{error, message, space: {usedBytes, limitBytes}, account?: {usedBytes,\n * limitBytes, plan}}`); older nodes send only the plain-text message. Returns\n * undefined when the body is neither, e.g. a proxy's own 413 page.\n */\nexport function parseStorageRejection(responseText: string): StorageRejectionDetails | undefined {\n let body: unknown;\n try {\n body = JSON.parse(responseText);\n } catch {\n return parseStorageQuotaBytes(responseText);\n }\n if (typeof body !== \"object\" || body === null) return undefined;\n const record = body as Record<string, unknown>;\n const message = typeof record.message === \"string\" ? record.message : \"\";\n const space = byteCountsOf(record.space) ?? parseStorageQuotaBytes(message);\n const isStorageBody = typeof record.error === \"string\" && STORAGE_REJECTION_BODY_ERRORS[record.error] === true;\n if (!space && !isStorageBody) {\n return undefined;\n }\n const details: StorageRejectionDetails = { ...space };\n const account = byteCountsOf(record.account);\n if (account) {\n const plan = (record.account as Record<string, unknown>).plan;\n details.account = typeof plan === \"string\" && plan !== \"\" ? { ...account, plan } : account;\n }\n return details;\n}\n\n/**\n * Build the error for a write the node rejected for storage: 402 when storage\n * is already full, 413 when this write is larger than what is left. KV, SQL\n * and DuckDB all report it with the same codes and message so an app can\n * detect \"storage full\" in one place with {@link isStorageFullError}.\n * `meta.usedBytes`/`meta.limitBytes` describe the space; `meta.account`\n * carries the account totals when the node sent them.\n */\nexport function storageRejectionError(\n service: string,\n status: 402 | 413,\n meta: Record<string, unknown>,\n responseText: string\n): ServiceError {\n const quotaMeta = { ...meta, ...parseStorageRejection(responseText) };\n return status === 402\n ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta)\n : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);\n}\n\n/**\n * True when a write failed for storage: the owner's TinyCloud storage is full\n * (`STORAGE_QUOTA_EXCEEDED`), or this write is larger than what is left\n * (`STORAGE_LIMIT_REACHED`). Reads keep working in both cases, so apps should\n * stay usable read-only and point the user at freeing up space or upgrading.\n */\nexport function isStorageFullError(error: { code?: string } | null | undefined): boolean {\n return (\n error?.code === ErrorCodes.STORAGE_QUOTA_EXCEEDED ||\n error?.code === ErrorCodes.STORAGE_LIMIT_REACHED\n );\n}\n\n/**\n * Wrap an unknown error in a ServiceError.\n */\nexport function wrapError(\n service: string,\n error: unknown,\n defaultCode: string = ErrorCodes.NETWORK_ERROR\n): ServiceError {\n if (error instanceof Error) {\n // A configured request timeout elapsed (see BaseService.createRequestSignal)\n if (isRequestTimeoutError(error)) {\n return timeoutError(service, error.timeoutMs, error);\n }\n\n // Check for abort errors\n if (error.name === \"AbortError\") {\n return abortedError(service);\n }\n\n // Check for timeout errors (varies by platform)\n if (\n error.name === \"TimeoutError\" ||\n error.message.toLowerCase().includes(\"timeout\")\n ) {\n return timeoutError(service);\n }\n\n return {\n code: defaultCode,\n message: error.message,\n service,\n cause: error,\n };\n }\n\n return {\n code: defaultCode,\n message: String(error),\n service,\n };\n}\n\n/**\n * Create an error Result from a ServiceError.\n */\nexport function errorResult(error: ServiceError) {\n return err(error);\n}\n","/**\n * TinyCloud encryption network identifiers.\n *\n * A network id is `urn:tinycloud:encryption:<ownerDid>:<network>` where\n * `ownerDid` is the owner's DID and `network` is a\n * non-empty label drawn from `[a-z0-9][a-z0-9-]*`.\n *\n * The embedded owner DID is the root authority for the network: any\n * delegation chain ending in a `tinycloud.encryption/decrypt` grant on\n * the network must root at this owner DID.\n */\n\nconst URN_PREFIX = \"urn:tinycloud:encryption:\";\nconst NETWORK_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;\nconst PKH_EIP155_DID_RE = /^did:pkh:eip155:(\\d+):(0x[a-fA-F0-9]{40})$/;\n\nexport interface ParsedNetworkId {\n /** The full URN string. */\n networkId: string;\n /** Owner DID embedded in the URN (the network's root authority). */\n ownerDid: string;\n /** Network label (the suffix after the owner DID). */\n name: string;\n}\n\nexport class NetworkIdError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"NetworkIdError\";\n }\n}\n\n/**\n * Parse a network id string into its owner DID and name components.\n *\n * Throws {@link NetworkIdError} when the input does not match\n * `urn:tinycloud:encryption:<did>:<name>`, when the embedded DID is\n * malformed, or when the network name fails {@link NETWORK_NAME_RE}.\n */\nexport function parseNetworkId(networkId: string): ParsedNetworkId {\n if (typeof networkId !== \"string\" || networkId.length === 0) {\n throw new NetworkIdError(\"networkId must be a non-empty string\");\n }\n if (!networkId.startsWith(URN_PREFIX)) {\n throw new NetworkIdError(\n `networkId must start with ${URN_PREFIX} (got ${JSON.stringify(networkId)})`,\n );\n }\n const body = networkId.slice(URN_PREFIX.length);\n // `body` = \"<ownerDid>:<network>\"\n // ownerDid contains ':' (e.g. did:key:z6Mk...), so we split on the LAST colon\n // and treat the suffix as the network name.\n const lastColon = body.lastIndexOf(\":\");\n if (lastColon <= 0 || lastColon === body.length - 1) {\n throw new NetworkIdError(\n `networkId missing ownerDid or name segment (got ${JSON.stringify(networkId)})`,\n );\n }\n const ownerDid = body.slice(0, lastColon);\n const name = body.slice(lastColon + 1);\n\n if (!ownerDid.startsWith(\"did:\")) {\n throw new NetworkIdError(\n `networkId ownerDid must be a DID (got ${JSON.stringify(ownerDid)})`,\n );\n }\n // Minimal DID shape: did:<method>:<id> — three colon-separated segments,\n // each non-empty.\n const didParts = ownerDid.split(\":\");\n if (didParts.length < 3 || didParts.some((p) => p.length === 0)) {\n throw new NetworkIdError(\n `networkId ownerDid is not a well-formed DID (got ${JSON.stringify(ownerDid)})`,\n );\n }\n if (!NETWORK_NAME_RE.test(name)) {\n throw new NetworkIdError(\n `networkId name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`,\n );\n }\n return { networkId, ownerDid, name };\n}\n\n/**\n * Construct a network id URN from an owner DID and a network name.\n * Validates inputs and throws {@link NetworkIdError} on bad shape.\n */\nexport function buildNetworkId(ownerDid: string, name: string): string {\n if (typeof ownerDid !== \"string\" || !ownerDid.startsWith(\"did:\")) {\n throw new NetworkIdError(\"ownerDid must be a DID\");\n }\n if (typeof name !== \"string\" || !NETWORK_NAME_RE.test(name)) {\n throw new NetworkIdError(\n `network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`,\n );\n }\n const networkId = `${URN_PREFIX}${ownerDid}:${name}`;\n // Re-validate the composed result so the same error path triggers\n // for caller inputs that compose into a malformed URN.\n parseNetworkId(networkId);\n return networkId;\n}\n\n/**\n * Returns true when {@link networkId} is a syntactically valid network URN.\n */\nexport function isNetworkId(networkId: unknown): networkId is string {\n if (typeof networkId !== \"string\") {\n return false;\n }\n try {\n parseNetworkId(networkId);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction parsePkhOwnerDid(ownerDid: string):\n | { chainId: string; address: string }\n | null {\n const match = ownerDid.match(PKH_EIP155_DID_RE);\n if (!match) return null;\n return {\n chainId: match[1],\n address: match[2].toLowerCase(),\n };\n}\n\n/**\n * Compare owner DIDs as network principals. For `did:pkh:eip155`, EVM\n * address casing is not part of principal identity; other DID methods\n * remain exact string matches.\n */\nexport function ownerDidMatches(a: string, b: string): boolean {\n const aPkh = parsePkhOwnerDid(a);\n const bPkh = parsePkhOwnerDid(b);\n if (aPkh && bPkh) {\n return aPkh.chainId === bPkh.chainId && aPkh.address === bPkh.address;\n }\n return a === b;\n}\n\n/**\n * Resolve the discovery key used to look up a network's descriptor under\n * an owner's public account-space.\n *\n * Format: `.well-known/encryption/network/<name>`.\n */\nexport function networkDiscoveryKey(name: string): string {\n if (!NETWORK_NAME_RE.test(name)) {\n throw new NetworkIdError(\n `network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`,\n );\n }\n return `.well-known/encryption/network/${name}`;\n}\n\nexport const ENCRYPTION_NETWORK_URN_PREFIX = URN_PREFIX;\nexport const NETWORK_NAME_PATTERN = NETWORK_NAME_RE;\n","/**\n * BaseService - Abstract base class for all TinyCloud services.\n *\n * Provides common functionality:\n * - Context management\n * - Session lifecycle hooks\n * - Abort signal handling\n * - Telemetry emission\n */\n\nimport {\n IService,\n IServiceContext,\n ServiceSession,\n ServiceError,\n TelemetryEvents,\n err,\n Result,\n} from \"../types\";\nimport { authRequiredError, wrapError, RequestTimeoutError } from \"../errors\";\nimport type { RequestSignal } from \"./types\";\n\n/** setTimeout's delay is a signed 32-bit integer; larger values fire at once. */\nconst MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/**\n * Abstract base class for TinyCloud services.\n *\n * Services extend this class to get common functionality like\n * context management, session lifecycle, and abort handling.\n *\n * @example\n * ```typescript\n * class MyService extends BaseService implements IMyService {\n * static readonly serviceName = 'myservice';\n *\n * constructor(config: MyServiceConfig = {}) {\n * super();\n * this._config = config;\n * }\n *\n * async doSomething(): Promise<Result<Data>> {\n * if (!this.requireAuth()) {\n * return err(authRequiredError('myservice'));\n * }\n * // ... implementation\n * }\n * }\n * ```\n */\nexport abstract class BaseService implements IService {\n /**\n * Service identifier used for registration.\n * Must be overridden by subclasses.\n */\n static readonly serviceName: string;\n\n /**\n * Service context providing access to platform dependencies.\n * Set during initialize().\n */\n protected context!: IServiceContext;\n\n /**\n * Abort controller for this service's operations.\n * Reset on sign-out.\n */\n protected abortController: AbortController = new AbortController();\n\n /**\n * Service-specific configuration.\n */\n protected _config: Record<string, unknown> = {};\n\n /**\n * Get the service configuration.\n */\n get config(): Record<string, unknown> {\n return this._config;\n }\n\n /**\n * Initialize the service with context.\n * Called by the SDK after instantiation.\n *\n * @param context - The service context\n */\n initialize(context: IServiceContext): void {\n this.context = context;\n }\n\n /**\n * Called when session changes (sign-in, sign-out, refresh).\n * Override in subclasses to handle session changes.\n *\n * @param session - The new session, or null if signed out\n */\n onSessionChange(session: ServiceSession | null): void {\n // Override in subclass if needed\n }\n\n /**\n * Called when SDK signs out.\n * Aborts all pending operations.\n */\n onSignOut(): void {\n this.abortController.abort();\n this.abortController = new AbortController();\n }\n\n /**\n * Get the abort signal for this service.\n * Combines the service-level abort with context-level abort.\n */\n protected get abortSignal(): AbortSignal {\n return this.combineSignals();\n }\n\n /**\n * Check if the service is authenticated.\n */\n protected get isAuthenticated(): boolean {\n return this.context?.isAuthenticated ?? false;\n }\n\n /**\n * Get the current session.\n * Throws if not authenticated.\n */\n protected get session(): ServiceSession {\n if (!this.context?.session) {\n throw new Error(\"Not authenticated\");\n }\n return this.context.session;\n }\n\n /**\n * Check authentication and return error result if not authenticated.\n * Use this at the start of methods that require authentication.\n *\n * @returns true if authenticated, false otherwise\n */\n protected requireAuth(): boolean {\n return this.isAuthenticated;\n }\n\n /**\n * Emit a telemetry event.\n *\n * @param event - Event name\n * @param data - Event data\n */\n protected emit(event: string, data: unknown): void {\n this.context?.emit(event, data);\n }\n\n /**\n * Emit a service request event.\n *\n * @param action - The action being performed\n * @param key - Optional key/path being accessed\n */\n protected emitRequest(action: string, key?: string): void {\n const service = this.getServiceName();\n this.emit(TelemetryEvents.SERVICE_REQUEST, {\n service,\n action,\n span: this.spanName(action),\n key,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Emit a service response event.\n *\n * @param action - The action that was performed\n * @param ok - Whether the request was successful\n * @param startTime - Start time for duration calculation\n * @param status - Optional HTTP status code\n */\n protected emitResponse(\n action: string,\n ok: boolean,\n startTime: number,\n status?: number\n ): void {\n const service = this.getServiceName();\n const durationMs = Date.now() - startTime;\n const span = this.spanName(action);\n this.emit(TelemetryEvents.SERVICE_RESPONSE, {\n service,\n action,\n span,\n ok,\n duration: durationMs,\n durationMs,\n status,\n });\n this.emit(TelemetryEvents.SPAN, {\n span,\n service,\n action,\n ok,\n durationMs,\n status,\n });\n }\n\n /**\n * Emit a service error event.\n *\n * @param error - The service error\n */\n protected emitError(error: ServiceError, action?: string): void {\n const span = action ? this.spanName(action) : undefined;\n this.emit(TelemetryEvents.SERVICE_ERROR, {\n service: this.getServiceName(),\n ...(span ? { span } : {}),\n error,\n });\n }\n\n /**\n * Get the service name from the static property.\n * Subclasses must define static serviceName.\n */\n protected getServiceName(): string {\n return (this.constructor as typeof BaseService).serviceName;\n }\n\n /**\n * Stable span name used by SDK telemetry sinks.\n */\n protected spanName(action: string): string {\n return `sdk.${this.getServiceName()}.${action}`;\n }\n\n /**\n * Create a combined abort signal from multiple sources.\n *\n * @param signals - Additional abort signals to combine\n * @returns A combined abort signal\n */\n protected combineSignals(...signals: (AbortSignal | undefined)[]): AbortSignal {\n const controller = new AbortController();\n const allSignals = [\n this.abortController.signal,\n this.context?.abortSignal,\n ...signals.filter(Boolean),\n ].filter(Boolean) as AbortSignal[];\n\n for (const signal of allSignals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n signal.addEventListener(\"abort\", () => controller.abort(signal.reason), {\n once: true,\n });\n }\n\n return controller.signal;\n }\n\n /**\n * Create the abort signal for one request, bounded by the request timeout.\n *\n * The signal aborts when the service signs out, the context aborts, the\n * caller's `signal` aborts, or the timeout elapses, whichever comes first.\n * A timeout aborts with a {@link RequestTimeoutError} (`TimeoutError`), which\n * `wrapError` maps to `ErrorCodes.TIMEOUT`; the other sources keep their own\n * reason (normally `AbortError`, mapped to `ErrorCodes.ABORTED`).\n *\n * The timeout is `timeoutMs` when given, otherwise `config.timeout`. Only a\n * positive, finite value (at most 2^31-1 ms) applies; `undefined`, `0`, and\n * `Infinity` mean no timeout, so a per-call `0` opts out of a configured one.\n *\n * Callers must call `dispose()` once the response body has been consumed,\n * which clears the timer and removes the listeners this adds to the\n * long-lived service and context signals.\n *\n * @param signal - The caller's abort signal for this request\n * @param timeoutMs - Per-request timeout override in milliseconds\n */\n protected createRequestSignal(\n signal?: AbortSignal,\n timeoutMs?: number\n ): RequestSignal {\n const controller = new AbortController();\n const detachers: Array<() => void> = [];\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const dispose = (): void => {\n if (timer !== undefined) {\n clearTimeout(timer);\n timer = undefined;\n }\n for (const detach of detachers.splice(0)) {\n detach();\n }\n };\n const abort = (reason: unknown): void => {\n dispose();\n controller.abort(reason);\n };\n const requestSignal: RequestSignal = { signal: controller.signal, dispose };\n\n const parents = [\n this.abortController.signal,\n this.context?.abortSignal,\n this.context?.operationAbortSignal,\n signal,\n ].filter((parent): parent is AbortSignal => parent !== undefined);\n for (const parent of parents) {\n if (parent.aborted) {\n abort(parent.reason);\n return requestSignal;\n }\n const onAbort = (): void => abort(parent.reason);\n parent.addEventListener(\"abort\", onAbort, { once: true });\n detachers.push(() => parent.removeEventListener(\"abort\", onAbort));\n }\n\n const timeout = timeoutMs ?? this._config.timeout;\n if (\n typeof timeout === \"number\" &&\n timeout > 0 &&\n timeout <= MAX_TIMER_DELAY_MS\n ) {\n timer = setTimeout(() => abort(new RequestTimeoutError(timeout)), timeout);\n }\n\n return requestSignal;\n }\n\n /**\n * Wrap an operation with error handling and telemetry.\n *\n * @param action - The action name for telemetry\n * @param key - Optional key for telemetry\n * @param operation - The operation to execute\n * @returns Result of the operation\n */\n protected async withTelemetry<T>(\n action: string,\n key: string | undefined,\n operation: () => Promise<Result<T>>\n ): Promise<Result<T>> {\n const startTime = Date.now();\n this.emitRequest(action, key);\n\n try {\n const result = await operation();\n\n if (result.ok) {\n this.emitResponse(action, true, startTime);\n } else {\n this.emitResponse(action, false, startTime);\n this.emitError(result.error, action);\n }\n\n return result;\n } catch (error) {\n const serviceError = wrapError(this.getServiceName(), error);\n this.emitResponse(action, false, startTime);\n this.emitError(serviceError, action);\n return err(serviceError);\n }\n }\n}\n","/**\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, ok, err, ErrorCodes, serviceError } from \"../types\";\nimport {\n KVGetOptions,\n KVPutOptions,\n KVBatchPutItem,\n KVBatchPutOptions,\n KVBatchPutResponse,\n KVBatchReadResponse,\n KVListOptions,\n KVChange,\n KVChangesOptions,\n KVChangesResponse,\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 batchGet<T = unknown>(\n keys: string[],\n options?: Omit<KVGetOptions, 'prefix'>\n ): Promise<Result<KVBatchReadResponse<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 * Store multiple values within this prefix in one TinyCloud KV invocation.\n */\n batchPut(\n items: KVBatchPutItem[],\n options?: Omit<KVBatchPutOptions, 'prefix'>\n ): Promise<Result<KVBatchPutResponse>>;\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 * Read one page of the change feed for everything under this prefix\n * (`tinycloud.kv/sync` on `<prefix>/`). Keys are returned relative to the\n * prefix, like `list`; `source.prefix` stays the node's full prefix.\n * See `IKVService.changes` for paging and errors.\n *\n * @example\n * ```typescript\n * const notes = kv.withPrefix('notes');\n * const page = await notes.changes({ limit: 100 });\n * // -> Feed of: notes/*, keys like 'a.md'\n * ```\n */\n changes(options?: Omit<KVChangesOptions, 'prefix'>): Promise<Result<KVChangesResponse>>;\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(\n key: string,\n options?: Omit<KVDeleteOptions, 'prefix'>\n ): Promise<Result<KVResponse<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 batchHead(\n keys: string[],\n options?: Omit<KVHeadOptions, 'prefix'>\n ): Promise<Result<KVBatchReadResponse<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 batchGet<T = unknown>(\n keys: string[],\n options?: KVGetOptions\n ): Promise<Result<KVBatchReadResponse<T>>>;\n\n put(\n key: string,\n value: unknown,\n options?: KVPutOptions\n ): Promise<Result<KVResponse<void>>>;\n\n batchPut(\n items: KVBatchPutItem[],\n options?: KVBatchPutOptions\n ): Promise<Result<KVBatchPutResponse>>;\n\n list(options?: KVListOptions): Promise<Result<KVListResponse>>;\n\n changes(options: KVChangesOptions): Promise<Result<KVChangesResponse>>;\n\n delete(key: string, options?: KVDeleteOptions): Promise<Result<KVResponse<void>>>;\n\n head(key: string, options?: KVHeadOptions): Promise<Result<KVResponse<void>>>;\n\n batchHead(\n keys: string[],\n options?: KVHeadOptions\n ): Promise<Result<KVBatchReadResponse<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 async batchGet<T = unknown>(\n keys: string[],\n options?: Omit<KVGetOptions, 'prefix'>\n ): Promise<Result<KVBatchReadResponse<T>>> {\n const response = await this._kv.batchGet<T>(\n keys.map((key) => this.getFullKey(key)),\n { ...options, prefix: '' }\n );\n if (!response.ok) return response;\n return ok({\n ...response.data,\n results: response.data.results.map((item, index) => ({\n ...item,\n key: keys[index]!,\n })),\n });\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 * Store multiple values within this prefix in one TinyCloud KV invocation.\n */\n async batchPut(\n items: KVBatchPutItem[],\n options?: Omit<KVBatchPutOptions, 'prefix'>\n ): Promise<Result<KVBatchPutResponse>> {\n return this._kv.batchPut(\n items.map((item) => ({\n ...item,\n key: this.getFullKey(item.key),\n })),\n { ...options, prefix: '' }\n );\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 * Read the change feed for everything under this prefix. A view with an\n * empty prefix (`\"\"` or `\"/\"`) is refused: the node does not serve a\n * whole-space feed.\n */\n async changes(\n options?: Omit<KVChangesOptions, 'prefix'>\n ): Promise<Result<KVChangesResponse>> {\n if (this._prefix.replace(/\\/+$/, '') === '') {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n 'KV changes needs a non-empty prefix; this view has none',\n 'kv'\n ));\n }\n const syncPrefix = `${this._prefix}/`;\n const response = await this._kv.changes({ ...options, prefix: syncPrefix });\n if (!response.ok) return response;\n return ok({\n ...response.data,\n changes: response.data.changes.map((change): KVChange => ({\n ...change,\n key: change.key.startsWith(syncPrefix)\n ? change.key.slice(syncPrefix.length)\n : change.key,\n })),\n });\n }\n\n /**\n * Delete a key.\n */\n async delete(\n key: string,\n options?: Omit<KVDeleteOptions, 'prefix'>\n ): Promise<Result<KVResponse<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 async batchHead(\n keys: string[],\n options?: Omit<KVHeadOptions, 'prefix'>\n ): Promise<Result<KVBatchReadResponse<void>>> {\n const response = await this._kv.batchHead(\n keys.map((key) => this.getFullKey(key)),\n { ...options, prefix: '' }\n );\n if (!response.ok) return response;\n return ok({\n ...response.data,\n results: response.data.results.map((item, index) => ({\n ...item,\n key: keys[index]!,\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?: 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\nimport type { Result } from \"../types\";\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 each KV request, including reading\n * the response body. When it elapses the request is aborted and the\n * operation returns an `ErrorCodes.TIMEOUT` error. A per-call `timeout`\n * option overrides it. Unset (the default), `0`, or `Infinity` means no\n * timeout.\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 * Require this read to bypass replication and use the network directly.\n * A configured read-through observer receives `NETWORK_REQUESTED` after the\n * network result settles, with its outcome and latency. This is supported by\n * `get` and `list`; `head`, `batchGet`, `batchHead` and signed URL operations\n * always use the network.\n */\n source?: \"network\";\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 * Return the raw response bytes as a Uint8Array instead of parsed/text data.\n * Use this to read back binary values (e.g. images) byte-identically. Takes\n * precedence over {@link raw}.\n */\n binary?: boolean;\n\n /**\n * Ask the node to reject the response when the stored value exceeds this\n * number of bytes. This bounds downloads before the response body is read.\n */\n maxResponseBytes?: number;\n\n /**\n * Custom timeout for this operation in milliseconds. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\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 /** Only write when the current object has this strong ETag. */\n ifMatch?: string;\n\n /** Only write when the key does not exist. */\n ifNoneMatch?: \"*\";\n\n /**\n * Custom timeout for this operation in milliseconds. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * One entry in a KV batch put request.\n */\nexport interface KVBatchPutItem {\n /**\n * The key to store under.\n */\n key: string;\n\n /**\n * The value to store.\n *\n * Objects are JSON stringified. Strings are stored as text. Binary values\n * should be supplied as Blob, ArrayBuffer, or Uint8Array.\n */\n value: unknown;\n\n /**\n * Content type for this item. Defaults to application/json for objects and\n * application/octet-stream for binary values.\n */\n contentType?: string;\n}\n\n/**\n * Options for KV batch put operations.\n */\nexport interface KVBatchPutOptions {\n /**\n * Override the default prefix for all entries in this batch.\n */\n prefix?: string;\n\n /**\n * Custom timeout for this operation in milliseconds. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Response from KV batch put operations.\n */\nexport interface KVBatchPutResponse {\n /**\n * Keys successfully written by the batch.\n */\n written: string[];\n\n /**\n * Number of written keys.\n */\n count: number;\n}\n\n/**\n * Response from a KV batch read. Transport or authorization failures are\n * returned by the outer Result; each requested key has its own Result so a\n * missing key does not discard successful siblings.\n */\nexport interface KVBatchReadResponse<T = unknown> {\n results: Array<{\n key: string;\n result: Result<KVResponse<T>>;\n }>;\n count: number;\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 * Require this read to bypass replication and use the network directly. A\n * configured read-through observer receives `NETWORK_REQUESTED` after the\n * network result settles, with its outcome and latency.\n */\n source?: \"network\";\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 /** Maximum number of keys the node may return. */\n limit?: number;\n\n /** Opaque scope-bound cursor returned by a previous bounded list. */\n cursor?: string;\n\n /**\n * Custom timeout for this operation in milliseconds. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\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 /** Only delete when the current object has this strong ETag. */\n ifMatch?: string;\n\n /**\n * Custom timeout for this operation in milliseconds. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\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. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\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. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\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 /** True when more matching keys exist than the requested limit. */\n truncated?: boolean;\n\n /** Opaque cursor for the next page, when the node returned one. */\n nextCursor?: string;\n}\n\n/**\n * Options for {@link IKVService.changes}, the `tinycloud.kv/sync` change feed.\n */\nexport interface KVChangesOptions {\n /**\n * The space-relative KV prefix to follow, sent as the invocation path. It\n * is not joined with the service's configured prefix, and must be non-empty.\n * Prefixes match whole path segments: `notes` covers `notes` and\n * `notes/a`, while `notes/` covers everything under `notes/` but not\n * `notes` itself. The session must hold `tinycloud.kv/sync` on it by name.\n *\n * Returned `changes[].key` values are full paths in the space, like this\n * prefix. On a service with a configured prefix (for example\n * `DelegatedAccess.kv`, whose prefix is the delegation path), `get` joins\n * that prefix again, so strip it from the key first, or follow the feed\n * with `kv.withPrefix(...).changes()`, which returns relative keys.\n */\n prefix: string;\n\n /**\n * The `cursor` from the previous page. Omit it to bootstrap from the start\n * of the prefix. Opaque; never parse or build one.\n */\n cursor?: string;\n\n /**\n * Page size, 1 through 1000 (node default 500). A page never splits one\n * invocation's changes, so it can hold more than `limit` changes.\n */\n limit?: number;\n\n /**\n * CID of a never-invoked delegation carrying `tinycloud.kv/retain` on the\n * prefix. When valid, the node attests `authority.retainUntil`.\n */\n retentionGrant?: string;\n\n /**\n * Custom timeout for this operation in milliseconds. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * One key's latest state in a change feed page. Content is not included:\n * read it with `get`/`batchGet` and check it against `etag`.\n */\nexport type KVChange =\n | {\n key: string;\n deleted: false;\n /** The strong ETag `get` returns for this state. */\n etag: string;\n metadata: Record<string, string>;\n }\n | {\n key: string;\n deleted: true;\n };\n\n/**\n * The node-attested window in which the caller's authority holds. Values are\n * RFC 3339 timestamps, or `null` when nothing in the chain sets one.\n */\nexport interface KVChangesAuthority {\n /** The latest `nbf` across the invocation's whole delegation chain. */\n notBefore: string | null;\n /** The earliest `exp` across the invocation's whole delegation chain. */\n expiresAt: string | null;\n /**\n * The earliest `exp` across a valid retention grant's own chain. `null`\n * means no retention: none was presented, or its chain sets no expiry.\n */\n retainUntil: string | null;\n}\n\n/**\n * One page of the `tinycloud.kv/sync` change feed.\n */\nexport interface KVChangesResponse {\n /** Each changed key's latest state, in commit order. */\n changes: KVChange[];\n /**\n * More changes are available now. A page may be empty with `more: true`;\n * keep paging with the returned cursor.\n */\n more: boolean;\n /**\n * The cursor for the next request. An empty poll returns the request's\n * cursor unchanged.\n */\n cursor: string;\n /** The node, space and prefix that served the page. */\n source: {\n nodeDid: string;\n space: string;\n prefix: string;\n };\n authority: KVChangesAuthority;\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 /** The change feed; never implied by `*` or `tinycloud.kv/*`. */\n SYNC: \"tinycloud.kv/sync\",\n /** Retention attestation; presented through `retentionGrant`, never invoked or implied. */\n RETAIN: \"tinycloud.kv/retain\",\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 type { KVReadThrough, KVListPage } from \"./replication/types\";\nimport {\n authRequiredError,\n wrapError,\n storageRejectionError,\n parseStorageRejection,\n parseAuthError,\n parsePermissionHintFromErrorText,\n authUnauthorizedError,\n validatedCapabilityOf,\n isRequestTimeoutError,\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 KVBatchPutItem,\n KVBatchPutOptions,\n KVBatchPutResponse,\n KVBatchReadResponse,\n KVListOptions,\n KVChange,\n KVChangesOptions,\n KVChangesResponse,\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\ninterface KvBatchNodeItem {\n key: string;\n ok: boolean;\n dataBase64?: string;\n headers?: Record<string, string>;\n error?: { code: string; message: string };\n}\n\ninterface KvBatchNodeResponse {\n results: KvBatchNodeItem[];\n}\n\nconst MAX_KV_BATCH_READ_ITEMS = 100;\n\nfunction isJsonObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\nconst pathByteEncoder = new TextEncoder();\n\n/**\n * Lexicographic UTF-8 byte comparison, matching the ordering of the node's\n * `BTreeMap` (Rust `String` ordering is by UTF-8 bytes). JS string comparison\n * is by UTF-16 code unit, which diverges outside the BMP.\n */\nfunction comparePathsByBytes(a: string, b: string): number {\n const left = pathByteEncoder.encode(a);\n const right = pathByteEncoder.encode(b);\n const length = Math.min(left.length, right.length);\n for (let index = 0; index < length; index++) {\n if (left[index] !== right[index]) {\n return left[index]! - right[index]!;\n }\n }\n return left.length - right.length;\n}\n\n\n/**\n * The node's whole 401 body for a revoked chain: tinycloud-core's\n * `InvocationError::DelegationRevoked` (`delegation-revoked: <cid>`) or\n * `DelegationAncestorRevoked` (`delegation-ancestor-revoked: ancestor=<cid>\n * invoked=<cid>`), rendered as-is (older nodes prefixed `Invalid invocation: `).\n * Anchored: only CIDs follow the error kind, so a key, prefix or path in any\n * other 401 (e.g. `Unauthorized Action: <space>/kv/<prefix> / ...`) never matches.\n */\nconst REVOKED_BODY =\n /^(?:Invalid invocation: )?delegation-(?:(revoked): [A-Za-z0-9]+|(ancestor-revoked): ancestor=[A-Za-z0-9]+ invoked=[A-Za-z0-9]+)$/;\n\nfunction revocationCodeOf(\n body: string\n): typeof ErrorCodes.AUTH_DELEGATION_REVOKED | typeof ErrorCodes.AUTH_DELEGATION_ANCESTOR_REVOKED | undefined {\n const match = REVOKED_BODY.exec(body.trim());\n if (match === null) return undefined;\n return match[1] !== undefined ? ErrorCodes.AUTH_DELEGATION_REVOKED : ErrorCodes.AUTH_DELEGATION_ANCESTOR_REVOKED;\n}\n\nfunction encodeKvBatchPartName(path: string): string {\n return encodeURIComponent(path).replace(/[!'()*]/g, (char) =>\n `%${char.charCodeAt(0).toString(16).toUpperCase()}`\n );\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 private readThrough: KVReadThrough | null = null;\n private readThroughReady: Promise<void> | null = null;\n\n /** Delay KV routing until an asynchronously loaded read-through is attached. */\n setReadThroughReady(ready: Promise<unknown>): void {\n const settled = ready.then(() => undefined, () => undefined);\n this.readThroughReady = settled;\n void settled.then(() => {\n if (this.readThroughReady === settled) this.readThroughReady = null;\n });\n }\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 /** Install or remove the optional replication read-through policy. */\n setReadThrough(readThrough: KVReadThrough | null): void {\n this.readThrough = readThrough;\n }\n\n\n private handleQuotaErrorResponse(\n response: FetchResponse,\n errorText: string,\n key: string\n ): Result<never> | undefined {\n if (response.status !== 402 && response.status !== 413) {\n return undefined;\n }\n // A 413 is about storage only when the node says so. A proxy's own\n // request-size limit (Cloudflare's HTML 413) is not.\n if (response.status === 413 && !parseStorageRejection(errorText)) {\n return undefined;\n }\n return err(\n storageRejectionError(\n \"kv\",\n response.status,\n { status: response.status, statusText: response.statusText, key },\n errorText\n )\n );\n }\n\n /**\n * Classify a KV 404 by reading the response body once.\n *\n * The server returns 404 both for a genuinely missing key AND for an\n * un-hosted space (body \"Space not found\"). Previously get/head/delete\n * collapsed every 404 to KV_NOT_FOUND before reading the body, so an\n * un-hosted-space read was indistinguishable from a missing key. We now\n * preserve status + the \"Space not found\" body for the un-hosted case (so the\n * CLI/SDK can normalize it to SPACE_NOT_HOSTED, matching put/list/sql), and\n * fall through to KV_NOT_FOUND for a real missing key.\n */\n private async classifyNotFound(\n response: FetchResponse,\n key: string\n ): Promise<Result<never>> {\n const errorText = await response.text();\n if (/space not found/i.test(errorText)) {\n return err(\n serviceError(\n ErrorCodes.KV_NOT_FOUND,\n `KV ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n return err(serviceError(ErrorCodes.KV_NOT_FOUND, `Key not found: ${key}`, \"kv\"));\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 // A delegated share may scope this service to one exact key. In that\n // case `get(\"\")` means that key itself, not a synthetic child with a\n // trailing slash. This also keeps the service's prefix semantics useful\n // for exact (rather than prefix) capabilities.\n return prefix ? (key === \"\" ? 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 - Request signal from createRequestSignal()\n * @returns Fetch response\n */\n private async invokeOperation(\n path: string,\n action: string,\n body: Blob | string | undefined,\n signal: AbortSignal,\n extraHeaders?: Readonly<Record<string, string>>\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 const requestHeaders: ServiceHeaders = Array.isArray(headers)\n ? [...headers, ...Object.entries(extraHeaders ?? {})]\n : { ...headers, ...extraHeaders };\n\n return this.context.fetch(`${this.host}/invoke`, {\n method: \"POST\",\n headers: requestHeaders,\n body,\n signal,\n });\n }\n\n /**\n * Serialize a single put value into a fetch body.\n *\n * Binary values (Blob/ArrayBuffer/typed-array, incl. Node Buffer) are sent as\n * raw bytes (as a Blob) so they round-trip byte-identically — without this a\n * Buffer would be JSON.stringify'd into `{\"type\":\"Buffer\",\"data\":[...]}`.\n * Strings are returned unchanged (preserving prior behavior); other values are\n * JSON-encoded. `contentType` overrides the inferred type for binary values.\n */\n private serializePutValue(\n value: unknown,\n contentType?: string\n ): Blob | string {\n if (value instanceof Blob) {\n if (!contentType || value.type === contentType) {\n return value;\n }\n return new Blob([value], { type: contentType });\n }\n\n if (value instanceof ArrayBuffer) {\n return new Blob([value], {\n type: contentType ?? \"application/octet-stream\",\n });\n }\n\n if (ArrayBuffer.isView(value)) {\n // Pass a ranged view (honors byteOffset/byteLength so a Node Buffer backed\n // by a shared pool isn't over-read); Blob snapshots the bytes at\n // construction, so no defensive copy is needed.\n const view = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n return new Blob([view], {\n type: contentType ?? \"application/octet-stream\",\n });\n }\n\n if (typeof value === \"string\") {\n return contentType ? new Blob([value], { type: contentType }) : value;\n }\n\n return JSON.stringify(value);\n }\n\n private serializeBatchPutValue(item: KVBatchPutItem): Blob {\n const contentType = item.contentType;\n\n if (item.value instanceof Blob) {\n if (!contentType || item.value.type === contentType) {\n return item.value;\n }\n return new Blob([item.value], { type: contentType });\n }\n\n if (item.value instanceof ArrayBuffer) {\n return new Blob([item.value], {\n type: contentType ?? \"application/octet-stream\",\n });\n }\n\n if (ArrayBuffer.isView(item.value)) {\n const value = item.value;\n const bytes = new Uint8Array(value.byteLength);\n bytes.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));\n return new Blob([bytes], {\n type: contentType ?? \"application/octet-stream\",\n });\n }\n\n if (typeof item.value === \"string\") {\n return new Blob([item.value], {\n type: contentType ?? \"text/plain;charset=UTF-8\",\n });\n }\n\n const json = JSON.stringify(item.value);\n if (json === undefined) {\n throw new Error(`Cannot JSON serialize KV batch value for key \"${item.key}\"`);\n }\n\n return new Blob([json], {\n type: contentType ?? \"application/json\",\n });\n }\n\n private normalizeBatchPutResponse(data: unknown): KVBatchPutResponse | undefined {\n if (!data || typeof data !== \"object\") {\n return undefined;\n }\n\n const response = data as Partial<KVBatchPutResponse>;\n if (\n !Array.isArray(response.written) ||\n !response.written.every((key) => typeof key === \"string\") ||\n typeof response.count !== \"number\"\n ) {\n return undefined;\n }\n\n return {\n written: response.written,\n count: response.count,\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 private createBatchResponseHeaders(\n values: Record<string, string>\n ): KVResponseHeaders {\n const normalized = new Map(\n Object.entries(values).map(([name, value]) => [name.toLowerCase(), value])\n );\n return this.createResponseHeaders({\n get: (name) => normalized.get(name.toLowerCase()) ?? null,\n });\n }\n\n private parseBatchValue<T>(\n dataBase64: string,\n headers: Record<string, string>,\n raw: boolean = false,\n binary: boolean = false\n ): T {\n const encoded = globalThis.atob(dataBase64);\n const bytes = Uint8Array.from(encoded, (character) => character.charCodeAt(0));\n if (binary) return bytes as unknown as T;\n\n const text = new TextDecoder().decode(bytes);\n if (raw) return text as unknown as T;\n const contentType = Object.entries(headers).find(\n ([name]) => name.toLowerCase() === \"content-type\"\n )?.[1];\n return (contentType?.includes(\"application/json\")\n ? JSON.parse(text)\n : text) as T;\n }\n\n /**\n * Validate a batch read response and return its items in the caller's\n * request order. The node flattens invocation abilities from a `BTreeMap`,\n * so results arrive sorted by UTF-8 byte order of the path, not in request\n * order. Items that carry their path in `key` are matched by path; if no\n * item carries a key, items are assumed to be in the node's byte-sorted\n * order and the request is aligned the same way. Any mix of keyed and\n * keyless items, a missing requested path, an unrequested path, or a\n * malformed item fails closed (returns undefined).\n */\n private normalizeBatchReadResponse(\n data: unknown,\n paths: string[],\n requireData: boolean\n ): KvBatchNodeItem[] | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n const response = data as Partial<KvBatchNodeResponse>;\n const items = response.results;\n if (!Array.isArray(items) || items.length !== paths.length) {\n return undefined;\n }\n\n for (const item of items) {\n if (!item || typeof item !== \"object\" || typeof item.ok !== \"boolean\") {\n return undefined;\n }\n if (\n item.ok &&\n (!item.headers ||\n typeof item.headers !== \"object\" ||\n Object.values(item.headers).some((value) => typeof value !== \"string\") ||\n (requireData && typeof item.dataBase64 !== \"string\"))\n ) {\n return undefined;\n }\n if (\n !item.ok &&\n (!item.error ||\n typeof item.error.code !== \"string\" ||\n typeof item.error.message !== \"string\")\n ) {\n return undefined;\n }\n }\n\n if (items.every((item) => typeof item.key === \"string\")) {\n const byPath = new Map(items.map((item) => [item.key, item]));\n if (byPath.size !== items.length) return undefined;\n const aligned: KvBatchNodeItem[] = [];\n for (const path of paths) {\n const item = byPath.get(path);\n if (!item) return undefined;\n aligned.push(item);\n }\n return aligned;\n }\n\n if (items.some((item) => item.key !== undefined)) return undefined;\n\n // No keys carried: the node's order is byte-sorted by path, so align by\n // sorting the request the same way. Request paths are unique (duplicates\n // are rejected before the request is signed).\n const requestOrder = paths\n .map((_, index) => index)\n .sort((a, b) => comparePathsByBytes(paths[a]!, paths[b]!));\n const aligned = new Array<KvBatchNodeItem>(items.length);\n for (let position = 0; position < requestOrder.length; position++) {\n aligned[requestOrder[position]!] = items[position]!;\n }\n return aligned;\n }\n\n private async batchRead<T>(\n keys: string[],\n action: typeof KVAction.GET | typeof KVAction.HEAD,\n options?: KVGetOptions | KVHeadOptions\n ): Promise<Result<KVBatchReadResponse<T>>> {\n if (this.readThroughReady !== null) await this.readThroughReady;\n if (!this.requireAuth()) return err(authRequiredError(\"kv\"));\n if (keys.length === 0) return ok({ results: [], count: 0 });\n if (keys.length > MAX_KV_BATCH_READ_ITEMS) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n `KV batch reads accept at most ${MAX_KV_BATCH_READ_ITEMS} keys`,\n \"kv\"\n ));\n }\n if (keys.length === 1) {\n const key = keys[0]!;\n const result = action === KVAction.GET\n ? await this.get<T>(key, options as KVGetOptions)\n : await this.head(key, options as KVHeadOptions);\n return ok({\n results: [{ key, result: result as Result<KVResponse<T>> }],\n count: 1,\n });\n }\n if (!this.context.invokeAny) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV batch reads require SDK runtime support for multi-resource invocations\",\n \"kv\"\n ));\n }\n\n const getOptions = action === KVAction.GET ? options as KVGetOptions : undefined;\n if (\n getOptions?.maxResponseBytes !== undefined &&\n (!Number.isSafeInteger(getOptions.maxResponseBytes) ||\n getOptions.maxResponseBytes <= 0)\n ) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV maxResponseBytes must be a positive safe integer\",\n \"kv\"\n ));\n }\n\n const paths = keys.map((key) => this.getFullPath(key, options?.prefix));\n if (new Set(paths).size !== paths.length) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV batch read received duplicate keys after prefix resolution\",\n \"kv\"\n ));\n }\n\n const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const session = this.context.session!;\n const invocationHeaders = this.context.invokeAny(\n session,\n paths.map((path) => ({\n spaceId: session.spaceId,\n service: \"kv\",\n path,\n action,\n }))\n );\n const limitHeaders: Record<string, string> = {};\n if (getOptions?.maxResponseBytes !== undefined) {\n limitHeaders[\"x-tinycloud-max-response-bytes\"] = String(\n getOptions.maxResponseBytes\n );\n }\n const headers: ServiceHeaders = Array.isArray(invocationHeaders)\n ? [...invocationHeaders, ...Object.entries(limitHeaders)]\n : { ...invocationHeaders, ...limitHeaders };\n const response = await this.context.fetch(`${this.host}/invoke`, {\n method: \"POST\",\n headers,\n signal: request.signal,\n });\n\n if (!response.ok) {\n const errorText = response.status === 401 || response.status === 403\n ? await this.readAuthorizationText(response)\n : await response.text();\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to batch read ${keys.length} key(s)`,\n response,\n errorText,\n paths,\n action\n );\n }\n if (response.status === 413) {\n return err(serviceError(\n ErrorCodes.KV_RESPONSE_TOO_LARGE,\n \"A KV batch value exceeds the requested response limit\",\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n return err(serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to batch read ${keys.length} key(s): ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n\n const items = this.normalizeBatchReadResponse(\n await response.json(),\n paths,\n action === KVAction.GET\n );\n if (!items) {\n return err(serviceError(\n ErrorCodes.NETWORK_ERROR,\n \"KV batch read response did not match the requested keys\",\n \"kv\"\n ));\n }\n\n const results = items.map((item, index) => {\n if (!item.ok) {\n return {\n key: keys[index]!,\n result: err(serviceError(item.error!.code, item.error!.message, \"kv\")),\n };\n }\n const headers = item.headers!;\n const data = action === KVAction.HEAD\n ? undefined\n : this.parseBatchValue<T>(\n item.dataBase64!,\n headers,\n getOptions?.raw,\n getOptions?.binary\n );\n return {\n key: keys[index]!,\n result: ok({\n data: data as T,\n headers: this.createBatchResponseHeaders(headers),\n }),\n };\n });\n return ok({ results, count: results.length });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n } finally {\n request.dispose();\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 binary: boolean = false\n ): Promise<T | undefined> {\n if (!response.ok) {\n return undefined;\n }\n\n if (binary) {\n return new Uint8Array(await response.arrayBuffer()) as unknown as T;\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 path: 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 return this.authorizationFailure(\n `Failed to create signed read URL for key ${JSON.stringify(key)}`,\n response,\n errorText,\n [path],\n KVAction.GET\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 ${JSON.stringify(key)}: ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n /** A known 401/403 keeps its typed status even if reading its body fails. */\n private async readAuthorizationText(response: FetchResponse): Promise<string> {\n try {\n return await response.text();\n } catch {\n return \"\";\n }\n }\n\n /**\n * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP\n * status and the server text (`<context>: <status> - <text>`, the same\n * shape as every other KV failure) so callers that rethrow only the message\n * still see the status; `meta.status` carries it for typed callers.\n * Server hints must match the requested path/action (whether the node sends\n * a relative path or full resource); metadata uses only the request-derived\n * canonical resource under the authenticated session's space.\n */\n private authorizationFailure(\n context: string,\n response: FetchResponse,\n errorText: string,\n paths: readonly string[],\n expectedAction: string,\n extraMeta: Record<string, unknown> = {}\n ): Result<never> {\n const { resource, action } = parseAuthError(errorText);\n const requestedPath = paths.find((path) =>\n resource === path ||\n resource === `${this.context.session!.spaceId}/kv/${path}`\n );\n const capability = requestedPath !== undefined && action === expectedAction\n ? validatedCapabilityOf({\n service: \"kv\",\n code: ErrorCodes.AUTH_UNAUTHORIZED,\n meta: {\n status: response.status,\n resource: `${this.context.session!.spaceId}/kv/${requestedPath}`,\n requiredAction: action,\n },\n })\n : undefined;\n const detail = errorText.trim().length > 0\n ? errorText\n : response.statusText || \"authorization failed\";\n return err(authUnauthorizedError(\"kv\", `${context}: ${response.status} - ${detail}`, {\n status: response.status,\n ...extraMeta,\n ...capability,\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.readThroughReady !== null) await this.readThroughReady;\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n if (\n options?.maxResponseBytes !== undefined &&\n (!Number.isSafeInteger(options.maxResponseBytes) || options.maxResponseBytes <= 0)\n ) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV maxResponseBytes must be a positive safe integer\",\n \"kv\"\n ));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const network = () => this.getFromNetwork<T>(key, path, options, request.signal);\n if (options?.source === \"network\") {\n const observer = this.readThrough;\n if (observer === null) return await network();\n let outcome: \"found\" | \"not_found\" | \"error\" = \"error\";\n const startedAt = Date.now();\n try {\n const result = await network();\n outcome = result.ok ? \"found\" : result.error.code === ErrorCodes.KV_NOT_FOUND ? \"not_found\" : \"error\";\n return result;\n } finally {\n try {\n observer.observeNetworkRequested({\n op: \"get\",\n space: this.context.session!.spaceId,\n path,\n reason: \"NETWORK_REQUESTED\",\n outcome,\n latencyMs: Date.now() - startedAt,\n });\n } catch {\n // Observation is best-effort and must not affect the network read.\n }\n }\n\n }\n if (this.readThrough === null) return await network();\n return await this.readThrough.get({\n space: this.context.session!.spaceId,\n key,\n path,\n options,\n signal: request.signal,\n network,\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n } finally {\n request.dispose();\n }\n });\n }\n private async getFromNetwork<T>(\n key: string,\n path: string,\n options: KVGetOptions | undefined,\n signal: AbortSignal\n ): Promise<Result<KVResponse<T>>> {\n const response = await this.invokeOperation(\n path,\n KVAction.GET,\n undefined,\n signal,\n options?.maxResponseBytes === undefined\n ? undefined\n : { \"x-tinycloud-max-response-bytes\": String(options.maxResponseBytes) }\n );\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n const errorText = await this.readAuthorizationText(response);\n const permissionHint = parsePermissionHintFromErrorText(errorText);\n const structuredCapability = permissionHint?.service === \"tinycloud.kv\" &&\n permissionHint.space === this.context.session!.spaceId &&\n permissionHint.path === path\n ? validatedCapabilityOf({\n service: \"kv\",\n code: ErrorCodes.AUTH_UNAUTHORIZED,\n meta: {\n status: response.status,\n resource: `${this.context.session!.spaceId}/kv/${path}`,\n requiredAction: KVAction.GET,\n },\n })\n : undefined;\n return this.authorizationFailure(\n `Failed to get key ${JSON.stringify(key)}`,\n response,\n errorText,\n [path],\n KVAction.GET,\n structuredCapability ? { permissionHint, ...structuredCapability } : {}\n );\n }\n if (response.status === 404) return this.classifyNotFound(response, key);\n const errorText = await response.text();\n if (response.status === 413) {\n return err(serviceError(\n ErrorCodes.KV_RESPONSE_TOO_LARGE,\n `KV value at key ${JSON.stringify(key)} exceeds the requested response limit`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n return err(serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to get key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n const data = await this.parseResponse<T>(response, options?.raw, options?.binary);\n return ok({ data: data as T, headers: this.createResponseHeaders(response.headers) });\n }\n\n async batchGet<T = unknown>(\n keys: string[],\n options?: KVGetOptions\n ): Promise<Result<KVBatchReadResponse<T>>> {\n return this.withTelemetry(\"batchGet\", String(keys.length), () =>\n this.batchRead<T>(keys, KVAction.GET, options)\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.readThroughReady !== null) await this.readThroughReady;\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n if (options?.ifMatch !== undefined && options.ifNoneMatch !== undefined) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV put cannot combine ifMatch and ifNoneMatch\",\n \"kv\"\n ));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n // Serialize the value. Binary values (Blob/ArrayBuffer/typed-array/Buffer)\n // are sent as raw bytes so they round-trip byte-identically; strings are\n // sent as-is; everything else is JSON. Mirrors serializeBatchPutValue.\n const body = this.serializePutValue(value, options?.contentType);\n\n const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const network = () => this.putToNetwork(key, path, body, options, request.signal);\n if (this.readThrough === null) return await network();\n return await this.readThrough.write({\n op: \"put\",\n space: this.context.session!.spaceId,\n entries: [{ path, body }],\n signal: request.signal,\n conditional: options?.ifMatch !== undefined || options?.ifNoneMatch !== undefined,\n network,\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n } finally {\n request.dispose();\n }\n });\n }\n private async putToNetwork(\n key: string,\n path: string,\n body: Blob | string,\n options: KVPutOptions | undefined,\n signal: AbortSignal\n ): Promise<Result<KVResponse<void>>> {\n const response = await this.invokeOperation(\n path,\n KVAction.PUT,\n body,\n signal,\n {\n ...(options?.ifMatch === undefined ? {} : { \"if-match\": options.ifMatch }),\n ...(options?.ifNoneMatch === undefined ? {} : { \"if-none-match\": options.ifNoneMatch }),\n }\n );\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to put key ${JSON.stringify(key)}`,\n response,\n await this.readAuthorizationText(response),\n [path],\n KVAction.PUT\n );\n }\n if (!response.ok) {\n const errorText = await response.text();\n if (response.status === 412) {\n return err(serviceError(\n ErrorCodes.KV_PRECONDITION_FAILED,\n `KV precondition failed for key ${JSON.stringify(key)}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n if (response.status === 503 && (options?.ifMatch !== undefined || options?.ifNoneMatch !== undefined)) {\n return err(serviceError(\n ErrorCodes.KV_CONFLICT,\n `Concurrent KV update conflicted for key ${JSON.stringify(key)}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n const quotaError = this.handleQuotaErrorResponse(response, errorText, key);\n if (quotaError) return quotaError;\n return err(serviceError(\n ErrorCodes.KV_WRITE_FAILED,\n `Failed to put key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n return ok({\n data: undefined as void,\n headers: this.createResponseHeaders(response.headers),\n });\n }\n\n /**\n * Store multiple values in one TinyCloud KV invocation.\n */\n async batchPut(\n items: KVBatchPutItem[],\n options?: KVBatchPutOptions\n ): Promise<Result<KVBatchPutResponse>> {\n return this.withTelemetry(\"batchPut\", String(items.length), async () => {\n if (this.readThroughReady !== null) await this.readThroughReady;\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n if (items.length === 0) {\n return ok({ written: [], count: 0 });\n }\n\n if (!this.context.invokeAny) {\n return err(\n serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV batchPut requires SDK runtime support for multi-resource invocations\",\n \"kv\"\n )\n );\n }\n\n const session = this.context.session!;\n const paths = items.map((item) => this.getFullPath(item.key, options?.prefix));\n const seen = new Set<string>();\n for (const path of paths) {\n if (seen.has(path)) {\n return err(\n serviceError(\n ErrorCodes.INVALID_INPUT,\n `KV batchPut received duplicate key after prefix resolution: ${path}`,\n \"kv\"\n )\n );\n }\n seen.add(path);\n }\n\n const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const network = () => this.batchPutToNetwork(items, paths, request.signal);\n if (this.readThrough === null) return await network();\n const entries = items.map((item, index) => ({\n path: paths[index]!,\n body: this.serializeBatchPutValue(item),\n }));\n return await this.readThrough.write({\n op: \"batchPut\",\n space: session.spaceId,\n entries,\n signal: request.signal,\n network: () => this.batchPutToNetwork(items, paths, request.signal, entries),\n });\n } catch (error) {\n const wrapped = wrapError(\"kv\", error);\n return err({ ...wrapped, meta: { ...wrapped.meta, requestMayHaveDispatched: false } });\n } finally {\n request.dispose();\n }\n });\n }\n private async batchPutToNetwork(\n items: KVBatchPutItem[],\n paths: string[],\n signal: AbortSignal,\n entries?: Array<{ path: string; body: Blob }>\n ): Promise<Result<KVBatchPutResponse>> {\n const session = this.context.session!;\n // Conservative: `true` means we handed a fully-constructed request to\n // fetch(). It does NOT prove bytes reached the node — DNS/connect\n // failures and synchronous fetch rejections are included. Proving\n // actual dispatch requires lower-level transport instrumentation (out\n // of scope). The over-approximation is intentional and bounded: it can\n // only cause a reconciliation of at most N idempotent, byte-identical\n // overwrites — never a corrupted or differently-timestamped record.\n let requestMayHaveDispatched = false;\n try {\n const body = new FormData();\n for (let index = 0; index < items.length; index++) {\n body.append(\n encodeKvBatchPartName(paths[index]!),\n entries?.[index]?.body ?? this.serializeBatchPutValue(items[index]!)\n );\n }\n\n const headers = this.context.invokeAny!(\n session,\n paths.map((path) => ({\n spaceId: session.spaceId,\n service: \"kv\",\n path,\n action: KVAction.PUT,\n }))\n );\n\n const url = `${this.host}/invoke`;\n const init = {\n method: \"POST\",\n headers,\n body,\n signal,\n };\n\n // Resolve the fetch function to a local BEFORE flipping the flag.\n // `this.context.fetch` is a getter (context.ts:154-156) that can\n // itself throw (assertActive()) — if the flag were set first, that\n // throw would be mislabeled as \"may have dispatched\" even though no\n // request was ever handed to a transport (Sol B2).\n const fetchFn = this.context.fetch;\n requestMayHaveDispatched = true;\n const response = await fetchFn(url, init);\n\n if (!response.ok) {\n let errorText: string;\n try {\n errorText = await response.text();\n } catch (textError) {\n // A response was received — the status is known and\n // authoritative — but its body could not be read. Falling\n // through to the generic catch below would report\n // NETWORK_ERROR + requestMayHaveDispatched: true, which the\n // allow-list treats as ambiguous regardless of status. That\n // would turn a deterministic 4xx (nothing written) into a false\n // ambiguity (Sol B4). Classify by status alone instead; whether\n // this is later reconciled is still governed solely by\n // AMBIGUOUS_WRITE_STATUSES, exactly like the body-readable path.\n //\n // Sol B2 (round 2): preserve the underlying rejection instead of\n // discarding it — a bare `catch {}` here would make a\n // body-read failure silently invisible, which is exactly the\n // debugging trap the no-swallowed-errors rule exists to\n // prevent.\n const cause = textError instanceof Error ? textError : new Error(String(textError));\n return err(\n serviceError(\n ErrorCodes.KV_WRITE_FAILED,\n `Failed to batch put ${items.length} key(s): ${response.status} - <response body could not be read: ${cause.message}>`,\n \"kv\",\n { cause, meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to batch put ${items.length} key(s)`,\n response,\n errorText,\n paths,\n KVAction.PUT\n );\n }\n\n const quotaError = this.handleQuotaErrorResponse(\n response,\n errorText,\n \"batch\"\n );\n if (quotaError) {\n return quotaError;\n }\n\n return err(\n serviceError(\n ErrorCodes.KV_WRITE_FAILED,\n `Failed to batch put ${items.length} key(s): ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n let rawBody: unknown;\n try {\n rawBody = await response.json();\n } catch (jsonError) {\n // A 2xx response whose body isn't valid JSON is precisely the\n // unconfirmed-2xx case — the node answered success but the write\n // set can't be confirmed. Falling through to the generic catch\n // below would carry only `requestMayHaveDispatched`, omitting\n // `responseReceived`/`status`/`outcome`, so this must be classified\n // here with the identical metadata block used by the two sibling\n // unconfirmed branches (Sol B3).\n //\n // Sol B2 (round 2): preserve the underlying rejection instead of\n // discarding it — a bare `catch {}` here would make a\n // JSON-parse failure silently invisible, which is exactly the\n // debugging trap the no-swallowed-errors rule exists to\n // prevent.\n const cause = jsonError instanceof Error ? jsonError : new Error(String(jsonError));\n // The configured timeout can also elapse while the 2xx body is\n // still streaming: report TIMEOUT, with the same unconfirmed meta.\n const timedOut = isRequestTimeoutError(cause);\n return err(\n serviceError(\n timedOut ? ErrorCodes.TIMEOUT : ErrorCodes.NETWORK_ERROR,\n timedOut\n ? `KV batchPut response body did not arrive in time: ${cause.message}`\n : `KV batchPut response was not valid JSON: ${cause.message}`,\n \"kv\",\n {\n cause,\n meta: {\n ...(timedOut ? { timeoutMs: cause.timeoutMs } : {}),\n requestMayHaveDispatched: true,\n responseReceived: true,\n status: response.status,\n outcome: \"batch-unconfirmed\",\n },\n }\n )\n );\n }\n\n const batchResponse = this.normalizeBatchPutResponse(rawBody);\n if (!batchResponse) {\n // The code stays NETWORK_ERROR deliberately: adding a member to\n // ErrorCodes (types.ts:73-115) widens the exported ErrorCode union\n // (:117) and would break consumers with exhaustive switches. The\n // node answered 2xx (the write very likely landed) but the body\n // could not confirm it, so the ambiguity is carried in metadata\n // instead of a dedicated code.\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n \"KV batchPut response did not include matching written keys and count\",\n \"kv\",\n {\n meta: {\n requestMayHaveDispatched: true,\n responseReceived: true,\n status: response.status,\n outcome: \"batch-unconfirmed\",\n },\n }\n )\n );\n }\n\n // Validate against the exact requested path set, not just internal\n // self-consistency (count === written.length would accept a\n // malformed response that reports the right count for the wrong\n // keys, e.g. a partial write padded to look complete).\n const requestedPaths = new Set(paths);\n const writtenPaths = new Set(batchResponse.written);\n const matchesRequest =\n batchResponse.count === paths.length &&\n batchResponse.written.length === paths.length &&\n writtenPaths.size === paths.length &&\n batchResponse.written.every((key) => requestedPaths.has(key));\n\n if (!matchesRequest) {\n // Same rationale as above: NETWORK_ERROR is kept and the\n // ambiguity is carried in meta rather than a new ErrorCodes member.\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `KV batchPut response did not confirm all ${paths.length} requested key(s) were written`,\n \"kv\",\n {\n meta: {\n requestMayHaveDispatched: true,\n responseReceived: true,\n status: response.status,\n outcome: \"batch-unconfirmed\",\n },\n }\n )\n );\n }\n\n return ok(batchResponse);\n } catch (error) {\n const wrapped = wrapError(\"kv\", error);\n return err({\n ...wrapped,\n meta: { ...wrapped.meta, requestMayHaveDispatched },\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.readThroughReady !== null) await this.readThroughReady;\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n if (\n options?.limit !== undefined &&\n (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 1000)\n ) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV list limit must be an integer from 1 through 1000\",\n \"kv\"\n ));\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 const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const network = () => this.listFromNetwork(listPath, options, request.signal);\n let page: Result<KVListPage>;\n if (options?.source === \"network\") {\n const observer = this.readThrough;\n if (observer === null) {\n page = await network();\n } else {\n let outcome: \"found\" | \"not_found\" | \"error\" = \"error\";\n const startedAt = Date.now();\n try {\n page = await network();\n outcome = page.ok ? \"found\" : \"error\";\n } finally {\n try {\n observer.observeNetworkRequested({\n op: \"list\",\n space: this.context.session!.spaceId,\n path: listPath,\n reason: \"NETWORK_REQUESTED\",\n outcome,\n latencyMs: Date.now() - startedAt,\n });\n } catch {\n // Observation is best-effort and must not affect the network read.\n }\n }\n }\n } else if (this.readThrough === null) {\n page = await network();\n } else {\n page = await this.readThrough.list({\n space: this.context.session!.spaceId,\n listPath,\n options,\n signal: request.signal,\n network,\n });\n }\n return page.ok\n ? this.finishList(page.data, listPath, options?.removePrefix)\n : page;\n } catch (error) {\n return err(wrapError(\"kv\", error));\n } finally {\n request.dispose();\n }\n });\n }\n private async listFromNetwork(\n listPath: string,\n options: KVListOptions | undefined,\n signal: AbortSignal\n ): Promise<Result<KVListPage>> {\n const response = await this.invokeOperation(\n listPath,\n KVAction.LIST,\n undefined,\n signal,\n options?.limit === undefined && options?.cursor === undefined\n ? undefined\n : {\n ...(options?.limit === undefined ? {} : { \"x-tinycloud-limit\": String(options.limit) }),\n ...(options?.cursor === undefined ? {} : { \"x-tinycloud-cursor\": options.cursor }),\n }\n );\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n \"Failed to list keys\",\n response,\n await this.readAuthorizationText(response),\n [listPath],\n KVAction.LIST\n );\n }\n const errorText = await response.text();\n return err(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 const keys = (await this.parseResponse<string[]>(response, options?.raw)) ?? [];\n return ok({\n keys,\n ...(response.headers.get(\"x-tinycloud-truncated\") === null\n ? {}\n : { truncated: response.headers.get(\"x-tinycloud-truncated\") === \"true\" }),\n ...(response.headers.get(\"x-tinycloud-next-cursor\") === null\n ? {}\n : { nextCursor: response.headers.get(\"x-tinycloud-next-cursor\") ?? undefined }),\n });\n }\n\n private finishList(\n page: KVListPage,\n listPath: string,\n removePrefix: boolean | undefined\n ): Result<KVListResponse> {\n let keys = page.keys;\n if (removePrefix && listPath) {\n const prefixWithSlash = listPath.endsWith(\"/\") ? listPath : `${listPath}/`;\n keys = keys.map((key) =>\n key.startsWith(prefixWithSlash) ? key.slice(prefixWithSlash.length) : key\n );\n }\n return ok({ keys, ...(page.truncated === undefined ? {} : { truncated: page.truncated }),\n ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }) });\n }\n\n /**\n * Read one page of the `tinycloud.kv/sync` change feed for a prefix.\n */\n async changes(options: KVChangesOptions): Promise<Result<KVChangesResponse>> {\n return this.withTelemetry(\"changes\", options?.prefix, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n // Plain-JS callers can omit the options; TypeScript requires them.\n if (typeof options?.prefix !== \"string\" || options.prefix.length === 0) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV changes prefix must be a non-empty string\",\n \"kv\"\n ));\n }\n if (\n options.limit !== undefined &&\n (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 1000)\n ) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV changes limit must be an integer from 1 through 1000\",\n \"kv\"\n ));\n }\n\n const requestPrefix = options.prefix === \"/\" ? \"\" : options.prefix;\n const headers: Record<string, string> = {};\n if (options.limit !== undefined) headers[\"x-tinycloud-limit\"] = String(options.limit);\n if (options.cursor !== undefined) headers[\"x-tinycloud-cursor\"] = options.cursor;\n if (options.retentionGrant !== undefined) {\n headers[\"x-tinycloud-retention-grant\"] = options.retentionGrant;\n }\n\n const request = this.createRequestSignal(options.signal, options.timeout);\n try {\n const response = await this.invokeOperation(\n requestPrefix,\n KVAction.SYNC,\n undefined,\n request.signal,\n headers\n );\n\n if (!response.ok) {\n // Awaited so the timeout and abort listeners stay live while the\n // error body is read; `finally` disposes them only afterwards.\n return await this.changesFailure(response, options.prefix, request.signal);\n }\n\n const text = await response.text();\n let body: unknown;\n try {\n body = JSON.parse(text);\n } catch {\n body = undefined;\n }\n const page = this.normalizeChangesResponse(body, options.prefix);\n if (!page) {\n return err(serviceError(\n ErrorCodes.NETWORK_ERROR,\n \"KV changes response was not a tinycloud.kv/sync page\",\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n return ok(page);\n } catch (error) {\n return err(wrapError(\"kv\", error));\n } finally {\n request.dispose();\n }\n });\n }\n\n /**\n * Map a failed `kv/sync` response. 410 and the retention 403 carry\n * `{\"error\":{\"code\",\"reason\"}}`; a revoked grant is a 401 whose whole body\n * is the node's revocation error (see REVOKED_BODY). A body read that\n * fails because the request was cancelled or timed out rethrows, so the\n * caller sees `ABORTED`/`TIMEOUT` rather than a mapped status.\n */\n private async changesFailure(\n response: FetchResponse,\n prefix: string,\n signal: AbortSignal\n ): Promise<Result<never>> {\n if (response.status === 404) {\n return this.classifyNotFound(response, prefix);\n }\n let errorText: string;\n try {\n errorText = await response.text();\n } catch (error) {\n if (signal.aborted) throw error;\n errorText = \"\";\n }\n const meta = { status: response.status, statusText: response.statusText };\n let reason: string | undefined;\n let bodyCode: string | undefined;\n try {\n const body: unknown = JSON.parse(errorText);\n const error = isJsonObject(body) ? body.error : undefined;\n if (isJsonObject(error)) {\n if (typeof error.code === \"string\") bodyCode = error.code;\n if (typeof error.reason === \"string\") reason = error.reason;\n }\n } catch {\n // Plain-text body.\n }\n const context = `Failed to read KV changes for ${JSON.stringify(prefix)}`;\n\n if (response.status === 410) {\n return err(serviceError(\n ErrorCodes.KV_SYNC_RESET_REQUIRED,\n `${context}: the node requires a reset (${reason ?? \"reason not given\"}); discard sync state and restart without a cursor`,\n \"kv\",\n { meta: { ...meta, reason } }\n ));\n }\n if (response.status === 403 && bodyCode === \"RETENTION_GRANT_REFUSED\") {\n return err(serviceError(\n ErrorCodes.KV_RETENTION_GRANT_REFUSED,\n `${context}: the retention grant was refused (${reason ?? \"reason not given\"})`,\n \"kv\",\n { meta: { ...meta, reason } }\n ));\n }\n if (response.status === 401) {\n const revoked = revocationCodeOf(errorText);\n if (revoked !== undefined) {\n return err(serviceError(revoked, `${context}: 401 - ${errorText}`, \"kv\", { meta }));\n }\n }\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(context, response, errorText, [prefix], KVAction.SYNC);\n }\n return err(serviceError(\n response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR,\n `${context}: ${response.status} - ${errorText}`,\n \"kv\",\n { meta }\n ));\n }\n\n private normalizeChangesResponse(data: unknown, requestedPrefix: string): KVChangesResponse | undefined {\n if (!isJsonObject(data)) return undefined;\n const { changes, more, cursor, source, authority } = data;\n if (\n !Array.isArray(changes) ||\n typeof more !== \"boolean\" ||\n typeof cursor !== \"string\" ||\n !isJsonObject(source) ||\n typeof source.nodeDid !== \"string\" ||\n typeof source.space !== \"string\" ||\n typeof source.prefix !== \"string\" ||\n !isJsonObject(authority)\n ) {\n return undefined;\n }\n const { notBefore, expiresAt, retainUntil } = authority;\n if (\n (notBefore !== null && typeof notBefore !== \"string\") ||\n (expiresAt !== null && typeof expiresAt !== \"string\") ||\n (retainUntil !== null && typeof retainUntil !== \"string\")\n ) {\n return undefined;\n }\n\n const parsed: KVChange[] = [];\n for (const change of changes) {\n if (!isJsonObject(change) || typeof change.key !== \"string\") return undefined;\n if (change.deleted === true) {\n parsed.push({ key: change.key, deleted: true });\n continue;\n }\n const { metadata } = change;\n if (\n change.deleted !== false ||\n typeof change.etag !== \"string\" ||\n !isJsonObject(metadata) ||\n !Object.values(metadata).every((value) => typeof value === \"string\")\n ) {\n return undefined;\n }\n parsed.push({\n key: change.key,\n deleted: false,\n etag: change.etag,\n metadata: metadata as Record<string, string>,\n });\n }\n\n return {\n changes: parsed,\n more,\n cursor,\n source: {\n nodeDid: source.nodeDid,\n space: source.space,\n prefix: requestedPrefix === \"/\" && source.prefix === \"\" ? \"/\" : source.prefix,\n },\n authority: { notBefore, expiresAt, retainUntil },\n };\n }\n\n /**\n * Delete a key.\n */\n async delete(\n key: string,\n options?: KVDeleteOptions\n ): Promise<Result<KVResponse<void>>> {\n return this.withTelemetry(\"delete\", key, async () => {\n if (this.readThroughReady !== null) await this.readThroughReady;\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const network = () => this.deleteFromNetwork(key, path, options, request.signal);\n if (this.readThrough === null) return await network();\n return await this.readThrough.write({\n op: \"delete\",\n space: this.context.session!.spaceId,\n entries: [{ path }],\n signal: request.signal,\n conditional: options?.ifMatch !== undefined,\n network,\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n } finally {\n request.dispose();\n }\n });\n }\n private async deleteFromNetwork(\n key: string,\n path: string,\n options: KVDeleteOptions | undefined,\n signal: AbortSignal\n ): Promise<Result<KVResponse<void>>> {\n const response = await this.invokeOperation(\n path,\n KVAction.DELETE,\n undefined,\n signal,\n options?.ifMatch === undefined ? undefined : { \"if-match\": options.ifMatch }\n );\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to delete key ${JSON.stringify(key)}`,\n response,\n await this.readAuthorizationText(response),\n [path],\n KVAction.DELETE\n );\n }\n if (response.status === 404) return this.classifyNotFound(response, key);\n const errorText = await response.text();\n if (response.status === 412) {\n return err(serviceError(\n ErrorCodes.KV_PRECONDITION_FAILED,\n `KV precondition failed for key ${JSON.stringify(key)}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n if (response.status === 503 && options?.ifMatch !== undefined) {\n return err(serviceError(\n ErrorCodes.KV_CONFLICT,\n `Concurrent KV delete conflicted for key ${JSON.stringify(key)}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n return err(serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to delete key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n return ok({\n data: undefined as void,\n headers: this.createResponseHeaders(response.headers),\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 const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.HEAD,\n undefined,\n request.signal\n );\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to get metadata for key ${JSON.stringify(key)}`,\n response,\n await this.readAuthorizationText(response),\n [path],\n KVAction.HEAD\n );\n }\n\n if (response.status === 404) {\n return await this.classifyNotFound(response, key);\n }\n\n const errorText = await response.text();\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to get metadata for key ${JSON.stringify(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 } finally {\n request.dispose();\n }\n });\n }\n\n async batchHead(\n keys: string[],\n options?: KVHeadOptions\n ): Promise<Result<KVBatchReadResponse<void>>> {\n return this.withTelemetry(\"batchHead\", String(keys.length), () =>\n this.batchRead<void>(keys, KVAction.HEAD, options)\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 const request = this.createRequestSignal(options?.signal, options?.timeout);\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: request.signal,\n });\n\n if (!response.ok) {\n return await this.createSignedReadUrlError(response, key, path);\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 } finally {\n request.dispose();\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","export const CLEAR_PENDING_WARNING =\n \"Clearing pending writes surrenders read-your-writes protection for the cleared keys. If a write whose record was cleared had an unknown outcome or never finished, it may commit now or later; this device can serve an older value until the next sync after that commit, as it would for a write from another device.\";\n","import type { ReplicationIdentity } from \"./types\";\n\nconst SPACE_ID = /^(tinycloud:pkh:eip155:\\d+:)([^:]+)(:.+)$/;\nconst PRINCIPAL_DID = /^(did:pkh:eip155:\\d+:)([^:]+)$/;\n\n/**\n * Pure canonicalization: lower-cases only EIP-155 address segments and preserves all other\n * space/principal bytes. Rejects non-HTTP(S) hosts, credentials, query/fragment, and non-full\n * TinyCloud space ids or non-pkh principals.\n */\nexport function canonicalReplicationIdentity(\n raw: ReplicationIdentity,\n): ReplicationIdentity {\n if (\n !raw ||\n typeof raw.host !== \"string\" ||\n typeof raw.space !== \"string\" ||\n typeof raw.principal !== \"string\"\n ) {\n throw new TypeError(\n \"Replication identity host, space, and principal must be strings\",\n );\n }\n const authority = /^https?:\\/\\/([^/?#]*)/i.exec(raw.host)?.[1];\n let url: URL;\n try {\n url = new URL(raw.host);\n } catch {\n throw new TypeError(\n \"Replication identity host must be an absolute HTTP(S) URL\",\n );\n }\n if (\n !authority ||\n authority.includes(\"@\") ||\n raw.host.includes(\"?\") ||\n raw.host.includes(\"#\") ||\n url.username ||\n url.password\n ) {\n throw new TypeError(\n \"Replication identity host must be HTTP(S) without credentials, query, or fragment\",\n );\n }\n const space = SPACE_ID.exec(raw.space);\n const principal = PRINCIPAL_DID.exec(raw.principal);\n if (!space || !principal) {\n throw new TypeError(\n \"Replication identity requires a full TinyCloud space id and a pkh principal DID\",\n );\n }\n return {\n host: `${url.origin}${url.pathname.replace(/\\/+$/, \"\")}`,\n space: `${space[1]}${space[2]!.toLowerCase()}${space[3]}`,\n principal: `${principal[1]}${principal[2]!.toLowerCase()}`,\n };\n}\n\n/** Stable in-memory key; durable adapters hash its UTF-8 bytes with their platform crypto. */\nexport function replicationIdentityKey(id: ReplicationIdentity): string {\n const canonical = canonicalReplicationIdentity(id);\n return `tc-replication/v1\\n${canonical.host}\\n${canonical.space}\\n${canonical.principal}`;\n}\n","/** Shared prefix boundary and configuration rules; no runtime dependencies. */\nexport function kvPrefixCovers(prefix: string, key: string): boolean {\n return prefix === \"/\" || key.startsWith(prefix);\n}\n\n/** Replication scopes are `/` or non-empty, slash-terminated folder paths. */\nexport function assertFolderPrefix(prefix: unknown): asserts prefix is string {\n const validFolder = typeof prefix === \"string\" &&\n prefix.length > 0 &&\n !prefix.startsWith(\"/\") &&\n !prefix.includes(\"//\") &&\n prefix.endsWith(\"/\");\n if (prefix !== \"/\" && !validFolder) {\n const folderHint = typeof prefix === \"string\"\n ? prefix.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\")\n : \"\";\n const suggestion = folderHint.length > 0 && !folderHint.includes(\"//\")\n ? ` Use ${JSON.stringify(`${folderHint}/`)}.`\n : ' Use \"/\" or a slash-terminated folder path such as \"notes/\".';\n throw new TypeError(`Replication prefix must be \"/\" or a non-empty slash-terminated folder path without empty segments.${suggestion}`);\n }\n}\n","export { kvPrefixCovers } from \"./prefix\";\n\n/** LIST returns its exact path plus descendants below its slash-terminated path. */\nexport function listRangeCovers(path: string, key: string): boolean {\n return path === \"\" || key === path || key.startsWith(path.endsWith(\"/\") ? path : `${path}/`);\n}\n\n/** Whether replicating this space/prefix can copy encrypted secret material to disk. */\nexport function requiresSecretsOptIn(space: string, prefix: string): boolean {\n if (prefix === \"/\") return true;\n if (space === \"secrets\" || space.endsWith(\":secrets\")) return true;\n return prefix.split(\"/\", 1)[0] === \"vault\";\n}\n"],"mappings":"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","names":["ok","serviceError","aligned","headers","errorText"]}
1
+ {"version":3,"sources":["../../src/types.ts","../../src/responseErrors.ts","../../src/errors.ts","../../src/encryption/networkId.ts","../../src/base/BaseService.ts","../../src/kv/PrefixedKVService.ts","../../src/kv/types.ts","../../src/kv/KVService.ts","../../src/kv/replication/constants.ts","../../src/kv/replication/identity.ts","../../src/kv/replication/prefix.ts","../../src/kv/replication/scope.ts"],"sourcesContent":["/**\n * SDK Services - Core Types\n *\n * These types define the service architecture for TinyCloud SDK.\n * Services use dependency injection via IServiceContext for platform independence.\n */\n\n// =============================================================================\n// Result Type Pattern\n// =============================================================================\n\n/**\n * Result type for service operations.\n * Services return Result instead of throwing, making error handling explicit.\n *\n * @template T - The success data type\n * @template E - The error type (defaults to ServiceError)\n *\n * @example\n * ```typescript\n * const result = await kv.get('key');\n * if (result.ok) {\n * console.log(result.data);\n * } else {\n * console.error(result.error.code);\n * }\n * ```\n */\nexport type Result<T, E = ServiceError> =\n | { ok: true; data: T }\n | { ok: false; error: E };\n\n/**\n * Service error with structured information.\n */\nexport interface ServiceError {\n /** Error code for programmatic handling (e.g., 'KV_NOT_FOUND', 'AUTH_EXPIRED') */\n code: string;\n /** Human-readable error message */\n message: string;\n /** Service that produced the error (e.g., 'kv', 'sql') */\n service: string;\n /** Original error if this wraps another error */\n cause?: Error;\n /** Additional metadata about the error */\n meta?: Record<string, unknown>;\n}\n\n/**\n * A node-supplied permission hint after strict SDK validation. This is\n * deliberately narrower than a general capability: no wildcards, caveats,\n * descriptions, or transport metadata can cross the service boundary.\n */\nexport interface PermissionHint {\n readonly service: \"tinycloud.kv\" | \"tinycloud.encryption\";\n readonly space?: string;\n readonly path: string;\n readonly actions: readonly string[];\n}\n\n/**\n * Storage quota information returned with quota-related errors.\n * @deprecated Read `usedBytes`, `limitBytes` and `account` from the error's\n * `meta`, or use `StorageFullEvent` from `tc.on(\"storage.full\", handler)`.\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 /** HTTP 401: the delegation the invocation cites was revoked. */\n AUTH_DELEGATION_REVOKED: \"AUTH_DELEGATION_REVOKED\",\n /** HTTP 401: a delegation the cited one rests on was revoked. */\n AUTH_DELEGATION_ANCESTOR_REVOKED: \"AUTH_DELEGATION_ANCESTOR_REVOKED\",\n NETWORK_ERROR: \"NETWORK_ERROR\",\n TIMEOUT: \"TIMEOUT\",\n ABORTED: \"ABORTED\",\n INVALID_INPUT: \"INVALID_INPUT\",\n PERMISSION_DENIED: \"PERMISSION_DENIED\",\n\n // Node retry signals (tinycloud-node #347). `meta.retryable` says whether a\n // retry is safe; `meta.retryAfterMs` carries the node's Retry-After.\n /** HTTP 503 `sql_busy:<stage>` / `unavailable_retryable`: nothing was applied; retry after `meta.retryAfterMs`. */\n NODE_BUSY: \"NODE_BUSY\",\n /** HTTP 503 `request_timeout`: the node's deadline passed before any write began; nothing was applied. */\n REQUEST_TIMEOUT: \"REQUEST_TIMEOUT\",\n /** HTTP 500 `outcome_unknown`: a write may have been applied; read back before retrying a non-idempotent operation. */\n OUTCOME_UNKNOWN: \"OUTCOME_UNKNOWN\",\n\n // KV-specific errors\n KV_NOT_FOUND: \"KV_NOT_FOUND\",\n KV_WRITE_FAILED: \"KV_WRITE_FAILED\",\n KV_PRECONDITION_FAILED: \"KV_PRECONDITION_FAILED\",\n KV_CONFLICT: \"KV_CONFLICT\",\n KV_RESPONSE_TOO_LARGE: \"KV_RESPONSE_TOO_LARGE\",\n /** HTTP 410 from the change feed: discard sync state and restart without a cursor; `meta.reason` says why. */\n KV_SYNC_RESET_REQUIRED: \"KV_SYNC_RESET_REQUIRED\",\n /** HTTP 403 from the change feed: the retention grant was refused; `meta.reason` says why. */\n KV_RETENTION_GRANT_REFUSED: \"KV_RETENTION_GRANT_REFUSED\",\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 /** The node answered the usage read without usage numbers (it predates the read). */\n STORAGE_STATUS_UNAVAILABLE: \"STORAGE_STATUS_UNAVAILABLE\",\n\n // DuckDB-specific errors\n DUCKDB_ERROR: \"DUCKDB_ERROR\",\n DUCKDB_PERMISSION_DENIED: \"DUCKDB_PERMISSION_DENIED\",\n DUCKDB_DATABASE_NOT_FOUND: \"DUCKDB_DATABASE_NOT_FOUND\",\n DUCKDB_RESPONSE_TOO_LARGE: \"DUCKDB_RESPONSE_TOO_LARGE\",\n DUCKDB_QUOTA_EXCEEDED: \"DUCKDB_QUOTA_EXCEEDED\",\n DUCKDB_INVALID_STATEMENT: \"DUCKDB_INVALID_STATEMENT\",\n DUCKDB_SCHEMA_ERROR: \"DUCKDB_SCHEMA_ERROR\",\n DUCKDB_READONLY_VIOLATION: \"DUCKDB_READONLY_VIOLATION\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\n// =============================================================================\n// Service Session\n// =============================================================================\n\n/**\n * Session data required for authenticated service operations.\n * Both TinyCloudSession and web-sdk Session can be cast to this interface.\n */\nexport interface ServiceSession {\n /** The delegation header containing the UCAN */\n delegationHeader: { Authorization: string };\n /** The delegation CID */\n delegationCid: string;\n /** The space ID for this session */\n spaceId: string;\n /** The verification method DID */\n verificationMethod: string;\n /** The session key JWK (required for invoke) */\n jwk: object;\n}\n\n// =============================================================================\n// Platform Dependencies (Injected)\n// =============================================================================\n\n/**\n * Headers type - compatible with both browser and Node.js.\n */\nexport type ServiceHeaders = Record<string, string> | [string, string][];\n\n/**\n * A single fact object to include in the UCAN invocation.\n * Facts are key-value objects that the server reads from the UCAN facts field.\n */\nexport interface InvocationFact {\n [key: string]: unknown;\n}\n\n/**\n * Facts to include in the UCAN invocation.\n * This is an array of fact objects per the UCAN spec.\n * Used to pass additional parameters that the server reads from the UCAN facts field.\n */\nexport type InvocationFacts = InvocationFact[];\n\n/**\n * Invoke function signature - platform-specific implementation injected via DI.\n * Both node-sdk-wasm and web-sdk-wasm export this with identical signature.\n *\n * @param session - The service session with delegation data\n * @param service - Service name (e.g., \"kv\")\n * @param path - Resource path or key\n * @param action - Action to perform (e.g., \"tinycloud.kv/get\")\n * @param facts - Optional facts to include in the UCAN (e.g., for capabilities/read params)\n * @returns Headers to include in the request\n */\nexport type InvokeFunction = (\n session: ServiceSession,\n service: string,\n path: string,\n action: string,\n facts?: InvocationFacts,\n) => ServiceHeaders;\n\n/**\n * Multi-resource invocation entry.\n */\nexport interface InvokeAnyEntry {\n /**\n * Legacy space-scoped resource. Optional when `resource` is provided.\n */\n spaceId?: string;\n service: string;\n path: string;\n action: string;\n /** Optional raw resource URI. When set, WASM signs this URI directly. */\n resource?: string;\n /** Capability caveats that must remain on this invocation attenuation. */\n caveats?: Record<string, unknown>[];\n}\n\n/**\n * Invoke function for minting a single authorization header that covers\n * multiple capabilities across one effective invoker.\n */\nexport type InvokeAnyFunction = (\n session: ServiceSession,\n entries: InvokeAnyEntry[],\n facts?: InvocationFacts,\n) => ServiceHeaders;\n\n/**\n * Fetch request options - compatible with standard fetch API.\n */\nexport interface FetchRequestInit {\n method?: string;\n headers?: ServiceHeaders;\n body?: Blob | FormData | string;\n signal?: AbortSignal;\n redirect?: \"error\";\n}\n\n/**\n * Fetch response interface - compatible with standard Response.\n */\nexport interface FetchResponse {\n ok: boolean;\n status: number;\n statusText: string;\n body?: unknown;\n headers: {\n get(name: string): string | null;\n };\n json(): Promise<unknown>;\n text(): Promise<string>;\n arrayBuffer(): Promise<ArrayBuffer>;\n blob(): Promise<Blob>;\n}\n\n/**\n * Fetch function signature - allows for custom fetch implementations.\n * Compatible with both browser fetch and Node.js fetch.\n */\nexport type FetchFunction = (\n url: string,\n init?: FetchRequestInit,\n) => Promise<FetchResponse>;\n\n// =============================================================================\n// Retry Policy\n// =============================================================================\n\n/**\n * Configuration for automatic retry of failed requests.\n */\nexport interface RetryPolicy {\n /** Maximum number of attempts (including initial) */\n maxAttempts: number;\n /** Backoff strategy between retries */\n backoff: \"none\" | \"linear\" | \"exponential\";\n /** Base delay in milliseconds for backoff calculation */\n baseDelayMs: number;\n /** Maximum delay in milliseconds between retries */\n maxDelayMs: number;\n /** Error codes that should trigger a retry */\n retryableErrors: string[];\n}\n\n/**\n * Default retry policy.\n */\nexport const defaultRetryPolicy: RetryPolicy = {\n maxAttempts: 3,\n backoff: \"exponential\",\n baseDelayMs: 1000,\n maxDelayMs: 10000,\n retryableErrors: [ErrorCodes.NETWORK_ERROR, ErrorCodes.TIMEOUT],\n};\n\n// =============================================================================\n// Service Context\n// =============================================================================\n\n/**\n * Event handler function type.\n */\nexport type EventHandler = (data: unknown) => void;\n\n/**\n * SDK telemetry event handler.\n */\nexport type TelemetryEventHandler = (event: string, data: unknown) => void;\n\n/**\n * Default-off telemetry configuration.\n */\nexport type TelemetryConfig =\n | boolean\n | {\n enabled?: boolean;\n onEvent?: TelemetryEventHandler;\n };\n\n/**\n * Service interface - base contract for all services.\n */\nexport interface IService {\n /** Initialize service with context */\n initialize(context: IServiceContext): void;\n\n /** Called when session changes (sign-in, sign-out, refresh) */\n onSessionChange(session: ServiceSession | null): void;\n\n /** Called when SDK signs out - should abort pending operations */\n onSignOut(): void;\n\n /** Service-specific configuration */\n readonly config: Record<string, unknown>;\n}\n\n/**\n * Context provided to services for accessing platform dependencies.\n * The SDK creates this context and passes it to services during initialization.\n */\nexport interface IServiceContext {\n // Session management\n /** Current active session, or null if not authenticated */\n readonly session: ServiceSession | null;\n /** Whether there is an active authenticated session */\n readonly isAuthenticated: boolean;\n\n // Platform dependencies (injected by SDK)\n /** Platform-specific invoke function from WASM binding */\n readonly invoke: InvokeFunction;\n /** Optional multi-resource invoke function */\n readonly invokeAny?: InvokeAnyFunction;\n /** Fetch function (defaults to globalThis.fetch) */\n readonly fetch: FetchFunction;\n /** Available TinyCloud host URLs */\n readonly hosts: string[];\n\n // Cross-service access\n /** Get another registered service by name */\n getService<T extends IService>(name: string): T | undefined;\n\n // Telemetry/Events\n /** Emit a telemetry event */\n emit(event: string, data: unknown): void;\n /** Subscribe to events */\n on(event: string, handler: EventHandler): () => void;\n\n // Lifecycle\n /** Abort signal that fires when SDK signs out. */\n readonly abortSignal: AbortSignal;\n /** Additional abort signal for a scoped operation, when active. */\n readonly operationAbortSignal?: AbortSignal;\n /** Install an additional signal for the duration of a scoped operation. */\n setOperationAbortSignal?(signal: AbortSignal | undefined): void;\n // Retry policy\n /** Retry policy for failed requests */\n readonly retryPolicy: RetryPolicy;\n}\n\n// =============================================================================\n// Telemetry Events\n// =============================================================================\n\n/**\n * Event emitted before a service request.\n */\nexport interface ServiceRequestEvent {\n service: string;\n action: string;\n span?: string;\n key?: string;\n timestamp: number;\n}\n\n/**\n * Event emitted after a service response.\n */\nexport interface ServiceResponseEvent {\n service: string;\n action: string;\n span?: string;\n ok: boolean;\n duration: number;\n durationMs?: number;\n status?: number;\n}\n\n/**\n * Event emitted on service error.\n */\nexport interface ServiceErrorEvent {\n service: string;\n span?: string;\n error: ServiceError;\n}\n\n/**\n * Event emitted on retry attempt.\n */\nexport interface ServiceRetryEvent {\n service: string;\n attempt: number;\n maxAttempts: number;\n error: ServiceError;\n}\n\n/**\n * Generic named span event for aggregating operation timings.\n */\nexport interface TelemetrySpanEvent {\n span: string;\n ok: boolean;\n durationMs: number;\n service?: string;\n action?: string;\n status?: number;\n error?: ServiceError;\n}\n\n/**\n * Telemetry event names.\n */\nexport const TelemetryEvents = {\n SPAN: \"telemetry.span\",\n SERVICE_REQUEST: \"service.request\",\n SERVICE_RESPONSE: \"service.response\",\n SERVICE_ERROR: \"service.error\",\n SERVICE_RETRY: \"service.retry\",\n SESSION_CHANGED: \"session.changed\",\n SESSION_EXPIRED: \"session.expired\",\n REPLICATION_READ: \"replication.read\",\n REPLICATION_WRITE: \"replication.write\",\n REPLICATION_SYNC: \"replication.sync\",\n REPLICATION_STATE: \"replication.state\",\n REPLICATION_DIVERGENCE: \"replication.divergence\",\n REPLICATION_GRANT: \"replication.grant\",\n REPLICATION_LIFECYCLE: \"replication.lifecycle\",\n /** Emitted once, on the first write rejected because storage is full. */\n STORAGE_FULL: \"storage.full\",\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","export interface ParsedServiceErrorBody {\n error?: string;\n message?: string;\n code?: string;\n}\n\nexport function parseServiceErrorBody(errorText: string): ParsedServiceErrorBody {\n try {\n return JSON.parse(errorText) as ParsedServiceErrorBody;\n } catch {\n return {};\n }\n}\n\nexport function formatServiceResponseError(\n serviceLabel: string,\n operation: string,\n status: number,\n errorText: string,\n parsed: ParsedServiceErrorBody,\n): string {\n if (parsed.message) {\n return compactMessage(parsed.message);\n }\n\n if (looksLikeHtml(errorText)) {\n if (status === 524 || /524\\s*[:-]/i.test(errorText) || /a timeout occurred/i.test(errorText)) {\n return `${serviceLabel} ${operation} failed: upstream request timed out. Please retry.`;\n }\n return `${serviceLabel} ${operation} failed: upstream service returned an HTML error page (${status}).`;\n }\n\n return `${serviceLabel} ${operation} failed: ${status} - ${compactMessage(errorText)}`;\n}\n\nexport function responseErrorMeta(\n status: number,\n statusText: string,\n errorText: string,\n): Record<string, unknown> {\n const meta: Record<string, unknown> = { status, statusText };\n const snippet = compactMessage(errorText);\n if (snippet) {\n meta.responseSnippet = snippet.slice(0, 300);\n }\n return meta;\n}\n\nfunction looksLikeHtml(text: string): boolean {\n return /<!doctype html/i.test(text) || /<html[\\s>]/i.test(text);\n}\n\nfunction compactMessage(message: string): string {\n const compact = message.replace(/\\s+/g, \" \").trim();\n return compact.length > 500 ? `${compact.slice(0, 500)}...` : compact;\n}\n","/**\n * SDK Services - Error Utilities\n *\n * Utilities for creating and handling service errors.\n */\n\nimport { responseErrorMeta } from \"./responseErrors\";\nimport { CAPABILITY_REGISTRY } from \"@tinycloud/bootstrap\";\nimport { parseNetworkId } from \"./encryption/networkId\";\n\nimport {\n type ServiceError,\n ErrorCodes,\n err,\n serviceError,\n type PermissionHint,\n} from \"./types\";\n\n/** Validate one structured node permission hint without accepting raw errors. */\nexport function parsePermissionHint(value: unknown): PermissionHint | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n const candidate = value as Record<string, unknown>;\n const keys = Object.keys(candidate).sort();\n if (keys.some((key) => ![\"actions\", \"path\", \"service\", \"space\"].includes(key))) return undefined;\n if (candidate.service !== \"tinycloud.kv\" && candidate.service !== \"tinycloud.encryption\") return undefined;\n if (typeof candidate.path !== \"string\" || candidate.path.length === 0 || candidate.path.includes(\"*\")) return undefined;\n if (candidate.service === \"tinycloud.kv\" &&\n (typeof candidate.space !== \"string\" || candidate.space.length === 0 || !candidate.path.startsWith(\"vault/\") || candidate.path.endsWith(\"/\"))) {\n return undefined;\n }\n if (candidate.service === \"tinycloud.encryption\" &&\n (candidate.space !== undefined || !candidate.path.startsWith(\"urn:tinycloud:encryption:\") || candidate.path.endsWith(\":\"))) {\n return undefined;\n }\n if (!Array.isArray(candidate.actions) || candidate.actions.length !== 1 ||\n typeof candidate.actions[0] !== \"string\" || candidate.actions[0].includes(\"*\")) return undefined;\n const expectedAction = candidate.service === \"tinycloud.kv\"\n ? \"tinycloud.kv/get\"\n : \"tinycloud.encryption/decrypt\";\n if (candidate.actions[0] !== expectedAction) return undefined;\n const space = typeof candidate.space === \"string\" ? candidate.space : undefined;\n return {\n service: candidate.service,\n ...(space === undefined ? {} : { space }),\n path: candidate.path,\n actions: [candidate.actions[0]],\n };\n}\n\n/** Extract only a validated structured hint from an SDK-owned error body. */\nexport function parsePermissionHintFromErrorText(text: string): PermissionHint | undefined {\n try {\n const parsed: unknown = JSON.parse(text);\n if (typeof parsed !== \"object\" || parsed === null) return undefined;\n const root = parsed as Record<string, unknown>;\n const nested = root.error;\n const nestedRecord = typeof nested === \"object\" && nested !== null\n ? nested as Record<string, unknown>\n : undefined;\n return parsePermissionHint(root.permissionHint) ??\n parsePermissionHint(nestedRecord?.permissionHint);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Create a service error for authentication required.\n */\nexport function authRequiredError(service: string): ServiceError {\n return {\n code: ErrorCodes.AUTH_REQUIRED,\n message: \"Authentication required. Please sign in first.\",\n service,\n };\n}\n\n/**\n * Create a service error for expired authentication.\n */\nexport function authExpiredError(service: string): ServiceError {\n return {\n code: ErrorCodes.AUTH_EXPIRED,\n message: \"Session has expired. Please sign in again.\",\n service,\n };\n}\n\n/**\n * Create a service error for network issues.\n */\nexport function networkError(\n service: string,\n message: string,\n cause?: Error\n): ServiceError {\n return {\n code: ErrorCodes.NETWORK_ERROR,\n message,\n service,\n cause,\n };\n}\n\n/**\n * Classify a node retry signal (tinycloud-node #347, TC-1054). The node's\n * bodies are plain text led by a stable token:\n * - `503 sql_busy: <stage>: ...` and `503 unavailable_retryable: ...` are\n * `NODE_BUSY`: nothing was applied; retry after `meta.retryAfterMs`.\n * - `503 request_timeout: ...` is `REQUEST_TIMEOUT`: the deadline passed before\n * any write began, so nothing was applied.\n * - `500 outcome_unknown: ...` is `OUTCOME_UNKNOWN`: a write may have been\n * applied; read back before retrying a non-idempotent operation.\n *\n * `meta.retryable` is the safe-to-retry verdict and `meta.retryAfterMs` the\n * node's `Retry-After` (the node sends whole seconds; anything else is\n * ignored). The SDK never retries on its own. Returns `undefined` for any\n * other response, including the bodyless 503/500 of nodes before 1.21, which\n * callers keep classifying as they always did.\n */\nexport function nodeRetryError(\n service: string,\n message: string,\n response: { status: number; statusText: string; headers: { get(name: string): string | null } },\n errorText: string\n): ServiceError | undefined {\n const body = errorText.trim();\n const [, token, stage] = /^([a-z_]+)(?::\\s*([a-z_]+):)?/.exec(body) ?? [];\n let code: string;\n if (response.status === 503 && (token === \"sql_busy\" || token === \"unavailable_retryable\")) {\n code = ErrorCodes.NODE_BUSY;\n } else if (response.status === 503 && token === \"request_timeout\") {\n code = ErrorCodes.REQUEST_TIMEOUT;\n } else if (response.status === 500 && token === \"outcome_unknown\") {\n code = ErrorCodes.OUTCOME_UNKNOWN;\n } else {\n return undefined;\n }\n const retryable = code !== ErrorCodes.OUTCOME_UNKNOWN;\n const retryAfter = response.headers.get(\"retry-after\")?.trim();\n return serviceError(code, message, service, {\n meta: {\n ...responseErrorMeta(response.status, response.statusText, errorText),\n retryable,\n ...(token === \"sql_busy\" && stage ? { stage } : {}),\n ...(retryable && retryAfter && /^\\d{1,9}$/.test(retryAfter) ? { retryAfterMs: Number(retryAfter) * 1000 } : {}),\n },\n });\n}\n\n/**\n * Abort reason for a service request that exceeded its configured timeout.\n *\n * Named `TimeoutError` (like the reason of the platform's\n * `AbortSignal.timeout()`), so it maps to `ErrorCodes.TIMEOUT` and never to\n * `ErrorCodes.ABORTED`, which is reserved for caller and sign-out aborts.\n */\nexport class RequestTimeoutError extends Error {\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms.`);\n this.name = \"TimeoutError\";\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * Whether an error is a {@link RequestTimeoutError}. Also matches by shape so\n * that a copy of the class from another bundle (ESM/CJS entrypoint) is\n * recognized.\n */\nexport function isRequestTimeoutError(error: unknown): error is RequestTimeoutError {\n return (\n error instanceof RequestTimeoutError ||\n (error instanceof Error &&\n error.name === \"TimeoutError\" &&\n typeof (error as { timeoutMs?: unknown }).timeoutMs === \"number\")\n );\n}\n\n/**\n * Create a service error for timeouts.\n *\n * @param timeoutMs - The configured timeout that elapsed, when known\n * @param cause - The underlying timeout error, when known\n */\nexport function timeoutError(\n service: string,\n timeoutMs?: number,\n cause?: Error\n): ServiceError {\n if (timeoutMs === undefined) {\n return {\n code: ErrorCodes.TIMEOUT,\n message: \"Request timed out.\",\n service,\n ...(cause === undefined ? {} : { cause }),\n };\n }\n return {\n code: ErrorCodes.TIMEOUT,\n message: `Request timed out after ${timeoutMs}ms.`,\n service,\n ...(cause === undefined ? {} : { cause }),\n meta: { timeoutMs },\n };\n}\n\n/**\n * Create a service error for aborted requests.\n */\nexport function abortedError(service: string): ServiceError {\n return {\n code: ErrorCodes.ABORTED,\n message: \"Request was aborted.\",\n service,\n };\n}\n\n/**\n * Create a service error for not found resources.\n */\nexport function notFoundError(\n service: string,\n resource: string\n): ServiceError {\n return {\n code: ErrorCodes.NOT_FOUND,\n message: `Resource not found: ${resource}`,\n service,\n };\n}\n\n/**\n * Create a service error for permission denied.\n */\nexport function permissionDeniedError(\n service: string,\n action: string\n): ServiceError {\n return {\n code: ErrorCodes.PERMISSION_DENIED,\n message: `Permission denied for action: ${action}`,\n service,\n };\n}\n\n/**\n * Parse the server's \"Unauthorized Action: {resource} / {ability}\" pattern.\n */\nexport function parseAuthError(responseText: string): { resource?: string; action?: string } {\n const match = responseText.match(/^Unauthorized Action:\\s*(.+?)\\s*\\/\\s*(tinycloud\\.\\S+)$/m);\n if (match) {\n return { resource: match[1].trim(), action: match[2].trim() };\n }\n return {};\n}\n\n/**\n * Create a service error for unauthorized action (missing capability).\n */\nexport function authUnauthorizedError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return serviceError(ErrorCodes.AUTH_UNAUTHORIZED, message, service, { meta });\n}\n\n/**\n * Authorization verdict read from an error's structure, never its message:\n * - `\"unauthenticated\"`: HTTP 401 — the session is missing or stale.\n * - `\"forbidden\"`: HTTP 403, or `AUTH_UNAUTHORIZED` without a status — the\n * session is valid but lacks the capability.\n * - `\"other\"`: a non-authorization HTTP error status (4xx/5xx).\n */\nexport type AuthorizationVerdict = \"unauthenticated\" | \"forbidden\" | \"other\";\n\nconst grantableActions = new Set(\n CAPABILITY_REGISTRY.map(({ urn }) => urn).filter((urn) => !urn.endsWith(\"/*\"))\n);\n\nconst capabilityServices = new Set(\n CAPABILITY_REGISTRY.map(({ service }) => service.slice(\"tinycloud.\".length))\n);\n\n/**\n * Parse a canonical TinyCloud resource, keeping nested space names intact.\n * The first registered service segment is authoritative: a later matching\n * segment in the path cannot turn a different service into grant advice.\n */\nexport function parseCapabilityResource(\n resource: string,\n service: string\n): { space: string; path: string } | undefined {\n if (\n resource.length === 0 ||\n resource.length > 1024 ||\n !capabilityServices.has(service) ||\n /[\\s\\p{C}*#]/u.test(resource) ||\n resource.includes(\"://\")\n ) return undefined;\n\n const segments = resource.split(\"/\");\n if (!/^tinycloud:[A-Za-z0-9][A-Za-z0-9:._-]*$/.test(segments[0])) return undefined;\n const serviceIndex = segments.findIndex((segment, index) =>\n index > 0 && capabilityServices.has(segment)\n );\n if (serviceIndex < 1 || segments[serviceIndex] !== service) return undefined;\n if (segments.slice(1, serviceIndex).some((segment) =>\n segment.length === 0 || segment === \".\" || segment === \"..\"\n )) return undefined;\n const pathSegments = segments.slice(serviceIndex + 1);\n if (pathSegments.some((segment) => segment === \".\" || segment === \"..\")) return undefined;\n if (service !== \"kv\" && (pathSegments.length === 0 || pathSegments[pathSegments.length - 1] === \"\")) return undefined;\n\n return {\n space: segments.slice(0, serviceIndex).join(\"/\"),\n path: pathSegments.join(\"/\"),\n };\n}\n\n/** A capability resource is a canonical TinyCloud resource or encryption network, never a URL or expression. */\nfunction safeCapabilityResource(value: unknown, service: string, action: string): value is string {\n if (typeof value !== \"string\" || value.length === 0 || value.length > 1024) return false;\n if (service === \"encryption\" && value.startsWith(\"urn:tinycloud:encryption:\")) {\n if (\n !/^[A-Za-z0-9_][A-Za-z0-9_.:/-]*$/.test(value) ||\n value.includes(\"//\") ||\n value.split(\"/\").some((segment) => segment === \".\" || segment === \"..\")\n ) return false;\n try {\n parseNetworkId(value);\n return true;\n } catch {\n return false;\n }\n }\n const parsed = parseCapabilityResource(value, service);\n return parsed !== undefined &&\n (!parsed.path.endsWith(\"/\") || (service === \"kv\" && action === \"tinycloud.kv/list\")) &&\n (parsed.path !== \"\" || (service === \"kv\" && action === \"tinycloud.kv/list\"));\n}\n\n/**\n * Return a grantable missing capability only when typed authorization evidence\n * and both safe, service-matched capability fields are present. Status alone,\n * message text, wildcard actions and unrecognized actions cannot grant scope.\n */\nexport function validatedCapabilityOf(\n error: unknown\n): { resource: string; requiredAction: string } | undefined {\n const verdict = authorizationVerdictOf(error);\n if (verdict !== \"unauthenticated\" && verdict !== \"forbidden\") return undefined;\n\n const seen = new Set<unknown>();\n let node: unknown = error;\n for (\n let depth = 0;\n depth < MAX_CAUSE_DEPTH && typeof node === \"object\" && node !== null && !seen.has(node);\n depth += 1\n ) {\n seen.add(node);\n const record = node as Record<string, unknown>;\n const status = errorStatusOf(record);\n const meta = record.meta;\n if (\n (status === 401 || status === 403 || (status === undefined && record.code === ErrorCodes.AUTH_UNAUTHORIZED)) &&\n typeof meta === \"object\" && meta !== null && !Array.isArray(meta)\n ) {\n const metadata = meta as Record<string, unknown>;\n const { resource, requiredAction } = metadata;\n if (typeof requiredAction === \"string\" && grantableActions.has(requiredAction)) {\n const actionService = requiredAction.slice(\"tinycloud.\".length, requiredAction.indexOf(\"/\"));\n const explicitService = record.service === undefined ? metadata.service : record.service;\n if (\n safeCapabilityResource(resource, actionService, requiredAction) &&\n (explicitService === undefined ||\n explicitService === actionService ||\n explicitService === `tinycloud.${actionService}`)\n ) {\n return { resource, requiredAction };\n }\n }\n }\n node = record.cause;\n }\n return undefined;\n}\n\nconst MAX_CAUSE_DEPTH = 8;\n\n/** The first 4xx/5xx among `status`, `statusCode`, `meta.status`. Success and\n * informational statuses (e.g. a 200 `Response` kept as `cause`) are not\n * failures, so they are skipped rather than ending the search. */\nfunction errorStatusOf(node: Record<string, unknown>): number | undefined {\n const meta = node.meta;\n const candidates = [\n node.status,\n node.statusCode,\n typeof meta === \"object\" && meta !== null\n ? (meta as Record<string, unknown>).status\n : undefined,\n ];\n for (const candidate of candidates) {\n if (\n typeof candidate === \"number\" &&\n Number.isInteger(candidate) &&\n candidate >= 400 &&\n candidate <= 599\n ) {\n return candidate;\n }\n }\n return undefined;\n}\n\n/**\n * Classify an error from its typed HTTP error status (`status`, `statusCode`,\n * or `meta.status`, 4xx/5xx only) and `AUTH_UNAUTHORIZED` code, following the\n * `cause` chain outer-first (at most {@link MAX_CAUSE_DEPTH} links, cycles\n * stop the walk) so wrappers that rethrow with `cause` keep the verdict. The\n * outermost error status decides, so an outer 5xx keeps its retry budget even\n * over an inner 401. Returns `undefined` for untyped errors; only then may\n * callers fall back to message heuristics.\n */\nexport function authorizationVerdictOf(\n error: unknown,\n): AuthorizationVerdict | undefined {\n const seen = new Set<unknown>();\n let sawUnauthorizedCode = false;\n let node: unknown = error;\n for (\n let depth = 0;\n depth < MAX_CAUSE_DEPTH && typeof node === \"object\" && node !== null && !seen.has(node);\n depth += 1\n ) {\n seen.add(node);\n const record = node as Record<string, unknown>;\n const status = errorStatusOf(record);\n if (status === 401) return \"unauthenticated\";\n if (status === 403) return \"forbidden\";\n if (status !== undefined) return \"other\";\n if (record.code === ErrorCodes.AUTH_UNAUTHORIZED) sawUnauthorizedCode = true;\n node = record.cause;\n }\n return sawUnauthorizedCode ? \"forbidden\" : undefined;\n}\n\n/**\n * Create a service error for storage quota exceeded (402 Payment Required).\n */\nexport function storageQuotaExceededError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return {\n code: ErrorCodes.STORAGE_QUOTA_EXCEEDED,\n message,\n service,\n meta,\n };\n}\n\n/**\n * Create a service error for storage limit reached (413 Payload Too Large).\n */\nexport function storageLimitReachedError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return {\n code: ErrorCodes.STORAGE_LIMIT_REACHED,\n message,\n service,\n meta,\n };\n}\n\n/** Shown when a write is rejected because the owner's storage is full. */\nexport const STORAGE_FULL_MESSAGE =\n \"TinyCloud storage is full, so this change was not saved. Reading still works. Free up space or upgrade your plan to save again.\";\n\n/** Shown when a write is larger than the storage the owner has left. */\nexport const STORAGE_WRITE_TOO_LARGE_MESSAGE =\n \"This change is larger than the TinyCloud storage you have left, so it was not saved. Reading still works. Free up space or upgrade your plan to save it.\";\n\n/**\n * Parse the byte counts from a node storage rejection\n * (\"... Used: 155744 bytes, Limit: 0 bytes\"). The limit is the share of the\n * owner's account-wide budget left for this space, so it is 0 when the\n * owner's other spaces already use the whole budget.\n */\nexport function parseStorageQuotaBytes(\n responseText: string\n): { usedBytes: number; limitBytes: number } | undefined {\n const match = responseText.match(/Used:\\s*(\\d+)\\s*bytes,\\s*Limit:\\s*(\\d+)\\s*bytes/i);\n if (!match) return undefined;\n return {\n usedBytes: parseInt(match[1], 10),\n limitBytes: parseInt(match[2], 10),\n };\n}\n\n/**\n * Account-wide storage totals the node attaches to a storage rejection when\n * billing supplied them. `limitBytes` is the plan budget shared by all the\n * owner's spaces, so this is the number to show the user, not the space's.\n */\nexport interface StorageAccountUsage {\n usedBytes: number;\n limitBytes: number;\n /** Plan tier id, e.g. `free` or `paid`. */\n plan?: string;\n}\n\n/** What a node storage rejection says about the space and the account. */\nexport interface StorageRejectionDetails {\n usedBytes?: number;\n limitBytes?: number;\n account?: StorageAccountUsage;\n}\n\nconst STORAGE_REJECTION_BODY_ERRORS: Record<string, true> = {\n storage_quota_exceeded: true,\n storage_limit_reached: true,\n};\n\nfunction isByteCount(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction byteCountsOf(value: unknown): { usedBytes: number; limitBytes: number } | undefined {\n if (typeof value !== \"object\" || value === null) return undefined;\n const { usedBytes, limitBytes } = value as Record<string, unknown>;\n return isByteCount(usedBytes) && isByteCount(limitBytes) ? { usedBytes, limitBytes } : undefined;\n}\n\n/**\n * Parse a node storage rejection. The node sends a JSON body\n * (`{error, message, space: {usedBytes, limitBytes}, account?: {usedBytes,\n * limitBytes, plan}}`); older nodes send only the plain-text message. Returns\n * undefined when the body is neither, e.g. a proxy's own 413 page.\n */\nexport function parseStorageRejection(responseText: string): StorageRejectionDetails | undefined {\n let body: unknown;\n try {\n body = JSON.parse(responseText);\n } catch {\n return parseStorageQuotaBytes(responseText);\n }\n if (typeof body !== \"object\" || body === null) return undefined;\n const record = body as Record<string, unknown>;\n const message = typeof record.message === \"string\" ? record.message : \"\";\n const space = byteCountsOf(record.space) ?? parseStorageQuotaBytes(message);\n const isStorageBody = typeof record.error === \"string\" && STORAGE_REJECTION_BODY_ERRORS[record.error] === true;\n if (!space && !isStorageBody) {\n return undefined;\n }\n const details: StorageRejectionDetails = { ...space };\n const account = byteCountsOf(record.account);\n if (account) {\n const plan = (record.account as Record<string, unknown>).plan;\n details.account = typeof plan === \"string\" && plan !== \"\" ? { ...account, plan } : account;\n }\n return details;\n}\n\n/**\n * Build the error for a write the node rejected for storage: 402 when storage\n * is already full, 413 when this write is larger than what is left. KV, SQL\n * and DuckDB all report it with the same codes and message so an app can\n * detect \"storage full\" in one place with {@link isStorageFullError}.\n * `meta.usedBytes`/`meta.limitBytes` describe the space; `meta.account`\n * carries the account totals when the node sent them.\n */\nexport function storageRejectionError(\n service: string,\n status: 402 | 413,\n meta: Record<string, unknown>,\n responseText: string\n): ServiceError {\n const quotaMeta = { ...meta, ...parseStorageRejection(responseText) };\n return status === 402\n ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta)\n : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);\n}\n\n/**\n * True when a write failed for storage: the owner's TinyCloud storage is full\n * (`STORAGE_QUOTA_EXCEEDED`), or this write is larger than what is left\n * (`STORAGE_LIMIT_REACHED`). Reads keep working in both cases, so apps should\n * stay usable read-only and point the user at freeing up space or upgrading.\n */\nexport function isStorageFullError(error: { code?: string } | null | undefined): boolean {\n return (\n error?.code === ErrorCodes.STORAGE_QUOTA_EXCEEDED ||\n error?.code === ErrorCodes.STORAGE_LIMIT_REACHED\n );\n}\n\n/**\n * Wrap an unknown error in a ServiceError.\n */\nexport function wrapError(\n service: string,\n error: unknown,\n defaultCode: string = ErrorCodes.NETWORK_ERROR\n): ServiceError {\n if (error instanceof Error) {\n // A configured request timeout elapsed (see BaseService.createRequestSignal)\n if (isRequestTimeoutError(error)) {\n return timeoutError(service, error.timeoutMs, error);\n }\n\n // Check for abort errors\n if (error.name === \"AbortError\") {\n return abortedError(service);\n }\n\n // Check for timeout errors (varies by platform)\n if (\n error.name === \"TimeoutError\" ||\n error.message.toLowerCase().includes(\"timeout\")\n ) {\n return timeoutError(service);\n }\n\n return {\n code: defaultCode,\n message: error.message,\n service,\n cause: error,\n };\n }\n\n return {\n code: defaultCode,\n message: String(error),\n service,\n };\n}\n\n/**\n * Create an error Result from a ServiceError.\n */\nexport function errorResult(error: ServiceError) {\n return err(error);\n}\n","/**\n * TinyCloud encryption network identifiers.\n *\n * A network id is `urn:tinycloud:encryption:<ownerDid>:<network>` where\n * `ownerDid` is the owner's DID and `network` is a\n * non-empty label drawn from `[a-z0-9][a-z0-9-]*`.\n *\n * The embedded owner DID is the root authority for the network: any\n * delegation chain ending in a `tinycloud.encryption/decrypt` grant on\n * the network must root at this owner DID.\n */\n\nconst URN_PREFIX = \"urn:tinycloud:encryption:\";\nconst NETWORK_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;\nconst PKH_EIP155_DID_RE = /^did:pkh:eip155:(\\d+):(0x[a-fA-F0-9]{40})$/;\n\nexport interface ParsedNetworkId {\n /** The full URN string. */\n networkId: string;\n /** Owner DID embedded in the URN (the network's root authority). */\n ownerDid: string;\n /** Network label (the suffix after the owner DID). */\n name: string;\n}\n\nexport class NetworkIdError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"NetworkIdError\";\n }\n}\n\n/**\n * Parse a network id string into its owner DID and name components.\n *\n * Throws {@link NetworkIdError} when the input does not match\n * `urn:tinycloud:encryption:<did>:<name>`, when the embedded DID is\n * malformed, or when the network name fails {@link NETWORK_NAME_RE}.\n */\nexport function parseNetworkId(networkId: string): ParsedNetworkId {\n if (typeof networkId !== \"string\" || networkId.length === 0) {\n throw new NetworkIdError(\"networkId must be a non-empty string\");\n }\n if (!networkId.startsWith(URN_PREFIX)) {\n throw new NetworkIdError(\n `networkId must start with ${URN_PREFIX} (got ${JSON.stringify(networkId)})`,\n );\n }\n const body = networkId.slice(URN_PREFIX.length);\n // `body` = \"<ownerDid>:<network>\"\n // ownerDid contains ':' (e.g. did:key:z6Mk...), so we split on the LAST colon\n // and treat the suffix as the network name.\n const lastColon = body.lastIndexOf(\":\");\n if (lastColon <= 0 || lastColon === body.length - 1) {\n throw new NetworkIdError(\n `networkId missing ownerDid or name segment (got ${JSON.stringify(networkId)})`,\n );\n }\n const ownerDid = body.slice(0, lastColon);\n const name = body.slice(lastColon + 1);\n\n if (!ownerDid.startsWith(\"did:\")) {\n throw new NetworkIdError(\n `networkId ownerDid must be a DID (got ${JSON.stringify(ownerDid)})`,\n );\n }\n // Minimal DID shape: did:<method>:<id> — three colon-separated segments,\n // each non-empty.\n const didParts = ownerDid.split(\":\");\n if (didParts.length < 3 || didParts.some((p) => p.length === 0)) {\n throw new NetworkIdError(\n `networkId ownerDid is not a well-formed DID (got ${JSON.stringify(ownerDid)})`,\n );\n }\n if (!NETWORK_NAME_RE.test(name)) {\n throw new NetworkIdError(\n `networkId name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`,\n );\n }\n return { networkId, ownerDid, name };\n}\n\n/**\n * Construct a network id URN from an owner DID and a network name.\n * Validates inputs and throws {@link NetworkIdError} on bad shape.\n */\nexport function buildNetworkId(ownerDid: string, name: string): string {\n if (typeof ownerDid !== \"string\" || !ownerDid.startsWith(\"did:\")) {\n throw new NetworkIdError(\"ownerDid must be a DID\");\n }\n if (typeof name !== \"string\" || !NETWORK_NAME_RE.test(name)) {\n throw new NetworkIdError(\n `network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`,\n );\n }\n const networkId = `${URN_PREFIX}${ownerDid}:${name}`;\n // Re-validate the composed result so the same error path triggers\n // for caller inputs that compose into a malformed URN.\n parseNetworkId(networkId);\n return networkId;\n}\n\n/**\n * Returns true when {@link networkId} is a syntactically valid network URN.\n */\nexport function isNetworkId(networkId: unknown): networkId is string {\n if (typeof networkId !== \"string\") {\n return false;\n }\n try {\n parseNetworkId(networkId);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction parsePkhOwnerDid(ownerDid: string):\n | { chainId: string; address: string }\n | null {\n const match = ownerDid.match(PKH_EIP155_DID_RE);\n if (!match) return null;\n return {\n chainId: match[1],\n address: match[2].toLowerCase(),\n };\n}\n\n/**\n * Compare owner DIDs as network principals. For `did:pkh:eip155`, EVM\n * address casing is not part of principal identity; other DID methods\n * remain exact string matches.\n */\nexport function ownerDidMatches(a: string, b: string): boolean {\n const aPkh = parsePkhOwnerDid(a);\n const bPkh = parsePkhOwnerDid(b);\n if (aPkh && bPkh) {\n return aPkh.chainId === bPkh.chainId && aPkh.address === bPkh.address;\n }\n return a === b;\n}\n\n/**\n * Resolve the discovery key used to look up a network's descriptor under\n * an owner's public account-space.\n *\n * Format: `.well-known/encryption/network/<name>`.\n */\nexport function networkDiscoveryKey(name: string): string {\n if (!NETWORK_NAME_RE.test(name)) {\n throw new NetworkIdError(\n `network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`,\n );\n }\n return `.well-known/encryption/network/${name}`;\n}\n\nexport const ENCRYPTION_NETWORK_URN_PREFIX = URN_PREFIX;\nexport const NETWORK_NAME_PATTERN = NETWORK_NAME_RE;\n","/**\n * BaseService - Abstract base class for all TinyCloud services.\n *\n * Provides common functionality:\n * - Context management\n * - Session lifecycle hooks\n * - Abort signal handling\n * - Telemetry emission\n */\n\nimport {\n IService,\n IServiceContext,\n ServiceSession,\n ServiceError,\n TelemetryEvents,\n err,\n Result,\n} from \"../types\";\nimport { authRequiredError, wrapError, RequestTimeoutError } from \"../errors\";\nimport type { RequestSignal } from \"./types\";\n\n/** setTimeout's delay is a signed 32-bit integer; larger values fire at once. */\nconst MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/**\n * Abstract base class for TinyCloud services.\n *\n * Services extend this class to get common functionality like\n * context management, session lifecycle, and abort handling.\n *\n * @example\n * ```typescript\n * class MyService extends BaseService implements IMyService {\n * static readonly serviceName = 'myservice';\n *\n * constructor(config: MyServiceConfig = {}) {\n * super();\n * this._config = config;\n * }\n *\n * async doSomething(): Promise<Result<Data>> {\n * if (!this.requireAuth()) {\n * return err(authRequiredError('myservice'));\n * }\n * // ... implementation\n * }\n * }\n * ```\n */\nexport abstract class BaseService implements IService {\n /**\n * Service identifier used for registration.\n * Must be overridden by subclasses.\n */\n static readonly serviceName: string;\n\n /**\n * Service context providing access to platform dependencies.\n * Set during initialize().\n */\n protected context!: IServiceContext;\n\n /**\n * Abort controller for this service's operations.\n * Reset on sign-out.\n */\n protected abortController: AbortController = new AbortController();\n\n /**\n * Service-specific configuration.\n */\n protected _config: Record<string, unknown> = {};\n\n /**\n * Get the service configuration.\n */\n get config(): Record<string, unknown> {\n return this._config;\n }\n\n /**\n * Initialize the service with context.\n * Called by the SDK after instantiation.\n *\n * @param context - The service context\n */\n initialize(context: IServiceContext): void {\n this.context = context;\n }\n\n /**\n * Called when session changes (sign-in, sign-out, refresh).\n * Override in subclasses to handle session changes.\n *\n * @param session - The new session, or null if signed out\n */\n onSessionChange(session: ServiceSession | null): void {\n // Override in subclass if needed\n }\n\n /**\n * Called when SDK signs out.\n * Aborts all pending operations.\n */\n onSignOut(): void {\n this.abortController.abort();\n this.abortController = new AbortController();\n }\n\n /**\n * Get the abort signal for this service.\n * Combines the service-level abort with context-level abort.\n */\n protected get abortSignal(): AbortSignal {\n return this.combineSignals();\n }\n\n /**\n * Check if the service is authenticated.\n */\n protected get isAuthenticated(): boolean {\n return this.context?.isAuthenticated ?? false;\n }\n\n /**\n * Get the current session.\n * Throws if not authenticated.\n */\n protected get session(): ServiceSession {\n if (!this.context?.session) {\n throw new Error(\"Not authenticated\");\n }\n return this.context.session;\n }\n\n /**\n * Check authentication and return error result if not authenticated.\n * Use this at the start of methods that require authentication.\n *\n * @returns true if authenticated, false otherwise\n */\n protected requireAuth(): boolean {\n return this.isAuthenticated;\n }\n\n /**\n * Emit a telemetry event.\n *\n * @param event - Event name\n * @param data - Event data\n */\n protected emit(event: string, data: unknown): void {\n this.context?.emit(event, data);\n }\n\n /**\n * Emit a service request event.\n *\n * @param action - The action being performed\n * @param key - Optional key/path being accessed\n */\n protected emitRequest(action: string, key?: string): void {\n const service = this.getServiceName();\n this.emit(TelemetryEvents.SERVICE_REQUEST, {\n service,\n action,\n span: this.spanName(action),\n key,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Emit a service response event.\n *\n * @param action - The action that was performed\n * @param ok - Whether the request was successful\n * @param startTime - Start time for duration calculation\n * @param status - Optional HTTP status code\n */\n protected emitResponse(\n action: string,\n ok: boolean,\n startTime: number,\n status?: number\n ): void {\n const service = this.getServiceName();\n const durationMs = Date.now() - startTime;\n const span = this.spanName(action);\n this.emit(TelemetryEvents.SERVICE_RESPONSE, {\n service,\n action,\n span,\n ok,\n duration: durationMs,\n durationMs,\n status,\n });\n this.emit(TelemetryEvents.SPAN, {\n span,\n service,\n action,\n ok,\n durationMs,\n status,\n });\n }\n\n /**\n * Emit a service error event.\n *\n * @param error - The service error\n */\n protected emitError(error: ServiceError, action?: string): void {\n const span = action ? this.spanName(action) : undefined;\n this.emit(TelemetryEvents.SERVICE_ERROR, {\n service: this.getServiceName(),\n ...(span ? { span } : {}),\n error,\n });\n }\n\n /**\n * Get the service name from the static property.\n * Subclasses must define static serviceName.\n */\n protected getServiceName(): string {\n return (this.constructor as typeof BaseService).serviceName;\n }\n\n /**\n * Stable span name used by SDK telemetry sinks.\n */\n protected spanName(action: string): string {\n return `sdk.${this.getServiceName()}.${action}`;\n }\n\n /**\n * Create a combined abort signal from multiple sources.\n *\n * @param signals - Additional abort signals to combine\n * @returns A combined abort signal\n */\n protected combineSignals(...signals: (AbortSignal | undefined)[]): AbortSignal {\n const controller = new AbortController();\n const allSignals = [\n this.abortController.signal,\n this.context?.abortSignal,\n ...signals.filter(Boolean),\n ].filter(Boolean) as AbortSignal[];\n\n for (const signal of allSignals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n signal.addEventListener(\"abort\", () => controller.abort(signal.reason), {\n once: true,\n });\n }\n\n return controller.signal;\n }\n\n /**\n * Create the abort signal for one request, bounded by the request timeout.\n *\n * The signal aborts when the service signs out, the context aborts, the\n * caller's `signal` aborts, or the timeout elapses, whichever comes first.\n * A timeout aborts with a {@link RequestTimeoutError} (`TimeoutError`), which\n * `wrapError` maps to `ErrorCodes.TIMEOUT`; the other sources keep their own\n * reason (normally `AbortError`, mapped to `ErrorCodes.ABORTED`).\n *\n * The timeout is `timeoutMs` when given, otherwise `config.timeout`. Only a\n * positive, finite value (at most 2^31-1 ms) applies; `undefined`, `0`, and\n * `Infinity` mean no timeout, so a per-call `0` opts out of a configured one.\n *\n * Callers must call `dispose()` once the response body has been consumed,\n * which clears the timer and removes the listeners this adds to the\n * long-lived service and context signals.\n *\n * @param signal - The caller's abort signal for this request\n * @param timeoutMs - Per-request timeout override in milliseconds\n */\n protected createRequestSignal(\n signal?: AbortSignal,\n timeoutMs?: number\n ): RequestSignal {\n const controller = new AbortController();\n const detachers: Array<() => void> = [];\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const dispose = (): void => {\n if (timer !== undefined) {\n clearTimeout(timer);\n timer = undefined;\n }\n for (const detach of detachers.splice(0)) {\n detach();\n }\n };\n const abort = (reason: unknown): void => {\n dispose();\n controller.abort(reason);\n };\n const requestSignal: RequestSignal = { signal: controller.signal, dispose };\n\n const parents = [\n this.abortController.signal,\n this.context?.abortSignal,\n this.context?.operationAbortSignal,\n signal,\n ].filter((parent): parent is AbortSignal => parent !== undefined);\n for (const parent of parents) {\n if (parent.aborted) {\n abort(parent.reason);\n return requestSignal;\n }\n const onAbort = (): void => abort(parent.reason);\n parent.addEventListener(\"abort\", onAbort, { once: true });\n detachers.push(() => parent.removeEventListener(\"abort\", onAbort));\n }\n\n const timeout = timeoutMs ?? this._config.timeout;\n if (\n typeof timeout === \"number\" &&\n timeout > 0 &&\n timeout <= MAX_TIMER_DELAY_MS\n ) {\n timer = setTimeout(() => abort(new RequestTimeoutError(timeout)), timeout);\n }\n\n return requestSignal;\n }\n\n /**\n * Wrap an operation with error handling and telemetry.\n *\n * @param action - The action name for telemetry\n * @param key - Optional key for telemetry\n * @param operation - The operation to execute\n * @returns Result of the operation\n */\n protected async withTelemetry<T>(\n action: string,\n key: string | undefined,\n operation: () => Promise<Result<T>>\n ): Promise<Result<T>> {\n const startTime = Date.now();\n this.emitRequest(action, key);\n\n try {\n const result = await operation();\n\n if (result.ok) {\n this.emitResponse(action, true, startTime);\n } else {\n this.emitResponse(action, false, startTime);\n this.emitError(result.error, action);\n }\n\n return result;\n } catch (error) {\n const serviceError = wrapError(this.getServiceName(), error);\n this.emitResponse(action, false, startTime);\n this.emitError(serviceError, action);\n return err(serviceError);\n }\n }\n}\n","/**\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, ok, err, ErrorCodes, serviceError } from \"../types\";\nimport {\n KVGetOptions,\n KVPutOptions,\n KVBatchPutItem,\n KVBatchPutOptions,\n KVBatchPutResponse,\n KVBatchReadResponse,\n KVListOptions,\n KVChange,\n KVChangesOptions,\n KVChangesResponse,\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 batchGet<T = unknown>(\n keys: string[],\n options?: Omit<KVGetOptions, 'prefix'>\n ): Promise<Result<KVBatchReadResponse<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 * Store multiple values within this prefix in one TinyCloud KV invocation.\n */\n batchPut(\n items: KVBatchPutItem[],\n options?: Omit<KVBatchPutOptions, 'prefix'>\n ): Promise<Result<KVBatchPutResponse>>;\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 * Read one page of the change feed for everything under this prefix\n * (`tinycloud.kv/sync` on `<prefix>/`). Keys are returned relative to the\n * prefix, like `list`; `source.prefix` stays the node's full prefix.\n * See `IKVService.changes` for paging and errors.\n *\n * @example\n * ```typescript\n * const notes = kv.withPrefix('notes');\n * const page = await notes.changes({ limit: 100 });\n * // -> Feed of: notes/*, keys like 'a.md'\n * ```\n */\n changes(options?: Omit<KVChangesOptions, 'prefix'>): Promise<Result<KVChangesResponse>>;\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(\n key: string,\n options?: Omit<KVDeleteOptions, 'prefix'>\n ): Promise<Result<KVResponse<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 batchHead(\n keys: string[],\n options?: Omit<KVHeadOptions, 'prefix'>\n ): Promise<Result<KVBatchReadResponse<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 batchGet<T = unknown>(\n keys: string[],\n options?: KVGetOptions\n ): Promise<Result<KVBatchReadResponse<T>>>;\n\n put(\n key: string,\n value: unknown,\n options?: KVPutOptions\n ): Promise<Result<KVResponse<void>>>;\n\n batchPut(\n items: KVBatchPutItem[],\n options?: KVBatchPutOptions\n ): Promise<Result<KVBatchPutResponse>>;\n\n list(options?: KVListOptions): Promise<Result<KVListResponse>>;\n\n changes(options: KVChangesOptions): Promise<Result<KVChangesResponse>>;\n\n delete(key: string, options?: KVDeleteOptions): Promise<Result<KVResponse<void>>>;\n\n head(key: string, options?: KVHeadOptions): Promise<Result<KVResponse<void>>>;\n\n batchHead(\n keys: string[],\n options?: KVHeadOptions\n ): Promise<Result<KVBatchReadResponse<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 async batchGet<T = unknown>(\n keys: string[],\n options?: Omit<KVGetOptions, 'prefix'>\n ): Promise<Result<KVBatchReadResponse<T>>> {\n const response = await this._kv.batchGet<T>(\n keys.map((key) => this.getFullKey(key)),\n { ...options, prefix: '' }\n );\n if (!response.ok) return response;\n return ok({\n ...response.data,\n results: response.data.results.map((item, index) => ({\n ...item,\n key: keys[index]!,\n })),\n });\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 * Store multiple values within this prefix in one TinyCloud KV invocation.\n */\n async batchPut(\n items: KVBatchPutItem[],\n options?: Omit<KVBatchPutOptions, 'prefix'>\n ): Promise<Result<KVBatchPutResponse>> {\n return this._kv.batchPut(\n items.map((item) => ({\n ...item,\n key: this.getFullKey(item.key),\n })),\n { ...options, prefix: '' }\n );\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 * Read the change feed for everything under this prefix. A view with an\n * empty prefix (`\"\"` or `\"/\"`) is refused: the node does not serve a\n * whole-space feed.\n */\n async changes(\n options?: Omit<KVChangesOptions, 'prefix'>\n ): Promise<Result<KVChangesResponse>> {\n if (this._prefix.replace(/\\/+$/, '') === '') {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n 'KV changes needs a non-empty prefix; this view has none',\n 'kv'\n ));\n }\n const syncPrefix = `${this._prefix}/`;\n const response = await this._kv.changes({ ...options, prefix: syncPrefix });\n if (!response.ok) return response;\n return ok({\n ...response.data,\n changes: response.data.changes.map((change): KVChange => ({\n ...change,\n key: change.key.startsWith(syncPrefix)\n ? change.key.slice(syncPrefix.length)\n : change.key,\n })),\n });\n }\n\n /**\n * Delete a key.\n */\n async delete(\n key: string,\n options?: Omit<KVDeleteOptions, 'prefix'>\n ): Promise<Result<KVResponse<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 async batchHead(\n keys: string[],\n options?: Omit<KVHeadOptions, 'prefix'>\n ): Promise<Result<KVBatchReadResponse<void>>> {\n const response = await this._kv.batchHead(\n keys.map((key) => this.getFullKey(key)),\n { ...options, prefix: '' }\n );\n if (!response.ok) return response;\n return ok({\n ...response.data,\n results: response.data.results.map((item, index) => ({\n ...item,\n key: keys[index]!,\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?: 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\nimport type { Result } from \"../types\";\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 each KV request, including reading\n * the response body. When it elapses the request is aborted and the\n * operation returns an `ErrorCodes.TIMEOUT` error. A per-call `timeout`\n * option overrides it. Unset (the default), `0`, or `Infinity` means no\n * timeout.\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 * Require this read to bypass replication and use the network directly.\n * A configured read-through observer receives `NETWORK_REQUESTED` after the\n * network result settles, with its outcome and latency. This is supported by\n * `get` and `list`; `head`, `batchGet`, `batchHead` and signed URL operations\n * always use the network.\n */\n source?: \"network\";\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 * Return the raw response bytes as a Uint8Array instead of parsed/text data.\n * Use this to read back binary values (e.g. images) byte-identically. Takes\n * precedence over {@link raw}.\n */\n binary?: boolean;\n\n /**\n * Ask the node to reject the response when the stored value exceeds this\n * number of bytes. This bounds downloads before the response body is read.\n */\n maxResponseBytes?: number;\n\n /**\n * Custom timeout for this operation in milliseconds. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\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 /** Only write when the current object has this strong ETag. */\n ifMatch?: string;\n\n /** Only write when the key does not exist. */\n ifNoneMatch?: \"*\";\n\n /**\n * Custom timeout for this operation in milliseconds. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * One entry in a KV batch put request.\n */\nexport interface KVBatchPutItem {\n /**\n * The key to store under.\n */\n key: string;\n\n /**\n * The value to store.\n *\n * Objects are JSON stringified. Strings are stored as text. Binary values\n * should be supplied as Blob, ArrayBuffer, or Uint8Array.\n */\n value: unknown;\n\n /**\n * Content type for this item. Defaults to application/json for objects and\n * application/octet-stream for binary values.\n */\n contentType?: string;\n}\n\n/**\n * Options for KV batch put operations.\n */\nexport interface KVBatchPutOptions {\n /**\n * Override the default prefix for all entries in this batch.\n */\n prefix?: string;\n\n /**\n * Custom timeout for this operation in milliseconds. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Response from KV batch put operations.\n */\nexport interface KVBatchPutResponse {\n /**\n * Keys successfully written by the batch.\n */\n written: string[];\n\n /**\n * Number of written keys.\n */\n count: number;\n}\n\n/**\n * Response from a KV batch read. Transport or authorization failures are\n * returned by the outer Result; each requested key has its own Result so a\n * missing key does not discard successful siblings.\n */\nexport interface KVBatchReadResponse<T = unknown> {\n results: Array<{\n key: string;\n result: Result<KVResponse<T>>;\n }>;\n count: number;\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 * Require this read to bypass replication and use the network directly. A\n * configured read-through observer receives `NETWORK_REQUESTED` after the\n * network result settles, with its outcome and latency.\n */\n source?: \"network\";\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 /** Maximum number of keys the node may return. */\n limit?: number;\n\n /** Opaque scope-bound cursor returned by a previous bounded list. */\n cursor?: string;\n\n /**\n * Custom timeout for this operation in milliseconds. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\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 /** Only delete when the current object has this strong ETag. */\n ifMatch?: string;\n\n /**\n * Custom timeout for this operation in milliseconds. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\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. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\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. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\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 /** True when more matching keys exist than the requested limit. */\n truncated?: boolean;\n\n /** Opaque cursor for the next page, when the node returned one. */\n nextCursor?: string;\n}\n\n/**\n * Options for {@link IKVService.changes}, the `tinycloud.kv/sync` change feed.\n */\nexport interface KVChangesOptions {\n /**\n * The space-relative KV prefix to follow, sent as the invocation path. It\n * is not joined with the service's configured prefix, and must be non-empty.\n * Prefixes match whole path segments: `notes` covers `notes` and\n * `notes/a`, while `notes/` covers everything under `notes/` but not\n * `notes` itself. The session must hold `tinycloud.kv/sync` on it by name.\n *\n * Returned `changes[].key` values are full paths in the space, like this\n * prefix. On a service with a configured prefix (for example\n * `DelegatedAccess.kv`, whose prefix is the delegation path), `get` joins\n * that prefix again, so strip it from the key first, or follow the feed\n * with `kv.withPrefix(...).changes()`, which returns relative keys.\n */\n prefix: string;\n\n /**\n * The `cursor` from the previous page. Omit it to bootstrap from the start\n * of the prefix. Opaque; never parse or build one.\n */\n cursor?: string;\n\n /**\n * Page size, 1 through 1000 (node default 500). A page never splits one\n * invocation's changes, so it can hold more than `limit` changes.\n */\n limit?: number;\n\n /**\n * CID of a never-invoked delegation carrying `tinycloud.kv/retain` on the\n * prefix. When valid, the node attests `authority.retainUntil`.\n */\n retentionGrant?: string;\n\n /**\n * Custom timeout for this operation in milliseconds. Overrides\n * `KVServiceConfig.timeout`; `0` disables a configured timeout.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * One key's latest state in a change feed page. Content is not included:\n * read it with `get`/`batchGet` and check it against `etag`.\n */\nexport type KVChange =\n | {\n key: string;\n deleted: false;\n /** The strong ETag `get` returns for this state. */\n etag: string;\n metadata: Record<string, string>;\n }\n | {\n key: string;\n deleted: true;\n };\n\n/**\n * The node-attested window in which the caller's authority holds. Values are\n * RFC 3339 timestamps, or `null` when nothing in the chain sets one.\n */\nexport interface KVChangesAuthority {\n /** The latest `nbf` across the invocation's whole delegation chain. */\n notBefore: string | null;\n /** The earliest `exp` across the invocation's whole delegation chain. */\n expiresAt: string | null;\n /**\n * The earliest `exp` across a valid retention grant's own chain. `null`\n * means no retention: none was presented, or its chain sets no expiry.\n */\n retainUntil: string | null;\n}\n\n/**\n * One page of the `tinycloud.kv/sync` change feed.\n */\nexport interface KVChangesResponse {\n /** Each changed key's latest state, in commit order. */\n changes: KVChange[];\n /**\n * More changes are available now. A page may be empty with `more: true`;\n * keep paging with the returned cursor.\n */\n more: boolean;\n /**\n * The cursor for the next request. An empty poll returns the request's\n * cursor unchanged.\n */\n cursor: string;\n /** The node, space and prefix that served the page. */\n source: {\n nodeDid: string;\n space: string;\n prefix: string;\n };\n authority: KVChangesAuthority;\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 /** The change feed; never implied by `*` or `tinycloud.kv/*`. */\n SYNC: \"tinycloud.kv/sync\",\n /** Retention attestation; presented through `retentionGrant`, never invoked or implied. */\n RETAIN: \"tinycloud.kv/retain\",\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 type { KVReadThrough, KVListPage } from \"./replication/types\";\nimport {\n authRequiredError,\n wrapError,\n storageRejectionError,\n parseStorageRejection,\n parseAuthError,\n parsePermissionHintFromErrorText,\n authUnauthorizedError,\n validatedCapabilityOf,\n isRequestTimeoutError,\n nodeRetryError,\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 KVBatchPutItem,\n KVBatchPutOptions,\n KVBatchPutResponse,\n KVBatchReadResponse,\n KVListOptions,\n KVChange,\n KVChangesOptions,\n KVChangesResponse,\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\ninterface KvBatchNodeItem {\n key: string;\n ok: boolean;\n dataBase64?: string;\n headers?: Record<string, string>;\n error?: { code: string; message: string };\n}\n\ninterface KvBatchNodeResponse {\n results: KvBatchNodeItem[];\n}\n\nconst MAX_KV_BATCH_READ_ITEMS = 100;\n\nfunction isJsonObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\nconst pathByteEncoder = new TextEncoder();\n\n/**\n * Lexicographic UTF-8 byte comparison, matching the ordering of the node's\n * `BTreeMap` (Rust `String` ordering is by UTF-8 bytes). JS string comparison\n * is by UTF-16 code unit, which diverges outside the BMP.\n */\nfunction comparePathsByBytes(a: string, b: string): number {\n const left = pathByteEncoder.encode(a);\n const right = pathByteEncoder.encode(b);\n const length = Math.min(left.length, right.length);\n for (let index = 0; index < length; index++) {\n if (left[index] !== right[index]) {\n return left[index]! - right[index]!;\n }\n }\n return left.length - right.length;\n}\n\n\n/**\n * The node's whole 401 body for a revoked chain: tinycloud-core's\n * `InvocationError::DelegationRevoked` (`delegation-revoked: <cid>`) or\n * `DelegationAncestorRevoked` (`delegation-ancestor-revoked: ancestor=<cid>\n * invoked=<cid>`), rendered as-is (older nodes prefixed `Invalid invocation: `).\n * Anchored: only CIDs follow the error kind, so a key, prefix or path in any\n * other 401 (e.g. `Unauthorized Action: <space>/kv/<prefix> / ...`) never matches.\n */\nconst REVOKED_BODY =\n /^(?:Invalid invocation: )?delegation-(?:(revoked): [A-Za-z0-9]+|(ancestor-revoked): ancestor=[A-Za-z0-9]+ invoked=[A-Za-z0-9]+)$/;\n\nfunction revocationCodeOf(\n body: string\n): typeof ErrorCodes.AUTH_DELEGATION_REVOKED | typeof ErrorCodes.AUTH_DELEGATION_ANCESTOR_REVOKED | undefined {\n const match = REVOKED_BODY.exec(body.trim());\n if (match === null) return undefined;\n return match[1] !== undefined ? ErrorCodes.AUTH_DELEGATION_REVOKED : ErrorCodes.AUTH_DELEGATION_ANCESTOR_REVOKED;\n}\n\nfunction encodeKvBatchPartName(path: string): string {\n return encodeURIComponent(path).replace(/[!'()*]/g, (char) =>\n `%${char.charCodeAt(0).toString(16).toUpperCase()}`\n );\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 private readThrough: KVReadThrough | null = null;\n private readThroughReady: Promise<void> | null = null;\n\n /** Delay KV routing until an asynchronously loaded read-through is attached. */\n setReadThroughReady(ready: Promise<unknown>): void {\n const settled = ready.then(() => undefined, () => undefined);\n this.readThroughReady = settled;\n void settled.then(() => {\n if (this.readThroughReady === settled) this.readThroughReady = null;\n });\n }\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 /** Install or remove the optional replication read-through policy. */\n setReadThrough(readThrough: KVReadThrough | null): void {\n this.readThrough = readThrough;\n }\n\n\n private handleQuotaErrorResponse(\n response: FetchResponse,\n errorText: string,\n key: string\n ): Result<never> | undefined {\n if (response.status !== 402 && response.status !== 413) {\n return undefined;\n }\n // A 413 is about storage only when the node says so. A proxy's own\n // request-size limit (Cloudflare's HTML 413) is not.\n if (response.status === 413 && !parseStorageRejection(errorText)) {\n return undefined;\n }\n return err(\n storageRejectionError(\n \"kv\",\n response.status,\n { status: response.status, statusText: response.statusText, key },\n errorText\n )\n );\n }\n\n /**\n * Classify a KV 404 by reading the response body once.\n *\n * The server returns 404 both for a genuinely missing key AND for an\n * un-hosted space (body \"Space not found\"). Previously get/head/delete\n * collapsed every 404 to KV_NOT_FOUND before reading the body, so an\n * un-hosted-space read was indistinguishable from a missing key. We now\n * preserve status + the \"Space not found\" body for the un-hosted case (so the\n * CLI/SDK can normalize it to SPACE_NOT_HOSTED, matching put/list/sql), and\n * fall through to KV_NOT_FOUND for a real missing key.\n */\n private async classifyNotFound(\n response: FetchResponse,\n key: string\n ): Promise<Result<never>> {\n const errorText = await response.text();\n if (/space not found/i.test(errorText)) {\n return err(\n serviceError(\n ErrorCodes.KV_NOT_FOUND,\n `KV ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n return err(serviceError(ErrorCodes.KV_NOT_FOUND, `Key not found: ${key}`, \"kv\"));\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 // A delegated share may scope this service to one exact key. In that\n // case `get(\"\")` means that key itself, not a synthetic child with a\n // trailing slash. This also keeps the service's prefix semantics useful\n // for exact (rather than prefix) capabilities.\n return prefix ? (key === \"\" ? 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 - Request signal from createRequestSignal()\n * @returns Fetch response\n */\n private async invokeOperation(\n path: string,\n action: string,\n body: Blob | string | undefined,\n signal: AbortSignal,\n extraHeaders?: Readonly<Record<string, string>>\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 const requestHeaders: ServiceHeaders = Array.isArray(headers)\n ? [...headers, ...Object.entries(extraHeaders ?? {})]\n : { ...headers, ...extraHeaders };\n\n return this.context.fetch(`${this.host}/invoke`, {\n method: \"POST\",\n headers: requestHeaders,\n body,\n signal,\n });\n }\n\n /**\n * Serialize a single put value into a fetch body.\n *\n * Binary values (Blob/ArrayBuffer/typed-array, incl. Node Buffer) are sent as\n * raw bytes (as a Blob) so they round-trip byte-identically — without this a\n * Buffer would be JSON.stringify'd into `{\"type\":\"Buffer\",\"data\":[...]}`.\n * Strings are returned unchanged (preserving prior behavior); other values are\n * JSON-encoded. `contentType` overrides the inferred type for binary values.\n */\n private serializePutValue(\n value: unknown,\n contentType?: string\n ): Blob | string {\n if (value instanceof Blob) {\n if (!contentType || value.type === contentType) {\n return value;\n }\n return new Blob([value], { type: contentType });\n }\n\n if (value instanceof ArrayBuffer) {\n return new Blob([value], {\n type: contentType ?? \"application/octet-stream\",\n });\n }\n\n if (ArrayBuffer.isView(value)) {\n // Pass a ranged view (honors byteOffset/byteLength so a Node Buffer backed\n // by a shared pool isn't over-read); Blob snapshots the bytes at\n // construction, so no defensive copy is needed.\n const view = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n return new Blob([view], {\n type: contentType ?? \"application/octet-stream\",\n });\n }\n\n if (typeof value === \"string\") {\n return contentType ? new Blob([value], { type: contentType }) : value;\n }\n\n return JSON.stringify(value);\n }\n\n private serializeBatchPutValue(item: KVBatchPutItem): Blob {\n const contentType = item.contentType;\n\n if (item.value instanceof Blob) {\n if (!contentType || item.value.type === contentType) {\n return item.value;\n }\n return new Blob([item.value], { type: contentType });\n }\n\n if (item.value instanceof ArrayBuffer) {\n return new Blob([item.value], {\n type: contentType ?? \"application/octet-stream\",\n });\n }\n\n if (ArrayBuffer.isView(item.value)) {\n const value = item.value;\n const bytes = new Uint8Array(value.byteLength);\n bytes.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));\n return new Blob([bytes], {\n type: contentType ?? \"application/octet-stream\",\n });\n }\n\n if (typeof item.value === \"string\") {\n return new Blob([item.value], {\n type: contentType ?? \"text/plain;charset=UTF-8\",\n });\n }\n\n const json = JSON.stringify(item.value);\n if (json === undefined) {\n throw new Error(`Cannot JSON serialize KV batch value for key \"${item.key}\"`);\n }\n\n return new Blob([json], {\n type: contentType ?? \"application/json\",\n });\n }\n\n private normalizeBatchPutResponse(data: unknown): KVBatchPutResponse | undefined {\n if (!data || typeof data !== \"object\") {\n return undefined;\n }\n\n const response = data as Partial<KVBatchPutResponse>;\n if (\n !Array.isArray(response.written) ||\n !response.written.every((key) => typeof key === \"string\") ||\n typeof response.count !== \"number\"\n ) {\n return undefined;\n }\n\n return {\n written: response.written,\n count: response.count,\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 private createBatchResponseHeaders(\n values: Record<string, string>\n ): KVResponseHeaders {\n const normalized = new Map(\n Object.entries(values).map(([name, value]) => [name.toLowerCase(), value])\n );\n return this.createResponseHeaders({\n get: (name) => normalized.get(name.toLowerCase()) ?? null,\n });\n }\n\n private parseBatchValue<T>(\n dataBase64: string,\n headers: Record<string, string>,\n raw: boolean = false,\n binary: boolean = false\n ): T {\n const encoded = globalThis.atob(dataBase64);\n const bytes = Uint8Array.from(encoded, (character) => character.charCodeAt(0));\n if (binary) return bytes as unknown as T;\n\n const text = new TextDecoder().decode(bytes);\n if (raw) return text as unknown as T;\n const contentType = Object.entries(headers).find(\n ([name]) => name.toLowerCase() === \"content-type\"\n )?.[1];\n return (contentType?.includes(\"application/json\")\n ? JSON.parse(text)\n : text) as T;\n }\n\n /**\n * Validate a batch read response and return its items in the caller's\n * request order. The node flattens invocation abilities from a `BTreeMap`,\n * so results arrive sorted by UTF-8 byte order of the path, not in request\n * order. Items that carry their path in `key` are matched by path; if no\n * item carries a key, items are assumed to be in the node's byte-sorted\n * order and the request is aligned the same way. Any mix of keyed and\n * keyless items, a missing requested path, an unrequested path, or a\n * malformed item fails closed (returns undefined).\n */\n private normalizeBatchReadResponse(\n data: unknown,\n paths: string[],\n requireData: boolean\n ): KvBatchNodeItem[] | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n const response = data as Partial<KvBatchNodeResponse>;\n const items = response.results;\n if (!Array.isArray(items) || items.length !== paths.length) {\n return undefined;\n }\n\n for (const item of items) {\n if (!item || typeof item !== \"object\" || typeof item.ok !== \"boolean\") {\n return undefined;\n }\n if (\n item.ok &&\n (!item.headers ||\n typeof item.headers !== \"object\" ||\n Object.values(item.headers).some((value) => typeof value !== \"string\") ||\n (requireData && typeof item.dataBase64 !== \"string\"))\n ) {\n return undefined;\n }\n if (\n !item.ok &&\n (!item.error ||\n typeof item.error.code !== \"string\" ||\n typeof item.error.message !== \"string\")\n ) {\n return undefined;\n }\n }\n\n if (items.every((item) => typeof item.key === \"string\")) {\n const byPath = new Map(items.map((item) => [item.key, item]));\n if (byPath.size !== items.length) return undefined;\n const aligned: KvBatchNodeItem[] = [];\n for (const path of paths) {\n const item = byPath.get(path);\n if (!item) return undefined;\n aligned.push(item);\n }\n return aligned;\n }\n\n if (items.some((item) => item.key !== undefined)) return undefined;\n\n // No keys carried: the node's order is byte-sorted by path, so align by\n // sorting the request the same way. Request paths are unique (duplicates\n // are rejected before the request is signed).\n const requestOrder = paths\n .map((_, index) => index)\n .sort((a, b) => comparePathsByBytes(paths[a]!, paths[b]!));\n const aligned = new Array<KvBatchNodeItem>(items.length);\n for (let position = 0; position < requestOrder.length; position++) {\n aligned[requestOrder[position]!] = items[position]!;\n }\n return aligned;\n }\n\n private async batchRead<T>(\n keys: string[],\n action: typeof KVAction.GET | typeof KVAction.HEAD,\n options?: KVGetOptions | KVHeadOptions\n ): Promise<Result<KVBatchReadResponse<T>>> {\n if (this.readThroughReady !== null) await this.readThroughReady;\n if (!this.requireAuth()) return err(authRequiredError(\"kv\"));\n if (keys.length === 0) return ok({ results: [], count: 0 });\n if (keys.length > MAX_KV_BATCH_READ_ITEMS) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n `KV batch reads accept at most ${MAX_KV_BATCH_READ_ITEMS} keys`,\n \"kv\"\n ));\n }\n if (keys.length === 1) {\n const key = keys[0]!;\n const result = action === KVAction.GET\n ? await this.get<T>(key, options as KVGetOptions)\n : await this.head(key, options as KVHeadOptions);\n return ok({\n results: [{ key, result: result as Result<KVResponse<T>> }],\n count: 1,\n });\n }\n if (!this.context.invokeAny) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV batch reads require SDK runtime support for multi-resource invocations\",\n \"kv\"\n ));\n }\n\n const getOptions = action === KVAction.GET ? options as KVGetOptions : undefined;\n if (\n getOptions?.maxResponseBytes !== undefined &&\n (!Number.isSafeInteger(getOptions.maxResponseBytes) ||\n getOptions.maxResponseBytes <= 0)\n ) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV maxResponseBytes must be a positive safe integer\",\n \"kv\"\n ));\n }\n\n const paths = keys.map((key) => this.getFullPath(key, options?.prefix));\n if (new Set(paths).size !== paths.length) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV batch read received duplicate keys after prefix resolution\",\n \"kv\"\n ));\n }\n\n const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const session = this.context.session!;\n const invocationHeaders = this.context.invokeAny(\n session,\n paths.map((path) => ({\n spaceId: session.spaceId,\n service: \"kv\",\n path,\n action,\n }))\n );\n const limitHeaders: Record<string, string> = {};\n if (getOptions?.maxResponseBytes !== undefined) {\n limitHeaders[\"x-tinycloud-max-response-bytes\"] = String(\n getOptions.maxResponseBytes\n );\n }\n const headers: ServiceHeaders = Array.isArray(invocationHeaders)\n ? [...invocationHeaders, ...Object.entries(limitHeaders)]\n : { ...invocationHeaders, ...limitHeaders };\n const response = await this.context.fetch(`${this.host}/invoke`, {\n method: \"POST\",\n headers,\n signal: request.signal,\n });\n\n if (!response.ok) {\n const errorText = response.status === 401 || response.status === 403\n ? await this.readAuthorizationText(response)\n : await response.text();\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to batch read ${keys.length} key(s)`,\n response,\n errorText,\n paths,\n action\n );\n }\n if (response.status === 413) {\n return err(serviceError(\n ErrorCodes.KV_RESPONSE_TOO_LARGE,\n \"A KV batch value exceeds the requested response limit\",\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n return err(serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to batch read ${keys.length} key(s): ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n\n const items = this.normalizeBatchReadResponse(\n await response.json(),\n paths,\n action === KVAction.GET\n );\n if (!items) {\n return err(serviceError(\n ErrorCodes.NETWORK_ERROR,\n \"KV batch read response did not match the requested keys\",\n \"kv\"\n ));\n }\n\n const results = items.map((item, index) => {\n if (!item.ok) {\n return {\n key: keys[index]!,\n result: err(serviceError(item.error!.code, item.error!.message, \"kv\")),\n };\n }\n const headers = item.headers!;\n const data = action === KVAction.HEAD\n ? undefined\n : this.parseBatchValue<T>(\n item.dataBase64!,\n headers,\n getOptions?.raw,\n getOptions?.binary\n );\n return {\n key: keys[index]!,\n result: ok({\n data: data as T,\n headers: this.createBatchResponseHeaders(headers),\n }),\n };\n });\n return ok({ results, count: results.length });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n } finally {\n request.dispose();\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 binary: boolean = false\n ): Promise<T | undefined> {\n if (!response.ok) {\n return undefined;\n }\n\n if (binary) {\n return new Uint8Array(await response.arrayBuffer()) as unknown as T;\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 path: 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 return this.authorizationFailure(\n `Failed to create signed read URL for key ${JSON.stringify(key)}`,\n response,\n errorText,\n [path],\n KVAction.GET\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 ${JSON.stringify(key)}: ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n /** A known 401/403 keeps its typed status even if reading its body fails. */\n private async readAuthorizationText(response: FetchResponse): Promise<string> {\n try {\n return await response.text();\n } catch {\n return \"\";\n }\n }\n\n /**\n * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP\n * status and the server text (`<context>: <status> - <text>`, the same\n * shape as every other KV failure) so callers that rethrow only the message\n * still see the status; `meta.status` carries it for typed callers.\n * Server hints must match the requested path/action (whether the node sends\n * a relative path or full resource); metadata uses only the request-derived\n * canonical resource under the authenticated session's space.\n */\n private authorizationFailure(\n context: string,\n response: FetchResponse,\n errorText: string,\n paths: readonly string[],\n expectedAction: string,\n extraMeta: Record<string, unknown> = {}\n ): Result<never> {\n const { resource, action } = parseAuthError(errorText);\n const requestedPath = paths.find((path) =>\n resource === path ||\n resource === `${this.context.session!.spaceId}/kv/${path}`\n );\n const capability = requestedPath !== undefined && action === expectedAction\n ? validatedCapabilityOf({\n service: \"kv\",\n code: ErrorCodes.AUTH_UNAUTHORIZED,\n meta: {\n status: response.status,\n resource: `${this.context.session!.spaceId}/kv/${requestedPath}`,\n requiredAction: action,\n },\n })\n : undefined;\n const detail = errorText.trim().length > 0\n ? errorText\n : response.statusText || \"authorization failed\";\n return err(authUnauthorizedError(\"kv\", `${context}: ${response.status} - ${detail}`, {\n status: response.status,\n ...extraMeta,\n ...capability,\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.readThroughReady !== null) await this.readThroughReady;\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n if (\n options?.maxResponseBytes !== undefined &&\n (!Number.isSafeInteger(options.maxResponseBytes) || options.maxResponseBytes <= 0)\n ) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV maxResponseBytes must be a positive safe integer\",\n \"kv\"\n ));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const network = () => this.getFromNetwork<T>(key, path, options, request.signal);\n if (options?.source === \"network\") {\n const observer = this.readThrough;\n if (observer === null) return await network();\n let outcome: \"found\" | \"not_found\" | \"error\" = \"error\";\n const startedAt = Date.now();\n try {\n const result = await network();\n outcome = result.ok ? \"found\" : result.error.code === ErrorCodes.KV_NOT_FOUND ? \"not_found\" : \"error\";\n return result;\n } finally {\n try {\n observer.observeNetworkRequested({\n op: \"get\",\n space: this.context.session!.spaceId,\n path,\n reason: \"NETWORK_REQUESTED\",\n outcome,\n latencyMs: Date.now() - startedAt,\n });\n } catch {\n // Observation is best-effort and must not affect the network read.\n }\n }\n\n }\n if (this.readThrough === null) return await network();\n return await this.readThrough.get({\n space: this.context.session!.spaceId,\n key,\n path,\n options,\n signal: request.signal,\n network,\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n } finally {\n request.dispose();\n }\n });\n }\n private async getFromNetwork<T>(\n key: string,\n path: string,\n options: KVGetOptions | undefined,\n signal: AbortSignal\n ): Promise<Result<KVResponse<T>>> {\n const response = await this.invokeOperation(\n path,\n KVAction.GET,\n undefined,\n signal,\n options?.maxResponseBytes === undefined\n ? undefined\n : { \"x-tinycloud-max-response-bytes\": String(options.maxResponseBytes) }\n );\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n const errorText = await this.readAuthorizationText(response);\n const permissionHint = parsePermissionHintFromErrorText(errorText);\n const structuredCapability = permissionHint?.service === \"tinycloud.kv\" &&\n permissionHint.space === this.context.session!.spaceId &&\n permissionHint.path === path\n ? validatedCapabilityOf({\n service: \"kv\",\n code: ErrorCodes.AUTH_UNAUTHORIZED,\n meta: {\n status: response.status,\n resource: `${this.context.session!.spaceId}/kv/${path}`,\n requiredAction: KVAction.GET,\n },\n })\n : undefined;\n return this.authorizationFailure(\n `Failed to get key ${JSON.stringify(key)}`,\n response,\n errorText,\n [path],\n KVAction.GET,\n structuredCapability ? { permissionHint, ...structuredCapability } : {}\n );\n }\n if (response.status === 404) return this.classifyNotFound(response, key);\n const errorText = await response.text();\n if (response.status === 413) {\n return err(serviceError(\n ErrorCodes.KV_RESPONSE_TOO_LARGE,\n `KV value at key ${JSON.stringify(key)} exceeds the requested response limit`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n const message = `Failed to get key ${JSON.stringify(key)}: ${response.status} - ${errorText}`;\n const retryError = nodeRetryError(\"kv\", message, response, errorText);\n if (retryError) return err(retryError);\n return err(serviceError(\n ErrorCodes.NETWORK_ERROR,\n message,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n const data = await this.parseResponse<T>(response, options?.raw, options?.binary);\n return ok({ data: data as T, headers: this.createResponseHeaders(response.headers) });\n }\n\n async batchGet<T = unknown>(\n keys: string[],\n options?: KVGetOptions\n ): Promise<Result<KVBatchReadResponse<T>>> {\n return this.withTelemetry(\"batchGet\", String(keys.length), () =>\n this.batchRead<T>(keys, KVAction.GET, options)\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.readThroughReady !== null) await this.readThroughReady;\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n if (options?.ifMatch !== undefined && options.ifNoneMatch !== undefined) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV put cannot combine ifMatch and ifNoneMatch\",\n \"kv\"\n ));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n // Serialize the value. Binary values (Blob/ArrayBuffer/typed-array/Buffer)\n // are sent as raw bytes so they round-trip byte-identically; strings are\n // sent as-is; everything else is JSON. Mirrors serializeBatchPutValue.\n const body = this.serializePutValue(value, options?.contentType);\n\n const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const network = () => this.putToNetwork(key, path, body, options, request.signal);\n if (this.readThrough === null) return await network();\n return await this.readThrough.write({\n op: \"put\",\n space: this.context.session!.spaceId,\n entries: [{ path, body }],\n signal: request.signal,\n conditional: options?.ifMatch !== undefined || options?.ifNoneMatch !== undefined,\n network,\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n } finally {\n request.dispose();\n }\n });\n }\n private async putToNetwork(\n key: string,\n path: string,\n body: Blob | string,\n options: KVPutOptions | undefined,\n signal: AbortSignal\n ): Promise<Result<KVResponse<void>>> {\n const response = await this.invokeOperation(\n path,\n KVAction.PUT,\n body,\n signal,\n {\n ...(options?.ifMatch === undefined ? {} : { \"if-match\": options.ifMatch }),\n ...(options?.ifNoneMatch === undefined ? {} : { \"if-none-match\": options.ifNoneMatch }),\n }\n );\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to put key ${JSON.stringify(key)}`,\n response,\n await this.readAuthorizationText(response),\n [path],\n KVAction.PUT\n );\n }\n if (!response.ok) {\n const errorText = await response.text();\n if (response.status === 412) {\n return err(serviceError(\n ErrorCodes.KV_PRECONDITION_FAILED,\n `KV precondition failed for key ${JSON.stringify(key)}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n // A 503 is never a precondition failure (that is the 412 above): it is\n // the node's busy/timeout signal, or an unclassified outage.\n const message = `Failed to put key ${JSON.stringify(key)}: ${response.status} - ${errorText}`;\n const retryError = nodeRetryError(\"kv\", message, response, errorText);\n if (retryError) return err(retryError);\n const quotaError = this.handleQuotaErrorResponse(response, errorText, key);\n if (quotaError) return quotaError;\n return err(serviceError(\n ErrorCodes.KV_WRITE_FAILED,\n message,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n return ok({\n data: undefined as void,\n headers: this.createResponseHeaders(response.headers),\n });\n }\n\n /**\n * Store multiple values in one TinyCloud KV invocation.\n */\n async batchPut(\n items: KVBatchPutItem[],\n options?: KVBatchPutOptions\n ): Promise<Result<KVBatchPutResponse>> {\n return this.withTelemetry(\"batchPut\", String(items.length), async () => {\n if (this.readThroughReady !== null) await this.readThroughReady;\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n if (items.length === 0) {\n return ok({ written: [], count: 0 });\n }\n\n if (!this.context.invokeAny) {\n return err(\n serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV batchPut requires SDK runtime support for multi-resource invocations\",\n \"kv\"\n )\n );\n }\n\n const session = this.context.session!;\n const paths = items.map((item) => this.getFullPath(item.key, options?.prefix));\n const seen = new Set<string>();\n for (const path of paths) {\n if (seen.has(path)) {\n return err(\n serviceError(\n ErrorCodes.INVALID_INPUT,\n `KV batchPut received duplicate key after prefix resolution: ${path}`,\n \"kv\"\n )\n );\n }\n seen.add(path);\n }\n\n const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const network = () => this.batchPutToNetwork(items, paths, request.signal);\n if (this.readThrough === null) return await network();\n const entries = items.map((item, index) => ({\n path: paths[index]!,\n body: this.serializeBatchPutValue(item),\n }));\n return await this.readThrough.write({\n op: \"batchPut\",\n space: session.spaceId,\n entries,\n signal: request.signal,\n network: () => this.batchPutToNetwork(items, paths, request.signal, entries),\n });\n } catch (error) {\n const wrapped = wrapError(\"kv\", error);\n return err({ ...wrapped, meta: { ...wrapped.meta, requestMayHaveDispatched: false } });\n } finally {\n request.dispose();\n }\n });\n }\n private async batchPutToNetwork(\n items: KVBatchPutItem[],\n paths: string[],\n signal: AbortSignal,\n entries?: Array<{ path: string; body: Blob }>\n ): Promise<Result<KVBatchPutResponse>> {\n const session = this.context.session!;\n // Conservative: `true` means we handed a fully-constructed request to\n // fetch(). It does NOT prove bytes reached the node — DNS/connect\n // failures and synchronous fetch rejections are included. Proving\n // actual dispatch requires lower-level transport instrumentation (out\n // of scope). The over-approximation is intentional and bounded: it can\n // only cause a reconciliation of at most N idempotent, byte-identical\n // overwrites — never a corrupted or differently-timestamped record.\n let requestMayHaveDispatched = false;\n try {\n const body = new FormData();\n for (let index = 0; index < items.length; index++) {\n body.append(\n encodeKvBatchPartName(paths[index]!),\n entries?.[index]?.body ?? this.serializeBatchPutValue(items[index]!)\n );\n }\n\n const headers = this.context.invokeAny!(\n session,\n paths.map((path) => ({\n spaceId: session.spaceId,\n service: \"kv\",\n path,\n action: KVAction.PUT,\n }))\n );\n\n const url = `${this.host}/invoke`;\n const init = {\n method: \"POST\",\n headers,\n body,\n signal,\n };\n\n // Resolve the fetch function to a local BEFORE flipping the flag.\n // `this.context.fetch` is a getter (context.ts:154-156) that can\n // itself throw (assertActive()) — if the flag were set first, that\n // throw would be mislabeled as \"may have dispatched\" even though no\n // request was ever handed to a transport (Sol B2).\n const fetchFn = this.context.fetch;\n requestMayHaveDispatched = true;\n const response = await fetchFn(url, init);\n\n if (!response.ok) {\n let errorText: string;\n try {\n errorText = await response.text();\n } catch (textError) {\n // A response was received — the status is known and\n // authoritative — but its body could not be read. Falling\n // through to the generic catch below would report\n // NETWORK_ERROR + requestMayHaveDispatched: true, which the\n // allow-list treats as ambiguous regardless of status. That\n // would turn a deterministic 4xx (nothing written) into a false\n // ambiguity (Sol B4). Classify by status alone instead; whether\n // this is later reconciled is still governed solely by\n // AMBIGUOUS_WRITE_STATUSES, exactly like the body-readable path.\n //\n // Sol B2 (round 2): preserve the underlying rejection instead of\n // discarding it — a bare `catch {}` here would make a\n // body-read failure silently invisible, which is exactly the\n // debugging trap the no-swallowed-errors rule exists to\n // prevent.\n const cause = textError instanceof Error ? textError : new Error(String(textError));\n return err(\n serviceError(\n ErrorCodes.KV_WRITE_FAILED,\n `Failed to batch put ${items.length} key(s): ${response.status} - <response body could not be read: ${cause.message}>`,\n \"kv\",\n { cause, meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to batch put ${items.length} key(s)`,\n response,\n errorText,\n paths,\n KVAction.PUT\n );\n }\n\n const quotaError = this.handleQuotaErrorResponse(\n response,\n errorText,\n \"batch\"\n );\n if (quotaError) {\n return quotaError;\n }\n\n return err(\n serviceError(\n ErrorCodes.KV_WRITE_FAILED,\n `Failed to batch put ${items.length} key(s): ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n let rawBody: unknown;\n try {\n rawBody = await response.json();\n } catch (jsonError) {\n // A 2xx response whose body isn't valid JSON is precisely the\n // unconfirmed-2xx case — the node answered success but the write\n // set can't be confirmed. Falling through to the generic catch\n // below would carry only `requestMayHaveDispatched`, omitting\n // `responseReceived`/`status`/`outcome`, so this must be classified\n // here with the identical metadata block used by the two sibling\n // unconfirmed branches (Sol B3).\n //\n // Sol B2 (round 2): preserve the underlying rejection instead of\n // discarding it — a bare `catch {}` here would make a\n // JSON-parse failure silently invisible, which is exactly the\n // debugging trap the no-swallowed-errors rule exists to\n // prevent.\n const cause = jsonError instanceof Error ? jsonError : new Error(String(jsonError));\n // The configured timeout can also elapse while the 2xx body is\n // still streaming: report TIMEOUT, with the same unconfirmed meta.\n const timedOut = isRequestTimeoutError(cause);\n return err(\n serviceError(\n timedOut ? ErrorCodes.TIMEOUT : ErrorCodes.NETWORK_ERROR,\n timedOut\n ? `KV batchPut response body did not arrive in time: ${cause.message}`\n : `KV batchPut response was not valid JSON: ${cause.message}`,\n \"kv\",\n {\n cause,\n meta: {\n ...(timedOut ? { timeoutMs: cause.timeoutMs } : {}),\n requestMayHaveDispatched: true,\n responseReceived: true,\n status: response.status,\n outcome: \"batch-unconfirmed\",\n },\n }\n )\n );\n }\n\n const batchResponse = this.normalizeBatchPutResponse(rawBody);\n if (!batchResponse) {\n // The code stays NETWORK_ERROR deliberately: adding a member to\n // ErrorCodes (types.ts:73-115) widens the exported ErrorCode union\n // (:117) and would break consumers with exhaustive switches. The\n // node answered 2xx (the write very likely landed) but the body\n // could not confirm it, so the ambiguity is carried in metadata\n // instead of a dedicated code.\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n \"KV batchPut response did not include matching written keys and count\",\n \"kv\",\n {\n meta: {\n requestMayHaveDispatched: true,\n responseReceived: true,\n status: response.status,\n outcome: \"batch-unconfirmed\",\n },\n }\n )\n );\n }\n\n // Validate against the exact requested path set, not just internal\n // self-consistency (count === written.length would accept a\n // malformed response that reports the right count for the wrong\n // keys, e.g. a partial write padded to look complete).\n const requestedPaths = new Set(paths);\n const writtenPaths = new Set(batchResponse.written);\n const matchesRequest =\n batchResponse.count === paths.length &&\n batchResponse.written.length === paths.length &&\n writtenPaths.size === paths.length &&\n batchResponse.written.every((key) => requestedPaths.has(key));\n\n if (!matchesRequest) {\n // Same rationale as above: NETWORK_ERROR is kept and the\n // ambiguity is carried in meta rather than a new ErrorCodes member.\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `KV batchPut response did not confirm all ${paths.length} requested key(s) were written`,\n \"kv\",\n {\n meta: {\n requestMayHaveDispatched: true,\n responseReceived: true,\n status: response.status,\n outcome: \"batch-unconfirmed\",\n },\n }\n )\n );\n }\n\n return ok(batchResponse);\n } catch (error) {\n const wrapped = wrapError(\"kv\", error);\n return err({\n ...wrapped,\n meta: { ...wrapped.meta, requestMayHaveDispatched },\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.readThroughReady !== null) await this.readThroughReady;\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n if (\n options?.limit !== undefined &&\n (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 1000)\n ) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV list limit must be an integer from 1 through 1000\",\n \"kv\"\n ));\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 const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const network = () => this.listFromNetwork(listPath, options, request.signal);\n let page: Result<KVListPage>;\n if (options?.source === \"network\") {\n const observer = this.readThrough;\n if (observer === null) {\n page = await network();\n } else {\n let outcome: \"found\" | \"not_found\" | \"error\" = \"error\";\n const startedAt = Date.now();\n try {\n page = await network();\n outcome = page.ok ? \"found\" : \"error\";\n } finally {\n try {\n observer.observeNetworkRequested({\n op: \"list\",\n space: this.context.session!.spaceId,\n path: listPath,\n reason: \"NETWORK_REQUESTED\",\n outcome,\n latencyMs: Date.now() - startedAt,\n });\n } catch {\n // Observation is best-effort and must not affect the network read.\n }\n }\n }\n } else if (this.readThrough === null) {\n page = await network();\n } else {\n page = await this.readThrough.list({\n space: this.context.session!.spaceId,\n listPath,\n options,\n signal: request.signal,\n network,\n });\n }\n return page.ok\n ? this.finishList(page.data, listPath, options?.removePrefix)\n : page;\n } catch (error) {\n return err(wrapError(\"kv\", error));\n } finally {\n request.dispose();\n }\n });\n }\n private async listFromNetwork(\n listPath: string,\n options: KVListOptions | undefined,\n signal: AbortSignal\n ): Promise<Result<KVListPage>> {\n const response = await this.invokeOperation(\n listPath,\n KVAction.LIST,\n undefined,\n signal,\n options?.limit === undefined && options?.cursor === undefined\n ? undefined\n : {\n ...(options?.limit === undefined ? {} : { \"x-tinycloud-limit\": String(options.limit) }),\n ...(options?.cursor === undefined ? {} : { \"x-tinycloud-cursor\": options.cursor }),\n }\n );\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n \"Failed to list keys\",\n response,\n await this.readAuthorizationText(response),\n [listPath],\n KVAction.LIST\n );\n }\n const errorText = await response.text();\n const message = `Failed to list keys: ${response.status} - ${errorText}`;\n const retryError = nodeRetryError(\"kv\", message, response, errorText);\n if (retryError) return err(retryError);\n return err(serviceError(\n ErrorCodes.NETWORK_ERROR,\n message,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n const keys = (await this.parseResponse<string[]>(response, options?.raw)) ?? [];\n return ok({\n keys,\n ...(response.headers.get(\"x-tinycloud-truncated\") === null\n ? {}\n : { truncated: response.headers.get(\"x-tinycloud-truncated\") === \"true\" }),\n ...(response.headers.get(\"x-tinycloud-next-cursor\") === null\n ? {}\n : { nextCursor: response.headers.get(\"x-tinycloud-next-cursor\") ?? undefined }),\n });\n }\n\n private finishList(\n page: KVListPage,\n listPath: string,\n removePrefix: boolean | undefined\n ): Result<KVListResponse> {\n let keys = page.keys;\n if (removePrefix && listPath) {\n const prefixWithSlash = listPath.endsWith(\"/\") ? listPath : `${listPath}/`;\n keys = keys.map((key) =>\n key.startsWith(prefixWithSlash) ? key.slice(prefixWithSlash.length) : key\n );\n }\n return ok({ keys, ...(page.truncated === undefined ? {} : { truncated: page.truncated }),\n ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }) });\n }\n\n /**\n * Read one page of the `tinycloud.kv/sync` change feed for a prefix.\n */\n async changes(options: KVChangesOptions): Promise<Result<KVChangesResponse>> {\n return this.withTelemetry(\"changes\", options?.prefix, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n // Plain-JS callers can omit the options; TypeScript requires them.\n if (typeof options?.prefix !== \"string\" || options.prefix.length === 0) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV changes prefix must be a non-empty string\",\n \"kv\"\n ));\n }\n if (\n options.limit !== undefined &&\n (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 1000)\n ) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV changes limit must be an integer from 1 through 1000\",\n \"kv\"\n ));\n }\n\n const requestPrefix = options.prefix === \"/\" ? \"\" : options.prefix;\n const headers: Record<string, string> = {};\n if (options.limit !== undefined) headers[\"x-tinycloud-limit\"] = String(options.limit);\n if (options.cursor !== undefined) headers[\"x-tinycloud-cursor\"] = options.cursor;\n if (options.retentionGrant !== undefined) {\n headers[\"x-tinycloud-retention-grant\"] = options.retentionGrant;\n }\n\n const request = this.createRequestSignal(options.signal, options.timeout);\n try {\n const response = await this.invokeOperation(\n requestPrefix,\n KVAction.SYNC,\n undefined,\n request.signal,\n headers\n );\n\n if (!response.ok) {\n // Awaited so the timeout and abort listeners stay live while the\n // error body is read; `finally` disposes them only afterwards.\n return await this.changesFailure(response, options.prefix, request.signal);\n }\n\n const text = await response.text();\n let body: unknown;\n try {\n body = JSON.parse(text);\n } catch {\n body = undefined;\n }\n const page = this.normalizeChangesResponse(body, options.prefix);\n if (!page) {\n return err(serviceError(\n ErrorCodes.NETWORK_ERROR,\n \"KV changes response was not a tinycloud.kv/sync page\",\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n return ok(page);\n } catch (error) {\n return err(wrapError(\"kv\", error));\n } finally {\n request.dispose();\n }\n });\n }\n\n /**\n * Map a failed `kv/sync` response. 410 and the retention 403 carry\n * `{\"error\":{\"code\",\"reason\"}}`; a revoked grant is a 401 whose whole body\n * is the node's revocation error (see REVOKED_BODY). A body read that\n * fails because the request was cancelled or timed out rethrows, so the\n * caller sees `ABORTED`/`TIMEOUT` rather than a mapped status.\n */\n private async changesFailure(\n response: FetchResponse,\n prefix: string,\n signal: AbortSignal\n ): Promise<Result<never>> {\n if (response.status === 404) {\n return this.classifyNotFound(response, prefix);\n }\n let errorText: string;\n try {\n errorText = await response.text();\n } catch (error) {\n if (signal.aborted) throw error;\n errorText = \"\";\n }\n const meta = { status: response.status, statusText: response.statusText };\n let reason: string | undefined;\n let bodyCode: string | undefined;\n try {\n const body: unknown = JSON.parse(errorText);\n const error = isJsonObject(body) ? body.error : undefined;\n if (isJsonObject(error)) {\n if (typeof error.code === \"string\") bodyCode = error.code;\n if (typeof error.reason === \"string\") reason = error.reason;\n }\n } catch {\n // Plain-text body.\n }\n const context = `Failed to read KV changes for ${JSON.stringify(prefix)}`;\n\n if (response.status === 410) {\n return err(serviceError(\n ErrorCodes.KV_SYNC_RESET_REQUIRED,\n `${context}: the node requires a reset (${reason ?? \"reason not given\"}); discard sync state and restart without a cursor`,\n \"kv\",\n { meta: { ...meta, reason } }\n ));\n }\n if (response.status === 403 && bodyCode === \"RETENTION_GRANT_REFUSED\") {\n return err(serviceError(\n ErrorCodes.KV_RETENTION_GRANT_REFUSED,\n `${context}: the retention grant was refused (${reason ?? \"reason not given\"})`,\n \"kv\",\n { meta: { ...meta, reason } }\n ));\n }\n if (response.status === 401) {\n const revoked = revocationCodeOf(errorText);\n if (revoked !== undefined) {\n return err(serviceError(revoked, `${context}: 401 - ${errorText}`, \"kv\", { meta }));\n }\n }\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(context, response, errorText, [prefix], KVAction.SYNC);\n }\n return err(serviceError(\n response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR,\n `${context}: ${response.status} - ${errorText}`,\n \"kv\",\n { meta }\n ));\n }\n\n private normalizeChangesResponse(data: unknown, requestedPrefix: string): KVChangesResponse | undefined {\n if (!isJsonObject(data)) return undefined;\n const { changes, more, cursor, source, authority } = data;\n if (\n !Array.isArray(changes) ||\n typeof more !== \"boolean\" ||\n typeof cursor !== \"string\" ||\n !isJsonObject(source) ||\n typeof source.nodeDid !== \"string\" ||\n typeof source.space !== \"string\" ||\n typeof source.prefix !== \"string\" ||\n !isJsonObject(authority)\n ) {\n return undefined;\n }\n const { notBefore, expiresAt, retainUntil } = authority;\n if (\n (notBefore !== null && typeof notBefore !== \"string\") ||\n (expiresAt !== null && typeof expiresAt !== \"string\") ||\n (retainUntil !== null && typeof retainUntil !== \"string\")\n ) {\n return undefined;\n }\n\n const parsed: KVChange[] = [];\n for (const change of changes) {\n if (!isJsonObject(change) || typeof change.key !== \"string\") return undefined;\n if (change.deleted === true) {\n parsed.push({ key: change.key, deleted: true });\n continue;\n }\n const { metadata } = change;\n if (\n change.deleted !== false ||\n typeof change.etag !== \"string\" ||\n !isJsonObject(metadata) ||\n !Object.values(metadata).every((value) => typeof value === \"string\")\n ) {\n return undefined;\n }\n parsed.push({\n key: change.key,\n deleted: false,\n etag: change.etag,\n metadata: metadata as Record<string, string>,\n });\n }\n\n return {\n changes: parsed,\n more,\n cursor,\n source: {\n nodeDid: source.nodeDid,\n space: source.space,\n prefix: requestedPrefix === \"/\" && source.prefix === \"\" ? \"/\" : source.prefix,\n },\n authority: { notBefore, expiresAt, retainUntil },\n };\n }\n\n /**\n * Delete a key.\n */\n async delete(\n key: string,\n options?: KVDeleteOptions\n ): Promise<Result<KVResponse<void>>> {\n return this.withTelemetry(\"delete\", key, async () => {\n if (this.readThroughReady !== null) await this.readThroughReady;\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const network = () => this.deleteFromNetwork(key, path, options, request.signal);\n if (this.readThrough === null) return await network();\n return await this.readThrough.write({\n op: \"delete\",\n space: this.context.session!.spaceId,\n entries: [{ path }],\n signal: request.signal,\n conditional: options?.ifMatch !== undefined,\n network,\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n } finally {\n request.dispose();\n }\n });\n }\n private async deleteFromNetwork(\n key: string,\n path: string,\n options: KVDeleteOptions | undefined,\n signal: AbortSignal\n ): Promise<Result<KVResponse<void>>> {\n const response = await this.invokeOperation(\n path,\n KVAction.DELETE,\n undefined,\n signal,\n options?.ifMatch === undefined ? undefined : { \"if-match\": options.ifMatch }\n );\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to delete key ${JSON.stringify(key)}`,\n response,\n await this.readAuthorizationText(response),\n [path],\n KVAction.DELETE\n );\n }\n if (response.status === 404) return this.classifyNotFound(response, key);\n const errorText = await response.text();\n if (response.status === 412) {\n return err(serviceError(\n ErrorCodes.KV_PRECONDITION_FAILED,\n `KV precondition failed for key ${JSON.stringify(key)}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n const message = `Failed to delete key ${JSON.stringify(key)}: ${response.status} - ${errorText}`;\n const retryError = nodeRetryError(\"kv\", message, response, errorText);\n if (retryError) return err(retryError);\n return err(serviceError(\n ErrorCodes.NETWORK_ERROR,\n message,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n return ok({\n data: undefined as void,\n headers: this.createResponseHeaders(response.headers),\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 const request = this.createRequestSignal(options?.signal, options?.timeout);\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.HEAD,\n undefined,\n request.signal\n );\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to get metadata for key ${JSON.stringify(key)}`,\n response,\n await this.readAuthorizationText(response),\n [path],\n KVAction.HEAD\n );\n }\n\n if (response.status === 404) {\n return await this.classifyNotFound(response, key);\n }\n\n const errorText = await response.text();\n const message = `Failed to get metadata for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`;\n const retryError = nodeRetryError(\"kv\", message, response, errorText);\n if (retryError) return err(retryError);\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n message,\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 } finally {\n request.dispose();\n }\n });\n }\n\n async batchHead(\n keys: string[],\n options?: KVHeadOptions\n ): Promise<Result<KVBatchReadResponse<void>>> {\n return this.withTelemetry(\"batchHead\", String(keys.length), () =>\n this.batchRead<void>(keys, KVAction.HEAD, options)\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 const request = this.createRequestSignal(options?.signal, options?.timeout);\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: request.signal,\n });\n\n if (!response.ok) {\n return await this.createSignedReadUrlError(response, key, path);\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 } finally {\n request.dispose();\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","export const CLEAR_PENDING_WARNING =\n \"Clearing pending writes surrenders read-your-writes protection for the cleared keys. If a write whose record was cleared had an unknown outcome or never finished, it may commit now or later; this device can serve an older value until the next sync after that commit, as it would for a write from another device.\";\n","import type { ReplicationIdentity } from \"./types\";\n\nconst SPACE_ID = /^(tinycloud:pkh:eip155:\\d+:)([^:]+)(:.+)$/;\nconst PRINCIPAL_DID = /^(did:pkh:eip155:\\d+:)([^:]+)$/;\n\n/**\n * Pure canonicalization: lower-cases only EIP-155 address segments and preserves all other\n * space/principal bytes. Rejects non-HTTP(S) hosts, credentials, query/fragment, and non-full\n * TinyCloud space ids or non-pkh principals.\n */\nexport function canonicalReplicationIdentity(\n raw: ReplicationIdentity,\n): ReplicationIdentity {\n if (\n !raw ||\n typeof raw.host !== \"string\" ||\n typeof raw.space !== \"string\" ||\n typeof raw.principal !== \"string\"\n ) {\n throw new TypeError(\n \"Replication identity host, space, and principal must be strings\",\n );\n }\n const authority = /^https?:\\/\\/([^/?#]*)/i.exec(raw.host)?.[1];\n let url: URL;\n try {\n url = new URL(raw.host);\n } catch {\n throw new TypeError(\n \"Replication identity host must be an absolute HTTP(S) URL\",\n );\n }\n if (\n !authority ||\n authority.includes(\"@\") ||\n raw.host.includes(\"?\") ||\n raw.host.includes(\"#\") ||\n url.username ||\n url.password\n ) {\n throw new TypeError(\n \"Replication identity host must be HTTP(S) without credentials, query, or fragment\",\n );\n }\n const space = SPACE_ID.exec(raw.space);\n const principal = PRINCIPAL_DID.exec(raw.principal);\n if (!space || !principal) {\n throw new TypeError(\n \"Replication identity requires a full TinyCloud space id and a pkh principal DID\",\n );\n }\n return {\n host: `${url.origin}${url.pathname.replace(/\\/+$/, \"\")}`,\n space: `${space[1]}${space[2]!.toLowerCase()}${space[3]}`,\n principal: `${principal[1]}${principal[2]!.toLowerCase()}`,\n };\n}\n\n/** Stable in-memory key; durable adapters hash its UTF-8 bytes with their platform crypto. */\nexport function replicationIdentityKey(id: ReplicationIdentity): string {\n const canonical = canonicalReplicationIdentity(id);\n return `tc-replication/v1\\n${canonical.host}\\n${canonical.space}\\n${canonical.principal}`;\n}\n","/** Shared prefix boundary and configuration rules; no runtime dependencies. */\nexport function kvPrefixCovers(prefix: string, key: string): boolean {\n return prefix === \"/\" || key.startsWith(prefix);\n}\n\n/** Replication scopes are `/` or non-empty, slash-terminated folder paths. */\nexport function assertFolderPrefix(prefix: unknown): asserts prefix is string {\n const validFolder = typeof prefix === \"string\" &&\n prefix.length > 0 &&\n !prefix.startsWith(\"/\") &&\n !prefix.includes(\"//\") &&\n prefix.endsWith(\"/\");\n if (prefix !== \"/\" && !validFolder) {\n const folderHint = typeof prefix === \"string\"\n ? prefix.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\")\n : \"\";\n const suggestion = folderHint.length > 0 && !folderHint.includes(\"//\")\n ? ` Use ${JSON.stringify(`${folderHint}/`)}.`\n : ' Use \"/\" or a slash-terminated folder path such as \"notes/\".';\n throw new TypeError(`Replication prefix must be \"/\" or a non-empty slash-terminated folder path without empty segments.${suggestion}`);\n }\n}\n","export { kvPrefixCovers } from \"./prefix\";\n\n/** LIST returns its exact path plus descendants below its slash-terminated path. */\nexport function listRangeCovers(path: string, key: string): boolean {\n return path === \"\" || key === path || key.startsWith(path.endsWith(\"/\") ? path : `${path}/`);\n}\n\n/** Whether replicating this space/prefix can copy encrypted secret material to disk. */\nexport function requiresSecretsOptIn(space: string, prefix: string): boolean {\n if (prefix === \"/\") return true;\n if (space === \"secrets\" || space.endsWith(\":secrets\")) return true;\n return prefix.split(\"/\", 1)[0] === \"vault\";\n}\n"],"mappings":"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","names":["ok","serviceError","aligned","headers","errorText"]}