opencode-skills-antigravity 1.0.20 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +1330 -0
  2. package/bundled-skills/claude-monitor/scripts/api_bench.py +12 -1
  3. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  4. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  5. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  6. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  7. package/bundled-skills/docs/users/bundles.md +1 -1
  8. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  9. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  10. package/bundled-skills/docs/users/getting-started.md +1 -1
  11. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  12. package/bundled-skills/docs/users/usage.md +4 -4
  13. package/bundled-skills/docs/users/visual-guide.md +4 -4
  14. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +3 -3
  15. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package.json +1 -0
  16. package/bundled-skills/loki-mode/examples/todo-app-generated/frontend/package-lock.json +3 -3
  17. package/bundled-skills/loki-mode/examples/todo-app-generated/frontend/package.json +1 -0
  18. package/bundled-skills/phase-gated-debugging/SKILL.md +70 -0
  19. package/bundled-skills/saas-multi-tenant/SKILL.md +183 -0
  20. package/bundled-skills/threejs-animation/SKILL.md +13 -8
  21. package/bundled-skills/threejs-fundamentals/SKILL.md +44 -1
  22. package/bundled-skills/threejs-geometry/SKILL.md +30 -0
  23. package/bundled-skills/threejs-interaction/SKILL.md +12 -0
  24. package/bundled-skills/threejs-lighting/SKILL.md +3 -2
  25. package/bundled-skills/threejs-loaders/SKILL.md +21 -1
  26. package/bundled-skills/threejs-materials/SKILL.md +22 -0
  27. package/bundled-skills/threejs-postprocessing/SKILL.md +30 -9
  28. package/bundled-skills/threejs-shaders/SKILL.md +43 -0
  29. package/bundled-skills/threejs-skills/SKILL.md +103 -42
  30. package/bundled-skills/threejs-textures/SKILL.md +8 -0
  31. package/bundled-skills/whatsapp-cloud-api/scripts/send_test_message.py +18 -14
  32. package/bundled-skills/whatsapp-cloud-api/scripts/validate_config.py +54 -37
  33. package/package.json +1 -1
@@ -34,6 +34,17 @@ ENDPOINTS = [
34
34
  ]
35
35
 
36
36
 
37
+ def create_tls_context():
38
+ """Cria contexto TLS restringindo conexoes a TLS 1.2+."""
39
+ context = ssl.create_default_context()
40
+ if hasattr(ssl, "TLSVersion"):
41
+ context.minimum_version = ssl.TLSVersion.TLSv1_2
42
+ else:
43
+ context.options |= getattr(ssl, "OP_NO_TLSv1", 0)
44
+ context.options |= getattr(ssl, "OP_NO_TLSv1_1", 0)
45
+ return context
46
+
47
+
37
48
  def test_tcp_latency(host, port, timeout=5):
38
49
  """Testa latência TCP para um host:port."""
39
50
  try:
@@ -49,7 +60,7 @@ def test_tcp_latency(host, port, timeout=5):
49
60
  def test_tls_handshake(host, port=443, timeout=5):
50
61
  """Testa tempo do handshake TLS."""
51
62
  try:
52
- context = ssl.create_default_context()
63
+ context = create_tls_context()
53
64
  start = time.time()
54
65
  with socket.create_connection((host, port), timeout=timeout) as sock:
55
66
  with context.wrap_socket(sock, server_hostname=host) as ssock:
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  title: Jetski/Cortex + Gemini Integration Guide
3
- description: "Come usare antigravity-awesome-skills con Jetski/Cortex evitando l’overflow di contesto con 1.329+ skill."
3
+ description: "Come usare antigravity-awesome-skills con Jetski/Cortex evitando l’overflow di contesto con 1.331+ skill."
4
4
  ---
5
5
 
6
- # Jetski/Cortex + Gemini: integrazione sicura con 1.329+ skill
6
+ # Jetski/Cortex + Gemini: integrazione sicura con 1.331+ skill
7
7
 
8
8
  Questa guida mostra come integrare il repository `antigravity-awesome-skills` con un agente basato su **Jetski/Cortex + Gemini** (o framework simili) **senza superare il context window** del modello.
9
9
 
@@ -23,7 +23,7 @@ Non bisogna mai:
23
23
  - concatenare il contenuto di tutte le `SKILL.md` in un singolo system prompt;
24
24
  - reiniettare l’intera libreria per **ogni** richiesta.
25
25
 
26
- Con oltre 1.329 skill, questo approccio riempie il context window prima ancora di aggiungere i messaggi dell’utente, causando l’errore di truncation.
26
+ Con oltre 1.331 skill, questo approccio riempie il context window prima ancora di aggiungere i messaggi dell’utente, causando l’errore di truncation.
27
27
 
28
28
  ---
29
29
 
@@ -20,7 +20,7 @@ This example shows one way to integrate **antigravity-awesome-skills** with a Je
20
20
  - How to enforce a **maximum number of skills per turn** via `maxSkillsPerTurn`.
21
21
  - How to choose whether to **truncate or error** when too many skills are requested via `overflowBehavior`.
22
22
 
23
- This pattern avoids context overflow when you have 1,329+ skills installed.
23
+ This pattern avoids context overflow when you have 1,331+ skills installed.
24
24
 
25
25
  ---
26
26
 
@@ -6,7 +6,7 @@ This document keeps the repository's GitHub-facing discovery copy aligned with t
6
6
 
7
7
  Preferred positioning:
8
8
 
9
- > Installable GitHub library of 1,329+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.
9
+ > Installable GitHub library of 1,331+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.
10
10
 
11
11
  Key framing:
12
12
 
@@ -20,7 +20,7 @@ Key framing:
20
20
 
21
21
  Preferred description:
22
22
 
23
- > Installable GitHub library of 1,329+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.
23
+ > Installable GitHub library of 1,331+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.
24
24
 
25
25
  Preferred homepage:
26
26
 
@@ -28,7 +28,7 @@ Preferred homepage:
28
28
 
29
29
  Preferred social preview:
30
30
 
31
- - use a clean preview image that says `1,329+ Agentic Skills`;
31
+ - use a clean preview image that says `1,331+ Agentic Skills`;
32
32
  - mention Claude Code, Cursor, Codex CLI, and Gemini CLI;
33
33
  - avoid dense text and tiny logos that disappear in social cards.
34
34
 
@@ -69,7 +69,7 @@ For manual updates, you need:
69
69
  The update process refreshes:
70
70
  - Skills index (`skills_index.json`)
71
71
  - Web app skills data (`apps\web-app\public\skills.json`)
72
- - All 1,329+ skills from the skills directory
72
+ - All 1,331+ skills from the skills directory
73
73
 
74
74
  ## When to Update
75
75
 
@@ -673,4 +673,4 @@ Found a skill that should be in a bundle? Or want to create a new bundle? [Open
673
673
 
674
674
  ---
675
675
 
676
- _Last updated: March 2026 | Total Skills: 1,329+ | Total Bundles: 37_
676
+ _Last updated: March 2026 | Total Skills: 1,331+ | Total Bundles: 37_
@@ -12,7 +12,7 @@ Install the library into Claude Code, then invoke focused skills directly in the
12
12
 
13
13
  ## Why use this repo for Claude Code
14
14
 
15
- - It includes 1,329+ skills instead of a narrow single-domain starter pack.
15
+ - It includes 1,331+ skills instead of a narrow single-domain starter pack.
16
16
  - It supports the standard `.claude/skills/` path and the Claude Code plugin marketplace flow.
17
17
  - It also ships generated bundle plugins so teams can install focused packs like `Essentials` or `Security Developer` from the marketplace metadata.
18
18
  - It includes onboarding docs, bundles, and workflows so new users do not need to guess where to begin.
@@ -12,7 +12,7 @@ Install into the Gemini skills path, then ask Gemini to apply one skill at a tim
12
12
 
13
13
  - It installs directly into the expected Gemini skills path.
14
14
  - It includes both core software engineering skills and deeper agent/LLM-oriented skills.
15
- - It helps new users get started with bundles and workflows rather than forcing a cold start from 1,329+ files.
15
+ - It helps new users get started with bundles and workflows rather than forcing a cold start from 1,331+ files.
16
16
  - It is useful whether you want a broad internal skill library or a single repo to test many workflows quickly.
17
17
 
18
18
  ## Install Gemini CLI Skills
@@ -1,4 +1,4 @@
1
- # Getting Started with Antigravity Awesome Skills (V9.0.0)
1
+ # Getting Started with Antigravity Awesome Skills (V9.1.0)
2
2
 
3
3
  **New here? This guide will help you supercharge your AI Agent in 5 minutes.**
4
4
 
@@ -18,7 +18,7 @@ Kiro is AWS's agentic AI IDE that combines:
18
18
 
19
19
  Kiro's agentic capabilities are enhanced by skills that provide:
20
20
 
21
- - **Domain expertise** across 1,329+ specialized areas
21
+ - **Domain expertise** across 1,331+ specialized areas
22
22
  - **Best practices** from Anthropic, OpenAI, Google, Microsoft, and AWS
23
23
  - **Workflow automation** for common development tasks
24
24
  - **AWS-specific patterns** for serverless, infrastructure, and cloud architecture
@@ -14,7 +14,7 @@ If you came in through a **Claude Code** or **Codex** plugin instead of a full l
14
14
 
15
15
  When you ran `npx antigravity-awesome-skills` or cloned the repository, you:
16
16
 
17
- ✅ **Downloaded 1,329+ skill files** to your computer (default: `~/.gemini/antigravity/skills/`; or a custom path like `~/.agent/skills/` if you used `--path`)
17
+ ✅ **Downloaded 1,331+ skill files** to your computer (default: `~/.gemini/antigravity/skills/`; or a custom path like `~/.agent/skills/` if you used `--path`)
18
18
  ✅ **Made them available** to your AI assistant
19
19
  ❌ **Did NOT enable them all automatically** (they're just sitting there, waiting)
20
20
 
@@ -34,7 +34,7 @@ Bundles are **curated groups** of skills organized by role. They help you decide
34
34
 
35
35
  **Analogy:**
36
36
 
37
- - You installed a toolbox with 1,329+ tools (✅ done)
37
+ - You installed a toolbox with 1,331+ tools (✅ done)
38
38
  - Bundles are like **labeled organizer trays** saying: "If you're a carpenter, start with these 10 tools"
39
39
  - You can either **pick skills from the tray** or install that tray as a focused marketplace bundle plugin
40
40
 
@@ -212,7 +212,7 @@ Let's actually use a skill right now. Follow these steps:
212
212
 
213
213
  ## Step 5: Picking Your First Skills (Practical Advice)
214
214
 
215
- Don't try to use all 1,329+ skills at once. Here's a sensible approach:
215
+ Don't try to use all 1,331+ skills at once. Here's a sensible approach:
216
216
 
217
217
  If you want a tool-specific starting point before choosing skills, use:
218
218
 
@@ -343,7 +343,7 @@ Usually no, but if your AI doesn't recognize a skill:
343
343
 
344
344
  ### "Can I load all skills into the model at once?"
345
345
 
346
- No. Even though you have 1,329+ skills installed locally, you should **not** concatenate every `SKILL.md` into a single system prompt or context block.
346
+ No. Even though you have 1,331+ skills installed locally, you should **not** concatenate every `SKILL.md` into a single system prompt or context block.
347
347
 
348
348
  The intended pattern is:
349
349
 
@@ -34,7 +34,7 @@ antigravity-awesome-skills/
34
34
  ├── 📄 CONTRIBUTING.md ← Contributor workflow
35
35
  ├── 📄 CATALOG.md ← Full generated catalog
36
36
 
37
- ├── 📁 skills/ ← 1,329+ skills live here
37
+ ├── 📁 skills/ ← 1,331+ skills live here
38
38
  │ │
39
39
  │ ├── 📁 brainstorming/
40
40
  │ │ └── 📄 SKILL.md ← Skill definition
@@ -47,7 +47,7 @@ antigravity-awesome-skills/
47
47
  │ │ └── 📁 2d-games/
48
48
  │ │ └── 📄 SKILL.md ← Nested skills also supported
49
49
  │ │
50
- │ └── ... (1,329+ total)
50
+ │ └── ... (1,331+ total)
51
51
 
52
52
  ├── 📁 apps/
53
53
  │ └── 📁 web-app/ ← Interactive browser
@@ -100,7 +100,7 @@ antigravity-awesome-skills/
100
100
 
101
101
  ```
102
102
  ┌─────────────────────────┐
103
- │ 1,329+ SKILLS │
103
+ │ 1,331+ SKILLS │
104
104
  └────────────┬────────────┘
105
105
 
106
106
  ┌────────────────────────┼────────────────────────┐
@@ -201,7 +201,7 @@ If you want a workspace-style manual install instead, cloning into `.agent/skill
201
201
  │ ├── 📁 brainstorming/ │
202
202
  │ ├── 📁 stripe-integration/ │
203
203
  │ ├── 📁 react-best-practices/ │
204
- │ └── ... (1,329+ total) │
204
+ │ └── ... (1,331+ total) │
205
205
  └─────────────────────────────────────────┘
206
206
  ```
207
207
 
@@ -1110,9 +1110,9 @@
1110
1110
  }
1111
1111
  },
1112
1112
  "node_modules/path-to-regexp": {
1113
- "version": "0.1.12",
1114
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
1115
- "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
1113
+ "version": "0.1.13",
1114
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
1115
+ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
1116
1116
  "license": "MIT"
1117
1117
  },
1118
1118
  "node_modules/prebuild-install": {
@@ -24,6 +24,7 @@
24
24
  },
25
25
  "overrides": {
26
26
  "diff": "4.0.4",
27
+ "path-to-regexp": "0.1.13",
27
28
  "qs": "^6.15.0"
28
29
  }
29
30
  }
@@ -1527,9 +1527,9 @@
1527
1527
  "license": "ISC"
1528
1528
  },
1529
1529
  "node_modules/picomatch": {
1530
- "version": "4.0.3",
1531
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
1532
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
1530
+ "version": "4.0.4",
1531
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
1532
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
1533
1533
  "dev": true,
1534
1534
  "license": "MIT",
1535
1535
  "engines": {
@@ -23,6 +23,7 @@
23
23
  "react-dom": "^19.2.3"
24
24
  },
25
25
  "overrides": {
26
+ "picomatch": "4.0.4",
26
27
  "rollup": "^4.59.0"
27
28
  }
28
29
  }
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: phase-gated-debugging
3
+ description: "Use when debugging any bug. Enforces a 5-phase protocol where code edits are blocked until root cause is confirmed. Prevents premature fix attempts."
4
+ risk: safe
5
+ source: community
6
+ date_added: "2026-03-28"
7
+ ---
8
+
9
+ # Phase-Gated Debugging
10
+
11
+ ## Overview
12
+
13
+ AI coding agents see an error and immediately edit code. They guess at fixes, get it wrong, and spiral. This skill enforces a strict 5-phase protocol where you CANNOT edit source code until the root cause is identified and confirmed.
14
+
15
+ Based on [claude-debug](https://github.com/krabat-l/claude-debug) (full plugin with PreToolUse hook enforcement).
16
+
17
+ ## When to Use
18
+
19
+ Use this skill when:
20
+
21
+ - a bug keeps getting "fixed" without resolving the underlying issue
22
+ - you need to slow an agent down and force disciplined debugging before code edits
23
+ - the failure is intermittent, a regression, performance-related, or otherwise hard to isolate
24
+ - you want an explicit user confirmation checkpoint before any fix is applied
25
+
26
+ ## The Protocol
27
+
28
+ ### Phase 1: REPRODUCE
29
+ Run the failing command/test. Capture the exact error. Run 2-3 times for consistency.
30
+ - Do NOT read source code
31
+ - Do NOT hypothesize
32
+ - Do NOT edit any files
33
+
34
+ ### Phase 2: ISOLATE
35
+ Read code. Add diagnostic logging marked `// DEBUG`. Re-run with diagnostics. Binary search to narrow down.
36
+ - Only `// DEBUG` marked logging is allowed
37
+ - Do NOT fix the bug even if you see it
38
+
39
+ ### Phase 3: ROOT CAUSE
40
+ Analyze WHY at the isolated location. Use "5 Whys" technique. Remove debug logging.
41
+
42
+ State: "This is my root cause analysis: [explanation]. Do you agree, or should I investigate further?"
43
+
44
+ **WAIT for user confirmation. Do NOT proceed without it.**
45
+
46
+ ### Phase 4: FIX
47
+ Remove all `// DEBUG` lines. Apply minimal change addressing confirmed root cause.
48
+ - Only edit files related to root cause
49
+ - Do NOT refactor unrelated code
50
+
51
+ ### Phase 5: VERIFY
52
+ Run original failing test — must pass. Run related tests. For intermittent bugs, run 5+ times.
53
+ If verification fails: root cause was wrong, go back to Phase 2.
54
+
55
+ ## Bug-Type Strategies
56
+
57
+ | Type | Technique |
58
+ |------|-----------|
59
+ | Crash/Panic | Stack trace backward — trace the bad value to its source |
60
+ | Wrong Output | Binary search — log midpoint, halve search space each iteration |
61
+ | Intermittent | Compare passing vs failing run logs — find ordering divergence |
62
+ | Regression | `git bisect` — find the offending commit |
63
+ | Performance | Timing at stage boundaries — find the bottleneck |
64
+
65
+ ## Key Rules
66
+
67
+ 1. NEVER edit source code in phases 1-3 (except `// DEBUG` in phase 2)
68
+ 2. NEVER proceed past phase 3 without user confirmation
69
+ 3. ALWAYS reproduce before investigating
70
+ 4. ALWAYS verify after fixing
@@ -0,0 +1,183 @@
1
+ ---
2
+ name: saas-multi-tenant
3
+ description: "Design and implement multi-tenant SaaS architectures with row-level security, tenant-scoped queries, shared-schema isolation, and safe cross-tenant admin patterns in PostgreSQL and TypeScript."
4
+ risk: safe
5
+ source: community
6
+ date_added: "2026-03-28"
7
+ tags: [multi-tenancy, saas, row-level-security, postgresql, tenant-isolation]
8
+ tools: [claude, cursor, gemini]
9
+ ---
10
+
11
+ # SaaS Multi-Tenant Architecture
12
+
13
+ ## When to Use This Skill
14
+
15
+ - The user is building a SaaS application where multiple customers share the same database
16
+ - The user asks about tenant isolation, row-level security, or data leakage prevention
17
+ - The user needs to scope every database query to a specific tenant without manual WHERE clauses
18
+ - The user asks about shared-schema vs schema-per-tenant vs database-per-tenant tradeoffs
19
+ - The user is implementing admin endpoints that must access data across tenants
20
+ - The user needs to add `tenant_id` columns to an existing single-tenant application
21
+ - The user asks about PostgreSQL RLS policies for tenant isolation
22
+ - The user is building tenant-aware middleware in Express, Fastify, or Next.js API routes
23
+
24
+ Do NOT use this skill when:
25
+ - The user is building a single-user application with no shared infrastructure
26
+ - The user asks about authentication only without tenant scoping (use an auth skill instead)
27
+ - The user needs general database schema design without multi-tenancy requirements
28
+
29
+ ## Core Workflow
30
+
31
+ 1. Determine the tenancy model. Ask the user about their scale expectations and isolation requirements. For most SaaS apps under 1000 tenants, shared-schema with a `tenant_id` column on every table is the correct default. Schema-per-tenant adds operational overhead (migrations run N times). Database-per-tenant is only justified when tenants have regulatory data residency requirements.
32
+
33
+ 2. Add `tenant_id` to every tenant-scoped table. The column must be `NOT NULL`, type `UUID` or `TEXT`, and included in every composite index. Never allow a tenant-scoped table to exist without this column — a missing `tenant_id` is a data leak waiting to happen.
34
+
35
+ 3. Set up PostgreSQL Row-Level Security (RLS). Create a policy on each tenant-scoped table that filters rows by `current_setting('app.current_tenant_id')`. This acts as a database-level safety net — even if application code forgets a WHERE clause, RLS blocks cross-tenant reads.
36
+
37
+ 4. Build tenant-aware middleware. At the start of every request, extract the `tenant_id` from the authenticated session or JWT claims. Set it on the database connection using `SET LOCAL app.current_tenant_id = '...'` inside a transaction. Every subsequent query in that request inherits the tenant scope automatically.
38
+
39
+ 5. Scope all ORM queries by tenant. If using Prisma, apply a global middleware that injects `where: { tenantId }` into every `findMany`, `findFirst`, `update`, and `delete` call. If using Drizzle, create a base query builder that includes the tenant filter. Never rely on developers remembering to add the filter manually.
40
+
41
+ 6. Handle tenant-aware migrations. Every new table migration must include `tenant_id` as a column. Write a linting rule or CI check that rejects any migration creating a table without `tenant_id` unless the table is explicitly marked as global (e.g., `plans`, `feature_flags`).
42
+
43
+ 7. Build cross-tenant admin routes separately. Admin endpoints that aggregate data across tenants must bypass RLS explicitly using `SET LOCAL role = 'admin_bypass'` or a dedicated database role. These routes must be protected by a separate admin authentication flow — never reuse tenant user sessions for admin access.
44
+
45
+ 8. Implement tenant provisioning. When a new customer signs up, create their tenant record, seed default data (roles, settings, onboarding state), and assign the founding user. Wrap this in a database transaction so partial provisioning never leaves orphan records.
46
+
47
+ ## Examples
48
+
49
+ ### Example 1: PostgreSQL RLS Policy for Tenant Isolation
50
+
51
+ ```sql
52
+ -- Enable RLS on the table
53
+ ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
54
+ ALTER TABLE projects FORCE ROW LEVEL SECURITY;
55
+
56
+ -- Policy: users can only see rows where tenant_id matches the session variable
57
+ CREATE POLICY tenant_isolation ON projects
58
+ USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
59
+
60
+ -- Policy for INSERT: new rows must match the current tenant
61
+ CREATE POLICY tenant_insert ON projects
62
+ FOR INSERT
63
+ WITH CHECK (tenant_id = current_setting('app.current_tenant_id')::uuid);
64
+ ```
65
+
66
+ ### Example 2: Express Middleware That Sets Tenant Context per Request
67
+
68
+ ```typescript
69
+ import { Pool } from "pg";
70
+
71
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
72
+
73
+ async function tenantMiddleware(req, res, next) {
74
+ const tenantId = req.auth?.tenantId; // extracted from JWT during auth
75
+ if (!tenantId) return res.status(403).json({ error: "No tenant context" });
76
+
77
+ const client = await pool.connect();
78
+ try {
79
+ await client.query("BEGIN");
80
+ // Use set_config — SET LOCAL does not accept bind placeholders ($1)
81
+ await client.query("SELECT set_config('app.current_tenant_id', $1, true)", [tenantId]);
82
+ req.db = client;
83
+ req.tenantId = tenantId;
84
+
85
+ // Cleanup on response finish — guarantees release even if handler skips next()
86
+ res.on("finish", async () => {
87
+ try { await client.query("COMMIT"); } catch { await client.query("ROLLBACK"); }
88
+ client.release();
89
+ });
90
+
91
+ next();
92
+ } catch (err) {
93
+ await client.query("ROLLBACK").catch(() => {});
94
+ client.release();
95
+ next(err);
96
+ }
97
+ }
98
+ ```
99
+
100
+ ### Example 3: Prisma Middleware for Automatic Tenant Scoping
101
+
102
+ ```typescript
103
+ import { PrismaClient } from "@prisma/client";
104
+
105
+ // Tables that do NOT have tenant_id (global tables)
106
+ const GLOBAL_TABLES = new Set(["Plan", "FeatureFlag", "SystemConfig"]);
107
+
108
+ function createTenantPrisma(tenantId: string): PrismaClient {
109
+ const prisma = new PrismaClient();
110
+
111
+ prisma.$use(async (params, next) => {
112
+ if (GLOBAL_TABLES.has(params.model ?? "")) return next(params);
113
+
114
+ // Initialize args.where — Prisma passes undefined args for calls like findMany()
115
+ params.args = params.args ?? {};
116
+ params.args.where = params.args.where ?? {};
117
+
118
+ // Inject tenant filter on reads (skip findUnique — it only accepts unique-field selectors)
119
+ if (["findMany", "findFirst", "count", "aggregate"].includes(params.action)) {
120
+ params.args.where = { ...params.args.where, tenantId };
121
+ }
122
+
123
+ // Inject tenant_id on creates
124
+ if (["create", "createMany"].includes(params.action)) {
125
+ params.args.data = params.args.data ?? {};
126
+ if (params.action === "createMany") {
127
+ params.args.data = params.args.data.map((d: any) => ({ ...d, tenantId }));
128
+ } else {
129
+ params.args.data = { ...params.args.data, tenantId };
130
+ }
131
+ }
132
+
133
+ // Scope updates and deletes
134
+ if (["update", "updateMany", "delete", "deleteMany"].includes(params.action)) {
135
+ params.args.where = { ...params.args.where, tenantId };
136
+ }
137
+
138
+ return next(params);
139
+ });
140
+
141
+ return prisma;
142
+ }
143
+ ```
144
+
145
+ ## Never Do This
146
+
147
+ 1. **Never query a tenant-scoped table without a `tenant_id` filter.** Even if your ORM middleware handles it, raw SQL queries bypass middleware entirely. Every raw query must include `WHERE tenant_id = $1` or rely on RLS. A single unscoped `SELECT * FROM invoices` leaks every customer's billing data.
148
+
149
+ 2. **Never store `tenant_id` only in the application session without enforcing it at the database level.** Application-layer filtering is a suggestion. RLS is enforcement. If a bug in your middleware skips the tenant filter, only RLS prevents the data leak. Run both layers.
150
+
151
+ 3. **Never use auto-incrementing integer IDs for tenant-scoped resources.** Sequential IDs (`invoice #1042`) let attackers enumerate other tenants' resources by incrementing the ID. Use UUIDs for all tenant-scoped primary keys. Reserve integer IDs for internal-only tables.
152
+
153
+ 4. **Never let tenant users access admin aggregation endpoints.** A route like `GET /admin/metrics` that queries across all tenants must never be reachable with a regular tenant JWT. Use a separate authentication mechanism (API key, admin role claim with a different issuer) for cross-tenant routes.
154
+
155
+ 5. **Never run migrations with RLS enabled on the migration connection.** The migration user needs to create tables, add columns, and modify policies. If RLS is active on the migration connection, `ALTER TABLE` commands may silently fail or affect only the "current tenant's" view. Use a dedicated superuser or `bypassrls` role for migrations.
156
+
157
+ 6. **Never share connection pools across tenants when using `SET LOCAL`.** If you use `SET LOCAL app.current_tenant_id` inside a transaction, that setting is scoped to the transaction. But if a previous request's transaction was not properly committed or rolled back, the connection returns to the pool with stale tenant context. Always `RESET app.current_tenant_id` in the cleanup path.
158
+
159
+ ## Edge Cases
160
+
161
+ 1. **Tenant deletion and data retention.** When a tenant cancels their subscription, you cannot simply `DELETE FROM tenants WHERE id = $1`. Foreign key cascades may time out on large datasets. Instead, soft-delete the tenant (set `deleted_at`), revoke all user sessions, then run a background job that deletes tenant data in batches over hours or days.
162
+
163
+ 2. **Tenant data export for GDPR/compliance.** When a tenant requests a full data export, you need to query every tenant-scoped table for that `tenant_id` and package it. Build a registry of all tenant-scoped tables (parse your migration files or maintain a manifest) so the export job doesn't miss tables added after the export feature was built.
164
+
165
+ 3. **Shared resources between tenants.** Some features require shared state — e.g., a marketplace where Tenant A's products are visible to Tenant B's users. These tables need a different RLS policy: read access is public (no tenant filter), but write access is still scoped to the owning tenant. Model these as `owner_tenant_id` instead of `tenant_id`.
166
+
167
+ 4. **Tenant-aware background jobs.** When a cron job or queue worker processes tasks, there is no HTTP request to extract `tenant_id` from. The job payload must include `tenant_id`, and the worker must set the database session variable before processing. Never run background jobs without tenant context — they will either fail on RLS or bypass it entirely.
168
+
169
+ 5. **Connection pool exhaustion with schema-per-tenant.** If you use one PostgreSQL schema per tenant and each schema requires its own connection pool, 500 tenants means 500 pools. This exhausts `max_connections` fast. Use a connection pooler like PgBouncer in transaction mode, or switch to shared-schema before hitting this wall.
170
+
171
+ ## Best Practices
172
+
173
+ 1. **Create a `tenants` table as the single source of truth.** Every `tenant_id` foreign key in every table points back to `tenants.id`. Include columns for `name`, `slug` (for subdomain routing), `plan_id`, `created_at`, and `deleted_at`. This table is the root of your entire data model.
174
+
175
+ 2. **Index `tenant_id` as the first column in every composite index.** PostgreSQL uses leftmost prefix matching for composite indexes. An index on `(tenant_id, created_at)` serves both "all items for tenant X" and "items for tenant X sorted by date." An index on `(created_at, tenant_id)` only helps date-range queries across all tenants.
176
+
177
+ 3. **Use subdomains or path prefixes for tenant routing.** `acme.yourapp.com` or `yourapp.com/org/acme` — both work. Map the subdomain or path to a `tenant_id` lookup at the edge (middleware or reverse proxy). This lookup should be cached (Redis or in-memory with 60s TTL) since it runs on every single request.
178
+
179
+ 4. **Separate tenant-scoped tables from global tables explicitly.** Maintain a list (code constant or database table) of which tables are global (no `tenant_id`) and which are tenant-scoped. Use this list in your ORM middleware, your migration linter, and your data export job. If a table isn't in either list, the CI check should fail.
180
+
181
+ 5. **Test with at least 3 tenants in your seed data.** A single tenant in development hides every multi-tenancy bug. Two tenants hides bugs where the first tenant's data leaks to the second but not vice versa. Three tenants catches ordering and filtering bugs that only appear with multiple peers.
182
+
183
+ 6. **Rate-limit and quota per tenant, not globally.** A global rate limit of 1000 requests/minute means one noisy tenant can exhaust the quota for everyone. Implement per-tenant rate limiting using a Redis key pattern like `ratelimit:{tenant_id}:{endpoint}` with a sliding window counter.
@@ -12,22 +12,23 @@ source: community
12
12
  ```javascript
13
13
  import * as THREE from "three";
14
14
 
15
- // Simple procedural animation
16
- const clock = new THREE.Clock();
15
+ // Simple procedural animation with Timer (recommended in r183)
16
+ const timer = new THREE.Timer();
17
17
 
18
- function animate() {
19
- const delta = clock.getDelta();
20
- const elapsed = clock.getElapsedTime();
18
+ renderer.setAnimationLoop(() => {
19
+ timer.update();
20
+ const delta = timer.getDelta();
21
+ const elapsed = timer.getElapsed();
21
22
 
22
23
  mesh.rotation.y += delta;
23
24
  mesh.position.y = Math.sin(elapsed) * 0.5;
24
25
 
25
- requestAnimationFrame(animate);
26
26
  renderer.render(scene, camera);
27
- }
28
- animate();
27
+ });
29
28
  ```
30
29
 
30
+ **Note:** `THREE.Timer` is recommended over `THREE.Clock` as of r183. Timer pauses when the page is hidden and has a cleaner API. `THREE.Clock` still works but is considered legacy.
31
+
31
32
  ## Animation System Overview
32
33
 
33
34
  Three.js animation system has three main components:
@@ -118,6 +119,10 @@ track.setInterpolation(THREE.InterpolateSmooth); // Cubic spline
118
119
  track.setInterpolation(THREE.InterpolateDiscrete); // Step function
119
120
  ```
120
121
 
122
+ ### BezierInterpolant (r183)
123
+
124
+ Three.js r183 adds `THREE.BezierInterpolant` for bezier curve interpolation in keyframe tracks, enabling smoother animation curves with tangent control.
125
+
121
126
  ## AnimationMixer
122
127
 
123
128
  Plays animations on an object and its descendants.
@@ -416,7 +416,24 @@ function dispose() {
416
416
  }
417
417
  ```
418
418
 
419
- ### Clock for Animation
419
+ ### Timer and Clock for Animation
420
+
421
+ **Timer (recommended in r183)** - pauses when tab is hidden, cleaner API:
422
+
423
+ ```javascript
424
+ const timer = new THREE.Timer();
425
+
426
+ renderer.setAnimationLoop(() => {
427
+ timer.update();
428
+ const delta = timer.getDelta();
429
+ const elapsed = timer.getElapsed();
430
+
431
+ mesh.rotation.y += delta * 0.5;
432
+ renderer.render(scene, camera);
433
+ });
434
+ ```
435
+
436
+ **Clock (legacy, still works):**
420
437
 
421
438
  ```javascript
422
439
  const clock = new THREE.Clock();
@@ -432,6 +449,17 @@ function animate() {
432
449
  }
433
450
  ```
434
451
 
452
+ ### Animation Loop
453
+
454
+ Prefer `renderer.setAnimationLoop()` over manual `requestAnimationFrame`. It handles WebXR compatibility and is the standard Three.js pattern:
455
+
456
+ ```javascript
457
+ renderer.setAnimationLoop(() => {
458
+ controls.update();
459
+ renderer.render(scene, camera);
460
+ });
461
+ ```
462
+
435
463
  ### Responsive Canvas
436
464
 
437
465
  ```javascript
@@ -483,6 +511,21 @@ lod.addLevel(lowDetailMesh, 100);
483
511
  scene.add(lod);
484
512
  ```
485
513
 
514
+ ## WebGPU Renderer (r183)
515
+
516
+ Three.js includes an experimental WebGPU renderer as an alternative to WebGL:
517
+
518
+ ```javascript
519
+ import { WebGPURenderer } from "three/addons/renderers/webgpu/WebGPURenderer.js";
520
+
521
+ const renderer = new WebGPURenderer({ antialias: true });
522
+ await renderer.init();
523
+ renderer.setSize(window.innerWidth, window.innerHeight);
524
+ document.body.appendChild(renderer.domElement);
525
+ ```
526
+
527
+ WebGPU uses TSL (Three.js Shading Language) instead of GLSL. The WebGL renderer remains the default and is fully supported.
528
+
486
529
  ## See Also
487
530
 
488
531
  - `threejs-geometry` - Geometry creation and manipulation