@niledatabase/server 5.0.0-alpha.9 → 5.0.1-alpha.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 CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var async_hooks = require('async_hooks');
3
4
  require('dotenv/config');
4
5
  var pg = require('pg');
5
6
 
@@ -8,6 +9,15 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
9
  var pg__default = /*#__PURE__*/_interopDefault(pg);
9
10
 
10
11
  // src/types.ts
12
+ var ExtensionState = /* @__PURE__ */ ((ExtensionState2) => {
13
+ ExtensionState2["onHandleRequest"] = "onHandleRequest";
14
+ ExtensionState2["onRequest"] = "onRequest";
15
+ ExtensionState2["onResponse"] = "onResponse";
16
+ ExtensionState2["withContext"] = "withContext";
17
+ ExtensionState2["withTenantId"] = "withTenantId";
18
+ ExtensionState2["withUserId"] = "withUserId";
19
+ return ExtensionState2;
20
+ })(ExtensionState || {});
11
21
  var APIErrorErrorCodeEnum = {
12
22
  InternalError: "internal_error",
13
23
  BadRequest: "bad_request",
@@ -63,21 +73,18 @@ var appRoutes = (prefix = DEFAULT_PREFIX) => ({
63
73
  INVITE: `${prefix}${"/tenants/{tenantId}/invite" /* INVITE */}`,
64
74
  LOG: `${prefix}/_log`
65
75
  });
66
- var apiRoutes = (config) => ({
67
- ME: makeRestUrl(config, "/me"),
68
- USERS: (qp) => makeRestUrl(config, "/users", qp),
69
- USER: (userId) => makeRestUrl(config, `/users/${userId}`),
70
- TENANTS: makeRestUrl(config, "/tenants"),
71
- TENANT: (tenantId) => makeRestUrl(config, `/tenants/${tenantId}`),
72
- SIGNUP: makeRestUrl(config, "/signup"),
73
- TENANT_USERS: (tenantId) => makeRestUrl(config, `/tenants/${tenantId}/users`),
74
- INVITES: (tenantId) => makeRestUrl(config, `/tenants/${tenantId}/invites`),
75
- INVITE: (tenantId) => makeRestUrl(config, `/tenants/${tenantId}/invite`),
76
- TENANT_USER: makeRestUrl(
77
- config,
78
- `/tenants/${config.tenantId}/users/${config.userId}`
79
- ),
80
- USER_TENANTS: (userId) => makeRestUrl(config, `/users/${userId}/tenants`)
76
+ var apiRoutes = (apiUrl) => ({
77
+ ME: makeRestUrl(apiUrl, "/me"),
78
+ USERS: (qp) => makeRestUrl(apiUrl, "/users", qp),
79
+ USER: (userId) => makeRestUrl(apiUrl, `/users/${userId}`),
80
+ TENANTS: makeRestUrl(apiUrl, "/tenants"),
81
+ TENANT: (tenantId) => makeRestUrl(apiUrl, `/tenants/${tenantId}`),
82
+ SIGNUP: makeRestUrl(apiUrl, "/signup"),
83
+ TENANT_USERS: (tenantId) => makeRestUrl(apiUrl, `/tenants/${tenantId}/users`),
84
+ INVITES: (tenantId) => makeRestUrl(apiUrl, `/tenants/${tenantId}/invites`),
85
+ INVITE: (tenantId) => makeRestUrl(apiUrl, `/tenants/${tenantId}/invite`),
86
+ TENANT_USER: (tenantId, userId) => makeRestUrl(apiUrl, `/tenants/${tenantId}/users/${userId}`),
87
+ USER_TENANTS: (userId) => makeRestUrl(apiUrl, `/users/${userId}/tenants`)
81
88
  });
82
89
  var proxyRoutes = (config) => ({
83
90
  SIGNIN: makeRestUrl(config, "/auth/signin" /* SIGNIN */),
@@ -101,8 +108,8 @@ function filterNullUndefined(obj) {
101
108
  )
102
109
  );
103
110
  }
104
- function makeRestUrl(config, path, qp) {
105
- const url = config.apiUrl || NILEDB_API_URL;
111
+ function makeRestUrl(apiUrl, path, qp) {
112
+ const url = apiUrl || NILEDB_API_URL;
106
113
  if (!url) {
107
114
  throw new Error(
108
115
  "An API url is required. Set it via NILEDB_API_URL. Was auto configuration run?"
@@ -126,9 +133,309 @@ function isUUID(value) {
126
133
  return regex.test(value);
127
134
  }
128
135
 
136
+ // src/utils/Logger.ts
137
+ var red = "\x1B[31m";
138
+ var yellow = "\x1B[38;2;255;255;0m";
139
+ var purple = "\x1B[38;2;200;160;255m";
140
+ var orange = "\x1B[38;2;255;165;0m";
141
+ var reset = "\x1B[0m";
142
+ var baseLogger = (config, ...params) => ({
143
+ silly(message, meta) {
144
+ if (config?.debug && process.env.LOG_LEVEL === "silly") {
145
+ console.log(
146
+ `${orange}[niledb]${reset}${purple}[DEBUG]${reset}${params.join(
147
+ ""
148
+ )}${reset} ${message}`,
149
+ meta ? `${JSON.stringify(meta)}` : ""
150
+ );
151
+ }
152
+ },
153
+ info(message, meta) {
154
+ if (config?.debug) {
155
+ console.info(
156
+ `${orange}[niledb]${reset}${purple}[DEBUG]${reset}${params.join(
157
+ ""
158
+ )}${reset} ${message}`,
159
+ meta ? `${JSON.stringify(meta)}` : ""
160
+ );
161
+ }
162
+ },
163
+ debug(message, meta) {
164
+ if (config?.debug) {
165
+ console.log(
166
+ `${orange}[niledb]${reset}${purple}[DEBUG]${reset}${params.join(
167
+ ""
168
+ )}${reset} ${message}`,
169
+ meta ? `${JSON.stringify(meta)}` : ""
170
+ );
171
+ }
172
+ },
173
+ warn(message, meta) {
174
+ if (config?.debug) {
175
+ console.warn(
176
+ `${orange}[niledb]${reset}${yellow}[WARN]${reset}${params.join(
177
+ ""
178
+ )}${reset} ${message}`,
179
+ meta ? JSON.stringify(meta) : ""
180
+ );
181
+ }
182
+ },
183
+ error(message, meta) {
184
+ console.error(
185
+ `${orange}[niledb]${reset}${red}[ERROR]${reset}${params.join(
186
+ ""
187
+ )}${red} ${message}`,
188
+ meta ? meta : "",
189
+ `${reset}`
190
+ );
191
+ }
192
+ });
193
+ function Logger(config) {
194
+ return (prefixes) => {
195
+ const { info, debug, warn: warn2, error, silly: silly2 } = config && typeof config?.logger === "function" ? config.logger(prefixes) : baseLogger(config, prefixes);
196
+ return {
197
+ info,
198
+ debug,
199
+ warn: warn2,
200
+ error,
201
+ silly: silly2
202
+ };
203
+ };
204
+ }
205
+ function matchesLog(configRoutes, request2) {
206
+ return urlMatches(request2.url, configRoutes.LOG);
207
+ }
208
+
209
+ // src/api/utils/extensions.ts
210
+ function getRequestConfig(params) {
211
+ if (typeof params[1] === "object") {
212
+ return params[1];
213
+ }
214
+ return {};
215
+ }
216
+ function bindRunExtensions(instance) {
217
+ return async function runExtensions(toRun, config, params, _init) {
218
+ const { debug } = config.logger("[EXTENSIONS]");
219
+ const extensionConfig = getRequestConfig(
220
+ Array.isArray(params) ? params : [null, params]
221
+ );
222
+ if (config.extensions) {
223
+ for (const create2 of config.extensions) {
224
+ if (typeof create2 !== "function") {
225
+ continue;
226
+ }
227
+ const ext = create2(instance);
228
+ if (extensionConfig.disableExtensions?.includes(ext.id)) {
229
+ continue;
230
+ }
231
+ if (ext.withTenantId && toRun === "withTenantId" /* withTenantId */) {
232
+ ctx.set({
233
+ tenantId: await ext.withTenantId()
234
+ });
235
+ }
236
+ if (ext.withUserId && toRun === "withUserId" /* withUserId */) {
237
+ ctx.set({ userId: await ext.withUserId() });
238
+ }
239
+ if (ext.withContext && toRun === "withContext" /* withContext */) {
240
+ await ext.withContext(ctx);
241
+ }
242
+ if (ext.onHandleRequest && toRun === "onHandleRequest" /* onHandleRequest */) {
243
+ const result = await ext.onHandleRequest(
244
+ Array.isArray(params) ? params : [params]
245
+ );
246
+ debug(`${ext.id ?? create2.name} ran onHandleRequest`);
247
+ if (result != null) {
248
+ return result;
249
+ }
250
+ }
251
+ const [param] = Array.isArray(params) ? params : [params];
252
+ if (ext.onRequest && toRun === "onRequest" /* onRequest */) {
253
+ const { ...previousContext } = ctx.get();
254
+ if (!_init) {
255
+ continue;
256
+ }
257
+ const previousHeaders = new Headers(previousContext.headers);
258
+ await ext.onRequest(_init.request, ctx);
259
+ const updatedContext = ctx.get();
260
+ if (updatedContext?.headers) {
261
+ const cookie = updatedContext.headers.get("cookie");
262
+ if (cookie && param.headers) {
263
+ const updatedCookies = mergeCookies(
264
+ previousHeaders?.get("cookie"),
265
+ updatedContext.headers.get("cookie")
266
+ );
267
+ param.headers.set("cookie", updatedCookies);
268
+ }
269
+ if (updatedContext.tenantId && param.headers) {
270
+ param.headers.set(
271
+ TENANT_COOKIE,
272
+ String(updatedContext.headers.get(TENANT_COOKIE))
273
+ );
274
+ }
275
+ ctx.set({ headers: param.headers });
276
+ }
277
+ debug(`${ext.id ?? create2.name} ran onRequest`);
278
+ }
279
+ if (ext.onResponse && toRun === "onResponse" /* onResponse */) {
280
+ const result = await ext.onResponse(param, ctx);
281
+ debug(`${ext.id ?? create2.name} ran onResponse`);
282
+ if (result != null) {
283
+ return result;
284
+ }
285
+ }
286
+ }
287
+ }
288
+ return void 0;
289
+ };
290
+ }
291
+ function buildExtensionConfig(instance) {
292
+ return {
293
+ runExtensions: bindRunExtensions(instance)
294
+ };
295
+ }
296
+ function mergeCookies(...cookieStrings) {
297
+ const cookieMap = /* @__PURE__ */ new Map();
298
+ for (const str of cookieStrings) {
299
+ if (!str) continue;
300
+ for (const part of str.split(";")) {
301
+ const [key17, value] = part.split("=").map((s) => s.trim());
302
+ if (key17 && value) cookieMap.set(key17, value);
303
+ }
304
+ }
305
+ return [...cookieMap.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
306
+ }
307
+ async function runExtensionContext(config) {
308
+ await config?.extensionCtx?.runExtensions("withContext" /* withContext */, config);
309
+ await config?.extensionCtx?.runExtensions(
310
+ "withTenantId" /* withTenantId */,
311
+ config
312
+ );
313
+ await config?.extensionCtx?.runExtensions("withUserId" /* withUserId */, config);
314
+ }
315
+
316
+ // src/api/utils/request-context.ts
317
+ var { warn, silly } = Logger({ debug: true })("[REQUEST CONTEXT]");
318
+ var storage = new async_hooks.AsyncLocalStorage();
319
+ var defaultContext = {
320
+ headers: new Headers(),
321
+ tenantId: void 0,
322
+ userId: void 0
323
+ };
324
+ var lastUsedContext = defaultContext;
325
+ var ctx = {
326
+ run(ctx2, fn) {
327
+ const merged = {
328
+ ...defaultContext,
329
+ ...ctx2,
330
+ headers: ctx2.headers instanceof Headers ? ctx2.headers : new Headers(ctx2.headers)
331
+ };
332
+ lastUsedContext = merged;
333
+ return storage.run(merged, fn);
334
+ },
335
+ get: () => {
336
+ const ctx2 = storage.getStore();
337
+ if (!ctx2) {
338
+ return { ...defaultContext };
339
+ }
340
+ silly(`[GET] ${serializeContext(ctx2)}`);
341
+ return ctx2;
342
+ },
343
+ /**
344
+ * This is a mirror of Server.getContext, but only for requests. We keep only the request
345
+ * information around, everything else is :above_my_pay_grade:
346
+ * @param partial A partial context to override
347
+ */
348
+ set: (partial) => {
349
+ const store = storage.getStore();
350
+ if (!store) {
351
+ warn("ctx.set() called outside of ctx.run(). This will not persist.");
352
+ return;
353
+ }
354
+ if (partial.headers === null) {
355
+ store.headers = new Headers();
356
+ } else if (partial.headers && store.headers instanceof Headers) {
357
+ for (const [key17, value] of new Headers(partial.headers).entries()) {
358
+ if (key17.toLowerCase() === "cookie") {
359
+ const existingCookies = parseCookieHeader(
360
+ store.headers.get("cookie") || ""
361
+ );
362
+ const newCookies = parseCookieHeader(value);
363
+ const mergedCookies = { ...existingCookies, ...newCookies };
364
+ store.headers.set("cookie", serializeCookies(mergedCookies));
365
+ } else {
366
+ store.headers.set(key17, value);
367
+ }
368
+ }
369
+ }
370
+ if ("tenantId" in partial) store.tenantId = partial.tenantId;
371
+ if ("userId" in partial) store.userId = partial.userId;
372
+ silly(`[SET] ${serializeContext(store)}`);
373
+ lastUsedContext = { ...store };
374
+ },
375
+ // for convenience only
376
+ getLastUsed: () => lastUsedContext
377
+ };
378
+ function withNileContext(config, fn, name = "unknown") {
379
+ const initialContext = config.context;
380
+ const existing = ctx.get();
381
+ const mergedHeaders = new Headers(existing.headers);
382
+ if (initialContext instanceof Request) {
383
+ initialContext.headers.forEach((value, key17) => {
384
+ mergedHeaders.set(key17, value);
385
+ });
386
+ const context2 = {
387
+ headers: mergedHeaders,
388
+ tenantId: existing.tenantId,
389
+ userId: existing.userId
390
+ };
391
+ silly(`${name} [INITIAL - Request] ${serializeContext(context2)}`);
392
+ return ctx.run(context2, fn);
393
+ }
394
+ if (initialContext.headers) {
395
+ const incoming = initialContext.headers instanceof Headers ? initialContext.headers : new Headers(initialContext.headers);
396
+ incoming.forEach((value, key17) => {
397
+ mergedHeaders.set(key17, value);
398
+ });
399
+ }
400
+ const hasTenantId = "tenantId" in initialContext;
401
+ const hasUserId = "userId" in initialContext;
402
+ const context = {
403
+ headers: mergedHeaders,
404
+ tenantId: hasTenantId ? initialContext.tenantId : existing.tenantId,
405
+ userId: hasUserId ? initialContext.userId : existing.userId
406
+ };
407
+ silly(`${name} [INITIAL - Partial<Context>] ${serializeContext(context)}`);
408
+ return ctx.run(context, async () => {
409
+ await runExtensionContext(config);
410
+ return fn();
411
+ });
412
+ }
413
+ function serializeContext(context) {
414
+ const headers = {};
415
+ const rawHeaders = new Headers(context.headers);
416
+ rawHeaders.forEach((value, key17) => {
417
+ headers[key17] = value;
418
+ });
419
+ return JSON.stringify({
420
+ headers,
421
+ tenantId: context.tenantId,
422
+ userId: context.userId
423
+ });
424
+ }
425
+ function parseCookieHeader(header) {
426
+ return header.split(";").map((c) => c.trim()).filter(Boolean).reduce((acc, curr) => {
427
+ const [key17, ...val] = curr.split("=");
428
+ if (key17) acc[key17] = val.join("=");
429
+ return acc;
430
+ }, {});
431
+ }
432
+ function serializeCookies(cookies) {
433
+ return Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join("; ");
434
+ }
435
+
129
436
  // src/api/utils/request.ts
130
437
  async function request(url, _init, config) {
131
- const { debug, info, error } = config.logger("[REQUEST]");
438
+ const { debug, info, error, warn: warn2 } = config.logger("[REQUEST]");
132
439
  const { request: request2, ...init } = _init;
133
440
  const requestUrl = new URL(request2.url);
134
441
  const updatedHeaders = new Headers({});
@@ -162,9 +469,17 @@ async function request(url, _init, config) {
162
469
  if (passedOrigin) {
163
470
  updatedHeaders.set(HEADER_ORIGIN, passedOrigin);
164
471
  } else {
165
- const reqOrigin = config.routePrefix !== DEFAULT_PREFIX ? `${requestUrl.origin}${config.routePrefix}` : requestUrl.origin;
166
- updatedHeaders.set(HEADER_ORIGIN, reqOrigin);
167
- debug(`Obtained origin from request ${reqOrigin}`);
472
+ const { headers } = ctx.get();
473
+ const host = headers.get("host");
474
+ if (host) {
475
+ const serverSideOrigin = `${getProtocolFromHeaders(headers)}://${host}`;
476
+ updatedHeaders.set(HEADER_ORIGIN, serverSideOrigin);
477
+ debug(`Obtained origin from server side headers ${serverSideOrigin}`);
478
+ } else {
479
+ const reqOrigin = config.routePrefix !== DEFAULT_PREFIX ? `${requestUrl.origin}${config.routePrefix}` : requestUrl.origin;
480
+ updatedHeaders.set(HEADER_ORIGIN, reqOrigin);
481
+ debug(`Obtained origin from request ${reqOrigin}`);
482
+ }
168
483
  }
169
484
  }
170
485
  const params = { ...init };
@@ -189,7 +504,12 @@ async function request(url, _init, config) {
189
504
  params.headers.set("request-id", crypto.randomUUID());
190
505
  params.cache = "no-store";
191
506
  }
192
- await config.extensionCtx?.handleOnRequest(config, _init, params);
507
+ await config.extensionCtx?.runExtensions(
508
+ "onRequest" /* onRequest */,
509
+ config,
510
+ params,
511
+ _init
512
+ );
193
513
  try {
194
514
  const res = await fetch(fullUrl, {
195
515
  ...params
@@ -209,6 +529,14 @@ async function request(url, _init, config) {
209
529
  statusText: res?.statusText,
210
530
  text: await loggingRes?.text()
211
531
  });
532
+ const updatedRes = await config.extensionCtx?.runExtensions(
533
+ "onResponse" /* onResponse */,
534
+ config,
535
+ { ...params, response: res }
536
+ );
537
+ if (updatedRes) {
538
+ return updatedRes;
539
+ }
212
540
  return res;
213
541
  } catch (e) {
214
542
  if (e instanceof Error) {
@@ -223,6 +551,19 @@ async function request(url, _init, config) {
223
551
  );
224
552
  }
225
553
  }
554
+ function getProtocolFromHeaders(headers) {
555
+ const get = (key17) => headers instanceof Headers ? headers.get(key17) : headers[key17.toLowerCase()];
556
+ const xfp = get("x-forwarded-proto");
557
+ if (xfp) return xfp.toLowerCase();
558
+ const forwarded = get("forwarded");
559
+ if (forwarded) {
560
+ const match = forwarded.match(/proto=(https?)/i);
561
+ if (match) return match[1].toLowerCase();
562
+ }
563
+ const ref = get("referer") || get("origin");
564
+ if (ref && ref.startsWith("https")) return "https";
565
+ return "http";
566
+ }
226
567
 
227
568
  // src/api/utils/auth.ts
228
569
  async function auth(req, config) {
@@ -232,16 +573,13 @@ async function auth(req, config) {
232
573
  info(`using session ${sessionUrl}`);
233
574
  req.headers.delete("content-length");
234
575
  const res = await request(sessionUrl, { request: req }, config);
235
- if (!res) {
236
- info("no session found");
237
- return void 0;
238
- }
239
- info("session active");
240
576
  try {
241
577
  const session = await new Response(res.body).json();
242
578
  if (Object.keys(session).length === 0) {
579
+ info("no session found");
243
580
  return void 0;
244
581
  }
582
+ info("session active");
245
583
  return session;
246
584
  } catch (e) {
247
585
  error(e);
@@ -252,7 +590,7 @@ async function auth(req, config) {
252
590
  // src/api/routes/me/index.ts
253
591
  var key = "ME";
254
592
  async function route(request2, config) {
255
- const url = apiRoutes(config)[key];
593
+ const url = apiRoutes(config.apiUrl)[key];
256
594
  if (request2.method === "GET") {
257
595
  return await GET(url, { request: request2 }, config);
258
596
  }
@@ -273,8 +611,9 @@ function matches(configRoutes, request2) {
273
611
  }
274
612
  async function fetchMe(config, method, body) {
275
613
  const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/me" /* ME */}`;
614
+ const { headers } = ctx.get();
276
615
  const init = {
277
- headers: config.headers,
616
+ headers,
278
617
  method: method ?? "GET"
279
618
  };
280
619
  if (method === "PUT") {
@@ -326,9 +665,9 @@ function getTokenFromCookie(headers, cookieKey) {
326
665
  return _cookies[cookieKey];
327
666
  }
328
667
  }
329
- function getTenantFromHttp(headers, config) {
668
+ function getTenantFromHttp(headers, context) {
330
669
  const cookieTenant = getTokenFromCookie(headers, TENANT_COOKIE);
331
- return cookieTenant ? cookieTenant : config?.tenantId;
670
+ return cookieTenant ? cookieTenant : context?.tenantId;
332
671
  }
333
672
 
334
673
  // src/api/routes/users/POST.ts
@@ -338,8 +677,11 @@ async function POST(config, init) {
338
677
  const yurl = new URL(init.request.url);
339
678
  const tenantId = yurl.searchParams.get("tenantId");
340
679
  const newTenantName = yurl.searchParams.get("newTenantName");
341
- const tenant = tenantId ?? getTenantFromHttp(init.request.headers, config);
342
- const url = apiRoutes(config).USERS({ tenantId: tenant, newTenantName });
680
+ const tenant = tenantId ?? getTenantFromHttp(init.request.headers);
681
+ const url = apiRoutes(config.apiUrl).USERS({
682
+ tenantId: tenant,
683
+ newTenantName
684
+ });
343
685
  return await request(url, init, config);
344
686
  }
345
687
 
@@ -347,13 +689,13 @@ async function POST(config, init) {
347
689
  async function GET2(config, init, log) {
348
690
  const yurl = new URL(init.request.url);
349
691
  const tenantId = yurl.searchParams.get("tenantId");
350
- const tenant = tenantId ?? getTenantFromHttp(init.request.headers, config);
692
+ const tenant = tenantId ?? getTenantFromHttp(init.request.headers, config.context);
351
693
  if (!tenant) {
352
694
  log("[GET] No tenant id provided.");
353
695
  return new Response(null, { status: 404 });
354
696
  }
355
697
  init.method = "GET";
356
- const url = apiRoutes(config).TENANT_USERS(tenant);
698
+ const url = apiRoutes(config.apiUrl).TENANT_USERS(tenant);
357
699
  return await request(url, init, config);
358
700
  }
359
701
 
@@ -362,7 +704,7 @@ async function PUT2(config, init) {
362
704
  init.body = init.request.body;
363
705
  init.method = "PUT";
364
706
  const [userId] = new URL(init.request.url).pathname.split("/").reverse();
365
- const url = apiRoutes(config).USER(userId);
707
+ const url = apiRoutes(config.apiUrl).USER(userId);
366
708
  return await request(url, init, config);
367
709
  }
368
710
 
@@ -389,7 +731,7 @@ function matches2(configRoutes, request2) {
389
731
  async function GET3(config, init) {
390
732
  const yurl = new URL(init.request.url);
391
733
  const [, tenantId] = yurl.pathname.split("/").reverse();
392
- const url = `${apiRoutes(config).TENANT_USERS(tenantId)}`;
734
+ const url = `${apiRoutes(config.apiUrl).TENANT_USERS(tenantId)}`;
393
735
  return await request(url, init, config);
394
736
  }
395
737
 
@@ -403,7 +745,7 @@ async function POST2(config, init) {
403
745
  const [, tenantId] = yurl.pathname.split("/").reverse();
404
746
  init.body = JSON.stringify({ email: session.email });
405
747
  init.method = "POST";
406
- const url = apiRoutes(config).TENANT_USERS(tenantId);
748
+ const url = apiRoutes(config.apiUrl).TENANT_USERS(tenantId);
407
749
  return await request(url, init, config);
408
750
  }
409
751
 
@@ -437,12 +779,13 @@ function matches3(configRoutes, request2) {
437
779
  }
438
780
  async function fetchTenantUsers(config, method, payload) {
439
781
  const { body, params } = {};
440
- if (!config.tenantId) {
782
+ const { tenantId, headers } = ctx.get();
783
+ if (!tenantId) {
441
784
  throw new Error(
442
785
  "Unable to fetch the user's tenants, the tenantId context is missing. Call nile.setContext({ tenantId })"
443
786
  );
444
787
  }
445
- if (!isUUID(config.tenantId)) {
788
+ if (!isUUID(tenantId)) {
446
789
  config.logger("fetchTenantUsers").warn(
447
790
  "nile.tenantId is not a valid UUID. This may lead to unexpected behavior in your application."
448
791
  );
@@ -454,14 +797,11 @@ async function fetchTenantUsers(config, method, payload) {
454
797
  if (params?.tenantId) {
455
798
  q.set("tenantId", params.tenantId);
456
799
  }
457
- const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/tenants/{tenantId}/users" /* TENANT_USERS */.replace(
458
- "{tenantId}",
459
- config.tenantId
460
- )}`;
800
+ const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/tenants/{tenantId}/users" /* TENANT_USERS */.replace("{tenantId}", tenantId)}`;
461
801
  const m = method;
462
802
  const init = {
463
803
  method: m,
464
- headers: config.headers
804
+ headers
465
805
  };
466
806
  const req = new Request(clientUrl, init);
467
807
  return await config.handlers[m](req);
@@ -478,9 +818,9 @@ async function PUT3(config, init) {
478
818
  init.body = new URLSearchParams(yurl.searchParams).toString();
479
819
  }
480
820
  init.method = "PUT";
481
- const url = `${apiRoutes(config).INVITE(tenantId)}`;
821
+ const url = `${apiRoutes(config.apiUrl).INVITE(tenantId)}`;
482
822
  const res = await request(url, init, config);
483
- const location = res?.headers.get("location");
823
+ const location = res?.headers?.get("location");
484
824
  if (location) {
485
825
  return new Response(res?.body, {
486
826
  status: 302,
@@ -502,7 +842,7 @@ async function POST3(config, init) {
502
842
  }
503
843
  init.method = "POST";
504
844
  init.body = init.request.body;
505
- const url = `${apiRoutes(config).INVITE(tenantId)}`;
845
+ const url = `${apiRoutes(config.apiUrl).INVITE(tenantId)}`;
506
846
  return await request(url, init, config);
507
847
  }
508
848
 
@@ -528,21 +868,22 @@ function matches4(configRoutes, request2) {
528
868
  return urlMatches(request2.url, route20);
529
869
  }
530
870
  async function fetchInvite(config, method, body) {
531
- if (!config.tenantId) {
871
+ const { headers, tenantId } = ctx.get();
872
+ if (!tenantId) {
532
873
  throw new Error(
533
874
  "Unable to fetch the invite for the tenant, the tenantId context is missing. Call nile.setContext({ tenantId })"
534
875
  );
535
876
  }
536
- if (!isUUID(config.tenantId)) {
877
+ if (!isUUID(tenantId)) {
537
878
  config.logger("fetchInvite").warn(
538
879
  "nile.tenantId is not a valid UUID. This may lead to unexpected behavior in your application."
539
880
  );
540
881
  }
541
- let clientUrl = `${config.serverOrigin}${config.routePrefix}${"/tenants/{tenantId}/invite" /* INVITE */.replace("{tenantId}", config.tenantId)}`;
882
+ let clientUrl = `${config.serverOrigin}${config.routePrefix}${"/tenants/{tenantId}/invite" /* INVITE */.replace("{tenantId}", tenantId)}`;
542
883
  const m = method ?? "GET";
543
884
  const init = {
544
885
  method: m,
545
- headers: config.headers
886
+ headers
546
887
  };
547
888
  if (method === "POST" || method === "PUT") {
548
889
  init.body = body;
@@ -562,7 +903,7 @@ async function GET4(config, init) {
562
903
  return new Response(null, { status: 404 });
563
904
  }
564
905
  init.method = "GET";
565
- const url = `${apiRoutes(config).INVITES(tenantId)}`;
906
+ const url = `${apiRoutes(config.apiUrl).INVITES(tenantId)}`;
566
907
  return await request(url, init, config);
567
908
  }
568
909
 
@@ -583,29 +924,29 @@ function matches5(configRoutes, request2) {
583
924
  return url.pathname.endsWith(route20);
584
925
  }
585
926
  async function fetchInvites(config) {
586
- if (!config.tenantId) {
927
+ const { tenantId, headers } = ctx.get();
928
+ if (!tenantId) {
587
929
  throw new Error(
588
930
  "Unable to fetch invites for the tenant, the tenantId context is missing. Call nile.setContext({ tenantId })"
589
931
  );
590
932
  }
591
- if (!isUUID(config.tenantId)) {
933
+ if (!isUUID(tenantId)) {
592
934
  config.logger("fetchInvites").warn(
593
935
  "nile.tenantId is not a valid UUID. This may lead to unexpected behavior in your application."
594
936
  );
595
937
  }
596
- const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/tenants/{tenantId}/invites" /* INVITES */.replace("{tenantId}", config.tenantId)}`;
597
- const req = new Request(clientUrl, { headers: config.headers });
938
+ const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/tenants/{tenantId}/invites" /* INVITES */.replace("{tenantId}", tenantId)}`;
939
+ const req = new Request(clientUrl, { headers });
598
940
  return await config.handlers.GET(req);
599
941
  }
600
942
 
601
943
  // src/api/routes/tenants/GET.ts
602
944
  async function GET5(config, session, init) {
603
- let url = `${apiRoutes(config).USER_TENANTS(session.id)}`;
945
+ let url = `${apiRoutes(config.apiUrl).USER_TENANTS(session.id)}`;
604
946
  if (typeof session === "object" && "user" in session && session.user) {
605
- url = `${apiRoutes(config).USER_TENANTS(session.user.id)}`;
947
+ url = `${apiRoutes(config.apiUrl).USER_TENANTS(session.user.id)}`;
606
948
  }
607
- const res = await request(url, init, config);
608
- return res;
949
+ return await request(url, init, config);
609
950
  }
610
951
 
611
952
  // src/api/routes/tenants/[tenantId]/GET.ts
@@ -617,7 +958,7 @@ async function GET6(config, init, log) {
617
958
  return new Response(null, { status: 404 });
618
959
  }
619
960
  init.method = "GET";
620
- const url = `${apiRoutes(config).TENANT(tenantId)}`;
961
+ const url = `${apiRoutes(config.apiUrl).TENANT(tenantId)}`;
621
962
  return await request(url, init, config);
622
963
  }
623
964
 
@@ -629,7 +970,7 @@ async function DELETE2(config, init) {
629
970
  return new Response(null, { status: 404 });
630
971
  }
631
972
  init.method = "DELETE";
632
- const url = `${apiRoutes(config).TENANT(tenantId)}`;
973
+ const url = `${apiRoutes(config.apiUrl).TENANT(tenantId)}`;
633
974
  return await request(url, init, config);
634
975
  }
635
976
 
@@ -642,7 +983,7 @@ async function PUT4(config, init) {
642
983
  }
643
984
  init.body = init.request.body;
644
985
  init.method = "PUT";
645
- const url = `${apiRoutes(config).TENANT(tenantId)}`;
986
+ const url = `${apiRoutes(config.apiUrl).TENANT(tenantId)}`;
646
987
  return await request(url, init, config);
647
988
  }
648
989
 
@@ -650,7 +991,7 @@ async function PUT4(config, init) {
650
991
  async function POST4(config, init) {
651
992
  init.body = init.request.body;
652
993
  init.method = "POST";
653
- const url = `${apiRoutes(config).TENANTS}`;
994
+ const url = `${apiRoutes(config.apiUrl).TENANTS}`;
654
995
  return await request(url, init, config);
655
996
  }
656
997
 
@@ -684,10 +1025,11 @@ function matches6(configRoutes, request2) {
684
1025
  return urlMatches(request2.url, configRoutes[key6]);
685
1026
  }
686
1027
  async function fetchTenants(config, method, body) {
1028
+ const { headers } = ctx.get();
687
1029
  const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/tenants" /* TENANTS */}`;
688
1030
  const init = {
689
1031
  method,
690
- headers: config.headers
1032
+ headers
691
1033
  };
692
1034
  {
693
1035
  init.body = body;
@@ -696,21 +1038,22 @@ async function fetchTenants(config, method, body) {
696
1038
  return await config.handlers.POST(req);
697
1039
  }
698
1040
  async function fetchTenant(config, method, body) {
699
- if (!config.tenantId) {
1041
+ const { headers, tenantId } = ctx.get();
1042
+ if (!tenantId) {
700
1043
  throw new Error(
701
1044
  "Unable to fetch tenants, the tenantId context is missing. Call nile.setContext({ tenantId })"
702
1045
  );
703
1046
  }
704
- if (!isUUID(config.tenantId)) {
1047
+ if (!isUUID(tenantId)) {
705
1048
  config.logger("fetch tenant").warn(
706
1049
  "nile.tenantId is not a valid UUID. This may lead to unexpected behavior in your application."
707
1050
  );
708
1051
  }
709
- const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/tenants/{tenantId}" /* TENANT */.replace("{tenantId}", config.tenantId)}`;
1052
+ const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/tenants/{tenantId}" /* TENANT */.replace("{tenantId}", tenantId)}`;
710
1053
  const m = method ?? "GET";
711
1054
  const init = {
712
1055
  method: m,
713
- headers: config.headers
1056
+ headers
714
1057
  };
715
1058
  if (m === "PUT") {
716
1059
  init.body = body;
@@ -719,32 +1062,33 @@ async function fetchTenant(config, method, body) {
719
1062
  return await config.handlers[m](req);
720
1063
  }
721
1064
  async function fetchTenantsByUser(config) {
722
- const { warn } = config.logger("fetchTenantsByUser");
723
- if (!config.userId) {
724
- warn(
1065
+ const { warn: warn2 } = config.logger(" fetchTenantsByUser ");
1066
+ const { userId, headers } = ctx.get();
1067
+ if (!userId) {
1068
+ warn2(
725
1069
  "nile.userId is not set. The call will still work for the API, but the database context is not set properly and may lead to unexpected behavior in your application."
726
1070
  );
727
- } else if (!isUUID(config.userId)) {
728
- warn(
1071
+ } else if (!isUUID(userId)) {
1072
+ warn2(
729
1073
  "nile.userId is not a valid UUID. This may lead to unexpected behavior in your application."
730
1074
  );
731
1075
  }
732
1076
  const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/tenants" /* TENANTS */}`;
733
- const req = new Request(clientUrl, { headers: config.headers });
1077
+ const req = new Request(clientUrl, { headers });
734
1078
  return await config.handlers.GET(req);
735
1079
  }
736
1080
 
737
1081
  // src/api/routes/auth/signin.ts
738
1082
  var key7 = "SIGNIN";
739
1083
  async function route7(req, config) {
740
- let url = proxyRoutes(config)[key7];
1084
+ let url = proxyRoutes(config.apiUrl)[key7];
741
1085
  const init = {
742
1086
  method: req.method,
743
1087
  headers: req.headers
744
1088
  };
745
1089
  if (req.method === "POST") {
746
1090
  const [provider] = new URL(req.url).pathname.split("/").reverse();
747
- url = `${proxyRoutes(config)[key7]}/${provider}`;
1091
+ url = `${proxyRoutes(config.apiUrl)[key7]}/${provider}`;
748
1092
  }
749
1093
  const passThroughUrl = new URL(req.url);
750
1094
  const params = new URLSearchParams(passThroughUrl.search);
@@ -757,10 +1101,11 @@ function matches7(configRoutes, request2) {
757
1101
  }
758
1102
  async function fetchSignIn(config, provider, body) {
759
1103
  const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/auth/signin" /* SIGNIN */}/${provider}`;
1104
+ const { headers } = ctx.get();
760
1105
  const req = new Request(clientUrl, {
761
1106
  method: "POST",
762
- headers: config.headers,
763
- body
1107
+ body,
1108
+ headers
764
1109
  });
765
1110
  return await config.handlers.POST(req);
766
1111
  }
@@ -768,7 +1113,7 @@ async function fetchSignIn(config, provider, body) {
768
1113
  // src/api/routes/auth/session.ts
769
1114
  async function route8(req, config) {
770
1115
  return request(
771
- proxyRoutes(config).SESSION,
1116
+ proxyRoutes(config.apiUrl).SESSION,
772
1117
  {
773
1118
  method: req.method,
774
1119
  request: req
@@ -781,9 +1126,10 @@ function matches8(configRoutes, request2) {
781
1126
  }
782
1127
  async function fetchSession(config) {
783
1128
  const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/auth/session" /* SESSION */}`;
1129
+ const { headers } = ctx.get();
784
1130
  const req = new Request(clientUrl, {
785
1131
  method: "GET",
786
- headers: config.headers
1132
+ headers
787
1133
  });
788
1134
  return await config.handlers.GET(req);
789
1135
  }
@@ -791,7 +1137,7 @@ async function fetchSession(config) {
791
1137
  // src/api/routes/auth/providers.ts
792
1138
  async function route9(req, config) {
793
1139
  return request(
794
- proxyRoutes(config).PROVIDERS,
1140
+ proxyRoutes(config.apiUrl).PROVIDERS,
795
1141
  {
796
1142
  method: req.method,
797
1143
  request: req
@@ -804,9 +1150,10 @@ function matches9(configRoutes, request2) {
804
1150
  }
805
1151
  async function fetchProviders(config) {
806
1152
  const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/auth/providers" /* PROVIDERS */}`;
1153
+ const { headers } = ctx.get();
807
1154
  const req = new Request(clientUrl, {
808
1155
  method: "GET",
809
- headers: config.headers
1156
+ headers
810
1157
  });
811
1158
  return await config.handlers.GET(req);
812
1159
  }
@@ -814,7 +1161,7 @@ async function fetchProviders(config) {
814
1161
  // src/api/routes/auth/csrf.ts
815
1162
  async function route10(req, config) {
816
1163
  return request(
817
- proxyRoutes(config).CSRF,
1164
+ proxyRoutes(config.apiUrl).CSRF,
818
1165
  {
819
1166
  method: req.method,
820
1167
  request: req
@@ -827,9 +1174,10 @@ function matches10(configRoutes, request2) {
827
1174
  }
828
1175
  async function fetchCsrf(config) {
829
1176
  const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/auth/csrf" /* CSRF */}`;
1177
+ const { headers } = ctx.get();
830
1178
  const req = new Request(clientUrl, {
831
1179
  method: "GET",
832
- headers: config.headers
1180
+ headers
833
1181
  });
834
1182
  return await config.handlers.GET(req);
835
1183
  }
@@ -842,7 +1190,7 @@ async function route11(req, config) {
842
1190
  try {
843
1191
  const passThroughUrl = new URL(req.url);
844
1192
  const params = new URLSearchParams(passThroughUrl.search);
845
- const url = `${proxyRoutes(config)[key8]}/${provider}${params.toString() !== "" ? `?${params.toString()}` : ""}`;
1193
+ const url = `${proxyRoutes(config.apiUrl)[key8]}/${provider}${params.toString() !== "" ? `?${params.toString()}` : ""}`;
846
1194
  const res = await request(
847
1195
  url,
848
1196
  {
@@ -853,7 +1201,7 @@ async function route11(req, config) {
853
1201
  ).catch((e) => {
854
1202
  error("an error as occurred", e);
855
1203
  });
856
- const location = res?.headers.get("location");
1204
+ const location = res?.headers?.get("location");
857
1205
  if (location) {
858
1206
  return new Response(res?.body, {
859
1207
  status: 302,
@@ -873,10 +1221,11 @@ function matches11(configRoutes, request2) {
873
1221
  return urlMatches(request2.url, configRoutes.CALLBACK);
874
1222
  }
875
1223
  async function fetchCallback(config, provider, body, request2, method = "POST") {
1224
+ const { headers } = ctx.get();
876
1225
  const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/auth/callback" /* CALLBACK */}/${provider}${request2 ? `?${new URL(request2.url).searchParams}` : ""}`;
877
1226
  const req = new Request(clientUrl, {
878
1227
  method,
879
- headers: config.headers,
1228
+ headers,
880
1229
  body
881
1230
  });
882
1231
  return await config.handlers.POST(req);
@@ -885,14 +1234,14 @@ async function fetchCallback(config, provider, body, request2, method = "POST")
885
1234
  // src/api/routes/auth/signout.ts
886
1235
  var key9 = "SIGNOUT";
887
1236
  async function route12(request2, config) {
888
- let url = proxyRoutes(config)[key9];
1237
+ let url = proxyRoutes(config.apiUrl)[key9];
889
1238
  const init = {
890
1239
  method: request2.method
891
1240
  };
892
1241
  if (request2.method === "POST") {
893
1242
  init.body = request2.body;
894
1243
  const [provider] = new URL(request2.url).pathname.split("/").reverse();
895
- url = `${proxyRoutes(config)[key9]}${provider !== "signout" ? `/${provider}` : ""}`;
1244
+ url = `${proxyRoutes(config.apiUrl)[key9]}${provider !== "signout" ? `/${provider}` : ""}`;
896
1245
  }
897
1246
  const res = await request(url, { ...init, request: request2 }, config);
898
1247
  return res;
@@ -902,10 +1251,11 @@ function matches12(configRoutes, request2) {
902
1251
  }
903
1252
  async function fetchSignOut(config, body) {
904
1253
  const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/auth/signout" /* SIGNOUT */}`;
1254
+ const { headers } = ctx.get();
905
1255
  const req = new Request(clientUrl, {
906
1256
  method: "POST",
907
1257
  body,
908
- headers: config.headers
1258
+ headers
909
1259
  });
910
1260
  return await config.handlers.POST(req);
911
1261
  }
@@ -914,7 +1264,7 @@ async function fetchSignOut(config, body) {
914
1264
  var key10 = "ERROR";
915
1265
  async function route13(req, config) {
916
1266
  return request(
917
- proxyRoutes(config)[key10],
1267
+ proxyRoutes(config.apiUrl)[key10],
918
1268
  {
919
1269
  method: req.method,
920
1270
  request: req
@@ -930,7 +1280,7 @@ function matches13(configRoutes, request2) {
930
1280
  var key11 = "VERIFY_REQUEST";
931
1281
  async function route14(req, config) {
932
1282
  return request(
933
- proxyRoutes(config)[key11],
1283
+ proxyRoutes(config.apiUrl)[key11],
934
1284
  {
935
1285
  method: req.method,
936
1286
  request: req
@@ -945,7 +1295,7 @@ function matches14(configRoutes, request2) {
945
1295
  // src/api/routes/auth/password-reset.ts
946
1296
  var key12 = "PASSWORD_RESET";
947
1297
  async function route15(req, config) {
948
- const url = proxyRoutes(config)[key12];
1298
+ const url = proxyRoutes(config.apiUrl)[key12];
949
1299
  const res = await request(
950
1300
  url,
951
1301
  {
@@ -954,7 +1304,7 @@ async function route15(req, config) {
954
1304
  },
955
1305
  config
956
1306
  );
957
- const location = res?.headers.get("location");
1307
+ const location = res?.headers?.get("location");
958
1308
  if (location) {
959
1309
  return new Response(res?.body, {
960
1310
  status: 302,
@@ -974,10 +1324,11 @@ async function fetchResetPassword(config, method, body, params, useJson = true)
974
1324
  if (useJson) {
975
1325
  authParams?.set("json", "true");
976
1326
  }
1327
+ const { headers } = ctx.get();
977
1328
  const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/auth/reset-password" /* PASSWORD_RESET */}?${authParams?.toString()}`;
978
1329
  const init = {
979
1330
  method,
980
- headers: config.headers
1331
+ headers
981
1332
  };
982
1333
  if (body && method !== "GET") {
983
1334
  init.body = body;
@@ -989,7 +1340,7 @@ async function fetchResetPassword(config, method, body, params, useJson = true)
989
1340
  // src/api/routes/auth/verify-email.ts
990
1341
  var key13 = "VERIFY_EMAIL";
991
1342
  async function route16(req, config) {
992
- const url = proxyRoutes(config)[key13];
1343
+ const url = proxyRoutes(config.apiUrl)[key13];
993
1344
  const res = await request(
994
1345
  url,
995
1346
  {
@@ -998,7 +1349,7 @@ async function route16(req, config) {
998
1349
  },
999
1350
  config
1000
1351
  );
1001
- const location = res?.headers.get("location");
1352
+ const location = res?.headers?.get("location");
1002
1353
  if (location) {
1003
1354
  return new Response(res?.body, {
1004
1355
  status: 302,
@@ -1015,9 +1366,10 @@ function matches16(configRoutes, request2) {
1015
1366
  }
1016
1367
  async function fetchVerifyEmail(config, method, body) {
1017
1368
  const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/auth/verify-email" /* VERIFY_EMAIL */}`;
1369
+ const { headers } = ctx.get();
1018
1370
  const init = {
1019
1371
  method,
1020
- headers: config.headers
1372
+ headers
1021
1373
  };
1022
1374
  if (body) {
1023
1375
  init.body = body;
@@ -1028,8 +1380,21 @@ async function fetchVerifyEmail(config, method, body) {
1028
1380
 
1029
1381
  // src/api/handlers/GET.ts
1030
1382
  function GETTER(configRoutes, config) {
1031
- const { info, warn } = config.logger("[GET MATCHER]");
1032
- return async function GET7(req) {
1383
+ const { error, info, warn: warn2 } = config.logger("[GET MATCHER]");
1384
+ return async function GET7(...params) {
1385
+ const handledRequest = await config.extensionCtx?.runExtensions(
1386
+ "onHandleRequest" /* onHandleRequest */,
1387
+ config,
1388
+ params
1389
+ );
1390
+ if (handledRequest) {
1391
+ return handledRequest;
1392
+ }
1393
+ const req = params[0] instanceof Request ? params[0] : null;
1394
+ if (!req) {
1395
+ error("Proxy requests failed, a Request object was not passed.");
1396
+ return;
1397
+ }
1033
1398
  if (matches(configRoutes, req)) {
1034
1399
  info("matches me");
1035
1400
  return route(req, config);
@@ -1094,78 +1459,16 @@ function GETTER(configRoutes, config) {
1094
1459
  info("matches error");
1095
1460
  return route13(req, config);
1096
1461
  }
1097
- warn(`No GET routes matched ${req.url}`);
1462
+ warn2(`No GET routes matched ${req.url}`);
1098
1463
  return new Response(null, { status: 404 });
1099
1464
  };
1100
1465
  }
1101
1466
 
1102
- // src/utils/Logger.ts
1103
- var red = "\x1B[31m";
1104
- var yellow = "\x1B[38;2;255;255;0m";
1105
- var purple = "\x1B[38;2;200;160;255m";
1106
- var orange = "\x1B[38;2;255;165;0m";
1107
- var reset = "\x1B[0m";
1108
- var baseLogger = (config, ...params) => ({
1109
- info(message, meta) {
1110
- if (config?.debug) {
1111
- console.info(
1112
- `${orange}[niledb]${reset}${purple}[DEBUG]${reset}${params.join(
1113
- ""
1114
- )}${reset} ${message}`,
1115
- meta ? `${JSON.stringify(meta)}` : ""
1116
- );
1117
- }
1118
- },
1119
- debug(message, meta) {
1120
- if (config?.debug) {
1121
- console.debug(
1122
- `${orange}[niledb]${reset}${purple}[DEBUG]${reset}${params.join(
1123
- ""
1124
- )}${reset} ${message}`,
1125
- meta ? `${JSON.stringify(meta)}` : ""
1126
- );
1127
- }
1128
- },
1129
- warn(message, meta) {
1130
- if (config?.debug) {
1131
- console.warn(
1132
- `${orange}[niledb]${reset}${yellow}[WARN]${reset}${params.join(
1133
- ""
1134
- )}${reset} ${message}`,
1135
- meta ? JSON.stringify(meta) : ""
1136
- );
1137
- }
1138
- },
1139
- error(message, meta) {
1140
- console.error(
1141
- `${orange}[niledb]${reset}${red}[ERROR]${reset}${params.join(
1142
- ""
1143
- )}${red} ${message}`,
1144
- meta ? meta : "",
1145
- `${reset}`
1146
- );
1147
- }
1148
- });
1149
- function Logger(config) {
1150
- return (prefixes) => {
1151
- const { info, debug, warn, error } = config && typeof config?.logger === "function" ? config.logger(prefixes) : baseLogger(config, prefixes);
1152
- return {
1153
- info,
1154
- debug,
1155
- warn,
1156
- error
1157
- };
1158
- };
1159
- }
1160
- function matchesLog(configRoutes, request2) {
1161
- return urlMatches(request2.url, configRoutes.LOG);
1162
- }
1163
-
1164
1467
  // src/api/routes/signup/POST.ts
1165
1468
  async function POST5(config, init) {
1166
1469
  init.body = init.request.body;
1167
1470
  init.method = "POST";
1168
- const url = `${apiRoutes(config).SIGNUP}`;
1471
+ const url = `${apiRoutes(config.apiUrl).SIGNUP}`;
1169
1472
  return await request(url, init, config);
1170
1473
  }
1171
1474
 
@@ -1192,9 +1495,10 @@ async function fetchSignUp(config, payload) {
1192
1495
  q.set("tenantId", params.tenantId);
1193
1496
  }
1194
1497
  const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/signup" /* SIGNUP */}${q.size > 0 ? `?${q}` : ""}`;
1498
+ const { headers } = ctx.get();
1195
1499
  const req = new Request(clientUrl, {
1196
1500
  method: "POST",
1197
- headers: config.headers,
1501
+ headers,
1198
1502
  body
1199
1503
  });
1200
1504
  return await config.handlers.POST(req);
@@ -1202,8 +1506,21 @@ async function fetchSignUp(config, payload) {
1202
1506
 
1203
1507
  // src/api/handlers/POST.ts
1204
1508
  function POSTER(configRoutes, config) {
1205
- const { info, warn, error } = config.logger("[POST MATCHER]");
1206
- return async function POST6(req) {
1509
+ const { info, warn: warn2, error } = config.logger("[POST MATCHER]");
1510
+ return async function POST6(...params) {
1511
+ const handledRequest = await config.extensionCtx?.runExtensions(
1512
+ "onHandleRequest" /* onHandleRequest */,
1513
+ config,
1514
+ params
1515
+ );
1516
+ if (handledRequest) {
1517
+ return handledRequest;
1518
+ }
1519
+ const req = params[0] instanceof Request ? params[0] : null;
1520
+ if (!req) {
1521
+ error("Proxy requests failed, a Request object was not passed.");
1522
+ return;
1523
+ }
1207
1524
  if (matchesLog(configRoutes, req)) {
1208
1525
  try {
1209
1526
  const json = await req.clone().json();
@@ -1225,10 +1542,6 @@ function POSTER(configRoutes, config) {
1225
1542
  info("matches signup");
1226
1543
  return route17(req, config);
1227
1544
  }
1228
- if (matches2(configRoutes, req)) {
1229
- info("matches users");
1230
- return route2(req, config);
1231
- }
1232
1545
  if (matches6(configRoutes, req)) {
1233
1546
  info("matches tenants");
1234
1547
  return route6(req, config);
@@ -1265,7 +1578,7 @@ function POSTER(configRoutes, config) {
1265
1578
  info("matches verify-email");
1266
1579
  return route16(req, config);
1267
1580
  }
1268
- warn(`No POST routes matched ${req.url}`);
1581
+ warn2(`No POST routes matched ${req.url}`);
1269
1582
  return new Response(null, { status: 404 });
1270
1583
  };
1271
1584
  }
@@ -1274,10 +1587,8 @@ function POSTER(configRoutes, config) {
1274
1587
  async function DELETE3(config, init) {
1275
1588
  const yurl = new URL(init.request.url);
1276
1589
  const [, userId, , tenantId] = yurl.pathname.split("/").reverse();
1277
- config.tenantId = tenantId;
1278
- config.userId = userId;
1279
1590
  init.method = "DELETE";
1280
- const url = `${apiRoutes(config).TENANT_USER}/link`;
1591
+ const url = `${apiRoutes(config.apiUrl).TENANT_USER(tenantId, userId)}/link`;
1281
1592
  return await request(url, init, config);
1282
1593
  }
1283
1594
 
@@ -1285,10 +1596,8 @@ async function DELETE3(config, init) {
1285
1596
  async function PUT5(config, init) {
1286
1597
  const yurl = new URL(init.request.url);
1287
1598
  const [, userId, , tenantId] = yurl.pathname.split("/").reverse();
1288
- config.tenantId = tenantId;
1289
- config.userId = userId;
1290
1599
  init.method = "PUT";
1291
- const url = `${apiRoutes(config).TENANT_USER}/link`;
1600
+ const url = `${apiRoutes(config.apiUrl).TENANT_USER(tenantId, userId)}/link`;
1292
1601
  return await request(url, init, config);
1293
1602
  }
1294
1603
 
@@ -1326,22 +1635,24 @@ function matches18(configRoutes, request2) {
1326
1635
  return urlMatches(request2.url, route20);
1327
1636
  }
1328
1637
  async function fetchTenantUser(config, method) {
1329
- if (!config.tenantId) {
1638
+ const { headers, tenantId, userId } = ctx.get();
1639
+ const action = method === "PUT" ? "add" : "delete";
1640
+ if (!tenantId) {
1330
1641
  throw new Error(
1331
- "The tenantId context is missing. Call nile.setContext({ tenantId })"
1642
+ `Unable to ${action} user to the tenant, the tenantId context is missing. Use nile.withContext({ tenantId })`
1332
1643
  );
1333
1644
  }
1334
- if (!config.userId) {
1645
+ if (!userId) {
1335
1646
  throw new Error(
1336
- "the userId context is missing. Call nile.setContext({ userId })"
1647
+ `Unable to ${action} user to tenant. The userId context is missing. Use nile.withContext({ userId })`
1337
1648
  );
1338
1649
  }
1339
- const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/tenants/{tenantId}/users/{userId}" /* TENANT_USER */.replace(
1340
- "{tenantId}",
1341
- config.tenantId
1342
- ).replace("{userId}", config.userId)}/link`;
1650
+ const clientUrl = `${config.serverOrigin}${config.routePrefix}${"/tenants/{tenantId}/users/{userId}" /* TENANT_USER */.replace("{tenantId}", tenantId).replace(
1651
+ "{userId}",
1652
+ userId
1653
+ )}/link`;
1343
1654
  const req = new Request(clientUrl, {
1344
- headers: config.headers,
1655
+ headers,
1345
1656
  method
1346
1657
  });
1347
1658
  return await config.handlers[method](req);
@@ -1355,7 +1666,7 @@ async function DELETE4(config, init) {
1355
1666
  return new Response(null, { status: 404 });
1356
1667
  }
1357
1668
  init.method = "DELETE";
1358
- const url = `${apiRoutes(config).INVITE(tenantId)}/${inviteId}`;
1669
+ const url = `${apiRoutes(config.apiUrl).INVITE(tenantId)}/${inviteId}`;
1359
1670
  return await request(url, init, config);
1360
1671
  }
1361
1672
 
@@ -1378,8 +1689,21 @@ function matches19(configRoutes, request2) {
1378
1689
 
1379
1690
  // src/api/handlers/DELETE.ts
1380
1691
  function DELETER(configRoutes, config) {
1381
- const { info, warn } = config.logger("[DELETE MATCHER]");
1382
- return async function DELETE5(req) {
1692
+ const { error, info, warn: warn2 } = config.logger("[DELETE MATCHER]");
1693
+ return async function DELETE5(...params) {
1694
+ const handledRequest = await config.extensionCtx?.runExtensions(
1695
+ "onHandleRequest" /* onHandleRequest */,
1696
+ config,
1697
+ params
1698
+ );
1699
+ if (handledRequest) {
1700
+ return handledRequest;
1701
+ }
1702
+ const req = params[0] instanceof Request ? params[0] : null;
1703
+ if (!req) {
1704
+ error("Proxy requests failed, a Request object was not passed.");
1705
+ return;
1706
+ }
1383
1707
  if (matches19(configRoutes, req)) {
1384
1708
  info("matches tenant invite id");
1385
1709
  return route19(req, config);
@@ -1388,10 +1712,6 @@ function DELETER(configRoutes, config) {
1388
1712
  info("matches tenant user");
1389
1713
  return route18(req, config);
1390
1714
  }
1391
- if (matches3(configRoutes, req)) {
1392
- info("matches tenant users");
1393
- return route3(req, config);
1394
- }
1395
1715
  if (matches6(configRoutes, req)) {
1396
1716
  info("matches tenants");
1397
1717
  return route6(req, config);
@@ -1400,18 +1720,31 @@ function DELETER(configRoutes, config) {
1400
1720
  info("matches me");
1401
1721
  return route(req, config);
1402
1722
  }
1403
- warn("No DELETE routes matched");
1723
+ warn2("No DELETE routes matched");
1404
1724
  return new Response(null, { status: 404 });
1405
1725
  };
1406
1726
  }
1407
1727
 
1408
1728
  // src/api/handlers/PUT.ts
1409
1729
  function PUTER(configRoutes, config) {
1410
- const { info, warn } = config.logger("[PUT MATCHER]");
1411
- return async function PUT6(req) {
1412
- if (matches4(configRoutes, req)) {
1413
- info("matches tenant invite");
1414
- return route4(req, config);
1730
+ const { error, info, warn: warn2 } = config.logger("[PUT MATCHER]");
1731
+ return async function PUT6(...params) {
1732
+ const handledRequest = await config.extensionCtx?.runExtensions(
1733
+ "onHandleRequest" /* onHandleRequest */,
1734
+ config,
1735
+ params
1736
+ );
1737
+ if (handledRequest) {
1738
+ return handledRequest;
1739
+ }
1740
+ const req = params[0] instanceof Request ? params[0] : null;
1741
+ if (!req) {
1742
+ error("Proxy requests failed, a Request object was not passed.");
1743
+ return;
1744
+ }
1745
+ if (matches4(configRoutes, req)) {
1746
+ info("matches tenant invite");
1747
+ return route4(req, config);
1415
1748
  }
1416
1749
  if (matches18(configRoutes, req)) {
1417
1750
  info("matches tenant user");
@@ -1421,10 +1754,6 @@ function PUTER(configRoutes, config) {
1421
1754
  info("matches tenant users");
1422
1755
  return route3(req, config);
1423
1756
  }
1424
- if (matches2(configRoutes, req)) {
1425
- info("matches users");
1426
- return route2(req, config);
1427
- }
1428
1757
  if (matches(configRoutes, req)) {
1429
1758
  info("matches me");
1430
1759
  return route(req, config);
@@ -1437,7 +1766,7 @@ function PUTER(configRoutes, config) {
1437
1766
  info("matches reset password");
1438
1767
  return route15(req, config);
1439
1768
  }
1440
- warn("No PUT routes matched");
1769
+ warn2("No PUT routes matched");
1441
1770
  return new Response(null, { status: 404 });
1442
1771
  };
1443
1772
  }
@@ -1641,18 +1970,7 @@ var Config = class {
1641
1970
  extensionCtx;
1642
1971
  extensions;
1643
1972
  logger;
1644
- /**
1645
- * Stores the set tenant id from Server for use in sub classes
1646
- */
1647
- tenantId;
1648
- /**
1649
- * Stores the set user id from Server for use in sub classes
1650
- */
1651
- userId;
1652
- /**
1653
- * Stores the headers to be used in `fetch` calls
1654
- */
1655
- headers;
1973
+ context;
1656
1974
  /**
1657
1975
  * The nile-auth url
1658
1976
  */
@@ -1703,11 +2021,11 @@ var Config = class {
1703
2021
  if (databaseName) {
1704
2022
  this.db.database = databaseName;
1705
2023
  }
1706
- if (config?.headers) {
1707
- this.headers = config?.headers;
1708
- } else {
1709
- this.headers = new Headers();
1710
- }
2024
+ this.context = {
2025
+ tenantId: config?.tenantId,
2026
+ userId: config?.userId,
2027
+ headers: config?.headers ? new Headers(config.headers) : new Headers()
2028
+ };
1711
2029
  this.routes = {
1712
2030
  ...appRoutes(config?.routePrefix),
1713
2031
  ...config?.routes
@@ -1750,8 +2068,6 @@ var Config = class {
1750
2068
  ],
1751
2069
  delete: [this.routes.TENANT_USER, this.routes.TENANT]
1752
2070
  };
1753
- this.tenantId = config?.tenantId;
1754
- this.userId = config?.userId;
1755
2071
  }
1756
2072
  };
1757
2073
 
@@ -1800,17 +2116,17 @@ var updateHeaders = (val) => {
1800
2116
  var watchHeaders = (cb) => eventer.subscribe("headers" /* Headers */, cb);
1801
2117
 
1802
2118
  // src/db/PoolProxy.ts
1803
- function createProxyForPool(pool, config) {
1804
- const { info, error } = config.logger("[pool]");
2119
+ function createProxyForPool(pool, config, logger, context) {
2120
+ const { info, error } = logger("[pool]");
1805
2121
  return new Proxy(pool, {
1806
2122
  get(target, property) {
1807
2123
  if (property === "query") {
1808
- if (!config.db.connectionString) {
1809
- if (!config.db.user || !config.db.password) {
2124
+ if (!config.connectionString) {
2125
+ if (!config.user || !config.password) {
1810
2126
  error(
1811
2127
  "Cannot connect to the database. User and/or password are missing. Generate them at https://console.thenile.dev"
1812
2128
  );
1813
- } else if (!config.db.database) {
2129
+ } else if (!config.database) {
1814
2130
  error(
1815
2131
  "Unable to obtain database name. Is process.env.NILEDB_POSTGRES_URL set?"
1816
2132
  );
@@ -1818,7 +2134,15 @@ function createProxyForPool(pool, config) {
1818
2134
  }
1819
2135
  const caller = target[property];
1820
2136
  return function query(...args) {
1821
- info("query", ...args);
2137
+ let log = "[QUERY]";
2138
+ const [tenantId, userId] = context;
2139
+ if (tenantId) {
2140
+ log = `${log}[TENANT:${tenantId}]`;
2141
+ }
2142
+ if (userId) {
2143
+ log = `${log}[USER:${userId}]`;
2144
+ }
2145
+ info(log, ...args);
1822
2146
  const called = caller.apply(this, args);
1823
2147
  return called;
1824
2148
  };
@@ -1834,39 +2158,44 @@ var NileDatabase = class {
1834
2158
  tenantId;
1835
2159
  userId;
1836
2160
  id;
1837
- config;
2161
+ logger;
1838
2162
  timer;
1839
- constructor(config, id) {
1840
- const { warn, info, debug } = config.logger("[NileInstance]");
2163
+ config;
2164
+ constructor(config, logger, id) {
2165
+ this.logger = logger("[NileInstance]");
1841
2166
  this.id = id;
1842
2167
  const poolConfig = {
1843
2168
  min: 0,
1844
2169
  max: 10,
1845
2170
  idleTimeoutMillis: 3e4,
1846
- ...config.db
2171
+ ...config
1847
2172
  };
1848
2173
  const { afterCreate, ...remaining } = poolConfig;
1849
- config.db = poolConfig;
1850
- this.config = config;
1851
- const cloned = { ...this.config.db };
2174
+ this.config = remaining;
2175
+ const cloned = { ...config };
1852
2176
  cloned.password = "***";
1853
- debug(`Connection pool config ${JSON.stringify(cloned)}`);
1854
- this.pool = createProxyForPool(new pg__default.default.Pool(remaining), this.config);
2177
+ this.logger.debug(`Connection pool config ${JSON.stringify(cloned)}`);
2178
+ this.pool = createProxyForPool(
2179
+ new pg__default.default.Pool(remaining),
2180
+ this.config,
2181
+ logger,
2182
+ id === "base" ? [] : id.split(":")
2183
+ );
1855
2184
  if (typeof afterCreate === "function") {
1856
- warn(
2185
+ this.logger.warn(
1857
2186
  "Providing an pool configuration will stop automatic tenant context setting."
1858
2187
  );
1859
2188
  }
1860
2189
  this.startTimeout();
1861
2190
  this.pool.on("connect", async (client) => {
1862
- debug(`pool connected ${this.id}`);
2191
+ this.logger.debug(`pool connected ${this.id}`);
1863
2192
  this.startTimeout();
1864
2193
  const afterCreate2 = makeAfterCreate(
1865
- config,
1866
- `${this.id}-${this.timer}`
2194
+ logger,
2195
+ `${this.id}|${this.timer}`
1867
2196
  );
1868
2197
  afterCreate2(client, (err) => {
1869
- const { error } = config.logger("[after create callback]");
2198
+ const { error } = logger("[after create callback]");
1870
2199
  if (err) {
1871
2200
  clearTimeout(this.timer);
1872
2201
  error("after create failed", {
@@ -1879,7 +2208,7 @@ var NileDatabase = class {
1879
2208
  });
1880
2209
  this.pool.on("error", (err) => {
1881
2210
  clearTimeout(this.timer);
1882
- info(`pool ${this.id} failed`, {
2211
+ this.logger.info(`pool ${this.id} failed`, {
1883
2212
  message: err.message,
1884
2213
  stack: err.stack
1885
2214
  });
@@ -1889,27 +2218,27 @@ var NileDatabase = class {
1889
2218
  if (destroy) {
1890
2219
  clearTimeout(this.timer);
1891
2220
  evictPool(this.id);
1892
- debug(`destroying pool ${this.id}`);
2221
+ this.logger.debug(`destroying pool ${this.id}`);
1893
2222
  }
1894
2223
  });
1895
2224
  }
1896
2225
  startTimeout() {
1897
- const { debug } = this.config.logger("[NileInstance]");
2226
+ const { debug } = this.logger;
1898
2227
  if (this.timer) {
1899
2228
  clearTimeout(this.timer);
1900
2229
  }
1901
2230
  this.timer = setTimeout(() => {
1902
2231
  debug(
1903
- `Pool reached idleTimeoutMillis. ${this.id} evicted after ${Number(this.config.db.idleTimeoutMillis) ?? 3e4}ms`
2232
+ `Pool reached idleTimeoutMillis. ${this.id} evicted after ${Number(this.config.idleTimeoutMillis) ?? 3e4}ms`
1904
2233
  );
1905
2234
  this.pool.end(() => {
1906
2235
  clearTimeout(this.timer);
1907
2236
  evictPool(this.id);
1908
2237
  });
1909
- }, Number(this.config.db.idleTimeoutMillis) ?? 3e4);
2238
+ }, Number(this.config.idleTimeoutMillis) ?? 3e4);
1910
2239
  }
1911
2240
  shutdown() {
1912
- const { debug } = this.config.logger("[NileInstance]");
2241
+ const { debug } = this.logger;
1913
2242
  debug(`attempting to shut down ${this.id}`);
1914
2243
  clearTimeout(this.timer);
1915
2244
  this.pool.end(() => {
@@ -1918,8 +2247,8 @@ var NileDatabase = class {
1918
2247
  }
1919
2248
  };
1920
2249
  var NileInstance_default = NileDatabase;
1921
- function makeAfterCreate(config, id) {
1922
- const { error, warn, debug } = config.logger("[afterCreate]");
2250
+ function makeAfterCreate(logger, id) {
2251
+ const { error, warn: warn2, debug } = logger("[afterCreate]");
1923
2252
  return (conn, done) => {
1924
2253
  conn.on("error", function errorHandler(e) {
1925
2254
  error(`Connection ${id} was terminated by server`, {
@@ -1928,13 +2257,15 @@ function makeAfterCreate(config, id) {
1928
2257
  });
1929
2258
  done(e, conn);
1930
2259
  });
1931
- if (config.tenantId) {
1932
- const query = [`SET nile.tenant_id = '${config.tenantId}'`];
1933
- if (config.userId) {
1934
- if (!config.tenantId) {
1935
- warn("A user id cannot be set in context without a tenant id");
2260
+ const [context] = id.split("|");
2261
+ const [tenantId, userId] = context.split(":");
2262
+ if (tenantId !== "base") {
2263
+ const query = [`SET nile.tenant_id = '${tenantId}'`];
2264
+ if (userId) {
2265
+ if (!tenantId) {
2266
+ warn2("A user id cannot be set in context without a tenant id");
1936
2267
  }
1937
- query.push(`SET nile.user_id = '${config.userId}'`);
2268
+ query.push(`SET nile.user_id = '${userId}'`);
1938
2269
  }
1939
2270
  conn.query(query.join(";"), function(err) {
1940
2271
  if (err) {
@@ -1947,11 +2278,11 @@ function makeAfterCreate(config, id) {
1947
2278
  });
1948
2279
  } else {
1949
2280
  if (query.length === 1) {
1950
- debug(`connection context set: tenantId=${config.tenantId}`);
2281
+ debug(`connection context set: tenantId=${tenantId}`);
1951
2282
  }
1952
2283
  if (query.length === 2) {
1953
2284
  debug(
1954
- `connection context set: tenantId=${config.tenantId} userId=${config.userId}`
2285
+ `connection context set: tenantId=${tenantId} userId=${userId}`
1955
2286
  );
1956
2287
  }
1957
2288
  }
@@ -1983,19 +2314,20 @@ var DBManager = class {
1983
2314
  watchEvictPool(this.poolWatcherFn);
1984
2315
  }
1985
2316
  poolWatcher = (config) => (id) => {
1986
- const { info, warn } = Logger(config)("[DBManager]");
2317
+ const { info, warn: warn2 } = Logger(config)("[DBManager]");
1987
2318
  if (id && this.connections.has(id)) {
1988
2319
  info(`Removing ${id} from db connection pool.`);
1989
2320
  const connection = this.connections.get(id);
1990
2321
  connection?.shutdown();
1991
2322
  this.connections.delete(id);
1992
2323
  } else {
1993
- warn(`missed eviction of ${id}`);
2324
+ warn2(`missed eviction of ${id}`);
1994
2325
  }
1995
2326
  };
1996
- getConnection = (config) => {
2327
+ getConnection = (config, noContext = false) => {
1997
2328
  const { info } = Logger(config)("[DBManager]");
1998
- const id = this.makeId(config.tenantId, config.userId);
2329
+ const { tenantId, userId } = noContext ? {} : ctx.getLastUsed();
2330
+ const id = this.makeId(tenantId, userId);
1999
2331
  const existing = this.connections.get(id);
2000
2332
  info(`# of instances: ${this.connections.size}`);
2001
2333
  if (existing) {
@@ -2003,7 +2335,7 @@ var DBManager = class {
2003
2335
  existing.startTimeout();
2004
2336
  return existing.pool;
2005
2337
  }
2006
- const newOne = new NileInstance_default(new Config(config), id);
2338
+ const newOne = new NileInstance_default(config.db, config.logger, id);
2007
2339
  this.connections.set(id, newOne);
2008
2340
  info(`created new ${id}`);
2009
2341
  info(`# of instances: ${this.connections.size}`);
@@ -2027,230 +2359,284 @@ var DBManager = class {
2027
2359
  var Auth = class {
2028
2360
  #logger;
2029
2361
  #config;
2362
+ /**
2363
+ * Create an Auth helper.
2364
+ *
2365
+ * @param config - runtime configuration used by the underlying fetch helpers
2366
+ * such as `serverOrigin`, `routePrefix` and default headers.
2367
+ */
2030
2368
  constructor(config) {
2031
2369
  this.#config = config;
2032
2370
  this.#logger = config.logger("[auth]");
2033
2371
  }
2034
2372
  async getSession(rawResponse = false) {
2035
- const res = await fetchSession(this.#config);
2036
- if (rawResponse) {
2037
- return res;
2038
- }
2039
- try {
2040
- const session = await res.clone().json();
2041
- if (Object.keys(session).length === 0) {
2042
- return void 0;
2373
+ return withNileContext(this.#config, async () => {
2374
+ const res = await fetchSession(this.#config);
2375
+ if (rawResponse) {
2376
+ return res;
2043
2377
  }
2044
- return session;
2045
- } catch {
2046
- return res;
2047
- }
2378
+ try {
2379
+ const session = await res.clone().json();
2380
+ if (Object.keys(session).length === 0) {
2381
+ return void 0;
2382
+ }
2383
+ return session;
2384
+ } catch {
2385
+ return res;
2386
+ }
2387
+ });
2048
2388
  }
2049
2389
  async getCsrf(rawResponse = false) {
2050
- return await obtainCsrf(this.#config, rawResponse);
2390
+ return withNileContext(this.#config, async () => {
2391
+ return await obtainCsrf(this.#config, rawResponse);
2392
+ });
2051
2393
  }
2052
2394
  async listProviders(rawResponse = false) {
2053
- const res = await fetchProviders(this.#config);
2054
- if (rawResponse) {
2055
- return res;
2056
- }
2057
- try {
2058
- return await res.clone().json();
2059
- } catch {
2060
- return res;
2061
- }
2395
+ return withNileContext(this.#config, async () => {
2396
+ const res = await fetchProviders(this.#config);
2397
+ if (rawResponse) {
2398
+ return res;
2399
+ }
2400
+ try {
2401
+ return await res.clone().json();
2402
+ } catch {
2403
+ return res;
2404
+ }
2405
+ });
2062
2406
  }
2407
+ /**
2408
+ * Sign the current user out by calling `/api/auth/signout`.
2409
+ *
2410
+ * The CSRF token is fetched automatically and the stored cookies are cleared
2411
+ * from the internal configuration once the request completes.
2412
+ */
2063
2413
  async signOut() {
2064
- const csrfRes = await this.getCsrf();
2065
- if (!("csrfToken" in csrfRes)) {
2066
- throw new Error("Unable to obtain CSRF token. Sign out failed.");
2067
- }
2068
- const body = JSON.stringify({
2069
- csrfToken: csrfRes.csrfToken,
2070
- json: true
2414
+ return withNileContext(this.#config, async () => {
2415
+ const csrfRes = await this.getCsrf();
2416
+ if (!("csrfToken" in csrfRes)) {
2417
+ throw new Error("Unable to obtain CSRF token. Sign out failed.");
2418
+ }
2419
+ const body = JSON.stringify({
2420
+ csrfToken: csrfRes.csrfToken,
2421
+ json: true
2422
+ });
2423
+ const res = await fetchSignOut(this.#config, body);
2424
+ updateHeaders(new Headers({}));
2425
+ ctx.set({ headers: null });
2426
+ return res;
2071
2427
  });
2072
- const res = await fetchSignOut(this.#config, body);
2073
- updateHeaders(new Headers({}));
2074
- this.#config.headers = new Headers();
2075
- return res;
2076
2428
  }
2077
2429
  async signUp(payload, rawResponse) {
2078
- this.#config.headers = new Headers();
2079
- const { email, password, ...params } = payload;
2080
- if (!email || !password) {
2081
- throw new Error(
2082
- "Server side sign up requires a user email and password."
2083
- );
2084
- }
2085
- const providers = await this.listProviders();
2086
- const { credentials } = providers ?? {};
2087
- if (!credentials) {
2088
- throw new Error(
2089
- "Unable to obtain credential provider. Aborting server side sign up."
2090
- );
2091
- }
2092
- const csrf = await this.getCsrf();
2093
- let csrfToken;
2094
- if ("csrfToken" in csrf) {
2095
- csrfToken = csrf.csrfToken;
2096
- } else {
2097
- throw new Error("Unable to obtain parse CSRF. Request blocked.");
2098
- }
2099
- const body = JSON.stringify({
2100
- email,
2101
- password,
2102
- csrfToken,
2103
- callbackUrl: credentials.callbackUrl
2104
- });
2105
- const res = await fetchSignUp(this.#config, { body, params });
2106
- if (res.status > 299) {
2107
- this.#logger.error(await res.clone().text());
2108
- return void 0;
2109
- }
2110
- const token = parseToken(res.headers);
2111
- if (!token) {
2112
- throw new Error("Server side sign up failed. Session token not found");
2113
- }
2114
- this.#config.headers?.append("cookie", token);
2115
- updateHeaders(this.#config.headers);
2116
- if (rawResponse) {
2117
- return res;
2118
- }
2119
- try {
2120
- const json = await res.clone().json();
2121
- if (json && typeof json === "object" && "tenants" in json) {
2122
- const tenantId = json.tenants[0];
2123
- if (tenantId) {
2124
- updateTenantId(tenantId);
2430
+ return withNileContext(this.#config, async () => {
2431
+ ctx.set({ headers: null });
2432
+ const { email, password, ...params } = payload;
2433
+ if (!email || !password) {
2434
+ throw new Error(
2435
+ "Server side sign up requires a user email and password."
2436
+ );
2437
+ }
2438
+ const providers = await this.listProviders();
2439
+ const { credentials } = providers ?? {};
2440
+ if (!credentials) {
2441
+ throw new Error(
2442
+ "Unable to obtain credential provider. Aborting server side sign up."
2443
+ );
2444
+ }
2445
+ const csrf = await obtainCsrf(this.#config);
2446
+ let csrfToken;
2447
+ if ("csrfToken" in csrf) {
2448
+ csrfToken = csrf.csrfToken;
2449
+ } else {
2450
+ throw new Error("Unable to obtain parse CSRF. Request blocked.");
2451
+ }
2452
+ const body = JSON.stringify({
2453
+ email,
2454
+ password,
2455
+ csrfToken,
2456
+ callbackUrl: credentials.callbackUrl
2457
+ });
2458
+ const res = await fetchSignUp(this.#config, { body, params });
2459
+ if (res.status > 299) {
2460
+ this.#logger.error(await res.clone().text());
2461
+ return void 0;
2462
+ }
2463
+ const token = parseToken(res.headers);
2464
+ if (!token) {
2465
+ throw new Error("Server side sign up failed. Session token not found");
2466
+ }
2467
+ const { headers } = ctx.get();
2468
+ headers?.append("cookie", token);
2469
+ ctx.set({ headers });
2470
+ updateHeaders(headers);
2471
+ if (rawResponse) {
2472
+ return res;
2473
+ }
2474
+ try {
2475
+ const json = await res.clone().json();
2476
+ if (json && typeof json === "object" && "tenants" in json) {
2477
+ const tenantId = json.tenants[0];
2478
+ if (tenantId) {
2479
+ updateTenantId(tenantId);
2480
+ }
2125
2481
  }
2482
+ return json;
2483
+ } catch {
2484
+ return res;
2126
2485
  }
2127
- return json;
2128
- } catch {
2129
- return res;
2130
- }
2131
- }
2132
- async forgotPassword(req) {
2133
- let email = "";
2134
- const defaults = defaultCallbackUrl({
2135
- config: this.#config
2136
- });
2137
- let callbackUrl = defaults.callbackUrl;
2138
- let redirectUrl = defaults.redirectUrl;
2139
- if ("email" in req) {
2140
- email = req.email;
2141
- }
2142
- if ("callbackUrl" in req) {
2143
- callbackUrl = req.callbackUrl ? req.callbackUrl : null;
2144
- }
2145
- if ("redirectUrl" in req) {
2146
- redirectUrl = req.redirectUrl ? req.redirectUrl : null;
2147
- }
2148
- const body = JSON.stringify({
2149
- email,
2150
- redirectUrl,
2151
- callbackUrl
2152
2486
  });
2153
- const data = await fetchResetPassword(
2154
- this.#config,
2155
- "POST",
2156
- body,
2157
- new URLSearchParams(),
2158
- false
2159
- );
2160
- return data;
2161
2487
  }
2162
- async resetPassword(req) {
2163
- let email = "";
2164
- let password = "";
2165
- const defaults = defaultCallbackUrl({ config: this.#config });
2166
- let callbackUrl = defaults.callbackUrl;
2167
- let redirectUrl = defaults.redirectUrl;
2168
- if (req instanceof Request) {
2169
- const body2 = await req.json();
2170
- email = body2.email;
2171
- password = body2.password;
2172
- const cbFromHeaders = parseCallback(req.headers);
2173
- if (cbFromHeaders) {
2174
- callbackUrl = cbFromHeaders;
2175
- }
2176
- if (body2.callbackUrl) {
2177
- callbackUrl = body2.callbackUrl;
2178
- }
2179
- if (body2.redirectUrl) {
2180
- redirectUrl = body2.redirectUrl;
2181
- }
2182
- } else {
2488
+ /**
2489
+ * Request a password reset email.
2490
+ *
2491
+ * Sends a `POST` to `/api/auth/password-reset` with the provided email and
2492
+ * optional callback information. The endpoint responds with a redirect URL
2493
+ * which is returned as a {@link Response} object.
2494
+ */
2495
+ async forgotPassword(req) {
2496
+ return withNileContext(this.#config, async () => {
2497
+ let email = "";
2498
+ const defaults = defaultCallbackUrl(this.#config);
2499
+ let callbackUrl = defaults.callbackUrl;
2500
+ let redirectUrl = defaults.redirectUrl;
2183
2501
  if ("email" in req) {
2184
2502
  email = req.email;
2185
2503
  }
2186
- if ("password" in req) {
2187
- password = req.password;
2188
- }
2189
2504
  if ("callbackUrl" in req) {
2190
- callbackUrl = req.callbackUrl ? req.callbackUrl : null;
2505
+ callbackUrl = fQUrl(req.callbackUrl ?? "", this.#config);
2191
2506
  }
2192
2507
  if ("redirectUrl" in req) {
2193
- redirectUrl = req.redirectUrl ? req.redirectUrl : null;
2508
+ redirectUrl = fQUrl(req.redirectUrl ?? "", this.#config);
2194
2509
  }
2195
- }
2196
- await this.getCsrf();
2197
- const body = JSON.stringify({
2198
- email,
2199
- password,
2200
- redirectUrl,
2201
- callbackUrl
2202
- });
2203
- let urlWithParams;
2204
- try {
2205
- const data = await fetchResetPassword(this.#config, "POST", body);
2206
- const cloned = data.clone();
2207
- if (data.status === 400) {
2208
- const text = await cloned.text();
2209
- this.#logger.error(text);
2210
- return data;
2211
- }
2212
- const { url } = await data.json();
2213
- urlWithParams = url;
2214
- } catch {
2215
- }
2216
- let token;
2217
- try {
2218
- const worthyParams = new URL(urlWithParams).searchParams;
2219
- const answer = await fetchResetPassword(
2510
+ const body = JSON.stringify({
2511
+ email,
2512
+ redirectUrl,
2513
+ callbackUrl
2514
+ });
2515
+ const data = await fetchResetPassword(
2220
2516
  this.#config,
2221
- "GET",
2222
- null,
2223
- worthyParams
2224
- );
2225
- token = parseResetToken(answer.headers);
2226
- } catch {
2227
- this.#logger.warn(
2228
- "Unable to parse reset password url. Password not reset."
2229
- );
2230
- }
2231
- const cookie = this.#config.headers.get("cookie")?.split("; ");
2232
- if (token) {
2233
- cookie?.push(token);
2234
- } else {
2235
- throw new Error(
2236
- "Unable to reset password, reset token is missing from response"
2517
+ "POST",
2518
+ body,
2519
+ new URLSearchParams(),
2520
+ false
2237
2521
  );
2238
- }
2239
- this.#config.headers = new Headers({
2240
- ...this.#config.headers,
2241
- cookie: cookie?.join("; ")
2522
+ return data;
2242
2523
  });
2243
- const res = await fetchResetPassword(this.#config, "PUT", body);
2244
- cookie?.pop();
2245
- const cleaned = cookie?.filter((c) => !c.includes("nile.session")) ?? [];
2246
- cleaned.push(String(parseToken(res.headers)));
2247
- const updatedHeaders = new Headers({ cookie: cleaned.join("; ") });
2248
- updateHeaders(updatedHeaders);
2249
- return res;
2250
2524
  }
2525
+ /**
2526
+ * Complete a password reset.
2527
+ *
2528
+ * This workflow expects a token obtained from {@link forgotPassword}. The
2529
+ * function performs a POST/GET/PUT sequence against
2530
+ * `/api/auth/password-reset` as described in the OpenAPI specification.
2531
+ *
2532
+ * @param req - either a {@link Request} with a JSON body or an object
2533
+ * containing the necessary fields.
2534
+ */
2535
+ async resetPassword(req) {
2536
+ return withNileContext(this.#config, async () => {
2537
+ let email = "";
2538
+ let password = "";
2539
+ const defaults = defaultCallbackUrl(this.#config);
2540
+ let callbackUrl = defaults.callbackUrl;
2541
+ let redirectUrl = defaults.redirectUrl;
2542
+ if (req instanceof Request) {
2543
+ const body2 = await req.json();
2544
+ email = body2.email;
2545
+ password = body2.password;
2546
+ const cbFromHeaders = parseCallback(req.headers);
2547
+ if (cbFromHeaders) {
2548
+ callbackUrl = cbFromHeaders;
2549
+ }
2550
+ if (body2.callbackUrl) {
2551
+ callbackUrl = body2.callbackUrl;
2552
+ }
2553
+ if (body2.redirectUrl) {
2554
+ redirectUrl = body2.redirectUrl;
2555
+ }
2556
+ } else {
2557
+ if ("email" in req) {
2558
+ email = req.email;
2559
+ }
2560
+ if ("password" in req) {
2561
+ password = req.password;
2562
+ }
2563
+ if ("callbackUrl" in req) {
2564
+ callbackUrl = req.callbackUrl ? req.callbackUrl : null;
2565
+ }
2566
+ if ("redirectUrl" in req) {
2567
+ redirectUrl = req.redirectUrl ? req.redirectUrl : null;
2568
+ }
2569
+ }
2570
+ await this.getCsrf();
2571
+ const body = JSON.stringify({
2572
+ email,
2573
+ password,
2574
+ redirectUrl,
2575
+ callbackUrl
2576
+ });
2577
+ let urlWithParams;
2578
+ try {
2579
+ const data = await fetchResetPassword(this.#config, "POST", body);
2580
+ const cloned = data.clone();
2581
+ if (data.status === 400) {
2582
+ const text = await cloned.text();
2583
+ this.#logger.error(text);
2584
+ return data;
2585
+ }
2586
+ const { url } = await data.json();
2587
+ urlWithParams = url;
2588
+ } catch {
2589
+ }
2590
+ let token;
2591
+ try {
2592
+ const worthyParams = new URL(urlWithParams).searchParams;
2593
+ const answer = await fetchResetPassword(
2594
+ this.#config,
2595
+ "GET",
2596
+ null,
2597
+ worthyParams
2598
+ );
2599
+ token = parseResetToken(answer.headers);
2600
+ } catch {
2601
+ this.#logger.warn(
2602
+ "Unable to parse reset password url. Password not reset."
2603
+ );
2604
+ }
2605
+ const { headers } = ctx.get();
2606
+ const cookie = headers?.get("cookie")?.split("; ");
2607
+ if (token) {
2608
+ cookie?.push(token);
2609
+ } else {
2610
+ throw new Error(
2611
+ "Unable to reset password, reset token is missing from response"
2612
+ );
2613
+ }
2614
+ if (cookie) {
2615
+ headers?.set("cookie", cookie?.join("; "));
2616
+ ctx.set({
2617
+ headers
2618
+ });
2619
+ }
2620
+ const res = await fetchResetPassword(this.#config, "PUT", body);
2621
+ cookie?.pop();
2622
+ const cleaned = cookie?.filter((c) => !c.includes("nile.session")) ?? [];
2623
+ cleaned.push(String(parseToken(res.headers)));
2624
+ const updatedHeaders = new Headers({ cookie: cleaned.join("; ") });
2625
+ updateHeaders(updatedHeaders);
2626
+ return res;
2627
+ });
2628
+ }
2629
+ /**
2630
+ * Low level helper used by {@link signIn} to complete provider flows.
2631
+ *
2632
+ * Depending on the provider this issues either a GET or POST request to
2633
+ * `/api/auth/callback/{provider}` via {@link fetchCallback}.
2634
+ */
2251
2635
  async callback(provider, body) {
2252
2636
  if (body instanceof Request) {
2253
- this.#config.headers = body.headers;
2637
+ ctx.set({
2638
+ headers: body.headers
2639
+ });
2254
2640
  return await fetchCallback(
2255
2641
  this.#config,
2256
2642
  provider,
@@ -2262,109 +2648,115 @@ var Auth = class {
2262
2648
  return await fetchCallback(this.#config, provider, body);
2263
2649
  }
2264
2650
  async signIn(provider, payload, rawResponse) {
2265
- if (payload instanceof Request) {
2266
- const body2 = new URLSearchParams(await payload.text());
2267
- const origin = new URL(payload.url).origin;
2268
- const payloadUrl = body2?.get("callbackUrl");
2269
- const csrfToken2 = body2?.get("csrfToken");
2270
- const callbackUrl = `${!payloadUrl?.startsWith("http") ? origin : ""}${payloadUrl}`;
2271
- if (!csrfToken2) {
2651
+ return withNileContext(this.#config, async () => {
2652
+ if (payload instanceof Request) {
2653
+ const body2 = new URLSearchParams(await payload.text());
2654
+ const origin = new URL(payload.url).origin;
2655
+ const payloadUrl = body2?.get("callbackUrl");
2656
+ const csrfToken2 = body2?.get("csrfToken");
2657
+ const callbackUrl = `${!payloadUrl?.startsWith("http") ? origin : ""}${payloadUrl}`;
2658
+ if (!csrfToken2) {
2659
+ throw new Error(
2660
+ "CSRF token in missing from request. Request it by the client before calling sign in"
2661
+ );
2662
+ }
2663
+ const updatedHeaders = new Headers(payload.headers);
2664
+ updatedHeaders.set("Content-Type", "application/x-www-form-urlencoded");
2665
+ ctx.set({ headers: updatedHeaders });
2666
+ const params = new URLSearchParams({
2667
+ csrfToken: csrfToken2,
2668
+ json: String(true)
2669
+ });
2670
+ if (payloadUrl) {
2671
+ params.set("callbackUrl", callbackUrl);
2672
+ }
2673
+ return await fetchSignIn(this.#config, provider, params);
2674
+ }
2675
+ ctx.set({ headers: null });
2676
+ const { info, error } = this.#logger;
2677
+ const providers = await this.listProviders();
2678
+ info("Obtaining csrf");
2679
+ const csrf = await obtainCsrf(this.#config);
2680
+ let csrfToken;
2681
+ if ("csrfToken" in csrf) {
2682
+ csrfToken = csrf.csrfToken;
2683
+ } else {
2684
+ throw new Error("Unable to obtain parse CSRF. Request blocked.");
2685
+ }
2686
+ const { credentials } = providers ?? {};
2687
+ if (!credentials) {
2272
2688
  throw new Error(
2273
- "CSRF token in missing from request. Request it by the client before calling sign in"
2689
+ "Unable to obtain credential provider. Aborting server side sign in."
2274
2690
  );
2275
2691
  }
2276
- this.#config.headers = new Headers(payload.headers);
2277
- this.#config.headers.set(
2278
- "Content-Type",
2279
- "application/x-www-form-urlencoded"
2280
- );
2281
- const params = new URLSearchParams({
2282
- csrfToken: csrfToken2,
2283
- json: String(true)
2692
+ const { email, password } = payload ?? {};
2693
+ if (provider === "email" && (!email || !password)) {
2694
+ throw new Error(
2695
+ "Server side sign in requires a user email and password."
2696
+ );
2697
+ }
2698
+ info(`Obtaining providers for ${email}`);
2699
+ info(`Attempting sign in with email ${email}`);
2700
+ if (!email) {
2701
+ throw new Error("Email missing from payload, unable to sign in");
2702
+ }
2703
+ const body = JSON.stringify({
2704
+ email,
2705
+ password,
2706
+ csrfToken,
2707
+ callbackUrl: credentials.callbackUrl
2284
2708
  });
2285
- if (payloadUrl) {
2286
- params.set("callbackUrl", callbackUrl);
2287
- }
2288
- return await fetchSignIn(this.#config, provider, params);
2289
- }
2290
- this.#config.headers = new Headers();
2291
- const { info, error } = this.#logger;
2292
- const providers = await this.listProviders();
2293
- info("Obtaining csrf");
2294
- const csrf = await this.getCsrf();
2295
- let csrfToken;
2296
- if ("csrfToken" in csrf) {
2297
- csrfToken = csrf.csrfToken;
2298
- } else {
2299
- throw new Error("Unable to obtain parse CSRF. Request blocked.");
2300
- }
2301
- const { credentials } = providers ?? {};
2302
- if (!credentials) {
2303
- throw new Error(
2304
- "Unable to obtain credential provider. Aborting server side sign in."
2305
- );
2306
- }
2307
- const { email, password } = payload ?? {};
2308
- if (provider === "email" && (!email || !password)) {
2309
- throw new Error(
2310
- "Server side sign in requires a user email and password."
2311
- );
2312
- }
2313
- info(`Obtaining providers for ${email}`);
2314
- info(`Attempting sign in with email ${email}`);
2315
- const body = JSON.stringify({
2316
- email,
2317
- password,
2318
- csrfToken,
2319
- callbackUrl: credentials.callbackUrl
2320
- });
2321
- const signInRes = await this.callback(provider, body);
2322
- const authCookie = signInRes?.headers.get("set-cookie");
2323
- if (!authCookie) {
2324
- throw new Error("authentication failed");
2325
- }
2326
- const token = parseToken(signInRes?.headers);
2327
- const possibleError = signInRes?.headers.get("location");
2328
- if (possibleError) {
2329
- let urlError;
2709
+ const signInRes = await this.callback(provider, body);
2710
+ const authCookie = signInRes?.headers.get("set-cookie");
2711
+ if (!authCookie) {
2712
+ throw new Error("authentication failed");
2713
+ }
2714
+ const token = parseToken(signInRes?.headers);
2715
+ const possibleError = signInRes?.headers.get("location");
2716
+ if (possibleError) {
2717
+ let urlError;
2718
+ try {
2719
+ urlError = new URL(possibleError).searchParams.get("error");
2720
+ } catch {
2721
+ }
2722
+ if (urlError) {
2723
+ error("Unable to log user in", { error: urlError });
2724
+ return new Response(urlError, { status: signInRes.status });
2725
+ }
2726
+ }
2727
+ if (!token) {
2728
+ error("Unable to obtain auth token", {
2729
+ authCookie,
2730
+ signInRes
2731
+ });
2732
+ throw new Error("Server login failed");
2733
+ }
2734
+ info("Server sign in successful", { authCookie });
2735
+ const setCookie = signInRes.headers.get("set-cookie");
2736
+ const { headers } = ctx.get();
2737
+ if (setCookie) {
2738
+ const cookie = [
2739
+ parseCSRF(headers),
2740
+ parseCallback(signInRes.headers),
2741
+ parseToken(signInRes.headers)
2742
+ ].filter(Boolean).join("; ");
2743
+ const uHeaders = new Headers({ cookie });
2744
+ updateHeaders(uHeaders);
2745
+ ctx.set({ headers: uHeaders });
2746
+ } else {
2747
+ error("Unable to set context after sign in", {
2748
+ headers: signInRes.headers
2749
+ });
2750
+ }
2751
+ if (rawResponse) {
2752
+ return signInRes;
2753
+ }
2330
2754
  try {
2331
- urlError = new URL(possibleError).searchParams.get("error");
2755
+ return await signInRes.clone().json();
2332
2756
  } catch {
2757
+ return signInRes;
2333
2758
  }
2334
- if (urlError) {
2335
- error("Unable to log user in", { error: urlError });
2336
- return new Response(urlError, { status: signInRes.status });
2337
- }
2338
- }
2339
- if (!token) {
2340
- error("Unable to obtain auth token", {
2341
- authCookie,
2342
- signInRes
2343
- });
2344
- throw new Error("Server login failed");
2345
- }
2346
- info("Server sign in successful", { authCookie });
2347
- const setCookie = signInRes.headers.get("set-cookie");
2348
- if (setCookie) {
2349
- const cookie = [
2350
- parseCSRF(this.#config.headers),
2351
- parseCallback(signInRes.headers),
2352
- parseToken(signInRes.headers)
2353
- ].filter(Boolean).join("; ");
2354
- updateHeaders(new Headers({ cookie }));
2355
- } else {
2356
- error("Unable to set context after sign in", {
2357
- headers: signInRes.headers
2358
- });
2359
- }
2360
- if (rawResponse) {
2361
- return signInRes;
2362
- }
2363
- try {
2364
- return await signInRes.clone().json();
2365
- } catch {
2366
- return signInRes;
2367
- }
2759
+ });
2368
2760
  }
2369
2761
  };
2370
2762
  function parseCSRF(headers) {
@@ -2411,22 +2803,54 @@ function parseResetToken(headers) {
2411
2803
  const [, token] = /((__Secure-)?nile\.reset=[^;]+)/.exec(authCookie) ?? [];
2412
2804
  return token;
2413
2805
  }
2414
- function defaultCallbackUrl({ config }) {
2806
+ function parseTenantId(headers) {
2807
+ let authCookie = headers?.get("set-cookie");
2808
+ if (!authCookie) {
2809
+ authCookie = headers?.get("cookie");
2810
+ }
2811
+ if (!authCookie) {
2812
+ return void 0;
2813
+ }
2814
+ const [, token] = /((__Secure-)?nile\.tenant-id=[^;]+)/.exec(authCookie) ?? [];
2815
+ if (token) {
2816
+ const [, tenantId] = token.split("=");
2817
+ return tenantId;
2818
+ }
2819
+ return null;
2820
+ }
2821
+ function defaultCallbackUrl(config) {
2415
2822
  let cb = null;
2416
2823
  let redirect = null;
2417
- const fallbackCb = parseCallback(config.headers);
2824
+ const { headers } = ctx.get();
2825
+ const fallbackCb = parseCallback(headers);
2418
2826
  if (fallbackCb) {
2419
2827
  const [, value] = fallbackCb.split("=");
2420
2828
  cb = decodeURIComponent(value);
2421
2829
  if (value) {
2422
- redirect = `${new URL(cb).origin}${"/auth/reset-password" /* PASSWORD_RESET */}`;
2830
+ redirect = `${new URL(cb).origin}${config.routePrefix}${"/auth/reset-password" /* PASSWORD_RESET */}`;
2423
2831
  }
2424
2832
  }
2425
2833
  return { callbackUrl: cb, redirectUrl: redirect };
2426
2834
  }
2835
+ function fQUrl(path, config) {
2836
+ if (path.startsWith("/")) {
2837
+ const { callbackUrl } = defaultCallbackUrl(config);
2838
+ if (callbackUrl) {
2839
+ const { origin } = new URL(callbackUrl);
2840
+ return `${origin}${path}`;
2841
+ }
2842
+ }
2843
+ try {
2844
+ new URL(path);
2845
+ } catch {
2846
+ throw new Error("An invalid URL has been passed.");
2847
+ }
2848
+ return path;
2849
+ }
2427
2850
 
2428
2851
  // src/auth/obtainCsrf.ts
2429
2852
  async function obtainCsrf(config, rawResponse = false) {
2853
+ const { headers } = ctx.get();
2430
2854
  const res = await fetchCsrf(config);
2431
2855
  const csrfCook = parseCSRF(res.headers);
2432
2856
  if (csrfCook) {
@@ -2439,28 +2863,27 @@ async function obtainCsrf(config, rawResponse = false) {
2439
2863
  parseCallback(res.headers),
2440
2864
  parseToken(res.headers)
2441
2865
  ].filter(Boolean).join("; ");
2442
- config.headers.set("cookie", cookie);
2443
- updateHeaders(new Headers({ cookie }));
2866
+ headers.set("cookie", cookie);
2867
+ ctx.set({ headers });
2868
+ updateHeaders(headers);
2444
2869
  }
2445
2870
  if (!rawResponse) {
2446
2871
  return { csrfToken: token };
2447
2872
  }
2448
2873
  } else {
2449
- const existingCookie = config.headers.get("cookie");
2874
+ const existingCookie = headers.get("cookie");
2450
2875
  const cookieParts = [];
2451
2876
  if (existingCookie) {
2452
- cookieParts.push(
2453
- parseToken(config.headers),
2454
- parseCallback(config.headers)
2455
- );
2877
+ cookieParts.push(parseToken(headers), parseCallback(headers));
2456
2878
  }
2457
2879
  if (csrfCook) {
2458
2880
  cookieParts.push(csrfCook);
2459
2881
  } else {
2460
- cookieParts.push(parseCSRF(config.headers));
2882
+ cookieParts.push(parseCSRF(headers));
2461
2883
  }
2462
2884
  const cookie = cookieParts.filter(Boolean).join("; ");
2463
- config.headers.set("cookie", cookie);
2885
+ headers.set("cookie", cookie);
2886
+ ctx.set({ headers });
2464
2887
  updateHeaders(new Headers({ cookie }));
2465
2888
  }
2466
2889
  if (rawResponse) {
@@ -2473,74 +2896,131 @@ async function obtainCsrf(config, rawResponse = false) {
2473
2896
  }
2474
2897
  }
2475
2898
 
2899
+ // src/utils/qualifyDomain.ts
2900
+ function fQUrl2(callbackUrl, path) {
2901
+ if (path.startsWith("/")) {
2902
+ if (callbackUrl) {
2903
+ const { origin } = new URL(callbackUrl);
2904
+ return `${origin}${path}`;
2905
+ }
2906
+ }
2907
+ try {
2908
+ new URL(path);
2909
+ } catch {
2910
+ throw new Error("An invalid URL has been passed.");
2911
+ }
2912
+ return path;
2913
+ }
2914
+
2476
2915
  // src/users/index.ts
2477
2916
  var Users = class {
2478
2917
  #config;
2479
2918
  #logger;
2919
+ /**
2920
+ * Create a new Users helper.
2921
+ * @param config - The configuration used for requests.
2922
+ */
2480
2923
  constructor(config) {
2481
2924
  this.#config = config;
2482
2925
  this.#logger = config.logger("[me]");
2483
2926
  }
2927
+ /**
2928
+ * Update the current user via `PUT /api/me`.
2929
+ *
2930
+ * The OpenAPI description for this endpoint can be found in
2931
+ * `packages/server/src/api/routes/me/index.ts` under `updateSelf`.
2932
+ *
2933
+ * @param req - Partial user fields to send.
2934
+ * @param [rawResponse] - When `true`, return the raw {@link Response}.
2935
+ */
2484
2936
  async updateSelf(req, rawResponse) {
2485
- const res = await fetchMe(this.#config, "PUT", JSON.stringify(req));
2486
- if (rawResponse) {
2487
- return res;
2488
- }
2489
- try {
2490
- return await res?.clone().json();
2491
- } catch {
2492
- return res;
2493
- }
2937
+ return withNileContext(this.#config, async () => {
2938
+ const res = await fetchMe(this.#config, "PUT", JSON.stringify(req));
2939
+ if (rawResponse) {
2940
+ return res;
2941
+ }
2942
+ try {
2943
+ return await res?.clone().json();
2944
+ } catch {
2945
+ return res;
2946
+ }
2947
+ });
2494
2948
  }
2949
+ /**
2950
+ * Remove the current user using `DELETE /api/me`.
2951
+ *
2952
+ * After the request the authentication headers are cleared with
2953
+ * {@link updateHeaders}. The OpenAPI docs for this route are in
2954
+ * `packages/server/src/api/routes/me/index.ts` under `removeSelf`.
2955
+ */
2495
2956
  async removeSelf() {
2496
- const me = await this.getSelf();
2497
- if ("id" in me) {
2498
- this.#config.userId = me.id;
2499
- }
2500
- const res = await fetchMe(this.#config, "DELETE");
2501
- updateHeaders(new Headers());
2502
- return res;
2957
+ return withNileContext(this.#config, async () => {
2958
+ const me = await this.getSelf();
2959
+ if ("id" in me) {
2960
+ const userId = me.id;
2961
+ ctx.set({ userId });
2962
+ }
2963
+ const res = await fetchMe(this.#config, "DELETE");
2964
+ updateHeaders(new Headers());
2965
+ return res;
2966
+ });
2503
2967
  }
2504
2968
  async getSelf(rawResponse) {
2505
- const res = await fetchMe(this.#config);
2506
- if (rawResponse) {
2507
- return res;
2508
- }
2509
- try {
2510
- return await res?.clone().json();
2511
- } catch {
2512
- return res;
2513
- }
2969
+ return withNileContext(this.#config, async () => {
2970
+ const res = await fetchMe(this.#config);
2971
+ if (rawResponse) {
2972
+ return res;
2973
+ }
2974
+ try {
2975
+ return await res?.clone().json();
2976
+ } catch {
2977
+ return res;
2978
+ }
2979
+ });
2514
2980
  }
2515
2981
  async verifySelf(options, rawResponse = false) {
2516
- const bypassEmail = typeof options === "object" ? options.bypassEmail ?? process.env.NODE_ENV !== "production" : process.env.NODE_ENV !== "production";
2517
- const callbackUrl = typeof options === "object" ? options.callbackUrl : defaultCallbackUrl2(this.#config).callbackUrl;
2518
- let res;
2519
- try {
2520
- const me = await this.getSelf();
2521
- if (me instanceof Response) {
2522
- return me;
2982
+ return withNileContext(this.#config, async () => {
2983
+ const bypassEmail = typeof options === "object" && options?.bypassEmail === true;
2984
+ const callbackUrl = fQUrl2(
2985
+ defaultCallbackUrl2().callbackUrl,
2986
+ typeof options === "object" ? String(options.callbackUrl) : "/"
2987
+ );
2988
+ let res;
2989
+ try {
2990
+ const me = await this.getSelf();
2991
+ if (me instanceof Response) {
2992
+ return me;
2993
+ }
2994
+ res = await verifyEmailAddress(this.#config, me, String(callbackUrl));
2995
+ return res;
2996
+ } catch (e) {
2997
+ if (!bypassEmail) {
2998
+ let message = "Unable to verify email.";
2999
+ if (e instanceof Error) {
3000
+ message = e.message;
3001
+ }
3002
+ this.#logger?.error(
3003
+ `${message} you can bypass this message by setting bypassEmail: true when calling 'verifySelf'`
3004
+ );
3005
+ res = new Response(message, { status: 400 });
3006
+ }
3007
+ }
3008
+ if (bypassEmail) {
3009
+ this.#logger?.info(
3010
+ "bypassing email requirements for email verification"
3011
+ );
3012
+ res = this.updateSelf({ emailVerified: true }, rawResponse);
2523
3013
  }
2524
- res = await verifyEmailAddress(this.#config, me, String(callbackUrl));
2525
3014
  return res;
2526
- } catch {
2527
- const message = "Unable to verify email.";
2528
- this.#logger?.warn(message);
2529
- res = new Response(message, { status: 400 });
2530
- }
2531
- if (bypassEmail) {
2532
- res = this.updateSelf({ emailVerified: true }, rawResponse);
2533
- }
2534
- this.#logger.error(
2535
- "Unable to verify email address. Configure your SMTP server in the console."
2536
- );
2537
- return res;
3015
+ });
2538
3016
  }
2539
3017
  };
2540
3018
  async function verifyEmailAddress(config, user, callback) {
2541
- config.headers.set("content-type", "application/x-www-form-urlencoded");
3019
+ const { headers } = ctx.get();
3020
+ headers?.set("content-type", "application/x-www-form-urlencoded");
3021
+ ctx.set({ headers });
2542
3022
  const { csrfToken } = await obtainCsrf(config);
2543
- const defaults = defaultCallbackUrl2(config);
3023
+ const defaults = defaultCallbackUrl2();
2544
3024
  const callbackUrl = callback ?? String(defaults.callbackUrl);
2545
3025
  const res = await fetchVerifyEmail(
2546
3026
  config,
@@ -2556,9 +3036,10 @@ async function verifyEmailAddress(config, user, callback) {
2556
3036
  }
2557
3037
  return res;
2558
3038
  }
2559
- function defaultCallbackUrl2(config) {
3039
+ function defaultCallbackUrl2() {
2560
3040
  let cb = null;
2561
- const fallbackCb = parseCallback(config.headers);
3041
+ const { headers } = ctx.get();
3042
+ const fallbackCb = parseCallback(headers);
2562
3043
  if (fallbackCb) {
2563
3044
  const [, value] = fallbackCb.split("=");
2564
3045
  cb = decodeURIComponent(value);
@@ -2572,194 +3053,286 @@ var Tenants = class {
2572
3053
  constructor(config) {
2573
3054
  this.#config = config;
2574
3055
  }
3056
+ /**
3057
+ * Create a new tenant using `POST /api/tenants`.
3058
+ * See `packages/server/src/api/routes/tenants/POST.ts` for the
3059
+ * `createTenant` operation definition.
3060
+ */
2575
3061
  async create(req, rawResponse) {
2576
- let res;
2577
- if (typeof req === "string") {
2578
- res = await fetchTenants(
2579
- this.#config,
2580
- "POST",
2581
- JSON.stringify({ name: req })
2582
- );
2583
- } else if (typeof req === "object" && ("name" in req || "id" in req)) {
2584
- res = await fetchTenants(this.#config, "POST", JSON.stringify(req));
2585
- }
2586
- if (rawResponse) {
2587
- return res;
2588
- }
2589
- try {
2590
- return await res?.clone().json();
2591
- } catch {
2592
- return res;
2593
- }
3062
+ return withNileContext(this.#config, async () => {
3063
+ let res;
3064
+ if (typeof req === "string") {
3065
+ res = await fetchTenants(
3066
+ this.#config,
3067
+ "POST",
3068
+ JSON.stringify({ name: req })
3069
+ );
3070
+ } else if (typeof req === "object" && ("name" in req || "id" in req)) {
3071
+ res = await fetchTenants(this.#config, "POST", JSON.stringify(req));
3072
+ }
3073
+ if (rawResponse) {
3074
+ return res;
3075
+ }
3076
+ try {
3077
+ return await res?.clone().json();
3078
+ } catch {
3079
+ return res;
3080
+ }
3081
+ });
2594
3082
  }
3083
+ /**
3084
+ * Remove a tenant via `DELETE /api/tenants/{tenantId}`.
3085
+ *
3086
+ * @param req - The tenant to remove or context containing the id.
3087
+ */
2595
3088
  async delete(req) {
2596
- if (typeof req === "string") {
2597
- this.#config.tenantId = req;
2598
- }
2599
- if (typeof req === "object" && "id" in req) {
2600
- this.#config.tenantId = req.id;
2601
- }
2602
- const res = await fetchTenant(this.#config, "DELETE");
2603
- return res;
3089
+ return withNileContext(this.#config, async () => {
3090
+ if (typeof req === "string") {
3091
+ ctx.set({ tenantId: req });
3092
+ }
3093
+ if (typeof req === "object" && "id" in req) {
3094
+ ctx.set({ tenantId: req.id });
3095
+ }
3096
+ const res = await fetchTenant(this.#config, "DELETE");
3097
+ return res;
3098
+ });
2604
3099
  }
3100
+ /**
3101
+ * Fetch details for a tenant using `GET /api/tenants/{tenantId}`.
3102
+ *
3103
+ * @param req - Tenant identifier or context.
3104
+ * @param [rawResponse] - When true, return the raw {@link Response}.
3105
+ */
2605
3106
  async get(req, rawResponse) {
2606
- if (typeof req === "string") {
2607
- this.#config.tenantId = req;
2608
- } else if (typeof req === "object" && "id" in req) {
2609
- this.#config.tenantId = req.id;
2610
- }
2611
- const res = await fetchTenant(this.#config, "GET");
2612
- if (rawResponse === true || req === true) {
2613
- return res;
2614
- }
2615
- try {
2616
- return await res?.clone().json();
2617
- } catch {
2618
- return res;
2619
- }
3107
+ return withNileContext(this.#config, async () => {
3108
+ if (typeof req === "string") {
3109
+ ctx.set({ tenantId: req });
3110
+ } else if (typeof req === "object" && "id" in req) {
3111
+ ctx.set({ tenantId: req.id });
3112
+ }
3113
+ const res = await fetchTenant(this.#config, "GET");
3114
+ if (rawResponse === true || req === true) {
3115
+ return res;
3116
+ }
3117
+ try {
3118
+ return await res?.clone().json();
3119
+ } catch {
3120
+ return res;
3121
+ }
3122
+ });
2620
3123
  }
2621
3124
  async update(req, rawResponse) {
2622
- let res;
2623
- if (typeof req === "object" && ("name" in req || "id" in req)) {
2624
- const { id, ...remaining } = req;
2625
- if (id) {
2626
- this.#config.tenantId = id;
3125
+ return withNileContext(this.#config, async () => {
3126
+ let res;
3127
+ if (typeof req === "object" && ("name" in req || "id" in req)) {
3128
+ const { id, ...remaining } = req;
3129
+ if (id) {
3130
+ ctx.set({ tenantId: id });
3131
+ }
3132
+ res = await fetchTenant(this.#config, "PUT", JSON.stringify(remaining));
2627
3133
  }
2628
- res = await fetchTenant(this.#config, "PUT", JSON.stringify(remaining));
2629
- }
2630
- if (rawResponse) {
2631
- return res;
2632
- }
2633
- try {
2634
- return await res?.clone().json();
2635
- } catch {
2636
- return res;
2637
- }
3134
+ if (rawResponse) {
3135
+ return res;
3136
+ }
3137
+ try {
3138
+ return await res?.clone().json();
3139
+ } catch {
3140
+ return res;
3141
+ }
3142
+ });
2638
3143
  }
3144
+ /**
3145
+ * List tenants for the current user via `GET /api/tenants`.
3146
+ * See `packages/server/src/api/routes/tenants/GET.ts` for details.
3147
+ */
2639
3148
  async list(req) {
2640
- const res = await fetchTenantsByUser(this.#config);
2641
- if (req === true) {
2642
- return res;
2643
- }
2644
- try {
2645
- return await res?.clone().json();
2646
- } catch {
2647
- return res;
2648
- }
3149
+ return withNileContext(
3150
+ this.#config,
3151
+ async () => {
3152
+ const res = await fetchTenantsByUser(this.#config);
3153
+ if (req === true) {
3154
+ return res;
3155
+ }
3156
+ try {
3157
+ return await res?.clone().json();
3158
+ } catch {
3159
+ return res;
3160
+ }
3161
+ },
3162
+ "listTenants"
3163
+ );
2649
3164
  }
3165
+ /**
3166
+ * Leave the current tenant using `DELETE /api/tenants/{tenantId}/users/{userId}`.
3167
+ *
3168
+ * @param [req] - Optionally specify the tenant id to leave.
3169
+ */
2650
3170
  async leaveTenant(req) {
2651
- const me = await fetchMe(this.#config);
2652
- try {
2653
- const json = await me.json();
2654
- if ("id" in json) {
2655
- this.#config.userId = json.id;
3171
+ return withNileContext(this.#config, async () => {
3172
+ const me = await fetchMe(this.#config);
3173
+ try {
3174
+ const json = await me.json();
3175
+ if ("id" in json) {
3176
+ ctx.set({ userId: json.id });
3177
+ }
3178
+ } catch {
2656
3179
  }
2657
- } catch {
2658
- }
2659
- if (typeof req === "string") {
2660
- this.#config.tenantId = req;
2661
- } else {
2662
- this.#handleContext(req);
2663
- }
2664
- return await fetchTenantUser(this.#config, "DELETE");
3180
+ if (typeof req === "string") {
3181
+ ctx.set({ tenantId: req });
3182
+ } else {
3183
+ this.#handleContext(req);
3184
+ }
3185
+ return await fetchTenantUser(this.#config, "DELETE");
3186
+ });
2665
3187
  }
2666
3188
  async addMember(req, rawResponse) {
2667
- if (typeof req === "string") {
2668
- this.#config.userId = req;
2669
- } else {
2670
- this.#handleContext(req);
2671
- }
2672
- const res = await fetchTenantUser(this.#config, "PUT");
2673
- return responseHandler(res, rawResponse);
3189
+ return withNileContext(this.#config, async () => {
3190
+ if (typeof req === "string") {
3191
+ ctx.set({ userId: req });
3192
+ } else {
3193
+ this.#handleContext(req);
3194
+ }
3195
+ const res = await fetchTenantUser(this.#config, "PUT");
3196
+ return responseHandler(res, rawResponse);
3197
+ });
2674
3198
  }
3199
+ /**
3200
+ * Remove a user from a tenant with `DELETE /api/tenants/{tenantId}/users/{userId}`.
3201
+ *
3202
+ * @param req - User and tenant identifiers or context.
3203
+ * @param [rawResponse] - When true, return the raw {@link Response}.
3204
+ */
2675
3205
  async removeMember(req, rawResponse) {
2676
- this.#handleContext(req);
2677
- const res = await fetchTenantUser(this.#config, "DELETE");
2678
- return responseHandler(res, rawResponse);
3206
+ return withNileContext(this.#config, async () => {
3207
+ this.#handleContext(req);
3208
+ if (typeof req === "string") {
3209
+ ctx.set({ userId: req });
3210
+ }
3211
+ const res = await fetchTenantUser(this.#config, "DELETE");
3212
+ return responseHandler(res, rawResponse);
3213
+ });
2679
3214
  }
2680
3215
  async users(req, rawResponse) {
2681
- this.#handleContext(req);
2682
- const res = await fetchTenantUsers(this.#config, "GET");
2683
- return responseHandler(
2684
- res,
2685
- rawResponse || typeof req === "boolean" && req
3216
+ return withNileContext(
3217
+ this.#config,
3218
+ async () => {
3219
+ this.#handleContext(req);
3220
+ const res = await fetchTenantUsers(this.#config, "GET");
3221
+ return responseHandler(
3222
+ res,
3223
+ rawResponse || typeof req === "boolean" && req
3224
+ );
3225
+ },
3226
+ "users"
2686
3227
  );
2687
3228
  }
3229
+ /**
3230
+ * List invites for the current tenant via `GET /api/tenants/{tenantId}/invites`.
3231
+ */
2688
3232
  async invites() {
2689
- const res = await fetchInvites(this.#config);
2690
- return responseHandler(res);
3233
+ return withNileContext(
3234
+ this.#config,
3235
+ async () => {
3236
+ const res = await fetchInvites(this.#config);
3237
+ return responseHandler(res);
3238
+ },
3239
+ "invites"
3240
+ );
2691
3241
  }
2692
3242
  async invite(req, rawResponse) {
2693
- const { csrfToken } = await obtainCsrf(this.#config);
2694
- const defaults = defaultCallbackUrl3(this.#config);
2695
- let identifier = req;
2696
- let callbackUrl = defaults.callbackUrl;
2697
- let redirectUrl = defaults.redirectUrl;
2698
- if (typeof req === "object") {
2699
- if ("email" in req) {
2700
- identifier = req.email;
2701
- }
2702
- if ("callbackUrl" in req) {
2703
- callbackUrl = req.callbackUrl ? req.callbackUrl : "";
2704
- }
2705
- if ("redirectUrl" in req) {
2706
- redirectUrl = req.redirectUrl ? req.redirectUrl : "";
2707
- }
2708
- }
2709
- this.#config.headers.set(
2710
- "Content-Type",
2711
- "application/x-www-form-urlencoded"
2712
- );
2713
- const res = await fetchInvite(
3243
+ return withNileContext(
2714
3244
  this.#config,
2715
- "POST",
2716
- new URLSearchParams({
2717
- identifier,
2718
- csrfToken,
2719
- callbackUrl,
2720
- redirectUrl
2721
- }).toString()
3245
+ async () => {
3246
+ const { csrfToken } = await obtainCsrf(
3247
+ this.#config
3248
+ );
3249
+ const defaults = defaultCallbackUrl3(this.#config);
3250
+ let identifier = req;
3251
+ let callbackUrl = defaults.callbackUrl;
3252
+ let redirectUrl = defaults.redirectUrl;
3253
+ if (typeof req === "object") {
3254
+ if ("email" in req) {
3255
+ identifier = req.email;
3256
+ }
3257
+ const { callbackUrl: cbUrl } = defaultCallbackUrl3(this.#config);
3258
+ if ("callbackUrl" in req) {
3259
+ callbackUrl = fQUrl2(cbUrl, req.callbackUrl ?? "/");
3260
+ }
3261
+ if ("redirectUrl" in req) {
3262
+ redirectUrl = fQUrl2(cbUrl, req.redirectUrl ?? "/");
3263
+ }
3264
+ }
3265
+ const { headers } = ctx.get();
3266
+ headers?.set("Content-Type", "application/x-www-form-urlencoded");
3267
+ ctx.set({ headers });
3268
+ const res = await fetchInvite(
3269
+ this.#config,
3270
+ "POST",
3271
+ new URLSearchParams({
3272
+ identifier,
3273
+ csrfToken,
3274
+ callbackUrl,
3275
+ redirectUrl
3276
+ }).toString()
3277
+ );
3278
+ return responseHandler(res, rawResponse);
3279
+ },
3280
+ "invites"
2722
3281
  );
2723
- return responseHandler(res, rawResponse);
2724
3282
  }
3283
+ /**
3284
+ * Accept an invite using `PUT /api/tenants/{tenantId}/invite`.
3285
+ *
3286
+ * @param req - Identifier and token from the invite email.
3287
+ * @param [rawResponse] - When true, return the raw {@link Response}.
3288
+ */
2725
3289
  async acceptInvite(req, rawResponse) {
2726
- if (!req) {
2727
- throw new Error("The identifier and token are required.");
2728
- }
2729
- const { identifier, token } = req;
2730
- const defaults = defaultCallbackUrl3(this.#config);
2731
- const callbackUrl = String(defaults.callbackUrl);
2732
- const res = await fetchInvite(
2733
- this.#config,
2734
- "PUT",
2735
- new URLSearchParams({
2736
- identifier,
2737
- token,
2738
- callbackUrl
2739
- }).toString()
2740
- );
2741
- return responseHandler(res, rawResponse);
3290
+ return withNileContext(this.#config, async () => {
3291
+ if (!req) {
3292
+ throw new Error("The identifier and token are required.");
3293
+ }
3294
+ const { identifier, token } = req;
3295
+ const { callbackUrl: cbUrl } = defaultCallbackUrl3(this.#config);
3296
+ const callbackUrl = fQUrl2(cbUrl, req?.callbackUrl ?? "/");
3297
+ const res = await fetchInvite(
3298
+ this.#config,
3299
+ "PUT",
3300
+ new URLSearchParams({
3301
+ identifier,
3302
+ token,
3303
+ callbackUrl
3304
+ }).toString()
3305
+ );
3306
+ return responseHandler(res, rawResponse);
3307
+ });
2742
3308
  }
3309
+ /**
3310
+ * Delete a pending invite using `DELETE /api/tenants/{tenantId}/invite/{inviteId}`.
3311
+ *
3312
+ * @param req - Identifier of the invite to remove.
3313
+ */
2743
3314
  async deleteInvite(req) {
2744
- let id = "";
2745
- if (typeof req === "object") {
2746
- id = req.id;
2747
- } else {
2748
- id = req;
2749
- }
2750
- if (!id) {
2751
- throw new Error("An invite id is required.");
2752
- }
2753
- const res = await fetchInvite(this.#config, "DELETE", id);
2754
- return responseHandler(res, true);
3315
+ return withNileContext(this.#config, async () => {
3316
+ let id = "";
3317
+ if (typeof req === "object") {
3318
+ id = req.id;
3319
+ } else {
3320
+ id = req;
3321
+ }
3322
+ if (!id) {
3323
+ throw new Error("An invite id is required.");
3324
+ }
3325
+ const res = await fetchInvite(this.#config, "DELETE", id);
3326
+ return responseHandler(res, true);
3327
+ });
2755
3328
  }
2756
3329
  #handleContext(req) {
2757
3330
  if (typeof req === "object") {
2758
3331
  if ("tenantId" in req) {
2759
- this.#config.tenantId = req.tenantId;
3332
+ ctx.set({ tenantId: req.tenantId });
2760
3333
  }
2761
3334
  if ("userId" in req) {
2762
- this.#config.tenantId = req.tenantId;
3335
+ ctx.set({ userId: req.userId });
2763
3336
  }
2764
3337
  }
2765
3338
  }
@@ -2777,15 +3350,13 @@ async function responseHandler(res, rawResponse) {
2777
3350
  function defaultCallbackUrl3(config) {
2778
3351
  let cb = null;
2779
3352
  let redirect = null;
2780
- const fallbackCb = parseCallback(config.headers);
3353
+ const { headers, tenantId } = ctx.get();
3354
+ const fallbackCb = parseCallback(headers);
2781
3355
  if (fallbackCb) {
2782
3356
  const [, value] = fallbackCb.split("=");
2783
3357
  cb = decodeURIComponent(value);
2784
3358
  if (value) {
2785
- redirect = `${new URL(cb).origin}${config.routePrefix}${"/tenants/{tenantId}/invite" /* INVITE */.replace(
2786
- "{tenantId}",
2787
- String(config.tenantId)
2788
- )}`;
3359
+ redirect = `${new URL(cb).origin}${config.routePrefix}${"/tenants/{tenantId}/invite" /* INVITE */.replace("{tenantId}", String(tenantId))}`;
2789
3360
  }
2790
3361
  }
2791
3362
  return { callbackUrl: cb, redirectUrl: redirect };
@@ -2823,90 +3394,34 @@ function handlersWithContext(config) {
2823
3394
  function updateConfig(response, config) {
2824
3395
  let origin = "http://localhost:3000";
2825
3396
  let headers = null;
2826
- if (response?.status === 302) {
2827
- const location = response.headers.get("location");
2828
- if (location) {
2829
- const urlLocation = new URL(location);
2830
- origin = urlLocation.origin;
3397
+ if (response instanceof Response) {
3398
+ if (response?.status === 302) {
3399
+ const location = response.headers.get("location");
3400
+ if (location) {
3401
+ const urlLocation = new URL(location);
3402
+ origin = urlLocation.origin;
3403
+ }
2831
3404
  }
2832
- }
2833
- const setCookies = [];
2834
- if (response?.headers) {
2835
- for (const [key17, value] of response.headers) {
2836
- if (key17.toLowerCase() === "set-cookie") {
2837
- setCookies.push(value);
3405
+ const setCookies = [];
3406
+ if (response?.headers) {
3407
+ for (const [key17, value] of response.headers) {
3408
+ if (key17.toLowerCase() === "set-cookie") {
3409
+ setCookies.push(value);
3410
+ }
2838
3411
  }
2839
3412
  }
2840
- }
2841
- if (setCookies.length > 0) {
2842
- const cookieHeader = setCookies.map((cookieStr) => cookieStr.split(";")[0]).join("; ");
2843
- headers = new Headers({ cookie: cookieHeader });
3413
+ if (setCookies.length > 0) {
3414
+ const cookieHeader = setCookies.map((cookieStr) => cookieStr.split(";")[0]).join("; ");
3415
+ headers = new Headers({ cookie: cookieHeader });
3416
+ }
2844
3417
  }
2845
3418
  return {
2846
3419
  ...config,
2847
3420
  origin,
2848
3421
  headers: headers ?? void 0,
2849
- preserveHeaders: true
2850
- };
2851
- }
2852
-
2853
- // src/api/utils/extensions.ts
2854
- function bindHandleOnRequest(instance) {
2855
- return async function handleOnRequest(config, _init, params) {
2856
- const { debug } = config.logger("[EXTENSIONS]");
2857
- if (config.extensions) {
2858
- for (const create2 of config.extensions) {
2859
- if (typeof create2 !== "function") {
2860
- return void 0;
2861
- }
2862
- const ext = await create2(instance);
2863
- if (ext.onRequest) {
2864
- const previousContext = instance.getContext();
2865
- if (previousContext.preserveHeaders) {
2866
- instance.setContext({ preserveHeaders: false });
2867
- }
2868
- await ext.onRequest(_init.request);
2869
- const updatedContext = instance.getContext();
2870
- if (updatedContext?.headers) {
2871
- const cookie = updatedContext.headers.get("cookie");
2872
- if (cookie) {
2873
- params.headers.set(
2874
- "cookie",
2875
- mergeCookies(
2876
- previousContext.preserveHeaders ? previousContext.headers?.get("cookie") : null,
2877
- updatedContext.headers.get("cookie")
2878
- )
2879
- );
2880
- }
2881
- if (updatedContext.tenantId) {
2882
- params.headers.set(
2883
- TENANT_COOKIE,
2884
- String(updatedContext.headers.get(TENANT_COOKIE))
2885
- );
2886
- }
2887
- }
2888
- debug(`${ext.id ?? create2.name} ran onRequest`);
2889
- }
2890
- }
2891
- }
3422
+ useLastContext: true
2892
3423
  };
2893
3424
  }
2894
- function buildExtensionConfig(instance) {
2895
- return {
2896
- handleOnRequest: bindHandleOnRequest(instance)
2897
- };
2898
- }
2899
- function mergeCookies(...cookieStrings) {
2900
- const cookieMap = /* @__PURE__ */ new Map();
2901
- for (const str of cookieStrings) {
2902
- if (!str) continue;
2903
- for (const part of str.split(";")) {
2904
- const [key17, value] = part.split("=").map((s) => s.trim());
2905
- if (key17 && value) cookieMap.set(key17, value);
2906
- }
2907
- }
2908
- return [...cookieMap.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
2909
- }
2910
3425
 
2911
3426
  // src/Server.ts
2912
3427
  var Server = class {
@@ -2915,149 +3430,164 @@ var Server = class {
2915
3430
  auth;
2916
3431
  #config;
2917
3432
  #handlers;
2918
- #paths;
2919
3433
  #manager;
2920
- #headers;
2921
- #preserveHeaders;
2922
3434
  constructor(config) {
2923
3435
  this.#config = new Config({
2924
3436
  ...config,
2925
3437
  extensionCtx: buildExtensionConfig(this)
2926
3438
  });
2927
3439
  watchTenantId((tenantId) => {
2928
- if (tenantId !== this.#config.tenantId) {
2929
- this.#config.tenantId = tenantId;
3440
+ if (tenantId !== this.#config.context.tenantId) {
3441
+ this.#config.context.tenantId = String(tenantId);
2930
3442
  this.#reset();
2931
3443
  }
2932
3444
  });
2933
3445
  watchUserId((userId) => {
2934
- if (userId !== this.#config.userId) {
2935
- this.#config.userId = userId;
3446
+ if (userId !== this.#config.context.userId) {
3447
+ this.#config.context.userId = String(userId);
2936
3448
  this.#reset();
2937
3449
  }
2938
3450
  });
2939
3451
  watchHeaders((headers) => {
2940
- this.setContext(headers);
2941
- this.#reset();
3452
+ if (headers) {
3453
+ this.#config.context.headers = new Headers(headers);
3454
+ this.#reset();
3455
+ }
2942
3456
  });
2943
3457
  this.#handlers = {
2944
3458
  ...this.#config.handlers,
2945
3459
  withContext: handlersWithContext(this.#config)
2946
3460
  };
2947
- this.#preserveHeaders = config?.preserveHeaders ?? false;
2948
- this.#paths = this.#config.paths;
2949
- this.#config.tenantId = getTenantId({ config: this.#config });
3461
+ this.#config.context.tenantId = getTenantId({ config: this.#config });
2950
3462
  this.#manager = new DBManager(this.#config);
2951
3463
  this.#handleHeaders(config);
2952
3464
  this.users = new Users(this.#config);
2953
3465
  this.tenants = new Tenants(this.#config);
2954
3466
  this.auth = new Auth(this.#config);
3467
+ if (config?.extensions) {
3468
+ for (const create2 of config.extensions) {
3469
+ if (typeof create2 !== "function") {
3470
+ continue;
3471
+ }
3472
+ const ext = create2(this);
3473
+ if (ext.onConfigure) {
3474
+ ext.onConfigure();
3475
+ }
3476
+ if (ext?.replace?.handlers) {
3477
+ this.#config.logger("[EXTENSION]").debug(`${ext.id} replacing handlers`);
3478
+ this.#handlers = ext.replace.handlers({
3479
+ ...this.#config.handlers,
3480
+ withContext: handlersWithContext(this.#config)
3481
+ });
3482
+ }
3483
+ }
3484
+ }
2955
3485
  }
2956
- get db() {
3486
+ /**
3487
+ * Query the database with the current context
3488
+ */
3489
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3490
+ query = (queryStream, values) => {
3491
+ this.#config.context = { ...this.getContext() };
2957
3492
  const pool = this.#manager.getConnection(this.#config);
3493
+ return pool.query(queryStream, values);
3494
+ };
3495
+ /**
3496
+ * Return a db object that can be used to talk to the database
3497
+ * Does not have a context by default
3498
+ */
3499
+ get db() {
3500
+ const pool = this.#manager.getConnection(this.#config, true);
2958
3501
  return Object.assign(pool, {
2959
3502
  clearConnections: () => {
2960
3503
  this.#manager.clear(this.#config);
2961
3504
  }
2962
3505
  });
2963
3506
  }
2964
- /**
2965
- * A convenience function that applies a config and ensures whatever was passed is set properly
2966
- */
2967
- getInstance(config, req) {
2968
- const _config = { ...this.#config, ...config };
2969
- const updatedConfig = new Config(_config);
2970
- this.#config = new Config(updatedConfig);
2971
- this.#config.tenantId = config.tenantId;
2972
- this.#config.userId = config.userId;
2973
- if (req) {
2974
- this.setContext(req);
2975
- }
2976
- this.#reset();
2977
- return this;
3507
+ get logger() {
3508
+ return this.#config.logger;
2978
3509
  }
2979
- getPaths() {
2980
- return this.#paths;
3510
+ get extensions() {
3511
+ return {
3512
+ remove: async (id) => {
3513
+ if (!this.#config.extensions) return;
3514
+ const resolved = this.#config.extensions.map((ext) => ext(this));
3515
+ const index = resolved.findIndex((ext) => ext.id === id);
3516
+ if (index !== -1) {
3517
+ this.#config.extensions.splice(index, 1);
3518
+ }
3519
+ return resolved;
3520
+ },
3521
+ add: (extension) => {
3522
+ if (!this.#config.extensions) {
3523
+ this.#config.extensions = [];
3524
+ }
3525
+ this.#config.extensions.push(extension);
3526
+ }
3527
+ };
2981
3528
  }
2982
3529
  get handlers() {
2983
3530
  return this.#handlers;
2984
3531
  }
2985
- /**
2986
- * Allow the setting of headers from a req or header object.
2987
- * Makes it possible to handle REST requests easily
2988
- * Also makes it easy to set user + tenant in some way
2989
- * @param req
2990
- * @returns undefined
2991
- */
2992
- setContext(req) {
2993
- try {
2994
- if (req instanceof Headers) {
2995
- this.#handleHeaders(req);
2996
- this.#reset();
2997
- return;
2998
- } else if (req instanceof Request) {
2999
- this.#handleHeaders(new Headers(req.headers));
3000
- this.#reset();
3001
- return;
3002
- }
3003
- } catch {
3004
- }
3005
- let ok = false;
3006
- if (req && typeof req === "object" && "tenantId" in req) {
3007
- ok = true;
3008
- this.#config.tenantId = req.tenantId;
3009
- }
3010
- if (req && typeof req === "object" && "userId" in req) {
3011
- ok = true;
3012
- this.#config.userId = req.userId;
3013
- }
3014
- if (req && typeof req === "object" && "preserveHeaders" in req) {
3015
- ok = true;
3016
- this.#preserveHeaders = Boolean(req.preserveHeaders);
3017
- }
3018
- if (ok) {
3019
- return;
3532
+ get paths() {
3533
+ return this.#config.paths;
3534
+ }
3535
+ set paths(paths) {
3536
+ this.#config.paths = paths;
3537
+ }
3538
+ async withContext(contextOrFn, maybeFn) {
3539
+ const isFn = typeof contextOrFn === "function";
3540
+ const context = isFn ? {} : contextOrFn ?? {};
3541
+ const fn = isFn ? contextOrFn : maybeFn;
3542
+ const preserve = "useLastContext" in context ? context.useLastContext : true;
3543
+ if (preserve) {
3544
+ this.#config.context = { ...this.getContext(), ...context };
3545
+ } else {
3546
+ this.#config.context = { ...defaultContext, ...context };
3020
3547
  }
3021
- if (typeof req === "object") {
3022
- const headers = new Headers(req);
3023
- if (headers) {
3024
- this.#handleHeaders(headers);
3025
- this.#reset();
3026
- return;
3548
+ return withNileContext(this.#config, async () => {
3549
+ return fn ? fn(this) : this;
3550
+ });
3551
+ }
3552
+ async noContext(fn) {
3553
+ this.#config.context.tenantId = void 0;
3554
+ this.#config.context.userId = void 0;
3555
+ return withNileContext(this.#config, async () => {
3556
+ ctx.set({ userId: void 0, tenantId: void 0 });
3557
+ if (fn) {
3558
+ return fn(this);
3027
3559
  }
3028
- }
3029
- const { warn } = Logger(this.#config)("[API]");
3030
- if (warn) {
3031
- warn(
3032
- "Set context expects a Request, Header instance or an object of Record<string, string>"
3033
- );
3034
- }
3560
+ return this;
3561
+ });
3035
3562
  }
3563
+ /**
3564
+ *
3565
+ * @returns the last used (basically global) context object, useful for debugging or making your own context
3566
+ */
3036
3567
  getContext() {
3037
- return {
3038
- headers: this.#headers,
3039
- userId: this.#config.userId,
3040
- tenantId: this.#config.tenantId,
3041
- preserveHeaders: this.#preserveHeaders
3042
- };
3568
+ return ctx.getLastUsed();
3043
3569
  }
3044
3570
  /**
3045
3571
  * Merge headers together
3572
+ * Saves them in a singleton for use in a request later. It's basically the "default" value
3046
3573
  * Internally, passed a NileConfig, externally, should be using Headers
3047
3574
  */
3048
3575
  #handleHeaders(config) {
3049
3576
  const updates = [];
3050
3577
  let headers;
3051
- this.#headers = new Headers();
3578
+ this.#config.context.headers = new Headers();
3052
3579
  if (config instanceof Headers) {
3053
3580
  headers = config;
3054
3581
  } else if (config?.headers) {
3055
3582
  headers = config?.headers;
3056
3583
  if (config && config.origin) {
3057
- this.#headers.set(HEADER_ORIGIN, config.origin);
3584
+ this.#config.context.headers.set(HEADER_ORIGIN, config.origin);
3058
3585
  }
3059
3586
  if (config && config.secureCookies != null) {
3060
- this.#headers.set(HEADER_SECURE_COOKIES, String(config.secureCookies));
3587
+ this.#config.context.headers.set(
3588
+ HEADER_SECURE_COOKIES,
3589
+ String(config.secureCookies)
3590
+ );
3061
3591
  }
3062
3592
  }
3063
3593
  if (headers instanceof Headers) {
@@ -3070,8 +3600,7 @@ var Server = class {
3070
3600
  }
3071
3601
  }
3072
3602
  const merged = {};
3073
- this.#config.tenantId = getTenantFromHttp(this.#headers, this.#config);
3074
- this.#headers?.forEach((value, key17) => {
3603
+ this.#config.context.headers?.forEach((value, key17) => {
3075
3604
  if (key17.toLowerCase() !== "cookie") {
3076
3605
  merged[key17.toLowerCase()] = value;
3077
3606
  }
@@ -3080,16 +3609,15 @@ var Server = class {
3080
3609
  merged[key17] = value;
3081
3610
  }
3082
3611
  for (const [key17, value] of Object.entries(merged)) {
3083
- this.#headers.set(key17, value);
3612
+ this.#config.context.headers.set(key17, value);
3084
3613
  }
3085
3614
  this.#config.logger("[handleHeaders]").debug(JSON.stringify(merged));
3086
- this.#config.headers = this.#headers;
3087
3615
  }
3088
3616
  /**
3089
3617
  * Allow some internal mutations to reset our config + headers
3090
3618
  */
3091
3619
  #reset = () => {
3092
- this.#config.headers = this.#headers ?? new Headers();
3620
+ this.#config.extensionCtx = buildExtensionConfig(this);
3093
3621
  this.users = new Users(this.#config);
3094
3622
  this.tenants = new Tenants(this.#config);
3095
3623
  this.auth = new Auth(this.#config);
@@ -3104,6 +3632,7 @@ function create(config) {
3104
3632
  }
3105
3633
 
3106
3634
  exports.APIErrorErrorCodeEnum = APIErrorErrorCodeEnum;
3635
+ exports.ExtensionState = ExtensionState;
3107
3636
  exports.HEADER_ORIGIN = HEADER_ORIGIN;
3108
3637
  exports.HEADER_SECURE_COOKIES = HEADER_SECURE_COOKIES;
3109
3638
  exports.LoginUserResponseTokenTypeEnum = LoginUserResponseTokenTypeEnum;
@@ -3113,6 +3642,8 @@ exports.TENANT_COOKIE = TENANT_COOKIE;
3113
3642
  exports.USER_COOKIE = USER_COOKIE;
3114
3643
  exports.parseCSRF = parseCSRF;
3115
3644
  exports.parseCallback = parseCallback;
3645
+ exports.parseResetToken = parseResetToken;
3646
+ exports.parseTenantId = parseTenantId;
3116
3647
  exports.parseToken = parseToken;
3117
3648
  //# sourceMappingURL=index.js.map
3118
3649
  //# sourceMappingURL=index.js.map