lazypock 0.10.2 → 0.12.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lazypock",
3
- "version": "0.10.2",
3
+ "version": "0.12.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
package/src/codegen.ts CHANGED
@@ -12,6 +12,23 @@
12
12
  import type { CollectionSchema, SchemaField } from "./schema";
13
13
  import { fieldTypeScriptType } from "./typegen";
14
14
 
15
+ /**
16
+ * Members already declared by the generated `BaseRecord` interface. A
17
+ * collection field with one of these names must NOT be re-declared on the
18
+ * per-collection read/query interfaces: redeclaring `created`/`updated`
19
+ * (PocketBase-style autodate fields, which the mapper types as `string`) as
20
+ * an optional member makes TypeScript reject the interface with TS2430
21
+ * ("incorrectly extends interface 'BaseRecord'") because the base declares
22
+ * them as required `string`. The base declaration is kept.
23
+ */
24
+ const BASE_RECORD_KEYS = new Set([
25
+ "id",
26
+ "collectionId",
27
+ "collectionName",
28
+ "created",
29
+ "updated",
30
+ ]);
31
+
15
32
  /** Format a raw collection name into a valid TS identifier (PascalCase). */
16
33
  export function collectionTypeName(name: string): string {
17
34
  return name
@@ -73,11 +90,22 @@ export function generateTypes(
73
90
  }`);
74
91
  }
75
92
 
93
+ // Resolve relation targets (collectionId → record type) for the expand maps.
94
+ // Only collections with a real id are indexed — schemas without ids (e.g.
95
+ // minimal hand-written snapshots) fall back to the name lookup below.
96
+ const collById = new Map(
97
+ filtered
98
+ .filter((c) => c.id !== undefined && c.id !== "")
99
+ .map((c) => [String(c.id), c]),
100
+ );
101
+
76
102
  // One interface per collection
77
103
  for (const coll of filtered) {
78
104
  const typeName = collectionTypeName(coll.name);
79
105
  const fields = coll.fields ?? [];
80
- const lines = fields.map((f) => memberLine(f)).filter((l) => l !== "");
106
+ const lines = fields
107
+ .map((f) => memberLine(f, includeBaseFields))
108
+ .filter((l) => l !== "");
81
109
  const body = lines.join("\n");
82
110
  sections.push(
83
111
  `export interface ${typeName}Record${renderInterface({
@@ -114,6 +142,9 @@ export function generateTypes(
114
142
  for (const f of fields) {
115
143
  if (f.type === "password") continue;
116
144
  const key = fieldKey(f.name);
145
+ // The query interface extends BaseRecord too (when base fields are
146
+ // emitted), so the same key collisions apply.
147
+ if (includeBaseFields && BASE_RECORD_KEYS.has(key)) continue;
117
148
  const type = fieldTypeScriptType(f);
118
149
  if (type === "never") continue;
119
150
  queryLines.push(` ${JSON.stringify(key)}?: ${type};`);
@@ -124,6 +155,42 @@ export function generateTypes(
124
155
  body: queryLines.join("\n"),
125
156
  })}`,
126
157
  );
158
+
159
+ // Expand map — relation field → target record type, so expanded
160
+ // records are typed (`record.expand.user` is `UsersRecord` instead of
161
+ // `unknown`). Built from each relation's `options.collectionId`; auth
162
+ // targets get `& AuthRecord`, multi-relations (maxSelect > 1) get
163
+ // arrays, and unknown targets (e.g. skipped collections) fall back to
164
+ // `unknown` via the `ExpandValue` fallback.
165
+ const expandLines: string[] = [];
166
+ for (const f of fields) {
167
+ if (f.type !== "relation") continue;
168
+ // Resolve the target collection: real PocketBase schemas carry
169
+ // `options.collectionId` (the target collection's id), while some
170
+ // tooling/snapshots use `options.collection` (the target's name).
171
+ // Unknown/absent targets fall back to `unknown` via ExpandValue.
172
+ // SAFETY: both option keys are strings; guard with String().
173
+ const target =
174
+ collById.get(String(f.options?.collectionId ?? "")) ??
175
+ filtered.find(
176
+ (c) => c.name === String(f.options?.collection ?? ""),
177
+ );
178
+ if (!target) continue;
179
+ const targetType = `${collectionTypeName(target.name)}Record${
180
+ target.type === "auth" ? " & AuthRecord" : ""
181
+ }`;
182
+ const many = (f.options?.maxSelect ?? 1) > 1;
183
+ expandLines.push(
184
+ ` ${JSON.stringify(fieldKey(f.name))}: ${
185
+ many ? `Array<${targetType}>` : targetType
186
+ };`,
187
+ );
188
+ }
189
+ sections.push(
190
+ `export interface ${typeName}ExpandMap${renderInterface({
191
+ body: expandLines.join("\n"),
192
+ })}`,
193
+ );
127
194
  }
128
195
 
129
196
  // Auth collection type
@@ -167,6 +234,15 @@ ${createMapEntries}
167
234
  .join("\n");
168
235
  sections.push(`export interface LazypockQueryFields {
169
236
  ${queryMapEntries}
237
+ }`);
238
+
239
+ // Expand map — relation field → target record type per collection, bound
240
+ // as the 4th CollectionService generic so expanded records are typed.
241
+ const expandMapEntries = filtered
242
+ .map((c) => ` "${c.name}": ${collectionTypeName(c.name)}ExpandMap;`)
243
+ .join("\n");
244
+ sections.push(`export interface LazypockExpandMaps {
245
+ ${expandMapEntries}
170
246
  }`);
171
247
 
172
248
  // Runtime schema snapshot — lets the client exclude hidden fields by
@@ -228,25 +304,31 @@ export class TypedClient extends LazypockClient {
228
304
  // The (string & {}) intersection keeps the union from collapsing to plain
229
305
  // string (which would silently kill the suggestions).
230
306
  //
231
- // The service binds three types: the read model, the write-only *CreateData,
232
- // and the *QueryFields (filter/sort/expand/select keys). QueryFields is what
233
- // lets hidden relation fields — excluded from the read model but still
234
- // expandable/filterable at runtime — typecheck:
307
+ // The service binds four types: the read model, the write-only *CreateData,
308
+ // the *QueryFields (filter/sort/expand/select keys), and the *ExpandMap
309
+ // (relation field → target record type). QueryFields is what lets hidden
310
+ // relation fields — excluded from the read model but still expandable/
311
+ // filterable at runtime — typecheck:
235
312
  // client.collection("project_members").getFullList({ expand: "user" }) // ✓
313
+ // ExpandMap is what types the expanded data on the result:
314
+ // const [rec] = await client.collection("posts").getFullList({ expand: "user" });
315
+ // rec.expand?.user // ✓ UsersRecord, not unknown
236
316
  override collection<K extends keyof LazypockCollections | (string & {})>(
237
317
  name: K,
238
318
  ): K extends keyof LazypockCollections
239
319
  ? CollectionService<
240
320
  LazypockCollections[K],
241
321
  LazypockCreateData[K],
242
- LazypockQueryFields[K]
322
+ LazypockQueryFields[K],
323
+ LazypockExpandMaps[K]
243
324
  >
244
325
  : CollectionService<unknown> {
245
326
  return super.collection(name) as K extends keyof LazypockCollections
246
327
  ? CollectionService<
247
328
  LazypockCollections[K],
248
329
  LazypockCreateData[K],
249
- LazypockQueryFields[K]
330
+ LazypockQueryFields[K],
331
+ LazypockExpandMaps[K]
250
332
  >
251
333
  : CollectionService<unknown>;
252
334
  }
@@ -266,12 +348,19 @@ function renderInterface(opts: { extends?: string; body: string }): string {
266
348
  return `${ext} {\n${opts.body}\n}`;
267
349
  }
268
350
 
269
- /** Render a single interface member line for a field. */
270
- function memberLine(f: SchemaField): string {
351
+ /**
352
+ * Render a single interface member line for a field.
353
+ *
354
+ * `skipBaseKeys` is true when the interface extends `BaseRecord`: keys the
355
+ * base already declares are omitted so the child never redeclares them with
356
+ * an incompatible (optional / non-string) type.
357
+ */
358
+ function memberLine(f: SchemaField, skipBaseKeys = false): string {
271
359
  // Hidden fields are excluded from API responses by the schema-aware
272
360
  // client default — don't expose them on the read model either.
273
361
  if (f.hidden) return "";
274
362
  const key = fieldKey(f.name);
363
+ if (skipBaseKeys && BASE_RECORD_KEYS.has(key)) return "";
275
364
  const req = f.required || f.type === "password" ? "" : "?";
276
365
  const type = fieldTypeScriptType(f);
277
366
  // `never` members (passwords) are omitted from read models.
package/src/collection.ts CHANGED
@@ -128,8 +128,17 @@ function normalizeAction(
128
128
  * Defaults to `T`. The codegen client binds a *QueryFields type that includes
129
129
  * hidden fields — excluded from the read model but still expandable/filterable
130
130
  * at runtime — so `expand: "hiddenRelation"` typechecks.
131
+ * @typeParam TExpand — Map of relation field → target record type. Defaults to
132
+ * `Record<string, unknown>`. The codegen client binds a per-collection
133
+ * *ExpandMap, so `record.expand.user` is typed as the target `UsersRecord`
134
+ * instead of `unknown`.
131
135
  */
132
- export class CollectionService<T = ApiRecord, TData = never, TFields = T> {
136
+ export class CollectionService<
137
+ T = ApiRecord,
138
+ TData = never,
139
+ TFields = T,
140
+ TExpand = Record<string, unknown>,
141
+ > {
133
142
  private http: HttpClient;
134
143
  private collectionName: string;
135
144
  private authStore?: AuthStore;
@@ -189,9 +198,9 @@ export class CollectionService<T = ApiRecord, TData = never, TFields = T> {
189
198
  */
190
199
  select<K extends FieldKey<TFields> | "*">(
191
200
  ...fields: K[]
192
- ): CollectionService<T, TData, TFields> {
201
+ ): CollectionService<T, TData, TFields, TExpand> {
193
202
  const preset = fields.length === 0 ? "*" : fields.join(",");
194
- const derived = new CollectionService<T, TData, TFields>(
203
+ const derived = new CollectionService<T, TData, TFields, TExpand>(
195
204
  this.http,
196
205
  this.collectionName,
197
206
  this.authStore,
@@ -302,7 +311,7 @@ export class CollectionService<T = ApiRecord, TData = never, TFields = T> {
302
311
  page = 1,
303
312
  perPage = 30,
304
313
  options?: ListOptions<TFields, E, S, F> & RequestOptions,
305
- ): Promise<ListResult<T2 & { expand?: ExpandObj<E> }> | null> {
314
+ ): Promise<ListResult<T2 & { expand?: ExpandObj<E, TExpand> }> | null> {
306
315
  const {
307
316
  requestKey,
308
317
  autoCancel,
@@ -332,7 +341,7 @@ export class CollectionService<T = ApiRecord, TData = never, TFields = T> {
332
341
  }).map(([k, v]) => [k, String(v)]),
333
342
  ),
334
343
  ).toString();
335
- return this.http.get<ListResult<T2 & { expand?: ExpandObj<E> }>>(
344
+ return this.http.get<ListResult<T2 & { expand?: ExpandObj<E, TExpand> }>>(
336
345
  "/" + this.encodeId(this.collectionName) + "?" + qs,
337
346
  {
338
347
  requestKey,
@@ -360,7 +369,7 @@ export class CollectionService<T = ApiRecord, TData = never, TFields = T> {
360
369
  F extends string = never,
361
370
  >(
362
371
  options?: ListOptions<TFields, E, S, F> & RequestOptions,
363
- ): Promise<Array<T2 & { expand?: ExpandObj<E> }>> {
372
+ ): Promise<Array<T2 & { expand?: ExpandObj<E, TExpand> }>> {
364
373
  const { batch = 1000, ...rest } = options ?? {};
365
374
 
366
375
  // Build a stable request key for the whole full-list fetch (NOT per page,
@@ -373,7 +382,7 @@ export class CollectionService<T = ApiRecord, TData = never, TFields = T> {
373
382
  ? rest.requestKey
374
383
  : `getFullList:${this.collectionName}:${stableStringify(rest)}`;
375
384
 
376
- const items: Array<T2 & { expand?: ExpandObj<E> }> = [];
385
+ const items: Array<T2 & { expand?: ExpandObj<E, TExpand> }> = [];
377
386
  let page = 1;
378
387
  for (;;) {
379
388
  const res = await this.getList<T2>(
@@ -386,7 +395,7 @@ export class CollectionService<T = ApiRecord, TData = never, TFields = T> {
386
395
  } as ListOptions<TFields> & RequestOptions,
387
396
  );
388
397
  if (!res || !res.items || res.items.length === 0) break;
389
- items.push(...(res.items as Array<T2 & { expand?: ExpandObj<E> }>));
398
+ items.push(...(res.items as Array<T2 & { expand?: ExpandObj<E, TExpand> }>));
390
399
  if (page >= (res.totalPages ?? page)) break;
391
400
  page += 1;
392
401
  }
@@ -407,7 +416,7 @@ export class CollectionService<T = ApiRecord, TData = never, TFields = T> {
407
416
  >(
408
417
  filter: FilterString<TFields, F>,
409
418
  options?: ListOptions<TFields, E, S, F> & RequestOptions,
410
- ): Promise<(T2 & { expand?: ExpandObj<E> }) | null> {
419
+ ): Promise<(T2 & { expand?: ExpandObj<E, TExpand> }) | null> {
411
420
  const res = await this.getList<T2, E, S, F>(1, 1, {
412
421
  ...options,
413
422
  filter,
@@ -423,7 +432,7 @@ export class CollectionService<T = ApiRecord, TData = never, TFields = T> {
423
432
  getOne<E extends string = never>(
424
433
  id: string,
425
434
  options?: ReadOptions<TFields, E> & RequestOptions,
426
- ): Promise<(T & { expand?: ExpandObj<E> }) | null> {
435
+ ): Promise<(T & { expand?: ExpandObj<E, TExpand> }) | null> {
427
436
  const fields = this.effectiveFields(options?.fields);
428
437
  if (typeof options?.expand === "string") {
429
438
  this.validateExpand(options.expand);
@@ -432,7 +441,7 @@ export class CollectionService<T = ApiRecord, TData = never, TFields = T> {
432
441
  if (fields !== undefined) qs.set("fields", fields);
433
442
  if (typeof options?.expand === "string") qs.set("expand", options.expand);
434
443
  const qsStr = qs.toString();
435
- return this.http.get<T & { expand?: ExpandObj<E> }>(
444
+ return this.http.get<T & { expand?: ExpandObj<E, TExpand> }>(
436
445
  "/" +
437
446
  this.encodeId(this.collectionName) +
438
447
  "/" +
@@ -550,8 +559,8 @@ export class CollectionService<T = ApiRecord, TData = never, TFields = T> {
550
559
  * `types.schemas`). Enables schema-aware defaults: hidden fields are
551
560
  * excluded from responses, select/expand are validated.
552
561
  */
553
- withSchema(schema: CollectionSchema): CollectionService<T, TData, TFields> {
554
- const derived = new CollectionService<T, TData, TFields>(
562
+ withSchema(schema: CollectionSchema): CollectionService<T, TData, TFields, TExpand> {
563
+ const derived = new CollectionService<T, TData, TFields, TExpand>(
555
564
  this.http,
556
565
  this.collectionName,
557
566
  this.authStore,
package/src/typegen.ts CHANGED
@@ -23,6 +23,7 @@ export function fieldTypeScriptType(
23
23
  case "editor":
24
24
  case "date":
25
25
  case "datetime":
26
+ case "autodate":
26
27
  return "string";
27
28
  case "number":
28
29
  return "number";
@@ -86,6 +87,7 @@ export function fieldTypeKind(field: SchemaField): FieldTypeKind {
86
87
  case "editor":
87
88
  case "date":
88
89
  case "datetime":
90
+ case "autodate":
89
91
  case "select":
90
92
  case "file":
91
93
  return "string";
package/src/types.ts CHANGED
@@ -149,7 +149,13 @@ export type FieldKey<T> = Extract<keyof T, string> extends never
149
149
  ? string
150
150
  : Extract<keyof T, string>;
151
151
 
152
- /** Valid filter operators, matching the backend FilterCompiler. */
152
+ /**
153
+ * Valid filter operators, matching the backend FilterCompiler.
154
+ *
155
+ * The `?`-prefixed operators are PocketBase's "any/at least one of"
156
+ * conditions over array-valued fields (multi-select, multiple relation,
157
+ * multiple file). They compile to `= ANY` / `ILIKE ANY` SQL in the backend.
158
+ */
153
159
  export type FilterOp =
154
160
  | "="
155
161
  | "!="
@@ -158,7 +164,15 @@ export type FilterOp =
158
164
  | ">"
159
165
  | ">="
160
166
  | "<"
161
- | "<=";
167
+ | "<="
168
+ | "?="
169
+ | "?!="
170
+ | "?~"
171
+ | "?!~"
172
+ | "?>"
173
+ | "?>="
174
+ | "?<"
175
+ | "?<=";
162
176
 
163
177
  /** Optional whitespace around a filter operator (`project=x` and `project = x` both compile). */
164
178
  type FilterWs = "" | " ";
@@ -412,17 +426,27 @@ type TopExpandKey<S extends string> = S extends `${infer A}.${string}` ? A : S;
412
426
  type ExpandKeys<S extends string> = S extends "" ? never :
413
427
  S extends `${infer H},${infer T}` ? TopExpandKey<H> | ExpandKeys<T> : TopExpandKey<S>;
414
428
 
429
+ /**
430
+ * Value type of one expanded relation: the target record from the expand
431
+ * map (`TExpand`, e.g. the codegen-generated `PostsExpandMap`) when the key
432
+ * is a known relation, `unknown` otherwise.
433
+ */
434
+ type ExpandValue<K extends string, TExpand> = K extends keyof TExpand ? TExpand[K] : unknown;
435
+
415
436
  /**
416
437
  * The `expand` property attached to records when `expand` is used. Keys are
417
438
  * parsed from the caller's expand literal (dot-paths collapse to their first
418
- * segment), so `record.expand.user` typechecks and the nested record is
419
- * `unknown` (its shape belongs to the target collection). Falls back to
420
- * `Record<string, unknown>` when the expand value is dynamic or absent.
439
+ * segment) and each value is typed from `TExpand` — a per-collection map of
440
+ * relation field → target record type (the codegen CLI emits these), so
441
+ * `record.expand.user` is `UsersRecord`, not `unknown`.
442
+ *
443
+ * Falls back to `Record<string, unknown>` when the expand value is dynamic
444
+ * or absent, or when no expand map is bound (hand-written services).
421
445
  */
422
- export type ExpandObj<E extends string> =
446
+ export type ExpandObj<E extends string, TExpand = Record<string, unknown>> =
423
447
  [E] extends [never] ? Record<string, unknown> :
424
448
  [string] extends [E] ? Record<string, unknown> :
425
- Partial<Record<ExpandKeys<E>, unknown>>;
449
+ Partial<{ [K in ExpandKeys<E>]: ExpandValue<K, TExpand> }>;
426
450
 
427
451
  /**
428
452
  * Query options for list/read operations, typed against a record shape `T`.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/typegen.ts","../src/codegen.ts"],"sourcesContent":["// ── Type mapping (runtime + codegen) ────────────────────\n// Maps server field types to TypeScript types.\n// Shared by the runtime `SchemaTypes` helper and the codegen CLI.\n\nimport type { SchemaField } from \"./schema\";\n\n/**\n * Map a single server field to its TypeScript type string.\n * Used by the codegen CLI to emit interface members.\n *\n * @param field The field definition.\n * @param fallback Fallback type for unknown field types (default `unknown`).\n */\nexport function fieldTypeScriptType(\n\tfield: SchemaField,\n\tfallback = \"unknown\",\n): string {\n\tconst opts = field.options ?? {};\n\tswitch (field.type) {\n\t\tcase \"text\":\n\t\tcase \"email\":\n\t\tcase \"url\":\n\t\tcase \"editor\":\n\t\tcase \"date\":\n\t\tcase \"datetime\":\n\t\t\treturn \"string\";\n\t\tcase \"number\":\n\t\t\treturn \"number\";\n\t\tcase \"bool\":\n\t\t\treturn \"boolean\";\n\t\tcase \"select\": {\n\t\t\tconst values = Array.isArray(opts.values) ? opts.values : [];\n\t\t\tif (values.length > 0) {\n\t\t\t\treturn values.map((v) => JSON.stringify(String(v))).join(\" | \");\n\t\t\t}\n\t\t\treturn \"string\";\n\t\t}\n\t\tcase \"multi_select\": {\n\t\t\tconst values = Array.isArray(opts.values) ? opts.values : [];\n\t\t\tif (values.length > 0) {\n\t\t\t\treturn `(${values.map((v) => JSON.stringify(String(v))).join(\" | \")})[]`;\n\t\t\t}\n\t\t\treturn \"string[]\";\n\t\t}\n\t\tcase \"file\":\n\t\t\treturn \"string\";\n\t\tcase \"multi_file\":\n\t\t\treturn \"string[]\";\n\t\tcase \"json\":\n\t\tcase \"geo\":\n\t\t\treturn \"Record<string, unknown>\";\n\t\tcase \"relation\":\n\t\t\t// Relations store the target record's ID (string) — or an array\n\t\t\t// of IDs when multi-relation (maxSelect > 1).\n\t\t\treturn (opts.maxSelect ?? 1) > 1 ? \"string[]\" : \"string\";\n\t\tcase \"password\":\n\t\t\t// Passwords are write-only; never expose on read models.\n\t\t\treturn \"never\";\n\t\tdefault:\n\t\t\treturn fallback;\n\t}\n}\n\n/**\n * Returns the runtime type kind for a field — used by {@link schemaFieldType}\n * to build structural types at runtime.\n */\nexport type FieldTypeKind =\n\t| \"string\"\n\t| \"number\"\n\t| \"boolean\"\n\t| \"string-array\"\n\t| \"json\"\n\t| \"relation\"\n\t| \"relation-many\"\n\t| \"password\"\n\t| \"unknown\";\n\n/** Map a server field to its runtime type kind. */\nexport function fieldTypeKind(field: SchemaField): FieldTypeKind {\n\tconst opts = field.options ?? {};\n\tswitch (field.type) {\n\t\tcase \"text\":\n\t\tcase \"email\":\n\t\tcase \"url\":\n\t\tcase \"editor\":\n\t\tcase \"date\":\n\t\tcase \"datetime\":\n\t\tcase \"select\":\n\t\tcase \"file\":\n\t\t\treturn \"string\";\n\t\tcase \"number\":\n\t\t\treturn \"number\";\n\t\tcase \"bool\":\n\t\t\treturn \"boolean\";\n\t\tcase \"multi_select\":\n\t\tcase \"multi_file\":\n\t\t\treturn \"string-array\";\n\t\tcase \"json\":\n\t\tcase \"geo\":\n\t\t\treturn \"json\";\n\t\tcase \"relation\":\n\t\t\treturn (opts.maxSelect ?? 1) > 1 ? \"relation-many\" : \"relation\";\n\t\tcase \"password\":\n\t\t\treturn \"password\";\n\t\tdefault:\n\t\t\treturn \"unknown\";\n\t}\n}\n\n/**\n * Derive a TypeScript field type from a {@link SchemaField} — the runtime\n * counterpart to the codegen mapper. Lets consumers build typed clients\n * from a fetched schema without running the CLI.\n */\nexport function schemaFieldType(field: SchemaField): unknown {\n\tswitch (fieldTypeKind(field)) {\n\t\tcase \"string\":\n\t\t\treturn String;\n\t\tcase \"number\":\n\t\t\treturn Number;\n\t\tcase \"boolean\":\n\t\t\treturn Boolean;\n\t\tcase \"string-array\":\n\t\t\treturn [String] as const;\n\t\tcase \"relation\":\n\t\t\treturn String;\n\t\tcase \"relation-many\":\n\t\t\treturn [String] as const;\n\t\tcase \"json\":\n\t\t\treturn Object;\n\t\tcase \"password\":\n\t\t\treturn undefined;\n\t\tcase \"unknown\":\n\t\t\treturn undefined;\n\t}\n}\n","// ── Codegen ─────────────────────────────────────────────\n// Generates a `lazypock.types.ts` module from the live API schema.\n//\n// The generated file exports:\n// - One interface per collection (e.g. `PostsRecord`)\n// - A `LazypockCollections` map: { posts: PostsRecord; users: UsersRecord; ... }\n// - A `createClient()` factory pre-bound to those types, so\n// `client.collection(\"posts\").create({ title: \"x\" })` is fully type-checked.\n//\n// The CLI in `src/cli.ts` wires this to `GET /collections`.\n\nimport type { CollectionSchema, SchemaField } from \"./schema\";\nimport { fieldTypeScriptType } from \"./typegen\";\n\n/** Format a raw collection name into a valid TS identifier (PascalCase). */\nexport function collectionTypeName(name: string): string {\n\treturn name\n\t\t.split(/[^a-zA-Z0-9]+/)\n\t\t.filter(Boolean)\n\t\t.map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n\t\t.join(\"\")\n\t\t.replace(/^[0-9]/, \"_$&\");\n}\n\n/** Interface member name — sanitize hyphens/spaces but keep readability. */\nexport function fieldKey(name: string): string {\n\treturn name.replace(/[^a-zA-Z0-9_]/g, \"_\");\n}\n\n\n\n/**\n * Generate the full TypeScript source for the typed SDK module.\n *\n * @param collections Collections fetched from the API.\n * @param options Generation options.\n */\nexport function generateTypes(\n\tcollections: CollectionSchema[],\n\toptions: {\n\t\t/** Import specifier for the lazypock package (default `lazypock`). */\n\t\tpackageName?: string;\n\t\t/** Emit base record fields (id, created, updated, …). Default true. */\n\t\tincludeBaseFields?: boolean;\n\t\t/** Skip system collections (names starting with `_` or `users`). Default false. */\n\t\tskipSystem?: boolean;\n\t} = {},\n): string {\n\tconst {\n\t\tpackageName = \"lazypock\",\n\t\tincludeBaseFields = true,\n\t\tskipSystem = false,\n\t} = options;\n\n\tconst filtered = skipSystem\n\t\t? collections.filter(\n\t\t\t\t(c) => !c.system && !c.name.startsWith(\"_\") && c.name !== \"users\",\n\t\t\t)\n\t\t: collections;\n\n\tconst sections: string[] = [];\n\tsections.push(`// ── Auto-generated by lazypock-ts ──────────────────────────\n// Do not edit by hand. Regenerate with: npx lazypock-gen\n// Schema snapshot: ${new Date().toISOString()}`);\n\n\tif (includeBaseFields) {\n\t\tsections.push(`export interface BaseRecord {\n id: string;\n collectionId: string;\n collectionName: string;\n created: string;\n updated: string;\n}`);\n\t}\n\n\t// One interface per collection\n\tfor (const coll of filtered) {\n\t\tconst typeName = collectionTypeName(coll.name);\n\t\tconst fields = coll.fields ?? [];\n\t\tconst lines = fields.map((f) => memberLine(f)).filter((l) => l !== \"\");\n\t\tconst body = lines.join(\"\\n\");\n\t\tsections.push(\n\t\t\t`export interface ${typeName}Record${renderInterface({\n\t\t\t\textends: includeBaseFields ? \"BaseRecord\" : undefined,\n\t\t\t\tbody,\n\t\t\t})}`,\n\t\t);\n\n\t\t// Write-only create data: what `create()`/`update()` accept. This\n\t\t// intentionally includes password fields (and hidden fields) that are\n\t\t// excluded from the read model — the server accepts them on write,\n\t\t// hashes passwords, and never returns them in responses.\n\t\tconst createLines: string[] = [];\n\t\tconst seenKeys = new Set<string>();\n\t\tfor (const f of fields) {\n\t\t\tfor (const { key, line } of createDataMemberLines(f)) {\n\t\t\t\tif (seenKeys.has(key)) continue;\n\t\t\t\tseenKeys.add(key);\n\t\t\t\tcreateLines.push(line);\n\t\t\t}\n\t\t}\n\t\tsections.push(\n\t\t\t`export interface ${typeName}CreateData${renderInterface({\n\t\t\t\tbody: createLines.join(\"\\n\"),\n\t\t\t})}`,\n\t\t);\n\n\t\t// Query fields — the key set for filter/sort/expand/select. This\n\t\t// intentionally includes hidden fields: a hidden relation is still\n\t\t// expandable/filterable at runtime (the server resolves it) even\n\t\t// though it is excluded from the read model. Only password fields\n\t\t// are never queryable.\n\t\tconst queryLines: string[] = [];\n\t\tfor (const f of fields) {\n\t\t\tif (f.type === \"password\") continue;\n\t\t\tconst key = fieldKey(f.name);\n\t\t\tconst type = fieldTypeScriptType(f);\n\t\t\tif (type === \"never\") continue;\n\t\t\tqueryLines.push(` ${JSON.stringify(key)}?: ${type};`);\n\t\t}\n\t\tsections.push(\n\t\t\t`export interface ${typeName}QueryFields${renderInterface({\n\t\t\t\textends: includeBaseFields ? \"BaseRecord\" : undefined,\n\t\t\t\tbody: queryLines.join(\"\\n\"),\n\t\t\t})}`,\n\t\t);\n\t}\n\n\t// Auth collection type\n\tsections.push(`export interface AuthRecord extends BaseRecord {\n email: string;\n verified: boolean;\n}`);\n\n\t// Collections map\n\tconst mapEntries = filtered\n\t\t.map(\n\t\t\t(c) =>\n\t\t\t\t` \"${c.name}\": ${collectionTypeName(c.name)}Record${\n\t\t\t\t\tc.type === \"auth\" ? \" & AuthRecord\" : \"\"\n\t\t\t\t};`,\n\t\t)\n\t\t.join(\"\\n\");\n\tsections.push(`export interface LazypockCollections {\n${mapEntries}\n}`);\n\n\t// Create-data map — typed `create()`/`update()` payloads, including\n\t// write-only password fields.\n\tconst createMapEntries = filtered\n\t\t.map((c) => ` \"${c.name}\": ${collectionTypeName(c.name)}CreateData;`)\n\t\t.join(\"\\n\");\n\tsections.push(`export interface LazypockCreateData {\n${createMapEntries}\n}`);\n\n\t// Query-fields map — filter/sort/expand/select keys per collection,\n\t// including hidden fields (valid at runtime despite being excluded from\n\t// the read model). Auth collections get the auth system keys too.\n\tconst queryMapEntries = filtered\n\t\t.map(\n\t\t\t(c) =>\n\t\t\t\t` \"${c.name}\": ${collectionTypeName(c.name)}QueryFields${\n\t\t\t\t\tc.type === \"auth\" ? \" & AuthRecord\" : \"\"\n\t\t\t\t};`,\n\t\t)\n\t\t.join(\"\\n\");\n\tsections.push(`export interface LazypockQueryFields {\n${queryMapEntries}\n}`);\n\n\t// Runtime schema snapshot — lets the client exclude hidden fields by\n\t// default, validate select/expand, and derive visible field lists.\n\tconst schemaEntries = collections\n\t\t.map(\n\t\t\t(c) =>\n\t\t\t\t` {\n id: ${JSON.stringify(c.id ?? null)},\n name: ${JSON.stringify(c.name)},\n type: ${JSON.stringify(c.type)},\n system: ${JSON.stringify(c.system ?? false)},\n fields: ${JSON.stringify(c.fields ?? [], null, 2)},\n rules: ${JSON.stringify(c.rules ?? {})},\n options: ${JSON.stringify(c.options ?? {})},\n }`,\n\t\t)\n\t\t.join(\",\\n\");\n\tsections.push(`// Schema snapshot (runtime) — hidden-field exclusion + query validation.\nexport const lazypockSchema: import(\"${packageName}\").CollectionSchema[] = [\n${schemaEntries}\n];`);\n\n\t// createClient factory\n\tsections.push(`import { LazypockClient, type LazypockClientOptions, type CollectionService } from \"${packageName}\";\n\n/**\n * Create a Lazypock client typed against this schema snapshot.\n * Collection access is fully type-checked:\n * client.collection(\"posts\").create({ title: \"x\" }) // title must exist\n *\n * For auth collections the generated *CreateData type includes the\n * write-only password field, so creating a user is type-safe:\n * client.collection(\"users\").create({ email, password }) // ✓\n *\n * The schema snapshot is wired into the client automatically, so hidden\n * fields are excluded from responses and select/expand are validated.\n */\nexport function createClient(options: LazypockClientOptions): TypedClient {\n return new TypedClient({\n ...options,\n types: {\n ...options.types,\n schemas: options.types?.schemas ?? lazypockSchema,\n },\n });\n}\n\nexport class TypedClient extends LazypockClient {\n // The constraint is \"keyof LazypockCollections | (string & {})\" rather than a\n // bare generic string or a strict keyof:\n // - the keyof LazypockCollections member is what makes the IDE suggest\n // collection names, and known literals resolve to the typed service:\n // client.collection(\"posts\") // suggested + typed\n // - the string & {} member keeps unknown/dynamic names compiling — they\n // resolve to the untyped service, so a studio that manages\n // user-created collections can still call collection(someString).\n //\n // The (string & {}) intersection keeps the union from collapsing to plain\n // string (which would silently kill the suggestions).\n //\n // The service binds three types: the read model, the write-only *CreateData,\n // and the *QueryFields (filter/sort/expand/select keys). QueryFields is what\n // lets hidden relation fields — excluded from the read model but still\n // expandable/filterable at runtime — typecheck:\n // client.collection(\"project_members\").getFullList({ expand: \"user\" }) // ✓\n override collection<K extends keyof LazypockCollections | (string & {})>(\n name: K,\n ): K extends keyof LazypockCollections\n ? CollectionService<\n LazypockCollections[K],\n LazypockCreateData[K],\n LazypockQueryFields[K]\n >\n : CollectionService<unknown> {\n return super.collection(name) as K extends keyof LazypockCollections\n ? CollectionService<\n LazypockCollections[K],\n LazypockCreateData[K],\n LazypockQueryFields[K]\n >\n : CollectionService<unknown>;\n }\n}\n`);\n\n\treturn sections.join(\"\\n\\n\") + \"\\n\";\n}\n\n/**\n * Render the interface body including the extends clause.\n * Empty body → ` extends BaseRecord {}` (valid TS).\n */\nfunction renderInterface(opts: { extends?: string; body: string }): string {\n\tconst ext = opts.extends ? ` extends ${opts.extends}` : \"\";\n\tif (!opts.body) return `${ext} {}`;\n\treturn `${ext} {\\n${opts.body}\\n}`;\n}\n\n/** Render a single interface member line for a field. */\nfunction memberLine(f: SchemaField): string {\n\t// Hidden fields are excluded from API responses by the schema-aware\n\t// client default — don't expose them on the read model either.\n\tif (f.hidden) return \"\";\n\tconst key = fieldKey(f.name);\n\tconst req = f.required || f.type === \"password\" ? \"\" : \"?\";\n\tconst type = fieldTypeScriptType(f);\n\t// `never` members (passwords) are omitted from read models.\n\tif (type === \"never\") return \"\";\n\treturn ` ${JSON.stringify(key)}${req}: ${type};`;\n}\n\n/**\n * Render the write-only create-data members for a field.\n *\n * Unlike the read model, create/update data includes password fields (sent as\n * plain strings — the server hashes them) and hidden fields: the server\n * accepts them on write and never returns them in responses.\n *\n * Password fields are exposed under the canonical PocketBase API key\n * `password` (the server accepts `password` for any single password field and\n * hashes it into the backing column, e.g. `password_hash`), with the raw\n * metadata name kept as a backward-compat alias.\n *\n * A field is only marked required when it actually needs a client value:\n * fields with a server-side default (`options.defaultValue`) and the auth\n * system fields `verified` / `emailVisibility` (the server defaults them to\n * false/true even though the snapshot doesn't carry `defaultValue`) stay\n * optional, so `create({ email, password })` typechecks without forcing\n * callers to send values the server would fill in anyway. Password fields\n * are optional (accounts may exist without a password, e.g. OAuth-only).\n */\nfunction createDataMemberLines(\n\tf: SchemaField,\n): { key: string; line: string }[] {\n\t// Autodate columns are server-managed; never writable.\n\tif (f.type === \"autodate\") return [];\n\tconst rawName = fieldKey(f.name);\n\tconst serverDefaulted =\n\t\tf.options?.defaultValue !== undefined ||\n\t\t(f.system && (f.name === \"verified\" || f.name === \"emailVisibility\"));\n\tconst req = f.required && !serverDefaulted ? \"\" : \"?\";\n\tconst type = f.type === \"password\" ? \"string\" : fieldTypeScriptType(f);\n\tif (type === \"never\") return [];\n\n\tconst members = [\n\t\t{ key: rawName, line: ` ${JSON.stringify(rawName)}${req}: ${type};` },\n\t];\n\t// Write-only password: canonical key is `password` (PocketBase parity) —\n\t// the server accepts `password` for any single password field and hashes\n\t// it. The raw metadata name (e.g. `password_hash`) stays as a compat alias.\n\tif (f.type === \"password\" && rawName !== \"password\") {\n\t\tmembers.unshift({\n\t\t\tkey: \"password\",\n\t\t\tline: ` ${JSON.stringify(\"password\")}${req}: ${type};`,\n\t\t});\n\t}\n\treturn members;\n}\n"],"mappings":"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","names":[]}