@niledatabase/server 5.0.0-alpha.3 → 5.0.0-alpha.31

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