@lunora/auth 1.0.0-alpha.182 → 1.0.0-alpha.184

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.
@@ -0,0 +1 @@
1
+ export { fetchClientMetadataResource as default } from '@better-auth/cimd/node';
@@ -0,0 +1 @@
1
+ export { fetchClientMetadataResource as default } from '@better-auth/cimd/node';
@@ -0,0 +1 @@
1
+ import{fetchClientMetadataResource as a}from"@better-auth/cimd/node";export{a as default};
@@ -0,0 +1,32 @@
1
+ import { ClientMetadataResourceFetch } from '@better-auth/oauth-provider';
2
+ /**
3
+ * Build the `fetchClientMetadataResource` transport for `cimd()` on Cloudflare
4
+ * Workers.
5
+ *
6
+ * ```ts
7
+ * import { cimd, jwt, mcp } from "@lunora/auth/plugins";
8
+ * import workersCimdFetch from "@lunora/auth/cimd/workers";
9
+ *
10
+ * plugins: [
11
+ * jwt(),
12
+ * mcp({ … }),
13
+ * cimd({
14
+ * fetchClientMetadataResource: workersCimdFetch(),
15
+ * metadataProfile: "mcp-2026-07-28",
16
+ * isMetadataDocumentUrlAllowed: (url) => new URL(url).origin === "https://claude.ai",
17
+ * // The allowlist gates only the `client_id` fetch; bind `jwks_uri` to it too.
18
+ * originBoundFields: ["post_logout_redirect_uris", "client_uri", "jwks_uri"],
19
+ * }),
20
+ * ];
21
+ * ```
22
+ *
23
+ * The returned transport only issues `GET` / `HEAD` to `https:` URLs without
24
+ * credentials, and never follows a redirect: it fetches with `redirect: "manual"`
25
+ * (workerd does not implement `"error"`) and throws on any redirect status, which
26
+ * cimd reports as `invalid_client`. A `304` from a conditional revalidation passes
27
+ * through.
28
+ * @throws LunoraError `AUTH_CIMD_FETCH_NOT_PUBLIC` when the Worker runs without the
29
+ * `global_fetch_strictly_public` compatibility flag.
30
+ */
31
+ declare const workersCimdFetch: () => ClientMetadataResourceFetch;
32
+ export { workersCimdFetch as default };
@@ -0,0 +1,32 @@
1
+ import { ClientMetadataResourceFetch } from '@better-auth/oauth-provider';
2
+ /**
3
+ * Build the `fetchClientMetadataResource` transport for `cimd()` on Cloudflare
4
+ * Workers.
5
+ *
6
+ * ```ts
7
+ * import { cimd, jwt, mcp } from "@lunora/auth/plugins";
8
+ * import workersCimdFetch from "@lunora/auth/cimd/workers";
9
+ *
10
+ * plugins: [
11
+ * jwt(),
12
+ * mcp({ … }),
13
+ * cimd({
14
+ * fetchClientMetadataResource: workersCimdFetch(),
15
+ * metadataProfile: "mcp-2026-07-28",
16
+ * isMetadataDocumentUrlAllowed: (url) => new URL(url).origin === "https://claude.ai",
17
+ * // The allowlist gates only the `client_id` fetch; bind `jwks_uri` to it too.
18
+ * originBoundFields: ["post_logout_redirect_uris", "client_uri", "jwks_uri"],
19
+ * }),
20
+ * ];
21
+ * ```
22
+ *
23
+ * The returned transport only issues `GET` / `HEAD` to `https:` URLs without
24
+ * credentials, and never follows a redirect: it fetches with `redirect: "manual"`
25
+ * (workerd does not implement `"error"`) and throws on any redirect status, which
26
+ * cimd reports as `invalid_client`. A `304` from a conditional revalidation passes
27
+ * through.
28
+ * @throws LunoraError `AUTH_CIMD_FETCH_NOT_PUBLIC` when the Worker runs without the
29
+ * `global_fetch_strictly_public` compatibility flag.
30
+ */
31
+ declare const workersCimdFetch: () => ClientMetadataResourceFetch;
32
+ export { workersCimdFetch as default };
@@ -0,0 +1 @@
1
+ import{LunoraError as c}from"@lunora/errors";const a="global_fetch_strictly_public",i=()=>{const{Cloudflare:t}=globalThis;return t?.compatibilityFlags?.[a]===!0},n=new Set([301,302,303,307,308]),l=t=>n.has(t.status)||t.type==="opaqueredirect",d=()=>{if(!i())throw new c("AUTH_CIMD_FETCH_NOT_PUBLIC",`CIMD needs the "${a}" compatibility flag, so metadata fetches cannot reach private addresses. Add it to "compatibility_flags" in wrangler.jsonc.`);return async(t,s)=>{const e=new Request(t,{...s,redirect:"manual"}),r=new URL(e.url);if(r.protocol!=="https:")throw new TypeError(`refusing to fetch a client metadata resource over ${r.protocol}`);if(r.username!==""||r.password!=="")throw new TypeError("refusing to fetch a client metadata resource URL that carries credentials");if(e.method!=="GET"&&e.method!=="HEAD")throw new TypeError(`refusing to ${e.method} a client metadata resource`);const o=await fetch(e);if(l(o))throw new TypeError("client metadata resource fetches must not follow redirects");return o}};export{d as default};
package/dist/index.d.mts CHANGED
@@ -789,8 +789,9 @@ declare class LunoraAuthDO {
789
789
  */
790
790
  constructor(state: AuthDoState, optionsFactory: () => LunoraAuthOptions, options?: AuthDoOptions);
791
791
  /**
792
- * Serve an auth request. Routes under `/api/auth` go to better-auth; the internal
793
- * session route is handled here; anything else is a 404.
792
+ * Serve an auth request. Routes under `/api/auth` go to better-auth, and so do
793
+ * the OAuth discovery documents its options configure (`./discovery`); the
794
+ * internal session route is handled here; anything else is a 404.
794
795
  *
795
796
  * The base path is not configurable. The worker half only ever forwards
796
797
  * `/api/auth/*` (`createDoAuthWiring`, which codegen calls with no base path),
@@ -798,6 +799,29 @@ declare class LunoraAuthDO {
798
799
  */
799
800
  fetch(request: Request): Promise<Response>;
800
801
  }
802
+ /**
803
+ * The exact `.well-known` paths this auth configuration serves outside its base
804
+ * path: the protected-resource metadata for each MCP resource, and the
805
+ * authorization-server metadata for the issuer. Empty unless an
806
+ * `oauthProvider()` or `mcp()` plugin is configured.
807
+ * @throws LunoraError `AUTH_MCP_RESOURCE_AMBIGUOUS` — see {@link mcpResourceOf}.
808
+ */
809
+ declare const authDiscoveryPaths: (options: LunoraAuthOptions) => ReadonlyArray<string>;
810
+ /**
811
+ * Derive {@link authDiscoveryPaths} for an `.auth()` declaration, memoised on the
812
+ * declaration object. A framework-hosted worker rebuilds its options on every
813
+ * request, and calling `options(env)` each time would rebuild every plugin.
814
+ */
815
+ declare const authDiscoveryPathsFor: <Env>(declaration: {
816
+ options: (env: Env) => LunoraAuthOptions;
817
+ }, env: Env) => ReadonlyArray<string>;
818
+ /**
819
+ * Serve an OAuth discovery document from `auth` when `request` asks for one of the
820
+ * paths {@link authDiscoveryPaths} derives from its options; `undefined` for
821
+ * anything else, so the caller's own 404 stands. A 404 from better-auth itself is
822
+ * also passed over as `undefined`.
823
+ */
824
+ declare const handleAuthDiscoveryRequest: (auth: LunoraAuth, request: Request) => Promise<Response | undefined>;
801
825
  /**
802
826
  * The `CREATE TABLE` / `CREATE INDEX` statements for a better-auth config, in
803
827
  * execution order (every table before any index).
@@ -903,6 +927,13 @@ interface AuthNamespaceLike {
903
927
  type AuthJurisdiction = "eu" | "fedramp" | "us";
904
928
  /** What {@link createDoAuthWiring} needs, already resolved against `env`. */
905
929
  interface DoAuthWiringOptions {
930
+ /**
931
+ * The OAuth discovery paths the object serves outside `/api/auth` —
932
+ * `authDiscoveryPaths(options)` over the same auth options the object builds.
933
+ * Derived in the worker so a probe of any other path never costs a round-trip
934
+ * to the object. Empty or absent: nothing is forwarded.
935
+ */
936
+ discoveryPaths?: ReadonlyArray<string>;
906
937
  /**
907
938
  * Shared secret presented on the object's internal session route. `undefined`
908
939
  * means identity resolution fails closed — see {@link DoAuthWiring.resolveIdentity}.
@@ -945,6 +976,12 @@ interface DoAuthWiring {
945
976
  * subclass that set its own served nothing.
946
977
  */
947
978
  authHandler: (request: Request) => Promise<Response | undefined>;
979
+ /**
980
+ * Forwards a `GET`/`HEAD` for one of {@link DoAuthWiringOptions.discoveryPaths}
981
+ * to the object; `undefined` for anything else, and for the object's 404. The
982
+ * worker calls it only after the app's own routes missed.
983
+ */
984
+ discoveryHandler: (request: Request) => Promise<Response | undefined>;
948
985
  /**
949
986
  * Copy the auth tables from the un-pinned object into the pinned one, and later
950
987
  * purge the un-pinned copy. Present only when a `jurisdiction` is set. Backs the
@@ -1140,4 +1177,4 @@ declare const validateSessionPolicy: (policy: SessionPolicy) => SessionPolicy;
1140
1177
  * with the same 60s cookie cache as `rolling`.
1141
1178
  */
1142
1179
  declare const sessionPresets: Record<"longLived" | "rolling" | "strict", SessionPolicy>;
1143
- export { READ_AUDIT_PATH as AUTH_DO_AUDIT_PATH, INTERNAL_SECRET_HEADER as AUTH_DO_SECRET_HEADER, RESOLVE_SESSION_PATH as AUTH_DO_SESSION_PATH, type AppendAuthAuditEntry, type AppendAuthAuditOptions, type AuthAccount, type AuthAdmin, type AuthAdminSession, type AuthAdminUser, type AuthAuditEvent, type AuthAuditHookConfig, type AuthAuditReader, type AuthCapabilities, type AuthConfigInfo, type AuthDoOptions, type AuthDoState, type AuthInvitation, type AuthJurisdiction, type AuthJurisdictionMove, type AuthMember, type AuthMoveResult, type AuthMoveTableReport, type AuthNamespaceLike, type AuthOrgRole, type AuthOrganization, type AuthPage, type AuthPasskey, type AuthSignUpInvitation, type AuthTeam, type AuthTeamMember, type AuthTimestamp, type AuthUserFieldSpec, type CreateAuthAdminOptions, DEFAULT_AUTH_BASE_PATH, type DoAuthWiring, type DoAuthWiringOptions, type EmailClassification, type EmailGateConfig, type EmailGateHookConfig, type ImpersonationResult, type ListUsersOptions, type LunoraAuth, LunoraAuthAdminError, LunoraAuthDO, type LunoraAuthOptions, type SessionPolicy, type SqlExecutor, authAuditHook, authDoColumnAdditions, authDoSchemaStatements, buildAuditEntry, compileMigrationsSql, createAuthAdmin, createDoAuthWiring, emailGateDatabaseHooks, ensureMigrated, eventForPath, handleAuthRequest, legacyIssuerCleanupStatements, sessionPresets, validateSessionPolicy, withAuthAudit, withEmailGate };
1180
+ export { READ_AUDIT_PATH as AUTH_DO_AUDIT_PATH, INTERNAL_SECRET_HEADER as AUTH_DO_SECRET_HEADER, RESOLVE_SESSION_PATH as AUTH_DO_SESSION_PATH, type AppendAuthAuditEntry, type AppendAuthAuditOptions, type AuthAccount, type AuthAdmin, type AuthAdminSession, type AuthAdminUser, type AuthAuditEvent, type AuthAuditHookConfig, type AuthAuditReader, type AuthCapabilities, type AuthConfigInfo, type AuthDoOptions, type AuthDoState, type AuthInvitation, type AuthJurisdiction, type AuthJurisdictionMove, type AuthMember, type AuthMoveResult, type AuthMoveTableReport, type AuthNamespaceLike, type AuthOrgRole, type AuthOrganization, type AuthPage, type AuthPasskey, type AuthSignUpInvitation, type AuthTeam, type AuthTeamMember, type AuthTimestamp, type AuthUserFieldSpec, type CreateAuthAdminOptions, DEFAULT_AUTH_BASE_PATH, type DoAuthWiring, type DoAuthWiringOptions, type EmailClassification, type EmailGateConfig, type EmailGateHookConfig, type ImpersonationResult, type ListUsersOptions, type LunoraAuth, LunoraAuthAdminError, LunoraAuthDO, type LunoraAuthOptions, type SessionPolicy, type SqlExecutor, authAuditHook, authDiscoveryPaths, authDiscoveryPathsFor, authDoColumnAdditions, authDoSchemaStatements, buildAuditEntry, compileMigrationsSql, createAuthAdmin, createDoAuthWiring, emailGateDatabaseHooks, ensureMigrated, eventForPath, handleAuthDiscoveryRequest, handleAuthRequest, legacyIssuerCleanupStatements, sessionPresets, validateSessionPolicy, withAuthAudit, withEmailGate };
package/dist/index.d.ts CHANGED
@@ -789,8 +789,9 @@ declare class LunoraAuthDO {
789
789
  */
790
790
  constructor(state: AuthDoState, optionsFactory: () => LunoraAuthOptions, options?: AuthDoOptions);
791
791
  /**
792
- * Serve an auth request. Routes under `/api/auth` go to better-auth; the internal
793
- * session route is handled here; anything else is a 404.
792
+ * Serve an auth request. Routes under `/api/auth` go to better-auth, and so do
793
+ * the OAuth discovery documents its options configure (`./discovery`); the
794
+ * internal session route is handled here; anything else is a 404.
794
795
  *
795
796
  * The base path is not configurable. The worker half only ever forwards
796
797
  * `/api/auth/*` (`createDoAuthWiring`, which codegen calls with no base path),
@@ -798,6 +799,29 @@ declare class LunoraAuthDO {
798
799
  */
799
800
  fetch(request: Request): Promise<Response>;
800
801
  }
802
+ /**
803
+ * The exact `.well-known` paths this auth configuration serves outside its base
804
+ * path: the protected-resource metadata for each MCP resource, and the
805
+ * authorization-server metadata for the issuer. Empty unless an
806
+ * `oauthProvider()` or `mcp()` plugin is configured.
807
+ * @throws LunoraError `AUTH_MCP_RESOURCE_AMBIGUOUS` — see {@link mcpResourceOf}.
808
+ */
809
+ declare const authDiscoveryPaths: (options: LunoraAuthOptions) => ReadonlyArray<string>;
810
+ /**
811
+ * Derive {@link authDiscoveryPaths} for an `.auth()` declaration, memoised on the
812
+ * declaration object. A framework-hosted worker rebuilds its options on every
813
+ * request, and calling `options(env)` each time would rebuild every plugin.
814
+ */
815
+ declare const authDiscoveryPathsFor: <Env>(declaration: {
816
+ options: (env: Env) => LunoraAuthOptions;
817
+ }, env: Env) => ReadonlyArray<string>;
818
+ /**
819
+ * Serve an OAuth discovery document from `auth` when `request` asks for one of the
820
+ * paths {@link authDiscoveryPaths} derives from its options; `undefined` for
821
+ * anything else, so the caller's own 404 stands. A 404 from better-auth itself is
822
+ * also passed over as `undefined`.
823
+ */
824
+ declare const handleAuthDiscoveryRequest: (auth: LunoraAuth, request: Request) => Promise<Response | undefined>;
801
825
  /**
802
826
  * The `CREATE TABLE` / `CREATE INDEX` statements for a better-auth config, in
803
827
  * execution order (every table before any index).
@@ -903,6 +927,13 @@ interface AuthNamespaceLike {
903
927
  type AuthJurisdiction = "eu" | "fedramp" | "us";
904
928
  /** What {@link createDoAuthWiring} needs, already resolved against `env`. */
905
929
  interface DoAuthWiringOptions {
930
+ /**
931
+ * The OAuth discovery paths the object serves outside `/api/auth` —
932
+ * `authDiscoveryPaths(options)` over the same auth options the object builds.
933
+ * Derived in the worker so a probe of any other path never costs a round-trip
934
+ * to the object. Empty or absent: nothing is forwarded.
935
+ */
936
+ discoveryPaths?: ReadonlyArray<string>;
906
937
  /**
907
938
  * Shared secret presented on the object's internal session route. `undefined`
908
939
  * means identity resolution fails closed — see {@link DoAuthWiring.resolveIdentity}.
@@ -945,6 +976,12 @@ interface DoAuthWiring {
945
976
  * subclass that set its own served nothing.
946
977
  */
947
978
  authHandler: (request: Request) => Promise<Response | undefined>;
979
+ /**
980
+ * Forwards a `GET`/`HEAD` for one of {@link DoAuthWiringOptions.discoveryPaths}
981
+ * to the object; `undefined` for anything else, and for the object's 404. The
982
+ * worker calls it only after the app's own routes missed.
983
+ */
984
+ discoveryHandler: (request: Request) => Promise<Response | undefined>;
948
985
  /**
949
986
  * Copy the auth tables from the un-pinned object into the pinned one, and later
950
987
  * purge the un-pinned copy. Present only when a `jurisdiction` is set. Backs the
@@ -1140,4 +1177,4 @@ declare const validateSessionPolicy: (policy: SessionPolicy) => SessionPolicy;
1140
1177
  * with the same 60s cookie cache as `rolling`.
1141
1178
  */
1142
1179
  declare const sessionPresets: Record<"longLived" | "rolling" | "strict", SessionPolicy>;
1143
- export { READ_AUDIT_PATH as AUTH_DO_AUDIT_PATH, INTERNAL_SECRET_HEADER as AUTH_DO_SECRET_HEADER, RESOLVE_SESSION_PATH as AUTH_DO_SESSION_PATH, type AppendAuthAuditEntry, type AppendAuthAuditOptions, type AuthAccount, type AuthAdmin, type AuthAdminSession, type AuthAdminUser, type AuthAuditEvent, type AuthAuditHookConfig, type AuthAuditReader, type AuthCapabilities, type AuthConfigInfo, type AuthDoOptions, type AuthDoState, type AuthInvitation, type AuthJurisdiction, type AuthJurisdictionMove, type AuthMember, type AuthMoveResult, type AuthMoveTableReport, type AuthNamespaceLike, type AuthOrgRole, type AuthOrganization, type AuthPage, type AuthPasskey, type AuthSignUpInvitation, type AuthTeam, type AuthTeamMember, type AuthTimestamp, type AuthUserFieldSpec, type CreateAuthAdminOptions, DEFAULT_AUTH_BASE_PATH, type DoAuthWiring, type DoAuthWiringOptions, type EmailClassification, type EmailGateConfig, type EmailGateHookConfig, type ImpersonationResult, type ListUsersOptions, type LunoraAuth, LunoraAuthAdminError, LunoraAuthDO, type LunoraAuthOptions, type SessionPolicy, type SqlExecutor, authAuditHook, authDoColumnAdditions, authDoSchemaStatements, buildAuditEntry, compileMigrationsSql, createAuthAdmin, createDoAuthWiring, emailGateDatabaseHooks, ensureMigrated, eventForPath, handleAuthRequest, legacyIssuerCleanupStatements, sessionPresets, validateSessionPolicy, withAuthAudit, withEmailGate };
1180
+ export { READ_AUDIT_PATH as AUTH_DO_AUDIT_PATH, INTERNAL_SECRET_HEADER as AUTH_DO_SECRET_HEADER, RESOLVE_SESSION_PATH as AUTH_DO_SESSION_PATH, type AppendAuthAuditEntry, type AppendAuthAuditOptions, type AuthAccount, type AuthAdmin, type AuthAdminSession, type AuthAdminUser, type AuthAuditEvent, type AuthAuditHookConfig, type AuthAuditReader, type AuthCapabilities, type AuthConfigInfo, type AuthDoOptions, type AuthDoState, type AuthInvitation, type AuthJurisdiction, type AuthJurisdictionMove, type AuthMember, type AuthMoveResult, type AuthMoveTableReport, type AuthNamespaceLike, type AuthOrgRole, type AuthOrganization, type AuthPage, type AuthPasskey, type AuthSignUpInvitation, type AuthTeam, type AuthTeamMember, type AuthTimestamp, type AuthUserFieldSpec, type CreateAuthAdminOptions, DEFAULT_AUTH_BASE_PATH, type DoAuthWiring, type DoAuthWiringOptions, type EmailClassification, type EmailGateConfig, type EmailGateHookConfig, type ImpersonationResult, type ListUsersOptions, type LunoraAuth, LunoraAuthAdminError, LunoraAuthDO, type LunoraAuthOptions, type SessionPolicy, type SqlExecutor, authAuditHook, authDiscoveryPaths, authDiscoveryPathsFor, authDoColumnAdditions, authDoSchemaStatements, buildAuditEntry, compileMigrationsSql, createAuthAdmin, createDoAuthWiring, emailGateDatabaseHooks, ensureMigrated, eventForPath, handleAuthDiscoveryRequest, handleAuthRequest, legacyIssuerCleanupStatements, sessionPresets, validateSessionPolicy, withAuthAudit, withEmailGate };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{l as r,a as o,b as a}from"./packem_shared/adapter-CG0Yv85f.mjs";import{LunoraAuthAdminError as u,createAuthAdmin as s}from"./packem_shared/LunoraAuthAdminError-uhKG1MgQ.mjs";import{AUTH_AUDIT_TABLE as n,appendAuthAuditEntry as m,createAuthAuditReader as l,ensureAuthAuditTable as p,readAuthAuditLog as d}from"./audit.mjs";import{authAuditHook as f,buildAuditEntry as x,eventForPath as E,withAuthAudit as T}from"./packem_shared/authAuditHook-DPTKdjBe.mjs";import{R as D,I as _,a as c,L as U}from"./packem_shared/auth-do-DoiVT_x7.mjs";import{createAuth as I,resolveAuthOptions as g}from"./packem_shared/createAuth-8aUw8v8D.mjs";import{authDoColumnAdditions as L,authDoSchemaStatements as y}from"./packem_shared/authDoColumnAdditions-UwF3Rl41.mjs";import{createDoAuthWiring as O}from"./packem_shared/createDoAuthWiring-5upQbv-P.mjs";import{emailGateDatabaseHooks as w,withEmailGate as b}from"./packem_shared/emailGateDatabaseHooks-CAygR3iq.mjs";import{assertEmailAllowed as N,classifyEmail as k,emailGateMiddleware as q,loadEmailDomainLists as C}from"./email-guard.mjs";import{DEFAULT_AUTH_BASE_PATH as G,handleAuthRequest as B}from"./packem_shared/DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import{createSignUpInvitation as V,listSignUpInvitations as Y,pruneSignUpInvitations as j,revokeSignUpInvitation as z}from"./packem_shared/createSignUpInvitation-DHZL9fNl.mjs";import{legacyIssuerCleanupStatements as K}from"./packem_shared/legacyIssuerCleanupStatements-D9Kf8OD1.mjs";import{LunoraAuthHeadersError as X,withAuthPlugins as Z}from"./middleware.mjs";import{compileMigrationsSql as tt,ensureMigrated as et}from"./packem_shared/compileMigrationsSql-DKGrw7Lf.mjs";import{default as ot}from"./schema.mjs";import{sessionPresets as it,validateSessionPolicy as ut}from"./packem_shared/sessionPresets-C867Mlo4.mjs";import{c as At,d as nt}from"./packem_shared/sql-store-CYa4HJsO.mjs";import{createMemoryAuthStore as lt,matchesWhere as pt}from"./store.mjs";import{TURNSTILE_VERIFY_ENDPOINT as ht,verifyTurnstile as ft}from"./turnstile.mjs";import{verifyTurnstileMiddleware as Et}from"./turnstile-middleware.mjs";export{n as AUTH_AUDIT_TABLE,D as AUTH_DO_AUDIT_PATH,_ as AUTH_DO_SECRET_HEADER,c as AUTH_DO_SESSION_PATH,G as DEFAULT_AUTH_BASE_PATH,u as LunoraAuthAdminError,U as LunoraAuthDO,X as LunoraAuthHeadersError,ht as TURNSTILE_VERIFY_ENDPOINT,m as appendAuthAuditEntry,N as assertEmailAllowed,f as authAuditHook,L as authDoColumnAdditions,y as authDoSchemaStatements,ot as authTables,x as buildAuditEntry,k as classifyEmail,tt as compileMigrationsSql,I as createAuth,s as createAuthAdmin,l as createAuthAuditReader,O as createDoAuthWiring,lt as createMemoryAuthStore,V as createSignUpInvitation,At as createSqlAuthStore,nt as d1Executor,w as emailGateDatabaseHooks,q as emailGateMiddleware,p as ensureAuthAuditTable,et as ensureMigrated,E as eventForPath,B as handleAuthRequest,K as legacyIssuerCleanupStatements,Y as listSignUpInvitations,C as loadEmailDomainLists,r as lunoraAuthAdapter,o as lunoraD1Adapter,a as lunoraDoAdapter,pt as matchesWhere,j as pruneSignUpInvitations,d as readAuthAuditLog,g as resolveAuthOptions,z as revokeSignUpInvitation,it as sessionPresets,ut as validateSessionPolicy,ft as verifyTurnstile,Et as verifyTurnstileMiddleware,T as withAuthAudit,Z as withAuthPlugins,b as withEmailGate};
1
+ import{l as r,a as o,b as a}from"./packem_shared/adapter-CG0Yv85f.mjs";import{LunoraAuthAdminError as s,createAuthAdmin as u}from"./packem_shared/LunoraAuthAdminError-uhKG1MgQ.mjs";import{AUTH_AUDIT_TABLE as n,appendAuthAuditEntry as m,createAuthAuditReader as h,ensureAuthAuditTable as l,readAuthAuditLog as p}from"./audit.mjs";import{authAuditHook as f,buildAuditEntry as x,eventForPath as E,withAuthAudit as T}from"./packem_shared/authAuditHook-DPTKdjBe.mjs";import{R as c,I as S,a as _,L as v}from"./packem_shared/auth-do-l5HwPI4A.mjs";import{createAuth as H,resolveAuthOptions as I}from"./packem_shared/createAuth-BdVltKyf.mjs";import{authDiscoveryPaths as g,authDiscoveryPathsFor as P,handleAuthDiscoveryRequest as L}from"./packem_shared/authDiscoveryPaths-B14lM14u.mjs";import{authDoColumnAdditions as O,authDoSchemaStatements as w}from"./packem_shared/authDoColumnAdditions-UwF3Rl41.mjs";import{createDoAuthWiring as M}from"./packem_shared/createDoAuthWiring-DIAtEFds.mjs";import{emailGateDatabaseHooks as F,withEmailGate as N}from"./packem_shared/emailGateDatabaseHooks-CAygR3iq.mjs";import{assertEmailAllowed as C,classifyEmail as G,emailGateMiddleware as B,loadEmailDomainLists as W}from"./email-guard.mjs";import{DEFAULT_AUTH_BASE_PATH as Y,handleAuthRequest as j}from"./packem_shared/DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import{createSignUpInvitation as J,listSignUpInvitations as K,pruneSignUpInvitations as Q,revokeSignUpInvitation as X}from"./packem_shared/createSignUpInvitation-DHZL9fNl.mjs";import{legacyIssuerCleanupStatements as $}from"./packem_shared/legacyIssuerCleanupStatements-D9Kf8OD1.mjs";import{LunoraAuthHeadersError as et,withAuthPlugins as rt}from"./middleware.mjs";import{compileMigrationsSql as at,ensureMigrated as it}from"./packem_shared/compileMigrationsSql-BiYsLz91.mjs";import{default as ut}from"./schema.mjs";import{sessionPresets as nt,validateSessionPolicy as mt}from"./packem_shared/sessionPresets-C867Mlo4.mjs";import{c as lt,d as pt}from"./packem_shared/sql-store-CYa4HJsO.mjs";import{createMemoryAuthStore as ft,matchesWhere as xt}from"./store.mjs";import{TURNSTILE_VERIFY_ENDPOINT as Tt,verifyTurnstile as Dt}from"./turnstile.mjs";import{verifyTurnstileMiddleware as St}from"./turnstile-middleware.mjs";export{n as AUTH_AUDIT_TABLE,c as AUTH_DO_AUDIT_PATH,S as AUTH_DO_SECRET_HEADER,_ as AUTH_DO_SESSION_PATH,Y as DEFAULT_AUTH_BASE_PATH,s as LunoraAuthAdminError,v as LunoraAuthDO,et as LunoraAuthHeadersError,Tt as TURNSTILE_VERIFY_ENDPOINT,m as appendAuthAuditEntry,C as assertEmailAllowed,f as authAuditHook,g as authDiscoveryPaths,P as authDiscoveryPathsFor,O as authDoColumnAdditions,w as authDoSchemaStatements,ut as authTables,x as buildAuditEntry,G as classifyEmail,at as compileMigrationsSql,H as createAuth,u as createAuthAdmin,h as createAuthAuditReader,M as createDoAuthWiring,ft as createMemoryAuthStore,J as createSignUpInvitation,lt as createSqlAuthStore,pt as d1Executor,F as emailGateDatabaseHooks,B as emailGateMiddleware,l as ensureAuthAuditTable,it as ensureMigrated,E as eventForPath,L as handleAuthDiscoveryRequest,j as handleAuthRequest,$ as legacyIssuerCleanupStatements,K as listSignUpInvitations,W as loadEmailDomainLists,r as lunoraAuthAdapter,o as lunoraD1Adapter,a as lunoraDoAdapter,xt as matchesWhere,Q as pruneSignUpInvitations,p as readAuthAuditLog,I as resolveAuthOptions,X as revokeSignUpInvitation,nt as sessionPresets,mt as validateSessionPolicy,Dt as verifyTurnstile,St as verifyTurnstileMiddleware,T as withAuthAudit,rt as withAuthPlugins,N as withEmailGate};
@@ -0,0 +1 @@
1
+ import{I as R,L as _,R as S,a as T}from"./auth-do-l5HwPI4A.mjs";import"./adapter-CG0Yv85f.mjs";import"../audit.mjs";import"./createAuth-BdVltKyf.mjs";import"./authDiscoveryPaths-B14lM14u.mjs";import"./authDoColumnAdditions-UwF3Rl41.mjs";import"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import"./legacyIssuerCleanupStatements-D9Kf8OD1.mjs";export{R as INTERNAL_SECRET_HEADER,_ as LunoraAuthDO,S as READ_AUDIT_PATH,T as RESOLVE_SESSION_PATH};
@@ -0,0 +1 @@
1
+ import{getAuthTablesWithResolvedIndexes as x}from"@better-auth/core/db/internal";import{LunoraError as b,toErrorBody as re}from"@lunora/errors";import{d as P,b as ne}from"./adapter-CG0Yv85f.mjs";import{AUTH_AUDIT_TABLE as oe,ensureAuthAuditTable as se,createAuthAuditReader as ie}from"../audit.mjs";import{resolveAuthOptions as ae,createAuth as ce}from"./createAuth-BdVltKyf.mjs";import{handleAuthDiscoveryRequest as ue}from"./authDiscoveryPaths-B14lM14u.mjs";import{authDoSchemaStatements as fe,authDoColumnAdditions as le}from"./authDoColumnAdditions-UwF3Rl41.mjs";import{handleAuthRequest as me}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import{q as c}from"./quote-identifier-CGiYFBvY.mjs";import{schemaDeclaresIssuer as de,legacyIssuerCleanupStatements as pe,indexesReferencingIssuer as Ee}from"./legacyIssuerCleanupStatements-D9Kf8OD1.mjs";const he=(t,e)=>{const r=Math.max(t.length,e.length);let n=t.length^e.length;for(let s=0;s<r;s+=1){const o=s<t.length?t.charCodeAt(s):0,i=s<e.length?e.charCodeAt(s):0;n|=o^i}return n===0},W=2166136261,V=16777619,F=(t,e=W)=>{let r=e;for(let n=0;n<t.length;n+=1)r^=t.charCodeAt(n),r=Math.imul(r,V);return(r>>>0).toString(16).padStart(8,"0")},we=t=>`${F(t,W)}${F(t,V)}`,$=t=>{let e="";for(let n=0;n<t.length;n+=32768)e+=String.fromCharCode(...t.subarray(n,n+32768));return btoa(e)},ye=t=>{const e=atob(t),r=new Uint8Array(e.length);for(let n=0;n<e.length;n+=1)r[n]=e.codePointAt(n)??0;return r},d="$lunora.wire$",j=64,H=1024,U="__proto__",B={BigInt64Array,BigUint64Array,Float32Array,Float64Array,Int8Array,Int16Array,Int32Array,Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array},q={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},Te=t=>{if(t===null||typeof t!="object")return!1;const e=Object.getPrototypeOf(t);return e===null||e===Object.prototype},T=(t,e=0)=>{if(e>j)throw new RangeError(`wire-codec: value nesting exceeds the ${j}-level limit`);if(t===void 0)return[d,"undefined"];if(t===null)return null;const r=typeof t;if(r==="bigint")return[d,"bigint",t.toString()];if(r==="number"){const o=t;return Number.isNaN(o)?[d,"nan"]:o===1/0?[d,"inf"]:o===-1/0?[d,"-inf"]:o}if(r!=="object")return t;if(t instanceof Date)return[d,"date",T(t.getTime(),e+1)];if(t instanceof Error){const o=t,i={};for(const u of Object.keys(o)){if(o[u]===void 0)continue;const f=T(o[u],e+1);u===U?Object.defineProperty(i,u,{configurable:!0,enumerable:!0,value:f,writable:!0}):i[u]=f}const a=[d,"error",String(o.name),String(o.message),i];return o.cause!==void 0&&a.push(T(o.cause,e+1)),a}if(t instanceof URL)return[d,"url",t.href];if(t instanceof Map)return[d,"map",[...t.entries()].map(([o,i])=>[T(o,e+1),T(i,e+1)])];if(t instanceof Set)return[d,"set",[...t].map(o=>T(o,e+1))];if(t instanceof ArrayBuffer)return[d,"bytes",$(new Uint8Array(t)),"ArrayBuffer"];if(ArrayBuffer.isView(t)){const o=t,i=o.constructor.name,a=new Uint8Array(o.buffer,o.byteOffset,o.byteLength);return i==="Uint8Array"?[d,"bytes",$(a)]:[d,"bytes",$(a),i]}if(Array.isArray(t)){const o=t.map(i=>T(i,e+1));return o.length>0&&o[0]===d?[d,"arr",o]:o}if(!Te(t)){const o=t.constructor?.name??"value";throw new TypeError(`wire-codec: cannot encode a ${o} over the Lunora wire — only plain objects, arrays, and the supported built-ins (Date, Error, URL, Map, Set, ArrayBuffer/typed arrays, bigint) round-trip`)}const n=t,s={};for(const o of Object.keys(n)){const i=n[o];if(i===void 0)continue;const a=T(i,e+1);o===U?Object.defineProperty(s,o,{configurable:!0,enumerable:!0,value:a,writable:!0}):s[o]=a}return s},E=(t,e=0)=>{if(e>j)throw new RangeError(`wire-codec: value nesting exceeds the ${j}-level limit`);if(t===null||typeof t!="object")return t;if(Array.isArray(t)){if(t[0]===d)switch(t[1]){case"-inf":return-1/0;case"arr":return t[2].map(o=>E(o,e+1));case"bigint":{const o=t[2];if(typeof o!="string"||o.length>H||!/^-?\d+$/.test(o))throw new RangeError(`wire-codec: invalid or over-long bigint (max ${H} digits)`);return BigInt(o)}case"date":{const o=E(t[2],e+1);if(typeof o!="number")throw new TypeError("wire-codec: malformed date — epoch must be a number");return new Date(o)}case"map":{const o=t[2];return new Map(o.map(i=>{if(!Array.isArray(i)||i.length!==2)throw new TypeError("wire-codec: malformed map entry — expected a [key, value] pair");return[E(i[0],e+1),E(i[1],e+1)]}))}case"set":return new Set(t[2].map(o=>E(o,e+1)));case"url":{const o=t[2];if(typeof o!="string")throw new TypeError("wire-codec: malformed url — href must be a string");return new URL(o)}case"error":{const o=t[2],i=t[3];if(typeof o!="string"||typeof i!="string")throw new TypeError("wire-codec: malformed error — name and message must be strings");const a=(Object.hasOwn(q,o)?q[o]:void 0)??Error,u=new a(i);u.name!==o&&Object.defineProperty(u,"name",{configurable:!0,value:o,writable:!0});const f=E(t[4],e+1);if(f===null||typeof f!="object"||Array.isArray(f))throw new TypeError("wire-codec: malformed error — props must be an object");for(const m of Object.keys(f))m===U?Object.defineProperty(u,m,{configurable:!0,enumerable:!0,value:f[m],writable:!0}):u[m]=f[m];return t.length>5&&Object.defineProperty(u,"cause",{configurable:!0,value:E(t[5],e+1),writable:!0}),u}case"bytes":{const o=t[2];if(typeof o!="string")throw new TypeError("wire-codec: malformed bytes — payload must be a base64 string");const i=ye(o);if($(i)!==o)throw new TypeError("wire-codec: malformed bytes — payload must be canonical padded base64");const a=t[3]??"Uint8Array";if(a==="ArrayBuffer")return i.buffer.byteLength===i.byteLength?i.buffer:i.slice().buffer;const u=Object.hasOwn(B,a)?B[a]:void 0;return u?new u(i.slice().buffer):i}case"inf":return 1/0;case"nan":return Number.NaN;case"undefined":return;default:return t.map(o=>E(o,e+1))}return t.map(s=>E(s,e+1))}const r=t,n={};for(const s of Object.keys(r)){const o=E(r[s],e+1);s===U?Object.defineProperty(n,s,{configurable:!0,enumerable:!0,value:o,writable:!0}):n[s]=o}return n},ge="/__lunora/auth/move",g="__lunora_auth_move__",S="__lunora_auth_move_rows__",D="__lunora_auth_purged__",G=100,Ae=16,be=100,Se=2n**64n,X=t=>t.startsWith("sqlite_")||t.startsWith("_cf_")||t===g||t===S||t===D,Y=async(t,e)=>t.transaction(async()=>Promise.resolve().then(e)),h=(t,e,...r)=>[...t.sql.exec(e,...r)],l=(t,e,...r)=>{[...t.sql.exec(e,...r)]},N=t=>h(t,"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name").map(e=>String(e.name)).filter(e=>!X(e)),J=(t,e)=>h(t,"SELECT name, pk FROM pragma_table_info(?)",e).map(r=>({name:String(r.name),pk:Number(r.pk)})),L=(t,e)=>J(t,e).map(({name:r})=>r),K=(t,e)=>{const r=J(t,e),n=r.filter(({pk:s})=>s>0).toSorted((s,o)=>s.pk-o.pk).map(({name:s})=>s);return n.length>0?n:r.map(({name:s})=>s)},Q=(t,e)=>Number(h(t,`SELECT count(*) AS n FROM ${c(e)}`)[0]?.n??0),z=t=>JSON.stringify(T(t)),C=(t,e)=>BigInt(`0x${we(z(e.map(r=>t[r]??null)))}`),Z=(t,e)=>{let r=0,n=0n;for(const s of t)r+=1,n=(n+e(s))%Se;return`${String(r)}:${n.toString(16)}`},ee=(t,e)=>{const r=L(t,e).toSorted((n,s)=>n.localeCompare(s));return Z(t.sql.exec(`SELECT * FROM ${c(e)}`),n=>C(n,r))},Re=t=>N(t).map(e=>{const r=h(t,"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?",e)[0]?.sql,n=h(t,"SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ? AND sql IS NOT NULL",e).map(s=>({name:String(s.name),sql:String(s.sql)}));return{columns:L(t,e),indexes:n,name:e,rows:Q(t,e),sql:typeof r=="string"?r:""}}),v=(t,e)=>{if(typeof e!="string"||!N(t).includes(e))throw new TypeError(`unknown table ${JSON.stringify(e)}`);return e},Oe=(t,e,r)=>{const n=h(t,`SELECT rowid AS "__lunora_rowid__", * FROM ${c(e)} WHERE rowid > ? ORDER BY rowid LIMIT ?`,r,G),s=n.at(-1)?.__lunora_rowid__;return{last:typeof s=="number"?s:void 0,rows:n.map(({__lunora_rowid__:o,...i})=>i)}},Ne=t=>L(t,g).length>0,_=t=>Ne(t)?Object.fromEntries(h(t,`SELECT tbl, after, done, pass, cols, watermark FROM ${c(g)}`).map(e=>[String(e.tbl),{after:Number(e.after),columns:JSON.parse(String(e.cols)),done:e.done===1,pass:Number(e.pass),watermark:Number(e.watermark)}])):{},_e=(t,e,r)=>Z(t.sql.exec(`SELECT h FROM ${c(S)} WHERE tbl = ? AND pass = ?`,e,r),n=>BigInt(`0x${String(n.h)}`)),Le=t=>{l(t,`CREATE TABLE IF NOT EXISTS ${c(g)} (tbl TEXT NOT NULL PRIMARY KEY, after INTEGER NOT NULL, done INTEGER NOT NULL, pass INTEGER NOT NULL, cols TEXT NOT NULL, watermark INTEGER NOT NULL DEFAULT 0)`),l(t,`CREATE TABLE IF NOT EXISTS ${c(S)} (tbl TEXT NOT NULL, k TEXT NOT NULL, h TEXT NOT NULL, pass INTEGER NOT NULL, PRIMARY KEY (tbl, k))`)},Ie=(t,e,r)=>{if(!N(t).includes(e))return 0;const n=K(t,e).map(s=>`u.${c(s)}`).join(", ");return Number(h(t,`SELECT count(*) AS n FROM ${c(e)} u WHERE u.rowid > ? AND NOT EXISTS (SELECT 1 FROM ${c(S)} c WHERE c.tbl = ? AND c.k = json_array(${n}))`,r,e)[0]?.n??0)},$e=/^CREATE TABLE /iu,je=/^CREATE (?:UNIQUE )?INDEX /iu,Ue=(t,e,r)=>{if(X(e.name))throw new TypeError(`refusing reserved table ${JSON.stringify(e.name)}`);if(!$e.test(e.sql)||e.indexes.some(({sql:s})=>!je.test(s)))throw new TypeError(`refusing unexpected DDL for ${JSON.stringify(e.name)}`);if(r.has(e.name)){const s=new Set(L(t,e.name));for(const o of e.columns.filter(i=>!s.has(i)))l(t,`ALTER TABLE ${c(e.name)} ADD COLUMN ${c(o)}`)}else l(t,e.sql);const n=new Set(h(t,"SELECT name FROM sqlite_master WHERE type = 'index'").map(s=>String(s.name)));for(const s of e.indexes.filter(({name:o})=>!n.has(o)))l(t,s.sql)},De=(t,e,r)=>{Le(t);const n=Ie(t,r.userTable,_(t)[r.userTable]?.watermark??0);if(n>0&&!r.force)throw new b("AUTH_MOVE_TARGET_NOT_EMPTY",`the pinned auth object has ${String(n)} user(s) the copy did not write; copying into it would merge two user bases — pass force to copy anyway`,{data:{users:n}});const s=new Set(N(t)),o=_(t);for(const a of e){Ue(t,a,s);const u=o[a.name],f=JSON.stringify(a.columns.toSorted((m,w)=>m.localeCompare(w)));u===void 0?l(t,`INSERT INTO ${c(g)} (tbl, after, done, pass, cols) VALUES (?, 0, 0, 1, ?)`,a.name,f):l(t,`UPDATE ${c(g)} SET cols = ? WHERE tbl = ?`,f,a.name)}l(t,`UPDATE ${c(g)} SET watermark = (SELECT coalesce(max(rowid), 0) FROM ${c(r.userTable)}) WHERE tbl = ?`,r.userTable);const i=_(t);return{cursors:Object.fromEntries(e.map(({name:a})=>[a,i[a]?.after??0])),done:Object.fromEntries(e.map(({name:a})=>[a,i[a]?.done??!1])),targetRows:Object.fromEntries(e.map(({name:a})=>[a,Q(t,a)]))}},te=(t,e,r,n,s)=>h(t,`SELECT ${s.map(o=>c(o)).join(", ")} FROM ${c(e)} WHERE ${r.map(o=>`${c(o)} IS ?`).join(" AND ")} LIMIT 1`,...n)[0],k=(t,e)=>{if(!e)throw new b("AUTH_MOVE_CONFLICT",`a row copied into ${t} collides with a row the pinned auth object holds on its own — nothing from this page was written; pass force to keep the pinned object's rows`,{data:{table:t}});return"conflicts"},ke=t=>t instanceof Error&&t.message.includes("UNIQUE constraint failed"),Ce=(t,e)=>{if(e.prior!==void 0)return k(e.table,e.force);try{l(t,`INSERT INTO ${c(e.table)} (${e.columns.map(r=>c(r)).join(", ")}) VALUES (${e.columns.map(()=>"?").join(", ")})`,...e.columns.map(r=>e.row[r]))}catch(r){if(!ke(r))throw r;return k(e.table,e.force)}return"copied"},Me=(t,e,r)=>{const n=C(r,e.columns).toString(16);return n===e.h?"unchanged":e.prior===void 0||n!==e.prior?k(e.table,e.force):(l(t,`UPDATE ${c(e.table)} SET ${e.columns.map(s=>`${c(s)} = ?`).join(", ")} WHERE ${e.keys.map(s=>`${c(s)} IS ?`).join(" AND ")}`,...e.columns.map(s=>e.row[s]),...e.key),"updated")},xe=(t,e,r,n)=>{const{force:s,keys:o,pass:i}=n,a=Object.keys(r).toSorted((A,I)=>A.localeCompare(I));if(a.length+o.length>be)throw new TypeError(`${e} has too many columns to copy`);const u=o.map(A=>r[A]??null),f=z(u),m=C(r,a).toString(16),w=h(t,`SELECT h FROM ${c(S)} WHERE tbl = ? AND k = ?`,e,f)[0]?.h,p={columns:a,force:s,h:m,key:u,keys:o,prior:typeof w=="string"?w:void 0,row:r,table:e},y=p.prior===m?void 0:te(t,e,o,u,a);let R="unchanged";return p.prior!==m&&(R=y===void 0?Ce(t,p):Me(t,p,y)),l(t,`INSERT INTO ${c(S)} (tbl, k, h, pass) VALUES (?, ?, ?, ?) ON CONFLICT (tbl, k) DO UPDATE SET h = excluded.h, pass = excluded.pass`,e,f,m,i),R},Pe=(t,e,r)=>{const{counts:n,force:s,keys:o,marker:i}=r,a=h(t,`SELECT k, h FROM ${c(S)} WHERE tbl = ? AND pass < ?`,e,i.pass);for(const u of a){const f=E(JSON.parse(String(u.k))),m=te(t,e,o,f,i.columns);m!==void 0&&(C(m,i.columns).toString(16)===String(u.h)?(l(t,`DELETE FROM ${c(e)} WHERE ${o.map(w=>`${c(w)} IS ?`).join(" AND ")}`,...f),n.deleted+=1):n[k(e,s)]+=1),l(t,`DELETE FROM ${c(S)} WHERE tbl = ? AND k = ?`,e,String(u.k))}},Fe=async(t,e,r)=>{const n=_(t)[e];if(n===void 0)throw new TypeError(`table ${JSON.stringify(e)} was not part of this move`);return Y(t,()=>{const s={conflicts:0,copied:0,deleted:0,unchanged:0,updated:0},o=K(t,e);for(const i of r.rows)s[xe(t,e,i,{force:r.force,keys:o,pass:n.pass})]+=1;return r.final&&Pe(t,e,{counts:s,force:r.force,keys:o,marker:n}),l(t,`UPDATE ${c(g)} SET after = ?, done = ? WHERE tbl = ?`,r.last,r.final?1:0,e),s})},He=async(t,e)=>{const r=N(t);return await Y(t,()=>{const n=r.filter(o=>e[o]===void 0);if(n.length>0)throw new b("AUTH_MOVE_INCOMPLETE",`refusing to purge the un-pinned auth object: ${n.join(", ")} not fully copied — run the copy to done: true first`,{data:{unfinished:n}});const s=r.filter(o=>ee(t,o)!==e[o]);if(s.length>0)throw new b("AUTH_MOVE_SOURCE_CHANGED",`refusing to purge the un-pinned auth object: ${s.join(", ")} changed since it was copied — run the copy again to carry the changes, then purge`,{data:{changed:s}});for(const o of r)l(t,`DROP TABLE ${c(o)}`);l(t,`CREATE TABLE IF NOT EXISTS ${c(D)} (at INTEGER NOT NULL)`),l(t,`INSERT INTO ${c(D)} (at) VALUES (?)`,Date.now())}),{dropped:r}},Be=t=>{if(L(t,D).length>0)throw new b("AUTH_MOVE_SOURCE_PURGED","the un-pinned auth object was purged after its tables were copied; anything in it now is new, and copying it would delete the pinned object's users")},qe=new Set(["fingerprints","manifest","page","purge"]),ve=(t,e,r)=>{e.prepare();const n=_(t);return{copied:r?Object.fromEntries(Object.entries(n).map(([s,o])=>[s,_e(t,s,o.pass)])):{},markers:n,order:e.order()}},We=(t,e)=>{for(const r of e)l(t,`UPDATE ${c(g)} SET after = 0, done = 0, pass = pass + 1 WHERE tbl = ?`,r);return{}},Ve=[/(?:UNIQUE|NOT NULL|CHECK|FOREIGN KEY|PRIMARY KEY) constraint failed/u,/no such (?:table|column)/u,/datatype mismatch/u,/SQLITE_[A-Z_]+/u],Ge=t=>{const e=t instanceof Error?t.message:String(t);for(const r of Ve){const n=r.exec(e);if(n)return n[0]}return t instanceof Error?t.name:"unknown"},Xe=async(t,e,r)=>{switch(qe.has(String(e.op))&&Be(t),e.op){case"begin":return r.prepare(),De(t,e.tables,{force:e.force===!0,userTable:r.userTable()});case"fingerprints":return{fingerprints:Object.fromEntries(N(t).map(n=>[n,ee(t,n)]))};case"manifest":return{tables:Re(t)};case"page":{const n=Oe(t,v(t,e.table),Number(e.after??0));return{last:n.last,rows:T(n.rows)}}case"purge":{const n=await He(t,e.expected??{});return r.onPurge(),n}case"rescan":return We(t,e.tables??[]);case"status":return ve(t,r,e.fingerprints===!0);case"write":return Fe(t,v(t,e.table),{final:e.final===!0,force:e.force===!0,last:Number(e.last),rows:E(e.rows)});default:throw new TypeError(`unknown move op ${JSON.stringify(e.op)}`)}},Ye=async(t,e,r)=>{try{return await Xe(t,e,r)}catch(n){if(n instanceof b)throw n;console.error("@lunora/auth: auth move step failed",n);const s=String(e.op),o=typeof e.table=="string"?e.table:void 0,i=Ge(n);throw new b("AUTH_MOVE_FAILED",`auth move ${s}${o===void 0?"":` on ${o}`} failed: ${i}`,{data:{cause:i,op:s,...o===void 0?{}:{table:o}}})}},Je=(t,e)=>{const r=n=>{const s=e.first.indexOf(n);if(s!==-1)return s;const o=e.last.indexOf(n);return o===-1?e.first.length:e.first.length+1+o};return t.toSorted((n,s)=>r(n.name)-r(s.name)||n.name.localeCompare(s.name))},Ke=(t,e)=>({conflicts:0,copied:0,deleted:0,sourceRows:t.rows,table:t.name,targetRows:e,unchanged:0,updated:0}),lt=t=>{const e=async(r,n)=>{const s=await t(r,n);if(!s.ok){let o={};try{const i=await s.json();typeof i.error=="object"&&i.error!==null&&(o=i.error)}catch{}throw new b(o.code??"AUTH_MOVE_FAILED",o.message??`auth move ${String(n.op)} on the ${r} object failed (${String(s.status)})`,{data:o.data,status:s.status})}return s.json()};return{copy:async(r={})=>{const n=r.force===!0,s=await e("target",{op:"status"}),{tables:o}=await e("source",{op:"manifest"}),i=await e("target",{force:n,op:"begin",tables:o}),a=[];let u=0;for(const p of Je(o,s.order)){const y=Ke(p,i.targetRows[p.name]??0);let R=i.cursors[p.name]??0;for(a.push(y);;){if(u>=Ae)return{done:!1,tables:a};u+=1;const A=await e("source",{after:R,op:"page",table:p.name}),I=E(A.rows),M=I.length<G;if(I.length===0&&i.done[p.name]===!0)break;const O=await e("target",{final:M,force:n,last:A.last??R,op:"write",rows:A.rows,table:p.name});if(y.conflicts+=O.conflicts,y.copied+=O.copied,y.deleted+=O.deleted,y.unchanged+=O.unchanged,y.updated+=O.updated,y.targetRows+=O.copied-O.deleted,R=A.last??R,M)break}}const{fingerprints:f}=await e("source",{op:"fingerprints"}),m=await e("target",{fingerprints:!0,op:"status"}),w=Object.keys(f).filter(p=>m.copied[p]!==f[p]);return w.length>0&&await e("target",{op:"rescan",tables:w}),{done:w.length===0,tables:a}},purge:async()=>{const{copied:r,markers:n}=await e("target",{fingerprints:!0,op:"status"}),s=Object.fromEntries(Object.entries(n).filter(([,o])=>o.done).map(([o])=>[o,r[o]]));return e("source",{expected:s,op:"purge"})}}},Qe="/__lunora/auth/session",ze="/__lunora/auth/audit",Ze="x-lunora-auth-do-secret",et=t=>{if(t===null||typeof t!="object"||Array.isArray(t))return{error:"body must be a JSON object"};const e={};for(const[r,n]of Object.entries(t))if(r==="limit"||r==="sinceSeq"){if(typeof n!="number"||!Number.isFinite(n))return{error:`"${r}" must be a finite number`};e[r]=n}else if(r==="actorId"||r==="event"){if(typeof n!="string")return{error:`"${r}" must be a string`};e[r]=n}else return{error:`unknown audit read option "${r}"`};return{options:e}};class mt{#r;#a;#c;#e;#t;#n=!1;#o={first:["user"],last:[]};constructor(e,r,n={}){this.#e=e.storage,this.#r=P(e.storage),this.#c=r,this.#a=n}#s(){if(this.#t!==void 0)return this.#t;const e=this.#c(),r=ae(e),{tables:n}=x(r);if(this.#o={first:[n.user?.modelName??"user",n.account?.modelName??"account",n.session?.modelName??"session"],last:[oe,n.rateLimit?.modelName??"rateLimit"]},!this.#n){for(const s of fe(r))[...this.#e.sql.exec(s)];for(const s of le(r,o=>this.#u(o)))[...this.#e.sql.exec(s)];this.#f(r),this.#n=!0}return this.#t=ce({...e,database:ne(this.#e)}),this.#t}#u(e){return[...this.#e.sql.exec("SELECT name FROM pragma_table_info(?)",e)].map(n=>String(n.name))}#f(e){const{tables:r}=x(e),{account:n}=r;if(n===void 0)return;const s=Object.entries(n.fields).map(([o,i])=>i.fieldName??o);if(!(de(s)||!this.#u(n.modelName).includes("issuer")))try{const o=[...this.#e.sql.exec("SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ?",n.modelName)].map(i=>({name:String(i.name),sql:typeof i.sql=="string"?i.sql:void 0}));for(const i of pe(n.modelName,Ee(o)))[...this.#e.sql.exec(i)]}catch(o){console.error("@lunora/auth: could not drop the reverted `account.issuer` column; if it is still present, sign-ups will fail until it is removed.",o)}}async#l(e){if(!this.#i(e))return Response.json({error:"unauthorized"},{status:401});let r;try{r=await e.json()}catch{return Response.json({error:"invalid body"},{status:400})}const n=et(r??{});if("error"in n)return Response.json({error:n.error},{status:400});await se(this.#r);const s=await ie(this.#r).read(n.options);return Response.json({entries:s})}async#m(e){if(e.method!=="POST"||!this.#i(e))return Response.json({error:"unauthorized"},{status:401});try{const r=await e.json(),n=await Ye(this.#e,r,{onPurge:()=>{this.#n=!1,this.#t=void 0,this.#r=P(this.#e)},order:()=>this.#o,prepare:()=>{this.#s()},userTable:()=>this.#o.first[0]??"user"});return Response.json(n)}catch(r){const{body:n,redacted:s,status:o}=re(r,{encodeData:i=>i,fallbackCode:"AUTH_MOVE_FAILED",redactedMessage:"auth move failed"});return s&&console.error("@lunora/auth: auth move failed",r),Response.json({error:n},{status:o})}}#i(e){const{internalSecret:r}=this.#a;if(r===void 0||r==="")return!1;const n=e.headers.get(Ze);return n!==null&&he(n,r)}async#d(e){if(!this.#i(e))return Response.json({error:"unauthorized"},{status:401});const n=await this.#s().api.getSession({headers:e.headers}),s=n?.user.id;if(s===void 0)return Response.json({});const o=n?.session.expiresAt,i=n?.user,a=i?.role;return Response.json({...typeof i?.email=="string"&&i.email.length>0?{email:i.email}:{},...o instanceof Date?{expiresAtMs:o.getTime()}:{},...typeof i?.name=="string"&&i.name.length>0?{name:i.name}:{},...typeof a=="string"&&a.length>0?{role:a}:{},userId:s})}async fetch(e){const r=new URL(e.url);if(r.pathname===Qe)return this.#d(e);if(r.pathname===ze)return this.#l(e);if(r.pathname===ge)return this.#m(e);const n=this.#s();return await me(n,e)??await ue(n,e)??Response.json({error:"not an auth route"},{status:404})}}export{Ze as I,mt as L,ge as M,ze as R,Qe as a,lt as c};
@@ -0,0 +1 @@
1
+ import{LunoraError as h}from"@lunora/errors";import{DEFAULT_AUTH_BASE_PATH as p}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";const l=Symbol.for("lunora.auth.mcpResource"),i=/\/$/u,d="/.well-known/oauth-protected-resource",f="/.well-known/oauth-authorization-server",R=t=>`${d}${new URL(t).pathname.replace(i,"")}`,w=t=>`${f}${t.replace(i,"")}`,v=t=>{if(typeof t=="string")try{const e=new URL(t).pathname.replace(i,"");return e===""?void 0:e}catch{return}},m=(t,e,r)=>{const n=e.find(o=>o.id==="jwt")?.options,s=r?.disableJwtPlugin===!0?void 0:n?.jwt?.issuer;if(typeof s=="string")try{return new URL(s).pathname}catch{}return v(t.baseURL)??t.basePath??p},y=t=>(Array.isArray(t)?t:[]).map(e=>typeof e=="object"&&e!==null?e.identifier:e).filter(e=>typeof e=="string"),E=t=>{const e=t[l];if(typeof e=="string")return e;const r=t.options,n=[...new Set(y(r?.resources))];if(n.length===1)return n[0];if(n.length>1&&Array.isArray(r?.clientRegistrationDefaultResources))throw new h("AUTH_MCP_RESOURCE_AMBIGUOUS",`@lunora/auth cannot tell which of the provider's resources (${n.join(", ")}) is the MCP resource. Use \`mcp\` from "@lunora/auth/plugins", which records it, and pass the plugin object as is.`)},u=t=>{const e=t.plugins??[],r=e.filter(o=>o.id==="oauth-provider");if(r.length===0)return[];const n=r.map(o=>E(o)).filter(o=>o!==void 0).map(o=>R(o)),s=m(t,e,r[0]?.options);return[...new Set([...n,w(s)])]},a=new WeakMap,S=(t,e)=>{let r=a.get(t);return r===void 0&&(r=u(t.options(e)),a.set(t,r)),r},A=t=>t?.status===404?void 0:t,c=new WeakMap,P=(t,e)=>(t.method==="GET"||t.method==="HEAD")&&e.has(new URL(t.url).pathname),_=async(t,e)=>{let r=c.get(t);if(r===void 0&&(r=new Set(u(t.options)),c.set(t,r)),!!P(e,r))return A(await t.handler(e))};export{l as MCP_RESOURCE_KEY,u as authDiscoveryPaths,S as authDiscoveryPathsFor,w as authorizationServerPath,_ as handleAuthDiscoveryRequest,P as isDiscoveryRequest,R as protectedResourcePath,A as unlessNotFound};
@@ -1 +1 @@
1
- import{getAuthTablesWithResolvedIndexes as w,invalidateSchemaChecks as y}from"@better-auth/core/db/internal";import{LunoraError as R}from"@lunora/errors";import{getMigrations as l}from"better-auth/db/migration";import{q as c}from"./quote-identifier-CGiYFBvY.mjs";import{resolveAuthOptions as _}from"./createAuth-8aUw8v8D.mjs";import{schemaDeclaresIssuer as x,legacyIssuerCleanupStatements as M,indexesReferencingIssuer as T}from"./legacyIssuerCleanupStatements-D9Kf8OD1.mjs";const A=/pragma_index_list\s*\(/iu,N=/sqlite_master/iu,C=/^["`[]|["\]`]$/gu,D=/\s+/u,S=/create\s+unique\s+index/iu,O=/\bwhere\b/iu,P=e=>A.test(e)&&N.test(e),L=e=>(e.trim().split(D)[0]??"").replaceAll(C,"").trim(),U=e=>{const t=e.indexOf("(");if(t===-1)return{columns:[],tail:""};let s=0;for(let n=t;n<e.length;n+=1){const a=e[n];if(a==="(")s+=1;else if(a===")"&&(s-=1,s===0))return{columns:e.slice(t+1,n).split(",").map(o=>L(o)).filter(o=>o!==""),tail:e.slice(n+1)}}return{columns:[],tail:""}},q=async e=>{const t=e.prepare("SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'index'"),{results:s}=await t.all(),n=[];for(const a of s??[]){const o=a.name,r=a.tbl_name,i=a.sql;if(typeof o!="string"||typeof r!="string")continue;if(typeof i!="string"){n.push({columnPosition:0,indexName:o,isPartial:0,isUnique:1,tableName:r});continue}const f=S.test(i)?1:0,{columns:h,tail:b}=U(i),E=O.test(b)?1:0;for(const[I,g]of h.entries())n.push({columnName:g,columnPosition:I,indexName:o,isPartial:E,isUnique:f,tableName:r})}return n},v=e=>{const t=async()=>{const n=await q(e);return{meta:{},results:n,success:!0}},s={all:t,bind:()=>s,run:t};return s},W=e=>new Proxy(e,{get(t,s,n){if(s==="prepare")return o=>P(o)?v(t):t.prepare(o);const a=Reflect.get(t,s,n);return typeof a=="function"?a.bind(t):a}}),m=e=>typeof e=="object"&&e!==null&&typeof e.prepare=="function"&&typeof e.batch=="function",p=e=>{const{database:t}=e;if(!(t&&typeof t!="function"))throw new R("AUTH_MIGRATOR_UNSUPPORTED",t?"@lunora/auth: this auth instance's `database` is a custom adapter, which better-auth's migrator cannot drive.":"@lunora/auth: this auth instance has no `database`, so better-auth's migrator has nothing to introspect.")},d=e=>m(e.database)?{...e,database:W(e.database)}:e,F=async(e,t)=>{const s=c("t");try{return await e.prepare(`SELECT ${s}.${c("issuer")} FROM ${c(t)} AS ${s} LIMIT 0`).run(),!0}catch{return!1}},H=async e=>{const{database:t}=e,{tables:s}=w(e),{account:n}=s;if(n!==void 0&&!x(Object.entries(n.fields).map(([a,o])=>o.fieldName??a))&&m(t)&&await F(t,n.modelName))try{const{results:a}=await t.prepare("SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ?").bind(n.modelName).all();await t.batch(M(n.modelName,T(a)).map(o=>t.prepare(o)))}catch(a){console.error("@lunora/auth: could not drop the reverted `account.issuer` column; if it is still present, sign-ups will fail until it is removed.",a)}},$=e=>{const{database:t}=e;typeof t=="object"&&t!==null&&y(t)},u=new WeakMap,Y=async e=>{const{options:t}=e;p(t);const s=u.get(t);if(s){await s;return}const n=(async()=>{const{runMigrations:a}=await l(d(t));await a(),await H(t),$(t)})();u.set(t,n);try{await n}catch(a){throw u.delete(t),a}},z=async e=>{const t=_(e);p(t);const{compileMigrations:s}=await l(d(t));return s()};export{z as compileMigrationsSql,Y as ensureMigrated};
1
+ import{getAuthTablesWithResolvedIndexes as w,invalidateSchemaChecks as y}from"@better-auth/core/db/internal";import{LunoraError as R}from"@lunora/errors";import{getMigrations as l}from"better-auth/db/migration";import{q as c}from"./quote-identifier-CGiYFBvY.mjs";import{resolveAuthOptions as _}from"./createAuth-BdVltKyf.mjs";import{schemaDeclaresIssuer as x,legacyIssuerCleanupStatements as M,indexesReferencingIssuer as T}from"./legacyIssuerCleanupStatements-D9Kf8OD1.mjs";const A=/pragma_index_list\s*\(/iu,N=/sqlite_master/iu,C=/^["`[]|["\]`]$/gu,D=/\s+/u,S=/create\s+unique\s+index/iu,O=/\bwhere\b/iu,P=e=>A.test(e)&&N.test(e),L=e=>(e.trim().split(D)[0]??"").replaceAll(C,"").trim(),U=e=>{const t=e.indexOf("(");if(t===-1)return{columns:[],tail:""};let s=0;for(let n=t;n<e.length;n+=1){const a=e[n];if(a==="(")s+=1;else if(a===")"&&(s-=1,s===0))return{columns:e.slice(t+1,n).split(",").map(o=>L(o)).filter(o=>o!==""),tail:e.slice(n+1)}}return{columns:[],tail:""}},q=async e=>{const t=e.prepare("SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'index'"),{results:s}=await t.all(),n=[];for(const a of s??[]){const o=a.name,r=a.tbl_name,i=a.sql;if(typeof o!="string"||typeof r!="string")continue;if(typeof i!="string"){n.push({columnPosition:0,indexName:o,isPartial:0,isUnique:1,tableName:r});continue}const f=S.test(i)?1:0,{columns:h,tail:b}=U(i),E=O.test(b)?1:0;for(const[I,g]of h.entries())n.push({columnName:g,columnPosition:I,indexName:o,isPartial:E,isUnique:f,tableName:r})}return n},v=e=>{const t=async()=>{const n=await q(e);return{meta:{},results:n,success:!0}},s={all:t,bind:()=>s,run:t};return s},W=e=>new Proxy(e,{get(t,s,n){if(s==="prepare")return o=>P(o)?v(t):t.prepare(o);const a=Reflect.get(t,s,n);return typeof a=="function"?a.bind(t):a}}),m=e=>typeof e=="object"&&e!==null&&typeof e.prepare=="function"&&typeof e.batch=="function",p=e=>{const{database:t}=e;if(!(t&&typeof t!="function"))throw new R("AUTH_MIGRATOR_UNSUPPORTED",t?"@lunora/auth: this auth instance's `database` is a custom adapter, which better-auth's migrator cannot drive.":"@lunora/auth: this auth instance has no `database`, so better-auth's migrator has nothing to introspect.")},d=e=>m(e.database)?{...e,database:W(e.database)}:e,F=async(e,t)=>{const s=c("t");try{return await e.prepare(`SELECT ${s}.${c("issuer")} FROM ${c(t)} AS ${s} LIMIT 0`).run(),!0}catch{return!1}},H=async e=>{const{database:t}=e,{tables:s}=w(e),{account:n}=s;if(n!==void 0&&!x(Object.entries(n.fields).map(([a,o])=>o.fieldName??a))&&m(t)&&await F(t,n.modelName))try{const{results:a}=await t.prepare("SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ?").bind(n.modelName).all();await t.batch(M(n.modelName,T(a)).map(o=>t.prepare(o)))}catch(a){console.error("@lunora/auth: could not drop the reverted `account.issuer` column; if it is still present, sign-ups will fail until it is removed.",a)}},$=e=>{const{database:t}=e;typeof t=="object"&&t!==null&&y(t)},u=new WeakMap,Y=async e=>{const{options:t}=e;p(t);const s=u.get(t);if(s){await s;return}const n=(async()=>{const{runMigrations:a}=await l(d(t));await a(),await H(t),$(t)})();u.set(t,n);try{await n}catch(a){throw u.delete(t),a}},z=async e=>{const t=_(e);p(t);const{compileMigrations:s}=await l(d(t));return s()};export{z as compileMigrationsSql,Y as ensureMigrated};
@@ -0,0 +1 @@
1
+ import{LunoraError as c}from"@lunora/errors";import{betterAuth as l}from"better-auth";import{getIP as u}from"better-auth/api";import{o as f}from"./on-cloudflare-edge-Dx6gFjBO.mjs";import{authDiscoveryPaths as h}from"./authDiscoveryPaths-B14lM14u.mjs";import{validateSessionPolicy as m}from"./sessionPresets-C867Mlo4.mjs";const n="cf-connecting-ip",p=e=>{const t=e!==void 0&&e.length>0;return f()?t?[n,"x-forwarded-for"]:[n]:t?["x-forwarded-for"]:[]},a="/**",A=100,E=e=>{const t=e.rateLimit?.customRules??{};return t[a]!==void 0?t:{...t,[a]:(o,r)=>u(o,e)===null?{max:r.max*A,window:r.window}:r}},d=32,L=e=>{const t=typeof e=="string"?e.trim().length:0;return t>0&&t<d},i=e=>typeof e=="string"?e.toLowerCase().startsWith("http://"):e&&typeof e=="object"?e.protocol==="http"?!0:e.protocol==="https"?!1:typeof e.fallback=="string"&&e.fallback.toLowerCase().startsWith("http://"):!1,g=new Set(["127.0.0.1","localhost"]),v=e=>{if(typeof e!="string")return;let t;try{t=new URL(e)}catch{return}if(!(t.protocol!=="http:"||!g.has(t.hostname)))return{allowedHosts:[t.hostname,`${t.hostname}:*`],fallback:e,protocol:"http"}},w=e=>{const t=v(e.baseURL)??e.baseURL;if(L(e.secret)){const s=`@lunora/auth: AUTH_SECRET is only ${String(e.secret?.trim().length)} characters. Use at least ${String(d)} for a brute-force-resistant secret — generate one with \`openssl rand -hex 32\`.`;if(!i(t))throw new c("INTERNAL",s);console.warn(s)}const o=e.advanced??{},r=o.ipAddress??{};return{...e,advanced:{...o,defaultCookieAttributes:o.defaultCookieAttributes??{httpOnly:!0,path:"/",sameSite:"lax"},ipAddress:{...r,...r.ipAddressHeaders===void 0?{ipAddressHeaders:p(r.trustedProxies)}:{}},...o.useSecureCookies===void 0?{useSecureCookies:!i(t)}:{}},baseURL:t}},C=e=>{const t=w(e),o=t.rateLimit?.enabled===void 0,r=t.rateLimit?.storage===void 0&&t.rateLimit?.enabled!==!1,s=t.rateLimit?.enabled===!1?{}:{rateLimit:{...t.rateLimit,...o?{enabled:!0}:{},...r?{storage:"database"}:{},customRules:E(t)}};return{...t,...s,...t.session?.cookieCache===void 0?{session:{...t.session,cookieCache:{enabled:!0,maxAge:60}}}:{}}},R=e=>{if(!e.secret||e.secret.trim()==="")throw new c("INTERNAL",'@lunora/auth: `secret` is required. Set AUTH_SECRET locally in .dev.vars (`lunora env set AUTH_SECRET "$(openssl rand -hex 32)"`), and in production with `wrangler secret put AUTH_SECRET`.');return e.session&&m(e.session),h(e),l(C(e))};export{R as createAuth,C as resolveAuthOptions};
@@ -0,0 +1 @@
1
+ import{LunoraError as v}from"@lunora/errors";import{I as d,a as w,c as A,R,M as E}from"./auth-do-l5HwPI4A.mjs";import{isDiscoveryRequest as g,unlessNotFound as j}from"./authDiscoveryPaths-B14lM14u.mjs";import{isAuthRoutePath as T,DEFAULT_AUTH_BASE_PATH as b}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";const H=a=>{const{internalSecret:i,jurisdiction:c,objectName:l="auth"}=a,m=new Set(a.discoveryPaths),h=a.namespace;let{namespace:r}=a;if(r&&c!==void 0){if(typeof r.jurisdiction!="function")throw new TypeError(`@lunora/auth: Durable Object namespace does not support jurisdiction("${c}") — update @cloudflare/workers-types or remove the jurisdiction option`);r=r.jurisdiction(c)}const u=()=>{if(r)return r.get(r.idFromName(l))},y=async(t,n,o)=>{if(!i)return;const s=u();if(!s)return;const e=await s.fetch(new Request(new URL(t,n),{body:JSON.stringify(o),headers:{"content-type":"application/json",[d]:i},method:"POST"}));return e.ok?e:void 0},p=r,f=h&&p&&c!==void 0?A(async(t,n)=>{if(!i)throw new v("AUTH_MOVE_NOT_CONFIGURED","copying the auth object needs its internal secret; set the auth object's `internalSecret`");const o=t==="source"?h:p;return o.get(o.idFromName(l)).fetch(new Request(new URL(E,"https://auth-do.invalid"),{body:JSON.stringify(n),headers:{"content-type":"application/json",[d]:i},method:"POST"}))}):void 0;return{...f===void 0?{}:{jurisdictionMove:f},auditReader:{read:async t=>{const n=await y(R,"https://auth-do.invalid",t);return n?(await n.json())?.entries??[]:[]}},authHandler:async t=>{if(T(new URL(t.url).pathname,b))return u()?.fetch(t)},discoveryHandler:async t=>{if(g(t,m))return j(await u()?.fetch(t))},resolveIdentity:async t=>{if(!i)return null;const n=u();if(!n)return null;const o=new Headers(t.headers);o.set(d,i);const s=await n.fetch(new Request(new URL(w,t.url),{headers:o}));if(!s.ok)return null;const e=await s.json();return e?.userId?{...typeof e.email=="string"&&e.email.length>0?{email:e.email}:{},...typeof e.expiresAtMs=="number"&&Number.isFinite(e.expiresAtMs)?{expiresAtMs:e.expiresAtMs}:{},...typeof e.name=="string"&&e.name.length>0?{name:e.name}:{},...typeof e.role=="string"&&e.role.length>0?{role:e.role}:{},userId:e.userId}:null}}};export{H as createDoAuthWiring};
@@ -0,0 +1 @@
1
+ import{mcp as o,requireMcpAuth as s}from"@better-auth/mcp";import{LunoraError as c}from"@lunora/errors";import{MCP_RESOURCE_KEY as u,protectedResourcePath as i,authorizationServerPath as n}from"./authDiscoveryPaths-B14lM14u.mjs";import{DEFAULT_AUTH_BASE_PATH as p}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";const a=e=>{if(!(typeof e=="string"&&URL.canParse(e)))throw new c("AUTH_MCP_RESOURCE_INVALID",`requireMcpAuth needs \`resource\` set to the absolute URL passed to mcp({ resource }), got ${JSON.stringify(e)}. Without it no issued token's audience matches and every request is refused.`)},d=e=>{const r=o(e);return Object.defineProperty(r,u,{value:e.resource}),r},E=(e,r,t)=>(a(t.resource),s(e,r,t)),P=(e,r=p)=>[i(e),n(r)];export{d as mcp,P as mcpDiscoveryPaths,E as requireMcpAuth};
@@ -20,12 +20,15 @@ type a as IssuedSignUpInvitation,
20
20
  * re-export.
21
21
  */
22
22
  type S as SignUpInvitation, i as inviteOnly } from "./packem_shared/invite-only.d-DKyHHkkQ.mjs";
23
+ import { RequireMcpAuthOptions, McpOptions, mcp as mcp$1, requireMcpAuth as requireMcpAuth$1 } from '@better-auth/mcp';
24
+ export { createMcpProtectedRequestHandler } from '@better-auth/mcp';
23
25
  import { BetterAuthPlugin } from 'better-auth';
24
26
  export { apiKey } from '@better-auth/api-key';
25
- export { createMcpProtectedRequestHandler, mcp, requireMcpAuth } from '@better-auth/mcp';
27
+ export { cimd } from '@better-auth/cimd';
26
28
  export { oauthDeviceAuthorization, oauthProvider } from '@better-auth/oauth-provider';
27
29
  export { passkey } from '@better-auth/passkey';
28
30
  export { scim } from '@better-auth/scim';
31
+ export { createInsufficientScopeError } from 'better-auth/oauth2';
29
32
  export { captcha, lastLoginMethod, oneTap } from 'better-auth/plugins';
30
33
  export { createAccessControl } from 'better-auth/plugins/access';
31
34
  export { admin } from 'better-auth/plugins/admin';
@@ -47,6 +50,45 @@ export { siwe } from 'better-auth/plugins/siwe';
47
50
  export { twoFactor } from 'better-auth/plugins/two-factor';
48
51
  export { username } from 'better-auth/plugins/username';
49
52
  import "./packem_shared/create-auth.d-D1CKOFWc.mjs";
53
+ /** better-auth's `requireMcpAuth` options, with `resource` required. */
54
+ type LunoraRequireMcpAuthOptions = {
55
+ /**
56
+ * The protected-resource URL tokens must be bound to — the same value passed
57
+ * to `mcp({ resource })`.
58
+ */
59
+ resource: string;
60
+ } & RequireMcpAuthOptions;
61
+ /** The auth instance shape better-auth's `requireMcpAuth` accepts. */
62
+ type McpAuthInstance = Parameters<typeof requireMcpAuth$1>[0];
63
+ /** The protected handler: receives the request and the verified token claims. */
64
+ type McpProtectedHandler = Parameters<typeof requireMcpAuth$1>[1];
65
+ /**
66
+ * The MCP authorization server: better-auth's `oauthProvider` configured for MCP,
67
+ * serving the RFC 9728 protected-resource metadata for `resource`. Lunora's
68
+ * worker serves that document and the authorization-server metadata outside the
69
+ * auth base path on its own; that needs this `mcp`, not `@better-auth/mcp`'s.
70
+ */
71
+ declare const mcp: (options: McpOptions) => ReturnType<typeof mcp$1>;
72
+ /**
73
+ * Protect an MCP route on the same deployment as the authorization server.
74
+ * Verifies the bearer token's signature, issuer, audience and expiry, and hands
75
+ * the verified claims to `handler`.
76
+ * @throws LunoraError when `resource` is missing, empty or not an absolute URL.
77
+ */
78
+ declare const requireMcpAuth: (auth: McpAuthInstance, handler: McpProtectedHandler, options: LunoraRequireMcpAuthOptions) => ((request: Request) => Promise<Response>);
79
+ /**
80
+ * The two `.well-known` paths an MCP client fetches before it authorizes:
81
+ * the RFC 9728 protected-resource metadata for `resource` (where the 401
82
+ * challenge points), then the RFC 8414 authorization-server metadata for the
83
+ * issuer.
84
+ *
85
+ * The Lunora worker already serves both for an `.auth()` declaration with
86
+ * `mcp()`; this is for routing them yourself, e.g. in front of a hand-built
87
+ * worker. Pass `authBasePath` when `createAuth` was given a `basePath` other
88
+ * than `/api/auth`.
89
+ * @throws TypeError when `resource` is not an absolute URL.
90
+ */
91
+ declare const mcpDiscoveryPaths: (resource: string, authBasePath?: string) => ReadonlyArray<string>;
50
92
  /** Organization sub-features a UI branches on. */
51
93
  interface UiConfigOrganization {
52
94
  /** Whether an ordinary user may create one at all. */
@@ -124,7 +166,7 @@ interface UiConfigOptions {
124
166
  * consumes anyway.
125
167
  */
126
168
  declare const uiConfig: (options?: UiConfigOptions) => BetterAuthPlugin;
127
- export {
169
+ export { type LunoraRequireMcpAuthOptions,
128
170
  /**
129
171
  * Publish which plugins and social providers this deployment enabled, at
130
172
  * `GET {basePath}/ui-config`, so an auth UI configures itself instead of making
@@ -142,4 +184,4 @@ type UiConfigOrganization,
142
184
  * `GET {basePath}/ui-config`, so an auth UI configures itself instead of making
143
185
  * you restate the list client-side. Lunora's own, not a better-auth re-export.
144
186
  */
145
- type UiConfigPayload, uiConfig };
187
+ type UiConfigPayload, mcp, mcpDiscoveryPaths, requireMcpAuth, uiConfig };
package/dist/plugins.d.ts CHANGED
@@ -20,12 +20,15 @@ type a as IssuedSignUpInvitation,
20
20
  * re-export.
21
21
  */
22
22
  type S as SignUpInvitation, i as inviteOnly } from "./packem_shared/invite-only.d-CsPJWAwM.js";
23
+ import { RequireMcpAuthOptions, McpOptions, mcp as mcp$1, requireMcpAuth as requireMcpAuth$1 } from '@better-auth/mcp';
24
+ export { createMcpProtectedRequestHandler } from '@better-auth/mcp';
23
25
  import { BetterAuthPlugin } from 'better-auth';
24
26
  export { apiKey } from '@better-auth/api-key';
25
- export { createMcpProtectedRequestHandler, mcp, requireMcpAuth } from '@better-auth/mcp';
27
+ export { cimd } from '@better-auth/cimd';
26
28
  export { oauthDeviceAuthorization, oauthProvider } from '@better-auth/oauth-provider';
27
29
  export { passkey } from '@better-auth/passkey';
28
30
  export { scim } from '@better-auth/scim';
31
+ export { createInsufficientScopeError } from 'better-auth/oauth2';
29
32
  export { captcha, lastLoginMethod, oneTap } from 'better-auth/plugins';
30
33
  export { createAccessControl } from 'better-auth/plugins/access';
31
34
  export { admin } from 'better-auth/plugins/admin';
@@ -47,6 +50,45 @@ export { siwe } from 'better-auth/plugins/siwe';
47
50
  export { twoFactor } from 'better-auth/plugins/two-factor';
48
51
  export { username } from 'better-auth/plugins/username';
49
52
  import "./packem_shared/create-auth.d-D1CKOFWc.js";
53
+ /** better-auth's `requireMcpAuth` options, with `resource` required. */
54
+ type LunoraRequireMcpAuthOptions = {
55
+ /**
56
+ * The protected-resource URL tokens must be bound to — the same value passed
57
+ * to `mcp({ resource })`.
58
+ */
59
+ resource: string;
60
+ } & RequireMcpAuthOptions;
61
+ /** The auth instance shape better-auth's `requireMcpAuth` accepts. */
62
+ type McpAuthInstance = Parameters<typeof requireMcpAuth$1>[0];
63
+ /** The protected handler: receives the request and the verified token claims. */
64
+ type McpProtectedHandler = Parameters<typeof requireMcpAuth$1>[1];
65
+ /**
66
+ * The MCP authorization server: better-auth's `oauthProvider` configured for MCP,
67
+ * serving the RFC 9728 protected-resource metadata for `resource`. Lunora's
68
+ * worker serves that document and the authorization-server metadata outside the
69
+ * auth base path on its own; that needs this `mcp`, not `@better-auth/mcp`'s.
70
+ */
71
+ declare const mcp: (options: McpOptions) => ReturnType<typeof mcp$1>;
72
+ /**
73
+ * Protect an MCP route on the same deployment as the authorization server.
74
+ * Verifies the bearer token's signature, issuer, audience and expiry, and hands
75
+ * the verified claims to `handler`.
76
+ * @throws LunoraError when `resource` is missing, empty or not an absolute URL.
77
+ */
78
+ declare const requireMcpAuth: (auth: McpAuthInstance, handler: McpProtectedHandler, options: LunoraRequireMcpAuthOptions) => ((request: Request) => Promise<Response>);
79
+ /**
80
+ * The two `.well-known` paths an MCP client fetches before it authorizes:
81
+ * the RFC 9728 protected-resource metadata for `resource` (where the 401
82
+ * challenge points), then the RFC 8414 authorization-server metadata for the
83
+ * issuer.
84
+ *
85
+ * The Lunora worker already serves both for an `.auth()` declaration with
86
+ * `mcp()`; this is for routing them yourself, e.g. in front of a hand-built
87
+ * worker. Pass `authBasePath` when `createAuth` was given a `basePath` other
88
+ * than `/api/auth`.
89
+ * @throws TypeError when `resource` is not an absolute URL.
90
+ */
91
+ declare const mcpDiscoveryPaths: (resource: string, authBasePath?: string) => ReadonlyArray<string>;
50
92
  /** Organization sub-features a UI branches on. */
51
93
  interface UiConfigOrganization {
52
94
  /** Whether an ordinary user may create one at all. */
@@ -124,7 +166,7 @@ interface UiConfigOptions {
124
166
  * consumes anyway.
125
167
  */
126
168
  declare const uiConfig: (options?: UiConfigOptions) => BetterAuthPlugin;
127
- export {
169
+ export { type LunoraRequireMcpAuthOptions,
128
170
  /**
129
171
  * Publish which plugins and social providers this deployment enabled, at
130
172
  * `GET {basePath}/ui-config`, so an auth UI configures itself instead of making
@@ -142,4 +184,4 @@ type UiConfigOrganization,
142
184
  * `GET {basePath}/ui-config`, so an auth UI configures itself instead of making
143
185
  * you restate the list client-side. Lunora's own, not a better-auth re-export.
144
186
  */
145
- type UiConfigPayload, uiConfig };
187
+ type UiConfigPayload, mcp, mcpDiscoveryPaths, requireMcpAuth, uiConfig };
package/dist/plugins.mjs CHANGED
@@ -1 +1 @@
1
- import{inviteOnly as e}from"./packem_shared/createSignUpInvitation-DHZL9fNl.mjs";import{uiConfig as m}from"./packem_shared/uiConfig-B8pN6pks.mjs";import{apiKey as f}from"@better-auth/api-key";import{createMcpProtectedRequestHandler as i,mcp as n,requireMcpAuth as a}from"@better-auth/mcp";import{oauthDeviceAuthorization as s,oauthProvider as u}from"@better-auth/oauth-provider";import{passkey as d}from"@better-auth/passkey";import{scim as A}from"@better-auth/scim";import{captcha as v,lastLoginMethod as y,oneTap as P}from"better-auth/plugins";import{createAccessControl as T}from"better-auth/plugins/access";import{admin as z}from"better-auth/plugins/admin";import{anonymous as O}from"better-auth/plugins/anonymous";import{bearer as q}from"better-auth/plugins/bearer";import{customSession as L}from"better-auth/plugins/custom-session";import{deviceAuthorization as j}from"better-auth/plugins/device-authorization";import{emailOTP as D}from"better-auth/plugins/email-otp";import{genericOAuth as H}from"better-auth/plugins/generic-oauth";import{haveIBeenPwned as K}from"better-auth/plugins/haveibeenpwned";import{jwt as R}from"better-auth/plugins/jwt";import{magicLink as G}from"better-auth/plugins/magic-link";import{multiSession as Q}from"better-auth/plugins/multi-session";import{oAuthProxy as V}from"better-auth/plugins/oauth-proxy";import{oneTimeToken as X}from"better-auth/plugins/one-time-token";import{organization as Z}from"better-auth/plugins/organization";import{phoneNumber as $}from"better-auth/plugins/phone-number";import{siwe as ro}from"better-auth/plugins/siwe";import{twoFactor as to}from"better-auth/plugins/two-factor";import{username as po}from"better-auth/plugins/username";export{z as admin,O as anonymous,f as apiKey,q as bearer,v as captcha,T as createAccessControl,i as createMcpProtectedRequestHandler,L as customSession,j as deviceAuthorization,D as emailOTP,H as genericOAuth,K as haveIBeenPwned,e as inviteOnly,R as jwt,y as lastLoginMethod,G as magicLink,n as mcp,Q as multiSession,V as oAuthProxy,s as oauthDeviceAuthorization,u as oauthProvider,P as oneTap,X as oneTimeToken,Z as organization,d as passkey,$ as phoneNumber,a as requireMcpAuth,A as scim,ro as siwe,to as twoFactor,m as uiConfig,po as username};
1
+ import{inviteOnly as e}from"./packem_shared/createSignUpInvitation-DHZL9fNl.mjs";import{mcp as m,mcpDiscoveryPaths as p,requireMcpAuth as f}from"./packem_shared/mcp-CgX1hTOv.mjs";import{uiConfig as x}from"./packem_shared/uiConfig-B8pN6pks.mjs";import{apiKey as a}from"@better-auth/api-key";import{cimd as s}from"@better-auth/cimd";import{createMcpProtectedRequestHandler as h}from"@better-auth/mcp";import{oauthDeviceAuthorization as l,oauthProvider as v}from"@better-auth/oauth-provider";import{passkey as A}from"@better-auth/passkey";import{scim as g}from"@better-auth/scim";import{createInsufficientScopeError as T}from"better-auth/oauth2";import{captcha as z,lastLoginMethod as M,oneTap as O}from"better-auth/plugins";import{createAccessControl as b}from"better-auth/plugins/access";import{admin as C}from"better-auth/plugins/admin";import{anonymous as I}from"better-auth/plugins/anonymous";import{bearer as j}from"better-auth/plugins/bearer";import{customSession as E}from"better-auth/plugins/custom-session";import{deviceAuthorization as H}from"better-auth/plugins/device-authorization";import{emailOTP as N}from"better-auth/plugins/email-otp";import{genericOAuth as G}from"better-auth/plugins/generic-oauth";import{haveIBeenPwned as Q}from"better-auth/plugins/haveibeenpwned";import{jwt as V}from"better-auth/plugins/jwt";import{magicLink as X}from"better-auth/plugins/magic-link";import{multiSession as Z}from"better-auth/plugins/multi-session";import{oAuthProxy as $}from"better-auth/plugins/oauth-proxy";import{oneTimeToken as ro}from"better-auth/plugins/one-time-token";import{organization as to}from"better-auth/plugins/organization";import{phoneNumber as po}from"better-auth/plugins/phone-number";import{siwe as io}from"better-auth/plugins/siwe";import{twoFactor as no}from"better-auth/plugins/two-factor";import{username as co}from"better-auth/plugins/username";export{C as admin,I as anonymous,a as apiKey,j as bearer,z as captcha,s as cimd,b as createAccessControl,T as createInsufficientScopeError,h as createMcpProtectedRequestHandler,E as customSession,H as deviceAuthorization,N as emailOTP,G as genericOAuth,Q as haveIBeenPwned,e as inviteOnly,V as jwt,M as lastLoginMethod,X as magicLink,m as mcp,p as mcpDiscoveryPaths,Z as multiSession,$ as oAuthProxy,l as oauthDeviceAuthorization,v as oauthProvider,O as oneTap,ro as oneTimeToken,to as organization,A as passkey,po as phoneNumber,f as requireMcpAuth,g as scim,io as siwe,no as twoFactor,x as uiConfig,co as username};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/auth",
3
- "version": "1.0.0-alpha.182",
3
+ "version": "1.0.0-alpha.184",
4
4
  "description": "Auth for Lunora — a thin better-auth wrapper: email/password, OAuth, plugins, D1-backed",
5
5
  "keywords": [
6
6
  "auth",
@@ -56,6 +56,14 @@
56
56
  "types": "./dist/plugins-enterprise-client.d.ts",
57
57
  "import": "./dist/plugins-enterprise-client.mjs"
58
58
  },
59
+ "./cimd/node": {
60
+ "types": "./dist/cimd-node.d.ts",
61
+ "import": "./dist/cimd-node.mjs"
62
+ },
63
+ "./cimd/workers": {
64
+ "types": "./dist/cimd-workers.d.ts",
65
+ "import": "./dist/cimd-workers.mjs"
66
+ },
59
67
  "./middleware": {
60
68
  "types": "./dist/middleware.d.ts",
61
69
  "import": "./dist/middleware.mjs"
@@ -99,12 +107,13 @@
99
107
  },
100
108
  "dependencies": {
101
109
  "@better-auth/api-key": "1.7.3",
110
+ "@better-auth/cimd": "1.7.3",
102
111
  "@better-auth/mcp": "1.7.3",
103
112
  "@better-auth/oauth-provider": "1.7.3",
104
113
  "@better-auth/passkey": "1.7.3",
105
114
  "@better-auth/scim": "1.7.3",
106
- "@lunora/errors": "1.0.0-alpha.48",
107
- "@lunora/values": "1.0.0-alpha.58",
115
+ "@lunora/errors": "1.0.0-alpha.49",
116
+ "@lunora/values": "1.0.0-alpha.59",
108
117
  "@visulima/disposable-email-domains": "1.1.1",
109
118
  "@visulima/email-verifier": "1.0.9",
110
119
  "@visulima/free-email-domains": "1.0.1",
@@ -1 +0,0 @@
1
- import{I as i,L as R,R as _,a as S}from"./auth-do-DoiVT_x7.mjs";import"./adapter-CG0Yv85f.mjs";import"../audit.mjs";import"./createAuth-8aUw8v8D.mjs";import"./authDoColumnAdditions-UwF3Rl41.mjs";import"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import"./legacyIssuerCleanupStatements-D9Kf8OD1.mjs";export{i as INTERNAL_SECRET_HEADER,R as LunoraAuthDO,_ as READ_AUDIT_PATH,S as RESOLVE_SESSION_PATH};
@@ -1 +0,0 @@
1
- import{getAuthTablesWithResolvedIndexes as x}from"@better-auth/core/db/internal";import{LunoraError as b,toErrorBody as re}from"@lunora/errors";import{d as P,b as ne}from"./adapter-CG0Yv85f.mjs";import{AUTH_AUDIT_TABLE as oe,ensureAuthAuditTable as se,createAuthAuditReader as ie}from"../audit.mjs";import{resolveAuthOptions as ae,createAuth as ce}from"./createAuth-8aUw8v8D.mjs";import{authDoSchemaStatements as ue,authDoColumnAdditions as fe}from"./authDoColumnAdditions-UwF3Rl41.mjs";import{handleAuthRequest as le}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import{q as c}from"./quote-identifier-CGiYFBvY.mjs";import{schemaDeclaresIssuer as me,legacyIssuerCleanupStatements as de,indexesReferencingIssuer as pe}from"./legacyIssuerCleanupStatements-D9Kf8OD1.mjs";const Ee=(t,e)=>{const r=Math.max(t.length,e.length);let n=t.length^e.length;for(let s=0;s<r;s+=1){const o=s<t.length?t.charCodeAt(s):0,i=s<e.length?e.charCodeAt(s):0;n|=o^i}return n===0},W=2166136261,V=16777619,F=(t,e=W)=>{let r=e;for(let n=0;n<t.length;n+=1)r^=t.charCodeAt(n),r=Math.imul(r,V);return(r>>>0).toString(16).padStart(8,"0")},he=t=>`${F(t,W)}${F(t,V)}`,$=t=>{let e="";for(let n=0;n<t.length;n+=32768)e+=String.fromCharCode(...t.subarray(n,n+32768));return btoa(e)},we=t=>{const e=atob(t),r=new Uint8Array(e.length);for(let n=0;n<e.length;n+=1)r[n]=e.codePointAt(n)??0;return r},d="$lunora.wire$",j=64,H=1024,U="__proto__",B={BigInt64Array,BigUint64Array,Float32Array,Float64Array,Int8Array,Int16Array,Int32Array,Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array},q={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},ye=t=>{if(t===null||typeof t!="object")return!1;const e=Object.getPrototypeOf(t);return e===null||e===Object.prototype},T=(t,e=0)=>{if(e>j)throw new RangeError(`wire-codec: value nesting exceeds the ${j}-level limit`);if(t===void 0)return[d,"undefined"];if(t===null)return null;const r=typeof t;if(r==="bigint")return[d,"bigint",t.toString()];if(r==="number"){const o=t;return Number.isNaN(o)?[d,"nan"]:o===1/0?[d,"inf"]:o===-1/0?[d,"-inf"]:o}if(r!=="object")return t;if(t instanceof Date)return[d,"date",T(t.getTime(),e+1)];if(t instanceof Error){const o=t,i={};for(const u of Object.keys(o)){if(o[u]===void 0)continue;const f=T(o[u],e+1);u===U?Object.defineProperty(i,u,{configurable:!0,enumerable:!0,value:f,writable:!0}):i[u]=f}const a=[d,"error",String(o.name),String(o.message),i];return o.cause!==void 0&&a.push(T(o.cause,e+1)),a}if(t instanceof URL)return[d,"url",t.href];if(t instanceof Map)return[d,"map",[...t.entries()].map(([o,i])=>[T(o,e+1),T(i,e+1)])];if(t instanceof Set)return[d,"set",[...t].map(o=>T(o,e+1))];if(t instanceof ArrayBuffer)return[d,"bytes",$(new Uint8Array(t)),"ArrayBuffer"];if(ArrayBuffer.isView(t)){const o=t,i=o.constructor.name,a=new Uint8Array(o.buffer,o.byteOffset,o.byteLength);return i==="Uint8Array"?[d,"bytes",$(a)]:[d,"bytes",$(a),i]}if(Array.isArray(t)){const o=t.map(i=>T(i,e+1));return o.length>0&&o[0]===d?[d,"arr",o]:o}if(!ye(t)){const o=t.constructor?.name??"value";throw new TypeError(`wire-codec: cannot encode a ${o} over the Lunora wire — only plain objects, arrays, and the supported built-ins (Date, Error, URL, Map, Set, ArrayBuffer/typed arrays, bigint) round-trip`)}const n=t,s={};for(const o of Object.keys(n)){const i=n[o];if(i===void 0)continue;const a=T(i,e+1);o===U?Object.defineProperty(s,o,{configurable:!0,enumerable:!0,value:a,writable:!0}):s[o]=a}return s},E=(t,e=0)=>{if(e>j)throw new RangeError(`wire-codec: value nesting exceeds the ${j}-level limit`);if(t===null||typeof t!="object")return t;if(Array.isArray(t)){if(t[0]===d)switch(t[1]){case"-inf":return-1/0;case"arr":return t[2].map(o=>E(o,e+1));case"bigint":{const o=t[2];if(typeof o!="string"||o.length>H||!/^-?\d+$/.test(o))throw new RangeError(`wire-codec: invalid or over-long bigint (max ${H} digits)`);return BigInt(o)}case"date":{const o=E(t[2],e+1);if(typeof o!="number")throw new TypeError("wire-codec: malformed date — epoch must be a number");return new Date(o)}case"map":{const o=t[2];return new Map(o.map(i=>{if(!Array.isArray(i)||i.length!==2)throw new TypeError("wire-codec: malformed map entry — expected a [key, value] pair");return[E(i[0],e+1),E(i[1],e+1)]}))}case"set":return new Set(t[2].map(o=>E(o,e+1)));case"url":{const o=t[2];if(typeof o!="string")throw new TypeError("wire-codec: malformed url — href must be a string");return new URL(o)}case"error":{const o=t[2],i=t[3];if(typeof o!="string"||typeof i!="string")throw new TypeError("wire-codec: malformed error — name and message must be strings");const a=(Object.hasOwn(q,o)?q[o]:void 0)??Error,u=new a(i);u.name!==o&&Object.defineProperty(u,"name",{configurable:!0,value:o,writable:!0});const f=E(t[4],e+1);if(f===null||typeof f!="object"||Array.isArray(f))throw new TypeError("wire-codec: malformed error — props must be an object");for(const m of Object.keys(f))m===U?Object.defineProperty(u,m,{configurable:!0,enumerable:!0,value:f[m],writable:!0}):u[m]=f[m];return t.length>5&&Object.defineProperty(u,"cause",{configurable:!0,value:E(t[5],e+1),writable:!0}),u}case"bytes":{const o=t[2];if(typeof o!="string")throw new TypeError("wire-codec: malformed bytes — payload must be a base64 string");const i=we(o);if($(i)!==o)throw new TypeError("wire-codec: malformed bytes — payload must be canonical padded base64");const a=t[3]??"Uint8Array";if(a==="ArrayBuffer")return i.buffer.byteLength===i.byteLength?i.buffer:i.slice().buffer;const u=Object.hasOwn(B,a)?B[a]:void 0;return u?new u(i.slice().buffer):i}case"inf":return 1/0;case"nan":return Number.NaN;case"undefined":return;default:return t.map(o=>E(o,e+1))}return t.map(s=>E(s,e+1))}const r=t,n={};for(const s of Object.keys(r)){const o=E(r[s],e+1);s===U?Object.defineProperty(n,s,{configurable:!0,enumerable:!0,value:o,writable:!0}):n[s]=o}return n},Te="/__lunora/auth/move",g="__lunora_auth_move__",S="__lunora_auth_move_rows__",D="__lunora_auth_purged__",G=100,ge=16,Ae=100,be=2n**64n,X=t=>t.startsWith("sqlite_")||t.startsWith("_cf_")||t===g||t===S||t===D,Y=async(t,e)=>t.transaction(async()=>Promise.resolve().then(e)),h=(t,e,...r)=>[...t.sql.exec(e,...r)],l=(t,e,...r)=>{[...t.sql.exec(e,...r)]},N=t=>h(t,"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name").map(e=>String(e.name)).filter(e=>!X(e)),J=(t,e)=>h(t,"SELECT name, pk FROM pragma_table_info(?)",e).map(r=>({name:String(r.name),pk:Number(r.pk)})),L=(t,e)=>J(t,e).map(({name:r})=>r),K=(t,e)=>{const r=J(t,e),n=r.filter(({pk:s})=>s>0).toSorted((s,o)=>s.pk-o.pk).map(({name:s})=>s);return n.length>0?n:r.map(({name:s})=>s)},Q=(t,e)=>Number(h(t,`SELECT count(*) AS n FROM ${c(e)}`)[0]?.n??0),z=t=>JSON.stringify(T(t)),C=(t,e)=>BigInt(`0x${he(z(e.map(r=>t[r]??null)))}`),Z=(t,e)=>{let r=0,n=0n;for(const s of t)r+=1,n=(n+e(s))%be;return`${String(r)}:${n.toString(16)}`},ee=(t,e)=>{const r=L(t,e).toSorted((n,s)=>n.localeCompare(s));return Z(t.sql.exec(`SELECT * FROM ${c(e)}`),n=>C(n,r))},Se=t=>N(t).map(e=>{const r=h(t,"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?",e)[0]?.sql,n=h(t,"SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ? AND sql IS NOT NULL",e).map(s=>({name:String(s.name),sql:String(s.sql)}));return{columns:L(t,e),indexes:n,name:e,rows:Q(t,e),sql:typeof r=="string"?r:""}}),v=(t,e)=>{if(typeof e!="string"||!N(t).includes(e))throw new TypeError(`unknown table ${JSON.stringify(e)}`);return e},Re=(t,e,r)=>{const n=h(t,`SELECT rowid AS "__lunora_rowid__", * FROM ${c(e)} WHERE rowid > ? ORDER BY rowid LIMIT ?`,r,G),s=n.at(-1)?.__lunora_rowid__;return{last:typeof s=="number"?s:void 0,rows:n.map(({__lunora_rowid__:o,...i})=>i)}},Oe=t=>L(t,g).length>0,_=t=>Oe(t)?Object.fromEntries(h(t,`SELECT tbl, after, done, pass, cols, watermark FROM ${c(g)}`).map(e=>[String(e.tbl),{after:Number(e.after),columns:JSON.parse(String(e.cols)),done:e.done===1,pass:Number(e.pass),watermark:Number(e.watermark)}])):{},Ne=(t,e,r)=>Z(t.sql.exec(`SELECT h FROM ${c(S)} WHERE tbl = ? AND pass = ?`,e,r),n=>BigInt(`0x${String(n.h)}`)),_e=t=>{l(t,`CREATE TABLE IF NOT EXISTS ${c(g)} (tbl TEXT NOT NULL PRIMARY KEY, after INTEGER NOT NULL, done INTEGER NOT NULL, pass INTEGER NOT NULL, cols TEXT NOT NULL, watermark INTEGER NOT NULL DEFAULT 0)`),l(t,`CREATE TABLE IF NOT EXISTS ${c(S)} (tbl TEXT NOT NULL, k TEXT NOT NULL, h TEXT NOT NULL, pass INTEGER NOT NULL, PRIMARY KEY (tbl, k))`)},Le=(t,e,r)=>{if(!N(t).includes(e))return 0;const n=K(t,e).map(s=>`u.${c(s)}`).join(", ");return Number(h(t,`SELECT count(*) AS n FROM ${c(e)} u WHERE u.rowid > ? AND NOT EXISTS (SELECT 1 FROM ${c(S)} c WHERE c.tbl = ? AND c.k = json_array(${n}))`,r,e)[0]?.n??0)},Ie=/^CREATE TABLE /iu,$e=/^CREATE (?:UNIQUE )?INDEX /iu,je=(t,e,r)=>{if(X(e.name))throw new TypeError(`refusing reserved table ${JSON.stringify(e.name)}`);if(!Ie.test(e.sql)||e.indexes.some(({sql:s})=>!$e.test(s)))throw new TypeError(`refusing unexpected DDL for ${JSON.stringify(e.name)}`);if(r.has(e.name)){const s=new Set(L(t,e.name));for(const o of e.columns.filter(i=>!s.has(i)))l(t,`ALTER TABLE ${c(e.name)} ADD COLUMN ${c(o)}`)}else l(t,e.sql);const n=new Set(h(t,"SELECT name FROM sqlite_master WHERE type = 'index'").map(s=>String(s.name)));for(const s of e.indexes.filter(({name:o})=>!n.has(o)))l(t,s.sql)},Ue=(t,e,r)=>{_e(t);const n=Le(t,r.userTable,_(t)[r.userTable]?.watermark??0);if(n>0&&!r.force)throw new b("AUTH_MOVE_TARGET_NOT_EMPTY",`the pinned auth object has ${String(n)} user(s) the copy did not write; copying into it would merge two user bases — pass force to copy anyway`,{data:{users:n}});const s=new Set(N(t)),o=_(t);for(const a of e){je(t,a,s);const u=o[a.name],f=JSON.stringify(a.columns.toSorted((m,w)=>m.localeCompare(w)));u===void 0?l(t,`INSERT INTO ${c(g)} (tbl, after, done, pass, cols) VALUES (?, 0, 0, 1, ?)`,a.name,f):l(t,`UPDATE ${c(g)} SET cols = ? WHERE tbl = ?`,f,a.name)}l(t,`UPDATE ${c(g)} SET watermark = (SELECT coalesce(max(rowid), 0) FROM ${c(r.userTable)}) WHERE tbl = ?`,r.userTable);const i=_(t);return{cursors:Object.fromEntries(e.map(({name:a})=>[a,i[a]?.after??0])),done:Object.fromEntries(e.map(({name:a})=>[a,i[a]?.done??!1])),targetRows:Object.fromEntries(e.map(({name:a})=>[a,Q(t,a)]))}},te=(t,e,r,n,s)=>h(t,`SELECT ${s.map(o=>c(o)).join(", ")} FROM ${c(e)} WHERE ${r.map(o=>`${c(o)} IS ?`).join(" AND ")} LIMIT 1`,...n)[0],k=(t,e)=>{if(!e)throw new b("AUTH_MOVE_CONFLICT",`a row copied into ${t} collides with a row the pinned auth object holds on its own — nothing from this page was written; pass force to keep the pinned object's rows`,{data:{table:t}});return"conflicts"},De=t=>t instanceof Error&&t.message.includes("UNIQUE constraint failed"),ke=(t,e)=>{if(e.prior!==void 0)return k(e.table,e.force);try{l(t,`INSERT INTO ${c(e.table)} (${e.columns.map(r=>c(r)).join(", ")}) VALUES (${e.columns.map(()=>"?").join(", ")})`,...e.columns.map(r=>e.row[r]))}catch(r){if(!De(r))throw r;return k(e.table,e.force)}return"copied"},Ce=(t,e,r)=>{const n=C(r,e.columns).toString(16);return n===e.h?"unchanged":e.prior===void 0||n!==e.prior?k(e.table,e.force):(l(t,`UPDATE ${c(e.table)} SET ${e.columns.map(s=>`${c(s)} = ?`).join(", ")} WHERE ${e.keys.map(s=>`${c(s)} IS ?`).join(" AND ")}`,...e.columns.map(s=>e.row[s]),...e.key),"updated")},Me=(t,e,r,n)=>{const{force:s,keys:o,pass:i}=n,a=Object.keys(r).toSorted((A,I)=>A.localeCompare(I));if(a.length+o.length>Ae)throw new TypeError(`${e} has too many columns to copy`);const u=o.map(A=>r[A]??null),f=z(u),m=C(r,a).toString(16),w=h(t,`SELECT h FROM ${c(S)} WHERE tbl = ? AND k = ?`,e,f)[0]?.h,p={columns:a,force:s,h:m,key:u,keys:o,prior:typeof w=="string"?w:void 0,row:r,table:e},y=p.prior===m?void 0:te(t,e,o,u,a);let R="unchanged";return p.prior!==m&&(R=y===void 0?ke(t,p):Ce(t,p,y)),l(t,`INSERT INTO ${c(S)} (tbl, k, h, pass) VALUES (?, ?, ?, ?) ON CONFLICT (tbl, k) DO UPDATE SET h = excluded.h, pass = excluded.pass`,e,f,m,i),R},xe=(t,e,r)=>{const{counts:n,force:s,keys:o,marker:i}=r,a=h(t,`SELECT k, h FROM ${c(S)} WHERE tbl = ? AND pass < ?`,e,i.pass);for(const u of a){const f=E(JSON.parse(String(u.k))),m=te(t,e,o,f,i.columns);m!==void 0&&(C(m,i.columns).toString(16)===String(u.h)?(l(t,`DELETE FROM ${c(e)} WHERE ${o.map(w=>`${c(w)} IS ?`).join(" AND ")}`,...f),n.deleted+=1):n[k(e,s)]+=1),l(t,`DELETE FROM ${c(S)} WHERE tbl = ? AND k = ?`,e,String(u.k))}},Pe=async(t,e,r)=>{const n=_(t)[e];if(n===void 0)throw new TypeError(`table ${JSON.stringify(e)} was not part of this move`);return Y(t,()=>{const s={conflicts:0,copied:0,deleted:0,unchanged:0,updated:0},o=K(t,e);for(const i of r.rows)s[Me(t,e,i,{force:r.force,keys:o,pass:n.pass})]+=1;return r.final&&xe(t,e,{counts:s,force:r.force,keys:o,marker:n}),l(t,`UPDATE ${c(g)} SET after = ?, done = ? WHERE tbl = ?`,r.last,r.final?1:0,e),s})},Fe=async(t,e)=>{const r=N(t);return await Y(t,()=>{const n=r.filter(o=>e[o]===void 0);if(n.length>0)throw new b("AUTH_MOVE_INCOMPLETE",`refusing to purge the un-pinned auth object: ${n.join(", ")} not fully copied — run the copy to done: true first`,{data:{unfinished:n}});const s=r.filter(o=>ee(t,o)!==e[o]);if(s.length>0)throw new b("AUTH_MOVE_SOURCE_CHANGED",`refusing to purge the un-pinned auth object: ${s.join(", ")} changed since it was copied — run the copy again to carry the changes, then purge`,{data:{changed:s}});for(const o of r)l(t,`DROP TABLE ${c(o)}`);l(t,`CREATE TABLE IF NOT EXISTS ${c(D)} (at INTEGER NOT NULL)`),l(t,`INSERT INTO ${c(D)} (at) VALUES (?)`,Date.now())}),{dropped:r}},He=t=>{if(L(t,D).length>0)throw new b("AUTH_MOVE_SOURCE_PURGED","the un-pinned auth object was purged after its tables were copied; anything in it now is new, and copying it would delete the pinned object's users")},Be=new Set(["fingerprints","manifest","page","purge"]),qe=(t,e,r)=>{e.prepare();const n=_(t);return{copied:r?Object.fromEntries(Object.entries(n).map(([s,o])=>[s,Ne(t,s,o.pass)])):{},markers:n,order:e.order()}},ve=(t,e)=>{for(const r of e)l(t,`UPDATE ${c(g)} SET after = 0, done = 0, pass = pass + 1 WHERE tbl = ?`,r);return{}},We=[/(?:UNIQUE|NOT NULL|CHECK|FOREIGN KEY|PRIMARY KEY) constraint failed/u,/no such (?:table|column)/u,/datatype mismatch/u,/SQLITE_[A-Z_]+/u],Ve=t=>{const e=t instanceof Error?t.message:String(t);for(const r of We){const n=r.exec(e);if(n)return n[0]}return t instanceof Error?t.name:"unknown"},Ge=async(t,e,r)=>{switch(Be.has(String(e.op))&&He(t),e.op){case"begin":return r.prepare(),Ue(t,e.tables,{force:e.force===!0,userTable:r.userTable()});case"fingerprints":return{fingerprints:Object.fromEntries(N(t).map(n=>[n,ee(t,n)]))};case"manifest":return{tables:Se(t)};case"page":{const n=Re(t,v(t,e.table),Number(e.after??0));return{last:n.last,rows:T(n.rows)}}case"purge":{const n=await Fe(t,e.expected??{});return r.onPurge(),n}case"rescan":return ve(t,e.tables??[]);case"status":return qe(t,r,e.fingerprints===!0);case"write":return Pe(t,v(t,e.table),{final:e.final===!0,force:e.force===!0,last:Number(e.last),rows:E(e.rows)});default:throw new TypeError(`unknown move op ${JSON.stringify(e.op)}`)}},Xe=async(t,e,r)=>{try{return await Ge(t,e,r)}catch(n){if(n instanceof b)throw n;console.error("@lunora/auth: auth move step failed",n);const s=String(e.op),o=typeof e.table=="string"?e.table:void 0,i=Ve(n);throw new b("AUTH_MOVE_FAILED",`auth move ${s}${o===void 0?"":` on ${o}`} failed: ${i}`,{data:{cause:i,op:s,...o===void 0?{}:{table:o}}})}},Ye=(t,e)=>{const r=n=>{const s=e.first.indexOf(n);if(s!==-1)return s;const o=e.last.indexOf(n);return o===-1?e.first.length:e.first.length+1+o};return t.toSorted((n,s)=>r(n.name)-r(s.name)||n.name.localeCompare(s.name))},Je=(t,e)=>({conflicts:0,copied:0,deleted:0,sourceRows:t.rows,table:t.name,targetRows:e,unchanged:0,updated:0}),ut=t=>{const e=async(r,n)=>{const s=await t(r,n);if(!s.ok){let o={};try{const i=await s.json();typeof i.error=="object"&&i.error!==null&&(o=i.error)}catch{}throw new b(o.code??"AUTH_MOVE_FAILED",o.message??`auth move ${String(n.op)} on the ${r} object failed (${String(s.status)})`,{data:o.data,status:s.status})}return s.json()};return{copy:async(r={})=>{const n=r.force===!0,s=await e("target",{op:"status"}),{tables:o}=await e("source",{op:"manifest"}),i=await e("target",{force:n,op:"begin",tables:o}),a=[];let u=0;for(const p of Ye(o,s.order)){const y=Je(p,i.targetRows[p.name]??0);let R=i.cursors[p.name]??0;for(a.push(y);;){if(u>=ge)return{done:!1,tables:a};u+=1;const A=await e("source",{after:R,op:"page",table:p.name}),I=E(A.rows),M=I.length<G;if(I.length===0&&i.done[p.name]===!0)break;const O=await e("target",{final:M,force:n,last:A.last??R,op:"write",rows:A.rows,table:p.name});if(y.conflicts+=O.conflicts,y.copied+=O.copied,y.deleted+=O.deleted,y.unchanged+=O.unchanged,y.updated+=O.updated,y.targetRows+=O.copied-O.deleted,R=A.last??R,M)break}}const{fingerprints:f}=await e("source",{op:"fingerprints"}),m=await e("target",{fingerprints:!0,op:"status"}),w=Object.keys(f).filter(p=>m.copied[p]!==f[p]);return w.length>0&&await e("target",{op:"rescan",tables:w}),{done:w.length===0,tables:a}},purge:async()=>{const{copied:r,markers:n}=await e("target",{fingerprints:!0,op:"status"}),s=Object.fromEntries(Object.entries(n).filter(([,o])=>o.done).map(([o])=>[o,r[o]]));return e("source",{expected:s,op:"purge"})}}},Ke="/__lunora/auth/session",Qe="/__lunora/auth/audit",ze="x-lunora-auth-do-secret",Ze=t=>{if(t===null||typeof t!="object"||Array.isArray(t))return{error:"body must be a JSON object"};const e={};for(const[r,n]of Object.entries(t))if(r==="limit"||r==="sinceSeq"){if(typeof n!="number"||!Number.isFinite(n))return{error:`"${r}" must be a finite number`};e[r]=n}else if(r==="actorId"||r==="event"){if(typeof n!="string")return{error:`"${r}" must be a string`};e[r]=n}else return{error:`unknown audit read option "${r}"`};return{options:e}};class ft{#r;#a;#c;#e;#t;#n=!1;#o={first:["user"],last:[]};constructor(e,r,n={}){this.#e=e.storage,this.#r=P(e.storage),this.#c=r,this.#a=n}#s(){if(this.#t!==void 0)return this.#t;const e=this.#c(),r=ae(e),{tables:n}=x(r);if(this.#o={first:[n.user?.modelName??"user",n.account?.modelName??"account",n.session?.modelName??"session"],last:[oe,n.rateLimit?.modelName??"rateLimit"]},!this.#n){for(const s of ue(r))[...this.#e.sql.exec(s)];for(const s of fe(r,o=>this.#u(o)))[...this.#e.sql.exec(s)];this.#f(r),this.#n=!0}return this.#t=ce({...e,database:ne(this.#e)}),this.#t}#u(e){return[...this.#e.sql.exec("SELECT name FROM pragma_table_info(?)",e)].map(n=>String(n.name))}#f(e){const{tables:r}=x(e),{account:n}=r;if(n===void 0)return;const s=Object.entries(n.fields).map(([o,i])=>i.fieldName??o);if(!(me(s)||!this.#u(n.modelName).includes("issuer")))try{const o=[...this.#e.sql.exec("SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ?",n.modelName)].map(i=>({name:String(i.name),sql:typeof i.sql=="string"?i.sql:void 0}));for(const i of de(n.modelName,pe(o)))[...this.#e.sql.exec(i)]}catch(o){console.error("@lunora/auth: could not drop the reverted `account.issuer` column; if it is still present, sign-ups will fail until it is removed.",o)}}async#l(e){if(!this.#i(e))return Response.json({error:"unauthorized"},{status:401});let r;try{r=await e.json()}catch{return Response.json({error:"invalid body"},{status:400})}const n=Ze(r??{});if("error"in n)return Response.json({error:n.error},{status:400});await se(this.#r);const s=await ie(this.#r).read(n.options);return Response.json({entries:s})}async#m(e){if(e.method!=="POST"||!this.#i(e))return Response.json({error:"unauthorized"},{status:401});try{const r=await e.json(),n=await Xe(this.#e,r,{onPurge:()=>{this.#n=!1,this.#t=void 0,this.#r=P(this.#e)},order:()=>this.#o,prepare:()=>{this.#s()},userTable:()=>this.#o.first[0]??"user"});return Response.json(n)}catch(r){const{body:n,redacted:s,status:o}=re(r,{encodeData:i=>i,fallbackCode:"AUTH_MOVE_FAILED",redactedMessage:"auth move failed"});return s&&console.error("@lunora/auth: auth move failed",r),Response.json({error:n},{status:o})}}#i(e){const{internalSecret:r}=this.#a;if(r===void 0||r==="")return!1;const n=e.headers.get(ze);return n!==null&&Ee(n,r)}async#d(e){if(!this.#i(e))return Response.json({error:"unauthorized"},{status:401});const n=await this.#s().api.getSession({headers:e.headers}),s=n?.user.id;if(s===void 0)return Response.json({});const o=n?.session.expiresAt,i=n?.user,a=i?.role;return Response.json({...typeof i?.email=="string"&&i.email.length>0?{email:i.email}:{},...o instanceof Date?{expiresAtMs:o.getTime()}:{},...typeof i?.name=="string"&&i.name.length>0?{name:i.name}:{},...typeof a=="string"&&a.length>0?{role:a}:{},userId:s})}async fetch(e){const r=new URL(e.url);if(r.pathname===Ke)return this.#d(e);if(r.pathname===Qe)return this.#l(e);if(r.pathname===Te)return this.#m(e);const n=this.#s();return await le(n,e)??Response.json({error:"not an auth route"},{status:404})}}export{ze as I,ft as L,Te as M,Qe as R,Ke as a,ut as c};
@@ -1 +0,0 @@
1
- import{LunoraError as c}from"@lunora/errors";import{betterAuth as l}from"better-auth";import{getIP as u}from"better-auth/api";import{o as f}from"./on-cloudflare-edge-Dx6gFjBO.mjs";import{validateSessionPolicy as h}from"./sessionPresets-C867Mlo4.mjs";const n="cf-connecting-ip",m=e=>{const t=e!==void 0&&e.length>0;return f()?t?[n,"x-forwarded-for"]:[n]:t?["x-forwarded-for"]:[]},a="/**",p=100,A=e=>{const t=e.rateLimit?.customRules??{};return t[a]!==void 0?t:{...t,[a]:(o,r)=>u(o,e)===null?{max:r.max*p,window:r.window}:r}},d=32,E=e=>{const t=typeof e=="string"?e.trim().length:0;return t>0&&t<d},i=e=>typeof e=="string"?e.toLowerCase().startsWith("http://"):e&&typeof e=="object"?e.protocol==="http"?!0:e.protocol==="https"?!1:typeof e.fallback=="string"&&e.fallback.toLowerCase().startsWith("http://"):!1,L=new Set(["127.0.0.1","localhost"]),g=e=>{if(typeof e!="string")return;let t;try{t=new URL(e)}catch{return}if(!(t.protocol!=="http:"||!L.has(t.hostname)))return{allowedHosts:[t.hostname,`${t.hostname}:*`],fallback:e,protocol:"http"}},w=e=>{const t=g(e.baseURL)??e.baseURL;if(E(e.secret)){const s=`@lunora/auth: AUTH_SECRET is only ${String(e.secret?.trim().length)} characters. Use at least ${String(d)} for a brute-force-resistant secret — generate one with \`openssl rand -hex 32\`.`;if(!i(t))throw new c("INTERNAL",s);console.warn(s)}const o=e.advanced??{},r=o.ipAddress??{};return{...e,advanced:{...o,defaultCookieAttributes:o.defaultCookieAttributes??{httpOnly:!0,path:"/",sameSite:"lax"},ipAddress:{...r,...r.ipAddressHeaders===void 0?{ipAddressHeaders:m(r.trustedProxies)}:{}},...o.useSecureCookies===void 0?{useSecureCookies:!i(t)}:{}},baseURL:t}},C=e=>{const t=w(e),o=t.rateLimit?.enabled===void 0,r=t.rateLimit?.storage===void 0&&t.rateLimit?.enabled!==!1,s=t.rateLimit?.enabled===!1?{}:{rateLimit:{...t.rateLimit,...o?{enabled:!0}:{},...r?{storage:"database"}:{},customRules:A(t)}};return{...t,...s,...t.session?.cookieCache===void 0?{session:{...t.session,cookieCache:{enabled:!0,maxAge:60}}}:{}}},x=e=>{if(!e.secret||e.secret.trim()==="")throw new c("INTERNAL",'@lunora/auth: `secret` is required. Set AUTH_SECRET locally in .dev.vars (`lunora env set AUTH_SECRET "$(openssl rand -hex 32)"`), and in production with `wrangler secret put AUTH_SECRET`.');return e.session&&h(e.session),l(C(e))};export{x as createAuth,C as resolveAuthOptions};
@@ -1 +0,0 @@
1
- import{LunoraError as y}from"@lunora/errors";import{I as d,a as A,c as w,R as E,M as R}from"./auth-do-DoiVT_x7.mjs";import{isAuthRoutePath as v,DEFAULT_AUTH_BASE_PATH as g}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";const _=u=>{const{internalSecret:i,jurisdiction:a,objectName:l="auth"}=u,p=u.namespace;let{namespace:r}=u;if(r&&a!==void 0){if(typeof r.jurisdiction!="function")throw new TypeError(`@lunora/auth: Durable Object namespace does not support jurisdiction("${a}") — update @cloudflare/workers-types or remove the jurisdiction option`);r=r.jurisdiction(a)}const c=()=>{if(r)return r.get(r.idFromName(l))},m=async(t,n,o)=>{if(!i)return;const s=c();if(!s)return;const e=await s.fetch(new Request(new URL(t,n),{body:JSON.stringify(o),headers:{"content-type":"application/json",[d]:i},method:"POST"}));return e.ok?e:void 0},h=r,f=p&&h&&a!==void 0?w(async(t,n)=>{if(!i)throw new y("AUTH_MOVE_NOT_CONFIGURED","copying the auth object needs its internal secret; set the auth object's `internalSecret`");const o=t==="source"?p:h;return o.get(o.idFromName(l)).fetch(new Request(new URL(R,"https://auth-do.invalid"),{body:JSON.stringify(n),headers:{"content-type":"application/json",[d]:i},method:"POST"}))}):void 0;return{...f===void 0?{}:{jurisdictionMove:f},auditReader:{read:async t=>{const n=await m(E,"https://auth-do.invalid",t);return n?(await n.json())?.entries??[]:[]}},authHandler:async t=>{if(v(new URL(t.url).pathname,g))return c()?.fetch(t)},resolveIdentity:async t=>{if(!i)return null;const n=c();if(!n)return null;const o=new Headers(t.headers);o.set(d,i);const s=await n.fetch(new Request(new URL(A,t.url),{headers:o}));if(!s.ok)return null;const e=await s.json();return e?.userId?{...typeof e.email=="string"&&e.email.length>0?{email:e.email}:{},...typeof e.expiresAtMs=="number"&&Number.isFinite(e.expiresAtMs)?{expiresAtMs:e.expiresAtMs}:{},...typeof e.name=="string"&&e.name.length>0?{name:e.name}:{},...typeof e.role=="string"&&e.role.length>0?{role:e.role}:{},userId:e.userId}:null}}};export{_ as createDoAuthWiring};