@stardeck-customer-apps/testing 0.1.1

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/SKILL.md ADDED
@@ -0,0 +1,118 @@
1
+ # @stardeck-customer-apps/testing
2
+
3
+ Vitest harness for Stardeck apps. Tests run against an **in-process Postgres
4
+ (PGlite)** and a **simulated control plane**, so the real Stardeck SDKs
5
+ (`data-store-sdk`, `email-sdk`, `project-auth`) execute their production code
6
+ paths with no network, no mocks to write, and full determinism.
7
+
8
+ ## Setup (once per project)
9
+
10
+ 1. `vitest.config.ts` in the project root:
11
+
12
+ ```ts
13
+ import { defineStardeckTestConfig } from "@stardeck-customer-apps/testing/config";
14
+
15
+ export default defineStardeckTestConfig();
16
+ ```
17
+
18
+ 2. `package.json` scripts: `"test": "vitest run"`.
19
+
20
+ 3. If the app uses a data store, regenerate the schema snapshot whenever the
21
+ schema changes (also emits Kysely types):
22
+
23
+ ```bash
24
+ npx stardeck-data-store generate-types
25
+ ```
26
+
27
+ This writes `src/generated/data-store-types.ts` and
28
+ `src/generated/data-store-schema.sql`. The harness applies the SQL snapshot to
29
+ PGlite at test boot. **Commit both files.**
30
+
31
+ ## Writing tests
32
+
33
+ ```ts
34
+ import { beforeAll, beforeEach, afterAll, expect, it } from "vitest";
35
+ import {
36
+ createTestApp,
37
+ callRoute,
38
+ describeWorkflow,
39
+ type TestApp,
40
+ } from "@stardeck-customer-apps/testing";
41
+ import { POST as checkout } from "@/app/api/checkout/route";
42
+
43
+ let app: TestApp;
44
+
45
+ beforeAll(async () => {
46
+ app = await createTestApp({
47
+ seed: async (db) => {
48
+ await db.exec(`INSERT INTO products (name, price, stock) VALUES ('Widget', 19.99, 10)`);
49
+ },
50
+ });
51
+ });
52
+ beforeEach(() => app.reset());
53
+ afterAll(() => app.close());
54
+
55
+ describeWorkflow("checkout", () => {
56
+ it("completes an order, decrements stock, and emails the customer", async () => {
57
+ const user = app.asUser({ email: "buyer@example.com" });
58
+
59
+ const res = await callRoute(checkout, { body: { items: [{ sku: "Widget", qty: 2 }] } });
60
+ expect(res.status).toBe(200);
61
+
62
+ const [product] = await app.query(`SELECT stock FROM products WHERE name = 'Widget'`);
63
+ expect(product.stock).toBe(8);
64
+
65
+ expect(app.inbox.latest(user.email!)?.subject).toMatch(/order confirmed/i);
66
+ });
67
+ });
68
+ ```
69
+
70
+ ## API
71
+
72
+ - `createTestApp(options)` — boots PGlite + the control-plane simulator and
73
+ sets all Stardeck env vars (`CONTROL_PLANE_URL`, `DEPLOYMENT_SECRET`,
74
+ `DATA_STORE_URL`, ids). One per test file, in `beforeAll`.
75
+ - `schema` — path to the DDL snapshot (default
76
+ `./src/generated/data-store-schema.sql`); `null` for apps without a store.
77
+ - `schemaSql` — inline DDL instead of a file.
78
+ - `seed(db)` — seed rows; re-runs on every `reset()`.
79
+ - `allowNetwork` — permit non-Stardeck outbound fetches (off by default).
80
+ - `app.reset()` — wipe + re-apply schema + re-seed + clear inbox/sessions.
81
+ Call in `beforeEach`.
82
+ - `app.asUser(partial?)` — sign in a test user; `getSession()` in app code
83
+ resolves to it. `app.signOut()` clears.
84
+ - `app.issueSession(partial?)` — mint access/refresh tokens for cookie-based
85
+ auth flows (`__stardeck:sess`).
86
+ - `app.inbox` — emails sent through the email-sdk: `.latest(addr?)`,
87
+ `.to(addr)`, `.all()`, `.count`, `.clear()`.
88
+ - `app.query(sql, params?)` / `app.db` — direct database access for
89
+ assertions and seeding.
90
+ - `callRoute(handler, opts)` — invoke an App Router route handler with a real
91
+ `NextRequest`. Options: `body`, `searchParams`, `params`, `headers`,
92
+ `cookies`, `method`, `path`, `user`. `redirect()` calls come back as a
93
+ redirect `Response` instead of throwing.
94
+ - `describeWorkflow(name, fn)` — tag a suite as a critical business workflow
95
+ ("checkout", "booking", "inventory"). The platform reports these per
96
+ deployment — every critical workflow should have one.
97
+
98
+ ## What works out of the box
99
+
100
+ - `DataStoreClient` (query/insert/update/delete/schema ops) — served by the
101
+ simulator against PGlite with production semantics.
102
+ - `createDataStore()` / Kysely — the Neon HTTP driver speaks to PGlite through
103
+ the simulated wire protocol; generated types work unchanged.
104
+ - `getSession()` / `requireAuth()` from project-auth — resolves the
105
+ `asUser(...)` user (header fast path) or issued session cookies.
106
+ - `EmailClient.send()` — captured in `app.inbox`, never delivered.
107
+ - `next/headers` (`headers()`/`cookies()`) inside handlers under `callRoute`.
108
+
109
+ ## Rules
110
+
111
+ - Test **business logic and route handlers**, not framework plumbing. Focus on
112
+ the workflows the owner cares about: orders, bookings, inventory, payments.
113
+ - Never weaken or skip a failing test to make a run pass — fix the code, or
114
+ report the failure.
115
+ - Tests must not depend on real network, current time of day, or run order.
116
+ `reset()` in `beforeEach` is the supported isolation model.
117
+ - Assert on outcomes (rows, status codes, captured emails), not on
118
+ implementation details.
@@ -0,0 +1,16 @@
1
+ import { ViteUserConfig } from 'vitest/config';
2
+
3
+ /**
4
+ * Vitest config for Stardeck customer apps. Aliases `next/headers` (and
5
+ * `server-only`) to harness shims so server code runs outside a Next request
6
+ * scope, and registers the harness setup file.
7
+ *
8
+ * ```ts
9
+ * // vitest.config.ts
10
+ * import { defineStardeckTestConfig } from "@stardeck-customer-apps/testing/config";
11
+ * export default defineStardeckTestConfig();
12
+ * ```
13
+ */
14
+ declare function defineStardeckTestConfig(overrides?: ViteUserConfig): ViteUserConfig;
15
+
16
+ export { defineStardeckTestConfig };
@@ -0,0 +1,16 @@
1
+ import { ViteUserConfig } from 'vitest/config';
2
+
3
+ /**
4
+ * Vitest config for Stardeck customer apps. Aliases `next/headers` (and
5
+ * `server-only`) to harness shims so server code runs outside a Next request
6
+ * scope, and registers the harness setup file.
7
+ *
8
+ * ```ts
9
+ * // vitest.config.ts
10
+ * import { defineStardeckTestConfig } from "@stardeck-customer-apps/testing/config";
11
+ * export default defineStardeckTestConfig();
12
+ * ```
13
+ */
14
+ declare function defineStardeckTestConfig(overrides?: ViteUserConfig): ViteUserConfig;
15
+
16
+ export { defineStardeckTestConfig };
package/dist/config.js ADDED
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/config.ts
21
+ var config_exports = {};
22
+ __export(config_exports, {
23
+ defineStardeckTestConfig: () => defineStardeckTestConfig
24
+ });
25
+ module.exports = __toCommonJS(config_exports);
26
+ function defineStardeckTestConfig(overrides = {}) {
27
+ return {
28
+ ...overrides,
29
+ resolve: {
30
+ ...overrides.resolve,
31
+ alias: {
32
+ "next/headers": "@stardeck-customer-apps/testing/next-headers",
33
+ "server-only": "@stardeck-customer-apps/testing/server-only",
34
+ ...overrides.resolve?.alias
35
+ }
36
+ },
37
+ test: {
38
+ environment: "node",
39
+ globals: true,
40
+ include: ["src/**/*.test.{ts,tsx}", "tests/**/*.test.{ts,tsx}"],
41
+ // One test file per isolated fork: harness state (PGlite, fetch
42
+ // interceptor, sessions) is process-global by design, so files must
43
+ // not share a live process.
44
+ pool: "forks",
45
+ isolate: true,
46
+ ...overrides.test,
47
+ setupFiles: [
48
+ "@stardeck-customer-apps/testing/setup",
49
+ ...overrides.test?.setupFiles ? Array.isArray(overrides.test.setupFiles) ? overrides.test.setupFiles : [overrides.test.setupFiles] : []
50
+ ]
51
+ }
52
+ };
53
+ }
54
+ // Annotate the CommonJS export names for ESM import in node:
55
+ 0 && (module.exports = {
56
+ defineStardeckTestConfig
57
+ });
@@ -0,0 +1,32 @@
1
+ // src/config.ts
2
+ function defineStardeckTestConfig(overrides = {}) {
3
+ return {
4
+ ...overrides,
5
+ resolve: {
6
+ ...overrides.resolve,
7
+ alias: {
8
+ "next/headers": "@stardeck-customer-apps/testing/next-headers",
9
+ "server-only": "@stardeck-customer-apps/testing/server-only",
10
+ ...overrides.resolve?.alias
11
+ }
12
+ },
13
+ test: {
14
+ environment: "node",
15
+ globals: true,
16
+ include: ["src/**/*.test.{ts,tsx}", "tests/**/*.test.{ts,tsx}"],
17
+ // One test file per isolated fork: harness state (PGlite, fetch
18
+ // interceptor, sessions) is process-global by design, so files must
19
+ // not share a live process.
20
+ pool: "forks",
21
+ isolate: true,
22
+ ...overrides.test,
23
+ setupFiles: [
24
+ "@stardeck-customer-apps/testing/setup",
25
+ ...overrides.test?.setupFiles ? Array.isArray(overrides.test.setupFiles) ? overrides.test.setupFiles : [overrides.test.setupFiles] : []
26
+ ]
27
+ }
28
+ };
29
+ }
30
+ export {
31
+ defineStardeckTestConfig
32
+ };
@@ -0,0 +1,143 @@
1
+ import { PGlite } from '@electric-sql/pglite';
2
+
3
+ /**
4
+ * The user shape returned by project-auth's `getSession()`. Mirrors the
5
+ * `UserSchema` in @stardeck-customer-apps/project-auth — keep in sync.
6
+ */
7
+ interface TestUser {
8
+ id: string;
9
+ email?: string | null;
10
+ name?: string | null;
11
+ role?: string | null;
12
+ permissions?: string[];
13
+ organizationId?: string | null;
14
+ projectId?: string | null;
15
+ }
16
+ interface CapturedEmail {
17
+ resendId: string;
18
+ fromAddress: string;
19
+ from: {
20
+ name: string;
21
+ prefix: string;
22
+ };
23
+ to: string[];
24
+ cc: string[];
25
+ bcc: string[];
26
+ subject: string;
27
+ html?: string;
28
+ text?: string;
29
+ replyTo?: string;
30
+ attachments: Array<{
31
+ filename: string;
32
+ contentType?: string;
33
+ }>;
34
+ sentAt: Date;
35
+ }
36
+ interface TestInbox {
37
+ /** All captured emails, oldest first. */
38
+ all(): CapturedEmail[];
39
+ /** Emails addressed to (to/cc/bcc) the given address. */
40
+ to(address: string): CapturedEmail[];
41
+ /** Most recent email, optionally filtered by recipient address. */
42
+ latest(address?: string): CapturedEmail | undefined;
43
+ clear(): void;
44
+ get count(): number;
45
+ }
46
+ interface TestAppOptions {
47
+ /**
48
+ * Path to the schema.sql snapshot generated by
49
+ * `npx stardeck-data-store generate-types` (relative to the project root).
50
+ * Set to `null` for apps without a data store.
51
+ */
52
+ schema?: string | null;
53
+ /** Inline DDL instead of a schema file (useful for harness-level tests). */
54
+ schemaSql?: string;
55
+ /** Seed data after the schema is applied. Runs again on every `reset()`. */
56
+ seed?: (db: PGlite) => Promise<void>;
57
+ /**
58
+ * Allow real network access for hosts other than the simulated Stardeck
59
+ * services. Defaults to false: unexpected outbound fetches fail the test
60
+ * with a descriptive error so tests stay deterministic.
61
+ */
62
+ allowNetwork?: boolean;
63
+ }
64
+ interface SessionTokens {
65
+ accessToken: string;
66
+ refreshToken: string;
67
+ }
68
+ interface TestApp {
69
+ /** The in-process Postgres instance backing all simulated data stores. */
70
+ db: PGlite;
71
+ /** Captured outbound email. */
72
+ inbox: TestInbox;
73
+ /** Convenience for raw SQL: `app.query("SELECT ...", [param])`. */
74
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
75
+ /**
76
+ * Sign in a test user for subsequent requests. `getSession()` in app code
77
+ * will resolve to this user. Returns the full user object.
78
+ */
79
+ asUser(user?: Partial<TestUser>): TestUser;
80
+ /** Clear the active test user. */
81
+ signOut(): void;
82
+ /**
83
+ * Mint access/refresh tokens for cookie-based auth flows. The simulated
84
+ * control-plane verify endpoint resolves these tokens back to the user.
85
+ */
86
+ issueSession(user?: Partial<TestUser>): SessionTokens;
87
+ /** Drop all tables, re-apply the schema, re-run the seed, clear inbox/user. */
88
+ reset(): Promise<void>;
89
+ /** Tear down the app and restore global fetch. */
90
+ close(): Promise<void>;
91
+ }
92
+
93
+ /**
94
+ * Boots the test harness: in-process Postgres, simulated control plane, and
95
+ * test env vars. Call once per test file (typically in `beforeAll`), and use
96
+ * `app.reset()` in `beforeEach` for a clean slate per test.
97
+ */
98
+ declare function createTestApp(options?: TestAppOptions): Promise<TestApp>;
99
+
100
+ type RouteHandler<Req extends Request, P> = (request: Req, context: {
101
+ params: Promise<P>;
102
+ }) => Promise<Response> | Response;
103
+ interface CallRouteOptions {
104
+ /** Request path, e.g. "/api/orders". Defaults to "/api/test-route". */
105
+ path?: string;
106
+ method?: string;
107
+ /** JSON body. Implies POST unless `method` is set. */
108
+ body?: unknown;
109
+ searchParams?: Record<string, string>;
110
+ /** Next.js dynamic segment params, e.g. { id: "123" }. */
111
+ params?: Record<string, string | string[]>;
112
+ headers?: Record<string, string>;
113
+ cookies?: Record<string, string>;
114
+ /** Override the active test user for this call only. */
115
+ user?: TestUser | null;
116
+ }
117
+ declare function callRoute<Req extends Request = Request, P = Record<string, string | string[]>>(handler: RouteHandler<Req, P>, options?: CallRouteOptions): Promise<Response>;
118
+
119
+ /**
120
+ * Tags a describe block as covering a named critical workflow ("checkout",
121
+ * "booking", "inventory"). The platform parses the `workflow:` prefix out of
122
+ * test reporter output to show per-workflow pass/fail on deployments — use
123
+ * one block per workflow the app's owner cares about.
124
+ */
125
+ declare function describeWorkflow(name: string, fn: () => void): void;
126
+ declare const WORKFLOW_NAME_PREFIX = "workflow:";
127
+ /** Extracts the workflow name from a tagged describe block title, if any. */
128
+ declare function parseWorkflowName(describeTitle: string): string | null;
129
+
130
+ declare const CONTROL_PLANE_TEST_URL = "https://control-plane.stardeck.test";
131
+ declare const DATA_STORE_TEST_HOST = "db.stardeck.test";
132
+ declare const TEST_ENV_DEFAULTS: {
133
+ readonly CONTROL_PLANE_URL: "https://control-plane.stardeck.test";
134
+ readonly DEPLOYMENT_SECRET: "stardeck-test-deployment-secret";
135
+ readonly ORGANIZATION_ID: "00000000-0000-4000-8000-00000000000a";
136
+ readonly PROJECT_ID: "00000000-0000-4000-8000-00000000000b";
137
+ readonly DEPLOYMENT_ID: "00000000-0000-4000-8000-00000000000c";
138
+ readonly DATA_STORE_URL: "postgresql://test:test@db.stardeck.test/main";
139
+ };
140
+ /** Default location of the DDL snapshot written by `generate-types`. */
141
+ declare const DEFAULT_SCHEMA_PATH = "./src/generated/data-store-schema.sql";
142
+
143
+ export { CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedEmail, DATA_STORE_TEST_HOST, DEFAULT_SCHEMA_PATH, type SessionTokens, TEST_ENV_DEFAULTS, type TestApp, type TestAppOptions, type TestInbox, type TestUser, WORKFLOW_NAME_PREFIX, callRoute, createTestApp, describeWorkflow, parseWorkflowName };
@@ -0,0 +1,143 @@
1
+ import { PGlite } from '@electric-sql/pglite';
2
+
3
+ /**
4
+ * The user shape returned by project-auth's `getSession()`. Mirrors the
5
+ * `UserSchema` in @stardeck-customer-apps/project-auth — keep in sync.
6
+ */
7
+ interface TestUser {
8
+ id: string;
9
+ email?: string | null;
10
+ name?: string | null;
11
+ role?: string | null;
12
+ permissions?: string[];
13
+ organizationId?: string | null;
14
+ projectId?: string | null;
15
+ }
16
+ interface CapturedEmail {
17
+ resendId: string;
18
+ fromAddress: string;
19
+ from: {
20
+ name: string;
21
+ prefix: string;
22
+ };
23
+ to: string[];
24
+ cc: string[];
25
+ bcc: string[];
26
+ subject: string;
27
+ html?: string;
28
+ text?: string;
29
+ replyTo?: string;
30
+ attachments: Array<{
31
+ filename: string;
32
+ contentType?: string;
33
+ }>;
34
+ sentAt: Date;
35
+ }
36
+ interface TestInbox {
37
+ /** All captured emails, oldest first. */
38
+ all(): CapturedEmail[];
39
+ /** Emails addressed to (to/cc/bcc) the given address. */
40
+ to(address: string): CapturedEmail[];
41
+ /** Most recent email, optionally filtered by recipient address. */
42
+ latest(address?: string): CapturedEmail | undefined;
43
+ clear(): void;
44
+ get count(): number;
45
+ }
46
+ interface TestAppOptions {
47
+ /**
48
+ * Path to the schema.sql snapshot generated by
49
+ * `npx stardeck-data-store generate-types` (relative to the project root).
50
+ * Set to `null` for apps without a data store.
51
+ */
52
+ schema?: string | null;
53
+ /** Inline DDL instead of a schema file (useful for harness-level tests). */
54
+ schemaSql?: string;
55
+ /** Seed data after the schema is applied. Runs again on every `reset()`. */
56
+ seed?: (db: PGlite) => Promise<void>;
57
+ /**
58
+ * Allow real network access for hosts other than the simulated Stardeck
59
+ * services. Defaults to false: unexpected outbound fetches fail the test
60
+ * with a descriptive error so tests stay deterministic.
61
+ */
62
+ allowNetwork?: boolean;
63
+ }
64
+ interface SessionTokens {
65
+ accessToken: string;
66
+ refreshToken: string;
67
+ }
68
+ interface TestApp {
69
+ /** The in-process Postgres instance backing all simulated data stores. */
70
+ db: PGlite;
71
+ /** Captured outbound email. */
72
+ inbox: TestInbox;
73
+ /** Convenience for raw SQL: `app.query("SELECT ...", [param])`. */
74
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
75
+ /**
76
+ * Sign in a test user for subsequent requests. `getSession()` in app code
77
+ * will resolve to this user. Returns the full user object.
78
+ */
79
+ asUser(user?: Partial<TestUser>): TestUser;
80
+ /** Clear the active test user. */
81
+ signOut(): void;
82
+ /**
83
+ * Mint access/refresh tokens for cookie-based auth flows. The simulated
84
+ * control-plane verify endpoint resolves these tokens back to the user.
85
+ */
86
+ issueSession(user?: Partial<TestUser>): SessionTokens;
87
+ /** Drop all tables, re-apply the schema, re-run the seed, clear inbox/user. */
88
+ reset(): Promise<void>;
89
+ /** Tear down the app and restore global fetch. */
90
+ close(): Promise<void>;
91
+ }
92
+
93
+ /**
94
+ * Boots the test harness: in-process Postgres, simulated control plane, and
95
+ * test env vars. Call once per test file (typically in `beforeAll`), and use
96
+ * `app.reset()` in `beforeEach` for a clean slate per test.
97
+ */
98
+ declare function createTestApp(options?: TestAppOptions): Promise<TestApp>;
99
+
100
+ type RouteHandler<Req extends Request, P> = (request: Req, context: {
101
+ params: Promise<P>;
102
+ }) => Promise<Response> | Response;
103
+ interface CallRouteOptions {
104
+ /** Request path, e.g. "/api/orders". Defaults to "/api/test-route". */
105
+ path?: string;
106
+ method?: string;
107
+ /** JSON body. Implies POST unless `method` is set. */
108
+ body?: unknown;
109
+ searchParams?: Record<string, string>;
110
+ /** Next.js dynamic segment params, e.g. { id: "123" }. */
111
+ params?: Record<string, string | string[]>;
112
+ headers?: Record<string, string>;
113
+ cookies?: Record<string, string>;
114
+ /** Override the active test user for this call only. */
115
+ user?: TestUser | null;
116
+ }
117
+ declare function callRoute<Req extends Request = Request, P = Record<string, string | string[]>>(handler: RouteHandler<Req, P>, options?: CallRouteOptions): Promise<Response>;
118
+
119
+ /**
120
+ * Tags a describe block as covering a named critical workflow ("checkout",
121
+ * "booking", "inventory"). The platform parses the `workflow:` prefix out of
122
+ * test reporter output to show per-workflow pass/fail on deployments — use
123
+ * one block per workflow the app's owner cares about.
124
+ */
125
+ declare function describeWorkflow(name: string, fn: () => void): void;
126
+ declare const WORKFLOW_NAME_PREFIX = "workflow:";
127
+ /** Extracts the workflow name from a tagged describe block title, if any. */
128
+ declare function parseWorkflowName(describeTitle: string): string | null;
129
+
130
+ declare const CONTROL_PLANE_TEST_URL = "https://control-plane.stardeck.test";
131
+ declare const DATA_STORE_TEST_HOST = "db.stardeck.test";
132
+ declare const TEST_ENV_DEFAULTS: {
133
+ readonly CONTROL_PLANE_URL: "https://control-plane.stardeck.test";
134
+ readonly DEPLOYMENT_SECRET: "stardeck-test-deployment-secret";
135
+ readonly ORGANIZATION_ID: "00000000-0000-4000-8000-00000000000a";
136
+ readonly PROJECT_ID: "00000000-0000-4000-8000-00000000000b";
137
+ readonly DEPLOYMENT_ID: "00000000-0000-4000-8000-00000000000c";
138
+ readonly DATA_STORE_URL: "postgresql://test:test@db.stardeck.test/main";
139
+ };
140
+ /** Default location of the DDL snapshot written by `generate-types`. */
141
+ declare const DEFAULT_SCHEMA_PATH = "./src/generated/data-store-schema.sql";
142
+
143
+ export { CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedEmail, DATA_STORE_TEST_HOST, DEFAULT_SCHEMA_PATH, type SessionTokens, TEST_ENV_DEFAULTS, type TestApp, type TestAppOptions, type TestInbox, type TestUser, WORKFLOW_NAME_PREFIX, callRoute, createTestApp, describeWorkflow, parseWorkflowName };