@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.
Files changed (92) hide show
  1. package/README.md +141 -0
  2. package/dist/analyzer.d.ts +96 -0
  3. package/dist/analyzer.d.ts.map +1 -0
  4. package/dist/analyzer.js +404 -0
  5. package/dist/auth.d.ts +14 -0
  6. package/dist/auth.d.ts.map +1 -0
  7. package/dist/auth.js +106 -0
  8. package/dist/cli.d.ts +3 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +322875 -0
  11. package/dist/commands/build.d.ts +6 -0
  12. package/dist/commands/build.d.ts.map +1 -0
  13. package/dist/commands/build.js +36 -0
  14. package/dist/commands/config.d.ts +8 -0
  15. package/dist/commands/config.d.ts.map +1 -0
  16. package/dist/commands/config.js +23 -0
  17. package/dist/commands/data.d.ts +6 -0
  18. package/dist/commands/data.d.ts.map +1 -0
  19. package/dist/commands/data.js +300 -0
  20. package/dist/commands/deploy.d.ts +9 -0
  21. package/dist/commands/deploy.d.ts.map +1 -0
  22. package/dist/commands/deploy.js +59 -0
  23. package/dist/commands/dev.d.ts +10 -0
  24. package/dist/commands/dev.d.ts.map +1 -0
  25. package/dist/commands/dev.js +132 -0
  26. package/dist/commands/generate.d.ts +6 -0
  27. package/dist/commands/generate.d.ts.map +1 -0
  28. package/dist/commands/generate.js +100 -0
  29. package/dist/commands/init.d.ts +7 -0
  30. package/dist/commands/init.d.ts.map +1 -0
  31. package/dist/commands/logs.d.ts +10 -0
  32. package/dist/commands/logs.d.ts.map +1 -0
  33. package/dist/commands/logs.js +38 -0
  34. package/dist/commands/pull.d.ts +16 -0
  35. package/dist/commands/pull.d.ts.map +1 -0
  36. package/dist/commands/pull.js +415 -0
  37. package/dist/commands/restart.d.ts +11 -0
  38. package/dist/commands/restart.d.ts.map +1 -0
  39. package/dist/commands/restart.js +63 -0
  40. package/dist/commands/start.d.ts +11 -0
  41. package/dist/commands/start.d.ts.map +1 -0
  42. package/dist/commands/start.js +74 -0
  43. package/dist/commands/status.d.ts +8 -0
  44. package/dist/commands/status.d.ts.map +1 -0
  45. package/dist/commands/status.js +69 -0
  46. package/dist/commands/stop.d.ts +8 -0
  47. package/dist/commands/stop.d.ts.map +1 -0
  48. package/dist/commands/stop.js +23 -0
  49. package/dist/commands/upgrade.d.ts +12 -0
  50. package/dist/commands/upgrade.d.ts.map +1 -0
  51. package/dist/commands/upgrade.js +77 -0
  52. package/dist/docker/compose.d.ts +3 -0
  53. package/dist/docker/compose.d.ts.map +1 -0
  54. package/dist/docker/compose.js +47 -0
  55. package/dist/docker/config.d.ts +12 -0
  56. package/dist/docker/config.d.ts.map +1 -0
  57. package/dist/docker/config.js +183 -0
  58. package/dist/docker/manager.d.ts +19 -0
  59. package/dist/docker/manager.d.ts.map +1 -0
  60. package/dist/docker/manager.js +239 -0
  61. package/dist/docker/ports.d.ts +6 -0
  62. package/dist/docker/ports.d.ts.map +1 -0
  63. package/dist/docker/restart-on-deploy.d.ts +6 -0
  64. package/dist/docker/restart-on-deploy.d.ts.map +1 -0
  65. package/dist/docker/types.d.ts +36 -0
  66. package/dist/docker/types.d.ts.map +1 -0
  67. package/dist/docker/types.js +1 -0
  68. package/dist/events-listener.d.ts +19 -0
  69. package/dist/events-listener.d.ts.map +1 -0
  70. package/dist/events-listener.js +105 -0
  71. package/dist/generator.d.ts +44 -0
  72. package/dist/generator.d.ts.map +1 -0
  73. package/dist/generator.js +1816 -0
  74. package/dist/generators/di.d.ts +21 -0
  75. package/dist/generators/di.d.ts.map +1 -0
  76. package/dist/generators/di.js +100 -0
  77. package/dist/index.d.ts +7 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +4 -0
  80. package/dist/project.d.ts +18 -0
  81. package/dist/project.d.ts.map +1 -0
  82. package/dist/protocol.d.ts +58 -0
  83. package/dist/protocol.d.ts.map +1 -0
  84. package/dist/protocol.js +5 -0
  85. package/dist/uploader.d.ts +63 -0
  86. package/dist/uploader.d.ts.map +1 -0
  87. package/dist/uploader.js +255 -0
  88. package/dist/watcher.d.ts +13 -0
  89. package/dist/watcher.d.ts.map +1 -0
  90. package/dist/watcher.js +38 -0
  91. package/package.json +58 -0
  92. 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"}
@@ -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"}