rastack 0.0.62 → 0.0.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/auth/provider.ts +71 -8
- package/dist/admin.js +9 -9
- package/dist/deploy/ci.d.ts +10 -3
- package/dist/deploy/ci.js +22 -11
- package/dist/deploy/config.d.ts +155 -5
- package/dist/deploy/config.js +216 -6
- package/dist/deploy/io.d.ts +7 -0
- package/dist/deploy/io.js +17 -0
- package/dist/deploy/workflows.d.ts +30 -0
- package/dist/deploy/workflows.js +117 -12
- package/dist/rastack-ci.js +19 -1
- package/dist/rastack-dev.js +48 -2
- package/dist/rastack-init.js +190 -64
- package/dist/rastack.js +1 -0
- package/dist/templates/cloudformation/app.yaml +133 -2
- package/dist/wasm/rastack_wasm_bg.wasm +0 -0
- package/package.json +1 -1
- package/src/deploy/ci.ts +50 -16
- package/src/deploy/config.ts +340 -7
- package/src/deploy/io.ts +17 -1
- package/src/deploy/workflows.ts +122 -12
- package/src/rastack-ci.ts +20 -2
- package/src/rastack-dev.ts +61 -6
- package/src/rastack-init.ts +235 -76
- package/src/rastack.ts +1 -0
package/dist/deploy/io.js
CHANGED
|
@@ -53,6 +53,7 @@ exports.ghAvailable = ghAvailable;
|
|
|
53
53
|
exports.ghAuthenticated = ghAuthenticated;
|
|
54
54
|
exports.ghAuthLogin = ghAuthLogin;
|
|
55
55
|
exports.setRepoVariable = setRepoVariable;
|
|
56
|
+
exports.ghEnvironmentCreate = ghEnvironmentCreate;
|
|
56
57
|
exports.ask = ask;
|
|
57
58
|
exports.withPrompt = withPrompt;
|
|
58
59
|
const child_process_1 = require("child_process");
|
|
@@ -278,6 +279,22 @@ function setRepoVariable(args, cwd = process.cwd()) {
|
|
|
278
279
|
return false;
|
|
279
280
|
}
|
|
280
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Create (idempotently, via `gh api --method PUT`) a GitHub Actions Environment
|
|
284
|
+
* so its scoped Variables can be set. Returns true on success. Best-effort — a
|
|
285
|
+
* missing/unauthenticated `gh` or an API error degrades to the printed
|
|
286
|
+
* instructions, exactly like `setRepoVariable`.
|
|
287
|
+
*/
|
|
288
|
+
function ghEnvironmentCreate(args, cwd = process.cwd()) {
|
|
289
|
+
try {
|
|
290
|
+
// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process
|
|
291
|
+
(0, child_process_1.execFileSync)("gh", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
281
298
|
/**
|
|
282
299
|
* Ask a single question with a default, echoed as `label [default]: `. Returns
|
|
283
300
|
* the trimmed answer, or the default on an empty line.
|
|
@@ -34,6 +34,36 @@ export declare function renderConfigFile(cfg: DeployConfig): string;
|
|
|
34
34
|
export declare const CONFIG_PATH = ".rastack/deploy.json";
|
|
35
35
|
export declare const CI_WORKFLOW_PATH = ".github/workflows/ci.yml";
|
|
36
36
|
export declare const DEPLOY_WORKFLOW_PATH = ".github/workflows/deploy-backend.yml";
|
|
37
|
+
export declare const ENV_PATH = ".env";
|
|
38
|
+
export declare const GITIGNORE_PATH = ".gitignore";
|
|
39
|
+
/**
|
|
40
|
+
* The generated `.env` — the app's **local** environment. Every value is
|
|
41
|
+
* non-secret (client ids, pool ids, public URLs from the stack Outputs), but the
|
|
42
|
+
* file is git-ignored by convention (`gitignoreWith`) so a developer can add
|
|
43
|
+
* their own secrets beside these without ever committing them. Values are blank
|
|
44
|
+
* until the matching stack is deployed; `rastack init` rewrites the file with the
|
|
45
|
+
* resolved Outputs after standing the backends up.
|
|
46
|
+
*
|
|
47
|
+
* Keys are `RASTACK_<ENV>_*` (a neutral prefix). For a browser bundle, re-export
|
|
48
|
+
* the ones you need under your bundler's public prefix (EXPO_PUBLIC_/VITE_/…);
|
|
49
|
+
* `rastack dev` also serves the dev pool config live at `/__rastack/cognito.json`.
|
|
50
|
+
*/
|
|
51
|
+
export declare function renderEnvFile(cfg: DeployConfig): string;
|
|
52
|
+
/**
|
|
53
|
+
* Upsert the generated `RASTACK_*` keys from `renderEnvFile(cfg)` into an
|
|
54
|
+
* existing `.env`, preserving every other line (a developer's own secrets,
|
|
55
|
+
* comments, extra keys). A matching `RASTACK_*=` line is updated in place;
|
|
56
|
+
* generated keys not yet present are appended. When there is no existing file,
|
|
57
|
+
* this is exactly `renderEnvFile(cfg)`. Pure — the IO wrapper reads/writes.
|
|
58
|
+
*/
|
|
59
|
+
export declare function applyEnvValues(existing: string, cfg: DeployConfig): string;
|
|
60
|
+
/**
|
|
61
|
+
* Return `.gitignore` content with a `.env` entry present (idempotent). Appends
|
|
62
|
+
* under a labelled block when missing, leaves the file untouched when `.env` is
|
|
63
|
+
* already ignored — so `rastack init` never duplicates the entry or clobbers a
|
|
64
|
+
* developer's existing ignore rules.
|
|
65
|
+
*/
|
|
66
|
+
export declare function gitignoreWith(existing: string, entry?: string): string;
|
|
37
67
|
export interface ScaffoldOptions {
|
|
38
68
|
/** Omit the GitHub Actions workflows (config only). */
|
|
39
69
|
noWorkflows?: boolean;
|
package/dist/deploy/workflows.js
CHANGED
|
@@ -12,11 +12,15 @@
|
|
|
12
12
|
* Pure string builders — `rastack-init.ts` is the thin wrapper that writes them.
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.DEPLOY_WORKFLOW_PATH = exports.CI_WORKFLOW_PATH = exports.CONFIG_PATH = void 0;
|
|
15
|
+
exports.GITIGNORE_PATH = exports.ENV_PATH = exports.DEPLOY_WORKFLOW_PATH = exports.CI_WORKFLOW_PATH = exports.CONFIG_PATH = void 0;
|
|
16
16
|
exports.renderCiWorkflow = renderCiWorkflow;
|
|
17
17
|
exports.renderDeployWorkflow = renderDeployWorkflow;
|
|
18
18
|
exports.renderConfigFile = renderConfigFile;
|
|
19
|
+
exports.renderEnvFile = renderEnvFile;
|
|
20
|
+
exports.applyEnvValues = applyEnvValues;
|
|
21
|
+
exports.gitignoreWith = gitignoreWith;
|
|
19
22
|
exports.scaffoldFiles = scaffoldFiles;
|
|
23
|
+
const config_1 = require("./config");
|
|
20
24
|
/** Node version the templated workflows pin (LTS — safe for a fresh repo). */
|
|
21
25
|
const NODE_VERSION = "22";
|
|
22
26
|
/**
|
|
@@ -24,6 +28,8 @@ const NODE_VERSION = "22";
|
|
|
24
28
|
* every push/PR. The clean replacement for hand-written compile/check steps.
|
|
25
29
|
*/
|
|
26
30
|
function renderCiWorkflow(cfg) {
|
|
31
|
+
const prod = (0, config_1.resolveEnvironment)(cfg, "prod");
|
|
32
|
+
const dev = (0, config_1.resolveEnvironment)(cfg, "dev");
|
|
27
33
|
return `name: CI
|
|
28
34
|
|
|
29
35
|
# Validate the resource graph and compile the manifest on every change.
|
|
@@ -32,7 +38,7 @@ function renderCiWorkflow(cfg) {
|
|
|
32
38
|
|
|
33
39
|
on:
|
|
34
40
|
push:
|
|
35
|
-
branches: [${
|
|
41
|
+
branches: [${prod.branch}, ${dev.branch}]
|
|
36
42
|
pull_request:
|
|
37
43
|
|
|
38
44
|
permissions:
|
|
@@ -61,19 +67,28 @@ jobs:
|
|
|
61
67
|
*/
|
|
62
68
|
function renderDeployWorkflow(cfg) {
|
|
63
69
|
const arch = cfg.lambda.arch === "arm64" ? "arm64" : "x86_64";
|
|
70
|
+
const prod = (0, config_1.resolveEnvironment)(cfg, "prod");
|
|
71
|
+
const dev = (0, config_1.resolveEnvironment)(cfg, "dev");
|
|
72
|
+
// Pick the environment (and rastack --env flag) from the pushed branch.
|
|
73
|
+
const ghEnv = `\${{ github.ref_name == '${prod.branch}' && '${prod.ghEnvironment}' || '${dev.ghEnvironment}' }}`;
|
|
74
|
+
const envFlag = `\${{ github.ref_name == '${prod.branch}' && 'prod' || 'dev' }}`;
|
|
64
75
|
return `name: Deploy backend
|
|
65
76
|
|
|
66
|
-
# Ship the Rust API (rastack-server) to AWS Lambda. Infrastructure
|
|
67
|
-
# once by
|
|
77
|
+
# Ship the Rust API (rastack-server) to AWS Lambda — dev and prod. Infrastructure
|
|
78
|
+
# is created once by \`rastack init\` (docs/serverless-deploy.md); this workflow
|
|
68
79
|
# only ever runs \`aws lambda update-function-code\`, wrapped by \`rastack ci\`.
|
|
69
80
|
#
|
|
70
|
-
#
|
|
81
|
+
# The environment is chosen from the branch:
|
|
82
|
+
# ${dev.branch} → ${dev.ghEnvironment} (dev stack) ${prod.branch} → ${prod.ghEnvironment} (prod stack)
|
|
83
|
+
#
|
|
84
|
+
# Each GitHub Actions Environment holds its own AWS Variables (wired by
|
|
85
|
+
# \`rastack init\` from that environment's stack Outputs):
|
|
71
86
|
# AWS_DEPLOY_ROLE_ARN DeployRoleArn (the OIDC role this job assumes)
|
|
72
87
|
# AWS_REGION Region (${cfg.region})
|
|
73
88
|
|
|
74
89
|
on:
|
|
75
90
|
push:
|
|
76
|
-
branches: [${
|
|
91
|
+
branches: [${prod.branch}, ${dev.branch}]
|
|
77
92
|
|
|
78
93
|
# Least privilege: read the repo + mint an OIDC token to assume the deploy role.
|
|
79
94
|
permissions:
|
|
@@ -81,15 +96,20 @@ permissions:
|
|
|
81
96
|
id-token: write
|
|
82
97
|
|
|
83
98
|
concurrency:
|
|
84
|
-
|
|
99
|
+
# One in-flight deploy per branch, so dev and prod never cancel each other.
|
|
100
|
+
group: deploy-backend-\${{ github.ref_name }}
|
|
85
101
|
cancel-in-progress: true
|
|
86
102
|
|
|
87
103
|
jobs:
|
|
88
104
|
deploy:
|
|
89
105
|
name: Build & deploy the Lambda (${arch})
|
|
90
106
|
runs-on: ubuntu-latest
|
|
91
|
-
#
|
|
92
|
-
#
|
|
107
|
+
# Select the GitHub Environment from the branch. Its scoped Variables
|
|
108
|
+
# (AWS_DEPLOY_ROLE_ARN / AWS_REGION) then resolve to the right stack, and
|
|
109
|
+
# prod protection rules (if any) gate the run.
|
|
110
|
+
environment: ${ghEnv}
|
|
111
|
+
# Skip on forks / environments that never wired the deploy role, so the job
|
|
112
|
+
# does not fail red for contributors who cannot (and should not) deploy.
|
|
93
113
|
if: vars.AWS_DEPLOY_ROLE_ARN != ''
|
|
94
114
|
steps:
|
|
95
115
|
- uses: actions/checkout@v4
|
|
@@ -116,10 +136,10 @@ jobs:
|
|
|
116
136
|
role-to-assume: \${{ vars.AWS_DEPLOY_ROLE_ARN }}
|
|
117
137
|
aws-region: \${{ vars.AWS_REGION }}
|
|
118
138
|
|
|
119
|
-
# One line: compile the manifest, cross-build the bundle, bake the
|
|
120
|
-
# and update-function-code
|
|
139
|
+
# One line: compile the manifest, cross-build the bundle, bake the env's
|
|
140
|
+
# Cognito JWKS, and update-function-code for that env's function.
|
|
121
141
|
- name: Build & deploy
|
|
122
|
-
run: rastack ci deploy
|
|
142
|
+
run: rastack ci deploy --env ${envFlag}
|
|
123
143
|
`;
|
|
124
144
|
}
|
|
125
145
|
/** The deploy config written to `.rastack/deploy.json`, pretty-printed. */
|
|
@@ -129,6 +149,91 @@ function renderConfigFile(cfg) {
|
|
|
129
149
|
exports.CONFIG_PATH = ".rastack/deploy.json";
|
|
130
150
|
exports.CI_WORKFLOW_PATH = ".github/workflows/ci.yml";
|
|
131
151
|
exports.DEPLOY_WORKFLOW_PATH = ".github/workflows/deploy-backend.yml";
|
|
152
|
+
exports.ENV_PATH = ".env";
|
|
153
|
+
exports.GITIGNORE_PATH = ".gitignore";
|
|
154
|
+
/**
|
|
155
|
+
* The generated `.env` — the app's **local** environment. Every value is
|
|
156
|
+
* non-secret (client ids, pool ids, public URLs from the stack Outputs), but the
|
|
157
|
+
* file is git-ignored by convention (`gitignoreWith`) so a developer can add
|
|
158
|
+
* their own secrets beside these without ever committing them. Values are blank
|
|
159
|
+
* until the matching stack is deployed; `rastack init` rewrites the file with the
|
|
160
|
+
* resolved Outputs after standing the backends up.
|
|
161
|
+
*
|
|
162
|
+
* Keys are `RASTACK_<ENV>_*` (a neutral prefix). For a browser bundle, re-export
|
|
163
|
+
* the ones you need under your bundler's public prefix (EXPO_PUBLIC_/VITE_/…);
|
|
164
|
+
* `rastack dev` also serves the dev pool config live at `/__rastack/cognito.json`.
|
|
165
|
+
*/
|
|
166
|
+
function renderEnvFile(cfg) {
|
|
167
|
+
const lines = [
|
|
168
|
+
"# Generated by `rastack init` — local app environment (git-ignored).",
|
|
169
|
+
"# Non-secret values from the CloudFormation stack Outputs; add your own",
|
|
170
|
+
"# secrets below. Re-run `rastack init` to refresh after a deploy.",
|
|
171
|
+
"",
|
|
172
|
+
`RASTACK_APP=${cfg.app}`,
|
|
173
|
+
`RASTACK_REGION=${cfg.region}`,
|
|
174
|
+
"",
|
|
175
|
+
];
|
|
176
|
+
const envKeys = [
|
|
177
|
+
["dev", "DEV"],
|
|
178
|
+
["prod", "PROD"],
|
|
179
|
+
];
|
|
180
|
+
for (const [env, prefix] of envKeys) {
|
|
181
|
+
const e = (0, config_1.resolveEnvironment)(cfg, env);
|
|
182
|
+
const o = e.outputs ?? {};
|
|
183
|
+
lines.push(`# ${env} (${e.branch} → ${e.ghEnvironment})`, `RASTACK_${prefix}_API_URL=${o.apiUrl ?? ""}`, `RASTACK_${prefix}_USER_POOL_ID=${o.userPoolId ?? ""}`, `RASTACK_${prefix}_USER_POOL_CLIENT_ID=${o.userPoolClientId ?? ""}`, `RASTACK_${prefix}_HOSTED_UI_URL=${o.hostedUiUrl ?? ""}`, `RASTACK_${prefix}_PROXY_URL=${o.proxyUrl ?? ""}`, "");
|
|
184
|
+
}
|
|
185
|
+
return lines.join("\n");
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Upsert the generated `RASTACK_*` keys from `renderEnvFile(cfg)` into an
|
|
189
|
+
* existing `.env`, preserving every other line (a developer's own secrets,
|
|
190
|
+
* comments, extra keys). A matching `RASTACK_*=` line is updated in place;
|
|
191
|
+
* generated keys not yet present are appended. When there is no existing file,
|
|
192
|
+
* this is exactly `renderEnvFile(cfg)`. Pure — the IO wrapper reads/writes.
|
|
193
|
+
*/
|
|
194
|
+
function applyEnvValues(existing, cfg) {
|
|
195
|
+
const rendered = renderEnvFile(cfg);
|
|
196
|
+
if (!existing.trim())
|
|
197
|
+
return rendered;
|
|
198
|
+
const generated = new Map();
|
|
199
|
+
for (const line of rendered.split("\n")) {
|
|
200
|
+
const m = /^(RASTACK_[A-Z0-9_]+)=(.*)$/.exec(line);
|
|
201
|
+
if (m)
|
|
202
|
+
generated.set(m[1], m[2]);
|
|
203
|
+
}
|
|
204
|
+
const seen = new Set();
|
|
205
|
+
const out = existing.split("\n").map((line) => {
|
|
206
|
+
const m = /^(RASTACK_[A-Z0-9_]+)=/.exec(line);
|
|
207
|
+
if (m && generated.has(m[1])) {
|
|
208
|
+
seen.add(m[1]);
|
|
209
|
+
return `${m[1]}=${generated.get(m[1])}`;
|
|
210
|
+
}
|
|
211
|
+
return line;
|
|
212
|
+
});
|
|
213
|
+
const missing = [...generated].filter(([k]) => !seen.has(k));
|
|
214
|
+
if (missing.length) {
|
|
215
|
+
if (out.length && out[out.length - 1].trim() !== "")
|
|
216
|
+
out.push("");
|
|
217
|
+
for (const [k, v] of missing)
|
|
218
|
+
out.push(`${k}=${v}`);
|
|
219
|
+
out.push("");
|
|
220
|
+
}
|
|
221
|
+
return out.join("\n");
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Return `.gitignore` content with a `.env` entry present (idempotent). Appends
|
|
225
|
+
* under a labelled block when missing, leaves the file untouched when `.env` is
|
|
226
|
+
* already ignored — so `rastack init` never duplicates the entry or clobbers a
|
|
227
|
+
* developer's existing ignore rules.
|
|
228
|
+
*/
|
|
229
|
+
function gitignoreWith(existing, entry = ".env") {
|
|
230
|
+
const lines = existing.split("\n").map((l) => l.trim());
|
|
231
|
+
if (lines.includes(entry))
|
|
232
|
+
return existing;
|
|
233
|
+
const base = existing.replace(/\n+$/, "");
|
|
234
|
+
const block = `# rastack — local environment (secrets)\n${entry}\n`;
|
|
235
|
+
return base ? `${base}\n\n${block}` : block;
|
|
236
|
+
}
|
|
132
237
|
/**
|
|
133
238
|
* The full set of files `rastack init` templates: the deploy config plus the two
|
|
134
239
|
* base workflows. Returned as data so the caller decides what to write, skip, or
|
package/dist/rastack-ci.js
CHANGED
|
@@ -19,9 +19,17 @@
|
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
20
|
const child_process_1 = require("child_process");
|
|
21
21
|
const compile_1 = require("./compile");
|
|
22
|
+
const config_1 = require("./deploy/config");
|
|
22
23
|
const ci_1 = require("./deploy/ci");
|
|
23
24
|
const io_1 = require("./deploy/io");
|
|
24
25
|
const PHASES = ["check", "build", "deploy"];
|
|
26
|
+
/** Coerce a string to an `EnvName`, or undefined when unset/unrecognised. */
|
|
27
|
+
function parseEnv(value) {
|
|
28
|
+
const v = (value ?? "").trim();
|
|
29
|
+
return config_1.ENV_NAMES.includes(v)
|
|
30
|
+
? v
|
|
31
|
+
: undefined;
|
|
32
|
+
}
|
|
25
33
|
function parseFlags(argv) {
|
|
26
34
|
let phase = "";
|
|
27
35
|
let plan = false;
|
|
@@ -29,6 +37,7 @@ function parseFlags(argv) {
|
|
|
29
37
|
bin: process.env.RASTACK_BIN || "rastack",
|
|
30
38
|
functionName: process.env.RASTACK_FUNCTION || undefined,
|
|
31
39
|
jwksUrl: process.env.RASTACK_JWKS_URL || undefined,
|
|
40
|
+
env: parseEnv(process.env.RASTACK_ENV),
|
|
32
41
|
};
|
|
33
42
|
for (let i = 0; i < argv.length; i++) {
|
|
34
43
|
const a = argv[i];
|
|
@@ -47,6 +56,15 @@ function parseFlags(argv) {
|
|
|
47
56
|
case "--jwks-url":
|
|
48
57
|
opts.jwksUrl = next();
|
|
49
58
|
break;
|
|
59
|
+
case "--env": {
|
|
60
|
+
const env = parseEnv(next());
|
|
61
|
+
if (!env) {
|
|
62
|
+
console.error(`Unknown --env: expected ${config_1.ENV_NAMES.join(" | ")}.`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
opts.env = env;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
50
68
|
case "--bin":
|
|
51
69
|
opts.bin = next();
|
|
52
70
|
break;
|
|
@@ -65,7 +83,7 @@ function parseFlags(argv) {
|
|
|
65
83
|
}
|
|
66
84
|
}
|
|
67
85
|
if (!phase) {
|
|
68
|
-
console.error(`Usage: rastack ci <${PHASES.join(" | ")}> [--plan] [--skip-build]\n`);
|
|
86
|
+
console.error(`Usage: rastack ci <${PHASES.join(" | ")}> [--env dev|prod] [--plan] [--skip-build]\n`);
|
|
69
87
|
process.exit(1);
|
|
70
88
|
}
|
|
71
89
|
return { phase, plan, opts };
|
package/dist/rastack-dev.js
CHANGED
|
@@ -60,6 +60,8 @@ const http = __importStar(require("http"));
|
|
|
60
60
|
const net = __importStar(require("net"));
|
|
61
61
|
const path = __importStar(require("path"));
|
|
62
62
|
const compile_1 = require("./compile");
|
|
63
|
+
const config_1 = require("./deploy/config");
|
|
64
|
+
const io_1 = require("./deploy/io");
|
|
63
65
|
const harness_1 = require("./dev/harness");
|
|
64
66
|
const HERE = __dirname; // dist/ when installed
|
|
65
67
|
/** True when `cargo` is on PATH (the from-source wasm fallback needs it). */
|
|
@@ -131,6 +133,38 @@ function serveFile(res, file) {
|
|
|
131
133
|
res.writeHead(200, { "Content-Type": (0, harness_1.contentType)(file) }).end(data);
|
|
132
134
|
});
|
|
133
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Serve the dev environment's Cognito coordinates (`/__rastack/cognito.json`) so
|
|
138
|
+
* a locally-running app can sign in against the *real* dev user pool. Read from
|
|
139
|
+
* `.rastack/deploy.json`'s dev outputs; `{}` when the dev stack isn't deployed
|
|
140
|
+
* yet (the app then falls back to `mode:"local"`). Returns whether a real config
|
|
141
|
+
* was found, for the startup hint.
|
|
142
|
+
*/
|
|
143
|
+
function serveDevCognito(res, cwd) {
|
|
144
|
+
let body = "{}";
|
|
145
|
+
try {
|
|
146
|
+
const cfg = (0, io_1.loadConfig)(cwd);
|
|
147
|
+
const runtime = cfg ? (0, config_1.cognitoRuntimeConfig)(cfg, "dev") : null;
|
|
148
|
+
if (runtime)
|
|
149
|
+
body = JSON.stringify(runtime);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
/* no/invalid config → empty object, app uses the local shim */
|
|
153
|
+
}
|
|
154
|
+
res
|
|
155
|
+
.writeHead(200, { "Content-Type": "application/json; charset=utf-8" })
|
|
156
|
+
.end(body);
|
|
157
|
+
}
|
|
158
|
+
/** Whether a real dev Cognito config is available to advertise on startup. */
|
|
159
|
+
function hasDevCognito(cwd) {
|
|
160
|
+
try {
|
|
161
|
+
const cfg = (0, io_1.loadConfig)(cwd);
|
|
162
|
+
return Boolean(cfg && (0, config_1.cognitoRuntimeConfig)(cfg, "dev"));
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
134
168
|
/** Best-effort open the default browser at `url`. */
|
|
135
169
|
function openBrowser(url) {
|
|
136
170
|
const cmd = process.platform === "darwin"
|
|
@@ -190,7 +224,7 @@ function getFreePort() {
|
|
|
190
224
|
srv.listen(0, "127.0.0.1", () => {
|
|
191
225
|
const addr = srv.address();
|
|
192
226
|
const port = addr && typeof addr === "object" ? addr.port : 0;
|
|
193
|
-
srv.close(() =>
|
|
227
|
+
srv.close(() => port ? resolve(port) : reject(new Error("no free port")));
|
|
194
228
|
});
|
|
195
229
|
});
|
|
196
230
|
}
|
|
@@ -240,7 +274,13 @@ function waitForMetro(port, timeoutMs = 120000) {
|
|
|
240
274
|
}
|
|
241
275
|
/** Reverse-proxy one HTTP request/response pair to Metro. */
|
|
242
276
|
function proxyToMetro(req, res, port) {
|
|
243
|
-
const proxyReq = http.request({
|
|
277
|
+
const proxyReq = http.request({
|
|
278
|
+
host: "127.0.0.1",
|
|
279
|
+
port,
|
|
280
|
+
method: req.method,
|
|
281
|
+
path: req.url,
|
|
282
|
+
headers: req.headers,
|
|
283
|
+
}, (proxyRes) => {
|
|
244
284
|
res.writeHead(proxyRes.statusCode || 502, proxyRes.headers);
|
|
245
285
|
proxyRes.pipe(res);
|
|
246
286
|
});
|
|
@@ -310,6 +350,8 @@ async function runMetroMode(args, cwd) {
|
|
|
310
350
|
return serveUnder(res, wasmDir, url.slice("/wasm/".length));
|
|
311
351
|
if (url === "/schema.rastack.json" || url === "/openapi.json")
|
|
312
352
|
return serveUnder(res, args.outDir, path.basename(url));
|
|
353
|
+
if (url === "/__rastack/cognito.json")
|
|
354
|
+
return serveDevCognito(res, cwd);
|
|
313
355
|
if (url.startsWith("/data/warehouse/"))
|
|
314
356
|
return serveUnder(res, args.warehouseDir, url.slice("/data/warehouse/".length));
|
|
315
357
|
// Everything else → the live Metro bundle (HTML, JS, assets, HMR polls).
|
|
@@ -372,6 +414,8 @@ async function main(argv) {
|
|
|
372
414
|
return serveUnder(res, wasmDir, url.slice("/wasm/".length));
|
|
373
415
|
if (url === "/schema.rastack.json" || url === "/openapi.json")
|
|
374
416
|
return serveUnder(res, args.outDir, path.basename(url));
|
|
417
|
+
if (url === "/__rastack/cognito.json")
|
|
418
|
+
return serveDevCognito(res, cwd);
|
|
375
419
|
if (url.startsWith("/data/warehouse/"))
|
|
376
420
|
return serveUnder(res, args.warehouseDir, url.slice("/data/warehouse/".length));
|
|
377
421
|
// No app → the Django-style welcome page (root only; else 404).
|
|
@@ -404,6 +448,8 @@ async function main(argv) {
|
|
|
404
448
|
console.log(` ✓ app: ${path.relative(process.cwd(), appRoot) || "."}`);
|
|
405
449
|
else
|
|
406
450
|
console.log(` ○ no app found — serving the welcome page`);
|
|
451
|
+
if (hasDevCognito(cwd))
|
|
452
|
+
console.log(` ✓ dev Cognito: /__rastack/cognito.json (sign in against the real dev pool)`);
|
|
407
453
|
console.log(`\n ▸ ${url}\n`);
|
|
408
454
|
console.log(" Need the database console? Run `rastack admin`. Ctrl+C to stop.\n");
|
|
409
455
|
if (args.open)
|