elm-ssr 1.0.3 → 1.0.4

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.
Files changed (2) hide show
  1. package/lib/scaffold.mjs +55 -50
  2. package/package.json +1 -1
package/lib/scaffold.mjs CHANGED
@@ -495,23 +495,17 @@ view user =
495
495
  `;
496
496
 
497
497
  const betterAuthEndpointTemplate = () => `import { betterAuth } from "better-auth";
498
- import type { Middleware } from "elm-ssr/http";
499
- import type { RequestSession } from "elm-ssr/sessions";
500
-
501
- // BetterAuth is initialised per-request to pick up the right DB binding.
502
- // Production (Cloudflare Workers): env.DB is the D1 binding.
503
- // Local dev (Bun): falls back to bun:sqlite using app.db.
504
- const createAuth = (env: any) => {
505
- const db = env?.DB ?? (() => {
506
- // eslint-disable-next-line @typescript-eslint/no-require-imports
507
- const { Database } = require("bun:sqlite");
508
- return new Database("app.db");
509
- })();
510
-
511
- return betterAuth({
498
+
499
+ // BetterAuth is initialised per-request to pick up the right database binding.
500
+ // On Cloudflare Workers env.DB is the D1 binding; on Bun (local dev) the
501
+ // runtime injects a bun:sqlite Database as env.DB see runtime.ts.
502
+ // Pass env.DB directly so BetterAuth's adapter auto-detects the dialect
503
+ // (D1 via "batch"/"exec"/"prepare", bun:sqlite via "fileControl").
504
+ export const createAuth = (env: any) =>
505
+ betterAuth({
512
506
  baseURL: (env?.BETTER_AUTH_URL as string) ?? "http://localhost:8787",
513
507
  secret: (env?.BETTER_AUTH_SECRET as string) ?? "change-me-in-production",
514
- database: { type: "sqlite", db },
508
+ database: env?.DB,
515
509
  emailAndPassword: { enabled: true },
516
510
  // Uncomment to add social providers (add matching env vars in .dev.vars):
517
511
  // socialProviders: {
@@ -521,39 +515,9 @@ const createAuth = (env: any) => {
521
515
  // },
522
516
  // },
523
517
  });
524
- };
525
518
 
526
519
  export const handleAuth = (request: Request, env: any): Promise<Response> =>
527
520
  createAuth(env).handler(request);
528
-
529
- // Reads the BetterAuth session from the request and returns the user.
530
- const getSessionUser = async (
531
- request: Request,
532
- env: any
533
- ): Promise<{ email: string; name: string } | null> => {
534
- try {
535
- const session = await createAuth(env).api.getSession({ headers: request.headers });
536
- if (!session?.user) return null;
537
- return { email: session.user.email, name: session.user.name ?? session.user.email };
538
- } catch {
539
- return null;
540
- }
541
- };
542
-
543
- // elm-ssr middleware: reads BetterAuth's session and injects the user into
544
- // context.session so Loader.session / Loader.requireUser work on every request.
545
- export const betterAuthMiddleware: Middleware = async (context, next) => {
546
- const user = await getSessionUser(context.request, context.env);
547
- context.session = {
548
- id: user ? \`ba:\${user.email}\` : "",
549
- data: user,
550
- csrf: "",
551
- dirty: false,
552
- destroyed: false,
553
- isNew: false,
554
- } as RequestSession;
555
- return next(context);
556
- };
557
521
  `;
558
522
 
559
523
  const auth0EndpointTemplate = () => `import { generateSessionId, signValue, generateCsrfToken, readSignedCookie } from "elm-ssr/sessions";
@@ -686,7 +650,9 @@ const runtimeTemplate = (appRoot, db = false, auth = undefined) => {
686
650
 
687
651
  if (isBetterAuth) {
688
652
  imports.push(`import { sessionEffects } from "elm-ssr/sessions";`);
689
- imports.push(`import { handleAuth, betterAuthMiddleware } from "./src/Endpoints/Auth";`);
653
+ imports.push(`import type { Middleware } from "elm-ssr/http";`);
654
+ imports.push(`import type { RequestSession } from "elm-ssr/sessions";`);
655
+ imports.push(`import { createAuth, handleAuth } from "./src/Endpoints/Auth";`);
690
656
  } else if (isAuth0) {
691
657
  imports.push(`import { memorySessionStore } from "elm-ssr/sessions";`);
692
658
  imports.push(`import { handleAuth } from "./src/Endpoints/Auth";`);
@@ -746,13 +712,52 @@ if (typeof Bun !== "undefined") {
746
712
  }`;
747
713
 
748
714
  const effectsConfig = isBetterAuth
749
- ? `,\n effects: sessionEffects(${baseEffectsBody}),\n middlewares: [betterAuthMiddleware]`
715
+ ? `,\n effects: sessionEffects(${baseEffectsBody}),\n middlewares: [betterAuthBridge]`
750
716
  : `,\n effects: ${baseEffectsBody}`;
751
717
 
752
718
  // Auth0 keeps elm-ssr session middleware; BetterAuth manages sessions itself.
753
719
  let sessionsConfig = '';
754
720
  let authInit = '';
755
- if (isAuth0) {
721
+ if (isBetterAuth) {
722
+ authInit = `
723
+ // Local dev: open a SQLite database so BetterAuth works without a Cloudflare D1 binding.
724
+ // On Cloudflare Workers this block is eliminated by esbuild (typeof Bun is statically "undefined").
725
+ let bunAuthDb: any = undefined;
726
+ if (typeof (globalThis as any).Bun !== "undefined") {
727
+ const sqliteModule = "bun" + ":sqlite";
728
+ const { Database } = require(sqliteModule);
729
+ bunAuthDb = new Database(import.meta.dir + "/app.db");
730
+ }
731
+
732
+ const getAuthEnv = (env: any): any =>
733
+ bunAuthDb && !env?.DB ? { ...(env ?? {}), DB: bunAuthDb } : env;
734
+
735
+ // Bridge: reads BetterAuth session on every request so Loader.requireUser works.
736
+ const betterAuthBridge: Middleware = async (context, next) => {
737
+ try {
738
+ const session = await createAuth(getAuthEnv(context.env)).api.getSession({
739
+ headers: context.request.headers,
740
+ });
741
+ const user = session?.user
742
+ ? { email: session.user.email, name: session.user.name ?? session.user.email }
743
+ : null;
744
+ context.session = {
745
+ id: user ? \`ba:\${user.email}\` : "",
746
+ data: user,
747
+ csrf: "",
748
+ dirty: false,
749
+ destroyed: false,
750
+ isNew: false,
751
+ } as RequestSession;
752
+ } catch {
753
+ context.session = {
754
+ id: "", data: null, csrf: "", dirty: false, destroyed: false, isNew: false,
755
+ } as RequestSession;
756
+ }
757
+ return next(context);
758
+ };
759
+ `;
760
+ } else if (isAuth0) {
756
761
  authInit = `\nexport const sessionStore = memorySessionStore();\n`;
757
762
  sessionsConfig = `,
758
763
  sessions: {
@@ -766,12 +771,12 @@ if (typeof Bun !== "undefined") {
766
771
  let authIntercept = '';
767
772
  if (isBetterAuth) {
768
773
  authIntercept = `
769
- // Delegate all /api/auth/* requests to BetterAuth.
774
+ // Delegate all /api/auth/* requests to BetterAuth, injecting the local db when needed.
770
775
  const baseWorkerFetch = worker.fetch;
771
776
  worker.fetch = async (request, env, ctx) => {
772
777
  const url = new URL(request.url);
773
778
  if (url.pathname.startsWith("/api/auth/")) {
774
- return handleAuth(request, env);
779
+ return handleAuth(request, getAuthEnv(env));
775
780
  }
776
781
  return baseWorkerFetch(request, env, ctx);
777
782
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elm-ssr",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Elm-first SSR library and framework for Cloudflare Workers (and Bun): file-based routes/islands, backend-neutral effect adapters (KV/D1/Redis/Postgres), background tasks (waitUntil/Queues), SQL-file migrations, CLI scaffold + build.",
5
5
  "license": "MIT",
6
6
  "author": "Michał Majchrzak <michmajchrzak@gmail.com>",