lazypock 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.cts CHANGED
@@ -1,12 +1,36 @@
1
- /** Shape of a record returned from any collection. */
2
- interface ApiRecord {
1
+ /**
2
+ * Base shape every record returned from any collection satisfies.
3
+ * Generated record interfaces extend this.
4
+ */
5
+ interface BaseRecordFields {
3
6
  id: string;
4
7
  collectionId: string;
5
8
  collectionName: string;
6
9
  created: string;
7
10
  updated: string;
11
+ }
12
+ /** Shape of a record returned from any collection. */
13
+ interface ApiRecord extends BaseRecordFields {
8
14
  [key: string]: unknown;
9
15
  }
16
+ /**
17
+ * Structural marker for concrete record shapes (generated or hand-written).
18
+ * Used to differentiate a typed collection service from the untyped default.
19
+ */
20
+ type RecordShape = Record<string, unknown>;
21
+ /** System fields every record carries — not user-provided on create. */
22
+ type SystemFields = "id" | "collectionId" | "collectionName" | "created" | "updated";
23
+ /**
24
+ * Data accepted by `create()`: any subset of `T`'s fields, but
25
+ * never the system fields. Unknown/extra keys are rejected at compile
26
+ * time via excess-property checking (object literals).
27
+ */
28
+ type CreateData<T> = Partial<Omit<T, SystemFields>>;
29
+ /**
30
+ * Data accepted by `update()`: any subset of `T`'s fields.
31
+ * Unknown/extra keys are rejected at compile time.
32
+ */
33
+ type UpdateData<T> = Partial<Omit<T, SystemFields>>;
10
34
  /** Paginated list response matching PocketBase format. */
11
35
  interface ListResult<T = ApiRecord> {
12
36
  items: T[];
@@ -159,78 +183,6 @@ declare class HttpClient {
159
183
  delete<T = unknown>(path: string, options?: RequestOptions): Promise<T | null>;
160
184
  }
161
185
 
162
- /**
163
- * Typed CRUD service for a single dynamic collection.
164
- * Get an instance via {@link LazypockClient.collection}.
165
- */
166
- declare class CollectionService {
167
- private http;
168
- private collectionName;
169
- private authStore?;
170
- /** @internal */
171
- constructor(http: HttpClient, collectionName: string, authStore?: AuthStore);
172
- private encodeId;
173
- /**
174
- * List records with optional filter/sort/pagination.
175
- * @param params Query parameters including `filter`, `sort`, `page`, `perPage`, `expand`.
176
- * @param options Optional request options.
177
- */
178
- list<T = ApiRecord>(params?: Record<string, string>, options?: RequestOptions): Promise<ListResult<T> | null>;
179
- /**
180
- * Get a single record by ID.
181
- * @param id Record ID.
182
- * @param options Optional request options.
183
- */
184
- getOne<T = ApiRecord>(id: string, options?: RequestOptions): Promise<T | null>;
185
- /**
186
- * Create a new record.
187
- * @param data Record fields.
188
- * @param options Optional request options.
189
- */
190
- create<T = ApiRecord>(data: Record<string, unknown>, options?: RequestOptions): Promise<T | null>;
191
- /**
192
- * Update a record by ID.
193
- * @param id Record ID.
194
- * @param data Updated record fields.
195
- * @param options Optional request options.
196
- */
197
- update<T = ApiRecord>(id: string, data: Record<string, unknown>, options?: RequestOptions): Promise<T | null>;
198
- /**
199
- * Delete a record by ID.
200
- * @param id Record ID.
201
- * @param options Optional request options.
202
- */
203
- delete(id: string, options?: RequestOptions): Promise<null>;
204
- /**
205
- * Get a list of expandable (relation) fields for this collection.
206
- * Useful for constructing `expand` query parameters.
207
- */
208
- expandFields(options?: RequestOptions): Promise<{
209
- field: string;
210
- targetCollection: string;
211
- }[] | null>;
212
- /**
213
- * Authenticate with email/password against this auth collection.
214
- * Stores the returned token and user model in the auth store.
215
- */
216
- authWithPassword(identity: string, password: string, options?: RequestOptions): Promise<({
217
- token: string;
218
- record: ApiRecord;
219
- } & Record<string, unknown>) | null>;
220
- /**
221
- * Refresh the auth token for the currently authenticated user.
222
- * Updates the stored token and user model.
223
- */
224
- authRefresh(options?: RequestOptions): Promise<({
225
- token: string;
226
- record: ApiRecord;
227
- } & Record<string, unknown>) | null>;
228
- /**
229
- * Get available auth methods for this collection.
230
- */
231
- authMethods(options?: RequestOptions): Promise<Record<string, unknown> | null>;
232
- }
233
-
234
186
  interface RealtimeEvent {
235
187
  event: string;
236
188
  topic: string;
@@ -272,6 +224,21 @@ declare class RealtimeService {
272
224
  onError?: (err: Event) => void;
273
225
  private url;
274
226
  private token;
227
+ /** Whether the WebSocket is currently open. */
228
+ get isOpen(): boolean;
229
+ /**
230
+ * The most recently used WebSocket URL (set on {@link connect}).
231
+ * Useful for SDK convenience methods that auto-connect before subscribing.
232
+ */
233
+ get lastUrl(): string;
234
+ /** The auth token configured for this connection (set on connect). */
235
+ get lastToken(): string | undefined;
236
+ /**
237
+ * Set the socket URL. Useful before subscribing so the SDK can
238
+ * auto-connect on the first {@link subscribe}.
239
+ */
240
+ setUrl(url: string): void;
241
+ ensureConnected(): void;
275
242
  connect(opts: RealtimeConnectOpts): void;
276
243
  disconnect(): void;
277
244
  /**
@@ -295,6 +262,130 @@ declare class RealtimeService {
295
262
  private clearReconnectTimer;
296
263
  }
297
264
 
265
+ /**
266
+ * A realtime record-change event delivered to subscription callbacks.
267
+ * Mirrors PocketBase's RealtimeService result shape (`action` + `record`).
268
+ */
269
+ interface RealtimeMessage {
270
+ action: "create" | "update" | "delete";
271
+ record: Record<string, unknown>;
272
+ topic?: string;
273
+ }
274
+ /** Subscription callback for a collection's realtime events. */
275
+ type RealtimeCallback = (e: RealtimeMessage) => void;
276
+ /**
277
+ * Typed CRUD service for a single dynamic collection.
278
+ * Get an instance via {@link LazypockClient.collection}.
279
+ *
280
+ * @typeParam T — The record shape for this collection. Defaults to {@link ApiRecord}.
281
+ */
282
+ declare class CollectionService<T = ApiRecord> {
283
+ private http;
284
+ private collectionName;
285
+ private authStore?;
286
+ private realtime?;
287
+ /** @internal */
288
+ constructor(http: HttpClient, collectionName: string, authStore?: AuthStore, realtime?: RealtimeService);
289
+ private encodeId;
290
+ /**
291
+ * List records with optional filter/sort/pagination.
292
+ * @param params Query parameters including `filter`, `sort`, `page`, `perPage`, `expand`.
293
+ * @param options Optional request options.
294
+ */
295
+ list(params?: Record<string, string>, options?: RequestOptions): Promise<ListResult<T> | null>;
296
+ /**
297
+ * Get a single record by ID.
298
+ * @param id Record ID.
299
+ * @param options Optional request options.
300
+ */
301
+ getOne(id: string, options?: RequestOptions): Promise<T | null>;
302
+ /**
303
+ * Create a new record.
304
+ * @param data Record fields. When `T` is a concrete shape (e.g. a generated
305
+ * record type), excess/unknown fields are rejected at compile time.
306
+ * @param options Optional request options.
307
+ */
308
+ create(data: T extends ApiRecord ? Record<string, unknown> : CreateData<T>, options?: RequestOptions): Promise<T | null>;
309
+ /**
310
+ * Update a record by ID.
311
+ * @param id Record ID.
312
+ * @param data Updated record fields. When `T` is a concrete shape, `data`
313
+ * must be a partial of `T` — unknown fields are rejected.
314
+ * @param options Optional request options.
315
+ */
316
+ update(id: string, data: T extends ApiRecord ? Record<string, unknown> : UpdateData<T>, options?: RequestOptions): Promise<T | null>;
317
+ /**
318
+ * Delete a record by ID.
319
+ * @param id Record ID.
320
+ * @param options Optional request options.
321
+ */
322
+ delete(id: string, options?: RequestOptions): Promise<null>;
323
+ /**
324
+ * Get a list of expandable (relation) fields for this collection.
325
+ * Useful for constructing `expand` query parameters.
326
+ */
327
+ expandFields(options?: RequestOptions): Promise<{
328
+ field: string;
329
+ targetCollection: string;
330
+ }[] | null>;
331
+ /**
332
+ * Cast this service to a specific record shape.
333
+ * Use when you have a hand-written or generated interface for the
334
+ * collection and want compile-time checking of create/update/list.
335
+ *
336
+ * @example
337
+ * ```ts
338
+ * interface Post {
339
+ * id: string;
340
+ * title: string;
341
+ * published: boolean;
342
+ * }
343
+ * const posts = client.collection("posts").typed<Post>();
344
+ * await posts.create({ title: "Hi", published: true }); // ✓
345
+ * await posts.create({ nope: 1 }); // ✗ compile error
346
+ * ```
347
+ */
348
+ typed<TRecord = ApiRecord>(): CollectionService<TRecord>;
349
+ /**
350
+ * Subscribe to realtime changes for this collection.
351
+ * The event's `action` is one of `"create" | "update" | "delete"`.
352
+ *
353
+ * Access is governed by the collection's `listRule` (PocketBase semantics):
354
+ * public collections allow anonymous subscriptions; other collections
355
+ * require a matching logged-in user or superuser.
356
+ *
357
+ * @param callback Received on every record change.
358
+ * @param recordId Optional — subscribe to a single record instead of `*`.
359
+ * @returns A function that unsubscribes this callback.
360
+ */
361
+ subscribe(callback: RealtimeCallback, recordId?: string): () => void;
362
+ /**
363
+ * Unsubscribe all callbacks from this collection (or a specific record).
364
+ * @param recordId Optional record id; omitting it unsubs everything.
365
+ */
366
+ unsubscribe(recordId?: string): void;
367
+ /**
368
+ * Authenticate with email/password against this auth collection.
369
+ * Stores the returned token and user model in the auth store.
370
+ */
371
+ authWithPassword(identity: string, password: string, options?: RequestOptions): Promise<({
372
+ token: string;
373
+ record: ApiRecord;
374
+ } & Record<string, unknown>) | null>;
375
+ /**
376
+ * Refresh the auth token for the currently authenticated user.
377
+ * Updates the stored token and user model.
378
+ */
379
+ authRefresh(options?: RequestOptions): Promise<({
380
+ token: string;
381
+ record: ApiRecord;
382
+ } & Record<string, unknown>) | null>;
383
+ /**
384
+ * Get available auth methods for this collection.
385
+ */
386
+ authMethods(options?: RequestOptions): Promise<Record<string, unknown> | null>;
387
+ }
388
+
298
389
  /** Response shape from the server file endpoints */
299
390
  interface FileRecord {
300
391
  id: string;
@@ -341,6 +432,76 @@ declare class FilesService {
341
432
  delete(fileId: string, options?: RequestOptions): Promise<null>;
342
433
  }
343
434
 
435
+ /** A single field definition as returned by `GET /collections`. */
436
+ interface SchemaField {
437
+ id?: string;
438
+ name: string;
439
+ type: string;
440
+ required?: boolean;
441
+ unique?: boolean;
442
+ options?: {
443
+ /** For relation fields: the target collection name. */
444
+ collection?: string;
445
+ /** Max selectable/related items (multi when > 1). */
446
+ maxSelect?: number;
447
+ /** Allowed values for select / multi_select fields. */
448
+ values?: string[];
449
+ } & Record<string, unknown>;
450
+ indexed?: boolean;
451
+ hidden?: boolean;
452
+ system?: boolean;
453
+ sort_order?: number;
454
+ }
455
+ /** A collection definition as returned by `GET /collections`. */
456
+ interface CollectionSchema {
457
+ id?: string;
458
+ name: string;
459
+ type: "base" | "auth";
460
+ system?: boolean;
461
+ fields?: SchemaField[];
462
+ rules?: Record<string, unknown>;
463
+ options?: Record<string, unknown>;
464
+ }
465
+
466
+ /** Format a raw collection name into a valid TS identifier (PascalCase). */
467
+ declare function collectionTypeName(name: string): string;
468
+ /**
469
+ * Generate the full TypeScript source for the typed SDK module.
470
+ *
471
+ * @param collections Collections fetched from the API.
472
+ * @param options Generation options.
473
+ */
474
+ declare function generateTypes(collections: CollectionSchema[], options?: {
475
+ /** Import specifier for the lazypock package (default `lazypock`). */
476
+ packageName?: string;
477
+ /** Emit base record fields (id, created, updated, …). Default true. */
478
+ includeBaseFields?: boolean;
479
+ /** Skip system collections (names starting with `_` or `users`). Default false. */
480
+ skipSystem?: boolean;
481
+ }): string;
482
+
483
+ /**
484
+ * Map a single server field to its TypeScript type string.
485
+ * Used by the codegen CLI to emit interface members.
486
+ *
487
+ * @param field The field definition.
488
+ * @param fallback Fallback type for unknown field types (default `unknown`).
489
+ */
490
+ declare function fieldTypeScriptType(field: SchemaField, fallback?: string): string;
491
+ /**
492
+ * Returns the runtime type kind for a field — used by {@link schemaFieldType}
493
+ * to build structural types at runtime.
494
+ */
495
+ type FieldTypeKind = "string" | "number" | "boolean" | "string-array" | "json" | "relation" | "relation-many" | "password" | "unknown";
496
+ /** Map a server field to its runtime type kind. */
497
+ declare function fieldTypeKind(field: SchemaField): FieldTypeKind;
498
+ /**
499
+ * Derive a TypeScript field type from a {@link SchemaField} — the runtime
500
+ * counterpart to the codegen mapper. Lets consumers build typed clients
501
+ * from a fetched schema without running the CLI.
502
+ */
503
+ declare function schemaFieldType(field: SchemaField): unknown;
504
+
344
505
  /** Options for constructing a {@link LazypockClient}. */
345
506
  interface LazypockClientOptions {
346
507
  /** API base URL (e.g. 'http://localhost:4000/api') */
@@ -351,6 +512,17 @@ interface LazypockClientOptions {
351
512
  authStore?: AuthStore;
352
513
  /** Real-time service for Phoenix Channel WebSocket subscriptions */
353
514
  realtime?: RealtimeService;
515
+ /**
516
+ * Optional schema types for generating typed services at runtime.
517
+ * When provided, `collection()` returns a service whose create/update
518
+ * inputs are validated against the mapped field types.
519
+ *
520
+ * @experimental
521
+ */
522
+ types?: {
523
+ /** Collection schemas fetched from the API (e.g. via `GET /collections`). */
524
+ schemas?: CollectionSchema[];
525
+ };
354
526
  }
355
527
  /**
356
528
  * Lazypock API client.
@@ -371,19 +543,40 @@ declare class LazypockClient {
371
543
  readonly realtime: RealtimeService;
372
544
  readonly files: FilesService;
373
545
  private collectionCache;
546
+ private schemaByName?;
374
547
  /**
375
548
  * Create a new Lazypock client.
376
549
  * @param options Configuration options.
377
550
  */
378
551
  constructor(options: LazypockClientOptions);
379
552
  /**
380
- * Get or create a typed service for the given collection.
553
+ * Get or create a service for the given collection.
381
554
  * Services are cached after first access.
382
555
  *
556
+ * For typed CRUD, either:
557
+ * - cast at the call site: `client.collection("posts").typed<Post>()`
558
+ * - or use the typed factory: `createClient<{ posts: Post }>()`
559
+ *
383
560
  * @param name The collection name.
384
561
  * @returns A {@link CollectionService} instance.
385
562
  */
386
- collection(name: string): CollectionService;
563
+ collection(name: string): CollectionService<unknown>;
564
+ /**
565
+ * Get a typed service whose record shape is derived from the schema
566
+ * passed via `options.types.schemas` (if available), or fall back to
567
+ * the untyped service otherwise.
568
+ *
569
+ * @experimental
570
+ */
571
+ collectionFor<TRecord = ApiRecord>(name: string): CollectionService<TRecord>;
572
+ /**
573
+ * Generate TypeScript types from the schemas provided to this client
574
+ * (via `options.types.schemas`). Returns a string ready to write to a
575
+ * `lazypock.types.ts` file.
576
+ */
577
+ generateTypes(options?: {
578
+ packageName?: string;
579
+ }): string;
387
580
  /** Check whether any superuser exists (for login vs setup screen routing). */
388
581
  checkSuperuser(): Promise<{
389
582
  has_superuser: boolean;
@@ -517,4 +710,32 @@ declare class LazypockClient {
517
710
  deleteRecord(coll: string, id: string, options?: RequestOptions): Promise<null>;
518
711
  }
519
712
 
520
- export { ApiError, type ApiRecord, type AuthModel, AuthStore, type FileRecord, FilesService, LazypockClient, type LazypockClientOptions, type ListResult, RealtimeService, type RequestOptions, type StorageAdapter, getFileUrl, wsUrlFromBaseUrl };
713
+ /**
714
+ * A collections map: `{ posts: PostRecord; users: UserRecord; ... }`.
715
+ * Generated by the codegen CLI (`npx lazypock-gen`) or written by hand.
716
+ */
717
+ type LazypockCollections = Record<string, unknown>;
718
+ /**
719
+ * Client typed against a {@link LazypockCollections} map.
720
+ *
721
+ * ```ts
722
+ * import { createClient } from "lazypock/generated";
723
+ * const client = createClient({ baseUrl: "http://localhost:4000/api" });
724
+ * const posts = await client.collection("posts").list<PostsRecord>();
725
+ * ```
726
+ */
727
+ declare class TypedClient<TCollections extends LazypockCollections = LazypockCollections> extends LazypockClient {
728
+ /**
729
+ * Get a typed service for a collection.
730
+ * When `TCollections` is provided, unknown collection names are rejected.
731
+ * @typeParam K — Collection name (keyof TCollections).
732
+ */
733
+ collection<K extends keyof TCollections>(name: K): CollectionService<TCollections[K]>;
734
+ }
735
+ /**
736
+ * Create a {@link TypedClient}.
737
+ * @typeParam TCollections — Map of collection name → record shape.
738
+ */
739
+ declare function createClient<TCollections extends LazypockCollections = LazypockCollections>(options: LazypockClientOptions): TypedClient<TCollections>;
740
+
741
+ export { ApiError, type ApiRecord, type AuthModel, AuthStore, type CollectionSchema, CollectionService, type CreateData, type FileRecord, FilesService, LazypockClient, type LazypockClientOptions, type LazypockCollections, type ListResult, type RealtimeCallback, type RealtimeMessage, RealtimeService, type RecordShape, type RequestOptions, type SchemaField, type StorageAdapter, type SystemFields, TypedClient, type UpdateData, collectionTypeName, createClient, fieldTypeKind, fieldTypeScriptType, generateTypes, getFileUrl, schemaFieldType, wsUrlFromBaseUrl };