@commzy/sdk-react-native 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +107 -0
  2. package/dist/auth/TokenManager.d.ts +49 -0
  3. package/dist/auth/TokenManager.d.ts.map +1 -0
  4. package/dist/auth/TokenManager.js +132 -0
  5. package/dist/auth/TokenManager.js.map +1 -0
  6. package/dist/client/CommzyClient.d.ts +57 -0
  7. package/dist/client/CommzyClient.d.ts.map +1 -0
  8. package/dist/client/CommzyClient.js +195 -0
  9. package/dist/client/CommzyClient.js.map +1 -0
  10. package/dist/device/deviceId.d.ts +19 -0
  11. package/dist/device/deviceId.d.ts.map +1 -0
  12. package/dist/device/deviceId.js +65 -0
  13. package/dist/device/deviceId.js.map +1 -0
  14. package/dist/index.d.ts +5 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +12 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/internal/emitter.d.ts +13 -0
  19. package/dist/internal/emitter.d.ts.map +1 -0
  20. package/dist/internal/emitter.js +41 -0
  21. package/dist/internal/emitter.js.map +1 -0
  22. package/dist/internal/jwt.d.ts +19 -0
  23. package/dist/internal/jwt.d.ts.map +1 -0
  24. package/dist/internal/jwt.js +64 -0
  25. package/dist/internal/jwt.js.map +1 -0
  26. package/dist/transport/RealtimeClient.d.ts +29 -0
  27. package/dist/transport/RealtimeClient.d.ts.map +1 -0
  28. package/dist/transport/RealtimeClient.js +72 -0
  29. package/dist/transport/RealtimeClient.js.map +1 -0
  30. package/dist/transport/RestClient.d.ts +31 -0
  31. package/dist/transport/RestClient.d.ts.map +1 -0
  32. package/dist/transport/RestClient.js +74 -0
  33. package/dist/transport/RestClient.js.map +1 -0
  34. package/dist/types.d.ts +88 -0
  35. package/dist/types.d.ts.map +1 -0
  36. package/dist/types.js +11 -0
  37. package/dist/types.js.map +1 -0
  38. package/package.json +61 -0
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # @commzy/sdk-react-native
2
+
3
+ Embeddable **React Native** SDK for [Commzy](https://commzy.ai) — add authenticated
4
+ chat, presence, and realtime communication to your app. White-label, multi-tenant.
5
+
6
+ > **Model (like Stripe):** your app never holds a secret. Your **backend** mints a
7
+ > short-lived token for your already-authenticated user (using your secret key), and
8
+ > the SDK consumes it. See the full [integration guide](https://docs.commzy.ai) for
9
+ > the backend piece.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @commzy/sdk-react-native
15
+ # peer: react-native >= 0.76
16
+ ```
17
+
18
+ ## Quick start
19
+
20
+ ```ts
21
+ import { CommzyClient } from '@commzy/sdk-react-native';
22
+
23
+ const client = new CommzyClient({
24
+ baseUrl: 'https://api.commzy.ai',
25
+ publishableKey: 'pk_live_…', // safe to ship in the app
26
+ tokenProvider: async ({ deviceId }) => {
27
+ // Call YOUR backend, which mints a Commzy token with your secret key (sk_).
28
+ const res = await fetch('https://your-backend.com/commzy-token', {
29
+ method: 'POST',
30
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${yourSession}` },
31
+ body: JSON.stringify({ deviceId }),
32
+ });
33
+ const { token } = await res.json();
34
+ return token; // the Commzy access token
35
+ },
36
+ });
37
+
38
+ client.on('stateChange', (s) => console.log(s.status)); // connecting → authenticated → connected
39
+ client.on('error', (e) => console.warn(e.code, e.message));
40
+
41
+ await client.connect();
42
+
43
+ const { orgId } = client.getState();
44
+ const channels = await client.restRequest({ path: '/v1/channels', query: { orgId } });
45
+
46
+ client.on('realtimeEvent', ({ name, payload }) => {
47
+ if (name === 'message:received') { /* render */ }
48
+ });
49
+ ```
50
+
51
+ ## What the SDK handles for you
52
+
53
+ - **Token lifecycle** — fetches tokens via your `tokenProvider`, **auto-renews** before
54
+ expiry, retries with backoff.
55
+ - **Realtime** — connects the WebSocket with the token and reconnects with a fresh one.
56
+ - **Device sessions** — generates a stable per-install `deviceId` so token renewals
57
+ reuse one session (multi-device supported).
58
+
59
+ ## API surface (this release)
60
+
61
+ | Member | Purpose |
62
+ |---|---|
63
+ | `new CommzyClient(config)` | Create the client (see `CommzyClientConfig`). |
64
+ | `connect()` | Authenticate + open the realtime session. |
65
+ | `restRequest({ method?, path, query?, body? })` | Authenticated REST call. |
66
+ | `signOut()` | Local teardown (drop token, close socket). |
67
+ | `getState()` / `getDeviceId()` | Current `SessionSnapshot` / device id. |
68
+ | `on(event, cb)` / `off(event, cb)` | `stateChange`, `error`, `realtimeEvent`. |
69
+ | `destroy()` | Tear down and remove all listeners. |
70
+
71
+ ### Config
72
+
73
+ ```ts
74
+ interface CommzyClientConfig {
75
+ baseUrl: string;
76
+ publishableKey: string;
77
+ tokenProvider: (p: { deviceId: string }) => Promise<string>;
78
+ storage?: StorageAdapter; // persist deviceId (e.g. AsyncStorage); defaults to memory
79
+ deviceId?: string; // override the generated device id
80
+ renewSkewSeconds?: number; // renew this many seconds before expiry (default 60)
81
+ }
82
+ ```
83
+
84
+ ### Persisting the device id (recommended)
85
+
86
+ Provide a `StorageAdapter` so the device id survives restarts:
87
+
88
+ ```ts
89
+ import AsyncStorage from '@react-native-async-storage/async-storage';
90
+
91
+ const storage = {
92
+ getItem: (k: string) => AsyncStorage.getItem(k),
93
+ setItem: (k: string, v: string) => AsyncStorage.setItem(k, v),
94
+ };
95
+ new CommzyClient({ /* … */, storage });
96
+ ```
97
+
98
+ ## Security
99
+
100
+ - Ship only the **publishable key** (`pk_`). The **secret key** (`sk_`) lives on your
101
+ backend and mints tokens there — never in the app.
102
+ - The SDK never mints tokens; it only consumes what your `tokenProvider` returns.
103
+
104
+ ---
105
+
106
+ Prebuilt UI components (`ChannelList`, `ChannelView`, …) and domain helpers are on the
107
+ roadmap. Today the SDK is a headless authenticated session over REST + WebSocket.
@@ -0,0 +1,49 @@
1
+ import { type DecodedToken } from '../internal/jwt.js';
2
+ import type { CommzyError, TokenProvider } from '../types.js';
3
+ export interface TokenState {
4
+ token: string;
5
+ decoded: DecodedToken;
6
+ }
7
+ interface TokenManagerOptions {
8
+ tokenProvider: TokenProvider;
9
+ deviceId: string;
10
+ /** Seconds before exp to renew. */
11
+ renewSkewSeconds: number;
12
+ /** Called after every successful (re)issue, including renewals. */
13
+ onToken: (state: TokenState) => void;
14
+ /** Called when acquiring/renewing a token ultimately fails. */
15
+ onError: (error: CommzyError) => void;
16
+ }
17
+ /**
18
+ * Owns the access token lifecycle: fetches it from the vendor's tokenProvider,
19
+ * decodes it to learn its expiry, and proactively renews before it lapses. On
20
+ * provider failure it retries with exponential backoff. The SDK never mints — it
21
+ * only consumes what the provider returns.
22
+ */
23
+ export declare class TokenManager {
24
+ private readonly opts;
25
+ private current;
26
+ private renewTimer;
27
+ private retryTimer;
28
+ private stopped;
29
+ private inflight;
30
+ constructor(options: TokenManagerOptions);
31
+ getToken(): TokenState | null;
32
+ /** Acquire the first token and arm renewal. Throws if the first fetch fails. */
33
+ start(): Promise<TokenState>;
34
+ /** Force an immediate renewal (e.g. after a 401). Coalesces concurrent calls. */
35
+ renewNow(): Promise<TokenState>;
36
+ /** Stop all timers and clear state. */
37
+ stop(): void;
38
+ private clearTimers;
39
+ /** Fetch a token from the provider, decode + validate it. Coalesces in-flight calls. */
40
+ private fetchToken;
41
+ /** Arm a timer to renew `renewSkewSeconds` before expiry (min 0). */
42
+ private scheduleRenewal;
43
+ /** Renew, retrying with exponential backoff on failure until stopped. */
44
+ private renewWithBackoff;
45
+ private error;
46
+ private asCommzyError;
47
+ }
48
+ export {};
49
+ //# sourceMappingURL=TokenManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenManager.d.ts","sourceRoot":"","sources":["../../src/auth/TokenManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE9D,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,YAAY,CAAC;CACvB;AAED,UAAU,mBAAmB;IAC3B,aAAa,EAAE,aAAa,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,gBAAgB,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACrC,+DAA+D;IAC/D,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;CACvC;AAKD;;;;;GAKG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;IAC3C,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAoC;gBAExC,OAAO,EAAE,mBAAmB;IAIxC,QAAQ,IAAI,UAAU,GAAG,IAAI;IAI7B,gFAAgF;IAC1E,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAOlC,iFAAiF;IAC3E,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC;IAMrC,uCAAuC;IACvC,IAAI,IAAI,IAAI;IAOZ,OAAO,CAAC,WAAW;IAWnB,wFAAwF;IACxF,OAAO,CAAC,UAAU;IAyBlB,qEAAqE;IACrE,OAAO,CAAC,eAAe;IAWvB,yEAAyE;YAC3D,gBAAgB;IAgB9B,OAAO,CAAC,KAAK;IAOb,OAAO,CAAC,aAAa;CAUtB"}
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TokenManager = void 0;
4
+ const jwt_js_1 = require("../internal/jwt.js");
5
+ const MAX_BACKOFF_MS = 30_000;
6
+ const BASE_BACKOFF_MS = 1_000;
7
+ /**
8
+ * Owns the access token lifecycle: fetches it from the vendor's tokenProvider,
9
+ * decodes it to learn its expiry, and proactively renews before it lapses. On
10
+ * provider failure it retries with exponential backoff. The SDK never mints — it
11
+ * only consumes what the provider returns.
12
+ */
13
+ class TokenManager {
14
+ opts;
15
+ current = null;
16
+ renewTimer = null;
17
+ retryTimer = null;
18
+ stopped = false;
19
+ inflight = null;
20
+ constructor(options) {
21
+ this.opts = options;
22
+ }
23
+ getToken() {
24
+ return this.current;
25
+ }
26
+ /** Acquire the first token and arm renewal. Throws if the first fetch fails. */
27
+ async start() {
28
+ this.stopped = false;
29
+ const state = await this.fetchToken();
30
+ this.scheduleRenewal(state.decoded.exp);
31
+ return state;
32
+ }
33
+ /** Force an immediate renewal (e.g. after a 401). Coalesces concurrent calls. */
34
+ async renewNow() {
35
+ const state = await this.fetchToken();
36
+ this.scheduleRenewal(state.decoded.exp);
37
+ return state;
38
+ }
39
+ /** Stop all timers and clear state. */
40
+ stop() {
41
+ this.stopped = true;
42
+ this.clearTimers();
43
+ this.current = null;
44
+ this.inflight = null;
45
+ }
46
+ clearTimers() {
47
+ if (this.renewTimer) {
48
+ clearTimeout(this.renewTimer);
49
+ this.renewTimer = null;
50
+ }
51
+ if (this.retryTimer) {
52
+ clearTimeout(this.retryTimer);
53
+ this.retryTimer = null;
54
+ }
55
+ }
56
+ /** Fetch a token from the provider, decode + validate it. Coalesces in-flight calls. */
57
+ fetchToken() {
58
+ if (this.inflight) {
59
+ return this.inflight;
60
+ }
61
+ this.inflight = (async () => {
62
+ let raw;
63
+ try {
64
+ raw = await this.opts.tokenProvider({ deviceId: this.opts.deviceId });
65
+ }
66
+ catch (cause) {
67
+ throw this.error('TOKEN_PROVIDER_FAILED', 'tokenProvider threw or rejected', cause);
68
+ }
69
+ const decoded = (0, jwt_js_1.decodeToken)(raw);
70
+ if (!decoded) {
71
+ throw this.error('TOKEN_INVALID', 'tokenProvider returned a malformed token');
72
+ }
73
+ const state = { token: raw, decoded };
74
+ this.current = state;
75
+ this.opts.onToken(state);
76
+ return state;
77
+ })().finally(() => {
78
+ this.inflight = null;
79
+ });
80
+ return this.inflight;
81
+ }
82
+ /** Arm a timer to renew `renewSkewSeconds` before expiry (min 0). */
83
+ scheduleRenewal(expUnixSeconds) {
84
+ this.clearTimers();
85
+ if (this.stopped)
86
+ return;
87
+ const nowMs = Date.now();
88
+ const renewAtMs = (expUnixSeconds - this.opts.renewSkewSeconds) * 1000;
89
+ const delay = Math.max(0, renewAtMs - nowMs);
90
+ this.renewTimer = setTimeout(() => {
91
+ void this.renewWithBackoff(0);
92
+ }, delay);
93
+ }
94
+ /** Renew, retrying with exponential backoff on failure until stopped. */
95
+ async renewWithBackoff(attempt) {
96
+ if (this.stopped)
97
+ return;
98
+ try {
99
+ const state = await this.fetchToken();
100
+ this.scheduleRenewal(state.decoded.exp);
101
+ }
102
+ catch (err) {
103
+ const commzyError = this.asCommzyError(err);
104
+ this.opts.onError(commzyError);
105
+ if (this.stopped)
106
+ return;
107
+ const backoff = Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * 2 ** attempt);
108
+ this.retryTimer = setTimeout(() => {
109
+ void this.renewWithBackoff(attempt + 1);
110
+ }, backoff);
111
+ }
112
+ }
113
+ error(code, message, cause) {
114
+ const err = new Error(message);
115
+ err.code = code;
116
+ if (cause !== undefined)
117
+ err.cause = cause;
118
+ return err;
119
+ }
120
+ asCommzyError(err) {
121
+ if (err && typeof err === 'object' && 'code' in err && 'message' in err) {
122
+ return err;
123
+ }
124
+ return {
125
+ code: 'TOKEN_PROVIDER_FAILED',
126
+ message: err instanceof Error ? err.message : 'Token renewal failed',
127
+ cause: err,
128
+ };
129
+ }
130
+ }
131
+ exports.TokenManager = TokenManager;
132
+ //# sourceMappingURL=TokenManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenManager.js","sourceRoot":"","sources":["../../src/auth/TokenManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import { type RestRequest } from '../transport/RestClient.js';
2
+ import type { CommzyClientConfig, CommzyClientEvents, SessionSnapshot } from '../types.js';
3
+ /**
4
+ * The Commzy mobile SDK entry point.
5
+ *
6
+ * const client = new CommzyClient({ baseUrl, publishableKey, tokenProvider });
7
+ * await client.connect(); // authenticate + open realtime
8
+ * client.on('stateChange', s => ...); // observe session lifecycle
9
+ * const channels = await client.rest({ path: '/v1/channels', query: { orgId } });
10
+ * client.signOut(); // local teardown
11
+ *
12
+ * It is a CLIENT of the existing platform — it consumes tokens minted by the
13
+ * vendor's backend (never mints) and reuses the platform's REST + Socket.io.
14
+ */
15
+ export declare class CommzyClient {
16
+ private readonly config;
17
+ private readonly storage;
18
+ private readonly emitter;
19
+ private tokenManager;
20
+ private realtime;
21
+ private rest;
22
+ private deviceId;
23
+ private snapshot;
24
+ constructor(config: CommzyClientConfig);
25
+ /** Current session snapshot. */
26
+ getState(): SessionSnapshot;
27
+ /** The resolved stable device id (available after connect). */
28
+ getDeviceId(): string | null;
29
+ on<K extends keyof CommzyClientEvents>(event: K, listener: CommzyClientEvents[K]): () => void;
30
+ off<K extends keyof CommzyClientEvents>(event: K, listener: CommzyClientEvents[K]): void;
31
+ /**
32
+ * Authenticate and open the live session: resolve deviceId → fetch a token via
33
+ * the provider → REST authenticated → open the realtime socket. Idempotent-ish:
34
+ * calling connect when already connected re-validates the token.
35
+ */
36
+ connect(): Promise<SessionSnapshot>;
37
+ /** Make an authenticated REST request against the Commzy API. */
38
+ restRequest<T>(req: RestRequest): Promise<T>;
39
+ /**
40
+ * Sign out — LOCAL teardown only. Drops the token, closes the socket, clears
41
+ * state. Per design, the vendor owns the user lifecycle, so this does NOT revoke
42
+ * the session server-side; the session expires on its own.
43
+ */
44
+ signOut(): void;
45
+ /** Tear down everything and remove all listeners. */
46
+ destroy(): void;
47
+ private handleToken;
48
+ private applyToken;
49
+ private handleRealtimeConnect;
50
+ private handleRealtimeDisconnect;
51
+ private handleError;
52
+ private setStatus;
53
+ private validateConfig;
54
+ private toError;
55
+ private makeError;
56
+ }
57
+ //# sourceMappingURL=CommzyClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommzyClient.d.ts","sourceRoot":"","sources":["../../src/client/CommzyClient.ts"],"names":[],"mappings":"AAIA,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EAElB,eAAe,EAGhB,MAAM,aAAa,CAAC;AAIrB;;;;;;;;;;;GAWG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0C;IAElE,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,IAAI,CAA2B;IACvC,OAAO,CAAC,QAAQ,CAAuB;IAEvC,OAAO,CAAC,QAAQ,CAMd;gBAEU,MAAM,EAAE,kBAAkB;IAQtC,gCAAgC;IAChC,QAAQ,IAAI,eAAe;IAI3B,+DAA+D;IAC/D,WAAW,IAAI,MAAM,GAAG,IAAI;IAI5B,EAAE,CAAC,CAAC,SAAS,MAAM,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAI7F,GAAG,CAAC,CAAC,SAAS,MAAM,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,IAAI;IAIxF;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC;IA4CzC,iEAAiE;IAC3D,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;IAOlD;;;;OAIG;IACH,OAAO,IAAI,IAAI;IAgBf,qDAAqD;IACrD,OAAO,IAAI,IAAI;IAOf,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,wBAAwB;IAQhC,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,SAAS;CAGlB"}
@@ -0,0 +1,195 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CommzyClient = void 0;
4
+ const TokenManager_js_1 = require("../auth/TokenManager.js");
5
+ const deviceId_js_1 = require("../device/deviceId.js");
6
+ const emitter_js_1 = require("../internal/emitter.js");
7
+ const RealtimeClient_js_1 = require("../transport/RealtimeClient.js");
8
+ const RestClient_js_1 = require("../transport/RestClient.js");
9
+ const DEFAULT_RENEW_SKEW_SECONDS = 60;
10
+ /**
11
+ * The Commzy mobile SDK entry point.
12
+ *
13
+ * const client = new CommzyClient({ baseUrl, publishableKey, tokenProvider });
14
+ * await client.connect(); // authenticate + open realtime
15
+ * client.on('stateChange', s => ...); // observe session lifecycle
16
+ * const channels = await client.rest({ path: '/v1/channels', query: { orgId } });
17
+ * client.signOut(); // local teardown
18
+ *
19
+ * It is a CLIENT of the existing platform — it consumes tokens minted by the
20
+ * vendor's backend (never mints) and reuses the platform's REST + Socket.io.
21
+ */
22
+ class CommzyClient {
23
+ config;
24
+ storage;
25
+ emitter = new emitter_js_1.TypedEmitter();
26
+ tokenManager = null;
27
+ realtime = null;
28
+ rest = null;
29
+ deviceId = null;
30
+ snapshot = {
31
+ status: 'idle',
32
+ userId: null,
33
+ orgId: null,
34
+ realtimeConnected: false,
35
+ error: null,
36
+ };
37
+ constructor(config) {
38
+ this.validateConfig(config);
39
+ this.config = config;
40
+ this.storage = config.storage ?? new deviceId_js_1.MemoryStorage();
41
+ }
42
+ // ── Public API ────────────────────────────────────────────────────────────
43
+ /** Current session snapshot. */
44
+ getState() {
45
+ return { ...this.snapshot };
46
+ }
47
+ /** The resolved stable device id (available after connect). */
48
+ getDeviceId() {
49
+ return this.deviceId;
50
+ }
51
+ on(event, listener) {
52
+ return this.emitter.on(event, listener);
53
+ }
54
+ off(event, listener) {
55
+ this.emitter.off(event, listener);
56
+ }
57
+ /**
58
+ * Authenticate and open the live session: resolve deviceId → fetch a token via
59
+ * the provider → REST authenticated → open the realtime socket. Idempotent-ish:
60
+ * calling connect when already connected re-validates the token.
61
+ */
62
+ async connect() {
63
+ this.setStatus('connecting');
64
+ try {
65
+ this.deviceId = await (0, deviceId_js_1.resolveDeviceId)(this.storage, this.config.deviceId);
66
+ this.tokenManager = new TokenManager_js_1.TokenManager({
67
+ tokenProvider: this.config.tokenProvider,
68
+ deviceId: this.deviceId,
69
+ renewSkewSeconds: this.config.renewSkewSeconds ?? DEFAULT_RENEW_SKEW_SECONDS,
70
+ onToken: (state) => this.handleToken(state),
71
+ onError: (error) => this.handleError(error),
72
+ });
73
+ const tokenState = await this.tokenManager.start();
74
+ this.rest = new RestClient_js_1.RestClient({
75
+ baseUrl: this.config.baseUrl,
76
+ getToken: () => this.tokenManager?.getToken()?.token ?? null,
77
+ refreshToken: async () => (await this.tokenManager.renewNow()).token,
78
+ });
79
+ // Token is valid → REST is usable now.
80
+ this.applyToken(tokenState);
81
+ this.setStatus('authenticated');
82
+ // Bring up realtime; 'connected' is reached on the socket's connect event.
83
+ this.realtime = new RealtimeClient_js_1.RealtimeClient({
84
+ baseUrl: this.config.baseUrl,
85
+ getToken: () => this.tokenManager?.getToken()?.token ?? null,
86
+ onConnect: () => this.handleRealtimeConnect(),
87
+ onDisconnect: () => this.handleRealtimeDisconnect(),
88
+ onError: (error) => this.emitter.emit('error', error),
89
+ onEvent: (name, payload) => this.emitter.emit('realtimeEvent', { name, payload }),
90
+ });
91
+ this.realtime.connect();
92
+ return this.getState();
93
+ }
94
+ catch (err) {
95
+ const error = this.toError(err);
96
+ this.handleError(error, 'error');
97
+ throw error;
98
+ }
99
+ }
100
+ /** Make an authenticated REST request against the Commzy API. */
101
+ async restRequest(req) {
102
+ if (!this.rest) {
103
+ throw this.makeError('NOT_CONNECTED', 'Call connect() before making requests');
104
+ }
105
+ return this.rest.request(req);
106
+ }
107
+ /**
108
+ * Sign out — LOCAL teardown only. Drops the token, closes the socket, clears
109
+ * state. Per design, the vendor owns the user lifecycle, so this does NOT revoke
110
+ * the session server-side; the session expires on its own.
111
+ */
112
+ signOut() {
113
+ this.tokenManager?.stop();
114
+ this.realtime?.disconnect();
115
+ this.tokenManager = null;
116
+ this.realtime = null;
117
+ this.rest = null;
118
+ this.snapshot = {
119
+ status: 'disconnected',
120
+ userId: null,
121
+ orgId: null,
122
+ realtimeConnected: false,
123
+ error: null,
124
+ };
125
+ this.emitter.emit('stateChange', this.getState());
126
+ }
127
+ /** Tear down everything and remove all listeners. */
128
+ destroy() {
129
+ this.signOut();
130
+ this.emitter.removeAll();
131
+ }
132
+ // ── Internals ───────────────────────────────────────────────────────────────
133
+ handleToken(state) {
134
+ this.applyToken(state);
135
+ // A renewal arriving while live should refresh the socket's handshake token.
136
+ if (this.realtime && this.snapshot.status !== 'connecting') {
137
+ this.realtime.updateAuthAndReconnect();
138
+ }
139
+ }
140
+ applyToken(state) {
141
+ this.snapshot = {
142
+ ...this.snapshot,
143
+ userId: state.decoded.sub,
144
+ orgId: state.decoded.orgId,
145
+ error: null,
146
+ };
147
+ }
148
+ handleRealtimeConnect() {
149
+ this.snapshot = { ...this.snapshot, realtimeConnected: true };
150
+ this.setStatus('connected');
151
+ }
152
+ handleRealtimeDisconnect() {
153
+ this.snapshot = { ...this.snapshot, realtimeConnected: false };
154
+ // Drop back to 'authenticated' (REST still works) while the socket retries.
155
+ if (this.snapshot.status === 'connected') {
156
+ this.setStatus('authenticated');
157
+ }
158
+ }
159
+ handleError(error, status) {
160
+ this.snapshot = { ...this.snapshot, error };
161
+ this.emitter.emit('error', error);
162
+ if (status) {
163
+ this.setStatus(status);
164
+ }
165
+ else {
166
+ // Persistent token failure → session is effectively expired.
167
+ this.setStatus('expired');
168
+ }
169
+ }
170
+ setStatus(status) {
171
+ if (this.snapshot.status === status) {
172
+ this.snapshot = { ...this.snapshot };
173
+ }
174
+ else {
175
+ this.snapshot = { ...this.snapshot, status };
176
+ }
177
+ this.emitter.emit('stateChange', this.getState());
178
+ }
179
+ validateConfig(config) {
180
+ if (!config.baseUrl || !config.publishableKey || typeof config.tokenProvider !== 'function') {
181
+ throw this.makeError('CONFIG_INVALID', 'CommzyClient requires baseUrl, publishableKey, and a tokenProvider');
182
+ }
183
+ }
184
+ toError(err) {
185
+ if (err && typeof err === 'object' && 'code' in err && 'message' in err) {
186
+ return err;
187
+ }
188
+ return this.makeError('TRANSPORT_FAILED', err instanceof Error ? err.message : 'Unknown error', err);
189
+ }
190
+ makeError(code, message, cause) {
191
+ return cause === undefined ? { code, message } : { code, message, cause };
192
+ }
193
+ }
194
+ exports.CommzyClient = CommzyClient;
195
+ //# sourceMappingURL=CommzyClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommzyClient.js","sourceRoot":"","sources":["../../src/client/CommzyClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { StorageAdapter } from '../types.js';
2
+ /** In-memory fallback storage — id is not persisted across app restarts. */
3
+ export declare class MemoryStorage implements StorageAdapter {
4
+ private readonly map;
5
+ getItem(key: string): Promise<string | null>;
6
+ setItem(key: string, value: string): Promise<void>;
7
+ }
8
+ /**
9
+ * Resolve a stable per-install device id. Order of precedence:
10
+ * 1. An explicit override (vendor-supplied).
11
+ * 2. A previously persisted id from storage.
12
+ * 3. A freshly generated id, persisted for next time.
13
+ *
14
+ * The id is the key that lets same-device token re-mints collapse onto one
15
+ * server session (preventing orphan-session accumulation) while different devices
16
+ * keep their own session.
17
+ */
18
+ export declare function resolveDeviceId(storage: StorageAdapter, override?: string): Promise<string>;
19
+ //# sourceMappingURL=deviceId.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deviceId.d.ts","sourceRoot":"","sources":["../../src/device/deviceId.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,4EAA4E;AAC5E,qBAAa,aAAc,YAAW,cAAc;IAClD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;IAEjD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI5C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAInD;AA2BD;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,cAAc,EACvB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAWjB"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryStorage = void 0;
4
+ exports.resolveDeviceId = resolveDeviceId;
5
+ const DEVICE_ID_KEY = 'commzy.sdk.deviceId';
6
+ /** In-memory fallback storage — id is not persisted across app restarts. */
7
+ class MemoryStorage {
8
+ map = new Map();
9
+ getItem(key) {
10
+ return Promise.resolve(this.map.get(key) ?? null);
11
+ }
12
+ setItem(key, value) {
13
+ this.map.set(key, value);
14
+ return Promise.resolve();
15
+ }
16
+ }
17
+ exports.MemoryStorage = MemoryStorage;
18
+ /**
19
+ * RFC4122-ish v4 uuid. Uses crypto.randomUUID / getRandomValues when available
20
+ * (RN, modern browsers, Node), falling back to Math.random only if neither
21
+ * exists — good enough for a per-install identifier.
22
+ */
23
+ function generateId() {
24
+ const cryptoObj = globalThis.crypto;
25
+ if (cryptoObj?.randomUUID) {
26
+ return cryptoObj.randomUUID();
27
+ }
28
+ const bytes = new Uint8Array(16);
29
+ if (cryptoObj?.getRandomValues) {
30
+ cryptoObj.getRandomValues(bytes);
31
+ }
32
+ else {
33
+ for (let i = 0; i < 16; i += 1) {
34
+ bytes[i] = Math.floor(Math.random() * 256);
35
+ }
36
+ }
37
+ // Set version (4) and variant bits.
38
+ bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;
39
+ bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;
40
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
41
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
42
+ }
43
+ /**
44
+ * Resolve a stable per-install device id. Order of precedence:
45
+ * 1. An explicit override (vendor-supplied).
46
+ * 2. A previously persisted id from storage.
47
+ * 3. A freshly generated id, persisted for next time.
48
+ *
49
+ * The id is the key that lets same-device token re-mints collapse onto one
50
+ * server session (preventing orphan-session accumulation) while different devices
51
+ * keep their own session.
52
+ */
53
+ async function resolveDeviceId(storage, override) {
54
+ if (override && override.trim().length > 0) {
55
+ return override.trim();
56
+ }
57
+ const existing = await storage.getItem(DEVICE_ID_KEY);
58
+ if (existing && existing.length > 0) {
59
+ return existing;
60
+ }
61
+ const fresh = generateId();
62
+ await storage.setItem(DEVICE_ID_KEY, fresh);
63
+ return fresh;
64
+ }
65
+ //# sourceMappingURL=deviceId.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deviceId.js","sourceRoot":"","sources":["../../src/device/deviceId.ts"],"names":[],"mappings":";;;AAqDA,0CAcC;AAjED,MAAM,aAAa,GAAG,qBAAqB,CAAC;AAE5C,4EAA4E;AAC5E,MAAa,aAAa;IACP,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEjD,OAAO,CAAC,GAAW;QACjB,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,CAAC,GAAW,EAAE,KAAa;QAChC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACzB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;CACF;AAXD,sCAWC;AAED;;;;GAIG;AACH,SAAS,UAAU;IACjB,MAAM,SAAS,GAAI,UAAkC,CAAC,MAAM,CAAC;IAC7D,IAAI,SAAS,EAAE,UAAU,EAAE,CAAC;QAC1B,OAAO,SAAS,CAAC,UAAU,EAAE,CAAC;IAChC,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACjC,IAAI,SAAS,EAAE,eAAe,EAAE,CAAC;QAC/B,SAAS,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;SAAM,CAAC;QACN,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IACD,oCAAoC;IACpC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3C,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3C,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/E,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;AAC7G,CAAC;AAED;;;;;;;;;GASG;AACI,KAAK,UAAU,eAAe,CACnC,OAAuB,EACvB,QAAiB;IAEjB,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3C,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACtD,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;IAC3B,MAAM,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IAC5C,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { CommzyClient } from './client/CommzyClient.js';
2
+ export { MemoryStorage } from './device/deviceId.js';
3
+ export type { CommzyClientConfig, CommzyClientEvents, CommzyError, SessionSnapshot, SessionStatus, StorageAdapter, TokenProvider, } from './types.js';
4
+ export type { RestRequest } from './transport/RestClient.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,aAAa,EACb,cAAc,EACd,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ // @commzy/sdk-react-native — public API
3
+ //
4
+ // Headless authenticated-session slice. UI components and domain helpers
5
+ // (channels/messages/presence) build on this in later milestones.
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.MemoryStorage = exports.CommzyClient = void 0;
8
+ var CommzyClient_js_1 = require("./client/CommzyClient.js");
9
+ Object.defineProperty(exports, "CommzyClient", { enumerable: true, get: function () { return CommzyClient_js_1.CommzyClient; } });
10
+ var deviceId_js_1 = require("./device/deviceId.js");
11
+ Object.defineProperty(exports, "MemoryStorage", { enumerable: true, get: function () { return deviceId_js_1.MemoryStorage; } });
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,wCAAwC;AACxC,EAAE;AACF,yEAAyE;AACzE,kEAAkE;;;AAElE,4DAAwD;AAA/C,+GAAA,YAAY,OAAA;AACrB,oDAAqD;AAA5C,4GAAA,aAAa,OAAA"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Minimal typed event emitter — no Node `events` dependency, so it runs in React
3
+ * Native, browsers, and Node alike. Listeners for an event share a Set; emit is
4
+ * defensively wrapped so one throwing listener can't break the others.
5
+ */
6
+ export declare class TypedEmitter<TEvents extends Record<string, (...args: never[]) => void>> {
7
+ private readonly listeners;
8
+ on<K extends keyof TEvents>(event: K, listener: TEvents[K]): () => void;
9
+ off<K extends keyof TEvents>(event: K, listener: TEvents[K]): void;
10
+ emit<K extends keyof TEvents>(event: K, ...args: Parameters<TEvents[K]>): void;
11
+ removeAll(): void;
12
+ }
13
+ //# sourceMappingURL=emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/internal/emitter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,YAAY,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC;IAClF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6D;IAEvF,EAAE,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAUvE,GAAG,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAIlE,IAAI,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAY9E,SAAS,IAAI,IAAI;CAGlB"}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TypedEmitter = void 0;
4
+ /**
5
+ * Minimal typed event emitter — no Node `events` dependency, so it runs in React
6
+ * Native, browsers, and Node alike. Listeners for an event share a Set; emit is
7
+ * defensively wrapped so one throwing listener can't break the others.
8
+ */
9
+ class TypedEmitter {
10
+ listeners = new Map();
11
+ on(event, listener) {
12
+ let set = this.listeners.get(event);
13
+ if (!set) {
14
+ set = new Set();
15
+ this.listeners.set(event, set);
16
+ }
17
+ set.add(listener);
18
+ return () => this.off(event, listener);
19
+ }
20
+ off(event, listener) {
21
+ this.listeners.get(event)?.delete(listener);
22
+ }
23
+ emit(event, ...args) {
24
+ const set = this.listeners.get(event);
25
+ if (!set)
26
+ return;
27
+ for (const listener of [...set]) {
28
+ try {
29
+ listener(...args);
30
+ }
31
+ catch {
32
+ // A misbehaving host listener must not break SDK internals.
33
+ }
34
+ }
35
+ }
36
+ removeAll() {
37
+ this.listeners.clear();
38
+ }
39
+ }
40
+ exports.TypedEmitter = TypedEmitter;
41
+ //# sourceMappingURL=emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../src/internal/emitter.ts"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,MAAa,YAAY;IACN,SAAS,GAAG,IAAI,GAAG,EAAkD,CAAC;IAEvF,EAAE,CAA0B,KAAQ,EAAE,QAAoB;QACxD,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACjC,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,QAAsC,CAAC,CAAC;QAChD,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACzC,CAAC;IAED,GAAG,CAA0B,KAAQ,EAAE,QAAoB;QACzD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,QAAsC,CAAC,CAAC;IAC5E,CAAC;IAED,IAAI,CAA0B,KAAQ,EAAE,GAAG,IAA4B;QACrE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;YAChC,IAAI,CAAC;gBACF,QAAmD,CAAC,GAAG,IAAI,CAAC,CAAC;YAChE,CAAC;YAAC,MAAM,CAAC;gBACP,4DAA4D;YAC9D,CAAC;QACH,CAAC;IACH,CAAC;IAED,SAAS;QACP,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;CACF;AAhCD,oCAgCC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Client-side JWT decoding. We do NOT verify the signature here — that is the
3
+ * server's responsibility on every request. The SDK only reads claims to know the
4
+ * user/org and when to renew the token before it expires.
5
+ */
6
+ export interface DecodedToken {
7
+ /** User id. */
8
+ sub: string;
9
+ /** Tenant the token is scoped to (present on SDK-minted tokens). */
10
+ orgId: string | null;
11
+ /** Expiry, unix seconds. */
12
+ exp: number;
13
+ }
14
+ /**
15
+ * Decode a JWT and extract the claims the SDK needs. Returns null if the token is
16
+ * malformed (caller treats that as an invalid token).
17
+ */
18
+ export declare function decodeToken(token: string): DecodedToken | null;
19
+ //# sourceMappingURL=jwt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/internal/jwt.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,YAAY;IAC3B,eAAe;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,oEAAoE;IACpE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4BAA4B;IAC5B,GAAG,EAAE,MAAM,CAAC;CACb;AAsCD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAsB9D"}
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * Client-side JWT decoding. We do NOT verify the signature here — that is the
4
+ * server's responsibility on every request. The SDK only reads claims to know the
5
+ * user/org and when to renew the token before it expires.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.decodeToken = decodeToken;
9
+ /** Decode a base64url string to UTF-8 across RN / browser / Node. */
10
+ function base64UrlDecode(input) {
11
+ const base64 = input.replace(/-/g, '+').replace(/_/g, '/');
12
+ const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=');
13
+ // Node / RN with global Buffer.
14
+ const maybeBuffer = globalThis
15
+ .Buffer;
16
+ if (maybeBuffer) {
17
+ return maybeBuffer.from(padded, 'base64').toString('utf-8');
18
+ }
19
+ // Browser / RN with atob.
20
+ const maybeAtob = globalThis.atob;
21
+ if (maybeAtob) {
22
+ const binary = maybeAtob(padded);
23
+ let result = '';
24
+ for (let i = 0; i < binary.length; i += 1) {
25
+ result += String.fromCharCode(binary.charCodeAt(i));
26
+ }
27
+ // Best-effort UTF-8 reconstruction.
28
+ try {
29
+ return decodeURIComponent(result
30
+ .split('')
31
+ .map((c) => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`)
32
+ .join(''));
33
+ }
34
+ catch {
35
+ return result;
36
+ }
37
+ }
38
+ throw new Error('No base64 decoder available in this environment');
39
+ }
40
+ /**
41
+ * Decode a JWT and extract the claims the SDK needs. Returns null if the token is
42
+ * malformed (caller treats that as an invalid token).
43
+ */
44
+ function decodeToken(token) {
45
+ const parts = token.split('.');
46
+ if (parts.length !== 3 || !parts[1]) {
47
+ return null;
48
+ }
49
+ try {
50
+ const payload = JSON.parse(base64UrlDecode(parts[1]));
51
+ if (typeof payload.sub !== 'string' || typeof payload.exp !== 'number') {
52
+ return null;
53
+ }
54
+ return {
55
+ sub: payload.sub,
56
+ orgId: typeof payload.orgId === 'string' ? payload.orgId : null,
57
+ exp: payload.exp,
58
+ };
59
+ }
60
+ catch {
61
+ return null;
62
+ }
63
+ }
64
+ //# sourceMappingURL=jwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/internal/jwt.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAmDH,kCAsBC;AA9DD,qEAAqE;AACrE,SAAS,eAAe,CAAC,KAAa;IACpC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAEnF,gCAAgC;IAChC,MAAM,WAAW,GAAI,UAA2F;SAC7G,MAAM,CAAC;IACV,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC9D,CAAC;IAED,0BAA0B;IAC1B,MAAM,SAAS,GAAI,UAA+C,CAAC,IAAI,CAAC;IACxE,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,CAAC;QACD,oCAAoC;QACpC,IAAI,CAAC;YACH,OAAO,kBAAkB,CACvB,MAAM;iBACH,KAAK,CAAC,EAAE,CAAC;iBACT,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC/D,IAAI,CAAC,EAAE,CAAC,CACZ,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;AACrE,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,KAAa;IACvC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAInD,CAAC;QACF,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;YACvE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO;YACL,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,KAAK,EAAE,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;YAC/D,GAAG,EAAE,OAAO,CAAC,GAAG;SACjB,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { CommzyError } from '../types.js';
2
+ export interface RealtimeClientOptions {
3
+ baseUrl: string;
4
+ /** Current bearer token for the handshake; read fresh on each (re)connect. */
5
+ getToken: () => string | null;
6
+ onConnect: () => void;
7
+ onDisconnect: (reason: string) => void;
8
+ onError: (error: CommzyError) => void;
9
+ /** Generic pass-through for server events the host app may want. */
10
+ onEvent: (name: string, payload: unknown) => void;
11
+ }
12
+ /**
13
+ * Realtime layer over the existing Commzy Socket.io gateway. The gateway
14
+ * authenticates a token-only handshake (`handshake.auth.token`) and joins the user
15
+ * to their org/channel rooms from OrgMember — so the SDK is purely a client; no
16
+ * server changes. On reconnect we re-read the token so a renewed token is used.
17
+ */
18
+ export declare class RealtimeClient {
19
+ private readonly opts;
20
+ private socket;
21
+ constructor(options: RealtimeClientOptions);
22
+ get connected(): boolean;
23
+ connect(): void;
24
+ /** Apply the latest token and force a reconnect (e.g. right after a renewal). */
25
+ updateAuthAndReconnect(): void;
26
+ emit(event: string, payload: unknown): void;
27
+ disconnect(): void;
28
+ }
29
+ //# sourceMappingURL=RealtimeClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RealtimeClient.d.ts","sourceRoot":"","sources":["../../src/transport/RealtimeClient.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC,oEAAoE;IACpE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACnD;AAED;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;IAC7C,OAAO,CAAC,MAAM,CAAuB;gBAEzB,OAAO,EAAE,qBAAqB;IAI1C,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,OAAO,IAAI,IAAI;IAiCf,iFAAiF;IACjF,sBAAsB,IAAI,IAAI;IAa9B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAI3C,UAAU,IAAI,IAAI;CAOnB"}
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RealtimeClient = void 0;
4
+ const socket_io_client_1 = require("socket.io-client");
5
+ /**
6
+ * Realtime layer over the existing Commzy Socket.io gateway. The gateway
7
+ * authenticates a token-only handshake (`handshake.auth.token`) and joins the user
8
+ * to their org/channel rooms from OrgMember — so the SDK is purely a client; no
9
+ * server changes. On reconnect we re-read the token so a renewed token is used.
10
+ */
11
+ class RealtimeClient {
12
+ opts;
13
+ socket = null;
14
+ constructor(options) {
15
+ this.opts = options;
16
+ }
17
+ get connected() {
18
+ return this.socket?.connected ?? false;
19
+ }
20
+ connect() {
21
+ if (this.socket) {
22
+ this.updateAuthAndReconnect();
23
+ return;
24
+ }
25
+ const token = this.opts.getToken();
26
+ const socket = (0, socket_io_client_1.io)(this.opts.baseUrl.replace(/\/$/, ''), {
27
+ transports: ['websocket'],
28
+ auth: { token: token ?? '' },
29
+ reconnection: true,
30
+ reconnectionAttempts: Infinity,
31
+ reconnectionDelay: 1_000,
32
+ reconnectionDelayMax: 10_000,
33
+ });
34
+ socket.on('connect', () => this.opts.onConnect());
35
+ socket.on('disconnect', (reason) => this.opts.onDisconnect(reason));
36
+ socket.on('connect_error', (err) => this.opts.onError({ code: 'TRANSPORT_FAILED', message: err.message, cause: err }));
37
+ // Refresh the handshake token before each automatic reconnection attempt so a
38
+ // renewed token is always used.
39
+ socket.io.on('reconnect_attempt', () => {
40
+ socket.auth = { token: this.opts.getToken() ?? '' };
41
+ });
42
+ // Forward any server event to the host app via a catch-all listener.
43
+ socket.onAny((name, payload) => this.opts.onEvent(name, payload));
44
+ this.socket = socket;
45
+ }
46
+ /** Apply the latest token and force a reconnect (e.g. right after a renewal). */
47
+ updateAuthAndReconnect() {
48
+ if (!this.socket) {
49
+ this.connect();
50
+ return;
51
+ }
52
+ this.socket.auth = { token: this.opts.getToken() ?? '' };
53
+ if (this.socket.connected) {
54
+ this.socket.disconnect().connect();
55
+ }
56
+ else {
57
+ this.socket.connect();
58
+ }
59
+ }
60
+ emit(event, payload) {
61
+ this.socket?.emit(event, payload);
62
+ }
63
+ disconnect() {
64
+ if (this.socket) {
65
+ this.socket.removeAllListeners();
66
+ this.socket.disconnect();
67
+ this.socket = null;
68
+ }
69
+ }
70
+ }
71
+ exports.RealtimeClient = RealtimeClient;
72
+ //# sourceMappingURL=RealtimeClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RealtimeClient.js","sourceRoot":"","sources":["../../src/transport/RealtimeClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /** Supplies the current bearer token; lets the REST client always use the latest. */
2
+ export type TokenAccessor = () => string | null;
3
+ /** Triggers a token renewal and resolves with the fresh token (used on 401). */
4
+ export type TokenRefresher = () => Promise<string>;
5
+ export interface RestClientOptions {
6
+ baseUrl: string;
7
+ getToken: TokenAccessor;
8
+ refreshToken: TokenRefresher;
9
+ }
10
+ export interface RestRequest {
11
+ method?: 'GET' | 'POST' | 'PATCH' | 'DELETE';
12
+ path: string;
13
+ query?: Record<string, string | number | undefined>;
14
+ body?: unknown;
15
+ }
16
+ /**
17
+ * Thin authenticated REST client over the existing Commzy API. Adds the bearer
18
+ * token, unwraps the platform's `{ success, data }` envelope, and on a 401 makes
19
+ * ONE renewal attempt (via the TokenManager) before retrying — so a token that
20
+ * lapsed between renewals doesn't surface as a hard failure.
21
+ */
22
+ export declare class RestClient {
23
+ private readonly opts;
24
+ constructor(options: RestClientOptions);
25
+ request<T>(req: RestRequest): Promise<T>;
26
+ private send;
27
+ private buildUrl;
28
+ private parse;
29
+ private httpError;
30
+ }
31
+ //# sourceMappingURL=RestClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RestClient.d.ts","sourceRoot":"","sources":["../../src/transport/RestClient.ts"],"names":[],"mappings":"AAEA,qFAAqF;AACrF,MAAM,MAAM,aAAa,GAAG,MAAM,MAAM,GAAG,IAAI,CAAC;AAEhD,gFAAgF;AAChF,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,aAAa,CAAC;IACxB,YAAY,EAAE,cAAc,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;IACpD,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;GAKG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;gBAE7B,OAAO,EAAE,iBAAiB;IAIhC,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;IAW9C,OAAO,CAAC,IAAI;IAaZ,OAAO,CAAC,QAAQ;YAaF,KAAK;IAiBnB,OAAO,CAAC,SAAS;CAOlB"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RestClient = void 0;
4
+ /**
5
+ * Thin authenticated REST client over the existing Commzy API. Adds the bearer
6
+ * token, unwraps the platform's `{ success, data }` envelope, and on a 401 makes
7
+ * ONE renewal attempt (via the TokenManager) before retrying — so a token that
8
+ * lapsed between renewals doesn't surface as a hard failure.
9
+ */
10
+ class RestClient {
11
+ opts;
12
+ constructor(options) {
13
+ this.opts = options;
14
+ }
15
+ async request(req) {
16
+ const res = await this.send(req, this.opts.getToken());
17
+ if (res.status === 401) {
18
+ // Token may have lapsed — renew once and retry.
19
+ const fresh = await this.opts.refreshToken();
20
+ const retry = await this.send(req, fresh);
21
+ return this.parse(retry);
22
+ }
23
+ return this.parse(res);
24
+ }
25
+ send(req, token) {
26
+ const url = this.buildUrl(req.path, req.query);
27
+ const headers = { 'Content-Type': 'application/json' };
28
+ if (token) {
29
+ headers['Authorization'] = `Bearer ${token}`;
30
+ }
31
+ const init = { method: req.method ?? 'GET', headers };
32
+ if (req.body !== undefined) {
33
+ init.body = JSON.stringify(req.body);
34
+ }
35
+ return fetch(url, init);
36
+ }
37
+ buildUrl(path, query) {
38
+ const base = this.opts.baseUrl.replace(/\/$/, '');
39
+ const url = new URL(`${base}${path}`);
40
+ if (query) {
41
+ for (const [key, value] of Object.entries(query)) {
42
+ if (value !== undefined) {
43
+ url.searchParams.set(key, String(value));
44
+ }
45
+ }
46
+ }
47
+ return url.toString();
48
+ }
49
+ async parse(res) {
50
+ let json;
51
+ try {
52
+ json = await res.json();
53
+ }
54
+ catch {
55
+ json = undefined;
56
+ }
57
+ if (!res.ok) {
58
+ throw this.httpError(res.status, json);
59
+ }
60
+ // Unwrap the platform's { success, data } envelope when present.
61
+ if (json && typeof json === 'object' && 'data' in json) {
62
+ return json.data;
63
+ }
64
+ return json;
65
+ }
66
+ httpError(status, body) {
67
+ const message = body && typeof body === 'object' && 'message' in body
68
+ ? String(body.message)
69
+ : `Request failed with status ${status}`;
70
+ return { code: 'TRANSPORT_FAILED', message, cause: { status, body } };
71
+ }
72
+ }
73
+ exports.RestClient = RestClient;
74
+ //# sourceMappingURL=RestClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RestClient.js","sourceRoot":"","sources":["../../src/transport/RestClient.ts"],"names":[],"mappings":";;;AAqBA;;;;;GAKG;AACH,MAAa,UAAU;IACJ,IAAI,CAAoB;IAEzC,YAAY,OAA0B;QACpC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,OAAO,CAAI,GAAgB;QAC/B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACvD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,gDAAgD;YAChD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YAC7C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC1C,OAAO,IAAI,CAAC,KAAK,CAAI,KAAK,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAI,GAAG,CAAC,CAAC;IAC5B,CAAC;IAEO,IAAI,CAAC,GAAgB,EAAE,KAAoB;QACjD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/C,MAAM,OAAO,GAA2B,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;QAC/E,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,KAAK,EAAE,CAAC;QAC/C,CAAC;QACD,MAAM,IAAI,GAAgB,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,KAAK,EAAE,OAAO,EAAE,CAAC;QACnE,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC1B,CAAC;IAEO,QAAQ,CAAC,IAAY,EAAE,KAA4B;QACzD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAClD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;QACtC,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACxB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC3C,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAEO,KAAK,CAAC,KAAK,CAAI,GAAa;QAClC,IAAI,IAAa,CAAC;QAClB,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,GAAG,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACzC,CAAC;QACD,iEAAiE;QACjE,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,IAAK,IAAgC,EAAE,CAAC;YACpF,OAAQ,IAAoB,CAAC,IAAI,CAAC;QACpC,CAAC;QACD,OAAO,IAAS,CAAC;IACnB,CAAC;IAEO,SAAS,CAAC,MAAc,EAAE,IAAa;QAC7C,MAAM,OAAO,GACX,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,SAAS,IAAK,IAAgC;YAChF,CAAC,CAAC,MAAM,CAAE,IAA6B,CAAC,OAAO,CAAC;YAChD,CAAC,CAAC,8BAA8B,MAAM,EAAE,CAAC;QAC7C,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC;IACxE,CAAC;CACF;AApED,gCAoEC"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Public types for @commzy/sdk-react-native.
3
+ *
4
+ * The SDK is a CLIENT of the existing Commzy platform. It never mints tokens
5
+ * (that is the vendor backend's job via sk_); it consumes tokens supplied by a
6
+ * tokenProvider and manages the authenticated session. See
7
+ * docs/architecture/sdk-platform-architecture.md.
8
+ */
9
+ /**
10
+ * Function the SDK calls whenever it needs a fresh access token. The vendor
11
+ * implements this to fetch a token from THEIR backend (which mints it from Commzy
12
+ * with the secret key). Called on connect, before expiry, and on reconnect.
13
+ *
14
+ * `deviceId` is the SDK's stable per-install id — the vendor should forward it to
15
+ * their backend so re-mints from the same device collapse onto one session.
16
+ */
17
+ export type TokenProvider = (params: {
18
+ deviceId: string;
19
+ }) => Promise<string>;
20
+ /**
21
+ * Pluggable key/value storage for the stable device id. RN apps inject an
22
+ * AsyncStorage-backed adapter; tests/Node inject memory or file. If omitted, the
23
+ * SDK falls back to an in-memory store (id is not persisted across restarts).
24
+ */
25
+ export interface StorageAdapter {
26
+ getItem(key: string): Promise<string | null>;
27
+ setItem(key: string, value: string): Promise<void>;
28
+ }
29
+ /** Configuration passed to `new CommzyClient(config)`. */
30
+ export interface CommzyClientConfig {
31
+ /** Commzy API base URL, e.g. https://api.commzy.ai. Used for REST + WebSocket. */
32
+ baseUrl: string;
33
+ /**
34
+ * The app's publishable key (pk_live_…). Identifies the app/environment and
35
+ * will drive white-label branding later. It does NOT authenticate users — the
36
+ * tokenProvider does. Safe to ship in the app binary.
37
+ */
38
+ publishableKey: string;
39
+ /** Supplies fresh access tokens from the vendor's backend. */
40
+ tokenProvider: TokenProvider;
41
+ /** Optional persistent storage for the device id (defaults to in-memory). */
42
+ storage?: StorageAdapter;
43
+ /**
44
+ * Override the stable device id instead of letting the SDK generate one. Use
45
+ * when the vendor already has a stable per-install identifier.
46
+ */
47
+ deviceId?: string;
48
+ /**
49
+ * Seconds before token expiry to trigger a proactive renewal. Default 60.
50
+ */
51
+ renewSkewSeconds?: number;
52
+ }
53
+ /** The session lifecycle states the host app can observe. */
54
+ export type SessionStatus = 'idle' | 'connecting' | 'authenticated' | 'connected' | 'expired' | 'error' | 'disconnected';
55
+ /** A snapshot of the current session, returned by `client.getState()`. */
56
+ export interface SessionSnapshot {
57
+ status: SessionStatus;
58
+ /** The authenticated user id (JWT `sub`), once known. */
59
+ userId: string | null;
60
+ /** The org the session is scoped to (JWT `orgId`), once known. */
61
+ orgId: string | null;
62
+ /** Whether the realtime socket is currently connected. */
63
+ realtimeConnected: boolean;
64
+ /** The last error, if status is 'error'. */
65
+ error: CommzyError | null;
66
+ }
67
+ /** Normalized SDK error surfaced to the host app. */
68
+ export interface CommzyError {
69
+ code: 'TOKEN_PROVIDER_FAILED' | 'TOKEN_INVALID' | 'TRANSPORT_FAILED' | 'NOT_CONNECTED' | 'CONFIG_INVALID';
70
+ message: string;
71
+ /** The underlying cause, if any. */
72
+ cause?: unknown;
73
+ }
74
+ /** Events emitted by the client (event-emitter API). */
75
+ export interface CommzyClientEvents {
76
+ /** Fired whenever the session status changes. */
77
+ stateChange: (snapshot: SessionSnapshot) => void;
78
+ /** Fired on errors (also reflected in state when terminal). */
79
+ error: (error: CommzyError) => void;
80
+ /** Fired for each realtime event received over the socket. */
81
+ realtimeEvent: (event: {
82
+ name: string;
83
+ payload: unknown;
84
+ }) => void;
85
+ /** Index signature so the typed emitter can constrain on this shape. */
86
+ [event: string]: (...args: never[]) => void;
87
+ }
88
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE9E;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD;AAED,0DAA0D;AAC1D,MAAM,WAAW,kBAAkB;IACjC,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,aAAa,EAAE,aAAa,CAAC;IAC7B,6EAA6E;IAC7E,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,6DAA6D;AAC7D,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,YAAY,GACZ,eAAe,GACf,WAAW,GACX,SAAS,GACT,OAAO,GACP,cAAc,CAAC;AAEnB,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,aAAa,CAAC;IACtB,yDAAyD;IACzD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,kEAAkE;IAClE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,0DAA0D;IAC1D,iBAAiB,EAAE,OAAO,CAAC;IAC3B,4CAA4C;IAC5C,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;CAC3B;AAED,qDAAqD;AACrD,MAAM,WAAW,WAAW;IAC1B,IAAI,EACA,uBAAuB,GACvB,eAAe,GACf,kBAAkB,GAClB,eAAe,GACf,gBAAgB,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wDAAwD;AACxD,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,WAAW,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,IAAI,CAAC;IACjD,+DAA+D;IAC/D,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACpC,8DAA8D;IAC9D,aAAa,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IACnE,wEAAwE;IACxE,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC;CAC7C"}
package/dist/types.js ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ /**
3
+ * Public types for @commzy/sdk-react-native.
4
+ *
5
+ * The SDK is a CLIENT of the existing Commzy platform. It never mints tokens
6
+ * (that is the vendor backend's job via sk_); it consumes tokens supplied by a
7
+ * tokenProvider and manages the authenticated session. See
8
+ * docs/architecture/sdk-platform-architecture.md.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@commzy/sdk-react-native",
3
+ "version": "0.1.0",
4
+ "description": "Commzy embeddable mobile SDK for React Native — authenticated session, chat, presence (white-label).",
5
+ "keywords": [
6
+ "commzy",
7
+ "chat",
8
+ "messaging",
9
+ "react-native",
10
+ "sdk",
11
+ "websocket",
12
+ "cpaas"
13
+ ],
14
+ "license": "UNLICENSED",
15
+ "author": "QSS Technosoft",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://gitlab.com/commzy/commzy.git",
19
+ "directory": "packages/sdk-react-native"
20
+ },
21
+ "main": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "import": "./dist/index.js",
26
+ "types": "./dist/index.d.ts"
27
+ }
28
+ },
29
+ "sideEffects": false,
30
+ "files": [
31
+ "dist",
32
+ "README.md"
33
+ ],
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.json",
39
+ "dev": "tsc -p tsconfig.json --watch",
40
+ "lint": "eslint src/",
41
+ "typecheck": "tsc --noEmit",
42
+ "clean": "rm -rf dist *.tsbuildinfo",
43
+ "prepack": "pnpm run clean && pnpm run build"
44
+ },
45
+ "dependencies": {
46
+ "socket.io-client": "^4.8.3"
47
+ },
48
+ "peerDependencies": {
49
+ "react-native": ">=0.76.0"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "react-native": {
53
+ "optional": true
54
+ }
55
+ },
56
+ "devDependencies": {
57
+ "@commzy/config": "workspace:*",
58
+ "@commzy/shared": "workspace:*",
59
+ "typescript": "^5.7.3"
60
+ }
61
+ }