@koolbase/react-native 10.2.0 → 10.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/dist/cjs/index.js +46 -25
- package/dist/esm/index.js +46 -25
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,44 @@ adheres to [Semantic Versioning][semver].
|
|
|
7
7
|
[kac]: https://keepachangelog.com/en/1.1.0/
|
|
8
8
|
[semver]: https://semver.org/
|
|
9
9
|
|
|
10
|
+
## 10.4.0
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`auth.verifyEmail(token)`** — complete email verification with a token
|
|
15
|
+
from a verification link. The endpoint and the Flutter SDK have had this;
|
|
16
|
+
the TypeScript SDKs did not, so an app's verify-email page had nothing to
|
|
17
|
+
call.
|
|
18
|
+
|
|
19
|
+
- **`auth.resendVerificationEmail()`** — re-send the verification email to a
|
|
20
|
+
signed-in but unverified user. Returns `{ alreadyVerified, expiresAt,
|
|
21
|
+
cooldownUntil }`; an already-verified account is a no-op that says so
|
|
22
|
+
rather than an error, which is what a resend button needs when the user
|
|
23
|
+
verified in another tab.
|
|
24
|
+
|
|
25
|
+
The server throttles this, and the refusal now arrives typed:
|
|
26
|
+
`VerificationResendCooldownError` carries `cooldownUntil` so you can show a
|
|
27
|
+
countdown, and `VerificationResendDailyCapError` is separate because the
|
|
28
|
+
remedy differs — wait seconds versus wait until tomorrow.
|
|
29
|
+
|
|
30
|
+
## 10.3.0
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- **Analytics events carried no user unless the app called `identify()`.**
|
|
35
|
+
Nothing errored when it never did, so every event landed anonymous and
|
|
36
|
+
retention, funnels and per-user analysis were quietly worthless — found in
|
|
37
|
+
a real project as 53 events, 8 registered users, and not one event carrying
|
|
38
|
+
a user id. The SDK already knows who is signed in; it now says so. The
|
|
39
|
+
Flutter SDK fixed this in 11.2.0 and the TypeScript SDKs did not, until now.
|
|
40
|
+
|
|
41
|
+
`identify()` still wins for an app with its own identity system, and
|
|
42
|
+
`reset()` releases that override and falls back to the Koolbase session.
|
|
43
|
+
|
|
44
|
+
If you have been calling `identify()` yourself, nothing changes. If you have
|
|
45
|
+
not, your events will start carrying users — which is the point, though it
|
|
46
|
+
means your analytics before and after this version are not comparable.
|
|
47
|
+
|
|
10
48
|
## 10.2.0
|
|
11
49
|
|
|
12
50
|
### Changed
|
package/dist/cjs/index.js
CHANGED
|
@@ -35,6 +35,15 @@ let _flags = null;
|
|
|
35
35
|
let _analytics = null;
|
|
36
36
|
let _messaging = null;
|
|
37
37
|
let _initialized = false;
|
|
38
|
+
// The in-flight initialize, so overlapping callers await the same one.
|
|
39
|
+
//
|
|
40
|
+
// A boolean guard is not enough: the check happens before the first await
|
|
41
|
+
// and the flag is set after the last, so two calls that overlap in that
|
|
42
|
+
// window both pass and both build a whole SDK — two of every client, two
|
|
43
|
+
// analytics flush timers, two sync engines over one queue. React strict
|
|
44
|
+
// mode does exactly this in development, and so does any app that
|
|
45
|
+
// initializes from two components.
|
|
46
|
+
let _initializing = null;
|
|
38
47
|
function ensureInitialized() {
|
|
39
48
|
if (!_initialized) {
|
|
40
49
|
throw new Error('Koolbase not initialized. Call Koolbase.initialize() first.');
|
|
@@ -44,33 +53,45 @@ exports.Koolbase = {
|
|
|
44
53
|
async initialize(config) {
|
|
45
54
|
if (_initialized)
|
|
46
55
|
return;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
56
|
+
if (_initializing)
|
|
57
|
+
return _initializing;
|
|
58
|
+
_initializing = (async () => {
|
|
59
|
+
// The host platform, installed before any subsystem touches storage or
|
|
60
|
+
// the network. Tests leave this unset and run on the in-memory default.
|
|
61
|
+
(0, core_1.setPlatform)(config.platform ?? (0, platform_js_2.reactNativePlatform)());
|
|
62
|
+
_auth = new core_1.KoolbaseAuth(config);
|
|
63
|
+
_db = new core_1.KoolbaseDatabase(config, () => _auth?.currentUser?.id ?? null, () => _auth?.validAccessToken() ?? Promise.resolve(null),
|
|
64
|
+
// A session the server refuses is not a session. Clearing it here means an
|
|
65
|
+
// app catching KoolbaseUnauthenticatedError is already signed out and can
|
|
66
|
+
// route to login, rather than looping on a dead token.
|
|
67
|
+
async () => { await _auth?.clearStoredSession(); });
|
|
68
|
+
_storage = new core_1.KoolbaseStorage(config, () => _auth?.validAccessToken() ?? Promise.resolve(null), async () => { await _auth?.clearStoredSession(); });
|
|
69
|
+
_realtime = new core_1.KoolbaseRealtime(config, () => _auth?.validAccessToken() ?? Promise.resolve(null), () => _auth?.currentUser?.id ?? null);
|
|
70
|
+
_functions = new core_1.KoolbaseFunctions(config, () => _auth?.validAccessToken() ?? Promise.resolve(null), async () => { await _auth?.clearStoredSession(); });
|
|
71
|
+
// One anonymous device id for the whole SDK — bucketing (flags), targeting
|
|
72
|
+
// (code push), and registration keying (messaging) must all agree on it.
|
|
73
|
+
const deviceId = await (0, core_1.getOrCreateDeviceId)();
|
|
74
|
+
_flags = new core_1.KoolbaseFlags(config, deviceId);
|
|
75
|
+
// Initialize analytics
|
|
76
|
+
if (config.analyticsEnabled !== false) {
|
|
77
|
+
_analytics = new core_1.KoolbaseAnalytics(config, () => _auth?.currentUser?.id ?? null);
|
|
78
|
+
await _analytics.init(config.appVersion);
|
|
79
|
+
}
|
|
80
|
+
// Initialize messaging
|
|
81
|
+
if (config.messagingEnabled !== false) {
|
|
82
|
+
_messaging = new core_1.KoolbaseMessaging(config);
|
|
83
|
+
_messaging.setDeviceId(deviceId);
|
|
84
|
+
}
|
|
85
|
+
_initialized = true;
|
|
86
|
+
})();
|
|
87
|
+
try {
|
|
88
|
+
await _initializing;
|
|
67
89
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
90
|
+
finally {
|
|
91
|
+
// Cleared either way: a failed initialize must be retryable rather
|
|
92
|
+
// than leaving every later caller awaiting a rejected promise.
|
|
93
|
+
_initializing = null;
|
|
72
94
|
}
|
|
73
|
-
_initialized = true;
|
|
74
95
|
},
|
|
75
96
|
get auth() {
|
|
76
97
|
ensureInitialized();
|
package/dist/esm/index.js
CHANGED
|
@@ -17,6 +17,15 @@ let _flags = null;
|
|
|
17
17
|
let _analytics = null;
|
|
18
18
|
let _messaging = null;
|
|
19
19
|
let _initialized = false;
|
|
20
|
+
// The in-flight initialize, so overlapping callers await the same one.
|
|
21
|
+
//
|
|
22
|
+
// A boolean guard is not enough: the check happens before the first await
|
|
23
|
+
// and the flag is set after the last, so two calls that overlap in that
|
|
24
|
+
// window both pass and both build a whole SDK — two of every client, two
|
|
25
|
+
// analytics flush timers, two sync engines over one queue. React strict
|
|
26
|
+
// mode does exactly this in development, and so does any app that
|
|
27
|
+
// initializes from two components.
|
|
28
|
+
let _initializing = null;
|
|
20
29
|
function ensureInitialized() {
|
|
21
30
|
if (!_initialized) {
|
|
22
31
|
throw new Error('Koolbase not initialized. Call Koolbase.initialize() first.');
|
|
@@ -26,33 +35,45 @@ export const Koolbase = {
|
|
|
26
35
|
async initialize(config) {
|
|
27
36
|
if (_initialized)
|
|
28
37
|
return;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
38
|
+
if (_initializing)
|
|
39
|
+
return _initializing;
|
|
40
|
+
_initializing = (async () => {
|
|
41
|
+
// The host platform, installed before any subsystem touches storage or
|
|
42
|
+
// the network. Tests leave this unset and run on the in-memory default.
|
|
43
|
+
setPlatform(config.platform ?? reactNativePlatform());
|
|
44
|
+
_auth = new KoolbaseAuth(config);
|
|
45
|
+
_db = new KoolbaseDatabase(config, () => _auth?.currentUser?.id ?? null, () => _auth?.validAccessToken() ?? Promise.resolve(null),
|
|
46
|
+
// A session the server refuses is not a session. Clearing it here means an
|
|
47
|
+
// app catching KoolbaseUnauthenticatedError is already signed out and can
|
|
48
|
+
// route to login, rather than looping on a dead token.
|
|
49
|
+
async () => { await _auth?.clearStoredSession(); });
|
|
50
|
+
_storage = new KoolbaseStorage(config, () => _auth?.validAccessToken() ?? Promise.resolve(null), async () => { await _auth?.clearStoredSession(); });
|
|
51
|
+
_realtime = new KoolbaseRealtime(config, () => _auth?.validAccessToken() ?? Promise.resolve(null), () => _auth?.currentUser?.id ?? null);
|
|
52
|
+
_functions = new KoolbaseFunctions(config, () => _auth?.validAccessToken() ?? Promise.resolve(null), async () => { await _auth?.clearStoredSession(); });
|
|
53
|
+
// One anonymous device id for the whole SDK — bucketing (flags), targeting
|
|
54
|
+
// (code push), and registration keying (messaging) must all agree on it.
|
|
55
|
+
const deviceId = await getOrCreateDeviceId();
|
|
56
|
+
_flags = new KoolbaseFlags(config, deviceId);
|
|
57
|
+
// Initialize analytics
|
|
58
|
+
if (config.analyticsEnabled !== false) {
|
|
59
|
+
_analytics = new KoolbaseAnalytics(config, () => _auth?.currentUser?.id ?? null);
|
|
60
|
+
await _analytics.init(config.appVersion);
|
|
61
|
+
}
|
|
62
|
+
// Initialize messaging
|
|
63
|
+
if (config.messagingEnabled !== false) {
|
|
64
|
+
_messaging = new KoolbaseMessaging(config);
|
|
65
|
+
_messaging.setDeviceId(deviceId);
|
|
66
|
+
}
|
|
67
|
+
_initialized = true;
|
|
68
|
+
})();
|
|
69
|
+
try {
|
|
70
|
+
await _initializing;
|
|
49
71
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
72
|
+
finally {
|
|
73
|
+
// Cleared either way: a failed initialize must be retryable rather
|
|
74
|
+
// than leaving every later caller awaiting a rejected promise.
|
|
75
|
+
_initializing = null;
|
|
54
76
|
}
|
|
55
|
-
_initialized = true;
|
|
56
77
|
},
|
|
57
78
|
get auth() {
|
|
58
79
|
ensureInitialized();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koolbase/react-native",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.4.0",
|
|
4
4
|
"description": "React Native SDK for Koolbase \u2014 auth, database, storage, realtime, feature flags, and functions in one package.",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"types": "./dist/esm/index.d.ts",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"url": "https://github.com/koolbase/koolbase-react-native"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@koolbase/core": "10.
|
|
27
|
+
"@koolbase/core": "10.4.0"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"@react-native-async-storage/async-storage": "^3.0.2",
|