@kardoe/quickback 0.5.15 → 0.6.0
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.
- package/dist/commands/create.js +6 -7
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/cursor.d.ts +11 -0
- package/dist/commands/cursor.d.ts.map +1 -0
- package/dist/commands/cursor.js +156 -0
- package/dist/commands/cursor.js.map +1 -0
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +27 -3
- package/dist/commands/docs.js.map +1 -1
- package/dist/commands/mcp.d.ts +21 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +330 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/cursor/quickback.mdc +235 -0
- package/dist/docs/content.d.ts.map +1 -1
- package/dist/docs/content.js +18 -13
- package/dist/docs/content.js.map +1 -1
- package/dist/index.js +19 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +32 -20
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/file-loader.d.ts +2 -2
- package/dist/lib/file-loader.js +8 -8
- package/dist/lib/file-loader.js.map +1 -1
- package/dist/skill/SKILL.md +629 -0
- package/dist/skill/agents/quickback-specialist/AGENT.md +220 -0
- package/dist/skill/skill/SKILL.md +629 -0
- package/dist/skill/skill/agents/quickback-specialist/AGENT.md +220 -0
- package/package.json +9 -4
- package/src/cursor/quickback.mdc +235 -0
- package/src/skill/SKILL.md +1 -3
- package/src/skill/agents/quickback-specialist/AGENT.md +2 -2
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server command — starts a stdio-based MCP server
|
|
3
|
+
*
|
|
4
|
+
* Exposes Quickback documentation and project context to any MCP-compatible AI tool
|
|
5
|
+
* (Claude Desktop, Cursor, VS Code Copilot, Windsurf, etc.)
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* quickback mcp # Start stdio MCP server
|
|
9
|
+
*
|
|
10
|
+
* Configuration (claude_desktop_config.json or .cursor/mcp.json):
|
|
11
|
+
* {
|
|
12
|
+
* "mcpServers": {
|
|
13
|
+
* "quickback": {
|
|
14
|
+
* "command": "npx",
|
|
15
|
+
* "args": ["@kardoe/quickback", "mcp"]
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
*/
|
|
20
|
+
import { DOCS, TOPIC_LIST } from "../docs/content.js";
|
|
21
|
+
import { promises as fs } from "fs";
|
|
22
|
+
import { join } from "path";
|
|
23
|
+
export async function mcp() {
|
|
24
|
+
const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
|
|
25
|
+
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
26
|
+
const { z } = await import("zod");
|
|
27
|
+
const server = new McpServer({
|
|
28
|
+
name: "quickback",
|
|
29
|
+
version: "1.0.0",
|
|
30
|
+
});
|
|
31
|
+
// --- Documentation Resources ---
|
|
32
|
+
// Each doc topic is available as a readable resource
|
|
33
|
+
for (const topic of TOPIC_LIST) {
|
|
34
|
+
const doc = DOCS[topic];
|
|
35
|
+
if (!doc)
|
|
36
|
+
continue;
|
|
37
|
+
server.resource(topic, `quickback://docs/${topic}`, { description: doc.title, mimeType: "text/markdown" }, async () => ({
|
|
38
|
+
contents: [
|
|
39
|
+
{
|
|
40
|
+
uri: `quickback://docs/${topic}`,
|
|
41
|
+
text: `# ${doc.title}\n\n${doc.content}`,
|
|
42
|
+
mimeType: "text/markdown",
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
// --- Documentation Tools ---
|
|
48
|
+
server.tool("list_topics", "List all available Quickback documentation topics", {}, async () => {
|
|
49
|
+
const grouped = {};
|
|
50
|
+
for (const topic of TOPIC_LIST) {
|
|
51
|
+
const section = topic.split("/")[0];
|
|
52
|
+
if (!grouped[section])
|
|
53
|
+
grouped[section] = [];
|
|
54
|
+
grouped[section].push(topic);
|
|
55
|
+
}
|
|
56
|
+
let output = "# Quickback Documentation Topics\n\n";
|
|
57
|
+
for (const [section, topics] of Object.entries(grouped)) {
|
|
58
|
+
output += `## ${section}\n`;
|
|
59
|
+
for (const t of topics) {
|
|
60
|
+
const doc = DOCS[t];
|
|
61
|
+
output += `- **${t}** — ${doc?.title || t}\n`;
|
|
62
|
+
}
|
|
63
|
+
output += "\n";
|
|
64
|
+
}
|
|
65
|
+
return { content: [{ type: "text", text: output }] };
|
|
66
|
+
});
|
|
67
|
+
server.tool("get_doc", "Get a specific Quickback documentation topic by key. Supports fuzzy suffix matching (e.g., 'firewall' matches 'compiler/definitions/firewall').", { topic: z.string().describe("Topic key or short name (e.g., 'firewall', 'compiler/definitions/firewall')") }, async ({ topic }) => {
|
|
68
|
+
// Exact match first
|
|
69
|
+
let resolved = DOCS[topic] ? topic : null;
|
|
70
|
+
// Fuzzy suffix match
|
|
71
|
+
if (!resolved) {
|
|
72
|
+
const matches = TOPIC_LIST.filter((t) => t === topic || t.endsWith(`/${topic}`));
|
|
73
|
+
if (matches.length === 1) {
|
|
74
|
+
resolved = matches[0];
|
|
75
|
+
}
|
|
76
|
+
else if (matches.length > 1) {
|
|
77
|
+
return {
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
type: "text",
|
|
81
|
+
text: `Ambiguous topic "${topic}". Did you mean one of:\n${matches.map((m) => `- ${m}`).join("\n")}`,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!resolved || !DOCS[resolved]) {
|
|
88
|
+
return {
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: "text",
|
|
92
|
+
text: `Topic "${topic}" not found. Use list_topics to see all available topics.`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const doc = DOCS[resolved];
|
|
98
|
+
return {
|
|
99
|
+
content: [
|
|
100
|
+
{
|
|
101
|
+
type: "text",
|
|
102
|
+
text: `# ${doc.title}\n\n${doc.content}`,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
server.tool("search_docs", "Search Quickback documentation content by keyword. Returns matching topics with relevant excerpts.", { query: z.string().describe("Search query (case-insensitive)") }, async ({ query }) => {
|
|
108
|
+
const lower = query.toLowerCase();
|
|
109
|
+
const results = [];
|
|
110
|
+
for (const topic of TOPIC_LIST) {
|
|
111
|
+
const doc = DOCS[topic];
|
|
112
|
+
if (!doc)
|
|
113
|
+
continue;
|
|
114
|
+
const content = doc.content.toLowerCase();
|
|
115
|
+
const title = doc.title.toLowerCase();
|
|
116
|
+
if (content.includes(lower) || title.includes(lower)) {
|
|
117
|
+
// Extract excerpt around first match
|
|
118
|
+
const idx = content.indexOf(lower);
|
|
119
|
+
const start = Math.max(0, idx - 100);
|
|
120
|
+
const end = Math.min(content.length, idx + lower.length + 100);
|
|
121
|
+
const excerpt = (start > 0 ? "..." : "") +
|
|
122
|
+
doc.content.substring(start, end).replace(/\n/g, " ") +
|
|
123
|
+
(end < content.length ? "..." : "");
|
|
124
|
+
results.push({ topic, title: doc.title, excerpt });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (results.length === 0) {
|
|
128
|
+
return {
|
|
129
|
+
content: [
|
|
130
|
+
{
|
|
131
|
+
type: "text",
|
|
132
|
+
text: `No results found for "${query}".`,
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
let output = `# Search results for "${query}" (${results.length} matches)\n\n`;
|
|
138
|
+
for (const r of results.slice(0, 20)) {
|
|
139
|
+
output += `## ${r.topic}\n**${r.title}**\n${r.excerpt}\n\n`;
|
|
140
|
+
}
|
|
141
|
+
if (results.length > 20) {
|
|
142
|
+
output += `\n*...and ${results.length - 20} more results*\n`;
|
|
143
|
+
}
|
|
144
|
+
return { content: [{ type: "text", text: output }] };
|
|
145
|
+
});
|
|
146
|
+
// --- Project-Aware Tools ---
|
|
147
|
+
server.tool("read_config", "Read the current project's quickback.config.ts file. Returns the raw TypeScript source.", {}, async () => {
|
|
148
|
+
const configPath = await findProjectFile([
|
|
149
|
+
"quickback/quickback.config.ts",
|
|
150
|
+
"quickback.config.ts",
|
|
151
|
+
]);
|
|
152
|
+
if (!configPath) {
|
|
153
|
+
return {
|
|
154
|
+
content: [
|
|
155
|
+
{
|
|
156
|
+
type: "text",
|
|
157
|
+
text: "No quickback.config.ts found. Not in a Quickback project directory.",
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const source = await fs.readFile(configPath, "utf-8");
|
|
163
|
+
return {
|
|
164
|
+
content: [
|
|
165
|
+
{
|
|
166
|
+
type: "text",
|
|
167
|
+
text: `# quickback.config.ts\n\n\`\`\`typescript\n${source}\n\`\`\``,
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
server.tool("list_features", "List all features defined in the current Quickback project. Shows feature directories and their table definition files.", {}, async () => {
|
|
173
|
+
const featuresDir = await findProjectDir([
|
|
174
|
+
"quickback/features",
|
|
175
|
+
"features",
|
|
176
|
+
]);
|
|
177
|
+
if (!featuresDir) {
|
|
178
|
+
return {
|
|
179
|
+
content: [
|
|
180
|
+
{
|
|
181
|
+
type: "text",
|
|
182
|
+
text: "No features directory found. Not in a Quickback project directory.",
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const entries = await fs.readdir(featuresDir, { withFileTypes: true });
|
|
188
|
+
const features = [];
|
|
189
|
+
for (const entry of entries) {
|
|
190
|
+
if (!entry.isDirectory())
|
|
191
|
+
continue;
|
|
192
|
+
const featureDir = join(featuresDir, entry.name);
|
|
193
|
+
const files = await fs.readdir(featureDir);
|
|
194
|
+
features.push({
|
|
195
|
+
name: entry.name,
|
|
196
|
+
files: files.filter((f) => f.endsWith(".ts")),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
if (features.length === 0) {
|
|
200
|
+
return {
|
|
201
|
+
content: [
|
|
202
|
+
{
|
|
203
|
+
type: "text",
|
|
204
|
+
text: "Features directory exists but contains no feature definitions.",
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
let output = `# Project Features (${features.length})\n\n`;
|
|
210
|
+
for (const f of features) {
|
|
211
|
+
output += `## ${f.name}/\n`;
|
|
212
|
+
for (const file of f.files) {
|
|
213
|
+
output += `- ${file}\n`;
|
|
214
|
+
}
|
|
215
|
+
output += "\n";
|
|
216
|
+
}
|
|
217
|
+
return { content: [{ type: "text", text: output }] };
|
|
218
|
+
});
|
|
219
|
+
server.tool("read_feature", "Read the source code of a specific feature. Returns all .ts files in the feature directory.", {
|
|
220
|
+
feature: z.string().describe("Feature name (directory name under quickback/features/)"),
|
|
221
|
+
}, async ({ feature }) => {
|
|
222
|
+
const featuresDir = await findProjectDir([
|
|
223
|
+
"quickback/features",
|
|
224
|
+
"features",
|
|
225
|
+
]);
|
|
226
|
+
if (!featuresDir) {
|
|
227
|
+
return {
|
|
228
|
+
content: [
|
|
229
|
+
{
|
|
230
|
+
type: "text",
|
|
231
|
+
text: "No features directory found. Not in a Quickback project directory.",
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
const featureDir = join(featuresDir, feature);
|
|
237
|
+
try {
|
|
238
|
+
await fs.access(featureDir);
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
return {
|
|
242
|
+
content: [
|
|
243
|
+
{
|
|
244
|
+
type: "text",
|
|
245
|
+
text: `Feature "${feature}" not found. Use list_features to see available features.`,
|
|
246
|
+
},
|
|
247
|
+
],
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
const files = (await fs.readdir(featureDir)).filter((f) => f.endsWith(".ts"));
|
|
251
|
+
let output = `# Feature: ${feature}\n\n`;
|
|
252
|
+
for (const file of files) {
|
|
253
|
+
const source = await fs.readFile(join(featureDir, file), "utf-8");
|
|
254
|
+
output += `## ${file}\n\n\`\`\`typescript\n${source}\n\`\`\`\n\n`;
|
|
255
|
+
}
|
|
256
|
+
// Check for handlers/ subdirectory
|
|
257
|
+
const handlersDir = join(featureDir, "handlers");
|
|
258
|
+
try {
|
|
259
|
+
const handlerFiles = (await fs.readdir(handlersDir)).filter((f) => f.endsWith(".ts"));
|
|
260
|
+
for (const file of handlerFiles) {
|
|
261
|
+
const source = await fs.readFile(join(handlersDir, file), "utf-8");
|
|
262
|
+
output += `## handlers/${file}\n\n\`\`\`typescript\n${source}\n\`\`\`\n\n`;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
// No handlers directory
|
|
267
|
+
}
|
|
268
|
+
return { content: [{ type: "text", text: output }] };
|
|
269
|
+
});
|
|
270
|
+
server.tool("read_schema_registry", "Read the project's compiled schema-registry.json. Contains full metadata about all tables, columns, security rules, views, and actions.", {}, async () => {
|
|
271
|
+
const registryPath = await findProjectFile([
|
|
272
|
+
"src/schema-registry.json",
|
|
273
|
+
"dist/schema-registry.json",
|
|
274
|
+
"schema-registry.json",
|
|
275
|
+
]);
|
|
276
|
+
if (!registryPath) {
|
|
277
|
+
return {
|
|
278
|
+
content: [
|
|
279
|
+
{
|
|
280
|
+
type: "text",
|
|
281
|
+
text: "No schema-registry.json found. Run 'quickback compile' to generate it.",
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
const content = await fs.readFile(registryPath, "utf-8");
|
|
287
|
+
return {
|
|
288
|
+
content: [
|
|
289
|
+
{
|
|
290
|
+
type: "text",
|
|
291
|
+
text: `# Schema Registry\n\n\`\`\`json\n${content}\n\`\`\``,
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
};
|
|
295
|
+
});
|
|
296
|
+
// --- Helpers ---
|
|
297
|
+
async function findProjectFile(candidates) {
|
|
298
|
+
const cwd = process.cwd();
|
|
299
|
+
for (const candidate of candidates) {
|
|
300
|
+
const full = join(cwd, candidate);
|
|
301
|
+
try {
|
|
302
|
+
await fs.access(full);
|
|
303
|
+
return full;
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
// Not found, try next
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
async function findProjectDir(candidates) {
|
|
312
|
+
const cwd = process.cwd();
|
|
313
|
+
for (const candidate of candidates) {
|
|
314
|
+
const full = join(cwd, candidate);
|
|
315
|
+
try {
|
|
316
|
+
const stat = await fs.stat(full);
|
|
317
|
+
if (stat.isDirectory())
|
|
318
|
+
return full;
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
// Not found, try next
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
// --- Start Server ---
|
|
327
|
+
const transport = new StdioServerTransport();
|
|
328
|
+
await server.connect(transport);
|
|
329
|
+
}
|
|
330
|
+
//# sourceMappingURL=mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Quickback API engine - use when defining tables, schemas, security (Firewall, Access, Guards, Masking), actions, views, validation, or deploying Quickback projects
|
|
3
|
+
globs: quickback/**/*.ts
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Quickback
|
|
7
|
+
|
|
8
|
+
Quickback is two things:
|
|
9
|
+
|
|
10
|
+
1. **Compiler** — Transforms declarative TypeScript definitions into secure, production-ready APIs
|
|
11
|
+
2. **Stack** — A Supabase alternative running entirely on Cloudflare (D1, R2, KV, Durable Objects, Queues, Workers AI)
|
|
12
|
+
|
|
13
|
+
The output is standard TypeScript (Hono, Drizzle, Better Auth) running on your own infrastructure.
|
|
14
|
+
|
|
15
|
+
## Project Structure
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
my-app/
|
|
19
|
+
├── quickback/
|
|
20
|
+
│ ├── quickback.config.ts
|
|
21
|
+
│ └── features/
|
|
22
|
+
│ └── {feature-name}/
|
|
23
|
+
│ ├── {table}.ts # Schema + security (defineTable)
|
|
24
|
+
│ ├── actions.ts # Custom actions (optional)
|
|
25
|
+
│ └── handlers/ # Action handlers (optional)
|
|
26
|
+
└── ...
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## quickback.config.ts
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { defineConfig, defineRuntime, defineDatabase, defineAuth } from '@quickback/compiler';
|
|
33
|
+
|
|
34
|
+
export default defineConfig({
|
|
35
|
+
name: "my-saas-app",
|
|
36
|
+
providers: {
|
|
37
|
+
runtime: defineRuntime("cloudflare"),
|
|
38
|
+
database: defineDatabase("cloudflare-d1"),
|
|
39
|
+
auth: defineAuth("better-auth"),
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Automatic Audit Fields
|
|
45
|
+
|
|
46
|
+
Quickback auto-injects: `createdAt`, `modifiedAt`, `deletedAt`, `createdBy`, `modifiedBy`, `deletedBy`. Do NOT define these in schemas.
|
|
47
|
+
|
|
48
|
+
## defineTable() — Schema + Security
|
|
49
|
+
|
|
50
|
+
Each feature file exports a Drizzle table and a `defineTable()` default export with security config:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
|
54
|
+
import { defineTable } from "@quickback/compiler";
|
|
55
|
+
|
|
56
|
+
export const todos = sqliteTable("todos", {
|
|
57
|
+
id: integer("id").primaryKey(),
|
|
58
|
+
title: text("title").notNull(),
|
|
59
|
+
description: text("description"),
|
|
60
|
+
completed: integer("completed", { mode: "boolean" }).default(false),
|
|
61
|
+
userId: text("user_id").notNull(),
|
|
62
|
+
organizationId: text("organization_id").notNull(),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export default defineTable(todos, {
|
|
66
|
+
firewall: { organization: {}, owner: {} },
|
|
67
|
+
crud: {
|
|
68
|
+
list: { access: { roles: ["member", "admin"] } },
|
|
69
|
+
get: { access: { roles: ["member", "admin"] } },
|
|
70
|
+
create: { access: { roles: ["member", "admin"] } },
|
|
71
|
+
update: { access: { roles: ["admin"] } },
|
|
72
|
+
delete: { access: { roles: ["admin"] }, mode: "soft" },
|
|
73
|
+
},
|
|
74
|
+
guards: {
|
|
75
|
+
createable: ["title", "description", "completed"],
|
|
76
|
+
updatable: ["title", "description", "completed"],
|
|
77
|
+
},
|
|
78
|
+
masking: {
|
|
79
|
+
userId: { type: "redact", show: { roles: ["admin"] } },
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Four Security Layers
|
|
85
|
+
|
|
86
|
+
Every request passes through: `Request → Firewall → Access → Guards → Database → Masking → Response`
|
|
87
|
+
|
|
88
|
+
### 1. Firewall — Data Isolation
|
|
89
|
+
|
|
90
|
+
Auto WHERE clauses for tenant isolation:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
firewall: {
|
|
94
|
+
organization: {}, // WHERE organizationId = ctx.activeOrgId
|
|
95
|
+
owner: {}, // AND userId = ctx.userId
|
|
96
|
+
team: {}, // WHERE teamId = ctx.activeTeamId
|
|
97
|
+
softDelete: {}, // AND deletedAt IS NULL
|
|
98
|
+
exception: true, // No filtering (public)
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 2. Access — Permission Checks
|
|
103
|
+
|
|
104
|
+
Role-based and record-based (deny by default):
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
crud: {
|
|
108
|
+
list: { access: { roles: ['member'] } },
|
|
109
|
+
update: {
|
|
110
|
+
access: {
|
|
111
|
+
or: [
|
|
112
|
+
{ roles: ['admin'] },
|
|
113
|
+
{ record: { createdBy: { equals: '$ctx.userId' } } },
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Operators: `equals`, `notEquals`, `in`, `notIn`, `greaterThan`, `lessThan`
|
|
121
|
+
Context: `$ctx.userId`, `$ctx.activeOrgId`, `$ctx.roles`
|
|
122
|
+
|
|
123
|
+
### 3. Guards — Field Protection
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
guards: {
|
|
127
|
+
createable: ['name', 'description'],
|
|
128
|
+
updatable: ['description'],
|
|
129
|
+
immutable: ['invoiceNumber'],
|
|
130
|
+
protected: { status: ['approve'] }, // Only via named actions
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 4. Masking — PII Redaction
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
masking: {
|
|
138
|
+
ssn: { type: 'ssn', show: { roles: ['admin'] } },
|
|
139
|
+
email: { type: 'email', show: { roles: ['admin'], or: 'owner' } },
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Types: `email`, `phone`, `ssn`, `creditCard`, `name`, `redact`, `custom`
|
|
144
|
+
|
|
145
|
+
## Views — Column Projections
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
views: {
|
|
149
|
+
summary: { fields: ['id', 'name'], access: { roles: ['member'] } },
|
|
150
|
+
full: { fields: ['id', 'name', 'phone', 'ssn'], access: { roles: ['admin'] } },
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Validation
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
validation: {
|
|
158
|
+
name: { minLength: 1, maxLength: 100 },
|
|
159
|
+
capacity: { min: 1, max: 1000 },
|
|
160
|
+
roomType: { enum: ['meeting', 'conference'] },
|
|
161
|
+
email: { email: true },
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Actions — Custom Business Logic
|
|
166
|
+
|
|
167
|
+
Separate `actions.ts` file:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import { todos } from './todos';
|
|
171
|
+
import { defineActions } from '@quickback/compiler';
|
|
172
|
+
import { z } from 'zod';
|
|
173
|
+
|
|
174
|
+
export default defineActions(todos, {
|
|
175
|
+
complete: {
|
|
176
|
+
description: "Mark todo as complete",
|
|
177
|
+
input: z.object({ completedAt: z.string().datetime().optional() }),
|
|
178
|
+
guard: { roles: ["member", "admin"] },
|
|
179
|
+
execute: async ({ db, record, ctx, input }) => {
|
|
180
|
+
await db.update(todos).set({ completed: true }).where(eq(todos.id, record.id));
|
|
181
|
+
return { success: true };
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Record-based: `POST /api/v1/{resource}/:id/{action}`
|
|
188
|
+
Standalone: `standalone: true`, custom `path` and `method`
|
|
189
|
+
|
|
190
|
+
## CMS Layouts
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
layouts: {
|
|
194
|
+
default: {
|
|
195
|
+
sections: [
|
|
196
|
+
{ label: "Details", columns: 2, fields: ["name", "email"] },
|
|
197
|
+
{ label: "Notes", collapsed: true, fields: ["notes"] },
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## API Reference
|
|
204
|
+
|
|
205
|
+
| Method | Endpoint | Description |
|
|
206
|
+
|--------|----------|-------------|
|
|
207
|
+
| `GET` | `/api/v1/{resource}` | List |
|
|
208
|
+
| `GET` | `/api/v1/{resource}/:id` | Get |
|
|
209
|
+
| `POST` | `/api/v1/{resource}` | Create |
|
|
210
|
+
| `PATCH` | `/api/v1/{resource}/:id` | Update |
|
|
211
|
+
| `DELETE` | `/api/v1/{resource}/:id` | Delete |
|
|
212
|
+
|
|
213
|
+
Query params: `?limit=50&offset=0`, `?status=active`, `?sort=-createdAt`, `?fields=id,name`, `?search=text`
|
|
214
|
+
Filter operators: `.gt`, `.gte`, `.lt`, `.lte`, `.ne`, `.like`, `.in`
|
|
215
|
+
|
|
216
|
+
## Database Dialects
|
|
217
|
+
|
|
218
|
+
| Stack | Import | Table Function |
|
|
219
|
+
|-------|--------|----------------|
|
|
220
|
+
| Cloudflare D1 / SQLite | `drizzle-orm/sqlite-core` | `sqliteTable` |
|
|
221
|
+
| Supabase / PostgreSQL | `drizzle-orm/pg-core` | `pgTable` |
|
|
222
|
+
|
|
223
|
+
## CLI Commands
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
quickback create <template> <name> # Create project
|
|
227
|
+
quickback compile # Compile definitions
|
|
228
|
+
quickback docs [topic] # Show documentation
|
|
229
|
+
quickback claude install # Install Claude Code skill
|
|
230
|
+
quickback cursor install # Install Cursor rules
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Full Documentation
|
|
234
|
+
|
|
235
|
+
https://docs.quickback.dev
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/docs/content.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/docs/content.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAiazC,CAAC;AAEF,eAAO,MAAM,UAAU,UAyGtB,CAAC"}
|