@structcms/api 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +188 -0
- package/dist/index.cjs +1124 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +365 -0
- package/dist/index.d.ts +365 -0
- package/dist/index.js +1047 -0
- package/dist/index.js.map +1 -0
- package/dist/next/index.cjs +1114 -0
- package/dist/next/index.cjs.map +1 -0
- package/dist/next/index.d.cts +143 -0
- package/dist/next/index.d.ts +143 -0
- package/dist/next/index.js +1064 -0
- package/dist/next/index.js.map +1 -0
- package/dist/supabase/index.cjs +494 -0
- package/dist/supabase/index.cjs.map +1 -0
- package/dist/supabase/index.d.cts +18 -0
- package/dist/supabase/index.d.ts +18 -0
- package/dist/supabase/index.js +467 -0
- package/dist/supabase/index.js.map +1 -0
- package/dist/types-Zi0Iyow1.d.cts +204 -0
- package/dist/types-Zi0Iyow1.d.ts +204 -0
- package/package.json +78 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/media/resolve.ts","../../src/delivery/handlers.ts","../../src/media/types.ts","../../src/media/handlers.ts","../../src/utils/sanitize.ts","../../src/utils/slug.ts","../../src/storage/handlers.ts","../../src/next/factories.ts","../../src/auth/handlers.ts","../../src/next/auth-factories.ts"],"sourcesContent":["import type { PageSection } from '../storage/types';\nimport type { MediaAdapter } from './types';\n\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Convention: fields ending with these suffixes are treated as media references\n */\nconst MEDIA_FIELD_SUFFIXES = ['_image', '_media', '_photo', '_thumbnail', '_avatar', '_icon'];\n\n/**\n * Checks if a field name is a media reference field by convention\n */\nfunction isMediaField(fieldName: string): boolean {\n const lower = fieldName.toLowerCase();\n if (\n lower === 'image' ||\n lower === 'media' ||\n lower === 'photo' ||\n lower === 'thumbnail' ||\n lower === 'avatar' ||\n lower === 'icon'\n ) {\n return true;\n }\n return MEDIA_FIELD_SUFFIXES.some((suffix) => lower.endsWith(suffix));\n}\n\n/**\n * Checks if a value looks like a media ID (UUID format)\n */\nfunction isMediaId(value: unknown): value is string {\n return typeof value === 'string' && UUID_PATTERN.test(value);\n}\n\n/**\n * Resolves media references in a single data object.\n * Walks all fields and replaces media IDs with public URLs.\n * Returns a new object with resolved references.\n */\nasync function resolveDataObject(\n data: Record<string, unknown>,\n adapter: MediaAdapter\n): Promise<Record<string, unknown>> {\n const resolved: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(data)) {\n if (isMediaField(key) && isMediaId(value)) {\n const media = await adapter.getMedia(value);\n resolved[key] = media ? media.url : null;\n } else if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n resolved[key] = await resolveDataObject(value as Record<string, unknown>, adapter);\n } else {\n resolved[key] = value;\n }\n }\n\n return resolved;\n}\n\n/**\n * Resolves media references in page sections.\n * Walks section data fields and replaces media IDs (UUIDs) with public URLs.\n *\n * Convention: Fields named \"image\", \"media\", \"photo\", \"thumbnail\", \"avatar\", \"icon\"\n * or ending with \"_image\", \"_media\", etc. are treated as media references.\n *\n * Missing media is resolved to null.\n */\nexport async function resolveMediaReferences(\n sections: PageSection[],\n adapter: MediaAdapter\n): Promise<PageSection[]> {\n const resolved: PageSection[] = [];\n\n for (const section of sections) {\n const resolvedData = await resolveDataObject(section.data, adapter);\n resolved.push({\n id: section.id,\n type: section.type,\n data: resolvedData,\n });\n }\n\n return resolved;\n}\n","import { resolveMediaReferences } from '../media/resolve';\nimport type { MediaAdapter } from '../media/types';\nimport type { Navigation, Page, StorageAdapter } from '../storage/types';\nimport type { ListPagesOptions, NavigationResponse, PageResponse } from './types';\n\n/**\n * Maps internal Page type to PageResponse for delivery API with resolved media references\n */\nasync function toPageResponse(page: Page, mediaAdapter: MediaAdapter): Promise<PageResponse> {\n const resolvedSections = await resolveMediaReferences(page.sections, mediaAdapter);\n\n return {\n id: page.id,\n slug: page.slug,\n pageType: page.pageType,\n title: page.title,\n sections: resolvedSections,\n meta: {\n createdAt: page.createdAt.toISOString(),\n updatedAt: page.updatedAt.toISOString(),\n },\n };\n}\n\n/**\n * Maps internal Navigation type to NavigationResponse for delivery API\n */\nfunction toNavigationResponse(navigation: Navigation): NavigationResponse {\n return {\n id: navigation.id,\n name: navigation.name,\n items: navigation.items,\n meta: {\n updatedAt: navigation.updatedAt.toISOString(),\n },\n };\n}\n\n/**\n * Handler for GET /api/cms/pages\n * Returns all pages with resolved media references, optionally filtered by pageType\n */\nexport async function handleListPages(\n adapter: StorageAdapter,\n mediaAdapter: MediaAdapter,\n options?: ListPagesOptions\n): Promise<PageResponse[]> {\n const pages = await adapter.listPages({\n pageType: options?.pageType,\n limit: options?.limit,\n offset: options?.offset,\n });\n\n const results: PageResponse[] = [];\n for (const page of pages) {\n results.push(await toPageResponse(page, mediaAdapter));\n }\n\n return results;\n}\n\n/**\n * Handler for GET /api/cms/pages/:slug\n * Returns a single page by slug with resolved media references, or null if not found\n */\nexport async function handleGetPageBySlug(\n adapter: StorageAdapter,\n mediaAdapter: MediaAdapter,\n slug: string\n): Promise<PageResponse | null> {\n const page = await adapter.getPage(slug);\n\n if (!page) {\n return null;\n }\n\n return toPageResponse(page, mediaAdapter);\n}\n\n/**\n * Handler for GET /api/cms/navigation/:name\n * Returns a navigation by name, or null if not found\n */\nexport async function handleGetNavigation(\n adapter: StorageAdapter,\n name: string\n): Promise<NavigationResponse | null> {\n const navigation = await adapter.getNavigation(name);\n\n if (!navigation) {\n return null;\n }\n\n return toNavigationResponse(navigation);\n}\n","/**\n * Allowed MIME types for media uploads\n */\nexport const ALLOWED_MIME_TYPES = [\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n 'image/svg+xml',\n] as const;\n\nexport type AllowedMimeType = (typeof ALLOWED_MIME_TYPES)[number];\n\n/**\n * Represents a media file stored in the CMS\n */\nexport interface MediaFile {\n id: string;\n filename: string;\n url: string;\n mimeType: string;\n size: number;\n createdAt: Date;\n}\n\n/**\n * Input for uploading a media file\n * data accepts ArrayBuffer for platform-agnostic binary data\n */\nexport interface UploadMediaInput {\n filename: string;\n mimeType: string;\n size: number;\n data: ArrayBuffer | Uint8Array;\n}\n\n/**\n * Filter options for listing media files\n */\nexport interface MediaFilter {\n limit?: number;\n offset?: number;\n mimeType?: string;\n}\n\n/**\n * Media adapter interface for file storage operations.\n * This interface is Supabase-agnostic to allow future portability.\n */\nexport interface MediaAdapter {\n /**\n * Upload a media file\n */\n upload(input: UploadMediaInput): Promise<MediaFile>;\n\n /**\n * Get a media file by ID\n */\n getMedia(id: string): Promise<MediaFile | null>;\n\n /**\n * List media files with optional filtering and pagination\n */\n listMedia(filter?: MediaFilter): Promise<MediaFile[]>;\n\n /**\n * Delete a media file by ID\n */\n deleteMedia(id: string): Promise<void>;\n}\n","import type { MediaAdapter, MediaFile, MediaFilter, UploadMediaInput } from './types';\nimport { ALLOWED_MIME_TYPES } from './types';\n\n/**\n * Error thrown when media validation fails\n */\nexport class MediaValidationError extends Error {\n constructor(\n message: string,\n public readonly code: string\n ) {\n super(message);\n this.name = 'MediaValidationError';\n }\n}\n\n/**\n * Validates that the MIME type is allowed\n */\nfunction validateMimeType(mimeType: string): void {\n const allowed = ALLOWED_MIME_TYPES as readonly string[];\n if (!allowed.includes(mimeType)) {\n throw new MediaValidationError(\n `Invalid file type: ${mimeType}. Allowed types: ${ALLOWED_MIME_TYPES.join(', ')}`,\n 'INVALID_MIME_TYPE'\n );\n }\n}\n\n/**\n * Handler for uploading a media file\n * Validates the file type and delegates to the adapter\n */\nexport async function handleUploadMedia(\n adapter: MediaAdapter,\n input: UploadMediaInput\n): Promise<MediaFile> {\n validateMimeType(input.mimeType);\n return adapter.upload(input);\n}\n\n/**\n * Handler for retrieving a media file by ID\n */\nexport async function handleGetMedia(adapter: MediaAdapter, id: string): Promise<MediaFile | null> {\n return adapter.getMedia(id);\n}\n\n/**\n * Handler for listing media files with optional filtering\n */\nexport async function handleListMedia(\n adapter: MediaAdapter,\n filter?: MediaFilter\n): Promise<MediaFile[]> {\n return adapter.listMedia(filter);\n}\n\n/**\n * Handler for deleting a media file\n */\nexport async function handleDeleteMedia(adapter: MediaAdapter, id: string): Promise<void> {\n return adapter.deleteMedia(id);\n}\n","import sanitizeHtml from 'sanitize-html';\nimport type { PageSection } from '../storage/types';\n\n/**\n * Allowed HTML tags for sanitization\n */\nconst ALLOWED_TAGS = [\n 'p',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'ul',\n 'ol',\n 'li',\n 'a',\n 'strong',\n 'em',\n 'br',\n 'blockquote',\n 'code',\n 'pre',\n 'img',\n];\n\n/**\n * Allowed HTML attributes per tag\n */\nconst ALLOWED_ATTRIBUTES: Record<string, string[]> = {\n a: ['href'],\n img: ['src', 'alt'],\n};\n\n/**\n * Sanitize-html configuration\n */\nconst SANITIZE_OPTIONS: sanitizeHtml.IOptions = {\n allowedTags: ALLOWED_TAGS,\n allowedAttributes: ALLOWED_ATTRIBUTES,\n disallowedTagsMode: 'discard',\n};\n\n/**\n * Sanitize a single string value using the configured allowlist\n */\nexport function sanitizeString(value: string): string {\n return sanitizeHtml(value, SANITIZE_OPTIONS);\n}\n\n/**\n * Recursively sanitize all string values in an unknown data structure\n */\nexport function sanitizeValue(value: unknown): unknown {\n if (typeof value === 'string') {\n return sanitizeString(value);\n }\n\n if (Array.isArray(value)) {\n return value.map(sanitizeValue);\n }\n\n if (value !== null && typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n result[key] = sanitizeValue(val);\n }\n return result;\n }\n\n // number, boolean, null, undefined — pass through\n return value;\n}\n\n/**\n * Sanitize all string values in page sections' data\n * Returns a new array with sanitized sections (does not mutate input)\n */\nexport function sanitizeSectionData(sections: PageSection[]): PageSection[] {\n return sections.map((section) => ({\n ...section,\n data: sanitizeValue(section.data) as Record<string, unknown>,\n }));\n}\n","/**\n * Character replacements for German umlauts and special characters\n */\nconst UMLAUT_MAP: Record<string, string> = {\n ä: 'ae',\n ö: 'oe',\n ü: 'ue',\n Ä: 'Ae',\n Ö: 'Oe',\n Ü: 'Ue',\n ß: 'ss',\n};\n\n/**\n * Generates a URL-safe slug from a title string.\n *\n * @param title - The title to convert to a slug\n * @returns A URL-safe slug\n *\n * @example\n * generateSlug('Hello World') // 'hello-world'\n * generateSlug('Über uns') // 'ueber-uns'\n * generateSlug(' Multiple Spaces ') // 'multiple-spaces'\n */\nexport function generateSlug(title: string): string {\n let slug = title.toLowerCase().trim();\n\n // Replace German umlauts\n for (const [umlaut, replacement] of Object.entries(UMLAUT_MAP)) {\n slug = slug.replace(new RegExp(umlaut, 'g'), replacement.toLowerCase());\n }\n\n // Replace spaces and underscores with hyphens\n slug = slug.replace(/[\\s_]+/g, '-');\n\n // Remove all non-alphanumeric characters except hyphens\n slug = slug.replace(/[^a-z0-9-]/g, '');\n\n // Remove multiple consecutive hyphens\n slug = slug.replace(/-+/g, '-');\n\n // Remove leading and trailing hyphens\n slug = slug.replace(/^-+|-+$/g, '');\n\n return slug;\n}\n\n/**\n * Ensures a slug is unique by appending a numeric suffix if needed.\n *\n * @param slug - The base slug to make unique\n * @param existingSlugs - Array of existing slugs to check against\n * @returns A unique slug\n *\n * @example\n * ensureUniqueSlug('hello', ['hello', 'world']) // 'hello-1'\n * ensureUniqueSlug('hello', ['hello', 'hello-1']) // 'hello-2'\n * ensureUniqueSlug('new', ['hello', 'world']) // 'new'\n */\nexport function ensureUniqueSlug(slug: string, existingSlugs: string[]): string {\n if (!existingSlugs.includes(slug)) {\n return slug;\n }\n\n let counter = 1;\n let uniqueSlug = `${slug}-${counter}`;\n\n while (existingSlugs.includes(uniqueSlug)) {\n counter++;\n uniqueSlug = `${slug}-${counter}`;\n }\n\n return uniqueSlug;\n}\n","import { sanitizeSectionData } from '../utils/sanitize';\nimport { ensureUniqueSlug, generateSlug } from '../utils/slug';\nimport type {\n CreateNavigationInput,\n CreatePageInput,\n Navigation,\n Page,\n StorageAdapter,\n UpdateNavigationInput,\n UpdatePageInput,\n} from './types';\n\n/**\n * Error thrown when storage validation fails\n */\nexport class StorageValidationError extends Error {\n constructor(\n message: string,\n public readonly code: string\n ) {\n super(message);\n this.name = 'StorageValidationError';\n }\n}\n\n/**\n * Handler for creating a new page\n * Generates a slug from the title if not provided and ensures uniqueness\n */\nexport async function handleCreatePage(\n adapter: StorageAdapter,\n input: CreatePageInput\n): Promise<Page> {\n if (!input.title.trim()) {\n throw new StorageValidationError('Page title must not be empty', 'EMPTY_TITLE');\n }\n\n const slug = input.slug?.trim() || generateSlug(input.title);\n\n if (!slug) {\n throw new StorageValidationError(\n 'Could not generate a valid slug from the provided title',\n 'INVALID_SLUG'\n );\n }\n\n // Ensure slug uniqueness\n const existingPages = await adapter.listPages();\n const existingSlugs = existingPages.map((p) => p.slug);\n const uniqueSlug = ensureUniqueSlug(slug, existingSlugs);\n\n const sanitizedSections = input.sections ? sanitizeSectionData(input.sections) : undefined;\n\n return adapter.createPage({\n ...input,\n slug: uniqueSlug,\n sections: sanitizedSections,\n });\n}\n\n/**\n * Handler for updating an existing page\n */\nexport async function handleUpdatePage(\n adapter: StorageAdapter,\n input: UpdatePageInput\n): Promise<Page> {\n if (!input.id.trim()) {\n throw new StorageValidationError('Page ID must not be empty', 'EMPTY_ID');\n }\n\n if (input.title !== undefined && !input.title.trim()) {\n throw new StorageValidationError('Page title must not be empty', 'EMPTY_TITLE');\n }\n\n // If slug is being updated, ensure uniqueness\n if (input.slug !== undefined) {\n const slug = input.slug.trim();\n if (!slug) {\n throw new StorageValidationError('Page slug must not be empty', 'EMPTY_SLUG');\n }\n\n const existingPages = await adapter.listPages();\n const existingSlugs = existingPages.filter((p) => p.id !== input.id).map((p) => p.slug);\n\n if (existingSlugs.includes(slug)) {\n throw new StorageValidationError(`Slug \"${slug}\" is already in use`, 'DUPLICATE_SLUG');\n }\n }\n\n const sanitizedInput = input.sections\n ? { ...input, sections: sanitizeSectionData(input.sections) }\n : input;\n\n return adapter.updatePage(sanitizedInput);\n}\n\n/**\n * Handler for deleting a page by ID\n */\nexport async function handleDeletePage(adapter: StorageAdapter, id: string): Promise<void> {\n if (!id.trim()) {\n throw new StorageValidationError('Page ID must not be empty', 'EMPTY_ID');\n }\n\n return adapter.deletePage(id);\n}\n\n/**\n * Handler for creating a new navigation\n * Validates that the name is non-empty and unique\n */\nexport async function handleCreateNavigation(\n adapter: StorageAdapter,\n input: CreateNavigationInput\n): Promise<Navigation> {\n if (!input.name.trim()) {\n throw new StorageValidationError('Navigation name must not be empty', 'EMPTY_NAME');\n }\n\n // Ensure name uniqueness\n const existingNavigations = await adapter.listNavigations();\n const existingNames = existingNavigations.map((n) => n.name);\n\n if (existingNames.includes(input.name.trim())) {\n throw new StorageValidationError(\n `Navigation name \"${input.name.trim()}\" is already in use`,\n 'DUPLICATE_NAME'\n );\n }\n\n return adapter.createNavigation(input);\n}\n\n/**\n * Handler for updating an existing navigation\n */\nexport async function handleUpdateNavigation(\n adapter: StorageAdapter,\n input: UpdateNavigationInput\n): Promise<Navigation> {\n if (!input.id.trim()) {\n throw new StorageValidationError('Navigation ID must not be empty', 'EMPTY_ID');\n }\n\n // If name is being updated, ensure uniqueness\n if (input.name !== undefined) {\n const name = input.name.trim();\n if (!name) {\n throw new StorageValidationError('Navigation name must not be empty', 'EMPTY_NAME');\n }\n\n const existingNavigations = await adapter.listNavigations();\n const existingNames = existingNavigations.filter((n) => n.id !== input.id).map((n) => n.name);\n\n if (existingNames.includes(name)) {\n throw new StorageValidationError(\n `Navigation name \"${name}\" is already in use`,\n 'DUPLICATE_NAME'\n );\n }\n }\n\n return adapter.updateNavigation(input);\n}\n\n/**\n * Handler for deleting a navigation by ID\n */\nexport async function handleDeleteNavigation(adapter: StorageAdapter, id: string): Promise<void> {\n if (!id.trim()) {\n throw new StorageValidationError('Navigation ID must not be empty', 'EMPTY_ID');\n }\n\n return adapter.deleteNavigation(id);\n}\n","import { handleGetPageBySlug, handleListPages } from '../delivery';\nimport {\n MediaValidationError,\n handleDeleteMedia,\n handleGetMedia,\n handleListMedia,\n handleUploadMedia,\n} from '../media';\nimport type { MediaAdapter, UploadMediaInput } from '../media';\nimport {\n StorageValidationError,\n handleCreateNavigation,\n handleCreatePage,\n handleDeleteNavigation,\n handleDeletePage,\n handleUpdateNavigation,\n handleUpdatePage,\n} from '../storage';\nimport type {\n CreateNavigationInput,\n CreatePageInput,\n Navigation,\n NavigationItem,\n Page,\n PageSection,\n StorageAdapter,\n UpdateNavigationInput,\n} from '../storage';\n\ntype JsonRecord = Record<string, unknown>;\ntype ParsedPageSection = {\n id?: string;\n type: string;\n data: JsonRecord;\n};\n\ntype ParsedUpdatePagePatch = {\n slug?: string;\n pageType?: string;\n title?: string;\n sections?: ParsedPageSection[];\n};\n\ninterface RequestLike {\n json(): Promise<unknown>;\n formData(): Promise<FormDataLike>;\n}\n\ninterface FormDataLike {\n get(name: string): unknown;\n}\n\ninterface FileLike {\n name: string;\n type: string;\n size: number;\n arrayBuffer(): Promise<ArrayBuffer>;\n}\n\ninterface ResponseLike {\n status: number;\n json(): Promise<unknown>;\n}\n\ninterface ResponseConstructorLike {\n new (body?: string, init?: { status?: number; headers?: Record<string, string> }): ResponseLike;\n}\n\ninterface RouteContext<TParams extends Record<string, string | string[]>> {\n params: TParams | Promise<TParams>;\n}\n\nexport interface NextPagesRouteConfig {\n storageAdapter: StorageAdapter;\n mediaAdapter: MediaAdapter;\n}\n\nexport interface NextPageBySlugRouteConfig {\n storageAdapter: StorageAdapter;\n mediaAdapter: MediaAdapter;\n}\n\nexport interface NextPageByIdRouteConfig {\n storageAdapter: StorageAdapter;\n}\n\nexport interface NextMediaRouteConfig {\n mediaAdapter: MediaAdapter;\n}\n\nexport interface NextMediaByIdRouteConfig {\n mediaAdapter: MediaAdapter;\n}\n\nexport interface NextNavigationRouteConfig {\n storageAdapter: StorageAdapter;\n}\n\nexport interface NextNavigationByIdRouteConfig {\n storageAdapter: StorageAdapter;\n}\n\nfunction getResponseConstructor(): ResponseConstructorLike {\n const responseCtor = (globalThis as typeof globalThis & { Response?: ResponseConstructorLike })\n .Response;\n\n if (!responseCtor) {\n throw new Error('Response constructor is not available in this runtime');\n }\n\n return responseCtor;\n}\n\nfunction jsonResponse(data: unknown, status = 200): ResponseLike {\n const ResponseCtor = getResponseConstructor();\n\n return new ResponseCtor(JSON.stringify(data), {\n status,\n headers: {\n 'content-type': 'application/json',\n },\n });\n}\n\nfunction getErrorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n\n return 'Unknown error';\n}\n\nfunction errorResponse(error: unknown, fallbackStatus = 500): ResponseLike {\n if (\n error instanceof StorageValidationError ||\n error instanceof MediaValidationError ||\n error instanceof SyntaxError\n ) {\n return jsonResponse({ error: getErrorMessage(error) }, 400);\n }\n\n return jsonResponse({ error: getErrorMessage(error) }, fallbackStatus);\n}\n\nasync function resolveParams<TParams extends Record<string, string | string[]>>(\n context: RouteContext<TParams>\n): Promise<TParams> {\n return context.params;\n}\n\nfunction normalizeSlug(slug: string | string[]): string {\n return Array.isArray(slug) ? slug.join('/') : slug;\n}\n\nfunction asObject(value: unknown): JsonRecord | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return null;\n }\n\n return value as JsonRecord;\n}\n\nfunction isFileLike(value: unknown): value is FileLike {\n if (!value || typeof value !== 'object') {\n return false;\n }\n\n const candidate = value as {\n name?: unknown;\n type?: unknown;\n size?: unknown;\n arrayBuffer?: unknown;\n };\n\n return (\n typeof candidate.name === 'string' &&\n typeof candidate.type === 'string' &&\n typeof candidate.size === 'number' &&\n typeof candidate.arrayBuffer === 'function'\n );\n}\n\nfunction parseStringField(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction parsePageSections(value: unknown): ParsedPageSection[] | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (!Array.isArray(value)) {\n return undefined;\n }\n\n const sections: ParsedPageSection[] = [];\n\n for (const entry of value) {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {\n return undefined;\n }\n\n const sectionCandidate = entry as {\n id?: unknown;\n type?: unknown;\n data?: unknown;\n };\n\n if (\n (sectionCandidate.id !== undefined && typeof sectionCandidate.id !== 'string') ||\n typeof sectionCandidate.type !== 'string' ||\n !sectionCandidate.data ||\n typeof sectionCandidate.data !== 'object' ||\n Array.isArray(sectionCandidate.data)\n ) {\n return undefined;\n }\n\n sections.push({\n id: sectionCandidate.id,\n type: sectionCandidate.type,\n data: sectionCandidate.data as JsonRecord,\n });\n }\n\n return sections;\n}\n\nfunction normalizePageSections(\n sections: ParsedPageSection[] | undefined,\n existingSections: PageSection[] = []\n): PageSection[] | undefined {\n if (!sections) {\n return undefined;\n }\n\n return sections.map((section, index) => ({\n id: section.id ?? existingSections[index]?.id ?? `${section.type}-${index + 1}`,\n type: section.type,\n data: section.data,\n }));\n}\n\nfunction parseNavigationItems(value: unknown): NavigationItem[] | undefined {\n if (!Array.isArray(value)) {\n return undefined;\n }\n\n const items: NavigationItem[] = [];\n\n for (const entry of value) {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {\n return undefined;\n }\n\n const itemCandidate = entry as {\n label?: unknown;\n href?: unknown;\n children?: unknown;\n };\n\n if (typeof itemCandidate.label !== 'string' || typeof itemCandidate.href !== 'string') {\n return undefined;\n }\n\n const children =\n itemCandidate.children === undefined\n ? undefined\n : parseNavigationItems(itemCandidate.children);\n\n if (itemCandidate.children !== undefined && !children) {\n return undefined;\n }\n\n items.push({\n label: itemCandidate.label,\n href: itemCandidate.href,\n children,\n });\n }\n\n return items;\n}\n\nfunction parseCreatePageInput(payload: JsonRecord): CreatePageInput | null {\n const pageType = parseStringField(payload.pageType);\n const title = parseStringField(payload.title);\n const slug = parseStringField(payload.slug);\n const sections = normalizePageSections(parsePageSections(payload.sections));\n\n if (!pageType || !title) {\n return null;\n }\n\n if (payload.sections !== undefined && !sections) {\n return null;\n }\n\n return {\n pageType,\n title,\n slug,\n sections,\n };\n}\n\nfunction parseUpdatePagePatch(payload: JsonRecord): ParsedUpdatePagePatch | null {\n const slug = parseStringField(payload.slug);\n const pageType = parseStringField(payload.pageType);\n const title = parseStringField(payload.title);\n const sections = parsePageSections(payload.sections);\n\n if (payload.slug !== undefined && !slug) {\n return null;\n }\n if (payload.pageType !== undefined && !pageType) {\n return null;\n }\n if (payload.title !== undefined && !title) {\n return null;\n }\n if (payload.sections !== undefined && !sections) {\n return null;\n }\n\n return {\n slug,\n pageType,\n title,\n sections,\n };\n}\n\nfunction parseCreateNavigationInput(payload: JsonRecord): CreateNavigationInput | null {\n const name = parseStringField(payload.name);\n const items = parseNavigationItems(payload.items);\n\n if (!name || !items) {\n return null;\n }\n\n return {\n name,\n items,\n };\n}\n\nfunction parseUpdateNavigationPatch(payload: JsonRecord): Omit<UpdateNavigationInput, 'id'> | null {\n const name = parseStringField(payload.name);\n const items = payload.items === undefined ? undefined : parseNavigationItems(payload.items);\n\n if (payload.name !== undefined && !name) {\n return null;\n }\n if (payload.items !== undefined && !items) {\n return null;\n }\n\n return {\n name,\n items,\n };\n}\n\nfunction toPageResponse(page: Page): Record<string, unknown> {\n return {\n id: page.id,\n slug: page.slug,\n pageType: page.pageType,\n title: page.title,\n sections: page.sections,\n meta: {\n createdAt: page.createdAt.toISOString(),\n updatedAt: page.updatedAt.toISOString(),\n },\n };\n}\n\nfunction toNavigationResponse(navigation: Navigation): Record<string, unknown> {\n return {\n id: navigation.id,\n name: navigation.name,\n items: navigation.items,\n meta: {\n updatedAt: navigation.updatedAt.toISOString(),\n },\n };\n}\n\nexport function createNextPagesRoute(config: NextPagesRouteConfig) {\n return {\n GET: async (_request: RequestLike): Promise<ResponseLike> => {\n try {\n const pages = await handleListPages(config.storageAdapter, config.mediaAdapter);\n return jsonResponse(pages);\n } catch (error) {\n return errorResponse(error);\n }\n },\n POST: async (request: RequestLike): Promise<ResponseLike> => {\n try {\n const payload = asObject(await request.json());\n if (!payload) {\n return jsonResponse({ error: 'Request body must be an object' }, 400);\n }\n\n const createInput = parseCreatePageInput(payload);\n if (!createInput) {\n return jsonResponse({ error: 'Invalid page payload' }, 400);\n }\n\n const page = await handleCreatePage(config.storageAdapter, createInput);\n return jsonResponse(page, 201);\n } catch (error) {\n return errorResponse(error, 400);\n }\n },\n };\n}\n\nexport function createNextPageBySlugRoute(config: NextPageBySlugRouteConfig) {\n return {\n GET: async (\n _request: RequestLike,\n context: RouteContext<{ slug: string | string[] }>\n ): Promise<ResponseLike> => {\n try {\n const { slug } = await resolveParams(context);\n const page = await handleGetPageBySlug(\n config.storageAdapter,\n config.mediaAdapter,\n normalizeSlug(slug)\n );\n\n if (!page) {\n return jsonResponse({ error: 'Page not found' }, 404);\n }\n\n return jsonResponse(page);\n } catch (error) {\n return errorResponse(error);\n }\n },\n PUT: async (\n request: RequestLike,\n context: RouteContext<{ slug: string | string[] }>\n ): Promise<ResponseLike> => {\n try {\n const { slug } = await resolveParams(context);\n const normalizedSlug = normalizeSlug(slug);\n const existingPage = await handleGetPageBySlug(\n config.storageAdapter,\n config.mediaAdapter,\n normalizedSlug\n );\n\n if (!existingPage) {\n return jsonResponse({ error: 'Page not found' }, 404);\n }\n\n const payload = asObject(await request.json());\n if (!payload) {\n return jsonResponse({ error: 'Request body must be an object' }, 400);\n }\n\n const pagePatch = parseUpdatePagePatch(payload);\n if (!pagePatch) {\n return jsonResponse({ error: 'Invalid page payload' }, 400);\n }\n\n const normalizedSections = normalizePageSections(pagePatch.sections, existingPage.sections);\n\n const page = await handleUpdatePage(config.storageAdapter, {\n ...pagePatch,\n sections: normalizedSections,\n id: existingPage.id,\n });\n\n return jsonResponse(toPageResponse(page));\n } catch (error) {\n return errorResponse(error, 400);\n }\n },\n DELETE: async (\n _request: RequestLike,\n context: RouteContext<{ slug: string | string[] }>\n ): Promise<ResponseLike> => {\n try {\n const { slug } = await resolveParams(context);\n const existingPage = await handleGetPageBySlug(\n config.storageAdapter,\n config.mediaAdapter,\n normalizeSlug(slug)\n );\n\n if (!existingPage) {\n return jsonResponse({ error: 'Page not found' }, 404);\n }\n\n await handleDeletePage(config.storageAdapter, existingPage.id);\n return jsonResponse({ success: true });\n } catch (error) {\n return errorResponse(error);\n }\n },\n };\n}\n\nexport function createNextPageByIdRoute(config: NextPageByIdRouteConfig) {\n return {\n GET: async (\n _request: RequestLike,\n context: RouteContext<{ id: string }>\n ): Promise<ResponseLike> => {\n try {\n const { id } = await resolveParams(context);\n const page = await config.storageAdapter.getPageById(id);\n\n if (!page) {\n return jsonResponse({ error: 'Page not found' }, 404);\n }\n\n return jsonResponse(toPageResponse(page));\n } catch (error) {\n return errorResponse(error);\n }\n },\n PUT: async (\n request: RequestLike,\n context: RouteContext<{ id: string }>\n ): Promise<ResponseLike> => {\n try {\n const { id } = await resolveParams(context);\n const existingPage = await config.storageAdapter.getPageById(id);\n\n if (!existingPage) {\n return jsonResponse({ error: 'Page not found' }, 404);\n }\n\n const payload = asObject(await request.json());\n if (!payload) {\n return jsonResponse({ error: 'Request body must be an object' }, 400);\n }\n\n const pagePatch = parseUpdatePagePatch(payload);\n if (!pagePatch) {\n return jsonResponse({ error: 'Invalid page payload' }, 400);\n }\n\n const normalizedSections = normalizePageSections(pagePatch.sections, existingPage.sections);\n\n const page = await handleUpdatePage(config.storageAdapter, {\n ...pagePatch,\n sections: normalizedSections,\n id: existingPage.id,\n });\n\n return jsonResponse(toPageResponse(page));\n } catch (error) {\n return errorResponse(error, 400);\n }\n },\n DELETE: async (\n _request: RequestLike,\n context: RouteContext<{ id: string }>\n ): Promise<ResponseLike> => {\n try {\n const { id } = await resolveParams(context);\n const existingPage = await config.storageAdapter.getPageById(id);\n\n if (!existingPage) {\n return jsonResponse({ error: 'Page not found' }, 404);\n }\n\n await handleDeletePage(config.storageAdapter, existingPage.id);\n return jsonResponse({ success: true });\n } catch (error) {\n return errorResponse(error);\n }\n },\n };\n}\n\nexport function createNextMediaRoute(config: NextMediaRouteConfig) {\n return {\n GET: async (_request: RequestLike): Promise<ResponseLike> => {\n try {\n const media = await handleListMedia(config.mediaAdapter);\n return jsonResponse(media);\n } catch (error) {\n return errorResponse(error);\n }\n },\n POST: async (request: RequestLike): Promise<ResponseLike> => {\n try {\n const formData = await request.formData();\n const file = formData.get('file');\n\n if (!isFileLike(file)) {\n return jsonResponse({ error: 'No file provided' }, 400);\n }\n\n const input: UploadMediaInput = {\n filename: file.name,\n mimeType: file.type,\n size: file.size,\n data: await file.arrayBuffer(),\n };\n\n const mediaFile = await handleUploadMedia(config.mediaAdapter, input);\n return jsonResponse(mediaFile, 201);\n } catch (error) {\n return errorResponse(error, 400);\n }\n },\n };\n}\n\nexport function createNextMediaByIdRoute(config: NextMediaByIdRouteConfig) {\n return {\n GET: async (\n _request: RequestLike,\n context: RouteContext<{ id: string }>\n ): Promise<ResponseLike> => {\n try {\n const { id } = await resolveParams(context);\n const media = await handleGetMedia(config.mediaAdapter, id);\n\n if (!media) {\n return jsonResponse({ error: 'Media not found' }, 404);\n }\n\n return jsonResponse(media);\n } catch (error) {\n return errorResponse(error);\n }\n },\n DELETE: async (\n _request: RequestLike,\n context: RouteContext<{ id: string }>\n ): Promise<ResponseLike> => {\n try {\n const { id } = await resolveParams(context);\n await handleDeleteMedia(config.mediaAdapter, id);\n return jsonResponse({ success: true });\n } catch (error) {\n return errorResponse(error);\n }\n },\n };\n}\n\nexport function createNextNavigationRoute(config: NextNavigationRouteConfig) {\n return {\n GET: async (_request: RequestLike): Promise<ResponseLike> => {\n try {\n const navigations = await config.storageAdapter.listNavigations();\n return jsonResponse(navigations.map(toNavigationResponse));\n } catch (error) {\n return errorResponse(error);\n }\n },\n POST: async (request: RequestLike): Promise<ResponseLike> => {\n try {\n const payload = asObject(await request.json());\n if (!payload) {\n return jsonResponse({ error: 'Request body must be an object' }, 400);\n }\n\n const createInput = parseCreateNavigationInput(payload);\n if (!createInput) {\n return jsonResponse({ error: 'Invalid navigation payload' }, 400);\n }\n\n const navigation = await handleCreateNavigation(config.storageAdapter, createInput);\n\n return jsonResponse(navigation, 201);\n } catch (error) {\n return errorResponse(error, 400);\n }\n },\n };\n}\n\nexport function createNextNavigationByIdRoute(config: NextNavigationByIdRouteConfig) {\n return {\n GET: async (\n _request: RequestLike,\n context: RouteContext<{ id: string }>\n ): Promise<ResponseLike> => {\n try {\n const { id } = await resolveParams(context);\n const navigation = await config.storageAdapter.getNavigationById(id);\n\n if (!navigation) {\n return jsonResponse({ error: 'Navigation not found' }, 404);\n }\n\n return jsonResponse(toNavigationResponse(navigation));\n } catch (error) {\n return errorResponse(error);\n }\n },\n PUT: async (\n request: RequestLike,\n context: RouteContext<{ id: string }>\n ): Promise<ResponseLike> => {\n try {\n const { id } = await resolveParams(context);\n const existingNavigation = await config.storageAdapter.getNavigationById(id);\n\n if (!existingNavigation) {\n return jsonResponse({ error: 'Navigation not found' }, 404);\n }\n\n const payload = asObject(await request.json());\n if (!payload) {\n return jsonResponse({ error: 'Request body must be an object' }, 400);\n }\n\n const navigationPatch = parseUpdateNavigationPatch(payload);\n if (!navigationPatch) {\n return jsonResponse({ error: 'Invalid navigation payload' }, 400);\n }\n\n const navigation = await handleUpdateNavigation(config.storageAdapter, {\n ...navigationPatch,\n id: existingNavigation.id,\n });\n\n return jsonResponse(navigation);\n } catch (error) {\n return errorResponse(error, 400);\n }\n },\n DELETE: async (\n _request: RequestLike,\n context: RouteContext<{ id: string }>\n ): Promise<ResponseLike> => {\n try {\n const { id } = await resolveParams(context);\n const existingNavigation = await config.storageAdapter.getNavigationById(id);\n\n if (!existingNavigation) {\n return jsonResponse({ error: 'Navigation not found' }, 404);\n }\n\n await handleDeleteNavigation(config.storageAdapter, existingNavigation.id);\n return jsonResponse({ success: true });\n } catch (error) {\n return errorResponse(error);\n }\n },\n };\n}\n","import type {\n AuthAdapter,\n SignInWithOAuthInput,\n SignInWithPasswordInput,\n VerifySessionInput,\n} from './types';\n\nexport class AuthValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'AuthValidationError';\n }\n}\n\nexport async function handleSignInWithOAuth(adapter: AuthAdapter, input: SignInWithOAuthInput) {\n if (!input.provider) {\n throw new AuthValidationError('Provider is required');\n }\n\n const validProviders = ['google', 'github', 'gitlab', 'azure', 'bitbucket'];\n if (!validProviders.includes(input.provider)) {\n throw new AuthValidationError(`Invalid provider. Must be one of: ${validProviders.join(', ')}`);\n }\n\n return await adapter.signInWithOAuth(input);\n}\n\n/**\n * Validate password strength\n * Requirements:\n * - Minimum 8 characters\n * - At least 3 of: uppercase, lowercase, numbers, special characters\n * - Not a common password\n */\nfunction validatePassword(password: string): void {\n if (password.length < 8) {\n throw new AuthValidationError('Password must be at least 8 characters');\n }\n\n const hasUpperCase = /[A-Z]/.test(password);\n const hasLowerCase = /[a-z]/.test(password);\n const hasNumbers = /\\d/.test(password);\n const hasSpecialChar = /[!@#$%^&*(),.?\":{}|<>_\\-+=\\[\\]\\\\/'`~]/.test(password);\n\n const complexityCount = [hasUpperCase, hasLowerCase, hasNumbers, hasSpecialChar].filter(\n Boolean\n ).length;\n\n if (complexityCount < 3) {\n throw new AuthValidationError(\n 'Password must contain at least 3 of: uppercase letters, lowercase letters, numbers, special characters'\n );\n }\n\n // Check against common passwords\n const commonPasswords = [\n 'password',\n 'password1',\n 'password123',\n '12345678',\n '123456789',\n 'qwerty',\n 'abc123',\n 'monkey',\n '1234567890',\n 'letmein',\n 'trustno1',\n 'dragon',\n 'baseball',\n 'iloveyou',\n 'master',\n 'sunshine',\n 'ashley',\n 'bailey',\n 'shadow',\n '123123',\n ];\n\n if (commonPasswords.includes(password.toLowerCase())) {\n throw new AuthValidationError('Password is too common. Please choose a stronger password.');\n }\n}\n\nexport async function handleSignInWithPassword(\n adapter: AuthAdapter,\n input: SignInWithPasswordInput\n) {\n if (!input.email || !input.password) {\n throw new AuthValidationError('Email and password are required');\n }\n\n if (!input.email.includes('@')) {\n throw new AuthValidationError('Invalid email format');\n }\n\n // Strengthen password policy (Fix #5)\n validatePassword(input.password);\n\n return await adapter.signInWithPassword(input);\n}\n\nexport async function handleSignOut(adapter: AuthAdapter, accessToken: string) {\n if (!accessToken) {\n throw new AuthValidationError('Access token is required');\n }\n\n return await adapter.signOut(accessToken);\n}\n\nexport async function handleVerifySession(adapter: AuthAdapter, input: VerifySessionInput) {\n if (!input.accessToken) {\n throw new AuthValidationError('Access token is required');\n }\n\n // Enforce token expiration (Fix #7)\n if (input.expiresAt) {\n const now = new Date();\n const expiresAt = input.expiresAt instanceof Date ? input.expiresAt : new Date(input.expiresAt);\n \n if (now > expiresAt) {\n throw new AuthValidationError('Token has expired');\n }\n }\n\n const user = await adapter.verifySession(input);\n\n if (!user) {\n throw new AuthValidationError('Invalid or expired token');\n }\n\n return user;\n}\n\nexport async function handleRefreshSession(adapter: AuthAdapter, refreshToken: string) {\n if (!refreshToken) {\n throw new AuthValidationError('Refresh token is required');\n }\n\n return await adapter.refreshSession(refreshToken);\n}\n\nexport async function handleGetCurrentUser(adapter: AuthAdapter, accessToken: string) {\n if (!accessToken) {\n throw new AuthValidationError('Access token is required');\n }\n\n return await adapter.getCurrentUser(accessToken);\n}\n","import {\n AuthValidationError,\n handleGetCurrentUser,\n handleRefreshSession,\n handleSignInWithOAuth,\n handleSignInWithPassword,\n handleSignOut,\n handleVerifySession,\n} from '../auth';\nimport type { AuthAdapter, SignInWithOAuthInput, SignInWithPasswordInput } from '../auth';\n\ninterface RequestLike {\n json(): Promise<unknown>;\n headers: {\n get(name: string): string | null | undefined;\n };\n}\n\ninterface ResponseLike {\n status: number;\n json(): Promise<unknown>;\n}\n\ninterface ResponseConstructorLike {\n new (body?: string, init?: { status?: number; headers?: Record<string, string> }): ResponseLike;\n json(data: unknown, init?: { status?: number; headers?: Record<string, string> }): ResponseLike;\n}\n\nexport interface NextAuthOAuthRouteConfig {\n authAdapter: AuthAdapter;\n}\n\nexport interface NextAuthSignInRouteConfig {\n authAdapter: AuthAdapter;\n}\n\nexport interface NextAuthSignOutRouteConfig {\n authAdapter: AuthAdapter;\n}\n\nexport interface NextAuthVerifyRouteConfig {\n authAdapter: AuthAdapter;\n}\n\nexport interface NextAuthRefreshRouteConfig {\n authAdapter: AuthAdapter;\n}\n\nexport interface NextAuthCurrentUserRouteConfig {\n authAdapter: AuthAdapter;\n}\n\nfunction extractBearerToken(request: RequestLike): string | null {\n const authHeader = request.headers.get('authorization') ?? request.headers.get('Authorization');\n if (!authHeader) return null;\n\n const parts = authHeader.split(' ');\n if (parts.length !== 2 || parts[0] !== 'Bearer') return null;\n\n return parts[1] ?? null;\n}\n\nexport function createNextAuthOAuthRoute(\n config: NextAuthOAuthRouteConfig,\n Response: ResponseConstructorLike\n) {\n return async function POST(request: RequestLike): Promise<ResponseLike> {\n try {\n const body = await request.json();\n const input = body as SignInWithOAuthInput;\n\n const result = await handleSignInWithOAuth(config.authAdapter, input);\n\n return Response.json({ data: result }, { status: 200 });\n } catch (err) {\n if (err instanceof AuthValidationError) {\n return Response.json(\n { error: { message: err.message, code: 'VALIDATION_ERROR' } },\n { status: 400 }\n );\n }\n\n const message = err instanceof Error ? err.message : 'OAuth initialization failed';\n return Response.json({ error: { message, code: 'OAUTH_ERROR' } }, { status: 500 });\n }\n };\n}\n\nexport function createNextAuthSignInRoute(\n config: NextAuthSignInRouteConfig,\n Response: ResponseConstructorLike\n) {\n return async function POST(request: RequestLike): Promise<ResponseLike> {\n try {\n const body = await request.json();\n const input = body as SignInWithPasswordInput;\n\n const session = await handleSignInWithPassword(config.authAdapter, input);\n\n return Response.json(session, { status: 200 });\n } catch (err) {\n if (err instanceof AuthValidationError) {\n return Response.json(\n { error: { message: err.message, code: 'VALIDATION_ERROR' } },\n { status: 400 }\n );\n }\n\n const message = err instanceof Error ? err.message : 'Sign in failed';\n return Response.json({ error: { message, code: 'AUTH_ERROR' } }, { status: 401 });\n }\n };\n}\n\nexport function createNextAuthSignOutRoute(\n config: NextAuthSignOutRouteConfig,\n Response: ResponseConstructorLike\n) {\n return async function POST(request: RequestLike): Promise<ResponseLike> {\n try {\n const token = extractBearerToken(request);\n if (!token) {\n return Response.json(\n { error: { message: 'No token provided', code: 'NO_TOKEN' } },\n { status: 401 }\n );\n }\n\n await handleSignOut(config.authAdapter, token);\n\n return Response.json({ message: 'Signed out successfully' }, { status: 200 });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Sign out failed';\n return Response.json({ error: { message, code: 'SIGNOUT_ERROR' } }, { status: 500 });\n }\n };\n}\n\nexport function createNextAuthVerifyRoute(\n config: NextAuthVerifyRouteConfig,\n Response: ResponseConstructorLike\n) {\n return async function POST(request: RequestLike): Promise<ResponseLike> {\n try {\n const token = extractBearerToken(request);\n if (!token) {\n return Response.json(\n { error: { message: 'No token provided', code: 'NO_TOKEN' } },\n { status: 401 }\n );\n }\n\n const user = await handleVerifySession(config.authAdapter, { accessToken: token });\n\n if (!user) {\n return Response.json(\n { error: { message: 'Invalid token', code: 'INVALID_TOKEN' } },\n { status: 401 }\n );\n }\n\n return Response.json(user, { status: 200 });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Verification failed';\n return Response.json({ error: { message, code: 'VERIFY_ERROR' } }, { status: 401 });\n }\n };\n}\n\nexport function createNextAuthRefreshRoute(\n config: NextAuthRefreshRouteConfig,\n Response: ResponseConstructorLike\n) {\n return async function POST(request: RequestLike): Promise<ResponseLike> {\n try {\n const body = await request.json();\n const { refreshToken } = body as { refreshToken: string };\n\n if (!refreshToken) {\n return Response.json(\n { error: { message: 'Refresh token required', code: 'NO_REFRESH_TOKEN' } },\n { status: 400 }\n );\n }\n\n const session = await handleRefreshSession(config.authAdapter, refreshToken);\n\n return Response.json(session, { status: 200 });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Refresh failed';\n return Response.json({ error: { message, code: 'REFRESH_ERROR' } }, { status: 401 });\n }\n };\n}\n\nexport function createNextAuthCurrentUserRoute(\n config: NextAuthCurrentUserRouteConfig,\n Response: ResponseConstructorLike\n) {\n return async function GET(request: RequestLike): Promise<ResponseLike> {\n try {\n const token = extractBearerToken(request);\n if (!token) {\n return Response.json(\n { error: { message: 'No token provided', code: 'NO_TOKEN' } },\n { status: 401 }\n );\n }\n\n const user = await handleGetCurrentUser(config.authAdapter, token);\n\n if (!user) {\n return Response.json(\n { error: { message: 'User not found', code: 'USER_NOT_FOUND' } },\n { status: 401 }\n );\n }\n\n return Response.json(user, { status: 200 });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to get user';\n return Response.json({ error: { message, code: 'GET_USER_ERROR' } }, { status: 500 });\n }\n };\n}\n\nexport function createAuthenticatedRoute<T>(\n authAdapter: AuthAdapter,\n Response: ResponseConstructorLike,\n handler: (request: RequestLike, user: { id: string; email: string }) => Promise<T>\n) {\n return async (request: RequestLike): Promise<T | ResponseLike> => {\n const token = extractBearerToken(request);\n if (!token) {\n return Response.json(\n { error: { message: 'Authentication required', code: 'NO_TOKEN' } },\n { status: 401 }\n );\n }\n\n const user = await handleVerifySession(authAdapter, { accessToken: token });\n if (!user) {\n return Response.json(\n { error: { message: 'Invalid or expired token', code: 'INVALID_TOKEN' } },\n { status: 401 }\n );\n }\n\n return handler(request, user);\n };\n}\n"],"mappings":"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","names":["toPageResponse"]}
|
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/supabase/index.ts
|
|
21
|
+
var supabase_exports = {};
|
|
22
|
+
__export(supabase_exports, {
|
|
23
|
+
createSupabaseAdapters: () => createSupabaseAdapters
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(supabase_exports);
|
|
26
|
+
|
|
27
|
+
// src/supabase/factory.ts
|
|
28
|
+
var import_supabase_js = require("@supabase/supabase-js");
|
|
29
|
+
|
|
30
|
+
// src/auth/supabase-adapter.ts
|
|
31
|
+
var AuthError = class extends Error {
|
|
32
|
+
constructor(message, code, details) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.code = code;
|
|
35
|
+
this.details = details;
|
|
36
|
+
this.name = "AuthError";
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var SupabaseAuthAdapter = class {
|
|
40
|
+
client;
|
|
41
|
+
constructor(config) {
|
|
42
|
+
this.client = config.client;
|
|
43
|
+
}
|
|
44
|
+
mapSupabaseUser(user) {
|
|
45
|
+
if (!user.email) {
|
|
46
|
+
throw new AuthError("User email is required", "INVALID_USER");
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
id: user.id,
|
|
50
|
+
email: user.email,
|
|
51
|
+
metadata: user.user_metadata
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async signInWithOAuth(input) {
|
|
55
|
+
const { data, error } = await this.client.auth.signInWithOAuth({
|
|
56
|
+
provider: input.provider,
|
|
57
|
+
options: {
|
|
58
|
+
redirectTo: input.redirectTo
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
if (error) {
|
|
62
|
+
throw new AuthError(error.message, error.status?.toString(), error.message);
|
|
63
|
+
}
|
|
64
|
+
if (!data.url) {
|
|
65
|
+
throw new AuthError("OAuth URL not provided", "OAUTH_URL_MISSING");
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
url: data.url,
|
|
69
|
+
provider: input.provider
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
async signInWithPassword(input) {
|
|
73
|
+
const { data, error } = await this.client.auth.signInWithPassword({
|
|
74
|
+
email: input.email,
|
|
75
|
+
password: input.password
|
|
76
|
+
});
|
|
77
|
+
if (error) {
|
|
78
|
+
throw new AuthError(error.message, error.status?.toString(), error.message);
|
|
79
|
+
}
|
|
80
|
+
if (!data.session || !data.user) {
|
|
81
|
+
throw new AuthError("Session or user not returned", "AUTH_FAILED");
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
accessToken: data.session.access_token,
|
|
85
|
+
refreshToken: data.session.refresh_token,
|
|
86
|
+
expiresAt: data.session.expires_at ? new Date(data.session.expires_at * 1e3) : void 0,
|
|
87
|
+
user: this.mapSupabaseUser(data.user)
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async signOut(_accessToken) {
|
|
91
|
+
const { error } = await this.client.auth.signOut();
|
|
92
|
+
if (error) {
|
|
93
|
+
throw new AuthError(error.message, error.status?.toString(), error.message);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async verifySession(input) {
|
|
97
|
+
const { data, error } = await this.client.auth.getUser(input.accessToken);
|
|
98
|
+
if (error) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
if (!data.user) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
return this.mapSupabaseUser(data.user);
|
|
105
|
+
}
|
|
106
|
+
async refreshSession(refreshToken) {
|
|
107
|
+
const { data, error } = await this.client.auth.refreshSession({
|
|
108
|
+
refresh_token: refreshToken
|
|
109
|
+
});
|
|
110
|
+
if (error) {
|
|
111
|
+
throw new AuthError(error.message, error.status?.toString(), error.message);
|
|
112
|
+
}
|
|
113
|
+
if (!data.session || !data.user) {
|
|
114
|
+
throw new AuthError("Session refresh failed", "REFRESH_FAILED");
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
accessToken: data.session.access_token,
|
|
118
|
+
refreshToken: data.session.refresh_token,
|
|
119
|
+
expiresAt: data.session.expires_at ? new Date(data.session.expires_at * 1e3) : void 0,
|
|
120
|
+
user: this.mapSupabaseUser(data.user)
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
async getCurrentUser(accessToken) {
|
|
124
|
+
const { data, error } = await this.client.auth.getUser(accessToken);
|
|
125
|
+
if (error) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
if (!data.user) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
return this.mapSupabaseUser(data.user);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
function createAuthAdapter(config) {
|
|
135
|
+
return new SupabaseAuthAdapter(config);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/media/supabase-adapter.ts
|
|
139
|
+
var MediaError = class extends Error {
|
|
140
|
+
constructor(message, code, details) {
|
|
141
|
+
super(message);
|
|
142
|
+
this.code = code;
|
|
143
|
+
this.details = details;
|
|
144
|
+
this.name = "MediaError";
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
var SupabaseMediaAdapter = class {
|
|
148
|
+
client;
|
|
149
|
+
bucketName;
|
|
150
|
+
constructor(config) {
|
|
151
|
+
this.client = config.client;
|
|
152
|
+
this.bucketName = config.bucketName ?? "media";
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Generates a unique storage path for a file
|
|
156
|
+
*/
|
|
157
|
+
generateStoragePath(filename) {
|
|
158
|
+
const timestamp = Date.now();
|
|
159
|
+
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
|
160
|
+
const sanitizedFilename = filename.replace(/[^a-zA-Z0-9.-]/g, "_");
|
|
161
|
+
return `${timestamp}-${randomSuffix}-${sanitizedFilename}`;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Constructs the public URL for a stored file
|
|
165
|
+
*/
|
|
166
|
+
getPublicUrl(storagePath) {
|
|
167
|
+
const { data } = this.client.storage.from(this.bucketName).getPublicUrl(storagePath);
|
|
168
|
+
return data.publicUrl;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Maps a database row to MediaFile
|
|
172
|
+
*/
|
|
173
|
+
mapRowToMediaFile(row) {
|
|
174
|
+
return {
|
|
175
|
+
id: row.id,
|
|
176
|
+
filename: row.filename,
|
|
177
|
+
url: this.getPublicUrl(row.storage_path),
|
|
178
|
+
mimeType: row.mime_type,
|
|
179
|
+
size: row.size,
|
|
180
|
+
createdAt: new Date(row.created_at)
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
async upload(input) {
|
|
184
|
+
const storagePath = this.generateStoragePath(input.filename);
|
|
185
|
+
const { error: uploadError } = await this.client.storage.from(this.bucketName).upload(storagePath, input.data, {
|
|
186
|
+
contentType: input.mimeType,
|
|
187
|
+
upsert: false
|
|
188
|
+
});
|
|
189
|
+
if (uploadError) {
|
|
190
|
+
throw new MediaError(
|
|
191
|
+
`Failed to upload file: ${uploadError.message}`,
|
|
192
|
+
"UPLOAD_FAILED",
|
|
193
|
+
uploadError.message
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
const { data, error: dbError } = await this.client.from("media").insert({
|
|
197
|
+
filename: input.filename,
|
|
198
|
+
storage_path: storagePath,
|
|
199
|
+
mime_type: input.mimeType,
|
|
200
|
+
size: input.size
|
|
201
|
+
}).select().single();
|
|
202
|
+
if (dbError) {
|
|
203
|
+
await this.client.storage.from(this.bucketName).remove([storagePath]);
|
|
204
|
+
throw new MediaError(
|
|
205
|
+
`Failed to create media record: ${dbError.message}`,
|
|
206
|
+
dbError.code,
|
|
207
|
+
dbError.details
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
return this.mapRowToMediaFile(data);
|
|
211
|
+
}
|
|
212
|
+
async getMedia(id) {
|
|
213
|
+
const { data, error } = await this.client.from("media").select("*").eq("id", id).single();
|
|
214
|
+
if (error) {
|
|
215
|
+
if (error.code === "PGRST116") {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
throw new MediaError(error.message, error.code, error.details);
|
|
219
|
+
}
|
|
220
|
+
return this.mapRowToMediaFile(data);
|
|
221
|
+
}
|
|
222
|
+
async listMedia(filter) {
|
|
223
|
+
let query = this.client.from("media").select("*");
|
|
224
|
+
if (filter?.mimeType) {
|
|
225
|
+
query = query.eq("mime_type", filter.mimeType);
|
|
226
|
+
}
|
|
227
|
+
query = query.order("created_at", { ascending: false });
|
|
228
|
+
if (filter?.limit) {
|
|
229
|
+
query = query.limit(filter.limit);
|
|
230
|
+
}
|
|
231
|
+
if (filter?.offset) {
|
|
232
|
+
query = query.range(filter.offset, filter.offset + (filter.limit ?? 100) - 1);
|
|
233
|
+
}
|
|
234
|
+
const { data, error } = await query;
|
|
235
|
+
if (error) {
|
|
236
|
+
throw new MediaError(error.message, error.code, error.details);
|
|
237
|
+
}
|
|
238
|
+
return data.map((row) => this.mapRowToMediaFile(row));
|
|
239
|
+
}
|
|
240
|
+
async deleteMedia(id) {
|
|
241
|
+
const { data: mediaRecord, error: fetchError } = await this.client.from("media").select("storage_path").eq("id", id).single();
|
|
242
|
+
if (fetchError) {
|
|
243
|
+
if (fetchError.code === "PGRST116") {
|
|
244
|
+
throw new MediaError(`Media not found: ${id}`, "NOT_FOUND");
|
|
245
|
+
}
|
|
246
|
+
throw new MediaError(fetchError.message, fetchError.code, fetchError.details);
|
|
247
|
+
}
|
|
248
|
+
const storagePath = mediaRecord.storage_path;
|
|
249
|
+
const { error: storageError } = await this.client.storage.from(this.bucketName).remove([storagePath]);
|
|
250
|
+
if (storageError) {
|
|
251
|
+
throw new MediaError(
|
|
252
|
+
`Failed to delete file from storage: ${storageError.message}`,
|
|
253
|
+
"STORAGE_DELETE_FAILED",
|
|
254
|
+
storageError.message
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
const { error: dbError } = await this.client.from("media").delete().eq("id", id);
|
|
258
|
+
if (dbError) {
|
|
259
|
+
throw new MediaError(dbError.message, dbError.code, dbError.details);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
function createMediaAdapter(config) {
|
|
264
|
+
return new SupabaseMediaAdapter(config);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/storage/supabase-adapter.ts
|
|
268
|
+
function mapPageRowToPage(row) {
|
|
269
|
+
return {
|
|
270
|
+
id: row.id,
|
|
271
|
+
slug: row.slug,
|
|
272
|
+
pageType: row.page_type,
|
|
273
|
+
title: row.title,
|
|
274
|
+
sections: row.sections,
|
|
275
|
+
createdAt: new Date(row.created_at),
|
|
276
|
+
updatedAt: new Date(row.updated_at)
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function mapNavigationRowToNavigation(row) {
|
|
280
|
+
return {
|
|
281
|
+
id: row.id,
|
|
282
|
+
name: row.name,
|
|
283
|
+
items: row.items,
|
|
284
|
+
updatedAt: new Date(row.updated_at)
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
var StorageError = class extends Error {
|
|
288
|
+
constructor(message, code, details) {
|
|
289
|
+
super(message);
|
|
290
|
+
this.code = code;
|
|
291
|
+
this.details = details;
|
|
292
|
+
this.name = "StorageError";
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
var SupabaseStorageAdapter = class {
|
|
296
|
+
client;
|
|
297
|
+
constructor(config) {
|
|
298
|
+
this.client = config.client;
|
|
299
|
+
}
|
|
300
|
+
async getPage(slug) {
|
|
301
|
+
const { data, error } = await this.client.from("pages").select("*").eq("slug", slug).single();
|
|
302
|
+
if (error) {
|
|
303
|
+
if (error.code === "PGRST116") {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
throw new StorageError(error.message, error.code, error.details);
|
|
307
|
+
}
|
|
308
|
+
return mapPageRowToPage(data);
|
|
309
|
+
}
|
|
310
|
+
async getPageById(id) {
|
|
311
|
+
const { data, error } = await this.client.from("pages").select("*").eq("id", id).single();
|
|
312
|
+
if (error) {
|
|
313
|
+
if (error.code === "PGRST116") {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
throw new StorageError(error.message, error.code, error.details);
|
|
317
|
+
}
|
|
318
|
+
return mapPageRowToPage(data);
|
|
319
|
+
}
|
|
320
|
+
async createPage(input) {
|
|
321
|
+
const { data, error } = await this.client.from("pages").insert({
|
|
322
|
+
slug: input.slug,
|
|
323
|
+
page_type: input.pageType,
|
|
324
|
+
title: input.title,
|
|
325
|
+
sections: input.sections ?? []
|
|
326
|
+
}).select().single();
|
|
327
|
+
if (error) {
|
|
328
|
+
throw new StorageError(error.message, error.code, error.details);
|
|
329
|
+
}
|
|
330
|
+
return mapPageRowToPage(data);
|
|
331
|
+
}
|
|
332
|
+
async updatePage(input) {
|
|
333
|
+
const updateData = {};
|
|
334
|
+
if (input.slug !== void 0) {
|
|
335
|
+
updateData.slug = input.slug;
|
|
336
|
+
}
|
|
337
|
+
if (input.pageType !== void 0) {
|
|
338
|
+
updateData.page_type = input.pageType;
|
|
339
|
+
}
|
|
340
|
+
if (input.title !== void 0) {
|
|
341
|
+
updateData.title = input.title;
|
|
342
|
+
}
|
|
343
|
+
if (input.sections !== void 0) {
|
|
344
|
+
updateData.sections = input.sections;
|
|
345
|
+
}
|
|
346
|
+
const { data, error } = await this.client.from("pages").update(updateData).eq("id", input.id).select().single();
|
|
347
|
+
if (error) {
|
|
348
|
+
if (error.code === "PGRST116") {
|
|
349
|
+
throw new StorageError(`Page not found: ${input.id}`, "NOT_FOUND");
|
|
350
|
+
}
|
|
351
|
+
throw new StorageError(error.message, error.code, error.details);
|
|
352
|
+
}
|
|
353
|
+
return mapPageRowToPage(data);
|
|
354
|
+
}
|
|
355
|
+
async deletePage(id) {
|
|
356
|
+
const { error } = await this.client.from("pages").delete().eq("id", id);
|
|
357
|
+
if (error) {
|
|
358
|
+
throw new StorageError(error.message, error.code, error.details);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
async listPages(filter) {
|
|
362
|
+
let query = this.client.from("pages").select("*");
|
|
363
|
+
if (filter?.pageType) {
|
|
364
|
+
query = query.eq("page_type", filter.pageType);
|
|
365
|
+
}
|
|
366
|
+
query = query.order("created_at", { ascending: false });
|
|
367
|
+
if (filter?.limit) {
|
|
368
|
+
query = query.limit(filter.limit);
|
|
369
|
+
}
|
|
370
|
+
if (filter?.offset) {
|
|
371
|
+
query = query.range(filter.offset, filter.offset + (filter.limit ?? 100) - 1);
|
|
372
|
+
}
|
|
373
|
+
const { data, error } = await query;
|
|
374
|
+
if (error) {
|
|
375
|
+
throw new StorageError(error.message, error.code, error.details);
|
|
376
|
+
}
|
|
377
|
+
return data.map(mapPageRowToPage);
|
|
378
|
+
}
|
|
379
|
+
async getNavigation(name) {
|
|
380
|
+
const { data, error } = await this.client.from("navigation").select("*").eq("name", name).single();
|
|
381
|
+
if (error) {
|
|
382
|
+
if (error.code === "PGRST116") {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
throw new StorageError(error.message, error.code, error.details);
|
|
386
|
+
}
|
|
387
|
+
return mapNavigationRowToNavigation(data);
|
|
388
|
+
}
|
|
389
|
+
async getNavigationById(id) {
|
|
390
|
+
const { data, error } = await this.client.from("navigation").select("*").eq("id", id).single();
|
|
391
|
+
if (error) {
|
|
392
|
+
if (error.code === "PGRST116") {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
throw new StorageError(error.message, error.code, error.details);
|
|
396
|
+
}
|
|
397
|
+
return mapNavigationRowToNavigation(data);
|
|
398
|
+
}
|
|
399
|
+
async createNavigation(input) {
|
|
400
|
+
const { data, error } = await this.client.from("navigation").insert({
|
|
401
|
+
name: input.name,
|
|
402
|
+
items: input.items
|
|
403
|
+
}).select().single();
|
|
404
|
+
if (error) {
|
|
405
|
+
if (error.code === "23505") {
|
|
406
|
+
throw new StorageError(
|
|
407
|
+
`Navigation with name "${input.name}" already exists`,
|
|
408
|
+
"DUPLICATE_NAME"
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
throw new StorageError(error.message, error.code, error.details);
|
|
412
|
+
}
|
|
413
|
+
return mapNavigationRowToNavigation(data);
|
|
414
|
+
}
|
|
415
|
+
async updateNavigation(input) {
|
|
416
|
+
const updateData = {};
|
|
417
|
+
if (input.name !== void 0) {
|
|
418
|
+
updateData.name = input.name;
|
|
419
|
+
}
|
|
420
|
+
if (input.items !== void 0) {
|
|
421
|
+
updateData.items = input.items;
|
|
422
|
+
}
|
|
423
|
+
const { data, error } = await this.client.from("navigation").update(updateData).eq("id", input.id).select().single();
|
|
424
|
+
if (error) {
|
|
425
|
+
if (error.code === "PGRST116") {
|
|
426
|
+
throw new StorageError(`Navigation not found: ${input.id}`, "NOT_FOUND");
|
|
427
|
+
}
|
|
428
|
+
throw new StorageError(error.message, error.code, error.details);
|
|
429
|
+
}
|
|
430
|
+
return mapNavigationRowToNavigation(data);
|
|
431
|
+
}
|
|
432
|
+
async deleteNavigation(id) {
|
|
433
|
+
const { error } = await this.client.from("navigation").delete().eq("id", id);
|
|
434
|
+
if (error) {
|
|
435
|
+
throw new StorageError(error.message, error.code, error.details);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
async listNavigations() {
|
|
439
|
+
const { data, error } = await this.client.from("navigation").select("*").order("name", { ascending: true });
|
|
440
|
+
if (error) {
|
|
441
|
+
throw new StorageError(error.message, error.code, error.details);
|
|
442
|
+
}
|
|
443
|
+
return data.map(mapNavigationRowToNavigation);
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
function createStorageAdapter(config) {
|
|
447
|
+
return new SupabaseStorageAdapter(config);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/supabase/factory.ts
|
|
451
|
+
function readEnv(name) {
|
|
452
|
+
const processLike = globalThis.process;
|
|
453
|
+
return processLike?.env[name];
|
|
454
|
+
}
|
|
455
|
+
function resolveRequiredConfig(explicitValue, envName) {
|
|
456
|
+
const explicit = explicitValue?.trim();
|
|
457
|
+
if (explicit) {
|
|
458
|
+
return explicit;
|
|
459
|
+
}
|
|
460
|
+
const fromEnv = readEnv(envName)?.trim();
|
|
461
|
+
if (fromEnv) {
|
|
462
|
+
return fromEnv;
|
|
463
|
+
}
|
|
464
|
+
throw new Error(
|
|
465
|
+
`Missing Supabase configuration: provide ${envName} in factory config or environment variable`
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
function resolveBucketName(config) {
|
|
469
|
+
const explicitBucket = config.storage?.bucket?.trim();
|
|
470
|
+
if (explicitBucket) {
|
|
471
|
+
return explicitBucket;
|
|
472
|
+
}
|
|
473
|
+
const envBucket = readEnv("SUPABASE_STORAGE_BUCKET")?.trim();
|
|
474
|
+
if (envBucket) {
|
|
475
|
+
return envBucket;
|
|
476
|
+
}
|
|
477
|
+
return "media";
|
|
478
|
+
}
|
|
479
|
+
function createSupabaseAdapters(config = {}) {
|
|
480
|
+
const url = resolveRequiredConfig(config.url, "SUPABASE_URL");
|
|
481
|
+
const key = resolveRequiredConfig(config.key, "SUPABASE_SECRET_KEY");
|
|
482
|
+
const bucketName = resolveBucketName(config);
|
|
483
|
+
const client = (0, import_supabase_js.createClient)(url, key);
|
|
484
|
+
return {
|
|
485
|
+
storageAdapter: createStorageAdapter({ client }),
|
|
486
|
+
mediaAdapter: createMediaAdapter({ client, bucketName }),
|
|
487
|
+
authAdapter: createAuthAdapter({ client })
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
491
|
+
0 && (module.exports = {
|
|
492
|
+
createSupabaseAdapters
|
|
493
|
+
});
|
|
494
|
+
//# sourceMappingURL=index.cjs.map
|