diff --git a/.github/workflows/e2e-api-tests.yaml b/.github/workflows/e2e-api-tests.yaml index 0aab264c3..5f986bbee 100644 --- a/.github/workflows/e2e-api-tests.yaml +++ b/.github/workflows/e2e-api-tests.yaml @@ -59,6 +59,9 @@ jobs: - name: Create .env.test.local file for apps/e2e run: cp apps/e2e/.env.development apps/e2e/.env.test.local + - name: Create .env.test.local file for docs + run: cp docs/.env.development docs/.env.test.local + - name: Create .env.test.local file for examples/cjs-test run: cp examples/cjs-test/.env.development examples/cjs-test/.env.test.local diff --git a/.github/workflows/lint-and-build.yaml b/.github/workflows/lint-and-build.yaml index d8cc29759..240716a14 100644 --- a/.github/workflows/lint-and-build.yaml +++ b/.github/workflows/lint-and-build.yaml @@ -44,6 +44,9 @@ jobs: - name: Create .env.production.local file for apps/e2e run: cp apps/e2e/.env.development apps/e2e/.env.production.local + - name: Create .env.production.local file for docs + run: cp docs/.env.development docs/.env.production.local + - name: Create .env.production.local file for examples/cjs-test run: cp examples/cjs-test/.env.development examples/cjs-test/.env.production.local diff --git a/apps/backend/package.json b/apps/backend/package.json index 9b7e5454f..fc71d9129 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -17,15 +17,16 @@ "codegen-prisma:watch": "pnpm run prisma generate --watch", "codegen-route-info": "pnpm run with-env tsx scripts/generate-route-info.ts", "codegen-route-info:watch": "pnpm run with-env tsx watch --clear-screen=false scripts/generate-route-info.ts", - "codegen": "pnpm run with-env bash -c 'if [ \"$STACK_ACCELERATE_ENABLED\" = \"true\" ]; then pnpm run prisma generate --no-engine && pnpm run generate-openapi; else pnpm run codegen-prisma && pnpm run generate-openapi; fi' && pnpm run codegen-route-info", + "codegen": "pnpm run with-env bash -c 'if [ \"$STACK_ACCELERATE_ENABLED\" = \"true\" ]; then pnpm run prisma generate --no-engine; else pnpm run codegen-prisma; fi' && pnpm run codegen-route-info", "codegen:watch": "concurrently -n \"prisma,docs,route-info\" -k \"pnpm run codegen-prisma:watch\" \"pnpm run watch-docs\" \"pnpm run codegen-route-info:watch\"", "psql-inner": "psql $STACK_DATABASE_CONNECTION_STRING", "psql": "pnpm run with-env pnpm run psql-inner", "prisma": "pnpm run with-env prisma", "prisma-studio": "pnpm run with-env prisma studio --port 8106 --browser none", "lint": "next lint", - "watch-docs": "pnpm run with-env tsx watch --clear-screen=false scripts/generate-openapi.ts", + "watch-docs": "pnpm run with-env bash -c 'tsx watch --clear-screen=false scripts/generate-openapi-fumadocs.ts && pnpm run --filter=@stackframe/stack-docs generate-openapi-docs'", "generate-openapi": "pnpm run with-env tsx scripts/generate-openapi.ts", + "generate-openapi-fumadocs": "pnpm run with-env tsx scripts/generate-openapi-fumadocs.ts", "generate-keys": "pnpm run with-env tsx scripts/generate-keys.ts", "db-seed-script": "pnpm run with-env tsx prisma/seed.ts", "verify-data-integrity": "pnpm run with-env tsx scripts/verify-data-integrity.ts" diff --git a/apps/backend/scripts/generate-openapi-fumadocs.ts b/apps/backend/scripts/generate-openapi-fumadocs.ts new file mode 100644 index 000000000..64e2464a1 --- /dev/null +++ b/apps/backend/scripts/generate-openapi-fumadocs.ts @@ -0,0 +1,85 @@ +import { parseOpenAPI, parseWebhookOpenAPI } from '@/lib/openapi'; +import { isSmartRouteHandler } from '@/route-handlers/smart-route-handler'; +import { webhookEvents } from '@stackframe/stack-shared/dist/interface/webhooks'; +import { writeFileSyncIfChanged } from '@stackframe/stack-shared/dist/utils/fs'; +import { HTTP_METHODS } from '@stackframe/stack-shared/dist/utils/http'; +import { typedKeys } from '@stackframe/stack-shared/dist/utils/objects'; +import fs from 'fs'; +import { glob } from 'glob'; +import path from 'path'; + + +async function main() { + console.log("Started Fumadocs OpenAPI schema generator"); + + // Create openapi directory in Fumadocs project + const fumaDocsOpenApiDir = path.resolve("../../docs/openapi"); + + // Ensure the openapi directory exists + if (!fs.existsSync(fumaDocsOpenApiDir)) { + console.log('Creating OpenAPI directory...'); + fs.mkdirSync(fumaDocsOpenApiDir, { recursive: true }); + } + + // Generate OpenAPI specs for each audience (let parseOpenAPI handle the filtering) + const filePathPrefix = path.resolve(process.platform === "win32" ? "apps/src/app/api/latest" : "src/app/api/latest"); + const importPathPrefix = "@/app/api/latest"; + const filePaths = [...await glob(filePathPrefix + "/**/route.{js,jsx,ts,tsx}")]; + + const endpoints = new Map(await Promise.all(filePaths.map(async (filePath) => { + if (!filePath.startsWith(filePathPrefix)) { + throw new Error(`Invalid file path: ${filePath}`); + } + const suffix = filePath.slice(filePathPrefix.length); + const midfix = suffix.slice(0, suffix.lastIndexOf("/route.")); + const importPath = `${importPathPrefix}${suffix}`; + const urlPath = midfix.replaceAll("[", "{").replaceAll("]", "}").replaceAll(/\/\(.*\)/g, ""); + const myModule = require(importPath); + const handlersByMethod = new Map( + typedKeys(HTTP_METHODS).map(method => [method, myModule[method]] as const) + .filter(([_, handler]) => isSmartRouteHandler(handler)) + ); + return [urlPath, handlersByMethod] as const; + }))); + + console.log(`Found ${endpoints.size} total endpoint files`); + + // Generate specs for each audience using parseOpenAPI's built-in filtering + for (const audience of ['client', 'server', 'admin'] as const) { + const openApiSchemaObject = parseOpenAPI({ + endpoints, + audience, // Let parseOpenAPI handle the audience-specific filtering + }); + + // Update server URL for Fumadocs + openApiSchemaObject.servers = [{ + url: 'https://api.stack-auth.com/api/v1', + description: 'Stack REST API', + }]; + + console.log(`Generated ${Object.keys(openApiSchemaObject.paths || {}).length} endpoints for ${audience} audience`); + + // Write JSON files for Fumadocs (they prefer JSON over YAML) + writeFileSyncIfChanged( + path.join(fumaDocsOpenApiDir, `${audience}.json`), + JSON.stringify(openApiSchemaObject, null, 2) + ); + } + + // Generate webhooks schema + const webhookOpenAPISchema = parseWebhookOpenAPI({ + webhooks: webhookEvents, + }); + + writeFileSyncIfChanged( + path.join(fumaDocsOpenApiDir, 'webhooks.json'), + JSON.stringify(webhookOpenAPISchema, null, 2) + ); + + console.log("Successfully updated Fumadocs OpenAPI schemas with proper audience filtering"); +} + +main().catch((...args) => { + console.error(`ERROR! Could not update Fumadocs OpenAPI schema`, ...args); + process.exit(1); +}); diff --git a/docs/.env.development b/docs/.env.development new file mode 100644 index 000000000..b3d3207d7 --- /dev/null +++ b/docs/.env.development @@ -0,0 +1,6 @@ +# Contains the credentials for the internal project of Stack's default development environment setup. +# Do not use in a production environment, instead replace it with actual values gathered from https://app.stack-auth.com. +NEXT_PUBLIC_STACK_API_URL=http://localhost:8102 +NEXT_PUBLIC_STACK_PROJECT_ID=internal +NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=this-publishable-client-key-is-for-local-development-only +STACK_SECRET_SERVER_KEY=this-secret-server-key-is-for-local-development-only diff --git a/docs/.eslintrc.json b/docs/.eslintrc.json new file mode 100644 index 000000000..a392d3cb6 --- /dev/null +++ b/docs/.eslintrc.json @@ -0,0 +1,13 @@ +{ + "extends": [ + "../configs/eslint/defaults.js", + "../configs/eslint/next.js" + ], + "ignorePatterns": ["/*", "!/src"], + "rules": { + // Temporarily downgrade some rules to warnings during migration + // Remove these overrides once all issues are fixed + "@typescript-eslint/no-unused-vars": "warn", + "@typescript-eslint/no-explicit-any": "warn" + } +} diff --git a/docs/.gitignore b/docs/.gitignore index 4f9903e26..e1a6146bc 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,8 +1,34 @@ -fern/js.yml -fern/next.yml -fern/react.yml -fern/python.yml -fern/docs/pages-js -fern/docs/pages-next -fern/docs/pages-react -fern/docs/pages-python +# deps +/node_modules + +# generated content +.contentlayer +.content-collections +.source + +# test & build +/coverage +/.next/ +/out/ +/build +*.tsbuildinfo + +# misc +.DS_Store +*.pem +/.pnp +.pnp.js +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# others +.vercel +next-env.d.ts + +# ignore all generated docs content +/content/docs/ +/content/api/ +/public/openapi/ +/openapi/ + diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7d1067c49..b38bdd7cb 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,4 +1,4 @@ -# @stackframe/docs +# @stackframe/stack-docs ## 2.8.15 diff --git a/docs/LICENSE b/docs/LICENSE deleted file mode 100644 index 0ec883d12..000000000 --- a/docs/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2024 Stackframe - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..17d3ea68a --- /dev/null +++ b/docs/README.md @@ -0,0 +1,46 @@ +# stack-docs + +This is a Next.js application generated with +[Create Fumadocs](https://github.com/fuma-nama/fumadocs). + +Run development server: + +```bash +npm run dev +# or +pnpm dev +# or +yarn dev +``` + +Open http://localhost:3000 with your browser to see the result. + +## Explore + +In the project, you can see: + +- `lib/source.ts`: Code for content source adapter, [`loader()`](https://fumadocs.dev/docs/headless/source-api) provides the interface to access your content. +- `app/layout.config.tsx`: Shared options for layouts, optional but preferred to keep. + +| Route | Description | +| ------------------------- | ------------------------------------------------------ | +| `app/(home)` | The route group for your landing page and other pages. | +| `app/docs` | The documentation layout and pages. | +| `app/api` | The documentation for API pages. | +| `app/api/search/route.ts` | The Route Handler for search. | + +### Fumadocs MDX + +A `source.config.ts` config file has been included, you can customise different options like frontmatter schema. + +Read the [Introduction](https://fumadocs.dev/docs/mdx) for further details. + +## Learn More + +To learn more about Next.js and Fumadocs, take a look at the following +resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js + features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +- [Fumadocs](https://fumadocs.dev) - learn about Fumadocs diff --git a/docs/cli.json b/docs/cli.json new file mode 100644 index 000000000..c502d3e9b --- /dev/null +++ b/docs/cli.json @@ -0,0 +1,8 @@ +{ + "aliases": { + "cn": "./src/lib/utils.ts", + "componentsDir": "./src/components", + "uiDir": "./src/components/ui", + "libDir": "./src/lib" + } +} \ No newline at end of file diff --git a/docs/docs-platform.yml b/docs/docs-platform.yml new file mode 100644 index 000000000..abe6da791 --- /dev/null +++ b/docs/docs-platform.yml @@ -0,0 +1,217 @@ +# Platform-specific content filtering configuration +# Explicit page-by-page listing approach + +pages: + # Root pages + - path: overview.mdx + platforms: ["next", "react", "js", "python"] + + - path: faq.mdx + platforms: ["next", "react", "js", "python"] + + # Getting Started + - path: getting-started/setup.mdx + platforms: ["next", "react", "js", "python"] + + - path: getting-started/components.mdx + platforms: ["next", "react"] # Only React-like platforms + + - path: getting-started/users.mdx + platforms: ["next", "react", "js"] # No Python + + - path: getting-started/example-pages.mdx + platforms: ["js"] # Only vanilla JS + + - path: getting-started/production.mdx + platforms: ["next", "react", "js"] # No Python + + # Concepts + - path: concepts/stack-app.mdx + platforms: ["next", "react", "js"] # No Python + + - path: concepts/custom-user-data.mdx + platforms: ["next", "react", "js"] # No Python + + - path: concepts/user-onboarding.mdx + platforms: ["next", "react"] # No JS or Python + + - path: concepts/oauth.mdx + platforms: ["next", "react", "js"] # No Python + + - path: concepts/orgs-and-teams.mdx + platforms: ["next", "react", "js"] # No Python + + - path: concepts/team-selection.mdx + platforms: ["next", "react", "js"] # No Python + + - path: concepts/permissions.mdx + platforms: ["next", "react", "js"] # No Python + + - path: concepts/api-keys.mdx + platforms: ["next", "react", "js"] # No Python + + - path: concepts/webhooks.mdx + platforms: ["next", "react", "js"] # No Python + + - path: concepts/backend-integration.mdx + platforms: ["next", "react", "js", "python"] + + # Components (React-like only) + - path: components/overview.mdx + platforms: ["next", "react"] + + - path: components/user-button.mdx + platforms: ["next", "react"] + + - path: components/selected-team-switcher.mdx + platforms: ["next", "react"] + + - path: components/account-settings.mdx + platforms: ["next", "react"] + + - path: components/sign-in.mdx + platforms: ["next", "react"] + + - path: components/sign-up.mdx + platforms: ["next", "react"] + + - path: components/credential-sign-in.mdx + platforms: ["next", "react"] + + - path: components/credential-sign-up.mdx + platforms: ["next", "react"] + + - path: components/magic-link-sign-in.mdx + platforms: ["next", "react"] + + - path: components/forgot-password.mdx + platforms: ["next", "react"] + + - path: components/password-reset.mdx + platforms: ["next", "react"] + + - path: components/oauth-button.mdx + platforms: ["next", "react"] + + - path: components/oauth-button-group.mdx + platforms: ["next", "react"] + + - path: components/stack-handler.mdx + platforms: ["next", "react"] + + - path: components/stack-provider.mdx + platforms: ["next", "react"] + + - path: components/stack-theme.mdx + platforms: ["next", "react"] + + # Customization (React-like only) + - path: customization/dark-mode.mdx + platforms: ["next", "react"] + + - path: customization/custom-styles.mdx + platforms: ["next", "react"] + + - path: customization/internationalization.mdx + platforms: ["next", "react"] + + - path: customization/custom-pages.mdx + platforms: ["next", "react"] + + - path: customization/page-examples/index.mdx + platforms: ["next", "react"] + + - path: customization/page-examples/sign-in.mdx + platforms: ["next", "react"] + + - path: customization/page-examples/sign-up.mdx + platforms: ["next", "react"] + + - path: customization/page-examples/forgot-password.mdx + platforms: ["next", "react"] + + - path: customization/page-examples/password-reset.mdx + platforms: ["next", "react"] + + # SDK Reference + - path: sdk/overview.mdx + platforms: ["next", "react", "js"] # No Python + + - path: sdk/overview-new.mdx + platforms: ["next", "react", "js"] # No Python + + - path: sdk/objects/stack-app.mdx + platforms: ["next", "react", "js"] # No Python + + - path: sdk/objects/stack-app-test.mdx + platforms: ["next", "react", "js"] # No Python + + - path: sdk/types/api-key.mdx + platforms: ["next", "react", "js"] # No Python + + - path: sdk/types/connected-account.mdx + platforms: ["next", "react", "js"] # No Python + + - path: sdk/types/contact-channel.mdx + platforms: ["next", "react", "js"] # No Python + + - path: sdk/types/project.mdx + platforms: ["next", "react", "js"] # No Python + + - path: sdk/types/team.mdx + platforms: ["next", "react", "js"] # No Python + + - path: sdk/types/team-permission.mdx + platforms: ["next", "react", "js"] # No Python + + - path: sdk/types/team-profile.mdx + platforms: ["next", "react", "js"] # No Python + + - path: sdk/types/team-user.mdx + platforms: ["next", "react", "js"] # No Python + + - path: sdk/types/user.mdx + platforms: ["next", "react", "js"] # No Python + + # SDK Hooks (React-like only) + - path: sdk/hooks/use-stack-app.mdx + platforms: ["next", "react"] # No JS or Python + + - path: sdk/hooks/use-user.mdx + platforms: ["next", "react"] # No JS or Python + + # Snippets (utility files - exclude from Python) + - path: snippets/always-tab-codeblock.mdx + platforms: ["next", "react", "js"] # No Python + + - path: snippets/big-divider.mdx + platforms: ["next", "react", "js"] # No Python + + - path: snippets/divider.mdx + platforms: ["next", "react", "js"] # No Python + + - path: snippets/get-user-or-parameter.mdx + platforms: ["next", "react", "js"] # No Python + + - path: snippets/make-full-page.mdx + platforms: ["next", "react", "js"] # No Python + + - path: snippets/stack-app-constructor-options-after-ssk.mdx + platforms: ["next", "react", "js"] # No Python + + - path: snippets/stack-app-constructor-options-before-ssk.mdx + platforms: ["next", "react", "js"] # No Python + + - path: snippets/use-on-server-callout.mdx + platforms: ["next", "react", "js"] # No Python + + # Others + - path: others/self-host.mdx + platforms: ["next", "react", "js", "python"] # All platforms + + - path: others/supabase.mdx + platforms: ["next"] # Next only + + - path: others/cli-authentication.mdx + platforms: ["python"] # Python only + diff --git a/docs/examples/stack_auth_cli_template.py b/docs/examples/stack_auth_cli_template.py deleted file mode 100644 index 126d19b35..000000000 --- a/docs/examples/stack_auth_cli_template.py +++ /dev/null @@ -1,56 +0,0 @@ -import time -import requests -import webbrowser -import urllib.parse - -def prompt_cli_login( - *, - base_url: str = "https://api.stack-auth.com", - app_url: str, - project_id: str, - publishable_client_key: str, -): - if not app_url: - raise Exception("app_url is required and must be set to the URL of the app you're authenticating with") - if not project_id: - raise Exception("project_id is required") - if not publishable_client_key: - raise Exception("publishable_client_key is required") - - def post(endpoint, json): - return requests.request( - 'POST', - f'{base_url}{endpoint}', - headers={ - 'Content-Type': 'application/json', - 'x-stack-project-id': project_id, - 'x-stack-access-type': 'client', - 'x-stack-publishable-client-key': publishable_client_key, - }, - json=json, - ) - - # Step 1: Initiate the CLI auth process - init = post('/api/v1/auth/cli', { - 'expires_in_millis': 10 * 60 * 1000, - }) - if init.status_code != 200: - raise Exception(f"Failed to initiate CLI auth: {init.status_code} {init.text}") - polling_code = init.json()['polling_code'] - login_code = init.json()['login_code'] - - # Step 2: Open the browser for the user to authenticate - url = f'{app_url}/handler/cli-auth-confirm?login_code={urllib.parse.quote(login_code)}' - print(f"Opening browser to authenticate. If it doesn't open automatically, please visit:\n{url}") - webbrowser.open(url) - - # Step 3: Retrieve the token - while True: - status = post('/api/v1/auth/cli/poll', { - 'polling_code': polling_code, - }) - if status.status_code != 200 and status.status_code != 201: - raise Exception(f"Failed to get CLI auth status: {status.status_code} {status.text}") - if status.json()['status'] == 'success': - return status.json()['refresh_token'] - time.sleep(2) diff --git a/docs/fern/apis/admin/generators.yml b/docs/fern/apis/admin/generators.yml deleted file mode 100644 index bc401e176..000000000 --- a/docs/fern/apis/admin/generators.yml +++ /dev/null @@ -1,2 +0,0 @@ -api: - - path: ../../openapi/admin.yaml \ No newline at end of file diff --git a/docs/fern/apis/client/generators.yml b/docs/fern/apis/client/generators.yml deleted file mode 100644 index 588c1b0d7..000000000 --- a/docs/fern/apis/client/generators.yml +++ /dev/null @@ -1,2 +0,0 @@ -api: - - path: ../../openapi/client.yaml \ No newline at end of file diff --git a/docs/fern/apis/server/generators.yml b/docs/fern/apis/server/generators.yml deleted file mode 100644 index bbcec4bf9..000000000 --- a/docs/fern/apis/server/generators.yml +++ /dev/null @@ -1,2 +0,0 @@ -api: - - path: ../../openapi/server.yaml \ No newline at end of file diff --git a/docs/fern/apis/webhooks/generators.yml b/docs/fern/apis/webhooks/generators.yml deleted file mode 100644 index 2e91fe037..000000000 --- a/docs/fern/apis/webhooks/generators.yml +++ /dev/null @@ -1,2 +0,0 @@ -api: - - path: ../../openapi/webhooks.yaml \ No newline at end of file diff --git a/docs/fern/custom-script.js b/docs/fern/custom-script.js deleted file mode 100644 index af621928a..000000000 --- a/docs/fern/custom-script.js +++ /dev/null @@ -1,60 +0,0 @@ -function initPostHog() { - !function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys getNextSurveyStep onSessionId setPersonProperties".split(" "),n=0;n span:last-child:not(.stack-replaced-link-to)`); - for (const codeSpan of codeBlockSpans) { - codeSpan.classList.add("stack-replaced-link-to"); - if (codeSpan.textContent.trim().startsWith("//$stack-link-to:")) { - const href = codeSpan.textContent.trim().slice(17); - const tr = codeSpan.closest(`tr`); - tr.classList.add(`stack-clickable-row`); - if (href.startsWith("#") && window.location.href.includes("localhost")) { - if (!document.getElementById(href.slice(1))) { - tr.classList.add(`stack-clickable-row-missing`); - } - } - tr.addEventListener(`click`, () => { - window.location.href = href; - }); - codeSpan.remove(); - } - } - } -} - -function replaceEmptyTabs() { - const tabs = document.querySelectorAll(`.small-codeblock-tabs button:not(.stack-replaced-empty-tab)`); - for (const tab of tabs) { - tab.classList.add(`stack-replaced-empty-tab`); - if (tab.textContent.trim() === "<>") { - tab.remove(); - } - } -} - -function runRenderActions() { - try { - replaceStackLinkTo(); - replaceEmptyTabs(); - requestAnimationFrame(runRenderActions); - } catch (e) { - console.error(e); - alert(`Error during custom script while running render actions. This message will only be shown on localhost.\n\n${e} ${e.message} ${e.stack}`); - } -} - -async function main() { - initPostHog(); - runRenderActions(); -} - -main().catch((e) => { - console.error(e); - if (window.location.href.includes("localhost")) { - alert(`Error during custom script. This message will only be shown on localhost.\n\n${e} ${e.message} ${e.stack}`); - } -}); diff --git a/docs/fern/docs-template.yml b/docs/fern/docs-template.yml deleted file mode 100644 index 036d5685f..000000000 --- a/docs/fern/docs-template.yml +++ /dev/null @@ -1,288 +0,0 @@ -tabs: - documentation: - display-name: Documentation - icon: fa-solid fa-home - slug: docs - components: - display-name: Components - icon: fa-solid fa-puzzle - slug: components - platform: react-like - sdk: - display-name: SDK Reference - icon: fa-solid fa-hammer - slug: sdk - api: - display-name: REST API & Webhooks - icon: fa-solid fa-code - slug: rest-api - -navigation: - - tab: documentation - layout: - - page: Overview - icon: fa-regular fa-globe - path: ./docs/pages-{platform}/overview.mdx - - page: FAQ - icon: fa-regular fa-circle-question - path: ./docs/pages-{platform}/faq.mdx - - section: Getting Started - platform: js-like - contents: - - page: Installation & Setup - icon: fa-regular fa-download - path: ./docs/pages-{platform}/getting-started/setup.mdx - - page: Components - icon: fa-regular fa-puzzle - path: ./docs/pages-{platform}/getting-started/components.mdx - platform: react-like - - page: Users - icon: fa-regular fa-address-book - path: ./docs/pages-{platform}/getting-started/users.mdx - - page: Example Pages - icon: fa-regular fa-file-lines - path: ./docs/pages-{platform}/getting-started/example-pages.mdx - platform: js - - page: Going to Production - icon: fa-regular fa-rocket - path: ./docs/pages-{platform}/getting-started/production.mdx - - section: Getting Started - platform: python-like - contents: - - page: Setup - icon: fa-regular fa-download - path: ./docs/pages-{platform}/getting-started/setup.mdx - - section: Concepts - platform: js-like - contents: - - page: The StackApp Object - icon: fa-regular fa-folder-gear - path: ./docs/pages-{platform}/concepts/stack-app.mdx - - page: Custom User Data - icon: fa-regular fa-user-pen - path: ./docs/pages-{platform}/concepts/custom-user-data.mdx - - page: User Onboarding - icon: fa-regular fa-user-check - path: ./docs/pages-{platform}/concepts/user-onboarding.mdx - platform: react-like - - page: Connected OAuth Accounts - icon: fa-regular fa-link - path: ./docs/pages-{platform}/concepts/oauth.mdx - - page: Teams - icon: fa-regular fa-users - path: ./docs/pages-{platform}/concepts/orgs-and-teams.mdx - - page: Selecting a Team - icon: fa-regular fa-exchange - path: ./docs/pages-{platform}/concepts/team-selection.mdx - - page: Permissions & RBAC - icon: fa-regular fa-user-lock - path: ./docs/pages-{platform}/concepts/permissions.mdx - - page: API Keys - icon: fa-regular fa-key - path: ./docs/pages-{platform}/concepts/api-keys.mdx - - page: Webhooks - icon: fa-regular fa-webhook - path: ./docs/pages-{platform}/concepts/webhooks.mdx - - page: Backend Integration - icon: fa-regular fa-network-wired - path: ./docs/pages-{platform}/concepts/backend-integration.mdx - - section: Customization - platform: react-like - contents: - - page: Dark/Light Mode - icon: fa-regular fa-circle-half-stroke - path: ./docs/pages-{platform}/customization/dark-mode.mdx - - page: Colors and Styles - icon: fa-regular fa-paint-brush - path: ./docs/pages-{platform}/customization/custom-styles.mdx - - page: Internationalization - icon: fa-regular fa-language - path: ./docs/pages-{platform}/customization/internationalization.mdx - - page: Custom Layouts and Pages - icon: fa-regular fa-table-layout - path: ./docs/pages-{platform}/customization/custom-pages.mdx - - section: Custom Page Examples - icon: fa-regular fa-files - contents: - - page: Sign In - path: ./docs/pages-{platform}/customization/page-examples/sign-in.mdx - - page: Sign Up - path: ./docs/pages-{platform}/customization/page-examples/sign-up.mdx - - page: Forgot Password - path: ./docs/pages-{platform}/customization/page-examples/forgot-password.mdx - - page: Password Reset - path: ./docs/pages-{platform}/customization/page-examples/password-reset.mdx - - section: Others - platform: js-like - contents: - - page: Supabase Integration - icon: fa-regular fa-bolt - path: ./docs/pages-{platform}/others/supabase.mdx - platform: next - - page: Self-Hosting - icon: fa-regular fa-house-laptop - path: ./docs/pages-{platform}/others/self-host.mdx - - section: Others - platform: python-like - contents: - - page: CLI Authentication - icon: fa-regular fa-terminal - path: ./docs/pages-{platform}/others/cli-authentication.mdx - - tab: components - platform: react-like - layout: - - page: All Components - icon: fa-regular fa-globe - path: ./docs/pages-{platform}/components/overview.mdx - - section: Components - contents: - - page: - icon: fa-solid fa-square-code - path: ./docs/pages-{platform}/components/user-button.mdx - - page: - icon: fa-solid fa-square-code - path: ./docs/pages-{platform}/components/selected-team-switcher.mdx - - page: - icon: fa-solid fa-square-code - path: ./docs/pages-{platform}/components/account-settings.mdx - - page: - icon: fa-solid fa-square-code - path: ./docs/pages-{platform}/components/sign-in.mdx - - page: - icon: fa-solid fa-square-code - path: ./docs/pages-{platform}/components/sign-up.mdx - - page: - icon: fa-solid fa-square-code - path: ./docs/pages-{platform}/components/credential-sign-in.mdx - - page: - icon: fa-solid fa-square-code - path: ./docs/pages-{platform}/components/credential-sign-up.mdx - - page: - icon: fa-solid fa-square-code - path: ./docs/pages-{platform}/components/oauth-button.mdx - - page: - icon: fa-solid fa-square-code - path: ./docs/pages-{platform}/components/oauth-button-group.mdx - - section: Utilities - contents: - - page: - icon: fa-solid fa-square-u - path: ./docs/pages-{platform}/components/stack-handler.mdx - - page: - icon: fa-solid fa-square-u - path: ./docs/pages-{platform}/components/stack-provider.mdx - - page: - icon: fa-solid fa-square-u - path: ./docs/pages-{platform}/components/stack-theme.mdx - - tab: sdk - platform: js-like - layout: - - page: SDK Overview - icon: fa-regular fa-globe - path: ./docs/pages-{platform}/sdk/overview.mdx - - section: Objects - contents: - - section: StackApp - icon: fa-duotone fa-square-t - path: ./docs/pages-{platform}/sdk/objects/stack-app.mdx - contents: - - link: StackClientApp - icon: fa-duotone fa-square-t - href: /{platform}/sdk/objects/stack-app#stackclientapp - - link: StackServerApp - icon: fa-duotone fa-square-t - href: /{platform}/sdk/objects/stack-app#stackserverapp - - section: Types - contents: - - section: ContactChannel - icon: fa-duotone fa-square-t - path: ./docs/pages-{platform}/sdk/types/contact-channel.mdx - contents: - - link: ContactChannel - icon: fa-duotone fa-square-t - href: /sdk/types/contact-channel#contactchannel - - link: ServerContactChannel - icon: fa-duotone fa-square-t - href: /sdk/types/contact-channel#servercontactchannel - - section: API Key - icon: fa-duotone fa-square-t - path: ./docs/pages-{platform}/sdk/types/api-key.mdx - contents: - - link: UserApiKey - icon: fa-duotone fa-square-t - href: /sdk/types/api-key#userapikey - - link: TeamApiKey - icon: fa-duotone fa-square-t - href: /sdk/types/api-key#teamapikey - - page: Project - icon: fa-duotone fa-square-t - path: ./docs/pages-{platform}/sdk/types/project.mdx - - section: Team - icon: fa-duotone fa-square-t - path: ./docs/pages-{platform}/sdk/types/team.mdx - contents: - - link: Team - icon: fa-duotone fa-square-t - href: /sdk/types/team#team - - link: ServerTeam - icon: fa-duotone fa-square-t - href: /sdk/types/team#serverteam - - page: TeamPermission - icon: fa-duotone fa-square-t - path: ./docs/pages-{platform}/sdk/types/team-permission.mdx - - section: TeamProfile - icon: fa-duotone fa-square-t - path: ./docs/pages-{platform}/sdk/types/team-profile.mdx - contents: - - link: TeamProfile - icon: fa-duotone fa-square-t - href: /sdk/types/team-profile#teamprofile - - link: ServerTeamProfile - icon: fa-duotone fa-square-t - href: /sdk/types/team-profile#serverteamprofile - - section: TeamUser - icon: fa-duotone fa-square-t - path: ./docs/pages-{platform}/sdk/types/team-user.mdx - contents: - - link: TeamUser - icon: fa-duotone fa-square-t - href: /sdk/types/team-user#teamuser - - link: ServerTeamUser - icon: fa-duotone fa-square-t - href: /sdk/types/team-user#serverteamuser - - section: User - icon: fa-duotone fa-square-t - path: ./docs/pages-{platform}/sdk/types/user.mdx - contents: - - link: CurrentUser - icon: fa-duotone fa-square-t - href: /sdk/types/user#currentuser - - link: ServerUser - icon: fa-duotone fa-square-t - href: /sdk/types/user#serveruser - - link: CurrentServerUser - icon: fa-duotone fa-square-t - href: /sdk/types/user#currentserveruser - - section: Hooks - platform: react-like - contents: - - page: useStackApp() - icon: fa-duotone fa-square-h - path: ./docs/pages-{platform}/sdk/hooks/use-stack-app.mdx - - page: useUser() - icon: fa-duotone fa-square-h - path: ./docs/pages-{platform}/sdk/hooks/use-user.mdx - - tab: api - layout: - - page: API Overview - icon: fa-regular fa-globe - path: ./docs/pages-{platform}/rest-api/overview.mdx - - api: Client API - slug: client - api-name: client - - api: Server API - slug: server - api-name: server - - api: Webhooks - slug: webhooks - api-name: webhooks diff --git a/docs/fern/docs.yml b/docs/fern/docs.yml deleted file mode 100644 index 129cf1d11..000000000 --- a/docs/fern/docs.yml +++ /dev/null @@ -1,62 +0,0 @@ -instances: - - url: https://stack-auth.docs.buildwithfern.com - custom-domain: docs.stack-auth.com - edit-this-page: - github: - owner: stack-auth - repo: stack - branch: dev/docs -title: Stack Auth Documentation -js: ./custom-script.js - -versions: - - display-name: Next.js SDK - path: next.yml - slug: next - - display-name: React SDK - path: react.yml - slug: react - - display-name: JavaScript SDK - path: js.yml - slug: js - - display-name: Python - path: python.yml - slug: python - -navbar-links: - - type: secondary - text: Discord - url: https://discord.stack-auth.com - - type: secondary - text: GitHub - url: https://github.com/stack-auth/stack-auth - - type: primary - text: Dashboard - url: https://app.stack-auth.com - -colors: - accentPrimary: - light: '#000000' - dark: '#FFFFFF' - background: - light: '#FFFFFF' - dark: '#000000' - sidebar-background: - light: '#FCFCFC' - dark: '#090909' - card-background: - light: '#FCFCFC' - dark: '#090909' -layout: - page-width: full - content-width: 45rem - disable-header: false - header-height: 60px - sidebar-width: 300px -favicon: ./docs/assets/favicon.ico -logo: - light: ./docs/assets/logo-light-mode.svg - dark: ./docs/assets/logo-dark-mode.svg - height: 28 - href: https://stack-auth.com -css: ./style.css diff --git a/docs/fern/docs/assets/favicon.ico b/docs/fern/docs/assets/favicon.ico deleted file mode 100644 index 4e077c535..000000000 Binary files a/docs/fern/docs/assets/favicon.ico and /dev/null differ diff --git a/docs/fern/docs/assets/logo-light-mode.svg b/docs/fern/docs/assets/logo-light-mode.svg deleted file mode 100644 index 93b47b0e6..000000000 --- a/docs/fern/docs/assets/logo-light-mode.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/docs/fern/docs/pages-template/components/account-settings.mdx b/docs/fern/docs/pages-template/components/account-settings.mdx deleted file mode 100644 index 35906c76b..000000000 --- a/docs/fern/docs/pages-template/components/account-settings.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: "<AccountSettings />" -slug: components/account-settings ---- - -Renders an account settings page with customizable sidebar items and optional full-page layout. - -
- AccountSettings -
- -## Props - -- `fullPage` (optional): `boolean` - If true, renders the component in full-page mode. -- `extraItems` (optional): `Array` - Additional items to be added to the sidebar. Each item should have the following properties: - - `title`: `string` - The title of the item. - - `content`: `React.ReactNode` - The content to be rendered for the item. - - `subpath`: `string` - The subpath for the item's route. - - `icon` (optional): `React.ReactNode` - The icon component for the item. only used if `iconName` is not provided. - - `iconName` (optional): `string` - The name of the Lucide icon to be used for the item. only used if `icon` is not provided. - -## Example - -```tsx -import { AccountSettings } from '@stackframe/stack'; - -export default function MyAccountPage() { - return ( - , - subpath: '/custom', - }]} - /> - ); -} -``` diff --git a/docs/fern/docs/pages-template/components/password-reset.mdx b/docs/fern/docs/pages-template/components/password-reset.mdx deleted file mode 100644 index 7da06e7c7..000000000 --- a/docs/fern/docs/pages-template/components/password-reset.mdx +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: "<PasswordReset />" -slug: components/password-reset ---- - -Renders a password reset component based on the provided search parameters and optional full page display. - -## Props - -- `searchParams`: `Record` - An object containing search parameters, including the password reset code. -- `fullPage` (optional): `boolean` - Determines whether to display the component in full page mode. Defaults to `false`. - -## Example - -```tsx title="app/reset-password.tsx" -import { PasswordReset } from '@stackframe/stack'; - -export function ResetPasswordPage(props: { searchParams: Record }) { - return ( - - ); -} -``` \ No newline at end of file diff --git a/docs/fern/docs/pages-template/components/selected-team-switcher.mdx b/docs/fern/docs/pages-template/components/selected-team-switcher.mdx deleted file mode 100644 index 00c7b18e9..000000000 --- a/docs/fern/docs/pages-template/components/selected-team-switcher.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: "<SelectedTeamSwitcher />" -slug: components/selected-team-switcher ---- - -A React component for switching between teams. It displays a dropdown of teams and allows the user to select a team. - -
- SelectedTeamSwitcher -
- -For a comprehensive guide on using this component, refer to our [Team Selection documentation](../concepts/team-selection.mdx). - -## Props - -- `urlMap` (optional): `(team: Team) => string` - A function that maps a team to a URL. If provided, the component will navigate to this URL when a team is selected. -- `selectedTeam` (optional): `Team` - The initially selected team. -- `noUpdateSelectedTeam` (optional): `boolean` - If true, prevents updating the selected team in the user's settings when a new team is selected. Default is false. - -## Example - -```tsx -import { SelectedTeamSwitcher } from '@stackframe/stack'; - -export default function Page() { - return ( -
-

Team Switcher

- `/team/${team.id}`} - selectedTeam={currentTeam} - noUpdateSelectedTeam={false} - /> -
- ); -} -``` diff --git a/docs/fern/docs/pages-template/components/sign-in.mdx b/docs/fern/docs/pages-template/components/sign-in.mdx deleted file mode 100644 index a2a911248..000000000 --- a/docs/fern/docs/pages-template/components/sign-in.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: "<SignIn />" -slug: components/sign-in ---- - -Renders a sign-in component with customizable options. - -
- SignIn -
- -For more information, please refer to the [custom pages guide](../customization/custom-pages.mdx). - -## Props - -- `fullPage` (optional): `boolean` - If true, renders the sign-in page in full-page mode. -- `automaticRedirect` (optional): `boolean` - If true, redirect to afterSignIn/afterSignUp URL when user is already signed in without showing the 'You are signed in' message. -- `extraInfo` (optional): `React.ReactNode` - Additional content to be displayed on the sign-in page. -- `firstTab` (optional): `'magic-link' | 'password'` - Determines which tab is initially active. Defaults to 'magic-link' if not specified. - -## Example - -```tsx -import { SignIn } from '@stackframe/stack'; - -export default function Page() { - return ( -
-

Sign In

- When signing in, you agree to our Terms} - /> -
- ); -} -``` diff --git a/docs/fern/docs/pages-template/components/sign-up.mdx b/docs/fern/docs/pages-template/components/sign-up.mdx deleted file mode 100644 index 324e64b3e..000000000 --- a/docs/fern/docs/pages-template/components/sign-up.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: "<SignUp />" -slug: components/sign-up ---- - -A component that renders a sign-up page with various customization options. - -
- SignUp -
- -For more information, please refer to the [custom pages guide](../customization/custom-pages.mdx). - -## Props - -- `fullPage` (optional): `boolean` - If true, renders the sign-up page in full-page mode. -- `automaticRedirect` (optional): `boolean` - If true, redirect to afterSignIn/afterSignUp URL when user is already signed in without showing the 'You are signed in' message. -- `noPasswordRepeat` (optional): `boolean` - If true, removes the password confirmation field. -- `extraInfo` (optional): `React.ReactNode` - Additional information to display on the sign-up page. -- `firstTab` (optional): `'magic-link' | 'password' - Determines which tab is initially active. Defaults to 'magic-link' if not specified. - -## Example - -```tsx -import { SignUp } from '@stackframe/stack'; - -export default function Page() { - return ( -
-

Sign Up

- By signing up, you agree to our Terms} - /> -
- ); -} -``` diff --git a/docs/fern/docs/pages-template/components/stack-provider.mdx b/docs/fern/docs/pages-template/components/stack-provider.mdx deleted file mode 100644 index f18cf6953..000000000 --- a/docs/fern/docs/pages-template/components/stack-provider.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: "<StackProvider />" -slug: components/stack-provider ---- - -A React component that provides Stack context to its children. - -For detailed usage instructions, please refer to the manual section of the [setup guide](../getting-started/setup.mdx). - - -## Props - -- `children`: `React.ReactNode` - The child components to be wrapped by the StackProvider. -- `app`: `StackClientApp | StackServerApp` - The Stack app instance to be used. -- `lang` (optional): `"en-US" | "de-DE" | "es-419" | "es-ES" | "fr-CA" | "fr-FR" | "it-IT" | "pt-BR" | "pt-PT"` - The language to be used for translations. -- `translationOverrides` (optional): `Record` - A mapping of English translations to translated equivalents. These will take priority over the translations from the language specified in the `lang` property. Note that the keys are case-sensitive. You can find a full list of supported strings [on GitHub](https://github.com/stack-auth/stack-auth/blob/dev/packages/template/src/generated/quetzal-translations.ts). - -## Example - -```tsx title="layout.tsx" -import { StackProvider } from '@stackframe/stack'; -import { stackServerApp } from '@/stack'; - -function App() { - return ( - - {/* Your app content */} - - ); -} -``` diff --git a/docs/fern/docs/pages-template/components/stack-theme.mdx b/docs/fern/docs/pages-template/components/stack-theme.mdx deleted file mode 100644 index a1563c88b..000000000 --- a/docs/fern/docs/pages-template/components/stack-theme.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: "<StackTheme />" -slug: components/stack-theme ---- - -A component that applies a theme to its children. - -For more information, please refer to the [color and styles guide](../customization/custom-styles.mdx). - -## Props - -- `theme` (optional): `ThemeConfig` - Custom theme configuration to override the default theme. -- `children` (optional): `React.ReactNode` - Child components to be rendered within the themed context. - -## Example - -```tsx -const theme = { - light: { - primary: 'red', - }, - dark: { - primary: '#00FF00', - }, - radius: '8px', -} - -// ... - - - {/* children */} - - -``` \ No newline at end of file diff --git a/docs/fern/docs/pages-template/components/user-button.mdx b/docs/fern/docs/pages-template/components/user-button.mdx deleted file mode 100644 index e62538e16..000000000 --- a/docs/fern/docs/pages-template/components/user-button.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: "<UserButton />" -slug: components/user-button ---- - -Renders a user button component with optional user information, color mode toggle, and extra menu items. - -
- UserButton -
- -## Props - -- `showUserInfo`: `boolean` - Whether to display user information (display name and email) or only show the avatar. -- `colorModeToggle`: `() => void | Promise` - Function to be called when the color mode toggle button is clicked. If specified, a color mode toggle button will be shown. -- `extraItems`: `Array<{text: string, icon: React.ReactNode, onClick: Function}>` - Additional menu items to display. - -## Example - -```tsx -'use client'; -import { UserButton } from '@stackframe/stack'; - -export default function Page() { - return ( -
-

User Button

- { console.log("color mode toggle clicked") }} - extraItems={[{ - text: 'Custom Action', - icon: , - onClick: () => console.log('Custom action clicked') - }]} - /> -
- ); -} -``` diff --git a/docs/fern/docs/pages-template/sdk/objects/stack-app.mdx b/docs/fern/docs/pages-template/sdk/objects/stack-app.mdx deleted file mode 100644 index dfefc594e..000000000 --- a/docs/fern/docs/pages-template/sdk/objects/stack-app.mdx +++ /dev/null @@ -1,1094 +0,0 @@ ---- -slug: sdk/objects/stack-app ---- - - -
- -This is a detailed reference for the `StackApp` object. If you're looking for a more high-level overview, please read the [respective page in the Concepts section](../../concepts/stack-app.mdx). - -On this page: -- [StackClientApp](#stackclientapp) -- [StackServerApp](#stackserverapp) - -
- - - -# `StackClientApp` -
- -A [`StackApp`](../../concepts/stack-app.mdx) with client-level permissions. It contains most of the useful methods and hooks for your client-side code. - -{/* IF_PLATFORM: react-like */} -Most commonly you get an instance of `StackClientApp` by calling [`useStackApp()`](../hooks/use-stack-app.mdx) in a Client Component. -{/* END_PLATFORM */} - - - -### Table of Contents -```typescript -type StackClientApp = { - new(options): StackClientApp; //$stack-link-to:#constructor - - getUser([options]): Promise; //$stack-link-to:#stackclientappgetuseroptions - // NEXT_LINE_PLATFORM react-like - ⤷ useUser([options]): User; //$stack-link-to:#stackclientappuseuseroptions - getProject(): Promise; //$stack-link-to:#stackclientappgetproject - // NEXT_LINE_PLATFORM react-like - ⤷ useProject(): Project; //$stack-link-to:#stackclientappuseproject - - signInWithOAuth(provider): void; //$stack-link-to:#stackclientappsigninwithoauthprovider - signInWithCredential([options]): Promise<...>; //$stack-link-to:#stackclientappsigninwithcredentialoptions - signUpWithCredential([options]): Promise<...>; //$stack-link-to:#stackclientappsignupwithcredentialoptions - sendForgotPasswordEmail(email): Promise<...>; //$stack-link-to:#stackclientappsendforgotpasswordemailemail - sendMagicLinkEmail(email): Promise<...>; //$stack-link-to:#stackclientappsendmagiclinkemailemail -}; -``` - -
- - - -## Constructor -
- -Creates a new `StackClientApp` instance. - -Because each app creates a new connection to Stack Auth's backend, you should re-use existing instances wherever possible. - -{/* IF_PLATFORM: react-like */} - -This object is not usually constructed directly. More commonly, you would construct a [`StackServerApp`](#stackserverapp) instead, pass it into a [``](../../components/stack-provider.mdx), and then use `useStackApp()` hook to obtain a `StackClientApp`. - -The [setup wizard](../../getting-started/setup.mdx) does these steps for you, so you don't need to worry about it unless you are manually setting up Stack Auth. - -If you're building a client-only app and don't have a [`SECRET_SERVER_KEY`](../../rest-api/overview#should-i-use-client-or-server-access-type), you can construct a `StackClientApp` directly. - -{/* END_PLATFORM */} -### Parameters - -
- - An object containing multiple properties. - }> - - - - -
- - -
-### Signature - -```typescript -declare new(options: { - tokenStore: "nextjs-cookie" | "cookie" | { accessToken: string, refreshToken: string } | Request; - baseUrl?: string; - projectId?: string; - publishableClientKey?: string; - urls: { - ... - }; - noAutomaticPrefetch?: boolean; -}): StackClientApp; -``` - -### Examples -
- ```typescript Creating a new app - const stackClientApp = new StackClientApp({ - tokenStore: "nextjs-cookie", - baseUrl: "https://api.stack-auth.com", - projectId: "123", - publishableClientKey: "123", - urls: { - home: "/", - }, - }); - ``` - {/* IF_PLATFORM react-like */} - ```typescript Retrieving an app with useStackApp - "use client"; - - function MyReactComponent() { - const stackClientApp = useStackApp(); - } - ``` - {/* END_PLATFORM */} -
-
- -
- - - -## `stackClientApp.getUser([options])` -
- -Gets the current user. - -### Parameters - -
- - An object containing multiple properties. - }> - - - -
- -### Returns - -
- {"Promise<"}[CurrentUser](../types/user.mdx#currentuser){" | null>"}: The current user, or `null` if not signed in. If `or` is `"redirect"` or `"throw"`, never returns `null`. -
- -
-### Signature -```typescript -declare function getUser( - options: { - or?: "return-null" | "redirect" | "throw" - } -): Promise; -``` - -### Examples -
- ```typescript Getting the current user -const userOrNull = await stackClientApp.getUser(); -console.log(userOrNull); // null if not signed in - -const user = await stackClientApp.getUser({ or: "redirect" }); -console.log(user); // always defined; redirects to sign-in page if not signed in - ``` -
-
- -
- - - -{/* IF_PLATFORM react-like */} -## `stackClientApp.useUser([options])` -
- -Functionally equivalent to [`getUser()`](#stackclientappgetuseroptions), but as a React hook. - -{/* IF_PLATFORM: react-like */} -Equivalent to the [`useUser()`](../hooks/use-user.mdx) standalone hook (which is an alias for `useStackApp().useUser()`). -{/* END_PLATFORM */} - -### Parameters - -
- - An object containing multiple properties. - }> - - - -
- -### Returns - -
- [CurrentUser](../types/user.mdx#currentuser){" | null"}: The current user, or `null` if not signed in. If `or` is `"redirect"` or `"throw"`, never returns `null`. -
- -
-### Signature -```typescript -declare function useUser( - options: { - or?: "return-null" | "redirect" | "throw" - } -): CurrentUser | null; -``` - -### Examples -
-```jsx Displaying the current user's username -"use client"; - -function MyReactComponent() { - // useUser(...) is an alias for useStackApp().useUser(...) - const user = useUser(); - return user ?
Hello, {user.name}
- :
Not signed in
; -} -``` - -```tsx Redirecting vs. not redirecting -"use client"; - -function MyReactComponent() { - const user = useUser(); - console.log(user); // null if not signed in - - const user = useUser({ or: "redirect" }); // redirects to sign-in page if necessary - console.log(user); // always defined - - const user = useUser({ or: "throw" }); // throws an error if not signed in - console.log(user); // always defined -``` - -```tsx Protecting a page client-side -"use client"; - -function MyProtectedComponent() { - // Note: This component is protected on the client-side. - // It does not protect against malicious users, since - // they can just comment out the `useUser` call in their - // browser's developer console. - // - // The purpose of client-side protection is to redirect - // unauthenticated users to the sign-in page, not to - // hide secret information from them. - // - // For more information on protecting pages and how to - // protect a page server-side or in the middleware, see - // the Stack Auth documentation: - // https://docs.stack-auth.com/getting-started/users#protecting-a-page - - useUser({ or: "redirect" }); - return
You can only see this if you are authenticated
; -} -``` -
-
- -
- - -{/* END_PLATFORM */} - -## `stackClientApp.getProject()` -
- -Gets the current project. - -### Parameters - -
- No parameters. -
- -### Returns - -
- `Promise`: The current project. -
- -
-### Signature -```typescript -declare function getProject(): Promise; -``` - -### Examples -
-```typescript Getting the current project -const project = await stackClientApp.getProject(); -``` -
-
- -
- - - -{/* IF_PLATFORM react-like */} -## `stackClientApp.useProject()` -
- -Functionally equivalent to [`getProject()`](#stackclientappgetproject), but as a React hook. - -### Parameters - -
- No parameters. -
- -### Returns - -
- `Project`: The current project. -
- -
-### Signature -```typescript -declare function useProject(): Project; -``` - -### Examples -
-```typescript Getting the current project in a React component -function MyReactComponent() { - const project = useProject(); -} -``` -
-
- -
- - -{/* END_PLATFORM */} - -## `stackClientApp.signInWithOAuth(provider)` -
- -Initiates the OAuth sign-in process with the specified provider. This method: - -1. Redirects the user to the OAuth provider's sign-in page. -2. After successful authentication, redirects the user back to your application. -3. The final redirect destination is determined as follows: - - If an `after_auth_return_to` query parameter was provided when calling this function, it uses that URL. - - Otherwise, it uses the `afterSignIn` URL configured in the app settings. - -### Parameters - -
- - The type of the OAuth provider to sign in with. - -
- -### Returns - -
- `Promise`. -
- -
-### Signature -```typescript -declare function signInWithOAuth(provider: string): Promise; -``` - -### Examples -
-```typescript Signing in with Google -await stackClientApp.signInWithOAuth("google"); -``` -
-
- -
- - - -## `stackClientApp.signInWithCredential([options])` -
- -Sign in using email and password credentials. The behavior is as follows: - -1. If sign-in is successful: - - By default, redirects the user to the `afterSignIn` URL. - - If `after_auth_return_to` is provided in the query parameters, redirects to that URL instead. - - If `noRedirect` is set to `true`, it will not redirect and instead return a success `Result` object. - -2. If sign-in fails: - - No redirection occurs. - - Returns a `Result` object containing error information. - -### Parameters - -
- - An object containing multiple properties. - }> - - The email of the user to sign in with. - - - The password of the user to sign in with. - - - Whether to not redirect the user after sign-in. Defaults to `false`. - - - -
- -### Returns - -
- `Promise>`: A promise that resolves to a `Result` object. -
- - -
-### Signature -```typescript -declare function signInWithCredential(options: { - email: string; - password: string; - noRedirect?: boolean; -}): Promise>; -``` - -### Examples -
-```typescript Signing in with email and password -const result = await stackClientApp.signInWithCredential({ - email: "test@example.com", - password: "password", -}); - -if (result.status === "error") { - console.error("Sign in failed", result.error.message); -} -``` -
-
- -
- - - -## `stackClientApp.signUpWithCredential([options])` -
- -Sign up using email and password credentials. The behavior is as follows: - -1. If sign-up is successful: - - By default, redirects the user to the `afterSignUp` URL. - - If `after_auth_return_to` is provided in the query parameters, redirects to that URL instead. - - If `noRedirect` is set to `true`, it will not redirect and instead return a success `Result` object. - -2. If sign-up fails: - - No redirection occurs. - - Returns a `Result` object containing error information. - -### Parameters - -
- - An object containing multiple properties. - }> - - The email of the user to sign up with. - - - The password of the user to sign up with. - - - Whether to not redirect the user after sign-up. Defaults to `false`. - - - -
- -### Returns - -
- `Promise>`: A promise that resolves to a `Result` object. -
- -
-### Signature -```typescript -declare function signUpWithCredential(options: { - email: string; - password: string; - noRedirect?: boolean; -}): Promise>; -``` - -### Examples - -
-```typescript Signing up with email and password -const result = await stackClientApp.signUpWithCredential({ - email: "test@example.com", - password: "password", -}); - -if (result.status === "error") { - console.error("Sign up failed", result.error.message); -} -``` -
-
- -
- - - -## `stackClientApp.sendForgotPasswordEmail(email)` -
- -Send a forgot password email to an email address. - -### Parameters - -
- - The email of the user to send the forgot password email to. - -
- -### Returns - -
- `Promise>`: A promise that resolves to a `Result` object. -
- -
-### Signature -```typescript -declare function sendForgotPasswordEmail(email: string): Promise>; -``` - -### Examples -
-```typescript Sending a forgot password email -const result = await stackClientApp.sendForgotPasswordEmail("test@example.com"); - -if (result.status === "success") { - console.log("Forgot password email sent"); -} else { - console.error("Failed to send forgot password email", result.error.message); -} -``` -
-
- -
- - - -## `stackClientApp.sendMagicLinkEmail(email)` -
- -Send a magic link/OTP sign-in email to an email address. - -### Parameters - -
- - The email of the user to send the magic link email to. - -
- -### Returns - -
- `Promise>`: A promise that resolves to a `Result` object. -
- -
-### Signature -```typescript -declare function sendMagicLinkEmail(email: string): Promise>; -``` - -### Examples - -
-```typescript Sending a magic link email -const result = await stackClientApp.sendMagicLinkEmail("test@example.com"); -``` -
-
- -
- - - - -# `StackServerApp` -
- -Like `StackClientApp`, but with [server permissions](../../concepts/stack-app.mdx#client-vs-server). Has full read and write access to all users. - -Since this functionality should only be available in environments you trust (ie. your own server), it requires a [`SECRET_SERVER_KEY`](../../rest-api/overview.mdx). In some cases, you may want to use a `StackServerApp` on the client; an example for this is an internal dashboard that only your own employees have access to. We generally recommend against doing this unless you are aware of and protected against the (potentially severe) security implications of exposing `SECRET_SERVER_KEY` on the client. - -### Table of Contents - -```typescript -type StackServerApp = - // Inherits all functionality from StackClientApp - & StackClientApp //$stack-link-to:#stackclientapp - & { - new(options): StackServerApp; //$stack-link-to:#constructor-1 - - getUser([id][, options]): Promise; //$stack-link-to:#stackserverappgetuserid-options - // NEXT_LINE_PLATFORM react-like - ⤷ useUser([id][, options]): ServerUser; //$stack-link-to:#stackserverappuseuserid-options - listUsers([options]): Promise; //$stack-link-to:#stackserverapplistusersoptions - // NEXT_LINE_PLATFORM react-like - ⤷ useUsers([options]): ServerUser[]; //$stack-link-to:#stackserverappuseusersoptions - createUser([options]): Promise; //$stack-link-to:#stackserverappcreateuseroptions - - getTeam(id): Promise; //$stack-link-to:#stackserverappgetteamid - // NEXT_LINE_PLATFORM react-like - ⤷ useTeam(id): ServerTeam; //$stack-link-to:#stackserverappuseteamid - listTeams(): Promise; //$stack-link-to:#stackserverapplistteams - // NEXT_LINE_PLATFORM react-like - ⤷ useTeams(): ServerTeam[]; //$stack-link-to:#stackserverappuseteams - createTeam([options]): Promise; //$stack-link-to:#stackserverappcreateteamoptions - } -``` - -
- - - -## Constructor -
- -### Parameters - -
- - An object containing multiple properties. - }> - - - The secret server key of the app, as found on Stack Auth's dashboard. Defaults to the value of the `SECRET_SERVER_KEY` environment variable. - - - - -
- -
-### Signature -```typescript -declare new(options: { - tokenStore: "nextjs-cookie" | "cookie" | { accessToken: string, refreshToken: string } | Request; - baseUrl?: string; - projectId?: string; - publishableClientKey?: string; - secretServerKey?: string; - urls: { - ... - }; - noAutomaticPrefetch?: boolean; -}): StackServerApp; - -``` - -### Examples -
-```typescript Create a StackServerApp with a custom sign-in page -const stackServerApp = new StackServerApp({ - tokenStore: "nextjs-cookie", - urls: { - signIn: '/my-custom-sign-in-page', - }, -}); -``` -
-
- -
- - - -## `stackServerApp.getUser([id], [options])` -
- -Similar to `StackClientApp.getUser()`, but: - -- Returns a [`ServerUser`](../types/user.mdx#serveruser), which is more powerful -- Has an additional overload that takes an `id` parameter instead of `options`, returning the user with the given ID instead of the current user. - -### Parameters - -
- - The ID of the user to get. - - - An object containing multiple properties. - }> - - - -
- -### Returns - -
- {"Promise<"}[ServerUser](../types/user.mdx#serveruser){" | null>"}: The user, or null if not found. If `id` is not provided, returns a [`CurrentServerUser`](../types/user.mdx#currentserveruser). - - If `id` is not provided and `or` is `"redirect"` or `"throw"`, never returns `null`. -
- - -
-### Signature -```typescript -// This function has two overloads: -declare function getUser(id: string): Promise; -declare function getUser( - options: { - or?: "return-null" | "redirect" | "throw" - } -): Promise; -``` - -### Examples -
-```typescript Get the current user on the server -const user = await stackServerApp.getUser(); -console.log(user); // CurrentServerUser -``` - -```typescript Get a user by ID -const user = await stackServerApp.getUser("12345678-1234-1234-1234-123456789abc"); -console.log(user); // ServerUser -``` -
-
- -
- - - -{/* IF_PLATFORM react-like */} -## `stackServerApp.useUser([id][, options])` -
- - - -Functionally equivalent to [`getUser()`](#stackserverappgetuserid-options), but as a React hook. - -
- - -{/* END_PLATFORM */} - -## `stackServerApp.listUsers([options])` -
- -Lists all users on the project. - -### Parameters - -
- - An object containing multiple properties. - }> - - The cursor to start the result set from. - - - - The maximum number of items to return. If not provided, it will return all users. - - - - The field to sort the results by. Currently, only `signedUpAt` is supported. - - - - Whether to sort the results in descending order. - - - - A query to filter the results by. This is a free-text search on the user's display name and emails. - - - -
- -### Returns - -
- {"Promise<"}[ServerUser](../types/user.mdx#serveruser){"[]>"}: The list of users. The array has an additional `nextCursor` property, which is the cursor to the next page of users if `limit` is provided. -
- -
-### Signature -```typescript -declare function listUsers(options: { - cursor?: string; - limit?: number; - orderBy?: "signedUpAt"; - desc?: boolean; - query?: string; -}): Promise; -``` - -### Examples - -
-```typescript List all users -const users = await stackServerApp.listUsers({ limit: 20 }); -console.log(users); - -if (users.nextCursor) { - const nextPageUsers = await stackServerApp.listUsers({ cursor: users.nextCursor, limit: 20 }); - console.log(nextPageUsers); -} -``` -
-
- -
- - - -{/* IF_PLATFORM react-like */} -## `stackServerApp.useUsers([options])` -
- - - -Functionally equivalent to [`listUsers()`](#stackserverapplistusersoptions), but as a React hook. - -
- - -{/* END_PLATFORM */} -## `stackServerApp.createUser([options])` -
- -Creates a new user from the server. - -Note that this function is meant for server-side code; on the client, use any of the `signInWithXyz` or `signUpWithXyz` functions instead. - -### Parameters - -
- - An object containing multiple properties. - }> - - The primary email of the user to create. - - - - Whether the primary email is verified. Defaults to `false`. - - - - Whether the primary email is enabled. When using password or otp auth, this must be set to `true`, otherwise the user will not be able to sign in. - - - - The password for the new user. An error will be thrown if a password is provided - but password authentication is not enabled for the project in the dashboard. - - - - Enables OTP (One-Time Password) or magic link sign-in using the primary email. - Note: Only verified emails can be used for OTP sign-in. An error will be thrown - if set to `true` when OTP authentication is not enabled in the dashboard. - - - - The display name of the user to create. - - - -
- -### Returns - -
- `Promise`: The created user. -
- -
-### Signature -```typescript -declare function createUser(options: { - primaryEmail?: string; - primaryEmailVerified?: boolean; - primaryEmailAuthEnabled?: boolean; - password?: string; - otpAuthEnabled?: boolean; - displayName?: string; -}): Promise; -``` - -### Examples - -
-```typescript Create a user with password auth -const user = await stackServerApp.createUser({ - primaryEmail: "test@example.com", - primaryEmailAuthEnabled: true, - password: "password123", -}); -``` - -```typescript Create a user with magic link auth -const user = await stackServerApp.createUser({ - primaryEmail: "test@example.com", - primaryEmailVerified: true, - primaryEmailAuthEnabled: true, - otpAuthEnabled: true, -}); -``` -
-
- -
- - - - -## `stackServerApp.getTeam(id)` -
- -Get a team by its ID. - -### Parameters - -
- - The ID of the team to get. - -
- -### Returns - -
- `Promise`: The team, or null if not found. -
- -
-### Signature -```typescript -declare function getTeam(id: string): Promise; -``` - -### Examples - -
-```typescript Get a team by ID -const team = await stackServerApp.getTeam("team_id_123"); -console.log(team); // null if not found -``` -
-
- -
- - - -{/* IF_PLATFORM react-like */} -## `stackServerApp.useTeam(id)` -
- - - -Functionally equivalent to [`getTeam(id)`](#stackserverappgetteamid), but as a React hook. - -
- - -{/* END_PLATFORM */} - -## `stackServerApp.listTeams()` -
-Lists all teams on the current project. - -### Parameters - -
- None. -
- -### Returns - -
- `Promise`: All teams on the current project. -
- -
-### Signature -```typescript -declare function listTeams(): Promise; -``` - -### Examples - -
-```typescript List all teams -const teams = await stackServerApp.listTeams(); -console.log(teams); -``` -
-
- -
- - - -{/* IF_PLATFORM react-like */} -## `stackServerApp.useTeams()` - -
- - - -Functionally equivalent to [`listTeams()`](#stackserverapplistteams), but as a React hook. - -
- - -{/* END_PLATFORM */} - -## `stackServerApp.createTeam([options])` -
- -Creates a team. - -This is different from `user.createTeam()` because it does not add a user to the team. The newly created team will not have a creator. - -### Parameters - -
- - }> - - The display name for the team. - - - The URL of the team's profile image (base64 image allowed, crop and compress before passing it in), or null to remove. - - - -
- -### Returns - -
- `Promise`: The created team. -
- -
-### Signature -```typescript -declare function createTeam(data: { - displayName: string; - profileImageUrl?: string | null; -}): Promise; -``` - -### Examples - -
-```typescript Create a team -const team = await stackServerApp.createTeam({ - displayName: "New Team", - profileImageUrl: "https://example.com/profile.jpg", -}); -``` -
- - - -
diff --git a/docs/fern/docs/pages-template/sdk/overview.mdx b/docs/fern/docs/pages-template/sdk/overview.mdx deleted file mode 100644 index ab78f95ba..000000000 --- a/docs/fern/docs/pages-template/sdk/overview.mdx +++ /dev/null @@ -1,82 +0,0 @@ ---- -slug: sdk ---- - -This is the SDK reference for Stack Auth's Next.js SDK. - -For a list of components, see the [Components](../components) page. For instructions on how to get started and how to use the SDK, see the [Setup & Installation](../getting-started/setup.mdx) page. If you are using a framework or programming language other than Next.js, you can use [our REST API](../rest-api). - - - - -
-

General

- -
[StackClientApp](./sdk/objects/stack-app#stackclientapp)
-
[StackServerApp](./sdk/objects/stack-app#stackserverapp)
-
- -
[Project](./sdk/types/project#project)
-
-
- -

Users & user data

- -
[CurrentUser](./sdk/types/user#currentuser)
-
[ServerUser](./sdk/types/user#serveruser)
-
[CurrentServerUser](./sdk/types/user#currentserveruser)
- -
[ContactChannel](./sdk/types/contact-channel#contactchannel)
-
[ServerContactChannel](./sdk/types/contact-channel#servercontactchannel)
-
- -

Teams

- -
[Team](./sdk/types/team#team)
-
[ServerTeam](./sdk/types/team#serverteam)
-
- -
[TeamPermission](./sdk/types/team-permission#teampermission)
-
[ServerTeamPermission](./sdk/types/team-permission#serverteampermission)
-
- -
[TeamUser](./sdk/types/team-user#teamuser)
-
[ServerTeamUser](./sdk/types/team-user#serverteamuser)
-
- -
[TeamProfile](./sdk/types/team-profile#teamprofile)
-
[ServerTeamProfile](./sdk/types/team-profile#serverteamprofile)
-
- -

Hooks

-
[useStackApp](./sdk/hooks/use-stack-app)
-
[useUser](./sdk/hooks/use-user)
-
- -
diff --git a/docs/fern/docs/pages-template/sdk/types/api-key.mdx b/docs/fern/docs/pages-template/sdk/types/api-key.mdx deleted file mode 100644 index 1d9b07e0e..000000000 --- a/docs/fern/docs/pages-template/sdk/types/api-key.mdx +++ /dev/null @@ -1,465 +0,0 @@ - ---- -slug: sdk/types/api-key ---- - - -
- -`ApiKey` represents an authentication token that allows programmatic access to your application's backend. API keys can be associated with individual users or teams. - -On this page: -- [`ApiKey`](#apikey) -- Types: - - [`UserApiKey`](#userapikey) - - [`TeamApiKey`](#teamapikey) - -
- - -# `ApiKey` -
- -API keys provide a way for users to authenticate with your backend services without using their primary credentials. They can be created for individual users or for teams, allowing programmatic access to your application. - -API keys can be obtained through: -- [`user.createApiKey()`](../types/user.mdx#currentusercreateapikeyoptions) -- [`user.listApiKeys()`](../types/user.mdx#currentuserlistapikeys) -- [`user.useApiKeys()`](../types/user.mdx#currentuseruseapikeys) (React hook) -- [`team.createApiKey()`](../types/team.mdx#teamcreateapikeyoptions) -- [`team.listApiKeys()`](../types/team.mdx#teamlistapikeys) -- [`team.useApiKeys()`](../types/team.mdx#teamuseapikeys) (React hook) - -### Table of Contents - -```typescript -type ApiKey = { - id: string; //$stack-link-to:#apikeyid - description: string; //$stack-link-to:#apikeydescription - expiresAt?: Date; //$stack-link-to:#apikeyexpiresat - manuallyRevokedAt: Date | null; //$stack-link-to:#apikeymanuallyrevokedat - createdAt: Date; //$stack-link-to:#apikeycreatedat - value: IsFirstView extends true ? string : { lastFour: string }; //$stack-link-to:#apikeyvalue - - // User or Team properties based on Type - ...(Type extends "user" ? { - type: "user"; - userId: string; //$stack-link-to:#apikeyuserid - } : { - type: "team"; - teamId: string; //$stack-link-to:#apikeyteamid - }) - - // Methods - isValid(): boolean; //$stack-link-to:#apikeyisvalid - whyInvalid(): "manually-revoked" | "expired" | null; //$stack-link-to:#apikeywhyinvalid - revoke(): Promise; //$stack-link-to:#apikeyrevoke - update(options): Promise; //$stack-link-to:#apikeyupdateoptions -}; -``` - -
- - -## `apiKey.id` -
- -The unique identifier for this API key. - -
-### Type Definition - -```typescript -declare const id: string; -``` -
- -
- - -## `apiKey.description` -
- -A human-readable description of the API key's purpose. - -
-### Type Definition - -```typescript -declare const description: string; -``` -
- -
- - -## `apiKey.expiresAt` -
- -The date and time when this API key will expire. If not set, the key does not expire. - -
-### Type Definition - -```typescript -declare const expiresAt?: Date; -``` -
- -
- - -## `apiKey.manuallyRevokedAt` -
- -The date and time when this API key was manually revoked. If null, the key has not been revoked. - -
-### Type Definition - -```typescript -declare const manuallyRevokedAt: Date | null; -``` -
- -
- - -## `apiKey.createdAt` -
- -The date and time when this API key was created. - -
-### Type Definition - -```typescript -declare const createdAt: Date; -``` -
- -
- - -## `apiKey.value` -
- -The value of the API key. When the key is first created, this is the full API key string. After that, only the last four characters are available for security reasons. - -
-### Type Definition - -```typescript -// On first creation -declare const value: string; - -// On subsequent retrievals -declare const value: { lastFour: string }; -``` -
- -
- - -## `apiKey.userId` -
- -For user API keys, the ID of the user that owns this API key. - -
-### Type Definition - -```typescript -declare const userId: string; -``` -
- -
- - -## `apiKey.teamId` -
- -For team API keys, the ID of the team that owns this API key. - -
-### Type Definition - -```typescript -declare const teamId: string; -``` -
- -
- - -## `apiKey.isValid()` -
- -Checks if the API key is still valid (not expired and not revoked). - -### Parameters - -
- None. -
- -### Returns - -
- `boolean`: True if the key is valid, false otherwise. -
- -
-### Signature - -```typescript -declare function isValid(): boolean; -``` - -### Examples - -
-```typescript Checking if an API key is valid -if (apiKey.isValid()) { - console.log("API key is still valid"); -} else { - console.log("API key is invalid"); -} -``` -
-
- -
- - -## `apiKey.whyInvalid()` -
- -Returns the reason why the API key is invalid, or null if it is valid. - -### Parameters - -
- None. -
- -### Returns - -
- `"manually-revoked" | "expired" | null`: The reason the key is invalid, or null if it's valid. -
- -
-### Signature - -```typescript -declare function whyInvalid(): "manually-revoked" | "expired" | null; -``` - -### Examples - -
-```typescript Checking why an API key is invalid -const reason = apiKey.whyInvalid(); -if (reason) { - console.log(`API key is invalid because it was ${reason}`); -} else { - console.log("API key is valid"); -} -``` -
-
- -
- - -## `apiKey.revoke()` -
- -Revokes the API key, preventing it from being used for authentication. - -### Parameters - -
- None. -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function revoke(): Promise; -``` - -### Examples - -
-```typescript Revoking an API key -await apiKey.revoke(); -console.log("API key has been revoked"); -``` -
-
- -
- - -## `apiKey.update(options)` -
- -Updates the API key properties. - -### Parameters - -
- - An object containing properties for updating. - }> - - A new description for the API key. - - - A new expiration date, or null to remove the expiration. - - - Set to true to revoke the API key. - - - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function update(options: { - description?: string; - expiresAt?: Date | null; - revoked?: boolean; -}): Promise; -``` - -### Examples - -
-```typescript Updating an API key -await apiKey.update({ - description: "Updated description", - expiresAt: new Date(Date.now() + 60 * 24 * 60 * 60 * 1000), // 60 days -}); -``` -
-
- -
- - -# Types - -
- -## `UserApiKey` - -A type alias for an API key owned by a user: - -```typescript -type UserApiKey = ApiKey<"user", false>; -``` - -## `UserApiKeyFirstView` - -A type alias for a newly created user API key, which includes the full key value: - -```typescript -type UserApiKeyFirstView = ApiKey<"user", true>; -``` - -## `TeamApiKey` - -A type alias for an API key owned by a team: - -```typescript -type TeamApiKey = ApiKey<"team", false>; -``` - -## `TeamApiKeyFirstView` - -A type alias for a newly created team API key, which includes the full key value: - -```typescript -type TeamApiKeyFirstView = ApiKey<"team", true>; -``` - -
- - -# Creation options - -
- -When creating an API key using [`user.createApiKey()`](../types/user.mdx#currentusercreatekeyoptions) or [`team.createApiKey()`](../types/team.mdx#teamcreatekeyoptions), you need to provide an options object with the following properties: - -```typescript -type ApiKeyCreationOptions = { - description: string; - expiresAt: Date | null; - isPublic?: boolean; -}; -``` - -### Properties - -
- - A human-readable description of the API key's purpose. - - - The date when the API key will expire. Use null for keys that don't expire. - - - Whether the API key is public. Defaults to false. - - - **Secret API Keys** (default) are monitored by Stack Auth's secret scanner, which can revoke them if detected in public code repositories. - - **Public API Keys** are designed for client-side code where exposure is not a concern. - -
- -
-### Examples - -
-```typescript Creating a user API key -// Get the current user -const user = await stackApp.getUser(); - -// Create a secret API key (default) -const secretKey = await user.createApiKey({ - description: "Backend integration", - expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90 days - isPublic: false, -}); - -// Create a public API key -const publicKey = await user.createApiKey({ - description: "Client-side access", - expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days - isPublic: true, -}); -``` -
-
- -
diff --git a/docs/fern/docs/pages-template/sdk/types/connected-account.mdx b/docs/fern/docs/pages-template/sdk/types/connected-account.mdx deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/fern/docs/pages-template/sdk/types/contact-channel.mdx b/docs/fern/docs/pages-template/sdk/types/contact-channel.mdx deleted file mode 100644 index a8d015015..000000000 --- a/docs/fern/docs/pages-template/sdk/types/contact-channel.mdx +++ /dev/null @@ -1,360 +0,0 @@ ---- -slug: sdk/types/contact-channel ---- - - -
- -`ContactChannel` represents a user's contact information, such as an email address or phone number. Some auth methods, like OTP/magic link or password, use contact channels for authentication. - -On this page: - -- [`ContactChannel`](#contactchannel) -- [`ServerContactChannel`](#servercontactchannel) -
- - -# `ContactChannel` -
- -Basic information about a contact channel, as seen by a user themselves. - -Usually obtained by calling [`user.listContactChannels()`](../types/user.mdx#currentuserlistcontactchannels) -or [`user.useContactChannels()`](../types/user.mdx#currentuserusecontactchannels) {/* THIS_LINE_PLATFORM react-like */} -. - -### Table of Contents - -```typescript -type ContactChannel = { - id: string; //$stack-link-to:#contactchannelid - value: string; //$stack-link-to:#contactchannelvalue - type: 'email'; //$stack-link-to:#contactchanneltype - isPrimary: boolean; //$stack-link-to:#contactchannelisprimary - isVerified: boolean; //$stack-link-to:#contactchannelisverified - usedForAuth: boolean; //$stack-link-to:#contactchannelusedforauth - - sendVerificationEmail(): Promise; //$stack-link-to:#contactchannelsendverificationemail - update(options): Promise; //$stack-link-to:#contactchannelupdateoptions - delete(): Promise; //$stack-link-to:#contactchanneldelete -}; -``` - - -
- - -## `contactChannel.id` -
- -The id of the contact channel as a `string`. - -
-### Type Definition - -```typescript -declare const id: string; -``` -
- -
- - -## `contactChannel.value` -
- -The value of the contact channel. If type is `"email"`, this is an email address. - -
-### Type Definition - -```typescript -declare const value: string; -``` -
- -
- - -## `contactChannel.type` -
- -The type of the contact channel. Currently always `"email"`. - -
-### Type Definition - -```typescript -declare const type: 'email'; -``` -
- -
- - -## `contactChannel.isPrimary` -
- -Indicates whether the contact channel is the user's primary contact channel. If an email is set to primary, it will be the value on the `user.primaryEmail` field. - -
-### Type Definition - -```typescript -declare const isPrimary: boolean; -``` -
- -
- - -## `contactChannel.isVerified` -
- -Indicates whether the contact channel is verified. - -
-### Type Definition - -```typescript -declare const isVerified: boolean; -``` -
- -
- - -## `contactChannel.usedForAuth` -
- -Indicates whether the contact channel is used for authentication. If set to `true`, the user can use this contact channel with OTP or password to sign in. - -
-### Type Definition - -```typescript -declare const usedForAuth: boolean; -``` -
- -
- - -## `contactChannel.sendVerificationEmail()` -
- -Sends a verification email to this contact channel. Once the user clicks the verification link in the email, the contact channel will be marked as verified. - -### Parameters - -
- None. -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function sendVerificationEmail(): Promise; -``` - -### Examples - -
-```typescript Sending verification email -await contactChannel.sendVerificationEmail(); -``` -
-
- -
- - -## `contactChannel.update(options)` -
- -Updates the contact channel. After updating the value, the contact channel will be marked as unverified. - -### Parameters - -
- - An object containing properties for updating. - }> - - The new value of the contact channel. - - - The new type of the contact channel. Currently always `"email"`. - - - Indicates whether the contact channel is used for authentication. - - - Indicates whether the contact channel is the user's primary contact channel. - - - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function update(options: { - value?: string; - type?: 'email'; - usedForAuth?: boolean; - isPrimary?: boolean; -}): Promise; -``` - -### Examples - -
-```typescript Updating contact channel -await contactChannel.update({ - value: "new-email@example.com", - usedForAuth: true, -}); -``` -
-
- -
- - -## `contactChannel.delete()` -
- -Deletes the contact channel. - -### Parameters - -
- None. -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function delete(): Promise; -``` - -### Examples - -
-```typescript Deleting contact channel -await contactChannel.delete(); -``` -
-
- -
- - -# `ServerContactChannel` -
- -Like `ContactChannel`, but includes additional methods and properties that require the `SECRET_SERVER_KEY`. - -Usually obtained by calling [`serverUser.listContactChannels()`](../types/user.mdx#serveruserlistcontactchannels) -or [`serverUser.useContactChannels()`](../types/user.mdx#serveruserusecontactchannels) {/* THIS_LINE_PLATFORM react-like */} -. - -### Table of Contents - -```typescript -type ServerContactChannel = - // Inherits all properties from ContactChannel - & ContactChannel //$stack-link-to:#contactchannel - & { - update(options): Promise; //$stack-link-to:#servercontactchannelupdateoptions - }; -``` - -
- - -## `serverContactChannel.update(options)` -
- -Updates the contact channel. - -This method is similar to the one on `ContactChannel`, but also allows setting the `isVerified` property. - -### Parameters - -
- - An object containing properties for updating. - }> - - The new value of the contact channel. - - - The new type of the contact channel. Currently always `"email"`. - - - Indicates whether the contact channel is used for authentication. - - - Indicates whether the contact channel is verified. - - - Indicates whether the contact channel is the user's primary contact channel. - - - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function update(options: { - value?: string; - type?: 'email'; - usedForAuth?: boolean; - isVerified?: boolean; - isPrimary?: boolean; -}): Promise; -``` - -### Examples - -
-```typescript Updating server contact channel -await serverContactChannel.update({ - value: "new-email@example.com", - usedForAuth: true, - isVerified: true, -}); -``` -
-
diff --git a/docs/fern/docs/pages-template/sdk/types/project.mdx b/docs/fern/docs/pages-template/sdk/types/project.mdx deleted file mode 100644 index 120575d88..000000000 --- a/docs/fern/docs/pages-template/sdk/types/project.mdx +++ /dev/null @@ -1,99 +0,0 @@ ---- -slug: sdk/types/project ---- - - -
- -The `Project` object contains the information and configuration of a project, such as the name, description, and enabled authentication methods. - -Each [Stack app](../../concepts/stack-app.mdx) corresponds to a project. You can obtain its `Project` object by calling [`stackApp.getProject()`](../objects/stack-app.mdx#stackappgetproject) -or [`stackApp.useProject()`](../objects/stack-app.mdx#stackappuseproject) {/* THIS_LINE_PLATFORM react-like */} -. - -### Table of Contents - -```typescript -type Project = { - id: string; //$stack-link-to:#projectid - displayName: string; //$stack-link-to:#projectdisplayname - config: { ... }; //$stack-link-to:#projectconfig -}; -``` - -
- - -## `project.id` -
- -The unique ID of the project as a `string`. - -
-### Type Definition - -```typescript -declare const id: string; -``` -
- -
- - -## `project.displayName` -
- -The display name of the project as a `string`. - -
-### Type Definition - -```typescript -declare const displayName: string; -``` -
- -
- - -## `project.config` -
- -The configuration settings for the project. - -}> - - Indicates if sign-up is enabled for the project. - - - - Specifies if credential-based authentication is enabled for the project. - - - - States whether magic link authentication is enabled for the project. - - - - Determines if client-side team creation is permitted within the project. - - - - Indicates if client-side user deletion is enabled for the project. - - - -
-### Type Definition - -```typescript -declare const config: { - signUpEnabled: boolean; - credentialEnabled: boolean; - magicLinkEnabled: boolean; - clientTeamCreationEnabled: boolean; - clientUserDeletionEnabled: boolean; -}; -``` - -
diff --git a/docs/fern/docs/pages-template/sdk/types/team-permission.mdx b/docs/fern/docs/pages-template/sdk/types/team-permission.mdx deleted file mode 100644 index ca2c9c589..000000000 --- a/docs/fern/docs/pages-template/sdk/types/team-permission.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -slug: sdk/types/team-permission ---- - - -
- -The `TeamPermission` object represents a permission that a user has within a team. Currently, it contains only an `id` to specify the permission. - -You can get `TeamPermission` objects by calling functions such as `user.getPermission(...)` or `user.listPermissions()`. - -### Table of Contents - -```typescript -type TeamPermission = { - id: string; //$stack-link-to:#teampermissionid -}; -``` - -
- - -## `teamPermission.id` -
- -The identifier of the permission as a `string`. - -
-### Type Definition - -```typescript -declare const id: string; -``` -
diff --git a/docs/fern/docs/pages-template/sdk/types/team-profile.mdx b/docs/fern/docs/pages-template/sdk/types/team-profile.mdx deleted file mode 100644 index 306a45609..000000000 --- a/docs/fern/docs/pages-template/sdk/types/team-profile.mdx +++ /dev/null @@ -1,79 +0,0 @@ ---- -slug: sdk/types/team-profile ---- - - -
- -This is a detailed reference for the `TeamProfile` and `ServerTeamProfile` objects. - -On this page: -- [TeamProfile](#teamprofile) -- [ServerTeamProfile](#serverteamprofile) - -
- - -# `TeamProfile` -
- -The `TeamProfile` object represents the profile of a user within the context of a team. It includes the user's profile information specific to the team and can be accessed through the `teamUser.teamProfile` property on a `TeamUser` object. - -### Table of Contents - -```typescript -type TeamProfile = { - displayName: string | null; //$stack-link-to:#teamprofiledisplayname - profileImageUrl: string | null; //$stack-link-to:#teamprofileprofileimageurl -}; -``` - -
- - -## `teamProfile.displayName` -
- -The display name of the user within the team context as a `string` or `null` if no display name is set. - -
-### Type Definition - -```typescript -declare const displayName: string | null; -``` -
- -
- - -## `teamProfile.profileImageUrl` -
- -The profile image URL of the user within the team context as a `string`, or `null` if no profile image is set. - -
-### Type Definition - -```typescript -declare const profileImageUrl: string | null; -``` -
- -
- - -# `ServerTeamProfile` -
- -The `ServerTeamProfile` object is currently the same as `TeamProfile`. - -### Table of Contents - -```typescript -type ServerTeamProfile = - // Inherits all functionality from TeamProfile - & TeamProfile; //$stack-link-to:#teamprofile -``` - -
diff --git a/docs/fern/docs/pages-template/sdk/types/team-user.mdx b/docs/fern/docs/pages-template/sdk/types/team-user.mdx deleted file mode 100644 index 6d369483b..000000000 --- a/docs/fern/docs/pages-template/sdk/types/team-user.mdx +++ /dev/null @@ -1,107 +0,0 @@ ---- -slug: sdk/types/team-user ---- - - -
- -On this page: -- [TeamUser](#teamuser) -- [ServerTeamUser](#serverteamuser) - -
- - - -# `TeamUser` -
- -The `TeamUser` object is used on the client side to represent a user in the context of a team, providing minimal information about the user, including their ID and team-specific profile. - -It is usually obtained by calling -`team.useUsers()` or {/* THIS_LINE_PLATFORM react-like */} -`team.listUsers()` on a [`Team` object](../types/team.mdx#team). - -### Table of Contents - -```typescript -type TeamUser = { - id: string; //$stack-link-to:#teamuserid - teamProfile: TeamProfile; //$stack-link-to:#teamuserteamprofile -}; -``` - -
- - - -## `teamUser.id` -
- -The ID of the user. - -
-### Type Definition - -```typescript -declare const id: string; -``` -
- -
- - - -## `teamUser.teamProfile` -
- -The team profile of the user as a `TeamProfile` object. - -
-### Type Definition - -```typescript -declare const teamProfile: TeamProfile; -``` -
- -
- - - -# `ServerTeamUser` -
- -The `ServerTeamUser` object is used on the server side to represent a user within a team. Besides the team profile, it also includes all the functionality of a [`ServerUser`](../types/user.mdx#serveruser). - -It is usually obtained by calling `serverTeam.listUsers()` on a [`ServerTeam` object](../types/team.mdx#serverteam). - -### Table of Contents - -```typescript -type ServerTeamUser = - // Inherits all functionality from TeamUser - & TeamUser //$stack-link-to:#teamuser - // Inherits all functionality from ServerUser - & ServerUser //$stack-link-to:./user#serveruser - & { - teamProfile: ServerTeamProfile; //$stack-link-to:#serverteamuserteamprofile - }; -``` - -
- - - -## `serverTeamUser.teamProfile` -
- -The team profile of the user as a `ServerTeamProfile` object. - -
-### Type Definition - -```typescript -declare const teamProfile: ServerTeamProfile; -``` -
diff --git a/docs/fern/docs/pages-template/sdk/types/team.mdx b/docs/fern/docs/pages-template/sdk/types/team.mdx deleted file mode 100644 index a094375ae..000000000 --- a/docs/fern/docs/pages-template/sdk/types/team.mdx +++ /dev/null @@ -1,777 +0,0 @@ ---- -slug: sdk/types/team ---- - - -
- -This is a detailed reference for the `Team` object. If you're looking for a more high-level overview, please refer to our [guide on teams](../../concepts/orgs-and-teams.mdx). - -On this page: -- [Team](#team) -- [ServerTeam](#serverteam) - -
- - - -# `Team` -
- -A `Team` object contains basic information and functions about a team, to the extent of which a member of the team would have access to it. - -You can get `Team` objects with the -`user.useTeams()` or {/* THIS_LINE_PLATFORM react-like */} -`user.listTeams()` functions. The created team will then inherit the permissions of that user; for example, the `team.update(...)` function can only succeed if the user is allowed to make updates to the team. - -### Table of Contents - -```typescript -type Team = { - id: string; //$stack-link-to:#teamid - displayName: string; //$stack-link-to:#teamdisplayname - profileImageUrl: string | null; //$stack-link-to:#teamprofileimageurl - clientMetadata: Json; //$stack-link-to:#teamclientmetadata - clientReadOnlyMetadata: Json; //$stack-link-to:#teamclientreadonlymetadata - - update(data): Promise; //$stack-link-to:#teamupdatedata - inviteUser(options): Promise; //$stack-link-to:#teaminviteuseroptions - listUsers(): Promise; //$stack-link-to:#teamlistusers - // NEXT_LINE_PLATFORM react-like - ⤷ useUsers(): TeamUser[]; //$stack-link-to:#teamuseusers - listInvitations(): Promise<{ ... }[]>; //$stack-link-to:#teamlistinvitations - // NEXT_LINE_PLATFORM react-like - ⤷ useInvitations(): { ... }[]; //$stack-link-to:#teamuseinvitations - - createApiKey(options): Promise; //$stack-link-to:#teamcreateapikeyoptions - listApiKeys(): Promise; //$stack-link-to:#teamlistapikeys - // NEXT_LINE_PLATFORM react-like - ⤷ useApiKeys(): TeamApiKey[]; //$stack-link-to:#teamuseapikeys -}; -``` - -
- - -## `team.id` -
- -The team ID as a `string`. This value is always unique. - -
-### Type Definition - -```typescript -declare const id: string; -``` -
- -
- - -## `team.displayName` -
- -The display name of the team as a `string`. - -
-### Type Definition - -```typescript -declare const displayName: string; -``` -
- -
- - - -## `team.profileImageUrl` -
- -The profile image URL of the team as a `string`, or `null` if no profile image is set. - -
-### Type Definition - -```typescript -declare const profileImageUrl: string | null; -``` -
- -
- - - -## `team.clientMetadata` -
- -The client metadata of the team as a `Json` object. - -
-### Type Definition - -```typescript -declare const clientMetadata: Json; -``` -
- -
- - - -## `team.clientReadOnlyMetadata` -
- -The client read-only metadata of the team as a `Json` object. - -
-### Type Definition - -```typescript -declare const clientReadOnlyMetadata: Json; -``` -
- -
- - - -## `team.update(data)` -
- -Updates the team information. - -Note that this operation requires the current user to have the `$update_team` permission. If the user lacks this permission, an error will be thrown. - -### Parameters - -
- - The fields to update. - }> - - The display name of the team. - - - - The profile image URL of the team. - - - - The client metadata of the team. - - - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function update(options: { - displayName?: string; - profileImageUrl?: string | null; - clientMetadata?: Json; -}): Promise; -``` - -### Examples - -
-```typescript Updating team details -await team.update({ - displayName: 'New Team Name', - profileImageUrl: 'https://example.com/profile.png', - clientMetadata: { - address: '123 Main St, Anytown, USA', - }, -}); -``` -
-
- -
- - - -## `team.inviteUser(options)` -
- -Sends an invitation email to a user to join the team. - -Note that this operation requires the current user to have the `$invite_members` permission. If the user lacks this permission, an error will be thrown. - -An invitation email containing a magic link will be sent to the specified user. If the user has an existing account, they will be automatically added to the team upon clicking the link. For users without an account, the link will guide them through the sign-up process before adding them to the team. - -### Parameters - -
- - An object containing multiple properties. - }> - - The email of the user to invite. - - - - The URL where users will be redirected after accepting the team invitation. - - Required when calling `inviteUser()` in the server environment since the URL cannot be automatically determined. - - Example: `https://your-app-url.com/handler/team-invitation` - - - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function inviteUser(options: { - email: string; - callbackUrl?: string; -}): Promise; -``` - -### Examples - -
-```typescript Sending a team invitation -await team.inviteUser({ - email: 'user@example.com', -}); -``` -
-
- -
- - - -## `team.listUsers()` -
- -Gets a list of users in the team. - -Note that this operation requires the current user to have the `$read_members` permission. If the user lacks this permission, an error will be thrown. - -### Parameters - -
- None. -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function listUsers(): Promise; -``` - -### Examples - -
-```typescript Listing team members -const users = await team.listUsers(); -users.forEach(user => { - console.log(user.id, user.teamProfile.displayName); -}); -``` -
-
- -
- - -{/* IF_PLATFORM next */} - -## `team.useUsers()` -
- -Functionally equivalent to [`listUsers()`](#teamlistusers), but as a React hook. - -### Parameters - -
- None. -
- -### Returns - -
- `TeamUser[]` -
- -
-### Signature - -```typescript -declare function useUsers(): TeamUser[]; -``` - -### Examples - -
-```typescript Listing team members in React component -const users = team.useUsers(); -users.forEach(user => { - console.log(user.id, user.teamProfile.displayName); -}); -``` -
-
- -
- - -{/* END_PLATFORM */} - -## `team.listInvitations()` -
- -Gets a list of invitations to the team. - -Note that this operation requires the current user to have the `$read_members` and `$invite_members` permissions. If the user lacks this permission, an error will be thrown. - -### Parameters - -
- None. -
- -### Returns - -
- `Promise<{ id: string, email: string, expiresAt: Date }[]>` -
- -
-### Signature - -```typescript -declare function listInvitations(): Promise<{ id: string, email: string, expiresAt: Date }[]>; -``` - -### Examples - -
-```typescript Listing team invitations -const invitations = await team.listInvitations(); -invitations.forEach(invitation => { - console.log(invitation.id, invitation.email); -}); -``` -
-
- -
- - -{/* IF_PLATFORM next */} - -## `team.useInvitations()` -
- -Functionally equivalent to [`listInvitations()`](#teamlistinvitations), but as a React hook. - -### Parameters - -
- None. -
- -### Returns - -
- `{ id: string, email: string, expiresAt: Date }[]` -
- -
-### Signature - -```typescript -declare function useInvitations(): { id: string, email: string, expiresAt: Date }[]; -``` - -### Examples - -
-```typescript Listing team invitations in React component -const invitations = team.useInvitations(); -invitations.forEach(invitation => { - console.log(invitation.id, invitation.email); -}); -``` -
-
- -
- - -{/* END_PLATFORM */} - -## `team.createApiKey(options)` -
- -Creates a new API key for the team. - -### Parameters - -
- - An object containing multiple properties. - }> - - The name of the API key. - - - - The description of the API key. - - - - The expiration date of the API key. - - - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function createApiKey(options: { - name: string; - description: string; - expiresAt: Date; -}): Promise; -``` - -### Examples - -
-```typescript Creating a new API key -await team.createApiKey({ - name: 'New API Key', - description: 'This is a new API key', - expiresAt: new Date('2024-01-01'), -}); -``` -
-
- -
- - - -## `team.listApiKeys()` -
- -Gets a list of API keys for the team. - -### Parameters - -
- None. -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function listApiKeys(): Promise; -``` - -### Examples - -
-```typescript Listing API keys -const apiKeys = await team.listApiKeys(); -apiKeys.forEach(key => { - console.log(key.id, key.name); -}); -``` -
-
- -
- - -{/* IF_PLATFORM next */} - -## `team.useApiKeys()` -
- - - -Functionally equivalent to [`listApiKeys()`](#teamlistapikeys), but as a React hook. - -
- - - -{/* END_PLATFORM */} - -# `ServerTeam` -
- -Like [`Team`](#team), but with [server permissions](../../concepts/stack-app.mdx#client-vs-server). Has full read and write access to everything. - -Calling `serverUser.getTeam(...)` and `serverUser.listTeams()` will return `ServerTeam` objects if the user is a [`ServerUser`](../types/user.mdx#serveruser). Alternatively, you can call `stackServerApp.getTeam('team_id_123')` or `stackServerApp.listTeams()` to query all teams of the project. - -`ServerTeam` extends the `Team` object, providing additional functions and properties as detailed below. It's important to note that while the `Team` object's functions may require specific user permissions, the corresponding functions in `ServerTeam` can be executed without these permission checks. This allows for more flexible and unrestricted team management on the server side. - -### Table of Contents - -```typescript -type ServerTeam = - // Inherits all functionality from Team - & Team //$stack-link-to:#team - & { - createdAt: Date; //$stack-link-to:#serverteamcreatedat - serverMetadata: Json; //$stack-link-to:#serverteamservermetadata - - listUsers(): Promise; //$stack-link-to:#serverteamlistusers - // NEXT_LINE_PLATFORM react-like - ⤷ useUsers(): ServerTeamUser[]; //$stack-link-to:#serverteamuseusers - addUser(userId): Promise; //$stack-link-to:#serverteamadduseruserid - removeUser(userId): Promise; //$stack-link-to:#serverteamremoveuseruserid - delete(): Promise; //$stack-link-to:#serverteamdelete - }; -``` - -
- - -## `serverTeam.createdAt` -
- -The date and time when the team was created. - -
-### Type Definition -```typescript -declare const createdAt: Date; -``` -
- -
- - - -## `serverTeam.serverMetadata` -
- -The server metadata of the team as a `Json` object. - -
-### Type Definition -```typescript -declare const serverMetadata: Json; -``` -
- -
- - - -## `serverTeam.listUsers()` -
- -Gets a list of users in the team. - -This is similar to the `listUsers` method on the `Team` object, but it returns `ServerTeamUser` objects instead of `TeamUser` objects and does not require any permissions. - -### Parameters - -
- None. -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function listUsers(): Promise; -``` - -### Examples - -
-```typescript Listing server team members -const users = await team.listUsers(); -users.forEach(user => { - console.log(user.id, user.teamProfile.displayName); -}); -``` -
-
- -
- - -{/* IF_PLATFORM next */} - -## `serverTeam.useUsers()` -
- - - -Functionally equivalent to [`listUsers()`](#stackteamlistusers), but as a React hook. - -
- - - -{/* END_PLATFORM */} - -## `serverTeam.addUser(userId)` -
- -Adds a user to the team directly without sending an invitation email. - -### Parameters - -
- - The ID of the user to add. - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function addUser(userId: string): Promise; -``` - -### Examples - -
-```typescript Adding a user to the team -await team.addUser('user_id_123'); -``` -
-
- -
- - - -## `serverTeam.removeUser(userId)` -
- -Removes a user from the team. - -### Parameters - -
- - The ID of the user to remove. - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function removeUser(userId: string): Promise; -``` - -### Examples - -
-```typescript Removing a user from the team -await team.removeUser('user_id_123'); -``` -
-
- -
- - - -## `serverTeam.delete()` -
- -Deletes the team. - -### Parameters - -
- None. -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function delete(): Promise; -``` - -### Examples - -
-```typescript Deleting a team -await team.delete(); -``` -
- - -
diff --git a/docs/fern/docs/pages-template/sdk/types/user.mdx b/docs/fern/docs/pages-template/sdk/types/user.mdx deleted file mode 100644 index 92dabb9ba..000000000 --- a/docs/fern/docs/pages-template/sdk/types/user.mdx +++ /dev/null @@ -1,1887 +0,0 @@ ---- -slug: sdk/types/user ---- - - -
- -This is a detailed reference for the `User` object. If you're looking for a more high-level overview, please refer to our guide on users [here](../../getting-started/users.mdx). - -On this page: -- [CurrentUser](#currentuser) -- [ServerUser](#serveruser) -- [CurrentServerUser](#currentserveruser) - -
- - -# `CurrentUser` -
- -You can call `useUser()` or `stackServerApp.getUser()` to get the `CurrentUser` object. - -### Table of Contents - -```typescript maxLines=50 -type CurrentUser = { - id: string; //$stack-link-to:#currentuserid - displayName: string | null; //$stack-link-to:#currentuserdisplayname - primaryEmail: string | null; //$stack-link-to:#currentuserprimaryemail - primaryEmailVerified: boolean; //$stack-link-to:#currentuserprimaryemailverified - profileImageUrl: string | null; //$stack-link-to:#currentuserprofileimageurl - signedUpAt: Date; //$stack-link-to:#currentusersignedupat - hasPassword: boolean; //$stack-link-to:#currentuserhaspassword - clientMetadata: Json; //$stack-link-to:#currentuserclientmetadata - clientReadOnlyMetadata: Json; //$stack-link-to:#currentuserclientreadonlymetadata - selectedTeam: Team | null; //$stack-link-to:#currentuserselectedteam - - update(data): Promise; //$stack-link-to:#currentuserupdatedata - updatePassword(data): Promise; //$stack-link-to:#currentuserupdatepassworddata - getAuthHeaders(): Promise>; //$stack-link-to:#currentusergetauthheaders - getAuthJson(): Promise<{ accessToken: string | null }>; //$stack-link-to:#currentusergetauthjson - signOut([options]): Promise; //$stack-link-to:#currentusersignoutoptions - delete(): Promise; //$stack-link-to:#currentuserdelete - - getTeam(id): Promise; //$stack-link-to:#currentusergetteamid - // NEXT_LINE_PLATFORM react-like - ⤷ useTeam(id): Team | null; //$stack-link-to:#currentuseruseteamid - listTeams(): Promise; //$stack-link-to:#currentuserlistteams - // NEXT_LINE_PLATFORM react-like - ⤷ useTeams(): Team[]; //$stack-link-to:#currentuseruseteams - setSelectedTeam(team): Promise; //$stack-link-to:#currentusersetselectedteamteam - createTeam(data): Promise; //$stack-link-to:#currentusercreateteamdata - leaveTeam(team): Promise; //$stack-link-to:#currentuserleaveteamteam - getTeamProfile(team): Promise; //$stack-link-to:#currentusergetteamprofileteam - // NEXT_LINE_PLATFORM react-like - ⤷ useTeamProfile(team): EditableTeamMemberProfile; //$stack-link-to:#currentuseruseteamprofileteam - - hasPermission(scope, permissionId): Promise; //$stack-link-to:#currentuserhaspermissionscope-permissionid - getPermission(scope, permissionId[, options]): Promise; //$stack-link-to:#currentusergetpermissionscope-permissionid-options - // NEXT_LINE_PLATFORM react-like - ⤷ usePermission(scope, permissionId[, options]): TeamPermission | null; //$stack-link-to:#currentuserusepermissionscope-permissionid-options - listPermissions(scope[, options]): Promise; //$stack-link-to:#currentuserlistpermissionsscope-options - // NEXT_LINE_PLATFORM react-like - ⤷ usePermissions(scope[, options]): TeamPermission[]; //$stack-link-to:#currentuserusepermissionsscope-options - - listContactChannels(): Promise; //$stack-link-to:#currentuserlistcontactchannels - // NEXT_LINE_PLATFORM react-like - ⤷ useContactChannels(): ContactChannel[]; //$stack-link-to:#currentuserusecontactchannels - - createApiKey(options): Promise; //$stack-link-to:#currentusercreateapikeyoptions - listApiKeys(): Promise; //$stack-link-to:#currentuserlistapikeys - // NEXT_LINE_PLATFORM react-like - ⤷ useApiKeys(): UserApiKey[]; //$stack-link-to:#currentuseruseapikeys -}; -``` - -
- - -## `currentUser.id` -
- -The user ID as a `string`. This is the unique identifier of the user. - -
-### Type Definition - -```typescript -declare const id: string; -``` -
- -
- - - -## `currentUser.displayName` -
- -The display name of the user as a `string` or `null` if not set. The user can modify this value. - -
-### Type Definition - -```typescript -declare const displayName: string | null; -``` -
- -
- - - -## `currentUser.primaryEmail` -
- -The primary email of the user as a `string` or `null`. Note that this is not necessarily unique. - -
-### Type Definition - -```typescript -declare const primaryEmail: string | null; -``` -
- -
- - - -## `currentUser.primaryEmailVerified` -
- -A `boolean` indicating whether the primary email of the user is verified. - -
-### Type Definition - -```typescript -declare const primaryEmailVerified: boolean; -``` -
- -
- - - -## `currentUser.profileImageUrl` -
- -The profile image URL of the user as a `string` or `null` if no profile image is set. - -
-### Type Definition - -```typescript -declare const profileImageUrl: string | null; -``` -
- -
- - - -## `currentUser.signedUpAt` -
- -The date and time when the user signed up, as a `Date`. - -
-### Type Definition - -```typescript -declare const signedUpAt: Date; -``` -
- -
- - - -## `currentUser.hasPassword` -
- -A `boolean` indicating whether the user has a password set. - -
-### Type Definition - -```typescript -declare const hasPassword: boolean; -``` -
- -
- - - -## `currentUser.clientMetadata` -
- -The client metadata of the user as an `object`. This metadata is visible on the client side but should not contain sensitive or server-only information. - -
-### Type Definition - -```typescript -declare const clientMetadata: Json; -``` -
- -
- - - -## `currentUser.clientReadOnlyMetadata` -
- -Read-only metadata visible on the client side. This metadata can only be modified on the server side. - -
-### Type Definition - -```typescript -declare const clientReadOnlyMetadata: Json; -``` -
- -
- - - -## `currentUser.selectedTeam` -
- -The currently selected team for the user, if applicable, as a `Team` object or `null` if no team is selected. - -
-### Type Definition - -```typescript -declare const selectedTeam: Team | null; -``` -
- -
- - - -## `currentUser.update(data)` -
- -Updates the user information. - -### Parameters - -
- - The fields to update. - }> - - The new display name for the user. - - - Custom metadata visible to the client. - - - The ID of the team to set as selected, or `null` to clear selection. - - - The URL of the user's new profile image, or `null` to remove it. - - - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function update(data: { - displayName?: string; - clientMetadata?: Json; - selectedTeamId?: string | null; - profileImageUrl?: string | null; -}): Promise; -``` - -### Examples - -
-```typescript Updating user details -await user.update({ - displayName: "New Display Name", - clientMetadata: { - address: "123 Main St", - }, -}); -``` -
-
- -
- - -## `currentUser.getTeam(id)` -
- -Gets the team with the specified ID. - -### Parameters - -
- - The ID of the team to get. - -
- -### Returns - -
- `Promise`: The team object, or `null` if the team is not found or the user is not a member of the team. -
- -
-### Signature - -```typescript -declare function getTeam(id: string): Promise; -``` - -### Examples - -
-```typescript Getting a team by ID -const team = await user.getTeam("teamId"); -``` -
-
- -
- - - -{/* IF_PLATFORM next */} -## `currentUser.useTeam(id)` -
- -Gets the team with the given ID. This is the same as `getTeam` but is used as a React hook. - -### Parameters - -
- - The ID of the team to get. - -
- -### Returns - -
- `Team | null`: The team object, or `null` if the team is not found or the user is not a member of the team. -
- -
-### Signature - -```typescript -declare function useTeam(id: string): Team | null; -``` - -### Examples - -
-```typescript Using a team in a React component -const team = user.useTeam("teamId"); -``` -
-
- -
- - -{/* END_PLATFORM */} - - -## `currentUser.listTeams()` -
- -Lists all the teams the user is a member of. - -### Parameters - -
- None. -
- -### Returns - -
- `Promise`: The list of teams. -
- -
-### Signature - -```typescript -declare function listTeams(): Promise; -``` - -### Examples - -
-```typescript Listing all teams -const teams = await user.listTeams(); -``` -
-
- -
- - - -{/* IF_PLATFORM next */} -## `currentUser.useTeams()` -
- -Lists all the teams the user is a member of. This is the same as `listTeams` but is used as a React hook. - -### Parameters - -
- None. -
- -### Returns - -
- `Team[]`: The list of teams. -
- -
-### Signature - -```typescript -declare function useTeams(): Team[]; -``` - -### Examples - -
-```typescript Using teams in a React component -const teams = user.useTeams(); -``` -
-
- -
- - -{/* END_PLATFORM */} - -## `currentUser.setSelectedTeam(team)` -
- -Sets the currently selected team for the user. - -### Parameters - -
- - The team to set as selected, or `null` to clear selection. - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function setSelectedTeam(team: Team | null): Promise; -``` - -### Examples - -
-```typescript Setting the selected team -const team = await user.getTeam("team_id_123"); -await user.setSelectedTeam(team); -``` -
-
- -
- - - -## `currentUser.createTeam(data)` -
- -Creates a new team for the user. The user will be added to the team and given creator permissions. - -**Note**: If client-side team creation is disabled in the Stack dashboard, this will throw an error. - -### Parameters - -
- - The data for creating the team. - }> - - The display name for the team. - - - The URL of the team's profile image, or `null` to remove it. - - - -
- -### Returns - -
- `Promise`: The created team. -
- -
-### Signature - -```typescript -declare function createTeam(data: { - displayName: string; - profileImageUrl?: string | null; -}): Promise; -``` - -### Examples - -
-```typescript Creating a new team -const team = await user.createTeam({ - displayName: "New Team", - profileImageUrl: "https://example.com/profile.jpg", -}); -``` -
-
- -
- - -## `currentUser.leaveTeam(team)` -
- -Allows the user to leave a team. If the user is not a member of the team, this will throw an error. - -### Parameters - -
- - The team to leave. - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function leaveTeam(team: Team): Promise; -``` - -### Examples - -
-```typescript Leaving a team -await user.leaveTeam(team); -``` -
-
- -
- - - -## `currentUser.getTeamProfile(team)` -
- -Retrieves the user's profile within a specific team. - -### Parameters - -
- - The team to retrieve the profile for. - -
- -### Returns - -
- `Promise`: The user's editable profile for the specified team. -
- -
-### Signature - -```typescript -declare function getTeamProfile(team: Team): Promise; -``` - -### Examples - -
-```typescript Getting a team profile -const profile = await user.getTeamProfile(team); -``` -
-
- -
- - - -{/* IF_PLATFORM next */} -## `currentUser.useTeamProfile(team)` -
- -Retrieves the user's profile within a specific team. This is the same as `getTeamProfile` but is used as a React hook. - -### Parameters - -
- - The team to retrieve the profile for. - -
- -### Returns - -
- `EditableTeamMemberProfile`: The user's editable profile for the specified team. -
- -
-### Signature - -```typescript -declare function useTeamProfile(team: Team): EditableTeamMemberProfile; -``` - -### Examples - -
-```typescript Using a team profile in React -const profile = user.useTeamProfile(team); -``` -
-
- -
- - -{/* END_PLATFORM */} - -## `currentUser.hasPermission(scope, permissionId)` -
- -Checks if the user has a specific permission for a team. - -### Parameters - -
- - The team to check the permission for. - - - The ID of the permission to check. - -
- -### Returns - -
- `Promise`: Whether the user has the specified permission. -
- -
-### Signature - -```typescript -declare function hasPermission(scope: Team, permissionId: string): Promise; -``` - -### Examples - -
-```typescript Checking user permission -const hasPermission = await user.hasPermission(team, "permissionId"); -``` -
-
- -
- - - -## `currentUser.getPermission(scope, permissionId, options?)` -
- -Retrieves a specific permission for a user within a team. - -### Parameters - -
- - The team to retrieve the permission for. - - - The ID of the permission to retrieve. - - - An object containing multiple properties. - }> - - Whether to retrieve the permission recursively. Default is `true`. - - - -
- -### Returns - -
- `Promise`: The permission object, or `null` if not found. -
- -
-### Signature - -```typescript -declare function getPermission(scope: Team, permissionId: string, options?: { recursive?: boolean }): Promise; -``` - -### Examples - -
-```typescript Getting a permission -const permission = await user.getPermission(team, "read_secret_info"); -``` -
-
- -
- - - -{/* IF_PLATFORM next */} - -## `currentUser.usePermission(scope, permissionId, options?)` -
- -Retrieves a specific permission for a user within a team, used as a React hook. - -### Parameters - -
- - The team to retrieve the permission for. - - - The ID of the permission to retrieve. - - - An object containing multiple properties. - }> - - Whether to retrieve the permission recursively. Default is `true`. - - - -
- -### Returns - -
- `TeamPermission | null`: The permission object, or `null` if not found. -
- -
-### Signature - -```typescript -declare function usePermission(scope: Team, permissionId: string, options?: { recursive?: boolean }): TeamPermission | null; -``` - -### Examples - -
-```typescript Using a permission in React -const permission = user.usePermission(team, "read_secret_info"); -``` -
-
- -
- - -{/* END_PLATFORM */} - -## `currentUser.listPermissions(scope[, options])` -
- -Lists all permissions the user has for a specified team. - -### Parameters - -
- - The team to list permissions for. - - - An object containing multiple properties. - }> - - Whether to list the permissions recursively. Default is `true`. - - - -
- -### Returns - -
- `Promise`: An array of permissions. -
- -
-### Signature - -```typescript -declare function listPermissions(scope: Team, options?: { recursive?: boolean }): Promise; -``` - -### Examples - -
-```typescript Listing user permissions -const permissions = await user.listPermissions(team); -``` -
-
- -
- - - -{/* IF_PLATFORM next */} - -## `currentUser.usePermissions(scope, options?)` -
- -Lists all permissions the user has for a specified team, used as a React hook. - -### Parameters - -
- - The team to retrieve permissions for. - - - An object containing multiple properties. - }> - - Whether to list the permissions recursively. Default is `true`. - - - -
- -### Returns - -
- `TeamPermission[]`: An array of permissions. -
- -
-### Signature - -```typescript -declare function usePermissions(scope: Team, options?: { recursive?: boolean }): TeamPermission[]; -``` - -### Examples - -
-```typescript Using permissions in a React component -const permissions = user.usePermissions(team); -``` -
-
- -
- - -{/* END_PLATFORM */} - -## `currentUser.listContactChannels()` -
- -Lists all the contact channels of the user. - -### Parameters - -
- No parameters. -
- -### Returns - -
- `Promise`: An array of contact channels. -
- -
-### Signature - -```typescript -declare function listContactChannels(): Promise; -``` - -### Examples - -
-```typescript Listing contact channels -const contactChannels = await user.listContactChannels(); -``` -
-
- -
- - - -{/* IF_PLATFORM next */} - -## `currentUser.useContactChannels()` -
- -Lists all the contact channels of the user, used as a React hook. - -### Parameters - -
- No parameters. -
- -### Returns - -
- `ContactChannel[]`: An array of contact channels. -
- -
-### Signature - -```typescript -declare function useContactChannels(): ContactChannel[]; -``` - -### Examples - -
-```typescript Using contact channels in React -const contactChannels = user.useContactChannels(); -``` -
-
- -
- - -{/* END_PLATFORM */} - -## `currentUser.createApiKey(options)` -
- -Creates a new API key for the user, which can be used for programmatic access to your application's backend. - -### Parameters - -
- - Options for creating the API key. - }> - - A human-readable description of the API key's purpose. - - - The date when the API key will expire. Use null for keys that don't expire. - - - Whether the API key is public. Defaults to false. - - - **Secret API Keys** (default) begin with `sk_` and are monitored by Stack Auth's secret scanner, which can revoke them if detected in public code repositories. - - **Public API Keys** begin with `pk_` and are designed for client-side code where exposure is not a concern. - - - -
- -### Returns - -
- `Promise`: The newly created API key. Note that this is the only time the full API key value will be visible. -
- -
-### Signature - -```typescript -declare function createApiKey(options: { - description: string; - expiresAt: Date | null; - isPublic?: boolean; -}): Promise; -``` - -### Examples - -
-```typescript Creating an API key -const apiKey = await user.createApiKey({ - description: "Backend integration", - expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90 days - isPublic: false, -}); - -// Save the API key value securely, as it won't be retrievable later -console.log("API Key:", apiKey.value); -``` -
-
- -
- - -## `currentUser.listApiKeys()` -
- -Lists all API keys that belong to the user. - -### Parameters - -
- None. -
- -### Returns - -
- `Promise`: An array of API keys belonging to the user. -
- -
-### Signature - -```typescript -declare function listApiKeys(): Promise; -``` - -### Examples - -
-```typescript Listing API keys -const apiKeys = await user.listApiKeys(); -console.log(`You have ${apiKeys.length} API keys`); - -// Find keys that are about to expire -const soonToExpire = apiKeys.filter(key => - key.expiresAt && key.expiresAt.getTime() - Date.now() < 7 * 24 * 60 * 60 * 1000 -); -``` -
-
- -
- - - -{/* IF_PLATFORM next */} -## `currentUser.useApiKeys()` -
- -Lists all API keys that belong to the user, used as a React hook. - -### Parameters - -
- None. -
- -### Returns - -
- `UserApiKey[]`: An array of API keys belonging to the user. -
- -
-### Signature - -```typescript -declare function useApiKeys(): UserApiKey[]; -``` - -### Examples - -
-```typescript Using API keys in a React component -function ApiKeysList() { - const user = useUser(); - const apiKeys = user.useApiKeys(); - - return ( -
-

Your API Keys ({apiKeys.length})

-
    - {apiKeys.map(key => ( -
  • - {key.description} - Last four: {key.value.lastFour} - {key.isValid() ? ' (valid)' : ` (invalid: ${key.whyInvalid()})`} -
  • - ))} -
-
- ); -} -``` -
-
- -
- - -{/* END_PLATFORM */} - -## `currentUser.updatePassword(data)` -
- -Updates the user's password. - -### Parameters - -
- - The fields required for updating the password. - }> - - The current password of the user. - - - The new password for the user. - - - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function updatePassword(data: { - oldPassword: string; - newPassword: string; -}): Promise; -``` - -### Examples - -
-```typescript Updating user password -const result = await user.updatePassword({ - oldPassword: "currentPassword", - newPassword: "newPassword", -}); -if (result.status === "error") { - console.error("Error updating password", result.error); -} else { - console.log("Password updated"); -} -``` -
-
- -
- - -## `currentUser.getAuthHeaders()` -
- -Returns headers for sending authenticated HTTP requests to external servers. Most commonly used in cross-origin -requests. Similar to `getAuthJson`, but specifically for HTTP requests. - -If you are using `tokenStore: "cookie"`, you don't need this for same-origin requests. However, most -browsers now disable third-party cookies by default, so we must pass authentication tokens by header instead -if the client and server are on different origins. - -This function returns a header object that can be used with `fetch` or other HTTP request libraries to send -authenticated requests. - -On the server, you can then pass in the `Request` object to the `tokenStore` option -of your Stack app. Please note that CORS does not allow most headers by default, so you -must include `x-stack-auth` in the [`Access-Control-Allow-Headers` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers) -of the CORS preflight response. - -### Parameters - -
- No parameters. -
- -### Returns - -
- `Promise>`: An object containing the authentication headers. -
- -
-### Signature - -```typescript -declare function getAuthHeaders(): Promise>; -``` - -### Examples - -
-```typescript Passing auth headers to an external server -// client -const res = await fetch("https://api.example.com", { - headers: { - ...await stackApp.getAuthHeaders() - // you can also add your own headers here - }, -}); - -// server -function handleRequest(req: Request) { - const user = await stackServerApp.getUser({ tokenStore: req }); - return new Response("Welcome, " + user.displayName); -} -``` -
-
- -
- - - -## `currentUser.getAuthJson()` -
- -Creates a JSON-serializable object containing the information to authenticate a user on an external server. - -While `getAuthHeaders` is the recommended way to send authentication tokens over HTTP, your app may use -a different protocol, for example WebSockets or gRPC. This function returns a token object that can be JSON-serialized and sent to the server in any way you like. - -On the server, you can pass in this token object into the `tokenStore` option to fetch user details. - -### Parameters - -
- No parameters. -
- -### Returns - -
- `Promise<{ accessToken: string | null }>`: An object containing the access token. -
- -
-### Signature - -```typescript -declare function getAuthJson(): Promise<{ accessToken: string | null }>; -``` - -### Examples - -
-```typescript Passing auth tokens over an RPC call -// client -const res = await rpcCall(rpcEndpoint, { - data: { - auth: await stackApp.getAuthJson(), - }, -}); - -// server -function handleRequest(data) { - const user = await stackServerApp.getUser({ tokenStore: data.auth }); - return new Response("Welcome, " + user.displayName); -} -``` -
-
- -
- - - -## `currentUser.signOut(options)` -
- -Signs out the user and clears the session. - -### Parameters - -
- - An object containing multiple properties. - }> - - The URL to redirect to after signing out. Defaults to the `afterSignOut` URL from the Stack app's `urls` object. - - - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function signOut(options?: { redirectUrl?: string }): Promise; -``` - -### Examples - -
-```typescript Signing out -await user.signOut(); -``` -
-
- -
- - - -## `currentUser.delete()` -
- -Deletes the user. This action is irreversible and can only be used if client-side user deletion is enabled in the Stack dashboard. - -### Parameters - -
- No parameters. -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function delete(): Promise; -``` - -### Examples - -
-```typescript Deleting the user -await user.delete(); -``` -
-
- -
- - - -# `ServerUser` -
- -The `ServerUser` object contains most `CurrentUser` properties and methods with the exception of those that require an active session (`getAuthJson` and `signOut`). It also contains some additional functions that require [server-level permissions](../../concepts/stack-app.mdx#client-vs-server). - - - -### Table of Contents - -```typescript -type ServerUser = - // Inherits most functionality from CurrentUser - & Omit //$stack-link-to:#currentuser - & { - lastActiveAt: Date; //$stack-link-to:#serveruserlastactiveat - serverMetadata: Json; //$stack-link-to:#serveruserservermetadata - - update(data): Promise; //$stack-link-to:#serveruserupdatedata - - listContactChannels(): Promise; //$stack-link-to:#serveruserlistcontactchannels - // NEXT_LINE_PLATFORM react-like - ⤷ useContactChannels(): ContactChannel[]; //$stack-link-to:#serveruserusecontactchannels - - grantPermission(scope, permissionId): Promise; //$stack-link-to:#serverusergrantpermissionscope-permissionid - revokePermission(scope, permissionId): Promise; //$stack-link-to:#serveruserrevokepermissionscope-permissionid - }; -``` - -
- - - -## `serverUser.lastActiveAt` -
- -The last active date and time of the user as a `Date`. - -
-### Type Definition - -```typescript -declare const lastActiveAt: Date; -``` -
- -
- - - -## `serverUser.serverMetadata` -
- -The server metadata of the user, accessible only on the server side. - -
-### Type Definition - -```typescript -declare const serverMetadata: Json; -``` -
- -
- - -## `serverUser.update(data)` -
- -Updates the user's information on the server side. This is similar to the `CurrentUser.update()` method but includes additional capabilities, such as updating server metadata or setting a new password directly. - -### Parameters - -
- - The fields to update. - }> - - The new display name for the user. - - - The new primary email for the user. - - - Whether the primary email is verified. - - - Whether auth should be enabled for the primary email. - - - The new password for the user. - - - The ID of the team to set as selected, or `null` to clear selection. - - - The URL of the user's new profile image, or `null` to remove. - - - Metadata visible on the client side. - - - Metadata that is read-only on the client but modifiable on the server side. - - - Metadata only accessible and modifiable on the server side. - - - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function update(data: { - displayName?: string; - profileImageUrl?: string | null; - primaryEmail?: string, - primaryEmailVerified?: boolean, - primaryEmailAuthEnabled?: boolean, - password?: string; - selectedTeamId?: string | null; - clientMetadata?: Json; - clientReadOnlyMetadata?: Json; - serverMetadata?: Json; -}): Promise; -``` - -### Examples - -
-```typescript Updating user details on the server -await serverUser.update({ - displayName: "Updated Display Name", - password: "newSecurePassword", - serverMetadata: { - internalNote: "Confidential information", - }, -}); -``` -
-
- -
- - - -## `serverUser.listContactChannels()` -
- -Lists all the contact channels of the user on the server side. This is similar to `CurrentUser.listContactChannels()` but returns a list of `ServerContactChannel` objects, which may include additional server-only details. - -### Parameters - -
- No parameters. -
- -### Returns - -
- `Promise`: An array of server-specific contact channels. -
- -
-### Signature - -```typescript -declare function listContactChannels(): Promise; -``` - -### Examples - -
-```typescript Listing server-specific contact channels -const contactChannels = await serverUser.listContactChannels(); -``` -
-
- -
- - -{/* IF_PLATFORM next */} -## `serverUser.useContactChannels()` -
- - - -Functionally equivalent to [`listContactChannels()`](#serveruserlistcontactchannels), but as a React hook. - -
- - - -{/* END_PLATFORM */} - -## `serverUser.grantPermission(scope, permissionId)` -
- -Grants a specific permission to the user for a given team. - -### Parameters - -
- - The team to grant the permission for. - - - The ID of the permission to grant. - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function grantPermission(scope: Team, permissionId: string): Promise; -``` - -### Examples - -
-```typescript Granting permission to a user -await serverUser.grantPermission(team, "read_secret_info"); -``` -
-
- -
- - - -## `serverUser.revokePermission(scope, permissionId)` -
- -Revokes a specific permission from the user for a given team. - -### Parameters - -
- - The team to revoke the permission from. - - - The ID of the permission to revoke. - -
- -### Returns - -
- `Promise` -
- -
-### Signature - -```typescript -declare function revokePermission(scope: Team, permissionId: string): Promise; -``` - -### Examples - -
-```typescript Revoking permission from a user -await serverUser.revokePermission(team, "read_secret_info"); -``` -
-
- -
- - - -## `serverUser.createApiKey(options)` -
- -Creates a new API key for the user, which can be used for programmatic access to your application's backend. - -### Parameters - -
- - Options for creating the API key. - }> - - A human-readable description of the API key's purpose. - - - The date when the API key will expire. Use null for keys that don't expire. - - - Whether the API key is public. Defaults to false. - - - **Secret API Keys** (default) begin with `sk_` and are monitored by Stack Auth's secret scanner, which can revoke them if detected in public code repositories. - - **Public API Keys** begin with `pk_` and are designed for client-side code where exposure is not a concern. - - - -
- -### Returns - -
- `Promise`: The newly created API key. Note that this is the only time the full API key value will be visible. -
- -
-### Signature - -```typescript -declare function createApiKey(options: { - description: string; - expiresAt: Date | null; - isPublic?: boolean; -}): Promise; -``` - -### Examples - -
-```typescript Creating an API key -const apiKey = await serverUser.createApiKey({ - description: "Backend integration", - expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90 days - isPublic: false, -}); - -// Save the API key value securely, as it won't be retrievable later -console.log("API Key:", apiKey.value); -``` -
-
- -
- - -## `serverUser.listApiKeys()` -
- -Lists all API keys that belong to the user. - -### Parameters - -
- None. -
- -### Returns - -
- `Promise`: An array of API keys belonging to the user. -
- -
-### Signature - -```typescript -declare function listApiKeys(): Promise; -``` - -### Examples - -
-```typescript Listing API keys -const apiKeys = await serverUser.listApiKeys(); -console.log(`You have ${apiKeys.length} API keys`); - -// Find keys that are about to expire -const soonToExpire = apiKeys.filter(key => - key.expiresAt && key.expiresAt.getTime() - Date.now() < 7 * 24 * 60 * 60 * 1000 -); -``` -
-
- -
- - - -{/* IF_PLATFORM next */} -## `serverUser.useApiKeys()` -
- -Lists all API keys that belong to the user, used as a React hook. - -### Parameters - -
- None. -
- -### Returns - -
- `UserApiKey[]`: An array of API keys belonging to the user. -
- -
-### Signature - -```typescript -declare function useApiKeys(): UserApiKey[]; -``` - -### Examples - -
-```typescript Using API keys in a React component -function ApiKeysList() { - const user = useUser(); - const apiKeys = user.useApiKeys(); - - return ( -
-

Your API Keys ({apiKeys.length})

-
    - {apiKeys.map(key => ( -
  • - {key.description} - Last four: {key.value.lastFour} - {key.isValid() ? ' (valid)' : ` (invalid: ${key.whyInvalid()})`} -
  • - ))} -
-
- ); -} -``` -
-
- -
- - -{/* END_PLATFORM */} - -
- - - -# `CurrentServerUser` -
- -The `CurrentServerUser` object combines all the properties and methods of both `CurrentUser` and `ServerUser`. You can obtain a `CurrentServerUser` by calling `stackServerApp.getUser()` on the server side. - -### Table of Contents - -```typescript -type CurrentServerUser = - // Inherits all functionality from CurrentUser - & CurrentUser //$stack-link-to:#currentuser - // Inherits all functionality from ServerUser - & ServerUser; //$stack-link-to:#serveruser -``` - -
- - -
diff --git a/docs/fern/docs/pages-template/snippets/always-tab-codeblock.mdx b/docs/fern/docs/pages-template/snippets/always-tab-codeblock.mdx deleted file mode 100644 index 1bc0aa795..000000000 --- a/docs/fern/docs/pages-template/snippets/always-tab-codeblock.mdx +++ /dev/null @@ -1,3 +0,0 @@ -```typescript <> -// No tab selected. -``` diff --git a/docs/fern/fern.config.json b/docs/fern/fern.config.json deleted file mode 100644 index 37343ed76..000000000 --- a/docs/fern/fern.config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "organization": "stack-auth", - "version": "0.44.11" -} \ No newline at end of file diff --git a/docs/fern/generators.yml b/docs/fern/generators.yml deleted file mode 100644 index 7c05bcd6c..000000000 --- a/docs/fern/generators.yml +++ /dev/null @@ -1,14 +0,0 @@ -groups: - python-sdk: - generators: - - name: fernapi/fern-python-sdk - version: 2.9.5 - # output: - # location: pypi - # package-name: "stack-auth" - # token: ${ PYPI_TOKEN } - github: - repository: fern-demo/stack-auth-python-sdk - # mode: pull-request - config: - client_class_name: StackAuth \ No newline at end of file diff --git a/docs/fern/openapi/.gitignore b/docs/fern/openapi/.gitignore deleted file mode 100644 index 1e82fc7de..000000000 --- a/docs/fern/openapi/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.yaml diff --git a/docs/fern/style.css b/docs/fern/style.css deleted file mode 100644 index a419b4367..000000000 --- a/docs/fern/style.css +++ /dev/null @@ -1,206 +0,0 @@ -.indented { - padding-left: 1.5rem; - border-left: 1px solid var(--border); -} - -.stack-white-image-showcase { - display: flex; - justify-content: center; - background-color: white; - border-radius: 12px; - padding: 16px; -} - -.grid:has(.prose .stack-aside-container) { - grid-template-columns: minmax(0, 1fr) !important; -} - -.stack-aside-container { - display: grid; - grid-template-columns: 55% 40%; - justify-content: space-between; -} - -.stack-aside-container > .stack-full-size { - grid-column: 1 / 3; -} - -.stack-aside-container > .stack-content { - grid-column: 1; -} - -.stack-aside-container > .stack-aside { - grid-column: 2; -} - -@media (max-width: 1200px) { - .stack-aside-container { - grid-template-columns: 100%; - } - .stack-aside-container > .stack-aside { - grid-column: 1; - } - .stack-aside-container > .stack-content { - grid-column: 1; - } - .stack-aside-container > .stack-full-size { - grid-column: 1; - } -} - -.stack-aside-container > :not(.stack-aside):not(.stack-content):not(.stack-full-size) { - background-color: red; -} -.stack-aside-container > :not(.stack-aside):not(.stack-content):not(.stack-full-size) { - content: "All elements in a stack-aside-container must be wrapped in stack-aside, stack-content, or stack-full-size"; - font-size: 300%; -} - -.stack-sticky { - position: sticky; - top: 64px; -} - -.fern-accordion > * > button > .fern-accordion-trigger-title { - font-weight: normal; - vertical-align: top; - transform: translateY(-2px); - -} - -.fern-accordion > [data-state="closed"] .fern-accordion-trigger-title > .accordion-show-properties::before { - content: "Show properties"; -} - -.fern-accordion > [data-state="open"] .fern-accordion-trigger-title > .accordion-show-properties::before { - content: "Hide properties"; -} - -.fern-accordion > [data-state="closed"] .fern-accordion-trigger-title > .accordion-show-possible-values::before { - content: "Show possible values"; -} - -.fern-accordion > [data-state="open"] .fern-accordion-trigger-title > .accordion-show-possible-values::before { - content: "Hide possible values"; -} - -.fern-api-property.border-b:first-of-type { - padding-top: 0 !important; -} - - -.fern-api-property.border-b:last-of-type { - border-bottom: none !important; - padding-bottom: 0 !important; -} - -.small-codeblock-tabs .bg-tag-default-soft .fern-x-overflow { - flex-wrap: wrap; -} - -.small-codeblock-tabs .bg-tag-default-soft .fern-x-overflow .text-sm { - font-size: 75%; -} - -tr.stack-clickable-row { - cursor: pointer; - background-color: rgba(var(--accent), 0.12); - user-select: none; - -webkit-user-select: none; -} - -tr.stack-clickable-row:hover { - background-color: rgba(var(--accent), 0.17); -} - -tr.stack-clickable-row:active { - background-color: rgba(var(--accent), 0.25); -} - -tr.stack-clickable-row-missing { - background-color: red !important; -} - -svg[icon*="square-t"] { - color: #763fa6 !important; -} - -svg[icon*="square-h"] { - color: #3fa641 !important; -} - -svg[icon*="square-code"] { - color: #4448c2 !important; -} - -svg[icon*="square-u"] { - color: #eb7f38 !important; -} - -.stack-dim-text { - opacity: 0.5; -} - -.fern-sidebar-link span + svg { - display: none; -} - -div.stack-50h > span > span > img, img.stack-50h { - aspect-ratio: auto !important; - height: unset !important; - width: unset !important; - height: 50px !important; - object-fit: contain !important; -} - -div.stack-100h > span > span > img, img.stack-100h { - aspect-ratio: auto !important; - height: unset !important; - width: unset !important; - height: 100px !important; - object-fit: contain !important; -} - -div.stack-150h > span > span > img, img.stack-150h { - aspect-ratio: auto !important; - height: unset !important; - width: unset !important; - height: 150px !important; - object-fit: contain !important; -} - -div.stack-200h > span > span > img, img.stack-200h { - aspect-ratio: auto !important; - height: unset !important; - width: unset !important; - height: 200px !important; - object-fit: contain !important; -} - -div.stack-250h > span > span > img, img.stack-250h { - aspect-ratio: auto !important; - height: unset !important; - width: unset !important; - height: 250px !important; - object-fit: contain !important; -} - -div.stack-300h > span > span > img, img.stack-300h { - aspect-ratio: auto !important; - height: unset !important; - width: unset !important; - height: 300px !important; - object-fit: contain !important; -} - -div.stack-350h > span > span > img, img.stack-350h { - aspect-ratio: auto !important; - height: unset !important; - width: unset !important; - height: 350px !important; - object-fit: contain !important; -} - -.fern-layout-reference-content { - grid-template-columns: repeat(1,minmax(0,1fr)) !important; -} diff --git a/docs/lib/source.ts b/docs/lib/source.ts new file mode 100644 index 000000000..282865f39 --- /dev/null +++ b/docs/lib/source.ts @@ -0,0 +1,48 @@ +import { api, docs } from '@/.source'; +import { loader } from 'fumadocs-core/source'; +import { attachFile } from 'fumadocs-openapi/server'; +import { icons } from 'lucide-react'; +import { createElement } from 'react'; + +// Main docs source for /docs routes +export const source = loader({ + // it assigns a URL to your pages + baseUrl: '/docs', + source: docs.toFumadocsSource(), + pageTree: { + attachFile, + }, + icon(icon) { + if (!icon) { + // You may set a default icon here if needed + return; + } + + if (icon in icons) { + return createElement(icons[icon as keyof typeof icons]); + } + + // Fallback to undefined if icon is not found + return; + }, +}); + +// API source for /api routes +export const apiSource = loader({ + baseUrl: '/api', + source: api.toFumadocsSource(), + pageTree: { + attachFile, + }, + icon(icon) { + if (!icon) { + return; + } + + if (icon in icons) { + return createElement(icons[icon as keyof typeof icons]); + } + + return; + }, +}); diff --git a/docs/next.config.mjs b/docs/next.config.mjs new file mode 100644 index 000000000..0e8fba19c --- /dev/null +++ b/docs/next.config.mjs @@ -0,0 +1,40 @@ +import { createMDX } from 'fumadocs-mdx/next'; + +const withMDX = createMDX(); + +/** @type {import('next').NextConfig} */ +const config = { + reactStrictMode: true, + eslint: { + // Temporarily disable ESLint during builds for Vercel deployment + ignoreDuringBuilds: true, + }, + // Include OpenAPI files in output tracing for Vercel deployments + outputFileTracingIncludes: { + '/api/**/*': ['./openapi/**/*'], + '/**/*': ['./openapi/**/*'], + }, + async redirects() { + return [ + // Redirect /docs/api to the overview page + { + source: '/docs/api', + destination: '/docs/api/overview', + permanent: false, + }, + ]; + }, + async rewrites() { + return [ + // Serve OpenAPI files from the openapi directory + { + source: '/openapi/:path*', + destination: '/openapi/:path*', + }, + // No other rewrites needed for API docs - they're served directly from /docs/api/* + ]; + }, +}; + +export default withMDX(config); + diff --git a/docs/package.json b/docs/package.json index 29d1bde65..254e629b8 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,22 +1,60 @@ { - "name": "@stackframe/docs", + "name": "@stackframe/stack-docs", "version": "2.8.15", "description": "", "main": "index.js", "private": true, + "type": "module", "scripts": { - "clean": "rimraf .next && rimraf node_modules && rimraf fern/openapi/*.yaml", - "dev": "pnpm run fern docs dev --port 8104", - "fern": "fern", - "build": "pnpm run fern check" + "build": "next build", + "dev": "next dev --port 8104 --hostname 0.0.0.0", + "start": "next start --port 8104", + "lint": "next lint", + "lint:fix": "next lint --fix", + "postinstall": "fumadocs-mdx", + "generate-docs": "node scripts/generate-docs.js", + "generate-openapi-docs": "node scripts/generate-functional-api-docs.mjs", + "setup-openapi": "node scripts/setup-openapi.mjs", + "clear-docs": "node scripts/clear-docs.js" }, - "keywords": [], - "author": "", "dependencies": { - "fern-api": "^0.53.17" + "@radix-ui/react-collapsible": "^1.1.11", + "@radix-ui/react-popover": "^1.1.14", + "@radix-ui/react-presence": "^1.1.4", + "@radix-ui/react-scroll-area": "^1.2.9", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-tabs": "^1.1.12", + "@stackframe/stack": "workspace:^", + "@xyflow/react": "^12.6.4", + "class-variance-authority": "^0.7.1", + "fumadocs-core": "15.3.3", + "fumadocs-mdx": "11.6.4", + "fumadocs-openapi": "^8.1.12", + "fumadocs-typescript": "^4.0.5", + "fumadocs-ui": "15.3.3", + "js-yaml": "^4.1.0", + "lucide-react": "^0.511.0", + "mermaid": "^11.6.0", + "minimatch": "^10.0.1", + "next": "15.3.2", + "next-themes": "^0.4.6", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-remove-scroll": "^2.7.0", + "shiki": "^3.4.2", + "tailwind-merge": "^3.3.0" }, "devDependencies": { - "rimraf": "^5.0.7", - "@stackframe/stack-backend": "workspace:*" + "@tailwindcss/postcss": "^4.1.7", + "@types/mdx": "^2.0.13", + "@types/node": "22.15.18", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.0", + "eslint": "^8", + "eslint-config-next": "15.3.2", + "glob": "^11.0.0", + "postcss": "^8.5.3", + "tailwindcss": "^4.1.7", + "typescript": "^5.8.3" } } diff --git a/docs/postcss.config.mjs b/docs/postcss.config.mjs new file mode 100644 index 000000000..a34a3d560 --- /dev/null +++ b/docs/postcss.config.mjs @@ -0,0 +1,5 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + }, +}; diff --git a/docs/fern/docs/pages-template/imgs/account-settings.png b/docs/public/imgs/account-settings.png similarity index 100% rename from docs/fern/docs/pages-template/imgs/account-settings.png rename to docs/public/imgs/account-settings.png diff --git a/docs/fern/docs/pages-template/imgs/credential-sign-in.png b/docs/public/imgs/credential-sign-in.png similarity index 100% rename from docs/fern/docs/pages-template/imgs/credential-sign-in.png rename to docs/public/imgs/credential-sign-in.png diff --git a/docs/fern/docs/pages-template/imgs/credential-sign-up.png b/docs/public/imgs/credential-sign-up.png similarity index 100% rename from docs/fern/docs/pages-template/imgs/credential-sign-up.png rename to docs/public/imgs/credential-sign-up.png diff --git a/docs/fern/docs/pages-template/imgs/dashboard.png b/docs/public/imgs/dashboard.png similarity index 100% rename from docs/fern/docs/pages-template/imgs/dashboard.png rename to docs/public/imgs/dashboard.png diff --git a/docs/fern/docs/pages-template/imgs/oauth-button-group.png b/docs/public/imgs/oauth-button-group.png similarity index 100% rename from docs/fern/docs/pages-template/imgs/oauth-button-group.png rename to docs/public/imgs/oauth-button-group.png diff --git a/docs/fern/docs/pages-template/imgs/oauth-button.png b/docs/public/imgs/oauth-button.png similarity index 100% rename from docs/fern/docs/pages-template/imgs/oauth-button.png rename to docs/public/imgs/oauth-button.png diff --git a/docs/fern/docs/pages-template/imgs/selected-team-switcher.png b/docs/public/imgs/selected-team-switcher.png similarity index 100% rename from docs/fern/docs/pages-template/imgs/selected-team-switcher.png rename to docs/public/imgs/selected-team-switcher.png diff --git a/docs/fern/docs/pages-template/imgs/sign-in.png b/docs/public/imgs/sign-in.png similarity index 100% rename from docs/fern/docs/pages-template/imgs/sign-in.png rename to docs/public/imgs/sign-in.png diff --git a/docs/fern/docs/pages-template/imgs/sign-up.png b/docs/public/imgs/sign-up.png similarity index 100% rename from docs/fern/docs/pages-template/imgs/sign-up.png rename to docs/public/imgs/sign-up.png diff --git a/docs/fern/docs/pages-template/imgs/signup-page.png b/docs/public/imgs/signup-page.png similarity index 100% rename from docs/fern/docs/pages-template/imgs/signup-page.png rename to docs/public/imgs/signup-page.png diff --git a/docs/fern/docs/pages-template/imgs/team-switcher.png b/docs/public/imgs/team-switcher.png similarity index 100% rename from docs/fern/docs/pages-template/imgs/team-switcher.png rename to docs/public/imgs/team-switcher.png diff --git a/docs/fern/docs/pages-template/imgs/user-button.png b/docs/public/imgs/user-button.png similarity index 100% rename from docs/fern/docs/pages-template/imgs/user-button.png rename to docs/public/imgs/user-button.png diff --git a/docs/fern/docs/assets/logo-dark-mode.svg b/docs/public/logo-dark-mode.svg similarity index 100% rename from docs/fern/docs/assets/logo-dark-mode.svg rename to docs/public/logo-dark-mode.svg diff --git a/docs/scripts/clear-docs.js b/docs/scripts/clear-docs.js new file mode 100644 index 000000000..f827e4fad --- /dev/null +++ b/docs/scripts/clear-docs.js @@ -0,0 +1,58 @@ +#!/usr/bin/env node + +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +// Get __dirname equivalent in ES modules +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/** + * Recursively remove all files and directories within a directory + * but keep the directory itself + */ +function clearDirectory(dirPath) { + if (!fs.existsSync(dirPath)) { + console.log(`Directory ${dirPath} does not exist.`); + return; + } + + const items = fs.readdirSync(dirPath); + + for (const item of items) { + const itemPath = path.join(dirPath, item); + const stat = fs.statSync(itemPath); + + if (stat.isDirectory()) { + // Recursively remove directory and all its contents + fs.rmSync(itemPath, { recursive: true, force: true }); + console.log(`Removed directory: ${itemPath}`); + } else { + // Remove file + fs.unlinkSync(itemPath); + console.log(`Removed file: ${itemPath}`); + } + } +} + +function main() { + const docsPath = path.join(__dirname, '..', 'content', 'docs'); + const apiDocsPath = path.join(__dirname, '..', 'content', 'api'); + + console.log('🧹 Clearing all files and directories in content/docs, and content/api'); + console.log(`Target directory: ${docsPath}`); + console.log(`Target directory: ${apiDocsPath}`); + + try { + clearDirectory(docsPath); + clearDirectory(apiDocsPath); + console.log('✅ Successfully cleared content/docs directory!'); + } catch (error) { + console.error('❌ Error clearing content/docs directory:', error.message); + process.exit(1); + } +} + +// Run the script +main(); diff --git a/docs/scripts/generate-docs.js b/docs/scripts/generate-docs.js new file mode 100644 index 000000000..f7076c952 --- /dev/null +++ b/docs/scripts/generate-docs.js @@ -0,0 +1,340 @@ +import fs from 'fs'; +import { glob } from 'glob'; +import yaml from 'js-yaml'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +// Get __dirname equivalent in ESM +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Configure paths +const TEMPLATE_DIR = path.resolve(__dirname, '../templates'); +const OUTPUT_BASE_DIR = path.resolve(__dirname, '../content/docs'); +const CONFIG_FILE = path.resolve(__dirname, '../docs-platform.yml'); +const PLATFORMS = ['next', 'react', 'js', 'python']; + +// Platform groups mapping +const PLATFORM_GROUPS = { + 'react-like': ['next', 'react'], // Platforms that use React components + 'js-like': ['next', 'react', 'js'] // Platforms that use JavaScript SDK (includes React-based platforms) +}; + +// Load platform configuration +let platformConfig = {}; +try { + const configContent = fs.readFileSync(CONFIG_FILE, 'utf8'); + platformConfig = yaml.load(configContent); + console.log('Loaded platform configuration from docs-platform.yml'); +} catch (error) { + console.error('Failed to load platform configuration:', error.message); + console.log('Falling back to include all files for all platforms'); +} + +// Platform folder naming - now using root folders +function getFolderName(platform) { + return platform; // Use direct platform names instead of pages-{platform} +} + +// Platform display names +function getPlatformDisplayName(platform) { + const platformNames = { + 'next': 'Next.js', + 'react': 'React', + 'js': 'JavaScript', + 'python': 'Python' + }; + return platformNames[platform] || platform; +} + +// Platform-specific content markers - Updated regex to handle both syntaxes (with and without colon) +const PLATFORM_START_MARKER = /{\s*\/\*\s*IF_PLATFORM:?\s*([\w-]+)\s*\*\/\s*}/; +const PLATFORM_ELSE_MARKER = /{\s*\/\*\s*ELSE_IF_PLATFORM:?\s+([\w-]+)\s*\*\/\s*}/; +const PLATFORM_END_MARKER = /{\s*\/\*\s*END_PLATFORM\s*\*\/\s*}/; + +/** + * Check if a platform or platform group includes the target platform + */ +function isPlatformMatch(platformSpec, targetPlatform) { + // Direct platform match + if (platformSpec === targetPlatform) { + return true; + } + + // Platform group match + if (PLATFORM_GROUPS[platformSpec]) { + return PLATFORM_GROUPS[platformSpec].includes(targetPlatform); + } + + return false; +} + +/** + * Check if a file should be included for a specific platform + */ +function shouldIncludeFileForPlatform(platform, filePath) { + // If no configuration loaded, include everything + if (!platformConfig.pages) { + return true; + } + + // Find the page configuration for this file + const pageConfig = platformConfig.pages.find(page => page.path === filePath); + + // If no specific configuration found, exclude by default + if (!pageConfig) { + console.log(`No configuration found for ${filePath}, excluding by default`); + return false; + } + + // Check if the platform is in the allowed list + return pageConfig.platforms.includes(platform); +} + +/** + * Process a template file for a specific platform + */ +function processTemplateForPlatform(content, targetPlatform) { + const lines = content.split('\n'); + let result = []; + let currentPlatformSpec = null; + let isIncluding = true; + let platformSection = false; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + // Check for platform start + const startMatch = line.match(PLATFORM_START_MARKER); + if (startMatch) { + platformSection = true; + currentPlatformSpec = startMatch[1]; + isIncluding = isPlatformMatch(currentPlatformSpec, targetPlatform); + continue; + } + + // Check for platform else + const elseMatch = line.match(PLATFORM_ELSE_MARKER); + if (elseMatch && platformSection) { + currentPlatformSpec = elseMatch[1]; + isIncluding = isPlatformMatch(currentPlatformSpec, targetPlatform); + continue; + } + + // Check for platform end + const endMatch = line.match(PLATFORM_END_MARKER); + if (endMatch && platformSection) { + platformSection = false; + isIncluding = true; + continue; + } + + // Include the line if we're supposed to + if (isIncluding) { + result.push(line); + } + } + + return result.join('\n'); +} + +/** + * Generate meta.json files for Fumadocs navigation + */ +function generateMetaFiles() { + // Process meta.json files for each platform from templates + for (const platform of PLATFORMS) { + const folderName = getFolderName(platform); + const platformDisplayName = getPlatformDisplayName(platform); + + // Find all meta.json files in the template directory + const metaFiles = glob.sync('**/meta.json', { cwd: TEMPLATE_DIR }); + + for (const metaFile of metaFiles) { + const srcPath = path.join(TEMPLATE_DIR, metaFile); + const destPath = path.join(OUTPUT_BASE_DIR, folderName, metaFile); + + // If this is a nested meta.json (not root), check if the folder should exist for this platform + if (metaFile !== 'meta.json') { + const folderPath = path.dirname(metaFile); + + // Check if any pages in this folder are included for this platform + const hasContentInFolder = platformConfig.pages && platformConfig.pages.some(configPage => + configPage.path.startsWith(`${folderPath}/`) && + configPage.platforms.includes(platform) + ); + + if (!hasContentInFolder) { + console.log(`Skipped meta.json for ${folderPath} (no content for ${platform})`); + continue; // Skip this meta.json file + } + } + + // Read and parse the template meta.json + const templateContent = fs.readFileSync(srcPath, 'utf8'); + const metaData = JSON.parse(templateContent); + + // If this is the root meta.json, customize it for the platform + if (metaFile === 'meta.json') { + metaData.title = platformDisplayName; + metaData.description = `Stack Auth for ${platformDisplayName} applications`; + metaData.root = true; + + // Filter pages based on platform configuration + if (platformConfig.pages && metaData.pages) { + const cleanedPages = []; + let currentSectionPages = []; + let currentSectionHeader = null; + + for (let i = 0; i < metaData.pages.length; i++) { + const page = metaData.pages[i]; + + // If this is a section divider + if (typeof page === 'string' && page.startsWith('---')) { + // Process the previous section first (or pages before first section) + if (currentSectionPages.length > 0) { + if (currentSectionHeader !== null) { + // Add section header if we had one + cleanedPages.push(currentSectionHeader); + } + cleanedPages.push(...currentSectionPages); + } + + // Start new section + currentSectionHeader = page; + currentSectionPages = []; + } + // If this is a folder reference (like "...customization") + else if (typeof page === 'string' && page.startsWith('...')) { + // Only include folder references if they have content for this platform + const folderName = page.substring(3); // Remove "..." + const hasContentInFolder = platformConfig.pages.some(configPage => + configPage.path.startsWith(`${folderName}/`) && + configPage.platforms.includes(platform) + ); + + if (hasContentInFolder) { + currentSectionPages.push(page); + } + } + // Regular page + else { + const pagePath = `${page}.mdx`; + if (shouldIncludeFileForPlatform(platform, pagePath)) { + currentSectionPages.push(page); + } + } + } + + // Don't forget the last section (or remaining pages) + if (currentSectionPages.length > 0) { + if (currentSectionHeader !== null) { + cleanedPages.push(currentSectionHeader); + } + cleanedPages.push(...currentSectionPages); + } + + metaData.pages = cleanedPages; + } + } + + // Create directory if it doesn't exist + fs.mkdirSync(path.dirname(destPath), { recursive: true }); + + // Write the processed meta.json + fs.writeFileSync(destPath, JSON.stringify(metaData, null, 2)); + console.log(`Generated platform-specific meta.json for ${platform}: ${destPath}`); + } + } +} + +/** + * Copy assets from template to platform-specific directories + */ +function copyAssets() { + const assetDirs = ['imgs']; + + for (const dir of assetDirs) { + const srcDir = path.join(TEMPLATE_DIR, dir); + + if (fs.existsSync(srcDir)) { + // Copy assets to each platform directory + for (const platform of PLATFORMS) { + const folderName = getFolderName(platform); + const destDir = path.join(OUTPUT_BASE_DIR, folderName, dir); + fs.mkdirSync(destDir, { recursive: true }); + + // Find and copy all files + const files = glob.sync('**/*', { cwd: srcDir, nodir: true }); + for (const file of files) { + const srcFile = path.join(srcDir, file); + const destFile = path.join(destDir, file); + fs.mkdirSync(path.dirname(destFile), { recursive: true }); + fs.copyFileSync(srcFile, destFile); + console.log(`Copied asset: ${srcFile} -> ${destFile}`); + } + } + } + } +} + +/** + * Main function to generate platform-specific docs + */ +function generateDocs() { + // Find all MDX files in the template directory + const templateFiles = glob.sync('**/*.mdx', { cwd: TEMPLATE_DIR }); + + if (templateFiles.length === 0) { + console.warn(`No template files found in ${TEMPLATE_DIR}`); + return; + } + + console.log(`Found ${templateFiles.length} template files`); + + // Process for each platform + for (const platform of PLATFORMS) { + const folderName = getFolderName(platform); + const outputDir = path.join(OUTPUT_BASE_DIR, folderName); + + // Create the output directory + fs.mkdirSync(outputDir, { recursive: true }); + + // Process each template file + for (const file of templateFiles) { + // Check if this file should be included for this platform + if (!shouldIncludeFileForPlatform(platform, file)) { + console.log(`Skipped file (not configured for platform): ${file} for ${platform}`); + continue; + } + + const inputFile = path.join(TEMPLATE_DIR, file); + const outputFile = path.join(outputDir, file); + + // Read the template + const templateContent = fs.readFileSync(inputFile, 'utf8'); + + // Process for this platform + const processedContent = processTemplateForPlatform(templateContent, platform); + + // Create output directory if it doesn't exist + fs.mkdirSync(path.dirname(outputFile), { recursive: true }); + + // Write the processed content + fs.writeFileSync(outputFile, processedContent); + + console.log(`Generated: ${outputFile}`); + } + } + + // Generate meta.json files for navigation + generateMetaFiles(); + + // Copy assets (images, etc.) + copyAssets(); + + console.log('Documentation generation complete!'); +} + +// Run the generator +generateDocs(); diff --git a/docs/scripts/generate-functional-api-docs.mjs b/docs/scripts/generate-functional-api-docs.mjs new file mode 100644 index 000000000..aa9d91f86 --- /dev/null +++ b/docs/scripts/generate-functional-api-docs.mjs @@ -0,0 +1,536 @@ +import fs from 'fs'; +import { generateFiles } from 'fumadocs-openapi'; +import path from 'path'; + +// Use relative paths to avoid path duplication issues +const OPENAPI_DIR = './openapi'; +const OUTPUT_DIR = './content/api'; +const TEMPLATES_API_DIR = './templates-api'; + +// Define the functional tag order based on user requirements +const FUNCTIONAL_TAGS = [ + 'Anonymous', + 'API Keys', + 'CLI Authentication', + 'Contact Channels', + 'Oauth', // Note: OpenAPI uses "Oauth" not "OAuth" + 'OTP', + 'Password', + 'Permissions', + 'Projects', + 'Sessions', + 'Teams', + 'Users', + 'Others' // For any miscellaneous endpoints +]; + +/** + * Create a filtered OpenAPI spec containing only endpoints with the specified tag + */ +function createTagFilteredSpec(originalSpec, targetTag) { + const filteredSpec = { + ...originalSpec, + paths: {}, + webhooks: {} + }; + + // Filter regular API paths + if (originalSpec.paths) { + for (const [path, methods] of Object.entries(originalSpec.paths)) { + const filteredMethods = {}; + + for (const [method, endpoint] of Object.entries(methods)) { + if (endpoint.tags && endpoint.tags.includes(targetTag)) { + filteredMethods[method] = endpoint; + } + } + + // Only include the path if it has methods with the target tag + if (Object.keys(filteredMethods).length > 0) { + filteredSpec.paths[path] = filteredMethods; + } + } + } + + // Filter webhooks + if (originalSpec.webhooks) { + for (const [webhookName, methods] of Object.entries(originalSpec.webhooks)) { + const filteredMethods = {}; + + for (const [method, endpoint] of Object.entries(methods)) { + if (endpoint.tags && endpoint.tags.includes(targetTag)) { + filteredMethods[method] = endpoint; + } + } + + // Only include the webhook if it has methods with the target tag + if (Object.keys(filteredMethods).length > 0) { + filteredSpec.webhooks[webhookName] = filteredMethods; + } + } + } + + return filteredSpec; +} + +/** + * Get all unique tags from an OpenAPI spec + */ +function extractTags(spec) { + const tags = new Set(); + + // Handle regular API paths + if (spec.paths) { + for (const methods of Object.values(spec.paths)) { + for (const endpoint of Object.values(methods)) { + if (endpoint.tags) { + endpoint.tags.forEach(tag => tags.add(tag)); + } + } + } + } + + // Handle webhooks (different structure) + if (spec.webhooks) { + for (const webhookMethods of Object.values(spec.webhooks)) { + for (const endpoint of Object.values(webhookMethods)) { + if (endpoint.tags) { + endpoint.tags.forEach(tag => tags.add(tag)); + } + } + } + } + + return Array.from(tags); +} + +/** + * Convert tag name to a URL-friendly slug + */ +function tagToSlug(tag) { + return tag.toLowerCase() + .replace(/\s+/g, '-') + .replace(/[^a-z0-9-]/g, ''); +} + +/** + * Convert tag name to a readable folder name + */ +function tagToFolderName(tag) { + // Special case mappings + const specialCases = { + 'Oauth': 'oauth', + 'API Keys': 'api-keys', + 'CLI Authentication': 'cli-authentication', + 'Contact Channels': 'contact-channels', + 'OTP': 'otp' + }; + + return specialCases[tag] || tag.toLowerCase().replace(/\s+/g, '-'); +} + +/** + * Recursively find all MDX files in a directory + */ +function findMdxFiles(dir) { + const files = []; + + if (!fs.existsSync(dir)) { + return files; + } + + const items = fs.readdirSync(dir); + + for (const item of items) { + const fullPath = path.join(dir, item); + const stat = fs.statSync(fullPath); + + if (stat.isDirectory()) { + files.push(...findMdxFiles(fullPath)); + } else if (item.endsWith('.mdx')) { + files.push(fullPath); + } + } + + return files; +} + +/** + * Flatten the generated file structure + */ +function flattenGeneratedFiles(functionalCategoryPath) { + // Skip flattening - fumadocs expects the directory structure to match routing + // The original nested structure from fumadocs-openapi is what we want to keep + console.log(` 📁 Keeping original directory structure for ${functionalCategoryPath}`); +} + +/** + * Update document references in MDX files to point to permanent filtered OpenAPI files + */ +function updateDocumentReferences(functionalCategoryPath, newDocumentPath) { + const mdxFiles = findMdxFiles(functionalCategoryPath); + + for (const filePath of mdxFiles) { + const content = fs.readFileSync(filePath, 'utf8'); + + // Update the document reference in the APIPage component + // Match both old public/openapi paths and temp files + const updatedContent = content.replace( + /document=\{"(public\/openapi\/|openapi\/)[^"]+"\}/g, + `document={"${newDocumentPath}"}` + ); + + if (content !== updatedContent) { + fs.writeFileSync(filePath, updatedContent); + console.log(` 🔗 Updated document reference in ${path.basename(filePath)}`); + } + } +} + +/** + * Replace APIPage with EnhancedAPIPage in generated MDX files + */ +function replaceAPIPageWithEnhanced(functionalCategoryPath) { + const mdxFiles = findMdxFiles(functionalCategoryPath); + + for (const filePath of mdxFiles) { + const content = fs.readFileSync(filePath, 'utf8'); + + // Replace APIPage with EnhancedAPIPage + const updatedContent = content.replace( + /-" or ">" syntax) + if (frontmatterContent.includes('description: >')) { + // Extract multiline description + const multilineMatch = frontmatterContent.match(/description:\s*>-?\s*\n((?:\s{2,}.*\n?)*)/); + if (multilineMatch) { + // Join the indented lines and clean up + description = multilineMatch[1] + .split('\n') + .map(line => line.replace(/^\s{2,}/, '')) // Remove leading indentation + .filter(line => line.trim()) // Remove empty lines + .join(' ') + .trim(); + } + } else { + // Handle single-line descriptions + const singleLineMatch = frontmatterContent.match(/description:\s*['"]?([^'"]+?)['"]?\s*$/m); + if (singleLineMatch) { + description = singleLineMatch[1].trim(); + } + } + + if (!description) continue; + + // Add description prop to EnhancedAPIPage if not already present + const updatedContent = content.replace( + /(]*?)(\s+\/?>)/g, + (match, componentStart, componentEnd) => { + // Check if description prop already exists + if (componentStart.includes('description=')) { + return match; + } + // Add description prop + return `${componentStart} description={"${description.replace(/"/g, '\\"')}"}${componentEnd}`; + } + ); + + if (content !== updatedContent) { + fs.writeFileSync(filePath, updatedContent); + console.log(` 📝 Added description prop to EnhancedAPIPage in ${path.basename(filePath)}`); + } + } +} + +/** + * Add description prop to WebhooksAPIPage components from frontmatter + */ +function addDescriptionToWebhooksAPIPage(functionalCategoryPath) { + const mdxFiles = findMdxFiles(functionalCategoryPath); + + for (const filePath of mdxFiles) { + const content = fs.readFileSync(filePath, 'utf8'); + + // Extract description from frontmatter + const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); + if (!frontmatterMatch) continue; + + const frontmatterContent = frontmatterMatch[1]; + let description = null; + + // Handle multiline YAML descriptions (">-" or ">" syntax) + if (frontmatterContent.includes('description: >')) { + // Extract multiline description + const multilineMatch = frontmatterContent.match(/description:\s*>-?\s*\n((?:\s{2,}.*\n?)*)/); + if (multilineMatch) { + // Join the indented lines and clean up + description = multilineMatch[1] + .split('\n') + .map(line => line.replace(/^\s{2,}/, '')) // Remove leading indentation + .filter(line => line.trim()) // Remove empty lines + .join(' ') + .trim(); + } + } else { + // Handle single-line descriptions + const singleLineMatch = frontmatterContent.match(/description:\s*['"]?([^'"]+?)['"]?\s*$/m); + if (singleLineMatch) { + description = singleLineMatch[1].trim(); + } + } + + if (!description) continue; + + // Add description prop to WebhooksAPIPage if not already present + const updatedContent = content.replace( + /(]*?)(\s+\/?>)/g, + (match, componentStart, componentEnd) => { + // Check if description prop already exists + if (componentStart.includes('description=')) { + return match; + } + // Add description prop + return `${componentStart} description={"${description.replace(/"/g, '\\"')}"}${componentEnd}`; + } + ); + + if (content !== updatedContent) { + fs.writeFileSync(filePath, updatedContent); + console.log(` 📝 Added description prop to WebhooksAPIPage in ${path.basename(filePath)}`); + } + } +} + +/** + * Copy the API overview page from template + */ +function copyAPIOverviewFromTemplate() { + console.log('📄 Copying API overview page from template...'); + + const templatePath = path.join(TEMPLATES_API_DIR, 'overview.mdx'); + const outputPath = path.join(OUTPUT_DIR, 'overview.mdx'); + + if (!fs.existsSync(templatePath)) { + console.error(`❌ Template file not found: ${templatePath}`); + console.log(' Please create the template file first.'); + return; + } + + // Ensure output directory exists + fs.mkdirSync(path.dirname(outputPath), { recursive: true }); + + // Copy the template file + fs.copyFileSync(templatePath, outputPath); + console.log('✅ Copied API overview page from template'); +} + +async function generateFunctionalAPIDocs() { + console.log('🚀 Starting functional OpenAPI documentation generation...\n'); + + // Ensure the OpenAPI directory exists + if (!fs.existsSync(OPENAPI_DIR)) { + console.log('Creating OpenAPI directory...'); + fs.mkdirSync(OPENAPI_DIR, { recursive: true }); + } + + // Process each API type in complete isolation to avoid fumadocs conflicts + const apiTypes = ['client', 'server', 'admin', 'webhooks']; + + for (const apiType of apiTypes) { + await processApiTypeInIsolation(apiType); + } + + // Copy API overview page from template + copyAPIOverviewFromTemplate(); + + // Generate main API meta.json + console.log('📁 Generating main API navigation...'); + const mainApiMetaPath = path.join(OUTPUT_DIR, 'meta.json'); + const mainApiMeta = { + pages: [ + 'overview', + 'client', + 'server', + 'admin', + 'webhooks' + ] + }; + + fs.writeFileSync(mainApiMetaPath, JSON.stringify(mainApiMeta, null, 2)); + console.log('✅ Generated main API meta.json'); + + console.log('\n🎉 Functional OpenAPI documentation generation complete!'); + console.log(`📂 Documentation generated in: ${path.resolve(OUTPUT_DIR)}/`); + console.log('\n📋 Structure:'); + console.log(' /overview.mdx'); + console.log(' /client/{functional-categories}/'); + console.log(' /server/{functional-categories}/'); + console.log(' /admin/{functional-categories}/'); + console.log(' /webhooks/'); +} + +/** + * Process a single API type in complete isolation + */ +async function processApiTypeInIsolation(apiType) { + const jsonFile = path.join(OPENAPI_DIR, `${apiType}.json`); + + if (!fs.existsSync(jsonFile)) { + console.log(`⚠️ OpenAPI file not found: ${jsonFile}`); + console.log(` Run 'pnpm run generate-openapi-fumadocs' from the root to generate OpenAPI schemas first.`); + return; + } + + console.log(`🔄 Processing ${apiType} API in isolation...`); + + // Read and parse the OpenAPI spec + const originalSpec = JSON.parse(fs.readFileSync(jsonFile, 'utf8')); + + // Extract all tags from this API + const availableTags = extractTags(originalSpec); + console.log(` 📋 Found tags: ${availableTags.join(', ')}`); + + // Process each functional tag for this API type + const processedTags = []; + for (const tag of FUNCTIONAL_TAGS) { + if (!availableTags.includes(tag)) { + continue; // Skip tags not present in this API + } + + console.log(` 📄 Generating docs for ${tag}...`); + + // Create filtered spec for this tag + const tagFilteredSpec = createTagFilteredSpec(originalSpec, tag); + + // Create temporary file for this tag's spec + const tempSpecPath = path.join(OPENAPI_DIR, `temp-${apiType}-${tagToSlug(tag)}.json`); + fs.writeFileSync(tempSpecPath, JSON.stringify(tagFilteredSpec, null, 2)); + + try { + // Generate docs for this tag in isolation + const outputPath = path.join(OUTPUT_DIR, apiType, tagToFolderName(tag)); + + // Create permanent filtered OpenAPI file + const permanentSpecPath = path.join(OPENAPI_DIR, `${apiType}-${tagToSlug(tag)}.json`); + fs.writeFileSync(permanentSpecPath, JSON.stringify(tagFilteredSpec, null, 2)); + + // Process this tag completely in isolation - no fumadocs state shared between calls + await generateFiles({ + input: [tempSpecPath], + output: outputPath, + includeDescription: false, + frontmatter: (title, description) => ({ + title, + description, + full: true, // Use full-width layout for API docs + }), + }); + + console.log(` ✅ Generated ${tag} docs for ${apiType}`); + processedTags.push(tag); + + // Keep original directory structure for proper fumadocs routing + flattenGeneratedFiles(outputPath); + + // Update document references in MDX files + console.log(` 🔗 Updating document references for ${tag}...`); + updateDocumentReferences(outputPath, `openapi/${apiType}-${tagToSlug(tag)}.json`); + + // Replace APIPage with appropriate component based on API type + if (apiType === 'webhooks') { + console.log(` 🔄 Replacing APIPage with WebhooksAPIPage for ${tag}...`); + replaceAPIPageWithWebhooks(outputPath); + + // Add description prop to WebhooksAPIPage + console.log(` 📝 Adding description prop to WebhooksAPIPage for ${tag}...`); + addDescriptionToWebhooksAPIPage(outputPath); + } else { + console.log(` 🔄 Replacing APIPage with EnhancedAPIPage for ${tag}...`); + replaceAPIPageWithEnhanced(outputPath); + + // Add description prop to EnhancedAPIPage + console.log(` 📝 Adding description prop to EnhancedAPIPage for ${tag}...`); + addDescriptionToEnhancedAPIPage(outputPath); + } + + } catch (error) { + console.error(` ❌ Error generating ${tag} docs for ${apiType}:`, error); + } finally { + // Clean up temporary file + if (fs.existsSync(tempSpecPath)) { + fs.unlinkSync(tempSpecPath); + } + } + } + + // Generate meta.json for this API type + if (processedTags.length > 0) { + console.log(` 📁 Generating navigation meta for ${apiType}...`); + + const apiMetaPath = path.join(OUTPUT_DIR, apiType, 'meta.json'); + const apiMeta = { + pages: processedTags.map(tag => tagToFolderName(tag)) + }; + + fs.mkdirSync(path.dirname(apiMetaPath), { recursive: true }); + fs.writeFileSync(apiMetaPath, JSON.stringify(apiMeta, null, 2)); + + console.log(` ✅ Generated meta.json for ${apiType} API`); + } + + console.log(`🎯 Completed ${apiType} API processing (${processedTags.length} functional categories)\n`); +} + +// Run the generator +generateFunctionalAPIDocs().catch((error) => { + console.error('❌ Failed to generate functional API documentation:', error); + process.exit(1); +}); diff --git a/docs/source.config.ts b/docs/source.config.ts new file mode 100644 index 000000000..94379c9c0 --- /dev/null +++ b/docs/source.config.ts @@ -0,0 +1,34 @@ +import { + defineConfig, + defineDocs, + frontmatterSchema, + metaSchema, +} from 'fumadocs-mdx/config'; + +// You can customise Zod schemas for frontmatter and `meta.json` here +// see https://fumadocs.vercel.app/docs/mdx/collections#define-docs +export const docs = defineDocs({ + docs: { + schema: frontmatterSchema, + }, + meta: { + schema: metaSchema, + }, +}); + +// Separate collection for API content +export const api = defineDocs({ + dir: './content/api', + docs: { + schema: frontmatterSchema, + }, + meta: { + schema: metaSchema, + }, +}); + +export default defineConfig({ + mdxOptions: { + // MDX options + }, +}); diff --git a/docs/src/app/(home)/layout.tsx b/docs/src/app/(home)/layout.tsx new file mode 100644 index 000000000..df6ebe8ea --- /dev/null +++ b/docs/src/app/(home)/layout.tsx @@ -0,0 +1,20 @@ +import { baseOptions } from '@/app/layout.config'; +import Footer from '@/components/homepage/homepage-footer'; +import { HomeLayout } from 'fumadocs-ui/layouts/home'; + +// Enable search for home page navbar +const homeOptions = { + ...baseOptions, + searchToggle: { + enabled: true, + }, +}; + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + + {children} +