@geekmidas/cli 1.9.1 → 1.10.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/CHANGELOG.md +24 -0
- package/README.md +42 -6
- package/dist/{config-6JHOwLCx.cjs → config-D3ORuiUs.cjs} +2 -2
- package/dist/{config-6JHOwLCx.cjs.map → config-D3ORuiUs.cjs.map} +1 -1
- package/dist/{config-DxASSNjr.mjs → config-jsRYHOHU.mjs} +2 -2
- package/dist/{config-DxASSNjr.mjs.map → config-jsRYHOHU.mjs.map} +1 -1
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +2 -2
- package/dist/config.d.mts +2 -2
- package/dist/config.mjs +2 -2
- package/dist/{index-Bt2kX0-R.d.mts → index-3n-giNaw.d.mts} +18 -6
- package/dist/index-3n-giNaw.d.mts.map +1 -0
- package/dist/{index-Cyk2rTyj.d.cts → index-CiEOtKEX.d.cts} +18 -6
- package/dist/index-CiEOtKEX.d.cts.map +1 -0
- package/dist/index.cjs +189 -165
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +186 -162
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-CnvwSRDU.cjs → openapi-BYxAWwok.cjs} +178 -32
- package/dist/openapi-BYxAWwok.cjs.map +1 -0
- package/dist/{openapi-BYlyAbH3.mjs → openapi-DenF-okj.mjs} +148 -32
- package/dist/openapi-DenF-okj.mjs.map +1 -0
- package/dist/openapi.cjs +3 -3
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.d.mts.map +1 -1
- package/dist/openapi.mjs +3 -3
- package/dist/{types-l53qUmGt.d.cts → types-C7QJJl9f.d.cts} +6 -2
- package/dist/types-C7QJJl9f.d.cts.map +1 -0
- package/dist/{types-wXMIMOyK.d.mts → types-Iqsq_FIG.d.mts} +6 -2
- package/dist/types-Iqsq_FIG.d.mts.map +1 -0
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-D2ocAlpl.cjs → workspace-4SP3Gx4Y.cjs} +11 -3
- package/dist/{workspace-D2ocAlpl.cjs.map → workspace-4SP3Gx4Y.cjs.map} +1 -1
- package/dist/{workspace-9IQIjwkQ.mjs → workspace-D4z4A4cq.mjs} +11 -3
- package/dist/{workspace-9IQIjwkQ.mjs.map → workspace-D4z4A4cq.mjs.map} +1 -1
- package/package.json +4 -4
- package/src/build/__tests__/manifests.spec.ts +171 -0
- package/src/build/__tests__/partitions.spec.ts +110 -0
- package/src/build/index.ts +58 -15
- package/src/build/manifests.ts +153 -32
- package/src/build/partitions.ts +58 -0
- package/src/deploy/sniffer.ts +6 -1
- package/src/generators/Generator.ts +27 -7
- package/src/generators/OpenApiTsGenerator.ts +4 -4
- package/src/init/versions.ts +7 -7
- package/src/openapi.ts +2 -1
- package/src/types.ts +17 -1
- package/src/workspace/client-generator.ts +6 -3
- package/src/workspace/schema.ts +13 -3
- package/dist/index-Bt2kX0-R.d.mts.map +0 -1
- package/dist/index-Cyk2rTyj.d.cts.map +0 -1
- package/dist/openapi-BYlyAbH3.mjs.map +0 -1
- package/dist/openapi-CnvwSRDU.cjs.map +0 -1
- package/dist/types-l53qUmGt.d.cts.map +0 -1
- package/dist/types-wXMIMOyK.d.mts.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
const require_chunk = require('./chunk-CUT6urMc.cjs');
|
|
3
|
-
const require_workspace = require('./workspace-
|
|
4
|
-
const require_config = require('./config-
|
|
3
|
+
const require_workspace = require('./workspace-4SP3Gx4Y.cjs');
|
|
4
|
+
const require_config = require('./config-D3ORuiUs.cjs');
|
|
5
5
|
const require_credentials = require('./credentials-C8DWtnMY.cjs');
|
|
6
|
-
const require_openapi = require('./openapi-
|
|
6
|
+
const require_openapi = require('./openapi-BYxAWwok.cjs');
|
|
7
7
|
const require_storage = require('./storage-CoCNe0Pt.cjs');
|
|
8
8
|
const require_dokploy_api = require('./dokploy-api-DLgvEQlr.cjs');
|
|
9
9
|
const require_encryption = require('./encryption-BE0UOb8j.cjs');
|
|
@@ -35,7 +35,7 @@ const prompts = require_chunk.__toESM(require("prompts"));
|
|
|
35
35
|
|
|
36
36
|
//#region package.json
|
|
37
37
|
var name = "@geekmidas/cli";
|
|
38
|
-
var version = "1.
|
|
38
|
+
var version = "1.10.0";
|
|
39
39
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
40
40
|
var private$1 = false;
|
|
41
41
|
var type = "module";
|
|
@@ -133,7 +133,7 @@ var package_default = {
|
|
|
133
133
|
|
|
134
134
|
//#endregion
|
|
135
135
|
//#region src/auth/index.ts
|
|
136
|
-
const logger$
|
|
136
|
+
const logger$12 = console;
|
|
137
137
|
/**
|
|
138
138
|
* Validate Dokploy token by making a test API call
|
|
139
139
|
*/
|
|
@@ -201,36 +201,36 @@ async function prompt$1(message, hidden = false) {
|
|
|
201
201
|
async function loginCommand(options) {
|
|
202
202
|
const { service, token: providedToken, endpoint: providedEndpoint } = options;
|
|
203
203
|
if (service === "dokploy") {
|
|
204
|
-
logger$
|
|
204
|
+
logger$12.log("\n🔐 Logging in to Dokploy...\n");
|
|
205
205
|
let endpoint = providedEndpoint;
|
|
206
206
|
if (!endpoint) endpoint = await prompt$1("Dokploy URL (e.g., https://dokploy.example.com): ");
|
|
207
207
|
endpoint = endpoint.replace(/\/$/, "");
|
|
208
208
|
try {
|
|
209
209
|
new URL(endpoint);
|
|
210
210
|
} catch {
|
|
211
|
-
logger$
|
|
211
|
+
logger$12.error("Invalid URL format");
|
|
212
212
|
process.exit(1);
|
|
213
213
|
}
|
|
214
214
|
let token = providedToken;
|
|
215
215
|
if (!token) {
|
|
216
|
-
logger$
|
|
216
|
+
logger$12.log(`\nGenerate a token at: ${endpoint}/settings/profile\n`);
|
|
217
217
|
token = await prompt$1("API Token: ", true);
|
|
218
218
|
}
|
|
219
219
|
if (!token) {
|
|
220
|
-
logger$
|
|
220
|
+
logger$12.error("Token is required");
|
|
221
221
|
process.exit(1);
|
|
222
222
|
}
|
|
223
|
-
logger$
|
|
223
|
+
logger$12.log("\nValidating credentials...");
|
|
224
224
|
const isValid = await validateDokployToken(endpoint, token);
|
|
225
225
|
if (!isValid) {
|
|
226
|
-
logger$
|
|
226
|
+
logger$12.error("\n✗ Invalid credentials. Please check your token and try again.");
|
|
227
227
|
process.exit(1);
|
|
228
228
|
}
|
|
229
229
|
await require_credentials.storeDokployCredentials(token, endpoint);
|
|
230
|
-
logger$
|
|
231
|
-
logger$
|
|
232
|
-
logger$
|
|
233
|
-
logger$
|
|
230
|
+
logger$12.log("\n✓ Successfully logged in to Dokploy!");
|
|
231
|
+
logger$12.log(` Endpoint: ${endpoint}`);
|
|
232
|
+
logger$12.log(` Credentials stored in: ${require_credentials.getCredentialsPath()}`);
|
|
233
|
+
logger$12.log("\nYou can now use deploy commands without setting DOKPLOY_API_TOKEN.");
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
/**
|
|
@@ -240,28 +240,28 @@ async function logoutCommand(options) {
|
|
|
240
240
|
const { service = "dokploy" } = options;
|
|
241
241
|
if (service === "all") {
|
|
242
242
|
const dokployRemoved = await require_credentials.removeDokployCredentials();
|
|
243
|
-
if (dokployRemoved) logger$
|
|
244
|
-
else logger$
|
|
243
|
+
if (dokployRemoved) logger$12.log("\n✓ Logged out from all services");
|
|
244
|
+
else logger$12.log("\nNo stored credentials found");
|
|
245
245
|
return;
|
|
246
246
|
}
|
|
247
247
|
if (service === "dokploy") {
|
|
248
248
|
const removed = await require_credentials.removeDokployCredentials();
|
|
249
|
-
if (removed) logger$
|
|
250
|
-
else logger$
|
|
249
|
+
if (removed) logger$12.log("\n✓ Logged out from Dokploy");
|
|
250
|
+
else logger$12.log("\nNo Dokploy credentials found");
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
/**
|
|
254
254
|
* Show current login status
|
|
255
255
|
*/
|
|
256
256
|
async function whoamiCommand() {
|
|
257
|
-
logger$
|
|
257
|
+
logger$12.log("\n📋 Current credentials:\n");
|
|
258
258
|
const dokploy = await require_credentials.getDokployCredentials();
|
|
259
259
|
if (dokploy) {
|
|
260
|
-
logger$
|
|
261
|
-
logger$
|
|
262
|
-
logger$
|
|
263
|
-
} else logger$
|
|
264
|
-
logger$
|
|
260
|
+
logger$12.log(" Dokploy:");
|
|
261
|
+
logger$12.log(` Endpoint: ${dokploy.endpoint}`);
|
|
262
|
+
logger$12.log(` Token: ${maskToken(dokploy.token)}`);
|
|
263
|
+
} else logger$12.log(" Dokploy: Not logged in");
|
|
264
|
+
logger$12.log(`\n Credentials file: ${require_credentials.getCredentialsPath()}`);
|
|
265
265
|
}
|
|
266
266
|
/**
|
|
267
267
|
* Mask a token for display
|
|
@@ -347,7 +347,7 @@ function isEnabled(config) {
|
|
|
347
347
|
var CronGenerator = class extends require_openapi.ConstructGenerator {
|
|
348
348
|
async build(context, constructs, outputDir, options) {
|
|
349
349
|
const provider = options?.provider || "aws-lambda";
|
|
350
|
-
const logger$
|
|
350
|
+
const logger$13 = console;
|
|
351
351
|
const cronInfos = [];
|
|
352
352
|
if (constructs.length === 0 || provider !== "aws-lambda") return cronInfos;
|
|
353
353
|
const cronsDir = (0, node_path.join)(outputDir, "crons");
|
|
@@ -362,7 +362,7 @@ var CronGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
362
362
|
memorySize: construct.memorySize,
|
|
363
363
|
environment: await construct.getEnvironment()
|
|
364
364
|
});
|
|
365
|
-
logger$
|
|
365
|
+
logger$13.log(`Generated cron handler: ${key}`);
|
|
366
366
|
}
|
|
367
367
|
return cronInfos;
|
|
368
368
|
}
|
|
@@ -398,7 +398,7 @@ var FunctionGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
398
398
|
}
|
|
399
399
|
async build(context, constructs, outputDir, options) {
|
|
400
400
|
const provider = options?.provider || "aws-lambda";
|
|
401
|
-
const logger$
|
|
401
|
+
const logger$13 = console;
|
|
402
402
|
const functionInfos = [];
|
|
403
403
|
if (constructs.length === 0 || provider !== "aws-lambda") return functionInfos;
|
|
404
404
|
const functionsDir = (0, node_path.join)(outputDir, "functions");
|
|
@@ -412,7 +412,7 @@ var FunctionGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
412
412
|
memorySize: construct.memorySize,
|
|
413
413
|
environment: await construct.getEnvironment()
|
|
414
414
|
});
|
|
415
|
-
logger$
|
|
415
|
+
logger$13.log(`Generated function handler: ${key}`);
|
|
416
416
|
}
|
|
417
417
|
return functionInfos;
|
|
418
418
|
}
|
|
@@ -445,11 +445,11 @@ var SubscriberGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
445
445
|
}
|
|
446
446
|
async build(context, constructs, outputDir, options) {
|
|
447
447
|
const provider = options?.provider || "aws-lambda";
|
|
448
|
-
const logger$
|
|
448
|
+
const logger$13 = console;
|
|
449
449
|
const subscriberInfos = [];
|
|
450
450
|
if (provider === "server") {
|
|
451
451
|
await this.generateServerSubscribersFile(outputDir, constructs);
|
|
452
|
-
logger$
|
|
452
|
+
logger$13.log(`Generated server subscribers file with ${constructs.length} subscribers (polling mode)`);
|
|
453
453
|
return subscriberInfos;
|
|
454
454
|
}
|
|
455
455
|
if (constructs.length === 0) return subscriberInfos;
|
|
@@ -466,7 +466,7 @@ var SubscriberGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
466
466
|
memorySize: construct.memorySize,
|
|
467
467
|
environment: await construct.getEnvironment()
|
|
468
468
|
});
|
|
469
|
-
logger$
|
|
469
|
+
logger$13.log(`Generated subscriber handler: ${key}`);
|
|
470
470
|
}
|
|
471
471
|
return subscriberInfos;
|
|
472
472
|
}
|
|
@@ -631,98 +631,6 @@ export async function setupSubscribers(
|
|
|
631
631
|
}
|
|
632
632
|
};
|
|
633
633
|
|
|
634
|
-
//#endregion
|
|
635
|
-
//#region src/workspace/client-generator.ts
|
|
636
|
-
const logger$12 = console;
|
|
637
|
-
/**
|
|
638
|
-
* Get frontend apps that depend on a backend app.
|
|
639
|
-
*/
|
|
640
|
-
function getDependentFrontends(workspace, backendAppName) {
|
|
641
|
-
const dependentApps = [];
|
|
642
|
-
for (const [appName, app] of Object.entries(workspace.apps)) if (app.type === "frontend" && app.dependencies.includes(backendAppName)) dependentApps.push(appName);
|
|
643
|
-
return dependentApps;
|
|
644
|
-
}
|
|
645
|
-
/**
|
|
646
|
-
* Get the path to a backend's OpenAPI spec file.
|
|
647
|
-
*/
|
|
648
|
-
function getBackendOpenApiPath(workspace, backendAppName) {
|
|
649
|
-
const app = workspace.apps[backendAppName];
|
|
650
|
-
if (!app || app.type !== "backend") return null;
|
|
651
|
-
return (0, node_path.join)(workspace.root, app.path, ".gkm", "openapi.ts");
|
|
652
|
-
}
|
|
653
|
-
/**
|
|
654
|
-
* Count endpoints in an OpenAPI spec content.
|
|
655
|
-
*/
|
|
656
|
-
function countEndpoints(content) {
|
|
657
|
-
const endpointMatches = content.match(/'(GET|POST|PUT|PATCH|DELETE)\s+\/[^']+'/g);
|
|
658
|
-
return endpointMatches?.length ?? 0;
|
|
659
|
-
}
|
|
660
|
-
/**
|
|
661
|
-
* Copy the OpenAPI client from a backend to all dependent frontend apps.
|
|
662
|
-
* Called when the backend's .gkm/openapi.ts file changes.
|
|
663
|
-
*/
|
|
664
|
-
async function copyClientToFrontends(workspace, backendAppName, options = {}) {
|
|
665
|
-
const log = options.silent ? () => {} : logger$12.log.bind(logger$12);
|
|
666
|
-
const results = [];
|
|
667
|
-
const backendApp = workspace.apps[backendAppName];
|
|
668
|
-
if (!backendApp || backendApp.type !== "backend") return results;
|
|
669
|
-
const openApiPath = (0, node_path.join)(workspace.root, backendApp.path, ".gkm", "openapi.ts");
|
|
670
|
-
if (!(0, node_fs.existsSync)(openApiPath)) return results;
|
|
671
|
-
const content = await (0, node_fs_promises.readFile)(openApiPath, "utf-8");
|
|
672
|
-
const endpointCount = countEndpoints(content);
|
|
673
|
-
const dependentFrontends = getDependentFrontends(workspace, backendAppName);
|
|
674
|
-
for (const frontendAppName of dependentFrontends) {
|
|
675
|
-
const frontendApp = workspace.apps[frontendAppName];
|
|
676
|
-
if (!frontendApp || frontendApp.type !== "frontend") continue;
|
|
677
|
-
const clientOutput = frontendApp.client?.output;
|
|
678
|
-
if (!clientOutput) continue;
|
|
679
|
-
const result = {
|
|
680
|
-
frontendApp: frontendAppName,
|
|
681
|
-
backendApp: backendAppName,
|
|
682
|
-
outputPath: "",
|
|
683
|
-
endpointCount,
|
|
684
|
-
success: false
|
|
685
|
-
};
|
|
686
|
-
try {
|
|
687
|
-
const frontendPath = (0, node_path.join)(workspace.root, frontendApp.path);
|
|
688
|
-
const outputDir = (0, node_path.join)(frontendPath, clientOutput);
|
|
689
|
-
await (0, node_fs_promises.mkdir)(outputDir, { recursive: true });
|
|
690
|
-
const fileName = `${backendAppName}.ts`;
|
|
691
|
-
const outputPath = (0, node_path.join)(outputDir, fileName);
|
|
692
|
-
const backendRelPath = (0, node_path.relative)((0, node_path.dirname)(outputPath), (0, node_path.join)(workspace.root, backendApp.path));
|
|
693
|
-
const clientContent = `/**
|
|
694
|
-
* Auto-generated API client for ${backendAppName}
|
|
695
|
-
* Generated from: ${backendRelPath}
|
|
696
|
-
*
|
|
697
|
-
* DO NOT EDIT - This file is automatically regenerated when backend schemas change.
|
|
698
|
-
*/
|
|
699
|
-
|
|
700
|
-
${content}
|
|
701
|
-
`;
|
|
702
|
-
await (0, node_fs_promises.writeFile)(outputPath, clientContent);
|
|
703
|
-
result.outputPath = outputPath;
|
|
704
|
-
result.success = true;
|
|
705
|
-
log(`📦 Copied client to ${frontendAppName} from ${backendAppName} (${endpointCount} endpoints)`);
|
|
706
|
-
} catch (error) {
|
|
707
|
-
result.error = error.message;
|
|
708
|
-
}
|
|
709
|
-
results.push(result);
|
|
710
|
-
}
|
|
711
|
-
return results;
|
|
712
|
-
}
|
|
713
|
-
/**
|
|
714
|
-
* Copy clients from all backends to their dependent frontends.
|
|
715
|
-
* Useful for initial setup or force refresh.
|
|
716
|
-
*/
|
|
717
|
-
async function copyAllClients(workspace, options = {}) {
|
|
718
|
-
const allResults = [];
|
|
719
|
-
for (const [appName, app] of Object.entries(workspace.apps)) if (app.type === "backend" && app.routes) {
|
|
720
|
-
const results = await copyClientToFrontends(workspace, appName, options);
|
|
721
|
-
allResults.push(...results);
|
|
722
|
-
}
|
|
723
|
-
return allResults;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
634
|
//#endregion
|
|
727
635
|
//#region src/dev/index.ts
|
|
728
636
|
const logger$11 = console;
|
|
@@ -1375,7 +1283,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1375
1283
|
logger$11.log("✅ Frontend apps validated");
|
|
1376
1284
|
}
|
|
1377
1285
|
if (frontendApps.length > 0 && backendApps.length > 0) {
|
|
1378
|
-
const clientResults = await copyAllClients(workspace);
|
|
1286
|
+
const clientResults = await require_openapi.copyAllClients(workspace);
|
|
1379
1287
|
const copiedCount = clientResults.filter((r) => r.success).length;
|
|
1380
1288
|
if (copiedCount > 0) logger$11.log(`\n📦 Copied ${copiedCount} API client(s)`);
|
|
1381
1289
|
}
|
|
@@ -1443,7 +1351,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1443
1351
|
if (frontendApps.length > 0 && backendApps.length > 0) {
|
|
1444
1352
|
const openApiPaths = [];
|
|
1445
1353
|
for (const [appName] of backendApps) {
|
|
1446
|
-
const openApiPath = getBackendOpenApiPath(workspace, appName);
|
|
1354
|
+
const openApiPath = require_openapi.getBackendOpenApiPath(workspace, appName);
|
|
1447
1355
|
if (openApiPath) openApiPaths.push({
|
|
1448
1356
|
path: openApiPath,
|
|
1449
1357
|
appName
|
|
@@ -1465,7 +1373,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1465
1373
|
if (!backendAppName) return;
|
|
1466
1374
|
logger$11.log(`\n🔄 OpenAPI spec changed for ${backendAppName}`);
|
|
1467
1375
|
try {
|
|
1468
|
-
const results = await copyClientToFrontends(workspace, backendAppName, { silent: true });
|
|
1376
|
+
const results = await require_openapi.copyClientToFrontends(workspace, backendAppName, { silent: true });
|
|
1469
1377
|
for (const result of results) if (result.success) logger$11.log(` 📦 Copied client to ${result.frontendApp} (${result.endpointCount} endpoints)`);
|
|
1470
1378
|
else if (result.error) logger$11.error(` ❌ Failed to copy client to ${result.frontendApp}: ${result.error}`);
|
|
1471
1379
|
} catch (error) {
|
|
@@ -1982,22 +1890,58 @@ async function execCommand(commandArgs, options = {}) {
|
|
|
1982
1890
|
//#endregion
|
|
1983
1891
|
//#region src/build/manifests.ts
|
|
1984
1892
|
const logger$10 = console;
|
|
1893
|
+
function isPartitioned(field) {
|
|
1894
|
+
return !Array.isArray(field);
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Serialize a manifest field to a TypeScript string.
|
|
1898
|
+
* Flat arrays serialize as JSON arrays, partitioned fields as objects of arrays.
|
|
1899
|
+
*/
|
|
1900
|
+
function serializeField(field, indent = 2) {
|
|
1901
|
+
if (Array.isArray(field)) return JSON.stringify(field, null, indent);
|
|
1902
|
+
const entries = Object.entries(field).map(([key, value]) => ` ${JSON.stringify(key)}: ${JSON.stringify(value, null, indent)}`).join(",\n");
|
|
1903
|
+
return `{\n${entries},\n }`;
|
|
1904
|
+
}
|
|
1905
|
+
/**
|
|
1906
|
+
* Count total items across a manifest field (flat or partitioned).
|
|
1907
|
+
*/
|
|
1908
|
+
function countItems(field) {
|
|
1909
|
+
if (Array.isArray(field)) return field.length;
|
|
1910
|
+
return Object.values(field).reduce((sum, arr) => sum + arr.length, 0);
|
|
1911
|
+
}
|
|
1912
|
+
/**
|
|
1913
|
+
* Generate derived types for a construct field.
|
|
1914
|
+
* @param fieldName - The field name in the manifest (e.g., 'routes')
|
|
1915
|
+
* @param typeName - The exported type name (e.g., 'Route')
|
|
1916
|
+
* @param partitioned - Whether this field is partitioned
|
|
1917
|
+
*/
|
|
1918
|
+
function generateDerivedType(fieldName, typeName, partitioned) {
|
|
1919
|
+
if (partitioned) {
|
|
1920
|
+
const partitionTypeName = `${typeName}Partition`;
|
|
1921
|
+
return [`export type ${partitionTypeName} = keyof typeof manifest.${fieldName};`, `export type ${typeName}<P extends ${partitionTypeName} = ${partitionTypeName}> = (typeof manifest.${fieldName})[P][number];`].join("\n");
|
|
1922
|
+
}
|
|
1923
|
+
return `export type ${typeName} = (typeof manifest.${fieldName})[number];`;
|
|
1924
|
+
}
|
|
1985
1925
|
async function generateAwsManifest(outputDir, routes, functions, crons, subscribers) {
|
|
1986
1926
|
const manifestDir = (0, node_path.join)(outputDir, "manifest");
|
|
1987
1927
|
await (0, node_fs_promises.mkdir)(manifestDir, { recursive: true });
|
|
1988
|
-
const awsRoutes = routes
|
|
1928
|
+
const awsRoutes = filterAllRoutes(routes);
|
|
1929
|
+
const routesPartitioned = isPartitioned(awsRoutes);
|
|
1930
|
+
const functionsPartitioned = isPartitioned(functions);
|
|
1931
|
+
const cronsPartitioned = isPartitioned(crons);
|
|
1932
|
+
const subscribersPartitioned = isPartitioned(subscribers);
|
|
1989
1933
|
const content = `export const manifest = {
|
|
1990
|
-
routes: ${
|
|
1991
|
-
functions: ${
|
|
1992
|
-
crons: ${
|
|
1993
|
-
subscribers: ${
|
|
1934
|
+
routes: ${serializeField(awsRoutes)},
|
|
1935
|
+
functions: ${serializeField(functions)},
|
|
1936
|
+
crons: ${serializeField(crons)},
|
|
1937
|
+
subscribers: ${serializeField(subscribers)},
|
|
1994
1938
|
} as const;
|
|
1995
1939
|
|
|
1996
1940
|
// Derived types
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
1941
|
+
${generateDerivedType("routes", "Route", routesPartitioned)}
|
|
1942
|
+
${generateDerivedType("functions", "Function", functionsPartitioned)}
|
|
1943
|
+
${generateDerivedType("crons", "Cron", cronsPartitioned)}
|
|
1944
|
+
${generateDerivedType("subscribers", "Subscriber", subscribersPartitioned)}
|
|
2001
1945
|
|
|
2002
1946
|
// Useful union types
|
|
2003
1947
|
export type Authorizer = Route['authorizer'];
|
|
@@ -2006,30 +1950,25 @@ export type RoutePath = Route['path'];
|
|
|
2006
1950
|
`;
|
|
2007
1951
|
const manifestPath = (0, node_path.join)(manifestDir, "aws.ts");
|
|
2008
1952
|
await (0, node_fs_promises.writeFile)(manifestPath, content);
|
|
2009
|
-
logger$10.log(`Generated AWS manifest with ${awsRoutes
|
|
1953
|
+
logger$10.log(`Generated AWS manifest with ${countItems(awsRoutes)} routes, ${countItems(functions)} functions, ${countItems(crons)} crons, ${countItems(subscribers)} subscribers`);
|
|
2010
1954
|
logger$10.log(`Manifest: ${(0, node_path.relative)(process.cwd(), manifestPath)}`);
|
|
2011
1955
|
}
|
|
2012
1956
|
async function generateServerManifest(outputDir, appInfo, routes, subscribers) {
|
|
2013
1957
|
const manifestDir = (0, node_path.join)(outputDir, "manifest");
|
|
2014
1958
|
await (0, node_fs_promises.mkdir)(manifestDir, { recursive: true });
|
|
2015
|
-
const serverRoutes =
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
}));
|
|
2020
|
-
const serverSubscribers = subscribers.map((s) => ({
|
|
2021
|
-
name: s.name,
|
|
2022
|
-
subscribedEvents: s.subscribedEvents
|
|
2023
|
-
}));
|
|
1959
|
+
const serverRoutes = mapRouteMetadata(filterAllRoutes(routes));
|
|
1960
|
+
const serverSubscribers = mapSubscriberMetadata(subscribers);
|
|
1961
|
+
const routesPartitioned = isPartitioned(serverRoutes);
|
|
1962
|
+
const subscribersPartitioned = isPartitioned(serverSubscribers);
|
|
2024
1963
|
const content = `export const manifest = {
|
|
2025
1964
|
app: ${JSON.stringify(appInfo, null, 2)},
|
|
2026
|
-
routes: ${
|
|
2027
|
-
subscribers: ${
|
|
1965
|
+
routes: ${serializeField(serverRoutes)},
|
|
1966
|
+
subscribers: ${serializeField(serverSubscribers)},
|
|
2028
1967
|
} as const;
|
|
2029
1968
|
|
|
2030
1969
|
// Derived types
|
|
2031
|
-
|
|
2032
|
-
|
|
1970
|
+
${generateDerivedType("routes", "Route", routesPartitioned)}
|
|
1971
|
+
${generateDerivedType("subscribers", "Subscriber", subscribersPartitioned)}
|
|
2033
1972
|
|
|
2034
1973
|
// Useful union types
|
|
2035
1974
|
export type Authorizer = Route['authorizer'];
|
|
@@ -2038,9 +1977,70 @@ export type RoutePath = Route['path'];
|
|
|
2038
1977
|
`;
|
|
2039
1978
|
const manifestPath = (0, node_path.join)(manifestDir, "server.ts");
|
|
2040
1979
|
await (0, node_fs_promises.writeFile)(manifestPath, content);
|
|
2041
|
-
logger$10.log(`Generated server manifest with ${serverRoutes
|
|
1980
|
+
logger$10.log(`Generated server manifest with ${countItems(serverRoutes)} routes, ${countItems(serverSubscribers)} subscribers`);
|
|
2042
1981
|
logger$10.log(`Manifest: ${(0, node_path.relative)(process.cwd(), manifestPath)}`);
|
|
2043
1982
|
}
|
|
1983
|
+
/**
|
|
1984
|
+
* Filter out 'ALL' method routes from a manifest field (flat or partitioned).
|
|
1985
|
+
*/
|
|
1986
|
+
function filterAllRoutes(routes) {
|
|
1987
|
+
if (Array.isArray(routes)) return routes.filter((r) => r.method !== "ALL");
|
|
1988
|
+
const result = {};
|
|
1989
|
+
for (const [partition, partitionRoutes] of Object.entries(routes)) result[partition] = partitionRoutes.filter((r) => r.method !== "ALL");
|
|
1990
|
+
return result;
|
|
1991
|
+
}
|
|
1992
|
+
/**
|
|
1993
|
+
* Map routes to server metadata (path, method, authorizer only).
|
|
1994
|
+
*/
|
|
1995
|
+
function mapRouteMetadata(routes) {
|
|
1996
|
+
const mapFn = (r) => ({
|
|
1997
|
+
path: r.path,
|
|
1998
|
+
method: r.method,
|
|
1999
|
+
authorizer: r.authorizer
|
|
2000
|
+
});
|
|
2001
|
+
if (Array.isArray(routes)) return routes.map(mapFn);
|
|
2002
|
+
const result = {};
|
|
2003
|
+
for (const [partition, partitionRoutes] of Object.entries(routes)) result[partition] = partitionRoutes.map(mapFn);
|
|
2004
|
+
return result;
|
|
2005
|
+
}
|
|
2006
|
+
/**
|
|
2007
|
+
* Map subscribers to server metadata (name, subscribedEvents only).
|
|
2008
|
+
*/
|
|
2009
|
+
function mapSubscriberMetadata(subscribers) {
|
|
2010
|
+
const mapFn = (s) => ({
|
|
2011
|
+
name: s.name,
|
|
2012
|
+
subscribedEvents: s.subscribedEvents
|
|
2013
|
+
});
|
|
2014
|
+
if (Array.isArray(subscribers)) return subscribers.map(mapFn);
|
|
2015
|
+
const result = {};
|
|
2016
|
+
for (const [partition, partitionSubs] of Object.entries(subscribers)) result[partition] = partitionSubs.map(mapFn);
|
|
2017
|
+
return result;
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
//#endregion
|
|
2021
|
+
//#region src/build/partitions.ts
|
|
2022
|
+
const DEFAULT_PARTITION = "default";
|
|
2023
|
+
/**
|
|
2024
|
+
* Check if any construct across the given arrays has a non-undefined partition.
|
|
2025
|
+
* When true, the manifest should use the partitioned shape for that construct type.
|
|
2026
|
+
*/
|
|
2027
|
+
function hasPartitions(constructs) {
|
|
2028
|
+
return constructs.some((c) => c.partition !== void 0);
|
|
2029
|
+
}
|
|
2030
|
+
/**
|
|
2031
|
+
* Group an info array by partition, using the partition values from
|
|
2032
|
+
* the corresponding construct array. Both arrays must be the same length
|
|
2033
|
+
* and in the same order.
|
|
2034
|
+
*/
|
|
2035
|
+
function groupInfosByPartition(infos, constructs) {
|
|
2036
|
+
const groups = {};
|
|
2037
|
+
for (let i = 0; i < infos.length; i++) {
|
|
2038
|
+
const partition = constructs[i]?.partition ?? DEFAULT_PARTITION;
|
|
2039
|
+
if (!groups[partition]) groups[partition] = [];
|
|
2040
|
+
groups[partition].push(infos[i]);
|
|
2041
|
+
}
|
|
2042
|
+
return groups;
|
|
2043
|
+
}
|
|
2044
2044
|
|
|
2045
2045
|
//#endregion
|
|
2046
2046
|
//#region src/build/index.ts
|
|
@@ -2062,10 +2062,10 @@ async function buildCommand(options) {
|
|
|
2062
2062
|
const production = normalizeProductionConfig(options.production ?? false, productionConfigFromGkm);
|
|
2063
2063
|
if (production) logger$9.log(`🏭 Building for PRODUCTION`);
|
|
2064
2064
|
logger$9.log(`Building with providers: ${resolved.providers.join(", ")}`);
|
|
2065
|
-
logger$9.log(`Loading routes from: ${config.routes}`);
|
|
2066
|
-
if (config.functions) logger$9.log(`Loading functions from: ${config.functions}`);
|
|
2067
|
-
if (config.crons) logger$9.log(`Loading crons from: ${config.crons}`);
|
|
2068
|
-
if (config.subscribers) logger$9.log(`Loading subscribers from: ${config.subscribers}`);
|
|
2065
|
+
logger$9.log(`Loading routes from: ${formatRoutes(config.routes)}`);
|
|
2066
|
+
if (config.functions) logger$9.log(`Loading functions from: ${formatRoutes(config.functions)}`);
|
|
2067
|
+
if (config.crons) logger$9.log(`Loading crons from: ${formatRoutes(config.crons)}`);
|
|
2068
|
+
if (config.subscribers) logger$9.log(`Loading subscribers from: ${formatRoutes(config.subscribers)}`);
|
|
2069
2069
|
logger$9.log(`Using envParser: ${config.envParser}`);
|
|
2070
2070
|
const { path: envParserPath, importPattern: envParserImportPattern } = require_config.parseModuleConfig(config.envParser, "envParser");
|
|
2071
2071
|
const { path: loggerPath, importPattern: loggerImportPattern } = require_config.parseModuleConfig(config.logger, "logger");
|
|
@@ -2137,6 +2137,10 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2137
2137
|
subscriberGenerator.build(context, subscribers, outputDir, { provider })
|
|
2138
2138
|
]);
|
|
2139
2139
|
logger$9.log(`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers for ${provider}`);
|
|
2140
|
+
const manifestRoutes = assembleManifestField(routes, endpoints);
|
|
2141
|
+
const manifestFunctions = assembleManifestField(functionInfos, functions);
|
|
2142
|
+
const manifestCrons = assembleManifestField(cronInfos, crons);
|
|
2143
|
+
const manifestSubscribers = assembleManifestField(subscriberInfos, subscribers);
|
|
2140
2144
|
if (provider === "server") {
|
|
2141
2145
|
const routeMetadata = await Promise.all(endpoints.map(async ({ construct }) => ({
|
|
2142
2146
|
path: construct._path,
|
|
@@ -2144,11 +2148,12 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2144
2148
|
handler: "",
|
|
2145
2149
|
authorizer: construct.authorizer?.name ?? "none"
|
|
2146
2150
|
})));
|
|
2151
|
+
const serverRouteField = assembleManifestField(routeMetadata, endpoints);
|
|
2147
2152
|
const appInfo = {
|
|
2148
2153
|
handler: (0, node_path.relative)(process.cwd(), (0, node_path.join)(outputDir, "app.ts")),
|
|
2149
2154
|
endpoints: (0, node_path.relative)(process.cwd(), (0, node_path.join)(outputDir, "endpoints.ts"))
|
|
2150
2155
|
};
|
|
2151
|
-
await generateServerManifest(rootOutputDir, appInfo,
|
|
2156
|
+
await generateServerManifest(rootOutputDir, appInfo, serverRouteField, manifestSubscribers);
|
|
2152
2157
|
let masterKey;
|
|
2153
2158
|
if (context.production?.bundle && !skipBundle) {
|
|
2154
2159
|
logger$9.log(`\n📦 Bundling production server...`);
|
|
@@ -2178,7 +2183,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2178
2183
|
}
|
|
2179
2184
|
}
|
|
2180
2185
|
return { masterKey };
|
|
2181
|
-
} else await generateAwsManifest(rootOutputDir,
|
|
2186
|
+
} else await generateAwsManifest(rootOutputDir, manifestRoutes, manifestFunctions, manifestCrons, manifestSubscribers);
|
|
2182
2187
|
return {};
|
|
2183
2188
|
}
|
|
2184
2189
|
/**
|
|
@@ -2276,6 +2281,25 @@ function getAppOutputPath(workspace, _appName, app) {
|
|
|
2276
2281
|
if (app.type === "frontend") return (0, node_path.join)(appPath, ".next");
|
|
2277
2282
|
else return (0, node_path.join)(appPath, ".gkm");
|
|
2278
2283
|
}
|
|
2284
|
+
/**
|
|
2285
|
+
* Format routes for logging, handling PartitionedRoutes.
|
|
2286
|
+
*/
|
|
2287
|
+
function formatRoutes(routes) {
|
|
2288
|
+
if (require_openapi.isPartitionedRoutes(routes)) {
|
|
2289
|
+
const paths = Array.isArray(routes.paths) ? routes.paths.join(", ") : routes.paths;
|
|
2290
|
+
return `${paths} (partitioned)`;
|
|
2291
|
+
}
|
|
2292
|
+
return Array.isArray(routes) ? routes.join(", ") : routes;
|
|
2293
|
+
}
|
|
2294
|
+
/**
|
|
2295
|
+
* Assemble a ManifestField from build infos and constructs.
|
|
2296
|
+
* If any construct has a partition, returns a Record<string, T[]>.
|
|
2297
|
+
* Otherwise, returns a flat T[].
|
|
2298
|
+
*/
|
|
2299
|
+
function assembleManifestField(infos, constructs) {
|
|
2300
|
+
if (!hasPartitions(constructs)) return infos;
|
|
2301
|
+
return groupInfosByPartition(infos, constructs);
|
|
2302
|
+
}
|
|
2279
2303
|
|
|
2280
2304
|
//#endregion
|
|
2281
2305
|
//#region src/deploy/state.ts
|
|
@@ -4890,7 +4914,7 @@ async function sniffAppEnvironment(app, appName, workspacePath, options = {}) {
|
|
|
4890
4914
|
};
|
|
4891
4915
|
}
|
|
4892
4916
|
if (app.routes) {
|
|
4893
|
-
const result = await sniffRouteFiles(app.routes, app.path, workspacePath);
|
|
4917
|
+
const result = await sniffRouteFiles(require_openapi.normalizeRoutes(app.routes), app.path, workspacePath);
|
|
4894
4918
|
if (logWarnings && result.error) console.warn(`[sniffer] ${appName}: Route sniffing threw error (env vars still captured): ${result.error.message}`);
|
|
4895
4919
|
return {
|
|
4896
4920
|
appName,
|
|
@@ -6442,22 +6466,22 @@ const CLI_VERSION = `~${pkg.version}`;
|
|
|
6442
6466
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
6443
6467
|
*/
|
|
6444
6468
|
const GEEKMIDAS_VERSIONS = {
|
|
6445
|
-
"@geekmidas/audit": "~
|
|
6446
|
-
"@geekmidas/auth": "~
|
|
6447
|
-
"@geekmidas/cache": "~1.
|
|
6448
|
-
"@geekmidas/client": "~
|
|
6469
|
+
"@geekmidas/audit": "~2.0.0",
|
|
6470
|
+
"@geekmidas/auth": "~2.0.0",
|
|
6471
|
+
"@geekmidas/cache": "~1.1.0",
|
|
6472
|
+
"@geekmidas/client": "~4.0.0",
|
|
6449
6473
|
"@geekmidas/cloud": "~1.0.0",
|
|
6450
|
-
"@geekmidas/constructs": "~
|
|
6474
|
+
"@geekmidas/constructs": "~3.0.0",
|
|
6451
6475
|
"@geekmidas/db": "~1.0.0",
|
|
6452
6476
|
"@geekmidas/emailkit": "~1.0.0",
|
|
6453
6477
|
"@geekmidas/envkit": "~1.0.3",
|
|
6454
6478
|
"@geekmidas/errors": "~1.0.0",
|
|
6455
6479
|
"@geekmidas/events": "~1.1.0",
|
|
6456
6480
|
"@geekmidas/logger": "~1.0.0",
|
|
6457
|
-
"@geekmidas/rate-limit": "~
|
|
6481
|
+
"@geekmidas/rate-limit": "~2.0.0",
|
|
6458
6482
|
"@geekmidas/schema": "~1.0.0",
|
|
6459
6483
|
"@geekmidas/services": "~1.0.1",
|
|
6460
|
-
"@geekmidas/storage": "~
|
|
6484
|
+
"@geekmidas/storage": "~2.0.0",
|
|
6461
6485
|
"@geekmidas/studio": "~1.0.0",
|
|
6462
6486
|
"@geekmidas/telescope": "~1.0.0",
|
|
6463
6487
|
"@geekmidas/testkit": "~1.0.2",
|