@lsts_tech/infra 1.0.0 → 1.0.2

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 (39) hide show
  1. package/README.md +58 -70
  2. package/dist/bin/init.d.ts +4 -3
  3. package/dist/bin/init.d.ts.map +1 -1
  4. package/dist/bin/init.js +619 -117
  5. package/dist/bin/init.js.map +1 -1
  6. package/dist/src/auth/index.d.ts +17 -0
  7. package/dist/src/auth/index.d.ts.map +1 -0
  8. package/dist/src/auth/index.js +18 -0
  9. package/dist/src/auth/index.js.map +1 -0
  10. package/dist/stacks/Dns.d.ts +24 -14
  11. package/dist/stacks/Dns.d.ts.map +1 -1
  12. package/dist/stacks/Dns.js +69 -18
  13. package/dist/stacks/Dns.js.map +1 -1
  14. package/dist/stacks/Pipeline.d.ts +7 -0
  15. package/dist/stacks/Pipeline.d.ts.map +1 -1
  16. package/dist/stacks/Pipeline.js +60 -7
  17. package/dist/stacks/Pipeline.js.map +1 -1
  18. package/docs/CLI.md +58 -15
  19. package/docs/CONFIGURATION.md +73 -30
  20. package/docs/EXAMPLES.md +5 -1
  21. package/examples/delegated-subdomain/infra.config.ts +102 -0
  22. package/examples/next-and-expo/infra.config.ts +33 -28
  23. package/examples/next-only/infra.config.ts +35 -22
  24. package/package.json +10 -4
  25. package/scripts/ensure-pipelines.sh +151 -43
  26. package/scripts/postdeploy-update-dns.sh +42 -11
  27. package/scripts/predeploy-checks.sh +38 -5
  28. package/templates/buildspec.yml +23 -0
  29. package/templates/ensure-pipelines.sh +157 -22
  30. package/templates/env.example +15 -0
  31. package/templates/infra.config.expo-web.ts +153 -0
  32. package/templates/infra.config.next-only.ts +159 -0
  33. package/templates/infra.config.ts +21 -4
  34. package/templates/pipelines.example.json +19 -0
  35. package/templates/private.example.json +13 -0
  36. package/templates/scaffold.gitignore +29 -0
  37. package/templates/scaffold.package.json +25 -0
  38. package/templates/scaffold.tsconfig.json +22 -0
  39. package/templates/secrets.schema.expo-web.json +8 -0
@@ -1,23 +1,29 @@
1
1
  # Configuration Guide
2
2
 
3
- ## Environment Variables
3
+ ## Core Environment Variables
4
4
 
5
5
  The scaffolded `infra.config.ts` is environment-driven.
6
6
 
7
7
  | Variable | Required | Description |
8
8
  |---|---|---|
9
+ | `INFRA_PROFILE` | No | `next-only`, `next-expo`, `expo-web` |
9
10
  | `INFRA_APP_NAME` | No | SST app name |
11
+ | `INFRA_USE_EXTERNAL_CERTS` | No | `true` to force use of `INFRA_*_CERT_ARN_*` values |
10
12
  | `INFRA_ROOT_DOMAIN` | Yes | Root domain for stage resolution |
13
+ | `INFRA_HOSTED_ZONE_DOMAIN` | No | Explicit Route53 zone domain override (eg. `alternun.co`) |
11
14
  | `INFRA_PIPELINE_REPO` | No | GitHub repo in `owner/repo` format |
12
15
  | `INFRA_PIPELINE_PREFIX` | No | Prefix for pipeline names |
13
16
  | `INFRA_PROJECT_TAG` | No | Resource tag value |
14
- | `INFRA_PIPELINES` | No | Pipeline stages CSV: `production,dev,mobile` |
17
+ | `INFRA_PIPELINES` | No | Pipeline stage CSV: `production,dev,mobile` |
15
18
  | `INFRA_PIPELINE_BRANCH_PROD` | No | Branch for production pipeline |
16
19
  | `INFRA_PIPELINE_BRANCH_DEV` | No | Branch for dev pipeline |
17
20
  | `INFRA_PIPELINE_BRANCH_MOBILE` | No | Branch for mobile pipeline |
18
- | `INFRA_ENABLE_EXPO_SITE` | No | `true` enables Expo `StaticSite` deploy |
21
+ | `INFRA_PIPELINES_CONFIG_PATH` | No | Runtime pipeline config JSON path |
22
+ | `INFRA_CREATE_PIPELINES` | No | `true` allows production deploy to create/update pipelines |
23
+ | `INFRA_PIPELINE_PERMISSIONS_MODE` | No | `admin` or `least-privilege` for CodeBuild IAM |
24
+ | `INFRA_ENABLE_EXPO_SITE` | No | `true` enables Expo site for `next-expo` profile |
19
25
 
20
- ### Domain Overrides (Optional)
26
+ ## Domain Overrides (Optional)
21
27
 
22
28
  | Variable | Description |
23
29
  |---|---|
@@ -28,19 +34,7 @@ The scaffolded `infra.config.ts` is environment-driven.
28
34
  | `INFRA_EXPO_DOMAIN_DEV` | Expo dev domain |
29
35
  | `INFRA_EXPO_DOMAIN_MOBILE` | Expo mobile-stage domain |
30
36
 
31
- ## DNS Helper Script Overrides
32
-
33
- These are consumed by `predeploy-checks.sh` and `postdeploy-update-dns.sh` (typically via `buildspec.yml`):
34
-
35
- | Variable | Description |
36
- |---|---|
37
- | `DOMAIN_ROOT` | Base/root domain |
38
- | `DOMAIN_PRODUCTION` | Explicit production domain override |
39
- | `DOMAIN_DEV` | Explicit dev domain override |
40
- | `DOMAIN_MOBILE` | Explicit mobile domain override |
41
- | `DOMAIN_CUSTOM` | Explicit override for non-standard stage names |
42
-
43
- ### Certificate Reuse (Optional)
37
+ ## Certificate Reuse (Optional)
44
38
 
45
39
  | Variable | Description |
46
40
  |---|---|
@@ -51,28 +45,77 @@ These are consumed by `predeploy-checks.sh` and `postdeploy-update-dns.sh` (typi
51
45
  | `INFRA_EXPO_CERT_ARN_DEV` | Existing ACM cert ARN for expo dev domain |
52
46
  | `INFRA_EXPO_CERT_ARN_MOBILE` | Existing ACM cert ARN for expo mobile-stage domain |
53
47
 
54
- ## SST Secrets
48
+ ## Runtime Pipeline Config
55
49
 
56
- Minimum secrets from the default template:
50
+ `INFRA_PIPELINES_CONFIG_PATH` defaults to `config/pipelines.json`.
51
+ If present, `scripts/ensure-pipelines.sh` reads it to drive stage enablement/branches/repos.
57
52
 
58
- - `DatabaseUrl`
59
- - `AuthSecret`
53
+ Example (`config/pipelines.json`):
60
54
 
61
- Set per stage:
55
+ ```json
56
+ {
57
+ "pipelines": {
58
+ "production": { "enabled": true, "branch": "main", "repo": "myorg/web" },
59
+ "dev": { "enabled": true, "branch": "develop", "repo": "myorg/web" },
60
+ "mobile": { "enabled": false, "branch": "mobile", "repo": "myorg/mobile" }
61
+ }
62
+ }
63
+ ```
64
+
65
+ ## Pipeline Mutation Safety
66
+
67
+ Normal deploys should keep:
62
68
 
63
69
  ```bash
64
- npx sst secrets set DatabaseUrl "postgresql://..." --stage dev
65
- npx sst secrets set AuthSecret "your-secret" --stage dev
70
+ INFRA_CREATE_PIPELINES=false
66
71
  ```
67
72
 
68
- ## Pipeline Provisioning
69
-
70
- 1. Ensure your `INFRA_PIPELINES` and branch variables are correct.
71
- 2. Deploy once (`sst deploy`) from the infra package.
72
- 3. Run:
73
+ To intentionally create/update pipelines:
73
74
 
74
75
  ```bash
75
76
  APPROVE=true bash scripts/ensure-pipelines.sh
76
77
  ```
77
78
 
78
- This script checks for missing pipelines and creates them by running stage-specific SST deploys.
79
+ This script flips `INFRA_CREATE_PIPELINES=true` only for explicit pipeline-creation deploys.
80
+
81
+ ## Delegated Subdomain / Parent Zone Setup
82
+
83
+ For setups like:
84
+
85
+ - app domain: `airs.alternun.co`
86
+ - stage domain: `dev.airs.alternun.co`
87
+ - hosted zone available in AWS: `alternun.co`
88
+
89
+ set:
90
+
91
+ ```bash
92
+ INFRA_ROOT_DOMAIN=airs.alternun.co
93
+ INFRA_HOSTED_ZONE_DOMAIN=alternun.co
94
+ ```
95
+
96
+ Helper scripts also fallback to parent zones automatically during Route53 checks/updates.
97
+
98
+ ## Local Private Config
99
+
100
+ Scaffolded defaults include:
101
+
102
+ - `config/private.example.json`
103
+ - `.gitignore` rules:
104
+ - `config/*.json`
105
+ - `!config/*.example.json`
106
+
107
+ Use `config/private.json` for local-only deployment metadata/secrets (never commit it).
108
+
109
+ ## SST Secrets
110
+
111
+ Minimum secrets from Next.js templates:
112
+
113
+ - `DatabaseUrl`
114
+ - `AuthSecret`
115
+
116
+ Set per stage:
117
+
118
+ ```bash
119
+ npx sst secret set DatabaseUrl "postgresql://..." --stage dev
120
+ npx sst secret set AuthSecret "your-secret" --stage dev
121
+ ```
package/docs/EXAMPLES.md CHANGED
@@ -6,4 +6,8 @@
6
6
  - [`next-and-expo/infra.config.ts`](../examples/next-and-expo/infra.config.ts)
7
7
  Next.js + Expo web static site + production/dev/mobile pipelines.
8
8
 
9
- Both examples are provider-scoped to AWS for v1.0.0.
9
+ - [`delegated-subdomain/infra.config.ts`](../examples/delegated-subdomain/infra.config.ts)
10
+ Delegated subdomain deployment with explicit parent hosted zone fallback:
11
+ `airs.alternun.co` + `dev.airs.alternun.co` using hosted zone `alternun.co`.
12
+
13
+ All examples are AWS-scoped for v1.x.
@@ -0,0 +1,102 @@
1
+ import { resolveDomain, createNextSite, createExpoSite, createPipeline } from "@lsts_tech/infra";
2
+
3
+ type PipelineStage = "production" | "dev" | "mobile";
4
+
5
+ const secrets = {
6
+ DatabaseUrl: new sst.Secret("DatabaseUrl"),
7
+ AuthSecret: new sst.Secret("AuthSecret"),
8
+ };
9
+
10
+ const rootDomain = process.env.INFRA_ROOT_DOMAIN ?? "airs.alternun.co";
11
+ const hostedZoneDomain = process.env.INFRA_HOSTED_ZONE_DOMAIN ?? "alternun.co";
12
+ const pipelineRepo = process.env.INFRA_PIPELINE_REPO ?? "myorg/myrepo";
13
+ const pipelinePrefix = process.env.INFRA_PIPELINE_PREFIX ?? "airs";
14
+ const createPipelines = (process.env.INFRA_CREATE_PIPELINES ?? "false") === "true";
15
+
16
+ const webStageMap: Record<string, string> = {
17
+ production: process.env.INFRA_WEB_DOMAIN_PRODUCTION ?? rootDomain,
18
+ dev: process.env.INFRA_WEB_DOMAIN_DEV ?? `dev.${rootDomain}`,
19
+ mobile: process.env.INFRA_WEB_DOMAIN_MOBILE ?? `api.${rootDomain}`,
20
+ };
21
+
22
+ const expoStageMap: Record<string, string> = {
23
+ production: process.env.INFRA_EXPO_DOMAIN_PRODUCTION ?? `mobile.${rootDomain}`,
24
+ dev: process.env.INFRA_EXPO_DOMAIN_DEV ?? `dev.mobile.${rootDomain}`,
25
+ mobile: process.env.INFRA_EXPO_DOMAIN_MOBILE ?? `preview.mobile.${rootDomain}`,
26
+ };
27
+
28
+ const selectedPipelines = (process.env.INFRA_PIPELINES ?? "production,dev,mobile")
29
+ .split(",")
30
+ .map((value) => value.trim().toLowerCase())
31
+ .filter((value): value is PipelineStage => value === "production" || value === "dev" || value === "mobile");
32
+
33
+ export function createInfrastructure() {
34
+ const stage = $app.stage;
35
+
36
+ const webDomain = resolveDomain({
37
+ rootDomain,
38
+ stage,
39
+ stageMap: webStageMap,
40
+ hostedZoneDomain,
41
+ });
42
+
43
+ const web = createNextSite({
44
+ appPath: "../../apps/web",
45
+ id: `web-${stage}`,
46
+ domain: webDomain.domain,
47
+ environment: {
48
+ NEXT_PUBLIC_APP_URL: `https://${webDomain.domainName}`,
49
+ DATABASE_URL: secrets.DatabaseUrl.value,
50
+ AUTH_SECRET: secrets.AuthSecret.value,
51
+ },
52
+ });
53
+
54
+ const expoDomain = resolveDomain({
55
+ rootDomain,
56
+ stage,
57
+ stageMap: expoStageMap,
58
+ hostedZoneDomain,
59
+ });
60
+
61
+ const expo = createExpoSite({
62
+ appPath: "../../apps/mobile",
63
+ id: `mobile-${stage}`,
64
+ domain: expoDomain.domain,
65
+ environment: {
66
+ EXPO_PUBLIC_API_URL: `https://${webDomain.domainName}/api`,
67
+ },
68
+ });
69
+
70
+ const outputs: Record<string, unknown> = {
71
+ siteUrl: web.url,
72
+ domain: webDomain.domainName,
73
+ mobileUrl: expo.url,
74
+ mobileDomain: expoDomain.domainName,
75
+ hostedZoneDomain,
76
+ };
77
+
78
+ if (stage === "production" && createPipelines) {
79
+ for (const pipelineStage of selectedPipelines) {
80
+ const branch =
81
+ pipelineStage === "production"
82
+ ? process.env.INFRA_PIPELINE_BRANCH_PROD ?? "main"
83
+ : pipelineStage === "dev"
84
+ ? process.env.INFRA_PIPELINE_BRANCH_DEV ?? "develop"
85
+ : process.env.INFRA_PIPELINE_BRANCH_MOBILE ?? "mobile";
86
+
87
+ const suffix = pipelineStage === "production" ? "prod" : pipelineStage;
88
+ const pipeline = createPipeline({
89
+ name: `${pipelinePrefix}-${suffix}`,
90
+ repo: pipelineRepo,
91
+ branch,
92
+ stage: pipelineStage,
93
+ permissionsMode:
94
+ process.env.INFRA_PIPELINE_PERMISSIONS_MODE === "least-privilege" ? "least-privilege" : "admin",
95
+ });
96
+
97
+ outputs[`${pipelineStage}PipelineName`] = pipeline.pipelineName;
98
+ }
99
+ }
100
+
101
+ return outputs;
102
+ }
@@ -1,19 +1,27 @@
1
1
  import { resolveDomain, createNextSite, createExpoSite, createPipeline } from "@lsts_tech/infra";
2
2
 
3
+ type PipelineStage = "production" | "dev" | "mobile";
4
+
3
5
  const secrets = {
4
6
  DatabaseUrl: new sst.Secret("DatabaseUrl"),
5
7
  AuthSecret: new sst.Secret("AuthSecret"),
6
8
  };
7
9
 
10
+ const rootDomain = process.env.INFRA_ROOT_DOMAIN ?? "example.com";
11
+ const hostedZoneDomain = process.env.INFRA_HOSTED_ZONE_DOMAIN || undefined;
12
+ const createPipelines = (process.env.INFRA_CREATE_PIPELINES ?? "false") === "true";
13
+ const pipelinePermissionsMode =
14
+ process.env.INFRA_PIPELINE_PERMISSIONS_MODE === "least-privilege" ? "least-privilege" : "admin";
15
+
8
16
  export function createInfrastructure() {
9
17
  const stage = $app.stage;
10
- const rootDomain = process.env.INFRA_ROOT_DOMAIN ?? "example.com";
11
18
  const repo = process.env.INFRA_PIPELINE_REPO ?? "myorg/myrepo";
12
19
  const prefix = process.env.INFRA_PIPELINE_PREFIX ?? "myapp";
13
20
 
14
21
  const webDomain = resolveDomain({
15
22
  rootDomain,
16
23
  stage,
24
+ hostedZoneDomain,
17
25
  stageMap: {
18
26
  production: process.env.INFRA_WEB_DOMAIN_PRODUCTION ?? rootDomain,
19
27
  dev: process.env.INFRA_WEB_DOMAIN_DEV ?? `dev.${rootDomain}`,
@@ -40,6 +48,7 @@ export function createInfrastructure() {
40
48
  const expoDomain = resolveDomain({
41
49
  rootDomain,
42
50
  stage,
51
+ hostedZoneDomain,
43
52
  stageMap: {
44
53
  production: process.env.INFRA_EXPO_DOMAIN_PRODUCTION ?? `mobile.${rootDomain}`,
45
54
  dev: process.env.INFRA_EXPO_DOMAIN_DEV ?? `dev.mobile.${rootDomain}`,
@@ -68,36 +77,32 @@ export function createInfrastructure() {
68
77
  mobileDomain: expoDomain.domainName,
69
78
  };
70
79
 
71
- if (stage === "production") {
72
- const production = createPipeline({
73
- name: `${prefix}-prod`,
74
- repo,
75
- branch: process.env.INFRA_PIPELINE_BRANCH_PROD ?? "main",
76
- stage: "production",
77
- projectTag: process.env.INFRA_PROJECT_TAG ?? prefix,
78
- });
80
+ if (stage === "production" && createPipelines) {
81
+ const selected = (process.env.INFRA_PIPELINES ?? "production,dev,mobile")
82
+ .split(",")
83
+ .map((value) => value.trim().toLowerCase())
84
+ .filter((value): value is PipelineStage => value === "production" || value === "dev" || value === "mobile");
79
85
 
80
- const dev = createPipeline({
81
- name: `${prefix}-dev`,
82
- repo,
83
- branch: process.env.INFRA_PIPELINE_BRANCH_DEV ?? "develop",
84
- stage: "dev",
85
- projectTag: process.env.INFRA_PROJECT_TAG ?? prefix,
86
- });
86
+ for (const pipelineStage of selected) {
87
+ const suffix = pipelineStage === "production" ? "prod" : pipelineStage;
88
+ const branch =
89
+ pipelineStage === "production"
90
+ ? process.env.INFRA_PIPELINE_BRANCH_PROD ?? "main"
91
+ : pipelineStage === "dev"
92
+ ? process.env.INFRA_PIPELINE_BRANCH_DEV ?? "develop"
93
+ : process.env.INFRA_PIPELINE_BRANCH_MOBILE ?? "mobile";
87
94
 
88
- const mobile = createPipeline({
89
- name: `${prefix}-mobile`,
90
- repo,
91
- branch: process.env.INFRA_PIPELINE_BRANCH_MOBILE ?? "mobile",
92
- stage: "mobile",
93
- projectTag: process.env.INFRA_PROJECT_TAG ?? prefix,
94
- });
95
+ const pipeline = createPipeline({
96
+ name: `${prefix}-${suffix}`,
97
+ repo,
98
+ branch,
99
+ stage: pipelineStage,
100
+ projectTag: process.env.INFRA_PROJECT_TAG ?? prefix,
101
+ permissionsMode: pipelinePermissionsMode,
102
+ });
95
103
 
96
- outputs.pipelines = {
97
- production: production.pipelineName,
98
- dev: dev.pipelineName,
99
- mobile: mobile.pipelineName,
100
- };
104
+ outputs[`${pipelineStage}PipelineName`] = pipeline.pipelineName;
105
+ }
101
106
  }
102
107
 
103
108
  return outputs;
@@ -1,17 +1,25 @@
1
1
  import { resolveDomain, createNextSite, createPipeline } from "@lsts_tech/infra";
2
2
 
3
+ type PipelineStage = "production" | "dev" | "mobile";
4
+
3
5
  const secrets = {
4
6
  DatabaseUrl: new sst.Secret("DatabaseUrl"),
5
7
  AuthSecret: new sst.Secret("AuthSecret"),
6
8
  };
7
9
 
10
+ const rootDomain = process.env.INFRA_ROOT_DOMAIN ?? "example.com";
11
+ const hostedZoneDomain = process.env.INFRA_HOSTED_ZONE_DOMAIN || undefined;
12
+ const createPipelines = (process.env.INFRA_CREATE_PIPELINES ?? "false") === "true";
13
+ const pipelinePermissionsMode =
14
+ process.env.INFRA_PIPELINE_PERMISSIONS_MODE === "least-privilege" ? "least-privilege" : "admin";
15
+
8
16
  export function createInfrastructure() {
9
17
  const stage = $app.stage;
10
- const rootDomain = process.env.INFRA_ROOT_DOMAIN ?? "example.com";
11
18
 
12
19
  const { domain, domainName } = resolveDomain({
13
20
  rootDomain,
14
21
  stage,
22
+ hostedZoneDomain,
15
23
  });
16
24
 
17
25
  const { url } = createNextSite({
@@ -30,30 +38,35 @@ export function createInfrastructure() {
30
38
  domain: domainName,
31
39
  };
32
40
 
33
- if (stage === "production") {
41
+ if (stage === "production" && createPipelines) {
34
42
  const repo = process.env.INFRA_PIPELINE_REPO ?? "myorg/myrepo";
35
43
  const prefix = process.env.INFRA_PIPELINE_PREFIX ?? "myapp";
36
44
 
37
- const prod = createPipeline({
38
- name: `${prefix}-prod`,
39
- repo,
40
- branch: process.env.INFRA_PIPELINE_BRANCH_PROD ?? "main",
41
- stage: "production",
42
- projectTag: process.env.INFRA_PROJECT_TAG ?? prefix,
43
- });
44
-
45
- const dev = createPipeline({
46
- name: `${prefix}-dev`,
47
- repo,
48
- branch: process.env.INFRA_PIPELINE_BRANCH_DEV ?? "develop",
49
- stage: "dev",
50
- projectTag: process.env.INFRA_PROJECT_TAG ?? prefix,
51
- });
52
-
53
- outputs.pipelines = {
54
- production: prod.pipelineName,
55
- dev: dev.pipelineName,
56
- };
45
+ const selected = (process.env.INFRA_PIPELINES ?? "production,dev")
46
+ .split(",")
47
+ .map((value) => value.trim().toLowerCase())
48
+ .filter((value): value is PipelineStage => value === "production" || value === "dev" || value === "mobile");
49
+
50
+ for (const pipelineStage of selected) {
51
+ const suffix = pipelineStage === "production" ? "prod" : pipelineStage;
52
+ const branch =
53
+ pipelineStage === "production"
54
+ ? process.env.INFRA_PIPELINE_BRANCH_PROD ?? "main"
55
+ : pipelineStage === "dev"
56
+ ? process.env.INFRA_PIPELINE_BRANCH_DEV ?? "develop"
57
+ : process.env.INFRA_PIPELINE_BRANCH_MOBILE ?? "mobile";
58
+
59
+ const pipeline = createPipeline({
60
+ name: `${prefix}-${suffix}`,
61
+ repo,
62
+ branch,
63
+ stage: pipelineStage,
64
+ projectTag: process.env.INFRA_PROJECT_TAG ?? prefix,
65
+ permissionsMode: pipelinePermissionsMode,
66
+ });
67
+
68
+ outputs[`${pipelineStage}PipelineName`] = pipeline.pipelineName;
69
+ }
57
70
  }
58
71
 
59
72
  return outputs;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lsts_tech/infra",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Reusable SST v3 infrastructure constructs for deploying Next.js and Expo web apps from monorepos.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -53,6 +53,10 @@
53
53
  "./stacks/Pipeline": {
54
54
  "types": "./dist/stacks/Pipeline.d.ts",
55
55
  "import": "./dist/stacks/Pipeline.js"
56
+ },
57
+ "./auth": {
58
+ "types": "./dist/src/auth/index.d.ts",
59
+ "import": "./dist/src/auth/index.js"
56
60
  }
57
61
  },
58
62
  "bin": {
@@ -75,6 +79,7 @@
75
79
  "deploy": "sst deploy",
76
80
  "deploy:dev": "sst deploy --stage dev",
77
81
  "deploy:prod": "sst deploy --stage production",
82
+ "doctor": "node dist/bin/init.js doctor --target .",
78
83
  "remove": "sst remove",
79
84
  "console": "sst console",
80
85
  "secrets": "sst secrets",
@@ -88,9 +93,10 @@
88
93
  "registry": "https://registry.npmjs.org/"
89
94
  },
90
95
  "dependencies": {
91
- "sst": "^3.7.0",
96
+ "@edcalderon/auth": "^1.2.1",
92
97
  "@pulumi/aws": "^6.66.0",
93
- "@pulumi/pulumi": "^3.145.0"
98
+ "@pulumi/pulumi": "^3.145.0",
99
+ "sst": "^3.7.0"
94
100
  },
95
101
  "devDependencies": {
96
102
  "@types/node": "^22.13.9",
@@ -99,4 +105,4 @@
99
105
  "engines": {
100
106
  "node": ">=20"
101
107
  }
102
- }
108
+ }