diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc116d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + +# Rules + +*.mdc \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..7d74fe2 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,9 @@ +# Package Managers +package-lock.json +pnpm-lock.yaml +yarn.lock +bun.lock +bun.lockb + +# Miscellaneous +/static/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..b513262 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,17 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": [ + "prettier-plugin-svelte" + ], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ] +} diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 466695b..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 Appwrite - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3e07947 --- /dev/null +++ b/README.md @@ -0,0 +1,225 @@ +# Appwrite Cursor Rules Generator + +A web application for generating comprehensive Cursor rules (`.mdc` files) for Appwrite development across multiple SDKs and frameworks. This tool helps developers create customized development rules that include best practices, code examples, and guidance for building applications with Appwrite. + +## Features + +- **Multi-SDK Support**: Generate rules for JavaScript/TypeScript, Python, PHP, Go, Flutter/Dart, Apple, Android, Swift, Kotlin, Ruby, .NET, and React Native +- **Framework-Specific Rules**: Get tailored rules for popular frameworks like Next.js, React, Vue, Svelte, Angular, Astro, Nuxt, Qwik, Solid, and more +- **Feature Selection**: Choose which Appwrite features to include: + - Authentication & Teams + - Database Operations + - Storage Operations + - Functions + - Messaging + - Sites + - Realtime Subscriptions + - MCP (Model Context Protocol) recommendations +- **Export Options**: Copy to clipboard or download as `.mdc` file +- **Best Practices**: Generated rules include comprehensive best practices, multi-tenancy patterns, and security guidelines + +## Supported SDKs and Frameworks + +| SDK | Frameworks | +|-----|------------| +| JavaScript/TypeScript | Next.js, React, Vue, Svelte, Angular, Astro, Nuxt, Qwik, Solid, TanStack, Node.js, Vanilla | +| React Native | React Native, Vanilla | +| Python | Flask, Django, FastAPI, Server | +| Flutter/Dart | Flutter, Server | +| Apple | Vanilla | +| Android | Vanilla | +| Swift | Server, Vanilla | +| Kotlin | Server, Vanilla | +| PHP | Laravel, Symfony, Server | +| Go | Gin, Fiber, Server | +| Ruby | Rails, Server | +| .NET | ASP.NET, Server, Vanilla | + +## Getting Started + +### Prerequisites + +- Node.js 18+ +- pnpm + +### Installation + +1. Clone the repository: +```bash +git clone +cd appwrite-cursor-rules +``` + +2. Install dependencies: +```bash +pnpm install +``` + +3. Start the development server: +```bash +pnpm dev +``` + +4. Open your browser and navigate to `http://localhost:5173` (or the port shown in the terminal) + +## Usage + +1. **Select SDK**: Choose your preferred Appwrite SDK from the dropdown +2. **Select Framework**: Pick the framework you're using (options depend on the selected SDK) +3. **Choose Features**: Check the boxes for the Appwrite features you want to include in your rules +4. **Generate Rules**: Click the "Generate Rules" button +5. **Export**: Copy the rules to your clipboard or download as a `.mdc` file + +The generated rules file can be used in Cursor IDE to provide AI-assisted development guidance specific to your Appwrite setup. + +## API Endpoints + +The application exposes REST API endpoints for programmatic access to rule generation. + +### Get Available SDKs and Frameworks + +**GET** `/api/sdks` + +Returns a list of all available SDKs, their frameworks, and available features. + +**Response:** +```json +{ + "sdks": [ + { + "id": "javascript", + "name": "JavaScript/TypeScript", + "frameworks": ["nextjs", "react", "vue", ...], + "importSyntax": "import", + "exportSyntax": "export", + "asyncSyntax": "async/await" + }, + ... + ], + "availableFeatures": ["auth", "database", "storage", "functions", "messaging", "sites", "realtime"] +} +``` + +### Generate Rules + +**GET** `/api/rules` + +Generate rules using query parameters. + +**Query Parameters:** +- `sdk` (required): SDK identifier (e.g., `javascript`, `python`, `go`) +- `framework` (required): Framework identifier (e.g., `nextjs`, `react`, `flask`) +- `features` (optional): Comma-separated list of features (default: `auth`) + - Available: `auth`, `database`, `storage`, `functions`, `messaging`, `sites`, `realtime` + - Special: `all` - includes all available features +- `mcp` (optional): Include MCP recommendations (`true` or `false`, default: `false`) +- `format` (optional): Response format (`text` or `json`, default: `text`) + +**Example:** +```bash +# Get rules as markdown text +curl "http://localhost:5173/api/rules?sdk=javascript&framework=nextjs&features=auth,database&format=text" + +# Get all features +curl "http://localhost:5173/api/rules?sdk=javascript&framework=nextjs&features=all" + +# Get rules as JSON +curl "http://localhost:5173/api/rules?sdk=python&framework=flask&features=auth,storage&format=json" +``` + +**Response (format=text):** +- Content-Type: `text/markdown; charset=utf-8` +- Returns the generated rules as markdown text +- Includes `Content-Disposition` header for file download + +**Response (format=json):** +```json +{ + "sdk": "javascript", + "framework": "nextjs", + "features": ["auth", "database"], + "includeMCP": false, + "rules": "---\ndescription: You are an expert developer...\n---\n\n# Appwrite Development Rules\n..." +} +``` + +**Error Responses:** +- `400 Bad Request`: Invalid SDK or framework +- `500 Internal Server Error`: Server error during rule generation + +**Example Usage:** + +```javascript +// Fetch rules using fetch API +const response = await fetch('/api/rules?sdk=javascript&framework=nextjs&features=auth,database'); +const rules = await response.text(); + +// Get all features as JSON +const response = await fetch('/api/rules?sdk=javascript&framework=nextjs&features=all&format=json'); +const data = await response.json(); +console.log(data.rules); +``` + +## Development + +### Available Scripts + +- `pnpm dev` - Start development server +- `pnpm build` - Build for production +- `pnpm preview` - Preview production build +- `pnpm check` - Run Svelte type checking +- `pnpm lint` - Run ESLint and Prettier +- `pnpm format` - Format code with Prettier +- `pnpm generate:nextjs` - Generate Next.js rules file (example script) + +### Project Structure + +``` +appwrite-cursor-rules/ +├── src/ +│ ├── lib/ +│ │ ├── languages/ # SDK and framework-specific code examples +│ │ │ ├── js/ # JavaScript/TypeScript frameworks +│ │ │ ├── python/ # Python frameworks +│ │ │ ├── common/ # Shared rules (products, MCP, etc.) +│ │ │ └── ... # Other SDKs +│ │ ├── rules-generator.js # Main rules generation logic +│ │ └── utils/ # Utility functions +│ └── routes/ +│ ├── api/ +│ │ ├── rules/ +│ │ │ └── +server.js # API endpoint for generating rules +│ │ └── sdks/ +│ │ └── +server.js # API endpoint for listing SDKs +│ ├── +page.svelte # Main application page +│ └── +layout.svelte # Layout component +├── scripts/ +│ ├── generate-nextjs-rules.js # Example script for generating rules +│ └── lib-loader.js # Module loader for scripts +└── static/ # Static assets +``` + +### Adding New SDKs or Frameworks + +1. Create a new file in `src/lib/languages/[sdk-name]/index.js` (or add to existing SDK directory) +2. Export framework-specific initialization code +3. Add the SDK configuration to `SDK_OPTIONS` in `src/lib/rules-generator.js` +4. Export the SDK module in `src/lib/languages/index.js` + +### Adding New Features + +1. Create a new section generator function in `src/lib/rules-generator.js` (e.g., `generateNewFeatureSection`) +2. Add the feature to the features array in `src/routes/+page.svelte` +3. Include the feature in the `generateRules` function's Promise.all array + +## Generated Rules Format + +The generated rules follow the Cursor `.mdc` format and include: + +- **Frontmatter**: Metadata about the rules (description, alwaysApply flag) +- **SDK Initialization**: Framework-specific code examples for setting up Appwrite +- **Feature Sections**: Best practices and guidance for selected features +- **Multi-Tenancy Guide**: Comprehensive guide on using teams and permissions +- **Product Links**: Links to official Appwrite documentation + + diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..134b1b2 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,40 @@ +import { fileURLToPath } from 'node:url'; +import { includeIgnoreFile } from '@eslint/compat'; +import js from '@eslint/js'; +import svelte from 'eslint-plugin-svelte'; +import { defineConfig } from 'eslint/config'; +import globals from 'globals'; +import ts from 'typescript-eslint'; +import svelteConfig from './svelte.config.js'; + +const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url)); + +export default defineConfig( + includeIgnoreFile(gitignorePath), + js.configs.recommended, + ...ts.configs.recommended, + ...svelte.configs.recommended, + { + languageOptions: { + globals: { ...globals.browser, ...globals.node } + }, + rules: { // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. + // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors + "no-undef": 'off' } + }, + { + files: [ + '**/*.svelte', + '**/*.svelte.ts', + '**/*.svelte.js' + ], + languageOptions: { + parserOptions: { + projectService: true, + extraFileExtensions: ['.svelte'], + parser: ts.parser, + svelteConfig + } + } + } +); diff --git a/package.json b/package.json new file mode 100644 index 0000000..665662b --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "appwrite-cursor-rules", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "format": "prettier --write .", + "lint": "prettier --check . && eslint .", + "generate:nextjs": "node --loader ./scripts/lib-loader.js scripts/generate-nextjs-rules.js" + }, + "devDependencies": { + "@eslint/compat": "^1.4.0", + "@eslint/js": "^9.39.1", + "@sveltejs/adapter-auto": "^7.0.0", + "@sveltejs/kit": "^2.48.5", + "@sveltejs/vite-plugin-svelte": "^6.2.1", + "@types/node": "^22", + "eslint": "^9.39.1", + "eslint-plugin-svelte": "^3.13.0", + "globals": "^16.5.0", + "prettier": "^3.6.2", + "prettier-plugin-svelte": "^3.4.0", + "svelte": "^5.43.8", + "svelte-check": "^4.3.4", + "typescript": "^5.9.3", + "typescript-eslint": "^8.47.0", + "vite": "^7.2.2" + }, + "dependencies": { + "@appwrite.io/pink": "^1.0.0" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..3db13e6 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2054 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@appwrite.io/pink': + specifier: ^1.0.0 + version: 1.0.0 + devDependencies: + '@eslint/compat': + specifier: ^1.4.0 + version: 1.4.1(eslint@9.39.1) + '@eslint/js': + specifier: ^9.39.1 + version: 9.39.1 + '@sveltejs/adapter-auto': + specifier: ^7.0.0 + version: 7.0.0(@sveltejs/kit@2.49.0(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)))(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1))) + '@sveltejs/kit': + specifier: ^2.48.5 + version: 2.49.0(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)))(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)) + '@sveltejs/vite-plugin-svelte': + specifier: ^6.2.1 + version: 6.2.1(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)) + '@types/node': + specifier: ^22 + version: 22.19.1 + eslint: + specifier: ^9.39.1 + version: 9.39.1 + eslint-plugin-svelte: + specifier: ^3.13.0 + version: 3.13.0(eslint@9.39.1)(svelte@5.44.0) + globals: + specifier: ^16.5.0 + version: 16.5.0 + prettier: + specifier: ^3.6.2 + version: 3.6.2 + prettier-plugin-svelte: + specifier: ^3.4.0 + version: 3.4.0(prettier@3.6.2)(svelte@5.44.0) + svelte: + specifier: ^5.43.8 + version: 5.44.0 + svelte-check: + specifier: ^4.3.4 + version: 4.3.4(picomatch@4.0.3)(svelte@5.44.0)(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + typescript-eslint: + specifier: ^8.47.0 + version: 8.48.0(eslint@9.39.1)(typescript@5.9.3) + vite: + specifier: ^7.2.2 + version: 7.2.4(@types/node@22.19.1) + +packages: + + '@appwrite.io/pink-icons@1.0.0': + resolution: {integrity: sha512-+zpksP07MvOYwhx9AZDFW0pxXQNC2juKwyOQVRAwAOkN1ACSQKPlyytkI1u2ci6CQPWjJe20CzbvBBuRNXOKjA==} + + '@appwrite.io/pink@1.0.0': + resolution: {integrity: sha512-q/RFlKjtGL7yBfjYkPkHYnhPt9jSMT3zoLRlEIu+G2lt59QWjybELQW17cS8gm1ZBTcncuzoPtVd6x1vrEo5uw==} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/compat@1.4.1': + resolution: {integrity: sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.40 || 9 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.1': + resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@rollup/rollup-android-arm-eabi@4.53.3': + resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.53.3': + resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.53.3': + resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.53.3': + resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.53.3': + resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.53.3': + resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.53.3': + resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.53.3': + resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.53.3': + resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.53.3': + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.53.3': + resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.53.3': + resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.53.3': + resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.53.3': + resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.53.3': + resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.53.3': + resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + cpu: [x64] + os: [win32] + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@sveltejs/acorn-typescript@1.0.7': + resolution: {integrity: sha512-znp1A/Y1Jj4l/Zy7PX5DZKBE0ZNY+5QBngiE21NJkfSTyzzC5iKNWOtwFXKtIrn7MXEFBck4jD95iBNkGjK92Q==} + peerDependencies: + acorn: ^8.9.0 + + '@sveltejs/adapter-auto@7.0.0': + resolution: {integrity: sha512-ImDWaErTOCkRS4Gt+5gZuymKFBobnhChXUZ9lhUZLahUgvA4OOvRzi3sahzYgbxGj5nkA6OV0GAW378+dl/gyw==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + + '@sveltejs/kit@2.49.0': + resolution: {integrity: sha512-oH8tXw7EZnie8FdOWYrF7Yn4IKrqTFHhXvl8YxXxbKwTMcD/5NNCryUSEXRk2ZR4ojnub0P8rNrsVGHXWqIDtA==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.0.0 + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + + '@sveltejs/vite-plugin-svelte-inspector@5.0.1': + resolution: {integrity: sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0 + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@sveltejs/vite-plugin-svelte@6.2.1': + resolution: {integrity: sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@22.19.1': + resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==} + + '@typescript-eslint/eslint-plugin@8.48.0': + resolution: {integrity: sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.48.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.48.0': + resolution: {integrity: sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.48.0': + resolution: {integrity: sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.48.0': + resolution: {integrity: sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.48.0': + resolution: {integrity: sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.48.0': + resolution: {integrity: sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.48.0': + resolution: {integrity: sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.48.0': + resolution: {integrity: sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.48.0': + resolution: {integrity: sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.48.0': + resolution: {integrity: sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + devalue@5.5.0: + resolution: {integrity: sha512-69sM5yrHfFLJt0AZ9QqZXGCPfJ7fQjvpln3Rq5+PS03LD32Ost1Q9N+eEnaQwGRIriKkMImXD56ocjQmfjbV3w==} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-svelte@3.13.0: + resolution: {integrity: sha512-2ohCCQJJTNbIpQCSDSTWj+FN0OVfPmSO03lmSNT7ytqMaWF6kpT86LdzDqtm4sh7TVPl/OEWJ/d7R87bXP2Vjg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.1 || ^9.0.0 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.39.1: + resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrap@2.2.0: + resolution: {integrity: sha512-WBmtxe7R9C5mvL4n2le8nMUe4mD5V9oiK2vJpQ9I3y20ENPUomPcphBXE8D1x/Bm84oN1V+lOfgXxtqmxTp3Xg==} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + known-css-properties@0.37.0: + resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + normalize.css@8.0.1: + resolution: {integrity: sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + postcss-load-config@3.1.4: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + engines: {node: '>=4'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-plugin-svelte@3.4.0: + resolution: {integrity: sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==} + peerDependencies: + prettier: ^3.0.0 + svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + rollup@4.53.3: + resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + svelte-check@4.3.4: + resolution: {integrity: sha512-DVWvxhBrDsd+0hHWKfjP99lsSXASeOhHJYyuKOFYJcP7ThfSCKgjVarE8XfuMWpS5JV3AlDf+iK1YGGo2TACdw==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + + svelte-eslint-parser@1.4.0: + resolution: {integrity: sha512-fjPzOfipR5S7gQ/JvI9r2H8y9gMGXO3JtmrylHLLyahEMquXI0lrebcjT+9/hNgDej0H7abTyox5HpHmW1PSWA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0, pnpm: 10.18.3} + peerDependencies: + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + svelte@5.44.0: + resolution: {integrity: sha512-R7387No2zEGw4CtYtI2rgsui6BqjFARzoZFGLiLN5OPla0Pq4Ra2WwcP/zBomP3MYalhSNvF1fzDMuU0P0zPJw==} + engines: {node: '>=18'} + + the-new-css-reset@1.11.3: + resolution: {integrity: sha512-61SB81vu9foUyEIqoU1CeqxrdlsVjJojj/CBXoG8BdvlKFsllB0Rza63DblnRqH+3uttPj3FGWo7+c9nu7MT+A==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript-eslint@8.48.0: + resolution: {integrity: sha512-fcKOvQD9GUn3Xw63EgiDqhvWJ5jsyZUaekl3KVpGsDJnN46WJTe3jWxtQP9lMZm1LJNkFLlTaWAxK2vUQR+cqw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite@7.2.4: + resolution: {integrity: sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.1: + resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + vite: + optional: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + +snapshots: + + '@appwrite.io/pink-icons@1.0.0': {} + + '@appwrite.io/pink@1.0.0': + dependencies: + '@appwrite.io/pink-icons': 1.0.0 + normalize.css: 8.0.1 + the-new-css-reset: 1.11.3 + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1)': + dependencies: + eslint: 9.39.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/compat@1.4.1(eslint@9.39.1)': + dependencies: + '@eslint/core': 0.17.0 + optionalDependencies: + eslint: 9.39.1 + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.1': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@polka/url@1.0.0-next.29': {} + + '@rollup/rollup-android-arm-eabi@4.53.3': + optional: true + + '@rollup/rollup-android-arm64@4.53.3': + optional: true + + '@rollup/rollup-darwin-arm64@4.53.3': + optional: true + + '@rollup/rollup-darwin-x64@4.53.3': + optional: true + + '@rollup/rollup-freebsd-arm64@4.53.3': + optional: true + + '@rollup/rollup-freebsd-x64@4.53.3': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-x64-musl@4.53.3': + optional: true + + '@rollup/rollup-openharmony-arm64@4.53.3': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.53.3': + optional: true + + '@standard-schema/spec@1.0.0': {} + + '@sveltejs/acorn-typescript@1.0.7(acorn@8.15.0)': + dependencies: + acorn: 8.15.0 + + '@sveltejs/adapter-auto@7.0.0(@sveltejs/kit@2.49.0(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)))(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)))': + dependencies: + '@sveltejs/kit': 2.49.0(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)))(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)) + + '@sveltejs/kit@2.49.0(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)))(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1))': + dependencies: + '@standard-schema/spec': 1.0.0 + '@sveltejs/acorn-typescript': 1.0.7(acorn@8.15.0) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)) + '@types/cookie': 0.6.0 + acorn: 8.15.0 + cookie: 0.6.0 + devalue: 5.5.0 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.21 + mrmime: 2.0.1 + sade: 1.8.1 + set-cookie-parser: 2.7.2 + sirv: 3.0.2 + svelte: 5.44.0 + vite: 7.2.4(@types/node@22.19.1) + + '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)))(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1))': + dependencies: + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)) + debug: 4.4.3 + svelte: 5.44.0 + vite: 7.2.4(@types/node@22.19.1) + transitivePeerDependencies: + - supports-color + + '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)))(svelte@5.44.0)(vite@7.2.4(@types/node@22.19.1)) + debug: 4.4.3 + deepmerge: 4.3.1 + magic-string: 0.30.21 + svelte: 5.44.0 + vite: 7.2.4(@types/node@22.19.1) + vitefu: 1.1.1(vite@7.2.4(@types/node@22.19.1)) + transitivePeerDependencies: + - supports-color + + '@types/cookie@0.6.0': {} + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@22.19.1': + dependencies: + undici-types: 6.21.0 + + '@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.48.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.48.0 + '@typescript-eslint/type-utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.48.0 + eslint: 9.39.1 + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.48.0(eslint@9.39.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.48.0 + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.48.0 + debug: 4.4.3 + eslint: 9.39.1 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.48.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) + '@typescript-eslint/types': 8.48.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.48.0': + dependencies: + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/visitor-keys': 8.48.0 + + '@typescript-eslint/tsconfig-utils@8.48.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.48.0(eslint@9.39.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.1 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.48.0': {} + + '@typescript-eslint/typescript-estree@8.48.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.48.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/visitor-keys': 8.48.0 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.48.0(eslint@9.39.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) + '@typescript-eslint/scope-manager': 8.48.0 + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + eslint: 9.39.1 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.48.0': + dependencies: + '@typescript-eslint/types': 8.48.0 + eslint-visitor-keys: 4.2.1 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + cookie@0.6.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssesc@3.0.0: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + devalue@5.5.0: {} + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + escape-string-regexp@4.0.0: {} + + eslint-plugin-svelte@3.13.0(eslint@9.39.1)(svelte@5.44.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) + '@jridgewell/sourcemap-codec': 1.5.5 + eslint: 9.39.1 + esutils: 2.0.3 + globals: 16.5.0 + known-css-properties: 0.37.0 + postcss: 8.5.6 + postcss-load-config: 3.1.4(postcss@8.5.6) + postcss-safe-parser: 7.0.1(postcss@8.5.6) + semver: 7.7.3 + svelte-eslint-parser: 1.4.0(svelte@5.44.0) + optionalDependencies: + svelte: 5.44.0 + transitivePeerDependencies: + - ts-node + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.39.1: + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.39.1 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + esm-env@1.2.2: {} + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrap@2.2.0: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + fsevents@2.3.3: + optional: true + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.5.0: {} + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + isexe@2.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@4.1.5: {} + + known-css-properties@0.37.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lilconfig@2.1.0: {} + + locate-character@3.0.0: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + mri@1.2.0: {} + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + normalize.css@8.0.1: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + postcss-load-config@3.1.4(postcss@8.5.6): + dependencies: + lilconfig: 2.1.0 + yaml: 1.10.2 + optionalDependencies: + postcss: 8.5.6 + + postcss-safe-parser@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-scss@4.0.9(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-selector-parser@7.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.44.0): + dependencies: + prettier: 3.6.2 + svelte: 5.44.0 + + prettier@3.6.2: {} + + punycode@2.3.1: {} + + readdirp@4.1.2: {} + + resolve-from@4.0.0: {} + + rollup@4.53.3: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.3 + '@rollup/rollup-android-arm64': 4.53.3 + '@rollup/rollup-darwin-arm64': 4.53.3 + '@rollup/rollup-darwin-x64': 4.53.3 + '@rollup/rollup-freebsd-arm64': 4.53.3 + '@rollup/rollup-freebsd-x64': 4.53.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 + '@rollup/rollup-linux-arm-musleabihf': 4.53.3 + '@rollup/rollup-linux-arm64-gnu': 4.53.3 + '@rollup/rollup-linux-arm64-musl': 4.53.3 + '@rollup/rollup-linux-loong64-gnu': 4.53.3 + '@rollup/rollup-linux-ppc64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-musl': 4.53.3 + '@rollup/rollup-linux-s390x-gnu': 4.53.3 + '@rollup/rollup-linux-x64-gnu': 4.53.3 + '@rollup/rollup-linux-x64-musl': 4.53.3 + '@rollup/rollup-openharmony-arm64': 4.53.3 + '@rollup/rollup-win32-arm64-msvc': 4.53.3 + '@rollup/rollup-win32-ia32-msvc': 4.53.3 + '@rollup/rollup-win32-x64-gnu': 4.53.3 + '@rollup/rollup-win32-x64-msvc': 4.53.3 + fsevents: 2.3.3 + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + semver@7.7.3: {} + + set-cookie-parser@2.7.2: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + source-map-js@1.2.1: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + svelte-check@4.3.4(picomatch@4.0.3)(svelte@5.44.0)(typescript@5.9.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + chokidar: 4.0.3 + fdir: 6.5.0(picomatch@4.0.3) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.44.0 + typescript: 5.9.3 + transitivePeerDependencies: + - picomatch + + svelte-eslint-parser@1.4.0(svelte@5.44.0): + dependencies: + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + postcss: 8.5.6 + postcss-scss: 4.0.9(postcss@8.5.6) + postcss-selector-parser: 7.1.0 + optionalDependencies: + svelte: 5.44.0 + + svelte@5.44.0: + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.7(acorn@8.15.0) + '@types/estree': 1.0.8 + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + devalue: 5.5.0 + esm-env: 1.2.2 + esrap: 2.2.0 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.21 + zimmerframe: 1.1.4 + + the-new-css-reset@1.11.3: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + totalist@3.0.1: {} + + ts-api-utils@2.1.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@8.48.0(eslint@9.39.1)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.48.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) + eslint: 9.39.1 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + vite@7.2.4(@types/node@22.19.1): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.3 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.19.1 + fsevents: 2.3.3 + + vitefu@1.1.1(vite@7.2.4(@types/node@22.19.1)): + optionalDependencies: + vite: 7.2.4(@types/node@22.19.1) + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yaml@1.10.2: {} + + yocto-queue@0.1.0: {} + + zimmerframe@1.1.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..efc037a --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +onlyBuiltDependencies: + - esbuild diff --git a/scripts/generate-nextjs-rules.js b/scripts/generate-nextjs-rules.js new file mode 100644 index 0000000..1a4b7cb --- /dev/null +++ b/scripts/generate-nextjs-rules.js @@ -0,0 +1,32 @@ +import { generateRules } from '../src/lib/rules-generator.js'; +import { writeFile } from 'fs/promises'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +async function generateNextJSRules() { + try { + console.log('Generating Next.js rules with all products enabled...'); + + const rules = await generateRules({ + sdk: 'javascript', + framework: 'nextjs', + features: ['auth', 'database', 'storage', 'functions', 'messaging', 'sites', 'realtime'], + includeMCP: true + }); + + const outputPath = join(__dirname, '..', 'APPWRITE-javascript-nextjs.mdc'); + await writeFile(outputPath, rules, 'utf-8'); + + console.log(`Rules generated successfully!`); + console.log(`Output file: ${outputPath}`); + } catch (error) { + console.error('Error generating rules:', error); + process.exit(1); + } +} + +generateNextJSRules(); + diff --git a/scripts/lib-loader.js b/scripts/lib-loader.js new file mode 100644 index 0000000..22b6fe9 --- /dev/null +++ b/scripts/lib-loader.js @@ -0,0 +1,22 @@ +// Custom Node.js loader to resolve $lib aliases +import { pathToFileURL } from 'node:url'; +import { resolve as resolvePath } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = fileURLToPath(new URL('.', import.meta.url)); +const projectRoot = resolvePath(__dirname, '..'); + +export async function resolve(specifier, context, nextResolve) { + if (specifier.startsWith('$lib/')) { + const relativePath = specifier.replace('$lib/', ''); + const absolutePath = resolvePath(projectRoot, 'src', 'lib', relativePath); + const fileUrl = pathToFileURL(absolutePath).href; + return { + url: fileUrl, + format: 'module', + shortCircuit: true + }; + } + return nextResolve(specifier, context); +} + diff --git a/src/app.d.ts b/src/app.d.ts new file mode 100644 index 0000000..da08e6d --- /dev/null +++ b/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/src/app.html b/src/app.html new file mode 100644 index 0000000..0bcd3ce --- /dev/null +++ b/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/src/lib/appwrite.js b/src/lib/appwrite.js new file mode 100644 index 0000000..7b905d2 --- /dev/null +++ b/src/lib/appwrite.js @@ -0,0 +1,14 @@ +import { Client, Account } from 'appwrite'; +// @ts-ignore - SvelteKit env variables +import { PUBLIC_APPWRITE_ENDPOINT, PUBLIC_APPWRITE_PROJECT_ID } from '$env/static/public'; + +const endpoint = PUBLIC_APPWRITE_ENDPOINT || 'https://cloud.appwrite.io/v1'; +const projectId = PUBLIC_APPWRITE_PROJECT_ID || ''; + +const client = new Client() + .setEndpoint(endpoint) + .setProject(projectId); + +export const account = new Account(client); +export { client }; + diff --git a/src/lib/assets/favicon.svg b/src/lib/assets/favicon.svg new file mode 100644 index 0000000..bd847aa --- /dev/null +++ b/src/lib/assets/favicon.svg @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/src/lib/languages/android/index.js b/src/lib/languages/android/index.js new file mode 100644 index 0000000..bee6839 --- /dev/null +++ b/src/lib/languages/android/index.js @@ -0,0 +1,47 @@ +import { getSDKVersion } from '$lib/utils/versions.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { clientSecurity } from '../common/security.js'; + +/** + * Generates the Android SDK installation template with the latest version + * @param {string} version - The SDK version to use + * @returns {string} + */ +function generateInstallationTemplate(version) { + return `## SDK Installation + +Add the Appwrite Android SDK to your \`build.gradle.kts\`: + +**Recommended: Specify exact version for stability** + +\`\`\`kotlin +dependencies { + implementation("io.appwrite:sdk-for-android:${version}") +} +\`\`\` + +Or for Maven, add to \`pom.xml\`: + +**Recommended: Specify exact version** + +\`\`\`xml + + io.appwrite + sdk-for-android + ${version} + +\`\`\` +`; +} + +/** + * Gets the Android SDK installation template with the latest version from Appwrite's API + * This is the main export used by the rules generator + * @returns {Promise} + */ +export const vanilla = async () => { + const version = await getSDKVersion('client-android'); + const installation = generateInstallationTemplate(version); + return createFrameworkTemplate({ installation, securityNotes: clientSecurity }); +}; + diff --git a/src/lib/languages/apple/index.js b/src/lib/languages/apple/index.js new file mode 100644 index 0000000..d477e88 --- /dev/null +++ b/src/lib/languages/apple/index.js @@ -0,0 +1,37 @@ +import { getSDKVersion } from '$lib/utils/versions.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { clientSecurity } from '../common/security.js'; + +/** + * Generates the Apple SDK installation template with the latest version + * @param {string} version - The SDK version to use + * @returns {string} + */ +function generateInstallationTemplate(version) { + return `## SDK Installation + +Add the Appwrite Apple SDK to your \`Package.swift\`: + +\`\`\`swift +dependencies: [ + .package(url: "https://github.com/appwrite/sdk-for-apple", from: "${version}") +] +\`\`\` + +Or add it via Xcode: +1. File → Add Packages... +2. Enter: \`https://github.com/appwrite/sdk-for-apple\` +3. Select version: \`${version}\` or later`; +} + +/** + * Gets the Apple SDK installation template with the latest version from Appwrite's API + * This is the main export used by the rules generator + * @returns {Promise} + */ +export const vanilla = async () => { + const version = await getSDKVersion('client-apple'); + const installation = generateInstallationTemplate(version); + return createFrameworkTemplate({ installation, securityNotes: clientSecurity }); +}; + diff --git a/src/lib/languages/common/install.js b/src/lib/languages/common/install.js new file mode 100644 index 0000000..ec1d33d --- /dev/null +++ b/src/lib/languages/common/install.js @@ -0,0 +1,149 @@ +/** + * Common installation instructions by package manager/language + */ + +export const npmInstall = `npm install appwrite`; + +export const yarnInstall = `yarn add appwrite`; + +export const pnpmInstall = `pnpm add appwrite`; + +export const bunInstall = `bun add appwrite`; + +/** + * JavaScript/TypeScript installation section + * @param {string} [packageName] - Package name (default: 'appwrite') + * @param {string} [title] - Installation title (default: 'Install the Appwrite JavaScript SDK') + */ +export function jsInstall(packageName = 'appwrite', title = 'Install the Appwrite JavaScript SDK') { + return `## SDK Installation + +${title} using npm: + +\`\`\`bash +npm install ${packageName} +\`\`\` + +Or using yarn: + +\`\`\`bash +yarn add ${packageName} +\`\`\` + +Or using pnpm: + +\`\`\`bash +pnpm add ${packageName} +\`\`\` + +Or using bun: + +\`\`\`bash +bun add ${packageName} +\`\`\``; +} + +/** + * Default JavaScript installation (export as constant for backwards compatibility) + */ +export const jsInstallDefault = jsInstall(); + +/** + * Python installation section + */ +export const pythonInstall = `## SDK Installation + +Install the Appwrite Python SDK using pip: + +\`\`\`bash +pip install appwrite +\`\`\` + +Or using poetry: + +\`\`\`bash +poetry add appwrite +\`\`\``; + +/** + * PHP installation section + */ +export const phpInstall = `## SDK Installation + +Install the Appwrite PHP SDK using Composer: + +\`\`\`bash +composer require appwrite/appwrite +\`\`\``; + +/** + * Go installation section + */ +export const goInstall = `## SDK Installation + +Install the Appwrite Go SDK: + +\`\`\`bash +go get github.com/appwrite/sdk-for-go +\`\`\``; + +/** + * Ruby installation section + */ +export const rubyInstall = `## SDK Installation + +Install the Appwrite Ruby SDK using Bundler: + +\`\`\`bash +gem install appwrite +\`\`\` + +Or add to your \`Gemfile\`: + +\`\`\`ruby +gem 'appwrite' +\`\`\``; + +/** + * .NET installation section + */ +export const dotnetInstall = `## SDK Installation + +Install the Appwrite .NET SDK using NuGet: + +\`\`\`bash +dotnet add package Appwrite +\`\`\` + +Or using Package Manager: + +\`\`\`powershell +Install-Package Appwrite +\`\`\``; + +/** + * Dart/Flutter installation section (async - requires version lookup) + * @param {string} version - SDK version + * @param {boolean} isServer - Whether this is for server SDK + */ +export const dartInstall = (version, isServer = false) => { + const sdkName = isServer ? 'Dart Server SDK' : 'Flutter SDK'; + const pubCommand = isServer ? 'dart pub get' : 'flutter pub get'; + const packageName = isServer ? 'dart_appwrite' : 'appwrite'; + + return `## SDK Installation + +Add the Appwrite ${sdkName} to your \`pubspec.yaml\`: + +\`\`\`yaml +dependencies: + ${packageName}: ^${version} +\`\`\` + +Then install it: + +\`\`\`bash +${pubCommand} +\`\`\``; +}; + diff --git a/src/lib/languages/common/mcp.js b/src/lib/languages/common/mcp.js new file mode 100644 index 0000000..556aa87 --- /dev/null +++ b/src/lib/languages/common/mcp.js @@ -0,0 +1,55 @@ +/** + * MCP Server recommendation content + * This content is included in all generated rules to recommend installing the Appwrite Docs MCP server + */ + +/** + * MCP Server recommendation section + * @returns {string} + */ +export function generateMCPRecommendation() { + return `## Recommended: Install Appwrite Docs MCP Server + +**We highly recommend installing the Appwrite Docs MCP server** to get instant access to Appwrite documentation, code examples, and best practices directly in Cursor. This enables the AI assistant to provide accurate, up-to-date Appwrite guidance and code examples. + +### Installation Steps + +1. **Open Cursor Settings** + - Go to **Cursor Settings** → **MCP** tab + - Click **Add new global MCP server** + +2. **Add the Docs Server** + - Update the \`mcp.json\` file with the following configuration: + +\`\`\`json +{ + "mcpServers": { + "appwrite-docs": { + "command": "npx", + "args": [ + "mcp-remote", + "https://mcp-for-docs.appwrite.io" + ] + } + } +} +\`\`\` + +3. **Save and Restart** + - Save the \`mcp.json\` file + - Restart Cursor if the MCP server doesn't start automatically + +### Benefits + +Once installed, you can ask questions like: +- "How do I set up realtime subscriptions in Appwrite?" +- "Show me how to authenticate users with OAuth" +- "What are the best practices for database queries?" +- "How do I implement file uploads with Appwrite Storage?" +- "Show me an example of using Appwrite Functions" + +**Note:** You can also install the Appwrite API MCP server for direct API interactions. For full setup instructions, visit: https://appwrite.io/docs/tooling/mcp/cursor + +---`; +} + diff --git a/src/lib/languages/common/permissions-examples.js b/src/lib/languages/common/permissions-examples.js new file mode 100644 index 0000000..6ff4a62 --- /dev/null +++ b/src/lib/languages/common/permissions-examples.js @@ -0,0 +1,4889 @@ +/** + * Language-specific permission examples for Appwrite + * All examples are validated against official Appwrite documentation + */ + +export const permissionExamples = { + javascript: { + avoidUserPermissions: `// DON'T do this for multi-tenant apps +import { TablesDB, Permission, Role } from 'appwrite'; + +await tablesDB.createRow({ + databaseId: '', + tableId: '', + rowId: '', + data: { title: 'My Row' }, + permissions: [ + Permission.read(Role.user('')), + Permission.write(Role.user('')) + ] +});`, + preferTeamPermissions: `// DO this for multi-tenant apps +import { TablesDB, Permission, Role } from 'appwrite'; + +await tablesDB.createRow({ + databaseId: '', + tableId: '', + rowId: '', + data: { title: 'My Row' }, + permissions: [ + Permission.read(Role.team('', 'owner')), + Permission.read(Role.team('', 'admin')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'owner')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +});`, + createTeam: `import { Client, Teams } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +// Create a team when a new tenant/organization signs up +const team = await teams.create({ + teamId: '', // Unique team ID (can be auto-generated) + name: '', // Display name + roles: ['owner', 'admin', 'member'] // Optional: Array of role strings +});`, + createMembershipEmail: `import { Client, Teams } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +// Send team invitation (email-based) +const invite = await teams.createMembership({ + teamId: '', + roles: ['admin', 'member'], + email: 'user@example.com', + url: 'https://yourapp.com/accept-invite' // Invitation redirect URL +});`, + createMembershipUserId: `import { Client, Teams } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +// Or invite by user ID (if user already exists) +const membership = await teams.createMembership({ + teamId: '', + roles: ['admin', 'member'], + userId: '' +});`, + listMemberships: `import { Client, Teams } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +// Get all members of a team +const response = await teams.listMemberships({ + teamId: '' +}); + +// Access member data +response.memberships.forEach(membership => { + console.log(membership.userId); + console.log(membership.roles); // Array of role strings + console.log(membership.userName); + console.log(membership.userEmail); +});`, + updateMembership: `import { Client, Teams } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +// Update a member's roles (only team owners/admins can do this) +await teams.updateMembership({ + teamId: '', + membershipId: '', + roles: ['admin', 'member'] // New roles array +});`, + deleteMembership: `import { Client, Teams } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +// Remove a member from a team +await teams.deleteMembership({ + teamId: '', + membershipId: '' +});`, + listTeams: `import { Client, Teams } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +// List all teams the current user belongs to +const response = await teams.list(); + +response.teams.forEach(team => { + console.log(team.$id); + console.log(team.name); +});`, + getUserRole: `import { Client, Teams } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +// Get membership details for current user in a specific team +const response = await teams.listMemberships({ + teamId: '' +}); + +const userMembership = response.memberships.find( + m => m.userId === '' +); + +if (userMembership) { + console.log(userMembership.roles); // ['owner', 'admin', etc.] + const hasAdminRole = userMembership.roles.includes('admin'); +}`, + createRow: `import { Client, TablesDB, Permission, Role } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const tablesDB = new TablesDB(client); + +// Create row with team-based permissions +await tablesDB.createRow({ + databaseId: '', + tableId: '', + rowId: '', + data: { + title: 'My Row', + teamId: '', // Always store teamId for querying + // ... other fields + }, + permissions: [ + // Owners and admins can do everything + Permission.read(Role.team('', 'owner')), + Permission.read(Role.team('', 'admin')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'owner')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')), + Permission.delete(Role.team('', 'admin')) + ] +});`, + createTable: `import { Client, TablesDB, Permission, Role } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); // Server SDK requires API key + +const tablesDB = new TablesDB(client); + +// Create table with team-based permissions +await tablesDB.createTable({ + databaseId: '', + tableId: '', + name: '', + permissions: [ + // Table permissions + Permission.create(Role.team('', 'member')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +});`, + listRows: `import { Client, TablesDB, Query } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const tablesDB = new TablesDB(client); + +// ALWAYS filter by teamId to ensure tenant isolation +const response = await tablesDB.listRows({ + databaseId: '', + tableId: '', + queries: [ + Query.equal('teamId', ''), // Critical: filter by team + Query.orderDesc('$createdAt'), + Query.limit(25) + ] +});`, + createFile: `import { Client, Storage, Permission, Role } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const storage = new Storage(client); + +// Create file with team-based permissions +await storage.createFile({ + bucketId: '', + fileId: '', + file: fileInput, // File object from input + permissions: [ + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +});`, + teamCreationFlow: `// When user creates account/organization +import { Client, Teams, ID } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +const team = await teams.create({ + teamId: ID.unique(), + name: 'Company Name' +}); + +// Make creator an owner +await teams.createMembership({ + teamId: team.$id, + roles: ['owner'], + userId: '' +});`, + inviteFlow: `// Owner/admin invites new member +import { Client, Teams } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +const invite = await teams.createMembership({ + teamId: '', + roles: ['member'], // Default role + email: 'user@example.com', + url: 'https://yourapp.com/accept-invite' // Redirect after accepting +}); +// User receives email, clicks link, accepts invitation`, + memberListUI: `// Display all team members with their roles +import { Client, Teams } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +const response = await teams.listMemberships({ + teamId: '' +}); +// Show list with role badges and action buttons`, + roleChange: `// Admin/owner changes member role +import { Client, Teams } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +await teams.updateMembership({ + teamId: '', + membershipId: '', + roles: ['admin'] +});`, + memberRemoval: `// Remove member (with confirmation) +import { Client, Teams } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +await teams.deleteMembership({ + teamId: '', + membershipId: '' +});`, + roleCheck: `const response = await teams.listMemberships({ + teamId: '' +}); + +const membership = response.memberships.find( + m => m.userId === '' +); + +if (!membership || !membership.roles.includes('admin')) { + throw new Error('Insufficient permissions'); +}` + }, + 'react-native': { + // React Native uses the same syntax as JavaScript + avoidUserPermissions: `// DON'T do this for multi-tenant apps +import { TablesDB, Permission, Role } from 'react-native-appwrite'; + +await tablesDB.createRow({ + databaseId: '', + tableId: '', + rowId: '', + data: { title: 'My Row' }, + permissions: [ + Permission.read(Role.user('')), + Permission.write(Role.user('')) + ] +});`, + preferTeamPermissions: `// DO this for multi-tenant apps +import { TablesDB, Permission, Role } from 'react-native-appwrite'; + +await tablesDB.createRow({ + databaseId: '', + tableId: '', + rowId: '', + data: { title: 'My Row' }, + permissions: [ + Permission.read(Role.team('', 'owner')), + Permission.read(Role.team('', 'admin')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'owner')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +});`, + createTeam: `import { Client, Teams } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +const team = await teams.create({ + teamId: '', + name: '', + roles: ['owner', 'admin', 'member'] +});`, + createMembershipEmail: `import { Client, Teams } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +const invite = await teams.createMembership({ + teamId: '', + roles: ['admin', 'member'], + email: 'user@example.com', + url: 'https://yourapp.com/accept-invite' +});`, + createMembershipUserId: `import { Client, Teams } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +const membership = await teams.createMembership({ + teamId: '', + roles: ['admin', 'member'], + userId: '' +});`, + listMemberships: `import { Client, Teams } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +const response = await teams.listMemberships({ + teamId: '' +}); + +response.memberships.forEach(membership => { + console.log(membership.userId); + console.log(membership.roles); + console.log(membership.userName); + console.log(membership.userEmail); +});`, + updateMembership: `import { Client, Teams } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +await teams.updateMembership({ + teamId: '', + membershipId: '', + roles: ['admin', 'member'] +});`, + deleteMembership: `import { Client, Teams } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +await teams.deleteMembership({ + teamId: '', + membershipId: '' +});`, + listTeams: `import { Client, Teams } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +const response = await teams.list(); + +response.teams.forEach(team => { + console.log(team.$id); + console.log(team.name); +});`, + getUserRole: `import { Client, Teams } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +const response = await teams.listMemberships({ + teamId: '' +}); + +const userMembership = response.memberships.find( + m => m.userId === '' +); + +if (userMembership) { + console.log(userMembership.roles); + const hasAdminRole = userMembership.roles.includes('admin'); +}`, + createRow: `import { Client, TablesDB, Permission, Role } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const tablesDB = new TablesDB(client); + +await tablesDB.createRow({ + databaseId: '', + tableId: '', + rowId: '', + data: { + title: 'My Row', + teamId: '', + }, + permissions: [ + Permission.read(Role.team('', 'owner')), + Permission.read(Role.team('', 'admin')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'owner')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')), + Permission.delete(Role.team('', 'admin')) + ] +});`, + createTable: `import { Client, TablesDB, Permission, Role } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const tablesDB = new TablesDB(client); + +await tablesDB.createTable({ + databaseId: '', + tableId: '', + name: '', + permissions: [ + Permission.create(Role.team('', 'member')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +});`, + listRows: `import { Client, TablesDB, Query } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const tablesDB = new TablesDB(client); + +const response = await tablesDB.listRows({ + databaseId: '', + tableId: '', + queries: [ + Query.equal('teamId', ''), + Query.orderDesc('$createdAt'), + Query.limit(25) + ] +});`, + createFile: `import { Client, Storage, Permission, Role } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const storage = new Storage(client); + +await storage.createFile({ + bucketId: '', + fileId: '', + file: fileInput, + permissions: [ + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +});`, + teamCreationFlow: `import { Client, Teams, ID } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +const team = await teams.create({ + teamId: ID.unique(), + name: 'Company Name' +}); + +await teams.createMembership({ + teamId: team.$id, + roles: ['owner'], + userId: '' +});`, + inviteFlow: `import { Client, Teams } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +const invite = await teams.createMembership({ + teamId: '', + roles: ['member'], + email: 'user@example.com', + url: 'https://yourapp.com/accept-invite' +});`, + memberListUI: `import { Client, Teams } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +const response = await teams.listMemberships({ + teamId: '' +});`, + roleChange: `import { Client, Teams } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +await teams.updateMembership({ + teamId: '', + membershipId: '', + roles: ['admin'] +});`, + memberRemoval: `import { Client, Teams } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const teams = new Teams(client); + +await teams.deleteMembership({ + teamId: '', + membershipId: '' +});`, + roleCheck: `const response = await teams.listMemberships({ + teamId: '' +}); + +const membership = response.memberships.find( + m => m.userId === '' +); + +if (!membership || !membership.roles.includes('admin')) { + throw new Error('Insufficient permissions'); +}` + }, + python: { + avoidUserPermissions: `# DON'T do this for multi-tenant apps +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB +from appwrite.models import Permission, Role + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +tables_db = TablesDB(client) + +tables_db.create_row( + database_id='', + table_id='', + row_id='', + data={'title': 'My Row'}, + permissions=[ + Permission.read(Role.user('')), + Permission.write(Role.user('')) + ] +)`, + preferTeamPermissions: `# DO this for multi-tenant apps +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB +from appwrite.models import Permission, Role + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +tables_db = TablesDB(client) + +tables_db.create_row( + database_id='', + table_id='', + row_id='', + data={'title': 'My Row'}, + permissions=[ + Permission.read(Role.team('', 'owner')), + Permission.read(Role.team('', 'admin')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'owner')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +)`, + createTeam: `from appwrite.client import Client +from appwrite.services.teams import Teams + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +teams = Teams(client) + +# Create a team when a new tenant/organization signs up +team = teams.create( + team_id='', + name='', + roles=['owner', 'admin', 'member'] # optional +)`, + createMembershipEmail: `from appwrite.client import Client +from appwrite.services.teams import Teams + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +teams = Teams(client) + +# Send team invitation (email-based) +invite = teams.create_membership( + team_id='', + roles=['admin', 'member'], + email='user@example.com', + url='https://yourapp.com/accept-invite' +)`, + createMembershipUserId: `from appwrite.client import Client +from appwrite.services.teams import Teams + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') # Server SDK + +teams = Teams(client) + +# Or invite by user ID (if user already exists) +membership = teams.create_membership( + team_id='', + roles=['admin', 'member'], + user_id='' +)`, + listMemberships: `from appwrite.client import Client +from appwrite.services.teams import Teams + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +teams = Teams(client) + +# Get all members of a team +response = teams.list_memberships(team_id='') + +# Access member data +for membership in response['memberships']: + print(membership['userId']) + print(membership['roles']) # Array of role strings + print(membership['userName']) + print(membership['userEmail'])`, + updateMembership: `from appwrite.client import Client +from appwrite.services.teams import Teams + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +teams = Teams(client) + +# Update a member's roles (only team owners/admins can do this) +teams.update_membership( + team_id='', + membership_id='', + roles=['admin', 'member'] +)`, + deleteMembership: `from appwrite.client import Client +from appwrite.services.teams import Teams + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +teams = Teams(client) + +# Remove a member from a team +teams.delete_membership( + team_id='', + membership_id='' +)`, + listTeams: `from appwrite.client import Client +from appwrite.services.teams import Teams + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +teams = Teams(client) + +# List all teams the current user belongs to +response = teams.list() + +for team in response['teams']: + print(team['$id']) + print(team['name'])`, + getUserRole: `from appwrite.client import Client +from appwrite.services.teams import Teams + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +teams = Teams(client) + +# Get membership details for current user in a specific team +response = teams.list_memberships(team_id='') + +user_membership = next( + (m for m in response['memberships'] if m['userId'] == ''), + None +) + +if user_membership: + print(user_membership['roles']) # ['owner', 'admin', etc.] + has_admin_role = 'admin' in user_membership['roles']`, + createRow: `from appwrite.client import Client +from appwrite.services.tables_db import TablesDB +from appwrite.models import Permission, Role + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +tables_db = TablesDB(client) + +# Create row with team-based permissions +tables_db.create_row( + database_id='', + table_id='', + row_id='', + data={ + 'title': 'My Row', + 'teamId': '', # Always store teamId for querying + }, + permissions=[ + Permission.read(Role.team('', 'owner')), + Permission.read(Role.team('', 'admin')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'owner')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')), + Permission.delete(Role.team('', 'admin')) + ] +)`, + createTable: `from appwrite.client import Client +from appwrite.services.tables_db import TablesDB +from appwrite.models import Permission, Role + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') # Server SDK requires API key + +tables_db = TablesDB(client) + +# Create table with team-based permissions +tables_db.create_table( + database_id='', + table_id='', + name='', + permissions=[ + Permission.create(Role.team('', 'member')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +)`, + listRows: `from appwrite.client import Client +from appwrite.services.tables_db import TablesDB +from appwrite.query import Query + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +tables_db = TablesDB(client) + +# ALWAYS filter by teamId to ensure tenant isolation +response = tables_db.list_rows( + database_id='', + table_id='', + queries=[ + Query.equal('teamId', ''), # Critical: filter by team + Query.order_desc('$createdAt'), + Query.limit(25) + ] +)`, + createFile: `from appwrite.client import Client +from appwrite.services.storage import Storage +from appwrite.models import Permission, Role + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +storage = Storage(client) + +# Create file with team-based permissions +storage.create_file( + bucket_id='', + file_id='', + file=file_input, + permissions=[ + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +)`, + teamCreationFlow: `# When user creates account/organization +from appwrite.client import Client +from appwrite.services.teams import Teams +from appwrite.id import ID + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +teams = Teams(client) + +team = teams.create( + team_id=ID.unique(), + name='Company Name' +) + +# Make creator an owner +teams.create_membership( + team_id=team['$id'], + roles=['owner'], + user_id='' +)`, + inviteFlow: `# Owner/admin invites new member +from appwrite.client import Client +from appwrite.services.teams import Teams + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +teams = Teams(client) + +invite = teams.create_membership( + team_id='', + roles=['member'], # Default role + email='user@example.com', + url='https://yourapp.com/accept-invite' +) +# User receives email, clicks link, accepts invitation`, + memberListUI: `# Display all team members with their roles +from appwrite.client import Client +from appwrite.services.teams import Teams + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +teams = Teams(client) + +response = teams.list_memberships(team_id='') +# Show list with role badges and action buttons`, + roleChange: `# Admin/owner changes member role +from appwrite.client import Client +from appwrite.services.teams import Teams + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +teams = Teams(client) + +teams.update_membership( + team_id='', + membership_id='', + roles=['admin'] +)`, + memberRemoval: `# Remove member (with confirmation) +from appwrite.client import Client +from appwrite.services.teams import Teams + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_session('') + +teams = Teams(client) + +teams.delete_membership( + team_id='', + membership_id='' +)`, + roleCheck: `response = teams.list_memberships(team_id='') + +user_membership = next( + (m for m in response['memberships'] if m['userId'] == ''), + None +) + +if not user_membership or 'admin' not in user_membership['roles']: + raise Exception('Insufficient permissions')` + }, + php: { + avoidUserPermissions: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$tablesDB = new TablesDB($client); + +$tablesDB->createRow( + databaseId: '', + tableId: '', + rowId: '', + data: ['title' => 'My Row'], + permissions: [ + Permission::read(Role::user('')), + Permission::write(Role::user('')) + ] +);`, + preferTeamPermissions: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$tablesDB = new TablesDB($client); + +$tablesDB->createRow( + databaseId: '', + tableId: '', + rowId: '', + data: ['title' => 'My Row'], + permissions: [ + Permission::read(Role::team('', 'owner')), + Permission::read(Role::team('', 'admin')), + Permission::read(Role::team('', 'member')), + Permission::update(Role::team('', 'owner')), + Permission::update(Role::team('', 'admin')), + Permission::delete(Role::team('', 'owner')) + ] +);`, + createTeam: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$teams = new Teams($client); + +// Create a team when a new tenant/organization signs up +$team = $teams->create( + teamId: '', + name: '', + roles: ['owner', 'admin', 'member'] // optional +);`, + createMembershipEmail: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$teams = new Teams($client); + +// Send team invitation (email-based) +$invite = $teams->createMembership( + teamId: '', + roles: ['admin', 'member'], + email: 'user@example.com', + url: 'https://yourapp.com/accept-invite' +);`, + createMembershipUserId: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey(''); // Server SDK + +$teams = new Teams($client); + +// Or invite by user ID (if user already exists) +$membership = $teams->createMembership( + teamId: '', + roles: ['admin', 'member'], + userId: '' +);`, + listMemberships: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$teams = new Teams($client); + +// Get all members of a team +$response = $teams->listMemberships(teamId: ''); + +// Access member data +foreach ($response['memberships'] as $membership) { + echo $membership['userId']; + echo implode(', ', $membership['roles']); // Array of role strings + echo $membership['userName']; + echo $membership['userEmail']; +}`, + updateMembership: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$teams = new Teams($client); + +// Update a member's roles (only team owners/admins can do this) +$teams->updateMembership( + teamId: '', + membershipId: '', + roles: ['admin', 'member'] +);`, + deleteMembership: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$teams = new Teams($client); + +// Remove a member from a team +$teams->deleteMembership( + teamId: '', + membershipId: '' +);`, + listTeams: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$teams = new Teams($client); + +// List all teams the current user belongs to +$response = $teams->list(); + +foreach ($response['teams'] as $team) { + echo $team['$id']; + echo $team['name']; +}`, + getUserRole: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$teams = new Teams($client); + +// Get membership details for current user in a specific team +$response = $teams->listMemberships(teamId: ''); + +$userMembership = array_filter( + $response['memberships'], + fn($m) => $m['userId'] === '' +); + +if (!empty($userMembership)) { + $membership = reset($userMembership); + echo implode(', ', $membership['roles']); // ['owner', 'admin', etc.] + $hasAdminRole = in_array('admin', $membership['roles']); +}`, + createRow: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$tablesDB = new TablesDB($client); + +// Create row with team-based permissions +$tablesDB->createRow( + databaseId: '', + tableId: '', + rowId: '', + data: [ + 'title' => 'My Row', + 'teamId' => '', // Always store teamId for querying + ], + permissions: [ + Permission::read(Role::team('', 'owner')), + Permission::read(Role::team('', 'admin')), + Permission::read(Role::team('', 'member')), + Permission::update(Role::team('', 'owner')), + Permission::update(Role::team('', 'admin')), + Permission::delete(Role::team('', 'owner')), + Permission::delete(Role::team('', 'admin')) + ] +);`, + createTable: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey(''); // Server SDK requires API key + +$tablesDB = new TablesDB($client); + +// Create table with team-based permissions +$tablesDB->createTable( + databaseId: '', + tableId: '', + name: '', + permissions: [ + Permission::create(Role::team('', 'member')), + Permission::read(Role::team('', 'member')), + Permission::update(Role::team('', 'admin')), + Permission::delete(Role::team('', 'owner')) + ] +);`, + listRows: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$tablesDB = new TablesDB($client); + +// ALWAYS filter by teamId to ensure tenant isolation +$response = $tablesDB->listRows( + databaseId: '', + tableId: '', + queries: [ + Query::equal('teamId', ''), // Critical: filter by team + Query::orderDesc('$createdAt'), + Query::limit(25) + ] +);`, + createFile: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$storage = new Storage($client); + +// Create file with team-based permissions +$storage->createFile( + bucketId: '', + fileId: '', + file: $fileInput, + permissions: [ + Permission::read(Role::team('', 'member')), + Permission::update(Role::team('', 'admin')), + Permission::delete(Role::team('', 'owner')) + ] +);`, + teamCreationFlow: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$teams = new Teams($client); + +$team = $teams->create( + teamId: ID::unique(), + name: 'Company Name' +); + +// Make creator an owner +$teams->createMembership( + teamId: $team['$id'], + roles: ['owner'], + userId: '' +);`, + inviteFlow: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$teams = new Teams($client); + +$invite = $teams->createMembership( + teamId: '', + roles: ['member'], // Default role + email: 'user@example.com', + url: 'https://yourapp.com/accept-invite' +); +// User receives email, clicks link, accepts invitation`, + memberListUI: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$teams = new Teams($client); + +$response = $teams->listMemberships(teamId: ''); +// Show list with role badges and action buttons`, + roleChange: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$teams = new Teams($client); + +$teams->updateMembership( + teamId: '', + membershipId: '', + roles: ['admin'] +);`, + memberRemoval: `setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setSession(''); + +$teams = new Teams($client); + +$teams->deleteMembership( + teamId: '', + membershipId: '' +);`, + roleCheck: `$response = $teams->listMemberships(teamId: ''); + +$userMembership = array_filter( + $response['memberships'], + fn($m) => $m['userId'] === '' +); + +if (empty($userMembership) || !in_array('admin', reset($userMembership)['roles'])) { + throw new Exception('Insufficient permissions'); +}` + }, + go: { + avoidUserPermissions: `// DON'T do this for multi-tenant apps +package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/tablesdb" + "github.com/appwrite/sdk-for-go/models" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +tablesDB := tablesdb.New(client) + +tablesDB.CreateRow( + "", + "", + "", + map[string]interface{}{ + "title": "My Row", + }, + []interface{}{ + models.PermissionRead(models.RoleUser("")), + models.PermissionWrite(models.RoleUser("")), + }, +)`, + preferTeamPermissions: `// DO this for multi-tenant apps +package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/tablesdb" + "github.com/appwrite/sdk-for-go/models" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +tablesDB := tablesdb.New(client) + +tablesDB.CreateRow( + "", + "", + "", + map[string]interface{}{ + "title": "My Row", + }, + []interface{}{ + models.PermissionRead(models.RoleTeam("", "owner")), + models.PermissionRead(models.RoleTeam("", "admin")), + models.PermissionRead(models.RoleTeam("", "member")), + models.PermissionUpdate(models.RoleTeam("", "owner")), + models.PermissionUpdate(models.RoleTeam("", "admin")), + models.PermissionDelete(models.RoleTeam("", "owner")), + }, +)`, + createTeam: `package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/teams" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +service := teams.New(client) + +// Create a team when a new tenant/organization signs up +service.Create( + "", + "", + teams.WithCreateRoles([]interface{}{"owner", "admin", "member"}), +)`, + createMembershipEmail: `package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/teams" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +service := teams.New(client) + +// Send team invitation (email-based) +service.CreateMembership( + "", + []interface{}{"admin", "member"}, + teams.WithCreateMembershipEmail("user@example.com"), + teams.WithCreateMembershipUrl("https://yourapp.com/accept-invite"), +)`, + createMembershipUserId: `package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/teams" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithKey(""), // Server SDK +) + +service := teams.New(client) + +// Or invite by user ID (if user already exists) +service.CreateMembership( + "", + []interface{}{"admin", "member"}, + teams.WithCreateMembershipUserId(""), +)`, + listMemberships: `package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/teams" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +service := teams.New(client) + +// Get all members of a team +response, _ := service.ListMemberships("") + +// Access member data +for _, membership := range response.Memberships { + fmt.Println(membership.UserId) + fmt.Println(membership.Roles) // Array of role strings + fmt.Println(membership.UserName) + fmt.Println(membership.UserEmail) +}`, + updateMembership: `package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/teams" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +service := teams.New(client) + +// Update a member's roles (only team owners/admins can do this) +service.UpdateMembership( + "", + "", + []interface{}{"admin", "member"}, +)`, + deleteMembership: `package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/teams" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +service := teams.New(client) + +// Remove a member from a team +service.DeleteMembership( + "", + "", +)`, + listTeams: `package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/teams" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +service := teams.New(client) + +// List all teams the current user belongs to +response, _ := service.List() + +for _, team := range response.Teams { + fmt.Println(team.Id) + fmt.Println(team.Name) +}`, + getUserRole: `package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/teams" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +service := teams.New(client) + +// Get membership details for current user in a specific team +response, _ := service.ListMemberships("") + +var userMembership *teams.Membership +for _, membership := range response.Memberships { + if membership.UserId == "" { + userMembership = &membership + break + } +} + +if userMembership != nil { + fmt.Println(userMembership.Roles) // ['owner', 'admin', etc.] + hasAdminRole := false + for _, role := range userMembership.Roles { + if role == "admin" { + hasAdminRole = true + break + } + } +}`, + createRow: `package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/tablesdb" + "github.com/appwrite/sdk-for-go/models" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +tablesDB := tablesdb.New(client) + +// Create row with team-based permissions +tablesDB.CreateRow( + "", + "", + "", + map[string]interface{}{ + "title": "My Row", + "teamId": "", // Always store teamId for querying + }, + []interface{}{ + models.PermissionRead(models.RoleTeam("", "owner")), + models.PermissionRead(models.RoleTeam("", "admin")), + models.PermissionRead(models.RoleTeam("", "member")), + models.PermissionUpdate(models.RoleTeam("", "owner")), + models.PermissionUpdate(models.RoleTeam("", "admin")), + models.PermissionDelete(models.RoleTeam("", "owner")), + models.PermissionDelete(models.RoleTeam("", "admin")), + }, +)`, + createTable: `package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/tablesdb" + "github.com/appwrite/sdk-for-go/models" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithKey(""), // Server SDK requires API key +) + +tablesDB := tablesdb.New(client) + +// Create table with team-based permissions +tablesDB.CreateTable( + "", + "", + "", + tablesdb.WithCreateTablePermissions([]interface{}{ + models.PermissionCreate(models.RoleTeam("", "member")), + models.PermissionRead(models.RoleTeam("", "member")), + models.PermissionUpdate(models.RoleTeam("", "admin")), + models.PermissionDelete(models.RoleTeam("", "owner")), + }), +)`, + listRows: `package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/tablesdb" + "github.com/appwrite/sdk-for-go/query" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +tablesDB := tablesdb.New(client) + +// ALWAYS filter by teamId to ensure tenant isolation +tablesDB.ListRows( + "", + "", + tablesdb.WithListRowsQueries([]interface{}{ + query.Equal("teamId", ""), // Critical: filter by team + query.OrderDesc("$createdAt"), + query.Limit(25), + }), +)`, + createFile: `package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/storage" + "github.com/appwrite/sdk-for-go/models" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +storageService := storage.New(client) + +// Create file with team-based permissions +storageService.CreateFile( + "", + "", + fileInput, + storage.WithCreateFilePermissions([]interface{}{ + models.PermissionRead(models.RoleTeam("", "member")), + models.PermissionUpdate(models.RoleTeam("", "admin")), + models.PermissionDelete(models.RoleTeam("", "owner")), + }), +)`, + teamCreationFlow: `// When user creates account/organization +package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/teams" + "github.com/appwrite/sdk-for-go/id" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +service := teams.New(client) + +team, _ := service.Create( + id.Unique(), + "Company Name", +) + +// Make creator an owner +service.CreateMembership( + team.Id, + []interface{}{"owner"}, + teams.WithCreateMembershipUserId(""), +)`, + inviteFlow: `// Owner/admin invites new member +package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/teams" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +service := teams.New(client) + +service.CreateMembership( + "", + []interface{}{"member"}, // Default role + teams.WithCreateMembershipEmail("user@example.com"), + teams.WithCreateMembershipUrl("https://yourapp.com/accept-invite"), +) +// User receives email, clicks link, accepts invitation`, + memberListUI: `// Display all team members with their roles +package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/teams" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +service := teams.New(client) + +response, _ := service.ListMemberships("") +// Show list with role badges and action buttons`, + roleChange: `// Admin/owner changes member role +package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/teams" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +service := teams.New(client) + +service.UpdateMembership( + "", + "", + []interface{}{"admin"}, +)`, + memberRemoval: `// Remove member (with confirmation) +package main + +import ( + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/teams" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithSession(""), +) + +service := teams.New(client) + +service.DeleteMembership( + "", + "", +)`, + roleCheck: `response, _ := service.ListMemberships("") + +var userMembership *teams.Membership +for _, membership := range response.Memberships { + if membership.UserId == "" { + userMembership = &membership + break + } +} + +hasAdminRole := false +if userMembership != nil { + for _, role := range userMembership.Roles { + if role == "admin" { + hasAdminRole = true + break + } + } +} + +if !hasAdminRole { + panic("Insufficient permissions") +}` + }, + ruby: { + avoidUserPermissions: `# DON'T do this for multi-tenant apps +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +tables_db = TablesDB.new(client) + +tables_db.create_row( + database_id: '', + table_id: '', + row_id: '', + data: {'title' => 'My Row'}, + permissions: [ + Permission.read(Role.user('')), + Permission.write(Role.user('')) + ] +)`, + preferTeamPermissions: `# DO this for multi-tenant apps +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +tables_db = TablesDB.new(client) + +tables_db.create_row( + database_id: '', + table_id: '', + row_id: '', + data: {'title' => 'My Row'}, + permissions: [ + Permission.read(Role.team('', 'owner')), + Permission.read(Role.team('', 'admin')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'owner')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +)`, + createTeam: `require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +teams = Teams.new(client) + +# Create a team when a new tenant/organization signs up +team = teams.create( + team_id: '', + name: '', + roles: ['owner', 'admin', 'member'] # optional +)`, + createMembershipEmail: `require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +teams = Teams.new(client) + +# Send team invitation (email-based) +invite = teams.create_membership( + team_id: '', + roles: ['admin', 'member'], + email: 'user@example.com', + url: 'https://yourapp.com/accept-invite' +)`, + createMembershipUserId: `require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') # Server SDK + +teams = Teams.new(client) + +# Or invite by user ID (if user already exists) +membership = teams.create_membership( + team_id: '', + roles: ['admin', 'member'], + user_id: '' +)`, + listMemberships: `require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +teams = Teams.new(client) + +# Get all members of a team +response = teams.list_memberships(team_id: '') + +# Access member data +response['memberships'].each do |membership| + puts membership['userId'] + puts membership['roles'] # Array of role strings + puts membership['userName'] + puts membership['userEmail'] +end`, + updateMembership: `require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +teams = Teams.new(client) + +# Update a member's roles (only team owners/admins can do this) +teams.update_membership( + team_id: '', + membership_id: '', + roles: ['admin', 'member'] +)`, + deleteMembership: `require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +teams = Teams.new(client) + +# Remove a member from a team +teams.delete_membership( + team_id: '', + membership_id: '' +)`, + listTeams: `require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +teams = Teams.new(client) + +# List all teams the current user belongs to +response = teams.list + +response['teams'].each do |team| + puts team['$id'] + puts team['name'] +end`, + getUserRole: `require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +teams = Teams.new(client) + +# Get membership details for current user in a specific team +response = teams.list_memberships(team_id: '') + +user_membership = response['memberships'].find do |m| + m['userId'] == '' +end + +if user_membership + puts user_membership['roles'] # ['owner', 'admin', etc.] + has_admin_role = user_membership['roles'].include?('admin') +end`, + createRow: `require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +tables_db = TablesDB.new(client) + +# Create row with team-based permissions +tables_db.create_row( + database_id: '', + table_id: '', + row_id: '', + data: { + 'title' => 'My Row', + 'teamId' => '', # Always store teamId for querying + }, + permissions: [ + Permission.read(Role.team('', 'owner')), + Permission.read(Role.team('', 'admin')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'owner')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')), + Permission.delete(Role.team('', 'admin')) + ] +)`, + createTable: `require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') # Server SDK requires API key + +tables_db = TablesDB.new(client) + +# Create table with team-based permissions +tables_db.create_table( + database_id: '', + table_id: '', + name: '', + permissions: [ + Permission.create(Role.team('', 'member')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +)`, + listRows: `require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +tables_db = TablesDB.new(client) + +# ALWAYS filter by teamId to ensure tenant isolation +response = tables_db.list_rows( + database_id: '', + table_id: '', + queries: [ + Query.equal('teamId', ''), # Critical: filter by team + Query.order_desc('$createdAt'), + Query.limit(25) + ] +)`, + createFile: `require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +storage = Storage.new(client) + +# Create file with team-based permissions +storage.create_file( + bucket_id: '', + file_id: '', + file: file_input, + permissions: [ + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +)`, + teamCreationFlow: `# When user creates account/organization +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +teams = Teams.new(client) + +team = teams.create( + team_id: ID.unique, + name: 'Company Name' +) + +# Make creator an owner +teams.create_membership( + team_id: team['$id'], + roles: ['owner'], + user_id: '' +)`, + inviteFlow: `# Owner/admin invites new member +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +teams = Teams.new(client) + +invite = teams.create_membership( + team_id: '', + roles: ['member'], # Default role + email: 'user@example.com', + url: 'https://yourapp.com/accept-invite' +) +# User receives email, clicks link, accepts invitation`, + memberListUI: `# Display all team members with their roles +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +teams = Teams.new(client) + +response = teams.list_memberships(team_id: '') +# Show list with role badges and action buttons`, + roleChange: `# Admin/owner changes member role +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +teams = Teams.new(client) + +teams.update_membership( + team_id: '', + membership_id: '', + roles: ['admin'] +)`, + memberRemoval: `# Remove member (with confirmation) +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_session('') + +teams = Teams.new(client) + +teams.delete_membership( + team_id: '', + membership_id: '' +)`, + roleCheck: `response = teams.list_memberships(team_id: '') + +user_membership = response['memberships'].find do |m| + m['userId'] == '' +end + +if !user_membership || !user_membership['roles'].include?('admin') + raise 'Insufficient permissions' +end` + }, + dotnet: { + avoidUserPermissions: `// DON'T do this for multi-tenant apps +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +TablesDB tablesDB = new TablesDB(client); + +await tablesDB.CreateRow( + databaseId: "", + tableId: "", + rowId: "", + data: new { title = "My Row" }, + permissions: new List { + Permission.Read(Role.User("")), + Permission.Write(Role.User("")) + } +);`, + preferTeamPermissions: `// DO this for multi-tenant apps +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +TablesDB tablesDB = new TablesDB(client); + +await tablesDB.CreateRow( + databaseId: "", + tableId: "", + rowId: "", + data: new { title = "My Row" }, + permissions: new List { + Permission.Read(Role.Team("", "owner")), + Permission.Read(Role.Team("", "admin")), + Permission.Read(Role.Team("", "member")), + Permission.Update(Role.Team("", "owner")), + Permission.Update(Role.Team("", "admin")), + Permission.Delete(Role.Team("", "owner")) + } +);`, + createTeam: `using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +Teams teams = new Teams(client); + +// Create a team when a new tenant/organization signs up +Team team = await teams.Create( + teamId: "", + name: "", + roles: new List { "owner", "admin", "member" } // optional +);`, + createMembershipEmail: `using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +Teams teams = new Teams(client); + +// Send team invitation (email-based) +Membership invite = await teams.CreateMembership( + teamId: "", + roles: new List { "admin", "member" }, + email: "user@example.com", + url: "https://yourapp.com/accept-invite" +);`, + createMembershipUserId: `using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetKey(""); // Server SDK + +Teams teams = new Teams(client); + +// Or invite by user ID (if user already exists) +Membership membership = await teams.CreateMembership( + teamId: "", + roles: new List { "admin", "member" }, + userId: "" +);`, + listMemberships: `using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +Teams teams = new Teams(client); + +// Get all members of a team +MembershipList response = await teams.ListMemberships(teamId: ""); + +// Access member data +foreach (var membership in response.Memberships) { + Console.WriteLine(membership.UserId); + Console.WriteLine(string.Join(", ", membership.Roles)); // Array of role strings + Console.WriteLine(membership.UserName); + Console.WriteLine(membership.UserEmail); +}`, + updateMembership: `using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +Teams teams = new Teams(client); + +// Update a member's roles (only team owners/admins can do this) +await teams.UpdateMembership( + teamId: "", + membershipId: "", + roles: new List { "admin", "member" } +);`, + deleteMembership: `using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +Teams teams = new Teams(client); + +// Remove a member from a team +await teams.DeleteMembership( + teamId: "", + membershipId: "" +);`, + listTeams: `using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +Teams teams = new Teams(client); + +// List all teams the current user belongs to +TeamList response = await teams.List(); + +foreach (var team in response.Teams) { + Console.WriteLine(team.Id); + Console.WriteLine(team.Name); +}`, + getUserRole: `using Appwrite; +using Appwrite.Models; +using Appwrite.Services; +using System.Linq; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +Teams teams = new Teams(client); + +// Get membership details for current user in a specific team +MembershipList response = await teams.ListMemberships(teamId: ""); + +var userMembership = response.Memberships.FirstOrDefault( + m => m.UserId == "" +); + +if (userMembership != null) { + Console.WriteLine(string.Join(", ", userMembership.Roles)); // ['owner', 'admin', etc.] + bool hasAdminRole = userMembership.Roles.Contains("admin"); +}`, + createRow: `using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +TablesDB tablesDB = new TablesDB(client); + +// Create row with team-based permissions +await tablesDB.CreateRow( + databaseId: "", + tableId: "", + rowId: "", + data: new { + title = "My Row", + teamId = "", // Always store teamId for querying + }, + permissions: new List { + Permission.Read(Role.Team("", "owner")), + Permission.Read(Role.Team("", "admin")), + Permission.Read(Role.Team("", "member")), + Permission.Update(Role.Team("", "owner")), + Permission.Update(Role.Team("", "admin")), + Permission.Delete(Role.Team("", "owner")), + Permission.Delete(Role.Team("", "admin")) + } +);`, + createTable: `using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetKey(""); // Server SDK requires API key + +TablesDB tablesDB = new TablesDB(client); + +// Create table with team-based permissions +await tablesDB.CreateTable( + databaseId: "", + tableId: "", + name: "", + permissions: new List { + Permission.Create(Role.Team("", "member")), + Permission.Read(Role.Team("", "member")), + Permission.Update(Role.Team("", "admin")), + Permission.Delete(Role.Team("", "owner")) + } +);`, + listRows: `using Appwrite; +using Appwrite.Models; +using Appwrite.Services; +using Appwrite.Query; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +TablesDB tablesDB = new TablesDB(client); + +// ALWAYS filter by teamId to ensure tenant isolation +RowList response = await tablesDB.ListRows( + databaseId: "", + tableId: "", + queries: new List { + Query.Equal("teamId", ""), // Critical: filter by team + Query.OrderDesc("$createdAt"), + Query.Limit(25) + } +);`, + createFile: `using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +Storage storage = new Storage(client); + +// Create file with team-based permissions +await storage.CreateFile( + bucketId: "", + fileId: "", + file: fileInput, + permissions: new List { + Permission.Read(Role.Team("", "member")), + Permission.Update(Role.Team("", "admin")), + Permission.Delete(Role.Team("", "owner")) + } +);`, + teamCreationFlow: `// When user creates account/organization +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; +using Appwrite.ID; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +Teams teams = new Teams(client); + +Team team = await teams.Create( + teamId: ID.Unique(), + name: "Company Name" +); + +// Make creator an owner +await teams.CreateMembership( + teamId: team.Id, + roles: new List { "owner" }, + userId: "" +);`, + inviteFlow: `// Owner/admin invites new member +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +Teams teams = new Teams(client); + +Membership invite = await teams.CreateMembership( + teamId: "", + roles: new List { "member" }, // Default role + email: "user@example.com", + url: "https://yourapp.com/accept-invite" +); +// User receives email, clicks link, accepts invitation`, + memberListUI: `// Display all team members with their roles +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +Teams teams = new Teams(client); + +MembershipList response = await teams.ListMemberships(teamId: ""); +// Show list with role badges and action buttons`, + roleChange: `// Admin/owner changes member role +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +Teams teams = new Teams(client); + +await teams.UpdateMembership( + teamId: "", + membershipId: "", + roles: new List { "admin" } +);`, + memberRemoval: `// Remove member (with confirmation) +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetSession(""); + +Teams teams = new Teams(client); + +await teams.DeleteMembership( + teamId: "", + membershipId: "" +);`, + roleCheck: `MembershipList response = await teams.ListMemberships(teamId: ""); + +var userMembership = response.Memberships.FirstOrDefault( + m => m.UserId == "" +); + +if (userMembership == null || !userMembership.Roles.Contains("admin")) { + throw new Exception("Insufficient permissions"); +}` + }, + swift: { + avoidUserPermissions: `// DON'T do this for multi-tenant apps +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let tablesDB = TablesDB(client) + +try await tablesDB.createRow( + databaseId: "", + tableId: "", + rowId: "", + data: ["title": "My Row"], + permissions: [ + Permission.read(Role.user("")), + Permission.write(Role.user("")) + ] +)`, + preferTeamPermissions: `// DO this for multi-tenant apps +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let tablesDB = TablesDB(client) + +try await tablesDB.createRow( + databaseId: "", + tableId: "", + rowId: "", + data: ["title": "My Row"], + permissions: [ + Permission.read(Role.team("", "owner")), + Permission.read(Role.team("", "admin")), + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "owner")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")) + ] +)`, + createTeam: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let teams = Teams(client) + +// Create a team when a new tenant/organization signs up +let team = try await teams.create( + teamId: "", + name: "", + roles: ["owner", "admin", "member"] // optional +)`, + createMembershipEmail: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let teams = Teams(client) + +// Send team invitation (email-based) +let invite = try await teams.createMembership( + teamId: "", + roles: ["admin", "member"], + email: "user@example.com", + url: "https://yourapp.com/accept-invite" +)`, + createMembershipUserId: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") // Server SDK + +let teams = Teams(client) + +// Or invite by user ID (if user already exists) +let membership = try await teams.createMembership( + teamId: "", + roles: ["admin", "member"], + userId: "" +)`, + listMemberships: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let teams = Teams(client) + +// Get all members of a team +let response = try await teams.listMemberships(teamId: "") + +// Access member data +for membership in response.memberships { + print(membership.userId) + print(membership.roles) // Array of role strings + print(membership.userName ?? "") + print(membership.userEmail ?? "") +}`, + updateMembership: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let teams = Teams(client) + +// Update a member's roles (only team owners/admins can do this) +try await teams.updateMembership( + teamId: "", + membershipId: "", + roles: ["admin", "member"] +)`, + deleteMembership: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let teams = Teams(client) + +// Remove a member from a team +try await teams.deleteMembership( + teamId: "", + membershipId: "" +)`, + listTeams: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let teams = Teams(client) + +// List all teams the current user belongs to +let response = try await teams.list() + +for team in response.teams { + print(team.id) + print(team.name) +}`, + getUserRole: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let teams = Teams(client) + +// Get membership details for current user in a specific team +let response = try await teams.listMemberships(teamId: "") + +let userMembership = response.memberships.first { $0.userId == "" } + +if let membership = userMembership { + print(membership.roles) // ['owner', 'admin', etc.] + let hasAdminRole = membership.roles.contains("admin") +}`, + createRow: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let tablesDB = TablesDB(client) + +// Create row with team-based permissions +try await tablesDB.createRow( + databaseId: "", + tableId: "", + rowId: "", + data: [ + "title": "My Row", + "teamId": "", // Always store teamId for querying + ], + permissions: [ + Permission.read(Role.team("", "owner")), + Permission.read(Role.team("", "admin")), + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "owner")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")), + Permission.delete(Role.team("", "admin")) + ] +)`, + createTable: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") // Server SDK requires API key + +let tablesDB = TablesDB(client) + +// Create table with team-based permissions +try await tablesDB.createTable( + databaseId: "", + tableId: "", + name: "", + permissions: [ + Permission.create(Role.team("", "member")), + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")) + ] +)`, + listRows: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let tablesDB = TablesDB(client) + +// ALWAYS filter by teamId to ensure tenant isolation +let response = try await tablesDB.listRows( + databaseId: "", + tableId: "", + queries: [ + Query.equal("teamId", ""), // Critical: filter by team + Query.orderDesc("$createdAt"), + Query.limit(25) + ] +)`, + createFile: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let storage = Storage(client) + +// Create file with team-based permissions +try await storage.createFile( + bucketId: "", + fileId: "", + file: fileInput, + permissions: [ + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")) + ] +)`, + teamCreationFlow: `// When user creates account/organization +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let teams = Teams(client) + +let team = try await teams.create( + teamId: ID.unique(), + name: "Company Name" +) + +// Make creator an owner +try await teams.createMembership( + teamId: team.id, + roles: ["owner"], + userId: "" +)`, + inviteFlow: `// Owner/admin invites new member +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let teams = Teams(client) + +let invite = try await teams.createMembership( + teamId: "", + roles: ["member"], // Default role + email: "user@example.com", + url: "https://yourapp.com/accept-invite" +) +// User receives email, clicks link, accepts invitation`, + memberListUI: `// Display all team members with their roles +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let teams = Teams(client) + +let response = try await teams.listMemberships(teamId: "") +// Show list with role badges and action buttons`, + roleChange: `// Admin/owner changes member role +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let teams = Teams(client) + +try await teams.updateMembership( + teamId: "", + membershipId: "", + roles: ["admin"] +)`, + memberRemoval: `// Remove member (with confirmation) +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +let teams = Teams(client) + +try await teams.deleteMembership( + teamId: "", + membershipId: "" +)`, + roleCheck: `let response = try await teams.listMemberships(teamId: "") + +let userMembership = response.memberships.first { $0.userId == "" } + +if userMembership == nil || !userMembership!.roles.contains("admin") { + throw NSError(domain: "Appwrite", code: 403, userInfo: [NSLocalizedDescriptionKey: "Insufficient permissions"]) +}` + }, + kotlin: { + avoidUserPermissions: `// DON'T do this for multi-tenant apps +import io.appwrite.Client +import io.appwrite.services.TablesDB +import io.appwrite.models.Permission +import io.appwrite.models.Role + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val tablesDB = TablesDB(client) + +tablesDB.createRow( + databaseId = "", + tableId = "", + rowId = "", + data = mapOf("title" to "My Row"), + permissions = listOf( + Permission.read(Role.user("")), + Permission.write(Role.user("")) + ) +)`, + preferTeamPermissions: `// DO this for multi-tenant apps +import io.appwrite.Client +import io.appwrite.services.TablesDB +import io.appwrite.models.Permission +import io.appwrite.models.Role + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val tablesDB = TablesDB(client) + +tablesDB.createRow( + databaseId = "", + tableId = "", + rowId = "", + data = mapOf("title" to "My Row"), + permissions = listOf( + Permission.read(Role.team("", "owner")), + Permission.read(Role.team("", "admin")), + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "owner")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")) + ) +)`, + createTeam: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val teams = Teams(client) + +// Create a team when a new tenant/organization signs up +teams.create( + "", // teamId + "", // name + listOf("owner", "admin", "member") // roles (optional) +)`, + createMembershipEmail: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val teams = Teams(client) + +// Send team invitation (email-based) +teams.createMembership( + "", // teamId + listOf("admin", "member"), // roles + "user@example.com", // email + null, // userId (optional) + null, // phone (optional) + "https://yourapp.com/accept-invite", // url + null // name (optional) +)`, + createMembershipUserId: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") // Server SDK + +val teams = Teams(client) + +// Or invite by user ID (if user already exists) +teams.createMembership( + "", // teamId + listOf("admin", "member"), // roles + null, // email + "", // userId + null, // phone + null, // url + null // name +)`, + listMemberships: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val teams = Teams(client) + +// Get all members of a team +val response = teams.listMemberships( + "", // teamId + listOf(), // queries (optional) + null // search (optional) +) + +// Access member data +response.memberships.forEach { membership -> + println(membership.userId) + println(membership.roles) // Array of role strings + println(membership.userName) + println(membership.userEmail) +}`, + updateMembership: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val teams = Teams(client) + +// Update a member's roles (only team owners/admins can do this) +teams.updateMembership( + "", // teamId + "", // membershipId + listOf("admin", "member") // roles +)`, + deleteMembership: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val teams = Teams(client) + +// Remove a member from a team +teams.deleteMembership( + "", // teamId + "" // membershipId +)`, + listTeams: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val teams = Teams(client) + +// List all teams the current user belongs to +val response = teams.list( + listOf(), // queries (optional) + null // search (optional) +) + +response.teams.forEach { team -> + println(team.id) + println(team.name) +}`, + getUserRole: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val teams = Teams(client) + +// Get membership details for current user in a specific team +val response = teams.listMemberships("") + +val userMembership = response.memberships.find { + it.userId == "" +} + +if (userMembership != null) { + println(userMembership.roles) // ['owner', 'admin', etc.] + val hasAdminRole = userMembership.roles.contains("admin") +}`, + createRow: `import io.appwrite.Client +import io.appwrite.services.TablesDB +import io.appwrite.models.Permission +import io.appwrite.models.Role + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val tablesDB = TablesDB(client) + +// Create row with team-based permissions +tablesDB.createRow( + databaseId = "", + tableId = "", + rowId = "", + data = mapOf( + "title" to "My Row", + "teamId" to "", // Always store teamId for querying + ), + permissions = listOf( + Permission.read(Role.team("", "owner")), + Permission.read(Role.team("", "admin")), + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "owner")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")), + Permission.delete(Role.team("", "admin")) + ) +)`, + createTable: `import io.appwrite.Client +import io.appwrite.services.TablesDB +import io.appwrite.models.Permission +import io.appwrite.models.Role + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") // Server SDK requires API key + +val tablesDB = TablesDB(client) + +// Create table with team-based permissions +tablesDB.createTable( + databaseId = "", + tableId = "", + name = "", + permissions = listOf( + Permission.create(Role.team("", "member")), + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")) + ) +)`, + listRows: `import io.appwrite.Client +import io.appwrite.services.TablesDB +import io.appwrite.Query + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val tablesDB = TablesDB(client) + +// ALWAYS filter by teamId to ensure tenant isolation +val response = tablesDB.listRows( + databaseId = "", + tableId = "", + queries = listOf( + Query.equal("teamId", ""), // Critical: filter by team + Query.orderDesc("$createdAt"), + Query.limit(25) + ) +)`, + createFile: `import io.appwrite.Client +import io.appwrite.services.Storage +import io.appwrite.models.Permission +import io.appwrite.models.Role + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val storage = Storage(client) + +// Create file with team-based permissions +storage.createFile( + bucketId = "", + fileId = "", + file = fileInput, + permissions = listOf( + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")) + ) +)`, + teamCreationFlow: `// When user creates account/organization +import io.appwrite.Client +import io.appwrite.services.Teams +import io.appwrite.ID + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val teams = Teams(client) + +val team = teams.create( + ID.unique(), + "Company Name" +) + +// Make creator an owner +teams.createMembership( + team.id, // teamId + listOf("owner"), // roles + null, // email + "", // userId + null, // phone + null, // url + null // name +)`, + inviteFlow: `// Owner/admin invites new member +import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val teams = Teams(client) + +teams.createMembership( + "", // teamId + listOf("member"), // Default role + "user@example.com", // email + null, // userId + null, // phone + "https://yourapp.com/accept-invite", // url + null // name +) +// User receives email, clicks link, accepts invitation`, + memberListUI: `// Display all team members with their roles +import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val teams = Teams(client) + +val response = teams.listMemberships("") +// Show list with role badges and action buttons`, + roleChange: `// Admin/owner changes member role +import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val teams = Teams(client) + +teams.updateMembership( + "", // teamId + "", // membershipId + listOf("admin") // roles +)`, + memberRemoval: `// Remove member (with confirmation) +import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setSession("") + +val teams = Teams(client) + +teams.deleteMembership( + "", // teamId + "" // membershipId +)`, + roleCheck: `val response = teams.listMemberships("") + +val userMembership = response.memberships.find { + it.userId == "" +} + +if (userMembership == null || !userMembership.roles.contains("admin")) { + throw Exception("Insufficient permissions") +}` + }, + apple: { + avoidUserPermissions: `// DON'T do this for multi-tenant apps +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let tablesDB = TablesDB(client) + +let row = try await tablesDB.createRow( + databaseId: "", + tableId: "", + rowId: "", + data: ["title": "My Row"], + permissions: [ + Permission.read(Role.user("")), + Permission.write(Role.user("")) + ] +)`, + preferTeamPermissions: `// DO this for multi-tenant apps +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let tablesDB = TablesDB(client) + +let row = try await tablesDB.createRow( + databaseId: "", + tableId: "", + rowId: "", + data: ["title": "My Row"], + permissions: [ + Permission.read(Role.team("", "owner")), + Permission.read(Role.team("", "admin")), + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "owner")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")) + ] +)`, + createTeam: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let teams = Teams(client) + +// Create a team when a new tenant/organization signs up +let team = try await teams.create( + teamId: "", + name: "", + roles: ["owner", "admin", "member"] // optional +)`, + createMembershipEmail: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let teams = Teams(client) + +// Send team invitation (email-based) +let membership = try await teams.createMembership( + teamId: "", + roles: ["admin", "member"], + email: "user@example.com", + url: "https://yourapp.com/accept-invite" +)`, + createMembershipUserId: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let teams = Teams(client) + +// Or invite by user ID (if user already exists) +let membership = try await teams.createMembership( + teamId: "", + roles: ["admin", "member"], + userId: "" +)`, + listMemberships: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let teams = Teams(client) + +// Get all members of a team +let response = try await teams.listMemberships( + teamId: "" +) + +// Access member data +for membership in response.memberships { + print(membership.userId) + print(membership.roles) // Array of role strings + print(membership.userName ?? "") + print(membership.userEmail ?? "") +}`, + updateMembership: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let teams = Teams(client) + +// Update a member's roles (only team owners/admins can do this) +let membership = try await teams.updateMembership( + teamId: "", + membershipId: "", + roles: ["admin", "member"] +)`, + deleteMembership: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let teams = Teams(client) + +// Remove a member from a team +try await teams.deleteMembership( + teamId: "", + membershipId: "" +)`, + listTeams: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let teams = Teams(client) + +// List all teams the current user belongs to +let response = try await teams.list() + +for team in response.teams { + print(team.id) + print(team.name) +}`, + getUserRole: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let teams = Teams(client) + +// Get membership details for current user in a specific team +let response = try await teams.listMemberships(teamId: "") + +let userMembership = response.memberships.first { $0.userId == "" } + +if let membership = userMembership { + print(membership.roles) // ['owner', 'admin', etc.] + let hasAdminRole = membership.roles.contains("admin") +}`, + createRow: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let tablesDB = TablesDB(client) + +// Create row with team-based permissions +let row = try await tablesDB.createRow( + databaseId: "", + tableId: "", + rowId: "", + data: [ + "title": "My Row", + "teamId": "", // Always store teamId for querying + ], + permissions: [ + Permission.read(Role.team("", "owner")), + Permission.read(Role.team("", "admin")), + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "owner")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")), + Permission.delete(Role.team("", "admin")) + ] +)`, + createTable: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") // Server SDK requires API key + +let tablesDB = TablesDB(client) + +// Create table with team-based permissions +let table = try await tablesDB.createTable( + databaseId: "", + tableId: "", + name: "", + permissions: [ + Permission.create(Role.team("", "member")), + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")) + ] +)`, + listRows: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let tablesDB = TablesDB(client) + +// ALWAYS filter by teamId to ensure tenant isolation +let response = try await tablesDB.listRows( + databaseId: "", + tableId: "", + queries: [ + Query.equal("teamId", ""), // Critical: filter by team + Query.orderDesc("$createdAt"), + Query.limit(25) + ] +)`, + createFile: `import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let storage = Storage(client) + +// Create file with team-based permissions +let file = try await storage.createFile( + bucketId: "", + fileId: "", + file: fileInput, + permissions: [ + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")) + ] +)`, + teamCreationFlow: `// When user creates account/organization +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let teams = Teams(client) + +let team = try await teams.create( + teamId: ID.unique(), + name: "Company Name" +) + +// Make creator an owner +try await teams.createMembership( + teamId: team.id, + roles: ["owner"], + userId: "" +)`, + inviteFlow: `// Owner/admin invites new member +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let teams = Teams(client) + +let invite = try await teams.createMembership( + teamId: "", + roles: ["member"], // Default role + email: "user@example.com", + url: "https://yourapp.com/accept-invite" +) +// User receives email, clicks link, accepts invitation`, + memberListUI: `// Display all team members with their roles +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let teams = Teams(client) + +let response = try await teams.listMemberships(teamId: "") +// Show list with role badges and action buttons`, + roleChange: `// Admin/owner changes member role +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let teams = Teams(client) + +let membership = try await teams.updateMembership( + teamId: "", + membershipId: "", + roles: ["admin"] +)`, + memberRemoval: `// Remove member (with confirmation) +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let teams = Teams(client) + +try await teams.deleteMembership( + teamId: "", + membershipId: "" +)`, + roleCheck: `let response = try await teams.listMemberships(teamId: "") + +let userMembership = response.memberships.first { $0.userId == "" } + +if userMembership == nil || !userMembership!.roles.contains("admin") { + throw NSError(domain: "Appwrite", code: 403, userInfo: [NSLocalizedDescriptionKey: "Insufficient permissions"]) +}` + }, + android: { + avoidUserPermissions: `// DON'T do this for multi-tenant apps +import io.appwrite.Client +import io.appwrite.services.TablesDB +import io.appwrite.models.Permission +import io.appwrite.models.Role + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val tablesDB = TablesDB(client) + +tablesDB.createRow( + databaseId = "", + tableId = "", + rowId = "", + data = mapOf("title" to "My Row"), + permissions = listOf( + Permission.read(Role.user("")), + Permission.write(Role.user("")) + ) +)`, + preferTeamPermissions: `// DO this for multi-tenant apps +import io.appwrite.Client +import io.appwrite.services.TablesDB +import io.appwrite.models.Permission +import io.appwrite.models.Role + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val tablesDB = TablesDB(client) + +tablesDB.createRow( + databaseId = "", + tableId = "", + rowId = "", + data = mapOf("title" to "My Row"), + permissions = listOf( + Permission.read(Role.team("", "owner")), + Permission.read(Role.team("", "admin")), + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "owner")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")) + ) +)`, + createTeam: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val teams = Teams(client) + +// Create a team when a new tenant/organization signs up +teams.create( + "", // teamId + "", // name + listOf("owner", "admin", "member") // roles (optional) +)`, + createMembershipEmail: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val teams = Teams(client) + +// Send team invitation (email-based) +teams.createMembership( + "", // teamId + listOf("admin", "member"), // roles + "user@example.com", // email + null, // userId (optional) + null, // phone (optional) + "https://yourapp.com/accept-invite", // url + null // name (optional) +)`, + createMembershipUserId: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val teams = Teams(client) + +// Or invite by user ID (if user already exists) +teams.createMembership( + "", // teamId + listOf("admin", "member"), // roles + null, // email + "", // userId + null, // phone + null, // url + null // name +)`, + listMemberships: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val teams = Teams(client) + +// Get all members of a team +val response = teams.listMemberships( + "", // teamId + listOf(), // queries (optional) + null // search (optional) +) + +// Access member data +response.memberships.forEach { membership -> + Log.d("Appwrite", membership.userId) + Log.d("Appwrite", membership.roles.toString()) // Array of role strings + Log.d("Appwrite", membership.userName ?: "") + Log.d("Appwrite", membership.userEmail ?: "") +}`, + updateMembership: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val teams = Teams(client) + +// Update a member's roles (only team owners/admins can do this) +teams.updateMembership( + "", // teamId + "", // membershipId + listOf("admin", "member") // roles +)`, + deleteMembership: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val teams = Teams(client) + +// Remove a member from a team +teams.deleteMembership( + "", // teamId + "" // membershipId +)`, + listTeams: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val teams = Teams(client) + +// List all teams the current user belongs to +val response = teams.list( + listOf(), // queries (optional) + null // search (optional) +) + +response.teams.forEach { team -> + Log.d("Appwrite", team.id) + Log.d("Appwrite", team.name) +}`, + getUserRole: `import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val teams = Teams(client) + +// Get membership details for current user in a specific team +val response = teams.listMemberships("") + +val userMembership = response.memberships.find { + it.userId == "" +} + +if (userMembership != null) { + Log.d("Appwrite", userMembership.roles.toString()) // ['owner', 'admin', etc.] + val hasAdminRole = userMembership.roles.contains("admin") +}`, + createRow: `import io.appwrite.Client +import io.appwrite.services.TablesDB +import io.appwrite.models.Permission +import io.appwrite.models.Role + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val tablesDB = TablesDB(client) + +// Create row with team-based permissions +tablesDB.createRow( + databaseId = "", + tableId = "", + rowId = "", + data = mapOf( + "title" to "My Row", + "teamId" to "", // Always store teamId for querying + ), + permissions = listOf( + Permission.read(Role.team("", "owner")), + Permission.read(Role.team("", "admin")), + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "owner")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")), + Permission.delete(Role.team("", "admin")) + ) +)`, + createTable: `import io.appwrite.Client +import io.appwrite.services.TablesDB +import io.appwrite.models.Permission +import io.appwrite.models.Role + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") // Server SDK requires API key + +val tablesDB = TablesDB(client) + +// Create table with team-based permissions +tablesDB.createTable( + databaseId = "", + tableId = "", + name = "", + permissions = listOf( + Permission.create(Role.team("", "member")), + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")) + ) +)`, + listRows: `import io.appwrite.Client +import io.appwrite.services.TablesDB +import io.appwrite.Query + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val tablesDB = TablesDB(client) + +// ALWAYS filter by teamId to ensure tenant isolation +val response = tablesDB.listRows( + databaseId = "", + tableId = "", + queries = listOf( + Query.equal("teamId", ""), // Critical: filter by team + Query.orderDesc("$createdAt"), + Query.limit(25) + ) +)`, + createFile: `import io.appwrite.Client +import io.appwrite.services.Storage +import io.appwrite.models.Permission +import io.appwrite.models.Role + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val storage = Storage(client) + +// Create file with team-based permissions +storage.createFile( + bucketId = "", + fileId = "", + file = fileInput, + permissions = listOf( + Permission.read(Role.team("", "member")), + Permission.update(Role.team("", "admin")), + Permission.delete(Role.team("", "owner")) + ) +)`, + teamCreationFlow: `// When user creates account/organization +import io.appwrite.Client +import io.appwrite.services.Teams +import io.appwrite.ID + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val teams = Teams(client) + +val team = teams.create( + ID.unique(), + "Company Name" +) + +// Make creator an owner +teams.createMembership( + team.id, // teamId + listOf("owner"), // roles + null, // email + "", // userId + null, // phone + null, // url + null // name +)`, + inviteFlow: `// Owner/admin invites new member +import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val teams = Teams(client) + +teams.createMembership( + "", // teamId + listOf("member"), // Default role + "user@example.com", // email + null, // userId + null, // phone + "https://yourapp.com/accept-invite", // url + null // name +) +// User receives email, clicks link, accepts invitation`, + memberListUI: `// Display all team members with their roles +import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val teams = Teams(client) + +val response = teams.listMemberships("") +// Show list with role badges and action buttons`, + roleChange: `// Admin/owner changes member role +import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val teams = Teams(client) + +teams.updateMembership( + "", // teamId + "", // membershipId + listOf("admin") // roles +)`, + memberRemoval: `// Remove member (with confirmation) +import io.appwrite.Client +import io.appwrite.services.Teams + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val teams = Teams(client) + +teams.deleteMembership( + "", // teamId + "" // membershipId +)`, + roleCheck: `val response = teams.listMemberships("") + +val userMembership = response.memberships.find { + it.userId == "" +} + +if (userMembership == null || !userMembership.roles.contains("admin")) { + throw Exception("Insufficient permissions") +}` + }, + flutter: { + avoidUserPermissions: `// DON'T do this for multi-tenant apps +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final tablesDB = TablesDB(client); + +final row = await tablesDB.createRow( + databaseId: '', + tableId: '', + rowId: '', + data: {'title': 'My Row'}, + permissions: [ + Permission.read(Role.user('')), + Permission.write(Role.user('')) + ] +);`, + preferTeamPermissions: `// DO this for multi-tenant apps +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final tablesDB = TablesDB(client); + +final row = await tablesDB.createRow( + databaseId: '', + tableId: '', + rowId: '', + data: {'title': 'My Row'}, + permissions: [ + Permission.read(Role.team('', 'owner')), + Permission.read(Role.team('', 'admin')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'owner')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +);`, + createTeam: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Create a team when a new tenant/organization signs up +final team = await teams.create( + teamId: '', + name: '', + roles: ['owner', 'admin', 'member'] // optional +);`, + createMembershipEmail: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Send team invitation (email-based) +final invite = await teams.createMembership( + teamId: '', + roles: ['admin', 'member'], + email: 'user@example.com', + url: 'https://yourapp.com/accept-invite' +);`, + createMembershipUserId: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Or invite by user ID (if user already exists) +final membership = await teams.createMembership( + teamId: '', + roles: ['admin', 'member'], + userId: '' +);`, + listMemberships: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Get all members of a team +final response = await teams.listMemberships(teamId: ''); + +// Access member data +for (final membership in response.memberships) { + print(membership.userId); + print(membership.roles); // Array of role strings + print(membership.userName); + print(membership.userEmail); +}`, + updateMembership: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Update a member's roles (only team owners/admins can do this) +await teams.updateMembership( + teamId: '', + membershipId: '', + roles: ['admin', 'member'] +);`, + deleteMembership: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Remove a member from a team +await teams.deleteMembership( + teamId: '', + membershipId: '' +);`, + listTeams: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// List all teams the current user belongs to +final response = await teams.list(); + +for (final team in response.teams) { + print(team.id); + print(team.name); +}`, + getUserRole: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Get membership details for current user in a specific team +final response = await teams.listMemberships(teamId: ''); + +final userMembership = response.memberships.firstWhere( + (m) => m.userId == '', + orElse: () => null, +); + +if (userMembership != null) { + print(userMembership.roles); // ['owner', 'admin', etc.] + final hasAdminRole = userMembership.roles.contains('admin'); +}`, + createRow: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final tablesDB = TablesDB(client); + +// Create row with team-based permissions +final row = await tablesDB.createRow( + databaseId: '', + tableId: '', + rowId: '', + data: { + 'title': 'My Row', + 'teamId': '', // Always store teamId for querying + }, + permissions: [ + Permission.read(Role.team('', 'owner')), + Permission.read(Role.team('', 'admin')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'owner')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')), + Permission.delete(Role.team('', 'admin')) + ] +);`, + createTable: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); // Server SDK requires API key + +final tablesDB = TablesDB(client); + +// Create table with team-based permissions +final table = await tablesDB.createTable( + databaseId: '', + tableId: '', + name: '', + permissions: [ + Permission.create(Role.team('', 'member')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +);`, + listRows: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final tablesDB = TablesDB(client); + +// ALWAYS filter by teamId to ensure tenant isolation +final response = await tablesDB.listRows( + databaseId: '', + tableId: '', + queries: [ + Query.equal('teamId', ''), // Critical: filter by team + Query.orderDesc('\$createdAt'), + Query.limit(25) + ] +);`, + createFile: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final storage = Storage(client); + +// Create file with team-based permissions +final file = await storage.createFile( + bucketId: '', + fileId: '', + file: fileInput, + permissions: [ + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +);`, + teamCreationFlow: `// When user creates account/organization +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +final team = await teams.create( + teamId: ID.unique(), + name: 'Company Name' +); + +// Make creator an owner +await teams.createMembership( + teamId: team.id, + roles: ['owner'], + userId: '' +);`, + inviteFlow: `// Owner/admin invites new member +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +final invite = await teams.createMembership( + teamId: '', + roles: ['member'], // Default role + email: 'user@example.com', + url: 'https://yourapp.com/accept-invite' +); +// User receives email, clicks link, accepts invitation`, + memberListUI: `// Display all team members with their roles +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +final response = await teams.listMemberships(teamId: ''); +// Show list with role badges and action buttons`, + roleChange: `// Admin/owner changes member role +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +await teams.updateMembership( + teamId: '', + membershipId: '', + roles: ['admin'] +);`, + memberRemoval: `// Remove member (with confirmation) +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +await teams.deleteMembership( + teamId: '', + membershipId: '' +);`, + roleCheck: `final response = await teams.listMemberships(teamId: ''); + +final userMembership = response.memberships.firstWhere( + (m) => m.userId == '', + orElse: () => null, +); + +if (userMembership == null || !userMembership.roles.contains('admin')) { + throw Exception('Insufficient permissions'); +}` + }, + dart: { + // Dart uses the same syntax as Flutter + avoidUserPermissions: `// DON'T do this for multi-tenant apps +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final tablesDB = TablesDB(client); + +final row = await tablesDB.createRow( + databaseId: '', + tableId: '', + rowId: '', + data: {'title': 'My Row'}, + permissions: [ + Permission.read(Role.user('')), + Permission.write(Role.user('')) + ] +);`, + preferTeamPermissions: `// DO this for multi-tenant apps +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final tablesDB = TablesDB(client); + +final row = await tablesDB.createRow( + databaseId: '', + tableId: '', + rowId: '', + data: {'title': 'My Row'}, + permissions: [ + Permission.read(Role.team('', 'owner')), + Permission.read(Role.team('', 'admin')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'owner')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +);`, + createTeam: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Create a team when a new tenant/organization signs up +final team = await teams.create( + teamId: '', + name: '', + roles: ['owner', 'admin', 'member'] // optional +);`, + createMembershipEmail: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Send team invitation (email-based) +final invite = await teams.createMembership( + teamId: '', + roles: ['admin', 'member'], + email: 'user@example.com', + url: 'https://yourapp.com/accept-invite' +);`, + createMembershipUserId: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Or invite by user ID (if user already exists) +final membership = await teams.createMembership( + teamId: '', + roles: ['admin', 'member'], + userId: '' +);`, + listMemberships: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Get all members of a team +final response = await teams.listMemberships(teamId: ''); + +// Access member data +for (final membership in response.memberships) { + print(membership.userId); + print(membership.roles); // Array of role strings + print(membership.userName); + print(membership.userEmail); +}`, + updateMembership: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Update a member's roles (only team owners/admins can do this) +await teams.updateMembership( + teamId: '', + membershipId: '', + roles: ['admin', 'member'] +);`, + deleteMembership: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Remove a member from a team +await teams.deleteMembership( + teamId: '', + membershipId: '' +);`, + listTeams: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// List all teams the current user belongs to +final response = await teams.list(); + +for (final team in response.teams) { + print(team.id); + print(team.name); +}`, + getUserRole: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +// Get membership details for current user in a specific team +final response = await teams.listMemberships(teamId: ''); + +final userMembership = response.memberships.firstWhere( + (m) => m.userId == '', + orElse: () => null, +); + +if (userMembership != null) { + print(userMembership.roles); // ['owner', 'admin', etc.] + final hasAdminRole = userMembership.roles.contains('admin'); +}`, + createRow: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final tablesDB = TablesDB(client); + +// Create row with team-based permissions +final row = await tablesDB.createRow( + databaseId: '', + tableId: '', + rowId: '', + data: { + 'title': 'My Row', + 'teamId': '', // Always store teamId for querying + }, + permissions: [ + Permission.read(Role.team('', 'owner')), + Permission.read(Role.team('', 'admin')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'owner')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')), + Permission.delete(Role.team('', 'admin')) + ] +);`, + createTable: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); // Server SDK requires API key + +final tablesDB = TablesDB(client); + +// Create table with team-based permissions +final table = await tablesDB.createTable( + databaseId: '', + tableId: '', + name: '', + permissions: [ + Permission.create(Role.team('', 'member')), + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +);`, + listRows: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final tablesDB = TablesDB(client); + +// ALWAYS filter by teamId to ensure tenant isolation +final response = await tablesDB.listRows( + databaseId: '', + tableId: '', + queries: [ + Query.equal('teamId', ''), // Critical: filter by team + Query.orderDesc('\$createdAt'), + Query.limit(25) + ] +);`, + createFile: `import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final storage = Storage(client); + +// Create file with team-based permissions +final file = await storage.createFile( + bucketId: '', + fileId: '', + file: fileInput, + permissions: [ + Permission.read(Role.team('', 'member')), + Permission.update(Role.team('', 'admin')), + Permission.delete(Role.team('', 'owner')) + ] +);`, + teamCreationFlow: `// When user creates account/organization +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +final team = await teams.create( + teamId: ID.unique(), + name: 'Company Name' +); + +// Make creator an owner +await teams.createMembership( + teamId: team.id, + roles: ['owner'], + userId: '' +);`, + inviteFlow: `// Owner/admin invites new member +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +final invite = await teams.createMembership( + teamId: '', + roles: ['member'], // Default role + email: 'user@example.com', + url: 'https://yourapp.com/accept-invite' +); +// User receives email, clicks link, accepts invitation`, + memberListUI: `// Display all team members with their roles +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +final response = await teams.listMemberships(teamId: ''); +// Show list with role badges and action buttons`, + roleChange: `// Admin/owner changes member role +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +await teams.updateMembership( + teamId: '', + membershipId: '', + roles: ['admin'] +);`, + memberRemoval: `// Remove member (with confirmation) +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final teams = Teams(client); + +await teams.deleteMembership( + teamId: '', + membershipId: '' +);`, + roleCheck: `final response = await teams.listMemberships(teamId: ''); + +final userMembership = response.memberships.firstWhere( + (m) => m.userId == '', + orElse: () => null, +); + +if (userMembership == null || !userMembership.roles.contains('admin')) { + throw Exception('Insufficient permissions'); +}` + } +}; + +/** + * Get permission examples for a specific SDK + * @param {string} sdk - The SDK name (javascript, python, php, go, etc.) + * @returns {Object} Permission examples for the SDK + */ +export function getPermissionExamples(sdk) { + // Map SDK names to their examples + /** @type {Record} */ + const sdkMap = { + javascript: permissionExamples.javascript, + 'react-native': permissionExamples['react-native'], + python: permissionExamples.python, + php: permissionExamples.php, + go: permissionExamples.go, + ruby: permissionExamples.ruby, + dotnet: permissionExamples.dotnet, + swift: permissionExamples.swift, + kotlin: permissionExamples.kotlin, + apple: permissionExamples.apple, + android: permissionExamples.android, + flutter: permissionExamples.flutter, + dart: permissionExamples.dart + }; + + return sdkMap[sdk] || permissionExamples.javascript; // Default to JavaScript +} + diff --git a/src/lib/languages/common/products.js b/src/lib/languages/common/products.js new file mode 100644 index 0000000..e01a13e --- /dev/null +++ b/src/lib/languages/common/products.js @@ -0,0 +1,110 @@ +/** + * Common product documentation links and descriptions + * These are reused across all SDKs and frameworks + */ + +/** + * Authentication product documentation + */ +export const authProductLinks = `For detailed authentication and session management instructions, see the [Authentication Quick Start Guide](https://appwrite.io/docs/products/auth/quick-start). + +For OAuth providers and social authentication, see the [OAuth2 Documentation](https://appwrite.io/docs/products/auth/oauth2). + +For server-side rendering (SSR) authentication, see the [SSR Login Guide](https://appwrite.io/docs/products/auth/server-side-rendering). + +For Teams management, team invitations, and team-based permissions, see the [Teams Documentation](https://appwrite.io/docs/products/auth/teams). + +For team invites and membership management, see the [Team Invites Guide](https://appwrite.io/docs/products/auth/team-invites). + +For multi-tenancy using Teams, see the [Multi-tenancy Guide](https://appwrite.io/docs/products/auth/multi-tenancy).`; + +/** + * Database product documentation + */ +export const databaseProductLinks = `For detailed database operations, see the [Rows Documentation](https://appwrite.io/docs/products/databases/rows). + +For querying and filtering data, see the [Queries Guide](https://appwrite.io/docs/products/databases/queries). + +For pagination, see the [Pagination Documentation](https://appwrite.io/docs/products/databases/pagination). + +For permissions and access control, see the [Permissions Guide](https://appwrite.io/docs/products/databases/permissions). + +For transactions, see the [Transactions Documentation](https://appwrite.io/docs/products/databases/transactions).`; + +/** + * Storage product documentation + */ +export const storageProductLinks = `For detailed storage operations, see the [Storage Documentation](https://appwrite.io/docs/products/storage/upload-download). + +For file uploads and downloads, see the [Upload & Download Guide](https://appwrite.io/docs/products/storage/upload-download). + +For permissions and access control, see the [Storage Permissions Documentation](https://appwrite.io/docs/products/storage/permissions).`; + +/** + * Functions product documentation + */ +export const functionsProductLinks = `For detailed serverless function execution, see the [Functions Execution Documentation](https://appwrite.io/docs/products/functions/execute). + +For function domains and custom endpoints, see the [Functions Domains Guide](https://appwrite.io/docs/products/functions/domains). + +For event-driven function execution, see the [Events Documentation](https://appwrite.io/docs/advanced/platform/events). + +For scheduled function execution, see the [Scheduled Executions Guide](https://appwrite.io/docs/products/functions/execute#schedule).`; + +/** + * Messaging product documentation + */ +export const messagingProductLinks = `For detailed messaging operations, see the [Messaging Documentation](https://appwrite.io/docs/products/messaging). + +For sending push notifications, see the [Push Notifications Guide](https://appwrite.io/docs/products/messaging/send-push-notifications). + +For sending emails, see the [Email Messages Guide](https://appwrite.io/docs/products/messaging/send-email-messages). + +For sending SMS messages, see the [SMS Messages Guide](https://appwrite.io/docs/products/messaging/send-sms-messages). + +For messaging providers, see the [Providers Documentation](https://appwrite.io/docs/products/messaging/providers).`; + +/** + * Sites product documentation + */ +export const sitesProductLinks = `For detailed Sites hosting and deployment, see the [Sites Documentation](https://appwrite.io/docs/products/sites). + +For getting started with Sites, see the [Sites Quick Start Guide](https://appwrite.io/docs/products/sites/quick-start). + +For deploying from Git, see the [Deploy from Git Guide](https://appwrite.io/docs/products/sites/deploy-from-git). + +For deploying from CLI, see the [Deploy from CLI Guide](https://appwrite.io/docs/products/sites/deploy-from-cli). + +For manual deployments, see the [Manual Deployment Guide](https://appwrite.io/docs/products/sites/deploy-manually). + +For deployment management, see the [Deployments Documentation](https://appwrite.io/docs/products/sites/deployments). + +For custom domains, see the [Domains Documentation](https://appwrite.io/docs/products/sites/domains). + +For rendering strategies (static vs SSR), see the [Rendering Documentation](https://appwrite.io/docs/products/sites/rendering). + +For static site hosting, see the [Static Rendering Guide](https://appwrite.io/docs/products/sites/rendering/static). + +For server-side rendering, see the [SSR Rendering Guide](https://appwrite.io/docs/products/sites/rendering/ssr). + +For supported frameworks, see the [Frameworks Documentation](https://appwrite.io/docs/products/sites/frameworks). + +For instant rollbacks, see the [Instant Rollbacks Guide](https://appwrite.io/docs/products/sites/instant-rollbacks). + +For deployment previews, see the [Previews Documentation](https://appwrite.io/docs/products/sites/previews).`; + +/** + * Realtime product documentation + */ +export const realtimeProductLinks = `For detailed subscriptions, see the [Realtime Documentation](https://appwrite.io/docs/products/realtime). + +For subscribing to database changes, see the [Database Subscriptions Guide](https://appwrite.io/docs/products/realtime/subscribe-to-databases). + +For subscribing to storage changes, see the [Storage Subscriptions Guide](https://appwrite.io/docs/products/realtime/subscribe-to-storage). + +For subscribing to account changes, see the [Account Subscriptions Guide](https://appwrite.io/docs/products/realtime/subscribe-to-account). + +For realtime channels and events, see the [Channels Documentation](https://appwrite.io/docs/products/realtime/channels). + +For realtime event types and payloads, see the [Events Documentation](https://appwrite.io/docs/products/realtime/events).`; + diff --git a/src/lib/languages/common/security.js b/src/lib/languages/common/security.js new file mode 100644 index 0000000..3a7f23f --- /dev/null +++ b/src/lib/languages/common/security.js @@ -0,0 +1,62 @@ +/** + * Common security best practices and guidelines + */ + +/** + * Client-side security best practices (for web frameworks) + */ +export const clientSecurity = `**Best Practices:** +- Store endpoint and project ID in environment variables +- Never commit API keys to version control +- Initialize services once and export as singletons`; + +/** + * Client-side security with environment variable note + * @param {string} envFile - Environment file name (e.g., '.env.local', '.env') + */ +export const clientSecurityWithEnv = (envFile = '.env.local') => `**Best Practices:** +- Store endpoint and project ID in environment variables (${envFile}) +- Never commit API keys to version control +- Initialize services once and export as singletons`; + +/** + * Server-side security best practices + */ +export const serverSecurity = `**Security:** +- API keys should NEVER be exposed to client-side code +- Use environment variables for all sensitive configuration +- API keys grant admin access - use with extreme caution`; + +/** + * Server-side security with framework-specific config note + * @param {string} configMethod - Configuration method (e.g., "configuration files or environment variables", "Rails credentials") + */ +export const serverSecurityWithConfig = (configMethod = 'environment variables') => `**Security:** +- API keys should NEVER be exposed to client-side code +- Use ${configMethod} for configuration +- API keys grant admin access - use with extreme caution`; + +/** + * Authentication-specific notes + */ +export const authNote = `**Authentication:** +- Prefer SSR auth for better security and performance`; + +/** + * Framework-specific notes + */ +export const frameworkNotes = { + angular: `**Best Practices:** +- Store endpoint and project ID in environment variables +- Never commit API keys to version control +- Use Angular services for dependency injection + +${authNote}`, + + nodejs: `**Best Practices:** +- Store endpoint and project ID in environment variables +- Never commit API keys to version control +- Use environment variables for configuration +- API keys grant admin access - use with extreme caution` +}; + diff --git a/src/lib/languages/common/utils.js b/src/lib/languages/common/utils.js new file mode 100644 index 0000000..6be628b --- /dev/null +++ b/src/lib/languages/common/utils.js @@ -0,0 +1,98 @@ +/** + * Utility functions for composing SDK initialization templates + */ + +/** + * Creates a security/best practices section (SDK Initialization section removed) + * @param {Object} options + * @param {string} options.securityNotes - Security/best practices section + * @param {string} [options.additionalNotes] - Additional framework-specific notes + * @returns {string} + */ +export function createSecuritySection({ + securityNotes, + additionalNotes = '' +}) { + return securityNotes + (additionalNotes ? `\n\n${additionalNotes}` : ''); +} + +/** + * Creates a complete framework template by combining installation and security notes + * @param {Object} options + * @param {string} options.installation - Installation section + * @param {string} options.securityNotes - Security/best practices section + * @param {string} [options.additionalNotes] - Additional framework-specific notes + * @returns {string} + */ +export function createFrameworkTemplate({ installation, securityNotes, additionalNotes = '' }) { + const securitySection = createSecuritySection({ securityNotes, additionalNotes }); + return `${installation} + +${securitySection}`; +} + +/** + * Quick Start Guide URLs by framework + */ +export const quickStartUrls = { + // JavaScript frameworks + react: 'https://appwrite.io/docs/quick-starts/react', + nextjs: 'https://appwrite.io/docs/quick-starts/nextjs', + vue: 'https://appwrite.io/docs/quick-starts/vue', + svelte: 'https://appwrite.io/docs/quick-starts/sveltekit', + angular: 'https://appwrite.io/docs/quick-starts/angular', + astro: 'https://appwrite.io/docs/quick-starts/astro', + nuxt: 'https://appwrite.io/docs/quick-starts/nuxt', + qwik: 'https://appwrite.io/docs/quick-starts/qwik', + solid: 'https://appwrite.io/docs/quick-starts/solid', + tanstack: 'https://appwrite.io/docs/quick-starts/tanstack', + nodejs: 'https://appwrite.io/docs/quick-starts/nodejs', + vanilla: 'https://appwrite.io/docs/quick-starts/web', + + // Mobile Client SDKs + apple: 'https://appwrite.io/docs/quick-starts/apple', + android: 'https://appwrite.io/docs/quick-starts/android', + flutter: 'https://appwrite.io/docs/quick-starts/flutter', + 'react-native': 'https://appwrite.io/docs/quick-starts/react-native', + + // Server SDKs + python: 'https://appwrite.io/docs/quick-starts/python', + php: 'https://appwrite.io/docs/quick-starts/php', + go: 'https://appwrite.io/docs/quick-starts/go', + ruby: 'https://appwrite.io/docs/quick-starts/ruby', + dotnet: 'https://appwrite.io/docs/quick-starts/dotnet', + dart: 'https://appwrite.io/docs/quick-starts/dart', + kotlin: 'https://appwrite.io/docs/quick-starts/kotlin', + swift: 'https://appwrite.io/docs/quick-starts/swift' +}; + +/** + * Framework display names + */ +export const frameworkNames = { + react: 'React', + nextjs: 'Next.js', + vue: 'Vue', + svelte: 'SvelteKit', + angular: 'Angular', + astro: 'Astro', + nuxt: 'Nuxt', + qwik: 'Qwik', + solid: 'Solid', + tanstack: 'TanStack', + nodejs: 'Node.js', + vanilla: 'Web', + apple: 'Apple', + android: 'Android', + flutter: 'Flutter', + 'react-native': 'React Native', + python: 'Python', + php: 'PHP', + go: 'Go', + ruby: 'Ruby', + dotnet: '.NET', + dart: 'Dart', + kotlin: 'Kotlin', + swift: 'Swift' +}; + diff --git a/src/lib/languages/dart/index.js b/src/lib/languages/dart/index.js new file mode 100644 index 0000000..8b82657 --- /dev/null +++ b/src/lib/languages/dart/index.js @@ -0,0 +1,26 @@ +import { getSDKVersion } from '$lib/utils/versions.js'; +import { dartInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; + +/** + * Gets the Flutter SDK installation template with the latest version from Appwrite's API + * This is the main export used by the rules generator for Flutter + * @returns {Promise} + */ +export const flutter = async () => { + const version = await getSDKVersion('client-flutter'); + const installation = dartInstall(version, false); + return createFrameworkTemplate({ installation, securityNotes: '' }); +}; + +/** + * Export vanilla as an alias to flutter for backwards compatibility + * @returns {Promise} + */ +export const vanilla = flutter; + +/** + * Export server from server.js + */ +export { server } from './server.js'; + diff --git a/src/lib/languages/dart/server.js b/src/lib/languages/dart/server.js new file mode 100644 index 0000000..a00c1f3 --- /dev/null +++ b/src/lib/languages/dart/server.js @@ -0,0 +1,15 @@ +import { getSDKVersion } from '$lib/utils/versions.js'; +import { dartInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { serverSecurity } from '../common/security.js'; + +/** + * Gets the Dart Server SDK installation template with the latest version from Appwrite's API + * @returns {Promise} + */ +export const server = async () => { + const version = await getSDKVersion('server-dart'); + const installation = dartInstall(version, true); + return createFrameworkTemplate({ installation, securityNotes: serverSecurity }); +}; + diff --git a/src/lib/languages/dotnet/index.js b/src/lib/languages/dotnet/index.js new file mode 100644 index 0000000..fac460d --- /dev/null +++ b/src/lib/languages/dotnet/index.js @@ -0,0 +1,3 @@ +export { server } from './server.js'; +export { vanilla } from './vanilla.js'; + diff --git a/src/lib/languages/dotnet/server.js b/src/lib/languages/dotnet/server.js new file mode 100644 index 0000000..d009aca --- /dev/null +++ b/src/lib/languages/dotnet/server.js @@ -0,0 +1,9 @@ +import { dotnetInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { serverSecurityWithConfig } from '../common/security.js'; + +export const server = createFrameworkTemplate({ + installation: dotnetInstall, + securityNotes: serverSecurityWithConfig('configuration files or environment variables') +}); + diff --git a/src/lib/languages/dotnet/vanilla.js b/src/lib/languages/dotnet/vanilla.js new file mode 100644 index 0000000..4b481e2 --- /dev/null +++ b/src/lib/languages/dotnet/vanilla.js @@ -0,0 +1,8 @@ +import { dotnetInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; + +export const vanilla = createFrameworkTemplate({ + installation: dotnetInstall, + securityNotes: '' +}); + diff --git a/src/lib/languages/go/index.js b/src/lib/languages/go/index.js new file mode 100644 index 0000000..5a95a78 --- /dev/null +++ b/src/lib/languages/go/index.js @@ -0,0 +1,2 @@ +export { server } from './server.js'; + diff --git a/src/lib/languages/go/server.js b/src/lib/languages/go/server.js new file mode 100644 index 0000000..1c6dfb7 --- /dev/null +++ b/src/lib/languages/go/server.js @@ -0,0 +1,9 @@ +import { goInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { serverSecurity } from '../common/security.js'; + +export const server = createFrameworkTemplate({ + installation: goInstall, + securityNotes: serverSecurity +}); + diff --git a/src/lib/languages/index.js b/src/lib/languages/index.js new file mode 100644 index 0000000..060ee08 --- /dev/null +++ b/src/lib/languages/index.js @@ -0,0 +1,13 @@ +export * as js from './js/index.js'; +export * as python from './python/index.js'; +export * as php from './php/index.js'; +export * as go from './go/index.js'; +export * as dart from './dart/index.js'; +export * as swift from './swift/index.js'; +export * as kotlin from './kotlin/index.js'; +export * as reactNative from './react-native/index.js'; +export * as ruby from './ruby/index.js'; +export * as dotnet from './dotnet/index.js'; +export * as apple from './apple/index.js'; +export * as android from './android/index.js'; + diff --git a/src/lib/languages/js/angular.js b/src/lib/languages/js/angular.js new file mode 100644 index 0000000..ba24fad --- /dev/null +++ b/src/lib/languages/js/angular.js @@ -0,0 +1,9 @@ +import { jsInstallDefault as jsInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { frameworkNotes } from '../common/security.js'; + +export const angular = createFrameworkTemplate({ + installation: jsInstall, + securityNotes: frameworkNotes.angular +}); + diff --git a/src/lib/languages/js/astro.js b/src/lib/languages/js/astro.js new file mode 100644 index 0000000..3726df4 --- /dev/null +++ b/src/lib/languages/js/astro.js @@ -0,0 +1,12 @@ +import { jsInstallDefault as jsInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { clientSecurityWithEnv, authNote } from '../common/security.js'; + +export const astro = createFrameworkTemplate({ + installation: jsInstall, + securityNotes: `${clientSecurityWithEnv('.env')} + +- Use separate clients for client-side and server-side operations`, + additionalNotes: authNote +}); + diff --git a/src/lib/languages/js/index.js b/src/lib/languages/js/index.js new file mode 100644 index 0000000..ce81dcd --- /dev/null +++ b/src/lib/languages/js/index.js @@ -0,0 +1,13 @@ +export { react } from './react.js'; +export { nextjs } from './nextjs.js'; +export { svelte } from './svelte.js'; +export { nodejs } from './nodejs.js'; +export { vanilla } from './vanilla.js'; +export { vue } from './vue.js'; +export { angular } from './angular.js'; +export { astro } from './astro.js'; +export { nuxt } from './nuxt.js'; +export { qwik } from './qwik.js'; +export { solid } from './solid.js'; +export { tanstack } from './tanstack.js'; + diff --git a/src/lib/languages/js/nextjs.js b/src/lib/languages/js/nextjs.js new file mode 100644 index 0000000..a418c40 --- /dev/null +++ b/src/lib/languages/js/nextjs.js @@ -0,0 +1,10 @@ +import { jsInstallDefault as jsInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { serverSecurity, authNote } from '../common/security.js'; + +export const nextjs = createFrameworkTemplate({ + installation: jsInstall, + securityNotes: serverSecurity, + additionalNotes: authNote +}); + diff --git a/src/lib/languages/js/nodejs.js b/src/lib/languages/js/nodejs.js new file mode 100644 index 0000000..5176ec4 --- /dev/null +++ b/src/lib/languages/js/nodejs.js @@ -0,0 +1,10 @@ +import { jsInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { serverSecurity } from '../common/security.js'; + +export const nodejs = createFrameworkTemplate({ + installation: jsInstall('node-appwrite', 'Install the Appwrite Node.js SDK'), + securityNotes: `${serverSecurity} +- Never log or expose API keys in error messages` +}); + diff --git a/src/lib/languages/js/nuxt.js b/src/lib/languages/js/nuxt.js new file mode 100644 index 0000000..3ad76a1 --- /dev/null +++ b/src/lib/languages/js/nuxt.js @@ -0,0 +1,13 @@ +import { jsInstallDefault as jsInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { authNote } from '../common/security.js'; + +export const nuxt = createFrameworkTemplate({ + installation: jsInstall, + securityNotes: `**Best Practices:** +- Store endpoint and project ID in \`nuxt.config.ts\` +- Never commit API keys to version control +- Use Nuxt plugins for client-side initialization`, + additionalNotes: authNote +}); + diff --git a/src/lib/languages/js/qwik.js b/src/lib/languages/js/qwik.js new file mode 100644 index 0000000..4da9355 --- /dev/null +++ b/src/lib/languages/js/qwik.js @@ -0,0 +1,17 @@ +import { jsInstallDefault as jsInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { clientSecurityWithEnv, authNote } from '../common/security.js'; + +export const qwik = createFrameworkTemplate({ + installation: jsInstall, + securityNotes: `${clientSecurityWithEnv('.env')} + +**Server-Side Security (Critical for Qwik):** +- API keys should NEVER be exposed to client-side code +- Separate client and server code: protect API keys on server, never expose them to client +- Use route loaders (\`routeLoader$\`) or server endpoints (\`server$\`) for secret operations +- For operations requiring API keys, use server actions or route loaders that run only on the server +- Client-side code should only use public endpoint and project ID`, + additionalNotes: authNote +}); + diff --git a/src/lib/languages/js/react.js b/src/lib/languages/js/react.js new file mode 100644 index 0000000..ea92424 --- /dev/null +++ b/src/lib/languages/js/react.js @@ -0,0 +1,9 @@ +import { jsInstallDefault as jsInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { clientSecurity } from '../common/security.js'; + +export const react = createFrameworkTemplate({ + installation: jsInstall, + securityNotes: clientSecurity +}); + diff --git a/src/lib/languages/js/solid.js b/src/lib/languages/js/solid.js new file mode 100644 index 0000000..006df4e --- /dev/null +++ b/src/lib/languages/js/solid.js @@ -0,0 +1,10 @@ +import { jsInstallDefault as jsInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { clientSecurityWithEnv, authNote } from '../common/security.js'; + +export const solid = createFrameworkTemplate({ + installation: jsInstall, + securityNotes: clientSecurityWithEnv('.env'), + additionalNotes: authNote +}); + diff --git a/src/lib/languages/js/svelte.js b/src/lib/languages/js/svelte.js new file mode 100644 index 0000000..673df65 --- /dev/null +++ b/src/lib/languages/js/svelte.js @@ -0,0 +1,10 @@ +import { jsInstallDefault as jsInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { clientSecurityWithEnv, authNote } from '../common/security.js'; + +export const svelte = createFrameworkTemplate({ + installation: jsInstall, + securityNotes: clientSecurityWithEnv('.env'), + additionalNotes: authNote +}); + diff --git a/src/lib/languages/js/tanstack.js b/src/lib/languages/js/tanstack.js new file mode 100644 index 0000000..68569d8 --- /dev/null +++ b/src/lib/languages/js/tanstack.js @@ -0,0 +1,17 @@ +import { jsInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { authNote } from '../common/security.js'; + +export const tanstack = createFrameworkTemplate({ + installation: jsInstall('appwrite @tanstack/react-query', 'Install the Appwrite JavaScript SDK and TanStack Query'), + securityNotes: `For TanStack Query integration examples and best practices, refer to the [TanStack Query Documentation](https://tanstack.com/query/latest). + +**Best Practices:** +- Use query keys consistently for cache management +- Invalidate queries after mutations to keep data fresh +- Use \`enabled\` option to conditionally fetch data +- Store endpoint and project ID in environment variables +- Never commit API keys to version control`, + additionalNotes: authNote +}); + diff --git a/src/lib/languages/js/vanilla.js b/src/lib/languages/js/vanilla.js new file mode 100644 index 0000000..9cf3bd1 --- /dev/null +++ b/src/lib/languages/js/vanilla.js @@ -0,0 +1,9 @@ +import { jsInstallDefault as jsInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { clientSecurity } from '../common/security.js'; + +export const vanilla = createFrameworkTemplate({ + installation: jsInstall, + securityNotes: clientSecurity.replace('- Initialize services once and export as singletons', '').trim() +}); + diff --git a/src/lib/languages/js/vue.js b/src/lib/languages/js/vue.js new file mode 100644 index 0000000..c8d5d8d --- /dev/null +++ b/src/lib/languages/js/vue.js @@ -0,0 +1,9 @@ +import { jsInstallDefault as jsInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { clientSecurityWithEnv } from '../common/security.js'; + +export const vue = createFrameworkTemplate({ + installation: jsInstall, + securityNotes: clientSecurityWithEnv('.env') +}); + diff --git a/src/lib/languages/kotlin/index.js b/src/lib/languages/kotlin/index.js new file mode 100644 index 0000000..9b0a111 --- /dev/null +++ b/src/lib/languages/kotlin/index.js @@ -0,0 +1,46 @@ +import { getSDKVersion } from '$lib/utils/versions.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { serverSecurity } from '../common/security.js'; + +/** + * Generates the Kotlin SDK installation template with the latest version + * @param {string} version - The SDK version to use + * @returns {string} + */ +function generateInstallationTemplate(version) { + return `## SDK Installation + +Add the Appwrite Kotlin SDK to your \`build.gradle.kts\`: + +**Recommended: Specify exact version for stability** + +\`\`\`kotlin +dependencies { + implementation("io.appwrite:sdk-for-kotlin:${version}") +} +\`\`\` + +Or for Maven, add to \`pom.xml\`: + +**Recommended: Specify exact version** + +\`\`\`xml + + io.appwrite + sdk-for-kotlin + ${version} + +\`\`\` +`; +} + +/** + * Gets the Kotlin SDK installation template with the latest version from Appwrite's API + * This is the main export used by the rules generator + * @returns {Promise} + */ +export const vanilla = async () => { + const version = await getSDKVersion('server-kotlin'); + const installation = generateInstallationTemplate(version); + return createFrameworkTemplate({ installation, securityNotes: serverSecurity }); +}; diff --git a/src/lib/languages/php/index.js b/src/lib/languages/php/index.js new file mode 100644 index 0000000..5a95a78 --- /dev/null +++ b/src/lib/languages/php/index.js @@ -0,0 +1,2 @@ +export { server } from './server.js'; + diff --git a/src/lib/languages/php/server.js b/src/lib/languages/php/server.js new file mode 100644 index 0000000..182f357 --- /dev/null +++ b/src/lib/languages/php/server.js @@ -0,0 +1,9 @@ +import { phpInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { serverSecurity } from '../common/security.js'; + +export const server = createFrameworkTemplate({ + installation: phpInstall, + securityNotes: serverSecurity +}); + diff --git a/src/lib/languages/python/flask.js b/src/lib/languages/python/flask.js new file mode 100644 index 0000000..9425800 --- /dev/null +++ b/src/lib/languages/python/flask.js @@ -0,0 +1,8 @@ +import { createSecuritySection } from '../common/utils.js'; + +export const flask = createSecuritySection({ + securityNotes: `**Best Practices:** +- Use environment variables for configuration +- Never commit API keys to version control` +}); + diff --git a/src/lib/languages/python/index.js b/src/lib/languages/python/index.js new file mode 100644 index 0000000..3ab6ffe --- /dev/null +++ b/src/lib/languages/python/index.js @@ -0,0 +1,3 @@ +export { flask } from './flask.js'; +export { server } from './server.js'; + diff --git a/src/lib/languages/python/server.js b/src/lib/languages/python/server.js new file mode 100644 index 0000000..0ceba5b --- /dev/null +++ b/src/lib/languages/python/server.js @@ -0,0 +1,9 @@ +import { pythonInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { serverSecurity } from '../common/security.js'; + +export const server = createFrameworkTemplate({ + installation: pythonInstall, + securityNotes: serverSecurity +}); + diff --git a/src/lib/languages/react-native/index.js b/src/lib/languages/react-native/index.js new file mode 100644 index 0000000..57da198 --- /dev/null +++ b/src/lib/languages/react-native/index.js @@ -0,0 +1,2 @@ +export { vanilla } from './vanilla.js'; + diff --git a/src/lib/languages/react-native/vanilla.js b/src/lib/languages/react-native/vanilla.js new file mode 100644 index 0000000..f125743 --- /dev/null +++ b/src/lib/languages/react-native/vanilla.js @@ -0,0 +1,11 @@ +import { jsInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; + +export const vanilla = createFrameworkTemplate({ + installation: jsInstall('react-native-appwrite', 'Install the Appwrite React Native SDK'), + securityNotes: `**Best Practices:** +- Store endpoint and project ID in environment variables or config files +- Never commit API keys to version control +- Initialize services once and export as singletons` +}); + diff --git a/src/lib/languages/ruby/index.js b/src/lib/languages/ruby/index.js new file mode 100644 index 0000000..5a95a78 --- /dev/null +++ b/src/lib/languages/ruby/index.js @@ -0,0 +1,2 @@ +export { server } from './server.js'; + diff --git a/src/lib/languages/ruby/server.js b/src/lib/languages/ruby/server.js new file mode 100644 index 0000000..f45ed75 --- /dev/null +++ b/src/lib/languages/ruby/server.js @@ -0,0 +1,9 @@ +import { rubyInstall } from '../common/install.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { serverSecurityWithConfig } from '../common/security.js'; + +export const server = createFrameworkTemplate({ + installation: rubyInstall, + securityNotes: serverSecurityWithConfig('environment variables or Rails credentials') +}); + diff --git a/src/lib/languages/swift/index.js b/src/lib/languages/swift/index.js new file mode 100644 index 0000000..4e33efc --- /dev/null +++ b/src/lib/languages/swift/index.js @@ -0,0 +1,37 @@ +import { getSDKVersion } from '$lib/utils/versions.js'; +import { createFrameworkTemplate } from '../common/utils.js'; +import { serverSecurity } from '../common/security.js'; + +/** + * Generates the Swift SDK installation template with the latest version + * @param {string} version - The SDK version to use + * @returns {string} + */ +function generateInstallationTemplate(version) { + return `## SDK Installation + +Add the Appwrite Swift Server SDK to your \`Package.swift\`: + +\`\`\`swift +dependencies: [ + .package(url: "https://github.com/appwrite/sdk-for-swift", from: "${version}") +] +\`\`\` + +Or add it via Xcode: +1. File → Add Packages... +2. Enter: \`https://github.com/appwrite/sdk-for-swift\` +3. Select version: \`${version}\` or later`; +} + +/** + * Gets the Swift SDK installation template with the latest version from Appwrite's API + * This is the main export used by the rules generator + * @returns {Promise} + */ +export const vanilla = async () => { + const version = await getSDKVersion('server-swift'); + const installation = generateInstallationTemplate(version); + return createFrameworkTemplate({ installation, securityNotes: serverSecurity }); +}; + diff --git a/src/lib/rules-generator.js b/src/lib/rules-generator.js new file mode 100644 index 0000000..49b50c1 --- /dev/null +++ b/src/lib/rules-generator.js @@ -0,0 +1,679 @@ +// Rules generator for different Appwrite SDKs and frameworks +import * as codeExamples from './languages/index.js'; +import { generateMCPRecommendation } from './languages/common/mcp.js'; + +/** @typedef {Object} SDKConfig + * @property {string} name + * @property {string[]} frameworks + * @property {string} importSyntax + * @property {string} exportSyntax + * @property {string} asyncSyntax + */ + +/** @typedef {Object} GeneratorConfig + * @property {string} sdk + * @property {string} framework + * @property {string[]} features + * @property {boolean} [includeMCP] - Whether to include MCP recommendation section + */ + +/** @type {Record} */ +export const SDK_OPTIONS = { + javascript: { + name: 'JavaScript/TypeScript', + frameworks: ['nextjs', 'react', 'vue', 'svelte', 'angular', 'astro', 'nuxt', 'qwik', 'solid', 'tanstack', 'nodejs', 'vanilla'], + importSyntax: 'import', + exportSyntax: 'export', + asyncSyntax: 'async/await' + }, + 'react-native': { + name: 'React Native', + frameworks: ['react-native', 'vanilla'], + importSyntax: 'import', + exportSyntax: 'export', + asyncSyntax: 'async/await' + }, + python: { + name: 'Python', + frameworks: ['flask', 'django', 'fastapi', 'server'], + importSyntax: 'from', + exportSyntax: 'def', + asyncSyntax: 'async def' + }, + flutter: { + name: 'Flutter/Dart', + frameworks: ['flutter', 'server'], + importSyntax: 'import', + exportSyntax: 'class', + asyncSyntax: 'Future' + }, + apple: { + name: 'Apple', + frameworks: ['vanilla'], + importSyntax: 'import', + exportSyntax: 'func', + asyncSyntax: 'async' + }, + android: { + name: 'Android', + frameworks: ['vanilla'], + importSyntax: 'import', + exportSyntax: 'fun', + asyncSyntax: 'suspend' + }, + swift: { + name: 'Swift', + frameworks: ['server', 'vanilla'], + importSyntax: 'import', + exportSyntax: 'func', + asyncSyntax: 'async' + }, + kotlin: { + name: 'Kotlin', + frameworks: ['server', 'vanilla'], + importSyntax: 'import', + exportSyntax: 'fun', + asyncSyntax: 'suspend' + }, + php: { + name: 'PHP', + frameworks: ['laravel', 'symfony', 'server'], + importSyntax: 'use', + exportSyntax: 'function', + asyncSyntax: 'async' + }, + go: { + name: 'Go', + frameworks: ['gin', 'fiber', 'server'], + importSyntax: 'import', + exportSyntax: 'func', + asyncSyntax: 'goroutine' + }, + ruby: { + name: 'Ruby', + frameworks: ['rails', 'server'], + importSyntax: 'require', + exportSyntax: 'def', + asyncSyntax: 'async' + }, + dotnet: { + name: '.NET', + frameworks: ['aspnet', 'server', 'vanilla'], + importSyntax: 'using', + exportSyntax: 'public', + asyncSyntax: 'async Task' + } +}; + +/** + * @param {GeneratorConfig} config + * @returns {Promise} + */ +export async function generateRules(config) { + const { sdk, framework, features, includeMCP = false } = config; + const sdkInfo = SDK_OPTIONS[sdk]; + + const sdkInit = await generateSDKInitialization(sdk, framework); + + // Generate all sections in parallel + // Permissions section is mandatory for all products + const sections = await Promise.all([ + features.includes('auth') ? generateAuthSection() : Promise.resolve(''), + generatePermissionsSection(sdk), + features.includes('database') ? generateDatabaseSection() : Promise.resolve(''), + features.includes('storage') ? generateStorageSection() : Promise.resolve(''), + features.includes('functions') ? generateFunctionsSection(sdk) : Promise.resolve(''), + features.includes('messaging') ? generateMessagingSection() : Promise.resolve(''), + features.includes('sites') ? generateSitesSection() : Promise.resolve(''), + features.includes('realtime') ? generateRealtimeSection() : Promise.resolve('') + ]); + + const mcpSection = includeMCP ? `${generateMCPRecommendation()}\n\n` : ''; + + let rules = `--- +description: You are an expert developer focused on building apps with Appwrite's ${sdkInfo?.name || sdk} SDK. +alwaysApply: false +--- + +# Appwrite Development Rules + +${mcpSection}${sdkInit} +${sections.join('\n\n')} +`; + + return rules; +} + + +/** + * @param {string} sdk + * @param {string} framework + * @returns {Promise} + */ +async function generateSDKInitialization(sdk, framework) { + /** @type {Record Promise)>>} */ + const templates = { + javascript: codeExamples.js, + 'react-native': codeExamples.reactNative, + python: codeExamples.python, + php: codeExamples.php, + go: codeExamples.go, + flutter: codeExamples.dart, + apple: codeExamples.apple, + android: codeExamples.android, + swift: codeExamples.swift, + kotlin: codeExamples.kotlin, + ruby: codeExamples.ruby, + dotnet: codeExamples.dotnet + }; + + const sdkTemplates = templates[sdk]; + if (sdkTemplates && sdkTemplates[framework]) { + const template = sdkTemplates[framework]; + // Check if it's an async function + if (typeof template === 'function') { + return await template(); + } + return template; + } + + // Fallback to vanilla if available + if (sdkTemplates && sdkTemplates.vanilla) { + const template = sdkTemplates.vanilla; + // Check if it's an async function + if (typeof template === 'function') { + return await template(); + } + return template; + } + + // Final fallback + return `## SDK Initialization + +Configure your Appwrite client for ${SDK_OPTIONS[sdk]?.name || sdk}.`; +} + +/** + * @returns {Promise} + */ +async function generateAuthSection() { + const { authProductLinks } = await import('./languages/common/products.js'); + return `## Authentication & Teams + +${authProductLinks} + +### Best Practices for Authentication & Teams + +- **Session Security**: Always use HttpOnly cookies for session storage in SSR applications +- **API Keys**: Never expose API keys to client-side code - use environment variables +- **Session Validation**: Always validate sessions on the server before trusting them +- **Team-Based Architecture**: ALWAYS prefer team/member-based roles over user-specific roles for any application requiring shared access or multi-tenancy +- **Multi-Tenant Applications**: Use teams as the primary mechanism for tenant isolation and resource sharing +- **OAuth Redirects**: Handle OAuth redirects properly with success and failure URLs +- **Password Security**: Use strong password requirements and consider implementing MFA +- **Session Expiry**: Configure appropriate session expiry times based on your security requirements + +### Team & Member Management Fundamentals + +When building applications that involve multiple users or tenants: + +1. **Always Start with Teams**: For any feature requiring shared access, create a team first, then add members with roles +2. **Role-Based Access**: Assign roles (e.g., "owner", "admin", "member", "viewer") to team members rather than setting individual user permissions +3. **Team Isolation**: Use teams as the boundary for data isolation in multi-tenant applications +4. **Member Invitations**: Implement team invitation workflows for onboarding new members +5. **Role Management**: Build role management UIs that allow team owners/admins to manage member roles dynamically`; +} + +/** + * @param {string} sdk + * @returns {Promise} + */ +async function generatePermissionsSection(sdk) { + const { authProductLinks } = await import('./languages/common/products.js'); + const { getPermissionExamples } = await import('./languages/common/permissions-examples.js'); + const examples = getPermissionExamples(sdk); + + return `## Permissions & Multi-Tenancy + +This section is CRITICAL for building secure, scalable applications with Appwrite. Multi-tenancy is one of the most important architectural patterns in modern applications, and Appwrite's team-based permission system is designed specifically for this. + +### Why Multi-Tenancy Matters + +Multi-tenancy allows a single application instance to serve multiple isolated groups of users (tenants) while maintaining complete data isolation and security. Almost every modern SaaS application requires multi-tenancy to scale efficiently. + +### Team/Member Roles vs User-Specific Roles: The Critical Distinction + +**ALWAYS PREFER TEAM/MEMBER-BASED ROLES over user-specific roles.** This is a fundamental architectural decision: + +#### Avoid: User-Specific Permissions +\`\`\`${getLanguageFromSdk(sdk)} +${examples.avoidUserPermissions} +\`\`\` + +**Problems with user-specific permissions:** +- Hard to scale when users need to share resources +- Difficult to add/remove access without updating every row +- No way to represent organizational hierarchies +- Poor support for collaborative features +- Maintenance nightmare as teams grow + +#### Prefer: Team/Member-Based Roles +\`\`\`${getLanguageFromSdk(sdk)} +${examples.preferTeamPermissions} +\`\`\` + +**Benefits of team/member-based roles:** +- Automatic access for all team members based on their role +- Easy to add/remove members without touching rows +- Scales naturally as teams grow +- Supports organizational hierarchies and complex permissions +- Industry-standard pattern for SaaS applications + +### Building Multi-Tenant Applications from Scratch + +#### Step 1: Create Teams Structure + +Teams in Appwrite represent tenants. Each team should map to a business entity (company, organization, workspace, etc.). + +**Creating a team:** +\`\`\`${getLanguageFromSdk(sdk)} +${examples.createTeam} +\`\`\` + +#### Step 2: Define Custom Roles + +Create roles that match your application's permission model. Common roles: +- **owner**: Full control, can manage team settings and members +- **admin**: Can manage resources and most settings, but not team membership +- **member**: Can create/edit resources, but with limited permissions +- **viewer**: Read-only access + +**Creating custom roles (Server-side only):** +\`\`\`${getLanguageFromSdk(sdk)} +// Define roles when creating the team (optional, defaults exist) +// Or create via Appwrite Console or Server SDK +// Roles are created per team, allowing different permission models per tenant +\`\`\` + +#### Step 3: Member Management from Scratch + +Member management is the foundation of multi-tenant applications. Here's how to build it: + +**A. Invite Members to Teams** + +\`\`\`${getLanguageFromSdk(sdk)} +${examples.createMembershipEmail} +\`\`\` + +\`\`\`${getLanguageFromSdk(sdk)} +${examples.createMembershipUserId} +\`\`\` + +**B. List Team Members** + +\`\`\`${getLanguageFromSdk(sdk)} +${examples.listMemberships} +\`\`\` + +**C. Update Member Roles** + +\`\`\`${getLanguageFromSdk(sdk)} +${examples.updateMembership} +\`\`\` + +**D. Remove Members** + +\`\`\`${getLanguageFromSdk(sdk)} +${examples.deleteMembership} +\`\`\` + +**E. Get Current User's Teams** + +\`\`\`${getLanguageFromSdk(sdk)} +${examples.listTeams} +\`\`\` + +**F. Get Current User's Role in a Team** + +\`\`\`${getLanguageFromSdk(sdk)} +${examples.getUserRole} +\`\`\` + +#### Step 4: Apply Permissions in Tables + +When creating rows in multi-tenant applications, always use team roles: + +**Database Tables:** + +\`\`\`${getLanguageFromSdk(sdk)} +${examples.createRow} +\`\`\` + +**Table-Level Permissions:** + +When creating tables, set default permissions: + +\`\`\`${getLanguageFromSdk(sdk)} +${examples.createTable} +\`\`\` + +#### Step 5: Query with Team Isolation + +Always filter queries by teamId to ensure data isolation: + +\`\`\`${getLanguageFromSdk(sdk)} +${examples.listRows} +\`\`\` + +#### Step 6: Storage Permissions + +Apply the same team-based permission pattern to storage: + +\`\`\`${getLanguageFromSdk(sdk)} +${examples.createFile} +\`\`\` + +### Complete Member Management Implementation Pattern + +Here's a complete pattern for building member management UI and logic: + +**1. Team Creation Flow:** +\`\`\`${getLanguageFromSdk(sdk)} +${examples.teamCreationFlow} +\`\`\` + +**2. Invite Flow:** +\`\`\`${getLanguageFromSdk(sdk)} +${examples.inviteFlow} +\`\`\` + +**3. Member List UI:** +\`\`\`${getLanguageFromSdk(sdk)} +${examples.memberListUI} +\`\`\` + +**4. Role Change:** +\`\`\`${getLanguageFromSdk(sdk)} +${examples.roleChange} +\`\`\` + +**5. Member Removal:** +\`\`\`${getLanguageFromSdk(sdk)} +${examples.memberRemoval} +\`\`\` + +### Permission Best Practices + +1. **Always Store teamId**: Every row/resource in a multi-tenant app should have a \`teamId\` field for filtering and isolation + +2. **Default Deny**: Don't grant permissions unless explicitly needed. Use minimal permission sets. + +3. **Role Hierarchy**: Design your roles to reflect natural hierarchies (owner > admin > member > viewer) + +4. **Permission Consistency**: Use the same permission pattern across database, storage, and other resources + +5. **Server-Side Validation**: Always validate team membership on the server side, even if client has permissions + +6. **Query Isolation**: Always include \`teamId\` in queries to prevent cross-tenant data leaks + +7. **Role Checks**: Before allowing sensitive operations, check the user's role in the team: + \`\`\`${getLanguageFromSdk(sdk)} +${examples.roleCheck} + \`\`\` + +8. **Permission Inheritance**: Consider if child resources should inherit parent team permissions + +9. **Row-Level Permissions**: For fine-grained control, set permissions on individual rows while still using team roles + +10. **Audit Trail**: Log permission changes and team membership changes for security auditing + +### Common Multi-Tenancy Patterns + +**Pattern 1: Workspace-Based (e.g., Notion, Slack)** +- Each workspace is a team +- Users can belong to multiple teams +- Resources belong to one team +- Perfect for: Collaboration tools, project management + +**Pattern 2: Organization-Based (e.g., GitHub, GitLab)** +- Each organization is a team +- Resources belong to organization +- Members have roles within organization +- Perfect for: Enterprise SaaS, developer tools + +**Pattern 3: Project-Based (e.g., Linear, Asana)** +- Each project is a team +- Resources scoped to project +- Members invited per project +- Perfect for: Project management, task tracking + +### Debugging Permission Issues + +When permissions aren't working: + +1. **Check Team Membership**: Verify user is actually a member of the team +2. **Verify Roles**: Ensure user has the required role (check \`membership.roles\`) +3. **Check Permission Strings**: Verify permission strings match exactly (case-sensitive) +4. **Query Filters**: Ensure \`teamId\` filters are applied correctly +5. **Server vs Client**: Some operations require server SDK (like creating custom roles) +6. **Session Context**: Permissions are evaluated in the context of the current session + +### Additional Resources + +${authProductLinks} + +For comprehensive permission patterns and examples, always refer to the official Appwrite documentation on Teams, Multi-tenancy, and Permissions.`; +} + +/** + * Get language identifier for code blocks based on SDK + * @param {string} sdk + * @returns {string} + */ +function getLanguageFromSdk(sdk) { + const languageMap = { + javascript: 'javascript', + 'react-native': 'javascript', + python: 'python', + php: 'php', + go: 'go', + flutter: 'dart', + dart: 'dart', + apple: 'swift', + android: 'kotlin', + swift: 'swift', + kotlin: 'kotlin', + ruby: 'ruby', + dotnet: 'csharp' + }; + return languageMap[sdk] || 'javascript'; +} + +/** + * @returns {Promise} + */ +async function generateDatabaseSection() { + const { databaseProductLinks } = await import('./languages/common/products.js'); + return `## Database Operations + +${databaseProductLinks} + +### Best Practices for Databases + +- **SDK Usage**: Always use \`TablesDB\` instead of \`Databases\` in the SDKs +- **Permissions & Multi-Tenancy**: ALWAYS use team/member-based roles for permissions (see Permissions & Multi-Tenancy section above). Never use user-specific permissions in multi-tenant applications +- **Tenant Isolation**: Always include \`teamId\` fields in your rows and filter queries by \`teamId\` to ensure complete data isolation between tenants +- **Permission Patterns**: Apply team roles (owner, admin, member, viewer) consistently across all tables. Use Role.team() for all permission checks +- **Query Security**: Every multi-tenant query MUST include a \`teamId\` filter to prevent cross-tenant data access +- **Table Permissions**: Set table-level permissions using team roles, then override at row level when needed +- **Query Optimization**: Use indexes for frequently queried fields, especially on \`teamId\` and commonly filtered fields +- **Data Validation**: Validate data before creating or updating rows, including team membership validation +- **Transactions**: Use transactions for operations that must succeed or fail together, ensuring atomicity across tenant boundaries +- **Pagination**: Always implement pagination for large datasets to improve performance and reduce response sizes +- **Type Safety**: Use type-safe models when available in your SDK for better code quality and fewer runtime errors`; +} + +/** + * @returns {Promise} + */ +async function generateStorageSection() { + const { storageProductLinks } = await import('./languages/common/products.js'); + return `## Storage Operations + +${storageProductLinks} + +### Best Practices for Storage + +- **Permissions & Multi-Tenancy**: ALWAYS use team/member-based roles for storage permissions (see Permissions & Multi-Tenancy section above). Apply Role.team() permissions to buckets and files for proper tenant isolation +- **Bucket Organization**: Consider organizing files by team/tenant using folder structures or bucket naming conventions for easier management +- **Tenant Isolation**: When querying files, always filter by metadata (e.g., \`teamId\`) to ensure users only access files from their teams +- **File Size Limits**: Set appropriate file size limits to prevent abuse and manage costs +- **File Types**: Validate file types before upload to ensure security and prevent malicious uploads +- **Permission Patterns**: Use team roles (owner, admin, member, viewer) consistently for bucket and file permissions, matching your database permission model +- **Cleanup**: Implement cleanup strategies for unused or temporary files, especially when teams are deleted +- **Virus Scanning**: Consider implementing virus scanning for uploaded files to protect all tenants +- **Access Control**: Validate team membership before allowing file uploads/downloads, even if permissions are set correctly`; +} + +/** + * Maps SDK names to their corresponding template paths in the Appwrite templates repository + * @param {string} sdk + * @returns {string|null} Template path or null if no template available + */ +function getFunctionTemplatePath(sdk) { + /** @type {Record} */ + const templateMap = { + javascript: 'node/starter', + 'react-native': 'node/starter', + python: 'python/starter', + php: 'php/starter', + go: 'go/starter', + flutter: 'dart/starter', + swift: 'swift/starter', + kotlin: 'kotlin/starter', + ruby: 'ruby/starter', + dotnet: 'dotnet/starter' + }; + return templateMap[sdk] || null; +} + +/** + * Generates template links section for functions + * @param {string} sdk + * @returns {string} + */ +function generateFunctionTemplateLinks(sdk) { + const templatePath = getFunctionTemplatePath(sdk); + if (!templatePath) { + return ''; + } + + const templateUrl = `https://github.com/appwrite/templates/tree/main/${templatePath}`; + const templatesBaseUrl = 'https://github.com/appwrite/templates'; + + return `### Starter Templates + +For getting started with Appwrite Functions, use the official starter template for your runtime: + +- **${SDK_OPTIONS[sdk]?.name || sdk} Starter**: [View Template](${templateUrl}) + +For more templates and examples, see the [Appwrite Templates Repository](${templatesBaseUrl}).`; +} + +/** + * @param {string} sdk + * @returns {Promise} + */ +async function generateFunctionsSection(sdk) { + const { functionsProductLinks } = await import('./languages/common/products.js'); + const templateLinks = generateFunctionTemplateLinks(sdk); + + return `## Functions + +${functionsProductLinks} + +${templateLinks} + +### When to Use Starter Templates + +**ALWAYS use starter templates from the [Appwrite Templates Repository](https://github.com/appwrite/templates) when building functions for:** + +- **Scheduled Tasks**: Functions that run on a schedule (cron jobs, periodic cleanup, etc.) +- **Event-Driven Tasks**: Functions triggered by Appwrite events (database changes, storage uploads, user events, etc.) +- **Background Processing**: Long-running or resource-intensive operations +- **Integration Functions**: Functions that integrate with third-party services (APIs, webhooks, etc.) +- **Complex Functions**: Any function that requires specific runtime configuration or dependencies + +**Why use templates?** Starter templates provide the correct project structure, dependencies, and configuration needed for functions to build and execute successfully. They ensure proper handling of environment variables, logging, error handling, and Appwrite SDK initialization. + +### Best Practices for Functions + +- **Error Handling**: Implement comprehensive error handling in your functions +- **Timeouts**: Be aware of function execution timeouts and optimize accordingly +- **Environment Variables**: Use environment variables for configuration, not hardcoded values +- **Logging**: Implement proper logging for debugging and monitoring +- **Security**: Validate all inputs and never trust user-provided data +- **Resource Limits**: Be mindful of memory and CPU limits for function executions +- **Template Usage**: Start with official templates for scheduled and event-driven functions to ensure proper setup`; +} + +/** + * @returns {Promise} + */ +async function generateMessagingSection() { + const { messagingProductLinks } = await import('./languages/common/products.js'); + return `## Messaging + +${messagingProductLinks} + +### Best Practices for Messaging + +- **Provider Selection**: Choose the right messaging provider based on your needs (FCM, APNS, Mailgun, Twilio, etc.) +- **Message Content**: Keep push notifications concise and actionable +- **Scheduling**: Use scheduled messages for better user engagement timing +- **Personalization**: Personalize messages to increase engagement +- **Rate Limiting**: Be mindful of rate limits when sending bulk messages +- **Error Handling**: Implement retry logic for failed message deliveries`; +} + +/** + * @returns {Promise} + */ +async function generateSitesSection() { + const { sitesProductLinks } = await import('./languages/common/products.js'); + return `## Sites + +${sitesProductLinks} + +### Best Practices for Sites + +- **Environment Variables**: Use environment variables for configuration, not hardcoded values +- **Custom Domains**: Configure custom domains for production sites for better branding +- **Rendering Strategy**: Choose between static and SSR based on your content needs and SEO requirements +- **Deployment Strategy**: Use Git deployments for automatic builds on commits +- **Rollback Plan**: Keep previous deployments ready for instant rollbacks if needed`; +} + +/** + * @returns {Promise} + */ +async function generateRealtimeSection() { + const { realtimeProductLinks } = await import('./languages/common/products.js'); + return `## Realtime Subscriptions + +${realtimeProductLinks} + +### Best Practices for Realtime Subscriptions + +- **Connection Management**: Always unsubscribe from channels when components unmount or pages are closed to prevent memory leaks +- **Error Handling**: Implement reconnection logic for dropped connections and handle network errors gracefully +- **Event Filtering**: Filter events on the client side to only process relevant updates for better performance +- **Channel Selection**: Subscribe only to the specific channels you need to minimize bandwidth and improve performance +- **Payload Validation**: Always validate payload data before processing to ensure data integrity +- **Rate Limiting**: Be mindful of the number of subscriptions and events to avoid overwhelming the client +- **State Synchronization**: Use realtime updates to keep local state in sync with server state, but handle conflicts appropriately +- **Authentication**: Ensure proper authentication is in place before subscribing to protected channels +- **Testing**: Test realtime functionality with network interruptions and reconnection scenarios +- **Cleanup**: Store unsubscribe functions and call them in cleanup hooks (useEffect cleanup, componentWillUnmount, etc.)`; +} + diff --git a/src/lib/utils/versions.js b/src/lib/utils/versions.js new file mode 100644 index 0000000..9771863 --- /dev/null +++ b/src/lib/utils/versions.js @@ -0,0 +1,61 @@ +/** + * Fetches the latest SDK versions from Appwrite's versions API + * @returns {Promise>} + */ +export async function fetchSDKVersions() { + try { + const response = await fetch('https://cloud.appwrite.io/versions'); + if (!response.ok) { + throw new Error(`Failed to fetch versions: ${response.statusText}`); + } + return await response.json(); + } catch (error) { + console.error('Error fetching SDK versions:', error); + // Return fallback versions if API is unavailable + return { + 'server-kotlin': '13.0.0', + 'client-flutter': '20.3.2', + 'client-apple': '13.4.0', + 'client-android': '11.3.0', + 'client-react-native': '0.18.0', + 'server-nodejs': '26.0.0', + 'server-php': '20.0.0', + 'server-python': '15.0.0', + 'server-ruby': '19.4.0', + 'server-go': 'v0.15.0', + 'server-dotnet': '0.23.0', + 'server-dart': '20.0.1', + 'server-swift': '14.0.0', + 'client-web': '21.4.0' + }; + } +} + +/** + * Gets the version for a specific SDK + * @param {string} sdkKey - The SDK key (e.g., 'server-kotlin', 'client-flutter') + * @returns {Promise} + */ +export async function getSDKVersion(sdkKey) { + const versions = await fetchSDKVersions(); + return versions[sdkKey] || 'latest'; +} + +/** + * Version cache to avoid multiple API calls + * @type {Record | null} + */ +let versionCache = null; + +/** + * Gets cached or fetches SDK versions + * @returns {Promise>} + */ +export async function getCachedVersions() { + if (versionCache) { + return versionCache; + } + versionCache = await fetchSDKVersions(); + return versionCache; +} + diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..5cdf9e2 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,13 @@ + + + + + + + +{@render children()} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..fc01412 --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,454 @@ + + + + Appwrite Rules Generator + + +
+ + +
+ {#if previewVisible && generatedRules} +
+
+

Generated Rules

+
+ + +
+
+
+
{generatedRules}
+
+
+ {:else} +
+
+

Select your options and click "Generate Rules" to see the output here.

+
+
+ {/if} +
+
+ + + + diff --git a/src/routes/api/rules/+server.js b/src/routes/api/rules/+server.js new file mode 100644 index 0000000..65f8a03 --- /dev/null +++ b/src/routes/api/rules/+server.js @@ -0,0 +1,85 @@ +import { generateRules, SDK_OPTIONS } from '$lib/rules-generator.js'; +import { json, text } from '@sveltejs/kit'; + +// All available features +const ALL_FEATURES = ['auth', 'database', 'storage', 'functions', 'messaging', 'sites', 'realtime']; + +/** + * Normalize features array - replace 'all' with all available features + * @param {string[]} features + * @returns {string[]} + */ +function normalizeFeatures(features) { + if (features.includes('all')) { + return ALL_FEATURES; + } + return features; +} + +/** + * @param {{ url: URL }} event + */ +export async function GET({ url }) { + try { + const sdk = url.searchParams.get('sdk') || 'javascript'; + const framework = url.searchParams.get('framework') || 'nextjs'; + const featuresParam = url.searchParams.get('features'); + const includeMCP = url.searchParams.get('mcp') === 'true'; + const format = url.searchParams.get('format') || 'text'; // 'text' or 'json' + + // Validate SDK + if (!SDK_OPTIONS[sdk]) { + return json( + { error: `Invalid SDK: ${sdk}. Available SDKs: ${Object.keys(SDK_OPTIONS).join(', ')}` }, + { status: 400 } + ); + } + + // Validate framework + const sdkInfo = SDK_OPTIONS[sdk]; + if (!sdkInfo.frameworks.includes(framework)) { + return json( + { + error: `Invalid framework: ${framework} for SDK: ${sdk}. Available frameworks: ${sdkInfo.frameworks.join(', ')}` + }, + { status: 400 } + ); + } + + // Parse and normalize features + const features = normalizeFeatures( + featuresParam ? featuresParam.split(',').filter(Boolean) : ['auth'] + ); + + // Generate rules + const rules = await generateRules({ + sdk, + framework, + features, + includeMCP + }); + + // Return based on format + if (format === 'json') { + return json({ + sdk, + framework, + features, + includeMCP, + rules + }); + } + + return text(rules, { + headers: { + 'Content-Type': 'text/markdown; charset=utf-8', + 'Content-Disposition': `attachment; filename="APPWRITE-${sdk}-${framework}.mdc"` + } + }); + } catch (error) { + console.error('Error generating rules:', error); + const errorMessage = error instanceof Error ? error.message : 'Failed to generate rules'; + return json({ error: errorMessage }, { status: 500 }); + } +} + diff --git a/src/routes/api/sdks/+server.js b/src/routes/api/sdks/+server.js new file mode 100644 index 0000000..ce9dd19 --- /dev/null +++ b/src/routes/api/sdks/+server.js @@ -0,0 +1,33 @@ +import { SDK_OPTIONS } from '$lib/rules-generator.js'; +import { json } from '@sveltejs/kit'; + +export async function GET() { + try { + const sdks = Object.entries(SDK_OPTIONS).map(([key, value]) => ({ + id: key, + name: value.name, + frameworks: value.frameworks, + importSyntax: value.importSyntax, + exportSyntax: value.exportSyntax, + asyncSyntax: value.asyncSyntax + })); + + return json({ + sdks, + availableFeatures: [ + 'auth', + 'database', + 'storage', + 'functions', + 'messaging', + 'sites', + 'realtime', + 'all' + ] + }); + } catch (error) { + console.error('Error fetching SDKs:', error); + return json({ error: error.message || 'Failed to fetch SDKs' }, { status: 500 }); + } +} + diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/svelte.config.js b/svelte.config.js new file mode 100644 index 0000000..1295460 --- /dev/null +++ b/svelte.config.js @@ -0,0 +1,18 @@ +import adapter from '@sveltejs/adapter-auto'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://svelte.dev/docs/kit/integrations + // for more information about preprocessors + preprocess: vitePreprocess(), + + kit: { + // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. + // If your environment is not supported, or you settled on a specific environment, switch out the adapter. + // See https://svelte.dev/docs/kit/adapters for more information about adapters. + adapter: adapter() + } +}; + +export default config; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2c2ed3c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "rewriteRelativeImportExtensions": true, + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..bbf8c7d --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,6 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [sveltekit()] +});