kysely-hydrate 0.7.2 → 0.8.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 +35 -6
- package/dist/{errors-EVqhxGJc.mjs → errors-CbAVHTlW.mjs} +2 -2
- package/dist/experimental.mjs +1 -1
- package/dist/index.d.mts +57 -6
- package/dist/index.mjs +17 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -132,6 +132,7 @@ type Result = Array<{
|
|
|
132
132
|
- [Inspect the SQL](#inspecting-the-sql)
|
|
133
133
|
- [Mapped properties with `.mapFields()`](#mapped-properties-with-mapfields)
|
|
134
134
|
- [Computed properties with `.extras()`](#computed-properties-with-extras)
|
|
135
|
+
- [Computed properties with `.extend()`](#computed-properties-with-extend)
|
|
135
136
|
- [Excluded properties with `.omit()`](#excluded-properties-with-omit)
|
|
136
137
|
- [Output transformations with `.map()`](#output-transformations-with-map)
|
|
137
138
|
- [Composable mappings with `.with()`](#composable-mappings-with-with)
|
|
@@ -146,7 +147,7 @@ type Result = Array<{
|
|
|
146
147
|
- [Output transformations with `.map()`](#output-transformations-with-map-1)
|
|
147
148
|
- [Attached collections with `.attach*()`](#attached-collections-with-attach)
|
|
148
149
|
- [Prefixed collections with `.has*()`](#prefixed-collections-with-has)
|
|
149
|
-
- [Composing hydrators with `.
|
|
150
|
+
- [Composing hydrators with `.with()`](#composing-hydrators-with-with)
|
|
150
151
|
- [FAQ](#faq)
|
|
151
152
|
|
|
152
153
|
## Installation
|
|
@@ -843,6 +844,34 @@ type Result = Array<{
|
|
|
843
844
|
}>;
|
|
844
845
|
```
|
|
845
846
|
|
|
847
|
+
### Computed properties with `.extend()`
|
|
848
|
+
|
|
849
|
+
Like `.extras()`, but takes a single function that returns an object. All
|
|
850
|
+
returned keys are merged into the output. This is useful when multiple computed
|
|
851
|
+
fields share intermediate work.
|
|
852
|
+
|
|
853
|
+
```ts
|
|
854
|
+
const users = await querySet(db)
|
|
855
|
+
.selectAs("user", db.selectFrom("users").select(["id", "firstName", "lastName", "birthDate"]))
|
|
856
|
+
.extend((row) => {
|
|
857
|
+
const names = [row.firstName, row.lastName];
|
|
858
|
+
return {
|
|
859
|
+
fullName: names.join(" "),
|
|
860
|
+
initials: names.map((n) => n[0]).join(""),
|
|
861
|
+
};
|
|
862
|
+
})
|
|
863
|
+
.execute();
|
|
864
|
+
// ⬇
|
|
865
|
+
type Result = Array<{
|
|
866
|
+
id: number;
|
|
867
|
+
firstName: string;
|
|
868
|
+
lastName: string;
|
|
869
|
+
birthDate: Date;
|
|
870
|
+
fullName: string;
|
|
871
|
+
initials: string;
|
|
872
|
+
}>;
|
|
873
|
+
```
|
|
874
|
+
|
|
846
875
|
### Excluded properties with `.omit()`
|
|
847
876
|
|
|
848
877
|
Remove fields from the final output. This is useful for removing intermediate
|
|
@@ -1355,7 +1384,7 @@ type Result = UserModel[];
|
|
|
1355
1384
|
|
|
1356
1385
|
As in query sets, `.map()` is a terminal operation—after calling it, you can
|
|
1357
1386
|
only call `.map()` again or `.hydrate()`. You cannot call configuration methods
|
|
1358
|
-
like `.fields()`, `.extras()`, `.has*()`, or `.
|
|
1387
|
+
like `.fields()`, `.extras()`, `.has*()`, or `.with()`.
|
|
1359
1388
|
|
|
1360
1389
|
```ts
|
|
1361
1390
|
const mapped = createHydrator<User>()
|
|
@@ -1368,7 +1397,7 @@ mapped.hydrate(rows);
|
|
|
1368
1397
|
|
|
1369
1398
|
// ❌ These don't compile:
|
|
1370
1399
|
mapped.fields({ ... }); // Error: Property 'fields' does not exist
|
|
1371
|
-
mapped.
|
|
1400
|
+
mapped.with(...); // Error: Property 'with' does not exist
|
|
1372
1401
|
```
|
|
1373
1402
|
|
|
1374
1403
|
### Attached collections with `.attach*()`
|
|
@@ -1423,7 +1452,7 @@ non-nullable column that was made nullable by a left join; or, (b) it's actually
|
|
|
1423
1452
|
nullable in the "posts" table. The query set API, on the other hand, _does_
|
|
1424
1453
|
know the difference, and so does not suffer from this problem.
|
|
1425
1454
|
|
|
1426
|
-
### Composing hydrators with `.
|
|
1455
|
+
### Composing hydrators with `.with()`
|
|
1427
1456
|
|
|
1428
1457
|
Merges two hydrators. The second hydrator's configuration takes precedence.
|
|
1429
1458
|
|
|
@@ -1431,7 +1460,7 @@ This is a good way to build small, reusable hydrators (for a "user preview", a
|
|
|
1431
1460
|
"user display name", etc.) and compose them.
|
|
1432
1461
|
|
|
1433
1462
|
> [!NOTE]
|
|
1434
|
-
> Hydrators must have the same `keyBy`. If they don't, `.
|
|
1463
|
+
> Hydrators must have the same `keyBy`. If they don't, `.with()` throws.
|
|
1435
1464
|
|
|
1436
1465
|
```ts
|
|
1437
1466
|
type UserRow = { id: number; username: string; email: string };
|
|
@@ -1442,7 +1471,7 @@ const withDisplayName = createHydrator<UserRow>().extras({
|
|
|
1442
1471
|
displayName: (u) => `${u.username} <${u.email}>`,
|
|
1443
1472
|
});
|
|
1444
1473
|
|
|
1445
|
-
const combined = base.
|
|
1474
|
+
const combined = base.with(withDisplayName);
|
|
1446
1475
|
// ⬇
|
|
1447
1476
|
type Result = Hydrator<UserRow, { id: number; username: string; displayName: string }>;
|
|
1448
1477
|
```
|
|
@@ -76,12 +76,12 @@ var UnsupportedNodeTypeError = class extends KyselyHydrateError {
|
|
|
76
76
|
}
|
|
77
77
|
};
|
|
78
78
|
/**
|
|
79
|
-
* Error thrown when
|
|
79
|
+
* Error thrown when composing a Hydrator with another Hydrator
|
|
80
80
|
* that has a different keyBy configuration.
|
|
81
81
|
*/
|
|
82
82
|
var KeyByMismatchError = class extends KyselyHydrateError {
|
|
83
83
|
constructor(thisKeyBy, otherKeyBy) {
|
|
84
|
-
super(`Cannot
|
|
84
|
+
super(`Cannot compose hydrators with different keyBy: ${thisKeyBy} vs ${otherKeyBy}`);
|
|
85
85
|
}
|
|
86
86
|
};
|
|
87
87
|
/**
|
package/dist/experimental.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as UnsupportedAliasNodeTypeError, f as UnsupportedNodeTypeError, m as WildcardSelectionError, p as UnsupportedTableAliasNodeTypeError, t as AmbiguousColumnReferenceError, u as UnexpectedSelectionTypeError } from "./errors-
|
|
1
|
+
import { d as UnsupportedAliasNodeTypeError, f as UnsupportedNodeTypeError, m as WildcardSelectionError, p as UnsupportedTableAliasNodeTypeError, t as AmbiguousColumnReferenceError, u as UnexpectedSelectionTypeError } from "./errors-CbAVHTlW.mjs";
|
|
2
2
|
import * as k from "kysely";
|
|
3
3
|
|
|
4
4
|
//#region src/experimental/mapped-expression.ts
|
package/dist/index.d.mts
CHANGED
|
@@ -101,6 +101,15 @@ type Extras<Input> = Record<string, (input: Input) => unknown>;
|
|
|
101
101
|
* Uses the return type of each extra function.
|
|
102
102
|
*/
|
|
103
103
|
type InferExtras<Input, E extends Extras<Input>> = { [K in keyof E]: ReturnType<E[K]> };
|
|
104
|
+
/**
|
|
105
|
+
* An extender function that receives the full input and returns an object
|
|
106
|
+
* of computed properties to merge into the output.
|
|
107
|
+
*/
|
|
108
|
+
type Extender<Input> = (input: Input) => Record<string, unknown>;
|
|
109
|
+
/**
|
|
110
|
+
* Infers the output type for an extender function.
|
|
111
|
+
*/
|
|
112
|
+
type InferExtender<Input, F extends Extender<Input>> = ReturnType<F>;
|
|
104
113
|
/**
|
|
105
114
|
* The mode of a collection.
|
|
106
115
|
*
|
|
@@ -298,18 +307,29 @@ interface FullHydrator<Input, Output> extends MappedHydrator<Input, Output> {
|
|
|
298
307
|
*/
|
|
299
308
|
extras<E extends Extras<Input>>(extras: E): FullHydrator<Input, Extend<Output, InferExtras<Input, E>>>;
|
|
300
309
|
/**
|
|
301
|
-
*
|
|
310
|
+
* Adds computed fields to the hydrated output by spreading the return value
|
|
311
|
+
* of a function. Unlike `.extras()` which defines one field at a time,
|
|
312
|
+
* `.extend()` calls a single function whose returned object is merged into
|
|
313
|
+
* the output.
|
|
314
|
+
*
|
|
315
|
+
* @param fn - A function that receives the input and returns an object of
|
|
316
|
+
* computed properties
|
|
317
|
+
* @returns A new Hydrator with the extender applied
|
|
318
|
+
*/
|
|
319
|
+
extend<F extends Extender<Input>>(fn: F): FullHydrator<Input, Extend<Output, InferExtender<Input, F>>>;
|
|
320
|
+
/**
|
|
321
|
+
* Composes this Hydrator with the configuration from another Hydrator. The
|
|
302
322
|
* other Hydrator's configuration takes precedence in case of conflicts.
|
|
303
323
|
*
|
|
304
324
|
* Both hydrators must have the same `keyBy`, and any overlapping fields
|
|
305
325
|
* between the two input types must have compatible types.
|
|
306
326
|
*
|
|
307
|
-
* @param other - The Hydrator to
|
|
327
|
+
* @param other - The Hydrator to compose with
|
|
308
328
|
* @returns A new Hydrator with merged configuration
|
|
309
329
|
* @throws {KeyByMismatchError} If the keyBy configurations don't match
|
|
310
330
|
*/
|
|
311
|
-
|
|
312
|
-
|
|
331
|
+
with<OtherInput extends Partial<Input>, OtherOutput>(other: FullHydrator<OtherInput, OtherOutput>): FullHydrator<Input & OtherInput, Extend<Output, OtherOutput>>;
|
|
332
|
+
with<OtherInput extends Partial<Input>, OtherOutput>(other: MappedHydrator<OtherInput, OtherOutput>): MappedHydrator<Input & OtherInput, Extend<Output, OtherOutput>>;
|
|
313
333
|
/**
|
|
314
334
|
* Configures a nested collection that exists in the same query result. The
|
|
315
335
|
* child data is expected to be prefixed in the input (e.g., `posts$$id`,
|
|
@@ -621,7 +641,7 @@ interface TWithBaseQuery<in out T extends TQuerySet, in out BaseQuery extends TQ
|
|
|
621
641
|
Collections: T["Collections"];
|
|
622
642
|
JoinedQuery: TJoinedQueryWithBaseQuery<T["BaseAlias"], T["JoinedQuery"], BaseQuery>;
|
|
623
643
|
OrderableColumns: T["OrderableColumns"] | (keyof BaseQuery["O"] & string);
|
|
624
|
-
HydratedOutput: Flatten<Omit<RawExtend<BaseQuery["O"], T["HydratedOutput"]>, T["OmittedKeys"]>>;
|
|
644
|
+
HydratedOutput: T["IsMapped"] extends true ? T["HydratedOutput"] : Flatten<Omit<RawExtend<BaseQuery["O"], T["HydratedOutput"]>, T["OmittedKeys"]>>;
|
|
625
645
|
OmittedKeys: T["OmittedKeys"];
|
|
626
646
|
}
|
|
627
647
|
interface TWithOutput<in out T extends TQuerySet, in out Output> {
|
|
@@ -1359,6 +1379,37 @@ interface QuerySet<in out T extends TQuerySet> extends MappedQuerySet<T> {
|
|
|
1359
1379
|
* @returns A new HydratedQueryBuilder with the extras applied.
|
|
1360
1380
|
*/
|
|
1361
1381
|
extras<E extends Extras<TInput<T>>>(extras: E): QuerySet<TWithExtendedOutput<T, InferExtras<TInput<T>, E>>>;
|
|
1382
|
+
/**
|
|
1383
|
+
* Adds computed fields to the hydrated output by spreading the return value
|
|
1384
|
+
* of a function. Unlike `.extras()` which defines one field at a time,
|
|
1385
|
+
* `.extend()` calls a single function whose returned object is merged into
|
|
1386
|
+
* the output.
|
|
1387
|
+
*
|
|
1388
|
+
* ### Examples
|
|
1389
|
+
*
|
|
1390
|
+
* ```ts
|
|
1391
|
+
* const users = await querySet(db)
|
|
1392
|
+
* .selectAs("users", (eb) => eb.selectFrom("users").select(["users.id", "users.firstName", "users.lastName"]))
|
|
1393
|
+
* .extend((row) => ({
|
|
1394
|
+
* fullName: `${row.firstName} ${row.lastName}`,
|
|
1395
|
+
* initials: `${row.firstName[0]}${row.lastName[0]}`,
|
|
1396
|
+
* }))
|
|
1397
|
+
* .execute();
|
|
1398
|
+
* // ⬇
|
|
1399
|
+
* type Result = Array<{
|
|
1400
|
+
* id: number;
|
|
1401
|
+
* firstName: string;
|
|
1402
|
+
* lastName: string;
|
|
1403
|
+
* fullName: string;
|
|
1404
|
+
* initials: string;
|
|
1405
|
+
* }>;
|
|
1406
|
+
* ```
|
|
1407
|
+
*
|
|
1408
|
+
* @param fn - A function that receives the row and returns an object of
|
|
1409
|
+
* computed properties.
|
|
1410
|
+
* @returns A new HydratedQueryBuilder with the extender applied.
|
|
1411
|
+
*/
|
|
1412
|
+
extend<F extends Extender<TInput<T>>>(fn: F): QuerySet<TWithExtendedOutput<T, InferExtender<TInput<T>, F>>>;
|
|
1362
1413
|
/**
|
|
1363
1414
|
* Transforms already-selected field values in the hydrated output. Fields
|
|
1364
1415
|
* not mentioned in the mappings will still be included as-is.
|
|
@@ -2581,7 +2632,7 @@ declare class UnsupportedNodeTypeError extends KyselyHydrateError {
|
|
|
2581
2632
|
constructor(kind: string);
|
|
2582
2633
|
}
|
|
2583
2634
|
/**
|
|
2584
|
-
* Error thrown when
|
|
2635
|
+
* Error thrown when composing a Hydrator with another Hydrator
|
|
2585
2636
|
* that has a different keyBy configuration.
|
|
2586
2637
|
*/
|
|
2587
2638
|
declare class KeyByMismatchError extends KyselyHydrateError {
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as KeyByMismatchError, c as UnexpectedComplexAliasError, d as UnsupportedAliasNodeTypeError, f as UnsupportedNodeTypeError, i as InvalidJoinedQuerySetError, l as UnexpectedSelectAllError, m as WildcardSelectionError, n as CardinalityViolationError, o as KyselyHydrateError, p as UnsupportedTableAliasNodeTypeError, r as ExpectedOneItemError, s as UnexpectedCaseError, t as AmbiguousColumnReferenceError, u as UnexpectedSelectionTypeError } from "./errors-
|
|
1
|
+
import { a as KeyByMismatchError, c as UnexpectedComplexAliasError, d as UnsupportedAliasNodeTypeError, f as UnsupportedNodeTypeError, i as InvalidJoinedQuerySetError, l as UnexpectedSelectAllError, m as WildcardSelectionError, n as CardinalityViolationError, o as KyselyHydrateError, p as UnsupportedTableAliasNodeTypeError, r as ExpectedOneItemError, s as UnexpectedCaseError, t as AmbiguousColumnReferenceError, u as UnexpectedSelectionTypeError } from "./errors-CbAVHTlW.mjs";
|
|
2
2
|
import * as k from "kysely";
|
|
3
3
|
|
|
4
4
|
//#region src/helpers/order-by.ts
|
|
@@ -264,7 +264,13 @@ var HydratorImpl = class HydratorImpl {
|
|
|
264
264
|
extras: addObjectToMap(this.#props.extras, extras)
|
|
265
265
|
});
|
|
266
266
|
}
|
|
267
|
-
extend(
|
|
267
|
+
extend(fn) {
|
|
268
|
+
return new HydratorImpl({
|
|
269
|
+
...this.#props,
|
|
270
|
+
extenders: [...this.#props.extenders ?? [], fn]
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
with(other) {
|
|
268
274
|
const otherImpl = other;
|
|
269
275
|
const thisKeyBy = JSON.stringify(this.#props.keyBy);
|
|
270
276
|
const otherKeyBy = JSON.stringify(otherImpl.#props.keyBy);
|
|
@@ -276,6 +282,7 @@ var HydratorImpl = class HydratorImpl {
|
|
|
276
282
|
keyBy: otherProps.keyBy,
|
|
277
283
|
fields: new Map([...ownProps.fields ?? [], ...otherProps.fields ?? []]),
|
|
278
284
|
extras: new Map([...ownProps.extras ?? [], ...otherProps.extras ?? []]),
|
|
285
|
+
extenders: [...ownProps.extenders ?? [], ...otherProps.extenders ?? []],
|
|
279
286
|
collections: mergedCollections,
|
|
280
287
|
attachedCollections: new Map([...ownProps.attachedCollections ?? [], ...otherProps.attachedCollections ?? []]),
|
|
281
288
|
mapFns: [...this.#props.mapFns ?? [], ...otherProps.mapFns ?? []],
|
|
@@ -412,7 +419,7 @@ var HydratorImpl = class HydratorImpl {
|
|
|
412
419
|
* Hydrates a single entity. All attach collections are already fetched and provided in attachedDataMap.
|
|
413
420
|
*/
|
|
414
421
|
#hydrateOne(ctx, prefix, input, inputRows) {
|
|
415
|
-
const { fields, extras, collections, attachedCollections } = this.#props;
|
|
422
|
+
const { fields, extras, extenders, collections, attachedCollections } = this.#props;
|
|
416
423
|
const entity = {};
|
|
417
424
|
if (ctx.autoIncludeFields) for (const key of this.#getAutoFields(ctx, prefix, input)) entity[key] = getPrefixedValue(prefix, input, key);
|
|
418
425
|
if (fields) for (const [key, field] of fields) {
|
|
@@ -420,9 +427,10 @@ var HydratorImpl = class HydratorImpl {
|
|
|
420
427
|
const value = getPrefixedValue(prefix, input, key);
|
|
421
428
|
entity[key] = field === true ? value : field(value);
|
|
422
429
|
}
|
|
423
|
-
if (extras) {
|
|
430
|
+
if (extras || extenders) {
|
|
424
431
|
const accessor = createdPrefixedAccessor(prefix, input);
|
|
425
|
-
for (const [key, extra] of extras) entity[key] = extra(accessor);
|
|
432
|
+
if (extras) for (const [key, extra] of extras) entity[key] = extra(accessor);
|
|
433
|
+
if (extenders) for (const extender of extenders) Object.assign(entity, extender(accessor));
|
|
426
434
|
}
|
|
427
435
|
if (collections) {
|
|
428
436
|
const childCtx = this.#props.hasMultipleManyCollections && !ctx.hasSiblingManyCollections ? {
|
|
@@ -878,6 +886,9 @@ var QuerySetImpl = class QuerySetImpl {
|
|
|
878
886
|
extras(extras) {
|
|
879
887
|
return this.#clone({ hydrator: asFullHydrator(this.#props.hydrator).extras(extras) });
|
|
880
888
|
}
|
|
889
|
+
extend(fn) {
|
|
890
|
+
return this.#clone({ hydrator: asFullHydrator(this.#props.hydrator).extend(fn) });
|
|
891
|
+
}
|
|
881
892
|
mapFields(mappings) {
|
|
882
893
|
return this.#clone({ hydrator: asFullHydrator(this.#props.hydrator).fields(mappings) });
|
|
883
894
|
}
|
|
@@ -885,7 +896,7 @@ var QuerySetImpl = class QuerySetImpl {
|
|
|
885
896
|
return this.#clone({ hydrator: asFullHydrator(this.#props.hydrator).omit(keys) });
|
|
886
897
|
}
|
|
887
898
|
with(hydrator) {
|
|
888
|
-
return this.#clone({ hydrator: asFullHydrator(this.#props.hydrator).
|
|
899
|
+
return this.#clone({ hydrator: asFullHydrator(this.#props.hydrator).with(hydrator) });
|
|
889
900
|
}
|
|
890
901
|
#addAttach(mode, key, fetchFn, keys) {
|
|
891
902
|
return this.#clone({ hydrator: asFullHydrator(this.#props.hydrator).attach(mode, key, fetchFn, keys) });
|