lazypock 0.1.2 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -42
- package/dist/{chunk-3MZJ5HCD.js → chunk-Y6PMBL2S.js} +7 -5
- package/dist/{chunk-3MZJ5HCD.js.map → chunk-Y6PMBL2S.js.map} +1 -1
- package/dist/cli.cjs +9 -7
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.global.js +9 -7
- package/dist/cli.global.js.map +1 -1
- package/dist/cli.js +4 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +189 -133
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +98 -78
- package/dist/index.d.ts +98 -78
- package/dist/index.global.js +188 -133
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +183 -130
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +7 -4
- package/src/codegen.ts +6 -4
- package/src/collection.ts +64 -10
- package/src/collections.ts +200 -0
- package/src/index.ts +2 -0
- package/src/lazypock.ts +5 -170
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// ── Collections Service ─────────────────────────────────
|
|
2
|
+
// Registry-level service for collections (CRUD + realtime registry events),
|
|
3
|
+
// mirroring PocketBase's `pb.collections` service.
|
|
4
|
+
//
|
|
5
|
+
// Get an instance via {@link LazypockClient.collections}.
|
|
6
|
+
//
|
|
7
|
+
// This is distinct from `CollectionService` (single collection record
|
|
8
|
+
// events) — it operates on the *collections* themselves: listing / creating /
|
|
9
|
+
// updating / deleting collections via `/api/collections`, and subscribing to
|
|
10
|
+
// the `collections` registry channel (AdminChannel), which fires when
|
|
11
|
+
// collections themselves are created / updated / deleted.
|
|
12
|
+
|
|
13
|
+
import type { HttpClient } from "./http";
|
|
14
|
+
import type { RealtimeService } from "./realtime";
|
|
15
|
+
import type { ListResult, RequestOptions, ApiRecord } from "./types";
|
|
16
|
+
|
|
17
|
+
const REGISTRY_TOPIC = "collections";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Registry-level realtime events for the collections admin channel.
|
|
21
|
+
* The backend (AdminChannel) broadcasts the action as the *event name*,
|
|
22
|
+
* with the collection JSON as the payload.
|
|
23
|
+
*/
|
|
24
|
+
export interface CollectionsMessage {
|
|
25
|
+
action: "create" | "update" | "delete";
|
|
26
|
+
/** The collection payload (id + metadata), or {} when unavailable. */
|
|
27
|
+
collection: Record<string, unknown>;
|
|
28
|
+
topic?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Registry service for collections (list/create/update/delete + realtime
|
|
33
|
+
* registry events). Mirrors PocketBase's `pb.collections`.
|
|
34
|
+
*/
|
|
35
|
+
export class CollectionsService {
|
|
36
|
+
private http?: HttpClient;
|
|
37
|
+
private realtime?: RealtimeService;
|
|
38
|
+
|
|
39
|
+
/** @internal */
|
|
40
|
+
constructor(http?: HttpClient, realtime?: RealtimeService) {
|
|
41
|
+
this.http = http;
|
|
42
|
+
this.realtime = realtime;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Fetch a paginated list of collections (admin).
|
|
47
|
+
*
|
|
48
|
+
* @param params Optional query params (`page`, `perPage`, `filter`, `sort`).
|
|
49
|
+
* @param options Optional request options.
|
|
50
|
+
*/
|
|
51
|
+
async getList<T = ApiRecord>(
|
|
52
|
+
params?: Record<string, unknown>,
|
|
53
|
+
options?: RequestOptions,
|
|
54
|
+
): Promise<ListResult<T> | null> {
|
|
55
|
+
const qs = params
|
|
56
|
+
? "?" +
|
|
57
|
+
new URLSearchParams(
|
|
58
|
+
Object.fromEntries(
|
|
59
|
+
Object.entries(params).map(([k, v]) => [k, String(v)]),
|
|
60
|
+
),
|
|
61
|
+
).toString()
|
|
62
|
+
: "";
|
|
63
|
+
return this.http?.get<ListResult<T>>("/collections" + qs, options) ?? null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Fetch all collections at once (auto-paginates). Mirrors PocketBase's
|
|
68
|
+
* `pb.collections.getFullList()` — defaults to listing everything.
|
|
69
|
+
*
|
|
70
|
+
* @param options Query params (`sort`, `batch`, etc.) or request options.
|
|
71
|
+
*/
|
|
72
|
+
async getFullList<T = ApiRecord>(
|
|
73
|
+
options?: Record<string, unknown> & RequestOptions,
|
|
74
|
+
): Promise<Array<T>> {
|
|
75
|
+
if (!this.http) return [];
|
|
76
|
+
const { batch = 1000, ...rest } = options ?? {};
|
|
77
|
+
const items: T[] = [];
|
|
78
|
+
let page = 1;
|
|
79
|
+
// Auto-paginate until empty (bounded by perPage and totalPages).
|
|
80
|
+
for (;;) {
|
|
81
|
+
const res = await this.getList<T>({
|
|
82
|
+
...rest,
|
|
83
|
+
page,
|
|
84
|
+
perPage: batch,
|
|
85
|
+
} as Record<string, unknown>);
|
|
86
|
+
if (!res || !res.items || res.items.length === 0) break;
|
|
87
|
+
items.push(...(res.items as T[]));
|
|
88
|
+
if (page >= (res.totalPages ?? page)) break;
|
|
89
|
+
page += 1;
|
|
90
|
+
}
|
|
91
|
+
return items;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get a single collection by ID or name (admin).
|
|
96
|
+
*
|
|
97
|
+
* @param id Collection ID or name.
|
|
98
|
+
* @param options Optional request options.
|
|
99
|
+
*/
|
|
100
|
+
async getOne<T = ApiRecord>(
|
|
101
|
+
id: string,
|
|
102
|
+
options?: RequestOptions,
|
|
103
|
+
): Promise<T | null> {
|
|
104
|
+
return (
|
|
105
|
+
this.http?.get<T>("/collections/" + encodeURIComponent(id), options) ??
|
|
106
|
+
null
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Create a new collection (admin).
|
|
112
|
+
*
|
|
113
|
+
* @param data Collection definition (name, type, fields, options, rules, etc.).
|
|
114
|
+
* @param options Optional request options.
|
|
115
|
+
*/
|
|
116
|
+
async create<T = ApiRecord>(
|
|
117
|
+
data: Record<string, unknown>,
|
|
118
|
+
options?: RequestOptions,
|
|
119
|
+
): Promise<T | null> {
|
|
120
|
+
return this.http?.post<T>("/collections", data, options) ?? null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Update an existing collection (admin).
|
|
125
|
+
*
|
|
126
|
+
* @param id Collection ID or name.
|
|
127
|
+
* @param data Updated collection fields.
|
|
128
|
+
* @param options Optional request options.
|
|
129
|
+
*/
|
|
130
|
+
async update<T = ApiRecord>(
|
|
131
|
+
id: string,
|
|
132
|
+
data: Record<string, unknown>,
|
|
133
|
+
options?: RequestOptions,
|
|
134
|
+
): Promise<T | null> {
|
|
135
|
+
return (
|
|
136
|
+
this.http?.patch<T>(
|
|
137
|
+
"/collections/" + encodeURIComponent(id),
|
|
138
|
+
data,
|
|
139
|
+
options,
|
|
140
|
+
) ?? null
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Delete a collection (admin).
|
|
146
|
+
*
|
|
147
|
+
* @param id Collection ID or name.
|
|
148
|
+
* @param options Optional request options.
|
|
149
|
+
*/
|
|
150
|
+
async delete(id: string, options?: RequestOptions): Promise<boolean> {
|
|
151
|
+
const res = this.http?.delete(
|
|
152
|
+
"/collections/" + encodeURIComponent(id),
|
|
153
|
+
options,
|
|
154
|
+
);
|
|
155
|
+
return res == null ? false : true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Subscribe to collection registry changes.
|
|
160
|
+
*
|
|
161
|
+
* @param callback Received on every collection create/update/delete.
|
|
162
|
+
* @returns A function that unsubscribes this callback.
|
|
163
|
+
*/
|
|
164
|
+
subscribe(callback: (e: CollectionsMessage) => void): () => void {
|
|
165
|
+
if (!this.realtime) {
|
|
166
|
+
console.warn("[lazypock] No realtime service configured.");
|
|
167
|
+
return () => {};
|
|
168
|
+
}
|
|
169
|
+
const handler = (raw: {
|
|
170
|
+
event: string;
|
|
171
|
+
topic: string;
|
|
172
|
+
payload?: Record<string, unknown>;
|
|
173
|
+
}) => {
|
|
174
|
+
callback({
|
|
175
|
+
action: normalizeAction(raw.event),
|
|
176
|
+
collection: (raw.payload ?? {}) as Record<string, unknown>,
|
|
177
|
+
topic: raw.topic,
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
this.realtime.ensureConnected();
|
|
181
|
+
this.realtime.subscribe(REGISTRY_TOPIC, handler as never);
|
|
182
|
+
return () => this.realtime?.unsubscribe(REGISTRY_TOPIC, handler as never);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Unsubscribe all callbacks from the registry channel.
|
|
187
|
+
*/
|
|
188
|
+
unsubscribe(): void {
|
|
189
|
+
this.realtime?.unsubscribe(REGISTRY_TOPIC);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Map a registry event name (create/update/delete) to an action. */
|
|
194
|
+
function normalizeAction(event: string): CollectionsMessage["action"] {
|
|
195
|
+
const e = event.toLowerCase();
|
|
196
|
+
if (e === "create") return "create";
|
|
197
|
+
if (e === "update") return "update";
|
|
198
|
+
if (e === "delete") return "delete";
|
|
199
|
+
return "update";
|
|
200
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ export {
|
|
|
14
14
|
getFileUrl,
|
|
15
15
|
LazypockClient,
|
|
16
16
|
CollectionService,
|
|
17
|
+
CollectionsService,
|
|
17
18
|
generateTypes,
|
|
18
19
|
collectionTypeName,
|
|
19
20
|
fieldTypeScriptType,
|
|
@@ -42,3 +43,4 @@ export type {
|
|
|
42
43
|
} from "./lazypock";
|
|
43
44
|
export type { LazypockCollections } from "./client";
|
|
44
45
|
export type { RealtimeMessage, RealtimeCallback } from "./collection";
|
|
46
|
+
export type { CollectionsMessage } from "./collections";
|
package/src/lazypock.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
} from "./types";
|
|
23
23
|
import { RealtimeService, wsUrlFromBaseUrl } from "./realtime";
|
|
24
24
|
import { FilesService, getFileUrl, type FileRecord } from "./files";
|
|
25
|
+
import { CollectionsService } from "./collections";
|
|
25
26
|
import type { CollectionSchema, SchemaField } from "./schema";
|
|
26
27
|
import { generateTypes, collectionTypeName } from "./codegen";
|
|
27
28
|
import { fieldTypeScriptType, fieldTypeKind, schemaFieldType } from "./typegen";
|
|
@@ -34,6 +35,7 @@ export {
|
|
|
34
35
|
FilesService,
|
|
35
36
|
getFileUrl,
|
|
36
37
|
CollectionService,
|
|
38
|
+
CollectionsService,
|
|
37
39
|
generateTypes,
|
|
38
40
|
collectionTypeName,
|
|
39
41
|
fieldTypeScriptType,
|
|
@@ -88,13 +90,14 @@ export interface LazypockClientOptions {
|
|
|
88
90
|
* ```ts
|
|
89
91
|
* const client = new LazypockClient({ baseUrl: 'http://localhost:4000/api' });
|
|
90
92
|
* await client.login('admin@example.com', 'password');
|
|
91
|
-
* const posts = await client.collection('posts').
|
|
93
|
+
* const posts = await client.collection('posts').getList();
|
|
92
94
|
* ```
|
|
93
95
|
*/
|
|
94
96
|
export class LazypockClient {
|
|
95
97
|
readonly http: HttpClient;
|
|
96
98
|
readonly authStore: AuthStore;
|
|
97
99
|
readonly realtime: RealtimeService;
|
|
100
|
+
readonly collections: CollectionsService;
|
|
98
101
|
readonly files: FilesService;
|
|
99
102
|
private collectionCache = new Map<string, CollectionService>();
|
|
100
103
|
private schemaByName?: Map<string, CollectionSchema>;
|
|
@@ -114,6 +117,7 @@ export class LazypockClient {
|
|
|
114
117
|
this.realtime.setUrl(wsUrlFromBaseUrl(baseUrl));
|
|
115
118
|
}
|
|
116
119
|
this.files = new FilesService(this.http);
|
|
120
|
+
this.collections = new CollectionsService(this.http, this.realtime);
|
|
117
121
|
if (options.types?.schemas) {
|
|
118
122
|
this.schemaByName = new Map(
|
|
119
123
|
options.types.schemas.map((s) => [s.name, s]),
|
|
@@ -318,173 +322,4 @@ export class LazypockClient {
|
|
|
318
322
|
health(options?: RequestOptions): Promise<Record<string, unknown> | null> {
|
|
319
323
|
return this.http.get<Record<string, unknown>>("/health", options);
|
|
320
324
|
}
|
|
321
|
-
|
|
322
|
-
// ── Collection Management (admin) ──
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* List all collections (admin).
|
|
326
|
-
* @param q URL query string (e.g. `page=1&perPage=200`).
|
|
327
|
-
* @param options Optional request options.
|
|
328
|
-
*/
|
|
329
|
-
listCollections(
|
|
330
|
-
q?: string,
|
|
331
|
-
options?: RequestOptions,
|
|
332
|
-
): Promise<ListResult<ApiRecord> | null> {
|
|
333
|
-
return this.http.get<ListResult<ApiRecord>>(
|
|
334
|
-
"/collections" + (q ? "?" + q : ""),
|
|
335
|
-
options,
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Get a single collection by ID or name.
|
|
341
|
-
* @param id Collection ID or name.
|
|
342
|
-
* @param options Optional request options.
|
|
343
|
-
*/
|
|
344
|
-
getCollection(
|
|
345
|
-
id: string,
|
|
346
|
-
options?: RequestOptions,
|
|
347
|
-
): Promise<ApiRecord | null> {
|
|
348
|
-
return this.http.get<ApiRecord>(
|
|
349
|
-
"/collections/" + encodeURIComponent(id),
|
|
350
|
-
options,
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Create a new collection (admin).
|
|
356
|
-
* @param data Collection definition (name, type, fields, options, rules, etc.).
|
|
357
|
-
* @param options Optional request options.
|
|
358
|
-
*/
|
|
359
|
-
createCollection(
|
|
360
|
-
data: Record<string, unknown>,
|
|
361
|
-
options?: RequestOptions,
|
|
362
|
-
): Promise<ApiRecord | null> {
|
|
363
|
-
return this.http.post<ApiRecord>("/collections", data, options);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Update an existing collection (admin).
|
|
368
|
-
* @param id Collection ID or name.
|
|
369
|
-
* @param data Updated collection fields.
|
|
370
|
-
* @param options Optional request options.
|
|
371
|
-
*/
|
|
372
|
-
updateCollection(
|
|
373
|
-
id: string,
|
|
374
|
-
data: Record<string, unknown>,
|
|
375
|
-
options?: RequestOptions,
|
|
376
|
-
): Promise<ApiRecord | null> {
|
|
377
|
-
return this.http.patch<ApiRecord>(
|
|
378
|
-
"/collections/" + encodeURIComponent(id),
|
|
379
|
-
data,
|
|
380
|
-
options,
|
|
381
|
-
);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Delete a collection (admin).
|
|
386
|
-
* @param id Collection ID or name.
|
|
387
|
-
* @param options Optional request options.
|
|
388
|
-
*/
|
|
389
|
-
deleteCollection(id: string, options?: RequestOptions): Promise<null> {
|
|
390
|
-
return this.http.delete("/collections/" + encodeURIComponent(id), options);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// ── Records (dynamic collection) ──
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* List records from a dynamic collection with optional filter/sort/pagination.
|
|
397
|
-
*
|
|
398
|
-
* @param coll Collection name.
|
|
399
|
-
* @param params Query parameters including:
|
|
400
|
-
* - `filter` — PocketBase filter syntax (e.g. `title~'hello' && published=true`)
|
|
401
|
-
* - `sort` — Comma-separated, `-` prefix for DESC (e.g. `-created,title`)
|
|
402
|
-
* - `page` — Page number (default: 1)
|
|
403
|
-
* - `perPage` — Items per page (default: 30, max: 200)
|
|
404
|
-
* - `expand` — Comma-separated relation fields (e.g. `author,category`)
|
|
405
|
-
* @param options Optional request options.
|
|
406
|
-
*/
|
|
407
|
-
listRecords(
|
|
408
|
-
coll: string,
|
|
409
|
-
params?: Record<string, string>,
|
|
410
|
-
options?: RequestOptions,
|
|
411
|
-
): Promise<ListResult<ApiRecord> | null> {
|
|
412
|
-
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
413
|
-
return this.http.get<ListResult<ApiRecord>>(
|
|
414
|
-
"/" + encodeURIComponent(coll) + qs,
|
|
415
|
-
options,
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* Get a single record by ID.
|
|
421
|
-
* @param coll Collection name.
|
|
422
|
-
* @param id Record ID.
|
|
423
|
-
* @param options Optional request options.
|
|
424
|
-
*/
|
|
425
|
-
getRecord(
|
|
426
|
-
coll: string,
|
|
427
|
-
id: string,
|
|
428
|
-
options?: RequestOptions,
|
|
429
|
-
): Promise<ApiRecord | null> {
|
|
430
|
-
return this.http.get<ApiRecord>(
|
|
431
|
-
"/" + encodeURIComponent(coll) + "/" + encodeURIComponent(id),
|
|
432
|
-
options,
|
|
433
|
-
);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Create a record in a dynamic collection.
|
|
438
|
-
* @param coll Collection name.
|
|
439
|
-
* @param data Record fields.
|
|
440
|
-
* @param options Optional request options.
|
|
441
|
-
*/
|
|
442
|
-
createRecord(
|
|
443
|
-
coll: string,
|
|
444
|
-
data: Record<string, unknown>,
|
|
445
|
-
options?: RequestOptions,
|
|
446
|
-
): Promise<ApiRecord | null> {
|
|
447
|
-
return this.http.post<ApiRecord>(
|
|
448
|
-
"/" + encodeURIComponent(coll),
|
|
449
|
-
data,
|
|
450
|
-
options,
|
|
451
|
-
);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Update a record in a dynamic collection.
|
|
456
|
-
* @param coll Collection name.
|
|
457
|
-
* @param id Record ID.
|
|
458
|
-
* @param data Updated record fields.
|
|
459
|
-
* @param options Optional request options.
|
|
460
|
-
*/
|
|
461
|
-
updateRecord(
|
|
462
|
-
coll: string,
|
|
463
|
-
id: string,
|
|
464
|
-
data: Record<string, unknown>,
|
|
465
|
-
options?: RequestOptions,
|
|
466
|
-
): Promise<ApiRecord | null> {
|
|
467
|
-
return this.http.patch<ApiRecord>(
|
|
468
|
-
"/" + encodeURIComponent(coll) + "/" + encodeURIComponent(id),
|
|
469
|
-
data,
|
|
470
|
-
options,
|
|
471
|
-
);
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Delete a record from a dynamic collection.
|
|
476
|
-
* @param coll Collection name.
|
|
477
|
-
* @param id Record ID.
|
|
478
|
-
* @param options Optional request options.
|
|
479
|
-
*/
|
|
480
|
-
deleteRecord(
|
|
481
|
-
coll: string,
|
|
482
|
-
id: string,
|
|
483
|
-
options?: RequestOptions,
|
|
484
|
-
): Promise<null> {
|
|
485
|
-
return this.http.delete(
|
|
486
|
-
"/" + encodeURIComponent(coll) + "/" + encodeURIComponent(id),
|
|
487
|
-
options,
|
|
488
|
-
);
|
|
489
|
-
}
|
|
490
325
|
}
|