Skip to content

Commit 1ebc71a

Browse files
authored
feat: xanoscript to be included in generate-repo command (#152)
2 parents cb4b970 + a949bf7 commit 1ebc71a

File tree

8 files changed

+166
-28
lines changed

8 files changed

+166
-28
lines changed

.changeset/brave-suits-punch.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@calycode/core": minor
3+
"@calycode/cli": minor
4+
---
5+
6+
feat: include mcp_server, addon, agent, middleware in the workspace xs export

.changeset/young-clowns-push.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@calycode/core': minor
3+
'@calycode/cli': minor
4+
---
5+
6+
chore: add query VERBS into xanoscript generation, to avoid overrides
7+
chore: cleanup of repo directory structure generation, added notes to the function
8+
feat: integrating xanoscript into repo-generation

.continue/rules/review.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
invokable: true
3+
---
4+
5+
Review this code for potential issues, including:
6+
7+
- Correct Xano CLI command registration (commands are discoverable, correctly registered, and context is managed)
8+
- Monorepo structure consistency (all packages properly separated, with clear interfaces and responsibilities)
9+
- Adherence to TypeScript best practices, type safety, and explicit type usage
10+
- Efficient and reliable automation in code generation, documentation, OAS, registry, backup, and testing workflows
11+
- Clear separation between CLI, business logic (core), types, and utility functions
12+
- Valid, up-to-date configuration, schema, and CI/CD scripts
13+
- Readable, maintainable, and well-commented code (especially around CLI entrypoints and registry features)
14+
- Secure handling of credentials, tokens, and configuration (especially when running local/GitHub Actions)
15+
- Actions and scripts should not break if Xano metadata/API structure changes subtly
16+
- Registry and codegen should be robust for large projects and handle edge cases (naming, conflicts, missing metadata)
17+
- Documentation and CLI output should be clear and actionable for humans
18+
19+
Provide specific, actionable feedback for improvements.

AGENTS.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Repository Overview
2+
3+
## Project Description
4+
This project provides a suite of tools to improve developer experience with Xano, focusing on clarity, transparency, automation, and robust version control for Xano backend developments. The main goal is to minimize reliance on AI by offering reliable CLI-based workflows for code generation, documentation, testing, and registry management. The tools allow teams to automate backups, generate OpenAPI specifications, create fully browsable repositories from Xano metadata, scaffold and serve reusable component registries, and run automated tests—all integrated with Git workflows and CI/CD.
5+
6+
**Key technologies used:**
7+
- Node.js (>=18)
8+
- TypeScript
9+
- PNPM workspaces
10+
- TurboRepo for monorepo builds
11+
- Jest for testing
12+
- Esbuild/Rollup for bundling
13+
- Commander.js for the CLI
14+
- js-yaml for YAML processing
15+
16+
## Architecture Overview
17+
- **Monorepo Layout:** Managed by PNPM and TurboRepo with logical separation by package:
18+
- `@calycode/cli`: The main CLI.
19+
- `@calycode/core`: Business and program logic for processing Xano data and interfacing with metadata APIs.
20+
- `@calycode/types`: Shared type definitions.
21+
- `@calycode/utils`: Shared utilities.
22+
- **CLI Entry Point:** The CLI (`xano`) registers commands for setup, codegen, OAS gen, tests, registry, backup, etc. Each command routes into core business logic via composed packages.
23+
- **Extensible Registry System:** For sharing and consuming standardized Xano components (functions, tables, queries, etc.).
24+
- **Configurable Storage and Context Management:** Multi-user, multi-environment ready.
25+
- **Automated CI/CD Ready:** Designed to run in GitHub Actions or local workflows.
26+
27+
**Data Flow & Interactions:**
28+
- User issues a CLI command (`xano ...`)
29+
- CLI parses config/context, invokes logic in `@calycode/core`
30+
- Utilities help fetch, transform, and validate data
31+
- Output is written to file system, appropriate directories, or remote registries
32+
- CI/CD scripts automate build and publish steps
33+
34+
## Directory Structure
35+
- **./packages**: Main monorepo packages (cli, core, types, utils)
36+
- **./docs**: Documentation, including CLI command references
37+
- **./schemas**: JSON schemas for registries and related features
38+
- **.github/**: GitHub workflows for CI/CD and publishing
39+
- **scripts/**: Build, clean, and documentation generation scripts
40+
- **test-config.json, turbo.json, pnpm-workspace.yaml**: Config files for monorepo tooling
41+
- **Entry Points:**
42+
- CLI: `packages/cli/src/index.ts` and `dist/index.cjs`
43+
- Core logic: `packages/core/src/index.ts`
44+
45+
## Development Workflow
46+
- **Install/Setup**: `pnpm install` to install everything. (Requires pnpm)
47+
- **Build**: `pnpm build` (runs TurboRepo, builds all packages)
48+
- **Run CLI**: `pnpm xano <command>` or if globally linked, `xano <command>`
49+
- **Testing**: `pnpm test` (runs tests across all packages)
50+
- **Linting/Formatting**: Eslint/Prettier with default configs, e.g. `pnpm lint`, some packages have their own config and scripts
51+
- **CI/CD**: See `.github/workflows`. Ready for automated release, docs update, and registry actions
52+
53+
---
54+
55+
- See also `docs/README.md` and `docs/commands` for command reference.
56+
- Key configuration in `.changeset/`, `schemas/registry/`, and package-level config files.

packages/core/src/features/process-xano/core/generateRunReadme.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function getDboLink(method, dboMapping) {
3131
const dboId = method.context?.dbo?.id;
3232
if (dboId && dboMapping[dboId]) {
3333
const dboName = dboMapping[dboId].name;
34-
const dboPath = `/src/dbo/${dboName.replace(/\//g, '_')}/`;
34+
const dboPath = `/src/table/${dboName.replace(/\//g, '_')}/`;
3535
return `**[${dboName}](${dboPath})**`;
3636
}
3737
return '';

packages/core/src/features/process-xano/core/processItem.ts

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,53 @@ import { generateQueryLogicDescription } from './generateRunReadme';
22
import { convertXanoDBDescription } from '../adapters/dbmlGenerator';
33
import { sanitizeFileName, joinPath } from '@repo/utils';
44

5-
function getItemDir({ key, item, dirPath, appMapping }) {
6-
let baseDir;
7-
let itemName = item.name || 'unnamed';
8-
9-
if (key === 'query' && item.app?.id) {
10-
const appName = appMapping[item.app.id] || `app_${item.app.id}`;
11-
baseDir = joinPath('app', appName, sanitizeFileName(itemName));
12-
} else if (key === 'app') {
13-
baseDir = joinPath(dirPath, itemName);
14-
} else {
15-
baseDir = joinPath(dirPath, sanitizeFileName(itemName));
16-
}
17-
18-
// Queries may have a verb subfolder
19-
if (key === 'query' && item.verb) {
20-
baseDir = joinPath(baseDir, item.verb);
5+
/**
6+
* Determines the target directory path for a given schema item, normalizing names for human-friendly structure.
7+
* Handles special cases for item types (e.g., 'dbo', 'query', 'trigger') and integrates API group mappings.
8+
*
9+
* @param {string} key - The schema item type (e.g., 'dbo', 'query', 'app', 'trigger').
10+
* @param {Object} item - The schema item object, with metadata and Xano schema.
11+
* @param {string} dirPath - The base directory path for output.
12+
* @param {Object} appMapping - Maps app IDs to human-friendly names.
13+
* @returns {string} The computed directory path for the item.
14+
*/
15+
function getItemDir({
16+
key,
17+
item,
18+
dirPath,
19+
appMapping,
20+
}: {
21+
key: string;
22+
item: any;
23+
dirPath: string;
24+
appMapping: Record<string, string>;
25+
}): string {
26+
// Get a safe, human-friendly item name
27+
const itemName = sanitizeFileName(item.name ?? 'unnamed');
28+
29+
// Compute the base directory depending on the item type
30+
switch (key) {
31+
case 'query': {
32+
// Use mapped app name if available
33+
const appId = item.app?.id;
34+
const appName = appId ? sanitizeFileName(appMapping[appId] ?? `app_${appId}`) : undefined;
35+
let baseDir = appId ? joinPath('app', appName, itemName) : joinPath(dirPath, itemName);
36+
// Add verb subfolder if present
37+
if (item.verb) baseDir = joinPath(baseDir, item.verb);
38+
return baseDir;
39+
}
40+
case 'app':
41+
return joinPath(dirPath, itemName);
42+
case 'dbo':
43+
return joinPath('table', itemName);
44+
case 'trigger':
45+
if (item.obj_type === 'database') {
46+
return joinPath('table', key, itemName);
47+
}
48+
return joinPath(dirPath, itemName);
49+
default:
50+
return joinPath(dirPath, itemName);
2151
}
22-
return baseDir;
2352
}
2453

2554
function makeReadmeContent({ key, item, functionMapping, dboMapping }) {

packages/core/src/features/process-xano/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ async function rebuildDirectoryStructure({
2828
workspace?: string;
2929
branch?: string;
3030
}) {
31-
const { dbo, app, query, function: func, addon, trigger, task, middleware } = jsonData.payload;
31+
const { addon, app, dbo, function: func, middleware, query, task, trigger } = jsonData.payload;
3232

3333
core.emit('start', { name: 'generate-repo', payload: null });
3434

3535
// Mappings
36-
const appMapping = buildMapping(app, (a) => a.name.replace(/\//g, '_'));
36+
const appMapping = buildMapping(app, (a) => sanitizeFileName(a.name));
3737
const appDescriptions = buildMapping(app, (a) => a.description || '//...');
3838
const functionMapping = buildMapping(func, (f) => ({
3939
name: f.name,
@@ -42,7 +42,7 @@ async function rebuildDirectoryStructure({
4242
}));
4343
const dboMapping = buildMapping(dbo, (d) => ({
4444
name: d.name,
45-
path: `dbo/${sanitizeFileName(d.name)}`,
45+
path: `table/${sanitizeFileName(d.name)}`,
4646
description: d.description ?? '',
4747
}));
4848
const appQueries = {};

packages/core/src/implementations/build-xanoscript-repo.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,25 @@ import { metaApiGet, sanitizeFileName } from '@repo/utils';
22
import type { Caly } from '..';
33
import type { CoreContext } from '@repo/types';
44

5+
const SUPPORTED_ENTITIES = [
6+
'addon',
7+
'agent',
8+
'agent/trigger',
9+
'apigroup',
10+
'function',
11+
'mcp_server',
12+
'mcp_server/trigger',
13+
'middleware',
14+
'realtime/channel',
15+
'realtime/channel/trigger',
16+
'table',
17+
'table/trigger',
18+
'task',
19+
'tool',
20+
'trigger',
21+
'workflow_test',
22+
];
23+
524
async function fetchAndProcessEntities({
625
baseUrl,
726
token,
@@ -36,9 +55,11 @@ async function fetchAndProcessEntities({
3655
const sanitizedName = sanitizeFileName(name);
3756
const path =
3857
entity === 'api'
39-
? sanitizedName
58+
? `${sanitizedName}/${item.verb.toUpperCase()}`
4059
: entity === 'table'
41-
? `dbo/${sanitizedName}`
60+
? `table/${sanitizedName}`
61+
: entity === 'apigroup'
62+
? `app/${sanitizedName}`
4263
: `${entity}/${sanitizedName}`;
4364
const metaDataContent = item;
4465
delete metaDataContent.xanoscript;
@@ -89,13 +110,13 @@ async function buildXanoscriptRepoImplementation({
89110
const branchLabel = branchConfig.label;
90111
// Supported entities: functions, tables, api groups > apis
91112
// [ ] Add hidden pagination to make sure that all functions and queries are captured.
92-
for (const entity of ['function', 'table']) {
113+
for (const entity of SUPPORTED_ENTITIES) {
93114
core.emit('progress', {
94115
name: 'xs-repo-generation',
95116
payload: {
96117
entity,
97118
},
98-
percent: ((['function', 'table'].indexOf(entity) + 1) / 3) * 60,
119+
percent: ((SUPPORTED_ENTITIES.indexOf(entity) + 1) / 3) * 60,
99120
});
100121
const tempResults = await fetchAndProcessEntities({
101122
baseUrl,
@@ -111,7 +132,7 @@ async function buildXanoscriptRepoImplementation({
111132
payload: {
112133
entity,
113134
},
114-
percent: ((['function', 'table'].indexOf(entity) + 1) / 2) * 60,
135+
percent: ((SUPPORTED_ENTITIES.indexOf(entity) + 1) / 2) * 60,
115136
});
116137
}
117138

@@ -158,7 +179,7 @@ async function buildXanoscriptRepoImplementation({
158179

159180
results.push(
160181
...tempResults.map((item) => ({
161-
// ADd the apigroup name to the path for nice folder structure
182+
// Add the apigroup name to the path for nice folder structure
162183
path: `app/${apiGroupPath}/${item.path}`,
163184
content: item.content,
164185
}))
@@ -176,4 +197,3 @@ async function buildXanoscriptRepoImplementation({
176197
}
177198

178199
export { buildXanoscriptRepoImplementation };
179-

0 commit comments

Comments
 (0)