diff --git a/.github/workflows/docs-last-changed.yml b/.github/workflows/docs-last-changed.yml index 882e7847367..5025e4b0d10 100644 --- a/.github/workflows/docs-last-changed.yml +++ b/.github/workflows/docs-last-changed.yml @@ -19,7 +19,7 @@ jobs: env: NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.SEARCH_SUPABASE_URL }} - SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SEARCH_SUPABASE_SERVICE_ROLE_KEY }} + SUPABASE_SECRET_KEY: ${{ secrets.SEARCH_SUPABASE_SERVICE_ROLE_KEY }} steps: - name: Check out repo diff --git a/.github/workflows/docs-lint-v2-comment.yml b/.github/workflows/docs-lint-v2-comment.yml index 89c037c42fa..7194e9e2181 100644 --- a/.github/workflows/docs-lint-v2-comment.yml +++ b/.github/workflows/docs-lint-v2-comment.yml @@ -1,4 +1,4 @@ -name: Comment on external PRs with linter results +name: docs_lint_comment_external # This is a continuation of ./docs-lint-v2.yml, to write comments on external # PRs. @@ -11,7 +11,7 @@ name: Comment on external PRs with linter results on: workflow_run: - workflows: ['[Docs] Lint v2'] + workflows: [docs_lint] types: - completed @@ -21,7 +21,7 @@ permissions: jobs: comment_on_pr: runs-on: ubuntu-latest - if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' + if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'failure' steps: - id: download_artifact name: 'Download artifact' diff --git a/.github/workflows/docs-lint-v2-scheduled.yml b/.github/workflows/docs-lint-v2-scheduled.yml index a6cfd2506a3..f476dfcf920 100644 --- a/.github/workflows/docs-lint-v2-scheduled.yml +++ b/.github/workflows/docs-lint-v2-scheduled.yml @@ -45,18 +45,15 @@ jobs: git config --global user.email 'github-docs-bot@supabase.com' BRANCH_NAME="bot/docs-lint-fixes" EXISTING_BRANCH=$(git ls-remote --heads origin $BRANCH_NAME) - if [ -n "$EXISTING_BRANCH" ]; then - git checkout $BRANCH_NAME - else - git checkout -b $BRANCH_NAME + if [[ -n "$EXISTING_BRANCH" ]]; then + git push origin --delete $BRANCH_NAME fi + git checkout -b $BRANCH_NAME supa-mdx-lint apps/docs/content --fix || FIX_FAILED=1 git add . git commit -m '[bot] fix lint errors' || true git push origin $BRANCH_NAME - if [ -z "$EXISTING_BRANCH" ]; then - gh pr create --title '[bot] fix lint errors' --body 'This PR fixes lint errors in the documentation.' --head $BRANCH_NAME - fi + gh pr create --title '[bot] fix lint errors' --body 'This PR fixes lint errors in the documentation.' --head $BRANCH_NAME if [ "${FIX_FAILED:-0}" -eq 1 ]; then echo "Fix did not correct all errors." exit 1 diff --git a/.github/workflows/docs-lint-v2.yml b/.github/workflows/docs-lint-v2.yml index cf543f15f1e..4ceb1f82102 100644 --- a/.github/workflows/docs-lint-v2.yml +++ b/.github/workflows/docs-lint-v2.yml @@ -1,4 +1,4 @@ -name: '[Docs] Lint v2' +name: docs_lint # Runs the docs linter on PRs that edit docs content. # There are two branches of this workflow for internal and external PRs, due @@ -102,3 +102,6 @@ jobs: with: name: lint_results path: __github_actions__pr/ + - name: fail if linter fails (external) + if: steps.filter.outputs.docs == 'true' && github.event.pull_request.head.repo.full_name != github.repository && steps.external_lint.outputs.LINT_EXIT_CODE != 0 + run: exit 1 diff --git a/.github/workflows/docs-sync-troubleshooting.yml b/.github/workflows/docs-sync-troubleshooting.yml index f0d2ec07116..52c0d793e7c 100644 --- a/.github/workflows/docs-sync-troubleshooting.yml +++ b/.github/workflows/docs-sync-troubleshooting.yml @@ -17,9 +17,9 @@ jobs: runs-on: ubuntu-latest env: - SEARCH_GITHUB_APP_ID: ${{ secrets.SEARCH_GITHUB_APP_ID }} - SEARCH_GITHUB_APP_INSTALLATION_ID: ${{ secrets.SEARCH_GITHUB_APP_INSTALLATION_ID }} - SEARCH_GITHUB_APP_PRIVATE_KEY: ${{ secrets.SEARCH_GITHUB_APP_PRIVATE_KEY }} + DOCS_GITHUB_APP_ID: ${{ secrets.SEARCH_GITHUB_APP_ID }} + DOCS_GITHUB_APP_INSTALLATION_ID: ${{ secrets.SEARCH_GITHUB_APP_INSTALLATION_ID }} + DOCS_GITHUB_APP_PRIVATE_KEY: ${{ secrets.SEARCH_GITHUB_APP_PRIVATE_KEY }} NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.SEARCH_SUPABASE_URL }} SUPABASE_SECRET_KEY: ${{ secrets.SEARCH_SUPABASE_SERVICE_ROLE_KEY }} diff --git a/.github/workflows/search.yml b/.github/workflows/search.yml index 03de1b2e7aa..9d5aa59959a 100644 --- a/.github/workflows/search.yml +++ b/.github/workflows/search.yml @@ -29,12 +29,14 @@ jobs: runs-on: ubuntu-latest env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + DOCS_GITHUB_APP_ID: ${{ secrets.SEARCH_GITHUB_APP_ID }} + DOCS_GITHUB_APP_INSTALLATION_ID: ${{ secrets.SEARCH_GITHUB_APP_INSTALLATION_ID }} + DOCS_GITHUB_APP_PRIVATE_KEY: ${{ secrets.SEARCH_GITHUB_APP_PRIVATE_KEY }} + NEXT_PUBLIC_MISC_URL: ${{ secrets.NEXT_PUBLIC_MISC_URL}} + NEXT_PUBLIC_MISC_ANON_KEY: ${{ secrets.NEXT_PUBLIC_MISC_ANON_KEY }} NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.SEARCH_SUPABASE_URL }} - SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SEARCH_SUPABASE_SERVICE_ROLE_KEY }} - SEARCH_GITHUB_APP_ID: ${{ secrets.SEARCH_GITHUB_APP_ID }} - SEARCH_GITHUB_APP_INSTALLATION_ID: ${{ secrets.SEARCH_GITHUB_APP_INSTALLATION_ID }} - SEARCH_GITHUB_APP_PRIVATE_KEY: ${{ secrets.SEARCH_GITHUB_APP_PRIVATE_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + SUPABASE_SECRET_KEY: ${{ secrets.SEARCH_SUPABASE_SERVICE_ROLE_KEY }} steps: - name: Check out repo @@ -58,10 +60,6 @@ jobs: - name: Download dependencies run: pnpm i - # Need the miscellaneous use API, which is available publicly (by design) in www - - name: Copy environment variables - run: cp apps/www/.env.local.example apps/docs/.env - - name: Update embeddings working-directory: ./apps/docs if: ${{ !inputs.refresh }} diff --git a/apps/design-system/__registry__/index.tsx b/apps/design-system/__registry__/index.tsx index 2892f9da56a..fc9529a1f89 100644 --- a/apps/design-system/__registry__/index.tsx +++ b/apps/design-system/__registry__/index.tsx @@ -9,7 +9,7 @@ export const Index: Record = { name: "ConfirmationModal", type: "components:fragment", registryDependencies: undefined, - component: React.lazy(() => import("@/../../packages/ui-patterns/Dialogs/ConfirmationModal")), + component: React.lazy(() => import("@/../../packages/ui-patterns/src/Dialogs/ConfirmationModal")), source: "", files: ["registry/default//Dialogs/ConfirmationModal.tsx"], category: "undefined", @@ -20,7 +20,7 @@ export const Index: Record = { name: "TextConfirmModal", type: "components:fragment", registryDependencies: undefined, - component: React.lazy(() => import("@/../../packages/ui-patterns/Dialogs/TextConfirmModal")), + component: React.lazy(() => import("@/../../packages/ui-patterns/src/Dialogs/TextConfirmModal")), source: "", files: ["registry/default//Dialogs/TextConfirmModal.tsx"], category: "undefined", @@ -31,7 +31,7 @@ export const Index: Record = { name: "ConfirmDialog", type: "components:fragment", registryDependencies: undefined, - component: React.lazy(() => import("@/../../packages/ui-patterns/Dialogs/ConfirmDialog")), + component: React.lazy(() => import("@/../../packages/ui-patterns/src/Dialogs/ConfirmDialog")), source: "", files: ["registry/default//Dialogs/ConfirmDialog.tsx"], category: "undefined", diff --git a/apps/design-system/package.json b/apps/design-system/package.json index 15c2ba465ba..2263d111122 100644 --- a/apps/design-system/package.json +++ b/apps/design-system/package.json @@ -51,7 +51,6 @@ "@types/lodash.template": "4.5.0", "@types/react": "catalog:", "@types/react-dom": "catalog:", - "autoprefixer": "^10.0.1", "config": "workspace:^", "mdast-util-toc": "^6.1.1", "postcss": "^8.5.3", diff --git a/apps/design-system/postcss.config.cjs b/apps/design-system/postcss.config.cjs index 12a703d900d..08a01d4d167 100644 --- a/apps/design-system/postcss.config.cjs +++ b/apps/design-system/postcss.config.cjs @@ -1,6 +1,5 @@ module.exports = { plugins: { tailwindcss: {}, - autoprefixer: {}, }, -}; +} diff --git a/apps/design-system/scripts/build-registry.mts b/apps/design-system/scripts/build-registry.mts index abd22ef46a3..e2a9c5799da 100644 --- a/apps/design-system/scripts/build-registry.mts +++ b/apps/design-system/scripts/build-registry.mts @@ -211,7 +211,7 @@ export const Index: Record = { packagePath = `../../packages/ui/src/components/shadcn/ui` } if (type === 'fragment') { - packagePath = `../../packages/ui-patterns${item.optionalPath}` + packagePath = `../../packages/ui-patterns/src${item.optionalPath}` } if (type === 'example') { packagePath = `registry/${style.name}/${type}` diff --git a/apps/design-system/tailwind.config.js b/apps/design-system/tailwind.config.js index 5be9a0d5a1d..10c142cc9cb 100644 --- a/apps/design-system/tailwind.config.js +++ b/apps/design-system/tailwind.config.js @@ -8,8 +8,7 @@ module.exports = config({ // purge styles from grid library // './../../packages/ui/src/**/*.{tsx,ts,js}', - './../../packages/ui-patterns/**/*.{tsx,ts,js}', - '!./../../packages/ui-patterns/node_modules/**/*', // ignore node_modules rule + './../../packages/ui-patterns/src/**/*.{tsx,ts,js}', ], theme: { extend: { diff --git a/apps/design-system/tsconfig.json b/apps/design-system/tsconfig.json index 7dcbbf6d634..9e21bf5ea57 100644 --- a/apps/design-system/tsconfig.json +++ b/apps/design-system/tsconfig.json @@ -12,7 +12,7 @@ "incremental": true, "esModuleInterop": true, "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", diff --git a/apps/docs/.env.development b/apps/docs/.env.development index 7c41f0565e4..f5e09eed460 100644 --- a/apps/docs/.env.development +++ b/apps/docs/.env.development @@ -2,5 +2,17 @@ # Only publicly available environment variables should be listed here. # Do not add secrets here. Use .env.local instead. +NEXT_PUBLIC_SITE_URL="http://localhost:3001" +NEXT_PUBLIC_BASE_PATH="/docs" + +# Whether to enable features available only on Supabase's own hosted services +# Setting this to true requires certain secret keys +NEXT_PUBLIC_IS_PLATFORM="false" + +# Supabase project containing integration information NEXT_PUBLIC_MISC_URL="https://obuldanrptloktxcffvn.supabase.co" NEXT_PUBLIC_MISC_ANON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9idWxkYW5ycHRsb2t0eGNmZnZuIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTg2MTQ2ODUsImV4cCI6MjAzNDE5MDY4NX0.NFt49g6DFkc1X5khCzN5p01iAVo2TMxlx88cY1V0E2M" + +# Supabase project containing docs content information +NEXT_PUBLIC_SUPABASE_URL="https://xguihxuzqibwxjnimxev.supabase.co" +NEXT_PUBLIC_SUPABASE_ANON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhndWloeHV6cWlid3hqbmlteGV2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTgzNzc1MTgsImV4cCI6MjAzMzk1MzUxOH0.aIqjQ9V7djMxYit-DT1fYNV_VWMHSqldh_18XfX2_BE" diff --git a/apps/docs/.env.example b/apps/docs/.env.example deleted file mode 100644 index 735dd88086d..00000000000 --- a/apps/docs/.env.example +++ /dev/null @@ -1,14 +0,0 @@ -# PUBLIC -NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321 -NEXT_PUBLIC_SUPABASE_ANON_KEY= - -# PRIVATE -OPENAI_API_KEY= -SUPABASE_SERVICE_ROLE_KEY= -SEARCH_GITHUB_APP_ID= -SEARCH_GITHUB_APP_INSTALLATION_ID= -SEARCH_GITHUB_APP_PRIVATE_KEY= - -# Set IS_PLATFORM to true if you have the api keys and urls -NEXT_PUBLIC_IS_PLATFORM=false -NEXT_PUBLIC_LOCAL_SUPABASE=false diff --git a/apps/docs/.eslintrc.js b/apps/docs/.eslintrc.cjs similarity index 100% rename from apps/docs/.eslintrc.js rename to apps/docs/.eslintrc.cjs diff --git a/apps/docs/app/api/graphql/__snapshots__/route.test.ts.snap b/apps/docs/app/api/graphql/__snapshots__/route.test.ts.snap index c668a0b5208..5c0bac58a6a 100644 --- a/apps/docs/app/api/graphql/__snapshots__/route.test.ts.snap +++ b/apps/docs/app/api/graphql/__snapshots__/route.test.ts.snap @@ -131,6 +131,9 @@ type RootQueryType { """Search the Supabase docs for content matching a query string""" searchDocs(query: String!, limit: Int): SearchResultCollection + + """Get the details of an error code returned from a Supabase service""" + error(code: String!, service: Service!): Error } """A collection of search results containing content from Supabase docs""" @@ -149,5 +152,30 @@ type SearchResultCollection { type SearchResultEdge { """The SearchResult at the end of the edge""" node: SearchResult! +} + +"""An error returned by a Supabase service""" +type Error { + """ + The unique code identifying the error. The code is stable, and can be used for string matching during error handling. + """ + code: String! + + """The Supabase service that returns this error.""" + service: Service! + + """The HTTP status code returned with this error.""" + httpStatusCode: Int + + """ + A human-readable message describing the error. The message is not stable, and should not be used for string matching during error handling. Use the code instead. + """ + message: String +} + +enum Service { + AUTH + REALTIME + STORAGE }" `; diff --git a/apps/docs/app/api/graphql/route.test.ts b/apps/docs/app/api/graphql/route.test.ts index 8b464e23f9d..bcd6633e9bf 100644 --- a/apps/docs/app/api/graphql/route.test.ts +++ b/apps/docs/app/api/graphql/route.test.ts @@ -3,15 +3,11 @@ import { POST } from './route' describe('/api/graphql basic error statuses', () => { beforeAll(() => { - vi.mock('server-only', () => { - return {} - }) vi.spyOn(console, 'error').mockImplementation(() => {}) }) afterAll(() => { vi.restoreAllMocks() - vi.doUnmock('server-only') }) beforeEach(() => { diff --git a/apps/docs/app/api/graphql/tests/errors.test.ts b/apps/docs/app/api/graphql/tests/errors.test.ts new file mode 100644 index 00000000000..92972b850fb --- /dev/null +++ b/apps/docs/app/api/graphql/tests/errors.test.ts @@ -0,0 +1,64 @@ +import { describe, expect, it, vi } from 'vitest' +import { POST } from '../route' + +describe('/api/graphql error', () => { + it('returns the error matching given error code and service', async () => { + const errorQuery = ` + query { + error(code: "test_code", service: AUTH) { + code + service + httpStatusCode + message + } + } + ` + const request = new Request('http://localhost/api/graphql', { + method: 'POST', + body: JSON.stringify({ query: errorQuery }), + }) + + const result = await POST(request) + const { + data: { error }, + errors, + } = await result.json() + + expect(errors).toBeUndefined() + expect(error.code).toBe('test_code') + expect(error.service).toBe('AUTH') + expect(error.httpStatusCode).toBe(500) + expect(error.message).toBe('This is a test error message') + }) + + it('returns error if no matching error exists', async () => { + vi.spyOn(console, 'error').mockImplementation(() => {}) + + const errorQuery = ` + query { + error(code: "nonexistent_code", service: AUTH) { + code + service + httpStatusCode + message + } + } + ` + const request = new Request('http://localhost/api/graphql', { + method: 'POST', + body: JSON.stringify({ query: errorQuery }), + }) + + const result = await POST(request) + const { + data: { error }, + errors, + } = await result.json() + + expect(error).toBe(null) + expect(errors).toHaveLength(1) + expect(errors[0].message).toMatch(/not found/i) + + vi.restoreAllMocks() + }) +}) diff --git a/apps/docs/app/api/utils.ts b/apps/docs/app/api/utils.ts index a2e87017713..949947abce5 100644 --- a/apps/docs/app/api/utils.ts +++ b/apps/docs/app/api/utils.ts @@ -45,6 +45,24 @@ export class InvalidRequestError
extends } } +export class NoDataError
extends ApiError
{ + constructor(message: string, source?: unknown, details?: Details) { + super(`Data not found: ${message}`, source, details) + } + + isPrivate() { + return false + } + + isUserError() { + return true + } + + statusCode() { + return 404 + } +} + export function convertUnknownToApiError(error: unknown): ApiError { return new ApiError('Unknown error', error) } diff --git a/apps/docs/app/guides/ai/python/[slug]/page.tsx b/apps/docs/app/guides/ai/python/[slug]/page.tsx index 0edb4efe589..cddeb1a68e9 100644 --- a/apps/docs/app/guides/ai/python/[slug]/page.tsx +++ b/apps/docs/app/guides/ai/python/[slug]/page.tsx @@ -1,14 +1,14 @@ -import type { SerializeOptions } from 'next-mdx-remote/dist/types' import { notFound } from 'next/navigation' import { relative } from 'path' import rehypeSlug from 'rehype-slug' -import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils' import { fetchRevalidatePerDay } from '~/features/helpers.fetch' import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' +import { SerializeOptions } from '~/types/next-mdx-remote-serialize' export const dynamicParams = false @@ -135,4 +135,4 @@ const generateStaticParams = () => pageMap.map(({ slug }) => ({ slug })) const generateMetadata = genGuideMeta(getContent) export default PythonClientDocs -export { generateStaticParams, generateMetadata } +export { generateMetadata, generateStaticParams } diff --git a/apps/docs/app/guides/database/database-advisors/page.tsx b/apps/docs/app/guides/database/database-advisors/page.tsx index 184c8fee410..987979377ed 100644 --- a/apps/docs/app/guides/database/database-advisors/page.tsx +++ b/apps/docs/app/guides/database/database-advisors/page.tsx @@ -1,19 +1,18 @@ import { Octokit } from '@octokit/core' -import { capitalize } from 'lodash' -import { type SerializeOptions } from 'next-mdx-remote/dist/types' +import { capitalize } from 'lodash-es' import rehypeSlug from 'rehype-slug' - import { Heading } from 'ui' -import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { MDXRemoteBase } from '~/features/docs/MdxBase' import { fetchRevalidatePerDay } from '~/features/helpers.fetch' -import { Tabs, TabPanel } from '~/features/ui/Tabs' +import { TabPanel, Tabs } from '~/features/ui/Tabs' import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' +import { SerializeOptions } from '~/types/next-mdx-remote-serialize' // We fetch these docs at build time from an external repo const org = 'supabase' diff --git a/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx b/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx index 7589b023bb5..b1ded944cfc 100644 --- a/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx @@ -1,9 +1,9 @@ import matter from 'gray-matter' -import { type SerializeOptions } from 'next-mdx-remote/dist/types' import { readFile } from 'node:fs/promises' import { join, relative } from 'node:path' import rehypeSlug from 'rehype-slug' import emoji from 'remark-emoji' + import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' import { genGuideMeta, @@ -17,6 +17,7 @@ import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' import { octokit } from '~/lib/octokit' +import { SerializeOptions } from '~/types/next-mdx-remote-serialize' export const dynamicParams = false diff --git a/apps/docs/app/guides/deployment/ci/[slug]/page.tsx b/apps/docs/app/guides/deployment/ci/[slug]/page.tsx index 756ac13b384..d3f617171b3 100644 --- a/apps/docs/app/guides/deployment/ci/[slug]/page.tsx +++ b/apps/docs/app/guides/deployment/ci/[slug]/page.tsx @@ -1,15 +1,15 @@ -import { type SerializeOptions } from 'next-mdx-remote/dist/types' import { notFound } from 'next/navigation' import { relative } from 'node:path' import rehypeSlug from 'rehype-slug' -import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils' import { fetchRevalidatePerDay } from '~/features/helpers.fetch' import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' +import { SerializeOptions } from '~/types/next-mdx-remote-serialize' export const dynamicParams = false @@ -133,4 +133,4 @@ const generateStaticParams = () => pageMap.map(({ slug }) => ({ slug })) const generateMetadata = genGuideMeta(getContent) export default ActionDocs -export { generateStaticParams, generateMetadata } +export { generateMetadata, generateStaticParams } diff --git a/apps/docs/app/guides/deployment/terraform/[[...slug]]/page.tsx b/apps/docs/app/guides/deployment/terraform/[[...slug]]/page.tsx index 72b49c49a85..2bb83de9298 100644 --- a/apps/docs/app/guides/deployment/terraform/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/deployment/terraform/[[...slug]]/page.tsx @@ -1,10 +1,9 @@ import matter from 'gray-matter' -import { type SerializeOptions } from 'next-mdx-remote/dist/types' import { notFound } from 'next/navigation' import rehypeSlug from 'rehype-slug' -import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils' import { fetchRevalidatePerDay } from '~/features/helpers.fetch' import { isValidGuideFrontmatter } from '~/lib/docs' import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' @@ -17,6 +16,7 @@ import { terraformDocsOrg, terraformDocsRepo, } from '../terraformConstants' +import { SerializeOptions } from '~/types/next-mdx-remote-serialize' export const dynamicParams = false @@ -146,4 +146,4 @@ const generateStaticParams = async () => pageMap.map(({ slug }) => ({ slug: slug const generateMetadata = genGuideMeta(getContent) export default TerraformDocs -export { generateStaticParams, generateMetadata } +export { generateMetadata, generateStaticParams } diff --git a/apps/docs/app/guides/graphql/[[...slug]]/page.tsx b/apps/docs/app/guides/graphql/[[...slug]]/page.tsx index 70f9347385f..64b69a6a8c3 100644 --- a/apps/docs/app/guides/graphql/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/graphql/[[...slug]]/page.tsx @@ -1,7 +1,7 @@ -import { type SerializeOptions } from 'next-mdx-remote/dist/types' import { notFound } from 'next/navigation' import { isAbsolute, relative } from 'path' import rehypeSlug from 'rehype-slug' + import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { REVALIDATION_TAGS } from '~/features/helpers.fetch' @@ -9,6 +9,7 @@ import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLin import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' +import { SerializeOptions } from '~/types/next-mdx-remote-serialize' export const dynamicParams = false diff --git a/apps/docs/components/Feedback/Feedback.utils.ts b/apps/docs/components/Feedback/Feedback.utils.ts index 3337d2f57ee..c93ebb55715 100644 --- a/apps/docs/components/Feedback/Feedback.utils.ts +++ b/apps/docs/components/Feedback/Feedback.utils.ts @@ -1,4 +1,4 @@ -import { pick } from 'lodash' +import { pick } from 'lodash-es' /** * Gets the Notion team to send feedback to based on the pathname. diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index b06eb9be02e..641b40d937d 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -119,7 +119,7 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [ level: 'platform', }, { - label: 'Security', + label: 'Security & Compliance', icon: 'security', href: '/guides/security', level: 'security', @@ -2092,6 +2092,19 @@ export const security: NavMenuConstant = { { name: 'HIPAA', url: '/guides/security/hipaa-compliance' }, ], }, + { + name: 'Guides', + url: undefined, + items: [ + { name: 'Production Checklist', url: '/guides/deployment/going-into-prod' }, + { + name: 'Shared Responsibility Model', + url: '/guides/deployment/shared-responsibility-model', + }, + { name: 'Row Level Security', url: '/guides/database/postgres/row-level-security' }, + { name: 'Hardening the Data API', url: '/guides/database/hardening-data-api' }, + ], + }, ], } diff --git a/apps/docs/components/ProjectConfigVariables/ProjectConfigVariables.ComboBox.tsx b/apps/docs/components/ProjectConfigVariables/ProjectConfigVariables.ComboBox.tsx index 7b210ea3032..53d62179b15 100644 --- a/apps/docs/components/ProjectConfigVariables/ProjectConfigVariables.ComboBox.tsx +++ b/apps/docs/components/ProjectConfigVariables/ProjectConfigVariables.ComboBox.tsx @@ -1,18 +1,18 @@ +import { noop } from 'lodash-es' import { Check, ChevronsUpDown } from 'lucide-react' -import { noop } from 'lodash' import { useState } from 'react' import { Button_Shadcn_ as Button, - Popover_Shadcn_ as Popover, - PopoverTrigger_Shadcn_ as PopoverTrigger, - PopoverContent_Shadcn_ as PopoverContent, - Command_Shadcn_ as Command, - CommandInput_Shadcn_ as CommandInput, - CommandEmpty_Shadcn_ as CommandEmpty, - CommandItem_Shadcn_ as CommandItem, - CommandGroup_Shadcn_ as CommandGroup, - CommandList_Shadcn_ as CommandList, cn, + Command_Shadcn_ as Command, + CommandEmpty_Shadcn_ as CommandEmpty, + CommandGroup_Shadcn_ as CommandGroup, + CommandInput_Shadcn_ as CommandInput, + CommandItem_Shadcn_ as CommandItem, + CommandList_Shadcn_ as CommandList, + Popover_Shadcn_ as Popover, + PopoverContent_Shadcn_ as PopoverContent, + PopoverTrigger_Shadcn_ as PopoverTrigger, ScrollArea, } from 'ui' diff --git a/apps/docs/components/SharedData.tsx b/apps/docs/components/SharedData.tsx index f5b4cbc9111..113a8340195 100644 --- a/apps/docs/components/SharedData.tsx +++ b/apps/docs/components/SharedData.tsx @@ -1,4 +1,4 @@ -import { at } from 'lodash' +import { at } from 'lodash-es' import { ReactNode } from 'react' import { config, logConstants } from 'shared-data' diff --git a/apps/docs/content/guides/api/rest/generating-types.mdx b/apps/docs/content/guides/api/rest/generating-types.mdx index ee46e45db4c..7c5b6d288e9 100644 --- a/apps/docs/content/guides/api/rest/generating-types.mdx +++ b/apps/docs/content/guides/api/rest/generating-types.mdx @@ -141,6 +141,84 @@ To use `MergeDeep`, set `compilerOptions.strictNullChecks` to `true` in your `ts +## Enhanced type inference for JSON fields + +Starting from [supabase-js v2.48.0](https://github.com/supabase/supabase-js/releases/tag/v2.48.0), you can define custom types for JSON fields and get enhanced type inference when using JSON selectors with the `->` and `->>` operators. This makes your code more type-safe and intuitive when working with JSON/JSONB columns. + +### Defining custom JSON types + +You can extend your generated database types to include custom JSON schemas using `MergeDeep`: + +```ts ./database.types.ts +import { MergeDeep } from 'type-fest' +import { Database as DatabaseGenerated } from './database-generated.types' + +// Define your custom JSON type +type CustomJsonType = { + foo: string + bar: { baz: number } + en: 'ONE' | 'TWO' | 'THREE' +} + +export type Database = MergeDeep< + DatabaseGenerated, + { + public: { + Tables: { + your_table: { + Row: { + data: CustomJsonType | null + } + // Optional: Use if you want type-checking for inserts and updates + // Insert: { + // data?: CustomJsonType | null; + // }; + // Update: { + // data?: CustomJsonType | null; + // }; + } + } + Views: { + your_view: { + Row: { + data: CustomJsonType | null + } + } + } + } + } +> +``` + +### Type-safe JSON querying + +Once you've defined your custom JSON types, TypeScript will automatically infer the correct types when using JSON selectors: + +```ts +const res = await client.from('your_table').select('data->bar->baz, data->en, data->bar') + +if (res.data) { + console.log(res.data) + // TypeScript infers the shape of your JSON data: + // [ + // { + // baz: number; + // en: 'ONE' | 'TWO' | 'THREE'; + // bar: { baz: number }; + // } + // ] +} +``` + +This feature works with: + +- Single-level JSON access: `data->foo` +- Nested JSON access: `data->bar->baz` +- Text extraction: `data->>foo` (returns string) +- Mixed selections combining multiple JSON paths + +The type inference automatically handles the difference between `->` (returns JSON) and `->>` (returns text) operators, ensuring your TypeScript types match the actual runtime behavior. + You can also override the type of an individual successful response if needed: ```ts diff --git a/apps/docs/content/guides/auth/auth-hooks/custom-access-token-hook.mdx b/apps/docs/content/guides/auth/auth-hooks/custom-access-token-hook.mdx index d26c8eee48d..4c0af8c9c46 100644 --- a/apps/docs/content/guides/auth/auth-hooks/custom-access-token-hook.mdx +++ b/apps/docs/content/guides/auth/auth-hooks/custom-access-token-hook.mdx @@ -10,8 +10,9 @@ Claims returned must conform to our specification. Supabase Auth will check for These are the fields currently available on an access token: -Required Claims: `iss`, `aud`, `exp`, `iat`, `sub`, `role`, `aal`, `session_id` -Optional Claims: `jti`, `nbf`, `app_metadata`, `user_metadata`, `amr`, `email`, `phone` +Required Claims: `iss`, `aud`, `exp`, `iat`, `sub`, `role`, `aal`, `session_id`, `email`, `phone`, `is_anonymous` + +Optional Claims: `jti`, `nbf`, `app_metadata`, `user_metadata`, `amr`, **Inputs** @@ -241,7 +242,10 @@ as $$ 'sub', 'role', 'aal', - 'session_id' + 'session_id', + 'email', + 'phone', + 'is_anonymous' ] loop if original_claims ? claim then -- original_claims contains one of the listed claims, set it on new_claims diff --git a/apps/docs/content/guides/auth/managing-user-data.mdx b/apps/docs/content/guides/auth/managing-user-data.mdx index 47022bf98f9..150c6069ab2 100644 --- a/apps/docs/content/guides/auth/managing-user-data.mdx +++ b/apps/docs/content/guides/auth/managing-user-data.mdx @@ -182,7 +182,7 @@ val metadata = user?.userMetadata ## Deleting users -You may delete users directly or via the management console at Authentication > Users. Note that deleting a user from the `auth.users` table does not automatically sign out a user. As Supabase makes use of JSON Web Tokens (JWT), a user's JWT will remain "valid" until it has expired. Should you wish to immediately revoke access for a user, do consider making use of a Row Level Security policy as described below. +You may delete users directly or via the management console at Authentication > Users. Note that deleting a user from the `auth.users` table does not automatically sign out a user. As Supabase makes use of JSON Web Tokens (JWT), a user's JWT will remain "valid" until it has expired. diff --git a/apps/docs/content/guides/database/hardening-data-api.mdx b/apps/docs/content/guides/database/hardening-data-api.mdx index 64258cf784a..bfe1f421b7b 100644 --- a/apps/docs/content/guides/database/hardening-data-api.mdx +++ b/apps/docs/content/guides/database/hardening-data-api.mdx @@ -2,7 +2,24 @@ title: 'Hardening the Data API' --- -Your database's automatically generated Data API exposes the `public` schema by default. If your `public` schema is used by other tools as a default space, you might want to lock down this schema. This helps prevent accidental exposure of data that's automatically added to `public`. +Your database's auto-generated Data API exposes the `public` schema by default. You can change this to any schema in your database, or even disable the Data API completely. + +Any tables that are accessible through the Data API _must_ have [Row Level Security](/docs/guides/database/postgres/row-level-security) enabled. Row Level Security (RLS) is enabled by default when you create tables from the Supabase Dashboard. If you create a table using the SQL editor or your own SQL client or migration runner, you*must* enable RLS yourself. + +## Shared responsibility + +Your application's security is your responsibility as a developer. This includes RLS, falling under the [Shared Responsibility](/docs/guides/deployment/shared-responsibility-model) model. To help you: + +- Supabase sends daily emails warning of any tables that are exposed to the Data API which do not have RLS enabled. +- Supabase provides a Security Advisor and other tools in the Supabase Dashboard to fix any issues. + +## Private schemas + +We highly recommend creating a `private` schema for storing tables that you do not want to expose via the Data API. These tables can be accessed via Supabase Edge Functions or any other serverside tool. In this model, you should implement your security model in your serverside code. Although it's not required, we _still_ recommend enabling RLS for private tables and then connecting to your database using a Postgres role with `bypassrls` privileges. + +## Managing the public schema + +If your `public` schema is used by other tools as a default space, you might want to lock down this schema. This helps prevent accidental exposure of data that's automatically added to `public`. There are two levels of security hardening for the Data API: diff --git a/apps/docs/content/guides/database/replication/setting-up-replication.mdx b/apps/docs/content/guides/database/replication/setting-up-replication.mdx index 170b54b64d4..36d4d528705 100644 --- a/apps/docs/content/guides/database/replication/setting-up-replication.mdx +++ b/apps/docs/content/guides/database/replication/setting-up-replication.mdx @@ -10,9 +10,9 @@ sidebar_label: 'Setting up replication and CDC' To set up replication, the following is recommended: - Instance size of XL or greater -- [IPv4 add-on](/guides/platform/ipv4-address) enabled +- [IPv4 add-on](/docs/guides/platform/ipv4-address) enabled -To create a replication slot, you will need to use the `postgres` user and follow the instructions in our [guide](/guides/database/postgres/setup-replication-external). +To create a replication slot, you will need to use the `postgres` user and follow the instructions in our [guide](/docs/guides/database/postgres/setup-replication-external). If you are running Postgres 17 or higher, you can create a new user and grant them replication diff --git a/apps/docs/content/guides/functions/auth.mdx b/apps/docs/content/guides/functions/auth.mdx index 7b97b497278..17ef8d77112 100644 --- a/apps/docs/content/guides/functions/auth.mdx +++ b/apps/docs/content/guides/functions/auth.mdx @@ -19,6 +19,11 @@ Deno.serve(async (req: Request) => { const supabaseClient = createClient( Deno.env.get('SUPABASE_URL') ?? '', Deno.env.get('SUPABASE_ANON_KEY') ?? '', + { + global: { + headers: { Authorization: req.headers.get('Authorization') }, + }, + } ); // Get the session or user object @@ -43,6 +48,11 @@ Deno.serve(async (req: Request) => { const supabaseClient = createClient( Deno.env.get('SUPABASE_URL') ?? '', Deno.env.get('SUPABASE_ANON_KEY') ?? '', + { + global: { + headers: { Authorization: req.headers.get('Authorization') }, + }, + } ) // Get the session or user object @@ -71,6 +81,11 @@ Deno.serve(async (req: Request) => { const supabaseClient = createClient( Deno.env.get('SUPABASE_URL') ?? '', Deno.env.get('SUPABASE_ANON_KEY') ?? '', + { + global: { + headers: { Authorization: req.headers.get('Authorization') }, + }, + } ); // Get the session or user object diff --git a/apps/docs/content/guides/functions/examples/auth-send-email-hook-react-email-resend.mdx b/apps/docs/content/guides/functions/examples/auth-send-email-hook-react-email-resend.mdx index 4ca2f65f668..aa095e5c618 100644 --- a/apps/docs/content/guides/functions/examples/auth-send-email-hook-react-email-resend.mdx +++ b/apps/docs/content/guides/functions/examples/auth-send-email-hook-react-email-resend.mdx @@ -294,7 +294,7 @@ Set the secrets from the `.env` file: supabase secrets set --env-file supabase/functions/.env ``` -Now your Supabase Edge Function will be triggered anytime an Auth Email needs to be send to the user! +Now your Supabase Edge Function will be triggered anytime an Auth Email needs to be sent to the user! ## More resources diff --git a/apps/docs/content/guides/platform/manage-your-usage/egress.mdx b/apps/docs/content/guides/platform/manage-your-usage/egress.mdx index 74d6bdff874..9f979e821ee 100644 --- a/apps/docs/content/guides/platform/manage-your-usage/egress.mdx +++ b/apps/docs/content/guides/platform/manage-your-usage/egress.mdx @@ -15,7 +15,7 @@ Data sent to the client when retrieving data stored in your database. -There are various ways to interact with your database, such as through the PostgREST API using one of the client SDKs or via the Supavisor connection pooler. On the Supabase Dashboard, Egress from the PostgREST API is labeled as **Database Egress**, while Egress through Supavisor is labeled as **Supavisor Egress**. +There are various ways to interact with your database, such as through the PostgREST API using one of the client SDKs or via the Supavisor connection pooler. On the Supabase Dashboard, Egress from the PostgREST API is labeled as **Database Egress**, while Egress through Supavisor is labeled as **Shared Pooler Egress**. @@ -43,6 +43,12 @@ Data pushed to clients via Supabase Realtime for subscribed events. **Example:** When a user views a product page in an online shop, their client subscribes to real-time inventory updates. As stock levels change, Supabase Realtime pushes updates to all subscribed clients, contributing to Realtime Egress. +### Shared pooler Egress + +Data sent to the client when using the shared connection pooler (Supavisor) to access your database. WHen using the shared connection pooler, we do not count database egress, as this would otherwise count double (Database -> Shared Pooler + Shared Pooler -> Client). + +**Example:** You are using our [shared connection pooler](https://supabase.com/docs/guides/database/connecting-to-postgres#shared-pooler) and you query a list of invoices in your backend. The data returned from that query is contributing to Shared Pooler Egress. + ### Log Drain Egress Data pushed to the connected log drain. diff --git a/apps/docs/content/guides/realtime/broadcast.mdx b/apps/docs/content/guides/realtime/broadcast.mdx index abc206aa9ce..14fc142a370 100644 --- a/apps/docs/content/guides/realtime/broadcast.mdx +++ b/apps/docs/content/guides/realtime/broadcast.mdx @@ -76,11 +76,15 @@ Go to your Supabase project's [API Settings](https://supabase.com/dashboard/proj ```python - from supabase import create_client + import asyncio + from supabase import acreate_client URL = "https://.supabase.co" KEY = "" - supabase = create_client(URL, KEY) + + async def create_supabase(): + supabase = await acreate_client(URL, KEY) + return supabase ``` @@ -179,6 +183,11 @@ You can provide a callback for the `broadcast` channel to receive message. This + + + In the following Realtime examples, certain methods are awaited. These should be enclosed within an `async` function. + + {/* prettier-ignore */} ```python @@ -190,7 +199,7 @@ You can provide a callback for the `broadcast` channel to receive message. This print(f"Broadcast received: {payload}") # Subscribe to the Channel - my_channel + await my_channel .on_broadcast('shout', message_received) # Listen for "shout". Can be "*" to listen to all events .subscribe() ``` @@ -323,6 +332,12 @@ You can use the Supabase client libraries to send Broadcast messages. + + + When an asynchronous method needs to be used within a synchronous context, such as the callback for `.subscribe()`, utilize `asyncio.create_task()` to schedule the coroutine. This is why the [initialize the client](#initialize-the-client) example includes an import of `asyncio`. + + + {/* prettier-ignore */} ```python my_channel = supabase.channel('test-channel') @@ -332,12 +347,12 @@ You can use the Supabase client libraries to send Broadcast messages. if status != RealtimeSubscribeStates.SUBSCRIBED: return - my_channel.send_broadcast( + asyncio.create_task(my_channel.send_broadcast( 'shout', { "message": 'hello, world' }, - ) + )) - my_channel.subscribe(on_subscribe) + await my_channel.subscribe(on_subscribe) ``` @@ -543,6 +558,12 @@ You can pass configuration options while initializing the Supabase Client. + + + When an asynchronous method needs to be used within a synchronous context, such as the callback for `.subscribe()`, utilize `asyncio.create_task()` to schedule the coroutine. This is why the [initialize the client](#initialize-the-client) example includes an import of `asyncio`. + + + By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `self` parameter to `True`. ```python @@ -559,10 +580,10 @@ You can pass configuration options while initializing the Supabase Client. return # Send a message once the client is subscribed - channel_b.send_broadcast( + asyncio.create_task(channel_b.send_broadcast( 'test-my-messages', { "message": 'talking to myself' }, - ) + )) my_channel.subscribe(on_subscribe) ``` diff --git a/apps/docs/content/guides/self-hosting/docker.mdx b/apps/docs/content/guides/self-hosting/docker.mdx index 088542a471c..530477d47df 100644 --- a/apps/docs/content/guides/self-hosting/docker.mdx +++ b/apps/docs/content/guides/self-hosting/docker.mdx @@ -354,7 +354,7 @@ services: ```bash name=.env ## Never check your secrets into version control -`${JWT_SECRET}` +JWT_SECRET=${JWT_SECRET} ``` diff --git a/apps/docs/features/docs/GuidesMdx.template.tsx b/apps/docs/features/docs/GuidesMdx.template.tsx index 1d748c23778..4472742fc2f 100644 --- a/apps/docs/features/docs/GuidesMdx.template.tsx +++ b/apps/docs/features/docs/GuidesMdx.template.tsx @@ -1,5 +1,4 @@ import { ExternalLink } from 'lucide-react' -import { type SerializeOptions } from 'next-mdx-remote/dist/types' import { type ReactNode } from 'react' import ReactMarkdown from 'react-markdown' @@ -11,6 +10,7 @@ import { TocAnchorsProvider } from '~/features/docs/GuidesMdx.client' import { MDXRemoteBase } from '~/features/docs/MdxBase' import type { WithRequired } from '~/features/helpers.types' import { type GuideFrontmatter } from '~/lib/docs' +import { SerializeOptions } from '~/types/next-mdx-remote-serialize' const EDIT_LINK_SYMBOL = Symbol('edit link') interface EditLink { diff --git a/apps/docs/features/docs/MdxBase.tsx b/apps/docs/features/docs/MdxBase.tsx index 2e306ad9a3a..8e77bf230e4 100644 --- a/apps/docs/features/docs/MdxBase.tsx +++ b/apps/docs/features/docs/MdxBase.tsx @@ -1,12 +1,12 @@ -import { type SerializeOptions } from 'next-mdx-remote/dist/types' import { MDXRemote } from 'next-mdx-remote/rsc' import { type ComponentProps } from 'react' -import remarkGfm from 'remark-gfm' import rehypeKatex from 'rehype-katex' +import remarkGfm from 'remark-gfm' import remarkMath from 'remark-math' import { preprocessMdxWithDefaults } from '~/features/directives/utils' import { components } from '~/features/docs/MdxBase.shared' +import { SerializeOptions } from '~/types/next-mdx-remote-serialize' const mdxOptions: SerializeOptions = { mdxOptions: { diff --git a/apps/docs/features/docs/Reference.generated.script.ts b/apps/docs/features/docs/Reference.generated.script.ts index 42c5f1b6a5c..b4cefeec563 100644 --- a/apps/docs/features/docs/Reference.generated.script.ts +++ b/apps/docs/features/docs/Reference.generated.script.ts @@ -1,4 +1,4 @@ -import { isPlainObject, keyBy } from 'lodash' +import { isPlainObject, keyBy } from 'lodash-es' import { mkdir, readFile, writeFile } from 'node:fs/promises' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' diff --git a/apps/docs/features/docs/Reference.navigation.client.tsx b/apps/docs/features/docs/Reference.navigation.client.tsx index 4ef50c32ff9..ca574e3e4c0 100644 --- a/apps/docs/features/docs/Reference.navigation.client.tsx +++ b/apps/docs/features/docs/Reference.navigation.client.tsx @@ -2,7 +2,7 @@ import * as Collapsible from '@radix-ui/react-collapsible' -import { debounce } from 'lodash' +import { debounce } from 'lodash-es' import { ChevronUp } from 'lucide-react' import Link from 'next/link' import { usePathname } from 'next/navigation' diff --git a/apps/docs/features/docs/Reference.ui.tsx b/apps/docs/features/docs/Reference.ui.tsx index 4cc062daf17..a1047dbf4df 100644 --- a/apps/docs/features/docs/Reference.ui.tsx +++ b/apps/docs/features/docs/Reference.ui.tsx @@ -1,4 +1,4 @@ -import { isEqual } from 'lodash' +import { isEqual } from 'lodash-es' import { ChevronRight, XCircle } from 'lucide-react' import type { HTMLAttributes, PropsWithChildren } from 'react' import ReactMarkdown from 'react-markdown' diff --git a/apps/docs/features/docs/Troubleshooting.script.mjs b/apps/docs/features/docs/Troubleshooting.script.mjs index b4f03549c3c..e3a710b8747 100644 --- a/apps/docs/features/docs/Troubleshooting.script.mjs +++ b/apps/docs/features/docs/Troubleshooting.script.mjs @@ -7,6 +7,8 @@ * Discussions. */ +import _configureDotEnv from '../../scripts/utils/dotenv' + import { createAppAuth } from '@octokit/auth-app' import { Octokit } from '@octokit/core' import { createClient } from '@supabase/supabase-js' @@ -20,13 +22,12 @@ import { gfm } from 'micromark-extension-gfm' import { mdxjs } from 'micromark-extension-mdxjs' import { readFile, writeFile } from 'node:fs/promises' import { parse, stringify } from 'smol-toml' - import { getAllTroubleshootingEntriesInternal as getAllTroubleshootingEntries, getArticleSlug, } from './Troubleshooting.utils.common.mjs' -import 'dotenv/config' +const _ = _configureDotEnv const REPOSITORY_ID = 'MDEwOlJlcG9zaXRvcnkyMTQ1ODcxOTM=' const TROUBLESHOOTING_CATEGORY_ID = 'DIC_kwDODMpXOc4CUvEr' @@ -52,9 +53,9 @@ function octokit() { octokitInstance = new Octokit({ authStrategy: createAppAuth, auth: { - appId: process.env.SEARCH_GITHUB_APP_ID, - installationId: process.env.SEARCH_GITHUB_APP_INSTALLATION_ID, - privateKey: process.env.SEARCH_GITHUB_APP_PRIVATE_KEY, + appId: process.env.DOCS_GITHUB_APP_ID, + installationId: process.env.DOCS_GITHUB_APP_INSTALLATION_ID, + privateKey: process.env.DOCS_GITHUB_APP_PRIVATE_KEY, }, }) } diff --git a/apps/docs/generator/helpers.ts b/apps/docs/generator/helpers.ts index 30b8037d818..b0ab9705c03 100644 --- a/apps/docs/generator/helpers.ts +++ b/apps/docs/generator/helpers.ts @@ -1,5 +1,5 @@ import * as fs from 'fs' -import { mapValues, values } from 'lodash' +import { mapValues, values } from 'lodash-es' export const slugify = (text: string) => { if (!text) return '' diff --git a/apps/docs/generator/legacy.ts b/apps/docs/generator/legacy.ts index cf066ef1d0a..bc248b769a3 100644 --- a/apps/docs/generator/legacy.ts +++ b/apps/docs/generator/legacy.ts @@ -8,15 +8,15 @@ * tsx ReferenceGenerator.ts -o docs/client spec/supabase.yml */ +import * as fs from 'fs' +import * as yaml from 'js-yaml' +import { uniqBy } from 'lodash-es' +import { flattenSections } from '../lib/helpers' import Example from './legacy/components/Example' import Page from './legacy/components/Page' import Tab from './legacy/components/Tab' +import { OpenRef, TsDoc } from './legacy/definitions' import { slugify, tsDocCommentToMdComment, writeToDisk } from './legacy/lib/helpers' -import { TsDoc, OpenRef } from './legacy/definitions' -import { uniqBy } from 'lodash' -import * as fs from 'fs' -import * as yaml from 'js-yaml' -import { flattenSections } from '../lib/helpers' const commonDocSpecJson = JSON.parse( fs.readFileSync('spec/common-client-libs-sections.json', 'utf8') diff --git a/apps/docs/lib/docs.ts b/apps/docs/lib/docs.ts index 7e468a3d0e1..096dd822fd7 100644 --- a/apps/docs/lib/docs.ts +++ b/apps/docs/lib/docs.ts @@ -1,14 +1,13 @@ import matter from 'gray-matter' import { serialize } from 'next-mdx-remote/serialize' -import type { SerializeOptions } from 'next-mdx-remote/dist/types' import { existsSync } from 'node:fs' import { readdir, readFile } from 'node:fs/promises' import { basename, extname, join, sep } from 'node:path' -import remarkGfm from 'remark-gfm' import rehypeKatex from 'rehype-katex' +import remarkGfm from 'remark-gfm' import remarkMath from 'remark-math' -import codeHikeTheme from 'config/code-hike.theme.json' with { type: 'json' } +import { SerializeOptions } from '~/types/next-mdx-remote-serialize' // MUST be process.cwd() here, not import.meta.url, or files that are added // with outputFileTracingIncludes (not auto-traced) will not be found at diff --git a/apps/docs/lib/refGenerator/helpers.ts b/apps/docs/lib/refGenerator/helpers.ts index f377d56155c..bd0de078ead 100644 --- a/apps/docs/lib/refGenerator/helpers.ts +++ b/apps/docs/lib/refGenerator/helpers.ts @@ -1,9 +1,9 @@ import type { TsDoc } from '../../generator/legacy/definitions' -import { values, mapValues } from 'lodash' +import { mapValues, values } from 'lodash-es' import type { OpenAPIV3 } from 'openapi-types' -import { flattenSections } from '../helpers' import type { ICommonItem } from '~/components/reference/Reference.types' +import { flattenSections } from '../helpers' export function extractTsDocNode(nodeToFind: string, definition: any) { const nodePath = nodeToFind.split('.') diff --git a/apps/docs/lib/remotePatterns.js b/apps/docs/lib/remotePatterns.js index 2b06dc1c058..e30aa6d1294 100644 --- a/apps/docs/lib/remotePatterns.js +++ b/apps/docs/lib/remotePatterns.js @@ -1,4 +1,4 @@ -module.exports = [ +export default [ { protocol: 'https', hostname: 'avatars.githubusercontent.com', diff --git a/apps/docs/lib/supabase.ts b/apps/docs/lib/supabase.ts index 3d3ab8820f8..7c46ef9c993 100644 --- a/apps/docs/lib/supabase.ts +++ b/apps/docs/lib/supabase.ts @@ -2,6 +2,7 @@ import { createClient, type SupabaseClient } from '@supabase/supabase-js' import { type Database as DatabaseGenerated } from 'common' type Database = { + content: DatabaseGenerated['content'] graphql_public: DatabaseGenerated['graphql_public'] public: { Tables: DatabaseGenerated['public']['Tables'] diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs index 400ac7c4b3f..cbea04fe398 100644 --- a/apps/docs/next.config.mjs +++ b/apps/docs/next.config.mjs @@ -34,13 +34,6 @@ const nextConfig = { // @ts-ignore remotePatterns, }, - // TODO: @next/mdx ^13.0.2 only supports experimental mdxRs flag. next ^13.0.2 will stop warning about this being unsupported. - // mdxRs: true, - modularizeImports: { - lodash: { - transform: 'lodash/{{member}}', - }, - }, webpack: (config) => { config.module.rules.push({ test: /\.include$/, diff --git a/apps/docs/package.json b/apps/docs/package.json index b98508a1c73..be764959be4 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -2,37 +2,38 @@ "name": "docs", "version": "0.0.0", "private": true, + "type": "module", "scripts": { - "preinstall": "npx only-allow pnpm", - "predev": "pnpm run codegen:graphql && pnpm run codegen:references && pnpm run codegen:examples", - "dev": "concurrently --kill-others \"next dev --port 3001\" \"pnpm run dev:watch:troubleshooting\"", - "dev:watch:troubleshooting": "node ./scripts/troubleshooting/watch.mjs", - "dev:secrets:pull": "AWS_PROFILE=supabase-dev node ../../scripts/getSecrets.js -n local/docs", - "prebuild": "pnpm run codegen:graphql && pnpm run codegen:references && pnpm run codegen:examples", "build": "next build", "build:analyze": "ANALYZE=true next build", - "build:sitemap": "tsx ./internals/generate-sitemap.ts", "build:llms": "tsx ./scripts/llms.ts", - "postbuild": "pnpm run build:sitemap && pnpm run build:llms && ./../../scripts/upload-static-assets.sh", - "start": "next start", + "build:sitemap": "tsx ./internals/generate-sitemap.ts", + "clean": "rimraf .next .turbo node_modules features/docs/generated examples __generated__", + "codegen:examples": "cp -r ../../examples ./examples", + "codegen:graphql": "tsx --conditions=react-server ./scripts/graphqlSchema.ts && graphql-codegen --config codegen.ts", + "codegen:references": "tsx features/docs/Reference.generated.script.ts", + "codemod:frontmatter": "node ./scripts/codemod/mdx-meta.mjs && prettier --write \"content/**/*.mdx\"", + "dev": "concurrently --kill-others \"next dev --port 3001\" \"pnpm run dev:watch:troubleshooting\"", + "dev:secrets:pull": "AWS_PROFILE=supabase-dev node ../../scripts/getSecrets.js -n local/docs", + "dev:watch:troubleshooting": "node ./scripts/troubleshooting/watch.mjs", + "embeddings": "tsx scripts/search/generate-embeddings.ts", + "embeddings:refresh": "pnpm run embeddings --refresh", + "last-changed": "tsx scripts/last-changed.ts", + "last-changed:reset": "pnpm run last-changed -- --reset", "lint": "next lint", "lint:mdx": "supa-mdx-lint content --config ../../supa-mdx-lint.config.toml", - "pretypecheck": "pnpm run codegen:graphql", - "typecheck": "tsc --noEmit", + "postbuild": "pnpm run build:sitemap && pnpm run build:llms && ./../../scripts/upload-static-assets.sh", + "prebuild": "pnpm run codegen:graphql && pnpm run codegen:references && pnpm run codegen:examples", + "predev": "pnpm run codegen:graphql && pnpm run codegen:references && pnpm run codegen:examples", + "preinstall": "npx only-allow pnpm", "pretest": "pnpm run codegen:examples", + "pretypecheck": "pnpm run codegen:graphql", + "start": "next start", "test": "pnpm supabase start && pnpm run test:local && pnpm supabase stop", "test:local": "vitest --exclude \"**/*.smoke.test.ts\"", "test:smoke": "pnpm run codegen:references && vitest -t \"prod smoke test\"", - "embeddings": "tsx scripts/search/generate-embeddings.ts", - "embeddings:refresh": "pnpm run embeddings --refresh", "troubleshooting:sync": "node features/docs/Troubleshooting.script.mjs", - "last-changed": "tsx scripts/last-changed.ts", - "last-changed:reset": "pnpm run last-changed -- --reset", - "codegen:examples": "cp -r ../../examples ./examples", - "codemod:frontmatter": "node ./scripts/codemod/mdx-meta.mjs && prettier --write \"content/**/*.mdx\"", - "codegen:graphql": "tsx --conditions=react-server ./scripts/graphqlSchema.ts && graphql-codegen --config codegen.ts", - "codegen:references": "tsx features/docs/Reference.generated.script.ts", - "clean": "rimraf .next .turbo node_modules features/docs/generated examples __generated__" + "typecheck": "tsc --noEmit" }, "dependencies": { "@har-sdk/openapi-sampler": "^2.2.0", @@ -70,7 +71,7 @@ "jsrsasign": "^11.0.0", "katex": "^0.16.21", "libpg-query": "15.2.0", - "lodash": "^4.17.21", + "lodash-es": "^4.17.21", "lucide-react": "*", "mdast": "^3.0.0", "mdast-util-from-markdown": "^1.2.0", @@ -125,6 +126,7 @@ "@types/graphql-validation-complexity": "^0.4.4", "@types/hast": "^2.3.4", "@types/jsrsasign": "^10.5.12", + "@types/lodash-es": "^4.17.12", "@types/mdast": "^3.0.0", "@types/node": "catalog:", "@types/react": "catalog:", diff --git a/apps/docs/postcss.config.js b/apps/docs/postcss.config.cjs similarity index 100% rename from apps/docs/postcss.config.js rename to apps/docs/postcss.config.cjs diff --git a/apps/docs/resources/error/errorModel.ts b/apps/docs/resources/error/errorModel.ts new file mode 100644 index 00000000000..ce9cb1d7c60 --- /dev/null +++ b/apps/docs/resources/error/errorModel.ts @@ -0,0 +1,74 @@ +import { ApiErrorGeneric, convertPostgrestToApiError, NoDataError } from '~/app/api/utils' +import { Result } from '~/features/helpers.fn' +import { supabase } from '~/lib/supabase' + +export const SERVICES = { + AUTH: { + value: 'AUTH', + }, + REALTIME: { + value: 'REALTIME', + }, + STORAGE: { + value: 'STORAGE', + }, +} as const + +type Service = keyof typeof SERVICES + +export class ErrorModel { + public code: string + public service: Service + public httpStatusCode?: number + public message?: string + + constructor({ + code, + service, + httpStatusCode: httpStatusCode, + message, + }: { + code: string + service: Service + httpStatusCode?: number + message?: string + }) { + this.code = code + this.service = service + this.httpStatusCode = httpStatusCode + this.message = message + } + + static async loadSingleError({ + code, + service, + }: { + code: string + service: Service + }): Promise> { + return new Result( + await supabase() + .schema('content') + .from('error') + .select('code, ...service(service:name), httpStatusCode:http_status_code, message') + .eq('code', code) + .eq('service.name', service) + .is('deleted_at', null) + .single<{ + code: string + service: Service + httpStatusCode?: number + message?: string + }>() + ) + .map((data) => { + return new ErrorModel(data) + }) + .mapError((error) => { + if (error.code === 'PGRST116') { + return new NoDataError('Error for given code and service does not exist', error) + } + return convertPostgrestToApiError(error) + }) + } +} diff --git a/apps/docs/resources/error/errorResolver.ts b/apps/docs/resources/error/errorResolver.ts new file mode 100644 index 00000000000..ef79e2d887b --- /dev/null +++ b/apps/docs/resources/error/errorResolver.ts @@ -0,0 +1,43 @@ +import { GraphQLError, GraphQLNonNull, GraphQLResolveInfo, GraphQLString } from 'graphql' +import { type RootQueryTypeErrorArgs } from '~/__generated__/graphql' +import { convertUnknownToApiError } from '~/app/api/utils' +import { Result } from '~/features/helpers.fn' +import { ErrorModel } from './errorModel' +import { + GRAPHQL_FIELD_ERROR_GLOBAL, + GraphQLEnumTypeService, + GraphQLObjectTypeError, +} from './errorSchema' + +async function resolveSingleError( + _parent: unknown, + args: RootQueryTypeErrorArgs, + _context: unknown, + _info: GraphQLResolveInfo +): Promise { + return ( + await Result.tryCatchFlat(ErrorModel.loadSingleError, convertUnknownToApiError, args) + ).match( + (data) => data, + (error) => { + console.error(`Error resolving ${GRAPHQL_FIELD_ERROR_GLOBAL}:`, error) + return new GraphQLError(error.isPrivate() ? 'Internal Server Error' : error.message) + } + ) +} + +export const errorRoot = { + [GRAPHQL_FIELD_ERROR_GLOBAL]: { + description: 'Get the details of an error code returned from a Supabase service', + args: { + code: { + type: new GraphQLNonNull(GraphQLString), + }, + service: { + type: new GraphQLNonNull(GraphQLEnumTypeService), + }, + }, + type: GraphQLObjectTypeError, + resolve: resolveSingleError, + }, +} diff --git a/apps/docs/resources/error/errorSchema.ts b/apps/docs/resources/error/errorSchema.ts new file mode 100644 index 00000000000..c752d705336 --- /dev/null +++ b/apps/docs/resources/error/errorSchema.ts @@ -0,0 +1,40 @@ +import { + GraphQLEnumType, + GraphQLInt, + GraphQLNonNull, + GraphQLObjectType, + GraphQLString, +} from 'graphql' +import { SERVICES } from './errorModel' + +export const GRAPHQL_FIELD_ERROR_GLOBAL = 'error' as const + +export const GraphQLEnumTypeService = new GraphQLEnumType({ + name: 'Service', + values: SERVICES, +}) + +export const GraphQLObjectTypeError = new GraphQLObjectType({ + name: 'Error', + description: 'An error returned by a Supabase service', + fields: { + code: { + type: new GraphQLNonNull(GraphQLString), + description: + 'The unique code identifying the error. The code is stable, and can be used for string matching during error handling.', + }, + service: { + type: new GraphQLNonNull(GraphQLEnumTypeService), + description: 'The Supabase service that returns this error.', + }, + httpStatusCode: { + type: GraphQLInt, + description: 'The HTTP status code returned with this error.', + }, + message: { + type: GraphQLString, + description: + 'A human-readable message describing the error. The message is not stable, and should not be used for string matching during error handling. Use the code instead.', + }, + }, +}) diff --git a/apps/docs/resources/rootSchema.ts b/apps/docs/resources/rootSchema.ts index 5c543d1e79f..02ea9cc9697 100644 --- a/apps/docs/resources/rootSchema.ts +++ b/apps/docs/resources/rootSchema.ts @@ -6,6 +6,7 @@ import { printSchema, } from 'graphql' import { RootQueryTypeResolvers } from '~/__generated__/graphql' +import { errorRoot } from './error/errorResolver' import { searchRoot } from './globalSearch/globalSearchResolver' import { GraphQLObjectTypeGuide } from './guide/guideSchema' import { GraphQLObjectTypeReferenceCLICommand } from './reference/referenceCLISchema' @@ -35,6 +36,7 @@ export const rootGraphQLSchema = new GraphQLSchema({ fields: { ...introspectRoot, ...searchRoot, + ...errorRoot, }, }), types: [ diff --git a/apps/docs/scripts/graphqlSchema.ts b/apps/docs/scripts/graphqlSchema.ts index 17b03215ed7..2f00e2fa79f 100644 --- a/apps/docs/scripts/graphqlSchema.ts +++ b/apps/docs/scripts/graphqlSchema.ts @@ -1,7 +1,10 @@ import { printSchema } from 'graphql' import fs from 'node:fs' import path from 'node:path' -import { rootGraphQLSchema } from '../resources/rootSchema' +import { fileURLToPath } from 'node:url' +import { rootGraphQLSchema } from '../resources/rootSchema.js' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) async function generateGraphQLSchema() { try { @@ -22,6 +25,6 @@ async function generateGraphQLSchema() { } } -if (require.main === module) { +if (process.argv[1] === fileURLToPath(import.meta.url)) { generateGraphQLSchema() } diff --git a/apps/docs/scripts/last-changed.ts b/apps/docs/scripts/last-changed.ts index 456f4d56804..d4892653beb 100644 --- a/apps/docs/scripts/last-changed.ts +++ b/apps/docs/scripts/last-changed.ts @@ -11,18 +11,21 @@ * the last Git commit date. */ -import 'dotenv/config' +import _configureDotEnv from './utils/dotenv' import { createClient, type SupabaseClient } from '@supabase/supabase-js' import matter from 'gray-matter' import { createHash } from 'node:crypto' import { readdirSync } from 'node:fs' import { readFile } from 'node:fs/promises' -import { join } from 'node:path' +import path, { join } from 'node:path' +import { fileURLToPath } from 'node:url' import { parseArgs } from 'node:util' import { SimpleGit, simpleGit } from 'simple-git' import { parse } from 'smol-toml' -import { type Section } from './helpers.mdx' +import { Section } from './helpers.mdx.js' + +const _ = _configureDotEnv interface Options { reset: boolean @@ -46,9 +49,11 @@ type SectionWithChecksum = Omit & const REQUIRED_ENV_VARS = { SUPABASE_URL: 'NEXT_PUBLIC_SUPABASE_URL', - SERVICE_ROLE_KEY: 'SUPABASE_SERVICE_ROLE_KEY', + SERVICE_ROLE_KEY: 'SUPABASE_SECRET_KEY', } as const +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + async function main() { console.log('Updating content timestamps....') diff --git a/apps/docs/scripts/llms.ts b/apps/docs/scripts/llms.ts index 47a1b16b48b..abea8501c76 100644 --- a/apps/docs/scripts/llms.ts +++ b/apps/docs/scripts/llms.ts @@ -1,6 +1,8 @@ +import _configureDotEnv from './utils/dotenv' + import 'dotenv/config' import fs from 'node:fs/promises' - +import { fileURLToPath } from 'node:url' import { fetchCliLibReferenceSource, fetchCSharpLibReferenceSource, @@ -11,7 +13,9 @@ import { fetchPythonLibReferenceSource, fetchSwiftLibReferenceSource, type SearchSource, -} from './search/sources' +} from './search/sources/index.js' + +const _ = _configureDotEnv interface Source { title: string @@ -97,6 +101,6 @@ async function generateLlmsTxt() { } } -if (require.main === module) { +if (process.argv[1] === fileURLToPath(import.meta.url)) { generateLlmsTxt() } diff --git a/apps/docs/scripts/search/generate-embeddings.ts b/apps/docs/scripts/search/generate-embeddings.ts index dfb83112fac..66d7bb8f6e0 100644 --- a/apps/docs/scripts/search/generate-embeddings.ts +++ b/apps/docs/scripts/search/generate-embeddings.ts @@ -1,10 +1,13 @@ +import _configureDotenv from '../utils/dotenv' + import { createClient } from '@supabase/supabase-js' -import 'dotenv/config' import { parseArgs } from 'node:util' import { OpenAI } from 'openai' import { v4 as uuidv4 } from 'uuid' -import type { Json, Section } from '../helpers.mdx' -import { fetchAllSources } from './sources' +import type { Json, Section } from '../helpers.mdx.js' +import { fetchAllSources } from './sources/index.js' + +const _ = _configureDotenv const args = parseArgs({ options: { @@ -18,19 +21,20 @@ async function generateEmbeddings() { const shouldRefresh = Boolean(args.values.refresh) const requiredEnvVars = [ + 'DOCS_GITHUB_APP_ID', + 'DOCS_GITHUB_APP_INSTALLATION_ID', + 'DOCS_GITHUB_APP_PRIVATE_KEY', + 'NEXT_PUBLIC_MISC_ANON_KEY', + 'NEXT_PUBLIC_MISC_URL', 'NEXT_PUBLIC_SUPABASE_URL', - 'SUPABASE_SERVICE_ROLE_KEY', 'OPENAI_API_KEY', - 'NEXT_PUBLIC_MISC_USE_URL', - 'NEXT_PUBLIC_MISC_USE_ANON_KEY', - 'SEARCH_GITHUB_APP_ID', - 'SEARCH_GITHUB_APP_INSTALLATION_ID', - 'SEARCH_GITHUB_APP_PRIVATE_KEY', + 'SUPABASE_SECRET_KEY', ] - if (requiredEnvVars.some((name) => !process.env[name])) { + const missingEnvVars = requiredEnvVars.filter((name) => !process.env[name]) + if (missingEnvVars.length > 0) { throw new Error( - `Environment variables ${requiredEnvVars.join( + `Environment variables ${missingEnvVars.join( ', ' )} are required: skipping embeddings generation` ) @@ -38,8 +42,7 @@ async function generateEmbeddings() { const supabaseClient = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL, - // eslint-disable-next-line turbo/no-undeclared-env-vars - process.env.SUPABASE_SERVICE_ROLE_KEY, + process.env.SUPABASE_SECRET_KEY, { auth: { persistSession: false, diff --git a/apps/docs/scripts/search/sources/base.ts b/apps/docs/scripts/search/sources/base.ts index 7416f8bdb67..32b5aad9e9c 100644 --- a/apps/docs/scripts/search/sources/base.ts +++ b/apps/docs/scripts/search/sources/base.ts @@ -1,4 +1,4 @@ -import type { Json, Section } from '../../helpers.mdx' +import type { Json, Section } from '../../helpers.mdx.js' export abstract class BaseLoader { type: string diff --git a/apps/docs/scripts/search/sources/github-discussion.ts b/apps/docs/scripts/search/sources/github-discussion.ts index 8eb158122e1..8c3b9a70adc 100644 --- a/apps/docs/scripts/search/sources/github-discussion.ts +++ b/apps/docs/scripts/search/sources/github-discussion.ts @@ -2,7 +2,7 @@ import { createAppAuth } from '@octokit/auth-app' import { Octokit } from '@octokit/core' import { paginateGraphql } from '@octokit/plugin-paginate-graphql' import crypto, { createHash } from 'node:crypto' -import { BaseLoader, BaseSource } from './base' +import { BaseLoader, BaseSource } from './base.js' export const ExtendedOctokit = Octokit.plugin(paginateGraphql) export type ExtendedOctokit = InstanceType @@ -25,11 +25,9 @@ export type DiscussionsResponse = { } } -const appId = process.env.SEARCH_GITHUB_APP_ID ?? process.env.DOCS_GITHUB_APP_ID -const installationId = - process.env.SEARCH_GITHUB_APP_INSTALLATION_ID ?? process.env.DOCS_GITHUB_APP_INSTALLATION_ID -const privateKey = - process.env.SEARCH_GITHUB_APP_PRIVATE_KEY ?? process.env.DOCS_GITHUB_APP_PRIVATE_KEY +const appId = process.env.DOCS_GITHUB_APP_ID +const installationId = process.env.DOCS_GITHUB_APP_INSTALLATION_ID +const privateKey = process.env.DOCS_GITHUB_APP_PRIVATE_KEY /** * Fetches GitHub discussions for a repository + category diff --git a/apps/docs/scripts/search/sources/index.ts b/apps/docs/scripts/search/sources/index.ts index 5d02b68421b..9f07c9c72be 100644 --- a/apps/docs/scripts/search/sources/index.ts +++ b/apps/docs/scripts/search/sources/index.ts @@ -4,9 +4,9 @@ import { GitHubDiscussionLoader, type GitHubDiscussionSource, fetchDiscussions, -} from './github-discussion' -import { MarkdownLoader, type MarkdownSource } from './markdown' -import { IntegrationLoader, type IntegrationSource, fetchPartners } from './partner-integrations' +} from './github-discussion.js' +import { MarkdownLoader, type MarkdownSource } from './markdown.js' +import { IntegrationLoader, type IntegrationSource, fetchPartners } from './partner-integrations.js' import { CliReferenceLoader, type CliReferenceSource, @@ -14,8 +14,8 @@ import { type ClientLibReferenceSource, OpenApiReferenceLoader, type OpenApiReferenceSource, -} from './reference-doc' -import { walk } from './util' +} from './reference-doc.js' +import { walk } from './util.js' const ignoredFiles = ['pages/404.mdx'] diff --git a/apps/docs/scripts/search/sources/markdown.ts b/apps/docs/scripts/search/sources/markdown.ts index a34e77c5a97..dcf2d1db722 100644 --- a/apps/docs/scripts/search/sources/markdown.ts +++ b/apps/docs/scripts/search/sources/markdown.ts @@ -1,6 +1,6 @@ import { readFile } from 'fs/promises' -import { processMdx } from '../../helpers.mdx' -import { BaseLoader, BaseSource } from './base' +import { processMdx } from '../../helpers.mdx.js' +import { BaseLoader, BaseSource } from './base.js' export class MarkdownLoader extends BaseLoader { type = 'markdown' as const diff --git a/apps/docs/scripts/search/sources/partner-integrations.ts b/apps/docs/scripts/search/sources/partner-integrations.ts index 76d78e172a2..742e1367990 100644 --- a/apps/docs/scripts/search/sources/partner-integrations.ts +++ b/apps/docs/scripts/search/sources/partner-integrations.ts @@ -1,17 +1,15 @@ import { type SupabaseClient, createClient } from '@supabase/supabase-js' -import { upperFirst } from 'lodash' - -import { BaseLoader, BaseSource } from './base' -import { processMdx } from '../../helpers.mdx' +import { upperFirst } from 'lodash-es' +import { processMdx } from '../../helpers.mdx.js' +import { BaseLoader, BaseSource } from './base.js' type PartnerData = { slug: string // The partner slug corresponding to the last part of the URL overview: string // The Markdown content for indexing } -const supabaseUrl = process.env.NEXT_PUBLIC_MISC_USE_URL ?? process.env.NEXT_PUBLIC_MISC_URL -const supabaseAnonKey = - process.env.NEXT_PUBLIC_MISC_USE_ANON_KEY ?? process.env.NEXT_PUBLIC_MISC_ANON_KEY +const supabaseUrl = process.env.NEXT_PUBLIC_MISC_URL +const supabaseAnonKey = process.env.NEXT_PUBLIC_MISC_ANON_KEY let supabaseClient: SupabaseClient function getSupabaseClient() { diff --git a/apps/docs/scripts/search/sources/reference-doc.ts b/apps/docs/scripts/search/sources/reference-doc.ts index 11315fd1c22..047017ace0f 100644 --- a/apps/docs/scripts/search/sources/reference-doc.ts +++ b/apps/docs/scripts/search/sources/reference-doc.ts @@ -7,12 +7,12 @@ import type { ICommonSection, IFunctionDefinition, ISpec, -} from '../../../components/reference/Reference.types' -import type { CliCommand, CliSpec } from '../../../generator/types/CliSpec' -import { flattenSections } from '../../../lib/helpers' -import { enrichedOperation, gen_v3 } from '../../../lib/refGenerator/helpers' -import type { Json } from '../../helpers.mdx' -import { BaseLoader, BaseSource } from './base' +} from '../../../components/reference/Reference.types.js' +import type { CliCommand, CliSpec } from '../../../generator/types/CliSpec.js' +import { flattenSections } from '../../../lib/helpers.js' +import { enrichedOperation, gen_v3 } from '../../../lib/refGenerator/helpers.js' +import type { Json } from '../../helpers.mdx.js' +import { BaseLoader, BaseSource } from './base.js' export abstract class ReferenceLoader extends BaseLoader { type = 'reference' as const diff --git a/apps/docs/scripts/tsconfig.json b/apps/docs/scripts/tsconfig.json index de1ecb67a32..821a210c5e5 100644 --- a/apps/docs/scripts/tsconfig.json +++ b/apps/docs/scripts/tsconfig.json @@ -1,6 +1,8 @@ // generator files need their own tsconfig.json because they don't like "module": "esnext" setting that nextjs requires in the main tsconfig { "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", "incremental": true, "noImplicitAny": false, "esModuleInterop": true, diff --git a/apps/docs/scripts/utils/dotenv.ts b/apps/docs/scripts/utils/dotenv.ts new file mode 100644 index 00000000000..6a826531068 --- /dev/null +++ b/apps/docs/scripts/utils/dotenv.ts @@ -0,0 +1,5 @@ +import dotenv from 'dotenv' + +dotenv.config({ path: '.env.local' }) + +export default undefined diff --git a/apps/docs/spec/cli_v1_commands.yaml b/apps/docs/spec/cli_v1_commands.yaml index 624088d0d46..05c5bb84268 100644 --- a/apps/docs/spec/cli_v1_commands.yaml +++ b/apps/docs/spec/cli_v1_commands.yaml @@ -1,7 +1,7 @@ clispec: '001' info: id: cli - version: 2.23.4 + version: 2.24.3 title: Supabase CLI language: sh source: https://github.com/supabase/cli @@ -1070,35 +1070,62 @@ commands: description: Select a desired instance size for your project. default_value: '' accepted_values: - - id: micro - name: micro - type: string - - id: small - name: small + - id: large + name: large type: string - id: medium name: medium type: string - - id: large - name: large + - id: micro + name: micro type: string - - id: xlarge - name: xlarge + - id: 12xlarge + name: 12xlarge + type: string + - id: 16xlarge + name: 16xlarge + type: string + - id: 24xlarge + name: 24xlarge + type: string + - id: 24xlarge_high_memory + name: 24xlarge_high_memory + type: string + - id: 24xlarge_optimized_cpu + name: 24xlarge_optimized_cpu + type: string + - id: 24xlarge_optimized_memory + name: 24xlarge_optimized_memory type: string - id: 2xlarge name: 2xlarge type: string + - id: 48xlarge + name: 48xlarge + type: string + - id: 48xlarge_high_memory + name: 48xlarge_high_memory + type: string + - id: 48xlarge_optimized_cpu + name: 48xlarge_optimized_cpu + type: string + - id: 48xlarge_optimized_memory + name: 48xlarge_optimized_memory + type: string - id: 4xlarge name: 4xlarge type: string - id: 8xlarge name: 8xlarge type: string - - id: 12xlarge - name: 12xlarge + - id: pico + name: pico type: string - - id: 16xlarge - name: 16xlarge + - id: small + name: small + type: string + - id: xlarge + name: xlarge type: string - id: supabase-projects-api-keys title: supabase projects api-keys @@ -3770,35 +3797,62 @@ commands: description: Select a desired instance size for the branch database. default_value: '' accepted_values: - - id: micro - name: micro - type: string - - id: small - name: small + - id: large + name: large type: string - id: medium name: medium type: string - - id: large - name: large + - id: micro + name: micro type: string - - id: xlarge - name: xlarge + - id: 12xlarge + name: 12xlarge + type: string + - id: 16xlarge + name: 16xlarge + type: string + - id: 24xlarge + name: 24xlarge + type: string + - id: 24xlarge_high_memory + name: 24xlarge_high_memory + type: string + - id: 24xlarge_optimized_cpu + name: 24xlarge_optimized_cpu + type: string + - id: 24xlarge_optimized_memory + name: 24xlarge_optimized_memory type: string - id: 2xlarge name: 2xlarge type: string + - id: 48xlarge + name: 48xlarge + type: string + - id: 48xlarge_high_memory + name: 48xlarge_high_memory + type: string + - id: 48xlarge_optimized_cpu + name: 48xlarge_optimized_cpu + type: string + - id: 48xlarge_optimized_memory + name: 48xlarge_optimized_memory + type: string - id: 4xlarge name: 4xlarge type: string - id: 8xlarge name: 8xlarge type: string - - id: 12xlarge - name: 12xlarge + - id: pico + name: pico type: string - - id: 16xlarge - name: 16xlarge + - id: small + name: small + type: string + - id: xlarge + name: xlarge type: string - id: experimental name: --experimental diff --git a/apps/docs/spec/common-client-libs-sections.json b/apps/docs/spec/common-client-libs-sections.json index a9206ccf0c4..b7087b1ef69 100644 --- a/apps/docs/spec/common-client-libs-sections.json +++ b/apps/docs/spec/common-client-libs-sections.json @@ -791,6 +791,12 @@ "type": "category", "title": "Realtime", "items": [ + { + "id": "realtime-api", + "title": "Overview", + "slug": "realtime-api", + "type": "function" + }, { "id": "stream", "title": "Listen to database changes", diff --git a/apps/docs/spec/supabase_py_v2.yml b/apps/docs/spec/supabase_py_v2.yml index 90862db77d1..e582bcd3cce 100644 --- a/apps/docs/spec/supabase_py_v2.yml +++ b/apps/docs/spec/supabase_py_v2.yml @@ -97,7 +97,7 @@ functions: url: str = os.environ.get("SUPABASE_URL") key: str = os.environ.get("SUPABASE_KEY") supabase: Client = create_client( - url, + url, key, options=ClientOptions( postgrest_client_timeout=10, @@ -173,7 +173,7 @@ functions: ```python response = supabase.auth.sign_up( { - "email": "email@example.com", + "email": "email@example.com", "password": "password", } ) @@ -281,7 +281,7 @@ functions: ```python response = supabase.auth.sign_up( { - "phone": "123456789", + "phone": "123456789", "password": "password", } ) @@ -467,7 +467,7 @@ functions: ```python response = supabase.auth.sign_in_with_password( { - "email": "email@example.com", + "email": "email@example.com", "password": "example-password", } ) @@ -551,7 +551,7 @@ functions: ```python response = supabase.auth.sign_in_with_password( { - "phone": "+13334445555", + "phone": "+13334445555", "password": "some-password", } ) @@ -776,7 +776,7 @@ functions: ```python response = supabase.auth.sign_in_with_id_token( { - "provider": "google", + "provider": "google", "token": "your-id-token", } ) @@ -1000,8 +1000,8 @@ functions: ```python response = supabase.auth.verify_otp( { - "email": "email@example.com", - "token": "123456", + "email": "email@example.com", + "token": "123456", "type": "email", } ) @@ -1081,8 +1081,8 @@ functions: ```python response = supabase.auth.verify_otp( { - "phone": "+13334445555", - "token": "123456", + "phone": "+13334445555", + "token": "123456", "type": "sms", } ) @@ -1094,8 +1094,8 @@ functions: ```python response = supabase.auth.verify_otp( { - "email": "email@example.com", - "token_hash": "", + "email": "email@example.com", + "token_hash": "", "type": "email", } ) @@ -5531,8 +5531,8 @@ functions: supabase.table("texts") .select("content") .text_search( - "content", - "'eggs' & 'ham'", + "content", + "'eggs' & 'ham'", options={"config": "english"}, ) .execute() @@ -6650,7 +6650,7 @@ functions: code: | ```python response = supabase.functions.invoke( - "hello-world", + "hello-world", invoke_options={ "body": {"name": "Functions"}, }, @@ -6703,6 +6703,32 @@ functions: ) ``` + - id: realtime-api + title: 'Overview' + description: | + Realtime in Python only works with the asynchronous client. + You can initialize a new Supabase client using the `acreate_client()` method. + + - Some Realtime methods are asynchronous and must be awaited. Ensure these methods are called within an `async` function. + - In the following Realtime examples, certain methods are awaited. These should be enclosed within an `async` function. + - When an asynchronous method needs to be used within a synchronous context, such as the callback for `.subscribe()`, utilize `asyncio.create_task()` to schedule the coroutine. This is why the `acreate_client` example includes an import of `asyncio`. + examples: + - id: acreate-client + name: acreate_client() + code: | + ```python + import os + import asyncio + from supabase import acreate_client, AsyncClient + + url: str = os.environ.get("SUPABASE_URL") + key: str = os.environ.get("SUPABASE_KEY") + + async def create_supabase(): + supabase: AsyncClient = await acreate_client(url, key) + return supabase + ``` + - id: subscribe title: on().subscribe() notes: | @@ -6720,15 +6746,15 @@ functions: def on_subscribe(status, err): if status == RealtimeSubscribeStates.SUBSCRIBED: - channel.send_broadcast( - "cursor-pos", + asyncio.create_task(channel.send_broadcast( + "cursor-pos", {"x": random.random(), "y": random.random()} - ) + )) def handle_broadcast(payload): print("Cursor position received!", payload) - channel.on_broadcast(event="cursor-pos", callback=handle_broadcast).subscribe(on_subscribe) + await channel.on_broadcast(event="cursor-pos", callback=handle_broadcast).subscribe(on_subscribe) ``` - id: listen-to-presence-sync name: Listen to presence sync @@ -6739,12 +6765,12 @@ functions: def on_subscribe(status, err): if status == RealtimeSubscribeStates.SUBSCRIBED: - channel.track({"online_at": datetime.datetime.now().isoformat()}) + asyncio.create_task(channel.track({"online_at": datetime.datetime.now().isoformat()})) def handle_presence_sync(): print("Synced presence state: ", channel.presence.state) - channel.on_presence_sync(callback=handle_presence_sync).subscribe(on_subscribe) + await channel.on_presence_sync(callback=handle_presence_sync).subscribe(on_subscribe) ``` - id: listen-to-presence-join name: Listen to presence join @@ -6758,9 +6784,9 @@ functions: def on_subscribe(status, err): if status == RealtimeSubscribeStates.SUBSCRIBED: - channel.track({"online_at": datetime.datetime.now().isoformat()}) + asyncio.create_task(channel.track({"online_at": datetime.datetime.now().isoformat()})) - channel.on_presence_join(callback=handle_presence_join).subscribe(on_subscribe) + await channel.on_presence_join(callback=handle_presence_join).subscribe(on_subscribe) ``` - id: listen-to-presence-leave name: Listen to presence leave @@ -6774,10 +6800,10 @@ functions: def on_subscribe(status, err): if status == RealtimeSubscribeStates.SUBSCRIBED: - channel.track({"online_at": datetime.datetime.now().isoformat()}) - channel.untrack() + asyncio.create_task(channel.track({"online_at": datetime.datetime.now().isoformat()})) + asyncio.create_task(channel.untrack()) - channel.on_presence_leave(callback=handle_presence_leave).subscribe(on_subscribe) + await channel.on_presence_leave(callback=handle_presence_leave).subscribe(on_subscribe) ``` - id: listen-to-all-database-changes name: Listen to all database changes @@ -6785,7 +6811,7 @@ functions: code: | ```python response = ( - supabase.channel("room1") + await supabase.channel("room1") .on_postgres_changes("*", schema="*", callback=handle_record_updated) .subscribe() ) @@ -6795,7 +6821,7 @@ functions: code: | ```python response = ( - supabase.channel("room1") + await supabase.channel("room1") .on_postgres_changes("*", schema="public", table="countries", callback=handle_record_updated) .subscribe() ) @@ -6805,7 +6831,7 @@ functions: code: | ```python response = ( - supabase.channel("room1") + await supabase.channel("room1") .on_postgres_changes("INSERT", schema="public", table="countries", callback=handle_record_inserted) .subscribe() ) @@ -6822,7 +6848,7 @@ functions: code: | ```python response = ( - supabase.channel("room1") + await supabase.channel("room1") .on_postgres_changes("UPDATE", schema="public", table="countries", callback=handle_record_updated) .subscribe() ) @@ -6839,7 +6865,7 @@ functions: code: | ```python response = ( - supabase.channel("room1") + await supabase.channel("room1") .on_postgres_changes("DELETE", schema="public", table="countries", callback=handle_record_deleted) .subscribe() ) @@ -6850,7 +6876,7 @@ functions: code: | ```python response = ( - supabase.channel("room1") + await supabase.channel("room1") .on_postgres_changes("INSERT", schema="public", table="countries", callback=handle_record_inserted) .on_postgres_changes("DELETE", schema="public", table="countries", callback=handle_record_deleted) .subscribe() @@ -6864,7 +6890,7 @@ functions: code: | ```python response = ( - supabase.channel("room1") + await supabase.channel("room1") .on_postgres_changes("UPDATE", schema="public", table="countries", filter="id=eq.200", callback=handle_record_updated) .subscribe() ) @@ -6883,9 +6909,9 @@ functions: def on_subscribe(status, err): if status == RealtimeSubscribeStates.SUBSCRIBED: - channel.send_broadcast('cursor-pos', {"x": random.random(), "y": random.random()}) + asyncio.create_task(channel.send_broadcast('cursor-pos', {"x": random.random(), "y": random.random()})) - channel.subscribe(on_subscribe) + await channel.subscribe(on_subscribe) ``` response: | ``` @@ -6912,7 +6938,7 @@ functions: isSpotlight: true code: | ```python - supabase.remove_channel(myChannel) + await supabase.remove_channel(myChannel) ``` - id: remove-all-channels @@ -6925,7 +6951,7 @@ functions: isSpotlight: true code: | ```python - supabase.remove_all_channels() + await supabase.remove_all_channels() ``` - id: list-buckets @@ -7305,7 +7331,7 @@ functions: supabase.storage .from_("avatars") .move( - "public/avatar1.png", + "public/avatar1.png", "private/avatar2.png" ) ) @@ -7342,7 +7368,7 @@ functions: supabase.storage .from_("avatars") .copy( - "public/avatar1.png", + "public/avatar1.png", "private/avatar2.png" ) ) @@ -7391,7 +7417,7 @@ functions: supabase.storage .from_("avatars") .create_signed_urls( - ["folder/avatar1.png", "folder/avatar2.png"], + ["folder/avatar1.png", "folder/avatar2.png"], 60 ) ) @@ -7475,7 +7501,7 @@ functions: supabase.storage .from_("avatars") .create_signed_url( - "folder/avatar1.png", + "folder/avatar1.png", 60 ) ) @@ -7495,8 +7521,8 @@ functions: supabase.storage .from_("avatars") .create_signed_url( - "folder/avatar1.png", - 60, + "folder/avatar1.png", + 60, {"transform": {"width": 100, "height": 100}} ) ) @@ -7510,8 +7536,8 @@ functions: supabase.storage .from_("avatars") .create_signed_url( - "folder/avatar1.png", - 60, + "folder/avatar1.png", + 60, {"download": True} ) ) @@ -7691,7 +7717,7 @@ functions: supabase.storage .from_("avatars") .get_public_url( - "folder/avatar1.jpg", + "folder/avatar1.jpg", {"transform": {"width": 100, "height": 100}} ) ) @@ -7705,7 +7731,7 @@ functions: supabase.storage .from_("avatars") .get_public_url( - "folder/avatar1.jpg", + "folder/avatar1.jpg", {"download": True} ) ) diff --git a/apps/docs/tailwind.config.js b/apps/docs/tailwind.config.js index 176e4401cee..18cea7130c4 100644 --- a/apps/docs/tailwind.config.js +++ b/apps/docs/tailwind.config.js @@ -10,8 +10,7 @@ module.exports = config({ './layouts/**/*.tsx', './pages/**/*.{tsx,mdx}', './../../packages/ui/src/**/*.{tsx,ts,js}', - './../../packages/ui-patterns/**/*.{tsx,ts,js}', - '!./../../packages/ui-patterns/node_modules/**/*', // ignore node_modules rule + './../../packages/ui-patterns/src/**/*.{tsx,ts,js}', ], plugins: [ function ({ addUtilities, addVariant }) { diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json index 7c10606df6a..2de27417e91 100644 --- a/apps/docs/tsconfig.json +++ b/apps/docs/tsconfig.json @@ -15,7 +15,7 @@ "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", diff --git a/apps/docs/turbo.json b/apps/docs/turbo.json index 3eed84d3fe6..41bf9f6455c 100644 --- a/apps/docs/turbo.json +++ b/apps/docs/turbo.json @@ -17,9 +17,7 @@ "NEXT_PUBLIC_SUPABASE_URL", "NEXT_PUBLIC_SUPABASE_ANON_KEY", "NEXT_PUBLIC_MISC_URL", - "NEXT_PUBLIC_MISC_USE_URL", "NEXT_PUBLIC_MISC_ANON_KEY", - "NEXT_PUBLIC_MISC_USE_ANON_KEY", "NODE_ENV", "NEXT_PUBLIC_API_URL", "NEXT_PUBLIC_BASE_PATH", @@ -48,9 +46,6 @@ "DOCS_REVALIDATION_OVERRIDE_KEYS", "FORCE_ASSET_CDN", "OPENAI_API_KEY", - "SEARCH_GITHUB_APP_ID", - "SEARCH_GITHUB_APP_INSTALLATION_ID", - "SEARCH_GITHUB_APP_PRIVATE_KEY", "SITE_NAME", "SUPABASE_SECRET_KEY" ], diff --git a/apps/docs/types/next-mdx-remote-serialize.ts b/apps/docs/types/next-mdx-remote-serialize.ts new file mode 100644 index 00000000000..4e5c0711d65 --- /dev/null +++ b/apps/docs/types/next-mdx-remote-serialize.ts @@ -0,0 +1,5 @@ +import { serialize } from 'next-mdx-remote/serialize' + +// The SerializeOptions is not exported from next-mdx-remote/serialize, so we need to +// manually define it here. +export type SerializeOptions = Parameters[1] diff --git a/apps/docs/vitest.config.ts b/apps/docs/vitest.config.ts index b0047198db1..bbadb9591ec 100644 --- a/apps/docs/vitest.config.ts +++ b/apps/docs/vitest.config.ts @@ -1,9 +1,10 @@ -import { defineConfig } from 'vitest/config' import tsconfigPaths from 'vite-tsconfig-paths' +import { defineConfig } from 'vitest/config' export default defineConfig({ test: { exclude: ['examples/**/*', '**/node_modules/**'], + setupFiles: ['vitest.setup.ts'], }, plugins: [tsconfigPaths({ root: import.meta.dirname })], }) diff --git a/apps/docs/vitest.setup.ts b/apps/docs/vitest.setup.ts new file mode 100644 index 00000000000..ef03c799e6a --- /dev/null +++ b/apps/docs/vitest.setup.ts @@ -0,0 +1,24 @@ +import { afterAll, beforeAll, vi } from 'vitest' + +let oldEnv: NodeJS.ProcessEnv + +beforeAll(() => { + // Use local Supabase to run e2e tests + oldEnv = { ...process.env } + process.env = { + ...process.env, + NEXT_PUBLIC_SUPABASE_URL: 'http://localhost:54321', + NEXT_PUBLIC_SUPABASE_ANON_KEY: + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0', + } + + // Prevent errors about importing server-only modules from Client Components + vi.mock('server-only', () => { + return {} + }) +}) + +afterAll(() => { + process.env = oldEnv + vi.doUnmock('server-only') +}) diff --git a/apps/studio/components/interfaces/Auth/EmailTemplates/TemplateEditor.tsx b/apps/studio/components/interfaces/Auth/EmailTemplates/TemplateEditor.tsx index 34d72fd0e02..e1448112fb5 100644 --- a/apps/studio/components/interfaces/Auth/EmailTemplates/TemplateEditor.tsx +++ b/apps/studio/components/interfaces/Auth/EmailTemplates/TemplateEditor.tsx @@ -340,6 +340,7 @@ const TemplateEditor = ({ template }: TemplateEditorProps) => { className="!mb-0 mt-0 overflow-hidden h-96 w-full" title={id} srcDoc={bodyValue} + sandbox="allow-scripts allow-forms" /> Applied to:{' '} {policy.roles.slice(0, 3).map((role, i) => ( - - {role} - {policy.roles.length > 1 ? '' : ' '} - + + {role} + {i < Math.min(policy.roles.length, 3) - 1 && ', '} + ))} {policy.roles.length > 1 ? 'roles' : 'role'} diff --git a/apps/studio/components/interfaces/Connect/content/remix/supabasejs/content.tsx b/apps/studio/components/interfaces/Connect/content/remix/supabasejs/content.tsx index 0d9a97bad5f..5e33bcbc7a5 100644 --- a/apps/studio/components/interfaces/Connect/content/remix/supabasejs/content.tsx +++ b/apps/studio/components/interfaces/Connect/content/remix/supabasejs/content.tsx @@ -21,7 +21,7 @@ const ContentFile = ({ projectKeys }: ContentFileProps) => { {` SUPABASE_URL=${projectKeys.apiUrl ?? 'your-project-url'} -SUPABASE_KEY=${projectKeys.anonKey ?? 'your-anon-key'} +SUPABASE_ANON_KEY=${projectKeys.anonKey ?? 'your-anon-key'} `} diff --git a/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx b/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx new file mode 100644 index 00000000000..d51bdf2f5d4 --- /dev/null +++ b/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx @@ -0,0 +1,309 @@ +import Table from 'components/to-be-cleaned/Table' +import { motion } from 'framer-motion' +import { BASE_PATH } from 'lib/constants' +import { ArrowUpRight, Circle, Database, MoreVertical, Plus, Search } from 'lucide-react' +import { useTheme } from 'next-themes' +import Link from 'next/link' +import { useMemo } from 'react' +import ReactFlow, { Background, Handle, Position, ReactFlowProvider } from 'reactflow' +import 'reactflow/dist/style.css' +import { Button, Input } from 'ui' +import { NODE_WIDTH } from '../../Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.constants' + +const STATIC_NODES = [ + { + id: '1', + type: 'primary', + data: { + label: 'Primary Database', + region: 'East US (Ohio)', + provider: 'AWS', + regionIcon: 'EAST_US', + }, + position: { x: 825, y: 0 }, + }, + { + id: '2', + type: 'replica', + data: { + label: 'Iceberg', + details: '3 tables', + regionIcon: 'WEST_US', + }, + position: { x: 875, y: 110 }, + }, + { + id: '3', + type: 'replica', + data: { + label: 'BigQuery', + details: '5 tables', + regionIcon: 'WEST_US', + }, + position: { x: 875, y: 200 }, + }, + { + id: '4', + type: 'blank', + position: { x: 875, y: 290 }, + data: {}, + }, + { + id: '5', + type: 'cta', + position: { x: 125, y: 20 }, + data: {}, + }, +] + +const STATIC_EDGES = [ + { id: 'e1-2', source: '1', target: '2', type: 'smoothstep', animated: true }, + { id: 'e1-3', source: '1', target: '3', type: 'smoothstep', animated: true }, + { id: 'e1-4', source: '1', target: '4', type: 'smoothstep', animated: true }, +] + +const ReplicationStaticMockup = () => { + const nodes = useMemo(() => STATIC_NODES, []) + const edges = useMemo(() => STATIC_EDGES, []) + + const { resolvedTheme } = useTheme() + + const backgroundPatternColor = + resolvedTheme === 'dark' ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.4)' + + const nodeTypes = useMemo( + () => ({ + primary: PrimaryNode, + replica: ReplicaNode, + blank: BlankNode, + cta: CTANode, + }), + [] + ) + + return ( +
+
+ + + +
+ +
+ ) +} + +const ReplicationComingSoon = () => { + return ( + + + + ) +} + +export default ReplicationComingSoon + +const PrimaryNode = ({ + data, +}: { + data: { label: string; region: string; provider: string; regionIcon: string } +}) => { + return ( +
+
+
+
+ +
+
+

{data.label}

+

+ {data.region} +

+

+ {data.provider} +

+
+
+
+ region icon + +
+
+ +
+ ) +} +const ReplicaNode = ({ + data, +}: { + data: { label: string; details: string; regionIcon: string } +}) => { + return ( +
+
+
+
+

{data.label}

+

+ {data.details} +

+
+
+
+ region icon + +
+
+ + +
+ ) +} + +const BlankNode = () => { + return ( +
+
+
+
+ + Add new +
+
+ +
+
+ ) +} + +const CTANode = () => { + return ( + +
+ Early Access +

Replicate Your Data in Real Time

+

+ Stream changes from your Postgres database into your data warehouse—no manual exports, no + lag. +

+

+ We're rolling this out to a limited group of early adopters. Sign up to get early access. +

+

+ +

+
+
+ ) +} + +const StaticDestinations = () => { + const mockRows = [ + { name: 'BigQuery', tables: 4, lag: '55ms', status: 'Enabled' }, + { name: 'Iceberg', tables: 4, lag: '85ms', status: 'Enabled' }, + { name: 'US East', tables: 4, lag: '125ms', status: 'Enabled' }, + ] + + return ( + <> +
+
+
+ } + placeholder="Search..." + /> + +
+ Name, + Publication, + Lag, + Status, + , + ]} + className="mt-4" + body={mockRows.map((row, i) => ( + + {row.name} + + + All + {row.tables} tables + + + {row.lag} + + + + {row.status} + + + + + + + ))} + /> + + + ) +} diff --git a/apps/studio/components/interfaces/Database/Schemas/SchemaGraph.tsx b/apps/studio/components/interfaces/Database/Schemas/SchemaGraph.tsx index 6a8dc557694..ae81db255ce 100644 --- a/apps/studio/components/interfaces/Database/Schemas/SchemaGraph.tsx +++ b/apps/studio/components/interfaces/Database/Schemas/SchemaGraph.tsx @@ -1,6 +1,6 @@ import type { PostgresSchema } from '@supabase/postgres-meta' import { toPng, toSvg } from 'html-to-image' -import { Download, Loader2 } from 'lucide-react' +import { Check, Download, Loader2, Clipboard, Info } from 'lucide-react' import { useTheme } from 'next-themes' import { useEffect, useMemo, useState } from 'react' import ReactFlow, { Background, BackgroundVariant, MiniMap, useReactFlow } from 'reactflow' @@ -21,7 +21,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge import { SchemaGraphLegend } from './SchemaGraphLegend' import { getGraphDataFromTables, getLayoutedElementsViaDagre } from './Schemas.utils' import { TableNode } from './SchemaTableNode' - +import { copyToClipboard } from 'ui' // [Joshen] Persisting logic: Only save positions to local storage WHEN a node is moved OR when explicitly clicked to reset layout export const SchemaGraph = () => { @@ -30,6 +30,13 @@ export const SchemaGraph = () => { const { project } = useProjectContext() const { selectedSchema, setSelectedSchema } = useQuerySchemaState() + const [copied, setCopied] = useState(false) + useEffect(() => { + if (copied) { + setTimeout(() => setCopied(false), 2000) + } + }, [copied]) + const [isDownloading, setIsDownloading] = useState(false) const miniMapNodeColor = '#111318' @@ -160,6 +167,59 @@ export const SchemaGraph = () => { } } + function tablesToSQL(t: typeof tables) { + if (!Array.isArray(t)) return '' + const warning = + '-- WARNING: This schema is for context only and is not meant to be run.\n-- Table order and constraints may not be valid for execution.\n\n' + const sql = t + .map((table) => { + if (!table || !Array.isArray((table as any).columns)) return '' + + const columns = (table as { columns?: any[] }).columns ?? [] + const columnLines = columns.map((c) => { + let line = ` ${c.name} ${c.data_type}` + if (c.is_identity) { + line += ' GENERATED ALWAYS AS IDENTITY' + } + if (c.is_nullable === false) { + line += ' NOT NULL' + } + if (c.default_value !== null && c.default_value !== undefined) { + line += ` DEFAULT ${c.default_value}` + } + if (c.is_unique) { + line += ' UNIQUE' + } + if (c.check) { + line += ` CHECK (${c.check})` + } + return line + }) + + const constraints: string[] = [] + + if (Array.isArray(table.primary_keys) && table.primary_keys.length > 0) { + const pkCols = table.primary_keys.map((pk) => pk.name).join(', ') + constraints.push(` CONSTRAINT ${table.name}_pkey PRIMARY KEY (${pkCols})`) + } + + if (Array.isArray(table.relationships)) { + table.relationships.forEach((rel) => { + if (rel && rel.source_table_name === table.name) { + constraints.push( + ` CONSTRAINT ${rel.constraint_name} FOREIGN KEY (${rel.source_column_name}) REFERENCES ${rel.target_table_schema}.${rel.target_table_name}(${rel.target_column_name})` + ) + } + }) + } + + const allLines = [...columnLines, ...constraints] + return `CREATE TABLE ${table.schema}.${table.name} (\n${allLines.join(',\n')}\n);` + }) + .join('\n') + return warning + sql + } + useEffect(() => { if (isSuccessTables && isSuccessSchemas && tables.length > 0) { const schema = schemas.find((s) => s.name === selectedSchema) as PostgresSchema @@ -192,6 +252,40 @@ export const SchemaGraph = () => { onSelectSchema={setSelectedSchema} />
+ : } + onClick={() => { + if (tables) { + copyToClipboard(tablesToSQL(tables)) + setCopied(true) + } + }} + tooltip={{ + content: { + side: 'bottom', + text: ( +
+

Note

+

+ This schema is for context or debugging only. Table order and constraints + may be invalid. Not meant to be run as-is. +

+
+ ), + }, + }} + > + Copy as SQL +
+ } + onClick={() => downloadImage('png')} + tooltip={{ content: { side: 'bottom', text: 'Download current view as PNG' } }} + /> { + if (isHealthy) return 'Healthy' if (isLoading) return 'Checking status' if (status === 'UNHEALTHY') return 'Unhealthy' if (status === 'COMING_UP') return 'Coming up...' if (status === 'ACTIVE_HEALTHY') return 'Healthy' if (isProjectNew) return 'Coming up...' - if (isSuccess) return 'Healthy' return 'Unable to connect' } @@ -52,21 +52,21 @@ const CheckIcon = () => const StatusIcon = ({ isLoading, - isSuccess, + isHealthy, isProjectNew, projectStatus, }: { isLoading: boolean - isSuccess: boolean + isHealthy: boolean isProjectNew: boolean projectStatus?: ProjectServiceStatus }) => { + if (isHealthy) return if (isLoading) return if (projectStatus === 'UNHEALTHY') return if (projectStatus === 'COMING_UP') return if (projectStatus === 'ACTIVE_HEALTHY') return if (isProjectNew) return - if (isSuccess) return return } @@ -124,17 +124,17 @@ export const ServiceStatus = () => { error?: string docsUrl?: string isLoading: boolean - isSuccess?: boolean + isHealthy: boolean + status: ProjectServiceStatus logsUrl: string - status?: ProjectServiceStatus }[] = [ { name: 'Database', error: undefined, docsUrl: undefined, isLoading: isLoading, - isSuccess: dbStatus?.healthy, - status: dbStatus?.status, + isHealthy: !!dbStatus?.healthy, + status: dbStatus?.status ?? 'UNHEALTHY', logsUrl: '/logs/postgres-logs', }, { @@ -142,8 +142,8 @@ export const ServiceStatus = () => { error: restStatus?.error, docsUrl: undefined, isLoading, - isSuccess: restStatus?.healthy, - status: restStatus?.status, + isHealthy: !!restStatus?.healthy, + status: restStatus?.status ?? 'UNHEALTHY', logsUrl: '/logs/postgrest-logs', }, ...(authEnabled @@ -153,8 +153,8 @@ export const ServiceStatus = () => { error: authStatus?.error, docsUrl: undefined, isLoading, - isSuccess: authStatus?.healthy, - status: authStatus?.status, + isHealthy: !!authStatus?.healthy, + status: authStatus?.status ?? 'UNHEALTHY', logsUrl: '/logs/auth-logs', }, ] @@ -166,8 +166,8 @@ export const ServiceStatus = () => { error: realtimeStatus?.error, docsUrl: undefined, isLoading, - isSuccess: realtimeStatus?.healthy, - status: realtimeStatus?.status, + isHealthy: !!realtimeStatus?.healthy, + status: realtimeStatus?.status ?? 'UNHEALTHY', logsUrl: '/logs/realtime-logs', }, ] @@ -179,8 +179,8 @@ export const ServiceStatus = () => { error: storageStatus?.error, docsUrl: undefined, isLoading, - isSuccess: storageStatus?.healthy, - status: storageStatus?.status, + isHealthy: !!storageStatus?.healthy, + status: storageStatus?.status ?? 'UNHEALTHY', logsUrl: '/logs/storage-logs', }, ] @@ -192,7 +192,12 @@ export const ServiceStatus = () => { error: undefined, docsUrl: 'https://supabase.com/docs/guides/functions/troubleshooting', isLoading, - isSuccess: edgeFunctionsStatus?.healthy, + isHealthy: !!edgeFunctionsStatus?.healthy, + status: edgeFunctionsStatus?.healthy + ? 'ACTIVE_HEALTHY' + : isLoading + ? 'COMING_UP' + : ('UNHEALTHY' as ProjectServiceStatus), logsUrl: '/logs/edge-functions-logs', }, ] @@ -200,7 +205,7 @@ export const ServiceStatus = () => { ] const isLoadingChecks = services.some((service) => service.isLoading) - const allServicesOperational = services.every((service) => service.isSuccess) + const allServicesOperational = services.every((service) => service.isHealthy) // If the project is less than 5 minutes old, and status is not operational, then it's likely the service is still starting up const isProjectNew = @@ -234,7 +239,7 @@ export const ServiceStatus = () => { -
diff --git a/apps/www/components/LaunchWeek/14/TicketCanvas.tsx b/apps/www/components/LaunchWeek/14/TicketCanvas.tsx deleted file mode 100644 index dc84059be6a..00000000000 --- a/apps/www/components/LaunchWeek/14/TicketCanvas.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { cn } from 'ui' -import { useThreeJS } from './helpers' -import { useCallback, useEffect, useRef } from 'react' -import SceneRenderer from './utils/SceneRenderer' -import TicketScene from './scenes/TicketScene' -import useConfData from './hooks/use-conf-data' - -interface TicketCanvasProps { - className?: string - onUpgradeToSecret?: () => void - narrow: boolean -} - -const TicketCanvas = ({ className, onUpgradeToSecret, narrow }: TicketCanvasProps) => { - const sceneRef = useRef(null) - const initQueue = useRef<{ init: Promise; renderer: SceneRenderer }[]>([]) - const onUpgradeToSecretRef = useRef(onUpgradeToSecret) - const [state, dispatch] = useConfData() - const userData = state.userTicketData - const initialSceneDataRef = useRef({ - visible: state.ticketVisibility, - secret: state.userTicketData.secret, - platinum: state.userTicketData.platinum, - narrow: narrow, - user: { - id: state.userTicketData.id, - name: state.userTicketData.name ?? state.userTicketData.username, - ticketNumber: state.userTicketData.ticket_number, - }, - }) - - const setup = useCallback( - (container: HTMLElement) => { - const uuid = Math.random().toString(36).substring(7) - - const sceneRenderer = new SceneRenderer(container, initQueue.current, uuid) - - const initPromise = sceneRenderer.init(async () => { - dispatch({ type: 'TICKET_LOADING_START' }) - const scene = new TicketScene({ - defaultVisible: initialSceneDataRef.current.visible, - defaultSecret: initialSceneDataRef.current.secret, - defaultPlatinum: initialSceneDataRef.current.platinum, - narrow: initialSceneDataRef.current.narrow, - user: initialSceneDataRef.current.user, - onSeatChartButtonClicked: () => { - scene.showBackSide() - scene.upgradeToSecret() - onUpgradeToSecretRef.current?.() - }, - onGoBackButtonClicked: () => { - scene.showFrontSide() - }, - }) - - await sceneRenderer.activateScene(scene, true) - - sceneRef.current = scene - - dispatch({ type: 'TICKET_LOADING_SUCCESS' }) - }) - - initQueue.current.push({ init: initPromise, renderer: sceneRenderer }) - - return sceneRenderer - }, - [dispatch] - ) - - useEffect(() => { - async function updateTicket() { - if (sceneRef.current) { - sceneRef.current.setVisible(state.ticketVisibility) - sceneRef.current.setTicketNumber(state.userTicketData.ticket_number ?? 0) - sceneRef.current.setUserName( - state.userTicketData.name ?? state.userTicketData.username ?? '' - ) - - if (state.userTicketData.secret) await sceneRef.current.upgradeToSecret() - if (state.userTicketData.platinum) await sceneRef.current.upgradeToPlatinum() - - sceneRef.current.reloadTextures() - } - } - - if (sceneRef.current) { - void updateTicket() - } - }, [ - narrow, - state.ticketVisibility, - state.userTicketData.name, - state.userTicketData.platinum, - state.userTicketData.secret, - state.userTicketData.ticket_number, - state.userTicketData.username, - ]) - - useEffect(() => { - onUpgradeToSecretRef.current = onUpgradeToSecret - }, [onUpgradeToSecret]) - - const { containerRef } = useThreeJS(setup) - return ( -
-
{ - sceneRef.current?.click(e.nativeEvent) - }} - /> -
- ) -} - -export default TicketCanvas diff --git a/apps/www/components/LaunchWeek/14/TicketClaim.tsx b/apps/www/components/LaunchWeek/14/TicketClaim.tsx deleted file mode 100644 index 011a49021d1..00000000000 --- a/apps/www/components/LaunchWeek/14/TicketClaim.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { ReactNode } from 'react' -import useConfData from './hooks/use-conf-data' - -export const TicketClaim = ({ children, narrow }: { children: ReactNode; narrow?: boolean }) => { - return
{children}
-} - -export const TicketClaimContent = ({ children }: { children?: ReactNode }) => { - return
{children}
-} - -export const TicketClaimMessage = () => { - const [state] = useConfData() - return ( -
-
- Share your ticket for a chance to win swag -
-
- ) -} - -export const TicketClaimButtons = ({ children }: { children?: ReactNode }) => { - return ( -
- {children} -
- ) -} diff --git a/apps/www/components/LaunchWeek/14/TicketCopy.tsx b/apps/www/components/LaunchWeek/14/TicketCopy.tsx deleted file mode 100644 index 30bafa922c0..00000000000 --- a/apps/www/components/LaunchWeek/14/TicketCopy.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { useState, useRef } from 'react' -import { LW14_URL } from '~/lib/constants' -import { Check, Copy } from 'lucide-react' -import useConfData from './hooks/use-conf-data' -import { cn } from 'ui' - -export default function TicketCopy({ className }: { className?: string }) { - const [state] = useConfData() - const userData = state.userTicketData - const { username } = userData - const [copied, setCopied] = useState(false) - const buttonRef = useRef(null) - const displayUrl = `.../launch-week/tickets/${username}` - const link = `${LW14_URL}/tickets/${username}` - - return ( - - ) -} diff --git a/apps/www/components/LaunchWeek/14/TicketLayout.tsx b/apps/www/components/LaunchWeek/14/TicketLayout.tsx deleted file mode 100644 index 8ba23e6b2fe..00000000000 --- a/apps/www/components/LaunchWeek/14/TicketLayout.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { ReactNode } from 'react' -import { cn } from 'ui' -import SectionContainerWithCn from '~/components/Layouts/SectionContainerWithCn' - -export const TicketLayout = ({ children }: { children: ReactNode }) => { - return ( - - {children} - - ) -} - -export const TicketLayoutCanvas = ({ children, narrow }: { children: ReactNode; narrow: true }) => { - return ( -
- {children} -
- ) -} diff --git a/apps/www/components/LaunchWeek/14/TicketShare.tsx b/apps/www/components/LaunchWeek/14/TicketShare.tsx deleted file mode 100644 index 61712dab331..00000000000 --- a/apps/www/components/LaunchWeek/14/TicketShare.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { useBreakpoint } from 'common' -import dayjs from 'dayjs' -import { Check } from 'lucide-react' -import { useTheme } from 'next-themes' -import Link from 'next/link' -import { useEffect, useRef, useState } from 'react' -import { Button } from 'ui' -import useConfData from './hooks/use-conf-data' -import { - LW14_TWEET_TEXT, - LW14_TWEET_TEXT_PLATINUM, - LW14_TWEET_TEXT_SECRET, - LW14_URL, -} from '~/lib/constants' -import supabase from './supabase' - -export default function TicketShare() { - const { resolvedTheme } = useTheme() - const [state] = useConfData() - const userData = state.userTicketData - const { platinum, username, metadata, secret: hasSecretTicket } = userData - const [_imgReady, setImgReady] = useState(false) - const [_loading, setLoading] = useState(false) - const isLessThanMd = useBreakpoint() - const downloadLink = useRef() - const link = `${LW14_URL}/tickets/${username}?t=${dayjs(new Date()).format('DHHmmss')}` - const permalink = encodeURIComponent(link) - const text = hasSecretTicket - ? LW14_TWEET_TEXT_SECRET - : platinum - ? LW14_TWEET_TEXT_PLATINUM - : LW14_TWEET_TEXT - const encodedText = encodeURIComponent(text) - const tweetUrl = `https://twitter.com/intent/tweet?url=${permalink}&text=${encodedText}` - const linkedInUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${permalink}&text=${encodedText}` - const downloadUrl = `/api-v2/ticket-og?username=${encodeURIComponent(username ?? '')}` - const TICKETS_TABLE = 'tickets' - - useEffect(() => { - setImgReady(false) - - const img = new Image() - - img.src = downloadUrl - img.onload = () => { - setImgReady(true) - setLoading(false) - if (downloadLink.current) { - downloadLink.current.click() - downloadLink.current = undefined - } - } - }, [downloadUrl]) - - const handleShare = async (social: 'twitter' | 'linkedin') => { - setTimeout(async () => { - if (social === 'twitter') { - await supabase - .from(TICKETS_TABLE) - .update({ - shared_on_twitter: 'now', - metadata: { ...metadata, theme: resolvedTheme, hasSharedSecret: hasSecretTicket }, - }) - .eq('launch_week', 'lw14') - .eq('username', username) - } else if (social === 'linkedin') { - await supabase - .from(TICKETS_TABLE) - .update({ - shared_on_linkedin: 'now', - metadata: { ...metadata, theme: resolvedTheme, hasSharedSecret: hasSecretTicket }, - }) - .eq('launch_week', 'lw14') - .eq('username', username) - } - - if (userData.shared_on_linkedin && userData.shared_on_twitter) { - await fetch(`/api-v2/ticket-og?username=${username}&platinum=true`) - } - }) - } - - return ( -
- - -
- ) -} diff --git a/apps/www/components/LaunchWeek/14/TicketShareLayout.tsx b/apps/www/components/LaunchWeek/14/TicketShareLayout.tsx deleted file mode 100644 index d03964e6305..00000000000 --- a/apps/www/components/LaunchWeek/14/TicketShareLayout.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { ReactNode } from 'react' -import { cn } from 'ui' -import useLw14ConfData from './hooks/use-conf-data' - -export const TicketShareLayout = ({ - children, - narrow, -}: { - children?: ReactNode - narrow?: boolean -}) => { - const [{ userTicketData }] = useLw14ConfData() - - const userName = userTicketData.name ?? userTicketData.username - let userWelcomeText = userName ? , {userName}! : '!' - - return ( -
- {narrow && ( - <> - {/* */} -
- Good to see you{userWelcomeText} -
- - )} -
- Share your ticket for a chance to win swag -
- {children} -
- ) -} diff --git a/apps/www/components/LaunchWeek/14/effects/crt-shader.ts b/apps/www/components/LaunchWeek/14/effects/crt-shader.ts deleted file mode 100644 index 9a4cefe9972..00000000000 --- a/apps/www/components/LaunchWeek/14/effects/crt-shader.ts +++ /dev/null @@ -1,89 +0,0 @@ -export const CRTShader = { - uniforms: { - tDiffuse: { value: null }, - time: { value: 0 }, - scanlineIntensity: { value: 0.6 }, - scanlineCount: { value: 360 }, - vignetteIntensity: { value: 0.5 }, - noiseIntensity: { value: 0.01 }, - flickerIntensity: { value: 0.01 }, - rgbShiftAmount: { value: 0.0 }, - // Add intensity control - intensity: { value: 1.0 }, - }, - vertexShader: ` - varying vec2 vUv; - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `, - fragmentShader: ` - uniform sampler2D tDiffuse; - uniform float time; - uniform float scanlineIntensity; - uniform float scanlineCount; - uniform float vignetteIntensity; - uniform float noiseIntensity; - uniform float flickerIntensity; - uniform float rgbShiftAmount; - uniform float intensity; - - varying vec2 vUv; - - // Random function - // https://stackoverflow.com/a/10625698 - float random(vec2 st) { - return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123); - } - - void main() { - // Get original color - vec4 originalColor = texture2D(tDiffuse, vUv); - - // Skip effects if intensity is zero - if (intensity <= 0.0) { - gl_FragColor = originalColor; - return; - } - - // Scale effect parameters by intensity - float activeRGBShift = rgbShiftAmount * intensity; - float activeScanlineIntensity = scanlineIntensity * intensity; - float activeVignetteIntensity = vignetteIntensity * intensity; - float activeNoiseIntensity = noiseIntensity * intensity; - float activeFlickerIntensity = flickerIntensity * intensity; - - // RGB shift effect - more subtle to preserve detail - vec2 shiftR = vec2(activeRGBShift, 0.0); - vec2 shiftG = vec2(0.0, activeRGBShift); - - float r = texture2D(tDiffuse, vUv + shiftR).r; - float g = texture2D(tDiffuse, vUv + shiftG).g; - float b = texture2D(tDiffuse, vUv).b; - - vec4 shiftedColor = vec4(r, g, b, originalColor.a); - - // Scanline effect - thicker and more pronounced - float scanlineFreq = scanlineCount * 0.7; // Reduce frequency for thicker lines - float scanline = sin(vUv.y * scanlineFreq * 3.14159) * 0.5 + 0.5; - scanline = pow(scanline, 0.7) * activeScanlineIntensity * 1.5; // Increase intensity and make lines thicker with lower power - - // Apply scanline as a multiplicative overlay with stronger effect - vec4 scanlineColor = mix(shiftedColor, shiftedColor * (1.0 - scanline), activeScanlineIntensity * 1.2); - - // Vignette effect - vec2 center = vec2(0.5, 0.5); - float dist = distance(vUv, center); - float vignette = smoothstep(0.3, 0.85, dist) * activeVignetteIntensity; - - // Apply vignette as the final overlay effect - vec4 finalColor = scanlineColor * (1.0 - vignette * 0.7); - - // Blend between original and effect based on intensity - vec4 final = mix(originalColor, finalColor, intensity); - final.a = originalColor.a; - gl_FragColor = final; - } - `, -} diff --git a/apps/www/components/LaunchWeek/14/effects/glitch.ts b/apps/www/components/LaunchWeek/14/effects/glitch.ts deleted file mode 100644 index 703c8ee1dad..00000000000 --- a/apps/www/components/LaunchWeek/14/effects/glitch.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { - DataTexture, - FloatType, - MathUtils, - RedFormat, - ShaderMaterial, - UniformsUtils, - WebGLRenderer, - WebGLRenderTarget, -} from 'three' -import { Pass, FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass' -import { DigitalGlitch } from 'three/examples/jsm/shaders/DigitalGlitch' - -class GlitchPass extends Pass { - uniforms: typeof DigitalGlitch.uniforms - heightMap: DataTexture - material: ShaderMaterial - fsQuad: FullScreenQuad - goWild: boolean - curF: number - randX: number - intensity: number - - constructor(dt_size = 64) { - super() - - const shader = DigitalGlitch - - this.uniforms = UniformsUtils.clone(shader.uniforms) - - this.heightMap = this.generateHeightmap(dt_size) - - this.uniforms['tDisp'].value = this.heightMap - - this.material = new ShaderMaterial({ - uniforms: this.uniforms, - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader, - }) - - this.fsQuad = new FullScreenQuad(this.material) - - this.goWild = false - this.curF = 0 - this.randX = 0 - this.generateTrigger() - this.intensity = 0 - } - - render(renderer: WebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget) { - this.uniforms['tDiffuse'].value = readBuffer.texture - this.uniforms['seed'].value = Math.random() //default seeding - this.uniforms['byp'].value = 0 - - if (this.curF % this.randX < this.randX / 10) { - this.uniforms['amount'].value = 0 * this.intensity - this.uniforms['angle'].value = Math.PI - this.uniforms['distortion_x'].value = MathUtils.randFloat(0, 0.05) * this.intensity - this.uniforms['distortion_y'].value = MathUtils.randFloat(0, 0.05) * this.intensity - this.uniforms['seed_x'].value = MathUtils.randFloat(-0.05, 0.05) * this.intensity - this.uniforms['seed_y'].value = MathUtils.randFloat(-0.05, 0.05) * this.intensity - this.curF = 0 - } - - this.curF++ - - if (this.renderToScreen) { - renderer.setRenderTarget(null) - this.fsQuad.render(renderer) - } else { - renderer.setRenderTarget(writeBuffer) - if (this.clear) renderer.clear() - this.fsQuad.render(renderer) - } - } - - generateTrigger() { - this.randX = MathUtils.randInt(200, 240) - } - - generateHeightmap(dt_size: number) { - const data_arr = new Float32Array(dt_size * dt_size) - const length = dt_size * dt_size - - for (let i = 0; i < length; i++) { - const val = MathUtils.randFloat(0, 1) - data_arr[i] = val - } - - const texture = new DataTexture(data_arr, dt_size, dt_size, RedFormat, FloatType) - texture.needsUpdate = true - return texture - } - - setIntensity(amount: number) { - this.intensity = amount < 0.01 ? 0 : amount / 4 - } - - enable(value: boolean) { - this.uniforms['byp'].value = value ? 0 : 1 - } - - dispose() { - this.material.dispose() - - this.heightMap.dispose() - - this.fsQuad.dispose() - } -} - -export { GlitchPass } diff --git a/apps/www/components/LaunchWeek/14/effects/helpers.ts b/apps/www/components/LaunchWeek/14/effects/helpers.ts deleted file mode 100644 index ae8d59d8793..00000000000 --- a/apps/www/components/LaunchWeek/14/effects/helpers.ts +++ /dev/null @@ -1,238 +0,0 @@ -export const shared = ` -vec3 mod289(vec3 x) { - return x - floor(x * (1.0 / 289.0)) * 289.0; -} - -vec4 mod289(vec4 x) { - return x - floor(x * (1.0 / 289.0)) * 289.0; -} - -vec4 permute(vec4 x) -{ - return mod289(((x*34.0)+1.0)*x); -} - -vec4 taylorInvSqrt(vec4 r) -{ - return 1.79284291400159 - 0.85373472095314 * r; -} - -` - -export const simplexNoise3d = ` -// -// Description : Array and textureless GLSL 2D/3D/4D simplex -// noise functions. -// Author : Ian McEwan, Ashima Arts. -// Maintainer : ijm -// Lastmod : 20110822 (ijm) -// License : Copyright (C) 2011 Ashima Arts. All rights reserved. -// Distributed under the MIT License. See LICENSE file. -// https://github.com/ashima/webgl-noise -// - -float snoise3D(vec3 v) - { - const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; - const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); - -// First corner - vec3 i = floor(v + dot(v, C.yyy) ); - vec3 x0 = v - i + dot(i, C.xxx) ; - -// Other corners - vec3 g = step(x0.yzx, x0.xyz); - vec3 l = 1.0 - g; - vec3 i1 = min( g.xyz, l.zxy ); - vec3 i2 = max( g.xyz, l.zxy ); - - // x0 = x0 - 0.0 + 0.0 * C.xxx; - // x1 = x0 - i1 + 1.0 * C.xxx; - // x2 = x0 - i2 + 2.0 * C.xxx; - // x3 = x0 - 1.0 + 3.0 * C.xxx; - vec3 x1 = x0 - i1 + C.xxx; - vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y - vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y - -// Permutations - i = mod289(i); - vec4 p = permute( permute( permute( - i.z + vec4(0.0, i1.z, i2.z, 1.0 )) - + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) - + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); - -// Gradients: 7x7 points over a square, mapped onto an octahedron. -// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) - float n_ = 0.142857142857; // 1.0/7.0 - vec3 ns = n_ * D.wyz - D.xzx; - - vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) - - vec4 x_ = floor(j * ns.z); - vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) - - vec4 x = x_ *ns.x + ns.yyyy; - vec4 y = y_ *ns.x + ns.yyyy; - vec4 h = 1.0 - abs(x) - abs(y); - - vec4 b0 = vec4( x.xy, y.xy ); - vec4 b1 = vec4( x.zw, y.zw ); - - //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; - //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; - vec4 s0 = floor(b0)*2.0 + 1.0; - vec4 s1 = floor(b1)*2.0 + 1.0; - vec4 sh = -step(h, vec4(0.0)); - - vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; - vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; - - vec3 p0 = vec3(a0.xy,h.x); - vec3 p1 = vec3(a0.zw,h.y); - vec3 p2 = vec3(a1.xy,h.z); - vec3 p3 = vec3(a1.zw,h.w); - -//Normalise gradients - vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); - p0 *= norm.x; - p1 *= norm.y; - p2 *= norm.z; - p3 *= norm.w; - -// Mix final noise value - vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); - m = m * m; - return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), - dot(p2,x2), dot(p3,x3) ) ); - } - -#pragma glslify: export(snoise) -` -export const periodicNoise3d = ` -// -// GLSL textureless classic 3D noise "cnoise", -// with an RSL-style periodic variant "pnoise". -// Author: Stefan Gustavson (stefan.gustavson@liu.se) -// Version: 2011-10-11 -// -// Many thanks to Ian McEwan of Ashima Arts for the -// ideas for permutation and gradient selection. -// -// Copyright (c) 2011 Stefan Gustavson. All rights reserved. -// Distributed under the MIT license. See LICENSE file. -// https://github.com/ashima/webgl-noise -// - - -vec3 fade(vec3 t) { - return t*t*t*(t*(t*6.0-15.0)+10.0); -} - -// Classic Perlin noise, periodic variant -float pnoise3D(vec3 P, vec3 rep) -{ - vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period - vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period - Pi0 = mod289(Pi0); - Pi1 = mod289(Pi1); - vec3 Pf0 = fract(P); // Fractional part for interpolation - vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 - vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); - vec4 iy = vec4(Pi0.yy, Pi1.yy); - vec4 iz0 = Pi0.zzzz; - vec4 iz1 = Pi1.zzzz; - - vec4 ixy = permute(permute(ix) + iy); - vec4 ixy0 = permute(ixy + iz0); - vec4 ixy1 = permute(ixy + iz1); - - vec4 gx0 = ixy0 * (1.0 / 7.0); - vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5; - gx0 = fract(gx0); - vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); - vec4 sz0 = step(gz0, vec4(0.0)); - gx0 -= sz0 * (step(0.0, gx0) - 0.5); - gy0 -= sz0 * (step(0.0, gy0) - 0.5); - - vec4 gx1 = ixy1 * (1.0 / 7.0); - vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5; - gx1 = fract(gx1); - vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); - vec4 sz1 = step(gz1, vec4(0.0)); - gx1 -= sz1 * (step(0.0, gx1) - 0.5); - gy1 -= sz1 * (step(0.0, gy1) - 0.5); - - vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); - vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); - vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); - vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); - vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); - vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); - vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); - vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); - - vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); - g000 *= norm0.x; - g010 *= norm0.y; - g100 *= norm0.z; - g110 *= norm0.w; - vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); - g001 *= norm1.x; - g011 *= norm1.y; - g101 *= norm1.z; - g111 *= norm1.w; - - float n000 = dot(g000, Pf0); - float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); - float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); - float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); - float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); - float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); - float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); - float n111 = dot(g111, Pf1); - - vec3 fade_xyz = fade(Pf0); - vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); - vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); - float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); - return 2.2 * n_xyz; -} - -#pragma glslify: export(pnoise) -` - -export const blendSoftLight = ` -// The MIT License (MIT) Copyright (c) 2015 Matt DesLauriers -// -// 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. - -vec3 blendSoftLight(vec3 base, vec3 blend) { - return mix( - sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend), - 2.0 * base * blend + base * base * (1.0 - 2.0 * blend), - step(base, vec3(0.5)) - ); -} -` - -export const luma = ` -// This software is released under the MIT license: -// -// 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. - -float luma(vec3 color) { - return dot(color, vec3(0.299, 0.587, 0.114)); -} - -float luma(vec4 color) { - return dot(color.rgb, vec3(0.299, 0.587, 0.114)); -} -` diff --git a/apps/www/components/LaunchWeek/14/effects/hud-shader.ts b/apps/www/components/LaunchWeek/14/effects/hud-shader.ts deleted file mode 100644 index b0cb8c1c8e2..00000000000 --- a/apps/www/components/LaunchWeek/14/effects/hud-shader.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { blendSoftLight, luma, periodicNoise3d, shared, simplexNoise3d } from './helpers' - -export const HUDShader = { - vertexShader: ` -precision mediump float; - -varying vec2 vUv; - -void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); -} -`, - fragmentShader: ` - uniform sampler2D tDiffuse; - uniform vec2 resolution; - uniform float vignetteRadius; - uniform float vignetteSmoothness; - uniform float time; - - varying vec2 vUv; - varying vec2 screenPosition; - - ${shared} - ${periodicNoise3d} - ${simplexNoise3d} - ${blendSoftLight} - ${luma} - - // grain function - // The MIT License (MIT) Copyright (c) 2015 Matt DesLauriers - // 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. - - float grain(vec2 texCoord, vec2 resolution, float frame, float multiplier) { - vec2 mult = texCoord * resolution; - float offset = snoise3D(vec3(mult / multiplier, frame)); - float n1 = pnoise3D(vec3(mult, offset), vec3(1.0/texCoord * resolution, 1.0)); - return n1 / 2.0 + 0.5; - } - - float grain(vec2 texCoord, vec2 resolution, float frame) { - return grain(texCoord, resolution, frame, 2.5); - } - - float grain(vec2 texCoord, vec2 resolution) { - return grain(texCoord, resolution, 0.0); - } - - // Vignette function - // MIT License - // - // Copyright (c) 2017 Tyler Lindberg - // - // 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. - - float vignette(vec2 uv, float radius, float smoothness) { - float diff = radius - distance(uv, vec2(0.5, 0.5)); - return smoothstep(-smoothness, smoothness, diff); - } - - void main() { - // Sample the diffuse texture to get base color with alpha - - vec4 blackBase = vec4(0.0, 0.0, 0.0, 1.0); - - float v = vignette(vUv, vignetteRadius, vignetteSmoothness); - float invertedVignette = 1.0 - v; - - float minAlpha = 0.3; - blackBase.a *= mix(minAlpha, 1.0, invertedVignette); - - vec4 textureColor = texture2D(tDiffuse, vUv); - - vec4 stackedColor = vec4( - mix(blackBase.rgb, textureColor.rgb, textureColor.a), - mix(blackBase.a, max(blackBase.a, textureColor.a), 0.8) - ); - - float grainSize = 3.0 + 4.0 * sin(time); - vec3 g = vec3(grain(vUv, resolution / grainSize, time)); - - vec3 noiseColor = blendSoftLight(vec3(0.2,0.2,0.2), g); - - float grainStrength = 0.02; - stackedColor = vec4( - mix(stackedColor.rgb, noiseColor, grainStrength), - stackedColor.a - ); - - gl_FragColor = stackedColor; - } -`, - uniforms: { - tDiffuse: { value: null as any }, - time: { value: 0.0 }, - resolution: { value: [70.0, 100.0] }, // Adjusted for Lygia noise scale - vignetteSmoothness: { value: 0 }, - vignetteRadius: { value: 1 }, - }, -} diff --git a/apps/www/components/LaunchWeek/14/effects/transparent-bloom.ts b/apps/www/components/LaunchWeek/14/effects/transparent-bloom.ts deleted file mode 100644 index b63fd6f11cb..00000000000 --- a/apps/www/components/LaunchWeek/14/effects/transparent-bloom.ts +++ /dev/null @@ -1,437 +0,0 @@ -/** - * ISC License - * - * Copyright 2021 mbalex99 - * - * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, - * provided that the above copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE - * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -import { - AdditiveBlending, - Color, - LinearFilter, - MeshBasicMaterial, - RGBAFormat, - ShaderMaterial, - Texture, - UniformsUtils, - Vector2, - Vector3, - WebGLRenderer, - WebGLRenderTarget, -} from 'three' - -import { Pass } from 'three/examples/jsm/postprocessing/Pass' - -// typescript definitions doesn't have FullScreenQuad -//@ts-ignore -import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass' - -import { CopyShader } from 'three/examples/jsm/shaders/CopyShader.js' -import { LuminosityHighPassShader } from 'three/examples/jsm/shaders/LuminosityHighPassShader.js' - -/** - * Thanks to https://github.com/mrdoob/three.js/issues/14104#issuecomment-429664412 for this fragmentShaderfix - * - * UnrealBloomPass is inspired by the bloom pass of Unreal Engine. It creates a - * mip map chain of bloom textures and blurs them with different radii. Because - * of the weighted combination of mips, and because larger blurs are done on - * higher mips, this effect provides good quality and performance. - * - * Reference: - * - https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/ - */ -class TransparentBloomPass extends Pass { - strength: number - radius: number - threshold: number - resolution: Vector2 - clearColor: Color - renderTargetsHorizontal: any[] - renderTargetsVertical: any[] - nMips: number - renderTargetBright: WebGLRenderTarget - highPassUniforms: any - materialHighPassFilter: ShaderMaterial - separableBlurMaterials: any[] - compositeMaterial: ShaderMaterial - bloomTintColors: Vector3[] - copyUniforms: any - materialCopy: ShaderMaterial - _oldClearColor: Color - oldClearAlpha: number - basic: MeshBasicMaterial - fsQuad: FullScreenQuad - static BlurDirectionX: any - static BlurDirectionY: any - constructor(resolution: Vector2, strength: number, radius: number, threshold: number) { - super() - - this.strength = strength !== undefined ? strength : 1 - this.radius = radius - this.threshold = threshold - this.resolution = - resolution !== undefined ? new Vector2(resolution.x, resolution.y) : new Vector2(256, 256) - - // create color only once here, reuse it later inside the render function - this.clearColor = new Color(0, 0, 0) - - // render targets - const pars = { - minFilter: LinearFilter, - magFilter: LinearFilter, - format: RGBAFormat, - } - this.renderTargetsHorizontal = [] - this.renderTargetsVertical = [] - this.nMips = 5 - let resx = Math.round(this.resolution.x / 2) - let resy = Math.round(this.resolution.y / 2) - - this.renderTargetBright = new WebGLRenderTarget(resx, resy, pars) - this.renderTargetBright.texture.name = 'UnrealBloomPass.bright' - this.renderTargetBright.texture.generateMipmaps = false - - for (let i = 0; i < this.nMips; i++) { - const renderTargetHorizonal = new WebGLRenderTarget(resx, resy, pars) - - renderTargetHorizonal.texture.name = 'UnrealBloomPass.h' + i - renderTargetHorizonal.texture.generateMipmaps = false - - this.renderTargetsHorizontal.push(renderTargetHorizonal) - - const renderTargetVertical = new WebGLRenderTarget(resx, resy, pars) - - renderTargetVertical.texture.name = 'UnrealBloomPass.v' + i - renderTargetVertical.texture.generateMipmaps = false - - this.renderTargetsVertical.push(renderTargetVertical) - - resx = Math.round(resx / 2) - - resy = Math.round(resy / 2) - } - - // luminosity high pass material - - if (LuminosityHighPassShader === undefined) - console.error('THREE.UnrealBloomPass relies on LuminosityHighPassShader') - - const highPassShader = LuminosityHighPassShader - this.highPassUniforms = UniformsUtils.clone(highPassShader.uniforms) - - this.highPassUniforms['luminosityThreshold'].value = threshold - this.highPassUniforms['smoothWidth'].value = 0.01 - - this.materialHighPassFilter = new ShaderMaterial({ - uniforms: this.highPassUniforms, - vertexShader: highPassShader.vertexShader, - fragmentShader: highPassShader.fragmentShader, - defines: {}, - }) - - // Gaussian Blur Materials - this.separableBlurMaterials = [] - const kernelSizeArray = [3, 5, 7, 9, 11] - resx = Math.round(this.resolution.x / 2) - resy = Math.round(this.resolution.y / 2) - - for (let i = 0; i < this.nMips; i++) { - this.separableBlurMaterials.push(this.getSeperableBlurMaterial(kernelSizeArray[i])) - - this.separableBlurMaterials[i].uniforms['texSize'].value = new Vector2(resx, resy) - - resx = Math.round(resx / 2) - - resy = Math.round(resy / 2) - } - - // Composite material - this.compositeMaterial = this.getCompositeMaterial(this.nMips) - this.compositeMaterial.uniforms['blurTexture1'].value = this.renderTargetsVertical[0].texture - this.compositeMaterial.uniforms['blurTexture2'].value = this.renderTargetsVertical[1].texture - this.compositeMaterial.uniforms['blurTexture3'].value = this.renderTargetsVertical[2].texture - this.compositeMaterial.uniforms['blurTexture4'].value = this.renderTargetsVertical[3].texture - this.compositeMaterial.uniforms['blurTexture5'].value = this.renderTargetsVertical[4].texture - this.compositeMaterial.uniforms['bloomStrength'].value = strength - this.compositeMaterial.uniforms['bloomRadius'].value = 0.1 - this.compositeMaterial.needsUpdate = true - - const bloomFactors = [1.0, 0.8, 0.6, 0.4, 0.2] - this.compositeMaterial.uniforms['bloomFactors'].value = bloomFactors - this.bloomTintColors = [ - new Vector3(1, 1, 1), - new Vector3(1, 1, 1), - new Vector3(1, 1, 1), - new Vector3(1, 1, 1), - new Vector3(1, 1, 1), - ] - this.compositeMaterial.uniforms['bloomTintColors'].value = this.bloomTintColors - - // copy material - if (CopyShader === undefined) { - console.error('THREE.UnrealBloomPass relies on CopyShader') - } - - const copyShader = CopyShader - - this.copyUniforms = UniformsUtils.clone(copyShader.uniforms) - this.copyUniforms['opacity'].value = 1.0 - - this.materialCopy = new ShaderMaterial({ - uniforms: this.copyUniforms, - vertexShader: copyShader.vertexShader, - fragmentShader: copyShader.fragmentShader, - blending: AdditiveBlending, - depthTest: false, - depthWrite: false, - transparent: true, - }) - - this.enabled = true - this.needsSwap = false - - this._oldClearColor = new Color() - this.oldClearAlpha = 1 - - this.basic = new MeshBasicMaterial() - - this.fsQuad = new FullScreenQuad(undefined) - } - - dispose() { - for (let i = 0; i < this.renderTargetsHorizontal.length; i++) { - this.renderTargetsHorizontal[i].dispose() - } - - for (let i = 0; i < this.renderTargetsVertical.length; i++) { - this.renderTargetsVertical[i].dispose() - } - - this.renderTargetBright.dispose() - } - - setSize(width: number, height: number) { - let resx = Math.round(width / 2) - let resy = Math.round(height / 2) - - this.renderTargetBright.setSize(resx, resy) - - for (let i = 0; i < this.nMips; i++) { - this.renderTargetsHorizontal[i].setSize(resx, resy) - this.renderTargetsVertical[i].setSize(resx, resy) - - this.separableBlurMaterials[i].uniforms['texSize'].value = new Vector2(resx, resy) - - resx = Math.round(resx / 2) - resy = Math.round(resy / 2) - } - } - - render( - renderer: WebGLRenderer, - writeBuffer: any, - readBuffer: { texture: Texture }, - deltaTime: any, - maskActive: any - ) { - renderer.getClearColor(this._oldClearColor) - this.oldClearAlpha = renderer.getClearAlpha() - const oldAutoClear = renderer.autoClear - renderer.autoClear = false - - renderer.setClearColor(this.clearColor, 0) - - if (maskActive) renderer.state.buffers.stencil.setTest(false) - - // Render input to screen - - if (this.renderToScreen) { - this.fsQuad.material = this.basic - this.basic.map = readBuffer.texture - - renderer.setRenderTarget(null) - renderer.clear() - this.fsQuad.render(renderer) - } - - // 1. Extract Bright Areas - - this.highPassUniforms['tDiffuse'].value = readBuffer.texture - this.highPassUniforms['luminosityThreshold'].value = this.threshold - this.fsQuad.material = this.materialHighPassFilter - - renderer.setRenderTarget(this.renderTargetBright) - renderer.clear() - this.fsQuad.render(renderer) - - // 2. Blur All the mips progressively - - let inputRenderTarget = this.renderTargetBright - - for (let i = 0; i < this.nMips; i++) { - this.fsQuad.material = this.separableBlurMaterials[i] - - this.separableBlurMaterials[i].uniforms['colorTexture'].value = inputRenderTarget.texture - this.separableBlurMaterials[i].uniforms['direction'].value = - TransparentBloomPass.BlurDirectionX - renderer.setRenderTarget(this.renderTargetsHorizontal[i]) - renderer.clear() - this.fsQuad.render(renderer) - - this.separableBlurMaterials[i].uniforms['colorTexture'].value = - this.renderTargetsHorizontal[i].texture - this.separableBlurMaterials[i].uniforms['direction'].value = - TransparentBloomPass.BlurDirectionY - renderer.setRenderTarget(this.renderTargetsVertical[i]) - renderer.clear() - this.fsQuad.render(renderer) - - inputRenderTarget = this.renderTargetsVertical[i] - } - - // Composite All the mips - - this.fsQuad.material = this.compositeMaterial - this.compositeMaterial.uniforms['bloomStrength'].value = this.strength - this.compositeMaterial.uniforms['bloomRadius'].value = this.radius - this.compositeMaterial.uniforms['bloomTintColors'].value = this.bloomTintColors - - renderer.setRenderTarget(this.renderTargetsHorizontal[0]) - renderer.clear() - this.fsQuad.render(renderer) - - // Blend it additively over the input texture - - this.fsQuad.material = this.materialCopy - this.copyUniforms['tDiffuse'].value = this.renderTargetsHorizontal[0].texture - - if (maskActive) renderer.state.buffers.stencil.setTest(true) - - if (this.renderToScreen) { - renderer.setRenderTarget(null) - this.fsQuad.render(renderer) - } else { - renderer.setRenderTarget(readBuffer as any) - this.fsQuad.render(renderer) - } - - // Restore renderer settings - - renderer.setClearColor(this._oldClearColor, this.oldClearAlpha) - renderer.autoClear = oldAutoClear - } - - getSeperableBlurMaterial(kernelRadius: number) { - return new ShaderMaterial({ - defines: { - KERNEL_RADIUS: kernelRadius, - SIGMA: kernelRadius, - }, - - uniforms: { - colorTexture: { value: null }, - texSize: { value: new Vector2(0.5, 0.5) }, - direction: { value: new Vector2(0.5, 0.5) }, - }, - - vertexShader: `varying vec2 vUv; - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - }`, - - fragmentShader: `#include - varying vec2 vUv; - uniform sampler2D colorTexture; - uniform vec2 texSize; - uniform vec2 direction; - - float gaussianPdf(in float x, in float sigma) { - return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma; - } - void main() {\n\ - vec2 invSize = 1.0 / texSize;\ - float fSigma = float(SIGMA);\ - float weightSum = gaussianPdf(0.0, fSigma);\ - float alphaSum = 0.0;\ - vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;\ - for( int i = 1; i < KERNEL_RADIUS; i ++ ) {\ - float x = float(i);\ - float w = gaussianPdf(x, fSigma);\ - vec2 uvOffset = direction * invSize * x;\ - vec4 sample1 = texture2D( colorTexture, vUv + uvOffset);\ - vec4 sample2 = texture2D( colorTexture, vUv - uvOffset);\ - diffuseSum += (sample1.rgb + sample2.rgb) * w;\ - alphaSum += (sample1.a + sample2.a) * w;\ - weightSum += 2.0 * w;\ - }\ - gl_FragColor = vec4(diffuseSum/weightSum, alphaSum/weightSum);\n\ - }`, - }) - } - - getCompositeMaterial(nMips: number) { - return new ShaderMaterial({ - defines: { - NUM_MIPS: nMips, - }, - - uniforms: { - blurTexture1: { value: null }, - blurTexture2: { value: null }, - blurTexture3: { value: null }, - blurTexture4: { value: null }, - blurTexture5: { value: null }, - dirtTexture: { value: null }, - bloomStrength: { value: 1.0 }, - bloomFactors: { value: null }, - bloomTintColors: { value: null }, - bloomRadius: { value: 0.0 }, - }, - - vertexShader: `varying vec2 vUv; - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - }`, - - fragmentShader: `varying vec2 vUv; - uniform sampler2D blurTexture1; - uniform sampler2D blurTexture2; - uniform sampler2D blurTexture3; - uniform sampler2D blurTexture4; - uniform sampler2D blurTexture5; - uniform sampler2D dirtTexture; - uniform float bloomStrength; - uniform float bloomRadius; - uniform float bloomFactors[NUM_MIPS]; - uniform vec3 bloomTintColors[NUM_MIPS]; - - float lerpBloomFactor(const in float factor) { - float mirrorFactor = 1.2 - factor; - return mix(factor, mirrorFactor, bloomRadius); - } - - void main() { - gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) + - lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) + - lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) + - lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) + - lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) ); - }`, - }) - } -} - -TransparentBloomPass.BlurDirectionX = new Vector2(1.0, 0.0) -TransparentBloomPass.BlurDirectionY = new Vector2(0.0, 1.0) - -export { TransparentBloomPass as UnrealBloomPass, TransparentBloomPass } diff --git a/apps/www/components/LaunchWeek/14/helpers.ts b/apps/www/components/LaunchWeek/14/helpers.ts deleted file mode 100644 index b9bbb677ddd..00000000000 --- a/apps/www/components/LaunchWeek/14/helpers.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { useRef, useEffect } from 'react' -import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js' - -/** - * Helper to simplifies controlling requestAnimationFrame - * @param callback Animation callback function - * @returns Object with start and stop functions - */ -export const createThreeAnimation = (callback: (time?: number) => void) => { - const requestRef = { current: undefined } as { current: number | undefined } - const previousTimeRef = { current: undefined } as { current: number | undefined } - - const animate = (time: number) => { - if (previousTimeRef.current !== undefined) { - callback(time) - } - previousTimeRef.current = time - requestRef.current = requestAnimationFrame(animate) - } - - const start = () => { - requestRef.current = requestAnimationFrame(animate) - } - - const stop = () => { - if (requestRef.current) { - cancelAnimationFrame(requestRef.current) - } - } - - return { start, stop } -} - -/** - * Helper function to load a GLTF model - * @param url URL of the GLTF model to load - * @returns Promise that resolves with the loaded model - */ -export const loadGLTFModel = (url: string) => { - return new Promise((resolve, reject) => { - const loader = new GLTFLoader() - loader.load( - url, - (gltf) => { - resolve(gltf) - }, - undefined, - (error) => reject(error) - ) - }) -} - -/** - * Custom hook for Three.js setup and animation - * @param setupCallback Callback function for setting up the scene - * @returns Object with containerRef - */ -export const useThreeJS = ( - setupCallback: (container: HTMLElement) => { - cleanup: () => void - animate: (time?: number) => void - } -) => { - const containerRef = useRef(null) - - useEffect(() => { - if (!containerRef.current) return - - const container = containerRef.current - const renderer = setupCallback(container) - - // Set up animation loop - const { start, stop } = createThreeAnimation(renderer.animate.bind(renderer)) - start() - - // Cleanup on unmount - return () => { - stop() - renderer.cleanup() - } - }, [setupCallback]) - - return { containerRef } -} - -export function colorObjToRgb(color: { rgb: number; alpha: number }) { - return `rgb(${(color.rgb >> 16) & 255} ${(color.rgb >> 8) & 255} ${(color.rgb >> 0) & 255} / ${color.alpha})` -} diff --git a/apps/www/components/LaunchWeek/14/scenes/TicketScene.ts b/apps/www/components/LaunchWeek/14/scenes/TicketScene.ts deleted file mode 100644 index af785a1b8f1..00000000000 --- a/apps/www/components/LaunchWeek/14/scenes/TicketScene.ts +++ /dev/null @@ -1,1210 +0,0 @@ -import * as THREE from 'three' -import { colorObjToRgb, loadGLTFModel } from '../helpers' -import SceneRenderer, { BaseScene } from '../utils/SceneRenderer' -import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass' -import { Camera, Euler, MathUtils, Scene, Vector3 } from 'three' -import { GlitchPass } from '../effects/glitch' -import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass' -import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader' -import { CRTShader } from '../effects/crt-shader' -import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass' -import { TransparentBloomPass } from '../effects/transparent-bloom' - -interface TicketSceneState { - visible: boolean - secret: boolean - platinum: boolean - frontside: boolean - ticketNumber: number - narrow: boolean - texts: { - username: string - seatCode: string - date: string - } -} - -interface TicketSceneOptions { - defaultVisible?: boolean - defaultSecret?: boolean - defaultPlatinum?: boolean - user: { - id?: string - name?: string - ticketNumber?: number - } - narrow?: boolean - onSeatChartButtonClicked?: () => void - onWebsiteButtonClicked?: () => void - onGoBackButtonClicked?: () => void -} - -type AvailableTextures = (typeof TicketScene)['TEXTURE_NAMES'][number] - -interface TextureDescriptor { - url: string - cachedData: THREE.Texture | null -} - -class TicketScene implements BaseScene { - raycaster = new THREE.Raycaster() - sceneUrl = '/images/launchweek/14/ticket-model.glb' - - textureImages = { - basic: { - back: { - url: '/images/launchweek/14/back-basic-ticket-texture.png', - cachedData: null, - } as TextureDescriptor, - front: { - url: '/images/launchweek/14/front-basic-ticket-texture.png', - cachedData: null, - } as TextureDescriptor, - - bgColor: { rgb: 0x202020, alpha: 1 }, - textColor: { rgb: 0xffffff, alpha: 1 }, - textDimmedColor: { rgb: 0x515151, alpha: 1 }, - textNeonColor: { rgb: 0xffffff, alpha: 1 }, - textNeonDimmedColor: { rgb: 0x515151, alpha: 1 }, - transparentBg: { rgb: 0x000000, alpha: 0 }, - }, - secret: { - back: { - url: '/images/launchweek/14/back-secret-ticket-texture.png', - cachedData: null, - } as TextureDescriptor, - front: { - url: '/images/launchweek/14/front-secret-ticket-texture.png', - cachedData: null, - } as TextureDescriptor, - seat: { - url: '', - cachedData: null, - } as TextureDescriptor, - bgColor: { rgb: 0x050505, alpha: 1 }, - textColor: { rgb: 0xffffff, alpha: 1 }, - textDimmedColor: { rgb: 0x515151, alpha: 1 }, - textNeonColor: { rgb: 0x2cf494, alpha: 1 }, - textNeonDimmedColor: { rgb: 0x12623b, alpha: 1 }, - transparentBg: { rgb: 0x2cf494, alpha: 0.4 }, - seatPath: new Path2D( - 'M0.824219 0.661133H41.2231C48.5923 0.661133 54.5703 6.63925 54.5703 14.0084V41.06C54.5703 48.4292 48.5923 54.4072 41.2231 54.4072H0.824219V0.661133Z' - ), - }, - platinum: { - back: { - url: '/images/launchweek/14/back-platinum-ticket-texture.png', - cachedData: null, - } as TextureDescriptor, - front: { - url: '/images/launchweek/14/front-platinum-ticket-texture.png', - cachedData: null, - } as TextureDescriptor, - - bgColor: { rgb: 0x050505, alpha: 1 }, - textColor: { rgb: 0xffc73a, alpha: 1 }, - textDimmedColor: { rgb: 0xffc73a, alpha: 1 }, - textNeonColor: { rgb: 0xffc73a, alpha: 1 }, - textNeonDimmedColor: { rgb: 0xffc73a, alpha: 1 }, - transparentBg: { rgb: 0xffc73a, alpha: 0.4 }, - }, - } - - typography = { - main: { - family: 'Departure Mono', - relativeSize: 100 / 1400, - }, - ticketNumber: { - family: 'Nippo-Variable', - weight: 400, - relativeSize: 125 / 1400, - }, - } - - texts = { - user: { - x: 367 / 2000, - y: 533 / 1400, - }, - date: { - x: 1185 / 2000, - y: 255 / 1400, - }, - ticketNumber: { - x: 368 / 2000, - y: 1077.69 / 1400, - }, - ticketNumberBack: { - x: 368 / 2000, - y: 1070 / 1400, - }, - seatStart: { - x: 561.89 / 2000, - y: 739.4 / 1400, - }, - seatPositions: { - x: [ - 0 / 2000, - 99.82 / 2000, - 199.77 / 2000, - 389.84 / 2000, - 488.05 / 2000, - 588.25 / 2000, - 688.43 / 2000, - 788.63 / 2000, - ], - y: [0 / 1400, 71.61 / 1400, 163.5 / 1400, 235.54 / 1400], - }, - } - - fonts: ConstructorParameters[] = [ - [ - 'Departure Mono', - 'url("/fonts/launchweek/14/DepartureMono-Regular.woff2") format("woff2")', - { - weight: '400', - style: 'normal', - }, - ], - [ - 'Nippo-Variable', - 'url("/fonts/launchweek/14/Nippo-Variable.woff2") format("woff2")', - { - weight: '400 700', - style: 'normal', - }, - ], - ] - - state: TicketSceneState - - resolutions = { - 0: { - ticketPosition: new Vector3(0, 0, -1.6), - ticketScale: new Vector3(0.55, 0.55, 0.55), - }, - - 480: { - ticketPosition: new Vector3(0, 0, -1.5), - ticketScale: new Vector3(0.65, 0.65, 0.65), - }, - - 768: { - ticketPosition: new Vector3(0, 0, -0.5), - ticketScale: new Vector3(0.65, 0.65, 0.65), - }, - - // 784: { - // ticketPosition: new Vector3(0, 0, -0.5), - // ticketScale: new Vector3(0.85, 0.85, 0.85), - // }, - - 1024: { - ticketPosition: new Vector3(0, 0, -0.5), - ticketScale: new Vector3(1, 1, 1), - }, - } - - narrowResolutions = { - 0: { - ticketPosition: new Vector3(0, 0, -1.6), - ticketScale: new Vector3(0.65, 0.65, 0.65), - }, - - 480: { - ticketPosition: new Vector3(0, 0, -1.5), - ticketScale: new Vector3(0.65, 0.65, 0.65), - }, - - 768: { - ticketPosition: new Vector3(0, 0, -0.5), - ticketScale: new Vector3(0.65, 0.65, 0.65), - }, - - // 784: { - // ticketPosition: new Vector3(0, 0, -0.5), - // ticketScale: new Vector3(0.85, 0.85, 0.85), - // }, - - 1024: { - ticketPosition: new Vector3(3.0, 0, 0), - ticketScale: new Vector3(1.2, 1.2, 1.2), - }, - - 1280: { - ticketPosition: new Vector3(4.0, 0, 0), - ticketScale: new Vector3(1.4, 1.4, 1.4), - }, - } - - private _internalState = { - naturalRotation: new Vector3(0, 0, Math.PI), - naturalPosition: new Vector3(0, 0, -0.5), - naturalScale: new Vector3(1, 1, 1), - - fontsLoaded: false, - loadedTextureType: null as 'basic' | 'secret' | 'platinum' | null, - effectsIntensity: 0, - } - - private _sceneConfig = { - camera: { - position: new Vector3(0, 10, 0), - rotation: new Euler(MathUtils.degToRad(-90), 0, 0), - fov: 30, - }, - } - - private _sceneRenderer: SceneRenderer | null = null - - private _ticket: THREE.Scene | null = null - private _modelRenderPass: RenderPass | null = null - private static TEXTURE_NAMES = [ - 'TicketFront', - 'TicketBack', - 'TicketEdge', - 'TicketFrontWebsiteButton', - 'TicketFrontSeatChartButton', - 'TicketBackGoBackButton', - 'TicketBackWebsiteButton', - ] as const - private static TEXTURE_PIXEL_DENSITY_FACTOR = 400 - private texturePlaneMapping: { [key in AvailableTextures]?: 'back' | 'front' | 'edge' } = { - TicketFront: 'front', - TicketBack: 'back', - TicketEdge: 'edge', - TicketFrontWebsiteButton: 'front', - TicketFrontSeatChartButton: 'front', - TicketBackGoBackButton: 'back', - TicketBackWebsiteButton: 'back', - } - - private _textureCanvases: { - [key in AvailableTextures]?: { canvas: HTMLCanvasElement; context: CanvasRenderingContext2D } - } = {} - - private _namedMeshes: { [key in AvailableTextures]?: THREE.Mesh } = {} - - private _bloomPass: TransparentBloomPass | null = null - private _glitchPass: GlitchPass | null = null - private _crtPass: ShaderPass | null = null - private _effectsEnabled = false - private _fxaaPass: ShaderPass | null = null - - constructor(private options: TicketSceneOptions) { - this.state = { - visible: options.defaultVisible ?? false, - secret: options.defaultSecret || false, - platinum: options.defaultPlatinum || false, - frontside: true, - ticketNumber: options.user.ticketNumber || 0, - narrow: Boolean(options.narrow), - texts: { - username: options.user.name ?? '', - date: 'MAR 31—APR 4', - // Start assigning seats from A001 - seatCode: (466561 + (options.user.ticketNumber || 0)).toString(36), - }, - } - } - - getId(): string { - return 'TicketScene' - } - - async setup(context: SceneRenderer): Promise { - this._sceneRenderer = context - - // Load fonts before loading the model - await this._loadFonts() - await this._preloadAllTextureSets() - - const resolutionDescriptor = this.getResolutionDescriptor(window.innerWidth) - - this._internalState.naturalPosition.set( - resolutionDescriptor.ticketPosition.x, - resolutionDescriptor.ticketPosition.y, - resolutionDescriptor.ticketPosition.z - ) - - this._internalState.naturalScale.set( - resolutionDescriptor.ticketScale.x, - resolutionDescriptor.ticketScale.y, - resolutionDescriptor.ticketScale.z - ) - - const gltf = await loadGLTFModel(this.sceneUrl) - - this._ticket = gltf.scene.getObjectByName('Plane') as unknown as Scene - - if (!this.state.visible) this._ticket.scale.set(0, 0, 0) - this._ticket.rotation.setFromVector3(this._internalState.naturalRotation) - this._ticket.position.set( - this._internalState.naturalPosition.x, - this._internalState.naturalPosition.y, - this._internalState.naturalPosition.z - ) - - this._setCamera(context.camera) - this._modelRenderPass = new RenderPass(gltf.scene as unknown as Scene, context.camera) - - // Set up post-processing effects but keep them disabled initially - this._setupPostProcessingEffects(context) - - // Important: Don't set clear values for the render pass - // Let the renderer handle clearing - - // Set renderer clear color to make background transparent - context.renderer.setClearColor(0x000000, 0) - - // Keep everything in linear space until the end of the pipeline - // Don't set outputColorSpace here - context.renderer.autoClear = false - - // Add ambient light with increased intensity - const ambientLight = new THREE.AmbientLight(0xffffff, 1.0) - this._ticket.add(ambientLight) - - // Add directional light - const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5) - directionalLight.position.set(5, 5, 5) - gltf.scene.add(directionalLight) - - // Add a point light to better illuminate the placeholder - const pointLight = new THREE.PointLight(0xffffff, 1.0) - pointLight.position.set(0, 0, 5) - this._ticket.add(pointLight) - - this._setupNamedObjects() - this._setupTextureCanvases() - await this._loadTextures() - - context.composer.addPass(this._modelRenderPass) - - this._fxaaPass = new ShaderPass(FXAAShader) - this._fxaaPass.material.uniforms['resolution'].value.x = 1 / (window.innerWidth * 2) - this._fxaaPass.material.uniforms['resolution'].value.y = 1 / (window.innerHeight * 2) - - // Add the effects passes but with zero intensity - if (this._glitchPass) context.composer.addPass(this._glitchPass) - if (this._crtPass) context.composer.addPass(this._crtPass) - if (this._bloomPass) context.composer.addPass(this._bloomPass) - context.composer.addPass(new OutputPass()) - - context.composer.addPass(this._fxaaPass) - - return gltf.scene as unknown as Scene - } - - update(context: SceneRenderer, time?: number): void { - // Clear the renderer with transparent background before rendering - context.renderer.clear(true, true, true) - - const mainRenderPass = context.composer.passes[0] - if (mainRenderPass instanceof RenderPass) { - this._updateNaturalPosition() - this._updateTicketSize() - this._updateTicketPosition() - if (this._ticket) this._updateTicketToFollowMouse(this._ticket, time) - this._updatePasses(time) - } - } - - cleanup(): void {} - - resize(_ev: UIEvent): void { - const resolutionDescriptor = this.getResolutionDescriptor(window.innerWidth) - - this._internalState.naturalPosition.set( - resolutionDescriptor.ticketPosition.x, - resolutionDescriptor.ticketPosition.y, - resolutionDescriptor.ticketPosition.z - ) - - this._internalState.naturalScale.set( - resolutionDescriptor.ticketScale.x, - resolutionDescriptor.ticketScale.y, - resolutionDescriptor.ticketScale.z - ) - - if (this._sceneRenderer && this._fxaaPass) { - this._fxaaPass.material.uniforms['resolution'].value.x = 1 / (window.innerWidth * 2) - this._fxaaPass.material.uniforms['resolution'].value.y = 1 / (window.innerHeight * 2) - } - - return - } - - showFrontSide() { - this.state.frontside = true - } - - showBackSide() { - this.state.frontside = false - } - - async upgradeToSecret() { - this.state.secret = true - await this._loadTextures() - // Start enabling effects gradually - this._enableSecretEffects() - } - - async upgradeToPlatinum() { - this.state.platinum = true - await this._loadTextures() - } - - // In your click method - click(_e: MouseEvent) { - if (!this._sceneRenderer || !this._sceneRenderer.mousePositionState?.isWithinContainer) return - - const mousePosition = this._sceneRenderer.mousePositionState - - this.raycaster.setFromCamera( - new THREE.Vector2(mousePosition.containerX, mousePosition.containerY), - this._sceneRenderer.camera - ) - - // Get all meshes to check for intersection - const meshes = Object.values(this._namedMeshes).filter(Boolean) as THREE.Mesh[] - - // Check for intersections - const intersects = this.raycaster.intersectObjects(meshes, false) - // - for (const intersect of intersects) { - const clickedMesh = intersect.object as THREE.Mesh - - // Handle click based on which mesh was clicked - if (clickedMesh === this._namedMeshes.TicketFrontWebsiteButton) { - this.options.onWebsiteButtonClicked?.() - // Add your action here - } else if (clickedMesh === this._namedMeshes.TicketFrontSeatChartButton) { - this.options.onSeatChartButtonClicked?.() - // Add your action here - } else if (clickedMesh === this._namedMeshes.TicketBackGoBackButton) { - this.options.onGoBackButtonClicked?.() - } else if (clickedMesh === this._namedMeshes.TicketBackWebsiteButton) { - this.options.onWebsiteButtonClicked?.() - // Add your action here - } - } - } - - setVisible(value: boolean) { - this.state.visible = value - } - - setTicketNumber(ticketNumber: number) { - this.state.ticketNumber = ticketNumber - this.state.texts.seatCode = (466561 + ticketNumber).toString(36) - } - - setUserName(name: string) { - this.state.texts.username = name - } - - reloadTextures() { - this._loadTextures(true) - } - - devicePixelRatioChanged(newPixelRatio: number, oldPixelRatio: number): void { - // Update FXAA pass resolution values if it exists - if (this._fxaaPass) { - this._fxaaPass.material.uniforms['resolution'].value.x = - 1 / (window.innerWidth * newPixelRatio) - this._fxaaPass.material.uniforms['resolution'].value.y = - 1 / (window.innerHeight * newPixelRatio) - } - - // Regenerate texture canvases with new pixel ratio - this._setupTextureCanvases() - - // Reload textures to apply new pixel ratio - this._loadTextures(true) - - // Update bloom pass resolution if it exists - if (this._bloomPass && this._sceneRenderer) { - this._bloomPass.resolution.set( - this._sceneRenderer.container.clientWidth * newPixelRatio, - this._sceneRenderer.container.clientHeight * newPixelRatio - ) - } - } - - private _enableSecretEffects() { - if (this._effectsEnabled) return // Already enabled - - this._effectsEnabled = true - this._internalState.effectsIntensity = 0 - - // Enable all passes - if (this._glitchPass) this._glitchPass.enabled = true - if (this._crtPass) this._crtPass.enabled = true - if (this._bloomPass) this._bloomPass.enabled = true - } - - private _updatePasses(time?: number) { - if (!this._sceneRenderer) { - throw new Error('SceneRenderer not loaded') - } - - const mousePosition = this._sceneRenderer.mousePositionState - - // Update time-based uniforms for shader passes - if (this._crtPass && this._crtPass.uniforms['time']) { - this._crtPass.uniforms['time'].value = time - } - - // Update effect intensities if effects are enabled - if (this._effectsEnabled) { - // Gradually increase effect intensity over time - this._internalState.effectsIntensity = Math.min( - this._internalState.effectsIntensity + 0.01, - 1.0 - ) - - // Update glitch pass intensity - if (this._glitchPass) { - const glitchIntensity = mousePosition?.mouseIntensity ?? 1 - this._glitchPass.setIntensity(glitchIntensity * this._internalState.effectsIntensity * 4) - } - - // Update CRT shader intensity - if (this._crtPass && this._crtPass.uniforms['intensity']) { - this._crtPass.uniforms['intensity'].value = this._internalState.effectsIntensity * 1 // Scale to desired max (0.3) - } - - // Update bloom pass parameters based on secret version state - if (this._bloomPass) { - if (this.state.secret) { - // Gradually increase bloom strength from 0 to 3 without affecting transparency - const targetStrength = 3.0 - const currentStrength = this._bloomPass.strength || 0 - const newStrength = currentStrength + (targetStrength - currentStrength) * 0.05 - this._bloomPass.strength = newStrength - - // Gradually decrease threshold for more bloom - const targetThreshold = 0.2 - const currentThreshold = this._bloomPass.threshold || 1.0 - const newThreshold = currentThreshold - (currentThreshold - targetThreshold) * 0.05 - this._bloomPass.threshold = newThreshold - } else { - // Gradually reset bloom to initial values - this._bloomPass.strength = Math.max(0, (this._bloomPass.strength || 0) - 0.05) - this._bloomPass.threshold = Math.min(1.0, (this._bloomPass.threshold || 0.2) + 0.05) - } - } - } else { - // If effects are disabled, ensure all intensities are zero - this._internalState.effectsIntensity = 0 - - if (this._glitchPass) this._glitchPass.setIntensity(0) - if (this._crtPass && this._crtPass.uniforms['intensity']) - this._crtPass.uniforms['intensity'].value = 0 - if (this._bloomPass) { - this._bloomPass.strength = 0 - this._bloomPass.threshold = 1.0 - this._bloomPass.radius = 0.5 - } - } - } - - private _setupPostProcessingEffects(context: SceneRenderer) { - // Create glitch pass and disable it initially - const glitchPass = new GlitchPass(521) - glitchPass.enabled = false // Start disabled - glitchPass.uniforms.col_s.value = 0 - glitchPass.setIntensity(0) // Start with zero intensity - this._glitchPass = glitchPass - - // Create CRT pass with initial zero intensity - const crtPass = new ShaderPass(CRTShader) - crtPass.enabled = false // Start disabled - crtPass.uniforms.intensity.value = 0 // Start with zero intensity - this._crtPass = crtPass - - if (!this._ticket) throw new Error('Ticket not loaded') - if (!this._sceneRenderer) throw new Error('SceneRenderer not loaded') - - // Create bloom pass with initial zero intensity - const bloomPass = new TransparentBloomPass( - new THREE.Vector2(context.container.clientWidth, context.container.clientHeight), - 0, // Initial strength (0-3) - 0.5, // Initial radius - 1.0 // Initial threshold (higher = less bloom) - ) - - bloomPass.enabled = false // Start disabled - this._bloomPass = bloomPass - } - - private _updateTicketToFollowMouse(scene: THREE.Scene, dt?: number) { - if (!this._sceneRenderer) { - throw new Error('SceneRenderer not loaded') - } - - const mousePosition = this._sceneRenderer.mousePositionState - - if (mousePosition.isWithinContainer) { - // Get the scene's current position in world space - const scenePosition = new THREE.Vector3() - scene.getWorldPosition(scenePosition) - - // Convert scene position to normalized device coordinates (NDC) - const sceneNDC = scenePosition.clone() - sceneNDC.project(this._sceneRenderer.camera) - - // Calculate mouse position relative to the scene's position - // This gives us a vector from the scene center to the mouse - const relativeMouseX = mousePosition.containerX - sceneNDC.x - const relativeMouseY = mousePosition.containerY - sceneNDC.y - - // Scale the rotation based on distance from scene center - // The further from center, the more rotation - const distanceScale = Math.min( - 1.0, - Math.sqrt(relativeMouseX * relativeMouseX + relativeMouseY * relativeMouseY) * 2 - ) - - // Calculate rotation angles with distance scaling - // Limit the rotation angles to a reasonable range - const targetRotationX = MathUtils.clamp(relativeMouseY * -0.2 * distanceScale, -0.3, 0.3) - const targetRotationZ = MathUtils.clamp(relativeMouseX * -0.3 * distanceScale, -0.4, 0.4) - - // Apply smooth rotation with a smaller lerp factor - const lerpFactor = Math.min(dt ?? 0.05, 0.05) - scene.rotation.x = MathUtils.lerp( - scene.rotation.x, - this._internalState.naturalRotation.x + targetRotationX, - lerpFactor - ) - scene.rotation.z = MathUtils.lerp( - scene.rotation.z, - this._internalState.naturalRotation.z + targetRotationZ, - lerpFactor - ) - } else { - // Return to neutral position more slowly - const lerpFactor = Math.min(dt ?? 0.03, 0.03) - scene.rotation.x = MathUtils.lerp( - scene.rotation.x, - this._internalState.naturalRotation.x, - lerpFactor - ) - scene.rotation.z = MathUtils.lerp( - scene.rotation.z, - this._internalState.naturalRotation.z, - lerpFactor - ) - } - } - - private _updateNaturalPosition() { - if (this.state.frontside) { - this._internalState.naturalRotation.set(0, 0, Math.PI) - } else { - this._internalState.naturalRotation.set(0, 0, 0) - } - } - - private _setCamera(camera: Camera) { - camera.position.copy(this._sceneConfig.camera.position) - camera.rotation.copy(this._sceneConfig.camera.rotation) - - if (camera instanceof THREE.PerspectiveCamera) { - camera.fov = this._sceneConfig.camera.fov - camera.updateProjectionMatrix() - } - } - - _invisibleScaleVector = new Vector3(0, 0, 0) - - private _updateTicketSize() { - if (this.state.visible) { - this._ticket?.scale.lerp(this._internalState.naturalScale, 0.1) - } else { - this._ticket?.scale.lerp(this._invisibleScaleVector, 0.1) - } - - // Update world matrices after scaling to ensure raycaster works correctly - this._ticket?.updateMatrixWorld(true) - - // Update bounding boxes for all meshes after scaling - this._updateMeshBoundingBoxes() - } - - private _updateTicketPosition() { - this._ticket?.position.lerp(this._internalState.naturalPosition, 0.01) - } - - private _updateMeshBoundingBoxes() { - // Update bounding boxes for all named meshes - for (const meshName in this._namedMeshes) { - const mesh = this._namedMeshes[meshName as AvailableTextures] - if (mesh && mesh.geometry) { - // Ensure the geometry has a bounding box - if (!mesh.geometry.boundingBox) { - mesh.geometry.computeBoundingBox() - } - } - } - } - - private _setupTextureCanvases() { - for (const [name, mesh] of Object.entries(this._namedMeshes)) { - const planeType = this.texturePlaneMapping[name as AvailableTextures] - let referenceMesh: THREE.Mesh | undefined - if (planeType === 'front') { - referenceMesh = this._namedMeshes.TicketFront - } else if (planeType === 'back') { - referenceMesh = this._namedMeshes.TicketBack - } else if (planeType === 'edge') { - referenceMesh = this._namedMeshes.TicketEdge - } - - if (!referenceMesh) { - throw new Error(`Could not find reference mesh for texture ${name}`) - } - - const localBox = new THREE.Box3().setFromObject(referenceMesh) - const localSize = localBox.getSize(new THREE.Vector3()) - - // Get world scale - const worldScale = new THREE.Vector3() - mesh.matrixWorld.decompose(new THREE.Vector3(), new THREE.Quaternion(), worldScale) - - // Apply world scale to get world size - const worldSize = new THREE.Vector3( - localSize.x / Math.abs(worldScale.x), - localSize.y / Math.abs(worldScale.y), - localSize.z / Math.abs(worldScale.z) - ) - - const canvas = document.createElement('canvas') - const canvasWidth = worldSize.x * TicketScene.TEXTURE_PIXEL_DENSITY_FACTOR - const canvasHeight = Math.floor(canvasWidth * (localSize.z / localSize.x)) // Maintain aspect ratio - - canvas.width = canvasWidth - canvas.height = canvasHeight - - // Get canvas context and set up text rendering - const context = canvas.getContext('2d') - - if (!context) { - throw new Error(`Could not get 2D context for text "${name}"`) - } - - this._textureCanvases[name as AvailableTextures] = { - canvas, - context, - } - } - } - - private _setupNamedObjects() { - const mesh = this._ticket - - if (!mesh) { - throw new Error(`Ticket mesh not loaded`) - } - - for (const part of mesh.children) { - if (!(part instanceof THREE.Mesh)) { - continue - } - - if (!(part.material instanceof THREE.Material)) { - console.warn(`Material is not an instance of THREE.Material. Got:`, part.material) - continue - } - - if ((TicketScene.TEXTURE_NAMES as readonly string[]).includes(part.material.name)) { - part.material.dithering = true - this._namedMeshes[part.material.name as AvailableTextures] = part - } else { - console.warn(`Mesh ${part.material.name} is not a named texture`) - } - } - } - - private async _loadTextures(force?: boolean) { - // Load textures for each named mesh - // Create a texture loader - const textureLoader = new THREE.TextureLoader() - const loadingPromises: Promise[] = [] - - const textureSetKey = this.state.platinum ? 'platinum' : this.state.secret ? 'secret' : 'basic' - - if (!force) { - if (this._internalState.loadedTextureType === textureSetKey) { - return - } - } - - this._internalState.loadedTextureType = textureSetKey - - // Determine which texture set to use based on ticket type - const textureSet = this.textureImages[textureSetKey] - - // Map of which image to use for each mesh - const textureImageMap: Partial> = { - TicketFront: textureSet.front, - TicketBack: textureSet.back, - TicketFrontWebsiteButton: textureSet.front, - TicketBackGoBackButton: textureSet.back, - TicketFrontSeatChartButton: textureSet.front, - TicketBackWebsiteButton: textureSet.back, - // Add mappings for buttons if they have separate textures - // Or they can reuse the front/back textures with different UV coordinates - } - - const allMeshes = Object.entries(this._namedMeshes) - // Preload all textures and prepare canvases - for (const [name, mesh] of allMeshes) { - if (!mesh || !(mesh instanceof THREE.Mesh)) { - console.warn(`Mesh ${name} is not a THREE.Mesh`) - continue - } - if (!mesh.material || !(mesh.material instanceof THREE.MeshStandardMaterial)) { - console.warn(`Mesh ${name} has no material or is not a MeshStandardMaterial`) - continue - } - - const textureKey = name as AvailableTextures - const textureCanvas = this._textureCanvases[textureKey] - - if (!textureCanvas) { - console.warn(`No texture canvas found for texture ${textureKey}`) - continue - } - const { canvas, context } = textureCanvas - - // If we have an image for this texture - const textureDescriptor = textureImageMap[textureKey] - if (!textureDescriptor) { - continue - } - - if (textureDescriptor.cachedData === null) { - // Create a loading promise for this texture - await new Promise((resolve) => { - textureLoader.load( - textureDescriptor.url, - (loadedTexture) => { - // Create an image from the loaded texture - textureDescriptor.cachedData = loadedTexture - - // Fix: Set the correct color space to match the original model - loadedTexture.colorSpace = THREE.SRGBColorSpace - - resolve() - }, - undefined, // onProgress callback - (error) => { - console.error(`Error loading texture ${textureDescriptor.url}:`, error) - resolve() // Resolve anyway to not block other textures - } - ) - }) - } - - if (!textureDescriptor.cachedData) { - throw new Error(`Failed to load texture ${textureDescriptor.url}`) - } - - context.drawImage(textureDescriptor.cachedData.image, 0, 0, canvas.width, canvas.height) - - // For meshes without base images, just draw custom content - this._drawCustomContentOnTexture(textureKey, context, canvas) - - const texture = new THREE.CanvasTexture(canvas) - texture.flipY = false - // Set the correct color space for the texture - texture.colorSpace = THREE.SRGBColorSpace - texture.needsUpdate = true - - // Fix: Preserve the original material properties - const originalMaterial = mesh.material - const originalColor = mesh.material.color.clone() - const originalEmissive = mesh.material.emissive ? mesh.material.emissive.clone() : null - const originalEmissiveIntensity = mesh.material.emissiveIntensity - - mesh.material.map = texture - mesh.material.color = originalColor - if (originalEmissive) mesh.material.emissive = originalEmissive - if (originalEmissiveIntensity) mesh.material.emissiveIntensity = originalEmissiveIntensity - - // Ensure material properties are preserved - mesh.material.needsUpdate = true - } - - // Return a promise that resolves when all textures are loaded - return Promise.all(loadingPromises) - } - - // Method to draw custom content on each texture type - private _drawCustomContentOnTexture( - textureKey: AvailableTextures, - context: CanvasRenderingContext2D, - canvas: HTMLCanvasElement - ) { - // Use fallback fonts if custom fonts failed to load - const mainFontFamily = this._internalState.fontsLoaded - ? this.typography.main.family - : 'monospace' - - const ticketNumberFontFamily = this._internalState.fontsLoaded - ? this.typography.ticketNumber.family - : 'monospace' - - // Get the appropriate color scheme based on ticket type - const colors = this.state.platinum - ? this.textureImages.platinum - : this.state.secret - ? this.textureImages.secret - : this.textureImages.basic - - const isNeon = this.state.secret || this.state.platinum - - function getWrappedText({ - text, - maxChars, - maxLines, - }: { - text: string - maxChars: number - maxLines: number - }): string[] { - const lines: string[] = [] - let remainingText = text.trim() - - for (let i = 0; i < maxLines && remainingText.length > 0; i++) { - // If remaining text fits in one line - if (remainingText.length <= maxChars) { - lines.push(remainingText) - break - } - - // Check if we're on the last allowed line - if (i === maxLines - 1) { - // Add ellipsis for truncated text - lines.push(remainingText.substring(0, maxChars - 3) + '...') - break - } - - // Try to find a space to break at - let breakPos = remainingText.lastIndexOf(' ', maxChars) - - // If no space found or it's too early in the string, break at maxChars - if (breakPos <= 0 || breakPos < maxChars / 3) { - breakPos = maxChars - } - - // Add the line and update remaining text - lines.push(remainingText.substring(0, breakPos).trim()) - remainingText = remainingText.substring(breakPos).trim() - } - - return lines - } - - switch (textureKey) { - case 'TicketFront': { - const fontSize = this.typography.main.relativeSize * canvas.height - const usernameLines = getWrappedText({ - text: this.state.texts.username, - maxChars: 18, - maxLines: 4, - }) - - context.font = `400 ${fontSize}px ${mainFontFamily}` - context.textAlign = 'left' - context.textBaseline = 'top' - const lineHeight = fontSize * 1.2 - const padding = 4 - - // Calculate max line width for consistent background width - const maxLineWidth = Math.max( - ...usernameLines.map((line) => context.measureText(line).width) - ) - - // Draw username lines with background - usernameLines.forEach((line, index) => { - const x = this.texts.user.x * canvas.width - const y = this.texts.user.y * canvas.height + index * lineHeight - - const textWidth = context.measureText(line).width - const isLastLine = index === usernameLines.length - 1 - const backgroundWidth = isLastLine ? textWidth : maxLineWidth - - // Draw main background - context.fillStyle = colorObjToRgb(colors.bgColor) - context.fillRect( - x - padding, - y - padding, - backgroundWidth + padding * 2, - fontSize + padding * 2 - ) - - // Draw secondary background (glow effect) - context.fillStyle = colorObjToRgb(colors.transparentBg) - context.fillRect(x - 287, y + 4, backgroundWidth + 280, fontSize - 24) - - // Draw text - context.fillStyle = colorObjToRgb(isNeon ? colors.textNeonColor : colors.textColor) - context.fillText(line, x, y) - }) - - context.fillStyle = colorObjToRgb(isNeon ? colors.textDimmedColor : colors.textColor) - context.fillText( - this.state.texts.date, - this.texts.date.x * canvas.width, - this.texts.date.y * canvas.height - ) - - context.fillStyle = colorObjToRgb(isNeon ? colors.textNeonColor : colors.textDimmedColor) - const ticketNumberFontSize = this.typography.ticketNumber.relativeSize * canvas.height - context.font = `${this.typography.ticketNumber.weight} ${ticketNumberFontSize}px ${ticketNumberFontFamily}` - context.fillText( - this.state.texts.seatCode.toUpperCase(), - this.texts.ticketNumber.x * canvas.width, - this.texts.ticketNumber.y * canvas.height - ) - break - } - case 'TicketBack': { - context.textAlign = 'left' - context.textBaseline = 'top' - - const fontSize = this.typography.main.relativeSize * canvas.height - context.font = `400 ${fontSize}px ${mainFontFamily}` - context.fillStyle = colorObjToRgb(isNeon ? colors.textDimmedColor : colors.textColor) - context.fillText( - this.state.texts.date, - this.texts.date.x * canvas.width, - this.texts.date.y * canvas.height - ) - - context.fillStyle = colorObjToRgb(isNeon ? colors.textNeonColor : colors.textColor) - - // Draw ticket number with different font - const ticketNumberFontSize = this.typography.ticketNumber.relativeSize * canvas.height - context.font = `${this.typography.ticketNumber.weight} ${ticketNumberFontSize}px ${ticketNumberFontFamily}` - context.fillText( - this.state.texts.seatCode.toUpperCase(), - this.texts.ticketNumberBack.x * canvas.width, - this.texts.ticketNumberBack.y * canvas.height - ) - - const seatCoord = this.state.ticketNumber % 32 - const seatCol = seatCoord % 8 - const seatRow = Math.floor(seatCoord / 8) - - context.fillStyle = colorObjToRgb(colors.textNeonColor) - - // Save the current context state - context.save() - // Translate the context based on seat coordinates - // Adjust these multipliers to control spacing between seats - context.translate( - this.texts.seatStart.x * canvas.width + - (this.texts.seatPositions.x[seatCol] ?? 0) * canvas.width, - this.texts.seatStart.y * canvas.height + - (this.texts.seatPositions.y[seatRow] ?? 0) * canvas.height - ) - // Fill the path at the new position - context.fill(this.textureImages.secret.seatPath) - // Restore the context to its original state - context.restore() - - break - } - // Handle other texture types as needed - case 'TicketFrontWebsiteButton': - case 'TicketFrontSeatChartButton': - case 'TicketBackGoBackButton': - case 'TicketBackWebsiteButton': - // These might not need custom content if the base image is sufficient - break - } - } - - private async _loadFonts(): Promise { - try { - // Define the fonts to load - const fontFaces = this.fonts.map((f) => new FontFace(...f)) - // Load all fonts - await Promise.all( - fontFaces.map(async (font) => { - const loadedFont = await font.load() - // Add the loaded font to the document - document.fonts.add(loadedFont) - return loadedFont - }) - ) - - // All fonts loaded successfully - this._internalState.fontsLoaded = true - } catch (error) { - // Handle font loading errors - console.error('Failed to load fonts:', error) - // Still continue to not block rendering, but with a warning - this._internalState.fontsLoaded = false - } - } - - private async _preloadAllTextureSets() { - const textureLoader = new THREE.TextureLoader() - const allSets = ['basic', 'secret', 'platinum'] as const - - let promises: Promise[] = [] - for (const set of allSets) { - const textureSet = this.textureImages[set] - if (textureSet.front.cachedData === null) { - const promise = new Promise((resolve, reject) => { - textureLoader.load( - textureSet.front.url, - (texture) => { - textureSet.front.cachedData = texture - resolve() - }, - undefined, - reject - ) - }) - promises.push(promise) - } - if (textureSet.back.cachedData === null) { - const promise = new Promise((resolve, reject) => { - textureLoader.load( - textureSet.back.url, - (texture) => { - textureSet.back.cachedData = texture - resolve() - }, - undefined, - reject - ) - }) - - promises.push(promise) - } - } - - await Promise.all(promises) - } - - private getResolutionDescriptor(resolution: number) { - const resolutionBase = this.state.narrow ? this.narrowResolutions : this.resolutions - const resolutions = Object.keys(resolutionBase).map(Number) - const closestResolution = resolutions.reduce((prev, curr) => - resolution - curr >= 0 && resolution - curr < resolution - prev ? curr : prev - ) as keyof typeof this.resolutions - - return resolutionBase[closestResolution] - } -} - -export default TicketScene diff --git a/apps/www/components/LaunchWeek/14/utils/SceneRenderer.ts b/apps/www/components/LaunchWeek/14/utils/SceneRenderer.ts deleted file mode 100644 index bccfa39ec52..00000000000 --- a/apps/www/components/LaunchWeek/14/utils/SceneRenderer.ts +++ /dev/null @@ -1,295 +0,0 @@ -import { - ColorManagement, - HalfFloatType, - PerspectiveCamera, - Scene, - SRGBColorSpace, - WebGLRenderer, - WebGLRenderTarget, -} from 'three' -import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer' - -export interface MousePositionState { - clientX: number - clientY: number - isWithinContainer: boolean - containerX: number - containerY: number - mouseIntensity: number -} - -export interface BaseScene { - getId(): string - /** - * Initialize the scene with the given context - */ - setup(context: SceneRenderer): Promise - - /** - * Update the scene for each animation frame - */ - update(context: SceneRenderer, dt?: number): void - - /** - * Clean up resources when the scene is destroyed - */ - cleanup(): void - - resize(ev: UIEvent): void - - /** - * Handle device pixel ratio changes - */ - devicePixelRatioChanged?(newPixelRatio: number, oldPixelRatio: number): void -} - -class SceneRenderer { - renderer: WebGLRenderer - composer: EffectComposer - camera: PerspectiveCamera - mainThreeJsScene: Scene | null = null - activeScenes: BaseScene[] = [] - cachedContainerBBox: DOMRect - mousePositionState: MousePositionState - mouseIntensityDecay = 0.98 - mouseIntensityGainRate = 0.003 - currentPixelRatio: number - - private _resizeHandler: ((ev: UIEvent) => void) | null = null - private _mouseMoveHandler: ((ev: MouseEvent) => void) | null = null - private _mediaQueryListHandler: ((ev: MediaQueryListEvent) => void) | null = null - private _isDisposed = false - private _isInitialized = false - - constructor( - public container: HTMLElement, - private waitFor?: { init: Promise; renderer: SceneRenderer }[], - private uuid?: string - ) { - this.renderer = new WebGLRenderer({ antialias: true, alpha: true }) - this.composer = new EffectComposer(this.renderer) - - this.camera = new PerspectiveCamera( - 75, - this.container.clientWidth / this.container.clientHeight, - 0.1, - 1000 - ) - - this.mousePositionState = { - clientX: 0, - clientY: 0, - isWithinContainer: false, - containerX: 0, - containerY: 0, - mouseIntensity: 0, - } - - this.cachedContainerBBox = this.container.getBoundingClientRect() - this.currentPixelRatio = window.devicePixelRatio - } - - async init(sceneInitializer: () => Promise) { - // console.log('SCENE RENDERER: Init call', this.waitFor?.length, this.uuid) - await Promise.allSettled( - this.waitFor?.filter((t) => t.renderer !== this).map((t) => t.init) || [] - ) - - if (this._isDisposed) { - // console.log('SCENE RENDERER: Already disposed before sceneInitializer', this.uuid) - return - } - - // console.log('SCENE RENDERER: Waited for all pending inits', this.waitFor?.length, this.uuid) - - await sceneInitializer() - - if (this._isDisposed) { - // console.log('SCENE RENDERER: Already disposed after sceneInitializer', this.uuid) - return - } - - this.renderer.setSize(this.container.clientWidth, this.container.clientHeight) - this.renderer.setPixelRatio(window.devicePixelRatio) - this.renderer.setClearColor(0x000000, 0) // Set transparent background - this.renderer.autoClear = false // Prevent automatic clearing - this.renderer.outputColorSpace = SRGBColorSpace - ColorManagement.enabled = true - - // Initialize composer with correct size and pixel ratio - this.composer.setSize(this.container.clientWidth, this.container.clientHeight) - this.composer.setPixelRatio(window.devicePixelRatio) - - this.container.appendChild(this.renderer.domElement) - - this._resizeHandler = this._resize.bind(this) - window.addEventListener('resize', this._resizeHandler) - - this._mouseMoveHandler = this._updateMousePosition.bind(this) - window.addEventListener('mousemove', this._mouseMoveHandler) - - // Disable it since it brakes Chrome browser - // // Add device pixel ratio change listener - // this._mediaQueryListHandler = this._handleDevicePixelRatioChange.bind(this) - // window - // .matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`) - // .addEventListener('change', this._mediaQueryListHandler) - } - - async activateScene(scene: BaseScene, main?: boolean) { - if (this._isDisposed) { - // console.log('SCENE RENDERER: Already disposed before activateScene', this.uuid) - return - } - - const threeScene = await scene.setup(this) - - if (this._isDisposed) { - // console.log('SCENE RENDERER: Already disposed after activateScene', this.uuid) - return - } - - if (main && threeScene) { - this.mainThreeJsScene = threeScene - } - - const newId = scene.getId() - const existingSceneId = this.activeScenes.findIndex((s) => s.getId() === newId) - if (existingSceneId > -1) { - const sceneToRemove = this.activeScenes[existingSceneId] - this.activeScenes[existingSceneId] = scene - sceneToRemove.cleanup() - } else { - this.activeScenes.push(scene) - } - } - - animate(time?: number) { - // Clear the renderer before rendering - this.renderer.clear() - - this._decayMouseIntensity() - - for (const activeScene of this.activeScenes) { - activeScene.update(this, time) - } - this.composer.render(time) - } - - cleanup() { - // console.log('SCENE RENDERER: Cleanup', this.uuid) - - this._isDisposed = true - this.composer.dispose() - this.renderer.dispose() - - try { - this.container.removeChild(this.renderer.domElement) - } catch (e) { - console.warn(e) - } - - if (this._resizeHandler) { - window.removeEventListener('resize', this._resizeHandler) - this._resizeHandler = null - } - - if (this._mouseMoveHandler) { - window.removeEventListener('mousemove', this._mouseMoveHandler) - this._mouseMoveHandler = null - } - - if (this._mediaQueryListHandler) { - window - .matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`) - .removeEventListener('change', this._mediaQueryListHandler) - this._mediaQueryListHandler = null - } - - if (this.waitFor) { - // Mutate array in place instead of replacing reference - this.waitFor.splice( - 0, - this.waitFor.length, - ...this.waitFor.filter((t) => t.renderer === this) - ) - } - } - - private _resize(ev: UIEvent) { - this.camera.aspect = this.container.clientWidth / this.container.clientHeight - this.camera.updateProjectionMatrix() - this.renderer.setSize(this.container.clientWidth, this.container.clientHeight) - this.composer.setSize(this.container.clientWidth, this.container.clientHeight) - this.cachedContainerBBox = this.container.getBoundingClientRect() - - for (const activeScene of this.activeScenes) { - activeScene.resize(ev) - } - } - - private _handleDevicePixelRatioChange(ev: MediaQueryListEvent) { - const oldPixelRatio = this.currentPixelRatio - const newPixelRatio = window.devicePixelRatio - - // Update renderer and composer pixel ratio - this.renderer.setPixelRatio(newPixelRatio) - this.composer.setPixelRatio(newPixelRatio) - - // Store the new pixel ratio - this.currentPixelRatio = newPixelRatio - - // Notify all active scenes about the pixel ratio change - for (const activeScene of this.activeScenes) { - if (typeof activeScene.devicePixelRatioChanged === 'function') { - activeScene.devicePixelRatioChanged(newPixelRatio, oldPixelRatio) - } - } - - // console.log( - // `SCENE RENDERER: Device pixel ratio changed from ${oldPixelRatio} to ${newPixelRatio}` - // ) - } - - private _decayMouseIntensity() { - if (this.mousePositionState.mouseIntensity > 0) { - this.mousePositionState.mouseIntensity *= this.mouseIntensityDecay - if (this.mousePositionState.mouseIntensity < 0.0000001) { - this.mousePositionState.mouseIntensity = 0 - } - } - } - - private _updateMousePosition(ev: MouseEvent) { - this.cachedContainerBBox = this.container.getBoundingClientRect() - const dx = ev.clientX - this.mousePositionState.clientX - const dy = ev.clientY - this.mousePositionState.clientY - const distance = Math.sqrt(dx * dx + dy * dy) - - // Update intensity based on movement (clamped between 0 and 1) - this.mousePositionState.mouseIntensity = Math.min( - 1.0, - Math.max(0, this.mousePositionState.mouseIntensity + distance * this.mouseIntensityGainRate) - ) - - // Update last position - this.mousePositionState.clientX = ev.clientX - this.mousePositionState.clientY = ev.clientY - - const rect = this.cachedContainerBBox - if (!rect) { - return - } - const isWithinContainer = - ev.clientX >= rect.left && - ev.clientX <= rect.right && - ev.clientY >= rect.top && - ev.clientY <= rect.bottom - - this.mousePositionState.isWithinContainer = isWithinContainer - this.mousePositionState.containerX = ((ev.clientX - rect.left) / rect.width) * 2 - 1 - this.mousePositionState.containerY = -((ev.clientY - rect.top) / rect.height) * 2 + 1 - } -} - -export default SceneRenderer diff --git a/apps/www/components/LaunchWeek/14/utils/ticketThemes.tsx b/apps/www/components/LaunchWeek/14/utils/ticketThemes.tsx deleted file mode 100644 index 7693a461b66..00000000000 --- a/apps/www/components/LaunchWeek/14/utils/ticketThemes.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export const themes = () => ({ - regular: { - TICKET_FOREGROUND: '#1F1F1F', - }, - platinum: { - TICKET_FOREGROUND: '#1F1F1F', - }, - secret: { - TICKET_FOREGROUND: '#1F1F1F', - }, - platinumSecret: { - TICKET_FOREGROUND: '#1F1F1F', - }, -}) diff --git a/apps/www/data/features.tsx b/apps/www/data/features.tsx index 0555f654c37..bb147c2b314 100644 --- a/apps/www/data/features.tsx +++ b/apps/www/data/features.tsx @@ -1959,7 +1959,7 @@ This feature is essential for organizations aiming to maintain high security sta products: [ADDITIONAL_PRODUCTS.STUDIO], heroImage: '/images/features/security-and-performance-advisor.png', heroImageLight: '/images/features/security-and-performance-advisor-light.png', - docsUrl: 'https://supabase.com/blog/security-and-performance-advisor', + docsUrl: 'https://supabase.com/blog/security-performance-advisor', slug: 'security-and-performance-advisor', status: { stage: PRODUCT_STAGES.BETA, diff --git a/apps/www/package.json b/apps/www/package.json index 0256bb49737..db6989e702f 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -73,7 +73,6 @@ "remark-html": "^16.0.1", "shared-data": "workspace:*", "swiper": "^11.0.7", - "three": "^0.170.0", "typed.js": "^2.0.16", "typescript": "~5.5.0", "ui": "workspace:*", @@ -97,7 +96,6 @@ "@types/three": "^0.169.0", "@types/uuid": "^9.0.8", "api-types": "workspace:*", - "autoprefixer": "^10.4.14", "postcss": "^8.5.3", "react-hook-form": "^7.45.0", "tsconfig": "workspace:*", diff --git a/apps/www/pages/changelog.tsx b/apps/www/pages/changelog.tsx index cabb9ac13d2..e202e843b0a 100644 --- a/apps/www/pages/changelog.tsx +++ b/apps/www/pages/changelog.tsx @@ -211,7 +211,7 @@ export const getServerSideProps: GetServerSideProps = async ({ res, query }) => // Combine discussions and releases into a single array of entries const combinedEntries = formattedDiscussions.concat(formattedReleases).filter(Boolean) - const sortedCombinedEntries = combinedEntries.sort((a, b) => { + const sortedCombinedEntries = combinedEntries.sort((a: any, b: any) => { const dateA = dayjs(a.created_at) const dateB = dayjs(b.created_at) diff --git a/apps/www/pages/launch-week/index.tsx b/apps/www/pages/launch-week/index.tsx index a2354e01c68..26376aa5640 100644 --- a/apps/www/pages/launch-week/index.tsx +++ b/apps/www/pages/launch-week/index.tsx @@ -1,6 +1,5 @@ import { NextSeo } from 'next-seo' import { LW14_DATE, LW14_TITLE, LW14_URL, SITE_ORIGIN } from '~/lib/constants' -import { LwView } from '~/components/LaunchWeek/14/LwView' import { useRouter } from 'next/router' import { Lw14ConfDataProvider } from '~/components/LaunchWeek/14/hooks/use-conf-data' import DefaultLayout from '~/components/Layouts/Default' @@ -52,7 +51,6 @@ const Lw14Page = () => { -
diff --git a/apps/www/pages/launch-week/tickets/[username].tsx b/apps/www/pages/launch-week/tickets/[username].tsx deleted file mode 100644 index 982ff7149d7..00000000000 --- a/apps/www/pages/launch-week/tickets/[username].tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { NextSeo } from 'next-seo' -import dayjs from 'dayjs' -import Error from 'next/error' -import { GetStaticPaths, GetStaticProps } from 'next' -import { LW14_DATE, LW14_TITLE, LW14_URL, SITE_ORIGIN } from '~/lib/constants' -import { LwView } from '~/components/LaunchWeek/14/LwView' -import { - Lw14ConfDataProvider, - UserTicketData, -} from '~/components/LaunchWeek/14/hooks/use-conf-data' -import { createClient } from '@supabase/supabase-js' -import DefaultLayout from '~/components/Layouts/Default' -import { Tunnel } from '~/components/LaunchWeek/14/Tunnel' - -interface Props { - user: UserTicketData - ogImageUrl: string -} - -const Lw14Page = ({ user, ogImageUrl }: Props) => { - const username = user?.username - const TITLE = `${LW14_TITLE} | ${LW14_DATE}` - const DESCRIPTION = 'Join us for a week of announcing new features, every day at 7 AM PT.' - const PAGE_URL = `${LW14_URL}/tickets/${username}` - - if (!username) { - return - } - - return ( - <> - - - - - - - - - ) -} - -export default Lw14Page - -export const getStaticProps: GetStaticProps = async ({ params }) => { - const username = params?.username?.toString() || null - let user - - const supabaseAdmin = createClient( - process.env.NEXT_PUBLIC_SUPABASE_URL!, - process.env.LIVE_SUPABASE_COM_SERVICE_ROLE_KEY! - ) - - // fetch the normal ticket - // stores the og images in supabase storage - fetch( - // @ts-ignore - `${SITE_ORIGIN}/api-v2/ticket-og?username=${encodeURIComponent(username ?? '')}` - ) - - // fetch a specific user - if (username) { - const { data, error } = await supabaseAdmin! - .from('tickets_view') - .select('name, username, ticket_number, metadata, platinum, secret, role, company, location') - .eq('launch_week', 'lw14') - .eq('username', username) - .single() - - user = data - } - - // fetch the platinum ticket - // stores the og images in supabase storage - if (user?.secret) { - fetch( - `${SITE_ORIGIN}/api-v2/ticket-og?username=${encodeURIComponent(username ?? '')}&secret=true` - ) - } else if (user?.platinum) { - // fetch /api-v2/ticket-og - fetch( - `${SITE_ORIGIN}/api-v2/ticket-og?username=${encodeURIComponent(username ?? '')}&platinum=true` - ) - } - - const ticketType = user?.secret - ? user?.platinum - ? 'platinumSecret' - : 'secret' - : user?.platinum - ? 'platinum' - : 'regular' - const ogImageUrl = `${process.env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/images/launch-week/lw14/og/${ticketType}/${username}.png?t=${dayjs(new Date()).format('DHHmmss')}` - - return { - props: { - user: { - ...user, - username, - }, - ogImageUrl, - key: username, - }, - revalidate: 5, - } -} - -export const getStaticPaths: GetStaticPaths = async () => { - return Promise.resolve({ - paths: [], - fallback: 'blocking', - }) -} diff --git a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/cover-event-triggers-dark.png b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/cover-event-triggers-dark.png new file mode 100644 index 00000000000..4380846473d Binary files /dev/null and b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/cover-event-triggers-dark.png differ diff --git a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/cover-event-triggers-light.png b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/cover-event-triggers-light.png new file mode 100644 index 00000000000..c990cf5f175 Binary files /dev/null and b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/cover-event-triggers-light.png differ diff --git a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/cover-event-triggers.png b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/cover-event-triggers.png deleted file mode 100644 index 0c1b0916883..00000000000 Binary files a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/cover-event-triggers.png and /dev/null differ diff --git a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/fmgr-hook-dark.png b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/fmgr-hook-dark.png new file mode 100644 index 00000000000..198752cfa8d Binary files /dev/null and b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/fmgr-hook-dark.png differ diff --git a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/fmgr-hook-light.png b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/fmgr-hook-light.png new file mode 100644 index 00000000000..c7aef0e80bf Binary files /dev/null and b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/fmgr-hook-light.png differ diff --git a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/fmgr-hook.png b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/fmgr-hook.png deleted file mode 100644 index b862e3718a2..00000000000 Binary files a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/fmgr-hook.png and /dev/null differ diff --git a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/utility-hook-dark.png b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/utility-hook-dark.png new file mode 100644 index 00000000000..da0ebd8faa4 Binary files /dev/null and b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/utility-hook-dark.png differ diff --git a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/utility-hook-light.png b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/utility-hook-light.png new file mode 100644 index 00000000000..405aa34561c Binary files /dev/null and b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/utility-hook-light.png differ diff --git a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/utility-hook.png b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/utility-hook.png deleted file mode 100644 index 9b90a72440e..00000000000 Binary files a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/utility-hook.png and /dev/null differ diff --git a/apps/www/public/planetpg-steve_chavez-rss.xml b/apps/www/public/planetpg-steve_chavez-rss.xml index 0cf42092594..ed086a19ba2 100644 --- a/apps/www/public/planetpg-steve_chavez-rss.xml +++ b/apps/www/public/planetpg-steve_chavez-rss.xml @@ -5,9 +5,9 @@ https://supabase.com/blog Latest Postgres news from Steve Chavez at Supabase en - Fri, 16 Dec 2022 00:00:00 -0700 + Thu, 08 May 2025 00:00:00 -0700 - + https://supabase.com/blog/event-triggers-wo-superuser PostgreSQL Event Triggers without superuser access https://supabase.com/blog/event-triggers-wo-superuser diff --git a/apps/www/public/rss.xml b/apps/www/public/rss.xml index ff71e142a6f..a90e31bf251 100644 --- a/apps/www/public/rss.xml +++ b/apps/www/public/rss.xml @@ -5,11 +5,18 @@ https://supabase.com Latest news from Supabase en - Fri, 04 Apr 2025 00:00:00 -0700 + Mon, 26 May 2025 00:00:00 -0700 + + https://supabase.com/blog/open-data-standards-postgres-otel-iceberg + Open Data Standards: Postgres, OTel, and Iceberg + https://supabase.com/blog/open-data-standards-postgres-otel-iceberg + Replace custom CRUD endpoints, reduce infrastructure complexity, and accelerate product delivery. + Mon, 26 May 2025 00:00:00 -0700 + https://supabase.com/blog/simplify-backend-with-data-api - Simplify back-end complexity with Supabase Data APIs + Simplifying back-end complexity with Supabase Data APIs https://supabase.com/blog/simplify-backend-with-data-api Replace custom CRUD endpoints, reduce infrastructure complexity, and accelerate product delivery. Sat, 17 May 2025 00:00:00 -0700 diff --git a/apps/www/tailwind.config.js b/apps/www/tailwind.config.js index 61fd861a21b..f0df4d91954 100644 --- a/apps/www/tailwind.config.js +++ b/apps/www/tailwind.config.js @@ -9,8 +9,7 @@ module.exports = config({ './lib/mdx/mdxComponents.tsx', './pages/**/*.{tsx,mdx}', './../../packages/ui/src/**/*.{tsx,ts,js}', - './../../packages/ui-patterns/**/*.{tsx,ts,js}', - '!./../../packages/ui-patterns/node_modules/**/*', // ignore node_modules rule + './../../packages/ui-patterns/src/**/*.{tsx,ts,js}', ], theme: { extend: { diff --git a/apps/www/tsconfig.json b/apps/www/tsconfig.json index bc0bd756980..280df54026b 100644 --- a/apps/www/tsconfig.json +++ b/apps/www/tsconfig.json @@ -17,7 +17,7 @@ "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "noEmit": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "incremental": true, diff --git a/apps/www/types/typed-js.d.ts b/apps/www/types/typed-js.d.ts new file mode 100644 index 00000000000..a8745194d9b --- /dev/null +++ b/apps/www/types/typed-js.d.ts @@ -0,0 +1,3 @@ +declare module 'typed.js' { + export { default, type TypedOptions } from 'typed.js/index' +} diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 156f0e975e8..8ca3d74606e 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -11,7 +11,7 @@ services: studio: container_name: supabase-studio - image: supabase/studio:2025.05.19-sha-3487831 + image: supabase/studio:2025.06.02-sha-8f2993d restart: unless-stopped healthcheck: test: @@ -79,7 +79,7 @@ services: auth: container_name: supabase-auth - image: supabase/gotrue:v2.172.1 + image: supabase/gotrue:v2.174.0 restart: unless-stopped healthcheck: test: @@ -231,7 +231,7 @@ services: # To use S3 backed storage: docker compose -f docker-compose.yml -f docker-compose.s3.yml up storage: container_name: supabase-storage - image: supabase/storage-api:v1.22.17 + image: supabase/storage-api:v1.23.0 restart: unless-stopped volumes: - ./volumes/storage:/var/lib/storage:z @@ -296,7 +296,7 @@ services: meta: container_name: supabase-meta - image: supabase/postgres-meta:v0.89.0 + image: supabase/postgres-meta:v0.89.3 restart: unless-stopped depends_on: db: diff --git a/packages/common/database-types.ts b/packages/common/database-types.ts index 7941ad59978..8bdff5b8734 100644 --- a/packages/common/database-types.ts +++ b/packages/common/database-types.ts @@ -1,6 +1,98 @@ export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[] export type Database = { + content: { + Tables: { + error: { + Row: { + code: string + created_at: string | null + deleted_at: string | null + http_status_code: number | null + message: string | null + service: string + updated_at: string | null + } + Insert: { + code: string + created_at?: string | null + deleted_at?: string | null + http_status_code?: number | null + message?: string | null + service: string + updated_at?: string | null + } + Update: { + code?: string + created_at?: string | null + deleted_at?: string | null + http_status_code?: number | null + message?: string | null + service?: string + updated_at?: string | null + } + Relationships: [ + { + foreignKeyName: 'error_service_fkey' + columns: ['service'] + isOneToOne: false + referencedRelation: 'service' + referencedColumns: ['id'] + }, + ] + } + service: { + Row: { + created_at: string | null + deleted_at: string | null + id: string + name: string + updated_at: string | null + } + Insert: { + created_at?: string | null + deleted_at?: string | null + id?: string + name: string + updated_at?: string | null + } + Update: { + created_at?: string | null + deleted_at?: string | null + id?: string + name?: string + updated_at?: string | null + } + Relationships: [] + } + } + Views: { + [_ in never]: never + } + Functions: { + delete_error_codes_except: { + Args: { + skip_codes: Json + } + Returns: undefined + } + update_error_code: { + Args: { + code: string + service: string + http_status_code?: number + message?: string + } + Returns: boolean + } + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } graphql_public: { Tables: { [_ in never]: never diff --git a/packages/common/gotrue.ts b/packages/common/gotrue.ts index 309dd3ab51d..414e3c219ff 100644 --- a/packages/common/gotrue.ts +++ b/packages/common/gotrue.ts @@ -9,17 +9,28 @@ export const AUTH_NAVIGATOR_LOCK_DISABLED_KEY = process.env.NEXT_PUBLIC_AUTH_NAVIGATOR_LOCK_KEY || 'supabase.dashboard.auth.navigatorLock.disabled' +/** + * Catches errors thrown when accessing localStorage. Safari with certain + * security settings throws when localStorage is accessed. + */ +function safeGetLocalStorage(key: string) { + try { + return globalThis?.localStorage?.getItem(key) + } catch { + return null + } +} + const debug = - process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' && - globalThis?.localStorage?.getItem(AUTH_DEBUG_KEY) === 'true' + process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' && safeGetLocalStorage(AUTH_DEBUG_KEY) === 'true' const persistedDebug = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' && - globalThis?.localStorage?.getItem(AUTH_DEBUG_PERSISTED_KEY) === 'true' + safeGetLocalStorage(AUTH_DEBUG_PERSISTED_KEY) === 'true' const shouldEnableNavigatorLock = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' && - !(globalThis?.localStorage?.getItem(AUTH_NAVIGATOR_LOCK_DISABLED_KEY) === 'true') + !(safeGetLocalStorage(AUTH_NAVIGATOR_LOCK_DISABLED_KEY) === 'true') const shouldDetectSessionInUrl = process.env.NEXT_PUBLIC_AUTH_DETECT_SESSION_IN_URL ? process.env.NEXT_PUBLIC_AUTH_DETECT_SESSION_IN_URL === 'true' diff --git a/packages/config/postcss.config.js b/packages/config/postcss.config.js index 33ad091d26d..08a01d4d167 100644 --- a/packages/config/postcss.config.js +++ b/packages/config/postcss.config.js @@ -1,6 +1,5 @@ module.exports = { plugins: { tailwindcss: {}, - autoprefixer: {}, }, } diff --git a/packages/ui-patterns/index.tsx b/packages/ui-patterns/index.tsx index 47f08a27e03..f71baaa6483 100644 --- a/packages/ui-patterns/index.tsx +++ b/packages/ui-patterns/index.tsx @@ -2,28 +2,14 @@ * The components are listed here so that VsCode can find out about them and list them as import suggestions. Don't * import directly from here. */ -export * from './admonition' -export * from './AssistantChat/AssistantChatForm' -export * from './AssistantChat/AssistantCommandsPopover' -export * from './AuthenticatedDropdownMenu' -export * from './CommandMenu' -export * from './ComputeBadge' -export * from './ConsentToast' -export * from './consent' -export * from './CountdownWidget' -export * from './Dialogs/ConfirmDialog' -export * from './Dialogs/ConfirmationModal' -export * from './ExpandableVideo' -export * from './GlassPanel' -export * from './IconPanel' -export * from './InnerSideMenu' -export * from './LogsBarChart' -export * from './MobileSheetNav' -export * from './PrivacySettings' -export * from './PromoToast' -export * from './ThemeToggle' -export * from './TweetCard' -export * from './TimestampInfo' -export * from './FilterBar' -export * from './Toc' -export * from './ShimmeringLoader' +export * from './src/admonition' +export * from './src/AssistantChat/AssistantChatForm' +export * from './src/AuthenticatedDropdownMenu' +export * from './src/CommandMenu' +export * from './src/ComputeBadge' +export * from './src/FilterBar' +export * from './src/GlassPanel' +export * from './src/InnerSideMenu' +export * from './src/ShimmeringLoader' +export * from './src/TimestampInfo' +export * from './src/Toc' diff --git a/packages/ui-patterns/package.json b/packages/ui-patterns/package.json index f440a4179c5..cb3051fb466 100644 --- a/packages/ui-patterns/package.json +++ b/packages/ui-patterns/package.json @@ -7,14 +7,484 @@ "scripts": { "preinstall": "npx only-allow pnpm", "test": "vitest", + "test:coverage": "vitest --run --coverage", "clean": "rimraf node_modules", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "gen:exports": "tsx ./scripts/update-exports.ts" + }, + "exports": { + "./package.json": "./package.json", + ".": { + "import": "./index.tsx", + "types": "./index.tsx" + }, + "./AssistantChat/AssistantChatForm": { + "import": "./src/AssistantChat/AssistantChatForm.tsx", + "types": "./src/AssistantChat/AssistantChatForm.tsx" + }, + "./AssistantChat/AssistantCommandsPopover": { + "import": "./src/AssistantChat/AssistantCommandsPopover.tsx", + "types": "./src/AssistantChat/AssistantCommandsPopover.tsx" + }, + "./AssistantChat": { + "import": "./src/AssistantChat/index.ts", + "types": "./src/AssistantChat/index.ts" + }, + "./AuthenticatedDropdownMenu": { + "import": "./src/AuthenticatedDropdownMenu/index.tsx", + "types": "./src/AuthenticatedDropdownMenu/index.tsx" + }, + "./Banners/Countdown": { + "import": "./src/Banners/Countdown.tsx", + "types": "./src/Banners/Countdown.tsx" + }, + "./Banners/LW14Announcement": { + "import": "./src/Banners/LW14Announcement.tsx", + "types": "./src/Banners/LW14Announcement.tsx" + }, + "./Banners/LW14Banner": { + "import": "./src/Banners/LW14Banner.tsx", + "types": "./src/Banners/LW14Banner.tsx" + }, + "./CommandMenu/api/Badges": { + "import": "./src/CommandMenu/api/Badges.tsx", + "types": "./src/CommandMenu/api/Badges.tsx" + }, + "./CommandMenu/api/CommandHeader": { + "import": "./src/CommandMenu/api/CommandHeader.tsx", + "types": "./src/CommandMenu/api/CommandHeader.tsx" + }, + "./CommandMenu/api/CommandInput": { + "import": "./src/CommandMenu/api/CommandInput.tsx", + "types": "./src/CommandMenu/api/CommandInput.tsx" + }, + "./CommandMenu/api/CommandList": { + "import": "./src/CommandMenu/api/CommandList.tsx", + "types": "./src/CommandMenu/api/CommandList.tsx" + }, + "./CommandMenu/api/CommandMenu": { + "import": "./src/CommandMenu/api/CommandMenu.tsx", + "types": "./src/CommandMenu/api/CommandMenu.tsx" + }, + "./CommandMenu/api/CommandProvider": { + "import": "./src/CommandMenu/api/CommandProvider.tsx", + "types": "./src/CommandMenu/api/CommandProvider.tsx" + }, + "./CommandMenu/api/TextHighlighter": { + "import": "./src/CommandMenu/api/TextHighlighter.tsx", + "types": "./src/CommandMenu/api/TextHighlighter.tsx" + }, + "./CommandMenu/api/hooks/commandsHooks.test": { + "import": "./src/CommandMenu/api/hooks/commandsHooks.test.tsx", + "types": "./src/CommandMenu/api/hooks/commandsHooks.test.tsx" + }, + "./CommandMenu/api/hooks/commandsHooks": { + "import": "./src/CommandMenu/api/hooks/commandsHooks.ts", + "types": "./src/CommandMenu/api/hooks/commandsHooks.ts" + }, + "./CommandMenu/api/hooks/pagesHooks.test": { + "import": "./src/CommandMenu/api/hooks/pagesHooks.test.tsx", + "types": "./src/CommandMenu/api/hooks/pagesHooks.test.tsx" + }, + "./CommandMenu/api/hooks/pagesHooks": { + "import": "./src/CommandMenu/api/hooks/pagesHooks.ts", + "types": "./src/CommandMenu/api/hooks/pagesHooks.ts" + }, + "./CommandMenu/api/hooks/queryHooks": { + "import": "./src/CommandMenu/api/hooks/queryHooks.ts", + "types": "./src/CommandMenu/api/hooks/queryHooks.ts" + }, + "./CommandMenu/api/hooks/useCommandFilterState": { + "import": "./src/CommandMenu/api/hooks/useCommandFilterState.ts", + "types": "./src/CommandMenu/api/hooks/useCommandFilterState.ts" + }, + "./CommandMenu/api/hooks/useCrossCompatRouter": { + "import": "./src/CommandMenu/api/hooks/useCrossCompatRouter.tsx", + "types": "./src/CommandMenu/api/hooks/useCrossCompatRouter.tsx" + }, + "./CommandMenu/api/hooks/useHistoryKeys": { + "import": "./src/CommandMenu/api/hooks/useHistoryKeys.ts", + "types": "./src/CommandMenu/api/hooks/useHistoryKeys.ts" + }, + "./CommandMenu/api/hooks/viewHooks": { + "import": "./src/CommandMenu/api/hooks/viewHooks.ts", + "types": "./src/CommandMenu/api/hooks/viewHooks.ts" + }, + "./CommandMenu/api/types": { + "import": "./src/CommandMenu/api/types.ts", + "types": "./src/CommandMenu/api/types.ts" + }, + "./CommandMenu/api/utils": { + "import": "./src/CommandMenu/api/utils.ts", + "types": "./src/CommandMenu/api/utils.ts" + }, + "./CommandMenu": { + "import": "./src/CommandMenu/index.tsx", + "types": "./src/CommandMenu/index.tsx" + }, + "./CommandMenu/internal/Command": { + "import": "./src/CommandMenu/internal/Command.tsx", + "types": "./src/CommandMenu/internal/Command.tsx" + }, + "./CommandMenu/internal/CommandEmpty": { + "import": "./src/CommandMenu/internal/CommandEmpty.tsx", + "types": "./src/CommandMenu/internal/CommandEmpty.tsx" + }, + "./CommandMenu/internal/CommandGroup": { + "import": "./src/CommandMenu/internal/CommandGroup.tsx", + "types": "./src/CommandMenu/internal/CommandGroup.tsx" + }, + "./CommandMenu/internal/CommandSection": { + "import": "./src/CommandMenu/internal/CommandSection.tsx", + "types": "./src/CommandMenu/internal/CommandSection.tsx" + }, + "./CommandMenu/internal/Context": { + "import": "./src/CommandMenu/internal/Context.tsx", + "types": "./src/CommandMenu/internal/Context.tsx" + }, + "./CommandMenu/internal/state/commandsState": { + "import": "./src/CommandMenu/internal/state/commandsState.ts", + "types": "./src/CommandMenu/internal/state/commandsState.ts" + }, + "./CommandMenu/internal/state/commandsStates.test": { + "import": "./src/CommandMenu/internal/state/commandsStates.test.ts", + "types": "./src/CommandMenu/internal/state/commandsStates.test.ts" + }, + "./CommandMenu/internal/state/pagesState.test": { + "import": "./src/CommandMenu/internal/state/pagesState.test.ts", + "types": "./src/CommandMenu/internal/state/pagesState.test.ts" + }, + "./CommandMenu/internal/state/pagesState": { + "import": "./src/CommandMenu/internal/state/pagesState.ts", + "types": "./src/CommandMenu/internal/state/pagesState.ts" + }, + "./CommandMenu/internal/state/queryState": { + "import": "./src/CommandMenu/internal/state/queryState.ts", + "types": "./src/CommandMenu/internal/state/queryState.ts" + }, + "./CommandMenu/internal/state/viewState": { + "import": "./src/CommandMenu/internal/state/viewState.ts", + "types": "./src/CommandMenu/internal/state/viewState.ts" + }, + "./CommandMenu/internal/state/viewState.types": { + "import": "./src/CommandMenu/internal/state/viewState.types.ts", + "types": "./src/CommandMenu/internal/state/viewState.types.ts" + }, + "./CommandMenu/internal/types": { + "import": "./src/CommandMenu/internal/types.ts", + "types": "./src/CommandMenu/internal/types.ts" + }, + "./CommandMenu/prepackaged/Changelog": { + "import": "./src/CommandMenu/prepackaged/Changelog.tsx", + "types": "./src/CommandMenu/prepackaged/Changelog.tsx" + }, + "./CommandMenu/prepackaged/DocsAi/DocsAiPage": { + "import": "./src/CommandMenu/prepackaged/DocsAi/DocsAiPage.tsx", + "types": "./src/CommandMenu/prepackaged/DocsAi/DocsAiPage.tsx" + }, + "./CommandMenu/prepackaged/DocsAi": { + "import": "./src/CommandMenu/prepackaged/DocsAi/index.tsx", + "types": "./src/CommandMenu/prepackaged/DocsAi/index.tsx" + }, + "./CommandMenu/prepackaged/DocsSearch/DocsSearchPage": { + "import": "./src/CommandMenu/prepackaged/DocsSearch/DocsSearchPage.tsx", + "types": "./src/CommandMenu/prepackaged/DocsSearch/DocsSearchPage.tsx" + }, + "./CommandMenu/prepackaged/DocsSearch": { + "import": "./src/CommandMenu/prepackaged/DocsSearch/index.tsx", + "types": "./src/CommandMenu/prepackaged/DocsSearch/index.tsx" + }, + "./CommandMenu/prepackaged/Support": { + "import": "./src/CommandMenu/prepackaged/Support.tsx", + "types": "./src/CommandMenu/prepackaged/Support.tsx" + }, + "./CommandMenu/prepackaged/ThemeSwitcher": { + "import": "./src/CommandMenu/prepackaged/ThemeSwitcher.tsx", + "types": "./src/CommandMenu/prepackaged/ThemeSwitcher.tsx" + }, + "./CommandMenu/prepackaged/ai/AiWarning": { + "import": "./src/CommandMenu/prepackaged/ai/AiWarning.tsx", + "types": "./src/CommandMenu/prepackaged/ai/AiWarning.tsx" + }, + "./CommandMenu/prepackaged/ai": { + "import": "./src/CommandMenu/prepackaged/ai/index.tsx", + "types": "./src/CommandMenu/prepackaged/ai/index.tsx" + }, + "./CommandMenu/prepackaged/ai/queryAi": { + "import": "./src/CommandMenu/prepackaged/ai/queryAi.ts", + "types": "./src/CommandMenu/prepackaged/ai/queryAi.ts" + }, + "./CommandMenu/prepackaged/ai/useAiChat": { + "import": "./src/CommandMenu/prepackaged/ai/useAiChat.ts", + "types": "./src/CommandMenu/prepackaged/ai/useAiChat.ts" + }, + "./CommandMenu/prepackaged/ai/utils": { + "import": "./src/CommandMenu/prepackaged/ai/utils.ts", + "types": "./src/CommandMenu/prepackaged/ai/utils.ts" + }, + "./CommandMenu/prepackaged/shared/constants": { + "import": "./src/CommandMenu/prepackaged/shared/constants.ts", + "types": "./src/CommandMenu/prepackaged/shared/constants.ts" + }, + "./ComplexTabs": { + "import": "./src/ComplexTabs/index.tsx", + "types": "./src/ComplexTabs/index.tsx" + }, + "./ComplexTabs/withQueryParams": { + "import": "./src/ComplexTabs/withQueryParams.tsx", + "types": "./src/ComplexTabs/withQueryParams.tsx" + }, + "./ComplexTabs/withSticky": { + "import": "./src/ComplexTabs/withSticky.tsx", + "types": "./src/ComplexTabs/withSticky.tsx" + }, + "./ComplexTabs/withSticky.utils": { + "import": "./src/ComplexTabs/withSticky.utils.ts", + "types": "./src/ComplexTabs/withSticky.utils.ts" + }, + "./ComputeBadge": { + "import": "./src/ComputeBadge/index.tsx", + "types": "./src/ComputeBadge/index.tsx" + }, + "./ConsentToast": { + "import": "./src/ConsentToast/index.tsx", + "types": "./src/ConsentToast/index.tsx" + }, + "./CountdownWidget/CountdownStep": { + "import": "./src/CountdownWidget/CountdownStep.tsx", + "types": "./src/CountdownWidget/CountdownStep.tsx" + }, + "./CountdownWidget": { + "import": "./src/CountdownWidget/index.tsx", + "types": "./src/CountdownWidget/index.tsx" + }, + "./DataInputs/Input": { + "import": "./src/DataInputs/Input.tsx", + "types": "./src/DataInputs/Input.tsx" + }, + "./Dialogs/ConfirmDialog": { + "import": "./src/Dialogs/ConfirmDialog.tsx", + "types": "./src/Dialogs/ConfirmDialog.tsx" + }, + "./Dialogs/ConfirmationModal": { + "import": "./src/Dialogs/ConfirmationModal.tsx", + "types": "./src/Dialogs/ConfirmationModal.tsx" + }, + "./Dialogs/TextConfirmModal": { + "import": "./src/Dialogs/TextConfirmModal.tsx", + "types": "./src/Dialogs/TextConfirmModal.tsx" + }, + "./ExpandableVideo": { + "import": "./src/ExpandableVideo/index.tsx", + "types": "./src/ExpandableVideo/index.tsx" + }, + "./FilterBar/FilterBar": { + "import": "./src/FilterBar/FilterBar.tsx", + "types": "./src/FilterBar/FilterBar.tsx" + }, + "./FilterBar/FilterCondition": { + "import": "./src/FilterBar/FilterCondition.tsx", + "types": "./src/FilterBar/FilterCondition.tsx" + }, + "./FilterBar/FilterGroup": { + "import": "./src/FilterBar/FilterGroup.tsx", + "types": "./src/FilterBar/FilterGroup.tsx" + }, + "./FilterBar": { + "import": "./src/FilterBar/index.ts", + "types": "./src/FilterBar/index.ts" + }, + "./FilterBar/types": { + "import": "./src/FilterBar/types.ts", + "types": "./src/FilterBar/types.ts" + }, + "./GlassPanel": { + "import": "./src/GlassPanel/index.tsx", + "types": "./src/GlassPanel/index.tsx" + }, + "./IconPanel": { + "import": "./src/IconPanel/index.tsx", + "types": "./src/IconPanel/index.tsx" + }, + "./InnerSideMenu": { + "import": "./src/InnerSideMenu/index.tsx", + "types": "./src/InnerSideMenu/index.tsx" + }, + "./LogsBarChart": { + "import": "./src/LogsBarChart/index.tsx", + "types": "./src/LogsBarChart/index.tsx" + }, + "./MobileSheetNav/MobileSheetNav": { + "import": "./src/MobileSheetNav/MobileSheetNav.tsx", + "types": "./src/MobileSheetNav/MobileSheetNav.tsx" + }, + "./MobileSheetNav": { + "import": "./src/MobileSheetNav/index.ts", + "types": "./src/MobileSheetNav/index.ts" + }, + "./MultiSelectDeprecated/Badges": { + "import": "./src/MultiSelectDeprecated/Badges.tsx", + "types": "./src/MultiSelectDeprecated/Badges.tsx" + }, + "./MultiSelectDeprecated/MultiSelectV2": { + "import": "./src/MultiSelectDeprecated/MultiSelectV2.tsx", + "types": "./src/MultiSelectDeprecated/MultiSelectV2.tsx" + }, + "./MultiSelectDeprecated": { + "import": "./src/MultiSelectDeprecated/index.tsx", + "types": "./src/MultiSelectDeprecated/index.tsx" + }, + "./PrivacySettings": { + "import": "./src/PrivacySettings/index.tsx", + "types": "./src/PrivacySettings/index.tsx" + }, + "./PromoToast/PromoBg": { + "import": "./src/PromoToast/PromoBg.tsx", + "types": "./src/PromoToast/PromoBg.tsx" + }, + "./PromoToast/PromoToast": { + "import": "./src/PromoToast/PromoToast.tsx", + "types": "./src/PromoToast/PromoToast.tsx" + }, + "./PromoToast": { + "import": "./src/PromoToast/index.ts", + "types": "./src/PromoToast/index.ts" + }, + "./ShimmeringLoader": { + "import": "./src/ShimmeringLoader/index.tsx", + "types": "./src/ShimmeringLoader/index.tsx" + }, + "./SqlToRest/assumptions": { + "import": "./src/SqlToRest/assumptions.ts", + "types": "./src/SqlToRest/assumptions.ts" + }, + "./SqlToRest/base-url-dialog": { + "import": "./src/SqlToRest/base-url-dialog.tsx", + "types": "./src/SqlToRest/base-url-dialog.tsx" + }, + "./SqlToRest/faqs": { + "import": "./src/SqlToRest/faqs.ts", + "types": "./src/SqlToRest/faqs.ts" + }, + "./SqlToRest": { + "import": "./src/SqlToRest/index.tsx", + "types": "./src/SqlToRest/index.tsx" + }, + "./SqlToRest/sql-to-rest": { + "import": "./src/SqlToRest/sql-to-rest.tsx", + "types": "./src/SqlToRest/sql-to-rest.tsx" + }, + "./SqlToRest/syntax-highlighter/framer-features": { + "import": "./src/SqlToRest/syntax-highlighter/framer-features.ts", + "types": "./src/SqlToRest/syntax-highlighter/framer-features.ts" + }, + "./SqlToRest/syntax-highlighter/transform-renderer": { + "import": "./src/SqlToRest/syntax-highlighter/transform-renderer.tsx", + "types": "./src/SqlToRest/syntax-highlighter/transform-renderer.tsx" + }, + "./SqlToRest/util": { + "import": "./src/SqlToRest/util.ts", + "types": "./src/SqlToRest/util.ts" + }, + "./ThemeToggle": { + "import": "./src/ThemeToggle.tsx", + "types": "./src/ThemeToggle.tsx" + }, + "./TimestampInfo": { + "import": "./src/TimestampInfo/index.tsx", + "types": "./src/TimestampInfo/index.tsx" + }, + "./Toc": { + "import": "./src/Toc/index.ts", + "types": "./src/Toc/index.ts" + }, + "./Toc/mdx-plugins/remark-heading": { + "import": "./src/Toc/mdx-plugins/remark-heading.ts", + "types": "./src/Toc/mdx-plugins/remark-heading.ts" + }, + "./Toc/mdx-plugins/remark-utils": { + "import": "./src/Toc/mdx-plugins/remark-utils.ts", + "types": "./src/Toc/mdx-plugins/remark-utils.ts" + }, + "./Toc/server/get-toc": { + "import": "./src/Toc/server/get-toc.tsx", + "types": "./src/Toc/server/get-toc.tsx" + }, + "./Toc/toc-primitive": { + "import": "./src/Toc/toc-primitive.tsx", + "types": "./src/Toc/toc-primitive.tsx" + }, + "./Toc/toc-thumb": { + "import": "./src/Toc/toc-thumb.tsx", + "types": "./src/Toc/toc-thumb.tsx" + }, + "./Toc/toc": { + "import": "./src/Toc/toc.tsx", + "types": "./src/Toc/toc.tsx" + }, + "./Toc/types": { + "import": "./src/Toc/types.ts", + "types": "./src/Toc/types.ts" + }, + "./TweetCard": { + "import": "./src/TweetCard/index.tsx", + "types": "./src/TweetCard/index.tsx" + }, + "./admonition": { + "import": "./src/admonition.tsx", + "types": "./src/admonition.tsx" + }, + "./consent": { + "import": "./src/consent.tsx", + "types": "./src/consent.tsx" + }, + "./form/FormItemLayout/FormItemLayout": { + "import": "./src/form/FormItemLayout/FormItemLayout.tsx", + "types": "./src/form/FormItemLayout/FormItemLayout.tsx" + }, + "./form/FormLayout2": { + "import": "./src/form/FormLayout2.tsx", + "types": "./src/form/FormLayout2.tsx" + }, + "./form/Layout/FormLayout": { + "import": "./src/form/Layout/FormLayout.tsx", + "types": "./src/form/Layout/FormLayout.tsx" + }, + "./form/Layout/InputIconContainer": { + "import": "./src/form/Layout/InputIconContainer.tsx", + "types": "./src/form/Layout/InputIconContainer.tsx" + }, + "./form/utils": { + "import": "./src/form/utils.tsx", + "types": "./src/form/utils.tsx" + }, + "./info-tooltip": { + "import": "./src/info-tooltip.tsx", + "types": "./src/info-tooltip.tsx" + }, + "./multi-select": { + "import": "./src/multi-select/index.tsx", + "types": "./src/multi-select/index.tsx" + }, + "./multi-select/multi-select.test": { + "import": "./src/multi-select/multi-select.test.tsx", + "types": "./src/multi-select/multi-select.test.tsx" + }, + "./multi-select/multi-select": { + "import": "./src/multi-select/multi-select.tsx", + "types": "./src/multi-select/multi-select.tsx" + }, + "./types/assets.d": { + "import": "./src/types/assets.d.ts", + "types": "./src/types/assets.d.ts" + } }, "dependencies": { "@hookform/resolvers": "^3.1.1", "@monaco-editor/react": "^4.6.0", "@supabase/sql-to-rest": "^0.1.6", "@supabase/supabase-js": "catalog:", + "@vitest/coverage-v8": "^3.0.9", "class-variance-authority": "^0.6.0", "clsx": "^1.2.1", "cmdk": "^1.0.0", @@ -67,6 +537,7 @@ "api-types": "workspace:*", "next-router-mock": "^0.9.13", "typescript": "~5.5.0", + "tsx": "^4.19.3", "unified": "^11.0.5", "vfile": "^6.0.3", "vite": "^6.2.6", diff --git a/packages/ui-patterns/scripts/update-exports.ts b/packages/ui-patterns/scripts/update-exports.ts new file mode 100644 index 00000000000..88111ea4c30 --- /dev/null +++ b/packages/ui-patterns/scripts/update-exports.ts @@ -0,0 +1,64 @@ +import * as fs from 'node:fs' +import * as path from 'node:path' + +const SRC_DIR = path.resolve(__dirname, '..', 'src') + +interface ExportMap { + [key: string]: { + import: string + types: string + } +} + +function getAllSourceFiles(dir: string): ExportMap { + const entries = fs.readdirSync(dir, { withFileTypes: true }) + const exportsMap: ExportMap = {} + + for (const entry of entries) { + const fullPath = path.join(dir, entry.name) + + if (entry.isDirectory()) { + Object.assign(exportsMap, getAllSourceFiles(fullPath)) + } else if (entry.isFile() && /\.(ts|tsx)$/.test(entry.name)) { + const relativePath = path.relative(SRC_DIR, fullPath) + const noExtension = relativePath.replace(/\.(ts|tsx)$/, '') + const segments = noExtension.split(path.sep) + + // If filename is "index", remove it from the export path + const isIndex = segments[segments.length - 1] === 'index' + const exportSegments = isIndex ? segments.slice(0, -1) : segments + + const subpath = `./${exportSegments.join('/')}` // clean export + const filePath = `./src/${relativePath.replace(/\\/g, '/')}` + + exportsMap[subpath] = { + import: filePath, + types: filePath, + } + } + } + + return exportsMap +} + +function updatePackageJson(exportsMap: ExportMap): void { + const packageJsonPath = path.resolve(__dirname, '..', 'package.json') + const packageJsonRaw = fs.readFileSync(packageJsonPath, 'utf8') + const packageJson = JSON.parse(packageJsonRaw) + + packageJson.exports = { + './package.json': './package.json', + '.': { + import: './index.tsx', + types: './index.tsx', + }, + ...exportsMap, + } + + fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)) + console.log('✅ package.json exports updated (with clean index paths).') +} + +// Run the export generation +const exportsMap = getAllSourceFiles(SRC_DIR) +updatePackageJson(exportsMap) diff --git a/packages/ui-patterns/AssistantChat/AssistantChatForm.tsx b/packages/ui-patterns/src/AssistantChat/AssistantChatForm.tsx similarity index 100% rename from packages/ui-patterns/AssistantChat/AssistantChatForm.tsx rename to packages/ui-patterns/src/AssistantChat/AssistantChatForm.tsx diff --git a/packages/ui-patterns/AssistantChat/AssistantCommandsPopover.tsx b/packages/ui-patterns/src/AssistantChat/AssistantCommandsPopover.tsx similarity index 97% rename from packages/ui-patterns/AssistantChat/AssistantCommandsPopover.tsx rename to packages/ui-patterns/src/AssistantChat/AssistantCommandsPopover.tsx index ffe1996abe2..01656d475ca 100644 --- a/packages/ui-patterns/AssistantChat/AssistantCommandsPopover.tsx +++ b/packages/ui-patterns/src/AssistantChat/AssistantCommandsPopover.tsx @@ -16,9 +16,9 @@ import { CommandItem, CommandList, CommandSeparator, -} from './../../ui/src/components/shadcn/ui/command' -import { Popover, PopoverAnchor, PopoverContent } from './../../ui/src/components/shadcn/ui/popover' -import { cn } from './../../ui/src/lib/utils/cn' +} from 'ui/src/components/shadcn/ui/command' +import { Popover, PopoverAnchor, PopoverContent } from 'ui/src/components/shadcn/ui/popover' +import { cn } from 'ui/src/lib/utils/cn' const AssistantCommandsPopover = forwardRef< ElementRef, diff --git a/packages/ui-patterns/AssistantChat/index.ts b/packages/ui-patterns/src/AssistantChat/index.ts similarity index 100% rename from packages/ui-patterns/AssistantChat/index.ts rename to packages/ui-patterns/src/AssistantChat/index.ts diff --git a/packages/ui-patterns/AuthenticatedDropdownMenu/index.tsx b/packages/ui-patterns/src/AuthenticatedDropdownMenu/index.tsx similarity index 100% rename from packages/ui-patterns/AuthenticatedDropdownMenu/index.tsx rename to packages/ui-patterns/src/AuthenticatedDropdownMenu/index.tsx diff --git a/packages/ui-patterns/Banners/Countdown.tsx b/packages/ui-patterns/src/Banners/Countdown.tsx similarity index 100% rename from packages/ui-patterns/Banners/Countdown.tsx rename to packages/ui-patterns/src/Banners/Countdown.tsx diff --git a/packages/ui-patterns/Banners/LW14Announcement.tsx b/packages/ui-patterns/src/Banners/LW14Announcement.tsx similarity index 100% rename from packages/ui-patterns/Banners/LW14Announcement.tsx rename to packages/ui-patterns/src/Banners/LW14Announcement.tsx diff --git a/packages/ui-patterns/Banners/LW14Banner.tsx b/packages/ui-patterns/src/Banners/LW14Banner.tsx similarity index 100% rename from packages/ui-patterns/Banners/LW14Banner.tsx rename to packages/ui-patterns/src/Banners/LW14Banner.tsx diff --git a/packages/ui-patterns/Banners/data.json b/packages/ui-patterns/src/Banners/data.json similarity index 100% rename from packages/ui-patterns/Banners/data.json rename to packages/ui-patterns/src/Banners/data.json diff --git a/packages/ui-patterns/CommandMenu/api/Badges.tsx b/packages/ui-patterns/src/CommandMenu/api/Badges.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/api/Badges.tsx rename to packages/ui-patterns/src/CommandMenu/api/Badges.tsx diff --git a/packages/ui-patterns/CommandMenu/api/CommandHeader.tsx b/packages/ui-patterns/src/CommandMenu/api/CommandHeader.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/api/CommandHeader.tsx rename to packages/ui-patterns/src/CommandMenu/api/CommandHeader.tsx diff --git a/packages/ui-patterns/CommandMenu/api/CommandInput.tsx b/packages/ui-patterns/src/CommandMenu/api/CommandInput.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/api/CommandInput.tsx rename to packages/ui-patterns/src/CommandMenu/api/CommandInput.tsx diff --git a/packages/ui-patterns/CommandMenu/api/CommandList.tsx b/packages/ui-patterns/src/CommandMenu/api/CommandList.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/api/CommandList.tsx rename to packages/ui-patterns/src/CommandMenu/api/CommandList.tsx diff --git a/packages/ui-patterns/CommandMenu/api/CommandMenu.tsx b/packages/ui-patterns/src/CommandMenu/api/CommandMenu.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/api/CommandMenu.tsx rename to packages/ui-patterns/src/CommandMenu/api/CommandMenu.tsx diff --git a/packages/ui-patterns/CommandMenu/api/CommandProvider.tsx b/packages/ui-patterns/src/CommandMenu/api/CommandProvider.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/api/CommandProvider.tsx rename to packages/ui-patterns/src/CommandMenu/api/CommandProvider.tsx diff --git a/packages/ui-patterns/CommandMenu/api/TextHighlighter.tsx b/packages/ui-patterns/src/CommandMenu/api/TextHighlighter.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/api/TextHighlighter.tsx rename to packages/ui-patterns/src/CommandMenu/api/TextHighlighter.tsx diff --git a/packages/ui-patterns/CommandMenu/api/hooks/commandsHooks.test.tsx b/packages/ui-patterns/src/CommandMenu/api/hooks/commandsHooks.test.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/api/hooks/commandsHooks.test.tsx rename to packages/ui-patterns/src/CommandMenu/api/hooks/commandsHooks.test.tsx diff --git a/packages/ui-patterns/CommandMenu/api/hooks/commandsHooks.ts b/packages/ui-patterns/src/CommandMenu/api/hooks/commandsHooks.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/api/hooks/commandsHooks.ts rename to packages/ui-patterns/src/CommandMenu/api/hooks/commandsHooks.ts diff --git a/packages/ui-patterns/CommandMenu/api/hooks/pagesHooks.test.tsx b/packages/ui-patterns/src/CommandMenu/api/hooks/pagesHooks.test.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/api/hooks/pagesHooks.test.tsx rename to packages/ui-patterns/src/CommandMenu/api/hooks/pagesHooks.test.tsx diff --git a/packages/ui-patterns/CommandMenu/api/hooks/pagesHooks.ts b/packages/ui-patterns/src/CommandMenu/api/hooks/pagesHooks.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/api/hooks/pagesHooks.ts rename to packages/ui-patterns/src/CommandMenu/api/hooks/pagesHooks.ts diff --git a/packages/ui-patterns/CommandMenu/api/hooks/queryHooks.ts b/packages/ui-patterns/src/CommandMenu/api/hooks/queryHooks.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/api/hooks/queryHooks.ts rename to packages/ui-patterns/src/CommandMenu/api/hooks/queryHooks.ts diff --git a/packages/ui-patterns/CommandMenu/api/hooks/useCommandFilterState.ts b/packages/ui-patterns/src/CommandMenu/api/hooks/useCommandFilterState.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/api/hooks/useCommandFilterState.ts rename to packages/ui-patterns/src/CommandMenu/api/hooks/useCommandFilterState.ts diff --git a/packages/ui-patterns/CommandMenu/api/hooks/useCrossCompatRouter.tsx b/packages/ui-patterns/src/CommandMenu/api/hooks/useCrossCompatRouter.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/api/hooks/useCrossCompatRouter.tsx rename to packages/ui-patterns/src/CommandMenu/api/hooks/useCrossCompatRouter.tsx diff --git a/packages/ui-patterns/CommandMenu/api/hooks/useHistoryKeys.ts b/packages/ui-patterns/src/CommandMenu/api/hooks/useHistoryKeys.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/api/hooks/useHistoryKeys.ts rename to packages/ui-patterns/src/CommandMenu/api/hooks/useHistoryKeys.ts diff --git a/packages/ui-patterns/CommandMenu/api/hooks/viewHooks.ts b/packages/ui-patterns/src/CommandMenu/api/hooks/viewHooks.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/api/hooks/viewHooks.ts rename to packages/ui-patterns/src/CommandMenu/api/hooks/viewHooks.ts diff --git a/packages/ui-patterns/CommandMenu/api/types.ts b/packages/ui-patterns/src/CommandMenu/api/types.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/api/types.ts rename to packages/ui-patterns/src/CommandMenu/api/types.ts diff --git a/packages/ui-patterns/CommandMenu/api/utils.ts b/packages/ui-patterns/src/CommandMenu/api/utils.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/api/utils.ts rename to packages/ui-patterns/src/CommandMenu/api/utils.ts diff --git a/packages/ui-patterns/CommandMenu/index.tsx b/packages/ui-patterns/src/CommandMenu/index.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/index.tsx rename to packages/ui-patterns/src/CommandMenu/index.tsx diff --git a/packages/ui-patterns/CommandMenu/internal/Command.tsx b/packages/ui-patterns/src/CommandMenu/internal/Command.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/internal/Command.tsx rename to packages/ui-patterns/src/CommandMenu/internal/Command.tsx diff --git a/packages/ui-patterns/CommandMenu/internal/CommandEmpty.tsx b/packages/ui-patterns/src/CommandMenu/internal/CommandEmpty.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/internal/CommandEmpty.tsx rename to packages/ui-patterns/src/CommandMenu/internal/CommandEmpty.tsx diff --git a/packages/ui-patterns/CommandMenu/internal/CommandGroup.tsx b/packages/ui-patterns/src/CommandMenu/internal/CommandGroup.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/internal/CommandGroup.tsx rename to packages/ui-patterns/src/CommandMenu/internal/CommandGroup.tsx diff --git a/packages/ui-patterns/CommandMenu/internal/CommandSection.tsx b/packages/ui-patterns/src/CommandMenu/internal/CommandSection.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/internal/CommandSection.tsx rename to packages/ui-patterns/src/CommandMenu/internal/CommandSection.tsx diff --git a/packages/ui-patterns/CommandMenu/internal/Context.tsx b/packages/ui-patterns/src/CommandMenu/internal/Context.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/internal/Context.tsx rename to packages/ui-patterns/src/CommandMenu/internal/Context.tsx diff --git a/packages/ui-patterns/CommandMenu/internal/state/commandsState.ts b/packages/ui-patterns/src/CommandMenu/internal/state/commandsState.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/internal/state/commandsState.ts rename to packages/ui-patterns/src/CommandMenu/internal/state/commandsState.ts diff --git a/packages/ui-patterns/CommandMenu/internal/state/commandsStates.test.ts b/packages/ui-patterns/src/CommandMenu/internal/state/commandsStates.test.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/internal/state/commandsStates.test.ts rename to packages/ui-patterns/src/CommandMenu/internal/state/commandsStates.test.ts diff --git a/packages/ui-patterns/CommandMenu/internal/state/pagesState.test.ts b/packages/ui-patterns/src/CommandMenu/internal/state/pagesState.test.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/internal/state/pagesState.test.ts rename to packages/ui-patterns/src/CommandMenu/internal/state/pagesState.test.ts diff --git a/packages/ui-patterns/CommandMenu/internal/state/pagesState.ts b/packages/ui-patterns/src/CommandMenu/internal/state/pagesState.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/internal/state/pagesState.ts rename to packages/ui-patterns/src/CommandMenu/internal/state/pagesState.ts diff --git a/packages/ui-patterns/CommandMenu/internal/state/queryState.ts b/packages/ui-patterns/src/CommandMenu/internal/state/queryState.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/internal/state/queryState.ts rename to packages/ui-patterns/src/CommandMenu/internal/state/queryState.ts diff --git a/packages/ui-patterns/CommandMenu/internal/state/viewState.ts b/packages/ui-patterns/src/CommandMenu/internal/state/viewState.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/internal/state/viewState.ts rename to packages/ui-patterns/src/CommandMenu/internal/state/viewState.ts diff --git a/packages/ui-patterns/CommandMenu/internal/state/viewState.types.ts b/packages/ui-patterns/src/CommandMenu/internal/state/viewState.types.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/internal/state/viewState.types.ts rename to packages/ui-patterns/src/CommandMenu/internal/state/viewState.types.ts diff --git a/packages/ui-patterns/CommandMenu/internal/types.ts b/packages/ui-patterns/src/CommandMenu/internal/types.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/internal/types.ts rename to packages/ui-patterns/src/CommandMenu/internal/types.ts diff --git a/packages/ui-patterns/CommandMenu/prepackaged/Changelog.tsx b/packages/ui-patterns/src/CommandMenu/prepackaged/Changelog.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/prepackaged/Changelog.tsx rename to packages/ui-patterns/src/CommandMenu/prepackaged/Changelog.tsx diff --git a/packages/ui-patterns/CommandMenu/prepackaged/DocsAi/DocsAiPage.tsx b/packages/ui-patterns/src/CommandMenu/prepackaged/DocsAi/DocsAiPage.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/prepackaged/DocsAi/DocsAiPage.tsx rename to packages/ui-patterns/src/CommandMenu/prepackaged/DocsAi/DocsAiPage.tsx diff --git a/packages/ui-patterns/CommandMenu/prepackaged/DocsAi/index.tsx b/packages/ui-patterns/src/CommandMenu/prepackaged/DocsAi/index.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/prepackaged/DocsAi/index.tsx rename to packages/ui-patterns/src/CommandMenu/prepackaged/DocsAi/index.tsx diff --git a/packages/ui-patterns/CommandMenu/prepackaged/DocsSearch/DocsSearchPage.tsx b/packages/ui-patterns/src/CommandMenu/prepackaged/DocsSearch/DocsSearchPage.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/prepackaged/DocsSearch/DocsSearchPage.tsx rename to packages/ui-patterns/src/CommandMenu/prepackaged/DocsSearch/DocsSearchPage.tsx diff --git a/packages/ui-patterns/CommandMenu/prepackaged/DocsSearch/index.tsx b/packages/ui-patterns/src/CommandMenu/prepackaged/DocsSearch/index.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/prepackaged/DocsSearch/index.tsx rename to packages/ui-patterns/src/CommandMenu/prepackaged/DocsSearch/index.tsx diff --git a/packages/ui-patterns/CommandMenu/prepackaged/Support.tsx b/packages/ui-patterns/src/CommandMenu/prepackaged/Support.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/prepackaged/Support.tsx rename to packages/ui-patterns/src/CommandMenu/prepackaged/Support.tsx diff --git a/packages/ui-patterns/CommandMenu/prepackaged/ThemeSwitcher.tsx b/packages/ui-patterns/src/CommandMenu/prepackaged/ThemeSwitcher.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/prepackaged/ThemeSwitcher.tsx rename to packages/ui-patterns/src/CommandMenu/prepackaged/ThemeSwitcher.tsx diff --git a/packages/ui-patterns/CommandMenu/prepackaged/ai/AiWarning.tsx b/packages/ui-patterns/src/CommandMenu/prepackaged/ai/AiWarning.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/prepackaged/ai/AiWarning.tsx rename to packages/ui-patterns/src/CommandMenu/prepackaged/ai/AiWarning.tsx diff --git a/packages/ui-patterns/CommandMenu/prepackaged/ai/index.tsx b/packages/ui-patterns/src/CommandMenu/prepackaged/ai/index.tsx similarity index 100% rename from packages/ui-patterns/CommandMenu/prepackaged/ai/index.tsx rename to packages/ui-patterns/src/CommandMenu/prepackaged/ai/index.tsx diff --git a/packages/ui-patterns/CommandMenu/prepackaged/ai/queryAi.ts b/packages/ui-patterns/src/CommandMenu/prepackaged/ai/queryAi.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/prepackaged/ai/queryAi.ts rename to packages/ui-patterns/src/CommandMenu/prepackaged/ai/queryAi.ts diff --git a/packages/ui-patterns/CommandMenu/prepackaged/ai/useAiChat.ts b/packages/ui-patterns/src/CommandMenu/prepackaged/ai/useAiChat.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/prepackaged/ai/useAiChat.ts rename to packages/ui-patterns/src/CommandMenu/prepackaged/ai/useAiChat.ts diff --git a/packages/ui-patterns/CommandMenu/prepackaged/ai/utils.ts b/packages/ui-patterns/src/CommandMenu/prepackaged/ai/utils.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/prepackaged/ai/utils.ts rename to packages/ui-patterns/src/CommandMenu/prepackaged/ai/utils.ts diff --git a/packages/ui-patterns/CommandMenu/prepackaged/shared/constants.ts b/packages/ui-patterns/src/CommandMenu/prepackaged/shared/constants.ts similarity index 100% rename from packages/ui-patterns/CommandMenu/prepackaged/shared/constants.ts rename to packages/ui-patterns/src/CommandMenu/prepackaged/shared/constants.ts diff --git a/packages/ui-patterns/ComplexTabs/index.tsx b/packages/ui-patterns/src/ComplexTabs/index.tsx similarity index 100% rename from packages/ui-patterns/ComplexTabs/index.tsx rename to packages/ui-patterns/src/ComplexTabs/index.tsx diff --git a/packages/ui-patterns/ComplexTabs/withQueryParams.tsx b/packages/ui-patterns/src/ComplexTabs/withQueryParams.tsx similarity index 99% rename from packages/ui-patterns/ComplexTabs/withQueryParams.tsx rename to packages/ui-patterns/src/ComplexTabs/withQueryParams.tsx index d4d2434aaa5..f2e0ce89c4b 100644 --- a/packages/ui-patterns/ComplexTabs/withQueryParams.tsx +++ b/packages/ui-patterns/src/ComplexTabs/withQueryParams.tsx @@ -1,3 +1,5 @@ +'use client' + import { useSearchParamsShallow } from 'common' import { xor } from 'lodash' import { Children, useEffect, useRef, type FC, type PropsWithChildren } from 'react' diff --git a/packages/ui-patterns/ComplexTabs/withSticky.tsx b/packages/ui-patterns/src/ComplexTabs/withSticky.tsx similarity index 99% rename from packages/ui-patterns/ComplexTabs/withSticky.tsx rename to packages/ui-patterns/src/ComplexTabs/withSticky.tsx index 31d2c9c1ce8..c97d051fb98 100644 --- a/packages/ui-patterns/ComplexTabs/withSticky.tsx +++ b/packages/ui-patterns/src/ComplexTabs/withSticky.tsx @@ -1,3 +1,5 @@ +'use client' + import { type TabsProps } from 'ui/src/components/Tabs' import { useSticky } from './withSticky.utils' import { PropsWithChildren, useCallback, type FC } from 'react' diff --git a/packages/ui-patterns/ComplexTabs/withSticky.utils.ts b/packages/ui-patterns/src/ComplexTabs/withSticky.utils.ts similarity index 100% rename from packages/ui-patterns/ComplexTabs/withSticky.utils.ts rename to packages/ui-patterns/src/ComplexTabs/withSticky.utils.ts diff --git a/packages/ui-patterns/ComputeBadge/index.tsx b/packages/ui-patterns/src/ComputeBadge/index.tsx similarity index 100% rename from packages/ui-patterns/ComputeBadge/index.tsx rename to packages/ui-patterns/src/ComputeBadge/index.tsx diff --git a/packages/ui-patterns/ConsentToast/index.tsx b/packages/ui-patterns/src/ConsentToast/index.tsx similarity index 100% rename from packages/ui-patterns/ConsentToast/index.tsx rename to packages/ui-patterns/src/ConsentToast/index.tsx diff --git a/packages/ui-patterns/CountdownWidget/CountdownStep.tsx b/packages/ui-patterns/src/CountdownWidget/CountdownStep.tsx similarity index 100% rename from packages/ui-patterns/CountdownWidget/CountdownStep.tsx rename to packages/ui-patterns/src/CountdownWidget/CountdownStep.tsx diff --git a/packages/ui-patterns/CountdownWidget/index.tsx b/packages/ui-patterns/src/CountdownWidget/index.tsx similarity index 100% rename from packages/ui-patterns/CountdownWidget/index.tsx rename to packages/ui-patterns/src/CountdownWidget/index.tsx diff --git a/packages/ui-patterns/DataInputs/Input.tsx b/packages/ui-patterns/src/DataInputs/Input.tsx similarity index 100% rename from packages/ui-patterns/DataInputs/Input.tsx rename to packages/ui-patterns/src/DataInputs/Input.tsx diff --git a/packages/ui-patterns/Dialogs/ConfirmDialog.tsx b/packages/ui-patterns/src/Dialogs/ConfirmDialog.tsx similarity index 100% rename from packages/ui-patterns/Dialogs/ConfirmDialog.tsx rename to packages/ui-patterns/src/Dialogs/ConfirmDialog.tsx diff --git a/packages/ui-patterns/Dialogs/ConfirmationModal.tsx b/packages/ui-patterns/src/Dialogs/ConfirmationModal.tsx similarity index 100% rename from packages/ui-patterns/Dialogs/ConfirmationModal.tsx rename to packages/ui-patterns/src/Dialogs/ConfirmationModal.tsx diff --git a/packages/ui-patterns/Dialogs/TextConfirmModal.tsx b/packages/ui-patterns/src/Dialogs/TextConfirmModal.tsx similarity index 100% rename from packages/ui-patterns/Dialogs/TextConfirmModal.tsx rename to packages/ui-patterns/src/Dialogs/TextConfirmModal.tsx diff --git a/packages/ui-patterns/ExpandableVideo/index.tsx b/packages/ui-patterns/src/ExpandableVideo/index.tsx similarity index 100% rename from packages/ui-patterns/ExpandableVideo/index.tsx rename to packages/ui-patterns/src/ExpandableVideo/index.tsx diff --git a/packages/ui-patterns/FilterBar/FilterBar.tsx b/packages/ui-patterns/src/FilterBar/FilterBar.tsx similarity index 100% rename from packages/ui-patterns/FilterBar/FilterBar.tsx rename to packages/ui-patterns/src/FilterBar/FilterBar.tsx diff --git a/packages/ui-patterns/FilterBar/FilterCondition.tsx b/packages/ui-patterns/src/FilterBar/FilterCondition.tsx similarity index 100% rename from packages/ui-patterns/FilterBar/FilterCondition.tsx rename to packages/ui-patterns/src/FilterBar/FilterCondition.tsx diff --git a/packages/ui-patterns/FilterBar/FilterGroup.tsx b/packages/ui-patterns/src/FilterBar/FilterGroup.tsx similarity index 100% rename from packages/ui-patterns/FilterBar/FilterGroup.tsx rename to packages/ui-patterns/src/FilterBar/FilterGroup.tsx diff --git a/packages/ui-patterns/FilterBar/index.ts b/packages/ui-patterns/src/FilterBar/index.ts similarity index 100% rename from packages/ui-patterns/FilterBar/index.ts rename to packages/ui-patterns/src/FilterBar/index.ts diff --git a/packages/ui-patterns/FilterBar/types.ts b/packages/ui-patterns/src/FilterBar/types.ts similarity index 100% rename from packages/ui-patterns/FilterBar/types.ts rename to packages/ui-patterns/src/FilterBar/types.ts diff --git a/packages/ui-patterns/GlassPanel/index.tsx b/packages/ui-patterns/src/GlassPanel/index.tsx similarity index 100% rename from packages/ui-patterns/GlassPanel/index.tsx rename to packages/ui-patterns/src/GlassPanel/index.tsx diff --git a/packages/ui-patterns/IconPanel/index.tsx b/packages/ui-patterns/src/IconPanel/index.tsx similarity index 100% rename from packages/ui-patterns/IconPanel/index.tsx rename to packages/ui-patterns/src/IconPanel/index.tsx diff --git a/packages/ui-patterns/InnerSideMenu/index.tsx b/packages/ui-patterns/src/InnerSideMenu/index.tsx similarity index 100% rename from packages/ui-patterns/InnerSideMenu/index.tsx rename to packages/ui-patterns/src/InnerSideMenu/index.tsx diff --git a/packages/ui-patterns/LogsBarChart/index.tsx b/packages/ui-patterns/src/LogsBarChart/index.tsx similarity index 100% rename from packages/ui-patterns/LogsBarChart/index.tsx rename to packages/ui-patterns/src/LogsBarChart/index.tsx diff --git a/packages/ui-patterns/MobileSheetNav/MobileSheetNav.tsx b/packages/ui-patterns/src/MobileSheetNav/MobileSheetNav.tsx similarity index 100% rename from packages/ui-patterns/MobileSheetNav/MobileSheetNav.tsx rename to packages/ui-patterns/src/MobileSheetNav/MobileSheetNav.tsx diff --git a/packages/ui-patterns/MobileSheetNav/index.ts b/packages/ui-patterns/src/MobileSheetNav/index.ts similarity index 100% rename from packages/ui-patterns/MobileSheetNav/index.ts rename to packages/ui-patterns/src/MobileSheetNav/index.ts diff --git a/packages/ui-patterns/MultiSelectDeprecated/Badges.tsx b/packages/ui-patterns/src/MultiSelectDeprecated/Badges.tsx similarity index 100% rename from packages/ui-patterns/MultiSelectDeprecated/Badges.tsx rename to packages/ui-patterns/src/MultiSelectDeprecated/Badges.tsx diff --git a/packages/ui-patterns/MultiSelectDeprecated/MultiSelectV2.tsx b/packages/ui-patterns/src/MultiSelectDeprecated/MultiSelectV2.tsx similarity index 100% rename from packages/ui-patterns/MultiSelectDeprecated/MultiSelectV2.tsx rename to packages/ui-patterns/src/MultiSelectDeprecated/MultiSelectV2.tsx diff --git a/packages/ui-patterns/MultiSelectDeprecated/index.tsx b/packages/ui-patterns/src/MultiSelectDeprecated/index.tsx similarity index 100% rename from packages/ui-patterns/MultiSelectDeprecated/index.tsx rename to packages/ui-patterns/src/MultiSelectDeprecated/index.tsx diff --git a/packages/ui-patterns/PrivacySettings/index.tsx b/packages/ui-patterns/src/PrivacySettings/index.tsx similarity index 100% rename from packages/ui-patterns/PrivacySettings/index.tsx rename to packages/ui-patterns/src/PrivacySettings/index.tsx diff --git a/packages/ui-patterns/PromoToast/PromoBg.tsx b/packages/ui-patterns/src/PromoToast/PromoBg.tsx similarity index 100% rename from packages/ui-patterns/PromoToast/PromoBg.tsx rename to packages/ui-patterns/src/PromoToast/PromoBg.tsx diff --git a/packages/ui-patterns/PromoToast/PromoToast.tsx b/packages/ui-patterns/src/PromoToast/PromoToast.tsx similarity index 100% rename from packages/ui-patterns/PromoToast/PromoToast.tsx rename to packages/ui-patterns/src/PromoToast/PromoToast.tsx diff --git a/packages/ui-patterns/PromoToast/index.ts b/packages/ui-patterns/src/PromoToast/index.ts similarity index 100% rename from packages/ui-patterns/PromoToast/index.ts rename to packages/ui-patterns/src/PromoToast/index.ts diff --git a/packages/ui-patterns/PromoToast/styles.css b/packages/ui-patterns/src/PromoToast/styles.css similarity index 100% rename from packages/ui-patterns/PromoToast/styles.css rename to packages/ui-patterns/src/PromoToast/styles.css diff --git a/packages/ui-patterns/ShimmeringLoader/index.tsx b/packages/ui-patterns/src/ShimmeringLoader/index.tsx similarity index 100% rename from packages/ui-patterns/ShimmeringLoader/index.tsx rename to packages/ui-patterns/src/ShimmeringLoader/index.tsx diff --git a/packages/ui-patterns/SqlToRest/assumptions.ts b/packages/ui-patterns/src/SqlToRest/assumptions.ts similarity index 100% rename from packages/ui-patterns/SqlToRest/assumptions.ts rename to packages/ui-patterns/src/SqlToRest/assumptions.ts diff --git a/packages/ui-patterns/SqlToRest/base-url-dialog.tsx b/packages/ui-patterns/src/SqlToRest/base-url-dialog.tsx similarity index 100% rename from packages/ui-patterns/SqlToRest/base-url-dialog.tsx rename to packages/ui-patterns/src/SqlToRest/base-url-dialog.tsx diff --git a/packages/ui-patterns/SqlToRest/faqs.ts b/packages/ui-patterns/src/SqlToRest/faqs.ts similarity index 100% rename from packages/ui-patterns/SqlToRest/faqs.ts rename to packages/ui-patterns/src/SqlToRest/faqs.ts diff --git a/packages/ui-patterns/SqlToRest/index.tsx b/packages/ui-patterns/src/SqlToRest/index.tsx similarity index 100% rename from packages/ui-patterns/SqlToRest/index.tsx rename to packages/ui-patterns/src/SqlToRest/index.tsx diff --git a/packages/ui-patterns/SqlToRest/sql-to-rest.tsx b/packages/ui-patterns/src/SqlToRest/sql-to-rest.tsx similarity index 100% rename from packages/ui-patterns/SqlToRest/sql-to-rest.tsx rename to packages/ui-patterns/src/SqlToRest/sql-to-rest.tsx diff --git a/packages/ui-patterns/SqlToRest/syntax-highlighter/framer-features.ts b/packages/ui-patterns/src/SqlToRest/syntax-highlighter/framer-features.ts similarity index 100% rename from packages/ui-patterns/SqlToRest/syntax-highlighter/framer-features.ts rename to packages/ui-patterns/src/SqlToRest/syntax-highlighter/framer-features.ts diff --git a/packages/ui-patterns/SqlToRest/syntax-highlighter/transform-renderer.tsx b/packages/ui-patterns/src/SqlToRest/syntax-highlighter/transform-renderer.tsx similarity index 100% rename from packages/ui-patterns/SqlToRest/syntax-highlighter/transform-renderer.tsx rename to packages/ui-patterns/src/SqlToRest/syntax-highlighter/transform-renderer.tsx diff --git a/packages/ui-patterns/SqlToRest/util.ts b/packages/ui-patterns/src/SqlToRest/util.ts similarity index 100% rename from packages/ui-patterns/SqlToRest/util.ts rename to packages/ui-patterns/src/SqlToRest/util.ts diff --git a/packages/ui-patterns/ThemeToggle.tsx b/packages/ui-patterns/src/ThemeToggle.tsx similarity index 100% rename from packages/ui-patterns/ThemeToggle.tsx rename to packages/ui-patterns/src/ThemeToggle.tsx diff --git a/packages/ui-patterns/TimestampInfo/index.tsx b/packages/ui-patterns/src/TimestampInfo/index.tsx similarity index 100% rename from packages/ui-patterns/TimestampInfo/index.tsx rename to packages/ui-patterns/src/TimestampInfo/index.tsx diff --git a/packages/ui-patterns/Toc/index.ts b/packages/ui-patterns/src/Toc/index.ts similarity index 100% rename from packages/ui-patterns/Toc/index.ts rename to packages/ui-patterns/src/Toc/index.ts diff --git a/packages/ui-patterns/Toc/mdx-plugins/remark-heading.ts b/packages/ui-patterns/src/Toc/mdx-plugins/remark-heading.ts similarity index 100% rename from packages/ui-patterns/Toc/mdx-plugins/remark-heading.ts rename to packages/ui-patterns/src/Toc/mdx-plugins/remark-heading.ts diff --git a/packages/ui-patterns/Toc/mdx-plugins/remark-utils.ts b/packages/ui-patterns/src/Toc/mdx-plugins/remark-utils.ts similarity index 100% rename from packages/ui-patterns/Toc/mdx-plugins/remark-utils.ts rename to packages/ui-patterns/src/Toc/mdx-plugins/remark-utils.ts diff --git a/packages/ui-patterns/Toc/server/get-toc.tsx b/packages/ui-patterns/src/Toc/server/get-toc.tsx similarity index 100% rename from packages/ui-patterns/Toc/server/get-toc.tsx rename to packages/ui-patterns/src/Toc/server/get-toc.tsx diff --git a/packages/ui-patterns/Toc/toc-primitive.tsx b/packages/ui-patterns/src/Toc/toc-primitive.tsx similarity index 100% rename from packages/ui-patterns/Toc/toc-primitive.tsx rename to packages/ui-patterns/src/Toc/toc-primitive.tsx diff --git a/packages/ui-patterns/Toc/toc-thumb.tsx b/packages/ui-patterns/src/Toc/toc-thumb.tsx similarity index 100% rename from packages/ui-patterns/Toc/toc-thumb.tsx rename to packages/ui-patterns/src/Toc/toc-thumb.tsx diff --git a/packages/ui-patterns/Toc/toc.tsx b/packages/ui-patterns/src/Toc/toc.tsx similarity index 100% rename from packages/ui-patterns/Toc/toc.tsx rename to packages/ui-patterns/src/Toc/toc.tsx diff --git a/packages/ui-patterns/Toc/types.ts b/packages/ui-patterns/src/Toc/types.ts similarity index 100% rename from packages/ui-patterns/Toc/types.ts rename to packages/ui-patterns/src/Toc/types.ts diff --git a/packages/ui-patterns/TweetCard/index.tsx b/packages/ui-patterns/src/TweetCard/index.tsx similarity index 100% rename from packages/ui-patterns/TweetCard/index.tsx rename to packages/ui-patterns/src/TweetCard/index.tsx diff --git a/packages/ui-patterns/admonition.tsx b/packages/ui-patterns/src/admonition.tsx similarity index 100% rename from packages/ui-patterns/admonition.tsx rename to packages/ui-patterns/src/admonition.tsx diff --git a/packages/ui-patterns/consent.tsx b/packages/ui-patterns/src/consent.tsx similarity index 100% rename from packages/ui-patterns/consent.tsx rename to packages/ui-patterns/src/consent.tsx diff --git a/packages/ui-patterns/form/FormItemLayout/FormItemLayout.tsx b/packages/ui-patterns/src/form/FormItemLayout/FormItemLayout.tsx similarity index 100% rename from packages/ui-patterns/form/FormItemLayout/FormItemLayout.tsx rename to packages/ui-patterns/src/form/FormItemLayout/FormItemLayout.tsx diff --git a/packages/ui-patterns/form/FormLayout2.tsx b/packages/ui-patterns/src/form/FormLayout2.tsx similarity index 100% rename from packages/ui-patterns/form/FormLayout2.tsx rename to packages/ui-patterns/src/form/FormLayout2.tsx diff --git a/packages/ui-patterns/form/Layout/FormLayout.tsx b/packages/ui-patterns/src/form/Layout/FormLayout.tsx similarity index 98% rename from packages/ui-patterns/form/Layout/FormLayout.tsx rename to packages/ui-patterns/src/form/Layout/FormLayout.tsx index 0905177ad8e..ab086656ae8 100644 --- a/packages/ui-patterns/form/Layout/FormLayout.tsx +++ b/packages/ui-patterns/src/form/Layout/FormLayout.tsx @@ -86,7 +86,7 @@ const LabelContainerVariants = cva('transition-all duration-500 ease-in-out', { labelLayout: { horizontal: '', vertical: '', - ['']: '', + '': '', }, }, compoundVariants: [ @@ -101,12 +101,11 @@ const LabelContainerVariants = cva('transition-all duration-500 ease-in-out', { className: 'order-1', }, { - layout: 'vertical' || 'flex', + layout: ['vertical', 'flex'], labelLayout: undefined, flex: false, className: 'flex flex-row gap-2 justify-between', }, - { layout: 'horizontal', className: 'flex flex-col gap-2', @@ -144,7 +143,7 @@ const DataContainerVariants = cva('transition-all duration-500 ease-in-out', { className: 'order-2', }, { - layout: 'vertical' || 'flex', + layout: ['vertical', 'flex'], className: 'col-span-12', }, { @@ -289,7 +288,7 @@ export const FormLayout = React.forwardRef< size = 'medium', beforeLabel, afterLabel, - nonBoxInput = label ? false : true, + nonBoxInput = !label, hideMessage = false, isReactForm, ...props @@ -321,7 +320,7 @@ export const FormLayout = React.forwardRef< {description} diff --git a/packages/ui-patterns/form/Layout/InputIconContainer.module.css b/packages/ui-patterns/src/form/Layout/InputIconContainer.module.css similarity index 100% rename from packages/ui-patterns/form/Layout/InputIconContainer.module.css rename to packages/ui-patterns/src/form/Layout/InputIconContainer.module.css diff --git a/packages/ui-patterns/form/Layout/InputIconContainer.tsx b/packages/ui-patterns/src/form/Layout/InputIconContainer.tsx similarity index 100% rename from packages/ui-patterns/form/Layout/InputIconContainer.tsx rename to packages/ui-patterns/src/form/Layout/InputIconContainer.tsx diff --git a/packages/ui-patterns/form/utils.tsx b/packages/ui-patterns/src/form/utils.tsx similarity index 100% rename from packages/ui-patterns/form/utils.tsx rename to packages/ui-patterns/src/form/utils.tsx diff --git a/packages/ui-patterns/info-tooltip.tsx b/packages/ui-patterns/src/info-tooltip.tsx similarity index 100% rename from packages/ui-patterns/info-tooltip.tsx rename to packages/ui-patterns/src/info-tooltip.tsx diff --git a/packages/ui-patterns/multi-select/index.tsx b/packages/ui-patterns/src/multi-select/index.tsx similarity index 100% rename from packages/ui-patterns/multi-select/index.tsx rename to packages/ui-patterns/src/multi-select/index.tsx diff --git a/packages/ui-patterns/multi-select/multi-select.test.tsx b/packages/ui-patterns/src/multi-select/multi-select.test.tsx similarity index 100% rename from packages/ui-patterns/multi-select/multi-select.test.tsx rename to packages/ui-patterns/src/multi-select/multi-select.test.tsx diff --git a/packages/ui-patterns/multi-select/multi-select.tsx b/packages/ui-patterns/src/multi-select/multi-select.tsx similarity index 100% rename from packages/ui-patterns/multi-select/multi-select.tsx rename to packages/ui-patterns/src/multi-select/multi-select.tsx diff --git a/packages/ui-patterns/types/assets.d.ts b/packages/ui-patterns/src/types/assets.d.ts similarity index 100% rename from packages/ui-patterns/types/assets.d.ts rename to packages/ui-patterns/src/types/assets.d.ts diff --git a/packages/ui-patterns/tsconfig.json b/packages/ui-patterns/tsconfig.json index 59fbfb092e6..feaeab7a8cf 100644 --- a/packages/ui-patterns/tsconfig.json +++ b/packages/ui-patterns/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "tsconfig/react-library.json", "compilerOptions": { - "resolveJsonModule": true + "resolveJsonModule": true, + "incremental": true }, "include": [".", "./../../packages/ui/src/**/*.d.ts", "../ui/src/components/Icons"], "exclude": ["dist", "build", "node_modules"] diff --git a/packages/ui-patterns/vitest.config.ts b/packages/ui-patterns/vitest.config.ts index 8506bb4f23a..7327c096b39 100644 --- a/packages/ui-patterns/vitest.config.ts +++ b/packages/ui-patterns/vitest.config.ts @@ -4,5 +4,9 @@ export default defineConfig({ test: { environment: 'jsdom', setupFiles: ['./vitest.setup.ts'], + reporters: ['default', 'json'], + coverage: { + reporter: ['text', 'json', 'html'], + }, }, }) diff --git a/packages/ui/package.json b/packages/ui/package.json index 80d54592c42..b6e8bb5f1fd 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -49,7 +49,6 @@ "@radix-ui/react-tooltip": "^1.1.6", "@tailwindcss/forms": "^0.5.0", "@tailwindcss/typography": "^0.5.9", - "autoprefixer": "^10.4.14", "class-variance-authority": "^0.6.1", "clsx": "^1.2.1", "cmdk": "^1.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 73835d32d72..63b6e767076 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -183,9 +183,6 @@ importers: '@types/react-dom': specifier: 'catalog:' version: 18.3.0 - autoprefixer: - specifier: ^10.0.1 - version: 10.4.16(postcss@8.5.3) config: specifier: workspace:^ version: link:../../packages/config @@ -203,7 +200,7 @@ importers: version: 1.6.0 tailwindcss: specifier: ^3.3.0 - version: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)) + version: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)) tsconfig: specifier: workspace:* version: link:../../packages/tsconfig @@ -324,7 +321,7 @@ importers: libpg-query: specifier: 15.2.0 version: 15.2.0(encoding@0.1.13)(supports-color@8.1.1) - lodash: + lodash-es: specifier: ^4.17.21 version: 4.17.21 lucide-react: @@ -484,6 +481,9 @@ importers: '@types/jsrsasign': specifier: ^10.5.12 version: 10.5.12 + '@types/lodash-es': + specifier: ^4.17.12 + version: 4.17.12 '@types/mdast': specifier: ^3.0.0 version: 3.0.15 @@ -982,16 +982,13 @@ importers: version: 4.4.2 '@vitest/coverage-v8': specifier: ^3.0.9 - version: 3.0.9(supports-color@8.1.1)(vitest@3.0.9) + version: 3.0.9(supports-color@8.1.1)(vitest@3.0.9(@types/node@20.12.11)(@vitest/ui@3.0.4)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.4.11(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5)) '@vitest/ui': specifier: ^3.0.0 version: 3.0.4(vitest@3.0.9) api-types: specifier: workspace:* version: link:../../packages/api-types - autoprefixer: - specifier: ^10.4.14 - version: 10.4.16(postcss@8.5.3) date-fns: specifier: ^2.30.0 version: 2.30.0 @@ -1182,9 +1179,6 @@ importers: '@types/react-dom': specifier: 'catalog:' version: 18.3.0 - autoprefixer: - specifier: ^10.0.1 - version: 10.4.16(postcss@8.5.3) config: specifier: workspace:^ version: link:../../packages/config @@ -1253,7 +1247,7 @@ importers: version: 14.2.3 '@next/mdx': specifier: ^14.2.3 - version: 14.2.3(@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0(esbuild@0.25.2)))(@mdx-js/react@2.3.0(react@18.3.1)) + version: 14.2.3(@mdx-js/loader@2.3.0(supports-color@8.1.1))(@mdx-js/react@2.3.0(react@18.3.1)) '@octokit/auth-app': specifier: ^7.0.0 version: 7.1.5 @@ -1401,9 +1395,6 @@ importers: swiper: specifier: ^11.0.7 version: 11.0.7 - three: - specifier: ^0.170.0 - version: 0.170.0 typed.js: specifier: ^2.0.16 version: 2.0.16 @@ -1468,9 +1459,6 @@ importers: api-types: specifier: workspace:* version: link:../../packages/api-types - autoprefixer: - specifier: ^10.4.14 - version: 10.4.16(postcss@8.5.3) postcss: specifier: ^8.5.3 version: 8.5.3 @@ -1655,10 +1643,10 @@ importers: version: 0.1.9 '@tailwindcss/forms': specifier: ^0.5.0 - version: 0.5.6(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))) + version: 0.5.6(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))) '@tailwindcss/typography': specifier: ^0.5.9 - version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))) + version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))) deepmerge: specifier: ^4.2.2 version: 4.3.1 @@ -1671,10 +1659,10 @@ importers: devDependencies: tailwindcss: specifier: ^3.4.1 - version: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)) + version: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)) tailwindcss-animate: specifier: ^1.0.6 - version: 1.0.7(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))) + version: 1.0.7(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))) packages/eslint-config-supabase: dependencies: @@ -1855,9 +1843,6 @@ importers: '@tailwindcss/typography': specifier: ^0.5.9 version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))) - autoprefixer: - specifier: ^10.4.14 - version: 10.4.16(postcss@8.5.3) class-variance-authority: specifier: ^0.6.1 version: 0.6.1 @@ -2018,6 +2003,9 @@ importers: '@supabase/supabase-js': specifier: 'catalog:' version: 2.49.3 + '@vitest/coverage-v8': + specifier: ^3.0.9 + version: 3.0.9(supports-color@8.1.1)(vitest@3.0.9(@types/node@20.12.11)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.7.3(@types/node@20.12.11)(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5)) class-variance-authority: specifier: ^0.6.0 version: 0.6.1 @@ -2169,6 +2157,9 @@ importers: next-router-mock: specifier: ^0.9.13 version: 0.9.13(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.72.0))(react@18.3.1) + tsx: + specifier: ^4.19.3 + version: 4.19.3 typescript: specifier: ~5.5.0 version: 5.5.2 @@ -7691,6 +7682,9 @@ packages: '@types/katex@0.16.7': resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + '@types/lodash.template@4.5.0': resolution: {integrity: sha512-4LgHxK16IPbGR7TmXpPvNT7iNGsLCdQY6Rc0mi1a/JECt8et/D4hx6NMVAJej/d932sj1mJsg0QYHKL189O0Qw==} @@ -8559,11 +8553,6 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.23.3: - resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - browserslist@4.24.4: resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -8646,9 +8635,6 @@ packages: camelize@1.0.1: resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} - caniuse-lite@1.0.30001651: - resolution: {integrity: sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==} - caniuse-lite@1.0.30001695: resolution: {integrity: sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==} @@ -9614,9 +9600,6 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.13: - resolution: {integrity: sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==} - electron-to-chromium@1.5.88: resolution: {integrity: sha512-K3C2qf1o+bGzbilTDCTBhTQcMS9KW60yTAaTeeXsfvQuTDDwlokLam/AdqlqcSy9u4UainDgsHV23ksXAOgamw==} @@ -9751,10 +9734,6 @@ packages: engines: {node: '>=18'} hasBin: true - escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} - escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -12758,9 +12737,6 @@ packages: node-readfiles@0.2.0: resolution: {integrity: sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==} - node-releases@2.0.18: - resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} - node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} @@ -15133,9 +15109,6 @@ packages: thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} - three@0.170.0: - resolution: {integrity: sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==} - throttle-debounce@3.0.1: resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} engines: {node: '>=10'} @@ -15704,12 +15677,6 @@ packages: unwasm@0.3.9: resolution: {integrity: sha512-LDxTx/2DkFURUd+BU1vUsF/moj0JsoTvl+2tcg2AUOiEzVturhGGx17/IMgGvKUYdZwr33EJHtChCJuhu9Ouvg==} - update-browserslist-db@1.1.0: - resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - update-browserslist-db@1.1.2: resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} hasBin: true @@ -18709,15 +18676,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0(esbuild@0.25.2))': - dependencies: - '@mdx-js/mdx': 2.3.0(supports-color@8.1.1) - source-map: 0.7.4 - webpack: 5.94.0(esbuild@0.25.2) - transitivePeerDependencies: - - supports-color - optional: true - '@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0)': dependencies: '@mdx-js/mdx': 2.3.0(supports-color@8.1.1) @@ -18885,11 +18843,11 @@ snapshots: dependencies: glob: 10.3.10 - '@next/mdx@14.2.3(@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0(esbuild@0.25.2)))(@mdx-js/react@2.3.0(react@18.3.1))': + '@next/mdx@14.2.3(@mdx-js/loader@2.3.0(supports-color@8.1.1))(@mdx-js/react@2.3.0(react@18.3.1))': dependencies: source-map: 0.7.4 optionalDependencies: - '@mdx-js/loader': 2.3.0(supports-color@8.1.1)(webpack@5.94.0(esbuild@0.25.2)) + '@mdx-js/loader': 2.3.0(supports-color@8.1.1)(webpack@5.94.0) '@mdx-js/react': 2.3.0(react@18.3.1) '@next/mdx@15.3.1(@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0))(@mdx-js/react@2.3.0(react@18.3.1))': @@ -22608,11 +22566,6 @@ snapshots: mini-svg-data-uri: 1.4.4 tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)) - '@tailwindcss/forms@0.5.6(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)))': - dependencies: - mini-svg-data-uri: 1.4.4 - tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)) - '@tailwindcss/typography@0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)))': dependencies: lodash.castarray: 4.4.0 @@ -22621,14 +22574,6 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)) - '@tailwindcss/typography@0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)))': - dependencies: - lodash.castarray: 4.4.0 - lodash.isplainobject: 4.0.6 - lodash.merge: 4.6.2 - postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)) - '@tanstack/directive-functions-plugin@1.114.12(@types/node@22.13.14)(jiti@2.4.2)(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5)': dependencies: '@babel/code-frame': 7.26.2 @@ -23547,9 +23492,13 @@ snapshots: '@types/katex@0.16.7': {} + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.16 + '@types/lodash.template@4.5.0': dependencies: - '@types/lodash': 4.17.5 + '@types/lodash': 4.17.16 '@types/lodash@4.17.16': {} @@ -23910,6 +23859,24 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitest/coverage-v8@3.0.9(supports-color@8.1.1)(vitest@3.0.9(@types/node@20.12.11)(@vitest/ui@3.0.4)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.4.11(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 1.0.2 + debug: 4.4.0(supports-color@8.1.1) + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6(supports-color@8.1.1) + istanbul-reports: 3.1.7 + magic-string: 0.30.17 + magicast: 0.3.5 + std-env: 3.8.1 + test-exclude: 7.0.1 + tinyrainbow: 2.0.0 + vitest: 3.0.9(@types/node@20.12.11)(@vitest/ui@3.0.4)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.4.11(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5) + transitivePeerDependencies: + - supports-color + '@vitest/coverage-v8@3.0.9(supports-color@8.1.1)(vitest@3.0.9(@types/node@20.12.11)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.7.3(@types/node@20.12.11)(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))': dependencies: '@ampproject/remapping': 2.3.0 @@ -23946,24 +23913,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@3.0.9(supports-color@8.1.1)(vitest@3.0.9)': - dependencies: - '@ampproject/remapping': 2.3.0 - '@bcoe/v8-coverage': 1.0.2 - debug: 4.4.0(supports-color@8.1.1) - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6(supports-color@8.1.1) - istanbul-reports: 3.1.7 - magic-string: 0.30.17 - magicast: 0.3.5 - std-env: 3.8.1 - test-exclude: 7.0.1 - tinyrainbow: 2.0.0 - vitest: 3.0.9(@types/node@20.12.11)(@vitest/ui@3.0.4)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.4.11(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5) - transitivePeerDependencies: - - supports-color - '@vitest/expect@3.0.9': dependencies: '@vitest/spy': 3.0.9 @@ -24558,11 +24507,11 @@ snapshots: autoprefixer@10.4.16(postcss@8.5.3): dependencies: - browserslist: 4.23.3 - caniuse-lite: 1.0.30001651 + browserslist: 4.24.4 + caniuse-lite: 1.0.30001695 fraction.js: 4.3.6 normalize-range: 0.1.2 - picocolors: 1.0.1 + picocolors: 1.1.1 postcss: 8.5.3 postcss-value-parser: 4.2.0 @@ -24688,13 +24637,6 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.23.3: - dependencies: - caniuse-lite: 1.0.30001695 - electron-to-chromium: 1.5.13 - node-releases: 2.0.18 - update-browserslist-db: 1.1.0(browserslist@4.23.3) - browserslist@4.24.4: dependencies: caniuse-lite: 1.0.30001695 @@ -24815,8 +24757,6 @@ snapshots: camelize@1.0.1: {} - caniuse-lite@1.0.30001651: {} - caniuse-lite@1.0.30001695: {} capital-case@1.0.4: @@ -25748,8 +25688,6 @@ snapshots: dependencies: jake: 10.8.7 - electron-to-chromium@1.5.13: {} - electron-to-chromium@1.5.88: {} emoji-regex-xs@1.0.0: {} @@ -25984,8 +25922,6 @@ snapshots: '@esbuild/win32-ia32': 0.25.2 '@esbuild/win32-x64': 0.25.2 - escalade@3.1.2: {} - escalade@3.2.0: {} escape-carriage@1.3.1: {} @@ -26102,7 +26038,7 @@ snapshots: debug: 4.4.0(supports-color@8.1.1) enhanced-resolve: 5.17.1 eslint: 8.57.0(supports-color@8.1.1) - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-plugin-import@2.29.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1))(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1) fast-glob: 3.3.2 get-tsconfig: 4.7.2 @@ -26114,7 +26050,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-plugin-import@2.29.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1))(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1): dependencies: debug: 3.2.7(supports-color@8.1.1) optionalDependencies: @@ -26141,7 +26077,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0(supports-color@8.1.1) eslint-import-resolver-node: 0.3.9(supports-color@8.1.1) - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-plugin-import@2.29.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1))(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -30088,8 +30024,6 @@ snapshots: dependencies: es6-promise: 3.3.1 - node-releases@2.0.18: {} - node-releases@2.0.19: {} node-sql-parser@4.18.0: @@ -32861,9 +32795,9 @@ snapshots: tailwind-merge@1.14.0: {} - tailwindcss-animate@1.0.7(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))): + tailwindcss-animate@1.0.7(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))): dependencies: - tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)) + tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)) tailwindcss-radix@2.8.0: {} @@ -33011,8 +32945,6 @@ snapshots: dependencies: real-require: 0.2.0 - three@0.170.0: {} - throttle-debounce@3.0.1: {} throttleit@2.1.0: {} @@ -33599,12 +33531,6 @@ snapshots: pkg-types: 1.3.1 unplugin: 1.16.1 - update-browserslist-db@1.1.0(browserslist@4.23.3): - dependencies: - browserslist: 4.23.3 - escalade: 3.1.2 - picocolors: 1.1.1 - update-browserslist-db@1.1.2(browserslist@4.24.4): dependencies: browserslist: 4.24.4 diff --git a/supa-mdx-lint/Rule003Spelling.toml b/supa-mdx-lint/Rule003Spelling.toml index 9ca43af1105..9c174104a2f 100644 --- a/supa-mdx-lint/Rule003Spelling.toml +++ b/supa-mdx-lint/Rule003Spelling.toml @@ -35,6 +35,7 @@ allow_list = [ "[Cc]onfigs?", "[Cc]onsecutiveness", "[Cc]ooldowns?", + "[Cc]oroutines?", "[Cc]ron", "[Dd]atasets?", "[Dd]atasources?", diff --git a/supabase/seed.sql b/supabase/seed.sql index 702adcfcd90..fa76ef7fb1e 100644 --- a/supabase/seed.sql +++ b/supabase/seed.sql @@ -6,3 +6,13 @@ values ('Singapore', 'Singapore', 'lw12', now(), true); insert into public.launch_weeks (id) values ('lw14'); + +-- Insert mock error codes for testing +insert into content.error (code, service, http_status_code, message) +values + ( + 'test_code', + (select id from content.service where name = 'AUTH'), + 500, + 'This is a test error message' + );