Instruction file imported from x-pact-pro/qvac (
.cursor/rules/sdk/main.mdc). Copyright stays with the author.
Client
Stack
- Bun (Typescript)
- Zod for schema validation
- Hyperswarm (Holepunch) for p2p model download
- Bare (Holepunch, but with Typescript)
Rules
- Start every message with š and the final one with š«”
- Code should be as portable as possible and with little dependencies as possible.
- Anything under the
barefolder is run on Bare, not Node, be mindful of imports and stdlib - Use all bare equivalents of node stdlib that you need (if in doubt ask)
- Never use classes, everything should be a composition of stateless functions, if in need just add a factory function that can instantiate an object
- Exception: Error classes extend
QvacErrorBase(see error-handling.mdc)
- Exception: Error classes extend
- ALWAYS use function declarations instead of const arrow functions, unless strictly necessary (e.g., when function needs to be passed as a value or requires specific closure behavior)
- ā
Correct:
function myFunction() { ... }orexport function myFunction() { ... } - ā Wrong:
const myFunction = () => { ... }orexport const myFunction = () => { ... } - This makes code more consistent, improves hoisting behavior, and follows functional programming best practices
- ā
Correct:
- Never use any or unknown as types, unless absolutely necessary and instructed to do so
- Do not add return types to function definitions
- Write concise, brief, elegant code
- ALWAYS use the
@alias for imports - Never use relative imports (e.g.,../../../utils/something)- ā
Correct:
import { something } from "@/utils/something" - ā Wrong:
import { something } from "../../../utils/something" - This applies to ALL files in the project (client, server, schemas, utils, etc.)
- ā
Correct:
- Never write vanilla JS, only Typescript, even if you see vanilla JS code
- Types should be co located with the code using them, if shared just add them to a
commonsubfolder with a module per type (e.g.chat-message.ts) - Schemas that are shared between server and client should be in a common folder at the same level as them
- Anything under
serverwill run on Bare so anything that imports something from there must be Bare compatible. Do not share that code with the client. - When a new schema or changes to existing schemas are required, that are shared across server and client, they MUST be put in the shared schemas folder, defined as Zod schemas and both schema and inferred type exported and used in client and server
- Avoid using typeof and generics unless extremely necessary
- Avoid doc comments on top of functions unless explicitly instructed
- Zod schemas start with lowercase letter, their inferred types with uppercase, e.g.
const mySchema = z.object(...)->type MySchema = z.infer<typeof mySchema>
Error Handling
- ALWAYS use structured error classes from
@/utils/errors-clientor@/utils/errors-server(see error-handling.mdc for details) - NEVER throw plain
Errorobjects in SDK code (client/server directories) - ALWAYS preserve original errors via the
causeparameter when catching and re-throwing - Import error classes:
import { ModelNotFoundError } from "@/utils/errors-server" - Use error chaining:
throw new ModelLoadFailedError(details, originalError)
Useful commands
bun lintandbun format(code quality, append:fixto attempt autofix)bun run buildif you make any changes underbare
Commit Messages and PR Titles
- ALWAYS follow the structured format (see commit-and-pr-format.mdc for full details):
- Commits:
prefix[tags]?: subject(e.g.,feat[api]: add new endpoint) - PRs:
TICKET prefix[tags]: subject(e.g.,QVAC-123 feat[api]: add new endpoint)
- Commits:
- Allowed prefixes: feat, fix, doc, test, mod, chore, infra
- Allowed tags: [api] (non-breaking), [bc] (breaking changes)
- Required PR body content:
[bc]ā MUST include BEFORE/AFTER code examples[api]ā MUST include at least one code block showing new API usage
- When creating commits or PRs, ALWAYS validate against this format
- Test locally:
node scripts/sdk/validator.cjs --type=commit --msg="your message"