@w6w/sdk 0.5.0 → 0.9.8
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 +185 -32
- package/dist/mod.d.ts +10 -4
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +3 -0
- package/dist/mod.js.map +1 -1
- package/dist/src/client.d.ts +37 -5
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +40 -5
- package/dist/src/client.js.map +1 -1
- package/dist/src/config.d.ts +83 -6
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +41 -5
- package/dist/src/config.js.map +1 -1
- package/dist/src/connections.d.ts +3 -2
- package/dist/src/connections.d.ts.map +1 -1
- package/dist/src/connections.js +7 -2
- package/dist/src/connections.js.map +1 -1
- package/dist/src/console/aliases.d.ts +137 -0
- package/dist/src/console/aliases.d.ts.map +1 -0
- package/dist/src/console/aliases.js +113 -0
- package/dist/src/console/aliases.js.map +1 -0
- package/dist/src/console/api-calls.d.ts +4 -3
- package/dist/src/console/api-calls.d.ts.map +1 -1
- package/dist/src/console/api-calls.js +4 -3
- package/dist/src/console/api-calls.js.map +1 -1
- package/dist/src/console/apps.d.ts +181 -8
- package/dist/src/console/apps.d.ts.map +1 -1
- package/dist/src/console/apps.js +67 -12
- package/dist/src/console/apps.js.map +1 -1
- package/dist/src/console/auth.d.ts +245 -7
- package/dist/src/console/auth.d.ts.map +1 -1
- package/dist/src/console/auth.js +177 -8
- package/dist/src/console/auth.js.map +1 -1
- package/dist/src/console/commerce.d.ts +428 -0
- package/dist/src/console/commerce.d.ts.map +1 -0
- package/dist/src/console/commerce.js +220 -0
- package/dist/src/console/commerce.js.map +1 -0
- package/dist/src/console/dashboard.d.ts +8 -0
- package/dist/src/console/dashboard.d.ts.map +1 -1
- package/dist/src/console/dashboard.js.map +1 -1
- package/dist/src/console/endpoints.d.ts +79 -6
- package/dist/src/console/endpoints.d.ts.map +1 -1
- package/dist/src/console/endpoints.js.map +1 -1
- package/dist/src/console/functions.d.ts +35 -4
- package/dist/src/console/functions.d.ts.map +1 -1
- package/dist/src/console/functions.js +13 -0
- package/dist/src/console/functions.js.map +1 -1
- package/dist/src/console/mod.d.ts +32 -7
- package/dist/src/console/mod.d.ts.map +1 -1
- package/dist/src/console/mod.js +26 -1
- package/dist/src/console/mod.js.map +1 -1
- package/dist/src/console/otp.d.ts +127 -0
- package/dist/src/console/otp.d.ts.map +1 -0
- package/dist/src/console/otp.js +118 -0
- package/dist/src/console/otp.js.map +1 -0
- package/dist/src/console/passkeys.d.ts +246 -0
- package/dist/src/console/passkeys.d.ts.map +1 -0
- package/dist/src/console/passkeys.js +192 -0
- package/dist/src/console/passkeys.js.map +1 -0
- package/dist/src/console/reliability.d.ts +101 -6
- package/dist/src/console/reliability.d.ts.map +1 -1
- package/dist/src/console/reliability.js +16 -5
- package/dist/src/console/reliability.js.map +1 -1
- package/dist/src/console/step-tests.d.ts +7 -1
- package/dist/src/console/step-tests.d.ts.map +1 -1
- package/dist/src/console/step-tests.js +1 -1
- package/dist/src/console/step-tests.js.map +1 -1
- package/dist/src/console/subscriptions.d.ts +38 -16
- package/dist/src/console/subscriptions.d.ts.map +1 -1
- package/dist/src/console/subscriptions.js +7 -13
- package/dist/src/console/subscriptions.js.map +1 -1
- package/dist/src/console/tenant-login-flags.d.ts +106 -0
- package/dist/src/console/tenant-login-flags.d.ts.map +1 -0
- package/dist/src/console/tenant-login-flags.js +113 -0
- package/dist/src/console/tenant-login-flags.js.map +1 -0
- package/dist/src/console/tenant-oauth-apps.d.ts +75 -14
- package/dist/src/console/tenant-oauth-apps.d.ts.map +1 -1
- package/dist/src/console/tenant-oauth-apps.js +37 -11
- package/dist/src/console/tenant-oauth-apps.js.map +1 -1
- package/dist/src/console/tokens.d.ts +2 -0
- package/dist/src/console/tokens.d.ts.map +1 -1
- package/dist/src/console/tokens.js.map +1 -1
- package/dist/src/console/workflows.d.ts +14 -0
- package/dist/src/console/workflows.d.ts.map +1 -1
- package/dist/src/console/workflows.js +13 -0
- package/dist/src/console/workflows.js.map +1 -1
- package/dist/src/documents.d.ts +9 -5
- package/dist/src/documents.d.ts.map +1 -1
- package/dist/src/documents.js +6 -3
- package/dist/src/documents.js.map +1 -1
- package/dist/src/endpoints.d.ts +75 -0
- package/dist/src/endpoints.d.ts.map +1 -0
- package/dist/src/endpoints.js +47 -0
- package/dist/src/endpoints.js.map +1 -0
- package/dist/src/functions.d.ts +236 -0
- package/dist/src/functions.d.ts.map +1 -0
- package/dist/src/functions.js +201 -0
- package/dist/src/functions.js.map +1 -0
- package/dist/src/http.d.ts +58 -10
- package/dist/src/http.d.ts.map +1 -1
- package/dist/src/http.js +82 -11
- package/dist/src/http.js.map +1 -1
- package/dist/src/me.d.ts +3 -2
- package/dist/src/me.d.ts.map +1 -1
- package/dist/src/me.js +7 -2
- package/dist/src/me.js.map +1 -1
- package/dist/src/run.d.ts +19 -5
- package/dist/src/run.d.ts.map +1 -1
- package/dist/src/run.js +14 -4
- package/dist/src/run.js.map +1 -1
- package/dist/src/server/mod.d.ts +100 -0
- package/dist/src/server/mod.d.ts.map +1 -0
- package/dist/src/server/mod.js +121 -0
- package/dist/src/server/mod.js.map +1 -0
- package/dist/src/team.d.ts +204 -0
- package/dist/src/team.d.ts.map +1 -0
- package/dist/src/team.js +161 -0
- package/dist/src/team.js.map +1 -0
- package/dist/src/types.d.ts +143 -10
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +27 -0
- package/dist/src/types.js.map +1 -1
- package/dist/src/vars.d.ts +5 -3
- package/dist/src/vars.d.ts.map +1 -1
- package/dist/src/vars.js +14 -4
- package/dist/src/vars.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/workflows.d.ts +261 -9
- package/dist/src/workflows.d.ts.map +1 -1
- package/dist/src/workflows.js +246 -7
- package/dist/src/workflows.js.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@w6w/sdk/server` — a backend-only token exchange, for a partner embedding
|
|
3
|
+
* w6w inside its own product.
|
|
4
|
+
*
|
|
5
|
+
* A **separate** entry point from the package root (`@w6w/sdk`) and from
|
|
6
|
+
* `@w6w/sdk/console`: deliberately excluded from `endpoints.json`'s
|
|
7
|
+
* `operations[]` and from the root barrel (`../../mod.ts`), documented
|
|
8
|
+
* instead in `docs/sdk-surface.md` (R-5). Nothing here is reachable from
|
|
9
|
+
* either of those entry points, and this module imports nothing from
|
|
10
|
+
* `../console/mod.ts`.
|
|
11
|
+
*
|
|
12
|
+
* **Egress / trust boundary.** {@linkcode exchangeToken} sends a tenant's
|
|
13
|
+
* client secret over the wire, once per call, and only as an HTTP Basic
|
|
14
|
+
* credential — never in the body, the URL or a query string. It is built on
|
|
15
|
+
* the package's existing transport (`../http.ts`'s `request`, `../config.ts`'s
|
|
16
|
+
* `resolveConfig`) with `requireAuth: false` — the same escape hatch
|
|
17
|
+
* `console.auth.login` uses for a route that authenticates itself rather than
|
|
18
|
+
* via a bearer — so this is a thin caller of the one HTTP path this package
|
|
19
|
+
* has, never a second one.
|
|
20
|
+
*
|
|
21
|
+
* This function is meant to run on a partner's OWN backend, never in a
|
|
22
|
+
* browser: the client secret it takes must never reach client-side code. See
|
|
23
|
+
* `README.md`'s "Embedding for enterprise tenants" section for the worked
|
|
24
|
+
* route this is designed to sit behind.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import { type FetchLike } from "../config.ts";
|
|
29
|
+
/** Options for {@linkcode exchangeToken}. */
|
|
30
|
+
export interface ExchangeTokenOptions {
|
|
31
|
+
/**
|
|
32
|
+
* The w6w server's origin, e.g. `https://api.example.com`. Same rules as
|
|
33
|
+
* {@linkcode W6WClientOptions.baseUrl} — an absolute `http(s)` URL with a
|
|
34
|
+
* host.
|
|
35
|
+
*/
|
|
36
|
+
baseUrl: string;
|
|
37
|
+
/** The tenant's client id. Must not contain `:` or a non-latin-1 character. */
|
|
38
|
+
clientId: string;
|
|
39
|
+
/** The tenant's client secret. Must be latin-1 encodable. */
|
|
40
|
+
clientSecret: string;
|
|
41
|
+
/**
|
|
42
|
+
* The partner's end-user id. Minted into the returned token's `sub` claim
|
|
43
|
+
* verbatim — this function does not validate or canonicalise it.
|
|
44
|
+
*/
|
|
45
|
+
subject: string;
|
|
46
|
+
/**
|
|
47
|
+
* Optional account claim. **Trusted verbatim**: the server mints whatever
|
|
48
|
+
* `account` it is given into the token, so the caller — this function's own
|
|
49
|
+
* caller, on the partner's backend — is responsible for deriving it from
|
|
50
|
+
* ITS OWN membership data, never from an unauthenticated client input. See
|
|
51
|
+
* `README.md`'s embedding example.
|
|
52
|
+
*/
|
|
53
|
+
account?: string;
|
|
54
|
+
/** Transport override. Defaults to `globalThis.fetch`, bound. */
|
|
55
|
+
fetch?: FetchLike;
|
|
56
|
+
}
|
|
57
|
+
/** The shape `POST /auth/exchange` answers with on success. */
|
|
58
|
+
export interface ExchangeTokenResult {
|
|
59
|
+
/** The minted, short-lived bearer token. */
|
|
60
|
+
token: string;
|
|
61
|
+
/** The identity the token carries. */
|
|
62
|
+
user: {
|
|
63
|
+
/** The `subject` this call passed, echoed back. */
|
|
64
|
+
subject: string;
|
|
65
|
+
/** The tenant the client credentials resolved to. */
|
|
66
|
+
tenant: string;
|
|
67
|
+
/** Always `"user"` for a token minted this way. */
|
|
68
|
+
role: string;
|
|
69
|
+
/** The resolved account claim — the `account` passed, or the tenant id when omitted. */
|
|
70
|
+
account: string;
|
|
71
|
+
};
|
|
72
|
+
/** Token lifetime, in seconds. */
|
|
73
|
+
expiresIn: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Exchange a tenant's client credentials for a short-lived, per-user w6w
|
|
77
|
+
* token: `POST /auth/exchange`.
|
|
78
|
+
*
|
|
79
|
+
* Credentials travel ONLY as `Authorization: Basic base64(clientId:clientSecret)`
|
|
80
|
+
* — never in the body, the URL or a query string. The body sent is exactly
|
|
81
|
+
* `{ subject }`, or `{ subject, account }` when `account` is given; omitting
|
|
82
|
+
* `account` never puts the key in the body at all (not even as `null` or
|
|
83
|
+
* `undefined`).
|
|
84
|
+
*
|
|
85
|
+
* Every validation failure below is a local {@linkcode ConfigError}, raised
|
|
86
|
+
* **before any network call**, and none of them ever echo `clientSecret`.
|
|
87
|
+
*
|
|
88
|
+
* @param options - See {@linkcode ExchangeTokenOptions}.
|
|
89
|
+
* @returns The minted token and the identity it carries.
|
|
90
|
+
* @throws {ConfigError} Blank `clientId`/`clientSecret`/`subject`; a `clientId`
|
|
91
|
+
* containing `:`; a `clientId`/`clientSecret` outside latin-1; no base URL;
|
|
92
|
+
* no runtime `fetch`.
|
|
93
|
+
* @throws {ApiError} `invalid_client` (401, unknown/disabled credentials),
|
|
94
|
+
* `invalid_body` / `invalid_subject` / `invalid_account` (400), or any of
|
|
95
|
+
* the transport's own failure modes (`../http.ts`). Never retried: this
|
|
96
|
+
* call always passes `requireAuth: false`, which `request()` never grants a
|
|
97
|
+
* recovery attempt.
|
|
98
|
+
*/
|
|
99
|
+
export declare function exchangeToken(options: ExchangeTokenOptions): Promise<ExchangeTokenResult>;
|
|
100
|
+
//# sourceMappingURL=mod.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../../src/server/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,cAAc,CAAC;AAI7D,6CAA6C;AAC7C,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,+DAA+D;AAC/D,MAAM,WAAW,mBAAmB;IAClC,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,IAAI,EAAE;QACJ,mDAAmD;QACnD,OAAO,EAAE,MAAM,CAAC;QAChB,qDAAqD;QACrD,MAAM,EAAE,MAAM,CAAC;QACf,mDAAmD;QACnD,IAAI,EAAE,MAAM,CAAC;QACb,wFAAwF;QACxF,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAC;CACnB;AAsDD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAwB9B"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@w6w/sdk/server` — a backend-only token exchange, for a partner embedding
|
|
3
|
+
* w6w inside its own product.
|
|
4
|
+
*
|
|
5
|
+
* A **separate** entry point from the package root (`@w6w/sdk`) and from
|
|
6
|
+
* `@w6w/sdk/console`: deliberately excluded from `endpoints.json`'s
|
|
7
|
+
* `operations[]` and from the root barrel (`../../mod.ts`), documented
|
|
8
|
+
* instead in `docs/sdk-surface.md` (R-5). Nothing here is reachable from
|
|
9
|
+
* either of those entry points, and this module imports nothing from
|
|
10
|
+
* `../console/mod.ts`.
|
|
11
|
+
*
|
|
12
|
+
* **Egress / trust boundary.** {@linkcode exchangeToken} sends a tenant's
|
|
13
|
+
* client secret over the wire, once per call, and only as an HTTP Basic
|
|
14
|
+
* credential — never in the body, the URL or a query string. It is built on
|
|
15
|
+
* the package's existing transport (`../http.ts`'s `request`, `../config.ts`'s
|
|
16
|
+
* `resolveConfig`) with `requireAuth: false` — the same escape hatch
|
|
17
|
+
* `console.auth.login` uses for a route that authenticates itself rather than
|
|
18
|
+
* via a bearer — so this is a thin caller of the one HTTP path this package
|
|
19
|
+
* has, never a second one.
|
|
20
|
+
*
|
|
21
|
+
* This function is meant to run on a partner's OWN backend, never in a
|
|
22
|
+
* browser: the client secret it takes must never reach client-side code. See
|
|
23
|
+
* `README.md`'s "Embedding for enterprise tenants" section for the worked
|
|
24
|
+
* route this is designed to sit behind.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import { resolveConfig } from "../config.js";
|
|
29
|
+
import { ConfigError } from "../errors.js";
|
|
30
|
+
import { request } from "../http.js";
|
|
31
|
+
/** Non-blank after trimming. */
|
|
32
|
+
function required(value, field) {
|
|
33
|
+
if (value.trim().length === 0) {
|
|
34
|
+
throw new ConfigError(`exchangeToken: \`${field}\` must not be blank.`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Base64-encode `clientId:clientSecret` the way the server decodes it:
|
|
39
|
+
* latin-1 (`atob`'s own alphabet), via `btoa`.
|
|
40
|
+
*
|
|
41
|
+
* `btoa` itself rejects any character outside latin-1 (U+0000–U+00FF),
|
|
42
|
+
* raising for us — this is NOT re-implemented with a UTF-8 path, because that
|
|
43
|
+
* would silently encode a secret the server's `atob` could never decode back
|
|
44
|
+
* to the same bytes.
|
|
45
|
+
*
|
|
46
|
+
* @throws {ConfigError} When `clientId` contains `:` (indistinguishable from
|
|
47
|
+
* the Basic separator on decode) or either value is not latin-1.
|
|
48
|
+
*/
|
|
49
|
+
function basicAuthHeader(clientId, clientSecret) {
|
|
50
|
+
if (clientId.includes(":")) {
|
|
51
|
+
throw new ConfigError('exchangeToken: `clientId` must not contain ":" — the server splits a ' +
|
|
52
|
+
"decoded Basic credential on the FIRST colon, so a colon in `clientId` " +
|
|
53
|
+
"would be read as part of the secret instead.");
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return btoa(`${clientId}:${clientSecret}`);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw new ConfigError("exchangeToken: `clientId`/`clientSecret` must be representable as " +
|
|
60
|
+
"latin-1 (ISO-8859-1) — the server decodes the Basic header with " +
|
|
61
|
+
"`atob`, which cannot represent a wider character set.");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function resolveFetch(fetchOption) {
|
|
65
|
+
if (fetchOption)
|
|
66
|
+
return fetchOption;
|
|
67
|
+
const global = globalThis.fetch;
|
|
68
|
+
if (typeof global !== "function") {
|
|
69
|
+
throw new ConfigError("This runtime has no global fetch. Pass an implementation " +
|
|
70
|
+
"(exchangeToken({ ..., fetch })) or run on Node 18+, Deno or Bun.");
|
|
71
|
+
}
|
|
72
|
+
// Bound, so it keeps working when called as a bare function reference —
|
|
73
|
+
// exactly W6WClient's own default (../client.ts).
|
|
74
|
+
return global.bind(globalThis);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Exchange a tenant's client credentials for a short-lived, per-user w6w
|
|
78
|
+
* token: `POST /auth/exchange`.
|
|
79
|
+
*
|
|
80
|
+
* Credentials travel ONLY as `Authorization: Basic base64(clientId:clientSecret)`
|
|
81
|
+
* — never in the body, the URL or a query string. The body sent is exactly
|
|
82
|
+
* `{ subject }`, or `{ subject, account }` when `account` is given; omitting
|
|
83
|
+
* `account` never puts the key in the body at all (not even as `null` or
|
|
84
|
+
* `undefined`).
|
|
85
|
+
*
|
|
86
|
+
* Every validation failure below is a local {@linkcode ConfigError}, raised
|
|
87
|
+
* **before any network call**, and none of them ever echo `clientSecret`.
|
|
88
|
+
*
|
|
89
|
+
* @param options - See {@linkcode ExchangeTokenOptions}.
|
|
90
|
+
* @returns The minted token and the identity it carries.
|
|
91
|
+
* @throws {ConfigError} Blank `clientId`/`clientSecret`/`subject`; a `clientId`
|
|
92
|
+
* containing `:`; a `clientId`/`clientSecret` outside latin-1; no base URL;
|
|
93
|
+
* no runtime `fetch`.
|
|
94
|
+
* @throws {ApiError} `invalid_client` (401, unknown/disabled credentials),
|
|
95
|
+
* `invalid_body` / `invalid_subject` / `invalid_account` (400), or any of
|
|
96
|
+
* the transport's own failure modes (`../http.ts`). Never retried: this
|
|
97
|
+
* call always passes `requireAuth: false`, which `request()` never grants a
|
|
98
|
+
* recovery attempt.
|
|
99
|
+
*/
|
|
100
|
+
export async function exchangeToken(options) {
|
|
101
|
+
const { baseUrl, clientId, clientSecret, subject, account } = options;
|
|
102
|
+
required(clientId, "clientId");
|
|
103
|
+
required(clientSecret, "clientSecret");
|
|
104
|
+
required(subject, "subject");
|
|
105
|
+
const basic = basicAuthHeader(clientId, clientSecret);
|
|
106
|
+
const config = resolveConfig({ baseUrl });
|
|
107
|
+
const fetchImpl = resolveFetch(options.fetch);
|
|
108
|
+
const body = { subject };
|
|
109
|
+
if (account !== undefined) {
|
|
110
|
+
body.account = account;
|
|
111
|
+
}
|
|
112
|
+
const res = await request(config, fetchImpl, {
|
|
113
|
+
method: "POST",
|
|
114
|
+
path: "/auth/exchange",
|
|
115
|
+
body,
|
|
116
|
+
headers: { authorization: `Basic ${basic}` },
|
|
117
|
+
requireAuth: false,
|
|
118
|
+
});
|
|
119
|
+
return res.body;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=mod.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.js","sourceRoot":"","sources":["../../../src/server/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAkB,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAkDrC,gCAAgC;AAChC,SAAS,QAAQ,CAAC,KAAa,EAAE,KAAa;IAC5C,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,WAAW,CAAC,oBAAoB,KAAK,uBAAuB,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,eAAe,CAAC,QAAgB,EAAE,YAAoB;IAC7D,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,WAAW,CACnB,uEAAuE;YACrE,wEAAwE;YACxE,8CAA8C,CACjD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,GAAG,QAAQ,IAAI,YAAY,EAAE,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,WAAW,CACnB,oEAAoE;YAClE,kEAAkE;YAClE,uDAAuD,CAC1D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,WAAkC;IACtD,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IACpC,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC;IAChC,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,IAAI,WAAW,CACnB,2DAA2D;YACzD,kEAAkE,CACrE,CAAC;IACJ,CAAC;IACD,wEAAwE;IACxE,kDAAkD;IAClD,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAA6B;IAE7B,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAEtE,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC7B,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAEtD,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAE9C,MAAM,IAAI,GAA0C,EAAE,OAAO,EAAE,CAAC;IAChE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,OAAO,CAAsB,MAAM,EAAE,SAAS,EAAE;QAChE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,gBAAgB;QACtB,IAAI;QACJ,OAAO,EAAE,EAAE,aAAa,EAAE,SAAS,KAAK,EAAE,EAAE;QAC5C,WAAW,EAAE,KAAK;KACnB,CAAC,CAAC;IACH,OAAO,GAAG,CAAC,IAAI,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `client.team.*` — the caller's account team, self-service.
|
|
3
|
+
*
|
|
4
|
+
* Six operations, one HTTP call each: list the roster, invite someone (or mint
|
|
5
|
+
* an open, shareable invite link), list open invites, revoke one, change a
|
|
6
|
+
* member's role, remove a member. Nothing here composes two requests or
|
|
7
|
+
* filters a list client-side — `team.invites()` returns only OPEN invites
|
|
8
|
+
* because the **server** filters them, not because this module scans the
|
|
9
|
+
* account's whole invite history and drops the closed ones
|
|
10
|
+
* (`docs/implementation.md`'s "what a wrapper is" rule, mirrored from
|
|
11
|
+
* `documents.ts`'s header).
|
|
12
|
+
*
|
|
13
|
+
* **Not project-scoped.** Team membership is an account-wide concept, so
|
|
14
|
+
* (unlike `documents`) this namespace carries no `?project=` anywhere and is
|
|
15
|
+
* handed the transport only — the same narrow host `connections.ts` and
|
|
16
|
+
* `vars.ts` use, so a caller cannot even accidentally reach a default project
|
|
17
|
+
* to send.
|
|
18
|
+
*
|
|
19
|
+
* `docs/team.md`'s pinned wire contract (mirrored in this task's `plan.md`
|
|
20
|
+
* §"Pinned wire contract") is the source these six methods transcribe:
|
|
21
|
+
*
|
|
22
|
+
* ```
|
|
23
|
+
* GET /me/account/members → 200 { members: TeamMember[] }
|
|
24
|
+
* POST /me/account/invites { email?, role? } → 201 { invite: TeamInviteWithLink }
|
|
25
|
+
* GET /me/account/invites → 200 { invites: TeamInvite[] }
|
|
26
|
+
* DELETE /me/account/invites/:id → 200 { ok: true }
|
|
27
|
+
* PATCH /me/account/members/:userId { role } → 200 { member: TeamMember }
|
|
28
|
+
* DELETE /me/account/members/:userId → 200 { ok: true }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* This module implements the client half of the server's live
|
|
32
|
+
* `/me/account/...` routes (`endpoints.json`: `status: "required"` for each).
|
|
33
|
+
* Calling any of these against a server that has not shipped T1.2.1 yet
|
|
34
|
+
* answers `404`, which reaches the caller as an ordinary `ApiError`.
|
|
35
|
+
*
|
|
36
|
+
* @module
|
|
37
|
+
*/
|
|
38
|
+
import { type HttpResponse, type RequestOptions } from "./http.ts";
|
|
39
|
+
/**
|
|
40
|
+
* The slice of `W6WClient` this namespace needs: the transport, and nothing
|
|
41
|
+
* else. Team operations carry no per-call scope to resolve, so — as with
|
|
42
|
+
* `connections` and `vars` — this host cannot reach the client's
|
|
43
|
+
* configuration even by accident.
|
|
44
|
+
*/
|
|
45
|
+
export interface TeamHost {
|
|
46
|
+
/** Perform one request. */
|
|
47
|
+
request<T>(options: RequestOptions): Promise<HttpResponse<T>>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* One member of the caller's account team, as the roster projects it.
|
|
51
|
+
*
|
|
52
|
+
* `email`/`displayName` are nullable rather than defaulted to `""`: a member
|
|
53
|
+
* whose profile carries neither is a real, distinct state (a machine
|
|
54
|
+
* principal, or a user who has set up neither) and flattening it to an empty
|
|
55
|
+
* string would make "no display name" indistinguishable from "the server sent
|
|
56
|
+
* an empty one".
|
|
57
|
+
*/
|
|
58
|
+
export interface TeamMember {
|
|
59
|
+
/** The member's `usr_…` id — addresses `setRole` and `removeMember`. */
|
|
60
|
+
userId: string;
|
|
61
|
+
/** The member's email, or `null` when the profile carries none. */
|
|
62
|
+
email: string | null;
|
|
63
|
+
/** The member's display name, or `null` when unset. */
|
|
64
|
+
displayName: string | null;
|
|
65
|
+
/** The member's role within the account, e.g. `"owner"`, `"admin"`, `"member"`. */
|
|
66
|
+
role: string;
|
|
67
|
+
/** ISO-8601 timestamp: when this membership began. */
|
|
68
|
+
createdAt: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* One invite, as `team.invites()` lists it.
|
|
72
|
+
*
|
|
73
|
+
* Deliberately carries no `token` — see {@linkcode TeamInviteWithLink}, the
|
|
74
|
+
* only response that does, and only once.
|
|
75
|
+
*/
|
|
76
|
+
export interface TeamInvite {
|
|
77
|
+
/** The invite's own id — addresses `revokeInvite`. */
|
|
78
|
+
id: string;
|
|
79
|
+
/** The account this invite joins. */
|
|
80
|
+
account: string;
|
|
81
|
+
/** The invited email, or `null` for an open (link-only) invite. */
|
|
82
|
+
email: string | null;
|
|
83
|
+
/** The role the invite grants on redemption, or `null` (server defaults to `"member"`). */
|
|
84
|
+
role: string | null;
|
|
85
|
+
/** ISO-8601 expiry, or `null` for an invite that does not expire. */
|
|
86
|
+
expiresAt: string | null;
|
|
87
|
+
/** ISO-8601 timestamp: when this invite was created. */
|
|
88
|
+
createdAt: string;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* What `team.invite()` returns: a {@linkcode TeamInvite} plus the redemption
|
|
92
|
+
* material, present **only** in this one response.
|
|
93
|
+
*
|
|
94
|
+
* The server never re-sends `token` or `redemptionLink` from any other route
|
|
95
|
+
* — `team.invites()` lists the invite without them — so a caller that drops
|
|
96
|
+
* this return value cannot recover it and must revoke and re-invite instead.
|
|
97
|
+
*/
|
|
98
|
+
export interface TeamInviteWithLink extends TeamInvite {
|
|
99
|
+
/** The plaintext redemption token. Shown once. */
|
|
100
|
+
token: string;
|
|
101
|
+
/** The ready-to-share redemption URL, built around `token`. Shown once. */
|
|
102
|
+
redemptionLink: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The body of `team.invite()`.
|
|
106
|
+
*
|
|
107
|
+
* Both fields are optional: an omitted `email` mints an OPEN invite (a
|
|
108
|
+
* shareable link with no target address), and an omitted `role` defaults
|
|
109
|
+
* server-side to `"member"`. `"owner"` is refused — there is no self-service
|
|
110
|
+
* path to a second owner.
|
|
111
|
+
*/
|
|
112
|
+
export interface TeamInviteInput {
|
|
113
|
+
/** The address to invite. Omitted for an open, shareable invite. */
|
|
114
|
+
email?: string;
|
|
115
|
+
/** The role the invite grants on redemption. Omitted defaults to `"member"`. */
|
|
116
|
+
role?: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The `team` namespace on a `W6WClient`.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```ts
|
|
123
|
+
* const members = await client.team.members();
|
|
124
|
+
* const { redemptionLink } = await client.team.invite({ role: "admin" });
|
|
125
|
+
* await client.team.setRole(members[0].userId, "admin");
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export declare class TeamApi {
|
|
129
|
+
#private;
|
|
130
|
+
/**
|
|
131
|
+
* @param host - The client this namespace issues requests through.
|
|
132
|
+
*/
|
|
133
|
+
constructor(host: TeamHost);
|
|
134
|
+
/**
|
|
135
|
+
* List the caller's account team.
|
|
136
|
+
*
|
|
137
|
+
* Member-readable: any active member of the account may call this,
|
|
138
|
+
* regardless of role — only the write operations below require
|
|
139
|
+
* `canManageMembers`.
|
|
140
|
+
*
|
|
141
|
+
* @returns The team, unwrapped from the `members` envelope.
|
|
142
|
+
* @throws {ApiError} On any non-2xx.
|
|
143
|
+
*/
|
|
144
|
+
members(): Promise<TeamMember[]>;
|
|
145
|
+
/**
|
|
146
|
+
* Invite someone to the account, or mint an open, shareable invite link.
|
|
147
|
+
*
|
|
148
|
+
* @param input - `email` and `role`, both optional.
|
|
149
|
+
* @returns The created invite, including its one-time `token` and `redemptionLink`.
|
|
150
|
+
* @throws {ApiError} `403 forbidden` without `canManageMembers`.
|
|
151
|
+
* @throws {ApiError} `400 invalid_role` for `role: "owner"` or an unrecognised role.
|
|
152
|
+
*/
|
|
153
|
+
invite(input?: TeamInviteInput): Promise<TeamInviteWithLink>;
|
|
154
|
+
/**
|
|
155
|
+
* List the account's open (pending, unrevoked, unexpired) invites.
|
|
156
|
+
*
|
|
157
|
+
* The filtering happens server-side — this is one `GET`, never a
|
|
158
|
+
* list-then-filter over the account's whole invite history.
|
|
159
|
+
*
|
|
160
|
+
* @returns The open invites, unwrapped from the `invites` envelope.
|
|
161
|
+
* @throws {ApiError} `403 forbidden` without `canManageMembers`.
|
|
162
|
+
*/
|
|
163
|
+
invites(): Promise<TeamInvite[]>;
|
|
164
|
+
/**
|
|
165
|
+
* Revoke a pending invite before it is redeemed.
|
|
166
|
+
*
|
|
167
|
+
* Not idempotent: revoking an invite that is already used or already
|
|
168
|
+
* revoked is a rejected request, not a silent success.
|
|
169
|
+
*
|
|
170
|
+
* @param id - The invite's own id (see: `team.invites()`).
|
|
171
|
+
* @throws {ApiError} `403 forbidden` without `canManageMembers`.
|
|
172
|
+
* @throws {ApiError} `404 invite_not_found` for an unknown id.
|
|
173
|
+
* @throws {ApiError} `409 invite_not_open` when it was already used or revoked.
|
|
174
|
+
*/
|
|
175
|
+
revokeInvite(id: string): Promise<void>;
|
|
176
|
+
/**
|
|
177
|
+
* Change a team member's role.
|
|
178
|
+
*
|
|
179
|
+
* @param userId - The member's `usr_…` id (see: `team.members()`).
|
|
180
|
+
* @param role - The role to set.
|
|
181
|
+
* @returns The updated member.
|
|
182
|
+
* @throws {ApiError} `403 forbidden` without `canManageMembers`.
|
|
183
|
+
* @throws {ApiError} `403 cannot_change_owner_role` for `role: "owner"` or when
|
|
184
|
+
* `userId` is the account's current owner — there is no self-service ownership transfer.
|
|
185
|
+
* @throws {ApiError} `400 invalid_role` for an unrecognised role.
|
|
186
|
+
* @throws {ApiError} `404 member_not_found` for an unknown `userId`.
|
|
187
|
+
*/
|
|
188
|
+
setRole(userId: string, role: string): Promise<TeamMember>;
|
|
189
|
+
/**
|
|
190
|
+
* Remove a member from the account.
|
|
191
|
+
*
|
|
192
|
+
* Tombstones the membership server-side rather than deleting it outright
|
|
193
|
+
* (D-R3), but that is a server implementation detail this method does not
|
|
194
|
+
* surface — it returns nothing, the same pin as `documents.delete`.
|
|
195
|
+
*
|
|
196
|
+
* @param userId - The member's `usr_…` id (see: `team.members()`).
|
|
197
|
+
* @throws {ApiError} `403 forbidden` without `canManageMembers`.
|
|
198
|
+
* @throws {ApiError} `403 cannot_remove_owner` when `userId` is the account's owner.
|
|
199
|
+
* @throws {ApiError} `404 member_not_found` for an unknown `userId`.
|
|
200
|
+
* @throws {ApiError} `409 last_member` when `userId` is the account's only active member.
|
|
201
|
+
*/
|
|
202
|
+
removeMember(userId: string): Promise<void>;
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=team.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team.d.ts","sourceRoot":"","sources":["../../src/team.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EAAE,KAAK,YAAY,EAAQ,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAGzE;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,2BAA2B;IAC3B,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;CAC/D;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,uDAAuD;IACvD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,sDAAsD;IACtD,EAAE,EAAE,MAAM,CAAC;IACX,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,2FAA2F;IAC3F,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,qEAAqE;IACrE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,qBAAa,OAAO;;IAGlB;;OAEG;gBACS,IAAI,EAAE,QAAQ;IAI1B;;;;;;;;;OASG;IACG,OAAO,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAKtC;;;;;;;OAOG;IACG,MAAM,CAAC,KAAK,GAAE,eAAoB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAYtE;;;;;;;;OAQG;IACG,OAAO,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAKtC;;;;;;;;;;OAUG;IACG,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO7C;;;;;;;;;;;OAWG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAShE;;;;;;;;;;;;OAYG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMlD"}
|
package/dist/src/team.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `client.team.*` — the caller's account team, self-service.
|
|
3
|
+
*
|
|
4
|
+
* Six operations, one HTTP call each: list the roster, invite someone (or mint
|
|
5
|
+
* an open, shareable invite link), list open invites, revoke one, change a
|
|
6
|
+
* member's role, remove a member. Nothing here composes two requests or
|
|
7
|
+
* filters a list client-side — `team.invites()` returns only OPEN invites
|
|
8
|
+
* because the **server** filters them, not because this module scans the
|
|
9
|
+
* account's whole invite history and drops the closed ones
|
|
10
|
+
* (`docs/implementation.md`'s "what a wrapper is" rule, mirrored from
|
|
11
|
+
* `documents.ts`'s header).
|
|
12
|
+
*
|
|
13
|
+
* **Not project-scoped.** Team membership is an account-wide concept, so
|
|
14
|
+
* (unlike `documents`) this namespace carries no `?project=` anywhere and is
|
|
15
|
+
* handed the transport only — the same narrow host `connections.ts` and
|
|
16
|
+
* `vars.ts` use, so a caller cannot even accidentally reach a default project
|
|
17
|
+
* to send.
|
|
18
|
+
*
|
|
19
|
+
* `docs/team.md`'s pinned wire contract (mirrored in this task's `plan.md`
|
|
20
|
+
* §"Pinned wire contract") is the source these six methods transcribe:
|
|
21
|
+
*
|
|
22
|
+
* ```
|
|
23
|
+
* GET /me/account/members → 200 { members: TeamMember[] }
|
|
24
|
+
* POST /me/account/invites { email?, role? } → 201 { invite: TeamInviteWithLink }
|
|
25
|
+
* GET /me/account/invites → 200 { invites: TeamInvite[] }
|
|
26
|
+
* DELETE /me/account/invites/:id → 200 { ok: true }
|
|
27
|
+
* PATCH /me/account/members/:userId { role } → 200 { member: TeamMember }
|
|
28
|
+
* DELETE /me/account/members/:userId → 200 { ok: true }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* This module implements the client half of the server's live
|
|
32
|
+
* `/me/account/...` routes (`endpoints.json`: `status: "required"` for each).
|
|
33
|
+
* Calling any of these against a server that has not shipped T1.2.1 yet
|
|
34
|
+
* answers `404`, which reaches the caller as an ordinary `ApiError`.
|
|
35
|
+
*
|
|
36
|
+
* @module
|
|
37
|
+
*/
|
|
38
|
+
import { path } from "./http.js";
|
|
39
|
+
import { unwrap } from "./types.js";
|
|
40
|
+
/**
|
|
41
|
+
* The `team` namespace on a `W6WClient`.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* const members = await client.team.members();
|
|
46
|
+
* const { redemptionLink } = await client.team.invite({ role: "admin" });
|
|
47
|
+
* await client.team.setRole(members[0].userId, "admin");
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export class TeamApi {
|
|
51
|
+
#host;
|
|
52
|
+
/**
|
|
53
|
+
* @param host - The client this namespace issues requests through.
|
|
54
|
+
*/
|
|
55
|
+
constructor(host) {
|
|
56
|
+
this.#host = host;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* List the caller's account team.
|
|
60
|
+
*
|
|
61
|
+
* Member-readable: any active member of the account may call this,
|
|
62
|
+
* regardless of role — only the write operations below require
|
|
63
|
+
* `canManageMembers`.
|
|
64
|
+
*
|
|
65
|
+
* @returns The team, unwrapped from the `members` envelope.
|
|
66
|
+
* @throws {ApiError} On any non-2xx.
|
|
67
|
+
*/
|
|
68
|
+
async members() {
|
|
69
|
+
const res = await this.#host.request({ method: "GET", path: "/me/account/members" });
|
|
70
|
+
return unwrap(res, "members");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Invite someone to the account, or mint an open, shareable invite link.
|
|
74
|
+
*
|
|
75
|
+
* @param input - `email` and `role`, both optional.
|
|
76
|
+
* @returns The created invite, including its one-time `token` and `redemptionLink`.
|
|
77
|
+
* @throws {ApiError} `403 forbidden` without `canManageMembers`.
|
|
78
|
+
* @throws {ApiError} `400 invalid_role` for `role: "owner"` or an unrecognised role.
|
|
79
|
+
*/
|
|
80
|
+
async invite(input = {}) {
|
|
81
|
+
const res = await this.#host.request({
|
|
82
|
+
method: "POST",
|
|
83
|
+
path: "/me/account/invites",
|
|
84
|
+
// Assembled field by field, exactly the two documented fields: an
|
|
85
|
+
// `undefined` one is dropped by JSON serialisation rather than sent as
|
|
86
|
+
// an explicit `null`.
|
|
87
|
+
body: { email: input.email, role: input.role },
|
|
88
|
+
});
|
|
89
|
+
return unwrap(res, "invite");
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* List the account's open (pending, unrevoked, unexpired) invites.
|
|
93
|
+
*
|
|
94
|
+
* The filtering happens server-side — this is one `GET`, never a
|
|
95
|
+
* list-then-filter over the account's whole invite history.
|
|
96
|
+
*
|
|
97
|
+
* @returns The open invites, unwrapped from the `invites` envelope.
|
|
98
|
+
* @throws {ApiError} `403 forbidden` without `canManageMembers`.
|
|
99
|
+
*/
|
|
100
|
+
async invites() {
|
|
101
|
+
const res = await this.#host.request({ method: "GET", path: "/me/account/invites" });
|
|
102
|
+
return unwrap(res, "invites");
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Revoke a pending invite before it is redeemed.
|
|
106
|
+
*
|
|
107
|
+
* Not idempotent: revoking an invite that is already used or already
|
|
108
|
+
* revoked is a rejected request, not a silent success.
|
|
109
|
+
*
|
|
110
|
+
* @param id - The invite's own id (see: `team.invites()`).
|
|
111
|
+
* @throws {ApiError} `403 forbidden` without `canManageMembers`.
|
|
112
|
+
* @throws {ApiError} `404 invite_not_found` for an unknown id.
|
|
113
|
+
* @throws {ApiError} `409 invite_not_open` when it was already used or revoked.
|
|
114
|
+
*/
|
|
115
|
+
async revokeInvite(id) {
|
|
116
|
+
await this.#host.request({
|
|
117
|
+
method: "DELETE",
|
|
118
|
+
path: path `/me/account/invites/${id}`,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Change a team member's role.
|
|
123
|
+
*
|
|
124
|
+
* @param userId - The member's `usr_…` id (see: `team.members()`).
|
|
125
|
+
* @param role - The role to set.
|
|
126
|
+
* @returns The updated member.
|
|
127
|
+
* @throws {ApiError} `403 forbidden` without `canManageMembers`.
|
|
128
|
+
* @throws {ApiError} `403 cannot_change_owner_role` for `role: "owner"` or when
|
|
129
|
+
* `userId` is the account's current owner — there is no self-service ownership transfer.
|
|
130
|
+
* @throws {ApiError} `400 invalid_role` for an unrecognised role.
|
|
131
|
+
* @throws {ApiError} `404 member_not_found` for an unknown `userId`.
|
|
132
|
+
*/
|
|
133
|
+
async setRole(userId, role) {
|
|
134
|
+
const res = await this.#host.request({
|
|
135
|
+
method: "PATCH",
|
|
136
|
+
path: path `/me/account/members/${userId}`,
|
|
137
|
+
body: { role },
|
|
138
|
+
});
|
|
139
|
+
return unwrap(res, "member");
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Remove a member from the account.
|
|
143
|
+
*
|
|
144
|
+
* Tombstones the membership server-side rather than deleting it outright
|
|
145
|
+
* (D-R3), but that is a server implementation detail this method does not
|
|
146
|
+
* surface — it returns nothing, the same pin as `documents.delete`.
|
|
147
|
+
*
|
|
148
|
+
* @param userId - The member's `usr_…` id (see: `team.members()`).
|
|
149
|
+
* @throws {ApiError} `403 forbidden` without `canManageMembers`.
|
|
150
|
+
* @throws {ApiError} `403 cannot_remove_owner` when `userId` is the account's owner.
|
|
151
|
+
* @throws {ApiError} `404 member_not_found` for an unknown `userId`.
|
|
152
|
+
* @throws {ApiError} `409 last_member` when `userId` is the account's only active member.
|
|
153
|
+
*/
|
|
154
|
+
async removeMember(userId) {
|
|
155
|
+
await this.#host.request({
|
|
156
|
+
method: "DELETE",
|
|
157
|
+
path: path `/me/account/members/${userId}`,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=team.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team.js","sourceRoot":"","sources":["../../src/team.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EAAqB,IAAI,EAAuB,MAAM,WAAW,CAAC;AACzE,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAsFpC;;;;;;;;;GASG;AACH,MAAM,OAAO,OAAO;IACT,KAAK,CAAW;IAEzB;;OAEG;IACH,YAAY,IAAc;QACxB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAU,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,qBAAqB,EAAE,CAAC,CAAC;QAC9F,OAAO,MAAM,CAAe,GAAG,EAAE,SAAS,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,MAAM,CAAC,QAAyB,EAAE;QACtC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAU;YAC5C,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,qBAAqB;YAC3B,kEAAkE;YAClE,uEAAuE;YACvE,sBAAsB;YACtB,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE;SAC/C,CAAC,CAAC;QACH,OAAO,MAAM,CAAqB,GAAG,EAAE,QAAQ,CAAC,CAAC;IACnD,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAU,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,qBAAqB,EAAE,CAAC,CAAC;QAC9F,OAAO,MAAM,CAAe,GAAG,EAAE,SAAS,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,YAAY,CAAC,EAAU;QAC3B,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAU;YAChC,MAAM,EAAE,QAAQ;YAChB,IAAI,EAAE,IAAI,CAAA,uBAAuB,EAAE,EAAE;SACtC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,IAAY;QACxC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAU;YAC5C,MAAM,EAAE,OAAO;YACf,IAAI,EAAE,IAAI,CAAA,uBAAuB,MAAM,EAAE;YACzC,IAAI,EAAE,EAAE,IAAI,EAAE;SACf,CAAC,CAAC;QACH,OAAO,MAAM,CAAa,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,YAAY,CAAC,MAAc;QAC/B,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAU;YAChC,MAAM,EAAE,QAAQ;YAChB,IAAI,EAAE,IAAI,CAAA,uBAAuB,MAAM,EAAE;SAC1C,CAAC,CAAC;IACL,CAAC;CACF"}
|