@archlast/cli 0.0.1
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/README.md +141 -0
- package/dist/analyzer.d.ts +96 -0
- package/dist/analyzer.d.ts.map +1 -0
- package/dist/analyzer.js +404 -0
- package/dist/auth.d.ts +14 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +106 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +322875 -0
- package/dist/commands/build.d.ts +6 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +36 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +23 -0
- package/dist/commands/data.d.ts +6 -0
- package/dist/commands/data.d.ts.map +1 -0
- package/dist/commands/data.js +300 -0
- package/dist/commands/deploy.d.ts +9 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +59 -0
- package/dist/commands/dev.d.ts +10 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +132 -0
- package/dist/commands/generate.d.ts +6 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +100 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/logs.d.ts +10 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +38 -0
- package/dist/commands/pull.d.ts +16 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +415 -0
- package/dist/commands/restart.d.ts +11 -0
- package/dist/commands/restart.d.ts.map +1 -0
- package/dist/commands/restart.js +63 -0
- package/dist/commands/start.d.ts +11 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +74 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +69 -0
- package/dist/commands/stop.d.ts +8 -0
- package/dist/commands/stop.d.ts.map +1 -0
- package/dist/commands/stop.js +23 -0
- package/dist/commands/upgrade.d.ts +12 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +77 -0
- package/dist/docker/compose.d.ts +3 -0
- package/dist/docker/compose.d.ts.map +1 -0
- package/dist/docker/compose.js +47 -0
- package/dist/docker/config.d.ts +12 -0
- package/dist/docker/config.d.ts.map +1 -0
- package/dist/docker/config.js +183 -0
- package/dist/docker/manager.d.ts +19 -0
- package/dist/docker/manager.d.ts.map +1 -0
- package/dist/docker/manager.js +239 -0
- package/dist/docker/ports.d.ts +6 -0
- package/dist/docker/ports.d.ts.map +1 -0
- package/dist/docker/restart-on-deploy.d.ts +6 -0
- package/dist/docker/restart-on-deploy.d.ts.map +1 -0
- package/dist/docker/types.d.ts +36 -0
- package/dist/docker/types.d.ts.map +1 -0
- package/dist/docker/types.js +1 -0
- package/dist/events-listener.d.ts +19 -0
- package/dist/events-listener.d.ts.map +1 -0
- package/dist/events-listener.js +105 -0
- package/dist/generator.d.ts +44 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/generator.js +1816 -0
- package/dist/generators/di.d.ts +21 -0
- package/dist/generators/di.d.ts.map +1 -0
- package/dist/generators/di.js +100 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/project.d.ts +18 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/protocol.d.ts +58 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +5 -0
- package/dist/uploader.d.ts +63 -0
- package/dist/uploader.d.ts.map +1 -0
- package/dist/uploader.js +255 -0
- package/dist/watcher.d.ts +13 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +38 -0
- package/package.json +58 -0
- package/scripts/postinstall.cjs +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Archlast CLI
|
|
2
|
+
|
|
3
|
+
CLI tool for Archlast development, deployment, and Docker lifecycle management.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Node.js 18+
|
|
8
|
+
- Docker Desktop or Docker Engine
|
|
9
|
+
- Bun 1.3+
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g @archlast/cli
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
On Windows, ensure Bun is on your PATH (the installer adds it, but terminals need a restart):
|
|
18
|
+
|
|
19
|
+
```powershell
|
|
20
|
+
[Environment]::SetEnvironmentVariable(
|
|
21
|
+
"Path",
|
|
22
|
+
"$env:Path;$env:USERPROFILE\\.bun\\bin",
|
|
23
|
+
"User"
|
|
24
|
+
)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
archlast init
|
|
31
|
+
archlast start
|
|
32
|
+
archlast status
|
|
33
|
+
archlast logs --follow
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
To watch and auto-deploy functions during development:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
archlast dev --path ./archlast --server http://localhost:4000
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Commands
|
|
43
|
+
|
|
44
|
+
- `dev` - watch `archlast/src` for changes, regenerate types, and deploy deltas
|
|
45
|
+
- `deploy` - one-time deployment to the server
|
|
46
|
+
- `build` - generate types without deploying
|
|
47
|
+
- `pull` - pull schema/files from the server, or use `--docker` to pull an image
|
|
48
|
+
- `init` - scaffold the minimal Archlast project structure
|
|
49
|
+
- `start` / `stop` / `restart` - manage the Docker container
|
|
50
|
+
- `status` - print container status and health
|
|
51
|
+
- `logs` - stream container logs
|
|
52
|
+
- `upgrade` - pull a new image and restart the container
|
|
53
|
+
- `config` - show resolved Docker config (use `--json` for JSON)
|
|
54
|
+
- `generate crud <collection>` - scaffold CRUD handlers from schema
|
|
55
|
+
- `data` - snapshot/export/import/restore data (requires `ARCHLAST_API_KEY`)
|
|
56
|
+
|
|
57
|
+
## Common options
|
|
58
|
+
|
|
59
|
+
- `--path <path>` points to your Archlast project folder
|
|
60
|
+
- `--server <url>` sets the API base URL (default: `http://localhost:4000`)
|
|
61
|
+
- `--port <port>` overrides the container port for `start` and `upgrade`
|
|
62
|
+
- `--config <path>` points to `archlast.config.js`
|
|
63
|
+
- `--name <name>` sets the project name when running `init`
|
|
64
|
+
|
|
65
|
+
## Configuration
|
|
66
|
+
|
|
67
|
+
The CLI reads configuration in this order:
|
|
68
|
+
1. CLI flags
|
|
69
|
+
2. `archlast.config.js`
|
|
70
|
+
3. `.env` or `.env.local`
|
|
71
|
+
4. defaults
|
|
72
|
+
|
|
73
|
+
Example `archlast.config.js`:
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
export default {
|
|
77
|
+
docker: {
|
|
78
|
+
image: "algochad/archlast-server",
|
|
79
|
+
tag: "latest",
|
|
80
|
+
containerName: "archlast-server",
|
|
81
|
+
restartOnDeploy: true,
|
|
82
|
+
},
|
|
83
|
+
server: { port: 4000 },
|
|
84
|
+
paths: {
|
|
85
|
+
config: ".archlast/config",
|
|
86
|
+
deploy: ".archlast-deploy",
|
|
87
|
+
},
|
|
88
|
+
cors: { origins: ["http://localhost:3000"] },
|
|
89
|
+
env: {
|
|
90
|
+
ARCHLAST_DASHBOARD_PORT: "4001",
|
|
91
|
+
ARCHLAST_STORE_PORT: "7001",
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Environment variables starting with `ARCHLAST_`, `S3_`, `AWS_`, and `STORAGE_`
|
|
97
|
+
are forwarded into the container.
|
|
98
|
+
|
|
99
|
+
If `containerName` or `volumeName` is not set, the CLI generates unique names per
|
|
100
|
+
project to keep data isolated.
|
|
101
|
+
|
|
102
|
+
The CLI stores per-project metadata in `.archlast/project.json` and persists the
|
|
103
|
+
selected port when it has to pick a new one.
|
|
104
|
+
|
|
105
|
+
When `restartOnDeploy` is enabled, `archlast dev` and `archlast deploy` restart the
|
|
106
|
+
Docker container after a successful upload.
|
|
107
|
+
|
|
108
|
+
## Dev and deploy flow
|
|
109
|
+
|
|
110
|
+
The server receives deployments at `POST /_archlast/deploy` with payload:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
{
|
|
114
|
+
functions: Array<{
|
|
115
|
+
name: string;
|
|
116
|
+
type: "query" | "mutation" | "action";
|
|
117
|
+
filePath: string;
|
|
118
|
+
code: string;
|
|
119
|
+
}>;
|
|
120
|
+
schema: { filePath: string; code: string } | null;
|
|
121
|
+
timestamp: number;
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Data management
|
|
126
|
+
|
|
127
|
+
Use the `data` command group to snapshot, export, import, and restore data.
|
|
128
|
+
Set `ARCHLAST_API_KEY` in your environment for authentication.
|
|
129
|
+
|
|
130
|
+
Examples:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
archlast data snapshot --name "pre-migration"
|
|
134
|
+
archlast data export ./backup.zip
|
|
135
|
+
archlast data import ./backup.zip --strategy merge
|
|
136
|
+
archlast data restore snapshot-2026-01-01.zip
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Publishing (maintainers)
|
|
140
|
+
|
|
141
|
+
See `docs/npm-publishing.md` for the release workflow and manual publish steps.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export interface FunctionDefinition {
|
|
2
|
+
name: string;
|
|
3
|
+
type: "query" | "mutation" | "action";
|
|
4
|
+
filePath: string;
|
|
5
|
+
hash: string;
|
|
6
|
+
}
|
|
7
|
+
export interface HttpRouteDefinition {
|
|
8
|
+
name: string;
|
|
9
|
+
type: "http" | "webhook";
|
|
10
|
+
method: string;
|
|
11
|
+
path: string;
|
|
12
|
+
filePath: string;
|
|
13
|
+
hash: string;
|
|
14
|
+
}
|
|
15
|
+
export interface RpcDefinition {
|
|
16
|
+
name: string;
|
|
17
|
+
procedureType: "query" | "mutation";
|
|
18
|
+
filePath: string;
|
|
19
|
+
moduleName: string;
|
|
20
|
+
hash: string;
|
|
21
|
+
}
|
|
22
|
+
export interface InjectableDefinition {
|
|
23
|
+
className: string;
|
|
24
|
+
provide: string;
|
|
25
|
+
implementation: string;
|
|
26
|
+
filePath: string;
|
|
27
|
+
hash: string;
|
|
28
|
+
}
|
|
29
|
+
export interface SchemaDefinition {
|
|
30
|
+
tables: Record<string, any>;
|
|
31
|
+
hash: string;
|
|
32
|
+
/** Path to the schema file (for merged schemas, indicates "schema/*.ts") */
|
|
33
|
+
filePath?: string;
|
|
34
|
+
/** For merged schemas, list of all schema files that were combined */
|
|
35
|
+
files?: string[];
|
|
36
|
+
}
|
|
37
|
+
export interface AnalysisResult {
|
|
38
|
+
functions: FunctionDefinition[];
|
|
39
|
+
httpRoutes: HttpRouteDefinition[];
|
|
40
|
+
rpcRoutes: RpcDefinition[];
|
|
41
|
+
injectables: InjectableDefinition[];
|
|
42
|
+
schema: SchemaDefinition | string | null;
|
|
43
|
+
timestamp: number;
|
|
44
|
+
}
|
|
45
|
+
export declare class CodeAnalyzer {
|
|
46
|
+
private archlastPath;
|
|
47
|
+
constructor(archlastPath: string);
|
|
48
|
+
analyze(): Promise<AnalysisResult>;
|
|
49
|
+
private analyzeFunctions;
|
|
50
|
+
private analyzeHttpRoutes;
|
|
51
|
+
private analyzeRpcRoutes;
|
|
52
|
+
private analyzeInjectables;
|
|
53
|
+
/**
|
|
54
|
+
* Analyze schema definition
|
|
55
|
+
* Supports both single-file (schema.ts) and folder-based (schema/*.ts) schemas
|
|
56
|
+
* Priority: Single file > Folder with index.ts > Folder (scanned and merged)
|
|
57
|
+
*/
|
|
58
|
+
private analyzeSchema;
|
|
59
|
+
/**
|
|
60
|
+
* Parse a single schema file
|
|
61
|
+
*/
|
|
62
|
+
private parseSchemaFile;
|
|
63
|
+
/**
|
|
64
|
+
* Scan schema folder for all .ts files
|
|
65
|
+
*/
|
|
66
|
+
private scanSchemaFolder;
|
|
67
|
+
/**
|
|
68
|
+
* Merge multiple schema files into a single schema definition
|
|
69
|
+
* Later files override earlier ones for conflicting table names
|
|
70
|
+
*/
|
|
71
|
+
private mergeSchemaFiles;
|
|
72
|
+
/**
|
|
73
|
+
* Extract table definitions from schema content
|
|
74
|
+
* Uses regex parsing to find defineTable calls
|
|
75
|
+
*/
|
|
76
|
+
private extractTablesFromSchema;
|
|
77
|
+
private hashString;
|
|
78
|
+
}
|
|
79
|
+
export declare class CodeDiffer {
|
|
80
|
+
diff(prev: AnalysisResult, current: AnalysisResult): {
|
|
81
|
+
added: FunctionDefinition[];
|
|
82
|
+
removed: FunctionDefinition[];
|
|
83
|
+
modified: FunctionDefinition[];
|
|
84
|
+
addedRoutes: HttpRouteDefinition[];
|
|
85
|
+
removedRoutes: HttpRouteDefinition[];
|
|
86
|
+
modifiedRoutes: HttpRouteDefinition[];
|
|
87
|
+
addedRpcRoutes: RpcDefinition[];
|
|
88
|
+
removedRpcRoutes: RpcDefinition[];
|
|
89
|
+
modifiedRpcRoutes: RpcDefinition[];
|
|
90
|
+
addedInjectables: InjectableDefinition[];
|
|
91
|
+
removedInjectables: InjectableDefinition[];
|
|
92
|
+
modifiedInjectables: InjectableDefinition[];
|
|
93
|
+
schemaChanged: boolean;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,OAAO,GAAG,UAAU,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,kBAAkB,EAAE,CAAC;IAChC,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,MAAM,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAAC;IACzC,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,YAAY;IACrB,OAAO,CAAC,YAAY,CAAS;gBAEjB,YAAY,EAAE,MAAM;IAK1B,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC;YAiB1B,gBAAgB;YAuDhB,iBAAiB;YAmDjB,gBAAgB;YAgDhB,kBAAkB;IAkEhC;;;;OAIG;YACW,aAAa;IAgC3B;;OAEG;IACH,OAAO,CAAC,eAAe;IAQvB;;OAEG;YACW,gBAAgB;IAuB9B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAsBxB;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAoB/B,OAAO,CAAC,UAAU;CASrB;AAED,qBAAa,UAAU;IACnB,IAAI,CACA,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,cAAc,GACxB;QACC,KAAK,EAAE,kBAAkB,EAAE,CAAC;QAC5B,OAAO,EAAE,kBAAkB,EAAE,CAAC;QAC9B,QAAQ,EAAE,kBAAkB,EAAE,CAAC;QAC/B,WAAW,EAAE,mBAAmB,EAAE,CAAC;QACnC,aAAa,EAAE,mBAAmB,EAAE,CAAC;QACrC,cAAc,EAAE,mBAAmB,EAAE,CAAC;QACtC,cAAc,EAAE,aAAa,EAAE,CAAC;QAChC,gBAAgB,EAAE,aAAa,EAAE,CAAC;QAClC,iBAAiB,EAAE,aAAa,EAAE,CAAC;QACnC,gBAAgB,EAAE,oBAAoB,EAAE,CAAC;QACzC,kBAAkB,EAAE,oBAAoB,EAAE,CAAC;QAC3C,mBAAmB,EAAE,oBAAoB,EAAE,CAAC;QAC5C,aAAa,EAAE,OAAO,CAAC;KAC1B;CAsHJ"}
|
package/dist/analyzer.js
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { glob } from "glob";
|
|
4
|
+
export class CodeAnalyzer {
|
|
5
|
+
archlastPath;
|
|
6
|
+
constructor(archlastPath) {
|
|
7
|
+
this.archlastPath = path.resolve(archlastPath);
|
|
8
|
+
}
|
|
9
|
+
async analyze() {
|
|
10
|
+
const functions = await this.analyzeFunctions();
|
|
11
|
+
const httpRoutes = await this.analyzeHttpRoutes();
|
|
12
|
+
const rpcRoutes = await this.analyzeRpcRoutes();
|
|
13
|
+
const injectables = await this.analyzeInjectables();
|
|
14
|
+
const schema = await this.analyzeSchema();
|
|
15
|
+
return {
|
|
16
|
+
functions,
|
|
17
|
+
httpRoutes,
|
|
18
|
+
rpcRoutes,
|
|
19
|
+
injectables,
|
|
20
|
+
schema,
|
|
21
|
+
timestamp: Date.now(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async analyzeFunctions() {
|
|
25
|
+
const pattern = path.join(this.archlastPath, "src", "**/*.ts").replace(/\\/g, "/");
|
|
26
|
+
const files = await glob(pattern, {
|
|
27
|
+
ignore: ["**/node_modules/**", "**/_generated/**", "**/schema.ts"],
|
|
28
|
+
});
|
|
29
|
+
const functions = [];
|
|
30
|
+
for (const file of files) {
|
|
31
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
32
|
+
const srcPath = path.join(this.archlastPath, "src");
|
|
33
|
+
const relativePath = path.relative(srcPath, file);
|
|
34
|
+
// Simple regex to detect exported functions
|
|
35
|
+
const queryMatches = content.matchAll(/export\s+(?:const|let)\s+(\w+)\s*=\s*query\s*\(/g);
|
|
36
|
+
const mutationMatches = content.matchAll(/export\s+(?:const|let)\s+(\w+)\s*=\s*mutation\s*\(/g);
|
|
37
|
+
const actionMatches = content.matchAll(/export\s+(?:const|let)\s+(\w+)\s*=\s*action\s*\(/g);
|
|
38
|
+
for (const match of queryMatches) {
|
|
39
|
+
functions.push({
|
|
40
|
+
name: match[1],
|
|
41
|
+
type: "query",
|
|
42
|
+
filePath: relativePath,
|
|
43
|
+
hash: this.hashString(content),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
for (const match of mutationMatches) {
|
|
47
|
+
functions.push({
|
|
48
|
+
name: match[1],
|
|
49
|
+
type: "mutation",
|
|
50
|
+
filePath: relativePath,
|
|
51
|
+
hash: this.hashString(content),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
for (const match of actionMatches) {
|
|
55
|
+
functions.push({
|
|
56
|
+
name: match[1],
|
|
57
|
+
type: "action",
|
|
58
|
+
filePath: relativePath,
|
|
59
|
+
hash: this.hashString(content),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return functions;
|
|
64
|
+
}
|
|
65
|
+
async analyzeHttpRoutes() {
|
|
66
|
+
const pattern = path.join(this.archlastPath, "src", "**/*.ts").replace(/\\/g, "/");
|
|
67
|
+
const files = await glob(pattern, {
|
|
68
|
+
ignore: ["**/node_modules/**", "**/_generated/**", "**/schema.ts"],
|
|
69
|
+
});
|
|
70
|
+
const routes = [];
|
|
71
|
+
for (const file of files) {
|
|
72
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
73
|
+
const srcPath = path.join(this.archlastPath, "src");
|
|
74
|
+
const relativePath = path.relative(srcPath, file);
|
|
75
|
+
// Detect http.get, http.post, etc.
|
|
76
|
+
// Use [\s\S]*? to handle multiline object definitions
|
|
77
|
+
const httpMatches = content.matchAll(/export\s+(?:const|let)\s+(\w+)\s*=\s*http\.(get|post|put|delete|patch)\s*\(\s*\{[\s\S]*?path:\s*["']([^"']+)["']/g);
|
|
78
|
+
for (const match of httpMatches) {
|
|
79
|
+
routes.push({
|
|
80
|
+
name: match[1],
|
|
81
|
+
type: "http",
|
|
82
|
+
method: match[2].toUpperCase(),
|
|
83
|
+
path: match[3],
|
|
84
|
+
filePath: relativePath,
|
|
85
|
+
hash: this.hashString(content),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
// Detect webhook definitions
|
|
89
|
+
const webhookMatches = content.matchAll(/export\s+(?:const|let)\s+(\w+)\s*=\s*webhook\s*\(\s*\{[^}]*path:\s*["']([^"']+)["']/g);
|
|
90
|
+
for (const match of webhookMatches) {
|
|
91
|
+
routes.push({
|
|
92
|
+
name: match[1],
|
|
93
|
+
type: "webhook",
|
|
94
|
+
method: "POST",
|
|
95
|
+
path: match[2],
|
|
96
|
+
filePath: relativePath,
|
|
97
|
+
hash: this.hashString(content),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return routes;
|
|
102
|
+
}
|
|
103
|
+
async analyzeRpcRoutes() {
|
|
104
|
+
const pattern = path.join(this.archlastPath, "src", "**/*.ts").replace(/\\/g, "/");
|
|
105
|
+
const files = await glob(pattern, {
|
|
106
|
+
ignore: ["**/node_modules/**", "**/_generated/**", "**/schema.ts"],
|
|
107
|
+
});
|
|
108
|
+
const routes = [];
|
|
109
|
+
for (const file of files) {
|
|
110
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
111
|
+
const srcPath = path.join(this.archlastPath, "src");
|
|
112
|
+
const relativePath = path.relative(srcPath, file);
|
|
113
|
+
const moduleName = relativePath.replace(/\.ts$/, "");
|
|
114
|
+
// Detect rpc.query definitions
|
|
115
|
+
const queryMatches = content.matchAll(/export\s+(?:const|let)\s+(\w+)\s*=\s*rpc\.query\s*\(/g);
|
|
116
|
+
for (const match of queryMatches) {
|
|
117
|
+
routes.push({
|
|
118
|
+
name: match[1],
|
|
119
|
+
procedureType: "query",
|
|
120
|
+
filePath: relativePath,
|
|
121
|
+
moduleName,
|
|
122
|
+
hash: this.hashString(content),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// Detect rpc.mutation definitions
|
|
126
|
+
const mutationMatches = content.matchAll(/export\s+(?:const|let)\s+(\w+)\s*=\s*rpc\.mutation\s*\(/g);
|
|
127
|
+
for (const match of mutationMatches) {
|
|
128
|
+
routes.push({
|
|
129
|
+
name: match[1],
|
|
130
|
+
procedureType: "mutation",
|
|
131
|
+
filePath: relativePath,
|
|
132
|
+
moduleName,
|
|
133
|
+
hash: this.hashString(content),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return routes;
|
|
138
|
+
}
|
|
139
|
+
async analyzeInjectables() {
|
|
140
|
+
const pattern = path.join(this.archlastPath, "src", "**/*.ts").replace(/\\/g, "/");
|
|
141
|
+
const files = await glob(pattern, {
|
|
142
|
+
ignore: ["**/node_modules/**", "**/_generated/**", "**/schema.ts"],
|
|
143
|
+
});
|
|
144
|
+
const injectables = [];
|
|
145
|
+
for (const file of files) {
|
|
146
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
147
|
+
const srcPath = path.join(this.archlastPath, "src");
|
|
148
|
+
const relativePath = path.relative(srcPath, file);
|
|
149
|
+
// Detect @Injectable() decorators
|
|
150
|
+
// Pattern: @Injectable({ provide: 'Token', useFactory: 'Implementation' })
|
|
151
|
+
// or @Injectable() for simple cases
|
|
152
|
+
const decoratorMatches = content.matchAll(/@Injectable\s*\(\s*(?:\{([^}]*)\})?\s*\)\s*(?:export\s+)?class\s+(\w+)/g);
|
|
153
|
+
for (const match of decoratorMatches) {
|
|
154
|
+
const optionsStr = match[1] || "";
|
|
155
|
+
const className = match[2];
|
|
156
|
+
// Parse options
|
|
157
|
+
let provide = className;
|
|
158
|
+
let implementation = "default";
|
|
159
|
+
const provideMatch = optionsStr.match(/provide:\s*["']([^"']+)["']/);
|
|
160
|
+
if (provideMatch) {
|
|
161
|
+
provide = provideMatch[1];
|
|
162
|
+
}
|
|
163
|
+
const factoryMatch = optionsStr.match(/useFactory:\s*["']([^"']+)["']/);
|
|
164
|
+
if (factoryMatch) {
|
|
165
|
+
implementation = factoryMatch[1];
|
|
166
|
+
}
|
|
167
|
+
injectables.push({
|
|
168
|
+
className,
|
|
169
|
+
provide,
|
|
170
|
+
implementation,
|
|
171
|
+
filePath: relativePath,
|
|
172
|
+
hash: this.hashString(content),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
// Also detect @Factory decorator
|
|
176
|
+
const factoryMatches = content.matchAll(/@Factory\s*\(\s*["']([^"']+)["']\s*,\s*["']([^"']+)["']\s*\)\s*(?:export\s+)?class\s+(\w+)/g);
|
|
177
|
+
for (const match of factoryMatches) {
|
|
178
|
+
injectables.push({
|
|
179
|
+
className: match[3],
|
|
180
|
+
provide: match[1],
|
|
181
|
+
implementation: match[2],
|
|
182
|
+
filePath: relativePath,
|
|
183
|
+
hash: this.hashString(content),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return injectables;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Analyze schema definition
|
|
191
|
+
* Supports both single-file (schema.ts) and folder-based (schema/*.ts) schemas
|
|
192
|
+
* Priority: Single file > Folder with index.ts > Folder (scanned and merged)
|
|
193
|
+
*/
|
|
194
|
+
async analyzeSchema() {
|
|
195
|
+
const srcPath = path.join(this.archlastPath, "src");
|
|
196
|
+
const singleSchemaPath = path.join(srcPath, "schema.ts");
|
|
197
|
+
const schemaFolderPath = path.join(srcPath, "schema");
|
|
198
|
+
const schemaIndexPath = path.join(schemaFolderPath, "index.ts");
|
|
199
|
+
// Check for single schema file first (highest priority)
|
|
200
|
+
if (fs.existsSync(singleSchemaPath)) {
|
|
201
|
+
const content = fs.readFileSync(singleSchemaPath, "utf-8");
|
|
202
|
+
return this.parseSchemaFile(singleSchemaPath, content);
|
|
203
|
+
}
|
|
204
|
+
// Check for schema folder
|
|
205
|
+
if (!fs.existsSync(schemaFolderPath)) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
// Check for schema/index.ts
|
|
209
|
+
if (fs.existsSync(schemaIndexPath)) {
|
|
210
|
+
const content = fs.readFileSync(schemaIndexPath, "utf-8");
|
|
211
|
+
return this.parseSchemaFile(schemaIndexPath, content);
|
|
212
|
+
}
|
|
213
|
+
// Scan and merge all schema files in folder
|
|
214
|
+
const schemaFiles = await this.scanSchemaFolder(schemaFolderPath);
|
|
215
|
+
if (schemaFiles.length === 0) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
return this.mergeSchemaFiles(schemaFiles);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Parse a single schema file
|
|
222
|
+
*/
|
|
223
|
+
parseSchemaFile(filePath, content) {
|
|
224
|
+
return {
|
|
225
|
+
tables: this.extractTablesFromSchema(content),
|
|
226
|
+
filePath: path.relative(this.archlastPath, filePath),
|
|
227
|
+
hash: this.hashString(content),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Scan schema folder for all .ts files
|
|
232
|
+
*/
|
|
233
|
+
async scanSchemaFolder(folderPath) {
|
|
234
|
+
const pattern = path.join(folderPath, "*.ts").replace(/\\/g, "/");
|
|
235
|
+
const files = await glob(pattern, {
|
|
236
|
+
ignore: ["**/node_modules/**", "**/_generated/**"],
|
|
237
|
+
});
|
|
238
|
+
const schemaFiles = [];
|
|
239
|
+
for (const file of files) {
|
|
240
|
+
// Skip index.ts as it's handled separately
|
|
241
|
+
if (path.basename(file) === "index.ts") {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
245
|
+
schemaFiles.push({
|
|
246
|
+
filePath: file,
|
|
247
|
+
content,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
return schemaFiles;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Merge multiple schema files into a single schema definition
|
|
254
|
+
* Later files override earlier ones for conflicting table names
|
|
255
|
+
*/
|
|
256
|
+
mergeSchemaFiles(schemaFiles) {
|
|
257
|
+
const tables = {};
|
|
258
|
+
const hashes = [];
|
|
259
|
+
for (const { filePath, content } of schemaFiles) {
|
|
260
|
+
const fileTables = this.extractTablesFromSchema(content);
|
|
261
|
+
// Merge tables (later files override)
|
|
262
|
+
Object.assign(tables, fileTables);
|
|
263
|
+
hashes.push(this.hashString(content));
|
|
264
|
+
}
|
|
265
|
+
// Combined hash from all files
|
|
266
|
+
const combinedHash = this.hashString(hashes.join("|"));
|
|
267
|
+
return {
|
|
268
|
+
tables,
|
|
269
|
+
filePath: "schema/*.ts", // Indicator that this is a merged schema
|
|
270
|
+
hash: combinedHash,
|
|
271
|
+
files: schemaFiles.map(f => path.relative(this.archlastPath, f.filePath)),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Extract table definitions from schema content
|
|
276
|
+
* Uses regex parsing to find defineTable calls
|
|
277
|
+
*/
|
|
278
|
+
extractTablesFromSchema(content) {
|
|
279
|
+
const tables = {};
|
|
280
|
+
// Pattern to match defineTable calls
|
|
281
|
+
// export const <tableName> = defineTable({ ... }, options?)
|
|
282
|
+
const tableMatches = content.matchAll(/export\s+(?:const|let)\s+(\w+)\s*=\s*defineTable\s*\(\s*\{([^}]+)\}/g);
|
|
283
|
+
for (const match of tableMatches) {
|
|
284
|
+
const tableName = match[1];
|
|
285
|
+
tables[tableName] = {
|
|
286
|
+
name: tableName,
|
|
287
|
+
fields: match[2] || "",
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
return tables;
|
|
291
|
+
}
|
|
292
|
+
hashString(str) {
|
|
293
|
+
let hash = 0;
|
|
294
|
+
for (let i = 0; i < str.length; i++) {
|
|
295
|
+
const char = str.charCodeAt(i);
|
|
296
|
+
hash = (hash << 5) - hash + char;
|
|
297
|
+
hash = hash & hash;
|
|
298
|
+
}
|
|
299
|
+
return hash.toString(36);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
export class CodeDiffer {
|
|
303
|
+
diff(prev, current) {
|
|
304
|
+
const prevFuncMap = new Map(prev.functions.map((f) => [f.name, f]));
|
|
305
|
+
const currFuncMap = new Map(current.functions.map((f) => [f.name, f]));
|
|
306
|
+
const added = [];
|
|
307
|
+
const removed = [];
|
|
308
|
+
const modified = [];
|
|
309
|
+
// Find added and modified functions
|
|
310
|
+
for (const [name, func] of currFuncMap) {
|
|
311
|
+
const prevFunc = prevFuncMap.get(name);
|
|
312
|
+
if (!prevFunc) {
|
|
313
|
+
added.push(func);
|
|
314
|
+
}
|
|
315
|
+
else if (prevFunc.hash !== func.hash) {
|
|
316
|
+
modified.push(func);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
// Find removed functions
|
|
320
|
+
for (const [name, func] of prevFuncMap) {
|
|
321
|
+
if (!currFuncMap.has(name)) {
|
|
322
|
+
removed.push(func);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// Diff HTTP routes
|
|
326
|
+
const prevRouteMap = new Map((prev.httpRoutes || []).map((r) => [r.name, r]));
|
|
327
|
+
const currRouteMap = new Map((current.httpRoutes || []).map((r) => [r.name, r]));
|
|
328
|
+
const addedRoutes = [];
|
|
329
|
+
const removedRoutes = [];
|
|
330
|
+
const modifiedRoutes = [];
|
|
331
|
+
for (const [name, route] of currRouteMap) {
|
|
332
|
+
const prevRoute = prevRouteMap.get(name);
|
|
333
|
+
if (!prevRoute) {
|
|
334
|
+
addedRoutes.push(route);
|
|
335
|
+
}
|
|
336
|
+
else if (prevRoute.hash !== route.hash) {
|
|
337
|
+
modifiedRoutes.push(route);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
for (const [name, route] of prevRouteMap) {
|
|
341
|
+
if (!currRouteMap.has(name)) {
|
|
342
|
+
removedRoutes.push(route);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// Diff RPC routes
|
|
346
|
+
const prevRpcMap = new Map((prev.rpcRoutes || []).map((r) => [`${r.moduleName}.${r.name}`, r]));
|
|
347
|
+
const currRpcMap = new Map((current.rpcRoutes || []).map((r) => [`${r.moduleName}.${r.name}`, r]));
|
|
348
|
+
const addedRpcRoutes = [];
|
|
349
|
+
const removedRpcRoutes = [];
|
|
350
|
+
const modifiedRpcRoutes = [];
|
|
351
|
+
for (const [fullName, route] of currRpcMap) {
|
|
352
|
+
const prevRoute = prevRpcMap.get(fullName);
|
|
353
|
+
if (!prevRoute) {
|
|
354
|
+
addedRpcRoutes.push(route);
|
|
355
|
+
}
|
|
356
|
+
else if (prevRoute.hash !== route.hash) {
|
|
357
|
+
modifiedRpcRoutes.push(route);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
for (const [fullName, route] of prevRpcMap) {
|
|
361
|
+
if (!currRpcMap.has(fullName)) {
|
|
362
|
+
removedRpcRoutes.push(route);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// Diff injectables
|
|
366
|
+
const prevInjectableMap = new Map((prev.injectables || []).map((i) => [`${i.provide}:${i.implementation}`, i]));
|
|
367
|
+
const currInjectableMap = new Map((current.injectables || []).map((i) => [`${i.provide}:${i.implementation}`, i]));
|
|
368
|
+
const addedInjectables = [];
|
|
369
|
+
const removedInjectables = [];
|
|
370
|
+
const modifiedInjectables = [];
|
|
371
|
+
for (const [key, injectable] of currInjectableMap) {
|
|
372
|
+
const prevInjectable = prevInjectableMap.get(key);
|
|
373
|
+
if (!prevInjectable) {
|
|
374
|
+
addedInjectables.push(injectable);
|
|
375
|
+
}
|
|
376
|
+
else if (prevInjectable.hash !== injectable.hash) {
|
|
377
|
+
modifiedInjectables.push(injectable);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
for (const [key, injectable] of prevInjectableMap) {
|
|
381
|
+
if (!currInjectableMap.has(key)) {
|
|
382
|
+
removedInjectables.push(injectable);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const prevHash = typeof prev.schema === "string" ? prev.schema : prev.schema?.hash;
|
|
386
|
+
const currHash = typeof current.schema === "string" ? current.schema : current.schema?.hash;
|
|
387
|
+
const schemaChanged = prevHash !== currHash;
|
|
388
|
+
return {
|
|
389
|
+
added,
|
|
390
|
+
removed,
|
|
391
|
+
modified,
|
|
392
|
+
addedRoutes,
|
|
393
|
+
removedRoutes,
|
|
394
|
+
modifiedRoutes,
|
|
395
|
+
addedRpcRoutes,
|
|
396
|
+
removedRpcRoutes,
|
|
397
|
+
modifiedRpcRoutes,
|
|
398
|
+
addedInjectables,
|
|
399
|
+
removedInjectables,
|
|
400
|
+
modifiedInjectables,
|
|
401
|
+
schemaChanged,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
}
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function validateApiKey(apiKey: string): {
|
|
2
|
+
valid: boolean;
|
|
3
|
+
error?: string;
|
|
4
|
+
};
|
|
5
|
+
export declare class AdminTokenNotFoundError extends Error {
|
|
6
|
+
constructor();
|
|
7
|
+
}
|
|
8
|
+
export declare function isBetterAuthApiKey(token: string): boolean;
|
|
9
|
+
export declare function extractFromEnv(content: string, varName: string): string | null;
|
|
10
|
+
export declare function readAdminToken(archlastPath?: string): string;
|
|
11
|
+
export declare function getAuthHeaders(archlastPath?: string): Record<string, string>;
|
|
12
|
+
export declare function hashString(str: string): string;
|
|
13
|
+
export declare function hashFile(filePath: string): string;
|
|
14
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAMA,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAcjF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;;CAUjD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAY9E;AAED,wBAAgB,cAAc,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAyD5D;AAED,wBAAgB,cAAc,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAK5E;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQ9C;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOjD"}
|