forked from jeremydaly/dynamodb-toolbox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.ts
152 lines (129 loc) · 4.32 KB
/
utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
151
152
import { A, L } from 'ts-toolbelt'
import { PureAttributeDefinition } from '../classes/Entity/types.js'
import { DynamoDBTypes, DynamoDBKeyTypes } from '../classes/Table/types.js'
import { unmarshall } from '@aws-sdk/util-dynamodb'
export const validTypes: DynamoDBTypes[] = [
'string',
'boolean',
'number',
'bigint',
'list',
'map',
'binary',
'set'
]
export const validKeyTypes: DynamoDBKeyTypes[] = ['string', 'number', 'bigint', 'binary']
export const isDynamoDbType = (value: string): value is DynamoDBTypes =>
validTypes.includes(value as DynamoDBTypes)
export const isDynamoDbKeyType = (value: string): value is DynamoDBKeyTypes =>
validKeyTypes.includes(value as DynamoDBKeyTypes)
// Boolean conversion
export const toBool = (val: any) =>
typeof val === 'boolean'
? val
: ['false', '0', 'no'].includes(String(val).toLowerCase())
? false
: Boolean(val)
export const toDynamoBigInt = (value: bigint) =>
unmarshall({ valueToUnmarshall: {N: value.toString()} }, { wrapNumbers: true }).valueToUnmarshall.value
// has value shortcut
export const hasValue = (val: any) => val !== undefined && val !== null
// isEmpty object shortcut
export const isEmpty = (val: any) =>
val === undefined || (typeof val === 'object' && Object.keys(val).length === 0)
// Inline error handler
export const error = (err: string) => {
throw new Error(err)
}
// Standard type error
export const typeError = (field: string) => {
error(
`Invalid or missing type for '${field}'. ` +
`Valid types are '${validTypes.slice(0, -1).join(`', '`)}',` +
` and '${validTypes.slice(-1)}'.`
)
}
// Key type error
export const keyTypeError = (field: string) => {
error(
`Invalid or missing type for '${field}'. ` +
`Valid types for partitionKey and sortKey are 'string','number' and 'binary'`
)
}
// Condition error
export const conditionError = (op: string) =>
error(`You can only supply one sortKey condition per query. Already using '${op}'`)
// Transform attribute values
export const transformAttr = (mapping: PureAttributeDefinition, value: any, data: {}) => {
value = mapping.transform ? mapping.transform(value, data) : value
return mapping.prefix || mapping.suffix
? `${mapping.prefix || ''}${value}${mapping.suffix || ''}`
: value
}
export function typeOf(data?: any) {
if (data === null && typeof data === 'object') {
return 'null'
} else if (data !== undefined && isBinary(data)) {
return 'Binary'
} else if (data !== undefined && data.constructor) {
return data.constructor.name.toLowerCase()
} else if (data !== undefined && typeof data === 'object') {
// this object is the result of Object.create(null), hence the absence of a
// defined constructor
return 'Object'
} else {
return 'undefined'
}
}
export function isArrayOfSameType<T>(array: Array<T>): boolean {
const length = array.length
if (length <= 1) {
return true
}
const firstType = typeOf(array[0])
return array.slice(1).every((el: T) => typeOf(el) === firstType)
}
export function isBinary(data: any): boolean {
const binaryTypes = [
'ArrayBuffer',
'Blob',
'Buffer',
'DataView',
'File',
'Int8Array',
'Uint8Array',
'Uint8ClampedArray',
'Int16Array',
'Uint16Array',
'Int32Array',
'Uint32Array',
'Float32Array',
'Float64Array',
'BigInt64Array',
'BigUint64Array',
]
if (data?.constructor) {
return binaryTypes.includes(data.constructor.name)
}
return false
}
// Type now exists in ts-toolbelt but requires upgrading ts: See https://github.com/millsp/ts-toolbelt/issues/169
export type If<C extends 0 | 1, T, E = never> = C extends 1 ? (1 extends C ? T : E) : E
export type FirstDefined<List extends L.List> = {
stopNone: undefined
stopOne: L.Head<List>
continue: FirstDefined<L.Tail<List>>
}[A.Cast<
If<A.Equals<List, []>, 'stopNone', If<A.Equals<L.Head<List>, undefined>, 'continue', 'stopOne'>>,
'stopNone' | 'stopOne' | 'continue'
>]
// ts-toolbelt A.Compute has issues: A.Compute<any[]> returns { [key:string]: any } and A.Compute<unknown> returns {}
export type Compute<A> = A extends Promise<infer T>
? Promise<Compute<T>>
: A extends (...args: infer P) => infer R
? (...args: Compute<P>) => Compute<R>
: A extends Set<infer V>
? Set<Compute<V>>
: A extends object
? { [key in keyof A]: Compute<A[key]> }
: A