@lyeve-labs/client 0.2.1 → 0.2.2
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 +11 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +20 -14
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @lyeve/
|
|
1
|
+
# @lyeve-labs/client
|
|
2
2
|
|
|
3
3
|
Framework-agnostic HTTP client for the LyEve Core API. The foundation all other
|
|
4
4
|
SDK packages build on.
|
|
@@ -7,12 +7,12 @@ SDK packages build on.
|
|
|
7
7
|
[](https://www.typescriptlang.org)
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
pnpm add @lyeve/
|
|
10
|
+
pnpm add @lyeve-labs/client
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
-
import { createClient } from "@lyeve/
|
|
15
|
-
import { getSchemas } from "@lyeve/
|
|
14
|
+
import { createClient } from "@lyeve-labs/client";
|
|
15
|
+
import { getSchemas } from "@lyeve-labs/client-rest";
|
|
16
16
|
|
|
17
17
|
const client = createClient(fetch, { Authorization: "Bearer <token>" });
|
|
18
18
|
const schemas = await getSchemas(client);
|
|
@@ -40,15 +40,15 @@ Zero dependencies. Native `fetch`. One client, every transport.
|
|
|
40
40
|
## Install
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
pnpm add @lyeve/
|
|
44
|
-
# or npm install @lyeve/
|
|
45
|
-
# or yarn add @lyeve/
|
|
43
|
+
pnpm add @lyeve-labs/client
|
|
44
|
+
# or npm install @lyeve-labs/client
|
|
45
|
+
# or yarn add @lyeve-labs/client
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
## Use
|
|
49
49
|
|
|
50
50
|
```ts
|
|
51
|
-
import { createClient, PaginationIterator } from "@lyeve/
|
|
51
|
+
import { createClient, PaginationIterator } from "@lyeve-labs/client";
|
|
52
52
|
|
|
53
53
|
const client = createClient(fetch, {
|
|
54
54
|
Authorization: "Bearer <token>",
|
|
@@ -70,7 +70,7 @@ for await (const page of new PaginationIterator((cursor) =>
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// Retry
|
|
73
|
-
import { createRetryFetch } from "@lyeve/
|
|
73
|
+
import { createRetryFetch } from "@lyeve-labs/client";
|
|
74
74
|
const resilient = createRetryFetch(fetch, {
|
|
75
75
|
maxAttempts: 3,
|
|
76
76
|
baseDelay: 200,
|
|
@@ -78,7 +78,7 @@ const resilient = createRetryFetch(fetch, {
|
|
|
78
78
|
const retryClient = createClient(resilient);
|
|
79
79
|
|
|
80
80
|
// Dedup
|
|
81
|
-
import { RequestDeduplicator } from "@lyeve/
|
|
81
|
+
import { RequestDeduplicator } from "@lyeve-labs/client";
|
|
82
82
|
const dedup = new RequestDeduplicator();
|
|
83
83
|
const [a, b] = await Promise.all([
|
|
84
84
|
dedup.dedup("key-1", () => client.get("/api/v1/data")),
|
|
@@ -146,7 +146,7 @@ tests/ # vitest test suite
|
|
|
146
146
|
|
|
147
147
|
## Versioning
|
|
148
148
|
|
|
149
|
-
`@lyeve/
|
|
149
|
+
`@lyeve-labs/client` follows [SemVer](https://semver.org). While under `1.0`,
|
|
150
150
|
breaking changes bump the **minor** version; additive changes bump the **patch**.
|
|
151
151
|
Every release is logged in [`CHANGELOG.md`](CHANGELOG.md).
|
|
152
152
|
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/pagination.ts","../src/query-builder.ts","../src/retry.ts","../src/dedupe.ts"],"sourcesContent":["/**\n * LyEve CMS Client : Framework-agnostic TypeScript SDK core.\n *\n * Provides the HTTP client factory, domain types, and utilities.\n * Protocol-specific packages (@lyeve/cms-client-rest,\n * @lyeve/cms-client-graphql, etc.) build on this core.\n *\n * @example\n * ```ts\n * import { createClient } from '@lyeve/cms-client';\n * import { getSchemas } from '@lyeve/cms-client-rest';\n *\n * const client = createClient(fetch, { Authorization: 'Bearer xxx' });\n * const schemas = await getSchemas(client);\n * ```\n *\n * @packageDocumentation\n */\n\n// Core client\nexport { createClient, ApiError } from \"./client.js\";\nexport type { HttpClient } from \"./client.js\";\n\n// Domain types\nexport type {\n Schema,\n SchemaField,\n FieldType,\n Content,\n User,\n APIKey,\n CreateAPIKeyResponse,\n Webhook,\n WebhookDelivery,\n WebhookTestResult,\n RetryDeliveryResult,\n RetryConfig,\n RetryConfigInput,\n DeadLetter,\n DLQStatus,\n PaginatedResponse,\n ListResponse,\n WebhookHealthStats,\n GlobalHealthStats,\n IncomingWebhook,\n OAuthProvider,\n Permission,\n Entitlements,\n} from \"./types.js\";\n\n// Pagination iterator\nexport { PaginationIterator } from \"./pagination.js\";\nexport type {\n PaginationConfig,\n PageFetcher,\n CursorPage,\n} from \"./pagination.js\";\n\n// Query builder\nexport { QueryBuilder, query } from \"./query-builder.js\";\nexport type { ContentQuery, ContentStatus } from \"./query-builder.js\";\n\n// Retry wrapper\nexport { createRetryFetch } from \"./retry.js\";\nexport type { RetryConfig as RetryFetchConfig } from \"./retry.js\";\n\n// Request deduplication\nexport { RequestDeduplicator } from \"./dedupe.js\";\n","/**\n * Framework-agnostic HTTP client for the LyEve CMS API.\n *\n * The `createClient` factory accepts any `fetch`-compatible function\n * (globalThis.fetch, SvelteKit's event.fetch, a Node.js polyfill) and\n * returns a typed client with get/post/put/delete methods that handle\n * JSON serialization, error mapping, and timeout.\n */\n\nexport class ApiError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = \"ApiError\";\n }\n}\n\nexport type HttpClient = ReturnType<typeof createClient>;\n\nexport function createClient(\n fetchFn: typeof fetch,\n defaultHeaders: Record<string, string> = {},\n) {\n async function request<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetchFn(url, {\n ...init,\n // Default 15 s timeout so a slow/unresponsive backend can't hang\n // the caller indefinitely. Callers may override by passing their\n // own AbortSignal.\n signal: init?.signal ?? AbortSignal.timeout(15_000),\n headers: {\n \"Content-Type\": \"application/json\",\n ...defaultHeaders,\n ...(init?.headers instanceof Headers\n ? Object.fromEntries(init.headers.entries())\n : Array.isArray(init?.headers)\n ? Object.fromEntries(init.headers)\n : init?.headers),\n },\n });\n\n if (!res.ok) {\n const text = await res.text();\n let message = text;\n try {\n const json = JSON.parse(text) as { error?: string };\n message = json.error ?? text;\n } catch {\n // use raw text\n }\n throw new ApiError(res.status, message);\n }\n\n if (res.status === 204) return undefined as T;\n return res.json() as Promise<T>;\n }\n\n return {\n get: <T>(url: string, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"GET\" }),\n post: <T>(url: string, body: unknown, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"POST\", body: JSON.stringify(body) }),\n put: <T>(url: string, body: unknown, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"PUT\", body: JSON.stringify(body) }),\n patch: <T>(url: string, body: unknown, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"PATCH\", body: JSON.stringify(body) }),\n delete: <T>(url: string, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"DELETE\" }),\n };\n}\n","/**\n * Async iterator for cursor-based content pagination.\n *\n * Walks the Content API's cursor endpoint, yielding records one at a time.\n * Each call to `.next()` returns the next record.\n *\n * ## Usage\n * ```ts\n * const paginator = new PaginationIterator<MyRecord>({\n * fetchPage: (cursor) => api.getPage(cursor),\n * });\n *\n * for await (const record of paginator) {\n * console.log(record);\n * }\n *\n * // With AbortController:\n * const ac = new AbortController();\n * setTimeout(() => ac.abort(), 5000);\n * for await (const record of paginator.withSignal(ac.signal)) {\n * // stops after 5 seconds\n * }\n * ```\n */\n\n/** Response shape from a cursor-paginated endpoint. */\nexport interface CursorPage<T> {\n items: T[];\n /** Present when more pages are available. */\n next_cursor?: string;\n}\n\n/** Function that fetches a single cursor page. */\nexport type PageFetcher<T> = (cursor?: string) => Promise<CursorPage<T>>;\n\nexport interface PaginationConfig<T> {\n /** Function that fetches a single cursor page. */\n fetchPage: PageFetcher<T>;\n}\n\n/**\n * Async iterable iterator over paginated records.\n *\n * Implements `AsyncIterableIterator<T>` so it can be used directly in\n * `for await...of` loops. Also exposes `.withSignal()` for AbortController\n * integration.\n */\nexport class PaginationIterator<T> implements AsyncIterableIterator<T> {\n #fetchPage: PageFetcher<T>;\n #signal?: AbortSignal;\n\n // Internal paging state\n #buffer: T[] = [];\n #cursor: string | undefined = undefined;\n #exhausted = false;\n #fetching: Promise<void> | null = null;\n\n constructor(config: PaginationConfig<T>) {\n this.#fetchPage = config.fetchPage;\n }\n\n /**\n * Return a new iterator sharing the same underlying state but with a\n * different AbortSignal. Useful when the signal is only known at the\n * call site, not at construction time.\n */\n withSignal(signal: AbortSignal): PaginationIterator<T> {\n const clone = new PaginationIterator<T>({\n fetchPage: this.#fetchPage,\n });\n clone.#signal = signal;\n clone.#buffer = this.#buffer;\n clone.#cursor = this.#cursor;\n clone.#exhausted = this.#exhausted;\n clone.#fetching = this.#fetching;\n return clone;\n }\n\n // AsyncIterator protocol\n\n [Symbol.asyncIterator](): AsyncIterableIterator<T> {\n return this;\n }\n\n async next(): Promise<IteratorResult<T>> {\n this.#throwIfAborted();\n\n // Yield from buffer if we have cached records\n if (this.#buffer.length > 0) {\n const value = this.#buffer.shift()!;\n return { value, done: false };\n }\n\n // Done if exhausted\n if (this.#exhausted) {\n return { value: undefined, done: true };\n }\n\n // Fetch next page\n try {\n await this.#fetchPageInternal();\n } catch (err) {\n if (err instanceof Error) throw err;\n throw new Error(String(err));\n }\n\n if (this.#buffer.length > 0) {\n const value = this.#buffer.shift()!;\n return { value, done: false };\n }\n\n return { value: undefined, done: true };\n }\n\n return?(value?: unknown): Promise<IteratorResult<T>> {\n this.#exhausted = true;\n this.#buffer.length = 0;\n return Promise.resolve({\n value: value as T | undefined,\n done: true,\n });\n }\n\n throw?(e?: unknown): Promise<IteratorResult<T>> {\n this.#exhausted = true;\n this.#buffer.length = 0;\n return Promise.reject(e);\n }\n\n // Internal\n\n async #fetchPageInternal(): Promise<void> {\n if (this.#fetching) {\n await this.#fetching;\n return;\n }\n\n this.#throwIfAborted();\n\n this.#fetching = this.#fetchPage(this.#cursor)\n .then((page) => {\n this.#throwIfAborted();\n this.#buffer = [...page.items];\n if (page.next_cursor) {\n this.#cursor = page.next_cursor;\n } else {\n this.#exhausted = true;\n }\n })\n .finally(() => {\n this.#fetching = null;\n });\n\n await this.#fetching;\n }\n\n #throwIfAborted(): void {\n if (this.#signal?.aborted) {\n throw this.#signal.reason ?? new DOMException(\"Aborted\", \"AbortError\");\n }\n }\n}\n","/**\n * Fluent query builder for LyEve CMS Content API queries.\n *\n * Produces ContentQuery objects with a chainable API.\n *\n * ## Usage\n * ```ts\n * import { query } from '@lyeve/cms-client';\n *\n * const q = query('posts')\n * .where('status', 'eq:published')\n * .sort('-created_at')\n * .limit(20)\n * .build();\n * ```\n */\n\nexport type ContentStatus = \"draft\" | \"published\" | \"archived\";\n\nexport interface ContentQuery {\n /** Filter by status. */\n status?: ContentStatus;\n /** Max records per page. */\n limit?: number;\n /** Offset for offset-based pagination. Mutually exclusive with cursor. */\n offset?: number;\n /** Cursor for cursor-based pagination. Mutually exclusive with offset. */\n cursor?: string;\n /** Field-level filters in `field=op:value` format. */\n filters?: Record<string, string>;\n /** Sort field(s), comma-separated. Prefix with `-` for descending. */\n sort?: string;\n}\n\n/**\n * Builder for ContentQuery objects.\n *\n * Each field can only hold one filter (Record<string, string>). Calling\n * where() (or whereEq/whereGt etc.) with an already-set field name\n * overwrites the previous filter. For range queries on the same field,\n * use the server's range filter syntax in a single call.\n */\nexport class QueryBuilder {\n #schema: string;\n #status?: ContentStatus;\n #limit?: number;\n #offset?: number;\n #cursor?: string;\n #filters: Record<string, string> = {};\n #sort?: string;\n\n constructor(schema: string) {\n this.#schema = schema;\n }\n\n /** The schema/collection this query targets. */\n get schema(): string {\n return this.#schema;\n }\n\n /** Filter by content status (published, draft, archived). */\n whereStatus(s: ContentStatus | undefined): this {\n this.#status = s;\n return this;\n }\n\n /** Add a field filter in `op:value` format. */\n where(field: string, value: string): this {\n this.#filters[field] = value;\n return this;\n }\n\n /** Add a field-level equality filter. */\n whereEq(field: string, value: string | number | boolean): this {\n this.#filters[field] = `eq:${String(value)}`;\n return this;\n }\n\n /** Add a field-level \"in\" filter (value is a JSON array). */\n whereIn(field: string, values: (string | number)[]): this {\n this.#filters[field] = `in:${JSON.stringify(values)}`;\n return this;\n }\n\n /** Greater-than comparison. */\n whereGt(field: string, value: number): this {\n this.#filters[field] = `gt:${value}`;\n return this;\n }\n\n /** Greater-than-or-equal comparison. */\n whereGte(field: string, value: number): this {\n this.#filters[field] = `gte:${value}`;\n return this;\n }\n\n /** Less-than comparison. */\n whereLt(field: string, value: number): this {\n this.#filters[field] = `lt:${value}`;\n return this;\n }\n\n /** Less-than-or-equal comparison. */\n whereLte(field: string, value: number): this {\n this.#filters[field] = `lte:${value}`;\n return this;\n }\n\n /** Full-text search (if the schema supports it). */\n whereSearch(field: string, query: string): this {\n this.#filters[field] = `search:${query}`;\n return this;\n }\n\n /** Set the max records per page. */\n limit(n: number | undefined): this {\n this.#limit = n;\n return this;\n }\n\n /** Set the offset for offset-based pagination. Clears cursor. */\n offset(n: number | undefined): this {\n this.#offset = n;\n if (n !== undefined) this.#cursor = undefined;\n return this;\n }\n\n /** Set the cursor for cursor-based pagination. Clears offset. */\n cursor(c: string | undefined): this {\n this.#cursor = c;\n if (c !== undefined) this.#offset = undefined;\n return this;\n }\n\n /** Set sort fields. Prefix with `-` for descending. */\n sort(...fields: string[]): this {\n this.#sort = fields.join(\",\");\n return this;\n }\n\n /** Clear all filters, keeping only the schema name. */\n resetFilters(): this {\n this.#filters = {};\n this.#status = undefined;\n this.#sort = undefined;\n this.#limit = undefined;\n this.#offset = undefined;\n this.#cursor = undefined;\n return this;\n }\n\n /** Produce the ContentQuery object. */\n build(): ContentQuery {\n return {\n status: this.#status,\n limit: this.#limit,\n offset: this.#offset,\n cursor: this.#cursor,\n filters:\n Object.keys(this.#filters).length > 0\n ? { ...this.#filters }\n : undefined,\n sort: this.#sort,\n };\n }\n}\n\n/**\n * Create a new query builder for the given schema.\n * Equivalent to `new QueryBuilder(schema)`.\n */\nexport function query(schema: string): QueryBuilder {\n return new QueryBuilder(schema);\n}\n","/**\n * Automatic retry with exponential backoff and jitter.\n *\n * Wraps fetch to retry on transient errors (429, 5xx, network failures).\n * Uses \"full jitter\" backoff: `random(0, min(cap, base * 2^attempt))`.\n *\n * Usage:\n * const fetchWithRetry = createRetryFetch(fetch, {\n * maxRetries: 3,\n * baseDelay: 1000,\n * maxDelay: 30000,\n * retryOn: [429, 500, 502, 503, 504],\n * onRetry: (attempt, err, delay) => console.warn(`Retry ${attempt} in ${delay}ms`),\n * });\n * const res = await fetchWithRetry(url, init);\n */\n\nexport interface RetryConfig {\n /** Maximum number of retry attempts (default: 3). */\n maxRetries?: number;\n /** Base delay in ms before first retry (default: 1000). */\n baseDelay?: number;\n /** Maximum delay in ms between retries (default: 30000). */\n maxDelay?: number;\n /** HTTP status codes that trigger a retry (default: [429, 500, 502, 503, 504]). */\n retryOn?: number[];\n /** If true, retry on network/abort errors as well (default: true). */\n retryOnNetworkError?: boolean;\n /** Called before each retry with (attempt, error, delayMs). */\n onRetry?: (attempt: number, error: unknown, delayMs: number) => void;\n}\n\nconst DEFAULT_RETRY_ON = [429, 500, 502, 503, 504];\n\nfunction jitter(cap: number): number {\n return Math.floor(Math.random() * cap);\n}\n\nfunction backoff(attempt: number, base: number, max: number): number {\n return jitter(Math.min(max, base * Math.pow(2, attempt)));\n}\n\n/** Sleep for `ms` milliseconds, respecting an AbortSignal. */\nasync function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n return;\n }\n const timer = setTimeout(resolve, ms);\n if (!signal) return;\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Create a fetch wrapper that automatically retries on transient failures.\n *\n * Retry logic:\n * 1. If the response status is in `retryOn`, read+discard the body, then retry.\n * 2. If the request threw a network/abort error and `retryOnNetworkError` is true,\n * retry.\n * 3. Exponential backoff with full jitter.\n * 4. Respects AbortSignal : aborts cancel the current attempt and skip remaining\n * retries.\n */\nexport function createRetryFetch(\n fetchImpl: typeof globalThis.fetch,\n config?: RetryConfig,\n): typeof globalThis.fetch {\n const maxRetries = config?.maxRetries ?? 3;\n const baseDelay = config?.baseDelay ?? 1000;\n const maxDelay = config?.maxDelay ?? 30000;\n const retryOn = config?.retryOn ?? DEFAULT_RETRY_ON;\n const retryOnNetworkError = config?.retryOnNetworkError ?? true;\n\n return async (input: RequestInfo | URL, init?: RequestInit) => {\n let lastError: unknown;\n const signal = init?.signal;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n let resolvedInit = init;\n if (attempt > 0 && init?.body) {\n resolvedInit = { ...init, body: await cloneBody(init.body) };\n }\n\n const res = await fetchImpl(input, resolvedInit);\n\n if (!retryOn.includes(res.status) || attempt === maxRetries) {\n return res;\n }\n\n // Drain the body so the connection can be reused\n await drainBody(res);\n lastError = new Error(`HTTP ${res.status}`);\n } catch (err) {\n if (signal?.aborted) throw err;\n\n if (!retryOnNetworkError || attempt === maxRetries) throw err;\n lastError = err;\n }\n\n const delay = backoff(attempt, baseDelay, maxDelay);\n config?.onRetry?.(attempt + 1, lastError, delay);\n await sleep(delay, signal ?? undefined);\n }\n\n throw lastError;\n };\n}\n\nasync function cloneBody(body: BodyInit | null): Promise<BodyInit | null> {\n if (body === null || body === undefined) return null;\n if (typeof body === \"string\") return body;\n if (body instanceof URLSearchParams) return new URLSearchParams(body);\n if (body instanceof FormData) return body;\n if (body instanceof Blob) return body;\n if (body instanceof ArrayBuffer) return body.slice(0);\n if (body instanceof ReadableStream) {\n throw new Error(\n \"Cannot retry a request with a ReadableStream body. Use string or JSON.\",\n );\n }\n if (ArrayBuffer.isView(body)) {\n return new Uint8Array(\n body.buffer,\n body.byteOffset,\n body.byteLength,\n ).slice();\n }\n return body;\n}\n\nasync function drainBody(res: Response): Promise<void> {\n try {\n await res.body?.cancel();\n } catch {\n // Best effort\n }\n}\n","/**\n * In-flight request deduplication.\n *\n * When multiple callers request the same URL+method+body combination\n * concurrently, only one network request is made. All callers receive\n * the same response (the promise is shared).\n *\n * Cache entries are evicted when the shared promise settles (success or\n * failure). Late subscribers that arrive after eviction start fresh.\n */\n\n/**\n * Deduplicator for in-flight HTTP requests.\n *\n * ## Usage\n * ```ts\n * const dedupe = new RequestDeduplicator();\n *\n * async function fetchDeduped(url: string, init?: RequestInit): Promise<Response> {\n * const method = init?.method ?? 'GET';\n * const key = `${method}:${url}:${JSON.stringify(init?.body ?? '')}`;\n * return dedupe.dedup(key, () => fetch(url, init), init?.signal);\n * }\n * ```\n */\nexport class RequestDeduplicator {\n #pending = new Map<string, Promise<unknown>>();\n\n /**\n * Execute `factory` once for the given key. Concurrent callers with\n * the same key receive the same promise. The entry is evicted after\n * the shared promise settles.\n *\n * @param key - Unique request key (method + URL + stable body hash).\n * @param factory - The network operation to perform.\n * @param signal - Optional AbortSignal. If it fires, this caller gets\n * an AbortError. The shared request continues.\n */\n async dedup<T>(\n key: string,\n factory: () => Promise<T>,\n signal?: AbortSignal,\n ): Promise<T> {\n const existing = this.#pending.get(key);\n if (existing !== undefined) {\n return this.#raceWithSignal(existing as Promise<T>, signal);\n }\n\n const promise = factory().finally(() => {\n this.#pending.delete(key);\n });\n this.#pending.set(key, promise);\n\n return this.#raceWithSignal(promise, signal);\n }\n\n /** How many unique requests are currently in flight. */\n get inflight(): number {\n return this.#pending.size;\n }\n\n /** Remove all pending deduplication entries. */\n clear(): void {\n this.#pending.clear();\n }\n\n /**\n * Race a promise against an AbortSignal. If the signal fires first,\n * the caller gets an AbortError but the promise still settles normally.\n */\n #raceWithSignal<T>(p: Promise<T>, s?: AbortSignal): Promise<T> {\n if (!s) return p;\n if (s.aborted) {\n return Promise.reject(\n s.reason ?? new DOMException(\"Aborted\", \"AbortError\"),\n );\n }\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n reject(s.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n };\n s.addEventListener(\"abort\", onAbort, { once: true });\n p.then(\n (v) => {\n s.removeEventListener(\"abort\", onAbort);\n resolve(v);\n },\n (e) => {\n s.removeEventListener(\"abort\", onAbort);\n reject(e);\n },\n );\n });\n }\n}\n"],"mappings":"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","names":["query"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/pagination.ts","../src/query-builder.ts","../src/retry.ts","../src/dedupe.ts"],"sourcesContent":["/**\n * LyEve CMS Client : Framework-agnostic TypeScript SDK core.\n *\n * Provides the HTTP client factory, domain types, and utilities.\n * Protocol-specific packages (@lyeve-labs/client-rest,\n * @lyeve-labs/client-graphql, etc.) build on this core.\n *\n * @example\n * ```ts\n * import { createClient } from '@lyeve-labs/client';\n * import { getSchemas } from '@lyeve-labs/client-rest';\n *\n * const client = createClient(fetch, { Authorization: 'Bearer xxx' });\n * const schemas = await getSchemas(client);\n * ```\n *\n * @packageDocumentation\n */\n\n// Core client\nexport { createClient, ApiError } from \"./client.js\";\nexport type { HttpClient } from \"./client.js\";\n\n// Domain types\nexport type {\n Schema,\n SchemaField,\n FieldType,\n Content,\n User,\n APIKey,\n CreateAPIKeyResponse,\n Webhook,\n WebhookDelivery,\n WebhookTestResult,\n RetryDeliveryResult,\n RetryConfig,\n RetryConfigInput,\n DeadLetter,\n DLQStatus,\n PaginatedResponse,\n ListResponse,\n WebhookHealthStats,\n GlobalHealthStats,\n IncomingWebhook,\n OAuthProvider,\n Permission,\n Entitlements,\n} from \"./types.js\";\n\n// Pagination iterator\nexport { PaginationIterator } from \"./pagination.js\";\nexport type {\n PaginationConfig,\n PageFetcher,\n CursorPage,\n} from \"./pagination.js\";\n\n// Query builder\nexport { QueryBuilder, query } from \"./query-builder.js\";\nexport type { ContentQuery, ContentStatus } from \"./query-builder.js\";\n\n// Retry wrapper\nexport { createRetryFetch } from \"./retry.js\";\nexport type { RetryConfig as RetryFetchConfig } from \"./retry.js\";\n\n// Request deduplication\nexport { RequestDeduplicator } from \"./dedupe.js\";\n","/**\n * Framework-agnostic HTTP client for the LyEve CMS API.\n *\n * The `createClient` factory accepts any `fetch`-compatible function\n * (globalThis.fetch, SvelteKit's event.fetch, a Node.js polyfill) and\n * returns a typed client with get/post/put/delete methods that handle\n * JSON serialization, error mapping, and timeout.\n */\n\nexport class ApiError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = \"ApiError\";\n }\n}\n\nexport type HttpClient = ReturnType<typeof createClient>;\n\nexport function createClient(\n fetchFn: typeof fetch,\n defaultHeaders: Record<string, string> = {},\n) {\n async function request<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetchFn(url, {\n ...init,\n // Default 15 s timeout so a slow/unresponsive backend can't hang\n // the caller indefinitely. Callers may override by passing their\n // own AbortSignal.\n signal: init?.signal ?? AbortSignal.timeout(15_000),\n headers: {\n \"Content-Type\": \"application/json\",\n ...defaultHeaders,\n ...(init?.headers instanceof Headers\n ? Object.fromEntries(init.headers.entries())\n : Array.isArray(init?.headers)\n ? Object.fromEntries(init.headers)\n : init?.headers),\n },\n });\n\n if (!res.ok) {\n const text = await res.text();\n let message = text;\n try {\n const json = JSON.parse(text) as { error?: string };\n message = json.error ?? text;\n } catch {\n // use raw text\n }\n throw new ApiError(res.status, message);\n }\n\n if (res.status === 204) return undefined as T;\n return res.json() as Promise<T>;\n }\n\n return {\n get: <T>(url: string, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"GET\" }),\n post: <T>(url: string, body: unknown, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"POST\", body: JSON.stringify(body) }),\n put: <T>(url: string, body: unknown, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"PUT\", body: JSON.stringify(body) }),\n patch: <T>(url: string, body: unknown, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"PATCH\", body: JSON.stringify(body) }),\n delete: <T>(url: string, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"DELETE\" }),\n };\n}\n","/**\n * Async iterator for cursor-based content pagination.\n *\n * Walks the Content API's cursor endpoint, yielding records one at a time.\n * Each call to `.next()` returns the next record.\n *\n * ## Usage\n * ```ts\n * const paginator = new PaginationIterator<MyRecord>({\n * fetchPage: (cursor) => api.getPage(cursor),\n * });\n *\n * for await (const record of paginator) {\n * console.log(record);\n * }\n *\n * // With AbortController:\n * const ac = new AbortController();\n * setTimeout(() => ac.abort(), 5000);\n * for await (const record of paginator.withSignal(ac.signal)) {\n * // stops after 5 seconds\n * }\n * ```\n */\n\n/** Response shape from a cursor-paginated endpoint. */\nexport interface CursorPage<T> {\n items: T[];\n /** Present when more pages are available. */\n next_cursor?: string;\n}\n\n/** Function that fetches a single cursor page. */\nexport type PageFetcher<T> = (cursor?: string) => Promise<CursorPage<T>>;\n\nexport interface PaginationConfig<T> {\n /** Function that fetches a single cursor page. */\n fetchPage: PageFetcher<T>;\n}\n\n/**\n * Async iterable iterator over paginated records.\n *\n * Implements `AsyncIterableIterator<T>` so it can be used directly in\n * `for await...of` loops. Also exposes `.withSignal()` for AbortController\n * integration.\n */\nexport class PaginationIterator<T> implements AsyncIterableIterator<T> {\n #fetchPage: PageFetcher<T>;\n #signal?: AbortSignal;\n\n // Internal paging state\n #buffer: T[] = [];\n #cursor: string | undefined = undefined;\n #exhausted = false;\n #fetching: Promise<void> | null = null;\n\n constructor(config: PaginationConfig<T>) {\n this.#fetchPage = config.fetchPage;\n }\n\n /**\n * Return a new iterator sharing the same underlying state but with a\n * different AbortSignal. Useful when the signal is only known at the\n * call site, not at construction time.\n */\n withSignal(signal: AbortSignal): PaginationIterator<T> {\n const clone = new PaginationIterator<T>({\n fetchPage: this.#fetchPage,\n });\n clone.#signal = signal;\n clone.#buffer = this.#buffer;\n clone.#cursor = this.#cursor;\n clone.#exhausted = this.#exhausted;\n clone.#fetching = this.#fetching;\n return clone;\n }\n\n // AsyncIterator protocol\n\n [Symbol.asyncIterator](): AsyncIterableIterator<T> {\n return this;\n }\n\n async next(): Promise<IteratorResult<T>> {\n this.#throwIfAborted();\n\n // Yield from buffer if we have cached records\n if (this.#buffer.length > 0) {\n const value = this.#buffer.shift()!;\n return { value, done: false };\n }\n\n // Done if exhausted\n if (this.#exhausted) {\n return { value: undefined, done: true };\n }\n\n // Fetch next page\n try {\n await this.#fetchPageInternal();\n } catch (err) {\n if (err instanceof Error) throw err;\n throw new Error(String(err));\n }\n\n if (this.#buffer.length > 0) {\n const value = this.#buffer.shift()!;\n return { value, done: false };\n }\n\n return { value: undefined, done: true };\n }\n\n return?(value?: unknown): Promise<IteratorResult<T>> {\n this.#exhausted = true;\n this.#buffer.length = 0;\n return Promise.resolve({\n value: value as T | undefined,\n done: true,\n });\n }\n\n throw?(e?: unknown): Promise<IteratorResult<T>> {\n this.#exhausted = true;\n this.#buffer.length = 0;\n return Promise.reject(e);\n }\n\n // Internal\n\n async #fetchPageInternal(): Promise<void> {\n if (this.#fetching) {\n await this.#fetching;\n return;\n }\n\n this.#throwIfAborted();\n\n this.#fetching = this.#fetchPage(this.#cursor)\n .then((page) => {\n this.#throwIfAborted();\n this.#buffer = [...page.items];\n if (page.next_cursor) {\n this.#cursor = page.next_cursor;\n } else {\n this.#exhausted = true;\n }\n })\n .finally(() => {\n this.#fetching = null;\n });\n\n await this.#fetching;\n }\n\n #throwIfAborted(): void {\n if (this.#signal?.aborted) {\n throw this.#signal.reason ?? new DOMException(\"Aborted\", \"AbortError\");\n }\n }\n}\n","/**\n * Fluent query builder for LyEve CMS Content API queries.\n *\n * Produces ContentQuery objects with a chainable API.\n *\n * ## Usage\n * ```ts\n * import { query } from '@lyeve-labs/client';\n *\n * const q = query('posts')\n * .where('status', 'eq:published')\n * .sort('-created_at')\n * .limit(20)\n * .build();\n * ```\n */\n\nexport type ContentStatus = \"draft\" | \"published\" | \"archived\";\n\nexport interface ContentQuery {\n /** Filter by status. */\n status?: ContentStatus;\n /** Max records per page. */\n limit?: number;\n /** Offset for offset-based pagination. Mutually exclusive with cursor. */\n offset?: number;\n /** Cursor for cursor-based pagination. Mutually exclusive with offset. */\n cursor?: string;\n /** Field-level filters in `field=op:value` format. */\n filters?: Record<string, string>;\n /** Sort field(s), comma-separated. Prefix with `-` for descending. */\n sort?: string;\n}\n\n/**\n * Builder for ContentQuery objects.\n *\n * Each field can only hold one filter (Record<string, string>). Calling\n * where() (or whereEq/whereGt etc.) with an already-set field name\n * overwrites the previous filter. For range queries on the same field,\n * use the server's range filter syntax in a single call.\n */\nexport class QueryBuilder {\n #schema: string;\n #status?: ContentStatus;\n #limit?: number;\n #offset?: number;\n #cursor?: string;\n #filters: Record<string, string> = {};\n #sort?: string;\n\n constructor(schema: string) {\n this.#schema = schema;\n }\n\n /** The schema/collection this query targets. */\n get schema(): string {\n return this.#schema;\n }\n\n /** Filter by content status (published, draft, archived). */\n whereStatus(s: ContentStatus | undefined): this {\n this.#status = s;\n return this;\n }\n\n /** Add a field filter in `op:value` format. */\n where(field: string, value: string): this {\n this.#filters[field] = value;\n return this;\n }\n\n /** Add a field-level equality filter. */\n whereEq(field: string, value: string | number | boolean): this {\n this.#filters[field] = `eq:${String(value)}`;\n return this;\n }\n\n /** Add a field-level \"in\" filter (value is a JSON array). */\n whereIn(field: string, values: (string | number)[]): this {\n this.#filters[field] = `in:${JSON.stringify(values)}`;\n return this;\n }\n\n /** Greater-than comparison. */\n whereGt(field: string, value: number): this {\n this.#filters[field] = `gt:${value}`;\n return this;\n }\n\n /** Greater-than-or-equal comparison. */\n whereGte(field: string, value: number): this {\n this.#filters[field] = `gte:${value}`;\n return this;\n }\n\n /** Less-than comparison. */\n whereLt(field: string, value: number): this {\n this.#filters[field] = `lt:${value}`;\n return this;\n }\n\n /** Less-than-or-equal comparison. */\n whereLte(field: string, value: number): this {\n this.#filters[field] = `lte:${value}`;\n return this;\n }\n\n /** Full-text search (if the schema supports it). */\n whereSearch(field: string, query: string): this {\n this.#filters[field] = `search:${query}`;\n return this;\n }\n\n /** Set the max records per page. */\n limit(n: number | undefined): this {\n this.#limit = n;\n return this;\n }\n\n /** Set the offset for offset-based pagination. Clears cursor. */\n offset(n: number | undefined): this {\n this.#offset = n;\n if (n !== undefined) this.#cursor = undefined;\n return this;\n }\n\n /** Set the cursor for cursor-based pagination. Clears offset. */\n cursor(c: string | undefined): this {\n this.#cursor = c;\n if (c !== undefined) this.#offset = undefined;\n return this;\n }\n\n /** Set sort fields. Prefix with `-` for descending. */\n sort(...fields: string[]): this {\n this.#sort = fields.join(\",\");\n return this;\n }\n\n /** Clear all filters, keeping only the schema name. */\n resetFilters(): this {\n this.#filters = {};\n this.#status = undefined;\n this.#sort = undefined;\n this.#limit = undefined;\n this.#offset = undefined;\n this.#cursor = undefined;\n return this;\n }\n\n /** Produce the ContentQuery object. */\n build(): ContentQuery {\n return {\n status: this.#status,\n limit: this.#limit,\n offset: this.#offset,\n cursor: this.#cursor,\n filters:\n Object.keys(this.#filters).length > 0\n ? { ...this.#filters }\n : undefined,\n sort: this.#sort,\n };\n }\n}\n\n/**\n * Create a new query builder for the given schema.\n * Equivalent to `new QueryBuilder(schema)`.\n */\nexport function query(schema: string): QueryBuilder {\n return new QueryBuilder(schema);\n}\n","/**\n * Automatic retry with exponential backoff and jitter.\n *\n * Wraps fetch to retry on transient errors (429, 5xx, network failures).\n * Uses \"full jitter\" backoff: `random(0, min(cap, base * 2^attempt))`.\n *\n * Usage:\n * const fetchWithRetry = createRetryFetch(fetch, {\n * maxRetries: 3,\n * baseDelay: 1000,\n * maxDelay: 30000,\n * retryOn: [429, 500, 502, 503, 504],\n * onRetry: (attempt, err, delay) => console.warn(`Retry ${attempt} in ${delay}ms`),\n * });\n * const res = await fetchWithRetry(url, init);\n */\n\nexport interface RetryConfig {\n /** Maximum number of retry attempts (default: 3). */\n maxRetries?: number;\n /** Base delay in ms before first retry (default: 1000). */\n baseDelay?: number;\n /** Maximum delay in ms between retries (default: 30000). */\n maxDelay?: number;\n /** HTTP status codes that trigger a retry (default: [429, 500, 502, 503, 504]). */\n retryOn?: number[];\n /** If true, retry on network/abort errors as well (default: true). */\n retryOnNetworkError?: boolean;\n /** Called before each retry with (attempt, error, delayMs). */\n onRetry?: (attempt: number, error: unknown, delayMs: number) => void;\n}\n\nconst DEFAULT_RETRY_ON = [429, 500, 502, 503, 504];\n\nfunction jitter(cap: number): number {\n return Math.floor(Math.random() * cap);\n}\n\nfunction backoff(attempt: number, base: number, max: number): number {\n return jitter(Math.min(max, base * Math.pow(2, attempt)));\n}\n\n/** Sleep for `ms` milliseconds, respecting an AbortSignal. */\nasync function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n return;\n }\n const timer = setTimeout(resolve, ms);\n if (!signal) return;\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Create a fetch wrapper that automatically retries on transient failures.\n *\n * Retry logic:\n * 1. If the response status is in `retryOn`, read+discard the body, then retry.\n * 2. If the request threw a network/abort error and `retryOnNetworkError` is true,\n * retry.\n * 3. Exponential backoff with full jitter.\n * 4. Respects AbortSignal : aborts cancel the current attempt and skip remaining\n * retries.\n */\nexport function createRetryFetch(\n fetchImpl: typeof globalThis.fetch,\n config?: RetryConfig,\n): typeof globalThis.fetch {\n const maxRetries = config?.maxRetries ?? 3;\n const baseDelay = config?.baseDelay ?? 1000;\n const maxDelay = config?.maxDelay ?? 30000;\n const retryOn = config?.retryOn ?? DEFAULT_RETRY_ON;\n const retryOnNetworkError = config?.retryOnNetworkError ?? true;\n\n return async (input: RequestInfo | URL, init?: RequestInit) => {\n let lastError: unknown;\n const signal = init?.signal;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n let resolvedInit = init;\n if (attempt > 0 && init?.body) {\n resolvedInit = { ...init, body: await cloneBody(init.body) };\n }\n\n const res = await fetchImpl(input, resolvedInit);\n\n if (!retryOn.includes(res.status) || attempt === maxRetries) {\n return res;\n }\n\n // Drain the body so the connection can be reused\n await drainBody(res);\n lastError = new Error(`HTTP ${res.status}`);\n } catch (err) {\n if (signal?.aborted) throw err;\n\n if (!retryOnNetworkError || attempt === maxRetries) throw err;\n lastError = err;\n }\n\n const delay = backoff(attempt, baseDelay, maxDelay);\n config?.onRetry?.(attempt + 1, lastError, delay);\n await sleep(delay, signal ?? undefined);\n }\n\n throw lastError;\n };\n}\n\nasync function cloneBody(body: BodyInit | null): Promise<BodyInit | null> {\n if (body === null || body === undefined) return null;\n if (typeof body === \"string\") return body;\n if (body instanceof URLSearchParams) return new URLSearchParams(body);\n if (body instanceof FormData) return body;\n if (body instanceof Blob) return body;\n if (body instanceof ArrayBuffer) return body.slice(0);\n if (body instanceof ReadableStream) {\n throw new Error(\n \"Cannot retry a request with a ReadableStream body. Use string or JSON.\",\n );\n }\n if (ArrayBuffer.isView(body)) {\n return new Uint8Array(\n body.buffer,\n body.byteOffset,\n body.byteLength,\n ).slice();\n }\n return body;\n}\n\nasync function drainBody(res: Response): Promise<void> {\n try {\n await res.body?.cancel();\n } catch {\n // Best effort\n }\n}\n","/**\n * In-flight request deduplication.\n *\n * When multiple callers request the same URL+method+body combination\n * concurrently, only one network request is made. All callers receive\n * the same response (the promise is shared).\n *\n * Cache entries are evicted when the shared promise settles (success or\n * failure). Late subscribers that arrive after eviction start fresh.\n */\n\n/**\n * Deduplicator for in-flight HTTP requests.\n *\n * ## Usage\n * ```ts\n * const dedupe = new RequestDeduplicator();\n *\n * async function fetchDeduped(url: string, init?: RequestInit): Promise<Response> {\n * const method = init?.method ?? 'GET';\n * const key = `${method}:${url}:${JSON.stringify(init?.body ?? '')}`;\n * return dedupe.dedup(key, () => fetch(url, init), init?.signal);\n * }\n * ```\n */\nexport class RequestDeduplicator {\n #pending = new Map<string, Promise<unknown>>();\n\n /**\n * Execute `factory` once for the given key. Concurrent callers with\n * the same key receive the same promise. The entry is evicted after\n * the shared promise settles.\n *\n * @param key - Unique request key (method + URL + stable body hash).\n * @param factory - The network operation to perform.\n * @param signal - Optional AbortSignal. If it fires, this caller gets\n * an AbortError. The shared request continues.\n */\n async dedup<T>(\n key: string,\n factory: () => Promise<T>,\n signal?: AbortSignal,\n ): Promise<T> {\n const existing = this.#pending.get(key);\n if (existing !== undefined) {\n return this.#raceWithSignal(existing as Promise<T>, signal);\n }\n\n const promise = factory().finally(() => {\n this.#pending.delete(key);\n });\n this.#pending.set(key, promise);\n\n return this.#raceWithSignal(promise, signal);\n }\n\n /** How many unique requests are currently in flight. */\n get inflight(): number {\n return this.#pending.size;\n }\n\n /** Remove all pending deduplication entries. */\n clear(): void {\n this.#pending.clear();\n }\n\n /**\n * Race a promise against an AbortSignal. If the signal fires first,\n * the caller gets an AbortError but the promise still settles normally.\n */\n #raceWithSignal<T>(p: Promise<T>, s?: AbortSignal): Promise<T> {\n if (!s) return p;\n if (s.aborted) {\n return Promise.reject(\n s.reason ?? new DOMException(\"Aborted\", \"AbortError\"),\n );\n }\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n reject(s.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n };\n s.addEventListener(\"abort\", onAbort, { once: true });\n p.then(\n (v) => {\n s.removeEventListener(\"abort\", onAbort);\n resolve(v);\n },\n (e) => {\n s.removeEventListener(\"abort\", onAbort);\n reject(e);\n },\n );\n });\n }\n}\n"],"mappings":"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","names":["query"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -302,7 +302,7 @@ declare class PaginationIterator<T> implements AsyncIterableIterator<T> {
|
|
|
302
302
|
*
|
|
303
303
|
* ## Usage
|
|
304
304
|
* ```ts
|
|
305
|
-
* import { query } from '@lyeve/
|
|
305
|
+
* import { query } from '@lyeve-labs/client';
|
|
306
306
|
*
|
|
307
307
|
* const q = query('posts')
|
|
308
308
|
* .where('status', 'eq:published')
|
package/dist/index.d.ts
CHANGED
|
@@ -302,7 +302,7 @@ declare class PaginationIterator<T> implements AsyncIterableIterator<T> {
|
|
|
302
302
|
*
|
|
303
303
|
* ## Usage
|
|
304
304
|
* ```ts
|
|
305
|
-
* import { query } from '@lyeve/
|
|
305
|
+
* import { query } from '@lyeve-labs/client';
|
|
306
306
|
*
|
|
307
307
|
* const q = query('posts')
|
|
308
308
|
* .where('status', 'eq:published')
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/pagination.ts","../src/query-builder.ts","../src/retry.ts","../src/dedupe.ts"],"sourcesContent":["/**\n * Framework-agnostic HTTP client for the LyEve CMS API.\n *\n * The `createClient` factory accepts any `fetch`-compatible function\n * (globalThis.fetch, SvelteKit's event.fetch, a Node.js polyfill) and\n * returns a typed client with get/post/put/delete methods that handle\n * JSON serialization, error mapping, and timeout.\n */\n\nexport class ApiError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = \"ApiError\";\n }\n}\n\nexport type HttpClient = ReturnType<typeof createClient>;\n\nexport function createClient(\n fetchFn: typeof fetch,\n defaultHeaders: Record<string, string> = {},\n) {\n async function request<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetchFn(url, {\n ...init,\n // Default 15 s timeout so a slow/unresponsive backend can't hang\n // the caller indefinitely. Callers may override by passing their\n // own AbortSignal.\n signal: init?.signal ?? AbortSignal.timeout(15_000),\n headers: {\n \"Content-Type\": \"application/json\",\n ...defaultHeaders,\n ...(init?.headers instanceof Headers\n ? Object.fromEntries(init.headers.entries())\n : Array.isArray(init?.headers)\n ? Object.fromEntries(init.headers)\n : init?.headers),\n },\n });\n\n if (!res.ok) {\n const text = await res.text();\n let message = text;\n try {\n const json = JSON.parse(text) as { error?: string };\n message = json.error ?? text;\n } catch {\n // use raw text\n }\n throw new ApiError(res.status, message);\n }\n\n if (res.status === 204) return undefined as T;\n return res.json() as Promise<T>;\n }\n\n return {\n get: <T>(url: string, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"GET\" }),\n post: <T>(url: string, body: unknown, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"POST\", body: JSON.stringify(body) }),\n put: <T>(url: string, body: unknown, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"PUT\", body: JSON.stringify(body) }),\n patch: <T>(url: string, body: unknown, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"PATCH\", body: JSON.stringify(body) }),\n delete: <T>(url: string, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"DELETE\" }),\n };\n}\n","/**\n * Async iterator for cursor-based content pagination.\n *\n * Walks the Content API's cursor endpoint, yielding records one at a time.\n * Each call to `.next()` returns the next record.\n *\n * ## Usage\n * ```ts\n * const paginator = new PaginationIterator<MyRecord>({\n * fetchPage: (cursor) => api.getPage(cursor),\n * });\n *\n * for await (const record of paginator) {\n * console.log(record);\n * }\n *\n * // With AbortController:\n * const ac = new AbortController();\n * setTimeout(() => ac.abort(), 5000);\n * for await (const record of paginator.withSignal(ac.signal)) {\n * // stops after 5 seconds\n * }\n * ```\n */\n\n/** Response shape from a cursor-paginated endpoint. */\nexport interface CursorPage<T> {\n items: T[];\n /** Present when more pages are available. */\n next_cursor?: string;\n}\n\n/** Function that fetches a single cursor page. */\nexport type PageFetcher<T> = (cursor?: string) => Promise<CursorPage<T>>;\n\nexport interface PaginationConfig<T> {\n /** Function that fetches a single cursor page. */\n fetchPage: PageFetcher<T>;\n}\n\n/**\n * Async iterable iterator over paginated records.\n *\n * Implements `AsyncIterableIterator<T>` so it can be used directly in\n * `for await...of` loops. Also exposes `.withSignal()` for AbortController\n * integration.\n */\nexport class PaginationIterator<T> implements AsyncIterableIterator<T> {\n #fetchPage: PageFetcher<T>;\n #signal?: AbortSignal;\n\n // Internal paging state\n #buffer: T[] = [];\n #cursor: string | undefined = undefined;\n #exhausted = false;\n #fetching: Promise<void> | null = null;\n\n constructor(config: PaginationConfig<T>) {\n this.#fetchPage = config.fetchPage;\n }\n\n /**\n * Return a new iterator sharing the same underlying state but with a\n * different AbortSignal. Useful when the signal is only known at the\n * call site, not at construction time.\n */\n withSignal(signal: AbortSignal): PaginationIterator<T> {\n const clone = new PaginationIterator<T>({\n fetchPage: this.#fetchPage,\n });\n clone.#signal = signal;\n clone.#buffer = this.#buffer;\n clone.#cursor = this.#cursor;\n clone.#exhausted = this.#exhausted;\n clone.#fetching = this.#fetching;\n return clone;\n }\n\n // AsyncIterator protocol\n\n [Symbol.asyncIterator](): AsyncIterableIterator<T> {\n return this;\n }\n\n async next(): Promise<IteratorResult<T>> {\n this.#throwIfAborted();\n\n // Yield from buffer if we have cached records\n if (this.#buffer.length > 0) {\n const value = this.#buffer.shift()!;\n return { value, done: false };\n }\n\n // Done if exhausted\n if (this.#exhausted) {\n return { value: undefined, done: true };\n }\n\n // Fetch next page\n try {\n await this.#fetchPageInternal();\n } catch (err) {\n if (err instanceof Error) throw err;\n throw new Error(String(err));\n }\n\n if (this.#buffer.length > 0) {\n const value = this.#buffer.shift()!;\n return { value, done: false };\n }\n\n return { value: undefined, done: true };\n }\n\n return?(value?: unknown): Promise<IteratorResult<T>> {\n this.#exhausted = true;\n this.#buffer.length = 0;\n return Promise.resolve({\n value: value as T | undefined,\n done: true,\n });\n }\n\n throw?(e?: unknown): Promise<IteratorResult<T>> {\n this.#exhausted = true;\n this.#buffer.length = 0;\n return Promise.reject(e);\n }\n\n // Internal\n\n async #fetchPageInternal(): Promise<void> {\n if (this.#fetching) {\n await this.#fetching;\n return;\n }\n\n this.#throwIfAborted();\n\n this.#fetching = this.#fetchPage(this.#cursor)\n .then((page) => {\n this.#throwIfAborted();\n this.#buffer = [...page.items];\n if (page.next_cursor) {\n this.#cursor = page.next_cursor;\n } else {\n this.#exhausted = true;\n }\n })\n .finally(() => {\n this.#fetching = null;\n });\n\n await this.#fetching;\n }\n\n #throwIfAborted(): void {\n if (this.#signal?.aborted) {\n throw this.#signal.reason ?? new DOMException(\"Aborted\", \"AbortError\");\n }\n }\n}\n","/**\n * Fluent query builder for LyEve CMS Content API queries.\n *\n * Produces ContentQuery objects with a chainable API.\n *\n * ## Usage\n * ```ts\n * import { query } from '@lyeve/cms-client';\n *\n * const q = query('posts')\n * .where('status', 'eq:published')\n * .sort('-created_at')\n * .limit(20)\n * .build();\n * ```\n */\n\nexport type ContentStatus = \"draft\" | \"published\" | \"archived\";\n\nexport interface ContentQuery {\n /** Filter by status. */\n status?: ContentStatus;\n /** Max records per page. */\n limit?: number;\n /** Offset for offset-based pagination. Mutually exclusive with cursor. */\n offset?: number;\n /** Cursor for cursor-based pagination. Mutually exclusive with offset. */\n cursor?: string;\n /** Field-level filters in `field=op:value` format. */\n filters?: Record<string, string>;\n /** Sort field(s), comma-separated. Prefix with `-` for descending. */\n sort?: string;\n}\n\n/**\n * Builder for ContentQuery objects.\n *\n * Each field can only hold one filter (Record<string, string>). Calling\n * where() (or whereEq/whereGt etc.) with an already-set field name\n * overwrites the previous filter. For range queries on the same field,\n * use the server's range filter syntax in a single call.\n */\nexport class QueryBuilder {\n #schema: string;\n #status?: ContentStatus;\n #limit?: number;\n #offset?: number;\n #cursor?: string;\n #filters: Record<string, string> = {};\n #sort?: string;\n\n constructor(schema: string) {\n this.#schema = schema;\n }\n\n /** The schema/collection this query targets. */\n get schema(): string {\n return this.#schema;\n }\n\n /** Filter by content status (published, draft, archived). */\n whereStatus(s: ContentStatus | undefined): this {\n this.#status = s;\n return this;\n }\n\n /** Add a field filter in `op:value` format. */\n where(field: string, value: string): this {\n this.#filters[field] = value;\n return this;\n }\n\n /** Add a field-level equality filter. */\n whereEq(field: string, value: string | number | boolean): this {\n this.#filters[field] = `eq:${String(value)}`;\n return this;\n }\n\n /** Add a field-level \"in\" filter (value is a JSON array). */\n whereIn(field: string, values: (string | number)[]): this {\n this.#filters[field] = `in:${JSON.stringify(values)}`;\n return this;\n }\n\n /** Greater-than comparison. */\n whereGt(field: string, value: number): this {\n this.#filters[field] = `gt:${value}`;\n return this;\n }\n\n /** Greater-than-or-equal comparison. */\n whereGte(field: string, value: number): this {\n this.#filters[field] = `gte:${value}`;\n return this;\n }\n\n /** Less-than comparison. */\n whereLt(field: string, value: number): this {\n this.#filters[field] = `lt:${value}`;\n return this;\n }\n\n /** Less-than-or-equal comparison. */\n whereLte(field: string, value: number): this {\n this.#filters[field] = `lte:${value}`;\n return this;\n }\n\n /** Full-text search (if the schema supports it). */\n whereSearch(field: string, query: string): this {\n this.#filters[field] = `search:${query}`;\n return this;\n }\n\n /** Set the max records per page. */\n limit(n: number | undefined): this {\n this.#limit = n;\n return this;\n }\n\n /** Set the offset for offset-based pagination. Clears cursor. */\n offset(n: number | undefined): this {\n this.#offset = n;\n if (n !== undefined) this.#cursor = undefined;\n return this;\n }\n\n /** Set the cursor for cursor-based pagination. Clears offset. */\n cursor(c: string | undefined): this {\n this.#cursor = c;\n if (c !== undefined) this.#offset = undefined;\n return this;\n }\n\n /** Set sort fields. Prefix with `-` for descending. */\n sort(...fields: string[]): this {\n this.#sort = fields.join(\",\");\n return this;\n }\n\n /** Clear all filters, keeping only the schema name. */\n resetFilters(): this {\n this.#filters = {};\n this.#status = undefined;\n this.#sort = undefined;\n this.#limit = undefined;\n this.#offset = undefined;\n this.#cursor = undefined;\n return this;\n }\n\n /** Produce the ContentQuery object. */\n build(): ContentQuery {\n return {\n status: this.#status,\n limit: this.#limit,\n offset: this.#offset,\n cursor: this.#cursor,\n filters:\n Object.keys(this.#filters).length > 0\n ? { ...this.#filters }\n : undefined,\n sort: this.#sort,\n };\n }\n}\n\n/**\n * Create a new query builder for the given schema.\n * Equivalent to `new QueryBuilder(schema)`.\n */\nexport function query(schema: string): QueryBuilder {\n return new QueryBuilder(schema);\n}\n","/**\n * Automatic retry with exponential backoff and jitter.\n *\n * Wraps fetch to retry on transient errors (429, 5xx, network failures).\n * Uses \"full jitter\" backoff: `random(0, min(cap, base * 2^attempt))`.\n *\n * Usage:\n * const fetchWithRetry = createRetryFetch(fetch, {\n * maxRetries: 3,\n * baseDelay: 1000,\n * maxDelay: 30000,\n * retryOn: [429, 500, 502, 503, 504],\n * onRetry: (attempt, err, delay) => console.warn(`Retry ${attempt} in ${delay}ms`),\n * });\n * const res = await fetchWithRetry(url, init);\n */\n\nexport interface RetryConfig {\n /** Maximum number of retry attempts (default: 3). */\n maxRetries?: number;\n /** Base delay in ms before first retry (default: 1000). */\n baseDelay?: number;\n /** Maximum delay in ms between retries (default: 30000). */\n maxDelay?: number;\n /** HTTP status codes that trigger a retry (default: [429, 500, 502, 503, 504]). */\n retryOn?: number[];\n /** If true, retry on network/abort errors as well (default: true). */\n retryOnNetworkError?: boolean;\n /** Called before each retry with (attempt, error, delayMs). */\n onRetry?: (attempt: number, error: unknown, delayMs: number) => void;\n}\n\nconst DEFAULT_RETRY_ON = [429, 500, 502, 503, 504];\n\nfunction jitter(cap: number): number {\n return Math.floor(Math.random() * cap);\n}\n\nfunction backoff(attempt: number, base: number, max: number): number {\n return jitter(Math.min(max, base * Math.pow(2, attempt)));\n}\n\n/** Sleep for `ms` milliseconds, respecting an AbortSignal. */\nasync function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n return;\n }\n const timer = setTimeout(resolve, ms);\n if (!signal) return;\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Create a fetch wrapper that automatically retries on transient failures.\n *\n * Retry logic:\n * 1. If the response status is in `retryOn`, read+discard the body, then retry.\n * 2. If the request threw a network/abort error and `retryOnNetworkError` is true,\n * retry.\n * 3. Exponential backoff with full jitter.\n * 4. Respects AbortSignal : aborts cancel the current attempt and skip remaining\n * retries.\n */\nexport function createRetryFetch(\n fetchImpl: typeof globalThis.fetch,\n config?: RetryConfig,\n): typeof globalThis.fetch {\n const maxRetries = config?.maxRetries ?? 3;\n const baseDelay = config?.baseDelay ?? 1000;\n const maxDelay = config?.maxDelay ?? 30000;\n const retryOn = config?.retryOn ?? DEFAULT_RETRY_ON;\n const retryOnNetworkError = config?.retryOnNetworkError ?? true;\n\n return async (input: RequestInfo | URL, init?: RequestInit) => {\n let lastError: unknown;\n const signal = init?.signal;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n let resolvedInit = init;\n if (attempt > 0 && init?.body) {\n resolvedInit = { ...init, body: await cloneBody(init.body) };\n }\n\n const res = await fetchImpl(input, resolvedInit);\n\n if (!retryOn.includes(res.status) || attempt === maxRetries) {\n return res;\n }\n\n // Drain the body so the connection can be reused\n await drainBody(res);\n lastError = new Error(`HTTP ${res.status}`);\n } catch (err) {\n if (signal?.aborted) throw err;\n\n if (!retryOnNetworkError || attempt === maxRetries) throw err;\n lastError = err;\n }\n\n const delay = backoff(attempt, baseDelay, maxDelay);\n config?.onRetry?.(attempt + 1, lastError, delay);\n await sleep(delay, signal ?? undefined);\n }\n\n throw lastError;\n };\n}\n\nasync function cloneBody(body: BodyInit | null): Promise<BodyInit | null> {\n if (body === null || body === undefined) return null;\n if (typeof body === \"string\") return body;\n if (body instanceof URLSearchParams) return new URLSearchParams(body);\n if (body instanceof FormData) return body;\n if (body instanceof Blob) return body;\n if (body instanceof ArrayBuffer) return body.slice(0);\n if (body instanceof ReadableStream) {\n throw new Error(\n \"Cannot retry a request with a ReadableStream body. Use string or JSON.\",\n );\n }\n if (ArrayBuffer.isView(body)) {\n return new Uint8Array(\n body.buffer,\n body.byteOffset,\n body.byteLength,\n ).slice();\n }\n return body;\n}\n\nasync function drainBody(res: Response): Promise<void> {\n try {\n await res.body?.cancel();\n } catch {\n // Best effort\n }\n}\n","/**\n * In-flight request deduplication.\n *\n * When multiple callers request the same URL+method+body combination\n * concurrently, only one network request is made. All callers receive\n * the same response (the promise is shared).\n *\n * Cache entries are evicted when the shared promise settles (success or\n * failure). Late subscribers that arrive after eviction start fresh.\n */\n\n/**\n * Deduplicator for in-flight HTTP requests.\n *\n * ## Usage\n * ```ts\n * const dedupe = new RequestDeduplicator();\n *\n * async function fetchDeduped(url: string, init?: RequestInit): Promise<Response> {\n * const method = init?.method ?? 'GET';\n * const key = `${method}:${url}:${JSON.stringify(init?.body ?? '')}`;\n * return dedupe.dedup(key, () => fetch(url, init), init?.signal);\n * }\n * ```\n */\nexport class RequestDeduplicator {\n #pending = new Map<string, Promise<unknown>>();\n\n /**\n * Execute `factory` once for the given key. Concurrent callers with\n * the same key receive the same promise. The entry is evicted after\n * the shared promise settles.\n *\n * @param key - Unique request key (method + URL + stable body hash).\n * @param factory - The network operation to perform.\n * @param signal - Optional AbortSignal. If it fires, this caller gets\n * an AbortError. The shared request continues.\n */\n async dedup<T>(\n key: string,\n factory: () => Promise<T>,\n signal?: AbortSignal,\n ): Promise<T> {\n const existing = this.#pending.get(key);\n if (existing !== undefined) {\n return this.#raceWithSignal(existing as Promise<T>, signal);\n }\n\n const promise = factory().finally(() => {\n this.#pending.delete(key);\n });\n this.#pending.set(key, promise);\n\n return this.#raceWithSignal(promise, signal);\n }\n\n /** How many unique requests are currently in flight. */\n get inflight(): number {\n return this.#pending.size;\n }\n\n /** Remove all pending deduplication entries. */\n clear(): void {\n this.#pending.clear();\n }\n\n /**\n * Race a promise against an AbortSignal. If the signal fires first,\n * the caller gets an AbortError but the promise still settles normally.\n */\n #raceWithSignal<T>(p: Promise<T>, s?: AbortSignal): Promise<T> {\n if (!s) return p;\n if (s.aborted) {\n return Promise.reject(\n s.reason ?? new DOMException(\"Aborted\", \"AbortError\"),\n );\n }\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n reject(s.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n };\n s.addEventListener(\"abort\", onAbort, { once: true });\n p.then(\n (v) => {\n s.removeEventListener(\"abort\", onAbort);\n resolve(v);\n },\n (e) => {\n s.removeEventListener(\"abort\", onAbort);\n reject(e);\n },\n );\n });\n }\n}\n"],"mappings":"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","names":["query"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/pagination.ts","../src/query-builder.ts","../src/retry.ts","../src/dedupe.ts"],"sourcesContent":["/**\n * Framework-agnostic HTTP client for the LyEve CMS API.\n *\n * The `createClient` factory accepts any `fetch`-compatible function\n * (globalThis.fetch, SvelteKit's event.fetch, a Node.js polyfill) and\n * returns a typed client with get/post/put/delete methods that handle\n * JSON serialization, error mapping, and timeout.\n */\n\nexport class ApiError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = \"ApiError\";\n }\n}\n\nexport type HttpClient = ReturnType<typeof createClient>;\n\nexport function createClient(\n fetchFn: typeof fetch,\n defaultHeaders: Record<string, string> = {},\n) {\n async function request<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetchFn(url, {\n ...init,\n // Default 15 s timeout so a slow/unresponsive backend can't hang\n // the caller indefinitely. Callers may override by passing their\n // own AbortSignal.\n signal: init?.signal ?? AbortSignal.timeout(15_000),\n headers: {\n \"Content-Type\": \"application/json\",\n ...defaultHeaders,\n ...(init?.headers instanceof Headers\n ? Object.fromEntries(init.headers.entries())\n : Array.isArray(init?.headers)\n ? Object.fromEntries(init.headers)\n : init?.headers),\n },\n });\n\n if (!res.ok) {\n const text = await res.text();\n let message = text;\n try {\n const json = JSON.parse(text) as { error?: string };\n message = json.error ?? text;\n } catch {\n // use raw text\n }\n throw new ApiError(res.status, message);\n }\n\n if (res.status === 204) return undefined as T;\n return res.json() as Promise<T>;\n }\n\n return {\n get: <T>(url: string, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"GET\" }),\n post: <T>(url: string, body: unknown, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"POST\", body: JSON.stringify(body) }),\n put: <T>(url: string, body: unknown, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"PUT\", body: JSON.stringify(body) }),\n patch: <T>(url: string, body: unknown, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"PATCH\", body: JSON.stringify(body) }),\n delete: <T>(url: string, init?: RequestInit) =>\n request<T>(url, { ...init, method: \"DELETE\" }),\n };\n}\n","/**\n * Async iterator for cursor-based content pagination.\n *\n * Walks the Content API's cursor endpoint, yielding records one at a time.\n * Each call to `.next()` returns the next record.\n *\n * ## Usage\n * ```ts\n * const paginator = new PaginationIterator<MyRecord>({\n * fetchPage: (cursor) => api.getPage(cursor),\n * });\n *\n * for await (const record of paginator) {\n * console.log(record);\n * }\n *\n * // With AbortController:\n * const ac = new AbortController();\n * setTimeout(() => ac.abort(), 5000);\n * for await (const record of paginator.withSignal(ac.signal)) {\n * // stops after 5 seconds\n * }\n * ```\n */\n\n/** Response shape from a cursor-paginated endpoint. */\nexport interface CursorPage<T> {\n items: T[];\n /** Present when more pages are available. */\n next_cursor?: string;\n}\n\n/** Function that fetches a single cursor page. */\nexport type PageFetcher<T> = (cursor?: string) => Promise<CursorPage<T>>;\n\nexport interface PaginationConfig<T> {\n /** Function that fetches a single cursor page. */\n fetchPage: PageFetcher<T>;\n}\n\n/**\n * Async iterable iterator over paginated records.\n *\n * Implements `AsyncIterableIterator<T>` so it can be used directly in\n * `for await...of` loops. Also exposes `.withSignal()` for AbortController\n * integration.\n */\nexport class PaginationIterator<T> implements AsyncIterableIterator<T> {\n #fetchPage: PageFetcher<T>;\n #signal?: AbortSignal;\n\n // Internal paging state\n #buffer: T[] = [];\n #cursor: string | undefined = undefined;\n #exhausted = false;\n #fetching: Promise<void> | null = null;\n\n constructor(config: PaginationConfig<T>) {\n this.#fetchPage = config.fetchPage;\n }\n\n /**\n * Return a new iterator sharing the same underlying state but with a\n * different AbortSignal. Useful when the signal is only known at the\n * call site, not at construction time.\n */\n withSignal(signal: AbortSignal): PaginationIterator<T> {\n const clone = new PaginationIterator<T>({\n fetchPage: this.#fetchPage,\n });\n clone.#signal = signal;\n clone.#buffer = this.#buffer;\n clone.#cursor = this.#cursor;\n clone.#exhausted = this.#exhausted;\n clone.#fetching = this.#fetching;\n return clone;\n }\n\n // AsyncIterator protocol\n\n [Symbol.asyncIterator](): AsyncIterableIterator<T> {\n return this;\n }\n\n async next(): Promise<IteratorResult<T>> {\n this.#throwIfAborted();\n\n // Yield from buffer if we have cached records\n if (this.#buffer.length > 0) {\n const value = this.#buffer.shift()!;\n return { value, done: false };\n }\n\n // Done if exhausted\n if (this.#exhausted) {\n return { value: undefined, done: true };\n }\n\n // Fetch next page\n try {\n await this.#fetchPageInternal();\n } catch (err) {\n if (err instanceof Error) throw err;\n throw new Error(String(err));\n }\n\n if (this.#buffer.length > 0) {\n const value = this.#buffer.shift()!;\n return { value, done: false };\n }\n\n return { value: undefined, done: true };\n }\n\n return?(value?: unknown): Promise<IteratorResult<T>> {\n this.#exhausted = true;\n this.#buffer.length = 0;\n return Promise.resolve({\n value: value as T | undefined,\n done: true,\n });\n }\n\n throw?(e?: unknown): Promise<IteratorResult<T>> {\n this.#exhausted = true;\n this.#buffer.length = 0;\n return Promise.reject(e);\n }\n\n // Internal\n\n async #fetchPageInternal(): Promise<void> {\n if (this.#fetching) {\n await this.#fetching;\n return;\n }\n\n this.#throwIfAborted();\n\n this.#fetching = this.#fetchPage(this.#cursor)\n .then((page) => {\n this.#throwIfAborted();\n this.#buffer = [...page.items];\n if (page.next_cursor) {\n this.#cursor = page.next_cursor;\n } else {\n this.#exhausted = true;\n }\n })\n .finally(() => {\n this.#fetching = null;\n });\n\n await this.#fetching;\n }\n\n #throwIfAborted(): void {\n if (this.#signal?.aborted) {\n throw this.#signal.reason ?? new DOMException(\"Aborted\", \"AbortError\");\n }\n }\n}\n","/**\n * Fluent query builder for LyEve CMS Content API queries.\n *\n * Produces ContentQuery objects with a chainable API.\n *\n * ## Usage\n * ```ts\n * import { query } from '@lyeve-labs/client';\n *\n * const q = query('posts')\n * .where('status', 'eq:published')\n * .sort('-created_at')\n * .limit(20)\n * .build();\n * ```\n */\n\nexport type ContentStatus = \"draft\" | \"published\" | \"archived\";\n\nexport interface ContentQuery {\n /** Filter by status. */\n status?: ContentStatus;\n /** Max records per page. */\n limit?: number;\n /** Offset for offset-based pagination. Mutually exclusive with cursor. */\n offset?: number;\n /** Cursor for cursor-based pagination. Mutually exclusive with offset. */\n cursor?: string;\n /** Field-level filters in `field=op:value` format. */\n filters?: Record<string, string>;\n /** Sort field(s), comma-separated. Prefix with `-` for descending. */\n sort?: string;\n}\n\n/**\n * Builder for ContentQuery objects.\n *\n * Each field can only hold one filter (Record<string, string>). Calling\n * where() (or whereEq/whereGt etc.) with an already-set field name\n * overwrites the previous filter. For range queries on the same field,\n * use the server's range filter syntax in a single call.\n */\nexport class QueryBuilder {\n #schema: string;\n #status?: ContentStatus;\n #limit?: number;\n #offset?: number;\n #cursor?: string;\n #filters: Record<string, string> = {};\n #sort?: string;\n\n constructor(schema: string) {\n this.#schema = schema;\n }\n\n /** The schema/collection this query targets. */\n get schema(): string {\n return this.#schema;\n }\n\n /** Filter by content status (published, draft, archived). */\n whereStatus(s: ContentStatus | undefined): this {\n this.#status = s;\n return this;\n }\n\n /** Add a field filter in `op:value` format. */\n where(field: string, value: string): this {\n this.#filters[field] = value;\n return this;\n }\n\n /** Add a field-level equality filter. */\n whereEq(field: string, value: string | number | boolean): this {\n this.#filters[field] = `eq:${String(value)}`;\n return this;\n }\n\n /** Add a field-level \"in\" filter (value is a JSON array). */\n whereIn(field: string, values: (string | number)[]): this {\n this.#filters[field] = `in:${JSON.stringify(values)}`;\n return this;\n }\n\n /** Greater-than comparison. */\n whereGt(field: string, value: number): this {\n this.#filters[field] = `gt:${value}`;\n return this;\n }\n\n /** Greater-than-or-equal comparison. */\n whereGte(field: string, value: number): this {\n this.#filters[field] = `gte:${value}`;\n return this;\n }\n\n /** Less-than comparison. */\n whereLt(field: string, value: number): this {\n this.#filters[field] = `lt:${value}`;\n return this;\n }\n\n /** Less-than-or-equal comparison. */\n whereLte(field: string, value: number): this {\n this.#filters[field] = `lte:${value}`;\n return this;\n }\n\n /** Full-text search (if the schema supports it). */\n whereSearch(field: string, query: string): this {\n this.#filters[field] = `search:${query}`;\n return this;\n }\n\n /** Set the max records per page. */\n limit(n: number | undefined): this {\n this.#limit = n;\n return this;\n }\n\n /** Set the offset for offset-based pagination. Clears cursor. */\n offset(n: number | undefined): this {\n this.#offset = n;\n if (n !== undefined) this.#cursor = undefined;\n return this;\n }\n\n /** Set the cursor for cursor-based pagination. Clears offset. */\n cursor(c: string | undefined): this {\n this.#cursor = c;\n if (c !== undefined) this.#offset = undefined;\n return this;\n }\n\n /** Set sort fields. Prefix with `-` for descending. */\n sort(...fields: string[]): this {\n this.#sort = fields.join(\",\");\n return this;\n }\n\n /** Clear all filters, keeping only the schema name. */\n resetFilters(): this {\n this.#filters = {};\n this.#status = undefined;\n this.#sort = undefined;\n this.#limit = undefined;\n this.#offset = undefined;\n this.#cursor = undefined;\n return this;\n }\n\n /** Produce the ContentQuery object. */\n build(): ContentQuery {\n return {\n status: this.#status,\n limit: this.#limit,\n offset: this.#offset,\n cursor: this.#cursor,\n filters:\n Object.keys(this.#filters).length > 0\n ? { ...this.#filters }\n : undefined,\n sort: this.#sort,\n };\n }\n}\n\n/**\n * Create a new query builder for the given schema.\n * Equivalent to `new QueryBuilder(schema)`.\n */\nexport function query(schema: string): QueryBuilder {\n return new QueryBuilder(schema);\n}\n","/**\n * Automatic retry with exponential backoff and jitter.\n *\n * Wraps fetch to retry on transient errors (429, 5xx, network failures).\n * Uses \"full jitter\" backoff: `random(0, min(cap, base * 2^attempt))`.\n *\n * Usage:\n * const fetchWithRetry = createRetryFetch(fetch, {\n * maxRetries: 3,\n * baseDelay: 1000,\n * maxDelay: 30000,\n * retryOn: [429, 500, 502, 503, 504],\n * onRetry: (attempt, err, delay) => console.warn(`Retry ${attempt} in ${delay}ms`),\n * });\n * const res = await fetchWithRetry(url, init);\n */\n\nexport interface RetryConfig {\n /** Maximum number of retry attempts (default: 3). */\n maxRetries?: number;\n /** Base delay in ms before first retry (default: 1000). */\n baseDelay?: number;\n /** Maximum delay in ms between retries (default: 30000). */\n maxDelay?: number;\n /** HTTP status codes that trigger a retry (default: [429, 500, 502, 503, 504]). */\n retryOn?: number[];\n /** If true, retry on network/abort errors as well (default: true). */\n retryOnNetworkError?: boolean;\n /** Called before each retry with (attempt, error, delayMs). */\n onRetry?: (attempt: number, error: unknown, delayMs: number) => void;\n}\n\nconst DEFAULT_RETRY_ON = [429, 500, 502, 503, 504];\n\nfunction jitter(cap: number): number {\n return Math.floor(Math.random() * cap);\n}\n\nfunction backoff(attempt: number, base: number, max: number): number {\n return jitter(Math.min(max, base * Math.pow(2, attempt)));\n}\n\n/** Sleep for `ms` milliseconds, respecting an AbortSignal. */\nasync function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n return;\n }\n const timer = setTimeout(resolve, ms);\n if (!signal) return;\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Create a fetch wrapper that automatically retries on transient failures.\n *\n * Retry logic:\n * 1. If the response status is in `retryOn`, read+discard the body, then retry.\n * 2. If the request threw a network/abort error and `retryOnNetworkError` is true,\n * retry.\n * 3. Exponential backoff with full jitter.\n * 4. Respects AbortSignal : aborts cancel the current attempt and skip remaining\n * retries.\n */\nexport function createRetryFetch(\n fetchImpl: typeof globalThis.fetch,\n config?: RetryConfig,\n): typeof globalThis.fetch {\n const maxRetries = config?.maxRetries ?? 3;\n const baseDelay = config?.baseDelay ?? 1000;\n const maxDelay = config?.maxDelay ?? 30000;\n const retryOn = config?.retryOn ?? DEFAULT_RETRY_ON;\n const retryOnNetworkError = config?.retryOnNetworkError ?? true;\n\n return async (input: RequestInfo | URL, init?: RequestInit) => {\n let lastError: unknown;\n const signal = init?.signal;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n let resolvedInit = init;\n if (attempt > 0 && init?.body) {\n resolvedInit = { ...init, body: await cloneBody(init.body) };\n }\n\n const res = await fetchImpl(input, resolvedInit);\n\n if (!retryOn.includes(res.status) || attempt === maxRetries) {\n return res;\n }\n\n // Drain the body so the connection can be reused\n await drainBody(res);\n lastError = new Error(`HTTP ${res.status}`);\n } catch (err) {\n if (signal?.aborted) throw err;\n\n if (!retryOnNetworkError || attempt === maxRetries) throw err;\n lastError = err;\n }\n\n const delay = backoff(attempt, baseDelay, maxDelay);\n config?.onRetry?.(attempt + 1, lastError, delay);\n await sleep(delay, signal ?? undefined);\n }\n\n throw lastError;\n };\n}\n\nasync function cloneBody(body: BodyInit | null): Promise<BodyInit | null> {\n if (body === null || body === undefined) return null;\n if (typeof body === \"string\") return body;\n if (body instanceof URLSearchParams) return new URLSearchParams(body);\n if (body instanceof FormData) return body;\n if (body instanceof Blob) return body;\n if (body instanceof ArrayBuffer) return body.slice(0);\n if (body instanceof ReadableStream) {\n throw new Error(\n \"Cannot retry a request with a ReadableStream body. Use string or JSON.\",\n );\n }\n if (ArrayBuffer.isView(body)) {\n return new Uint8Array(\n body.buffer,\n body.byteOffset,\n body.byteLength,\n ).slice();\n }\n return body;\n}\n\nasync function drainBody(res: Response): Promise<void> {\n try {\n await res.body?.cancel();\n } catch {\n // Best effort\n }\n}\n","/**\n * In-flight request deduplication.\n *\n * When multiple callers request the same URL+method+body combination\n * concurrently, only one network request is made. All callers receive\n * the same response (the promise is shared).\n *\n * Cache entries are evicted when the shared promise settles (success or\n * failure). Late subscribers that arrive after eviction start fresh.\n */\n\n/**\n * Deduplicator for in-flight HTTP requests.\n *\n * ## Usage\n * ```ts\n * const dedupe = new RequestDeduplicator();\n *\n * async function fetchDeduped(url: string, init?: RequestInit): Promise<Response> {\n * const method = init?.method ?? 'GET';\n * const key = `${method}:${url}:${JSON.stringify(init?.body ?? '')}`;\n * return dedupe.dedup(key, () => fetch(url, init), init?.signal);\n * }\n * ```\n */\nexport class RequestDeduplicator {\n #pending = new Map<string, Promise<unknown>>();\n\n /**\n * Execute `factory` once for the given key. Concurrent callers with\n * the same key receive the same promise. The entry is evicted after\n * the shared promise settles.\n *\n * @param key - Unique request key (method + URL + stable body hash).\n * @param factory - The network operation to perform.\n * @param signal - Optional AbortSignal. If it fires, this caller gets\n * an AbortError. The shared request continues.\n */\n async dedup<T>(\n key: string,\n factory: () => Promise<T>,\n signal?: AbortSignal,\n ): Promise<T> {\n const existing = this.#pending.get(key);\n if (existing !== undefined) {\n return this.#raceWithSignal(existing as Promise<T>, signal);\n }\n\n const promise = factory().finally(() => {\n this.#pending.delete(key);\n });\n this.#pending.set(key, promise);\n\n return this.#raceWithSignal(promise, signal);\n }\n\n /** How many unique requests are currently in flight. */\n get inflight(): number {\n return this.#pending.size;\n }\n\n /** Remove all pending deduplication entries. */\n clear(): void {\n this.#pending.clear();\n }\n\n /**\n * Race a promise against an AbortSignal. If the signal fires first,\n * the caller gets an AbortError but the promise still settles normally.\n */\n #raceWithSignal<T>(p: Promise<T>, s?: AbortSignal): Promise<T> {\n if (!s) return p;\n if (s.aborted) {\n return Promise.reject(\n s.reason ?? new DOMException(\"Aborted\", \"AbortError\"),\n );\n }\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n reject(s.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n };\n s.addEventListener(\"abort\", onAbort, { once: true });\n p.then(\n (v) => {\n s.removeEventListener(\"abort\", onAbort);\n resolve(v);\n },\n (e) => {\n s.removeEventListener(\"abort\", onAbort);\n reject(e);\n },\n );\n });\n }\n}\n"],"mappings":"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","names":["query"]}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lyeve-labs/client",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Framework-agnostic TypeScript SDK for the LyEve Core API",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"author": "LyEve
|
|
6
|
+
"author": "LyEve <info@lyeve.com>",
|
|
7
7
|
"type": "module",
|
|
8
|
+
"packageManager": "pnpm@9.15.0",
|
|
8
9
|
"engines": {
|
|
9
10
|
"node": ">=20"
|
|
10
11
|
},
|
|
@@ -13,9 +14,14 @@
|
|
|
13
14
|
"types": "./dist/index.d.ts",
|
|
14
15
|
"exports": {
|
|
15
16
|
".": {
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
"import": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"require": {
|
|
22
|
+
"types": "./dist/index.d.cts",
|
|
23
|
+
"default": "./dist/index.cjs"
|
|
24
|
+
}
|
|
19
25
|
},
|
|
20
26
|
"./package.json": "./package.json"
|
|
21
27
|
},
|
|
@@ -28,20 +34,20 @@
|
|
|
28
34
|
"publishConfig": {
|
|
29
35
|
"access": "public"
|
|
30
36
|
},
|
|
31
|
-
"devDependencies": {
|
|
32
|
-
"tsup": "^8.4.0",
|
|
33
|
-
"typescript": "^5.7.0",
|
|
34
|
-
"vitest": "^2.1.0",
|
|
35
|
-
"publint": "^0.3.0",
|
|
36
|
-
"prettier": "^3.4.0"
|
|
37
|
-
},
|
|
38
37
|
"scripts": {
|
|
39
38
|
"dev": "tsup --watch",
|
|
40
|
-
"build": "tsup && publint",
|
|
39
|
+
"build": "tsup && publint && node scripts/check-dist.mjs",
|
|
41
40
|
"check": "tsc --noEmit",
|
|
42
41
|
"test": "vitest run",
|
|
43
42
|
"test:watch": "vitest",
|
|
44
43
|
"format": "prettier --write .",
|
|
45
44
|
"format:check": "prettier --check ."
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"tsup": "^8.4.0",
|
|
48
|
+
"typescript": "^5.7.0",
|
|
49
|
+
"vitest": "^2.1.0",
|
|
50
|
+
"publint": "^0.3.0",
|
|
51
|
+
"prettier": "^3.4.0"
|
|
46
52
|
}
|
|
47
|
-
}
|
|
53
|
+
}
|