kysely-hydrate 0.4.0 → 0.4.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.mts +1 -22
- package/dist/index.mjs +55 -113
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -127,20 +127,6 @@ type HydratorArg<Input, Output> = MappedHydrator<Input, Output> | HydratorFactor
|
|
|
127
127
|
* The input type is automatically prefixed based on the parent's prefix.
|
|
128
128
|
*/
|
|
129
129
|
type ChildHydratorArg<P extends string, ParentInput, ChildOutput> = MappedHydrator<SelectAndStripPrefix<P, ParentInput>, ChildOutput> | ((create: CreateHydratorFn<SelectAndStripPrefix<P, ParentInput>>) => MappedHydrator<SelectAndStripPrefix<P, ParentInput>, ChildOutput>);
|
|
130
|
-
/**
|
|
131
|
-
* Ensures fields are included in the Hydrator's output if they don't already
|
|
132
|
-
* have an explicit configuration (including explicit omission).
|
|
133
|
-
*
|
|
134
|
-
* This is used internally by `HydratedQueryBuilder` to auto-detect selected
|
|
135
|
-
* fields without overwriting explicit field mappings or omissions from
|
|
136
|
-
* `mapFields()` and `omit()`.
|
|
137
|
-
*
|
|
138
|
-
* @param hydrator - The Hydrator instance
|
|
139
|
-
* @param fieldNames - Field names to ensure are included
|
|
140
|
-
* @returns A new Hydrator with the fields ensured
|
|
141
|
-
* @internal
|
|
142
|
-
*/
|
|
143
|
-
|
|
144
130
|
declare const IsFullHydrator: unique symbol;
|
|
145
131
|
/**
|
|
146
132
|
* A configuration for how to hydrate flat database rows into a denormalized structure.
|
|
@@ -1042,13 +1028,6 @@ declare class UnsupportedTableAliasNodeTypeError extends KyselyHydrateError {
|
|
|
1042
1028
|
declare class UnsupportedNodeTypeError extends KyselyHydrateError {
|
|
1043
1029
|
constructor(kind: string);
|
|
1044
1030
|
}
|
|
1045
|
-
/**
|
|
1046
|
-
* Error thrown when attempting to call a private method on an instance that
|
|
1047
|
-
* was not properly registered with the private accessor.
|
|
1048
|
-
*/
|
|
1049
|
-
declare class InvalidInstanceError extends KyselyHydrateError {
|
|
1050
|
-
constructor();
|
|
1051
|
-
}
|
|
1052
1031
|
/**
|
|
1053
1032
|
* Error thrown when attempting to extend a Hydrator with another Hydrator
|
|
1054
1033
|
* that has a different keyBy configuration.
|
|
@@ -1057,4 +1036,4 @@ declare class KeyByMismatchError extends KyselyHydrateError {
|
|
|
1057
1036
|
constructor(thisKeyBy: string, otherKeyBy: string);
|
|
1058
1037
|
}
|
|
1059
1038
|
//#endregion
|
|
1060
|
-
export { AmbiguousColumnReferenceError, ExpectedOneItemError, HydratePlugin, type Hydrator,
|
|
1039
|
+
export { AmbiguousColumnReferenceError, ExpectedOneItemError, HydratePlugin, type Hydrator, KeyByMismatchError, KyselyHydrateError, UnexpectedCaseError, UnexpectedComplexAliasError, UnexpectedSelectAllError, UnexpectedSelectionTypeError, UnsupportedAliasNodeTypeError, UnsupportedNodeTypeError, UnsupportedTableAliasNodeTypeError, WildcardSelectionError, createHydrator, hydrate, hydrateData, isFullHydrator, map };
|
package/dist/index.mjs
CHANGED
|
@@ -70,15 +70,6 @@ var UnsupportedNodeTypeError = class extends KyselyHydrateError {
|
|
|
70
70
|
}
|
|
71
71
|
};
|
|
72
72
|
/**
|
|
73
|
-
* Error thrown when attempting to call a private method on an instance that
|
|
74
|
-
* was not properly registered with the private accessor.
|
|
75
|
-
*/
|
|
76
|
-
var InvalidInstanceError = class extends KyselyHydrateError {
|
|
77
|
-
constructor() {
|
|
78
|
-
super("Invalid instance - private method not registered");
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
/**
|
|
82
73
|
* Error thrown when attempting to extend a Hydrator with another Hydrator
|
|
83
74
|
* that has a different keyBy configuration.
|
|
84
75
|
*/
|
|
@@ -103,12 +94,6 @@ function makePrefix(prefix, key) {
|
|
|
103
94
|
return `${prefix}${key}${SEP}`;
|
|
104
95
|
}
|
|
105
96
|
/**
|
|
106
|
-
* Indicates whether a string has any prefixes from `makePrefix`applied to it.
|
|
107
|
-
*/
|
|
108
|
-
function hasAnyPrefix(string) {
|
|
109
|
-
return string.includes(SEP);
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
97
|
* Applies a prefix to a key.
|
|
113
98
|
*
|
|
114
99
|
* ```ts
|
|
@@ -171,49 +156,6 @@ function addObjectToMap(map$1, obj) {
|
|
|
171
156
|
}
|
|
172
157
|
return clone;
|
|
173
158
|
}
|
|
174
|
-
/**
|
|
175
|
-
* Creates a pair of functions for accessing private instance methods from outside the class.
|
|
176
|
-
* Uses a WeakMap to store bound methods, ensuring proper memory cleanup.
|
|
177
|
-
*
|
|
178
|
-
* @returns An object with `call` and `register` functions
|
|
179
|
-
* - call: Calls the private method on an instance
|
|
180
|
-
* - register: Registers a private method for an instance (call in constructor)
|
|
181
|
-
*
|
|
182
|
-
* @example
|
|
183
|
-
* ```ts
|
|
184
|
-
* const privateAccessor = createPrivateAccessor<
|
|
185
|
-
* MyClass,
|
|
186
|
-
* [arg1: string, arg2: number],
|
|
187
|
-
* ReturnType
|
|
188
|
-
* >();
|
|
189
|
-
*
|
|
190
|
-
* class MyClass {
|
|
191
|
-
* constructor() {
|
|
192
|
-
* privateAccessor.register(this, this.#privateMethod.bind(this));
|
|
193
|
-
* }
|
|
194
|
-
* #privateMethod(arg1: string, arg2: number): ReturnType { ... }
|
|
195
|
-
* }
|
|
196
|
-
*
|
|
197
|
-
* // External usage with proper JSDoc:
|
|
198
|
-
* \/**
|
|
199
|
-
* * Documentation here
|
|
200
|
-
* *\/
|
|
201
|
-
* export const callPrivateMethod = privateAccessor.call;
|
|
202
|
-
* ```
|
|
203
|
-
*/
|
|
204
|
-
function createPrivateAccessor() {
|
|
205
|
-
const registry = /* @__PURE__ */ new WeakMap();
|
|
206
|
-
return {
|
|
207
|
-
register: (instance, method) => {
|
|
208
|
-
registry.set(instance, method);
|
|
209
|
-
},
|
|
210
|
-
call: (instance, ...args) => {
|
|
211
|
-
const method = registry.get(instance);
|
|
212
|
-
if (!method) throw new InvalidInstanceError();
|
|
213
|
-
return method(...args);
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
159
|
|
|
218
160
|
//#endregion
|
|
219
161
|
//#region src/hydrator.ts
|
|
@@ -222,24 +164,6 @@ function createPrivateAccessor() {
|
|
|
222
164
|
* Only used when the input type has an "id" property.
|
|
223
165
|
*/
|
|
224
166
|
const DEFAULT_KEY_BY = "id";
|
|
225
|
-
/**
|
|
226
|
-
* Private accessor for the ensureFields method.
|
|
227
|
-
*/
|
|
228
|
-
const ensureFieldsAccessor = createPrivateAccessor();
|
|
229
|
-
/**
|
|
230
|
-
* Ensures fields are included in the Hydrator's output if they don't already
|
|
231
|
-
* have an explicit configuration (including explicit omission).
|
|
232
|
-
*
|
|
233
|
-
* This is used internally by `HydratedQueryBuilder` to auto-detect selected
|
|
234
|
-
* fields without overwriting explicit field mappings or omissions from
|
|
235
|
-
* `mapFields()` and `omit()`.
|
|
236
|
-
*
|
|
237
|
-
* @param hydrator - The Hydrator instance
|
|
238
|
-
* @param fieldNames - Field names to ensure are included
|
|
239
|
-
* @returns A new Hydrator with the fields ensured
|
|
240
|
-
* @internal
|
|
241
|
-
*/
|
|
242
|
-
const ensureFields = ensureFieldsAccessor.call;
|
|
243
167
|
const IsFullHydrator = Symbol("HydratorType");
|
|
244
168
|
/**
|
|
245
169
|
* Determines if a hydrator is a full hydrator, meaning it has not had a .map()
|
|
@@ -261,13 +185,16 @@ const asFullHydrator = (hydrator) => {
|
|
|
261
185
|
throw new Error("Hydrator is not a full hydrator");
|
|
262
186
|
};
|
|
263
187
|
/**
|
|
188
|
+
* Special constant to enable auto-inclusion of fields at each level.
|
|
189
|
+
*/
|
|
190
|
+
const EnableAutoInclusion = Symbol();
|
|
191
|
+
/**
|
|
264
192
|
* Implements the entire inheritance chain of Hydrators.
|
|
265
193
|
*/
|
|
266
194
|
var HydratorImpl = class HydratorImpl {
|
|
267
195
|
#props;
|
|
268
196
|
constructor(props) {
|
|
269
197
|
this.#props = props;
|
|
270
|
-
ensureFieldsAccessor.register(this, this.#ensureFields.bind(this));
|
|
271
198
|
}
|
|
272
199
|
get [IsFullHydrator]() {
|
|
273
200
|
return !this.#props.mapFns?.length;
|
|
@@ -285,14 +212,6 @@ var HydratorImpl = class HydratorImpl {
|
|
|
285
212
|
fields: addObjectToMap(this.#props.fields, omitFields)
|
|
286
213
|
});
|
|
287
214
|
}
|
|
288
|
-
#ensureFields(fieldNames) {
|
|
289
|
-
const clone = new Map(this.#props.fields);
|
|
290
|
-
for (const name of fieldNames) if (!clone.has(name)) clone.set(name, true);
|
|
291
|
-
return new HydratorImpl({
|
|
292
|
-
...this.#props,
|
|
293
|
-
fields: clone
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
215
|
extras(extras) {
|
|
297
216
|
return new HydratorImpl({
|
|
298
217
|
...this.#props,
|
|
@@ -367,7 +286,7 @@ var HydratorImpl = class HydratorImpl {
|
|
|
367
286
|
*
|
|
368
287
|
* Writes directly to the provided attachedDataMap and fetchPromises array.
|
|
369
288
|
*/
|
|
370
|
-
#fetchAllAttachedCollections(prefix, inputs,
|
|
289
|
+
#fetchAllAttachedCollections(ctx, prefix, inputs, fetchPromises) {
|
|
371
290
|
const { attachedCollections, collections } = this.#props;
|
|
372
291
|
if (attachedCollections) {
|
|
373
292
|
let inputArray;
|
|
@@ -380,21 +299,45 @@ var HydratorImpl = class HydratorImpl {
|
|
|
380
299
|
const mapKey = prefix ? applyPrefix(prefix, key) : key;
|
|
381
300
|
fetchPromises.push(Promise.resolve(attachedCollection.fetchFn(inputArray)).then((attachedOutputs) => {
|
|
382
301
|
const grouped = groupByKey("", attachedOutputs, attachedCollection.matchChild);
|
|
383
|
-
attachedDataMap.set(mapKey, grouped);
|
|
302
|
+
ctx.attachedDataMap.set(mapKey, grouped);
|
|
384
303
|
}));
|
|
385
304
|
}
|
|
386
305
|
}
|
|
387
306
|
if (collections) for (const collection of collections.values()) {
|
|
388
307
|
const childPrefix = applyPrefix(prefix, collection.prefix);
|
|
389
|
-
collection.hydrator.#fetchAllAttachedCollections(childPrefix, inputs,
|
|
308
|
+
collection.hydrator.#fetchAllAttachedCollections(ctx, childPrefix, inputs, fetchPromises);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Gets all the fields belonging to the current prefix level; not to the
|
|
313
|
+
* parent, and not to any nested collection. Does this once per hydration
|
|
314
|
+
* (assumes all inputs have the same keys).
|
|
315
|
+
*/
|
|
316
|
+
#getAutoFields(ctx, prefix, input) {
|
|
317
|
+
const cached = ctx.autoFieldsCache.get(prefix);
|
|
318
|
+
if (cached) return cached;
|
|
319
|
+
if (typeof input !== "object" || input === null) return [];
|
|
320
|
+
const { fields, extras, collections } = this.#props;
|
|
321
|
+
const nestedPrefixes = [];
|
|
322
|
+
if (collections) for (const collection of collections.values()) nestedPrefixes.push(applyPrefix(prefix, collection.prefix));
|
|
323
|
+
const autoFields = [];
|
|
324
|
+
for (const inputKey of Object.keys(input)) {
|
|
325
|
+
if (!hasPrefix(prefix, inputKey)) continue;
|
|
326
|
+
if (nestedPrefixes.some((nestedPrefix) => hasPrefix(nestedPrefix, inputKey))) continue;
|
|
327
|
+
const unprefixedKey = removePrefix(prefix, inputKey);
|
|
328
|
+
if (fields?.has(unprefixedKey) || extras?.has(unprefixedKey)) continue;
|
|
329
|
+
autoFields.push(unprefixedKey);
|
|
390
330
|
}
|
|
331
|
+
ctx.autoFieldsCache.set(prefix, autoFields);
|
|
332
|
+
return autoFields;
|
|
391
333
|
}
|
|
392
334
|
/**
|
|
393
335
|
* Hydrates a single entity. All attach collections are already fetched and provided in attachedDataMap.
|
|
394
336
|
*/
|
|
395
|
-
#hydrateOne(
|
|
337
|
+
#hydrateOne(ctx, prefix, input, inputRows) {
|
|
396
338
|
const { fields, extras, collections, attachedCollections } = this.#props;
|
|
397
339
|
const entity = {};
|
|
340
|
+
if (ctx.autoIncludeFields) for (const key of this.#getAutoFields(ctx, prefix, input)) entity[key] = getPrefixedValue(prefix, input, key);
|
|
398
341
|
if (fields) for (const [key, field] of fields) {
|
|
399
342
|
if (field === false) continue;
|
|
400
343
|
const value = getPrefixedValue(prefix, input, key);
|
|
@@ -406,12 +349,12 @@ var HydratorImpl = class HydratorImpl {
|
|
|
406
349
|
}
|
|
407
350
|
if (collections) for (const [key, collection] of collections) {
|
|
408
351
|
const childPrefix = applyPrefix(prefix, collection.prefix);
|
|
409
|
-
entity[key] = applyCollectionMode(collection.hydrator.#hydrateMany(childPrefix, inputRows
|
|
352
|
+
entity[key] = applyCollectionMode(collection.hydrator.#hydrateMany(ctx, childPrefix, inputRows), collection.mode, key);
|
|
410
353
|
}
|
|
411
354
|
if (attachedCollections) for (const [key, collection] of attachedCollections) {
|
|
412
355
|
const inputKey = getKey(prefix, input, collection.toParent);
|
|
413
356
|
const mapKey = prefix ? applyPrefix(prefix, key) : key;
|
|
414
|
-
const attachedRows = attachedDataMap.get(mapKey)?.get(inputKey);
|
|
357
|
+
const attachedRows = ctx.attachedDataMap.get(mapKey)?.get(inputKey);
|
|
415
358
|
entity[key] = applyCollectionMode(attachedRows, collection.mode, key);
|
|
416
359
|
}
|
|
417
360
|
const { mapFns } = this.#props;
|
|
@@ -425,13 +368,13 @@ var HydratorImpl = class HydratorImpl {
|
|
|
425
368
|
/**
|
|
426
369
|
* Hydrates many entities. All attach collections are already fetched and provided in attachedDataMap.
|
|
427
370
|
*/
|
|
428
|
-
#hydrateMany(prefix, inputs
|
|
371
|
+
#hydrateMany(ctx, prefix, inputs) {
|
|
429
372
|
const { keyBy, collections } = this.#props;
|
|
430
373
|
const result = [];
|
|
431
374
|
if (!collections) {
|
|
432
375
|
for (const input of inputs) {
|
|
433
376
|
if (isKeyNil(getKey(prefix, input, keyBy))) continue;
|
|
434
|
-
const entity = this.#hydrateOne(
|
|
377
|
+
const entity = this.#hydrateOne(ctx, prefix, input, [input]);
|
|
435
378
|
result.push(entity);
|
|
436
379
|
}
|
|
437
380
|
return result;
|
|
@@ -439,18 +382,22 @@ var HydratorImpl = class HydratorImpl {
|
|
|
439
382
|
const grouped = groupByKey(prefix, inputs, keyBy);
|
|
440
383
|
for (const groupRows of grouped.values()) {
|
|
441
384
|
const firstRow = groupRows[0];
|
|
442
|
-
const entity = this.#hydrateOne(
|
|
385
|
+
const entity = this.#hydrateOne(ctx, prefix, firstRow, groupRows);
|
|
443
386
|
result.push(entity);
|
|
444
387
|
}
|
|
445
388
|
return result;
|
|
446
389
|
}
|
|
447
|
-
hydrate(input) {
|
|
448
|
-
const
|
|
390
|
+
hydrate(input, autoIncludeFields) {
|
|
391
|
+
const ctx = {
|
|
392
|
+
autoIncludeFields: autoIncludeFields === EnableAutoInclusion,
|
|
393
|
+
attachedDataMap: /* @__PURE__ */ new Map(),
|
|
394
|
+
autoFieldsCache: /* @__PURE__ */ new Map()
|
|
395
|
+
};
|
|
449
396
|
const fetchPromises = [];
|
|
450
|
-
this.#fetchAllAttachedCollections("", isIterable(input) ? input : [input],
|
|
397
|
+
this.#fetchAllAttachedCollections(ctx, "", isIterable(input) ? input : [input], fetchPromises);
|
|
451
398
|
const hydrateWithData = () => {
|
|
452
|
-
if (isIterable(input)) return this.#hydrateMany("", input
|
|
453
|
-
return this.#hydrateOne("",
|
|
399
|
+
if (isIterable(input)) return this.#hydrateMany(ctx, "", input);
|
|
400
|
+
return this.#hydrateOne(ctx, "", input, [input]);
|
|
454
401
|
};
|
|
455
402
|
return fetchPromises.length > 0 ? Promise.all(fetchPromises).then(hydrateWithData) : Promise.resolve(hydrateWithData());
|
|
456
403
|
}
|
|
@@ -647,23 +594,20 @@ var HydratedQueryBuilderImpl = class HydratedQueryBuilderImpl {
|
|
|
647
594
|
return new AliasedHydratedQueryBuilder(alias, this);
|
|
648
595
|
}
|
|
649
596
|
#hydrate(rows) {
|
|
650
|
-
|
|
651
|
-
const firstRow = isArray ? rows[0] : rows;
|
|
652
|
-
if (!firstRow) return isArray ? [] : void 0;
|
|
653
|
-
const fieldNames = Object.keys(firstRow).filter((key) => !hasAnyPrefix(key));
|
|
654
|
-
return ensureFields(this.#props.hydrator, fieldNames).hydrate(rows);
|
|
597
|
+
return this.#props.hydrator.hydrate(rows, EnableAutoInclusion);
|
|
655
598
|
}
|
|
656
599
|
async execute() {
|
|
657
600
|
const rows = await this.#props.qb.execute();
|
|
658
601
|
return this.#hydrate(rows);
|
|
659
602
|
}
|
|
660
603
|
async executeTakeFirst() {
|
|
661
|
-
const result = await this
|
|
662
|
-
return result
|
|
604
|
+
const [result] = await this.execute();
|
|
605
|
+
return result;
|
|
663
606
|
}
|
|
664
607
|
async executeTakeFirstOrThrow(errorConstructor = k.NoResultError) {
|
|
665
|
-
const result = await this
|
|
666
|
-
|
|
608
|
+
const result = await this.executeTakeFirst();
|
|
609
|
+
if (result === void 0) throw k.isNoResultErrorConstructor(errorConstructor) ? new errorConstructor(this.toOperationNode()) : errorConstructor(this.toOperationNode());
|
|
610
|
+
return result;
|
|
667
611
|
}
|
|
668
612
|
extras(extras) {
|
|
669
613
|
return new HydratedQueryBuilderImpl({
|
|
@@ -729,8 +673,7 @@ var HydratedQueryBuilderImpl = class HydratedQueryBuilderImpl {
|
|
|
729
673
|
const prefixedSelections = prefixSelectArg(this.#props.prefix, selection);
|
|
730
674
|
return new HydratedQueryBuilderImpl({
|
|
731
675
|
...this.#props,
|
|
732
|
-
qb: this.#props.qb.select(prefixedSelections)
|
|
733
|
-
hydrator: asFullHydrator(this.#props.hydrator).fields(prefixedSelections.map((selection$1) => selection$1.originalName))
|
|
676
|
+
qb: this.#props.qb.select(prefixedSelections)
|
|
734
677
|
});
|
|
735
678
|
}
|
|
736
679
|
#join(method, from, ...args) {
|
|
@@ -748,8 +691,7 @@ var HydratedQueryBuilderImpl = class HydratedQueryBuilderImpl {
|
|
|
748
691
|
let newQb = this.#props.qb;
|
|
749
692
|
newQb = newQb[method](aliasedQb, ...args);
|
|
750
693
|
newQb = newQb.select(hoistedSelections);
|
|
751
|
-
|
|
752
|
-
newHydrator = ensureFields(newHydrator, hoistedSelections.map((selection) => selection.originalName).filter((name) => !hasAnyPrefix(name)));
|
|
694
|
+
const newHydrator = asFullHydrator(this.#props.hydrator).extend(nestedHydrator);
|
|
753
695
|
return new HydratedQueryBuilderImpl({
|
|
754
696
|
...this.#props,
|
|
755
697
|
qb: newQb,
|
|
@@ -1175,4 +1117,4 @@ var HydratePlugin = class {
|
|
|
1175
1117
|
};
|
|
1176
1118
|
|
|
1177
1119
|
//#endregion
|
|
1178
|
-
export { AmbiguousColumnReferenceError, ExpectedOneItemError, HydratePlugin,
|
|
1120
|
+
export { AmbiguousColumnReferenceError, ExpectedOneItemError, HydratePlugin, KeyByMismatchError, KyselyHydrateError, UnexpectedCaseError, UnexpectedComplexAliasError, UnexpectedSelectAllError, UnexpectedSelectionTypeError, UnsupportedAliasNodeTypeError, UnsupportedNodeTypeError, UnsupportedTableAliasNodeTypeError, WildcardSelectionError, createHydrator, hydrate, hydrateData, isFullHydrator, map };
|