@specific.dev/spectest 0.26.0 → 0.28.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/dist/aws-sigv4.d.ts +42 -0
- package/dist/aws-sigv4.js +166 -0
- package/dist/browser.d.ts +314 -0
- package/dist/browser.js +1320 -0
- package/dist/components/email.d.ts +135 -0
- package/dist/components/email.js +271 -0
- package/dist/components/expo.d.ts +69 -0
- package/dist/components/expo.js +125 -0
- package/dist/components/index.d.ts +8 -0
- package/dist/components/index.js +18 -0
- package/dist/components/k3s.d.ts +172 -0
- package/dist/components/k3s.js +1124 -0
- package/dist/components/postgres.d.ts +93 -0
- package/dist/components/postgres.js +58 -0
- package/dist/components/replayFake.d.ts +169 -0
- package/dist/components/replayFake.js +738 -0
- package/dist/components/s3.d.ts +99 -0
- package/dist/components/s3.js +81 -0
- package/dist/components/supabase.d.ts +197 -0
- package/dist/components/supabase.js +1003 -0
- package/dist/daemon.d.ts +1 -0
- package/dist/daemon.js +4611 -0
- package/dist/ids.d.ts +2 -0
- package/{src/ids.ts → dist/ids.js} +46 -50
- package/dist/index.d.ts +1328 -0
- package/dist/index.js +769 -0
- package/dist/ingress.d.ts +114 -0
- package/dist/ingress.js +210 -0
- package/dist/inspect.d.ts +228 -0
- package/dist/inspect.js +429 -0
- package/dist/locator.d.ts +260 -0
- package/dist/locator.js +293 -0
- package/dist/mobile.d.ts +71 -0
- package/dist/mobile.js +65 -0
- package/dist/record-secrets.d.ts +9 -0
- package/{src/record-secrets.ts → dist/record-secrets.js} +13 -15
- package/dist/recorder.d.ts +527 -0
- package/dist/recorder.js +219 -0
- package/dist/redis.d.ts +54 -0
- package/dist/redis.js +126 -0
- package/dist/replay-bundle.d.ts +38 -0
- package/{src/replay-bundle.ts → dist/replay-bundle.js} +29 -47
- package/dist/resolver.d.ts +1 -0
- package/dist/resolver.js +309 -0
- package/dist/s3.d.ts +89 -0
- package/dist/s3.js +198 -0
- package/dist/sql.d.ts +74 -0
- package/dist/sql.js +151 -0
- package/dist/terminal.d.ts +161 -0
- package/dist/terminal.js +538 -0
- package/package.json +24 -9
- package/src/browser.ts +0 -1819
- package/src/components/email.ts +0 -398
- package/src/components/expo.ts +0 -167
- package/src/components/index.ts +0 -63
- package/src/components/k3s.ts +0 -1312
- package/src/components/postgres.ts +0 -105
- package/src/components/replayFake.ts +0 -848
- package/src/components/s3.ts +0 -132
- package/src/components/supabase.ts +0 -1299
- package/src/daemon.ts +0 -4969
- package/src/index.ts +0 -2350
- package/src/ingress.ts +0 -288
- package/src/inspect.ts +0 -673
- package/src/locator.ts +0 -594
- package/src/mobile.ts +0 -133
- package/src/recorder.ts +0 -817
- package/src/redis.ts +0 -202
- package/src/resolver.ts +0 -351
- package/src/s3.ts +0 -333
- package/src/sql.ts +0 -243
- package/src/terminal.ts +0 -740
- package/src/vendor/rrweb-plugin-console-record.umd.js +0 -521
- package/src/vendor/rrweb-record.min.js +0 -5061
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type S3ClientLike } from "../s3.js";
|
|
2
|
+
export interface S3Options {
|
|
3
|
+
/** HTTP port the S3 API is served on. Default `9090`. */
|
|
4
|
+
port?: number;
|
|
5
|
+
/**
|
|
6
|
+
* Buckets to create on first boot. The backing server's own `initialBuckets`
|
|
7
|
+
* env is unreliable across versions, so the component creates them in `setup`
|
|
8
|
+
* (a plain CreateBucket `PUT`) — captured into the warm snapshot, never
|
|
9
|
+
* re-run on warm starts/forks.
|
|
10
|
+
*/
|
|
11
|
+
buckets?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Default bucket the `helpers.client` targets (so `client.file("k")` resolves
|
|
14
|
+
* without a per-call `{ bucket }`). Defaults to the first entry of `buckets`.
|
|
15
|
+
*/
|
|
16
|
+
bucket?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Hostnames to additionally serve the store at over **HTTPS** (CA-trusted),
|
|
19
|
+
* via the daemon's TLS-terminating reverse proxy. Optional — defaults to none;
|
|
20
|
+
* the store is always reachable at `http://<key>:<port>` regardless. Set this
|
|
21
|
+
* only when the app under test hardcodes a specific prod S3 endpoint it can't
|
|
22
|
+
* be told to override (`https://<bucket>.s3.amazonaws.com`, a provider's
|
|
23
|
+
* storage host): the daemon mints a cert for each host and proxies to the
|
|
24
|
+
* store's plain HTTP. The store ignores SigV4, so the Host rewrite is harmless.
|
|
25
|
+
*/
|
|
26
|
+
hosts?: string[];
|
|
27
|
+
/** Access key id the helper client signs with. The store ignores SigV4, so
|
|
28
|
+
* any non-empty value works; default `"s3"`. */
|
|
29
|
+
accessKeyId?: string;
|
|
30
|
+
/** Secret key the helper client signs with (ignored by the store). Default
|
|
31
|
+
* `"s3"`. */
|
|
32
|
+
secretAccessKey?: string;
|
|
33
|
+
/** Region for the helper client. Default `"us-east-1"`. */
|
|
34
|
+
region?: string;
|
|
35
|
+
/** Extra environment variables forwarded to the container. */
|
|
36
|
+
env?: Record<string, string>;
|
|
37
|
+
}
|
|
38
|
+
/** Helpers an `s3(...)` service exposes on `ctx.svc.<name>`. */
|
|
39
|
+
export interface S3Helpers {
|
|
40
|
+
/** An instrumented {@link S3Client} pointed at this store (path-style) — each
|
|
41
|
+
* object op lands on the test event log and its result comes back
|
|
42
|
+
* provenance-wrapped, so `expect(...)` on a read links to the op. */
|
|
43
|
+
client: S3ClientLike;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A ready-to-use, S3-compatible object store (backed by
|
|
47
|
+
* [`adobe/s3mock`](https://github.com/adobe/S3Mock)), with an instrumented S3
|
|
48
|
+
* client on `ctx.svc.<key>.client`. Drop into `environment.services`:
|
|
49
|
+
*
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { s3 } from "@specific.dev/spectest/components";
|
|
52
|
+
*
|
|
53
|
+
* services: {
|
|
54
|
+
* storage: s3({ buckets: ["uploads"] }),
|
|
55
|
+
* },
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* Tests get an instrumented S3 client at `ctx.svc.<key>.client`:
|
|
59
|
+
*
|
|
60
|
+
* ```ts
|
|
61
|
+
* await ctx.svc.storage.client.write("hello.txt", "hi there");
|
|
62
|
+
* const body = await ctx.svc.storage.client.file("hello.txt").text();
|
|
63
|
+
* expect(body).toBe("hi there"); // nests under the s3 read step
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* The store skips SigV4 validation and is path-style only (what S3-compatible
|
|
67
|
+
* clients use), so it stands in for AWS S3, R2, Spaces, etc. without credential
|
|
68
|
+
* plumbing. If — and only if — the app under test hardcodes a specific prod S3
|
|
69
|
+
* endpoint, pass `hosts` to also serve the store there over a CA-trusted cert:
|
|
70
|
+
*
|
|
71
|
+
* ```ts
|
|
72
|
+
* // app hardcodes https://files.example.com — serve it there too:
|
|
73
|
+
* storage: s3({ hosts: ["files.example.com"] }),
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare function s3(opts?: S3Options): {
|
|
77
|
+
setup?: (({ name }: {
|
|
78
|
+
name: string;
|
|
79
|
+
}) => Promise<void>) | undefined;
|
|
80
|
+
readyCheck: {
|
|
81
|
+
type: "http";
|
|
82
|
+
port: number;
|
|
83
|
+
path: string;
|
|
84
|
+
timeoutSecs: number;
|
|
85
|
+
};
|
|
86
|
+
helpers: ({ name }: {
|
|
87
|
+
name: string;
|
|
88
|
+
}) => S3Helpers;
|
|
89
|
+
tls?: {
|
|
90
|
+
hostname: string;
|
|
91
|
+
port: number;
|
|
92
|
+
}[] | undefined;
|
|
93
|
+
env?: Record<string, string> | undefined;
|
|
94
|
+
image: {
|
|
95
|
+
type: "registry";
|
|
96
|
+
reference: string;
|
|
97
|
+
};
|
|
98
|
+
ports: number[];
|
|
99
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { S3Client } from "../s3.js";
|
|
2
|
+
// Backing image, pinned internally (not user-configurable). adobe/s3mock 5.1.0
|
|
3
|
+
// is the current release; avoid exactly 5.0.0, which crashed at startup on
|
|
4
|
+
// Java 25 (`NoClassDefFoundError: KotlinBuiltIns$2`) — fixed in 5.1.0.
|
|
5
|
+
const S3_IMAGE = "adobe/s3mock:5.1.0";
|
|
6
|
+
/**
|
|
7
|
+
* A ready-to-use, S3-compatible object store (backed by
|
|
8
|
+
* [`adobe/s3mock`](https://github.com/adobe/S3Mock)), with an instrumented S3
|
|
9
|
+
* client on `ctx.svc.<key>.client`. Drop into `environment.services`:
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { s3 } from "@specific.dev/spectest/components";
|
|
13
|
+
*
|
|
14
|
+
* services: {
|
|
15
|
+
* storage: s3({ buckets: ["uploads"] }),
|
|
16
|
+
* },
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Tests get an instrumented S3 client at `ctx.svc.<key>.client`:
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* await ctx.svc.storage.client.write("hello.txt", "hi there");
|
|
23
|
+
* const body = await ctx.svc.storage.client.file("hello.txt").text();
|
|
24
|
+
* expect(body).toBe("hi there"); // nests under the s3 read step
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* The store skips SigV4 validation and is path-style only (what S3-compatible
|
|
28
|
+
* clients use), so it stands in for AWS S3, R2, Spaces, etc. without credential
|
|
29
|
+
* plumbing. If — and only if — the app under test hardcodes a specific prod S3
|
|
30
|
+
* endpoint, pass `hosts` to also serve the store there over a CA-trusted cert:
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* // app hardcodes https://files.example.com — serve it there too:
|
|
34
|
+
* storage: s3({ hosts: ["files.example.com"] }),
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function s3(opts = {}) {
|
|
38
|
+
const port = opts.port ?? 9090;
|
|
39
|
+
const buckets = opts.buckets ?? [];
|
|
40
|
+
const defaultBucket = opts.bucket ?? buckets[0];
|
|
41
|
+
const accessKeyId = opts.accessKeyId ?? "s3";
|
|
42
|
+
const secretAccessKey = opts.secretAccessKey ?? "s3";
|
|
43
|
+
const region = opts.region ?? "us-east-1";
|
|
44
|
+
return {
|
|
45
|
+
image: { type: "registry", reference: S3_IMAGE },
|
|
46
|
+
ports: [port],
|
|
47
|
+
...(opts.env ? { env: opts.env } : {}),
|
|
48
|
+
...(opts.hosts
|
|
49
|
+
? { tls: opts.hosts.map((hostname) => ({ hostname, port })) }
|
|
50
|
+
: {}),
|
|
51
|
+
// GET / returns the ListBuckets XML with 200 once the store is up.
|
|
52
|
+
readyCheck: { type: "http", port, path: "/", timeoutSecs: 60 },
|
|
53
|
+
helpers: ({ name }) => ({
|
|
54
|
+
client: new S3Client({
|
|
55
|
+
endpoint: `http://${name}:${port}`,
|
|
56
|
+
region,
|
|
57
|
+
accessKeyId,
|
|
58
|
+
secretAccessKey,
|
|
59
|
+
...(defaultBucket ? { bucket: defaultBucket } : {}),
|
|
60
|
+
// Path-style (Bun's default) — the store is path-style only.
|
|
61
|
+
}),
|
|
62
|
+
}),
|
|
63
|
+
...(buckets.length > 0
|
|
64
|
+
? {
|
|
65
|
+
setup: async ({ name }) => {
|
|
66
|
+
for (const bucket of buckets) {
|
|
67
|
+
// CreateBucket is a plain `PUT` on the bucket root; the store
|
|
68
|
+
// returns 200 (or 409 if it already exists — idempotent enough to
|
|
69
|
+
// ignore).
|
|
70
|
+
const res = await fetch(`http://${name}:${port}/${bucket}`, {
|
|
71
|
+
method: "PUT",
|
|
72
|
+
});
|
|
73
|
+
if (!res.ok && res.status !== 409) {
|
|
74
|
+
throw new Error(`s3: failed to create bucket ${bucket}: ${res.status}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
: {}),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { type ServiceGroup, type ServicesMap } from "../index.js";
|
|
2
|
+
import { type SqlClient } from "../sql.js";
|
|
3
|
+
import { type EmailHelpers } from "./email.js";
|
|
4
|
+
export interface SupabaseOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Key of the API-gateway service and the prefix for every other piece.
|
|
7
|
+
* Default `"supabase"`. The gateway is reachable at `http://<name>:8000`
|
|
8
|
+
* and its helpers at `ctx.svc.<name>`; the rest are `<name>-db`,
|
|
9
|
+
* `<name>-auth`, `<name>-rest`, `<name>-storage`, `<name>-realtime`, ….
|
|
10
|
+
*/
|
|
11
|
+
name?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Password for the Postgres roles the services authenticate with (and the
|
|
14
|
+
* `postgres` superuser the app / SQL helper connect as). Default
|
|
15
|
+
* `"postgres"`. Purely local to the hermetic VM — not a secret to guard.
|
|
16
|
+
*/
|
|
17
|
+
dbPassword?: string;
|
|
18
|
+
/**
|
|
19
|
+
* HS256 secret that signs and verifies all JWTs (user access tokens plus the
|
|
20
|
+
* derived `anonKey` / `serviceRoleKey` API keys). Must be ≥32 chars. Default
|
|
21
|
+
* is Supabase's well-known demo secret. Changing it re-derives the keys
|
|
22
|
+
* deterministically, so the warm-template cache stays stable.
|
|
23
|
+
*/
|
|
24
|
+
jwtSecret?: string;
|
|
25
|
+
/** Studio dashboard Basic-Auth username (only relevant with `studio`). Default `"supabase"`. */
|
|
26
|
+
dashboardUsername?: string;
|
|
27
|
+
/** Studio dashboard Basic-Auth password (only relevant with `studio`). */
|
|
28
|
+
dashboardPassword?: string;
|
|
29
|
+
/** Include GoTrue auth (`<name>-auth`). Default `true`. */
|
|
30
|
+
auth?: boolean;
|
|
31
|
+
/** Include storage-api + imgproxy (`<name>-storage`, `<name>-imgproxy`). Default `true`. */
|
|
32
|
+
storage?: boolean;
|
|
33
|
+
/** Include Realtime (`<name>-realtime`). Default `true`. */
|
|
34
|
+
realtime?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Include the Studio dashboard (`<name>-studio`) and its postgres-meta
|
|
37
|
+
* backend (`<name>-meta`). Off by default — Studio is a heavy Next.js image
|
|
38
|
+
* and is only useful for interactive `ctx.browser()` exploration, not
|
|
39
|
+
* automated backend assertions. Turning it on implies `meta`.
|
|
40
|
+
*/
|
|
41
|
+
studio?: boolean;
|
|
42
|
+
/** Include postgres-meta (`<name>-meta`). Defaults to whatever `studio` is. */
|
|
43
|
+
meta?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Also serve the gateway over **HTTPS** at `https://<hostname>` via the
|
|
46
|
+
* daemon's CA-trusted TLS reverse proxy (in addition to the plain
|
|
47
|
+
* `http://<name>:8000`). Set this only when the app under test hardcodes a
|
|
48
|
+
* specific Supabase URL it can't be told to override — otherwise leave it and
|
|
49
|
+
* point the app at `sb.url`.
|
|
50
|
+
*/
|
|
51
|
+
hostname?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Capture GoTrue's outgoing mail (magic links, OTPs, password recovery,
|
|
54
|
+
* invites — and signup confirmations with `autoconfirm: false`) with the
|
|
55
|
+
* standard `email()` component. **On by default**: the stack includes the
|
|
56
|
+
* capture server (`<name>-mail`) with GoTrue's SMTP wired at it, and tests
|
|
57
|
+
* read the mailbox through the typed helpers at `ctx.svc.<name>.mail`
|
|
58
|
+
* (`lastEmail` / `emails` / `clear`), each captured message rendering
|
|
59
|
+
* mail-client-style on the dashboard timeline.
|
|
60
|
+
*
|
|
61
|
+
* Signups stay one-step by default (`autoconfirm: true`) so the mailbox is
|
|
62
|
+
* purely additive; pass `{ autoconfirm: false }` to test real
|
|
63
|
+
* email-confirmation flows. Pass `{ service: "<key>" }` to point GoTrue at
|
|
64
|
+
* an `email()` service the environment already declares — keep a single
|
|
65
|
+
* mailbox per environment rather than one per component: if the app under
|
|
66
|
+
* test also sends mail, share one `email()` between it and Supabase
|
|
67
|
+
* instead of standing up a second capture server. `false` disables mail
|
|
68
|
+
* capture entirely.
|
|
69
|
+
*/
|
|
70
|
+
mail?: boolean | SupabaseMailOptions;
|
|
71
|
+
/**
|
|
72
|
+
* Extra API keys the gateway accepts alongside the derived JWT keys — for
|
|
73
|
+
* clients that ship a hardcoded key (e.g. an `sb_publishable_…` /
|
|
74
|
+
* `sb_secret_…` pair baked into an app build). Each `anon` key is admitted
|
|
75
|
+
* with anon privileges and each `serviceRole` key with service-role
|
|
76
|
+
* privileges; Kong swaps the matching *derived JWT* in before proxying, so
|
|
77
|
+
* GoTrue/PostgREST/Realtime still receive a validly-signed token.
|
|
78
|
+
*/
|
|
79
|
+
extraApiKeys?: {
|
|
80
|
+
anon?: string[];
|
|
81
|
+
serviceRole?: string[];
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Auto-apply SQL migrations found in the project before any dependent service
|
|
85
|
+
* starts. `true` (default) applies every `*.sql` under `supabase/migrations/`
|
|
86
|
+
* (sorted, the Supabase convention), then `supabase/seed.sql` if present — a
|
|
87
|
+
* no-op when the directory is absent. Pass a string to point at a different
|
|
88
|
+
* directory (relative to the project root or absolute), or `false` to skip.
|
|
89
|
+
* An *explicitly configured* path that doesn't resolve is an error at env
|
|
90
|
+
* start (a silent no-op there surfaces much later as a confusing
|
|
91
|
+
* `relation … does not exist`).
|
|
92
|
+
* Because `supabase/**` is project content, editing a migration correctly
|
|
93
|
+
* forces a cold rebuild and re-apply (unlike `spectest/tests/**`).
|
|
94
|
+
*/
|
|
95
|
+
migrations?: boolean | string;
|
|
96
|
+
/**
|
|
97
|
+
* Seed SQL applied after migrations. `true` (default) applies
|
|
98
|
+
* `supabase/seed.sql` if it exists; a string overrides the path; `false`
|
|
99
|
+
* skips it.
|
|
100
|
+
*/
|
|
101
|
+
seed?: boolean | string;
|
|
102
|
+
}
|
|
103
|
+
/** Tuning for `SupabaseOptions.mail`. */
|
|
104
|
+
export interface SupabaseMailOptions {
|
|
105
|
+
/**
|
|
106
|
+
* Reuse an `email()` service the environment already declares (its
|
|
107
|
+
* services-map key) instead of adding one to the group — the way to keep a
|
|
108
|
+
* single global mailbox when the app under test sends mail too. Assumes
|
|
109
|
+
* the service's default ports (SMTP 1025, API 8025).
|
|
110
|
+
*/
|
|
111
|
+
service?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Whether GoTrue autoconfirms signups. `true` (default): signups complete
|
|
114
|
+
* in one step and send no confirmation mail — recovery, magic-link and OTP
|
|
115
|
+
* mail is still sent and captured. Set `false` to exercise real
|
|
116
|
+
* email-confirmation flows (signups require the emailed verification).
|
|
117
|
+
*/
|
|
118
|
+
autoconfirm?: boolean;
|
|
119
|
+
/** Sender address GoTrue mails from. Default `admin@example.com`. */
|
|
120
|
+
adminEmail?: string;
|
|
121
|
+
/** Sender display name. Default `Supabase`. */
|
|
122
|
+
senderName?: string;
|
|
123
|
+
}
|
|
124
|
+
/** Helpers the gateway service exposes on `ctx.svc.<name>`. */
|
|
125
|
+
export interface SupabaseHelpers {
|
|
126
|
+
/**
|
|
127
|
+
* An instrumented {@link SqlClient} connected to the database as the
|
|
128
|
+
* `postgres` superuser — every query lands on the test event log and its rows
|
|
129
|
+
* come back provenance-wrapped, so `expect(...)` on a read links to the
|
|
130
|
+
* query. Point of direct DB verification alongside the REST/auth surface.
|
|
131
|
+
*/
|
|
132
|
+
sql: SqlClient;
|
|
133
|
+
/** The gateway base URL the app uses as `SUPABASE_URL` (e.g. `http://supabase:8000`). */
|
|
134
|
+
url: string;
|
|
135
|
+
/** Long-lived `anon` API key (HS256 JWT). Pass as the `apikey` header. */
|
|
136
|
+
anonKey: string;
|
|
137
|
+
/** Long-lived `service_role` API key (HS256 JWT). Bypasses RLS — server-side only. */
|
|
138
|
+
serviceRoleKey: string;
|
|
139
|
+
/**
|
|
140
|
+
* The captured-mail mailbox (`lastEmail` / `emails` / `clear`) — present
|
|
141
|
+
* unless the stack was built with `mail: false`. Wait for delivery with
|
|
142
|
+
* the standard `ctx.poll`:
|
|
143
|
+
*
|
|
144
|
+
* ```ts
|
|
145
|
+
* const mail = await ctx.poll("confirmation email", () =>
|
|
146
|
+
* ctx.svc.supabase.mail!.lastEmail());
|
|
147
|
+
* expect(mail.to).toContain("alice@example.com");
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
mail?: EmailHelpers;
|
|
151
|
+
}
|
|
152
|
+
/** What `supabase()` returns: the service group plus the derived
|
|
153
|
+
* connection surface for wiring the app under test. */
|
|
154
|
+
export interface SupabaseStack {
|
|
155
|
+
/** The Supabase stack as a service group. Mount it at the key matching
|
|
156
|
+
* the `name` option (default `"supabase"`):
|
|
157
|
+
* `services: { supabase: sb.group, ... }`. */
|
|
158
|
+
group: ServiceGroup<ServicesMap, SupabaseHelpers>;
|
|
159
|
+
/** Gateway base URL — `http://<name>:8000`, or `https://<hostname>` when set. */
|
|
160
|
+
url: string;
|
|
161
|
+
/** `anon` API key (HS256 JWT). */
|
|
162
|
+
anonKey: string;
|
|
163
|
+
/** `service_role` API key (HS256 JWT). */
|
|
164
|
+
serviceRoleKey: string;
|
|
165
|
+
/** Direct Postgres URL for the app (`postgres` superuser): `postgresql://postgres:<pw>@<name>-db:5432/postgres`. */
|
|
166
|
+
dbUrl: string;
|
|
167
|
+
/** The gateway service key — use in a dependent service's `dependsOn` to wait
|
|
168
|
+
* for the whole stack (the gateway waits on the services behind it). */
|
|
169
|
+
ready: string;
|
|
170
|
+
/**
|
|
171
|
+
* Where the stack's SMTP capture server listens (absent with
|
|
172
|
+
* `mail: false`) — point the app under test's own mail transport here to
|
|
173
|
+
* share the one mailbox: `env: { SMTP_HOST: sb.smtp.host, SMTP_PORT: String(sb.smtp.port) }`.
|
|
174
|
+
*/
|
|
175
|
+
smtp?: {
|
|
176
|
+
host: string;
|
|
177
|
+
port: number;
|
|
178
|
+
};
|
|
179
|
+
/** Ready-to-spread env for the app under test: `SUPABASE_URL`,
|
|
180
|
+
* `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`, `DATABASE_URL`. */
|
|
181
|
+
appEnv: Record<string, string>;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* A ready-to-use self-hosted Supabase stack. Mount `.group` at the key
|
|
185
|
+
* matching the `name` option (default `"supabase"`) and wire the app with
|
|
186
|
+
* `.appEnv` / `.ready`:
|
|
187
|
+
*
|
|
188
|
+
* ```ts
|
|
189
|
+
* const sb = supabase({ dbPassword: "postgres" });
|
|
190
|
+
* services: { supabase: sb.group, app: { image, env: sb.appEnv, dependsOn: [sb.ready] } }
|
|
191
|
+
* ```
|
|
192
|
+
*
|
|
193
|
+
* See the file header for the full picture. Tests get an instrumented DB client
|
|
194
|
+
* at `ctx.svc.<name>.sql` plus `sb.url` / `sb.anonKey` / `sb.serviceRoleKey`
|
|
195
|
+
* for hitting the REST/auth/storage APIs through the gateway.
|
|
196
|
+
*/
|
|
197
|
+
export declare function supabase(opts?: SupabaseOptions): SupabaseStack;
|