Skip to content

Commit ba588c6

Browse files
author
test
committed
Merge upstream/staging into feat/public-page-flags
2 parents 7b8c38e + 8f15be2 commit ba588c6

File tree

1,406 files changed

+288053
-28909
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,406 files changed

+288053
-28909
lines changed

.claude/commands/add-connector.md

Lines changed: 437 additions & 0 deletions
Large diffs are not rendered by default.

.cursor/rules/landing-seo-geo.mdc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
description: SEO and GEO guidelines for the landing page
3+
globs: ["apps/sim/app/(home)/**/*.tsx"]
4+
---
5+
6+
# Landing Page — SEO / GEO
7+
8+
## SEO
9+
10+
- One `<h1>` per page, in Hero only — never add another.
11+
- Strict heading hierarchy: H1 (Hero) → H2 (section titles) → H3 (feature names).
12+
- Every section: `<section id="…" aria-labelledby="…-heading">`.
13+
- Decorative/animated elements: `aria-hidden="true"`.
14+
- All internal routes use Next.js `<Link>` (crawlable). External links get `rel="noopener noreferrer"`.
15+
- Navbar is a Server Component (no `'use client'`) for immediate crawlability. Logo `<Image>` has `priority` (LCP element).
16+
- Navbar `<nav>` carries `SiteNavigationElement` schema.org markup.
17+
- Feature lists must stay in sync with `WebApplication.featureList` in `structured-data.tsx`.
18+
19+
## GEO (Generative Engine Optimisation)
20+
21+
- **Answer-first pattern**: each section's H2 + subtitle should directly answer a user question (e.g. "What is Sim?", "How fast can I deploy?").
22+
- **Atomic answer blocks**: each feature / template card should be independently extractable by an AI summariser.
23+
- **Entity consistency**: always write "Sim" by name — never "the platform" or "our tool".
24+
- **Keyword density**: first 150 visible chars of Hero must name "Sim", "AI agents", "agentic workflows".
25+
- **sr-only summaries**: Hero and Templates each have a `<p className="sr-only">` (~50 words) as an atomic product/catalog summary for AI citation.
26+
- **Specific numbers**: prefer concrete figures ("1,000+ integrations", "15+ AI providers") over vague claims.

.cursor/rules/sim-queries.mdc

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,62 +5,122 @@ globs: ["apps/sim/hooks/queries/**/*.ts"]
55

66
# React Query Patterns
77

8-
All React Query hooks live in `hooks/queries/`.
8+
All React Query hooks live in `hooks/queries/`. All server state must go through React Query — never use `useState` + `fetch` in components for data fetching or mutations.
99

1010
## Query Key Factory
1111

12-
Every query file defines a keys factory:
12+
Every query file defines a hierarchical keys factory with an `all` root key and intermediate plural keys for prefix-level invalidation:
1313

1414
```typescript
1515
export const entityKeys = {
1616
all: ['entity'] as const,
17-
list: (workspaceId?: string) => [...entityKeys.all, 'list', workspaceId ?? ''] as const,
18-
detail: (id?: string) => [...entityKeys.all, 'detail', id ?? ''] as const,
17+
lists: () => [...entityKeys.all, 'list'] as const,
18+
list: (workspaceId?: string) => [...entityKeys.lists(), workspaceId ?? ''] as const,
19+
details: () => [...entityKeys.all, 'detail'] as const,
20+
detail: (id?: string) => [...entityKeys.details(), id ?? ''] as const,
1921
}
2022
```
2123

24+
Never use inline query keys — always use the factory.
25+
2226
## File Structure
2327

2428
```typescript
2529
// 1. Query keys factory
2630
// 2. Types (if needed)
27-
// 3. Private fetch functions
31+
// 3. Private fetch functions (accept signal parameter)
2832
// 4. Exported hooks
2933
```
3034

3135
## Query Hook
3236

37+
- Every `queryFn` must destructure and forward `signal` for request cancellation
38+
- Every query must have an explicit `staleTime`
39+
- Use `keepPreviousData` only on variable-key queries (where params change), never on static keys
40+
3341
```typescript
42+
async function fetchEntities(workspaceId: string, signal?: AbortSignal) {
43+
const response = await fetch(`/api/entities?workspaceId=${workspaceId}`, { signal })
44+
if (!response.ok) throw new Error('Failed to fetch entities')
45+
return response.json()
46+
}
47+
3448
export function useEntityList(workspaceId?: string, options?: { enabled?: boolean }) {
3549
return useQuery({
3650
queryKey: entityKeys.list(workspaceId),
37-
queryFn: () => fetchEntities(workspaceId as string),
51+
queryFn: ({ signal }) => fetchEntities(workspaceId as string, signal),
3852
enabled: Boolean(workspaceId) && (options?.enabled ?? true),
3953
staleTime: 60 * 1000,
40-
placeholderData: keepPreviousData,
54+
placeholderData: keepPreviousData, // OK: workspaceId varies
4155
})
4256
}
4357
```
4458

4559
## Mutation Hook
4660

61+
- Use targeted invalidation (`entityKeys.lists()`) not broad (`entityKeys.all`) when possible
62+
- Invalidation must cover all affected query key prefixes (lists, details, related views)
63+
4764
```typescript
4865
export function useCreateEntity() {
4966
const queryClient = useQueryClient()
5067
return useMutation({
5168
mutationFn: async (variables) => { /* fetch POST */ },
52-
onSuccess: () => queryClient.invalidateQueries({ queryKey: entityKeys.all }),
69+
onSuccess: () => {
70+
queryClient.invalidateQueries({ queryKey: entityKeys.lists() })
71+
},
5372
})
5473
}
5574
```
5675

5776
## Optimistic Updates
5877

78+
For optimistic mutations, use `onSettled` (not `onSuccess`) for cache reconciliation — `onSettled` fires on both success and error, ensuring the cache is always reconciled with the server.
79+
80+
```typescript
81+
export function useUpdateEntity() {
82+
const queryClient = useQueryClient()
83+
return useMutation({
84+
mutationFn: async (variables) => { /* ... */ },
85+
onMutate: async (variables) => {
86+
await queryClient.cancelQueries({ queryKey: entityKeys.detail(variables.id) })
87+
const previous = queryClient.getQueryData(entityKeys.detail(variables.id))
88+
queryClient.setQueryData(entityKeys.detail(variables.id), /* optimistic value */)
89+
return { previous }
90+
},
91+
onError: (_err, variables, context) => {
92+
queryClient.setQueryData(entityKeys.detail(variables.id), context?.previous)
93+
},
94+
onSettled: (_data, _error, variables) => {
95+
queryClient.invalidateQueries({ queryKey: entityKeys.lists() })
96+
queryClient.invalidateQueries({ queryKey: entityKeys.detail(variables.id) })
97+
},
98+
})
99+
}
100+
```
101+
59102
For optimistic mutations syncing with Zustand, use `createOptimisticMutationHandlers` from `@/hooks/queries/utils/optimistic-mutation`.
60103

104+
## useCallback Dependencies
105+
106+
Never include mutation objects (e.g., `createEntity`) in `useCallback` dependency arrays — the mutation object is not referentially stable and changes on every state update. The `.mutate()` and `.mutateAsync()` functions are stable in TanStack Query v5.
107+
108+
```typescript
109+
// ✗ Bad — causes unnecessary recreations
110+
const handler = useCallback(() => {
111+
createEntity.mutate(data)
112+
}, [createEntity]) // unstable reference
113+
114+
// ✓ Good — omit from deps, mutate is stable
115+
const handler = useCallback(() => {
116+
createEntity.mutate(data)
117+
// eslint-disable-next-line react-hooks/exhaustive-deps
118+
}, [data])
119+
```
120+
61121
## Naming
62122

63123
- **Keys**: `entityKeys`
64124
- **Query hooks**: `useEntity`, `useEntityList`
65-
- **Mutation hooks**: `useCreateEntity`, `useUpdateEntity`
66-
- **Fetch functions**: `fetchEntity` (private)
125+
- **Mutation hooks**: `useCreateEntity`, `useUpdateEntity`, `useDeleteEntity`
126+
- **Fetch functions**: `fetchEntity`, `fetchEntities` (private)

0 commit comments

Comments
 (0)