@x-fiber-sys/be-common 0.1.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 +11 -0
- package/package.json +22 -0
- package/src/audit/index.ts +1 -0
- package/src/audit/users-actions.ts +12 -0
- package/src/constants/account-kind.ts +11 -0
- package/src/constants/common-headers.ts +32 -0
- package/src/constants/ecosystem-kind.ts +12 -0
- package/src/constants/index.ts +8 -0
- package/src/constants/kafka-headers.ts +17 -0
- package/src/constants/protocol-kind.ts +12 -0
- package/src/constants/services-list.ts +18 -0
- package/src/constants/session-kind.ts +13 -0
- package/src/constants/trace-tags.ts +28 -0
- package/src/errors/index.ts +120 -0
- package/src/index.ts +7 -0
- package/src/jwt/index.ts +0 -0
- package/src/schemas/index.ts +1 -0
- package/src/schemas/union-schema.ts +47 -0
- package/src/types/fastify.d.ts +3 -0
- package/src/types/index.d.ts +7 -0
- package/src/types/jwt-payloads.d.ts +18 -0
- package/src/types/protocol.d.ts +2 -0
- package/src/types/repository.d.ts +1 -0
- package/src/types/transport.d.ts +2 -0
- package/src/utils/chunk-generator.ts +5 -0
- package/src/utils/create-object-enum.ts +9 -0
- package/src/utils/create-symbols.ts +8 -0
- package/src/utils/get-strictness-fields.ts +12 -0
- package/src/utils/get-stringify-header.ts +6 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/revert-object.ts +12 -0
package/README.md
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@x-fiber-sys/be-common",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Package with common c-argo-be information's - schemas, decorators, etc.",
|
|
5
|
+
"author": "pestsov-v <pestsov.js@gmail.com>",
|
|
6
|
+
"homepage": "https://github.com/pestsov-v/c-argo-back-mono#readme",
|
|
7
|
+
"license": "ISC",
|
|
8
|
+
"main": "src/index.ts",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/pestsov-v/c-argo-back-mono.git"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/pestsov-v/c-argo-back-mono/issues"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@sinclair/typebox": "^0.34.41"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './users-actions'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { revertObject } from '../utils'
|
|
2
|
+
|
|
3
|
+
export const UsersActionMapping = {
|
|
4
|
+
user_signin: 1,
|
|
5
|
+
user_signup: 2,
|
|
6
|
+
user_logout: 3,
|
|
7
|
+
} as const
|
|
8
|
+
|
|
9
|
+
export const UsersActionReverted = revertObject(UsersActionMapping)
|
|
10
|
+
|
|
11
|
+
export type UsersActionKey = keyof typeof UsersActionMapping
|
|
12
|
+
export type UsersAction = (typeof UsersActionMapping)[UsersActionKey]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { revertObject } from '../utils'
|
|
2
|
+
|
|
3
|
+
export const AccountKindMapping = {
|
|
4
|
+
organisation: 1,
|
|
5
|
+
personal: 2,
|
|
6
|
+
} as const
|
|
7
|
+
|
|
8
|
+
export type AccountKindKey = keyof typeof AccountKindMapping
|
|
9
|
+
export type AccountKind = (typeof AccountKindMapping)[AccountKindKey]
|
|
10
|
+
|
|
11
|
+
export const AccountKindReverted = revertObject(AccountKindMapping)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const UserHeaders = {
|
|
2
|
+
X_ORGANISATION_ID: 'x-c-argo-organisation-id',
|
|
3
|
+
X_USER_ID: 'x-c-argo-user-id',
|
|
4
|
+
X_USER_NAME: 'x-c-argo-user-name',
|
|
5
|
+
X_DEVICE_ID: 'x-c-argo-device-id',
|
|
6
|
+
X_ROLE_IDS: 'x-c-argo-role-ids',
|
|
7
|
+
X_SUBDIVISIONS_IDS: 'x-c-argo-subdivisions-ids',
|
|
8
|
+
X_SESSION_ID: 'x-c-argo-session-id',
|
|
9
|
+
X_ACCOUNT_KIND: 'x-c-argo-account-kind',
|
|
10
|
+
X_SESSION_KIND: 'x-c-argo-session-kind',
|
|
11
|
+
X_ECOSYSTEM_DOMAIN: 'x-c-argo-ecosystem-domain',
|
|
12
|
+
X_ORIGIN_IP: 'x-c-argo-origin-ip',
|
|
13
|
+
X_ORIGIN_USER_AGENT: 'x-c-argo-user-agent',
|
|
14
|
+
X_SERVICE_NAME: 'x-c-argo-service-name',
|
|
15
|
+
} as const
|
|
16
|
+
|
|
17
|
+
export const TraceHeaders = {
|
|
18
|
+
X_TRACE_ID: 'x-c-argo-trace-id',
|
|
19
|
+
X_CARRIER_PAYLOAD: 'x-c-argo-carrier-payload',
|
|
20
|
+
} as const
|
|
21
|
+
|
|
22
|
+
export const AuthHeaders = {
|
|
23
|
+
X_ACCESS_TOKEN: 'x-c-argo-access-token',
|
|
24
|
+
X_REFRESH_TOKEN: 'x-c-argo-refresh-token',
|
|
25
|
+
} as const
|
|
26
|
+
|
|
27
|
+
export const ManagerHeaders = {
|
|
28
|
+
X_MANAGER_SERVICE_NAME: 'x-c-argo-manager-service-name',
|
|
29
|
+
X_MANAGER_USER_ID: 'x-c-argo-manager-user-id',
|
|
30
|
+
X_MANAGER_USER_NAME: 'x-c-argo-manager-user-name',
|
|
31
|
+
X_MANAGER_USER_PASSWORD: 'x-c-argo-user-password',
|
|
32
|
+
} as const
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { revertObject } from '../utils'
|
|
2
|
+
|
|
3
|
+
export const EcosystemKindMapping = {
|
|
4
|
+
ERP: 1,
|
|
5
|
+
market: 2,
|
|
6
|
+
exchange: 3,
|
|
7
|
+
} as const
|
|
8
|
+
|
|
9
|
+
export const EcosystemKindReverted = revertObject(EcosystemKindMapping)
|
|
10
|
+
|
|
11
|
+
export type EcosystemKindKey = keyof typeof EcosystemKindMapping
|
|
12
|
+
export type EcosystemKind = (typeof EcosystemKindMapping)[EcosystemKindKey]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './common-headers'
|
|
2
|
+
export * from './account-kind'
|
|
3
|
+
export * from './ecosystem-kind'
|
|
4
|
+
export * from './protocol-kind'
|
|
5
|
+
export * from './trace-tags'
|
|
6
|
+
export * from './session-kind'
|
|
7
|
+
export * from './services-list'
|
|
8
|
+
export * from './kafka-headers'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { revertObject } from '../utils'
|
|
2
|
+
|
|
3
|
+
export const KafkaHeadersMapping = {
|
|
4
|
+
X_BUTCH_SIZE: 'x-sys-kafka-butch-size',
|
|
5
|
+
X_BUTCH_KIND: 'x-sys-kafka-butch-kind',
|
|
6
|
+
X_BUTCH_LAST_ITEM_INDEX: 'x-sys-kafka-last-item-index',
|
|
7
|
+
X_BUTCH_FIRST_ITEM_INDEX: 'x-sys-kafka-first-item-index',
|
|
8
|
+
|
|
9
|
+
X_BUTCH_CHUNK_GROUP_ID: 'x-sys-kafka-butch-group-id',
|
|
10
|
+
X_BUTCH_CHUNK_INDEX: 'x-sys-kafka-butch-chunk-index',
|
|
11
|
+
X_BUTCH_LAST_CHUNK_INDEX: 'x-sys-kafka-butch-last-chunk-index',
|
|
12
|
+
} as const
|
|
13
|
+
|
|
14
|
+
export const KafkaHeadersReverted = revertObject(KafkaHeadersMapping)
|
|
15
|
+
|
|
16
|
+
export type KafkaHeadersKey = keyof typeof KafkaHeadersMapping
|
|
17
|
+
export type KafkaHeaders = (typeof KafkaHeadersMapping)[KafkaHeadersKey]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { revertObject } from '../utils'
|
|
2
|
+
|
|
3
|
+
export const ProtocolKindMapping = {
|
|
4
|
+
http: 1,
|
|
5
|
+
mq: 2,
|
|
6
|
+
ws: 3,
|
|
7
|
+
} as const
|
|
8
|
+
|
|
9
|
+
export const ProtocolKindReverted = revertObject(ProtocolKindMapping)
|
|
10
|
+
|
|
11
|
+
export type ProtocolKindKey = keyof typeof ProtocolKindMapping
|
|
12
|
+
export type ProtocolKind = (typeof ProtocolKindMapping)[ProtocolKindKey]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { revertObject } from '../utils'
|
|
2
|
+
|
|
3
|
+
export const ServiceListUrls = {
|
|
4
|
+
// sys
|
|
5
|
+
'alerts-service': 'ALERTS_SERVICE_URL',
|
|
6
|
+
'api-gw-service': 'API_GW_SERVICE_URL',
|
|
7
|
+
'toggle-service': 'TOGGLE_SERVICE_URL',
|
|
8
|
+
'jobs-service': 'JOBS_SERVICE_URL',
|
|
9
|
+
'manager-service': 'MANAGER_SERVICE_URL',
|
|
10
|
+
|
|
11
|
+
// business
|
|
12
|
+
'users-service': 'USERS_SERVICE_URL',
|
|
13
|
+
} as const
|
|
14
|
+
|
|
15
|
+
export const ServiceListUrlsReverted = revertObject(ServiceListUrls)
|
|
16
|
+
|
|
17
|
+
export type ServiceListUrlsKey = keyof typeof ServiceListUrls
|
|
18
|
+
export type ServiceListUrls = (typeof ServiceListUrls)[ServiceListUrlsKey]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { revertObject } from '../utils'
|
|
2
|
+
|
|
3
|
+
export const SessionKindMapping = {
|
|
4
|
+
jobs: 'J',
|
|
5
|
+
sys: 'S',
|
|
6
|
+
user: 'U',
|
|
7
|
+
device: 'D',
|
|
8
|
+
} as const
|
|
9
|
+
|
|
10
|
+
export type SessionKindKey = keyof typeof SessionKindMapping
|
|
11
|
+
export type SessionKind = (typeof SessionKindMapping)[SessionKindKey]
|
|
12
|
+
|
|
13
|
+
export const SessionKindReverted = revertObject(SessionKindMapping)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const DefaultTraceTags = {
|
|
2
|
+
// user
|
|
3
|
+
user_id: 'user.id',
|
|
4
|
+
user_organisation_id: 'user.organisation_id',
|
|
5
|
+
user_session_id: 'user.session_id',
|
|
6
|
+
role_ids: 'user.role_ids',
|
|
7
|
+
account_kind: 'user.user_account_kind',
|
|
8
|
+
|
|
9
|
+
// device
|
|
10
|
+
device_id: 'device.device_id',
|
|
11
|
+
device_session_id: 'device.session_id',
|
|
12
|
+
device_organisation_id: 'device.organisation_id',
|
|
13
|
+
|
|
14
|
+
// request
|
|
15
|
+
request_id: 'request.request_id',
|
|
16
|
+
request_ip: 'request.ip',
|
|
17
|
+
origin_ip: 'request.origin_ip',
|
|
18
|
+
|
|
19
|
+
// sys
|
|
20
|
+
sys_user_id: 'sys.user_id',
|
|
21
|
+
sys_service_name: 'sys.service_name',
|
|
22
|
+
|
|
23
|
+
// action
|
|
24
|
+
action_kind: 'action.kind',
|
|
25
|
+
action_protocol: 'action.protocol',
|
|
26
|
+
action_version: 'action.version',
|
|
27
|
+
action_method: 'action.method',
|
|
28
|
+
} as const
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export type ErrorKind =
|
|
2
|
+
| 'BadRequest'
|
|
3
|
+
| 'Conflict'
|
|
4
|
+
| 'Forbidden'
|
|
5
|
+
| 'Unauthorized'
|
|
6
|
+
| 'NotFound'
|
|
7
|
+
| 'Internal'
|
|
8
|
+
|
|
9
|
+
export type ErrorLayer = 'system' | 'business'
|
|
10
|
+
|
|
11
|
+
export type AppErrorOptions<TMeta = object> = {
|
|
12
|
+
type: ErrorKind
|
|
13
|
+
statusCode?: number
|
|
14
|
+
message?: string
|
|
15
|
+
layer: ErrorLayer
|
|
16
|
+
origin?: Error
|
|
17
|
+
meta?: TMeta
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type AppSpecificErrorOptions<TMeta = object> = Omit<AppErrorOptions<TMeta>, 'type' | 'layer'>
|
|
21
|
+
|
|
22
|
+
export type ErrorPayload<TMeta = object> = AppErrorOptions<TMeta> & {
|
|
23
|
+
textCode: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class AppError<C extends string, TMeta extends object | undefined = object>
|
|
27
|
+
extends Error
|
|
28
|
+
implements AppErrorOptions<TMeta>
|
|
29
|
+
{
|
|
30
|
+
public readonly textCode: string
|
|
31
|
+
public readonly statusCode?: number
|
|
32
|
+
public readonly type: ErrorKind
|
|
33
|
+
public readonly layer: ErrorLayer
|
|
34
|
+
|
|
35
|
+
public readonly origin?: Error
|
|
36
|
+
public readonly msg?: string
|
|
37
|
+
public readonly meta?: TMeta
|
|
38
|
+
|
|
39
|
+
constructor(textCode: C, options: AppErrorOptions<TMeta>) {
|
|
40
|
+
super(textCode)
|
|
41
|
+
this.textCode = textCode
|
|
42
|
+
this.type = options.type
|
|
43
|
+
this.layer = options.layer
|
|
44
|
+
this.statusCode = options.statusCode
|
|
45
|
+
|
|
46
|
+
this.name = new.target.name
|
|
47
|
+
this.msg = options.message
|
|
48
|
+
this.origin = options.origin
|
|
49
|
+
this.meta = options.meta
|
|
50
|
+
|
|
51
|
+
if (Error.captureStackTrace) {
|
|
52
|
+
Error.captureStackTrace(this, this.constructor)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
toJSON(): ErrorPayload<TMeta> {
|
|
57
|
+
return {
|
|
58
|
+
textCode: this.textCode,
|
|
59
|
+
origin: this.origin,
|
|
60
|
+
type: this.type,
|
|
61
|
+
message: this.msg,
|
|
62
|
+
meta: this.meta,
|
|
63
|
+
layer: this.layer,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export class BadRequest<
|
|
69
|
+
C extends string = string,
|
|
70
|
+
TMeta extends object | undefined = object,
|
|
71
|
+
> extends AppError<C, TMeta> {
|
|
72
|
+
constructor(textCode: C, options?: AppSpecificErrorOptions<TMeta>) {
|
|
73
|
+
super(textCode, { type: 'BadRequest', layer: 'business', ...options })
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export class Conflict<
|
|
78
|
+
C extends string = string,
|
|
79
|
+
TMeta extends object | undefined = object,
|
|
80
|
+
> extends AppError<C, TMeta> {
|
|
81
|
+
constructor(textCode: C, options?: AppSpecificErrorOptions<TMeta>) {
|
|
82
|
+
super(textCode, { type: 'Conflict', layer: 'business', ...options })
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export class Forbidden<
|
|
87
|
+
C extends string = string,
|
|
88
|
+
TMeta extends object | undefined = object,
|
|
89
|
+
> extends AppError<C, TMeta> {
|
|
90
|
+
constructor(textCode: C, options?: AppSpecificErrorOptions<TMeta>) {
|
|
91
|
+
super(textCode, { type: 'Forbidden', layer: 'business', ...options })
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class Unauthorized<
|
|
96
|
+
C extends string = string,
|
|
97
|
+
TMeta extends object | undefined = object,
|
|
98
|
+
> extends AppError<C, TMeta> {
|
|
99
|
+
constructor(textCode: C, options?: AppSpecificErrorOptions<TMeta>) {
|
|
100
|
+
super(textCode, { type: 'Unauthorized', layer: 'business', ...options })
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export class NotFound<
|
|
105
|
+
C extends string = string,
|
|
106
|
+
TMeta extends object | undefined = object,
|
|
107
|
+
> extends AppError<C, TMeta> {
|
|
108
|
+
constructor(textCode: C, options?: AppSpecificErrorOptions<TMeta>) {
|
|
109
|
+
super(textCode, { type: 'NotFound', layer: 'business', ...options })
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export class Internal<
|
|
114
|
+
C extends string = string,
|
|
115
|
+
TMeta extends object | undefined = object,
|
|
116
|
+
> extends AppError<C, TMeta> {
|
|
117
|
+
constructor(textCode: C, options?: AppSpecificErrorOptions<TMeta>) {
|
|
118
|
+
super(textCode, { type: 'Internal', layer: 'business', ...options })
|
|
119
|
+
}
|
|
120
|
+
}
|
package/src/index.ts
ADDED
package/src/jwt/index.ts
ADDED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './union-schema'
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { TLiteral, Type } from '@sinclair/typebox'
|
|
2
|
+
import { UserHeaders } from '../constants'
|
|
3
|
+
|
|
4
|
+
import type { TSchema, TUnion, Static } from '@sinclair/typebox'
|
|
5
|
+
|
|
6
|
+
export const UnionSchema = <T extends Record<string, string | number | symbol>>(
|
|
7
|
+
obj: T,
|
|
8
|
+
): TSchema => {
|
|
9
|
+
const literals = (Object.keys(obj) as Array<keyof T>).map((k) => Type.Literal(k as string))
|
|
10
|
+
return Type.Union(literals)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Nullable = <T extends TSchema>(
|
|
14
|
+
schema: T,
|
|
15
|
+
): TUnion<[ReturnType<typeof Type.Null>, T]> => {
|
|
16
|
+
return Type.Union([Type.Null(), schema])
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const LiteralUnion = <
|
|
20
|
+
TObject extends Record<string, any>,
|
|
21
|
+
TKey extends keyof TObject & string,
|
|
22
|
+
>(
|
|
23
|
+
obj: TObject,
|
|
24
|
+
): TUnion<[TLiteral<TKey>, ...TLiteral<TKey>[]]> => {
|
|
25
|
+
const keys = Object.keys(obj) as TKey[]
|
|
26
|
+
|
|
27
|
+
const literals = keys.map((key) => Type.Literal(key))
|
|
28
|
+
|
|
29
|
+
return Type.Union(literals as [TLiteral<TKey>, ...TLiteral<TKey>[]])
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const DateSchema = Type.Unsafe<Date>({ type: 'string', format: 'date-time' })
|
|
33
|
+
|
|
34
|
+
export const UserAuthHeadersSchema = Type.Object({
|
|
35
|
+
[UserHeaders.X_ORGANISATION_ID]: Type.String(),
|
|
36
|
+
[UserHeaders.X_USER_ID]: Type.String(),
|
|
37
|
+
[UserHeaders.X_SESSION_ID]: Type.String(),
|
|
38
|
+
[UserHeaders.X_ACCOUNT_KIND]: Type.Union([
|
|
39
|
+
Type.Literal('personal'),
|
|
40
|
+
Type.Literal('organisation'),
|
|
41
|
+
]),
|
|
42
|
+
[UserHeaders.X_ECOSYSTEM_DOMAIN]: Type.Union([Type.Literal('ERP')]),
|
|
43
|
+
[UserHeaders.X_ORIGIN_IP]: Type.String(),
|
|
44
|
+
[UserHeaders.X_ROLE_IDS]: Type.Union([Type.String(), Type.Null()]),
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
export type UserAuthHeaders = Static<typeof UserAuthHeadersSchema>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AccountKindKey, EcosystemKindKey, SessionKindKey } from '../constants'
|
|
2
|
+
|
|
3
|
+
export interface JwtRefreshPayload {
|
|
4
|
+
sessionId: string
|
|
5
|
+
version: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface JwtAccessPayload extends JwtRefreshPayload {
|
|
9
|
+
user_id: string
|
|
10
|
+
session_id: string
|
|
11
|
+
account_kind: AccountKindKey
|
|
12
|
+
session_kind: SessionKindKey
|
|
13
|
+
ecosystem_kind: EcosystemKindKey
|
|
14
|
+
organisation_id: string
|
|
15
|
+
subdivisions_ids: string[] | null
|
|
16
|
+
role_ids: string[]
|
|
17
|
+
expired_at: string
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type ContainKind = 'include' | 'exclude'
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { DatasetEnumItem } from '@x-fiber-sys/contract'
|
|
2
|
+
|
|
3
|
+
export const createObjectEnum = <T extends Record<string, number>, K extends keyof T>(
|
|
4
|
+
object: T,
|
|
5
|
+
): DatasetEnumItem<T, K>[] => {
|
|
6
|
+
return Object.entries(object).map(([k, v]) => {
|
|
7
|
+
return { key: k, value: v }
|
|
8
|
+
}) as DatasetEnumItem<T, K>[]
|
|
9
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const getStrictnessFields = <T = object>(fields: T): Partial<T> => {
|
|
2
|
+
const updatedFields: Partial<T> = {}
|
|
3
|
+
|
|
4
|
+
for (const key in fields) {
|
|
5
|
+
const val = fields[key]
|
|
6
|
+
if (val !== undefined && val !== null) {
|
|
7
|
+
updatedFields[key] = val
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return updatedFields
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type Reverted<T extends Record<string, number | string>> = {
|
|
2
|
+
[K in keyof T as T[K]]: K
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export const revertObject = <TObject extends Record<string, number | string>>(object: TObject) =>
|
|
6
|
+
Object.entries(object).reduce<Reverted<TObject>>(
|
|
7
|
+
(prev, [key, value]) => ({
|
|
8
|
+
...prev,
|
|
9
|
+
[value]: key,
|
|
10
|
+
}),
|
|
11
|
+
{} as Reverted<TObject>,
|
|
12
|
+
)
|