@lyeve-labs/client 0.2.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LyEve Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # @lyeve/cms-client
2
+
3
+ Framework-agnostic HTTP client for the LyEve Core API. The foundation all other
4
+ SDK packages build on.
5
+
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-3178c6.svg)](https://www.typescriptlang.org)
8
+
9
+ ```bash
10
+ pnpm add @lyeve/cms-client
11
+ ```
12
+
13
+ ```ts
14
+ import { createClient } from "@lyeve/cms-client";
15
+ import { getSchemas } from "@lyeve/cms-client-rest";
16
+
17
+ const client = createClient(fetch, { Authorization: "Bearer <token>" });
18
+ const schemas = await getSchemas(client);
19
+ ```
20
+
21
+ Zero dependencies. Native `fetch`. One client, every transport.
22
+
23
+ ---
24
+
25
+ ## What's in the box
26
+
27
+ - **Typed HTTP client:** `get`, `post`, `put`, `patch`, `delete`. All generic, all typed end-to-end.
28
+ - **ApiError:** thrown on every non-OK response. `status` and `message` always available.
29
+ - **Automatic JSON:** `Content-Type: application/json` added by default. 15s timeout via `AbortSignal`.
30
+ - **PaginationIterator:** async iterator over cursor-paginated endpoints. One loop, every page.
31
+ - **QueryBuilder:** fluent `query(schema).where().orderBy().limit()` builder for content queries.
32
+ - **createRetryFetch:** automatic retry with configurable exponential backoff.
33
+ - **RequestDeduplicator:** coalesce in-flight duplicate requests into a single network call.
34
+ - **Shared types:** `Schema`, `Content`, `User`, `APIKey`, `Webhook`, and 20+ more.
35
+
36
+ ## Requirements
37
+
38
+ - **Node 20** or newer
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ pnpm add @lyeve/cms-client
44
+ # or npm install @lyeve/cms-client
45
+ # or yarn add @lyeve/cms-client
46
+ ```
47
+
48
+ ## Use
49
+
50
+ ```ts
51
+ import { createClient, PaginationIterator } from "@lyeve/cms-client";
52
+
53
+ const client = createClient(fetch, {
54
+ Authorization: "Bearer <token>",
55
+ });
56
+
57
+ // GET
58
+ const data = await client.get<{ ok: boolean }>("/api/v1/health");
59
+
60
+ // POST
61
+ const result = await client.post<{ id: string }>("/api/v1/content", {
62
+ title: "Hello",
63
+ });
64
+
65
+ // Pagination
66
+ for await (const page of new PaginationIterator((cursor) =>
67
+ client.get(`/api/v1/items?after=${cursor}`),
68
+ )) {
69
+ console.log(page.items);
70
+ }
71
+
72
+ // Retry
73
+ import { createRetryFetch } from "@lyeve/cms-client";
74
+ const resilient = createRetryFetch(fetch, {
75
+ maxAttempts: 3,
76
+ baseDelay: 200,
77
+ });
78
+ const retryClient = createClient(resilient);
79
+
80
+ // Dedup
81
+ import { RequestDeduplicator } from "@lyeve/cms-client";
82
+ const dedup = new RequestDeduplicator();
83
+ const [a, b] = await Promise.all([
84
+ dedup.dedup("key-1", () => client.get("/api/v1/data")),
85
+ dedup.dedup("key-1", () => client.get("/api/v1/data")), // reuses in-flight request
86
+ ]);
87
+ ```
88
+
89
+ ## API
90
+
91
+ ### createClient(fetchFn, defaultHeaders?)
92
+
93
+ Returns `{ get, post, put, patch, delete }`. Each method is a typed generic:
94
+
95
+ ```ts
96
+ client.get<T>(url: string, init?: RequestInit): Promise<T>
97
+ client.post<T>(url: string, body?: unknown, init?: RequestInit): Promise<T>
98
+ client.put<T>(url: string, body?: unknown, init?: RequestInit): Promise<T>
99
+ client.patch<T>(url: string, body?: unknown, init?: RequestInit): Promise<T>
100
+ client.delete<T>(url: string, init?: RequestInit): Promise<T | undefined>
101
+ ```
102
+
103
+ ### ApiError
104
+
105
+ `new ApiError(status, message)`. Thrown on non-OK responses.
106
+
107
+ ### Types
108
+
109
+ Schema, SchemaField, FieldType, Content, User, APIKey, CreateAPIKeyResponse,
110
+ Webhook, WebhookDelivery, WebhookTestResult, RetryDeliveryResult, RetryConfig,
111
+ RetryConfigInput, DeadLetter, DLQStatus, PaginatedResponse\<T\>, ListResponse\<T\>,
112
+ WebhookHealthStats, GlobalHealthStats, IncomingWebhook, OAuthProvider,
113
+ Permission, Entitlements
114
+
115
+ ### Utilities
116
+
117
+ | Export | Description |
118
+ | --------------------------------- | --------------------------------------------- |
119
+ | `PaginationIterator<T>` | Async iterator for cursor-paginated endpoints |
120
+ | `QueryBuilder` / `query(schema)` | Fluent query builder for content queries |
121
+ | `createRetryFetch(fetch, config)` | Auto-retry with exponential backoff |
122
+ | `RequestDeduplicator` | Deduplicate in-flight requests by key |
123
+
124
+ ## Local development
125
+
126
+ ```bash
127
+ pnpm install # install dependencies
128
+ pnpm test # run unit tests
129
+ pnpm check # type-check
130
+ pnpm build # tsup + publint -> dist/
131
+ ```
132
+
133
+ ## Project layout
134
+
135
+ ```
136
+ src/
137
+ client.ts # createClient
138
+ index.ts # public API
139
+ types.ts # shared TypeScript types
140
+ pagination.ts # PaginationIterator
141
+ query-builder.ts # query() / QueryBuilder
142
+ retry.ts # createRetryFetch
143
+ dedupe.ts # RequestDeduplicator
144
+ tests/ # vitest test suite
145
+ ```
146
+
147
+ ## Versioning
148
+
149
+ `@lyeve/cms-client` follows [SemVer](https://semver.org). While under `1.0`,
150
+ breaking changes bump the **minor** version; additive changes bump the **patch**.
151
+ Every release is logged in [`CHANGELOG.md`](CHANGELOG.md).
152
+
153
+ ## Contributing
154
+
155
+ Bug reports and feature requests are welcome. See
156
+ [`CONTRIBUTING.md`](CONTRIBUTING.md) for the development setup and conventions.
157
+
158
+ ## License
159
+
160
+ MIT. See [`LICENSE`](LICENSE).
package/dist/index.cjs ADDED
@@ -0,0 +1,435 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ ApiError: () => ApiError,
24
+ PaginationIterator: () => PaginationIterator,
25
+ QueryBuilder: () => QueryBuilder,
26
+ RequestDeduplicator: () => RequestDeduplicator,
27
+ createClient: () => createClient,
28
+ createRetryFetch: () => createRetryFetch,
29
+ query: () => query
30
+ });
31
+ module.exports = __toCommonJS(src_exports);
32
+
33
+ // src/client.ts
34
+ var ApiError = class extends Error {
35
+ constructor(status, message) {
36
+ super(message);
37
+ this.status = status;
38
+ this.name = "ApiError";
39
+ }
40
+ status;
41
+ };
42
+ function createClient(fetchFn, defaultHeaders = {}) {
43
+ async function request(url, init) {
44
+ const res = await fetchFn(url, {
45
+ ...init,
46
+ // Default 15 s timeout so a slow/unresponsive backend can't hang
47
+ // the caller indefinitely. Callers may override by passing their
48
+ // own AbortSignal.
49
+ signal: init?.signal ?? AbortSignal.timeout(15e3),
50
+ headers: {
51
+ "Content-Type": "application/json",
52
+ ...defaultHeaders,
53
+ ...init?.headers instanceof Headers ? Object.fromEntries(init.headers.entries()) : Array.isArray(init?.headers) ? Object.fromEntries(init.headers) : init?.headers
54
+ }
55
+ });
56
+ if (!res.ok) {
57
+ const text = await res.text();
58
+ let message = text;
59
+ try {
60
+ const json = JSON.parse(text);
61
+ message = json.error ?? text;
62
+ } catch {
63
+ }
64
+ throw new ApiError(res.status, message);
65
+ }
66
+ if (res.status === 204) return void 0;
67
+ return res.json();
68
+ }
69
+ return {
70
+ get: (url, init) => request(url, { ...init, method: "GET" }),
71
+ post: (url, body, init) => request(url, { ...init, method: "POST", body: JSON.stringify(body) }),
72
+ put: (url, body, init) => request(url, { ...init, method: "PUT", body: JSON.stringify(body) }),
73
+ patch: (url, body, init) => request(url, { ...init, method: "PATCH", body: JSON.stringify(body) }),
74
+ delete: (url, init) => request(url, { ...init, method: "DELETE" })
75
+ };
76
+ }
77
+
78
+ // src/pagination.ts
79
+ var PaginationIterator = class _PaginationIterator {
80
+ #fetchPage;
81
+ #signal;
82
+ // Internal paging state
83
+ #buffer = [];
84
+ #cursor = void 0;
85
+ #exhausted = false;
86
+ #fetching = null;
87
+ constructor(config) {
88
+ this.#fetchPage = config.fetchPage;
89
+ }
90
+ /**
91
+ * Return a new iterator sharing the same underlying state but with a
92
+ * different AbortSignal. Useful when the signal is only known at the
93
+ * call site, not at construction time.
94
+ */
95
+ withSignal(signal) {
96
+ const clone = new _PaginationIterator({
97
+ fetchPage: this.#fetchPage
98
+ });
99
+ clone.#signal = signal;
100
+ clone.#buffer = this.#buffer;
101
+ clone.#cursor = this.#cursor;
102
+ clone.#exhausted = this.#exhausted;
103
+ clone.#fetching = this.#fetching;
104
+ return clone;
105
+ }
106
+ // AsyncIterator protocol
107
+ [Symbol.asyncIterator]() {
108
+ return this;
109
+ }
110
+ async next() {
111
+ this.#throwIfAborted();
112
+ if (this.#buffer.length > 0) {
113
+ const value = this.#buffer.shift();
114
+ return { value, done: false };
115
+ }
116
+ if (this.#exhausted) {
117
+ return { value: void 0, done: true };
118
+ }
119
+ try {
120
+ await this.#fetchPageInternal();
121
+ } catch (err) {
122
+ if (err instanceof Error) throw err;
123
+ throw new Error(String(err));
124
+ }
125
+ if (this.#buffer.length > 0) {
126
+ const value = this.#buffer.shift();
127
+ return { value, done: false };
128
+ }
129
+ return { value: void 0, done: true };
130
+ }
131
+ return(value) {
132
+ this.#exhausted = true;
133
+ this.#buffer.length = 0;
134
+ return Promise.resolve({
135
+ value,
136
+ done: true
137
+ });
138
+ }
139
+ throw(e) {
140
+ this.#exhausted = true;
141
+ this.#buffer.length = 0;
142
+ return Promise.reject(e);
143
+ }
144
+ // Internal
145
+ async #fetchPageInternal() {
146
+ if (this.#fetching) {
147
+ await this.#fetching;
148
+ return;
149
+ }
150
+ this.#throwIfAborted();
151
+ this.#fetching = this.#fetchPage(this.#cursor).then((page) => {
152
+ this.#throwIfAborted();
153
+ this.#buffer = [...page.items];
154
+ if (page.next_cursor) {
155
+ this.#cursor = page.next_cursor;
156
+ } else {
157
+ this.#exhausted = true;
158
+ }
159
+ }).finally(() => {
160
+ this.#fetching = null;
161
+ });
162
+ await this.#fetching;
163
+ }
164
+ #throwIfAborted() {
165
+ if (this.#signal?.aborted) {
166
+ throw this.#signal.reason ?? new DOMException("Aborted", "AbortError");
167
+ }
168
+ }
169
+ };
170
+
171
+ // src/query-builder.ts
172
+ var QueryBuilder = class {
173
+ #schema;
174
+ #status;
175
+ #limit;
176
+ #offset;
177
+ #cursor;
178
+ #filters = {};
179
+ #sort;
180
+ constructor(schema) {
181
+ this.#schema = schema;
182
+ }
183
+ /** The schema/collection this query targets. */
184
+ get schema() {
185
+ return this.#schema;
186
+ }
187
+ /** Filter by content status (published, draft, archived). */
188
+ whereStatus(s) {
189
+ this.#status = s;
190
+ return this;
191
+ }
192
+ /** Add a field filter in `op:value` format. */
193
+ where(field, value) {
194
+ this.#filters[field] = value;
195
+ return this;
196
+ }
197
+ /** Add a field-level equality filter. */
198
+ whereEq(field, value) {
199
+ this.#filters[field] = `eq:${String(value)}`;
200
+ return this;
201
+ }
202
+ /** Add a field-level "in" filter (value is a JSON array). */
203
+ whereIn(field, values) {
204
+ this.#filters[field] = `in:${JSON.stringify(values)}`;
205
+ return this;
206
+ }
207
+ /** Greater-than comparison. */
208
+ whereGt(field, value) {
209
+ this.#filters[field] = `gt:${value}`;
210
+ return this;
211
+ }
212
+ /** Greater-than-or-equal comparison. */
213
+ whereGte(field, value) {
214
+ this.#filters[field] = `gte:${value}`;
215
+ return this;
216
+ }
217
+ /** Less-than comparison. */
218
+ whereLt(field, value) {
219
+ this.#filters[field] = `lt:${value}`;
220
+ return this;
221
+ }
222
+ /** Less-than-or-equal comparison. */
223
+ whereLte(field, value) {
224
+ this.#filters[field] = `lte:${value}`;
225
+ return this;
226
+ }
227
+ /** Full-text search (if the schema supports it). */
228
+ whereSearch(field, query2) {
229
+ this.#filters[field] = `search:${query2}`;
230
+ return this;
231
+ }
232
+ /** Set the max records per page. */
233
+ limit(n) {
234
+ this.#limit = n;
235
+ return this;
236
+ }
237
+ /** Set the offset for offset-based pagination. Clears cursor. */
238
+ offset(n) {
239
+ this.#offset = n;
240
+ if (n !== void 0) this.#cursor = void 0;
241
+ return this;
242
+ }
243
+ /** Set the cursor for cursor-based pagination. Clears offset. */
244
+ cursor(c) {
245
+ this.#cursor = c;
246
+ if (c !== void 0) this.#offset = void 0;
247
+ return this;
248
+ }
249
+ /** Set sort fields. Prefix with `-` for descending. */
250
+ sort(...fields) {
251
+ this.#sort = fields.join(",");
252
+ return this;
253
+ }
254
+ /** Clear all filters, keeping only the schema name. */
255
+ resetFilters() {
256
+ this.#filters = {};
257
+ this.#status = void 0;
258
+ this.#sort = void 0;
259
+ this.#limit = void 0;
260
+ this.#offset = void 0;
261
+ this.#cursor = void 0;
262
+ return this;
263
+ }
264
+ /** Produce the ContentQuery object. */
265
+ build() {
266
+ return {
267
+ status: this.#status,
268
+ limit: this.#limit,
269
+ offset: this.#offset,
270
+ cursor: this.#cursor,
271
+ filters: Object.keys(this.#filters).length > 0 ? { ...this.#filters } : void 0,
272
+ sort: this.#sort
273
+ };
274
+ }
275
+ };
276
+ function query(schema) {
277
+ return new QueryBuilder(schema);
278
+ }
279
+
280
+ // src/retry.ts
281
+ var DEFAULT_RETRY_ON = [429, 500, 502, 503, 504];
282
+ function jitter(cap) {
283
+ return Math.floor(Math.random() * cap);
284
+ }
285
+ function backoff(attempt, base, max) {
286
+ return jitter(Math.min(max, base * Math.pow(2, attempt)));
287
+ }
288
+ async function sleep(ms, signal) {
289
+ return new Promise((resolve, reject) => {
290
+ if (signal?.aborted) {
291
+ reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
292
+ return;
293
+ }
294
+ const timer = setTimeout(resolve, ms);
295
+ if (!signal) return;
296
+ const onAbort = () => {
297
+ clearTimeout(timer);
298
+ reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
299
+ };
300
+ signal.addEventListener("abort", onAbort, { once: true });
301
+ });
302
+ }
303
+ function createRetryFetch(fetchImpl, config) {
304
+ const maxRetries = config?.maxRetries ?? 3;
305
+ const baseDelay = config?.baseDelay ?? 1e3;
306
+ const maxDelay = config?.maxDelay ?? 3e4;
307
+ const retryOn = config?.retryOn ?? DEFAULT_RETRY_ON;
308
+ const retryOnNetworkError = config?.retryOnNetworkError ?? true;
309
+ return async (input, init) => {
310
+ let lastError;
311
+ const signal = init?.signal;
312
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
313
+ try {
314
+ let resolvedInit = init;
315
+ if (attempt > 0 && init?.body) {
316
+ resolvedInit = { ...init, body: await cloneBody(init.body) };
317
+ }
318
+ const res = await fetchImpl(input, resolvedInit);
319
+ if (!retryOn.includes(res.status) || attempt === maxRetries) {
320
+ return res;
321
+ }
322
+ await drainBody(res);
323
+ lastError = new Error(`HTTP ${res.status}`);
324
+ } catch (err) {
325
+ if (signal?.aborted) throw err;
326
+ if (!retryOnNetworkError || attempt === maxRetries) throw err;
327
+ lastError = err;
328
+ }
329
+ const delay = backoff(attempt, baseDelay, maxDelay);
330
+ config?.onRetry?.(attempt + 1, lastError, delay);
331
+ await sleep(delay, signal ?? void 0);
332
+ }
333
+ throw lastError;
334
+ };
335
+ }
336
+ async function cloneBody(body) {
337
+ if (body === null || body === void 0) return null;
338
+ if (typeof body === "string") return body;
339
+ if (body instanceof URLSearchParams) return new URLSearchParams(body);
340
+ if (body instanceof FormData) return body;
341
+ if (body instanceof Blob) return body;
342
+ if (body instanceof ArrayBuffer) return body.slice(0);
343
+ if (body instanceof ReadableStream) {
344
+ throw new Error(
345
+ "Cannot retry a request with a ReadableStream body. Use string or JSON."
346
+ );
347
+ }
348
+ if (ArrayBuffer.isView(body)) {
349
+ return new Uint8Array(
350
+ body.buffer,
351
+ body.byteOffset,
352
+ body.byteLength
353
+ ).slice();
354
+ }
355
+ return body;
356
+ }
357
+ async function drainBody(res) {
358
+ try {
359
+ await res.body?.cancel();
360
+ } catch {
361
+ }
362
+ }
363
+
364
+ // src/dedupe.ts
365
+ var RequestDeduplicator = class {
366
+ #pending = /* @__PURE__ */ new Map();
367
+ /**
368
+ * Execute `factory` once for the given key. Concurrent callers with
369
+ * the same key receive the same promise. The entry is evicted after
370
+ * the shared promise settles.
371
+ *
372
+ * @param key - Unique request key (method + URL + stable body hash).
373
+ * @param factory - The network operation to perform.
374
+ * @param signal - Optional AbortSignal. If it fires, this caller gets
375
+ * an AbortError. The shared request continues.
376
+ */
377
+ async dedup(key, factory, signal) {
378
+ const existing = this.#pending.get(key);
379
+ if (existing !== void 0) {
380
+ return this.#raceWithSignal(existing, signal);
381
+ }
382
+ const promise = factory().finally(() => {
383
+ this.#pending.delete(key);
384
+ });
385
+ this.#pending.set(key, promise);
386
+ return this.#raceWithSignal(promise, signal);
387
+ }
388
+ /** How many unique requests are currently in flight. */
389
+ get inflight() {
390
+ return this.#pending.size;
391
+ }
392
+ /** Remove all pending deduplication entries. */
393
+ clear() {
394
+ this.#pending.clear();
395
+ }
396
+ /**
397
+ * Race a promise against an AbortSignal. If the signal fires first,
398
+ * the caller gets an AbortError but the promise still settles normally.
399
+ */
400
+ #raceWithSignal(p, s) {
401
+ if (!s) return p;
402
+ if (s.aborted) {
403
+ return Promise.reject(
404
+ s.reason ?? new DOMException("Aborted", "AbortError")
405
+ );
406
+ }
407
+ return new Promise((resolve, reject) => {
408
+ const onAbort = () => {
409
+ reject(s.reason ?? new DOMException("Aborted", "AbortError"));
410
+ };
411
+ s.addEventListener("abort", onAbort, { once: true });
412
+ p.then(
413
+ (v) => {
414
+ s.removeEventListener("abort", onAbort);
415
+ resolve(v);
416
+ },
417
+ (e) => {
418
+ s.removeEventListener("abort", onAbort);
419
+ reject(e);
420
+ }
421
+ );
422
+ });
423
+ }
424
+ };
425
+ // Annotate the CommonJS export names for ESM import in node:
426
+ 0 && (module.exports = {
427
+ ApiError,
428
+ PaginationIterator,
429
+ QueryBuilder,
430
+ RequestDeduplicator,
431
+ createClient,
432
+ createRetryFetch,
433
+ query
434
+ });
435
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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"]}