relation-matcher 1.0.3
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/.prettierrc +4 -0
- package/.vscode/settings.json +3 -0
- package/.yarn/install-state.gz +0 -0
- package/.yarnrc.yml +1 -0
- package/dist/dist/jest.config.js +10 -0
- package/dist/dist/src/index.js +107 -0
- package/dist/dist/src/index.test.js +81 -0
- package/dist/dist/src/testing/file-output.js +9 -0
- package/dist/dist/src/testing/test-data.js +164 -0
- package/dist/dist/src/types/generic-bases.js +1 -0
- package/dist/dist/src/types/generic-less.js +1 -0
- package/dist/dist/src/types/inputs.js +1 -0
- package/dist/dist/src/types/return.js +1 -0
- package/dist/dist/src/types/typetest.js +6 -0
- package/dist/dist/src/types/utils.js +1 -0
- package/dist/dist/src/utils/invertInput.js +17 -0
- package/dist/dist/src/utils/keys.js +1 -0
- package/dist/dist/src/utils/matcher.js +1 -0
- package/dist/jest.config.js +10 -0
- package/dist/src/index.js +107 -0
- package/dist/src/index.test.js +81 -0
- package/dist/src/testing/file-output.js +9 -0
- package/dist/src/testing/test-data.js +164 -0
- package/dist/src/types/generic-bases.js +1 -0
- package/dist/src/types/generic-less.js +1 -0
- package/dist/src/types/inputs.js +1 -0
- package/dist/src/types/return.js +1 -0
- package/dist/src/types/typetest.js +6 -0
- package/dist/src/types/utils.js +1 -0
- package/dist/src/utils/invertInput.js +17 -0
- package/dist/src/utils/keys.js +1 -0
- package/dist/src/utils/matcher.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/jest.config.js +12 -0
- package/package.json +25 -0
- package/result.json +136 -0
- package/src/index.test.ts +88 -0
- package/src/index.ts +184 -0
- package/src/testing/file-output.ts +15 -0
- package/src/testing/test-data.ts +171 -0
- package/src/types/generic-bases.ts +9 -0
- package/src/types/generic-less.ts +14 -0
- package/src/types/inputs.ts +29 -0
- package/src/types/return.ts +37 -0
- package/src/types/typetest.ts +46 -0
- package/src/types/utils.ts +16 -0
- package/src/utils/invertInput.ts +28 -0
- package/src/utils/keys.ts +1 -0
- package/src/utils/matcher.ts +8 -0
- package/tsconfig.json +36 -0
package/jest.config.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createDefaultPreset } from "ts-jest";
|
|
2
|
+
|
|
3
|
+
const tsJestTransformCfg = createDefaultPreset().transform;
|
|
4
|
+
|
|
5
|
+
/** @type {import("jest").Config} **/
|
|
6
|
+
export default {
|
|
7
|
+
preset: "ts-jest",
|
|
8
|
+
testEnvironment: "node",
|
|
9
|
+
transform: {
|
|
10
|
+
...tsJestTransformCfg,
|
|
11
|
+
},
|
|
12
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "relation-matcher",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "A utility to convert table data (such as out of a SQL query) into structured JSON.",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"author": "",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/src/index.js",
|
|
9
|
+
"types": "dist/src/index.d.ts",
|
|
10
|
+
"private": false,
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "jest",
|
|
13
|
+
"file-output": "tsx --watch src/file-output.ts",
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"build:publish": "yarn run build && npm publish"
|
|
16
|
+
},
|
|
17
|
+
"packageManager": "yarn@4.12.0",
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/jest": "^30.0.0",
|
|
20
|
+
"@types/node": "^25.0.10",
|
|
21
|
+
"jest": "^30.2.0",
|
|
22
|
+
"ts-jest": "^29.4.6",
|
|
23
|
+
"typescript": "^5.9.3"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/result.json
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
{
|
|
2
|
+
"c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01": {
|
|
3
|
+
"id": "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
4
|
+
"clerkId": "user_abc123",
|
|
5
|
+
"email": "alice@example.com",
|
|
6
|
+
"createdAt": "2025-01-12T09:41:22.000Z",
|
|
7
|
+
"teamToUsers": [
|
|
8
|
+
{
|
|
9
|
+
"teamId": "a2e5a3de-6d14-4e9b-9c9f-3cbb2cdb8a10",
|
|
10
|
+
"userId": "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
11
|
+
"role": "admin",
|
|
12
|
+
"team": {
|
|
13
|
+
"id": "a2e5a3de-6d14-4e9b-9c9f-3cbb2cdb8a10",
|
|
14
|
+
"name": "Red Dragons",
|
|
15
|
+
"teamToUsers": [
|
|
16
|
+
{
|
|
17
|
+
"teamId": "a2e5a3de-6d14-4e9b-9c9f-3cbb2cdb8a10",
|
|
18
|
+
"userId": "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
19
|
+
"role": "admin",
|
|
20
|
+
"user": {
|
|
21
|
+
"id": "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
22
|
+
"clerkId": "user_abc123",
|
|
23
|
+
"email": "alice@example.com",
|
|
24
|
+
"createdAt": "2025-01-12T09:41:22.000Z"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"teamId": "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
32
|
+
"userId": "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
33
|
+
"role": "member",
|
|
34
|
+
"team": {
|
|
35
|
+
"id": "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
36
|
+
"name": "Blue Sharks",
|
|
37
|
+
"teamToUsers": [
|
|
38
|
+
{
|
|
39
|
+
"teamId": "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
40
|
+
"userId": "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
41
|
+
"role": "member",
|
|
42
|
+
"user": {
|
|
43
|
+
"id": "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
44
|
+
"clerkId": "user_abc123",
|
|
45
|
+
"email": "alice@example.com",
|
|
46
|
+
"createdAt": "2025-01-12T09:41:22.000Z"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"teamId": "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
51
|
+
"userId": "f44a8c17-3c6d-4e38-9f61-0a9f2b1c8d55",
|
|
52
|
+
"role": "admin",
|
|
53
|
+
"user": {
|
|
54
|
+
"id": "f44a8c17-3c6d-4e38-9f61-0a9f2b1c8d55",
|
|
55
|
+
"clerkId": "user_xyz789",
|
|
56
|
+
"email": "dave@example.com",
|
|
57
|
+
"createdAt": "2025-02-03T14:18:10.000Z"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"posts": [
|
|
65
|
+
{
|
|
66
|
+
"id": "f13d8f22-0b61-4d6a-8b1e-5b6b3d0c8a21",
|
|
67
|
+
"userId": "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
68
|
+
"title": "First post",
|
|
69
|
+
"published": true,
|
|
70
|
+
"comments": [
|
|
71
|
+
{
|
|
72
|
+
"id": "9d1a7c3b-2f6a-4f7c-bf4b-8f6e3c5d9e01",
|
|
73
|
+
"postId": "f13d8f22-0b61-4d6a-8b1e-5b6b3d0c8a21",
|
|
74
|
+
"body": "Nice post!",
|
|
75
|
+
"authorEmail": "bob@example.com"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"id": "e3c9b5a2-7f42-4b7e-9e3d-4a6f1d8b2c44",
|
|
79
|
+
"postId": "f13d8f22-0b61-4d6a-8b1e-5b6b3d0c8a21",
|
|
80
|
+
"body": "Thanks for sharing",
|
|
81
|
+
"authorEmail": "charlie@example.com"
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "6bcb2b74-9b2f-4b38-bdb5-77c2e63d9c10",
|
|
87
|
+
"userId": "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
88
|
+
"title": "Second post",
|
|
89
|
+
"published": false,
|
|
90
|
+
"comments": []
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
"f44a8c17-3c6d-4e38-9f61-0a9f2b1c8d55": {
|
|
95
|
+
"id": "f44a8c17-3c6d-4e38-9f61-0a9f2b1c8d55",
|
|
96
|
+
"clerkId": "user_xyz789",
|
|
97
|
+
"email": "dave@example.com",
|
|
98
|
+
"createdAt": "2025-02-03T14:18:10.000Z",
|
|
99
|
+
"teamToUsers": [
|
|
100
|
+
{
|
|
101
|
+
"teamId": "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
102
|
+
"userId": "f44a8c17-3c6d-4e38-9f61-0a9f2b1c8d55",
|
|
103
|
+
"role": "admin",
|
|
104
|
+
"team": {
|
|
105
|
+
"id": "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
106
|
+
"name": "Blue Sharks",
|
|
107
|
+
"teamToUsers": [
|
|
108
|
+
{
|
|
109
|
+
"teamId": "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
110
|
+
"userId": "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
111
|
+
"role": "member",
|
|
112
|
+
"user": {
|
|
113
|
+
"id": "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
114
|
+
"clerkId": "user_abc123",
|
|
115
|
+
"email": "alice@example.com",
|
|
116
|
+
"createdAt": "2025-01-12T09:41:22.000Z"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"teamId": "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
121
|
+
"userId": "f44a8c17-3c6d-4e38-9f61-0a9f2b1c8d55",
|
|
122
|
+
"role": "admin",
|
|
123
|
+
"user": {
|
|
124
|
+
"id": "f44a8c17-3c6d-4e38-9f61-0a9f2b1c8d55",
|
|
125
|
+
"clerkId": "user_xyz789",
|
|
126
|
+
"email": "dave@example.com",
|
|
127
|
+
"createdAt": "2025-02-03T14:18:10.000Z"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
],
|
|
134
|
+
"posts": []
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { relationMatcherRoot } from ".";
|
|
2
|
+
import { testData, testSchema } from "./testing/test-data";
|
|
3
|
+
|
|
4
|
+
test("Tests output of mapper.", () => {
|
|
5
|
+
const relationMatcherData = relationMatcherRoot(testData, testSchema);
|
|
6
|
+
|
|
7
|
+
console.log(JSON.stringify(relationMatcherData, null, "\t"));
|
|
8
|
+
|
|
9
|
+
expect(relationMatcherData).toStrictEqual({
|
|
10
|
+
"c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01": {
|
|
11
|
+
id: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
12
|
+
clerkId: "user_abc123",
|
|
13
|
+
email: "alice@example.com",
|
|
14
|
+
createdAt: "2025-01-12T09:41:22.000Z",
|
|
15
|
+
|
|
16
|
+
teamToUsers: [
|
|
17
|
+
{
|
|
18
|
+
teamId: "a2e5a3de-6d14-4e9b-9c9f-3cbb2cdb8a10",
|
|
19
|
+
userId: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
20
|
+
role: "admin",
|
|
21
|
+
team: {
|
|
22
|
+
id: "a2e5a3de-6d14-4e9b-9c9f-3cbb2cdb8a10",
|
|
23
|
+
name: "Red Dragons",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
teamId: "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
28
|
+
userId: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
29
|
+
role: "member",
|
|
30
|
+
team: {
|
|
31
|
+
id: "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
32
|
+
name: "Blue Sharks",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
|
|
37
|
+
posts: [
|
|
38
|
+
{
|
|
39
|
+
id: "f13d8f22-0b61-4d6a-8b1e-5b6b3d0c8a21",
|
|
40
|
+
title: "First post",
|
|
41
|
+
published: true,
|
|
42
|
+
userId: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
43
|
+
comments: [
|
|
44
|
+
{
|
|
45
|
+
id: "9d1a7c3b-2f6a-4f7c-bf4b-8f6e3c5d9e01",
|
|
46
|
+
body: "Nice post!",
|
|
47
|
+
postId: "f13d8f22-0b61-4d6a-8b1e-5b6b3d0c8a21",
|
|
48
|
+
authorEmail: "bob@example.com",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "e3c9b5a2-7f42-4b7e-9e3d-4a6f1d8b2c44",
|
|
52
|
+
body: "Thanks for sharing",
|
|
53
|
+
postId: "f13d8f22-0b61-4d6a-8b1e-5b6b3d0c8a21",
|
|
54
|
+
authorEmail: "charlie@example.com",
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "6bcb2b74-9b2f-4b38-bdb5-77c2e63d9c10",
|
|
60
|
+
title: "Second post",
|
|
61
|
+
published: false,
|
|
62
|
+
userId: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
63
|
+
comments: [],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
"f44a8c17-3c6d-4e38-9f61-0a9f2b1c8d55": {
|
|
68
|
+
id: "f44a8c17-3c6d-4e38-9f61-0a9f2b1c8d55",
|
|
69
|
+
clerkId: "user_xyz789",
|
|
70
|
+
email: "dave@example.com",
|
|
71
|
+
createdAt: "2025-02-03T14:18:10.000Z",
|
|
72
|
+
|
|
73
|
+
teamToUsers: [
|
|
74
|
+
{
|
|
75
|
+
teamId: "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
76
|
+
userId: "f44a8c17-3c6d-4e38-9f61-0a9f2b1c8d55",
|
|
77
|
+
role: "admin",
|
|
78
|
+
team: {
|
|
79
|
+
id: "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
80
|
+
name: "Blue Sharks",
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
|
|
85
|
+
posts: [],
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import type { TInputBase } from "./types/generic-bases";
|
|
2
|
+
import type { Output } from "./types/generic-less";
|
|
3
|
+
import type { RelationMapRoot } from "./types/inputs";
|
|
4
|
+
import type { RelationMapperReturnRoot } from "./types/return";
|
|
5
|
+
import invertInput, { type InvertedInput } from "./utils/invertInput";
|
|
6
|
+
import { getJoinFinalKey } from "./utils/keys";
|
|
7
|
+
import { matcherFunc } from "./utils/matcher";
|
|
8
|
+
|
|
9
|
+
export const relationMatcherRoot = <
|
|
10
|
+
TInput extends TInputBase,
|
|
11
|
+
TOutputRoot extends RelationMapRoot<TInput>,
|
|
12
|
+
>(
|
|
13
|
+
inputs: TInput[],
|
|
14
|
+
output: TOutputRoot,
|
|
15
|
+
): Record<string, RelationMapperReturnRoot<TInput, TOutputRoot>> => {
|
|
16
|
+
const invertedInput = invertInput<TInput>(inputs);
|
|
17
|
+
|
|
18
|
+
const baseItems = invertedInput[output.base].reduce<
|
|
19
|
+
Record<string, TInput[TOutputRoot["base"]]>
|
|
20
|
+
>((final, row) => {
|
|
21
|
+
if (!row) {
|
|
22
|
+
return final;
|
|
23
|
+
}
|
|
24
|
+
// if (final[row[output.id as string] as string]) {
|
|
25
|
+
// return final;
|
|
26
|
+
// }
|
|
27
|
+
|
|
28
|
+
const relations = Object.entries(output).reduce<
|
|
29
|
+
Record<string, unknown>
|
|
30
|
+
>((final, [key, value]) => {
|
|
31
|
+
// Filters out base parameters so value is only joins;
|
|
32
|
+
// value will be similar to { base: "team_to_user"; joinsTo: "team_id"; joinsFrom: "id"; joinType: "single"; }
|
|
33
|
+
if (typeof value === "object") {
|
|
34
|
+
const finalKey = getJoinFinalKey(key);
|
|
35
|
+
|
|
36
|
+
if (value.joinType === "array") {
|
|
37
|
+
const baseObjArr = invertedInput[value.base]!.filter(
|
|
38
|
+
matcherFunc(row, value as Output),
|
|
39
|
+
) as TInputBase[string][];
|
|
40
|
+
|
|
41
|
+
final[finalKey] = Object.values(
|
|
42
|
+
baseObjArr.reduce<
|
|
43
|
+
Record<string, NonNullable<TInputBase[string]>>
|
|
44
|
+
>((final, item) => {
|
|
45
|
+
if (
|
|
46
|
+
!item
|
|
47
|
+
// final[item[value.id as string] as string]
|
|
48
|
+
)
|
|
49
|
+
return final;
|
|
50
|
+
|
|
51
|
+
// if (!item[value.joinsFrom as string]) {
|
|
52
|
+
console.log(item, value.joinsFrom);
|
|
53
|
+
// }
|
|
54
|
+
|
|
55
|
+
final[item[value.id as string] as string] = {
|
|
56
|
+
...item,
|
|
57
|
+
...relationMatcherJoiner(
|
|
58
|
+
invertedInput,
|
|
59
|
+
value as Output,
|
|
60
|
+
item,
|
|
61
|
+
),
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return final;
|
|
65
|
+
}, {}),
|
|
66
|
+
);
|
|
67
|
+
} else {
|
|
68
|
+
const item =
|
|
69
|
+
(invertedInput[value.base]!.find(
|
|
70
|
+
matcherFunc(row, value as Output),
|
|
71
|
+
) as TInputBase[string] | undefined) ?? null;
|
|
72
|
+
|
|
73
|
+
if (item) {
|
|
74
|
+
final[finalKey] = {
|
|
75
|
+
...item,
|
|
76
|
+
...relationMatcherJoiner(
|
|
77
|
+
invertedInput,
|
|
78
|
+
value as Output,
|
|
79
|
+
item,
|
|
80
|
+
),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// if (value.joinType === 'array')
|
|
86
|
+
|
|
87
|
+
// final[finalKey] = relationMatcherJoiner(
|
|
88
|
+
// invertedInput,
|
|
89
|
+
// value as Output,
|
|
90
|
+
// row[value.joinsFrom as string] as string,
|
|
91
|
+
// );
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return final;
|
|
95
|
+
}, {}) as TInput[TOutputRoot["base"]];
|
|
96
|
+
|
|
97
|
+
final[row[output.id as string] as string] = { ...row, ...relations };
|
|
98
|
+
|
|
99
|
+
return final;
|
|
100
|
+
}, {});
|
|
101
|
+
|
|
102
|
+
return baseItems as Record<
|
|
103
|
+
string,
|
|
104
|
+
RelationMapperReturnRoot<TInput, TOutputRoot>
|
|
105
|
+
>;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const relationMatcher = relationMatcherRoot;
|
|
109
|
+
export default relationMatcherRoot;
|
|
110
|
+
|
|
111
|
+
const relationMatcherJoiner = (
|
|
112
|
+
input: InvertedInput,
|
|
113
|
+
output: Output,
|
|
114
|
+
joiningFrom: Record<string, unknown>,
|
|
115
|
+
) => {
|
|
116
|
+
return Object.entries(output).reduce<
|
|
117
|
+
Record<string, TInputBase[string] | TInputBase[string][] | null>
|
|
118
|
+
>((final, [key, value]) => {
|
|
119
|
+
if (typeof value === "object") {
|
|
120
|
+
// console.log(value.base);
|
|
121
|
+
const finalKey = key.replace(/^_/, "");
|
|
122
|
+
|
|
123
|
+
const matcherFunc = (
|
|
124
|
+
item: InvertedInput[keyof InvertedInput][number],
|
|
125
|
+
) => item?.[value.joinsTo] === joiningFrom[value.joinsFrom];
|
|
126
|
+
|
|
127
|
+
if (value.joinType === "array") {
|
|
128
|
+
const baseObjArr = input[value.base]!.filter(
|
|
129
|
+
matcherFunc,
|
|
130
|
+
) as InvertedInput[number];
|
|
131
|
+
|
|
132
|
+
// console.log("BaseObjArr:", baseObjArr);
|
|
133
|
+
// console.log(
|
|
134
|
+
// "input[value.base]:",
|
|
135
|
+
// input[value.base]?.[0]?.[value.joinsTo],
|
|
136
|
+
// );
|
|
137
|
+
// console.log("joiningId", joiningId);
|
|
138
|
+
|
|
139
|
+
final[finalKey] = Object.values(
|
|
140
|
+
baseObjArr.reduce<
|
|
141
|
+
Record<string, NonNullable<TInputBase[string]>>
|
|
142
|
+
>((final, item) => {
|
|
143
|
+
if (!item) return final;
|
|
144
|
+
|
|
145
|
+
const propertyKey = item[value.id as string] as string;
|
|
146
|
+
|
|
147
|
+
if (final[propertyKey]) return final;
|
|
148
|
+
|
|
149
|
+
final[propertyKey] = {
|
|
150
|
+
...item,
|
|
151
|
+
...relationMatcherJoiner(input, value, item),
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
return final;
|
|
155
|
+
}, {}),
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
// baseObjArr.map(item => (item ? {
|
|
159
|
+
// ...item,
|
|
160
|
+
// ...relationMatcherJoiner(input, value, item[value.joinsFrom as keyof TInputBase])
|
|
161
|
+
// } : null))
|
|
162
|
+
} else {
|
|
163
|
+
const item =
|
|
164
|
+
(input[value.base]!.find(matcherFunc) as
|
|
165
|
+
| InvertedInput[string][number]
|
|
166
|
+
| undefined) ?? null;
|
|
167
|
+
|
|
168
|
+
// console.log("item:", item);
|
|
169
|
+
// console.log("input[value.base]:", input[value.base]);
|
|
170
|
+
// console.log("joiningId:", joiningId);
|
|
171
|
+
// console.log("value:", value);
|
|
172
|
+
|
|
173
|
+
if (item) {
|
|
174
|
+
final[finalKey] = {
|
|
175
|
+
...item,
|
|
176
|
+
...relationMatcherJoiner(input, value, item),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return final;
|
|
183
|
+
}, {});
|
|
184
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { relationMatcherRoot } from "..";
|
|
4
|
+
import { testData, testSchema } from "./test-data";
|
|
5
|
+
|
|
6
|
+
const main = async () => {
|
|
7
|
+
const result = relationMatcherRoot(testData, testSchema);
|
|
8
|
+
|
|
9
|
+
await fs.writeFile(
|
|
10
|
+
path.join(process.cwd(), "/result.json"),
|
|
11
|
+
JSON.stringify(result, null, "\t"),
|
|
12
|
+
);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
void main();
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { RelationMapRoot } from "~/types/inputs";
|
|
2
|
+
|
|
3
|
+
export const testData = [
|
|
4
|
+
{
|
|
5
|
+
users: {
|
|
6
|
+
id: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
7
|
+
clerkId: "user_abc123",
|
|
8
|
+
email: "alice@example.com",
|
|
9
|
+
createdAt: "2025-01-12T09:41:22.000Z",
|
|
10
|
+
},
|
|
11
|
+
teamToUser: {
|
|
12
|
+
teamId: "a2e5a3de-6d14-4e9b-9c9f-3cbb2cdb8a10",
|
|
13
|
+
userId: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
14
|
+
role: "admin",
|
|
15
|
+
},
|
|
16
|
+
teams: {
|
|
17
|
+
id: "a2e5a3de-6d14-4e9b-9c9f-3cbb2cdb8a10",
|
|
18
|
+
name: "Red Dragons",
|
|
19
|
+
},
|
|
20
|
+
posts: {
|
|
21
|
+
id: "f13d8f22-0b61-4d6a-8b1e-5b6b3d0c8a21",
|
|
22
|
+
userId: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
23
|
+
title: "First post",
|
|
24
|
+
published: true,
|
|
25
|
+
},
|
|
26
|
+
comments: {
|
|
27
|
+
id: "9d1a7c3b-2f6a-4f7c-bf4b-8f6e3c5d9e01",
|
|
28
|
+
postId: "f13d8f22-0b61-4d6a-8b1e-5b6b3d0c8a21",
|
|
29
|
+
body: "Nice post!",
|
|
30
|
+
authorEmail: "bob@example.com",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
users: {
|
|
35
|
+
id: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
36
|
+
clerkId: "user_abc123",
|
|
37
|
+
email: "alice@example.com",
|
|
38
|
+
createdAt: "2025-01-12T09:41:22.000Z",
|
|
39
|
+
},
|
|
40
|
+
teamToUser: {
|
|
41
|
+
teamId: "a2e5a3de-6d14-4e9b-9c9f-3cbb2cdb8a10",
|
|
42
|
+
userId: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
43
|
+
role: "admin",
|
|
44
|
+
},
|
|
45
|
+
teams: {
|
|
46
|
+
id: "a2e5a3de-6d14-4e9b-9c9f-3cbb2cdb8a10",
|
|
47
|
+
name: "Red Dragons",
|
|
48
|
+
},
|
|
49
|
+
posts: {
|
|
50
|
+
id: "f13d8f22-0b61-4d6a-8b1e-5b6b3d0c8a21",
|
|
51
|
+
userId: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
52
|
+
title: "First post",
|
|
53
|
+
published: true,
|
|
54
|
+
},
|
|
55
|
+
comments: {
|
|
56
|
+
id: "e3c9b5a2-7f42-4b7e-9e3d-4a6f1d8b2c44",
|
|
57
|
+
postId: "f13d8f22-0b61-4d6a-8b1e-5b6b3d0c8a21",
|
|
58
|
+
body: "Thanks for sharing",
|
|
59
|
+
authorEmail: "charlie@example.com",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
users: {
|
|
64
|
+
id: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
65
|
+
clerkId: "user_abc123",
|
|
66
|
+
email: "alice@example.com",
|
|
67
|
+
createdAt: "2025-01-12T09:41:22.000Z",
|
|
68
|
+
},
|
|
69
|
+
teamToUser: {
|
|
70
|
+
teamId: "a2e5a3de-6d14-4e9b-9c9f-3cbb2cdb8a10",
|
|
71
|
+
userId: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
72
|
+
role: "admin",
|
|
73
|
+
},
|
|
74
|
+
teams: {
|
|
75
|
+
id: "a2e5a3de-6d14-4e9b-9c9f-3cbb2cdb8a10",
|
|
76
|
+
name: "Red Dragons",
|
|
77
|
+
},
|
|
78
|
+
posts: {
|
|
79
|
+
id: "6bcb2b74-9b2f-4b38-bdb5-77c2e63d9c10",
|
|
80
|
+
userId: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
81
|
+
title: "Second post",
|
|
82
|
+
published: false,
|
|
83
|
+
},
|
|
84
|
+
comments: null,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
users: {
|
|
88
|
+
id: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
89
|
+
clerkId: "user_abc123",
|
|
90
|
+
email: "alice@example.com",
|
|
91
|
+
createdAt: "2025-01-12T09:41:22.000Z",
|
|
92
|
+
},
|
|
93
|
+
teamToUser: {
|
|
94
|
+
teamId: "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
95
|
+
userId: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
96
|
+
role: "member",
|
|
97
|
+
},
|
|
98
|
+
teams: {
|
|
99
|
+
id: "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
100
|
+
name: "Blue Sharks",
|
|
101
|
+
},
|
|
102
|
+
posts: {
|
|
103
|
+
id: "f13d8f22-0b61-4d6a-8b1e-5b6b3d0c8a21",
|
|
104
|
+
userId: "c7a2c1c8-9f4c-4f89-9d72-6b5a2f0c1e01",
|
|
105
|
+
title: "First post",
|
|
106
|
+
published: true,
|
|
107
|
+
},
|
|
108
|
+
comments: {
|
|
109
|
+
id: "9d1a7c3b-2f6a-4f7c-bf4b-8f6e3c5d9e01",
|
|
110
|
+
postId: "f13d8f22-0b61-4d6a-8b1e-5b6b3d0c8a21",
|
|
111
|
+
body: "Nice post!",
|
|
112
|
+
authorEmail: "bob@example.com",
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
users: {
|
|
117
|
+
id: "f44a8c17-3c6d-4e38-9f61-0a9f2b1c8d55",
|
|
118
|
+
clerkId: "user_xyz789",
|
|
119
|
+
email: "dave@example.com",
|
|
120
|
+
createdAt: "2025-02-03T14:18:10.000Z",
|
|
121
|
+
},
|
|
122
|
+
teamToUser: {
|
|
123
|
+
teamId: "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
124
|
+
userId: "f44a8c17-3c6d-4e38-9f61-0a9f2b1c8d55",
|
|
125
|
+
role: "admin",
|
|
126
|
+
},
|
|
127
|
+
teams: {
|
|
128
|
+
id: "d91f42a6-8cbb-4e63-9b5c-8d1b4f2a7e77",
|
|
129
|
+
name: "Blue Sharks",
|
|
130
|
+
},
|
|
131
|
+
posts: null,
|
|
132
|
+
comments: null,
|
|
133
|
+
},
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
export const testSchema: RelationMapRoot<(typeof testData)[number]> = {
|
|
137
|
+
base: "users",
|
|
138
|
+
id: "id",
|
|
139
|
+
|
|
140
|
+
_teamToUsers: {
|
|
141
|
+
base: "teamToUser",
|
|
142
|
+
id: "teamId",
|
|
143
|
+
joinsFrom: "id",
|
|
144
|
+
joinsTo: "userId",
|
|
145
|
+
joinType: "array",
|
|
146
|
+
|
|
147
|
+
_team: {
|
|
148
|
+
base: "teams",
|
|
149
|
+
id: "id",
|
|
150
|
+
joinsFrom: "teamId",
|
|
151
|
+
joinsTo: "id",
|
|
152
|
+
joinType: "single",
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
_posts: {
|
|
157
|
+
base: "posts",
|
|
158
|
+
id: "id",
|
|
159
|
+
joinsFrom: "id",
|
|
160
|
+
joinsTo: "userId",
|
|
161
|
+
joinType: "array",
|
|
162
|
+
|
|
163
|
+
_comments: {
|
|
164
|
+
base: "comments",
|
|
165
|
+
id: "id",
|
|
166
|
+
joinsFrom: "id",
|
|
167
|
+
joinsTo: "postId",
|
|
168
|
+
joinType: "array",
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RelationMapBase, RelationMapJoiner } from "./inputs";
|
|
2
|
+
|
|
3
|
+
export type TInputBase = Record<string, Record<string, unknown> | null>;
|
|
4
|
+
export type TJoinedFromBase = TInputBase[keyof TInputBase];
|
|
5
|
+
export type TOutputBase<
|
|
6
|
+
TInput extends TInputBase,
|
|
7
|
+
TJoinedFrom extends TJoinedFromBase,
|
|
8
|
+
> = RelationMapBase<TInput, TJoinedFrom> &
|
|
9
|
+
RelationMapJoiner<TInput, TJoinedFrom>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TInputBase } from "./generic-bases";
|
|
2
|
+
|
|
3
|
+
export type Output<TJoinType extends "single" | "array" = "single" | "array"> =
|
|
4
|
+
OutputJoins & {
|
|
5
|
+
base: keyof TInputBase;
|
|
6
|
+
id: string;
|
|
7
|
+
joinsTo: string;
|
|
8
|
+
joinsFrom: string;
|
|
9
|
+
joinType: TJoinType;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type OutputJoins = {
|
|
13
|
+
[k: `_${string}`]: Output;
|
|
14
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { TInputBase, TJoinedFromBase } from "./generic-bases";
|
|
2
|
+
|
|
3
|
+
export type RelationMapRoot<TInput extends TInputBase> = {
|
|
4
|
+
[k in keyof TInput]: {
|
|
5
|
+
base: k;
|
|
6
|
+
id: keyof TInput[k];
|
|
7
|
+
} & RelationMapJoiner<TInput, TInput[k]>;
|
|
8
|
+
}[keyof TInput];
|
|
9
|
+
|
|
10
|
+
export type RelationMapBase<
|
|
11
|
+
TInput extends TInputBase,
|
|
12
|
+
TJoinedFrom extends TJoinedFromBase,
|
|
13
|
+
> = {
|
|
14
|
+
[k in keyof TInput]: {
|
|
15
|
+
base: k;
|
|
16
|
+
id: keyof TInput[k];
|
|
17
|
+
joinsTo: keyof TInput[k];
|
|
18
|
+
joinsFrom: keyof TJoinedFrom;
|
|
19
|
+
joinType: "single" | "array";
|
|
20
|
+
} & RelationMapJoiner<TInput, TInput[k]>;
|
|
21
|
+
}[keyof TInput];
|
|
22
|
+
|
|
23
|
+
export type RelationMapJoiner<
|
|
24
|
+
TInput extends TInputBase,
|
|
25
|
+
TJoinedFrom extends TJoinedFromBase,
|
|
26
|
+
> = {
|
|
27
|
+
[k: `_${string}`]: RelationMapJoiner<TInput, TJoinedFrom> &
|
|
28
|
+
RelationMapBase<TInput, TJoinedFrom>;
|
|
29
|
+
};
|