@techfinityedge/koolbase-react-native 1.7.0 → 1.9.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.
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Koolbase React Native SDK version. Sent in the `x-koolbase-sdk-version`
3
+ * header on every authenticated request so the server can route
4
+ * version-conditional logic (deprecation warnings, schema migrations,
5
+ * feature flags). Must match the `version` field in package.json.
6
+ */
7
+ export declare const koolbaseSdkVersion = "1.9.0";
8
+ /**
9
+ * Builds device-identifying headers attached to every Koolbase auth
10
+ * request. Mirrors the Flutter SDK's `DeviceMetadata` for parity. Apps
11
+ * with privacy concerns can swap in a custom storage adapter to avoid
12
+ * persisting the device label.
13
+ *
14
+ * Headers emitted:
15
+ * - User-Agent: koolbase-react-native/<sdk> (<platform> <version>)
16
+ * - x-koolbase-sdk: react-native
17
+ * - x-koolbase-sdk-version: <koolbaseSdkVersion>
18
+ * - x-koolbase-platform: ios | android | web | etc.
19
+ * - x-koolbase-platform-version: numeric SDK level or OS version string
20
+ * - x-koolbase-app-version: from KoolbaseConfig.appVersion or 'unknown'
21
+ * - x-koolbase-device-label: persistent UUID per install
22
+ */
23
+ export declare class DeviceMetadata {
24
+ private cached;
25
+ private ephemeralLabel;
26
+ private readonly appVersion;
27
+ constructor(appVersion?: string);
28
+ /**
29
+ * Build (or return cached) device headers. The first call may perform
30
+ * an async keychain read to look up the persisted device label;
31
+ * subsequent calls return the in-memory cache synchronously via the
32
+ * returned Promise.
33
+ */
34
+ build(): Promise<Record<string, string>>;
35
+ private getOrCreateDeviceLabel;
36
+ }
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DeviceMetadata = exports.koolbaseSdkVersion = void 0;
4
+ const react_native_1 = require("react-native");
5
+ const auth_storage_1 = require("./auth-storage");
6
+ /**
7
+ * Koolbase React Native SDK version. Sent in the `x-koolbase-sdk-version`
8
+ * header on every authenticated request so the server can route
9
+ * version-conditional logic (deprecation warnings, schema migrations,
10
+ * feature flags). Must match the `version` field in package.json.
11
+ */
12
+ exports.koolbaseSdkVersion = '1.9.0';
13
+ /**
14
+ * Generate a UUIDv4-shaped string for use as a stable per-install
15
+ * device label. Not cryptographically secure — this is a label, not a
16
+ * security primitive. Avoids pulling in a crypto-grade UUID dependency.
17
+ */
18
+ function generateDeviceLabel() {
19
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
20
+ const r = (Math.random() * 16) | 0;
21
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
22
+ return v.toString(16);
23
+ });
24
+ }
25
+ /**
26
+ * Builds device-identifying headers attached to every Koolbase auth
27
+ * request. Mirrors the Flutter SDK's `DeviceMetadata` for parity. Apps
28
+ * with privacy concerns can swap in a custom storage adapter to avoid
29
+ * persisting the device label.
30
+ *
31
+ * Headers emitted:
32
+ * - User-Agent: koolbase-react-native/<sdk> (<platform> <version>)
33
+ * - x-koolbase-sdk: react-native
34
+ * - x-koolbase-sdk-version: <koolbaseSdkVersion>
35
+ * - x-koolbase-platform: ios | android | web | etc.
36
+ * - x-koolbase-platform-version: numeric SDK level or OS version string
37
+ * - x-koolbase-app-version: from KoolbaseConfig.appVersion or 'unknown'
38
+ * - x-koolbase-device-label: persistent UUID per install
39
+ */
40
+ class DeviceMetadata {
41
+ constructor(appVersion) {
42
+ this.cached = null;
43
+ this.ephemeralLabel = null;
44
+ this.appVersion = appVersion ?? 'unknown';
45
+ }
46
+ /**
47
+ * Build (or return cached) device headers. The first call may perform
48
+ * an async keychain read to look up the persisted device label;
49
+ * subsequent calls return the in-memory cache synchronously via the
50
+ * returned Promise.
51
+ */
52
+ async build() {
53
+ if (this.cached)
54
+ return this.cached;
55
+ const platform = String(react_native_1.Platform.OS);
56
+ const platformVersion = String(react_native_1.Platform.Version);
57
+ const deviceLabel = await this.getOrCreateDeviceLabel();
58
+ const userAgent = `koolbase-react-native/${exports.koolbaseSdkVersion} (${platform} ${platformVersion})`;
59
+ this.cached = {
60
+ 'User-Agent': userAgent,
61
+ 'x-koolbase-sdk': 'react-native',
62
+ 'x-koolbase-sdk-version': exports.koolbaseSdkVersion,
63
+ 'x-koolbase-platform': platform,
64
+ 'x-koolbase-platform-version': platformVersion,
65
+ 'x-koolbase-app-version': this.appVersion,
66
+ 'x-koolbase-device-label': deviceLabel,
67
+ };
68
+ return this.cached;
69
+ }
70
+ async getOrCreateDeviceLabel() {
71
+ // No keychain available → ephemeral per-session label.
72
+ // (Better than no label — still useful for in-session debugging.)
73
+ if (!(0, auth_storage_1.isKeychainAvailable)()) {
74
+ if (!this.ephemeralLabel) {
75
+ this.ephemeralLabel = generateDeviceLabel();
76
+ }
77
+ return this.ephemeralLabel;
78
+ }
79
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
80
+ const Keychain = require('react-native-keychain');
81
+ const service = 'koolbase_device_label_v1';
82
+ try {
83
+ const existing = await Keychain.getGenericPassword({ service });
84
+ if (existing && existing.password) {
85
+ return existing.password;
86
+ }
87
+ }
88
+ catch {
89
+ // fall through to create
90
+ }
91
+ const newLabel = generateDeviceLabel();
92
+ try {
93
+ await Keychain.setGenericPassword('device', newLabel, { service });
94
+ }
95
+ catch {
96
+ // Persistence failed — return the generated label anyway, but
97
+ // don't cache it as ephemeral since future requests may persist.
98
+ }
99
+ return newLabel;
100
+ }
101
+ }
102
+ exports.DeviceMetadata = DeviceMetadata;
@@ -1,7 +1,8 @@
1
1
  import { KoolbaseConfig, FunctionInvokeResult, DeployOptions, DeployResult } from './types';
2
2
  export declare class KoolbaseFunctions {
3
3
  private config;
4
- constructor(config: KoolbaseConfig);
4
+ private getUserAccessToken?;
5
+ constructor(config: KoolbaseConfig, getUserAccessToken?: () => string | null);
5
6
  deploy(options: DeployOptions): Promise<DeployResult>;
6
7
  invoke(name: string, body?: Record<string, unknown>): Promise<FunctionInvokeResult>;
7
8
  }
package/dist/functions.js CHANGED
@@ -3,8 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KoolbaseFunctions = void 0;
4
4
  const types_1 = require("./types");
5
5
  class KoolbaseFunctions {
6
- constructor(config) {
6
+ constructor(config, getUserAccessToken) {
7
7
  this.config = config;
8
+ this.getUserAccessToken = getUserAccessToken;
8
9
  }
9
10
  // ─── Deploy ────────────────────────────────────────────────────────────────
10
11
  async deploy(options) {
@@ -40,12 +41,17 @@ class KoolbaseFunctions {
40
41
  }
41
42
  // ─── Invoke ────────────────────────────────────────────────────────────────
42
43
  async invoke(name, body) {
44
+ const headers = {
45
+ 'Content-Type': 'application/json',
46
+ 'x-api-key': this.config.publicKey,
47
+ };
48
+ const userToken = this.getUserAccessToken?.();
49
+ if (userToken) {
50
+ headers['Authorization'] = `Bearer ${userToken}`;
51
+ }
43
52
  const res = await fetch(`${this.config.baseUrl}/v1/sdk/functions/${name}`, {
44
53
  method: 'POST',
45
- headers: {
46
- 'Content-Type': 'application/json',
47
- 'x-api-key': this.config.publicKey,
48
- },
54
+ headers,
49
55
  body: JSON.stringify({ body: body ?? {} }),
50
56
  });
51
57
  const data = await res.json().catch(() => null);
package/dist/index.d.ts CHANGED
@@ -36,3 +36,8 @@ export declare const Koolbase: {
36
36
  readonly messaging: KoolbaseMessaging;
37
37
  checkVersion(currentVersion: string): VersionCheckResult;
38
38
  };
39
+ export { koolbaseSdkVersion } from './device-metadata';
40
+ export { RestoreResult } from './types';
41
+ export type { AuthStateListener, FetchLike, KoolbaseAuthStorage } from './types';
42
+ export { SecureAuthStorage } from './auth-storage';
43
+ export { KoolbaseAuthError, InvalidCredentialsError, EmailAlreadyInUseError, UserDisabledError, WeakPasswordError, SessionExpiredError, TokenRevokedError, AccountLockedError, UnlockTokenInvalidError, RateLimitError, NetworkError, InvalidPhoneNumberError, OtpExpiredError, OtpInvalidError, OtpMaxAttemptsError, OtpRateLimitError, PhoneAlreadyLinkedError, SmsConfigMissingError, } from './auth-errors';
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.Koolbase = exports.KoolbaseStorage = exports.KoolbaseRealtime = exports.KoolbaseFunctions = exports.KoolbaseFlags = exports.KoolbaseDatabase = exports.KoolbaseAuth = exports.KoolbaseCodePush = exports.KoolbaseAnalytics = exports.KoolbaseAppleAuth = exports.KoolbaseMessaging = void 0;
20
+ exports.SmsConfigMissingError = exports.PhoneAlreadyLinkedError = exports.OtpRateLimitError = exports.OtpMaxAttemptsError = exports.OtpInvalidError = exports.OtpExpiredError = exports.InvalidPhoneNumberError = exports.NetworkError = exports.RateLimitError = exports.UnlockTokenInvalidError = exports.AccountLockedError = exports.TokenRevokedError = exports.SessionExpiredError = exports.WeakPasswordError = exports.UserDisabledError = exports.EmailAlreadyInUseError = exports.InvalidCredentialsError = exports.KoolbaseAuthError = exports.SecureAuthStorage = exports.RestoreResult = exports.koolbaseSdkVersion = exports.Koolbase = exports.KoolbaseStorage = exports.KoolbaseRealtime = exports.KoolbaseFunctions = exports.KoolbaseFlags = exports.KoolbaseDatabase = exports.KoolbaseAuth = exports.KoolbaseCodePush = exports.KoolbaseAnalytics = exports.KoolbaseAppleAuth = exports.KoolbaseMessaging = void 0;
21
21
  const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
22
22
  const auth_1 = require("./auth");
23
23
  Object.defineProperty(exports, "KoolbaseAuth", { enumerable: true, get: function () { return auth_1.KoolbaseAuth; } });
@@ -69,7 +69,7 @@ exports.Koolbase = {
69
69
  _db = new database_1.KoolbaseDatabase(config, () => _auth?.currentUser?.id ?? null);
70
70
  _storage = new storage_1.KoolbaseStorage(config, () => _auth?.accessToken ?? null);
71
71
  _realtime = new realtime_1.KoolbaseRealtime(config);
72
- _functions = new functions_1.KoolbaseFunctions(config);
72
+ _functions = new functions_1.KoolbaseFunctions(config, () => _auth?.accessToken ?? null);
73
73
  _flags = new flags_1.KoolbaseFlags(config, 'rn-device');
74
74
  _codePush = new code_push_1.KoolbaseCodePush(config, config.codePushChannel ?? 'stable');
75
75
  // Initialize code push — loads cached bundle then checks in background
@@ -164,3 +164,29 @@ exports.Koolbase = {
164
164
  return _flags.checkVersion(currentVersion);
165
165
  },
166
166
  };
167
+ // v1.9.0 additions
168
+ var device_metadata_1 = require("./device-metadata");
169
+ Object.defineProperty(exports, "koolbaseSdkVersion", { enumerable: true, get: function () { return device_metadata_1.koolbaseSdkVersion; } });
170
+ var types_1 = require("./types");
171
+ Object.defineProperty(exports, "RestoreResult", { enumerable: true, get: function () { return types_1.RestoreResult; } });
172
+ var auth_storage_1 = require("./auth-storage");
173
+ Object.defineProperty(exports, "SecureAuthStorage", { enumerable: true, get: function () { return auth_storage_1.SecureAuthStorage; } });
174
+ var auth_errors_1 = require("./auth-errors");
175
+ Object.defineProperty(exports, "KoolbaseAuthError", { enumerable: true, get: function () { return auth_errors_1.KoolbaseAuthError; } });
176
+ Object.defineProperty(exports, "InvalidCredentialsError", { enumerable: true, get: function () { return auth_errors_1.InvalidCredentialsError; } });
177
+ Object.defineProperty(exports, "EmailAlreadyInUseError", { enumerable: true, get: function () { return auth_errors_1.EmailAlreadyInUseError; } });
178
+ Object.defineProperty(exports, "UserDisabledError", { enumerable: true, get: function () { return auth_errors_1.UserDisabledError; } });
179
+ Object.defineProperty(exports, "WeakPasswordError", { enumerable: true, get: function () { return auth_errors_1.WeakPasswordError; } });
180
+ Object.defineProperty(exports, "SessionExpiredError", { enumerable: true, get: function () { return auth_errors_1.SessionExpiredError; } });
181
+ Object.defineProperty(exports, "TokenRevokedError", { enumerable: true, get: function () { return auth_errors_1.TokenRevokedError; } });
182
+ Object.defineProperty(exports, "AccountLockedError", { enumerable: true, get: function () { return auth_errors_1.AccountLockedError; } });
183
+ Object.defineProperty(exports, "UnlockTokenInvalidError", { enumerable: true, get: function () { return auth_errors_1.UnlockTokenInvalidError; } });
184
+ Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return auth_errors_1.RateLimitError; } });
185
+ Object.defineProperty(exports, "NetworkError", { enumerable: true, get: function () { return auth_errors_1.NetworkError; } });
186
+ Object.defineProperty(exports, "InvalidPhoneNumberError", { enumerable: true, get: function () { return auth_errors_1.InvalidPhoneNumberError; } });
187
+ Object.defineProperty(exports, "OtpExpiredError", { enumerable: true, get: function () { return auth_errors_1.OtpExpiredError; } });
188
+ Object.defineProperty(exports, "OtpInvalidError", { enumerable: true, get: function () { return auth_errors_1.OtpInvalidError; } });
189
+ Object.defineProperty(exports, "OtpMaxAttemptsError", { enumerable: true, get: function () { return auth_errors_1.OtpMaxAttemptsError; } });
190
+ Object.defineProperty(exports, "OtpRateLimitError", { enumerable: true, get: function () { return auth_errors_1.OtpRateLimitError; } });
191
+ Object.defineProperty(exports, "PhoneAlreadyLinkedError", { enumerable: true, get: function () { return auth_errors_1.PhoneAlreadyLinkedError; } });
192
+ Object.defineProperty(exports, "SmsConfigMissingError", { enumerable: true, get: function () { return auth_errors_1.SmsConfigMissingError; } });
package/dist/types.d.ts CHANGED
@@ -5,6 +5,24 @@ export interface KoolbaseConfig {
5
5
  analyticsEnabled?: boolean;
6
6
  appVersion?: string;
7
7
  messagingEnabled?: boolean;
8
+ /**
9
+ * Optional custom storage adapter for persisting auth state. If omitted,
10
+ * the SDK uses SecureAuthStorage backed by react-native-keychain (must
11
+ * be installed). For Expo Go or custom secure backends, provide your
12
+ * own KoolbaseAuthStorage implementation.
13
+ */
14
+ authStorage?: KoolbaseAuthStorage;
15
+ /**
16
+ * Per-request timeout in milliseconds for auth endpoints. Default 10000.
17
+ * On timeout, fetch rejects with an AbortError. restoreSession() treats
18
+ * this as Offline (preserves optimistic state).
19
+ */
20
+ authTimeout?: number;
21
+ /**
22
+ * Injectable fetch implementation. Defaults to the global fetch. Useful
23
+ * for testing (mock fetch), corporate proxies, or instrumented HTTP.
24
+ */
25
+ fetch?: FetchLike;
8
26
  }
9
27
  export interface KoolbaseUser {
10
28
  id: string;
@@ -19,8 +37,51 @@ export interface KoolbaseUser {
19
37
  export interface KoolbaseSession {
20
38
  accessToken: string;
21
39
  refreshToken: string;
40
+ /** ISO 8601 timestamp when accessToken expires; from server response. */
41
+ expiresAt: string;
22
42
  user: KoolbaseUser;
23
43
  }
44
+ /**
45
+ * Abstract storage interface for persisting authentication state.
46
+ *
47
+ * The SDK ships with SecureAuthStorage (react-native-keychain) as the
48
+ * default. Apps with custom requirements — Expo Go (where keychain is
49
+ * unavailable), compliant encryption layers, or in-memory test mocks —
50
+ * can implement this interface and inject it via KoolbaseConfig.authStorage.
51
+ */
52
+ export interface KoolbaseAuthStorage {
53
+ saveSession(session: KoolbaseSession): Promise<void>;
54
+ readSession(): Promise<KoolbaseSession | null>;
55
+ clear(): Promise<void>;
56
+ }
57
+ /**
58
+ * Result of KoolbaseAuth.restoreSession(). Apps should branch on this:
59
+ * - NoSession → show login screen
60
+ * - Restored → show authenticated UI
61
+ * - Expired → show login screen with "session expired" message
62
+ * - Offline → show authenticated UI optimistically; API calls will
63
+ * fail until network is reachable
64
+ */
65
+ export declare enum RestoreResult {
66
+ NoSession = "no_session",
67
+ Restored = "restored",
68
+ Expired = "expired",
69
+ Offline = "offline"
70
+ }
71
+ /**
72
+ * Callback invoked when authentication state changes. Receives the
73
+ * current user, or null when signed out. Listeners fire on login,
74
+ * register, refresh, session restoration, logout, setSession, and
75
+ * linkPhone. Errors thrown from a listener are swallowed so one
76
+ * broken listener cannot break propagation to others.
77
+ */
78
+ export type AuthStateListener = (user: KoolbaseUser | null) => void;
79
+ /**
80
+ * Drop-in replacement for the global `fetch` function. Inject via
81
+ * KoolbaseConfig.fetch for testing (mock fetch), proxying, or
82
+ * monitoring. Matches the standard fetch signature.
83
+ */
84
+ export type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
24
85
  export interface RegisterParams {
25
86
  email: string;
26
87
  password: string;
package/dist/types.js CHANGED
@@ -1,6 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FunctionRuntime = void 0;
3
+ exports.FunctionRuntime = exports.RestoreResult = void 0;
4
+ /**
5
+ * Result of KoolbaseAuth.restoreSession(). Apps should branch on this:
6
+ * - NoSession → show login screen
7
+ * - Restored → show authenticated UI
8
+ * - Expired → show login screen with "session expired" message
9
+ * - Offline → show authenticated UI optimistically; API calls will
10
+ * fail until network is reachable
11
+ */
12
+ var RestoreResult;
13
+ (function (RestoreResult) {
14
+ RestoreResult["NoSession"] = "no_session";
15
+ RestoreResult["Restored"] = "restored";
16
+ RestoreResult["Expired"] = "expired";
17
+ RestoreResult["Offline"] = "offline";
18
+ })(RestoreResult || (exports.RestoreResult = RestoreResult = {}));
4
19
  // ─── Functions ─────────────────────────────────────────────────────────────
5
20
  var FunctionRuntime;
6
21
  (function (FunctionRuntime) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@techfinityedge/koolbase-react-native",
3
- "version": "1.7.0",
4
- "description": "React Native SDK for Koolbase auth, database, storage, realtime, feature flags, and functions in one package.",
3
+ "version": "1.9.0",
4
+ "description": "React Native SDK for Koolbase \u2014 auth, database, storage, realtime, feature flags, and functions in one package.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "files": [
@@ -27,17 +27,24 @@
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/jszip": "^3.4.0",
30
+ "react-native-keychain": "^10.0.0",
30
31
  "typescript": "^5.0.0"
31
32
  },
32
33
  "peerDependencies": {
33
- "react-native": ">=0.70.0"
34
+ "react-native": ">=0.70.0",
35
+ "react-native-keychain": ">=8.0.0"
34
36
  },
35
37
  "dependencies": {
36
38
  "@react-native-async-storage/async-storage": "^3.0.2",
37
39
  "@react-native-community/netinfo": "^12.0.1",
38
40
  "jszip": "^3.10.1"
39
41
  },
40
- "publishConfig": {
42
+ "publishConfig": {
41
43
  "access": "public"
44
+ },
45
+ "peerDependenciesMeta": {
46
+ "react-native-keychain": {
47
+ "optional": true
48
+ }
42
49
  }
43
50
  }