backlex 0.1.0 → 0.1.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/dist/index.d.ts +456 -349
- package/dist/index.js +35 -1
- package/dist/{types-CbcbXGiA.d.ts → types-DiJCXOy-.d.ts} +43 -1
- package/dist/types.d.ts +1 -1
- package/dist/webhook.d.ts +13 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,83 +1,7 @@
|
|
|
1
|
-
import { L as ListQuery,
|
|
2
|
-
export { B as BacklexError, U as Upload,
|
|
1
|
+
import { L as ListQuery, m as ListResponse, C as Condition, h as DurationParts, R as RelativeNow, A as AggregateQuery, a as AggregateRow, S as SearchQuery, o as SearchResponse, I as ImportSummary, j as ItemQuery, k as ItemResponse, d as BatchResponse, f as BulkUpdateResponse, c as BatchOperation, i as ItemEvent, n as ResumableUploadResult, D as DeviceToken, P as PhoneNumber, l as JobStatus, J as Job, F as FlagState } from './types-DiJCXOy-.js';
|
|
2
|
+
export { B as BacklexError, U as Upload, p as UploadStatus } from './types-DiJCXOy-.js';
|
|
3
3
|
export { VerifyWebhookOptions, verifyWebhook } from './webhook.js';
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Offline-first sync for a single collection.
|
|
7
|
-
*
|
|
8
|
-
* Pulls the server changefeed (`GET /api/items/:slug/changes`) into a local
|
|
9
|
-
* store, keeps it live over SSE, and lets the app read/write while offline:
|
|
10
|
-
* writes apply optimistically to the local store and queue, then flush through
|
|
11
|
-
* the batch endpoint on reconnect. Conflicts resolve last-write-wins by
|
|
12
|
-
* `updated_at`, with locally-queued (not-yet-flushed) writes held until they
|
|
13
|
-
* land.
|
|
14
|
-
*
|
|
15
|
-
* The store is pluggable — `memoryStore()` works anywhere; `indexedDbStore()`
|
|
16
|
-
* persists across reloads in the browser.
|
|
17
|
-
*/
|
|
18
|
-
type QueuedOp = {
|
|
19
|
-
kind: "create";
|
|
20
|
-
tempId: string;
|
|
21
|
-
data: Record<string, unknown>;
|
|
22
|
-
} | {
|
|
23
|
-
kind: "update";
|
|
24
|
-
id: string;
|
|
25
|
-
data: Record<string, unknown>;
|
|
26
|
-
} | {
|
|
27
|
-
kind: "delete";
|
|
28
|
-
id: string;
|
|
29
|
-
};
|
|
30
|
-
interface SyncStore {
|
|
31
|
-
get(id: string): Promise<Record<string, unknown> | undefined>;
|
|
32
|
-
set(id: string, row: Record<string, unknown>): Promise<void>;
|
|
33
|
-
remove(id: string): Promise<void>;
|
|
34
|
-
all(): Promise<Record<string, unknown>[]>;
|
|
35
|
-
getMeta(key: string): Promise<string | null>;
|
|
36
|
-
setMeta(key: string, value: string): Promise<void>;
|
|
37
|
-
queueGet(): Promise<QueuedOp[]>;
|
|
38
|
-
queueSet(ops: QueuedOp[]): Promise<void>;
|
|
39
|
-
}
|
|
40
|
-
/** In-memory store — non-persistent; the default and the testing baseline. */
|
|
41
|
-
declare const memoryStore: () => SyncStore;
|
|
42
|
-
/** IndexedDB-backed store (browser). One object store for rows + one for meta;
|
|
43
|
-
* the write queue lives under a meta key. Falls back to throwing if IndexedDB
|
|
44
|
-
* is unavailable — use `memoryStore()` outside the browser. */
|
|
45
|
-
declare const indexedDbStore: (opts: {
|
|
46
|
-
collection: string;
|
|
47
|
-
dbName?: string;
|
|
48
|
-
}) => SyncStore;
|
|
49
|
-
/** The subset of the backlex client `createSync` needs. The full client satisfies it. */
|
|
50
|
-
interface SyncClientLike {
|
|
51
|
-
request: <T>(method: string, path: string, body?: unknown) => Promise<T>;
|
|
52
|
-
subscribe: (channel: string, onEvent: (e: {
|
|
53
|
-
event: "created" | "updated" | "deleted";
|
|
54
|
-
data: Record<string, unknown>;
|
|
55
|
-
}) => void, onError?: (err: unknown) => void) => () => void;
|
|
56
|
-
}
|
|
57
|
-
interface SyncOptions {
|
|
58
|
-
collection: string;
|
|
59
|
-
store?: SyncStore;
|
|
60
|
-
/** Primary-key field. Default `id`. */
|
|
61
|
-
pk?: string;
|
|
62
|
-
/** Rows per changefeed page. Default 200. */
|
|
63
|
-
pageSize?: number;
|
|
64
|
-
/** Called after the local store changes (pull / live / local write). */
|
|
65
|
-
onChange?: () => void;
|
|
66
|
-
}
|
|
67
|
-
declare const createSync: (client: SyncClientLike, options: SyncOptions) => {
|
|
68
|
-
pull: () => Promise<number>;
|
|
69
|
-
flush: () => Promise<void>;
|
|
70
|
-
live: () => (() => void);
|
|
71
|
-
start: () => Promise<void>;
|
|
72
|
-
stop: () => void;
|
|
73
|
-
getAll: () => Promise<Record<string, unknown>[]>;
|
|
74
|
-
get: (id: string) => Promise<Record<string, unknown> | undefined>;
|
|
75
|
-
create: (data: Record<string, unknown>) => Promise<string>;
|
|
76
|
-
update: (id: string, data: Record<string, unknown>) => Promise<void>;
|
|
77
|
-
remove: (id: string) => Promise<void>;
|
|
78
|
-
store: SyncStore;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
5
|
/**
|
|
82
6
|
* Type-safe fluent query builder — a Drizzle/Supabase-style ergonomics layer
|
|
83
7
|
* that COMPILES to the canonical JSON `Condition` / `ListQuery` the REST API
|
|
@@ -104,25 +28,46 @@ declare const createSync: (client: SyncClientLike, options: SyncOptions) => {
|
|
|
104
28
|
type FieldKey<T> = (keyof T & string) | (string & {});
|
|
105
29
|
/** `field` ascending, or `-field` descending. */
|
|
106
30
|
type SortKey<T> = FieldKey<T> | `-${string}`;
|
|
31
|
+
/** Fluent condition factory passed to `QueryBuilder.where(...)`. Each method
|
|
32
|
+
* returns a canonical {@link Condition}; multiple are combined with `and`/`or`. */
|
|
107
33
|
interface FilterBuilder<T> {
|
|
34
|
+
/** `field = value`. */
|
|
108
35
|
eq(field: FieldKey<T>, value: unknown): Condition;
|
|
36
|
+
/** `field != value`. */
|
|
109
37
|
neq(field: FieldKey<T>, value: unknown): Condition;
|
|
38
|
+
/** `field > value`. */
|
|
110
39
|
gt(field: FieldKey<T>, value: unknown): Condition;
|
|
40
|
+
/** `field >= value`. */
|
|
111
41
|
gte(field: FieldKey<T>, value: unknown): Condition;
|
|
42
|
+
/** `field < value`. */
|
|
112
43
|
lt(field: FieldKey<T>, value: unknown): Condition;
|
|
44
|
+
/** `field <= value`. */
|
|
113
45
|
lte(field: FieldKey<T>, value: unknown): Condition;
|
|
46
|
+
/** `field IN (values)`. */
|
|
114
47
|
in(field: FieldKey<T>, values: unknown[]): Condition;
|
|
48
|
+
/** `field NOT IN (values)`. */
|
|
115
49
|
nin(field: FieldKey<T>, values: unknown[]): Condition;
|
|
50
|
+
/** `field BETWEEN lo AND hi` (inclusive). */
|
|
116
51
|
between(field: FieldKey<T>, lo: unknown, hi: unknown): Condition;
|
|
52
|
+
/** `field IS NULL` (or `IS NOT NULL` when `isNull` is false). */
|
|
117
53
|
isNull(field: FieldKey<T>, isNull?: boolean): Condition;
|
|
54
|
+
/** `field` is NULL or empty string. */
|
|
118
55
|
empty(field: FieldKey<T>): Condition;
|
|
56
|
+
/** `field` is neither NULL nor empty string. */
|
|
119
57
|
nempty(field: FieldKey<T>): Condition;
|
|
58
|
+
/** `field` contains the substring `value`. */
|
|
120
59
|
contains(field: FieldKey<T>, value: string): Condition;
|
|
60
|
+
/** Case-insensitive {@link contains}. */
|
|
121
61
|
icontains(field: FieldKey<T>, value: string): Condition;
|
|
62
|
+
/** `field` starts with `value`. */
|
|
122
63
|
startsWith(field: FieldKey<T>, value: string): Condition;
|
|
64
|
+
/** `field` ends with `value`. */
|
|
123
65
|
endsWith(field: FieldKey<T>, value: string): Condition;
|
|
66
|
+
/** Logical AND of the given conditions. */
|
|
124
67
|
and(...conds: Condition[]): Condition;
|
|
68
|
+
/** Logical OR of the given conditions. */
|
|
125
69
|
or(...conds: Condition[]): Condition;
|
|
70
|
+
/** Logical NOT of a condition. */
|
|
126
71
|
not(cond: Condition): Condition;
|
|
127
72
|
/** Traverse a relation: keys produced by `build` are prefixed with `head.`. */
|
|
128
73
|
rel<R = Record<string, unknown>>(head: FieldKey<T>, build: (f: FilterBuilder<R>) => Condition): Condition;
|
|
@@ -132,6 +77,8 @@ interface FilterBuilder<T> {
|
|
|
132
77
|
sub?: DurationParts;
|
|
133
78
|
}): RelativeNow;
|
|
134
79
|
}
|
|
80
|
+
/** Fluent, type-safe query builder returned by `from(slug).query()`. Chain
|
|
81
|
+
* `.where`/`.select`/`.orderBy`/… and finish with `.list()` (or `.toQuery()`). */
|
|
135
82
|
declare class QueryBuilder<T extends Record<string, unknown>> {
|
|
136
83
|
private readonly listFn;
|
|
137
84
|
private _filter?;
|
|
@@ -144,10 +91,13 @@ declare class QueryBuilder<T extends Record<string, unknown>> {
|
|
|
144
91
|
private _locale?;
|
|
145
92
|
private _q?;
|
|
146
93
|
constructor(listFn: (q: ListQuery) => Promise<ListResponse<T>>);
|
|
94
|
+
/** Build the filter fluently with a {@link FilterBuilder}. */
|
|
147
95
|
where(build: (f: FilterBuilder<T>) => Condition): this;
|
|
148
96
|
/** Replace the filter with a raw canonical condition (escape hatch). */
|
|
149
97
|
filter(cond: Condition): this;
|
|
98
|
+
/** Project only these fields (column allow-list). */
|
|
150
99
|
select(...fields: FieldKey<T>[]): this;
|
|
100
|
+
/** Sort by one or more keys (`"-field"` for descending). */
|
|
151
101
|
orderBy(...sorts: SortKey<T>[]): this;
|
|
152
102
|
/** Inline single-hop relations (replaces each FK with the related object). */
|
|
153
103
|
expand(...rels: FieldKey<T>[]): this;
|
|
@@ -155,14 +105,128 @@ declare class QueryBuilder<T extends Record<string, unknown>> {
|
|
|
155
105
|
locale(loc: string): this;
|
|
156
106
|
/** Free-text search across readable text fields. */
|
|
157
107
|
search(text: string): this;
|
|
108
|
+
/** Max rows to return. */
|
|
158
109
|
limit(n: number): this;
|
|
110
|
+
/** Number of rows to skip (paging). */
|
|
159
111
|
offset(n: number): this;
|
|
112
|
+
/** Ask the server for a count alongside the page (`filter_count` / `total_count`). */
|
|
160
113
|
withMeta(m: "filter_count" | "total_count" | "*"): this;
|
|
161
114
|
/** Assemble the plain `ListQuery` — the canonical JSON the REST API takes. */
|
|
162
115
|
toQuery(): ListQuery;
|
|
116
|
+
/** Run the query and return the page of rows. */
|
|
163
117
|
list(): Promise<ListResponse<T>>;
|
|
164
118
|
}
|
|
165
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Offline-first sync for a single collection.
|
|
122
|
+
*
|
|
123
|
+
* Pulls the server changefeed (`GET /api/items/:slug/changes`) into a local
|
|
124
|
+
* store, keeps it live over SSE, and lets the app read/write while offline:
|
|
125
|
+
* writes apply optimistically to the local store and queue, then flush through
|
|
126
|
+
* the batch endpoint on reconnect. Conflicts resolve last-write-wins by
|
|
127
|
+
* `updated_at`, with locally-queued (not-yet-flushed) writes held until they
|
|
128
|
+
* land.
|
|
129
|
+
*
|
|
130
|
+
* The store is pluggable — `memoryStore()` works anywhere; `indexedDbStore()`
|
|
131
|
+
* persists across reloads in the browser.
|
|
132
|
+
*/
|
|
133
|
+
/** A locally-queued offline write awaiting flush to the server. */
|
|
134
|
+
type QueuedOp = {
|
|
135
|
+
kind: "create";
|
|
136
|
+
tempId: string;
|
|
137
|
+
data: Record<string, unknown>;
|
|
138
|
+
} | {
|
|
139
|
+
kind: "update";
|
|
140
|
+
id: string;
|
|
141
|
+
data: Record<string, unknown>;
|
|
142
|
+
} | {
|
|
143
|
+
kind: "delete";
|
|
144
|
+
id: string;
|
|
145
|
+
};
|
|
146
|
+
/** Pluggable persistence for the local sync store (rows + meta + write queue). */
|
|
147
|
+
interface SyncStore {
|
|
148
|
+
get(id: string): Promise<Record<string, unknown> | undefined>;
|
|
149
|
+
set(id: string, row: Record<string, unknown>): Promise<void>;
|
|
150
|
+
remove(id: string): Promise<void>;
|
|
151
|
+
all(): Promise<Record<string, unknown>[]>;
|
|
152
|
+
getMeta(key: string): Promise<string | null>;
|
|
153
|
+
setMeta(key: string, value: string): Promise<void>;
|
|
154
|
+
queueGet(): Promise<QueuedOp[]>;
|
|
155
|
+
queueSet(ops: QueuedOp[]): Promise<void>;
|
|
156
|
+
}
|
|
157
|
+
/** In-memory store — non-persistent; the default and the testing baseline. */
|
|
158
|
+
declare const memoryStore: () => SyncStore;
|
|
159
|
+
/** IndexedDB-backed store (browser). One object store for rows + one for meta;
|
|
160
|
+
* the write queue lives under a meta key. Falls back to throwing if IndexedDB
|
|
161
|
+
* is unavailable — use `memoryStore()` outside the browser. */
|
|
162
|
+
declare const indexedDbStore: (opts: {
|
|
163
|
+
collection: string;
|
|
164
|
+
dbName?: string;
|
|
165
|
+
}) => SyncStore;
|
|
166
|
+
/** The subset of the backlex client `createSync` needs. The full client satisfies it. */
|
|
167
|
+
interface SyncClientLike {
|
|
168
|
+
request: <T>(method: string, path: string, body?: unknown) => Promise<T>;
|
|
169
|
+
subscribe: (channel: string, onEvent: (e: {
|
|
170
|
+
event: "created" | "updated" | "deleted";
|
|
171
|
+
data: Record<string, unknown>;
|
|
172
|
+
}) => void, onError?: (err: unknown) => void) => () => void;
|
|
173
|
+
}
|
|
174
|
+
/** Options for `client.sync(...)` / `createSync(...)`. */
|
|
175
|
+
interface SyncOptions {
|
|
176
|
+
collection: string;
|
|
177
|
+
store?: SyncStore;
|
|
178
|
+
/** Primary-key field. Default `id`. */
|
|
179
|
+
pk?: string;
|
|
180
|
+
/** Rows per changefeed page. Default 200. */
|
|
181
|
+
pageSize?: number;
|
|
182
|
+
/** Called after the local store changes (pull / live / local write). */
|
|
183
|
+
onChange?: () => void;
|
|
184
|
+
}
|
|
185
|
+
/** Live, offline-first controller for one collection, returned by `createSync`. */
|
|
186
|
+
interface SyncController {
|
|
187
|
+
/** Drain the changefeed from the saved cursor to head; returns rows applied. */
|
|
188
|
+
pull(): Promise<number>;
|
|
189
|
+
/** Flush queued offline writes through the batch endpoint. */
|
|
190
|
+
flush(): Promise<void>;
|
|
191
|
+
/** Subscribe to live SSE updates; returns an unsubscribe function. */
|
|
192
|
+
live(): () => void;
|
|
193
|
+
/** Pull, go live, and re-sync whenever connectivity returns. */
|
|
194
|
+
start(): Promise<void>;
|
|
195
|
+
/** Stop live updates and remove the online listener. */
|
|
196
|
+
stop(): void;
|
|
197
|
+
/** Every row currently in the local store. */
|
|
198
|
+
getAll(): Promise<Record<string, unknown>[]>;
|
|
199
|
+
/** One row by id from the local store. */
|
|
200
|
+
get(id: string): Promise<Record<string, unknown> | undefined>;
|
|
201
|
+
/** Optimistically create a row locally + queue the write; returns the temp id. */
|
|
202
|
+
create(data: Record<string, unknown>): Promise<string>;
|
|
203
|
+
/** Optimistically update a row locally + queue the write. */
|
|
204
|
+
update(id: string, data: Record<string, unknown>): Promise<void>;
|
|
205
|
+
/** Optimistically remove a row locally + queue the delete. */
|
|
206
|
+
remove(id: string): Promise<void>;
|
|
207
|
+
/** The underlying pluggable local store. */
|
|
208
|
+
store: SyncStore;
|
|
209
|
+
}
|
|
210
|
+
declare const createSync: (client: SyncClientLike, options: SyncOptions) => SyncController;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @module
|
|
214
|
+
*
|
|
215
|
+
* The backlex client — a typed `fetch` wrapper over the backlex API. Create one
|
|
216
|
+
* with {@link createClient}, then use `.from(slug)` for typed collection CRUD,
|
|
217
|
+
* `.auth` for sign-in/up, `.subscribe` for realtime (SSE), plus `.storage`,
|
|
218
|
+
* `.jobs`, `.flags`, and offline-first `.sync`.
|
|
219
|
+
*
|
|
220
|
+
* ```ts
|
|
221
|
+
* import { createClient } from "backlex";
|
|
222
|
+
*
|
|
223
|
+
* const backlex = createClient({ url: "https://api.your.app", workspace: "acme" });
|
|
224
|
+
* await backlex.auth.signIn({ email, password });
|
|
225
|
+
* const { data } = await backlex.from("todos").list({ sort: ["-created_at"] });
|
|
226
|
+
* const off = backlex.subscribe("items:todos", (e) => console.log(e.event, e.data));
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
|
|
166
230
|
interface ClientOptions {
|
|
167
231
|
url: string;
|
|
168
232
|
/** Static API key (`pak_...`) for server-to-server calls. Browser apps
|
|
@@ -191,16 +255,19 @@ interface ClientOptions {
|
|
|
191
255
|
/** Optional fetch override (testing / Node polyfill). */
|
|
192
256
|
fetch?: typeof fetch;
|
|
193
257
|
}
|
|
258
|
+
/** A signed-in user as returned by the auth surface. */
|
|
194
259
|
interface AuthUser {
|
|
195
260
|
id: string;
|
|
196
261
|
email: string;
|
|
197
262
|
name?: string | null;
|
|
198
263
|
image?: string | null;
|
|
199
264
|
}
|
|
265
|
+
/** Result of a sign-in / sign-up — the user and (app mode) the session token. */
|
|
200
266
|
interface AuthResult {
|
|
201
267
|
user: AuthUser;
|
|
202
268
|
token?: string;
|
|
203
269
|
}
|
|
270
|
+
/** One active session (device/login) of the signed-in user. */
|
|
204
271
|
interface AuthSession {
|
|
205
272
|
id: string;
|
|
206
273
|
token: string;
|
|
@@ -211,12 +278,14 @@ interface AuthSession {
|
|
|
211
278
|
createdAt: string;
|
|
212
279
|
updatedAt: string;
|
|
213
280
|
}
|
|
281
|
+
/** A public sign-in provider as advertised by `auth.providers()`. */
|
|
214
282
|
interface PublicProvider {
|
|
215
283
|
id: string;
|
|
216
284
|
kind: "credential" | "magic-link" | "email-otp" | "passkey" | "social";
|
|
217
285
|
label: string;
|
|
218
286
|
enabled: boolean;
|
|
219
287
|
}
|
|
288
|
+
/** Public description of a workspace's auth surface (provider list + policy). */
|
|
220
289
|
interface AuthSurface {
|
|
221
290
|
tenantId: string | null;
|
|
222
291
|
providers: PublicProvider[];
|
|
@@ -261,6 +330,7 @@ interface CollectionClient<T extends Record<string, unknown>> {
|
|
|
261
330
|
deleteMany(ids: string[], opts?: {
|
|
262
331
|
atomic?: boolean;
|
|
263
332
|
}): Promise<BatchResponse<T>>;
|
|
333
|
+
bulkUpdate(keys: string[], data: Partial<T>): Promise<BulkUpdateResponse>;
|
|
264
334
|
batch(operations: BatchOperation<T>[], opts?: {
|
|
265
335
|
atomic?: boolean;
|
|
266
336
|
}): Promise<BatchResponse<T>>;
|
|
@@ -268,278 +338,317 @@ interface CollectionClient<T extends Record<string, unknown>> {
|
|
|
268
338
|
unpublish(id: string): Promise<ItemResponse<T>>;
|
|
269
339
|
schedulePublish(id: string, at: Date | string | null): Promise<ItemResponse<T>>;
|
|
270
340
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
/** Public description of this workspace's auth surface (provider list +
|
|
384
|
-
* policy flags) — what a sign-in screen needs to render. No secrets. */
|
|
385
|
-
providers: () => Promise<AuthSurface>;
|
|
386
|
-
/** The current workspace session token (app mode) — persist this across
|
|
387
|
-
* reloads and pass it back via `createClient({ token })`. */
|
|
388
|
-
getToken: () => string | null;
|
|
389
|
-
/** Restore a workspace session token (app mode). */
|
|
390
|
-
setToken: (token: string | null) => void;
|
|
391
|
-
};
|
|
392
|
-
storage: {
|
|
393
|
-
list: (prefix?: string) => Promise<{
|
|
394
|
-
data: {
|
|
395
|
-
key: string;
|
|
396
|
-
size: number;
|
|
397
|
-
contentType?: string;
|
|
398
|
-
ownerId: string | null;
|
|
399
|
-
uploadedAt: string;
|
|
400
|
-
}[];
|
|
401
|
-
}>;
|
|
402
|
-
put: (key: string, body: BodyInit, contentType?: string, folderId?: string) => Promise<any>;
|
|
403
|
-
download: (key: string) => Promise<Response>;
|
|
404
|
-
delete: (key: string) => Promise<{
|
|
405
|
-
ok: boolean;
|
|
406
|
-
}>;
|
|
407
|
-
/**
|
|
408
|
-
* Resumable upload (TUS 1.0.0). Splits `data` into chunks and PATCHes them
|
|
409
|
-
* to `/api/uploads`, resuming from the server's committed offset after a
|
|
410
|
-
* transient failure. `data` may be a `Blob`/`File`, `ArrayBuffer`, or
|
|
411
|
-
* `Uint8Array`. Returns the final key + the TUS session `location` (persist
|
|
412
|
-
* it to resume across page reloads via `resumeUpload`). The standard TUS
|
|
413
|
-
* protocol means Uppy / tus-js-client can also target `/api/uploads`.
|
|
414
|
-
*/
|
|
415
|
-
uploadResumable: (input: {
|
|
341
|
+
/** Auth surface for a workspace's end-users (and the admin pool). See `createClient`. */
|
|
342
|
+
interface AuthClient {
|
|
343
|
+
/** Email + password sign-up (app mode → a workspace end-user). */
|
|
344
|
+
signUp(input: {
|
|
345
|
+
email: string;
|
|
346
|
+
password: string;
|
|
347
|
+
name?: string;
|
|
348
|
+
}): Promise<AuthResult>;
|
|
349
|
+
/** Email + password sign-in. */
|
|
350
|
+
signIn(input: {
|
|
351
|
+
email: string;
|
|
352
|
+
password: string;
|
|
353
|
+
}): Promise<AuthResult>;
|
|
354
|
+
/** Begin an OAuth sign-in; returns the provider authorize `url` to navigate to. */
|
|
355
|
+
signInSocial(provider: string, input?: {
|
|
356
|
+
callbackURL?: string;
|
|
357
|
+
errorCallbackURL?: string;
|
|
358
|
+
}): Promise<{
|
|
359
|
+
url: string;
|
|
360
|
+
redirect: boolean;
|
|
361
|
+
}>;
|
|
362
|
+
/** Send a one-time sign-in link by email (magic-link provider). */
|
|
363
|
+
signInMagicLink(input: {
|
|
364
|
+
email: string;
|
|
365
|
+
callbackURL?: string;
|
|
366
|
+
}): Promise<{
|
|
367
|
+
status: boolean;
|
|
368
|
+
}>;
|
|
369
|
+
/** Email a one-time numeric code (email-otp provider). */
|
|
370
|
+
sendVerificationOTP(input: {
|
|
371
|
+
email: string;
|
|
372
|
+
type?: "sign-in" | "email-verification" | "forget-password";
|
|
373
|
+
}): Promise<{
|
|
374
|
+
success: boolean;
|
|
375
|
+
}>;
|
|
376
|
+
/** Complete an email-OTP sign-in with the emailed code. */
|
|
377
|
+
signInEmailOTP(input: {
|
|
378
|
+
email: string;
|
|
379
|
+
otp: string;
|
|
380
|
+
}): Promise<AuthResult>;
|
|
381
|
+
/** Send a password-reset email. */
|
|
382
|
+
requestPasswordReset(input: {
|
|
383
|
+
email: string;
|
|
384
|
+
redirectTo?: string;
|
|
385
|
+
}): Promise<{
|
|
386
|
+
status: boolean;
|
|
387
|
+
}>;
|
|
388
|
+
/** Complete a reset with the emailed token and a new password. */
|
|
389
|
+
resetPassword(input: {
|
|
390
|
+
newPassword: string;
|
|
391
|
+
token: string;
|
|
392
|
+
}): Promise<{
|
|
393
|
+
status: boolean;
|
|
394
|
+
}>;
|
|
395
|
+
/** Mint a fresh short-lived access JWT from the stored session token (app mode). */
|
|
396
|
+
refresh(): Promise<{
|
|
397
|
+
accessToken: string;
|
|
398
|
+
refreshToken: string;
|
|
399
|
+
expiresIn: number;
|
|
400
|
+
tokenType: string;
|
|
401
|
+
}>;
|
|
402
|
+
/** Change the signed-in user's password. */
|
|
403
|
+
changePassword(input: {
|
|
404
|
+
newPassword: string;
|
|
405
|
+
currentPassword: string;
|
|
406
|
+
revokeOtherSessions?: boolean;
|
|
407
|
+
}): Promise<Record<string, unknown>>;
|
|
408
|
+
/** Update the signed-in user's profile (e.g. `{ name, image }`). */
|
|
409
|
+
updateUser(attributes: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
410
|
+
/** Send an email-verification link to the signed-in (or named) user. */
|
|
411
|
+
sendVerificationEmail(input: {
|
|
412
|
+
email: string;
|
|
413
|
+
callbackURL?: string;
|
|
414
|
+
}): Promise<{
|
|
415
|
+
status: boolean;
|
|
416
|
+
}>;
|
|
417
|
+
/** Sign out the current session. */
|
|
418
|
+
signOut(): Promise<{
|
|
419
|
+
success: boolean;
|
|
420
|
+
}>;
|
|
421
|
+
/** Current session, or `{ user: null }`. */
|
|
422
|
+
getSession(): Promise<{
|
|
423
|
+
user: AuthUser | null;
|
|
424
|
+
} & Record<string, unknown>>;
|
|
425
|
+
/** List the signed-in user's active sessions. */
|
|
426
|
+
listSessions(): Promise<AuthSession[]>;
|
|
427
|
+
/** Revoke one session by its token. */
|
|
428
|
+
revokeSession(input: {
|
|
429
|
+
token: string;
|
|
430
|
+
}): Promise<{
|
|
431
|
+
status: boolean;
|
|
432
|
+
}>;
|
|
433
|
+
/** Revoke every session except the current one. */
|
|
434
|
+
revokeOtherSessions(): Promise<{
|
|
435
|
+
status: boolean;
|
|
436
|
+
}>;
|
|
437
|
+
/** Revoke all sessions, including the current one. */
|
|
438
|
+
revokeSessions(): Promise<{
|
|
439
|
+
status: boolean;
|
|
440
|
+
}>;
|
|
441
|
+
/** Public description of this workspace's auth surface (providers + policy). */
|
|
442
|
+
providers(): Promise<AuthSurface>;
|
|
443
|
+
/** The current workspace session token (app mode) — persist across reloads. */
|
|
444
|
+
getToken(): string | null;
|
|
445
|
+
/** Restore a workspace session token (app mode). */
|
|
446
|
+
setToken(token: string | null): void;
|
|
447
|
+
}
|
|
448
|
+
/** File storage + resumable (TUS) uploads. See `createClient`. */
|
|
449
|
+
interface StorageClient {
|
|
450
|
+
/** List stored objects, optionally under a key prefix. */
|
|
451
|
+
list(prefix?: string): Promise<{
|
|
452
|
+
data: {
|
|
416
453
|
key: string;
|
|
417
|
-
|
|
454
|
+
size: number;
|
|
418
455
|
contentType?: string;
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
* function with `payload.name` + `payload.input`) or `webhook.deliver`.
|
|
478
|
-
* Jobs retry with backoff and dead-letter after `maxAttempts`. Pass
|
|
479
|
-
* `runAt` (ISO string) to schedule for later. Admin-scoped. */
|
|
480
|
-
enqueue: (input: {
|
|
481
|
-
type: "function" | "webhook.deliver";
|
|
482
|
-
payload?: Record<string, unknown>;
|
|
483
|
-
queue?: string;
|
|
484
|
-
runAt?: string;
|
|
485
|
-
maxAttempts?: number;
|
|
486
|
-
priority?: number;
|
|
487
|
-
}) => Promise<{
|
|
456
|
+
ownerId: string | null;
|
|
457
|
+
uploadedAt: string;
|
|
458
|
+
}[];
|
|
459
|
+
}>;
|
|
460
|
+
/** Upload an object in one request. */
|
|
461
|
+
put(key: string, body: BodyInit, contentType?: string, folderId?: string): Promise<unknown>;
|
|
462
|
+
/** Download an object; returns the raw `Response`. */
|
|
463
|
+
download(key: string): Promise<Response>;
|
|
464
|
+
/** Delete an object by key. */
|
|
465
|
+
delete(key: string): Promise<{
|
|
466
|
+
ok: boolean;
|
|
467
|
+
}>;
|
|
468
|
+
/** Resumable upload (TUS 1.0.0) that resumes after a transient failure. */
|
|
469
|
+
uploadResumable(input: {
|
|
470
|
+
key: string;
|
|
471
|
+
data: Blob | ArrayBuffer | Uint8Array;
|
|
472
|
+
contentType?: string;
|
|
473
|
+
folderId?: string;
|
|
474
|
+
chunkSize?: number;
|
|
475
|
+
onProgress?: (sent: number, total: number) => void;
|
|
476
|
+
signal?: AbortSignal;
|
|
477
|
+
}): Promise<ResumableUploadResult>;
|
|
478
|
+
/** Resume a previously-started resumable upload at the server's offset. */
|
|
479
|
+
resumeUpload(location: string, data: Blob | ArrayBuffer | Uint8Array, opts2?: {
|
|
480
|
+
chunkSize?: number;
|
|
481
|
+
onProgress?: (sent: number, total: number) => void;
|
|
482
|
+
signal?: AbortSignal;
|
|
483
|
+
}): Promise<void>;
|
|
484
|
+
}
|
|
485
|
+
/** Push + SMS device registration for the current user. See `createClient`. */
|
|
486
|
+
interface MessagingClient {
|
|
487
|
+
/** Register (or refresh) the current user's push device. */
|
|
488
|
+
registerDevice(input: {
|
|
489
|
+
platform: "fcm" | "apns" | "web-push";
|
|
490
|
+
token: string;
|
|
491
|
+
keys?: {
|
|
492
|
+
p256dh: string;
|
|
493
|
+
auth: string;
|
|
494
|
+
};
|
|
495
|
+
deviceName?: string;
|
|
496
|
+
}): Promise<{
|
|
497
|
+
data: {
|
|
498
|
+
id: string;
|
|
499
|
+
};
|
|
500
|
+
}>;
|
|
501
|
+
/** Remove one of the caller's registered devices by id. */
|
|
502
|
+
unregister(id: string): Promise<{
|
|
503
|
+
ok: boolean;
|
|
504
|
+
}>;
|
|
505
|
+
/** List the caller's registered devices. */
|
|
506
|
+
listDevices(): Promise<{
|
|
507
|
+
data: DeviceToken[];
|
|
508
|
+
}>;
|
|
509
|
+
/** Register (or refresh) the caller's E.164 phone number for SMS. */
|
|
510
|
+
registerPhone(input: {
|
|
511
|
+
phoneNumber: string;
|
|
512
|
+
}): Promise<{
|
|
513
|
+
data: {
|
|
488
514
|
id: string;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
515
|
+
};
|
|
516
|
+
}>;
|
|
517
|
+
/** Remove one of the caller's registered phone numbers by id. */
|
|
518
|
+
unregisterPhone(id: string): Promise<{
|
|
519
|
+
ok: boolean;
|
|
520
|
+
}>;
|
|
521
|
+
/** List the caller's registered phone numbers. */
|
|
522
|
+
listPhones(): Promise<{
|
|
523
|
+
data: PhoneNumber[];
|
|
524
|
+
}>;
|
|
525
|
+
}
|
|
526
|
+
/** Durable background job queue (admin-scoped). See `createClient`. */
|
|
527
|
+
interface JobsClient {
|
|
528
|
+
/** Enqueue a durable background job. */
|
|
529
|
+
enqueue(input: {
|
|
530
|
+
type: "function" | "webhook.deliver";
|
|
531
|
+
payload?: Record<string, unknown>;
|
|
532
|
+
queue?: string;
|
|
533
|
+
runAt?: string;
|
|
534
|
+
maxAttempts?: number;
|
|
535
|
+
priority?: number;
|
|
536
|
+
}): Promise<{
|
|
537
|
+
id: string;
|
|
538
|
+
}>;
|
|
539
|
+
/** List jobs (newest first), optionally filtered by queue/status. */
|
|
540
|
+
list(q?: {
|
|
541
|
+
queue?: string;
|
|
542
|
+
status?: JobStatus;
|
|
543
|
+
limit?: number;
|
|
544
|
+
}): Promise<{
|
|
545
|
+
jobs: Job[];
|
|
546
|
+
}>;
|
|
547
|
+
/** Fetch a single job by id. */
|
|
548
|
+
get(id: string): Promise<Job>;
|
|
549
|
+
/** Requeue a failed / dead-lettered / cancelled job. */
|
|
550
|
+
retry(id: string): Promise<{
|
|
551
|
+
ok: boolean;
|
|
552
|
+
}>;
|
|
553
|
+
/** Cancel a pending job. */
|
|
554
|
+
cancel(id: string): Promise<{
|
|
555
|
+
ok: boolean;
|
|
556
|
+
}>;
|
|
557
|
+
/** Delete a job row. */
|
|
558
|
+
remove(id: string): Promise<{
|
|
559
|
+
ok: boolean;
|
|
560
|
+
}>;
|
|
561
|
+
}
|
|
562
|
+
/** Feature flags / remote config evaluated for the current caller. See `createClient`. */
|
|
563
|
+
interface FlagsClient {
|
|
564
|
+
/** Fetch + cache the evaluated flag map. */
|
|
565
|
+
all(): Promise<Record<string, FlagState>>;
|
|
566
|
+
/** Resolved value (remote config payload) for a flag, or `undefined`. */
|
|
567
|
+
get(key: string, opts?: {
|
|
568
|
+
refresh?: boolean;
|
|
569
|
+
}): Promise<unknown>;
|
|
570
|
+
/** Whether a flag is on for the caller. */
|
|
571
|
+
isEnabled(key: string, opts?: {
|
|
572
|
+
refresh?: boolean;
|
|
573
|
+
}): Promise<boolean>;
|
|
574
|
+
}
|
|
575
|
+
/** A visual workflow (flow) row. `operations` is the serialized op DSL the
|
|
576
|
+
* builder compiles; `layout` is a purely-presentational graph snapshot. */
|
|
577
|
+
interface Flow {
|
|
578
|
+
id: string;
|
|
579
|
+
tenantId?: string | null;
|
|
580
|
+
name: string;
|
|
581
|
+
trigger: string;
|
|
582
|
+
operations: unknown[];
|
|
583
|
+
layout?: unknown;
|
|
584
|
+
active: boolean;
|
|
585
|
+
}
|
|
586
|
+
/** Create/update payload for a flow. `operations` must be non-empty on create;
|
|
587
|
+
* `update` accepts any subset. */
|
|
588
|
+
interface FlowInput {
|
|
589
|
+
name: string;
|
|
590
|
+
trigger: string;
|
|
591
|
+
operations: unknown[];
|
|
592
|
+
layout?: unknown;
|
|
593
|
+
active?: boolean;
|
|
594
|
+
}
|
|
595
|
+
/** Outcome of a manual flow run. `ok: false` means the run halted on an
|
|
596
|
+
* unhandled op error; `error` carries the first failure message. */
|
|
597
|
+
interface FlowRunResult {
|
|
598
|
+
ok: boolean;
|
|
599
|
+
error?: string;
|
|
600
|
+
}
|
|
601
|
+
/** Visual workflows (admin-scoped). Mirrors `/api/flows`. See `createClient`. */
|
|
602
|
+
interface FlowsClient {
|
|
603
|
+
/** List every flow in the active workspace. */
|
|
604
|
+
list(): Promise<{
|
|
605
|
+
data: Flow[];
|
|
606
|
+
}>;
|
|
607
|
+
/** Fetch a single flow's full definition by id. */
|
|
608
|
+
get(id: string): Promise<{
|
|
609
|
+
data: Flow;
|
|
610
|
+
}>;
|
|
611
|
+
/** Create a flow scoped to the active workspace. */
|
|
612
|
+
create(input: FlowInput): Promise<{
|
|
613
|
+
data: Flow;
|
|
614
|
+
}>;
|
|
615
|
+
/** Partial update of a flow by id. */
|
|
616
|
+
update(id: string, patch: Partial<FlowInput>): Promise<{
|
|
617
|
+
ok: boolean;
|
|
618
|
+
}>;
|
|
619
|
+
/** Delete a flow by id. */
|
|
620
|
+
delete(id: string): Promise<{
|
|
621
|
+
ok: boolean;
|
|
622
|
+
}>;
|
|
623
|
+
/** Synchronously run a flow with an arbitrary `input` trigger payload. */
|
|
624
|
+
run(id: string, input?: Record<string, unknown>): Promise<FlowRunResult>;
|
|
625
|
+
}
|
|
626
|
+
/** The backlex client returned by `createClient` — data, auth, storage, realtime, and more. */
|
|
627
|
+
interface BacklexClient {
|
|
628
|
+
/** Typed data API for one collection by slug. */
|
|
629
|
+
from<T extends Record<string, unknown>>(slug: string): CollectionClient<T>;
|
|
630
|
+
/** Subscribe to a realtime channel (SSE); returns an unsubscribe function. */
|
|
631
|
+
subscribe<T = Record<string, unknown>>(channel: string, onEvent: (e: ItemEvent<T>) => void, onError?: (err: unknown) => void): () => void;
|
|
632
|
+
/** Auth surface (sign-in/up, sessions, tokens). */
|
|
633
|
+
auth: AuthClient;
|
|
634
|
+
/** File storage + resumable uploads. */
|
|
635
|
+
storage: StorageClient;
|
|
636
|
+
/** Push + SMS device registration. */
|
|
637
|
+
messaging: MessagingClient;
|
|
638
|
+
/** Durable background job queue. */
|
|
639
|
+
jobs: JobsClient;
|
|
640
|
+
/** Visual workflows (flows). */
|
|
641
|
+
flows: FlowsClient;
|
|
642
|
+
/** Feature flags / remote config. */
|
|
643
|
+
flags: FlagsClient;
|
|
644
|
+
/** Offline-first sync controller for one collection. */
|
|
645
|
+
sync(options: SyncOptions): SyncController;
|
|
540
646
|
/** Raw escape hatch — issues a request with auth headers applied. */
|
|
541
|
-
request
|
|
542
|
-
}
|
|
647
|
+
request<T>(method: string, path: string, body?: unknown, extraHeaders?: Record<string, string>): Promise<T>;
|
|
648
|
+
}
|
|
649
|
+
/** Create a backlex client. In app mode (`workspace` set) auth + data scope to
|
|
650
|
+
* one workspace and the session token is captured + replayed as a bearer. */
|
|
651
|
+
declare const createClient: (opts: ClientOptions) => BacklexClient;
|
|
543
652
|
/** A registry mapping each collection slug to its row type — the shape
|
|
544
653
|
* `backlex gen-types --sdk` emits as `Collections`. */
|
|
545
654
|
type CollectionsMap = Record<string, Record<string, unknown>>;
|
|
@@ -564,6 +673,4 @@ type TypedClient<R extends CollectionsMap> = BacklexClient & {
|
|
|
564
673
|
*/
|
|
565
674
|
declare const typedCollections: <R extends CollectionsMap>(client: BacklexClient) => TypedClient<R>;
|
|
566
675
|
|
|
567
|
-
type BacklexClient
|
|
568
|
-
|
|
569
|
-
export { AggregateQuery, AggregateRow, type BacklexClient, BatchOperation, BatchResponse, type ClientOptions, type CollectionClient, type CollectionsMap, DeviceToken, type FieldKey, type FilterBuilder, FlagState, ImportSummary, ItemEvent, ItemQuery, ItemResponse, Job, JobStatus, ListQuery, ListResponse, PhoneNumber, QueryBuilder, type QueuedOp, ResumableUploadResult, SearchQuery, SearchResponse, type SortKey, type SyncOptions, type SyncStore, type TypedClient, type TypedCollections, createClient, createSync, indexedDbStore, memoryStore, typedCollections };
|
|
676
|
+
export { AggregateQuery, AggregateRow, type AuthClient, type AuthResult, type AuthSession, type AuthSurface, type AuthUser, type BacklexClient, BatchOperation, BatchResponse, BulkUpdateResponse, type ClientOptions, type CollectionClient, type CollectionsMap, DeviceToken, type FieldKey, type FilterBuilder, FlagState, type FlagsClient, type Flow, type FlowInput, type FlowRunResult, type FlowsClient, ImportSummary, ItemEvent, ItemQuery, ItemResponse, Job, JobStatus, type JobsClient, ListQuery, ListResponse, type MessagingClient, PhoneNumber, type PublicProvider, QueryBuilder, type QueuedOp, ResumableUploadResult, SearchQuery, SearchResponse, type SortKey, type StorageClient, type SyncOptions, type SyncStore, type TypedClient, type TypedCollections, createClient, createSync, indexedDbStore, memoryStore, typedCollections };
|
package/dist/index.js
CHANGED
|
@@ -111,6 +111,7 @@ var QueryBuilder = class {
|
|
|
111
111
|
_meta;
|
|
112
112
|
_locale;
|
|
113
113
|
_q;
|
|
114
|
+
/** Build the filter fluently with a {@link FilterBuilder}. */
|
|
114
115
|
where(build) {
|
|
115
116
|
this._filter = normalizeCondition(build(makeFilterBuilder()));
|
|
116
117
|
return this;
|
|
@@ -120,10 +121,12 @@ var QueryBuilder = class {
|
|
|
120
121
|
this._filter = normalizeCondition(cond);
|
|
121
122
|
return this;
|
|
122
123
|
}
|
|
124
|
+
/** Project only these fields (column allow-list). */
|
|
123
125
|
select(...fields) {
|
|
124
126
|
this._fields.push(...fields);
|
|
125
127
|
return this;
|
|
126
128
|
}
|
|
129
|
+
/** Sort by one or more keys (`"-field"` for descending). */
|
|
127
130
|
orderBy(...sorts) {
|
|
128
131
|
this._sort.push(...sorts);
|
|
129
132
|
return this;
|
|
@@ -143,14 +146,17 @@ var QueryBuilder = class {
|
|
|
143
146
|
this._q = text;
|
|
144
147
|
return this;
|
|
145
148
|
}
|
|
149
|
+
/** Max rows to return. */
|
|
146
150
|
limit(n) {
|
|
147
151
|
this._limit = n;
|
|
148
152
|
return this;
|
|
149
153
|
}
|
|
154
|
+
/** Number of rows to skip (paging). */
|
|
150
155
|
offset(n) {
|
|
151
156
|
this._offset = n;
|
|
152
157
|
return this;
|
|
153
158
|
}
|
|
159
|
+
/** Ask the server for a count alongside the page (`filter_count` / `total_count`). */
|
|
154
160
|
withMeta(m) {
|
|
155
161
|
this._meta = m;
|
|
156
162
|
return this;
|
|
@@ -169,6 +175,7 @@ var QueryBuilder = class {
|
|
|
169
175
|
if (this._q) q.q = this._q;
|
|
170
176
|
return q;
|
|
171
177
|
}
|
|
178
|
+
/** Run the query and return the page of rows. */
|
|
172
179
|
list() {
|
|
173
180
|
return this.listFn(this.toQuery());
|
|
174
181
|
}
|
|
@@ -449,7 +456,11 @@ var createClient = (opts) => {
|
|
|
449
456
|
const tenantHeader = () => opts.tenant ? { "x-backlex-tenant": opts.tenant } : {};
|
|
450
457
|
const request = async (method, path, body, extraHeaders) => {
|
|
451
458
|
const headers = {
|
|
452
|
-
|
|
459
|
+
// Only advertise a JSON body when we actually send one. A bodyless POST
|
|
460
|
+
// (publish, unpublish, restore, fts-reindex, …) that still carried
|
|
461
|
+
// `content-type: application/json` made the server's body validator try to
|
|
462
|
+
// parse an empty body and fail with "Malformed JSON in request body".
|
|
463
|
+
...body !== void 0 ? { "content-type": "application/json" } : {},
|
|
453
464
|
...authHeader(),
|
|
454
465
|
...tenantHeader(),
|
|
455
466
|
...extraHeaders ?? {}
|
|
@@ -532,6 +543,14 @@ var createClient = (opts) => {
|
|
|
532
543
|
operations: ids.map((id) => ({ op: "delete", id })),
|
|
533
544
|
atomic: opts2?.atomic
|
|
534
545
|
}),
|
|
546
|
+
/** Set the SAME fields on many ids at once (one shared patch). Only the
|
|
547
|
+
* named fields change per row; partial-success (a key the caller can't
|
|
548
|
+
* write is reported `NOT_FOUND`). Differs from `updateMany`, which sends
|
|
549
|
+
* a distinct patch per id. */
|
|
550
|
+
bulkUpdate: (keys, data) => request("POST", `/api/items/${slug}/bulk-update`, {
|
|
551
|
+
keys,
|
|
552
|
+
data
|
|
553
|
+
}),
|
|
535
554
|
/** Mixed create/update/delete in one request. `atomic` = all-or-nothing. */
|
|
536
555
|
batch: (operations, opts2) => request("POST", `/api/items/${slug}/batch`, {
|
|
537
556
|
operations,
|
|
@@ -811,6 +830,20 @@ var createClient = (opts) => {
|
|
|
811
830
|
/** Delete a job row. */
|
|
812
831
|
remove: (id) => request("DELETE", `/api/jobs/${encodeURIComponent(id)}`)
|
|
813
832
|
};
|
|
833
|
+
const flows = {
|
|
834
|
+
/** List every flow in the active workspace. */
|
|
835
|
+
list: () => request("GET", "/api/flows"),
|
|
836
|
+
/** Fetch a single flow's full definition by id. */
|
|
837
|
+
get: (id) => request("GET", `/api/flows/${encodeURIComponent(id)}`),
|
|
838
|
+
/** Create a flow scoped to the active workspace. */
|
|
839
|
+
create: (input) => request("POST", "/api/flows", input),
|
|
840
|
+
/** Partial update of a flow by id. */
|
|
841
|
+
update: (id, patch) => request("PATCH", `/api/flows/${encodeURIComponent(id)}`, patch),
|
|
842
|
+
/** Delete a flow by id. */
|
|
843
|
+
delete: (id) => request("DELETE", `/api/flows/${encodeURIComponent(id)}`),
|
|
844
|
+
/** Run a flow synchronously with an arbitrary `input` trigger payload. */
|
|
845
|
+
run: (id, input) => request("POST", `/api/flows/${encodeURIComponent(id)}/run`, input ?? {})
|
|
846
|
+
};
|
|
814
847
|
let flagsCache = null;
|
|
815
848
|
const fetchFlags = async () => {
|
|
816
849
|
const res = await request("GET", "/api/flags");
|
|
@@ -841,6 +874,7 @@ var createClient = (opts) => {
|
|
|
841
874
|
storage,
|
|
842
875
|
messaging,
|
|
843
876
|
jobs,
|
|
877
|
+
flows,
|
|
844
878
|
flags,
|
|
845
879
|
sync,
|
|
846
880
|
/** Raw escape hatch — issues a request with auth headers applied. */
|
|
@@ -52,6 +52,15 @@ type Condition = {
|
|
|
52
52
|
[field: string]: ComparisonObj;
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* @module
|
|
57
|
+
*
|
|
58
|
+
* Wire types for the backlex client — the request/response shapes for list,
|
|
59
|
+
* item, query, search, aggregate, batch, device tokens, jobs, uploads, and
|
|
60
|
+
* feature flags, plus the {@link BacklexError} thrown on a failed request.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
/** Response from `from(slug).list(...)` — a page of rows + paging metadata. */
|
|
55
64
|
interface ListResponse<T> {
|
|
56
65
|
data: T[];
|
|
57
66
|
limit: number;
|
|
@@ -61,9 +70,11 @@ interface ListResponse<T> {
|
|
|
61
70
|
total_count?: number;
|
|
62
71
|
};
|
|
63
72
|
}
|
|
73
|
+
/** Response wrapping a single row (`one` / `create` / `update`). */
|
|
64
74
|
interface ItemResponse<T> {
|
|
65
75
|
data: T;
|
|
66
76
|
}
|
|
77
|
+
/** Query params for `from(slug).list(...)` — filter, sort, projection, paging. */
|
|
67
78
|
interface ListQuery {
|
|
68
79
|
filter?: Condition;
|
|
69
80
|
sort?: string | string[];
|
|
@@ -137,10 +148,12 @@ interface ImportSummary {
|
|
|
137
148
|
error: string;
|
|
138
149
|
}[];
|
|
139
150
|
}
|
|
151
|
+
/** A realtime event delivered to `subscribe(...)`. */
|
|
140
152
|
interface ItemEvent<T = Record<string, unknown>> {
|
|
141
153
|
event: "created" | "updated" | "deleted";
|
|
142
154
|
data: T;
|
|
143
155
|
}
|
|
156
|
+
/** One operation in a `batch(...)` request. */
|
|
144
157
|
type BatchOperation<T = Record<string, unknown>> = {
|
|
145
158
|
op: "create";
|
|
146
159
|
data: Partial<T>;
|
|
@@ -152,6 +165,7 @@ type BatchOperation<T = Record<string, unknown>> = {
|
|
|
152
165
|
op: "delete";
|
|
153
166
|
id: string;
|
|
154
167
|
};
|
|
168
|
+
/** Per-row outcome inside a {@link BatchResponse}. */
|
|
155
169
|
interface BatchRowResult<T = Record<string, unknown>> {
|
|
156
170
|
index: number;
|
|
157
171
|
op: "create" | "update" | "delete";
|
|
@@ -163,6 +177,7 @@ interface BatchRowResult<T = Record<string, unknown>> {
|
|
|
163
177
|
message: string;
|
|
164
178
|
};
|
|
165
179
|
}
|
|
180
|
+
/** Response from a bulk `createMany`/`updateMany`/`deleteMany`/`batch` call. */
|
|
166
181
|
interface BatchResponse<T = Record<string, unknown>> {
|
|
167
182
|
data: {
|
|
168
183
|
atomic: boolean;
|
|
@@ -172,6 +187,26 @@ interface BatchResponse<T = Record<string, unknown>> {
|
|
|
172
187
|
results: BatchRowResult<T>[];
|
|
173
188
|
};
|
|
174
189
|
}
|
|
190
|
+
/** Per-key outcome inside a {@link BulkUpdateResponse}. */
|
|
191
|
+
interface BulkUpdateRowResult {
|
|
192
|
+
id: string;
|
|
193
|
+
ok: boolean;
|
|
194
|
+
error?: {
|
|
195
|
+
code: string;
|
|
196
|
+
message: string;
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/** Response from a `bulkUpdate(keys, data)` call — one shared patch over many
|
|
200
|
+
* ids, partial-success (a key the caller can't write is `NOT_FOUND`). */
|
|
201
|
+
interface BulkUpdateResponse {
|
|
202
|
+
data: {
|
|
203
|
+
total: number;
|
|
204
|
+
updated: number;
|
|
205
|
+
failed: number;
|
|
206
|
+
results: BulkUpdateRowResult[];
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/** A registered push device (from `messaging.listDevices()`). */
|
|
175
210
|
interface DeviceToken {
|
|
176
211
|
id: string;
|
|
177
212
|
platform: "fcm" | "apns" | "web-push";
|
|
@@ -181,6 +216,7 @@ interface DeviceToken {
|
|
|
181
216
|
createdAt: string | number;
|
|
182
217
|
lastSeenAt: string | number | null;
|
|
183
218
|
}
|
|
219
|
+
/** A registered SMS phone number (from `messaging.listPhones()`). */
|
|
184
220
|
interface PhoneNumber {
|
|
185
221
|
id: string;
|
|
186
222
|
phoneNumber: string;
|
|
@@ -188,7 +224,9 @@ interface PhoneNumber {
|
|
|
188
224
|
createdAt: string | number;
|
|
189
225
|
lastSeenAt: string | number | null;
|
|
190
226
|
}
|
|
227
|
+
/** Lifecycle state of a durable {@link Job}. */
|
|
191
228
|
type JobStatus = "pending" | "active" | "succeeded" | "failed" | "dead_letter" | "cancelled";
|
|
229
|
+
/** A durable background job row (from `jobs.get`/`jobs.list`). */
|
|
192
230
|
interface Job {
|
|
193
231
|
id: string;
|
|
194
232
|
tenantId: string | null;
|
|
@@ -205,6 +243,7 @@ interface Job {
|
|
|
205
243
|
createdAt: string | number;
|
|
206
244
|
completedAt: string | number | null;
|
|
207
245
|
}
|
|
246
|
+
/** Status of a resumable {@link Upload} session. */
|
|
208
247
|
type UploadStatus = "pending" | "completed" | "aborted";
|
|
209
248
|
/** A resumable (TUS) upload session, as returned by the management API. */
|
|
210
249
|
interface Upload {
|
|
@@ -230,11 +269,14 @@ interface FlagState {
|
|
|
230
269
|
enabled: boolean;
|
|
231
270
|
value: unknown;
|
|
232
271
|
}
|
|
272
|
+
/** The error envelope returned by the API on a failed request. */
|
|
233
273
|
interface ApiError {
|
|
234
274
|
code: string;
|
|
235
275
|
message: string;
|
|
236
276
|
details?: unknown;
|
|
237
277
|
}
|
|
278
|
+
/** Thrown by every client method on a non-2xx response — carries the HTTP
|
|
279
|
+
* `status`, the API error `code`, the `message`, and any `details`. */
|
|
238
280
|
declare class BacklexError extends Error {
|
|
239
281
|
readonly code: string;
|
|
240
282
|
readonly status: number;
|
|
@@ -244,4 +286,4 @@ declare class BacklexError extends Error {
|
|
|
244
286
|
} | undefined);
|
|
245
287
|
}
|
|
246
288
|
|
|
247
|
-
export { type AggregateQuery as A, BacklexError as B, type Condition as C, type DeviceToken as D, type FlagState as F, type ImportSummary as I, type Job as J, type ListQuery as L, type PhoneNumber as P, type RelativeNow as R, type SearchQuery as S, type Upload as U, type AggregateRow as a, type ApiError as b, type BatchOperation as c, type BatchResponse as d, type BatchRowResult as e, type
|
|
289
|
+
export { type AggregateQuery as A, BacklexError as B, type Condition as C, type DeviceToken as D, type FlagState as F, type ImportSummary as I, type Job as J, type ListQuery as L, type PhoneNumber as P, type RelativeNow as R, type SearchQuery as S, type Upload as U, type AggregateRow as a, type ApiError as b, type BatchOperation as c, type BatchResponse as d, type BatchRowResult as e, type BulkUpdateResponse as f, type BulkUpdateRowResult as g, type DurationParts as h, type ItemEvent as i, type ItemQuery as j, type ItemResponse as k, type JobStatus as l, type ListResponse as m, type ResumableUploadResult as n, type SearchResponse as o, type UploadStatus as p };
|
package/dist/types.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { A as AggregateQuery, a as AggregateRow, b as ApiError, B as BacklexError, c as BatchOperation, d as BatchResponse, e as BatchRowResult, D as DeviceToken, F as FlagState, I as ImportSummary,
|
|
1
|
+
export { A as AggregateQuery, a as AggregateRow, b as ApiError, B as BacklexError, c as BatchOperation, d as BatchResponse, e as BatchRowResult, f as BulkUpdateResponse, g as BulkUpdateRowResult, D as DeviceToken, F as FlagState, I as ImportSummary, i as ItemEvent, j as ItemQuery, k as ItemResponse, J as Job, l as JobStatus, L as ListQuery, m as ListResponse, P as PhoneNumber, n as ResumableUploadResult, S as SearchQuery, o as SearchResponse, U as Upload, p as UploadStatus } from './types-DiJCXOy-.js';
|
package/dist/webhook.d.ts
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
*
|
|
4
|
+
* Receiver-side helper for verifying inbound backlex webhook signatures with
|
|
5
|
+
* {@link verifyWebhook}. Runs anywhere Web Crypto is available (Workers,
|
|
6
|
+
* Node 18+, Bun, Deno). Supports the V2 (timestamped, replay-protected) and
|
|
7
|
+
* legacy signature schemes.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { verifyWebhook } from "backlex/webhook";
|
|
11
|
+
* const ok = await verifyWebhook({ secret, body, signature, timestamp });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
1
14
|
interface VerifyWebhookOptions {
|
|
2
15
|
/** The hook's signing secret (the `secret` you configured on the webhook). */
|
|
3
16
|
secret: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backlex",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Typed TypeScript client for the backlex API — auth, collection CRUD, query builder, realtime, storage, jobs, flags, and offline sync. Zero dependencies. On npm as compiled ESM + types; on JSR (@backlex/backlex) as TypeScript source.",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Typed TypeScript client for the backlex API — auth, collection CRUD, query builder, realtime, storage, jobs, flows, flags, and offline sync. Zero dependencies. On npm as compiled ESM + types; on JSR (@backlex/backlex) as TypeScript source.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"homepage": "https://backlex.com",
|