@sponson/sim 0.0.0-stage → 0.4.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.
@@ -0,0 +1,370 @@
1
+ /**
2
+ * Simulated Vercel: project env vars and deployments.
3
+ *
4
+ * Assumptions about the real API that this fake encodes, each marked with how far it is checked. "Verified" means
5
+ * against Vercel's published OpenAPI document (https://openapi.vercel.sh/, fetched 2026-10-10); see
6
+ * docs/api-verification.md for the per-call table. A local fake cannot prove the rest; scenarios/contract.test.ts
7
+ * pins the critical ones against live accounts (`pnpm test:live`):
8
+ * V1. Env writes are visible to the next deployment immediately (no propagation delay). Lists are
9
+ * read-your-writes here; the real list may lag a write, so adapters trust write responses instead.
10
+ * Unverified: needs a live account.
11
+ * V2. GET /v7/deployments?projectId=&sha= filters by commit and returns newest first. The `sha` filter is
12
+ * verified (a v7 query parameter; v6 is not in the spec); items carry `readyState` (required) and `state`
13
+ * (optional), and `url` may be null until the upload completes (verified). The order is unverified: the
14
+ * adapter sorts by createdAt anyway.
15
+ * V3. GET /v10/projects/:id/env?decrypt=true returns plaintext with `decrypted: true`; without `decrypt`,
16
+ * `encrypted` values are opaque. `decrypt` (deprecated), `decrypted` and GET /v1/projects/:id/env/:id
17
+ * ("Retrieve the decrypted value") are verified; whether the deprecated parameter still decrypts is
18
+ * unverified, so the adapter falls back to the per-variable GET. `sensitive` values are never returned
19
+ * (the GET /v1 variant without `value`), so those always diff as `update`.
20
+ * V4. POST /v10/projects/:id/env?upsert=true answers 201 with `{ created, failed }` (verified: `upsert`, the
21
+ * status, `created` as one object or a list, `failed[].error` with `code`, `message`, `key`/`envVarKey`).
22
+ * Unverified: that `created` lists every entry written (new or updated, `createdAt === updatedAt` only for
23
+ * new ones), and that an entry whose key+gitBranch exists with an overlapping but different target set
24
+ * fails the whole request with 400 `ENV_CONFLICT`. Entries named in chaos `env_upsert_fail` come back under
25
+ * `failed` and are not written. Without `upsert` a duplicate answers 403 (verified: documented 403 reason).
26
+ * V5. POST /v13/deployments: `project` overrides `name` and a GitHub `gitSource` needs `ref` plus `repoId` (or
27
+ * `org` + `repo`) — both verified, as is `forceNew`. Deployments go QUEUED → BUILDING → READY over chaos
28
+ * `deploy_ms` (default 0: READY at once; the real API also passes through INITIALIZING, which the adapter
29
+ * handles as in progress). Every deployment has its own URL (verified: "the unique URL of the deployment").
30
+ * `deploy: cancel` cancels a branch's in-progress builds when a new one starts (unverified).
31
+ * V6. Pagination (chaos `page_size`): `{ envs, pagination: { count, next, prev } }` (verified: the spec's
32
+ * Pagination schema). The `?until=` page parameter is unverified for the env list, which documents no page
33
+ * parameter; the adapter drops repeated records, so an API that ignores it costs one extra request.
34
+ * V7. GET /v9/projects/:id carries `link`, the connected repository (`type`, `repoId`, …); verified.
35
+ * V8. GET /v7/deployments accepts `target` (`production` | `preview`) and each item's `target` is `production`,
36
+ * a custom environment name such as `staging`, or null for a preview; verified against the spec's
37
+ * `/v7/deployments` parameters and item schema. The git integration builds previews; production builds
38
+ * of a commit exist separately.
39
+ */
40
+ import { page, Reply, route, router } from "../provider.js";
41
+ /** Simulated Vercel (env vars, deployments); see the assumptions at the top of this file. */
42
+ export const vercelSim = {
43
+ env: { token: "VERCEL_TOKEN", url: "VERCEL_API_URL", testToken: "tok_vercel" },
44
+ defaultSeed: { projects: { prj_demo: { envs: [] } } },
45
+ reset(core, seed) {
46
+ const state = { projects: {} };
47
+ const now = Date.now();
48
+ for (const [id, p] of Object.entries(seed?.projects ?? {})) {
49
+ const project = { envs: [], deployments: [], link: { type: "github", repoId: 100000 + Object.keys(state.projects).length, org: "sim", repo: id, productionBranch: "main" } };
50
+ for (const e of p.envs) {
51
+ project.envs.push({
52
+ id: core.nextId("env_"),
53
+ key: e.key,
54
+ value: e.value,
55
+ target: [e.target],
56
+ type: "encrypted",
57
+ ...(e.gitBranch ? { gitBranch: e.gitBranch } : {}),
58
+ createdAt: now,
59
+ updatedAt: now,
60
+ createdBy: "sim",
61
+ });
62
+ }
63
+ state.projects[id] = project;
64
+ }
65
+ return state;
66
+ },
67
+ /**
68
+ * `env.<target>.<NAME>` every record of NAME in target
69
+ * `env.<target>@<branch>.<NAME>` only the record for that git branch (`@*`: the project-wide one)
70
+ * Value: a new value, "delete", or "recreate" (delete and re-create with the same name and a new id).
71
+ */
72
+ drift(core, state, { key, rest, value, only }) {
73
+ if (!rest.startsWith("env."))
74
+ return false;
75
+ const spec = rest.slice("env.".length);
76
+ // Env names have no dots; git branches may.
77
+ const dot = spec.lastIndexOf(".");
78
+ if (dot < 0)
79
+ throw new Error(`bad drift key: ${key}`);
80
+ const [target, branch] = splitOnce(spec.slice(0, dot), "@");
81
+ const name = spec.slice(dot + 1);
82
+ for (const [id, p] of Object.entries(state.projects)) {
83
+ if (only !== undefined && id !== only)
84
+ continue;
85
+ const hit = p.envs.filter((e) => e.key === name && e.target.includes(target) && (branch === undefined || (e.gitBranch ?? "*") === branch));
86
+ if (value === "delete")
87
+ p.envs = p.envs.filter((e) => !hit.includes(e));
88
+ else if (value === "recreate") {
89
+ p.envs = p.envs.filter((e) => !hit.includes(e));
90
+ for (const e of hit)
91
+ p.envs.push({ ...e, id: core.nextId("env_"), createdAt: Date.now(), updatedAt: Date.now(), createdBy: "sim" });
92
+ }
93
+ else
94
+ for (const e of hit)
95
+ Object.assign(e, { value, updatedAt: Date.now() });
96
+ }
97
+ return true;
98
+ },
99
+ routes(core, state, req) {
100
+ return routes(core, state, req);
101
+ },
102
+ };
103
+ const routes = router([
104
+ route("GET", "/v9/projects/:project", ({ state, params }) => {
105
+ const p = state.projects[params.project];
106
+ if (!p)
107
+ return notFound("project");
108
+ return new Reply(200, { id: params.project, name: params.project, ...(p.link ? { link: p.link } : {}) });
109
+ }),
110
+ route("GET", "/v10/projects/:project/env", ({ core, state, params, url }) => {
111
+ const p = state.projects[params.project];
112
+ if (!p)
113
+ return notFound("project");
114
+ const decrypt = url.searchParams.get("decrypt") === "true";
115
+ const pg = page(core, p.envs, url.searchParams.get("until"));
116
+ return new Reply(200, { envs: pg.items.map((e) => listedEnv(e, decrypt)), pagination: { count: pg.items.length, next: pg.next, prev: null } });
117
+ }),
118
+ route("POST", "/v10/projects/:project/env", ({ core, state, params, url, body }) => {
119
+ const p = state.projects[params.project];
120
+ if (!p)
121
+ return notFound("project");
122
+ return upsertEnvs(core, p, Array.isArray(body) ? body : [body], url.searchParams.get("upsert") === "true");
123
+ }),
124
+ route("GET", "/v1/projects/:project/env/:id", ({ state, params }) => {
125
+ const env = state.projects[params.project]?.envs.find((e) => e.id === params.id);
126
+ return env ? new Reply(200, listedEnv(env, true)) : notFound("env");
127
+ }),
128
+ route("PATCH", "/v9/projects/:project/env/:id", ({ state, params, body }) => {
129
+ const p = state.projects[params.project];
130
+ if (!p)
131
+ return notFound("project");
132
+ const env = p.envs.find((e) => e.id === params.id);
133
+ if (!env)
134
+ return notFound("env");
135
+ const b = (body ?? {});
136
+ if (typeof b.value === "string")
137
+ env.value = b.value;
138
+ env.updatedAt = Date.now();
139
+ return new Reply(200, publicEnv(env));
140
+ }),
141
+ route("DELETE", "/v9/projects/:project/env/:id", ({ state, params }) => {
142
+ const p = state.projects[params.project];
143
+ if (!p)
144
+ return notFound("project");
145
+ const env = p.envs.find((e) => e.id === params.id);
146
+ if (!env)
147
+ return notFound("env");
148
+ p.envs = p.envs.filter((e) => e !== env);
149
+ return new Reply(200, publicEnv(env));
150
+ }),
151
+ route("GET", "/v7/deployments", ({ core, state, url }) => listDeployments(core, state, url)),
152
+ route("POST", "/v13/deployments", ({ core, state, body }) => postDeployment(core, state, body)),
153
+ route("GET", "/v13/deployments/:id", ({ state, params }) => {
154
+ for (const p of Object.values(state.projects)) {
155
+ refreshDeployments(p.deployments);
156
+ const d = p.deployments.find((x) => x.uid === params.id);
157
+ if (d)
158
+ return new Reply(200, deploymentDetail(d));
159
+ }
160
+ return notFound("deployment");
161
+ }),
162
+ ], () => notFound("route"));
163
+ /**
164
+ * `GET /v7/deployments?projectId&sha&target&limit`. `target`: production → production deployments; preview →
165
+ * deployments whose target is null (previews). Asking for a sha the project has not deployed yet triggers the
166
+ * git integration's automatic deployment, as a push would have.
167
+ */
168
+ function listDeployments(core, state, url) {
169
+ const projectId = url.searchParams.get("projectId") ?? "";
170
+ const sha = url.searchParams.get("sha") ?? "";
171
+ const target = url.searchParams.get("target");
172
+ const inTarget = (d) => target === null || (target === "production" ? d.target === "production" : target === "preview" ? d.target === null : false);
173
+ const p = state.projects[projectId];
174
+ if (!p)
175
+ return notFound("project");
176
+ if (sha && !p.deployments.some((d) => d.meta.githubCommitSha === sha && inTarget(d)))
177
+ autoDeploy(core, projectId, p, sha, target === "production" ? "production" : undefined);
178
+ refreshDeployments(p.deployments);
179
+ const list = p.deployments.filter((d) => (!sha || d.meta.githubCommitSha === sha) && inTarget(d)).sort((a, b) => b.createdAt - a.createdAt);
180
+ const limit = Number(url.searchParams.get("limit") ?? 20);
181
+ const items = list.slice(0, limit);
182
+ return new Reply(200, { deployments: items.map((d) => publicDeployment(projectId, d)), pagination: { count: items.length, next: list.length > limit ? (items.at(-1)?.createdAt ?? null) : null, prev: null } });
183
+ }
184
+ function isGitSource(g) {
185
+ if (!g || g.type !== "github" || typeof g.ref !== "string")
186
+ return false;
187
+ return g.repoId !== undefined || (typeof g.org === "string" && typeof g.repo === "string");
188
+ }
189
+ /** `POST /v13/deployments`: a (re)deployment from a git source of the project's connected repository. */
190
+ function postDeployment(core, state, body) {
191
+ const b = (body ?? {});
192
+ if (typeof b.name !== "string" || b.name === "")
193
+ return badRequest("missing_name", "`name` is required");
194
+ // `project`, when given, overrides `name`.
195
+ const projectId = b.project ?? b.name;
196
+ const p = state.projects[projectId];
197
+ if (!p)
198
+ return notFound("project");
199
+ const g = b.gitSource;
200
+ if (!isGitSource(g))
201
+ return badRequest("bad_request", "Invalid request: `gitSource` needs `type`, `ref` and `repoId` (or `org` and `repo`)");
202
+ if (!p.link || (g.repoId !== undefined && String(g.repoId) !== String(p.link.repoId)))
203
+ return badRequest("incorrect_git_source_info", "The repository is not connected to this project");
204
+ const d = createDeployment(core, projectId, p, g.sha ?? "unknown", core.chaos.deploy === "fail" ? "ERROR" : "READY", Date.now(), {
205
+ ref: g.ref,
206
+ buildMs: core.chaos.deploy_ms,
207
+ ...(b.target === "production" ? { target: "production" } : {}),
208
+ });
209
+ return new Reply(200, deploymentDetail(d));
210
+ }
211
+ function isEnvInput(v) {
212
+ if (typeof v !== "object" || v === null)
213
+ return false;
214
+ const it = v;
215
+ return typeof it.key === "string" && typeof it.value === "string" && Array.isArray(it.target) && (it.gitBranch === undefined || typeof it.gitBranch === "string");
216
+ }
217
+ function upsertEnvs(core, p, body, upsert) {
218
+ const sameScope = (e, it) => e.key === it.key && (e.gitBranch ?? null) === (it.gitBranch ?? null);
219
+ // Validate the whole batch first: a rejected request writes nothing.
220
+ if (!body.every(isEnvInput))
221
+ return badRequest("bad_request", "bad env");
222
+ const items = body;
223
+ for (const it of items) {
224
+ const overlapping = p.envs.find((e) => sameScope(e, it) && !sameSet(e.target, it.target) && e.target.some((t) => it.target.includes(t)));
225
+ if (overlapping) {
226
+ return new Reply(400, { error: { code: "ENV_CONFLICT", key: it.key, message: `A variable with the key ${it.key} already exists for the target ${overlapping.target.join(",")}` } });
227
+ }
228
+ }
229
+ const created = [];
230
+ const failed = [];
231
+ for (const it of items) {
232
+ if (core.chaos.env_upsert_fail.includes(it.key)) {
233
+ failed.push({ error: { code: "ENV_CONFLICT", key: it.key, message: "rejected by chaos env_upsert_fail" } });
234
+ continue;
235
+ }
236
+ const existing = p.envs.find((e) => sameScope(e, it) && sameSet(e.target, it.target));
237
+ if (existing) {
238
+ // Without `upsert`, the spec documents 403 "cannot be created because it already exists".
239
+ if (!upsert)
240
+ return new Reply(403, { error: { code: "ENV_ALREADY_EXISTS", key: it.key, message: "already exists" } });
241
+ existing.value = it.value;
242
+ existing.updatedAt = Math.max(Date.now(), existing.createdAt + 1);
243
+ created.push(existing);
244
+ continue;
245
+ }
246
+ const now = Date.now();
247
+ const env = {
248
+ id: core.nextId("env_"),
249
+ key: it.key,
250
+ value: it.value,
251
+ target: [...it.target],
252
+ type: it.type === "plain" ? "plain" : "encrypted",
253
+ ...(it.gitBranch ? { gitBranch: it.gitBranch } : {}),
254
+ createdAt: now,
255
+ updatedAt: now,
256
+ createdBy: "api",
257
+ };
258
+ p.envs.push(env);
259
+ created.push(env);
260
+ }
261
+ return new Reply(201, { created: created.map(publicEnv), failed });
262
+ }
263
+ /**
264
+ * Every deployment gets its own URL: the first one of a sha is `<project>-<sha8>.vercel.app`, later ones
265
+ * (redeploys) `-2`, `-3`, … `buildMs` > 0 starts it QUEUED and makes it reach `state` over that time.
266
+ */
267
+ export function createDeployment(core, projectId, project, sha, state, createdAt, opts = {}) {
268
+ const n = project.deployments.filter((d) => d.meta.githubCommitSha === sha).length + 1;
269
+ const buildMs = opts.buildMs ?? 0;
270
+ const d = {
271
+ uid: core.nextId("dpl_"),
272
+ url: `${projectId}-${sha.slice(0, 8)}${n > 1 ? `-${n}` : ""}.vercel.app`,
273
+ state: buildMs > 0 ? "QUEUED" : state,
274
+ createdAt,
275
+ ...(buildMs > 0 ? { buildingAt: createdAt + buildMs / 3, readyAt: createdAt + buildMs, finalState: state } : {}),
276
+ meta: { githubCommitSha: sha, ...(opts.ref ? { githubCommitRef: opts.ref } : {}) },
277
+ target: opts.target ?? null,
278
+ createdBy: "api",
279
+ };
280
+ if (core.chaos.deploy === "cancel" && opts.ref) {
281
+ // Vercel's auto-cancel: a new build on a branch cancels the builds of that branch still in progress.
282
+ refreshDeployments(project.deployments, createdAt);
283
+ for (const old of project.deployments) {
284
+ if (old.meta.githubCommitRef === opts.ref && (old.state === "QUEUED" || old.state === "BUILDING")) {
285
+ old.state = "CANCELED";
286
+ delete old.finalState;
287
+ }
288
+ }
289
+ }
290
+ project.deployments.push(d);
291
+ return d;
292
+ }
293
+ /** Advance in-progress deployments to the state their timings say they are in now. */
294
+ export function refreshDeployments(list, now = Date.now()) {
295
+ for (const d of list) {
296
+ if (d.state !== "QUEUED" && d.state !== "BUILDING")
297
+ continue;
298
+ if (d.readyAt !== undefined && now >= d.readyAt) {
299
+ d.state = d.finalState ?? "READY";
300
+ delete d.finalState;
301
+ }
302
+ else if (d.state === "QUEUED" && (d.buildingAt === undefined || now >= d.buildingAt))
303
+ d.state = "BUILDING";
304
+ }
305
+ }
306
+ /** Stand-in for Vercel's git integration: the first lookup of a sha "triggers" its auto-deploy per the chaos mode. */
307
+ function autoDeploy(core, projectId, p, sha, target) {
308
+ const mode = core.chaos.deploy;
309
+ const now = Date.now();
310
+ // Vercel's git integration builds the production branch for production and every other push as a preview.
311
+ const opts = target ? { target } : {};
312
+ if (mode === "never")
313
+ return;
314
+ if (mode === "fail") {
315
+ createDeployment(core, projectId, p, sha, "ERROR", now, opts);
316
+ return;
317
+ }
318
+ if (mode === "stale") {
319
+ createDeployment(core, projectId, p, sha, "READY", now - 60_000, opts);
320
+ return;
321
+ }
322
+ if (mode === "double") {
323
+ createDeployment(core, projectId, p, sha, "READY", now - 1_000, opts);
324
+ createDeployment(core, projectId, p, sha, "READY", now, opts);
325
+ return;
326
+ }
327
+ const delay = mode.match(/^delay:(\d+(?:\.\d+)?)$/);
328
+ if (delay) {
329
+ const d = createDeployment(core, projectId, p, sha, "BUILDING", now, opts);
330
+ d.readyAt = now + Number(delay[1]) * 1000;
331
+ return;
332
+ }
333
+ createDeployment(core, projectId, p, sha, "READY", now, { ...opts, buildMs: core.chaos.deploy_ms });
334
+ }
335
+ function publicEnv(e) {
336
+ const { createdBy: _c, ...rest } = e;
337
+ return rest;
338
+ }
339
+ /** An item of GET /v7/deployments: `readyState` is the field the spec requires; `state` is optional there. */
340
+ function publicDeployment(projectId, d) {
341
+ return { uid: d.uid, name: projectId, projectId, url: d.url, readyState: d.state, state: d.state, created: d.createdAt, createdAt: d.createdAt, target: d.target, meta: d.meta };
342
+ }
343
+ function deploymentDetail(d) {
344
+ return { id: d.uid, url: d.url, readyState: d.state, createdAt: d.createdAt, target: d.target };
345
+ }
346
+ /**
347
+ * An env var as GET /v10/projects/:id/env (with `decrypt`) or GET /v1/projects/:id/env/:id (always decrypting)
348
+ * shows it: an `encrypted` value is opaque unless decrypted, a `sensitive` one is never returned.
349
+ */
350
+ function listedEnv(e, decrypt) {
351
+ const { createdBy: _c, value, ...rest } = e;
352
+ if (e.type === "plain")
353
+ return { ...rest, value, decrypted: false };
354
+ return { ...rest, value: decrypt ? value : `enc:${Buffer.from(value).toString("base64")}`, decrypted: decrypt };
355
+ }
356
+ /** Vercel's error body: `{ error: { code, message } }`. */
357
+ function badRequest(code, message) {
358
+ return new Reply(400, { error: { code, message } });
359
+ }
360
+ function notFound(what) {
361
+ return new Reply(404, { error: { code: "not_found", message: `${what} not found` } });
362
+ }
363
+ function sameSet(a, b) {
364
+ return a.length === b.length && a.every((x) => b.includes(x));
365
+ }
366
+ function splitOnce(s, sep) {
367
+ const i = s.indexOf(sep);
368
+ return i < 0 ? [s, undefined] : [s.slice(0, i), s.slice(i + 1)];
369
+ }
370
+ //# sourceMappingURL=vercel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../src/routes/vercel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type Server } from "node:http";
2
+ import { type SimState } from "./state.js";
3
+ /** The sim's HTTP server over `state`, not yet listening. Most callers want `startSim`. */
4
+ export declare function createSimServer(state: SimState): Server;
package/dist/server.js ADDED
@@ -0,0 +1,130 @@
1
+ /**
2
+ * HTTP front of the fake cloud. The core here does what is common to every provider: control endpoints (`/_…`),
3
+ * bearer-token check, latency, chaos (hang / drop / fail) and the write log. Everything under `/<provider>/` is
4
+ * answered by that provider's `routes/<provider>.ts`; each of those files lists the assumptions about the real
5
+ * API it encodes. The one cross-provider assumption:
6
+ * S1. 429s carry `Retry-After` in seconds (chaos `retry_after`).
7
+ */
8
+ import { createHash } from "node:crypto";
9
+ import { createServer } from "node:http";
10
+ import { setTimeout as sleep } from "node:timers/promises";
11
+ import { Reply, route, router } from "./provider.js";
12
+ import { providerEntries } from "./state.js";
13
+ /** `/<provider>` → its routes, one entry per registered provider. */
14
+ const ROUTES = Object.fromEntries(providerEntries().map(([name, p]) => [`/${name}`, (state, req) => p.routes(state, state.provider(name), req)]));
15
+ const WRITE_METHODS = new Set(["POST", "PATCH", "DELETE", "PUT"]);
16
+ /** The sim's HTTP server over `state`, not yet listening. Most callers want `startSim`. */
17
+ export function createSimServer(state) {
18
+ return createServer((req, res) => {
19
+ handle(state, req, res).catch((e) => {
20
+ send(res, 500, { error: e instanceof Error ? e.message : String(e) });
21
+ });
22
+ });
23
+ }
24
+ async function handle(state, req, res) {
25
+ const method = (req.method ?? "GET").toUpperCase();
26
+ const url = new URL(req.url ?? "/", "http://sim.local");
27
+ const raw = await readBody(req);
28
+ const parsed = parseJson(raw);
29
+ if (!parsed)
30
+ return send(res, 400, { error: "invalid json" });
31
+ const { body } = parsed;
32
+ if (url.pathname.startsWith("/_")) {
33
+ const r = control(state, state, { method, url, path: url.pathname, body });
34
+ return send(res, r.status, r.body);
35
+ }
36
+ // Any non-empty bearer token is accepted; missing-token bugs must surface as 401.
37
+ const auth = req.headers.authorization ?? "";
38
+ if (!/^Bearer\s+\S+$/.test(auth))
39
+ return send(res, 401, { error: "unauthorized" });
40
+ await serveProvider(state, req, res, { method, url, raw, body });
41
+ }
42
+ /** An authenticated provider request: latency, chaos, the write log, then the provider's routes. */
43
+ async function serveProvider(state, req, res, { method, url, raw, body }) {
44
+ if (state.chaos.latency_ms > 0)
45
+ await sleep(state.chaos.latency_ms);
46
+ const isWrite = WRITE_METHODS.has(method);
47
+ const action = state.chaosFor(method, url.pathname);
48
+ // Held: performed (and logged) only once released, like a request stuck in the provider's queue.
49
+ if (action?.kind === "hold")
50
+ await state.hold();
51
+ const entry = { at: new Date().toISOString(), method, path: url.pathname, bodyHash: sha256(raw) };
52
+ if (action?.kind === "hang" || action?.kind === "fail") {
53
+ // Never performed. A hang is accepted and never answered (sim.close() tears the socket down).
54
+ if (isWrite)
55
+ state.writes.push({ ...entry, failed: true });
56
+ if (action.kind === "fail")
57
+ send(res, action.status, { error: "chaos" }, action.headers);
58
+ return;
59
+ }
60
+ const logged = { ...entry };
61
+ if (isWrite)
62
+ state.writes.push(logged);
63
+ const r = dispatch(state, method, url, body);
64
+ // A refused write (409, 423, 404, …) did not change anything.
65
+ if (r.status >= 400)
66
+ logged.failed = true;
67
+ if (action?.kind === "drop") {
68
+ // The request was performed; the client never hears about it.
69
+ req.socket.destroy();
70
+ return;
71
+ }
72
+ send(res, r.status, r.body, r.headers);
73
+ }
74
+ /** `/<provider>/…` → that provider's routes, with the prefix stripped from the path. */
75
+ function dispatch(state, method, url, body) {
76
+ const prefix = url.pathname.match(/^\/[^/]+(?=\/)/)?.[0];
77
+ const routes = prefix === undefined ? undefined : ROUTES[prefix];
78
+ if (prefix === undefined || !routes)
79
+ return new Reply(404, { error: "not found" });
80
+ return routes(state, { method, url, path: url.pathname.slice(prefix.length), body });
81
+ }
82
+ /** The control endpoints tests and scenarios drive the sim with. */
83
+ const control = router([
84
+ route("POST", "/_chaos", ({ state, body }) => {
85
+ try {
86
+ return new Reply(200, state.applyChaos((body ?? {})));
87
+ }
88
+ catch (e) {
89
+ return new Reply(400, { error: e.message });
90
+ }
91
+ }),
92
+ route("GET", "/_state", ({ state }) => new Reply(200, state.snapshot())),
93
+ route("GET", "/_writes", ({ state, url }) => {
94
+ const since = Number(url.searchParams.get("since") ?? 0);
95
+ return new Reply(200, state.writes.slice(Number.isFinite(since) ? since : 0));
96
+ }),
97
+ route("POST", "/_release", ({ state }) => new Reply(200, { released: state.release() })),
98
+ route("POST", "/_reset", ({ state, body }) => {
99
+ state.reset(body);
100
+ return new Reply(200, { ok: true });
101
+ }),
102
+ ], () => new Reply(404, { error: "not found" }));
103
+ /** A request body: empty is `undefined`; null when it is not JSON. */
104
+ function parseJson(raw) {
105
+ if (raw.length === 0)
106
+ return { body: undefined };
107
+ try {
108
+ return { body: JSON.parse(raw) };
109
+ }
110
+ catch {
111
+ return null;
112
+ }
113
+ }
114
+ function readBody(req) {
115
+ return new Promise((resolve, reject) => {
116
+ const chunks = [];
117
+ req.on("data", (c) => chunks.push(c));
118
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
119
+ req.on("error", reject);
120
+ });
121
+ }
122
+ function send(res, status, body, headers = {}) {
123
+ const text = JSON.stringify(body ?? null);
124
+ res.writeHead(status, { ...headers, "content-type": "application/json", "content-length": Buffer.byteLength(text) });
125
+ res.end(text);
126
+ }
127
+ function sha256(s) {
128
+ return createHash("sha256").update(s).digest("hex");
129
+ }
130
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * In-memory state for the fake cloud: the core (chaos, write log, ids) plus one state object per registered
3
+ * provider, reachable as `state.<provider>` (e.g. `state.vercel.projects`).
4
+ */
5
+ import { type ChaosAction, type ChaosConfig, type ChaosRequest } from "./chaos.js";
6
+ import type { ProviderSim, SimCore } from "./provider.js";
7
+ /**
8
+ * Every simulated provider, by URL prefix (`/<name>/…`), drift prefix (`<name>.…`), seed key and state key.
9
+ * Order matters only for determinism: resets seed providers (and so allocate ids) in this order.
10
+ */
11
+ export declare const PROVIDERS: {
12
+ vercel: ProviderSim<import("./routes/vercel.js").VercelState, import("./routes/vercel.js").VercelSeed>;
13
+ neon: ProviderSim<import("./routes/neon.js").NeonState, import("./routes/neon.js").NeonSeed>;
14
+ clerk: ProviderSim<import("./routes/clerk.js").ClerkState, import("./routes/clerk.js").ClerkSeed>;
15
+ };
16
+ type Providers = typeof PROVIDERS;
17
+ /** A simulated provider's name: its URL prefix, seed key and state key. */
18
+ export type ProviderName = keyof Providers;
19
+ type StateOf<P> = P extends ProviderSim<infer S, infer _D> ? S : never;
20
+ type SeedOf<P> = P extends ProviderSim<infer _S, infer D> ? D : never;
21
+ /** The state object of every provider, by name (`state.vercel`, `state.neon`, ...). */
22
+ export type ProviderStates = {
23
+ [K in ProviderName]: StateOf<Providers[K]>;
24
+ };
25
+ /**
26
+ * Initial state for a sim (or a reset): per-provider seeds and optional chaos. Providers it does not name keep
27
+ * their defaults.
28
+ */
29
+ export type SimSeed = {
30
+ /** Chaos applied right after the seed, e.g. `{ page_size: 2 }`. */
31
+ chaos?: Partial<ChaosConfig>;
32
+ } & {
33
+ [K in ProviderName]?: SeedOf<Providers[K]>;
34
+ };
35
+ /** The demo projects every provider starts with when a seed does not name it. */
36
+ export declare const DEFAULT_SEED: SimSeed;
37
+ /**
38
+ * One write the sim received, for assertions such as "the second apply wrote nothing". Bodies are hashed, never
39
+ * stored.
40
+ */
41
+ export interface WriteLogEntry {
42
+ at: string;
43
+ method: string;
44
+ path: string;
45
+ bodyHash: string;
46
+ /** The write changed nothing: failed by chaos, or refused by the provider (4xx/5xx). */
47
+ failed?: true;
48
+ }
49
+ /** The registry as the core sees it: names and type-erased providers. */
50
+ export declare function providerEntries(): Array<[ProviderName, ProviderSim<unknown, unknown>]>;
51
+ /**
52
+ * The whole fake cloud in memory: chaos, the write log, and each provider's state as `state.<provider>`. Tests
53
+ * seed and inspect it directly. The per-provider fields are declared by this interface and assigned in `reset`.
54
+ */
55
+ export interface SimState extends ProviderStates {
56
+ }
57
+ export declare class SimState implements SimCore {
58
+ chaos: ChaosConfig;
59
+ writes: WriteLogEntry[];
60
+ private seq;
61
+ private held;
62
+ constructor(seed?: Partial<SimSeed>);
63
+ nextId(prefix: string): string;
64
+ /** The state object of one provider. */
65
+ provider(name: ProviderName): unknown;
66
+ /**
67
+ * Clear everything (state, chaos, writes) and apply a seed. A partial seed replaces only the providers it names;
68
+ * the others keep their defaults, so `{ clerk: {...} }` still has the demo Vercel and Neon projects.
69
+ */
70
+ reset(seed?: Partial<SimSeed> | null): void;
71
+ /** Merge a chaos request; drift is applied to state right away and not kept. */
72
+ applyChaos(req: ChaosRequest): ChaosConfig;
73
+ /**
74
+ * Drift: change provider state behind the client's back, like a human in a console. The key is
75
+ * `<provider>.<rest>`, or `<provider>:<project>.<rest>` to limit it to one project; each provider documents
76
+ * the `<rest>` forms it accepts in its `drift`. Values are a new value, "delete", or "recreate" (delete and
77
+ * re-create with the same name and a new id).
78
+ */
79
+ applyDrift(key: string, value: string): void;
80
+ /** Requests currently held by chaos `hold_next`. */
81
+ get heldCount(): number;
82
+ /** Park the calling request until `release()`. */
83
+ hold(): Promise<void>;
84
+ /** Let every held request proceed; returns how many there were. */
85
+ release(): number;
86
+ /** Decide what chaos does to this request, consuming one count when it does something. */
87
+ chaosFor(method: string, path: string): ChaosAction | null;
88
+ snapshot(): unknown;
89
+ }
90
+ export {};