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/README.md +117 -127
- package/dist/{chunk-LVEOMG6A.js → chunk-HA22OVUR.js} +51 -9
- package/dist/chunk-HA22OVUR.js.map +1 -0
- package/dist/cli.cjs +49 -8
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.global.js +49 -8
- package/dist/cli.global.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +50 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -14
- package/dist/index.d.ts +35 -14
- package/dist/index.global.js +50 -8
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/codegen.ts +98 -9
- package/src/collection.ts +22 -13
- package/src/typegen.ts +2 -0
- package/src/types.ts +31 -7
- package/dist/chunk-LVEOMG6A.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -101,8 +101,14 @@ declare class ApiError extends Error {
|
|
|
101
101
|
* untyped services accept any field name.
|
|
102
102
|
*/
|
|
103
103
|
type FieldKey<T> = Extract<keyof T, string> extends never ? string : Extract<keyof T, string>;
|
|
104
|
-
/**
|
|
105
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Valid filter operators, matching the backend FilterCompiler.
|
|
106
|
+
*
|
|
107
|
+
* The `?`-prefixed operators are PocketBase's "any/at least one of"
|
|
108
|
+
* conditions over array-valued fields (multi-select, multiple relation,
|
|
109
|
+
* multiple file). They compile to `= ANY` / `ILIKE ANY` SQL in the backend.
|
|
110
|
+
*/
|
|
111
|
+
type FilterOp = "=" | "!=" | "~" | "!~" | ">" | ">=" | "<" | "<=" | "?=" | "?!=" | "?~" | "?!~" | "?>" | "?>=" | "?<" | "?<=";
|
|
106
112
|
/** Optional whitespace around a filter operator (`project=x` and `project = x` both compile). */
|
|
107
113
|
type FilterWs = "" | " ";
|
|
108
114
|
/** Trim one-or-more leading/trailing spaces off a string literal. */
|
|
@@ -231,18 +237,29 @@ type FilterString<T, F extends string = never> = F & (F extends ValidFilter<T, F
|
|
|
231
237
|
type TopExpandKey<S extends string> = S extends `${infer A}.${string}` ? A : S;
|
|
232
238
|
/** Top-level keys of a comma-separated expand literal (`"a,b"` → `"a" | "b"`). */
|
|
233
239
|
type ExpandKeys<S extends string> = S extends "" ? never : S extends `${infer H},${infer T}` ? TopExpandKey<H> | ExpandKeys<T> : TopExpandKey<S>;
|
|
240
|
+
/**
|
|
241
|
+
* Value type of one expanded relation: the target record from the expand
|
|
242
|
+
* map (`TExpand`, e.g. the codegen-generated `PostsExpandMap`) when the key
|
|
243
|
+
* is a known relation, `unknown` otherwise.
|
|
244
|
+
*/
|
|
245
|
+
type ExpandValue<K extends string, TExpand> = K extends keyof TExpand ? TExpand[K] : unknown;
|
|
234
246
|
/**
|
|
235
247
|
* The `expand` property attached to records when `expand` is used. Keys are
|
|
236
248
|
* parsed from the caller's expand literal (dot-paths collapse to their first
|
|
237
|
-
* segment)
|
|
238
|
-
*
|
|
239
|
-
* `
|
|
249
|
+
* segment) and each value is typed from `TExpand` — a per-collection map of
|
|
250
|
+
* relation field → target record type (the codegen CLI emits these), so
|
|
251
|
+
* `record.expand.user` is `UsersRecord`, not `unknown`.
|
|
252
|
+
*
|
|
253
|
+
* Falls back to `Record<string, unknown>` when the expand value is dynamic
|
|
254
|
+
* or absent, or when no expand map is bound (hand-written services).
|
|
240
255
|
*/
|
|
241
|
-
type ExpandObj<E extends string
|
|
256
|
+
type ExpandObj<E extends string, TExpand = Record<string, unknown>> = [
|
|
242
257
|
E
|
|
243
258
|
] extends [never] ? Record<string, unknown> : [
|
|
244
259
|
string
|
|
245
|
-
] extends [E] ? Record<string, unknown> : Partial<
|
|
260
|
+
] extends [E] ? Record<string, unknown> : Partial<{
|
|
261
|
+
[K in ExpandKeys<E>]: ExpandValue<K, TExpand>;
|
|
262
|
+
}>;
|
|
246
263
|
/**
|
|
247
264
|
* Query options for list/read operations, typed against a record shape `T`.
|
|
248
265
|
*
|
|
@@ -640,8 +657,12 @@ interface RealtimeSubscribeOptions {
|
|
|
640
657
|
* Defaults to `T`. The codegen client binds a *QueryFields type that includes
|
|
641
658
|
* hidden fields — excluded from the read model but still expandable/filterable
|
|
642
659
|
* at runtime — so `expand: "hiddenRelation"` typechecks.
|
|
660
|
+
* @typeParam TExpand — Map of relation field → target record type. Defaults to
|
|
661
|
+
* `Record<string, unknown>`. The codegen client binds a per-collection
|
|
662
|
+
* *ExpandMap, so `record.expand.user` is typed as the target `UsersRecord`
|
|
663
|
+
* instead of `unknown`.
|
|
643
664
|
*/
|
|
644
|
-
declare class CollectionService<T = ApiRecord, TData = never, TFields = T
|
|
665
|
+
declare class CollectionService<T = ApiRecord, TData = never, TFields = T, TExpand = Record<string, unknown>> {
|
|
645
666
|
private http;
|
|
646
667
|
private collectionName;
|
|
647
668
|
private authStore?;
|
|
@@ -680,7 +701,7 @@ declare class CollectionService<T = ApiRecord, TData = never, TFields = T> {
|
|
|
680
701
|
* `fields` param — the server returns every non-password field.
|
|
681
702
|
* Pass `select("*")` to restore the default after a projection.
|
|
682
703
|
*/
|
|
683
|
-
select<K extends FieldKey<TFields> | "*">(...fields: K[]): CollectionService<T, TData, TFields>;
|
|
704
|
+
select<K extends FieldKey<TFields> | "*">(...fields: K[]): CollectionService<T, TData, TFields, TExpand>;
|
|
684
705
|
/**
|
|
685
706
|
* Fields to send with reads:
|
|
686
707
|
* 1. explicit `options.fields` (caller wins)
|
|
@@ -714,7 +735,7 @@ declare class CollectionService<T = ApiRecord, TData = never, TFields = T> {
|
|
|
714
735
|
* @param options Query params (`filter`, `sort`, `expand`, `fields`) + request options.
|
|
715
736
|
*/
|
|
716
737
|
getList<T2 = T, E extends string = never, S extends string = never, F extends string = never>(page?: number, perPage?: number, options?: ListOptions<TFields, E, S, F> & RequestOptions): Promise<ListResult<T2 & {
|
|
717
|
-
expand?: ExpandObj<E>;
|
|
738
|
+
expand?: ExpandObj<E, TExpand>;
|
|
718
739
|
}> | null>;
|
|
719
740
|
/**
|
|
720
741
|
* Fetch all records at once (auto-paginates). Mirrors PocketBase's
|
|
@@ -723,7 +744,7 @@ declare class CollectionService<T = ApiRecord, TData = never, TFields = T> {
|
|
|
723
744
|
* @param options Query params (`sort`, `filter`, `batch`, etc.) + request options.
|
|
724
745
|
*/
|
|
725
746
|
getFullList<T2 = T, E extends string = never, S extends string = never, F extends string = never>(options?: ListOptions<TFields, E, S, F> & RequestOptions): Promise<Array<T2 & {
|
|
726
|
-
expand?: ExpandObj<E>;
|
|
747
|
+
expand?: ExpandObj<E, TExpand>;
|
|
727
748
|
}>>;
|
|
728
749
|
/**
|
|
729
750
|
* Fetch the first record matching a filter (PocketBase `getFirstListItem`).
|
|
@@ -732,7 +753,7 @@ declare class CollectionService<T = ApiRecord, TData = never, TFields = T> {
|
|
|
732
753
|
* @param options Optional request options.
|
|
733
754
|
*/
|
|
734
755
|
getFirstListItem<T2 = T, F extends string = never, E extends string = never, S extends string = never>(filter: FilterString<TFields, F>, options?: ListOptions<TFields, E, S, F> & RequestOptions): Promise<(T2 & {
|
|
735
|
-
expand?: ExpandObj<E>;
|
|
756
|
+
expand?: ExpandObj<E, TExpand>;
|
|
736
757
|
}) | null>;
|
|
737
758
|
/**
|
|
738
759
|
* Get a single record by ID.
|
|
@@ -740,7 +761,7 @@ declare class CollectionService<T = ApiRecord, TData = never, TFields = T> {
|
|
|
740
761
|
* @param options Optional request options.
|
|
741
762
|
*/
|
|
742
763
|
getOne<E extends string = never>(id: string, options?: ReadOptions<TFields, E> & RequestOptions): Promise<(T & {
|
|
743
|
-
expand?: ExpandObj<E>;
|
|
764
|
+
expand?: ExpandObj<E, TExpand>;
|
|
744
765
|
}) | null>;
|
|
745
766
|
/**
|
|
746
767
|
* Create a new record.
|
|
@@ -794,7 +815,7 @@ declare class CollectionService<T = ApiRecord, TData = never, TFields = T> {
|
|
|
794
815
|
* `types.schemas`). Enables schema-aware defaults: hidden fields are
|
|
795
816
|
* excluded from responses, select/expand are validated.
|
|
796
817
|
*/
|
|
797
|
-
withSchema(schema: CollectionSchema): CollectionService<T, TData, TFields>;
|
|
818
|
+
withSchema(schema: CollectionSchema): CollectionService<T, TData, TFields, TExpand>;
|
|
798
819
|
/**
|
|
799
820
|
* Subscribe to realtime changes for this collection.
|
|
800
821
|
* The event's `action` is one of `"create" | "update" | "delete"` and the
|
package/dist/index.d.ts
CHANGED
|
@@ -101,8 +101,14 @@ declare class ApiError extends Error {
|
|
|
101
101
|
* untyped services accept any field name.
|
|
102
102
|
*/
|
|
103
103
|
type FieldKey<T> = Extract<keyof T, string> extends never ? string : Extract<keyof T, string>;
|
|
104
|
-
/**
|
|
105
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Valid filter operators, matching the backend FilterCompiler.
|
|
106
|
+
*
|
|
107
|
+
* The `?`-prefixed operators are PocketBase's "any/at least one of"
|
|
108
|
+
* conditions over array-valued fields (multi-select, multiple relation,
|
|
109
|
+
* multiple file). They compile to `= ANY` / `ILIKE ANY` SQL in the backend.
|
|
110
|
+
*/
|
|
111
|
+
type FilterOp = "=" | "!=" | "~" | "!~" | ">" | ">=" | "<" | "<=" | "?=" | "?!=" | "?~" | "?!~" | "?>" | "?>=" | "?<" | "?<=";
|
|
106
112
|
/** Optional whitespace around a filter operator (`project=x` and `project = x` both compile). */
|
|
107
113
|
type FilterWs = "" | " ";
|
|
108
114
|
/** Trim one-or-more leading/trailing spaces off a string literal. */
|
|
@@ -231,18 +237,29 @@ type FilterString<T, F extends string = never> = F & (F extends ValidFilter<T, F
|
|
|
231
237
|
type TopExpandKey<S extends string> = S extends `${infer A}.${string}` ? A : S;
|
|
232
238
|
/** Top-level keys of a comma-separated expand literal (`"a,b"` → `"a" | "b"`). */
|
|
233
239
|
type ExpandKeys<S extends string> = S extends "" ? never : S extends `${infer H},${infer T}` ? TopExpandKey<H> | ExpandKeys<T> : TopExpandKey<S>;
|
|
240
|
+
/**
|
|
241
|
+
* Value type of one expanded relation: the target record from the expand
|
|
242
|
+
* map (`TExpand`, e.g. the codegen-generated `PostsExpandMap`) when the key
|
|
243
|
+
* is a known relation, `unknown` otherwise.
|
|
244
|
+
*/
|
|
245
|
+
type ExpandValue<K extends string, TExpand> = K extends keyof TExpand ? TExpand[K] : unknown;
|
|
234
246
|
/**
|
|
235
247
|
* The `expand` property attached to records when `expand` is used. Keys are
|
|
236
248
|
* parsed from the caller's expand literal (dot-paths collapse to their first
|
|
237
|
-
* segment)
|
|
238
|
-
*
|
|
239
|
-
* `
|
|
249
|
+
* segment) and each value is typed from `TExpand` — a per-collection map of
|
|
250
|
+
* relation field → target record type (the codegen CLI emits these), so
|
|
251
|
+
* `record.expand.user` is `UsersRecord`, not `unknown`.
|
|
252
|
+
*
|
|
253
|
+
* Falls back to `Record<string, unknown>` when the expand value is dynamic
|
|
254
|
+
* or absent, or when no expand map is bound (hand-written services).
|
|
240
255
|
*/
|
|
241
|
-
type ExpandObj<E extends string
|
|
256
|
+
type ExpandObj<E extends string, TExpand = Record<string, unknown>> = [
|
|
242
257
|
E
|
|
243
258
|
] extends [never] ? Record<string, unknown> : [
|
|
244
259
|
string
|
|
245
|
-
] extends [E] ? Record<string, unknown> : Partial<
|
|
260
|
+
] extends [E] ? Record<string, unknown> : Partial<{
|
|
261
|
+
[K in ExpandKeys<E>]: ExpandValue<K, TExpand>;
|
|
262
|
+
}>;
|
|
246
263
|
/**
|
|
247
264
|
* Query options for list/read operations, typed against a record shape `T`.
|
|
248
265
|
*
|
|
@@ -640,8 +657,12 @@ interface RealtimeSubscribeOptions {
|
|
|
640
657
|
* Defaults to `T`. The codegen client binds a *QueryFields type that includes
|
|
641
658
|
* hidden fields — excluded from the read model but still expandable/filterable
|
|
642
659
|
* at runtime — so `expand: "hiddenRelation"` typechecks.
|
|
660
|
+
* @typeParam TExpand — Map of relation field → target record type. Defaults to
|
|
661
|
+
* `Record<string, unknown>`. The codegen client binds a per-collection
|
|
662
|
+
* *ExpandMap, so `record.expand.user` is typed as the target `UsersRecord`
|
|
663
|
+
* instead of `unknown`.
|
|
643
664
|
*/
|
|
644
|
-
declare class CollectionService<T = ApiRecord, TData = never, TFields = T
|
|
665
|
+
declare class CollectionService<T = ApiRecord, TData = never, TFields = T, TExpand = Record<string, unknown>> {
|
|
645
666
|
private http;
|
|
646
667
|
private collectionName;
|
|
647
668
|
private authStore?;
|
|
@@ -680,7 +701,7 @@ declare class CollectionService<T = ApiRecord, TData = never, TFields = T> {
|
|
|
680
701
|
* `fields` param — the server returns every non-password field.
|
|
681
702
|
* Pass `select("*")` to restore the default after a projection.
|
|
682
703
|
*/
|
|
683
|
-
select<K extends FieldKey<TFields> | "*">(...fields: K[]): CollectionService<T, TData, TFields>;
|
|
704
|
+
select<K extends FieldKey<TFields> | "*">(...fields: K[]): CollectionService<T, TData, TFields, TExpand>;
|
|
684
705
|
/**
|
|
685
706
|
* Fields to send with reads:
|
|
686
707
|
* 1. explicit `options.fields` (caller wins)
|
|
@@ -714,7 +735,7 @@ declare class CollectionService<T = ApiRecord, TData = never, TFields = T> {
|
|
|
714
735
|
* @param options Query params (`filter`, `sort`, `expand`, `fields`) + request options.
|
|
715
736
|
*/
|
|
716
737
|
getList<T2 = T, E extends string = never, S extends string = never, F extends string = never>(page?: number, perPage?: number, options?: ListOptions<TFields, E, S, F> & RequestOptions): Promise<ListResult<T2 & {
|
|
717
|
-
expand?: ExpandObj<E>;
|
|
738
|
+
expand?: ExpandObj<E, TExpand>;
|
|
718
739
|
}> | null>;
|
|
719
740
|
/**
|
|
720
741
|
* Fetch all records at once (auto-paginates). Mirrors PocketBase's
|
|
@@ -723,7 +744,7 @@ declare class CollectionService<T = ApiRecord, TData = never, TFields = T> {
|
|
|
723
744
|
* @param options Query params (`sort`, `filter`, `batch`, etc.) + request options.
|
|
724
745
|
*/
|
|
725
746
|
getFullList<T2 = T, E extends string = never, S extends string = never, F extends string = never>(options?: ListOptions<TFields, E, S, F> & RequestOptions): Promise<Array<T2 & {
|
|
726
|
-
expand?: ExpandObj<E>;
|
|
747
|
+
expand?: ExpandObj<E, TExpand>;
|
|
727
748
|
}>>;
|
|
728
749
|
/**
|
|
729
750
|
* Fetch the first record matching a filter (PocketBase `getFirstListItem`).
|
|
@@ -732,7 +753,7 @@ declare class CollectionService<T = ApiRecord, TData = never, TFields = T> {
|
|
|
732
753
|
* @param options Optional request options.
|
|
733
754
|
*/
|
|
734
755
|
getFirstListItem<T2 = T, F extends string = never, E extends string = never, S extends string = never>(filter: FilterString<TFields, F>, options?: ListOptions<TFields, E, S, F> & RequestOptions): Promise<(T2 & {
|
|
735
|
-
expand?: ExpandObj<E>;
|
|
756
|
+
expand?: ExpandObj<E, TExpand>;
|
|
736
757
|
}) | null>;
|
|
737
758
|
/**
|
|
738
759
|
* Get a single record by ID.
|
|
@@ -740,7 +761,7 @@ declare class CollectionService<T = ApiRecord, TData = never, TFields = T> {
|
|
|
740
761
|
* @param options Optional request options.
|
|
741
762
|
*/
|
|
742
763
|
getOne<E extends string = never>(id: string, options?: ReadOptions<TFields, E> & RequestOptions): Promise<(T & {
|
|
743
|
-
expand?: ExpandObj<E>;
|
|
764
|
+
expand?: ExpandObj<E, TExpand>;
|
|
744
765
|
}) | null>;
|
|
745
766
|
/**
|
|
746
767
|
* Create a new record.
|
|
@@ -794,7 +815,7 @@ declare class CollectionService<T = ApiRecord, TData = never, TFields = T> {
|
|
|
794
815
|
* `types.schemas`). Enables schema-aware defaults: hidden fields are
|
|
795
816
|
* excluded from responses, select/expand are validated.
|
|
796
817
|
*/
|
|
797
|
-
withSchema(schema: CollectionSchema): CollectionService<T, TData, TFields>;
|
|
818
|
+
withSchema(schema: CollectionSchema): CollectionService<T, TData, TFields, TExpand>;
|
|
798
819
|
/**
|
|
799
820
|
* Subscribe to realtime changes for this collection.
|
|
800
821
|
* The event's `action` is one of `"create" | "update" | "delete"` and the
|
package/dist/index.global.js
CHANGED
|
@@ -1437,6 +1437,7 @@ var Lazypock = (() => {
|
|
|
1437
1437
|
case "editor":
|
|
1438
1438
|
case "date":
|
|
1439
1439
|
case "datetime":
|
|
1440
|
+
case "autodate":
|
|
1440
1441
|
return "string";
|
|
1441
1442
|
case "number":
|
|
1442
1443
|
return "number";
|
|
@@ -1480,6 +1481,7 @@ var Lazypock = (() => {
|
|
|
1480
1481
|
case "editor":
|
|
1481
1482
|
case "date":
|
|
1482
1483
|
case "datetime":
|
|
1484
|
+
case "autodate":
|
|
1483
1485
|
case "select":
|
|
1484
1486
|
case "file":
|
|
1485
1487
|
return "string";
|
|
@@ -1525,6 +1527,13 @@ var Lazypock = (() => {
|
|
|
1525
1527
|
}
|
|
1526
1528
|
|
|
1527
1529
|
// src/codegen.ts
|
|
1530
|
+
var BASE_RECORD_KEYS = /* @__PURE__ */ new Set([
|
|
1531
|
+
"id",
|
|
1532
|
+
"collectionId",
|
|
1533
|
+
"collectionName",
|
|
1534
|
+
"created",
|
|
1535
|
+
"updated"
|
|
1536
|
+
]);
|
|
1528
1537
|
function collectionTypeName(name) {
|
|
1529
1538
|
return name.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("").replace(/^[0-9]/, "_$&");
|
|
1530
1539
|
}
|
|
@@ -1553,10 +1562,13 @@ var Lazypock = (() => {
|
|
|
1553
1562
|
updated: string;
|
|
1554
1563
|
}`);
|
|
1555
1564
|
}
|
|
1565
|
+
const collById = new Map(
|
|
1566
|
+
filtered.filter((c) => c.id !== void 0 && c.id !== "").map((c) => [String(c.id), c])
|
|
1567
|
+
);
|
|
1556
1568
|
for (const coll of filtered) {
|
|
1557
1569
|
const typeName = collectionTypeName(coll.name);
|
|
1558
1570
|
const fields = coll.fields ?? [];
|
|
1559
|
-
const lines = fields.map((f) => memberLine(f)).filter((l) => l !== "");
|
|
1571
|
+
const lines = fields.map((f) => memberLine(f, includeBaseFields)).filter((l) => l !== "");
|
|
1560
1572
|
const body = lines.join("\n");
|
|
1561
1573
|
sections.push(
|
|
1562
1574
|
`export interface ${typeName}Record${renderInterface({
|
|
@@ -1582,6 +1594,7 @@ var Lazypock = (() => {
|
|
|
1582
1594
|
for (const f of fields) {
|
|
1583
1595
|
if (f.type === "password") continue;
|
|
1584
1596
|
const key = fieldKey(f.name);
|
|
1597
|
+
if (includeBaseFields && BASE_RECORD_KEYS.has(key)) continue;
|
|
1585
1598
|
const type = fieldTypeScriptType(f);
|
|
1586
1599
|
if (type === "never") continue;
|
|
1587
1600
|
queryLines.push(` ${JSON.stringify(key)}?: ${type};`);
|
|
@@ -1592,6 +1605,24 @@ var Lazypock = (() => {
|
|
|
1592
1605
|
body: queryLines.join("\n")
|
|
1593
1606
|
})}`
|
|
1594
1607
|
);
|
|
1608
|
+
const expandLines = [];
|
|
1609
|
+
for (const f of fields) {
|
|
1610
|
+
if (f.type !== "relation") continue;
|
|
1611
|
+
const target = collById.get(String(f.options?.collectionId ?? "")) ?? filtered.find(
|
|
1612
|
+
(c) => c.name === String(f.options?.collection ?? "")
|
|
1613
|
+
);
|
|
1614
|
+
if (!target) continue;
|
|
1615
|
+
const targetType = `${collectionTypeName(target.name)}Record${target.type === "auth" ? " & AuthRecord" : ""}`;
|
|
1616
|
+
const many = (f.options?.maxSelect ?? 1) > 1;
|
|
1617
|
+
expandLines.push(
|
|
1618
|
+
` ${JSON.stringify(fieldKey(f.name))}: ${many ? `Array<${targetType}>` : targetType};`
|
|
1619
|
+
);
|
|
1620
|
+
}
|
|
1621
|
+
sections.push(
|
|
1622
|
+
`export interface ${typeName}ExpandMap${renderInterface({
|
|
1623
|
+
body: expandLines.join("\n")
|
|
1624
|
+
})}`
|
|
1625
|
+
);
|
|
1595
1626
|
}
|
|
1596
1627
|
sections.push(`export interface AuthRecord extends BaseRecord {
|
|
1597
1628
|
email: string;
|
|
@@ -1612,6 +1643,10 @@ ${createMapEntries}
|
|
|
1612
1643
|
).join("\n");
|
|
1613
1644
|
sections.push(`export interface LazypockQueryFields {
|
|
1614
1645
|
${queryMapEntries}
|
|
1646
|
+
}`);
|
|
1647
|
+
const expandMapEntries = filtered.map((c) => ` "${c.name}": ${collectionTypeName(c.name)}ExpandMap;`).join("\n");
|
|
1648
|
+
sections.push(`export interface LazypockExpandMaps {
|
|
1649
|
+
${expandMapEntries}
|
|
1615
1650
|
}`);
|
|
1616
1651
|
const schemaEntries = collections.map(
|
|
1617
1652
|
(c) => ` {
|
|
@@ -1665,25 +1700,31 @@ export class TypedClient extends LazypockClient {
|
|
|
1665
1700
|
// The (string & {}) intersection keeps the union from collapsing to plain
|
|
1666
1701
|
// string (which would silently kill the suggestions).
|
|
1667
1702
|
//
|
|
1668
|
-
// The service binds
|
|
1669
|
-
//
|
|
1670
|
-
//
|
|
1671
|
-
//
|
|
1703
|
+
// The service binds four types: the read model, the write-only *CreateData,
|
|
1704
|
+
// the *QueryFields (filter/sort/expand/select keys), and the *ExpandMap
|
|
1705
|
+
// (relation field \u2192 target record type). QueryFields is what lets hidden
|
|
1706
|
+
// relation fields \u2014 excluded from the read model but still expandable/
|
|
1707
|
+
// filterable at runtime \u2014 typecheck:
|
|
1672
1708
|
// client.collection("project_members").getFullList({ expand: "user" }) // \u2713
|
|
1709
|
+
// ExpandMap is what types the expanded data on the result:
|
|
1710
|
+
// const [rec] = await client.collection("posts").getFullList({ expand: "user" });
|
|
1711
|
+
// rec.expand?.user // \u2713 UsersRecord, not unknown
|
|
1673
1712
|
override collection<K extends keyof LazypockCollections | (string & {})>(
|
|
1674
1713
|
name: K,
|
|
1675
1714
|
): K extends keyof LazypockCollections
|
|
1676
1715
|
? CollectionService<
|
|
1677
1716
|
LazypockCollections[K],
|
|
1678
1717
|
LazypockCreateData[K],
|
|
1679
|
-
LazypockQueryFields[K]
|
|
1718
|
+
LazypockQueryFields[K],
|
|
1719
|
+
LazypockExpandMaps[K]
|
|
1680
1720
|
>
|
|
1681
1721
|
: CollectionService<unknown> {
|
|
1682
1722
|
return super.collection(name) as K extends keyof LazypockCollections
|
|
1683
1723
|
? CollectionService<
|
|
1684
1724
|
LazypockCollections[K],
|
|
1685
1725
|
LazypockCreateData[K],
|
|
1686
|
-
LazypockQueryFields[K]
|
|
1726
|
+
LazypockQueryFields[K],
|
|
1727
|
+
LazypockExpandMaps[K]
|
|
1687
1728
|
>
|
|
1688
1729
|
: CollectionService<unknown>;
|
|
1689
1730
|
}
|
|
@@ -1698,9 +1739,10 @@ export class TypedClient extends LazypockClient {
|
|
|
1698
1739
|
${opts.body}
|
|
1699
1740
|
}`;
|
|
1700
1741
|
}
|
|
1701
|
-
function memberLine(f) {
|
|
1742
|
+
function memberLine(f, skipBaseKeys = false) {
|
|
1702
1743
|
if (f.hidden) return "";
|
|
1703
1744
|
const key = fieldKey(f.name);
|
|
1745
|
+
if (skipBaseKeys && BASE_RECORD_KEYS.has(key)) return "";
|
|
1704
1746
|
const req = f.required || f.type === "password" ? "" : "?";
|
|
1705
1747
|
const type = fieldTypeScriptType(f);
|
|
1706
1748
|
if (type === "never") return "";
|