relation-matcher 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/src/index.d.ts +10 -5
- package/dist/src/index.js +35 -51
- package/dist/src/types/generic-bases.d.ts +2 -1
- package/dist/src/types/generic-less.d.ts +7 -3
- package/dist/src/types/utils.d.ts +3 -0
- package/dist/src/utils/invertInput.d.ts +2 -1
- package/dist/src/utils/isNullable.d.ts +3 -0
- package/dist/src/utils/isNullable.js +6 -0
- package/dist/src/utils/joins.d.ts +5 -0
- package/dist/src/utils/joins.js +9 -0
- package/dist/src/utils/matcher.d.ts +3 -3
- package/dist/src/utils/matcher.js +2 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/index.ts +69 -105
- package/src/types/generic-bases.ts +2 -1
- package/src/types/generic-less.ts +13 -8
- package/src/types/utils.ts +5 -0
- package/src/utils/invertInput.ts +3 -1
- package/src/utils/isNullable.ts +15 -0
- package/src/utils/joins.ts +22 -0
- package/src/utils/matcher.ts +12 -4
package/README.md
CHANGED
|
@@ -166,6 +166,14 @@ Record<
|
|
|
166
166
|
|
|
167
167
|
## Changelog
|
|
168
168
|
|
|
169
|
+
### 1.1.2
|
|
170
|
+
|
|
171
|
+
- Improved error when item is found to be nullable.
|
|
172
|
+
|
|
173
|
+
### 1.1.1
|
|
174
|
+
|
|
175
|
+
- Major refactor.
|
|
176
|
+
|
|
169
177
|
### 1.1.0
|
|
170
178
|
|
|
171
179
|
- Added ability to mark join as `isNullable: false`. This allows for manual marking of single joins as `NonNullable`.
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import type { MaybeArray, MaybeNull, SRecord } from "./types";
|
|
2
|
+
import type { TInputBase, TInputBaseJoin } from "./types/generic-bases";
|
|
3
|
+
import type { Output, OutputRoot } from "./types/generic-less";
|
|
4
|
+
import type { RelationMapRoot as Root } from "./types/inputs";
|
|
5
|
+
import type { RelationMapperReturnRoot as ReturnRoot } from "./types/return";
|
|
6
|
+
import { type InvertedInput } from "./utils/invertInput";
|
|
7
|
+
export declare const relationMatcherRoot: <TInput extends TInputBase, TOutputRoot extends Root<TInput>>(inputs: TInput[], output: TOutputRoot) => SRecord<ReturnRoot<TInput, TOutputRoot>>;
|
|
8
|
+
export declare const relationMatcher: <TInput extends TInputBase, TOutputRoot extends Root<TInput>>(inputs: TInput[], output: TOutputRoot) => SRecord<ReturnRoot<TInput, TOutputRoot>>;
|
|
6
9
|
export default relationMatcherRoot;
|
|
10
|
+
declare const joiner: (input: InvertedInput, output: Output | OutputRoot, joiningFrom: SRecord<unknown>) => SRecord<MaybeArray<MaybeNull<TInputBaseJoin>>>;
|
|
11
|
+
export { joiner as relationMatcherJoiner };
|
package/dist/src/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import invertInput, {} from "./utils/invertInput";
|
|
2
|
+
import { assertIsNullable } from "./utils/isNullable";
|
|
3
|
+
import { arrayJoinBase, singleJoinBase } from "./utils/joins";
|
|
2
4
|
import { getJoinFinalKey } from "./utils/keys";
|
|
3
|
-
import {
|
|
5
|
+
import { createMatcherFunc } from "./utils/matcher";
|
|
4
6
|
export const relationMatcherRoot = (inputs, output) => {
|
|
5
7
|
if (!inputs.length)
|
|
6
8
|
return {};
|
|
@@ -8,79 +10,61 @@ export const relationMatcherRoot = (inputs, output) => {
|
|
|
8
10
|
throw new Error("Input must be an array.");
|
|
9
11
|
}
|
|
10
12
|
const invertedInput = invertInput(inputs);
|
|
11
|
-
const baseItems = invertedInput[output.base].reduce((final,
|
|
12
|
-
if (!
|
|
13
|
+
const baseItems = invertedInput[output.base].reduce((final, inputRow) => {
|
|
14
|
+
if (!inputRow) {
|
|
13
15
|
return final;
|
|
14
16
|
}
|
|
15
|
-
const relations =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (!item)
|
|
22
|
-
return final;
|
|
23
|
-
final[item[value.id]] = {
|
|
24
|
-
...item,
|
|
25
|
-
...relationMatcherJoiner(invertedInput, value, item),
|
|
26
|
-
};
|
|
27
|
-
return final;
|
|
28
|
-
}, {}));
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
const item = invertedInput[value.base].find(matcherFunc(row, value)) ?? null;
|
|
32
|
-
if (item) {
|
|
33
|
-
final[finalKey] = {
|
|
34
|
-
...item,
|
|
35
|
-
...relationMatcherJoiner(invertedInput, value, item),
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return final;
|
|
41
|
-
}, {});
|
|
42
|
-
final[row[output.id]] = { ...row, ...relations };
|
|
17
|
+
const relations = joiner(invertedInput, output, inputRow);
|
|
18
|
+
const rowId = inputRow[output.id];
|
|
19
|
+
final[rowId] = {
|
|
20
|
+
...inputRow,
|
|
21
|
+
...relations,
|
|
22
|
+
};
|
|
43
23
|
return final;
|
|
44
24
|
}, {});
|
|
45
25
|
return baseItems;
|
|
46
26
|
};
|
|
47
27
|
export const relationMatcher = relationMatcherRoot;
|
|
48
28
|
export default relationMatcherRoot;
|
|
49
|
-
const
|
|
29
|
+
const joiner = (input, output, joiningFrom) => {
|
|
50
30
|
return Object.entries(output).reduce((final, [key, value]) => {
|
|
51
|
-
if (typeof value
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
31
|
+
if (typeof value !== "object")
|
|
32
|
+
return final;
|
|
33
|
+
const finalKey = getJoinFinalKey(key);
|
|
34
|
+
const matcherFunc = createMatcherFunc(joiningFrom, value);
|
|
35
|
+
const joinType = input[value.base];
|
|
36
|
+
if (!joinType)
|
|
37
|
+
throw new Error(`Input is missing rows for ${value.base}.`);
|
|
38
|
+
switch (value.joinType) {
|
|
39
|
+
case "array": {
|
|
40
|
+
const baseObjArr = arrayJoinBase(joinType, matcherFunc);
|
|
58
41
|
final[finalKey] = Object.values(baseObjArr.reduce((final, item) => {
|
|
59
|
-
if (!item)
|
|
60
|
-
return final;
|
|
61
42
|
const propertyKey = item[value.id];
|
|
62
|
-
|
|
63
|
-
return final;
|
|
64
|
-
final[propertyKey] = {
|
|
43
|
+
final[propertyKey] ??= {
|
|
65
44
|
...item,
|
|
66
|
-
...
|
|
45
|
+
...joiner(input, value, item),
|
|
67
46
|
};
|
|
68
47
|
return final;
|
|
69
48
|
}, {}));
|
|
49
|
+
break;
|
|
70
50
|
}
|
|
71
|
-
|
|
72
|
-
const item =
|
|
73
|
-
|
|
74
|
-
throw new Error("Value should exist as output schema defines it as nullable. Instead found null in array");
|
|
75
|
-
}
|
|
51
|
+
case "single": {
|
|
52
|
+
const item = singleJoinBase(joinType, matcherFunc);
|
|
53
|
+
assertIsNullable(item, value.isNullable, output, value.base);
|
|
76
54
|
if (item) {
|
|
77
55
|
final[finalKey] = {
|
|
78
56
|
...item,
|
|
79
|
-
...
|
|
57
|
+
...joiner(input, value, item),
|
|
80
58
|
};
|
|
81
59
|
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
default: {
|
|
63
|
+
value.joinType;
|
|
64
|
+
throw new Error(`Unhandled join type. Relation matcher can not handle ${value.joinType}.`);
|
|
82
65
|
}
|
|
83
66
|
}
|
|
84
67
|
return final;
|
|
85
68
|
}, {});
|
|
86
69
|
};
|
|
70
|
+
export { joiner as relationMatcherJoiner };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RelationMapBase, RelationMapJoiner } from "./inputs";
|
|
2
|
-
export type
|
|
2
|
+
export type TInputBaseJoin = Record<string, unknown>;
|
|
3
|
+
export type TInputBase = Record<string, TInputBaseJoin | null>;
|
|
3
4
|
export type TJoinedFromBase = TInputBase[keyof TInputBase];
|
|
4
5
|
export type TOutputBase<TInput extends TInputBase, TJoinedFrom extends TJoinedFromBase> = RelationMapBase<TInput, TJoinedFrom> & RelationMapJoiner<TInput, TJoinedFrom>;
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
base: keyof TInputBase;
|
|
1
|
+
export type OutputBase<TJoinType extends "single" | "array" = "single" | "array"> = {
|
|
2
|
+
base: string;
|
|
4
3
|
id: string;
|
|
5
4
|
joinsTo: string;
|
|
6
5
|
joinsFrom: string;
|
|
7
6
|
joinType: TJoinType;
|
|
8
7
|
isNullable?: false;
|
|
9
8
|
};
|
|
9
|
+
export type OutputRoot = OutputJoins & {
|
|
10
|
+
base: string;
|
|
11
|
+
id: string;
|
|
12
|
+
};
|
|
13
|
+
export type Output<TJoinType extends "single" | "array" = "single" | "array"> = OutputJoins & OutputBase<TJoinType>;
|
|
10
14
|
export type OutputJoins = {
|
|
11
15
|
[k: `_${string}`]: Output;
|
|
12
16
|
};
|
|
@@ -11,3 +11,6 @@ export type DeepNonNullable<T extends object> = {
|
|
|
11
11
|
export type ArrayOrSingle<T, TType extends "single" | "array"> = TType extends "array" ? Array<T> : T;
|
|
12
12
|
export type IsNullIfNull<TIsNull extends object | null, TReturn extends object | never = never> = TIsNull extends infer T ? (T & TReturn) | null : TIsNull & TReturn;
|
|
13
13
|
export type ExtendsNull<T> = null extends T ? true : false;
|
|
14
|
+
export type MaybeNull<T> = T | null;
|
|
15
|
+
export type MaybeArray<T> = T | T[];
|
|
16
|
+
export type SRecord<T> = Record<string, T>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TInputBase } from "~/types/generic-bases";
|
|
2
|
-
export type
|
|
2
|
+
export type InvertedInputRow<TInput extends TInputBase = TInputBase> = TInput[keyof TInput][];
|
|
3
|
+
export type InvertedInput<TInput extends TInputBase = TInputBase> = Record<keyof TInput, InvertedInputRow<TInput>>;
|
|
3
4
|
declare const invertInput: <TInput extends TInputBase>(inputs: TInputBase[]) => InvertedInput<TInput>;
|
|
4
5
|
export default invertInput;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { Output, OutputRoot } from "~/types";
|
|
2
|
+
/** Asserts that if `isNullable` is false then item is not null. */
|
|
3
|
+
export declare const assertIsNullable: (item: Record<string, unknown> | null, isNullable: false | undefined, output: Output | OutputRoot, base: string) => void;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Asserts that if `isNullable` is false then item is not null. */
|
|
2
|
+
export const assertIsNullable = (item, isNullable, output, base) => {
|
|
3
|
+
if (isNullable === false && item === null) {
|
|
4
|
+
throw new Error(`Value should exist as output schema defines it as non-nullable. Instead found null in array. Found when joining ${base} onto ${output.base}.`);
|
|
5
|
+
}
|
|
6
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { OutputBase } from "~/types";
|
|
2
|
+
import type { InvertedInputRow } from "./invertInput";
|
|
3
|
+
import { type MatcherFunc } from "./matcher";
|
|
4
|
+
export declare const singleJoinBase: <TValue extends OutputBase<"single">>(input: InvertedInputRow, matcherFunc: MatcherFunc) => Record<string, unknown> | null;
|
|
5
|
+
export declare const arrayJoinBase: <TValue extends OutputBase<"single">>(input: InvertedInputRow, matcherFunc: MatcherFunc) => Record<string, unknown>[];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import {} from "./matcher";
|
|
2
|
+
export const singleJoinBase = (input, matcherFunc) => {
|
|
3
|
+
const item = input.find(matcherFunc) ?? null;
|
|
4
|
+
return item;
|
|
5
|
+
};
|
|
6
|
+
export const arrayJoinBase = (input, matcherFunc) => {
|
|
7
|
+
const item = input.filter(matcherFunc);
|
|
8
|
+
return item;
|
|
9
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TInputBase } from "~/types/generic-bases";
|
|
2
|
-
import type { InvertedInput } from "./invertInput";
|
|
1
|
+
import type { TInputBase, TInputBaseJoin } from "~/types/generic-bases";
|
|
3
2
|
import type { Output } from "~/types/generic-less";
|
|
4
|
-
export
|
|
3
|
+
export type MatcherFunc = (item: TInputBaseJoin | null) => item is TInputBaseJoin;
|
|
4
|
+
export declare const createMatcherFunc: <TInput extends TInputBase>(row: TInput[keyof TInput], value: Output) => MatcherFunc;
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const createMatcherFunc = (row, value) => (item) => !!item &&
|
|
2
|
+
row?.[value.joinsFrom] === item?.[value.joinsTo];
|