@lunora/auth 1.0.0-alpha.4 → 1.0.0-alpha.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +6 -0
- package/README.md +55 -0
- package/dist/adapter.d.mts +35 -35
- package/dist/adapter.d.ts +35 -35
- package/dist/adapter.mjs +1 -47
- package/dist/audit.d.mts +114 -0
- package/dist/audit.d.ts +114 -0
- package/dist/audit.mjs +11 -0
- package/dist/email-guard.d.mts +122 -0
- package/dist/email-guard.d.ts +122 -0
- package/dist/email-guard.mjs +1 -0
- package/dist/index.d.mts +460 -146
- package/dist/index.d.ts +460 -146
- package/dist/index.mjs +1 -12
- package/dist/middleware.d.mts +156 -155
- package/dist/middleware.d.ts +156 -155
- package/dist/middleware.mjs +1 -53
- package/dist/packem_shared/DEFAULT_AUTH_BASE_PATH-kIwlEt8i.mjs +1 -0
- package/dist/packem_shared/LunoraAuthAdminError-BiNYZM9j.mjs +1 -0
- package/dist/packem_shared/authAuditHook-Dx3sqf3G.mjs +1 -0
- package/dist/packem_shared/compileMigrationsSql-ChiudSmt.mjs +1 -0
- package/dist/packem_shared/create-auth.d-De6IOirt.d.mts +128 -0
- package/dist/packem_shared/create-auth.d-De6IOirt.d.ts +128 -0
- package/dist/packem_shared/createAuth-DS6PL8Mb.mjs +1 -0
- package/dist/packem_shared/emailGateDatabaseHooks-DzBD1Qoq.mjs +1 -0
- package/dist/packem_shared/sessionPresets-DpEFjXKV.mjs +1 -0
- package/dist/plugins-client.mjs +1 -2
- package/dist/plugins.mjs +1 -22
- package/dist/schema.d.mts +39 -39
- package/dist/schema.d.ts +39 -39
- package/dist/schema.mjs +1 -62
- package/dist/sql-store.d.mts +28 -28
- package/dist/sql-store.d.ts +28 -28
- package/dist/sql-store.mjs +1 -162
- package/dist/store.d.mts +49 -31
- package/dist/store.d.ts +49 -31
- package/dist/store.mjs +1 -170
- package/dist/turnstile-middleware.d.mts +55 -55
- package/dist/turnstile-middleware.d.ts +55 -55
- package/dist/turnstile-middleware.mjs +1 -45
- package/dist/turnstile.d.mts +42 -59
- package/dist/turnstile.d.ts +42 -59
- package/dist/turnstile.mjs +1 -61
- package/package.json +19 -6
- package/dist/packem_shared/DEFAULT_AUTH_BASE_PATH-DjcUWEQl.mjs +0 -11
- package/dist/packem_shared/LunoraAuthAdminError-BxrfEeA_.mjs +0 -249
- package/dist/packem_shared/compileMigrationsSql-wZH3oXDu.mjs +0 -28
- package/dist/packem_shared/create-auth.d-M36jwG_Y.d.mts +0 -58
- package/dist/packem_shared/create-auth.d-M36jwG_Y.d.ts +0 -58
- package/dist/packem_shared/createAuth-B-tvsvQU.mjs +0 -56
- package/dist/packem_shared/sessionPresets-B95rXrd8.mjs +0 -35
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { betterAuth, BetterAuthOptions } from 'better-auth';
|
|
2
|
+
/**
|
|
3
|
+
* Lunora's options pass straight through to better-auth — the only thing we add
|
|
4
|
+
* is requiring `secret` up front so a misconfigured deployment fails loudly
|
|
5
|
+
* instead of at the first sign-in.
|
|
6
|
+
*
|
|
7
|
+
* For `database`, prefer `lunoraD1Adapter` (`database: lunoraD1Adapter(env.DB)`)
|
|
8
|
+
* over passing the raw `env.DB`. better-auth *does* accept a D1Database directly,
|
|
9
|
+
* but it then resolves its Kysely adapter via a runtime `await import(...)` inside
|
|
10
|
+
* `auth.$context` — and that import never settles under `@cloudflare/vite-plugin`'s
|
|
11
|
+
* worker runner, hanging every auth request in `pnpm dev`. The explicit adapter
|
|
12
|
+
* skips it, so dev and prod behave the same. (Raw `env.DB` is still correct for
|
|
13
|
+
* the migration-only instance — see `lunoraD1Adapter`'s note.)
|
|
14
|
+
*
|
|
15
|
+
* Session rotation / richer session policies are configured via the `session`
|
|
16
|
+
* field (a `SessionPolicy`); Lunora validates it for obviously-broken
|
|
17
|
+
* durations and forwards it verbatim to better-auth. See `sessionPresets`
|
|
18
|
+
* for ready-made rotation/expiry trade-offs.
|
|
19
|
+
*
|
|
20
|
+
* ## Serverless background tasks (Cloudflare Workers)
|
|
21
|
+
*
|
|
22
|
+
* better-auth runs some work *after* sending the response — most importantly the
|
|
23
|
+
* password-reset email, whose background send is what keeps reset responses
|
|
24
|
+
* constant-time (a timing-attack defence: the response doesn't reveal whether
|
|
25
|
+
* the account exists). On Cloudflare Workers a promise that isn't handed to
|
|
26
|
+
* `ctx.waitUntil` can be cancelled the moment the response returns, dropping
|
|
27
|
+
* that send and weakening the guarantee. Wire your request's `ctx.waitUntil`
|
|
28
|
+
* into better-auth's background handler so the work survives:
|
|
29
|
+
*
|
|
30
|
+
* ```ts
|
|
31
|
+
* // in your worker fetch handler, where `ctx: ExecutionContext` is in scope
|
|
32
|
+
* const auth = createAuth({
|
|
33
|
+
* secret: env.AUTH_SECRET,
|
|
34
|
+
* database: lunoraD1Adapter(env.DB),
|
|
35
|
+
* advanced: {
|
|
36
|
+
* backgroundTasks: { handler: (promise) => ctx.waitUntil(promise) },
|
|
37
|
+
* },
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* (Lunora can't set this for you — `ctx.waitUntil` is per-request, but
|
|
42
|
+
* `createAuth` runs once at worker setup.)
|
|
43
|
+
*/
|
|
44
|
+
type LunoraAuthOptions = BetterAuthOptions;
|
|
45
|
+
/**
|
|
46
|
+
* The full better-auth instance: `auth.handler` accepts a `Request` and
|
|
47
|
+
* returns a `Response` (used by `handleAuthRequest`); `auth.api`
|
|
48
|
+
* exposes the typed endpoint surface for server-side calls (e.g.
|
|
49
|
+
* `auth.api.getSession({ headers })` inside a query/mutation).
|
|
50
|
+
*/
|
|
51
|
+
type LunoraAuth = ReturnType<typeof betterAuth>;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the caller's options into the exact shape `createAuth` hands to
|
|
54
|
+
* `betterAuth` — the hardened, default-filled options the running worker uses.
|
|
55
|
+
* Exported (and pure) so the migration path can compile the schema from the
|
|
56
|
+
* same resolved options: `compileMigrationsSql` routes through here, so the
|
|
57
|
+
* `rateLimit` table the worker's durable limiter writes to is included in the
|
|
58
|
+
* migration rather than silently omitted (it would be, if migrations saw the
|
|
59
|
+
* raw options while the worker ran the resolved ones).
|
|
60
|
+
*
|
|
61
|
+
* ## What it fills (each gated independently on caller silence)
|
|
62
|
+
*
|
|
63
|
+
* Secure-by-default cookies + secret-strength warning via {@link hardenAuthOptions},
|
|
64
|
+
* applied first so all hardening composes onto one options object.
|
|
65
|
+
*
|
|
66
|
+
* Rate limiting is ON by default for `/api/auth/*`.
|
|
67
|
+
*
|
|
68
|
+
* better-auth's own default is `rateLimit.enabled ?? isProduction`, and its
|
|
69
|
+
* `isProduction` is `"production" === "production"` resolved at
|
|
70
|
+
* module-load time. On Cloudflare Workers that check is unreliable: the
|
|
71
|
+
* runtime has no Node `process.env` (absent entirely without
|
|
72
|
+
* `nodejs_compat`, and even with it `NODE_ENV` is rarely `"production"` at
|
|
73
|
+
* request time). So better-auth would silently leave auth endpoints
|
|
74
|
+
* _unthrottled_ on a real deployment — the surprise we refuse to ship.
|
|
75
|
+
*
|
|
76
|
+
* We therefore default `enabled: true` whenever the caller hasn't made an
|
|
77
|
+
* explicit choice. We only fill the `enabled` flag and otherwise forward
|
|
78
|
+
* the caller's `rateLimit` verbatim, so better-auth's `window` (10s) / `max`
|
|
79
|
+
* (100) defaults and any custom rules still apply. Callers who genuinely
|
|
80
|
+
* want it off can pass `rateLimit: { enabled: false }` (e.g. when fronting
|
|
81
|
+
* auth with their own limiter), and any explicit `enabled` value wins.
|
|
82
|
+
*
|
|
83
|
+
* We also default `storage: "database"` — but only when rate limiting is not
|
|
84
|
+
* explicitly disabled (`enabled !== false`). Filling storage under a disabled
|
|
85
|
+
* limiter is harmless at runtime but makes `getAuthTables` emit an unused
|
|
86
|
+
* `rateLimit` table, so we skip it there.
|
|
87
|
+
*
|
|
88
|
+
* better-auth's own default is `storage: "memory"` — a per-isolate,
|
|
89
|
+
* non-durable counter. On Cloudflare Workers that means each isolate keeps
|
|
90
|
+
* its own tally, counters vanish on isolate recycle, and traffic spread
|
|
91
|
+
* across isolates never sums to the configured `max` — a limiter that
|
|
92
|
+
* reports "enabled" while never enforcing a global limit (the exact
|
|
93
|
+
* brute-force / credential-stuffing protection on `/sign-in`, OTP, and
|
|
94
|
+
* password-reset it is meant to buy). `storage: "database"` rides the counter
|
|
95
|
+
* through the configured `database` adapter — Lunora's store over the D1 auth
|
|
96
|
+
* tables — so the limit is durable *and* atomic (the store's native
|
|
97
|
+
* `incrementOne` gives a one-winner guarantee across isolates). Callers with
|
|
98
|
+
* their own durable store can pass an explicit `rateLimit: { storage: … }`
|
|
99
|
+
* (or `customStorage`), and any explicit value wins.
|
|
100
|
+
*
|
|
101
|
+
* Session cookie cache is ON by default too.
|
|
102
|
+
*
|
|
103
|
+
* Every authenticated call resolves identity through better-auth's
|
|
104
|
+
* `getSession`, which — without a cache — is a DB (D1) read on the hot path
|
|
105
|
+
* of every query/mutation/action that reads `ctx.auth` and of the WebSocket
|
|
106
|
+
* upgrade. better-auth's `session.cookieCache` carries the session payload in
|
|
107
|
+
* a short-lived signed cookie so `getSession` can answer without hitting the
|
|
108
|
+
* database until the cache window elapses. We default it on with a
|
|
109
|
+
* deliberately short 60s `maxAge` (better-auth's own default is 300s): long
|
|
110
|
+
* enough to erase the per-request read for a burst of calls, short enough
|
|
111
|
+
* that a revoked or role-changed session self-corrects within a minute.
|
|
112
|
+
* The one tradeoff — a revoked session stays valid until the cache expires —
|
|
113
|
+
* is bounded by that TTL; callers who need immediate revocation opt out with
|
|
114
|
+
* `session: { cookieCache: { enabled: false } }` (or the `strict` preset).
|
|
115
|
+
*
|
|
116
|
+
* Every explicit caller value is forwarded verbatim. The two `rateLimit` fills
|
|
117
|
+
* merge into a single `rateLimit` object so neither clobbers the other.
|
|
118
|
+
*/
|
|
119
|
+
declare const resolveAuthOptions: (options: LunoraAuthOptions) => LunoraAuthOptions;
|
|
120
|
+
/**
|
|
121
|
+
* Create the auth instance. Thin wrapper around `betterAuth` that enforces
|
|
122
|
+
* the `secret` requirement at construction time so misconfigured deployments
|
|
123
|
+
* fail loudly at the first fetch rather than the first sign-in attempt, then
|
|
124
|
+
* hands {@link resolveAuthOptions}'s hardened, default-filled options to
|
|
125
|
+
* better-auth.
|
|
126
|
+
*/
|
|
127
|
+
declare const createAuth: (options: LunoraAuthOptions) => LunoraAuth;
|
|
128
|
+
export { LunoraAuth as L, LunoraAuthOptions as a, createAuth as c, resolveAuthOptions as r };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as a}from"@lunora/errors";import{betterAuth as n}from"better-auth";import{validateSessionPolicy as c}from"./sessionPresets-DpEFjXKV.mjs";const i=32,l=e=>{const t=typeof e=="string"?e.trim().length:0;return t>0&&t<i},s=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,u=e=>{if(l(e.secret)){const r=`@lunora/auth: AUTH_SECRET is only ${String(e.secret?.trim().length)} characters. Use at least ${String(i)} for a brute-force-resistant secret — generate one with \`openssl rand -hex 32\`.`;if(!s(e.baseURL))throw new a("INTERNAL",r);console.warn(r)}const t=e.advanced??{};return{...e,advanced:{...t,defaultCookieAttributes:t.defaultCookieAttributes??{httpOnly:!0,path:"/",sameSite:"lax"},...t.useSecureCookies===void 0?{useSecureCookies:!s(e.baseURL)}:{}}}},h=e=>{const t=u(e),r=t.rateLimit?.enabled===void 0,o=t.rateLimit?.storage===void 0&&t.rateLimit?.enabled!==!1;return{...t,...r||o?{rateLimit:{...t.rateLimit,...r?{enabled:!0}:{},...o?{storage:"database"}:{}}}:{},...t.session?.cookieCache===void 0?{session:{...t.session,cookieCache:{enabled:!0,maxAge:60}}}:{}}},m=e=>{if(!e.secret||e.secret.trim()==="")throw new a("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&&c(e.session),n(h(e))};export{m as createAuth,h as resolveAuthOptions};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as i}from"@lunora/errors";import{APIError as u}from"better-auth/api";import{assertEmailAllowed as E}from"../email-guard.mjs";const m=a=>{switch(a){case 400:return"BAD_REQUEST";case 422:return"UNPROCESSABLE_ENTITY";case 429:return"TOO_MANY_REQUESTS";default:return"INTERNAL_SERVER_ERROR"}},n=a=>async(s,t)=>{const r=typeof s.email=="string"?s.email:void 0;if(r===void 0||r==="")return;let o;try{o=await E(r,a)}catch(e){throw e instanceof i?new u(m(e.status),{code:e.code,message:e.message}):e}a.onClassify?.(o,s,t)},b=(a={})=>({user:{create:{before:n(a)}}}),R=(a,s={})=>{const t=n(s),r=a.databaseHooks?.user?.create?.before,o=r?async(e,c)=>(await t(e,c),r(e,c)):t;return{...a,databaseHooks:{...a.databaseHooks,user:{...a.databaseHooks?.user,create:{...a.databaseHooks?.user?.create,before:o}}}}};export{b as emailGateDatabaseHooks,R as withEmailGate};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const r=s=>{const o=["expiresIn","updateAge","freshAge"];for(const n of o){const e=s[n];if(e!==void 0&&(typeof e!="number"||!Number.isFinite(e)||e<0))throw new TypeError(`@lunora/auth: \`session.${n}\` must be a non-negative, finite number of seconds`)}return s},i={longLived:{cookieCache:{enabled:!0,maxAge:60},expiresIn:2592e3,freshAge:86400,updateAge:86400},rolling:{cookieCache:{enabled:!0,maxAge:60},expiresIn:604800,freshAge:86400,updateAge:86400},strict:{cookieCache:{enabled:!1},expiresIn:3600,freshAge:300,updateAge:900}};export{i as sessionPresets,r as validateSessionPolicy};
|
package/dist/plugins-client.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { adminClient, anonymousClient, customSessionClient, deviceAuthorizationClient, emailOTPClient, genericOAuthClient, inferAdditionalFields, inferOrgAdditionalFields, jwtClient, lastLoginMethodClient, magicLinkClient, multiSessionClient, oidcClient, oneTimeTokenClient, organizationClient, phoneNumberClient, siweClient, twoFactorClient, usernameClient } from 'better-auth/client/plugins';
|
|
1
|
+
import{passkeyClient as n}from"@better-auth/passkey/client";import{adminClient as l,anonymousClient as o,customSessionClient as C,deviceAuthorizationClient as a,emailOTPClient as r,genericOAuthClient as s,inferAdditionalFields as m,inferOrgAdditionalFields as d,jwtClient as u,lastLoginMethodClient as c,magicLinkClient as g,multiSessionClient as f,oidcClient as h,oneTimeTokenClient as p,organizationClient as A,phoneNumberClient as k,siweClient as w,twoFactorClient as F,usernameClient as O}from"better-auth/client/plugins";export{l as adminClient,o as anonymousClient,C as customSessionClient,a as deviceAuthorizationClient,r as emailOTPClient,s as genericOAuthClient,m as inferAdditionalFields,d as inferOrgAdditionalFields,u as jwtClient,c as lastLoginMethodClient,g as magicLinkClient,f as multiSessionClient,h as oidcClient,p as oneTimeTokenClient,A as organizationClient,n as passkeyClient,k as phoneNumberClient,w as siweClient,F as twoFactorClient,O as usernameClient};
|
package/dist/plugins.mjs
CHANGED
|
@@ -1,22 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { captcha, mcp, withMcpAuth } from 'better-auth/plugins';
|
|
3
|
-
export { createAccessControl } from 'better-auth/plugins/access';
|
|
4
|
-
export { admin } from 'better-auth/plugins/admin';
|
|
5
|
-
export { anonymous } from 'better-auth/plugins/anonymous';
|
|
6
|
-
export { bearer } from 'better-auth/plugins/bearer';
|
|
7
|
-
export { customSession } from 'better-auth/plugins/custom-session';
|
|
8
|
-
export { deviceAuthorization } from 'better-auth/plugins/device-authorization';
|
|
9
|
-
export { emailOTP } from 'better-auth/plugins/email-otp';
|
|
10
|
-
export { genericOAuth } from 'better-auth/plugins/generic-oauth';
|
|
11
|
-
export { haveIBeenPwned } from 'better-auth/plugins/haveibeenpwned';
|
|
12
|
-
export { jwt } from 'better-auth/plugins/jwt';
|
|
13
|
-
export { magicLink } from 'better-auth/plugins/magic-link';
|
|
14
|
-
export { multiSession } from 'better-auth/plugins/multi-session';
|
|
15
|
-
export { oAuthProxy } from 'better-auth/plugins/oauth-proxy';
|
|
16
|
-
export { oidcProvider } from 'better-auth/plugins/oidc-provider';
|
|
17
|
-
export { oneTimeToken } from 'better-auth/plugins/one-time-token';
|
|
18
|
-
export { organization } from 'better-auth/plugins/organization';
|
|
19
|
-
export { phoneNumber } from 'better-auth/plugins/phone-number';
|
|
20
|
-
export { siwe } from 'better-auth/plugins/siwe';
|
|
21
|
-
export { twoFactor } from 'better-auth/plugins/two-factor';
|
|
22
|
-
export { username } from 'better-auth/plugins/username';
|
|
1
|
+
import{passkey as e}from"@better-auth/passkey";import{captcha as m,mcp as p,withMcpAuth as x}from"better-auth/plugins";import{createAccessControl as i}from"better-auth/plugins/access";import{admin as a}from"better-auth/plugins/admin";import{anonymous as s}from"better-auth/plugins/anonymous";import{bearer as h}from"better-auth/plugins/bearer";import{customSession as w}from"better-auth/plugins/custom-session";import{deviceAuthorization as P}from"better-auth/plugins/device-authorization";import{emailOTP as k}from"better-auth/plugins/email-otp";import{genericOAuth as v}from"better-auth/plugins/generic-oauth";import{haveIBeenPwned as T}from"better-auth/plugins/haveibeenpwned";import{jwt as z}from"better-auth/plugins/jwt";import{magicLink as S}from"better-auth/plugins/magic-link";import{multiSession as B}from"better-auth/plugins/multi-session";import{oAuthProxy as F}from"better-auth/plugins/oauth-proxy";import{oidcProvider as L}from"better-auth/plugins/oidc-provider";import{oneTimeToken as N}from"better-auth/plugins/one-time-token";import{organization as D}from"better-auth/plugins/organization";import{phoneNumber as G}from"better-auth/plugins/phone-number";import{siwe as J}from"better-auth/plugins/siwe";import{twoFactor as Q}from"better-auth/plugins/two-factor";import{username as U}from"better-auth/plugins/username";export{a as admin,s as anonymous,h as bearer,m as captcha,i as createAccessControl,w as customSession,P as deviceAuthorization,k as emailOTP,v as genericOAuth,T as haveIBeenPwned,z as jwt,S as magicLink,p as mcp,B as multiSession,F as oAuthProxy,L as oidcProvider,N as oneTimeToken,D as organization,e as passkey,G as phoneNumber,J as siwe,Q as twoFactor,U as username,x as withMcpAuth};
|
package/dist/schema.d.mts
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
1
|
import { TableDefinition } from '@lunora/server';
|
|
2
|
-
import { a as LunoraAuthOptions } from "./packem_shared/create-auth.d-
|
|
2
|
+
import { a as LunoraAuthOptions } from "./packem_shared/create-auth.d-De6IOirt.mjs";
|
|
3
3
|
import 'better-auth';
|
|
4
4
|
/**
|
|
5
|
-
* Derive Lunora table definitions from a better-auth config — the bridge that
|
|
6
|
-
* makes the **full** better-auth plugin ecosystem first-class Lunora data.
|
|
7
|
-
*
|
|
8
|
-
* better-auth's own `getAuthTables(options)` already merges every configured
|
|
9
|
-
* plugin's `schema` into one table map (core `user`/`session`/`account`/
|
|
10
|
-
* `verification`, plus whatever the plugins on `options.plugins` add —
|
|
11
|
-
* `organization`/`member`/`invitation`/`team`/`teamMember` from the
|
|
12
|
-
* organization plugin, `role`/`banned`/… columns from admin, `passkey`,
|
|
13
|
-
* `twoFactor`, `jwks`, …). This walks that map and emits an equivalent
|
|
14
|
-
* `defineTable` for each, so adding a plugin to `options.plugins` automatically
|
|
15
|
-
* surfaces its tables in the Lunora schema — no hand-written table definitions
|
|
16
|
-
* to keep in sync.
|
|
17
|
-
*
|
|
18
|
-
* Spread the result into `defineSchema` alongside your app tables (the keys are
|
|
19
|
-
* better-auth's real table names — `user`, `session`, … — left **unprefixed**
|
|
20
|
-
* because better-auth's adapter addresses them by exactly those names). Because
|
|
21
|
-
* the names are unprefixed, do **not** declare an app table that reuses one of
|
|
22
|
-
* better-auth's reserved names in the same `defineSchema` — JS spread order
|
|
23
|
-
* would let the later key win silently (unlike the plugin-extension path, which
|
|
24
|
-
* throws on collision):
|
|
25
|
-
*
|
|
26
|
-
* ```ts
|
|
27
|
-
* import { authTables } from "@lunora/auth";
|
|
28
|
-
* const authOptions = { emailAndPassword: { enabled: true }, plugins: [organization(), admin()] };
|
|
29
|
-
* export const schema = defineSchema({
|
|
30
|
-
* ...authTables(authOptions),
|
|
31
|
-
* todos: defineTable({ title: v.string() }),
|
|
32
|
-
* });
|
|
33
|
-
* ```
|
|
34
|
-
*
|
|
35
|
-
* Scope: this emits the table **shapes** (columns + types + nullability +
|
|
36
|
-
* uniqueness + FK ids). It deliberately does not carry better-auth's
|
|
37
|
-
* `defaultValue`/`onUpdate` (filled by better-auth's own write layer), `index`
|
|
38
|
-
* hints, or `bigint` precision (`bigint` fields map to `v.number()`). Those only
|
|
39
|
-
* matter once better-auth's writes are routed through Lunora's ORM — a separate
|
|
40
|
-
* adapter follow-up; today the auth rows are still written by better-auth's D1
|
|
41
|
-
* adapter and these tables make them typed + queryable via `ctx.db`.
|
|
42
|
-
*/
|
|
5
|
+
* Derive Lunora table definitions from a better-auth config — the bridge that
|
|
6
|
+
* makes the **full** better-auth plugin ecosystem first-class Lunora data.
|
|
7
|
+
*
|
|
8
|
+
* better-auth's own `getAuthTables(options)` already merges every configured
|
|
9
|
+
* plugin's `schema` into one table map (core `user`/`session`/`account`/
|
|
10
|
+
* `verification`, plus whatever the plugins on `options.plugins` add —
|
|
11
|
+
* `organization`/`member`/`invitation`/`team`/`teamMember` from the
|
|
12
|
+
* organization plugin, `role`/`banned`/… columns from admin, `passkey`,
|
|
13
|
+
* `twoFactor`, `jwks`, …). This walks that map and emits an equivalent
|
|
14
|
+
* `defineTable` for each, so adding a plugin to `options.plugins` automatically
|
|
15
|
+
* surfaces its tables in the Lunora schema — no hand-written table definitions
|
|
16
|
+
* to keep in sync.
|
|
17
|
+
*
|
|
18
|
+
* Spread the result into `defineSchema` alongside your app tables (the keys are
|
|
19
|
+
* better-auth's real table names — `user`, `session`, … — left **unprefixed**
|
|
20
|
+
* because better-auth's adapter addresses them by exactly those names). Because
|
|
21
|
+
* the names are unprefixed, do **not** declare an app table that reuses one of
|
|
22
|
+
* better-auth's reserved names in the same `defineSchema` — JS spread order
|
|
23
|
+
* would let the later key win silently (unlike the plugin-extension path, which
|
|
24
|
+
* throws on collision):
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { authTables } from "@lunora/auth";
|
|
28
|
+
* const authOptions = { emailAndPassword: { enabled: true }, plugins: [organization(), admin()] };
|
|
29
|
+
* export const schema = defineSchema({
|
|
30
|
+
* ...authTables(authOptions),
|
|
31
|
+
* todos: defineTable({ title: v.string() }),
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* Scope: this emits the table **shapes** (columns + types + nullability +
|
|
36
|
+
* uniqueness + FK ids). It deliberately does not carry better-auth's
|
|
37
|
+
* `defaultValue`/`onUpdate` (filled by better-auth's own write layer), `index`
|
|
38
|
+
* hints, or `bigint` precision (`bigint` fields map to `v.number()`). Those only
|
|
39
|
+
* matter once better-auth's writes are routed through Lunora's ORM — a separate
|
|
40
|
+
* adapter follow-up; today the auth rows are still written by better-auth's D1
|
|
41
|
+
* adapter and these tables make them typed + queryable via `ctx.db`.
|
|
42
|
+
*/
|
|
43
43
|
declare const authTables: (options: LunoraAuthOptions) => Record<string, TableDefinition>;
|
|
44
44
|
export { authTables as default };
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
1
|
import { TableDefinition } from '@lunora/server';
|
|
2
|
-
import { a as LunoraAuthOptions } from "./packem_shared/create-auth.d-
|
|
2
|
+
import { a as LunoraAuthOptions } from "./packem_shared/create-auth.d-De6IOirt.js";
|
|
3
3
|
import 'better-auth';
|
|
4
4
|
/**
|
|
5
|
-
* Derive Lunora table definitions from a better-auth config — the bridge that
|
|
6
|
-
* makes the **full** better-auth plugin ecosystem first-class Lunora data.
|
|
7
|
-
*
|
|
8
|
-
* better-auth's own `getAuthTables(options)` already merges every configured
|
|
9
|
-
* plugin's `schema` into one table map (core `user`/`session`/`account`/
|
|
10
|
-
* `verification`, plus whatever the plugins on `options.plugins` add —
|
|
11
|
-
* `organization`/`member`/`invitation`/`team`/`teamMember` from the
|
|
12
|
-
* organization plugin, `role`/`banned`/… columns from admin, `passkey`,
|
|
13
|
-
* `twoFactor`, `jwks`, …). This walks that map and emits an equivalent
|
|
14
|
-
* `defineTable` for each, so adding a plugin to `options.plugins` automatically
|
|
15
|
-
* surfaces its tables in the Lunora schema — no hand-written table definitions
|
|
16
|
-
* to keep in sync.
|
|
17
|
-
*
|
|
18
|
-
* Spread the result into `defineSchema` alongside your app tables (the keys are
|
|
19
|
-
* better-auth's real table names — `user`, `session`, … — left **unprefixed**
|
|
20
|
-
* because better-auth's adapter addresses them by exactly those names). Because
|
|
21
|
-
* the names are unprefixed, do **not** declare an app table that reuses one of
|
|
22
|
-
* better-auth's reserved names in the same `defineSchema` — JS spread order
|
|
23
|
-
* would let the later key win silently (unlike the plugin-extension path, which
|
|
24
|
-
* throws on collision):
|
|
25
|
-
*
|
|
26
|
-
* ```ts
|
|
27
|
-
* import { authTables } from "@lunora/auth";
|
|
28
|
-
* const authOptions = { emailAndPassword: { enabled: true }, plugins: [organization(), admin()] };
|
|
29
|
-
* export const schema = defineSchema({
|
|
30
|
-
* ...authTables(authOptions),
|
|
31
|
-
* todos: defineTable({ title: v.string() }),
|
|
32
|
-
* });
|
|
33
|
-
* ```
|
|
34
|
-
*
|
|
35
|
-
* Scope: this emits the table **shapes** (columns + types + nullability +
|
|
36
|
-
* uniqueness + FK ids). It deliberately does not carry better-auth's
|
|
37
|
-
* `defaultValue`/`onUpdate` (filled by better-auth's own write layer), `index`
|
|
38
|
-
* hints, or `bigint` precision (`bigint` fields map to `v.number()`). Those only
|
|
39
|
-
* matter once better-auth's writes are routed through Lunora's ORM — a separate
|
|
40
|
-
* adapter follow-up; today the auth rows are still written by better-auth's D1
|
|
41
|
-
* adapter and these tables make them typed + queryable via `ctx.db`.
|
|
42
|
-
*/
|
|
5
|
+
* Derive Lunora table definitions from a better-auth config — the bridge that
|
|
6
|
+
* makes the **full** better-auth plugin ecosystem first-class Lunora data.
|
|
7
|
+
*
|
|
8
|
+
* better-auth's own `getAuthTables(options)` already merges every configured
|
|
9
|
+
* plugin's `schema` into one table map (core `user`/`session`/`account`/
|
|
10
|
+
* `verification`, plus whatever the plugins on `options.plugins` add —
|
|
11
|
+
* `organization`/`member`/`invitation`/`team`/`teamMember` from the
|
|
12
|
+
* organization plugin, `role`/`banned`/… columns from admin, `passkey`,
|
|
13
|
+
* `twoFactor`, `jwks`, …). This walks that map and emits an equivalent
|
|
14
|
+
* `defineTable` for each, so adding a plugin to `options.plugins` automatically
|
|
15
|
+
* surfaces its tables in the Lunora schema — no hand-written table definitions
|
|
16
|
+
* to keep in sync.
|
|
17
|
+
*
|
|
18
|
+
* Spread the result into `defineSchema` alongside your app tables (the keys are
|
|
19
|
+
* better-auth's real table names — `user`, `session`, … — left **unprefixed**
|
|
20
|
+
* because better-auth's adapter addresses them by exactly those names). Because
|
|
21
|
+
* the names are unprefixed, do **not** declare an app table that reuses one of
|
|
22
|
+
* better-auth's reserved names in the same `defineSchema` — JS spread order
|
|
23
|
+
* would let the later key win silently (unlike the plugin-extension path, which
|
|
24
|
+
* throws on collision):
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { authTables } from "@lunora/auth";
|
|
28
|
+
* const authOptions = { emailAndPassword: { enabled: true }, plugins: [organization(), admin()] };
|
|
29
|
+
* export const schema = defineSchema({
|
|
30
|
+
* ...authTables(authOptions),
|
|
31
|
+
* todos: defineTable({ title: v.string() }),
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* Scope: this emits the table **shapes** (columns + types + nullability +
|
|
36
|
+
* uniqueness + FK ids). It deliberately does not carry better-auth's
|
|
37
|
+
* `defaultValue`/`onUpdate` (filled by better-auth's own write layer), `index`
|
|
38
|
+
* hints, or `bigint` precision (`bigint` fields map to `v.number()`). Those only
|
|
39
|
+
* matter once better-auth's writes are routed through Lunora's ORM — a separate
|
|
40
|
+
* adapter follow-up; today the auth rows are still written by better-auth's D1
|
|
41
|
+
* adapter and these tables make them typed + queryable via `ctx.db`.
|
|
42
|
+
*/
|
|
43
43
|
declare const authTables: (options: LunoraAuthOptions) => Record<string, TableDefinition>;
|
|
44
44
|
export { authTables as default };
|
package/dist/schema.mjs
CHANGED
|
@@ -1,62 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { v } from '@lunora/values';
|
|
3
|
-
import { getAuthTables } from 'better-auth/db';
|
|
4
|
-
|
|
5
|
-
const baseValidator = (attribute) => {
|
|
6
|
-
if (attribute.references) {
|
|
7
|
-
return v.id(attribute.references.model);
|
|
8
|
-
}
|
|
9
|
-
const { type } = attribute;
|
|
10
|
-
if (Array.isArray(type)) {
|
|
11
|
-
return v.string();
|
|
12
|
-
}
|
|
13
|
-
switch (type) {
|
|
14
|
-
case "boolean": {
|
|
15
|
-
return v.boolean();
|
|
16
|
-
}
|
|
17
|
-
case "date": {
|
|
18
|
-
return v.date();
|
|
19
|
-
}
|
|
20
|
-
case "number": {
|
|
21
|
-
return v.number();
|
|
22
|
-
}
|
|
23
|
-
case "number[]": {
|
|
24
|
-
return v.array(v.number());
|
|
25
|
-
}
|
|
26
|
-
case "string": {
|
|
27
|
-
return v.string();
|
|
28
|
-
}
|
|
29
|
-
case "string[]": {
|
|
30
|
-
return v.array(v.string());
|
|
31
|
-
}
|
|
32
|
-
// "json" and anything unrecognised: keep the row shape permissive rather
|
|
33
|
-
// than fail schema generation on a plugin's exotic column type.
|
|
34
|
-
default: {
|
|
35
|
-
return v.any();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
const fieldValidator = (attribute) => {
|
|
40
|
-
let validator = baseValidator(attribute);
|
|
41
|
-
if (attribute.required === false) {
|
|
42
|
-
validator = validator.nullable();
|
|
43
|
-
}
|
|
44
|
-
if (attribute.unique === true) {
|
|
45
|
-
validator = validator.unique();
|
|
46
|
-
}
|
|
47
|
-
return validator;
|
|
48
|
-
};
|
|
49
|
-
const authTables = (options) => {
|
|
50
|
-
const tables = getAuthTables(options);
|
|
51
|
-
const schema = {};
|
|
52
|
-
for (const table of Object.values(tables)) {
|
|
53
|
-
const shape = {};
|
|
54
|
-
for (const [fieldKey, attribute] of Object.entries(table.fields)) {
|
|
55
|
-
shape[attribute.fieldName ?? fieldKey] = fieldValidator(attribute);
|
|
56
|
-
}
|
|
57
|
-
schema[table.modelName] = defineTable(shape).externallyManaged();
|
|
58
|
-
}
|
|
59
|
-
return schema;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export { authTables as default };
|
|
1
|
+
import{defineTable as i}from"@lunora/server";import{v as e}from"@lunora/values";import{getAuthTables as c}from"better-auth/db";const f=n=>{if(n.references)return e.id(n.references.model);const{type:r}=n;if(Array.isArray(r))return e.string();switch(r){case"boolean":return e.boolean();case"date":return e.date();case"number":return e.number();case"number[]":return e.array(e.number());case"string":return e.string();case"string[]":return e.array(e.string());default:return e.any()}},l=n=>{let r=f(n);return n.required===!1&&(r=r.nullable()),n.unique===!0&&(r=r.unique()),r},g=n=>{const r=c(n),t={};for(const a of Object.values(r)){const s={};for(const[o,u]of Object.entries(a.fields))s[u.fieldName??o]=l(u);t[a.modelName]=i(s).externallyManaged()}return t};export{g as default};
|
package/dist/sql-store.d.mts
CHANGED
|
@@ -12,40 +12,40 @@ interface D1Like {
|
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* The minimal SQL seam a {@link createSqlAuthStore} runs on — structurally the
|
|
16
|
-
* same `{ all, run }` contract as `@lunora/d1`'s `D1Exec`, so a Lunora D1 binding
|
|
17
|
-
* satisfies it directly (see {@link d1Executor}) and a `node:sqlite` handle does
|
|
18
|
-
* too in tests. `all` runs reads and returns rows; `run` runs writes.
|
|
19
|
-
*/
|
|
15
|
+
* The minimal SQL seam a {@link createSqlAuthStore} runs on — structurally the
|
|
16
|
+
* same `{ all, run }` contract as `@lunora/d1`'s `D1Exec`, so a Lunora D1 binding
|
|
17
|
+
* satisfies it directly (see {@link d1Executor}) and a `node:sqlite` handle does
|
|
18
|
+
* too in tests. `all` runs reads and returns rows; `run` runs writes.
|
|
19
|
+
*/
|
|
20
20
|
interface SqlExecutor {
|
|
21
21
|
all: (sql: string, parameters: ReadonlyArray<unknown>) => Promise<Record<string, unknown>[]>;
|
|
22
22
|
run: (sql: string, parameters: ReadonlyArray<unknown>) => Promise<void>;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
* An {@link AuthStore} backed by a SQL database through the {@link SqlExecutor}
|
|
26
|
-
* seam — the production counterpart to `createMemoryAuthStore`. Point it at the
|
|
27
|
-
* same database that hosts Lunora's global (D1) tables (the ones `authTables(...)`
|
|
28
|
-
* generates) and better-auth's reads/writes land there as ordinary rows Lunora
|
|
29
|
-
* can also query. Assumes the auth tables already exist (Lunora owns the schema /
|
|
30
|
-
* migrations); it never issues DDL.
|
|
31
|
-
*
|
|
32
|
-
* ```ts
|
|
33
|
-
* const store = createSqlAuthStore(d1Executor(env.DB));
|
|
34
|
-
* const auth = createAuth({ secret: env.AUTH_SECRET, database: lunoraAuthAdapter(store) });
|
|
35
|
-
* ```
|
|
36
|
-
*
|
|
37
|
-
* Clause semantics match `createMemoryAuthStore` (operator parity is
|
|
38
|
-
* covered by a cross-store agreement test) with one unavoidable caveat:
|
|
39
|
-
* case-**insensitive** matching uses SQLite's ASCII-only `LOWER()`, whereas the
|
|
40
|
-
* in-memory store uses JS full-Unicode `toLowerCase()`. They agree on ASCII
|
|
41
|
-
* (emails, ids, tokens — the credential path); they can differ only for
|
|
42
|
-
* case-insensitive comparison of non-ASCII text.
|
|
43
|
-
*/
|
|
25
|
+
* An {@link AuthStore} backed by a SQL database through the {@link SqlExecutor}
|
|
26
|
+
* seam — the production counterpart to `createMemoryAuthStore`. Point it at the
|
|
27
|
+
* same database that hosts Lunora's global (D1) tables (the ones `authTables(...)`
|
|
28
|
+
* generates) and better-auth's reads/writes land there as ordinary rows Lunora
|
|
29
|
+
* can also query. Assumes the auth tables already exist (Lunora owns the schema /
|
|
30
|
+
* migrations); it never issues DDL.
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* const store = createSqlAuthStore(d1Executor(env.DB));
|
|
34
|
+
* const auth = createAuth({ secret: env.AUTH_SECRET, database: lunoraAuthAdapter(store) });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* Clause semantics match `createMemoryAuthStore` (operator parity is
|
|
38
|
+
* covered by a cross-store agreement test) with one unavoidable caveat:
|
|
39
|
+
* case-**insensitive** matching uses SQLite's ASCII-only `LOWER()`, whereas the
|
|
40
|
+
* in-memory store uses JS full-Unicode `toLowerCase()`. They agree on ASCII
|
|
41
|
+
* (emails, ids, tokens — the credential path); they can differ only for
|
|
42
|
+
* case-insensitive comparison of non-ASCII text.
|
|
43
|
+
*/
|
|
44
44
|
declare const createSqlAuthStore: (executor: SqlExecutor) => AuthStore;
|
|
45
45
|
/**
|
|
46
|
-
* Wrap a Cloudflare D1 binding (`env.DB`) as a {@link SqlExecutor}, so
|
|
47
|
-
* `createSqlAuthStore(d1Executor(env.DB))` routes better-auth onto D1 — the same
|
|
48
|
-
* binding Lunora's `.global()` tables use.
|
|
49
|
-
*/
|
|
46
|
+
* Wrap a Cloudflare D1 binding (`env.DB`) as a {@link SqlExecutor}, so
|
|
47
|
+
* `createSqlAuthStore(d1Executor(env.DB))` routes better-auth onto D1 — the same
|
|
48
|
+
* binding Lunora's `.global()` tables use.
|
|
49
|
+
*/
|
|
50
50
|
declare const d1Executor: (database: D1Like) => SqlExecutor;
|
|
51
51
|
export { SqlExecutor, createSqlAuthStore, d1Executor };
|
package/dist/sql-store.d.ts
CHANGED
|
@@ -12,40 +12,40 @@ interface D1Like {
|
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* The minimal SQL seam a {@link createSqlAuthStore} runs on — structurally the
|
|
16
|
-
* same `{ all, run }` contract as `@lunora/d1`'s `D1Exec`, so a Lunora D1 binding
|
|
17
|
-
* satisfies it directly (see {@link d1Executor}) and a `node:sqlite` handle does
|
|
18
|
-
* too in tests. `all` runs reads and returns rows; `run` runs writes.
|
|
19
|
-
*/
|
|
15
|
+
* The minimal SQL seam a {@link createSqlAuthStore} runs on — structurally the
|
|
16
|
+
* same `{ all, run }` contract as `@lunora/d1`'s `D1Exec`, so a Lunora D1 binding
|
|
17
|
+
* satisfies it directly (see {@link d1Executor}) and a `node:sqlite` handle does
|
|
18
|
+
* too in tests. `all` runs reads and returns rows; `run` runs writes.
|
|
19
|
+
*/
|
|
20
20
|
interface SqlExecutor {
|
|
21
21
|
all: (sql: string, parameters: ReadonlyArray<unknown>) => Promise<Record<string, unknown>[]>;
|
|
22
22
|
run: (sql: string, parameters: ReadonlyArray<unknown>) => Promise<void>;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
* An {@link AuthStore} backed by a SQL database through the {@link SqlExecutor}
|
|
26
|
-
* seam — the production counterpart to `createMemoryAuthStore`. Point it at the
|
|
27
|
-
* same database that hosts Lunora's global (D1) tables (the ones `authTables(...)`
|
|
28
|
-
* generates) and better-auth's reads/writes land there as ordinary rows Lunora
|
|
29
|
-
* can also query. Assumes the auth tables already exist (Lunora owns the schema /
|
|
30
|
-
* migrations); it never issues DDL.
|
|
31
|
-
*
|
|
32
|
-
* ```ts
|
|
33
|
-
* const store = createSqlAuthStore(d1Executor(env.DB));
|
|
34
|
-
* const auth = createAuth({ secret: env.AUTH_SECRET, database: lunoraAuthAdapter(store) });
|
|
35
|
-
* ```
|
|
36
|
-
*
|
|
37
|
-
* Clause semantics match `createMemoryAuthStore` (operator parity is
|
|
38
|
-
* covered by a cross-store agreement test) with one unavoidable caveat:
|
|
39
|
-
* case-**insensitive** matching uses SQLite's ASCII-only `LOWER()`, whereas the
|
|
40
|
-
* in-memory store uses JS full-Unicode `toLowerCase()`. They agree on ASCII
|
|
41
|
-
* (emails, ids, tokens — the credential path); they can differ only for
|
|
42
|
-
* case-insensitive comparison of non-ASCII text.
|
|
43
|
-
*/
|
|
25
|
+
* An {@link AuthStore} backed by a SQL database through the {@link SqlExecutor}
|
|
26
|
+
* seam — the production counterpart to `createMemoryAuthStore`. Point it at the
|
|
27
|
+
* same database that hosts Lunora's global (D1) tables (the ones `authTables(...)`
|
|
28
|
+
* generates) and better-auth's reads/writes land there as ordinary rows Lunora
|
|
29
|
+
* can also query. Assumes the auth tables already exist (Lunora owns the schema /
|
|
30
|
+
* migrations); it never issues DDL.
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* const store = createSqlAuthStore(d1Executor(env.DB));
|
|
34
|
+
* const auth = createAuth({ secret: env.AUTH_SECRET, database: lunoraAuthAdapter(store) });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* Clause semantics match `createMemoryAuthStore` (operator parity is
|
|
38
|
+
* covered by a cross-store agreement test) with one unavoidable caveat:
|
|
39
|
+
* case-**insensitive** matching uses SQLite's ASCII-only `LOWER()`, whereas the
|
|
40
|
+
* in-memory store uses JS full-Unicode `toLowerCase()`. They agree on ASCII
|
|
41
|
+
* (emails, ids, tokens — the credential path); they can differ only for
|
|
42
|
+
* case-insensitive comparison of non-ASCII text.
|
|
43
|
+
*/
|
|
44
44
|
declare const createSqlAuthStore: (executor: SqlExecutor) => AuthStore;
|
|
45
45
|
/**
|
|
46
|
-
* Wrap a Cloudflare D1 binding (`env.DB`) as a {@link SqlExecutor}, so
|
|
47
|
-
* `createSqlAuthStore(d1Executor(env.DB))` routes better-auth onto D1 — the same
|
|
48
|
-
* binding Lunora's `.global()` tables use.
|
|
49
|
-
*/
|
|
46
|
+
* Wrap a Cloudflare D1 binding (`env.DB`) as a {@link SqlExecutor}, so
|
|
47
|
+
* `createSqlAuthStore(d1Executor(env.DB))` routes better-auth onto D1 — the same
|
|
48
|
+
* binding Lunora's `.global()` tables use.
|
|
49
|
+
*/
|
|
50
50
|
declare const d1Executor: (database: D1Like) => SqlExecutor;
|
|
51
51
|
export { SqlExecutor, createSqlAuthStore, d1Executor };
|