react-native-newinstance-chat 0.2.0 → 0.4.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/NewinstanceChat.podspec +1 -1
- package/README.md +263 -18
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/newinstancechat/NewinstanceChatModule.kt +73 -6
- package/ios/NewinstanceChat.mm +7 -0
- package/ios/NewinstanceChatImpl.swift +48 -6
- package/lib/module/NativeNewinstanceChat.js.map +1 -1
- package/lib/module/index.js +49 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeNewinstanceChat.d.ts +1 -0
- package/lib/typescript/src/NativeNewinstanceChat.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +34 -2
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/NativeNewinstanceChat.ts +1 -0
- package/src/index.tsx +63 -3
- package/src/types.ts +48 -2
package/NewinstanceChat.podspec
CHANGED
|
@@ -18,7 +18,7 @@ Pod::Spec.new do |s|
|
|
|
18
18
|
s.pod_target_xcconfig = { "DEFINES_MODULE" => "YES" }
|
|
19
19
|
|
|
20
20
|
# Native chat SDK we wrap, published on CocoaPods Trunk.
|
|
21
|
-
s.dependency "LiveAndAiChat", "~> 0.
|
|
21
|
+
s.dependency "LiveAndAiChat", "~> 0.4.0"
|
|
22
22
|
|
|
23
23
|
install_modules_dependencies(s)
|
|
24
24
|
end
|
package/README.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# react-native-newinstance-chat
|
|
2
2
|
|
|
3
|
-
React Native bindings for [LiveAndAiChat](https://newinstance.cloud)
|
|
3
|
+
React Native bindings for [LiveAndAiChat](https://newinstance.cloud). Drops a
|
|
4
4
|
complete AI + live-agent chat experience into your iOS and Android RN app
|
|
5
5
|
via native module bridges.
|
|
6
6
|
|
|
7
7
|
The chat UI, transport, attachments, image cache, typing indicators, and
|
|
8
8
|
silent gap-fill resync all come from the native iOS / Android SDKs. This
|
|
9
|
-
package is a thin TurboModule surface over them
|
|
9
|
+
package is a thin TurboModule surface over them, so your React Native code
|
|
10
10
|
stays small.
|
|
11
11
|
|
|
12
12
|
## Requirements
|
|
13
13
|
|
|
14
|
-
- React Native 0.74+ (TurboModule support
|
|
14
|
+
- React Native 0.74+ (TurboModule support; 0.85+ recommended)
|
|
15
15
|
- iOS 15.1+
|
|
16
16
|
- Android API 24+, JDK 17
|
|
17
17
|
|
|
@@ -45,7 +45,7 @@ cd ios && pod install
|
|
|
45
45
|
### Android
|
|
46
46
|
|
|
47
47
|
Autolink registers `NewinstanceChatPackage`. The underlying
|
|
48
|
-
`cloud.newinstance:liveandaichat` AAR is resolved from Maven Central
|
|
48
|
+
`cloud.newinstance:liveandaichat` AAR is resolved from Maven Central, so
|
|
49
49
|
ensure `mavenCentral()` is in `settings.gradle.kts`.
|
|
50
50
|
|
|
51
51
|
## Quick start
|
|
@@ -54,26 +54,212 @@ ensure `mavenCentral()` is in `settings.gradle.kts`.
|
|
|
54
54
|
import { NewinstanceChat } from 'react-native-newinstance-chat';
|
|
55
55
|
|
|
56
56
|
const sdk = new NewinstanceChat({
|
|
57
|
-
|
|
57
|
+
customerToken: tokenFromYourBackend,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
await sdk.ready;
|
|
61
|
+
await sdk.initialize();
|
|
62
|
+
|
|
63
|
+
sdk.openChat();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`sdk.ready` resolves once the native module is configured. It rejects with
|
|
67
|
+
an error whose `code` is `E_CONFIG` when the configuration is invalid, and
|
|
68
|
+
`initialize()` rejects with the same error.
|
|
69
|
+
|
|
70
|
+
The chat screen renders natively, so there is no React component to mount.
|
|
71
|
+
Your RN UI is whatever button, banner or FAB triggers `openChat()`.
|
|
72
|
+
|
|
73
|
+
## Authentication
|
|
74
|
+
|
|
75
|
+
The SDK runs in exactly one of two modes. Pass `customerToken` **or**
|
|
76
|
+
`apiKey`, never both. Passing both, or neither, rejects `ready` with
|
|
77
|
+
`E_CONFIG` and the message
|
|
78
|
+
`pass either apiKey (keyId:secret) or customerToken`. Blank strings count
|
|
79
|
+
as absent.
|
|
80
|
+
|
|
81
|
+
### JWT mode (`customerToken`), for apps you ship to the public
|
|
82
|
+
|
|
83
|
+
Your backend mints a short-lived chat token for the signed-in customer and
|
|
84
|
+
hands it to the app. The native SDK sends it as `Authorization: Bearer <token>`
|
|
85
|
+
on every request. The customer's identity comes only from the token's signed
|
|
86
|
+
claims, so a tampered app cannot claim to be someone else, and your API key
|
|
87
|
+
never leaves your server.
|
|
88
|
+
|
|
89
|
+
Mint the token on your backend, on the REST API host
|
|
90
|
+
(`baseUrl` below is `https://api.newinstance.cloud`), with your full
|
|
91
|
+
`keyId:secret` key. Server side only:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
POST {baseUrl}/api/v1/chat/sessions
|
|
95
|
+
x-api-key: sk_live_abc123:YOUR_SECRET_KEY
|
|
96
|
+
Content-Type: application/json
|
|
97
|
+
|
|
98
|
+
{
|
|
99
|
+
"customerId": "usr_123",
|
|
100
|
+
"customerName": "Ada Lovelace",
|
|
101
|
+
"customerEmail": "ada@example.com",
|
|
102
|
+
"customerPhone": "+44 20 7946 0958",
|
|
103
|
+
"metadata": { "plan": "enterprise" },
|
|
104
|
+
"expiresInSeconds": 3600
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`customerId` and `customerName` are required; `customerEmail`,
|
|
109
|
+
`customerPhone`, `metadata` and `expiresInSeconds` are optional. The
|
|
110
|
+
response is:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"token": "eyJhbGciOi...",
|
|
115
|
+
"expiresAt": "2026-09-29T13:00:00.000Z",
|
|
116
|
+
"expiresInSeconds": 3600,
|
|
117
|
+
"session": { "sessionId": "...", "customerId": "usr_123", "status": "active" }
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
For example, a Node backend route the app calls after sign-in:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
app.post('/chat-token', requireSignedInUser, async (req, res) => {
|
|
125
|
+
const response = await fetch(
|
|
126
|
+
'https://api.newinstance.cloud/api/v1/chat/sessions',
|
|
127
|
+
{
|
|
128
|
+
method: 'POST',
|
|
129
|
+
headers: {
|
|
130
|
+
'content-type': 'application/json',
|
|
131
|
+
'x-api-key': process.env.NEWINSTANCE_API_KEY,
|
|
132
|
+
},
|
|
133
|
+
body: JSON.stringify({
|
|
134
|
+
customerId: req.user.id,
|
|
135
|
+
customerName: req.user.name,
|
|
136
|
+
customerEmail: req.user.email,
|
|
137
|
+
expiresInSeconds: 3600,
|
|
138
|
+
}),
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
const { token, expiresAt } = await response.json();
|
|
142
|
+
res.json({ token, expiresAt });
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Hand `token` to the app and pass it in the config:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
const { token } = await fetch('https://your-backend.example.com/chat-token', {
|
|
150
|
+
method: 'POST',
|
|
151
|
+
}).then((r) => r.json());
|
|
152
|
+
|
|
153
|
+
const sdk = new NewinstanceChat({ customerToken: token });
|
|
154
|
+
await sdk.ready;
|
|
155
|
+
await sdk.initialize();
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Tokens expire (one hour by default, 60 seconds to 24 hours via
|
|
159
|
+
`expiresInSeconds`). When the token expires or is revoked, the server
|
|
160
|
+
rejects it and the `error` event fires with `type: 'auth'` and
|
|
161
|
+
`code: 'INVALID_IDENTITY_TOKEN'`. The SDK stops instead of reconnecting with
|
|
162
|
+
the same token. Fetch a fresh token from your backend and swap it in with
|
|
163
|
+
`setUserToken`. The SDK keeps running and keeps the conversation:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
sdk.addListener('error', async (e) => {
|
|
167
|
+
if (e.code === 'INVALID_IDENTITY_TOKEN') {
|
|
168
|
+
await sdk.setUserToken(await fetchFreshTokenFromYourBackend());
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`setUserToken` updates the credential for every later request, rebuilds the
|
|
174
|
+
realtime connection with the new token, and restarts initialization if the
|
|
175
|
+
auth failure stopped it. The stored conversation is kept unless the new
|
|
176
|
+
token belongs to a different customer. A blank token throws `E_CONFIG` with
|
|
177
|
+
the message `customerToken must not be blank`.
|
|
178
|
+
|
|
179
|
+
A customer holds **one** live session at a time, so minting while their
|
|
180
|
+
token is still valid returns `409` with the existing session. To rotate a
|
|
181
|
+
token before it expires, your backend must first revoke the session
|
|
182
|
+
(`DELETE /api/v1/chat/sessions?customerId=...`, also the sign-out call) and
|
|
183
|
+
then mint a new one. Revocation takes effect on the next request, so an
|
|
184
|
+
invalidated token stops working immediately.
|
|
185
|
+
|
|
186
|
+
In JWT mode `setUser` is accepted but ignored for identity: the name, email
|
|
187
|
+
and customer id always come from the token.
|
|
188
|
+
|
|
189
|
+
### Key mode (`apiKey`), for trusted internal apps only
|
|
190
|
+
|
|
191
|
+
**Key mode ships your secret key inside the app.** Anyone who has the app
|
|
192
|
+
binary can extract it. Use key mode only for trusted internal apps (staff
|
|
193
|
+
tools, kiosks, test builds), never for apps distributed to the public. Use
|
|
194
|
+
JWT mode for those.
|
|
195
|
+
|
|
196
|
+
`apiKey` is the full `keyId:secret` key from your dashboard. The native SDK
|
|
197
|
+
sends it whole as `x-api-key` on every request. A key ID without its secret
|
|
198
|
+
rejects `ready` with `E_CONFIG` and the message
|
|
199
|
+
`apiKey must be keyId:secret, or use customerToken`.
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
const sdk = new NewinstanceChat({
|
|
203
|
+
apiKey: keyFromYourBuildConfig,
|
|
58
204
|
});
|
|
59
205
|
|
|
60
206
|
sdk.setUser({
|
|
61
207
|
customerName: 'Ada Lovelace',
|
|
62
208
|
customerEmail: 'ada@example.com',
|
|
209
|
+
customerId: 'usr_123',
|
|
63
210
|
});
|
|
64
211
|
|
|
212
|
+
await sdk.ready;
|
|
65
213
|
await sdk.initialize();
|
|
214
|
+
```
|
|
66
215
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
await sdk.ready;
|
|
216
|
+
Load the key from your build configuration or an environment variable, and
|
|
217
|
+
never commit it to source control.
|
|
70
218
|
|
|
71
|
-
|
|
72
|
-
|
|
219
|
+
In key mode `setUser` supplies the customer. `setUserToken` is not
|
|
220
|
+
available and throws synchronously with `E_CONFIG` and the message
|
|
221
|
+
`setUserToken is only available when the SDK is configured with customerToken`.
|
|
222
|
+
A rejected key arrives on the error event as `INVALID_PUBLIC_KEY`.
|
|
223
|
+
|
|
224
|
+
### Migrating from 0.3.x
|
|
225
|
+
|
|
226
|
+
- `ChatUser.customerToken` is gone. Passing `customerToken` to `setUser`
|
|
227
|
+
throws `E_CONFIG` with the message
|
|
228
|
+
`customerToken is not a setUser field; pass it in the config or call setUserToken`.
|
|
229
|
+
Put the token in the config (`customerToken`) and use `setUserToken` to
|
|
230
|
+
swap it.
|
|
231
|
+
- `apiKey` is now the full `keyId:secret` and is sent whole. A bare key ID
|
|
232
|
+
is rejected. Prefer JWT mode for public apps.
|
|
233
|
+
- `setUserToken` returns a Promise and is only available in JWT mode.
|
|
234
|
+
|
|
235
|
+
### Theming
|
|
236
|
+
|
|
237
|
+
The chat resolves colours from three layers:
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
dashboard theme > your local theme > the SDK's built-in theme
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The merchant's dashboard configuration wins, your local theme fills in what
|
|
244
|
+
the dashboard did not set, and the built-in palette fills in the rest, so
|
|
245
|
+
every colour always has a value, even with no network. Merging is per
|
|
246
|
+
token, so setting one colour does not discard the others.
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
const sdk = new NewinstanceChat({
|
|
250
|
+
customerToken: tokenFromYourBackend,
|
|
251
|
+
theme: { mode: 'dark', sentBubble: '#16A34A' },
|
|
252
|
+
});
|
|
73
253
|
```
|
|
74
254
|
|
|
75
|
-
|
|
76
|
-
|
|
255
|
+
Every `ChatThemeOverride` field is optional; colours are hex strings. An
|
|
256
|
+
unparseable value is ignored rather than throwing. `mode` is only a
|
|
257
|
+
preference: the dashboard's explicit light/dark wins, and its `auto` defers
|
|
258
|
+
to yours, then to the device.
|
|
259
|
+
|
|
260
|
+
If remote configuration cannot be fetched, your local theme still applies
|
|
261
|
+
and the chat stays fully usable; you get a `CONFIG_FETCH_FAILED` warning on
|
|
262
|
+
the error event.
|
|
77
263
|
|
|
78
264
|
### Subscribing to events
|
|
79
265
|
|
|
@@ -89,6 +275,47 @@ Event names: `messageReceived`, `messageSent`, `agentTypingChanged`,
|
|
|
89
275
|
`connectionStateChanged`, `unreadCountChanged`, `error`,
|
|
90
276
|
`attachmentUpdated`, `chatClosed`. See `src/types.ts` for payload shapes.
|
|
91
277
|
|
|
278
|
+
### The error event
|
|
279
|
+
|
|
280
|
+
`error` is the channel for "why is my chat not working". It never surfaces
|
|
281
|
+
in the chat interface the customer sees, and it never carries the API key,
|
|
282
|
+
the secret, the customer token, or customer data.
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
sdk.addListener('error', (e) => {
|
|
286
|
+
if (e.code === 'INVALID_IDENTITY_TOKEN') return refreshChatToken();
|
|
287
|
+
if (!e.recoverable) reportToYourMonitoring(e.code, e.message);
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
| `code` | What went wrong |
|
|
292
|
+
|---|---|
|
|
293
|
+
| `MISSING_WIDGET_KEY` | No API key was supplied |
|
|
294
|
+
| `INVALID_PUBLIC_KEY` | Key mode: the key was rejected (wrong key, wrong environment, revoked, expired) |
|
|
295
|
+
| `CHAT_CONFIG_UNAVAILABLE` | The credential is fine, but the organization has no chat configuration |
|
|
296
|
+
| `CHAT_DISABLED` | Chat is switched off in the dashboard |
|
|
297
|
+
| `CONFIG_FETCH_FAILED` | Remote configuration could not be loaded; the local/built-in theme is in use |
|
|
298
|
+
| `INVALID_IDENTITY_TOKEN` | JWT mode: the token was malformed, expired, revoked, or signed for another organization |
|
|
299
|
+
| `NETWORK_ERROR` | The backend could not be reached |
|
|
300
|
+
| `TRANSPORT_ERROR` | The realtime connection dropped |
|
|
301
|
+
| `CHAT_INITIALIZATION_FAILED` | The conversation could not be started |
|
|
302
|
+
| `MESSAGE_SEND_FAILED` | A message could not be delivered |
|
|
303
|
+
| `ATTACHMENT_FAILED` | An attachment could not be uploaded |
|
|
304
|
+
| `SERVER_ERROR` | The backend returned something unusable |
|
|
305
|
+
|
|
306
|
+
Auth failures (`type: 'auth'`) report `INVALID_IDENTITY_TOKEN` in JWT mode
|
|
307
|
+
and `INVALID_PUBLIC_KEY` in key mode. After an auth failure the SDK does not
|
|
308
|
+
reconnect with the same credential.
|
|
309
|
+
|
|
310
|
+
The same codes are emitted by the web, Android, iOS and Flutter SDKs.
|
|
311
|
+
`type` (`network` / `validation` / `auth` / `system`) is still present for
|
|
312
|
+
handlers written against 0.2.x; `code` is `undefined` only when the
|
|
313
|
+
underlying native SDK predates coded errors.
|
|
314
|
+
|
|
315
|
+
Configuration mistakes are not emitted on the error event. They are thrown
|
|
316
|
+
(or reject `ready`) as an `Error` with `code: 'E_CONFIG'`,
|
|
317
|
+
`type: 'validation'` and `recoverable: false`.
|
|
318
|
+
|
|
92
319
|
### The chat-close event
|
|
93
320
|
|
|
94
321
|
`chatClosed` fires **exactly once per chat presentation**, however the
|
|
@@ -127,8 +354,8 @@ sdk.retryMessage(failedMessageId);
|
|
|
127
354
|
### Programmatic attachments
|
|
128
355
|
|
|
129
356
|
`attachFile` accepts base64, data URIs, local file paths,
|
|
130
|
-
`content://` / `file://` URIs (picker outputs), and
|
|
131
|
-
SDK is configured with `allowRemoteAttachmentUrls
|
|
357
|
+
`content://` / `file://` URIs (picker outputs), and, when the native
|
|
358
|
+
SDK is configured with `allowRemoteAttachmentUrls`, remote URLs.
|
|
132
359
|
`sourceType: 'auto'` (the default) safely detects string inputs.
|
|
133
360
|
|
|
134
361
|
```ts
|
|
@@ -155,8 +382,8 @@ await sdk.attachFile({ source: dataUri, name: 'chart.png' });
|
|
|
155
382
|
|
|
156
383
|
The returned promise resolves with the attachment **id** as soon as the
|
|
157
384
|
request is accepted and queued. Validation and upload run in the
|
|
158
|
-
background and never block the UI thread
|
|
159
|
-
`attachmentUpdated` with `status: 'failed'` plus an `error` event
|
|
385
|
+
background and never block the UI thread (failures surface as
|
|
386
|
+
`attachmentUpdated` with `status: 'failed'` plus an `error` event), and
|
|
160
387
|
the message is never sent until the upload succeeds (send drains only
|
|
161
388
|
uploaded attachments). Track progress and results via
|
|
162
389
|
`attachmentUpdated`:
|
|
@@ -182,7 +409,7 @@ Allowed MIME types and the 25 MB cap mirror the web widget; keys and
|
|
|
182
409
|
limits are enforced again server-side.
|
|
183
410
|
|
|
184
411
|
Handoff to a live agent and typing indicators are driven automatically
|
|
185
|
-
by the native chat screen
|
|
412
|
+
by the native chat screen; there's nothing to wire up from the host.
|
|
186
413
|
|
|
187
414
|
### Tear-down
|
|
188
415
|
|
|
@@ -191,7 +418,25 @@ sdk.destroy();
|
|
|
191
418
|
```
|
|
192
419
|
|
|
193
420
|
Call this when your host component unmounts. After `destroy()` the
|
|
194
|
-
instance is unusable
|
|
421
|
+
instance is unusable; construct a new one if you need to chat again.
|
|
422
|
+
|
|
423
|
+
## Example app
|
|
424
|
+
|
|
425
|
+
The example app in `example/` runs in JWT mode by default and fetches a
|
|
426
|
+
token from the placeholder URL `https://your-backend.example.com/chat-token`
|
|
427
|
+
in `example/src/App.tsx`. Point it at your own backend route.
|
|
428
|
+
|
|
429
|
+
To try key mode on a trusted development build instead, set the key in the
|
|
430
|
+
environment before starting Metro. Nothing is committed to the repository:
|
|
431
|
+
|
|
432
|
+
```sh
|
|
433
|
+
NEWINSTANCE_API_KEY='sk_test_abc123:YOUR_SECRET_KEY' yarn example start --reset-cache
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
`NEWINSTANCE_BASE_URL` optionally points a debug build at another backend.
|
|
437
|
+
Both variables are empty by default and are inlined by
|
|
438
|
+
`example/babel.config.js`, so restart Metro with `--reset-cache` after
|
|
439
|
+
changing them.
|
|
195
440
|
|
|
196
441
|
## Contributing
|
|
197
442
|
|
package/android/build.gradle
CHANGED
|
@@ -73,5 +73,5 @@ repositories {
|
|
|
73
73
|
dependencies {
|
|
74
74
|
implementation "com.facebook.react:react-android"
|
|
75
75
|
// Native chat SDK the bridge wraps. Published under cloud.newinstance.
|
|
76
|
-
implementation "cloud.newinstance:liveandaichat:0.
|
|
76
|
+
implementation "cloud.newinstance:liveandaichat:0.4.0"
|
|
77
77
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
package com.newinstancechat
|
|
2
2
|
|
|
3
|
+
import cloud.newinstance.liveandaichat.ChatAuthMode
|
|
3
4
|
import cloud.newinstance.liveandaichat.ConnectionState
|
|
4
5
|
import cloud.newinstance.liveandaichat.LiveAndAiChat
|
|
5
6
|
import cloud.newinstance.liveandaichat.LiveAndAiChatConfig
|
|
6
7
|
import cloud.newinstance.liveandaichat.LiveAndAiChatError
|
|
8
|
+
import cloud.newinstance.liveandaichat.ChatSdkError
|
|
9
|
+
import cloud.newinstance.liveandaichat.ChatThemeOverride
|
|
7
10
|
import cloud.newinstance.liveandaichat.ChatUser
|
|
11
|
+
import cloud.newinstance.liveandaichat.ErrorType
|
|
8
12
|
import cloud.newinstance.liveandaichat.model.AttachmentRequest
|
|
9
13
|
import cloud.newinstance.liveandaichat.model.AttachmentSource
|
|
10
14
|
import cloud.newinstance.liveandaichat.model.ChatCloseEvent
|
|
@@ -15,6 +19,7 @@ import com.facebook.react.bridge.Arguments
|
|
|
15
19
|
import com.facebook.react.bridge.Promise
|
|
16
20
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
17
21
|
import com.facebook.react.bridge.ReadableMap
|
|
22
|
+
import com.facebook.react.bridge.ReadableType
|
|
18
23
|
import com.facebook.react.bridge.WritableMap
|
|
19
24
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
20
25
|
|
|
@@ -39,6 +44,8 @@ class NewinstanceChatModule(reactContext: ReactApplicationContext) :
|
|
|
39
44
|
private var sdk: LiveAndAiChat? = null
|
|
40
45
|
private var pendingUser: ChatUser? = null
|
|
41
46
|
|
|
47
|
+
private var usingIdentityToken: Boolean = false
|
|
48
|
+
|
|
42
49
|
override fun getName(): String = NAME
|
|
43
50
|
|
|
44
51
|
private fun emit(event: String, body: Any?) {
|
|
@@ -50,9 +57,10 @@ class NewinstanceChatModule(reactContext: ReactApplicationContext) :
|
|
|
50
57
|
// MARK: - configure / setUser / initialize
|
|
51
58
|
|
|
52
59
|
override fun configure(config: ReadableMap, promise: Promise) {
|
|
53
|
-
val apiKey = config.getString("apiKey")
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
val apiKey = config.getString("apiKey")?.trim()?.takeIf { it.isNotEmpty() }
|
|
61
|
+
val customerToken = config.getString("customerToken")?.trim()?.takeIf { it.isNotEmpty() }
|
|
62
|
+
if ((apiKey == null) == (customerToken == null)) {
|
|
63
|
+
promise.reject("E_CONFIG", "pass either apiKey (keyId:secret) or customerToken")
|
|
56
64
|
return
|
|
57
65
|
}
|
|
58
66
|
val baseUrl = if (config.hasKey("baseUrl")) config.getString("baseUrl") else null
|
|
@@ -66,12 +74,26 @@ class NewinstanceChatModule(reactContext: ReactApplicationContext) :
|
|
|
66
74
|
}
|
|
67
75
|
} else null
|
|
68
76
|
|
|
77
|
+
val themeOverride = if (config.hasKey("theme")) {
|
|
78
|
+
config.getMap("theme")?.let { map ->
|
|
79
|
+
val values = mutableMapOf<String, String?>()
|
|
80
|
+
val it = map.keySetIterator()
|
|
81
|
+
while (it.hasNextKey()) {
|
|
82
|
+
val key = it.nextKey()
|
|
83
|
+
if (map.getType(key) == ReadableType.String) values[key] = map.getString(key)
|
|
84
|
+
}
|
|
85
|
+
ChatThemeOverride.fromMap(values)
|
|
86
|
+
}
|
|
87
|
+
} else null
|
|
88
|
+
|
|
69
89
|
try {
|
|
70
90
|
val cfg = LiveAndAiChatConfig(
|
|
71
91
|
apiKey = apiKey,
|
|
92
|
+
customerToken = customerToken,
|
|
72
93
|
baseUrl = baseUrl ?: LiveAndAiChatConfig.DEFAULT_BASE_URL,
|
|
73
94
|
transport = transport,
|
|
74
95
|
initialMessage = initialMessage,
|
|
96
|
+
theme = themeOverride,
|
|
75
97
|
)
|
|
76
98
|
sdk?.destroy()
|
|
77
99
|
val builder = LiveAndAiChat.Builder(reactApplicationContext).config(cfg)
|
|
@@ -79,16 +101,18 @@ class NewinstanceChatModule(reactContext: ReactApplicationContext) :
|
|
|
79
101
|
val instance = builder.build()
|
|
80
102
|
instance.addListener(this)
|
|
81
103
|
sdk = instance
|
|
104
|
+
usingIdentityToken = cfg.authMode == ChatAuthMode.CUSTOMER_TOKEN
|
|
82
105
|
pendingUser = null
|
|
83
106
|
promise.resolve(null)
|
|
107
|
+
} catch (e: LiveAndAiChatError) {
|
|
108
|
+
promise.reject(e.code ?: "E_BUILD", e.message, e)
|
|
84
109
|
} catch (e: Exception) {
|
|
85
110
|
promise.reject("E_BUILD", e.message, e)
|
|
86
111
|
}
|
|
87
112
|
}
|
|
88
113
|
|
|
89
114
|
override fun setUser(user: ReadableMap) {
|
|
90
|
-
val name = user.getString("customerName") ?: return
|
|
91
|
-
if (name.isBlank()) return
|
|
115
|
+
val name = user.getString("customerName")?.takeIf { it.isNotBlank() } ?: return
|
|
92
116
|
val chatUser = ChatUser(
|
|
93
117
|
customerName = name,
|
|
94
118
|
customerId = if (user.hasKey("customerId")) user.getString("customerId") else null,
|
|
@@ -102,6 +126,22 @@ class NewinstanceChatModule(reactContext: ReactApplicationContext) :
|
|
|
102
126
|
}
|
|
103
127
|
}
|
|
104
128
|
|
|
129
|
+
override fun setUserToken(token: String, promise: Promise) {
|
|
130
|
+
val current = sdk
|
|
131
|
+
if (current == null) {
|
|
132
|
+
promise.reject("E_NOT_CONFIGURED", "configure() must be called first")
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
current.setUserToken(token)
|
|
137
|
+
promise.resolve(null)
|
|
138
|
+
} catch (e: LiveAndAiChatError) {
|
|
139
|
+
promise.reject(e.code ?: "E_CONFIG", e.message, e)
|
|
140
|
+
} catch (e: Exception) {
|
|
141
|
+
promise.reject("E_CONFIG", e.message, e)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
105
145
|
override fun initialize(promise: Promise) {
|
|
106
146
|
val current = sdk
|
|
107
147
|
if (current == null) {
|
|
@@ -143,7 +183,7 @@ class NewinstanceChatModule(reactContext: ReactApplicationContext) :
|
|
|
143
183
|
if (source == null) {
|
|
144
184
|
promise.reject(
|
|
145
185
|
"E_SOURCE",
|
|
146
|
-
if (sourceType == "auto") "Could not detect the attachment source type
|
|
186
|
+
if (sourceType == "auto") "Could not detect the attachment source type: pass sourceType explicitly"
|
|
147
187
|
else "Unknown sourceType: $sourceType",
|
|
148
188
|
)
|
|
149
189
|
return
|
|
@@ -240,12 +280,39 @@ class NewinstanceChatModule(reactContext: ReactApplicationContext) :
|
|
|
240
280
|
|
|
241
281
|
override fun onError(error: LiveAndAiChatError) {
|
|
242
282
|
val map = Arguments.createMap()
|
|
283
|
+
map.putString("code", sdkErrorCode(error))
|
|
243
284
|
map.putString("type", error.type.name.lowercase())
|
|
244
285
|
map.putString("message", error.message)
|
|
245
286
|
map.putBoolean("recoverable", error.recoverable)
|
|
246
287
|
emit("error", map)
|
|
247
288
|
}
|
|
248
289
|
|
|
290
|
+
private fun sdkErrorCode(error: LiveAndAiChatError): String {
|
|
291
|
+
val known = setOf(
|
|
292
|
+
ChatSdkError.CHAT_CONFIG_UNAVAILABLE,
|
|
293
|
+
ChatSdkError.CHAT_DISABLED,
|
|
294
|
+
ChatSdkError.INVALID_PUBLIC_KEY,
|
|
295
|
+
ChatSdkError.MISSING_WIDGET_KEY,
|
|
296
|
+
ChatSdkError.CONFIG_FETCH_FAILED,
|
|
297
|
+
ChatSdkError.CHAT_INITIALIZATION_FAILED,
|
|
298
|
+
ChatSdkError.NETWORK_ERROR,
|
|
299
|
+
ChatSdkError.SERVER_ERROR,
|
|
300
|
+
ChatSdkError.INVALID_IDENTITY_TOKEN,
|
|
301
|
+
ChatSdkError.TRANSPORT_ERROR,
|
|
302
|
+
ChatSdkError.MESSAGE_SEND_FAILED,
|
|
303
|
+
ChatSdkError.ATTACHMENT_FAILED,
|
|
304
|
+
)
|
|
305
|
+
error.code?.let { if (it in known) return it }
|
|
306
|
+
return when (error.type) {
|
|
307
|
+
ErrorType.AUTH ->
|
|
308
|
+
if (usingIdentityToken) ChatSdkError.INVALID_IDENTITY_TOKEN else ChatSdkError.INVALID_PUBLIC_KEY
|
|
309
|
+
ErrorType.UNAVAILABLE -> ChatSdkError.CHAT_CONFIG_UNAVAILABLE
|
|
310
|
+
ErrorType.NETWORK -> ChatSdkError.NETWORK_ERROR
|
|
311
|
+
ErrorType.VALIDATION -> ChatSdkError.SERVER_ERROR
|
|
312
|
+
ErrorType.SYSTEM -> ChatSdkError.CHAT_INITIALIZATION_FAILED
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
249
316
|
// MARK: - Helpers
|
|
250
317
|
|
|
251
318
|
private fun chatMessageToMap(m: ChatMessage): WritableMap {
|
package/ios/NewinstanceChat.mm
CHANGED
|
@@ -56,6 +56,13 @@ RCT_EXPORT_MODULE()
|
|
|
56
56
|
[_impl setUser:user];
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
- (void)setUserToken:(NSString *)token
|
|
60
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
61
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
62
|
+
{
|
|
63
|
+
[_impl setUserToken:token resolver:resolve rejecter:reject];
|
|
64
|
+
}
|
|
65
|
+
|
|
59
66
|
- (void)initialize:(RCTPromiseResolveBlock)resolve
|
|
60
67
|
reject:(RCTPromiseRejectBlock)reject
|
|
61
68
|
{
|
|
@@ -29,14 +29,18 @@ public final class NewinstanceChatImpl: NSObject, LiveAndAiChatDelegate {
|
|
|
29
29
|
private var sdk: LiveAndAiChat?
|
|
30
30
|
private var pendingUser: ChatUser?
|
|
31
31
|
|
|
32
|
+
private var usingIdentityToken: Bool = false
|
|
33
|
+
|
|
32
34
|
// MARK: - configure / setUser / initialize
|
|
33
35
|
|
|
34
36
|
@objc(configure:resolver:rejecter:)
|
|
35
37
|
public func configure(_ configDict: NSDictionary,
|
|
36
38
|
resolver: @escaping RCTPromiseResolveBlock,
|
|
37
39
|
rejecter: @escaping RCTPromiseRejectBlock) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
let apiKey = Self.nonBlank(configDict["apiKey"])
|
|
41
|
+
let customerToken = Self.nonBlank(configDict["customerToken"])
|
|
42
|
+
guard (apiKey == nil) != (customerToken == nil) else {
|
|
43
|
+
rejecter("E_CONFIG", "pass either apiKey (keyId:secret) or customerToken", nil)
|
|
40
44
|
return
|
|
41
45
|
}
|
|
42
46
|
let baseUrl = configDict["baseUrl"] as? String
|
|
@@ -49,14 +53,25 @@ public final class NewinstanceChatImpl: NSObject, LiveAndAiChatDelegate {
|
|
|
49
53
|
}
|
|
50
54
|
}()
|
|
51
55
|
|
|
56
|
+
let themeOverride: ChatThemeOverride? = {
|
|
57
|
+
guard let raw = configDict["theme"] as? NSDictionary else { return nil }
|
|
58
|
+
var values: [String: String] = [:]
|
|
59
|
+
for (key, value) in raw {
|
|
60
|
+
if let k = key as? String, let v = value as? String { values[k] = v }
|
|
61
|
+
}
|
|
62
|
+
return values.isEmpty ? nil : ChatThemeOverride.fromDictionary(values)
|
|
63
|
+
}()
|
|
64
|
+
|
|
52
65
|
DispatchQueue.main.async { [weak self] in
|
|
53
66
|
guard let self = self else { return }
|
|
54
67
|
do {
|
|
55
68
|
let cfg = try LiveAndAiChatConfig(
|
|
56
69
|
apiKey: apiKey,
|
|
70
|
+
customerToken: customerToken,
|
|
57
71
|
baseUrl: baseUrl ?? LiveAndAiChatConfig.defaultBaseUrl,
|
|
58
72
|
transport: transport,
|
|
59
|
-
initialMessage: initialMessage
|
|
73
|
+
initialMessage: initialMessage,
|
|
74
|
+
theme: themeOverride
|
|
60
75
|
)
|
|
61
76
|
let builder = LiveAndAiChat.Builder().config(cfg)
|
|
62
77
|
if let user = self.pendingUser { _ = builder.user(user) }
|
|
@@ -64,10 +79,11 @@ public final class NewinstanceChatImpl: NSObject, LiveAndAiChatDelegate {
|
|
|
64
79
|
let instance = try builder.build()
|
|
65
80
|
instance.addDelegate(self)
|
|
66
81
|
self.sdk = instance
|
|
82
|
+
self.usingIdentityToken = cfg.authMode == .customerToken
|
|
67
83
|
self.pendingUser = nil
|
|
68
84
|
resolver(nil)
|
|
69
85
|
} catch let e as LiveAndAiChatError {
|
|
70
|
-
rejecter("E_BUILD", e.message, e)
|
|
86
|
+
rejecter(e.code ?? "E_BUILD", e.message, e)
|
|
71
87
|
} catch {
|
|
72
88
|
rejecter("E_BUILD", error.localizedDescription, error)
|
|
73
89
|
}
|
|
@@ -76,7 +92,7 @@ public final class NewinstanceChatImpl: NSObject, LiveAndAiChatDelegate {
|
|
|
76
92
|
|
|
77
93
|
@objc(setUser:)
|
|
78
94
|
public func setUser(_ userDict: NSDictionary) {
|
|
79
|
-
guard let name = userDict["customerName"] as? String
|
|
95
|
+
guard let name = (userDict["customerName"] as? String).flatMap({ $0.isEmpty ? nil : $0 }) else { return }
|
|
80
96
|
let user = ChatUser(
|
|
81
97
|
customerName: name,
|
|
82
98
|
customerId: userDict["customerId"] as? String,
|
|
@@ -92,6 +108,26 @@ public final class NewinstanceChatImpl: NSObject, LiveAndAiChatDelegate {
|
|
|
92
108
|
}
|
|
93
109
|
}
|
|
94
110
|
|
|
111
|
+
@objc(setUserToken:resolver:rejecter:)
|
|
112
|
+
public func setUserToken(_ token: String,
|
|
113
|
+
resolver: @escaping RCTPromiseResolveBlock,
|
|
114
|
+
rejecter: @escaping RCTPromiseRejectBlock) {
|
|
115
|
+
DispatchQueue.main.async { [weak self] in
|
|
116
|
+
guard let sdk = self?.sdk else {
|
|
117
|
+
rejecter("E_NOT_CONFIGURED", "configure() must be called first", nil)
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
do {
|
|
121
|
+
try sdk.setUserToken(token)
|
|
122
|
+
resolver(nil)
|
|
123
|
+
} catch let e as LiveAndAiChatError {
|
|
124
|
+
rejecter(e.code ?? "E_CONFIG", e.message, e)
|
|
125
|
+
} catch {
|
|
126
|
+
rejecter("E_CONFIG", error.localizedDescription, error)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
95
131
|
@objc(initializeWithResolver:rejecter:)
|
|
96
132
|
public func initialize(resolver: @escaping RCTPromiseResolveBlock,
|
|
97
133
|
rejecter: @escaping RCTPromiseRejectBlock) {
|
|
@@ -153,7 +189,7 @@ public final class NewinstanceChatImpl: NSObject, LiveAndAiChatDelegate {
|
|
|
153
189
|
rejecter(
|
|
154
190
|
"E_SOURCE",
|
|
155
191
|
sourceType == "auto"
|
|
156
|
-
? "Could not detect the attachment source type
|
|
192
|
+
? "Could not detect the attachment source type: pass sourceType explicitly"
|
|
157
193
|
: "Unknown sourceType: \(sourceType)",
|
|
158
194
|
nil
|
|
159
195
|
)
|
|
@@ -247,6 +283,7 @@ public final class NewinstanceChatImpl: NSObject, LiveAndAiChatDelegate {
|
|
|
247
283
|
}
|
|
248
284
|
public func didEncounterError(_ error: LiveAndAiChatError) {
|
|
249
285
|
emitter?.emitEvent("error", body: [
|
|
286
|
+
"code": error.sdkCode(usingIdentityToken: usingIdentityToken),
|
|
250
287
|
"type": Self.errorTypeName(error.type),
|
|
251
288
|
"message": error.message,
|
|
252
289
|
"recoverable": error.recoverable,
|
|
@@ -255,6 +292,11 @@ public final class NewinstanceChatImpl: NSObject, LiveAndAiChatDelegate {
|
|
|
255
292
|
|
|
256
293
|
// MARK: - Helpers
|
|
257
294
|
|
|
295
|
+
private static func nonBlank(_ value: Any?) -> String? {
|
|
296
|
+
guard let text = (value as? String)?.trimmingCharacters(in: .whitespacesAndNewlines), !text.isEmpty else { return nil }
|
|
297
|
+
return text
|
|
298
|
+
}
|
|
299
|
+
|
|
258
300
|
private static func chatMessageToDict(_ m: ChatMessage) -> [String: Any?] {
|
|
259
301
|
return [
|
|
260
302
|
"id": m.id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeNewinstanceChat.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;;AAEpE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeNewinstanceChat.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;;AAEpE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAqBA,eAAeA,mBAAmB,CAACC,YAAY,CAAO,iBAAiB,CAAC","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -7,6 +7,27 @@ import NativeNewinstanceChat from "./NativeNewinstanceChat.js";
|
|
|
7
7
|
// we feed it the legacy handle so listener counts get tracked.
|
|
8
8
|
const emitterSource = NativeModules.NewinstanceChat ?? NativeNewinstanceChat;
|
|
9
9
|
const emitter = new NativeEventEmitter(emitterSource);
|
|
10
|
+
function trimmed(value) {
|
|
11
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
12
|
+
}
|
|
13
|
+
function configProblem(apiKey, customerToken) {
|
|
14
|
+
if (!apiKey === !customerToken) {
|
|
15
|
+
return 'pass either apiKey (keyId:secret) or customerToken';
|
|
16
|
+
}
|
|
17
|
+
if (customerToken) return null;
|
|
18
|
+
const separator = apiKey.indexOf(':');
|
|
19
|
+
if (separator < 0 || !apiKey.slice(0, separator).trim() || !apiKey.slice(separator + 1).trim()) {
|
|
20
|
+
return 'apiKey must be keyId:secret, or use customerToken';
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
function configError(message) {
|
|
25
|
+
return Object.assign(new Error(message), {
|
|
26
|
+
code: 'E_CONFIG',
|
|
27
|
+
type: 'validation',
|
|
28
|
+
recoverable: false
|
|
29
|
+
});
|
|
30
|
+
}
|
|
10
31
|
|
|
11
32
|
/**
|
|
12
33
|
* React Native binding for the native LiveAndAiChat iOS / Android SDKs.
|
|
@@ -22,7 +43,22 @@ export class NewinstanceChat {
|
|
|
22
43
|
/** Resolves once the native SDK is configured. */
|
|
23
44
|
|
|
24
45
|
constructor(config) {
|
|
25
|
-
|
|
46
|
+
const {
|
|
47
|
+
apiKey,
|
|
48
|
+
customerToken,
|
|
49
|
+
...rest
|
|
50
|
+
} = config;
|
|
51
|
+
const key = trimmed(apiKey);
|
|
52
|
+
const token = trimmed(customerToken);
|
|
53
|
+
this.customerTokenMode = !key && !!token;
|
|
54
|
+
const problem = configProblem(key, token);
|
|
55
|
+
this.ready = problem ? Promise.reject(configError(problem)) : NativeNewinstanceChat.configure(token ? {
|
|
56
|
+
...rest,
|
|
57
|
+
customerToken: token
|
|
58
|
+
} : {
|
|
59
|
+
...rest,
|
|
60
|
+
apiKey: key
|
|
61
|
+
});
|
|
26
62
|
}
|
|
27
63
|
|
|
28
64
|
/**
|
|
@@ -31,8 +67,19 @@ export class NewinstanceChat {
|
|
|
31
67
|
* once the native instance exists.
|
|
32
68
|
*/
|
|
33
69
|
setUser(user) {
|
|
70
|
+
if ('customerToken' in user) {
|
|
71
|
+
throw configError('customerToken is not a setUser field; pass it in the config or call setUserToken');
|
|
72
|
+
}
|
|
34
73
|
NativeNewinstanceChat.setUser(user);
|
|
35
74
|
}
|
|
75
|
+
setUserToken(customerToken) {
|
|
76
|
+
if (!this.customerTokenMode) {
|
|
77
|
+
throw configError('setUserToken is only available when the SDK is configured with customerToken');
|
|
78
|
+
}
|
|
79
|
+
const token = trimmed(customerToken);
|
|
80
|
+
if (!token) throw configError('customerToken must not be blank');
|
|
81
|
+
return NativeNewinstanceChat.setUserToken(token);
|
|
82
|
+
}
|
|
36
83
|
|
|
37
84
|
/**
|
|
38
85
|
* Fetch org config, restore the saved conversation, and connect the
|
|
@@ -40,7 +87,7 @@ export class NewinstanceChat {
|
|
|
40
87
|
* the same in-flight init.
|
|
41
88
|
*/
|
|
42
89
|
initialize() {
|
|
43
|
-
return NativeNewinstanceChat.initialize();
|
|
90
|
+
return this.ready.then(() => NativeNewinstanceChat.initialize());
|
|
44
91
|
}
|
|
45
92
|
|
|
46
93
|
/** Present the native chat screen modally over the current RN host. */
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NativeEventEmitter","NativeModules","NativeNewinstanceChat","emitterSource","NewinstanceChat","emitter","constructor","config","ready","configure","setUser","user","initialize","openChat","closeChat","sendMessage","text","retryMessage","messageId","attachFile","opts","source","uri","
|
|
1
|
+
{"version":3,"names":["NativeEventEmitter","NativeModules","NativeNewinstanceChat","emitterSource","NewinstanceChat","emitter","trimmed","value","trim","configProblem","apiKey","customerToken","separator","indexOf","slice","configError","message","Object","assign","Error","code","type","recoverable","constructor","config","rest","key","token","customerTokenMode","problem","ready","Promise","reject","configure","setUser","user","setUserToken","initialize","then","openChat","closeChat","sendMessage","text","retryMessage","messageId","attachFile","opts","source","uri","removeAttachment","id","clearAttachments","addListener","event","handler","sub","remove","destroy"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -12,6 +12,7 @@ import { type TurboModule } from 'react-native';
|
|
|
12
12
|
export interface Spec extends TurboModule {
|
|
13
13
|
configure(config: Object): Promise<void>;
|
|
14
14
|
setUser(user: Object): void;
|
|
15
|
+
setUserToken(token: string): Promise<void>;
|
|
15
16
|
initialize(): Promise<void>;
|
|
16
17
|
openChat(): void;
|
|
17
18
|
closeChat(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeNewinstanceChat.d.ts","sourceRoot":"","sources":["../../../src/NativeNewinstanceChat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE;;;;;;;;;GASG;AACH,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,QAAQ,IAAI,IAAI,CAAC;IACjB,SAAS,IAAI,IAAI,CAAC;IAClB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,gBAAgB,IAAI,IAAI,CAAC;IACzB,OAAO,IAAI,IAAI,CAAC;IAIhB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;;AAED,wBAAyE"}
|
|
1
|
+
{"version":3,"file":"NativeNewinstanceChat.d.ts","sourceRoot":"","sources":["../../../src/NativeNewinstanceChat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE;;;;;;;;;GASG;AACH,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,QAAQ,IAAI,IAAI,CAAC;IACjB,SAAS,IAAI,IAAI,CAAC;IAClB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,gBAAgB,IAAI,IAAI,CAAC;IACzB,OAAO,IAAI,IAAI,CAAC;IAIhB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;;AAED,wBAAyE"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AttachmentInput, ChatUser, EventHandler, EventName, NewinstanceChatConfig } from './types';
|
|
2
|
-
export type { AttachmentInput, ChatMessage, ChatUser, ConnectionState, EventMap, EventName, NewinstanceChatConfig, NewinstanceChatError, NewinstanceChatErrorType, } from './types';
|
|
2
|
+
export type { AttachmentInput, AttachmentUpdate, ChatClosedEvent, ChatMessage, ChatThemeOverride, ChatUser, ConnectionState, EventMap, EventName, NewinstanceChatConfig, NewinstanceChatError, NewinstanceChatErrorCode, NewinstanceChatErrorType, } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* React Native binding for the native LiveAndAiChat iOS / Android SDKs.
|
|
5
5
|
*
|
|
@@ -13,6 +13,7 @@ export type { AttachmentInput, ChatMessage, ChatUser, ConnectionState, EventMap,
|
|
|
13
13
|
export declare class NewinstanceChat {
|
|
14
14
|
/** Resolves once the native SDK is configured. */
|
|
15
15
|
readonly ready: Promise<void>;
|
|
16
|
+
private readonly customerTokenMode;
|
|
16
17
|
constructor(config: NewinstanceChatConfig);
|
|
17
18
|
/**
|
|
18
19
|
* Identify the customer. Safe to call before or after `initialize()`. If
|
|
@@ -20,6 +21,7 @@ export declare class NewinstanceChat {
|
|
|
20
21
|
* once the native instance exists.
|
|
21
22
|
*/
|
|
22
23
|
setUser(user: ChatUser): void;
|
|
24
|
+
setUserToken(customerToken: string): Promise<void>;
|
|
23
25
|
/**
|
|
24
26
|
* Fetch org config, restore the saved conversation, and connect the
|
|
25
27
|
* subscription transport in the background. Concurrent callers share
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,eAAe,EACf,WAAW,EACX,QAAQ,EACR,eAAe,EACf,QAAQ,EACR,SAAS,EACT,qBAAqB,EACrB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,QAAQ,EACR,eAAe,EACf,QAAQ,EACR,SAAS,EACT,qBAAqB,EACrB,oBAAoB,EACpB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,SAAS,CAAC;AAqCjB;;;;;;;;;GASG;AACH,qBAAa,eAAe;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAU;gBAEhC,MAAM,EAAE,qBAAqB;IAazC;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IAS7B,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWlD;;;;OAIG;IACH,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,uEAAuE;IACvE,QAAQ,IAAI,IAAI;IAIhB,sCAAsC;IACtC,SAAS,IAAI,IAAI;IAIjB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI/B,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC;IASlD,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIlC,mEAAmE;IACnE,gBAAgB,IAAI,IAAI;IAIxB;;;OAGG;IACH,WAAW,CAAC,CAAC,SAAS,SAAS,EAC7B,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GACvB,MAAM,IAAI;IAQb;;;OAGG;IACH,OAAO,IAAI,IAAI;CAGhB"}
|
|
@@ -6,14 +6,43 @@
|
|
|
6
6
|
* are lowercased string enums, not platform-specific enum values).
|
|
7
7
|
*/
|
|
8
8
|
export interface NewinstanceChatConfig {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
customerToken?: string;
|
|
11
11
|
/** Backend base URL. Defaults to `https://service.newinstance.cloud`. */
|
|
12
12
|
baseUrl?: string;
|
|
13
13
|
/** Subscription transport. `'sse'` (default in production), `'ws'`, or omit to let the server choose. */
|
|
14
14
|
transport?: 'sse' | 'ws';
|
|
15
15
|
/** Optional message pre-seeded as the first outbound message. */
|
|
16
16
|
initialMessage?: string;
|
|
17
|
+
theme?: ChatThemeOverride;
|
|
18
|
+
}
|
|
19
|
+
export interface ChatThemeOverride {
|
|
20
|
+
mode?: 'light' | 'dark';
|
|
21
|
+
chatBackground?: string;
|
|
22
|
+
headerBackground?: string;
|
|
23
|
+
headerPrimaryText?: string;
|
|
24
|
+
headerSecondaryText?: string;
|
|
25
|
+
headerIcon?: string;
|
|
26
|
+
closeButton?: string;
|
|
27
|
+
receivedBubble?: string;
|
|
28
|
+
receivedText?: string;
|
|
29
|
+
receivedTimestamp?: string;
|
|
30
|
+
sentBubble?: string;
|
|
31
|
+
sentText?: string;
|
|
32
|
+
sentTimestamp?: string;
|
|
33
|
+
systemMessageText?: string;
|
|
34
|
+
daySeparatorBackground?: string;
|
|
35
|
+
daySeparatorText?: string;
|
|
36
|
+
footerContainer?: string;
|
|
37
|
+
chatInputBackground?: string;
|
|
38
|
+
chatInputText?: string;
|
|
39
|
+
chatInputPlaceholder?: string;
|
|
40
|
+
chatInputBorder?: string;
|
|
41
|
+
sendButtonBackground?: string;
|
|
42
|
+
sendButtonIcon?: string;
|
|
43
|
+
attachmentButton?: string;
|
|
44
|
+
unreadBadgeBackground?: string;
|
|
45
|
+
unreadBadgeText?: string;
|
|
17
46
|
}
|
|
18
47
|
export interface ChatUser {
|
|
19
48
|
customerName: string;
|
|
@@ -101,9 +130,12 @@ export interface ChatMessage {
|
|
|
101
130
|
totalMessages?: number;
|
|
102
131
|
}
|
|
103
132
|
export type NewinstanceChatErrorType = 'network' | 'validation' | 'auth' | 'system';
|
|
133
|
+
export type NewinstanceChatErrorCode = 'INVALID_PUBLIC_KEY' | 'MISSING_WIDGET_KEY' | 'CHAT_CONFIG_UNAVAILABLE' | 'CHAT_DISABLED' | 'CONFIG_FETCH_FAILED' | 'INVALID_IDENTITY_TOKEN' | 'NETWORK_ERROR' | 'TRANSPORT_ERROR' | 'CHAT_INITIALIZATION_FAILED' | 'MESSAGE_SEND_FAILED' | 'ATTACHMENT_FAILED' | 'SERVER_ERROR';
|
|
104
134
|
export interface NewinstanceChatError {
|
|
135
|
+
code?: NewinstanceChatErrorCode;
|
|
105
136
|
type: NewinstanceChatErrorType;
|
|
106
137
|
message: string;
|
|
138
|
+
developerMessage?: string;
|
|
107
139
|
recoverable: boolean;
|
|
108
140
|
}
|
|
109
141
|
/** Native event names. Subscribe with `sdk.addListener(event, handler)`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,qBAAqB;IACpC,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-newinstance-chat",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "New instance LiveAndAiChat React Native SDK
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "New instance LiveAndAiChat React Native SDK: AI + live-agent chat, drop-in native chat screen for iOS and Android.",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -13,6 +13,7 @@ import { TurboModuleRegistry, type TurboModule } from 'react-native';
|
|
|
13
13
|
export interface Spec extends TurboModule {
|
|
14
14
|
configure(config: Object): Promise<void>;
|
|
15
15
|
setUser(user: Object): void;
|
|
16
|
+
setUserToken(token: string): Promise<void>;
|
|
16
17
|
initialize(): Promise<void>;
|
|
17
18
|
openChat(): void;
|
|
18
19
|
closeChat(): void;
|
package/src/index.tsx
CHANGED
|
@@ -15,13 +15,17 @@ import type {
|
|
|
15
15
|
|
|
16
16
|
export type {
|
|
17
17
|
AttachmentInput,
|
|
18
|
+
AttachmentUpdate,
|
|
19
|
+
ChatClosedEvent,
|
|
18
20
|
ChatMessage,
|
|
21
|
+
ChatThemeOverride,
|
|
19
22
|
ChatUser,
|
|
20
23
|
ConnectionState,
|
|
21
24
|
EventMap,
|
|
22
25
|
EventName,
|
|
23
26
|
NewinstanceChatConfig,
|
|
24
27
|
NewinstanceChatError,
|
|
28
|
+
NewinstanceChatErrorCode,
|
|
25
29
|
NewinstanceChatErrorType,
|
|
26
30
|
} from './types';
|
|
27
31
|
|
|
@@ -32,6 +36,34 @@ const emitterSource: any =
|
|
|
32
36
|
NativeModules.NewinstanceChat ?? (NativeNewinstanceChat as unknown);
|
|
33
37
|
const emitter = new NativeEventEmitter(emitterSource);
|
|
34
38
|
|
|
39
|
+
function trimmed(value: unknown): string {
|
|
40
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function configProblem(apiKey: string, customerToken: string): string | null {
|
|
44
|
+
if (!apiKey === !customerToken) {
|
|
45
|
+
return 'pass either apiKey (keyId:secret) or customerToken';
|
|
46
|
+
}
|
|
47
|
+
if (customerToken) return null;
|
|
48
|
+
const separator = apiKey.indexOf(':');
|
|
49
|
+
if (
|
|
50
|
+
separator < 0 ||
|
|
51
|
+
!apiKey.slice(0, separator).trim() ||
|
|
52
|
+
!apiKey.slice(separator + 1).trim()
|
|
53
|
+
) {
|
|
54
|
+
return 'apiKey must be keyId:secret, or use customerToken';
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function configError(message: string) {
|
|
60
|
+
return Object.assign(new Error(message), {
|
|
61
|
+
code: 'E_CONFIG' as const,
|
|
62
|
+
type: 'validation' as const,
|
|
63
|
+
recoverable: false,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
35
67
|
/**
|
|
36
68
|
* React Native binding for the native LiveAndAiChat iOS / Android SDKs.
|
|
37
69
|
*
|
|
@@ -45,9 +77,19 @@ const emitter = new NativeEventEmitter(emitterSource);
|
|
|
45
77
|
export class NewinstanceChat {
|
|
46
78
|
/** Resolves once the native SDK is configured. */
|
|
47
79
|
readonly ready: Promise<void>;
|
|
80
|
+
private readonly customerTokenMode: boolean;
|
|
48
81
|
|
|
49
82
|
constructor(config: NewinstanceChatConfig) {
|
|
50
|
-
|
|
83
|
+
const { apiKey, customerToken, ...rest } = config;
|
|
84
|
+
const key = trimmed(apiKey);
|
|
85
|
+
const token = trimmed(customerToken);
|
|
86
|
+
this.customerTokenMode = !key && !!token;
|
|
87
|
+
const problem = configProblem(key, token);
|
|
88
|
+
this.ready = problem
|
|
89
|
+
? Promise.reject(configError(problem))
|
|
90
|
+
: NativeNewinstanceChat.configure(
|
|
91
|
+
token ? { ...rest, customerToken: token } : { ...rest, apiKey: key }
|
|
92
|
+
);
|
|
51
93
|
}
|
|
52
94
|
|
|
53
95
|
/**
|
|
@@ -56,16 +98,32 @@ export class NewinstanceChat {
|
|
|
56
98
|
* once the native instance exists.
|
|
57
99
|
*/
|
|
58
100
|
setUser(user: ChatUser): void {
|
|
101
|
+
if ('customerToken' in user) {
|
|
102
|
+
throw configError(
|
|
103
|
+
'customerToken is not a setUser field; pass it in the config or call setUserToken'
|
|
104
|
+
);
|
|
105
|
+
}
|
|
59
106
|
NativeNewinstanceChat.setUser(user);
|
|
60
107
|
}
|
|
61
108
|
|
|
109
|
+
setUserToken(customerToken: string): Promise<void> {
|
|
110
|
+
if (!this.customerTokenMode) {
|
|
111
|
+
throw configError(
|
|
112
|
+
'setUserToken is only available when the SDK is configured with customerToken'
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
const token = trimmed(customerToken);
|
|
116
|
+
if (!token) throw configError('customerToken must not be blank');
|
|
117
|
+
return NativeNewinstanceChat.setUserToken(token);
|
|
118
|
+
}
|
|
119
|
+
|
|
62
120
|
/**
|
|
63
121
|
* Fetch org config, restore the saved conversation, and connect the
|
|
64
122
|
* subscription transport in the background. Concurrent callers share
|
|
65
123
|
* the same in-flight init.
|
|
66
124
|
*/
|
|
67
125
|
initialize(): Promise<void> {
|
|
68
|
-
return NativeNewinstanceChat.initialize();
|
|
126
|
+
return this.ready.then(() => NativeNewinstanceChat.initialize());
|
|
69
127
|
}
|
|
70
128
|
|
|
71
129
|
/** Present the native chat screen modally over the current RN host. */
|
|
@@ -93,7 +151,9 @@ export class NewinstanceChat {
|
|
|
93
151
|
*/
|
|
94
152
|
attachFile(opts: AttachmentInput): Promise<string> {
|
|
95
153
|
if (!opts.source && !opts.uri) {
|
|
96
|
-
return Promise.reject(
|
|
154
|
+
return Promise.reject(
|
|
155
|
+
new Error('attachFile requires `source` (or the legacy `uri`)')
|
|
156
|
+
);
|
|
97
157
|
}
|
|
98
158
|
return NativeNewinstanceChat.attachFile(opts);
|
|
99
159
|
}
|
package/src/types.ts
CHANGED
|
@@ -7,14 +7,44 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
export interface NewinstanceChatConfig {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
customerToken?: string;
|
|
12
12
|
/** Backend base URL. Defaults to `https://service.newinstance.cloud`. */
|
|
13
13
|
baseUrl?: string;
|
|
14
14
|
/** Subscription transport. `'sse'` (default in production), `'ws'`, or omit to let the server choose. */
|
|
15
15
|
transport?: 'sse' | 'ws';
|
|
16
16
|
/** Optional message pre-seeded as the first outbound message. */
|
|
17
17
|
initialMessage?: string;
|
|
18
|
+
theme?: ChatThemeOverride;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ChatThemeOverride {
|
|
22
|
+
mode?: 'light' | 'dark';
|
|
23
|
+
chatBackground?: string;
|
|
24
|
+
headerBackground?: string;
|
|
25
|
+
headerPrimaryText?: string;
|
|
26
|
+
headerSecondaryText?: string;
|
|
27
|
+
headerIcon?: string;
|
|
28
|
+
closeButton?: string;
|
|
29
|
+
receivedBubble?: string;
|
|
30
|
+
receivedText?: string;
|
|
31
|
+
receivedTimestamp?: string;
|
|
32
|
+
sentBubble?: string;
|
|
33
|
+
sentText?: string;
|
|
34
|
+
sentTimestamp?: string;
|
|
35
|
+
systemMessageText?: string;
|
|
36
|
+
daySeparatorBackground?: string;
|
|
37
|
+
daySeparatorText?: string;
|
|
38
|
+
footerContainer?: string;
|
|
39
|
+
chatInputBackground?: string;
|
|
40
|
+
chatInputText?: string;
|
|
41
|
+
chatInputPlaceholder?: string;
|
|
42
|
+
chatInputBorder?: string;
|
|
43
|
+
sendButtonBackground?: string;
|
|
44
|
+
sendButtonIcon?: string;
|
|
45
|
+
attachmentButton?: string;
|
|
46
|
+
unreadBadgeBackground?: string;
|
|
47
|
+
unreadBadgeText?: string;
|
|
18
48
|
}
|
|
19
49
|
|
|
20
50
|
export interface ChatUser {
|
|
@@ -139,9 +169,25 @@ export type NewinstanceChatErrorType =
|
|
|
139
169
|
| 'auth'
|
|
140
170
|
| 'system';
|
|
141
171
|
|
|
172
|
+
export type NewinstanceChatErrorCode =
|
|
173
|
+
| 'INVALID_PUBLIC_KEY'
|
|
174
|
+
| 'MISSING_WIDGET_KEY'
|
|
175
|
+
| 'CHAT_CONFIG_UNAVAILABLE'
|
|
176
|
+
| 'CHAT_DISABLED'
|
|
177
|
+
| 'CONFIG_FETCH_FAILED'
|
|
178
|
+
| 'INVALID_IDENTITY_TOKEN'
|
|
179
|
+
| 'NETWORK_ERROR'
|
|
180
|
+
| 'TRANSPORT_ERROR'
|
|
181
|
+
| 'CHAT_INITIALIZATION_FAILED'
|
|
182
|
+
| 'MESSAGE_SEND_FAILED'
|
|
183
|
+
| 'ATTACHMENT_FAILED'
|
|
184
|
+
| 'SERVER_ERROR';
|
|
185
|
+
|
|
142
186
|
export interface NewinstanceChatError {
|
|
187
|
+
code?: NewinstanceChatErrorCode;
|
|
143
188
|
type: NewinstanceChatErrorType;
|
|
144
189
|
message: string;
|
|
190
|
+
developerMessage?: string;
|
|
145
191
|
recoverable: boolean;
|
|
146
192
|
}
|
|
147
193
|
|