@geekmidas/cloud 10.0.0-alpha.6 → 10.0.0-alpha.60

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 (48) hide show
  1. package/README.md +14 -6
  2. package/dist/index.cjs +3 -3
  3. package/dist/index.d.cts +1 -1
  4. package/dist/index.d.mts +1 -1
  5. package/dist/index.mjs +2 -3
  6. package/dist/utils/index.cjs +106 -5
  7. package/dist/utils/index.cjs.map +1 -0
  8. package/dist/utils/index.d.cts +62 -2
  9. package/dist/utils/index.d.cts.map +1 -0
  10. package/dist/utils/index.d.mts +62 -2
  11. package/dist/utils/index.d.mts.map +1 -0
  12. package/dist/utils/index.mjs +78 -2
  13. package/dist/utils/index.mjs.map +1 -0
  14. package/package.json +7 -7
  15. package/src/dokploy/Application.ts +52 -5
  16. package/src/dokploy/__tests__/Application.provider.spec.ts +206 -0
  17. package/src/sst/__tests__/api.spec.ts +185 -0
  18. package/src/sst/__tests__/encryption.spec.ts +90 -0
  19. package/src/sst/__tests__/fromManifest.integration.spec.ts +427 -0
  20. package/src/sst/__tests__/fromManifest.spec.ts +306 -5
  21. package/src/sst/__tests__/function.spec.ts +121 -0
  22. package/src/sst/__tests__/links.spec.ts +209 -0
  23. package/src/sst/__tests__/surfaces.spec.ts +17 -0
  24. package/src/sst/__type-tests__/overrides.type-test.ts +152 -0
  25. package/src/sst/aws/Api.ts +16 -44
  26. package/src/sst/aws/Cache.ts +4 -4
  27. package/src/sst/aws/Credential.ts +42 -9
  28. package/src/sst/aws/Cron.ts +5 -45
  29. package/src/sst/aws/Database.ts +15 -1
  30. package/src/sst/aws/DatabaseBootstrap.ts +3 -0
  31. package/src/sst/aws/Encryption.ts +91 -0
  32. package/src/sst/aws/ExternalApi.ts +58 -0
  33. package/src/sst/aws/Function.ts +9 -38
  34. package/src/sst/aws/Queue.ts +34 -1
  35. package/src/sst/aws/RestApiSurface.ts +36 -14
  36. package/src/sst/aws/bootstrap/handler.ts +10 -1
  37. package/src/sst/fromManifest.ts +253 -44
  38. package/src/sst/index.ts +6 -1
  39. package/src/sst/overrides.ts +133 -0
  40. package/dist/index-B5CZ1xVf.d.cts +0 -64
  41. package/dist/index-B5CZ1xVf.d.cts.map +0 -1
  42. package/dist/index-DhHRjduZ.d.mts +0 -64
  43. package/dist/index-DhHRjduZ.d.mts.map +0 -1
  44. package/dist/utils-B1a2UuEO.cjs +0 -129
  45. package/dist/utils-B1a2UuEO.cjs.map +0 -1
  46. package/dist/utils-DMOXJ27j.mjs +0 -83
  47. package/dist/utils-DMOXJ27j.mjs.map +0 -1
  48. package/src/sst/__type-tests__/manifest.type-test.ts +0 -77
package/README.md CHANGED
@@ -105,17 +105,25 @@ const cron = new Cron(stack, 'Nightly', { processor: fn, schedule: 'rate(1 day)'
105
105
 
106
106
  ### From a `gkm build` manifest
107
107
 
108
- `gkm build` emits a deployment manifest (types in `@geekmidas/manifest`). Each
109
- construct has a static `fromManifest` factory that maps it straight into infra:
108
+ The root `gkm build` writes the application's manifest to
109
+ `.gkm/manifest/aws.ts` (types in `@geekmidas/manifest`), and `fromManifest`
110
+ maps it straight into infra:
110
111
 
111
112
  ```ts
112
- import routes from './.gkm/routes-manifest.json';
113
+ // sst.config.ts
114
+ const { App, fromManifest, Stack } = await import('@geekmidas/cloud/sst');
115
+ const { backends, constructs } = await import('./.gkm/manifest/aws.js');
113
116
 
114
- const api = Api.fromManifest(stack, 'Api', routes, { links: [db], authorizers });
115
- const workers = Function.fromManifest(stack, functionsManifest, { links: [db] });
116
- const crons = Cron.fromManifest(stack, cronsManifest, { links: [db] });
117
+ fromManifest(new Stack(app, 'Shop'), constructs, { Database: { vpc } }, backends);
117
118
  ```
118
119
 
120
+ `constructs` is every declared construct keyed by id; the overrides are keyed
121
+ by the same ids. One call provisions the resources, each `RestApi`'s endpoints
122
+ (each a Lambda linked only to its own dependencies, placed in the database's
123
+ VPC when it reaches one, with the `iam` authorizer enforced by the gateway),
124
+ queue consumers, every function (a Lambda with an IAM-authorized URL) and every
125
+ cron (a Lambda on its schedule).
126
+
119
127
  ## `@geekmidas/cloud/utils` (runtime)
120
128
 
121
129
  ```ts
package/dist/index.cjs CHANGED
@@ -1,3 +1,3 @@
1
- const require_utils = require('./utils-B1a2UuEO.cjs');
2
-
3
- exports.buildResourceEnv = require_utils.buildResourceEnv;
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_utils_index = require("./utils/index.cjs");
3
+ exports.buildResourceEnv = require_utils_index.buildResourceEnv;
package/dist/index.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { buildResourceEnv } from "./index-B5CZ1xVf.cjs";
1
+ import { buildResourceEnv } from "./utils/index.cjs";
2
2
  export { buildResourceEnv };
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { buildResourceEnv } from "./index-DhHRjduZ.mjs";
1
+ import { buildResourceEnv } from "./utils/index.mjs";
2
2
  export { buildResourceEnv };
package/dist/index.mjs CHANGED
@@ -1,3 +1,2 @@
1
- import { buildResourceEnv } from "./utils-DMOXJ27j.mjs";
2
-
3
- export { buildResourceEnv };
1
+ import { buildResourceEnv } from "./utils/index.mjs";
2
+ export { buildResourceEnv };
@@ -1,6 +1,107 @@
1
- const require_utils = require('../utils-B1a2UuEO.cjs');
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
24
+ let node_os = require("node:os");
25
+ node_os = __toESM(node_os, 1);
26
+ let lodash_snakecase = require("lodash.snakecase");
27
+ lodash_snakecase = __toESM(lodash_snakecase, 1);
28
+ //#region src/utils/index.ts
29
+ function getLocalIpAddress() {
30
+ const networkInterfaces = node_os.default.networkInterfaces();
31
+ for (const interfaceName in networkInterfaces) {
32
+ const addresses = networkInterfaces[interfaceName] || [];
33
+ for (const addressInfo of addresses) if (addressInfo.family === "IPv4" && !addressInfo.internal) return addressInfo.address;
34
+ }
35
+ return null;
36
+ }
37
+ function environmentCase(name) {
38
+ return (0, lodash_snakecase.default)(name).toUpperCase().replace(/_\d+/g, (r) => {
39
+ return r.replace("_", "");
40
+ });
41
+ }
42
+ let ResourceType = /* @__PURE__ */ function(ResourceType) {
43
+ ResourceType["ApiGatewayV2"] = "sst.aws.ApiGatewayV2";
44
+ ResourceType["Postgres"] = "sst.aws.Postgres";
45
+ ResourceType["Function"] = "sst.aws.Function";
46
+ ResourceType["ObjectStorage"] = "sst.aws.Bucket";
47
+ ResourceType["Vpc"] = "sst.aws.Vpc";
48
+ ResourceType["Secret"] = "sst.sst.Secret";
49
+ ResourceType["SSTSecret"] = "sst:sst:Secret";
50
+ ResourceType["SSTFunction"] = "sst:sst:Function";
51
+ ResourceType["SSTApiGatewayV2"] = "sst:aws:ApiGatewayV2";
52
+ ResourceType["SSTPostgres"] = "sst:aws:Postgres";
53
+ ResourceType["SSTObjectStorage"] = "sst:aws:Bucket";
54
+ ResourceType["SnsTopic"] = "sst:aws:SnsTopic";
55
+ return ResourceType;
56
+ }({});
57
+ const secret = (name, value) => ({ [environmentCase(name)]: value.value });
58
+ const postgres = (key, value) => {
59
+ const prefix = `${environmentCase(key)}`;
60
+ return {
61
+ [`${prefix}_NAME`]: value.database,
62
+ [`${prefix}_HOST`]: value.host,
63
+ [`${prefix}_PASSWORD`]: value.password,
64
+ [`${prefix}_PORT`]: value.port,
65
+ [`${prefix}_USERNAME`]: value.username
66
+ };
67
+ };
68
+ const bucket = (name, value) => {
69
+ return { [`${`${environmentCase(name)}`}_NAME`]: value.name };
70
+ };
71
+ const topic = (name, value) => {
72
+ return { [`${`${environmentCase(name)}`}_ARN`]: value.arn };
73
+ };
74
+ const noop = (_name, _value) => ({});
75
+ const processors = {
76
+ ["sst.aws.ApiGatewayV2"]: noop,
77
+ ["sst.aws.Function"]: noop,
78
+ ["sst.aws.Vpc"]: noop,
79
+ ["sst.sst.Secret"]: secret,
80
+ ["sst.aws.Postgres"]: postgres,
81
+ ["sst.aws.Bucket"]: bucket,
82
+ ["sst:sst:Secret"]: secret,
83
+ ["sst:aws:Bucket"]: bucket,
84
+ ["sst:sst:Function"]: noop,
85
+ ["sst:aws:Postgres"]: postgres,
86
+ ["sst:aws:ApiGatewayV2"]: noop,
87
+ ["sst:aws:SnsTopic"]: topic
88
+ };
89
+ function buildResourceEnv(record) {
90
+ const env = {};
91
+ for (const [k, value] of Object.entries(record)) {
92
+ if (typeof value === "string") {
93
+ env[environmentCase(k)] = value;
94
+ continue;
95
+ }
96
+ const processor = processors[value.type];
97
+ if (processor) Object.assign(env, processor(k, value));
98
+ }
99
+ return env;
100
+ }
101
+ //#endregion
102
+ exports.ResourceType = ResourceType;
103
+ exports.buildResourceEnv = buildResourceEnv;
104
+ exports.environmentCase = environmentCase;
105
+ exports.getLocalIpAddress = getLocalIpAddress;
2
106
 
3
- exports.ResourceType = require_utils.ResourceType;
4
- exports.buildResourceEnv = require_utils.buildResourceEnv;
5
- exports.environmentCase = require_utils.environmentCase;
6
- exports.getLocalIpAddress = require_utils.getLocalIpAddress;
107
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["os","snakecase"],"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,oBAAoBA,QAAAA,QAAG,kBAAkB;CAC/C,KAAK,MAAM,iBAAiB,mBAAmB;EAC9C,MAAM,YAAY,kBAAkB,kBAAkB,CAAC;EACvD,KAAK,MAAM,eAAe,WAEzB,IAAI,YAAY,WAAW,UAAU,CAAC,YAAY,UACjD,OAAO,YAAY;CAGtB;CACA,OAAO;AACR;AAEA,SAAgB,gBAAgB,MAAc;CAC7C,QAAA,GAAOC,iBAAAA,QAAAA,CAAU,IAAI,CAAC,CACpB,YAAY,CAAC,CACb,QAAQ,UAAU,MAAM;EACxB,OAAO,EAAE,QAAQ,KAAK,EAAE;CACzB,CAAC;AACH;AACA,IAAY,eAAL,yBAAA,cAAA;CACN,aAAA,kBAAA;CACA,aAAA,cAAA;CACA,aAAA,cAAA;CACA,aAAA,mBAAA;CACA,aAAA,SAAA;CACA,aAAA,YAAA;CACA,aAAA,eAAA;CACA,aAAA,iBAAA;CACA,aAAA,qBAAA;CACA,aAAA,iBAAA;CACA,aAAA,sBAAA;CACA,aAAA,cAAA;;AACD,EAAA,CAAA,CAAA;AAEA,MAAM,UAAU,MAAc,WAAmB,GAC/C,gBAAgB,IAAI,IAAI,MAAM,MAChC;AACA,MAAM,YAAY,KAAa,UAAoB;CAClD,MAAM,SAAS,GAAG,gBAAgB,GAAG;CACrC,OAAO;GACL,GAAG,OAAO,SAAS,MAAM;GACzB,GAAG,OAAO,SAAS,MAAM;GACzB,GAAG,OAAO,aAAa,MAAM;GAC7B,GAAG,OAAO,SAAS,MAAM;GACzB,GAAG,OAAO,aAAa,MAAM;CAC/B;AACD;AAEA,MAAM,UAAU,MAAc,UAAkB;CAE/C,OAAO,GACL,GAAG,GAFa,gBAAgB,IAAI,IAE1B,SAAS,MAAM,KAC3B;AACD;AAEA,MAAM,SAAS,MAAc,UAAoB;CAIhD,OAAO,GACL,GAHa,GADG,gBAAgB,IAAI,IAChB,QAGd,MAAM,IACd;AACD;AAEA,MAAM,QAAQ,OAAe,YAAiB,CAAC;AAE/C,MAAM,aAA2D;EACnC,yBAAA;EACJ,qBAAA;EACL,gBAAA;EACG,mBAAA;EACE,qBAAA;EACK,mBAAA;EAEJ,mBAAA;EACO,mBAAA;EACL,qBAAA;EACA,qBAAA;EACI,yBAAA;EACP,qBAAA;AAC1B;AAEA,SAAgB,iBACf,QACyB;CACzB,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,CAAC,GAAG,UAAU,OAAO,QAAQ,MAAM,GAAG;EAChD,IAAI,OAAO,UAAU,UAAU;GAC9B,IAAI,gBAAgB,CAAC,KAAK;GAC1B;EACD;EAEA,MAAM,YAAY,WAAW,MAAM;EACnC,IAAI,WACH,OAAO,OAAO,KAAK,UAAU,GAAG,KAAK,CAAC;CAGxC;CAEA,OAAO;AACR"}
@@ -1,2 +1,62 @@
1
- import { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress } from "../index-B5CZ1xVf.cjs";
2
- export { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress };
1
+ //#region src/utils/index.d.ts
2
+ export declare function getLocalIpAddress(): string | null;
3
+ export declare function environmentCase(name: string): string;
4
+ export declare enum ResourceType {
5
+ ApiGatewayV2 = "sst.aws.ApiGatewayV2",
6
+ Postgres = "sst.aws.Postgres",
7
+ Function = "sst.aws.Function",
8
+ ObjectStorage = "sst.aws.Bucket",
9
+ Vpc = "sst.aws.Vpc",
10
+ Secret = "sst.sst.Secret",
11
+ SSTSecret = "sst:sst:Secret",
12
+ SSTFunction = "sst:sst:Function",
13
+ SSTApiGatewayV2 = "sst:aws:ApiGatewayV2",
14
+ SSTPostgres = "sst:aws:Postgres",
15
+ SSTObjectStorage = "sst:aws:Bucket",
16
+ SnsTopic = "sst:aws:SnsTopic"
17
+ }
18
+ export 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
+ */
27
+ export type ApiGatewayV2 = {
28
+ type: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;
29
+ url: string;
30
+ };
31
+ export type Postgres = {
32
+ database: string;
33
+ host: string;
34
+ password: string;
35
+ port: number;
36
+ type: ResourceType.Postgres | ResourceType.SSTPostgres;
37
+ username: string;
38
+ };
39
+ export type Function = {
40
+ name: string;
41
+ type: ResourceType.Function | ResourceType.SSTFunction;
42
+ };
43
+ export type Bucket = {
44
+ name: string;
45
+ type: ResourceType.ObjectStorage | ResourceType.SSTObjectStorage;
46
+ };
47
+ export type SnsTopic = {
48
+ arn: string;
49
+ type: ResourceType.SnsTopic;
50
+ };
51
+ export type Vpc = {
52
+ bastion: string;
53
+ type: ResourceType.Vpc;
54
+ };
55
+ export type Secret = {
56
+ type: ResourceType.Secret | ResourceType.SSTSecret;
57
+ value: string;
58
+ };
59
+ export type Resource = ApiGatewayV2 | Postgres | Function | Bucket | SnsTopic | Vpc | Secret;
60
+ export type ResourceProcessor<K extends Resource> = (name: string, value: K) => Record<string, string | number>;
61
+ //#endregion
62
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/utils/index.ts"],"mappings":";wBAGgB;wBAcA,gBAAgB;oBAOpB;EACX;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;wBAmDe,iBACf,QAAQ,eAAe,qBACrB;;;;;;;;;YA0BS;EACX,MAAM,aAAa,eAAe,aAAa;EAC/C;;YAGW;EACX;EACA;EACA;EACA;EACA,MAAM,aAAa,WAAW,aAAa;EAC3C;;YAGW;EACX;EACA,MAAM,aAAa,WAAW,aAAa;;YAGhC;EACX;EACA,MAAM,aAAa,gBAAgB,aAAa;;YAGrC;EACX;EACA,MAAM,aAAa;;YAGR;EACX;EACA,MAAM,aAAa;;YAGR;EACX,MAAM,aAAa,SAAS,aAAa;EACzC;;YAGW,WACT,eACA,WACA,WACA,SACA,WACA,MACA;YAES,kBAAkB,UAAU,aACvC,cACA,OAAO,MACH"}
@@ -1,2 +1,62 @@
1
- import { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress } from "../index-DhHRjduZ.mjs";
2
- export { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress };
1
+ //#region src/utils/index.d.ts
2
+ export declare function getLocalIpAddress(): string | null;
3
+ export declare function environmentCase(name: string): string;
4
+ export declare enum ResourceType {
5
+ ApiGatewayV2 = "sst.aws.ApiGatewayV2",
6
+ Postgres = "sst.aws.Postgres",
7
+ Function = "sst.aws.Function",
8
+ ObjectStorage = "sst.aws.Bucket",
9
+ Vpc = "sst.aws.Vpc",
10
+ Secret = "sst.sst.Secret",
11
+ SSTSecret = "sst:sst:Secret",
12
+ SSTFunction = "sst:sst:Function",
13
+ SSTApiGatewayV2 = "sst:aws:ApiGatewayV2",
14
+ SSTPostgres = "sst:aws:Postgres",
15
+ SSTObjectStorage = "sst:aws:Bucket",
16
+ SnsTopic = "sst:aws:SnsTopic"
17
+ }
18
+ export 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
+ */
27
+ export type ApiGatewayV2 = {
28
+ type: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;
29
+ url: string;
30
+ };
31
+ export type Postgres = {
32
+ database: string;
33
+ host: string;
34
+ password: string;
35
+ port: number;
36
+ type: ResourceType.Postgres | ResourceType.SSTPostgres;
37
+ username: string;
38
+ };
39
+ export type Function = {
40
+ name: string;
41
+ type: ResourceType.Function | ResourceType.SSTFunction;
42
+ };
43
+ export type Bucket = {
44
+ name: string;
45
+ type: ResourceType.ObjectStorage | ResourceType.SSTObjectStorage;
46
+ };
47
+ export type SnsTopic = {
48
+ arn: string;
49
+ type: ResourceType.SnsTopic;
50
+ };
51
+ export type Vpc = {
52
+ bastion: string;
53
+ type: ResourceType.Vpc;
54
+ };
55
+ export type Secret = {
56
+ type: ResourceType.Secret | ResourceType.SSTSecret;
57
+ value: string;
58
+ };
59
+ export type Resource = ApiGatewayV2 | Postgres | Function | Bucket | SnsTopic | Vpc | Secret;
60
+ export type ResourceProcessor<K extends Resource> = (name: string, value: K) => Record<string, string | number>;
61
+ //#endregion
62
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/utils/index.ts"],"mappings":";wBAGgB;wBAcA,gBAAgB;oBAOpB;EACX;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;wBAmDe,iBACf,QAAQ,eAAe,qBACrB;;;;;;;;;YA0BS;EACX,MAAM,aAAa,eAAe,aAAa;EAC/C;;YAGW;EACX;EACA;EACA;EACA;EACA,MAAM,aAAa,WAAW,aAAa;EAC3C;;YAGW;EACX;EACA,MAAM,aAAa,WAAW,aAAa;;YAGhC;EACX;EACA,MAAM,aAAa,gBAAgB,aAAa;;YAGrC;EACX;EACA,MAAM,aAAa;;YAGR;EACX;EACA,MAAM,aAAa;;YAGR;EACX,MAAM,aAAa,SAAS,aAAa;EACzC;;YAGW,WACT,eACA,WACA,WACA,SACA,WACA,MACA;YAES,kBAAkB,UAAU,aACvC,cACA,OAAO,MACH"}
@@ -1,3 +1,79 @@
1
- import { ResourceType, buildResourceEnv, environmentCase, getLocalIpAddress } from "../utils-DMOXJ27j.mjs";
1
+ import os from "node:os";
2
+ import snakecase from "lodash.snakecase";
3
+ //#region src/utils/index.ts
4
+ function getLocalIpAddress() {
5
+ const networkInterfaces = os.networkInterfaces();
6
+ for (const interfaceName in networkInterfaces) {
7
+ const addresses = networkInterfaces[interfaceName] || [];
8
+ for (const addressInfo of addresses) if (addressInfo.family === "IPv4" && !addressInfo.internal) return addressInfo.address;
9
+ }
10
+ return null;
11
+ }
12
+ function environmentCase(name) {
13
+ return snakecase(name).toUpperCase().replace(/_\d+/g, (r) => {
14
+ return r.replace("_", "");
15
+ });
16
+ }
17
+ let ResourceType = /* @__PURE__ */ function(ResourceType) {
18
+ ResourceType["ApiGatewayV2"] = "sst.aws.ApiGatewayV2";
19
+ ResourceType["Postgres"] = "sst.aws.Postgres";
20
+ ResourceType["Function"] = "sst.aws.Function";
21
+ ResourceType["ObjectStorage"] = "sst.aws.Bucket";
22
+ ResourceType["Vpc"] = "sst.aws.Vpc";
23
+ ResourceType["Secret"] = "sst.sst.Secret";
24
+ ResourceType["SSTSecret"] = "sst:sst:Secret";
25
+ ResourceType["SSTFunction"] = "sst:sst:Function";
26
+ ResourceType["SSTApiGatewayV2"] = "sst:aws:ApiGatewayV2";
27
+ ResourceType["SSTPostgres"] = "sst:aws:Postgres";
28
+ ResourceType["SSTObjectStorage"] = "sst:aws:Bucket";
29
+ ResourceType["SnsTopic"] = "sst:aws:SnsTopic";
30
+ return ResourceType;
31
+ }({});
32
+ const secret = (name, value) => ({ [environmentCase(name)]: value.value });
33
+ const postgres = (key, value) => {
34
+ const prefix = `${environmentCase(key)}`;
35
+ return {
36
+ [`${prefix}_NAME`]: value.database,
37
+ [`${prefix}_HOST`]: value.host,
38
+ [`${prefix}_PASSWORD`]: value.password,
39
+ [`${prefix}_PORT`]: value.port,
40
+ [`${prefix}_USERNAME`]: value.username
41
+ };
42
+ };
43
+ const bucket = (name, value) => {
44
+ return { [`${`${environmentCase(name)}`}_NAME`]: value.name };
45
+ };
46
+ const topic = (name, value) => {
47
+ return { [`${`${environmentCase(name)}`}_ARN`]: value.arn };
48
+ };
49
+ const noop = (_name, _value) => ({});
50
+ const processors = {
51
+ ["sst.aws.ApiGatewayV2"]: noop,
52
+ ["sst.aws.Function"]: noop,
53
+ ["sst.aws.Vpc"]: noop,
54
+ ["sst.sst.Secret"]: secret,
55
+ ["sst.aws.Postgres"]: postgres,
56
+ ["sst.aws.Bucket"]: bucket,
57
+ ["sst:sst:Secret"]: secret,
58
+ ["sst:aws:Bucket"]: bucket,
59
+ ["sst:sst:Function"]: noop,
60
+ ["sst:aws:Postgres"]: postgres,
61
+ ["sst:aws:ApiGatewayV2"]: noop,
62
+ ["sst:aws:SnsTopic"]: topic
63
+ };
64
+ function buildResourceEnv(record) {
65
+ const env = {};
66
+ for (const [k, value] of Object.entries(record)) {
67
+ if (typeof value === "string") {
68
+ env[environmentCase(k)] = value;
69
+ continue;
70
+ }
71
+ const processor = processors[value.type];
72
+ if (processor) Object.assign(env, processor(k, value));
73
+ }
74
+ return env;
75
+ }
76
+ //#endregion
77
+ export { ResourceType, buildResourceEnv, environmentCase, getLocalIpAddress };
2
78
 
3
- export { ResourceType, buildResourceEnv, environmentCase, getLocalIpAddress };
79
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"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,kBAAkB;CAC/C,KAAK,MAAM,iBAAiB,mBAAmB;EAC9C,MAAM,YAAY,kBAAkB,kBAAkB,CAAC;EACvD,KAAK,MAAM,eAAe,WAEzB,IAAI,YAAY,WAAW,UAAU,CAAC,YAAY,UACjD,OAAO,YAAY;CAGtB;CACA,OAAO;AACR;AAEA,SAAgB,gBAAgB,MAAc;CAC7C,OAAO,UAAU,IAAI,CAAC,CACpB,YAAY,CAAC,CACb,QAAQ,UAAU,MAAM;EACxB,OAAO,EAAE,QAAQ,KAAK,EAAE;CACzB,CAAC;AACH;AACA,IAAY,eAAL,yBAAA,cAAA;CACN,aAAA,kBAAA;CACA,aAAA,cAAA;CACA,aAAA,cAAA;CACA,aAAA,mBAAA;CACA,aAAA,SAAA;CACA,aAAA,YAAA;CACA,aAAA,eAAA;CACA,aAAA,iBAAA;CACA,aAAA,qBAAA;CACA,aAAA,iBAAA;CACA,aAAA,sBAAA;CACA,aAAA,cAAA;;AACD,EAAA,CAAA,CAAA;AAEA,MAAM,UAAU,MAAc,WAAmB,GAC/C,gBAAgB,IAAI,IAAI,MAAM,MAChC;AACA,MAAM,YAAY,KAAa,UAAoB;CAClD,MAAM,SAAS,GAAG,gBAAgB,GAAG;CACrC,OAAO;GACL,GAAG,OAAO,SAAS,MAAM;GACzB,GAAG,OAAO,SAAS,MAAM;GACzB,GAAG,OAAO,aAAa,MAAM;GAC7B,GAAG,OAAO,SAAS,MAAM;GACzB,GAAG,OAAO,aAAa,MAAM;CAC/B;AACD;AAEA,MAAM,UAAU,MAAc,UAAkB;CAE/C,OAAO,GACL,GAAG,GAFa,gBAAgB,IAAI,IAE1B,SAAS,MAAM,KAC3B;AACD;AAEA,MAAM,SAAS,MAAc,UAAoB;CAIhD,OAAO,GACL,GAHa,GADG,gBAAgB,IAAI,IAChB,QAGd,MAAM,IACd;AACD;AAEA,MAAM,QAAQ,OAAe,YAAiB,CAAC;AAE/C,MAAM,aAA2D;EACnC,yBAAA;EACJ,qBAAA;EACL,gBAAA;EACG,mBAAA;EACE,qBAAA;EACK,mBAAA;EAEJ,mBAAA;EACO,mBAAA;EACL,qBAAA;EACA,qBAAA;EACI,yBAAA;EACP,qBAAA;AAC1B;AAEA,SAAgB,iBACf,QACyB;CACzB,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,CAAC,GAAG,UAAU,OAAO,QAAQ,MAAM,GAAG;EAChD,IAAI,OAAO,UAAU,UAAU;GAC9B,IAAI,gBAAgB,CAAC,KAAK;GAC1B;EACD;EAEA,MAAM,YAAY,WAAW,MAAM;EACnC,IAAI,WACH,OAAO,OAAO,KAAK,UAAU,GAAG,KAAK,CAAC;CAGxC;CAEA,OAAO;AACR"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/cloud",
3
- "version": "10.0.0-alpha.6",
3
+ "version": "10.0.0-alpha.60",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -41,8 +41,8 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "lodash.snakecase": "~4.1.1",
44
- "@geekmidas/envkit": "10.0.0-alpha.6",
45
- "@geekmidas/manifest": "10.0.0-alpha.6"
44
+ "@geekmidas/envkit": "10.0.0-alpha.60",
45
+ "@geekmidas/manifest": "10.0.0-alpha.60"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/lodash.get": "~4.4.9",
@@ -58,10 +58,10 @@
58
58
  "@pulumi/aws": ">=7.47.0",
59
59
  "@pulumi/random": ">=4.21.2",
60
60
  "@pulumi/pulumi": ">=3.263.0",
61
- "@geekmidas/manifest": "^10.0.0-alpha.6",
62
- "@geekmidas/storage": "^10.0.0-alpha.6",
63
- "@geekmidas/events": "^10.0.0-alpha.6",
64
- "@geekmidas/db": "^10.0.0-alpha.6"
61
+ "@geekmidas/manifest": "^10.0.0-alpha.60",
62
+ "@geekmidas/storage": "^10.0.0-alpha.60",
63
+ "@geekmidas/events": "^10.0.0-alpha.60",
64
+ "@geekmidas/db": "^10.0.0-alpha.60"
65
65
  },
66
66
  "peerDependenciesMeta": {
67
67
  "@geekmidas/manifest": {
@@ -195,6 +195,45 @@ const provider: dynamic.ResourceProvider<
195
195
  },
196
196
  };
197
197
 
198
+ /**
199
+ * A Dokploy API call that answered with an error status.
200
+ *
201
+ * Defined here, beside the provider, rather than imported: the provider's
202
+ * functions are serialised into state, and Pulumi's closure serialiser
203
+ * captures a class declared in the same module by value — once, shared by
204
+ * every function that refers to it, so `instanceof` still holds in the
205
+ * deployed provider. An imported class would have to be resolvable as a
206
+ * module where the provider runs, which is the obscure failure the note on
207
+ * `call` below is about.
208
+ *
209
+ * Serialisation rewrites the class into a plain function and drops its link to
210
+ * `Error.prototype`, so the constructor sets `message` and `stack` itself and
211
+ * the class says how to print itself. `Application.provider.spec.ts` runs the
212
+ * serialised provider to keep all of this true.
213
+ */
214
+ export class DokployCallFailed extends Error {
215
+ constructor(
216
+ readonly path: string,
217
+ readonly status: number,
218
+ readonly statusText: string,
219
+ readonly detail: string,
220
+ ) {
221
+ const message = `Dokploy ${path} failed: ${status} ${statusText}${detail ? ` — ${detail}` : ''}. Check the endpoint and API token, and Dokploy's own logs for the request.`;
222
+ super(message);
223
+ // Serialised, this constructor becomes a plain function whose super call
224
+ // does not initialise `this`: without these the provider host, which
225
+ // reports `e.message` and logs `${e}: ${e.stack}`, would show nothing.
226
+ this.message = message;
227
+ Error.captureStackTrace?.(this, DokployCallFailed);
228
+ this.name = 'DokployCallFailed';
229
+ }
230
+
231
+ /** What `${error}` prints once serialisation has dropped Error's own. */
232
+ override toString(): string {
233
+ return `${this.name}: ${this.message}`;
234
+ }
235
+ }
236
+
198
237
  /**
199
238
  * One call to Dokploy.
200
239
  *
@@ -218,10 +257,11 @@ async function call<T>(
218
257
 
219
258
  if (!response.ok) {
220
259
  const detail = await response.text().catch(() => '');
221
- throw new Error(
222
- `Dokploy ${path} failed: ${response.status} ${response.statusText}${
223
- detail ? ` — ${detail}` : ''
224
- }`,
260
+ throw new DokployCallFailed(
261
+ path,
262
+ response.status,
263
+ response.statusText,
264
+ detail,
225
265
  );
226
266
  }
227
267
 
@@ -230,7 +270,7 @@ async function call<T>(
230
270
 
231
271
  /** Whether an error means the thing is already absent. */
232
272
  function isNotFound(error: unknown): boolean {
233
- return error instanceof Error && /\b404\b/.test(error.message);
273
+ return error instanceof DokployCallFailed && error.status === 404;
234
274
  }
235
275
 
236
276
  export interface ApplicationArgs {
@@ -241,6 +281,13 @@ export interface ApplicationArgs {
241
281
  environmentId: Input<string>;
242
282
  }
243
283
 
284
+ /**
285
+ * The provider itself, for tests: its create/read/update/delete are what talk
286
+ * to Dokploy, and a dynamic resource gives no other way to reach them without a
287
+ * Pulumi engine.
288
+ */
289
+ export { provider as applicationProvider };
290
+
244
291
  export class Application extends dynamic.Resource {
245
292
  declare readonly applicationId: Output<string>;
246
293
  declare readonly appName: Output<string>;