hylekit 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -4
- package/dist/bff/index.cjs +2 -2
- package/dist/bff/index.cjs.map +1 -1
- package/dist/bff/index.js +2 -2
- package/dist/bff/index.js.map +1 -1
- package/dist/client/nextjs.cjs +2 -2
- package/dist/client/nextjs.cjs.map +1 -1
- package/dist/client/nextjs.js +2 -2
- package/dist/client/nextjs.js.map +1 -1
- package/dist/client/sveltekit.cjs +2 -2
- package/dist/client/sveltekit.cjs.map +1 -1
- package/dist/client/sveltekit.js +2 -2
- package/dist/client/sveltekit.js.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/server/express.cjs +2 -2
- package/dist/server/express.cjs.map +1 -1
- package/dist/server/express.js +2 -2
- package/dist/server/express.js.map +1 -1
- package/package.json +4 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/express.ts","../../src/lib/db.ts","../../src/lib/schema.ts"],"sourcesContent":["import type { Request, Response, NextFunction } from \"express\";\nimport type { Session, User } from \"better-auth\";\nimport { db } from \"../lib/db\";\nimport { session as sessionTable, user as userTable } from \"../lib/schema\";\nimport { eq, and, gt } from \"drizzle-orm\";\n\n/**\n * Extended Request with authenticated user context.\n */\nexport interface AuthenticatedRequest extends Request {\n /**\n * The authenticated user. Available on all authenticated routes.\n */\n authUser: User;\n\n /**\n * The current session. Available on all authenticated routes.\n */\n authSession: Session;\n\n /**\n * @deprecated Use authUser instead\n */\n user?: User;\n\n /**\n * @deprecated Use authSession instead\n */\n session?: Session;\n}\n\nexport interface MiddlewareOptions {\n /**\n * Routes that don't require authentication.\n * Supports exact paths and patterns with wildcards.\n * @example [\"/health\", \"/public/*\", \"/api/webhooks/*\"]\n */\n unauthenticatedRoutes?: string[];\n\n /**\n * If true, verify session against the database.\n * Use for service-to-service calls where headers can't be implicitly trusted.\n * @default false\n */\n verifySession?: boolean;\n\n /**\n * Whether to require authentication for non-unauthenticated routes.\n * Returns 401 on missing/invalid session.\n * @default true\n */\n required?: boolean;\n}\n\n/**\n * Check if a path matches any of the unauthenticated route patterns.\n */\nfunction isUnauthenticatedRoute(path: string, patterns: string[]): boolean {\n for (const pattern of patterns) {\n if (pattern === path) return true;\n\n // Handle wildcard patterns like \"/public/*\"\n if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -2);\n if (path === prefix || path.startsWith(prefix + \"/\")) {\n return true;\n }\n }\n\n // Handle double wildcard patterns like \"/api/**/health\"\n if (pattern.includes(\"**\")) {\n const regex = new RegExp(\n \"^\" + pattern.replace(/\\*\\*/g, \".*\").replace(/\\*/g, \"[^/]*\") + \"$\"\n );\n if (regex.test(path)) return true;\n }\n }\n return false;\n}\n\n/**\n * Express middleware for session verification.\n * \n * @example\n * // Basic usage - trust headers from BFF\n * app.use(middleware());\n * \n * @example\n * // With DB verification for service-to-service calls\n * app.use(middleware({ verifySession: true }));\n * \n * @example\n * // With unauthenticated routes\n * app.use(middleware({\n * unauthenticatedRoutes: [\"/health\", \"/public/*\", \"/api/webhooks/*\"],\n * verifySession: true\n * }));\n */\nexport const middleware = (options: MiddlewareOptions = {}) => {\n const {\n unauthenticatedRoutes = [],\n verifySession = false,\n required = true\n } = options;\n\n return async (req: Request, res: Response, next: NextFunction) => {\n const authReq = req as AuthenticatedRequest;\n\n // Check if route is unauthenticated\n if (isUnauthenticatedRoute(req.path, unauthenticatedRoutes)) {\n return next();\n }\n\n try {\n const userHeader = req.headers[\"x-hyle-user\"];\n const sessionHeader = req.headers[\"x-hyle-session\"];\n\n // No session header provided\n if (!sessionHeader || typeof sessionHeader !== \"string\") {\n if (required) {\n return res.status(401).json({ error: \"Unauthorized\" });\n }\n return next();\n }\n\n const sessionData = JSON.parse(\n Buffer.from(sessionHeader, \"base64\").toString(\"utf-8\")\n );\n\n // Verify session against DB if required\n if (verifySession) {\n const result = await db\n .select({\n session: sessionTable,\n user: userTable,\n })\n .from(sessionTable)\n .innerJoin(userTable, eq(sessionTable.userId, userTable.id))\n .where(\n and(\n eq(sessionTable.id, sessionData.id),\n gt(sessionTable.expiresAt, new Date())\n )\n )\n .limit(1);\n\n if (result.length === 0) {\n if (required) {\n return res.status(401).json({ error: \"Invalid or expired session\" });\n }\n return next();\n }\n\n // Inject auth context\n authReq.authUser = result[0].user as User;\n authReq.authSession = result[0].session as Session;\n\n // Keep deprecated properties for backwards compatibility\n authReq.user = authReq.authUser;\n authReq.session = authReq.authSession;\n\n return next();\n }\n\n // Trust headers mode (for internal BFF calls)\n let userData: User | undefined;\n\n if (userHeader && typeof userHeader === \"string\") {\n userData = JSON.parse(\n Buffer.from(userHeader, \"base64\").toString(\"utf-8\")\n );\n }\n\n if (!userData && required) {\n return res.status(401).json({ error: \"Unauthorized\" });\n }\n\n if (userData) {\n // Inject auth context\n authReq.authUser = userData;\n authReq.authSession = sessionData as Session;\n\n // Keep deprecated properties for backwards compatibility\n authReq.user = authReq.authUser;\n authReq.session = authReq.authSession;\n }\n\n next();\n } catch (error) {\n console.error(\"[hyle] Auth middleware error:\", error);\n if (required) {\n return res.status(401).json({ error: \"Authentication failed\" });\n }\n next();\n }\n };\n};\n\n/**\n * Type guard to check if request is authenticated.\n */\nexport function isAuthenticated(req: Request): req is AuthenticatedRequest {\n return !!(req as AuthenticatedRequest).authUser;\n}\n\n/**\n * Helper to get auth context from request.\n * Throws if not authenticated.\n */\nexport function getAuthContext(req: Request): { user: User; session: Session } {\n const authReq = req as AuthenticatedRequest;\n if (!authReq.authUser || !authReq.authSession) {\n throw new Error(\"Request is not authenticated\");\n }\n return { user: authReq.authUser, session: authReq.authSession };\n}\n\n/**\n * Express adapter exports\n */\nexport const expressAdapter = {\n middleware,\n isAuthenticated,\n getAuthContext,\n};\n\n// Default export for convenience\nexport default expressAdapter;\n","import { drizzle } from \"drizzle-orm/libsql\";\nimport { createClient } from \"@libsql/client\";\nimport * as schema from \"./schema\";\n\nconst client = createClient({\n url: process.env.DATABASE_URL!,\n authToken: process.env.DATABASE_AUTH_TOKEN!,\n});\n\nexport const db = drizzle(client, { schema });\n","import { relations, sql } from \"drizzle-orm\";\nimport { sqliteTable, text, integer, index } from \"drizzle-orm/sqlite-core\";\n\nexport const user = sqliteTable(\"user\", {\n id: text(\"id\").primaryKey(),\n name: text(\"name\").notNull(),\n email: text(\"email\").notNull().unique(),\n emailVerified: integer(\"email_verified\", { mode: \"boolean\" })\n .default(false)\n .notNull(),\n image: text(\"image\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n});\n\nexport const session = sqliteTable(\n \"session\",\n {\n id: text(\"id\").primaryKey(),\n expiresAt: integer(\"expires_at\", { mode: \"timestamp_ms\" }).notNull(),\n token: text(\"token\").notNull().unique(),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n ipAddress: text(\"ip_address\"),\n userAgent: text(\"user_agent\"),\n userId: text(\"user_id\")\n .notNull()\n .references(() => user.id, { onDelete: \"cascade\" }),\n },\n (table) => [index(\"session_userId_idx\").on(table.userId)],\n);\n\nexport const account = sqliteTable(\n \"account\",\n {\n id: text(\"id\").primaryKey(),\n accountId: text(\"account_id\").notNull(),\n providerId: text(\"provider_id\").notNull(),\n userId: text(\"user_id\")\n .notNull()\n .references(() => user.id, { onDelete: \"cascade\" }),\n accessToken: text(\"access_token\"),\n refreshToken: text(\"refresh_token\"),\n idToken: text(\"id_token\"),\n accessTokenExpiresAt: integer(\"access_token_expires_at\", {\n mode: \"timestamp_ms\",\n }),\n refreshTokenExpiresAt: integer(\"refresh_token_expires_at\", {\n mode: \"timestamp_ms\",\n }),\n scope: text(\"scope\"),\n password: text(\"password\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n },\n (table) => [index(\"account_userId_idx\").on(table.userId)],\n);\n\nexport const verification = sqliteTable(\n \"verification\",\n {\n id: text(\"id\").primaryKey(),\n identifier: text(\"identifier\").notNull(),\n value: text(\"value\").notNull(),\n expiresAt: integer(\"expires_at\", { mode: \"timestamp_ms\" }).notNull(),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n },\n (table) => [index(\"verification_identifier_idx\").on(table.identifier)],\n);\n\nexport const userRelations = relations(user, ({ many }) => ({\n sessions: many(session),\n accounts: many(account),\n}));\n\nexport const sessionRelations = relations(session, ({ one }) => ({\n user: one(user, {\n fields: [session.userId],\n references: [user.id],\n }),\n}));\n\nexport const accountRelations = relations(account, ({ one }) => ({\n user: one(user, {\n fields: [account.userId],\n references: [user.id],\n }),\n}));\n"],"mappings":"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","names":["import_drizzle_orm"]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/express.ts","../../src/lib/db.ts","../../src/lib/schema.ts"],"sourcesContent":["import type { Request, Response, NextFunction } from \"express\";\nimport type { Session, User } from \"better-auth\";\nimport { db } from \"../lib/db\";\nimport { session as sessionTable, user as userTable } from \"../lib/schema\";\nimport { eq, and, gt } from \"drizzle-orm\";\n\n/**\n * Extended Request with authenticated user context.\n */\nexport interface AuthenticatedRequest extends Request {\n /**\n * The authenticated user. Available on all authenticated routes.\n */\n authUser: User;\n\n /**\n * The current session. Available on all authenticated routes.\n */\n authSession: Session;\n\n /**\n * @deprecated Use authUser instead\n */\n user?: User;\n\n /**\n * @deprecated Use authSession instead\n */\n session?: Session;\n}\n\nexport interface MiddlewareOptions {\n /**\n * Routes that don't require authentication.\n * Supports exact paths and patterns with wildcards.\n * @example [\"/health\", \"/public/*\", \"/api/webhooks/*\"]\n */\n unauthenticatedRoutes?: string[];\n\n /**\n * If true, verify session against the database.\n * Use for service-to-service calls where headers can't be implicitly trusted.\n * @default false\n */\n verifySession?: boolean;\n\n /**\n * Whether to require authentication for non-unauthenticated routes.\n * Returns 401 on missing/invalid session.\n * @default true\n */\n required?: boolean;\n}\n\n/**\n * Check if a path matches any of the unauthenticated route patterns.\n */\nfunction isUnauthenticatedRoute(path: string, patterns: string[]): boolean {\n for (const pattern of patterns) {\n if (pattern === path) return true;\n\n // Handle wildcard patterns like \"/public/*\"\n if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -2);\n if (path === prefix || path.startsWith(prefix + \"/\")) {\n return true;\n }\n }\n\n // Handle double wildcard patterns like \"/api/**/health\"\n if (pattern.includes(\"**\")) {\n const regex = new RegExp(\n \"^\" + pattern.replace(/\\*\\*/g, \".*\").replace(/\\*/g, \"[^/]*\") + \"$\"\n );\n if (regex.test(path)) return true;\n }\n }\n return false;\n}\n\n/**\n * Express middleware for session verification.\n * \n * @example\n * // Basic usage - trust headers from BFF\n * app.use(middleware());\n * \n * @example\n * // With DB verification for service-to-service calls\n * app.use(middleware({ verifySession: true }));\n * \n * @example\n * // With unauthenticated routes\n * app.use(middleware({\n * unauthenticatedRoutes: [\"/health\", \"/public/*\", \"/api/webhooks/*\"],\n * verifySession: true\n * }));\n */\nexport const middleware = (options: MiddlewareOptions = {}) => {\n const {\n unauthenticatedRoutes = [],\n verifySession = false,\n required = true\n } = options;\n\n return async (req: Request, res: Response, next: NextFunction) => {\n const authReq = req as AuthenticatedRequest;\n\n // Check if route is unauthenticated\n if (isUnauthenticatedRoute(req.path, unauthenticatedRoutes)) {\n return next();\n }\n\n try {\n const userHeader = req.headers[\"x-hyle-user\"];\n const sessionHeader = req.headers[\"x-hyle-session\"];\n\n // No session header provided\n if (!sessionHeader || typeof sessionHeader !== \"string\") {\n if (required) {\n return res.status(401).json({ error: \"Unauthorized\" });\n }\n return next();\n }\n\n const sessionData = JSON.parse(\n Buffer.from(sessionHeader, \"base64\").toString(\"utf-8\")\n );\n\n // Verify session against DB if required\n if (verifySession) {\n const result = await db\n .select({\n session: sessionTable,\n user: userTable,\n })\n .from(sessionTable)\n .innerJoin(userTable, eq(sessionTable.userId, userTable.id))\n .where(\n and(\n eq(sessionTable.id, sessionData.id),\n gt(sessionTable.expiresAt, new Date())\n )\n )\n .limit(1);\n\n if (result.length === 0) {\n if (required) {\n return res.status(401).json({ error: \"Invalid or expired session\" });\n }\n return next();\n }\n\n // Inject auth context\n authReq.authUser = result[0].user as User;\n authReq.authSession = result[0].session as Session;\n\n // Keep deprecated properties for backwards compatibility\n authReq.user = authReq.authUser;\n authReq.session = authReq.authSession;\n\n return next();\n }\n\n // Trust headers mode (for internal BFF calls)\n let userData: User | undefined;\n\n if (userHeader && typeof userHeader === \"string\") {\n userData = JSON.parse(\n Buffer.from(userHeader, \"base64\").toString(\"utf-8\")\n );\n }\n\n if (!userData && required) {\n return res.status(401).json({ error: \"Unauthorized\" });\n }\n\n if (userData) {\n // Inject auth context\n authReq.authUser = userData;\n authReq.authSession = sessionData as Session;\n\n // Keep deprecated properties for backwards compatibility\n authReq.user = authReq.authUser;\n authReq.session = authReq.authSession;\n }\n\n next();\n } catch (error) {\n console.error(\"[hyle] Auth middleware error:\", error);\n if (required) {\n return res.status(401).json({ error: \"Authentication failed\" });\n }\n next();\n }\n };\n};\n\n/**\n * Type guard to check if request is authenticated.\n */\nexport function isAuthenticated(req: Request): req is AuthenticatedRequest {\n return !!(req as AuthenticatedRequest).authUser;\n}\n\n/**\n * Helper to get auth context from request.\n * Throws if not authenticated.\n */\nexport function getAuthContext(req: Request): { user: User; session: Session } {\n const authReq = req as AuthenticatedRequest;\n if (!authReq.authUser || !authReq.authSession) {\n throw new Error(\"Request is not authenticated\");\n }\n return { user: authReq.authUser, session: authReq.authSession };\n}\n\n/**\n * Express adapter exports\n */\nexport const expressAdapter = {\n middleware,\n isAuthenticated,\n getAuthContext,\n};\n\n// Default export for convenience\nexport default expressAdapter;\n","import { drizzle } from \"drizzle-orm/libsql\";\nimport { createClient } from \"@libsql/client\";\nimport * as schema from \"./schema\";\n\nconst client = createClient({\n url: process.env.HYLE_DATABASE_URL!,\n authToken: process.env.HYLE_DATABASE_AUTH_TOKEN!,\n});\n\nexport const db = drizzle(client, { schema });\n","import { relations, sql } from \"drizzle-orm\";\nimport { sqliteTable, text, integer, index } from \"drizzle-orm/sqlite-core\";\n\nexport const user = sqliteTable(\"user\", {\n id: text(\"id\").primaryKey(),\n name: text(\"name\").notNull(),\n email: text(\"email\").notNull().unique(),\n emailVerified: integer(\"email_verified\", { mode: \"boolean\" })\n .default(false)\n .notNull(),\n image: text(\"image\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n});\n\nexport const session = sqliteTable(\n \"session\",\n {\n id: text(\"id\").primaryKey(),\n expiresAt: integer(\"expires_at\", { mode: \"timestamp_ms\" }).notNull(),\n token: text(\"token\").notNull().unique(),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n ipAddress: text(\"ip_address\"),\n userAgent: text(\"user_agent\"),\n userId: text(\"user_id\")\n .notNull()\n .references(() => user.id, { onDelete: \"cascade\" }),\n },\n (table) => [index(\"session_userId_idx\").on(table.userId)],\n);\n\nexport const account = sqliteTable(\n \"account\",\n {\n id: text(\"id\").primaryKey(),\n accountId: text(\"account_id\").notNull(),\n providerId: text(\"provider_id\").notNull(),\n userId: text(\"user_id\")\n .notNull()\n .references(() => user.id, { onDelete: \"cascade\" }),\n accessToken: text(\"access_token\"),\n refreshToken: text(\"refresh_token\"),\n idToken: text(\"id_token\"),\n accessTokenExpiresAt: integer(\"access_token_expires_at\", {\n mode: \"timestamp_ms\",\n }),\n refreshTokenExpiresAt: integer(\"refresh_token_expires_at\", {\n mode: \"timestamp_ms\",\n }),\n scope: text(\"scope\"),\n password: text(\"password\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n },\n (table) => [index(\"account_userId_idx\").on(table.userId)],\n);\n\nexport const verification = sqliteTable(\n \"verification\",\n {\n id: text(\"id\").primaryKey(),\n identifier: text(\"identifier\").notNull(),\n value: text(\"value\").notNull(),\n expiresAt: integer(\"expires_at\", { mode: \"timestamp_ms\" }).notNull(),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n },\n (table) => [index(\"verification_identifier_idx\").on(table.identifier)],\n);\n\nexport const userRelations = relations(user, ({ many }) => ({\n sessions: many(session),\n accounts: many(account),\n}));\n\nexport const sessionRelations = relations(session, ({ one }) => ({\n user: one(user, {\n fields: [session.userId],\n references: [user.id],\n }),\n}));\n\nexport const accountRelations = relations(account, ({ one }) => ({\n user: one(user, {\n fields: [account.userId],\n references: [user.id],\n }),\n}));\n"],"mappings":"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","names":["import_drizzle_orm"]}
|
package/dist/server/express.js
CHANGED
|
@@ -98,8 +98,8 @@ var accountRelations = relations(account, ({ one }) => ({
|
|
|
98
98
|
|
|
99
99
|
// src/lib/db.ts
|
|
100
100
|
var client = createClient({
|
|
101
|
-
url: process.env.
|
|
102
|
-
authToken: process.env.
|
|
101
|
+
url: process.env.HYLE_DATABASE_URL,
|
|
102
|
+
authToken: process.env.HYLE_DATABASE_AUTH_TOKEN
|
|
103
103
|
});
|
|
104
104
|
var db = drizzle(client, { schema: schema_exports });
|
|
105
105
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/db.ts","../../src/lib/schema.ts","../../src/server/express.ts"],"sourcesContent":["import { drizzle } from \"drizzle-orm/libsql\";\nimport { createClient } from \"@libsql/client\";\nimport * as schema from \"./schema\";\n\nconst client = createClient({\n url: process.env.DATABASE_URL!,\n authToken: process.env.DATABASE_AUTH_TOKEN!,\n});\n\nexport const db = drizzle(client, { schema });\n","import { relations, sql } from \"drizzle-orm\";\nimport { sqliteTable, text, integer, index } from \"drizzle-orm/sqlite-core\";\n\nexport const user = sqliteTable(\"user\", {\n id: text(\"id\").primaryKey(),\n name: text(\"name\").notNull(),\n email: text(\"email\").notNull().unique(),\n emailVerified: integer(\"email_verified\", { mode: \"boolean\" })\n .default(false)\n .notNull(),\n image: text(\"image\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n});\n\nexport const session = sqliteTable(\n \"session\",\n {\n id: text(\"id\").primaryKey(),\n expiresAt: integer(\"expires_at\", { mode: \"timestamp_ms\" }).notNull(),\n token: text(\"token\").notNull().unique(),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n ipAddress: text(\"ip_address\"),\n userAgent: text(\"user_agent\"),\n userId: text(\"user_id\")\n .notNull()\n .references(() => user.id, { onDelete: \"cascade\" }),\n },\n (table) => [index(\"session_userId_idx\").on(table.userId)],\n);\n\nexport const account = sqliteTable(\n \"account\",\n {\n id: text(\"id\").primaryKey(),\n accountId: text(\"account_id\").notNull(),\n providerId: text(\"provider_id\").notNull(),\n userId: text(\"user_id\")\n .notNull()\n .references(() => user.id, { onDelete: \"cascade\" }),\n accessToken: text(\"access_token\"),\n refreshToken: text(\"refresh_token\"),\n idToken: text(\"id_token\"),\n accessTokenExpiresAt: integer(\"access_token_expires_at\", {\n mode: \"timestamp_ms\",\n }),\n refreshTokenExpiresAt: integer(\"refresh_token_expires_at\", {\n mode: \"timestamp_ms\",\n }),\n scope: text(\"scope\"),\n password: text(\"password\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n },\n (table) => [index(\"account_userId_idx\").on(table.userId)],\n);\n\nexport const verification = sqliteTable(\n \"verification\",\n {\n id: text(\"id\").primaryKey(),\n identifier: text(\"identifier\").notNull(),\n value: text(\"value\").notNull(),\n expiresAt: integer(\"expires_at\", { mode: \"timestamp_ms\" }).notNull(),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n },\n (table) => [index(\"verification_identifier_idx\").on(table.identifier)],\n);\n\nexport const userRelations = relations(user, ({ many }) => ({\n sessions: many(session),\n accounts: many(account),\n}));\n\nexport const sessionRelations = relations(session, ({ one }) => ({\n user: one(user, {\n fields: [session.userId],\n references: [user.id],\n }),\n}));\n\nexport const accountRelations = relations(account, ({ one }) => ({\n user: one(user, {\n fields: [account.userId],\n references: [user.id],\n }),\n}));\n","import type { Request, Response, NextFunction } from \"express\";\nimport type { Session, User } from \"better-auth\";\nimport { db } from \"../lib/db\";\nimport { session as sessionTable, user as userTable } from \"../lib/schema\";\nimport { eq, and, gt } from \"drizzle-orm\";\n\n/**\n * Extended Request with authenticated user context.\n */\nexport interface AuthenticatedRequest extends Request {\n /**\n * The authenticated user. Available on all authenticated routes.\n */\n authUser: User;\n\n /**\n * The current session. Available on all authenticated routes.\n */\n authSession: Session;\n\n /**\n * @deprecated Use authUser instead\n */\n user?: User;\n\n /**\n * @deprecated Use authSession instead\n */\n session?: Session;\n}\n\nexport interface MiddlewareOptions {\n /**\n * Routes that don't require authentication.\n * Supports exact paths and patterns with wildcards.\n * @example [\"/health\", \"/public/*\", \"/api/webhooks/*\"]\n */\n unauthenticatedRoutes?: string[];\n\n /**\n * If true, verify session against the database.\n * Use for service-to-service calls where headers can't be implicitly trusted.\n * @default false\n */\n verifySession?: boolean;\n\n /**\n * Whether to require authentication for non-unauthenticated routes.\n * Returns 401 on missing/invalid session.\n * @default true\n */\n required?: boolean;\n}\n\n/**\n * Check if a path matches any of the unauthenticated route patterns.\n */\nfunction isUnauthenticatedRoute(path: string, patterns: string[]): boolean {\n for (const pattern of patterns) {\n if (pattern === path) return true;\n\n // Handle wildcard patterns like \"/public/*\"\n if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -2);\n if (path === prefix || path.startsWith(prefix + \"/\")) {\n return true;\n }\n }\n\n // Handle double wildcard patterns like \"/api/**/health\"\n if (pattern.includes(\"**\")) {\n const regex = new RegExp(\n \"^\" + pattern.replace(/\\*\\*/g, \".*\").replace(/\\*/g, \"[^/]*\") + \"$\"\n );\n if (regex.test(path)) return true;\n }\n }\n return false;\n}\n\n/**\n * Express middleware for session verification.\n * \n * @example\n * // Basic usage - trust headers from BFF\n * app.use(middleware());\n * \n * @example\n * // With DB verification for service-to-service calls\n * app.use(middleware({ verifySession: true }));\n * \n * @example\n * // With unauthenticated routes\n * app.use(middleware({\n * unauthenticatedRoutes: [\"/health\", \"/public/*\", \"/api/webhooks/*\"],\n * verifySession: true\n * }));\n */\nexport const middleware = (options: MiddlewareOptions = {}) => {\n const {\n unauthenticatedRoutes = [],\n verifySession = false,\n required = true\n } = options;\n\n return async (req: Request, res: Response, next: NextFunction) => {\n const authReq = req as AuthenticatedRequest;\n\n // Check if route is unauthenticated\n if (isUnauthenticatedRoute(req.path, unauthenticatedRoutes)) {\n return next();\n }\n\n try {\n const userHeader = req.headers[\"x-hyle-user\"];\n const sessionHeader = req.headers[\"x-hyle-session\"];\n\n // No session header provided\n if (!sessionHeader || typeof sessionHeader !== \"string\") {\n if (required) {\n return res.status(401).json({ error: \"Unauthorized\" });\n }\n return next();\n }\n\n const sessionData = JSON.parse(\n Buffer.from(sessionHeader, \"base64\").toString(\"utf-8\")\n );\n\n // Verify session against DB if required\n if (verifySession) {\n const result = await db\n .select({\n session: sessionTable,\n user: userTable,\n })\n .from(sessionTable)\n .innerJoin(userTable, eq(sessionTable.userId, userTable.id))\n .where(\n and(\n eq(sessionTable.id, sessionData.id),\n gt(sessionTable.expiresAt, new Date())\n )\n )\n .limit(1);\n\n if (result.length === 0) {\n if (required) {\n return res.status(401).json({ error: \"Invalid or expired session\" });\n }\n return next();\n }\n\n // Inject auth context\n authReq.authUser = result[0].user as User;\n authReq.authSession = result[0].session as Session;\n\n // Keep deprecated properties for backwards compatibility\n authReq.user = authReq.authUser;\n authReq.session = authReq.authSession;\n\n return next();\n }\n\n // Trust headers mode (for internal BFF calls)\n let userData: User | undefined;\n\n if (userHeader && typeof userHeader === \"string\") {\n userData = JSON.parse(\n Buffer.from(userHeader, \"base64\").toString(\"utf-8\")\n );\n }\n\n if (!userData && required) {\n return res.status(401).json({ error: \"Unauthorized\" });\n }\n\n if (userData) {\n // Inject auth context\n authReq.authUser = userData;\n authReq.authSession = sessionData as Session;\n\n // Keep deprecated properties for backwards compatibility\n authReq.user = authReq.authUser;\n authReq.session = authReq.authSession;\n }\n\n next();\n } catch (error) {\n console.error(\"[hyle] Auth middleware error:\", error);\n if (required) {\n return res.status(401).json({ error: \"Authentication failed\" });\n }\n next();\n }\n };\n};\n\n/**\n * Type guard to check if request is authenticated.\n */\nexport function isAuthenticated(req: Request): req is AuthenticatedRequest {\n return !!(req as AuthenticatedRequest).authUser;\n}\n\n/**\n * Helper to get auth context from request.\n * Throws if not authenticated.\n */\nexport function getAuthContext(req: Request): { user: User; session: Session } {\n const authReq = req as AuthenticatedRequest;\n if (!authReq.authUser || !authReq.authSession) {\n throw new Error(\"Request is not authenticated\");\n }\n return { user: authReq.authUser, session: authReq.authSession };\n}\n\n/**\n * Express adapter exports\n */\nexport const expressAdapter = {\n middleware,\n isAuthenticated,\n getAuthContext,\n};\n\n// Default export for convenience\nexport default expressAdapter;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/db.ts","../../src/lib/schema.ts","../../src/server/express.ts"],"sourcesContent":["import { drizzle } from \"drizzle-orm/libsql\";\nimport { createClient } from \"@libsql/client\";\nimport * as schema from \"./schema\";\n\nconst client = createClient({\n url: process.env.HYLE_DATABASE_URL!,\n authToken: process.env.HYLE_DATABASE_AUTH_TOKEN!,\n});\n\nexport const db = drizzle(client, { schema });\n","import { relations, sql } from \"drizzle-orm\";\nimport { sqliteTable, text, integer, index } from \"drizzle-orm/sqlite-core\";\n\nexport const user = sqliteTable(\"user\", {\n id: text(\"id\").primaryKey(),\n name: text(\"name\").notNull(),\n email: text(\"email\").notNull().unique(),\n emailVerified: integer(\"email_verified\", { mode: \"boolean\" })\n .default(false)\n .notNull(),\n image: text(\"image\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n});\n\nexport const session = sqliteTable(\n \"session\",\n {\n id: text(\"id\").primaryKey(),\n expiresAt: integer(\"expires_at\", { mode: \"timestamp_ms\" }).notNull(),\n token: text(\"token\").notNull().unique(),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n ipAddress: text(\"ip_address\"),\n userAgent: text(\"user_agent\"),\n userId: text(\"user_id\")\n .notNull()\n .references(() => user.id, { onDelete: \"cascade\" }),\n },\n (table) => [index(\"session_userId_idx\").on(table.userId)],\n);\n\nexport const account = sqliteTable(\n \"account\",\n {\n id: text(\"id\").primaryKey(),\n accountId: text(\"account_id\").notNull(),\n providerId: text(\"provider_id\").notNull(),\n userId: text(\"user_id\")\n .notNull()\n .references(() => user.id, { onDelete: \"cascade\" }),\n accessToken: text(\"access_token\"),\n refreshToken: text(\"refresh_token\"),\n idToken: text(\"id_token\"),\n accessTokenExpiresAt: integer(\"access_token_expires_at\", {\n mode: \"timestamp_ms\",\n }),\n refreshTokenExpiresAt: integer(\"refresh_token_expires_at\", {\n mode: \"timestamp_ms\",\n }),\n scope: text(\"scope\"),\n password: text(\"password\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n },\n (table) => [index(\"account_userId_idx\").on(table.userId)],\n);\n\nexport const verification = sqliteTable(\n \"verification\",\n {\n id: text(\"id\").primaryKey(),\n identifier: text(\"identifier\").notNull(),\n value: text(\"value\").notNull(),\n expiresAt: integer(\"expires_at\", { mode: \"timestamp_ms\" }).notNull(),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" })\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)\n .$onUpdate(() => /* @__PURE__ */ new Date())\n .notNull(),\n },\n (table) => [index(\"verification_identifier_idx\").on(table.identifier)],\n);\n\nexport const userRelations = relations(user, ({ many }) => ({\n sessions: many(session),\n accounts: many(account),\n}));\n\nexport const sessionRelations = relations(session, ({ one }) => ({\n user: one(user, {\n fields: [session.userId],\n references: [user.id],\n }),\n}));\n\nexport const accountRelations = relations(account, ({ one }) => ({\n user: one(user, {\n fields: [account.userId],\n references: [user.id],\n }),\n}));\n","import type { Request, Response, NextFunction } from \"express\";\nimport type { Session, User } from \"better-auth\";\nimport { db } from \"../lib/db\";\nimport { session as sessionTable, user as userTable } from \"../lib/schema\";\nimport { eq, and, gt } from \"drizzle-orm\";\n\n/**\n * Extended Request with authenticated user context.\n */\nexport interface AuthenticatedRequest extends Request {\n /**\n * The authenticated user. Available on all authenticated routes.\n */\n authUser: User;\n\n /**\n * The current session. Available on all authenticated routes.\n */\n authSession: Session;\n\n /**\n * @deprecated Use authUser instead\n */\n user?: User;\n\n /**\n * @deprecated Use authSession instead\n */\n session?: Session;\n}\n\nexport interface MiddlewareOptions {\n /**\n * Routes that don't require authentication.\n * Supports exact paths and patterns with wildcards.\n * @example [\"/health\", \"/public/*\", \"/api/webhooks/*\"]\n */\n unauthenticatedRoutes?: string[];\n\n /**\n * If true, verify session against the database.\n * Use for service-to-service calls where headers can't be implicitly trusted.\n * @default false\n */\n verifySession?: boolean;\n\n /**\n * Whether to require authentication for non-unauthenticated routes.\n * Returns 401 on missing/invalid session.\n * @default true\n */\n required?: boolean;\n}\n\n/**\n * Check if a path matches any of the unauthenticated route patterns.\n */\nfunction isUnauthenticatedRoute(path: string, patterns: string[]): boolean {\n for (const pattern of patterns) {\n if (pattern === path) return true;\n\n // Handle wildcard patterns like \"/public/*\"\n if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -2);\n if (path === prefix || path.startsWith(prefix + \"/\")) {\n return true;\n }\n }\n\n // Handle double wildcard patterns like \"/api/**/health\"\n if (pattern.includes(\"**\")) {\n const regex = new RegExp(\n \"^\" + pattern.replace(/\\*\\*/g, \".*\").replace(/\\*/g, \"[^/]*\") + \"$\"\n );\n if (regex.test(path)) return true;\n }\n }\n return false;\n}\n\n/**\n * Express middleware for session verification.\n * \n * @example\n * // Basic usage - trust headers from BFF\n * app.use(middleware());\n * \n * @example\n * // With DB verification for service-to-service calls\n * app.use(middleware({ verifySession: true }));\n * \n * @example\n * // With unauthenticated routes\n * app.use(middleware({\n * unauthenticatedRoutes: [\"/health\", \"/public/*\", \"/api/webhooks/*\"],\n * verifySession: true\n * }));\n */\nexport const middleware = (options: MiddlewareOptions = {}) => {\n const {\n unauthenticatedRoutes = [],\n verifySession = false,\n required = true\n } = options;\n\n return async (req: Request, res: Response, next: NextFunction) => {\n const authReq = req as AuthenticatedRequest;\n\n // Check if route is unauthenticated\n if (isUnauthenticatedRoute(req.path, unauthenticatedRoutes)) {\n return next();\n }\n\n try {\n const userHeader = req.headers[\"x-hyle-user\"];\n const sessionHeader = req.headers[\"x-hyle-session\"];\n\n // No session header provided\n if (!sessionHeader || typeof sessionHeader !== \"string\") {\n if (required) {\n return res.status(401).json({ error: \"Unauthorized\" });\n }\n return next();\n }\n\n const sessionData = JSON.parse(\n Buffer.from(sessionHeader, \"base64\").toString(\"utf-8\")\n );\n\n // Verify session against DB if required\n if (verifySession) {\n const result = await db\n .select({\n session: sessionTable,\n user: userTable,\n })\n .from(sessionTable)\n .innerJoin(userTable, eq(sessionTable.userId, userTable.id))\n .where(\n and(\n eq(sessionTable.id, sessionData.id),\n gt(sessionTable.expiresAt, new Date())\n )\n )\n .limit(1);\n\n if (result.length === 0) {\n if (required) {\n return res.status(401).json({ error: \"Invalid or expired session\" });\n }\n return next();\n }\n\n // Inject auth context\n authReq.authUser = result[0].user as User;\n authReq.authSession = result[0].session as Session;\n\n // Keep deprecated properties for backwards compatibility\n authReq.user = authReq.authUser;\n authReq.session = authReq.authSession;\n\n return next();\n }\n\n // Trust headers mode (for internal BFF calls)\n let userData: User | undefined;\n\n if (userHeader && typeof userHeader === \"string\") {\n userData = JSON.parse(\n Buffer.from(userHeader, \"base64\").toString(\"utf-8\")\n );\n }\n\n if (!userData && required) {\n return res.status(401).json({ error: \"Unauthorized\" });\n }\n\n if (userData) {\n // Inject auth context\n authReq.authUser = userData;\n authReq.authSession = sessionData as Session;\n\n // Keep deprecated properties for backwards compatibility\n authReq.user = authReq.authUser;\n authReq.session = authReq.authSession;\n }\n\n next();\n } catch (error) {\n console.error(\"[hyle] Auth middleware error:\", error);\n if (required) {\n return res.status(401).json({ error: \"Authentication failed\" });\n }\n next();\n }\n };\n};\n\n/**\n * Type guard to check if request is authenticated.\n */\nexport function isAuthenticated(req: Request): req is AuthenticatedRequest {\n return !!(req as AuthenticatedRequest).authUser;\n}\n\n/**\n * Helper to get auth context from request.\n * Throws if not authenticated.\n */\nexport function getAuthContext(req: Request): { user: User; session: Session } {\n const authReq = req as AuthenticatedRequest;\n if (!authReq.authUser || !authReq.authSession) {\n throw new Error(\"Request is not authenticated\");\n }\n return { user: authReq.authUser, session: authReq.authSession };\n}\n\n/**\n * Express adapter exports\n */\nexport const expressAdapter = {\n middleware,\n isAuthenticated,\n getAuthContext,\n};\n\n// Default export for convenience\nexport default expressAdapter;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hylekit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Distributed BetterAuth library for SvelteKit and Next.js with Google OAuth and Turso",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -55,9 +55,10 @@
|
|
|
55
55
|
"author": "",
|
|
56
56
|
"license": "ISC",
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@libsql/client": "^0.
|
|
58
|
+
"@libsql/client": "^0.17.0",
|
|
59
59
|
"better-auth": "^1.2.0",
|
|
60
|
-
"drizzle-orm": "^0.41.0"
|
|
60
|
+
"drizzle-orm": "^0.41.0",
|
|
61
|
+
"libsql": "^0.5.22"
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
63
64
|
"@sveltejs/kit": "^2.49.5",
|