rn-erxes-sdk 0.1.24 → 0.1.26
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 +322 -48
- package/lib/commonjs/App.js +5 -6
- package/lib/commonjs/App.js.map +1 -1
- package/lib/commonjs/assets/images/index.js +1 -1
- package/lib/commonjs/assets/images/index.js.map +1 -1
- package/lib/commonjs/assets/images/x.png +0 -0
- package/lib/commonjs/components/InputTools.js +3 -2
- package/lib/commonjs/components/InputTools.js.map +1 -1
- package/lib/commonjs/screen/home/Home.js +62 -5
- package/lib/commonjs/screen/home/Home.js.map +1 -1
- package/lib/commonjs/utils/objectId.js +61 -0
- package/lib/commonjs/utils/objectId.js.map +1 -0
- package/lib/module/App.js +5 -6
- package/lib/module/App.js.map +1 -1
- package/lib/module/assets/images/index.js +1 -1
- package/lib/module/assets/images/index.js.map +1 -1
- package/lib/module/assets/images/x.png +0 -0
- package/lib/module/components/InputTools.js +3 -2
- package/lib/module/components/InputTools.js.map +1 -1
- package/lib/module/screen/home/Home.js +63 -6
- package/lib/module/screen/home/Home.js.map +1 -1
- package/lib/module/utils/objectId.js +53 -0
- package/lib/module/utils/objectId.js.map +1 -0
- package/lib/typescript/components/InputTools.d.ts.map +1 -1
- package/lib/typescript/screen/home/Home.d.ts.map +1 -1
- package/lib/typescript/utils/objectId.d.ts +3 -0
- package/lib/typescript/utils/objectId.d.ts.map +1 -0
- package/package.json +5 -3
- package/src/App.tsx +6 -6
- package/src/assets/images/index.ts +1 -1
- package/src/assets/images/x.png +0 -0
- package/src/components/InputTools.tsx +2 -1
- package/src/screen/home/Home.tsx +73 -2
- package/src/utils/objectId.ts +59 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/* eslint-disable no-bitwise */
|
|
2
|
+
// Lightweight, React Native-compatible replacement for `bson`'s `ObjectId`.
|
|
3
|
+
//
|
|
4
|
+
// The SDK only ever used `new ObjectId().toString()` to mint a `visitorId`,
|
|
5
|
+
// which the erxes backend accepts as a plain `String`. Importing the whole
|
|
6
|
+
// `bson` package for this pulls in `bson/lib/bson.mjs`, whose top-level
|
|
7
|
+
// `await` crashes Metro/Hermes ("await is not defined") once Expo resolves
|
|
8
|
+
// packages through the ESM `exports` map.
|
|
9
|
+
//
|
|
10
|
+
// This helper returns the exact same shape an ObjectId stringifies to:
|
|
11
|
+
// 24 lowercase hexadecimal characters (a 12-byte value made of a 4-byte
|
|
12
|
+
// big-endian timestamp followed by 8 random bytes).
|
|
13
|
+
|
|
14
|
+
const toHex = bytes => {
|
|
15
|
+
let out = '';
|
|
16
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
17
|
+
const byte = bytes[i];
|
|
18
|
+
out += byte.toString(16).padStart(2, '0');
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
};
|
|
22
|
+
const getRandomBytes = length => {
|
|
23
|
+
const bytes = new Uint8Array(length);
|
|
24
|
+
|
|
25
|
+
// `react-native-get-random-values` (imported by the SDK entry point)
|
|
26
|
+
// polyfills `crypto.getRandomValues` on device. Fall back to `Math.random`
|
|
27
|
+
// so the helper still works in environments without the polyfill (e.g. Jest).
|
|
28
|
+
const cryptoObj = typeof globalThis !== 'undefined' ? globalThis.crypto : undefined;
|
|
29
|
+
if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {
|
|
30
|
+
cryptoObj.getRandomValues(bytes);
|
|
31
|
+
return bytes;
|
|
32
|
+
}
|
|
33
|
+
for (let i = 0; i < length; i++) {
|
|
34
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
35
|
+
}
|
|
36
|
+
return bytes;
|
|
37
|
+
};
|
|
38
|
+
export const createObjectIdLikeString = () => {
|
|
39
|
+
const bytes = new Uint8Array(12);
|
|
40
|
+
|
|
41
|
+
// 4-byte big-endian timestamp (seconds since epoch), like a real ObjectId.
|
|
42
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
43
|
+
bytes[0] = timestamp >> 24 & 0xff;
|
|
44
|
+
bytes[1] = timestamp >> 16 & 0xff;
|
|
45
|
+
bytes[2] = timestamp >> 8 & 0xff;
|
|
46
|
+
bytes[3] = timestamp & 0xff;
|
|
47
|
+
|
|
48
|
+
// Remaining 8 bytes from a CSPRNG when available.
|
|
49
|
+
bytes.set(getRandomBytes(8), 4);
|
|
50
|
+
return toHex(bytes);
|
|
51
|
+
};
|
|
52
|
+
export default createObjectIdLikeString;
|
|
53
|
+
//# sourceMappingURL=objectId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["toHex","bytes","out","i","length","byte","toString","padStart","getRandomBytes","Uint8Array","cryptoObj","globalThis","crypto","undefined","getRandomValues","Math","floor","random","createObjectIdLikeString","timestamp","Date","now","set"],"sourceRoot":"../../../src","sources":["utils/objectId.ts"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,MAAMA,KAAK,GAAIC,KAAiB,IAAa;EAC3C,IAAIC,GAAG,GAAG,EAAE;EACZ,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGF,KAAK,CAACG,MAAM,EAAED,CAAC,EAAE,EAAE;IACrC,MAAME,IAAI,GAAGJ,KAAK,CAACE,CAAC,CAAW;IAC/BD,GAAG,IAAIG,IAAI,CAACC,QAAQ,CAAC,EAAE,CAAC,CAACC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;EAC3C;EACA,OAAOL,GAAG;AACZ,CAAC;AAED,MAAMM,cAAc,GAAIJ,MAAc,IAAiB;EACrD,MAAMH,KAAK,GAAG,IAAIQ,UAAU,CAACL,MAAM,CAAC;;EAEpC;EACA;EACA;EACA,MAAMM,SAAS,GACb,OAAOC,UAAU,KAAK,WAAW,GAAIA,UAAU,CAASC,MAAM,GAAGC,SAAS;EAE5E,IAAIH,SAAS,IAAI,OAAOA,SAAS,CAACI,eAAe,KAAK,UAAU,EAAE;IAChEJ,SAAS,CAACI,eAAe,CAACb,KAAK,CAAC;IAChC,OAAOA,KAAK;EACd;EAEA,KAAK,IAAIE,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGC,MAAM,EAAED,CAAC,EAAE,EAAE;IAC/BF,KAAK,CAACE,CAAC,CAAC,GAAGY,IAAI,CAACC,KAAK,CAACD,IAAI,CAACE,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC;EAC5C;EACA,OAAOhB,KAAK;AACd,CAAC;AAED,OAAO,MAAMiB,wBAAwB,GAAGA,CAAA,KAAc;EACpD,MAAMjB,KAAK,GAAG,IAAIQ,UAAU,CAAC,EAAE,CAAC;;EAEhC;EACA,MAAMU,SAAS,GAAGJ,IAAI,CAACC,KAAK,CAACI,IAAI,CAACC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;EAC/CpB,KAAK,CAAC,CAAC,CAAC,GAAIkB,SAAS,IAAI,EAAE,GAAI,IAAI;EACnClB,KAAK,CAAC,CAAC,CAAC,GAAIkB,SAAS,IAAI,EAAE,GAAI,IAAI;EACnClB,KAAK,CAAC,CAAC,CAAC,GAAIkB,SAAS,IAAI,CAAC,GAAI,IAAI;EAClClB,KAAK,CAAC,CAAC,CAAC,GAAGkB,SAAS,GAAG,IAAI;;EAE3B;EACAlB,KAAK,CAACqB,GAAG,CAACd,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;EAE/B,OAAOR,KAAK,CAACC,KAAK,CAAC;AACrB,CAAC;AAED,eAAeiB,wBAAwB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InputTools.d.ts","sourceRoot":"","sources":["../../../src/components/InputTools.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+C,MAAM,OAAO,CAAC;AAoCpE,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,
|
|
1
|
+
{"version":3,"file":"InputTools.d.ts","sourceRoot":"","sources":["../../../src/components/InputTools.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+C,MAAM,OAAO,CAAC;AAoCpE,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,CAwO7B,CAAC;AA0IF,eAAe,UAAU,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Home.d.ts","sourceRoot":"","sources":["../../../../src/screen/home/Home.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Home.d.ts","sourceRoot":"","sources":["../../../../src/screen/home/Home.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAkB1C,QAAA,MAAM,IAAI,yBA8MT,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"objectId.d.ts","sourceRoot":"","sources":["../../../src/utils/objectId.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,wBAAwB,QAAO,MAc3C,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rn-erxes-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"description": "react-native erxes sdk",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -40,7 +40,10 @@
|
|
|
40
40
|
"ios",
|
|
41
41
|
"android"
|
|
42
42
|
],
|
|
43
|
-
"repository":
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/erxes/rn-erxes-sdk.git"
|
|
46
|
+
},
|
|
44
47
|
"author": "Munkh-orgil <monkhorgilbayarbaatar@gmail.com> (https://github.com/Munkhorgilb)",
|
|
45
48
|
"license": "MIT",
|
|
46
49
|
"bugs": {
|
|
@@ -169,7 +172,6 @@
|
|
|
169
172
|
},
|
|
170
173
|
"dependencies": {
|
|
171
174
|
"@apollo/client": "^3.14.1",
|
|
172
|
-
"bson": "^5.3.0",
|
|
173
175
|
"dayjs": "^1.11.8",
|
|
174
176
|
"graphql": "^16.6.0",
|
|
175
177
|
"graphql-ws": "^5.13.1",
|
package/src/App.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import 'react-native-get-random-values';
|
|
|
2
2
|
import React, { useEffect } from 'react';
|
|
3
3
|
import Widget from './Widget';
|
|
4
4
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
5
|
-
import {
|
|
5
|
+
import { createObjectIdLikeString } from './utils/objectId';
|
|
6
6
|
import ApolloContainer from './graphql/ApolloContainer';
|
|
7
7
|
|
|
8
8
|
export type PropTypes = {
|
|
@@ -44,7 +44,7 @@ const ErxesSDK: React.FC<PropTypes> = ({
|
|
|
44
44
|
const [show, setShow] = React.useState<boolean>(showWidget);
|
|
45
45
|
|
|
46
46
|
useEffect(() => {
|
|
47
|
-
let visitorId:
|
|
47
|
+
let visitorId: string | undefined;
|
|
48
48
|
let tempCustomerId = '';
|
|
49
49
|
setLoading(true);
|
|
50
50
|
AsyncStorage.getItem('cachedCustomerId')
|
|
@@ -53,12 +53,12 @@ const ErxesSDK: React.FC<PropTypes> = ({
|
|
|
53
53
|
tempCustomerId = JSON.parse(value);
|
|
54
54
|
}
|
|
55
55
|
if (!tempCustomerId) {
|
|
56
|
-
//
|
|
57
|
-
visitorId =
|
|
56
|
+
// mint a guest visitor id (24-char hex, same shape as a Mongo ObjectId)
|
|
57
|
+
visitorId = createObjectIdLikeString();
|
|
58
58
|
}
|
|
59
59
|
setConnection({
|
|
60
60
|
cachedCustomerId: tempCustomerId ? tempCustomerId : null,
|
|
61
|
-
visitorId: visitorId
|
|
61
|
+
visitorId: visitorId,
|
|
62
62
|
});
|
|
63
63
|
AsyncStorage.getItem('conversationId')
|
|
64
64
|
.then((v) => {
|
|
@@ -75,7 +75,7 @@ const ErxesSDK: React.FC<PropTypes> = ({
|
|
|
75
75
|
.catch((e) => {
|
|
76
76
|
setConnection({
|
|
77
77
|
cachedCustomerId: null,
|
|
78
|
-
visitorId:
|
|
78
|
+
visitorId: createObjectIdLikeString(),
|
|
79
79
|
});
|
|
80
80
|
setLoading(false);
|
|
81
81
|
console.log('Failed on cachedCustomerId', e.message);
|
|
Binary file
|
|
@@ -42,6 +42,7 @@ const InputTools: React.FC<any> = (props: any) => {
|
|
|
42
42
|
subDomain,
|
|
43
43
|
persistentMenus,
|
|
44
44
|
sending = false,
|
|
45
|
+
placeholder = 'Reply...',
|
|
45
46
|
} = props;
|
|
46
47
|
|
|
47
48
|
const [input, onInput] = useState<string>('');
|
|
@@ -205,7 +206,7 @@ const InputTools: React.FC<any> = (props: any) => {
|
|
|
205
206
|
) : null}
|
|
206
207
|
|
|
207
208
|
<TextInput
|
|
208
|
-
placeholder={
|
|
209
|
+
placeholder={placeholder}
|
|
209
210
|
selectionColor={bgColor}
|
|
210
211
|
underlineColorAndroid={'transparent'}
|
|
211
212
|
style={styles.textInput}
|
package/src/screen/home/Home.tsx
CHANGED
|
@@ -8,9 +8,11 @@ import {
|
|
|
8
8
|
Platform,
|
|
9
9
|
StatusBar,
|
|
10
10
|
ScrollView,
|
|
11
|
+
KeyboardAvoidingView,
|
|
11
12
|
} from 'react-native';
|
|
12
13
|
import React, { useContext } from 'react';
|
|
13
14
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
15
|
+
import { useMutation } from '@apollo/client';
|
|
14
16
|
import AppContext from '../../context/Context';
|
|
15
17
|
import Supporters from '../greetings/Supporters';
|
|
16
18
|
import Social from '../greetings/Social';
|
|
@@ -20,6 +22,8 @@ import {
|
|
|
20
22
|
AVAILABILITY_MESSAGE,
|
|
21
23
|
formatOnlineHours,
|
|
22
24
|
} from '../../utils/onlineHours';
|
|
25
|
+
import InputTools from '../../components/InputTools';
|
|
26
|
+
import { widgetsInsertMessage } from '../../graphql/mutation';
|
|
23
27
|
|
|
24
28
|
const LOGO_CHIP = 36;
|
|
25
29
|
|
|
@@ -35,12 +39,20 @@ const Home = () => {
|
|
|
35
39
|
textColor,
|
|
36
40
|
logoUrl,
|
|
37
41
|
integrationId,
|
|
42
|
+
customerId,
|
|
43
|
+
visitorId,
|
|
38
44
|
showWidget,
|
|
39
45
|
setShow,
|
|
40
46
|
setConversationId,
|
|
41
47
|
backIcon,
|
|
48
|
+
sendIcon,
|
|
49
|
+
subDomain,
|
|
42
50
|
} = value;
|
|
43
51
|
|
|
52
|
+
const [sendMutation] = useMutation(widgetsInsertMessage);
|
|
53
|
+
const [sending, setSending] = React.useState(false);
|
|
54
|
+
const sendingRef = React.useRef(false);
|
|
55
|
+
|
|
44
56
|
const greetingMessages = greetings?.messages?.greetings;
|
|
45
57
|
const title = greetingMessages?.title || 'How can we help?';
|
|
46
58
|
const message =
|
|
@@ -58,6 +70,48 @@ const Home = () => {
|
|
|
58
70
|
setConversationId('');
|
|
59
71
|
};
|
|
60
72
|
|
|
73
|
+
const onSend = (text: string, attachments?: any[]) => {
|
|
74
|
+
if (sendingRef.current) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
sendingRef.current = true;
|
|
79
|
+
setSending(true);
|
|
80
|
+
|
|
81
|
+
sendMutation({
|
|
82
|
+
variables: {
|
|
83
|
+
integrationId,
|
|
84
|
+
customerId,
|
|
85
|
+
visitorId,
|
|
86
|
+
conversationId: null,
|
|
87
|
+
contentType: 'text',
|
|
88
|
+
message: text,
|
|
89
|
+
attachments:
|
|
90
|
+
attachments && attachments.length > 0 ? attachments : undefined,
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
.then((res: any) => {
|
|
94
|
+
if (res.errors) {
|
|
95
|
+
return console.log(res.errors);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const insertedMessage = res.data.widgetsInsertMessage;
|
|
99
|
+
const nextConversationId = insertedMessage?.conversationId;
|
|
100
|
+
|
|
101
|
+
if (nextConversationId) {
|
|
102
|
+
AsyncStorage.setItem('conversationId', nextConversationId);
|
|
103
|
+
setConversationId(nextConversationId);
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
.catch((err: any) => {
|
|
107
|
+
console.log(err);
|
|
108
|
+
})
|
|
109
|
+
.finally(() => {
|
|
110
|
+
sendingRef.current = false;
|
|
111
|
+
setSending(false);
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
|
|
61
115
|
const renderLogoButton = () => {
|
|
62
116
|
if (!showWidget && !hasBack) {
|
|
63
117
|
return <View style={{ width: LOGO_CHIP, height: LOGO_CHIP }} />;
|
|
@@ -86,10 +140,15 @@ const Home = () => {
|
|
|
86
140
|
Platform.OS === 'android' ? StatusBar.currentHeight || 0 : 0;
|
|
87
141
|
|
|
88
142
|
return (
|
|
89
|
-
<
|
|
143
|
+
<KeyboardAvoidingView
|
|
144
|
+
style={styles.root}
|
|
145
|
+
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
|
146
|
+
>
|
|
90
147
|
<ScrollView
|
|
148
|
+
style={styles.scroll}
|
|
91
149
|
showsVerticalScrollIndicator={false}
|
|
92
150
|
contentContainerStyle={styles.scrollContent}
|
|
151
|
+
keyboardShouldPersistTaps="handled"
|
|
93
152
|
>
|
|
94
153
|
{/* Hero */}
|
|
95
154
|
<SafeAreaView style={[styles.hero, { backgroundColor: bgColor }]}>
|
|
@@ -164,7 +223,16 @@ const Home = () => {
|
|
|
164
223
|
</View>
|
|
165
224
|
</View>
|
|
166
225
|
</ScrollView>
|
|
167
|
-
|
|
226
|
+
<InputTools
|
|
227
|
+
onSend={onSend}
|
|
228
|
+
bgColor={bgColor}
|
|
229
|
+
sendIcon={sendIcon}
|
|
230
|
+
subDomain={subDomain}
|
|
231
|
+
sending={sending}
|
|
232
|
+
persistentMenus={greetings?.persistentMenus}
|
|
233
|
+
placeholder="Start a new conversation..."
|
|
234
|
+
/>
|
|
235
|
+
</KeyboardAvoidingView>
|
|
168
236
|
);
|
|
169
237
|
};
|
|
170
238
|
|
|
@@ -175,6 +243,9 @@ const styles = StyleSheet.create({
|
|
|
175
243
|
flex: 1,
|
|
176
244
|
backgroundColor: messengerTheme.colors.background,
|
|
177
245
|
},
|
|
246
|
+
scroll: {
|
|
247
|
+
flex: 1,
|
|
248
|
+
},
|
|
178
249
|
scrollContent: {
|
|
179
250
|
paddingBottom: messengerTheme.spacing.xl,
|
|
180
251
|
},
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/* eslint-disable no-bitwise */
|
|
2
|
+
// Lightweight, React Native-compatible replacement for `bson`'s `ObjectId`.
|
|
3
|
+
//
|
|
4
|
+
// The SDK only ever used `new ObjectId().toString()` to mint a `visitorId`,
|
|
5
|
+
// which the erxes backend accepts as a plain `String`. Importing the whole
|
|
6
|
+
// `bson` package for this pulls in `bson/lib/bson.mjs`, whose top-level
|
|
7
|
+
// `await` crashes Metro/Hermes ("await is not defined") once Expo resolves
|
|
8
|
+
// packages through the ESM `exports` map.
|
|
9
|
+
//
|
|
10
|
+
// This helper returns the exact same shape an ObjectId stringifies to:
|
|
11
|
+
// 24 lowercase hexadecimal characters (a 12-byte value made of a 4-byte
|
|
12
|
+
// big-endian timestamp followed by 8 random bytes).
|
|
13
|
+
|
|
14
|
+
const toHex = (bytes: Uint8Array): string => {
|
|
15
|
+
let out = '';
|
|
16
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
17
|
+
const byte = bytes[i] as number;
|
|
18
|
+
out += byte.toString(16).padStart(2, '0');
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const getRandomBytes = (length: number): Uint8Array => {
|
|
24
|
+
const bytes = new Uint8Array(length);
|
|
25
|
+
|
|
26
|
+
// `react-native-get-random-values` (imported by the SDK entry point)
|
|
27
|
+
// polyfills `crypto.getRandomValues` on device. Fall back to `Math.random`
|
|
28
|
+
// so the helper still works in environments without the polyfill (e.g. Jest).
|
|
29
|
+
const cryptoObj =
|
|
30
|
+
typeof globalThis !== 'undefined' ? (globalThis as any).crypto : undefined;
|
|
31
|
+
|
|
32
|
+
if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {
|
|
33
|
+
cryptoObj.getRandomValues(bytes);
|
|
34
|
+
return bytes;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < length; i++) {
|
|
38
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
39
|
+
}
|
|
40
|
+
return bytes;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const createObjectIdLikeString = (): string => {
|
|
44
|
+
const bytes = new Uint8Array(12);
|
|
45
|
+
|
|
46
|
+
// 4-byte big-endian timestamp (seconds since epoch), like a real ObjectId.
|
|
47
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
48
|
+
bytes[0] = (timestamp >> 24) & 0xff;
|
|
49
|
+
bytes[1] = (timestamp >> 16) & 0xff;
|
|
50
|
+
bytes[2] = (timestamp >> 8) & 0xff;
|
|
51
|
+
bytes[3] = timestamp & 0xff;
|
|
52
|
+
|
|
53
|
+
// Remaining 8 bytes from a CSPRNG when available.
|
|
54
|
+
bytes.set(getRandomBytes(8), 4);
|
|
55
|
+
|
|
56
|
+
return toHex(bytes);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default createObjectIdLikeString;
|