@sponson/sim 0.0.0-stage → 0.5.0
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/LICENSE +202 -0
- package/README.md +9 -2
- package/dist/auth.d.ts +60 -0
- package/dist/auth.js +195 -0
- package/dist/auth.js.map +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +16 -0
- package/dist/bin.js.map +1 -0
- package/dist/chaos.d.ts +74 -0
- package/dist/chaos.js +80 -0
- package/dist/chaos.js.map +1 -0
- package/dist/cli.d.ts +29 -0
- package/dist/cli.js +99 -0
- package/dist/cli.js.map +1 -0
- package/dist/demo.d.ts +39 -0
- package/dist/demo.js +168 -0
- package/dist/demo.js.map +1 -0
- package/dist/graphql.d.ts +22 -0
- package/dist/graphql.js +36 -0
- package/dist/graphql.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/provider.d.ts +138 -0
- package/dist/provider.js +68 -0
- package/dist/provider.js.map +1 -0
- package/dist/routes/algolia.d.ts +37 -0
- package/dist/routes/algolia.js +83 -0
- package/dist/routes/algolia.js.map +1 -0
- package/dist/routes/aws.d.ts +43 -0
- package/dist/routes/aws.js +211 -0
- package/dist/routes/aws.js.map +1 -0
- package/dist/routes/clerk.d.ts +33 -0
- package/dist/routes/clerk.js +85 -0
- package/dist/routes/clerk.js.map +1 -0
- package/dist/routes/cloudflare.d.ts +80 -0
- package/dist/routes/cloudflare.js +176 -0
- package/dist/routes/cloudflare.js.map +1 -0
- package/dist/routes/digitalocean.d.ts +58 -0
- package/dist/routes/digitalocean.js +158 -0
- package/dist/routes/digitalocean.js.map +1 -0
- package/dist/routes/fly.d.ts +22 -0
- package/dist/routes/fly.js +113 -0
- package/dist/routes/fly.js.map +1 -0
- package/dist/routes/launchdarkly.d.ts +98 -0
- package/dist/routes/launchdarkly.js +213 -0
- package/dist/routes/launchdarkly.js.map +1 -0
- package/dist/routes/neon.d.ts +73 -0
- package/dist/routes/neon.js +184 -0
- package/dist/routes/neon.js.map +1 -0
- package/dist/routes/netlify.d.ts +126 -0
- package/dist/routes/netlify.js +347 -0
- package/dist/routes/netlify.js.map +1 -0
- package/dist/routes/planetscale.d.ts +95 -0
- package/dist/routes/planetscale.js +234 -0
- package/dist/routes/planetscale.js.map +1 -0
- package/dist/routes/railway.d.ts +24 -0
- package/dist/routes/railway.js +112 -0
- package/dist/routes/railway.js.map +1 -0
- package/dist/routes/rest.d.ts +138 -0
- package/dist/routes/rest.js +462 -0
- package/dist/routes/rest.js.map +1 -0
- package/dist/routes/supabase.d.ts +103 -0
- package/dist/routes/supabase.js +234 -0
- package/dist/routes/supabase.js.map +1 -0
- package/dist/routes/vercel.d.ts +114 -0
- package/dist/routes/vercel.js +370 -0
- package/dist/routes/vercel.js.map +1 -0
- package/dist/routes/xata.d.ts +57 -0
- package/dist/routes/xata.js +115 -0
- package/dist/routes/xata.js.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +189 -0
- package/dist/server.js.map +1 -0
- package/dist/state.d.ts +102 -0
- package/dist/state.js +121 -0
- package/dist/state.js.map +1 -0
- package/package.json +51 -3
package/dist/chaos.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** No chaos: every request is answered normally and immediately. */
|
|
2
|
+
export const DEFAULT_CHAOS = {
|
|
3
|
+
latency_ms: 0,
|
|
4
|
+
fail_next: 0,
|
|
5
|
+
status: 503,
|
|
6
|
+
drop_response_next: 0,
|
|
7
|
+
hang_next: 0,
|
|
8
|
+
hold_next: 0,
|
|
9
|
+
deploy: "ok",
|
|
10
|
+
deploy_ms: 0,
|
|
11
|
+
neon_op_ms: 0,
|
|
12
|
+
planetscale_ready_ms: 0,
|
|
13
|
+
supabase_ready_ms: 0,
|
|
14
|
+
page_size: 0,
|
|
15
|
+
env_upsert_fail: [],
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Every key `POST /_chaos` accepts besides `drift`; anything else is rejected so a misspelt key cannot silently
|
|
19
|
+
* do nothing.
|
|
20
|
+
*/
|
|
21
|
+
export const CHAOS_KEYS = new Set([...Object.keys(DEFAULT_CHAOS), "fail_on", "retry_after"]);
|
|
22
|
+
/** A fresh default config (no shared arrays). */
|
|
23
|
+
export function defaultChaos() {
|
|
24
|
+
return { ...DEFAULT_CHAOS, env_upsert_fail: [] };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Decide what chaos does to this request, consuming one count of `c` when it does something.
|
|
28
|
+
* Writes are eligible when `fail_on` is absent or matches; reads only when a GET rule matches.
|
|
29
|
+
*/
|
|
30
|
+
export function chaosFor(c, method, path) {
|
|
31
|
+
if (c.hold_next <= 0 && c.hang_next <= 0 && c.drop_response_next <= 0 && c.fail_next <= 0)
|
|
32
|
+
return null;
|
|
33
|
+
return affected(c, method, path) ? consume(c) : null;
|
|
34
|
+
}
|
|
35
|
+
/** Whether `fail_on` lets chaos hit this request: writes by default, reads only when a GET rule names them. */
|
|
36
|
+
function affected(c, method, path) {
|
|
37
|
+
const rules = c.fail_on === undefined ? [] : Array.isArray(c.fail_on) ? c.fail_on : [c.fail_on];
|
|
38
|
+
if (method === "GET" || method === "HEAD")
|
|
39
|
+
return rules.some((r) => ruleMethod(r) === "GET" && matchesRule(r, "GET", path));
|
|
40
|
+
return rules.length === 0 || rules.some((r) => matchesRule(r, method, path));
|
|
41
|
+
}
|
|
42
|
+
/** Use up one pending chaos action, in the order hold, hang, drop, fail. */
|
|
43
|
+
function consume(c) {
|
|
44
|
+
if (c.hold_next > 0) {
|
|
45
|
+
c.hold_next -= 1;
|
|
46
|
+
return { kind: "hold" };
|
|
47
|
+
}
|
|
48
|
+
if (c.hang_next > 0) {
|
|
49
|
+
c.hang_next -= 1;
|
|
50
|
+
return { kind: "hang" };
|
|
51
|
+
}
|
|
52
|
+
if (c.drop_response_next > 0) {
|
|
53
|
+
c.drop_response_next -= 1;
|
|
54
|
+
return { kind: "drop" };
|
|
55
|
+
}
|
|
56
|
+
if (c.fail_next > 0) {
|
|
57
|
+
c.fail_next -= 1;
|
|
58
|
+
const headers = c.status === 429 && c.retry_after !== undefined ? { "retry-after": String(c.retry_after) } : {};
|
|
59
|
+
return { kind: "fail", status: c.status, headers };
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
/** `"DELETE /neon/*"`: method must match exactly (or be `*`), `*` in the path matches anything. */
|
|
64
|
+
export function matchesRule(rule, method, path) {
|
|
65
|
+
const space = rule.indexOf(" ");
|
|
66
|
+
const m = ruleMethod(rule);
|
|
67
|
+
const ruleGlob = space < 0 ? "*" : rule.slice(space + 1).trim();
|
|
68
|
+
if (m !== "*" && m !== method.toUpperCase())
|
|
69
|
+
return false;
|
|
70
|
+
const re = new RegExp("^" + ruleGlob.split("*").map(escapeRe).join(".*") + "$");
|
|
71
|
+
return re.test(path);
|
|
72
|
+
}
|
|
73
|
+
function escapeRe(s) {
|
|
74
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
75
|
+
}
|
|
76
|
+
function ruleMethod(rule) {
|
|
77
|
+
const space = rule.indexOf(" ");
|
|
78
|
+
return (space < 0 ? rule : rule.slice(0, space)).toUpperCase();
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=chaos.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chaos.js","sourceRoot":"","sources":["../src/chaos.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type SimHandle } from "./index.js";
|
|
2
|
+
/** Where the command line writes and what it reads; the bin passes the process's. */
|
|
3
|
+
export interface SimCliIO {
|
|
4
|
+
stdout(s: string): void;
|
|
5
|
+
stderr(s: string): void;
|
|
6
|
+
env: NodeJS.ProcessEnv;
|
|
7
|
+
cwd: string;
|
|
8
|
+
}
|
|
9
|
+
/** What the arguments ask for. */
|
|
10
|
+
export type SimCommand = {
|
|
11
|
+
kind: "serve";
|
|
12
|
+
port: number;
|
|
13
|
+
demo?: string;
|
|
14
|
+
} | {
|
|
15
|
+
kind: "env";
|
|
16
|
+
port: number;
|
|
17
|
+
} | {
|
|
18
|
+
kind: "help";
|
|
19
|
+
};
|
|
20
|
+
/** How the command ended: an exit code, or a running sim the caller keeps alive until a signal. */
|
|
21
|
+
export type SimCliResult = {
|
|
22
|
+
code: number;
|
|
23
|
+
sim?: SimHandle;
|
|
24
|
+
};
|
|
25
|
+
export declare const USAGE = "usage: sponson-sim [--port N] [--demo <dir>]\n sponson-sim env [--port N]\n\n --port N listen on 127.0.0.1:N (default: $SPONSON_SIM_PORT, else 4777)\n --demo <dir> also write a ready demo repository into <dir> (new or empty) and print what to run next\n env print the `export` lines that point sponson at a sim on that port, for eval \"$(sponson-sim env)\"\n";
|
|
26
|
+
/** Parse argv (without node and the script). Throws with a message for the user on anything it does not know. */
|
|
27
|
+
export declare function parseSimArgs(argv: string[], env: NodeJS.ProcessEnv): SimCommand;
|
|
28
|
+
/** Run the command line. A started sim is returned running; the caller decides when to close it. */
|
|
29
|
+
export declare function runSimCli(argv: string[], io: SimCliIO): Promise<SimCliResult>;
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `sponson-sim` command line, kept out of bin.ts so it can be tested in-process:
|
|
3
|
+
* sponson-sim [--port N] start the fake cloud and print the env that points sponson at it
|
|
4
|
+
* sponson-sim --demo <dir> [--port N] the same, plus a ready demo repository in <dir> and what to run next
|
|
5
|
+
* sponson-sim env [--port N] print only the `export` lines, for `eval "$(sponson-sim env)"`
|
|
6
|
+
* The demo lives here rather than in the `sponson` CLI, whose command set is fixed (docs/adr/0002-three-commands.md),
|
|
7
|
+
* and because it is about the fake cloud: it only makes sense with a sim running.
|
|
8
|
+
*/
|
|
9
|
+
import { resolve } from "node:path";
|
|
10
|
+
import { parseArgs } from "node:util";
|
|
11
|
+
import { demoInstructions, envExports, writeDemo } from "./demo.js";
|
|
12
|
+
import { startSim } from "./index.js";
|
|
13
|
+
export const USAGE = `usage: sponson-sim [--port N] [--demo <dir>]
|
|
14
|
+
sponson-sim env [--port N]
|
|
15
|
+
|
|
16
|
+
--port N listen on 127.0.0.1:N (default: $SPONSON_SIM_PORT, else 4777)
|
|
17
|
+
--demo <dir> also write a ready demo repository into <dir> (new or empty) and print what to run next
|
|
18
|
+
env print the \`export\` lines that point sponson at a sim on that port, for eval "$(sponson-sim env)"
|
|
19
|
+
`;
|
|
20
|
+
/** Parse argv (without node and the script). Throws with a message for the user on anything it does not know. */
|
|
21
|
+
export function parseSimArgs(argv, env) {
|
|
22
|
+
const { values, positionals } = parseArgs({
|
|
23
|
+
args: argv,
|
|
24
|
+
options: { port: { type: "string" }, demo: { type: "string" }, help: { type: "boolean", short: "h" } },
|
|
25
|
+
allowPositionals: true,
|
|
26
|
+
strict: true,
|
|
27
|
+
});
|
|
28
|
+
if (values.help)
|
|
29
|
+
return { kind: "help" };
|
|
30
|
+
const port = portFrom(values.port, env);
|
|
31
|
+
const [command, ...rest] = positionals;
|
|
32
|
+
if (rest.length > 0 || (command !== undefined && command !== "env"))
|
|
33
|
+
throw new Error(`unexpected argument: ${[command, ...rest].join(" ")}`);
|
|
34
|
+
if (command === "env") {
|
|
35
|
+
if (values.demo !== undefined)
|
|
36
|
+
throw new Error("--demo starts a sim; it cannot be combined with `env`");
|
|
37
|
+
return { kind: "env", port };
|
|
38
|
+
}
|
|
39
|
+
if (values.demo !== undefined && values.demo.trim() === "")
|
|
40
|
+
throw new Error("--demo needs a directory");
|
|
41
|
+
return { kind: "serve", port, ...(values.demo !== undefined ? { demo: values.demo } : {}) };
|
|
42
|
+
}
|
|
43
|
+
/** `--port`, else SPONSON_SIM_PORT, else 4777. */
|
|
44
|
+
function portFrom(flag, env) {
|
|
45
|
+
// A blank SPONSON_SIM_PORT is "not set", as CI systems export variables that do not apply.
|
|
46
|
+
const fromEnv = env.SPONSON_SIM_PORT?.trim();
|
|
47
|
+
const raw = flag ?? (fromEnv === undefined || fromEnv === "" ? "4777" : fromEnv);
|
|
48
|
+
const port = Number(raw);
|
|
49
|
+
if (raw.trim() === "" || !Number.isInteger(port) || port < 0 || port > 65535)
|
|
50
|
+
throw new Error(`invalid port: ${raw}`);
|
|
51
|
+
return port;
|
|
52
|
+
}
|
|
53
|
+
/** Run the command line. A started sim is returned running; the caller decides when to close it. */
|
|
54
|
+
export async function runSimCli(argv, io) {
|
|
55
|
+
let cmd;
|
|
56
|
+
try {
|
|
57
|
+
cmd = parseSimArgs(argv, io.env);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
io.stderr(`sponson-sim: ${e.message}\n${USAGE}`);
|
|
61
|
+
return { code: 2 };
|
|
62
|
+
}
|
|
63
|
+
if (cmd.kind === "help") {
|
|
64
|
+
io.stdout(USAGE);
|
|
65
|
+
return { code: 0 };
|
|
66
|
+
}
|
|
67
|
+
if (cmd.kind === "env") {
|
|
68
|
+
io.stdout(`${envExports(`http://127.0.0.1:${String(cmd.port)}`).join("\n")}\n`);
|
|
69
|
+
return { code: 0 };
|
|
70
|
+
}
|
|
71
|
+
return serve(cmd, io);
|
|
72
|
+
}
|
|
73
|
+
async function serve(cmd, io) {
|
|
74
|
+
let sim;
|
|
75
|
+
try {
|
|
76
|
+
sim = await startSim({ port: cmd.port });
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
const busy = e.code === "EADDRINUSE";
|
|
80
|
+
io.stderr(`sponson-sim: ${busy ? `port ${String(cmd.port)} is in use (another sponson-sim?); pass --port N or set SPONSON_SIM_PORT` : e.message}\n`);
|
|
81
|
+
return { code: 1 };
|
|
82
|
+
}
|
|
83
|
+
if (cmd.demo === undefined) {
|
|
84
|
+
io.stdout(`sponson-sim listening on ${sim.url}\n# paste into another shell to point sponson at the sim:\n${envExports(sim.url).join("\n")}\n`);
|
|
85
|
+
return { code: 0, sim };
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
// Written after the port is ours: a busy port must not leave a demo behind that points at someone else's sim.
|
|
89
|
+
await writeDemo(resolve(io.cwd, cmd.demo), sim.url);
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
await sim.close();
|
|
93
|
+
io.stderr(`sponson-sim: ${e.message}\n`);
|
|
94
|
+
return { code: 1 };
|
|
95
|
+
}
|
|
96
|
+
io.stdout(demoInstructions(cmd.demo, sim.url));
|
|
97
|
+
return { code: 0, sim };
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/demo.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** The branch the demo commit is on; Vercel variables are written for it. */
|
|
2
|
+
export declare const DEMO_BRANCH = "feat/checkout";
|
|
3
|
+
/** The pull request the demo pretends to be, so the scope reads `pr-42` as in the README. */
|
|
4
|
+
export declare const DEMO_PR = "42";
|
|
5
|
+
/** The file `source` loads in the second terminal. */
|
|
6
|
+
export declare const DEMO_ENV_FILE = "sim.env";
|
|
7
|
+
/**
|
|
8
|
+
* Comments carry the explanation, because a newcomer reads this file before running anything. It uses only the
|
|
9
|
+
* projects every provider's default seed creates (`prj_demo`, `proj_demo`; Clerk needs none).
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEMO_PLAN = "# A release plan for a preview of this branch, against the fake cloud started by `sponson-sim --demo`.\nversion: 1\n# Receipts (what each run did) go to .sponson/receipts in this directory: the demo has no git remote.\nreceipts: local\nproviders:\n vercel: { project: prj_demo }\n neon: { project: proj_demo }\n\nchanges:\n # 1. A Neon database branch for this preview, forked from main.\n - id: db\n adapter: neon\n op: branch\n parent: main\n environments: [preview]\n\n # 2. The branch's connection string as the preview's DATABASE_URL. A reference, never a value: nobody copies\n # a password between consoles, and it is masked in every line Sponson prints.\n - id: env\n adapter: vercel\n op: env\n target: preview\n values:\n DATABASE_URL: { from: db.connection_string }\n environments: [preview]\n\n # 3. The preview's URL as an allowed Clerk redirect. The URL exists only once Vercel has deployed this commit,\n # so this line waits for the deploy.\n - id: callback\n adapter: clerk\n op: redirect_allow\n url: { from: env.preview_url }\n environments: [preview]\n";
|
|
12
|
+
/** A console edit, as the sim's drift control applies it: someone pastes another database into DATABASE_URL. */
|
|
13
|
+
export declare const DEMO_DRIFT: {
|
|
14
|
+
"vercel.env.preview.DATABASE_URL": string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* What to run in the second terminal, in order. No inline comments: interactive zsh (macOS's default shell)
|
|
18
|
+
* treats `#` as an argument unless `interactivecomments` is set, so a pasted comment would break the command.
|
|
19
|
+
*/
|
|
20
|
+
export declare function demoSteps(dir: string): string[];
|
|
21
|
+
/**
|
|
22
|
+
* The env file: the sim's URLs and placeholder tokens (`simEnv`), `SPONSON_SIM_URL` for the drift command, and the
|
|
23
|
+
* pull request number. Setting `SPONSON_CTX_PR` also keeps `sponson` from asking `gh` which pull request this is,
|
|
24
|
+
* which in a repository without a remote only costs time. Without a global install, `sponson` falls back to npx.
|
|
25
|
+
*/
|
|
26
|
+
export declare function demoEnvFile(url: string): string;
|
|
27
|
+
/** `export NAME=value` for everything a shell needs to point `sponson` at the sim at `url`. */
|
|
28
|
+
export declare function envExports(url: string): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Write the demo into `dir`: the plan, a .gitignore, the env file, and one commit on `DEMO_BRANCH`. The commit's
|
|
31
|
+
* author, dates and contents are fixed, so its sha (and with it the preview URL the sim derives from it) is the
|
|
32
|
+
* same on every machine. The env file is ignored: it holds the port, which differs between runs.
|
|
33
|
+
* Refuses a non-empty directory rather than mixing the demo into someone's files.
|
|
34
|
+
*/
|
|
35
|
+
export declare function writeDemo(dir: string, url: string): Promise<void>;
|
|
36
|
+
/** The walkthrough printed once the sim is up and the demo is written. */
|
|
37
|
+
export declare function demoInstructions(dir: string, url: string): string;
|
|
38
|
+
/** Quote a path for a POSIX shell only when it needs it, so the common case reads naturally. */
|
|
39
|
+
export declare function shellQuote(s: string): string;
|
package/dist/demo.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The zero-account demo behind `sponson-sim --demo <dir>`: a git repository holding a plan wired to the projects
|
|
3
|
+
* the sim seeds by default, an env file that points `sponson` at the running sim, and the commands to run next.
|
|
4
|
+
* The README's "Try it in 60 seconds" block is these commands verbatim; `scenarios/docs/try-it.test.ts` runs that
|
|
5
|
+
* block and checks it against what `demoSteps` prints, so the two cannot drift apart.
|
|
6
|
+
*/
|
|
7
|
+
import { execFile } from "node:child_process";
|
|
8
|
+
import { mkdir, readdir, writeFile } from "node:fs/promises";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { promisify } from "node:util";
|
|
11
|
+
import { simEnv } from "./index.js";
|
|
12
|
+
const exec = promisify(execFile);
|
|
13
|
+
/** The branch the demo commit is on; Vercel variables are written for it. */
|
|
14
|
+
export const DEMO_BRANCH = "feat/checkout";
|
|
15
|
+
/** The pull request the demo pretends to be, so the scope reads `pr-42` as in the README. */
|
|
16
|
+
export const DEMO_PR = "42";
|
|
17
|
+
/** The file `source` loads in the second terminal. */
|
|
18
|
+
export const DEMO_ENV_FILE = "sim.env";
|
|
19
|
+
/**
|
|
20
|
+
* Comments carry the explanation, because a newcomer reads this file before running anything. It uses only the
|
|
21
|
+
* projects every provider's default seed creates (`prj_demo`, `proj_demo`; Clerk needs none).
|
|
22
|
+
*/
|
|
23
|
+
export const DEMO_PLAN = `# A release plan for a preview of this branch, against the fake cloud started by \`sponson-sim --demo\`.
|
|
24
|
+
version: 1
|
|
25
|
+
# Receipts (what each run did) go to .sponson/receipts in this directory: the demo has no git remote.
|
|
26
|
+
receipts: local
|
|
27
|
+
providers:
|
|
28
|
+
vercel: { project: prj_demo }
|
|
29
|
+
neon: { project: proj_demo }
|
|
30
|
+
|
|
31
|
+
changes:
|
|
32
|
+
# 1. A Neon database branch for this preview, forked from main.
|
|
33
|
+
- id: db
|
|
34
|
+
adapter: neon
|
|
35
|
+
op: branch
|
|
36
|
+
parent: main
|
|
37
|
+
environments: [preview]
|
|
38
|
+
|
|
39
|
+
# 2. The branch's connection string as the preview's DATABASE_URL. A reference, never a value: nobody copies
|
|
40
|
+
# a password between consoles, and it is masked in every line Sponson prints.
|
|
41
|
+
- id: env
|
|
42
|
+
adapter: vercel
|
|
43
|
+
op: env
|
|
44
|
+
target: preview
|
|
45
|
+
values:
|
|
46
|
+
DATABASE_URL: { from: db.connection_string }
|
|
47
|
+
environments: [preview]
|
|
48
|
+
|
|
49
|
+
# 3. The preview's URL as an allowed Clerk redirect. The URL exists only once Vercel has deployed this commit,
|
|
50
|
+
# so this line waits for the deploy.
|
|
51
|
+
- id: callback
|
|
52
|
+
adapter: clerk
|
|
53
|
+
op: redirect_allow
|
|
54
|
+
url: { from: env.preview_url }
|
|
55
|
+
environments: [preview]
|
|
56
|
+
`;
|
|
57
|
+
/** A console edit, as the sim's drift control applies it: someone pastes another database into DATABASE_URL. */
|
|
58
|
+
export const DEMO_DRIFT = { "vercel.env.preview.DATABASE_URL": "postgres://typed-in-the-console" };
|
|
59
|
+
/**
|
|
60
|
+
* What to run in the second terminal, in order. No inline comments: interactive zsh (macOS's default shell)
|
|
61
|
+
* treats `#` as an argument unless `interactivecomments` is set, so a pasted comment would break the command.
|
|
62
|
+
*/
|
|
63
|
+
export function demoSteps(dir) {
|
|
64
|
+
return [
|
|
65
|
+
`cd ${shellQuote(dir)} && source ${DEMO_ENV_FILE}`,
|
|
66
|
+
"sponson plan",
|
|
67
|
+
"sponson apply",
|
|
68
|
+
"sponson apply",
|
|
69
|
+
`curl -s -o /dev/null "$SPONSON_SIM_URL/_chaos" -d '${JSON.stringify({ drift: DEMO_DRIFT })}'`,
|
|
70
|
+
"sponson plan",
|
|
71
|
+
"sponson apply --destroy",
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
/** One line per step of `demoSteps`, for the printed walkthrough. */
|
|
75
|
+
const STEP_NOTES = [
|
|
76
|
+
"point sponson at the fake cloud (placeholder tokens, no accounts)",
|
|
77
|
+
"the diff: a branch to create, a variable and a callback waiting on it",
|
|
78
|
+
"creates the branch, injects DATABASE_URL by reference, finds the preview deploy, allows its URL",
|
|
79
|
+
"again: every line unchanged, zero writes",
|
|
80
|
+
"play a human editing DATABASE_URL in the Vercel console",
|
|
81
|
+
"the drift is caught: that line is blocked until you `sponson apply --reconcile`",
|
|
82
|
+
"removes everything this scope created, in reverse order",
|
|
83
|
+
];
|
|
84
|
+
/**
|
|
85
|
+
* The env file: the sim's URLs and placeholder tokens (`simEnv`), `SPONSON_SIM_URL` for the drift command, and the
|
|
86
|
+
* pull request number. Setting `SPONSON_CTX_PR` also keeps `sponson` from asking `gh` which pull request this is,
|
|
87
|
+
* which in a repository without a remote only costs time. Without a global install, `sponson` falls back to npx.
|
|
88
|
+
*/
|
|
89
|
+
export function demoEnvFile(url) {
|
|
90
|
+
const lines = [
|
|
91
|
+
"# Points sponson at the fake cloud started by `sponson-sim --demo`. Load it with: source sim.env",
|
|
92
|
+
...envExports(url),
|
|
93
|
+
"# The demo pretends this branch is pull request #42 (scope pr-42).",
|
|
94
|
+
`export SPONSON_CTX_PR=${DEMO_PR}`,
|
|
95
|
+
"# Not installed globally? Run the published CLI through npx.",
|
|
96
|
+
'command -v sponson >/dev/null 2>&1 || sponson() { npx --yes sponson "$@"; }',
|
|
97
|
+
];
|
|
98
|
+
return `${lines.join("\n")}\n`;
|
|
99
|
+
}
|
|
100
|
+
/** `export NAME=value` for everything a shell needs to point `sponson` at the sim at `url`. */
|
|
101
|
+
export function envExports(url) {
|
|
102
|
+
return [`export SPONSON_SIM_URL=${url}`, ...Object.entries(simEnv(url)).map(([k, v]) => `export ${k}=${v}`)];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Write the demo into `dir`: the plan, a .gitignore, the env file, and one commit on `DEMO_BRANCH`. The commit's
|
|
106
|
+
* author, dates and contents are fixed, so its sha (and with it the preview URL the sim derives from it) is the
|
|
107
|
+
* same on every machine. The env file is ignored: it holds the port, which differs between runs.
|
|
108
|
+
* Refuses a non-empty directory rather than mixing the demo into someone's files.
|
|
109
|
+
*/
|
|
110
|
+
export async function writeDemo(dir, url) {
|
|
111
|
+
const existing = await readdir(dir).catch((e) => {
|
|
112
|
+
if (e.code === "ENOENT")
|
|
113
|
+
return [];
|
|
114
|
+
throw e;
|
|
115
|
+
});
|
|
116
|
+
if (existing.length > 0)
|
|
117
|
+
throw new Error(`${dir} is not empty; pass a new directory to --demo (or delete this one)`);
|
|
118
|
+
await mkdir(dir, { recursive: true });
|
|
119
|
+
await writeFile(join(dir, "release.plan.yaml"), DEMO_PLAN);
|
|
120
|
+
await writeFile(join(dir, ".gitignore"), `.sponson/\n${DEMO_ENV_FILE}\n`);
|
|
121
|
+
await writeFile(join(dir, DEMO_ENV_FILE), demoEnvFile(url));
|
|
122
|
+
await git(dir, ["init", "-q"]);
|
|
123
|
+
await git(dir, ["checkout", "-q", "-b", DEMO_BRANCH]);
|
|
124
|
+
await git(dir, ["add", "release.plan.yaml", ".gitignore"]);
|
|
125
|
+
await git(dir, ["commit", "-q", "--no-verify", "-m", "Add the Sponson demo plan"]);
|
|
126
|
+
}
|
|
127
|
+
/** The walkthrough printed once the sim is up and the demo is written. */
|
|
128
|
+
export function demoInstructions(dir, url) {
|
|
129
|
+
const steps = demoSteps(dir);
|
|
130
|
+
return [
|
|
131
|
+
`Fake cloud (Vercel, Neon and Clerk APIs) listening on ${url}. State lives in memory; Ctrl-C stops it.`,
|
|
132
|
+
`Demo repository written to ${dir}: release.plan.yaml creates a Neon branch, puts its connection string`,
|
|
133
|
+
"into Vercel's DATABASE_URL by reference, waits for the preview deploy, then allows its URL in Clerk.",
|
|
134
|
+
"",
|
|
135
|
+
"In a second terminal, run these one at a time:",
|
|
136
|
+
"",
|
|
137
|
+
...steps.map((s) => ` ${s}`),
|
|
138
|
+
"",
|
|
139
|
+
"What each one does:",
|
|
140
|
+
"",
|
|
141
|
+
...STEP_NOTES.map((note, i) => ` ${String(i + 1)}. ${note}`),
|
|
142
|
+
"",
|
|
143
|
+
].join("\n");
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Git with the demo's fixed identity. Inherited GIT_DIR / GIT_WORK_TREE / GIT_INDEX_FILE would point it at
|
|
147
|
+
* another repository (they are set inside git hooks), and a user's signing or hook settings would change the
|
|
148
|
+
* commit, so both are overridden.
|
|
149
|
+
*/
|
|
150
|
+
async function git(cwd, args) {
|
|
151
|
+
const { GIT_DIR: _d, GIT_WORK_TREE: _w, GIT_INDEX_FILE: _i, ...env } = process.env;
|
|
152
|
+
const fixed = { GIT_AUTHOR_NAME: "Sponson demo", GIT_AUTHOR_EMAIL: "demo@sponson.invalid", GIT_COMMITTER_NAME: "Sponson demo", GIT_COMMITTER_EMAIL: "demo@sponson.invalid", GIT_AUTHOR_DATE: "2026-01-01T00:00:00Z", GIT_COMMITTER_DATE: "2026-01-01T00:00:00Z" };
|
|
153
|
+
const config = ["-c", "commit.gpgsign=false", "-c", "core.autocrlf=false", "-c", "init.defaultBranch=main"];
|
|
154
|
+
try {
|
|
155
|
+
await exec("git", [...config, ...args], { cwd, env: { ...env, ...fixed } });
|
|
156
|
+
}
|
|
157
|
+
catch (e) {
|
|
158
|
+
const err = e;
|
|
159
|
+
if (err.code === "ENOENT")
|
|
160
|
+
throw new Error("the demo needs git on PATH (Sponson reads the branch and commit from it)");
|
|
161
|
+
throw new Error(`git ${args.join(" ")} failed: ${(err.stderr ?? err.message).trim()}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/** Quote a path for a POSIX shell only when it needs it, so the common case reads naturally. */
|
|
165
|
+
export function shellQuote(s) {
|
|
166
|
+
return /^[\w@%+=:,./-]+$/.test(s) ? s : `'${s.replaceAll("'", `'\\''`)}'`;
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=demo.js.map
|
package/dist/demo.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"demo.js","sourceRoot":"","sources":["../src/demo.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the GraphQL sims (routes/railway.ts, routes/fly.ts) share: reading an operation's kind and root field from a
|
|
3
|
+
* request body, and answering in the GraphQL envelope. They do not parse GraphQL: a simulated schema is a table of
|
|
4
|
+
* root fields, each resolved from the request's variables, so a plan line must pass its inputs as variables (as the
|
|
5
|
+
* official clients do), not inline.
|
|
6
|
+
*/
|
|
7
|
+
import { Reply } from "./provider.js";
|
|
8
|
+
/** One GraphQL request as the sims see it. */
|
|
9
|
+
export interface GraphqlOperation {
|
|
10
|
+
kind: "query" | "mutation";
|
|
11
|
+
/** The first selected top-level field (after an alias). */
|
|
12
|
+
root: string;
|
|
13
|
+
variables: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
/** The operation of a request body; a 400 GRAPHQL_PARSE_FAILED answer when the body is not one. */
|
|
16
|
+
export declare function graphqlOperation(body: unknown): GraphqlOperation | Reply;
|
|
17
|
+
/** A successful answer: `{ data }`. */
|
|
18
|
+
export declare function graphqlData(data: unknown): Reply;
|
|
19
|
+
/** A failed answer: `{ errors: [{ message, extensions: { code } }], data: null }`, HTTP 200 unless said otherwise. */
|
|
20
|
+
export declare function graphqlError(message: string, code: string, status?: number): Reply;
|
|
21
|
+
/** True when a request body is a mutation: only those change state (the write log). */
|
|
22
|
+
export declare function isMutation(body: unknown): boolean;
|
package/dist/graphql.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the GraphQL sims (routes/railway.ts, routes/fly.ts) share: reading an operation's kind and root field from a
|
|
3
|
+
* request body, and answering in the GraphQL envelope. They do not parse GraphQL: a simulated schema is a table of
|
|
4
|
+
* root fields, each resolved from the request's variables, so a plan line must pass its inputs as variables (as the
|
|
5
|
+
* official clients do), not inline.
|
|
6
|
+
*/
|
|
7
|
+
import { Reply } from "./provider.js";
|
|
8
|
+
/** The operation of a request body; a 400 GRAPHQL_PARSE_FAILED answer when the body is not one. */
|
|
9
|
+
export function graphqlOperation(body) {
|
|
10
|
+
if (typeof body !== "object" || body === null)
|
|
11
|
+
return graphqlError("request body must be a JSON object { query, variables }", "GRAPHQL_PARSE_FAILED", 400);
|
|
12
|
+
const { query, variables } = body;
|
|
13
|
+
if (typeof query !== "string")
|
|
14
|
+
return graphqlError("`query` is required", "GRAPHQL_PARSE_FAILED", 400);
|
|
15
|
+
const text = query.replace(/#[^\n]*/g, "").trim();
|
|
16
|
+
const kind = /^mutation\b/.test(text) ? "mutation" : "query";
|
|
17
|
+
const m = /^\s*([_A-Za-z]\w*)(\s*:\s*([_A-Za-z]\w*))?/.exec(text.slice(text.indexOf("{") + 1));
|
|
18
|
+
if (!m)
|
|
19
|
+
return graphqlError("no field selected", "GRAPHQL_PARSE_FAILED", 400);
|
|
20
|
+
const vars = typeof variables === "object" && variables !== null && !Array.isArray(variables) ? variables : {};
|
|
21
|
+
return { kind, root: m[3] ?? m[1], variables: vars };
|
|
22
|
+
}
|
|
23
|
+
/** A successful answer: `{ data }`. */
|
|
24
|
+
export function graphqlData(data) {
|
|
25
|
+
return new Reply(200, { data });
|
|
26
|
+
}
|
|
27
|
+
/** A failed answer: `{ errors: [{ message, extensions: { code } }], data: null }`, HTTP 200 unless said otherwise. */
|
|
28
|
+
export function graphqlError(message, code, status = 200) {
|
|
29
|
+
return new Reply(status, { errors: [{ message, extensions: { code } }], data: null });
|
|
30
|
+
}
|
|
31
|
+
/** True when a request body is a mutation: only those change state (the write log). */
|
|
32
|
+
export function isMutation(body) {
|
|
33
|
+
const op = graphqlOperation(body);
|
|
34
|
+
return !(op instanceof Reply) && op.kind === "mutation";
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=graphql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql.js","sourceRoot":"","sources":["../src/graphql.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAUtC,mGAAmG;AACnG,MAAM,UAAU,gBAAgB,CAAC,IAAa;IAC5C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,YAAY,CAAC,yDAAyD,EAAE,sBAAsB,EAAE,GAAG,CAAC,CAAC;IAC3J,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,IAA+B,CAAC;IAC7D,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC,qBAAqB,EAAE,sBAAsB,EAAE,GAAG,CAAC,CAAC;IACvG,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAClD,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC;IAC7D,MAAM,CAAC,GAAG,4CAA4C,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC/F,IAAI,CAAC,CAAC;QAAE,OAAO,YAAY,CAAC,mBAAmB,EAAE,sBAAsB,EAAE,GAAG,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAG,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAE,SAAqC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5I,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AACxD,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,OAAO,IAAI,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,IAAY,EAAE,MAAM,GAAG,GAAG;IACtE,OAAO,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AACxF,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,MAAM,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAClC,OAAO,CAAC,CAAC,EAAE,YAAY,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,UAAU,CAAC;AAC1D,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { SimState, type SimSeed } from "./state.js";
|
|
2
|
+
export * from "./chaos.js";
|
|
3
|
+
export * from "./provider.js";
|
|
4
|
+
export * from "./state.js";
|
|
5
|
+
export * from "./routes/vercel.js";
|
|
6
|
+
export * from "./routes/neon.js";
|
|
7
|
+
export * from "./routes/clerk.js";
|
|
8
|
+
export * from "./routes/netlify.js";
|
|
9
|
+
export * from "./routes/planetscale.js";
|
|
10
|
+
export * from "./routes/launchdarkly.js";
|
|
11
|
+
export * from "./routes/rest.js";
|
|
12
|
+
export * from "./routes/supabase.js";
|
|
13
|
+
export * from "./routes/aws.js";
|
|
14
|
+
export * from "./routes/railway.js";
|
|
15
|
+
export * from "./routes/fly.js";
|
|
16
|
+
export * from "./routes/cloudflare.js";
|
|
17
|
+
export * from "./routes/algolia.js";
|
|
18
|
+
export * from "./routes/xata.js";
|
|
19
|
+
export * from "./routes/digitalocean.js";
|
|
20
|
+
export { createSimServer } from "./server.js";
|
|
21
|
+
/**
|
|
22
|
+
* A running sim: its base URL (`simEnv` turns it into adapter environment), its live state for seeding and
|
|
23
|
+
* assertions, and `close`.
|
|
24
|
+
*/
|
|
25
|
+
export interface SimHandle {
|
|
26
|
+
url: string;
|
|
27
|
+
port: number;
|
|
28
|
+
state: SimState;
|
|
29
|
+
close(): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
/** Start the fake cloud on 127.0.0.1. Port 0 picks a free one, which is what tests want. */
|
|
32
|
+
export declare function startSim(opts?: {
|
|
33
|
+
port?: number;
|
|
34
|
+
seed?: Partial<SimSeed>;
|
|
35
|
+
}): Promise<SimHandle>;
|
|
36
|
+
/** A placeholder credential per provider (`VERCEL_TOKEN: "tok_vercel"`, …). The sim accepts any non-empty token. */
|
|
37
|
+
export declare const SIM_TOKENS: Readonly<Record<string, string>>;
|
|
38
|
+
/**
|
|
39
|
+
* The env that points every provider's adapter at a sim (or at a proxy in front of one): each `*_API_URL`
|
|
40
|
+
* and, unless `tokens: false`, the placeholder credentials of SIM_TOKENS.
|
|
41
|
+
*/
|
|
42
|
+
export declare function simEnv(sim: {
|
|
43
|
+
url: string;
|
|
44
|
+
} | string, opts?: {
|
|
45
|
+
tokens?: boolean;
|
|
46
|
+
}): Record<string, string>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createSimServer } from "./server.js";
|
|
2
|
+
import { providerEntries, SimState } from "./state.js";
|
|
3
|
+
export * from "./chaos.js";
|
|
4
|
+
export * from "./provider.js";
|
|
5
|
+
export * from "./state.js";
|
|
6
|
+
export * from "./routes/vercel.js";
|
|
7
|
+
export * from "./routes/neon.js";
|
|
8
|
+
export * from "./routes/clerk.js";
|
|
9
|
+
export * from "./routes/netlify.js";
|
|
10
|
+
export * from "./routes/planetscale.js";
|
|
11
|
+
export * from "./routes/launchdarkly.js";
|
|
12
|
+
export * from "./routes/rest.js";
|
|
13
|
+
export * from "./routes/supabase.js";
|
|
14
|
+
export * from "./routes/aws.js";
|
|
15
|
+
export * from "./routes/railway.js";
|
|
16
|
+
export * from "./routes/fly.js";
|
|
17
|
+
export * from "./routes/cloudflare.js";
|
|
18
|
+
export * from "./routes/algolia.js";
|
|
19
|
+
export * from "./routes/xata.js";
|
|
20
|
+
export * from "./routes/digitalocean.js";
|
|
21
|
+
export { createSimServer } from "./server.js";
|
|
22
|
+
/** Start the fake cloud on 127.0.0.1. Port 0 picks a free one, which is what tests want. */
|
|
23
|
+
export async function startSim(opts = {}) {
|
|
24
|
+
const state = new SimState(opts.seed);
|
|
25
|
+
const server = createSimServer(state);
|
|
26
|
+
await new Promise((resolve, reject) => {
|
|
27
|
+
server.once("error", reject);
|
|
28
|
+
server.listen(opts.port ?? 0, "127.0.0.1", () => resolve());
|
|
29
|
+
});
|
|
30
|
+
const port = server.address().port;
|
|
31
|
+
return {
|
|
32
|
+
url: `http://127.0.0.1:${port}`,
|
|
33
|
+
port,
|
|
34
|
+
state,
|
|
35
|
+
close: () => new Promise((resolve, reject) => {
|
|
36
|
+
server.closeAllConnections();
|
|
37
|
+
server.close((e) => (e ? reject(e) : resolve()));
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** A placeholder credential per provider (`VERCEL_TOKEN: "tok_vercel"`, …). The sim accepts any non-empty token. */
|
|
42
|
+
export const SIM_TOKENS = Object.fromEntries(providerEntries().flatMap(([, p]) => [[p.env.token, p.env.testToken], ...Object.entries(p.env.more ?? {})]));
|
|
43
|
+
/**
|
|
44
|
+
* The env that points every provider's adapter at a sim (or at a proxy in front of one): each `*_API_URL`
|
|
45
|
+
* and, unless `tokens: false`, the placeholder credentials of SIM_TOKENS.
|
|
46
|
+
*/
|
|
47
|
+
export function simEnv(sim, opts = {}) {
|
|
48
|
+
const base = typeof sim === "string" ? sim : sim.url;
|
|
49
|
+
const env = opts.tokens === false ? {} : { ...SIM_TOKENS };
|
|
50
|
+
for (const [name, p] of providerEntries())
|
|
51
|
+
env[p.env.url] = `${base}/${name}`;
|
|
52
|
+
return env;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|