relation-matcher 1.0.13 → 1.1.1
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/.vscode/settings.json +4 -4
- package/.yarn/install-state.gz +0 -0
- package/.yarn/releases/yarn-4.13.0.cjs +940 -940
- package/.yarnrc.yml +3 -3
- package/README.md +185 -150
- package/dist/src/index.d.ts +10 -5
- package/dist/src/index.js +35 -46
- package/dist/src/types/generic-bases.d.ts +2 -1
- package/dist/src/types/generic-less.d.ts +8 -3
- package/dist/src/types/inputs.d.ts +14 -7
- package/dist/src/types/return.d.ts +5 -2
- package/dist/src/types/typetest.d.ts +1 -0
- package/dist/src/types/utils.d.ts +3 -0
- package/dist/src/utils/invertInput.d.ts +2 -1
- package/dist/src/utils/invertInput.js +4 -9
- package/dist/src/utils/isNullable.d.ts +2 -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/keys.js +6 -1
- 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 +36 -36
- package/src/index.test.ts +105 -105
- package/src/index.ts +124 -151
- package/src/testing/file-output.ts +15 -15
- package/src/testing/test-data.ts +172 -172
- package/src/types/generic-bases.ts +10 -9
- package/src/types/generic-less.ts +20 -14
- package/src/types/index.ts +5 -5
- package/src/types/inputs.ts +36 -30
- package/src/types/return.ts +65 -67
- package/src/types/typetest.ts +87 -84
- package/src/types/utils.ts +33 -28
- package/src/utils/invertInput.ts +27 -28
- package/src/utils/isNullable.ts +11 -0
- package/src/utils/joins.ts +22 -0
- package/src/utils/keys.ts +7 -1
- package/src/utils/matcher.ts +16 -8
package/.yarnrc.yml
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
nodeLinker: node-modules
|
|
2
|
-
|
|
3
|
-
yarnPath: .yarn/releases/yarn-4.13.0.cjs
|
|
1
|
+
nodeLinker: node-modules
|
|
2
|
+
|
|
3
|
+
yarnPath: .yarn/releases/yarn-4.13.0.cjs
|
package/README.md
CHANGED
|
@@ -1,150 +1,185 @@
|
|
|
1
|
-
# Relation Matcher
|
|
2
|
-
|
|
3
|
-
This is a utility to convert unstructured data from a database into structured relational json data.
|
|
4
|
-
|
|
5
|
-
## Contents
|
|
6
|
-
|
|
7
|
-
- [Installation](#installation)
|
|
8
|
-
- [Usage](#usage)
|
|
9
|
-
- [Data](#data)
|
|
10
|
-
- [Schema](#schema)
|
|
11
|
-
- [Output](#output)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
import db from "~/db/client";
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
1
|
+
# Relation Matcher
|
|
2
|
+
|
|
3
|
+
This is a utility to convert unstructured data from a database into structured relational json data.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Usage](#usage)
|
|
9
|
+
- [Data](#data)
|
|
10
|
+
- [Schema](#schema)
|
|
11
|
+
- [Output](#output)
|
|
12
|
+
- [Changelog](#changelog)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
| | Installation Command |
|
|
17
|
+
| ---- | --------------------------- |
|
|
18
|
+
| npm | `npm i relation-matcher` |
|
|
19
|
+
| yarn | `yarn add relation-matcher` |
|
|
20
|
+
| pnpm | `pnpm add relation-matcher` |
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
To use the relation matcher you feed in your `data` from your database and a `schema`. For example with drizzle:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// import db from "~/db/client";
|
|
28
|
+
// ...otherImports
|
|
29
|
+
import relationMatcher from "relation-matcher";
|
|
30
|
+
|
|
31
|
+
/* Get your data from the database */
|
|
32
|
+
const dbData = await db.select().fro...
|
|
33
|
+
|
|
34
|
+
const data /* Output */ = relationMatcher(
|
|
35
|
+
dbData /* Data */,
|
|
36
|
+
{...} /* Schema */
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
return data;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### `data`
|
|
43
|
+
|
|
44
|
+
The `data`'s shape should extend:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
Array<Record<string, Record<string, unknown> | null>>;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
#### Example
|
|
51
|
+
|
|
52
|
+
The data could have the following type:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
{
|
|
56
|
+
user: {
|
|
57
|
+
id: string;
|
|
58
|
+
email: string;
|
|
59
|
+
password: string;
|
|
60
|
+
...otherColumns
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
post: {
|
|
64
|
+
id: string;
|
|
65
|
+
title: string;
|
|
66
|
+
content: string;
|
|
67
|
+
|
|
68
|
+
author_id: string; // < Same as user.id
|
|
69
|
+
post_category_id: string; // < Same as post_category.id
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
post_category: {
|
|
73
|
+
id: string;
|
|
74
|
+
name: string;
|
|
75
|
+
} | null;
|
|
76
|
+
}[]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### `schema`
|
|
80
|
+
|
|
81
|
+
The `schema`'s shape should be:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
{
|
|
85
|
+
base: "name of table",
|
|
86
|
+
id: "distinct column in above table",
|
|
87
|
+
|
|
88
|
+
_yourJoinedTableName: {
|
|
89
|
+
base: "name of table to join",
|
|
90
|
+
id: "distinct column of above table",
|
|
91
|
+
joinsFrom: "column of parent table",
|
|
92
|
+
joinsTo: "column of current table",
|
|
93
|
+
joinType: "single" | "array",
|
|
94
|
+
isNullable?: false | undefined, // No-op on array join types.
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
> [!NOTE]
|
|
100
|
+
> Keys for joins start with an underscore; the final property key will have the leading underscore removed.
|
|
101
|
+
|
|
102
|
+
#### Example
|
|
103
|
+
|
|
104
|
+
With the above `data`, the `schema`'s shape could be:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
{
|
|
108
|
+
base: "user",
|
|
109
|
+
id: "id",
|
|
110
|
+
|
|
111
|
+
_posts: {
|
|
112
|
+
base: "post",
|
|
113
|
+
id: "id",
|
|
114
|
+
joinsFrom: "id",
|
|
115
|
+
joinsTo: "author_id",
|
|
116
|
+
joinType: "array",
|
|
117
|
+
|
|
118
|
+
_postCategory: {
|
|
119
|
+
base: "post_category",
|
|
120
|
+
id: "id",
|
|
121
|
+
joinsFrom: "post_category_id",
|
|
122
|
+
joinsTo: "id",
|
|
123
|
+
joinType: "single",
|
|
124
|
+
isNullable: false,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `output`
|
|
131
|
+
|
|
132
|
+
The outputted data will take the shape of your schema.
|
|
133
|
+
|
|
134
|
+
#### Example
|
|
135
|
+
|
|
136
|
+
For the `data` and `schema` provided above, the `output` will have the following type:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
Record<
|
|
140
|
+
string, // The id specified in the root of your schema.
|
|
141
|
+
{
|
|
142
|
+
id: string;
|
|
143
|
+
email: string;
|
|
144
|
+
password: string;
|
|
145
|
+
...otherColumns;
|
|
146
|
+
|
|
147
|
+
posts: Array<
|
|
148
|
+
{
|
|
149
|
+
id: string;
|
|
150
|
+
title: string;
|
|
151
|
+
content: string;
|
|
152
|
+
|
|
153
|
+
author_id: string;
|
|
154
|
+
post_category_id: string;
|
|
155
|
+
|
|
156
|
+
postCategory: {
|
|
157
|
+
id: string;
|
|
158
|
+
name: string;
|
|
159
|
+
} // | null;
|
|
160
|
+
// Note this is not nullable as the schema defined it as such.
|
|
161
|
+
}
|
|
162
|
+
>;
|
|
163
|
+
}
|
|
164
|
+
>;
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Changelog
|
|
168
|
+
|
|
169
|
+
### 1.1.1
|
|
170
|
+
|
|
171
|
+
- Major refactor.
|
|
172
|
+
|
|
173
|
+
### 1.1.0
|
|
174
|
+
|
|
175
|
+
- Added ability to mark join as `isNullable: false`. This allows for manual marking of single joins as `NonNullable`.
|
|
176
|
+
|
|
177
|
+
Will throw `Error` if value is not found.
|
|
178
|
+
|
|
179
|
+
### 1.0.13
|
|
180
|
+
|
|
181
|
+
- Added readme.
|
|
182
|
+
|
|
183
|
+
### 1.0.12
|
|
184
|
+
|
|
185
|
+
- Fixed type issue where return type would be repeated union of expected `output`.
|
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,74 +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
|
-
|
|
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);
|
|
56
41
|
final[finalKey] = Object.values(baseObjArr.reduce((final, item) => {
|
|
57
|
-
if (!item)
|
|
58
|
-
return final;
|
|
59
42
|
const propertyKey = item[value.id];
|
|
60
|
-
|
|
61
|
-
return final;
|
|
62
|
-
final[propertyKey] = {
|
|
43
|
+
final[propertyKey] ??= {
|
|
63
44
|
...item,
|
|
64
|
-
...
|
|
45
|
+
...joiner(input, value, item),
|
|
65
46
|
};
|
|
66
47
|
return final;
|
|
67
48
|
}, {}));
|
|
49
|
+
break;
|
|
68
50
|
}
|
|
69
|
-
|
|
70
|
-
const item =
|
|
51
|
+
case "single": {
|
|
52
|
+
const item = singleJoinBase(joinType, matcherFunc);
|
|
53
|
+
assertIsNullable(item, value.isNullable);
|
|
71
54
|
if (item) {
|
|
72
55
|
final[finalKey] = {
|
|
73
56
|
...item,
|
|
74
|
-
...
|
|
57
|
+
...joiner(input, value, item),
|
|
75
58
|
};
|
|
76
59
|
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
default: {
|
|
63
|
+
value.joinType;
|
|
64
|
+
throw new Error(`Unhandled join type. Relation matcher can not handle ${value.joinType}.`);
|
|
77
65
|
}
|
|
78
66
|
}
|
|
79
67
|
return final;
|
|
80
68
|
}, {});
|
|
81
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,11 +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;
|
|
7
|
+
isNullable?: false;
|
|
8
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>;
|
|
9
14
|
export type OutputJoins = {
|
|
10
15
|
[k: `_${string}`]: Output;
|
|
11
16
|
};
|
|
@@ -5,15 +5,22 @@ export type RelationMapRoot<TInput extends TInputBase> = {
|
|
|
5
5
|
id: keyof NonNullable<TInput[k]>;
|
|
6
6
|
} & RelationMapJoiner<TInput, NonNullable<TInput[k]>>;
|
|
7
7
|
}[keyof TInput];
|
|
8
|
+
export type IsNullable = {
|
|
9
|
+
false: false;
|
|
10
|
+
undefined: undefined;
|
|
11
|
+
};
|
|
8
12
|
export type RelationMapBase<TInput extends TInputBase, TJoinedFrom extends TJoinedFromBase> = {
|
|
9
13
|
[k in keyof TInput]: {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
[n in keyof IsNullable]: {
|
|
15
|
+
base: k;
|
|
16
|
+
/** The id that uniqueness is checked against. Either the primary key of the table, or the id you are joining **to** on a join table. */
|
|
17
|
+
id: keyof NonNullable<TInput[k]>;
|
|
18
|
+
joinsTo: keyof NonNullable<TInput[k]>;
|
|
19
|
+
joinsFrom: keyof NonNullable<TJoinedFrom>;
|
|
20
|
+
joinType: "single" | "array";
|
|
21
|
+
isNullable?: IsNullable[n];
|
|
22
|
+
};
|
|
23
|
+
}[keyof IsNullable] & RelationMapJoiner<TInput, TInput[k]>;
|
|
17
24
|
}[keyof TInput];
|
|
18
25
|
export type RelationMapJoiner<TInput extends TInputBase, TJoinedFrom extends TJoinedFromBase> = {
|
|
19
26
|
[k: `_${string}`]: RelationMapBase<TInput, TJoinedFrom> & RelationMapJoiner<TInput, TJoinedFrom>;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { TInputBase, TJoinedFromBase, TOutputBase } from "./generic-bases";
|
|
2
2
|
import type { RelationMapRoot } from "./inputs";
|
|
3
3
|
import type { ExtendsNull, NoUnderscore, Simplify } from "./utils";
|
|
4
|
-
|
|
4
|
+
type JoinedRoot<TInput extends TInputBase, TOutput extends RelationMapRoot<TInput>> = TInput[TOutput["base"]] & RelationMapperReturnJoinRoot<TInput, TOutput, TOutput["base"]>;
|
|
5
|
+
export type RelationMapperReturnRoot<TInput extends TInputBase, TOutput extends RelationMapRoot<TInput>> = Simplify<ExtendsNull<TInput[TOutput["base"]]> extends true ? JoinedRoot<TInput, TOutput> | null : JoinedRoot<TInput, TOutput>>;
|
|
5
6
|
export type RelationMapperReturnJoinRoot<TInput extends TInputBase, TOutput extends RelationMapRoot<TInput>, TInputKey extends keyof TInput> = {
|
|
6
7
|
[outputKey in NoUnderscore<keyof TOutput>]: RelationMapperReturn<TInput, TInput[TInputKey], TOutput[`_${outputKey}`]>;
|
|
7
8
|
};
|
|
9
|
+
export type Joined<TInput extends TInputBase, TJoinedFrom extends TJoinedFromBase, TOutput extends TOutputBase<TInput, TJoinedFrom>> = Simplify<TInput[TOutput["base"]] & RelationMapperReturnJoin<TInput, TJoinedFrom, TOutput>>;
|
|
8
10
|
/** Fill current object with value specified in base */
|
|
9
|
-
export type RelationMapperReturn<TInput extends TInputBase, TJoinedFrom extends TJoinedFromBase, TOutput extends TOutputBase<TInput, TJoinedFrom>> = TOutput["joinType"] extends "array" ? Array<
|
|
11
|
+
export type RelationMapperReturn<TInput extends TInputBase, TJoinedFrom extends TJoinedFromBase, TOutput extends TOutputBase<TInput, TJoinedFrom>> = TOutput["joinType"] extends "array" ? Array<Joined<TInput, TJoinedFrom, TOutput>> : ExtendsNull<TInput[TOutput["base"]]> extends true ? TOutput["isNullable"] extends false ? Joined<TInput, TJoinedFrom, TOutput> : Joined<TInput, TJoinedFrom, TOutput> | null : Joined<TInput, TJoinedFrom, TOutput>;
|
|
10
12
|
/** Takes underscored values (joins) and replaces their values with the actual output. */
|
|
11
13
|
export type RelationMapperReturnJoin<TInput extends TInputBase, TJoinedFrom extends TJoinedFromBase, TOutput extends TOutputBase<TInput, TJoinedFrom>> = {
|
|
12
14
|
[k in NoUnderscore<keyof TOutput>]: RelationMapperReturn<TInput, TJoinedFrom, TOutput[`_${k}`]>;
|
|
13
15
|
};
|
|
16
|
+
export {};
|
|
@@ -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;
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
const invertInput = (inputs) => {
|
|
2
2
|
const invertedInput = inputs.reduce((final, row) => {
|
|
3
3
|
Object.entries(row).forEach(([header, value]) => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
else {
|
|
9
|
-
final[header] = value
|
|
10
|
-
? [value]
|
|
11
|
-
: [];
|
|
12
|
-
}
|
|
4
|
+
final[header] ??= [];
|
|
5
|
+
// Instantiated above.
|
|
6
|
+
if (value)
|
|
7
|
+
final[header].push(value);
|
|
13
8
|
});
|
|
14
9
|
return final;
|
|
15
10
|
}, {});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Asserts that if `isNullable` is false then item is not null. */
|
|
2
|
+
export const assertIsNullable = (item, isNullable) => {
|
|
3
|
+
if (isNullable === false && item === null) {
|
|
4
|
+
throw new Error("Value should exist as output schema defines it as nullable. Instead found null in array");
|
|
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
|
+
};
|