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

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