@niledatabase/server 5.0.0-alpha.9 → 5.0.0

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