@venturekit/testing 0.0.0-dev.20260701100017
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/README.md +146 -0
- package/dist/auth-tokens.d.ts +55 -0
- package/dist/auth-tokens.d.ts.map +1 -0
- package/dist/auth-tokens.js +81 -0
- package/dist/auth-tokens.js.map +1 -0
- package/dist/client.d.ts +68 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +114 -0
- package/dist/client.js.map +1 -0
- package/dist/cognito.d.ts +69 -0
- package/dist/cognito.d.ts.map +1 -0
- package/dist/cognito.js +76 -0
- package/dist/cognito.js.map +1 -0
- package/dist/db.d.ts +32 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +52 -0
- package/dist/db.js.map +1 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +22 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/ready.d.ts +43 -0
- package/dist/ready.d.ts.map +1 -0
- package/dist/ready.js +67 -0
- package/dist/ready.js.map +1 -0
- package/dist/sql.d.ts +32 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/sql.js +40 -0
- package/dist/sql.js.map +1 -0
- package/dist/stack.d.ts +52 -0
- package/dist/stack.d.ts.map +1 -0
- package/dist/stack.js +142 -0
- package/dist/stack.js.map +1 -0
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @venturekit/testing
|
|
2
|
+
|
|
3
|
+
Integration & end-to-end test harness for VentureKit apps. It owns the
|
|
4
|
+
hard, framework-specific plumbing so your project only has to write the
|
|
5
|
+
actual test scenarios:
|
|
6
|
+
|
|
7
|
+
- **Launch a real local stack** — `startTestStack()` runs `vk migrate`
|
|
8
|
+
then `vk dev`, and waits until the server is ready.
|
|
9
|
+
- **Gate on readiness** — `waitForReady()` polls the dev server's
|
|
10
|
+
`/_dev/health` probe (the same liveness endpoint `vk` tooling uses).
|
|
11
|
+
- **Seed auth** — `createTestUser()` provisions a cognito-local user with a
|
|
12
|
+
permanent password over the `vk dev` admin API (no AWS SDK needed);
|
|
13
|
+
`loginAs()` additionally mints real JWTs for API-level tests.
|
|
14
|
+
- **Reset the database** — `truncateAllTables()` clears app tables between
|
|
15
|
+
specs while preserving the VentureKit migration/seed bookkeeping.
|
|
16
|
+
- **Drive the API** — `createApiClient()` is a typed `fetch` wrapper that
|
|
17
|
+
understands VentureKit's `{ data }` / `{ error }` envelope.
|
|
18
|
+
|
|
19
|
+
It's UI-framework agnostic: pair it with Playwright, Cypress, or plain
|
|
20
|
+
vitest. The scenarios (selectors, flows, assertions) stay in your project.
|
|
21
|
+
|
|
22
|
+
> Requires Docker (for the local Postgres / MinIO / cognito-local stack),
|
|
23
|
+
> so run these on macOS / Linux / CI — not over a Windows UNC share.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm add -D @venturekit/testing
|
|
29
|
+
# Optional peers (only if you use the matching helpers):
|
|
30
|
+
# @venturekit/data -> truncate* DB helpers
|
|
31
|
+
# @aws-sdk/client-cognito-identity-provider -> signInWithPassword / loginAs
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Playwright: `globalSetup` pattern
|
|
35
|
+
|
|
36
|
+
Let Playwright's `webServer` own the `vk dev` process and use the harness
|
|
37
|
+
for migration, auth seeding, and readiness:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// playwright.config.ts
|
|
41
|
+
import { defineConfig } from '@playwright/test';
|
|
42
|
+
|
|
43
|
+
export default defineConfig({
|
|
44
|
+
testDir: './e2e',
|
|
45
|
+
globalSetup: './e2e/global-setup.ts',
|
|
46
|
+
use: { baseURL: 'http://localhost:3005' }, // your UI
|
|
47
|
+
webServer: [
|
|
48
|
+
{
|
|
49
|
+
command: 'vk dev --stage test --port 4001 --no-watch',
|
|
50
|
+
url: 'http://localhost:4001/_dev/health',
|
|
51
|
+
reuseExistingServer: !process.env.CI,
|
|
52
|
+
timeout: 120_000,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
command: 'pnpm --filter @your/admin dev',
|
|
56
|
+
url: 'http://localhost:3005',
|
|
57
|
+
reuseExistingServer: !process.env.CI,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
// e2e/global-setup.ts
|
|
65
|
+
import { createTestUser, truncateAllTables, waitForReady } from '@venturekit/testing';
|
|
66
|
+
|
|
67
|
+
const API = 'http://localhost:4001';
|
|
68
|
+
|
|
69
|
+
export default async function globalSetup() {
|
|
70
|
+
await waitForReady({ baseUrl: API });
|
|
71
|
+
await truncateAllTables(); // needs DATABASE_URL / DB_* in env
|
|
72
|
+
await createTestUser({
|
|
73
|
+
baseUrl: API,
|
|
74
|
+
email: 'admin@example.com',
|
|
75
|
+
password: 'Passw0rd!',
|
|
76
|
+
attributes: { tenantId: 'global' },
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Your specs then log in through the real UI (the most faithful E2E):
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
// e2e/login.spec.ts
|
|
85
|
+
import { test, expect } from '@playwright/test';
|
|
86
|
+
|
|
87
|
+
test('logs in and lands on the dashboard', async ({ page }) => {
|
|
88
|
+
await page.goto('/login');
|
|
89
|
+
await page.getByLabel('Email').fill('admin@example.com');
|
|
90
|
+
await page.getByLabel('Password').fill('Passw0rd!');
|
|
91
|
+
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
92
|
+
await expect(page).toHaveURL(/\/(inbox|dashboard)/);
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## API-level integration tests (vitest)
|
|
97
|
+
|
|
98
|
+
No browser — boot the stack, mint a token, and hit the API directly:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { afterAll, beforeAll, expect, test } from 'vitest';
|
|
102
|
+
import { startTestStack, loginAs, createApiClient, type TestStack } from '@venturekit/testing';
|
|
103
|
+
|
|
104
|
+
let stack: TestStack;
|
|
105
|
+
|
|
106
|
+
beforeAll(async () => {
|
|
107
|
+
stack = await startTestStack({ port: 4100, seed: true });
|
|
108
|
+
}, 180_000);
|
|
109
|
+
|
|
110
|
+
afterAll(async () => {
|
|
111
|
+
await stack?.stop();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('GET /tenant returns the current tenant', async () => {
|
|
115
|
+
const { idToken } = await loginAs({
|
|
116
|
+
baseUrl: stack.baseUrl,
|
|
117
|
+
email: 'admin@example.com',
|
|
118
|
+
password: 'Passw0rd!',
|
|
119
|
+
});
|
|
120
|
+
const api = createApiClient({ baseUrl: stack.baseUrl, token: idToken });
|
|
121
|
+
const res = await api.get<{ slug: string }>('/tenant');
|
|
122
|
+
expect(res.status).toBe(200);
|
|
123
|
+
expect(res.data.slug).toBeDefined();
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## API reference
|
|
128
|
+
|
|
129
|
+
| Export | Purpose |
|
|
130
|
+
| --- | --- |
|
|
131
|
+
| `startTestStack(opts)` | Migrate + boot `vk dev`; returns `{ baseUrl, port, stop() }`. |
|
|
132
|
+
| `waitForReady(opts)` | Poll until ready (default `/_dev/health`). `vkDevServerReady` asserts the `vk` discriminator. |
|
|
133
|
+
| `createApiClient(opts)` | Typed `fetch` client; unwraps `{ data }`, throws `ApiError` on non-2xx. |
|
|
134
|
+
| `createTestUser(opts)` | Idempotently create a cognito-local user with a permanent password. |
|
|
135
|
+
| `setTestUserAttributes` / `deleteTestUser` / `getDevPools` | Manage cognito-local users / inspect pools. |
|
|
136
|
+
| `signInWithPassword(opts)` / `loginAs(opts)` | Mint real JWTs for a user (needs the Cognito SDK peer). |
|
|
137
|
+
| `truncateAllTables(opts)` / `truncateTables` / `listTables` | Reset the DB between specs (needs `@venturekit/data`). |
|
|
138
|
+
| `buildTruncateSql` / `filterTruncatableTables` / `quoteIdent` | Pure SQL builders (reusable / testable). |
|
|
139
|
+
|
|
140
|
+
## Notes
|
|
141
|
+
|
|
142
|
+
- `startTestStack` defaults to stage `test`, which targets a separate local
|
|
143
|
+
database (`<dbname>_test`) so your tests never clobber `vk dev` data.
|
|
144
|
+
- `vk dev` does **not** auto-migrate — `startTestStack` runs `vk migrate`
|
|
145
|
+
for you (pass `migrate: false` to skip, `seed: true` to also seed).
|
|
146
|
+
- DB helpers read the same `DATABASE_URL` / `DB_*` env the app uses.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mint real Cognito JWTs for a seeded cognito-local user, for API-level
|
|
3
|
+
* integration tests that need a bearer token (no browser).
|
|
4
|
+
*
|
|
5
|
+
* Uses the AWS Cognito SDK (`USER_PASSWORD_AUTH`) pointed at the local
|
|
6
|
+
* cognito-local mock. The SDK is an OPTIONAL peer dependency, lazy-imported
|
|
7
|
+
* so projects that don't need token minting never pull it in.
|
|
8
|
+
*/
|
|
9
|
+
import type { FetchImpl } from './types.js';
|
|
10
|
+
/** Default cognito-local endpoint (matches `@venturekit/cli`'s docker-compose). */
|
|
11
|
+
export declare const DEFAULT_COGNITO_ENDPOINT = "http://localhost:9229";
|
|
12
|
+
export declare const DEFAULT_COGNITO_REGION = "local";
|
|
13
|
+
export interface TokenSet {
|
|
14
|
+
idToken: string;
|
|
15
|
+
accessToken: string;
|
|
16
|
+
refreshToken?: string;
|
|
17
|
+
expiresIn?: number;
|
|
18
|
+
tokenType?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface SignInInput {
|
|
21
|
+
/** App client id (from `getDevPools`). */
|
|
22
|
+
clientId: string;
|
|
23
|
+
username: string;
|
|
24
|
+
password: string;
|
|
25
|
+
/** cognito-local endpoint. Defaults to `COGNITO_ENDPOINT` env or localhost:9229. */
|
|
26
|
+
endpoint?: string;
|
|
27
|
+
/** Region label. Defaults to `COGNITO_REGION` env or `local`. */
|
|
28
|
+
region?: string;
|
|
29
|
+
}
|
|
30
|
+
/** Sign in with username + password against cognito-local and return the tokens. */
|
|
31
|
+
export declare function signInWithPassword(input: SignInInput): Promise<TokenSet>;
|
|
32
|
+
export interface LoginAsInput {
|
|
33
|
+
/** Base URL of the running `vk dev` server (for pool lookup + user creation). */
|
|
34
|
+
baseUrl: string;
|
|
35
|
+
/** Auth intent id. Defaults to the server's first pool. */
|
|
36
|
+
intent?: string;
|
|
37
|
+
email: string;
|
|
38
|
+
password: string;
|
|
39
|
+
/** Attributes to set when creating the user (e.g. `{ tenantId: 'global' }`). */
|
|
40
|
+
attributes?: Record<string, string>;
|
|
41
|
+
/** Create the user first (idempotent). Default `true`. */
|
|
42
|
+
ensureUser?: boolean;
|
|
43
|
+
/** Override the cognito-local endpoint / region for the token call. */
|
|
44
|
+
endpoint?: string;
|
|
45
|
+
region?: string;
|
|
46
|
+
/** `fetch` implementation for the dev-server HTTP calls. */
|
|
47
|
+
fetch?: FetchImpl;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* One-shot helper for API tests: ensure a cognito-local user exists, then
|
|
51
|
+
* sign in and return its tokens. Resolves the app client id from the dev
|
|
52
|
+
* server's provisioned pools automatically.
|
|
53
|
+
*/
|
|
54
|
+
export declare function loginAs(input: LoginAsInput): Promise<TokenSet>;
|
|
55
|
+
//# sourceMappingURL=auth-tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-tokens.d.ts","sourceRoot":"","sources":["../src/auth-tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI5C,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,0BAA0B,CAAC;AAChE,eAAO,MAAM,sBAAsB,UAAU,CAAC;AAE9C,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAaD,oFAAoF;AACpF,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAiC9E;AAED,MAAM,WAAW,YAAY;IAC3B,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,0DAA0D;IAC1D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6BpE"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mint real Cognito JWTs for a seeded cognito-local user, for API-level
|
|
3
|
+
* integration tests that need a bearer token (no browser).
|
|
4
|
+
*
|
|
5
|
+
* Uses the AWS Cognito SDK (`USER_PASSWORD_AUTH`) pointed at the local
|
|
6
|
+
* cognito-local mock. The SDK is an OPTIONAL peer dependency, lazy-imported
|
|
7
|
+
* so projects that don't need token minting never pull it in.
|
|
8
|
+
*/
|
|
9
|
+
import { createTestUser, getDevPools } from './cognito.js';
|
|
10
|
+
/** Default cognito-local endpoint (matches `@venturekit/cli`'s docker-compose). */
|
|
11
|
+
export const DEFAULT_COGNITO_ENDPOINT = 'http://localhost:9229';
|
|
12
|
+
export const DEFAULT_COGNITO_REGION = 'local';
|
|
13
|
+
async function loadSdk() {
|
|
14
|
+
try {
|
|
15
|
+
return await import('@aws-sdk/client-cognito-identity-provider');
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
throw new Error("@venturekit/testing: token minting requires '@aws-sdk/client-cognito-identity-provider'. " +
|
|
19
|
+
'Install it as a dev dependency.');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Sign in with username + password against cognito-local and return the tokens. */
|
|
23
|
+
export async function signInWithPassword(input) {
|
|
24
|
+
const sdk = await loadSdk();
|
|
25
|
+
const endpoint = input.endpoint ?? process.env.COGNITO_ENDPOINT ?? DEFAULT_COGNITO_ENDPOINT;
|
|
26
|
+
const region = input.region ?? process.env.COGNITO_REGION ?? DEFAULT_COGNITO_REGION;
|
|
27
|
+
const client = new sdk.CognitoIdentityProviderClient({
|
|
28
|
+
region,
|
|
29
|
+
endpoint,
|
|
30
|
+
credentials: { accessKeyId: 'local-dev', secretAccessKey: 'local-dev' },
|
|
31
|
+
});
|
|
32
|
+
const res = await client.send(new sdk.InitiateAuthCommand({
|
|
33
|
+
AuthFlow: 'USER_PASSWORD_AUTH',
|
|
34
|
+
ClientId: input.clientId,
|
|
35
|
+
AuthParameters: { USERNAME: input.username, PASSWORD: input.password },
|
|
36
|
+
}));
|
|
37
|
+
const auth = res.AuthenticationResult;
|
|
38
|
+
if (!auth?.IdToken || !auth.AccessToken) {
|
|
39
|
+
throw new Error(`signInWithPassword: no tokens returned for ${input.username}` +
|
|
40
|
+
(res.ChallengeName ? ` (challenge: ${res.ChallengeName})` : ''));
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
idToken: auth.IdToken,
|
|
44
|
+
accessToken: auth.AccessToken,
|
|
45
|
+
refreshToken: auth.RefreshToken,
|
|
46
|
+
expiresIn: auth.ExpiresIn,
|
|
47
|
+
tokenType: auth.TokenType,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* One-shot helper for API tests: ensure a cognito-local user exists, then
|
|
52
|
+
* sign in and return its tokens. Resolves the app client id from the dev
|
|
53
|
+
* server's provisioned pools automatically.
|
|
54
|
+
*/
|
|
55
|
+
export async function loginAs(input) {
|
|
56
|
+
if (input.ensureUser !== false) {
|
|
57
|
+
await createTestUser({
|
|
58
|
+
baseUrl: input.baseUrl,
|
|
59
|
+
intent: input.intent,
|
|
60
|
+
email: input.email,
|
|
61
|
+
password: input.password,
|
|
62
|
+
attributes: input.attributes,
|
|
63
|
+
fetch: input.fetch,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const pools = await getDevPools({ baseUrl: input.baseUrl, intent: input.intent, fetch: input.fetch });
|
|
67
|
+
const intentId = input.intent ?? Object.keys(pools.pools)[0];
|
|
68
|
+
const pool = intentId ? pools.pools[intentId] : undefined;
|
|
69
|
+
if (!pool) {
|
|
70
|
+
throw new Error(`loginAs: no cognito-local pool found${intentId ? ` for intent '${intentId}'` : ''}. ` +
|
|
71
|
+
'Has `vk dev` started with an `auth` intent?');
|
|
72
|
+
}
|
|
73
|
+
return signInWithPassword({
|
|
74
|
+
clientId: pool.clientId,
|
|
75
|
+
username: input.email,
|
|
76
|
+
password: input.password,
|
|
77
|
+
endpoint: input.endpoint,
|
|
78
|
+
region: input.region,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=auth-tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-tokens.js","sourceRoot":"","sources":["../src/auth-tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAK3D,mFAAmF;AACnF,MAAM,CAAC,MAAM,wBAAwB,GAAG,uBAAuB,CAAC;AAChE,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAC;AAqB9C,KAAK,UAAU,OAAO;IACpB,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,2CAA2C,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,2FAA2F;YACzF,iCAAiC,CACpC,CAAC;IACJ,CAAC;AACH,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAkB;IACzD,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,wBAAwB,CAAC;IAC5F,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,sBAAsB,CAAC;IAEpF,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,6BAA6B,CAAC;QACnD,MAAM;QACN,QAAQ;QACR,WAAW,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE;KACxE,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAC3B,IAAI,GAAG,CAAC,mBAAmB,CAAC;QAC1B,QAAQ,EAAE,oBAAoB;QAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,cAAc,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE;KACvE,CAAC,CACH,CAAC;IAEF,MAAM,IAAI,GAAG,GAAG,CAAC,oBAAoB,CAAC;IACtC,IAAI,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CACb,8CAA8C,KAAK,CAAC,QAAQ,EAAE;YAC5D,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,gBAAgB,GAAG,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAClE,CAAC;IACJ,CAAC;IACD,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC;AACJ,CAAC;AAoBD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,KAAmB;IAC/C,IAAI,KAAK,CAAC,UAAU,KAAK,KAAK,EAAE,CAAC;QAC/B,MAAM,cAAc,CAAC;YACnB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB,CAAC,CAAC;IACL,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IACtG,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,uCAAuC,QAAQ,CAAC,CAAC,CAAC,gBAAgB,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI;YACpF,6CAA6C,CAChD,CAAC;IACJ,CAAC;IAED,OAAO,kBAAkB,CAAC;QACxB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,QAAQ,EAAE,KAAK,CAAC,KAAK;QACrB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,CAAC,CAAC;AACL,CAAC"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small, typed HTTP client for driving a running VentureKit API in
|
|
3
|
+
* integration / end-to-end tests.
|
|
4
|
+
*
|
|
5
|
+
* It understands VentureKit's response envelope:
|
|
6
|
+
* - success → `{ data, meta }` (status 2xx); `.data` is unwrapped for you.
|
|
7
|
+
* - error → `{ error: { code, message, details? } }`; thrown as `ApiError`.
|
|
8
|
+
*
|
|
9
|
+
* Everything is plain `fetch`, so it works against `vk dev`, a deployed
|
|
10
|
+
* API, or any HTTP base URL. Inject a `fetch` to stub it in unit tests.
|
|
11
|
+
*/
|
|
12
|
+
import type { FetchImpl, QueryValue } from './types.js';
|
|
13
|
+
export interface ApiClientOptions {
|
|
14
|
+
/** Base URL of the API, e.g. `http://localhost:4001`. */
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
/** Default bearer token applied to every request (overridable per call). */
|
|
17
|
+
token?: string;
|
|
18
|
+
/** Default `Cookie` header applied to every request (overridable per call). */
|
|
19
|
+
cookie?: string;
|
|
20
|
+
/** Default headers merged into every request. */
|
|
21
|
+
headers?: Record<string, string>;
|
|
22
|
+
/** `fetch` implementation. Defaults to the global `fetch`. */
|
|
23
|
+
fetch?: FetchImpl;
|
|
24
|
+
}
|
|
25
|
+
export interface RequestOptions {
|
|
26
|
+
/** Request body. Objects are JSON-encoded; strings are sent verbatim. */
|
|
27
|
+
body?: unknown;
|
|
28
|
+
/** Per-request headers (override client defaults). */
|
|
29
|
+
headers?: Record<string, string>;
|
|
30
|
+
/** Query-string params. `undefined` / `null` values are dropped. */
|
|
31
|
+
query?: Record<string, QueryValue>;
|
|
32
|
+
/** Per-request bearer token (overrides the client default). */
|
|
33
|
+
token?: string;
|
|
34
|
+
/** Per-request `Cookie` header (overrides the client default). */
|
|
35
|
+
cookie?: string;
|
|
36
|
+
/**
|
|
37
|
+
* When `false`, a non-2xx response resolves with `ok: false` instead of
|
|
38
|
+
* throwing `ApiError`. Useful for asserting error responses. Default `true`.
|
|
39
|
+
*/
|
|
40
|
+
throwOnError?: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface ApiResponse<T = unknown> {
|
|
43
|
+
status: number;
|
|
44
|
+
ok: boolean;
|
|
45
|
+
headers: Headers;
|
|
46
|
+
/**
|
|
47
|
+
* The unwrapped payload. For a VentureKit success envelope this is the
|
|
48
|
+
* `data` field; otherwise it's the full parsed JSON (or raw text).
|
|
49
|
+
*/
|
|
50
|
+
data: T;
|
|
51
|
+
/** The full parsed JSON body (envelope included), or raw text if not JSON. */
|
|
52
|
+
raw: unknown;
|
|
53
|
+
/** The raw response text. */
|
|
54
|
+
text: string;
|
|
55
|
+
}
|
|
56
|
+
export interface ApiClient {
|
|
57
|
+
readonly baseUrl: string;
|
|
58
|
+
request<T = unknown>(method: string, path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
59
|
+
get<T = unknown>(path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
60
|
+
post<T = unknown>(path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
61
|
+
put<T = unknown>(path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
62
|
+
patch<T = unknown>(path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
63
|
+
del<T = unknown>(path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
64
|
+
}
|
|
65
|
+
/** Join a base URL + path + query string into an absolute URL. */
|
|
66
|
+
export declare function buildUrl(baseUrl: string, path: string, query?: Record<string, QueryValue>): string;
|
|
67
|
+
export declare function createApiClient(options: ApiClientOptions): ApiClient;
|
|
68
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,8DAA8D;IAC9D,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,OAAO;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,IAAI,EAAE,CAAC,CAAC;IACR,8EAA8E;IAC9E,GAAG,EAAE,OAAO,CAAC;IACb,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IACtG,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAClF,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IACnF,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAClF,KAAK,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IACpF,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CACnF;AAED,kEAAkE;AAClE,wBAAgB,QAAQ,CACtB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GACjC,MAAM,CAcR;AAgBD,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAgFpE"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small, typed HTTP client for driving a running VentureKit API in
|
|
3
|
+
* integration / end-to-end tests.
|
|
4
|
+
*
|
|
5
|
+
* It understands VentureKit's response envelope:
|
|
6
|
+
* - success → `{ data, meta }` (status 2xx); `.data` is unwrapped for you.
|
|
7
|
+
* - error → `{ error: { code, message, details? } }`; thrown as `ApiError`.
|
|
8
|
+
*
|
|
9
|
+
* Everything is plain `fetch`, so it works against `vk dev`, a deployed
|
|
10
|
+
* API, or any HTTP base URL. Inject a `fetch` to stub it in unit tests.
|
|
11
|
+
*/
|
|
12
|
+
import { ApiError } from './errors.js';
|
|
13
|
+
/** Join a base URL + path + query string into an absolute URL. */
|
|
14
|
+
export function buildUrl(baseUrl, path, query) {
|
|
15
|
+
const base = baseUrl.replace(/\/+$/, '');
|
|
16
|
+
const suffix = path.startsWith('/') ? path : `/${path}`;
|
|
17
|
+
let url = `${base}${suffix}`;
|
|
18
|
+
if (query) {
|
|
19
|
+
const usp = new URLSearchParams();
|
|
20
|
+
for (const [key, value] of Object.entries(query)) {
|
|
21
|
+
if (value === undefined || value === null)
|
|
22
|
+
continue;
|
|
23
|
+
usp.append(key, String(value));
|
|
24
|
+
}
|
|
25
|
+
const qs = usp.toString();
|
|
26
|
+
if (qs)
|
|
27
|
+
url += (url.includes('?') ? '&' : '?') + qs;
|
|
28
|
+
}
|
|
29
|
+
return url;
|
|
30
|
+
}
|
|
31
|
+
/** Case-insensitive header presence check. */
|
|
32
|
+
function hasHeader(headers, name) {
|
|
33
|
+
const lower = name.toLowerCase();
|
|
34
|
+
return Object.keys(headers).some((k) => k.toLowerCase() === lower);
|
|
35
|
+
}
|
|
36
|
+
/** Pull the unwrapped `data` field out of a VentureKit success envelope. */
|
|
37
|
+
function unwrapData(raw) {
|
|
38
|
+
if (raw && typeof raw === 'object' && !Array.isArray(raw) && 'data' in raw) {
|
|
39
|
+
return raw.data;
|
|
40
|
+
}
|
|
41
|
+
return raw;
|
|
42
|
+
}
|
|
43
|
+
export function createApiClient(options) {
|
|
44
|
+
const doFetch = options.fetch ?? fetch;
|
|
45
|
+
const defaultHeaders = options.headers ?? {};
|
|
46
|
+
async function request(method, path, reqOptions = {}) {
|
|
47
|
+
const url = buildUrl(options.baseUrl, path, reqOptions.query);
|
|
48
|
+
const headers = { ...defaultHeaders, ...(reqOptions.headers ?? {}) };
|
|
49
|
+
const token = reqOptions.token ?? options.token;
|
|
50
|
+
if (token && !hasHeader(headers, 'authorization')) {
|
|
51
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
52
|
+
}
|
|
53
|
+
const cookie = reqOptions.cookie ?? options.cookie;
|
|
54
|
+
if (cookie && !hasHeader(headers, 'cookie')) {
|
|
55
|
+
headers['Cookie'] = cookie;
|
|
56
|
+
}
|
|
57
|
+
let body;
|
|
58
|
+
if (reqOptions.body !== undefined) {
|
|
59
|
+
if (typeof reqOptions.body === 'string') {
|
|
60
|
+
body = reqOptions.body;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
body = JSON.stringify(reqOptions.body);
|
|
64
|
+
if (!hasHeader(headers, 'content-type')) {
|
|
65
|
+
headers['Content-Type'] = 'application/json';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const res = await doFetch(url, { method, headers, body });
|
|
70
|
+
const text = await res.text();
|
|
71
|
+
let raw;
|
|
72
|
+
if (text) {
|
|
73
|
+
try {
|
|
74
|
+
raw = JSON.parse(text);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
raw = text;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const throwOnError = reqOptions.throwOnError ?? true;
|
|
81
|
+
if (!res.ok && throwOnError) {
|
|
82
|
+
const envelope = raw && typeof raw === 'object' && 'error' in raw
|
|
83
|
+
? raw.error
|
|
84
|
+
: undefined;
|
|
85
|
+
throw new ApiError({
|
|
86
|
+
status: res.status,
|
|
87
|
+
method,
|
|
88
|
+
url,
|
|
89
|
+
message: envelope?.message ?? `HTTP ${res.status} for ${method} ${url}`,
|
|
90
|
+
code: envelope?.code,
|
|
91
|
+
details: envelope?.details,
|
|
92
|
+
body: raw ?? text,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
status: res.status,
|
|
97
|
+
ok: res.ok,
|
|
98
|
+
headers: res.headers,
|
|
99
|
+
data: unwrapData(raw),
|
|
100
|
+
raw,
|
|
101
|
+
text,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
baseUrl: options.baseUrl,
|
|
106
|
+
request,
|
|
107
|
+
get: (path, opts) => request('GET', path, opts),
|
|
108
|
+
post: (path, opts) => request('POST', path, opts),
|
|
109
|
+
put: (path, opts) => request('PUT', path, opts),
|
|
110
|
+
patch: (path, opts) => request('PATCH', path, opts),
|
|
111
|
+
del: (path, opts) => request('DELETE', path, opts),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create / manage cognito-local users over the `vk dev` server's
|
|
3
|
+
* `/_dev/cognito/*` admin API.
|
|
4
|
+
*
|
|
5
|
+
* `vk dev` provisions a cognito-local user pool for each `auth` intent
|
|
6
|
+
* and exposes a guarded admin API (loopback + `VENTURE_LOCAL=true` only)
|
|
7
|
+
* that the `/_dev/users` page uses. These helpers reuse that same API so
|
|
8
|
+
* tests can seed a known login WITHOUT importing the AWS SDK.
|
|
9
|
+
*
|
|
10
|
+
* To obtain an actual JWT for a seeded user (e.g. for API-level tests),
|
|
11
|
+
* see `signInWithPassword` in `auth-tokens.ts`.
|
|
12
|
+
*/
|
|
13
|
+
import type { FetchImpl } from './types.js';
|
|
14
|
+
/** Identifies which `vk dev` server (and optionally which auth intent) to talk to. */
|
|
15
|
+
export interface DevServerRef {
|
|
16
|
+
/** Base URL of the running `vk dev` server, e.g. `http://localhost:4001`. */
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
/** Auth intent id. Defaults to the server's first declared intent. */
|
|
19
|
+
intent?: string;
|
|
20
|
+
/** `fetch` implementation. Defaults to the global `fetch`. */
|
|
21
|
+
fetch?: FetchImpl;
|
|
22
|
+
}
|
|
23
|
+
export interface CreateTestUserInput extends DevServerRef {
|
|
24
|
+
email: string;
|
|
25
|
+
password: string;
|
|
26
|
+
/**
|
|
27
|
+
* Extra attributes. Unprefixed custom attribute names get `custom:`
|
|
28
|
+
* added automatically by the dev server (e.g. `{ tenantId: 'global' }`
|
|
29
|
+
* → `custom:tenantId`). `email` + `email_verified=true` are always set.
|
|
30
|
+
*/
|
|
31
|
+
attributes?: Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
export interface DevPool {
|
|
34
|
+
userPoolId: string;
|
|
35
|
+
clientId: string;
|
|
36
|
+
count: number | null;
|
|
37
|
+
}
|
|
38
|
+
export interface DevPoolsResult {
|
|
39
|
+
project: string;
|
|
40
|
+
endpoint: string | null;
|
|
41
|
+
intents: Array<{
|
|
42
|
+
id: string;
|
|
43
|
+
signInWith?: string[];
|
|
44
|
+
allowSignUp?: boolean;
|
|
45
|
+
customAttributes: string[];
|
|
46
|
+
}>;
|
|
47
|
+
pools: Record<string, DevPool>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* List the cognito-local pools `vk dev` has provisioned for this project,
|
|
51
|
+
* including each pool's `userPoolId` / `clientId` (needed to mint tokens).
|
|
52
|
+
*/
|
|
53
|
+
export declare function getDevPools(ref: DevServerRef): Promise<DevPoolsResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Create a cognito-local user with a PERMANENT password (ready to log in
|
|
56
|
+
* immediately — no FORCE_CHANGE_PASSWORD step). Idempotent: if the user
|
|
57
|
+
* already exists, its password is reset to the supplied value.
|
|
58
|
+
*/
|
|
59
|
+
export declare function createTestUser(input: CreateTestUserInput): Promise<void>;
|
|
60
|
+
/** Set (or update) attributes on an existing cognito-local user. */
|
|
61
|
+
export declare function setTestUserAttributes(input: DevServerRef & {
|
|
62
|
+
email: string;
|
|
63
|
+
attributes: Record<string, string>;
|
|
64
|
+
}): Promise<void>;
|
|
65
|
+
/** Delete a cognito-local user. A missing user (404) is treated as success. */
|
|
66
|
+
export declare function deleteTestUser(input: DevServerRef & {
|
|
67
|
+
email: string;
|
|
68
|
+
}): Promise<void>;
|
|
69
|
+
//# sourceMappingURL=cognito.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cognito.d.ts","sourceRoot":"","sources":["../src/cognito.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,sFAAsF;AACtF,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,OAAO;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,KAAK,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,gBAAgB,EAAE,MAAM,EAAE,CAAC;KAC5B,CAAC,CAAC;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAMD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAI5E;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB9E;AAED,oEAAoE;AACpE,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC1E,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,+EAA+E;AAC/E,wBAAsB,cAAc,CAClC,KAAK,EAAE,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACtC,OAAO,CAAC,IAAI,CAAC,CAUf"}
|
package/dist/cognito.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create / manage cognito-local users over the `vk dev` server's
|
|
3
|
+
* `/_dev/cognito/*` admin API.
|
|
4
|
+
*
|
|
5
|
+
* `vk dev` provisions a cognito-local user pool for each `auth` intent
|
|
6
|
+
* and exposes a guarded admin API (loopback + `VENTURE_LOCAL=true` only)
|
|
7
|
+
* that the `/_dev/users` page uses. These helpers reuse that same API so
|
|
8
|
+
* tests can seed a known login WITHOUT importing the AWS SDK.
|
|
9
|
+
*
|
|
10
|
+
* To obtain an actual JWT for a seeded user (e.g. for API-level tests),
|
|
11
|
+
* see `signInWithPassword` in `auth-tokens.ts`.
|
|
12
|
+
*/
|
|
13
|
+
import { createApiClient } from './client.js';
|
|
14
|
+
import { ApiError } from './errors.js';
|
|
15
|
+
function queryFor(intent) {
|
|
16
|
+
return intent ? { intent } : undefined;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* List the cognito-local pools `vk dev` has provisioned for this project,
|
|
20
|
+
* including each pool's `userPoolId` / `clientId` (needed to mint tokens).
|
|
21
|
+
*/
|
|
22
|
+
export async function getDevPools(ref) {
|
|
23
|
+
const api = createApiClient({ baseUrl: ref.baseUrl, fetch: ref.fetch });
|
|
24
|
+
const res = await api.get('/_dev/cognito/pools');
|
|
25
|
+
return res.data;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create a cognito-local user with a PERMANENT password (ready to log in
|
|
29
|
+
* immediately — no FORCE_CHANGE_PASSWORD step). Idempotent: if the user
|
|
30
|
+
* already exists, its password is reset to the supplied value.
|
|
31
|
+
*/
|
|
32
|
+
export async function createTestUser(input) {
|
|
33
|
+
const { baseUrl, intent, email, password, attributes } = input;
|
|
34
|
+
const api = createApiClient({ baseUrl, fetch: input.fetch });
|
|
35
|
+
const query = queryFor(intent);
|
|
36
|
+
try {
|
|
37
|
+
await api.post('/_dev/cognito/users', {
|
|
38
|
+
body: { email, password, attributes },
|
|
39
|
+
query,
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
// 409 = user already exists; fall through to reset the password so the
|
|
45
|
+
// login is deterministic across repeated test runs. Anything else is fatal.
|
|
46
|
+
if (!(err instanceof ApiError) || err.status !== 409)
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
await api.post(`/_dev/cognito/users/${encodeURIComponent(email)}/password`, {
|
|
50
|
+
body: { password, permanent: true },
|
|
51
|
+
query,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** Set (or update) attributes on an existing cognito-local user. */
|
|
55
|
+
export async function setTestUserAttributes(input) {
|
|
56
|
+
const api = createApiClient({ baseUrl: input.baseUrl, fetch: input.fetch });
|
|
57
|
+
await api.post(`/_dev/cognito/users/${encodeURIComponent(input.email)}/attributes`, {
|
|
58
|
+
body: { attributes: input.attributes },
|
|
59
|
+
query: queryFor(input.intent),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/** Delete a cognito-local user. A missing user (404) is treated as success. */
|
|
63
|
+
export async function deleteTestUser(input) {
|
|
64
|
+
const api = createApiClient({ baseUrl: input.baseUrl, fetch: input.fetch });
|
|
65
|
+
try {
|
|
66
|
+
await api.del(`/_dev/cognito/users/${encodeURIComponent(input.email)}`, {
|
|
67
|
+
query: queryFor(input.intent),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
if (err instanceof ApiError && err.status === 404)
|
|
72
|
+
return;
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=cognito.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cognito.js","sourceRoot":"","sources":["../src/cognito.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA0CvC,SAAS,QAAQ,CAAC,MAAe;IAC/B,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAiB;IACjD,MAAM,GAAG,GAAG,eAAe,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;IACxE,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CAAiB,qBAAqB,CAAC,CAAC;IACjE,OAAO,GAAG,CAAC,IAAI,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAA0B;IAC7D,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IAC/D,MAAM,GAAG,GAAG,eAAe,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE/B,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE;YACpC,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE;YACrC,KAAK;SACN,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,uEAAuE;QACvE,4EAA4E;QAC5E,IAAI,CAAC,CAAC,GAAG,YAAY,QAAQ,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,MAAM,GAAG,CAAC;IAClE,CAAC;IAED,MAAM,GAAG,CAAC,IAAI,CAAC,uBAAuB,kBAAkB,CAAC,KAAK,CAAC,WAAW,EAAE;QAC1E,IAAI,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE;QACnC,KAAK;KACN,CAAC,CAAC;AACL,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,KAA2E;IAE3E,MAAM,GAAG,GAAG,eAAe,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IAC5E,MAAM,GAAG,CAAC,IAAI,CAAC,uBAAuB,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,EAAE;QAClF,IAAI,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE;QACtC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;KAC9B,CAAC,CAAC;AACL,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAuC;IAEvC,MAAM,GAAG,GAAG,eAAe,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IAC5E,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,GAAG,CAAC,uBAAuB,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE;YACtE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;SAC9B,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO;QAC1D,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
|