@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.
Files changed (78) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +9 -2
  3. package/dist/auth.d.ts +60 -0
  4. package/dist/auth.js +195 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/bin.d.ts +2 -0
  7. package/dist/bin.js +16 -0
  8. package/dist/bin.js.map +1 -0
  9. package/dist/chaos.d.ts +74 -0
  10. package/dist/chaos.js +80 -0
  11. package/dist/chaos.js.map +1 -0
  12. package/dist/cli.d.ts +29 -0
  13. package/dist/cli.js +99 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/demo.d.ts +39 -0
  16. package/dist/demo.js +168 -0
  17. package/dist/demo.js.map +1 -0
  18. package/dist/graphql.d.ts +22 -0
  19. package/dist/graphql.js +36 -0
  20. package/dist/graphql.js.map +1 -0
  21. package/dist/index.d.ts +46 -0
  22. package/dist/index.js +54 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/provider.d.ts +138 -0
  25. package/dist/provider.js +68 -0
  26. package/dist/provider.js.map +1 -0
  27. package/dist/routes/algolia.d.ts +37 -0
  28. package/dist/routes/algolia.js +83 -0
  29. package/dist/routes/algolia.js.map +1 -0
  30. package/dist/routes/aws.d.ts +43 -0
  31. package/dist/routes/aws.js +211 -0
  32. package/dist/routes/aws.js.map +1 -0
  33. package/dist/routes/clerk.d.ts +33 -0
  34. package/dist/routes/clerk.js +85 -0
  35. package/dist/routes/clerk.js.map +1 -0
  36. package/dist/routes/cloudflare.d.ts +80 -0
  37. package/dist/routes/cloudflare.js +176 -0
  38. package/dist/routes/cloudflare.js.map +1 -0
  39. package/dist/routes/digitalocean.d.ts +58 -0
  40. package/dist/routes/digitalocean.js +158 -0
  41. package/dist/routes/digitalocean.js.map +1 -0
  42. package/dist/routes/fly.d.ts +22 -0
  43. package/dist/routes/fly.js +113 -0
  44. package/dist/routes/fly.js.map +1 -0
  45. package/dist/routes/launchdarkly.d.ts +98 -0
  46. package/dist/routes/launchdarkly.js +213 -0
  47. package/dist/routes/launchdarkly.js.map +1 -0
  48. package/dist/routes/neon.d.ts +73 -0
  49. package/dist/routes/neon.js +184 -0
  50. package/dist/routes/neon.js.map +1 -0
  51. package/dist/routes/netlify.d.ts +126 -0
  52. package/dist/routes/netlify.js +347 -0
  53. package/dist/routes/netlify.js.map +1 -0
  54. package/dist/routes/planetscale.d.ts +95 -0
  55. package/dist/routes/planetscale.js +234 -0
  56. package/dist/routes/planetscale.js.map +1 -0
  57. package/dist/routes/railway.d.ts +24 -0
  58. package/dist/routes/railway.js +112 -0
  59. package/dist/routes/railway.js.map +1 -0
  60. package/dist/routes/rest.d.ts +138 -0
  61. package/dist/routes/rest.js +462 -0
  62. package/dist/routes/rest.js.map +1 -0
  63. package/dist/routes/supabase.d.ts +103 -0
  64. package/dist/routes/supabase.js +234 -0
  65. package/dist/routes/supabase.js.map +1 -0
  66. package/dist/routes/vercel.d.ts +114 -0
  67. package/dist/routes/vercel.js +370 -0
  68. package/dist/routes/vercel.js.map +1 -0
  69. package/dist/routes/xata.d.ts +57 -0
  70. package/dist/routes/xata.js +115 -0
  71. package/dist/routes/xata.js.map +1 -0
  72. package/dist/server.d.ts +4 -0
  73. package/dist/server.js +189 -0
  74. package/dist/server.js.map +1 -0
  75. package/dist/state.d.ts +102 -0
  76. package/dist/state.js +121 -0
  77. package/dist/state.js.map +1 -0
  78. package/package.json +51 -3
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Simulated Neon: branches, their endpoints, databases and connection strings.
3
+ *
4
+ * Assumptions about the real API that this fake encodes, each marked with how far it is checked. "Verified" means
5
+ * against Neon's published OpenAPI document (https://neon.tech/api_spec/release/v2.json, fetched 2026-10-10); see
6
+ * docs/api-verification.md:
7
+ * N1. Branch deletion is synchronous here: the branch is gone when DELETE returns. Contradicted in part: the
8
+ * spec says "the deletion completes after all operations finish" and DELETE answers with `operations`.
9
+ * Whether the list still shows the branch meanwhile is unverified: needs a live account.
10
+ * N2. Branch creation runs async operations (verified: the create answer carries `operations`): for chaos
11
+ * `neon_op_ms` (default 0) after a create, further creates in the project and writes to the new branch
12
+ * answer 423 Locked, which the spec documents as safe to retry (verified). Which requests are locked, and
13
+ * that the endpoint is usable at once, are unverified: needs a live account.
14
+ * N3. A root branch has one database `neondb` owned by role `neondb_owner` (a new project's defaults); a child
15
+ * copies its parent's. The adapter no longer relies on these names: it reads them from
16
+ * GET /projects/:id/branches/:id/databases (verified), and connection_uri requires both (verified).
17
+ * Branches carry `default: true` on the project's root branch (verified; `primary` is deprecated).
18
+ * N4. Pagination (chaos `page_size`): `{ branches, pagination: { next } }` with `?cursor=`, cursors opaque
19
+ * (verified: CursorPagination). Without `limit` the list is not paged here; the spec says a paged read
20
+ * starts with `limit`, and the adapter follows `next` either way.
21
+ * N5. Creating a branch whose name exists answers 409. Unverified: the spec documents only the generic error.
22
+ */
23
+ import { page, Reply, route, router } from "../provider.js";
24
+ /** Simulated Neon (projects, branches, operations); see the assumptions at the top of this file. */
25
+ export const neonSim = {
26
+ env: { token: "NEON_API_KEY", url: "NEON_API_URL", testToken: "tok_neon" },
27
+ defaultSeed: { projects: { proj_demo: { branches: [{ name: "main" }] } } },
28
+ reset(core, seed) {
29
+ const state = { projects: {} };
30
+ for (const [id, p] of Object.entries(seed?.projects ?? {})) {
31
+ const project = { branches: [], opUntil: 0 };
32
+ state.projects[id] = project;
33
+ for (const b of p.branches) {
34
+ const parent = b.parent ? project.branches.find((x) => x.name === b.parent) : undefined;
35
+ const branch = createBranch(core, project, b.name, parent?.id ?? null, "sim");
36
+ // The first root branch is the project's default, like the one Neon creates with the project.
37
+ if (!branch.parent_id && !project.branches.some((x) => x.default))
38
+ branch.default = true;
39
+ }
40
+ }
41
+ return state;
42
+ },
43
+ /** `branch.<name>`: "delete" | "recreate". */
44
+ drift(core, state, { key, rest, value, only }) {
45
+ if (!rest.startsWith("branch."))
46
+ return false;
47
+ const name = rest.slice("branch.".length);
48
+ if (value !== "delete" && value !== "recreate")
49
+ throw new Error(`drift ${key}: only "delete" and "recreate" are supported`);
50
+ for (const [id, p] of Object.entries(state.projects)) {
51
+ if (only !== undefined && id !== only)
52
+ continue;
53
+ const hit = p.branches.filter((b) => b.name === name);
54
+ p.branches = p.branches.filter((b) => !hit.includes(b));
55
+ if (value === "recreate")
56
+ for (const b of hit)
57
+ createBranch(core, p, b.name, b.parent_id, "sim");
58
+ }
59
+ return true;
60
+ },
61
+ routes(core, state, req) {
62
+ return routes(core, state, req);
63
+ },
64
+ };
65
+ const routes = router([
66
+ route("GET", "/projects/:project/branches", ({ core, state, params, url }) => {
67
+ const p = state.projects[params.project];
68
+ if (!p)
69
+ return notFound("project");
70
+ const pg = page(core, p.branches, url.searchParams.get("cursor"));
71
+ return new Reply(200, { branches: pg.items.map(publicBranch), pagination: pg.next === null ? {} : { next: String(pg.next) } });
72
+ }),
73
+ route("POST", "/projects/:project/branches", ({ core, state, params, body }) => {
74
+ const p = state.projects[params.project];
75
+ if (!p)
76
+ return notFound("project");
77
+ if (Date.now() < p.opUntil)
78
+ return locked();
79
+ const b = (body ?? {});
80
+ const name = b.branch?.name;
81
+ if (!name)
82
+ return new Reply(400, { code: "", message: "branch.name required" });
83
+ if (p.branches.some((x) => x.name === name))
84
+ return new Reply(409, { code: "", message: "branch already exists" });
85
+ const parentId = b.branch?.parent_id ?? null;
86
+ if (parentId && !p.branches.some((x) => x.id === parentId))
87
+ return notFound("parent branch");
88
+ const branch = createBranch(core, p, name, parentId, "api");
89
+ if (core.chaos.neon_op_ms > 0)
90
+ branch.opUntil = p.opUntil = Date.now() + core.chaos.neon_op_ms;
91
+ // CreatedBranch: branch, endpoints, operations, roles, databases and (one database and role) connection_uris.
92
+ return new Reply(201, {
93
+ branch: publicBranch(branch),
94
+ endpoints: [publicEndpoint(branch)],
95
+ operations: [{ id: core.nextId("op-"), branch_id: branch.id, action: "create_branch", status: core.chaos.neon_op_ms > 0 ? "running" : "finished" }],
96
+ roles: [{ branch_id: branch.id, name: branch.database.owner_name }],
97
+ databases: [publicDatabase(branch)],
98
+ connection_uris: [{ connection_uri: connectionUri(branch), connection_parameters: { database: branch.database.name, role: branch.database.owner_name, host: branch.endpoint.host } }],
99
+ });
100
+ }),
101
+ route("GET", "/projects/:project/branches/:branch/endpoints", ({ state, params }) => {
102
+ const b = branchOf(state, params.project, params.branch);
103
+ return b ? new Reply(200, { endpoints: [publicEndpoint(b)] }) : notFound("branch");
104
+ }),
105
+ route("GET", "/projects/:project/branches/:branch/databases", ({ state, params }) => {
106
+ const b = branchOf(state, params.project, params.branch);
107
+ return b ? new Reply(200, { databases: [publicDatabase(b)] }) : notFound("branch");
108
+ }),
109
+ route("GET", "/projects/:project/connection_uri", ({ state, params, url }) => {
110
+ const b = branchOf(state, params.project, url.searchParams.get("branch_id"));
111
+ if (!b)
112
+ return notFound("branch");
113
+ // Both are required query parameters in the spec.
114
+ const database = url.searchParams.get("database_name");
115
+ const role = url.searchParams.get("role_name");
116
+ if (!database || !role)
117
+ return new Reply(400, { code: "", message: "database_name and role_name are required" });
118
+ if (database !== b.database.name)
119
+ return notFound(`database ${database}`);
120
+ if (role !== b.database.owner_name)
121
+ return notFound(`role ${role}`);
122
+ return new Reply(200, { uri: connectionUri(b) });
123
+ }),
124
+ route("DELETE", "/projects/:project/branches/:branch", ({ core, state, params }) => {
125
+ const p = state.projects[params.project];
126
+ const b = branchOf(state, params.project, params.branch);
127
+ if (!p || !b)
128
+ return notFound("branch");
129
+ if (Date.now() < b.opUntil)
130
+ return locked();
131
+ // The real deletion "completes after all operations finish" (assumption N1); the sim removes it at once.
132
+ p.branches = p.branches.filter((x) => x !== b);
133
+ return new Reply(200, { branch: publicBranch(b), operations: [{ id: core.nextId("op-"), branch_id: b.id, action: "delete_timeline", status: "finished" }] });
134
+ }),
135
+ ], () => notFound("route"));
136
+ /** A branch of a project by id; undefined when either does not exist. */
137
+ function branchOf(state, project, branchId) {
138
+ return state.projects[project]?.branches.find((x) => x.id === branchId);
139
+ }
140
+ /**
141
+ * Add a branch to a project as Neon would (ids, timestamps, default flag). Tests use it to seed "someone else's"
142
+ * branch.
143
+ */
144
+ export function createBranch(core, project, name, parentId, createdBy) {
145
+ const id = core.nextId("br-");
146
+ const branch = {
147
+ id,
148
+ name,
149
+ parent_id: parentId,
150
+ default: false,
151
+ opUntil: 0,
152
+ created_at: new Date().toISOString(),
153
+ endpoint: { id: core.nextId("ep-"), host: `${id}.sim.neon.tech` },
154
+ database: { ...(project.branches.find((b) => b.id === parentId)?.database ?? { name: DATABASE, owner_name: ROLE }) },
155
+ createdAt: Date.now(),
156
+ createdBy,
157
+ };
158
+ project.branches.push(branch);
159
+ return branch;
160
+ }
161
+ /** A root branch's database and its owner: a new Neon project's defaults. */
162
+ const DATABASE = "neondb";
163
+ const ROLE = "neondb_owner";
164
+ /** The connection string Neon would hand out for a branch. */
165
+ export function connectionUri(branch) {
166
+ return `postgres://${branch.database.owner_name}:pw_${branch.id}@${branch.endpoint.host}/${branch.database.name}`;
167
+ }
168
+ function publicEndpoint(b) {
169
+ return { id: b.endpoint.id, host: b.endpoint.host, branch_id: b.id, type: "read_write" };
170
+ }
171
+ function publicDatabase(b) {
172
+ return { id: 1, branch_id: b.id, name: b.database.name, owner_name: b.database.owner_name, created_at: b.created_at, updated_at: b.created_at };
173
+ }
174
+ /** Neon's GeneralError: `{ code, message }`. */
175
+ function notFound(what) {
176
+ return new Reply(404, { code: "", message: `${what} not found` });
177
+ }
178
+ function publicBranch(b) {
179
+ return { id: b.id, name: b.name, parent_id: b.parent_id, default: b.default, current_state: "ready", created_at: b.created_at };
180
+ }
181
+ function locked() {
182
+ return new Reply(423, { code: "", message: "project already has running conflicting operations, scheduling of new ones is prohibited" });
183
+ }
184
+ //# sourceMappingURL=neon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neon.js","sourceRoot":"","sources":["../../src/routes/neon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Simulated Netlify: site environment variables (one object per key, one value per deploy context) and deploys.
3
+ *
4
+ * Assumptions about the real API that this fake encodes. "Verified" means against Netlify's OpenAPI document
5
+ * (https://github.com/netlify/open-api/blob/master/swagger.yml, rendered at https://open-api.netlify.com/) or its
6
+ * docs (cited), fetched 2026-10-11; docs/api-verification.md has the per-call table. scenarios/contract.test.ts pins
7
+ * the risky ones against a live account (`pnpm test:live`). NL, not N: N… are Neon's.
8
+ * NL1. Base URL https://api.netlify.com/api/v1, bearer token (OpenAPI `host`, `basePath`; the API guide's
9
+ * `Authorization: Bearer`). Verified.
10
+ * NL2. Site variables live under /accounts/{account_id}/env with `?site_id=`: GET lists them (getEnvVars), POST
11
+ * creates a list of them (createEnvVars, 201), GET/PATCH/DELETE /accounts/{account_id}/env/{key} read one,
12
+ * set one context's value (setEnvVarValue, 201: "updates or creates a new value for an existing
13
+ * environment variable") and delete the variable (204); DELETE …/env/{key}/value/{id} deletes one value
14
+ * (deleteEnvVarValue, 204). Verified (OpenAPI paths, statuses; the docs' API guide). `{account_id}` may be
15
+ * the account slug (verified: the API guide). GET /sites/{site_id} carries `account_id`, `account_slug` and
16
+ * `name` (verified field names; that `name` is the `<name>.netlify.app` subdomain is the docs' example, not a
17
+ * statement: unverified, pinned).
18
+ * NL3. A variable is `{ key, scopes, values: [{ id, value, context, context_parameter }], is_secret, updated_at }`;
19
+ * `context` is all | dev | dev-server | branch-deploy | deploy-preview | production | branch, and `branch`
20
+ * needs the branch name in `context_parameter` (verified: envVar / envVarValue definitions). Omitting
21
+ * `scopes` gives all scopes (verified: "By default, environment variables apply to all scopes", env var
22
+ * overview). Unverified: that PATCH leaves the other contexts' values (and their ids) alone and keeps the id
23
+ * of the value it replaces (the sim keeps it; the adapter re-reads ids from the answer anyway).
24
+ * NL4. Secret values (`is_secret`) are write-only except in the `dev` context (verified: Secrets Controller
25
+ * docs). How a masked value is returned is not documented; the sim omits `value` (unverified; the adapter
26
+ * ignores `value` of a secret either way).
27
+ * NL5. POST of a key that already exists is refused with 400 and writes nothing (unverified: status not
28
+ * documented). A variable with an `all` value and a context value together is refused with 400
29
+ * (unverified). The adapter re-lists after a 400/409 and never writes next to an `all` value.
30
+ * NL6. A variable whose last value is deleted stays, with no values (unverified; the adapter deletes it then, and
31
+ * a 404 on the follow-up read is fine).
32
+ * NL7. GET /sites/{site_id}/deploys lists deploys newest first, `page`/`per_page` (verified: the API guide;
33
+ * order unverified, the adapter sorts by `created_at`). A deploy carries `id`, `state`, `context`,
34
+ * `commit_ref`, `review_id`, `branch`, `created_at` (verified field names); `context` is production |
35
+ * deploy-preview | branch-deploy (unverified: no enum in the spec) and `review_id` is the pull request
36
+ * number (unverified).
37
+ * NL8. Deploy states: new, pending_review, accepted, rejected, enqueued, building, uploading, uploaded,
38
+ * preparing, prepared, processing, processed, ready, error, retrying (verified: the `state` filter's enum).
39
+ * The sim uses enqueued → building → ready, or error.
40
+ * NL9. URLs: `<deploy id>--<site name>.netlify.app` is a deploy's permalink and `deploy-preview-<n>--<site
41
+ * name>.netlify.app` the Deploy Preview of pull request n (verified: deploy overview docs).
42
+ * NL10. A build reads the variable values set when it started ("Environment variable changes require a build
43
+ * and deploy to take effect": verified, env var docs); `updated_at` is when a variable's values last
44
+ * changed (verified field; that a PATCH bumps it is unverified, pinned).
45
+ * NL11. POST /sites/{site_id}/builds starts a build of the production branch, or with `?branch=` a branch deploy
46
+ * of that branch's head (verified: createSiteBuild's `branch` description). Unverified: that it works for
47
+ * a branch whose branch deploys are not enabled in the site's settings. Deploy Previews cannot be rebuilt
48
+ * through the documented API.
49
+ * NL12. A `branch` value applies to "deploy permalinks, Deploy Previews, and branch deploys for the specified
50
+ * branch" (verified: env var overview), so a pull request's Deploy Preview reads its head branch's value.
51
+ *
52
+ * The git integration is modelled by `pushes` in the seed: the first deploy list of a site deploys every push it
53
+ * has not deployed yet (a pull request push as a Deploy Preview, `main` as production, other branches as branch
54
+ * deploys), following chaos `deploy` (ok | never | fail | stale | delay:<s>) and `deploy_ms` as Vercel's sim does.
55
+ */
56
+ import { type CreatedBy, type ProviderSim } from "../provider.js";
57
+ /** One value of a simulated variable. */
58
+ export interface NetlifyEnvValue {
59
+ id: string;
60
+ value: string;
61
+ context: string;
62
+ context_parameter?: string;
63
+ }
64
+ /** One simulated site variable. */
65
+ export interface NetlifyEnvVar {
66
+ key: string;
67
+ is_secret: boolean;
68
+ values: NetlifyEnvValue[];
69
+ updatedAt: number;
70
+ createdBy: CreatedBy;
71
+ }
72
+ /** One simulated deploy. */
73
+ export interface NetlifyDeploy {
74
+ id: string;
75
+ state: string;
76
+ context: "production" | "deploy-preview" | "branch-deploy";
77
+ branch: string;
78
+ commit_ref: string;
79
+ review_id: number | null;
80
+ createdAt: number;
81
+ /** While building: `building` until `readyAt`, then `finalState`. */
82
+ readyAt?: number;
83
+ finalState?: string;
84
+ createdBy: CreatedBy;
85
+ }
86
+ /** A commit the git integration has seen: what Netlify would build on its own. */
87
+ export interface NetlifyPush {
88
+ sha: string;
89
+ branch: string;
90
+ pr?: number;
91
+ }
92
+ /** One simulated site. */
93
+ export interface NetlifySite {
94
+ name: string;
95
+ accountId: string;
96
+ accountSlug: string;
97
+ envs: NetlifyEnvVar[];
98
+ deploys: NetlifyDeploy[];
99
+ pushes: NetlifyPush[];
100
+ /** Pushes already deployed by the git integration (by `<sha> <branch>`). */
101
+ built: string[];
102
+ }
103
+ /** The simulated Netlify's state: sites by id. */
104
+ export interface NetlifyState {
105
+ sites: Record<string, NetlifySite>;
106
+ }
107
+ /** What a scenario or test seeds the simulated Netlify with (`seed: { netlify: … }`). */
108
+ export interface NetlifySeed {
109
+ sites: Record<string, {
110
+ name?: string;
111
+ account?: string;
112
+ /** Variables that exist already (set "by a human", so unmanaged). */
113
+ envs?: Array<{
114
+ key: string;
115
+ is_secret?: boolean;
116
+ values: Array<{
117
+ context: string;
118
+ value: string;
119
+ context_parameter?: string;
120
+ }>;
121
+ }>;
122
+ pushes?: NetlifyPush[];
123
+ }>;
124
+ }
125
+ /** The simulated Netlify API (env vars, deploys), registered in `PROVIDERS` and served under `/netlify`. */
126
+ export declare const netlifySim: ProviderSim<NetlifyState, NetlifySeed>;