lazypock 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +226 -0
- package/dist/index.cjs +971 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +520 -0
- package/dist/index.d.ts +520 -0
- package/dist/index.global.js +963 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +938 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -0
- package/src/auth.ts +185 -0
- package/src/collection.ts +185 -0
- package/src/files.ts +96 -0
- package/src/http.ts +195 -0
- package/src/index.ts +423 -0
- package/src/realtime.ts +264 -0
- package/src/types.ts +46 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/http.ts","../src/auth.ts","../src/collection.ts","../src/realtime.ts","../src/files.ts"],"sourcesContent":["// ── Lazypock SDK — Root Client ─────────────────────────\n// Usage:\n// const client = new LazypockClient({ baseUrl: 'http://localhost:4000/api' });\n// await client.authStore.init();\n// await client.login('admin@example.com', 'password');\n// const records = await client.collection('articles').list();\n\nimport { HttpClient } from \"./http\";\nimport {\n\tAuthStore,\n\tmemoryStorage,\n\ttype StorageAdapter,\n\ttype AuthModel,\n} from \"./auth\";\nimport { CollectionService } from \"./collection\";\nimport {\n\tApiError,\n\ttype ApiRecord,\n\ttype ListResult,\n\ttype RequestOptions,\n} from \"./types\";\nimport { RealtimeService, wsUrlFromBaseUrl } from \"./realtime\";\nimport { FilesService, getFileUrl, type FileRecord } from \"./files\";\n\nexport {\n\tAuthStore,\n\tApiError,\n\tRealtimeService,\n\twsUrlFromBaseUrl,\n\tFilesService,\n\tgetFileUrl,\n};\nexport type {\n\tStorageAdapter,\n\tAuthModel,\n\tApiRecord,\n\tListResult,\n\tRequestOptions,\n\tFileRecord,\n};\n\n/** Options for constructing a {@link LazypockClient}. */\nexport interface LazypockClientOptions {\n\t/** API base URL (e.g. 'http://localhost:4000/api') */\n\tbaseUrl: string;\n\t/** Custom storage adapter (default: localStorage fallback) */\n\tstorage?: StorageAdapter;\n\t/** Explicit auth store instance (for sharing across modules) */\n\tauthStore?: AuthStore;\n\t/** Real-time service for Phoenix Channel WebSocket subscriptions */\n\trealtime?: RealtimeService;\n}\n\n/**\n * Lazypock API client.\n *\n * Provides methods for authentication, CRUD operations on dynamic collections,\n * file management, and real-time subscriptions.\n *\n * @example\n * ```ts\n * const client = new LazypockClient({ baseUrl: 'http://localhost:4000/api' });\n * await client.login('admin@example.com', 'password');\n * const posts = await client.collection('posts').list();\n * ```\n */\nexport class LazypockClient {\n\treadonly http: HttpClient;\n\treadonly authStore: AuthStore;\n\treadonly realtime: RealtimeService;\n\treadonly files: FilesService;\n\tprivate collectionCache = new Map<string, CollectionService>();\n\n\t/**\n\t * Create a new Lazypock client.\n\t * @param options Configuration options.\n\t */\n\tconstructor(options: LazypockClientOptions) {\n\t\tconst baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n\t\tthis.authStore =\n\t\t\toptions.authStore ?? new AuthStore(options.storage ?? memoryStorage);\n\t\tthis.http = new HttpClient(baseUrl, this.authStore);\n\t\tthis.realtime = options.realtime ?? new RealtimeService();\n\t\tthis.files = new FilesService(this.http);\n\t}\n\n\t/**\n\t * Get or create a typed service for the given collection.\n\t * Services are cached after first access.\n\t *\n\t * @param name The collection name.\n\t * @returns A {@link CollectionService} instance.\n\t */\n\tcollection(name: string): CollectionService {\n\t\tlet svc = this.collectionCache.get(name);\n\t\tif (!svc) {\n\t\t\tsvc = new CollectionService(this.http, name, this.authStore);\n\t\t\tthis.collectionCache.set(name, svc);\n\t\t}\n\t\treturn svc as unknown as CollectionService;\n\t}\n\n\t// ── Auth ──\n\n\t/** Check whether any superuser exists (for login vs setup screen routing). */\n\tasync checkSuperuser(): Promise<{ has_superuser: boolean } | null> {\n\t\treturn this.http.get<{ has_superuser: boolean }>(\"/superusers/check\");\n\t}\n\n\t/**\n\t * Create the initial superuser account.\n\t * Only works when no superuser exists yet.\n\t * Stores the returned token in the auth store.\n\t * @param email Superuser email.\n\t * @param password Superuser password (min 8 chars).\n\t */\n\tasync setup(\n\t\temail: string,\n\t\tpassword: string,\n\t): Promise<({ token: string } & Record<string, unknown>) | null> {\n\t\tconst data = await this.http.post<\n\t\t\t{ token: string } & Record<string, unknown>\n\t\t>(\"/superusers/setup\", { email, password });\n\t\tif (data) {\n\t\t\tthis.authStore.setCollectionName(null);\n\t\t\tthis.authStore.set(data.token, null);\n\t\t}\n\t\treturn data;\n\t}\n\n\t/**\n\t * Authenticate as a superuser or auth collection user.\n\t *\n\t * When `collection` is provided, authenticates against\n\t * `/{collection}/auth-with-password`. Otherwise logs in as superuser.\n\t * Stores the returned token in the auth store.\n\t *\n\t * @param email User email or identity.\n\t * @param password User password.\n\t * @param collection Optional auth collection name.\n\t */\n\tasync login(\n\t\temail: string,\n\t\tpassword: string,\n\t\tcollection?: string,\n\t): Promise<({ token: string } & Record<string, unknown>) | null> {\n\t\tlet data;\n\t\tif (collection) {\n\t\t\tdata = await this.http.post<\n\t\t\t\t{ token: string; record: Record<string, unknown> } & Record<\n\t\t\t\t\tstring,\n\t\t\t\t\tunknown\n\t\t\t\t>\n\t\t\t>(\"/\" + encodeURIComponent(collection) + \"/auth-with-password\", {\n\t\t\t\tidentity: email,\n\t\t\t\tpassword,\n\t\t\t});\n\t\t\tif (data && data.record) {\n\t\t\t\tthis.authStore.setCollectionName(collection);\n\t\t\t\tthis.authStore.set(data.token, data.record as unknown as AuthModel);\n\t\t\t}\n\t\t} else {\n\t\t\tdata = await this.http.post<{ token: string } & Record<string, unknown>>(\n\t\t\t\t\"/superusers/login\",\n\t\t\t\t{ email, password },\n\t\t\t);\n\t\t\tif (data) {\n\t\t\t\tthis.authStore.setCollectionName(null);\n\t\t\t\tthis.authStore.set(data.token, null);\n\t\t\t}\n\t\t}\n\t\treturn data;\n\t}\n\n\t/** Fetch the current superuser profile and refresh the auth model. */\n\tasync me<T = ApiRecord>(options?: RequestOptions): Promise<T | null> {\n\t\tconst data = await this.http.get<T>(\"/superusers/me\", options);\n\t\tif (data) {\n\t\t\t// Update the auth model with fresh data\n\t\t\tthis.authStore.set(this.authStore.token, data as unknown as AuthModel);\n\t\t}\n\t\treturn data;\n\t}\n\n\t/**\n\t * Authenticate against an auth collection with email/password.\n\t * Stores the returned token and user record in the auth store.\n\t *\n\t * @param collection The auth collection name.\n\t * @param identity Email or username.\n\t * @param password Password.\n\t * @param options Optional request options.\n\t */\n\tasync authWithPassword(\n\t\tcollection: string,\n\t\tidentity: string,\n\t\tpassword: string,\n\t\toptions?: RequestOptions,\n\t): Promise<\n\t\t({ token: string; record: ApiRecord } & Record<string, unknown>) | null\n\t> {\n\t\tconst data = await this.http.post<\n\t\t\t{ token: string; record: ApiRecord } & Record<string, unknown>\n\t\t>(\n\t\t\t\"/\" + encodeURIComponent(collection) + \"/auth-with-password\",\n\t\t\t{ identity, password },\n\t\t\toptions,\n\t\t);\n\t\tif (data) {\n\t\t\tthis.authStore.setCollectionName(collection);\n\t\t\tthis.authStore.set(data.token, data.record as unknown as AuthModel);\n\t\t}\n\t\treturn data;\n\t}\n\n\t/**\n\t * Refresh an auth collection token.\n\t * Uses the currently stored auth token.\n\t *\n\t * @param collection The auth collection name.\n\t * @param options Optional request options.\n\t */\n\tasync authRefresh(\n\t\tcollection: string,\n\t\toptions?: RequestOptions,\n\t): Promise<\n\t\t({ token: string; record: ApiRecord } & Record<string, unknown>) | null\n\t> {\n\t\tconst data = await this.http.post<\n\t\t\t{ token: string; record: ApiRecord } & Record<string, unknown>\n\t\t>(\n\t\t\t\"/\" + encodeURIComponent(collection) + \"/auth-refresh\",\n\t\t\tundefined,\n\t\t\toptions,\n\t\t);\n\t\tif (data) {\n\t\t\tthis.authStore.setCollectionName(collection);\n\t\t\tthis.authStore.set(data.token, data.record as unknown as AuthModel);\n\t\t}\n\t\treturn data;\n\t}\n\n\t/** Clear the current auth state and remove persisted tokens. */\n\tlogout(): void {\n\t\tthis.authStore.clear();\n\t}\n\n\t// ── Health ──\n\n\t/** Ping the API health endpoint. */\n\thealth(options?: RequestOptions): Promise<Record<string, unknown> | null> {\n\t\treturn this.http.get<Record<string, unknown>>(\"/health\", options);\n\t}\n\n\t// ── Collection Management (admin) ──\n\n\t/**\n\t * List all collections (admin).\n\t * @param q URL query string (e.g. `page=1&perPage=200`).\n\t * @param options Optional request options.\n\t */\n\tlistCollections(\n\t\tq?: string,\n\t\toptions?: RequestOptions,\n\t): Promise<ListResult<ApiRecord> | null> {\n\t\treturn this.http.get<ListResult<ApiRecord>>(\n\t\t\t\"/collections\" + (q ? \"?\" + q : \"\"),\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Get a single collection by ID or name.\n\t * @param id Collection ID or name.\n\t * @param options Optional request options.\n\t */\n\tgetCollection(\n\t\tid: string,\n\t\toptions?: RequestOptions,\n\t): Promise<ApiRecord | null> {\n\t\treturn this.http.get<ApiRecord>(\n\t\t\t\"/collections/\" + encodeURIComponent(id),\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new collection (admin).\n\t * @param data Collection definition (name, type, fields, options, rules, etc.).\n\t * @param options Optional request options.\n\t */\n\tcreateCollection(\n\t\tdata: Record<string, unknown>,\n\t\toptions?: RequestOptions,\n\t): Promise<ApiRecord | null> {\n\t\treturn this.http.post<ApiRecord>(\"/collections\", data, options);\n\t}\n\n\t/**\n\t * Update an existing collection (admin).\n\t * @param id Collection ID or name.\n\t * @param data Updated collection fields.\n\t * @param options Optional request options.\n\t */\n\tupdateCollection(\n\t\tid: string,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: RequestOptions,\n\t): Promise<ApiRecord | null> {\n\t\treturn this.http.patch<ApiRecord>(\n\t\t\t\"/collections/\" + encodeURIComponent(id),\n\t\t\tdata,\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Delete a collection (admin).\n\t * @param id Collection ID or name.\n\t * @param options Optional request options.\n\t */\n\tdeleteCollection(id: string, options?: RequestOptions): Promise<null> {\n\t\treturn this.http.delete(\"/collections/\" + encodeURIComponent(id), options);\n\t}\n\n\t// ── Records (dynamic collection) ──\n\n\t/**\n\t * List records from a dynamic collection with optional filter/sort/pagination.\n\t *\n\t * @param coll Collection name.\n\t * @param params Query parameters including:\n\t * - `filter` — PocketBase filter syntax (e.g. `title~'hello' && published=true`)\n\t * - `sort` — Comma-separated, `-` prefix for DESC (e.g. `-created,title`)\n\t * - `page` — Page number (default: 1)\n\t * - `perPage` — Items per page (default: 30, max: 200)\n\t * - `expand` — Comma-separated relation fields (e.g. `author,category`)\n\t * @param options Optional request options.\n\t */\n\tlistRecords(\n\t\tcoll: string,\n\t\tparams?: Record<string, string>,\n\t\toptions?: RequestOptions,\n\t): Promise<ListResult<ApiRecord> | null> {\n\t\tconst qs = params ? \"?\" + new URLSearchParams(params).toString() : \"\";\n\t\treturn this.http.get<ListResult<ApiRecord>>(\n\t\t\t\"/\" + encodeURIComponent(coll) + qs,\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Get a single record by ID.\n\t * @param coll Collection name.\n\t * @param id Record ID.\n\t * @param options Optional request options.\n\t */\n\tgetRecord(\n\t\tcoll: string,\n\t\tid: string,\n\t\toptions?: RequestOptions,\n\t): Promise<ApiRecord | null> {\n\t\treturn this.http.get<ApiRecord>(\n\t\t\t\"/\" + encodeURIComponent(coll) + \"/\" + encodeURIComponent(id),\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Create a record in a dynamic collection.\n\t * @param coll Collection name.\n\t * @param data Record fields.\n\t * @param options Optional request options.\n\t */\n\tcreateRecord(\n\t\tcoll: string,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: RequestOptions,\n\t): Promise<ApiRecord | null> {\n\t\treturn this.http.post<ApiRecord>(\n\t\t\t\"/\" + encodeURIComponent(coll),\n\t\t\tdata,\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Update a record in a dynamic collection.\n\t * @param coll Collection name.\n\t * @param id Record ID.\n\t * @param data Updated record fields.\n\t * @param options Optional request options.\n\t */\n\tupdateRecord(\n\t\tcoll: string,\n\t\tid: string,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: RequestOptions,\n\t): Promise<ApiRecord | null> {\n\t\treturn this.http.patch<ApiRecord>(\n\t\t\t\"/\" + encodeURIComponent(coll) + \"/\" + encodeURIComponent(id),\n\t\t\tdata,\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Delete a record from a dynamic collection.\n\t * @param coll Collection name.\n\t * @param id Record ID.\n\t * @param options Optional request options.\n\t */\n\tdeleteRecord(\n\t\tcoll: string,\n\t\tid: string,\n\t\toptions?: RequestOptions,\n\t): Promise<null> {\n\t\treturn this.http.delete(\n\t\t\t\"/\" + encodeURIComponent(coll) + \"/\" + encodeURIComponent(id),\n\t\t\toptions,\n\t\t);\n\t}\n}\n","// ── Record & Collection types ───────────────────────────\n\n/** Shape of a record returned from any collection. */\nexport interface ApiRecord {\n\tid: string;\n\tcollectionId: string;\n\tcollectionName: string;\n\tcreated: string;\n\tupdated: string;\n\t[key: string]: unknown;\n}\n\n/** Paginated list response matching PocketBase format. */\nexport interface ListResult<T = ApiRecord> {\n\titems: T[];\n\tpage: number;\n\tperPage: number;\n\ttotalItems: number;\n\ttotalPages: number;\n}\n\n/** HTTP method supported by the client. */\nexport type Method = \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\";\n\nexport interface RequestOptions {\n\t/** Search/filter params */\n\tparams?: Record<string, string>;\n\t/** Raw request headers to merge */\n\theaders?: Record<string, string>;\n\t/** Abort signal */\n\tsignal?: AbortSignal;\n\t/** Custom fetch implementation (for RN or test mocking) */\n\tfetch?: typeof globalThis.fetch;\n}\n\nexport class ApiError extends Error {\n\treadonly data: unknown;\n\treadonly status: number;\n\n\tconstructor(message: string, data: unknown, status: number) {\n\t\tsuper(message);\n\t\tthis.name = \"ApiError\";\n\t\tthis.data = data;\n\t\tthis.status = status;\n\t}\n}\n","// ── HTTP Client ─────────────────────────────────────────\n// Only relies on globalThis.fetch — works in browser, React Native, and Node 18+\n\nimport { ApiError, type Method, type RequestOptions } from \"./types\";\nimport type { AuthStore } from \"./auth\";\n\n/**\n * Low-level HTTP client wrapping `fetch` with automatic auth token injection.\n * Only relies on `globalThis.fetch` — works in browser, React Native, and Node 18+.\n */\nexport class HttpClient {\n\tprivate baseUrl: string;\n\tprivate authStore: AuthStore;\n\tprivate defaultFetch: typeof globalThis.fetch;\n\n\t/**\n\t * @param baseUrl The API base URL (e.g. `http://localhost:4000/api`). Trailing slash stripped.\n\t * @param authStore The auth store providing the token for Authorization headers.\n\t */\n\tconstructor(baseUrl: string, authStore: AuthStore) {\n\t\tthis.baseUrl = baseUrl.replace(/\\/+$/, \"\");\n\t\tthis.authStore = authStore;\n\t\tthis.defaultFetch = globalThis.fetch.bind(globalThis);\n\t}\n\n\tprivate async refreshAuth(): Promise<{\n\t\ttoken: string;\n\t\trecord: Record<string, unknown>;\n\t} | null> {\n\t\tconst collection = this.authStore.collectionName;\n\t\tif (!collection) return null;\n\t\ttry {\n\t\t\tconst url =\n\t\t\t\tthis.baseUrl + \"/\" + encodeURIComponent(collection) + \"/auth-refresh\";\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t};\n\t\t\tif (this.authStore.token) {\n\t\t\t\theaders[\"Authorization\"] = \"Bearer \" + this.authStore.token;\n\t\t\t}\n\t\t\tconst res = await this.defaultFetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders,\n\t\t\t});\n\t\t\tif (!res.ok) {\n\t\t\t\tthis.authStore.clear();\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst data = (await res.json()) as Record<string, unknown>;\n\t\t\tif (data && typeof data.token === \"string\") {\n\t\t\t\tthis.authStore.set(\n\t\t\t\t\tdata.token,\n\t\t\t\t\t(data.record as Record<string, unknown> as any) ?? null,\n\t\t\t\t);\n\t\t\t\treturn data as { token: string; record: Record<string, unknown> };\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Make an HTTP request with automatic auth token injection and optional auto-refresh.\n\t *\n\t * @param method HTTP method.\n\t * @param path URL path (appended to baseUrl).\n\t * @param body JSON-serializable body, or FormData for file uploads.\n\t * @param options Optional request options.\n\t * @returns Parsed JSON response, or null for 204 No Content.\n\t * @throws {ApiError} On non-2xx responses.\n\t */\n\tasync request<T = unknown>(\n\t\tmethod: Method,\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: RequestOptions,\n\t): Promise<T | null> {\n\t\t// Auto-refresh if token is expired\n\t\tif (this.authStore.isExpired && this.authStore.collectionName) {\n\t\t\tawait this.refreshAuth();\n\t\t}\n\n\t\tlet url = this.baseUrl + path;\n\t\tif (options?.params) {\n\t\t\tconst qs = new URLSearchParams(options.params).toString();\n\t\t\tif (qs) {\n\t\t\t\turl += (path.includes(\"?\") ? \"&\" : \"?\") + qs;\n\t\t\t}\n\t\t}\n\t\tconst headers: Record<string, string> = {\n\t\t\t...options?.headers,\n\t\t};\n\n\t\t// Don't set Content-Type for FormData (browser sets multipart boundary)\n\t\tif (!(body instanceof FormData)) {\n\t\t\theaders[\"Content-Type\"] = \"application/json\";\n\t\t}\n\n\t\tif (this.authStore.token) {\n\t\t\theaders[\"Authorization\"] = \"Bearer \" + this.authStore.token;\n\t\t}\n\n\t\tconst init: RequestInit = {\n\t\t\tmethod,\n\t\t\theaders,\n\t\t\tsignal: options?.signal,\n\t\t};\n\n\t\tif (body != null && method !== \"GET\" && method !== \"DELETE\") {\n\t\t\tif (body instanceof FormData) {\n\t\t\t\tinit.body = body;\n\t\t\t} else {\n\t\t\t\tinit.body = JSON.stringify(body);\n\t\t\t}\n\t\t}\n\n\t\tconst fetcher = options?.fetch ?? this.defaultFetch;\n\t\tconst res = await fetcher(url, init);\n\n\t\tif (res.status === 204) return null;\n\n\t\t// Safely parse JSON — some errored responses may have empty or non-JSON bodies\n\t\tlet bodyText = \"\";\n\t\tlet data: Record<string, unknown> = {};\n\t\ttry {\n\t\t\tbodyText = await res.text();\n\t\t\tif (bodyText) {\n\t\t\t\tdata = JSON.parse(bodyText) as Record<string, unknown>;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Not JSON — keep data as empty object\n\t\t}\n\n\t\tif (!res.ok) {\n\t\t\tthrow new ApiError(\n\t\t\t\t(typeof data.message === \"string\" ? data.message : res.statusText) ||\n\t\t\t\t\t`Request failed with status ${res.status}`,\n\t\t\t\tdata,\n\t\t\t\tres.status,\n\t\t\t);\n\t\t}\n\n\t\treturn data as T;\n\t}\n\n\t/**\n\t * HTTP GET.\n\t * @param path URL path.\n\t * @param options Optional request options.\n\t */\n\tget<T = unknown>(path: string, options?: RequestOptions): Promise<T | null> {\n\t\treturn this.request<T>(\"GET\", path, undefined, options);\n\t}\n\n\t/**\n\t * HTTP POST.\n\t * @param path URL path.\n\t * @param body Optional request body.\n\t * @param options Optional request options.\n\t */\n\tpost<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: RequestOptions,\n\t): Promise<T | null> {\n\t\treturn this.request<T>(\"POST\", path, body, options);\n\t}\n\n\t/**\n\t * HTTP PATCH.\n\t * @param path URL path.\n\t * @param body Optional request body.\n\t * @param options Optional request options.\n\t */\n\tpatch<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: RequestOptions,\n\t): Promise<T | null> {\n\t\treturn this.request<T>(\"PATCH\", path, body, options);\n\t}\n\n\t/**\n\t * HTTP DELETE.\n\t * @param path URL path.\n\t * @param options Optional request options.\n\t */\n\tdelete<T = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<T | null> {\n\t\treturn this.request<T>(\"DELETE\", path, undefined, options);\n\t}\n}\n","// ── Auth Store ──────────────────────────────────────────\n// Pluggable storage adapter: swap for AsyncStorage on RN, localStorage on web, etc.\n\n/**\n * Interface for pluggable persistence backends.\n * Swap for `AsyncStorage` on React Native, `localStorage` on web, etc.\n */\nexport interface StorageAdapter {\n\t/** Retrieve a stored value by key. */\n\tget(key: string): string | null | Promise<string | null>;\n\t/** Persist a key-value pair. */\n\tset(key: string, value: string): void | Promise<void>;\n\t/** Remove a stored value by key. */\n\tremove(key: string): void | Promise<void>;\n}\n\n/** Shape of an authenticated user record (from auth collections). */\nexport interface AuthModel {\n\tid: string;\n\t[key: string]: unknown;\n}\n\n/** Callback signature for auth state changes. */\nexport type AuthListener = (model: AuthModel | null, token: string) => void;\n\n// Server token TTL: 7 days (matches Phoenix.Token max_age)\nconst TOKEN_TTL_MS = 7 * 24 * 60 * 60 * 1000;\n\nexport class AuthStore {\n\tprivate _token = \"\";\n\tprivate _model: AuthModel | null = null;\n\tprivate _tokenExpiresAt: number | null = null;\n\tprivate _collectionName: string | null = null;\n\tprivate listeners = new Set<AuthListener>();\n\tprivate storage: StorageAdapter;\n\n\t/**\n\t * Create an AuthStore with optional custom storage adapter.\n\t * @param storage Persistence backend. Defaults to `memoryStorage` (localStorage fallback).\n\t */\n\tconstructor(storage?: StorageAdapter) {\n\t\tthis.storage = storage ?? {\n\t\t\tget: (_key: string) => null,\n\t\t\tset: () => {},\n\t\t\tremove: () => {},\n\t\t};\n\t}\n\n\t/** The current JWT token string, or empty string if not authenticated. */\n\tget token(): string {\n\t\treturn this._token;\n\t}\n\n\t/** The current authenticated user record, or null. */\n\tget model(): AuthModel | null {\n\t\treturn this._model;\n\t}\n\n\t/** Whether a token exists (does not check expiry). */\n\tget isValid(): boolean {\n\t\treturn !!this._token;\n\t}\n\n\t/**\n\t * Whether the current token has expired (with a 30-second buffer).\n\t * Returns false when no expiry has been recorded (e.g. superuser tokens).\n\t */\n\tget isExpired(): boolean {\n\t\treturn (\n\t\t\tthis._tokenExpiresAt !== null &&\n\t\t\tDate.now() >= this._tokenExpiresAt - 30000\n\t\t);\n\t}\n\n\t/** The auth collection name used for automatic token refresh. */\n\tget collectionName(): string | null {\n\t\treturn this._collectionName;\n\t}\n\n\t/**\n\t * Set the auth collection name (used internally by auto-refresh).\n\t * @param name The collection name, or null for superuser tokens.\n\t */\n\tsetCollectionName(name: string | null): void {\n\t\tthis._collectionName = name;\n\t}\n\n\t/**\n\t * Load persisted auth state from storage.\n\t * Should be called once at application startup.\n\t */\n\tasync init(): Promise<void> {\n\t\tconst [token, model, expiresAt] = await Promise.all([\n\t\t\tthis.storage.get(\"auth_token\"),\n\t\t\tthis.storage.get(\"auth_model\"),\n\t\t\tthis.storage.get(\"auth_expires_at\"),\n\t\t]);\n\t\tif (token) this._token = token;\n\t\tif (expiresAt) this._tokenExpiresAt = parseInt(expiresAt, 10) || null;\n\t\tif (model) {\n\t\t\ttry {\n\t\t\t\tthis._model = JSON.parse(model);\n\t\t\t} catch {\n\t\t\t\t// ignore corrupt data\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Update the current auth token and model, persist to storage, and notify listeners.\n\t * @param token The JWT token string.\n\t * @param model The authenticated user record, or null for superusers.\n\t */\n\tset(token: string, model: AuthModel | null): void {\n\t\tthis._token = token;\n\t\tthis._model = model;\n\t\tthis._tokenExpiresAt = Date.now() + TOKEN_TTL_MS;\n\t\tvoid Promise.all([\n\t\t\tthis.storage.set(\"auth_token\", token),\n\t\t\tthis.storage.set(\"auth_expires_at\", String(this._tokenExpiresAt)),\n\t\t\tmodel\n\t\t\t\t? this.storage.set(\"auth_model\", JSON.stringify(model))\n\t\t\t\t: this.storage.remove(\"auth_model\"),\n\t\t]);\n\t\tthis.notify();\n\t}\n\n\t/**\n\t * Clear all auth state (token, model, expiry) and notify listeners.\n\t */\n\tclear(): void {\n\t\tthis._token = \"\";\n\t\tthis._model = null;\n\t\tthis._tokenExpiresAt = null;\n\t\tthis._collectionName = null;\n\t\tvoid Promise.all([\n\t\t\tthis.storage.remove(\"auth_token\"),\n\t\t\tthis.storage.remove(\"auth_expires_at\"),\n\t\t\tthis.storage.remove(\"auth_model\"),\n\t\t]);\n\t\tthis.notify();\n\t}\n\n\t/**\n\t * Register a listener for auth state changes.\n\t * @param fn Callback invoked with (model, token) on every change.\n\t * @returns An unsubscribe function.\n\t */\n\tonChange(fn: AuthListener): () => void {\n\t\tthis.listeners.add(fn);\n\t\treturn () => this.listeners.delete(fn);\n\t}\n\n\tprivate notify(): void {\n\t\tfor (const fn of this.listeners) {\n\t\t\tfn(this._model, this._token);\n\t\t}\n\t}\n}\n\n// ── Memory-only storage (default, works everywhere) ─────\n/** Default storage adapter using `localStorage` with graceful fallback. */\nexport const memoryStorage: StorageAdapter = {\n\tget(key: string) {\n\t\ttry {\n\t\t\treturn localStorage.getItem(key);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t},\n\tset(key: string, value: string) {\n\t\ttry {\n\t\t\tlocalStorage.setItem(key, value);\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t},\n\tremove(key: string) {\n\t\ttry {\n\t\t\tlocalStorage.removeItem(key);\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t},\n};\n","// ── Collection Service ──────────────────────────────────\n// Typed CRUD for a single collection (like PocketBase's pb.collection(name))\n\nimport type { HttpClient } from \"./http\";\nimport type { AuthStore, AuthModel } from \"./auth\";\nimport type { ApiRecord, ListResult, RequestOptions } from \"./types\";\n\n/**\n * Typed CRUD service for a single dynamic collection.\n * Get an instance via {@link LazypockClient.collection}.\n */\nexport class CollectionService {\n\tprivate http: HttpClient;\n\tprivate collectionName: string;\n\tprivate authStore?: AuthStore;\n\n\t/** @internal */\n\tconstructor(http: HttpClient, collectionName: string, authStore?: AuthStore) {\n\t\tthis.http = http;\n\t\tthis.collectionName = collectionName;\n\t\tthis.authStore = authStore;\n\t}\n\n\tprivate encodeId(id: string): string {\n\t\treturn encodeURIComponent(id);\n\t}\n\n\t/**\n\t * List records with optional filter/sort/pagination.\n\t * @param params Query parameters including `filter`, `sort`, `page`, `perPage`, `expand`.\n\t * @param options Optional request options.\n\t */\n\tlist<T = ApiRecord>(\n\t\tparams?: Record<string, string>,\n\t\toptions?: RequestOptions,\n\t): Promise<ListResult<T> | null> {\n\t\tconst qs = params ? \"?\" + new URLSearchParams(params).toString() : \"\";\n\t\treturn this.http.get<ListResult<T>>(\n\t\t\t\"/\" + this.encodeId(this.collectionName) + qs,\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Get a single record by ID.\n\t * @param id Record ID.\n\t * @param options Optional request options.\n\t */\n\tgetOne<T = ApiRecord>(\n\t\tid: string,\n\t\toptions?: RequestOptions,\n\t): Promise<T | null> {\n\t\treturn this.http.get<T>(\n\t\t\t\"/\" + this.encodeId(this.collectionName) + \"/\" + this.encodeId(id),\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new record.\n\t * @param data Record fields.\n\t * @param options Optional request options.\n\t */\n\tcreate<T = ApiRecord>(\n\t\tdata: Record<string, unknown>,\n\t\toptions?: RequestOptions,\n\t): Promise<T | null> {\n\t\treturn this.http.post<T>(\n\t\t\t\"/\" + this.encodeId(this.collectionName),\n\t\t\tdata,\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Update a record by ID.\n\t * @param id Record ID.\n\t * @param data Updated record fields.\n\t * @param options Optional request options.\n\t */\n\tupdate<T = ApiRecord>(\n\t\tid: string,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: RequestOptions,\n\t): Promise<T | null> {\n\t\treturn this.http.patch<T>(\n\t\t\t\"/\" + this.encodeId(this.collectionName) + \"/\" + this.encodeId(id),\n\t\t\tdata,\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Delete a record by ID.\n\t * @param id Record ID.\n\t * @param options Optional request options.\n\t */\n\tdelete(id: string, options?: RequestOptions): Promise<null> {\n\t\treturn this.http.delete(\n\t\t\t\"/\" + this.encodeId(this.collectionName) + \"/\" + this.encodeId(id),\n\t\t\toptions,\n\t\t);\n\t}\n\t// ── Expand / Relation Fields ──\n\n\t/**\n\t * Get a list of expandable (relation) fields for this collection.\n\t * Useful for constructing `expand` query parameters.\n\t */\n\tasync expandFields(\n\t\toptions?: RequestOptions,\n\t): Promise<{ field: string; targetCollection: string }[] | null> {\n\t\tconst data = await this.http.get<{\n\t\t\tfields?: { name: string; type: string; options?: Record<string, string> }[];\n\t\t}>(\n\t\t\t\"/collections/\" + this.encodeId(this.collectionName),\n\t\t\toptions,\n\t\t);\n\t\tif (!data?.fields) return null;\n\t\treturn data.fields\n\t\t\t.filter((f) => f.type === \"relation\" && f.options?.collection)\n\t\t\t.map((f) => ({\n\t\t\t\tfield: f.name,\n\t\t\t\ttargetCollection: f.options!.collection,\n\t\t\t}));\n\t}\n\n\t// ── Auth Collection Methods ──\n\n\t/**\n\t * Authenticate with email/password against this auth collection.\n\t * Stores the returned token and user model in the auth store.\n\t */\n\tasync authWithPassword(\n\t\tidentity: string,\n\t\tpassword: string,\n\t\toptions?: RequestOptions,\n\t): Promise<({ token: string; record: ApiRecord } & Record<string, unknown>) | null> {\n\t\tconst data = await this.http.post<\n\t\t\t{ token: string; record: ApiRecord } & Record<string, unknown>\n\t\t>(\n\t\t\t\"/\" + this.encodeId(this.collectionName) + \"/auth-with-password\",\n\t\t\t{ identity, password },\n\t\t\toptions,\n\t\t);\n\t\tif (data && this.authStore) {\n\t\t\tthis.authStore.setCollectionName(this.collectionName);\n\t\t\tthis.authStore.set(data.token, data.record as unknown as AuthModel);\n\t\t}\n\t\treturn data;\n\t}\n\n\t/**\n\t * Refresh the auth token for the currently authenticated user.\n\t * Updates the stored token and user model.\n\t */\n\tasync authRefresh(\n\t\toptions?: RequestOptions,\n\t): Promise<({ token: string; record: ApiRecord } & Record<string, unknown>) | null> {\n\t\tconst data = await this.http.post<\n\t\t\t{ token: string; record: ApiRecord } & Record<string, unknown>\n\t\t>(\n\t\t\t\"/\" + this.encodeId(this.collectionName) + \"/auth-refresh\",\n\t\t\tundefined,\n\t\t\toptions,\n\t\t);\n\t\tif (data && this.authStore) {\n\t\t\tthis.authStore.setCollectionName(this.collectionName);\n\t\t\tthis.authStore.set(data.token, data.record as unknown as AuthModel);\n\t\t}\n\t\treturn data;\n\t}\n\n\t/**\n\t * Get available auth methods for this collection.\n\t */\n\tasync authMethods(\n\t\toptions?: RequestOptions,\n\t): Promise<Record<string, unknown> | null> {\n\t\treturn this.http.get<Record<string, unknown>>(\n\t\t\t\"/\" + this.encodeId(this.collectionName) + \"/auth-methods\",\n\t\t\toptions,\n\t\t);\n\t}\n}\n","// ── Phoenix Channel WebSocket Client ──────────────────\n//\n// Implements the Phoenix Channels protocol over WebSocket\n// to subscribe to collection realtime updates.\n//\n// Protocol: Phoenix V1 JSON Serializer (object-based messages).\n// Sends messages as JSON objects with {topic, event, payload, ref, join_ref}.\n// Receives messages as JSON objects with {topic, event, payload, ref}.\n\ninterface RealtimeEvent {\n\tevent: string;\n\ttopic: string;\n\tpayload: Record<string, unknown>;\n}\n\ninterface SubEntry {\n\ttopic: string;\n\tcallback: (e: RealtimeEvent) => void;\n}\n\nexport type RealtimeConnectOpts = {\n\t/** WebSocket URL (e.g. ws://localhost:4000/socket/websocket) */\n\turl: string;\n\t/** Auth token to pass as query param */\n\ttoken?: string;\n};\n\n/**\n * Derive a WebSocket URL from an HTTP base URL.\n * http://localhost:4000/api → ws://localhost:4000/socket/websocket\n */\nexport function wsUrlFromBaseUrl(baseUrl: string): string {\n\ttry {\n\t\tconst url = new URL(baseUrl);\n\t\tconst protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n\t\treturn `${protocol}//${url.host}/socket/websocket`;\n\t} catch {\n\t\treturn `${baseUrl.replace(/^http/, \"ws\").replace(/\\/api$/, \"\")}/socket/websocket`;\n\t}\n}\n\n/**\n * Phoenix Channel client for real-time collection subscriptions.\n *\n * Connects via WebSocket and subscribes to collection topics.\n * Includes automatic reconnection with exponential backoff.\n *\n * @example\n * ```ts\n * const rt = new RealtimeService();\n * rt.connect({ url: wsUrlFromBaseUrl('http://localhost:4000/api') });\n * rt.subscribe('collection:posts', (e) => console.log(e));\n * ```\n */\nexport class RealtimeService {\n\tprivate ws: WebSocket | null = null;\n\tprivate refCounter = 0;\n\tprivate subscriptions = new Map<string, SubEntry[]>();\n\tprivate reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate reconnectAttempt = 0;\n\tprivate maxReconnectDelay = 5000;\n\n\t// Callbacks for connection state\n\tonReconnect?: () => void;\n\tonDisconnect?: () => void;\n\tonError?: (err: Event) => void;\n\n\tprivate url: string = \"\";\n\tprivate token: string | undefined;\n\n\tconnect(opts: RealtimeConnectOpts): void {\n\t\tthis.url = opts.url;\n\t\tthis.token = opts.token;\n\t\tthis.reconnectAttempt = 0;\n\t\tthis.doConnect();\n\t}\n\n\tdisconnect(): void {\n\t\tthis.clearReconnectTimer();\n\t\tthis.ws?.close();\n\t\tthis.ws = null;\n\t}\n\n\t/**\n\t * Subscribe to a topic (e.g. \"collection:posts\" or \"collection:posts:*\").\n\t * The backend Channel authorizes via listRule on join.\n\t */\n\tsubscribe(topic: string, callback: (e: RealtimeEvent) => void): void {\n\t\tconst subs = this.subscriptions.get(topic) || [];\n\t\tsubs.push({ topic, callback });\n\t\tthis.subscriptions.set(topic, subs);\n\n\t\tif (this.ws?.readyState === WebSocket.OPEN) {\n\t\t\tthis.joinTopic(topic);\n\t\t}\n\t}\n\n\t/**\n\t * Unsubscribe a specific callback from a topic.\n\t */\n\tunsubscribe(topic: string, callback?: (e: RealtimeEvent) => void): void {\n\t\tif (!callback) {\n\t\t\tthis.subscriptions.delete(topic);\n\t\t\treturn;\n\t\t}\n\t\tconst subs = this.subscriptions\n\t\t\t.get(topic)\n\t\t\t?.filter((s) => s.callback !== callback);\n\t\tif (subs && subs.length > 0) {\n\t\t\tthis.subscriptions.set(topic, subs);\n\t\t} else {\n\t\t\tthis.subscriptions.delete(topic);\n\t\t}\n\t}\n\n\tprivate resubscribeAll(): void {\n\t\tfor (const topic of this.subscriptions.keys()) {\n\t\t\tif (this.ws?.readyState === WebSocket.OPEN) {\n\t\t\t\tthis.joinTopic(topic);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate doConnect(): void {\n\t\tif (typeof WebSocket === \"undefined\") {\n\t\t\tconsole.warn(\n\t\t\t\t\"[lazypock] WebSocket not available — realtime subscriptions disabled\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tlet url = this.url;\n\t\tif (this.token) {\n\t\t\turl +=\n\t\t\t\t(url.includes(\"?\") ? \"&\" : \"?\") +\n\t\t\t\t\"token=\" +\n\t\t\t\tencodeURIComponent(this.token);\n\t\t}\n\n\t\tthis.ws = new WebSocket(url);\n\n\t\tthis.ws.onopen = () => {\n\t\t\tthis.reconnectAttempt = 0;\n\t\t\tthis.resubscribeAll();\n\t\t\tthis.startHeartbeat();\n\t\t};\n\n\t\tthis.ws.onmessage = (msg: MessageEvent) => {\n\t\t\tthis.handleMessage(msg.data);\n\t\t};\n\n\t\tthis.ws.onclose = () => {\n\t\t\tthis.stopHeartbeat();\n\t\t\tthis.onDisconnect?.();\n\t\t\tthis.scheduleReconnect();\n\t\t};\n\n\t\tthis.ws.onerror = (err: Event) => {\n\t\t\tthis.onError?.(err);\n\t\t};\n\t}\n\n\tprivate handleMessage(data: string): void {\n\t\tlet parsed: Record<string, unknown>;\n\t\ttry {\n\t\t\tparsed = JSON.parse(data) as Record<string, unknown>;\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tif (typeof parsed !== \"object\" || !parsed.topic || !parsed.event) return;\n\n\t\tconst topic = parsed.topic as string;\n\t\tconst event = parsed.event as string;\n\t\tconst payload = (parsed.payload as Record<string, unknown>) || {};\n\n\t\t// Handle phx_reply (join/heartbeat responses)\n\t\tif (event === \"phx_reply\") return;\n\n\t\t// Relay incoming events to all subscribers of this topic\n\t\tconst subs = this.subscriptions.get(topic);\n\t\tif (subs) {\n\t\t\tconst e: RealtimeEvent = {\n\t\t\t\tevent,\n\t\t\t\ttopic,\n\t\t\t\tpayload: payload as Record<string, unknown>,\n\t\t\t};\n\t\t\tfor (const s of subs) {\n\t\t\t\ttry {\n\t\t\t\t\ts.callback(e);\n\t\t\t\t} catch {\n\t\t\t\t\t// swallow callback errors\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate joinTopic(topic: string): void {\n\t\tconst ref = this.nextRef();\n\t\t// Phoenix V1 JSON Serializer expects a JSON object, not an array\n\t\tconst msg = JSON.stringify({\n\t\t\ttopic: topic,\n\t\t\tevent: \"phx_join\",\n\t\t\tpayload: {},\n\t\t\tref: ref,\n\t\t});\n\t\tthis.ws?.send(msg);\n\t}\n\n\tprivate nextRef(): string {\n\t\tthis.refCounter++;\n\t\treturn this.refCounter.toString();\n\t}\n\n\t// ── Heartbeat ──\n\n\tprivate heartbeatInterval: ReturnType<typeof setInterval> | null = null;\n\n\tprivate startHeartbeat(): void {\n\t\tthis.stopHeartbeat();\n\t\tthis.heartbeatInterval = setInterval(() => {\n\t\t\tif (this.ws?.readyState === WebSocket.OPEN) {\n\t\t\t\tconst ref = this.nextRef();\n\t\t\t\t// Phoenix V1 JSON Serializer expects a JSON object\n\t\t\t\tconst msg = JSON.stringify({\n\t\t\t\t\ttopic: \"phoenix\",\n\t\t\t\t\tevent: \"heartbeat\",\n\t\t\t\t\tpayload: {},\n\t\t\t\t\tref: ref,\n\t\t\t\t});\n\t\t\t\tthis.ws.send(msg);\n\t\t\t}\n\t\t}, 30_000);\n\t}\n\n\tprivate stopHeartbeat(): void {\n\t\tif (this.heartbeatInterval) {\n\t\t\tclearInterval(this.heartbeatInterval);\n\t\t\tthis.heartbeatInterval = null;\n\t\t}\n\t}\n\n\t// ── Reconnect ──\n\n\tprivate scheduleReconnect(): void {\n\t\tthis.clearReconnectTimer();\n\t\tconst delay = Math.min(\n\t\t\t1000 * 2 ** this.reconnectAttempt,\n\t\t\tthis.maxReconnectDelay,\n\t\t);\n\t\tthis.reconnectAttempt++;\n\t\tthis.reconnectTimer = setTimeout(() => {\n\t\t\tthis.reconnectTimer = null;\n\t\t\tthis.onReconnect?.();\n\t\t\tthis.doConnect();\n\t\t}, delay);\n\t}\n\n\tprivate clearReconnectTimer(): void {\n\t\tif (this.reconnectTimer) {\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t\tthis.reconnectTimer = null;\n\t\t}\n\t}\n}\n","// ── File Service ─────────────────────────────────────────\n// Upload, download, and delete files.\n\nimport type { HttpClient } from \"./http\";\nimport type { RequestOptions } from \"./types\";\n\n/** Response shape from the server file endpoints */\nexport interface FileRecord {\n\tid: string;\n\tfilename: string;\n\tmimeType: string;\n\tsize: number;\n\turl: string;\n\t[key: string]: unknown;\n}\n\n/**\n * Construct a file URL from the API base URL and file ID.\n */\nexport function getFileUrl(baseUrl: string, fileId: string): string {\n\treturn baseUrl.replace(/\\/+$/, \"\") + \"/files/\" + encodeURIComponent(fileId);\n}\n\n/**\n * Service for file upload, retrieval, and deletion.\n * Access via {@link LazypockClient.files}.\n */\nexport class FilesService {\n\tconstructor(private http: HttpClient) {}\n\n\t/**\n\t * Upload a file or blob.\n\t *\n\t * @param file The File or Blob to upload.\n\t * @param filename Optional filename (required if `file` is a Blob without a name).\n\t * @param options Optional request options (signal, custom fetch).\n\t * @param meta Optional metadata: collectionName, recordId, fieldName for ownership tracking.\n\t */\n\tasync upload(\n\t\tfile: File | Blob,\n\t\tfilename?: string,\n\t\toptions?: RequestOptions,\n\t\tmeta?: { collectionName?: string; recordId?: string; fieldName?: string },\n\t): Promise<FileRecord | null> {\n\t\tif (typeof FormData === \"undefined\") {\n\t\t\tthrow new Error(\"FormData is not available in this environment\");\n\t\t}\n\n\t\tconst formData = new FormData();\n\t\tconst name = filename || (file instanceof File ? file.name : \"file\");\n\t\tformData.append(\"file\", file, name);\n\n\t\tif (meta?.collectionName)\n\t\t\tformData.append(\"collection_name\", meta.collectionName);\n\t\tif (meta?.recordId) formData.append(\"record_id\", meta.recordId);\n\t\tif (meta?.fieldName) formData.append(\"field_name\", meta.fieldName);\n\n\t\tconst data = await this.http.request<Record<string, unknown>>(\n\t\t\t\"POST\",\n\t\t\t\"/files\",\n\t\t\tformData,\n\t\t\toptions,\n\t\t);\n\n\t\treturn data as FileRecord | null;\n\t}\n\n\t/**\n\t * Fetch file metadata including URL.\n\t * @param fileId The file ID.\n\t */\n\tasync getUrl(fileId: string): Promise<string | null> {\n\t\tconst data = await this.http.request<Record<string, unknown>>(\n\t\t\t\"GET\",\n\t\t\t\"/files/\" + encodeURIComponent(fileId),\n\t\t);\n\t\tif (data && typeof data === \"object\" && \"url\" in data) {\n\t\t\treturn (data as Record<string, unknown>).url as string;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Delete a file by ID.\n\t * @param fileId The file ID.\n\t * @param options Optional request options.\n\t */\n\tasync delete(fileId: string, options?: RequestOptions): Promise<null> {\n\t\treturn this.http.request<null>(\n\t\t\t\"DELETE\",\n\t\t\t\"/files/\" + encodeURIComponent(fileId),\n\t\t\tundefined,\n\t\t\toptions,\n\t\t);\n\t}\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
/** Shape of a record returned from any collection. */
|
|
2
|
+
interface ApiRecord {
|
|
3
|
+
id: string;
|
|
4
|
+
collectionId: string;
|
|
5
|
+
collectionName: string;
|
|
6
|
+
created: string;
|
|
7
|
+
updated: string;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
/** Paginated list response matching PocketBase format. */
|
|
11
|
+
interface ListResult<T = ApiRecord> {
|
|
12
|
+
items: T[];
|
|
13
|
+
page: number;
|
|
14
|
+
perPage: number;
|
|
15
|
+
totalItems: number;
|
|
16
|
+
totalPages: number;
|
|
17
|
+
}
|
|
18
|
+
/** HTTP method supported by the client. */
|
|
19
|
+
type Method = "GET" | "POST" | "PATCH" | "DELETE";
|
|
20
|
+
interface RequestOptions {
|
|
21
|
+
/** Search/filter params */
|
|
22
|
+
params?: Record<string, string>;
|
|
23
|
+
/** Raw request headers to merge */
|
|
24
|
+
headers?: Record<string, string>;
|
|
25
|
+
/** Abort signal */
|
|
26
|
+
signal?: AbortSignal;
|
|
27
|
+
/** Custom fetch implementation (for RN or test mocking) */
|
|
28
|
+
fetch?: typeof globalThis.fetch;
|
|
29
|
+
}
|
|
30
|
+
declare class ApiError extends Error {
|
|
31
|
+
readonly data: unknown;
|
|
32
|
+
readonly status: number;
|
|
33
|
+
constructor(message: string, data: unknown, status: number);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Interface for pluggable persistence backends.
|
|
38
|
+
* Swap for `AsyncStorage` on React Native, `localStorage` on web, etc.
|
|
39
|
+
*/
|
|
40
|
+
interface StorageAdapter {
|
|
41
|
+
/** Retrieve a stored value by key. */
|
|
42
|
+
get(key: string): string | null | Promise<string | null>;
|
|
43
|
+
/** Persist a key-value pair. */
|
|
44
|
+
set(key: string, value: string): void | Promise<void>;
|
|
45
|
+
/** Remove a stored value by key. */
|
|
46
|
+
remove(key: string): void | Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
/** Shape of an authenticated user record (from auth collections). */
|
|
49
|
+
interface AuthModel {
|
|
50
|
+
id: string;
|
|
51
|
+
[key: string]: unknown;
|
|
52
|
+
}
|
|
53
|
+
/** Callback signature for auth state changes. */
|
|
54
|
+
type AuthListener = (model: AuthModel | null, token: string) => void;
|
|
55
|
+
declare class AuthStore {
|
|
56
|
+
private _token;
|
|
57
|
+
private _model;
|
|
58
|
+
private _tokenExpiresAt;
|
|
59
|
+
private _collectionName;
|
|
60
|
+
private listeners;
|
|
61
|
+
private storage;
|
|
62
|
+
/**
|
|
63
|
+
* Create an AuthStore with optional custom storage adapter.
|
|
64
|
+
* @param storage Persistence backend. Defaults to `memoryStorage` (localStorage fallback).
|
|
65
|
+
*/
|
|
66
|
+
constructor(storage?: StorageAdapter);
|
|
67
|
+
/** The current JWT token string, or empty string if not authenticated. */
|
|
68
|
+
get token(): string;
|
|
69
|
+
/** The current authenticated user record, or null. */
|
|
70
|
+
get model(): AuthModel | null;
|
|
71
|
+
/** Whether a token exists (does not check expiry). */
|
|
72
|
+
get isValid(): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Whether the current token has expired (with a 30-second buffer).
|
|
75
|
+
* Returns false when no expiry has been recorded (e.g. superuser tokens).
|
|
76
|
+
*/
|
|
77
|
+
get isExpired(): boolean;
|
|
78
|
+
/** The auth collection name used for automatic token refresh. */
|
|
79
|
+
get collectionName(): string | null;
|
|
80
|
+
/**
|
|
81
|
+
* Set the auth collection name (used internally by auto-refresh).
|
|
82
|
+
* @param name The collection name, or null for superuser tokens.
|
|
83
|
+
*/
|
|
84
|
+
setCollectionName(name: string | null): void;
|
|
85
|
+
/**
|
|
86
|
+
* Load persisted auth state from storage.
|
|
87
|
+
* Should be called once at application startup.
|
|
88
|
+
*/
|
|
89
|
+
init(): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Update the current auth token and model, persist to storage, and notify listeners.
|
|
92
|
+
* @param token The JWT token string.
|
|
93
|
+
* @param model The authenticated user record, or null for superusers.
|
|
94
|
+
*/
|
|
95
|
+
set(token: string, model: AuthModel | null): void;
|
|
96
|
+
/**
|
|
97
|
+
* Clear all auth state (token, model, expiry) and notify listeners.
|
|
98
|
+
*/
|
|
99
|
+
clear(): void;
|
|
100
|
+
/**
|
|
101
|
+
* Register a listener for auth state changes.
|
|
102
|
+
* @param fn Callback invoked with (model, token) on every change.
|
|
103
|
+
* @returns An unsubscribe function.
|
|
104
|
+
*/
|
|
105
|
+
onChange(fn: AuthListener): () => void;
|
|
106
|
+
private notify;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Low-level HTTP client wrapping `fetch` with automatic auth token injection.
|
|
111
|
+
* Only relies on `globalThis.fetch` — works in browser, React Native, and Node 18+.
|
|
112
|
+
*/
|
|
113
|
+
declare class HttpClient {
|
|
114
|
+
private baseUrl;
|
|
115
|
+
private authStore;
|
|
116
|
+
private defaultFetch;
|
|
117
|
+
/**
|
|
118
|
+
* @param baseUrl The API base URL (e.g. `http://localhost:4000/api`). Trailing slash stripped.
|
|
119
|
+
* @param authStore The auth store providing the token for Authorization headers.
|
|
120
|
+
*/
|
|
121
|
+
constructor(baseUrl: string, authStore: AuthStore);
|
|
122
|
+
private refreshAuth;
|
|
123
|
+
/**
|
|
124
|
+
* Make an HTTP request with automatic auth token injection and optional auto-refresh.
|
|
125
|
+
*
|
|
126
|
+
* @param method HTTP method.
|
|
127
|
+
* @param path URL path (appended to baseUrl).
|
|
128
|
+
* @param body JSON-serializable body, or FormData for file uploads.
|
|
129
|
+
* @param options Optional request options.
|
|
130
|
+
* @returns Parsed JSON response, or null for 204 No Content.
|
|
131
|
+
* @throws {ApiError} On non-2xx responses.
|
|
132
|
+
*/
|
|
133
|
+
request<T = unknown>(method: Method, path: string, body?: unknown, options?: RequestOptions): Promise<T | null>;
|
|
134
|
+
/**
|
|
135
|
+
* HTTP GET.
|
|
136
|
+
* @param path URL path.
|
|
137
|
+
* @param options Optional request options.
|
|
138
|
+
*/
|
|
139
|
+
get<T = unknown>(path: string, options?: RequestOptions): Promise<T | null>;
|
|
140
|
+
/**
|
|
141
|
+
* HTTP POST.
|
|
142
|
+
* @param path URL path.
|
|
143
|
+
* @param body Optional request body.
|
|
144
|
+
* @param options Optional request options.
|
|
145
|
+
*/
|
|
146
|
+
post<T = unknown>(path: string, body?: unknown, options?: RequestOptions): Promise<T | null>;
|
|
147
|
+
/**
|
|
148
|
+
* HTTP PATCH.
|
|
149
|
+
* @param path URL path.
|
|
150
|
+
* @param body Optional request body.
|
|
151
|
+
* @param options Optional request options.
|
|
152
|
+
*/
|
|
153
|
+
patch<T = unknown>(path: string, body?: unknown, options?: RequestOptions): Promise<T | null>;
|
|
154
|
+
/**
|
|
155
|
+
* HTTP DELETE.
|
|
156
|
+
* @param path URL path.
|
|
157
|
+
* @param options Optional request options.
|
|
158
|
+
*/
|
|
159
|
+
delete<T = unknown>(path: string, options?: RequestOptions): Promise<T | null>;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Typed CRUD service for a single dynamic collection.
|
|
164
|
+
* Get an instance via {@link LazypockClient.collection}.
|
|
165
|
+
*/
|
|
166
|
+
declare class CollectionService {
|
|
167
|
+
private http;
|
|
168
|
+
private collectionName;
|
|
169
|
+
private authStore?;
|
|
170
|
+
/** @internal */
|
|
171
|
+
constructor(http: HttpClient, collectionName: string, authStore?: AuthStore);
|
|
172
|
+
private encodeId;
|
|
173
|
+
/**
|
|
174
|
+
* List records with optional filter/sort/pagination.
|
|
175
|
+
* @param params Query parameters including `filter`, `sort`, `page`, `perPage`, `expand`.
|
|
176
|
+
* @param options Optional request options.
|
|
177
|
+
*/
|
|
178
|
+
list<T = ApiRecord>(params?: Record<string, string>, options?: RequestOptions): Promise<ListResult<T> | null>;
|
|
179
|
+
/**
|
|
180
|
+
* Get a single record by ID.
|
|
181
|
+
* @param id Record ID.
|
|
182
|
+
* @param options Optional request options.
|
|
183
|
+
*/
|
|
184
|
+
getOne<T = ApiRecord>(id: string, options?: RequestOptions): Promise<T | null>;
|
|
185
|
+
/**
|
|
186
|
+
* Create a new record.
|
|
187
|
+
* @param data Record fields.
|
|
188
|
+
* @param options Optional request options.
|
|
189
|
+
*/
|
|
190
|
+
create<T = ApiRecord>(data: Record<string, unknown>, options?: RequestOptions): Promise<T | null>;
|
|
191
|
+
/**
|
|
192
|
+
* Update a record by ID.
|
|
193
|
+
* @param id Record ID.
|
|
194
|
+
* @param data Updated record fields.
|
|
195
|
+
* @param options Optional request options.
|
|
196
|
+
*/
|
|
197
|
+
update<T = ApiRecord>(id: string, data: Record<string, unknown>, options?: RequestOptions): Promise<T | null>;
|
|
198
|
+
/**
|
|
199
|
+
* Delete a record by ID.
|
|
200
|
+
* @param id Record ID.
|
|
201
|
+
* @param options Optional request options.
|
|
202
|
+
*/
|
|
203
|
+
delete(id: string, options?: RequestOptions): Promise<null>;
|
|
204
|
+
/**
|
|
205
|
+
* Get a list of expandable (relation) fields for this collection.
|
|
206
|
+
* Useful for constructing `expand` query parameters.
|
|
207
|
+
*/
|
|
208
|
+
expandFields(options?: RequestOptions): Promise<{
|
|
209
|
+
field: string;
|
|
210
|
+
targetCollection: string;
|
|
211
|
+
}[] | null>;
|
|
212
|
+
/**
|
|
213
|
+
* Authenticate with email/password against this auth collection.
|
|
214
|
+
* Stores the returned token and user model in the auth store.
|
|
215
|
+
*/
|
|
216
|
+
authWithPassword(identity: string, password: string, options?: RequestOptions): Promise<({
|
|
217
|
+
token: string;
|
|
218
|
+
record: ApiRecord;
|
|
219
|
+
} & Record<string, unknown>) | null>;
|
|
220
|
+
/**
|
|
221
|
+
* Refresh the auth token for the currently authenticated user.
|
|
222
|
+
* Updates the stored token and user model.
|
|
223
|
+
*/
|
|
224
|
+
authRefresh(options?: RequestOptions): Promise<({
|
|
225
|
+
token: string;
|
|
226
|
+
record: ApiRecord;
|
|
227
|
+
} & Record<string, unknown>) | null>;
|
|
228
|
+
/**
|
|
229
|
+
* Get available auth methods for this collection.
|
|
230
|
+
*/
|
|
231
|
+
authMethods(options?: RequestOptions): Promise<Record<string, unknown> | null>;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
interface RealtimeEvent {
|
|
235
|
+
event: string;
|
|
236
|
+
topic: string;
|
|
237
|
+
payload: Record<string, unknown>;
|
|
238
|
+
}
|
|
239
|
+
type RealtimeConnectOpts = {
|
|
240
|
+
/** WebSocket URL (e.g. ws://localhost:4000/socket/websocket) */
|
|
241
|
+
url: string;
|
|
242
|
+
/** Auth token to pass as query param */
|
|
243
|
+
token?: string;
|
|
244
|
+
};
|
|
245
|
+
/**
|
|
246
|
+
* Derive a WebSocket URL from an HTTP base URL.
|
|
247
|
+
* http://localhost:4000/api → ws://localhost:4000/socket/websocket
|
|
248
|
+
*/
|
|
249
|
+
declare function wsUrlFromBaseUrl(baseUrl: string): string;
|
|
250
|
+
/**
|
|
251
|
+
* Phoenix Channel client for real-time collection subscriptions.
|
|
252
|
+
*
|
|
253
|
+
* Connects via WebSocket and subscribes to collection topics.
|
|
254
|
+
* Includes automatic reconnection with exponential backoff.
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```ts
|
|
258
|
+
* const rt = new RealtimeService();
|
|
259
|
+
* rt.connect({ url: wsUrlFromBaseUrl('http://localhost:4000/api') });
|
|
260
|
+
* rt.subscribe('collection:posts', (e) => console.log(e));
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
declare class RealtimeService {
|
|
264
|
+
private ws;
|
|
265
|
+
private refCounter;
|
|
266
|
+
private subscriptions;
|
|
267
|
+
private reconnectTimer;
|
|
268
|
+
private reconnectAttempt;
|
|
269
|
+
private maxReconnectDelay;
|
|
270
|
+
onReconnect?: () => void;
|
|
271
|
+
onDisconnect?: () => void;
|
|
272
|
+
onError?: (err: Event) => void;
|
|
273
|
+
private url;
|
|
274
|
+
private token;
|
|
275
|
+
connect(opts: RealtimeConnectOpts): void;
|
|
276
|
+
disconnect(): void;
|
|
277
|
+
/**
|
|
278
|
+
* Subscribe to a topic (e.g. "collection:posts" or "collection:posts:*").
|
|
279
|
+
* The backend Channel authorizes via listRule on join.
|
|
280
|
+
*/
|
|
281
|
+
subscribe(topic: string, callback: (e: RealtimeEvent) => void): void;
|
|
282
|
+
/**
|
|
283
|
+
* Unsubscribe a specific callback from a topic.
|
|
284
|
+
*/
|
|
285
|
+
unsubscribe(topic: string, callback?: (e: RealtimeEvent) => void): void;
|
|
286
|
+
private resubscribeAll;
|
|
287
|
+
private doConnect;
|
|
288
|
+
private handleMessage;
|
|
289
|
+
private joinTopic;
|
|
290
|
+
private nextRef;
|
|
291
|
+
private heartbeatInterval;
|
|
292
|
+
private startHeartbeat;
|
|
293
|
+
private stopHeartbeat;
|
|
294
|
+
private scheduleReconnect;
|
|
295
|
+
private clearReconnectTimer;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** Response shape from the server file endpoints */
|
|
299
|
+
interface FileRecord {
|
|
300
|
+
id: string;
|
|
301
|
+
filename: string;
|
|
302
|
+
mimeType: string;
|
|
303
|
+
size: number;
|
|
304
|
+
url: string;
|
|
305
|
+
[key: string]: unknown;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Construct a file URL from the API base URL and file ID.
|
|
309
|
+
*/
|
|
310
|
+
declare function getFileUrl(baseUrl: string, fileId: string): string;
|
|
311
|
+
/**
|
|
312
|
+
* Service for file upload, retrieval, and deletion.
|
|
313
|
+
* Access via {@link LazypockClient.files}.
|
|
314
|
+
*/
|
|
315
|
+
declare class FilesService {
|
|
316
|
+
private http;
|
|
317
|
+
constructor(http: HttpClient);
|
|
318
|
+
/**
|
|
319
|
+
* Upload a file or blob.
|
|
320
|
+
*
|
|
321
|
+
* @param file The File or Blob to upload.
|
|
322
|
+
* @param filename Optional filename (required if `file` is a Blob without a name).
|
|
323
|
+
* @param options Optional request options (signal, custom fetch).
|
|
324
|
+
* @param meta Optional metadata: collectionName, recordId, fieldName for ownership tracking.
|
|
325
|
+
*/
|
|
326
|
+
upload(file: File | Blob, filename?: string, options?: RequestOptions, meta?: {
|
|
327
|
+
collectionName?: string;
|
|
328
|
+
recordId?: string;
|
|
329
|
+
fieldName?: string;
|
|
330
|
+
}): Promise<FileRecord | null>;
|
|
331
|
+
/**
|
|
332
|
+
* Fetch file metadata including URL.
|
|
333
|
+
* @param fileId The file ID.
|
|
334
|
+
*/
|
|
335
|
+
getUrl(fileId: string): Promise<string | null>;
|
|
336
|
+
/**
|
|
337
|
+
* Delete a file by ID.
|
|
338
|
+
* @param fileId The file ID.
|
|
339
|
+
* @param options Optional request options.
|
|
340
|
+
*/
|
|
341
|
+
delete(fileId: string, options?: RequestOptions): Promise<null>;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** Options for constructing a {@link LazypockClient}. */
|
|
345
|
+
interface LazypockClientOptions {
|
|
346
|
+
/** API base URL (e.g. 'http://localhost:4000/api') */
|
|
347
|
+
baseUrl: string;
|
|
348
|
+
/** Custom storage adapter (default: localStorage fallback) */
|
|
349
|
+
storage?: StorageAdapter;
|
|
350
|
+
/** Explicit auth store instance (for sharing across modules) */
|
|
351
|
+
authStore?: AuthStore;
|
|
352
|
+
/** Real-time service for Phoenix Channel WebSocket subscriptions */
|
|
353
|
+
realtime?: RealtimeService;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Lazypock API client.
|
|
357
|
+
*
|
|
358
|
+
* Provides methods for authentication, CRUD operations on dynamic collections,
|
|
359
|
+
* file management, and real-time subscriptions.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```ts
|
|
363
|
+
* const client = new LazypockClient({ baseUrl: 'http://localhost:4000/api' });
|
|
364
|
+
* await client.login('admin@example.com', 'password');
|
|
365
|
+
* const posts = await client.collection('posts').list();
|
|
366
|
+
* ```
|
|
367
|
+
*/
|
|
368
|
+
declare class LazypockClient {
|
|
369
|
+
readonly http: HttpClient;
|
|
370
|
+
readonly authStore: AuthStore;
|
|
371
|
+
readonly realtime: RealtimeService;
|
|
372
|
+
readonly files: FilesService;
|
|
373
|
+
private collectionCache;
|
|
374
|
+
/**
|
|
375
|
+
* Create a new Lazypock client.
|
|
376
|
+
* @param options Configuration options.
|
|
377
|
+
*/
|
|
378
|
+
constructor(options: LazypockClientOptions);
|
|
379
|
+
/**
|
|
380
|
+
* Get or create a typed service for the given collection.
|
|
381
|
+
* Services are cached after first access.
|
|
382
|
+
*
|
|
383
|
+
* @param name The collection name.
|
|
384
|
+
* @returns A {@link CollectionService} instance.
|
|
385
|
+
*/
|
|
386
|
+
collection(name: string): CollectionService;
|
|
387
|
+
/** Check whether any superuser exists (for login vs setup screen routing). */
|
|
388
|
+
checkSuperuser(): Promise<{
|
|
389
|
+
has_superuser: boolean;
|
|
390
|
+
} | null>;
|
|
391
|
+
/**
|
|
392
|
+
* Create the initial superuser account.
|
|
393
|
+
* Only works when no superuser exists yet.
|
|
394
|
+
* Stores the returned token in the auth store.
|
|
395
|
+
* @param email Superuser email.
|
|
396
|
+
* @param password Superuser password (min 8 chars).
|
|
397
|
+
*/
|
|
398
|
+
setup(email: string, password: string): Promise<({
|
|
399
|
+
token: string;
|
|
400
|
+
} & Record<string, unknown>) | null>;
|
|
401
|
+
/**
|
|
402
|
+
* Authenticate as a superuser or auth collection user.
|
|
403
|
+
*
|
|
404
|
+
* When `collection` is provided, authenticates against
|
|
405
|
+
* `/{collection}/auth-with-password`. Otherwise logs in as superuser.
|
|
406
|
+
* Stores the returned token in the auth store.
|
|
407
|
+
*
|
|
408
|
+
* @param email User email or identity.
|
|
409
|
+
* @param password User password.
|
|
410
|
+
* @param collection Optional auth collection name.
|
|
411
|
+
*/
|
|
412
|
+
login(email: string, password: string, collection?: string): Promise<({
|
|
413
|
+
token: string;
|
|
414
|
+
} & Record<string, unknown>) | null>;
|
|
415
|
+
/** Fetch the current superuser profile and refresh the auth model. */
|
|
416
|
+
me<T = ApiRecord>(options?: RequestOptions): Promise<T | null>;
|
|
417
|
+
/**
|
|
418
|
+
* Authenticate against an auth collection with email/password.
|
|
419
|
+
* Stores the returned token and user record in the auth store.
|
|
420
|
+
*
|
|
421
|
+
* @param collection The auth collection name.
|
|
422
|
+
* @param identity Email or username.
|
|
423
|
+
* @param password Password.
|
|
424
|
+
* @param options Optional request options.
|
|
425
|
+
*/
|
|
426
|
+
authWithPassword(collection: string, identity: string, password: string, options?: RequestOptions): Promise<({
|
|
427
|
+
token: string;
|
|
428
|
+
record: ApiRecord;
|
|
429
|
+
} & Record<string, unknown>) | null>;
|
|
430
|
+
/**
|
|
431
|
+
* Refresh an auth collection token.
|
|
432
|
+
* Uses the currently stored auth token.
|
|
433
|
+
*
|
|
434
|
+
* @param collection The auth collection name.
|
|
435
|
+
* @param options Optional request options.
|
|
436
|
+
*/
|
|
437
|
+
authRefresh(collection: string, options?: RequestOptions): Promise<({
|
|
438
|
+
token: string;
|
|
439
|
+
record: ApiRecord;
|
|
440
|
+
} & Record<string, unknown>) | null>;
|
|
441
|
+
/** Clear the current auth state and remove persisted tokens. */
|
|
442
|
+
logout(): void;
|
|
443
|
+
/** Ping the API health endpoint. */
|
|
444
|
+
health(options?: RequestOptions): Promise<Record<string, unknown> | null>;
|
|
445
|
+
/**
|
|
446
|
+
* List all collections (admin).
|
|
447
|
+
* @param q URL query string (e.g. `page=1&perPage=200`).
|
|
448
|
+
* @param options Optional request options.
|
|
449
|
+
*/
|
|
450
|
+
listCollections(q?: string, options?: RequestOptions): Promise<ListResult<ApiRecord> | null>;
|
|
451
|
+
/**
|
|
452
|
+
* Get a single collection by ID or name.
|
|
453
|
+
* @param id Collection ID or name.
|
|
454
|
+
* @param options Optional request options.
|
|
455
|
+
*/
|
|
456
|
+
getCollection(id: string, options?: RequestOptions): Promise<ApiRecord | null>;
|
|
457
|
+
/**
|
|
458
|
+
* Create a new collection (admin).
|
|
459
|
+
* @param data Collection definition (name, type, fields, options, rules, etc.).
|
|
460
|
+
* @param options Optional request options.
|
|
461
|
+
*/
|
|
462
|
+
createCollection(data: Record<string, unknown>, options?: RequestOptions): Promise<ApiRecord | null>;
|
|
463
|
+
/**
|
|
464
|
+
* Update an existing collection (admin).
|
|
465
|
+
* @param id Collection ID or name.
|
|
466
|
+
* @param data Updated collection fields.
|
|
467
|
+
* @param options Optional request options.
|
|
468
|
+
*/
|
|
469
|
+
updateCollection(id: string, data: Record<string, unknown>, options?: RequestOptions): Promise<ApiRecord | null>;
|
|
470
|
+
/**
|
|
471
|
+
* Delete a collection (admin).
|
|
472
|
+
* @param id Collection ID or name.
|
|
473
|
+
* @param options Optional request options.
|
|
474
|
+
*/
|
|
475
|
+
deleteCollection(id: string, options?: RequestOptions): Promise<null>;
|
|
476
|
+
/**
|
|
477
|
+
* List records from a dynamic collection with optional filter/sort/pagination.
|
|
478
|
+
*
|
|
479
|
+
* @param coll Collection name.
|
|
480
|
+
* @param params Query parameters including:
|
|
481
|
+
* - `filter` — PocketBase filter syntax (e.g. `title~'hello' && published=true`)
|
|
482
|
+
* - `sort` — Comma-separated, `-` prefix for DESC (e.g. `-created,title`)
|
|
483
|
+
* - `page` — Page number (default: 1)
|
|
484
|
+
* - `perPage` — Items per page (default: 30, max: 200)
|
|
485
|
+
* - `expand` — Comma-separated relation fields (e.g. `author,category`)
|
|
486
|
+
* @param options Optional request options.
|
|
487
|
+
*/
|
|
488
|
+
listRecords(coll: string, params?: Record<string, string>, options?: RequestOptions): Promise<ListResult<ApiRecord> | null>;
|
|
489
|
+
/**
|
|
490
|
+
* Get a single record by ID.
|
|
491
|
+
* @param coll Collection name.
|
|
492
|
+
* @param id Record ID.
|
|
493
|
+
* @param options Optional request options.
|
|
494
|
+
*/
|
|
495
|
+
getRecord(coll: string, id: string, options?: RequestOptions): Promise<ApiRecord | null>;
|
|
496
|
+
/**
|
|
497
|
+
* Create a record in a dynamic collection.
|
|
498
|
+
* @param coll Collection name.
|
|
499
|
+
* @param data Record fields.
|
|
500
|
+
* @param options Optional request options.
|
|
501
|
+
*/
|
|
502
|
+
createRecord(coll: string, data: Record<string, unknown>, options?: RequestOptions): Promise<ApiRecord | null>;
|
|
503
|
+
/**
|
|
504
|
+
* Update a record in a dynamic collection.
|
|
505
|
+
* @param coll Collection name.
|
|
506
|
+
* @param id Record ID.
|
|
507
|
+
* @param data Updated record fields.
|
|
508
|
+
* @param options Optional request options.
|
|
509
|
+
*/
|
|
510
|
+
updateRecord(coll: string, id: string, data: Record<string, unknown>, options?: RequestOptions): Promise<ApiRecord | null>;
|
|
511
|
+
/**
|
|
512
|
+
* Delete a record from a dynamic collection.
|
|
513
|
+
* @param coll Collection name.
|
|
514
|
+
* @param id Record ID.
|
|
515
|
+
* @param options Optional request options.
|
|
516
|
+
*/
|
|
517
|
+
deleteRecord(coll: string, id: string, options?: RequestOptions): Promise<null>;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
export { ApiError, type ApiRecord, type AuthModel, AuthStore, type FileRecord, FilesService, LazypockClient, type LazypockClientOptions, type ListResult, RealtimeService, type RequestOptions, type StorageAdapter, getFileUrl, wsUrlFromBaseUrl };
|