### Create and Update Projects Source: https://context7.com/tontile/tonner/llms.txt Handles the creation and modification of projects. Includes functions to create a new project with basic details and to update a user's role within a project. Relies on Supabase mutations for data persistence. Requires project details or user/project identifiers. ```typescript import { createProject, updateProject, updateProjectUser, removeProjectUser } from "@tonner/supabase/mutations"; import { createClient } from "@tonner/supabase/server"; export async function createNewProject(accountName: string, projectName: string) { const supabase = createClient(); const project = await createProject({ account_name: accountName, partial_name: projectName, display_name: `${accountName}/${projectName}`, bio: 'New project description', public: false, archived: false }, supabase); if (!project) { throw new Error('Failed to create project'); } return project; } export async function updateMemberRole(projectId: string, userId: string, role: 'owner' | 'write' | 'read') { const supabase = createClient(); const result = await updateProjectUser({ project_id: projectId, user_id: userId, new_membership_role: role }, supabase); return result; } ``` -------------------------------- ### Retrieve Project Details and Members Source: https://context7.com/tontile/tonner/llms.txt Fetches a specific project's details along with its members. It queries the project and its associated users from Supabase, returning project information and a list of members with their count. Requires project ID as input. ```typescript import { getProjectQuery, getUserProjectsQuery, getProjectUsersQuery } from "@tonner/supabase/queries"; import { createClient } from "@tonner/supabase/server"; export async function getProjectWithMembers(projectId: string) { const supabase = createClient(); // Get project details with users const project = await getProjectQuery({ project_id: projectId }, supabase); if (!project) { throw new Error('Project not found'); } // Get all project members const members = await getProjectUsersQuery({ project_id: projectId }, supabase); return { ...project, memberCount: members?.length || 0, members: members || [] }; } export async function getUserProjectList(userId: string) { const supabase = createClient(); const projects = await getUserProjectsQuery({ user_id: userId }, supabase); return projects || []; } ``` -------------------------------- ### Structured Logger - Pino Utility Source: https://context7.com/tontile/tonner/llms.txt A lightweight Pino-based logger for structured logging across the monorepo. Supports various log levels (info, error, debug, warn, fatal) and includes context in error logging. Relies on the '@tonner/logger' package. ```typescript import { logger } from "@tonner/logger"; // Log info message logger.info('User logged in', { userId: '123', timestamp: Date.now() }); // Log error with context try { await riskyOperation(); } catch (error) { logger.error(error, 'Operation failed with context'); } // Log with custom levels logger.debug('Debugging information'); logger.warn('Warning message'); logger.fatal('Critical error'); ``` -------------------------------- ### Create Supabase Server Client - TypeScript Source: https://context7.com/tontile/tonner/llms.txt Creates a server-side Supabase client for Next.js applications, enabling authenticated and admin modes with automatic session management. It supports specifying custom schemas, such as for storage operations. ```typescript import { createClient } from "@tonner/supabase/server"; // Create authenticated client (uses user's session) export async function getUserData() { const supabase = createClient(); const { data: user } = await supabase.auth.getUser(); return user; } // Create admin client (uses service key) export async function adminOperation() { const supabase = createClient({ admin: true }); const { data, error } = await supabase.auth.admin.listUsers(); if (error) throw error; return data; } // Use with storage schema export async function getStorageFiles() { const supabase = createClient({ schema: 'storage' }); const { data, error } = await supabase.storage.from('avatars').list(); return data; } ``` -------------------------------- ### Secure Server Actions with Validation Source: https://context7.com/tontile/tonner/llms.txt Implements type-safe server actions using next-safe-action, incorporating authentication, input validation via Zod, and error handling. It allows secure updates to user information, interacting with Supabase for data persistence. Requires validated input and authentication context. ```typescript import { authActionClient } from "@/libs/safe-action"; import { z } from "zod"; import { updateUser } from "@tonner/supabase/mutations"; const updateUserSchema = z.object({ full_name: z.string().optional(), email: z.string().email().optional(), avatar_url: z.string().url().optional(), }); // Authenticated action with validation export const updateUserAction = authActionClient .schema(updateUserSchema) .action(async ({ parsedInput: input, ctx: { user, supabase } }) => { const result = await updateUser({ user_id: user.id, display_name: input.full_name, public_metadata: { email: input.email, avatar: input.avatar_url } }, supabase); if (!result) { throw new Error('Update failed'); } return result; }); // Usage in component async function handleSubmit(formData: FormData) { const result = await updateUserAction({ full_name: formData.get('name') as string, email: formData.get('email') as string }); if (result.serverError) { console.error(result.serverError); } } ``` -------------------------------- ### Manage Organizations and Members Source: https://context7.com/tontile/tonner/llms.txt Provides functionality for organization management, including creation and updating user roles. It uses Supabase mutations for CRUD operations and supports multi-tenant structures. Requires organization and user identifiers for updates. ```typescript import { createOrganization, updateOrganization, updateOrganizationUser, getOrganizationUsersQuery } from "@tonner/supabase/mutations"; import { createClient } from "@tonner/supabase/server"; export async function setupOrganization(accountName: string) { const supabase = createClient(); // Create organization const org = await createOrganization({ account_name: accountName, display_name: accountName, bio: 'Organization description' }, supabase); if (!org) { throw new Error('Failed to create organization'); } return org; } export async function changeUserRole(orgId: string, userId: string, role: 'owner' | 'write' | 'read') { const supabase = createClient(); const result = await updateOrganizationUser({ organization_id: orgId, user_id: userId, new_membership_role: role }, supabase); if (!result) { throw new Error('Failed to update user role'); } return result; } ``` -------------------------------- ### OAuth Callback Handler - Next.js API Route Source: https://context7.com/tontile/tonner/llms.txt Handles OAuth callbacks by exchanging an authorization code for a session. It supports environment-aware redirects based on whether the environment is local or deployed. Dependencies include 'next/server' and '@tonner/supabase/server'. ```typescript import { createClient } from "@tonner/supabase/server"; import { NextResponse } from "next/server"; export async function GET(request: Request) { const { searchParams, origin } = new URL(request.url); const code = searchParams.get("code"); const next = searchParams.get("next") ?? "/"; if (code) { const supabase = createClient(); const { error } = await supabase.auth.exchangeCodeForSession(code); if (!error) { const forwardedHost = request.headers.get("x-forwarded-host"); const isLocalEnv = process.env.NODE_ENV === "development"; if (isLocalEnv) { return NextResponse.redirect(`${origin}${next}`); } if (forwardedHost) { return NextResponse.redirect(`https://${forwardedHost}${next}`); } return NextResponse.redirect(`${origin}${next}`); } } return NextResponse.redirect(`${origin}?error=auth-code-error`); } ``` -------------------------------- ### Newsletter Subscription Action - Loops Integration Source: https://context7.com/tontile/tonner/llms.txt Server action to subscribe users to a newsletter via Loops. It takes form data and a user group, sending the email to the Loops API. Dependencies include 'fetch'. It returns JSON response from the Loops API. ```typescript export async function subscribeAction(formData: FormData, userGroup: string) { const email = formData.get("email") as string; const res = await fetch( `https://app.loops.so/api/newsletter-form/${process.env.NEXT_PUBLIC_LOOPS_FORM_ID}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, userGroup }), } ); const json = await res.json(); return json; } // Usage in form component async function handleSubscribe(formData: FormData) { const result = await subscribeAction(formData, 'developers'); if (result.success) { console.log('Successfully subscribed'); } } ``` -------------------------------- ### Update User Profile Information - TypeScript Source: https://context7.com/tontile/tonner/llms.txt Updates a user's profile information, including display name, bio, and public metadata. It utilizes a Supabase RPC function and supports optional metadata replacement. ```typescript import { updateUser, type UpdateUserParams } from "@tonner/supabase/mutations"; import { createClient } from "@tonner/supabase/server"; export async function updateUserProfile(userId: string, updates: Partial) { const supabase = createClient(); const params: UpdateUserParams = { user_id: userId, display_name: updates.display_name, bio: updates.bio, public_metadata: updates.public_metadata, replace_metadata: false // Merge with existing metadata }; const result = await updateUser(params, supabase); if (!result) { throw new Error('Failed to update user'); } return result; } ``` -------------------------------- ### Fetch User with Related Entities - TypeScript Source: https://context7.com/tontile/tonner/llms.txt Retrieves a user profile along with their associated organizations, teams, and projects using the Supabase query builder. This function includes support for an optional abort signal for request cancellation. ```typescript import { getUserQuery } from "@tonner/supabase/queries"; import { createClient } from "@tonner/supabase/server"; export async function getFullUserProfile(userId: string) { const supabase = createClient(); const controller = new AbortController(); try { const user = await getUserQuery( { user_id: userId }, supabase, controller.signal ); if (!user) { throw new Error('User not found'); } return { profile: user, organizations: user.organizations, teams: user.teams, projects: user.projects }; } catch (error) { controller.abort(); throw error; } } ``` -------------------------------- ### Cache Utility with SuperJSON - TypeScript Source: https://context7.com/tontile/tonner/llms.txt Wraps Next.js unstable_cache with SuperJSON for serializing complex data types like Dates, Maps, Sets, and BigInts. It supports automatic tag generation, revalidation, and conditional caching. ```typescript import { createUnstableCache } from "@tonner/cache"; // Cache database query with 3-minute revalidation export async function getCachedUser(userId: string) { return await createUnstableCache( async () => { const supabase = createClient(); const { data } = await supabase .from('users') .select('*') .eq('id', userId) .single(); return data; }, ['user', userId], { revalidate: 180, tags: ['users'] } ); } // Disable caching conditionally export async function getRealtimeData(id: string) { const skipCache = process.env.TONNER_CACHE !== 'true'; return await createUnstableCache( async () => fetchData(id), ['data', id], { revalidate: 60, tags: [] }, !skipCache ); } ``` -------------------------------- ### Hierarchical Cache Tag Generation - Next.js Cache Source: https://context7.com/tontile/tonner/llms.txt Utility function for generating hierarchical cache tags from string arrays, enabling granular cache invalidation strategies in Next.js. Uses the '@tonner/cache' package for tag creation and integrates with Next.js's 'revalidateTag' function. ```typescript import { createTags } from "@tonner/cache"; // Generate hierarchical tags const tags = createTags(['user', '123', 'projects']); // Returns: ['user', 'user:123', 'user:123:projects'] // Use with revalidateTag import { revalidateTag } from 'next/cache'; // Invalidate all user caches revalidateTag('user'); // Invalidate specific user revalidateTag('user:123'); // Invalidate specific user's projects revalidateTag('user:123:projects'); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.