@kerne/react 0.1.0 → 0.1.1
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/LICENSE +1 -1
- package/README.md +83 -0
- package/dist/index.cjs +791 -106
- package/dist/index.d.cts +503 -23
- package/dist/index.d.ts +503 -23
- package/dist/index.js +757 -103
- package/package.json +19 -12
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,224 +17,907 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
|
-
// src/index.
|
|
30
|
+
// src/index.ts
|
|
21
31
|
var index_exports = {};
|
|
22
32
|
__export(index_exports, {
|
|
33
|
+
Allows: () => Allows,
|
|
34
|
+
AuthLoading: () => AuthLoading,
|
|
35
|
+
Authenticated: () => Authenticated,
|
|
36
|
+
HasSubscription: () => HasSubscription,
|
|
23
37
|
KerneClient: () => KerneClient,
|
|
38
|
+
KerneContext: () => KerneContext,
|
|
39
|
+
KerneErrorBoundary: () => KerneErrorBoundary,
|
|
24
40
|
KerneProvider: () => KerneProvider,
|
|
25
|
-
|
|
41
|
+
KerneStore: () => KerneStore,
|
|
42
|
+
Protected: () => Protected,
|
|
43
|
+
Unauthenticated: () => Unauthenticated,
|
|
44
|
+
useAllows: () => useAllows,
|
|
26
45
|
useAuth: () => useAuth,
|
|
27
|
-
|
|
28
|
-
|
|
46
|
+
useCheck: () => useCheck,
|
|
47
|
+
useCheckout: () => useCheckout,
|
|
48
|
+
useClient: () => useClient,
|
|
49
|
+
useEntitlements: () => useEntitlements,
|
|
50
|
+
useInvitation: () => useInvitation,
|
|
51
|
+
useKerneError: () => useKerneError,
|
|
52
|
+
usePlans: () => usePlans,
|
|
53
|
+
usePortal: () => usePortal,
|
|
54
|
+
useSubscription: () => useSubscription,
|
|
55
|
+
useUsage: () => useUsage,
|
|
56
|
+
useUser: () => useUser,
|
|
57
|
+
useWaitlist: () => useWaitlist
|
|
29
58
|
});
|
|
30
59
|
module.exports = __toCommonJS(index_exports);
|
|
60
|
+
|
|
61
|
+
// src/provider.tsx
|
|
31
62
|
var import_react = require("react");
|
|
32
63
|
var import_server = require("@kerne/server");
|
|
33
64
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
34
65
|
var DEFAULT_STORAGE_KEY = "kerne_auth";
|
|
66
|
+
var DEFAULT_REFRESH_BUFFER = 60;
|
|
35
67
|
var KerneClient = class {
|
|
36
68
|
kerne;
|
|
37
69
|
storage;
|
|
38
70
|
storageKey;
|
|
39
71
|
onAuthChange;
|
|
40
|
-
|
|
41
|
-
|
|
72
|
+
autoRefresh;
|
|
73
|
+
refreshBuffer;
|
|
74
|
+
refreshTimer = null;
|
|
75
|
+
listeners = /* @__PURE__ */ new Set();
|
|
76
|
+
_user = null;
|
|
77
|
+
_token = null;
|
|
78
|
+
_refreshToken = null;
|
|
79
|
+
_expiresAt = null;
|
|
80
|
+
_isLoading = true;
|
|
81
|
+
// Public config properties
|
|
82
|
+
baseUrl;
|
|
83
|
+
appId;
|
|
42
84
|
constructor(config) {
|
|
85
|
+
this.baseUrl = config.baseUrl ?? "https://api.kerne.io";
|
|
86
|
+
this.appId = config.appId;
|
|
43
87
|
this.storage = config.storage ?? (typeof window !== "undefined" ? localStorage : null);
|
|
44
88
|
this.storageKey = config.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
45
89
|
this.onAuthChange = config.onAuthChange;
|
|
90
|
+
this.autoRefresh = config.autoRefresh ?? true;
|
|
91
|
+
this.refreshBuffer = config.refreshBuffer ?? DEFAULT_REFRESH_BUFFER;
|
|
46
92
|
this.kerne = new import_server.Kerne({
|
|
47
|
-
baseUrl:
|
|
48
|
-
appId:
|
|
49
|
-
timeout: config.timeout
|
|
93
|
+
baseUrl: this.baseUrl,
|
|
94
|
+
appId: this.appId,
|
|
95
|
+
timeout: config.timeout,
|
|
96
|
+
onUnauthorized: () => {
|
|
97
|
+
if (process.env.NODE_ENV === "development") {
|
|
98
|
+
console.warn("[Kerne] 401 received. Auto-logout disabled in dev.");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
this.clearSession();
|
|
102
|
+
}
|
|
50
103
|
});
|
|
51
104
|
this.loadSession();
|
|
52
105
|
}
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// Reactive State (useSyncExternalStore compatible)
|
|
108
|
+
// ============================================================================
|
|
109
|
+
subscribe(listener) {
|
|
110
|
+
this.listeners.add(listener);
|
|
111
|
+
return () => this.listeners.delete(listener);
|
|
112
|
+
}
|
|
113
|
+
notify() {
|
|
114
|
+
this.listeners.forEach((l) => l());
|
|
115
|
+
}
|
|
116
|
+
get user() {
|
|
117
|
+
return this._user;
|
|
118
|
+
}
|
|
119
|
+
get token() {
|
|
120
|
+
return this._token;
|
|
121
|
+
}
|
|
122
|
+
get isAuthenticated() {
|
|
123
|
+
return this._token !== null;
|
|
124
|
+
}
|
|
125
|
+
get isLoading() {
|
|
126
|
+
return this._isLoading;
|
|
127
|
+
}
|
|
128
|
+
// ============================================================================
|
|
129
|
+
// Session Management
|
|
130
|
+
// ============================================================================
|
|
53
131
|
loadSession() {
|
|
54
|
-
if (!this.storage)
|
|
132
|
+
if (!this.storage) {
|
|
133
|
+
this._isLoading = false;
|
|
134
|
+
this.notify();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
55
137
|
try {
|
|
56
138
|
const data = this.storage.getItem(this.storageKey);
|
|
57
139
|
if (data) {
|
|
58
|
-
const { user, token, expires_at } = JSON.parse(data);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
this.
|
|
140
|
+
const { user, token, refresh_token, expires_at } = JSON.parse(data);
|
|
141
|
+
const expiresAt = expires_at ? new Date(expires_at) : null;
|
|
142
|
+
if (!expiresAt || expiresAt > /* @__PURE__ */ new Date()) {
|
|
143
|
+
this._user = user;
|
|
144
|
+
this._token = token;
|
|
145
|
+
this._refreshToken = refresh_token ?? null;
|
|
146
|
+
this._expiresAt = expiresAt;
|
|
62
147
|
this.kerne = this.kerne.withToken(token);
|
|
148
|
+
this.scheduleRefresh();
|
|
63
149
|
} else {
|
|
64
150
|
this.storage.removeItem(this.storageKey);
|
|
65
151
|
}
|
|
66
152
|
}
|
|
67
|
-
} catch {
|
|
68
|
-
|
|
153
|
+
} catch (e) {
|
|
154
|
+
console.error("[Kerne] Failed to load session:", e);
|
|
155
|
+
this.storage?.removeItem(this.storageKey);
|
|
69
156
|
}
|
|
157
|
+
this._isLoading = false;
|
|
158
|
+
this.notify();
|
|
70
159
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
160
|
+
/**
|
|
161
|
+
* `AuthResponse.user` is the minimal `{id, email, role}` claim set the
|
|
162
|
+
* token endpoint returns, not the full profile (`User`) - so this always
|
|
163
|
+
* follows up with `users.me()` rather than assigning it directly (that
|
|
164
|
+
* used to leave `_user` looking like a `User` while actually missing
|
|
165
|
+
* `email_verified`/`first_name`/`avatar`/etc).
|
|
166
|
+
*/
|
|
167
|
+
async saveSession(response) {
|
|
168
|
+
this._token = response.token;
|
|
169
|
+
this._refreshToken = response.refresh_token;
|
|
170
|
+
this._expiresAt = response.expires_at ? new Date(response.expires_at) : null;
|
|
74
171
|
this.kerne = this.kerne.withToken(response.token);
|
|
172
|
+
this._user = await this.kerne.users.me();
|
|
75
173
|
if (this.storage) {
|
|
76
|
-
this.storage.setItem(
|
|
174
|
+
this.storage.setItem(
|
|
175
|
+
this.storageKey,
|
|
176
|
+
JSON.stringify({
|
|
177
|
+
user: this._user,
|
|
178
|
+
token: response.token,
|
|
179
|
+
refresh_token: response.refresh_token,
|
|
180
|
+
expires_at: response.expires_at
|
|
181
|
+
})
|
|
182
|
+
);
|
|
77
183
|
}
|
|
78
|
-
this.
|
|
184
|
+
this.scheduleRefresh();
|
|
185
|
+
this.onAuthChange?.(this._user);
|
|
186
|
+
this.notify();
|
|
79
187
|
}
|
|
80
188
|
clearSession() {
|
|
81
|
-
this.
|
|
82
|
-
this.
|
|
189
|
+
this._user = null;
|
|
190
|
+
this._token = null;
|
|
191
|
+
this._refreshToken = null;
|
|
192
|
+
this._expiresAt = null;
|
|
193
|
+
this.cancelRefresh();
|
|
83
194
|
if (this.storage) {
|
|
84
195
|
this.storage.removeItem(this.storageKey);
|
|
85
196
|
}
|
|
86
197
|
this.onAuthChange?.(null);
|
|
198
|
+
this.notify();
|
|
87
199
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// Auto-Refresh
|
|
202
|
+
// ============================================================================
|
|
203
|
+
scheduleRefresh() {
|
|
204
|
+
if (!this.autoRefresh || !this._expiresAt) return;
|
|
205
|
+
this.cancelRefresh();
|
|
206
|
+
const now = Date.now();
|
|
207
|
+
const expiry = this._expiresAt.getTime();
|
|
208
|
+
const refreshAt = expiry - this.refreshBuffer * 1e3;
|
|
209
|
+
const delay = Math.max(0, refreshAt - now);
|
|
210
|
+
if (delay > 0) {
|
|
211
|
+
this.refreshTimer = setTimeout(() => this.silentRefresh(), delay);
|
|
212
|
+
}
|
|
96
213
|
}
|
|
97
|
-
|
|
98
|
-
|
|
214
|
+
cancelRefresh() {
|
|
215
|
+
if (this.refreshTimer) {
|
|
216
|
+
clearTimeout(this.refreshTimer);
|
|
217
|
+
this.refreshTimer = null;
|
|
218
|
+
}
|
|
99
219
|
}
|
|
100
|
-
|
|
101
|
-
|
|
220
|
+
async silentRefresh() {
|
|
221
|
+
if (!this._refreshToken) return;
|
|
222
|
+
try {
|
|
223
|
+
const response = await this.kerne.auth.refreshToken({ refresh_token: this._refreshToken });
|
|
224
|
+
await this.saveSession(response);
|
|
225
|
+
console.debug("[Kerne] Session refreshed silently");
|
|
226
|
+
} catch (e) {
|
|
227
|
+
console.warn("[Kerne] Silent refresh failed:", e);
|
|
228
|
+
}
|
|
102
229
|
}
|
|
230
|
+
// ============================================================================
|
|
231
|
+
// Auth Methods
|
|
232
|
+
// ============================================================================
|
|
103
233
|
async register(params) {
|
|
104
234
|
const [firstName, ...lastNameParts] = (params.name || "").split(" ");
|
|
105
|
-
const lastName = lastNameParts.join(" ");
|
|
106
235
|
const response = await this.kerne.auth.signup({
|
|
107
236
|
email: params.email,
|
|
108
237
|
password: params.password,
|
|
109
|
-
first_name: firstName,
|
|
110
|
-
last_name:
|
|
238
|
+
first_name: firstName || void 0,
|
|
239
|
+
last_name: lastNameParts.join(" ") || void 0,
|
|
240
|
+
invitationToken: params.invitationToken,
|
|
241
|
+
invitationCode: params.invitationCode
|
|
111
242
|
});
|
|
112
|
-
this.saveSession(response);
|
|
243
|
+
await this.saveSession(response);
|
|
113
244
|
return response;
|
|
114
245
|
}
|
|
115
246
|
async login(params) {
|
|
116
|
-
const response = await this.kerne.auth.login(
|
|
117
|
-
|
|
118
|
-
password: params.password
|
|
119
|
-
});
|
|
120
|
-
this.saveSession(response);
|
|
247
|
+
const response = await this.kerne.auth.login(params);
|
|
248
|
+
await this.saveSession(response);
|
|
121
249
|
return response;
|
|
122
250
|
}
|
|
251
|
+
/** Manually trigger a token refresh - normally handled automatically by `autoRefresh`. */
|
|
123
252
|
async refreshToken() {
|
|
124
|
-
if (!this.
|
|
125
|
-
const response = await this.kerne.auth.refreshToken({
|
|
126
|
-
|
|
127
|
-
});
|
|
128
|
-
this.saveSession(response);
|
|
253
|
+
if (!this._refreshToken) return null;
|
|
254
|
+
const response = await this.kerne.auth.refreshToken({ refresh_token: this._refreshToken });
|
|
255
|
+
await this.saveSession(response);
|
|
129
256
|
return response;
|
|
130
257
|
}
|
|
131
258
|
logout() {
|
|
132
259
|
this.clearSession();
|
|
133
260
|
}
|
|
134
261
|
async refreshUser() {
|
|
135
|
-
if (!this.
|
|
262
|
+
if (!this._token) return null;
|
|
136
263
|
try {
|
|
137
264
|
const user = await this.kerne.users.me();
|
|
138
|
-
this.
|
|
265
|
+
this._user = user;
|
|
139
266
|
this.onAuthChange?.(user);
|
|
267
|
+
this.notify();
|
|
140
268
|
return user;
|
|
141
269
|
} catch {
|
|
142
270
|
this.clearSession();
|
|
143
271
|
return null;
|
|
144
272
|
}
|
|
145
273
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
274
|
+
// No `avatar` here - UpdateUserDto doesn't accept it server-side
|
|
275
|
+
// (`forbidNonWhitelisted` rejects the field with a 400).
|
|
276
|
+
async updateProfile(params) {
|
|
277
|
+
if (!this._user) throw new Error("User not authenticated");
|
|
278
|
+
const updated = await this.kerne.users.update(this._user.id, params);
|
|
279
|
+
this._user = updated;
|
|
280
|
+
if (this.storage && this._token) {
|
|
281
|
+
try {
|
|
282
|
+
const data = this.storage.getItem(this.storageKey);
|
|
283
|
+
if (data) {
|
|
284
|
+
const session = JSON.parse(data);
|
|
285
|
+
session.user = updated;
|
|
286
|
+
this.storage.setItem(this.storageKey, JSON.stringify(session));
|
|
287
|
+
}
|
|
288
|
+
} catch (e) {
|
|
289
|
+
}
|
|
152
290
|
}
|
|
291
|
+
this.onAuthChange?.(updated);
|
|
292
|
+
this.notify();
|
|
293
|
+
return updated;
|
|
153
294
|
}
|
|
154
|
-
async
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
295
|
+
async updatePassword(params) {
|
|
296
|
+
await this.kerne.auth.changePassword(params);
|
|
297
|
+
}
|
|
298
|
+
/** Forgot-password flow (logged out) - sends the reset email. */
|
|
299
|
+
async requestPasswordReset(email) {
|
|
300
|
+
await this.kerne.auth.requestPasswordReset(email);
|
|
301
|
+
}
|
|
302
|
+
/** Confirms a password reset with the token from the email. */
|
|
303
|
+
async confirmPasswordReset(token, password) {
|
|
304
|
+
await this.kerne.auth.confirmPasswordReset(token, password);
|
|
305
|
+
}
|
|
306
|
+
// ============================================================================
|
|
307
|
+
// Billing Methods
|
|
308
|
+
// ============================================================================
|
|
309
|
+
/**
|
|
310
|
+
* Errors are not swallowed here (unlike the old implementation, which
|
|
311
|
+
* caught everything and returned `false`) - a 401/500 must not be
|
|
312
|
+
* indistinguishable from a real denial, that's exactly what let the
|
|
313
|
+
* `has_access`/`allowed` mismatch below ship unnoticed. Callers that want
|
|
314
|
+
* a fail-closed boolean regardless of the reason (e.g. `<Allows>`)
|
|
315
|
+
* catch around this themselves.
|
|
316
|
+
*/
|
|
317
|
+
async allows(featureKey, requested) {
|
|
318
|
+
const check = await this.kerne.check(featureKey, { requested });
|
|
319
|
+
return check.allowed;
|
|
320
|
+
}
|
|
321
|
+
/** Full entitlement detail (limit/used/remaining/overage) - use `allows()` for a plain boolean. */
|
|
322
|
+
async check(featureKey, requested) {
|
|
323
|
+
return this.kerne.check(featureKey, { requested });
|
|
324
|
+
}
|
|
325
|
+
async createCheckout(planPriceId, options) {
|
|
326
|
+
const { url } = await this.kerne.billing.checkout(planPriceId, options);
|
|
159
327
|
return url;
|
|
160
328
|
}
|
|
161
|
-
async openCheckout(
|
|
162
|
-
const url = await this.createCheckout(
|
|
163
|
-
if (typeof window !== "undefined")
|
|
164
|
-
window.location.href = url;
|
|
165
|
-
}
|
|
329
|
+
async openCheckout(planPriceId, options) {
|
|
330
|
+
const url = await this.createCheckout(planPriceId, options);
|
|
331
|
+
if (typeof window !== "undefined") window.location.href = url;
|
|
166
332
|
}
|
|
167
333
|
async createPortal(returnUrl) {
|
|
168
|
-
const { url } = await this.kerne.billing.
|
|
334
|
+
const { url } = await this.kerne.billing.portal({ returnUrl });
|
|
169
335
|
return url;
|
|
170
336
|
}
|
|
171
337
|
async openPortal(returnUrl) {
|
|
172
338
|
const url = await this.createPortal(returnUrl);
|
|
173
|
-
if (typeof window !== "undefined")
|
|
174
|
-
|
|
175
|
-
|
|
339
|
+
if (typeof window !== "undefined") window.location.href = url;
|
|
340
|
+
}
|
|
341
|
+
async getSubscription(productSlug) {
|
|
342
|
+
const subs = await this.kerne.billing.subscriptions.list({
|
|
343
|
+
productSlug,
|
|
344
|
+
scopeType: "USER"
|
|
345
|
+
});
|
|
346
|
+
const active = subs.find(
|
|
347
|
+
(s) => s.status === "ACTIVE" || s.status === "TRIALING" || s.status === "PAST_DUE"
|
|
348
|
+
);
|
|
349
|
+
return active ?? null;
|
|
350
|
+
}
|
|
351
|
+
/** Public pricing data - omit `productIdOrSlug` for the tenant's default product. */
|
|
352
|
+
async getPlans(productIdOrSlug) {
|
|
353
|
+
return this.kerne.billing.plans(productIdOrSlug);
|
|
354
|
+
}
|
|
355
|
+
/** Every entitlement for the current user in one call - for a "your plan" screen. */
|
|
356
|
+
async getEntitlements() {
|
|
357
|
+
return this.kerne.entitlements();
|
|
358
|
+
}
|
|
359
|
+
/** Usage for the current user - all QUOTA features, or one via `featureKey`. */
|
|
360
|
+
async getUsage(featureKey) {
|
|
361
|
+
return this.kerne.usage({ featureKey });
|
|
362
|
+
}
|
|
363
|
+
// ============================================================================
|
|
364
|
+
// Growth Methods (waitlist, invitations)
|
|
365
|
+
// ============================================================================
|
|
366
|
+
async joinWaitlist(params) {
|
|
367
|
+
return this.kerne.waitlist.join(params);
|
|
368
|
+
}
|
|
369
|
+
/** Validate an invitation token before showing the registration form. */
|
|
370
|
+
async validateInvitationToken(token) {
|
|
371
|
+
return this.kerne.invitations.validateToken(token);
|
|
372
|
+
}
|
|
373
|
+
/** Validate an invitation code before showing the registration form. */
|
|
374
|
+
async validateInvitationCode(code) {
|
|
375
|
+
return this.kerne.invitations.validateCode(code);
|
|
376
|
+
}
|
|
377
|
+
// ============================================================================
|
|
378
|
+
// Email Verification Methods
|
|
379
|
+
// ============================================================================
|
|
380
|
+
async sendVerificationEmail(verificationType = "code") {
|
|
381
|
+
await this.kerne.auth.sendVerificationEmail(verificationType);
|
|
382
|
+
}
|
|
383
|
+
async verifyEmailWithCode(code) {
|
|
384
|
+
return await this.kerne.auth.verifyEmailWithCode(code);
|
|
385
|
+
}
|
|
386
|
+
async verifyEmailWithLink(token) {
|
|
387
|
+
return await this.kerne.auth.verifyEmailWithLink(token);
|
|
388
|
+
}
|
|
389
|
+
async getVerificationStatus() {
|
|
390
|
+
return await this.kerne.auth.getVerificationStatus();
|
|
176
391
|
}
|
|
177
392
|
};
|
|
178
|
-
function createKerneClient(config) {
|
|
179
|
-
return new KerneClient(config);
|
|
180
|
-
}
|
|
181
393
|
var KerneContext = (0, import_react.createContext)(null);
|
|
182
394
|
function KerneProvider({ children, ...config }) {
|
|
183
|
-
const
|
|
395
|
+
const clientRef = (0, import_react.useRef)(null);
|
|
184
396
|
const client = (0, import_react.useMemo)(() => {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
config.onAuthChange?.(u);
|
|
190
|
-
}
|
|
191
|
-
});
|
|
397
|
+
if (!clientRef.current) {
|
|
398
|
+
clientRef.current = new KerneClient(config);
|
|
399
|
+
}
|
|
400
|
+
return clientRef.current;
|
|
192
401
|
}, [config.appId, config.baseUrl]);
|
|
193
402
|
(0, import_react.useEffect)(() => {
|
|
194
|
-
|
|
403
|
+
return () => {
|
|
404
|
+
client.cancelRefresh?.();
|
|
405
|
+
};
|
|
195
406
|
}, [client]);
|
|
196
407
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(KerneContext.Provider, { value: client, children });
|
|
197
408
|
}
|
|
198
|
-
|
|
199
|
-
|
|
409
|
+
|
|
410
|
+
// src/hooks.ts
|
|
411
|
+
var import_react2 = require("react");
|
|
412
|
+
function useClient() {
|
|
413
|
+
const context = (0, import_react2.useContext)(KerneContext);
|
|
200
414
|
if (!context) {
|
|
201
|
-
throw new Error("
|
|
415
|
+
throw new Error("useClient must be used within a KerneProvider");
|
|
202
416
|
}
|
|
203
417
|
return context;
|
|
204
418
|
}
|
|
205
419
|
function useAuth() {
|
|
206
|
-
const client =
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
420
|
+
const client = useClient();
|
|
421
|
+
const user = (0, import_react2.useSyncExternalStore)(
|
|
422
|
+
(callback) => client.subscribe(callback),
|
|
423
|
+
() => client.user,
|
|
424
|
+
() => client.user
|
|
425
|
+
// Server snapshot
|
|
426
|
+
);
|
|
427
|
+
const token = (0, import_react2.useSyncExternalStore)(
|
|
428
|
+
(callback) => client.subscribe(callback),
|
|
429
|
+
() => client.token,
|
|
430
|
+
() => null
|
|
431
|
+
// Server always null
|
|
432
|
+
);
|
|
433
|
+
const isAuthenticated = (0, import_react2.useSyncExternalStore)(
|
|
434
|
+
(callback) => client.subscribe(callback),
|
|
435
|
+
() => client.isAuthenticated,
|
|
436
|
+
() => false
|
|
437
|
+
// Server always unauthenticated
|
|
438
|
+
);
|
|
439
|
+
const isLoading = (0, import_react2.useSyncExternalStore)(
|
|
440
|
+
(callback) => client.subscribe(callback),
|
|
441
|
+
() => client.isLoading,
|
|
442
|
+
() => true
|
|
443
|
+
// Server always loading
|
|
444
|
+
);
|
|
211
445
|
return {
|
|
212
446
|
user,
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
447
|
+
token,
|
|
448
|
+
isAuthenticated,
|
|
449
|
+
isLoading,
|
|
450
|
+
login: (0, import_react2.useCallback)(
|
|
451
|
+
(params) => client.login(params),
|
|
452
|
+
[client]
|
|
453
|
+
),
|
|
454
|
+
register: (0, import_react2.useCallback)(
|
|
455
|
+
(params) => client.register(params),
|
|
456
|
+
[client]
|
|
457
|
+
),
|
|
458
|
+
logout: (0, import_react2.useCallback)(() => client.logout(), [client]),
|
|
459
|
+
refreshUser: (0, import_react2.useCallback)(() => client.refreshUser(), [client]),
|
|
460
|
+
refreshToken: (0, import_react2.useCallback)(() => client.refreshToken(), [client]),
|
|
461
|
+
updateProfile: (0, import_react2.useCallback)(
|
|
462
|
+
(params) => client.updateProfile(params),
|
|
463
|
+
[client]
|
|
464
|
+
),
|
|
465
|
+
updatePassword: (0, import_react2.useCallback)(
|
|
466
|
+
(params) => client.updatePassword(params),
|
|
467
|
+
[client]
|
|
468
|
+
),
|
|
469
|
+
requestPasswordReset: (0, import_react2.useCallback)(
|
|
470
|
+
(email) => client.requestPasswordReset(email),
|
|
471
|
+
[client]
|
|
472
|
+
),
|
|
473
|
+
confirmPasswordReset: (0, import_react2.useCallback)(
|
|
474
|
+
(token2, password) => client.confirmPasswordReset(token2, password),
|
|
475
|
+
[client]
|
|
476
|
+
),
|
|
477
|
+
// Email verification
|
|
478
|
+
sendVerificationEmail: (0, import_react2.useCallback)(
|
|
479
|
+
(verificationType = "code") => client.sendVerificationEmail(verificationType),
|
|
480
|
+
[client]
|
|
481
|
+
),
|
|
482
|
+
verifyEmailWithCode: (0, import_react2.useCallback)((code) => client.verifyEmailWithCode(code), [client]),
|
|
483
|
+
verifyEmailWithLink: (0, import_react2.useCallback)(
|
|
484
|
+
(token2) => client.verifyEmailWithLink(token2),
|
|
485
|
+
[client]
|
|
486
|
+
),
|
|
487
|
+
getVerificationStatus: (0, import_react2.useCallback)(() => client.getVerificationStatus(), [client])
|
|
218
488
|
};
|
|
219
489
|
}
|
|
220
|
-
function
|
|
221
|
-
const client =
|
|
490
|
+
function useCheckout() {
|
|
491
|
+
const client = useClient();
|
|
222
492
|
return {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
493
|
+
createCheckout: (0, import_react2.useCallback)(
|
|
494
|
+
(planPriceId, options) => client.createCheckout(planPriceId, options),
|
|
495
|
+
[client]
|
|
496
|
+
),
|
|
497
|
+
openCheckout: (0, import_react2.useCallback)(
|
|
498
|
+
(planPriceId, options) => client.openCheckout(planPriceId, options),
|
|
499
|
+
[client]
|
|
500
|
+
)
|
|
228
501
|
};
|
|
229
502
|
}
|
|
503
|
+
function usePortal() {
|
|
504
|
+
const client = useClient();
|
|
505
|
+
return {
|
|
506
|
+
createPortal: (0, import_react2.useCallback)((returnUrl) => client.createPortal(returnUrl), [client]),
|
|
507
|
+
openPortal: (0, import_react2.useCallback)((returnUrl) => client.openPortal(returnUrl), [client])
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
function useAllows(featureKey, requested) {
|
|
511
|
+
const client = useClient();
|
|
512
|
+
const [state, setState] = (0, import_react2.useState)({
|
|
513
|
+
allowed: false,
|
|
514
|
+
isLoading: true,
|
|
515
|
+
error: null
|
|
516
|
+
});
|
|
517
|
+
(0, import_react2.useEffect)(() => {
|
|
518
|
+
let mounted = true;
|
|
519
|
+
client.allows(featureKey, requested).then((allowed) => {
|
|
520
|
+
if (mounted) {
|
|
521
|
+
setState({ allowed, isLoading: false, error: null });
|
|
522
|
+
}
|
|
523
|
+
}).catch((error) => {
|
|
524
|
+
if (mounted) {
|
|
525
|
+
setState({ allowed: false, isLoading: false, error });
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
return () => {
|
|
529
|
+
mounted = false;
|
|
530
|
+
};
|
|
531
|
+
}, [client, featureKey, requested]);
|
|
532
|
+
return state;
|
|
533
|
+
}
|
|
534
|
+
function useCheck(featureKey, requested) {
|
|
535
|
+
const client = useClient();
|
|
536
|
+
const [state, setState] = (0, import_react2.useState)({
|
|
537
|
+
check: null,
|
|
538
|
+
isLoading: true,
|
|
539
|
+
error: null
|
|
540
|
+
});
|
|
541
|
+
(0, import_react2.useEffect)(() => {
|
|
542
|
+
let mounted = true;
|
|
543
|
+
client.check(featureKey, requested).then((check) => {
|
|
544
|
+
if (mounted) {
|
|
545
|
+
setState({ check, isLoading: false, error: null });
|
|
546
|
+
}
|
|
547
|
+
}).catch((error) => {
|
|
548
|
+
if (mounted) {
|
|
549
|
+
setState({ check: null, isLoading: false, error });
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
return () => {
|
|
553
|
+
mounted = false;
|
|
554
|
+
};
|
|
555
|
+
}, [client, featureKey, requested]);
|
|
556
|
+
return state;
|
|
557
|
+
}
|
|
558
|
+
function useSubscription(productSlug) {
|
|
559
|
+
const client = useClient();
|
|
560
|
+
const [state, setState] = (0, import_react2.useState)({
|
|
561
|
+
subscription: null,
|
|
562
|
+
isLoading: true,
|
|
563
|
+
error: null
|
|
564
|
+
});
|
|
565
|
+
(0, import_react2.useEffect)(() => {
|
|
566
|
+
let mounted = true;
|
|
567
|
+
client.getSubscription(productSlug).then((subscription) => {
|
|
568
|
+
if (mounted) {
|
|
569
|
+
setState({ subscription, isLoading: false, error: null });
|
|
570
|
+
}
|
|
571
|
+
}).catch((error) => {
|
|
572
|
+
if (mounted) {
|
|
573
|
+
setState({ subscription: null, isLoading: false, error });
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
return () => {
|
|
577
|
+
mounted = false;
|
|
578
|
+
};
|
|
579
|
+
}, [client, productSlug]);
|
|
580
|
+
const isActive = state.subscription?.status === "ACTIVE" || state.subscription?.status === "TRIALING";
|
|
581
|
+
const planSlug = state.subscription?.plan?.slug;
|
|
582
|
+
return {
|
|
583
|
+
...state,
|
|
584
|
+
isActive,
|
|
585
|
+
planSlug
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function usePlans(productIdOrSlug) {
|
|
589
|
+
const client = useClient();
|
|
590
|
+
const [state, setState] = (0, import_react2.useState)({
|
|
591
|
+
plans: [],
|
|
592
|
+
isLoading: true,
|
|
593
|
+
error: null
|
|
594
|
+
});
|
|
595
|
+
(0, import_react2.useEffect)(() => {
|
|
596
|
+
let mounted = true;
|
|
597
|
+
client.getPlans(productIdOrSlug).then((plans) => {
|
|
598
|
+
if (mounted) {
|
|
599
|
+
setState({ plans, isLoading: false, error: null });
|
|
600
|
+
}
|
|
601
|
+
}).catch((error) => {
|
|
602
|
+
if (mounted) {
|
|
603
|
+
setState({ plans: [], isLoading: false, error });
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
return () => {
|
|
607
|
+
mounted = false;
|
|
608
|
+
};
|
|
609
|
+
}, [client, productIdOrSlug]);
|
|
610
|
+
return state;
|
|
611
|
+
}
|
|
612
|
+
function useEntitlements() {
|
|
613
|
+
const client = useClient();
|
|
614
|
+
const [state, setState] = (0, import_react2.useState)({
|
|
615
|
+
entitlements: null,
|
|
616
|
+
isLoading: true,
|
|
617
|
+
error: null
|
|
618
|
+
});
|
|
619
|
+
(0, import_react2.useEffect)(() => {
|
|
620
|
+
let mounted = true;
|
|
621
|
+
client.getEntitlements().then((entitlements) => {
|
|
622
|
+
if (mounted) {
|
|
623
|
+
setState({ entitlements, isLoading: false, error: null });
|
|
624
|
+
}
|
|
625
|
+
}).catch((error) => {
|
|
626
|
+
if (mounted) {
|
|
627
|
+
setState({ entitlements: null, isLoading: false, error });
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
return () => {
|
|
631
|
+
mounted = false;
|
|
632
|
+
};
|
|
633
|
+
}, [client]);
|
|
634
|
+
return state;
|
|
635
|
+
}
|
|
636
|
+
function useUsage(featureKey) {
|
|
637
|
+
const client = useClient();
|
|
638
|
+
const [state, setState] = (0, import_react2.useState)({
|
|
639
|
+
usage: [],
|
|
640
|
+
isLoading: true,
|
|
641
|
+
error: null
|
|
642
|
+
});
|
|
643
|
+
(0, import_react2.useEffect)(() => {
|
|
644
|
+
let mounted = true;
|
|
645
|
+
client.getUsage(featureKey).then((usage) => {
|
|
646
|
+
if (mounted) {
|
|
647
|
+
setState({ usage, isLoading: false, error: null });
|
|
648
|
+
}
|
|
649
|
+
}).catch((error) => {
|
|
650
|
+
if (mounted) {
|
|
651
|
+
setState({ usage: [], isLoading: false, error });
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
return () => {
|
|
655
|
+
mounted = false;
|
|
656
|
+
};
|
|
657
|
+
}, [client, featureKey]);
|
|
658
|
+
return state;
|
|
659
|
+
}
|
|
660
|
+
function useWaitlist() {
|
|
661
|
+
const client = useClient();
|
|
662
|
+
return {
|
|
663
|
+
join: (0, import_react2.useCallback)((params) => client.joinWaitlist(params), [client])
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function useInvitation(params) {
|
|
667
|
+
const client = useClient();
|
|
668
|
+
const { token, code } = params;
|
|
669
|
+
const [state, setState] = (0, import_react2.useState)({
|
|
670
|
+
result: null,
|
|
671
|
+
isLoading: !!(token || code),
|
|
672
|
+
error: null
|
|
673
|
+
});
|
|
674
|
+
(0, import_react2.useEffect)(() => {
|
|
675
|
+
if (!token && !code) {
|
|
676
|
+
setState({ result: null, isLoading: false, error: null });
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
let mounted = true;
|
|
680
|
+
const validate = token ? client.validateInvitationToken(token) : client.validateInvitationCode(code);
|
|
681
|
+
validate.then((result) => {
|
|
682
|
+
if (mounted) {
|
|
683
|
+
setState({ result, isLoading: false, error: null });
|
|
684
|
+
}
|
|
685
|
+
}).catch((error) => {
|
|
686
|
+
if (mounted) {
|
|
687
|
+
setState({ result: null, isLoading: false, error });
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
return () => {
|
|
691
|
+
mounted = false;
|
|
692
|
+
};
|
|
693
|
+
}, [client, token, code]);
|
|
694
|
+
return state;
|
|
695
|
+
}
|
|
696
|
+
function useUser() {
|
|
697
|
+
const { user, isLoading, updateProfile } = useAuth();
|
|
698
|
+
return {
|
|
699
|
+
user,
|
|
700
|
+
isLoading,
|
|
701
|
+
update: updateProfile,
|
|
702
|
+
// Computed properties
|
|
703
|
+
fullName: user ? `${user.first_name || ""} ${user.last_name || ""}`.trim() : null,
|
|
704
|
+
initials: user ? `${user.first_name?.[0] || ""}${user.last_name?.[0] || ""}`.toUpperCase() : null,
|
|
705
|
+
email: user?.email || null,
|
|
706
|
+
emailVerified: user?.email_verified || false
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// src/components.tsx
|
|
711
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
712
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
713
|
+
function Authenticated({ children, fallback = null }) {
|
|
714
|
+
const { isAuthenticated } = useAuth();
|
|
715
|
+
return isAuthenticated ? children : fallback;
|
|
716
|
+
}
|
|
717
|
+
function Unauthenticated({ children, fallback = null }) {
|
|
718
|
+
const { isAuthenticated } = useAuth();
|
|
719
|
+
return !isAuthenticated ? children : fallback;
|
|
720
|
+
}
|
|
721
|
+
function AuthLoading({ children }) {
|
|
722
|
+
const client = useClient();
|
|
723
|
+
return client.isLoading ? children : null;
|
|
724
|
+
}
|
|
725
|
+
function HasSubscription({ children, fallback = null }) {
|
|
726
|
+
const client = useClient();
|
|
727
|
+
const [hasSubscription, setHasSubscription] = import_react3.default.useState(null);
|
|
728
|
+
import_react3.default.useEffect(() => {
|
|
729
|
+
client.getSubscription().then((sub) => {
|
|
730
|
+
setHasSubscription(sub !== null && ["ACTIVE", "TRIALING"].includes(sub.status));
|
|
731
|
+
}).catch(() => {
|
|
732
|
+
setHasSubscription(false);
|
|
733
|
+
});
|
|
734
|
+
}, [client]);
|
|
735
|
+
if (hasSubscription === null) return null;
|
|
736
|
+
return hasSubscription ? children : fallback;
|
|
737
|
+
}
|
|
738
|
+
function Allows({
|
|
739
|
+
children,
|
|
740
|
+
key,
|
|
741
|
+
minimum,
|
|
742
|
+
fallback = null
|
|
743
|
+
}) {
|
|
744
|
+
const client = useClient();
|
|
745
|
+
const [allowed, setAllowed] = import_react3.default.useState(null);
|
|
746
|
+
import_react3.default.useEffect(() => {
|
|
747
|
+
client.allows(key, minimum).then((result) => {
|
|
748
|
+
setAllowed(result);
|
|
749
|
+
}).catch(() => {
|
|
750
|
+
setAllowed(false);
|
|
751
|
+
});
|
|
752
|
+
}, [client, key, minimum]);
|
|
753
|
+
if (allowed === null) return null;
|
|
754
|
+
return allowed ? children : fallback;
|
|
755
|
+
}
|
|
756
|
+
function Protected({
|
|
757
|
+
children,
|
|
758
|
+
auth = true,
|
|
759
|
+
key,
|
|
760
|
+
authFallback = null,
|
|
761
|
+
billingFallback = null
|
|
762
|
+
}) {
|
|
763
|
+
const { isAuthenticated } = useAuth();
|
|
764
|
+
if (auth && !isAuthenticated) {
|
|
765
|
+
return authFallback;
|
|
766
|
+
}
|
|
767
|
+
if (key) {
|
|
768
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Allows, { fallback: billingFallback, children }, key);
|
|
769
|
+
}
|
|
770
|
+
return children;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// src/error-boundary.tsx
|
|
774
|
+
var import_react4 = __toESM(require("react"), 1);
|
|
775
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
776
|
+
var KerneErrorBoundary = class extends import_react4.Component {
|
|
777
|
+
constructor(props) {
|
|
778
|
+
super(props);
|
|
779
|
+
this.state = { error: null, hasError: false };
|
|
780
|
+
}
|
|
781
|
+
static getDerivedStateFromError(error) {
|
|
782
|
+
return { error, hasError: true };
|
|
783
|
+
}
|
|
784
|
+
componentDidCatch(error, errorInfo) {
|
|
785
|
+
const kerneError = error;
|
|
786
|
+
this.props.onError?.(kerneError, errorInfo);
|
|
787
|
+
if (process.env.NODE_ENV === "development") {
|
|
788
|
+
console.error("[KerneSDK] Error caught by boundary:", kerneError);
|
|
789
|
+
console.error("[KerneSDK] Component stack:", errorInfo.componentStack);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
reset = () => {
|
|
793
|
+
this.props.onReset?.();
|
|
794
|
+
this.setState({ error: null, hasError: false });
|
|
795
|
+
};
|
|
796
|
+
render() {
|
|
797
|
+
if (this.state.hasError && this.state.error) {
|
|
798
|
+
const { fallback } = this.props;
|
|
799
|
+
const { error } = this.state;
|
|
800
|
+
if (fallback) {
|
|
801
|
+
if (typeof fallback === "function") {
|
|
802
|
+
return fallback(error, this.reset);
|
|
803
|
+
}
|
|
804
|
+
return fallback;
|
|
805
|
+
}
|
|
806
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { padding: "20px", textAlign: "center" }, children: [
|
|
807
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { style: { color: "#dc2626", marginBottom: "10px" }, children: "Something went wrong" }),
|
|
808
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { color: "#6b7280", marginBottom: "20px" }, children: error.message || "An unexpected error occurred" }),
|
|
809
|
+
error.code && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { style: { color: "#9ca3af", fontSize: "12px", marginBottom: "20px" }, children: [
|
|
810
|
+
"Error code: ",
|
|
811
|
+
error.code
|
|
812
|
+
] }),
|
|
813
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
814
|
+
"button",
|
|
815
|
+
{
|
|
816
|
+
onClick: this.reset,
|
|
817
|
+
style: {
|
|
818
|
+
padding: "10px 20px",
|
|
819
|
+
backgroundColor: "#3b82f6",
|
|
820
|
+
color: "white",
|
|
821
|
+
border: "none",
|
|
822
|
+
borderRadius: "6px",
|
|
823
|
+
cursor: "pointer"
|
|
824
|
+
},
|
|
825
|
+
children: "Try again"
|
|
826
|
+
}
|
|
827
|
+
)
|
|
828
|
+
] });
|
|
829
|
+
}
|
|
830
|
+
return this.props.children;
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
function useKerneError() {
|
|
834
|
+
const [error, setError] = import_react4.default.useState(null);
|
|
835
|
+
const handleError = import_react4.default.useCallback((err) => {
|
|
836
|
+
setError(err);
|
|
837
|
+
}, []);
|
|
838
|
+
const clearError = import_react4.default.useCallback(() => {
|
|
839
|
+
setError(null);
|
|
840
|
+
}, []);
|
|
841
|
+
return {
|
|
842
|
+
error,
|
|
843
|
+
hasError: error !== null,
|
|
844
|
+
handleError,
|
|
845
|
+
clearError
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// src/store.ts
|
|
850
|
+
var KerneStore = class {
|
|
851
|
+
state;
|
|
852
|
+
listeners = /* @__PURE__ */ new Set();
|
|
853
|
+
constructor(initialState) {
|
|
854
|
+
this.state = {
|
|
855
|
+
user: null,
|
|
856
|
+
isAuthenticated: false,
|
|
857
|
+
isLoading: true,
|
|
858
|
+
subscription: null,
|
|
859
|
+
error: null,
|
|
860
|
+
...initialState
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
getState() {
|
|
864
|
+
return this.state;
|
|
865
|
+
}
|
|
866
|
+
getSnapshot() {
|
|
867
|
+
return this.state;
|
|
868
|
+
}
|
|
869
|
+
getServerSnapshot() {
|
|
870
|
+
return this.state;
|
|
871
|
+
}
|
|
872
|
+
setState(partial) {
|
|
873
|
+
const prevState = this.state;
|
|
874
|
+
this.state = { ...this.state, ...partial };
|
|
875
|
+
if (this.hasChanged(prevState, this.state)) {
|
|
876
|
+
this.notify();
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
hasChanged(prev, next) {
|
|
880
|
+
return prev.user !== next.user || prev.isAuthenticated !== next.isAuthenticated || prev.isLoading !== next.isLoading || prev.subscription !== next.subscription || prev.error !== next.error;
|
|
881
|
+
}
|
|
882
|
+
subscribe(listener) {
|
|
883
|
+
this.listeners.add(listener);
|
|
884
|
+
return () => {
|
|
885
|
+
this.listeners.delete(listener);
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
notify() {
|
|
889
|
+
this.listeners.forEach((listener) => listener());
|
|
890
|
+
}
|
|
891
|
+
// Selector-based subscription for optimized re-renders
|
|
892
|
+
select(selector) {
|
|
893
|
+
return selector(this.state);
|
|
894
|
+
}
|
|
895
|
+
};
|
|
230
896
|
// Annotate the CommonJS export names for ESM import in node:
|
|
231
897
|
0 && (module.exports = {
|
|
898
|
+
Allows,
|
|
899
|
+
AuthLoading,
|
|
900
|
+
Authenticated,
|
|
901
|
+
HasSubscription,
|
|
232
902
|
KerneClient,
|
|
903
|
+
KerneContext,
|
|
904
|
+
KerneErrorBoundary,
|
|
233
905
|
KerneProvider,
|
|
234
|
-
|
|
906
|
+
KerneStore,
|
|
907
|
+
Protected,
|
|
908
|
+
Unauthenticated,
|
|
909
|
+
useAllows,
|
|
235
910
|
useAuth,
|
|
236
|
-
|
|
237
|
-
|
|
911
|
+
useCheck,
|
|
912
|
+
useCheckout,
|
|
913
|
+
useClient,
|
|
914
|
+
useEntitlements,
|
|
915
|
+
useInvitation,
|
|
916
|
+
useKerneError,
|
|
917
|
+
usePlans,
|
|
918
|
+
usePortal,
|
|
919
|
+
useSubscription,
|
|
920
|
+
useUsage,
|
|
921
|
+
useUser,
|
|
922
|
+
useWaitlist
|
|
238
923
|
});
|