You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .cursor/rules/sim-architecture.mdc
+2-2Lines changed: 2 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -61,9 +61,9 @@ Boundary HTTP request and response shapes for all routes under `apps/sim/app/api
61
61
62
62
- Each contract is built with `defineRouteContract({ method, path, params?, query?, body?, headers?, response: { mode: 'json', schema } })` and exports both schemas and named TypeScript type aliases (e.g., `export type CreateFolderBody = z.input<typeof createFolderBodySchema>`).
63
63
- Shared identifier schemas live in `apps/sim/lib/api/contracts/primitives.ts`.
64
-
- Routes validate via canonical helpers in `apps/sim/lib/api/server/validation.ts` (`parseRequest`, `validateSchema`, `validationErrorResponse`, `getValidationErrorMessage`, `isZodError`). Routes never `import { z } from 'zod'` and never use `instanceof z.ZodError`.
64
+
- Routes validate via canonical helpers in `apps/sim/lib/api/server/validation.ts` (`parseRequest`, `validationErrorResponse`, `getValidationErrorMessage`, `isZodError`). Routes never `import { z } from 'zod'` and never use `instanceof z.ZodError`.
65
65
- Clients call `requestJson(contract, ...)` from `apps/sim/lib/api/client/request.ts`; hooks import named type aliases from contracts, never `z.input/z.output`.
66
66
- Routes under `apps/sim/app/api/v1/**` use `apps/sim/app/api/v1/middleware.ts` for shared auth, rate-limit, and workspace access. Compose contract validation inside that middleware.
67
67
- `bun run check:api-validation` enforces this policy and must pass on PRs.
68
68
69
-
`bun run check:api-validation:strict` is the strict CI gate and additionally fails on annotations with empty reasons. Two per-line opt-out forms are recognized: `// boundary-raw-fetch: <reason>` (placed immediately above a legitimate raw `fetch(` call in `apps/sim/hooks/queries/**` or `apps/sim/hooks/selectors/**` for stream/binary/multipart/signed-URL/OAuth-redirect/external-origin cases) and `// double-cast-allowed: <reason>` (placed immediately above an `as unknown as X` cast outside test files). The reason must be non-empty. Whole-file allowlists for routes that legitimately import Zod for non-boundary reasons go through `INDIRECT_ZOD_ROUTES` in `scripts/check-api-validation-contracts.ts`, not per-line annotations.
69
+
`bun run check:api-validation:strict` is the strict CI gate and additionally fails on annotations with empty reasons. Four per-line opt-out forms are recognized: `// boundary-raw-fetch: <reason>` (placed immediately above a legitimate raw `fetch(` call in `apps/sim/hooks/queries/**` or `apps/sim/hooks/selectors/**` for stream/binary/multipart/signed-URL/OAuth-redirect/external-origin cases), `// double-cast-allowed: <reason>` (placed immediately above an `as unknown as X` cast outside test files), `// boundary-raw-json: <reason>` (placed immediately above a raw `await request.json()` / `await req.json()` read in a route handler that cannot go through `parseRequest` — JSON-RPC envelopes, tolerant `.catch(() => ({}))` parses), and `// untyped-response: <reason>` (placed immediately above a `schema: z.unknown()` response declaration in a contract file when the response body is genuinely opaque). The reason must be non-empty. Whole-file allowlists for routes that legitimately import Zod for non-boundary reasons go through `INDIRECT_ZOD_ROUTES` in `scripts/check-api-validation-contracts.ts`, not per-line annotations.
Copy file name to clipboardExpand all lines: AGENTS.md
+10-11Lines changed: 10 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -130,10 +130,12 @@ Domain validators that are not HTTP boundaries — tools, blocks, triggers, conn
130
130
131
131
### Boundary annotations
132
132
133
-
A small number of legitimate exceptions to the boundary rules are tolerated when annotated. The audit script recognizes two annotation forms:
133
+
A small number of legitimate exceptions to the boundary rules are tolerated when annotated. The audit script recognizes four annotation forms:
134
134
135
135
-`// boundary-raw-fetch: <reason>` — placed on the line directly above a raw `fetch(` call inside `apps/sim/hooks/queries/**` or `apps/sim/hooks/selectors/**`. Use only for documented exceptions: streaming responses, binary downloads, multipart uploads, signed-URL flows, OAuth redirects, and external-origin requests
136
136
-`// double-cast-allowed: <reason>` — placed on the line directly above an `as unknown as X` cast outside test files
137
+
-`// boundary-raw-json: <reason>` — placed on the line directly above a raw `await request.json()` / `await req.json()` read in a route handler. Use only when the body is a JSON-RPC envelope, a tolerant `.catch(() => ({}))` parse, or otherwise cannot go through `parseRequest`
138
+
-`// untyped-response: <reason>` — placed on the line directly above a `schema: z.unknown()` response declaration in a contract file. Use only when the response body is genuinely opaque (user-supplied data, third-party passthrough)
137
139
138
140
Placement rule: the annotation must immediately precede the call or cast. Up to three non-empty preceding comment lines are tolerated, so additional context comments above the annotation are fine. The reason must be non-empty after trimming — annotations with empty reasons fail strict mode (`annotationsMissingReason`).
139
141
@@ -155,8 +157,7 @@ const provider = config as unknown as LegacyProvider
155
157
156
158
Routes never `import { z } from 'zod'` and never define route-local boundary schemas. They consume the contract from `@/lib/api/contracts/**` and validate with canonical helpers from `@/lib/api/server`:
157
159
158
-
-`parseRequest(contract, request, context)` — fully contract-bound routes; parses params, query, body, and headers in one call
159
-
-`validateSchema(schema, data)` — for ad-hoc validation against a contract schema or primitive
160
+
-`parseRequest(contract, request, context, options?)` — fully contract-bound routes; parses params, query, body, and headers in one call. Pass `{}` for `context` on routes without route params, or the route's `context` argument when route params exist. Returns a discriminated union; check `parsed.success` and return `parsed.response` on failure
160
161
-`validationErrorResponse(error)` and `getValidationErrorMessage(error, fallback)` — produce 400 responses from a `ZodError`
161
162
-`validationErrorResponseFromError(error)` — when handling unknown caught errors that may or may not be a `ZodError`
162
163
-`isZodError(error)` — type guard. Routes never use `instanceof z.ZodError`
@@ -168,19 +169,17 @@ import { createLogger } from '@sim/logger'
Copy file name to clipboardExpand all lines: CLAUDE.md
+10-11Lines changed: 10 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -109,10 +109,12 @@ Domain validators that are not HTTP boundaries — tools, blocks, triggers, conn
109
109
110
110
### Boundary annotations
111
111
112
-
A small number of legitimate exceptions to the boundary rules are tolerated when annotated. The audit script recognizes two annotation forms:
112
+
A small number of legitimate exceptions to the boundary rules are tolerated when annotated. The audit script recognizes four annotation forms:
113
113
114
114
-`// boundary-raw-fetch: <reason>` — placed on the line directly above a raw `fetch(` call inside `apps/sim/hooks/queries/**` or `apps/sim/hooks/selectors/**`. Use only for documented exceptions: streaming responses, binary downloads, multipart uploads, signed-URL flows, OAuth redirects, and external-origin requests
115
115
-`// double-cast-allowed: <reason>` — placed on the line directly above an `as unknown as X` cast outside test files
116
+
-`// boundary-raw-json: <reason>` — placed on the line directly above a raw `await request.json()` / `await req.json()` read in a route handler. Use only when the body is a JSON-RPC envelope, a tolerant `.catch(() => ({}))` parse, or otherwise cannot go through `parseRequest`
117
+
-`// untyped-response: <reason>` — placed on the line directly above a `schema: z.unknown()` response declaration in a contract file. Use only when the response body is genuinely opaque (user-supplied data, third-party passthrough)
116
118
117
119
Placement rule: the annotation must immediately precede the call or cast. Up to three non-empty preceding comment lines are tolerated, so additional context comments above the annotation are fine. The reason must be non-empty after trimming — annotations with empty reasons fail strict mode (`annotationsMissingReason`).
118
120
@@ -136,8 +138,7 @@ Every API route handler must be wrapped with `withRouteHandler`. This sets up `A
136
138
137
139
Routes never `import { z } from 'zod'` and never define route-local boundary schemas. They consume the contract from `@/lib/api/contracts/**` and validate with canonical helpers from `@/lib/api/server`:
138
140
139
-
-`parseRequest(contract, request, context)` — fully contract-bound routes; parses params, query, body, and headers in one call
140
-
-`validateSchema(schema, data)` — for ad-hoc validation against a contract schema or primitive
141
+
-`parseRequest(contract, request, context, options?)` — fully contract-bound routes; parses params, query, body, and headers in one call. Pass `{}` for `context` on routes without route params, or the route's `context` argument when route params exist. Returns a discriminated union; check `parsed.success` and return `parsed.response` on failure
141
142
-`validationErrorResponse(error)` and `getValidationErrorMessage(error, fallback)` — produce 400 responses from a `ZodError`
142
143
-`validationErrorResponseFromError(error)` — when handling unknown caught errors that may or may not be a `ZodError`
143
144
-`isZodError(error)` — type guard. Routes never use `instanceof z.ZodError`
@@ -149,19 +150,17 @@ import { createLogger } from '@sim/logger'
Copy file name to clipboardExpand all lines: apps/sim/AGENTS.md
+10-11Lines changed: 10 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -81,10 +81,12 @@ Boundary HTTP request and response shapes for all routes under `apps/sim/app/api
81
81
82
82
### Boundary annotations
83
83
84
-
A small number of legitimate exceptions to the boundary rules are tolerated when annotated. The audit script recognizes two annotation forms:
84
+
A small number of legitimate exceptions to the boundary rules are tolerated when annotated. The audit script recognizes four annotation forms:
85
85
86
86
-`// boundary-raw-fetch: <reason>` — placed on the line directly above a raw `fetch(` call inside `apps/sim/hooks/queries/**` or `apps/sim/hooks/selectors/**`. Use only for documented exceptions: streaming responses, binary downloads, multipart uploads, signed-URL flows, OAuth redirects, and external-origin requests.
87
87
-`// double-cast-allowed: <reason>` — placed on the line directly above an `as unknown as X` cast outside test files.
88
+
-`// boundary-raw-json: <reason>` — placed on the line directly above a raw `await request.json()` / `await req.json()` read in a route handler. Use only when the body is a JSON-RPC envelope, a tolerant `.catch(() => ({}))` parse, or otherwise cannot go through `parseRequest`.
89
+
-`// untyped-response: <reason>` — placed on the line directly above a `schema: z.unknown()` response declaration in a contract file. Use only when the response body is genuinely opaque (user-supplied data, third-party passthrough).
88
90
89
91
Placement rule: the annotation must immediately precede the call or cast. Up to three non-empty preceding comment lines are tolerated, so additional context comments above the annotation are fine. The reason must be non-empty after trimming — annotations with empty reasons fail strict mode (`annotationsMissingReason`).
90
92
@@ -106,8 +108,7 @@ const provider = config as unknown as LegacyProvider
106
108
107
109
Routes never `import { z } from 'zod'` and never define route-local boundary schemas. They consume the contract from `@/lib/api/contracts/**` and validate with canonical helpers from `@/lib/api/server`:
108
110
109
-
-`parseRequest(contract, request, context)` — fully contract-bound routes; parses params, query, body, and headers in one call.
110
-
-`validateSchema(schema, data)` — for ad-hoc validation against a contract schema or primitive.
111
+
-`parseRequest(contract, request, context, options?)` — fully contract-bound routes; parses params, query, body, and headers in one call. Pass `{}` for `context` on routes without route params, or the route's `context` argument when route params exist. Returns a discriminated union; check `parsed.success` and return `parsed.response` on failure.
111
112
-`validationErrorResponse(error)` and `getValidationErrorMessage(error, fallback)` — produce 400 responses from a `ZodError`.
112
113
-`validationErrorResponseFromError(error)` — when handling unknown caught errors that may or may not be a `ZodError`.
113
114
-`isZodError(error)` — type guard. Routes never use `instanceof z.ZodError`.
@@ -119,19 +120,17 @@ import { createLogger } from '@sim/logger'
0 commit comments