@niledatabase/client 5.0.0-alpha.10

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.mjs ADDED
@@ -0,0 +1,697 @@
1
+ // src/logger.ts
2
+ var UnknownError = class extends Error {
3
+ code;
4
+ constructor(error) {
5
+ super(error?.message ?? error);
6
+ this.name = "UnknownError";
7
+ this.code = error.code;
8
+ if (error instanceof Error) {
9
+ this.stack = error.stack;
10
+ }
11
+ }
12
+ toJSON() {
13
+ return {
14
+ name: this.name,
15
+ message: this.message,
16
+ stack: this.stack
17
+ };
18
+ }
19
+ };
20
+ function formatError(o) {
21
+ if (o instanceof Error && !(o instanceof UnknownError)) {
22
+ return JSON.stringify({ message: o.message, stack: o.stack, name: o.name });
23
+ }
24
+ if (hasErrorProperty(o)) {
25
+ o.error = formatError(o.error);
26
+ o.message = o.message ?? o.error.message;
27
+ }
28
+ return o;
29
+ }
30
+ function hasErrorProperty(x) {
31
+ return !!x?.error;
32
+ }
33
+ var _logger = {
34
+ error(code, metadata) {
35
+ metadata = formatError(metadata);
36
+ console.error(`[nile-auth][error][${code}]`, metadata.message, metadata);
37
+ },
38
+ warn(code) {
39
+ console.warn(`[nile-auth][warn][${code}]`);
40
+ },
41
+ debug(code, metadata) {
42
+ console.log(`[next-auth][debug][${code}]`, metadata);
43
+ }
44
+ };
45
+ function proxyLogger(logger2 = _logger, authorizer2) {
46
+ try {
47
+ if (typeof window === "undefined") {
48
+ return logger2;
49
+ }
50
+ const clientLogger = {};
51
+ for (const level in logger2) {
52
+ clientLogger[level] = (code, metadata) => {
53
+ _logger[level](code, metadata);
54
+ if (level === "error") {
55
+ metadata = formatError(metadata);
56
+ }
57
+ metadata.client = true;
58
+ const url = `${authorizer2.state.basePath}/_log`;
59
+ const body = new URLSearchParams({ level, code, ...metadata });
60
+ if (navigator.sendBeacon) {
61
+ return navigator.sendBeacon(url, body);
62
+ }
63
+ return fetch(url, { method: "POST", body, keepalive: true });
64
+ };
65
+ }
66
+ return clientLogger;
67
+ } catch {
68
+ return _logger;
69
+ }
70
+ }
71
+ var logger = (authorizer2) => proxyLogger(_logger, authorizer2);
72
+
73
+ // src/broadcast.ts
74
+ function now() {
75
+ return Math.floor(Date.now() / 1e3);
76
+ }
77
+ function BroadcastChannel(name = "nextauth.message") {
78
+ return {
79
+ /** Get notified by other tabs/windows. */
80
+ receive(onReceive) {
81
+ const handler = (event) => {
82
+ if (event.key !== name) return;
83
+ const message = JSON.parse(event.newValue ?? "{}");
84
+ if (message?.event !== "session" || !message?.data) return;
85
+ onReceive(message);
86
+ };
87
+ window.addEventListener("storage", handler);
88
+ return () => window.removeEventListener("storage", handler);
89
+ },
90
+ /** Notify other tabs/windows. */
91
+ post(message) {
92
+ if (typeof window === "undefined") return;
93
+ try {
94
+ localStorage.setItem(
95
+ name,
96
+ JSON.stringify({ ...message, timestamp: now() })
97
+ );
98
+ } catch {
99
+ }
100
+ }
101
+ };
102
+ }
103
+ var broadcast = BroadcastChannel();
104
+
105
+ // src/observable.ts
106
+ function isEqual(a, b) {
107
+ if (a === b) return true;
108
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) {
109
+ return false;
110
+ }
111
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
112
+ const keysA = Object.keys(a);
113
+ const keysB = Object.keys(b);
114
+ if (keysA.length !== keysB.length) return false;
115
+ for (const key of keysA) {
116
+ if (!keysB.includes(key) || !isEqual(a[key], b[key])) {
117
+ return false;
118
+ }
119
+ }
120
+ return true;
121
+ }
122
+ function createObservableObject(obj, listenerKeys = ["loading", "session"], eventName = "objectChange") {
123
+ const eventTarget = new EventTarget();
124
+ const listeners = /* @__PURE__ */ new Map();
125
+ const handler = {
126
+ set(target, key, value) {
127
+ const prev = target[key];
128
+ target[key] = value;
129
+ if (isEqual(prev, value)) return true;
130
+ if (listenerKeys.includes(String(key))) {
131
+ eventTarget.dispatchEvent(
132
+ new CustomEvent(eventName, {
133
+ detail: { key, prev, next: value }
134
+ })
135
+ );
136
+ }
137
+ return true;
138
+ }
139
+ };
140
+ return {
141
+ proxy: new Proxy(obj, handler),
142
+ eventTarget,
143
+ addListener(callback) {
144
+ if (listeners.has(callback)) {
145
+ return;
146
+ }
147
+ const wrappedCallback = (e) => callback(e.detail);
148
+ listeners.set(callback, wrappedCallback);
149
+ eventTarget.addEventListener(eventName, wrappedCallback);
150
+ },
151
+ removeListener(callback) {
152
+ const wrappedCallback = listeners.get(callback);
153
+ if (wrappedCallback) {
154
+ eventTarget.removeEventListener(eventName, wrappedCallback);
155
+ listeners.delete(callback);
156
+ }
157
+ }
158
+ };
159
+ }
160
+
161
+ // src/Authorizer.ts
162
+ var Authorizer = class {
163
+ state;
164
+ #logger;
165
+ requestInit;
166
+ addListener;
167
+ removeListener;
168
+ status;
169
+ constructor(config) {
170
+ const { proxy, addListener, removeListener } = createObservableObject(
171
+ {
172
+ basePath: parseUrl(config?.basePath).path,
173
+ baseUrl: parseUrl(config?.baseUrl).origin,
174
+ lastSync: 0,
175
+ getSession: () => void 0,
176
+ session: void 0,
177
+ loading: true
178
+ },
179
+ config?.listenerKeys,
180
+ "auth"
181
+ );
182
+ this.state = proxy;
183
+ this.addListener = addListener;
184
+ this.removeListener = removeListener;
185
+ this.#logger = logger(this);
186
+ this.status = null;
187
+ }
188
+ async sync(event) {
189
+ try {
190
+ const storageEvent = event === "storage";
191
+ if (storageEvent || !this.state.session) {
192
+ this.state.getSession = await this.getSession;
193
+ this.state.lastSync = now();
194
+ }
195
+ if (!event || this.state.session == null || now() < this.state.lastSync) {
196
+ return;
197
+ }
198
+ this.state.lastSync = Date.now();
199
+ this.state.session = await this.getSession();
200
+ } catch (error) {
201
+ this.#logger.error("CLIENT_SESSION_ERROR", error);
202
+ }
203
+ }
204
+ set baseUrl(val) {
205
+ this.state.baseUrl = val;
206
+ this.#logger = logger(this);
207
+ }
208
+ get baseUrl() {
209
+ this.#logger = logger(this);
210
+ return this.state.baseUrl;
211
+ }
212
+ configure(config) {
213
+ if (config?.basePath) this.state.basePath = parseUrl(config?.basePath).path;
214
+ if (config?.baseUrl) this.baseUrl = config.baseUrl;
215
+ if (config?.init) this.requestInit = config.init;
216
+ return this;
217
+ }
218
+ sanitize() {
219
+ return {
220
+ state: {
221
+ baseUrl: this.baseUrl,
222
+ session: {
223
+ user: {
224
+ email: this.state.session?.user?.email
225
+ }
226
+ }
227
+ },
228
+ requestInit: this.requestInit
229
+ };
230
+ }
231
+ async initialize(params) {
232
+ const { baseUrl, session, event } = params ?? {};
233
+ if (baseUrl) this.baseUrl = baseUrl;
234
+ const hasInitialSession = session !== void 0;
235
+ this.state.loading = !hasInitialSession;
236
+ this.state.lastSync = hasInitialSession ? now() : 0;
237
+ this.state.session = session;
238
+ await this.sync(event);
239
+ }
240
+ get apiBaseUrl() {
241
+ return `${this.baseUrl}${this.state.basePath}`;
242
+ }
243
+ async #sendData(url, init) {
244
+ try {
245
+ const options = {
246
+ headers: {
247
+ "Content-Type": "application/json"
248
+ },
249
+ ...this.requestInit ? this.requestInit : {},
250
+ ...init
251
+ };
252
+ const filledUrl = !url.startsWith("http") ? `${window.location.origin}${url}` : url;
253
+ const res = await fetch(filledUrl, options);
254
+ this.state.loading = false;
255
+ return res;
256
+ } catch (error) {
257
+ this.#logger.error("CLIENT_FETCH_ERROR", { error, url });
258
+ return void 0;
259
+ }
260
+ }
261
+ async #fetchData(url, init) {
262
+ const options = {
263
+ ...this.requestInit ? this.requestInit : {},
264
+ ...init
265
+ };
266
+ const res = await this.#sendData(url, options);
267
+ const errorHandler = res?.clone();
268
+ try {
269
+ if (res?.ok) {
270
+ const data = await res.json();
271
+ this.state.loading = false;
272
+ return Object.keys(data).length > 0 ? data : void 0;
273
+ } else {
274
+ const error = await errorHandler?.text();
275
+ if (error) {
276
+ const updatedUrl = new URL(url);
277
+ updatedUrl.searchParams.set("error", error);
278
+ return { url: updatedUrl.toString() };
279
+ }
280
+ }
281
+ } catch (error) {
282
+ if (error instanceof Error) {
283
+ if (!error.message.includes("is not valid JSON")) {
284
+ this.#logger.error("CLIENT_FETCH_ERROR", {
285
+ error,
286
+ url
287
+ });
288
+ } else {
289
+ const error2 = await errorHandler?.text();
290
+ if (error2) {
291
+ const updatedUrl = new URL(url);
292
+ updatedUrl.searchParams.set("error", error2);
293
+ return { url: updatedUrl.toString() };
294
+ }
295
+ }
296
+ }
297
+ return void 0;
298
+ }
299
+ }
300
+ async #fetchFormData(url, init) {
301
+ try {
302
+ const res = await fetch(url, {
303
+ ...this.requestInit,
304
+ ...init,
305
+ headers: {
306
+ "Content-Type": "application/x-www-form-urlencoded"
307
+ }
308
+ });
309
+ if (res) {
310
+ if (res.ok) {
311
+ return {
312
+ data: await res.json(),
313
+ status: res.status,
314
+ ok: res.ok,
315
+ url: res.url
316
+ };
317
+ }
318
+ const { url: responseUrl } = await res.json();
319
+ return {
320
+ data: {},
321
+ status: res.status,
322
+ ok: res?.ok,
323
+ url: responseUrl
324
+ };
325
+ }
326
+ throw new Error(`Unable to fetch ${url}`);
327
+ } catch (error) {
328
+ if (error instanceof Error) {
329
+ if (!error.message.includes("is not valid JSON")) {
330
+ this.#logger.error("CLIENT_FETCH_ERROR", {
331
+ error,
332
+ url
333
+ });
334
+ }
335
+ }
336
+ return void 0;
337
+ }
338
+ }
339
+ async getProviders(url) {
340
+ return await this.#fetchData(url ?? `${this.apiBaseUrl}/auth/providers`);
341
+ }
342
+ async getCsrfToken(url) {
343
+ const response = await this.#fetchData(
344
+ url ?? `${this.apiBaseUrl}/auth/csrf`
345
+ );
346
+ return response?.csrfToken;
347
+ }
348
+ async getSession(params) {
349
+ if (this.status === "getSession" /* SESSION */) {
350
+ return;
351
+ }
352
+ this.status = "getSession" /* SESSION */;
353
+ if (params?.init) {
354
+ this.requestInit = params.init;
355
+ }
356
+ if (params?.baseUrl) {
357
+ this.baseUrl = params.baseUrl;
358
+ }
359
+ if (this.state.session && now() < this.state.lastSync) {
360
+ this.status = null;
361
+ return this.state.session;
362
+ }
363
+ this.state.loading = true;
364
+ const session = await this.#fetchData(
365
+ `${this.apiBaseUrl}/auth/session`
366
+ );
367
+ broadcast.post({ event: "session", data: { trigger: "getSession" } });
368
+ this.status = null;
369
+ if (session) {
370
+ this.state.session = session;
371
+ await this.sync("storage");
372
+ return { ...session, loading: this.state.loading };
373
+ }
374
+ return { loading: this.state.loading };
375
+ }
376
+ async refreshSession() {
377
+ this.state.loading = true;
378
+ const session = await this.#fetchData(
379
+ `${this.apiBaseUrl}/auth/session`
380
+ );
381
+ broadcast.post({ event: "session", data: { trigger: "getSession" } });
382
+ this.state.session = session;
383
+ await this.sync("storage");
384
+ return session;
385
+ }
386
+ async signOut(options) {
387
+ const {
388
+ callbackUrl = window.location.href,
389
+ baseUrl,
390
+ auth: auth2,
391
+ fetchUrl,
392
+ basePath
393
+ } = options ?? {};
394
+ if (basePath) {
395
+ this.state.basePath = basePath;
396
+ }
397
+ if (baseUrl) {
398
+ this.baseUrl = baseUrl;
399
+ }
400
+ if (auth2) {
401
+ this.requestInit = auth2.requestInit;
402
+ if (auth2.state?.baseUrl) {
403
+ this.baseUrl = auth2.state.baseUrl;
404
+ }
405
+ }
406
+ const baseFetch = fetchUrl ?? `${this.apiBaseUrl}/auth/signout`;
407
+ const fetchOptions = {
408
+ method: "post",
409
+ body: new URLSearchParams({
410
+ csrfToken: String(await this.getCsrfToken()),
411
+ callbackUrl,
412
+ json: String(true)
413
+ })
414
+ };
415
+ const res = await this.#fetchFormData(
416
+ baseFetch,
417
+ fetchOptions
418
+ );
419
+ broadcast.post({ event: "session", data: { trigger: "signout" } });
420
+ if (this.requestInit?.credentials) {
421
+ window.location.href = callbackUrl;
422
+ if (callbackUrl.includes("#")) window.location.reload();
423
+ return void 0;
424
+ }
425
+ if (options?.redirect ?? true) {
426
+ const url = res?.data?.url ?? callbackUrl;
427
+ window.location.href = url;
428
+ if (url.includes("#")) window.location.reload();
429
+ return void 0;
430
+ }
431
+ await this.state.getSession({ event: "storage" });
432
+ return res?.data;
433
+ }
434
+ async signIn(provider, options, authorizationParams) {
435
+ const {
436
+ callbackUrl = window.location.href,
437
+ resetUrl = window.location.href,
438
+ providersUrl,
439
+ csrfUrl,
440
+ baseUrl,
441
+ fetchUrl,
442
+ init,
443
+ auth: auth2,
444
+ redirect = true,
445
+ ...remaining
446
+ } = options ?? {};
447
+ if (baseUrl) {
448
+ this.baseUrl = baseUrl;
449
+ }
450
+ if (auth2) {
451
+ if (auth2.requestInit) {
452
+ this.requestInit = auth2.requestInit;
453
+ }
454
+ if (auth2.state?.baseUrl) {
455
+ this.baseUrl = auth2.state.baseUrl;
456
+ }
457
+ }
458
+ if (init) {
459
+ this.requestInit = init;
460
+ }
461
+ const providers = await this.getProviders(providersUrl);
462
+ if (!providers) {
463
+ return { error: "No providers enabled" };
464
+ }
465
+ if (!provider || !(provider in providers)) {
466
+ return { error: `Provider ${provider} not enabled` };
467
+ }
468
+ const isCredentials = providers[provider].type === "credentials";
469
+ const isEmail = providers[provider].type === "email";
470
+ const isSupportingReturn = isCredentials || isEmail;
471
+ const baseFetch = `${this.apiBaseUrl}/auth`;
472
+ const signInUrl = fetchUrl ?? `${baseFetch}/${isCredentials ? "callback" : "signin"}/${provider}`;
473
+ const _signInUrl = `${signInUrl}${authorizationParams ? `?${new URLSearchParams(authorizationParams)}` : ""}`;
474
+ const data = await this.#fetchFormData(_signInUrl, {
475
+ method: "post",
476
+ body: new URLSearchParams({
477
+ ...remaining,
478
+ csrfToken: String(await this.getCsrfToken(csrfUrl)),
479
+ callbackUrl,
480
+ json: String(true),
481
+ resetUrl
482
+ })
483
+ });
484
+ if (data?.ok && this.requestInit?.credentials && isSupportingReturn) {
485
+ window.location.reload();
486
+ return;
487
+ }
488
+ if (data?.ok && (redirect || !isSupportingReturn)) {
489
+ const url = data?.data.url ?? callbackUrl;
490
+ window.location.href = url;
491
+ if (url.includes("#")) window.location.reload();
492
+ return;
493
+ }
494
+ const error = data?.data?.url ? new URL(String(data?.data?.url)).searchParams.get("error") : null;
495
+ if (data?.ok) {
496
+ await this.initialize();
497
+ await this.getSession({ event: "storage" });
498
+ }
499
+ return {
500
+ error,
501
+ status: data?.status,
502
+ ok: data?.ok,
503
+ url: error ? null : data?.url
504
+ };
505
+ }
506
+ async signUp(options) {
507
+ const {
508
+ password,
509
+ tenantId,
510
+ fetchUrl,
511
+ newTenantName,
512
+ createTenant,
513
+ baseUrl,
514
+ init,
515
+ email,
516
+ auth: auth2,
517
+ callbackUrl = window.location.href
518
+ } = options;
519
+ if (baseUrl) {
520
+ this.baseUrl = baseUrl;
521
+ }
522
+ if (auth2) {
523
+ if (auth2.requestInit) {
524
+ this.requestInit = auth2.requestInit;
525
+ }
526
+ if (auth2.state?.baseUrl) {
527
+ this.baseUrl = auth2.state.baseUrl;
528
+ }
529
+ }
530
+ if (init) {
531
+ this.requestInit = init;
532
+ }
533
+ const searchParams = new URLSearchParams();
534
+ if (newTenantName) {
535
+ searchParams.set("newTenantName", newTenantName);
536
+ } else if (createTenant) {
537
+ if (typeof createTenant === "boolean") {
538
+ searchParams.set("newTenantName", email);
539
+ } else if (typeof createTenant === "string") {
540
+ searchParams.set("newTenantName", createTenant);
541
+ }
542
+ }
543
+ if (tenantId) {
544
+ searchParams.set("tenantId", tenantId);
545
+ }
546
+ let signUpUrl = fetchUrl ?? `${this.apiBaseUrl}/signup`;
547
+ if (searchParams.size > 0) {
548
+ signUpUrl += `?${searchParams}`;
549
+ }
550
+ const data = await this.#fetchData(signUpUrl, {
551
+ method: "post",
552
+ body: JSON.stringify({ email, password })
553
+ });
554
+ const error = data?.url ? new URL(data.url).searchParams.get("error") : null;
555
+ if (!error) {
556
+ if (data) {
557
+ await this.initialize({ event: "storage" });
558
+ await this.getSession({ event: "storage" });
559
+ }
560
+ if (this.requestInit?.credentials) {
561
+ window.location.reload();
562
+ return;
563
+ }
564
+ if (options?.redirect ?? true) {
565
+ const url = callbackUrl;
566
+ window.location.href = url;
567
+ if (url.includes("#")) window.location.reload();
568
+ }
569
+ }
570
+ return {
571
+ data,
572
+ error
573
+ };
574
+ }
575
+ async resetPassword(options) {
576
+ const { password, fetchUrl, email, redirect, callbackUrl } = options;
577
+ this.#configureFetch(options);
578
+ const resetPasswordUrl = fetchUrl ?? `${this.apiBaseUrl}/auth/reset-password`;
579
+ let resetPasswordWithParams = resetPasswordUrl;
580
+ const searchParams = new URLSearchParams();
581
+ if (redirect === false) {
582
+ searchParams.set("json", "true");
583
+ }
584
+ if (searchParams.size > 0) {
585
+ resetPasswordWithParams += `?${searchParams}`;
586
+ }
587
+ const data = await this.#sendData(resetPasswordWithParams, {
588
+ method: "post",
589
+ body: JSON.stringify({
590
+ email,
591
+ password,
592
+ redirectUrl: resetPasswordUrl,
593
+ callbackUrl
594
+ })
595
+ });
596
+ if (!data?.ok) {
597
+ throw new Error(await data?.clone().text());
598
+ }
599
+ if (redirect === false) {
600
+ const json = await data?.json();
601
+ const { url: urlWithParams } = json;
602
+ resetPasswordWithParams = `${urlWithParams}&redirect=false`;
603
+ await this.#sendData(resetPasswordWithParams);
604
+ return await this.#sendData(resetPasswordWithParams, {
605
+ method: password ? "put" : "post",
606
+ body: JSON.stringify({ email, password })
607
+ });
608
+ }
609
+ }
610
+ #configureFetch(params) {
611
+ const { baseUrl, init, auth: auth2 } = params;
612
+ if (baseUrl) {
613
+ this.baseUrl = baseUrl;
614
+ }
615
+ if (auth2) {
616
+ if (auth2.requestInit) {
617
+ this.requestInit = auth2.requestInit;
618
+ }
619
+ if (auth2.state?.baseUrl) {
620
+ this.baseUrl = auth2.state.baseUrl;
621
+ }
622
+ }
623
+ if (init) {
624
+ this.requestInit = init;
625
+ }
626
+ }
627
+ };
628
+ function parseUrl(url) {
629
+ let defaultUrl = new URL("http://localhost:3000");
630
+ if (typeof window !== "undefined") {
631
+ defaultUrl = new URL(`${window.location.origin}/api`);
632
+ }
633
+ if (url && !url.startsWith("http")) {
634
+ url = `https://${url}`;
635
+ }
636
+ const _url = new URL(url ?? defaultUrl);
637
+ const path = (_url.pathname === "/" ? defaultUrl.pathname : _url.pathname).replace(/\/$/, "");
638
+ const base = `${_url.origin}${path}`;
639
+ return {
640
+ origin: _url.origin,
641
+ host: _url.host,
642
+ path,
643
+ base,
644
+ toString: () => base
645
+ };
646
+ }
647
+ var authorizer = new Authorizer();
648
+ var _auth = () => {
649
+ return authorizer;
650
+ };
651
+ var auth = _auth();
652
+ var getSession = async function getSession2(params) {
653
+ return await auth.getSession(params);
654
+ };
655
+ var getCsrfToken = async function getCsrfToken2(url) {
656
+ return auth.getCsrfToken(url);
657
+ };
658
+ var getProviders = async function getProviders2() {
659
+ return auth.getProviders();
660
+ };
661
+ var signOut = async function signOut2(options) {
662
+ return auth.signOut(options);
663
+ };
664
+ var signIn = async function signIn2(provider, options, authParams) {
665
+ return auth.signIn(provider, options, authParams);
666
+ };
667
+ var signUp = async function signUp2(options) {
668
+ return auth.signUp(options);
669
+ };
670
+ var resetPassword = async function resetPassword2(options) {
671
+ return auth.resetPassword(options);
672
+ };
673
+
674
+ // src/status.ts
675
+ function getStatus(load, sess) {
676
+ if (load) {
677
+ return "loading";
678
+ }
679
+ if (sess) {
680
+ return "authenticated";
681
+ }
682
+ return "unauthenticated";
683
+ }
684
+ export {
685
+ Authorizer,
686
+ auth,
687
+ broadcast,
688
+ getCsrfToken,
689
+ getProviders,
690
+ getSession,
691
+ getStatus,
692
+ resetPassword,
693
+ signIn,
694
+ signOut,
695
+ signUp
696
+ };
697
+ //# sourceMappingURL=index.mjs.map