@geekmidas/cli 10.0.0-alpha.58 → 10.0.0-alpha.59
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/dist/{HostingerProvider-cL-6oQ5r.mjs → HostingerProvider-YQY3rMpV.mjs} +2 -2
- package/dist/{HostingerProvider-cL-6oQ5r.mjs.map → HostingerProvider-YQY3rMpV.mjs.map} +1 -1
- package/dist/{LegacyStateStore-ByRWQ9s1.mjs → LegacyStateStore-CB4ch3Dl.mjs} +2 -2
- package/dist/{LegacyStateStore-ByRWQ9s1.mjs.map → LegacyStateStore-CB4ch3Dl.mjs.map} +1 -1
- package/dist/{LegacyStateStore-C_OFs1gN.cjs → LegacyStateStore-CQJqvmXw.cjs} +7 -7
- package/dist/{LegacyStateStore-C_OFs1gN.cjs.map → LegacyStateStore-CQJqvmXw.cjs.map} +1 -1
- package/dist/{S3StateStore-FhzyENrN.mjs → S3StateStore-CXTenDTW.mjs} +2 -2
- package/dist/{S3StateStore-FhzyENrN.mjs.map → S3StateStore-CXTenDTW.mjs.map} +1 -1
- package/dist/{S3StateStore-C7eBae0G.cjs → S3StateStore-DLS4rEc0.cjs} +5 -5
- package/dist/{S3StateStore-C7eBae0G.cjs.map → S3StateStore-DLS4rEc0.cjs.map} +1 -1
- package/dist/{SSMStateStore-DC43R7zG.cjs → SSMStateStore-EqsTBIOs.cjs} +9 -9
- package/dist/{SSMStateStore-DC43R7zG.cjs.map → SSMStateStore-EqsTBIOs.cjs.map} +1 -1
- package/dist/{SSMStateStore-BjZZm-nV.mjs → SSMStateStore-hnP5deFJ.mjs} +2 -2
- package/dist/{SSMStateStore-BjZZm-nV.mjs.map → SSMStateStore-hnP5deFJ.mjs.map} +1 -1
- package/dist/StateStore-B4u0wx2t.cjs +412 -0
- package/dist/StateStore-B4u0wx2t.cjs.map +1 -0
- package/dist/StateStore-CYUfGYcx.mjs +359 -0
- package/dist/StateStore-CYUfGYcx.mjs.map +1 -0
- package/dist/{bundler-URImKj-A.mjs → bundler-ouDdNvk9.mjs} +2 -2
- package/dist/{bundler-URImKj-A.mjs.map → bundler-ouDdNvk9.mjs.map} +1 -1
- package/dist/{config-CSOK73pY.mjs → config-EuzFEkcH.mjs} +2 -2
- package/dist/config-EuzFEkcH.mjs.map +1 -0
- package/dist/{config-DdIu1O_I.cjs → config-WMOB9bLL.cjs} +2 -2
- package/dist/config-WMOB9bLL.cjs.map +1 -0
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +65 -3
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts +65 -3
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +2 -2
- package/dist/{encryption-BspqzZIb.cjs → encryption-BP8zfxEJ.cjs} +3 -3
- package/dist/{encryption-BspqzZIb.cjs.map → encryption-BP8zfxEJ.cjs.map} +1 -1
- package/dist/{encryption-Dlnl5XEU.mjs → encryption-BW7Gh51G.mjs} +3 -3
- package/dist/{encryption-Dlnl5XEU.mjs.map → encryption-BW7Gh51G.mjs.map} +1 -1
- package/dist/{github-mQorSS36.cjs → github-DHpDMBT7.cjs} +3 -3
- package/dist/{github-mQorSS36.cjs.map → github-DHpDMBT7.cjs.map} +1 -1
- package/dist/{github-BdF9tsvl.mjs → github-T_6sK3Rz.mjs} +3 -3
- package/dist/{github-BdF9tsvl.mjs.map → github-T_6sK3Rz.mjs.map} +1 -1
- package/dist/{index-BorYE_uO.d.cts → index-B5TJ-mB6.d.mts} +2 -2
- package/dist/{index-BorYE_uO.d.cts.map → index-B5TJ-mB6.d.mts.map} +1 -1
- package/dist/{index-C3JmluyO.d.mts → index-DV6j4Ydq.d.cts} +2 -2
- package/dist/{index-C3JmluyO.d.mts.map → index-DV6j4Ydq.d.cts.map} +1 -1
- package/dist/index.cjs +1210 -701
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1215 -704
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-CXVnh1J3.mjs → migrate-BrFREmgQ.mjs} +4 -4
- package/dist/{migrate-CXVnh1J3.mjs.map → migrate-BrFREmgQ.mjs.map} +1 -1
- package/dist/{migrate-DzYqA-e3.cjs → migrate-CThJfVHR.cjs} +4 -4
- package/dist/{migrate-DzYqA-e3.cjs.map → migrate-CThJfVHR.cjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/ready-DbDHneLR.mjs.map +1 -1
- package/dist/ready-mR96orkJ.cjs.map +1 -1
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/{types-D7j6QWeX.d.mts → types-CipIIm8f.d.mts} +34 -2
- package/dist/{types-D7j6QWeX.d.mts.map → types-CipIIm8f.d.mts.map} +1 -1
- package/dist/{types-JV4XOY01.d.cts → types-D9b-IYdW.d.cts} +34 -2
- package/dist/{types-JV4XOY01.d.cts.map → types-D9b-IYdW.d.cts.map} +1 -1
- 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-Bah36xSG.cjs → workspace-B64Xk1Mb.cjs} +14 -4
- package/dist/workspace-B64Xk1Mb.cjs.map +1 -0
- package/dist/{workspace-D-f58jsP.mjs → workspace-BRZ14o-o.mjs} +9 -6
- package/dist/{workspace-D-f58jsP.mjs.map → workspace-BRZ14o-o.mjs.map} +1 -1
- package/dist/{workspace-BvNXAXQI.mjs → workspace-Bp1xVaOz.mjs} +14 -4
- package/dist/workspace-Bp1xVaOz.mjs.map +1 -0
- package/dist/{workspace-B14L2TrY.cjs → workspace-Dbn07ILl.cjs} +7 -4
- package/dist/{workspace-B14L2TrY.cjs.map → workspace-Dbn07ILl.cjs.map} +1 -1
- package/package.json +13 -13
- package/dist/LocalStateProvider-7HR6SS2t.cjs +0 -48
- package/dist/LocalStateProvider-7HR6SS2t.cjs.map +0 -1
- package/dist/LocalStateProvider-D7QyJoLM.mjs +0 -48
- package/dist/LocalStateProvider-D7QyJoLM.mjs.map +0 -1
- package/dist/LocalStateStore-BupdV-VN.mjs +0 -163
- package/dist/LocalStateStore-BupdV-VN.mjs.map +0 -1
- package/dist/LocalStateStore-DDDU0HkI.cjs +0 -163
- package/dist/LocalStateStore-DDDU0HkI.cjs.map +0 -1
- package/dist/SSMStateProvider-B0Hu1MKZ.mjs +0 -75
- package/dist/SSMStateProvider-B0Hu1MKZ.mjs.map +0 -1
- package/dist/SSMStateProvider-VrQ-yeqd.cjs +0 -74
- package/dist/SSMStateProvider-VrQ-yeqd.cjs.map +0 -1
- package/dist/config-CSOK73pY.mjs.map +0 -1
- package/dist/config-DdIu1O_I.cjs.map +0 -1
- package/dist/workspace-Bah36xSG.cjs.map +0 -1
- package/dist/workspace-BvNXAXQI.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geekmidas/cli",
|
|
3
|
-
"version": "10.0.0-alpha.
|
|
3
|
+
"version": "10.0.0-alpha.59",
|
|
4
4
|
"description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -98,15 +98,15 @@
|
|
|
98
98
|
"prompts": "~2.4.2",
|
|
99
99
|
"tsx": "~4.23.15",
|
|
100
100
|
"yaml": "~2.9.1",
|
|
101
|
-
"@geekmidas/cache": "10.0.0-alpha.
|
|
102
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
103
|
-
"@geekmidas/db": "10.0.0-alpha.
|
|
104
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
105
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
106
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
107
|
-
"@geekmidas/manifest": "10.0.0-alpha.
|
|
108
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
109
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
101
|
+
"@geekmidas/cache": "10.0.0-alpha.59",
|
|
102
|
+
"@geekmidas/constructs": "~10.0.0-alpha.59",
|
|
103
|
+
"@geekmidas/db": "10.0.0-alpha.59",
|
|
104
|
+
"@geekmidas/envkit": "~10.0.0-alpha.59",
|
|
105
|
+
"@geekmidas/errors": "~10.0.0-alpha.59",
|
|
106
|
+
"@geekmidas/logger": "~10.0.0-alpha.59",
|
|
107
|
+
"@geekmidas/manifest": "10.0.0-alpha.59",
|
|
108
|
+
"@geekmidas/schema": "~10.0.0-alpha.59",
|
|
109
|
+
"@geekmidas/services": "~10.0.0-alpha.59"
|
|
110
110
|
},
|
|
111
111
|
"devDependencies": {
|
|
112
112
|
"@hono/node-server": "~1.19.5",
|
|
@@ -120,11 +120,11 @@
|
|
|
120
120
|
"typescript": "~7.0.2",
|
|
121
121
|
"vitest": "~5.0.2",
|
|
122
122
|
"zod": "~4.6.5",
|
|
123
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
124
|
-
"@geekmidas/testkit": "10.0.0-alpha.
|
|
123
|
+
"@geekmidas/events": "~10.0.0-alpha.59",
|
|
124
|
+
"@geekmidas/testkit": "10.0.0-alpha.59"
|
|
125
125
|
},
|
|
126
126
|
"peerDependencies": {
|
|
127
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
127
|
+
"@geekmidas/telescope": "~10.0.0-alpha.59"
|
|
128
128
|
},
|
|
129
129
|
"peerDependenciesMeta": {
|
|
130
130
|
"@geekmidas/telescope": {
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
let node_fs_promises = require("node:fs/promises");
|
|
2
|
-
let node_path = require("node:path");
|
|
3
|
-
//#region src/deploy/LocalStateProvider.ts
|
|
4
|
-
/**
|
|
5
|
-
* Local Filesystem State Provider
|
|
6
|
-
*
|
|
7
|
-
* Stores deployment state in .gkm/deploy-{stage}.json files.
|
|
8
|
-
* This is the default provider for local development.
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* Get the state file path for a stage.
|
|
12
|
-
*/
|
|
13
|
-
function getStateFilePath(workspaceRoot, stage) {
|
|
14
|
-
return (0, node_path.join)(workspaceRoot, ".gkm", `deploy-${stage}.json`);
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Local filesystem state provider.
|
|
18
|
-
*
|
|
19
|
-
* Stores state in .gkm/deploy-{stage}.json files in the workspace root.
|
|
20
|
-
*/
|
|
21
|
-
var LocalStateProvider = class {
|
|
22
|
-
workspaceRoot;
|
|
23
|
-
constructor(workspaceRoot) {
|
|
24
|
-
this.workspaceRoot = workspaceRoot;
|
|
25
|
-
}
|
|
26
|
-
async read(stage) {
|
|
27
|
-
const filePath = getStateFilePath(this.workspaceRoot, stage);
|
|
28
|
-
try {
|
|
29
|
-
const content = await (0, node_fs_promises.readFile)(filePath, "utf-8");
|
|
30
|
-
return JSON.parse(content);
|
|
31
|
-
} catch (error) {
|
|
32
|
-
if (error.code === "ENOENT") return null;
|
|
33
|
-
console.warn(`Warning: Could not read deploy state: ${error}`);
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
async write(stage, state) {
|
|
38
|
-
const filePath = getStateFilePath(this.workspaceRoot, stage);
|
|
39
|
-
const dir = (0, node_path.join)(this.workspaceRoot, ".gkm");
|
|
40
|
-
await (0, node_fs_promises.mkdir)(dir, { recursive: true });
|
|
41
|
-
state.lastDeployedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
42
|
-
await (0, node_fs_promises.writeFile)(filePath, JSON.stringify(state, null, 2));
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
//#endregion
|
|
46
|
-
exports.LocalStateProvider = LocalStateProvider;
|
|
47
|
-
|
|
48
|
-
//# sourceMappingURL=LocalStateProvider-7HR6SS2t.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LocalStateProvider-7HR6SS2t.cjs","names":["join","readFile","mkdir","writeFile"],"sources":["../src/deploy/LocalStateProvider.ts"],"sourcesContent":["/**\n * Local Filesystem State Provider\n *\n * Stores deployment state in .gkm/deploy-{stage}.json files.\n * This is the default provider for local development.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { StateProvider } from './StateProvider';\nimport type { DokployStageState } from './state';\n\n/**\n * Get the state file path for a stage.\n */\nfunction getStateFilePath(workspaceRoot: string, stage: string): string {\n\treturn join(workspaceRoot, '.gkm', `deploy-${stage}.json`);\n}\n\n/**\n * Local filesystem state provider.\n *\n * Stores state in .gkm/deploy-{stage}.json files in the workspace root.\n */\nexport class LocalStateProvider implements StateProvider {\n\tconstructor(private readonly workspaceRoot: string) {}\n\n\tasync read(stage: string): Promise<DokployStageState | null> {\n\t\tconst filePath = getStateFilePath(this.workspaceRoot, stage);\n\n\t\ttry {\n\t\t\tconst content = await readFile(filePath, 'utf-8');\n\t\t\treturn JSON.parse(content) as DokployStageState;\n\t\t} catch (error) {\n\t\t\t// File doesn't exist - return null\n\t\t\tif ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t// Log other errors but don't fail\n\t\t\tconsole.warn(`Warning: Could not read deploy state: ${error}`);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tasync write(stage: string, state: DokployStageState): Promise<void> {\n\t\tconst filePath = getStateFilePath(this.workspaceRoot, stage);\n\t\tconst dir = join(this.workspaceRoot, '.gkm');\n\n\t\t// Ensure .gkm directory exists\n\t\tawait mkdir(dir, { recursive: true });\n\n\t\t// Update last deployed timestamp\n\t\tstate.lastDeployedAt = new Date().toISOString();\n\n\t\tawait writeFile(filePath, JSON.stringify(state, null, 2));\n\t}\n}\n"],"mappings":";;;;;;;;;;;;AAeA,SAAS,iBAAiB,eAAuB,OAAuB;CACvE,QAAA,GAAOA,UAAAA,KAAAA,CAAK,eAAe,QAAQ,UAAU,MAAM,MAAM;AAC1D;;;;;;AAOA,IAAa,qBAAb,MAAyD;CAC3B;CAA7B,YAAY,eAAwC;EAAvB,KAAA,gBAAA;CAAwB;CAErD,MAAM,KAAK,OAAkD;EAC5D,MAAM,WAAW,iBAAiB,KAAK,eAAe,KAAK;EAE3D,IAAI;GACH,MAAM,UAAU,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,UAAU,OAAO;GAChD,OAAO,KAAK,MAAM,OAAO;EAC1B,SAAS,OAAO;GAEf,IAAK,MAAgC,SAAS,UAC7C,OAAO;GAGR,QAAQ,KAAK,yCAAyC,OAAO;GAC7D,OAAO;EACR;CACD;CAEA,MAAM,MAAM,OAAe,OAAyC;EACnE,MAAM,WAAW,iBAAiB,KAAK,eAAe,KAAK;EAC3D,MAAM,OAAA,GAAMD,UAAAA,KAAAA,CAAK,KAAK,eAAe,MAAM;EAG3C,OAAA,GAAME,iBAAAA,MAAAA,CAAM,KAAK,EAAE,WAAW,KAAK,CAAC;EAGpC,MAAM,kCAAiB,IAAI,KAAK,EAAA,CAAE,YAAY;EAE9C,OAAA,GAAMC,iBAAAA,UAAAA,CAAU,UAAU,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;CACzD;AACD"}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
//#region src/deploy/LocalStateProvider.ts
|
|
4
|
-
/**
|
|
5
|
-
* Local Filesystem State Provider
|
|
6
|
-
*
|
|
7
|
-
* Stores deployment state in .gkm/deploy-{stage}.json files.
|
|
8
|
-
* This is the default provider for local development.
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* Get the state file path for a stage.
|
|
12
|
-
*/
|
|
13
|
-
function getStateFilePath(workspaceRoot, stage) {
|
|
14
|
-
return join(workspaceRoot, ".gkm", `deploy-${stage}.json`);
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Local filesystem state provider.
|
|
18
|
-
*
|
|
19
|
-
* Stores state in .gkm/deploy-{stage}.json files in the workspace root.
|
|
20
|
-
*/
|
|
21
|
-
var LocalStateProvider = class {
|
|
22
|
-
workspaceRoot;
|
|
23
|
-
constructor(workspaceRoot) {
|
|
24
|
-
this.workspaceRoot = workspaceRoot;
|
|
25
|
-
}
|
|
26
|
-
async read(stage) {
|
|
27
|
-
const filePath = getStateFilePath(this.workspaceRoot, stage);
|
|
28
|
-
try {
|
|
29
|
-
const content = await readFile(filePath, "utf-8");
|
|
30
|
-
return JSON.parse(content);
|
|
31
|
-
} catch (error) {
|
|
32
|
-
if (error.code === "ENOENT") return null;
|
|
33
|
-
console.warn(`Warning: Could not read deploy state: ${error}`);
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
async write(stage, state) {
|
|
38
|
-
const filePath = getStateFilePath(this.workspaceRoot, stage);
|
|
39
|
-
const dir = join(this.workspaceRoot, ".gkm");
|
|
40
|
-
await mkdir(dir, { recursive: true });
|
|
41
|
-
state.lastDeployedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
42
|
-
await writeFile(filePath, JSON.stringify(state, null, 2));
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
//#endregion
|
|
46
|
-
export { LocalStateProvider };
|
|
47
|
-
|
|
48
|
-
//# sourceMappingURL=LocalStateProvider-D7QyJoLM.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LocalStateProvider-D7QyJoLM.mjs","names":[],"sources":["../src/deploy/LocalStateProvider.ts"],"sourcesContent":["/**\n * Local Filesystem State Provider\n *\n * Stores deployment state in .gkm/deploy-{stage}.json files.\n * This is the default provider for local development.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { StateProvider } from './StateProvider';\nimport type { DokployStageState } from './state';\n\n/**\n * Get the state file path for a stage.\n */\nfunction getStateFilePath(workspaceRoot: string, stage: string): string {\n\treturn join(workspaceRoot, '.gkm', `deploy-${stage}.json`);\n}\n\n/**\n * Local filesystem state provider.\n *\n * Stores state in .gkm/deploy-{stage}.json files in the workspace root.\n */\nexport class LocalStateProvider implements StateProvider {\n\tconstructor(private readonly workspaceRoot: string) {}\n\n\tasync read(stage: string): Promise<DokployStageState | null> {\n\t\tconst filePath = getStateFilePath(this.workspaceRoot, stage);\n\n\t\ttry {\n\t\t\tconst content = await readFile(filePath, 'utf-8');\n\t\t\treturn JSON.parse(content) as DokployStageState;\n\t\t} catch (error) {\n\t\t\t// File doesn't exist - return null\n\t\t\tif ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t// Log other errors but don't fail\n\t\t\tconsole.warn(`Warning: Could not read deploy state: ${error}`);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tasync write(stage: string, state: DokployStageState): Promise<void> {\n\t\tconst filePath = getStateFilePath(this.workspaceRoot, stage);\n\t\tconst dir = join(this.workspaceRoot, '.gkm');\n\n\t\t// Ensure .gkm directory exists\n\t\tawait mkdir(dir, { recursive: true });\n\n\t\t// Update last deployed timestamp\n\t\tstate.lastDeployedAt = new Date().toISOString();\n\n\t\tawait writeFile(filePath, JSON.stringify(state, null, 2));\n\t}\n}\n"],"mappings":";;;;;;;;;;;;AAeA,SAAS,iBAAiB,eAAuB,OAAuB;CACvE,OAAO,KAAK,eAAe,QAAQ,UAAU,MAAM,MAAM;AAC1D;;;;;;AAOA,IAAa,qBAAb,MAAyD;CAC3B;CAA7B,YAAY,eAAwC;EAAvB,KAAA,gBAAA;CAAwB;CAErD,MAAM,KAAK,OAAkD;EAC5D,MAAM,WAAW,iBAAiB,KAAK,eAAe,KAAK;EAE3D,IAAI;GACH,MAAM,UAAU,MAAM,SAAS,UAAU,OAAO;GAChD,OAAO,KAAK,MAAM,OAAO;EAC1B,SAAS,OAAO;GAEf,IAAK,MAAgC,SAAS,UAC7C,OAAO;GAGR,QAAQ,KAAK,yCAAyC,OAAO;GAC7D,OAAO;EACR;CACD;CAEA,MAAM,MAAM,OAAe,OAAyC;EACnE,MAAM,WAAW,iBAAiB,KAAK,eAAe,KAAK;EAC3D,MAAM,MAAM,KAAK,KAAK,eAAe,MAAM;EAG3C,MAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;EAGpC,MAAM,kCAAiB,IAAI,KAAK,EAAA,CAAE,YAAY;EAE9C,MAAM,UAAU,UAAU,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;CACzD;AACD"}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { i as DocumentStateStore, l as StateVersionConflict, o as StateLocked, s as StateStoreBusy, u as contentVersion } from "./index.mjs";
|
|
2
|
-
import { mkdir, open, readFile, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { randomUUID } from "node:crypto";
|
|
5
|
-
//#region src/deploy/LocalStateStore.ts
|
|
6
|
-
/**
|
|
7
|
-
* Local filesystem state store.
|
|
8
|
-
*
|
|
9
|
-
* `.gkm/deploy-<stage>.json` holds the state, `.gkm/deploy-<stage>.lock` the
|
|
10
|
-
* lock. Every file is mode 0600: state holds database passwords and IAM keys.
|
|
11
|
-
*/
|
|
12
|
-
const FILE_MODE = 384;
|
|
13
|
-
/** How long a write waits for another process's write to finish. */
|
|
14
|
-
const MUTEX_TIMEOUT_MS = 5e3;
|
|
15
|
-
/**
|
|
16
|
-
* A write holds the mutex for milliseconds, so one this old was left by a
|
|
17
|
-
* process that died mid-write and is safe to take over.
|
|
18
|
-
*/
|
|
19
|
-
const MUTEX_STALE_MS = 3e4;
|
|
20
|
-
function isCode(error, code) {
|
|
21
|
-
return error?.code === code;
|
|
22
|
-
}
|
|
23
|
-
var LocalStateStore = class extends DocumentStateStore {
|
|
24
|
-
workspaceRoot;
|
|
25
|
-
constructor(workspaceRoot) {
|
|
26
|
-
super();
|
|
27
|
-
this.workspaceRoot = workspaceRoot;
|
|
28
|
-
}
|
|
29
|
-
get dir() {
|
|
30
|
-
return join(this.workspaceRoot, ".gkm");
|
|
31
|
-
}
|
|
32
|
-
statePath(stage) {
|
|
33
|
-
return join(this.dir, `deploy-${stage}.json`);
|
|
34
|
-
}
|
|
35
|
-
lockPath(stage) {
|
|
36
|
-
return join(this.dir, `deploy-${stage}.lock`);
|
|
37
|
-
}
|
|
38
|
-
backupPath(stage) {
|
|
39
|
-
return join(this.dir, `deploy-${stage}.v1.json`);
|
|
40
|
-
}
|
|
41
|
-
mutexPath(stage) {
|
|
42
|
-
return join(this.dir, `deploy-${stage}.json.mutex`);
|
|
43
|
-
}
|
|
44
|
-
location(stage) {
|
|
45
|
-
return this.statePath(stage);
|
|
46
|
-
}
|
|
47
|
-
async readRaw(stage) {
|
|
48
|
-
try {
|
|
49
|
-
const body = await readFile(this.statePath(stage), "utf-8");
|
|
50
|
-
return {
|
|
51
|
-
body,
|
|
52
|
-
version: contentVersion(body)
|
|
53
|
-
};
|
|
54
|
-
} catch (error) {
|
|
55
|
-
if (isCode(error, "ENOENT")) return null;
|
|
56
|
-
throw error;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
async writeRaw(stage, body, expectedVersion) {
|
|
60
|
-
await this.ensureDir();
|
|
61
|
-
return this.withMutex(stage, async () => {
|
|
62
|
-
const actual = (await this.readRaw(stage))?.version ?? null;
|
|
63
|
-
if (actual !== expectedVersion) throw new StateVersionConflict(stage, expectedVersion, actual);
|
|
64
|
-
const target = this.statePath(stage);
|
|
65
|
-
const temp = `${target}.${randomUUID()}.tmp`;
|
|
66
|
-
try {
|
|
67
|
-
const handle = await open(temp, "wx", FILE_MODE);
|
|
68
|
-
try {
|
|
69
|
-
await handle.writeFile(body);
|
|
70
|
-
await handle.sync();
|
|
71
|
-
} finally {
|
|
72
|
-
await handle.close();
|
|
73
|
-
}
|
|
74
|
-
await rename(temp, target);
|
|
75
|
-
} catch (error) {
|
|
76
|
-
await rm(temp, { force: true });
|
|
77
|
-
throw error;
|
|
78
|
-
}
|
|
79
|
-
return contentVersion(body);
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
async writeV1Backup(stage, body) {
|
|
83
|
-
try {
|
|
84
|
-
await writeFile(this.backupPath(stage), body, {
|
|
85
|
-
flag: "wx",
|
|
86
|
-
mode: FILE_MODE
|
|
87
|
-
});
|
|
88
|
-
} catch (error) {
|
|
89
|
-
if (!isCode(error, "EEXIST")) throw error;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
async createLock(stage, holder) {
|
|
93
|
-
await this.ensureDir();
|
|
94
|
-
let handle;
|
|
95
|
-
try {
|
|
96
|
-
handle = await open(this.lockPath(stage), "wx", FILE_MODE);
|
|
97
|
-
} catch (error) {
|
|
98
|
-
if (isCode(error, "EEXIST")) throw new StateLocked(stage, await this.readLock(stage), this.lockPath(stage));
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
101
|
-
try {
|
|
102
|
-
await handle.writeFile(JSON.stringify(holder, null, 2));
|
|
103
|
-
} finally {
|
|
104
|
-
await handle.close();
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
async readLock(stage) {
|
|
108
|
-
try {
|
|
109
|
-
return JSON.parse(await readFile(this.lockPath(stage), "utf-8"));
|
|
110
|
-
} catch {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
async removeLock(stage, holder) {
|
|
115
|
-
if (holder) {
|
|
116
|
-
if ((await this.readLock(stage))?.id !== holder.id) return;
|
|
117
|
-
}
|
|
118
|
-
await rm(this.lockPath(stage), { force: true });
|
|
119
|
-
}
|
|
120
|
-
async ensureDir() {
|
|
121
|
-
await mkdir(this.dir, {
|
|
122
|
-
recursive: true,
|
|
123
|
-
mode: 448
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Serialises the check-then-rename of a conditional write across
|
|
128
|
-
* processes. The stage lock is for whole runs; this is for the instant of
|
|
129
|
-
* a single write, so even writers that skip the lock never interleave.
|
|
130
|
-
*/
|
|
131
|
-
async withMutex(stage, fn) {
|
|
132
|
-
const path = this.mutexPath(stage);
|
|
133
|
-
const deadline = Date.now() + MUTEX_TIMEOUT_MS;
|
|
134
|
-
for (;;) try {
|
|
135
|
-
await (await open(path, "wx", FILE_MODE)).close();
|
|
136
|
-
break;
|
|
137
|
-
} catch (error) {
|
|
138
|
-
if (!isCode(error, "EEXIST")) throw error;
|
|
139
|
-
if (await this.isStale(path)) {
|
|
140
|
-
await unlink(path).catch(() => {});
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (Date.now() > deadline) throw new StateStoreBusy(stage, path);
|
|
144
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
145
|
-
}
|
|
146
|
-
try {
|
|
147
|
-
return await fn();
|
|
148
|
-
} finally {
|
|
149
|
-
await rm(path, { force: true });
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
async isStale(path) {
|
|
153
|
-
try {
|
|
154
|
-
return Date.now() - (await stat(path)).mtimeMs > MUTEX_STALE_MS;
|
|
155
|
-
} catch {
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
//#endregion
|
|
161
|
-
export { LocalStateStore };
|
|
162
|
-
|
|
163
|
-
//# sourceMappingURL=LocalStateStore-BupdV-VN.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LocalStateStore-BupdV-VN.mjs","names":[],"sources":["../src/deploy/LocalStateStore.ts"],"sourcesContent":["/**\n * Local filesystem state store.\n *\n * `.gkm/deploy-<stage>.json` holds the state, `.gkm/deploy-<stage>.lock` the\n * lock. Every file is mode 0600: state holds database passwords and IAM keys.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport {\n\tmkdir,\n\topen,\n\treadFile,\n\trename,\n\trm,\n\tstat,\n\tunlink,\n\twriteFile,\n} from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tcontentVersion,\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\tStateStoreBusy,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nconst FILE_MODE = 0o600;\n\n/** How long a write waits for another process's write to finish. */\nconst MUTEX_TIMEOUT_MS = 5_000;\n/**\n * A write holds the mutex for milliseconds, so one this old was left by a\n * process that died mid-write and is safe to take over.\n */\nconst MUTEX_STALE_MS = 30_000;\n\nfunction isCode(error: unknown, code: string): boolean {\n\treturn (error as NodeJS.ErrnoException)?.code === code;\n}\n\nexport class LocalStateStore extends DocumentStateStore {\n\tconstructor(readonly workspaceRoot: string) {\n\t\tsuper();\n\t}\n\n\tprivate get dir(): string {\n\t\treturn join(this.workspaceRoot, '.gkm');\n\t}\n\n\tstatePath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json`);\n\t}\n\n\tlockPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.lock`);\n\t}\n\n\tbackupPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.v1.json`);\n\t}\n\n\tprivate mutexPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json.mutex`);\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn this.statePath(stage);\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\ttry {\n\t\t\tconst body = await readFile(this.statePath(stage), 'utf-8');\n\t\t\treturn { body, version: contentVersion(body) };\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'ENOENT')) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tawait this.ensureDir();\n\t\treturn this.withMutex(stage, async () => {\n\t\t\tconst current = await this.readRaw(stage);\n\t\t\tconst actual = current?.version ?? null;\n\t\t\tif (actual !== expectedVersion) {\n\t\t\t\tthrow new StateVersionConflict(stage, expectedVersion, actual);\n\t\t\t}\n\n\t\t\t// Written beside the target and renamed over it: a reader sees the\n\t\t\t// old file or the new one, never half of either.\n\t\t\tconst target = this.statePath(stage);\n\t\t\tconst temp = `${target}.${randomUUID()}.tmp`;\n\t\t\ttry {\n\t\t\t\tconst handle = await open(temp, 'wx', FILE_MODE);\n\t\t\t\ttry {\n\t\t\t\t\tawait handle.writeFile(body);\n\t\t\t\t\tawait handle.sync();\n\t\t\t\t} finally {\n\t\t\t\t\tawait handle.close();\n\t\t\t\t}\n\t\t\t\tawait rename(temp, target);\n\t\t\t} catch (error) {\n\t\t\t\tawait rm(temp, { force: true });\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn contentVersion(body);\n\t\t});\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait writeFile(this.backupPath(stage), body, {\n\t\t\t\tflag: 'wx',\n\t\t\t\tmode: FILE_MODE,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tawait this.ensureDir();\n\t\tlet handle: Awaited<ReturnType<typeof open>>;\n\t\ttry {\n\t\t\thandle = await open(this.lockPath(stage), 'wx', FILE_MODE);\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'EEXIST')) {\n\t\t\t\tthrow new StateLocked(\n\t\t\t\t\tstage,\n\t\t\t\t\tawait this.readLock(stage),\n\t\t\t\t\tthis.lockPath(stage),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\ttry {\n\t\t\tawait handle.writeFile(JSON.stringify(holder, null, 2));\n\t\t} finally {\n\t\t\tawait handle.close();\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\ttry {\n\t\t\treturn JSON.parse(\n\t\t\t\tawait readFile(this.lockPath(stage), 'utf-8'),\n\t\t\t) as LockHolder;\n\t\t} catch {\n\t\t\t// Missing, or created by a process that died before writing to it.\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tif (holder) {\n\t\t\tconst current = await this.readLock(stage);\n\t\t\tif (current?.id !== holder.id) return;\n\t\t}\n\t\tawait rm(this.lockPath(stage), { force: true });\n\t}\n\n\tprivate async ensureDir(): Promise<void> {\n\t\tawait mkdir(this.dir, { recursive: true, mode: 0o700 });\n\t}\n\n\t/**\n\t * Serialises the check-then-rename of a conditional write across\n\t * processes. The stage lock is for whole runs; this is for the instant of\n\t * a single write, so even writers that skip the lock never interleave.\n\t */\n\tprivate async withMutex<T>(stage: string, fn: () => Promise<T>): Promise<T> {\n\t\tconst path = this.mutexPath(stage);\n\t\tconst deadline = Date.now() + MUTEX_TIMEOUT_MS;\n\n\t\tfor (;;) {\n\t\t\ttry {\n\t\t\t\tconst handle = await open(path, 'wx', FILE_MODE);\n\t\t\t\tawait handle.close();\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t\t\tif (await this.isStale(path)) {\n\t\t\t\t\tawait unlink(path).catch(() => {});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Date.now() > deadline) throw new StateStoreBusy(stage, path);\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 10));\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tawait rm(path, { force: true });\n\t\t}\n\t}\n\n\tprivate async isStale(path: string): Promise<boolean> {\n\t\ttry {\n\t\t\treturn Date.now() - (await stat(path)).mtimeMs > MUTEX_STALE_MS;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
const require_index = require("./index.cjs");
|
|
2
|
-
let node_fs_promises = require("node:fs/promises");
|
|
3
|
-
let node_path = require("node:path");
|
|
4
|
-
let node_crypto = require("node:crypto");
|
|
5
|
-
//#region src/deploy/LocalStateStore.ts
|
|
6
|
-
/**
|
|
7
|
-
* Local filesystem state store.
|
|
8
|
-
*
|
|
9
|
-
* `.gkm/deploy-<stage>.json` holds the state, `.gkm/deploy-<stage>.lock` the
|
|
10
|
-
* lock. Every file is mode 0600: state holds database passwords and IAM keys.
|
|
11
|
-
*/
|
|
12
|
-
const FILE_MODE = 384;
|
|
13
|
-
/** How long a write waits for another process's write to finish. */
|
|
14
|
-
const MUTEX_TIMEOUT_MS = 5e3;
|
|
15
|
-
/**
|
|
16
|
-
* A write holds the mutex for milliseconds, so one this old was left by a
|
|
17
|
-
* process that died mid-write and is safe to take over.
|
|
18
|
-
*/
|
|
19
|
-
const MUTEX_STALE_MS = 3e4;
|
|
20
|
-
function isCode(error, code) {
|
|
21
|
-
return error?.code === code;
|
|
22
|
-
}
|
|
23
|
-
var LocalStateStore = class extends require_index.DocumentStateStore {
|
|
24
|
-
workspaceRoot;
|
|
25
|
-
constructor(workspaceRoot) {
|
|
26
|
-
super();
|
|
27
|
-
this.workspaceRoot = workspaceRoot;
|
|
28
|
-
}
|
|
29
|
-
get dir() {
|
|
30
|
-
return (0, node_path.join)(this.workspaceRoot, ".gkm");
|
|
31
|
-
}
|
|
32
|
-
statePath(stage) {
|
|
33
|
-
return (0, node_path.join)(this.dir, `deploy-${stage}.json`);
|
|
34
|
-
}
|
|
35
|
-
lockPath(stage) {
|
|
36
|
-
return (0, node_path.join)(this.dir, `deploy-${stage}.lock`);
|
|
37
|
-
}
|
|
38
|
-
backupPath(stage) {
|
|
39
|
-
return (0, node_path.join)(this.dir, `deploy-${stage}.v1.json`);
|
|
40
|
-
}
|
|
41
|
-
mutexPath(stage) {
|
|
42
|
-
return (0, node_path.join)(this.dir, `deploy-${stage}.json.mutex`);
|
|
43
|
-
}
|
|
44
|
-
location(stage) {
|
|
45
|
-
return this.statePath(stage);
|
|
46
|
-
}
|
|
47
|
-
async readRaw(stage) {
|
|
48
|
-
try {
|
|
49
|
-
const body = await (0, node_fs_promises.readFile)(this.statePath(stage), "utf-8");
|
|
50
|
-
return {
|
|
51
|
-
body,
|
|
52
|
-
version: require_index.contentVersion(body)
|
|
53
|
-
};
|
|
54
|
-
} catch (error) {
|
|
55
|
-
if (isCode(error, "ENOENT")) return null;
|
|
56
|
-
throw error;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
async writeRaw(stage, body, expectedVersion) {
|
|
60
|
-
await this.ensureDir();
|
|
61
|
-
return this.withMutex(stage, async () => {
|
|
62
|
-
const actual = (await this.readRaw(stage))?.version ?? null;
|
|
63
|
-
if (actual !== expectedVersion) throw new require_index.StateVersionConflict(stage, expectedVersion, actual);
|
|
64
|
-
const target = this.statePath(stage);
|
|
65
|
-
const temp = `${target}.${(0, node_crypto.randomUUID)()}.tmp`;
|
|
66
|
-
try {
|
|
67
|
-
const handle = await (0, node_fs_promises.open)(temp, "wx", FILE_MODE);
|
|
68
|
-
try {
|
|
69
|
-
await handle.writeFile(body);
|
|
70
|
-
await handle.sync();
|
|
71
|
-
} finally {
|
|
72
|
-
await handle.close();
|
|
73
|
-
}
|
|
74
|
-
await (0, node_fs_promises.rename)(temp, target);
|
|
75
|
-
} catch (error) {
|
|
76
|
-
await (0, node_fs_promises.rm)(temp, { force: true });
|
|
77
|
-
throw error;
|
|
78
|
-
}
|
|
79
|
-
return require_index.contentVersion(body);
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
async writeV1Backup(stage, body) {
|
|
83
|
-
try {
|
|
84
|
-
await (0, node_fs_promises.writeFile)(this.backupPath(stage), body, {
|
|
85
|
-
flag: "wx",
|
|
86
|
-
mode: FILE_MODE
|
|
87
|
-
});
|
|
88
|
-
} catch (error) {
|
|
89
|
-
if (!isCode(error, "EEXIST")) throw error;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
async createLock(stage, holder) {
|
|
93
|
-
await this.ensureDir();
|
|
94
|
-
let handle;
|
|
95
|
-
try {
|
|
96
|
-
handle = await (0, node_fs_promises.open)(this.lockPath(stage), "wx", FILE_MODE);
|
|
97
|
-
} catch (error) {
|
|
98
|
-
if (isCode(error, "EEXIST")) throw new require_index.StateLocked(stage, await this.readLock(stage), this.lockPath(stage));
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
101
|
-
try {
|
|
102
|
-
await handle.writeFile(JSON.stringify(holder, null, 2));
|
|
103
|
-
} finally {
|
|
104
|
-
await handle.close();
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
async readLock(stage) {
|
|
108
|
-
try {
|
|
109
|
-
return JSON.parse(await (0, node_fs_promises.readFile)(this.lockPath(stage), "utf-8"));
|
|
110
|
-
} catch {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
async removeLock(stage, holder) {
|
|
115
|
-
if (holder) {
|
|
116
|
-
if ((await this.readLock(stage))?.id !== holder.id) return;
|
|
117
|
-
}
|
|
118
|
-
await (0, node_fs_promises.rm)(this.lockPath(stage), { force: true });
|
|
119
|
-
}
|
|
120
|
-
async ensureDir() {
|
|
121
|
-
await (0, node_fs_promises.mkdir)(this.dir, {
|
|
122
|
-
recursive: true,
|
|
123
|
-
mode: 448
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Serialises the check-then-rename of a conditional write across
|
|
128
|
-
* processes. The stage lock is for whole runs; this is for the instant of
|
|
129
|
-
* a single write, so even writers that skip the lock never interleave.
|
|
130
|
-
*/
|
|
131
|
-
async withMutex(stage, fn) {
|
|
132
|
-
const path = this.mutexPath(stage);
|
|
133
|
-
const deadline = Date.now() + MUTEX_TIMEOUT_MS;
|
|
134
|
-
for (;;) try {
|
|
135
|
-
await (await (0, node_fs_promises.open)(path, "wx", FILE_MODE)).close();
|
|
136
|
-
break;
|
|
137
|
-
} catch (error) {
|
|
138
|
-
if (!isCode(error, "EEXIST")) throw error;
|
|
139
|
-
if (await this.isStale(path)) {
|
|
140
|
-
await (0, node_fs_promises.unlink)(path).catch(() => {});
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (Date.now() > deadline) throw new require_index.StateStoreBusy(stage, path);
|
|
144
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
145
|
-
}
|
|
146
|
-
try {
|
|
147
|
-
return await fn();
|
|
148
|
-
} finally {
|
|
149
|
-
await (0, node_fs_promises.rm)(path, { force: true });
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
async isStale(path) {
|
|
153
|
-
try {
|
|
154
|
-
return Date.now() - (await (0, node_fs_promises.stat)(path)).mtimeMs > MUTEX_STALE_MS;
|
|
155
|
-
} catch {
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
//#endregion
|
|
161
|
-
exports.LocalStateStore = LocalStateStore;
|
|
162
|
-
|
|
163
|
-
//# sourceMappingURL=LocalStateStore-DDDU0HkI.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LocalStateStore-DDDU0HkI.cjs","names":["DocumentStateStore","join","readFile","contentVersion","StateVersionConflict","randomUUID","open","rename","rm","writeFile","StateLocked","mkdir","unlink","StateStoreBusy","stat"],"sources":["../src/deploy/LocalStateStore.ts"],"sourcesContent":["/**\n * Local filesystem state store.\n *\n * `.gkm/deploy-<stage>.json` holds the state, `.gkm/deploy-<stage>.lock` the\n * lock. Every file is mode 0600: state holds database passwords and IAM keys.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport {\n\tmkdir,\n\topen,\n\treadFile,\n\trename,\n\trm,\n\tstat,\n\tunlink,\n\twriteFile,\n} from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tcontentVersion,\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\tStateStoreBusy,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nconst FILE_MODE = 0o600;\n\n/** How long a write waits for another process's write to finish. */\nconst MUTEX_TIMEOUT_MS = 5_000;\n/**\n * A write holds the mutex for milliseconds, so one this old was left by a\n * process that died mid-write and is safe to take over.\n */\nconst MUTEX_STALE_MS = 30_000;\n\nfunction isCode(error: unknown, code: string): boolean {\n\treturn (error as NodeJS.ErrnoException)?.code === code;\n}\n\nexport class LocalStateStore extends DocumentStateStore {\n\tconstructor(readonly workspaceRoot: string) {\n\t\tsuper();\n\t}\n\n\tprivate get dir(): string {\n\t\treturn join(this.workspaceRoot, '.gkm');\n\t}\n\n\tstatePath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json`);\n\t}\n\n\tlockPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.lock`);\n\t}\n\n\tbackupPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.v1.json`);\n\t}\n\n\tprivate mutexPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json.mutex`);\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn this.statePath(stage);\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\ttry {\n\t\t\tconst body = await readFile(this.statePath(stage), 'utf-8');\n\t\t\treturn { body, version: contentVersion(body) };\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'ENOENT')) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tawait this.ensureDir();\n\t\treturn this.withMutex(stage, async () => {\n\t\t\tconst current = await this.readRaw(stage);\n\t\t\tconst actual = current?.version ?? null;\n\t\t\tif (actual !== expectedVersion) {\n\t\t\t\tthrow new StateVersionConflict(stage, expectedVersion, actual);\n\t\t\t}\n\n\t\t\t// Written beside the target and renamed over it: a reader sees the\n\t\t\t// old file or the new one, never half of either.\n\t\t\tconst target = this.statePath(stage);\n\t\t\tconst temp = `${target}.${randomUUID()}.tmp`;\n\t\t\ttry {\n\t\t\t\tconst handle = await open(temp, 'wx', FILE_MODE);\n\t\t\t\ttry {\n\t\t\t\t\tawait handle.writeFile(body);\n\t\t\t\t\tawait handle.sync();\n\t\t\t\t} finally {\n\t\t\t\t\tawait handle.close();\n\t\t\t\t}\n\t\t\t\tawait rename(temp, target);\n\t\t\t} catch (error) {\n\t\t\t\tawait rm(temp, { force: true });\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn contentVersion(body);\n\t\t});\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait writeFile(this.backupPath(stage), body, {\n\t\t\t\tflag: 'wx',\n\t\t\t\tmode: FILE_MODE,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tawait this.ensureDir();\n\t\tlet handle: Awaited<ReturnType<typeof open>>;\n\t\ttry {\n\t\t\thandle = await open(this.lockPath(stage), 'wx', FILE_MODE);\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'EEXIST')) {\n\t\t\t\tthrow new StateLocked(\n\t\t\t\t\tstage,\n\t\t\t\t\tawait this.readLock(stage),\n\t\t\t\t\tthis.lockPath(stage),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\ttry {\n\t\t\tawait handle.writeFile(JSON.stringify(holder, null, 2));\n\t\t} finally {\n\t\t\tawait handle.close();\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\ttry {\n\t\t\treturn JSON.parse(\n\t\t\t\tawait readFile(this.lockPath(stage), 'utf-8'),\n\t\t\t) as LockHolder;\n\t\t} catch {\n\t\t\t// Missing, or created by a process that died before writing to it.\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tif (holder) {\n\t\t\tconst current = await this.readLock(stage);\n\t\t\tif (current?.id !== holder.id) return;\n\t\t}\n\t\tawait rm(this.lockPath(stage), { force: true });\n\t}\n\n\tprivate async ensureDir(): Promise<void> {\n\t\tawait mkdir(this.dir, { recursive: true, mode: 0o700 });\n\t}\n\n\t/**\n\t * Serialises the check-then-rename of a conditional write across\n\t * processes. The stage lock is for whole runs; this is for the instant of\n\t * a single write, so even writers that skip the lock never interleave.\n\t */\n\tprivate async withMutex<T>(stage: string, fn: () => Promise<T>): Promise<T> {\n\t\tconst path = this.mutexPath(stage);\n\t\tconst deadline = Date.now() + MUTEX_TIMEOUT_MS;\n\n\t\tfor (;;) {\n\t\t\ttry {\n\t\t\t\tconst handle = await open(path, 'wx', FILE_MODE);\n\t\t\t\tawait handle.close();\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t\t\tif (await this.isStale(path)) {\n\t\t\t\t\tawait unlink(path).catch(() => {});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Date.now() > deadline) throw new StateStoreBusy(stage, path);\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 10));\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tawait rm(path, { force: true });\n\t\t}\n\t}\n\n\tprivate async isStale(path: string): Promise<boolean> {\n\t\ttry {\n\t\t\treturn Date.now() - (await stat(path)).mtimeMs > MUTEX_STALE_MS;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { n as __require } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
2
|
-
import { GetParameterCommand, ParameterNotFound, PutParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
|
|
3
|
-
//#region src/deploy/SSMStateProvider.ts
|
|
4
|
-
/**
|
|
5
|
-
* AWS SSM Parameter Store State Provider
|
|
6
|
-
*
|
|
7
|
-
* Stores deployment state as SecureString parameters in AWS SSM.
|
|
8
|
-
* Uses AWS-managed KMS key for encryption (free tier).
|
|
9
|
-
*
|
|
10
|
-
* Parameter naming: /gkm/{workspaceName}/{stage}/state
|
|
11
|
-
*/
|
|
12
|
-
/**
|
|
13
|
-
* AWS SSM Parameter Store state provider.
|
|
14
|
-
*
|
|
15
|
-
* Stores state as encrypted SecureString parameters.
|
|
16
|
-
* Parameter path: /gkm/{workspaceName}/{stage}/state
|
|
17
|
-
*/
|
|
18
|
-
var SSMStateProvider = class SSMStateProvider {
|
|
19
|
-
workspaceName;
|
|
20
|
-
client;
|
|
21
|
-
constructor(workspaceName, client) {
|
|
22
|
-
this.workspaceName = workspaceName;
|
|
23
|
-
this.client = client;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Create an SSMStateProvider with a new SSMClient.
|
|
27
|
-
*/
|
|
28
|
-
static create(options) {
|
|
29
|
-
const clientConfig = {
|
|
30
|
-
region: options.region,
|
|
31
|
-
endpoint: options.endpoint
|
|
32
|
-
};
|
|
33
|
-
if (options.profile) {
|
|
34
|
-
const { fromIni } = __require("@aws-sdk/credential-providers");
|
|
35
|
-
clientConfig.credentials = fromIni({ profile: options.profile });
|
|
36
|
-
} else if (options.credentials) clientConfig.credentials = options.credentials;
|
|
37
|
-
const client = new SSMClient(clientConfig);
|
|
38
|
-
return new SSMStateProvider(options.workspaceName, client);
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Get the SSM parameter name for a stage.
|
|
42
|
-
*/
|
|
43
|
-
getParameterName(stage) {
|
|
44
|
-
return `/gkm/${this.workspaceName}/${stage}/state`;
|
|
45
|
-
}
|
|
46
|
-
async read(stage) {
|
|
47
|
-
const parameterName = this.getParameterName(stage);
|
|
48
|
-
try {
|
|
49
|
-
const response = await this.client.send(new GetParameterCommand({
|
|
50
|
-
Name: parameterName,
|
|
51
|
-
WithDecryption: true
|
|
52
|
-
}));
|
|
53
|
-
if (!response.Parameter?.Value) return null;
|
|
54
|
-
return JSON.parse(response.Parameter.Value);
|
|
55
|
-
} catch (error) {
|
|
56
|
-
if (error instanceof ParameterNotFound) return null;
|
|
57
|
-
throw error;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
async write(stage, state) {
|
|
61
|
-
const parameterName = this.getParameterName(stage);
|
|
62
|
-
state.lastDeployedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
63
|
-
await this.client.send(new PutParameterCommand({
|
|
64
|
-
Name: parameterName,
|
|
65
|
-
Value: JSON.stringify(state),
|
|
66
|
-
Type: "SecureString",
|
|
67
|
-
Overwrite: true,
|
|
68
|
-
Description: `GKM deployment state for ${this.workspaceName}/${stage}`
|
|
69
|
-
}));
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
//#endregion
|
|
73
|
-
export { SSMStateProvider };
|
|
74
|
-
|
|
75
|
-
//# sourceMappingURL=SSMStateProvider-B0Hu1MKZ.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SSMStateProvider-B0Hu1MKZ.mjs","names":[],"sources":["../src/deploy/SSMStateProvider.ts"],"sourcesContent":["/**\n * AWS SSM Parameter Store State Provider\n *\n * Stores deployment state as SecureString parameters in AWS SSM.\n * Uses AWS-managed KMS key for encryption (free tier).\n *\n * Parameter naming: /gkm/{workspaceName}/{stage}/state\n */\n\nimport {\n\tGetParameterCommand,\n\tParameterNotFound,\n\tPutParameterCommand,\n\tSSMClient,\n\ttype SSMClientConfig,\n} from '@aws-sdk/client-ssm';\nimport type { AwsRegion, StateProvider } from './StateProvider';\nimport type { DokployStageState } from './state';\n\nexport interface SSMStateProviderOptions {\n\t/** Workspace name (used in parameter path) */\n\tworkspaceName: string;\n\t/** AWS region */\n\tregion?: AwsRegion;\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile?: string;\n\t/** AWS credentials (optional - uses default credential chain if not provided) */\n\tcredentials?: SSMClientConfig['credentials'];\n\t/** Custom endpoint (for LocalStack or other S3-compatible services) */\n\tendpoint?: string;\n}\n\n/**\n * AWS SSM Parameter Store state provider.\n *\n * Stores state as encrypted SecureString parameters.\n * Parameter path: /gkm/{workspaceName}/{stage}/state\n */\nexport class SSMStateProvider implements StateProvider {\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\tprivate readonly client: SSMClient,\n\t) {}\n\n\t/**\n\t * Create an SSMStateProvider with a new SSMClient.\n\t */\n\tstatic create(options: SSMStateProviderOptions): SSMStateProvider {\n\t\tconst clientConfig: SSMClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t};\n\n\t\t// Use profile credentials if specified, otherwise use provided credentials or default chain\n\t\tif (options.profile) {\n\t\t\t// Dynamic import to avoid requiring @aws-sdk/credential-providers when not using profiles\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tclientConfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tclientConfig.credentials = options.credentials;\n\t\t}\n\n\t\tconst client = new SSMClient(clientConfig);\n\t\treturn new SSMStateProvider(options.workspaceName, client);\n\t}\n\n\t/**\n\t * Get the SSM parameter name for a stage.\n\t */\n\tprivate getParameterName(stage: string): string {\n\t\treturn `/gkm/${this.workspaceName}/${stage}/state`;\n\t}\n\n\tasync read(stage: string): Promise<DokployStageState | null> {\n\t\tconst parameterName = this.getParameterName(stage);\n\n\t\ttry {\n\t\t\tconst response = await this.client.send(\n\t\t\t\tnew GetParameterCommand({\n\t\t\t\t\tName: parameterName,\n\t\t\t\t\tWithDecryption: true,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (!response.Parameter?.Value) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn JSON.parse(response.Parameter.Value) as DokployStageState;\n\t\t} catch (error) {\n\t\t\t// Parameter doesn't exist - return null (new deployment)\n\t\t\tif (error instanceof ParameterNotFound) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Re-throw other errors (permission denied, network, etc.)\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync write(stage: string, state: DokployStageState): Promise<void> {\n\t\tconst parameterName = this.getParameterName(stage);\n\n\t\t// Update last deployed timestamp\n\t\tstate.lastDeployedAt = new Date().toISOString();\n\n\t\tawait this.client.send(\n\t\t\tnew PutParameterCommand({\n\t\t\t\tName: parameterName,\n\t\t\t\tValue: JSON.stringify(state),\n\t\t\t\tType: 'SecureString',\n\t\t\t\tOverwrite: true,\n\t\t\t\tDescription: `GKM deployment state for ${this.workspaceName}/${stage}`,\n\t\t\t}),\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAsCA,IAAa,mBAAb,MAAa,iBAA0C;CAE5C;CACQ;CAFlB,YACC,eACA,QACC;EAFQ,KAAA,gBAAA;EACQ,KAAA,SAAA;CACf;;;;CAKH,OAAO,OAAO,SAAoD;EACjE,MAAM,eAAgC;GACrC,QAAQ,QAAQ;GAChB,UAAU,QAAQ;EACnB;EAGA,IAAI,QAAQ,SAAS;GAEpB,MAAM,EAAE,YAAA,UAAoB,+BAA+B;GAC3D,aAAa,cAAc,QAAQ,EAAE,SAAS,QAAQ,QAAQ,CAAC;EAChE,OAAO,IAAI,QAAQ,aAClB,aAAa,cAAc,QAAQ;EAGpC,MAAM,SAAS,IAAI,UAAU,YAAY;EACzC,OAAO,IAAI,iBAAiB,QAAQ,eAAe,MAAM;CAC1D;;;;CAKA,iBAAyB,OAAuB;EAC/C,OAAO,QAAQ,KAAK,cAAc,GAAG,MAAM;CAC5C;CAEA,MAAM,KAAK,OAAkD;EAC5D,MAAM,gBAAgB,KAAK,iBAAiB,KAAK;EAEjD,IAAI;GACH,MAAM,WAAW,MAAM,KAAK,OAAO,KAClC,IAAI,oBAAoB;IACvB,MAAM;IACN,gBAAgB;GACjB,CAAC,CACF;GAEA,IAAI,CAAC,SAAS,WAAW,OACxB,OAAO;GAGR,OAAO,KAAK,MAAM,SAAS,UAAU,KAAK;EAC3C,SAAS,OAAO;GAEf,IAAI,iBAAiB,mBACpB,OAAO;GAIR,MAAM;EACP;CACD;CAEA,MAAM,MAAM,OAAe,OAAyC;EACnE,MAAM,gBAAgB,KAAK,iBAAiB,KAAK;EAGjD,MAAM,kCAAiB,IAAI,KAAK,EAAA,CAAE,YAAY;EAE9C,MAAM,KAAK,OAAO,KACjB,IAAI,oBAAoB;GACvB,MAAM;GACN,OAAO,KAAK,UAAU,KAAK;GAC3B,MAAM;GACN,WAAW;GACX,aAAa,4BAA4B,KAAK,cAAc,GAAG;EAChE,CAAC,CACF;CACD;AACD"}
|