@geekmidas/cloud 9.0.2 → 10.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/{index-ByEJy40r.d.cts → index-B5CZ1xVf.d.cts} +17 -9
  2. package/dist/index-B5CZ1xVf.d.cts.map +1 -0
  3. package/dist/{index-DZ0QrJQr.d.mts → index-DhHRjduZ.d.mts} +17 -9
  4. package/dist/index-DhHRjduZ.d.mts.map +1 -0
  5. package/dist/index.cjs +1 -1
  6. package/dist/index.d.cts +1 -1
  7. package/dist/index.d.mts +1 -1
  8. package/dist/index.mjs +1 -1
  9. package/dist/utils/index.cjs +1 -1
  10. package/dist/utils/index.d.cts +1 -1
  11. package/dist/utils/index.d.mts +1 -1
  12. package/dist/utils/index.mjs +1 -1
  13. package/dist/{utils-CtMjuIMR.cjs → utils-B1a2UuEO.cjs} +5 -5
  14. package/dist/utils-B1a2UuEO.cjs.map +1 -0
  15. package/dist/{utils-BdKG20_m.mjs → utils-DMOXJ27j.mjs} +5 -5
  16. package/dist/utils-DMOXJ27j.mjs.map +1 -0
  17. package/package.json +44 -7
  18. package/src/dokploy/Application.ts +259 -0
  19. package/src/dokploy/__tests__/Application.spec.ts +69 -0
  20. package/src/dokploy/index.ts +18 -0
  21. package/src/sst/__tests__/LinkedEnvironment.spec.ts +4 -1
  22. package/src/sst/__tests__/backends.spec.ts +249 -0
  23. package/src/sst/__tests__/bootstrap.spec.ts +140 -0
  24. package/src/sst/__tests__/database.spec.ts +119 -0
  25. package/src/sst/__tests__/fromManifest.spec.ts +266 -0
  26. package/src/sst/__tests__/provides.spec.ts +107 -0
  27. package/src/sst/__tests__/ses.spec.ts +93 -0
  28. package/src/sst/__tests__/surfaces.spec.ts +132 -0
  29. package/src/sst/__type-tests__/authorizers.type-test.ts +2 -2
  30. package/src/sst/__type-tests__/manifest.type-test.ts +3 -3
  31. package/src/sst/__type-tests__/messaging.type-test.ts +3 -3
  32. package/src/sst/__type-tests__/storage.type-test.ts +2 -2
  33. package/src/sst/{Api.ts → aws/Api.ts} +3 -3
  34. package/src/sst/aws/Cache.ts +259 -0
  35. package/src/sst/aws/Credential.ts +28 -0
  36. package/src/sst/{Cron.ts → aws/Cron.ts} +2 -2
  37. package/src/sst/aws/Database.ts +190 -0
  38. package/src/sst/aws/DatabaseBootstrap.ts +309 -0
  39. package/src/sst/aws/DerivedDatabase.ts +117 -0
  40. package/src/sst/aws/Email.ts +181 -0
  41. package/src/sst/aws/FileServer.ts +82 -0
  42. package/src/sst/{Function.ts → aws/Function.ts} +3 -3
  43. package/src/sst/aws/ObjectStorage.ts +76 -0
  44. package/src/sst/aws/Queue.ts +116 -0
  45. package/src/sst/aws/RestApiSurface.ts +92 -0
  46. package/src/sst/aws/Secret.ts +76 -0
  47. package/src/sst/aws/StaticSite.ts +92 -0
  48. package/src/sst/{Storage.ts → aws/Storage.ts} +3 -3
  49. package/src/sst/aws/Topic.ts +68 -0
  50. package/src/sst/aws/bootstrap/handler.ts +110 -0
  51. package/src/sst/aws/ses.ts +132 -0
  52. package/src/sst/errors.ts +65 -0
  53. package/src/sst/fromManifest.ts +899 -0
  54. package/src/sst/index.ts +62 -7
  55. package/src/sst/naming.ts +37 -16
  56. package/src/sst/tsconfig.json +2 -2
  57. package/src/sst/upstash.d.ts +38 -0
  58. package/dist/index-ByEJy40r.d.cts.map +0 -1
  59. package/dist/index-DZ0QrJQr.d.mts.map +0 -1
  60. package/dist/utils-BdKG20_m.mjs.map +0 -1
  61. package/dist/utils-CtMjuIMR.cjs.map +0 -1
  62. package/src/sst/Queue.ts +0 -46
  63. package/src/sst/Topic.ts +0 -37
@@ -5,19 +5,27 @@ declare enum ResourceType {
5
5
  ApiGatewayV2 = "sst.aws.ApiGatewayV2",
6
6
  Postgres = "sst.aws.Postgres",
7
7
  Function = "sst.aws.Function",
8
- Bucket = "sst.aws.Bucket",
8
+ ObjectStorage = "sst.aws.Bucket",
9
9
  Vpc = "sst.aws.Vpc",
10
10
  Secret = "sst.sst.Secret",
11
11
  SSTSecret = "sst:sst:Secret",
12
12
  SSTFunction = "sst:sst:Function",
13
13
  SSTApiGatewayV2 = "sst:aws:ApiGatewayV2",
14
14
  SSTPostgres = "sst:aws:Postgres",
15
- SSTBucket = "sst:aws:Bucket",
15
+ SSTObjectStorage = "sst:aws:Bucket",
16
16
  SnsTopic = "sst:aws:SnsTopic",
17
17
  }
18
18
  declare function buildResourceEnv(record: Record<string, Resource | string>): Record<string, string>;
19
+ /**
20
+ * SST links a resource under one of two spellings — `sst.aws.Bucket` when it
21
+ * comes from the state file, `sst:aws:Bucket` when it comes from the runtime —
22
+ * and `processors` has always handled both. Only the dotted half was admitted
23
+ * here, so the colon half could not be passed to `buildResourceEnv` without a
24
+ * cast, and `SnsTopic` was missing from `Resource` altogether despite being the
25
+ * only thing that produces `*_ARN`.
26
+ */
19
27
  type ApiGatewayV2 = {
20
- type: ResourceType.ApiGatewayV2;
28
+ type: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;
21
29
  url: string;
22
30
  };
23
31
  type Postgres = {
@@ -25,16 +33,16 @@ type Postgres = {
25
33
  host: string;
26
34
  password: string;
27
35
  port: number;
28
- type: ResourceType.Postgres;
36
+ type: ResourceType.Postgres | ResourceType.SSTPostgres;
29
37
  username: string;
30
38
  };
31
39
  type Function = {
32
40
  name: string;
33
- type: ResourceType.Function;
41
+ type: ResourceType.Function | ResourceType.SSTFunction;
34
42
  };
35
43
  type Bucket = {
36
44
  name: string;
37
- type: ResourceType.Bucket;
45
+ type: ResourceType.ObjectStorage | ResourceType.SSTObjectStorage;
38
46
  };
39
47
  type SnsTopic = {
40
48
  arn: string;
@@ -45,12 +53,12 @@ type Vpc = {
45
53
  type: ResourceType.Vpc;
46
54
  };
47
55
  type Secret = {
48
- type: ResourceType.Secret;
56
+ type: ResourceType.Secret | ResourceType.SSTSecret;
49
57
  value: string;
50
58
  };
51
- type Resource = ApiGatewayV2 | Postgres | Function | Bucket | Vpc | Secret;
59
+ type Resource = ApiGatewayV2 | Postgres | Function | Bucket | SnsTopic | Vpc | Secret;
52
60
  type ResourceProcessor<K extends Resource> = (name: string, value: K) => Record<string, string | number>;
53
61
  //# sourceMappingURL=index.d.ts.map
54
62
  //#endregion
55
63
  export { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress };
56
- //# sourceMappingURL=index-ByEJy40r.d.cts.map
64
+ //# sourceMappingURL=index-B5CZ1xVf.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-B5CZ1xVf.d.cts","names":[],"sources":["../src/utils/index.ts"],"sourcesContent":[],"mappings":";iBAGgB,iBAAA,CAAA;AAAA,iBAcA,eAAA,CAdiB,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAcjB,aAOJ,YAAA;EAAA,YAAA,GAAY,sBAAA;EA+DR,QAAA,GAAA,kBAAgB;EAAA,QAAA,GAAA,kBAAA;EAAA,aACR,GAAA,gBAAA;EAAQ,GAAvB,GAAA,aAAA;EAAM,MACZ,GAAA,gBAAA;EAAM,SAAA,GAAA,gBAAA;EA0BG,WAAA,GAAA,kBAAY;EAAA,eAAA,GAAA,sBAAA;EAAA,WACjB,GAAA,kBAAa;EAAY,gBAAgB,GAAA,gBAAA;EAAe,QAAA,GAAA,kBAAA;AAI/D;AAAoB,iBAjCJ,gBAAA,CAiCI,MAAA,EAhCX,MAgCW,CAAA,MAAA,EAhCI,QAgCJ,GAAA,MAAA,CAAA,CAAA,EA/BjB,MA+BiB,CAAA,MAAA,EAAA,MAAA,CAAA;;;AAKmC;AAIvD;;;;AAEuD;AAG3C,KAnBA,YAAA,GAmBM;EAAA,IAAA,EAlBX,YAAA,CAAa,YAkBF,GAlBiB,YAAA,CAAa,eAkB9B;EAAA,GAEX,EAAA,MAAA;CAA0B;AAAgC,KAhBrD,QAAA,GAgBqD;EAGrD,QAAA,EAAA,MAAQ;EAKR,IAAA,EAAG,MAAA;EAKH,QAAA,EAAM,MAAA;EAAA,IAAA,EAAA,MAAA;EAAA,IACX,EAzBA,YAAA,CAAa,QAyBA,GAzBW,YAAA,CAAa,WAyBxB;EAAM,QAAG,EAAA,MAAa;AAAS,CAAA;AAIvC,KAzBA,QAAA,GAyBQ;EAAA,IAAA,EAAA,MAAA;EAAA,IACjB,EAxBI,YAAA,CAAa,QAwBjB,GAxB4B,YAAA,CAAa,WAwBzC;CAAY;AAEZ,KAvBS,MAAA,GAuBT;EAAQ,IACR,EAAA,MAAA;EAAM,IACN,EAvBI,YAAA,CAAa,aAuBjB,GAvBiC,YAAA,CAAa,gBAuB9C;CAAQ;AAER,KAtBS,QAAA,GAsBT;EAAM,GAAA,EAAA,MAAA;EAEG,IAAA,EAtBL,YAAA,CAAa,QAsBS;CAAA;AAAW,KAnB5B,GAAA,GAmB4B;EAAQ,OAExC,EAAA,MAAA;EAAC,IACJ,EApBE,YAAA,CAAa,GAoBf;AAAM,CAAA;KAjBC,MAAA;QACL,YAAA,CAAa,SAAS,YAAA,CAAa;;;KAI9B,QAAA,GACT,eACA,WACA,WACA,SACA,WACA,MACA;KAES,4BAA4B,kCAEhC,MACH"}
@@ -5,19 +5,27 @@ declare enum ResourceType {
5
5
  ApiGatewayV2 = "sst.aws.ApiGatewayV2",
6
6
  Postgres = "sst.aws.Postgres",
7
7
  Function = "sst.aws.Function",
8
- Bucket = "sst.aws.Bucket",
8
+ ObjectStorage = "sst.aws.Bucket",
9
9
  Vpc = "sst.aws.Vpc",
10
10
  Secret = "sst.sst.Secret",
11
11
  SSTSecret = "sst:sst:Secret",
12
12
  SSTFunction = "sst:sst:Function",
13
13
  SSTApiGatewayV2 = "sst:aws:ApiGatewayV2",
14
14
  SSTPostgres = "sst:aws:Postgres",
15
- SSTBucket = "sst:aws:Bucket",
15
+ SSTObjectStorage = "sst:aws:Bucket",
16
16
  SnsTopic = "sst:aws:SnsTopic",
17
17
  }
18
18
  declare function buildResourceEnv(record: Record<string, Resource | string>): Record<string, string>;
19
+ /**
20
+ * SST links a resource under one of two spellings — `sst.aws.Bucket` when it
21
+ * comes from the state file, `sst:aws:Bucket` when it comes from the runtime —
22
+ * and `processors` has always handled both. Only the dotted half was admitted
23
+ * here, so the colon half could not be passed to `buildResourceEnv` without a
24
+ * cast, and `SnsTopic` was missing from `Resource` altogether despite being the
25
+ * only thing that produces `*_ARN`.
26
+ */
19
27
  type ApiGatewayV2 = {
20
- type: ResourceType.ApiGatewayV2;
28
+ type: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;
21
29
  url: string;
22
30
  };
23
31
  type Postgres = {
@@ -25,16 +33,16 @@ type Postgres = {
25
33
  host: string;
26
34
  password: string;
27
35
  port: number;
28
- type: ResourceType.Postgres;
36
+ type: ResourceType.Postgres | ResourceType.SSTPostgres;
29
37
  username: string;
30
38
  };
31
39
  type Function = {
32
40
  name: string;
33
- type: ResourceType.Function;
41
+ type: ResourceType.Function | ResourceType.SSTFunction;
34
42
  };
35
43
  type Bucket = {
36
44
  name: string;
37
- type: ResourceType.Bucket;
45
+ type: ResourceType.ObjectStorage | ResourceType.SSTObjectStorage;
38
46
  };
39
47
  type SnsTopic = {
40
48
  arn: string;
@@ -45,12 +53,12 @@ type Vpc = {
45
53
  type: ResourceType.Vpc;
46
54
  };
47
55
  type Secret = {
48
- type: ResourceType.Secret;
56
+ type: ResourceType.Secret | ResourceType.SSTSecret;
49
57
  value: string;
50
58
  };
51
- type Resource = ApiGatewayV2 | Postgres | Function | Bucket | Vpc | Secret;
59
+ type Resource = ApiGatewayV2 | Postgres | Function | Bucket | SnsTopic | Vpc | Secret;
52
60
  type ResourceProcessor<K extends Resource> = (name: string, value: K) => Record<string, string | number>;
53
61
  //# sourceMappingURL=index.d.ts.map
54
62
  //#endregion
55
63
  export { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress };
56
- //# sourceMappingURL=index-DZ0QrJQr.d.mts.map
64
+ //# sourceMappingURL=index-DhHRjduZ.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-DhHRjduZ.d.mts","names":[],"sources":["../src/utils/index.ts"],"sourcesContent":[],"mappings":";iBAGgB,iBAAA,CAAA;AAAA,iBAcA,eAAA,CAdiB,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAcjB,aAOJ,YAAA;EAAA,YAAA,GAAY,sBAAA;EA+DR,QAAA,GAAA,kBAAgB;EAAA,QAAA,GAAA,kBAAA;EAAA,aACR,GAAA,gBAAA;EAAQ,GAAvB,GAAA,aAAA;EAAM,MACZ,GAAA,gBAAA;EAAM,SAAA,GAAA,gBAAA;EA0BG,WAAA,GAAA,kBAAY;EAAA,eAAA,GAAA,sBAAA;EAAA,WACjB,GAAA,kBAAa;EAAY,gBAAgB,GAAA,gBAAA;EAAe,QAAA,GAAA,kBAAA;AAI/D;AAAoB,iBAjCJ,gBAAA,CAiCI,MAAA,EAhCX,MAgCW,CAAA,MAAA,EAhCI,QAgCJ,GAAA,MAAA,CAAA,CAAA,EA/BjB,MA+BiB,CAAA,MAAA,EAAA,MAAA,CAAA;;;AAKmC;AAIvD;;;;AAEuD;AAG3C,KAnBA,YAAA,GAmBM;EAAA,IAAA,EAlBX,YAAA,CAAa,YAkBF,GAlBiB,YAAA,CAAa,eAkB9B;EAAA,GAEX,EAAA,MAAA;CAA0B;AAAgC,KAhBrD,QAAA,GAgBqD;EAGrD,QAAA,EAAA,MAAQ;EAKR,IAAA,EAAG,MAAA;EAKH,QAAA,EAAM,MAAA;EAAA,IAAA,EAAA,MAAA;EAAA,IACX,EAzBA,YAAA,CAAa,QAyBA,GAzBW,YAAA,CAAa,WAyBxB;EAAM,QAAG,EAAA,MAAa;AAAS,CAAA;AAIvC,KAzBA,QAAA,GAyBQ;EAAA,IAAA,EAAA,MAAA;EAAA,IACjB,EAxBI,YAAA,CAAa,QAwBjB,GAxB4B,YAAA,CAAa,WAwBzC;CAAY;AAEZ,KAvBS,MAAA,GAuBT;EAAQ,IACR,EAAA,MAAA;EAAM,IACN,EAvBI,YAAA,CAAa,aAuBjB,GAvBiC,YAAA,CAAa,gBAuB9C;CAAQ;AAER,KAtBS,QAAA,GAsBT;EAAM,GAAA,EAAA,MAAA;EAEG,IAAA,EAtBL,YAAA,CAAa,QAsBS;CAAA;AAAW,KAnB5B,GAAA,GAmB4B;EAAQ,OAExC,EAAA,MAAA;EAAC,IACJ,EApBE,YAAA,CAAa,GAoBf;AAAM,CAAA;KAjBC,MAAA;QACL,YAAA,CAAa,SAAS,YAAA,CAAa;;;KAI9B,QAAA,GACT,eACA,WACA,WACA,SACA,WACA,MACA;KAES,4BAA4B,kCAEhC,MACH"}
package/dist/index.cjs CHANGED
@@ -1,3 +1,3 @@
1
- const require_utils = require('./utils-CtMjuIMR.cjs');
1
+ const require_utils = require('./utils-B1a2UuEO.cjs');
2
2
 
3
3
  exports.buildResourceEnv = require_utils.buildResourceEnv;
package/dist/index.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { buildResourceEnv } from "./index-ByEJy40r.cjs";
1
+ import { buildResourceEnv } from "./index-B5CZ1xVf.cjs";
2
2
  export { buildResourceEnv };
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { buildResourceEnv } from "./index-DZ0QrJQr.mjs";
1
+ import { buildResourceEnv } from "./index-DhHRjduZ.mjs";
2
2
  export { buildResourceEnv };
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { buildResourceEnv } from "./utils-BdKG20_m.mjs";
1
+ import { buildResourceEnv } from "./utils-DMOXJ27j.mjs";
2
2
 
3
3
  export { buildResourceEnv };
@@ -1,4 +1,4 @@
1
- const require_utils = require('../utils-CtMjuIMR.cjs');
1
+ const require_utils = require('../utils-B1a2UuEO.cjs');
2
2
 
3
3
  exports.ResourceType = require_utils.ResourceType;
4
4
  exports.buildResourceEnv = require_utils.buildResourceEnv;
@@ -1,2 +1,2 @@
1
- import { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress } from "../index-ByEJy40r.cjs";
1
+ import { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress } from "../index-B5CZ1xVf.cjs";
2
2
  export { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress };
@@ -1,2 +1,2 @@
1
- import { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress } from "../index-DZ0QrJQr.mjs";
1
+ import { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress } from "../index-DhHRjduZ.mjs";
2
2
  export { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress };
@@ -1,3 +1,3 @@
1
- import { ResourceType, buildResourceEnv, environmentCase, getLocalIpAddress } from "../utils-BdKG20_m.mjs";
1
+ import { ResourceType, buildResourceEnv, environmentCase, getLocalIpAddress } from "../utils-DMOXJ27j.mjs";
2
2
 
3
3
  export { ResourceType, buildResourceEnv, environmentCase, getLocalIpAddress };
@@ -42,14 +42,14 @@ let ResourceType = /* @__PURE__ */ function(ResourceType$1) {
42
42
  ResourceType$1["ApiGatewayV2"] = "sst.aws.ApiGatewayV2";
43
43
  ResourceType$1["Postgres"] = "sst.aws.Postgres";
44
44
  ResourceType$1["Function"] = "sst.aws.Function";
45
- ResourceType$1["Bucket"] = "sst.aws.Bucket";
45
+ ResourceType$1["ObjectStorage"] = "sst.aws.Bucket";
46
46
  ResourceType$1["Vpc"] = "sst.aws.Vpc";
47
47
  ResourceType$1["Secret"] = "sst.sst.Secret";
48
48
  ResourceType$1["SSTSecret"] = "sst:sst:Secret";
49
49
  ResourceType$1["SSTFunction"] = "sst:sst:Function";
50
50
  ResourceType$1["SSTApiGatewayV2"] = "sst:aws:ApiGatewayV2";
51
51
  ResourceType$1["SSTPostgres"] = "sst:aws:Postgres";
52
- ResourceType$1["SSTBucket"] = "sst:aws:Bucket";
52
+ ResourceType$1["SSTObjectStorage"] = "sst:aws:Bucket";
53
53
  ResourceType$1["SnsTopic"] = "sst:aws:SnsTopic";
54
54
  return ResourceType$1;
55
55
  }({});
@@ -80,9 +80,9 @@ const processors = {
80
80
  [ResourceType.Vpc]: noop,
81
81
  [ResourceType.Secret]: secret,
82
82
  [ResourceType.Postgres]: postgres,
83
- [ResourceType.Bucket]: bucket,
83
+ [ResourceType.ObjectStorage]: bucket,
84
84
  [ResourceType.SSTSecret]: secret,
85
- [ResourceType.SSTBucket]: bucket,
85
+ [ResourceType.SSTObjectStorage]: bucket,
86
86
  [ResourceType.SSTFunction]: noop,
87
87
  [ResourceType.SSTPostgres]: postgres,
88
88
  [ResourceType.SSTApiGatewayV2]: noop,
@@ -126,4 +126,4 @@ Object.defineProperty(exports, 'getLocalIpAddress', {
126
126
  return getLocalIpAddress;
127
127
  }
128
128
  });
129
- //# sourceMappingURL=utils-CtMjuIMR.cjs.map
129
+ //# sourceMappingURL=utils-B1a2UuEO.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-B1a2UuEO.cjs","names":["name: string","value: Secret","key: string","value: Postgres","value: Bucket","value: SnsTopic","_name: string","_value: any","processors: Record<ResourceType, ResourceProcessor<any>>","record: Record<string, Resource | string>","env: Record<string, string>"],"sources":["../src/utils/index.ts"],"sourcesContent":["import os from 'node:os';\nimport snakecase from 'lodash.snakecase';\n\nexport function getLocalIpAddress() {\n\tconst networkInterfaces = os.networkInterfaces();\n\tfor (const interfaceName in networkInterfaces) {\n\t\tconst addresses = networkInterfaces[interfaceName] || [];\n\t\tfor (const addressInfo of addresses) {\n\t\t\t// Look for an IPv4 address that is not internal (loopback)\n\t\t\tif (addressInfo.family === 'IPv4' && !addressInfo.internal) {\n\t\t\t\treturn addressInfo.address;\n\t\t\t}\n\t\t}\n\t}\n\treturn null; // No suitable IP address found\n}\n\nexport function environmentCase(name: string) {\n\treturn snakecase(name)\n\t\t.toUpperCase()\n\t\t.replace(/_\\d+/g, (r) => {\n\t\t\treturn r.replace('_', '');\n\t\t});\n}\nexport enum ResourceType {\n\tApiGatewayV2 = 'sst.aws.ApiGatewayV2',\n\tPostgres = 'sst.aws.Postgres',\n\tFunction = 'sst.aws.Function',\n\tObjectStorage = 'sst.aws.Bucket',\n\tVpc = 'sst.aws.Vpc',\n\tSecret = 'sst.sst.Secret',\n\tSSTSecret = 'sst:sst:Secret',\n\tSSTFunction = 'sst:sst:Function',\n\tSSTApiGatewayV2 = 'sst:aws:ApiGatewayV2',\n\tSSTPostgres = 'sst:aws:Postgres',\n\tSSTObjectStorage = 'sst:aws:Bucket',\n\tSnsTopic = 'sst:aws:SnsTopic',\n}\n\nconst secret = (name: string, value: Secret) => ({\n\t[environmentCase(name)]: value.value,\n});\nconst postgres = (key: string, value: Postgres) => {\n\tconst prefix = `${environmentCase(key)}`;\n\treturn {\n\t\t[`${prefix}_NAME`]: value.database,\n\t\t[`${prefix}_HOST`]: value.host,\n\t\t[`${prefix}_PASSWORD`]: value.password,\n\t\t[`${prefix}_PORT`]: value.port,\n\t\t[`${prefix}_USERNAME`]: value.username,\n\t};\n};\n\nconst bucket = (name: string, value: Bucket) => {\n\tconst prefix = `${environmentCase(name)}`;\n\treturn {\n\t\t[`${prefix}_NAME`]: value.name,\n\t};\n};\n\nconst topic = (name: string, value: SnsTopic) => {\n\tconst prefix = `${environmentCase(name)}`;\n\tconst key = `${prefix}_ARN`;\n\n\treturn {\n\t\t[key]: value.arn,\n\t};\n};\n\nconst noop = (_name: string, _value: any) => ({});\n\nconst processors: Record<ResourceType, ResourceProcessor<any>> = {\n\t[ResourceType.ApiGatewayV2]: noop,\n\t[ResourceType.Function]: noop,\n\t[ResourceType.Vpc]: noop,\n\t[ResourceType.Secret]: secret,\n\t[ResourceType.Postgres]: postgres,\n\t[ResourceType.ObjectStorage]: bucket,\n\n\t[ResourceType.SSTSecret]: secret,\n\t[ResourceType.SSTObjectStorage]: bucket,\n\t[ResourceType.SSTFunction]: noop,\n\t[ResourceType.SSTPostgres]: postgres,\n\t[ResourceType.SSTApiGatewayV2]: noop,\n\t[ResourceType.SnsTopic]: topic,\n};\n\nexport function buildResourceEnv(\n\trecord: Record<string, Resource | string>,\n): Record<string, string> {\n\tconst env: Record<string, string> = {};\n\tfor (const [k, value] of Object.entries(record)) {\n\t\tif (typeof value === 'string') {\n\t\t\tenv[environmentCase(k)] = value;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst processor = processors[value.type];\n\t\tif (processor) {\n\t\t\tObject.assign(env, processor(k, value));\n\t\t} else {\n\t\t}\n\t}\n\n\treturn env;\n}\n\n/**\n * SST links a resource under one of two spellings — `sst.aws.Bucket` when it\n * comes from the state file, `sst:aws:Bucket` when it comes from the runtime —\n * and `processors` has always handled both. Only the dotted half was admitted\n * here, so the colon half could not be passed to `buildResourceEnv` without a\n * cast, and `SnsTopic` was missing from `Resource` altogether despite being the\n * only thing that produces `*_ARN`.\n */\nexport type ApiGatewayV2 = {\n\ttype: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;\n\turl: string;\n};\n\nexport type Postgres = {\n\tdatabase: string;\n\thost: string;\n\tpassword: string;\n\tport: number;\n\ttype: ResourceType.Postgres | ResourceType.SSTPostgres;\n\tusername: string;\n};\n\nexport type Function = {\n\tname: string;\n\ttype: ResourceType.Function | ResourceType.SSTFunction;\n};\n\nexport type Bucket = {\n\tname: string;\n\ttype: ResourceType.ObjectStorage | ResourceType.SSTObjectStorage;\n};\n\nexport type SnsTopic = {\n\tarn: string;\n\ttype: ResourceType.SnsTopic;\n};\n\nexport type Vpc = {\n\tbastion: string;\n\ttype: ResourceType.Vpc;\n};\n\nexport type Secret = {\n\ttype: ResourceType.Secret | ResourceType.SSTSecret;\n\tvalue: string;\n};\n\nexport type Resource =\n\t| ApiGatewayV2\n\t| Postgres\n\t| Function\n\t| Bucket\n\t| SnsTopic\n\t| Vpc\n\t| Secret;\n\nexport type ResourceProcessor<K extends Resource> = (\n\tname: string,\n\tvalue: K,\n) => Record<string, string | number>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,SAAgB,oBAAoB;CACnC,MAAM,oBAAoB,gBAAG,mBAAmB;AAChD,MAAK,MAAM,iBAAiB,mBAAmB;EAC9C,MAAM,YAAY,kBAAkB,kBAAkB,CAAE;AACxD,OAAK,MAAM,eAAe,UAEzB,KAAI,YAAY,WAAW,WAAW,YAAY,SACjD,QAAO,YAAY;CAGrB;AACD,QAAO;AACP;AAED,SAAgB,gBAAgBA,MAAc;AAC7C,QAAO,8BAAU,KAAK,CACpB,aAAa,CACb,QAAQ,SAAS,CAAC,MAAM;AACxB,SAAO,EAAE,QAAQ,KAAK,GAAG;CACzB,EAAC;AACH;AACD,IAAY,wDAAL;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA;AAED,MAAM,SAAS,CAACA,MAAcC,WAAmB,GAC/C,gBAAgB,KAAK,GAAG,MAAM,MAC/B;AACD,MAAM,WAAW,CAACC,KAAaC,UAAoB;CAClD,MAAM,UAAU,EAAE,gBAAgB,IAAI,CAAC;AACvC,QAAO;IACJ,EAAE,OAAO,SAAS,MAAM;IACxB,EAAE,OAAO,SAAS,MAAM;IACxB,EAAE,OAAO,aAAa,MAAM;IAC5B,EAAE,OAAO,SAAS,MAAM;IACxB,EAAE,OAAO,aAAa,MAAM;CAC9B;AACD;AAED,MAAM,SAAS,CAACH,MAAcI,UAAkB;CAC/C,MAAM,UAAU,EAAE,gBAAgB,KAAK,CAAC;AACxC,QAAO,IACJ,EAAE,OAAO,SAAS,MAAM,KAC1B;AACD;AAED,MAAM,QAAQ,CAACJ,MAAcK,UAAoB;CAChD,MAAM,UAAU,EAAE,gBAAgB,KAAK,CAAC;CACxC,MAAM,OAAO,EAAE,OAAO;AAEtB,QAAO,GACL,MAAM,MAAM,IACb;AACD;AAED,MAAM,OAAO,CAACC,OAAeC,YAAiB,CAAE;AAEhD,MAAMC,aAA2D;EAC/D,aAAa,eAAe;EAC5B,aAAa,WAAW;EACxB,aAAa,MAAM;EACnB,aAAa,SAAS;EACtB,aAAa,WAAW;EACxB,aAAa,gBAAgB;EAE7B,aAAa,YAAY;EACzB,aAAa,mBAAmB;EAChC,aAAa,cAAc;EAC3B,aAAa,cAAc;EAC3B,aAAa,kBAAkB;EAC/B,aAAa,WAAW;AACzB;AAED,SAAgB,iBACfC,QACyB;CACzB,MAAMC,MAA8B,CAAE;AACtC,MAAK,MAAM,CAAC,GAAG,MAAM,IAAI,OAAO,QAAQ,OAAO,EAAE;AAChD,aAAW,UAAU,UAAU;AAC9B,OAAI,gBAAgB,EAAE,IAAI;AAC1B;EACA;EAED,MAAM,YAAY,WAAW,MAAM;AACnC,MAAI,UACH,QAAO,OAAO,KAAK,UAAU,GAAG,MAAM,CAAC;CAGxC;AAED,QAAO;AACP"}
@@ -19,14 +19,14 @@ let ResourceType = /* @__PURE__ */ function(ResourceType$1) {
19
19
  ResourceType$1["ApiGatewayV2"] = "sst.aws.ApiGatewayV2";
20
20
  ResourceType$1["Postgres"] = "sst.aws.Postgres";
21
21
  ResourceType$1["Function"] = "sst.aws.Function";
22
- ResourceType$1["Bucket"] = "sst.aws.Bucket";
22
+ ResourceType$1["ObjectStorage"] = "sst.aws.Bucket";
23
23
  ResourceType$1["Vpc"] = "sst.aws.Vpc";
24
24
  ResourceType$1["Secret"] = "sst.sst.Secret";
25
25
  ResourceType$1["SSTSecret"] = "sst:sst:Secret";
26
26
  ResourceType$1["SSTFunction"] = "sst:sst:Function";
27
27
  ResourceType$1["SSTApiGatewayV2"] = "sst:aws:ApiGatewayV2";
28
28
  ResourceType$1["SSTPostgres"] = "sst:aws:Postgres";
29
- ResourceType$1["SSTBucket"] = "sst:aws:Bucket";
29
+ ResourceType$1["SSTObjectStorage"] = "sst:aws:Bucket";
30
30
  ResourceType$1["SnsTopic"] = "sst:aws:SnsTopic";
31
31
  return ResourceType$1;
32
32
  }({});
@@ -57,9 +57,9 @@ const processors = {
57
57
  [ResourceType.Vpc]: noop,
58
58
  [ResourceType.Secret]: secret,
59
59
  [ResourceType.Postgres]: postgres,
60
- [ResourceType.Bucket]: bucket,
60
+ [ResourceType.ObjectStorage]: bucket,
61
61
  [ResourceType.SSTSecret]: secret,
62
- [ResourceType.SSTBucket]: bucket,
62
+ [ResourceType.SSTObjectStorage]: bucket,
63
63
  [ResourceType.SSTFunction]: noop,
64
64
  [ResourceType.SSTPostgres]: postgres,
65
65
  [ResourceType.SSTApiGatewayV2]: noop,
@@ -80,4 +80,4 @@ function buildResourceEnv(record) {
80
80
 
81
81
  //#endregion
82
82
  export { ResourceType, buildResourceEnv, environmentCase, getLocalIpAddress };
83
- //# sourceMappingURL=utils-BdKG20_m.mjs.map
83
+ //# sourceMappingURL=utils-DMOXJ27j.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-DMOXJ27j.mjs","names":["name: string","value: Secret","key: string","value: Postgres","value: Bucket","value: SnsTopic","_name: string","_value: any","processors: Record<ResourceType, ResourceProcessor<any>>","record: Record<string, Resource | string>","env: Record<string, string>"],"sources":["../src/utils/index.ts"],"sourcesContent":["import os from 'node:os';\nimport snakecase from 'lodash.snakecase';\n\nexport function getLocalIpAddress() {\n\tconst networkInterfaces = os.networkInterfaces();\n\tfor (const interfaceName in networkInterfaces) {\n\t\tconst addresses = networkInterfaces[interfaceName] || [];\n\t\tfor (const addressInfo of addresses) {\n\t\t\t// Look for an IPv4 address that is not internal (loopback)\n\t\t\tif (addressInfo.family === 'IPv4' && !addressInfo.internal) {\n\t\t\t\treturn addressInfo.address;\n\t\t\t}\n\t\t}\n\t}\n\treturn null; // No suitable IP address found\n}\n\nexport function environmentCase(name: string) {\n\treturn snakecase(name)\n\t\t.toUpperCase()\n\t\t.replace(/_\\d+/g, (r) => {\n\t\t\treturn r.replace('_', '');\n\t\t});\n}\nexport enum ResourceType {\n\tApiGatewayV2 = 'sst.aws.ApiGatewayV2',\n\tPostgres = 'sst.aws.Postgres',\n\tFunction = 'sst.aws.Function',\n\tObjectStorage = 'sst.aws.Bucket',\n\tVpc = 'sst.aws.Vpc',\n\tSecret = 'sst.sst.Secret',\n\tSSTSecret = 'sst:sst:Secret',\n\tSSTFunction = 'sst:sst:Function',\n\tSSTApiGatewayV2 = 'sst:aws:ApiGatewayV2',\n\tSSTPostgres = 'sst:aws:Postgres',\n\tSSTObjectStorage = 'sst:aws:Bucket',\n\tSnsTopic = 'sst:aws:SnsTopic',\n}\n\nconst secret = (name: string, value: Secret) => ({\n\t[environmentCase(name)]: value.value,\n});\nconst postgres = (key: string, value: Postgres) => {\n\tconst prefix = `${environmentCase(key)}`;\n\treturn {\n\t\t[`${prefix}_NAME`]: value.database,\n\t\t[`${prefix}_HOST`]: value.host,\n\t\t[`${prefix}_PASSWORD`]: value.password,\n\t\t[`${prefix}_PORT`]: value.port,\n\t\t[`${prefix}_USERNAME`]: value.username,\n\t};\n};\n\nconst bucket = (name: string, value: Bucket) => {\n\tconst prefix = `${environmentCase(name)}`;\n\treturn {\n\t\t[`${prefix}_NAME`]: value.name,\n\t};\n};\n\nconst topic = (name: string, value: SnsTopic) => {\n\tconst prefix = `${environmentCase(name)}`;\n\tconst key = `${prefix}_ARN`;\n\n\treturn {\n\t\t[key]: value.arn,\n\t};\n};\n\nconst noop = (_name: string, _value: any) => ({});\n\nconst processors: Record<ResourceType, ResourceProcessor<any>> = {\n\t[ResourceType.ApiGatewayV2]: noop,\n\t[ResourceType.Function]: noop,\n\t[ResourceType.Vpc]: noop,\n\t[ResourceType.Secret]: secret,\n\t[ResourceType.Postgres]: postgres,\n\t[ResourceType.ObjectStorage]: bucket,\n\n\t[ResourceType.SSTSecret]: secret,\n\t[ResourceType.SSTObjectStorage]: bucket,\n\t[ResourceType.SSTFunction]: noop,\n\t[ResourceType.SSTPostgres]: postgres,\n\t[ResourceType.SSTApiGatewayV2]: noop,\n\t[ResourceType.SnsTopic]: topic,\n};\n\nexport function buildResourceEnv(\n\trecord: Record<string, Resource | string>,\n): Record<string, string> {\n\tconst env: Record<string, string> = {};\n\tfor (const [k, value] of Object.entries(record)) {\n\t\tif (typeof value === 'string') {\n\t\t\tenv[environmentCase(k)] = value;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst processor = processors[value.type];\n\t\tif (processor) {\n\t\t\tObject.assign(env, processor(k, value));\n\t\t} else {\n\t\t}\n\t}\n\n\treturn env;\n}\n\n/**\n * SST links a resource under one of two spellings — `sst.aws.Bucket` when it\n * comes from the state file, `sst:aws:Bucket` when it comes from the runtime —\n * and `processors` has always handled both. Only the dotted half was admitted\n * here, so the colon half could not be passed to `buildResourceEnv` without a\n * cast, and `SnsTopic` was missing from `Resource` altogether despite being the\n * only thing that produces `*_ARN`.\n */\nexport type ApiGatewayV2 = {\n\ttype: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;\n\turl: string;\n};\n\nexport type Postgres = {\n\tdatabase: string;\n\thost: string;\n\tpassword: string;\n\tport: number;\n\ttype: ResourceType.Postgres | ResourceType.SSTPostgres;\n\tusername: string;\n};\n\nexport type Function = {\n\tname: string;\n\ttype: ResourceType.Function | ResourceType.SSTFunction;\n};\n\nexport type Bucket = {\n\tname: string;\n\ttype: ResourceType.ObjectStorage | ResourceType.SSTObjectStorage;\n};\n\nexport type SnsTopic = {\n\tarn: string;\n\ttype: ResourceType.SnsTopic;\n};\n\nexport type Vpc = {\n\tbastion: string;\n\ttype: ResourceType.Vpc;\n};\n\nexport type Secret = {\n\ttype: ResourceType.Secret | ResourceType.SSTSecret;\n\tvalue: string;\n};\n\nexport type Resource =\n\t| ApiGatewayV2\n\t| Postgres\n\t| Function\n\t| Bucket\n\t| SnsTopic\n\t| Vpc\n\t| Secret;\n\nexport type ResourceProcessor<K extends Resource> = (\n\tname: string,\n\tvalue: K,\n) => Record<string, string | number>;\n"],"mappings":";;;;AAGA,SAAgB,oBAAoB;CACnC,MAAM,oBAAoB,GAAG,mBAAmB;AAChD,MAAK,MAAM,iBAAiB,mBAAmB;EAC9C,MAAM,YAAY,kBAAkB,kBAAkB,CAAE;AACxD,OAAK,MAAM,eAAe,UAEzB,KAAI,YAAY,WAAW,WAAW,YAAY,SACjD,QAAO,YAAY;CAGrB;AACD,QAAO;AACP;AAED,SAAgB,gBAAgBA,MAAc;AAC7C,QAAO,UAAU,KAAK,CACpB,aAAa,CACb,QAAQ,SAAS,CAAC,MAAM;AACxB,SAAO,EAAE,QAAQ,KAAK,GAAG;CACzB,EAAC;AACH;AACD,IAAY,wDAAL;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA;AAED,MAAM,SAAS,CAACA,MAAcC,WAAmB,GAC/C,gBAAgB,KAAK,GAAG,MAAM,MAC/B;AACD,MAAM,WAAW,CAACC,KAAaC,UAAoB;CAClD,MAAM,UAAU,EAAE,gBAAgB,IAAI,CAAC;AACvC,QAAO;IACJ,EAAE,OAAO,SAAS,MAAM;IACxB,EAAE,OAAO,SAAS,MAAM;IACxB,EAAE,OAAO,aAAa,MAAM;IAC5B,EAAE,OAAO,SAAS,MAAM;IACxB,EAAE,OAAO,aAAa,MAAM;CAC9B;AACD;AAED,MAAM,SAAS,CAACH,MAAcI,UAAkB;CAC/C,MAAM,UAAU,EAAE,gBAAgB,KAAK,CAAC;AACxC,QAAO,IACJ,EAAE,OAAO,SAAS,MAAM,KAC1B;AACD;AAED,MAAM,QAAQ,CAACJ,MAAcK,UAAoB;CAChD,MAAM,UAAU,EAAE,gBAAgB,KAAK,CAAC;CACxC,MAAM,OAAO,EAAE,OAAO;AAEtB,QAAO,GACL,MAAM,MAAM,IACb;AACD;AAED,MAAM,OAAO,CAACC,OAAeC,YAAiB,CAAE;AAEhD,MAAMC,aAA2D;EAC/D,aAAa,eAAe;EAC5B,aAAa,WAAW;EACxB,aAAa,MAAM;EACnB,aAAa,SAAS;EACtB,aAAa,WAAW;EACxB,aAAa,gBAAgB;EAE7B,aAAa,YAAY;EACzB,aAAa,mBAAmB;EAChC,aAAa,cAAc;EAC3B,aAAa,cAAc;EAC3B,aAAa,kBAAkB;EAC/B,aAAa,WAAW;AACzB;AAED,SAAgB,iBACfC,QACyB;CACzB,MAAMC,MAA8B,CAAE;AACtC,MAAK,MAAM,CAAC,GAAG,MAAM,IAAI,OAAO,QAAQ,OAAO,EAAE;AAChD,aAAW,UAAU,UAAU;AAC9B,OAAI,gBAAgB,EAAE,IAAI;AAC1B;EACA;EAED,MAAM,YAAY,WAAW,MAAM;AACnC,MAAI,UACH,QAAO,OAAO,KAAK,UAAU,GAAG,MAAM,CAAC;CAGxC;AAED,QAAO;AACP"}
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@geekmidas/cloud",
3
- "version": "9.0.2",
3
+ "version": "10.0.0-alpha.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
7
- "src/sst"
7
+ "src/sst",
8
+ "src/dokploy"
8
9
  ],
9
10
  "exports": {
10
11
  ".": {
@@ -27,7 +28,8 @@
27
28
  "default": "./dist/utils/index.cjs"
28
29
  }
29
30
  },
30
- "./sst": "./src/sst/index.ts"
31
+ "./sst": "./src/sst/index.ts",
32
+ "./dokploy": "./src/dokploy/index.ts"
31
33
  },
32
34
  "repository": {
33
35
  "type": "git",
@@ -39,21 +41,56 @@
39
41
  },
40
42
  "dependencies": {
41
43
  "lodash.snakecase": "~4.1.1",
42
- "@geekmidas/envkit": "9.0.2",
43
- "@geekmidas/manifest": "9.0.2"
44
+ "@geekmidas/envkit": "10.0.0-alpha.1",
45
+ "@geekmidas/manifest": "10.0.0-alpha.1"
44
46
  },
45
47
  "devDependencies": {
46
48
  "@types/lodash.get": "~4.4.9",
47
49
  "@types/lodash.set": "~4.3.9",
48
50
  "@types/lodash.snakecase": "~4.1.9",
49
51
  "@types/node": "~24.9.1",
52
+ "@types/pg": "~8.15.5",
50
53
  "sst": "4.15.2"
51
54
  },
52
55
  "peerDependencies": {
53
- "sst": "^4.15.2"
56
+ "sst": "^4.15.2",
57
+ "pg": "^8.13.0",
58
+ "@pulumi/aws": "^7.20.0",
59
+ "@pulumi/random": "^4.16.6",
60
+ "@pulumi/pulumi": "^3.215.0",
61
+ "@geekmidas/manifest": "^10.0.0-alpha.1",
62
+ "@geekmidas/storage": "^10.0.0-alpha.1",
63
+ "@geekmidas/events": "^10.0.0-alpha.1",
64
+ "@geekmidas/db": "^10.0.0-alpha.1"
65
+ },
66
+ "peerDependenciesMeta": {
67
+ "@geekmidas/manifest": {
68
+ "optional": true
69
+ },
70
+ "@geekmidas/storage": {
71
+ "optional": true
72
+ },
73
+ "@geekmidas/events": {
74
+ "optional": true
75
+ },
76
+ "@geekmidas/db": {
77
+ "optional": true
78
+ },
79
+ "pg": {
80
+ "optional": true
81
+ },
82
+ "@pulumi/aws": {
83
+ "optional": true
84
+ },
85
+ "@pulumi/random": {
86
+ "optional": true
87
+ },
88
+ "@pulumi/pulumi": {
89
+ "optional": true
90
+ }
54
91
  },
55
92
  "scripts": {
56
- "sst:install": "sst install",
93
+ "sst:install": "node -e \"process.exit(require('fs').existsSync('sst.config.ts')?0:1)\" && sst install || true",
57
94
  "ts:check:sst": "tsc --noEmit -p src/sst/tsconfig.json 2>&1 | grep -E 'src/sst/.*error TS' && exit 1 || exit 0"
58
95
  }
59
96
  }
@@ -0,0 +1,259 @@
1
+ import { dynamic, type Input, type Output } from '@pulumi/pulumi';
2
+
3
+ /**
4
+ * A Dokploy application, as a Pulumi resource.
5
+ *
6
+ * **A prototype, and the point of it is the shape rather than the coverage.**
7
+ * One resource, to see whether wrapping Dokploy's REST API in a dynamic provider
8
+ * is worth doing for the rest — because if it is, a whole subsystem goes away.
9
+ *
10
+ * ## Why this is worth trying
11
+ *
12
+ * `packages/cli/src/deploy/` is a deployment engine written by hand: it calls
13
+ * the API, and it remembers what it created in `deploy/state.ts` and
14
+ * `SSMStateProvider` so the next deploy can find the same resources. That second
15
+ * half is the interesting one, because *remembering what you created* is the
16
+ * entire job of a Pulumi state file. A provider does not need it.
17
+ *
18
+ * What else falls out: `pulumi preview` starts working for the server target,
19
+ * and `fromManifest` becomes one shape across both targets — a table of
20
+ * provisioners — rather than an SST adapter beside a bespoke engine.
21
+ *
22
+ * ## Why it is a dynamic provider and not a real one
23
+ *
24
+ * A real Pulumi provider is a plugin binary with a schema, usually generated
25
+ * from a Terraform provider that does not exist here. A dynamic provider is a
26
+ * TypeScript object with `create`/`read`/`update`/`delete`, running in-process
27
+ * during the deploy. SST itself uses this exact escape hatch wherever official
28
+ * coverage stops — `vercel/providers/dns-record.ts`,
29
+ * `cloudflare/providers/kv-data.ts` — which is the precedent worth leaning on.
30
+ *
31
+ * ## The three things that make it risky
32
+ *
33
+ * 1. **The provider is serialised into state.** Pulumi captures the functions
34
+ * below and stores them, so they must be self-contained — no reaching for an
35
+ * import from the surrounding module at call time. That is why the API token
36
+ * is an *input* rather than something closed over, and why `fetch` is used
37
+ * directly instead of `DokployApi`, which would have to serialise with it.
38
+ * 2. **`delete` runs on destroy, and orphans are silent.** Today's engine fails
39
+ * loudly when it cannot find something; a provider that gets destroy wrong
40
+ * leaves resources behind that Pulumi believes it removed. This is the half
41
+ * to be most careful with, and the reason a prototype exists at all.
42
+ * 3. **`diff` is yours.** Without it, preview cannot say what a change will do —
43
+ * which is one of the main reasons for doing this.
44
+ *
45
+ * ## Not verified
46
+ *
47
+ * No Dokploy instance has run this. The shape type-checks and the decisions are
48
+ * assertable; nothing has been created or destroyed against a real server.
49
+ */
50
+
51
+ interface ApplicationInputs {
52
+ /** Where the Dokploy server answers, e.g. `https://dokploy.example.com`. */
53
+ endpoint: string;
54
+ /**
55
+ * The API token.
56
+ *
57
+ * An input rather than a captured variable, because the provider's functions
58
+ * are serialised into state and a closure over module scope does not survive
59
+ * that. SST's own dynamic providers pass credentials the same way.
60
+ */
61
+ apiToken: string;
62
+ name: string;
63
+ projectId: string;
64
+ environmentId: string;
65
+ }
66
+
67
+ interface ApplicationOutputs extends ApplicationInputs {
68
+ applicationId: string;
69
+ appName: string;
70
+ }
71
+
72
+ /**
73
+ * Dokploy's app name rule, applied where the name is decided.
74
+ *
75
+ * The existing client does this inline at the create call; here it is a function
76
+ * because `diff` has to ask whether a *changed* name produces a changed app
77
+ * name, and duplicating the expression is how those two answers drift apart.
78
+ */
79
+ export function appNameFor(name: string): string {
80
+ return name.toLowerCase().replace(/[^a-z0-9-]/g, '-');
81
+ }
82
+
83
+ /**
84
+ * What a change costs — pure, so preview's behaviour can be asserted without a
85
+ * Dokploy server.
86
+ *
87
+ * The name is the interesting one: Dokploy derives `appName` from it, and
88
+ * changing that is not an update but a different application, so the old one has
89
+ * to go. Saying so here is what makes `pulumi preview` warn *before* the
90
+ * destroy rather than reporting it after.
91
+ */
92
+ export function diffApplication(
93
+ olds: { name: string; projectId: string; environmentId: string },
94
+ news: { name: string; projectId: string; environmentId: string },
95
+ ): { changes: boolean; replaces: string[]; deleteBeforeReplace: boolean } {
96
+ const replaces: string[] = [];
97
+
98
+ if (appNameFor(olds.name) !== appNameFor(news.name)) replaces.push('name');
99
+ if (olds.projectId !== news.projectId) replaces.push('projectId');
100
+ if (olds.environmentId !== news.environmentId) replaces.push('environmentId');
101
+
102
+ return {
103
+ changes: replaces.length > 0 || olds.name !== news.name,
104
+ replaces,
105
+ // Create the replacement before removing the old one. A Dokploy project
106
+ // can hold both for a moment; it cannot hold a gap where the application
107
+ // used to be.
108
+ deleteBeforeReplace: false,
109
+ };
110
+ }
111
+
112
+ const provider: dynamic.ResourceProvider<
113
+ ApplicationInputs,
114
+ ApplicationOutputs
115
+ > = {
116
+ async create(inputs) {
117
+ const application = await call<{
118
+ applicationId: string;
119
+ appName: string;
120
+ }>(inputs, 'application.create', {
121
+ name: inputs.name,
122
+ projectId: inputs.projectId,
123
+ environmentId: inputs.environmentId,
124
+ appName: appNameFor(inputs.name),
125
+ });
126
+
127
+ return {
128
+ // Pulumi's own id for the resource. Using Dokploy's makes the two
129
+ // agree, so a state file read by a human names the same thing the
130
+ // Dokploy UI does.
131
+ id: application.applicationId,
132
+ outs: { ...inputs, ...application },
133
+ };
134
+ },
135
+
136
+ /**
137
+ * Refresh from the server.
138
+ *
139
+ * Returning no id is how a provider says "this is gone", which is what
140
+ * lets `pulumi refresh` notice something deleted in the Dokploy UI rather
141
+ * than failing on the next update against a resource that is not there.
142
+ */
143
+ async read(id, props) {
144
+ const application = await call<{
145
+ applicationId: string;
146
+ appName: string;
147
+ } | null>(
148
+ props as ApplicationInputs,
149
+ `application.one?applicationId=${id}`,
150
+ undefined,
151
+ );
152
+
153
+ if (!application) return { id: undefined };
154
+
155
+ return { id, props: { ...(props as ApplicationOutputs), ...application } };
156
+ },
157
+
158
+ /**
159
+ * What a change costs.
160
+ *
161
+ * The name is the interesting one: Dokploy derives `appName` from it, and
162
+ * changing that is not an update — it is a different application, so the
163
+ * old one has to go. Saying so here is what makes `pulumi preview` warn
164
+ * before the destroy rather than after.
165
+ */
166
+ async diff(_id, olds, news) {
167
+ return diffApplication(olds, news);
168
+ },
169
+
170
+ async update(id, _olds, news) {
171
+ await call(news, 'application.update', {
172
+ applicationId: id,
173
+ name: news.name,
174
+ });
175
+
176
+ return {
177
+ outs: { ...news, applicationId: id, appName: appNameFor(news.name) },
178
+ };
179
+ },
180
+
181
+ /**
182
+ * Remove it, and treat "already gone" as success.
183
+ *
184
+ * A destroy that fails because somebody removed the application by hand
185
+ * leaves the stack unable to make progress, which is worse than the thing
186
+ * it is protecting against — the resource is absent either way, which is
187
+ * what was asked for.
188
+ */
189
+ async delete(id, props) {
190
+ try {
191
+ await call(props, 'application.remove', { applicationId: id });
192
+ } catch (error) {
193
+ if (!isNotFound(error)) throw error;
194
+ }
195
+ },
196
+ };
197
+
198
+ /**
199
+ * One call to Dokploy.
200
+ *
201
+ * Written out rather than reusing `DokployApi` because the provider's functions
202
+ * are serialised into state: a class imported from another module would have to
203
+ * serialise too, and the failure when it cannot is obscure. `fetch` is a global.
204
+ */
205
+ async function call<T>(
206
+ auth: { endpoint: string; apiToken: string },
207
+ path: string,
208
+ body?: Record<string, unknown>,
209
+ ): Promise<T> {
210
+ const response = await fetch(`${auth.endpoint}/api/trpc/${path}`, {
211
+ method: body ? 'POST' : 'GET',
212
+ headers: {
213
+ 'x-api-key': auth.apiToken,
214
+ 'content-type': 'application/json',
215
+ },
216
+ ...(body ? { body: JSON.stringify(body) } : {}),
217
+ });
218
+
219
+ if (!response.ok) {
220
+ const detail = await response.text().catch(() => '');
221
+ throw new Error(
222
+ `Dokploy ${path} failed: ${response.status} ${response.statusText}${
223
+ detail ? ` — ${detail}` : ''
224
+ }`,
225
+ );
226
+ }
227
+
228
+ return (await response.json()) as T;
229
+ }
230
+
231
+ /** Whether an error means the thing is already absent. */
232
+ function isNotFound(error: unknown): boolean {
233
+ return error instanceof Error && /\b404\b/.test(error.message);
234
+ }
235
+
236
+ export interface ApplicationArgs {
237
+ endpoint: Input<string>;
238
+ apiToken: Input<string>;
239
+ name: Input<string>;
240
+ projectId: Input<string>;
241
+ environmentId: Input<string>;
242
+ }
243
+
244
+ export class Application extends dynamic.Resource {
245
+ declare readonly applicationId: Output<string>;
246
+ declare readonly appName: Output<string>;
247
+
248
+ constructor(name: string, args: ApplicationArgs, opts?: object) {
249
+ super(
250
+ provider,
251
+ name,
252
+ // The outputs have to be declared as undefined inputs or Pulumi will
253
+ // not populate them — a quiet rule, and the usual first thing to get
254
+ // wrong with a dynamic provider.
255
+ { ...args, applicationId: undefined, appName: undefined },
256
+ opts,
257
+ );
258
+ }
259
+ }