@selvajs/server 1.1.0 → 1.3.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/access/guards.d.ts +213 -0
- package/dist/access/guards.d.ts.map +1 -0
- package/dist/access/guards.js +417 -0
- package/dist/access/guards.js.map +1 -0
- package/dist/access/index.d.ts +2 -0
- package/dist/access/index.d.ts.map +1 -1
- package/dist/access/index.js +6 -0
- package/dist/access/index.js.map +1 -1
- package/dist/access/permissions-scope.d.ts +15 -0
- package/dist/access/permissions-scope.d.ts.map +1 -0
- package/dist/access/permissions-scope.js +23 -0
- package/dist/access/permissions-scope.js.map +1 -0
- package/dist/api/bodies.d.ts +104 -0
- package/dist/api/bodies.d.ts.map +1 -0
- package/dist/api/bodies.js +87 -0
- package/dist/api/bodies.js.map +1 -0
- package/dist/api/deps.d.ts +135 -0
- package/dist/api/deps.d.ts.map +1 -0
- package/dist/api/deps.js +55 -0
- package/dist/api/deps.js.map +1 -0
- package/dist/api/errors.d.ts +35 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +63 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/index.d.ts +12 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +15 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/map-core-error.d.ts +19 -0
- package/dist/api/map-core-error.d.ts.map +1 -0
- package/dist/api/map-core-error.js +49 -0
- package/dist/api/map-core-error.js.map +1 -0
- package/dist/api/pagination.d.ts +19 -0
- package/dist/api/pagination.d.ts.map +1 -0
- package/dist/api/pagination.js +40 -0
- package/dist/api/pagination.js.map +1 -0
- package/dist/api/request.d.ts +93 -0
- package/dist/api/request.d.ts.map +1 -0
- package/dist/api/request.js +150 -0
- package/dist/api/request.js.map +1 -0
- package/dist/api/respond.d.ts +29 -0
- package/dist/api/respond.d.ts.map +1 -0
- package/dist/api/respond.js +59 -0
- package/dist/api/respond.js.map +1 -0
- package/dist/api/responses-schema.d.ts +158 -0
- package/dist/api/responses-schema.d.ts.map +1 -0
- package/dist/api/responses-schema.js +104 -0
- package/dist/api/responses-schema.js.map +1 -0
- package/dist/api/responses.d.ts +19 -0
- package/dist/api/responses.d.ts.map +1 -0
- package/dist/api/responses.js +21 -0
- package/dist/api/responses.js.map +1 -0
- package/dist/api/shaped.d.ts +23 -0
- package/dist/api/shaped.d.ts.map +1 -0
- package/dist/api/shaped.js +29 -0
- package/dist/api/shaped.js.map +1 -0
- package/dist/api/types.d.ts +41 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +12 -0
- package/dist/api/types.js.map +1 -0
- package/dist/compute/errors.d.ts +18 -0
- package/dist/compute/errors.d.ts.map +1 -0
- package/dist/compute/errors.js +21 -0
- package/dist/compute/errors.js.map +1 -0
- package/dist/compute/evict-changed-servers.d.ts +22 -0
- package/dist/compute/evict-changed-servers.d.ts.map +1 -0
- package/dist/compute/evict-changed-servers.js +31 -0
- package/dist/compute/evict-changed-servers.js.map +1 -0
- package/dist/compute/idempotency-http.d.ts +47 -0
- package/dist/compute/idempotency-http.d.ts.map +1 -0
- package/dist/compute/idempotency-http.js +51 -0
- package/dist/compute/idempotency-http.js.map +1 -0
- package/dist/compute/index.d.ts +5 -0
- package/dist/compute/index.d.ts.map +1 -1
- package/dist/compute/index.js +5 -0
- package/dist/compute/index.js.map +1 -1
- package/dist/compute/resolve-server.d.ts +25 -0
- package/dist/compute/resolve-server.d.ts.map +1 -0
- package/dist/compute/resolve-server.js +35 -0
- package/dist/compute/resolve-server.js.map +1 -0
- package/dist/compute/server-config-write.d.ts +52 -0
- package/dist/compute/server-config-write.d.ts.map +1 -0
- package/dist/compute/server-config-write.js +100 -0
- package/dist/compute/server-config-write.js.map +1 -0
- package/dist/definitions/index.d.ts +1 -0
- package/dist/definitions/index.d.ts.map +1 -1
- package/dist/definitions/index.js +1 -0
- package/dist/definitions/index.js.map +1 -1
- package/dist/definitions/visibility.d.ts +74 -0
- package/dist/definitions/visibility.d.ts.map +1 -0
- package/dist/definitions/visibility.js +117 -0
- package/dist/definitions/visibility.js.map +1 -0
- package/dist/handlers/definitionVersions.d.ts +30 -0
- package/dist/handlers/definitionVersions.d.ts.map +1 -0
- package/dist/handlers/definitionVersions.js +93 -0
- package/dist/handlers/definitionVersions.js.map +1 -0
- package/dist/handlers/definitions.d.ts +35 -0
- package/dist/handlers/definitions.d.ts.map +1 -0
- package/dist/handlers/definitions.js +207 -0
- package/dist/handlers/definitions.js.map +1 -0
- package/dist/handlers/index.d.ts +27 -0
- package/dist/handlers/index.d.ts.map +1 -0
- package/dist/handlers/index.js +29 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/invite-lookup.d.ts +11 -0
- package/dist/handlers/invite-lookup.d.ts.map +1 -0
- package/dist/handlers/invite-lookup.js +20 -0
- package/dist/handlers/invite-lookup.js.map +1 -0
- package/dist/handlers/invites.d.ts +31 -0
- package/dist/handlers/invites.d.ts.map +1 -0
- package/dist/handlers/invites.js +148 -0
- package/dist/handlers/invites.js.map +1 -0
- package/dist/handlers/me.d.ts +8 -0
- package/dist/handlers/me.d.ts.map +1 -0
- package/dist/handlers/me.js +21 -0
- package/dist/handlers/me.js.map +1 -0
- package/dist/handlers/me.starred.d.ts +11 -0
- package/dist/handlers/me.starred.d.ts.map +1 -0
- package/dist/handlers/me.starred.js +21 -0
- package/dist/handlers/me.starred.js.map +1 -0
- package/dist/handlers/orgAssets.d.ts +13 -0
- package/dist/handlers/orgAssets.d.ts.map +1 -0
- package/dist/handlers/orgAssets.js +54 -0
- package/dist/handlers/orgAssets.js.map +1 -0
- package/dist/handlers/orgCompute.d.ts +16 -0
- package/dist/handlers/orgCompute.d.ts.map +1 -0
- package/dist/handlers/orgCompute.js +96 -0
- package/dist/handlers/orgCompute.js.map +1 -0
- package/dist/handlers/orgMembers.d.ts +29 -0
- package/dist/handlers/orgMembers.d.ts.map +1 -0
- package/dist/handlers/orgMembers.js +200 -0
- package/dist/handlers/orgMembers.js.map +1 -0
- package/dist/handlers/orgs.d.ts +16 -0
- package/dist/handlers/orgs.d.ts.map +1 -0
- package/dist/handlers/orgs.js +26 -0
- package/dist/handlers/orgs.js.map +1 -0
- package/dist/handlers/projectMembers.d.ts +15 -0
- package/dist/handlers/projectMembers.d.ts.map +1 -0
- package/dist/handlers/projectMembers.js +95 -0
- package/dist/handlers/projectMembers.js.map +1 -0
- package/dist/handlers/projects.d.ts +21 -0
- package/dist/handlers/projects.d.ts.map +1 -0
- package/dist/handlers/projects.js +164 -0
- package/dist/handlers/projects.js.map +1 -0
- package/dist/handlers/reclaim.d.ts +10 -0
- package/dist/handlers/reclaim.d.ts.map +1 -0
- package/dist/handlers/reclaim.js +38 -0
- package/dist/handlers/reclaim.js.map +1 -0
- package/dist/handlers/services.d.ts +22 -0
- package/dist/handlers/services.d.ts.map +1 -0
- package/dist/handlers/services.js +37 -0
- package/dist/handlers/services.js.map +1 -0
- package/dist/handlers/shareLinks.d.ts +23 -0
- package/dist/handlers/shareLinks.d.ts.map +1 -0
- package/dist/handlers/shareLinks.js +85 -0
- package/dist/handlers/shareLinks.js.map +1 -0
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +1 -0
- package/dist/http/index.js.map +1 -1
- package/dist/http/login-rate-limit.d.ts +72 -0
- package/dist/http/login-rate-limit.d.ts.map +1 -0
- package/dist/http/login-rate-limit.js +84 -0
- package/dist/http/login-rate-limit.js.map +1 -0
- package/dist/notifications/deliver-invite.d.ts +27 -0
- package/dist/notifications/deliver-invite.d.ts.map +1 -0
- package/dist/notifications/deliver-invite.js +32 -0
- package/dist/notifications/deliver-invite.js.map +1 -0
- package/dist/notifications/index.d.ts +4 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +4 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/smtp-config.d.ts +37 -0
- package/dist/notifications/smtp-config.d.ts.map +1 -0
- package/dist/notifications/smtp-config.js +41 -0
- package/dist/notifications/smtp-config.js.map +1 -0
- package/dist/notifications/smtp-provider.d.ts +18 -0
- package/dist/notifications/smtp-provider.d.ts.map +1 -0
- package/dist/notifications/smtp-provider.js +71 -0
- package/dist/notifications/smtp-provider.js.map +1 -0
- package/dist/ops/deploymentConfig.d.ts +73 -0
- package/dist/ops/deploymentConfig.d.ts.map +1 -0
- package/dist/ops/deploymentConfig.js +154 -0
- package/dist/ops/deploymentConfig.js.map +1 -0
- package/dist/ops/index.d.ts +1 -0
- package/dist/ops/index.d.ts.map +1 -1
- package/dist/ops/index.js +3 -2
- package/dist/ops/index.js.map +1 -1
- package/dist/organizations/index.d.ts +3 -0
- package/dist/organizations/index.d.ts.map +1 -0
- package/dist/organizations/index.js +5 -0
- package/dist/organizations/index.js.map +1 -0
- package/dist/organizations/list-all-members.d.ts +10 -0
- package/dist/organizations/list-all-members.d.ts.map +1 -0
- package/dist/organizations/list-all-members.js +36 -0
- package/dist/organizations/list-all-members.js.map +1 -0
- package/dist/organizations/org-asset-service.d.ts +39 -0
- package/dist/organizations/org-asset-service.d.ts.map +1 -0
- package/dist/organizations/org-asset-service.js +71 -0
- package/dist/organizations/org-asset-service.js.map +1 -0
- package/dist/projects/create-project.d.ts +30 -0
- package/dist/projects/create-project.d.ts.map +1 -0
- package/dist/projects/create-project.js +56 -0
- package/dist/projects/create-project.js.map +1 -0
- package/dist/projects/index.d.ts +2 -0
- package/dist/projects/index.d.ts.map +1 -0
- package/dist/projects/index.js +3 -0
- package/dist/projects/index.js.map +1 -0
- package/dist/providers/auth-lookup.d.ts +14 -0
- package/dist/providers/auth-lookup.d.ts.map +1 -0
- package/dist/providers/auth-lookup.js +34 -0
- package/dist/providers/auth-lookup.js.map +1 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +1 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/testing/harness.d.ts +168 -0
- package/dist/testing/harness.d.ts.map +1 -0
- package/dist/testing/harness.js +314 -0
- package/dist/testing/harness.js.map +1 -0
- package/dist/testing/index.d.ts +18 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +15 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/scenarios.d.ts +67 -0
- package/dist/testing/scenarios.d.ts.map +1 -0
- package/dist/testing/scenarios.js +104 -0
- package/dist/testing/scenarios.js.map +1 -0
- package/dist/testing/seed-adapter.d.ts +31 -0
- package/dist/testing/seed-adapter.d.ts.map +1 -0
- package/dist/testing/seed-adapter.js +19 -0
- package/dist/testing/seed-adapter.js.map +1 -0
- package/package.json +42 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/projects/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,2BAA2B,EAC3B,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/projects/index.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EACN,2BAA2B,EAG3B,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AuthUser, IAuthProvider } from '@selvajs/platform';
|
|
2
|
+
/**
|
|
3
|
+
* `IAuthProvider` has no by-email lookup and `listUsers` takes no filter, so
|
|
4
|
+
* this scans. Acceptable because the only caller is invite acceptance — once
|
|
5
|
+
* per invite, never on a hot path.
|
|
6
|
+
*
|
|
7
|
+
* A truncated scan would report an existing user as absent and send the flow
|
|
8
|
+
* down the create-account branch, where the provider rejects the duplicate. So
|
|
9
|
+
* exhausting the cursor matters: `null` must mean "not found", not "gave up".
|
|
10
|
+
* Returns `null` when the provider has no user store (`listUsers` → null) or
|
|
11
|
+
* when the cap is hit.
|
|
12
|
+
*/
|
|
13
|
+
export declare function findAuthUserByEmail(auth: IAuthProvider, email: string): Promise<AuthUser | null>;
|
|
14
|
+
//# sourceMappingURL=auth-lookup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-lookup.d.ts","sourceRoot":"","sources":["../../src/providers/auth-lookup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAOjE;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACxC,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAc1B"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const PAGE_LIMIT = 200;
|
|
2
|
+
// Runaway guard against an adapter returning a non-advancing cursor.
|
|
3
|
+
// 100 pages = 20k users, far past any deployment this product targets.
|
|
4
|
+
const MAX_PAGES = 100;
|
|
5
|
+
/**
|
|
6
|
+
* `IAuthProvider` has no by-email lookup and `listUsers` takes no filter, so
|
|
7
|
+
* this scans. Acceptable because the only caller is invite acceptance — once
|
|
8
|
+
* per invite, never on a hot path.
|
|
9
|
+
*
|
|
10
|
+
* A truncated scan would report an existing user as absent and send the flow
|
|
11
|
+
* down the create-account branch, where the provider rejects the duplicate. So
|
|
12
|
+
* exhausting the cursor matters: `null` must mean "not found", not "gave up".
|
|
13
|
+
* Returns `null` when the provider has no user store (`listUsers` → null) or
|
|
14
|
+
* when the cap is hit.
|
|
15
|
+
*/
|
|
16
|
+
export async function findAuthUserByEmail(auth, email) {
|
|
17
|
+
const needle = email.trim().toLowerCase();
|
|
18
|
+
if (!needle)
|
|
19
|
+
return null;
|
|
20
|
+
let cursor;
|
|
21
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
22
|
+
const result = await auth.listUsers({ limit: PAGE_LIMIT, cursor });
|
|
23
|
+
if (!result)
|
|
24
|
+
return null;
|
|
25
|
+
const hit = result.items.find((u) => u.email?.trim().toLowerCase() === needle);
|
|
26
|
+
if (hit)
|
|
27
|
+
return hit;
|
|
28
|
+
cursor = result.nextCursor;
|
|
29
|
+
if (!cursor)
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=auth-lookup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-lookup.js","sourceRoot":"","sources":["../../src/providers/auth-lookup.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,qEAAqE;AACrE,uEAAuE;AACvE,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,IAAmB,EACnB,KAAa;IAEb,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,IAAI,MAA0B,CAAC;IAC/B,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC;QAC7C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC;QAC/E,IAAI,GAAG;YAAE,OAAO,GAAG,CAAC;QACpB,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,MAAM,6BAA6B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/providers/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Configurable provider wiring — env-driven selection over a caller-supplied
|
|
2
2
|
// registry, with a `selva.config.js` override hook.
|
|
3
3
|
export { createSelvaProviders } from './create-selva-providers.js';
|
|
4
|
+
export { findAuthUserByEmail } from './auth-lookup.js';
|
|
4
5
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,oDAAoD;AAEpD,OAAO,EACN,oBAAoB,EAKpB,MAAM,6BAA6B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,oDAAoD;AAEpD,OAAO,EACN,oBAAoB,EAKpB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A host-agnostic harness for testing API handlers.
|
|
3
|
+
*
|
|
4
|
+
* Handlers are transport-free, but the tests around them were not: they built
|
|
5
|
+
* a SvelteKit `RequestEvent` and imported the Selva app's fixtures, so a second
|
|
6
|
+
* host could run the handlers and inherit none of the tests proving they work.
|
|
7
|
+
* That is the duplication this removes — a host wires a `TestHarness` once and
|
|
8
|
+
* gets the shared suites.
|
|
9
|
+
*
|
|
10
|
+
* Two things stay injected because no interface supplies them:
|
|
11
|
+
* - {@link SeedAuthAdapter}, since identity has no portable creation method.
|
|
12
|
+
* - `mapError`, since folding a host's own domain errors into the envelope is
|
|
13
|
+
* by definition host-specific. Omitting it is not neutral: a host whose
|
|
14
|
+
* guards throw something `runHandler` does not recognize gets a 500 where
|
|
15
|
+
* production returns 403, and every status assertion silently weakens.
|
|
16
|
+
*/
|
|
17
|
+
import { emptyProfile, type AuthUser, type DefinitionRecord, type DefinitionVersion, type ILogger, type OrgMember, type OrgPermission, type OrgRole, type Organization, type PlatformPermission, type Project, type ProjectMember, type ProjectRole, type ProjectVisibility, type RequestContext, type SelvaConfig, type ShareLink } from '@selvajs/platform';
|
|
18
|
+
import type { ApiError, ApiHandler, SelvaDeps } from '../api/index.js';
|
|
19
|
+
import type { SeedAuthAdapter } from './seed-adapter.js';
|
|
20
|
+
export interface TestHarness {
|
|
21
|
+
config: SelvaConfig;
|
|
22
|
+
auth: SeedAuthAdapter;
|
|
23
|
+
/** Folds host domain errors into the envelope, exactly as the host's adapter does. */
|
|
24
|
+
mapError?: (err: unknown) => ApiError | undefined;
|
|
25
|
+
/** Overrides merged onto the deps `callHandler` builds — token codecs, services, upload caps. */
|
|
26
|
+
deps?: Partial<Pick<SelvaDeps, 'tokens' | 'uploadLimits' | 'services'>>;
|
|
27
|
+
log?: ILogger;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Swallows output unless `LOUD=1`.
|
|
31
|
+
*
|
|
32
|
+
* `runHandler` logs through `req.log` on the 500 path. Without a logger a test
|
|
33
|
+
* that trips an unexpected 500 dies inside the error handler itself, which
|
|
34
|
+
* hides the error that caused it.
|
|
35
|
+
*/
|
|
36
|
+
export declare const silentLog: ILogger;
|
|
37
|
+
export interface SeededUser {
|
|
38
|
+
id: string;
|
|
39
|
+
email: string;
|
|
40
|
+
}
|
|
41
|
+
export declare function seedUser(h: TestHarness, email: string, platformPermissions?: PlatformPermission[]): Promise<SeededUser>;
|
|
42
|
+
export declare function seedOrg(h: TestHarness, opts: {
|
|
43
|
+
name: string;
|
|
44
|
+
slug: string;
|
|
45
|
+
ownerId: string;
|
|
46
|
+
}): Promise<Organization>;
|
|
47
|
+
export declare function seedOrgMember(h: TestHarness, opts: {
|
|
48
|
+
orgId: string;
|
|
49
|
+
userId: string;
|
|
50
|
+
role: OrgRole;
|
|
51
|
+
permissions?: OrgPermission[];
|
|
52
|
+
}): Promise<OrgMember>;
|
|
53
|
+
export declare function seedProject(h: TestHarness, opts: {
|
|
54
|
+
orgId: string;
|
|
55
|
+
name: string;
|
|
56
|
+
slug: string;
|
|
57
|
+
ownerId: string;
|
|
58
|
+
visibility?: ProjectVisibility;
|
|
59
|
+
autoJoinOnUpload?: boolean;
|
|
60
|
+
}): Promise<Project>;
|
|
61
|
+
export declare function seedProjectMember(h: TestHarness, opts: {
|
|
62
|
+
projectId: string;
|
|
63
|
+
userId: string;
|
|
64
|
+
role: ProjectRole;
|
|
65
|
+
}): Promise<ProjectMember>;
|
|
66
|
+
/**
|
|
67
|
+
* Grant platform-wide permissions to a seeded user.
|
|
68
|
+
*
|
|
69
|
+
* Replaces the whole set rather than adding to it, matching
|
|
70
|
+
* `IPermissionStore.set` — a test that means to add has to pass the existing
|
|
71
|
+
* permissions back in.
|
|
72
|
+
*/
|
|
73
|
+
export declare function grantPlatformPermissions(h: TestHarness, userId: string, perms: PlatformPermission[]): Promise<void>;
|
|
74
|
+
export interface SeededDefinition {
|
|
75
|
+
record: DefinitionRecord;
|
|
76
|
+
version: DefinitionVersion;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A definition with one version, written through the host's `DefinitionService`
|
|
80
|
+
* rather than straight into the store.
|
|
81
|
+
*
|
|
82
|
+
* Going through the service is what makes the fixture match production: it is
|
|
83
|
+
* what allocates the version, writes the blob, and moves the channel pointer,
|
|
84
|
+
* and a test that seeded the record alone would exercise a definition no solve
|
|
85
|
+
* path can load.
|
|
86
|
+
*/
|
|
87
|
+
export declare function seedDefinition(h: TestHarness, opts: {
|
|
88
|
+
projectId: string;
|
|
89
|
+
ownerId: string;
|
|
90
|
+
displayName?: string;
|
|
91
|
+
guid?: string;
|
|
92
|
+
}): Promise<SeededDefinition>;
|
|
93
|
+
export interface SeededShareLink {
|
|
94
|
+
link: ShareLink;
|
|
95
|
+
rawToken: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* A share link plus the plaintext token behind it.
|
|
99
|
+
*
|
|
100
|
+
* Minted through the harness's own codec, so the stored hash verifies against
|
|
101
|
+
* the same secret a handler resolves from `deps.tokens.shareLinks` — a second
|
|
102
|
+
* codec would hash to something no handler could ever match.
|
|
103
|
+
*/
|
|
104
|
+
export declare function seedShareLink(h: TestHarness, opts: {
|
|
105
|
+
definitionId: string;
|
|
106
|
+
channel?: 'live' | 'draft';
|
|
107
|
+
createdBy: string;
|
|
108
|
+
allowSolve?: boolean;
|
|
109
|
+
maxSolves?: number | null;
|
|
110
|
+
expiresAt?: string | null;
|
|
111
|
+
}): Promise<SeededShareLink>;
|
|
112
|
+
/**
|
|
113
|
+
* The harness a `callHandler` call should build its deps from, carried on the
|
|
114
|
+
* locals rather than passed alongside them.
|
|
115
|
+
*
|
|
116
|
+
* A test already threads `locals` from `actAs` into every call; making the
|
|
117
|
+
* harness a second argument would mean passing the same two values everywhere
|
|
118
|
+
* and would let them disagree — locals from one stack, deps from another, which
|
|
119
|
+
* type-checks and silently tests nothing.
|
|
120
|
+
*/
|
|
121
|
+
declare const HARNESS: unique symbol;
|
|
122
|
+
export interface ActingLocals {
|
|
123
|
+
user: AuthUser;
|
|
124
|
+
ctx: RequestContext;
|
|
125
|
+
profile: ReturnType<typeof emptyProfile>;
|
|
126
|
+
providers: SelvaConfig;
|
|
127
|
+
log: ILogger;
|
|
128
|
+
[HARNESS]?: TestHarness;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Build the per-request identity a handler sees.
|
|
132
|
+
*
|
|
133
|
+
* Mirrors what a host's auth hook produces: one membership lookup, plus the
|
|
134
|
+
* instance-admin fallback to the first listed org for an admin who belongs to
|
|
135
|
+
* none. Keeping this aligned with the host is the point of testing against real
|
|
136
|
+
* stores rather than mocks — a fixture that invented its own `ctx` would prove
|
|
137
|
+
* the handler works under scope no request ever has.
|
|
138
|
+
*/
|
|
139
|
+
export declare function actAs(h: TestHarness, userId: string): Promise<ActingLocals>;
|
|
140
|
+
export declare function anon(h: TestHarness): {
|
|
141
|
+
providers: SelvaConfig;
|
|
142
|
+
[HARNESS]?: TestHarness;
|
|
143
|
+
};
|
|
144
|
+
export interface CallHandlerOpts {
|
|
145
|
+
locals: unknown;
|
|
146
|
+
params?: Record<string, string>;
|
|
147
|
+
url?: string;
|
|
148
|
+
body?: unknown;
|
|
149
|
+
headers?: Record<string, string>;
|
|
150
|
+
}
|
|
151
|
+
export interface CallResult {
|
|
152
|
+
status: number;
|
|
153
|
+
headers: Headers;
|
|
154
|
+
json?: unknown;
|
|
155
|
+
text?: string;
|
|
156
|
+
location?: string;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Invoke an `ApiHandler` with no host framework in the loop.
|
|
160
|
+
*
|
|
161
|
+
* Still goes through `runHandler`, because that is the contract: it is what
|
|
162
|
+
* turns a thrown `ApiError` — or a host error `mapError` recognizes — into a
|
|
163
|
+
* status. Calling the handler bare would make every failure an exception and
|
|
164
|
+
* every status assertion untestable.
|
|
165
|
+
*/
|
|
166
|
+
export declare function callHandler(handler: ApiHandler, opts: CallHandlerOpts): Promise<CallResult>;
|
|
167
|
+
export {};
|
|
168
|
+
//# sourceMappingURL=harness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/testing/harness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A host-agnostic harness for testing API handlers.
|
|
3
|
+
*
|
|
4
|
+
* Handlers are transport-free, but the tests around them were not: they built
|
|
5
|
+
* a SvelteKit `RequestEvent` and imported the Selva app's fixtures, so a second
|
|
6
|
+
* host could run the handlers and inherit none of the tests proving they work.
|
|
7
|
+
* That is the duplication this removes — a host wires a `TestHarness` once and
|
|
8
|
+
* gets the shared suites.
|
|
9
|
+
*
|
|
10
|
+
* Two things stay injected because no interface supplies them:
|
|
11
|
+
* - {@link SeedAuthAdapter}, since identity has no portable creation method.
|
|
12
|
+
* - `mapError`, since folding a host's own domain errors into the envelope is
|
|
13
|
+
* by definition host-specific. Omitting it is not neutral: a host whose
|
|
14
|
+
* guards throw something `runHandler` does not recognize gets a 500 where
|
|
15
|
+
* production returns 403, and every status assertion silently weakens.
|
|
16
|
+
*/
|
|
17
|
+
import { randomUUID } from 'node:crypto';
|
|
18
|
+
import { SYSTEM_CONTEXT, DEFAULT_ORG_PERMISSIONS, DEFAULT_SHARE_LINK_MAX_SOLVES, emptyProfile } from '@selvajs/platform';
|
|
19
|
+
import { depsFromConfig, runHandler } from '../api/index.js';
|
|
20
|
+
/**
|
|
21
|
+
* Swallows output unless `LOUD=1`.
|
|
22
|
+
*
|
|
23
|
+
* `runHandler` logs through `req.log` on the 500 path. Without a logger a test
|
|
24
|
+
* that trips an unexpected 500 dies inside the error handler itself, which
|
|
25
|
+
* hides the error that caused it.
|
|
26
|
+
*/
|
|
27
|
+
export const silentLog = {
|
|
28
|
+
error: (...args) => {
|
|
29
|
+
if (process.env.LOUD)
|
|
30
|
+
console.error('[test log]', ...args);
|
|
31
|
+
},
|
|
32
|
+
warn: () => { },
|
|
33
|
+
info: () => { },
|
|
34
|
+
debug: () => { },
|
|
35
|
+
child: () => silentLog
|
|
36
|
+
};
|
|
37
|
+
export async function seedUser(h, email, platformPermissions = []) {
|
|
38
|
+
// Identity row then data-layer row, in that order — the sequence production
|
|
39
|
+
// runs through its auth hook. Reversing it leaves a data row keyed to an id
|
|
40
|
+
// no identity claims.
|
|
41
|
+
const u = await h.auth.createUser(email);
|
|
42
|
+
await h.config.data.ensureUser(SYSTEM_CONTEXT, u.id);
|
|
43
|
+
if (platformPermissions.length > 0) {
|
|
44
|
+
await h.config.data.permissions.set(SYSTEM_CONTEXT, u.id, platformPermissions);
|
|
45
|
+
}
|
|
46
|
+
// `AuthUser.email` is optional — a header-auth provider need not report one.
|
|
47
|
+
// The seeded email is the caller's own input, so hand that back rather than
|
|
48
|
+
// widening every test's `email` to `string | undefined`.
|
|
49
|
+
return { id: u.id, email };
|
|
50
|
+
}
|
|
51
|
+
export async function seedOrg(h, opts) {
|
|
52
|
+
const now = new Date().toISOString();
|
|
53
|
+
const org = {
|
|
54
|
+
id: randomUUID(),
|
|
55
|
+
name: opts.name,
|
|
56
|
+
slug: opts.slug,
|
|
57
|
+
ownerId: opts.ownerId,
|
|
58
|
+
createdBy: opts.ownerId,
|
|
59
|
+
updatedBy: opts.ownerId,
|
|
60
|
+
createdAt: now,
|
|
61
|
+
updatedAt: now,
|
|
62
|
+
deletedAt: null
|
|
63
|
+
};
|
|
64
|
+
await h.config.data.orgs.createOrg(SYSTEM_CONTEXT, org);
|
|
65
|
+
return org;
|
|
66
|
+
}
|
|
67
|
+
export async function seedOrgMember(h, opts) {
|
|
68
|
+
const now = new Date().toISOString();
|
|
69
|
+
const member = {
|
|
70
|
+
orgId: opts.orgId,
|
|
71
|
+
userId: opts.userId,
|
|
72
|
+
role: opts.role,
|
|
73
|
+
permissions: opts.permissions ?? [...DEFAULT_ORG_PERMISSIONS[opts.role]],
|
|
74
|
+
joinedAt: now,
|
|
75
|
+
updatedAt: now,
|
|
76
|
+
updatedBy: opts.userId,
|
|
77
|
+
deletedAt: null
|
|
78
|
+
};
|
|
79
|
+
await h.config.data.orgs.addOrgMember(SYSTEM_CONTEXT, member);
|
|
80
|
+
return member;
|
|
81
|
+
}
|
|
82
|
+
export async function seedProject(h, opts) {
|
|
83
|
+
const now = new Date().toISOString();
|
|
84
|
+
const project = {
|
|
85
|
+
id: randomUUID(),
|
|
86
|
+
orgId: opts.orgId,
|
|
87
|
+
name: opts.name,
|
|
88
|
+
slug: opts.slug,
|
|
89
|
+
visibility: opts.visibility ?? 'private',
|
|
90
|
+
ownerId: opts.ownerId,
|
|
91
|
+
createdBy: opts.ownerId,
|
|
92
|
+
updatedBy: opts.ownerId,
|
|
93
|
+
autoJoinOnUpload: opts.autoJoinOnUpload ?? false,
|
|
94
|
+
createdAt: now,
|
|
95
|
+
updatedAt: now,
|
|
96
|
+
deletedAt: null
|
|
97
|
+
};
|
|
98
|
+
await h.config.data.projects.createProject(SYSTEM_CONTEXT, project);
|
|
99
|
+
await h.config.data.projects.addProjectMember(SYSTEM_CONTEXT, {
|
|
100
|
+
projectId: project.id,
|
|
101
|
+
userId: opts.ownerId,
|
|
102
|
+
role: 'owner',
|
|
103
|
+
joinedAt: now,
|
|
104
|
+
updatedAt: now,
|
|
105
|
+
updatedBy: opts.ownerId,
|
|
106
|
+
deletedAt: null
|
|
107
|
+
});
|
|
108
|
+
return project;
|
|
109
|
+
}
|
|
110
|
+
export async function seedProjectMember(h, opts) {
|
|
111
|
+
const now = new Date().toISOString();
|
|
112
|
+
const member = {
|
|
113
|
+
projectId: opts.projectId,
|
|
114
|
+
userId: opts.userId,
|
|
115
|
+
role: opts.role,
|
|
116
|
+
joinedAt: now,
|
|
117
|
+
updatedAt: now,
|
|
118
|
+
updatedBy: opts.userId,
|
|
119
|
+
deletedAt: null
|
|
120
|
+
};
|
|
121
|
+
await h.config.data.projects.addProjectMember(SYSTEM_CONTEXT, member);
|
|
122
|
+
return member;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Grant platform-wide permissions to a seeded user.
|
|
126
|
+
*
|
|
127
|
+
* Replaces the whole set rather than adding to it, matching
|
|
128
|
+
* `IPermissionStore.set` — a test that means to add has to pass the existing
|
|
129
|
+
* permissions back in.
|
|
130
|
+
*/
|
|
131
|
+
export async function grantPlatformPermissions(h, userId, perms) {
|
|
132
|
+
await h.config.data.permissions.set(SYSTEM_CONTEXT, userId, perms);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A definition with one version, written through the host's `DefinitionService`
|
|
136
|
+
* rather than straight into the store.
|
|
137
|
+
*
|
|
138
|
+
* Going through the service is what makes the fixture match production: it is
|
|
139
|
+
* what allocates the version, writes the blob, and moves the channel pointer,
|
|
140
|
+
* and a test that seeded the record alone would exercise a definition no solve
|
|
141
|
+
* path can load.
|
|
142
|
+
*/
|
|
143
|
+
export async function seedDefinition(h, opts) {
|
|
144
|
+
const service = h.deps?.services?.definitions;
|
|
145
|
+
if (!service)
|
|
146
|
+
throw new Error('TestHarness.deps.services.definitions is required to seed one');
|
|
147
|
+
const input = {
|
|
148
|
+
guid: opts.guid ?? randomUUID(),
|
|
149
|
+
projectId: opts.projectId,
|
|
150
|
+
ownerId: opts.ownerId,
|
|
151
|
+
fileExt: 'gh',
|
|
152
|
+
displayName: opts.displayName ?? 'Test Definition',
|
|
153
|
+
originalFilename: 'test.gh'
|
|
154
|
+
};
|
|
155
|
+
const file = new TextEncoder().encode('FAKE_GH_BYTES');
|
|
156
|
+
// Admin-shaped ctx so the service's own tenancy checks don't refuse a seed.
|
|
157
|
+
const ctx = {
|
|
158
|
+
userId: opts.ownerId,
|
|
159
|
+
actingOrgId: undefined,
|
|
160
|
+
platformPermissions: ['instance_admin'],
|
|
161
|
+
orgPermissions: []
|
|
162
|
+
};
|
|
163
|
+
const schema = { name: 'Test', inputs: [], outputs: [] };
|
|
164
|
+
return service.create(ctx, input, file, schema);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* A share link plus the plaintext token behind it.
|
|
168
|
+
*
|
|
169
|
+
* Minted through the harness's own codec, so the stored hash verifies against
|
|
170
|
+
* the same secret a handler resolves from `deps.tokens.shareLinks` — a second
|
|
171
|
+
* codec would hash to something no handler could ever match.
|
|
172
|
+
*/
|
|
173
|
+
export async function seedShareLink(h, opts) {
|
|
174
|
+
const codec = h.deps?.tokens?.shareLinks;
|
|
175
|
+
if (!codec)
|
|
176
|
+
throw new Error('TestHarness.deps.tokens.shareLinks is required to seed a link');
|
|
177
|
+
const rawToken = codec.mintRawToken();
|
|
178
|
+
const link = {
|
|
179
|
+
id: randomUUID(),
|
|
180
|
+
definitionId: opts.definitionId,
|
|
181
|
+
channel: opts.channel ?? 'live',
|
|
182
|
+
tokenHash: codec.hashToken(rawToken),
|
|
183
|
+
createdBy: opts.createdBy,
|
|
184
|
+
createdAt: new Date().toISOString(),
|
|
185
|
+
expiresAt: opts.expiresAt ?? null,
|
|
186
|
+
revokedAt: null,
|
|
187
|
+
allowSolve: opts.allowSolve ?? true,
|
|
188
|
+
maxSolves: opts.maxSolves === undefined ? DEFAULT_SHARE_LINK_MAX_SOLVES : opts.maxSolves,
|
|
189
|
+
solveCount: 0
|
|
190
|
+
};
|
|
191
|
+
await h.config.data.shareLinks.create({
|
|
192
|
+
userId: opts.createdBy,
|
|
193
|
+
actingOrgId: undefined,
|
|
194
|
+
platformPermissions: ['instance_admin'],
|
|
195
|
+
orgPermissions: []
|
|
196
|
+
}, link);
|
|
197
|
+
return { link, rawToken };
|
|
198
|
+
}
|
|
199
|
+
// ============================================================================
|
|
200
|
+
// Acting identity
|
|
201
|
+
// ============================================================================
|
|
202
|
+
/**
|
|
203
|
+
* The harness a `callHandler` call should build its deps from, carried on the
|
|
204
|
+
* locals rather than passed alongside them.
|
|
205
|
+
*
|
|
206
|
+
* A test already threads `locals` from `actAs` into every call; making the
|
|
207
|
+
* harness a second argument would mean passing the same two values everywhere
|
|
208
|
+
* and would let them disagree — locals from one stack, deps from another, which
|
|
209
|
+
* type-checks and silently tests nothing.
|
|
210
|
+
*/
|
|
211
|
+
const HARNESS = Symbol.for('@selvajs/server/testing.harness');
|
|
212
|
+
/**
|
|
213
|
+
* Build the per-request identity a handler sees.
|
|
214
|
+
*
|
|
215
|
+
* Mirrors what a host's auth hook produces: one membership lookup, plus the
|
|
216
|
+
* instance-admin fallback to the first listed org for an admin who belongs to
|
|
217
|
+
* none. Keeping this aligned with the host is the point of testing against real
|
|
218
|
+
* stores rather than mocks — a fixture that invented its own `ctx` would prove
|
|
219
|
+
* the handler works under scope no request ever has.
|
|
220
|
+
*/
|
|
221
|
+
export async function actAs(h, userId) {
|
|
222
|
+
const user = await h.auth.findById(userId);
|
|
223
|
+
if (!user)
|
|
224
|
+
throw new Error(`actAs: user not found: ${userId}`);
|
|
225
|
+
const platformPermissions = await h.config.data.permissions.getFor(SYSTEM_CONTEXT, user.id);
|
|
226
|
+
const membership = await h.config.data.orgs.findUserMembership(SYSTEM_CONTEXT, user.id);
|
|
227
|
+
let actingOrgId = membership?.org.id;
|
|
228
|
+
const orgPermissions = membership ? [...membership.member.permissions] : [];
|
|
229
|
+
if (!actingOrgId && platformPermissions.includes('instance_admin')) {
|
|
230
|
+
const firstOrgPage = await h.config.data.orgs.listOrgs(SYSTEM_CONTEXT, { limit: 1 });
|
|
231
|
+
actingOrgId = firstOrgPage.items[0]?.id;
|
|
232
|
+
}
|
|
233
|
+
const profile = (await h.config.data.userProfile.getProfile(SYSTEM_CONTEXT, user.id)) ?? emptyProfile(user.id);
|
|
234
|
+
return {
|
|
235
|
+
user,
|
|
236
|
+
ctx: { userId: user.id, actingOrgId, platformPermissions, orgPermissions },
|
|
237
|
+
profile,
|
|
238
|
+
providers: h.config,
|
|
239
|
+
log: h.log ?? silentLog,
|
|
240
|
+
[HARNESS]: h
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
export function anon(h) {
|
|
244
|
+
return { providers: h.config, [HARNESS]: h };
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Invoke an `ApiHandler` with no host framework in the loop.
|
|
248
|
+
*
|
|
249
|
+
* Still goes through `runHandler`, because that is the contract: it is what
|
|
250
|
+
* turns a thrown `ApiError` — or a host error `mapError` recognizes — into a
|
|
251
|
+
* status. Calling the handler bare would make every failure an exception and
|
|
252
|
+
* every status assertion untestable.
|
|
253
|
+
*/
|
|
254
|
+
export async function callHandler(handler, opts) {
|
|
255
|
+
const url = new URL(opts.url ?? 'http://test.local/');
|
|
256
|
+
const init = {
|
|
257
|
+
method: opts.body !== undefined ? 'POST' : 'GET',
|
|
258
|
+
headers: opts.headers ?? {}
|
|
259
|
+
};
|
|
260
|
+
if (opts.body !== undefined) {
|
|
261
|
+
// FormData carries its own multipart content-type with a generated
|
|
262
|
+
// boundary; forcing JSON onto it makes `request.formData()` throw.
|
|
263
|
+
if (opts.body instanceof FormData) {
|
|
264
|
+
init.body = opts.body;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
init.headers['content-type'] ??= 'application/json';
|
|
268
|
+
init.body = typeof opts.body === 'string' ? opts.body : JSON.stringify(opts.body);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const locals = opts.locals;
|
|
272
|
+
const h = locals[HARNESS];
|
|
273
|
+
if (!h) {
|
|
274
|
+
// Without the harness there is no `mapError`, so every guard rejection
|
|
275
|
+
// would fall through to a 500 and each expected 403 would read as a
|
|
276
|
+
// passing-but-meaningless assertion. Fail loudly instead.
|
|
277
|
+
throw new Error('callHandler: locals carry no harness — build them with `actAs()` or `anon()`.');
|
|
278
|
+
}
|
|
279
|
+
const req = {
|
|
280
|
+
ctx: locals.ctx,
|
|
281
|
+
user: locals.user,
|
|
282
|
+
profile: locals.profile,
|
|
283
|
+
log: locals.log ?? h.log ?? silentLog,
|
|
284
|
+
params: opts.params ?? {},
|
|
285
|
+
url,
|
|
286
|
+
request: new Request(url.toString(), init),
|
|
287
|
+
deps: depsFromConfig(locals.providers, h.deps?.services ?? {}, {
|
|
288
|
+
tokens: h.deps?.tokens,
|
|
289
|
+
uploadLimits: h.deps?.uploadLimits
|
|
290
|
+
})
|
|
291
|
+
};
|
|
292
|
+
return readResponse(await runHandler(handler, req, { fallback: 'Test', mapError: h.mapError }));
|
|
293
|
+
}
|
|
294
|
+
async function readResponse(res) {
|
|
295
|
+
const contentType = res.headers.get('content-type') ?? '';
|
|
296
|
+
const out = { status: res.status, headers: res.headers };
|
|
297
|
+
if (res.headers.has('location'))
|
|
298
|
+
out.location = res.headers.get('location');
|
|
299
|
+
if (contentType.includes('application/json')) {
|
|
300
|
+
const text = await res.text();
|
|
301
|
+
out.text = text;
|
|
302
|
+
try {
|
|
303
|
+
out.json = text ? JSON.parse(text) : undefined;
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
// Leave it as text — a malformed body is the assertion's problem.
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
out.text = await res.text();
|
|
311
|
+
}
|
|
312
|
+
return out;
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=harness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness.js","sourceRoot":"","sources":["../../src/testing/harness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test harness for the transport-free API handlers.
|
|
3
|
+
*
|
|
4
|
+
* A host builds a {@link TestHarness} around its own provider stack and gets
|
|
5
|
+
* the seeders, `actAs`, and `callHandler` that the handler tests are written
|
|
6
|
+
* against — so the tests travel with the handlers instead of being rewritten
|
|
7
|
+
* per host.
|
|
8
|
+
*
|
|
9
|
+
* This entry point constructs no provider. Which stack a test runs on is the
|
|
10
|
+
* host's decision, and a package that picked one would drag that provider into
|
|
11
|
+
* every consumer's dependency tree.
|
|
12
|
+
*/
|
|
13
|
+
export type { SeedAuthAdapter } from './seed-adapter.js';
|
|
14
|
+
export { seedUser, seedOrg, seedOrgMember, seedProject, seedProjectMember, seedDefinition, seedShareLink, grantPlatformPermissions, actAs, anon, callHandler, silentLog } from './harness.js';
|
|
15
|
+
export type { TestHarness, SeededUser, SeededDefinition, SeededShareLink, ActingLocals, CallHandlerOpts, CallResult } from './harness.js';
|
|
16
|
+
export { seedAcme, seedBigClient, seedThirdOrg, seedCommons } from './scenarios.js';
|
|
17
|
+
export type { AcmeFixture, BigClientFixture, ThirdOrgFixture, CommonsFixture } from './scenarios.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EACN,QAAQ,EACR,OAAO,EACP,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,wBAAwB,EACxB,KAAK,EACL,IAAI,EACJ,WAAW,EACX,SAAS,EACT,MAAM,cAAc,CAAC;AACtB,YAAY,EACX,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,eAAe,EACf,UAAU,EACV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACpF,YAAY,EACX,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test harness for the transport-free API handlers.
|
|
3
|
+
*
|
|
4
|
+
* A host builds a {@link TestHarness} around its own provider stack and gets
|
|
5
|
+
* the seeders, `actAs`, and `callHandler` that the handler tests are written
|
|
6
|
+
* against — so the tests travel with the handlers instead of being rewritten
|
|
7
|
+
* per host.
|
|
8
|
+
*
|
|
9
|
+
* This entry point constructs no provider. Which stack a test runs on is the
|
|
10
|
+
* host's decision, and a package that picked one would drag that provider into
|
|
11
|
+
* every consumer's dependency tree.
|
|
12
|
+
*/
|
|
13
|
+
export { seedUser, seedOrg, seedOrgMember, seedProject, seedProjectMember, seedDefinition, seedShareLink, grantPlatformPermissions, actAs, anon, callHandler, silentLog } from './harness.js';
|
|
14
|
+
export { seedAcme, seedBigClient, seedThirdOrg, seedCommons } from './scenarios.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EACN,QAAQ,EACR,OAAO,EACP,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,wBAAwB,EACxB,KAAK,EACL,IAAI,EACJ,WAAW,EACX,SAAS,EACT,MAAM,cAAc,CAAC;AAUtB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
|