@geekmidas/cli 0.9.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +525 -0
- package/dist/bundler-DRXCw_YR.mjs +70 -0
- package/dist/bundler-DRXCw_YR.mjs.map +1 -0
- package/dist/bundler-WsEvH_b2.cjs +71 -0
- package/dist/bundler-WsEvH_b2.cjs.map +1 -0
- package/dist/{config-CFls09Ey.cjs → config-AmInkU7k.cjs} +10 -8
- package/dist/config-AmInkU7k.cjs.map +1 -0
- package/dist/{config-Bq72aj8e.mjs → config-DYULeEv8.mjs} +6 -4
- package/dist/config-DYULeEv8.mjs.map +1 -0
- package/dist/config.cjs +1 -1
- package/dist/config.d.cts +2 -1
- package/dist/config.d.cts.map +1 -0
- package/dist/config.d.mts +2 -1
- package/dist/config.d.mts.map +1 -0
- package/dist/config.mjs +1 -1
- package/dist/encryption-C8H-38Yy.mjs +42 -0
- package/dist/encryption-C8H-38Yy.mjs.map +1 -0
- package/dist/encryption-Dyf_r1h-.cjs +44 -0
- package/dist/encryption-Dyf_r1h-.cjs.map +1 -0
- package/dist/index.cjs +2125 -184
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2143 -197
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi--vOy9mo4.mjs → openapi-BfFlOBCG.mjs} +812 -49
- package/dist/openapi-BfFlOBCG.mjs.map +1 -0
- package/dist/{openapi-CHhTPief.cjs → openapi-Bt_1FDpT.cjs} +805 -42
- package/dist/openapi-Bt_1FDpT.cjs.map +1 -0
- package/dist/{openapi-react-query-o5iMi8tz.cjs → openapi-react-query-B-sNWHFU.cjs} +5 -5
- package/dist/openapi-react-query-B-sNWHFU.cjs.map +1 -0
- package/dist/{openapi-react-query-CcciaVu5.mjs → openapi-react-query-B6XTeGqS.mjs} +5 -5
- package/dist/openapi-react-query-B6XTeGqS.mjs.map +1 -0
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.d.cts.map +1 -0
- package/dist/openapi-react-query.d.mts.map +1 -0
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +2 -2
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.cts.map +1 -0
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.d.mts.map +1 -0
- package/dist/openapi.mjs +2 -2
- package/dist/storage-BUYQJgz7.cjs +4 -0
- package/dist/storage-BXoJvmv2.cjs +149 -0
- package/dist/storage-BXoJvmv2.cjs.map +1 -0
- package/dist/storage-C9PU_30f.mjs +101 -0
- package/dist/storage-C9PU_30f.mjs.map +1 -0
- package/dist/storage-DLJAYxzJ.mjs +3 -0
- package/dist/{types-b-vwGpqc.d.cts → types-BR0M2v_c.d.mts} +100 -1
- package/dist/types-BR0M2v_c.d.mts.map +1 -0
- package/dist/{types-DXgiA1sF.d.mts → types-BhkZc-vm.d.cts} +100 -1
- package/dist/types-BhkZc-vm.d.cts.map +1 -0
- package/examples/cron-example.ts +27 -27
- package/examples/env.ts +27 -27
- package/examples/function-example.ts +31 -31
- package/examples/gkm.config.json +20 -20
- package/examples/gkm.config.ts +8 -8
- package/examples/gkm.minimal.config.json +5 -5
- package/examples/gkm.production.config.json +25 -25
- package/examples/logger.ts +2 -2
- package/package.json +6 -6
- package/src/__tests__/EndpointGenerator.hooks.spec.ts +191 -191
- package/src/__tests__/config.spec.ts +55 -55
- package/src/__tests__/loadEnvFiles.spec.ts +93 -93
- package/src/__tests__/normalizeHooksConfig.spec.ts +58 -58
- package/src/__tests__/openapi-react-query.spec.ts +497 -497
- package/src/__tests__/openapi.spec.ts +428 -428
- package/src/__tests__/test-helpers.ts +77 -76
- package/src/auth/__tests__/credentials.spec.ts +204 -0
- package/src/auth/__tests__/index.spec.ts +168 -0
- package/src/auth/credentials.ts +187 -0
- package/src/auth/index.ts +226 -0
- package/src/build/__tests__/index-new.spec.ts +474 -474
- package/src/build/__tests__/manifests.spec.ts +333 -333
- package/src/build/bundler.ts +141 -0
- package/src/build/endpoint-analyzer.ts +236 -0
- package/src/build/handler-templates.ts +1253 -0
- package/src/build/index.ts +250 -179
- package/src/build/manifests.ts +52 -52
- package/src/build/providerResolver.ts +145 -145
- package/src/build/types.ts +64 -43
- package/src/config.ts +39 -37
- package/src/deploy/__tests__/docker.spec.ts +111 -0
- package/src/deploy/__tests__/dokploy.spec.ts +245 -0
- package/src/deploy/__tests__/init.spec.ts +662 -0
- package/src/deploy/docker.ts +128 -0
- package/src/deploy/dokploy.ts +204 -0
- package/src/deploy/index.ts +136 -0
- package/src/deploy/init.ts +484 -0
- package/src/deploy/types.ts +48 -0
- package/src/dev/__tests__/index.spec.ts +266 -266
- package/src/dev/index.ts +647 -593
- package/src/docker/__tests__/compose.spec.ts +531 -0
- package/src/docker/__tests__/templates.spec.ts +280 -0
- package/src/docker/compose.ts +273 -0
- package/src/docker/index.ts +230 -0
- package/src/docker/templates.ts +446 -0
- package/src/generators/CronGenerator.ts +72 -72
- package/src/generators/EndpointGenerator.ts +699 -398
- package/src/generators/FunctionGenerator.ts +84 -84
- package/src/generators/Generator.ts +72 -72
- package/src/generators/OpenApiTsGenerator.ts +589 -589
- package/src/generators/SubscriberGenerator.ts +124 -124
- package/src/generators/__tests__/CronGenerator.spec.ts +433 -433
- package/src/generators/__tests__/EndpointGenerator.spec.ts +532 -382
- package/src/generators/__tests__/FunctionGenerator.spec.ts +244 -244
- package/src/generators/__tests__/SubscriberGenerator.spec.ts +397 -382
- package/src/generators/index.ts +4 -4
- package/src/index.ts +628 -206
- package/src/init/__tests__/generators.spec.ts +334 -334
- package/src/init/__tests__/init.spec.ts +332 -332
- package/src/init/__tests__/utils.spec.ts +89 -89
- package/src/init/generators/config.ts +175 -175
- package/src/init/generators/docker.ts +41 -41
- package/src/init/generators/env.ts +72 -72
- package/src/init/generators/index.ts +1 -1
- package/src/init/generators/models.ts +64 -64
- package/src/init/generators/monorepo.ts +161 -161
- package/src/init/generators/package.ts +71 -71
- package/src/init/generators/source.ts +6 -6
- package/src/init/index.ts +203 -208
- package/src/init/templates/api.ts +115 -115
- package/src/init/templates/index.ts +75 -75
- package/src/init/templates/minimal.ts +98 -98
- package/src/init/templates/serverless.ts +89 -89
- package/src/init/templates/worker.ts +98 -98
- package/src/init/utils.ts +54 -56
- package/src/openapi-react-query.ts +194 -194
- package/src/openapi.ts +63 -63
- package/src/secrets/__tests__/encryption.spec.ts +226 -0
- package/src/secrets/__tests__/generator.spec.ts +319 -0
- package/src/secrets/__tests__/index.spec.ts +91 -0
- package/src/secrets/__tests__/storage.spec.ts +403 -0
- package/src/secrets/encryption.ts +91 -0
- package/src/secrets/generator.ts +164 -0
- package/src/secrets/index.ts +383 -0
- package/src/secrets/storage.ts +134 -0
- package/src/secrets/types.ts +53 -0
- package/src/types.ts +295 -176
- package/tsconfig.json +9 -0
- package/tsdown.config.ts +11 -8
- package/dist/config-Bq72aj8e.mjs.map +0 -1
- package/dist/config-CFls09Ey.cjs.map +0 -1
- package/dist/openapi--vOy9mo4.mjs.map +0 -1
- package/dist/openapi-CHhTPief.cjs.map +0 -1
- package/dist/openapi-react-query-CcciaVu5.mjs.map +0 -1
- package/dist/openapi-react-query-o5iMi8tz.cjs.map +0 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-CUT6urMc.cjs');
|
|
2
|
+
const node_fs = require_chunk.__toESM(require("node:fs"));
|
|
3
|
+
const node_path = require_chunk.__toESM(require("node:path"));
|
|
4
|
+
const node_fs_promises = require_chunk.__toESM(require("node:fs/promises"));
|
|
5
|
+
|
|
6
|
+
//#region src/secrets/storage.ts
|
|
7
|
+
/** Default secrets directory relative to project root */
|
|
8
|
+
const SECRETS_DIR = ".gkm/secrets";
|
|
9
|
+
/**
|
|
10
|
+
* Get the secrets directory path.
|
|
11
|
+
*/
|
|
12
|
+
function getSecretsDir(cwd = process.cwd()) {
|
|
13
|
+
return (0, node_path.join)(cwd, SECRETS_DIR);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get the secrets file path for a stage.
|
|
17
|
+
*/
|
|
18
|
+
function getSecretsPath(stage, cwd = process.cwd()) {
|
|
19
|
+
return (0, node_path.join)(getSecretsDir(cwd), `${stage}.json`);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Check if secrets exist for a stage.
|
|
23
|
+
*/
|
|
24
|
+
function secretsExist(stage, cwd = process.cwd()) {
|
|
25
|
+
return (0, node_fs.existsSync)(getSecretsPath(stage, cwd));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Read secrets for a stage.
|
|
29
|
+
* @returns StageSecrets or null if not found
|
|
30
|
+
*/
|
|
31
|
+
async function readStageSecrets(stage, cwd = process.cwd()) {
|
|
32
|
+
const path = getSecretsPath(stage, cwd);
|
|
33
|
+
if (!(0, node_fs.existsSync)(path)) return null;
|
|
34
|
+
const content = await (0, node_fs_promises.readFile)(path, "utf-8");
|
|
35
|
+
return JSON.parse(content);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Write secrets for a stage.
|
|
39
|
+
*/
|
|
40
|
+
async function writeStageSecrets(secrets, cwd = process.cwd()) {
|
|
41
|
+
const dir = getSecretsDir(cwd);
|
|
42
|
+
const path = getSecretsPath(secrets.stage, cwd);
|
|
43
|
+
await (0, node_fs_promises.mkdir)(dir, { recursive: true });
|
|
44
|
+
await (0, node_fs_promises.writeFile)(path, JSON.stringify(secrets, null, 2), "utf-8");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Convert StageSecrets to embeddable format (flat key-value pairs).
|
|
48
|
+
* This is what gets encrypted and embedded in the bundle.
|
|
49
|
+
*/
|
|
50
|
+
function toEmbeddableSecrets(secrets) {
|
|
51
|
+
return {
|
|
52
|
+
...secrets.urls,
|
|
53
|
+
...secrets.custom,
|
|
54
|
+
...secrets.services.postgres && {
|
|
55
|
+
POSTGRES_USER: secrets.services.postgres.username,
|
|
56
|
+
POSTGRES_PASSWORD: secrets.services.postgres.password,
|
|
57
|
+
POSTGRES_DB: secrets.services.postgres.database ?? "app",
|
|
58
|
+
POSTGRES_HOST: secrets.services.postgres.host,
|
|
59
|
+
POSTGRES_PORT: String(secrets.services.postgres.port)
|
|
60
|
+
},
|
|
61
|
+
...secrets.services.redis && {
|
|
62
|
+
REDIS_PASSWORD: secrets.services.redis.password,
|
|
63
|
+
REDIS_HOST: secrets.services.redis.host,
|
|
64
|
+
REDIS_PORT: String(secrets.services.redis.port)
|
|
65
|
+
},
|
|
66
|
+
...secrets.services.rabbitmq && {
|
|
67
|
+
RABBITMQ_USER: secrets.services.rabbitmq.username,
|
|
68
|
+
RABBITMQ_PASSWORD: secrets.services.rabbitmq.password,
|
|
69
|
+
RABBITMQ_HOST: secrets.services.rabbitmq.host,
|
|
70
|
+
RABBITMQ_PORT: String(secrets.services.rabbitmq.port),
|
|
71
|
+
RABBITMQ_VHOST: secrets.services.rabbitmq.vhost ?? "/"
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Update a custom secret in the secrets file.
|
|
77
|
+
*/
|
|
78
|
+
async function setCustomSecret(stage, key, value, cwd = process.cwd()) {
|
|
79
|
+
const secrets = await readStageSecrets(stage, cwd);
|
|
80
|
+
if (!secrets) throw new Error(`Secrets not found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
81
|
+
const updated = {
|
|
82
|
+
...secrets,
|
|
83
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
84
|
+
custom: {
|
|
85
|
+
...secrets.custom,
|
|
86
|
+
[key]: value
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
await writeStageSecrets(updated, cwd);
|
|
90
|
+
return updated;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Mask a password for display (show first 4 and last 2 chars).
|
|
94
|
+
*/
|
|
95
|
+
function maskPassword(password) {
|
|
96
|
+
if (password.length <= 8) return "********";
|
|
97
|
+
return `${password.slice(0, 4)}${"*".repeat(password.length - 6)}${password.slice(-2)}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
//#endregion
|
|
101
|
+
Object.defineProperty(exports, 'getSecretsDir', {
|
|
102
|
+
enumerable: true,
|
|
103
|
+
get: function () {
|
|
104
|
+
return getSecretsDir;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
Object.defineProperty(exports, 'getSecretsPath', {
|
|
108
|
+
enumerable: true,
|
|
109
|
+
get: function () {
|
|
110
|
+
return getSecretsPath;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
Object.defineProperty(exports, 'maskPassword', {
|
|
114
|
+
enumerable: true,
|
|
115
|
+
get: function () {
|
|
116
|
+
return maskPassword;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
Object.defineProperty(exports, 'readStageSecrets', {
|
|
120
|
+
enumerable: true,
|
|
121
|
+
get: function () {
|
|
122
|
+
return readStageSecrets;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
Object.defineProperty(exports, 'secretsExist', {
|
|
126
|
+
enumerable: true,
|
|
127
|
+
get: function () {
|
|
128
|
+
return secretsExist;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
Object.defineProperty(exports, 'setCustomSecret', {
|
|
132
|
+
enumerable: true,
|
|
133
|
+
get: function () {
|
|
134
|
+
return setCustomSecret;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
Object.defineProperty(exports, 'toEmbeddableSecrets', {
|
|
138
|
+
enumerable: true,
|
|
139
|
+
get: function () {
|
|
140
|
+
return toEmbeddableSecrets;
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
Object.defineProperty(exports, 'writeStageSecrets', {
|
|
144
|
+
enumerable: true,
|
|
145
|
+
get: function () {
|
|
146
|
+
return writeStageSecrets;
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
//# sourceMappingURL=storage-BXoJvmv2.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-BXoJvmv2.cjs","names":["stage: string","secrets: StageSecrets","key: string","value: string","updated: StageSecrets","password: string"],"sources":["../src/secrets/storage.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { EmbeddableSecrets, StageSecrets } from './types';\n\n/** Default secrets directory relative to project root */\nconst SECRETS_DIR = '.gkm/secrets';\n\n/**\n * Get the secrets directory path.\n */\nexport function getSecretsDir(cwd = process.cwd()): string {\n\treturn join(cwd, SECRETS_DIR);\n}\n\n/**\n * Get the secrets file path for a stage.\n */\nexport function getSecretsPath(stage: string, cwd = process.cwd()): string {\n\treturn join(getSecretsDir(cwd), `${stage}.json`);\n}\n\n/**\n * Check if secrets exist for a stage.\n */\nexport function secretsExist(stage: string, cwd = process.cwd()): boolean {\n\treturn existsSync(getSecretsPath(stage, cwd));\n}\n\n/**\n * Read secrets for a stage.\n * @returns StageSecrets or null if not found\n */\nexport async function readStageSecrets(\n\tstage: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets | null> {\n\tconst path = getSecretsPath(stage, cwd);\n\n\tif (!existsSync(path)) {\n\t\treturn null;\n\t}\n\n\tconst content = await readFile(path, 'utf-8');\n\treturn JSON.parse(content) as StageSecrets;\n}\n\n/**\n * Write secrets for a stage.\n */\nexport async function writeStageSecrets(\n\tsecrets: StageSecrets,\n\tcwd = process.cwd(),\n): Promise<void> {\n\tconst dir = getSecretsDir(cwd);\n\tconst path = getSecretsPath(secrets.stage, cwd);\n\n\t// Ensure directory exists\n\tawait mkdir(dir, { recursive: true });\n\n\t// Write with pretty formatting\n\tawait writeFile(path, JSON.stringify(secrets, null, 2), 'utf-8');\n}\n\n/**\n * Convert StageSecrets to embeddable format (flat key-value pairs).\n * This is what gets encrypted and embedded in the bundle.\n */\nexport function toEmbeddableSecrets(secrets: StageSecrets): EmbeddableSecrets {\n\treturn {\n\t\t...secrets.urls,\n\t\t...secrets.custom,\n\t\t// Also include individual service credentials if needed\n\t\t...(secrets.services.postgres && {\n\t\t\tPOSTGRES_USER: secrets.services.postgres.username,\n\t\t\tPOSTGRES_PASSWORD: secrets.services.postgres.password,\n\t\t\tPOSTGRES_DB: secrets.services.postgres.database ?? 'app',\n\t\t\tPOSTGRES_HOST: secrets.services.postgres.host,\n\t\t\tPOSTGRES_PORT: String(secrets.services.postgres.port),\n\t\t}),\n\t\t...(secrets.services.redis && {\n\t\t\tREDIS_PASSWORD: secrets.services.redis.password,\n\t\t\tREDIS_HOST: secrets.services.redis.host,\n\t\t\tREDIS_PORT: String(secrets.services.redis.port),\n\t\t}),\n\t\t...(secrets.services.rabbitmq && {\n\t\t\tRABBITMQ_USER: secrets.services.rabbitmq.username,\n\t\t\tRABBITMQ_PASSWORD: secrets.services.rabbitmq.password,\n\t\t\tRABBITMQ_HOST: secrets.services.rabbitmq.host,\n\t\t\tRABBITMQ_PORT: String(secrets.services.rabbitmq.port),\n\t\t\tRABBITMQ_VHOST: secrets.services.rabbitmq.vhost ?? '/',\n\t\t}),\n\t};\n}\n\n/**\n * Update a custom secret in the secrets file.\n */\nexport async function setCustomSecret(\n\tstage: string,\n\tkey: string,\n\tvalue: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets> {\n\tconst secrets = await readStageSecrets(stage, cwd);\n\n\tif (!secrets) {\n\t\tthrow new Error(\n\t\t\t`Secrets not found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t}\n\n\tconst updated: StageSecrets = {\n\t\t...secrets,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tcustom: {\n\t\t\t...secrets.custom,\n\t\t\t[key]: value,\n\t\t},\n\t};\n\n\tawait writeStageSecrets(updated, cwd);\n\treturn updated;\n}\n\n/**\n * Mask a password for display (show first 4 and last 2 chars).\n */\nexport function maskPassword(password: string): string {\n\tif (password.length <= 8) {\n\t\treturn '********';\n\t}\n\treturn `${password.slice(0, 4)}${'*'.repeat(password.length - 6)}${password.slice(-2)}`;\n}\n"],"mappings":";;;;;;;AAMA,MAAM,cAAc;;;;AAKpB,SAAgB,cAAc,MAAM,QAAQ,KAAK,EAAU;AAC1D,QAAO,oBAAK,KAAK,YAAY;AAC7B;;;;AAKD,SAAgB,eAAeA,OAAe,MAAM,QAAQ,KAAK,EAAU;AAC1E,QAAO,oBAAK,cAAc,IAAI,GAAG,EAAE,MAAM,OAAO;AAChD;;;;AAKD,SAAgB,aAAaA,OAAe,MAAM,QAAQ,KAAK,EAAW;AACzE,QAAO,wBAAW,eAAe,OAAO,IAAI,CAAC;AAC7C;;;;;AAMD,eAAsB,iBACrBA,OACA,MAAM,QAAQ,KAAK,EACY;CAC/B,MAAM,OAAO,eAAe,OAAO,IAAI;AAEvC,MAAK,wBAAW,KAAK,CACpB,QAAO;CAGR,MAAM,UAAU,MAAM,+BAAS,MAAM,QAAQ;AAC7C,QAAO,KAAK,MAAM,QAAQ;AAC1B;;;;AAKD,eAAsB,kBACrBC,SACA,MAAM,QAAQ,KAAK,EACH;CAChB,MAAM,MAAM,cAAc,IAAI;CAC9B,MAAM,OAAO,eAAe,QAAQ,OAAO,IAAI;AAG/C,OAAM,4BAAM,KAAK,EAAE,WAAW,KAAM,EAAC;AAGrC,OAAM,gCAAU,MAAM,KAAK,UAAU,SAAS,MAAM,EAAE,EAAE,QAAQ;AAChE;;;;;AAMD,SAAgB,oBAAoBA,SAA0C;AAC7E,QAAO;EACN,GAAG,QAAQ;EACX,GAAG,QAAQ;EAEX,GAAI,QAAQ,SAAS,YAAY;GAChC,eAAe,QAAQ,SAAS,SAAS;GACzC,mBAAmB,QAAQ,SAAS,SAAS;GAC7C,aAAa,QAAQ,SAAS,SAAS,YAAY;GACnD,eAAe,QAAQ,SAAS,SAAS;GACzC,eAAe,OAAO,QAAQ,SAAS,SAAS,KAAK;EACrD;EACD,GAAI,QAAQ,SAAS,SAAS;GAC7B,gBAAgB,QAAQ,SAAS,MAAM;GACvC,YAAY,QAAQ,SAAS,MAAM;GACnC,YAAY,OAAO,QAAQ,SAAS,MAAM,KAAK;EAC/C;EACD,GAAI,QAAQ,SAAS,YAAY;GAChC,eAAe,QAAQ,SAAS,SAAS;GACzC,mBAAmB,QAAQ,SAAS,SAAS;GAC7C,eAAe,QAAQ,SAAS,SAAS;GACzC,eAAe,OAAO,QAAQ,SAAS,SAAS,KAAK;GACrD,gBAAgB,QAAQ,SAAS,SAAS,SAAS;EACnD;CACD;AACD;;;;AAKD,eAAsB,gBACrBD,OACAE,KACAC,OACA,MAAM,QAAQ,KAAK,EACK;CACxB,MAAM,UAAU,MAAM,iBAAiB,OAAO,IAAI;AAElD,MAAK,QACJ,OAAM,IAAI,OACR,+BAA+B,MAAM,mCAAmC,MAAM;CAIjF,MAAMC,UAAwB;EAC7B,GAAG;EACH,WAAW,qBAAI,QAAO,aAAa;EACnC,QAAQ;GACP,GAAG,QAAQ;IACV,MAAM;EACP;CACD;AAED,OAAM,kBAAkB,SAAS,IAAI;AACrC,QAAO;AACP;;;;AAKD,SAAgB,aAAaC,UAA0B;AACtD,KAAI,SAAS,UAAU,EACtB,QAAO;AAER,SAAQ,EAAE,SAAS,MAAM,GAAG,EAAE,CAAC,EAAE,IAAI,OAAO,SAAS,SAAS,EAAE,CAAC,EAAE,SAAS,MAAM,GAAG,CAAC;AACtF"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
|
|
5
|
+
//#region src/secrets/storage.ts
|
|
6
|
+
/** Default secrets directory relative to project root */
|
|
7
|
+
const SECRETS_DIR = ".gkm/secrets";
|
|
8
|
+
/**
|
|
9
|
+
* Get the secrets directory path.
|
|
10
|
+
*/
|
|
11
|
+
function getSecretsDir(cwd = process.cwd()) {
|
|
12
|
+
return join(cwd, SECRETS_DIR);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get the secrets file path for a stage.
|
|
16
|
+
*/
|
|
17
|
+
function getSecretsPath(stage, cwd = process.cwd()) {
|
|
18
|
+
return join(getSecretsDir(cwd), `${stage}.json`);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Check if secrets exist for a stage.
|
|
22
|
+
*/
|
|
23
|
+
function secretsExist(stage, cwd = process.cwd()) {
|
|
24
|
+
return existsSync(getSecretsPath(stage, cwd));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Read secrets for a stage.
|
|
28
|
+
* @returns StageSecrets or null if not found
|
|
29
|
+
*/
|
|
30
|
+
async function readStageSecrets(stage, cwd = process.cwd()) {
|
|
31
|
+
const path = getSecretsPath(stage, cwd);
|
|
32
|
+
if (!existsSync(path)) return null;
|
|
33
|
+
const content = await readFile(path, "utf-8");
|
|
34
|
+
return JSON.parse(content);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Write secrets for a stage.
|
|
38
|
+
*/
|
|
39
|
+
async function writeStageSecrets(secrets, cwd = process.cwd()) {
|
|
40
|
+
const dir = getSecretsDir(cwd);
|
|
41
|
+
const path = getSecretsPath(secrets.stage, cwd);
|
|
42
|
+
await mkdir(dir, { recursive: true });
|
|
43
|
+
await writeFile(path, JSON.stringify(secrets, null, 2), "utf-8");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Convert StageSecrets to embeddable format (flat key-value pairs).
|
|
47
|
+
* This is what gets encrypted and embedded in the bundle.
|
|
48
|
+
*/
|
|
49
|
+
function toEmbeddableSecrets(secrets) {
|
|
50
|
+
return {
|
|
51
|
+
...secrets.urls,
|
|
52
|
+
...secrets.custom,
|
|
53
|
+
...secrets.services.postgres && {
|
|
54
|
+
POSTGRES_USER: secrets.services.postgres.username,
|
|
55
|
+
POSTGRES_PASSWORD: secrets.services.postgres.password,
|
|
56
|
+
POSTGRES_DB: secrets.services.postgres.database ?? "app",
|
|
57
|
+
POSTGRES_HOST: secrets.services.postgres.host,
|
|
58
|
+
POSTGRES_PORT: String(secrets.services.postgres.port)
|
|
59
|
+
},
|
|
60
|
+
...secrets.services.redis && {
|
|
61
|
+
REDIS_PASSWORD: secrets.services.redis.password,
|
|
62
|
+
REDIS_HOST: secrets.services.redis.host,
|
|
63
|
+
REDIS_PORT: String(secrets.services.redis.port)
|
|
64
|
+
},
|
|
65
|
+
...secrets.services.rabbitmq && {
|
|
66
|
+
RABBITMQ_USER: secrets.services.rabbitmq.username,
|
|
67
|
+
RABBITMQ_PASSWORD: secrets.services.rabbitmq.password,
|
|
68
|
+
RABBITMQ_HOST: secrets.services.rabbitmq.host,
|
|
69
|
+
RABBITMQ_PORT: String(secrets.services.rabbitmq.port),
|
|
70
|
+
RABBITMQ_VHOST: secrets.services.rabbitmq.vhost ?? "/"
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Update a custom secret in the secrets file.
|
|
76
|
+
*/
|
|
77
|
+
async function setCustomSecret(stage, key, value, cwd = process.cwd()) {
|
|
78
|
+
const secrets = await readStageSecrets(stage, cwd);
|
|
79
|
+
if (!secrets) throw new Error(`Secrets not found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
80
|
+
const updated = {
|
|
81
|
+
...secrets,
|
|
82
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
83
|
+
custom: {
|
|
84
|
+
...secrets.custom,
|
|
85
|
+
[key]: value
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
await writeStageSecrets(updated, cwd);
|
|
89
|
+
return updated;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Mask a password for display (show first 4 and last 2 chars).
|
|
93
|
+
*/
|
|
94
|
+
function maskPassword(password) {
|
|
95
|
+
if (password.length <= 8) return "********";
|
|
96
|
+
return `${password.slice(0, 4)}${"*".repeat(password.length - 6)}${password.slice(-2)}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
//#endregion
|
|
100
|
+
export { getSecretsDir, getSecretsPath, maskPassword, readStageSecrets, secretsExist, setCustomSecret, toEmbeddableSecrets, writeStageSecrets };
|
|
101
|
+
//# sourceMappingURL=storage-C9PU_30f.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-C9PU_30f.mjs","names":["stage: string","secrets: StageSecrets","key: string","value: string","updated: StageSecrets","password: string"],"sources":["../src/secrets/storage.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { EmbeddableSecrets, StageSecrets } from './types';\n\n/** Default secrets directory relative to project root */\nconst SECRETS_DIR = '.gkm/secrets';\n\n/**\n * Get the secrets directory path.\n */\nexport function getSecretsDir(cwd = process.cwd()): string {\n\treturn join(cwd, SECRETS_DIR);\n}\n\n/**\n * Get the secrets file path for a stage.\n */\nexport function getSecretsPath(stage: string, cwd = process.cwd()): string {\n\treturn join(getSecretsDir(cwd), `${stage}.json`);\n}\n\n/**\n * Check if secrets exist for a stage.\n */\nexport function secretsExist(stage: string, cwd = process.cwd()): boolean {\n\treturn existsSync(getSecretsPath(stage, cwd));\n}\n\n/**\n * Read secrets for a stage.\n * @returns StageSecrets or null if not found\n */\nexport async function readStageSecrets(\n\tstage: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets | null> {\n\tconst path = getSecretsPath(stage, cwd);\n\n\tif (!existsSync(path)) {\n\t\treturn null;\n\t}\n\n\tconst content = await readFile(path, 'utf-8');\n\treturn JSON.parse(content) as StageSecrets;\n}\n\n/**\n * Write secrets for a stage.\n */\nexport async function writeStageSecrets(\n\tsecrets: StageSecrets,\n\tcwd = process.cwd(),\n): Promise<void> {\n\tconst dir = getSecretsDir(cwd);\n\tconst path = getSecretsPath(secrets.stage, cwd);\n\n\t// Ensure directory exists\n\tawait mkdir(dir, { recursive: true });\n\n\t// Write with pretty formatting\n\tawait writeFile(path, JSON.stringify(secrets, null, 2), 'utf-8');\n}\n\n/**\n * Convert StageSecrets to embeddable format (flat key-value pairs).\n * This is what gets encrypted and embedded in the bundle.\n */\nexport function toEmbeddableSecrets(secrets: StageSecrets): EmbeddableSecrets {\n\treturn {\n\t\t...secrets.urls,\n\t\t...secrets.custom,\n\t\t// Also include individual service credentials if needed\n\t\t...(secrets.services.postgres && {\n\t\t\tPOSTGRES_USER: secrets.services.postgres.username,\n\t\t\tPOSTGRES_PASSWORD: secrets.services.postgres.password,\n\t\t\tPOSTGRES_DB: secrets.services.postgres.database ?? 'app',\n\t\t\tPOSTGRES_HOST: secrets.services.postgres.host,\n\t\t\tPOSTGRES_PORT: String(secrets.services.postgres.port),\n\t\t}),\n\t\t...(secrets.services.redis && {\n\t\t\tREDIS_PASSWORD: secrets.services.redis.password,\n\t\t\tREDIS_HOST: secrets.services.redis.host,\n\t\t\tREDIS_PORT: String(secrets.services.redis.port),\n\t\t}),\n\t\t...(secrets.services.rabbitmq && {\n\t\t\tRABBITMQ_USER: secrets.services.rabbitmq.username,\n\t\t\tRABBITMQ_PASSWORD: secrets.services.rabbitmq.password,\n\t\t\tRABBITMQ_HOST: secrets.services.rabbitmq.host,\n\t\t\tRABBITMQ_PORT: String(secrets.services.rabbitmq.port),\n\t\t\tRABBITMQ_VHOST: secrets.services.rabbitmq.vhost ?? '/',\n\t\t}),\n\t};\n}\n\n/**\n * Update a custom secret in the secrets file.\n */\nexport async function setCustomSecret(\n\tstage: string,\n\tkey: string,\n\tvalue: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets> {\n\tconst secrets = await readStageSecrets(stage, cwd);\n\n\tif (!secrets) {\n\t\tthrow new Error(\n\t\t\t`Secrets not found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t}\n\n\tconst updated: StageSecrets = {\n\t\t...secrets,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tcustom: {\n\t\t\t...secrets.custom,\n\t\t\t[key]: value,\n\t\t},\n\t};\n\n\tawait writeStageSecrets(updated, cwd);\n\treturn updated;\n}\n\n/**\n * Mask a password for display (show first 4 and last 2 chars).\n */\nexport function maskPassword(password: string): string {\n\tif (password.length <= 8) {\n\t\treturn '********';\n\t}\n\treturn `${password.slice(0, 4)}${'*'.repeat(password.length - 6)}${password.slice(-2)}`;\n}\n"],"mappings":";;;;;;AAMA,MAAM,cAAc;;;;AAKpB,SAAgB,cAAc,MAAM,QAAQ,KAAK,EAAU;AAC1D,QAAO,KAAK,KAAK,YAAY;AAC7B;;;;AAKD,SAAgB,eAAeA,OAAe,MAAM,QAAQ,KAAK,EAAU;AAC1E,QAAO,KAAK,cAAc,IAAI,GAAG,EAAE,MAAM,OAAO;AAChD;;;;AAKD,SAAgB,aAAaA,OAAe,MAAM,QAAQ,KAAK,EAAW;AACzE,QAAO,WAAW,eAAe,OAAO,IAAI,CAAC;AAC7C;;;;;AAMD,eAAsB,iBACrBA,OACA,MAAM,QAAQ,KAAK,EACY;CAC/B,MAAM,OAAO,eAAe,OAAO,IAAI;AAEvC,MAAK,WAAW,KAAK,CACpB,QAAO;CAGR,MAAM,UAAU,MAAM,SAAS,MAAM,QAAQ;AAC7C,QAAO,KAAK,MAAM,QAAQ;AAC1B;;;;AAKD,eAAsB,kBACrBC,SACA,MAAM,QAAQ,KAAK,EACH;CAChB,MAAM,MAAM,cAAc,IAAI;CAC9B,MAAM,OAAO,eAAe,QAAQ,OAAO,IAAI;AAG/C,OAAM,MAAM,KAAK,EAAE,WAAW,KAAM,EAAC;AAGrC,OAAM,UAAU,MAAM,KAAK,UAAU,SAAS,MAAM,EAAE,EAAE,QAAQ;AAChE;;;;;AAMD,SAAgB,oBAAoBA,SAA0C;AAC7E,QAAO;EACN,GAAG,QAAQ;EACX,GAAG,QAAQ;EAEX,GAAI,QAAQ,SAAS,YAAY;GAChC,eAAe,QAAQ,SAAS,SAAS;GACzC,mBAAmB,QAAQ,SAAS,SAAS;GAC7C,aAAa,QAAQ,SAAS,SAAS,YAAY;GACnD,eAAe,QAAQ,SAAS,SAAS;GACzC,eAAe,OAAO,QAAQ,SAAS,SAAS,KAAK;EACrD;EACD,GAAI,QAAQ,SAAS,SAAS;GAC7B,gBAAgB,QAAQ,SAAS,MAAM;GACvC,YAAY,QAAQ,SAAS,MAAM;GACnC,YAAY,OAAO,QAAQ,SAAS,MAAM,KAAK;EAC/C;EACD,GAAI,QAAQ,SAAS,YAAY;GAChC,eAAe,QAAQ,SAAS,SAAS;GACzC,mBAAmB,QAAQ,SAAS,SAAS;GAC7C,eAAe,QAAQ,SAAS,SAAS;GACzC,eAAe,OAAO,QAAQ,SAAS,SAAS,KAAK;GACrD,gBAAgB,QAAQ,SAAS,SAAS,SAAS;EACnD;CACD;AACD;;;;AAKD,eAAsB,gBACrBD,OACAE,KACAC,OACA,MAAM,QAAQ,KAAK,EACK;CACxB,MAAM,UAAU,MAAM,iBAAiB,OAAO,IAAI;AAElD,MAAK,QACJ,OAAM,IAAI,OACR,+BAA+B,MAAM,mCAAmC,MAAM;CAIjF,MAAMC,UAAwB;EAC7B,GAAG;EACH,WAAW,qBAAI,QAAO,aAAa;EACnC,QAAQ;GACP,GAAG,QAAQ;IACV,MAAM;EACP;CACD;AAED,OAAM,kBAAkB,SAAS,IAAI;AACrC,QAAO;AACP;;;;AAKD,SAAgB,aAAaC,UAA0B;AACtD,KAAI,SAAS,UAAU,EACtB,QAAO;AAER,SAAQ,EAAE,SAAS,MAAM,GAAG,EAAE,CAAC,EAAE,IAAI,OAAO,SAAS,SAAS,EAAE,CAAC,EAAE,SAAS,MAAM,GAAG,CAAC;AACtF"}
|
|
@@ -7,9 +7,81 @@ interface ProviderConfig {
|
|
|
7
7
|
}
|
|
8
8
|
interface AWSApiGatewayConfig extends ProviderConfig {}
|
|
9
9
|
interface AWSLambdaConfig extends ProviderConfig {}
|
|
10
|
+
interface ProductionConfig {
|
|
11
|
+
/** Enable production mode (default: false) */
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
/** Bundle server into single file (default: true) */
|
|
14
|
+
bundle?: boolean;
|
|
15
|
+
/** Minify bundled output (default: true) */
|
|
16
|
+
minify?: boolean;
|
|
17
|
+
/** Health check endpoint path (default: '/health') */
|
|
18
|
+
healthCheck?: string;
|
|
19
|
+
/** Enable graceful shutdown handling (default: true) */
|
|
20
|
+
gracefulShutdown?: boolean;
|
|
21
|
+
/** Packages to exclude from bundling (default: []) */
|
|
22
|
+
external?: string[];
|
|
23
|
+
/** Include subscribers in production build (default: 'exclude' for serverless) */
|
|
24
|
+
subscribers?: 'include' | 'exclude';
|
|
25
|
+
/** Include OpenAPI spec in production (default: false) */
|
|
26
|
+
openapi?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Enable build-time optimized handler generation (default: true)
|
|
29
|
+
* Generates specialized handlers based on endpoint tier:
|
|
30
|
+
* - minimal: Near-raw-Hono performance for simple endpoints
|
|
31
|
+
* - standard: Optimized handlers for auth/services
|
|
32
|
+
* - full: Uses HonoEndpoint.addRoutes for complex endpoints
|
|
33
|
+
*/
|
|
34
|
+
optimizedHandlers?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/** Service-specific configuration for docker-compose */
|
|
37
|
+
interface ServiceConfig {
|
|
38
|
+
/**
|
|
39
|
+
* Full Docker image reference (e.g., 'postgis/postgis:16-3.4-alpine').
|
|
40
|
+
* When specified, overrides the default image entirely.
|
|
41
|
+
*/
|
|
42
|
+
image?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Docker image version/tag (e.g., '15-alpine' for postgres).
|
|
45
|
+
* Only used when `image` is not specified.
|
|
46
|
+
*/
|
|
47
|
+
version?: string;
|
|
48
|
+
}
|
|
49
|
+
/** Supported docker-compose service names */
|
|
50
|
+
type ComposeServiceName = 'postgres' | 'redis' | 'rabbitmq';
|
|
51
|
+
/** Services configuration - can be boolean (use defaults) or object with version */
|
|
52
|
+
type ComposeServicesConfig = { [K in ComposeServiceName]?: boolean | ServiceConfig };
|
|
53
|
+
interface DockerConfig {
|
|
54
|
+
/** Container registry URL (e.g., 'ghcr.io/myorg') */
|
|
55
|
+
registry?: string;
|
|
56
|
+
/** Docker image name (default: derived from package.json name) */
|
|
57
|
+
imageName?: string;
|
|
58
|
+
/** Base Docker image (default: 'node:22-alpine') */
|
|
59
|
+
baseImage?: string;
|
|
60
|
+
/** Container port (default: 3000) */
|
|
61
|
+
port?: number;
|
|
62
|
+
/** docker-compose services to include */
|
|
63
|
+
compose?: {
|
|
64
|
+
/**
|
|
65
|
+
* Services to include in docker-compose.
|
|
66
|
+
* Can be an object with service configs or an array of service names (legacy).
|
|
67
|
+
*
|
|
68
|
+
* @example Object format (recommended)
|
|
69
|
+
* services: {
|
|
70
|
+
* postgres: { version: '15-alpine' },
|
|
71
|
+
* redis: true, // use default version
|
|
72
|
+
* }
|
|
73
|
+
*
|
|
74
|
+
* @example Array format (legacy, uses default versions)
|
|
75
|
+
* services: ['postgres', 'redis']
|
|
76
|
+
*/
|
|
77
|
+
services?: ComposeServicesConfig | ComposeServiceName[];
|
|
78
|
+
};
|
|
79
|
+
}
|
|
10
80
|
interface ServerConfig extends ProviderConfig {
|
|
11
81
|
enableOpenApi?: boolean;
|
|
12
82
|
port?: number;
|
|
83
|
+
/** Production build configuration */
|
|
84
|
+
production?: ProductionConfig;
|
|
13
85
|
}
|
|
14
86
|
type Runtime = 'node' | 'bun';
|
|
15
87
|
interface TelescopeConfig {
|
|
@@ -73,6 +145,17 @@ interface HooksConfig {
|
|
|
73
145
|
*/
|
|
74
146
|
server?: string;
|
|
75
147
|
}
|
|
148
|
+
/** Dokploy deployment configuration */
|
|
149
|
+
interface DokployProviderConfig {
|
|
150
|
+
/** Dokploy API endpoint (e.g., 'https://dokploy.example.com') */
|
|
151
|
+
endpoint: string;
|
|
152
|
+
/** Project ID in Dokploy */
|
|
153
|
+
projectId: string;
|
|
154
|
+
/** Application ID in Dokploy */
|
|
155
|
+
applicationId: string;
|
|
156
|
+
/** Container registry (overrides docker.registry if set) */
|
|
157
|
+
registry?: string;
|
|
158
|
+
}
|
|
76
159
|
interface ProvidersConfig {
|
|
77
160
|
aws?: {
|
|
78
161
|
apiGateway?: {
|
|
@@ -85,6 +168,8 @@ interface ProvidersConfig {
|
|
|
85
168
|
};
|
|
86
169
|
};
|
|
87
170
|
server?: boolean | ServerConfig;
|
|
171
|
+
/** Dokploy deployment configuration */
|
|
172
|
+
dokploy?: boolean | DokployProviderConfig;
|
|
88
173
|
}
|
|
89
174
|
interface GkmConfig {
|
|
90
175
|
routes: Routes;
|
|
@@ -157,7 +242,21 @@ interface GkmConfig {
|
|
|
157
242
|
* env: ['.env', '.env.local']
|
|
158
243
|
*/
|
|
159
244
|
env?: string | string[];
|
|
245
|
+
/**
|
|
246
|
+
* Docker deployment configuration.
|
|
247
|
+
* Used by `gkm docker` and `gkm prepack` commands.
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* docker: {
|
|
251
|
+
* registry: 'ghcr.io/myorg',
|
|
252
|
+
* imageName: 'my-api',
|
|
253
|
+
* compose: {
|
|
254
|
+
* services: ['postgres', 'redis']
|
|
255
|
+
* }
|
|
256
|
+
* }
|
|
257
|
+
*/
|
|
258
|
+
docker?: DockerConfig;
|
|
160
259
|
}
|
|
161
260
|
//#endregion
|
|
162
261
|
export { GkmConfig, OpenApiConfig };
|
|
163
|
-
//# sourceMappingURL=types-
|
|
262
|
+
//# sourceMappingURL=types-BR0M2v_c.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-BR0M2v_c.d.mts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;AASiB,KAFL,MAAA,GAEmB,MAAA,GAAA,MAAA,EAAA;AAKd,UALA,cAAA,CAKoB;EAIpB,OAAA,CAAA,EAAA,OAAA;EAIA,SAAA,CAAA,EAAA,MAAA;AA4BjB;AAcY,UAlDK,mBAAA,SAA4B,cAkDf,CAAA,CAG9B;AAAiC,UAjDhB,eAAA,SAAwB,cAiDR,CAAA;AACM,UA9CtB,gBAAA,CA8CsB;EAAa;EAGnC,OAAA,CAAA,EAAA,OAAY;EAAA;EAAA,MAwBhB,CAAA,EAAA,OAAA;EAAqB;EAAqB,MAAA,CAAA,EAAA,OAAA;EAItC;EAAa,WAAA,CAAA,EAAA,MAAA;EAAA;EAIA,gBAJQ,CAAA,EAAA,OAAA;EAAc;EAOxC,QAAA,CAAA,EAAO,MAAA,EAAA;EAEF;EAiBA,WAAA,CAAA,EAAA,SAAY,GAAA,SAAA;EASZ;EAWA,OAAA,CAAA,EAAA,OAAW;EA6BX;AAWjB;;;;;;EAQoC,iBAGhB,CAAA,EAAA,OAAA;;AAEsB;AAGzB,UAvJA,aAAA,CAuJS;EAAA;;;;EAGX,KACA,CAAA,EAAA,MAAA;EAAM;;;;EA+BoB,OAmBpB,CAAA,EAAA,MAAA;;;AA8BC,KA7NV,kBAAA,GA6NU,UAAA,GAAA,OAAA,GAAA,UAAA;;KA1NV,qBAAA,WACL,gCAAgC;UAGtB,YAAA;;;;;;;;;;;;;;;;;;;;;;;;eAwBJ,wBAAwB;;;UAIpB,YAAA,SAAqB;;;;eAIxB;;KAGF,OAAA;UAEK,eAAA;;;;;;;;;;;;;;;;UAiBA,YAAA;;;;;;;;UASA,aAAA;;;;;;;;;;UAWA,WAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA6BA,qBAAA;;;;;;;;;;UAWA,eAAA;;;qBAGC;qBACA;;;4BAGO;wBACJ;;;qBAGD;;sBAEC;;UAGJ,SAAA;UACR;cACI;UACJ;gBACM;;;cAGF;;;;;;;;;;UAUJ;;;;;;;;iCAQuB;;;;;;;;;;8BAUH;;;;;;;;;;;;;;;;;;;sBAmBR;;YAEV;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA4BD"}
|
|
@@ -7,9 +7,81 @@ interface ProviderConfig {
|
|
|
7
7
|
}
|
|
8
8
|
interface AWSApiGatewayConfig extends ProviderConfig {}
|
|
9
9
|
interface AWSLambdaConfig extends ProviderConfig {}
|
|
10
|
+
interface ProductionConfig {
|
|
11
|
+
/** Enable production mode (default: false) */
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
/** Bundle server into single file (default: true) */
|
|
14
|
+
bundle?: boolean;
|
|
15
|
+
/** Minify bundled output (default: true) */
|
|
16
|
+
minify?: boolean;
|
|
17
|
+
/** Health check endpoint path (default: '/health') */
|
|
18
|
+
healthCheck?: string;
|
|
19
|
+
/** Enable graceful shutdown handling (default: true) */
|
|
20
|
+
gracefulShutdown?: boolean;
|
|
21
|
+
/** Packages to exclude from bundling (default: []) */
|
|
22
|
+
external?: string[];
|
|
23
|
+
/** Include subscribers in production build (default: 'exclude' for serverless) */
|
|
24
|
+
subscribers?: 'include' | 'exclude';
|
|
25
|
+
/** Include OpenAPI spec in production (default: false) */
|
|
26
|
+
openapi?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Enable build-time optimized handler generation (default: true)
|
|
29
|
+
* Generates specialized handlers based on endpoint tier:
|
|
30
|
+
* - minimal: Near-raw-Hono performance for simple endpoints
|
|
31
|
+
* - standard: Optimized handlers for auth/services
|
|
32
|
+
* - full: Uses HonoEndpoint.addRoutes for complex endpoints
|
|
33
|
+
*/
|
|
34
|
+
optimizedHandlers?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/** Service-specific configuration for docker-compose */
|
|
37
|
+
interface ServiceConfig {
|
|
38
|
+
/**
|
|
39
|
+
* Full Docker image reference (e.g., 'postgis/postgis:16-3.4-alpine').
|
|
40
|
+
* When specified, overrides the default image entirely.
|
|
41
|
+
*/
|
|
42
|
+
image?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Docker image version/tag (e.g., '15-alpine' for postgres).
|
|
45
|
+
* Only used when `image` is not specified.
|
|
46
|
+
*/
|
|
47
|
+
version?: string;
|
|
48
|
+
}
|
|
49
|
+
/** Supported docker-compose service names */
|
|
50
|
+
type ComposeServiceName = 'postgres' | 'redis' | 'rabbitmq';
|
|
51
|
+
/** Services configuration - can be boolean (use defaults) or object with version */
|
|
52
|
+
type ComposeServicesConfig = { [K in ComposeServiceName]?: boolean | ServiceConfig };
|
|
53
|
+
interface DockerConfig {
|
|
54
|
+
/** Container registry URL (e.g., 'ghcr.io/myorg') */
|
|
55
|
+
registry?: string;
|
|
56
|
+
/** Docker image name (default: derived from package.json name) */
|
|
57
|
+
imageName?: string;
|
|
58
|
+
/** Base Docker image (default: 'node:22-alpine') */
|
|
59
|
+
baseImage?: string;
|
|
60
|
+
/** Container port (default: 3000) */
|
|
61
|
+
port?: number;
|
|
62
|
+
/** docker-compose services to include */
|
|
63
|
+
compose?: {
|
|
64
|
+
/**
|
|
65
|
+
* Services to include in docker-compose.
|
|
66
|
+
* Can be an object with service configs or an array of service names (legacy).
|
|
67
|
+
*
|
|
68
|
+
* @example Object format (recommended)
|
|
69
|
+
* services: {
|
|
70
|
+
* postgres: { version: '15-alpine' },
|
|
71
|
+
* redis: true, // use default version
|
|
72
|
+
* }
|
|
73
|
+
*
|
|
74
|
+
* @example Array format (legacy, uses default versions)
|
|
75
|
+
* services: ['postgres', 'redis']
|
|
76
|
+
*/
|
|
77
|
+
services?: ComposeServicesConfig | ComposeServiceName[];
|
|
78
|
+
};
|
|
79
|
+
}
|
|
10
80
|
interface ServerConfig extends ProviderConfig {
|
|
11
81
|
enableOpenApi?: boolean;
|
|
12
82
|
port?: number;
|
|
83
|
+
/** Production build configuration */
|
|
84
|
+
production?: ProductionConfig;
|
|
13
85
|
}
|
|
14
86
|
type Runtime = 'node' | 'bun';
|
|
15
87
|
interface TelescopeConfig {
|
|
@@ -73,6 +145,17 @@ interface HooksConfig {
|
|
|
73
145
|
*/
|
|
74
146
|
server?: string;
|
|
75
147
|
}
|
|
148
|
+
/** Dokploy deployment configuration */
|
|
149
|
+
interface DokployProviderConfig {
|
|
150
|
+
/** Dokploy API endpoint (e.g., 'https://dokploy.example.com') */
|
|
151
|
+
endpoint: string;
|
|
152
|
+
/** Project ID in Dokploy */
|
|
153
|
+
projectId: string;
|
|
154
|
+
/** Application ID in Dokploy */
|
|
155
|
+
applicationId: string;
|
|
156
|
+
/** Container registry (overrides docker.registry if set) */
|
|
157
|
+
registry?: string;
|
|
158
|
+
}
|
|
76
159
|
interface ProvidersConfig {
|
|
77
160
|
aws?: {
|
|
78
161
|
apiGateway?: {
|
|
@@ -85,6 +168,8 @@ interface ProvidersConfig {
|
|
|
85
168
|
};
|
|
86
169
|
};
|
|
87
170
|
server?: boolean | ServerConfig;
|
|
171
|
+
/** Dokploy deployment configuration */
|
|
172
|
+
dokploy?: boolean | DokployProviderConfig;
|
|
88
173
|
}
|
|
89
174
|
interface GkmConfig {
|
|
90
175
|
routes: Routes;
|
|
@@ -157,7 +242,21 @@ interface GkmConfig {
|
|
|
157
242
|
* env: ['.env', '.env.local']
|
|
158
243
|
*/
|
|
159
244
|
env?: string | string[];
|
|
245
|
+
/**
|
|
246
|
+
* Docker deployment configuration.
|
|
247
|
+
* Used by `gkm docker` and `gkm prepack` commands.
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* docker: {
|
|
251
|
+
* registry: 'ghcr.io/myorg',
|
|
252
|
+
* imageName: 'my-api',
|
|
253
|
+
* compose: {
|
|
254
|
+
* services: ['postgres', 'redis']
|
|
255
|
+
* }
|
|
256
|
+
* }
|
|
257
|
+
*/
|
|
258
|
+
docker?: DockerConfig;
|
|
160
259
|
}
|
|
161
260
|
//#endregion
|
|
162
261
|
export { GkmConfig, OpenApiConfig };
|
|
163
|
-
//# sourceMappingURL=types-
|
|
262
|
+
//# sourceMappingURL=types-BhkZc-vm.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-BhkZc-vm.d.cts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;AASiB,KAFL,MAAA,GAEmB,MAAA,GAAA,MAAA,EAAA;AAKd,UALA,cAAA,CAKoB;EAIpB,OAAA,CAAA,EAAA,OAAA;EAIA,SAAA,CAAA,EAAA,MAAA;AA4BjB;AAcY,UAlDK,mBAAA,SAA4B,cAkDf,CAAA,CAG9B;AAAiC,UAjDhB,eAAA,SAAwB,cAiDR,CAAA;AACM,UA9CtB,gBAAA,CA8CsB;EAAa;EAGnC,OAAA,CAAA,EAAA,OAAY;EAAA;EAAA,MAwBhB,CAAA,EAAA,OAAA;EAAqB;EAAqB,MAAA,CAAA,EAAA,OAAA;EAItC;EAAa,WAAA,CAAA,EAAA,MAAA;EAAA;EAIA,gBAJQ,CAAA,EAAA,OAAA;EAAc;EAOxC,QAAA,CAAA,EAAO,MAAA,EAAA;EAEF;EAiBA,WAAA,CAAA,EAAA,SAAY,GAAA,SAAA;EASZ;EAWA,OAAA,CAAA,EAAA,OAAW;EA6BX;AAWjB;;;;;;EAQoC,iBAGhB,CAAA,EAAA,OAAA;;AAEsB;AAGzB,UAvJA,aAAA,CAuJS;EAAA;;;;EAGX,KACA,CAAA,EAAA,MAAA;EAAM;;;;EA+BoB,OAmBpB,CAAA,EAAA,MAAA;;;AA8BC,KA7NV,kBAAA,GA6NU,UAAA,GAAA,OAAA,GAAA,UAAA;;KA1NV,qBAAA,WACL,gCAAgC;UAGtB,YAAA;;;;;;;;;;;;;;;;;;;;;;;;eAwBJ,wBAAwB;;;UAIpB,YAAA,SAAqB;;;;eAIxB;;KAGF,OAAA;UAEK,eAAA;;;;;;;;;;;;;;;;UAiBA,YAAA;;;;;;;;UASA,aAAA;;;;;;;;;;UAWA,WAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA6BA,qBAAA;;;;;;;;;;UAWA,eAAA;;;qBAGC;qBACA;;;4BAGO;wBACJ;;;qBAGD;;sBAEC;;UAGJ,SAAA;UACR;cACI;UACJ;gBACM;;;cAGF;;;;;;;;;;UAUJ;;;;;;;;iCAQuB;;;;;;;;;;8BAUH;;;;;;;;;;;;;;;;;;;sBAmBR;;YAEV;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA4BD"}
|
package/examples/cron-example.ts
CHANGED
|
@@ -4,42 +4,42 @@ import { cron } from '@geekmidas/constructs/crons';
|
|
|
4
4
|
* Example cron that generates a daily report at 9 AM UTC
|
|
5
5
|
*/
|
|
6
6
|
export const dailyReport = cron
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
.schedule('cron(0 9 * * ? *)')
|
|
8
|
+
.timeout(600000) // 10 minutes
|
|
9
|
+
.handle(async ({ services, logger }) => {
|
|
10
|
+
logger.info('Generating daily report');
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
const reportDate = new Date().toISOString().split('T')[0];
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
// Generate report logic here
|
|
15
|
+
const reportData = {
|
|
16
|
+
date: reportDate,
|
|
17
|
+
totalOrders: 150,
|
|
18
|
+
totalRevenue: 12500.0,
|
|
19
|
+
topProducts: [
|
|
20
|
+
{ id: 'prod-1', name: 'Widget A', sales: 45 },
|
|
21
|
+
{ id: 'prod-2', name: 'Widget B', sales: 32 },
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
logger.info('Daily report generated', reportData);
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
return reportData;
|
|
28
|
+
});
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Example cron that runs every hour
|
|
32
32
|
*/
|
|
33
33
|
export const hourlyCleanup = cron
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
.schedule('rate(1 hour)')
|
|
35
|
+
.timeout(300000) // 5 minutes
|
|
36
|
+
.handle(async ({ services, logger }) => {
|
|
37
|
+
logger.info('Running hourly cleanup');
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
// Cleanup logic here
|
|
40
|
+
const itemsCleaned = 42;
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
logger.info(`Cleaned ${itemsCleaned} items`);
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
return { itemsCleaned };
|
|
45
|
+
});
|