@singlebase/core 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.
package/dist/index.js ADDED
@@ -0,0 +1,604 @@
1
+ class g extends Error {
2
+ constructor(t, r) {
3
+ super(t.message), this.name = "SinglebaseError", this.status = t.status, this.type = t.type, this.code = t.message, this.details = t.details, this.traceId = t.trace_id ?? null, this.cause = r;
4
+ }
5
+ static fromNetworkError(t) {
6
+ return new g(
7
+ {
8
+ type: "NETWORK_ERROR",
9
+ status: 0,
10
+ message: "NETWORK_ERROR"
11
+ },
12
+ t
13
+ );
14
+ }
15
+ }
16
+ const L = {
17
+ INVALID_PAYLOAD: "highlight_invalid_fields",
18
+ INVALID_CREDENTIALS: "generic_credentials_message",
19
+ CODE_REQUIRED: "reveal_code_input",
20
+ INVALID_TOKEN: "expired_or_invalid_code_message",
21
+ INVALID_ID_TOKEN: "attempt_refresh_or_signout",
22
+ INVALID_BEARER_TOKEN: "attempt_refresh_or_signout",
23
+ INVALID_SESSION: "clear_session_and_ask_signin",
24
+ INVALID_TOKEN_AUDIENCE: "clear_session_config_error",
25
+ REQUIRE_PASSWORD_CHANGE: "route_to_recovery_or_invite",
26
+ INVALID_PASSWORD: "show_password_requirements",
27
+ MISSING_PROFILE_DATA: "require_changed_field",
28
+ AUTH_RATE_LIMITED: "disable_retry_show_rate_limited",
29
+ AUTH_DISABLED: "render_auth_unavailable",
30
+ EMAIL_PROVIDER_DISABLED: "hide_email_forms",
31
+ EMAIL_SIGNUP_DISABLED: "hide_signup",
32
+ OAUTH_DISABLED: "hide_oauth",
33
+ OAUTH_SIGNUP_DISABLED: "remove_oauth_from_signup",
34
+ INVALID_OAUTH_PROVIDER: "refresh_settings_hide_provider",
35
+ INVALID_NONCE: "restart_oauth_new_nonce",
36
+ SIGN_IN_TO_LINK_PROVIDER: "ask_signin_then_link",
37
+ AUTHENTICATION_REQUIRED: "route_to_signin",
38
+ UNAUTHORIZED_DOMAIN: "show_integrator_config_error"
39
+ };
40
+ function z(e) {
41
+ return L[e] ?? "unknown";
42
+ }
43
+ function k(e) {
44
+ return e === "localhost" || e === "127.0.0.1" || e === "0.0.0.0";
45
+ }
46
+ const p = "https://v1.singlebase.io/api";
47
+ function C(e) {
48
+ if (e.baseUrl === void 0) return p;
49
+ if (e.baseUrl === null || String(e.baseUrl).trim() === "")
50
+ throw new Error(
51
+ `SinglebaseOptions.baseUrl cannot be null or empty. Omit it to use ${p}.`
52
+ );
53
+ return String(e.baseUrl).trim().replace(/\/+$/, "");
54
+ }
55
+ function P(e) {
56
+ var n;
57
+ const t = C(e);
58
+ B(t);
59
+ const r = (n = e.urlAccessKey) == null ? void 0 : n.trim();
60
+ return r ? `${t}/${encodeURIComponent(r)}` : t;
61
+ }
62
+ function B(e) {
63
+ let t;
64
+ try {
65
+ t = new URL(e);
66
+ } catch {
67
+ throw new Error(`SinglebaseOptions.baseUrl is not a valid URL: ${e}`);
68
+ }
69
+ if (t.protocol !== "https:" && !k(t.hostname))
70
+ throw new Error(
71
+ `SinglebaseOptions.baseUrl must use HTTPS outside of localhost/127.0.0.1/0.0.0.0 (got ${t.protocol})`
72
+ );
73
+ }
74
+ async function m(e, t, r, n = {}) {
75
+ const s = P(e), o = { operation: t, payload: r };
76
+ n.options && (o.options = n.options);
77
+ const a = {
78
+ "Content-Type": "application/json",
79
+ "X-API-Key": e.apiKey
80
+ };
81
+ n.token && (a.Authorization = `Bearer ${n.token}`);
82
+ const i = e.fetch ?? globalThis.fetch;
83
+ let c;
84
+ try {
85
+ c = await i(s, {
86
+ method: "POST",
87
+ headers: a,
88
+ body: JSON.stringify(o),
89
+ signal: n.signal
90
+ });
91
+ } catch (h) {
92
+ throw g.fromNetworkError(h);
93
+ }
94
+ let f;
95
+ try {
96
+ f = await c.json();
97
+ } catch (h) {
98
+ throw g.fromNetworkError(h);
99
+ }
100
+ if ("error" in f)
101
+ throw new g(f.error);
102
+ return f.data;
103
+ }
104
+ const j = /* @__PURE__ */ new Set(["INVALID_ID_TOKEN", "INVALID_BEARER_TOKEN"]);
105
+ class X {
106
+ constructor(t) {
107
+ this.options = t, this.bridge = null, this.provider = null;
108
+ }
109
+ /** Registers the session owner (the auth client) as the token source. */
110
+ setAuthBridge(t) {
111
+ this.bridge = t;
112
+ }
113
+ /** Registers a factory that builds+hydrates the token source on first need. */
114
+ setAuthProvider(t) {
115
+ this.provider = t;
116
+ }
117
+ async resolveBridge() {
118
+ return this.bridge ? this.bridge : this.provider ? (this.bridge = await this.provider(), this.bridge) : null;
119
+ }
120
+ /**
121
+ * Sends one operation. Injects the session bearer unless told otherwise,
122
+ * and on a stale-token error refreshes once and retries the call exactly
123
+ * once — never looping.
124
+ */
125
+ async dispatch(t, { bearer: r, signal: n } = {}) {
126
+ const s = r !== void 0, o = s ? null : await this.resolveBridge(), a = s ? r : (o == null ? void 0 : o.getToken()) ?? null, i = { token: a, signal: n, options: t.options };
127
+ try {
128
+ return await m(
129
+ this.options,
130
+ t.operation,
131
+ t.payload ?? {},
132
+ i
133
+ );
134
+ } catch (c) {
135
+ const f = c instanceof g ? c.code : "";
136
+ if (!(!s && a !== null && o !== null && j.has(f))) throw c;
137
+ const A = await o.refresh();
138
+ if (!A) throw c;
139
+ return m(
140
+ this.options,
141
+ t.operation,
142
+ t.payload ?? {},
143
+ { ...i, token: A }
144
+ );
145
+ }
146
+ }
147
+ }
148
+ const K = "singlebase-auth", V = 1, l = "session", E = "wrapping-key", d = "session";
149
+ function I() {
150
+ let e = null;
151
+ return {
152
+ get: () => e,
153
+ set: (t) => {
154
+ e = t;
155
+ },
156
+ clear: () => {
157
+ e = null;
158
+ }
159
+ };
160
+ }
161
+ function R(e, t) {
162
+ return {
163
+ get: () => {
164
+ var n;
165
+ const r = (n = e()) == null ? void 0 : n.getItem(t);
166
+ if (!r) return null;
167
+ try {
168
+ return JSON.parse(r);
169
+ } catch {
170
+ return null;
171
+ }
172
+ },
173
+ set: (r) => {
174
+ var n;
175
+ (n = e()) == null || n.setItem(t, JSON.stringify(r));
176
+ },
177
+ clear: () => {
178
+ var r;
179
+ (r = e()) == null || r.removeItem(t);
180
+ }
181
+ };
182
+ }
183
+ function ee(e = "singlebase-auth-session") {
184
+ return R(() => globalThis.localStorage, e);
185
+ }
186
+ function M(e = "singlebase-auth-session") {
187
+ return R(() => globalThis.sessionStorage, e);
188
+ }
189
+ function b(e) {
190
+ return new Promise((t, r) => {
191
+ e.onsuccess = () => t(e.result), e.onerror = () => r(e.error);
192
+ });
193
+ }
194
+ function w() {
195
+ return new Promise((e, t) => {
196
+ const r = globalThis.indexedDB.open(K, V);
197
+ r.onupgradeneeded = () => {
198
+ r.result.objectStoreNames.contains(l) || r.result.createObjectStore(l);
199
+ }, r.onsuccess = () => e(r.result), r.onerror = () => t(r.error), r.onblocked = () => t(new Error("indexedDB open blocked"));
200
+ });
201
+ }
202
+ async function S(e) {
203
+ const t = await w();
204
+ try {
205
+ const r = t.transaction(l, "readonly");
206
+ return await b(r.objectStore(l).get(e));
207
+ } finally {
208
+ t.close();
209
+ }
210
+ }
211
+ async function T(e, t) {
212
+ const r = await w();
213
+ try {
214
+ const n = r.transaction(l, "readwrite");
215
+ await b(n.objectStore(l).put(t, e));
216
+ } finally {
217
+ r.close();
218
+ }
219
+ }
220
+ async function D(e) {
221
+ const t = await w();
222
+ try {
223
+ const r = t.transaction(l, "readwrite");
224
+ await b(r.objectStore(l).delete(e));
225
+ } finally {
226
+ t.close();
227
+ }
228
+ }
229
+ function H() {
230
+ return typeof globalThis.indexedDB < "u" && typeof globalThis.crypto < "u" && typeof globalThis.crypto.subtle < "u";
231
+ }
232
+ function G() {
233
+ let e = null;
234
+ async function t() {
235
+ return e || (e = (async () => {
236
+ const r = await S(E);
237
+ if (r) return r;
238
+ const n = await globalThis.crypto.subtle.generateKey(
239
+ { name: "AES-GCM", length: 256 },
240
+ // non-extractable: crypto.subtle can use it, JS can never read it
241
+ !1,
242
+ ["encrypt", "decrypt"]
243
+ );
244
+ return await T(E, n), n;
245
+ })().catch((r) => {
246
+ throw e = null, r;
247
+ })), e;
248
+ }
249
+ return {
250
+ async get() {
251
+ const r = await S(d);
252
+ if (!r) return null;
253
+ try {
254
+ const n = await t(), s = await globalThis.crypto.subtle.decrypt(
255
+ { name: "AES-GCM", iv: r.iv },
256
+ n,
257
+ r.data
258
+ );
259
+ return JSON.parse(new TextDecoder().decode(s));
260
+ } catch {
261
+ return await D(d).catch(() => {
262
+ }), null;
263
+ }
264
+ },
265
+ async set(r) {
266
+ const n = await t(), s = globalThis.crypto.getRandomValues(new Uint8Array(12)), o = await globalThis.crypto.subtle.encrypt(
267
+ { name: "AES-GCM", iv: s },
268
+ n,
269
+ new TextEncoder().encode(JSON.stringify(r))
270
+ );
271
+ await T(d, { iv: s, data: o });
272
+ },
273
+ async clear() {
274
+ await D(d).catch(() => {
275
+ });
276
+ }
277
+ };
278
+ }
279
+ function te() {
280
+ let e = null, t = !1;
281
+ async function r() {
282
+ if (e) return e;
283
+ if (!t) {
284
+ if (t = !0, H()) {
285
+ const n = G();
286
+ try {
287
+ return await n.get(), e = n, e;
288
+ } catch {
289
+ }
290
+ }
291
+ e = typeof globalThis.sessionStorage < "u" ? M() : I();
292
+ }
293
+ return e ?? I();
294
+ }
295
+ return {
296
+ async get() {
297
+ try {
298
+ return await (await r()).get();
299
+ } catch {
300
+ return null;
301
+ }
302
+ },
303
+ async set(n) {
304
+ try {
305
+ await (await r()).set(n);
306
+ } catch {
307
+ }
308
+ },
309
+ async clear() {
310
+ try {
311
+ await (await r()).clear();
312
+ } catch {
313
+ }
314
+ }
315
+ };
316
+ }
317
+ class re {
318
+ constructor() {
319
+ this.listeners = /* @__PURE__ */ new Map();
320
+ }
321
+ on(t, r) {
322
+ let n = this.listeners.get(t);
323
+ return n || (n = /* @__PURE__ */ new Set(), this.listeners.set(t, n)), n.add(r), () => n.delete(r);
324
+ }
325
+ emit(t, r) {
326
+ const n = this.listeners.get(t);
327
+ if (n)
328
+ for (const s of n) s(r);
329
+ }
330
+ }
331
+ const ne = [
332
+ "theme",
333
+ "density",
334
+ "fieldStyle",
335
+ "layout",
336
+ "oauthPlacement",
337
+ "stepped",
338
+ "logoUrl",
339
+ "logoText",
340
+ "brandLine",
341
+ "brandFoot",
342
+ "signInTitle",
343
+ "branding",
344
+ "initialScreen",
345
+ "noAccountView",
346
+ "redirectUrl",
347
+ "tosUrl",
348
+ "privacyUrl",
349
+ "nonceStorageKey",
350
+ "authEnabled",
351
+ "allowEmailSignin",
352
+ "allowEmailSignup",
353
+ "allowEmailOtp",
354
+ "allowOauth",
355
+ "allowAccountCreation",
356
+ "inviteEmail",
357
+ "inviteOrg",
358
+ "inviteCode"
359
+ ];
360
+ function se(e) {
361
+ return e.replace(/[A-Z]/g, (t) => "-" + t.toLowerCase());
362
+ }
363
+ const x = [
364
+ "$eq",
365
+ "$ne",
366
+ "$in",
367
+ "$nin",
368
+ "$gt",
369
+ "$gte",
370
+ "$lt",
371
+ "$lte",
372
+ "$elemMatch"
373
+ ], v = new Set(x);
374
+ function J(e) {
375
+ if (e === null || typeof e != "object" || Array.isArray(e)) return !1;
376
+ const t = Object.keys(e);
377
+ return t.length > 0 && t.every((r) => v.has(r));
378
+ }
379
+ function W(e, t) {
380
+ let r = e;
381
+ for (const n of t.split(".")) {
382
+ if (r == null || typeof r != "object") return;
383
+ r = r[n];
384
+ }
385
+ return r;
386
+ }
387
+ function u(e, t) {
388
+ if (e === t) return !0;
389
+ if (Array.isArray(e) && Array.isArray(t))
390
+ return e.length === t.length && e.every((r, n) => u(r, t[n]));
391
+ if (e && t && typeof e == "object" && typeof t == "object") {
392
+ const r = Object.keys(e), n = Object.keys(t);
393
+ return r.length === n.length && r.every(
394
+ (s) => u(e[s], t[s])
395
+ );
396
+ }
397
+ return !1;
398
+ }
399
+ function q(e, t) {
400
+ return typeof e == "number" && typeof t == "number" ? Number.isNaN(e) || Number.isNaN(t) ? null : e - t : typeof e == "string" && typeof t == "string" ? e < t ? -1 : e > t ? 1 : 0 : e instanceof Date && t instanceof Date ? e.getTime() - t.getTime() : null;
401
+ }
402
+ function U(e, t, r) {
403
+ switch (e) {
404
+ case "$eq":
405
+ return u(t, r);
406
+ case "$ne":
407
+ return !u(t, r);
408
+ case "$in":
409
+ return Array.isArray(r) ? Array.isArray(t) ? t.some((n) => r.some((s) => u(n, s))) : r.some((n) => u(t, n)) : !1;
410
+ case "$nin":
411
+ return Array.isArray(r) ? !U("$in", t, r) : !1;
412
+ case "$gt":
413
+ case "$gte":
414
+ case "$lt":
415
+ case "$lte": {
416
+ const n = q(t, r);
417
+ return n === null ? !1 : e === "$gt" ? n > 0 : e === "$gte" ? n >= 0 : e === "$lt" ? n < 0 : n <= 0;
418
+ }
419
+ case "$elemMatch":
420
+ return Array.isArray(t) ? t.some((n) => $(n, r)) : !1;
421
+ default:
422
+ return !1;
423
+ }
424
+ }
425
+ function $(e, t) {
426
+ return J(t) ? Object.entries(t).every(
427
+ ([r, n]) => U(r, e, n)
428
+ ) : u(e, t);
429
+ }
430
+ function oe(e, t) {
431
+ if (!t) return !0;
432
+ const r = Object.entries(t);
433
+ return r.length === 0 ? !0 : e == null ? !1 : r.every(([n, s]) => $(W(e, n), s));
434
+ }
435
+ function ie(e) {
436
+ if (e == null) return [];
437
+ if (typeof e != "object" || Array.isArray(e))
438
+ return ["predicate must be an object"];
439
+ const t = [];
440
+ for (const [r, n] of Object.entries(e)) {
441
+ if (r.startsWith("$")) {
442
+ t.push(`"${r}": top-level operators are not supported; use a field name`);
443
+ continue;
444
+ }
445
+ if (n === null || typeof n != "object" || Array.isArray(n)) continue;
446
+ const s = Object.keys(n), o = s.filter((a) => a.startsWith("$"));
447
+ if (o.length === 0) {
448
+ t.push(
449
+ `"${r}": nested objects are not supported; use dot notation, e.g. "${r}.field"`
450
+ );
451
+ continue;
452
+ }
453
+ o.length !== s.length && t.push(`"${r}": cannot mix operators with plain keys`);
454
+ for (const a of o)
455
+ v.has(a) || t.push(`"${r}": unsupported operator "${a}"`);
456
+ }
457
+ return t;
458
+ }
459
+ const y = () => {
460
+ };
461
+ function F() {
462
+ return { post: y, subscribe: () => y, close: y };
463
+ }
464
+ function Y() {
465
+ const e = globalThis.crypto;
466
+ return e && "randomUUID" in e ? e.randomUUID() : Math.random().toString(36).slice(2) + Date.now().toString(36);
467
+ }
468
+ function ae(e) {
469
+ const t = Y(), r = globalThis.BroadcastChannel;
470
+ if (r) {
471
+ let n;
472
+ try {
473
+ n = new r(e);
474
+ } catch {
475
+ return O(e, t);
476
+ }
477
+ return {
478
+ post(s) {
479
+ try {
480
+ n.postMessage({ signal: s, from: t, at: Date.now() });
481
+ } catch {
482
+ }
483
+ },
484
+ subscribe(s) {
485
+ const o = (a) => {
486
+ const i = a.data;
487
+ !i || typeof i != "object" || i.from !== t && (i.signal !== "session-changed" && i.signal !== "signout" || s(i));
488
+ };
489
+ return n.addEventListener("message", o), () => n.removeEventListener("message", o);
490
+ },
491
+ close() {
492
+ try {
493
+ n.close();
494
+ } catch {
495
+ }
496
+ }
497
+ };
498
+ }
499
+ return O(e, t);
500
+ }
501
+ function O(e, t) {
502
+ const r = Q();
503
+ if (!r) return F();
504
+ const n = `${e}:signal`;
505
+ return {
506
+ post(s) {
507
+ try {
508
+ r.setItem(n, JSON.stringify({ signal: s, from: t, at: Date.now() }));
509
+ } catch {
510
+ }
511
+ },
512
+ subscribe(s) {
513
+ var a;
514
+ const o = (i) => {
515
+ if (!(i.key !== n || !i.newValue))
516
+ try {
517
+ const c = JSON.parse(i.newValue);
518
+ if (c.from === t || c.signal !== "session-changed" && c.signal !== "signout") return;
519
+ s(c);
520
+ } catch {
521
+ }
522
+ };
523
+ return (a = globalThis.addEventListener) == null || a.call(globalThis, "storage", o), () => {
524
+ var i;
525
+ return (i = globalThis.removeEventListener) == null ? void 0 : i.call(globalThis, "storage", o);
526
+ };
527
+ },
528
+ close: y
529
+ };
530
+ }
531
+ function Q() {
532
+ try {
533
+ const e = globalThis.localStorage;
534
+ if (!e) return null;
535
+ const t = "__singlebase_probe__";
536
+ return e.setItem(t, "1"), e.removeItem(t), e;
537
+ } catch {
538
+ return null;
539
+ }
540
+ }
541
+ const N = Symbol.for("singlebase.client.registry");
542
+ function _() {
543
+ const e = globalThis;
544
+ let t = e[N];
545
+ return t || (t = { instances: /* @__PURE__ */ new Map(), default: null }, e[N] = t), t;
546
+ }
547
+ function Z(e) {
548
+ return [
549
+ e.baseUrl ?? p,
550
+ e.urlAccessKey ?? "",
551
+ e.apiKey,
552
+ e.audience ?? "web"
553
+ ].join("|");
554
+ }
555
+ function ce(e, t) {
556
+ const r = _(), n = Z(e), s = r.instances.get(n);
557
+ if (s) return s;
558
+ const o = t();
559
+ return r.instances.set(n, o), r.default || (r.default = o), o;
560
+ }
561
+ function le() {
562
+ return _().default;
563
+ }
564
+ function ue(e) {
565
+ _().default = e;
566
+ }
567
+ function fe(e) {
568
+ const t = _();
569
+ for (const [r, n] of t.instances)
570
+ n === e && t.instances.delete(r);
571
+ t.default === e && (t.default = t.instances.values().next().value ?? null);
572
+ }
573
+ export {
574
+ ne as AUTHUI_PROP_KEYS,
575
+ p as DEFAULT_BASE_URL,
576
+ re as EventEmitter,
577
+ x as OPERATORS,
578
+ X as RpcDispatcher,
579
+ g as SinglebaseAuthError,
580
+ g as SinglebaseError,
581
+ B as assertSecureBaseUrl,
582
+ se as authUIAttributeFor,
583
+ H as canUseCryptoStorage,
584
+ Z as clientKey,
585
+ ae as crossTabChannel,
586
+ te as defaultStorage,
587
+ P as endpointFor,
588
+ le as getDefaultClient,
589
+ W as getPath,
590
+ G as indexedDbCryptoStorage,
591
+ ee as localStorageAdapter,
592
+ z as mapErrorToHint,
593
+ oe as matchesPredicate,
594
+ I as memoryStorage,
595
+ F as noopCrossTabChannel,
596
+ m as request,
597
+ C as resolveBaseUrl,
598
+ ce as resolveClient,
599
+ M as sessionStorageAdapter,
600
+ ue as setDefaultClient,
601
+ fe as unregisterClient,
602
+ ie as validatePredicate
603
+ };
604
+ //# sourceMappingURL=index.js.map