@niledatabase/server 5.0.3-alpha.0 → 5.0.3-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -304,8 +304,13 @@ function mergeCookies(...cookieStrings) {
304
304
  }
305
305
  return [...cookieMap.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
306
306
  }
307
- async function runExtensionContext(config) {
308
- await config?.extensionCtx?.runExtensions("withContext" /* withContext */, config);
307
+ async function runExtensionContext(config, options) {
308
+ if (!options?.skipWithContext) {
309
+ await config?.extensionCtx?.runExtensions(
310
+ "withContext" /* withContext */,
311
+ config
312
+ );
313
+ }
309
314
  await config?.extensionCtx?.runExtensions(
310
315
  "withTenantId" /* withTenantId */,
311
316
  config
@@ -375,38 +380,55 @@ var ctx = {
375
380
  // for convenience only
376
381
  getLastUsed: () => lastUsedContext
377
382
  };
378
- function withNileContext(config, fn, name = "unknown") {
383
+ async function withNileContext(config, fn, name = "unknown") {
379
384
  const initialContext = config.context;
380
385
  const existing = ctx.get();
381
- const mergedHeaders = new Headers(existing.headers);
386
+ const { overrides: extensionOverrides, ran: extensionRan } = await resolveExtensionOverrides(config, existing);
387
+ let mergedHeaders = new Headers(existing.headers);
388
+ let tenantId = existing.tenantId;
389
+ let userId = existing.userId;
382
390
  if (initialContext instanceof Request) {
383
391
  initialContext.headers.forEach((value, key17) => {
384
392
  mergedHeaders.set(key17, value);
385
393
  });
386
- const context2 = {
387
- headers: mergedHeaders,
388
- tenantId: existing.tenantId,
389
- userId: existing.userId
390
- };
391
- silly(`${name} [INITIAL - Request] ${serializeContext(context2)}`);
392
- return ctx.run(context2, fn);
394
+ } else {
395
+ if (initialContext.headers === null) {
396
+ mergedHeaders = new Headers();
397
+ } else if (initialContext.headers) {
398
+ const incoming = initialContext.headers instanceof Headers ? initialContext.headers : new Headers(initialContext.headers);
399
+ incoming.forEach((value, key17) => {
400
+ mergedHeaders.set(key17, value);
401
+ });
402
+ }
403
+ if ("tenantId" in initialContext) {
404
+ tenantId = initialContext.tenantId;
405
+ }
406
+ if ("userId" in initialContext) {
407
+ userId = initialContext.userId;
408
+ }
393
409
  }
394
- if (initialContext.headers) {
395
- const incoming = initialContext.headers instanceof Headers ? initialContext.headers : new Headers(initialContext.headers);
396
- incoming.forEach((value, key17) => {
410
+ if (extensionOverrides?.headers) {
411
+ for (const key17 of extensionOverrides.headers.removed) {
412
+ mergedHeaders.delete(key17);
413
+ }
414
+ for (const [key17, value] of extensionOverrides.headers.set) {
397
415
  mergedHeaders.set(key17, value);
398
- });
416
+ }
417
+ }
418
+ if (extensionOverrides?.tenantId) {
419
+ tenantId = extensionOverrides.tenantId.value;
420
+ }
421
+ if (extensionOverrides?.userId) {
422
+ userId = extensionOverrides.userId.value;
399
423
  }
400
- const hasTenantId = "tenantId" in initialContext;
401
- const hasUserId = "userId" in initialContext;
402
424
  const context = {
403
425
  headers: mergedHeaders,
404
- tenantId: hasTenantId ? initialContext.tenantId : existing.tenantId,
405
- userId: hasUserId ? initialContext.userId : existing.userId
426
+ tenantId,
427
+ userId
406
428
  };
407
- silly(`${name} [INITIAL - Partial<Context>] ${serializeContext(context)}`);
429
+ silly(`${name} [INITIAL] ${serializeContext(context)}`);
408
430
  return ctx.run(context, async () => {
409
- await runExtensionContext(config);
431
+ await runExtensionContext(config, { skipWithContext: extensionRan });
410
432
  return fn();
411
433
  });
412
434
  }
@@ -432,10 +454,81 @@ function parseCookieHeader(header) {
432
454
  function serializeCookies(cookies) {
433
455
  return Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join("; ");
434
456
  }
457
+ async function resolveExtensionOverrides(config, existing) {
458
+ if (!config.extensions?.length || !config.extensionCtx) {
459
+ return { ran: false };
460
+ }
461
+ let updated;
462
+ await ctx.run(
463
+ {
464
+ headers: new Headers(existing.headers),
465
+ tenantId: existing.tenantId,
466
+ userId: existing.userId
467
+ },
468
+ async () => {
469
+ await config.extensionCtx?.runExtensions(
470
+ "withContext" /* withContext */,
471
+ config
472
+ );
473
+ updated = ctx.get();
474
+ }
475
+ );
476
+ if (!updated) {
477
+ return { ran: true };
478
+ }
479
+ const diff = diffContext(existing, updated);
480
+ return { overrides: diff, ran: true };
481
+ }
482
+ function diffContext(before, after) {
483
+ const headers = diffHeaders(before.headers, after.headers);
484
+ const tenantChanged = before.tenantId !== after.tenantId;
485
+ const userChanged = before.userId !== after.userId;
486
+ if (!headers && !tenantChanged && !userChanged) {
487
+ return void 0;
488
+ }
489
+ const overrides = {};
490
+ if (headers) {
491
+ overrides.headers = headers;
492
+ }
493
+ if (tenantChanged) {
494
+ overrides.tenantId = { value: after.tenantId };
495
+ }
496
+ if (userChanged) {
497
+ overrides.userId = { value: after.userId };
498
+ }
499
+ return overrides;
500
+ }
501
+ function diffHeaders(before, after) {
502
+ const beforeMap = headersToMap(before);
503
+ const afterMap = headersToMap(after);
504
+ const set = [];
505
+ const removed = [];
506
+ for (const [key17, value] of afterMap.entries()) {
507
+ if (beforeMap.get(key17) !== value) {
508
+ set.push([key17, value]);
509
+ }
510
+ }
511
+ for (const key17 of beforeMap.keys()) {
512
+ if (!afterMap.has(key17)) {
513
+ removed.push(key17);
514
+ }
515
+ }
516
+ if (set.length === 0 && removed.length === 0) {
517
+ return void 0;
518
+ }
519
+ return { set, removed };
520
+ }
521
+ function headersToMap(headers) {
522
+ const map = /* @__PURE__ */ new Map();
523
+ headers.forEach((value, key17) => {
524
+ map.set(key17.toLowerCase(), value);
525
+ });
526
+ return map;
527
+ }
435
528
 
436
529
  // src/api/utils/request.ts
437
530
  async function request(url, _init, config) {
438
- const { debug, info, error, warn: warn2 } = config.logger("[REQUEST]");
531
+ const { debug, info, error } = config.logger("[REQUEST]");
439
532
  const { request: request2, ...init } = _init;
440
533
  const requestUrl = new URL(request2.url);
441
534
  const updatedHeaders = new Headers({});
@@ -2411,79 +2504,89 @@ var Auth = class {
2411
2504
  * from the internal configuration once the request completes.
2412
2505
  */
2413
2506
  async signOut() {
2414
- return withNileContext(this.#config, async () => {
2415
- const csrfRes = await this.getCsrf();
2416
- if (!("csrfToken" in csrfRes)) {
2417
- throw new Error("Unable to obtain CSRF token. Sign out failed.");
2418
- }
2419
- const body = JSON.stringify({
2420
- csrfToken: csrfRes.csrfToken,
2421
- json: true
2422
- });
2423
- const res = await fetchSignOut(this.#config, body);
2424
- updateHeaders(new Headers({}));
2425
- ctx.set({ headers: null });
2426
- return res;
2427
- });
2507
+ return withNileContext(
2508
+ this.#config,
2509
+ async () => {
2510
+ const csrfRes = await this.getCsrf();
2511
+ if (!("csrfToken" in csrfRes)) {
2512
+ throw new Error("Unable to obtain CSRF token. Sign out failed.");
2513
+ }
2514
+ const body = JSON.stringify({
2515
+ csrfToken: csrfRes.csrfToken,
2516
+ json: true
2517
+ });
2518
+ const res = await fetchSignOut(this.#config, body);
2519
+ updateHeaders(new Headers({}));
2520
+ ctx.set({ headers: null });
2521
+ return res;
2522
+ },
2523
+ "signout"
2524
+ );
2428
2525
  }
2429
2526
  async signUp(payload, rawResponse) {
2430
- return withNileContext(this.#config, async () => {
2431
- ctx.set({ headers: null });
2432
- const { email, password, ...params } = payload;
2433
- if (!email || !password) {
2434
- throw new Error(
2435
- "Server side sign up requires a user email and password."
2436
- );
2437
- }
2438
- const providers = await this.listProviders();
2439
- const { credentials } = providers ?? {};
2440
- if (!credentials) {
2441
- throw new Error(
2442
- "Unable to obtain credential provider. Aborting server side sign up."
2443
- );
2444
- }
2445
- const csrf = await obtainCsrf(this.#config);
2446
- let csrfToken;
2447
- if ("csrfToken" in csrf) {
2448
- csrfToken = csrf.csrfToken;
2449
- } else {
2450
- throw new Error("Unable to obtain parse CSRF. Request blocked.");
2451
- }
2452
- const body = JSON.stringify({
2453
- email,
2454
- password,
2455
- csrfToken,
2456
- callbackUrl: credentials.callbackUrl
2457
- });
2458
- const res = await fetchSignUp(this.#config, { body, params });
2459
- if (res.status > 299) {
2460
- this.#logger.error(await res.clone().text());
2461
- return void 0;
2462
- }
2463
- const token = parseToken(res.headers);
2464
- if (!token) {
2465
- throw new Error("Server side sign up failed. Session token not found");
2466
- }
2467
- const { headers } = ctx.get();
2468
- headers?.append("cookie", token);
2469
- ctx.set({ headers });
2470
- updateHeaders(headers);
2471
- if (rawResponse) {
2472
- return res;
2473
- }
2474
- try {
2475
- const json = await res.clone().json();
2476
- if (json && typeof json === "object" && "tenants" in json) {
2477
- const tenantId = json.tenants[0];
2478
- if (tenantId) {
2479
- updateTenantId(tenantId);
2527
+ return withNileContext(
2528
+ this.#config,
2529
+ async () => {
2530
+ ctx.set({ headers: null });
2531
+ const { email, password, ...params } = payload;
2532
+ if (!email || !password) {
2533
+ throw new Error(
2534
+ "Server side sign up requires a user email and password."
2535
+ );
2536
+ }
2537
+ const providers = await this.listProviders();
2538
+ const { credentials } = providers ?? {};
2539
+ if (!credentials) {
2540
+ throw new Error(
2541
+ "Unable to obtain credential provider. Aborting server side sign up."
2542
+ );
2543
+ }
2544
+ const csrf = await obtainCsrf(this.#config);
2545
+ let csrfToken;
2546
+ if ("csrfToken" in csrf) {
2547
+ csrfToken = csrf.csrfToken;
2548
+ } else {
2549
+ throw new Error("Unable to obtain parse CSRF. Request blocked.");
2550
+ }
2551
+ const body = JSON.stringify({
2552
+ email,
2553
+ password,
2554
+ csrfToken,
2555
+ callbackUrl: credentials.callbackUrl
2556
+ });
2557
+ const res = await fetchSignUp(this.#config, { body, params });
2558
+ if (res.status > 299) {
2559
+ this.#logger.error(await res.clone().text());
2560
+ return void 0;
2561
+ }
2562
+ const token = parseToken(res.headers);
2563
+ if (!token) {
2564
+ throw new Error(
2565
+ "Server side sign up failed. Session token not found"
2566
+ );
2567
+ }
2568
+ const { headers } = ctx.get();
2569
+ headers?.append("cookie", token);
2570
+ ctx.set({ headers });
2571
+ updateHeaders(headers);
2572
+ if (rawResponse) {
2573
+ return res;
2574
+ }
2575
+ try {
2576
+ const json = await res.clone().json();
2577
+ if (json && typeof json === "object" && "tenants" in json) {
2578
+ const tenantId = json.tenants[0];
2579
+ if (tenantId) {
2580
+ updateTenantId(tenantId);
2581
+ }
2480
2582
  }
2583
+ return json;
2584
+ } catch {
2585
+ return res;
2481
2586
  }
2482
- return json;
2483
- } catch {
2484
- return res;
2485
- }
2486
- });
2587
+ },
2588
+ "signup"
2589
+ );
2487
2590
  }
2488
2591
  /**
2489
2592
  * Request a password reset email.
@@ -2966,17 +3069,21 @@ var Users = class {
2966
3069
  });
2967
3070
  }
2968
3071
  async getSelf(rawResponse) {
2969
- return withNileContext(this.#config, async () => {
2970
- const res = await fetchMe(this.#config);
2971
- if (rawResponse) {
2972
- return res;
2973
- }
2974
- try {
2975
- return await res?.clone().json();
2976
- } catch {
2977
- return res;
2978
- }
2979
- });
3072
+ return withNileContext(
3073
+ this.#config,
3074
+ async () => {
3075
+ const res = await fetchMe(this.#config);
3076
+ if (rawResponse) {
3077
+ return res;
3078
+ }
3079
+ try {
3080
+ return await res?.clone().json();
3081
+ } catch {
3082
+ return res;
3083
+ }
3084
+ },
3085
+ "getSelf"
3086
+ );
2980
3087
  }
2981
3088
  async verifySelf(options, rawResponse = false) {
2982
3089
  return withNileContext(this.#config, async () => {
@@ -3540,8 +3647,11 @@ var Server = class {
3540
3647
  const context = isFn ? {} : contextOrFn ?? {};
3541
3648
  const fn = isFn ? contextOrFn : maybeFn;
3542
3649
  const preserve = "useLastContext" in context ? context.useLastContext : true;
3650
+ let hydrated;
3543
3651
  if (preserve) {
3544
- this.#config.context = { ...this.getContext(), ...context };
3652
+ hydrated = await this.#hydrateContextFromExtensions();
3653
+ const base = hydrated ?? this.#config.context;
3654
+ this.#config.context = { ...base, ...context };
3545
3655
  } else {
3546
3656
  this.#config.context = { ...defaultContext, ...context };
3547
3657
  }
@@ -3613,6 +3723,31 @@ var Server = class {
3613
3723
  }
3614
3724
  this.#config.logger("[handleHeaders]").debug(JSON.stringify(merged));
3615
3725
  }
3726
+ async #hydrateContextFromExtensions() {
3727
+ if (!this.#config.extensions || this.#config.extensions.length === 0) {
3728
+ return void 0;
3729
+ }
3730
+ let updated;
3731
+ await ctx.run({}, async () => {
3732
+ await this.#config.extensionCtx?.runExtensions(
3733
+ "withContext" /* withContext */,
3734
+ this.#config
3735
+ );
3736
+ updated = ctx.get();
3737
+ });
3738
+ if (!updated) {
3739
+ return void 0;
3740
+ }
3741
+ const hydrated = {
3742
+ headers: new Headers(updated.headers),
3743
+ tenantId: updated.tenantId,
3744
+ userId: updated.userId
3745
+ };
3746
+ this.#config.context.headers = new Headers(hydrated.headers);
3747
+ this.#config.context.tenantId = hydrated.tenantId;
3748
+ this.#config.context.userId = hydrated.userId;
3749
+ return hydrated;
3750
+ }
3616
3751
  /**
3617
3752
  * Allow some internal mutations to reset our config + headers
3618
3753
  */