@convokitapp/react-native 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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -0
  3. package/lib/commonjs/client.js +79 -0
  4. package/lib/commonjs/client.js.map +1 -0
  5. package/lib/commonjs/encoding.js +90 -0
  6. package/lib/commonjs/encoding.js.map +1 -0
  7. package/lib/commonjs/expo.js +60 -0
  8. package/lib/commonjs/expo.js.map +1 -0
  9. package/lib/commonjs/exports.js +58 -0
  10. package/lib/commonjs/exports.js.map +1 -0
  11. package/lib/commonjs/index.js +21 -0
  12. package/lib/commonjs/index.js.map +1 -0
  13. package/lib/commonjs/package.json +1 -0
  14. package/lib/commonjs/runtime.js +53 -0
  15. package/lib/commonjs/runtime.js.map +1 -0
  16. package/lib/module/client.js +74 -0
  17. package/lib/module/client.js.map +1 -0
  18. package/lib/module/encoding.js +86 -0
  19. package/lib/module/encoding.js.map +1 -0
  20. package/lib/module/expo.js +39 -0
  21. package/lib/module/expo.js.map +1 -0
  22. package/lib/module/exports.js +7 -0
  23. package/lib/module/exports.js.map +1 -0
  24. package/lib/module/index.js +8 -0
  25. package/lib/module/index.js.map +1 -0
  26. package/lib/module/package.json +1 -0
  27. package/lib/module/runtime.js +47 -0
  28. package/lib/module/runtime.js.map +1 -0
  29. package/lib/typescript/commonjs/client.d.ts +26 -0
  30. package/lib/typescript/commonjs/client.d.ts.map +1 -0
  31. package/lib/typescript/commonjs/encoding.d.ts +3 -0
  32. package/lib/typescript/commonjs/encoding.d.ts.map +1 -0
  33. package/lib/typescript/commonjs/expo.d.ts +9 -0
  34. package/lib/typescript/commonjs/expo.d.ts.map +1 -0
  35. package/lib/typescript/commonjs/exports.d.ts +8 -0
  36. package/lib/typescript/commonjs/exports.d.ts.map +1 -0
  37. package/lib/typescript/commonjs/index.d.ts +4 -0
  38. package/lib/typescript/commonjs/index.d.ts.map +1 -0
  39. package/lib/typescript/commonjs/package.json +1 -0
  40. package/lib/typescript/commonjs/runtime.d.ts +21 -0
  41. package/lib/typescript/commonjs/runtime.d.ts.map +1 -0
  42. package/lib/typescript/module/client.d.ts +26 -0
  43. package/lib/typescript/module/client.d.ts.map +1 -0
  44. package/lib/typescript/module/encoding.d.ts +3 -0
  45. package/lib/typescript/module/encoding.d.ts.map +1 -0
  46. package/lib/typescript/module/expo.d.ts +9 -0
  47. package/lib/typescript/module/expo.d.ts.map +1 -0
  48. package/lib/typescript/module/exports.d.ts +8 -0
  49. package/lib/typescript/module/exports.d.ts.map +1 -0
  50. package/lib/typescript/module/index.d.ts +4 -0
  51. package/lib/typescript/module/index.d.ts.map +1 -0
  52. package/lib/typescript/module/package.json +1 -0
  53. package/lib/typescript/module/runtime.d.ts +21 -0
  54. package/lib/typescript/module/runtime.d.ts.map +1 -0
  55. package/package.json +78 -0
  56. package/src/client.ts +92 -0
  57. package/src/encoding.ts +69 -0
  58. package/src/expo.ts +38 -0
  59. package/src/exports.ts +20 -0
  60. package/src/index.ts +7 -0
  61. package/src/runtime.ts +64 -0
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ import 'react-native-url-polyfill/auto';
4
+ import * as ExpoCrypto from 'expo-crypto';
5
+ import * as FileSystem from 'expo-file-system';
6
+ import { ConvoKitClient as BaseClient } from "./client.js";
7
+ import { installEncodingPolyfills } from "./encoding.js";
8
+ installEncodingPolyfills();
9
+ if (typeof globalThis.crypto?.getRandomValues !== 'function') {
10
+ const current = globalThis.crypto ?? {};
11
+ Object.defineProperty(globalThis, 'crypto', {
12
+ configurable: true,
13
+ value: Object.assign(current, {
14
+ getRandomValues: ExpoCrypto.getRandomValues
15
+ })
16
+ });
17
+ }
18
+ export const expoRuntimeAdapter = {
19
+ async readFile(input) {
20
+ const file = new FileSystem.File(input.uri);
21
+ return file.arrayBuffer();
22
+ },
23
+ async saveDownload(input) {
24
+ if (!input.destinationUri) throw new Error('destinationUri is required by the Expo adapter');
25
+ const file = new FileSystem.File(input.destinationUri);
26
+ file.write(new Uint8Array(await input.blob.arrayBuffer()));
27
+ return file.uri;
28
+ }
29
+ };
30
+ export class ConvoKitClient extends BaseClient {
31
+ constructor(options) {
32
+ super({
33
+ ...options,
34
+ runtimeAdapter: options.runtimeAdapter ?? expoRuntimeAdapter
35
+ });
36
+ }
37
+ }
38
+ export * from "./exports.js";
39
+ //# sourceMappingURL=expo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["ExpoCrypto","FileSystem","ConvoKitClient","BaseClient","installEncodingPolyfills","globalThis","crypto","getRandomValues","current","Object","defineProperty","configurable","value","assign","expoRuntimeAdapter","readFile","input","file","File","uri","arrayBuffer","saveDownload","destinationUri","Error","write","Uint8Array","blob","constructor","options","runtimeAdapter"],"sourceRoot":"../../src","sources":["expo.ts"],"mappings":";;AAAA,OAAO,gCAAgC;AACvC,OAAO,KAAKA,UAAU,MAAM,aAAa;AACzC,OAAO,KAAKC,UAAU,MAAM,kBAAkB;AAE9C,SAASC,cAAc,IAAIC,UAAU,QAAuC,aAAU;AACtF,SAASC,wBAAwB,QAAQ,eAAY;AAGrDA,wBAAwB,CAAC,CAAC;AAE1B,IAAI,OAAOC,UAAU,CAACC,MAAM,EAAEC,eAAe,KAAK,UAAU,EAAE;EAC5D,MAAMC,OAAO,GAAGH,UAAU,CAACC,MAAM,IAAK,CAAC,CAAY;EACnDG,MAAM,CAACC,cAAc,CAACL,UAAU,EAAE,QAAQ,EAAE;IAC1CM,YAAY,EAAE,IAAI;IAClBC,KAAK,EAAEH,MAAM,CAACI,MAAM,CAACL,OAAO,EAAE;MAAED,eAAe,EAAEP,UAAU,CAACO;IAAgB,CAAC;EAC/E,CAAC,CAAC;AACJ;AAEA,OAAO,MAAMO,kBAA6C,GAAG;EAC3D,MAAMC,QAAQA,CAACC,KAAsB,EAAE;IACrC,MAAMC,IAAI,GAAG,IAAIhB,UAAU,CAACiB,IAAI,CAACF,KAAK,CAACG,GAAG,CAAC;IAC3C,OAAOF,IAAI,CAACG,WAAW,CAAC,CAAC;EAC3B,CAAC;EACD,MAAMC,YAAYA,CAACL,KAA2B,EAAE;IAC9C,IAAI,CAACA,KAAK,CAACM,cAAc,EAAE,MAAM,IAAIC,KAAK,CAAC,gDAAgD,CAAC;IAC5F,MAAMN,IAAI,GAAG,IAAIhB,UAAU,CAACiB,IAAI,CAACF,KAAK,CAACM,cAAc,CAAC;IACtDL,IAAI,CAACO,KAAK,CAAC,IAAIC,UAAU,CAAC,MAAMT,KAAK,CAACU,IAAI,CAACN,WAAW,CAAC,CAAC,CAAC,CAAC;IAC1D,OAAOH,IAAI,CAACE,GAAG;EACjB;AACF,CAAC;AAED,OAAO,MAAMjB,cAAc,SAASC,UAAU,CAAC;EAC7CwB,WAAWA,CAACC,OAAiC,EAAE;IAC7C,KAAK,CAAC;MAAE,GAAGA,OAAO;MAAEC,cAAc,EAAED,OAAO,CAACC,cAAc,IAAIf;IAAmB,CAAC,CAAC;EACrF;AACF;AAEA,cAAc,cAAW","ignoreList":[]}
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+
3
+ export { ConvoKitClient } from "./client.js";
4
+ export { assertReactNativeRuntime, fetchRuntimeAdapter } from "./runtime.js";
5
+ export { installEncodingPolyfills } from "./encoding.js";
6
+ export { CONVOKIT_API_URL, ConvoKitError, ConvoKitRealtime, createClientMessageId } from '@convokitapp/sdk';
7
+ //# sourceMappingURL=exports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["ConvoKitClient","assertReactNativeRuntime","fetchRuntimeAdapter","installEncodingPolyfills","CONVOKIT_API_URL","ConvoKitError","ConvoKitRealtime","createClientMessageId"],"sourceRoot":"../../src","sources":["exports.ts"],"mappings":";;AAAA,SAASA,cAAc,QAAQ,aAAU;AAEzC,SAASC,wBAAwB,EAAEC,mBAAmB,QAAQ,cAAW;AACzE,SAASC,wBAAwB,QAAQ,eAAY;AAMrD,SAASC,gBAAgB,EAAEC,aAAa,EAAEC,gBAAgB,EAAEC,qBAAqB,QAAQ,kBAAkB","ignoreList":[]}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+
3
+ import 'react-native-url-polyfill/auto';
4
+ import 'react-native-get-random-values';
5
+ import { installEncodingPolyfills } from "./encoding.js";
6
+ installEncodingPolyfills();
7
+ export * from "./exports.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["installEncodingPolyfills"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,OAAO,gCAAgC;AACvC,OAAO,gCAAgC;AACvC,SAASA,wBAAwB,QAAQ,eAAY;AAErDA,wBAAwB,CAAC,CAAC;AAE1B,cAAc,cAAW","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+
3
+ import { ConvoKitError } from '@convokitapp/sdk';
4
+ export const fetchRuntimeAdapter = {
5
+ async readFile(input) {
6
+ if (!input.uri.trim()) {
7
+ throw new ConvoKitError('uri is required', {
8
+ code: 'INVALID_ARGUMENT'
9
+ });
10
+ }
11
+ let response;
12
+ try {
13
+ response = await fetch(input.uri);
14
+ } catch (cause) {
15
+ throw new ConvoKitError('Could not read the selected file', {
16
+ code: 'NATIVE_FILE_READ_FAILED',
17
+ cause
18
+ });
19
+ }
20
+ if (!response.ok) {
21
+ throw new ConvoKitError('Could not read the selected file', {
22
+ code: 'NATIVE_FILE_READ_FAILED',
23
+ status: response.status
24
+ });
25
+ }
26
+ return response.blob();
27
+ }
28
+ };
29
+ export function assertReactNativeRuntime() {
30
+ const missing = ['fetch', 'URL', 'WebSocket', 'atob', 'TextDecoder'].filter(key => typeof globalThis[key] === 'undefined');
31
+ if (typeof globalThis.crypto?.getRandomValues !== 'function') {
32
+ missing.push('crypto.getRandomValues');
33
+ }
34
+ if (missing.length) {
35
+ throw new ConvoKitError(`React Native runtime is missing: ${missing.join(', ')}`, {
36
+ code: 'REACT_NATIVE_RUNTIME_UNAVAILABLE'
37
+ });
38
+ }
39
+ }
40
+ export function requiredText(value, name) {
41
+ const result = value.trim();
42
+ if (!result) throw new ConvoKitError(`${name} is required`, {
43
+ code: 'INVALID_ARGUMENT'
44
+ });
45
+ return result;
46
+ }
47
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["ConvoKitError","fetchRuntimeAdapter","readFile","input","uri","trim","code","response","fetch","cause","ok","status","blob","assertReactNativeRuntime","missing","filter","key","globalThis","crypto","getRandomValues","push","length","join","requiredText","value","name","result"],"sourceRoot":"../../src","sources":["runtime.ts"],"mappings":";;AAAA,SAASA,aAAa,QAAyB,kBAAkB;AAqBjE,OAAO,MAAMC,mBAA8C,GAAG;EAC5D,MAAMC,QAAQA,CAACC,KAAK,EAAE;IACpB,IAAI,CAACA,KAAK,CAACC,GAAG,CAACC,IAAI,CAAC,CAAC,EAAE;MACrB,MAAM,IAAIL,aAAa,CAAC,iBAAiB,EAAE;QAAEM,IAAI,EAAE;MAAmB,CAAC,CAAC;IAC1E;IACA,IAAIC,QAAkB;IACtB,IAAI;MACFA,QAAQ,GAAG,MAAMC,KAAK,CAACL,KAAK,CAACC,GAAG,CAAC;IACnC,CAAC,CAAC,OAAOK,KAAK,EAAE;MACd,MAAM,IAAIT,aAAa,CAAC,kCAAkC,EAAE;QAC1DM,IAAI,EAAE,yBAAyB;QAC/BG;MACF,CAAC,CAAC;IACJ;IACA,IAAI,CAACF,QAAQ,CAACG,EAAE,EAAE;MAChB,MAAM,IAAIV,aAAa,CAAC,kCAAkC,EAAE;QAC1DM,IAAI,EAAE,yBAAyB;QAC/BK,MAAM,EAAEJ,QAAQ,CAACI;MACnB,CAAC,CAAC;IACJ;IACA,OAAOJ,QAAQ,CAACK,IAAI,CAAC,CAAC;EACxB;AACF,CAAC;AAED,OAAO,SAASC,wBAAwBA,CAAA,EAAS;EAC/C,MAAMC,OAAO,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,CAAC,CAACC,MAAM,CACzEC,GAAG,IAAI,OAAQC,UAAU,CAA6BD,GAAG,CAAC,KAAK,WACjE,CAAC;EACD,IAAI,OAAOC,UAAU,CAACC,MAAM,EAAEC,eAAe,KAAK,UAAU,EAAE;IAC5DL,OAAO,CAACM,IAAI,CAAC,wBAAwB,CAAC;EACxC;EACA,IAAIN,OAAO,CAACO,MAAM,EAAE;IAClB,MAAM,IAAIrB,aAAa,CAAC,oCAAoCc,OAAO,CAACQ,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE;MAChFhB,IAAI,EAAE;IACR,CAAC,CAAC;EACJ;AACF;AAEA,OAAO,SAASiB,YAAYA,CAACC,KAAa,EAAEC,IAAY,EAAU;EAChE,MAAMC,MAAM,GAAGF,KAAK,CAACnB,IAAI,CAAC,CAAC;EAC3B,IAAI,CAACqB,MAAM,EAAE,MAAM,IAAI1B,aAAa,CAAC,GAAGyB,IAAI,cAAc,EAAE;IAAEnB,IAAI,EAAE;EAAmB,CAAC,CAAC;EACzF,OAAOoB,MAAM;AACf","ignoreList":[]}
@@ -0,0 +1,26 @@
1
+ import { ConvoKitClient as JavaScriptConvoKitClient, type ConvoKitClientOptions, type MessageMedia } from '@convokitapp/sdk';
2
+ import { type DownloadedMediaInput, type NativeFileInput, type ReactNativeRuntimeAdapter } from './runtime';
3
+ export interface ReactNativeClientOptions extends ConvoKitClientOptions {
4
+ runtimeAdapter?: ReactNativeRuntimeAdapter;
5
+ }
6
+ export interface NativeMessageMediaInput extends NativeFileInput {
7
+ conversationId: string;
8
+ mediaType?: 'image' | 'file';
9
+ metadata?: Record<string, string | number | boolean | null>;
10
+ }
11
+ export declare class ConvoKitClient extends JavaScriptConvoKitClient {
12
+ readonly runtimeAdapter: ReactNativeRuntimeAdapter;
13
+ constructor(options: ReactNativeClientOptions);
14
+ uploadUserAvatarFromUri(input: NativeFileInput & {
15
+ userId: string;
16
+ }): Promise<string>;
17
+ uploadConversationImageFromUri(input: NativeFileInput & {
18
+ conversationId: string;
19
+ }): Promise<string>;
20
+ uploadMessageMediaFromUri(input: NativeMessageMediaInput): Promise<{
21
+ url: string;
22
+ media: MessageMedia;
23
+ }>;
24
+ saveDownloadedMedia(url: string, output: Omit<DownloadedMediaInput, 'blob'>): Promise<string>;
25
+ }
26
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,IAAI,wBAAwB,EAE1C,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC/B,MAAM,WAAW,CAAA;AAElB,MAAM,WAAW,wBAAyB,SAAQ,qBAAqB;IACrE,cAAc,CAAC,EAAE,yBAAyB,CAAA;CAC3C;AAED,MAAM,WAAW,uBAAwB,SAAQ,eAAe;IAC9D,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,CAAA;CAC5D;AAED,qBAAa,cAAe,SAAQ,wBAAwB;IAC1D,QAAQ,CAAC,cAAc,EAAE,yBAAyB,CAAA;gBAEtC,OAAO,EAAE,wBAAwB;IAMvC,uBAAuB,CAAC,KAAK,EAAE,eAAe,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAUrF,8BAA8B,CAClC,KAAK,EAAE,eAAe,GAAG;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,GAClD,OAAO,CAAC,MAAM,CAAC;IAUZ,yBAAyB,CAC7B,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CAAC;IAqB1C,mBAAmB,CACvB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC,GACzC,OAAO,CAAC,MAAM,CAAC;CASnB"}
@@ -0,0 +1,3 @@
1
+ /** Installs only encoding globals that the host React Native runtime does not provide. */
2
+ export declare function installEncodingPolyfills(): void;
3
+ //# sourceMappingURL=encoding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoding.d.ts","sourceRoot":"","sources":["../../../src/encoding.ts"],"names":[],"mappings":"AA6DA,0FAA0F;AAC1F,wBAAgB,wBAAwB,IAAI,IAAI,CAM/C"}
@@ -0,0 +1,9 @@
1
+ import 'react-native-url-polyfill/auto';
2
+ import { ConvoKitClient as BaseClient, type ReactNativeClientOptions } from './client';
3
+ import type { ReactNativeRuntimeAdapter } from './runtime';
4
+ export declare const expoRuntimeAdapter: ReactNativeRuntimeAdapter;
5
+ export declare class ConvoKitClient extends BaseClient {
6
+ constructor(options: ReactNativeClientOptions);
7
+ }
8
+ export * from './exports';
9
+ //# sourceMappingURL=expo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expo.d.ts","sourceRoot":"","sources":["../../../src/expo.ts"],"names":[],"mappings":"AAAA,OAAO,gCAAgC,CAAA;AAIvC,OAAO,EAAE,cAAc,IAAI,UAAU,EAAE,KAAK,wBAAwB,EAAE,MAAM,UAAU,CAAA;AAEtF,OAAO,KAAK,EAAyC,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAYjG,eAAO,MAAM,kBAAkB,EAAE,yBAWhC,CAAA;AAED,qBAAa,cAAe,SAAQ,UAAU;gBAChC,OAAO,EAAE,wBAAwB;CAG9C;AAED,cAAc,WAAW,CAAA"}
@@ -0,0 +1,8 @@
1
+ export { ConvoKitClient } from './client';
2
+ export type { NativeMessageMediaInput, ReactNativeClientOptions } from './client';
3
+ export { assertReactNativeRuntime, fetchRuntimeAdapter } from './runtime';
4
+ export { installEncodingPolyfills } from './encoding';
5
+ export type { DownloadedMediaInput, NativeFileInput, ReactNativeRuntimeAdapter, } from './runtime';
6
+ export { CONVOKIT_API_URL, ConvoKitError, ConvoKitRealtime, createClientMessageId } from '@convokitapp/sdk';
7
+ export type { AppUser, ContactMedia, Conversation, ConversationListOptions, ConvoKitClientOptions, CreateConversationInput, FetchImplementation, FileMedia, ImageMedia, JsonPrimitive, JsonValue, LocationMedia, Message, MessageDeletedEvent, MessageEvent, MessageListOptions, MessageMedia, Page, PaginationOptions, Participant, PresenceEvent, ReadEvent, RealtimeConnectionEvent, RealtimeConnectionHandlers, RealtimeHandlers, RealtimeStatus, RealtimeSubscription, SendMessageInput, TokenProvider, TypingEvent, UploadBody, UploadConversationImageInput, UploadInput, UploadMessageMediaInput, UploadUserAvatarInput, } from '@convokitapp/sdk';
8
+ //# sourceMappingURL=exports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../../src/exports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACzC,YAAY,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAA;AACjF,OAAO,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AACzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AACrD,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,yBAAyB,GAC1B,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAC3G,YAAY,EACV,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,uBAAuB,EAAE,qBAAqB,EACnF,uBAAuB,EAAE,mBAAmB,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAClF,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,kBAAkB,EACxF,YAAY,EAAE,IAAI,EAAE,iBAAiB,EAAE,WAAW,EAAE,aAAa,EACjE,SAAS,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,gBAAgB,EAChF,cAAc,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,aAAa,EAAE,WAAW,EAClF,UAAU,EAAE,4BAA4B,EAAE,WAAW,EAAE,uBAAuB,EAC9E,qBAAqB,GACtB,MAAM,kBAAkB,CAAA"}
@@ -0,0 +1,4 @@
1
+ import 'react-native-url-polyfill/auto';
2
+ import 'react-native-get-random-values';
3
+ export * from './exports';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,gCAAgC,CAAA;AACvC,OAAO,gCAAgC,CAAA;AAKvC,cAAc,WAAW,CAAA"}
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,21 @@
1
+ import { type UploadBody } from '@convokitapp/sdk';
2
+ export interface NativeFileInput {
3
+ uri: string;
4
+ fileName: string;
5
+ contentType?: string;
6
+ size?: number;
7
+ }
8
+ export interface DownloadedMediaInput {
9
+ blob: Blob;
10
+ fileName: string;
11
+ contentType?: string;
12
+ destinationUri?: string;
13
+ }
14
+ export interface ReactNativeRuntimeAdapter {
15
+ readFile(input: NativeFileInput): Promise<UploadBody>;
16
+ saveDownload?(input: DownloadedMediaInput): Promise<string>;
17
+ }
18
+ export declare const fetchRuntimeAdapter: ReactNativeRuntimeAdapter;
19
+ export declare function assertReactNativeRuntime(): void;
20
+ export declare function requiredText(value: string, name: string): string;
21
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAEjE,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,IAAI,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IACrD,YAAY,CAAC,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CAC5D;AAED,eAAO,MAAM,mBAAmB,EAAE,yBAsBjC,CAAA;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAY/C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIhE"}
@@ -0,0 +1,26 @@
1
+ import { ConvoKitClient as JavaScriptConvoKitClient, type ConvoKitClientOptions, type MessageMedia } from '@convokitapp/sdk';
2
+ import { type DownloadedMediaInput, type NativeFileInput, type ReactNativeRuntimeAdapter } from './runtime';
3
+ export interface ReactNativeClientOptions extends ConvoKitClientOptions {
4
+ runtimeAdapter?: ReactNativeRuntimeAdapter;
5
+ }
6
+ export interface NativeMessageMediaInput extends NativeFileInput {
7
+ conversationId: string;
8
+ mediaType?: 'image' | 'file';
9
+ metadata?: Record<string, string | number | boolean | null>;
10
+ }
11
+ export declare class ConvoKitClient extends JavaScriptConvoKitClient {
12
+ readonly runtimeAdapter: ReactNativeRuntimeAdapter;
13
+ constructor(options: ReactNativeClientOptions);
14
+ uploadUserAvatarFromUri(input: NativeFileInput & {
15
+ userId: string;
16
+ }): Promise<string>;
17
+ uploadConversationImageFromUri(input: NativeFileInput & {
18
+ conversationId: string;
19
+ }): Promise<string>;
20
+ uploadMessageMediaFromUri(input: NativeMessageMediaInput): Promise<{
21
+ url: string;
22
+ media: MessageMedia;
23
+ }>;
24
+ saveDownloadedMedia(url: string, output: Omit<DownloadedMediaInput, 'blob'>): Promise<string>;
25
+ }
26
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,IAAI,wBAAwB,EAE1C,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC/B,MAAM,WAAW,CAAA;AAElB,MAAM,WAAW,wBAAyB,SAAQ,qBAAqB;IACrE,cAAc,CAAC,EAAE,yBAAyB,CAAA;CAC3C;AAED,MAAM,WAAW,uBAAwB,SAAQ,eAAe;IAC9D,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,CAAA;CAC5D;AAED,qBAAa,cAAe,SAAQ,wBAAwB;IAC1D,QAAQ,CAAC,cAAc,EAAE,yBAAyB,CAAA;gBAEtC,OAAO,EAAE,wBAAwB;IAMvC,uBAAuB,CAAC,KAAK,EAAE,eAAe,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAUrF,8BAA8B,CAClC,KAAK,EAAE,eAAe,GAAG;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,GAClD,OAAO,CAAC,MAAM,CAAC;IAUZ,yBAAyB,CAC7B,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CAAC;IAqB1C,mBAAmB,CACvB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC,GACzC,OAAO,CAAC,MAAM,CAAC;CASnB"}
@@ -0,0 +1,3 @@
1
+ /** Installs only encoding globals that the host React Native runtime does not provide. */
2
+ export declare function installEncodingPolyfills(): void;
3
+ //# sourceMappingURL=encoding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoding.d.ts","sourceRoot":"","sources":["../../../src/encoding.ts"],"names":[],"mappings":"AA6DA,0FAA0F;AAC1F,wBAAgB,wBAAwB,IAAI,IAAI,CAM/C"}
@@ -0,0 +1,9 @@
1
+ import 'react-native-url-polyfill/auto';
2
+ import { ConvoKitClient as BaseClient, type ReactNativeClientOptions } from './client';
3
+ import type { ReactNativeRuntimeAdapter } from './runtime';
4
+ export declare const expoRuntimeAdapter: ReactNativeRuntimeAdapter;
5
+ export declare class ConvoKitClient extends BaseClient {
6
+ constructor(options: ReactNativeClientOptions);
7
+ }
8
+ export * from './exports';
9
+ //# sourceMappingURL=expo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expo.d.ts","sourceRoot":"","sources":["../../../src/expo.ts"],"names":[],"mappings":"AAAA,OAAO,gCAAgC,CAAA;AAIvC,OAAO,EAAE,cAAc,IAAI,UAAU,EAAE,KAAK,wBAAwB,EAAE,MAAM,UAAU,CAAA;AAEtF,OAAO,KAAK,EAAyC,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAYjG,eAAO,MAAM,kBAAkB,EAAE,yBAWhC,CAAA;AAED,qBAAa,cAAe,SAAQ,UAAU;gBAChC,OAAO,EAAE,wBAAwB;CAG9C;AAED,cAAc,WAAW,CAAA"}
@@ -0,0 +1,8 @@
1
+ export { ConvoKitClient } from './client';
2
+ export type { NativeMessageMediaInput, ReactNativeClientOptions } from './client';
3
+ export { assertReactNativeRuntime, fetchRuntimeAdapter } from './runtime';
4
+ export { installEncodingPolyfills } from './encoding';
5
+ export type { DownloadedMediaInput, NativeFileInput, ReactNativeRuntimeAdapter, } from './runtime';
6
+ export { CONVOKIT_API_URL, ConvoKitError, ConvoKitRealtime, createClientMessageId } from '@convokitapp/sdk';
7
+ export type { AppUser, ContactMedia, Conversation, ConversationListOptions, ConvoKitClientOptions, CreateConversationInput, FetchImplementation, FileMedia, ImageMedia, JsonPrimitive, JsonValue, LocationMedia, Message, MessageDeletedEvent, MessageEvent, MessageListOptions, MessageMedia, Page, PaginationOptions, Participant, PresenceEvent, ReadEvent, RealtimeConnectionEvent, RealtimeConnectionHandlers, RealtimeHandlers, RealtimeStatus, RealtimeSubscription, SendMessageInput, TokenProvider, TypingEvent, UploadBody, UploadConversationImageInput, UploadInput, UploadMessageMediaInput, UploadUserAvatarInput, } from '@convokitapp/sdk';
8
+ //# sourceMappingURL=exports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../../src/exports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACzC,YAAY,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAA;AACjF,OAAO,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AACzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AACrD,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,yBAAyB,GAC1B,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAC3G,YAAY,EACV,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,uBAAuB,EAAE,qBAAqB,EACnF,uBAAuB,EAAE,mBAAmB,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAClF,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,kBAAkB,EACxF,YAAY,EAAE,IAAI,EAAE,iBAAiB,EAAE,WAAW,EAAE,aAAa,EACjE,SAAS,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,gBAAgB,EAChF,cAAc,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,aAAa,EAAE,WAAW,EAClF,UAAU,EAAE,4BAA4B,EAAE,WAAW,EAAE,uBAAuB,EAC9E,qBAAqB,GACtB,MAAM,kBAAkB,CAAA"}
@@ -0,0 +1,4 @@
1
+ import 'react-native-url-polyfill/auto';
2
+ import 'react-native-get-random-values';
3
+ export * from './exports';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,gCAAgC,CAAA;AACvC,OAAO,gCAAgC,CAAA;AAKvC,cAAc,WAAW,CAAA"}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,21 @@
1
+ import { type UploadBody } from '@convokitapp/sdk';
2
+ export interface NativeFileInput {
3
+ uri: string;
4
+ fileName: string;
5
+ contentType?: string;
6
+ size?: number;
7
+ }
8
+ export interface DownloadedMediaInput {
9
+ blob: Blob;
10
+ fileName: string;
11
+ contentType?: string;
12
+ destinationUri?: string;
13
+ }
14
+ export interface ReactNativeRuntimeAdapter {
15
+ readFile(input: NativeFileInput): Promise<UploadBody>;
16
+ saveDownload?(input: DownloadedMediaInput): Promise<string>;
17
+ }
18
+ export declare const fetchRuntimeAdapter: ReactNativeRuntimeAdapter;
19
+ export declare function assertReactNativeRuntime(): void;
20
+ export declare function requiredText(value: string, name: string): string;
21
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAEjE,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,IAAI,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IACrD,YAAY,CAAC,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CAC5D;AAED,eAAO,MAAM,mBAAmB,EAAE,yBAsBjC,CAAA;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAY/C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIhE"}
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@convokitapp/react-native",
3
+ "version": "0.4.0",
4
+ "description": "React Native runtime adapters for the ConvoKit JavaScript SDK.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "source": "./src/index.ts",
8
+ "main": "./lib/commonjs/index.js",
9
+ "module": "./lib/module/index.js",
10
+ "react-native": "./src/index.ts",
11
+ "types": "./lib/typescript/module/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "react-native": "./src/index.ts",
15
+ "import": {
16
+ "types": "./lib/typescript/module/index.d.ts",
17
+ "default": "./lib/module/index.js"
18
+ },
19
+ "require": {
20
+ "types": "./lib/typescript/commonjs/index.d.ts",
21
+ "default": "./lib/commonjs/index.js"
22
+ }
23
+ },
24
+ "./expo": {
25
+ "react-native": "./src/expo.ts",
26
+ "import": {
27
+ "types": "./lib/typescript/module/expo.d.ts",
28
+ "default": "./lib/module/expo.js"
29
+ },
30
+ "require": {
31
+ "types": "./lib/typescript/commonjs/expo.d.ts",
32
+ "default": "./lib/commonjs/expo.js"
33
+ }
34
+ }
35
+ },
36
+ "files": ["src", "lib", "README.md"],
37
+ "engines": { "node": ">=22.13.0" },
38
+ "peerDependencies": {
39
+ "@convokitapp/sdk": ">=0.4.0 <0.5.0",
40
+ "expo": ">=57.0.0 <58.0.0",
41
+ "expo-crypto": ">=15.0.0",
42
+ "expo-file-system": ">=19.0.0",
43
+ "react": ">=19.2.0",
44
+ "react-native": ">=0.86.0 <0.88.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "expo": { "optional": true },
48
+ "expo-crypto": { "optional": true },
49
+ "expo-file-system": { "optional": true }
50
+ },
51
+ "dependencies": {
52
+ "react-native-get-random-values": "^1.11.0",
53
+ "react-native-url-polyfill": "^2.0.0"
54
+ },
55
+ "devDependencies": {
56
+ "@convokitapp/sdk": "^0.4.0",
57
+ "@types/react": "^19.2.0",
58
+ "expo": "~57.0.0",
59
+ "expo-crypto": "~57.0.0",
60
+ "expo-file-system": "~57.0.6",
61
+ "react": "^19.2.0",
62
+ "react-native": "^0.87.0",
63
+ "react-native-builder-bob": "^0.40.0",
64
+ "typescript": "^5.9.3",
65
+ "vitest": "^4.0.0"
66
+ },
67
+ "scripts": {
68
+ "build": "bob build",
69
+ "check:boundaries": "node scripts/check-boundaries.mjs",
70
+ "typecheck": "tsc --noEmit",
71
+ "test": "vitest run"
72
+ },
73
+ "react-native-builder-bob": {
74
+ "source": "src",
75
+ "output": "lib",
76
+ "targets": [["commonjs", {"esm": true}], ["module", {"esm": true}], ["typescript", {"project": "tsconfig.build.json"}]]
77
+ }
78
+ }
package/src/client.ts ADDED
@@ -0,0 +1,92 @@
1
+ import {
2
+ ConvoKitClient as JavaScriptConvoKitClient,
3
+ ConvoKitError,
4
+ type ConvoKitClientOptions,
5
+ type MessageMedia,
6
+ } from '@convokitapp/sdk'
7
+ import {
8
+ assertReactNativeRuntime,
9
+ fetchRuntimeAdapter,
10
+ requiredText,
11
+ type DownloadedMediaInput,
12
+ type NativeFileInput,
13
+ type ReactNativeRuntimeAdapter,
14
+ } from './runtime'
15
+
16
+ export interface ReactNativeClientOptions extends ConvoKitClientOptions {
17
+ runtimeAdapter?: ReactNativeRuntimeAdapter
18
+ }
19
+
20
+ export interface NativeMessageMediaInput extends NativeFileInput {
21
+ conversationId: string
22
+ mediaType?: 'image' | 'file'
23
+ metadata?: Record<string, string | number | boolean | null>
24
+ }
25
+
26
+ export class ConvoKitClient extends JavaScriptConvoKitClient {
27
+ readonly runtimeAdapter: ReactNativeRuntimeAdapter
28
+
29
+ constructor(options: ReactNativeClientOptions) {
30
+ assertReactNativeRuntime()
31
+ super(options)
32
+ this.runtimeAdapter = options.runtimeAdapter ?? fetchRuntimeAdapter
33
+ }
34
+
35
+ async uploadUserAvatarFromUri(input: NativeFileInput & { userId: string }): Promise<string> {
36
+ const bytes = await this.runtimeAdapter.readFile(input)
37
+ return this.uploadUserAvatar({
38
+ userId: requiredText(input.userId, 'userId'),
39
+ bytes,
40
+ fileName: requiredText(input.fileName, 'fileName'),
41
+ ...(input.contentType === undefined ? {} : { contentType: input.contentType }),
42
+ })
43
+ }
44
+
45
+ async uploadConversationImageFromUri(
46
+ input: NativeFileInput & { conversationId: string },
47
+ ): Promise<string> {
48
+ const bytes = await this.runtimeAdapter.readFile(input)
49
+ return this.uploadConversationImage({
50
+ conversationId: requiredText(input.conversationId, 'conversationId'),
51
+ bytes,
52
+ fileName: requiredText(input.fileName, 'fileName'),
53
+ ...(input.contentType === undefined ? {} : { contentType: input.contentType }),
54
+ })
55
+ }
56
+
57
+ async uploadMessageMediaFromUri(
58
+ input: NativeMessageMediaInput,
59
+ ): Promise<{ url: string; media: MessageMedia }> {
60
+ const bytes = await this.runtimeAdapter.readFile(input)
61
+ const conversationId = requiredText(input.conversationId, 'conversationId')
62
+ const fileName = requiredText(input.fileName, 'fileName')
63
+ const url = await this.uploadMessageMedia({
64
+ conversationId, bytes, fileName,
65
+ ...(input.contentType === undefined ? {} : { contentType: input.contentType }),
66
+ })
67
+ const mediaType = input.mediaType ?? (input.contentType?.startsWith('image/') ? 'image' : 'file')
68
+ return {
69
+ url,
70
+ media: {
71
+ type: mediaType,
72
+ url,
73
+ name: fileName,
74
+ ...(input.size === undefined ? {} : { size: input.size }),
75
+ ...(input.metadata === undefined ? {} : { metadata: input.metadata }),
76
+ },
77
+ }
78
+ }
79
+
80
+ async saveDownloadedMedia(
81
+ url: string,
82
+ output: Omit<DownloadedMediaInput, 'blob'>,
83
+ ): Promise<string> {
84
+ if (!this.runtimeAdapter.saveDownload) {
85
+ throw new ConvoKitError('This runtime does not provide downloaded-file storage', {
86
+ code: 'NATIVE_FILE_SAVE_UNAVAILABLE',
87
+ })
88
+ }
89
+ const blob = await this.downloadMedia(url)
90
+ return this.runtimeAdapter.saveDownload({ ...output, blob })
91
+ }
92
+ }
@@ -0,0 +1,69 @@
1
+ const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
2
+
3
+ function decodeBase64(value: string): string {
4
+ const input = value.replace(/\s/g, '')
5
+ if (input.length % 4 === 1 || /[^A-Za-z0-9+/=]/.test(input)) throw new TypeError('Invalid base64 input')
6
+ let buffer = 0; let bits = 0; let output = ''
7
+ for (const character of input.replace(/=+$/, '')) {
8
+ const index = alphabet.indexOf(character)
9
+ if (index < 0) throw new TypeError('Invalid base64 input')
10
+ buffer = (buffer << 6) | index; bits += 6
11
+ if (bits >= 8) { bits -= 8; output += String.fromCharCode((buffer >> bits) & 0xff) }
12
+ }
13
+ return output
14
+ }
15
+
16
+ function encodeBase64(value: string): string {
17
+ let output = ''
18
+ for (let index = 0; index < value.length; index += 3) {
19
+ const first = value.charCodeAt(index)
20
+ const second = index + 1 < value.length ? value.charCodeAt(index + 1) : 0
21
+ const third = index + 2 < value.length ? value.charCodeAt(index + 2) : 0
22
+ if (first > 255 || second > 255 || third > 255) throw new TypeError('btoa only accepts Latin-1 input')
23
+ const packed = (first << 16) | (second << 8) | third
24
+ output += alphabet.charAt((packed >> 18) & 63) + alphabet.charAt((packed >> 12) & 63)
25
+ output += index + 1 < value.length ? alphabet.charAt((packed >> 6) & 63) : '='
26
+ output += index + 2 < value.length ? alphabet.charAt(packed & 63) : '='
27
+ }
28
+ return output
29
+ }
30
+
31
+ class ConvoKitTextEncoder {
32
+ encode(value = ''): Uint8Array {
33
+ const bytes: number[] = []
34
+ for (const character of value) {
35
+ const codePoint = character.codePointAt(0)!
36
+ if (codePoint <= 0x7f) bytes.push(codePoint)
37
+ else if (codePoint <= 0x7ff) bytes.push(0xc0 | codePoint >> 6, 0x80 | codePoint & 0x3f)
38
+ else if (codePoint <= 0xffff) bytes.push(0xe0 | codePoint >> 12, 0x80 | codePoint >> 6 & 0x3f, 0x80 | codePoint & 0x3f)
39
+ else bytes.push(0xf0 | codePoint >> 18, 0x80 | codePoint >> 12 & 0x3f, 0x80 | codePoint >> 6 & 0x3f, 0x80 | codePoint & 0x3f)
40
+ }
41
+ return new Uint8Array(bytes)
42
+ }
43
+ }
44
+
45
+ class ConvoKitTextDecoder {
46
+ decode(input?: AllowSharedBufferSource): string {
47
+ if (input === undefined) return ''
48
+ let bytes: Uint8Array
49
+ if (input instanceof ArrayBuffer ||
50
+ (typeof SharedArrayBuffer !== 'undefined' && input instanceof SharedArrayBuffer)) {
51
+ bytes = new Uint8Array(input)
52
+ } else {
53
+ const view = input as ArrayBufferView
54
+ bytes = new Uint8Array(view.buffer, view.byteOffset, view.byteLength)
55
+ }
56
+ let encoded = ''
57
+ for (const byte of bytes) encoded += `%${byte.toString(16).padStart(2, '0')}`
58
+ try { return decodeURIComponent(encoded) } catch { return '\uFFFD' }
59
+ }
60
+ }
61
+
62
+ /** Installs only encoding globals that the host React Native runtime does not provide. */
63
+ export function installEncodingPolyfills(): void {
64
+ const host = globalThis as typeof globalThis & Record<string, unknown>
65
+ if (typeof host.atob !== 'function') Object.defineProperty(host, 'atob', { configurable: true, value: decodeBase64 })
66
+ if (typeof host.btoa !== 'function') Object.defineProperty(host, 'btoa', { configurable: true, value: encodeBase64 })
67
+ if (typeof host.TextEncoder !== 'function') Object.defineProperty(host, 'TextEncoder', { configurable: true, value: ConvoKitTextEncoder })
68
+ if (typeof host.TextDecoder !== 'function') Object.defineProperty(host, 'TextDecoder', { configurable: true, value: ConvoKitTextDecoder })
69
+ }
package/src/expo.ts ADDED
@@ -0,0 +1,38 @@
1
+ import 'react-native-url-polyfill/auto'
2
+ import * as ExpoCrypto from 'expo-crypto'
3
+ import * as FileSystem from 'expo-file-system'
4
+
5
+ import { ConvoKitClient as BaseClient, type ReactNativeClientOptions } from './client'
6
+ import { installEncodingPolyfills } from './encoding'
7
+ import type { DownloadedMediaInput, NativeFileInput, ReactNativeRuntimeAdapter } from './runtime'
8
+
9
+ installEncodingPolyfills()
10
+
11
+ if (typeof globalThis.crypto?.getRandomValues !== 'function') {
12
+ const current = globalThis.crypto ?? ({} as Crypto)
13
+ Object.defineProperty(globalThis, 'crypto', {
14
+ configurable: true,
15
+ value: Object.assign(current, { getRandomValues: ExpoCrypto.getRandomValues }),
16
+ })
17
+ }
18
+
19
+ export const expoRuntimeAdapter: ReactNativeRuntimeAdapter = {
20
+ async readFile(input: NativeFileInput) {
21
+ const file = new FileSystem.File(input.uri)
22
+ return file.arrayBuffer()
23
+ },
24
+ async saveDownload(input: DownloadedMediaInput) {
25
+ if (!input.destinationUri) throw new Error('destinationUri is required by the Expo adapter')
26
+ const file = new FileSystem.File(input.destinationUri)
27
+ file.write(new Uint8Array(await input.blob.arrayBuffer()))
28
+ return file.uri
29
+ },
30
+ }
31
+
32
+ export class ConvoKitClient extends BaseClient {
33
+ constructor(options: ReactNativeClientOptions) {
34
+ super({ ...options, runtimeAdapter: options.runtimeAdapter ?? expoRuntimeAdapter })
35
+ }
36
+ }
37
+
38
+ export * from './exports'