@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.
@@ -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,CA6ZzC,CAAC;AAEF,eAAO,MAAM,UAAU,UAwGtB,CAAC"}
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"}