@webjsdev/server 0.8.91 → 0.10.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/README.md +4 -1
- package/index.d.ts +461 -102
- package/index.js +9 -4
- package/package.json +3 -3
- package/src/action-flash.js +185 -0
- package/src/action-input.js +208 -0
- package/src/action-params.js +156 -0
- package/src/action-route.js +8 -9
- package/src/action-seed.js +44 -5
- package/src/actions.js +112 -15
- package/src/api.js +67 -10
- package/src/app-layout.js +133 -0
- package/src/asset-hash.js +82 -49
- package/src/auth.js +612 -108
- package/src/browser-entries.js +2 -17
- package/src/build-epoch.js +1 -1
- package/src/cache-tags.js +5 -1
- package/src/check/helpers.js +85 -0
- package/src/check/rules-actions.js +37 -1
- package/src/check/rules-components.js +133 -28
- package/src/check/rules-imports.js +175 -4
- package/src/check/rules-route-modules.js +200 -0
- package/src/check/rules-routes.js +55 -0
- package/src/check/rules-routing.js +143 -95
- package/src/check/rules-typescript.js +24 -2
- package/src/check/rules.js +109 -9
- package/src/check/runner-support.js +53 -171
- package/src/check/runner.js +24 -6
- package/src/client-signals.js +171 -0
- package/src/component-scanner.js +22 -16
- package/src/context.js +152 -16
- package/src/cookie-store.js +262 -0
- package/src/core-exports.js +190 -0
- package/src/dev/bun-app-source.js +95 -5
- package/src/dev/config.js +7 -51
- package/src/dev/dev-service-worker.js +48 -0
- package/src/dev/dev-status.js +122 -0
- package/src/dev/esm-hot-hooks.js +373 -0
- package/src/dev/handler.js +632 -155
- package/src/dev/helpers.js +127 -96
- package/src/dev/hot-host.js +33 -6
- package/src/dev/poke-state.js +110 -0
- package/src/dev/serve.js +191 -133
- package/src/dev/server.js +109 -37
- package/src/dev/specifier-versions.js +125 -0
- package/src/dev/watch-ignore.js +49 -4
- package/src/dev/watch-poll.js +78 -0
- package/src/dev/watch-tree.js +59 -22
- package/src/dev-classify.js +59 -26
- package/src/dev-error.js +104 -3
- package/src/dev-hot-classify.js +291 -0
- package/src/dev-hot-client.js +245 -0
- package/src/dev-import.js +22 -5
- package/src/dev-overlay.js +26 -0
- package/src/dev-ready-probe.js +69 -0
- package/src/dev-regenerate.js +322 -78
- package/src/dev-reload-hold.js +49 -20
- package/src/dev-reload-reason.js +61 -0
- package/src/dev-reload-worker.js +62 -13
- package/src/dev-secret.js +73 -0
- package/src/dev-source-locations-hook.js +4 -1
- package/src/dev-source-locations.js +33 -2
- package/src/dev-styles.js +64 -6
- package/src/dev.js +0 -1
- package/src/env-files.js +124 -0
- package/src/file-storage.js +2 -2
- package/src/foreign-imports.js +228 -0
- package/src/form-action-identity.js +52 -25
- package/src/form-dispatch.js +150 -69
- package/src/html-cache.js +35 -4
- package/src/importmap.js +2 -2
- package/src/instrumentation.js +59 -3
- package/src/js-scan.js +5 -4
- package/src/jwe.js +155 -0
- package/src/lazy-components.js +166 -0
- package/src/listener-core.js +4 -2
- package/src/lit-hints.js +172 -0
- package/src/middleware-load.js +248 -0
- package/src/module-graph.js +73 -47
- package/src/module-side-effect.js +284 -0
- package/src/public-files.js +73 -0
- package/src/redirect-location.js +19 -0
- package/src/redirects.js +9 -1
- package/src/route-frontier.js +151 -0
- package/src/route-report.js +130 -0
- package/src/router.js +120 -13
- package/src/session.js +32 -10
- package/src/ssr/client-router-flag.js +1 -1
- package/src/ssr/document.js +49 -17
- package/src/ssr/head.js +228 -25
- package/src/ssr/preloads.js +31 -45
- package/src/ssr/render.js +243 -55
- package/src/ssr/responses.js +51 -4
- package/src/ssr/shipped-modules.js +52 -16
- package/src/testing.d.ts +31 -2
- package/src/testing.js +85 -17
- package/src/thenable-params.js +26 -0
- package/src/vendor/jspm.js +12 -1
- package/src/vendor/pins.js +9 -8
- package/src/vendor/scanner.js +14 -9
- package/src/vendor.js +5 -3
- package/webjs-config.schema.json +3 -8
- package/src/component-elision.js +0 -1852
- package/src/elision-differential.js +0 -101
- package/src/elision-report.js +0 -193
package/index.d.ts
CHANGED
|
@@ -34,6 +34,13 @@ export * from './src/testing.js';
|
|
|
34
34
|
|
|
35
35
|
/** A webjs middleware: receives the request + a `next()` continuation. */
|
|
36
36
|
export type Middleware = (req: Request, next: () => Promise<Response>) => Promise<Response> | Response;
|
|
37
|
+
/**
|
|
38
|
+
* The request an app's `middleware.ts` / `proxy.ts` (and a segment middleware)
|
|
39
|
+
* receives: the platform `Request` plus Next's `request.cookies`, the same
|
|
40
|
+
* store `cookies()` returns. Annotate with it where Next.js code writes
|
|
41
|
+
* `NextRequest`.
|
|
42
|
+
*/
|
|
43
|
+
export type MiddlewareRequest = Request & { readonly cookies: MiddlewareRequestCookies };
|
|
37
44
|
|
|
38
45
|
// `Handle` is re-exported from ./src/testing.js (the `export *` above), so it
|
|
39
46
|
// is not re-declared here. It is IMPORTED as well, because `export *` re-exports
|
|
@@ -45,16 +52,49 @@ export type Middleware = (req: Request, next: () => Promise<Response>) => Promis
|
|
|
45
52
|
* Mirrors AGENTS.md's documented shape; `@webjsdev/core` does not export it,
|
|
46
53
|
* so it is defined here (the one server-owned shared type).
|
|
47
54
|
*/
|
|
48
|
-
export type ActionResult<T =
|
|
49
|
-
| {
|
|
55
|
+
export type ActionResult<T = any> =
|
|
56
|
+
| {
|
|
57
|
+
success: true;
|
|
58
|
+
data?: T;
|
|
59
|
+
redirect?: string;
|
|
60
|
+
message?: string;
|
|
61
|
+
// The failure fields, absent here, so a read before narrowing on
|
|
62
|
+
// `success` (`result.error ?? result.fieldErrors?.body`) type-checks.
|
|
63
|
+
error?: undefined;
|
|
64
|
+
errors?: undefined;
|
|
65
|
+
fieldErrors?: undefined;
|
|
66
|
+
values?: undefined;
|
|
67
|
+
status?: undefined;
|
|
68
|
+
}
|
|
50
69
|
| {
|
|
51
70
|
success: false;
|
|
52
71
|
error?: string;
|
|
72
|
+
/** Next.js's `{ errors }` spelling, read as a failure like `fieldErrors`. */
|
|
73
|
+
errors?: Record<string, string | string[]>;
|
|
74
|
+
message?: string;
|
|
53
75
|
fieldErrors?: Record<string, string>;
|
|
54
76
|
values?: Record<string, string>;
|
|
55
77
|
status?: number;
|
|
56
78
|
};
|
|
57
79
|
|
|
80
|
+
/**
|
|
81
|
+
* What a form-bound action receives: a real `FormData` (so `get`, `getAll` and
|
|
82
|
+
* `instanceof FormData` work, as in Next) that also carries each field as a
|
|
83
|
+
* property, so `input.title` reads it too. A `name[]` field arrives as an array
|
|
84
|
+
* under `name`; a file input arrives as a `File`. Pass the field shape for
|
|
85
|
+
* typed property reads.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* 'use server';
|
|
89
|
+
* import type { FormInput } from '@webjsdev/server';
|
|
90
|
+
* export async function createTask(input: FormInput<{ title: string; tags: string[] }>) {
|
|
91
|
+
* const title = input.title.trim(); // or input.get('title')
|
|
92
|
+
* ...
|
|
93
|
+
* }
|
|
94
|
+
*/
|
|
95
|
+
export type FormInput<Fields extends Record<string, unknown> = Record<string, string | string[] | File>> =
|
|
96
|
+
FormData & Fields;
|
|
97
|
+
|
|
58
98
|
/** The pluggable cache store interface (`memoryStore` / `redisStore` / custom). */
|
|
59
99
|
export interface CacheStore {
|
|
60
100
|
get(key: string): Promise<string | null>;
|
|
@@ -248,6 +288,14 @@ export declare function matchApi(
|
|
|
248
288
|
table: unknown,
|
|
249
289
|
pathname: string,
|
|
250
290
|
): { route: { file: string }; params: Record<string, string> } | null;
|
|
291
|
+
/**
|
|
292
|
+
* The URL paths of every STATIC page route in a route table, sorted and
|
|
293
|
+
* deduped. Dynamic routes are excluded (listing one would mean inventing
|
|
294
|
+
* param values).
|
|
295
|
+
*/
|
|
296
|
+
export declare function staticPageRoutes(table: {
|
|
297
|
+
pages?: Array<{ routeDir?: string; paramNames?: string[] }>;
|
|
298
|
+
} | undefined): string[];
|
|
251
299
|
|
|
252
300
|
// ---------------------------------------------------------------------------
|
|
253
301
|
// route-types.js (#258)
|
|
@@ -342,8 +390,7 @@ export declare function setVendorEntries(
|
|
|
342
390
|
/**
|
|
343
391
|
* Bare-specifier npm imports an app could load in the browser, found by walking
|
|
344
392
|
* the module graph from every browser-bound entry rather than by scanning the
|
|
345
|
-
* directory.
|
|
346
|
-
* running server serves.
|
|
393
|
+
* directory. The result is a superset of what the running server serves.
|
|
347
394
|
*/
|
|
348
395
|
export declare function scanBareImports(dir: string): Promise<Set<string>>;
|
|
349
396
|
/** Extract the package name from an import specifier (`dayjs/plugin/utc` -> `dayjs`). */
|
|
@@ -492,8 +539,8 @@ export declare function extractComponents(src: string): Array<{ className: strin
|
|
|
492
539
|
* because there is no registration call anywhere or because the tag is
|
|
493
540
|
* computed. The registration cross-reference is app-wide, so a class a sibling
|
|
494
541
|
* module registers is not an orphan. Neither is visible to the
|
|
495
|
-
* scanner, so both always lose the
|
|
496
|
-
*
|
|
542
|
+
* scanner, so both always lose the tag-to-module registry entry, the boot import
|
|
543
|
+
* and the preload hint; assume the element does not upgrade either.
|
|
497
544
|
* This is about REGISTRATION, never about import reachability. The full
|
|
498
545
|
* semantics, including the one case where a computed tag still upgrades, live
|
|
499
546
|
* on `findOrphanComponents` in `src/component-scanner.js`.
|
|
@@ -503,110 +550,170 @@ export declare function findOrphanComponents(
|
|
|
503
550
|
): Promise<Array<{ className: string; file: string }>>;
|
|
504
551
|
|
|
505
552
|
// ---------------------------------------------------------------------------
|
|
506
|
-
//
|
|
553
|
+
// route-report.js (#1686)
|
|
507
554
|
// ---------------------------------------------------------------------------
|
|
508
555
|
|
|
509
|
-
/**
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
export interface
|
|
514
|
-
/** App-relative path of the component module. */
|
|
515
|
-
file: string;
|
|
516
|
-
/** Every tag this file registers, sorted. */
|
|
517
|
-
tags: string[];
|
|
518
|
-
verdict: 'elided' | 'shipped';
|
|
519
|
-
evidence: ElisionEvidence | null;
|
|
520
|
-
reason: string | null;
|
|
521
|
-
/** App-relative path of the module that forced the ship, where one did. */
|
|
522
|
-
by: string | null;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/** One page/layout route module's verdict. */
|
|
526
|
-
export interface ElisionRouteModuleRow {
|
|
556
|
+
/**
|
|
557
|
+
* One route module (page, layout or boundary). A route module never ships to
|
|
558
|
+
* the browser; the boot imports `emits`, its component frontier, in its place.
|
|
559
|
+
*/
|
|
560
|
+
export interface RouteModuleRow {
|
|
527
561
|
file: string;
|
|
528
|
-
|
|
529
|
-
/**
|
|
562
|
+
kind: 'page' | 'layout' | 'error' | 'loading' | 'not-found' | 'forbidden' | 'unauthorized' | 'global-not-found';
|
|
563
|
+
/** The component modules the boot imports in this module's place (empty: zero JS). */
|
|
530
564
|
emits: string[];
|
|
531
|
-
/**
|
|
532
|
-
|
|
533
|
-
reason: string | null;
|
|
565
|
+
/** Advisory notes on code that looks meant for the browser but runs only on the server. */
|
|
566
|
+
hints: string[];
|
|
534
567
|
}
|
|
535
568
|
|
|
536
569
|
/** A `class X extends WebComponent` with no literal-tag registration. */
|
|
537
|
-
export interface
|
|
570
|
+
export interface OrphanRow {
|
|
538
571
|
file: string;
|
|
539
572
|
className: string;
|
|
540
573
|
}
|
|
541
574
|
|
|
542
|
-
export interface
|
|
543
|
-
components: number;
|
|
544
|
-
elided: number;
|
|
545
|
-
shipped: number;
|
|
575
|
+
export interface RouteReportSummary {
|
|
546
576
|
routeModules: number;
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
577
|
+
/** Route modules whose frontier is empty: the browser loads nothing for them. */
|
|
578
|
+
zeroJs: number;
|
|
579
|
+
/** Route modules whose frontier names at least one component. */
|
|
580
|
+
carriers: number;
|
|
550
581
|
orphans: number;
|
|
551
582
|
}
|
|
552
583
|
|
|
553
|
-
/**
|
|
554
|
-
export interface
|
|
584
|
+
/** What each route costs the browser. Every path is app-relative; every array is sorted by `file`. */
|
|
585
|
+
export interface RouteReport {
|
|
555
586
|
analysed: boolean;
|
|
556
587
|
/** Why nothing was analysed, when `analysed` is false. */
|
|
557
|
-
skipped: 'no-app' | '
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
summary: ElisionSummary;
|
|
588
|
+
skipped: 'no-app' | 'unanalysable' | null;
|
|
589
|
+
routeModules: RouteModuleRow[];
|
|
590
|
+
orphans: OrphanRow[];
|
|
591
|
+
summary: RouteReportSummary;
|
|
562
592
|
}
|
|
563
593
|
|
|
564
594
|
/**
|
|
565
|
-
*
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
* shipped one naming the first client-effecting blocker), and every orphan
|
|
569
|
-
* component class that gets no verdict at all. A reporting layer over
|
|
570
|
-
* `analyzeElision`, never a second analysis and never a build artifact.
|
|
571
|
-
* Consumed by `webjs elision`, the MCP `list_elision` tool, and both `webjs
|
|
572
|
-
* doctor` elision checks.
|
|
595
|
+
* Every route module with the components the boot loads in its place and any
|
|
596
|
+
* hints about browser-looking code in it, plus every orphan component class.
|
|
597
|
+
* Consumed by both `webjs doctor` route checks.
|
|
573
598
|
*/
|
|
574
|
-
export declare function
|
|
599
|
+
export declare function analyzeRouteModules(appDir: string): Promise<RouteReport>;
|
|
575
600
|
|
|
576
601
|
// ---------------------------------------------------------------------------
|
|
577
|
-
//
|
|
602
|
+
// dev-regenerate.js (#967, #1672)
|
|
578
603
|
// ---------------------------------------------------------------------------
|
|
579
604
|
|
|
580
605
|
/**
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
606
|
+
* Where a `webjs.dev.regenerate` output stands against the build record in
|
|
607
|
+
* `.webjs/regenerate.json`: `fresh` (built by this rule's command from inputs
|
|
608
|
+
* that have not changed since, and not rewritten since), `stale`, `missing`
|
|
609
|
+
* (no output on disk), or `unrecorded` (an output no recorded build produced,
|
|
610
|
+
* such as one compiled by hand).
|
|
586
611
|
*/
|
|
587
|
-
export
|
|
612
|
+
export type RegenerateState = 'fresh' | 'stale' | 'missing' | 'unrecorded';
|
|
613
|
+
|
|
614
|
+
/** One rule's outcome from `regenerateOutputs`. */
|
|
615
|
+
export type RegenerateResult = { output: string; state: RegenerateState; built: boolean } & (
|
|
616
|
+
| { ok: true }
|
|
617
|
+
| { ok: false; reason: 'failed' | 'no-output' }
|
|
618
|
+
);
|
|
588
619
|
|
|
589
620
|
/**
|
|
590
|
-
*
|
|
591
|
-
*
|
|
592
|
-
*
|
|
621
|
+
* Build every `webjs.dev.regenerate` output that is not fresh, one at a time,
|
|
622
|
+
* and record what each was built from (#1672). The engine behind
|
|
623
|
+
* `webjs regenerate`, which an app runs in `dev.before` / `start.before` so a
|
|
624
|
+
* build outside the dev server records freshness the way a dev request does.
|
|
625
|
+
* A rule whose command fails, or exits 0 without writing its `output`, keeps
|
|
626
|
+
* its previous output and is reported `ok: false`.
|
|
593
627
|
*/
|
|
594
|
-
export declare function
|
|
595
|
-
|
|
596
|
-
|
|
628
|
+
export declare function regenerateOutputs(
|
|
629
|
+
appDir: string,
|
|
630
|
+
opts?: { force?: boolean; warn?: (message: string) => void },
|
|
631
|
+
): Promise<RegenerateResult[]>;
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Every `webjs.dev.regenerate` output's state against the build record,
|
|
635
|
+
* building nothing. What `webjs doctor` reads, so it and the dev server share
|
|
636
|
+
* one definition of fresh.
|
|
637
|
+
*/
|
|
638
|
+
export declare function regenerateStatus(
|
|
639
|
+
appDir: string,
|
|
640
|
+
): Promise<Array<{ output: string; command: string; state: RegenerateState }>>;
|
|
597
641
|
|
|
598
642
|
// ---------------------------------------------------------------------------
|
|
599
643
|
// context.js (per-request context helpers)
|
|
600
644
|
// ---------------------------------------------------------------------------
|
|
601
645
|
|
|
646
|
+
/**
|
|
647
|
+
* The in-flight request's headers, read-only as Next.js's `ReadonlyHeaders`:
|
|
648
|
+
* `set`, `append` and `delete` throw at runtime and are absent from the type.
|
|
649
|
+
*/
|
|
650
|
+
export type ReadonlyHeaders = Omit<Headers, 'set' | 'append' | 'delete'>;
|
|
602
651
|
/** Read-only headers for the in-flight request. Throws outside a request scope. */
|
|
603
|
-
export declare function headers():
|
|
604
|
-
/**
|
|
605
|
-
export
|
|
606
|
-
|
|
652
|
+
export declare function headers(): ReadonlyHeaders;
|
|
653
|
+
/** A cookie as read from the request. */
|
|
654
|
+
export interface RequestCookie { name: string; value: string }
|
|
655
|
+
/** A cookie to write (Next's `ResponseCookie`; `maxAge` in seconds, `path` defaults to `/`). */
|
|
656
|
+
export interface ResponseCookie {
|
|
657
|
+
name: string;
|
|
658
|
+
value: string;
|
|
659
|
+
expires?: Date | number;
|
|
660
|
+
maxAge?: number;
|
|
661
|
+
domain?: string;
|
|
662
|
+
path?: string;
|
|
663
|
+
secure?: boolean;
|
|
664
|
+
httpOnly?: boolean;
|
|
665
|
+
sameSite?: 'lax' | 'strict' | 'none' | 'Lax' | 'Strict' | 'None' | boolean;
|
|
666
|
+
partitioned?: boolean;
|
|
667
|
+
priority?: 'low' | 'medium' | 'high';
|
|
668
|
+
}
|
|
669
|
+
/** The request's cookie store (Next.js's `cookies()` shape). */
|
|
670
|
+
/**
|
|
671
|
+
* Next.js's `RequestCookies`, `request.cookies` in middleware and a route
|
|
672
|
+
* handler. Reads are the request's cookies; `set`, `delete` and `clear` change
|
|
673
|
+
* what the rest of this request reads (the page's `cookies()` included) and
|
|
674
|
+
* send nothing to the browser. Set a response cookie with `cookies().set()`.
|
|
675
|
+
*/
|
|
676
|
+
export interface MiddlewareRequestCookies {
|
|
677
|
+
get(name: string | { name: string }): RequestCookie | undefined;
|
|
678
|
+
getAll(name?: string | { name: string }): RequestCookie[];
|
|
607
679
|
has(name: string): boolean;
|
|
608
|
-
|
|
609
|
-
|
|
680
|
+
readonly size: number;
|
|
681
|
+
set(name: string, value: string): MiddlewareRequestCookies;
|
|
682
|
+
set(cookie: RequestCookie): MiddlewareRequestCookies;
|
|
683
|
+
delete(names: string): boolean;
|
|
684
|
+
delete(names: string[]): boolean[];
|
|
685
|
+
clear(): MiddlewareRequestCookies;
|
|
686
|
+
toString(): string;
|
|
687
|
+
[Symbol.iterator](): IterableIterator<[string, RequestCookie]>;
|
|
688
|
+
}
|
|
689
|
+
export interface RequestCookies {
|
|
690
|
+
get(name: string | { name: string }): RequestCookie | undefined;
|
|
691
|
+
getAll(name?: string | { name: string }): RequestCookie[];
|
|
692
|
+
has(name: string): boolean;
|
|
693
|
+
readonly size: number;
|
|
694
|
+
set(name: string, value: string, options?: Omit<ResponseCookie, 'name' | 'value'>): RequestCookies;
|
|
695
|
+
set(cookie: ResponseCookie): RequestCookies;
|
|
696
|
+
delete(name: string | { name: string; path?: string; domain?: string }): RequestCookies;
|
|
697
|
+
toString(): string;
|
|
698
|
+
[Symbol.iterator](): IterableIterator<[string, RequestCookie]>;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* The in-flight request's cookies, Next.js's `cookies()`: `cookies().get('x')?.value`
|
|
702
|
+
* reads, `set` / `delete` write a `Set-Cookie` onto this request's response. Also
|
|
703
|
+
* awaitable (`(await cookies()).get('x')`). Throws outside a request scope.
|
|
704
|
+
*
|
|
705
|
+
* `get` returns a {@link RequestCookie} (`{ name, value }`) or `undefined`, as
|
|
706
|
+
* in Next.js, never the bare string.
|
|
707
|
+
*
|
|
708
|
+
* @example
|
|
709
|
+
* ```ts
|
|
710
|
+
* import { cookies } from '@webjsdev/server';
|
|
711
|
+
* const theme: string = cookies().get('theme')?.value ?? 'light';
|
|
712
|
+
* cookies().set('theme', 'dark', { path: '/', maxAge: 60 * 60 * 24 * 365, httpOnly: true });
|
|
713
|
+
* cookies().delete('theme');
|
|
714
|
+
* ```
|
|
715
|
+
*/
|
|
716
|
+
export declare function cookies(): RequestCookies & Promise<RequestCookies>;
|
|
610
717
|
/** The raw `Request` for the in-flight scope, or null at module top-level. */
|
|
611
718
|
export declare function getRequest(): Request | null;
|
|
612
719
|
/** Run `fn` within a request scope. */
|
|
@@ -745,8 +852,13 @@ export declare function cache<T extends (...args: any[]) => Promise<any>>(
|
|
|
745
852
|
// cache-tags.js (#242) + html-cache.js (#241): server cache invalidation
|
|
746
853
|
// ---------------------------------------------------------------------------
|
|
747
854
|
|
|
748
|
-
/**
|
|
749
|
-
|
|
855
|
+
/**
|
|
856
|
+
* Evict every `cache()` entry tagged with `tag`. Takes Next.js 16's second
|
|
857
|
+
* argument, `revalidateTag(tag, 'max')` (a cache-life profile, or
|
|
858
|
+
* `{ expire }`), for source compatibility: WebJs evicts at once whatever the
|
|
859
|
+
* profile, since `cache()` has no stale-while-revalidate window to honour.
|
|
860
|
+
*/
|
|
861
|
+
export declare function revalidateTag(tag: string, profile?: string | { expire?: number }): Promise<void>;
|
|
750
862
|
/** Evict every `cache()` entry tagged with any of `tags`. */
|
|
751
863
|
export declare function revalidateTags(tags: string[]): Promise<void>;
|
|
752
864
|
/**
|
|
@@ -759,7 +871,7 @@ export declare function revalidateTags(tags: string[]): Promise<void>;
|
|
|
759
871
|
* `webjs.basePath` the absolute url is the PUBLIC one and its mount prefix is
|
|
760
872
|
* stripped, so it evicts the entry the write actually stored.
|
|
761
873
|
*/
|
|
762
|
-
export declare function revalidatePath(path: string): Promise<void>;
|
|
874
|
+
export declare function revalidatePath(path: string, type?: 'page' | 'layout'): Promise<void>;
|
|
763
875
|
/** Evict all cached HTML (per-process generation bump). */
|
|
764
876
|
export declare function revalidateAll(): Promise<void>;
|
|
765
877
|
|
|
@@ -785,12 +897,34 @@ export declare class Session {
|
|
|
785
897
|
set(key: string, value: unknown): void;
|
|
786
898
|
has(key: string): boolean;
|
|
787
899
|
unset(key: string): void;
|
|
788
|
-
/** Set a value
|
|
900
|
+
/** Set a value that lasts until a `get()` reads it (Remix's flash); `has()` does not consume it. */
|
|
789
901
|
flash(key: string, value: unknown): void;
|
|
790
902
|
destroy(): void;
|
|
903
|
+
/**
|
|
904
|
+
* Give the session a new id, keeping its data. Call it after a login to
|
|
905
|
+
* prevent session fixation; `deleteOld` also deletes the old id's stored data.
|
|
906
|
+
*/
|
|
907
|
+
regenerateId(deleteOld?: boolean): void;
|
|
791
908
|
}
|
|
792
909
|
|
|
793
|
-
/**
|
|
910
|
+
/**
|
|
911
|
+
* Session middleware factory: a signed session cookie for apps that do not use
|
|
912
|
+
* `createAuth`. It throws at import without `secret` or `SESSION_SECRET`.
|
|
913
|
+
*
|
|
914
|
+
* @example
|
|
915
|
+
* ```ts
|
|
916
|
+
* // middleware.ts
|
|
917
|
+
* import { session } from '@webjsdev/server';
|
|
918
|
+
* export default session({ secret: process.env.SESSION_SECRET });
|
|
919
|
+
*
|
|
920
|
+
* // In a 'use server' action or a page:
|
|
921
|
+
* import { getRequest, getSession } from '@webjsdev/server';
|
|
922
|
+
* const s = getSession(getRequest()!);
|
|
923
|
+
* s.regenerateId(true); // after a login, against session fixation
|
|
924
|
+
* s.set('userId', user.id);
|
|
925
|
+
* const userId = s.get<string>('userId');
|
|
926
|
+
* ```
|
|
927
|
+
*/
|
|
794
928
|
export declare function session(opts?: {
|
|
795
929
|
storage?: SessionStorage;
|
|
796
930
|
cookieName?: string;
|
|
@@ -809,8 +943,26 @@ export declare function storeSessionStorage(opts?: Record<string, unknown>): Ses
|
|
|
809
943
|
export declare const cookieSession: typeof cookieSessionStorage;
|
|
810
944
|
/** Alias of `storeSessionStorage`. */
|
|
811
945
|
export declare const storeSession: typeof storeSessionStorage;
|
|
812
|
-
/**
|
|
813
|
-
|
|
946
|
+
/**
|
|
947
|
+
* The session for the current request. With no argument it reads the request
|
|
948
|
+
* in scope (as `cookies()` and `headers()` do); passing `getRequest()` also works.
|
|
949
|
+
* Needs `session()` as root middleware; throws outside it.
|
|
950
|
+
*
|
|
951
|
+
* @example
|
|
952
|
+
* ```ts
|
|
953
|
+
* // middleware.ts
|
|
954
|
+
* import { session } from '@webjsdev/server';
|
|
955
|
+
* export default session(); // SESSION_SECRET from .env
|
|
956
|
+
*
|
|
957
|
+
* // in a page, action or route handler
|
|
958
|
+
* import { getSession } from '@webjsdev/server';
|
|
959
|
+
* const s = getSession();
|
|
960
|
+
* s.set('cart', [1, 2]); // persists across requests
|
|
961
|
+
* s.flash('notice', 'Saved'); // read once by the next request
|
|
962
|
+
* const notice = s.get('notice'); // unknown; narrow it before use
|
|
963
|
+
* ```
|
|
964
|
+
*/
|
|
965
|
+
export declare function getSession(req?: Request | null): Session;
|
|
814
966
|
|
|
815
967
|
// ---------------------------------------------------------------------------
|
|
816
968
|
// broadcast.js
|
|
@@ -827,9 +979,14 @@ export declare function broadcast(
|
|
|
827
979
|
// file-storage.js (#247)
|
|
828
980
|
// ---------------------------------------------------------------------------
|
|
829
981
|
|
|
830
|
-
/**
|
|
982
|
+
/**
|
|
983
|
+
* A streaming handle returned by `FileStore.get` (body is a stream, never
|
|
984
|
+
* buffered). The body is a web `ReadableStream`, so `new Response(file.body)`
|
|
985
|
+
* serves it as is; an adapter whose SDK returns a Node stream converts it with
|
|
986
|
+
* `Readable.toWeb`.
|
|
987
|
+
*/
|
|
831
988
|
export interface StoredObjectHandle {
|
|
832
|
-
body: ReadableStream
|
|
989
|
+
body: ReadableStream<Uint8Array>;
|
|
833
990
|
size: number;
|
|
834
991
|
contentType: string;
|
|
835
992
|
}
|
|
@@ -863,7 +1020,26 @@ export declare const DEFAULT_UPLOAD_DIR: string;
|
|
|
863
1020
|
export declare function diskStore(opts?: { dir?: string; baseUrl?: string }): FileStore;
|
|
864
1021
|
/** Set the default file store (call at startup to use a custom dir or S3). */
|
|
865
1022
|
export declare function setFileStore(store: FileStore): void;
|
|
866
|
-
/**
|
|
1023
|
+
/**
|
|
1024
|
+
* Get the default file store (`diskStore` under `<cwd>/.webjs/uploads` unless set).
|
|
1025
|
+
*
|
|
1026
|
+
* @example
|
|
1027
|
+
* ```ts
|
|
1028
|
+
* // In a 'use server' action bound to <form action=${save} enctype="multipart/form-data">
|
|
1029
|
+
* // with <input type="file" name="photo">: the field arrives as a File.
|
|
1030
|
+
* import { getFileStore, generateKey } from '@webjsdev/server';
|
|
1031
|
+
* const key = generateKey(input.photo.name); // opaque, traversal-safe
|
|
1032
|
+
* await getFileStore().put(key, input.photo); // streams to disk
|
|
1033
|
+
*
|
|
1034
|
+
* // app/uploads/[key]/route.ts serves it back:
|
|
1035
|
+
* export async function GET(_req: Request, { params }: { params: { key: string } }) {
|
|
1036
|
+
* let file;
|
|
1037
|
+
* try { file = await getFileStore().get(params.key); } catch { file = null; } // an unsafe key throws
|
|
1038
|
+
* if (!file) return new Response('Not found', { status: 404 });
|
|
1039
|
+
* return new Response(file.body, { headers: { 'content-type': file.contentType } });
|
|
1040
|
+
* }
|
|
1041
|
+
* ```
|
|
1042
|
+
*/
|
|
867
1043
|
export declare function getFileStore(): FileStore;
|
|
868
1044
|
/** Generate a random, opaque, traversal-safe key, preserving a whitelisted extension. */
|
|
869
1045
|
export declare function generateKey(filename?: string): string;
|
|
@@ -1039,13 +1215,102 @@ export type ResolvedAuthUser = keyof AuthUser extends never
|
|
|
1039
1215
|
: AuthUser;
|
|
1040
1216
|
|
|
1041
1217
|
/** Auth configuration for `createAuth`. */
|
|
1218
|
+
/**
|
|
1219
|
+
* The Auth.js adapter methods `createAuth` calls, the shape `@auth/*-adapter`
|
|
1220
|
+
* packages implement (`@auth/core/adapters`).
|
|
1221
|
+
*/
|
|
1222
|
+
export interface AuthAdapter {
|
|
1223
|
+
createUser(user: Record<string, any>): Promise<any> | any;
|
|
1224
|
+
getUser?(id: string): Promise<any> | any;
|
|
1225
|
+
getUserByEmail(email: string): Promise<any> | any;
|
|
1226
|
+
getUserByAccount(account: { provider: string; providerAccountId: string }): Promise<any> | any;
|
|
1227
|
+
updateUser?(user: Record<string, any>): Promise<any> | any;
|
|
1228
|
+
linkAccount(account: Record<string, any>): Promise<any> | any;
|
|
1229
|
+
createSession(session: { sessionToken: string; userId: string; expires: Date }): Promise<any> | any;
|
|
1230
|
+
getSessionAndUser(sessionToken: string): Promise<{ session: { sessionToken: string; userId: string; expires: Date }; user: any } | null> | { session: any; user: any } | null;
|
|
1231
|
+
updateSession?(session: { sessionToken: string; expires?: Date }): Promise<any> | any;
|
|
1232
|
+
deleteSession(sessionToken: string): Promise<any> | any;
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* The token the `jwt` and `session` callbacks see, Auth.js's `JWT`: the
|
|
1236
|
+
* standard claims plus whatever the `jwt` callback put on it.
|
|
1237
|
+
*/
|
|
1238
|
+
export interface AuthJWT {
|
|
1239
|
+
sub?: string;
|
|
1240
|
+
name?: string | null;
|
|
1241
|
+
email?: string | null;
|
|
1242
|
+
image?: string | null;
|
|
1243
|
+
iat?: number;
|
|
1244
|
+
exp?: number;
|
|
1245
|
+
[key: string]: unknown;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
/** The user the callbacks see, Auth.js's `User`: what `authorize` or the provider returned. */
|
|
1249
|
+
export interface AuthCallbackUser {
|
|
1250
|
+
id?: string;
|
|
1251
|
+
name?: string | null;
|
|
1252
|
+
email?: string | null;
|
|
1253
|
+
image?: string | null;
|
|
1254
|
+
[key: string]: unknown;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
/** The session the `session` callback receives and returns, Auth.js's `Session`. */
|
|
1258
|
+
export interface AuthCallbackSession {
|
|
1259
|
+
user: ResolvedAuthUser & { name?: string | null; email?: string | null; image?: string | null };
|
|
1260
|
+
expires: string;
|
|
1261
|
+
[key: string]: unknown;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
type AuthAwaitable<T> = T | Promise<T>;
|
|
1265
|
+
|
|
1266
|
+
/**
|
|
1267
|
+
* Auth.js's callbacks, with the parameters Auth.js passes, so a callback
|
|
1268
|
+
* written as Auth.js documents it (`jwt({ token, user })`,
|
|
1269
|
+
* `session({ session, token })`) is typed with no annotation. Declared as
|
|
1270
|
+
* methods so a callback annotated with its own narrower types still compiles.
|
|
1271
|
+
*/
|
|
1272
|
+
export interface AuthCallbacks {
|
|
1273
|
+
/** At sign-in (`trigger: 'signIn'`, with `user`, `account` and `profile`) and on every session read. */
|
|
1274
|
+
jwt?(params: { token: AuthJWT; user: AuthCallbackUser; account?: Record<string, any> | null; profile?: Record<string, any>; trigger?: 'signIn' | 'signUp' | 'update'; session?: any }): AuthAwaitable<AuthJWT | null>;
|
|
1275
|
+
/** On every session read. `token` under the jwt strategy, `user` under the database strategy. */
|
|
1276
|
+
session?(params: { session: AuthCallbackSession; token: AuthJWT; user: AuthCallbackUser }): AuthAwaitable<any>;
|
|
1277
|
+
/** Return true to allow, a url string to redirect there instead, anything else to deny (Auth.js's rule). */
|
|
1278
|
+
signIn?(params: { user: AuthCallbackUser; account?: Record<string, any> | null; profile?: Record<string, any>; credentials?: Record<string, any> }): AuthAwaitable<boolean | string>;
|
|
1279
|
+
redirect?(params: { url: string; baseUrl: string }): AuthAwaitable<string>;
|
|
1280
|
+
/** Run by `auth` as middleware: true continues, false redirects to sign in, a Response is sent as is. */
|
|
1281
|
+
authorized?: (params: { auth: { user: Record<string, unknown> } | null; request: Request }) => boolean | Response | Promise<boolean | Response>;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1042
1284
|
export interface AuthConfig {
|
|
1043
|
-
providers:
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1285
|
+
/** A provider config, or the provider function uncalled (`providers: [GitHub]`), as Auth.js accepts. */
|
|
1286
|
+
providers: Array<ProviderConfig | (() => ProviderConfig)>;
|
|
1287
|
+
/**
|
|
1288
|
+
* `strategy` defaults to `'database'` when an Auth.js adapter is given, else
|
|
1289
|
+
* `'jwt'`. `updateAge` (seconds, default a day) is how often a database
|
|
1290
|
+
* session's expiry is pushed forward, as in Auth.js.
|
|
1291
|
+
*/
|
|
1292
|
+
session?: { strategy?: 'jwt' | 'database'; maxAge?: number; updateAge?: number };
|
|
1293
|
+
/** Signs the session. Defaults to `process.env.AUTH_SECRET`, as in Auth.js. */
|
|
1294
|
+
secret?: string | undefined;
|
|
1295
|
+
callbacks?: AuthCallbacks;
|
|
1296
|
+
/**
|
|
1297
|
+
* An Auth.js database adapter (`DrizzleAdapter(db)` from `@auth/drizzle-adapter`,
|
|
1298
|
+
* and the rest), or WebJs's own session store for the database strategy.
|
|
1299
|
+
*/
|
|
1300
|
+
adapter?: AuthAdapter | { load?: Function; save?: Function; destroy?: Function };
|
|
1048
1301
|
pages?: { signIn?: string; signOut?: string; error?: string };
|
|
1302
|
+
/**
|
|
1303
|
+
* Auth.js's `useSecureCookies`: force the Secure flag and the `__Host-`
|
|
1304
|
+
* cookie names on or off. Unset, they follow the auth URL's protocol
|
|
1305
|
+
* (`AUTH_URL`'s when set, else the request's), never `NODE_ENV`.
|
|
1306
|
+
*/
|
|
1307
|
+
useSecureCookies?: boolean;
|
|
1308
|
+
/**
|
|
1309
|
+
* Where the auth routes are mounted, as Auth.js's `basePath`. Defaults to
|
|
1310
|
+
* the path of `AUTH_URL` when it has one, else `/api/auth`. `AUTH_URL` (or
|
|
1311
|
+
* `NEXTAUTH_URL`) also sets the origin OAuth callback URLs use.
|
|
1312
|
+
*/
|
|
1313
|
+
basePath?: string;
|
|
1049
1314
|
}
|
|
1050
1315
|
|
|
1051
1316
|
/**
|
|
@@ -1056,26 +1321,120 @@ export interface AuthConfig {
|
|
|
1056
1321
|
* via `createAuth<MyUser>(...)`.
|
|
1057
1322
|
*/
|
|
1058
1323
|
export interface AuthInstance<TUser = ResolvedAuthUser> {
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1324
|
+
/**
|
|
1325
|
+
* Auth.js's `auth`, in its three shapes: `auth()` / `auth(req)` reads the
|
|
1326
|
+
* session; `export { auth as middleware }` runs as middleware (it sets
|
|
1327
|
+
* `req.auth` and applies `callbacks.authorized`); `auth((req) => ...)` wraps
|
|
1328
|
+
* a middleware whose `req.auth` holds the session.
|
|
1329
|
+
*/
|
|
1330
|
+
auth: {
|
|
1331
|
+
(req?: Request | null): Promise<{ user: TUser } | null>;
|
|
1332
|
+
(req: Request, next: unknown): Promise<Response | undefined>;
|
|
1333
|
+
<R>(handler: (req: Request & { auth: { user: TUser } | null }, ...rest: any[]) => R): (req: Request, ...rest: any[]) => Promise<Response | Awaited<R>>;
|
|
1334
|
+
};
|
|
1335
|
+
/**
|
|
1336
|
+
* Auth.js v5's `signIn`. Inside a request (a server action, a route handler,
|
|
1337
|
+
* middleware) it sets the session cookie on the response and throws a
|
|
1338
|
+
* redirect, so `await signIn('credentials', formData)` in a form action signs
|
|
1339
|
+
* in and navigates; a credentials failure throws {@link CredentialsSignin}.
|
|
1340
|
+
* `{ redirect: false }` (in the options or, as Auth.js accepts it, in the
|
|
1341
|
+
* data) returns the target url instead of throwing. With no request in scope
|
|
1342
|
+
* it returns the Response the `/api/auth/*` routes send. The redirecting
|
|
1343
|
+
* form is typed `Promise<Response>`, so `return signIn(...)` fits an action
|
|
1344
|
+
* that returns `ActionResult | Response`.
|
|
1345
|
+
*/
|
|
1346
|
+
signIn: {
|
|
1347
|
+
(
|
|
1348
|
+
provider: string,
|
|
1349
|
+
data: (FormData | Record<string, unknown>) | undefined,
|
|
1350
|
+
opts: { redirectTo?: string; redirect: false; req?: Request },
|
|
1351
|
+
): Promise<string>;
|
|
1352
|
+
(
|
|
1353
|
+
provider: string,
|
|
1354
|
+
data: Record<string, unknown> & { redirect: false },
|
|
1355
|
+
opts?: { redirectTo?: string; req?: Request },
|
|
1356
|
+
): Promise<string>;
|
|
1357
|
+
(
|
|
1358
|
+
provider: string,
|
|
1359
|
+
data?: FormData | Record<string, unknown>,
|
|
1360
|
+
opts?: { redirectTo?: string; redirect?: true; req?: Request },
|
|
1361
|
+
): Promise<Response>;
|
|
1362
|
+
};
|
|
1363
|
+
/** Auth.js v5's `signOut`: clears the session and throws a redirect inside a request. */
|
|
1364
|
+
signOut: {
|
|
1365
|
+
(opts: { redirectTo?: string; redirect: false; req?: Request }): Promise<string>;
|
|
1366
|
+
(opts?: { redirectTo?: string; redirect?: true; req?: Request }): Promise<Response>;
|
|
1367
|
+
};
|
|
1066
1368
|
handlers: { GET: (req: Request) => Promise<Response>; POST: (req: Request) => Promise<Response> };
|
|
1067
1369
|
}
|
|
1068
1370
|
|
|
1069
1371
|
/**
|
|
1070
|
-
* Create the auth system.
|
|
1071
|
-
*
|
|
1072
|
-
*
|
|
1073
|
-
*
|
|
1074
|
-
*
|
|
1372
|
+
* Create the auth system, Auth.js's API. `createAuth<MyUser>(...)` (or an
|
|
1373
|
+
* augmented {@link AuthUser}) types `auth().user` with no cast.
|
|
1374
|
+
*
|
|
1375
|
+
* @example
|
|
1376
|
+
* ```ts
|
|
1377
|
+
* // modules/auth/auth.server.ts
|
|
1378
|
+
* export const { auth, signIn, signOut, handlers } = createAuth({
|
|
1379
|
+
* providers: [Credentials({
|
|
1380
|
+
* async authorize(c: { email: string; password: string }) {
|
|
1381
|
+
* const user = await findUserByEmail(c.email); // your own query
|
|
1382
|
+
* if (!user || !(await verifyPassword(c.password, user.passwordHash))) return null;
|
|
1383
|
+
* return { id: String(user.id), email: user.email };
|
|
1384
|
+
* },
|
|
1385
|
+
* })],
|
|
1386
|
+
* pages: { error: '/login' }, // an uncaught failure lands on /login?error=CredentialsSignin
|
|
1387
|
+
* });
|
|
1388
|
+
* // app/api/auth/[...path]/route.ts: export const { GET, POST } = handlers;
|
|
1389
|
+
* // Read the user anywhere on the server: const session = await auth();
|
|
1390
|
+
*
|
|
1391
|
+
* // A 'use server' action bound as <form action=${logIn}>, as with Auth.js in Next:
|
|
1392
|
+
* export async function logIn(formData: FormData) {
|
|
1393
|
+
* try {
|
|
1394
|
+
* await signIn('credentials', formData, { redirectTo: '/dashboard' });
|
|
1395
|
+
* } catch (error) {
|
|
1396
|
+
* if (error instanceof AuthError) return { success: false, error: 'Wrong email or password.' };
|
|
1397
|
+
* throw error; // the success redirect
|
|
1398
|
+
* }
|
|
1399
|
+
* }
|
|
1400
|
+
* // Sign out the same way: await signOut({ redirectTo: '/' }).
|
|
1401
|
+
* ```
|
|
1075
1402
|
*/
|
|
1076
1403
|
export declare function createAuth<TUser = ResolvedAuthUser>(config: AuthConfig): AuthInstance<TUser>;
|
|
1077
|
-
/**
|
|
1078
|
-
export declare
|
|
1404
|
+
/** Next.js's navigation helpers (`next/navigation`), re-exported from `@webjsdev/core` so server code can import them from either package. */
|
|
1405
|
+
export declare const notFound: typeof import('@webjsdev/core').notFound;
|
|
1406
|
+
export declare const forbidden: typeof import('@webjsdev/core').forbidden;
|
|
1407
|
+
export declare const unauthorized: typeof import('@webjsdev/core').unauthorized;
|
|
1408
|
+
export declare const redirect: typeof import('@webjsdev/core').redirect;
|
|
1409
|
+
export declare const permanentRedirect: typeof import('@webjsdev/core').permanentRedirect;
|
|
1410
|
+
|
|
1411
|
+
/** Auth.js's base error: `error instanceof AuthError` and `error.type` say why a sign-in failed. */
|
|
1412
|
+
export declare class AuthError extends Error {
|
|
1413
|
+
type: string;
|
|
1414
|
+
}
|
|
1415
|
+
/** Thrown by `signIn('credentials', ...)` when `authorize` returns null. */
|
|
1416
|
+
export declare class CredentialsSignin extends AuthError {
|
|
1417
|
+
type: 'CredentialsSignin';
|
|
1418
|
+
code: string;
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Options for the credentials provider, Auth.js's `Credentials`. `C` is the
|
|
1422
|
+
* shape of the submitted fields, inferred from `authorize`'s annotation
|
|
1423
|
+
* (`authorize(credentials: { email: string; password: string })`).
|
|
1424
|
+
*/
|
|
1425
|
+
export interface CredentialsOptions<C extends Record<string, unknown> = Record<string, unknown>> {
|
|
1426
|
+
/**
|
|
1427
|
+
* Check the submitted fields (the sign-in form's fields, or the object passed
|
|
1428
|
+
* to `signIn('credentials', data)`). Return the session user, or `null` to
|
|
1429
|
+
* refuse the sign-in.
|
|
1430
|
+
*/
|
|
1431
|
+
authorize: (credentials: C) => Promise<Record<string, unknown> | null> | Record<string, unknown> | null;
|
|
1432
|
+
[key: string]: unknown;
|
|
1433
|
+
}
|
|
1434
|
+
/** The credentials (email/password) provider, Auth.js's `Credentials`. */
|
|
1435
|
+
export declare function Credentials<C extends Record<string, unknown> = Record<string, unknown>>(
|
|
1436
|
+
opts: CredentialsOptions<C>,
|
|
1437
|
+
): ProviderConfig;
|
|
1079
1438
|
/** The Google OAuth provider. */
|
|
1080
1439
|
export declare function Google(opts?: Record<string, unknown>): ProviderConfig;
|
|
1081
1440
|
/** The GitHub OAuth provider. */
|